diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000000000..e4c4eef10b28c --- /dev/null +++ b/.clippy.toml @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +check-private-items = true + +disallowed-macros = [ + # The `clippy::dbg_macro` lint only works with `std::dbg!`, thus we simulate + # it here, see: https://github.com/rust-lang/rust-clippy/issues/11303. + { path = "kernel::dbg", reason = "the `dbg!` macro is intended as a debugging tool" }, +] diff --git a/.gitignore b/.gitignore index 9178e3556127c..8d96f0aaefb55 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ modules.order # !.github !.clang-format +!.clippy.toml !.cocciconfig !.editorconfig !.get_maintainer.ignore @@ -129,6 +130,7 @@ series # ctags files tags +!tags/ TAGS # cscope files diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 4feb692c4c1d3..b6720768d63d2 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -342,6 +342,70 @@ Description: Specific uncompressed frame descriptors support ========================= ===================================== +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased +Date: Sept 2024 +KernelVersion: 5.15 +Description: Framebased format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name +Date: Sept 2024 +KernelVersion: 5.15 +Description: Specific framebased format descriptors + + ================== ======================================= + bFormatIndex unique id for this format descriptor; + only defined after parent header is + linked into the streaming class; + read-only + bmaControls this format's data for bmaControls in + the streaming header + bmInterlaceFlags specifies interlace information, + read-only + bAspectRatioY the X dimension of the picture aspect + ratio, read-only + bAspectRatioX the Y dimension of the picture aspect + ratio, read-only + bDefaultFrameIndex optimum frame index for this stream + bBitsPerPixel number of bits per pixel used to + specify color in the decoded video + frame + guidFormat globally unique id used to identify + stream-encoding format + ================== ======================================= + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name/name +Date: Sept 2024 +KernelVersion: 5.15 +Description: Specific framebased frame descriptors + + ========================= ===================================== + bFrameIndex unique id for this framedescriptor; + only defined after parent format is + linked into the streaming header; + read-only + dwFrameInterval indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval the frame interval the device would + like to use as default + dwBytesPerLine Specifies the number of bytes per line + of video for packed fixed frame size + formats, allowing the receiver to + perform stride alignment of the video. + If the bVariableSize value (above) is + TRUE (1), or if the format does not + permit such alignment, this value shall + be set to zero (0). + dwMaxBitRate the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate the minimum bit rate at the longest + frame interval in bps + wHeight height of decoded bitmap frame in px + wWidth width of decoded bitmam frame in px + bmCapabilities still image support, fixed frame-rate + support + ========================= ===================================== + What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header Date: Dec 2014 KernelVersion: 4.0 diff --git a/Documentation/ABI/testing/debugfs-hisi-migration b/Documentation/ABI/testing/debugfs-hisi-migration new file mode 100644 index 0000000000000..2c01b2d387ddd --- /dev/null +++ b/Documentation/ABI/testing/debugfs-hisi-migration @@ -0,0 +1,25 @@ +What: /sys/kernel/debug/vfio//migration/hisi_acc/dev_data +Date: Jan 2025 +KernelVersion: 6.13 +Contact: Longfang Liu +Description: Read the configuration data and some status data + required for device live migration. These data include device + status data, queue configuration data, some task configuration + data and device attribute data. The output format of the data + is defined by the live migration driver. + +What: /sys/kernel/debug/vfio//migration/hisi_acc/migf_data +Date: Jan 2025 +KernelVersion: 6.13 +Contact: Longfang Liu +Description: Read the data from the last completed live migration. + This data includes the same device status data as in "dev_data". + The migf_data is the dev_data that is migrated. + +What: /sys/kernel/debug/vfio//migration/hisi_acc/cmd_state +Date: Jan 2025 +KernelVersion: 6.13 +Contact: Longfang Liu +Description: Used to obtain the device command sending and receiving + channel status. Returns failure or success logs based on the + results. diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-vpa-pmu b/Documentation/ABI/testing/sysfs-bus-event_source-devices-vpa-pmu index 8285263ff78dc..a116aee9709aa 100644 --- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-vpa-pmu +++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-vpa-pmu @@ -8,17 +8,18 @@ Description: Read-only. Attribute group to describe the magic bits Each attribute under this group defines a bit range of the perf_event_attr.config. Supported attribute are listed below:: - event = "config:0-31" - event ID - For example:: + event = "config:0-31" - event ID - l1_to_l2_lat = "event=0x1" + For example:: + + l1_to_l2_lat = "event=0x1" What: /sys/bus/event_source/devices/vpa_pmu/events Date: November 2024 Contact: Linux on PowerPC Developer List -Description: Read-only. Attribute group to describe performance monitoring +Description: Read-only. Attribute group to describe performance monitoring events for the Virtual Processor Area events. Each attribute - in this group describes a single performance monitoring event - supported by vpa_pmu. The name of the file is the name of - the event (See ABI/testing/sysfs-bus-event_source-devices-events). + in this group describes a single performance monitoring event + supported by vpa_pmu. The name of the file is the name of + the event (See ABI/testing/sysfs-bus-event_source-devices-events). diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 89943c2d54e8a..f83bd6829285c 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2268,6 +2268,30 @@ Description: An example format is 16-bytes, 2-digits-per-byte, HEX-string representing the sensor unique ID number. +What: /sys/bus/iio/devices/iio:deviceX/filter_type_available +What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_mode_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns a list with the possible filter modes. Options + for the attribute: + + * "sinc3" - The digital sinc3 filter. Moderate 1st + conversion time. Good noise performance. + * "sinc4" - Sinc 4. Excellent noise performance. Long + 1st conversion time. + * "sinc5" - The digital sinc5 filter. Excellent noise + performance + * "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion + time. + * "sinc3+rej60" - Sinc3 + 60Hz rejection. + * "sinc3+sinc1" - Sinc3 + averaging by 8. Low 1st conversion + time. + * "sinc3+pf1" - Sinc3 + device specific Post Filter 1. + * "sinc3+pf2" - Sinc3 + device specific Post Filter 2. + * "sinc3+pf3" - Sinc3 + device specific Post Filter 3. + * "sinc3+pf4" - Sinc3 + device specific Post Filter 4. + What: /sys/.../events/in_proximity_thresh_either_runningperiod KernelVersion: 6.6 Contact: linux-iio@vger.kernel.org @@ -2339,3 +2363,11 @@ KernelVersion: 6.10 Contact: linux-iio@vger.kernel.org Description: The value of current sense resistor in Ohms. + +What: /sys/.../iio:deviceX/in_attention_input +KernelVersion: 6.13 +Contact: linux-iio@vger.kernel.org +Description: + Value representing the user's attention to the system expressed + in units as percentage. This usually means if the user is + looking at the screen or not. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 deleted file mode 100644 index f24ed6687e900..0000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 +++ /dev/null @@ -1,46 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_mode_available -KernelVersion: 6.2 -Contact: linux-iio@vger.kernel.org -Description: - Reading returns a list with the possible filter modes. - - * "sinc4" - Sinc 4. Excellent noise performance. Long - 1st conversion time. No natural 50/60Hz rejection. - - * "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion - time. - - * "sinc3" - Sinc3. Moderate 1st conversion time. - Good noise performance. - - * "sinc3+rej60" - Sinc3 + 60Hz rejection. At a sampling - frequency of 50Hz, achieves simultaneous 50Hz and 60Hz - rejection. - - * "sinc3+sinc1" - Sinc3 + averaging by 8. Low 1st conversion - time. Best used with a sampling frequency of at least - 216.19Hz. - - * "sinc3+pf1" - Sinc3 + Post Filter 1. 53dB rejection @ - 50Hz, 58dB rejection @ 60Hz. - - * "sinc3+pf2" - Sinc3 + Post Filter 2. 70dB rejection @ - 50Hz, 70dB rejection @ 60Hz. - - * "sinc3+pf3" - Sinc3 + Post Filter 3. 99dB rejection @ - 50Hz, 103dB rejection @ 60Hz. - - * "sinc3+pf4" - Sinc3 + Post Filter 4. 103dB rejection @ - 50Hz, 109dB rejection @ 60Hz. - -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_mode -KernelVersion: 6.2 -Contact: linux-iio@vger.kernel.org -Description: - Set the filter mode of the differential channel. When the filter - mode changes, the in_voltageY-voltageZ_sampling_frequency and - in_voltageY-voltageZ_sampling_frequency_available attributes - might also change to accommodate the new filter mode. - If the current sampling frequency is out of range for the new - filter mode, the sampling frequency will be changed to the - closest valid one. diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 7f63c7e977735..5da6a14dc326b 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -163,6 +163,17 @@ Description: will be present in sysfs. Writing 1 to this file will perform reset. +What: /sys/bus/pci/devices/.../reset_subordinate +Date: October 2024 +Contact: linux-pci@vger.kernel.org +Description: + This is visible only for bridge devices. If you want to reset + all devices attached through the subordinate bus of a specific + bridge device, writing 1 to this will try to do it. This will + affect all devices attached to the system through this bridge + similiar to writing 1 to their individual "reset" file, so use + with caution. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 281b995beb05a..38e101c17a004 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -149,6 +149,19 @@ Description: advertise to the partner. The currently used capabilities are in brackets. Selection happens by writing to the file. +What: /sys/class/typec//usb_capability +Date: November 2024 +Contact: Heikki Krogerus +Description: Lists the supported USB Modes. The default USB mode that is used + next time with the Enter_USB Message is in brackets. The default + mode can be changed by writing to the file when supported by the + driver. + + Valid values: + - usb2 (USB 2.0) + - usb3 (USB 3.2) + - usb4 (USB4) + USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) What: /sys/class/typec/-partner/accessory_mode @@ -220,6 +233,20 @@ Description: directory exists, it will have an attribute file for every VDO in Discover Identity command result. +What: /sys/class/typec/-partner/usb_mode +Date: November 2024 +Contact: Heikki Krogerus +Description: The USB Modes that the partner device supports. The active mode + is displayed in brackets. The active USB mode can be changed by + writing to this file when the port driver is able to send Data + Reset Message to the partner. That requires USB Power Delivery + contract between the partner and the port. + + Valid values: + - usb2 (USB 2.0) + - usb3 (USB 3.2) + - usb4 (USB4) + USB Type-C cable devices (eg. /sys/class/typec/port0-cable/) Note: Electronically Marked Cables will have a device also for one cable plug diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index fdedf1ea944ba..3e1630c70d8ae 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -311,10 +311,13 @@ Description: Do background GC aggressively when set. Set to 0 by default. GC approach and turns SSR mode on. gc urgent low(2): lowers the bar of checking I/O idling in order to process outstanding discard commands and GC a - little bit aggressively. uses cost benefit GC approach. + little bit aggressively. always uses cost benefit GC approach, + and will override age-threshold GC approach if ATGC is enabled + at the same time. gc urgent mid(3): does GC forcibly in a period of given gc_urgent_sleep_time and executes a mid level of I/O idling check. - uses cost benefit GC approach. + always uses cost benefit GC approach, and will override + age-threshold GC approach if ATGC is enabled at the same time. What: /sys/fs/f2fs//gc_urgent_sleep_time Date: August 2017 @@ -819,3 +822,9 @@ Description: It controls the valid block ratio threshold not to trigger excessiv for zoned deivces. The initial value of it is 95(%). F2FS will stop the background GC thread from intiating GC for sections having valid blocks exceeding the ratio. + +What: /sys/fs/f2fs//max_read_extent_count +Date: November 2024 +Contact: "Chao Yu" +Description: It controls max read extent count for per-inode, the value of threshold + is 10240 by default. diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst index 21507e3cc2385..35f82f2d45f5e 100644 --- a/Documentation/PCI/endpoint/pci-endpoint.rst +++ b/Documentation/PCI/endpoint/pci-endpoint.rst @@ -117,6 +117,35 @@ by the PCI endpoint function driver. The PCI endpoint function driver should use pci_epc_mem_free_addr() to free the memory space allocated using pci_epc_mem_alloc_addr(). +* pci_epc_map_addr() + + A PCI endpoint function driver should use pci_epc_map_addr() to map to a RC + PCI address the CPU address of local memory obtained with + pci_epc_mem_alloc_addr(). + +* pci_epc_unmap_addr() + + A PCI endpoint function driver should use pci_epc_unmap_addr() to unmap the + CPU address of local memory mapped to a RC address with pci_epc_map_addr(). + +* pci_epc_mem_map() + + A PCI endpoint controller may impose constraints on the RC PCI addresses that + can be mapped. The function pci_epc_mem_map() allows endpoint function + drivers to allocate and map controller memory while handling such + constraints. This function will determine the size of the memory that must be + allocated with pci_epc_mem_alloc_addr() for successfully mapping a RC PCI + address range. This function will also indicate the size of the PCI address + range that was actually mapped, which can be less than the requested size, as + well as the offset into the allocated memory to use for accessing the mapped + RC PCI address range. + +* pci_epc_mem_unmap() + + A PCI endpoint function driver can use pci_epc_mem_unmap() to unmap and free + controller memory that was allocated and mapped using pci_epc_mem_map(). + + Other EPC APIs ~~~~~~~~~~~~~~ diff --git a/Documentation/PCI/index.rst b/Documentation/PCI/index.rst index e73f84aebde3b..5e7c4e6e726bb 100644 --- a/Documentation/PCI/index.rst +++ b/Documentation/PCI/index.rst @@ -18,3 +18,4 @@ PCI Bus Subsystem pcieaer-howto endpoint/index boot-interrupts + tph diff --git a/Documentation/PCI/pciebus-howto.rst b/Documentation/PCI/pciebus-howto.rst index f344452651e1f..375d9ce171f62 100644 --- a/Documentation/PCI/pciebus-howto.rst +++ b/Documentation/PCI/pciebus-howto.rst @@ -217,8 +217,12 @@ capability structure except the PCI Express capability structure, that is shared between many drivers including the service drivers. RMW Capability accessors (pcie_capability_clear_and_set_word(), pcie_capability_set_word(), and pcie_capability_clear_word()) protect -a selected set of PCI Express Capability Registers (Link Control -Register and Root Control Register). Any change to those registers -should be performed using RMW accessors to avoid problems due to -concurrent updates. For the up-to-date list of protected registers, -see pcie_capability_clear_and_set_word(). +a selected set of PCI Express Capability Registers: + +* Link Control Register +* Root Control Register +* Link Control 2 Register + +Any change to those registers should be performed using RMW accessors to +avoid problems due to concurrent updates. For the up-to-date list of +protected registers, see pcie_capability_clear_and_set_word(). diff --git a/Documentation/PCI/tph.rst b/Documentation/PCI/tph.rst new file mode 100644 index 0000000000000..e8993be64fd64 --- /dev/null +++ b/Documentation/PCI/tph.rst @@ -0,0 +1,132 @@ +.. SPDX-License-Identifier: GPL-2.0 + + +=========== +TPH Support +=========== + +:Copyright: 2024 Advanced Micro Devices, Inc. +:Authors: - Eric van Tassell + - Wei Huang + + +Overview +======== + +TPH (TLP Processing Hints) is a PCIe feature that allows endpoint devices +to provide optimization hints for requests that target memory space. +These hints, in a format called Steering Tags (STs), are embedded in the +requester's TLP headers, enabling the system hardware, such as the Root +Complex, to better manage platform resources for these requests. + +For example, on platforms with TPH-based direct data cache injection +support, an endpoint device can include appropriate STs in its DMA +traffic to specify which cache the data should be written to. This allows +the CPU core to have a higher probability of getting data from cache, +potentially improving performance and reducing latency in data +processing. + + +How to Use TPH +============== + +TPH is presented as an optional extended capability in PCIe. The Linux +kernel handles TPH discovery during boot, but it is up to the device +driver to request TPH enablement if it is to be utilized. Once enabled, +the driver uses the provided API to obtain the Steering Tag for the +target memory and to program the ST into the device's ST table. + +Enable TPH support in Linux +--------------------------- + +To support TPH, the kernel must be built with the CONFIG_PCIE_TPH option +enabled. + +Manage TPH +---------- + +To enable TPH for a device, use the following function:: + + int pcie_enable_tph(struct pci_dev *pdev, int mode); + +This function enables TPH support for device with a specific ST mode. +Current supported modes include: + + * PCI_TPH_ST_NS_MODE - NO ST Mode + * PCI_TPH_ST_IV_MODE - Interrupt Vector Mode + * PCI_TPH_ST_DS_MODE - Device Specific Mode + +`pcie_enable_tph()` checks whether the requested mode is actually +supported by the device before enabling. The device driver can figure out +which TPH mode is supported and can be properly enabled based on the +return value of `pcie_enable_tph()`. + +To disable TPH, use the following function:: + + void pcie_disable_tph(struct pci_dev *pdev); + +Manage ST +--------- + +Steering Tags are platform specific. PCIe spec does not specify where STs +are from. Instead PCI Firmware Specification defines an ACPI _DSM method +(see the `Revised _DSM for Cache Locality TPH Features ECN +`_) for retrieving +STs for a target memory of various properties. This method is what is +supported in this implementation. + +To retrieve a Steering Tag for a target memory associated with a specific +CPU, use the following function:: + + int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type type, + unsigned int cpu_uid, u16 *tag); + +The `type` argument is used to specify the memory type, either volatile +or persistent, of the target memory. The `cpu_uid` argument specifies the +CPU where the memory is associated to. + +After the ST value is retrieved, the device driver can use the following +function to write the ST into the device:: + + int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, + u16 tag); + +The `index` argument is the ST table entry index the ST tag will be +written into. `pcie_tph_set_st_entry()` will figure out the proper +location of ST table, either in the MSI-X table or in the TPH Extended +Capability space, and write the Steering Tag into the ST entry pointed by +the `index` argument. + +It is completely up to the driver to decide how to use these TPH +functions. For example a network device driver can use the TPH APIs above +to update the Steering Tag when interrupt affinity of a RX/TX queue has +been changed. Here is a sample code for IRQ affinity notifier: + +.. code-block:: c + + static void irq_affinity_notified(struct irq_affinity_notify *notify, + const cpumask_t *mask) + { + struct drv_irq *irq; + unsigned int cpu_id; + u16 tag; + + irq = container_of(notify, struct drv_irq, affinity_notify); + cpumask_copy(irq->cpu_mask, mask); + + /* Pick a right CPU as the target - here is just an example */ + cpu_id = cpumask_first(irq->cpu_mask); + + if (pcie_tph_get_cpu_st(irq->pdev, TPH_MEM_TYPE_VM, cpu_id, + &tag)) + return; + + if (pcie_tph_set_st_entry(irq->pdev, irq->msix_nr, tag)) + return; + } + +Disable TPH system-wide +----------------------- + +There is a kernel command line option available to control TPH feature: + * "notph": TPH will be disabled for all endpoint devices. diff --git a/Documentation/admin-guide/LSM/apparmor.rst b/Documentation/admin-guide/LSM/apparmor.rst index 6cf81bbd7ce8b..47939ee89d746 100644 --- a/Documentation/admin-guide/LSM/apparmor.rst +++ b/Documentation/admin-guide/LSM/apparmor.rst @@ -18,8 +18,11 @@ set ``CONFIG_SECURITY_APPARMOR=y`` If AppArmor should be selected as the default security module then set:: - CONFIG_DEFAULT_SECURITY="apparmor" - CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 + CONFIG_DEFAULT_SECURITY_APPARMOR=y + +The CONFIG_LSM parameter manages the order and selection of LSMs. +Specify apparmor as the first "major" module (e.g. AppArmor, SELinux, Smack) +in the list. Build the kernel diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst index 6247bbc9547ce..59931f21c9749 100644 --- a/Documentation/admin-guide/kernel-parameters.rst +++ b/Documentation/admin-guide/kernel-parameters.rst @@ -174,6 +174,7 @@ is applicable:: SCSI Appropriate SCSI support is enabled. A lot of drivers have their options described inside the Documentation/scsi/ sub-directory. + SDW SoundWire support is enabled. SECURITY Different security models are enabled. SELINUX SELinux support is enabled. SERIAL Serial support is enabled. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a4736ee87b1aa..dc663c0ca6706 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4686,6 +4686,10 @@ nomio [S390] Do not use MIO instructions. norid [S390] ignore the RID field and force use of one PCI domain per PCI function + notph [PCIE] If the PCIE_TPH kernel config parameter + is enabled, this kernel boot option can be used + to disable PCIe TLP Processing Hints support + system-wide. pcie_aspm= [PCIE] Forcibly enable or ignore PCIe Active State Power Management. @@ -6071,6 +6075,10 @@ non-zero "wait" parameter. See weight_single and weight_many. + sdw_mclk_divider=[SDW] + Specify the MCLK divider for Intel SoundWire buses in + case the BIOS does not provide the clock rate properly. + skew_tick= [KNL,EARLY] Offset the periodic timer tick per cpu to mitigate xtime_lock contention on larger systems, and/or RCU lock contention on all systems with CONFIG_MAXSMP set. @@ -6158,6 +6166,16 @@ For more information see Documentation/mm/slub.rst. (slub_nomerge legacy name also accepted for now) + slab_strict_numa [MM] + Support memory policies on a per object level + in the slab allocator. The default is for memory + policies to be applied at the folio level when + a new folio is needed or a partial folio is + retrieved from the lists. Increases overhead + in the slab fastpaths but gains more accurate + NUMA kernel object placement which helps with slow + interconnects in NUMA systems. + slram= [HW,MTD] smart2= [HW] diff --git a/Documentation/admin-guide/media/index.rst b/Documentation/admin-guide/media/index.rst index be7e0e4482ca5..b11737ae6c046 100644 --- a/Documentation/admin-guide/media/index.rst +++ b/Documentation/admin-guide/media/index.rst @@ -20,6 +20,11 @@ Documentation/driver-api/media/index.rst - for driver development information and Kernel APIs used by media devices; +Documentation/process/debugging/media_specific_debugging_guide.rst + + - for advice about essential tools and techniques to debug drivers on this + subsystem + .. toctree:: :caption: Table of Contents :maxdepth: 2 diff --git a/Documentation/admin-guide/sysctl/fs.rst b/Documentation/admin-guide/sysctl/fs.rst index 30c61474dec51..f5ec6c9312e1d 100644 --- a/Documentation/admin-guide/sysctl/fs.rst +++ b/Documentation/admin-guide/sysctl/fs.rst @@ -337,3 +337,13 @@ Each "watch" costs roughly 90 bytes on a 32-bit kernel, and roughly 160 bytes on a 64-bit one. The current default value for ``max_user_watches`` is 4% of the available low memory, divided by the "watch" cost in bytes. + +5. /proc/sys/fs/fuse - Configuration options for FUSE filesystems +===================================================================== + +This directory contains the following configuration options for FUSE +filesystems: + +``/proc/sys/fs/fuse/max_pages_limit`` is a read/write file for +setting/getting the maximum number of pages that can be used for servicing +requests in FUSE. diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index f8bc1630eba05..b2b36d0c3094d 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -401,6 +401,15 @@ The upper bound on the number of tasks that are checked. This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. +hung_task_detect_count +====================== + +Indicates the total number of tasks that have been detected as hung since +the system boot. + +This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. + + hung_task_timeout_secs ====================== diff --git a/Documentation/arch/arm64/cpu-feature-registers.rst b/Documentation/arch/arm64/cpu-feature-registers.rst index 44f9bd78539d3..253e9743de2f9 100644 --- a/Documentation/arch/arm64/cpu-feature-registers.rst +++ b/Documentation/arch/arm64/cpu-feature-registers.rst @@ -152,6 +152,8 @@ infrastructure: +------------------------------+---------+---------+ | DIT | [51-48] | y | +------------------------------+---------+---------+ + | MPAM | [43-40] | n | + +------------------------------+---------+---------+ | SVE | [35-32] | y | +------------------------------+---------+---------+ | GIC | [27-24] | n | diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index 65bfab1b18614..77db10e944f03 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -258,6 +258,8 @@ stable kernels. | Hisilicon | Hip{08,09,10,10C| #162001900 | N/A | | | ,11} SMMU PMCG | | | +----------------+-----------------+-----------------+-----------------------------+ +| Hisilicon | Hip09 | #162100801 | HISILICON_ERRATUM_162100801 | ++----------------+-----------------+-----------------+-----------------------------+ +----------------+-----------------+-----------------+-----------------------------+ | Qualcomm Tech. | Kryo/Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/Documentation/arch/loongarch/irq-chip-model.rst b/Documentation/arch/loongarch/irq-chip-model.rst index 6dd48256e39f7..a7ecce11e445c 100644 --- a/Documentation/arch/loongarch/irq-chip-model.rst +++ b/Documentation/arch/loongarch/irq-chip-model.rst @@ -85,6 +85,70 @@ to CPUINTC directly:: | Devices | +---------+ +Virtual Extended IRQ model +========================== + +In this model, IPI (Inter-Processor Interrupt) and CPU Local Timer interrupt +go to CPUINTC directly, CPU UARTS interrupts go to PCH-PIC, while all other +devices interrupts go to PCH-PIC/PCH-MSI and gathered by V-EIOINTC (Virtual +Extended I/O Interrupt Controller), and then go to CPUINTC directly:: + + +-----+ +-------------------+ +-------+ + | IPI |--> | CPUINTC(0-255vcpu)| <-- | Timer | + +-----+ +-------------------+ +-------+ + ^ + | + +-----------+ + | V-EIOINTC | + +-----------+ + ^ ^ + | | + +---------+ +---------+ + | PCH-PIC | | PCH-MSI | + +---------+ +---------+ + ^ ^ ^ + | | | + +--------+ +---------+ +---------+ + | UARTs | | Devices | | Devices | + +--------+ +---------+ +---------+ + + +Description +----------- +V-EIOINTC (Virtual Extended I/O Interrupt Controller) is an extension of +EIOINTC, it only works in VM mode which runs in KVM hypervisor. Interrupts can +be routed to up to four vCPUs via standard EIOINTC, however with V-EIOINTC +interrupts can be routed to up to 256 virtual cpus. + +With standard EIOINTC, interrupt routing setting includes two parts: eight +bits for CPU selection and four bits for CPU IP (Interrupt Pin) selection. +For CPU selection there is four bits for EIOINTC node selection, four bits +for EIOINTC CPU selection. Bitmap method is used for CPU selection and +CPU IP selection, so interrupt can only route to CPU0 - CPU3 and IP0-IP3 in +one EIOINTC node. + +With V-EIOINTC it supports to route more CPUs and CPU IP (Interrupt Pin), +there are two newly added registers with V-EIOINTC. + +EXTIOI_VIRT_FEATURES +-------------------- +This register is read-only register, which indicates supported features with +V-EIOINTC. Feature EXTIOI_HAS_INT_ENCODE and EXTIOI_HAS_CPU_ENCODE is added. + +Feature EXTIOI_HAS_INT_ENCODE is part of standard EIOINTC. If it is 1, it +indicates that CPU Interrupt Pin selection can be normal method rather than +bitmap method, so interrupt can be routed to IP0 - IP15. + +Feature EXTIOI_HAS_CPU_ENCODE is entension of V-EIOINTC. If it is 1, it +indicates that CPU selection can be normal method rather than bitmap method, +so interrupt can be routed to CPU0 - CPU255. + +EXTIOI_VIRT_CONFIG +------------------ +This register is read-write register, for compatibility intterupt routed uses +the default method which is the same with standard EIOINTC. If the bit is set +with 1, it indicated HW to use normal method rather than bitmap method. + Advanced Extended IRQ model =========================== diff --git a/Documentation/arch/riscv/hwprobe.rst b/Documentation/arch/riscv/hwprobe.rst index 85b709257918a..955fbcd19ce90 100644 --- a/Documentation/arch/riscv/hwprobe.rst +++ b/Documentation/arch/riscv/hwprobe.rst @@ -239,6 +239,9 @@ The following keys are defined: ratified in commit 98918c844281 ("Merge pull request #1217 from riscv/zawrs") of riscv-isa-manual. + * :c:macro:`RISCV_HWPROBE_EXT_SUPM`: The Supm extension is supported as + defined in version 1.0 of the RISC-V Pointer Masking extensions. + * :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: Deprecated. Returns similar values to :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was mistakenly classified as a bitmask rather than a value. @@ -274,3 +277,19 @@ The following keys are defined: represent the highest userspace virtual address usable. * :c:macro:`RISCV_HWPROBE_KEY_TIME_CSR_FREQ`: Frequency (in Hz) of `time CSR`. + +* :c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF`: An enum value describing the + performance of misaligned vector accesses on the selected set of processors. + + * :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN`: The performance of misaligned + vector accesses is unknown. + + * :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW`: 32-bit misaligned accesses using vector + registers are slower than the equivalent quantity of byte accesses via vector registers. + Misaligned accesses may be supported directly in hardware, or trapped and emulated by software. + + * :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_FAST`: 32-bit misaligned accesses using vector + registers are faster than the equivalent quantity of byte accesses via vector registers. + + * :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED`: Misaligned vector accesses are + not supported at all and will generate a misaligned address fault. diff --git a/Documentation/arch/riscv/uabi.rst b/Documentation/arch/riscv/uabi.rst index 2b420bab0527a..243e40062e34e 100644 --- a/Documentation/arch/riscv/uabi.rst +++ b/Documentation/arch/riscv/uabi.rst @@ -68,3 +68,19 @@ Misaligned accesses Misaligned scalar accesses are supported in userspace, but they may perform poorly. Misaligned vector accesses are only supported if the Zicclsm extension is supported. + +Pointer masking +--------------- + +Support for pointer masking in userspace (the Supm extension) is provided via +the ``PR_SET_TAGGED_ADDR_CTRL`` and ``PR_GET_TAGGED_ADDR_CTRL`` ``prctl()`` +operations. Pointer masking is disabled by default. To enable it, userspace +must call ``PR_SET_TAGGED_ADDR_CTRL`` with the ``PR_PMLEN`` field set to the +number of mask/tag bits needed by the application. ``PR_PMLEN`` is interpreted +as a lower bound; if the kernel is unable to satisfy the request, the +``PR_SET_TAGGED_ADDR_CTRL`` operation will fail. The actual number of tag bits +is returned in ``PR_PMLEN`` by the ``PR_GET_TAGGED_ADDR_CTRL`` operation. + +Additionally, when pointer masking is enabled (``PR_PMLEN`` is greater than 0), +a tagged address ABI is supported, with the same interface and behavior as +documented for AArch64 (Documentation/arch/arm64/tagged-address-abi.rst). diff --git a/Documentation/arch/x86/boot.rst b/Documentation/arch/x86/boot.rst index 4fd492cb49704..ad2d8ddad27fe 100644 --- a/Documentation/arch/x86/boot.rst +++ b/Documentation/arch/x86/boot.rst @@ -896,10 +896,19 @@ Offset/size: 0x260/4 The kernel runtime start address is determined by the following algorithm:: - if (relocatable_kernel) - runtime_start = align_up(load_address, kernel_alignment) - else - runtime_start = pref_address + if (relocatable_kernel) { + if (load_address < pref_address) + load_address = pref_address; + runtime_start = align_up(load_address, kernel_alignment); + } else { + runtime_start = pref_address; + } + +Hence the necessary memory window location and size can be estimated by +a boot loader as:: + + memory_window_start = runtime_start; + memory_window_size = init_size; ============ =============== Field name: handover_offset diff --git a/Documentation/core-api/gfp_mask-from-fs-io.rst b/Documentation/core-api/gfp_mask-from-fs-io.rst index e7c32a8de126a..858b2fbcb36c7 100644 --- a/Documentation/core-api/gfp_mask-from-fs-io.rst +++ b/Documentation/core-api/gfp_mask-from-fs-io.rst @@ -55,14 +55,16 @@ scope. What about __vmalloc(GFP_NOFS) ============================== -vmalloc doesn't support GFP_NOFS semantic because there are hardcoded -GFP_KERNEL allocations deep inside the allocator which are quite non-trivial -to fix up. That means that calling ``vmalloc`` with GFP_NOFS/GFP_NOIO is -almost always a bug. The good news is that the NOFS/NOIO semantic can be -achieved by the scope API. +Since v5.17, and specifically after the commit 451769ebb7e79 ("mm/vmalloc: +alloc GFP_NO{FS,IO} for vmalloc"), GFP_NOFS/GFP_NOIO are now supported in +``[k]vmalloc`` by implicitly using scope API. + +In earlier kernels ``vmalloc`` didn't support GFP_NOFS semantic because there +were hardcoded GFP_KERNEL allocations deep inside the allocator. That means +that calling ``vmalloc`` with GFP_NOFS/GFP_NOIO was almost always a bug. In the ideal world, upper layers should already mark dangerous contexts -and so no special care is required and vmalloc should be called without -any problems. Sometimes if the context is not really clear or there are -layering violations then the recommended way around that is to wrap ``vmalloc`` -by the scope API with a comment explaining the problem. +and so no special care is required and ``vmalloc`` should be called without any +problems. Sometimes if the context is not really clear or there are layering +violations then the recommended way around that (on pre-v5.17 kernels) is to +wrap ``vmalloc`` by the scope API with a comment explaining the problem. diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst index 6a875743dd4b7..563b8fc0002f7 100644 --- a/Documentation/core-api/index.rst +++ b/Documentation/core-api/index.rst @@ -52,6 +52,7 @@ Library functionality that is used throughout the kernel. wrappers/atomic_bitops floating-point union_find + min_heap Low level entry and exit ======================== diff --git a/Documentation/core-api/min_heap.rst b/Documentation/core-api/min_heap.rst new file mode 100644 index 0000000000000..0c636c8b7aa58 --- /dev/null +++ b/Documentation/core-api/min_heap.rst @@ -0,0 +1,300 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============ +Min Heap API +============ + +Introduction +============ + +The Min Heap API provides a set of functions and macros for managing min-heaps +in the Linux kernel. A min-heap is a binary tree structure where the value of +each node is less than or equal to the values of its children, ensuring that +the smallest element is always at the root. + +This document provides a guide to the Min Heap API, detailing how to define and +use min-heaps. Users should not directly call functions with **__min_heap_*()** +prefixes, but should instead use the provided macro wrappers. + +In addition to the standard version of the functions, the API also includes a +set of inline versions for performance-critical scenarios. These inline +functions have the same names as their non-inline counterparts but include an +**_inline** suffix. For example, **__min_heap_init_inline** and its +corresponding macro wrapper **min_heap_init_inline**. The inline versions allow +custom comparison and swap functions to be called directly, rather than through +indirect function calls. This can significantly reduce overhead, especially +when CONFIG_MITIGATION_RETPOLINE is enabled, as indirect function calls become +more expensive. As with the non-inline versions, it is important to use the +macro wrappers for inline functions instead of directly calling the functions +themselves. + +Data Structures +=============== + +Min-Heap Definition +------------------- + +The core data structure for representing a min-heap is defined using the +**MIN_HEAP_PREALLOCATED** and **DEFINE_MIN_HEAP** macros. These macros allow +you to define a min-heap with a preallocated buffer or dynamically allocated +memory. + +Example: + +.. code-block:: c + + #define MIN_HEAP_PREALLOCATED(_type, _name, _nr) + struct _name { + int nr; /* Number of elements in the heap */ + int size; /* Maximum number of elements that can be held */ + _type *data; /* Pointer to the heap data */ + _type preallocated[_nr]; /* Static preallocated array */ + } + + #define DEFINE_MIN_HEAP(_type, _name) MIN_HEAP_PREALLOCATED(_type, _name, 0) + +A typical heap structure will include a counter for the number of elements +(`nr`), the maximum capacity of the heap (`size`), and a pointer to an array of +elements (`data`). Optionally, you can specify a static array for preallocated +heap storage using **MIN_HEAP_PREALLOCATED**. + +Min Heap Callbacks +------------------ + +The **struct min_heap_callbacks** provides customization options for ordering +elements in the heap and swapping them. It contains two function pointers: + +.. code-block:: c + + struct min_heap_callbacks { + bool (*less)(const void *lhs, const void *rhs, void *args); + void (*swp)(void *lhs, void *rhs, void *args); + }; + +- **less** is the comparison function used to establish the order of elements. +- **swp** is a function for swapping elements in the heap. If swp is set to + NULL, the default swap function will be used, which swaps the elements based on their size + +Macro Wrappers +============== + +The following macro wrappers are provided for interacting with the heap in a +user-friendly manner. Each macro corresponds to a function that operates on the +heap, and they abstract away direct calls to internal functions. + +Each macro accepts various parameters that are detailed below. + +Heap Initialization +-------------------- + +.. code-block:: c + + min_heap_init(heap, data, size); + +- **heap**: A pointer to the min-heap structure to be initialized. +- **data**: A pointer to the buffer where the heap elements will be stored. If + `NULL`, the preallocated buffer within the heap structure will be used. +- **size**: The maximum number of elements the heap can hold. + +This macro initializes the heap, setting its initial state. If `data` is +`NULL`, the preallocated memory inside the heap structure will be used for +storage. Otherwise, the user-provided buffer is used. The operation is **O(1)**. + +**Inline Version:** min_heap_init_inline(heap, data, size) + +Accessing the Top Element +------------------------- + +.. code-block:: c + + element = min_heap_peek(heap); + +- **heap**: A pointer to the min-heap from which to retrieve the smallest + element. + +This macro returns a pointer to the smallest element (the root) of the heap, or +`NULL` if the heap is empty. The operation is **O(1)**. + +**Inline Version:** min_heap_peek_inline(heap) + +Heap Insertion +-------------- + +.. code-block:: c + + success = min_heap_push(heap, element, callbacks, args); + +- **heap**: A pointer to the min-heap into which the element should be inserted. +- **element**: A pointer to the element to be inserted into the heap. +- **callbacks**: A pointer to a `struct min_heap_callbacks` providing the + `less` and `swp` functions. +- **args**: Optional arguments passed to the `less` and `swp` functions. + +This macro inserts an element into the heap. It returns `true` if the insertion +was successful and `false` if the heap is full. The operation is **O(log n)**. + +**Inline Version:** min_heap_push_inline(heap, element, callbacks, args) + +Heap Removal +------------ + +.. code-block:: c + + success = min_heap_pop(heap, callbacks, args); + +- **heap**: A pointer to the min-heap from which to remove the smallest element. +- **callbacks**: A pointer to a `struct min_heap_callbacks` providing the + `less` and `swp` functions. +- **args**: Optional arguments passed to the `less` and `swp` functions. + +This macro removes the smallest element (the root) from the heap. It returns +`true` if the element was successfully removed, or `false` if the heap is +empty. The operation is **O(log n)**. + +**Inline Version:** min_heap_pop_inline(heap, callbacks, args) + +Heap Maintenance +---------------- + +You can use the following macros to maintain the heap's structure: + +.. code-block:: c + + min_heap_sift_down(heap, pos, callbacks, args); + +- **heap**: A pointer to the min-heap. +- **pos**: The index from which to start sifting down. +- **callbacks**: A pointer to a `struct min_heap_callbacks` providing the + `less` and `swp` functions. +- **args**: Optional arguments passed to the `less` and `swp` functions. + +This macro restores the heap property by moving the element at the specified +index (`pos`) down the heap until it is in the correct position. The operation +is **O(log n)**. + +**Inline Version:** min_heap_sift_down_inline(heap, pos, callbacks, args) + +.. code-block:: c + + min_heap_sift_up(heap, idx, callbacks, args); + +- **heap**: A pointer to the min-heap. +- **idx**: The index of the element to sift up. +- **callbacks**: A pointer to a `struct min_heap_callbacks` providing the + `less` and `swp` functions. +- **args**: Optional arguments passed to the `less` and `swp` functions. + +This macro restores the heap property by moving the element at the specified +index (`idx`) up the heap. The operation is **O(log n)**. + +**Inline Version:** min_heap_sift_up_inline(heap, idx, callbacks, args) + +.. code-block:: c + + min_heapify_all(heap, callbacks, args); + +- **heap**: A pointer to the min-heap. +- **callbacks**: A pointer to a `struct min_heap_callbacks` providing the + `less` and `swp` functions. +- **args**: Optional arguments passed to the `less` and `swp` functions. + +This macro ensures that the entire heap satisfies the heap property. It is +called when the heap is built from scratch or after many modifications. The +operation is **O(n)**. + +**Inline Version:** min_heapify_all_inline(heap, callbacks, args) + +Removing Specific Elements +-------------------------- + +.. code-block:: c + + success = min_heap_del(heap, idx, callbacks, args); + +- **heap**: A pointer to the min-heap. +- **idx**: The index of the element to delete. +- **callbacks**: A pointer to a `struct min_heap_callbacks` providing the + `less` and `swp` functions. +- **args**: Optional arguments passed to the `less` and `swp` functions. + +This macro removes an element at the specified index (`idx`) from the heap and +restores the heap property. The operation is **O(log n)**. + +**Inline Version:** min_heap_del_inline(heap, idx, callbacks, args) + +Other Utilities +=============== + +- **min_heap_full(heap)**: Checks whether the heap is full. + Complexity: **O(1)**. + +.. code-block:: c + + bool full = min_heap_full(heap); + +- `heap`: A pointer to the min-heap to check. + +This macro returns `true` if the heap is full, otherwise `false`. + +**Inline Version:** min_heap_full_inline(heap) + +- **min_heap_empty(heap)**: Checks whether the heap is empty. + Complexity: **O(1)**. + +.. code-block:: c + + bool empty = min_heap_empty(heap); + +- `heap`: A pointer to the min-heap to check. + +This macro returns `true` if the heap is empty, otherwise `false`. + +**Inline Version:** min_heap_empty_inline(heap) + +Example Usage +============= + +An example usage of the min-heap API would involve defining a heap structure, +initializing it, and inserting and removing elements as needed. + +.. code-block:: c + + #include + + int my_less_function(const void *lhs, const void *rhs, void *args) { + return (*(int *)lhs < *(int *)rhs); + } + + struct min_heap_callbacks heap_cb = { + .less = my_less_function, /* Comparison function for heap order */ + .swp = NULL, /* Use default swap function */ + }; + + void example_usage(void) { + /* Pre-populate the buffer with elements */ + int buffer[5] = {5, 2, 8, 1, 3}; + /* Declare a min-heap */ + DEFINE_MIN_HEAP(int, my_heap); + + /* Initialize the heap with preallocated buffer and size */ + min_heap_init(&my_heap, buffer, 5); + + /* Build the heap using min_heapify_all */ + my_heap.nr = 5; /* Set the number of elements in the heap */ + min_heapify_all(&my_heap, &heap_cb, NULL); + + /* Peek at the top element (should be 1 in this case) */ + int *top = min_heap_peek(&my_heap); + pr_info("Top element: %d\n", *top); + + /* Pop the top element (1) and get the new top (2) */ + min_heap_pop(&my_heap, &heap_cb, NULL); + top = min_heap_peek(&my_heap); + pr_info("New top element: %d\n", *top); + + /* Insert a new element (0) and recheck the top */ + int new_element = 0; + min_heap_push(&my_heap, &new_element, &heap_cb, NULL); + top = min_heap_peek(&my_heap); + pr_info("Top element after insertion: %d\n", *top); + } diff --git a/Documentation/dev-tools/autofdo.rst b/Documentation/dev-tools/autofdo.rst new file mode 100644 index 0000000000000..1f0a451e9ccd3 --- /dev/null +++ b/Documentation/dev-tools/autofdo.rst @@ -0,0 +1,168 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================== +Using AutoFDO with the Linux kernel +=================================== + +This enables AutoFDO build support for the kernel when using +the Clang compiler. AutoFDO (Auto-Feedback-Directed Optimization) +is a type of profile-guided optimization (PGO) used to enhance the +performance of binary executables. It gathers information about the +frequency of execution of various code paths within a binary using +hardware sampling. This data is then used to guide the compiler's +optimization decisions, resulting in a more efficient binary. AutoFDO +is a powerful optimization technique, and data indicates that it can +significantly improve kernel performance. It's especially beneficial +for workloads affected by front-end stalls. + +For AutoFDO builds, unlike non-FDO builds, the user must supply a +profile. Acquiring an AutoFDO profile can be done in several ways. +AutoFDO profiles are created by converting hardware sampling using +the "perf" tool. It is crucial that the workload used to create these +perf files is representative; they must exhibit runtime +characteristics similar to the workloads that are intended to be +optimized. Failure to do so will result in the compiler optimizing +for the wrong objective. + +The AutoFDO profile often encapsulates the program's behavior. If the +performance-critical codes are architecture-independent, the profile +can be applied across platforms to achieve performance gains. For +instance, using the profile generated on Intel architecture to build +a kernel for AMD architecture can also yield performance improvements. + +There are two methods for acquiring a representative profile: +(1) Sample real workloads using a production environment. +(2) Generate the profile using a representative load test. +When enabling the AutoFDO build configuration without providing an +AutoFDO profile, the compiler only modifies the dwarf information in +the kernel without impacting runtime performance. It's advisable to +use a kernel binary built with the same AutoFDO configuration to +collect the perf profile. While it's possible to use a kernel built +with different options, it may result in inferior performance. + +One can collect profiles using AutoFDO build for the previous kernel. +AutoFDO employs relative line numbers to match the profiles, offering +some tolerance for source changes. This mode is commonly used in a +production environment for profile collection. + +In a profile collection based on a load test, the AutoFDO collection +process consists of the following steps: + +#. Initial build: The kernel is built with AutoFDO options + without a profile. + +#. Profiling: The above kernel is then run with a representative + workload to gather execution frequency data. This data is + collected using hardware sampling, via perf. AutoFDO is most + effective on platforms supporting advanced PMU features like + LBR on Intel machines. + +#. AutoFDO profile generation: Perf output file is converted to + the AutoFDO profile via offline tools. + +The support requires a Clang compiler LLVM 17 or later. + +Preparation +=========== + +Configure the kernel with:: + + CONFIG_AUTOFDO_CLANG=y + +Customization +============= + +The default CONFIG_AUTOFDO_CLANG setting covers kernel space objects for +AutoFDO builds. One can, however, enable or disable AutoFDO build for +individual files and directories by adding a line similar to the following +to the respective kernel Makefile: + +- For enabling a single file (e.g. foo.o) :: + + AUTOFDO_PROFILE_foo.o := y + +- For enabling all files in one directory :: + + AUTOFDO_PROFILE := y + +- For disabling one file :: + + AUTOFDO_PROFILE_foo.o := n + +- For disabling all files in one directory :: + + AUTOFDO_PROFILE := n + +Workflow +======== + +Here is an example workflow for AutoFDO kernel: + +1) Build the kernel on the host machine with LLVM enabled, + for example, :: + + $ make menuconfig LLVM=1 + + Turn on AutoFDO build config:: + + CONFIG_AUTOFDO_CLANG=y + + With a configuration that with LLVM enabled, use the following command:: + + $ scripts/config -e AUTOFDO_CLANG + + After getting the config, build with :: + + $ make LLVM=1 + +2) Install the kernel on the test machine. + +3) Run the load tests. The '-c' option in perf specifies the sample + event period. We suggest using a suitable prime number, like 500009, + for this purpose. + + - For Intel platforms:: + + $ perf record -e BR_INST_RETIRED.NEAR_TAKEN:k -a -N -b -c -o -- + + - For AMD platforms: + + The supported systems are: Zen3 with BRS, or Zen4 with amd_lbr_v2. To check, + + For Zen3:: + + $ cat proc/cpuinfo | grep " brs" + + For Zen4:: + + $ cat proc/cpuinfo | grep amd_lbr_v2 + + The following command generated the perf data file:: + + $ perf record --pfm-events RETIRED_TAKEN_BRANCH_INSTRUCTIONS:k -a -N -b -c -o -- + +4) (Optional) Download the raw perf file to the host machine. + +5) To generate an AutoFDO profile, two offline tools are available: + create_llvm_prof and llvm_profgen. The create_llvm_prof tool is part + of the AutoFDO project and can be found on GitHub + (https://github.com/google/autofdo), version v0.30.1 or later. + The llvm_profgen tool is included in the LLVM compiler itself. It's + important to note that the version of llvm_profgen doesn't need to match + the version of Clang. It needs to be the LLVM 19 release of Clang + or later, or just from the LLVM trunk. :: + + $ llvm-profgen --kernel --binary= --perfdata= -o + + or :: + + $ create_llvm_prof --binary= --profile= --format=extbinary --out= + + Note that multiple AutoFDO profile files can be merged into one via:: + + $ llvm-profdata merge -o ... + +6) Rebuild the kernel using the AutoFDO profile file with the same config as step 1, + (Note CONFIG_AUTOFDO_CLANG needs to be enabled):: + + $ make LLVM=1 CLANG_AUTOFDO_PROFILE= diff --git a/Documentation/dev-tools/coccinelle.rst b/Documentation/dev-tools/coccinelle.rst index 535ce126fb4fa..6e70a1e9a3c0e 100644 --- a/Documentation/dev-tools/coccinelle.rst +++ b/Documentation/dev-tools/coccinelle.rst @@ -250,25 +250,17 @@ variables for .cocciconfig is as follows: - Your directory from which spatch is called is processed next - The directory provided with the ``--dir`` option is processed last, if used -Since coccicheck runs through make, it naturally runs from the kernel -proper dir; as such the second rule above would be implied for picking up a -.cocciconfig when using ``make coccicheck``. - ``make coccicheck`` also supports using M= targets. If you do not supply any M= target, it is assumed you want to target the entire kernel. The kernel coccicheck script has:: - if [ "$KBUILD_EXTMOD" = "" ] ; then - OPTIONS="--dir $srctree $COCCIINCLUDE" - else - OPTIONS="--dir $KBUILD_EXTMOD $COCCIINCLUDE" - fi - -KBUILD_EXTMOD is set when an explicit target with M= is used. For both cases -the spatch ``--dir`` argument is used, as such third rule applies when whether -M= is used or not, and when M= is used the target directory can have its own -.cocciconfig file. When M= is not passed as an argument to coccicheck the -target directory is the same as the directory from where spatch was called. + OPTIONS="--dir $srcroot $COCCIINCLUDE" + +Here, $srcroot refers to the source directory of the target: it points to the +external module's source directory when M= used, and otherwise, to the kernel +source directory. The third rule ensures the spatch reads the .cocciconfig from +the target directory, allowing external modules to have their own .cocciconfig +file. If not using the kernel's coccicheck target, keep the above precedence order logic of .cocciconfig reading. If using the kernel's coccicheck target, diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst index 53d4d124f9c52..3c0ac08b27091 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -34,6 +34,8 @@ Documentation/dev-tools/testing-overview.rst ktap checkuapi gpio-sloppy-logic-analyzer + autofdo + propeller .. only:: subproject and html diff --git a/Documentation/dev-tools/propeller.rst b/Documentation/dev-tools/propeller.rst new file mode 100644 index 0000000000000..92195958e3dbc --- /dev/null +++ b/Documentation/dev-tools/propeller.rst @@ -0,0 +1,162 @@ +.. SPDX-License-Identifier: GPL-2.0 + +===================================== +Using Propeller with the Linux kernel +===================================== + +This enables Propeller build support for the kernel when using Clang +compiler. Propeller is a profile-guided optimization (PGO) method used +to optimize binary executables. Like AutoFDO, it utilizes hardware +sampling to gather information about the frequency of execution of +different code paths within a binary. Unlike AutoFDO, this information +is then used right before linking phase to optimize (among others) +block layout within and across functions. + +A few important notes about adopting Propeller optimization: + +#. Although it can be used as a standalone optimization step, it is + strongly recommended to apply Propeller on top of AutoFDO, + AutoFDO+ThinLTO or Instrument FDO. The rest of this document + assumes this paradigm. + +#. Propeller uses another round of profiling on top of + AutoFDO/AutoFDO+ThinLTO/iFDO. The whole build process involves + "build-afdo - train-afdo - build-propeller - train-propeller - + build-optimized". + +#. Propeller requires LLVM 19 release or later for Clang/Clang++ + and the linker(ld.lld). + +#. In addition to LLVM toolchain, Propeller requires a profiling + conversion tool: https://github.com/google/autofdo with a release + after v0.30.1: https://github.com/google/autofdo/releases/tag/v0.30.1. + +The Propeller optimization process involves the following steps: + +#. Initial building: Build the AutoFDO or AutoFDO+ThinLTO binary as + you would normally do, but with a set of compile-time / link-time + flags, so that a special metadata section is created within the + kernel binary. The special section is only intend to be used by the + profiling tool, it is not part of the runtime image, nor does it + change kernel run time text sections. + +#. Profiling: The above kernel is then run with a representative + workload to gather execution frequency data. This data is collected + using hardware sampling, via perf. Propeller is most effective on + platforms supporting advanced PMU features like LBR on Intel + machines. This step is the same as profiling the kernel for AutoFDO + (the exact perf parameters can be different). + +#. Propeller profile generation: Perf output file is converted to a + pair of Propeller profiles via an offline tool. + +#. Optimized build: Build the AutoFDO or AutoFDO+ThinLTO optimized + binary as you would normally do, but with a compile-time / + link-time flag to pick up the Propeller compile time and link time + profiles. This build step uses 3 profiles - the AutoFDO profile, + the Propeller compile-time profile and the Propeller link-time + profile. + +#. Deployment: The optimized kernel binary is deployed and used + in production environments, providing improved performance + and reduced latency. + +Preparation +=========== + +Configure the kernel with:: + + CONFIG_AUTOFDO_CLANG=y + CONFIG_PROPELLER_CLANG=y + +Customization +============= + +The default CONFIG_PROPELLER_CLANG setting covers kernel space objects +for Propeller builds. One can, however, enable or disable Propeller build +for individual files and directories by adding a line similar to the +following to the respective kernel Makefile: + +- For enabling a single file (e.g. foo.o):: + + PROPELLER_PROFILE_foo.o := y + +- For enabling all files in one directory:: + + PROPELLER_PROFILE := y + +- For disabling one file:: + + PROPELLER_PROFILE_foo.o := n + +- For disabling all files in one directory:: + + PROPELLER__PROFILE := n + + +Workflow +======== + +Here is an example workflow for building an AutoFDO+Propeller kernel: + +1) Assuming an AutoFDO profile is already collected following + instructions in the AutoFDO document, build the kernel on the host + machine, with AutoFDO and Propeller build configs :: + + CONFIG_AUTOFDO_CLANG=y + CONFIG_PROPELLER_CLANG=y + + and :: + + $ make LLVM=1 CLANG_AUTOFDO_PROFILE= + +2) Install the kernel on the test machine. + +3) Run the load tests. The '-c' option in perf specifies the sample + event period. We suggest using a suitable prime number, like 500009, + for this purpose. + + - For Intel platforms:: + + $ perf record -e BR_INST_RETIRED.NEAR_TAKEN:k -a -N -b -c -o -- + + - For AMD platforms:: + + $ perf record --pfm-event RETIRED_TAKEN_BRANCH_INSTRUCTIONS:k -a -N -b -c -o -- + + Note you can repeat the above steps to collect multiple s. + +4) (Optional) Download the raw perf file(s) to the host machine. + +5) Use the create_llvm_prof tool (https://github.com/google/autofdo) to + generate Propeller profile. :: + + $ create_llvm_prof --binary= --profile= + --format=propeller --propeller_output_module_name + --out=_cc_profile.txt + --propeller_symorder=_ld_profile.txt + + "" can be something like "/home/user/dir/any_string". + + This command generates a pair of Propeller profiles: + "_cc_profile.txt" and + "_ld_profile.txt". + + If there are more than 1 perf_file collected in the previous step, + you can create a temp list file "" with each line + containing one perf file name and run:: + + $ create_llvm_prof --binary= --profile=@ + --format=propeller --propeller_output_module_name + --out=_cc_profile.txt + --propeller_symorder=_ld_profile.txt + +6) Rebuild the kernel using the AutoFDO and Propeller + profiles. :: + + CONFIG_AUTOFDO_CLANG=y + CONFIG_PROPELLER_CLANG=y + + and :: + + $ make LLVM=1 CLANG_AUTOFDO_PROFILE= CLANG_PROPELLER_PROFILE_PREFIX= diff --git a/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml b/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml new file mode 100644 index 0000000000000..67c449d804c22 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/airoha,en7581-chip-scu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha Chip SCU Controller for EN7581 SoC + +maintainers: + - Lorenzo Bianconi + +description: + The airoha chip-scu block provides a configuration interface for clock, + io-muxing and other functionalities used by multiple controllers (e.g. clock, + pinctrl, ecc) on EN7581 SoC. + +properties: + compatible: + items: + - enum: + - airoha,en7581-chip-scu + - const: syscon + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + syscon@1fa20000 { + compatible = "airoha,en7581-chip-scu", "syscon"; + reg = <0x0 0x1fa20000 0x0 0x388>; + }; + }; diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index fb216ce68bb35..67700440e23b5 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -253,6 +253,46 @@ properties: additionalProperties: false + sink-wait-cap-time-ms: + description: Represents the max time in ms that USB Type-C port (in sink + role) should wait for the port partner (source role) to send source caps. + SinkWaitCap timer starts when port in sink role attaches to the source. + This timer will stop when sink receives PD source cap advertisement before + timeout in which case it'll move to capability negotiation stage. A + timeout leads to a hard reset message by the port. + minimum: 310 + maximum: 620 + default: 310 + + ps-source-off-time-ms: + description: Represents the max time in ms that a DRP in source role should + take to turn off power after the PsSourceOff timer starts. PsSourceOff + timer starts when a sink's PHY layer receives EOP of the GoodCRC message + (corresponding to an Accept message sent in response to a PR_Swap or a + FR_Swap request). This timer stops when last bit of GoodCRC EOP + corresponding to the received PS_RDY message is transmitted by the PHY + layer. A timeout shall lead to error recovery in the type-c port. + minimum: 750 + maximum: 920 + default: 920 + + cc-debounce-time-ms: + description: Represents the max time in ms that a port shall wait to + determine if it's attached to a partner. + minimum: 100 + maximum: 200 + default: 200 + + sink-bc12-completion-time-ms: + description: Represents the max time in ms that a port in sink role takes + to complete Battery Charger (BC1.2) Detection. BC1.2 detection is a + hardware mechanism, which in some TCPC implementations, can run in + parallel once the Type-C connection state machine reaches the "potential + connect as sink" state. In TCPCs where this causes delays to respond to + the incoming PD messages, sink-bc12-completion-time-ms is used to delay + PD negotiation till BC1.2 detection completes. + default: 0 + dependencies: sink-vdos-v1: [ sink-vdos ] sink-vdos: [ sink-vdos-v1 ] @@ -380,7 +420,7 @@ examples: }; # USB-C connector attached to a typec port controller(ptn5110), which has - # power delivery support and enables drp. + # power delivery support, explicitly defines time properties and enables drp. - | #include typec: ptn5110 { @@ -393,6 +433,10 @@ examples: sink-pdos = ; op-sink-microwatt = <10000000>; + sink-wait-cap-time-ms = <465>; + ps-source-off-time-ms = <835>; + cc-debounce-time-ms = <101>; + sink-bc12-completion-time-ms = <500>; }; }; diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml index 4df4e61895d21..4ad56a409b9ca 100644 --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml @@ -26,6 +26,7 @@ properties: - enum: - qcom,qcm2290-gpi-dma - qcom,qdu1000-gpi-dma + - qcom,sar2130p-gpi-dma - qcom,sc7280-gpi-dma - qcom,sdx75-gpi-dma - qcom,sm6115-gpi-dma diff --git a/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml b/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml index ca24cf48769f6..b356251de5a82 100644 --- a/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml @@ -4,18 +4,16 @@ $id: http://devicetree.org/schemas/dma/renesas,rz-dmac.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Renesas RZ/{G2L,G2UL,V2L} DMA Controller +title: Renesas RZ DMA Controller maintainers: - Biju Das -allOf: - - $ref: dma-controller.yaml# - properties: compatible: items: - enum: + - renesas,r7s72100-dmac # RZ/A1H - renesas,r9a07g043-dmac # RZ/G2UL and RZ/Five - renesas,r9a07g044-dmac # RZ/G2{L,LC} - renesas,r9a07g054-dmac # RZ/V2L @@ -93,13 +91,26 @@ required: - reg - interrupts - interrupt-names - - clocks - - clock-names - '#dma-cells' - dma-channels - - power-domains - - resets - - reset-names + +allOf: + - $ref: dma-controller.yaml# + + - if: + not: + properties: + compatible: + contains: + enum: + - renesas,r7s72100-dmac + then: + required: + - clocks + - clock-names + - power-domains + - resets + - reset-names additionalProperties: false diff --git a/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml b/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml index 3b22183a1a379..609e38901434a 100644 --- a/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml +++ b/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml @@ -27,11 +27,16 @@ allOf: properties: compatible: - items: - - enum: - - microchip,mpfs-pdma - - sifive,fu540-c000-pdma - - const: sifive,pdma0 + oneOf: + - items: + - const: microchip,pic64gx-pdma + - const: microchip,mpfs-pdma + - const: sifive,pdma0 + - items: + - enum: + - microchip,mpfs-pdma + - sifive,fu540-c000-pdma + - const: sifive,pdma0 description: Should be "sifive,-pdma" and "sifive,pdma". Supported compatible strings are - diff --git a/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma3.yaml b/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma3.yaml index 7fdc44b2e6467..36f9fe860eb99 100644 --- a/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma3.yaml +++ b/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma3.yaml @@ -96,6 +96,12 @@ properties: including the update of the LLI if any 0x3: at channel level, the transfer complete event is generated at the end of the last LLI + -bit 16: Prevent packing/unpacking mode + 0x0: pack/unpack enabled when source data width/burst != destination data width/burst + 0x1: memory data width/burst forced to peripheral data width/burst to prevent pack/unpack + -bit 17: Prevent additional transfers due to linked-list refactoring + 0x0: don't prevent additional transfers for optimal performance + 0x1: prevent additional transfer to accommodate user constraints such as single transfer required: - compatible diff --git a/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml b/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml index 984fc1ed3ec6a..c4efcef591337 100644 --- a/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml @@ -33,6 +33,7 @@ properties: - allwinner,sun50i-a100-i2c - allwinner,sun50i-h616-i2c - allwinner,sun50i-r329-i2c + - allwinner,sun55i-a523-i2c - const: allwinner,sun8i-v536-i2c - const: allwinner,sun6i-a31-i2c - const: marvell,mv64xxx-i2c diff --git a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml index 60035a787e5c0..e5d05263c45a5 100644 --- a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml @@ -97,6 +97,22 @@ properties: - const: tx - const: rx + snps,bus-capacitance-pf: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + This property indicates the bus capacitance in picofarads (pF). + This value is used to compute the tHIGH and tLOW periods for high speed + mode. + enum: [100, 400] + default: 100 + + snps,clk-freq-optimized: + description: + This property indicates whether the hardware reduce its clock frequency + by reducing the internal latency required to generate the high period and + low period of SCL line. + type: boolean + unevaluatedProperties: false required: @@ -121,6 +137,8 @@ examples: i2c-sda-hold-time-ns = <300>; i2c-sda-falling-time-ns = <300>; i2c-scl-falling-time-ns = <300>; + snps,bus-capacitance-pf = <400>; + snps,clk-freq-optimized; }; - | i2c@2000 { diff --git a/Documentation/devicetree/bindings/i2c/st,nomadik-i2c.yaml b/Documentation/devicetree/bindings/i2c/st,nomadik-i2c.yaml index 44c54b162bb10..012402debfeb2 100644 --- a/Documentation/devicetree/bindings/i2c/st,nomadik-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/st,nomadik-i2c.yaml @@ -22,6 +22,7 @@ select: enum: - st,nomadik-i2c - mobileye,eyeq5-i2c + - mobileye,eyeq6h-i2c required: - compatible @@ -29,15 +30,15 @@ properties: compatible: oneOf: - items: - - const: st,nomadik-i2c + - enum: + - st,nomadik-i2c + - mobileye,eyeq5-i2c + - mobileye,eyeq6h-i2c - const: arm,primecell - items: - const: stericsson,db8500-i2c - const: st,nomadik-i2c - const: arm,primecell - - items: - - const: mobileye,eyeq5-i2c - - const: arm,primecell reg: maxItems: 1 @@ -54,7 +55,7 @@ properties: - items: - const: mclk - const: apb_pclk - # Clock name in DB8500 or EyeQ5 + # Clock name in DB8500 or EyeQ - items: - const: i2cclk - const: apb_pclk @@ -67,7 +68,7 @@ properties: clock-frequency: minimum: 1 - maximum: 400000 + maximum: 3400000 mobileye,olb: $ref: /schemas/types.yaml#/definitions/phandle-array diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml index 8e7835cf36fd2..b9bc02b5b07a4 100644 --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml @@ -37,6 +37,10 @@ properties: to both the positive and negative inputs of a differential ADC. The first value specifies the positive input pin, the second specifies the negative input pin. + There are also some ADCs, where the differential channel has dedicated + positive and negative inputs which can be used to measure differential + voltage levels. For those setups, this property can be configured with + the 'reg' property for both inputs (i.e. diff-channels = ). single-channel: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml index 17c5d39cc2c17..ad15cf9bc2ffd 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml @@ -28,6 +28,7 @@ description: | Datasheets for supported chips: https://www.analog.com/media/en/technical-documentation/data-sheets/AD4111.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD4112.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4114.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD4115.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD4116.pdf @@ -44,6 +45,7 @@ properties: enum: - adi,ad4111 - adi,ad4112 + - adi,ad4113 - adi,ad4114 - adi,ad4115 - adi,ad4116 @@ -331,6 +333,7 @@ allOf: enum: - adi,ad4111 - adi,ad4112 + - adi,ad4113 - adi,ad4114 - adi,ad4115 - adi,ad4116 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml index 0065d65088248..ada08005b3cd1 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml @@ -25,6 +25,8 @@ description: | * https://www.analog.com/en/products/ad7386-4.html * https://www.analog.com/en/products/ad7387-4.html * https://www.analog.com/en/products/ad7388-4.html + * https://www.analog.com/en/products/adaq4370-4.html + * https://www.analog.com/en/products/adaq4380-4.html $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -46,6 +48,8 @@ properties: - adi,ad7386-4 - adi,ad7387-4 - adi,ad7388-4 + - adi,adaq4370-4 + - adi,adaq4380-4 reg: maxItems: 1 @@ -70,6 +74,20 @@ properties: refin-supply: description: A 2.5V to 3.3V supply for external reference voltage, for ad7380-4 only. + For adaq devices, a 5V supply voltage. A 3.3V internal reference is + derived from it. Connect to vs-p-supply for normal operation. + + vs-p-supply: + description: + Amplifiers positive supply. + + vs-n-supply: + description: + Amplifiers negative supply. + + ldo-supply: + description: + LDO supply. Connect to vs-p-supply or a 3.6 to 5.5 V supply. aina-supply: description: @@ -97,12 +115,45 @@ properties: specify the ALERT interrupt. maxItems: 1 + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + required: - compatible - reg - vcc-supply - vlogic-supply +patternProperties: + "^channel@[0-3]$": + $ref: adc.yaml + type: object + + properties: + reg: + description: + The channel number. From 0 to 3 corresponding to channels A,B,C,D + minimum: 0 + maximum: 3 + + adi,gain-milli: + description: + The hardware gain applied to the ADC input (in milli units). + If not present, default to 1000 (no actual gain applied). + Refer to the typical connection diagrams section of the datasheet for + pin wiring. + $ref: /schemas/types.yaml#/definitions/uint16 + enum: [300, 600, 1000, 1600] + default: 1000 + + required: + - reg + + additionalProperties: false + unevaluatedProperties: false allOf: @@ -140,6 +191,7 @@ allOf: aind-supply: false # ad7380-4 uses refin-supply as external reference. + # adaq devices use internal reference only, derived from refin-supply # All other chips from ad738x family use refio as optional external reference. # When refio-supply is omitted, internal reference is used. - if: @@ -147,6 +199,8 @@ allOf: compatible: enum: - adi,ad7380-4 + - adi,adaq4370-4 + - adi,adaq4380-4 then: properties: refio-supply: false @@ -156,6 +210,27 @@ allOf: properties: refin-supply: false + # adaq devices need more supplies and using channel to declare gain property + # only applies to adaq devices + - if: + properties: + compatible: + enum: + - adi,adaq4370-4 + - adi,adaq4380-4 + then: + required: + - vs-p-supply + - vs-n-supply + - ldo-supply + else: + properties: + vs-p-supply: false + vs-n-supply: false + ldo-supply: false + patternProperties: + "^channel@[0-3]$": false + examples: - | #include @@ -180,3 +255,48 @@ examples: refio-supply = <&supply_2_5V>; }; }; + + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,adaq4380-4"; + reg = <0>; + + spi-cpol; + spi-cpha; + spi-max-frequency = <80000000>; + + interrupts = <27 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + vcc-supply = <&supply_3_3V>; + vlogic-supply = <&supply_3_3V>; + refin-supply = <&supply_5V>; + vs-p-supply = <&supply_5V>; + vs-n-supply = <&supply_0V>; + ldo-supply = <&supply_5V>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,gain-milli = /bits/ 16 <300>; + }; + + channel@2 { + reg = <2>; + adi,gain-milli = /bits/ 16 <600>; + }; + + channel@3 { + reg = <3>; + adi,gain-milli = /bits/ 16 <1000>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index 69408cae3db96..ab5881d0d017f 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -14,6 +14,11 @@ description: | https://www.analog.com/media/en/technical-documentation/data-sheets/AD7605-4.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/ad7606_7606-6_7606-4.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD7606B.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7606c-16.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7606c-18.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7607.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7608.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7609.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD7616.pdf properties: @@ -24,11 +29,27 @@ properties: - adi,ad7606-6 - adi,ad7606-8 # Referred to as AD7606 (without -8) in the datasheet - adi,ad7606b + - adi,ad7606c-16 + - adi,ad7606c-18 + - adi,ad7607 + - adi,ad7608 + - adi,ad7609 - adi,ad7616 reg: maxItems: 1 + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + # According to the datasheet, "Data is clocked in from SDI on the falling + # edge of SCLK, while data is clocked out on DOUTA on the rising edge of + # SCLK". Also, even if not stated textually in the datasheet, it is made + # clear on the diagrams that sclk idles at high. Subsequently, in case SPI + # interface is used, the correct way is to only set spi-cpol. spi-cpha: true spi-cpol: true @@ -114,18 +135,91 @@ properties: assumed that the pins are hardwired to VDD. type: boolean + pwms: + description: + In case the conversion is triggered by a PWM instead of a GPIO plugged to + the CONVST pin, the PWM must be referenced. + The first is the PWM connected to CONVST or CONVST1 for the chips with the + 2nd PWM connected to CONVST2, if CONVST2 is available and not shorted to + CONVST1. + minItems: 1 + maxItems: 2 + + pwm-names: + items: + - const: convst1 + - const: convst2 + + io-backends: + description: + A reference to the iio-backend, which is responsible handling the BUSY + pin's falling edge and communication. + An example of backend can be found at + http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html + + +patternProperties: + "^channel@[1-8]$": + type: object + $ref: adc.yaml + unevaluatedProperties: false + + properties: + reg: + description: + The channel number, as specified in the datasheet (from 1 to 8). + minimum: 1 + maximum: 8 + + diff-channels: + description: + Each channel can be configured as a bipolar differential channel. + The ADC uses the same positive and negative inputs for this. + This property must be specified as 'reg' (or the channel number) for + both positive and negative inputs (i.e. diff-channels = ). + Since the configuration is bipolar differential, the 'bipolar' + property is required. + items: + minimum: 1 + maximum: 8 + + bipolar: + description: + The ADC channels can be configured as + * Bipolar single-ended + * Unipolar single-ended + * Bipolar differential + Therefore in the DT, if no channel node is specified, it is considered + 'unipolar single-ended'. So for the other configurations the 'bipolar' + property must be specified. If 'diff-channels' is specified, it is + considered a bipolar differential channel. Otherwise it is bipolar + single-ended. + + required: + - reg + - bipolar + required: - compatible - reg - - spi-cpha - avcc-supply - vdrive-supply - - interrupts - - adi,conversion-start-gpios allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + - oneOf: + - required: + - adi,conversion-start-gpios + - required: + - pwms + + - oneOf: + - required: + - interrupts + - required: + - io-backends + - if: properties: compatible: @@ -162,17 +256,66 @@ allOf: - adi,ad7606-4 - adi,ad7606-6 - adi,ad7606-8 + - adi,ad7607 + - adi,ad7608 + - adi,ad7609 then: properties: adi,sw-mode: false else: properties: + pwms: + maxItems: 1 + pwm-names: + maxItems: 1 adi,conversion-start-gpios: maxItems: 1 + - if: + not: + required: + - adi,sw-mode + then: + patternProperties: + "^channel@[1-8]$": false + + - if: + not: + properties: + compatible: + enum: + - adi,ad7606c-16 + - adi,ad7606c-18 + then: + patternProperties: + "^channel@[1-8]$": false + unevaluatedProperties: false examples: + - | + #include + iio-backend { + #address-cells = <1>; + #size-cells = <0>; + adi_adc@0 { + compatible = "adi,ad7606b"; + reg = <0>; + pwms = <&axi_pwm_gen 0 0>; + + avcc-supply = <&adc_vref>; + vdrive-supply = <&vdd_supply>; + + reset-gpios = <&gpio0 91 GPIO_ACTIVE_HIGH>; + standby-gpios = <&gpio0 90 GPIO_ACTIVE_LOW>; + adi,range-gpios = <&gpio0 89 GPIO_ACTIVE_HIGH>; + adi,oversampling-ratio-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH + &gpio0 87 GPIO_ACTIVE_HIGH + &gpio0 86 GPIO_ACTIVE_HIGH>; + io-backends = <&iio_backend>; + }; + }; + - | #include #include @@ -185,7 +328,6 @@ examples: reg = <0>; spi-max-frequency = <1000000>; spi-cpol; - spi-cpha; avcc-supply = <&adc_vref>; vdrive-supply = <&vdd_supply>; @@ -202,4 +344,53 @@ examples: standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>; }; }; + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad7606c-18"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <1000000>; + spi-cpol; + + avcc-supply = <&adc_vref>; + vdrive-supply = <&vdd_supply>; + + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + + adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>; + adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; + standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>; + + adi,sw-mode; + + channel@1 { + reg = <1>; + diff-channels = <1 1>; + bipolar; + }; + + channel@3 { + reg = <3>; + bipolar; + }; + + channel@8 { + reg = <8>; + diff-channels = <8 8>; + bipolar; + }; + + }; + }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7625.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7625.yaml new file mode 100644 index 0000000000000..8848562af28f2 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7625.yaml @@ -0,0 +1,176 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad7625.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Fast PulSAR Analog to Digital Converters + +maintainers: + - Michael Hennerich + - Nuno Sá + +description: | + A family of single channel differential analog to digital converters. + + * https://www.analog.com/en/products/ad7625.html + * https://www.analog.com/en/products/ad7626.html + * https://www.analog.com/en/products/ad7960.html + * https://www.analog.com/en/products/ad7961.html + +properties: + compatible: + enum: + - adi,ad7625 + - adi,ad7626 + - adi,ad7960 + - adi,ad7961 + + vdd1-supply: true + vdd2-supply: true + vio-supply: true + + ref-supply: + description: + Voltage regulator for the external reference voltage (REF). + + refin-supply: + description: + Voltage regulator for the reference buffer input (REFIN). + + clocks: + description: + The clock connected to the CLK pins, gated by the clk_gate PWM. + maxItems: 1 + + pwms: + items: + - description: PWM connected to the CNV input on the ADC. + - description: PWM that gates the clock connected to the ADC's CLK input. + + pwm-names: + items: + - const: cnv + - const: clk_gate + + io-backends: + description: + The AXI ADC IP block connected to the D+/- and DCO+/- lines of the + ADC. An example backend can be found at + http://analogdevicesinc.github.io/hdl/projects/pulsar_lvds/index.html. + maxItems: 1 + + adi,no-dco: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates the wiring of the DCO+/- lines. If true, then they are + grounded and the device is in self-clocked mode. If this is not + present, then the device is in echoed clock mode. + + adi,en0-always-on: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates if EN0 is hard-wired to the high state. If neither this + nor en0-gpios are present, then EN0 is hard-wired low. + + adi,en1-always-on: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates if EN1 is hard-wired to the high state. If neither this + nor en1-gpios are present, then EN1 is hard-wired low. + + adi,en2-always-on: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates if EN2 is hard-wired to the high state. If neither this + nor en2-gpios are present, then EN2 is hard-wired low. + + adi,en3-always-on: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates if EN3 is hard-wired to the high state. If neither this + nor en3-gpios are present, then EN3 is hard-wired low. + + en0-gpios: + description: + Configurable EN0 pin. + + en1-gpios: + description: + Configurable EN1 pin. + + en2-gpios: + description: + Configurable EN2 pin. + + en3-gpios: + description: + Configurable EN3 pin. + +required: + - compatible + - vdd1-supply + - vdd2-supply + - vio-supply + - clocks + - pwms + - pwm-names + - io-backends + +allOf: + - if: + required: + - ref-supply + then: + properties: + refin-supply: false + - if: + required: + - refin-supply + then: + properties: + ref-supply: false + - if: + properties: + compatible: + contains: + enum: + - adi,ad7625 + - adi,ad7626 + then: + properties: + en2-gpios: false + en3-gpios: false + adi,en2-always-on: false + adi,en3-always-on: false + + - if: + properties: + compatible: + contains: + enum: + - adi,ad7960 + - adi,ad7961 + then: + # ad796x parts must have one of the two supplies + oneOf: + - required: [ref-supply] + - required: [refin-supply] + +additionalProperties: false + +examples: + - | + #include + adc { + compatible = "adi,ad7625"; + vdd1-supply = <&supply_5V>; + vdd2-supply = <&supply_2_5V>; + vio-supply = <&supply_2_5V>; + io-backends = <&axi_adc>; + clocks = <&ref_clk>; + pwms = <&axi_pwm_gen 0 0>, <&axi_pwm_gen 1 0>; + pwm-names = "cnv", "clk_gate"; + en0-gpios = <&gpio0 86 GPIO_ACTIVE_HIGH>; + en1-gpios = <&gpio0 87 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml new file mode 100644 index 0000000000000..044f92f39cfa7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7779.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad7779.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD777X family 8-Channel, 24-Bit, Simultaneous Sampling ADCs + +maintainers: + - Ramona Nechita + +description: | + The AD777X family consist of 8-channel, simultaneous sampling analog-to- + digital converter (ADC). Eight full Σ-Δ ADCs are on-chip. The + AD7771 provides an ultralow input current to allow direct sensor + connection. Each input channel has a programmable gain stage + allowing gains of 1, 2, 4, and 8 to map lower amplitude sensor + outputs into the full-scale ADC input range, maximizing the + dynamic range of the signal chain. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7770.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7771.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7779.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,ad7770 + - adi,ad7771 + - adi,ad7779 + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + clocks: + maxItems: 1 + + avdd1-supply: + description: Front-End analog supply AVDD1. Can be used as conversion ref. + + avdd2-supply: + description: AVDD2 Analog Supply from 2.2 V to 3.6 V. + + avdd4-supply: + description: AVDD4 SAR Analog Supply and Reference Source. + + interrupts: + minItems: 1 + items: + - description: | + adc_rdy: Interrupt line for DRDY signal which indicates the end of + conversion independently of the interface selected to read back the + Σ-∆ conversion. + - description: | + Alert: The chip includes self diagnostic features to guarantee the + correct operation. If an error is detected, the ALERT pin is pulled + high to generate an external interruption to the controller. + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: + - adc_rdy + - alert + + start-gpios: + description: + Pin that controls start synchronization pulse. + maxItems: 1 + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad7779"; + reg = <0>; + start-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio0 93 GPIO_ACTIVE_LOW>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "adc_rdy"; + clocks = <&adc_clk>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml index f748f3a60b352..b0962a4583ac7 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml @@ -98,6 +98,7 @@ allOf: compatible: contains: enum: + - amlogic,meson8-saradc - amlogic,meson8b-saradc - amlogic,meson8m2-saradc then: diff --git a/Documentation/devicetree/bindings/iio/adc/gehc,pmc-adc.yaml b/Documentation/devicetree/bindings/iio/adc/gehc,pmc-adc.yaml new file mode 100644 index 0000000000000..2cea7c104a269 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/gehc,pmc-adc.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/gehc,pmc-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GE HealthCare PMC Analog to Digital Converter (ADC) + +maintainers: + - Herve Codina + +description: + The GE HealthCare PMC ADC is a 16-Channel (voltage and current), 16-Bit ADC + with an I2C Interface. + +properties: + compatible: + const: gehc,pmc-adc + + reg: + maxItems: 1 + + vdd-supply: + description: + Regulator for the VDD power supply. + + vdda-supply: + description: + Regulator for the VDD analog (VDDA) power supply. + + vddio-supply: + description: + Regulator for the VDD IO (VDDIO) power supply. + + vref-supply: + description: + Regulator for the voltage reference power supply. + + clocks: + maxItems: 1 + description: + The component uses an external oscillator (osc) if an external oscillator + is connected to its clock pins. Otherwise, it uses an internal reference + clock. + + clock-names: + items: + - const: osc + + "#io-channel-cells": + const: 2 + description: | + The first cell is the channel type (dt-bindings/iio/adc/gehc,pmc-adc.h + defines these values): + - 0: voltage + - 1: current + The second cell is the channel number from 0 to 15. + +required: + - compatible + - reg + - vdd-supply + - vdda-supply + - vddio-supply + - vref-supply + - '#io-channel-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@14 { + compatible = "gehc,pmc-adc"; + reg = <0x14>; + vdd-supply = <®_vdd>; + vdda-supply = <®_vdda>; + vddio-supply = <®_vddio>; + vref-supply = <®_vref>; + #io-channel-cells = <2>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml index fc8b97f820775..2d2561a526838 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml @@ -30,7 +30,7 @@ properties: maxItems: 1 spi-max-frequency: - maximum: 30000000 + maximum: 66000000 reset-gpios: maxItems: 1 @@ -60,6 +60,12 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2, 3] + io-backends: + description: The iio backend reference. + Device can be optionally connected to the "axi-ad3552r IP" fpga-based + QSPI + DDR (Double Data Rate) controller to reach high speed transfers. + maxItems: 1 + '#address-cells': const: 1 @@ -128,6 +134,7 @@ patternProperties: - custom-output-range-config allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml index c81285d84db7a..79cb4b78a88a9 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml @@ -26,16 +26,47 @@ properties: vdd-supply: true vss-supply: true + vcc-supply: + description: + Supply that powers the chip. + + iovcc-supply: + description: + Supply for the digital interface. + + vrefp-supply: + description: + Positive referance input voltage range. From 5v to (vdd - 2.5) + + vrefn-supply: + description: + Negative referance input voltage range. From (vss + 2.5) to 0. + adi,rbuf-gain2-en: description: Specify to allow an external amplifier to be connected in a gain of two configuration. type: boolean + reset-gpios: + maxItems: 1 + + clear-gpios: + maxItems: 1 + + ldac-gpios: + description: + LDAC pin to be used as a hardware trigger to update the DAC channels. + maxItems: 1 + required: - compatible - reg - vdd-supply - vss-supply + - vcc-supply + - iovcc-supply + - vrefp-supply + - vrefn-supply allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -44,6 +75,7 @@ unevaluatedProperties: false examples: - | + #include spi { #address-cells = <1>; #size-cells = <0>; @@ -53,6 +85,13 @@ examples: reg = <0>; vss-supply = <&dac_vss>; vdd-supply = <&dac_vdd>; + vcc-supply = <&dac_vcc>; + iovcc-supply = <&dac_iovcc>; + vrefp-supply = <&dac_vrefp>; + vrefn-supply = <&dac_vrefn>; + reset-gpios = <&gpio_bd 16 GPIO_ACTIVE_LOW>; + clear-gpios = <&gpio_bd 17 GPIO_ACTIVE_LOW>; + ldac-gpios = <&gpio_bd 18 GPIO_ACTIVE_HIGH>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad8460.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad8460.yaml new file mode 100644 index 0000000000000..b65928024e12f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad8460.yaml @@ -0,0 +1,164 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2024 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad8460.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD8460 DAC + +maintainers: + - Mariel Tinaco + +description: | + Analog Devices AD8460 110 V High Voltage, 1 A High Current, + Arbitrary Waveform Generator with Integrated 14-Bit High Speed DAC + https://www.analog.com/media/en/technical-documentation/data-sheets/ad8460.pdf + +properties: + compatible: + enum: + - adi,ad8460 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + items: + - const: tx + + spi-max-frequency: + maximum: 20000000 + + hvcc-supply: + description: Positive high voltage power supply line + + hvee-supply: + description: Negative high voltage power supply line + + vcc-5v-supply: + description: Low voltage power supply + + vref-5v-supply: + description: Reference voltage for analog low voltage + + dvdd-3p3v-supply: + description: Digital supply bypass + + avdd-3p3v-supply: + description: Analog supply bypass + + refio-1p2v-supply: + description: Drive voltage in the range of 1.2V maximum to as low as + low as 0.12V through the REF_IO pin to adjust full scale output span + + adi,external-resistor-ohms: + description: Specify value of external resistor connected to FS_ADJ pin + to establish internal HVDAC's reference current I_REF + minimum: 2000 + maximum: 20000 + default: 2000 + + adi,range-microvolt: + description: Voltage output range specified as + items: + - minimum: -55000000 + maximum: 0 + default: 0 + - minimum: 0 + maximum: 55000000 + default: 0 + + adi,range-microamp: + description: Current output range specified as + items: + - minimum: -1000000 + maximum: 0 + default: 0 + - minimum: 0 + maximum: 1000000 + default: 0 + + adi,max-millicelsius: + description: Overtemperature threshold + minimum: 0 + maximum: 150000 + default: 0 + + shutdown-reset-gpios: + description: Corresponds to SDN_RESET pin. To exit shutdown + or sleep mode, pulse SDN_RESET HIGH, then leave LOW. + maxItems: 1 + + reset-gpios: + description: Manual Power On Reset (POR). Pull this GPIO pin + LOW and then HIGH to reset all digital registers to default + maxItems: 1 + + shutdown-gpios: + description: Corresponds to SDN_IO pin. Shutdown may be + initiated by the user, by pulsing SDN_IO high. To exit shutdown, + pulse SDN_IO low, then float. + maxItems: 1 + +required: + - compatible + - reg + - clocks + - hvcc-supply + - hvee-supply + - vcc-5v-supply + - vref-5v-supply + - dvdd-3p3v-supply + - avdd-3p3v-supply + - refio-1p2v-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "adi,ad8460"; + reg = <0>; + spi-max-frequency = <8000000>; + + dmas = <&tx_dma 0>; + dma-names = "tx"; + + shutdown-reset-gpios = <&gpio 86 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 91 GPIO_ACTIVE_LOW>; + shutdown-gpios = <&gpio 88 GPIO_ACTIVE_HIGH>; + + clocks = <&sync_ext_clk>; + + hvcc-supply = <&hvcc>; + hvee-supply = <&hvee>; + vcc-5v-supply = <&vcc_5>; + vref-5v-supply = <&vref_5>; + dvdd-3p3v-supply = <&dvdd_3_3>; + avdd-3p3v-supply = <&avdd_3_3>; + refio-1p2v-supply = <&refio_1_2>; + + adi,external-resistor-ohms = <2000>; + adi,range-microvolt = <(-40000000) 40000000>; + adi,range-microamp = <0 50000>; + adi,max-millicelsius = <50000>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml index a55e9bfc66d74..1adba9aceeb11 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml @@ -19,11 +19,13 @@ description: | memory via DMA into the DAC. https://wiki.analog.com/resources/fpga/docs/axi_dac_ip + https://analogdevicesinc.github.io/hdl/library/axi_ad3552r/index.html properties: compatible: enum: - adi,axi-dac-9.1.b + - adi,axi-ad3552r reg: maxItems: 1 @@ -36,7 +38,14 @@ properties: - const: tx clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: s_axi_aclk + - const: dac_clk + minItems: 1 '#io-backend-cells': const: 0 @@ -47,7 +56,29 @@ required: - reg - clocks -additionalProperties: false +allOf: + - if: + properties: + compatible: + contains: + const: adi,axi-ad3552r + then: + $ref: /schemas/spi/spi-controller.yaml# + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + required: + - clock-names + else: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + +unevaluatedProperties: false examples: - | @@ -57,6 +88,38 @@ examples: dmas = <&tx_dma 0>; dma-names = "tx"; #io-backend-cells = <0>; - clocks = <&axi_clk>; + clocks = <&clkc 15>; + clock-names = "s_axi_aclk"; + }; + + - | + #include + axi_dac: spi@44a70000 { + compatible = "adi,axi-ad3552r"; + reg = <0x44a70000 0x1000>; + dmas = <&dac_tx_dma 0>; + dma-names = "tx"; + #io-backend-cells = <0>; + clocks = <&clkc 15>, <&ref_clk>; + clock-names = "s_axi_aclk", "dac_clk"; + + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "adi,ad3552r"; + reg = <0>; + reset-gpios = <&gpio0 92 GPIO_ACTIVE_HIGH>; + io-backends = <&axi_dac>; + spi-max-frequency = <20000000>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,output-range-microvolt = <(-10000000) (10000000)>; + }; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,bmi270.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,bmi270.yaml new file mode 100644 index 0000000000000..7b0cde1c9b0a4 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/bosch,bmi270.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/bosch,bmi270.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bosch BMI270 6-Axis IMU + +maintainers: + - Alex Lanzano + +description: | + BMI270 is a 6-axis inertial measurement unit that can measure acceleration and + angular velocity. The sensor also supports configurable interrupt events such + as motion, step counter, and wrist motion gestures. The sensor can communicate + I2C or SPI. + https://www.bosch-sensortec.com/products/motion-sensors/imus/bmi270/ + +properties: + compatible: + enum: + - bosch,bmi260 + - bosch,bmi270 + + reg: + maxItems: 1 + + vdd-supply: true + vddio-supply: true + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: + - INT1 + - INT2 + + drive-open-drain: + description: + set if the specified interrupt pins should be configured as + open drain. If not set, defaults to push-pull. + + mount-matrix: + description: + an optional 3x3 mounting rotation matrix. + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + imu@68 { + compatible = "bosch,bmi270"; + reg = <0x68>; + vdd-supply = <&vdd>; + vddio-supply = <&vddio>; + interrupt-parent = <&gpio1>; + interrupts = <16 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,smi240.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,smi240.yaml new file mode 100644 index 0000000000000..58f1411728f63 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/bosch,smi240.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/bosch,smi240.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bosch smi240 imu + +maintainers: + - Jianping Shen + +description: + Inertial Measurement Unit with Accelerometer and Gyroscope + with a measurement range of +/-300°/s and up to 16g. + https://www.bosch-semiconductors.com/mems-sensors/highly-automated-driving/smi240/ + +properties: + compatible: + const: bosch,smi240 + + reg: + maxItems: 1 + + vdd-supply: true + vddio-supply: true + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + imu@0 { + compatible = "bosch,smi240"; + reg = <0>; + vdd-supply = <&vdd>; + vddio-supply = <&vddio>; + spi-max-frequency = <10000000>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 3769f8e8e98ce..7e4492bbd0278 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: InvenSense ICM-426xx Inertial Measurement Unit maintainers: - - Jean-Baptiste Maneyrol + - Jean-Baptiste Maneyrol description: | 6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml index 587ff2bced2dd..f91954870a44c 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device maintainers: - - Jean-Baptiste Maneyrol + - Jean-Baptiste Maneyrol description: | These devices support both I2C and SPI bus interfaces. @@ -36,6 +36,11 @@ properties: - items: - const: invensense,icm20608d - const: invensense,icm20608 + - items: + - enum: + - invensense,iam20680hp + - invensense,iam20680ht + - const: invensense,iam20680 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/light/ti,opt3001.yaml b/Documentation/devicetree/bindings/iio/light/ti,opt3001.yaml index 441e9343fc975..67ca8d08256a5 100644 --- a/Documentation/devicetree/bindings/iio/light/ti,opt3001.yaml +++ b/Documentation/devicetree/bindings/iio/light/ti,opt3001.yaml @@ -15,7 +15,9 @@ description: | properties: compatible: - const: ti,opt3001 + enum: + - ti,opt3001 + - ti,opt3002 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/light/veml6030.yaml b/Documentation/devicetree/bindings/iio/light/veml6030.yaml deleted file mode 100644 index fb19a2d7a8497..0000000000000 --- a/Documentation/devicetree/bindings/iio/light/veml6030.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/iio/light/veml6030.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: VEML6030 Ambient Light Sensor (ALS) - -maintainers: - - Rishi Gupta - -description: | - Bindings for the ambient light sensor veml6030 from Vishay - Semiconductors over an i2c interface. - - Irrespective of whether interrupt is used or not, application - can get the ALS and White channel reading from IIO raw interface. - - If the interrupts are used, application will receive an IIO event - whenever configured threshold is crossed. - - Specifications about the sensor can be found at: - https://www.vishay.com/docs/84366/veml6030.pdf - -properties: - compatible: - enum: - - vishay,veml6030 - - reg: - description: - I2C address of the device. - enum: - - 0x10 # ADDR pin pulled down - - 0x48 # ADDR pin pulled up - - interrupts: - description: - interrupt mapping for IRQ. Configure with IRQ_TYPE_LEVEL_LOW. - Refer to interrupt-controller/interrupts.txt for generic - interrupt client node bindings. - maxItems: 1 - -required: - - compatible - - reg - -additionalProperties: false - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - - light-sensor@10 { - compatible = "vishay,veml6030"; - reg = <0x10>; - interrupts = <12 IRQ_TYPE_LEVEL_LOW>; - }; - }; -... diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml new file mode 100644 index 0000000000000..4ea69f1fdd63a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0+ +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/vishay,veml6030.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: VEML3235, VEML6030, VEML6035 and VEML7700 Ambient Light Sensors (ALS) + +maintainers: + - Rishi Gupta + +description: | + Bindings for the ambient light sensors veml6030 and veml6035 from + Vishay Semiconductors over an i2c interface. + + Irrespective of whether interrupt is used or not, application + can get the ALS and White channel reading from IIO raw interface. + + If the interrupts are used, application will receive an IIO event + whenever configured threshold is crossed. + + Specifications about the sensors can be found at: + https://www.vishay.com/docs/80131/veml3235.pdf + https://www.vishay.com/docs/84366/veml6030.pdf + https://www.vishay.com/docs/84889/veml6035.pdf + https://www.vishay.com/docs/84286/veml7700.pdf + +properties: + compatible: + enum: + - vishay,veml3235 + - vishay,veml6030 + - vishay,veml6035 + - vishay,veml7700 + + reg: + maxItems: 1 + + interrupts: + description: + interrupt mapping for IRQ. Configure with IRQ_TYPE_LEVEL_LOW. + Refer to interrupt-controller/interrupts.txt for generic + interrupt client node bindings. + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + - vdd-supply + +allOf: + - if: + properties: + compatible: + enum: + - vishay,veml6030 + then: + properties: + reg: + enum: + - 0x10 # ADDR pin pulled down + - 0x48 # ADDR pin pulled up + + - if: + properties: + compatible: + enum: + - vishay,veml6035 + then: + properties: + reg: + enum: + - 0x29 + + - if: + properties: + compatible: + enum: + - vishay,veml3235 + - vishay,veml7700 + then: + properties: + reg: + enum: + - 0x10 + interrupts: false + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@10 { + compatible = "vishay,veml6030"; + reg = <0x10>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + vdd-supply = <&vdd>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml index ecf2339e02f65..d2effccbfb563 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/light/vishay,veml6075.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Vishay VEML6075 UVA/B and VEML6040 RGBW sensors +title: Vishay VEML6070 UVA, VEML6075 UVA/B and VEML6040 RGBW sensors maintainers: - Javier Carrasco @@ -16,11 +16,19 @@ properties: compatible: enum: - vishay,veml6040 + - vishay,veml6070 - vishay,veml6075 reg: maxItems: 1 + vishay,rset-ohms: + description: + Resistor used to select the integration time. + default: 270000 + minimum: 75000 + maximum: 1200000 + vdd-supply: true required: @@ -28,6 +36,17 @@ required: - reg - vdd-supply +allOf: + - if: + properties: + compatible: + enum: + - vishay,veml6040 + - vishay,veml6075 + then: + properties: + vishay,rset-ohms: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/iio/magnetometer/allegromicro,als31300.yaml b/Documentation/devicetree/bindings/iio/magnetometer/allegromicro,als31300.yaml new file mode 100644 index 0000000000000..52e3781834ee9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/allegromicro,als31300.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/allegromicro,als31300.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allegro MicroSystems ALS31300 3-D Linear Hall Effect sensor + +maintainers: + - Neil Armstrong + +properties: + $nodename: + pattern: '^magnetometer@[0-9a-f]+$' + + compatible: + enum: + - allegromicro,als31300-500 # Factory configured at 500 Gauss input range + - allegromicro,als31300-1000 # Factory configured at 1000 Gauss input range + - allegromicro,als31300-2000 # Factory configured at 2000 Gauss input range + + reg: + maxItems: 1 + + vcc-supply: + description: 5.5V supply + + interrupts: + maxItems: 1 + +required: + - compatible + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + magnetometer@61 { + compatible = "allegromicro,als31300-500"; + reg = <0x61>; + vcc-supply = <&hall_vcc>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml index 6fda887ee9d42..cb201cecfa1a3 100644 --- a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml @@ -47,15 +47,33 @@ properties: maxItems: 1 interrupts: - description: - interrupt mapping for IRQ (BMP085 only) maxItems: 1 + drive-open-drain: + description: + set if the interrupt pin should be configured as open drain. + If not set, defaults to push-pull configuration. + type: boolean + required: - compatible - vddd-supply - vdda-supply +allOf: + - if: + properties: + compatible: + not: + contains: + enum: + - bosch,bmp085 + - bosch,bmp380 + - bosch,bmp580 + then: + properties: + interrupts: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp006.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp006.yaml index d43002b9bfdc0..590f50ba3a315 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp006.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp006.yaml @@ -23,6 +23,9 @@ properties: vdd-supply: description: provide VDD power to the sensor. + interrupts: + maxItems: 1 + required: - compatible - reg @@ -31,6 +34,7 @@ additionalProperties: false examples: - | + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -38,5 +42,7 @@ examples: compatible = "ti,tmp006"; reg = <0x40>; vdd-supply = <&ldo4_reg>; + interrupt-parent = <&gpio1>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; }; }; diff --git a/Documentation/devicetree/bindings/input/mediatek,pmic-keys.yaml b/Documentation/devicetree/bindings/input/mediatek,pmic-keys.yaml index 466566ae7f103..60f09caa0e4c7 100644 --- a/Documentation/devicetree/bindings/input/mediatek,pmic-keys.yaml +++ b/Documentation/devicetree/bindings/input/mediatek,pmic-keys.yaml @@ -28,6 +28,7 @@ properties: - mediatek,mt6331-keys - mediatek,mt6357-keys - mediatek,mt6358-keys + - mediatek,mt6359-keys - mediatek,mt6397-keys power-off-time-sec: true diff --git a/Documentation/devicetree/bindings/input/rotary-encoder.yaml b/Documentation/devicetree/bindings/input/rotary-encoder.yaml index e315aab7f584f..f9332bb2343f2 100644 --- a/Documentation/devicetree/bindings/input/rotary-encoder.yaml +++ b/Documentation/devicetree/bindings/input/rotary-encoder.yaml @@ -41,7 +41,7 @@ properties: device, hence no steps need to be passed. rotary-encoder,rollover: - $ref: /schemas/types.yaml#/definitions/int32 + $ref: /schemas/types.yaml#/definitions/flag description: Automatic rollover when the rotary value becomes greater than the specified steps or smaller than 0. For absolute axis only. diff --git a/Documentation/devicetree/bindings/input/touchscreen/novatek,nvt-ts.yaml b/Documentation/devicetree/bindings/input/touchscreen/novatek,nvt-ts.yaml new file mode 100644 index 0000000000000..bd6a60486d1f1 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/novatek,nvt-ts.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/novatek,nvt-ts.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NVT Touchscreen Controller + +maintainers: + - Hans de Goede + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + enum: + - novatek,nt11205-ts + - novatek,nt36672a-ts + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + vcc-supply: true + iovcc-supply: true + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + touchscreen@1 { + compatible = "novatek,nt36672a-ts"; + reg = <0x01>; + interrupts-extended = <&tlmm 31 IRQ_TYPE_EDGE_RISING>; + reset-gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; + vcc-supply = <&vreg_l22a_2p85>; + iovcc-supply = <&vreg_l14a_1p8>; + pinctrl-0 = <&ts_int_default &ts_reset_default>; + pinctrl-1 = <&ts_int_sleep &ts_reset_sleep>; + pinctrl-names = "default", "sleep"; + touchscreen-size-x = <1080>; + touchscreen-size-y = <2246>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 189f5900ee50d..251410aabf38b 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -26,6 +26,7 @@ properties: - items: - enum: - qcom,qcm2290-cpu-bwmon + - qcom,qcs8300-cpu-bwmon - qcom,sa8775p-cpu-bwmon - qcom,sc7180-cpu-bwmon - qcom,sc7280-cpu-bwmon @@ -40,6 +41,7 @@ properties: - const: qcom,sdm845-bwmon # BWMON v4, unified register space - items: - enum: + - qcom,qcs8300-llcc-bwmon - qcom,sa8775p-llcc-bwmon - qcom,sc7180-llcc-bwmon - qcom,sc8280xp-llcc-bwmon diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml new file mode 100644 index 0000000000000..9d762b2a1fcf8 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,qcs615-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on QCS615 + +maintainers: + - Raviteja Laggyshetty + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,qcs615-rpmh.h + +properties: + compatible: + enum: + - qcom,qcs615-aggre1-noc + - qcom,qcs615-camnoc-virt + - qcom,qcs615-config-noc + - qcom,qcs615-dc-noc + - qcom,qcs615-gem-noc + - qcom,qcs615-ipa-virt + - qcom,qcs615-mc-virt + - qcom,qcs615-mmss-noc + - qcom,qcs615-system-noc + + reg: + maxItems: 1 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs615-camnoc-virt + - qcom,qcs615-ipa-virt + - qcom,qcs615-mc-virt + then: + properties: + reg: false + else: + required: + - reg + +unevaluatedProperties: false + +examples: + - | + gem_noc: interconnect@9680000 { + compatible = "qcom,qcs615-gem-noc"; + reg = <0x9680000 0x3e200>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mc_virt: interconnect-2 { + compatible = "qcom,qcs615-mc-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml new file mode 100644 index 0000000000000..e9f528d6d9a8c --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,qcs8300-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on QCS8300 + +maintainers: + - Raviteja Laggyshetty + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). + + See also: include/dt-bindings/interconnect/qcom,qcs8300-rpmh.h + +properties: + compatible: + enum: + - qcom,qcs8300-aggre1-noc + - qcom,qcs8300-aggre2-noc + - qcom,qcs8300-clk-virt + - qcom,qcs8300-config-noc + - qcom,qcs8300-dc-noc + - qcom,qcs8300-gem-noc + - qcom,qcs8300-gpdsp-anoc + - qcom,qcs8300-lpass-ag-noc + - qcom,qcs8300-mc-virt + - qcom,qcs8300-mmss-noc + - qcom,qcs8300-nspa-noc + - qcom,qcs8300-pcie-anoc + - qcom,qcs8300-system-noc + + reg: + maxItems: 1 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-clk-virt + - qcom,qcs8300-mc-virt + then: + properties: + reg: false + else: + required: + - reg + +unevaluatedProperties: false + +examples: + - | + gem_noc: interconnect@9100000 { + compatible = "qcom,qcs8300-gem-noc"; + reg = <0x9100000 0xf7080>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + clk_virt: interconnect-0 { + compatible = "qcom,qcs8300-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml new file mode 100644 index 0000000000000..4647dac740e9b --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,sar2130p-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on SAR2130P + +maintainers: + - Dmitry Baryshkov + - Georgi Djakov + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also:: include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h + +properties: + compatible: + enum: + - qcom,sar2130p-clk-virt + - qcom,sar2130p-config-noc + - qcom,sar2130p-gem-noc + - qcom,sar2130p-lpass-ag-noc + - qcom,sar2130p-mc-virt + - qcom,sar2130p-mmss-noc + - qcom,sar2130p-nsp-noc + - qcom,sar2130p-pcie-anoc + - qcom,sar2130p-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,sar2130p-clk-virt + - qcom,sar2130p-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,sar2130p-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sar2130p-system-noc + then: + properties: + clocks: + items: + - description: aggre USB3 PRIM AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sar2130p-system-noc + - qcom,sar2130p-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + clk_virt: interconnect-0 { + compatible = "qcom,sar2130p-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre1_noc: interconnect@1680000 { + compatible = "qcom,sar2130p-system-noc"; + reg = <0x01680000 0x29080>; + #interconnect-cells = <2>; + clocks = <&gcc_prim_axi_clk>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml index 404477910f029..1332aab9a888f 100644 --- a/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml @@ -15,6 +15,8 @@ properties: reg: oneOf: + - items: + - description: mailbox data registers - items: - description: mailbox control & data registers - description: mailbox interrupt registers @@ -23,6 +25,7 @@ properties: - description: mailbox control registers - description: mailbox interrupt registers - description: mailbox data registers + deprecated: true interrupts: maxItems: 1 @@ -41,12 +44,12 @@ additionalProperties: false examples: - | soc { - #address-cells = <2>; - #size-cells = <2>; - mbox: mailbox@37020000 { + #address-cells = <1>; + #size-cells = <1>; + + mailbox@37020800 { compatible = "microchip,mpfs-mailbox"; - reg = <0x0 0x37020000 0x0 0x58>, <0x0 0x2000318C 0x0 0x40>, - <0x0 0x37020800 0x0 0x100>; + reg = <0x37020800 0x100>; interrupt-parent = <&L1>; interrupts = <96>; #mbox-cells = <1>; diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index 982c741e62251..9d2dfd85b2073 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -165,12 +165,13 @@ allOf: - if: properties: compatible: - enum: - - qcom,msm8953-apcs-kpss-global - - qcom,msm8994-apcs-kpss-global - - qcom,msm8996-apcs-hmss-global - - qcom,qcm2290-apcs-hmss-global - - qcom,sdm845-apss-shared + contains: + enum: + - qcom,msm8953-apcs-kpss-global + - qcom,msm8994-apcs-kpss-global + - qcom,msm8996-apcs-hmss-global + - qcom,qcm2290-apcs-hmss-global + - qcom,sdm845-apss-shared then: properties: clocks: false diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index 2d66770ed3612..f69c0ec5d19d3 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -28,6 +28,7 @@ properties: - qcom,qdu1000-ipcc - qcom,sa8255p-ipcc - qcom,sa8775p-ipcc + - qcom,sar2130p-ipcc - qcom,sc7280-ipcc - qcom,sc8280xp-ipcc - qcom,sdx75-ipcc @@ -38,6 +39,7 @@ properties: - qcom,sm8450-ipcc - qcom,sm8550-ipcc - qcom,sm8650-ipcc + - qcom,sm8750-ipcc - qcom,x1e80100-ipcc - const: qcom,ipcc diff --git a/Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml b/Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml new file mode 100644 index 0000000000000..0971fb97896ef --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/thead,th1520-mbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-head TH1520 Mailbox Controller + +description: + The T-head mailbox controller enables communication and coordination between + cores within the SoC by passing messages (e.g., data, status, and control) + through mailbox channels. It also allows one core to signal another processor + using interrupts via the Interrupt Controller Unit (ICU). + +maintainers: + - Michal Wilczynski + +properties: + compatible: + const: thead,th1520-mbox + + clocks: + items: + - description: Clock for the local mailbox + - description: Clock for remote ICU 0 + - description: Clock for remote ICU 1 + - description: Clock for remote ICU 2 + + clock-names: + items: + - const: clk-local + - const: clk-remote-icu0 + - const: clk-remote-icu1 + - const: clk-remote-icu2 + + reg: + items: + - description: Mailbox local base address + - description: Remote ICU 0 base address + - description: Remote ICU 1 base address + - description: Remote ICU 2 base address + + reg-names: + items: + - const: local + - const: remote-icu0 + - const: remote-icu1 + - const: remote-icu2 + + interrupts: + maxItems: 1 + + '#mbox-cells': + const: 1 + description: + The one and only cell describes destination CPU ID. + +required: + - compatible + - clocks + - clock-names + - reg + - reg-names + - interrupts + - '#mbox-cells' + +additionalProperties: false + +examples: + - | + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + mailbox@ffffc38000 { + compatible = "thead,th1520-mbox"; + reg = <0xff 0xffc38000 0x0 0x4000>, + <0xff 0xffc44000 0x0 0x1000>, + <0xff 0xffc4c000 0x0 0x1000>, + <0xff 0xffc54000 0x0 0x1000>; + reg-names = "local", "remote-icu0", "remote-icu1", "remote-icu2"; + clocks = <&clk CLK_MBOX0>, <&clk CLK_MBOX1>, <&clk CLK_MBOX2>, + <&clk CLK_MBOX3>; + clock-names = "clk-local", "clk-remote-icu0", "clk-remote-icu1", + "clk-remote-icu2"; + interrupts = <28>; + #mbox-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml new file mode 100644 index 0000000000000..4a81ed82ef34d --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/airoha,en7581-gpio-sysctl.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/airoha,en7581-gpio-sysctl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN7581 GPIO System Controller + +maintainers: + - Christian Marangi + - Lorenzo Bianconi + +description: + Airoha EN7581 SoC GPIO system controller which provided a register map + for controlling the GPIO, pins and PWM of the SoC. + +properties: + compatible: + items: + - const: airoha,en7581-gpio-sysctl + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + pinctrl: + type: object + $ref: /schemas/pinctrl/airoha,en7581-pinctrl.yaml + description: + Child node definition for EN7581 Pin controller + + pwm: + type: object + $ref: /schemas/pwm/airoha,en7581-pwm.yaml + description: + Child node definition for EN7581 PWM controller + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + system-controller@1fbf0200 { + compatible = "airoha,en7581-gpio-sysctl", "syscon", "simple-mfd"; + reg = <0x1fbf0200 0xc0>; + + pinctrl { + compatible = "airoha,en7581-pinctrl"; + + interrupt-parent = <&gic>; + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + mmc-pins { + mux { + function = "emmc"; + groups = "emmc"; + }; + }; + + mdio-pins { + mux { + function = "mdio"; + groups = "mdio"; + }; + + conf { + pins = "gpio2"; + output-enable; + }; + }; + }; + + pwm { + compatible = "airoha,en7581-pwm"; + + #pwm-cells = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/fuse/renesas,rcar-efuse.yaml b/Documentation/devicetree/bindings/nvmem/renesas,rcar-efuse.yaml similarity index 54% rename from Documentation/devicetree/bindings/fuse/renesas,rcar-efuse.yaml rename to Documentation/devicetree/bindings/nvmem/renesas,rcar-efuse.yaml index d7e289244e72c..ce7d65afa4602 100644 --- a/Documentation/devicetree/bindings/fuse/renesas,rcar-efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/renesas,rcar-efuse.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/fuse/renesas,rcar-efuse.yaml# +$id: http://devicetree.org/schemas/nvmem/renesas,rcar-efuse.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: R-Car E-FUSE connected to PFC @@ -13,6 +13,9 @@ description: The E-FUSE is a type of non-volatile memory, which is accessible through the Pin Function Controller (PFC) on some R-Car Gen4 SoCs. +allOf: + - $ref: nvmem.yaml# + properties: compatible: enum: @@ -39,17 +42,27 @@ required: - power-domains - resets -additionalProperties: false +unevaluatedProperties: false examples: - | - #include - #include - - fuse: fuse@e6078800 { - compatible = "renesas,r8a779a0-efuse"; - reg = <0xe6078800 0x100>; - clocks = <&cpg CPG_MOD 916>; - power-domains = <&sysc R8A779A0_PD_ALWAYS_ON>; - resets = <&cpg 916>; + #include + #include + + fuse@e6078800 { + compatible = "renesas,r8a779f0-efuse"; + reg = <0xe6078800 0x200>; + clocks = <&cpg CPG_MOD 915>; + power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>; + resets = <&cpg 915>; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + calib@144 { + reg = <0x144 0x08>; + }; + }; }; diff --git a/Documentation/devicetree/bindings/fuse/renesas,rcar-otp.yaml b/Documentation/devicetree/bindings/nvmem/renesas,rcar-otp.yaml similarity index 60% rename from Documentation/devicetree/bindings/fuse/renesas,rcar-otp.yaml rename to Documentation/devicetree/bindings/nvmem/renesas,rcar-otp.yaml index d74872ae9ff37..3313c03ea68df 100644 --- a/Documentation/devicetree/bindings/fuse/renesas,rcar-otp.yaml +++ b/Documentation/devicetree/bindings/nvmem/renesas,rcar-otp.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/fuse/renesas,rcar-otp.yaml# +$id: http://devicetree.org/schemas/nvmem/renesas,rcar-otp.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: R-Car E-FUSE connected to OTP_MEM @@ -13,6 +13,9 @@ description: The E-FUSE is a type of non-volatile memory, which is accessible through the One-Time Programmable Memory (OTP_MEM) module on some R-Car Gen4 SoCs. +allOf: + - $ref: nvmem.yaml# + properties: compatible: enum: @@ -22,17 +25,19 @@ properties: reg: items: - description: OTP_MEM_0 - - description: OTP_MEM_1 + - description: OTP_MEM_1. + The addresses of cells defined under the optional nvmem-layout + subnode are relative to this register bank. required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | - otp: otp@e61be000 { - compatible = "renesas,r8a779g0-otp"; - reg = <0xe61be000 0x1000>, <0xe61bf000 0x1000>; + otp@e61be000 { + compatible = "renesas,r8a779g0-otp"; + reg = <0xe61be000 0x1000>, <0xe61bf000 0x1000>; }; diff --git a/Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt b/Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt deleted file mode 100644 index 586c08286aa9a..0000000000000 --- a/Documentation/devicetree/bindings/nvmem/sc27xx-efuse.txt +++ /dev/null @@ -1,52 +0,0 @@ -= Spreadtrum SC27XX PMIC eFuse device tree bindings = - -Required properties: -- compatible: Should be one of the following. - "sprd,sc2720-efuse" - "sprd,sc2721-efuse" - "sprd,sc2723-efuse" - "sprd,sc2730-efuse" - "sprd,sc2731-efuse" -- reg: Specify the address offset of efuse controller. -- hwlocks: Reference to a phandle of a hwlock provider node. - -= Data cells = -Are child nodes of eFuse, bindings of which as described in -bindings/nvmem/nvmem.txt - -Example: - - sc2731_pmic: pmic@0 { - compatible = "sprd,sc2731"; - reg = <0>; - spi-max-frequency = <26000000>; - interrupts = ; - interrupt-controller; - #interrupt-cells = <2>; - #address-cells = <1>; - #size-cells = <0>; - - efuse@380 { - compatible = "sprd,sc2731-efuse"; - reg = <0x380>; - #address-cells = <1>; - #size-cells = <1>; - hwlocks = <&hwlock 12>; - - /* Data cells */ - thermal_calib: calib@10 { - reg = <0x10 0x2>; - }; - }; - }; - -= Data consumers = -Are device nodes which consume nvmem data cells. - -Example: - - thermal { - ... - nvmem-cells = <&thermal_calib>; - nvmem-cell-names = "calibration"; - }; diff --git a/Documentation/devicetree/bindings/nvmem/sprd,sc2731-efuse.yaml b/Documentation/devicetree/bindings/nvmem/sprd,sc2731-efuse.yaml new file mode 100644 index 0000000000000..dc25fe3d1841f --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/sprd,sc2731-efuse.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/sprd,sc2731-efuse.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spreadtrum SC27XX PMIC eFuse + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +properties: + compatible: + enum: + - sprd,sc2720-efuse + - sprd,sc2721-efuse + - sprd,sc2723-efuse + - sprd,sc2730-efuse + - sprd,sc2731-efuse + + reg: + maxItems: 1 + + hwlocks: + maxItems: 1 + +required: + - compatible + - reg + - hwlocks + +allOf: + - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml# + +unevaluatedProperties: false + +examples: + - | + pmic { + #address-cells = <1>; + #size-cells = <0>; + + efuse@380 { + compatible = "sprd,sc2731-efuse"; + reg = <0x380>; + hwlocks = <&hwlock 12>; + #address-cells = <1>; + #size-cells = <1>; + + /* Data cells */ + fgu_calib: calib@6 { + reg = <0x6 0x2>; + bits = <0 9>; + }; + + adc_big_scale: calib@24 { + reg = <0x24 0x2>; + }; + + adc_small_scale: calib@26 { + reg = <0x26 0x2>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/nvmem/sprd,ums312-efuse.yaml b/Documentation/devicetree/bindings/nvmem/sprd,ums312-efuse.yaml new file mode 100644 index 0000000000000..00e0fd1353a3f --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/sprd,ums312-efuse.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/sprd,ums312-efuse.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spreadtrum UMS312 eFuse + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +properties: + compatible: + const: sprd,ums312-efuse + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: enable + + hwlocks: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - hwlocks + +allOf: + - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + efuse@32240000 { + compatible = "sprd,ums312-efuse"; + reg = <0x32240000 0x10000>; + clocks = <&aonapb_gate CLK_EFUSE_EB>; + clock-names = "enable"; + hwlocks = <&hwlock 8>; + #address-cells = <1>; + #size-cells = <1>; + + /* Data cells */ + thermal_calib: calib@10 { + reg = <0x10 0x2>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/nvmem/sprd-efuse.txt b/Documentation/devicetree/bindings/nvmem/sprd-efuse.txt deleted file mode 100644 index 96b6feec27f0b..0000000000000 --- a/Documentation/devicetree/bindings/nvmem/sprd-efuse.txt +++ /dev/null @@ -1,39 +0,0 @@ -= Spreadtrum eFuse device tree bindings = - -Required properties: -- compatible: Should be "sprd,ums312-efuse". -- reg: Specify the address offset of efuse controller. -- clock-names: Should be "enable". -- clocks: The phandle and specifier referencing the controller's clock. -- hwlocks: Reference to a phandle of a hwlock provider node. - -= Data cells = -Are child nodes of eFuse, bindings of which as described in -bindings/nvmem/nvmem.txt - -Example: - - ap_efuse: efuse@32240000 { - compatible = "sprd,ums312-efuse"; - reg = <0 0x32240000 0 0x10000>; - clock-names = "enable"; - hwlocks = <&hwlock 8>; - clocks = <&aonapb_gate CLK_EFUSE_EB>; - - /* Data cells */ - thermal_calib: calib@10 { - reg = <0x10 0x2>; - }; - }; - -= Data consumers = -Are device nodes which consume nvmem data cells. - -Example: - - thermal { - ... - - nvmem-cells = <&thermal_calib>; - nvmem-cell-names = "calibration"; - }; diff --git a/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.txt b/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.txt deleted file mode 100644 index 0df79d9e07ec2..0000000000000 --- a/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.txt +++ /dev/null @@ -1,40 +0,0 @@ -Zodiac Inflight Innovations RAVE EEPROM Bindings - -RAVE SP EEPROM device is a "MFD cell" device exposing physical EEPROM -attached to RAVE Supervisory Processor. It is expected that its Device -Tree node is specified as a child of the node corresponding to the -parent RAVE SP device (as documented in -Documentation/devicetree/bindings/mfd/zii,rave-sp.txt) - -Required properties: - -- compatible: Should be "zii,rave-sp-eeprom" - -Optional properties: - -- zii,eeprom-name: Unique EEPROM identifier describing its function in the - system. Will be used as created NVMEM deivce's name. - -Data cells: - -Data cells are child nodes of eerpom node, bindings for which are -documented in Documentation/devicetree/bindings/nvmem/nvmem.txt - -Example: - - rave-sp { - compatible = "zii,rave-sp-rdu1"; - current-speed = <38400>; - - eeprom@a4 { - compatible = "zii,rave-sp-eeprom"; - reg = <0xa4 0x4000>; - #address-cells = <1>; - #size-cells = <1>; - zii,eeprom-name = "main-eeprom"; - - wdt_timeout: wdt-timeout@81 { - reg = <0x81 2>; - }; - }; - } diff --git a/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.yaml b/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.yaml new file mode 100644 index 0000000000000..d073c51c2b9a9 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/zii,rave-sp-eeprom.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Zodiac Inflight Innovations RAVE EEPROM + +maintainers: + - Frank Li + +description: + RAVE SP EEPROM device is a "MFD cell" device exposing physical EEPROM + attached to RAVE Supervisory Processor. It is expected that its Device + Tree node is specified as a child of the node corresponding to the + parent RAVE SP device (as documented in + Documentation/devicetree/bindings/mfd/zii,rave-sp.yaml) + +properties: + compatible: + const: zii,rave-sp-eeprom + + reg: + maxItems: 1 + + zii,eeprom-name: + $ref: /schemas/types.yaml#/definitions/string + description: + Unique EEPROM identifier describing its function in the + system. Will be used as created NVMEM deivce's name. + +required: + - compatible + +allOf: + - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml# + +unevaluatedProperties: false + +examples: + - | + eeprom@a4 { + compatible = "zii,rave-sp-eeprom"; + reg = <0xa4 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + zii,eeprom-name = "main-eeprom"; + + wdt-timeout@81 { + reg = <0x81 2>; + }; + }; + diff --git a/Documentation/devicetree/bindings/opp/operating-points-v2-ti-cpu.yaml b/Documentation/devicetree/bindings/opp/operating-points-v2-ti-cpu.yaml index fd0c8d5c5f3e7..624d1f3f1382f 100644 --- a/Documentation/devicetree/bindings/opp/operating-points-v2-ti-cpu.yaml +++ b/Documentation/devicetree/bindings/opp/operating-points-v2-ti-cpu.yaml @@ -45,7 +45,25 @@ patternProperties: clock-latency-ns: true opp-hz: true opp-microvolt: true - opp-supported-hw: true + opp-supported-hw: + items: + items: + - description: + The revision of the SoC the OPP is supported by. + This can be easily obtained from the datasheet of the + part being ordered/used. For example, it will be 0x01 for SR1.0 + + - description: + The eFuse bits that indicate the particular OPP is available. + The device datasheet has a table talking about Device Speed Grades. + This table is to be sorted with only the unique elements of the + MAXIMUM OPERATING FREQUENCY starting from the first row which + tells the lowest OPP, to the highest. The corresponding bits + need to be set based on N elements of speed grade the device supports. + So, if there are 3 possible unique MAXIMUM OPERATING FREQUENCY + in the table, then BIT(0) | (1) | (2) will be set, which means + the value shall be 0x7. + opp-suspend: true turbo-mode: true diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml index 898c1be2d6a43..f05aab2b1addc 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml @@ -149,7 +149,7 @@ allOf: then: properties: clocks: - minItems: 4 + minItems: 6 clock-names: items: @@ -178,7 +178,7 @@ allOf: then: properties: clocks: - minItems: 4 + minItems: 6 clock-names: items: @@ -207,6 +207,7 @@ allOf: properties: clocks: minItems: 4 + maxItems: 4 clock-names: items: diff --git a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml index 612633ba59e2c..2e15475697029 100644 --- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml @@ -17,6 +17,12 @@ properties: compatible: const: microchip,pcie-host-1.0 # PolarFire + reg: + minItems: 3 + + reg-names: + minItems: 3 + clocks: description: Fabric Interface Controllers, FICs, are the interface between the FPGA @@ -62,8 +68,9 @@ examples: pcie0: pcie@2030000000 { compatible = "microchip,pcie-host-1.0"; reg = <0x0 0x70000000 0x0 0x08000000>, - <0x0 0x43000000 0x0 0x00010000>; - reg-names = "cfg", "apb"; + <0x0 0x43008000 0x0 0x00002000>, + <0x0 0x4300a000 0x0 0x00002000>; + reg-names = "cfg", "bridge", "ctrl"; device_type = "pci"; #address-cells = <3>; #size-cells = <2>; diff --git a/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml index 7a57a80052a01..039eecdbd6aad 100644 --- a/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml +++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml @@ -18,12 +18,18 @@ allOf: properties: reg: - maxItems: 2 + maxItems: 3 + minItems: 2 reg-names: - items: - - const: cfg - - const: apb + oneOf: + - items: + - const: cfg + - const: apb + - items: + - const: cfg + - const: bridge + - const: ctrl interrupts: minItems: 1 diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml index e18900c415761..0480c58f7d998 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml @@ -81,6 +81,10 @@ properties: vddpe-3v3-supply: description: PCIe endpoint power supply + operating-points-v2: true + opp-table: + type: object + required: - reg - reg-names diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml index 46bd59eefadba..6e0a6d8f0ed07 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml @@ -70,10 +70,6 @@ properties: - const: msi7 - const: global - operating-points-v2: true - opp-table: - type: object - resets: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml index 24cb38673581d..2b5498a35dcc1 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml @@ -20,6 +20,7 @@ properties: - const: qcom,pcie-sm8550 - items: - enum: + - qcom,sar2130p-pcie - qcom,pcie-sm8650 - const: qcom,pcie-sm8550 @@ -39,7 +40,7 @@ properties: clocks: minItems: 7 - maxItems: 8 + maxItems: 9 clock-names: minItems: 7 @@ -52,6 +53,7 @@ properties: - const: ddrss_sf_tbu # PCIe SF TBU clock - const: noc_aggr # Aggre NoC PCIe AXI clock - const: cnoc_sf_axi # Config NoC PCIe1 AXI clock + - const: qmip_pcie_ahb # QMIP PCIe AHB clock interrupts: minItems: 8 diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml index a9db0a2315639..257068a182649 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml @@ -47,9 +47,10 @@ properties: interrupts: minItems: 8 - maxItems: 8 + maxItems: 9 interrupt-names: + minItems: 8 items: - const: msi0 - const: msi1 @@ -59,6 +60,7 @@ properties: - const: msi5 - const: msi6 - const: msi7 + - const: global resets: minItems: 1 @@ -130,9 +132,10 @@ examples: , , , - ; + , + ; interrupt-names = "msi0", "msi1", "msi2", "msi3", - "msi4", "msi5", "msi6", "msi7"; + "msi4", "msi5", "msi6", "msi7", "global"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &intc 0 0 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml index ffabbac57fc16..bd87f6b49d680 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml @@ -26,6 +26,7 @@ properties: - qcom,pcie-ipq8064-v2 - qcom,pcie-ipq8074 - qcom,pcie-ipq8074-gen3 + - qcom,pcie-ipq9574 - qcom,pcie-msm8996 - qcom,pcie-qcs404 - qcom,pcie-sdm845 @@ -164,6 +165,7 @@ allOf: enum: - qcom,pcie-ipq6018 - qcom,pcie-ipq8074-gen3 + - qcom,pcie-ipq9574 then: properties: reg: @@ -400,6 +402,53 @@ allOf: - const: axi_m_sticky # AXI Master Sticky reset - const: axi_s_sticky # AXI Slave Sticky reset + - if: + properties: + compatible: + contains: + enum: + - qcom,pcie-ipq9574 + then: + properties: + clocks: + minItems: 6 + maxItems: 6 + clock-names: + items: + - const: axi_m # AXI Master clock + - const: axi_s # AXI Slave clock + - const: axi_bridge + - const: rchng + - const: ahb + - const: aux + + resets: + minItems: 8 + maxItems: 8 + reset-names: + items: + - const: pipe # PIPE reset + - const: sticky # Core Sticky reset + - const: axi_s_sticky # AXI Slave Sticky reset + - const: axi_s # AXI Slave reset + - const: axi_m_sticky # AXI Master Sticky reset + - const: axi_m # AXI Master reset + - const: aux # AUX Reset + - const: ahb # AHB Reset + + interrupts: + minItems: 8 + interrupt-names: + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - if: properties: compatible: @@ -510,6 +559,7 @@ allOf: - qcom,pcie-ipq8064v2 - qcom,pcie-ipq8074 - qcom,pcie-ipq8074-gen3 + - qcom,pcie-ipq9574 - qcom,pcie-qcs404 then: required: diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml index 548f59d76ef20..205326fb2d75c 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -230,7 +230,6 @@ examples: interrupts = <25>, <24>; interrupt-names = "msi", "hp"; - #interrupt-cells = <1>; reset-gpios = <&port0 0 1>; diff --git a/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml index 67151aaa39480..5f432452c815b 100644 --- a/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml @@ -16,6 +16,13 @@ properties: compatible: const: starfive,jh7110-pcie + + reg: + maxItems: 2 + + reg-names: + maxItems: 2 + clocks: items: - description: NOC bus clock diff --git a/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml index 426101530a21e..d72c02ab55ae8 100644 --- a/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml @@ -18,16 +18,8 @@ properties: const: brcm,ns-usb2-phy reg: - anyOf: - - maxItems: 1 - description: PHY control register - - maxItems: 1 - description: iomem address range of DMU (Device Management Unit) - deprecated: true - - reg-names: - items: - - const: dmu + maxItems: 1 + description: PHY control register brcm,syscon-clkset: description: phandle to syscon for clkset register @@ -50,12 +42,7 @@ required: - clocks - clock-names - "#phy-cells" - -oneOf: - - required: - - brcm,syscon-clkset - - required: - - reg-names + - brcm,syscon-clkset additionalProperties: false diff --git a/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml b/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml index dc3a3f709feaa..6d6d211883aee 100644 --- a/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml +++ b/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml @@ -11,12 +11,17 @@ maintainers: properties: compatible: - enum: - - fsl,imx8mq-usb-phy - - fsl,imx8mp-usb-phy + oneOf: + - enum: + - fsl,imx8mq-usb-phy + - fsl,imx8mp-usb-phy + - items: + - const: fsl,imx95-usb-phy + - const: fsl,imx8mp-usb-phy reg: - maxItems: 1 + minItems: 1 + maxItems: 2 "#phy-cells": const: 0 @@ -89,7 +94,34 @@ required: - clocks - clock-names -additionalProperties: false +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx95-usb-phy + then: + properties: + reg: + items: + - description: USB PHY Control range + - description: USB PHY TCA Block range + else: + properties: + reg: + maxItems: 1 + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx95-usb-phy + then: + $ref: /schemas/usb/usb-switch.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml b/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml index ce665a2779b79..d01b7d1870405 100644 --- a/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml +++ b/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml @@ -32,6 +32,7 @@ properties: - enum: - fsl,imx8dxl-usbphy - fsl,imx8qm-usbphy + - fsl,imx8qxp-usbphy - fsl,imx8ulp-usbphy - const: fsl,imx7ulp-usbphy diff --git a/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml index 423b7c4e62f2a..6be3aa4557e55 100644 --- a/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml +++ b/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml @@ -125,6 +125,16 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 default: 28 + power-domains: + description: + The TPHY of MediaTek should exist within a power domain. The + developer should be aware that the hardware design of MediaTek TPHY + does not require the addition of MTCMOS. If the power to the TPHY + is turned off, it will impact other functions. From the current + perspective of USB hardware design, even if MTCMOS is added to the + TPHY, it should remain always on. + maxItems: 1 + # Required child node: patternProperties: "^(usb|pcie|sata)-phy@[0-9a-f]+$": diff --git a/Documentation/devicetree/bindings/phy/microchip,sparx5-serdes.yaml b/Documentation/devicetree/bindings/phy/microchip,sparx5-serdes.yaml index bdbdb3bbddbea..fa0b02916dacf 100644 --- a/Documentation/devicetree/bindings/phy/microchip,sparx5-serdes.yaml +++ b/Documentation/devicetree/bindings/phy/microchip,sparx5-serdes.yaml @@ -8,6 +8,7 @@ title: Microchip Sparx5 Serdes controller maintainers: - Steen Hegelund + - Daniel Machon description: | The Sparx5 SERDES interfaces share the same basic functionality, but @@ -62,12 +63,26 @@ description: | * 10.3125 Gbps (10GBASE-R/10GBASE-KR/USXGMII) * 25.78125 Gbps (25GBASE-KR/25GBASE-CR/25GBASE-SR/25GBASE-LR/25GBASE-ER) + lan969x has ten SERDES10G interfaces that share the same features, operating + modes and data rates as the equivalent Sparx5 SERDES10G interfaces. + properties: $nodename: pattern: "^serdes@[0-9a-f]+$" compatible: - const: microchip,sparx5-serdes + oneOf: + - enum: + - microchip,sparx5-serdes + - microchip,lan9691-serdes + - items: + - enum: + - microchip,lan9698-serdes + - microchip,lan9696-serdes + - microchip,lan9694-serdes + - microchip,lan9693-serdes + - microchip,lan9692-serdes + - const: microchip,lan9691-serdes reg: minItems: 1 diff --git a/Documentation/devicetree/bindings/phy/nxp,ptn3222.yaml b/Documentation/devicetree/bindings/phy/nxp,ptn3222.yaml new file mode 100644 index 0000000000000..acec5bb2391d7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/nxp,ptn3222.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/nxp,ptn3222.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PTN3222 1-port eUSB2 to USB2 redriver + +maintainers: + - Dmitry Baryshkov + +properties: + compatible: + enum: + - nxp,ptn3222 + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + vdd1v8-supply: + description: power supply (1.8V) + + vdd3v3-supply: + description: power supply (3.3V) + + reset-gpios: true + +required: + - compatible + - reg + - '#phy-cells' + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + redriver@4f { + compatible = "nxp,ptn3222"; + reg = <0x4f>; + #phy-cells = <0>; + vdd3v3-supply = <&vreg_3p3>; + vdd1v8-supply = <&vreg_1p8>; + reset-gpios = <&gpio_reset GPIO_ACTIVE_LOW>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml index 37f028f7a0955..137ac5703853d 100644 --- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml +++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml @@ -96,7 +96,7 @@ patternProperties: Specifies the type of PHY for which the group of PHY lanes is used. Refer include/dt-bindings/phy/phy.h. Constants from the header should be used. $ref: /schemas/types.yaml#/definitions/uint32 - enum: [2, 4] + enum: [2, 4, 8, 9] cdns,num-lanes: description: diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-hdmi.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-hdmi.txt deleted file mode 100644 index 710cccd5ee56e..0000000000000 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-hdmi.txt +++ /dev/null @@ -1,43 +0,0 @@ -ROCKCHIP HDMI PHY WITH INNO IP BLOCK - -Required properties: - - compatible : should be one of the listed compatibles: - * "rockchip,rk3228-hdmi-phy", - * "rockchip,rk3328-hdmi-phy"; - - reg : Address and length of the hdmi phy control register set - - clocks : phandle + clock specifier for the phy clocks - - clock-names : string, clock name, must contain "sysclk" for system - control and register configuration, "refoclk" for crystal- - oscillator reference PLL clock input and "refpclk" for pclk- - based refeference PLL clock input. - - #clock-cells: should be 0. - - clock-output-names : shall be the name for the output clock. - - interrupts : phandle + interrupt specified for the hdmiphy interrupt - - #phy-cells : must be 0. See ./phy-bindings.txt for details. - -Optional properties for rk3328-hdmi-phy: - - nvmem-cells = phandle + nvmem specifier for the cpu-version efuse - - nvmem-cell-names : "cpu-version" to read the chip version, required - for adjustment to some frequency settings - -Example: - hdmi_phy: hdmi-phy@12030000 { - compatible = "rockchip,rk3228-hdmi-phy"; - reg = <0x12030000 0x10000>; - #phy-cells = <0>; - clocks = <&cru PCLK_HDMI_PHY>, <&xin24m>, <&cru DCLK_HDMIPHY>; - clock-names = "sysclk", "refoclk", "refpclk"; - #clock-cells = <0>; - clock-output-names = "hdmi_phy"; - status = "disabled"; - }; - -Then the PHY can be used in other nodes such as: - - hdmi: hdmi@200a0000 { - compatible = "rockchip,rk3228-dw-hdmi"; - ... - phys = <&hdmi_phy>; - phy-names = "hdmi"; - ... - }; diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml index 1f1f8863b80d0..b42f1272903d8 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml @@ -13,6 +13,7 @@ maintainers: properties: compatible: enum: + - rockchip,rk3576-usbdp-phy - rockchip,rk3588-usbdp-phy reg: diff --git a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml index 4e15d90d08b0e..293fb6a9b1c33 100644 --- a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml @@ -17,6 +17,7 @@ description: properties: compatible: enum: + - qcom,sa8775p-edp-phy - qcom,sc7280-edp-phy - qcom,sc8180x-edp-phy - qcom,sc8280xp-dp-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml index f1f4e4f83352d..1636285fbe535 100644 --- a/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml @@ -18,6 +18,7 @@ properties: enum: - qcom,msm8998-qmp-usb3-phy - qcom,qcm2290-qmp-usb3-phy + - qcom,qcs615-qmp-usb3-phy - qcom,sdm660-qmp-usb3-phy - qcom,sm6115-qmp-usb3-phy @@ -96,6 +97,7 @@ allOf: contains: enum: - qcom,msm8998-qmp-usb3-phy + - qcom,qcs615-qmp-usb3-phy - qcom,sdm660-qmp-usb3-phy then: properties: diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index 95eecbaef05c0..4aed4b5d65ec5 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -25,6 +25,7 @@ properties: - qcom,msm8996-qusb2-phy - qcom,msm8998-qusb2-phy - qcom,qcm2290-qusb2-phy + - qcom,qcs615-qusb2-phy - qcom,sdm660-qusb2-phy - qcom,sm4250-qusb2-phy - qcom,sm6115-qusb2-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml index b9107759b2a51..90fc8c039219c 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml @@ -15,7 +15,12 @@ description: properties: compatible: - const: qcom,sa8775p-dwmac-sgmii-phy + oneOf: + - items: + - enum: + - qcom,qcs8300-dwmac-sgmii-phy + - const: qcom,sa8775p-dwmac-sgmii-phy + - const: qcom,sa8775p-dwmac-sgmii-phy reg: items: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 380a9222a51d8..13fdf5f1bebae 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -41,6 +41,7 @@ properties: - qcom,x1e80100-qmp-gen3x2-pcie-phy - qcom,x1e80100-qmp-gen4x2-pcie-phy - qcom,x1e80100-qmp-gen4x4-pcie-phy + - qcom,x1e80100-qmp-gen4x8-pcie-phy reg: minItems: 1 @@ -172,6 +173,7 @@ allOf: - qcom,x1e80100-qmp-gen3x2-pcie-phy - qcom,x1e80100-qmp-gen4x2-pcie-phy - qcom,x1e80100-qmp-gen4x4-pcie-phy + - qcom,x1e80100-qmp-gen4x8-pcie-phy then: properties: clocks: @@ -202,6 +204,7 @@ allOf: - qcom,sm8650-qmp-gen4x2-pcie-phy - qcom,x1e80100-qmp-gen4x2-pcie-phy - qcom,x1e80100-qmp-gen4x4-pcie-phy + - qcom,x1e80100-qmp-gen4x8-pcie-phy then: properties: resets: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml index f9cfbd0b2de6c..72bed2933b034 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml @@ -15,26 +15,35 @@ description: properties: compatible: - enum: - - qcom,msm8996-qmp-ufs-phy - - qcom,msm8998-qmp-ufs-phy - - qcom,sa8775p-qmp-ufs-phy - - qcom,sc7180-qmp-ufs-phy - - qcom,sc7280-qmp-ufs-phy - - qcom,sc8180x-qmp-ufs-phy - - qcom,sc8280xp-qmp-ufs-phy - - qcom,sdm845-qmp-ufs-phy - - qcom,sm6115-qmp-ufs-phy - - qcom,sm6125-qmp-ufs-phy - - qcom,sm6350-qmp-ufs-phy - - qcom,sm7150-qmp-ufs-phy - - qcom,sm8150-qmp-ufs-phy - - qcom,sm8250-qmp-ufs-phy - - qcom,sm8350-qmp-ufs-phy - - qcom,sm8450-qmp-ufs-phy - - qcom,sm8475-qmp-ufs-phy - - qcom,sm8550-qmp-ufs-phy - - qcom,sm8650-qmp-ufs-phy + oneOf: + - items: + - enum: + - qcom,qcs615-qmp-ufs-phy + - const: qcom,sm6115-qmp-ufs-phy + - items: + - enum: + - qcom,qcs8300-qmp-ufs-phy + - const: qcom,sa8775p-qmp-ufs-phy + - enum: + - qcom,msm8996-qmp-ufs-phy + - qcom,msm8998-qmp-ufs-phy + - qcom,sa8775p-qmp-ufs-phy + - qcom,sc7180-qmp-ufs-phy + - qcom,sc7280-qmp-ufs-phy + - qcom,sc8180x-qmp-ufs-phy + - qcom,sc8280xp-qmp-ufs-phy + - qcom,sdm845-qmp-ufs-phy + - qcom,sm6115-qmp-ufs-phy + - qcom,sm6125-qmp-ufs-phy + - qcom,sm6350-qmp-ufs-phy + - qcom,sm7150-qmp-ufs-phy + - qcom,sm8150-qmp-ufs-phy + - qcom,sm8250-qmp-ufs-phy + - qcom,sm8350-qmp-ufs-phy + - qcom,sm8450-qmp-ufs-phy + - qcom,sm8475-qmp-ufs-phy + - qcom,sm8550-qmp-ufs-phy + - qcom,sm8650-qmp-ufs-phy reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml index 0e0b6cae07bc7..baf5134ea3d8a 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml @@ -20,6 +20,7 @@ properties: - qcom,ipq8074-qmp-usb3-phy - qcom,ipq9574-qmp-usb3-phy - qcom,msm8996-qmp-usb3-phy + - qcom,qcs8300-qmp-usb3-uni-phy - qcom,qdu1000-qmp-usb3-uni-phy - qcom,sa8775p-qmp-usb3-uni-phy - qcom,sc8180x-qmp-usb3-uni-phy @@ -111,6 +112,7 @@ allOf: compatible: contains: enum: + - qcom,qcs8300-qmp-usb3-uni-phy - qcom,qdu1000-qmp-usb3-uni-phy - qcom,sa8775p-qmp-usb3-uni-phy - qcom,sc8180x-qmp-usb3-uni-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml index b82f7f5731ed4..142b3c8839d62 100644 --- a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml @@ -17,6 +17,7 @@ properties: oneOf: - items: - enum: + - qcom,sar2130p-snps-eusb2-phy - qcom,sdx75-snps-eusb2-phy - qcom,sm8650-snps-eusb2-phy - qcom,x1e80100-snps-eusb2-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml index 519c2b403f66d..661759b250644 100644 --- a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml @@ -22,6 +22,7 @@ properties: - const: qcom,usb-snps-hs-5nm-phy - items: - enum: + - qcom,qcs8300-usb-hs-phy - qcom,qdu1000-usb-hs-phy - qcom,sc7280-usb-hs-phy - qcom,sc8180x-usb-hs-phy diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml index 5254413137c64..6a7ef556414ce 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml @@ -20,6 +20,7 @@ properties: - rockchip,rk3366-usb2phy - rockchip,rk3399-usb2phy - rockchip,rk3568-usb2phy + - rockchip,rk3576-usb2phy - rockchip,rk3588-usb2phy - rockchip,rv1108-usb2phy @@ -34,10 +35,15 @@ properties: const: 0 clocks: - maxItems: 1 + minItems: 1 + maxItems: 3 clock-names: - const: phyclk + minItems: 1 + items: + - const: phyclk + - const: aclk + - const: aclk_slv assigned-clocks: description: @@ -172,6 +178,41 @@ allOf: - interrupts - interrupt-names + - if: + properties: + compatible: + contains: + enum: + - rockchip,px30-usb2phy + - rockchip,rk3128-usb2phy + - rockchip,rk3228-usb2phy + - rockchip,rk3308-usb2phy + - rockchip,rk3328-usb2phy + - rockchip,rk3366-usb2phy + - rockchip,rk3399-usb2phy + - rockchip,rk3568-usb2phy + - rockchip,rk3588-usb2phy + - rockchip,rv1108-usb2phy + then: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3576-usb2phy + then: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/rockchip,rk3228-hdmi-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,rk3228-hdmi-phy.yaml new file mode 100644 index 0000000000000..ac15bf857ef99 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip,rk3228-hdmi-phy.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/rockchip,rk3228-hdmi-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip HDMI PHY with Innosilicon IP block + +maintainers: + - Heiko Stuebner + +properties: + compatible: + enum: + - rockchip,rk3228-hdmi-phy + - rockchip,rk3328-hdmi-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: sysclk + - const: refoclk + - const: refpclk + + clock-output-names: + description: + The hdmiphy output clock name, that gets fed back to the CRU. + + "#clock-cells": + const: 0 + + interrupts: + maxItems: 1 + + nvmem-cells: + maxItems: 1 + description: A phandle + nvmem specifier for the cpu-version efuse + for adjustment to some frequency settings, depending on cpu-version + + nvmem-cell-names: + items: + - const: cpu-version + + '#phy-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - clock-output-names + - '#clock-cells' + - '#phy-cells' + +allOf: + - if: + properties: + compatible: + contains: + const: rockchip,rk3228-hdmi-phy + + then: + properties: + interrupts: false + + - if: + properties: + compatible: + contains: + const: rockchip,rk3328-hdmi-phy + + then: + required: + - interrupts + +additionalProperties: false + +examples: + - | + + #include + hdmi_phy: phy@12030000 { + compatible = "rockchip,rk3228-hdmi-phy"; + reg = <0x12030000 0x10000>; + #phy-cells = <0>; + clocks = <&cru PCLK_HDMI_PHY>, <&xin24m>, <&cru DCLK_HDMI_PHY>; + clock-names = "sysclk", "refoclk", "refpclk"; + #clock-cells = <0>; + + clock-output-names = "hdmi_phy"; + }; diff --git a/Documentation/devicetree/bindings/phy/st,stm32mp25-combophy.yaml b/Documentation/devicetree/bindings/phy/st,stm32mp25-combophy.yaml new file mode 100644 index 0000000000000..a2e82c0bb56b4 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/st,stm32mp25-combophy.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/st,stm32mp25-combophy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STM32MP25 USB3/PCIe COMBOPHY + +maintainers: + - Christian Bruel + +description: + Single lane PHY shared (exclusive) between the USB3 and PCIe controllers. + Supports 5Gbit/s for USB3 and PCIe gen2 or 2.5Gbit/s for PCIe gen1. + +properties: + compatible: + const: st,stm32mp25-combophy + + reg: + maxItems: 1 + + "#phy-cells": + const: 1 + + clocks: + minItems: 2 + items: + - description: apb Bus clock mandatory to access registers. + - description: ker Internal RCC reference clock for USB3 or PCIe + - description: pad Optional on board clock input for PCIe only. Typically an + external 100Mhz oscillator wired on dedicated CLKIN pad. Used as reference + clock input instead of the ker + + clock-names: + minItems: 2 + items: + - const: apb + - const: ker + - const: pad + + resets: + maxItems: 1 + + reset-names: + const: phy + + power-domains: + maxItems: 1 + + wakeup-source: true + + interrupts: + maxItems: 1 + description: interrupt used for wakeup + + access-controllers: + maxItems: 1 + description: Phandle to the rifsc device to check access right. + + st,ssc-on: + $ref: /schemas/types.yaml#/definitions/flag + description: + A property whose presence indicates that the Spread Spectrum Clocking is active. + + st,rx-equalizer: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 2 + description: + A 3 bit value to tune the RX fixed equalizer setting for optimal eye compliance + + st,output-micro-ohms: + minimum: 3999000 + maximum: 6090000 + default: 4968000 + description: + A value property to tune the Single Ended Output Impedance, simulations results + at 25C for a VDDP=0.8V. The hardware accepts discrete values in this range. + + st,output-vswing-microvolt: + minimum: 442000 + maximum: 803000 + default: 803000 + description: + A value property in microvolt to tune the Single Ended Output Voltage Swing to change the + Vlo, Vhi for a VDDP = 0.8V. The hardware accepts discrete values in this range. + +required: + - compatible + - reg + - "#phy-cells" + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + + phy@480c0000 { + compatible = "st,stm32mp25-combophy"; + reg = <0x480c0000 0x1000>; + #phy-cells = <1>; + clocks = <&rcc CK_BUS_USB3PCIEPHY>, <&rcc CK_KER_USB3PCIEPHY>; + clock-names = "apb", "ker"; + resets = <&rcc USB3PCIEPHY_R>; + reset-names = "phy"; + access-controllers = <&rifsc 67>; + power-domains = <&CLUSTER_PD>; + wakeup-source; + interrupts-extended = <&exti1 45 IRQ_TYPE_EDGE_FALLING>; + }; diff --git a/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml b/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml index 79dad3e89aa69..4a8c3829d85d3 100644 --- a/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml +++ b/Documentation/devicetree/bindings/phy/ti,tcan104x-can.yaml @@ -14,10 +14,15 @@ properties: pattern: "^can-phy" compatible: - enum: - - nxp,tjr1443 - - ti,tcan1042 - - ti,tcan1043 + oneOf: + - items: + - enum: + - microchip,ata6561 + - const: ti,tcan1042 + - enum: + - ti,tcan1042 + - ti,tcan1043 + - nxp,tjr1443 '#phy-cells': const: 0 diff --git a/Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml new file mode 100644 index 0000000000000..b2601d698dcd4 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml @@ -0,0 +1,400 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/airoha,en7581-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN7581 Pin Controller + +maintainers: + - Lorenzo Bianconi + +description: + The Airoha's EN7581 Pin controller is used to control SoC pins. + +properties: + compatible: + const: airoha,en7581-pinctrl + + interrupts: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + +allOf: + - $ref: pinctrl.yaml# + +required: + - compatible + - interrupts + - gpio-controller + - "#gpio-cells" + - interrupt-controller + - "#interrupt-cells" + +patternProperties: + '-pins$': + type: object + + patternProperties: + '^mux(-|$)': + type: object + + description: + pinmux configuration nodes. + + $ref: /schemas/pinctrl/pinmux-node.yaml + + properties: + function: + description: + A string containing the name of the function to mux to the group. + enum: [pon, tod_1pps, sipo, mdio, uart, i2c, jtag, pcm, spi, + pcm_spi, i2s, emmc, pnand, pcie_reset, pwm, phy1_led0, + phy2_led0, phy3_led0, phy4_led0, phy1_led1, phy2_led1, + phy3_led1, phy4_led1] + + groups: + description: + An array of strings. Each string contains the name of a group. + + required: + - function + - groups + + allOf: + - if: + properties: + function: + const: pon + then: + properties: + groups: + enum: [pon] + - if: + properties: + function: + const: tod_1pps + then: + properties: + groups: + enum: [pon_tod_1pps, gsw_tod_1pps] + - if: + properties: + function: + const: sipo + then: + properties: + groups: + enum: [sipo, sipo_rclk] + - if: + properties: + function: + const: mdio + then: + properties: + groups: + enum: [mdio] + - if: + properties: + function: + const: uart + then: + properties: + groups: + items: + enum: [uart2, uart2_cts_rts, hsuart, hsuart_cts_rts, + uart4, uart5] + maxItems: 2 + - if: + properties: + function: + const: i2c + then: + properties: + groups: + enum: [i2c1] + - if: + properties: + function: + const: jtag + then: + properties: + groups: + enum: [jtag_udi, jtag_dfd] + - if: + properties: + function: + const: pcm + then: + properties: + groups: + enum: [pcm1, pcm2] + - if: + properties: + function: + const: spi + then: + properties: + groups: + items: + enum: [spi_quad, spi_cs1] + maxItems: 2 + - if: + properties: + function: + const: pcm_spi + then: + properties: + groups: + items: + enum: [pcm_spi, pcm_spi_int, pcm_spi_rst, pcm_spi_cs1, + pcm_spi_cs2_p156, pcm_spi_cs2_p128, pcm_spi_cs3, + pcm_spi_cs4] + maxItems: 7 + - if: + properties: + function: + const: i2c + then: + properties: + groups: + enum: [i2s] + - if: + properties: + function: + const: emmc + then: + properties: + groups: + enum: [emmc] + - if: + properties: + function: + const: pnand + then: + properties: + groups: + enum: [pnand] + - if: + properties: + function: + const: pcie_reset + then: + properties: + groups: + enum: [pcie_reset0, pcie_reset1, pcie_reset2] + - if: + properties: + function: + const: pwm + then: + properties: + groups: + enum: [gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6, + gpio7, gpio8, gpio9, gpio10, gpio11, gpio12, gpio13, + gpio14, gpio15, gpio16, gpio17, gpio18, gpio19, + gpio20, gpio21, gpio22, gpio23, gpio24, gpio25, + gpio26, gpio27, gpio28, gpio29, gpio30, gpio31, + gpio36, gpio37, gpio38, gpio39, gpio40, gpio41, + gpio42, gpio43, gpio44, gpio45, gpio46, gpio47] + - if: + properties: + function: + const: phy1_led0 + then: + properties: + groups: + enum: [gpio33, gpio34, gpio35, gpio42] + - if: + properties: + function: + const: phy2_led0 + then: + properties: + groups: + enum: [gpio33, gpio34, gpio35, gpio42] + - if: + properties: + function: + const: phy3_led0 + then: + properties: + groups: + enum: [gpio33, gpio34, gpio35, gpio42] + - if: + properties: + function: + const: phy4_led0 + then: + properties: + groups: + enum: [gpio33, gpio34, gpio35, gpio42] + - if: + properties: + function: + const: phy1_led1 + then: + properties: + groups: + enum: [gpio43, gpio44, gpio45, gpio46] + - if: + properties: + function: + const: phy2_led1 + then: + properties: + groups: + enum: [gpio43, gpio44, gpio45, gpio46] + - if: + properties: + function: + const: phy3_led1 + then: + properties: + groups: + enum: [gpio43, gpio44, gpio45, gpio46] + - if: + properties: + function: + const: phy4_led1 + then: + properties: + groups: + enum: [gpio43, gpio44, gpio45, gpio46] + + additionalProperties: false + + '^conf(-|$)': + type: object + + description: + pinconf configuration nodes. + + $ref: /schemas/pinctrl/pincfg-node.yaml + + properties: + pins: + description: + An array of strings. Each string contains the name of a pin. + items: + enum: [uart1_txd, uart1_rxd, i2c_scl, i2c_sda, spi_cs0, spi_clk, + spi_mosi, spi_miso, gpio0, gpio1, gpio2, gpio3, gpio4, + gpio5, gpio6, gpio7, gpio8, gpio9, gpio10, gpio11, gpio12, + gpio13, gpio14, gpio15, gpio16, gpio17, gpio18, gpio19, + gpio20, gpio21, gpio22, gpio23, gpio24, gpio25, gpio26, + gpio27, gpio28, gpio29, gpio30, gpio31, gpio32, gpio33, + gpio34, gpio35, gpio36, gpio37, gpio38, gpio39, gpio40, + gpio41, gpio42, gpio43, gpio44, gpio45, gpio46, + pcie_reset0, pcie_reset1, pcie_reset2] + minItems: 1 + maxItems: 58 + + bias-disable: true + + bias-pull-up: true + + bias-pull-down: true + + input-enable: true + + output-enable: true + + output-low: true + + output-high: true + + drive-open-drain: true + + drive-strength: + description: + Selects the drive strength for MIO pins, in mA. + enum: [2, 4, 6, 8] + + required: + - pins + + additionalProperties: false + + additionalProperties: false + +additionalProperties: false + +examples: + - | + #include + + pinctrl { + compatible = "airoha,en7581-pinctrl"; + + interrupt-parent = <&gic>; + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + pcie1-rst-pins { + conf { + pins = "pcie_reset1"; + drive-open-drain = <1>; + }; + }; + + pwm-pins { + mux { + function = "pwm"; + groups = "gpio18"; + }; + }; + + spi-pins { + mux { + function = "spi"; + groups = "spi_quad", "spi_cs1"; + }; + }; + + uart2-pins { + mux { + function = "uart"; + groups = "uart2", "uart2_cts_rts"; + }; + }; + + uar5-pins { + mux { + function = "uart"; + groups = "uart5"; + }; + }; + + mmc-pins { + mux { + function = "emmc"; + groups = "emmc"; + }; + }; + + mdio-pins { + mux { + function = "mdio"; + groups = "mdio"; + }; + + conf { + pins = "gpio2"; + output-enable; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/amlogic,meson8-pinctrl-cbus.yaml b/Documentation/devicetree/bindings/pinctrl/amlogic,meson8-pinctrl-cbus.yaml index 412bbcc276f3b..c954761e01505 100644 --- a/Documentation/devicetree/bindings/pinctrl/amlogic,meson8-pinctrl-cbus.yaml +++ b/Documentation/devicetree/bindings/pinctrl/amlogic,meson8-pinctrl-cbus.yaml @@ -46,7 +46,7 @@ patternProperties: - const: gpio gpio-line-names: - minItems: 86 # AXG + minItems: 83 # Meson8b maxItems: 120 # Meson8 unevaluatedProperties: diff --git a/Documentation/devicetree/bindings/pinctrl/canaan,k230-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/canaan,k230-pinctrl.yaml new file mode 100644 index 0000000000000..0b462eb6dfe16 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/canaan,k230-pinctrl.yaml @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/canaan,k230-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan Kendryte K230 Pin Controller + +maintainers: + - Ze Huang <18771902331@163.com> + +description: + The Canaan Kendryte K230 platform includes 64 IO pins, each capable of + multiplexing up to 5 different functions. Pin function configuration is + performed on a per-pin basis. + +properties: + compatible: + const: canaan,k230-pinctrl + + reg: + maxItems: 1 + +patternProperties: + '-pins$': + type: object + additionalProperties: false + description: + A pinctrl node should contain at least one subnode representing the + pinctrl groups available on the machine. + + patternProperties: + '-cfg$': + type: object + allOf: + - $ref: /schemas/pinctrl/pincfg-node.yaml + - $ref: /schemas/pinctrl/pinmux-node.yaml + additionalProperties: false + description: + Each subnode will list the pins it needs, and how they should + be configured, with regard to muxer configuration, bias, input + enable/disable, input schmitt trigger, slew-rate enable/disable, + slew-rate, drive strength. + + properties: + pinmux: + description: + The list of GPIOs and their mux settings that properties in + the node apply to. This should be set with the macro + 'K230_PINMUX(pin, mode)' + + bias-disable: true + + bias-pull-up: true + + bias-pull-down: true + + drive-strength: + minimum: 0 + maximum: 15 + + input-enable: true + + output-enable: true + + input-schmitt-enable: true + + slew-rate: + description: | + slew rate control enable + 0: disable + 1: enable + + enum: [0, 1] + + power-source: + description: | + Specifies the power source voltage for the IO bank that the + pin belongs to. Each bank of IO pins operate at a specific, + fixed voltage levels. Incorrect voltage configuration can + damage the chip. The defined constants represent the + possible voltage configurations: + + - K230_MSC_3V3 (value 0): 3.3V power supply + - K230_MSC_1V8 (value 1): 1.8V power supply + + The following banks have the corresponding voltage + configurations: + + - bank IO0 to IO1: Fixed at 1.8V + - bank IO2 to IO13: Fixed at 1.8V + - bank IO14 to IO25: Fixed at 1.8V + - bank IO26 to IO37: Fixed at 1.8V + - bank IO38 to IO49: Fixed at 1.8V + - bank IO50 to IO61: Fixed at 3.3V + - bank IO62 to IO63: Fixed at 1.8V + + enum: [0, 1] + + required: + - pinmux + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + pinctrl@91105000 { + compatible = "canaan,k230-pinctrl"; + reg = <0x91105000 0x100>; + + uart2-pins { + uart2-pins-cfg { + pinmux = <0x503>, /* uart2 txd */ + <0x603>; /* uart2 rxd */ + slew-rate = <0>; + drive-strength = <4>; + power-source = <1>; + input-enable; + output-enable; + bias-disable; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.txt deleted file mode 100644 index c083dfd25db93..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.txt +++ /dev/null @@ -1,33 +0,0 @@ -* Freescale IMX35 IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx35-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx35 datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_DRIVE_VOLAGAGE_18 (1 << 13) -PAD_CTL_DRIVE_VOLAGAGE_33 (0 << 13) -PAD_CTL_HYS (1 << 8) -PAD_CTL_PKE (1 << 7) -PAD_CTL_PUE (1 << 6) -PAD_CTL_PUS_100K_DOWN (0 << 4) -PAD_CTL_PUS_47K_UP (1 << 4) -PAD_CTL_PUS_100K_UP (2 << 4) -PAD_CTL_PUS_22K_UP (3 << 4) -PAD_CTL_ODE_CMOS (0 << 3) -PAD_CTL_ODE_OPENDRAIN (1 << 3) -PAD_CTL_DSE_NOMINAL (0 << 1) -PAD_CTL_DSE_HIGH (1 << 1) -PAD_CTL_DSE_MAX (2 << 1) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx35-pinfunc.h in device tree source folder for all available -imx35 PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx6ul-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml similarity index 50% rename from Documentation/devicetree/bindings/pinctrl/fsl,imx6ul-pinctrl.yaml rename to Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml index 906b264a9e3cd..265c43ab76f4c 100644 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx6ul-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/pinctrl/fsl,imx6ul-pinctrl.yaml# +$id: http://devicetree.org/schemas/pinctrl/fsl,imx35-pinctrl.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Freescale IMX6UL IOMUX Controller +title: Freescale IMX35/IMX5x/IMX6 IOMUX Controller maintainers: - Dong Aisheng @@ -18,9 +18,21 @@ allOf: properties: compatible: - enum: - - fsl,imx6ul-iomuxc - - fsl,imx6ull-iomuxc-snvs + oneOf: + - enum: + - fsl,imx35-iomuxc + - fsl,imx51-iomuxc + - fsl,imx53-iomuxc + - fsl,imx6dl-iomuxc + - fsl,imx6q-iomuxc + - fsl,imx6sl-iomuxc + - fsl,imx6sll-iomuxc + - fsl,imx6sx-iomuxc + - fsl,imx6ul-iomuxc + - fsl,imx6ull-iomuxc-snvs + - items: + - const: fsl,imx50-iomuxc + - const: fsl,imx53-iomuxc reg: maxItems: 1 @@ -39,9 +51,9 @@ patternProperties: each entry consists of 6 integers and represents the mux and config setting for one pin. The first 5 integers are specified using a PIN_FUNC_ID macro, which can - be found in . The last integer + be found in . The last integer CONFIG is the pad setting value like pull-up on this pin. Please - refer to i.MX6UL Reference Manual for detailed CONFIG settings. + refer to matching i.MX Reference Manual for detailed CONFIG settings. $ref: /schemas/types.yaml#/definitions/uint32-matrix items: items: @@ -56,7 +68,41 @@ patternProperties: - description: | "input_val" indicates the select input value to be applied. - description: | - "pad_setting" indicates the pad configuration value to be applied: + "pad_setting" indicates the pad configuration value to be applied. + Common i.MX35 + PAD_CTL_DRIVE_VOLAGAGE_18 (1 << 13) + PAD_CTL_DRIVE_VOLAGAGE_33 (0 << 13) + PAD_CTL_HYS (1 << 8) + PAD_CTL_PKE (1 << 7) + PAD_CTL_PUE (1 << 6) + PAD_CTL_PUS_100K_DOWN (0 << 4) + PAD_CTL_PUS_47K_UP (1 << 4) + PAD_CTL_PUS_100K_UP (2 << 4) + PAD_CTL_PUS_22K_UP (3 << 4) + PAD_CTL_ODE_CMOS (0 << 3) + PAD_CTL_ODE_OPENDRAIN (1 << 3) + PAD_CTL_DSE_NOMINAL (0 << 1) + PAD_CTL_DSE_HIGH (1 << 1) + PAD_CTL_DSE_MAX (2 << 1) + PAD_CTL_SRE_FAST (1 << 0) + PAD_CTL_SRE_SLOW (0 << 0) + Common i.MX50/i.MX51/i.MX53 bits + PAD_CTL_HVE (1 << 13) + PAD_CTL_HYS (1 << 8) + PAD_CTL_PKE (1 << 7) + PAD_CTL_PUE (1 << 6) + PAD_CTL_PUS_100K_DOWN (0 << 4) + PAD_CTL_PUS_47K_UP (1 << 4) + PAD_CTL_PUS_100K_UP (2 << 4) + PAD_CTL_PUS_22K_UP (3 << 4) + PAD_CTL_ODE (1 << 3) + PAD_CTL_DSE_LOW (0 << 1) + PAD_CTL_DSE_MED (1 << 1) + PAD_CTL_DSE_HIGH (2 << 1) + PAD_CTL_DSE_MAX (3 << 1) + PAD_CTL_SRE_FAST (1 << 0) + PAD_CTL_SRE_SLOW (0 << 0) + Common i.MX6 bits PAD_CTL_HYS (1 << 16) PAD_CTL_PUS_100K_DOWN (0 << 14) PAD_CTL_PUS_47K_UP (1 << 14) @@ -69,6 +115,11 @@ patternProperties: PAD_CTL_SPEED_MED (1 << 6) PAD_CTL_SPEED_HIGH (3 << 6) PAD_CTL_DSE_DISABLE (0 << 3) + PAD_CTL_SRE_FAST (1 << 0) + PAD_CTL_SRE_SLOW (0 << 0) + i.MX6SL/MX6SLL specific bits + PAD_CTL_LVE (1 << 22) (MX6SL/SLL only) + i.MX6SLL/i.MX6SX/i.MX6UL/i.MX6ULL specific bits PAD_CTL_DSE_260ohm (1 << 3) PAD_CTL_DSE_130ohm (2 << 3) PAD_CTL_DSE_87ohm (3 << 3) @@ -76,8 +127,14 @@ patternProperties: PAD_CTL_DSE_52ohm (5 << 3) PAD_CTL_DSE_43ohm (6 << 3) PAD_CTL_DSE_37ohm (7 << 3) - PAD_CTL_SRE_FAST (1 << 0) - PAD_CTL_SRE_SLOW (0 << 0) + i.MX6DL/i.MX6Q/i.MX6SL specific bits + PAD_CTL_DSE_240ohm (1 << 3) + PAD_CTL_DSE_120ohm (2 << 3) + PAD_CTL_DSE_80ohm (3 << 3) + PAD_CTL_DSE_60ohm (4 << 3) + PAD_CTL_DSE_48ohm (5 << 3) + PAD_CTL_DSE_40ohm (6 << 3) + PAD_CTL_DSE_34ohm (7 << 3) required: - fsl,pins @@ -114,3 +171,14 @@ examples: >; }; }; + - | + iomuxc_mx6q: pinctrl@20e0000 { + compatible = "fsl,imx6q-iomuxc"; + reg = <0x20e0000 0x4000>; + + pinctrl_uart4: uart4grp { + fsl,pins = + <0x288 0x658 0x000 0x3 0x0 0x140>, + <0x28c 0x65c 0x938 0x3 0x3 0x140>; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx50-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx50-pinctrl.txt deleted file mode 100644 index 6da01d619d33e..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx50-pinctrl.txt +++ /dev/null @@ -1,32 +0,0 @@ -* Freescale IMX50 IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx50-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx50 datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_HVE (1 << 13) -PAD_CTL_HYS (1 << 8) -PAD_CTL_PKE (1 << 7) -PAD_CTL_PUE (1 << 6) -PAD_CTL_PUS_100K_DOWN (0 << 4) -PAD_CTL_PUS_47K_UP (1 << 4) -PAD_CTL_PUS_100K_UP (2 << 4) -PAD_CTL_PUS_22K_UP (3 << 4) -PAD_CTL_ODE (1 << 3) -PAD_CTL_DSE_LOW (0 << 1) -PAD_CTL_DSE_MED (1 << 1) -PAD_CTL_DSE_HIGH (2 << 1) -PAD_CTL_DSE_MAX (3 << 1) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx50-pinfunc.h in device tree source folder for all available -imx50 PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx51-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx51-pinctrl.txt deleted file mode 100644 index 4d1408fcc99cf..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx51-pinctrl.txt +++ /dev/null @@ -1,32 +0,0 @@ -* Freescale IMX51 IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx51-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx51 datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_HVE (1 << 13) -PAD_CTL_HYS (1 << 8) -PAD_CTL_PKE (1 << 7) -PAD_CTL_PUE (1 << 6) -PAD_CTL_PUS_100K_DOWN (0 << 4) -PAD_CTL_PUS_47K_UP (1 << 4) -PAD_CTL_PUS_100K_UP (2 << 4) -PAD_CTL_PUS_22K_UP (3 << 4) -PAD_CTL_ODE (1 << 3) -PAD_CTL_DSE_LOW (0 << 1) -PAD_CTL_DSE_MED (1 << 1) -PAD_CTL_DSE_HIGH (2 << 1) -PAD_CTL_DSE_MAX (3 << 1) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx51-pinfunc.h in device tree source folder for all available -imx51 PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx53-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx53-pinctrl.txt deleted file mode 100644 index 25dcb77cfaf74..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx53-pinctrl.txt +++ /dev/null @@ -1,32 +0,0 @@ -* Freescale IMX53 IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx53-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx53 datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_HVE (1 << 13) -PAD_CTL_HYS (1 << 8) -PAD_CTL_PKE (1 << 7) -PAD_CTL_PUE (1 << 6) -PAD_CTL_PUS_100K_DOWN (0 << 4) -PAD_CTL_PUS_47K_UP (1 << 4) -PAD_CTL_PUS_100K_UP (2 << 4) -PAD_CTL_PUS_22K_UP (3 << 4) -PAD_CTL_ODE (1 << 3) -PAD_CTL_DSE_LOW (0 << 1) -PAD_CTL_DSE_MED (1 << 1) -PAD_CTL_DSE_HIGH (2 << 1) -PAD_CTL_DSE_MAX (3 << 1) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx53-pinfunc.h in device tree source folder for all available -imx53 PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx6dl-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx6dl-pinctrl.txt deleted file mode 100644 index 0ac5bee875056..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx6dl-pinctrl.txt +++ /dev/null @@ -1,38 +0,0 @@ -* Freescale IMX6 DualLite/Solo IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx6dl-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx6dl datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_HYS (1 << 16) -PAD_CTL_PUS_100K_DOWN (0 << 14) -PAD_CTL_PUS_47K_UP (1 << 14) -PAD_CTL_PUS_100K_UP (2 << 14) -PAD_CTL_PUS_22K_UP (3 << 14) -PAD_CTL_PUE (1 << 13) -PAD_CTL_PKE (1 << 12) -PAD_CTL_ODE (1 << 11) -PAD_CTL_SPEED_LOW (1 << 6) -PAD_CTL_SPEED_MED (2 << 6) -PAD_CTL_SPEED_HIGH (3 << 6) -PAD_CTL_DSE_DISABLE (0 << 3) -PAD_CTL_DSE_240ohm (1 << 3) -PAD_CTL_DSE_120ohm (2 << 3) -PAD_CTL_DSE_80ohm (3 << 3) -PAD_CTL_DSE_60ohm (4 << 3) -PAD_CTL_DSE_48ohm (5 << 3) -PAD_CTL_DSE_40ohm (6 << 3) -PAD_CTL_DSE_34ohm (7 << 3) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx6dl-pinfunc.h in device tree source folder for all available -imx6dl PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx6q-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx6q-pinctrl.txt deleted file mode 100644 index 546610cf2ae71..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx6q-pinctrl.txt +++ /dev/null @@ -1,38 +0,0 @@ -* Freescale IMX6Q IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx6q-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx6q datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_HYS (1 << 16) -PAD_CTL_PUS_100K_DOWN (0 << 14) -PAD_CTL_PUS_47K_UP (1 << 14) -PAD_CTL_PUS_100K_UP (2 << 14) -PAD_CTL_PUS_22K_UP (3 << 14) -PAD_CTL_PUE (1 << 13) -PAD_CTL_PKE (1 << 12) -PAD_CTL_ODE (1 << 11) -PAD_CTL_SPEED_LOW (1 << 6) -PAD_CTL_SPEED_MED (2 << 6) -PAD_CTL_SPEED_HIGH (3 << 6) -PAD_CTL_DSE_DISABLE (0 << 3) -PAD_CTL_DSE_240ohm (1 << 3) -PAD_CTL_DSE_120ohm (2 << 3) -PAD_CTL_DSE_80ohm (3 << 3) -PAD_CTL_DSE_60ohm (4 << 3) -PAD_CTL_DSE_48ohm (5 << 3) -PAD_CTL_DSE_40ohm (6 << 3) -PAD_CTL_DSE_34ohm (7 << 3) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx6q-pinfunc.h in device tree source folder for all available -imx6q PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx6sl-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx6sl-pinctrl.txt deleted file mode 100644 index e5f6d1f065a42..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx6sl-pinctrl.txt +++ /dev/null @@ -1,39 +0,0 @@ -* Freescale IMX6 SoloLite IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx6sl-iomuxc" -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, CONFIG is the pad setting value like - pull-up for this pin. Please refer to imx6sl datasheet for the valid pad - config settings. - -CONFIG bits definition: -PAD_CTL_LVE (1 << 22) -PAD_CTL_HYS (1 << 16) -PAD_CTL_PUS_100K_DOWN (0 << 14) -PAD_CTL_PUS_47K_UP (1 << 14) -PAD_CTL_PUS_100K_UP (2 << 14) -PAD_CTL_PUS_22K_UP (3 << 14) -PAD_CTL_PUE (1 << 13) -PAD_CTL_PKE (1 << 12) -PAD_CTL_ODE (1 << 11) -PAD_CTL_SPEED_LOW (1 << 6) -PAD_CTL_SPEED_MED (2 << 6) -PAD_CTL_SPEED_HIGH (3 << 6) -PAD_CTL_DSE_DISABLE (0 << 3) -PAD_CTL_DSE_240ohm (1 << 3) -PAD_CTL_DSE_120ohm (2 << 3) -PAD_CTL_DSE_80ohm (3 << 3) -PAD_CTL_DSE_60ohm (4 << 3) -PAD_CTL_DSE_48ohm (5 << 3) -PAD_CTL_DSE_40ohm (6 << 3) -PAD_CTL_DSE_34ohm (7 << 3) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx6sl-pinfunc.h in device tree source folder for all available -imx6sl PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx6sll-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx6sll-pinctrl.txt deleted file mode 100644 index 175e8939a3018..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx6sll-pinctrl.txt +++ /dev/null @@ -1,40 +0,0 @@ -* Freescale i.MX6 SLL IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx6sll-iomuxc" -- fsl,pins: each entry consists of 6 integers and represents the mux and config - setting for one pin. The first 5 integers are specified using a PIN_FUNC_ID macro, which can be found in - imx6sll-pinfunc.h under device tree source folder. The last integer CONFIG is - the pad setting value like pull-up on this pin. Please refer to i.MX6SLL - Reference Manual for detailed CONFIG settings. - -CONFIG bits definition: -PAD_CTL_LVE (1 << 22) -PAD_CTL_HYS (1 << 16) -PAD_CTL_PUS_100K_DOWN (0 << 14) -PAD_CTL_PUS_47K_UP (1 << 14) -PAD_CTL_PUS_100K_UP (2 << 14) -PAD_CTL_PUS_22K_UP (3 << 14) -PAD_CTL_PUE (1 << 13) -PAD_CTL_PKE (1 << 12) -PAD_CTL_ODE (1 << 11) -PAD_CTL_SPEED_LOW (0 << 6) -PAD_CTL_SPEED_MED (1 << 6) -PAD_CTL_SPEED_HIGH (3 << 6) -PAD_CTL_DSE_DISABLE (0 << 3) -PAD_CTL_DSE_260ohm (1 << 3) -PAD_CTL_DSE_130ohm (2 << 3) -PAD_CTL_DSE_87ohm (3 << 3) -PAD_CTL_DSE_65ohm (4 << 3) -PAD_CTL_DSE_52ohm (5 << 3) -PAD_CTL_DSE_43ohm (6 << 3) -PAD_CTL_DSE_37ohm (7 << 3) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) - -Refer to imx6sll-pinfunc.h in device tree source folder for all available -imx6sll PIN_FUNC_ID. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx6sx-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx6sx-pinctrl.txt deleted file mode 100644 index b1b595220f1bd..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx6sx-pinctrl.txt +++ /dev/null @@ -1,36 +0,0 @@ -* Freescale i.MX6 SoloX IOMUX Controller - -Please refer to fsl,imx-pinctrl.txt in this directory for common binding part -and usage. - -Required properties: -- compatible: "fsl,imx6sx-iomuxc" -- fsl,pins: each entry consists of 6 integers and represents the mux and config - setting for one pin. The first 5 integers are specified using a PIN_FUNC_ID macro, which can be found in - imx6sx-pinfunc.h under device tree source folder. The last integer CONFIG is - the pad setting value like pull-up on this pin. Please refer to i.MX6 SoloX - Reference Manual for detailed CONFIG settings. - -CONFIG bits definition: -PAD_CTL_HYS (1 << 16) -PAD_CTL_PUS_100K_DOWN (0 << 14) -PAD_CTL_PUS_47K_UP (1 << 14) -PAD_CTL_PUS_100K_UP (2 << 14) -PAD_CTL_PUS_22K_UP (3 << 14) -PAD_CTL_PUE (1 << 13) -PAD_CTL_PKE (1 << 12) -PAD_CTL_ODE (1 << 11) -PAD_CTL_SPEED_LOW (0 << 6) -PAD_CTL_SPEED_MED (1 << 6) -PAD_CTL_SPEED_HIGH (3 << 6) -PAD_CTL_DSE_DISABLE (0 << 3) -PAD_CTL_DSE_260ohm (1 << 3) -PAD_CTL_DSE_130ohm (2 << 3) -PAD_CTL_DSE_87ohm (3 << 3) -PAD_CTL_DSE_65ohm (4 << 3) -PAD_CTL_DSE_52ohm (5 << 3) -PAD_CTL_DSE_43ohm (6 << 3) -PAD_CTL_DSE_37ohm (7 << 3) -PAD_CTL_SRE_FAST (1 << 0) -PAD_CTL_SRE_SLOW (0 << 0) diff --git a/Documentation/devicetree/bindings/pinctrl/microchip,mcp23s08.yaml b/Documentation/devicetree/bindings/pinctrl/microchip,mcp23s08.yaml new file mode 100644 index 0000000000000..e07f4723de177 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/microchip,mcp23s08.yaml @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/microchip,mcp23s08.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip I/O expander with serial interface (I2C/SPI) + +maintainers: + - Himanshu Bhavani + +description: + Microchip MCP23008, MCP23017, MCP23S08, MCP23S17, MCP23S18 GPIO expander + chips.These chips provide 8 or 16 GPIO pins with either I2C or SPI interface. + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - microchip,mcp23s08 + - microchip,mcp23s17 + - microchip,mcp23s18 + - microchip,mcp23008 + - microchip,mcp23017 + - microchip,mcp23018 + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + interrupts: + maxItems: 1 + + reset-gpios: + description: GPIO specifier for active-low reset pin. + maxItems: 1 + + microchip,spi-present-mask: + description: + Multiple SPI chips can share the same SPI chipselect. Set a bit in + bit0-7 in this mask to 1 if there is a chip connected with the + corresponding spi address set. For example if you have a chip with + address 3 connected, you have to set bit3 to 1, which is 0x08. mcp23s08 + chip variant only supports bits 0-3. It is not possible to mix mcp23s08 + and mcp23s17 on the same chipselect. Set at least one bit to 1 for SPI + chips. + $ref: /schemas/types.yaml#/definitions/uint8 + + microchip,irq-mirror: + type: boolean + description: + Sets the mirror flag in the IOCON register. Devices with two interrupt + outputs (these are the devices ending with 17 and those that have 16 IOs) + have two IO banks IO 0-7 form bank 1 and IO 8-15 are bank 2. These chips + have two different interrupt outputs One for bank 1 and another for + bank 2. If irq-mirror is set, both interrupts are generated regardless of + the bank that an input change occurred on. If it is not set,the interrupt + are only generated for the bank they belong to. + + microchip,irq-active-high: + type: boolean + description: + Sets the INTPOL flag in the IOCON register.This configures the IRQ output + polarity as active high. + + drive-open-drain: + type: boolean + description: + Sets the ODR flag in the IOCON register. This configures the IRQ output as + open drain active low. + + pinmux: + type: object + properties: + pins: + description: + The list of GPIO pins controlled by this node. Each pin name + corresponds to a physical pin on the GPIO expander. + items: + pattern: '^gpio([0-9]|[1][0-5])$' + maxItems: 16 + + bias-pull-up: + type: boolean + description: + Configures pull-up resistors for the GPIO pins. Absence of this + property will leave the configuration in its default state. + + required: + - pins + + additionalProperties: false + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + gpio@21 { + compatible = "microchip,mcp23017"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + + interrupt-parent = <&gpio1>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <2>; + + microchip,irq-mirror; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c_gpio0>, <&gpiopullups>; + reset-gpios = <&gpio6 15 GPIO_ACTIVE_LOW>; + + gpiopullups: pinmux { + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15"; + bias-pull-up; + }; + }; + }; + + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@0 { + compatible = "microchip,mcp23s17"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + spi-max-frequency = <1000000>; + microchip,spi-present-mask = /bits/ 8 <0x01>; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/mscc,ocelot-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mscc,ocelot-pinctrl.yaml index dbb3e1bd58c10..31bc30a817529 100644 --- a/Documentation/devicetree/bindings/pinctrl/mscc,ocelot-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mscc,ocelot-pinctrl.yaml @@ -12,14 +12,24 @@ maintainers: properties: compatible: - enum: - - microchip,lan966x-pinctrl - - microchip,sparx5-pinctrl - - mscc,jaguar2-pinctrl - - mscc,luton-pinctrl - - mscc,ocelot-pinctrl - - mscc,serval-pinctrl - - mscc,servalt-pinctrl + oneOf: + - enum: + - microchip,lan966x-pinctrl + - microchip,lan9691-pinctrl + - microchip,sparx5-pinctrl + - mscc,jaguar2-pinctrl + - mscc,luton-pinctrl + - mscc,ocelot-pinctrl + - mscc,serval-pinctrl + - mscc,servalt-pinctrl + - items: + - enum: + - microchip,lan9698-pinctrl + - microchip,lan9696-pinctrl + - microchip,lan9694-pinctrl + - microchip,lan9693-pinctrl + - microchip,lan9692-pinctrl + - const: microchip,lan9691-pinctrl reg: items: @@ -85,6 +95,7 @@ allOf: contains: enum: - microchip,lan966x-pinctrl + - microchip,lan9691-pinctrl - microchip,sparx5-pinctrl then: properties: diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt deleted file mode 100644 index 2fa5edac7a35a..0000000000000 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt +++ /dev/null @@ -1,148 +0,0 @@ -Microchip MCP2308/MCP23S08/MCP23017/MCP23S17 driver for -8-/16-bit I/O expander with serial interface (I2C/SPI) - -Required properties: -- compatible : Should be - - "mcp,mcp23s08" (DEPRECATED) for 8 GPIO SPI version - - "mcp,mcp23s17" (DEPRECATED) for 16 GPIO SPI version - - "mcp,mcp23008" (DEPRECATED) for 8 GPIO I2C version or - - "mcp,mcp23017" (DEPRECATED) for 16 GPIO I2C version of the chip - - - "microchip,mcp23s08" for 8 GPIO SPI version - - "microchip,mcp23s17" for 16 GPIO SPI version - - "microchip,mcp23s18" for 16 GPIO SPI version - - "microchip,mcp23008" for 8 GPIO I2C version or - - "microchip,mcp23017" for 16 GPIO I2C version of the chip - - "microchip,mcp23018" for 16 GPIO I2C version - NOTE: Do not use the old mcp prefix any more. It is deprecated and will be - removed. -- #gpio-cells : Should be two. - - first cell is the pin number - - second cell is used to specify flags as described in - 'Documentation/devicetree/bindings/gpio/gpio.txt'. Allowed values defined by - 'include/dt-bindings/gpio/gpio.h' (e.g. GPIO_ACTIVE_LOW). -- gpio-controller : Marks the device node as a GPIO controller. -- reg : For an address on its bus. I2C uses this a the I2C address of the chip. - SPI uses this to specify the chipselect line which the chip is - connected to. The driver and the SPI variant of the chip support - multiple chips on the same chipselect. Have a look at - microchip,spi-present-mask below. - -Required device specific properties (only for SPI chips): -- mcp,spi-present-mask (DEPRECATED) -- microchip,spi-present-mask : This is a present flag, that makes only sense for SPI - chips - as the name suggests. Multiple SPI chips can share the same - SPI chipselect. Set a bit in bit0-7 in this mask to 1 if there is a - chip connected with the corresponding spi address set. For example if - you have a chip with address 3 connected, you have to set bit3 to 1, - which is 0x08. mcp23s08 chip variant only supports bits 0-3. It is not - possible to mix mcp23s08 and mcp23s17 on the same chipselect. Set at - least one bit to 1 for SPI chips. - NOTE: Do not use the old mcp prefix any more. It is deprecated and will be - removed. -- spi-max-frequency = The maximum frequency this chip is able to handle - -Optional properties: -- #interrupt-cells : Should be two. - - first cell is the pin number - - second cell is used to specify flags. -- interrupt-controller: Marks the device node as a interrupt controller. -- drive-open-drain: Sets the ODR flag in the IOCON register. This configures - the IRQ output as open drain active low. -- reset-gpios: Corresponds to the active-low RESET# pin for the chip - -Optional device specific properties: -- microchip,irq-mirror: Sets the mirror flag in the IOCON register. Devices - with two interrupt outputs (these are the devices ending with 17 and - those that have 16 IOs) have two IO banks: IO 0-7 form bank 1 and - IO 8-15 are bank 2. These chips have two different interrupt outputs: - One for bank 1 and another for bank 2. If irq-mirror is set, both - interrupts are generated regardless of the bank that an input change - occurred on. If it is not set, the interrupt are only generated for the - bank they belong to. - On devices with only one interrupt output this property is useless. -- microchip,irq-active-high: Sets the INTPOL flag in the IOCON register. This - configures the IRQ output polarity as active high. - -Example I2C (with interrupt): -gpiom1: gpio@20 { - compatible = "microchip,mcp23017"; - gpio-controller; - #gpio-cells = <2>; - reg = <0x20>; - - interrupt-parent = <&gpio1>; - interrupts = <17 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; - #interrupt-cells=<2>; - microchip,irq-mirror; -}; - -Example SPI: -gpiom1: gpio@0 { - compatible = "microchip,mcp23s17"; - gpio-controller; - #gpio-cells = <2>; - microchip,spi-present-mask = <0x01>; - reg = <0>; - spi-max-frequency = <1000000>; -}; - -Pull-up configuration -===================== - -If pins are used as output, they can also be configured with pull-ups. This is -done with pinctrl. - -Please refer file -for details of the common pinctrl bindings used by client devices, -including the meaning of the phrase "pin configuration node". - -Optional Pinmux properties: --------------------------- -Following properties are required if default setting of pins are required -at boot. -- pinctrl-names: A pinctrl state named per . -- pinctrl[0...n]: Properties to contain the phandle for pinctrl states per - . - -The pin configurations are defined as child of the pinctrl states node. Each -sub-node have following properties: - -Required properties: ------------------- -- pins: List of pins. Valid values of pins properties are: - gpio0 ... gpio7 for the devices with 8 GPIO pins and - gpio0 ... gpio15 for the devices with 16 GPIO pins. - -Optional properties: -------------------- -The following optional property is defined in the pinmux DT binding document -. Absence of this property will leave the configuration -in its default state. - bias-pull-up - -Example with pinctrl to pull-up output pins: -gpio21: gpio@21 { - compatible = "microchip,mcp23017"; - gpio-controller; - #gpio-cells = <0x2>; - reg = <0x21>; - interrupt-parent = <&socgpio>; - interrupts = <0x17 0x8>; - interrupt-names = "mcp23017@21 irq"; - interrupt-controller; - #interrupt-cells = <0x2>; - microchip,irq-mirror; - pinctrl-names = "default"; - pinctrl-0 = <&i2cgpio0irq>, <&gpio21pullups>; - reset-gpios = <&gpio6 15 GPIO_ACTIVE_LOW>; - - gpio21pullups: pinmux { - pins = "gpio0", "gpio1", "gpio2", "gpio3", - "gpio4", "gpio5", "gpio6", "gpio7", - "gpio8", "gpio9", "gpio10", "gpio11", - "gpio12", "gpio13", "gpio14", "gpio15"; - bias-pull-up; - }; -}; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml index e02595316c9f4..f83dbf32ad183 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml @@ -33,6 +33,10 @@ properties: - ti,omap5-padconf - ti,j7200-padconf - const: pinctrl-single + - items: + - enum: + - marvell,pxa1908-padconf + - const: pinconf-single reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq5424-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5424-tlmm.yaml new file mode 100644 index 0000000000000..5e64a232fc7a4 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5424-tlmm.yaml @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,ipq5424-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ5424 TLMM pin controller + +maintainers: + - Bjorn Andersson + +description: + Top Level Mode Multiplexer pin controller in Qualcomm IPQ5424 SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,ipq5424-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 25 + + gpio-line-names: + maxItems: 50 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-ipq5424-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-ipq5424-tlmm-state" + additionalProperties: false + +$defs: + qcom-ipq5424-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + pattern: "^gpio([0-9]|[1-4][0-9])$" + minItems: 1 + maxItems: 50 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ atest_char, atest_char0, atest_char1, atest_char2, atest_char3, + atest_tic, audio_pri, audio_pri0, audio_pri1, audio_sec, + audio_sec0, audio_sec1, core_voltage, cri_trng0, cri_trng1, + cri_trng2, cri_trng3, cxc_clk, cxc_data, dbg_out, gcc_plltest, + gcc_tlmm, gpio, i2c0_scl, i2c0_sda, i2c1_scl, i2c1_sda, i2c11, + mac0, mac1, mdc_mst, mdc_slv, mdio_mst, mdio_slv, pcie0_clk, + pcie0_wake, pcie1_clk, pcie1_wake, pcie2_clk, pcie2_wake, + pcie3_clk, pcie3_wake, pll_test, prng_rosc0, prng_rosc1, + prng_rosc2, prng_rosc3, PTA0_0, PTA0_1, PTA0_2, PTA10, PTA11, + pwm0, pwm1, pwm2, qdss_cti_trig_in_a0, qdss_cti_trig_out_a0, + qdss_cti_trig_in_a1, qdss_cti_trig_out_a1, qdss_cti_trig_in_b0, + qdss_cti_trig_out_b0, qdss_cti_trig_in_b1, qdss_cti_trig_out_b1, + qdss_traceclk_a, qdss_tracectl_a, qdss_tracedata_a, qspi_clk, + qspi_cs, qspi_data, resout, rx0, rx1, rx2, sdc_clk, sdc_cmd, + sdc_data, spi0, spi1, spi10, spi11, tsens_max, uart0, uart1, + wci_txd, wci_rxd, wsi_clk, wsi_data ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq5424-tlmm"; + reg = <0x01000000 0x300000>; + gpio-controller; + #gpio-cells = <0x2>; + gpio-ranges = <&tlmm 0 0 50>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <0x2>; + + uart1_pins: uart1-state { + pins = "gpio43", "gpio44"; + function = "uart1"; + drive-strength = <8>; + bias-pull-up; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml index c1b799167d81b..055cea5452eb6 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml @@ -48,6 +48,7 @@ properties: - qcom,pm8916-gpio - qcom,pm8917-gpio - qcom,pm8921-gpio + - qcom,pm8937-gpio - qcom,pm8941-gpio - qcom,pm8950-gpio - qcom,pm8953-gpio @@ -184,6 +185,7 @@ allOf: - qcom,pm8226-gpio - qcom,pm8350b-gpio - qcom,pm8550ve-gpio + - qcom,pm8937-gpio - qcom,pm8950-gpio - qcom,pm8953-gpio - qcom,pmi632-gpio @@ -468,6 +470,7 @@ $defs: - gpio1-gpio6 for pm8550vs - gpio1-gpio38 for pm8917 - gpio1-gpio44 for pm8921 + - gpio1-gpio8 for pm8937 (hole on gpio3, gpio4 and gpio6) - gpio1-gpio36 for pm8941 - gpio1-gpio8 for pm8950 (hole on gpio3) - gpio1-gpio8 for pm8953 (hole on gpio3 and gpio6) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml index 43146709e2044..9364ae05f3e68 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml @@ -22,6 +22,7 @@ properties: - qcom,pm8226-mpp - qcom,pm8841-mpp - qcom,pm8916-mpp + - qcom,pm8937-mpp - qcom,pm8941-mpp - qcom,pm8950-mpp - qcom,pmi8950-mpp @@ -92,6 +93,7 @@ $defs: this subnode. Valid pins are - mpp1-mpp4 for pm8841 - mpp1-mpp4 for pm8916 + - mpp1-mpp4 for pm8937 - mpp1-mpp8 for pm8941 - mpp1-mpp4 for pm8950 - mpp1-mpp4 for pmi8950 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,qcs615-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,qcs615-tlmm.yaml new file mode 100644 index 0000000000000..1ce4b5df584ab --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,qcs615-tlmm.yaml @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,qcs615-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. QCS615 TLMM block + +maintainers: + - Lijuan Gao + +description: + Top Level Mode Multiplexer pin controller in Qualcomm QCS615 SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,qcs615-tlmm + + reg: + maxItems: 3 + + reg-names: + items: + - const: east + - const: west + - const: south + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 62 + + gpio-line-names: + maxItems: 123 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-qcs615-tlmm-state" + - type: object + patternProperties: + "-pins$": + $ref: "#/$defs/qcom-qcs615-tlmm-state" + additionalProperties: false + +$defs: + qcom-qcs615-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-2])$" + - enum: [ sdc1_clk, sdc1_cmd, sdc1_data, sdc1_rclk, + sdc2_clk, sdc2_cmd, sdc2_data, ufs_reset ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + enum: [ gpio, adsp_ext, agera_pll, aoss_cti, atest_char, atest_tsens, + atest_usb, cam_mclk, cci_async, cci_i2c, cci_timer, copy_gp, + copy_phase, cri_trng, dbg_out_clk, ddr_bist, ddr_pxi, dp_hot, + edp_hot, edp_lcd, emac_gcc, emac_phy_intr, forced_usb, gcc_gp, + gp_pdm, gps_tx, hs0_mi2s, hs1_mi2s, jitter_bist, ldo_en, + ldo_update, m_voc, mclk1, mclk2, mdp_vsync, mdp_vsync0_out, + mdp_vsync1_out, mdp_vsync2_out, mdp_vsync3_out, mdp_vsync4_out, + mdp_vsync5_out, mi2s_1, mss_lte, nav_pps_in, nav_pps_out, + pa_indicator_or, pcie_clk_req, pcie_ep_rst, phase_flag, pll_bist, + pll_bypassnl, pll_reset_n, prng_rosc, qdss_cti, qdss_gpio, + qlink_enable, qlink_request, qspi, qup0, qup1, rgmii, + sd_write_protect, sp_cmu, ter_mi2s, tgu_ch, uim1, uim2, usb0_hs, + usb1_hs, usb_phy_ps, vfr_1, vsense_trigger_mirnat, wlan, wsa_clk, + wsa_data ] + + required: + - pins + +required: + - compatible + - reg + - reg-names + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@3000000 { + compatible = "qcom,qcs615-tlmm"; + reg = <0x03100000 0x300000>, + <0x03500000 0x300000>, + <0x03c00000 0x300000>; + reg-names = "east", "west", "south"; + interrupts = ; + gpio-ranges = <&tlmm 0 0 123>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + qup3-uart2-state { + pins ="gpio16", "gpio17"; + function = "qup0"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,qcs8300-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,qcs8300-tlmm.yaml new file mode 100644 index 0000000000000..bb0d7132886a1 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,qcs8300-tlmm.yaml @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,qcs8300-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. QCS8300 TLMM block + +maintainers: + - Jingyi Wang + +description: | + Top Level Mode Multiplexer pin controller in Qualcomm QCS8300 SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,qcs8300-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 67 + + gpio-line-names: + maxItems: 133 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-qcs8300-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-qcs8300-tlmm-state" + additionalProperties: false + +$defs: + qcom-qcs8300-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-2][0-9]|13[0-2])$" + - enum: [ ufs_reset, sdc1_rclk, sdc1_clk, sdc1_cmd, sdc1_data ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ aoss_cti, atest_char, atest_usb2, audio_ref, cam_mclk, + cci_async, cci_i2c_scl, cci_i2c_sda, cci_timer, cri_trng, + dbg_out, ddr_bist, ddr_pxi0, ddr_pxi1, ddr_pxi2, ddr_pxi3, + edp0_hot, edp0_lcd, edp1_lcd, egpio, emac0_mcg0, emac0_mcg1, + emac0_mcg2, emac0_mcg3, emac0_mdc, emac0_mdio, emac0_ptp_aux, + emac0_ptp_pps, gcc_gp1, gcc_gp2, gcc_gp3, gcc_gp4, gcc_gp5, + gpio, hs0_mi2s, hs1_mi2s, hs2_mi2s, ibi_i3c, jitter_bist, + mdp0_vsync0, mdp0_vsync1, mdp0_vsync3, mdp0_vsync6, mdp0_vsync7, + mdp_vsync, mi2s1_data0, mi2s1_data1, mi2s1_sck, mi2s1_ws, + mi2s2_data0, mi2s2_data1, mi2s2_sck, mi2s2_ws, mi2s_mclk0, + mi2s_mclk1, pcie0_clkreq, pcie1_clkreq, phase_flag, pll_bist, + pll_clk, prng_rosc0, prng_rosc1, prng_rosc2, prng_rosc3, + qdss_cti, qdss_gpio, qup0_se0, qup0_se1, qup0_se2, qup0_se3, + qup0_se4, qup0_se5, qup0_se6, qup0_se7, qup1_se0, qup1_se1, + qup1_se2, qup1_se3, qup1_se4, qup1_se5, qup1_se6, qup1_se7, + qup2_se0, sailss_emac0, sailss_ospi, sail_top, sgmii_phy, + tb_trig, tgu_ch0, tgu_ch1, tgu_ch2, tgu_ch3, tsense_pwm1, + tsense_pwm2, tsense_pwm3, tsense_pwm4, usb2phy_ac, + vsense_trigger ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@f100000 { + compatible = "qcom,qcs8300-tlmm"; + reg = <0x0f100000 0x300000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 133>; + interrupt-controller; + #interrupt-cells = <2>; + + qup-uart7-state { + pins = "gpio43", "gpio44"; + function = "qup0_se7"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sa8775p-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sa8775p-tlmm.yaml index e9abbf2c0689b..749dbc563ac50 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sa8775p-tlmm.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sa8775p-tlmm.yaml @@ -17,7 +17,13 @@ allOf: properties: compatible: - const: qcom,sa8775p-tlmm + oneOf: + - items: + - enum: + - qcom,sa8255p-tlmm + - const: qcom,sa8775p-tlmm + - items: + - const: qcom,sa8775p-tlmm reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sar2130p-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sar2130p-tlmm.yaml new file mode 100644 index 0000000000000..b388380b1ce02 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sar2130p-tlmm.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,sar2130p-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. SAR2130P TLMM block + +maintainers: + - Dmitry Baryshkov + +description: + Top Level Mode Multiplexer pin controller in Qualcomm SAR2130P SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,sar2130p-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 78 + + gpio-line-names: + maxItems: 156 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-sar2130p-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-sar2130p-tlmm-state" + additionalProperties: false + +$defs: + qcom-sar2130p-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-4][0-9]|15[0-5])$" + - enum: [ sdc1_clk, sdc1_cmd, sdc1_data, sdc1_rclk ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + enum: [ aoss_cti, atest_char, atest_char0, atest_char1, atest_char2, + atest_char3, atest_usb0, atest_usb00, atest_usb01, atest_usb02, + atest_usb03, audio_ref, cam_mclk, cci_async, cci_i2c, + cci_timer0, cci_timer1, cci_timer2, cci_timer3, cci_timer4, + cri_trng, cri_trng0, cri_trng1, dbg_out, ddr_bist, ddr_pxi0, + ddr_pxi1, ddr_pxi2, ddr_pxi3, dp0_hot, ext_mclk0, ext_mclk1, + gcc_gp1, gcc_gp2, gcc_gp3, gpio, host2wlan_sol, i2s0_data0, + i2s0_data1, i2s0_sck, i2s0_ws, ibi_i3c, jitter_bist, mdp_vsync, + mdp_vsync0, mdp_vsync1, mdp_vsync2, mdp_vsync3, pcie0_clkreqn, + pcie1_clkreqn, phase_flag0, phase_flag1, phase_flag10, + phase_flag11, phase_flag12, phase_flag13, phase_flag14, + phase_flag15, phase_flag16, phase_flag17, phase_flag18, + phase_flag19, phase_flag2, phase_flag20, phase_flag21, + phase_flag22, phase_flag23, phase_flag24, phase_flag25, + phase_flag26, phase_flag27, phase_flag28, phase_flag29, + phase_flag3, phase_flag30, phase_flag31, phase_flag4, + phase_flag5, phase_flag6, phase_flag7, phase_flag8, + phase_flag9, pll_bist, pll_clk, prng_rosc0, prng_rosc1, + prng_rosc2, prng_rosc3, qdss_cti, qdss_gpio, qdss_gpio0, + qdss_gpio1, qdss_gpio10, qdss_gpio11, qdss_gpio12, qdss_gpio13, + qdss_gpio14, qdss_gpio15, qdss_gpio2, qdss_gpio3, qdss_gpio4, + qdss_gpio5, qdss_gpio6, qdss_gpio7, qdss_gpio8, qdss_gpio9, + qspi0, qspi1, qspi2, qspi3, qspi_clk, qspi_cs0, qspi_cs1, qup0, + qup1, qup2, qup3, qup4, qup5, qup6, qup7, qup8, qup9, qup10, + qup11, tb_trig, tgu_ch0, tgu_ch1, tgu_ch2, tgu_ch3, + tmess_prng0, tmess_prng1, tmess_prng2, tmess_prng3, + tsense_pwm1, tsense_pwm2, usb0_phy, vsense_trigger ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + pinctrl@f100000 { + compatible = "qcom,sar2130p-tlmm"; + reg = <0x0f100000 0x300000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 156>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = ; + + gpio-wo-state { + pins = "gpio1"; + function = "gpio"; + }; + + uart-w-state { + rx-pins { + pins = "gpio26"; + function = "qup7"; + bias-pull-up; + }; + + tx-pins { + pins = "gpio27"; + function = "qup7"; + bias-disable; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml index db72143623018..e90a5274647df 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml @@ -16,7 +16,11 @@ description: properties: compatible: - const: qcom,sm8650-lpass-lpi-pinctrl + oneOf: + - const: qcom,sm8650-lpass-lpi-pinctrl + - items: + - const: qcom,sm8750-lpass-lpi-pinctrl + - const: qcom,sm8650-lpass-lpi-pinctrl reg: items: diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8750-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8750-tlmm.yaml new file mode 100644 index 0000000000000..7aecc97745a8c --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8750-tlmm.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,sm8750-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. SM8750 TLMM block + +maintainers: + - Melody Olvera + +description: + Top Level Mode Multiplexer pin controller in Qualcomm SM8750 SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,sm8750-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 108 + + gpio-line-names: + maxItems: 215 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-sm8750-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-sm8750-tlmm-state" + additionalProperties: false + +$defs: + qcom-sm8750-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-9][0-9]|20[0-9]|21[0-4])$" + - enum: [ ufs_reset, sdc2_clk, sdc2_cmd, sdc2_data ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + enum: [ gpio, aoss_cti, atest_char, atest_usb, audio_ext_mclk0, + audio_ext_mclk1, audio_ref_clk, cam_aon_mclk2, cam_aon_mclk4, + cam_mclk, cci_async_in, cci_i2c_scl, cci_i2c_sda, cci_timer, + cmu_rng, coex_uart1_rx, coex_uart1_tx, coex_uart2_rx, + coex_uart2_tx, dbg_out_clk, ddr_bist_complete, ddr_bist_fail, + ddr_bist_start, ddr_bist_stop, ddr_pxi0, ddr_pxi1, ddr_pxi2, + ddr_pxi3, dp_hot, egpio, gcc_gp1, gcc_gp2, gcc_gp3, gnss_adc0, + gnss_adc1, i2chub0_se0, i2chub0_se1, i2chub0_se2, i2chub0_se3, + i2chub0_se4, i2chub0_se5, i2chub0_se6, i2chub0_se7, i2chub0_se8, + i2chub0_se9, i2s0_data0, i2s0_data1, i2s0_sck, i2s0_ws, + i2s1_data0, i2s1_data1, i2s1_sck, i2s1_ws, ibi_i3c, jitter_bist, + mdp_esync0_out, mdp_esync1_out, mdp_vsync, mdp_vsync0_out, + mdp_vsync1_out, mdp_vsync2_out, mdp_vsync3_out, mdp_vsync5_out, + mdp_vsync_e, nav_gpio0, nav_gpio1, nav_gpio2, nav_gpio3, + pcie0_clk_req_n, phase_flag, pll_bist_sync, pll_clk_aux, + prng_rosc0, prng_rosc1, prng_rosc2, prng_rosc3, qdss_cti, + qlink_big_enable, qlink_big_request, qlink_little_enable, + qlink_little_request, qlink_wmss, qspi0, qspi1, qspi2, qspi3, + qspi_clk, qspi_cs, qup1_se0, qup1_se1, qup1_se2, qup1_se3, + qup1_se4, qup1_se5, qup1_se6, qup1_se7, qup2_se0, qup2_se1, + qup2_se2, qup2_se3, qup2_se4, qup2_se5, qup2_se6, qup2_se7, + sd_write_protect, sdc40, sdc41, sdc42, sdc43, sdc4_clk, + sdc4_cmd, tb_trig_sdc2, tb_trig_sdc4, tmess_prng0, tmess_prng1, + tmess_prng2, tmess_prng3, tsense_pwm1, tsense_pwm2, tsense_pwm3, + tsense_pwm4, uim0_clk, uim0_data, uim0_present, uim0_reset, + uim1_clk, uim1_data, uim1_present, uim1_reset, usb1_hs, usb_phy, + vfr_0, vfr_1, vsense_trigger_mirnat, wcn_sw, wcn_sw_ctrl ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + tlmm: pinctrl@f100000 { + compatible = "qcom,sm8750-tlmm"; + reg = <0x0f100000 0x300000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 216>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = ; + + gpio-wo-state { + pins = "gpio1"; + function = "gpio"; + }; + + uart-w-state { + rx-pins { + pins = "gpio60"; + function = "qup1_se7"; + bias-pull-up; + }; + + tx-pins { + pins = "gpio61"; + function = "qup1_se7"; + bias-disable; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml index 56d90c8e1fa3f..a1805b6e3f635 100644 --- a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml @@ -119,6 +119,10 @@ additionalProperties: bias-disable: true bias-pull-down: true bias-pull-up: true + input-schmitt-enable: true + input-schmitt-disable: true + drive-open-drain: true + drive-push-pull: true renesas,output-impedance: description: Output impedance for pins on the RZ/V2H(P) SoC. The value provided by this diff --git a/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl-wakeup-interrupt.yaml b/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl-wakeup-interrupt.yaml index 4dfb49b0e07f7..68ed714eb0a17 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl-wakeup-interrupt.yaml +++ b/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl-wakeup-interrupt.yaml @@ -42,10 +42,13 @@ properties: - samsung,exynos5433-wakeup-eint - samsung,exynos7885-wakeup-eint - samsung,exynos850-wakeup-eint + - samsung,exynos8895-wakeup-eint - const: samsung,exynos7-wakeup-eint - items: - enum: - google,gs101-wakeup-eint + - samsung,exynos9810-wakeup-eint + - samsung,exynos990-wakeup-eint - samsung,exynosautov9-wakeup-eint - const: samsung,exynos850-wakeup-eint - const: samsung,exynos7-wakeup-eint @@ -91,14 +94,18 @@ allOf: - if: properties: compatible: - # Match without "contains", to skip newer variants which are still - # compatible with samsung,exynos7-wakeup-eint - enum: - - samsung,s5pv210-wakeup-eint - - samsung,exynos4210-wakeup-eint - - samsung,exynos5433-wakeup-eint - - samsung,exynos7-wakeup-eint - - samsung,exynos7885-wakeup-eint + oneOf: + # Match without "contains", to skip newer variants which are still + # compatible with samsung,exynos7-wakeup-eint + - enum: + - samsung,exynos4210-wakeup-eint + - samsung,exynos7-wakeup-eint + - samsung,s5pv210-wakeup-eint + - contains: + enum: + - samsung,exynos5433-wakeup-eint + - samsung,exynos7885-wakeup-eint + - samsung,exynos8895-wakeup-eint then: properties: interrupts: diff --git a/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl.yaml index 242dd13c276b5..5296a9e4faaec 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/samsung,pinctrl.yaml @@ -53,6 +53,9 @@ properties: - samsung,exynos7-pinctrl - samsung,exynos7885-pinctrl - samsung,exynos850-pinctrl + - samsung,exynos8895-pinctrl + - samsung,exynos9810-pinctrl + - samsung,exynos990-pinctrl - samsung,exynosautov9-pinctrl - samsung,exynosautov920-pinctrl - tesla,fsd-pinctrl diff --git a/Documentation/devicetree/bindings/pinctrl/semtech,sx1501q.yaml b/Documentation/devicetree/bindings/pinctrl/semtech,sx1501q.yaml index 4214d7311f6bd..39d7dad3313b6 100644 --- a/Documentation/devicetree/bindings/pinctrl/semtech,sx1501q.yaml +++ b/Documentation/devicetree/bindings/pinctrl/semtech,sx1501q.yaml @@ -26,6 +26,10 @@ properties: reg: maxItems: 1 + gpio-line-names: + minItems: 5 + maxItems: 17 + interrupts: maxItems: 1 @@ -87,6 +91,45 @@ required: allOf: - $ref: pinctrl.yaml# + - if: + properties: + compatible: + contains: + enum: + - semtech,sx1501q + - semtech,sx1504q + - semtech,sx1507q + then: + properties: + gpio-line-names: + minItems: 5 + maxItems: 5 + - if: + properties: + compatible: + contains: + enum: + - semtech,sx1502q + - semtech,sx1505q + - semtech,sx1508q + then: + properties: + gpio-line-names: + minItems: 9 + maxItems: 9 + - if: + properties: + compatible: + contains: + enum: + - semtech,sx1503q + - semtech,sx1506q + - semtech,sx1509q + then: + properties: + gpio-line-names: + minItems: 17 + maxItems: 17 - if: not: properties: diff --git a/Documentation/devicetree/bindings/pinctrl/sophgo,cv1800-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/sophgo,cv1800-pinctrl.yaml index 1e6a55afe26a7..feb4785a3fac5 100644 --- a/Documentation/devicetree/bindings/pinctrl/sophgo,cv1800-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/sophgo,cv1800-pinctrl.yaml @@ -58,7 +58,7 @@ patternProperties: pinmux: description: | The list of GPIOs and their mux settings that properties in the - node apply to. This should be set using the GPIOMUX or GPIOMUX2 + node apply to. This should be set using the PINMUX or PINMUX2 macro. bias-pull-up: diff --git a/Documentation/devicetree/bindings/pinctrl/spacemit,k1-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/spacemit,k1-pinctrl.yaml new file mode 100644 index 0000000000000..b01ecd83b71b5 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/spacemit,k1-pinctrl.yaml @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/spacemit,k1-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 SoC Pin Controller + +maintainers: + - Yixun Lan + +properties: + compatible: + const: spacemit,k1-pinctrl + + reg: + items: + - description: pinctrl io memory base + +patternProperties: + '-cfg$': + type: object + additionalProperties: false + + description: + A pinctrl node should contain at least one subnode representing the + pinctrl groups available on the machine. + + patternProperties: + '-pins$': + type: object + additionalProperties: false + + description: + Each subnode will list the pins it needs, and how they should + be configured, with regard to muxer configuration, bias pull, + drive strength, input schmitt trigger, slew rate, power source. + + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + properties: + pinmux: + description: + The list of GPIOs and their mux settings that properties in the + node apply to. This should be set using the K1_PADCONF macro to + construct the value. + + bias-disable: true + + bias-pull-down: true + + bias-pull-up: + description: | + typical value for selecting bias pull up or strong pull up. + 0: normal bias pull up + 1: strong bias pull up + enum: [ 0, 1 ] + + drive-strength: + description: | + typical current when output high level. + 1.8V output: 11, 21, 32, 42 (mA) + 3.3V output: 7, 10, 13, 16, 19, 23, 26, 29 (mA) + + input-schmitt: + description: | + typical threshold for schmitt trigger. + 0: buffer mode + 1: trigger mode + 2, 3: trigger mode + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3 ] + + power-source: + description: external power supplies at 1.8v or 3.3v. + enum: [ 1800, 3300 ] + + slew-rate: + description: | + slew rate for output buffer. + 0: disable it + 1: enable it (via bundled value from drive strength) + 2: slow speed 0 + 3: slow speed 1 + 4: medium speed + 5: fast speed + enum: [ 0, 1, 2, 3, 4, 5 ] + + required: + - pinmux + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #define K1_PADCONF(pin, func) (((pin) << 16) | (func)) + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pinctrl@d401e000 { + compatible = "spacemit,k1-pinctrl"; + reg = <0x0 0xd401e000 0x0 0x400>; + + uart0_2_cfg: uart0-2-cfg { + uart0-2-pins { + pinmux = , + ; + + bias-pull-up = <0>; + drive-strength = <32>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml new file mode 100644 index 0000000000000..21f86740a63d4 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml @@ -0,0 +1,176 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/thead,th1520-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-Head TH1520 SoC pin controller + +maintainers: + - Emil Renner Berthing + +description: | + Pinmux and pinconf controller in the T-Head TH1520 RISC-V SoC. + + The TH1520 has 3 groups of pads each controlled from different memory ranges. + Confusingly the memory ranges are named + PADCTRL_AOSYS -> PAD Group 1 + PADCTRL1_APSYS -> PAD Group 2 + PADCTRL0_APSYS -> PAD Group 3 + + Each pad can be muxed individually to up to 6 different functions. For most + pads only a few of those 6 configurations are valid though, and a few pads in + group 1 does not support muxing at all. + + Pinconf is fairly regular except for a few pads in group 1 that either can't + be configured or has some special functions. The rest have configurable drive + strength, input enable, schmitt trigger, slew rate, pull-up and pull-down in + addition to a special strong pull up. + + Certain pads in group 1 can be muxed to AUDIO_PA0 - AUDIO_PA30 functions and + are then meant to be used by the audio co-processor. Each such pad can then + be further muxed to either audio GPIO or one of 4 functions such as UART, I2C + and I2S. If the audio pad is muxed to one of the 4 functions then pinconf is + also configured in different registers. All of this is done from a different + AUDIO_IOCTRL memory range and is left to the audio co-processor for now. + +properties: + compatible: + enum: + - thead,th1520-pinctrl + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + thead,pad-group: + description: | + Select the pad group that is associated with the pin controller instance. + + Base Address Name Group + 0xFF_FFF4_A000 PADCTRL_AOSYS 1 + 0xFF_E7F3_C000 PADCTRL1_APSYS 2 + 0xFF_EC00_7000 PADCTRL0_APSYS 3 + + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 3] + +required: + - compatible + - reg + - clocks + +patternProperties: + '-[0-9]+$': + type: object + additionalProperties: false + + patternProperties: + '-pins$': + type: object + allOf: + - $ref: /schemas/pinctrl/pincfg-node.yaml# + - $ref: /schemas/pinctrl/pinmux-node.yaml# + + additionalProperties: false + + description: + A pinctrl node should contain at least one subnode describing one + or more pads and their associated pinmux and pinconf settings. + + properties: + pins: + description: List of pads that properties in the node apply to. + + function: + enum: [ gpio, pwm, uart, ir, i2c, spi, qspi, sdio, audio, i2s, + gmac0, gmac1, dpu0, dpu1, isp, hdmi, bootsel, debug, + clock, jtag, iso7816, efuse, reset ] + description: The mux function to select for the given pins. + + bias-disable: true + + bias-pull-up: + oneOf: + - type: boolean + description: Enable the regular 48kOhm pull-up + - enum: [ 2100, 48000 ] + description: Enable the strong 2.1kOhm pull-up or regular 48kOhm pull-up + + bias-pull-down: + oneOf: + - type: boolean + - const: 44000 + description: Enable the regular 44kOhm pull-down + + drive-strength: + enum: [ 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 16, 18, 20, 21, 23, 25 ] + description: Drive strength in mA + + input-enable: true + + input-disable: true + + input-schmitt-enable: true + + input-schmitt-disable: true + + slew-rate: + maximum: 1 + + required: + - pins + +additionalProperties: false + +examples: + - | + padctrl0_apsys: pinctrl@ec007000 { + compatible = "thead,th1520-pinctrl"; + reg = <0xec007000 0x1000>; + clocks = <&apb_clk>; + thead,pad-group = <3>; + + uart0_pins: uart0-0 { + tx-pins { + pins = "UART0_TXD"; + function = "uart"; + bias-disable; + drive-strength = <3>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pins = "UART0_RXD"; + function = "uart"; + bias-disable; + drive-strength = <1>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + }; + + padctrl1_apsys: pinctrl@e7f3c000 { + compatible = "thead,th1520-pinctrl"; + reg = <0xe7f3c000 0x1000>; + clocks = <&apb_clk>; + thead,pad-group = <2>; + + i2c5_pins: i2c5-0 { + i2c-pins { + pins = "QSPI1_CSN0", /* I2C5_SCL */ + "QSPI1_D0_MOSI"; /* I2C5_SDA */ + function = "i2c"; + bias-pull-up = <2100>; + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml new file mode 100644 index 0000000000000..55ece6a8be5ef --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml @@ -0,0 +1,278 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/xlnx,versal-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx Versal Pinctrl + +maintainers: + - Sai Krishna Potthuri + +description: | + Please refer to pinctrl-bindings.txt in this directory for details of the + common pinctrl bindings used by client devices, including the meaning of the + phrase "pin configuration node". + + Versal's pin configuration nodes act as a container for an arbitrary number of + subnodes. Each of these subnodes represents some desired configuration for a + pin, a group, or a list of pins or groups. This configuration can include the + mux function to select on those pin(s)/group(s), and various pin configuration + parameters, such as pull-up, slew rate, etc. + + Each configuration node can consist of multiple nodes describing the pinmux and + pinconf options. Those nodes can be pinmux nodes or pinconf nodes. + +properties: + compatible: + const: xlnx,versal-pinctrl + +patternProperties: + '^(.*-)?(default|gpio-grp)$': + type: object + patternProperties: + '^mux': + type: object + description: + Pinctrl node's client devices use subnodes for pin muxes, + which in turn use below standard properties. + $ref: pinmux-node.yaml# + + properties: + pins: + description: + List of pins to select (either this or "groups" must be specified) + $ref: "#/$defs/pins/properties/pins" + + groups: + description: + List of groups to select (either this or "pins" must be + specified), available groups for this subnode. + anyOf: + - pattern: '^((LPD|PMC)_)MIO([0-9]|[1-6][0-9]|7[0-7])$' + - $ref: "#/$defs/pins/properties/groups" + + function: + description: + Specify the alternative function to be configured for the + given pin groups. + enum: [spi0, spi0_ss, spi1, spi1_ss, can0, can1, i2c0, i2c1, i2c_pmc, ttc0_clk, + ttc0_wav, ttc1_clk, ttc1_wav, ttc2_clk, ttc2_wav, ttc3_clk, ttc3_wav, wwdt0, + wwdt1, sysmon_i2c0, sysmon_i2c0_alrt, uart0, uart0_ctrl, uart1, uart1_ctrl, + gpio0, gpio1, gpio2, emio0, gem0, gem1, trace0, trace0_clk, mdio0, mdio1, gem_tsu0, + pcie0, smap0, usb0, sd0, sd0_pc, sd0_cd, sd0_wp, sd1, sd1_pc, sd1_wp, sd1_cd, + ospi0, ospi0_ss, qspi0, qspi0_fbclk, qspi0_ss, test_clk, test_scan, tamper_trigger] + + required: + - function + + oneOf: + - required: [ groups ] + - required: [ pins ] + + additionalProperties: false + + '^conf': + type: object + description: + Pinctrl node's client devices use subnodes for pin configurations, + which in turn use the standard properties below. + allOf: + - $ref: pincfg-node.yaml# + - $ref: "#/$defs/pins" + + additionalProperties: false + +allOf: + - $ref: pinctrl.yaml# + +required: + - compatible + +additionalProperties: false + +$defs: + pins: + properties: + groups: + description: + List of pin groups to select in this subnode. + items: + enum: [spi0_0_grp, spi0_1_grp, spi0_2_grp, spi0_3_grp, spi0_4_grp, spi0_5_grp, + spi0_ss_0_grp, spi0_ss_1_grp, spi0_ss_2_grp, spi0_ss_3_grp, spi0_ss_4_grp, + spi0_ss_5_grp, spi0_ss_6_grp, spi0_ss_7_grp, spi0_ss_8_grp, spi0_ss_9_grp, + spi0_ss_10_grp, spi0_ss_11_grp, spi0_ss_12_grp, spi0_ss_13_grp, spi0_ss_14_grp, + spi0_ss_15_grp, spi0_ss_16_grp, spi0_ss_17_grp, spi1_0_grp, spi1_1_grp, + spi1_2_grp, spi1_3_grp, spi1_4_grp, spi1_5_grp, spi1_ss_0_grp, spi1_ss_1_grp, + spi1_ss_2_grp, spi1_ss_3_grp, spi1_ss_4_grp, spi1_ss_5_grp, spi1_ss_6_grp, + spi1_ss_7_grp, spi1_ss_8_grp, spi1_ss_9_grp, spi1_ss_10_grp, spi1_ss_11_grp, + spi1_ss_12_grp, spi1_ss_13_grp, spi1_ss_14_grp, spi1_ss_15_grp, spi1_ss_16_grp + spi1_ss_17_grp, can0_0_grp, can0_1_grp, can0_2_grp, can0_3_grp, can0_4_grp, + can0_5_grp, can0_6_grp, can0_7_grp, can0_8_grp, can0_9_grp, can0_10_grp, + can0_11_grp, can0_12_grp, can0_13_grp, can0_14_grp, can0_15_grp, can0_16_grp, + can0_17_grp, can1_0_grp, can1_1_grp, can1_2_grp, can1_3_grp, can1_4_grp, + can1_5_grp, can1_6_grp, can1_7_grp, can1_8_grp, can1_9_grp, can1_10_grp, + can1_11_grp, can1_12_grp, can1_13_grp, can1_14_grp, can1_15_grp, can1_16_grp, + can1_17_grp, can1_18_grp, i2c0_0_grp, i2c0_1_grp, i2c0_2_grp, i2c0_3_grp, + i2c0_4_grp, i2c0_5_grp, i2c0_6_grp, i2c0_7_grp, i2c0_8_grp, i2c0_9_grp, + i2c0_10_grp, i2c0_11_grp, i2c0_12_grp, i2c0_13_grp, i2c0_14_grp, i2c0_15_grp, + i2c0_16_grp, i2c0_17_grp, i2c1_0_grp, i2c1_1_grp, i2c1_2_grp, i2c1_3_grp, + i2c1_4_grp, i2c1_5_grp, i2c1_6_grp, i2c1_7_grp, i2c1_8_grp, i2c1_9_grp, + i2c1_10_grp, i2c1_11_grp, i2c1_12_grp, i2c1_13_grp, i2c1_14_grp, i2c1_15_grp, + i2c1_16_grp, i2c1_17_grp, i2c1_18_grp, i2c_pmc_0_grp, i2c_pmc_1_grp, + i2c_pmc_2_grp, i2c_pmc_3_grp, i2c_pmc_4_grp, i2c_pmc_5_grp, i2c_pmc_6_grp, + i2c_pmc_7_grp, i2c_pmc_8_grp, i2c_pmc_9_grp, i2c_pmc_10_grp, i2c_pmc_11_grp, + i2c_pmc_12_grp, ttc0_clk_0_grp, ttc0_clk_1_grp, ttc0_clk_2_grp, ttc0_clk_3_grp, + ttc0_clk_4_grp, ttc0_clk_5_grp, ttc0_clk_6_grp, ttc0_clk_7_grp, ttc0_clk_8_grp, + ttc0_wav_0_grp, ttc0_wav_1_grp, ttc0_wav_2_grp, ttc0_wav_3_grp, ttc0_wav_4_grp, + ttc0_wav_5_grp, ttc0_wav_6_grp, ttc0_wav_7_grp, ttc0_wav_8_grp, ttc1_clk_0_grp, + ttc1_clk_1_grp, ttc1_clk_2_grp, ttc1_clk_3_grp, ttc1_clk_4_grp, ttc1_clk_5_grp, + ttc1_clk_6_grp, ttc1_clk_7_grp, ttc1_clk_8_grp, ttc1_wav_0_grp, ttc1_wav_1_grp, + ttc1_wav_2_grp, ttc1_wav_3_grp, ttc1_wav_4_grp, ttc1_wav_5_grp, ttc1_wav_6_grp, + ttc1_wav_7_grp, ttc1_wav_8_grp, ttc2_clk_0_grp, ttc2_clk_1_grp, ttc2_clk_2_grp, + ttc2_clk_3_grp, ttc2_clk_4_grp, ttc2_clk_5_grp, ttc2_clk_6_grp, ttc2_clk_7_grp, + ttc2_clk_8_grp, ttc2_wav_0_grp, ttc2_wav_1_grp, ttc2_wav_2_grp, ttc2_wav_3_grp, + ttc2_wav_4_grp, ttc2_wav_5_grp, ttc2_wav_6_grp, ttc2_wav_7_grp, ttc2_wav_8_grp, + ttc3_clk_0_grp, ttc3_clk_1_grp, ttc3_clk_2_grp, ttc3_clk_3_grp, ttc3_clk_4_grp, + ttc3_clk_5_grp, ttc3_clk_6_grp, ttc3_clk_7_grp, ttc3_clk_8_grp, ttc3_wav_0_grp, + ttc3_wav_1_grp, ttc3_wav_2_grp, ttc3_wav_3_grp, ttc3_wav_4_grp, ttc3_wav_5_grp, + ttc3_wav_6_grp, ttc3_wav_7_grp, ttc3_wav_8_grp, wwdt0_0_grp, wwdt0_1_grp, + wwdt0_2_grp, wwdt0_3_grp, wwdt0_4_grp, wwdt0_5_grp, wwdt1_0_grp, wwdt1_1_grp, + wwdt1_2_grp, wwdt1_3_grp, wwdt1_4_grp, wwdt1_5_grp, sysmon_i2c0_0_grp, + sysmon_i2c0_1_grp, sysmon_i2c0_2_grp, sysmon_i2c0_3_grp, sysmon_i2c0_4_grp, + sysmon_i2c0_5_grp, sysmon_i2c0_6_grp, sysmon_i2c0_7_grp, sysmon_i2c0_8_grp, + sysmon_i2c0_9_grp, sysmon_i2c0_10_grp, sysmon_i2c0_11_grp, sysmon_i2c0_12_grp, + sysmon_i2c0_13_grp, sysmon_i2c0_14_grp, sysmon_i2c0_15_grp, + sysmon_i2c0_16_grp, sysmon_i2c0_17_grp, sysmon_i2c0_alrt_0_grp, + sysmon_i2c0_alrt_1_grp, sysmon_i2c0_alrt_2_grp, sysmon_i2c0_alrt_3_grp, + sysmon_i2c0_alrt_4_grp, sysmon_i2c0_alrt_5_grp, sysmon_i2c0_alrt_6_grp, + sysmon_i2c0_alrt_7_grp, sysmon_i2c0_alrt_8_grp, sysmon_i2c0_alrt_9_grp, + sysmon_i2c0_alrt_10_grp, sysmon_i2c0_alrt_11_grp, sysmon_i2c0_alrt_12_grp, + sysmon_i2c0_alrt_13_grp, sysmon_i2c0_alrt_14_grp, sysmon_i2c0_alrt_15_grp, + sysmon_i2c0_alrt_16_grp, sysmon_i2c0_alrt_17_grp, uart0_0_grp, uart0_1_grp, + uart0_2_grp, uart0_3_grp, uart0_4_grp, uart0_5_grp, uart0_6_grp, uart0_7_grp, + uart0_8_grp, uart0_ctrl_0_grp, uart0_ctrl_1_grp, uart0_ctrl_2_grp, + uart0_ctrl_3_grp, uart0_ctrl_4_grp, uart0_ctrl_5_grp, uart0_ctrl_6_grp, + uart0_ctrl_7_grp, uart0_ctrl_8_grp, uart1_0_grp, uart1_1_grp, uart1_2_grp, + uart1_3_grp, uart1_4_grp, uart1_5_grp, uart1_6_grp, uart1_7_grp, uart1_8_grp, + uart1_ctrl_0_grp, uart1_ctrl_1_grp, uart1_ctrl_2_grp, uart1_ctrl_3_grp, + uart1_ctrl_4_grp, uart1_ctrl_5_grp, uart1_ctrl_6_grp, uart1_ctrl_7_grp, + uart1_ctrl_8_grp, gpio0_0_grp, gpio0_1_grp, gpio0_2_grp, gpio0_3_grp, + gpio0_4_grp, gpio0_5_grp, gpio0_6_grp, gpio0_7_grp, gpio0_8_grp, gpio0_9_grp, + gpio0_10_grp, gpio0_11_grp, gpio0_12_grp, gpio0_13_grp, gpio0_14_grp, + gpio0_15_grp, gpio0_16_grp, gpio0_17_grp, gpio0_18_grp, gpio0_19_grp, + gpio0_20_grp, gpio0_21_grp, gpio0_22_grp, gpio0_23_grp, gpio0_24_grp, + gpio0_25_grp, gpio1_0_grp, gpio1_1_grp, gpio1_2_grp, gpio1_3_grp, gpio1_4_grp, + gpio1_5_grp, gpio1_6_grp, gpio1_7_grp, gpio1_8_grp, gpio1_9_grp, + gpio1_10_grp, gpio1_11_grp, gpio1_12_grp, gpio1_13_grp, gpio1_14_grp, + gpio1_15_grp, gpio1_16_grp, gpio1_17_grp, gpio1_18_grp, gpio1_19_grp, + gpio1_20_grp, gpio1_21_grp, gpio1_22_grp, gpio1_23_grp, gpio1_24_grp, + gpio1_25_grp, gpio2_0_grp, gpio2_1_grp, gpio2_2_grp, gpio2_3_grp, gpio2_4_grp, + gpio2_5_grp, gpio2_6_grp, gpio2_7_grp, gpio2_8_grp, gpio2_9_grp, gpio2_10_grp, + gpio2_11_grp, gpio2_12_grp, gpio2_13_grp, gpio2_14_grp, gpio2_15_grp, + gpio2_16_grp, gpio2_17_grp, gpio2_18_grp, gpio2_19_grp, gpio2_20_grp, + gpio2_21_grp, gpio2_22_grp, gpio2_23_grp, gpio2_24_grp, gpio2_25_grp, + emio0_0_grp, emio0_1_grp, emio0_2_grp, emio0_3_grp, emio0_4_grp, emio0_5_grp, + emio0_6_grp, emio0_7_grp, emio0_8_grp, emio0_9_grp, emio0_10_grp, + emio0_11_grp, emio0_12_grp, emio0_13_grp, emio0_14_grp, emio0_15_grp, + emio0_16_grp, emio0_17_grp, emio0_18_grp, emio0_19_grp, emio0_20_grp, + emio0_21_grp, emio0_22_grp, emio0_23_grp, emio0_24_grp, emio0_25_grp, + emio0_26_grp, emio0_27_grp, emio0_28_grp, emio0_29_grp, emio0_30_grp, + emio0_31_grp, emio0_32_grp, emio0_33_grp, emio0_34_grp, emio0_35_grp, + emio0_36_grp, emio0_37_grp, emio0_38_grp, emio0_39_grp, emio0_40_grp, + emio0_41_grp, emio0_42_grp, emio0_43_grp, emio0_44_grp, emio0_45_grp, + emio0_46_grp, emio0_47_grp, emio0_48_grp, emio0_49_grp, emio0_50_grp, + emio0_51_grp, emio0_52_grp, emio0_53_grp, emio0_54_grp, emio0_55_grp, + emio0_56_grp, emio0_57_grp, emio0_58_grp, emio0_59_grp, emio0_60_grp, + emio0_61_grp, emio0_62_grp, emio0_63_grp, emio0_64_grp, emio0_65_grp, + emio0_66_grp, emio0_67_grp, emio0_68_grp, emio0_69_grp, emio0_70_grp, + emio0_71_grp, emio0_72_grp, emio0_73_grp, emio0_74_grp, emio0_75_grp, + emio0_76_grp, emio0_77_grp, gem0_0_grp, gem0_1_grp, gem1_0_grp, gem1_1_grp, + trace0_0_grp, trace0_1_grp, trace0_2_grp, trace0_clk_0_grp, trace0_clk_1_grp, + trace0_clk_2_grp, mdio0_0_grp, mdio0_1_grp, mdio1_0_grp, mdio1_1_grp, + gem_tsu0_0_grp, gem_tsu0_1_grp, gem_tsu0_2_grp, gem_tsu0_3_grp, pcie0_0_grp, + pcie0_1_grp, pcie0_2_grp, smap0_0_grp, usb0_0_grp, sd0_0_grp, sd0_1_grp, + sd0_2_grp, sd0_3_grp, sd0_4_grp, sd0_5_grp, sd0_6_grp, sd0_7_grp, sd0_8_grp, + sd0_9_grp, sd0_10_grp, sd0_11_grp, sd0_12_grp, sd0_13_grp, sd0_14_grp, + sd0_15_grp, sd0_16_grp, sd0_17_grp, sd0_18_grp, sd0_19_grp, sd0_20_grp, + sd0_21_grp, sd0_pc_0_grp, sd0_pc_1_grp, sd0_cd_0_grp, sd0_cd_1_grp, + sd0_wp_0_grp, sd0_wp_1_grp, sd1_0_grp, sd1_1_grp, sd1_2_grp, sd1_3_grp, + sd1_4_grp, sd1_5_grp, sd1_6_grp, sd1_7_grp, sd1_8_grp, sd1_9_grp, sd1_10_grp, + sd1_11_grp, sd1_12_grp, sd1_13_grp, sd1_14_grp, sd1_15_grp, sd1_16_grp, + sd1_17_grp, sd1_18_grp, sd1_19_grp, sd1_20_grp, sd1_21_grp, sd1_pc_0_grp, + sd1_pc_1_grp, sd1_cd_0_grp, sd1_cd_1_grp, sd1_wp_0_grp, sd1_wp_1_grp, + ospi0_0_grp, ospi0_ss_0_grp, qspi0_0_grp, qspi0_fbclk_0_grp, qspi0_ss_0_grp, + test_clk_0_grp, test_scan_0_grp, tamper_trigger_0_grp] + minItems: 1 + maxItems: 78 + + pins: + description: + List of pin names to select in this subnode. + items: + pattern: '^((LPD|PMC)_)MIO([0-9]|[1-6][0-9]|7[0-7])$' + minItems: 1 + maxItems: 78 + + bias-pull-up: true + bias-pull-down: true + bias-disable: true + input-schmitt-enable: true + input-schmitt-disable: true + bias-high-impedance: true + low-power-enable: true + low-power-disable: true + + slew-rate: + enum: [0, 1] + + output-enable: + description: + This will internally disable the tri-state for MIO pins. + + drive-strength: + description: + Selects the drive strength for MIO pins, in mA. + enum: [2, 4, 8, 12] + + power-source: + enum: [0, 1] + + oneOf: + - required: [ groups ] + - required: [ pins ] + + additionalProperties: false + +examples: + - | + #include + pinctrl { + compatible = "xlnx,versal-pinctrl"; + + uart0-default { + mux { + groups = "uart0_4_grp", "uart0_5_grp"; + function = "uart0"; + }; + + conf { + groups = "uart0_4_grp"; + slew-rate = ; + power-source = ; + }; + + conf-rx { + pins = "PMC_MIO42"; + bias-pull-up; + }; + + conf-tx { + pins = "PMC_MIO43"; + bias-disable; + input-schmitt-disable; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/power/reset/nvmem-reboot-mode.yaml b/Documentation/devicetree/bindings/power/reset/nvmem-reboot-mode.yaml index 627f8a6078c29..7f5f94673e9c2 100644 --- a/Documentation/devicetree/bindings/power/reset/nvmem-reboot-mode.yaml +++ b/Documentation/devicetree/bindings/power/reset/nvmem-reboot-mode.yaml @@ -31,6 +31,10 @@ properties: allOf: - $ref: reboot-mode.yaml# +patternProperties: + "^mode-.*$": + maxItems: 1 + required: - compatible - nvmem-cells diff --git a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml index fc8105a7b9b26..3da3d02a66908 100644 --- a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml +++ b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml @@ -54,6 +54,10 @@ required: - compatible - reg +patternProperties: + "^mode-.*$": + maxItems: 1 + unevaluatedProperties: false allOf: @@ -75,6 +79,9 @@ allOf: reg-names: items: - const: pon + else: + patternProperties: + "^mode-.*$": false # Special case for pm8941, which doesn't store reset mode - if: diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.yaml b/Documentation/devicetree/bindings/power/reset/reboot-mode.yaml index ad0a0b95cec12..3ddac06cec727 100644 --- a/Documentation/devicetree/bindings/power/reset/reboot-mode.yaml +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.yaml @@ -28,13 +28,13 @@ description: | properties: mode-normal: - $ref: /schemas/types.yaml#/definitions/uint32 + $ref: /schemas/types.yaml#/definitions/uint32-array description: Default value to set on a reboot if no command was provided. patternProperties: "^mode-.*$": - $ref: /schemas/types.yaml#/definitions/uint32 + $ref: /schemas/types.yaml#/definitions/uint32-array additionalProperties: true diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.yaml b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.yaml index b6acff199cdec..79ffc78b23eaf 100644 --- a/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.yaml +++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.yaml @@ -32,6 +32,10 @@ properties: allOf: - $ref: reboot-mode.yaml# +patternProperties: + "^mode-.*$": + maxItems: 1 + unevaluatedProperties: false required: diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml b/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml index 75061124d9a8b..19d3093e6cd2f 100644 --- a/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml +++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml @@ -31,6 +31,10 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: Offset in the register map for the reboot register (in bytes). + reg: + maxItems: 1 + description: Base address and size for the reboot register. + regmap: $ref: /schemas/types.yaml#/definitions/phandle deprecated: true @@ -45,9 +49,14 @@ properties: priority: default: 192 +oneOf: + - required: + - offset + - required: + - reg + required: - compatible - - offset additionalProperties: false diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml index 277c47e048b65..a0f9d49ff8fb6 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml @@ -60,7 +60,6 @@ examples: pmic { #address-cells = <1>; #size-cells = <0>; - #interrupt-cells = <4>; charger@1000 { compatible = "qcom,pmi8998-charger"; diff --git a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml index 9495397c92699..c1de2c80291df 100644 --- a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml +++ b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml @@ -27,6 +27,11 @@ properties: battery-detect-gpios: maxItems: 1 + bat-detect-gpio: + maxItems: 1 + deprecated: true + description: use battery-detect-gpios instead + interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/power/supply/ti,twl6030-charger.yaml b/Documentation/devicetree/bindings/power/supply/ti,twl6030-charger.yaml new file mode 100644 index 0000000000000..fc604d8a469df --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/ti,twl6030-charger.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/ti,twl6030-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TWL6030/32 BCI (Battery Charger Interface) + +description: + The battery charger needs to be configured to do any charging besides of + precharging. The GPADC in the PMIC has to be used to get the related + voltages. + +maintainers: + - Andreas Kemnade + +allOf: + - $ref: power-supply.yaml# + +properties: + compatible: + oneOf: + - const: ti,twl6030-charger + - items: + - const: ti,twl6032-charger + - const: ti,twl6030-charger + + interrupts: + items: + - description: Charger Control Interrupt + - description: Charger Fault Interrupt + + io-channels: + items: + - description: VBUS Voltage Channel + + io-channel-names: + items: + - const: vusb + + monitored-battery: true + +required: + - compatible + - interrupts + - monitored-battery + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/pwm/airoha,en7581-pwm.yaml b/Documentation/devicetree/bindings/pwm/airoha,en7581-pwm.yaml new file mode 100644 index 0000000000000..f36387572a978 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/airoha,en7581-pwm.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/airoha,en7581-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN7581 PWM Controller + +maintainers: + - Lorenzo Bianconi + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: airoha,en7581-pwm + + "#pwm-cells": + const: 3 + +required: + - compatible + - "#pwm-cells" + +additionalProperties: false + +examples: + - | + pwm { + compatible = "airoha,en7581-pwm"; + + #pwm-cells = <3>; + }; diff --git a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml index e987c39b223e7..83965076d6ab1 100644 --- a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml @@ -16,7 +16,12 @@ description: properties: compatible: - const: qcom,pmi8998-lab-ibb + oneOf: + - const: qcom,pmi8998-lab-ibb + - items: + - enum: + - qcom,pmi8950-lab-ibb + - const: qcom,pmi8998-lab-ibb lab: type: object diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml index 4b9fb74fb9e96..fd3423e6051bc 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml @@ -16,6 +16,7 @@ description: properties: compatible: enum: + - qcom,sar2130p-adsp-pas - qcom,sm8350-adsp-pas - qcom,sm8350-cdsp-pas - qcom,sm8350-slpi-pas @@ -61,6 +62,7 @@ allOf: properties: compatible: enum: + - qcom,sar2130p-adsp-pas - qcom,sm8350-adsp-pas - qcom,sm8350-cdsp-pas - qcom,sm8350-slpi-pas @@ -101,6 +103,7 @@ allOf: properties: compatible: enum: + - qcom,sar2130p-adsp-pas - qcom,sm8350-adsp-pas - qcom,sm8350-slpi-pas - qcom,sm8450-adsp-pas diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml index d7fad7b3c2c68..a24cbb61bda7d 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml @@ -15,16 +15,20 @@ description: properties: compatible: - enum: - - qcom,sdx75-mpss-pas - - qcom,sm8550-adsp-pas - - qcom,sm8550-cdsp-pas - - qcom,sm8550-mpss-pas - - qcom,sm8650-adsp-pas - - qcom,sm8650-cdsp-pas - - qcom,sm8650-mpss-pas - - qcom,x1e80100-adsp-pas - - qcom,x1e80100-cdsp-pas + oneOf: + - enum: + - qcom,sdx75-mpss-pas + - qcom,sm8550-adsp-pas + - qcom,sm8550-cdsp-pas + - qcom,sm8550-mpss-pas + - qcom,sm8650-adsp-pas + - qcom,sm8650-cdsp-pas + - qcom,sm8650-mpss-pas + - qcom,x1e80100-adsp-pas + - qcom,x1e80100-cdsp-pas + - items: + - const: qcom,sm8750-adsp-pas + - const: qcom,sm8550-adsp-pas reg: maxItems: 1 @@ -82,6 +86,20 @@ allOf: maxItems: 5 memory-region: maxItems: 2 + - if: + properties: + compatible: + contains: + enum: + - qcom,sm8750-adsp-pas + then: + properties: + interrupts: + maxItems: 6 + interrupt-names: + maxItems: 6 + memory-region: + maxItems: 2 - if: properties: compatible: @@ -129,10 +147,12 @@ allOf: - if: properties: compatible: - enum: - - qcom,sm8550-adsp-pas - - qcom,sm8650-adsp-pas - - qcom,x1e80100-adsp-pas + contains: + enum: + - qcom,sm8550-adsp-pas + - qcom,sm8650-adsp-pas + - qcom,sm8750-adsp-pas + - qcom,x1e80100-adsp-pas then: properties: power-domains: diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml index 2cf2026cff574..9c7dd7e75e0ca 100644 --- a/Documentation/devicetree/bindings/riscv/extensions.yaml +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml @@ -128,6 +128,18 @@ properties: changes to interrupts as frozen at commit ccbddab ("Merge pull request #42 from riscv/jhauser-2023-RC4") of riscv-aia. + - const: smmpm + description: | + The standard Smmpm extension for M-mode pointer masking as + ratified at commit d70011dde6c2 ("Update to ratified state") + of riscv-j-extension. + + - const: smnpm + description: | + The standard Smnpm extension for next-mode pointer masking as + ratified at commit d70011dde6c2 ("Update to ratified state") + of riscv-j-extension. + - const: smstateen description: | The standard Smstateen extension for controlling access to CSRs @@ -147,12 +159,46 @@ properties: and mode-based filtering as ratified at commit 01d1df0 ("Add ability to manually trigger workflow. (#2)") of riscv-count-overflow. + - const: ssnpm + description: | + The standard Ssnpm extension for next-mode pointer masking as + ratified at commit d70011dde6c2 ("Update to ratified state") + of riscv-j-extension. + - const: sstc description: | The standard Sstc supervisor-level extension for time compare as ratified at commit 3f9ed34 ("Add ability to manually trigger workflow. (#2)") of riscv-time-compare. + - const: svade + description: | + The standard Svade supervisor-level extension for SW-managed PTE A/D + bit updates as ratified in the 20240213 version of the privileged + ISA specification. + + Both Svade and Svadu extensions control the hardware behavior when + the PTE A/D bits need to be set. The default behavior for the four + possible combinations of these extensions in the device tree are: + 1) Neither Svade nor Svadu present in DT => It is technically + unknown whether the platform uses Svade or Svadu. Supervisor + software should be prepared to handle either hardware updating + of the PTE A/D bits or page faults when they need updated. + 2) Only Svade present in DT => Supervisor must assume Svade to be + always enabled. + 3) Only Svadu present in DT => Supervisor must assume Svadu to be + always enabled. + 4) Both Svade and Svadu present in DT => Supervisor must assume + Svadu turned-off at boot time. To use Svadu, supervisor must + explicitly enable it using the SBI FWFT extension. + + - const: svadu + description: | + The standard Svadu supervisor-level extension for hardware updating + of PTE A/D bits as ratified in the 20240528 version of the + privileged ISA specification. Please refer to Svade dt-binding + description for more details. + - const: svinval description: The standard Svinval supervisor-level extension for fine-grained @@ -178,6 +224,12 @@ properties: as ratified at commit 4a69197e5617 ("Update to ratified state") of riscv-svvptc. + - const: zabha + description: | + The Zabha extension for Byte and Halfword Atomic Memory Operations + as ratified at commit 49f49c842ff9 ("Update to Rafified state") of + riscv-zabha. + - const: zacas description: | The Zacas extension for Atomic Compare-and-Swap (CAS) instructions @@ -290,6 +342,12 @@ properties: in commit 64074bc ("Update version numbers for Zfh/Zfinx") of riscv-isa-manual. + - const: ziccrse + description: + The standard Ziccrse extension which provides forward progress + guarantee on LR/SC sequences, as ratified in commit b1d806605f87 + ("Updated to ratified state.") of the riscv profiles specification. + - const: zk description: The standard Zk Standard Scalar cryptography extension as ratified diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml index 4531eec568a65..9df5cdb6f63f2 100644 --- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml @@ -30,7 +30,9 @@ properties: - const: allwinner,sun50i-a64-rtc - const: allwinner,sun8i-h3-rtc - items: - - const: allwinner,sun20i-d1-rtc + - enum: + - allwinner,sun20i-d1-rtc + - allwinner,sun55i-a523-rtc - const: allwinner,sun50i-r329-rtc reg: diff --git a/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml b/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml new file mode 100644 index 0000000000000..5d3ac737abcba --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2024 Amlogic, Inc. All rights reserved +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/amlogic,a4-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic A4 and A5 RTC + +maintainers: + - Yiting Deng + - Xianwei Zhao + +allOf: + - $ref: rtc.yaml# + +properties: + compatible: + enum: + - amlogic,a4-rtc + - amlogic,a5-rtc + + reg: + maxItems: 1 + + clocks: + items: + - description: RTC clock source, available 24M or 32K crystal + oscillator source. when using 24M, need to divide 24M into 32K. + - description: RTC module accesses the clock of the apb bus. + + clock-names: + items: + - const: osc + - const: sys + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +additionalProperties: false + +examples: + - | + #include + apb { + #address-cells = <2>; + #size-cells = <2>; + + rtc@8e600 { + compatible = "amlogic,a4-rtc"; + reg = <0x0 0x8e600 0x0 0x38>; + clocks = <&xtal_32k>, <&clkc_periphs 1>; + clock-names = "osc", "sys"; + interrupts = ; + }; + }; diff --git a/Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml b/Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml similarity index 87% rename from Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml rename to Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml index 7742465b93839..a3e60d9f83993 100644 --- a/Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/rtc/microchip,mfps-rtc.yaml# +$id: http://devicetree.org/schemas/rtc/microchip,mpfs-rtc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# @@ -12,12 +12,14 @@ allOf: maintainers: - Daire McNamara - - Lewis Hanly properties: compatible: - enum: - - microchip,mpfs-rtc + oneOf: + - items: + - const: microchip,pic64gx-rtc + - const: microchip,mpfs-rtc + - const: microchip,mpfs-rtc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml b/Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml new file mode 100644 index 0000000000000..e70eeb66aa648 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/renesas,rz-rtca3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RTCA-3 Real Time Clock + +maintainers: + - Claudiu Beznea + +allOf: + - $ref: rtc.yaml# + +properties: + compatible: + items: + - enum: + - renesas,r9a08g045-rtca3 # RZ/G3S + - const: renesas,rz-rtca3 + + reg: + maxItems: 1 + + interrupts: + items: + - description: Alarm interrupt + - description: Periodic interrupt + - description: Carry interrupt + + interrupt-names: + items: + - const: alarm + - const: period + - const: carry + + clocks: + items: + - description: RTC bus clock + - description: RTC counter clock + + clock-names: + items: + - const: bus + - const: counter + + power-domains: + maxItems: 1 + + resets: + items: + - description: VBATTB module reset + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clocks + - clock-names + - power-domains + - resets + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + rtc@1004ec00 { + compatible = "renesas,r9a08g045-rtca3", "renesas,rz-rtca3"; + reg = <0x1004ec00 0x400>; + interrupts = , + , + ; + interrupt-names = "alarm", "period", "carry"; + clocks = <&cpg CPG_MOD R9A08G045_VBAT_BCLK>, <&vbattclk VBATTB_VBATTCLK>; + clock-names = "bus", "counter"; + power-domains = <&cpg>; + resets = <&cpg R9A08G045_VBAT_BRESETN>; + }; diff --git a/Documentation/devicetree/bindings/serial/rs485.yaml b/Documentation/devicetree/bindings/serial/rs485.yaml index 9418fd66a8e95..b93254ad2a287 100644 --- a/Documentation/devicetree/bindings/serial/rs485.yaml +++ b/Documentation/devicetree/bindings/serial/rs485.yaml @@ -18,16 +18,15 @@ properties: description: prop-encoded-array $ref: /schemas/types.yaml#/definitions/uint32-array items: - items: - - description: Delay between rts signal and beginning of data sent in - milliseconds. It corresponds to the delay before sending data. - default: 0 - maximum: 100 - - description: Delay between end of data sent and rts signal in milliseconds. - It corresponds to the delay after sending data and actual release - of the line. - default: 0 - maximum: 100 + - description: Delay between rts signal and beginning of data sent in + milliseconds. It corresponds to the delay before sending data. + default: 0 + maximum: 100 + - description: Delay between end of data sent and rts signal in milliseconds. + It corresponds to the delay after sending data and actual release + of the line. + default: 0 + maximum: 100 rs485-rts-active-high: description: drive RTS high when sending (this is the default). diff --git a/Documentation/devicetree/bindings/serial/samsung_uart.yaml b/Documentation/devicetree/bindings/serial/samsung_uart.yaml index 788c80e47831c..070eba9f19d3e 100644 --- a/Documentation/devicetree/bindings/serial/samsung_uart.yaml +++ b/Documentation/devicetree/bindings/serial/samsung_uart.yaml @@ -27,6 +27,7 @@ properties: - samsung,exynos4210-uart - samsung,exynos5433-uart - samsung,exynos850-uart + - samsung,exynos8895-uart - items: - enum: - samsung,exynos7-uart @@ -160,18 +161,27 @@ allOf: contains: enum: - google,gs101-uart + - samsung,exynos8895-uart then: required: - samsung,uart-fifosize properties: - reg-io-width: false - clocks: maxItems: 2 clock-names: maxItems: 2 + - if: + properties: + compatible: + contains: + enum: + - google,gs101-uart + then: + properties: + reg-io-width: false + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 4cdb0dcaccf38..1c163cb5dff10 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -37,6 +37,8 @@ properties: - const: renesas,rzn1-uart - items: - enum: + - brcm,bcm11351-dw-apb-uart + - brcm,bcm21664-dw-apb-uart - rockchip,px30-uart - rockchip,rk1808-uart - rockchip,rk3036-uart @@ -48,18 +50,13 @@ properties: - rockchip,rk3328-uart - rockchip,rk3368-uart - rockchip,rk3399-uart + - rockchip,rk3528-uart - rockchip,rk3568-uart + - rockchip,rk3576-uart - rockchip,rk3588-uart - rockchip,rv1108-uart - rockchip,rv1126-uart - - const: snps,dw-apb-uart - - items: - - enum: - - brcm,bcm11351-dw-apb-uart - - brcm,bcm21664-dw-apb-uart - - const: snps,dw-apb-uart - - items: - - enum: + - sophgo,sg2044-uart - starfive,jh7100-hsuart - starfive,jh7100-uart - starfive,jh7110-uart diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.yaml b/Documentation/devicetree/bindings/serial/sprd-uart.yaml index f4dbb6dc2b6ef..a2a5056eba048 100644 --- a/Documentation/devicetree/bindings/serial/sprd-uart.yaml +++ b/Documentation/devicetree/bindings/serial/sprd-uart.yaml @@ -17,6 +17,7 @@ properties: oneOf: - items: - enum: + - sprd,sc9632-uart - sprd,sc9860-uart - sprd,sc9863a-uart - sprd,ums512-uart diff --git a/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml b/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml index ac99883a3f295..7f0be0ac644aa 100644 --- a/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml +++ b/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml @@ -25,6 +25,7 @@ properties: - items: - enum: - mediatek,mt8186-spmi + - mediatek,mt8188-spmi - const: mediatek,mt8195-spmi reg: diff --git a/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml b/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml index a28b70fb330a3..7c3cc20a80d6c 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml +++ b/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml @@ -19,7 +19,11 @@ description: | properties: compatible: - const: qcom,x1e80100-spmi-pmic-arb + oneOf: + - items: + - const: qcom,sar2130p-spmi-pmic-arb + - const: qcom,x1e80100-spmi-pmic-arb + - const: qcom,x1e80100-spmi-pmic-arb reg: items: diff --git a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml index 25a5edeea1645..cde334e3206b0 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml @@ -26,6 +26,7 @@ properties: - qcom,msm8994-ufshc - qcom,msm8996-ufshc - qcom,msm8998-ufshc + - qcom,qcs8300-ufshc - qcom,sa8775p-ufshc - qcom,sc7180-ufshc - qcom,sc7280-ufshc @@ -146,6 +147,7 @@ allOf: contains: enum: - qcom,msm8998-ufshc + - qcom,qcs8300-ufshc - qcom,sa8775p-ufshc - qcom,sc7280-ufshc - qcom,sc8180x-ufshc diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml index bb5010dcefe18..7ffcd236d7bb4 100644 --- a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml +++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml @@ -25,6 +25,7 @@ properties: - allwinner,sun20i-d1-musb - allwinner,sun50i-a100-musb - allwinner,sun50i-h6-musb + - allwinner,sun55i-a523-musb - const: allwinner,sun8i-a33-musb - items: - const: allwinner,sun50i-h616-musb diff --git a/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml b/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml index 89fc9a434d05f..0620d82508c17 100644 --- a/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml +++ b/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml @@ -61,18 +61,15 @@ additionalProperties: false examples: - | - #include #include i2c { #address-cells = <1>; #size-cells = <0>; - #interrupt-cells = <2>; typec@8 { compatible = "cypress,cypd4226"; reg = <0x08>; - interrupt-parent = <&gpio_aon>; - interrupts = ; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; firmware-name = "nvidia,jetson-agx-xavier"; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml index 9ea1e4cd0709c..baf130669c387 100644 --- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml @@ -12,7 +12,11 @@ maintainers: properties: compatible: - const: fsl,imx8mp-dwc3 + oneOf: + - items: + - const: fsl,imx95-dwc3 + - const: fsl,imx8mp-dwc3 + - const: fsl,imx8mp-dwc3 reg: items: diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 0d797e01fc0b4..223f2abd5e592 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -32,6 +32,7 @@ properties: - allwinner,sun50i-a64-ehci - allwinner,sun50i-h6-ehci - allwinner,sun50i-h616-ehci + - allwinner,sun55i-a523-ehci - allwinner,sun5i-a13-ehci - allwinner,sun6i-a31-ehci - allwinner,sun7i-a20-ehci diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index cf33764553fa7..3ee1586fc8b96 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -19,6 +19,7 @@ properties: - allwinner,sun50i-a64-ohci - allwinner,sun50i-h6-ohci - allwinner,sun50i-h616-ohci + - allwinner,sun55i-a523-ohci - allwinner,sun5i-a13-ohci - allwinner,sun6i-a31-ohci - allwinner,sun7i-a20-ohci diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml index fc833363cfb49..6fe2d356dcbde 100644 --- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml +++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml @@ -62,7 +62,14 @@ allOf: peer-hub: true vdd-supply: true -additionalProperties: false +patternProperties: + "^.*@[0-9a-f]{1,2}$": + description: The hard wired USB devices + type: object + $ref: /schemas/usb/usb-device.yaml + additionalProperties: true + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml index 276bf7554215e..20b62228371bd 100644 --- a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml +++ b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml @@ -69,6 +69,7 @@ examples: PDO_FIXED_DATA_SWAP | PDO_FIXED_DUAL_ROLE) PDO_FIXED(9000, 2000, 0)>; + sink-bc12-completion-time-ms = <500>; }; }; }; diff --git a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml index 27b909de49922..a812317d80893 100644 --- a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml +++ b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml @@ -14,8 +14,11 @@ maintainers: properties: compatible: - enum: - - microchip,mpfs-musb + oneOf: + - items: + - const: microchip,pic64gx-musb + - const: microchip,mpfs-musb + - const: microchip,mpfs-musb dr_mode: true diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 18758efb8d296..935e204b607bb 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -26,8 +26,10 @@ properties: - qcom,msm8998-dwc3 - qcom,qcm2290-dwc3 - qcom,qcs404-dwc3 + - qcom,qcs8300-dwc3 - qcom,qdu1000-dwc3 - qcom,sa8775p-dwc3 + - qcom,sar2130p-dwc3 - qcom,sc7180-dwc3 - qcom,sc7280-dwc3 - qcom,sc8180x-dwc3 @@ -201,6 +203,7 @@ allOf: - qcom,msm8953-dwc3 - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 + - qcom,qcs8300-dwc3 - qcom,sa8775p-dwc3 - qcom,sc7180-dwc3 - qcom,sc7280-dwc3 @@ -338,6 +341,7 @@ allOf: contains: enum: - qcom,qcm2290-dwc3 + - qcom,sar2130p-dwc3 - qcom,sc8180x-dwc3 - qcom,sc8180x-dwc3-mp - qcom,sm6115-dwc3 @@ -465,6 +469,7 @@ allOf: - qcom,ipq4019-dwc3 - qcom,ipq8064-dwc3 - qcom,msm8994-dwc3 + - qcom,qcs8300-dwc3 - qcom,qdu1000-dwc3 - qcom,sa8775p-dwc3 - qcom,sc7180-dwc3 @@ -490,6 +495,7 @@ allOf: minItems: 4 maxItems: 5 interrupt-names: + minItems: 4 items: - const: pwr_event - const: hs_phy_irq diff --git a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml index c63db3ebd07bd..b23ef29bf7949 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml @@ -76,6 +76,10 @@ properties: Integer to use BUSWAIT register. renesas,enable-gpio: + deprecated: true + maxItems: 1 + + renesas,enable-gpios: maxItems: 1 description: | gpio specifier to check GPIO determining if USB function should be diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml index c4924113f9bde..a21cc098542d7 100644 --- a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml @@ -27,6 +27,7 @@ select: enum: - rockchip,rk3328-dwc3 - rockchip,rk3568-dwc3 + - rockchip,rk3576-dwc3 - rockchip,rk3588-dwc3 required: - compatible @@ -37,6 +38,7 @@ properties: - enum: - rockchip,rk3328-dwc3 - rockchip,rk3568-dwc3 + - rockchip,rk3576-dwc3 - rockchip,rk3588-dwc3 - const: snps,dwc3 @@ -113,7 +115,9 @@ allOf: properties: compatible: contains: - const: rockchip,rk3568-dwc3 + enum: + - rockchip,rk3568-dwc3 + - rockchip,rk3576-dwc3 then: properties: clocks: diff --git a/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml b/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml new file mode 100644 index 0000000000000..f713cac4a8ac8 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,tusb1046.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TUSB1046-DCI Type-C crosspoint switch + +maintainers: + - Romain Gantois + +allOf: + - $ref: usb-switch.yaml# + +properties: + compatible: + const: ti,tusb1046 + + reg: + maxItems: 1 + +required: + - compatible + - reg + - port + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec-mux@44 { + compatible = "ti,tusb1046"; + reg = <0x44>; + + mode-switch; + orientation-switch; + + port { + endpoint { + remote-endpoint = <&typec_controller>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/ti,tusb73x0-pci.yaml b/Documentation/devicetree/bindings/usb/ti,tusb73x0-pci.yaml new file mode 100644 index 0000000000000..ddda734f36fb2 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,tusb73x0-pci.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,tusb73x0-pci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TUSB73x0 USB 3.0 xHCI Host Controller (PCIe) + +maintainers: + - Francesco Dolcini + +description: + TUSB73x0 USB 3.0 xHCI Host Controller via PCIe x1 Gen2 interface. + The TUSB7320 supports up to two downstream ports, the TUSB7340 supports up + to four downstream ports, both variants share the same PCI device ID. + +properties: + compatible: + const: pci104c,8241 + + reg: + maxItems: 1 + + ti,pwron-active-high: + $ref: /schemas/types.yaml#/definitions/flag + description: + Configure the polarity of the PWRONx# signals. When this is present, the + PWRONx# pins are active high and their internal pull-down resistors are + disabled. When this is absent, the PWRONx# pins are active low (default) + and their internal pull-down resistors are enabled. + +required: + - compatible + - reg + +allOf: + - $ref: usb-xhci.yaml + +additionalProperties: false + +examples: + - | + pcie@0 { + reg = <0x0 0x1000>; + ranges = <0x02000000 0x0 0x100000 0x10000000 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + + usb@0 { + compatible = "pci104c,8241"; + reg = <0x0 0x0 0x0 0x0 0x0>; + ti,pwron-active-high; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 715663b450f85..da01616802c76 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -83,6 +83,8 @@ patternProperties: description: ALFA Network Inc. "^allegro,.*": description: Allegro DVT + "^allegromicro,.*": + description: Allegro MicroSystems, Inc. "^alliedvision,.*": description: Allied Vision Technologies GmbH "^allo,.*": @@ -565,6 +567,8 @@ patternProperties: description: GE Fanuc Intelligent Platforms Embedded Systems, Inc. "^GEFanuc,.*": description: GE Fanuc Intelligent Platforms Embedded Systems, Inc. + "^gehc,.*": + description: GE HealthCare "^gemei,.*": description: Gemei Digital Technology Co., Ltd. "^gemtek,.*": diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst index e6ffd59bb8f0f..af9697e60165d 100644 --- a/Documentation/doc-guide/kernel-doc.rst +++ b/Documentation/doc-guide/kernel-doc.rst @@ -533,6 +533,7 @@ identifiers: *[ function/type ...]* Include documentation for each *function* and *type* in *source*. If no *function* is specified, the documentation for all functions and types in the *source* will be included. + *type* can be a struct, union, enum, or typedef identifier. Examples:: diff --git a/Documentation/driver-api/auxiliary_bus.rst b/Documentation/driver-api/auxiliary_bus.rst index cec84908fbc0d..b236de773e1d6 100644 --- a/Documentation/driver-api/auxiliary_bus.rst +++ b/Documentation/driver-api/auxiliary_bus.rst @@ -24,7 +24,6 @@ Auxiliary Device Creation .. kernel-doc:: drivers/base/auxiliary.c :identifiers: auxiliary_device_init __auxiliary_device_add - auxiliary_find_device Auxiliary Device Memory Model and Lifespan ------------------------------------------ diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index ebbf8e4cc85fb..d594d0ea0e9df 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -394,7 +394,6 @@ PCI pcim_enable_device() : after success, some PCI ops become managed pcim_iomap() : do iomap() on a single BAR pcim_iomap_regions() : do request_region() and iomap() on multiple BARs - pcim_iomap_regions_request_all() : do request_region() on all and iomap() on multiple BARs pcim_iomap_table() : array of mapped addresses indexed by BAR pcim_iounmap() : do iounmap() on a single BAR pcim_iounmap_regions() : do iounmap() and release_region() on multiple BARs @@ -459,7 +458,6 @@ SERDEV SLAVE DMA ENGINE devm_acpi_dma_controller_register() - devm_acpi_dma_controller_free() SPI devm_spi_alloc_host() diff --git a/Documentation/driver-api/pci/pci.rst b/Documentation/driver-api/pci/pci.rst index aa40b1cc243b8..59d86e8271986 100644 --- a/Documentation/driver-api/pci/pci.rst +++ b/Documentation/driver-api/pci/pci.rst @@ -46,6 +46,9 @@ PCI Support Library .. kernel-doc:: drivers/pci/pci-sysfs.c :internal: +.. kernel-doc:: drivers/pci/tph.c + :export: + PCI Hotplug Support Library --------------------------- diff --git a/Documentation/driver-api/pwrseq.rst b/Documentation/driver-api/pwrseq.rst index a644084ded17a..ad18b2326b689 100644 --- a/Documentation/driver-api/pwrseq.rst +++ b/Documentation/driver-api/pwrseq.rst @@ -11,7 +11,7 @@ Introduction ============ This framework is designed to abstract complex power-up sequences that are -shared between multiple logical devices in the linux kernel. +shared between multiple logical devices in the Linux kernel. The intention is to allow consumers to obtain a power sequencing handle exposed by the power sequence provider and delegate the actual requesting and @@ -25,7 +25,7 @@ The power sequencing API uses a number of terms specific to the subsystem: Unit - A unit is a discreet chunk of a power sequence. For instance one unit may + A unit is a discrete chunk of a power sequence. For instance one unit may enable a set of regulators, another may enable a specific GPIO. Units can define dependencies in the form of other units that must be enabled before it itself can be. @@ -62,7 +62,7 @@ Provider interface The provider API is admittedly not nearly as straightforward as the one for consumers but it makes up for it in flexibility. -Each provider can logically split the power-up sequence into descrete chunks +Each provider can logically split the power-up sequence into discrete chunks (units) and define their dependencies. They can then expose named targets that consumers may use as the final point in the sequence that they wish to reach. @@ -72,7 +72,7 @@ register with the pwrseq subsystem by calling pwrseq_device_register(). Dynamic consumer matching ------------------------- -The main difference between pwrseq and other linux kernel providers is the +The main difference between pwrseq and other Linux kernel providers is the mechanism for dynamic matching of consumers and providers. Every power sequence provider driver must implement the `match()` callback and pass it to the pwrseq core when registering with the subsystems. diff --git a/Documentation/features/locking/queued-spinlocks/arch-support.txt b/Documentation/features/locking/queued-spinlocks/arch-support.txt index 22f2990392ff8..cf26042480e21 100644 --- a/Documentation/features/locking/queued-spinlocks/arch-support.txt +++ b/Documentation/features/locking/queued-spinlocks/arch-support.txt @@ -20,7 +20,7 @@ | openrisc: | ok | | parisc: | TODO | | powerpc: | ok | - | riscv: | TODO | + | riscv: | ok | | s390: | TODO | | sh: | TODO | | sparc: | ok | diff --git a/Documentation/filesystems/autofs.rst b/Documentation/filesystems/autofs.rst index 1ac576458c69a..5eb02394fcc3a 100644 --- a/Documentation/filesystems/autofs.rst +++ b/Documentation/filesystems/autofs.rst @@ -442,7 +442,7 @@ which can be used to communicate directly with the autofs filesystem. It requires CAP_SYS_ADMIN for access. The 'ioctl's that can be used on this device are described in a separate -document `autofs-mount-control.txt`, and are summarised briefly here. +document `autofs-mount-control.rst`, and are summarised briefly here. Each ioctl is passed a pointer to an `autofs_dev_ioctl` structure:: struct autofs_dev_ioctl { diff --git a/Documentation/filesystems/dlmfs.rst b/Documentation/filesystems/dlmfs.rst index 7e2b1fd471d79..70d4e48242c3a 100644 --- a/Documentation/filesystems/dlmfs.rst +++ b/Documentation/filesystems/dlmfs.rst @@ -36,7 +36,7 @@ None Usage ===== -If you're just interested in OCFS2, then please see ocfs2.txt. The +If you're just interested in OCFS2, then please see ocfs2.rst. The rest of this document will be geared towards those who want to use dlmfs for easy to setup and easy to use clustered locking in userspace. diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 68a0885fb5e69..fb7d2ee022bc0 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -943,3 +943,47 @@ NVMe Zoned Namespace devices can start before the zone-capacity and span across zone-capacity boundary. Such spanning segments are also considered as usable segments. All blocks past the zone-capacity are considered unusable in these segments. + +Device aliasing feature +----------------------- + +f2fs can utilize a special file called a "device aliasing file." This file allows +the entire storage device to be mapped with a single, large extent, not using +the usual f2fs node structures. This mapped area is pinned and primarily intended +for holding the space. + +Essentially, this mechanism allows a portion of the f2fs area to be temporarily +reserved and used by another filesystem or for different purposes. Once that +external usage is complete, the device aliasing file can be deleted, releasing +the reserved space back to F2FS for its own use. + + + +# ls /dev/vd* +/dev/vdb (32GB) /dev/vdc (32GB) +# mkfs.ext4 /dev/vdc +# mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb +# mount /dev/vdb /mnt/f2fs +# ls -l /mnt/f2fs +vdc.file +# df -h +/dev/vdb 64G 33G 32G 52% /mnt/f2fs + +# mount -o loop /dev/vdc /mnt/ext4 +# df -h +/dev/vdb 64G 33G 32G 52% /mnt/f2fs +/dev/loop7 32G 24K 30G 1% /mnt/ext4 +# umount /mnt/ext4 + +# f2fs_io getflags /mnt/f2fs/vdc.file +get a flag on /mnt/f2fs/vdc.file ret=0, flags=nocow(pinned),immutable +# f2fs_io setflags noimmutable /mnt/f2fs/vdc.file +get a flag on noimmutable ret=0, flags=800010 +set a flag on /mnt/f2fs/vdc.file ret=0, flags=noimmutable +# rm /mnt/f2fs/vdc.file +# df -h +/dev/vdb 64G 753M 64G 2% /mnt/f2fs + +So, the key idea is, user can do any file operations on /dev/vdc, and +reclaim the space after the use, while the space is counted as /data. +That doesn't require modifying partition size and filesystem format. diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index 0e2fac7a16da1..76e5382178689 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -16,7 +16,7 @@ btrfs filesystems. Like fscrypt, not too much filesystem-specific code is needed to support fs-verity. fs-verity is similar to `dm-verity -`_ +`_ but works on files rather than block devices. On regular files on filesystems supporting fs-verity, userspace can execute an ioctl that causes the filesystem to build a Merkle tree for the file and persist diff --git a/Documentation/filesystems/mount_api.rst b/Documentation/filesystems/mount_api.rst index 317934c9e8fca..d92c276f1575a 100644 --- a/Documentation/filesystems/mount_api.rst +++ b/Documentation/filesystems/mount_api.rst @@ -770,7 +770,8 @@ process the parameters it is given. * :: - bool fs_validate_description(const struct fs_parameter_description *desc); + bool fs_validate_description(const char *name, + const struct fs_parameter_description *desc); This performs some validation checks on a parameter description. It returns true if the description is good and false if it is not. It will diff --git a/Documentation/filesystems/path-lookup.rst b/Documentation/filesystems/path-lookup.rst index 2b2df6aa54324..9ced1135608ea 100644 --- a/Documentation/filesystems/path-lookup.rst +++ b/Documentation/filesystems/path-lookup.rst @@ -531,7 +531,7 @@ this retry process in the next article. Automount points are locations in the filesystem where an attempt to lookup a name can trigger changes to how that lookup should be handled, in particular by mounting a filesystem there. These are -covered in greater detail in autofs.txt in the Linux documentation +covered in greater detail in autofs.rst in the Linux documentation tree, but a few notes specifically related to path lookup are in order here. diff --git a/Documentation/filesystems/path-lookup.txt b/Documentation/filesystems/path-lookup.txt index 1aa7ce099f6f5..d2cf2852e1f8c 100644 --- a/Documentation/filesystems/path-lookup.txt +++ b/Documentation/filesystems/path-lookup.txt @@ -379,4 +379,4 @@ Papers and other documentation on dcache locking 2. http://lse.sourceforge.net/locking/dcache/dcache.html -3. path-lookup.md in this directory. +3. path-lookup.rst in this directory. diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst index 447f767c64627..fa4f81099cb44 100644 --- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst +++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst @@ -315,7 +315,7 @@ the above threads) is: 2) The cpio archive format chosen by the kernel is simpler and cleaner (and thus easier to create and parse) than any of the (literally dozens of) various tar archive formats. The complete initramfs archive format is - explained in buffer-format.txt, created in usr/gen_init_cpio.c, and + explained in buffer-format.rst, created in usr/gen_init_cpio.c, and extracted in init/initramfs.c. All three together come to less than 26k total of human-readable text. diff --git a/Documentation/iio/ad7380.rst b/Documentation/iio/ad7380.rst index 6f70b49b9ef27..c46127700e14c 100644 --- a/Documentation/iio/ad7380.rst +++ b/Documentation/iio/ad7380.rst @@ -27,6 +27,8 @@ The following chips are supported by this driver: * `AD7386-4 `_ * `AD7387-4 `_ * `AD7388-4 `_ +* `ADAQ4370-4 `_ +* `ADAQ4380-4 `_ Supported features @@ -47,6 +49,12 @@ ad7380-4 ad7380-4 supports only an external reference voltage (2.5V to 3.3V). It must be declared in the device tree as ``refin-supply``. +ADAQ devices +~~~~~~~~~~~~ + +adaq4370-4 and adaq4380-4 don't have an external reference, but use a 3.3V +internal reference derived from one of its supplies (``refin-supply``) + All other devices from ad738x family ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -121,6 +129,14 @@ Example for AD7386/7/8 (2 channels parts): When enabling sequencer mode, the effective sampling rate is divided by two. +Gain (ADAQ devices only) +~~~~~~~~~~~~~~~~~~~~~~~~ + +ADAQ devices have a pin selectable gain in front of each ADC. The appropriate +gain is selectable from device tree using the ``adi,gain-milli`` property. +Refer to the typical connection diagrams section of the datasheet for pin +wiring. + Unimplemented features ---------------------- diff --git a/Documentation/iio/ad7606.rst b/Documentation/iio/ad7606.rst new file mode 100644 index 0000000000000..930199e03c67f --- /dev/null +++ b/Documentation/iio/ad7606.rst @@ -0,0 +1,144 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD7606 driver +============= + +ADC driver for Analog Devices Inc. AD7606 and similar devices. The module name +is ``ad7606``. + +Supported devices +================= + +The following chips are supported by this driver: + +* `AD7605 `_ +* `AD7606 `_ +* `AD7606B `_ +* `AD7616 `_ + +Supported features +================== + +SPI wiring modes +---------------- + +These ADCs can output data on several SDO lines (1/2/4/8). The driver +currently supports only 1 SDO line. + +Parallel wiring mode +-------------------- + +There is also a parallel interface, with 16 lines (that can be reduced to 8 in +byte mode). The parallel interface is selected by declaring the device as +platform in the device tree (with no io-backends node defined, see below). + +IIO-backend mode +---------------- + +This mode allows to reach the best sample rates, but it requires an external +hardware (eg HDL or APU) to handle the low level communication. +The backend mode is enabled when through the definition of the "io-backends" +property in the device tree. + +The reference configuration for the current implementation of IIO-backend mode +is the HDL reference provided by ADI: +https://wiki.analog.com/resources/eval/user-guides/ad7606x-fmc/hdl + +This implementation embeds an IIO-backend compatible IP (adi-axi-adc) and a PWM +connected to the conversion trigger pin. + +.. code-block:: + + +---+ +---------------------------- + | | +-------+ |AD76xx + | A | controls | | | + | D |-------------->| PWM |-------------->| cnvst + | 7 | | | | + | 6 | +-------+ | + | 0 | controls +-----------+-----------+ | + | 6 |---------->| | |<--| frstdata + | | | Backend | Backend |<--| busy + | D | | Driver | | | + | R | | | |-->| clk + | I | requests |+---------+| DMA | | + | V |----------->| Buffer ||<---- |<=>| DATA + | E | |+---------+| | | + | R | +-----------+-----------+ | + | |-------------------------------------->| reset/configuration gpios + +---+ +----------------------------- + + +Software and hardware modes +--------------------------- + +While all the AD7606/AD7616 series parts can be configured using GPIOs, some of +them can be configured using register. + +The chips that support software mode have more values available for configuring +the device, as well as more settings, and allow to control the range and +calibration per channel. + +The following settings are available per channel in software mode: + - Scale + +Also, there is a broader choice of oversampling ratios in software mode. + +Conversion triggering +--------------------- + +The conversion can be triggered by two distinct ways: + + - A GPIO is connected to the conversion trigger pin, and this GPIO is controlled + by the driver directly. In this configuration, the driver sets back the + conversion trigger pin to high as soon as it has read all the conversions. + + - An external source is connected to the conversion trigger pin. In the + current implementation, it must be a PWM. In this configuration, the driver + does not control directly the conversion trigger pin. Instead, it can + control the PWM's frequency. This trigger is enabled only for iio-backend. + +Reference voltage +----------------- + +2 possible reference voltage sources are supported: + + - Internal reference (2.5V) + - External reference (2.5V) + +The source is determined by the device tree. If ``refin-supply`` is present, +then the external reference is used, otherwise the internal reference is used. + +Oversampling +------------ + +This family supports oversampling to improve SNR. +In software mode, the following ratios are available: +1 (oversampling disabled)/2/4/8/16/32/64/128/256. + +Unimplemented features +---------------------- + +- 2/4/8 SDO lines +- CRC indication +- Calibration + +Device buffers +============== + +IIO triggered buffer +-------------------- + +This driver supports IIO triggered buffers, with a "built in" trigger, i.e the +trigger is allocated and linked by the driver, and a new conversion is triggered +as soon as the samples are transferred, and a timestamp channel is added to make +up for the potential jitter induced by the delays in the interrupt handling. + +IIO backend buffer +------------------ + +When IIO backend is used, the trigger is not needed, and the sample rate is +considered as stable. There is no timestamp channel. The communication is +delegated to an external logic, called a backend, and the backend's driver +handles the buffer. When this mode is enabled, the driver cannot control the +conversion pin, because the busy pin is bound to the backend. diff --git a/Documentation/iio/ad7625.rst b/Documentation/iio/ad7625.rst new file mode 100644 index 0000000000000..61761e3b75c39 --- /dev/null +++ b/Documentation/iio/ad7625.rst @@ -0,0 +1,91 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +==================== +AD7625 driver +==================== + +ADC driver for Analog Devices Inc. AD7625, AD7626, AD7960, and AD7961 +devices. The module name is ``ad7625``. + +Supported devices +================= + +The following chips are supported by this driver: + +* `AD7625 `_ +* `AD7626 `_ +* `AD7960 `_ +* `AD7961 `_ + +The driver requires use of the Pulsar LVDS HDL project: + +* `Pulsar LVDS HDL `_ + +To trigger conversions and enable subsequent data transfer, the devices +require coupled PWM signals with a phase offset. + +Supported features +================== + +Conversion control modes +------------------------ + +The driver currently supports one of two possible LVDS conversion control methods. + +Echoed-Clock interface mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: + + +----------------+ + +xxxxxxxxxxxxxxxxxxxxxxxxxx| CNV | + X | | + v | HOST | + +----------------------------+ | | + | CNV+/CNV- DCO+/DCO- |xxxxxxx>| CLK_IN | + | | | | + | | | | + | AD7625 D+/D- |xxxxxxx>| DATA_IN | + | | | | + | | | | + | CLK+/CLK- | "endchoice" -This defines a choice group and accepts any of the above attributes as -options. +This defines a choice group and accepts "prompt", "default", "depends on", and +"help" attributes as options. A choice only allows a single config entry to be selected. diff --git a/Documentation/kbuild/llvm.rst b/Documentation/kbuild/llvm.rst index 6dc66b4f31a7b..bc8a283bc44bb 100644 --- a/Documentation/kbuild/llvm.rst +++ b/Documentation/kbuild/llvm.rst @@ -179,6 +179,9 @@ yet. Bug reports are always welcome at the issue tracker below! * - s390 - Maintained - ``LLVM=1`` (LLVM >= 18.1.0), ``CC=clang`` (LLVM < 18.1.0) + * - sparc (sparc64 only) + - Maintained + - ``CC=clang LLVM_IAS=0`` (LLVM >= 20) * - um (User Mode) - Maintained - ``LLVM=1`` diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 7964e0c245aeb..d36519f194dc0 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -449,6 +449,20 @@ $(obj) to prerequisites are referenced with $(src) (because they are not generated files). +$(srcroot) + $(srcroot) refers to the root of the source you are building, which can be + either the kernel source or the external modules source, depending on whether + KBUILD_EXTMOD is set. This can be either a relative or an absolute path, but + if KBUILD_ABS_SRCTREE=1 is set, it is always an absolute path. + +$(srctree) + $(srctree) refers to the root of the kernel source tree. When building the + kernel, this is the same as $(srcroot). + +$(objtree) + $(objtree) refers to the root of the kernel object tree. It is ``.`` when + building the kernel, but it is different when building external modules. + $(kecho) echoing information to user in a rule is often a good practice but when execution ``make -s`` one does not expect to see any output diff --git a/Documentation/kbuild/modules.rst b/Documentation/kbuild/modules.rst index cd5a54d91e6d2..101de236cd0c9 100644 --- a/Documentation/kbuild/modules.rst +++ b/Documentation/kbuild/modules.rst @@ -59,6 +59,12 @@ Command Syntax $ make -C /lib/modules/`uname -r`/build M=$PWD modules_install + Starting from Linux 6.13, you can use the -f option instead of -C. This + will avoid unnecessary change of the working directory. The external + module will be output to the directory where you invoke make. + + $ make -f /lib/modules/`uname -r`/build/Makefile M=$PWD + Options ------- @@ -66,7 +72,10 @@ Options of the kernel output directory if the kernel was built in a separate build directory.) - make -C $KDIR M=$PWD + You can optionally pass MO= option if you want to build the modules in + a separate directory. + + make -C $KDIR M=$PWD [MO=$BUILD_DIR] -C $KDIR The directory that contains the kernel and relevant build @@ -80,6 +89,9 @@ Options directory where the external module (kbuild file) is located. + MO=$BUILD_DIR + Specifies a separate output directory for the external module. + Targets ------- @@ -215,6 +227,21 @@ Separate Kbuild File and Makefile consisting of several hundred lines, and here it really pays off to separate the kbuild part from the rest. + Linux 6.13 and later support another way. The external module Makefile + can include the kernel Makefile directly, rather than invoking sub Make. + + Example 3:: + + --> filename: Kbuild + obj-m := 8123.o + 8123-y := 8123_if.o 8123_pci.o + + --> filename: Makefile + KDIR ?= /lib/modules/$(shell uname -r)/build + export KBUILD_EXTMOD := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) + include $(KDIR)/Makefile + + Building Multiple Modules ------------------------- diff --git a/Documentation/kernel-hacking/hacking.rst b/Documentation/kernel-hacking/hacking.rst index 1717348a4404e..0042776a9e17c 100644 --- a/Documentation/kernel-hacking/hacking.rst +++ b/Documentation/kernel-hacking/hacking.rst @@ -587,7 +587,7 @@ Defined in ``include/linux/export.h`` Similar to :c:func:`EXPORT_SYMBOL()` except that the symbols exported by :c:func:`EXPORT_SYMBOL_GPL()` can only be seen by -modules with a :c:func:`MODULE_LICENSE()` that specifies a GPL +modules with a :c:func:`MODULE_LICENSE()` that specifies a GPLv2 compatible license. It implies that the function is considered an internal implementation issue, and not really an interface. Some maintainers and developers may however require EXPORT_SYMBOL_GPL() diff --git a/Documentation/mm/slub.rst b/Documentation/mm/slub.rst index 60d350d08362b..84ca1dc94e5ee 100644 --- a/Documentation/mm/slub.rst +++ b/Documentation/mm/slub.rst @@ -175,6 +175,15 @@ can be influenced by kernel parameters: ``slab_max_order`` to 0, what cause minimum possible order of slabs allocation. +``slab_strict_numa`` + Enables the application of memory policies on each + allocation. This results in more accurate placement of + objects which may result in the reduction of accesses + to remote nodes. The default is to only apply memory + policies at the folio level when a new folio is acquired + or a folio is retrieved from the lists. Enabling this + option reduces the fastpath performance of the slab allocator. + SLUB Debug output ================= diff --git a/Documentation/networking/cdc_mbim.rst b/Documentation/networking/cdc_mbim.rst index 37f968acc473a..8404a3f794f37 100644 --- a/Documentation/networking/cdc_mbim.rst +++ b/Documentation/networking/cdc_mbim.rst @@ -51,7 +51,7 @@ Such userspace applications includes, but are not limited to: - mbimcli (included with the libmbim [3] library), and - ModemManager [4] -Establishing a MBIM IP session reequires at least these actions by the +Establishing a MBIM IP session requires at least these actions by the management application: - open the control channel diff --git a/Documentation/networking/tls-offload.rst b/Documentation/networking/tls-offload.rst index 5f0dea3d571e3..7354d48cdf926 100644 --- a/Documentation/networking/tls-offload.rst +++ b/Documentation/networking/tls-offload.rst @@ -51,7 +51,7 @@ and send them to the device for encryption and transmission. RX -- -On the receive side if the device handled decryption and authentication +On the receive side, if the device handled decryption and authentication successfully, the driver will set the decrypted bit in the associated :c:type:`struct sk_buff `. The packets reach the TCP stack and are handled normally. ``ktls`` is informed when data is queued to the socket @@ -120,8 +120,9 @@ before installing the connection state in the kernel. RX -- -In RX direction local networking stack has little control over the segmentation, -so the initial records' TCP sequence number may be anywhere inside the segment. +In the RX direction, the local networking stack has little control over +segmentation, so the initial records' TCP sequence number may be anywhere +inside the segment. Normal operation ================ @@ -138,8 +139,8 @@ There are no guarantees on record length or record segmentation. In particular segments may start at any point of a record and contain any number of records. Assuming segments are received in order, the device should be able to perform crypto operations and authentication regardless of segmentation. For this -to be possible device has to keep small amount of segment-to-segment state. -This includes at least: +to be possible, the device has to keep a small amount of segment-to-segment +state. This includes at least: * partial headers (if a segment carried only a part of the TLS header) * partial data block @@ -175,12 +176,12 @@ and packet transformation functions) the device validates the Layer 4 checksum and performs a 5-tuple lookup to find any TLS connection the packet may belong to (technically a 4-tuple lookup is sufficient - IP addresses and TCP port numbers, as the protocol -is always TCP). If connection is matched device confirms if the TCP sequence -number is the expected one and proceeds to TLS handling (record delineation, -decryption, authentication for each record in the packet). The device leaves -the record framing unmodified, the stack takes care of record decapsulation. -Device indicates successful handling of TLS offload in the per-packet context -(descriptor) passed to the host. +is always TCP). If the packet is matched to a connection, the device confirms +if the TCP sequence number is the expected one and proceeds to TLS handling +(record delineation, decryption, authentication for each record in the packet). +The device leaves the record framing unmodified, the stack takes care of record +decapsulation. Device indicates successful handling of TLS offload in the +per-packet context (descriptor) passed to the host. Upon reception of a TLS offloaded packet, the driver sets the :c:member:`decrypted` mark in :c:type:`struct sk_buff ` @@ -439,7 +440,7 @@ by the driver: * ``rx_tls_resync_req_end`` - number of times the TLS async resync request properly ended with providing the HW tracked tcp-seq. * ``rx_tls_resync_req_skip`` - number of times the TLS async resync request - procedure was started by not properly ended. + procedure was started but not properly ended. * ``rx_tls_resync_res_ok`` - number of times the TLS resync response call to the driver was successfully handled. * ``rx_tls_resync_res_skip`` - number of times the TLS resync response call to @@ -507,8 +508,8 @@ in packets as seen on the wire. Transport layer transparency ---------------------------- -The device should not modify any packet headers for the purpose -of the simplifying TLS offload. +For the purpose of simplifying TLS offload, the device should not modify any +packet headers. The device should not depend on any packet headers beyond what is strictly necessary for TLS offload. diff --git a/Documentation/process/debugging/driver_development_debugging_guide.rst b/Documentation/process/debugging/driver_development_debugging_guide.rst new file mode 100644 index 0000000000000..aef2040942050 --- /dev/null +++ b/Documentation/process/debugging/driver_development_debugging_guide.rst @@ -0,0 +1,223 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================== +Debugging advice for driver development +======================================== + +This document serves as a general starting point and lookup for debugging +device drivers. +While this guide focuses on debugging that requires re-compiling the +module/kernel, the :doc:`userspace debugging guide +` will guide +you through tools like dynamic debug, ftrace and other tools useful for +debugging issues and behavior. +For general debugging advice, see the :doc:`general advice document +`. + +.. contents:: + :depth: 3 + +The following sections show you the available tools. + +printk() & friends +------------------ + +These are derivatives of printf() with varying destinations and support for +being dynamically turned on or off, or lack thereof. + +Simple printk() +~~~~~~~~~~~~~~~ + +The classic, can be used to great effect for quick and dirty development +of new modules or to extract arbitrary necessary data for troubleshooting. + +Prerequisite: ``CONFIG_PRINTK`` (usually enabled by default) + +**Pros**: + +- No need to learn anything, simple to use +- Easy to modify exactly to your needs (formatting of the data (See: + :doc:`/core-api/printk-formats`), visibility in the log) +- Can cause delays in the execution of the code (beneficial to confirm whether + timing is a factor) + +**Cons**: + +- Requires rebuilding the kernel/module +- Can cause delays in the execution of the code (which can cause issues to be + not reproducible) + +For the full documentation see :doc:`/core-api/printk-basics` + +Trace_printk +~~~~~~~~~~~~ + +Prerequisite: ``CONFIG_DYNAMIC_FTRACE`` & ``#include `` + +It is a tiny bit less comfortable to use than printk(), because you will have +to read the messages from the trace file (See: :ref:`read_ftrace_log` +instead of from the kernel log, but very useful when printk() adds unwanted +delays into the code execution, causing issues to be flaky or hidden.) + +If the processing of this still causes timing issues then you can try +trace_puts(). + +For the full Documentation see trace_printk() + +dev_dbg +~~~~~~~ + +Print statement, which can be targeted by +:ref:`process/debugging/userspace_debugging_guide:dynamic debug` that contains +additional information about the device used within the context. + +**When is it appropriate to leave a debug print in the code?** + +Permanent debug statements have to be useful for a developer to troubleshoot +driver misbehavior. Judging that is a bit more of an art than a science, but +some guidelines are in the :ref:`Coding style guidelines +`. In almost all cases the +debug statements shouldn't be upstreamed, as a working driver is supposed to be +silent. + +Custom printk +~~~~~~~~~~~~~ + +Example:: + + #define core_dbg(fmt, arg...) do { \ + if (core_debug) \ + printk(KERN_DEBUG pr_fmt("core: " fmt), ## arg); \ + } while (0) + +**When should you do this?** + +It is better to just use a pr_debug(), which can later be turned on/off with +dynamic debug. Additionally, a lot of drivers activate these prints via a +variable like ``core_debug`` set by a module parameter. However, Module +parameters `are not recommended anymore +`_. + +Ftrace +------ + +Creating a custom Ftrace tracepoint +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A tracepoint adds a hook into your code that will be called and logged when the +tracepoint is enabled. This can be used, for example, to trace hitting a +conditional branch or to dump the internal state at specific points of the code +flow during a debugging session. + +Here is a basic description of :ref:`how to implement new tracepoints +`. + +For the full event tracing documentation see :doc:`/trace/events` + +For the full Ftrace documentation see :doc:`/trace/ftrace` + +DebugFS +------- + +Prerequisite: ``CONFIG_DEBUG_FS` & `#include `` + +DebugFS differs from the other approaches of debugging, as it doesn't write +messages to the kernel log nor add traces to the code. Instead it allows the +developer to handle a set of files. +With these files you can either store values of variables or make +register/memory dumps or you can make these files writable and modify +values/settings in the driver. + +Possible use-cases among others: + +- Store register values +- Keep track of variables +- Store errors +- Store settings +- Toggle a setting like debug on/off +- Error injection + +This is especially useful, when the size of a data dump would be hard to digest +as part of the general kernel log (for example when dumping raw bitstream data) +or when you are not interested in all the values all the time, but with the +possibility to inspect them. + +The general idea is: + +- Create a directory during probe (``struct dentry *parent = + debugfs_create_dir("my_driver", NULL);``) +- Create a file (``debugfs_create_u32("my_value", 444, parent, &my_variable);``) + + - In this example the file is found in + ``/sys/kernel/debug/my_driver/my_value`` (with read permissions for + user/group/all) + - any read of the file will return the current contents of the variable + ``my_variable`` + +- Clean up the directory when removing the device + (``debugfs_remove_recursive(parent);``) + +For the full documentation see :doc:`/filesystems/debugfs`. + +KASAN, UBSAN, lockdep and other error checkers +---------------------------------------------- + +KASAN (Kernel Address Sanitizer) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prerequisite: ``CONFIG_KASAN`` + +KASAN is a dynamic memory error detector that helps to find use-after-free and +out-of-bounds bugs. It uses compile-time instrumentation to check every memory +access. + +For the full documentation see :doc:`/dev-tools/kasan`. + +UBSAN (Undefined Behavior Sanitizer) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prerequisite: ``CONFIG_UBSAN`` + +UBSAN relies on compiler instrumentation and runtime checks to detect undefined +behavior. It is designed to find a variety of issues, including signed integer +overflow, array index out of bounds, and more. + +For the full documentation see :doc:`/dev-tools/ubsan` + +lockdep (Lock Dependency Validator) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prerequisite: ``CONFIG_DEBUG_LOCKDEP`` + +lockdep is a runtime lock dependency validator that detects potential deadlocks +and other locking-related issues in the kernel. +It tracks lock acquisitions and releases, building a dependency graph that is +analyzed for potential deadlocks. +lockdep is especially useful for validating the correctness of lock ordering in +the kernel. + +PSI (Pressure stall information tracking) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prerequisite: ``CONFIG_PSI`` + +PSI is a measurement tool to identify excessive overcommits on hardware +resources, that can cause performance disruptions or even OOM kills. + +device coredump +--------------- + +Prerequisite: ``#include `` + +Provides the infrastructure for a driver to provide arbitrary data to userland. +It is most often used in conjunction with udev or similar userland application +to listen for kernel uevents, which indicate that the dump is ready. Udev has +rules to copy that file somewhere for long-term storage and analysis, as by +default, the data for the dump is automatically cleaned up after 5 minutes. +That data is analyzed with driver-specific tools or GDB. + +You can find an example implementation at: +`drivers/media/platform/qcom/venus/core.c +`__ + +**Copyright** ©2024 : Collabora diff --git a/Documentation/process/debugging/index.rst b/Documentation/process/debugging/index.rst new file mode 100644 index 0000000000000..f6e4a00dfee32 --- /dev/null +++ b/Documentation/process/debugging/index.rst @@ -0,0 +1,78 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +Debugging advice for Linux Kernel developers +============================================ + +general guides +-------------- + +.. toctree:: + :maxdepth: 1 + + driver_development_debugging_guide + userspace_debugging_guide + +.. only:: subproject and html + +subsystem specific guides +------------------------- + +.. toctree:: + :maxdepth: 1 + + media_specific_debugging_guide + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` + +General debugging advice +======================== + +Depending on the issue, a different set of tools is available to track down the +problem or even to realize whether there is one in the first place. + +As a first step you have to figure out what kind of issue you want to debug. +Depending on the answer, your methodology and choice of tools may vary. + +Do I need to debug with limited access? +--------------------------------------- + +Do you have limited access to the machine or are you unable to stop the running +execution? + +In this case your debugging capability depends on built-in debugging support of +provided distribution kernel. +The :doc:`/process/debugging/userspace_debugging_guide` provides a brief +overview over a range of possible debugging tools in that situation. You can +check the capability of your kernel, in most cases, by looking into config file +within the /boot directory. + +Do I have root access to the system? +------------------------------------ + +Are you easily able to replace the module in question or to install a new +kernel? + +In that case your range of available tools is a lot bigger, you can find the +tools in the :doc:`/process/debugging/driver_development_debugging_guide`. + +Is timing a factor? +------------------- + +It is important to understand if the problem you want to debug manifests itself +consistently (i.e. given a set of inputs you always get the same, incorrect +output), or inconsistently. If it manifests itself inconsistently, some timing +factor might be at play. If inserting delays into the code does change the +behavior, then quite likely timing is a factor. + +When timing does alter the outcome of the code execution using a simple +printk() for debugging purposes may not work, a similar alternative is to use +trace_printk() , which logs the debug messages to the trace file instead of the +kernel log. + +**Copyright** ©2024 : Collabora diff --git a/Documentation/process/debugging/media_specific_debugging_guide.rst b/Documentation/process/debugging/media_specific_debugging_guide.rst new file mode 100644 index 0000000000000..c5a93bafaf675 --- /dev/null +++ b/Documentation/process/debugging/media_specific_debugging_guide.rst @@ -0,0 +1,180 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +Debugging and tracing in the media subsystem +============================================ + +This document serves as a starting point and lookup for debugging device +drivers in the media subsystem and to debug these drivers from userspace. + +.. contents:: + :depth: 3 + +General debugging advice +------------------------ + +For general advice see the :doc:`general advice document +`. + +The following sections show you some of the available tools. + +dev_debug module parameter +-------------------------- + +Every video device provides a ``dev_debug`` parameter, which allows to get +further insights into the IOCTLs in the background.:: + + # cat /sys/class/video4linux/video3/name + rkvdec + # echo 0xff > /sys/class/video4linux/video3/dev_debug + # dmesg -wH + [...] videodev: v4l2_open: video3: open (0) + [ +0.000036] video3: VIDIOC_QUERYCAP: driver=rkvdec, card=rkvdec, + bus=platform:rkvdec, version=0x00060900, capabilities=0x84204000, + device_caps=0x04204000 + +For the full documentation see :ref:`driver-api/media/v4l2-dev:video device +debugging` + +dev_dbg() / v4l2_dbg() +---------------------- + +Two debug print statements, which are specific for devices and for the v4l2 +subsystem, avoid adding these to your final submission unless they have +long-term value for investigations. + +For a general overview please see the +:ref:`process/debugging/driver_development_debugging_guide:printk() & friends` +guide. + +- Difference between both? + + - v4l2_dbg() utilizes v4l2_printk() under the hood, which further uses + printk() directly, thus it cannot be targeted by dynamic debug + - dev_dbg() can be targeted by dynamic debug + - v4l2_dbg() has a more specific prefix format for the media subsystem, while + dev_dbg only highlights the driver name and the location of the log + +Dynamic debug +------------- + +A method to trim down the debug output to your needs. + +For general advice see the +:ref:`process/debugging/userspace_debugging_guide:dynamic debug` guide. + +Here is one example, that enables all available pr_debug()'s within the file:: + + $ alias ddcmd='echo $* > /proc/dynamic_debug/control' + $ ddcmd '-p; file v4l2-h264.c +p' + $ grep =p /proc/dynamic_debug/control + drivers/media/v4l2-core/v4l2-h264.c:372 [v4l2_h264]print_ref_list_b =p + "ref_pic_list_b%u (cur_poc %u%c) %s" + drivers/media/v4l2-core/v4l2-h264.c:333 [v4l2_h264]print_ref_list_p =p + "ref_pic_list_p (cur_poc %u%c) %s\n" + +Ftrace +------ + +An internal kernel tracer that can trace static predefined events, function +calls, etc. Very useful for debugging problems without changing the kernel and +understanding the behavior of subsystems. + +For general advice see the +:ref:`process/debugging/userspace_debugging_guide:ftrace` guide. + +DebugFS +------- + +This tool allows you to dump or modify internal values of your driver to files +in a custom filesystem. + +For general advice see the +:ref:`process/debugging/driver_development_debugging_guide:debugfs` guide. + +Perf & alternatives +------------------- + +Tools to measure the various stats on a running system to diagnose issues. + +For general advice see the +:ref:`process/debugging/userspace_debugging_guide:perf & alternatives` guide. + +Example for media devices: + +Gather statistics data for a decoding job: (This example is on a RK3399 SoC +with the rkvdec codec driver using the `fluster test suite +`__):: + + perf stat -d python3 fluster.py run -d GStreamer-H.264-V4L2SL-Gst1.0 -ts + JVT-AVC_V1 -tv AUD_MW_E -j1 + ... + Performance counter stats for 'python3 fluster.py run -d + GStreamer-H.264-V4L2SL-Gst1.0 -ts JVT-AVC_V1 -tv AUD_MW_E -j1 -v': + + 7794.23 msec task-clock:u # 0.697 CPUs utilized + 0 context-switches:u # 0.000 /sec + 0 cpu-migrations:u # 0.000 /sec + 11901 page-faults:u # 1.527 K/sec + 882671556 cycles:u # 0.113 GHz (95.79%) + 711708695 instructions:u # 0.81 insn per cycle (95.79%) + 10581935 branches:u # 1.358 M/sec (15.13%) + 6871144 branch-misses:u # 64.93% of all branches (95.79%) + 281716547 L1-dcache-loads:u # 36.144 M/sec (95.79%) + 9019581 L1-dcache-load-misses:u # 3.20% of all L1-dcache accesses (95.79%) + LLC-loads:u + LLC-load-misses:u + + 11.180830431 seconds time elapsed + + 1.502318000 seconds user + 6.377221000 seconds sys + +The availability of events and metrics depends on the system you are running. + +Error checking & panic analysis +------------------------------- + +Various Kernel configuration options to enhance error detection of the Linux +Kernel with the cost of lowering performance. + +For general advice see the +:ref:`process/debugging/driver_development_debugging_guide:kasan, ubsan, +lockdep and other error checkers` guide. + +Driver verification with v4l2-compliance +---------------------------------------- + +To verify, that a driver adheres to the v4l2 API, the tool v4l2-compliance is +used, which is part of the `v4l_utils +`__, a suite of userspace tools to work +with the media subsystem. + +To see the detailed media topology (and check it) use:: + + v4l2-compliance -M /dev/mediaX --verbose + +You can also run a full compliance check for all devices referenced in the +media topology with:: + + v4l2-compliance -m /dev/mediaX + +Debugging problems with receiving video +--------------------------------------- + +Implementing vidioc_log_status in the driver: this can log the current status +to the kernel log. It's called by v4l2-ctl --log-status. Very useful for +debugging problems with receiving video (TV/S-Video/HDMI/etc) since the video +signal is external (so unpredictable). Less useful with camera sensor inputs +since you have control over what the camera sensor does. + +Usually you can just assign the default:: + + .vidioc_log_status = v4l2_ctrl_log_status, + +But you can also create your own callback, to create a custom status log. + +You can find an example in the cobalt driver +(`drivers/media/pci/cobalt/cobalt-v4l2.c `__). + +**Copyright** ©2024 : Collabora diff --git a/Documentation/process/debugging/userspace_debugging_guide.rst b/Documentation/process/debugging/userspace_debugging_guide.rst new file mode 100644 index 0000000000000..db7396261e07a --- /dev/null +++ b/Documentation/process/debugging/userspace_debugging_guide.rst @@ -0,0 +1,280 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +Userspace debugging advice +========================== + +This document provides a brief overview of common tools to debug the Linux +Kernel from userspace. +For debugging advice aimed at driver developers go :doc:`here +`. +For general debugging advice, see :doc:`general advice document +`. + +.. contents:: + :depth: 3 + +The following sections show you the available tools. + +Dynamic debug +------------- + +Mechanism to filter what ends up in the kernel log by dis-/en-abling log +messages. + +Prerequisite: ``CONFIG_DYNAMIC_DEBUG`` + +Dynamic debug is only able to target: + +- pr_debug() +- dev_dbg() +- print_hex_dump_debug() +- print_hex_dump_bytes() + +Therefore the usability of this tool is, as of now, quite limited as there is +no uniform rule for adding debug prints to the codebase, resulting in a variety +of ways these prints are implemented. + +Also, note that most debug statements are implemented as a variation of +dprintk(), which have to be activated via a parameter in respective module, +dynamic debug is unable to do that step for you. + +Here is one example, that enables all available pr_debug()'s within the file:: + + $ alias ddcmd='echo $* > /proc/dynamic_debug/control' + $ ddcmd '-p; file v4l2-h264.c +p' + $ grep =p /proc/dynamic_debug/control + drivers/media/v4l2-core/v4l2-h264.c:372 [v4l2_h264]print_ref_list_b =p + "ref_pic_list_b%u (cur_poc %u%c) %s" + drivers/media/v4l2-core/v4l2-h264.c:333 [v4l2_h264]print_ref_list_p =p + "ref_pic_list_p (cur_poc %u%c) %s\n" + +**When should you use this over Ftrace ?** + +- When the code contains one of the valid print statements (see above) or when + you have added multiple pr_debug() statements during development +- When timing is not an issue, meaning if multiple pr_debug() statements in + the code won't cause delays +- When you care more about receiving specific log messages than tracing the + pattern of how a function is called + +For the full documentation see :doc:`/admin-guide/dynamic-debug-howto` + +Ftrace +------ + +Prerequisite: ``CONFIG_DYNAMIC_FTRACE`` + +This tool uses the tracefs file system for the control files and output files. +That file system will be mounted as a ``tracing`` directory, which can be found +in either ``/sys/kernel/`` or ``/sys/debug/kernel/``. + +Some of the most important operations for debugging are: + +- You can perform a function trace by adding a function name to the + ``set_ftrace_filter`` file (which accepts any function name found within the + ``available_filter_functions`` file) or you can specifically disable certain + functions by adding their names to the ``set_ftrace_notrace`` file (more info + at: :ref:`trace/ftrace:dynamic ftrace`). +- In order to find out where calls originate from you can activate the + ``func_stack_trace`` option under ``options/func_stack_trace``. +- Tracing the children of a function call and showing the return values are + possible by adding the desired function in the ``set_graph_function`` file + (requires config ``FUNCTION_GRAPH_RETVAL``); more info at + :ref:`trace/ftrace:dynamic ftrace with the function graph tracer`. + +For the full Ftrace documentation see :doc:`/trace/ftrace` + +Or you could also trace for specific events by :ref:`using event tracing +`, which can be defined as described here: +:ref:`Creating a custom Ftrace tracepoint +`. + +For the full Ftrace event tracing documentation see :doc:`/trace/events` + +.. _read_ftrace_log: + +Reading the ftrace log +~~~~~~~~~~~~~~~~~~~~~~ + +The ``trace`` file can be read just like any other file (``cat``, ``tail``, +``head``, ``vim``, etc.), the size of the file is limited by the +``buffer_size_kb`` (``echo 1000 > buffer_size_kb``). The +:ref:`trace/ftrace:trace_pipe` will behave similarly to the ``trace`` file, but +whenever you read from the file the content is consumed. + +Kernelshark +~~~~~~~~~~~ + +A GUI interface to visualize the traces as a graph and list view from the +output of the `trace-cmd +`__ application. + +For the full documentation see ``__ + +Perf & alternatives +------------------- + +The tools mentioned above provide ways to inspect kernel code, results, +variable values, etc. Sometimes you have to find out first where to look and +for those cases, a box of performance tracking tools can help you to frame the +issue. + +Why should you do a performance analysis? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A performance analysis is a good first step when among other reasons: + +- you cannot define the issue +- you do not know where it occurs +- the running system should not be interrupted or it is a remote system, where + you cannot install a new module/kernel + +How to do a simple analysis with linux tools? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For the start of a performance analysis, you can start with the usual tools +like: + +- ``top`` / ``htop`` / ``atop`` (*get an overview of the system load, see + spikes on specific processes*) +- ``mpstat -P ALL`` (*look at the load distribution among CPUs*) +- ``iostat -x`` (*observe input and output devices utilization and performance*) +- ``vmstat`` (*overview of memory usage on the system*) +- ``pidstat`` (*similar to* ``vmstat`` *but per process, to dial it down to the + target*) +- ``strace -tp $PID`` (*once you know the process, you can figure out how it + communicates with the Kernel*) + +These should help to narrow down the areas to look at sufficiently. + +Diving deeper with perf +~~~~~~~~~~~~~~~~~~~~~~~ + +The **perf** tool provides a series of metrics and events to further dial down +on issues. + +Prerequisite: build or install perf on your system + +Gather statistics data for finding all files starting with ``gcc`` in ``/usr``:: + + # perf stat -d find /usr -name 'gcc*' | wc -l + + Performance counter stats for 'find /usr -name gcc*': + + 1277.81 msec task-clock # 0.997 CPUs utilized + 9 context-switches # 7.043 /sec + 1 cpu-migrations # 0.783 /sec + 704 page-faults # 550.943 /sec + 766548897 cycles # 0.600 GHz (97.15%) + 798285467 instructions # 1.04 insn per cycle (97.15%) + 57582731 branches # 45.064 M/sec (2.85%) + 3842573 branch-misses # 6.67% of all branches (97.15%) + 281616097 L1-dcache-loads # 220.390 M/sec (97.15%) + 4220975 L1-dcache-load-misses # 1.50% of all L1-dcache accesses (97.15%) + LLC-loads + LLC-load-misses + + 1.281746009 seconds time elapsed + + 0.508796000 seconds user + 0.773209000 seconds sys + + + 52 + +The availability of events and metrics depends on the system you are running. + +For the full documentation see +``__ + +Perfetto +~~~~~~~~ + +A set of tools to measure and analyze how well applications and systems perform. +You can use it to: + +* identify bottlenecks +* optimize code +* make software run faster and more efficiently. + +**What is the difference between perfetto and perf?** + +* perf is tool as part of and specialized for the Linux Kernel and has CLI user + interface. +* perfetto cross-platform performance analysis stack, has extended + functionality into userspace and provides a WEB user interface. + +For the full documentation see ``__ + +Kernel panic analysis tools +--------------------------- + + To capture the crash dump please use ``Kdump`` & ``Kexec``. Below you can find + some advice for analysing the data. + + For the full documentation see the :doc:`/admin-guide/kdump/kdump` + + In order to find the corresponding line in the code you can use `faddr2line + `__; note + that you need to enable ``CONFIG_DEBUG_INFO`` for that to work. + + An alternative to using ``faddr2line`` is the use of ``objdump`` (and its + derivatives for the different platforms like ``aarch64-linux-gnu-objdump``). + Take this line as an example: + + ``[ +0.000240] rkvdec_device_run+0x50/0x138 [rockchip_vdec]``. + + We can find the corresponding line of code by executing:: + + aarch64-linux-gnu-objdump -dS drivers/staging/media/rkvdec/rockchip-vdec.ko | grep rkvdec_device_run\>: -A 40 + 0000000000000ac8 : + ac8: d503201f nop + acc: d503201f nop + { + ad0: d503233f paciasp + ad4: a9bd7bfd stp x29, x30, [sp, #-48]! + ad8: 910003fd mov x29, sp + adc: a90153f3 stp x19, x20, [sp, #16] + ae0: a9025bf5 stp x21, x22, [sp, #32] + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + ae4: f9411814 ldr x20, [x0, #560] + struct rkvdec_dev *rkvdec = ctx->dev; + ae8: f9418015 ldr x21, [x0, #768] + if (WARN_ON(!desc)) + aec: b4000654 cbz x20, bb4 + ret = pm_runtime_resume_and_get(rkvdec->dev); + af0: f943d2b6 ldr x22, [x21, #1952] + ret = __pm_runtime_resume(dev, RPM_GET_PUT); + af4: aa0003f3 mov x19, x0 + af8: 52800081 mov w1, #0x4 // #4 + afc: aa1603e0 mov x0, x22 + b00: 94000000 bl 0 <__pm_runtime_resume> + if (ret < 0) { + b04: 37f80340 tbnz w0, #31, b6c + dev_warn(rkvdec->dev, "Not good\n"); + b08: f943d2a0 ldr x0, [x21, #1952] + b0c: 90000001 adrp x1, 0 + b10: 91000021 add x1, x1, #0x0 + b14: 94000000 bl 0 <_dev_warn> + *bad = 1; + b18: d2800001 mov x1, #0x0 // #0 + ... + + Meaning, in this line from the crash dump:: + + [ +0.000240] rkvdec_device_run+0x50/0x138 [rockchip_vdec] + + I can take the ``0x50`` as offset, which I have to add to the base address + of the corresponding function, which I find in this line:: + + 0000000000000ac8 : + + The result of ``0xac8 + 0x50 = 0xb18`` + And when I search for that address within the function I get the + following line:: + + *bad = 1; + b18: d2800001 mov x1, #0x0 + +**Copyright** ©2024 : Collabora diff --git a/Documentation/process/index.rst b/Documentation/process/index.rst index 6455eba3ef0c5..aa12f26601949 100644 --- a/Documentation/process/index.rst +++ b/Documentation/process/index.rst @@ -72,13 +72,15 @@ beyond). Dealing with bugs ----------------- -Bugs are a fact of life; it is important that we handle them properly. -The documents below describe our policies around the handling of a couple -of special classes of bugs: regressions and security problems. +Bugs are a fact of life; it is important that we handle them properly. The +documents below provide general advice about debugging and describe our +policies around the handling of a couple of special classes of bugs: +regressions and security problems. .. toctree:: :maxdepth: 1 + debugging/index handling-regressions security-bugs cve diff --git a/Documentation/process/license-rules.rst b/Documentation/process/license-rules.rst index 2ef44ada3f119..59a7832df7d08 100644 --- a/Documentation/process/license-rules.rst +++ b/Documentation/process/license-rules.rst @@ -471,14 +471,16 @@ _`MODULE_LICENSE` source files. "Proprietary" The module is under a proprietary license. - This string is solely for proprietary third - party modules and cannot be used for modules - which have their source code in the kernel - tree. Modules tagged that way are tainting - the kernel with the 'P' flag when loaded and - the kernel module loader refuses to link such - modules against symbols which are exported - with EXPORT_SYMBOL_GPL(). + "Proprietary" is to be understood only as + "The license is not compatible to GPLv2". + This string is solely for non-GPL2 compatible + third party modules and cannot be used for + modules which have their source code in the + kernel tree. Modules tagged that way are + tainting the kernel with the 'P' flag when + loaded and the kernel module loader refuses + to link such modules against symbols which + are exported with EXPORT_SYMBOL_GPL(). ============================= ============================================= diff --git a/Documentation/rust/coding-guidelines.rst b/Documentation/rust/coding-guidelines.rst index 329b070a1d473..a2e326b42410f 100644 --- a/Documentation/rust/coding-guidelines.rst +++ b/Documentation/rust/coding-guidelines.rst @@ -227,3 +227,149 @@ The equivalent in Rust may look like (ignoring documentation): That is, the equivalent of ``GPIO_LINE_DIRECTION_IN`` would be referred to as ``gpio::LineDirection::In``. In particular, it should not be named ``gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN``. + + +Lints +----- + +In Rust, it is possible to ``allow`` particular warnings (diagnostics, lints) +locally, making the compiler ignore instances of a given warning within a given +function, module, block, etc. + +It is similar to ``#pragma GCC diagnostic push`` + ``ignored`` + ``pop`` in C +[#]_: + +.. code-block:: c + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + static void f(void) {} + #pragma GCC diagnostic pop + +.. [#] In this particular case, the kernel's ``__{always,maybe}_unused`` + attributes (C23's ``[[maybe_unused]]``) may be used; however, the example + is meant to reflect the equivalent lint in Rust discussed afterwards. + +But way less verbose: + +.. code-block:: rust + + #[allow(dead_code)] + fn f() {} + +By that virtue, it makes it possible to comfortably enable more diagnostics by +default (i.e. outside ``W=`` levels). In particular, those that may have some +false positives but that are otherwise quite useful to keep enabled to catch +potential mistakes. + +On top of that, Rust provides the ``expect`` attribute which takes this further. +It makes the compiler warn if the warning was not produced. For instance, the +following will ensure that, when ``f()`` is called somewhere, we will have to +remove the attribute: + +.. code-block:: rust + + #[expect(dead_code)] + fn f() {} + +If we do not, we get a warning from the compiler:: + + warning: this lint expectation is unfulfilled + --> x.rs:3:10 + | + 3 | #[expect(dead_code)] + | ^^^^^^^^^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + +This means that ``expect``\ s do not get forgotten when they are not needed, which +may happen in several situations, e.g.: + +- Temporary attributes added while developing. + +- Improvements in lints in the compiler, Clippy or custom tools which may + remove a false positive. + +- When the lint is not needed anymore because it was expected that it would be + removed at some point, such as the ``dead_code`` example above. + +It also increases the visibility of the remaining ``allow``\ s and reduces the +chance of misapplying one. + +Thus prefer ``expect`` over ``allow`` unless: + +- Conditional compilation triggers the warning in some cases but not others. + + If there are only a few cases where the warning triggers (or does not + trigger) compared to the total number of cases, then one may consider using + a conditional ``expect`` (i.e. ``cfg_attr(..., expect(...))``). Otherwise, + it is likely simpler to just use ``allow``. + +- Inside macros, when the different invocations may create expanded code that + triggers the warning in some cases but not in others. + +- When code may trigger a warning for some architectures but not others, such + as an ``as`` cast to a C FFI type. + +As a more developed example, consider for instance this program: + +.. code-block:: rust + + fn g() {} + + fn main() { + #[cfg(CONFIG_X)] + g(); + } + +Here, function ``g()`` is dead code if ``CONFIG_X`` is not set. Can we use +``expect`` here? + +.. code-block:: rust + + #[expect(dead_code)] + fn g() {} + + fn main() { + #[cfg(CONFIG_X)] + g(); + } + +This would emit a lint if ``CONFIG_X`` is set, since it is not dead code in that +configuration. Therefore, in cases like this, we cannot use ``expect`` as-is. + +A simple possibility is using ``allow``: + +.. code-block:: rust + + #[allow(dead_code)] + fn g() {} + + fn main() { + #[cfg(CONFIG_X)] + g(); + } + +An alternative would be using a conditional ``expect``: + +.. code-block:: rust + + #[cfg_attr(not(CONFIG_X), expect(dead_code))] + fn g() {} + + fn main() { + #[cfg(CONFIG_X)] + g(); + } + +This would ensure that, if someone introduces another call to ``g()`` somewhere +(e.g. unconditionally), then it would be spotted that it is not dead code +anymore. However, the ``cfg_attr`` is more complex than a simple ``allow``. + +Therefore, it is likely that it is not worth using conditional ``expect``\ s when +more than one or two configurations are involved or when the lint may be +triggered due to non-local changes (such as ``dead_code``). + +For more information about diagnostics in Rust, please see: + + https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst index 2d107982c87bb..4aa50e5fcb8c0 100644 --- a/Documentation/rust/quick-start.rst +++ b/Documentation/rust/quick-start.rst @@ -87,6 +87,23 @@ they should generally work out of the box, e.g.:: zypper install rust rust1.79-src rust-bindgen clang +Ubuntu +****** + +Ubuntu LTS and non-LTS (interim) releases provide recent Rust releases and thus +they should generally work out of the box, e.g.:: + + apt install rustc-1.80 rust-1.80-src bindgen-0.65 rustfmt-1.80 rust-1.80-clippy + +``RUST_LIB_SRC`` needs to be set when using the versioned packages, e.g.:: + + RUST_LIB_SRC=/usr/src/rustc-$(rustc-1.80 --version | cut -d' ' -f2)/library + +In addition, ``bindgen-0.65`` is available in newer releases (24.04 LTS and +24.10), but it may not be available in older ones (20.04 LTS and 22.04 LTS), +thus ``bindgen`` may need to be built manually (please see below). + + Requirements: Building ---------------------- diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst index e199131bf5abb..f81e94d8f145b 100644 --- a/Documentation/sound/hd-audio/notes.rst +++ b/Documentation/sound/hd-audio/notes.rst @@ -42,7 +42,7 @@ If you are interested in the deep debugging of HD-audio, read the HD-audio specification at first. The specification is found on Intel's web page, for example: -* https://www.intel.com/standards/hdaudio/ +* https://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html HD-Audio Controller diff --git a/Documentation/sound/soc/dapm.rst b/Documentation/sound/soc/dapm.rst index 14c4dc026e6bd..73a42d5a9f302 100644 --- a/Documentation/sound/soc/dapm.rst +++ b/Documentation/sound/soc/dapm.rst @@ -35,6 +35,9 @@ The graph for the STM32MP1-DK1 sound card is shown in picture: :alt: Example DAPM graph :align: center +You can also generate compatible graph for your sound card using +`tools/sound/dapm-graph` utility. + DAPM power domains ================== diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst index 74d5bd801b1a8..272464bb7c602 100644 --- a/Documentation/trace/ftrace.rst +++ b/Documentation/trace/ftrace.rst @@ -1033,9 +1033,13 @@ explains which is which. irqs-off: 'd' interrupts are disabled. '.' otherwise. need-resched: + - 'B' all, TIF_NEED_RESCHED, PREEMPT_NEED_RESCHED and TIF_RESCHED_LAZY is set, - 'N' both TIF_NEED_RESCHED and PREEMPT_NEED_RESCHED is set, - 'n' only TIF_NEED_RESCHED is set, - 'p' only PREEMPT_NEED_RESCHED is set, + - 'L' both PREEMPT_NEED_RESCHED and TIF_RESCHED_LAZY is set, + - 'b' both TIF_NEED_RESCHED and TIF_RESCHED_LAZY is set, + - 'l' only TIF_RESCHED_LAZY is set - '.' otherwise. hardirq/softirq: diff --git a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst index 472761938682c..d4ff80de47b63 100644 --- a/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst +++ b/Documentation/translations/zh_CN/arch/loongarch/irq-chip-model.rst @@ -87,6 +87,61 @@ PCH-LPC/PCH-MSI,然后被EIOINTC统一收集,再直接到达CPUINTC:: | Devices | +---------+ +虚拟扩展IRQ模型 +=============== + +在这种模型里面, IPI(Inter-Processor Interrupt) 和CPU本地时钟中断直接发送到CPUINTC, +CPU串口 (UARTs) 中断发送到PCH-PIC, 而其他所有设备的中断则分别发送到所连接的PCH_PIC/ +PCH-MSI, 然后V-EIOINTC统一收集,再直接到达CPUINTC:: + + +-----+ +-------------------+ +-------+ + | IPI |--> | CPUINTC(0-255vcpu)| <-- | Timer | + +-----+ +-------------------+ +-------+ + ^ + | + +-----------+ + | V-EIOINTC | + +-----------+ + ^ ^ + | | + +---------+ +---------+ + | PCH-PIC | | PCH-MSI | + +---------+ +---------+ + ^ ^ ^ + | | | + +--------+ +---------+ +---------+ + | UARTs | | Devices | | Devices | + +--------+ +---------+ +---------+ + +V-EIOINTC 是EIOINTC的扩展, 仅工作在虚拟机模式下, 中断经EIOINTC最多可个路由到 +4个虚拟CPU. 但中断经V-EIOINTC最多可个路由到256个虚拟CPU. + +传统的EIOINTC中断控制器,中断路由分为两个部分:8比特用于控制路由到哪个CPU, +4比特用于控制路由到特定CPU的哪个中断管脚。控制CPU路由的8比特前4比特用于控制 +路由到哪个EIOINTC节点,后4比特用于控制此节点哪个CPU。中断路由在选择CPU路由 +和CPU中断管脚路由时,使用bitmap编码方式而不是正常编码方式,所以对于一个 +EIOINTC中断控制器节点,中断只能路由到CPU0 - CPU3,中断管脚IP0-IP3。 + +V-EIOINTC新增了两个寄存器,支持中断路由到更多CPU个和中断管脚。 + +V-EIOINTC功能寄存器 +------------------- +功能寄存器是只读寄存器,用于显示V-EIOINTC支持的特性,目前两个支持两个特性 +EXTIOI_HAS_INT_ENCODE 和 EXTIOI_HAS_CPU_ENCODE。 + +特性EXTIOI_HAS_INT_ENCODE是传统EIOINTC中断控制器的一个特性,如果此比特为1, +显示CPU中断管脚路由方式支持正常编码,而不是bitmap编码,所以中断可以路由到 +管脚IP0 - IP15。 + +特性EXTIOI_HAS_CPU_ENCODE是V-EIOINTC新增特性,如果此比特为1,表示CPU路由 +方式支持正常编码,而不是bitmap编码,所以中断可以路由到CPU0 - CPU255。 + +V-EIOINTC配置寄存器 +------------------- +配置寄存器是可读写寄存器,为了兼容性考虑,如果不写此寄存器,中断路由采用 +和传统EIOINTC相同的路由设置。如果对应比特设置为1,表示采用正常路由方式而 +不是bitmap编码的路由方式。 + 高级扩展IRQ模型 =============== diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index edc070c6e19b2..454c2aaa155e5 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -7,8 +7,19 @@ The Definitive KVM (Kernel-based Virtual Machine) API Documentation 1. General description ====================== -The kvm API is a set of ioctls that are issued to control various aspects -of a virtual machine. The ioctls belong to the following classes: +The kvm API is centered around different kinds of file descriptors +and ioctls that can be issued to these file descriptors. An initial +open("/dev/kvm") obtains a handle to the kvm subsystem; this handle +can be used to issue system ioctls. A KVM_CREATE_VM ioctl on this +handle will create a VM file descriptor which can be used to issue VM +ioctls. A KVM_CREATE_VCPU or KVM_CREATE_DEVICE ioctl on a VM fd will +create a virtual cpu or device and return a file descriptor pointing to +the new resource. + +In other words, the kvm API is a set of ioctls that are issued to +different kinds of file descriptor in order to control various aspects of +a virtual machine. Depending on the file descriptor that accepts them, +ioctls belong to the following classes: - System ioctls: These query and set global attributes which affect the whole kvm subsystem. In addition a system ioctl is used to create @@ -35,18 +46,19 @@ of a virtual machine. The ioctls belong to the following classes: device ioctls must be issued from the same process (address space) that was used to create the VM. -2. File descriptors -=================== +While most ioctls are specific to one kind of file descriptor, in some +cases the same ioctl can belong to more than one class. + +The KVM API grew over time. For this reason, KVM defines many constants +of the form ``KVM_CAP_*``, each corresponding to a set of functionality +provided by one or more ioctls. Availability of these "capabilities" can +be checked with :ref:`KVM_CHECK_EXTENSION `. Some +capabilities also need to be enabled for VMs or VCPUs where their +functionality is desired (see :ref:`cap_enable` and :ref:`cap_enable_vm`). -The kvm API is centered around file descriptors. An initial -open("/dev/kvm") obtains a handle to the kvm subsystem; this handle -can be used to issue system ioctls. A KVM_CREATE_VM ioctl on this -handle will create a VM file descriptor which can be used to issue VM -ioctls. A KVM_CREATE_VCPU or KVM_CREATE_DEVICE ioctl on a VM fd will -create a virtual cpu or device and return a file descriptor pointing to -the new resource. Finally, ioctls on a vcpu or device fd can be used -to control the vcpu or device. For vcpus, this includes the important -task of actually running guest code. + +2. Restrictions +=============== In general file descriptors can be migrated among processes by means of fork() and the SCM_RIGHTS facility of unix domain socket. These @@ -96,12 +108,9 @@ description: Capability: which KVM extension provides this ioctl. Can be 'basic', which means that is will be provided by any kernel that supports - API version 12 (see section 4.1), a KVM_CAP_xyz constant, which - means availability needs to be checked with KVM_CHECK_EXTENSION - (see section 4.4), or 'none' which means that while not all kernels - support this ioctl, there's no capability bit to check its - availability: for kernels that don't support the ioctl, - the ioctl returns -ENOTTY. + API version 12 (see :ref:`KVM_GET_API_VERSION `), + or a KVM_CAP_xyz constant that can be checked with + :ref:`KVM_CHECK_EXTENSION `. Architectures: which instruction set architectures provide this ioctl. @@ -118,6 +127,8 @@ description: are not detailed, but errors with specific meanings are. +.. _KVM_GET_API_VERSION: + 4.1 KVM_GET_API_VERSION ----------------------- @@ -246,6 +257,8 @@ This list also varies by kvm version and host processor, but does not change otherwise. +.. _KVM_CHECK_EXTENSION: + 4.4 KVM_CHECK_EXTENSION ----------------------- @@ -288,7 +301,7 @@ the VCPU file descriptor can be mmap-ed, including: - if KVM_CAP_DIRTY_LOG_RING is available, a number of pages at KVM_DIRTY_LOG_PAGE_OFFSET * PAGE_SIZE. For more information on - KVM_CAP_DIRTY_LOG_RING, see section 8.3. + KVM_CAP_DIRTY_LOG_RING, see :ref:`KVM_CAP_DIRTY_LOG_RING`. 4.7 KVM_CREATE_VCPU @@ -338,8 +351,8 @@ KVM_S390_SIE_PAGE_OFFSET in order to obtain a memory map of the virtual cpu's hardware control block. -4.8 KVM_GET_DIRTY_LOG (vm ioctl) --------------------------------- +4.8 KVM_GET_DIRTY_LOG +--------------------- :Capability: basic :Architectures: all @@ -1298,7 +1311,7 @@ See KVM_GET_VCPU_EVENTS for the data structure. :Capability: KVM_CAP_DEBUGREGS :Architectures: x86 -:Type: vm ioctl +:Type: vcpu ioctl :Parameters: struct kvm_debugregs (out) :Returns: 0 on success, -1 on error @@ -1320,7 +1333,7 @@ Reads debug registers from the vcpu. :Capability: KVM_CAP_DEBUGREGS :Architectures: x86 -:Type: vm ioctl +:Type: vcpu ioctl :Parameters: struct kvm_debugregs (in) :Returns: 0 on success, -1 on error @@ -1429,6 +1442,8 @@ because of a quirk in the virtualization implementation (see the internals documentation when it pops into existence). +.. _KVM_ENABLE_CAP: + 4.37 KVM_ENABLE_CAP ------------------- @@ -2116,8 +2131,8 @@ TLB, prior to calling KVM_RUN on the associated vcpu. The "bitmap" field is the userspace address of an array. This array consists of a number of bits, equal to the total number of TLB entries as -determined by the last successful call to KVM_CONFIG_TLB, rounded up to the -nearest multiple of 64. +determined by the last successful call to ``KVM_ENABLE_CAP(KVM_CAP_SW_TLB)``, +rounded up to the nearest multiple of 64. Each bit corresponds to one TLB entry, ordered the same as in the shared TLB array. @@ -2170,42 +2185,6 @@ userspace update the TCE table directly which is useful in some circumstances. -4.63 KVM_ALLOCATE_RMA ---------------------- - -:Capability: KVM_CAP_PPC_RMA -:Architectures: powerpc -:Type: vm ioctl -:Parameters: struct kvm_allocate_rma (out) -:Returns: file descriptor for mapping the allocated RMA - -This allocates a Real Mode Area (RMA) from the pool allocated at boot -time by the kernel. An RMA is a physically-contiguous, aligned region -of memory used on older POWER processors to provide the memory which -will be accessed by real-mode (MMU off) accesses in a KVM guest. -POWER processors support a set of sizes for the RMA that usually -includes 64MB, 128MB, 256MB and some larger powers of two. - -:: - - /* for KVM_ALLOCATE_RMA */ - struct kvm_allocate_rma { - __u64 rma_size; - }; - -The return value is a file descriptor which can be passed to mmap(2) -to map the allocated RMA into userspace. The mapped area can then be -passed to the KVM_SET_USER_MEMORY_REGION ioctl to establish it as the -RMA for a virtual machine. The size of the RMA in bytes (which is -fixed at host kernel boot time) is returned in the rma_size field of -the argument structure. - -The KVM_CAP_PPC_RMA capability is 1 or 2 if the KVM_ALLOCATE_RMA ioctl -is supported; 2 if the processor requires all virtual machines to have -an RMA, or 1 if the processor can use an RMA but doesn't require it, -because it supports the Virtual RMA (VRMA) facility. - - 4.64 KVM_NMI ------------ @@ -2602,7 +2581,7 @@ Specifically: ======================= ========= ===== ======================================= .. [1] These encodings are not accepted for SVE-enabled vcpus. See - KVM_ARM_VCPU_INIT. + :ref:`KVM_ARM_VCPU_INIT`. The equivalent register content can be accessed via bits [127:0] of the corresponding SVE Zn registers instead for vcpus that have SVE @@ -3593,6 +3572,27 @@ Errors: This ioctl returns the guest registers that are supported for the KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. +Note that s390 does not support KVM_GET_REG_LIST for historical reasons +(read: nobody cared). The set of registers in kernels 4.x and newer is: + +- KVM_REG_S390_TODPR + +- KVM_REG_S390_EPOCHDIFF + +- KVM_REG_S390_CPU_TIMER + +- KVM_REG_S390_CLOCK_COMP + +- KVM_REG_S390_PFTOKEN + +- KVM_REG_S390_PFCOMPARE + +- KVM_REG_S390_PFSELECT + +- KVM_REG_S390_PP + +- KVM_REG_S390_GBEA + 4.85 KVM_ARM_SET_DEVICE_ADDR (deprecated) ----------------------------------------- @@ -4956,8 +4956,8 @@ Coalesced pio is based on coalesced mmio. There is little difference between coalesced mmio and pio except that coalesced pio records accesses to I/O ports. -4.117 KVM_CLEAR_DIRTY_LOG (vm ioctl) ------------------------------------- +4.117 KVM_CLEAR_DIRTY_LOG +------------------------- :Capability: KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 :Architectures: x86, arm64, mips @@ -5093,8 +5093,8 @@ Recognised values for feature: Finalizes the configuration of the specified vcpu feature. The vcpu must already have been initialised, enabling the affected feature, by -means of a successful KVM_ARM_VCPU_INIT call with the appropriate flag set in -features[]. +means of a successful :ref:`KVM_ARM_VCPU_INIT ` call with the +appropriate flag set in features[]. For affected vcpu features, this is a mandatory step that must be performed before the vcpu is fully usable. @@ -5266,7 +5266,7 @@ the cpu reset definition in the POP (Principles Of Operation). 4.123 KVM_S390_INITIAL_RESET ---------------------------- -:Capability: none +:Capability: basic :Architectures: s390 :Type: vcpu ioctl :Parameters: none @@ -6205,7 +6205,7 @@ applied. .. _KVM_ARM_GET_REG_WRITABLE_MASKS: 4.139 KVM_ARM_GET_REG_WRITABLE_MASKS -------------------------------------------- +------------------------------------ :Capability: KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES :Architectures: arm64 @@ -6443,6 +6443,8 @@ the capability to be present. `flags` must currently be zero. +.. _kvm_run: + 5. The kvm_run structure ======================== @@ -6855,6 +6857,10 @@ the first `ndata` items (possibly zero) of the data array are valid. the guest issued a SYSTEM_RESET2 call according to v1.1 of the PSCI specification. + - for arm64, data[0] is set to KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 + if the guest issued a SYSTEM_OFF2 call according to v1.3 of the PSCI + specification. + - for RISC-V, data[0] is set to the value of the second argument of the ``sbi_system_reset`` call. @@ -6888,6 +6894,12 @@ either: - Deny the guest request to suspend the VM. See ARM DEN0022D.b 5.19.2 "Caller responsibilities" for possible return values. +Hibernation using the PSCI SYSTEM_OFF2 call is enabled when PSCI v1.3 +is enabled. If a guest invokes the PSCI SYSTEM_OFF2 function, KVM will +exit to userspace with the KVM_SYSTEM_EVENT_SHUTDOWN event type and with +data[0] set to KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2. The only +supported hibernate type for the SYSTEM_OFF2 function is HIBERNATE_OFF. + :: /* KVM_EXIT_IOAPIC_EOI */ @@ -7162,11 +7174,15 @@ primary storage for certain register types. Therefore, the kernel may use the values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set. +.. _cap_enable: + 6. Capabilities that can be enabled on vCPUs ============================================ There are certain capabilities that change the behavior of the virtual CPU or -the virtual machine when enabled. To enable them, please see section 4.37. +the virtual machine when enabled. To enable them, please see +:ref:`KVM_ENABLE_CAP`. + Below you can find a list of capabilities and what their effect on the vCPU or the virtual machine is when enabling them. @@ -7375,7 +7391,7 @@ KVM API and also from the guest. sets are supported (bitfields defined in arch/x86/include/uapi/asm/kvm.h). -As described above in the kvm_sync_regs struct info in section 5 (kvm_run): +As described above in the kvm_sync_regs struct info in section :ref:`kvm_run`, KVM_CAP_SYNC_REGS "allow[s] userspace to access certain guest registers without having to call SET/GET_*REGS". This reduces overhead by eliminating repeated ioctl calls for setting and/or getting register values. This is @@ -7421,13 +7437,15 @@ Unused bitfields in the bitarrays must be set to zero. This capability connects the vcpu to an in-kernel XIVE device. +.. _cap_enable_vm: + 7. Capabilities that can be enabled on VMs ========================================== There are certain capabilities that change the behavior of the virtual -machine when enabled. To enable them, please see section 4.37. Below -you can find a list of capabilities and what their effect on the VM -is when enabling them. +machine when enabled. To enable them, please see section +:ref:`KVM_ENABLE_CAP`. Below you can find a list of capabilities and +what their effect on the VM is when enabling them. The following information is provided along with the description: @@ -8107,6 +8125,28 @@ KVM_X86_QUIRK_SLOT_ZAP_ALL By default, for KVM_X86_DEFAULT_VM VMs, KVM or moved memslot isn't reachable, i.e KVM _may_ invalidate only SPTEs related to the memslot. + +KVM_X86_QUIRK_STUFF_FEATURE_MSRS By default, at vCPU creation, KVM sets the + vCPU's MSR_IA32_PERF_CAPABILITIES (0x345), + MSR_IA32_ARCH_CAPABILITIES (0x10a), + MSR_PLATFORM_INFO (0xce), and all VMX MSRs + (0x480..0x492) to the maximal capabilities + supported by KVM. KVM also sets + MSR_IA32_UCODE_REV (0x8b) to an arbitrary + value (which is different for Intel vs. + AMD). Lastly, when guest CPUID is set (by + userspace), KVM modifies select VMX MSR + fields to force consistency between guest + CPUID and L2's effective ISA. When this + quirk is disabled, KVM zeroes the vCPU's MSR + values (with two exceptions, see below), + i.e. treats the feature MSRs like CPUID + leaves and gives userspace full control of + the vCPU model definition. This quirk does + not affect VMX MSRs CR0/CR4_FIXED1 (0x487 + and 0x489), as KVM does now allow them to + be set by userspace (KVM sets them based on + guest CPUID, for safety purposes). =================================== ============================================ 7.32 KVM_CAP_MAX_VCPU_ID @@ -8588,6 +8628,8 @@ guest according to the bits in the KVM_CPUID_FEATURES CPUID leaf (0x40000001). Otherwise, a guest may use the paravirtual features regardless of what has actually been exposed through the CPUID leaf. +.. _KVM_CAP_DIRTY_LOG_RING: + 8.29 KVM_CAP_DIRTY_LOG_RING/KVM_CAP_DIRTY_LOG_RING_ACQ_REL ---------------------------------------------------------- diff --git a/Documentation/virt/kvm/locking.rst b/Documentation/virt/kvm/locking.rst index 1bedd56e2fe3a..c56d5f26c7505 100644 --- a/Documentation/virt/kvm/locking.rst +++ b/Documentation/virt/kvm/locking.rst @@ -135,8 +135,8 @@ We dirty-log for gfn1, that means gfn2 is lost in dirty-bitmap. For direct sp, we can easily avoid it since the spte of direct sp is fixed to gfn. For indirect sp, we disabled fast page fault for simplicity. -A solution for indirect sp could be to pin the gfn, for example via -gfn_to_pfn_memslot_atomic, before the cmpxchg. After the pinning: +A solution for indirect sp could be to pin the gfn before the cmpxchg. After +the pinning: - We have held the refcount of pfn; that means the pfn can not be freed and be reused for another gfn. @@ -147,49 +147,51 @@ Then, we can ensure the dirty bitmaps is correctly set for a gfn. 2) Dirty bit tracking -In the origin code, the spte can be fast updated (non-atomically) if the +In the original code, the spte can be fast updated (non-atomically) if the spte is read-only and the Accessed bit has already been set since the Accessed bit and Dirty bit can not be lost. But it is not true after fast page fault since the spte can be marked writable between reading spte and updating spte. Like below case: -+------------------------------------------------------------------------+ -| At the beginning:: | -| | -| spte.W = 0 | -| spte.Accessed = 1 | -+------------------------------------+-----------------------------------+ -| CPU 0: | CPU 1: | -+------------------------------------+-----------------------------------+ -| In mmu_spte_clear_track_bits():: | | -| | | -| old_spte = *spte; | | -| | | -| | | -| /* 'if' condition is satisfied. */| | -| if (old_spte.Accessed == 1 && | | -| old_spte.W == 0) | | -| spte = 0ull; | | -+------------------------------------+-----------------------------------+ -| | on fast page fault path:: | -| | | -| | spte.W = 1 | -| | | -| | memory write on the spte:: | -| | | -| | spte.Dirty = 1 | -+------------------------------------+-----------------------------------+ -| :: | | -| | | -| else | | -| old_spte = xchg(spte, 0ull) | | -| if (old_spte.Accessed == 1) | | -| kvm_set_pfn_accessed(spte.pfn);| | -| if (old_spte.Dirty == 1) | | -| kvm_set_pfn_dirty(spte.pfn); | | -| OOPS!!! | | -+------------------------------------+-----------------------------------+ ++-------------------------------------------------------------------------+ +| At the beginning:: | +| | +| spte.W = 0 | +| spte.Accessed = 1 | ++-------------------------------------+-----------------------------------+ +| CPU 0: | CPU 1: | ++-------------------------------------+-----------------------------------+ +| In mmu_spte_update():: | | +| | | +| old_spte = *spte; | | +| | | +| | | +| /* 'if' condition is satisfied. */ | | +| if (old_spte.Accessed == 1 && | | +| old_spte.W == 0) | | +| spte = new_spte; | | ++-------------------------------------+-----------------------------------+ +| | on fast page fault path:: | +| | | +| | spte.W = 1 | +| | | +| | memory write on the spte:: | +| | | +| | spte.Dirty = 1 | ++-------------------------------------+-----------------------------------+ +| :: | | +| | | +| else | | +| old_spte = xchg(spte, new_spte);| | +| if (old_spte.Accessed && | | +| !new_spte.Accessed) | | +| flush = true; | | +| if (old_spte.Dirty && | | +| !new_spte.Dirty) | | +| flush = true; | | +| OOPS!!! | | ++-------------------------------------+-----------------------------------+ The Dirty bit is lost in this case. diff --git a/Documentation/virt/kvm/x86/errata.rst b/Documentation/virt/kvm/x86/errata.rst index 4116045a8744d..37c79362a48fa 100644 --- a/Documentation/virt/kvm/x86/errata.rst +++ b/Documentation/virt/kvm/x86/errata.rst @@ -33,6 +33,18 @@ Note however that any software (e.g ``WIN87EM.DLL``) expecting these features to be present likely predates these CPUID feature bits, and therefore doesn't know to check for them anyway. +``KVM_SET_VCPU_EVENTS`` issue +----------------------------- + +Invalid KVM_SET_VCPU_EVENTS input with respect to error codes *may* result in +failed VM-Entry on Intel CPUs. Pre-CET Intel CPUs require that exception +injection through the VMCS correctly set the "error code valid" flag, e.g. +require the flag be set when injecting a #GP, clear when injecting a #UD, +clear when injecting a soft exception, etc. Intel CPUs that enumerate +IA32_VMX_BASIC[56] as '1' relax VMX's consistency checks, and AMD CPUs have no +restrictions whatsoever. KVM_SET_VCPU_EVENTS doesn't sanity check the vector +versus "has_error_code", i.e. KVM's ABI follows AMD behavior. + Nested virtualization features ------------------------------ diff --git a/MAINTAINERS b/MAINTAINERS index e7f0170977013..1e930c7a58b13 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1218,6 +1218,14 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml F: drivers/perf/amlogic/ F: include/soc/amlogic/ +AMLOGIC RTC DRIVER +M: Yiting Deng +M: Xianwei Zhao +L: linux-amlogic@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml +F: drivers/rtc/rtc-amlogic-a4.c + AMPHENOL CHIPCAP 2 HUMIDITY-TEMPERATURE IIO DRIVER M: Javier Carrasco L: linux-hwmon@vger.kernel.org @@ -1271,7 +1279,6 @@ M: Cosmin Tanislav L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers -F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c @@ -1336,6 +1343,17 @@ F: Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml F: drivers/iio/addac/ad74413r.c F: include/dt-bindings/iio/addac/adi,ad74413r.h +ANALOG DEVICES INC AD7625 DRIVER +M: Michael Hennerich +M: Nuno Sá +R: Trevor Gamblin +S: Supported +W: https://ez.analog.com/linux-software-drivers +W: http://analogdevicesinc.github.io/hdl/projects/pulsar_lvds/index.html +F: Documentation/devicetree/bindings/iio/adc/adi,ad7625.yaml +F: Documentation/iio/ad7625.rst +F: drivers/iio/adc/ad7625.c + ANALOG DEVICES INC AD7768-1 DRIVER M: Michael Hennerich L: linux-iio@vger.kernel.org @@ -1363,6 +1381,14 @@ F: Documentation/ABI/testing/debugfs-iio-ad9467 F: Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml F: drivers/iio/adc/ad9467.c +ANALOG DEVICES INC AD8460 DRIVER +M: Mariel Tinaco +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/dac/adi,ad8460.yaml +F: drivers/iio/dac/ad8460.c + ANALOG DEVICES INC AD9739a DRIVER M: Nuno Sa M: Dragos Bogdan @@ -1563,6 +1589,7 @@ F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 F: Documentation/devicetree/bindings/iio/*/adi,* F: Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml F: Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +F: Documentation/iio/ad7606.rst F: drivers/iio/*/ad* F: drivers/iio/adc/ltc249* F: drivers/iio/amplifiers/hmc425a.c @@ -2213,7 +2240,7 @@ F: drivers/mmc/host/usdhi6rol0.c F: drivers/pinctrl/pinctrl-artpec* ARM/ASPEED I2C DRIVER -M: Brendan Higgins +M: Ryan Chen R: Benjamin Herrenschmidt R: Joel Stanley L: linux-i2c@vger.kernel.org @@ -2924,6 +2951,7 @@ Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/ C: irc://irc.libera.chat/renesas-soc T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next F: Documentation/devicetree/bindings/hwinfo/renesas,prr.yaml +F: Documentation/devicetree/bindings/nvmem/renesas,* F: Documentation/devicetree/bindings/soc/renesas/ F: arch/arm/boot/dts/renesas/ F: arch/arm/configs/shmobile_defconfig @@ -2931,6 +2959,7 @@ F: arch/arm/include/debug/renesas-scif.S F: arch/arm/mach-shmobile/ F: arch/arm64/boot/dts/renesas/ F: arch/riscv/boot/dts/renesas/ +F: drivers/nvmem/rcar-efuse.c F: drivers/pmdomain/renesas/ F: drivers/soc/renesas/ F: include/linux/soc/renesas/ @@ -3686,6 +3715,13 @@ F: kernel/audit* F: lib/*audit.c K: \baudit_[a-z_0-9]\+\b +AUTOFDO BUILD +M: Rong Xu +M: Han Shen +S: Supported +F: Documentation/dev-tools/autofdo.rst +F: scripts/Makefile.autofdo + AUXILIARY BUS DRIVER M: Greg Kroah-Hartman R: Dave Ertman @@ -4036,6 +4072,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/accel/bosch,bma400.yaml F: drivers/iio/accel/bma400* +BOSCH SENSORTEC BMI270 IMU IIO DRIVER +M: Alex Lanzano +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/imu/bosch,bmi270.yaml +F: drivers/iio/imu/bmi270/ + BOSCH SENSORTEC BMI323 IMU IIO DRIVER M: Jagath Jog J L: linux-iio@vger.kernel.org @@ -9483,6 +9526,14 @@ M: Kieran Bingham S: Supported F: scripts/gdb/ +GE HEALTHCARE PMC ADC DRIVER +M: Herve Codina +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/gehc,pmc-adc.yaml +F: drivers/iio/adc/gehc-pmc-adc.c +F: include/dt-bindings/iio/adc/gehc,pmc-adc.h + GEMINI CRYPTO DRIVER M: Corentin Labbe L: linux-crypto@vger.kernel.org @@ -9718,6 +9769,11 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/gpd-pocket-fan.c +GPIB DRIVERS +M: Dave Penkler +S: Maintained +F: drivers/staging/gpib/ + GPIO ACPI SUPPORT M: Mika Westerberg M: Andy Shevchenko @@ -10702,6 +10758,14 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml F: drivers/i2c/busses/i2c-mv64xxx.c +I2C OF COMPONENT PROBER +M: Chen-Yu Tsai +L: linux-i2c@vger.kernel.org +L: devicetree@vger.kernel.org +S: Maintained +F: drivers/i2c/i2c-core-of-prober.c +F: include/linux/i2c-of-prober.h + I2C OVER PARALLEL PORT M: Jean Delvare L: linux-i2c@vger.kernel.org @@ -11641,7 +11705,7 @@ F: drivers/usb/misc/usb-ljca.c F: include/linux/usb/ljca.h INTEL MANAGEMENT ENGINE (mei) -M: Tomas Winkler +M: Alexander Usyskin L: linux-kernel@vger.kernel.org S: Supported F: Documentation/driver-api/mei/* @@ -11922,7 +11986,7 @@ F: Documentation/devicetree/bindings/media/i2c/isil,isl79987.yaml F: drivers/media/i2c/isl7998x.c INVENSENSE ICM-426xx IMU DRIVER -M: Jean-Baptiste Maneyrol +M: Jean-Baptiste Maneyrol L: linux-iio@vger.kernel.org S: Maintained W: https://invensense.tdk.com/ @@ -11937,6 +12001,14 @@ S: Maintained F: Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.yaml F: drivers/iio/gyro/mpu3050* +INVENSENSE MPU-6050 IMU DRIVER +M: Jean-Baptiste Maneyrol +L: linux-iio@vger.kernel.org +S: Maintained +W: https://invensense.tdk.com/ +F: Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml +F: drivers/iio/imu/inv_mpu6050/ + IOC3 ETHERNET DRIVER M: Thomas Bogendoerfer L: linux-mips@vger.kernel.org @@ -12403,7 +12475,7 @@ F: mm/usercopy.c F: security/Kconfig.hardening K: \b(add|choose)_random_kstack_offset\b K: \b__check_(object_size|heap_object)\b -K: \b__counted_by\b +K: \b__counted_by(_le|_be)?\b KERNEL JANITORS L: kernel-janitors@vger.kernel.org @@ -12436,6 +12508,7 @@ F: include/trace/misc/sunrpc.h F: include/uapi/linux/nfsd/ F: include/uapi/linux/sunrpc/ F: net/sunrpc/ +F: tools/net/sunrpc/ KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD) M: Thomas Weißschuh @@ -13407,12 +13480,12 @@ S: Maintained F: Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml F: drivers/gpio/gpio-loongson-64bit.c -LOONGSON LS2X APB DMA DRIVER +LOONGSON-2 APB DMA DRIVER M: Binbin Zhou L: dmaengine@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml -F: drivers/dma/ls2x-apb-dma.c +F: drivers/dma/loongson2-apb-dma.c LOONGSON LS2X I2C DRIVER M: Binbin Zhou @@ -13744,6 +13817,7 @@ F: Documentation/devicetree/bindings/mfd/marvell,88pm886-a1.yaml F: drivers/input/misc/88pm886-onkey.c F: drivers/mfd/88pm886.c F: drivers/regulator/88pm886-regulator.c +F: drivers/rtc/rtc-88pm886.c F: include/linux/mfd/88pm886.h MARVELL ARMADA 3700 PHY DRIVERS @@ -13926,6 +14000,12 @@ R: schalla@marvell.com R: vattunuru@marvell.com F: drivers/vdpa/octeon_ep/ +MARVELL OCTEON HOTPLUG DRIVER +R: Shijith Thotton +R: Vamsi Attunuru +S: Supported +F: drivers/pci/hotplug/octep_hp.c + MATROX FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org S: Orphan @@ -15585,6 +15665,15 @@ F: arch/arm/boot/dts/marvell/armada-xp-crs326-24g-2s.dts F: arch/arm/boot/dts/marvell/armada-xp-crs328-4c-20s-4s-bit.dts F: arch/arm/boot/dts/marvell/armada-xp-crs328-4c-20s-4s.dts +MIN HEAP +M: Kuan-Wei Chiu +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/core-api/min_heap.rst +F: include/linux/min_heap.h +F: lib/min_heap.c +F: lib/test_min_heap.c + MIPI CCS, SMIA AND SMIA++ IMAGE SENSOR DRIVER M: Sakari Ailus L: linux-media@vger.kernel.org @@ -16288,6 +16377,7 @@ X: include/net/wext.h X: net/9p/ X: net/bluetooth/ X: net/can/ +X: net/ceph/ X: net/mac80211/ X: net/rfkill/ X: net/wireless/ @@ -16570,6 +16660,7 @@ NOVATEK NVT-TS I2C TOUCHSCREEN DRIVER M: Hans de Goede L: linux-input@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/input/touchscreen/novatek,nvt-ts.yaml F: drivers/input/touchscreen/novatek-nvt-ts.c NSDEPS @@ -17983,8 +18074,8 @@ M: Bartosz Golaszewski L: linux-pci@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git -F: drivers/pci/pwrctl/* -F: include/linux/pci-pwrctl.h +F: drivers/pci/pwrctrl/* +F: include/linux/pci-pwrctrl.h PCI SUBSYSTEM M: Bjorn Helgaas @@ -18006,6 +18097,15 @@ F: include/linux/of_pci.h F: include/linux/pci* F: include/uapi/linux/pci* +PCIE BANDWIDTH CONTROLLER +M: Ilpo Järvinen +L: linux-pci@vger.kernel.org +S: Supported +F: drivers/pci/pcie/bwctrl.c +F: drivers/thermal/pcie_cooling.c +F: include/linux/pci-bwctrl.h +F: tools/testing/selftests/pcie_bwctrl/ + PCIE DRIVER FOR AMAZON ANNAPURNA LABS M: Jonathan Chocron L: linux-pci@vger.kernel.org @@ -18305,6 +18405,13 @@ F: drivers/pinctrl/ F: include/dt-bindings/pinctrl/ F: include/linux/pinctrl/ +PIN CONTROLLER - AIROHA +M: Lorenzo Bianconi +L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml +F: drivers/pinctrl/mediatek/pinctrl-airoha.c + PIN CONTROLLER - AMD M: Basavaraj Natikar M: Shyam Sundar S K @@ -18616,6 +18723,13 @@ S: Maintained F: include/linux/psi* F: kernel/sched/psi.c +PROPELLER BUILD +M: Rong Xu +M: Han Shen +S: Supported +F: Documentation/dev-tools/propeller.rst +F: scripts/Makefile.propeller + PRINTK M: Petr Mladek R: Steven Rostedt @@ -19832,6 +19946,14 @@ S: Supported F: Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml F: drivers/counter/rz-mtu3-cnt.c +RENESAS RTCA-3 RTC DRIVER +M: Claudiu Beznea +L: linux-rtc@vger.kernel.org +L: linux-renesas-soc@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml +F: drivers/rtc/rtc-renesas-rtca3.c + RENESAS RZ/N1 A5PSW SWITCH DRIVER M: Clément Léger L: linux-renesas-soc@vger.kernel.org @@ -20062,10 +20184,14 @@ L: linux-riscv@lists.infradead.org S: Maintained T: git https://github.com/pdp7/linux.git F: Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml +F: Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml F: Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml +F: Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml F: arch/riscv/boot/dts/thead/ F: drivers/clk/thead/clk-th1520-ap.c +F: drivers/mailbox/mailbox-th1520.c F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c +F: drivers/pinctrl/pinctrl-th1520.c F: include/dt-bindings/clock/thead,th1520-clk-ap.h RNBD BLOCK DRIVERS @@ -20346,13 +20472,22 @@ B: https://github.com/Rust-for-Linux/linux/issues C: zulip://rust-for-linux.zulipchat.com P: https://rust-for-linux.com/contributing T: git https://github.com/Rust-for-Linux/linux.git rust-next +F: .clippy.toml F: Documentation/rust/ +F: include/trace/events/rust_sample.h F: rust/ F: samples/rust/ F: scripts/*rust* F: tools/testing/selftests/rust/ K: \b(?i:rust)\b +RUST [ALLOC] +M: Danilo Krummrich +L: rust-for-linux@vger.kernel.org +S: Maintained +F: rust/kernel/alloc.rs +F: rust/kernel/alloc/ + RXRPC SOCKETS (AF_RXRPC) M: David Howells M: Marc Dionne @@ -22089,17 +22224,6 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/staging/media/atomisp/ -STAGING - FIELDBUS SUBSYSTEM -M: Sven Van Asbroeck -S: Maintained -F: drivers/staging/fieldbus/* -F: drivers/staging/fieldbus/Documentation/ - -STAGING - HMS ANYBUS-S BUS -M: Sven Van Asbroeck -S: Maintained -F: drivers/staging/fieldbus/anybuss/ - STAGING - INDUSTRIAL IO M: Jonathan Cameron L: linux-iio@vger.kernel.org @@ -22114,18 +22238,6 @@ L: linux-tegra@vger.kernel.org S: Maintained F: drivers/staging/nvec/ -STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON) -M: Jens Frederich -M: Jon Nettleton -S: Maintained -W: http://wiki.laptop.org/go/DCON -F: drivers/staging/olpc_dcon/ - -STAGING - REALTEK RTL8712U DRIVERS -M: Florian Schilhabel . -S: Odd Fixes -F: drivers/staging/rtl8712/ - STAGING - SEPS525 LCD CONTROLLER DRIVERS M: Michael Hennerich L: linux-fbdev@vger.kernel.org @@ -22141,11 +22253,6 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/staging/sm750fb/ -STAGING - VIA VT665X DRIVERS -M: Philipp Hortmann -S: Odd Fixes -F: drivers/staging/vt665?/ - STAGING SUBSYSTEM M: Greg Kroah-Hartman L: linux-staging@lists.linux.dev @@ -22382,6 +22489,12 @@ F: drivers/*/stm32-*timer* F: drivers/pwm/pwm-stm32* F: include/linux/*/stm32-*tim* +STM32MP25 USB3/PCIE COMBOPHY DRIVER +M: Christian Bruel +S: Maintained +F: Documentation/devicetree/bindings/phy/st,stm32mp25-combophy.yaml +F: drivers/phy/st/phy-stm32-combophy.c + STMMAC ETHERNET DRIVER M: Alexandre Torgue M: Jose Abreu @@ -23971,7 +24084,9 @@ F: drivers/ufs/host/*dwc* UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER EXYNOS HOOKS M: Alim Akhtar +R: Peter Griffin L: linux-scsi@vger.kernel.org +L: linux-samsung-soc@vger.kernel.org S: Maintained F: drivers/ufs/host/ufs-exynos* @@ -24298,6 +24413,13 @@ L: linux-usb@vger.kernel.org S: Orphan F: drivers/usb/typec/tcpm/ +USB TYPEC TUSB1046 MUX DRIVER +M: Romain Gantois +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/usb/ti,tusb1046.yaml +F: drivers/usb/typec/mux/tusb1046.c + USB UHCI DRIVER M: Alan Stern L: linux-usb@vger.kernel.org @@ -24851,6 +24973,18 @@ S: Maintained F: drivers/input/serio/userio.c F: include/uapi/linux/userio.h +VISHAY VEML3235 AMBIENT LIGHT SENSOR DRIVER +M: Javier Carrasco +S: Maintained +F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml +F: drivers/iio/light/veml3235.c + +VISHAY VEML6030 AMBIENT LIGHT SENSOR DRIVER +M: Javier Carrasco +S: Maintained +F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml +F: drivers/iio/light/veml6030.c + VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER M: Javier Carrasco S: Maintained diff --git a/Makefile b/Makefile index 68a8faff25432..93ab62cef244f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 -PATCHLEVEL = 12 +PATCHLEVEL = 13 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc1 NAME = Baby Opossum Posse # *DOCUMENTATION* @@ -40,7 +40,7 @@ __all: this-makefile := $(lastword $(MAKEFILE_LIST)) abs_srctree := $(realpath $(dir $(this-makefile))) -abs_objtree := $(CURDIR) +abs_output := $(CURDIR) ifneq ($(sub_make_done),1) @@ -134,6 +134,10 @@ ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif +ifeq ("$(origin MO)", "command line") + KBUILD_EXTMOD_OUTPUT := $(MO) +endif + $(if $(word 2, $(KBUILD_EXTMOD)), \ $(error building multiple external modules is not supported)) @@ -176,18 +180,41 @@ export KBUILD_EXTRA_WARN # The O= assignment takes precedence over the KBUILD_OUTPUT environment # variable. -# Do we want to change the working directory? ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif -ifneq ($(KBUILD_OUTPUT),) +ifdef KBUILD_EXTMOD + ifdef KBUILD_OUTPUT + objtree := $(realpath $(KBUILD_OUTPUT)) + $(if $(objtree),,$(error specified kernel directory "$(KBUILD_OUTPUT)" does not exist)) + else + objtree := $(abs_srctree) + endif + # If Make is invoked from the kernel directory (either kernel + # source directory or kernel build directory), external modules + # are built in $(KBUILD_EXTMOD) for backward compatibility, + # otherwise, built in the current directory. + output := $(or $(KBUILD_EXTMOD_OUTPUT),$(if $(filter $(CURDIR),$(objtree) $(abs_srctree)),$(KBUILD_EXTMOD))) + # KBUILD_EXTMOD might be a relative path. Remember its absolute path before + # Make changes the working directory. + srcroot := $(realpath $(KBUILD_EXTMOD)) + $(if $(srcroot),,$(error specified external module directory "$(KBUILD_EXTMOD)" does not exist)) +else + objtree := . + output := $(KBUILD_OUTPUT) +endif + +export objtree srcroot + +# Do we want to change the working directory? +ifneq ($(output),) # $(realpath ...) gets empty if the path does not exist. Run 'mkdir -p' first. -$(shell mkdir -p "$(KBUILD_OUTPUT)") +$(shell mkdir -p "$(output)") # $(realpath ...) resolves symlinks -abs_objtree := $(realpath $(KBUILD_OUTPUT)) -$(if $(abs_objtree),,$(error failed to create output directory "$(KBUILD_OUTPUT)")) -endif # ifneq ($(KBUILD_OUTPUT),) +abs_output := $(realpath $(output)) +$(if $(abs_output),,$(error failed to create output directory "$(output)")) +endif ifneq ($(words $(subst :, ,$(abs_srctree))), 1) $(error source directory cannot contain spaces or colons) @@ -197,7 +224,7 @@ export sub_make_done := 1 endif # sub_make_done -ifeq ($(abs_objtree),$(CURDIR)) +ifeq ($(abs_output),$(CURDIR)) # Suppress "Entering directory ..." if we are at the final work directory. no-print-directory := --no-print-directory else @@ -221,43 +248,41 @@ $(filter-out $(this-makefile), $(MAKECMDGOALS)) __all: __sub-make # Invoke a second make in the output directory, passing relevant variables __sub-make: - $(Q)$(MAKE) $(no-print-directory) -C $(abs_objtree) \ + $(Q)$(MAKE) $(no-print-directory) -C $(abs_output) \ -f $(abs_srctree)/Makefile $(MAKECMDGOALS) else # need-sub-make # We process the rest of the Makefile if this is the final invocation of make -ifeq ($(abs_srctree),$(abs_objtree)) - # building in the source tree - srctree := . - building_out_of_srctree := -else - ifeq ($(abs_srctree)/,$(dir $(abs_objtree))) - # building in a subdirectory of the source tree - srctree := .. - else - srctree := $(abs_srctree) - endif - building_out_of_srctree := 1 +ifndef KBUILD_EXTMOD +srcroot := $(abs_srctree) endif -ifneq ($(KBUILD_ABS_SRCTREE),) -srctree := $(abs_srctree) +ifeq ($(srcroot),$(CURDIR)) +building_out_of_srctree := +else +export building_out_of_srctree := 1 endif -objtree := . +ifdef KBUILD_ABS_SRCTREE + # Do nothing. Use the absolute path. +else ifeq ($(srcroot),$(CURDIR)) + # Building in the source. + srcroot := . +else ifeq ($(srcroot)/,$(dir $(CURDIR))) + # Building in a subdirectory of the source. + srcroot := .. +endif -VPATH := +export srctree := $(if $(KBUILD_EXTMOD),$(abs_srctree),$(srcroot)) -ifeq ($(KBUILD_EXTMOD),) ifdef building_out_of_srctree -VPATH := $(srctree) -endif +export VPATH := $(srcroot) +else +VPATH := endif -export building_out_of_srctree srctree objtree VPATH - # To make sure we do not include .config for any of the *config targets # catch them early, and hand them over to scripts/kconfig/Makefile # It is allowed to specify more targets when calling make, including @@ -276,7 +301,7 @@ no-dot-config-targets := $(clean-targets) \ outputmakefile rustavailable rustfmt rustfmtcheck no-sync-config-targets := $(no-dot-config-targets) %install modules_sign kernelrelease \ image_name -single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %.symtypes %/ +single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %/ config-build := mixed-build := @@ -354,7 +379,7 @@ else # !mixed-build include $(srctree)/scripts/Kbuild.include # Read KERNELRELEASE from include/config/kernel.release (if it exists) -KERNELRELEASE = $(call read-file, include/config/kernel.release) +KERNELRELEASE = $(call read-file, $(objtree)/include/config/kernel.release) KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION @@ -446,18 +471,23 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS) export rust_common_flags := --edition=2021 \ -Zbinary_dep_depinfo=y \ -Astable_features \ - -Dunsafe_op_in_unsafe_fn \ -Dnon_ascii_idents \ + -Dunsafe_op_in_unsafe_fn \ + -Wmissing_docs \ -Wrust_2018_idioms \ -Wunreachable_pub \ - -Wmissing_docs \ - -Wrustdoc::missing_crate_level_docs \ -Wclippy::all \ + -Wclippy::ignored_unit_patterns \ -Wclippy::mut_mut \ -Wclippy::needless_bitwise_bool \ -Wclippy::needless_continue \ + -Aclippy::needless_lifetimes \ -Wclippy::no_mangle_with_rust_abi \ - -Wclippy::dbg_macro + -Wclippy::undocumented_unsafe_blocks \ + -Wclippy::unnecessary_safety_comment \ + -Wclippy::unnecessary_safety_doc \ + -Wrustdoc::missing_crate_level_docs \ + -Wrustdoc::unescaped_backticks KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) \ $(HOSTCFLAGS) -I $(srctree)/scripts/include @@ -508,7 +538,7 @@ KGZIP = gzip KBZIP2 = bzip2 KLZOP = lzop LZMA = lzma -LZ4 = lz4c +LZ4 = lz4 XZ = xz ZSTD = zstd @@ -538,7 +568,7 @@ USERINCLUDE := \ LINUXINCLUDE := \ -I$(srctree)/arch/$(SRCARCH)/include \ -I$(objtree)/arch/$(SRCARCH)/include/generated \ - $(if $(building_out_of_srctree),-I$(srctree)/include) \ + -I$(srctree)/include \ -I$(objtree)/include \ $(USERINCLUDE) @@ -582,6 +612,9 @@ endif # Allows the usage of unstable features in stable compilers. export RUSTC_BOOTSTRAP := 1 +# Allows finding `.clippy.toml` in out-of-srctree builds. +export CLIPPY_CONF_DIR := $(srctree) + export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN export HOSTRUSTC KBUILD_HOSTRUSTFLAGS @@ -621,13 +654,25 @@ ifdef building_out_of_srctree # At the same time when output Makefile generated, generate .gitignore to # ignore whole output directory +ifdef KBUILD_EXTMOD +print_env_for_makefile = \ + echo "export KBUILD_OUTPUT = $(objtree)"; \ + echo "export KBUILD_EXTMOD = $(realpath $(srcroot))" ; \ + echo "export KBUILD_EXTMOD_OUTPUT = $(CURDIR)" +else +print_env_for_makefile = \ + echo "export KBUILD_OUTPUT = $(CURDIR)" +endif + quiet_cmd_makefile = GEN Makefile cmd_makefile = { \ - echo "\# Automatically generated by $(srctree)/Makefile: don't edit"; \ - echo "include $(srctree)/Makefile"; \ + echo "\# Automatically generated by $(abs_srctree)/Makefile: don't edit"; \ + $(print_env_for_makefile); \ + echo "include $(abs_srctree)/Makefile"; \ } > Makefile outputmakefile: +ifeq ($(KBUILD_EXTMOD),) @if [ -f $(srctree)/.config -o \ -d $(srctree)/include/config -o \ -d $(srctree)/arch/$(SRCARCH)/include/generated ]; then \ @@ -637,7 +682,16 @@ outputmakefile: echo >&2 "***"; \ false; \ fi - $(Q)ln -fsn $(srctree) source +else + @if [ -f $(srcroot)/modules.order ]; then \ + echo >&2 "***"; \ + echo >&2 "*** The external module source tree is not clean."; \ + echo >&2 "*** Please run 'make -C $(abs_srctree) M=$(realpath $(srcroot)) clean'"; \ + echo >&2 "***"; \ + false; \ + fi +endif + $(Q)ln -fsn $(srcroot) source $(call cmd,makefile) $(Q)test -e .gitignore || \ { echo "# this is build directory, ignore it"; echo "*"; } > .gitignore @@ -709,7 +763,7 @@ endif # in addition to whatever we do anyway. # Just "make" or "make all" shall build modules as well -ifneq ($(filter all modules nsdeps %compile_commands.json clang-%,$(MAKECMDGOALS)),) +ifneq ($(filter all modules nsdeps compile_commands.json clang-%,$(MAKECMDGOALS)),) KBUILD_MODULES := 1 endif @@ -720,7 +774,7 @@ endif export KBUILD_MODULES KBUILD_BUILTIN ifdef need-config -include include/config/auto.conf +include $(objtree)/include/config/auto.conf endif ifeq ($(KBUILD_EXTMOD),) @@ -781,17 +835,22 @@ $(KCONFIG_CONFIG): else # !may-sync-config # External modules and some install targets need include/generated/autoconf.h # and include/config/auto.conf but do not care if they are up-to-date. -# Use auto.conf to trigger the test -PHONY += include/config/auto.conf - -include/config/auto.conf: - @test -e include/generated/autoconf.h -a -e $@ || ( \ - echo >&2; \ - echo >&2 " ERROR: Kernel configuration is invalid."; \ - echo >&2 " include/generated/autoconf.h or $@ are missing.";\ - echo >&2 " Run 'make oldconfig && make prepare' on kernel src to fix it."; \ - echo >&2 ; \ - /bin/false) +# Use auto.conf to show the error message + +checked-configs := $(addprefix $(objtree)/, include/generated/autoconf.h include/generated/rustc_cfg include/config/auto.conf) +missing-configs := $(filter-out $(wildcard $(checked-configs)), $(checked-configs)) + +ifdef missing-configs +PHONY += $(objtree)/include/config/auto.conf + +$(objtree)/include/config/auto.conf: + @echo >&2 '***' + @echo >&2 '*** ERROR: Kernel configuration is invalid. The following files are missing:' + @printf >&2 '*** - %s\n' $(missing-configs) + @echo >&2 '*** Run "make oldconfig && make prepare" on kernel source to fix it.' + @echo >&2 '***' + @/bin/false +endif endif # may-sync-config endif # need-config @@ -1005,8 +1064,10 @@ ifdef CONFIG_CC_IS_GCC KBUILD_CFLAGS += -fconserve-stack endif -# change __FILE__ to the relative path from the srctree -KBUILD_CPPFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=) +# change __FILE__ to the relative path to the source directory +ifdef building_out_of_srctree +KBUILD_CPPFLAGS += $(call cc-option,-fmacro-prefix-map=$(srcroot)/=) +endif # include additional Makefiles when needed include-y := scripts/Makefile.extrawarn @@ -1018,6 +1079,8 @@ include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan include-$(CONFIG_KCOV) += scripts/Makefile.kcov include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct +include-$(CONFIG_AUTOFDO_CLANG) += scripts/Makefile.autofdo +include-$(CONFIG_PROPELLER_CLANG) += scripts/Makefile.propeller include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins include $(addprefix $(srctree)/, $(include-y)) @@ -1098,10 +1161,6 @@ export MODLIB PHONY += prepare0 -export extmod_prefix = $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD)/) -export MODORDER := $(extmod_prefix)modules.order -export MODULES_NSDEPS := $(extmod_prefix)modules.nsdeps - ifeq ($(KBUILD_EXTMOD),) build-dir := . @@ -1196,7 +1255,8 @@ PHONY += prepare archprepare archprepare: outputmakefile archheaders archscripts scripts include/config/kernel.release \ asm-generic $(version_h) include/generated/utsrelease.h \ - include/generated/compile.h include/generated/autoconf.h remove-stale-files + include/generated/compile.h include/generated/autoconf.h \ + include/generated/rustc_cfg remove-stale-files prepare0: archprepare $(Q)$(MAKE) $(build)=scripts/mod @@ -1427,6 +1487,10 @@ ifdef CONFIG_OF_EARLY_FLATTREE all: dtbs endif +ifdef CONFIG_GENERIC_BUILTIN_DTB +vmlinux: dtbs +endif + endif PHONY += scripts_dtc @@ -1494,7 +1558,8 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ modules.builtin modules.builtin.modinfo modules.nsdeps \ modules.builtin.ranges vmlinux.o.map \ compile_commands.json rust/test \ - rust-project.json .vmlinux.objs .vmlinux.export.c + rust-project.json .vmlinux.objs .vmlinux.export.c \ + .builtin-dtbs-list .builtin-dtb.S # Directories & files removed with 'make mrproper' MRPROPER_FILES += include/config include/generated \ @@ -1602,7 +1667,6 @@ help: @echo ' with a stack size larger than MINSTACKSIZE (default: 100)' @echo ' versioncheck - Sanity check on version.h usage' @echo ' includecheck - Check for duplicate included header files' - @echo ' export_report - List the usages of all exported symbols' @echo ' headerdep - Detect inclusion cycles in headers' @echo ' coccicheck - Check with Coccinelle' @echo ' clang-analyzer - Check with clang static analyzer' @@ -1743,18 +1807,9 @@ rusttest: prepare # Formatting targets PHONY += rustfmt rustfmtcheck -# We skip `rust/alloc` since we want to minimize the diff w.r.t. upstream. -# -# We match using absolute paths since `find` does not resolve them -# when matching, which is a problem when e.g. `srctree` is `..`. -# We `grep` afterwards in order to remove the directory entry itself. rustfmt: - $(Q)find $(abs_srctree) -type f -name '*.rs' \ - -o -path $(abs_srctree)/rust/alloc -prune \ - -o -path $(abs_objtree)/rust/test -prune \ - | grep -Fv $(abs_srctree)/rust/alloc \ - | grep -Fv $(abs_objtree)/rust/test \ - | grep -Fv generated \ + $(Q)find $(srctree) $(RCS_FIND_IGNORE) \ + -type f -a -name '*.rs' -a ! -name '*generated*' -print \ | xargs $(RUSTFMT) $(rustfmt_flags) rustfmtcheck: rustfmt_flags = --check @@ -1793,14 +1848,10 @@ filechk_kernel.release = echo $(KERNELRELEASE) KBUILD_BUILTIN := KBUILD_MODULES := 1 -build-dir := $(KBUILD_EXTMOD) +build-dir := . -compile_commands.json: $(extmod_prefix)compile_commands.json -PHONY += compile_commands.json - -clean-dirs := $(KBUILD_EXTMOD) -clean: private rm-files := $(KBUILD_EXTMOD)/Module.symvers $(KBUILD_EXTMOD)/modules.nsdeps \ - $(KBUILD_EXTMOD)/compile_commands.json +clean-dirs := . +clean: private rm-files := Module.symvers modules.nsdeps compile_commands.json PHONY += prepare # now expand this into a simple variable to reduce the cost of shell evaluations @@ -1859,7 +1910,7 @@ endif ifdef CONFIG_MODULES -$(MODORDER): $(build-dir) +modules.order: $(build-dir) @: # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules. @@ -1870,7 +1921,7 @@ ifneq ($(KBUILD_MODPOST_NOFINAL),1) endif PHONY += modules_check -modules_check: $(MODORDER) +modules_check: modules.order $(Q)$(CONFIG_SHELL) $(srctree)/scripts/modules-check.sh $< else # CONFIG_MODULES @@ -1911,15 +1962,15 @@ $(single-ko): single_modules $(single-no-ko): $(build-dir) @: -# Remove MODORDER when done because it is not the real one. +# Remove modules.order when done because it is not the real one. PHONY += single_modules single_modules: $(single-no-ko) modules_prepare - $(Q){ $(foreach m, $(single-ko), echo $(extmod_prefix)$(m:%.ko=%.o);) } > $(MODORDER) + $(Q){ $(foreach m, $(single-ko), echo $(m:%.ko=%.o);) } > modules.order $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost ifneq ($(KBUILD_MODPOST_NOFINAL),1) $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal endif - $(Q)rm -f $(MODORDER) + $(Q)rm -f modules.order single-goals := $(addprefix $(build-dir)/, $(single-no-ko)) @@ -1927,6 +1978,8 @@ KBUILD_MODULES := 1 endif +prepare: outputmakefile + # Preset locale variables to speed up the build process. Limit locale # tweaks to this spot to avoid wrong language settings when running # make menuconfig etc. @@ -1942,7 +1995,7 @@ $(clean-dirs): clean: $(clean-dirs) $(call cmd,rmfiles) - @find $(or $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ + @find . $(RCS_FIND_IGNORE) \ \( -name '*.[aios]' -o -name '*.rsi' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '*.ko.*' \ -o -name '*.dtb' -o -name '*.dtbo' \ @@ -1975,7 +2028,12 @@ tags TAGS cscope gtags: FORCE PHONY += rust-analyzer rust-analyzer: +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh +ifdef KBUILD_EXTMOD +# FIXME: external modules must not descend into a sub-directory of the kernel + $(Q)$(MAKE) $(build)=$(objtree)/rust src=$(srctree)/rust $@ +else $(Q)$(MAKE) $(build)=rust $@ +endif # Script to generate missing namespace dependencies # --------------------------------------------------------------------------- @@ -1991,12 +2049,12 @@ nsdeps: modules quiet_cmd_gen_compile_commands = GEN $@ cmd_gen_compile_commands = $(PYTHON3) $< -a $(AR) -o $@ $(filter-out $<, $(real-prereqs)) -$(extmod_prefix)compile_commands.json: $(srctree)/scripts/clang-tools/gen_compile_commands.py \ +compile_commands.json: $(srctree)/scripts/clang-tools/gen_compile_commands.py \ $(if $(KBUILD_EXTMOD),, vmlinux.a $(KBUILD_VMLINUX_LIBS)) \ - $(if $(CONFIG_MODULES), $(MODORDER)) FORCE + $(if $(CONFIG_MODULES), modules.order) FORCE $(call if_changed,gen_compile_commands) -targets += $(extmod_prefix)compile_commands.json +targets += compile_commands.json PHONY += clang-tidy clang-analyzer @@ -2004,7 +2062,7 @@ ifdef CONFIG_CC_IS_CLANG quiet_cmd_clang_tools = CHECK $< cmd_clang_tools = $(PYTHON3) $(srctree)/scripts/clang-tools/run-clang-tools.py $@ $< -clang-tidy clang-analyzer: $(extmod_prefix)compile_commands.json +clang-tidy clang-analyzer: compile_commands.json $(call cmd,clang_tools) else clang-tidy clang-analyzer: @@ -2015,7 +2073,7 @@ endif # Scripts to check various things for consistency # --------------------------------------------------------------------------- -PHONY += includecheck versioncheck coccicheck export_report +PHONY += includecheck versioncheck coccicheck includecheck: find $(srctree)/* $(RCS_FIND_IGNORE) \ @@ -2030,9 +2088,6 @@ versioncheck: coccicheck: $(Q)$(BASH) $(srctree)/scripts/$@ -export_report: - $(PERL) $(srctree)/scripts/export_report.pl - PHONY += checkstack kernelrelease kernelversion image_name # UML needs a little special treatment here. It wants to use the host diff --git a/arch/Kconfig b/arch/Kconfig index 832f68af7c779..6682b2a53e342 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -812,6 +812,45 @@ config LTO_CLANG_THIN If unsure, say Y. endchoice +config ARCH_SUPPORTS_AUTOFDO_CLANG + bool + +config AUTOFDO_CLANG + bool "Enable Clang's AutoFDO build (EXPERIMENTAL)" + depends on ARCH_SUPPORTS_AUTOFDO_CLANG + depends on CC_IS_CLANG && CLANG_VERSION >= 170000 + help + This option enables Clang’s AutoFDO build. When + an AutoFDO profile is specified in variable + CLANG_AUTOFDO_PROFILE during the build process, + Clang uses the profile to optimize the kernel. + + If no profile is specified, AutoFDO options are + still passed to Clang to facilitate the collection + of perf data for creating an AutoFDO profile in + subsequent builds. + + If unsure, say N. + +config ARCH_SUPPORTS_PROPELLER_CLANG + bool + +config PROPELLER_CLANG + bool "Enable Clang's Propeller build" + depends on ARCH_SUPPORTS_PROPELLER_CLANG + depends on CC_IS_CLANG && CLANG_VERSION >= 190000 + help + This option enables Clang’s Propeller build. When the Propeller + profiles is specified in variable CLANG_PROPELLER_PROFILE_PREFIX + during the build process, Clang uses the profiles to optimize + the kernel. + + If no profile is specified, Propeller options are still passed + to Clang to facilitate the collection of perf data for creating + the Propeller profiles in subsequent builds. + + If unsure, say N. + config ARCH_SUPPORTS_CFI_CLANG bool help diff --git a/arch/alpha/include/asm/spinlock_types.h b/arch/alpha/include/asm/spinlock_types.h index 2526fd3be5fd7..05a444d77c531 100644 --- a/arch/alpha/include/asm/spinlock_types.h +++ b/arch/alpha/include/asm/spinlock_types.h @@ -3,7 +3,7 @@ #define _ALPHA_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif typedef struct { diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 5808a66e2a81f..3048758304b57 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -64,7 +64,7 @@ static int __pci_mmap_fits(struct pci_dev *pdev, int num, * Return: %0 on success, negative error code otherwise */ static int pci_mmap_resource(struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma, int sparse) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); @@ -93,14 +93,14 @@ static int pci_mmap_resource(struct kobject *kobj, } static int pci_mmap_resource_sparse(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 1); } static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 0); diff --git a/arch/arm/Makefile b/arch/arm/Makefile index aafebf145738a..00ca7886b18ef 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -264,13 +264,13 @@ stack_protector_prepare: prepare0 -mstack-protector-guard=tls \ -mstack-protector-guard-offset=$(shell \ awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}'\ - include/generated/asm-offsets.h)) + $(objtree)/include/generated/asm-offsets.h)) else stack_protector_prepare: prepare0 $(eval SSP_PLUGIN_CFLAGS := \ -fplugin-arg-arm_ssp_per_task_plugin-offset=$(shell \ awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}'\ - include/generated/asm-offsets.h)) + $(objtree)/include/generated/asm-offsets.h)) $(eval KBUILD_CFLAGS += $(SSP_PLUGIN_CFLAGS)) $(eval GCC_PLUGINS_CFLAGS += $(SSP_PLUGIN_CFLAGS)) endif diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h index e4eb54f6cd9fe..a35aba7f548cc 100644 --- a/arch/arm/include/asm/jump_label.h +++ b/arch/arm/include/asm/jump_label.h @@ -9,13 +9,17 @@ #define JUMP_LABEL_NOP_SIZE 4 +/* This macro is also expanded on the Rust side. */ +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1:\n\t" \ + WASM(nop) "\n\t" \ + ".pushsection __jump_table, \"aw\"\n\t" \ + ".word 1b, " label ", " key "\n\t" \ + ".popsection\n\t" \ + static __always_inline bool arch_static_branch(struct static_key *key, bool branch) { - asm goto("1:\n\t" - WASM(nop) "\n\t" - ".pushsection __jump_table, \"aw\"\n\t" - ".word 1b, %l[l_yes], %c0\n\t" - ".popsection\n\t" + asm goto(ARCH_STATIC_BRANCH_ASM("%c0", "%l[l_yes]") : : "i" (&((char *)key)[branch]) : : l_yes); return false; diff --git a/arch/arm/include/asm/spinlock_types.h b/arch/arm/include/asm/spinlock_types.h index 0c14b36ef1013..5404a2a96bf3c 100644 --- a/arch/arm/include/asm/spinlock_types.h +++ b/arch/arm/include/asm/spinlock_types.h @@ -3,7 +3,7 @@ #define __ASM_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif #define TICKET_SHIFT 16 diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 1dfae1af8e31b..ef6a657c8d130 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -25,6 +25,7 @@ #include #include #include +#include #include "entry-header.S" #include @@ -561,6 +562,13 @@ ENTRY(__switch_to) @ entries covering the vmalloc region. @ ldr r2, [ip] +#ifdef CONFIG_KASAN_VMALLOC + @ Also dummy read from the KASAN shadow memory for the new stack if we + @ are using KASAN + mov_l r2, KASAN_SHADOW_OFFSET + add r2, r2, ip, lsr #KASAN_SHADOW_SCALE_SHIFT + ldr r2, [r2] +#endif #endif @ When CONFIG_THREAD_INFO_IN_TASK=n, the update of SP itself is what diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index ab767f0599295..e4fe059cd8614 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -6,7 +6,6 @@ menuconfig ARCH_MXC select CLKSRC_IMX_GPT select GENERIC_IRQ_CHIP select GPIOLIB - select PINCTRL select PM_OPP if PM select SOC_BUS select SRAM @@ -49,7 +48,6 @@ config SOC_IMX31 config SOC_IMX35 bool "i.MX35 support" select MXC_AVIC - select PINCTRL_IMX35 help This enables support for Freescale i.MX35 processor @@ -61,7 +59,6 @@ config SOC_IMX1 bool "i.MX1 support" select CPU_ARM920T select MXC_AVIC - select PINCTRL_IMX1 help This enables support for Freescale i.MX1 processor @@ -73,7 +70,6 @@ config SOC_IMX25 bool "i.MX25 support" select CPU_ARM926T select MXC_AVIC - select PINCTRL_IMX25 help This enables support for Freescale i.MX25 processor @@ -81,7 +77,6 @@ config SOC_IMX27 bool "i.MX27 support" select CPU_ARM926T select MXC_AVIC - select PINCTRL_IMX27 help This enables support for Freescale i.MX27 processor @@ -98,7 +93,6 @@ config SOC_IMX5 config SOC_IMX50 bool "i.MX50 support" - select PINCTRL_IMX50 select SOC_IMX5 help @@ -106,14 +100,12 @@ config SOC_IMX50 config SOC_IMX51 bool "i.MX51 support" - select PINCTRL_IMX51 select SOC_IMX5 help This enables support for Freescale i.MX51 processor config SOC_IMX53 bool "i.MX53 support" - select PINCTRL_IMX53 select SOC_IMX5 help @@ -137,7 +129,6 @@ config SOC_IMX6Q select ARM_ERRATA_775420 select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD - select PINCTRL_IMX6Q select SOC_IMX6 help @@ -147,7 +138,6 @@ config SOC_IMX6SL bool "i.MX6 SoloLite support" select ARM_ERRATA_754322 select ARM_ERRATA_775420 - select PINCTRL_IMX6SL select SOC_IMX6 help @@ -157,7 +147,6 @@ config SOC_IMX6SLL bool "i.MX6 SoloLiteLite support" select ARM_ERRATA_754322 select ARM_ERRATA_775420 - select PINCTRL_IMX6SLL select SOC_IMX6 help @@ -167,7 +156,6 @@ config SOC_IMX6SX bool "i.MX6 SoloX support" select ARM_ERRATA_754322 select ARM_ERRATA_775420 - select PINCTRL_IMX6SX select SOC_IMX6 help @@ -175,7 +163,6 @@ config SOC_IMX6SX config SOC_IMX6UL bool "i.MX6 UltraLite support" - select PINCTRL_IMX6UL select SOC_IMX6 select ARM_ERRATA_814220 @@ -211,7 +198,6 @@ config SOC_IMX7D_CM4 config SOC_IMX7D bool "i.MX7 Dual support" - select PINCTRL_IMX7D select SOC_IMX7D_CA7 if ARCH_MULTI_V7 select SOC_IMX7D_CM4 if ARM_SINGLE_ARMV7M select ARM_ERRATA_814220 if ARCH_MULTI_V7 @@ -221,7 +207,6 @@ config SOC_IMX7D config SOC_IMX7ULP bool "i.MX7ULP support" select CLKSRC_IMX_TPM - select PINCTRL_IMX7ULP select SOC_IMX7D_CA7 if ARCH_MULTI_V7 select SOC_IMX7D_CM4 if ARM_SINGLE_ARMV7M help @@ -237,7 +222,6 @@ config SOC_IMXRT config SOC_VF610 bool "Vybrid Family VF610 support" select ARM_GIC if ARCH_MULTI_V7 - select PINCTRL_VF610 help This enables support for Freescale Vybrid VF610 processor. diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 794cfea9f9d4c..89f1c97f3079c 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -23,6 +23,7 @@ */ #include #include +#include #include #include #include @@ -115,16 +116,40 @@ int ioremap_page(unsigned long virt, unsigned long phys, } EXPORT_SYMBOL(ioremap_page); +#ifdef CONFIG_KASAN +static unsigned long arm_kasan_mem_to_shadow(unsigned long addr) +{ + return (unsigned long)kasan_mem_to_shadow((void *)addr); +} +#else +static unsigned long arm_kasan_mem_to_shadow(unsigned long addr) +{ + return 0; +} +#endif + +static void memcpy_pgd(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + end = ALIGN(end, PGDIR_SIZE); + memcpy(pgd_offset(mm, start), pgd_offset_k(start), + sizeof(pgd_t) * (pgd_index(end) - pgd_index(start))); +} + void __check_vmalloc_seq(struct mm_struct *mm) { int seq; do { - seq = atomic_read(&init_mm.context.vmalloc_seq); - memcpy(pgd_offset(mm, VMALLOC_START), - pgd_offset_k(VMALLOC_START), - sizeof(pgd_t) * (pgd_index(VMALLOC_END) - - pgd_index(VMALLOC_START))); + seq = atomic_read_acquire(&init_mm.context.vmalloc_seq); + memcpy_pgd(mm, VMALLOC_START, VMALLOC_END); + if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { + unsigned long start = + arm_kasan_mem_to_shadow(VMALLOC_START); + unsigned long end = + arm_kasan_mem_to_shadow(VMALLOC_END); + memcpy_pgd(mm, start, end); + } /* * Use a store-release so that other CPUs that observe the * counter's new value are guaranteed to see the results of the diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index b68efe643a12c..d44867fc0c5ee 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -55,6 +55,34 @@ extern unsigned int VFP_arch_feroceon __alias(VFP_arch); */ union vfp_state *vfp_current_hw_state[NR_CPUS]; +/* + * Claim ownership of the VFP unit. + * + * The caller may change VFP registers until vfp_state_release() is called. + * + * local_bh_disable() is used to disable preemption and to disable VFP + * processing in softirq context. On PREEMPT_RT kernels local_bh_disable() is + * not sufficient because it only serializes soft interrupt related sections + * via a local lock, but stays preemptible. Disabling preemption is the right + * choice here as bottom half processing is always in thread context on RT + * kernels so it implicitly prevents bottom half processing as well. + */ +static void vfp_state_hold(void) +{ + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_bh_disable(); + else + preempt_disable(); +} + +static void vfp_state_release(void) +{ + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_bh_enable(); + else + preempt_enable(); +} + /* * Is 'thread's most up to date state stored in this CPUs hardware? * Must be called from non-preemptible context. @@ -240,7 +268,7 @@ static void vfp_panic(char *reason, u32 inst) /* * Process bitmask of exception conditions. */ -static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) +static int vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr) { int si_code = 0; @@ -248,8 +276,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ if (exceptions == VFP_EXCEPTION_ERROR) { vfp_panic("unhandled bounce", inst); - vfp_raise_sigfpe(FPE_FLTINV, regs); - return; + return FPE_FLTINV; } /* @@ -277,8 +304,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); - if (si_code) - vfp_raise_sigfpe(si_code, regs); + return si_code; } /* @@ -324,6 +350,8 @@ static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) { u32 fpscr, orig_fpscr, fpsid, exceptions; + int si_code2 = 0; + int si_code = 0; pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); @@ -369,8 +397,8 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) * unallocated VFP instruction but with FPSCR.IXE set and not * on VFP subarch 1. */ - vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); - return; + si_code = vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr); + goto exit; } /* @@ -394,14 +422,14 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) */ exceptions = vfp_emulate_instruction(trigger, fpscr, regs); if (exceptions) - vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); + si_code2 = vfp_raise_exceptions(exceptions, trigger, orig_fpscr); /* * If there isn't a second FP instruction, exit now. Note that * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. */ if ((fpexc & (FPEXC_EX | FPEXC_FP2V)) != (FPEXC_EX | FPEXC_FP2V)) - return; + goto exit; /* * The barrier() here prevents fpinst2 being read @@ -413,7 +441,13 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) emulate: exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); if (exceptions) - vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); + si_code = vfp_raise_exceptions(exceptions, trigger, orig_fpscr); +exit: + vfp_state_release(); + if (si_code2) + vfp_raise_sigfpe(si_code2, regs); + if (si_code) + vfp_raise_sigfpe(si_code, regs); } static void vfp_enable(void *unused) @@ -512,11 +546,9 @@ static inline void vfp_pm_init(void) { } */ void vfp_sync_hwstate(struct thread_info *thread) { - unsigned int cpu = get_cpu(); + vfp_state_hold(); - local_bh_disable(); - - if (vfp_state_in_hw(cpu, thread)) { + if (vfp_state_in_hw(raw_smp_processor_id(), thread)) { u32 fpexc = fmrx(FPEXC); /* @@ -527,8 +559,7 @@ void vfp_sync_hwstate(struct thread_info *thread) fmxr(FPEXC, fpexc); } - local_bh_enable(); - put_cpu(); + vfp_state_release(); } /* Ensure that the thread reloads the hardware VFP state on the next use. */ @@ -683,7 +714,7 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) if (!user_mode(regs)) return vfp_kmode_exception(regs, trigger); - local_bh_disable(); + vfp_state_hold(); fpexc = fmrx(FPEXC); /* @@ -748,6 +779,7 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) * replay the instruction that trapped. */ fmxr(FPEXC, fpexc); + vfp_state_release(); } else { /* Check for synchronous or asynchronous exceptions */ if (!(fpexc & (FPEXC_EX | FPEXC_DEX))) { @@ -762,17 +794,17 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) if (!(fpscr & FPSCR_IXE)) { if (!(fpscr & FPSCR_LENGTH_MASK)) { pr_debug("not VFP\n"); - local_bh_enable(); + vfp_state_release(); return -ENOEXEC; } fpexc |= FPEXC_DEX; } } bounce: regs->ARM_pc += 4; + /* VFP_bounce() will invoke vfp_state_release() */ VFP_bounce(trigger, fpexc, regs); } - local_bh_enable(); return 0; } @@ -837,7 +869,7 @@ void kernel_neon_begin(void) unsigned int cpu; u32 fpexc; - local_bh_disable(); + vfp_state_hold(); /* * Kernel mode NEON is only allowed outside of hardirq context with @@ -868,7 +900,7 @@ void kernel_neon_end(void) { /* Disable the NEON/VFP unit. */ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - local_bh_enable(); + vfp_state_release(); } EXPORT_SYMBOL(kernel_neon_end); diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index d743737bf9ce9..100570a048c5e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -14,7 +14,6 @@ config ARM64 select ARCH_HAS_DEBUG_WX select ARCH_BINFMT_ELF_EXTRA_PHDRS select ARCH_BINFMT_ELF_STATE - select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE select ARCH_ENABLE_HUGEPAGE_MIGRATION if HUGETLB_PAGE && MIGRATION select ARCH_ENABLE_MEMORY_HOTPLUG select ARCH_ENABLE_MEMORY_HOTREMOVE @@ -1236,6 +1235,17 @@ config HISILICON_ERRATUM_161600802 If unsure, say Y. +config HISILICON_ERRATUM_162100801 + bool "Hip09 162100801 erratum support" + default y + help + When enabling GICv4.1 in hip09, VMAPP will fail to clear some caches + during unmapping operation, which will cause some vSGIs lost. + To fix the issue, invalidate related vPE cache through GICR_INVALLR + after VMOVP. + + If unsure, say Y. + config QCOM_FALKOR_ERRATUM_1003 bool "Falkor E1003: Incorrect translation due to ASID change" default y diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 9efd3f37c2fd9..358c68565bfd0 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -71,7 +71,7 @@ stack_protector_prepare: prepare0 -mstack-protector-guard-reg=sp_el0 \ -mstack-protector-guard-offset=$(shell \ awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}' \ - include/generated/asm-offsets.h)) + $(objtree)/include/generated/asm-offsets.h)) endif ifeq ($(CONFIG_ARM64_BTI_KERNEL),y) diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi index ae0379fd42a91..dfc5c2f0ddefd 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtsi @@ -14,6 +14,7 @@ compatible = "melfas,mip4_ts"; reg = <0x34>; interrupts-extended = <&pio 88 IRQ_TYPE_LEVEL_LOW>; + status = "fail-needs-probe"; }; /* @@ -26,6 +27,7 @@ reg = <0x20>; hid-descr-addr = <0x0020>; interrupts-extended = <&pio 88 IRQ_TYPE_LEVEL_LOW>; + status = "fail-needs-probe"; }; /* Lenovo Ideapad C330 uses G2Touch touchscreen as a 2nd source touchscreen */ @@ -35,6 +37,7 @@ hid-descr-addr = <0x0001>; interrupt-parent = <&pio>; interrupts = <88 IRQ_TYPE_LEVEL_LOW>; + status = "fail-needs-probe"; }; }; @@ -47,6 +50,8 @@ trackpad2: trackpad@2c { compatible = "hid-over-i2c"; interrupts-extended = <&pio 117 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_irq>; reg = <0x2c>; hid-descr-addr = <0x0020>; /* @@ -58,6 +63,7 @@ */ vdd-supply = <&mt6397_vgp6_reg>; wakeup-source; + status = "fail-needs-probe"; }; }; @@ -82,3 +88,11 @@ }; }; }; + +&touchscreen { + status = "fail-needs-probe"; +}; + +&trackpad { + status = "fail-needs-probe"; +}; diff --git a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi index b4d85147b77b0..eee64461421f8 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173-elm.dtsi @@ -358,12 +358,12 @@ &i2c4 { clock-frequency = <400000>; status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&trackpad_irq>; trackpad: trackpad@15 { compatible = "elan,ekth3000"; interrupts-extended = <&pio 117 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&trackpad_irq>; reg = <0x15>; vcc-supply = <&mt6397_vgp6_reg>; wakeup-source; diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 9b73fd0cd7212..81e4157f92b74 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -46,6 +46,7 @@ struct cpuinfo_arm64 { u64 reg_revidr; u64 reg_gmid; u64 reg_smidr; + u64 reg_mpamidr; u64 reg_id_aa64dfr0; u64 reg_id_aa64dfr1; diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h index a08a1212ffbb8..201a46efd9188 100644 --- a/arch/arm64/include/asm/cpucaps.h +++ b/arch/arm64/include/asm/cpucaps.h @@ -62,6 +62,11 @@ cpucap_is_possible(const unsigned int cap) return IS_ENABLED(CONFIG_ARM64_WORKAROUND_REPEAT_TLBI); case ARM64_WORKAROUND_SPECULATIVE_SSBS: return IS_ENABLED(CONFIG_ARM64_ERRATUM_3194386); + case ARM64_MPAM: + /* + * KVM MPAM support doesn't rely on the host kernel supporting MPAM. + */ + return true; } return true; diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 3d63c20ccefcd..b64e49bd9d106 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -613,6 +613,13 @@ static inline bool id_aa64pfr1_sme(u64 pfr1) return val > 0; } +static inline bool id_aa64pfr0_mpam(u64 pfr0) +{ + u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL1_MPAM_SHIFT); + + return val > 0; +} + static inline bool id_aa64pfr1_mte(u64 pfr1) { u32 val = cpuid_feature_extract_unsigned_field(pfr1, ID_AA64PFR1_EL1_MTE_SHIFT); @@ -850,6 +857,16 @@ static inline bool system_supports_haft(void) cpus_have_final_cap(ARM64_HAFT); } +static __always_inline bool system_supports_mpam(void) +{ + return alternative_has_cap_unlikely(ARM64_MPAM); +} + +static __always_inline bool system_supports_mpam_hcr(void) +{ + return alternative_has_cap_unlikely(ARM64_MPAM_HCR); +} + int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt); bool try_emulate_mrs(struct pt_regs *regs, u32 isn); diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index 27086a81eae34..85ef966c08cd2 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -249,6 +249,19 @@ msr spsr_el2, x0 .endm +.macro __init_el2_mpam + /* Memory Partitioning And Monitoring: disable EL2 traps */ + mrs x1, id_aa64pfr0_el1 + ubfx x0, x1, #ID_AA64PFR0_EL1_MPAM_SHIFT, #4 + cbz x0, .Lskip_mpam_\@ // skip if no MPAM + msr_s SYS_MPAM2_EL2, xzr // use the default partition + // and disable lower traps + mrs_s x0, SYS_MPAMIDR_EL1 + tbz x0, #MPAMIDR_EL1_HAS_HCR_SHIFT, .Lskip_mpam_\@ // skip if no MPAMHCR reg + msr_s SYS_MPAMHCR_EL2, xzr // clear TRAP_MPAMIDR_EL1 -> EL2 +.Lskip_mpam_\@: +.endm + /** * Initialize EL2 registers to sane values. This should be called early on all * cores that were booted in EL2. Note that everything gets initialised as @@ -266,6 +279,7 @@ __init_el2_stage2 __init_el2_gicv3 __init_el2_hstr + __init_el2_mpam __init_el2_nvhe_idregs __init_el2_cptr __init_el2_fgt diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h index a0a5bbae7229e..424ed421cd979 100644 --- a/arch/arm64/include/asm/jump_label.h +++ b/arch/arm64/include/asm/jump_label.h @@ -19,10 +19,14 @@ #define JUMP_TABLE_ENTRY(key, label) \ ".pushsection __jump_table, \"aw\"\n\t" \ ".align 3\n\t" \ - ".long 1b - ., %l["#label"] - .\n\t" \ - ".quad %c0 - .\n\t" \ - ".popsection\n\t" \ - : : "i"(key) : : label + ".long 1b - ., " label " - .\n\t" \ + ".quad " key " - .\n\t" \ + ".popsection\n\t" + +/* This macro is also expanded on the Rust side. */ +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1: nop\n\t" \ + JUMP_TABLE_ENTRY(key, label) static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch) @@ -30,8 +34,8 @@ static __always_inline bool arch_static_branch(struct static_key * const key, char *k = &((char *)key)[branch]; asm goto( - "1: nop \n\t" - JUMP_TABLE_ENTRY(k, l_yes) + ARCH_STATIC_BRANCH_ASM("%c0", "%l[l_yes]") + : : "i"(k) : : l_yes ); return false; @@ -43,9 +47,11 @@ static __always_inline bool arch_static_branch_jump(struct static_key * const ke const bool branch) { char *k = &((char *)key)[branch]; + asm goto( "1: b %l[l_yes] \n\t" - JUMP_TABLE_ENTRY(k, l_yes) + JUMP_TABLE_ENTRY("%c0", "%l[l_yes]") + : : "i"(k) : : l_yes ); return false; l_yes: diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index 109a85ee69100..3e0f0de1d2da8 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -103,6 +103,7 @@ #define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H) #define HCRX_HOST_FLAGS (HCRX_EL2_MSCEn | HCRX_EL2_TCR2En | HCRX_EL2_EnFPM) +#define MPAMHCR_HOST_FLAGS 0 /* TCR_EL2 Registers bits */ #define TCR_EL2_DS (1UL << 32) @@ -311,35 +312,6 @@ GENMASK(19, 18) | \ GENMASK(15, 0)) -/* Hyp Debug Configuration Register bits */ -#define MDCR_EL2_E2TB_MASK (UL(0x3)) -#define MDCR_EL2_E2TB_SHIFT (UL(24)) -#define MDCR_EL2_HPMFZS (UL(1) << 36) -#define MDCR_EL2_HPMFZO (UL(1) << 29) -#define MDCR_EL2_MTPME (UL(1) << 28) -#define MDCR_EL2_TDCC (UL(1) << 27) -#define MDCR_EL2_HLP (UL(1) << 26) -#define MDCR_EL2_HCCD (UL(1) << 23) -#define MDCR_EL2_TTRF (UL(1) << 19) -#define MDCR_EL2_HPMD (UL(1) << 17) -#define MDCR_EL2_TPMS (UL(1) << 14) -#define MDCR_EL2_E2PB_MASK (UL(0x3)) -#define MDCR_EL2_E2PB_SHIFT (UL(12)) -#define MDCR_EL2_TDRA (UL(1) << 11) -#define MDCR_EL2_TDOSA (UL(1) << 10) -#define MDCR_EL2_TDA (UL(1) << 9) -#define MDCR_EL2_TDE (UL(1) << 8) -#define MDCR_EL2_HPME (UL(1) << 7) -#define MDCR_EL2_TPM (UL(1) << 6) -#define MDCR_EL2_TPMCR (UL(1) << 5) -#define MDCR_EL2_HPMN_MASK (UL(0x1F)) -#define MDCR_EL2_RES0 (GENMASK(63, 37) | \ - GENMASK(35, 30) | \ - GENMASK(25, 24) | \ - GENMASK(22, 20) | \ - BIT(18) | \ - GENMASK(16, 15)) - /* * FGT register definitions * diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 67afac659231e..ca25903443135 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -76,7 +76,6 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, __KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs, - __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_init_traps, __KVM_HOST_SMCCC_FUNC___pkvm_init_vm, __KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu, __KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm, diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index a601a9305b104..cf811009a33c9 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -225,6 +225,11 @@ static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu) return vcpu_has_nv(vcpu) && __is_hyp_ctxt(&vcpu->arch.ctxt); } +static inline bool vcpu_is_host_el0(const struct kvm_vcpu *vcpu) +{ + return is_hyp_ctxt(vcpu) && !vcpu_is_el2(vcpu); +} + /* * The layout of SPSR for an AArch32 state is different when observed from an * AArch64 SPSR_ELx or an AArch32 SPSR_*. This function generates the AArch32 @@ -693,4 +698,8 @@ static inline bool guest_hyp_sve_traps_enabled(const struct kvm_vcpu *vcpu) return __guest_hyp_cptr_xen_trap_enabled(vcpu, ZEN); } +static inline void kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu) +{ + vcpu_set_flag(vcpu, GUEST_HAS_PTRAUTH); +} #endif /* __ARM64_KVM_EMULATE_H__ */ diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index bf64fed9820ea..e18e9244d17a4 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -74,8 +74,6 @@ enum kvm_mode kvm_get_mode(void); static inline enum kvm_mode kvm_get_mode(void) { return KVM_MODE_NONE; }; #endif -DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use); - extern unsigned int __ro_after_init kvm_sve_max_vl; extern unsigned int __ro_after_init kvm_host_sve_max_vl; int __init kvm_arm_init_sve(void); @@ -374,7 +372,7 @@ struct kvm_arch { u64 ctr_el0; - /* Masks for VNCR-baked sysregs */ + /* Masks for VNCR-backed and general EL2 sysregs */ struct kvm_sysreg_masks *sysreg_masks; /* @@ -408,6 +406,9 @@ struct kvm_vcpu_fault_info { r = __VNCR_START__ + ((VNCR_ ## r) / 8), \ __after_##r = __MAX__(__before_##r - 1, r) +#define MARKER(m) \ + m, __after_##m = m - 1 + enum vcpu_sysreg { __INVALID_SYSREG__, /* 0 is reserved as an invalid value */ MPIDR_EL1, /* MultiProcessor Affinity Register */ @@ -468,13 +469,15 @@ enum vcpu_sysreg { /* EL2 registers */ SCTLR_EL2, /* System Control Register (EL2) */ ACTLR_EL2, /* Auxiliary Control Register (EL2) */ - MDCR_EL2, /* Monitor Debug Configuration Register (EL2) */ CPTR_EL2, /* Architectural Feature Trap Register (EL2) */ HACR_EL2, /* Hypervisor Auxiliary Control Register */ ZCR_EL2, /* SVE Control Register (EL2) */ TTBR0_EL2, /* Translation Table Base Register 0 (EL2) */ TTBR1_EL2, /* Translation Table Base Register 1 (EL2) */ TCR_EL2, /* Translation Control Register (EL2) */ + PIRE0_EL2, /* Permission Indirection Register 0 (EL2) */ + PIR_EL2, /* Permission Indirection Register 1 (EL2) */ + POR_EL2, /* Permission Overlay Register 2 (EL2) */ SPSR_EL2, /* EL2 saved program status register */ ELR_EL2, /* EL2 exception link register */ AFSR0_EL2, /* Auxiliary Fault Status Register 0 (EL2) */ @@ -494,7 +497,13 @@ enum vcpu_sysreg { CNTHV_CTL_EL2, CNTHV_CVAL_EL2, - __VNCR_START__, /* Any VNCR-capable reg goes after this point */ + /* Anything from this can be RES0/RES1 sanitised */ + MARKER(__SANITISED_REG_START__), + TCR2_EL2, /* Extended Translation Control Register (EL2) */ + MDCR_EL2, /* Monitor Debug Configuration Register (EL2) */ + + /* Any VNCR-capable reg goes after this point */ + MARKER(__VNCR_START__), VNCR(SCTLR_EL1),/* System Control Register */ VNCR(ACTLR_EL1),/* Auxiliary Control Register */ @@ -554,7 +563,7 @@ struct kvm_sysreg_masks { struct { u64 res0; u64 res1; - } mask[NR_SYS_REGS - __VNCR_START__]; + } mask[NR_SYS_REGS - __SANITISED_REG_START__]; }; struct kvm_cpu_context { @@ -1002,13 +1011,13 @@ static inline u64 *___ctxt_sys_reg(const struct kvm_cpu_context *ctxt, int r) #define ctxt_sys_reg(c,r) (*__ctxt_sys_reg(c,r)) -u64 kvm_vcpu_sanitise_vncr_reg(const struct kvm_vcpu *, enum vcpu_sysreg); +u64 kvm_vcpu_apply_reg_masks(const struct kvm_vcpu *, enum vcpu_sysreg, u64); #define __vcpu_sys_reg(v,r) \ (*({ \ const struct kvm_cpu_context *ctxt = &(v)->arch.ctxt; \ u64 *__r = __ctxt_sys_reg(ctxt, (r)); \ - if (vcpu_has_nv((v)) && (r) >= __VNCR_START__) \ - *__r = kvm_vcpu_sanitise_vncr_reg((v), (r)); \ + if (vcpu_has_nv((v)) && (r) >= __SANITISED_REG_START__) \ + *__r = kvm_vcpu_apply_reg_masks((v), (r), *__r);\ __r; \ })) @@ -1037,6 +1046,10 @@ static inline bool __vcpu_read_sys_reg_from_cpu(int reg, u64 *val) case TTBR0_EL1: *val = read_sysreg_s(SYS_TTBR0_EL12); break; case TTBR1_EL1: *val = read_sysreg_s(SYS_TTBR1_EL12); break; case TCR_EL1: *val = read_sysreg_s(SYS_TCR_EL12); break; + case TCR2_EL1: *val = read_sysreg_s(SYS_TCR2_EL12); break; + case PIR_EL1: *val = read_sysreg_s(SYS_PIR_EL12); break; + case PIRE0_EL1: *val = read_sysreg_s(SYS_PIRE0_EL12); break; + case POR_EL1: *val = read_sysreg_s(SYS_POR_EL12); break; case ESR_EL1: *val = read_sysreg_s(SYS_ESR_EL12); break; case AFSR0_EL1: *val = read_sysreg_s(SYS_AFSR0_EL12); break; case AFSR1_EL1: *val = read_sysreg_s(SYS_AFSR1_EL12); break; @@ -1083,6 +1096,10 @@ static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg) case TTBR0_EL1: write_sysreg_s(val, SYS_TTBR0_EL12); break; case TTBR1_EL1: write_sysreg_s(val, SYS_TTBR1_EL12); break; case TCR_EL1: write_sysreg_s(val, SYS_TCR_EL12); break; + case TCR2_EL1: write_sysreg_s(val, SYS_TCR2_EL12); break; + case PIR_EL1: write_sysreg_s(val, SYS_PIR_EL12); break; + case PIRE0_EL1: write_sysreg_s(val, SYS_PIRE0_EL12); break; + case POR_EL1: write_sysreg_s(val, SYS_POR_EL12); break; case ESR_EL1: write_sysreg_s(val, SYS_ESR_EL12); break; case AFSR0_EL1: write_sysreg_s(val, SYS_AFSR0_EL12); break; case AFSR1_EL1: write_sysreg_s(val, SYS_AFSR1_EL12); break; @@ -1140,7 +1157,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu, void kvm_arm_halt_guest(struct kvm *kvm); void kvm_arm_resume_guest(struct kvm *kvm); -#define vcpu_has_run_once(vcpu) !!rcu_access_pointer((vcpu)->pid) +#define vcpu_has_run_once(vcpu) (!!READ_ONCE((vcpu)->pid)) #ifndef __KVM_NVHE_HYPERVISOR__ #define kvm_call_hyp_nvhe(f, ...) \ @@ -1503,4 +1520,13 @@ void kvm_set_vm_id_reg(struct kvm *kvm, u32 reg, u64 val); (system_supports_fpmr() && \ kvm_has_feat((k), ID_AA64PFR2_EL1, FPMR, IMP)) +#define kvm_has_tcr2(k) \ + (kvm_has_feat((k), ID_AA64MMFR3_EL1, TCRX, IMP)) + +#define kvm_has_s1pie(k) \ + (kvm_has_feat((k), ID_AA64MMFR3_EL1, S1PIE, IMP)) + +#define kvm_has_s1poe(k) \ + (kvm_has_feat((k), ID_AA64MMFR3_EL1, S1POE, IMP)) + #endif /* __ARM64_KVM_HOST_H__ */ diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h index 03f4c3d7839c2..aab04097b5054 100644 --- a/arch/arm64/include/asm/kvm_pgtable.h +++ b/arch/arm64/include/asm/kvm_pgtable.h @@ -674,10 +674,8 @@ int kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size); * * If there is a valid, leaf page-table entry used to translate @addr, then * set the access flag in that entry. - * - * Return: The old page-table entry prior to setting the flag, 0 on failure. */ -kvm_pte_t kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr); +void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr); /** * kvm_pgtable_stage2_test_clear_young() - Test and optionally clear the access diff --git a/arch/arm64/include/asm/spinlock_types.h b/arch/arm64/include/asm/spinlock_types.h index 11ab1c0776977..7cde3d8bd0add 100644 --- a/arch/arm64/include/asm/spinlock_types.h +++ b/arch/arm64/include/asm/spinlock_types.h @@ -6,7 +6,7 @@ #define __ASM_SPINLOCK_TYPES_H #if !defined(__LINUX_SPINLOCK_TYPES_RAW_H) && !defined(__ASM_SPINLOCK_H) -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif #include diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 9c98ff448bd9e..b8303a83c0bff 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -542,18 +542,6 @@ #define SYS_MAIR_EL2 sys_reg(3, 4, 10, 2, 0) #define SYS_AMAIR_EL2 sys_reg(3, 4, 10, 3, 0) -#define SYS_MPAMHCR_EL2 sys_reg(3, 4, 10, 4, 0) -#define SYS_MPAMVPMV_EL2 sys_reg(3, 4, 10, 4, 1) -#define SYS_MPAM2_EL2 sys_reg(3, 4, 10, 5, 0) -#define __SYS__MPAMVPMx_EL2(x) sys_reg(3, 4, 10, 6, x) -#define SYS_MPAMVPM0_EL2 __SYS__MPAMVPMx_EL2(0) -#define SYS_MPAMVPM1_EL2 __SYS__MPAMVPMx_EL2(1) -#define SYS_MPAMVPM2_EL2 __SYS__MPAMVPMx_EL2(2) -#define SYS_MPAMVPM3_EL2 __SYS__MPAMVPMx_EL2(3) -#define SYS_MPAMVPM4_EL2 __SYS__MPAMVPMx_EL2(4) -#define SYS_MPAMVPM5_EL2 __SYS__MPAMVPMx_EL2(5) -#define SYS_MPAMVPM6_EL2 __SYS__MPAMVPMx_EL2(6) -#define SYS_MPAMVPM7_EL2 __SYS__MPAMVPMx_EL2(7) #define SYS_VBAR_EL2 sys_reg(3, 4, 12, 0, 0) #define SYS_RVBAR_EL2 sys_reg(3, 4, 12, 0, 1) diff --git a/arch/arm64/include/asm/vncr_mapping.h b/arch/arm64/include/asm/vncr_mapping.h index 06f8ec0906a6e..4f9bbd4d6c267 100644 --- a/arch/arm64/include/asm/vncr_mapping.h +++ b/arch/arm64/include/asm/vncr_mapping.h @@ -50,7 +50,6 @@ #define VNCR_VBAR_EL1 0x250 #define VNCR_TCR2_EL1 0x270 #define VNCR_PIRE0_EL1 0x290 -#define VNCR_PIRE0_EL2 0x298 #define VNCR_PIR_EL1 0x2A0 #define VNCR_POR_EL1 0x2A8 #define VNCR_ICH_LR0_EL2 0x400 diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 964df31da9751..66736ff04011e 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -484,6 +484,12 @@ enum { */ #define KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2 (1ULL << 0) +/* + * Shutdown caused by a PSCI v1.3 SYSTEM_OFF2 call. + * Valid only when the system event has a type of KVM_SYSTEM_EVENT_SHUTDOWN. + */ +#define KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 (1ULL << 0) + /* run->fail_entry.hardware_entry_failure_reason codes. */ #define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 351aa825ec40b..6ce71f444ed84 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -688,6 +688,14 @@ static const struct arm64_ftr_bits ftr_id_dfr1[] = { ARM64_FTR_END, }; +static const struct arm64_ftr_bits ftr_mpamidr[] = { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, MPAMIDR_EL1_PMG_MAX_SHIFT, MPAMIDR_EL1_PMG_MAX_WIDTH, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, MPAMIDR_EL1_VPMR_MAX_SHIFT, MPAMIDR_EL1_VPMR_MAX_WIDTH, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, MPAMIDR_EL1_HAS_HCR_SHIFT, 1, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, MPAMIDR_EL1_PARTID_MAX_SHIFT, MPAMIDR_EL1_PARTID_MAX_WIDTH, 0), + ARM64_FTR_END, +}; + /* * Common ftr bits for a 32bit register with all hidden, strict * attributes, with 4bit feature fields and a default safe value of @@ -808,6 +816,9 @@ static const struct __ftr_reg_entry { ARM64_FTR_REG(SYS_ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3), ARM64_FTR_REG(SYS_ID_AA64MMFR4_EL1, ftr_id_aa64mmfr4), + /* Op1 = 0, CRn = 10, CRm = 4 */ + ARM64_FTR_REG(SYS_MPAMIDR_EL1, ftr_mpamidr), + /* Op1 = 1, CRn = 0, CRm = 0 */ ARM64_FTR_REG(SYS_GMID_EL1, ftr_gmid), @@ -1167,6 +1178,9 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info) cpacr_restore(cpacr); } + if (id_aa64pfr0_mpam(info->reg_id_aa64pfr0)) + init_cpu_ftr_reg(SYS_MPAMIDR_EL1, info->reg_mpamidr); + if (id_aa64pfr1_mte(info->reg_id_aa64pfr1)) init_cpu_ftr_reg(SYS_GMID_EL1, info->reg_gmid); } @@ -1423,6 +1437,11 @@ void update_cpu_features(int cpu, cpacr_restore(cpacr); } + if (id_aa64pfr0_mpam(info->reg_id_aa64pfr0)) { + taint |= check_update_ftr_reg(SYS_MPAMIDR_EL1, cpu, + info->reg_mpamidr, boot->reg_mpamidr); + } + /* * The kernel uses the LDGM/STGM instructions and the number of tags * they read/write depends on the GMID_EL1.BS field. Check that the @@ -2389,6 +2408,36 @@ cpucap_panic_on_conflict(const struct arm64_cpu_capabilities *cap) return !!(cap->type & ARM64_CPUCAP_PANIC_ON_CONFLICT); } +static bool +test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) +{ + if (!has_cpuid_feature(entry, scope)) + return false; + + /* Check firmware actually enabled MPAM on this cpu. */ + return (read_sysreg_s(SYS_MPAM1_EL1) & MPAM1_EL1_MPAMEN); +} + +static void +cpu_enable_mpam(const struct arm64_cpu_capabilities *entry) +{ + /* + * Access by the kernel (at EL1) should use the reserved PARTID + * which is configured unrestricted. This avoids priority-inversion + * where latency sensitive tasks have to wait for a task that has + * been throttled to release the lock. + */ + write_sysreg_s(0, SYS_MPAM1_EL1); +} + +static bool +test_has_mpam_hcr(const struct arm64_cpu_capabilities *entry, int scope) +{ + u64 idr = read_sanitised_ftr_reg(SYS_MPAMIDR_EL1); + + return idr & MPAMIDR_EL1_HAS_HCR; +} + static const struct arm64_cpu_capabilities arm64_features[] = { { .capability = ARM64_ALWAYS_BOOT, @@ -2900,6 +2949,20 @@ static const struct arm64_cpu_capabilities arm64_features[] = { #endif }, #endif + { + .desc = "Memory Partitioning And Monitoring", + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .capability = ARM64_MPAM, + .matches = test_has_mpam, + .cpu_enable = cpu_enable_mpam, + ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, MPAM, 1) + }, + { + .desc = "Memory Partitioning And Monitoring Virtualisation", + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .capability = ARM64_MPAM_HCR, + .matches = test_has_mpam_hcr, + }, { .desc = "NV1", .capability = ARM64_HAS_HCR_NV1, @@ -3436,6 +3499,36 @@ static void verify_hyp_capabilities(void) } } +static void verify_mpam_capabilities(void) +{ + u64 cpu_idr = read_cpuid(ID_AA64PFR0_EL1); + u64 sys_idr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1); + u16 cpu_partid_max, cpu_pmg_max, sys_partid_max, sys_pmg_max; + + if (FIELD_GET(ID_AA64PFR0_EL1_MPAM_MASK, cpu_idr) != + FIELD_GET(ID_AA64PFR0_EL1_MPAM_MASK, sys_idr)) { + pr_crit("CPU%d: MPAM version mismatch\n", smp_processor_id()); + cpu_die_early(); + } + + cpu_idr = read_cpuid(MPAMIDR_EL1); + sys_idr = read_sanitised_ftr_reg(SYS_MPAMIDR_EL1); + if (FIELD_GET(MPAMIDR_EL1_HAS_HCR, cpu_idr) != + FIELD_GET(MPAMIDR_EL1_HAS_HCR, sys_idr)) { + pr_crit("CPU%d: Missing MPAM HCR\n", smp_processor_id()); + cpu_die_early(); + } + + cpu_partid_max = FIELD_GET(MPAMIDR_EL1_PARTID_MAX, cpu_idr); + cpu_pmg_max = FIELD_GET(MPAMIDR_EL1_PMG_MAX, cpu_idr); + sys_partid_max = FIELD_GET(MPAMIDR_EL1_PARTID_MAX, sys_idr); + sys_pmg_max = FIELD_GET(MPAMIDR_EL1_PMG_MAX, sys_idr); + if (cpu_partid_max < sys_partid_max || cpu_pmg_max < sys_pmg_max) { + pr_crit("CPU%d: MPAM PARTID/PMG max values are mismatched\n", smp_processor_id()); + cpu_die_early(); + } +} + /* * Run through the enabled system capabilities and enable() it on this CPU. * The capabilities were decided based on the available CPUs at the boot time. @@ -3462,6 +3555,9 @@ static void verify_local_cpu_capabilities(void) if (is_hyp_mode_available()) verify_hyp_capabilities(); + + if (system_supports_mpam()) + verify_mpam_capabilities(); } void check_local_cpu_capabilities(void) diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index f2f92c6b1c850..d79e88fccdfce 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -479,6 +479,9 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) __cpuinfo_store_cpu_32bit(&info->aarch32); + if (id_aa64pfr0_mpam(info->reg_id_aa64pfr0)) + info->reg_mpamidr = read_cpuid(MPAMIDR_EL1); + cpuinfo_detect_icache_policy(info); } diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 879982b1cc739..1215df5904185 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -206,8 +206,7 @@ void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map) static inline bool userspace_irqchip(struct kvm *kvm) { - return static_branch_unlikely(&userspace_irqchip_in_use) && - unlikely(!irqchip_in_kernel(kvm)); + return unlikely(!irqchip_in_kernel(kvm)); } static void soft_timer_start(struct hrtimer *hrt, u64 ns) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 48cafb65d6acf..a102c3aebdbc4 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -69,7 +69,6 @@ DECLARE_KVM_NVHE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt); static bool vgic_present, kvm_arm_initialised; static DEFINE_PER_CPU(unsigned char, kvm_hyp_initialized); -DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use); bool is_kvm_arm_initialised(void) { @@ -503,9 +502,6 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) { - if (vcpu_has_run_once(vcpu) && unlikely(!irqchip_in_kernel(vcpu->kvm))) - static_branch_dec(&userspace_irqchip_in_use); - kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); kvm_timer_vcpu_terminate(vcpu); kvm_pmu_vcpu_destroy(vcpu); @@ -848,22 +844,6 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) return ret; } - if (!irqchip_in_kernel(kvm)) { - /* - * Tell the rest of the code that there are userspace irqchip - * VMs in the wild. - */ - static_branch_inc(&userspace_irqchip_in_use); - } - - /* - * Initialize traps for protected VMs. - * NOTE: Move to run in EL2 directly, rather than via a hypercall, once - * the code is in place for first run initialization at EL2. - */ - if (kvm_vm_is_protected(kvm)) - kvm_call_hyp_nvhe(__pkvm_vcpu_init_traps, vcpu); - mutex_lock(&kvm->arch.config_lock); set_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags); mutex_unlock(&kvm->arch.config_lock); @@ -1077,7 +1057,7 @@ static bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu, int *ret) * state gets updated in kvm_timer_update_run and * kvm_pmu_update_run below). */ - if (static_branch_unlikely(&userspace_irqchip_in_use)) { + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) { if (kvm_timer_should_notify_user(vcpu) || kvm_pmu_should_notify_user(vcpu)) { *ret = -EINTR; @@ -1199,7 +1179,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) vcpu->mode = OUTSIDE_GUEST_MODE; isb(); /* Ensure work in x_flush_hwstate is committed */ kvm_pmu_sync_hwstate(vcpu); - if (static_branch_unlikely(&userspace_irqchip_in_use)) + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) kvm_timer_sync_user(vcpu); kvm_vgic_sync_hwstate(vcpu); local_irq_enable(); @@ -1245,7 +1225,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) * we don't want vtimer interrupts to race with syncing the * timer virtual interrupt state. */ - if (static_branch_unlikely(&userspace_irqchip_in_use)) + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) kvm_timer_sync_user(vcpu); kvm_arch_vcpu_ctxsync_fp(vcpu); diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 39f0e87a340e8..8c5d7990e5b31 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -24,6 +24,9 @@ struct s1_walk_info { unsigned int txsz; int sl; bool hpd; + bool e0poe; + bool poe; + bool pan; bool be; bool s2; }; @@ -37,6 +40,16 @@ struct s1_walk_result { u8 APTable; bool UXNTable; bool PXNTable; + bool uwxn; + bool uov; + bool ur; + bool uw; + bool ux; + bool pwxn; + bool pov; + bool pr; + bool pw; + bool px; }; struct { u8 fst; @@ -87,6 +100,51 @@ static enum trans_regime compute_translation_regime(struct kvm_vcpu *vcpu, u32 o } } +static bool s1pie_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime) +{ + if (!kvm_has_s1pie(vcpu->kvm)) + return false; + + switch (regime) { + case TR_EL2: + case TR_EL20: + return vcpu_read_sys_reg(vcpu, TCR2_EL2) & TCR2_EL2_PIE; + case TR_EL10: + return (__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TCR2En) && + (__vcpu_sys_reg(vcpu, TCR2_EL1) & TCR2_EL1x_PIE); + default: + BUG(); + } +} + +static void compute_s1poe(struct kvm_vcpu *vcpu, struct s1_walk_info *wi) +{ + u64 val; + + if (!kvm_has_s1poe(vcpu->kvm)) { + wi->poe = wi->e0poe = false; + return; + } + + switch (wi->regime) { + case TR_EL2: + case TR_EL20: + val = vcpu_read_sys_reg(vcpu, TCR2_EL2); + wi->poe = val & TCR2_EL2_POE; + wi->e0poe = (wi->regime == TR_EL20) && (val & TCR2_EL2_E0POE); + break; + case TR_EL10: + if (__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TCR2En) { + wi->poe = wi->e0poe = false; + return; + } + + val = __vcpu_sys_reg(vcpu, TCR2_EL1); + wi->poe = val & TCR2_EL1x_POE; + wi->e0poe = val & TCR2_EL1x_E0POE; + } +} + static int setup_s1_walk(struct kvm_vcpu *vcpu, u32 op, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va) { @@ -98,6 +156,8 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, u32 op, struct s1_walk_info *wi, wi->regime = compute_translation_regime(vcpu, op); as_el0 = (op == OP_AT_S1E0R || op == OP_AT_S1E0W); + wi->pan = (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) && + (*vcpu_cpsr(vcpu) & PSR_PAN_BIT); va55 = va & BIT(55); @@ -180,6 +240,14 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, u32 op, struct s1_walk_info *wi, (va55 ? FIELD_GET(TCR_HPD1, tcr) : FIELD_GET(TCR_HPD0, tcr))); + /* R_JHSVW */ + wi->hpd |= s1pie_enabled(vcpu, wi->regime); + + /* Do we have POE? */ + compute_s1poe(vcpu, wi); + + /* R_BVXDG */ + wi->hpd |= (wi->poe || wi->e0poe); /* Someone was silly enough to encode TG0/TG1 differently */ if (va55) { @@ -412,6 +480,11 @@ struct mmu_config { u64 ttbr1; u64 tcr; u64 mair; + u64 tcr2; + u64 pir; + u64 pire0; + u64 por_el0; + u64 por_el1; u64 sctlr; u64 vttbr; u64 vtcr; @@ -424,6 +497,17 @@ static void __mmu_config_save(struct mmu_config *config) config->ttbr1 = read_sysreg_el1(SYS_TTBR1); config->tcr = read_sysreg_el1(SYS_TCR); config->mair = read_sysreg_el1(SYS_MAIR); + if (cpus_have_final_cap(ARM64_HAS_TCR2)) { + config->tcr2 = read_sysreg_el1(SYS_TCR2); + if (cpus_have_final_cap(ARM64_HAS_S1PIE)) { + config->pir = read_sysreg_el1(SYS_PIR); + config->pire0 = read_sysreg_el1(SYS_PIRE0); + } + if (system_supports_poe()) { + config->por_el1 = read_sysreg_el1(SYS_POR); + config->por_el0 = read_sysreg_s(SYS_POR_EL0); + } + } config->sctlr = read_sysreg_el1(SYS_SCTLR); config->vttbr = read_sysreg(vttbr_el2); config->vtcr = read_sysreg(vtcr_el2); @@ -444,6 +528,17 @@ static void __mmu_config_restore(struct mmu_config *config) write_sysreg_el1(config->ttbr1, SYS_TTBR1); write_sysreg_el1(config->tcr, SYS_TCR); write_sysreg_el1(config->mair, SYS_MAIR); + if (cpus_have_final_cap(ARM64_HAS_TCR2)) { + write_sysreg_el1(config->tcr2, SYS_TCR2); + if (cpus_have_final_cap(ARM64_HAS_S1PIE)) { + write_sysreg_el1(config->pir, SYS_PIR); + write_sysreg_el1(config->pire0, SYS_PIRE0); + } + if (system_supports_poe()) { + write_sysreg_el1(config->por_el1, SYS_POR); + write_sysreg_s(config->por_el0, SYS_POR_EL0); + } + } write_sysreg_el1(config->sctlr, SYS_SCTLR); write_sysreg(config->vttbr, vttbr_el2); write_sysreg(config->vtcr, vtcr_el2); @@ -739,6 +834,9 @@ static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime) if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR1_EL1, PAN, PAN3)) return false; + if (s1pie_enabled(vcpu, regime)) + return true; + if (regime == TR_EL10) sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1); else @@ -747,111 +845,343 @@ static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime) return sctlr & SCTLR_EL1_EPAN; } -static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) +static void compute_s1_direct_permissions(struct kvm_vcpu *vcpu, + struct s1_walk_info *wi, + struct s1_walk_result *wr) { - bool perm_fail, ur, uw, ux, pr, pw, px; - struct s1_walk_result wr = {}; - struct s1_walk_info wi = {}; - int ret, idx; + bool wxn; - ret = setup_s1_walk(vcpu, op, &wi, &wr, vaddr); - if (ret) - goto compute_par; - - if (wr.level == S1_MMU_DISABLED) - goto compute_par; - - idx = srcu_read_lock(&vcpu->kvm->srcu); - - ret = walk_s1(vcpu, &wi, &wr, vaddr); - - srcu_read_unlock(&vcpu->kvm->srcu, idx); - - if (ret) - goto compute_par; - - /* FIXME: revisit when adding indirect permission support */ - /* AArch64.S1DirectBasePermissions() */ - if (wi.regime != TR_EL2) { - switch (FIELD_GET(PTE_USER | PTE_RDONLY, wr.desc)) { + /* Non-hierarchical part of AArch64.S1DirectBasePermissions() */ + if (wi->regime != TR_EL2) { + switch (FIELD_GET(PTE_USER | PTE_RDONLY, wr->desc)) { case 0b00: - pr = pw = true; - ur = uw = false; + wr->pr = wr->pw = true; + wr->ur = wr->uw = false; break; case 0b01: - pr = pw = ur = uw = true; + wr->pr = wr->pw = wr->ur = wr->uw = true; break; case 0b10: - pr = true; - pw = ur = uw = false; + wr->pr = true; + wr->pw = wr->ur = wr->uw = false; break; case 0b11: - pr = ur = true; - pw = uw = false; + wr->pr = wr->ur = true; + wr->pw = wr->uw = false; break; } - switch (wr.APTable) { + /* We don't use px for anything yet, but hey... */ + wr->px = !((wr->desc & PTE_PXN) || wr->uw); + wr->ux = !(wr->desc & PTE_UXN); + } else { + wr->ur = wr->uw = wr->ux = false; + + if (!(wr->desc & PTE_RDONLY)) { + wr->pr = wr->pw = true; + } else { + wr->pr = true; + wr->pw = false; + } + + /* XN maps to UXN */ + wr->px = !(wr->desc & PTE_UXN); + } + + switch (wi->regime) { + case TR_EL2: + case TR_EL20: + wxn = (vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_WXN); + break; + case TR_EL10: + wxn = (__vcpu_sys_reg(vcpu, SCTLR_EL1) & SCTLR_ELx_WXN); + break; + } + + wr->pwxn = wr->uwxn = wxn; + wr->pov = wi->poe; + wr->uov = wi->e0poe; +} + +static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu, + struct s1_walk_info *wi, + struct s1_walk_result *wr) +{ + /* Hierarchical part of AArch64.S1DirectBasePermissions() */ + if (wi->regime != TR_EL2) { + switch (wr->APTable) { case 0b00: break; case 0b01: - ur = uw = false; + wr->ur = wr->uw = false; break; case 0b10: - pw = uw = false; + wr->pw = wr->uw = false; break; case 0b11: - pw = ur = uw = false; + wr->pw = wr->ur = wr->uw = false; break; } - /* We don't use px for anything yet, but hey... */ - px = !((wr.desc & PTE_PXN) || wr.PXNTable || uw); - ux = !((wr.desc & PTE_UXN) || wr.UXNTable); + wr->px &= !wr->PXNTable; + wr->ux &= !wr->UXNTable; + } else { + if (wr->APTable & BIT(1)) + wr->pw = false; + + /* XN maps to UXN */ + wr->px &= !wr->UXNTable; + } +} - if (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) { - bool pan; +#define perm_idx(v, r, i) ((vcpu_read_sys_reg((v), (r)) >> ((i) * 4)) & 0xf) + +#define set_priv_perms(wr, r, w, x) \ + do { \ + (wr)->pr = (r); \ + (wr)->pw = (w); \ + (wr)->px = (x); \ + } while (0) + +#define set_unpriv_perms(wr, r, w, x) \ + do { \ + (wr)->ur = (r); \ + (wr)->uw = (w); \ + (wr)->ux = (x); \ + } while (0) + +#define set_priv_wxn(wr, v) \ + do { \ + (wr)->pwxn = (v); \ + } while (0) + +#define set_unpriv_wxn(wr, v) \ + do { \ + (wr)->uwxn = (v); \ + } while (0) + +/* Similar to AArch64.S1IndirectBasePermissions(), without GCS */ +#define set_perms(w, wr, ip) \ + do { \ + /* R_LLZDZ */ \ + switch ((ip)) { \ + case 0b0000: \ + set_ ## w ## _perms((wr), false, false, false); \ + break; \ + case 0b0001: \ + set_ ## w ## _perms((wr), true , false, false); \ + break; \ + case 0b0010: \ + set_ ## w ## _perms((wr), false, false, true ); \ + break; \ + case 0b0011: \ + set_ ## w ## _perms((wr), true , false, true ); \ + break; \ + case 0b0100: \ + set_ ## w ## _perms((wr), false, false, false); \ + break; \ + case 0b0101: \ + set_ ## w ## _perms((wr), true , true , false); \ + break; \ + case 0b0110: \ + set_ ## w ## _perms((wr), true , true , true ); \ + break; \ + case 0b0111: \ + set_ ## w ## _perms((wr), true , true , true ); \ + break; \ + case 0b1000: \ + set_ ## w ## _perms((wr), true , false, false); \ + break; \ + case 0b1001: \ + set_ ## w ## _perms((wr), true , false, false); \ + break; \ + case 0b1010: \ + set_ ## w ## _perms((wr), true , false, true ); \ + break; \ + case 0b1011: \ + set_ ## w ## _perms((wr), false, false, false); \ + break; \ + case 0b1100: \ + set_ ## w ## _perms((wr), true , true , false); \ + break; \ + case 0b1101: \ + set_ ## w ## _perms((wr), false, false, false); \ + break; \ + case 0b1110: \ + set_ ## w ## _perms((wr), true , true , true ); \ + break; \ + case 0b1111: \ + set_ ## w ## _perms((wr), false, false, false); \ + break; \ + } \ + \ + /* R_HJYGR */ \ + set_ ## w ## _wxn((wr), ((ip) == 0b0110)); \ + \ + } while (0) + +static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu, + struct s1_walk_info *wi, + struct s1_walk_result *wr) +{ + u8 up, pp, idx; - pan = *vcpu_cpsr(vcpu) & PSR_PAN_BIT; - pan &= ur || uw || (pan3_enabled(vcpu, wi.regime) && ux); - pw &= !pan; - pr &= !pan; - } - } else { - ur = uw = ux = false; + idx = pte_pi_index(wr->desc); - if (!(wr.desc & PTE_RDONLY)) { - pr = pw = true; - } else { - pr = true; - pw = false; - } + switch (wi->regime) { + case TR_EL10: + pp = perm_idx(vcpu, PIR_EL1, idx); + up = perm_idx(vcpu, PIRE0_EL1, idx); + break; + case TR_EL20: + pp = perm_idx(vcpu, PIR_EL2, idx); + up = perm_idx(vcpu, PIRE0_EL2, idx); + break; + case TR_EL2: + pp = perm_idx(vcpu, PIR_EL2, idx); + up = 0; + break; + } - if (wr.APTable & BIT(1)) - pw = false; + set_perms(priv, wr, pp); - /* XN maps to UXN */ - px = !((wr.desc & PTE_UXN) || wr.UXNTable); + if (wi->regime != TR_EL2) + set_perms(unpriv, wr, up); + else + set_unpriv_perms(wr, false, false, false); + + wr->pov = wi->poe && !(pp & BIT(3)); + wr->uov = wi->e0poe && !(up & BIT(3)); + + /* R_VFPJF */ + if (wr->px && wr->uw) { + set_priv_perms(wr, false, false, false); + set_unpriv_perms(wr, false, false, false); + } +} + +static void compute_s1_overlay_permissions(struct kvm_vcpu *vcpu, + struct s1_walk_info *wi, + struct s1_walk_result *wr) +{ + u8 idx, pov_perms, uov_perms; + + idx = FIELD_GET(PTE_PO_IDX_MASK, wr->desc); + + switch (wi->regime) { + case TR_EL10: + pov_perms = perm_idx(vcpu, POR_EL1, idx); + uov_perms = perm_idx(vcpu, POR_EL0, idx); + break; + case TR_EL20: + pov_perms = perm_idx(vcpu, POR_EL2, idx); + uov_perms = perm_idx(vcpu, POR_EL0, idx); + break; + case TR_EL2: + pov_perms = perm_idx(vcpu, POR_EL2, idx); + uov_perms = 0; + break; + } + + if (pov_perms & ~POE_RXW) + pov_perms = POE_NONE; + + if (wi->poe && wr->pov) { + wr->pr &= pov_perms & POE_R; + wr->px &= pov_perms & POE_X; + wr->pw &= pov_perms & POE_W; + } + + if (uov_perms & ~POE_RXW) + uov_perms = POE_NONE; + + if (wi->e0poe && wr->uov) { + wr->ur &= uov_perms & POE_R; + wr->ux &= uov_perms & POE_X; + wr->uw &= uov_perms & POE_W; } +} + +static void compute_s1_permissions(struct kvm_vcpu *vcpu, + struct s1_walk_info *wi, + struct s1_walk_result *wr) +{ + bool pan; + + if (!s1pie_enabled(vcpu, wi->regime)) + compute_s1_direct_permissions(vcpu, wi, wr); + else + compute_s1_indirect_permissions(vcpu, wi, wr); + + if (!wi->hpd) + compute_s1_hierarchical_permissions(vcpu, wi, wr); + + if (wi->poe || wi->e0poe) + compute_s1_overlay_permissions(vcpu, wi, wr); + + /* R_QXXPC */ + if (wr->pwxn) { + if (!wr->pov && wr->pw) + wr->px = false; + if (wr->pov && wr->px) + wr->pw = false; + } + + /* R_NPBXC */ + if (wr->uwxn) { + if (!wr->uov && wr->uw) + wr->ux = false; + if (wr->uov && wr->ux) + wr->uw = false; + } + + pan = wi->pan && (wr->ur || wr->uw || + (pan3_enabled(vcpu, wi->regime) && wr->ux)); + wr->pw &= !pan; + wr->pr &= !pan; +} - perm_fail = false; +static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) +{ + struct s1_walk_result wr = {}; + struct s1_walk_info wi = {}; + bool perm_fail = false; + int ret, idx; + + ret = setup_s1_walk(vcpu, op, &wi, &wr, vaddr); + if (ret) + goto compute_par; + + if (wr.level == S1_MMU_DISABLED) + goto compute_par; + + idx = srcu_read_lock(&vcpu->kvm->srcu); + + ret = walk_s1(vcpu, &wi, &wr, vaddr); + + srcu_read_unlock(&vcpu->kvm->srcu, idx); + + if (ret) + goto compute_par; + + compute_s1_permissions(vcpu, &wi, &wr); switch (op) { case OP_AT_S1E1RP: case OP_AT_S1E1R: case OP_AT_S1E2R: - perm_fail = !pr; + perm_fail = !wr.pr; break; case OP_AT_S1E1WP: case OP_AT_S1E1W: case OP_AT_S1E2W: - perm_fail = !pw; + perm_fail = !wr.pw; break; case OP_AT_S1E0R: - perm_fail = !ur; + perm_fail = !wr.ur; break; case OP_AT_S1E0W: - perm_fail = !uw; + perm_fail = !wr.uw; break; case OP_AT_S1E1A: case OP_AT_S1E2A: @@ -914,6 +1244,17 @@ static u64 __kvm_at_s1e01_fast(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) write_sysreg_el1(vcpu_read_sys_reg(vcpu, TTBR1_EL1), SYS_TTBR1); write_sysreg_el1(vcpu_read_sys_reg(vcpu, TCR_EL1), SYS_TCR); write_sysreg_el1(vcpu_read_sys_reg(vcpu, MAIR_EL1), SYS_MAIR); + if (kvm_has_tcr2(vcpu->kvm)) { + write_sysreg_el1(vcpu_read_sys_reg(vcpu, TCR2_EL1), SYS_TCR2); + if (kvm_has_s1pie(vcpu->kvm)) { + write_sysreg_el1(vcpu_read_sys_reg(vcpu, PIR_EL1), SYS_PIR); + write_sysreg_el1(vcpu_read_sys_reg(vcpu, PIRE0_EL1), SYS_PIRE0); + } + if (kvm_has_s1poe(vcpu->kvm)) { + write_sysreg_el1(vcpu_read_sys_reg(vcpu, POR_EL1), SYS_POR); + write_sysreg_s(vcpu_read_sys_reg(vcpu, POR_EL0), SYS_POR_EL0); + } + } write_sysreg_el1(vcpu_read_sys_reg(vcpu, SCTLR_EL1), SYS_SCTLR); __load_stage2(mmu, mmu->arch); @@ -992,12 +1333,9 @@ void __kvm_at_s1e2(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) * switching context behind everybody's back, disable interrupts... */ scoped_guard(write_lock_irqsave, &vcpu->kvm->mmu_lock) { - struct kvm_s2_mmu *mmu; u64 val, hcr; bool fail; - mmu = &vcpu->kvm->arch.mmu; - val = hcr = read_sysreg(hcr_el2); val &= ~HCR_TGE; val |= HCR_VM; diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 05b6435d02a97..1ffbfd1c3cf2e 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -16,9 +16,13 @@ enum trap_behaviour { BEHAVE_HANDLE_LOCALLY = 0, + BEHAVE_FORWARD_READ = BIT(0), BEHAVE_FORWARD_WRITE = BIT(1), - BEHAVE_FORWARD_ANY = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE, + BEHAVE_FORWARD_RW = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE, + + /* Traps that take effect in Host EL0, this is rare! */ + BEHAVE_FORWARD_IN_HOST_EL0 = BIT(2), }; struct trap_bits { @@ -79,7 +83,6 @@ enum cgt_group_id { CGT_MDCR_E2TB, CGT_MDCR_TDCC, - CGT_CPACR_E0POE, CGT_CPTR_TAM, CGT_CPTR_TCPAC, @@ -106,6 +109,7 @@ enum cgt_group_id { CGT_HCR_TPU_TOCU, CGT_HCR_NV1_nNV2_ENSCXT, CGT_MDCR_TPM_TPMCR, + CGT_MDCR_TPM_HPMN, CGT_MDCR_TDE_TDA, CGT_MDCR_TDE_TDOSA, CGT_MDCR_TDE_TDRA, @@ -122,6 +126,7 @@ enum cgt_group_id { CGT_CNTHCTL_EL1PTEN, CGT_CPTR_TTA, + CGT_MDCR_HPMN, /* Must be last */ __NR_CGT_GROUP_IDS__ @@ -138,7 +143,7 @@ static const struct trap_bits coarse_trap_bits[] = { .index = HCR_EL2, .value = HCR_TID2, .mask = HCR_TID2, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TID3] = { .index = HCR_EL2, @@ -162,37 +167,37 @@ static const struct trap_bits coarse_trap_bits[] = { .index = HCR_EL2, .value = HCR_TIDCP, .mask = HCR_TIDCP, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TACR] = { .index = HCR_EL2, .value = HCR_TACR, .mask = HCR_TACR, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TSW] = { .index = HCR_EL2, .value = HCR_TSW, .mask = HCR_TSW, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TPC] = { /* Also called TCPC when FEAT_DPB is implemented */ .index = HCR_EL2, .value = HCR_TPC, .mask = HCR_TPC, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TPU] = { .index = HCR_EL2, .value = HCR_TPU, .mask = HCR_TPU, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TTLB] = { .index = HCR_EL2, .value = HCR_TTLB, .mask = HCR_TTLB, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TVM] = { .index = HCR_EL2, @@ -204,7 +209,7 @@ static const struct trap_bits coarse_trap_bits[] = { .index = HCR_EL2, .value = HCR_TDZ, .mask = HCR_TDZ, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TRVM] = { .index = HCR_EL2, @@ -216,205 +221,201 @@ static const struct trap_bits coarse_trap_bits[] = { .index = HCR_EL2, .value = HCR_TLOR, .mask = HCR_TLOR, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TERR] = { .index = HCR_EL2, .value = HCR_TERR, .mask = HCR_TERR, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_APK] = { .index = HCR_EL2, .value = 0, .mask = HCR_APK, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_NV] = { .index = HCR_EL2, .value = HCR_NV, .mask = HCR_NV, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_NV_nNV2] = { .index = HCR_EL2, .value = HCR_NV, .mask = HCR_NV | HCR_NV2, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_NV1_nNV2] = { .index = HCR_EL2, .value = HCR_NV | HCR_NV1, .mask = HCR_NV | HCR_NV1 | HCR_NV2, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_AT] = { .index = HCR_EL2, .value = HCR_AT, .mask = HCR_AT, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_nFIEN] = { .index = HCR_EL2, .value = 0, .mask = HCR_FIEN, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TID4] = { .index = HCR_EL2, .value = HCR_TID4, .mask = HCR_TID4, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TICAB] = { .index = HCR_EL2, .value = HCR_TICAB, .mask = HCR_TICAB, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TOCU] = { .index = HCR_EL2, .value = HCR_TOCU, .mask = HCR_TOCU, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_ENSCXT] = { .index = HCR_EL2, .value = 0, .mask = HCR_ENSCXT, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TTLBIS] = { .index = HCR_EL2, .value = HCR_TTLBIS, .mask = HCR_TTLBIS, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCR_TTLBOS] = { .index = HCR_EL2, .value = HCR_TTLBOS, .mask = HCR_TTLBOS, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TPMCR] = { .index = MDCR_EL2, .value = MDCR_EL2_TPMCR, .mask = MDCR_EL2_TPMCR, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW | + BEHAVE_FORWARD_IN_HOST_EL0, }, [CGT_MDCR_TPM] = { .index = MDCR_EL2, .value = MDCR_EL2_TPM, .mask = MDCR_EL2_TPM, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW | + BEHAVE_FORWARD_IN_HOST_EL0, }, [CGT_MDCR_TDE] = { .index = MDCR_EL2, .value = MDCR_EL2_TDE, .mask = MDCR_EL2_TDE, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TDA] = { .index = MDCR_EL2, .value = MDCR_EL2_TDA, .mask = MDCR_EL2_TDA, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TDOSA] = { .index = MDCR_EL2, .value = MDCR_EL2_TDOSA, .mask = MDCR_EL2_TDOSA, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TDRA] = { .index = MDCR_EL2, .value = MDCR_EL2_TDRA, .mask = MDCR_EL2_TDRA, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_E2PB] = { .index = MDCR_EL2, .value = 0, .mask = BIT(MDCR_EL2_E2PB_SHIFT), - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TPMS] = { .index = MDCR_EL2, .value = MDCR_EL2_TPMS, .mask = MDCR_EL2_TPMS, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TTRF] = { .index = MDCR_EL2, .value = MDCR_EL2_TTRF, .mask = MDCR_EL2_TTRF, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_E2TB] = { .index = MDCR_EL2, .value = 0, .mask = BIT(MDCR_EL2_E2TB_SHIFT), - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_MDCR_TDCC] = { .index = MDCR_EL2, .value = MDCR_EL2_TDCC, .mask = MDCR_EL2_TDCC, - .behaviour = BEHAVE_FORWARD_ANY, - }, - [CGT_CPACR_E0POE] = { - .index = CPTR_EL2, - .value = CPACR_ELx_E0POE, - .mask = CPACR_ELx_E0POE, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_CPTR_TAM] = { .index = CPTR_EL2, .value = CPTR_EL2_TAM, .mask = CPTR_EL2_TAM, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_CPTR_TCPAC] = { .index = CPTR_EL2, .value = CPTR_EL2_TCPAC, .mask = CPTR_EL2_TCPAC, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCRX_EnFPM] = { .index = HCRX_EL2, .value = 0, .mask = HCRX_EL2_EnFPM, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_HCRX_TCR2En] = { .index = HCRX_EL2, .value = 0, .mask = HCRX_EL2_TCR2En, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_ICH_HCR_TC] = { .index = ICH_HCR_EL2, .value = ICH_HCR_TC, .mask = ICH_HCR_TC, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_ICH_HCR_TALL0] = { .index = ICH_HCR_EL2, .value = ICH_HCR_TALL0, .mask = ICH_HCR_TALL0, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_ICH_HCR_TALL1] = { .index = ICH_HCR_EL2, .value = ICH_HCR_TALL1, .mask = ICH_HCR_TALL1, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, [CGT_ICH_HCR_TDIR] = { .index = ICH_HCR_EL2, .value = ICH_HCR_TDIR, .mask = ICH_HCR_TDIR, - .behaviour = BEHAVE_FORWARD_ANY, + .behaviour = BEHAVE_FORWARD_RW, }, }; @@ -435,6 +436,7 @@ static const enum cgt_group_id *coarse_control_combo[] = { MCB(CGT_HCR_TPU_TOCU, CGT_HCR_TPU, CGT_HCR_TOCU), MCB(CGT_HCR_NV1_nNV2_ENSCXT, CGT_HCR_NV1_nNV2, CGT_HCR_ENSCXT), MCB(CGT_MDCR_TPM_TPMCR, CGT_MDCR_TPM, CGT_MDCR_TPMCR), + MCB(CGT_MDCR_TPM_HPMN, CGT_MDCR_TPM, CGT_MDCR_HPMN), MCB(CGT_MDCR_TDE_TDA, CGT_MDCR_TDE, CGT_MDCR_TDA), MCB(CGT_MDCR_TDE_TDOSA, CGT_MDCR_TDE, CGT_MDCR_TDOSA), MCB(CGT_MDCR_TDE_TDRA, CGT_MDCR_TDE, CGT_MDCR_TDRA), @@ -474,7 +476,7 @@ static enum trap_behaviour check_cnthctl_el1pcten(struct kvm_vcpu *vcpu) if (get_sanitized_cnthctl(vcpu) & (CNTHCTL_EL1PCTEN << 10)) return BEHAVE_HANDLE_LOCALLY; - return BEHAVE_FORWARD_ANY; + return BEHAVE_FORWARD_RW; } static enum trap_behaviour check_cnthctl_el1pten(struct kvm_vcpu *vcpu) @@ -482,7 +484,7 @@ static enum trap_behaviour check_cnthctl_el1pten(struct kvm_vcpu *vcpu) if (get_sanitized_cnthctl(vcpu) & (CNTHCTL_EL1PCEN << 10)) return BEHAVE_HANDLE_LOCALLY; - return BEHAVE_FORWARD_ANY; + return BEHAVE_FORWARD_RW; } static enum trap_behaviour check_cptr_tta(struct kvm_vcpu *vcpu) @@ -493,7 +495,35 @@ static enum trap_behaviour check_cptr_tta(struct kvm_vcpu *vcpu) val = translate_cptr_el2_to_cpacr_el1(val); if (val & CPACR_ELx_TTA) - return BEHAVE_FORWARD_ANY; + return BEHAVE_FORWARD_RW; + + return BEHAVE_HANDLE_LOCALLY; +} + +static enum trap_behaviour check_mdcr_hpmn(struct kvm_vcpu *vcpu) +{ + u32 sysreg = esr_sys64_to_sysreg(kvm_vcpu_get_esr(vcpu)); + unsigned int idx; + + + switch (sysreg) { + case SYS_PMEVTYPERn_EL0(0) ... SYS_PMEVTYPERn_EL0(30): + case SYS_PMEVCNTRn_EL0(0) ... SYS_PMEVCNTRn_EL0(30): + idx = (sys_reg_CRm(sysreg) & 0x3) << 3 | sys_reg_Op2(sysreg); + break; + case SYS_PMXEVTYPER_EL0: + case SYS_PMXEVCNTR_EL0: + idx = SYS_FIELD_GET(PMSELR_EL0, SEL, + __vcpu_sys_reg(vcpu, PMSELR_EL0)); + break; + default: + /* Someone used this trap helper for something else... */ + KVM_BUG_ON(1, vcpu->kvm); + return BEHAVE_HANDLE_LOCALLY; + } + + if (kvm_pmu_counter_is_hyp(vcpu, idx)) + return BEHAVE_FORWARD_RW | BEHAVE_FORWARD_IN_HOST_EL0; return BEHAVE_HANDLE_LOCALLY; } @@ -505,6 +535,7 @@ static const complex_condition_check ccc[] = { CCC(CGT_CNTHCTL_EL1PCTEN, check_cnthctl_el1pcten), CCC(CGT_CNTHCTL_EL1PTEN, check_cnthctl_el1pten), CCC(CGT_CPTR_TTA, check_cptr_tta), + CCC(CGT_MDCR_HPMN, check_mdcr_hpmn), }; /* @@ -711,6 +742,10 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = { SR_TRAP(SYS_MAIR_EL1, CGT_HCR_TVM_TRVM), SR_TRAP(SYS_AMAIR_EL1, CGT_HCR_TVM_TRVM), SR_TRAP(SYS_CONTEXTIDR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_PIR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_PIRE0_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_POR_EL0, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_POR_EL1, CGT_HCR_TVM_TRVM), SR_TRAP(SYS_TCR2_EL1, CGT_HCR_TVM_TRVM_HCRX_TCR2En), SR_TRAP(SYS_DC_ZVA, CGT_HCR_TDZ), SR_TRAP(SYS_DC_GVA, CGT_HCR_TDZ), @@ -919,77 +954,77 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = { SR_TRAP(SYS_PMOVSCLR_EL0, CGT_MDCR_TPM), SR_TRAP(SYS_PMCEID0_EL0, CGT_MDCR_TPM), SR_TRAP(SYS_PMCEID1_EL0, CGT_MDCR_TPM), - SR_TRAP(SYS_PMXEVTYPER_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMXEVTYPER_EL0, CGT_MDCR_TPM_HPMN), SR_TRAP(SYS_PMSWINC_EL0, CGT_MDCR_TPM), SR_TRAP(SYS_PMSELR_EL0, CGT_MDCR_TPM), - SR_TRAP(SYS_PMXEVCNTR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMXEVCNTR_EL0, CGT_MDCR_TPM_HPMN), SR_TRAP(SYS_PMCCNTR_EL0, CGT_MDCR_TPM), SR_TRAP(SYS_PMUSERENR_EL0, CGT_MDCR_TPM), SR_TRAP(SYS_PMINTENSET_EL1, CGT_MDCR_TPM), SR_TRAP(SYS_PMINTENCLR_EL1, CGT_MDCR_TPM), SR_TRAP(SYS_PMMIR_EL1, CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(0), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(1), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(2), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(3), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(4), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(5), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(6), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(7), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(8), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(9), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(10), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(11), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(12), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(13), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(14), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(15), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(16), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(17), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(18), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(19), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(20), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(21), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(22), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(23), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(24), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(25), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(26), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(27), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(28), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(29), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVCNTRn_EL0(30), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(0), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(1), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(2), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(3), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(4), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(5), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(6), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(7), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(8), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(9), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(10), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(11), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(12), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(13), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(14), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(15), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(16), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(17), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(18), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(19), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(20), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(21), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(22), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(23), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(24), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(25), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(26), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(27), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(28), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(29), CGT_MDCR_TPM), - SR_TRAP(SYS_PMEVTYPERn_EL0(30), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(0), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(1), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(2), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(3), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(4), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(5), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(6), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(7), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(8), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(9), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(10), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(11), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(12), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(13), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(14), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(15), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(16), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(17), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(18), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(19), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(20), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(21), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(22), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(23), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(24), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(25), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(26), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(27), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(28), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(29), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVCNTRn_EL0(30), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(0), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(1), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(2), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(3), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(4), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(5), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(6), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(7), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(8), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(9), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(10), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(11), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(12), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(13), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(14), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(15), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(16), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(17), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(18), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(19), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(20), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(21), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(22), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(23), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(24), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(25), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(26), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(27), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(28), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(29), CGT_MDCR_TPM_HPMN), + SR_TRAP(SYS_PMEVTYPERn_EL0(30), CGT_MDCR_TPM_HPMN), SR_TRAP(SYS_PMCCFILTR_EL0, CGT_MDCR_TPM), SR_TRAP(SYS_MDCCSR_EL0, CGT_MDCR_TDCC_TDE_TDA), SR_TRAP(SYS_MDCCINT_EL1, CGT_MDCR_TDCC_TDE_TDA), @@ -1141,7 +1176,6 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = { SR_TRAP(SYS_AMEVTYPER1_EL0(13), CGT_CPTR_TAM), SR_TRAP(SYS_AMEVTYPER1_EL0(14), CGT_CPTR_TAM), SR_TRAP(SYS_AMEVTYPER1_EL0(15), CGT_CPTR_TAM), - SR_TRAP(SYS_POR_EL0, CGT_CPACR_E0POE), /* op0=2, op1=1, and CRn<0b1000 */ SR_RANGE_TRAP(sys_reg(2, 1, 0, 0, 0), sys_reg(2, 1, 7, 15, 7), CGT_CPTR_TTA), @@ -2021,7 +2055,8 @@ int __init populate_nv_trap_config(void) cgids = coarse_control_combo[id - __MULTIPLE_CONTROL_BITS__]; for (int i = 0; cgids[i] != __RESERVED__; i++) { - if (cgids[i] >= __MULTIPLE_CONTROL_BITS__) { + if (cgids[i] >= __MULTIPLE_CONTROL_BITS__ && + cgids[i] < __COMPLEX_CONDITIONS__) { kvm_err("Recursive MCB %d/%d\n", id, cgids[i]); ret = -EINVAL; } @@ -2126,11 +2161,19 @@ static u64 kvm_get_sysreg_res0(struct kvm *kvm, enum vcpu_sysreg sr) return masks->mask[sr - __VNCR_START__].res0; } -static bool check_fgt_bit(struct kvm *kvm, bool is_read, +static bool check_fgt_bit(struct kvm_vcpu *vcpu, bool is_read, u64 val, const union trap_config tc) { + struct kvm *kvm = vcpu->kvm; enum vcpu_sysreg sr; + /* + * KVM doesn't know about any FGTs that apply to the host, and hopefully + * that'll remain the case. + */ + if (is_hyp_ctxt(vcpu)) + return false; + if (tc.pol) return (val & BIT(tc.bit)); @@ -2207,7 +2250,15 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index) * If we're not nesting, immediately return to the caller, with the * sysreg index, should we have it. */ - if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu)) + if (!vcpu_has_nv(vcpu)) + goto local; + + /* + * There are a few traps that take effect InHost, but are constrained + * to EL0. Don't bother with computing the trap behaviour if the vCPU + * isn't in EL0. + */ + if (is_hyp_ctxt(vcpu) && !vcpu_is_host_el0(vcpu)) goto local; switch ((enum fgt_group_id)tc.fgt) { @@ -2253,12 +2304,14 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index) goto local; } - if (tc.fgt != __NO_FGT_GROUP__ && check_fgt_bit(vcpu->kvm, is_read, - val, tc)) + if (tc.fgt != __NO_FGT_GROUP__ && check_fgt_bit(vcpu, is_read, val, tc)) goto inject; b = compute_trap_behaviour(vcpu, tc); + if (!(b & BEHAVE_FORWARD_IN_HOST_EL0) && vcpu_is_host_el0(vcpu)) + goto local; + if (((b & BEHAVE_FORWARD_READ) && is_read) || ((b & BEHAVE_FORWARD_WRITE) && !is_read)) goto inject; @@ -2393,6 +2446,8 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu) kvm_arch_vcpu_load(vcpu, smp_processor_id()); preempt_enable(); + + kvm_pmu_nested_transition(vcpu); } static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2, @@ -2475,6 +2530,8 @@ static int kvm_inject_nested(struct kvm_vcpu *vcpu, u64 esr_el2, kvm_arch_vcpu_load(vcpu, smp_processor_id()); preempt_enable(); + kvm_pmu_nested_transition(vcpu); + return 1; } diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index e738a353b20e4..12dad841f2a51 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -1051,21 +1051,19 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm, } while (length > 0) { - kvm_pfn_t pfn = gfn_to_pfn_prot(kvm, gfn, write, NULL); + struct page *page = __gfn_to_page(kvm, gfn, write); void *maddr; unsigned long num_tags; - struct page *page; struct folio *folio; - if (is_error_noslot_pfn(pfn)) { + if (!page) { ret = -EFAULT; goto out; } - page = pfn_to_online_page(pfn); - if (!page) { + if (!pfn_to_online_page(page_to_pfn(page))) { /* Reject ZONE_DEVICE memory */ - kvm_release_pfn_clean(pfn); + kvm_release_page_unused(page); ret = -EFAULT; goto out; } @@ -1082,7 +1080,7 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm, /* No tags in memory, so write zeros */ num_tags = MTE_GRANULES_PER_PAGE - clear_user(tags, MTE_GRANULES_PER_PAGE); - kvm_release_pfn_clean(pfn); + kvm_release_page_clean(page); } else { /* * Only locking to serialise with a concurrent @@ -1104,7 +1102,7 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm, else set_page_mte_tagged(page); - kvm_release_pfn_dirty(pfn); + kvm_release_page_dirty(page); } if (num_tags != MTE_GRANULES_PER_PAGE) { diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 5310fe1da6165..34f53707892df 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -204,6 +204,35 @@ static inline void __deactivate_traps_hfgxtr(struct kvm_vcpu *vcpu) __deactivate_fgt(hctxt, vcpu, kvm, HAFGRTR_EL2); } +static inline void __activate_traps_mpam(struct kvm_vcpu *vcpu) +{ + u64 r = MPAM2_EL2_TRAPMPAM0EL1 | MPAM2_EL2_TRAPMPAM1EL1; + + if (!system_supports_mpam()) + return; + + /* trap guest access to MPAMIDR_EL1 */ + if (system_supports_mpam_hcr()) { + write_sysreg_s(MPAMHCR_EL2_TRAP_MPAMIDR_EL1, SYS_MPAMHCR_EL2); + } else { + /* From v1.1 TIDR can trap MPAMIDR, set it unconditionally */ + r |= MPAM2_EL2_TIDR; + } + + write_sysreg_s(r, SYS_MPAM2_EL2); +} + +static inline void __deactivate_traps_mpam(void) +{ + if (!system_supports_mpam()) + return; + + write_sysreg_s(0, SYS_MPAM2_EL2); + + if (system_supports_mpam_hcr()) + write_sysreg_s(MPAMHCR_HOST_FLAGS, SYS_MPAMHCR_EL2); +} + static inline void __activate_traps_common(struct kvm_vcpu *vcpu) { /* Trap on AArch32 cp15 c15 (impdef sysregs) accesses (EL1 or EL0) */ @@ -244,6 +273,7 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) } __activate_traps_hfgxtr(vcpu); + __activate_traps_mpam(vcpu); } static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) @@ -263,6 +293,7 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) write_sysreg_s(HCRX_HOST_FLAGS, SYS_HCRX_EL2); __deactivate_traps_hfgxtr(vcpu); + __deactivate_traps_mpam(); } static inline void ___activate_traps(struct kvm_vcpu *vcpu, u64 hcr) diff --git a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h index 1579a3c08a36b..a651c43ad679f 100644 --- a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h +++ b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h @@ -58,7 +58,7 @@ static inline bool ctxt_has_s1pie(struct kvm_cpu_context *ctxt) return false; vcpu = ctxt_to_vcpu(ctxt); - return kvm_has_feat(kern_hyp_va(vcpu->kvm), ID_AA64MMFR3_EL1, S1PIE, IMP); + return kvm_has_s1pie(kern_hyp_va(vcpu->kvm)); } static inline bool ctxt_has_tcrx(struct kvm_cpu_context *ctxt) @@ -69,7 +69,7 @@ static inline bool ctxt_has_tcrx(struct kvm_cpu_context *ctxt) return false; vcpu = ctxt_to_vcpu(ctxt); - return kvm_has_feat(kern_hyp_va(vcpu->kvm), ID_AA64MMFR3_EL1, TCRX, IMP); + return kvm_has_tcr2(kern_hyp_va(vcpu->kvm)); } static inline bool ctxt_has_s1poe(struct kvm_cpu_context *ctxt) @@ -80,7 +80,7 @@ static inline bool ctxt_has_s1poe(struct kvm_cpu_context *ctxt) return false; vcpu = ctxt_to_vcpu(ctxt); - return kvm_has_feat(kern_hyp_va(vcpu->kvm), ID_AA64MMFR3_EL1, S1POE, IMP); + return kvm_has_s1poe(kern_hyp_va(vcpu->kvm)); } static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) @@ -152,9 +152,10 @@ static inline void __sysreg_restore_user_state(struct kvm_cpu_context *ctxt) write_sysreg(ctxt_sys_reg(ctxt, TPIDRRO_EL0), tpidrro_el0); } -static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt) +static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt, + u64 mpidr) { - write_sysreg(ctxt_sys_reg(ctxt, MPIDR_EL1), vmpidr_el2); + write_sysreg(mpidr, vmpidr_el2); if (has_vhe() || !cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) { diff --git a/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h b/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h index 45a84f0ade04b..1e6d995968a1f 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h +++ b/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h @@ -15,6 +15,4 @@ #define DECLARE_REG(type, name, ctxt, reg) \ type name = (type)cpu_reg(ctxt, (reg)) -void __pkvm_vcpu_init_traps(struct kvm_vcpu *vcpu); - #endif /* __ARM64_KVM_NVHE_TRAP_HANDLER_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index fefc89209f9e4..6aa0b13d86e58 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -105,8 +105,10 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.hw_mmu = host_vcpu->arch.hw_mmu; - hyp_vcpu->vcpu.arch.hcr_el2 = host_vcpu->arch.hcr_el2; hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; + hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE); + hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & + (HCR_TWI | HCR_TWE); hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags; @@ -349,13 +351,6 @@ static void handle___pkvm_prot_finalize(struct kvm_cpu_context *host_ctxt) cpu_reg(host_ctxt, 1) = __pkvm_prot_finalize(); } -static void handle___pkvm_vcpu_init_traps(struct kvm_cpu_context *host_ctxt) -{ - DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1); - - __pkvm_vcpu_init_traps(kern_hyp_va(vcpu)); -} - static void handle___pkvm_init_vm(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1); @@ -411,7 +406,6 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__kvm_timer_set_cntvoff), HANDLE_FUNC(__vgic_v3_save_vmcr_aprs), HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs), - HANDLE_FUNC(__pkvm_vcpu_init_traps), HANDLE_FUNC(__pkvm_init_vm), HANDLE_FUNC(__pkvm_init_vcpu), HANDLE_FUNC(__pkvm_teardown_vm), diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 077d4098548d2..01616c39a8107 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -6,6 +6,9 @@ #include #include + +#include + #include #include #include @@ -201,11 +204,46 @@ static void pvm_init_trap_regs(struct kvm_vcpu *vcpu) } } +static void pkvm_vcpu_reset_hcr(struct kvm_vcpu *vcpu) +{ + vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS; + + if (has_hvhe()) + vcpu->arch.hcr_el2 |= HCR_E2H; + + if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) { + /* route synchronous external abort exceptions to EL2 */ + vcpu->arch.hcr_el2 |= HCR_TEA; + /* trap error record accesses */ + vcpu->arch.hcr_el2 |= HCR_TERR; + } + + if (cpus_have_final_cap(ARM64_HAS_STAGE2_FWB)) + vcpu->arch.hcr_el2 |= HCR_FWB; + + if (cpus_have_final_cap(ARM64_HAS_EVT) && + !cpus_have_final_cap(ARM64_MISMATCHED_CACHE_TYPE)) + vcpu->arch.hcr_el2 |= HCR_TID4; + else + vcpu->arch.hcr_el2 |= HCR_TID2; + + if (vcpu_has_ptrauth(vcpu)) + vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK); +} + /* * Initialize trap register values in protected mode. */ -void __pkvm_vcpu_init_traps(struct kvm_vcpu *vcpu) +static void pkvm_vcpu_init_traps(struct kvm_vcpu *vcpu) { + vcpu->arch.cptr_el2 = kvm_get_reset_cptr_el2(vcpu); + vcpu->arch.mdcr_el2 = 0; + + pkvm_vcpu_reset_hcr(vcpu); + + if ((!vcpu_is_protected(vcpu))) + return; + pvm_init_trap_regs(vcpu); pvm_init_traps_aa64pfr0(vcpu); pvm_init_traps_aa64pfr1(vcpu); @@ -289,6 +327,65 @@ void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_spin_unlock(&vm_table_lock); } +static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struct kvm *host_kvm) +{ + struct kvm *kvm = &hyp_vm->kvm; + DECLARE_BITMAP(allowed_features, KVM_VCPU_MAX_FEATURES); + + /* No restrictions for non-protected VMs. */ + if (!kvm_vm_is_protected(kvm)) { + bitmap_copy(kvm->arch.vcpu_features, + host_kvm->arch.vcpu_features, + KVM_VCPU_MAX_FEATURES); + return; + } + + bitmap_zero(allowed_features, KVM_VCPU_MAX_FEATURES); + + /* + * For protected VMs, always allow: + * - CPU starting in poweroff state + * - PSCI v0.2 + */ + set_bit(KVM_ARM_VCPU_POWER_OFF, allowed_features); + set_bit(KVM_ARM_VCPU_PSCI_0_2, allowed_features); + + /* + * Check if remaining features are allowed: + * - Performance Monitoring + * - Scalable Vectors + * - Pointer Authentication + */ + if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), PVM_ID_AA64DFR0_ALLOW)) + set_bit(KVM_ARM_VCPU_PMU_V3, allowed_features); + + if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE), PVM_ID_AA64PFR0_ALLOW)) + set_bit(KVM_ARM_VCPU_SVE, allowed_features); + + if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_API), PVM_ID_AA64ISAR1_RESTRICT_UNSIGNED) && + FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_APA), PVM_ID_AA64ISAR1_RESTRICT_UNSIGNED)) + set_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, allowed_features); + + if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPI), PVM_ID_AA64ISAR1_ALLOW) && + FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPA), PVM_ID_AA64ISAR1_ALLOW)) + set_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, allowed_features); + + bitmap_and(kvm->arch.vcpu_features, host_kvm->arch.vcpu_features, + allowed_features, KVM_VCPU_MAX_FEATURES); +} + +static void pkvm_vcpu_init_ptrauth(struct pkvm_hyp_vcpu *hyp_vcpu) +{ + struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; + + if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PTRAUTH_ADDRESS) || + vcpu_has_feature(vcpu, KVM_ARM_VCPU_PTRAUTH_GENERIC)) { + kvm_vcpu_enable_ptrauth(vcpu); + } else { + vcpu_clear_flag(&hyp_vcpu->vcpu, GUEST_HAS_PTRAUTH); + } +} + static void unpin_host_vcpu(struct kvm_vcpu *host_vcpu) { if (host_vcpu) @@ -310,6 +407,18 @@ static void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm, hyp_vm->host_kvm = host_kvm; hyp_vm->kvm.created_vcpus = nr_vcpus; hyp_vm->kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr; + hyp_vm->kvm.arch.pkvm.enabled = READ_ONCE(host_kvm->arch.pkvm.enabled); + pkvm_init_features_from_host(hyp_vm, host_kvm); +} + +static void pkvm_vcpu_init_sve(struct pkvm_hyp_vcpu *hyp_vcpu, struct kvm_vcpu *host_vcpu) +{ + struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; + + if (!vcpu_has_feature(vcpu, KVM_ARM_VCPU_SVE)) { + vcpu_clear_flag(vcpu, GUEST_HAS_SVE); + vcpu_clear_flag(vcpu, VCPU_SVE_FINALIZED); + } } static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, @@ -335,6 +444,11 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, hyp_vcpu->vcpu.arch.hw_mmu = &hyp_vm->kvm.arch.mmu; hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags); + hyp_vcpu->vcpu.arch.mp_state.mp_state = KVM_MP_STATE_STOPPED; + + pkvm_vcpu_init_sve(hyp_vcpu, host_vcpu); + pkvm_vcpu_init_ptrauth(hyp_vcpu); + pkvm_vcpu_init_traps(&hyp_vcpu->vcpu); done: if (ret) unpin_host_vcpu(host_vcpu); diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c b/arch/arm64/kvm/hyp/nvhe/psci-relay.c index dfe8fe0f7eaff..9c2ce1e0e99a5 100644 --- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c +++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c @@ -265,6 +265,8 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_ case PSCI_1_0_FN_PSCI_FEATURES: case PSCI_1_0_FN_SET_SUSPEND_MODE: case PSCI_1_1_FN64_SYSTEM_RESET2: + case PSCI_1_3_FN_SYSTEM_OFF2: + case PSCI_1_3_FN64_SYSTEM_OFF2: return psci_forward(host_ctxt); case PSCI_1_0_FN64_SYSTEM_SUSPEND: return psci_system_suspend(func_id, host_ctxt); diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index 174007f3faddd..cbdd18cd3f984 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -95,7 +95,6 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, { void *start, *end, *virt = hyp_phys_to_virt(phys); unsigned long pgt_size = hyp_s1_pgtable_pages() << PAGE_SHIFT; - enum kvm_pgtable_prot prot; int ret, i; /* Recreate the hyp page-table using the early page allocator */ @@ -147,24 +146,7 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, return ret; } - pkvm_create_host_sve_mappings(); - - /* - * Map the host sections RO in the hypervisor, but transfer the - * ownership from the host to the hypervisor itself to make sure they - * can't be donated or shared with another entity. - * - * The ownership transition requires matching changes in the host - * stage-2. This will be done later (see finalize_host_mappings()) once - * the hyp_vmemmap is addressable. - */ - prot = pkvm_mkstate(PAGE_HYP_RO, PKVM_PAGE_SHARED_OWNED); - ret = pkvm_create_mappings(&kvm_vgic_global_state, - &kvm_vgic_global_state + 1, prot); - if (ret) - return ret; - - return 0; + return pkvm_create_host_sve_mappings(); } static void update_nvhe_init_params(void) diff --git a/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c b/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c index 29305022bc048..dba101565de36 100644 --- a/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c +++ b/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c @@ -28,7 +28,7 @@ void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt) void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt) { - __sysreg_restore_el1_state(ctxt); + __sysreg_restore_el1_state(ctxt, ctxt_sys_reg(ctxt, MPIDR_EL1)); __sysreg_restore_common_state(ctxt); __sysreg_restore_user_state(ctxt); __sysreg_restore_el2_return_state(ctxt); diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index b11bcebac908a..40bd559665404 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1245,19 +1245,16 @@ int kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size) NULL, NULL, 0); } -kvm_pte_t kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr) +void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr) { - kvm_pte_t pte = 0; int ret; ret = stage2_update_leaf_attrs(pgt, addr, 1, KVM_PTE_LEAF_ATTR_LO_S2_AF, 0, - &pte, NULL, + NULL, NULL, KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED); if (!ret) dsb(ishst); - - return pte; } struct stage2_age_data { diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c index 18d4677002b1a..3f9741e51d41b 100644 --- a/arch/arm64/kvm/hyp/vgic-v3-sr.c +++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c @@ -1012,9 +1012,6 @@ static void __vgic_v3_read_ctlr(struct kvm_vcpu *vcpu, u32 vmcr, int rt) val = ((vtr >> 29) & 7) << ICC_CTLR_EL1_PRI_BITS_SHIFT; /* IDbits */ val |= ((vtr >> 23) & 7) << ICC_CTLR_EL1_ID_BITS_SHIFT; - /* SEIS */ - if (kvm_vgic_global_state.ich_vtr_el2 & ICH_VTR_SEIS_MASK) - val |= BIT(ICC_CTLR_EL1_SEIS_SHIFT); /* A3V */ val |= ((vtr >> 21) & 1) << ICC_CTLR_EL1_A3V_SHIFT; /* EOImode */ diff --git a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c index e12bd7d6d2dce..5f78a39053a79 100644 --- a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c +++ b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c @@ -15,6 +15,131 @@ #include #include +static void __sysreg_save_vel2_state(struct kvm_vcpu *vcpu) +{ + /* These registers are common with EL1 */ + __vcpu_sys_reg(vcpu, PAR_EL1) = read_sysreg(par_el1); + __vcpu_sys_reg(vcpu, TPIDR_EL1) = read_sysreg(tpidr_el1); + + __vcpu_sys_reg(vcpu, ESR_EL2) = read_sysreg_el1(SYS_ESR); + __vcpu_sys_reg(vcpu, AFSR0_EL2) = read_sysreg_el1(SYS_AFSR0); + __vcpu_sys_reg(vcpu, AFSR1_EL2) = read_sysreg_el1(SYS_AFSR1); + __vcpu_sys_reg(vcpu, FAR_EL2) = read_sysreg_el1(SYS_FAR); + __vcpu_sys_reg(vcpu, MAIR_EL2) = read_sysreg_el1(SYS_MAIR); + __vcpu_sys_reg(vcpu, VBAR_EL2) = read_sysreg_el1(SYS_VBAR); + __vcpu_sys_reg(vcpu, CONTEXTIDR_EL2) = read_sysreg_el1(SYS_CONTEXTIDR); + __vcpu_sys_reg(vcpu, AMAIR_EL2) = read_sysreg_el1(SYS_AMAIR); + + /* + * In VHE mode those registers are compatible between EL1 and EL2, + * and the guest uses the _EL1 versions on the CPU naturally. + * So we save them into their _EL2 versions here. + * For nVHE mode we trap accesses to those registers, so our + * _EL2 copy in sys_regs[] is always up-to-date and we don't need + * to save anything here. + */ + if (vcpu_el2_e2h_is_set(vcpu)) { + u64 val; + + /* + * We don't save CPTR_EL2, as accesses to CPACR_EL1 + * are always trapped, ensuring that the in-memory + * copy is always up-to-date. A small blessing... + */ + __vcpu_sys_reg(vcpu, SCTLR_EL2) = read_sysreg_el1(SYS_SCTLR); + __vcpu_sys_reg(vcpu, TTBR0_EL2) = read_sysreg_el1(SYS_TTBR0); + __vcpu_sys_reg(vcpu, TTBR1_EL2) = read_sysreg_el1(SYS_TTBR1); + __vcpu_sys_reg(vcpu, TCR_EL2) = read_sysreg_el1(SYS_TCR); + + if (ctxt_has_tcrx(&vcpu->arch.ctxt)) { + __vcpu_sys_reg(vcpu, TCR2_EL2) = read_sysreg_el1(SYS_TCR2); + + if (ctxt_has_s1pie(&vcpu->arch.ctxt)) { + __vcpu_sys_reg(vcpu, PIRE0_EL2) = read_sysreg_el1(SYS_PIRE0); + __vcpu_sys_reg(vcpu, PIR_EL2) = read_sysreg_el1(SYS_PIR); + } + + if (ctxt_has_s1poe(&vcpu->arch.ctxt)) + __vcpu_sys_reg(vcpu, POR_EL2) = read_sysreg_el1(SYS_POR); + } + + /* + * The EL1 view of CNTKCTL_EL1 has a bunch of RES0 bits where + * the interesting CNTHCTL_EL2 bits live. So preserve these + * bits when reading back the guest-visible value. + */ + val = read_sysreg_el1(SYS_CNTKCTL); + val &= CNTKCTL_VALID_BITS; + __vcpu_sys_reg(vcpu, CNTHCTL_EL2) &= ~CNTKCTL_VALID_BITS; + __vcpu_sys_reg(vcpu, CNTHCTL_EL2) |= val; + } + + __vcpu_sys_reg(vcpu, SP_EL2) = read_sysreg(sp_el1); + __vcpu_sys_reg(vcpu, ELR_EL2) = read_sysreg_el1(SYS_ELR); + __vcpu_sys_reg(vcpu, SPSR_EL2) = read_sysreg_el1(SYS_SPSR); +} + +static void __sysreg_restore_vel2_state(struct kvm_vcpu *vcpu) +{ + u64 val; + + /* These registers are common with EL1 */ + write_sysreg(__vcpu_sys_reg(vcpu, PAR_EL1), par_el1); + write_sysreg(__vcpu_sys_reg(vcpu, TPIDR_EL1), tpidr_el1); + + write_sysreg(__vcpu_sys_reg(vcpu, MPIDR_EL1), vmpidr_el2); + write_sysreg_el1(__vcpu_sys_reg(vcpu, MAIR_EL2), SYS_MAIR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, VBAR_EL2), SYS_VBAR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, CONTEXTIDR_EL2), SYS_CONTEXTIDR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, AMAIR_EL2), SYS_AMAIR); + + if (vcpu_el2_e2h_is_set(vcpu)) { + /* + * In VHE mode those registers are compatible between + * EL1 and EL2. + */ + write_sysreg_el1(__vcpu_sys_reg(vcpu, SCTLR_EL2), SYS_SCTLR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, CPTR_EL2), SYS_CPACR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, TTBR0_EL2), SYS_TTBR0); + write_sysreg_el1(__vcpu_sys_reg(vcpu, TTBR1_EL2), SYS_TTBR1); + write_sysreg_el1(__vcpu_sys_reg(vcpu, TCR_EL2), SYS_TCR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, CNTHCTL_EL2), SYS_CNTKCTL); + } else { + /* + * CNTHCTL_EL2 only affects EL1 when running nVHE, so + * no need to restore it. + */ + val = translate_sctlr_el2_to_sctlr_el1(__vcpu_sys_reg(vcpu, SCTLR_EL2)); + write_sysreg_el1(val, SYS_SCTLR); + val = translate_cptr_el2_to_cpacr_el1(__vcpu_sys_reg(vcpu, CPTR_EL2)); + write_sysreg_el1(val, SYS_CPACR); + val = translate_ttbr0_el2_to_ttbr0_el1(__vcpu_sys_reg(vcpu, TTBR0_EL2)); + write_sysreg_el1(val, SYS_TTBR0); + val = translate_tcr_el2_to_tcr_el1(__vcpu_sys_reg(vcpu, TCR_EL2)); + write_sysreg_el1(val, SYS_TCR); + } + + if (ctxt_has_tcrx(&vcpu->arch.ctxt)) { + write_sysreg_el1(__vcpu_sys_reg(vcpu, TCR2_EL2), SYS_TCR2); + + if (ctxt_has_s1pie(&vcpu->arch.ctxt)) { + write_sysreg_el1(__vcpu_sys_reg(vcpu, PIR_EL2), SYS_PIR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, PIRE0_EL2), SYS_PIRE0); + } + + if (ctxt_has_s1poe(&vcpu->arch.ctxt)) + write_sysreg_el1(__vcpu_sys_reg(vcpu, POR_EL2), SYS_POR); + } + + write_sysreg_el1(__vcpu_sys_reg(vcpu, ESR_EL2), SYS_ESR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, AFSR0_EL2), SYS_AFSR0); + write_sysreg_el1(__vcpu_sys_reg(vcpu, AFSR1_EL2), SYS_AFSR1); + write_sysreg_el1(__vcpu_sys_reg(vcpu, FAR_EL2), SYS_FAR); + write_sysreg(__vcpu_sys_reg(vcpu, SP_EL2), sp_el1); + write_sysreg_el1(__vcpu_sys_reg(vcpu, ELR_EL2), SYS_ELR); + write_sysreg_el1(__vcpu_sys_reg(vcpu, SPSR_EL2), SYS_SPSR); +} + /* * VHE: Host and guest must save mdscr_el1 and sp_el0 (and the PC and * pstate, which are handled as part of the el2 return state) on every @@ -66,6 +191,7 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu) { struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt; struct kvm_cpu_context *host_ctxt; + u64 mpidr; host_ctxt = host_data_ptr(host_ctxt); __sysreg_save_user_state(host_ctxt); @@ -89,7 +215,29 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu) */ __sysreg32_restore_state(vcpu); __sysreg_restore_user_state(guest_ctxt); - __sysreg_restore_el1_state(guest_ctxt); + + if (unlikely(__is_hyp_ctxt(guest_ctxt))) { + __sysreg_restore_vel2_state(vcpu); + } else { + if (vcpu_has_nv(vcpu)) { + /* + * Use the guest hypervisor's VPIDR_EL2 when in a + * nested state. The hardware value of MIDR_EL1 gets + * restored on put. + */ + write_sysreg(ctxt_sys_reg(guest_ctxt, VPIDR_EL2), vpidr_el2); + + /* + * As we're restoring a nested guest, set the value + * provided by the guest hypervisor. + */ + mpidr = ctxt_sys_reg(guest_ctxt, VMPIDR_EL2); + } else { + mpidr = ctxt_sys_reg(guest_ctxt, MPIDR_EL1); + } + + __sysreg_restore_el1_state(guest_ctxt, mpidr); + } vcpu_set_flag(vcpu, SYSREGS_ON_CPU); } @@ -112,12 +260,20 @@ void __vcpu_put_switch_sysregs(struct kvm_vcpu *vcpu) host_ctxt = host_data_ptr(host_ctxt); - __sysreg_save_el1_state(guest_ctxt); + if (unlikely(__is_hyp_ctxt(guest_ctxt))) + __sysreg_save_vel2_state(vcpu); + else + __sysreg_save_el1_state(guest_ctxt); + __sysreg_save_user_state(guest_ctxt); __sysreg32_save_state(vcpu); /* Restore host user state */ __sysreg_restore_user_state(host_ctxt); + /* If leaving a nesting guest, restore MIDR_EL1 default view */ + if (vcpu_has_nv(vcpu)) + write_sysreg(read_cpuid_id(), vpidr_el2); + vcpu_clear_flag(vcpu, SYSREGS_ON_CPU); } diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index ee6573befb813..27ce4cb449049 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -575,6 +575,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) case KVM_ARM_PSCI_0_2: case KVM_ARM_PSCI_1_0: case KVM_ARM_PSCI_1_1: + case KVM_ARM_PSCI_1_2: + case KVM_ARM_PSCI_1_3: if (!wants_02) return -EINVAL; vcpu->kvm->arch.psci_version = val; diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c index cd6b7b83e2c37..ab365e839874e 100644 --- a/arch/arm64/kvm/mmio.c +++ b/arch/arm64/kvm/mmio.c @@ -72,6 +72,31 @@ unsigned long kvm_mmio_read_buf(const void *buf, unsigned int len) return data; } +static bool kvm_pending_sync_exception(struct kvm_vcpu *vcpu) +{ + if (!vcpu_get_flag(vcpu, PENDING_EXCEPTION)) + return false; + + if (vcpu_el1_is_32bit(vcpu)) { + switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) { + case unpack_vcpu_flag(EXCEPT_AA32_UND): + case unpack_vcpu_flag(EXCEPT_AA32_IABT): + case unpack_vcpu_flag(EXCEPT_AA32_DABT): + return true; + default: + return false; + } + } else { + switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) { + case unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC): + case unpack_vcpu_flag(EXCEPT_AA64_EL2_SYNC): + return true; + default: + return false; + } + } +} + /** * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation * or in-kernel IO emulation @@ -84,8 +109,11 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu) unsigned int len; int mask; - /* Detect an already handled MMIO return */ - if (unlikely(!vcpu->mmio_needed)) + /* + * Detect if the MMIO return was already handled or if userspace aborted + * the MMIO access. + */ + if (unlikely(!vcpu->mmio_needed || kvm_pending_sync_exception(vcpu))) return 1; vcpu->mmio_needed = 0; diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 56d9a7f414fe1..c9d46ad57e52d 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1451,6 +1451,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, long vma_pagesize, fault_granule; enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R; struct kvm_pgtable *pgt; + struct page *page; if (fault_is_perm) fault_granule = kvm_vcpu_trap_get_perm_fault_granule(vcpu); @@ -1572,7 +1573,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, /* * Read mmu_invalidate_seq so that KVM can detect if the results of - * vma_lookup() or __gfn_to_pfn_memslot() become stale prior to + * vma_lookup() or __kvm_faultin_pfn() become stale prior to * acquiring kvm->mmu_lock. * * Rely on mmap_read_unlock() for an implicit smp_rmb(), which pairs @@ -1581,8 +1582,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, mmu_seq = vcpu->kvm->mmu_invalidate_seq; mmap_read_unlock(current->mm); - pfn = __gfn_to_pfn_memslot(memslot, gfn, false, false, NULL, - write_fault, &writable, NULL); + pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, + &writable, &page); if (pfn == KVM_PFN_ERR_HWPOISON) { kvm_send_hwpoison_signal(hva, vma_shift); return 0; @@ -1595,7 +1596,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, * If the page was identified as device early by looking at * the VMA flags, vma_pagesize is already representing the * largest quantity we can map. If instead it was mapped - * via gfn_to_pfn_prot(), vma_pagesize is set to PAGE_SIZE + * via __kvm_faultin_pfn(), vma_pagesize is set to PAGE_SIZE * and must not be upgraded. * * In both cases, we don't let transparent_hugepage_adjust() @@ -1704,33 +1705,27 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, } out_unlock: + kvm_release_faultin_page(kvm, page, !!ret, writable); read_unlock(&kvm->mmu_lock); /* Mark the page dirty only if the fault is handled successfully */ - if (writable && !ret) { - kvm_set_pfn_dirty(pfn); + if (writable && !ret) mark_page_dirty_in_slot(kvm, memslot, gfn); - } - kvm_release_pfn_clean(pfn); return ret != -EAGAIN ? ret : 0; } /* Resolve the access fault by making the page young again. */ static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) { - kvm_pte_t pte; struct kvm_s2_mmu *mmu; trace_kvm_access_fault(fault_ipa); read_lock(&vcpu->kvm->mmu_lock); mmu = vcpu->arch.hw_mmu; - pte = kvm_pgtable_stage2_mkyoung(mmu->pgt, fault_ipa); + kvm_pgtable_stage2_mkyoung(mmu->pgt, fault_ipa); read_unlock(&vcpu->kvm->mmu_lock); - - if (kvm_pte_valid(pte)) - kvm_set_pfn_accessed(kvm_pte_to_pfn(pte)); } /** diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index c4b17d90fc49d..9b36218b48def 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -917,12 +917,13 @@ static void limit_nv_id_regs(struct kvm *kvm) ID_AA64MMFR4_EL1_E2H0_NI_NV1); kvm_set_vm_id_reg(kvm, SYS_ID_AA64MMFR4_EL1, val); - /* Only limited support for PMU, Debug, BPs and WPs */ + /* Only limited support for PMU, Debug, BPs, WPs, and HPMN0 */ val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1); val &= (NV_FTR(DFR0, PMUVer) | NV_FTR(DFR0, WRPs) | NV_FTR(DFR0, BRPs) | - NV_FTR(DFR0, DebugVer)); + NV_FTR(DFR0, DebugVer) | + NV_FTR(DFR0, HPMN0)); /* Cap Debug to ARMv8.1 */ tmp = FIELD_GET(NV_FTR(DFR0, DebugVer), val); @@ -933,15 +934,15 @@ static void limit_nv_id_regs(struct kvm *kvm) kvm_set_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1, val); } -u64 kvm_vcpu_sanitise_vncr_reg(const struct kvm_vcpu *vcpu, enum vcpu_sysreg sr) +u64 kvm_vcpu_apply_reg_masks(const struct kvm_vcpu *vcpu, + enum vcpu_sysreg sr, u64 v) { - u64 v = ctxt_sys_reg(&vcpu->arch.ctxt, sr); struct kvm_sysreg_masks *masks; masks = vcpu->kvm->arch.sysreg_masks; if (masks) { - sr -= __VNCR_START__; + sr -= __SANITISED_REG_START__; v &= ~masks->mask[sr].res0; v |= masks->mask[sr].res1; @@ -950,9 +951,13 @@ u64 kvm_vcpu_sanitise_vncr_reg(const struct kvm_vcpu *vcpu, enum vcpu_sysreg sr) return v; } -static void set_sysreg_masks(struct kvm *kvm, int sr, u64 res0, u64 res1) +static __always_inline void set_sysreg_masks(struct kvm *kvm, int sr, u64 res0, u64 res1) { - int i = sr - __VNCR_START__; + int i = sr - __SANITISED_REG_START__; + + BUILD_BUG_ON(!__builtin_constant_p(sr)); + BUILD_BUG_ON(sr < __SANITISED_REG_START__); + BUILD_BUG_ON(sr >= NR_SYS_REGS); kvm->arch.sysreg_masks->mask[i].res0 = res0; kvm->arch.sysreg_masks->mask[i].res1 = res1; @@ -1050,7 +1055,7 @@ int kvm_init_nv_sysregs(struct kvm *kvm) res0 |= HCRX_EL2_PTTWI; if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, SCTLRX, IMP)) res0 |= HCRX_EL2_SCTLR2En; - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, TCRX, IMP)) + if (!kvm_has_tcr2(kvm)) res0 |= HCRX_EL2_TCR2En; if (!kvm_has_feat(kvm, ID_AA64ISAR2_EL1, MOPS, IMP)) res0 |= (HCRX_EL2_MSCEn | HCRX_EL2_MCE2); @@ -1101,9 +1106,9 @@ int kvm_init_nv_sysregs(struct kvm *kvm) res0 |= (HFGxTR_EL2_nSMPRI_EL1 | HFGxTR_EL2_nTPIDR2_EL0); if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, THE, IMP)) res0 |= HFGxTR_EL2_nRCWMASK_EL1; - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S1PIE, IMP)) + if (!kvm_has_s1pie(kvm)) res0 |= (HFGxTR_EL2_nPIRE0_EL1 | HFGxTR_EL2_nPIR_EL1); - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S1POE, IMP)) + if (!kvm_has_s1poe(kvm)) res0 |= (HFGxTR_EL2_nPOR_EL0 | HFGxTR_EL2_nPOR_EL1); if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S2POE, IMP)) res0 |= HFGxTR_EL2_nS2POR_EL1; @@ -1200,6 +1205,28 @@ int kvm_init_nv_sysregs(struct kvm *kvm) res0 |= ~(res0 | res1); set_sysreg_masks(kvm, HAFGRTR_EL2, res0, res1); + /* TCR2_EL2 */ + res0 = TCR2_EL2_RES0; + res1 = TCR2_EL2_RES1; + if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, D128, IMP)) + res0 |= (TCR2_EL2_DisCH0 | TCR2_EL2_DisCH1 | TCR2_EL2_D128); + if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, MEC, IMP)) + res0 |= TCR2_EL2_AMEC1 | TCR2_EL2_AMEC0; + if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, HAFDBS, HAFT)) + res0 |= TCR2_EL2_HAFT; + if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, THE, IMP)) + res0 |= TCR2_EL2_PTTWI | TCR2_EL2_PnCH; + if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, AIE, IMP)) + res0 |= TCR2_EL2_AIE; + if (!kvm_has_s1poe(kvm)) + res0 |= TCR2_EL2_POE | TCR2_EL2_E0POE; + if (!kvm_has_s1pie(kvm)) + res0 |= TCR2_EL2_PIE; + if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, VH, IMP)) + res0 |= (TCR2_EL2_E0POE | TCR2_EL2_D128 | + TCR2_EL2_AMEC1 | TCR2_EL2_DisCH0 | TCR2_EL2_DisCH1); + set_sysreg_masks(kvm, TCR2_EL2, res0, res1); + /* SCTLR_EL1 */ res0 = SCTLR_EL1_RES0; res1 = SCTLR_EL1_RES1; @@ -1207,6 +1234,43 @@ int kvm_init_nv_sysregs(struct kvm *kvm) res0 |= SCTLR_EL1_EPAN; set_sysreg_masks(kvm, SCTLR_EL1, res0, res1); + /* MDCR_EL2 */ + res0 = MDCR_EL2_RES0; + res1 = MDCR_EL2_RES1; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, IMP)) + res0 |= (MDCR_EL2_HPMN | MDCR_EL2_TPMCR | + MDCR_EL2_TPM | MDCR_EL2_HPME); + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, IMP)) + res0 |= MDCR_EL2_E2PB | MDCR_EL2_TPMS; + if (!kvm_has_feat(kvm, ID_AA64DFR1_EL1, SPMU, IMP)) + res0 |= MDCR_EL2_EnSPM; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, V3P1)) + res0 |= MDCR_EL2_HPMD; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceFilt, IMP)) + res0 |= MDCR_EL2_TTRF; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, V3P5)) + res0 |= MDCR_EL2_HCCD | MDCR_EL2_HLP; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceBuffer, IMP)) + res0 |= MDCR_EL2_E2TB; + if (!kvm_has_feat(kvm, ID_AA64MMFR0_EL1, FGT, IMP)) + res0 |= MDCR_EL2_TDCC; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, MTPMU, IMP) || + kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL3, IMP)) + res0 |= MDCR_EL2_MTPME; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, V3P7)) + res0 |= MDCR_EL2_HPMFZO; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSS, IMP)) + res0 |= MDCR_EL2_PMSSE; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, V1P2)) + res0 |= MDCR_EL2_HPMFZS; + if (!kvm_has_feat(kvm, ID_AA64DFR1_EL1, EBEP, IMP)) + res0 |= MDCR_EL2_PMEE; + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, DebugVer, V8P9)) + res0 |= MDCR_EL2_EBWE; + if (!kvm_has_feat(kvm, ID_AA64DFR2_EL1, STEP, IMP)) + res0 |= MDCR_EL2_EnSTEPOP; + set_sysreg_masks(kvm, MDCR_EL2, res0, res1); + return 0; } diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index ac36c438b8c18..456102bc0b555 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -89,7 +89,11 @@ static bool kvm_pmc_is_64bit(struct kvm_pmc *pmc) static bool kvm_pmc_has_64bit_overflow(struct kvm_pmc *pmc) { - u64 val = kvm_vcpu_read_pmcr(kvm_pmc_to_vcpu(pmc)); + struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc); + u64 val = kvm_vcpu_read_pmcr(vcpu); + + if (kvm_pmu_counter_is_hyp(vcpu, pmc->idx)) + return __vcpu_sys_reg(vcpu, MDCR_EL2) & MDCR_EL2_HLP; return (pmc->idx < ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LP)) || (pmc->idx == ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LC)); @@ -111,6 +115,11 @@ static u32 counter_index_to_evtreg(u64 idx) return (idx == ARMV8_PMU_CYCLE_IDX) ? PMCCFILTR_EL0 : PMEVTYPER0_EL0 + idx; } +static u64 kvm_pmc_read_evtreg(const struct kvm_pmc *pmc) +{ + return __vcpu_sys_reg(kvm_pmc_to_vcpu(pmc), counter_index_to_evtreg(pmc->idx)); +} + static u64 kvm_pmu_get_pmc_value(struct kvm_pmc *pmc) { struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc); @@ -244,7 +253,7 @@ void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu) */ void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu) { - unsigned long mask = kvm_pmu_valid_counter_mask(vcpu); + unsigned long mask = kvm_pmu_implemented_counter_mask(vcpu); int i; for_each_set_bit(i, &mask, 32) @@ -265,7 +274,50 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu) irq_work_sync(&vcpu->arch.pmu.overflow_work); } -u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu) +static u64 kvm_pmu_hyp_counter_mask(struct kvm_vcpu *vcpu) +{ + unsigned int hpmn, n; + + if (!vcpu_has_nv(vcpu)) + return 0; + + hpmn = SYS_FIELD_GET(MDCR_EL2, HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2)); + n = vcpu->kvm->arch.pmcr_n; + + /* + * Programming HPMN to a value greater than PMCR_EL0.N is + * CONSTRAINED UNPREDICTABLE. Make the implementation choice that an + * UNKNOWN number of counters (in our case, zero) are reserved for EL2. + */ + if (hpmn >= n) + return 0; + + /* + * Programming HPMN=0 is CONSTRAINED UNPREDICTABLE if FEAT_HPMN0 isn't + * implemented. Since KVM's ability to emulate HPMN=0 does not directly + * depend on hardware (all PMU registers are trapped), make the + * implementation choice that all counters are included in the second + * range reserved for EL2/EL3. + */ + return GENMASK(n - 1, hpmn); +} + +bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int idx) +{ + return kvm_pmu_hyp_counter_mask(vcpu) & BIT(idx); +} + +u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu) +{ + u64 mask = kvm_pmu_implemented_counter_mask(vcpu); + + if (!vcpu_has_nv(vcpu) || vcpu_is_el2(vcpu)) + return mask; + + return mask & ~kvm_pmu_hyp_counter_mask(vcpu); +} + +u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu) { u64 val = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu)); @@ -336,15 +388,30 @@ void kvm_pmu_disable_counter_mask(struct kvm_vcpu *vcpu, u64 val) } } -static u64 kvm_pmu_overflow_status(struct kvm_vcpu *vcpu) +/* + * Returns the PMU overflow state, which is true if there exists an event + * counter where the values of the global enable control, PMOVSSET_EL0[n], and + * PMINTENSET_EL1[n] are all 1. + */ +static bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu) { - u64 reg = 0; + u64 reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0); - if ((kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E)) { - reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0); - reg &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); - reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1); - } + reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1); + + /* + * PMCR_EL0.E is the global enable control for event counters available + * to EL0 and EL1. + */ + if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E)) + reg &= kvm_pmu_hyp_counter_mask(vcpu); + + /* + * Otherwise, MDCR_EL2.HPME is the global enable control for event + * counters reserved for EL2. + */ + if (!(vcpu_read_sys_reg(vcpu, MDCR_EL2) & MDCR_EL2_HPME)) + reg &= ~kvm_pmu_hyp_counter_mask(vcpu); return reg; } @@ -357,7 +424,7 @@ static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) if (!kvm_vcpu_has_pmu(vcpu)) return; - overflow = !!kvm_pmu_overflow_status(vcpu); + overflow = kvm_pmu_overflow_status(vcpu); if (pmu->irq_level == overflow) return; @@ -574,7 +641,7 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) kvm_pmu_set_counter_value(vcpu, ARMV8_PMU_CYCLE_IDX, 0); if (val & ARMV8_PMU_PMCR_P) { - unsigned long mask = kvm_pmu_valid_counter_mask(vcpu); + unsigned long mask = kvm_pmu_accessible_counter_mask(vcpu); mask &= ~BIT(ARMV8_PMU_CYCLE_IDX); for_each_set_bit(i, &mask, 32) kvm_pmu_set_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, i), 0, true); @@ -585,8 +652,44 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc) { struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc); - return (kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) && - (__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx)); + unsigned int mdcr = __vcpu_sys_reg(vcpu, MDCR_EL2); + + if (!(__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx))) + return false; + + if (kvm_pmu_counter_is_hyp(vcpu, pmc->idx)) + return mdcr & MDCR_EL2_HPME; + + return kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E; +} + +static bool kvm_pmc_counts_at_el0(struct kvm_pmc *pmc) +{ + u64 evtreg = kvm_pmc_read_evtreg(pmc); + bool nsu = evtreg & ARMV8_PMU_EXCLUDE_NS_EL0; + bool u = evtreg & ARMV8_PMU_EXCLUDE_EL0; + + return u == nsu; +} + +static bool kvm_pmc_counts_at_el1(struct kvm_pmc *pmc) +{ + u64 evtreg = kvm_pmc_read_evtreg(pmc); + bool nsk = evtreg & ARMV8_PMU_EXCLUDE_NS_EL1; + bool p = evtreg & ARMV8_PMU_EXCLUDE_EL1; + + return p == nsk; +} + +static bool kvm_pmc_counts_at_el2(struct kvm_pmc *pmc) +{ + struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc); + u64 mdcr = __vcpu_sys_reg(vcpu, MDCR_EL2); + + if (!kvm_pmu_counter_is_hyp(vcpu, pmc->idx) && (mdcr & MDCR_EL2_HPMD)) + return false; + + return kvm_pmc_read_evtreg(pmc) & ARMV8_PMU_INCLUDE_EL2; } /** @@ -599,17 +702,15 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc) struct arm_pmu *arm_pmu = vcpu->kvm->arch.arm_pmu; struct perf_event *event; struct perf_event_attr attr; - u64 eventsel, reg, data; - bool p, u, nsk, nsu; + u64 eventsel, evtreg; - reg = counter_index_to_evtreg(pmc->idx); - data = __vcpu_sys_reg(vcpu, reg); + evtreg = kvm_pmc_read_evtreg(pmc); kvm_pmu_stop_counter(pmc); if (pmc->idx == ARMV8_PMU_CYCLE_IDX) eventsel = ARMV8_PMUV3_PERFCTR_CPU_CYCLES; else - eventsel = data & kvm_pmu_event_mask(vcpu->kvm); + eventsel = evtreg & kvm_pmu_event_mask(vcpu->kvm); /* * Neither SW increment nor chained events need to be backed @@ -627,22 +728,25 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc) !test_bit(eventsel, vcpu->kvm->arch.pmu_filter)) return; - p = data & ARMV8_PMU_EXCLUDE_EL1; - u = data & ARMV8_PMU_EXCLUDE_EL0; - nsk = data & ARMV8_PMU_EXCLUDE_NS_EL1; - nsu = data & ARMV8_PMU_EXCLUDE_NS_EL0; - memset(&attr, 0, sizeof(struct perf_event_attr)); attr.type = arm_pmu->pmu.type; attr.size = sizeof(attr); attr.pinned = 1; attr.disabled = !kvm_pmu_counter_is_enabled(pmc); - attr.exclude_user = (u != nsu); - attr.exclude_kernel = (p != nsk); + attr.exclude_user = !kvm_pmc_counts_at_el0(pmc); attr.exclude_hv = 1; /* Don't count EL2 events */ attr.exclude_host = 1; /* Don't count host events */ attr.config = eventsel; + /* + * Filter events at EL1 (i.e. vEL2) when in a hyp context based on the + * guest's EL2 filter. + */ + if (unlikely(is_hyp_ctxt(vcpu))) + attr.exclude_kernel = !kvm_pmc_counts_at_el2(pmc); + else + attr.exclude_kernel = !kvm_pmc_counts_at_el1(pmc); + /* * If counting with a 64bit counter, advertise it to the perf * code, carefully dealing with the initial sample period @@ -804,7 +908,7 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1) void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) { - u64 mask = kvm_pmu_valid_counter_mask(vcpu); + u64 mask = kvm_pmu_implemented_counter_mask(vcpu); kvm_pmu_handle_pmcr(vcpu, kvm_vcpu_read_pmcr(vcpu)); @@ -1139,3 +1243,32 @@ u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu) return u64_replace_bits(pmcr, vcpu->kvm->arch.pmcr_n, ARMV8_PMU_PMCR_N); } + +void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu) +{ + bool reprogrammed = false; + unsigned long mask; + int i; + + if (!kvm_vcpu_has_pmu(vcpu)) + return; + + mask = __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); + for_each_set_bit(i, &mask, 32) { + struct kvm_pmc *pmc = kvm_vcpu_idx_to_pmc(vcpu, i); + + /* + * We only need to reconfigure events where the filter is + * different at EL1 vs. EL2, as we're multiplexing the true EL1 + * event filter bit for nested. + */ + if (kvm_pmc_counts_at_el1(pmc) == kvm_pmc_counts_at_el2(pmc)) + continue; + + kvm_pmu_create_perf_event(pmc); + reprogrammed = true; + } + + if (reprogrammed) + kvm_vcpu_pmu_restore_guest(vcpu); +} diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c index 1f69b667332b2..3b5dbe9a0a0ea 100644 --- a/arch/arm64/kvm/psci.c +++ b/arch/arm64/kvm/psci.c @@ -194,6 +194,12 @@ static void kvm_psci_system_off(struct kvm_vcpu *vcpu) kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN, 0); } +static void kvm_psci_system_off2(struct kvm_vcpu *vcpu) +{ + kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN, + KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2); +} + static void kvm_psci_system_reset(struct kvm_vcpu *vcpu) { kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET, 0); @@ -322,7 +328,7 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor) switch(psci_fn) { case PSCI_0_2_FN_PSCI_VERSION: - val = minor == 0 ? KVM_ARM_PSCI_1_0 : KVM_ARM_PSCI_1_1; + val = PSCI_VERSION(1, minor); break; case PSCI_1_0_FN_PSCI_FEATURES: arg = smccc_get_arg1(vcpu); @@ -358,6 +364,11 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor) if (minor >= 1) val = 0; break; + case PSCI_1_3_FN_SYSTEM_OFF2: + case PSCI_1_3_FN64_SYSTEM_OFF2: + if (minor >= 3) + val = PSCI_1_3_OFF_TYPE_HIBERNATE_OFF; + break; } break; case PSCI_1_0_FN_SYSTEM_SUSPEND: @@ -392,6 +403,33 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor) break; } break; + case PSCI_1_3_FN_SYSTEM_OFF2: + kvm_psci_narrow_to_32bit(vcpu); + fallthrough; + case PSCI_1_3_FN64_SYSTEM_OFF2: + if (minor < 3) + break; + + arg = smccc_get_arg1(vcpu); + /* + * SYSTEM_OFF2 defaults to HIBERNATE_OFF if arg1 is zero. arg2 + * must be zero. + */ + if ((arg && arg != PSCI_1_3_OFF_TYPE_HIBERNATE_OFF) || + smccc_get_arg2(vcpu) != 0) { + val = PSCI_RET_INVALID_PARAMS; + break; + } + kvm_psci_system_off2(vcpu); + /* + * We shouldn't be going back to the guest after receiving a + * SYSTEM_OFF2 request. Preload a return value of + * INTERNAL_FAILURE should userspace ignore the exit and resume + * the vCPU. + */ + val = PSCI_RET_INTERNAL_FAILURE; + ret = 0; + break; default: return kvm_psci_0_2_call(vcpu); } @@ -449,6 +487,10 @@ int kvm_psci_call(struct kvm_vcpu *vcpu) } switch (version) { + case KVM_ARM_PSCI_1_3: + return kvm_psci_1_x_call(vcpu, 3); + case KVM_ARM_PSCI_1_2: + return kvm_psci_1_x_call(vcpu, 2); case KVM_ARM_PSCI_1_1: return kvm_psci_1_x_call(vcpu, 1); case KVM_ARM_PSCI_1_0: diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index 0b0ae5ae7bc2a..470524b31951e 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -167,11 +167,6 @@ static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu) memset(vcpu->arch.sve_state, 0, vcpu_sve_state_size(vcpu)); } -static void kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu) -{ - vcpu_set_flag(vcpu, GUEST_HAS_PTRAUTH); -} - /** * kvm_reset_vcpu - sets core registers and sys_regs to reset value * @vcpu: The VCPU pointer diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index ff8c4e1b847ed..83c6b4a07ef56 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -110,6 +110,14 @@ static bool get_el2_to_el1_mapping(unsigned int reg, PURE_EL2_SYSREG( RVBAR_EL2 ); PURE_EL2_SYSREG( TPIDR_EL2 ); PURE_EL2_SYSREG( HPFAR_EL2 ); + PURE_EL2_SYSREG( HCRX_EL2 ); + PURE_EL2_SYSREG( HFGRTR_EL2 ); + PURE_EL2_SYSREG( HFGWTR_EL2 ); + PURE_EL2_SYSREG( HFGITR_EL2 ); + PURE_EL2_SYSREG( HDFGRTR_EL2 ); + PURE_EL2_SYSREG( HDFGWTR_EL2 ); + PURE_EL2_SYSREG( HAFGRTR_EL2 ); + PURE_EL2_SYSREG( CNTVOFF_EL2 ); PURE_EL2_SYSREG( CNTHCTL_EL2 ); MAPPED_EL2_SYSREG(SCTLR_EL2, SCTLR_EL1, translate_sctlr_el2_to_sctlr_el1 ); @@ -126,10 +134,15 @@ static bool get_el2_to_el1_mapping(unsigned int reg, MAPPED_EL2_SYSREG(ESR_EL2, ESR_EL1, NULL ); MAPPED_EL2_SYSREG(FAR_EL2, FAR_EL1, NULL ); MAPPED_EL2_SYSREG(MAIR_EL2, MAIR_EL1, NULL ); + MAPPED_EL2_SYSREG(TCR2_EL2, TCR2_EL1, NULL ); + MAPPED_EL2_SYSREG(PIR_EL2, PIR_EL1, NULL ); + MAPPED_EL2_SYSREG(PIRE0_EL2, PIRE0_EL1, NULL ); + MAPPED_EL2_SYSREG(POR_EL2, POR_EL1, NULL ); MAPPED_EL2_SYSREG(AMAIR_EL2, AMAIR_EL1, NULL ); MAPPED_EL2_SYSREG(ELR_EL2, ELR_EL1, NULL ); MAPPED_EL2_SYSREG(SPSR_EL2, SPSR_EL1, NULL ); MAPPED_EL2_SYSREG(ZCR_EL2, ZCR_EL1, NULL ); + MAPPED_EL2_SYSREG(CONTEXTIDR_EL2, CONTEXTIDR_EL1, NULL ); default: return false; } @@ -148,6 +161,21 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) if (!is_hyp_ctxt(vcpu)) goto memory_read; + /* + * CNTHCTL_EL2 requires some special treatment to + * account for the bits that can be set via CNTKCTL_EL1. + */ + switch (reg) { + case CNTHCTL_EL2: + if (vcpu_el2_e2h_is_set(vcpu)) { + val = read_sysreg_el1(SYS_CNTKCTL); + val &= CNTKCTL_VALID_BITS; + val |= __vcpu_sys_reg(vcpu, reg) & ~CNTKCTL_VALID_BITS; + return val; + } + break; + } + /* * If this register does not have an EL1 counterpart, * then read the stored EL2 version. @@ -165,6 +193,9 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) /* Get the current version of the EL1 counterpart. */ WARN_ON(!__vcpu_read_sys_reg_from_cpu(el1r, &val)); + if (reg >= __SANITISED_REG_START__) + val = kvm_vcpu_apply_reg_masks(vcpu, reg, val); + return val; } @@ -198,6 +229,19 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg) */ __vcpu_sys_reg(vcpu, reg) = val; + switch (reg) { + case CNTHCTL_EL2: + /* + * If E2H=0, CNHTCTL_EL2 is a pure shadow register. + * Otherwise, some of the bits are backed by + * CNTKCTL_EL1, while the rest is kept in memory. + * Yes, this is fun stuff. + */ + if (vcpu_el2_e2h_is_set(vcpu)) + write_sysreg_el1(val, SYS_CNTKCTL); + return; + } + /* No EL1 counterpart? We're done here.? */ if (reg == el1r) return; @@ -390,10 +434,6 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu, bool was_enabled = vcpu_has_cache_enabled(vcpu); u64 val, mask, shift; - if (reg_to_encoding(r) == SYS_TCR2_EL1 && - !kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, TCRX, IMP)) - return undef_access(vcpu, p, r); - BUG_ON(!p->is_write); get_access_mask(r, &mask, &shift); @@ -1128,7 +1168,7 @@ static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 va { bool set; - val &= kvm_pmu_valid_counter_mask(vcpu); + val &= kvm_pmu_accessible_counter_mask(vcpu); switch (r->reg) { case PMOVSSET_EL0: @@ -1151,7 +1191,7 @@ static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 va static int get_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 *val) { - u64 mask = kvm_pmu_valid_counter_mask(vcpu); + u64 mask = kvm_pmu_accessible_counter_mask(vcpu); *val = __vcpu_sys_reg(vcpu, r->reg) & mask; return 0; @@ -1165,7 +1205,7 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p, if (pmu_access_el0_disabled(vcpu)) return false; - mask = kvm_pmu_valid_counter_mask(vcpu); + mask = kvm_pmu_accessible_counter_mask(vcpu); if (p->is_write) { val = p->regval & mask; if (r->Op2 & 0x1) { @@ -1188,7 +1228,7 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p, static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { - u64 mask = kvm_pmu_valid_counter_mask(vcpu); + u64 mask = kvm_pmu_accessible_counter_mask(vcpu); if (check_pmu_access_disabled(vcpu, 0)) return false; @@ -1212,7 +1252,7 @@ static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p, static bool access_pmovs(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { - u64 mask = kvm_pmu_valid_counter_mask(vcpu); + u64 mask = kvm_pmu_accessible_counter_mask(vcpu); if (pmu_access_el0_disabled(vcpu)) return false; @@ -1242,7 +1282,7 @@ static bool access_pmswinc(struct kvm_vcpu *vcpu, struct sys_reg_params *p, if (pmu_write_swinc_el0_disabled(vcpu)) return false; - mask = kvm_pmu_valid_counter_mask(vcpu); + mask = kvm_pmu_accessible_counter_mask(vcpu); kvm_pmu_software_increment(vcpu, p->regval & mask); return true; } @@ -1509,6 +1549,9 @@ static u8 pmuver_to_perfmon(u8 pmuver) } } +static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val); +static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val); + /* Read a sanitised cpufeature ID register by sys_reg_desc */ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) @@ -1522,6 +1565,12 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, val = read_sanitised_ftr_reg(id); switch (id) { + case SYS_ID_AA64DFR0_EL1: + val = sanitise_id_aa64dfr0_el1(vcpu, val); + break; + case SYS_ID_AA64PFR0_EL1: + val = sanitise_id_aa64pfr0_el1(vcpu, val); + break; case SYS_ID_AA64PFR1_EL1: if (!kvm_has_mte(vcpu->kvm)) val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE); @@ -1535,6 +1584,7 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTEX); val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_DF2); val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_PFAR); + val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MPAM_frac); break; case SYS_ID_AA64PFR2_EL1: /* We only expose FPMR */ @@ -1692,11 +1742,8 @@ static unsigned int fp8_visibility(const struct kvm_vcpu *vcpu, return REG_HIDDEN; } -static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, - const struct sys_reg_desc *rd) +static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val) { - u64 val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1); - if (!vcpu_has_sve(vcpu)) val &= ~ID_AA64PFR0_EL1_SVE_MASK; @@ -1724,6 +1771,13 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, val &= ~ID_AA64PFR0_EL1_AMU_MASK; + /* + * MPAM is disabled by default as KVM also needs a set of PARTID to + * program the MPAMVPMx_EL2 PARTID remapping registers with. But some + * older kernels let the guest see the ID bit. + */ + val &= ~ID_AA64PFR0_EL1_MPAM_MASK; + return val; } @@ -1737,11 +1791,8 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, (val); \ }) -static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu, - const struct sys_reg_desc *rd) +static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val) { - u64 val = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1); - val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8); /* @@ -1834,6 +1885,70 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu, return set_id_reg(vcpu, rd, val); } +static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, u64 user_val) +{ + u64 hw_val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1); + u64 mpam_mask = ID_AA64PFR0_EL1_MPAM_MASK; + + /* + * Commit 011e5f5bf529f ("arm64/cpufeature: Add remaining feature bits + * in ID_AA64PFR0 register") exposed the MPAM field of AA64PFR0_EL1 to + * guests, but didn't add trap handling. KVM doesn't support MPAM and + * always returns an UNDEF for these registers. The guest must see 0 + * for this field. + * + * But KVM must also accept values from user-space that were provided + * by KVM. On CPUs that support MPAM, permit user-space to write + * the sanitizied value to ID_AA64PFR0_EL1.MPAM, but ignore this field. + */ + if ((hw_val & mpam_mask) == (user_val & mpam_mask)) + user_val &= ~ID_AA64PFR0_EL1_MPAM_MASK; + + return set_id_reg(vcpu, rd, user_val); +} + +static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, u64 user_val) +{ + u64 hw_val = read_sanitised_ftr_reg(SYS_ID_AA64PFR1_EL1); + u64 mpam_mask = ID_AA64PFR1_EL1_MPAM_frac_MASK; + + /* See set_id_aa64pfr0_el1 for comment about MPAM */ + if ((hw_val & mpam_mask) == (user_val & mpam_mask)) + user_val &= ~ID_AA64PFR1_EL1_MPAM_frac_MASK; + + return set_id_reg(vcpu, rd, user_val); +} + +static int set_ctr_el0(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, u64 user_val) +{ + u8 user_L1Ip = SYS_FIELD_GET(CTR_EL0, L1Ip, user_val); + + /* + * Both AIVIVT (0b01) and VPIPT (0b00) are documented as reserved. + * Hence only allow to set VIPT(0b10) or PIPT(0b11) for L1Ip based + * on what hardware reports. + * + * Using a VIPT software model on PIPT will lead to over invalidation, + * but still correct. Hence, we can allow downgrading PIPT to VIPT, + * but not the other way around. This is handled via arm64_ftr_safe_value() + * as CTR_EL0 ftr_bits has L1Ip field with type FTR_EXACT and safe value + * set as VIPT. + */ + switch (user_L1Ip) { + case CTR_EL0_L1Ip_RESERVED_VPIPT: + case CTR_EL0_L1Ip_RESERVED_AIVIVT: + return -EINVAL; + case CTR_EL0_L1Ip_VIPT: + case CTR_EL0_L1Ip_PIPT: + return set_id_reg(vcpu, rd, user_val); + default: + return -ENOENT; + } +} + /* * cpufeature ID register user accessors * @@ -2104,6 +2219,15 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu, .val = v, \ } +#define EL2_REG_FILTERED(name, acc, rst, v, filter) { \ + SYS_DESC(SYS_##name), \ + .access = acc, \ + .reset = rst, \ + .reg = name, \ + .visibility = filter, \ + .val = v, \ +} + #define EL2_REG_VNCR(name, rst, v) EL2_REG(name, bad_vncr_trap, rst, v) #define EL2_REG_REDIR(name, rst, v) EL2_REG(name, bad_redir_trap, rst, v) @@ -2150,6 +2274,15 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu, .val = mask, \ } +/* sys_reg_desc initialiser for cpufeature ID registers that need filtering */ +#define ID_FILTERED(sysreg, name, mask) { \ + ID_DESC(sysreg), \ + .set_user = set_##name, \ + .visibility = id_visibility, \ + .reset = kvm_read_sanitised_id_reg, \ + .val = (mask), \ +} + /* * sys_reg_desc initialiser for architecturally unallocated cpufeature ID * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2 @@ -2236,16 +2369,18 @@ static u64 reset_hcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) return __vcpu_sys_reg(vcpu, r->reg) = val; } +static unsigned int __el2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, + unsigned int (*fn)(const struct kvm_vcpu *, + const struct sys_reg_desc *)) +{ + return el2_visibility(vcpu, rd) ?: fn(vcpu, rd); +} + static unsigned int sve_el2_visibility(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd) { - unsigned int r; - - r = el2_visibility(vcpu, rd); - if (r) - return r; - - return sve_visibility(vcpu, rd); + return __el2_visibility(vcpu, rd, sve_visibility); } static bool access_zcr_el2(struct kvm_vcpu *vcpu, @@ -2273,12 +2408,48 @@ static bool access_zcr_el2(struct kvm_vcpu *vcpu, static unsigned int s1poe_visibility(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd) { - if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1POE, IMP)) + if (kvm_has_s1poe(vcpu->kvm)) + return 0; + + return REG_HIDDEN; +} + +static unsigned int s1poe_el2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + return __el2_visibility(vcpu, rd, s1poe_visibility); +} + +static unsigned int tcr2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + if (kvm_has_tcr2(vcpu->kvm)) + return 0; + + return REG_HIDDEN; +} + +static unsigned int tcr2_el2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + return __el2_visibility(vcpu, rd, tcr2_visibility); +} + +static unsigned int s1pie_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + if (kvm_has_s1pie(vcpu->kvm)) return 0; return REG_HIDDEN; } +static unsigned int s1pie_el2_visibility(const struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd) +{ + return __el2_visibility(vcpu, rd, s1pie_visibility); +} + /* * Architected system registers. * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2 @@ -2374,18 +2545,15 @@ static const struct sys_reg_desc sys_reg_descs[] = { /* AArch64 ID registers */ /* CRm=4 */ - { SYS_DESC(SYS_ID_AA64PFR0_EL1), - .access = access_id_reg, - .get_user = get_id_reg, - .set_user = set_id_reg, - .reset = read_sanitised_id_aa64pfr0_el1, - .val = ~(ID_AA64PFR0_EL1_AMU | - ID_AA64PFR0_EL1_MPAM | - ID_AA64PFR0_EL1_SVE | - ID_AA64PFR0_EL1_RAS | - ID_AA64PFR0_EL1_AdvSIMD | - ID_AA64PFR0_EL1_FP), }, - ID_WRITABLE(ID_AA64PFR1_EL1, ~(ID_AA64PFR1_EL1_PFAR | + ID_FILTERED(ID_AA64PFR0_EL1, id_aa64pfr0_el1, + ~(ID_AA64PFR0_EL1_AMU | + ID_AA64PFR0_EL1_MPAM | + ID_AA64PFR0_EL1_SVE | + ID_AA64PFR0_EL1_RAS | + ID_AA64PFR0_EL1_AdvSIMD | + ID_AA64PFR0_EL1_FP)), + ID_FILTERED(ID_AA64PFR1_EL1, id_aa64pfr1_el1, + ~(ID_AA64PFR1_EL1_PFAR | ID_AA64PFR1_EL1_DF2 | ID_AA64PFR1_EL1_MTEX | ID_AA64PFR1_EL1_THE | @@ -2406,11 +2574,6 @@ static const struct sys_reg_desc sys_reg_descs[] = { ID_WRITABLE(ID_AA64FPFR0_EL1, ~ID_AA64FPFR0_EL1_RES0), /* CRm=5 */ - { SYS_DESC(SYS_ID_AA64DFR0_EL1), - .access = access_id_reg, - .get_user = get_id_reg, - .set_user = set_id_aa64dfr0_el1, - .reset = read_sanitised_id_aa64dfr0_el1, /* * Prior to FEAT_Debugv8.9, the architecture defines context-aware * breakpoints (CTX_CMPs) as the highest numbered breakpoints (BRPs). @@ -2423,10 +2586,11 @@ static const struct sys_reg_desc sys_reg_descs[] = { * See DDI0487K.a, section D2.8.3 Breakpoint types and linking * of breakpoints for more details. */ - .val = ID_AA64DFR0_EL1_DoubleLock_MASK | - ID_AA64DFR0_EL1_WRPs_MASK | - ID_AA64DFR0_EL1_PMUVer_MASK | - ID_AA64DFR0_EL1_DebugVer_MASK, }, + ID_FILTERED(ID_AA64DFR0_EL1, id_aa64dfr0_el1, + ID_AA64DFR0_EL1_DoubleLock_MASK | + ID_AA64DFR0_EL1_WRPs_MASK | + ID_AA64DFR0_EL1_PMUVer_MASK | + ID_AA64DFR0_EL1_DebugVer_MASK), ID_SANITISED(ID_AA64DFR1_EL1), ID_UNALLOCATED(5,2), ID_UNALLOCATED(5,3), @@ -2489,7 +2653,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_TTBR0_EL1), access_vm_reg, reset_unknown, TTBR0_EL1 }, { SYS_DESC(SYS_TTBR1_EL1), access_vm_reg, reset_unknown, TTBR1_EL1 }, { SYS_DESC(SYS_TCR_EL1), access_vm_reg, reset_val, TCR_EL1, 0 }, - { SYS_DESC(SYS_TCR2_EL1), access_vm_reg, reset_val, TCR2_EL1, 0 }, + { SYS_DESC(SYS_TCR2_EL1), access_vm_reg, reset_val, TCR2_EL1, 0, + .visibility = tcr2_visibility }, PTRAUTH_KEY(APIA), PTRAUTH_KEY(APIB), @@ -2543,8 +2708,10 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi }, { SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 }, - { SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1 }, - { SYS_DESC(SYS_PIR_EL1), NULL, reset_unknown, PIR_EL1 }, + { SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1, + .visibility = s1pie_visibility }, + { SYS_DESC(SYS_PIR_EL1), NULL, reset_unknown, PIR_EL1, + .visibility = s1pie_visibility }, { SYS_DESC(SYS_POR_EL1), NULL, reset_unknown, POR_EL1, .visibility = s1poe_visibility }, { SYS_DESC(SYS_AMAIR_EL1), access_vm_reg, reset_amair_el1, AMAIR_EL1 }, @@ -2553,8 +2720,11 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_LOREA_EL1), trap_loregion }, { SYS_DESC(SYS_LORN_EL1), trap_loregion }, { SYS_DESC(SYS_LORC_EL1), trap_loregion }, + { SYS_DESC(SYS_MPAMIDR_EL1), undef_access }, { SYS_DESC(SYS_LORID_EL1), trap_loregion }, + { SYS_DESC(SYS_MPAM1_EL1), undef_access }, + { SYS_DESC(SYS_MPAM0_EL1), undef_access }, { SYS_DESC(SYS_VBAR_EL1), access_rw, reset_val, VBAR_EL1, 0 }, { SYS_DESC(SYS_DISR_EL1), NULL, reset_val, DISR_EL1, 0 }, @@ -2599,10 +2769,12 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CCSIDR2_EL1), undef_access }, { SYS_DESC(SYS_SMIDR_EL1), undef_access }, { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 }, - ID_WRITABLE(CTR_EL0, CTR_EL0_DIC_MASK | - CTR_EL0_IDC_MASK | - CTR_EL0_DminLine_MASK | - CTR_EL0_IminLine_MASK), + ID_FILTERED(CTR_EL0, ctr_el0, + CTR_EL0_DIC_MASK | + CTR_EL0_IDC_MASK | + CTR_EL0_DminLine_MASK | + CTR_EL0_L1Ip_MASK | + CTR_EL0_IminLine_MASK), { SYS_DESC(SYS_SVCR), undef_access, reset_val, SVCR, 0, .visibility = sme_visibility }, { SYS_DESC(SYS_FPMR), undef_access, reset_val, FPMR, 0, .visibility = fp8_visibility }, @@ -2818,14 +2990,16 @@ static const struct sys_reg_desc sys_reg_descs[] = { EL2_REG_VNCR(HFGITR_EL2, reset_val, 0), EL2_REG_VNCR(HACR_EL2, reset_val, 0), - { SYS_DESC(SYS_ZCR_EL2), .access = access_zcr_el2, .reset = reset_val, - .visibility = sve_el2_visibility, .reg = ZCR_EL2 }, + EL2_REG_FILTERED(ZCR_EL2, access_zcr_el2, reset_val, 0, + sve_el2_visibility), EL2_REG_VNCR(HCRX_EL2, reset_val, 0), EL2_REG(TTBR0_EL2, access_rw, reset_val, 0), EL2_REG(TTBR1_EL2, access_rw, reset_val, 0), EL2_REG(TCR_EL2, access_rw, reset_val, TCR_EL2_RES1), + EL2_REG_FILTERED(TCR2_EL2, access_rw, reset_val, TCR2_EL2_RES1, + tcr2_el2_visibility), EL2_REG_VNCR(VTTBR_EL2, reset_val, 0), EL2_REG_VNCR(VTCR_EL2, reset_val, 0), @@ -2853,7 +3027,24 @@ static const struct sys_reg_desc sys_reg_descs[] = { EL2_REG(HPFAR_EL2, access_rw, reset_val, 0), EL2_REG(MAIR_EL2, access_rw, reset_val, 0), + EL2_REG_FILTERED(PIRE0_EL2, access_rw, reset_val, 0, + s1pie_el2_visibility), + EL2_REG_FILTERED(PIR_EL2, access_rw, reset_val, 0, + s1pie_el2_visibility), + EL2_REG_FILTERED(POR_EL2, access_rw, reset_val, 0, + s1poe_el2_visibility), EL2_REG(AMAIR_EL2, access_rw, reset_val, 0), + { SYS_DESC(SYS_MPAMHCR_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPMV_EL2), undef_access }, + { SYS_DESC(SYS_MPAM2_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM0_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM1_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM2_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM3_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM4_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM5_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM6_EL2), undef_access }, + { SYS_DESC(SYS_MPAMVPM7_EL2), undef_access }, EL2_REG(VBAR_EL2, access_rw, reset_val, 0), EL2_REG(RVBAR_EL2, access_rw, reset_val, 0), @@ -4719,7 +4910,7 @@ void kvm_calculate_traps(struct kvm_vcpu *vcpu) if (kvm_has_feat(kvm, ID_AA64ISAR2_EL1, MOPS, IMP)) vcpu->arch.hcrx_el2 |= (HCRX_EL2_MSCEn | HCRX_EL2_MCE2); - if (kvm_has_feat(kvm, ID_AA64MMFR3_EL1, TCRX, IMP)) + if (kvm_has_tcr2(kvm)) vcpu->arch.hcrx_el2 |= HCRX_EL2_TCR2En; if (kvm_has_fpmr(kvm)) @@ -4769,11 +4960,11 @@ void kvm_calculate_traps(struct kvm_vcpu *vcpu) kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_ATS1E1RP | HFGITR_EL2_ATS1E1WP); - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S1PIE, IMP)) + if (!kvm_has_s1pie(kvm)) kvm->arch.fgu[HFGxTR_GROUP] |= (HFGxTR_EL2_nPIRE0_EL1 | HFGxTR_EL2_nPIR_EL1); - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S1POE, IMP)) + if (!kvm_has_s1poe(kvm)) kvm->arch.fgu[HFGxTR_GROUP] |= (HFGxTR_EL2_nPOR_EL1 | HFGxTR_EL2_nPOR_EL0); diff --git a/arch/arm64/kvm/vgic/vgic-debug.c b/arch/arm64/kvm/vgic/vgic-debug.c index e1397ab2072a5..afb018528bc3b 100644 --- a/arch/arm64/kvm/vgic/vgic-debug.c +++ b/arch/arm64/kvm/vgic/vgic-debug.c @@ -287,7 +287,10 @@ static int vgic_debug_show(struct seq_file *s, void *v) * Expect this to succeed, as iter_mark_lpis() takes a reference on * every LPI to be visited. */ - irq = vgic_get_irq(kvm, vcpu, iter->intid); + if (iter->intid < VGIC_NR_PRIVATE_IRQS) + irq = vgic_get_vcpu_irq(vcpu, iter->intid); + else + irq = vgic_get_irq(kvm, iter->intid); if (WARN_ON_ONCE(!irq)) return -EINVAL; diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 48c952563e85f..bc7e22ab5d812 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -322,7 +322,7 @@ int vgic_init(struct kvm *kvm) goto out; for (i = 0; i < VGIC_NR_PRIVATE_IRQS; i++) { - struct vgic_irq *irq = vgic_get_irq(kvm, vcpu, i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, i); switch (dist->vgic_model) { case KVM_DEV_TYPE_ARM_VGIC_V3: diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index ba945ba78cc7d..f4c4494645c34 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -31,6 +31,41 @@ static int vgic_its_commit_v0(struct vgic_its *its); static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq, struct kvm_vcpu *filter_vcpu, bool needs_inv); +#define vgic_its_read_entry_lock(i, g, valp, t) \ + ({ \ + int __sz = vgic_its_get_abi(i)->t##_esz; \ + struct kvm *__k = (i)->dev->kvm; \ + int __ret; \ + \ + BUILD_BUG_ON(NR_ITS_ABIS == 1 && \ + sizeof(*(valp)) != ABI_0_ESZ); \ + if (NR_ITS_ABIS > 1 && \ + KVM_BUG_ON(__sz != sizeof(*(valp)), __k)) \ + __ret = -EINVAL; \ + else \ + __ret = kvm_read_guest_lock(__k, (g), \ + valp, __sz); \ + __ret; \ + }) + +#define vgic_its_write_entry_lock(i, g, val, t) \ + ({ \ + int __sz = vgic_its_get_abi(i)->t##_esz; \ + struct kvm *__k = (i)->dev->kvm; \ + typeof(val) __v = (val); \ + int __ret; \ + \ + BUILD_BUG_ON(NR_ITS_ABIS == 1 && \ + sizeof(__v) != ABI_0_ESZ); \ + if (NR_ITS_ABIS > 1 && \ + KVM_BUG_ON(__sz != sizeof(__v), __k)) \ + __ret = -EINVAL; \ + else \ + __ret = vgic_write_guest_lock(__k, (g), \ + &__v, __sz); \ + __ret; \ + }) + /* * Creates a new (reference to a) struct vgic_irq for a given LPI. * If this LPI is already mapped on another ITS, we increase its refcount @@ -42,7 +77,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, struct kvm_vcpu *vcpu) { struct vgic_dist *dist = &kvm->arch.vgic; - struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intid), *oldirq; + struct vgic_irq *irq = vgic_get_irq(kvm, intid), *oldirq; unsigned long flags; int ret; @@ -419,7 +454,7 @@ static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu) last_byte_offset = byte_offset; } - irq = vgic_get_irq(vcpu->kvm, NULL, intid); + irq = vgic_get_irq(vcpu->kvm, intid); if (!irq) continue; @@ -782,6 +817,9 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its, ite = find_ite(its, device_id, event_id); if (ite && its_is_collection_mapped(ite->collection)) { + struct its_device *device = find_its_device(its, device_id); + int ite_esz = vgic_its_get_abi(its)->ite_esz; + gpa_t gpa = device->itt_addr + ite->event_id * ite_esz; /* * Though the spec talks about removing the pending state, we * don't bother here since we clear the ITTE anyway and the @@ -790,7 +828,8 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its, vgic_its_invalidate_cache(its); its_free_ite(kvm, ite); - return 0; + + return vgic_its_write_entry_lock(its, gpa, 0ULL, ite); } return E_ITS_DISCARD_UNMAPPED_INTERRUPT; @@ -1140,8 +1179,9 @@ static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its, u8 num_eventid_bits = its_cmd_get_size(its_cmd); gpa_t itt_addr = its_cmd_get_ittaddr(its_cmd); struct its_device *device; + gpa_t gpa; - if (!vgic_its_check_id(its, its->baser_device_table, device_id, NULL)) + if (!vgic_its_check_id(its, its->baser_device_table, device_id, &gpa)) return E_ITS_MAPD_DEVICE_OOR; if (valid && num_eventid_bits > VITS_TYPER_IDBITS) @@ -1162,7 +1202,7 @@ static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its, * is an error, so we are done in any case. */ if (!valid) - return 0; + return vgic_its_write_entry_lock(its, gpa, 0ULL, dte); device = vgic_its_alloc_device(its, device_id, itt_addr, num_eventid_bits); @@ -1282,7 +1322,7 @@ int vgic_its_invall(struct kvm_vcpu *vcpu) unsigned long intid; xa_for_each(&dist->lpi_xa, intid, irq) { - irq = vgic_get_irq(kvm, NULL, intid); + irq = vgic_get_irq(kvm, intid); if (!irq) continue; @@ -1348,7 +1388,7 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its, return 0; xa_for_each(&dist->lpi_xa, intid, irq) { - irq = vgic_get_irq(kvm, NULL, intid); + irq = vgic_get_irq(kvm, intid); if (!irq) continue; @@ -2084,9 +2124,8 @@ static int scan_its_table(struct vgic_its *its, gpa_t base, int size, u32 esz, * vgic_its_save_ite - Save an interrupt translation entry at @gpa */ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev, - struct its_ite *ite, gpa_t gpa, int ite_esz) + struct its_ite *ite, gpa_t gpa) { - struct kvm *kvm = its->dev->kvm; u32 next_offset; u64 val; @@ -2095,7 +2134,8 @@ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev, ((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) | ite->collection->collection_id; val = cpu_to_le64(val); - return vgic_write_guest_lock(kvm, gpa, &val, ite_esz); + + return vgic_its_write_entry_lock(its, gpa, val, ite); } /** @@ -2195,7 +2235,7 @@ static int vgic_its_save_itt(struct vgic_its *its, struct its_device *device) if (ite->irq->hw && !kvm_vgic_global_state.has_gicv4_1) return -EACCES; - ret = vgic_its_save_ite(its, device, ite, gpa, ite_esz); + ret = vgic_its_save_ite(its, device, ite, gpa); if (ret) return ret; } @@ -2234,12 +2274,10 @@ static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev) * @its: ITS handle * @dev: ITS device * @ptr: GPA - * @dte_esz: device table entry size */ static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev, - gpa_t ptr, int dte_esz) + gpa_t ptr) { - struct kvm *kvm = its->dev->kvm; u64 val, itt_addr_field; u32 next_offset; @@ -2250,7 +2288,8 @@ static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev, (itt_addr_field << KVM_ITS_DTE_ITTADDR_SHIFT) | (dev->num_eventid_bits - 1)); val = cpu_to_le64(val); - return vgic_write_guest_lock(kvm, ptr, &val, dte_esz); + + return vgic_its_write_entry_lock(its, ptr, val, dte); } /** @@ -2326,10 +2365,8 @@ static int vgic_its_device_cmp(void *priv, const struct list_head *a, */ static int vgic_its_save_device_tables(struct vgic_its *its) { - const struct vgic_its_abi *abi = vgic_its_get_abi(its); u64 baser = its->baser_device_table; struct its_device *dev; - int dte_esz = abi->dte_esz; if (!(baser & GITS_BASER_VALID)) return 0; @@ -2348,7 +2385,7 @@ static int vgic_its_save_device_tables(struct vgic_its *its) if (ret) return ret; - ret = vgic_its_save_dte(its, dev, eaddr, dte_esz); + ret = vgic_its_save_dte(its, dev, eaddr); if (ret) return ret; } @@ -2429,7 +2466,7 @@ static int vgic_its_restore_device_tables(struct vgic_its *its) static int vgic_its_save_cte(struct vgic_its *its, struct its_collection *collection, - gpa_t gpa, int esz) + gpa_t gpa) { u64 val; @@ -2437,7 +2474,8 @@ static int vgic_its_save_cte(struct vgic_its *its, ((u64)collection->target_addr << KVM_ITS_CTE_RDBASE_SHIFT) | collection->collection_id); val = cpu_to_le64(val); - return vgic_write_guest_lock(its->dev->kvm, gpa, &val, esz); + + return vgic_its_write_entry_lock(its, gpa, val, cte); } /* @@ -2445,7 +2483,7 @@ static int vgic_its_save_cte(struct vgic_its *its, * Return +1 on success, 0 if the entry was invalid (which should be * interpreted as end-of-table), and a negative error value for generic errors. */ -static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz) +static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa) { struct its_collection *collection; struct kvm *kvm = its->dev->kvm; @@ -2453,8 +2491,7 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz) u64 val; int ret; - BUG_ON(esz > sizeof(val)); - ret = kvm_read_guest_lock(kvm, gpa, &val, esz); + ret = vgic_its_read_entry_lock(its, gpa, &val, cte); if (ret) return ret; val = le64_to_cpu(val); @@ -2492,7 +2529,6 @@ static int vgic_its_save_collection_table(struct vgic_its *its) u64 baser = its->baser_coll_table; gpa_t gpa = GITS_BASER_ADDR_48_to_52(baser); struct its_collection *collection; - u64 val; size_t max_size, filled = 0; int ret, cte_esz = abi->cte_esz; @@ -2502,7 +2538,7 @@ static int vgic_its_save_collection_table(struct vgic_its *its) max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; list_for_each_entry(collection, &its->collection_list, coll_list) { - ret = vgic_its_save_cte(its, collection, gpa, cte_esz); + ret = vgic_its_save_cte(its, collection, gpa); if (ret) return ret; gpa += cte_esz; @@ -2516,10 +2552,7 @@ static int vgic_its_save_collection_table(struct vgic_its *its) * table is not fully filled, add a last dummy element * with valid bit unset */ - val = 0; - BUG_ON(cte_esz > sizeof(val)); - ret = vgic_write_guest_lock(its->dev->kvm, gpa, &val, cte_esz); - return ret; + return vgic_its_write_entry_lock(its, gpa, 0ULL, cte); } /* @@ -2544,7 +2577,7 @@ static int vgic_its_restore_collection_table(struct vgic_its *its) max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K; while (read < max_size) { - ret = vgic_its_restore_cte(its, gpa, cte_esz); + ret = vgic_its_restore_cte(its, gpa); if (ret <= 0) break; gpa += cte_esz; diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v2.c b/arch/arm64/kvm/vgic/vgic-mmio-v2.c index e070cda86e12f..f25fccb1f8e63 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v2.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v2.c @@ -148,7 +148,7 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu, if (!(targets & (1U << c))) continue; - irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid); + irq = vgic_get_vcpu_irq(vcpu, intid); raw_spin_lock_irqsave(&irq->irq_lock, flags); irq->pending_latch = true; @@ -167,7 +167,7 @@ static unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu, u64 val = 0; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); val |= (u64)irq->targets << (i * 8); @@ -191,7 +191,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu, return; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i); + struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, intid + i); int target; raw_spin_lock_irqsave(&irq->irq_lock, flags); @@ -213,7 +213,7 @@ static unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu, u64 val = 0; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); val |= (u64)irq->source << (i * 8); @@ -231,7 +231,7 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu, unsigned long flags; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); @@ -253,7 +253,7 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu, unsigned long flags; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 9e50928f5d7df..ae4c0593d1145 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -194,7 +194,7 @@ static unsigned long vgic_mmio_read_irouter(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len) { int intid = VGIC_ADDR_TO_INTID(addr, 64); - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid); + struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, intid); unsigned long ret = 0; if (!irq) @@ -220,7 +220,7 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu, if (addr & 4) return; - irq = vgic_get_irq(vcpu->kvm, NULL, intid); + irq = vgic_get_irq(vcpu->kvm, intid); if (!irq) return; @@ -530,6 +530,7 @@ static void vgic_mmio_write_invlpi(struct kvm_vcpu *vcpu, unsigned long val) { struct vgic_irq *irq; + u32 intid; /* * If the guest wrote only to the upper 32bit part of the @@ -541,9 +542,13 @@ static void vgic_mmio_write_invlpi(struct kvm_vcpu *vcpu, if ((addr & 4) || !vgic_lpis_enabled(vcpu)) return; + intid = lower_32_bits(val); + if (intid < VGIC_MIN_LPI) + return; + vgic_set_rdist_busy(vcpu, true); - irq = vgic_get_irq(vcpu->kvm, NULL, lower_32_bits(val)); + irq = vgic_get_irq(vcpu->kvm, intid); if (irq) { vgic_its_inv_lpi(vcpu->kvm, irq); vgic_put_irq(vcpu->kvm, irq); @@ -1020,7 +1025,7 @@ int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr) static void vgic_v3_queue_sgi(struct kvm_vcpu *vcpu, u32 sgi, bool allow_group1) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, sgi); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, sgi); unsigned long flags; raw_spin_lock_irqsave(&irq->irq_lock, flags); diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c index cf76523a21945..e416e433baff3 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.c +++ b/arch/arm64/kvm/vgic/vgic-mmio.c @@ -50,7 +50,7 @@ unsigned long vgic_mmio_read_group(struct kvm_vcpu *vcpu, /* Loop over all IRQs affected by this read */ for (i = 0; i < len * 8; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); if (irq->group) value |= BIT(i); @@ -74,7 +74,7 @@ void vgic_mmio_write_group(struct kvm_vcpu *vcpu, gpa_t addr, unsigned long flags; for (i = 0; i < len * 8; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); irq->group = !!(val & BIT(i)); @@ -102,7 +102,7 @@ unsigned long vgic_mmio_read_enable(struct kvm_vcpu *vcpu, /* Loop over all IRQs affected by this read */ for (i = 0; i < len * 8; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); if (irq->enabled) value |= (1U << i); @@ -122,7 +122,7 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu, unsigned long flags; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); if (irq->hw && vgic_irq_is_sgi(irq->intid)) { @@ -171,7 +171,7 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu, unsigned long flags; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); if (irq->hw && vgic_irq_is_sgi(irq->intid) && irq->enabled) @@ -193,7 +193,7 @@ int vgic_uaccess_write_senable(struct kvm_vcpu *vcpu, unsigned long flags; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); irq->enabled = true; @@ -214,7 +214,7 @@ int vgic_uaccess_write_cenable(struct kvm_vcpu *vcpu, unsigned long flags; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); irq->enabled = false; @@ -236,7 +236,7 @@ static unsigned long __read_pending(struct kvm_vcpu *vcpu, /* Loop over all IRQs affected by this read */ for (i = 0; i < len * 8; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); unsigned long flags; bool val; @@ -309,7 +309,7 @@ static void __set_pending(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len, unsigned long flags; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); /* GICD_ISPENDR0 SGI bits are WI when written from the guest. */ if (is_vgic_v2_sgi(vcpu, irq) && !is_user) { @@ -395,7 +395,7 @@ static void __clear_pending(struct kvm_vcpu *vcpu, unsigned long flags; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); /* GICD_ICPENDR0 SGI bits are WI when written from the guest. */ if (is_vgic_v2_sgi(vcpu, irq) && !is_user) { @@ -494,7 +494,7 @@ static unsigned long __vgic_mmio_read_active(struct kvm_vcpu *vcpu, /* Loop over all IRQs affected by this read */ for (i = 0; i < len * 8; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); /* * Even for HW interrupts, don't evaluate the HW state as @@ -598,7 +598,7 @@ static void __vgic_mmio_write_cactive(struct kvm_vcpu *vcpu, int i; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); vgic_mmio_change_active(vcpu, irq, false); vgic_put_irq(vcpu->kvm, irq); } @@ -635,7 +635,7 @@ static void __vgic_mmio_write_sactive(struct kvm_vcpu *vcpu, int i; for_each_set_bit(i, &val, len * 8) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); vgic_mmio_change_active(vcpu, irq, true); vgic_put_irq(vcpu->kvm, irq); } @@ -672,7 +672,7 @@ unsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu, u64 val = 0; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); val |= (u64)irq->priority << (i * 8); @@ -698,7 +698,7 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu, unsigned long flags; for (i = 0; i < len; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); /* Narrow the priority range to what we actually support */ @@ -719,7 +719,7 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu, int i; for (i = 0; i < len * 4; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i); if (irq->config == VGIC_CONFIG_EDGE) value |= (2U << (i * 2)); @@ -750,7 +750,7 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu, if (intid + i < VGIC_NR_PRIVATE_IRQS) continue; - irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + irq = vgic_get_irq(vcpu->kvm, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); if (test_bit(i * 2 + 1, &val)) @@ -775,7 +775,7 @@ u32 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid) if ((intid + i) < VGIC_NR_SGIS || (intid + i) >= nr_irqs) continue; - irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + irq = vgic_get_vcpu_irq(vcpu, intid + i); if (irq->config == VGIC_CONFIG_LEVEL && irq->line_level) val |= (1U << i); @@ -799,7 +799,7 @@ void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid, if ((intid + i) < VGIC_NR_SGIS || (intid + i) >= nr_irqs) continue; - irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + irq = vgic_get_vcpu_irq(vcpu, intid + i); /* * Line level is set irrespective of irq type diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c index ae5a44d5702d1..381673f03c395 100644 --- a/arch/arm64/kvm/vgic/vgic-v2.c +++ b/arch/arm64/kvm/vgic/vgic-v2.c @@ -72,7 +72,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu) kvm_notify_acked_irq(vcpu->kvm, 0, intid - VGIC_NR_PRIVATE_IRQS); - irq = vgic_get_irq(vcpu->kvm, vcpu, intid); + irq = vgic_get_vcpu_irq(vcpu, intid); raw_spin_lock(&irq->irq_lock); diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index b217b256853c2..f267bc2486a18 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -65,7 +65,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) kvm_notify_acked_irq(vcpu->kvm, 0, intid - VGIC_NR_PRIVATE_IRQS); - irq = vgic_get_irq(vcpu->kvm, vcpu, intid); + irq = vgic_get_vcpu_irq(vcpu, intid); if (!irq) /* An LPI could have been unmapped. */ continue; diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c index 74a67ad87f29d..eedecbbbcf31b 100644 --- a/arch/arm64/kvm/vgic/vgic-v4.c +++ b/arch/arm64/kvm/vgic/vgic-v4.c @@ -123,7 +123,7 @@ static void vgic_v4_enable_vsgis(struct kvm_vcpu *vcpu) * IRQ. The SGI code will do its magic. */ for (i = 0; i < VGIC_NR_SGIS; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, i); struct irq_desc *desc; unsigned long flags; int ret; @@ -160,7 +160,7 @@ static void vgic_v4_disable_vsgis(struct kvm_vcpu *vcpu) int i; for (i = 0; i < VGIC_NR_SGIS; i++) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, i); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, i); struct irq_desc *desc; unsigned long flags; int ret; diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index f50274fd55815..cc8c6b9b5dd8b 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -84,17 +84,11 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid) * struct vgic_irq. It also increases the refcount, so any caller is expected * to call vgic_put_irq() once it's finished with this IRQ. */ -struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, - u32 intid) +struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid) { - /* SGIs and PPIs */ - if (intid <= VGIC_MAX_PRIVATE) { - intid = array_index_nospec(intid, VGIC_MAX_PRIVATE + 1); - return &vcpu->arch.vgic_cpu.private_irqs[intid]; - } - /* SPIs */ - if (intid < (kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) { + if (intid >= VGIC_NR_PRIVATE_IRQS && + intid < (kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) { intid = array_index_nospec(intid, kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS); return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS]; } @@ -106,6 +100,20 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, return NULL; } +struct vgic_irq *vgic_get_vcpu_irq(struct kvm_vcpu *vcpu, u32 intid) +{ + if (WARN_ON(!vcpu)) + return NULL; + + /* SGIs and PPIs */ + if (intid < VGIC_NR_PRIVATE_IRQS) { + intid = array_index_nospec(intid, VGIC_NR_PRIVATE_IRQS); + return &vcpu->arch.vgic_cpu.private_irqs[intid]; + } + + return vgic_get_irq(vcpu->kvm, intid); +} + /* * We can't do anything in here, because we lack the kvm pointer to * lock and remove the item from the lpi_list. So we keep this function @@ -437,7 +445,10 @@ int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, trace_vgic_update_irq_pending(vcpu ? vcpu->vcpu_idx : 0, intid, level); - irq = vgic_get_irq(kvm, vcpu, intid); + if (intid < VGIC_NR_PRIVATE_IRQS) + irq = vgic_get_vcpu_irq(vcpu, intid); + else + irq = vgic_get_irq(kvm, intid); if (!irq) return -EINVAL; @@ -499,7 +510,7 @@ static inline void kvm_vgic_unmap_irq(struct vgic_irq *irq) int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, u32 vintid, struct irq_ops *ops) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, vintid); unsigned long flags; int ret; @@ -524,7 +535,7 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, */ void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, vintid); unsigned long flags; if (!irq->hw) @@ -547,7 +558,7 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid) if (!vgic_initialized(vcpu->kvm)) return -EAGAIN; - irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); + irq = vgic_get_vcpu_irq(vcpu, vintid); BUG_ON(!irq); raw_spin_lock_irqsave(&irq->irq_lock, flags); @@ -560,7 +571,7 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid) int kvm_vgic_get_map(struct kvm_vcpu *vcpu, unsigned int vintid) { - struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, vintid); unsigned long flags; int ret = -1; @@ -596,7 +607,7 @@ int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner) if (!irq_is_ppi(intid) && !vgic_valid_spi(vcpu->kvm, intid)) return -EINVAL; - irq = vgic_get_irq(vcpu->kvm, vcpu, intid); + irq = vgic_get_vcpu_irq(vcpu, intid); raw_spin_lock_irqsave(&irq->irq_lock, flags); if (irq->owner && irq->owner != owner) ret = -EEXIST; @@ -1008,7 +1019,7 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid) if (!vgic_initialized(vcpu->kvm)) return false; - irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); + irq = vgic_get_vcpu_irq(vcpu, vintid); raw_spin_lock_irqsave(&irq->irq_lock, flags); map_is_active = irq->hw && irq->active; raw_spin_unlock_irqrestore(&irq->irq_lock, flags); diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index f2486b4d9f956..122d95b4e2845 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -179,8 +179,8 @@ int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, const struct vgic_register_region * vgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev, gpa_t addr, int len); -struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, - u32 intid); +struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid); +struct vgic_irq *vgic_get_vcpu_irq(struct kvm_vcpu *vcpu, u32 intid); void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq); bool vgic_get_phys_line_level(struct vgic_irq *irq); void vgic_irq_set_phys_pending(struct vgic_irq *irq, bool pending); diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 8dfb2fa51d127..eb17f59e543c4 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -62,6 +62,8 @@ HW_DBM KVM_HVHE KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE +MPAM +MPAM_HCR MTE MTE_ASYMM SME diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 283279af932ce..b081b54d6d227 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1200,7 +1200,7 @@ UnsignedEnum 55:52 BRBE 0b0001 IMP 0b0010 BRBE_V1P1 EndEnum -Enum 51:48 MTPMU +SignedEnum 51:48 MTPMU 0b0000 NI_IMPDEF 0b0001 IMP 0b1111 NI @@ -1208,6 +1208,7 @@ EndEnum UnsignedEnum 47:44 TraceBuffer 0b0000 NI 0b0001 IMP + 0b0010 TRBE_V1P1 EndEnum UnsignedEnum 43:40 TraceFilt 0b0000 NI @@ -1224,11 +1225,18 @@ UnsignedEnum 35:32 PMSVer 0b0011 V1P2 0b0100 V1P3 0b0101 V1P4 + 0b0110 V1P5 EndEnum Field 31:28 CTX_CMPs -Res0 27:24 +UnsignedEnum 27:24 SEBEP + 0b0000 NI + 0b0001 IMP +EndEnum Field 23:20 WRPs -Res0 19:16 +UnsignedEnum 19:16 PMSS + 0b0000 NI + 0b0001 IMP +EndEnum Field 15:12 BRPs UnsignedEnum 11:8 PMUVer 0b0000 NI @@ -1288,6 +1296,32 @@ Field 15:8 BRPs Field 7:0 SYSPMUID EndSysreg +Sysreg ID_AA64DFR2_EL1 3 0 0 5 2 +Res0 63:28 +UnsignedEnum 27:24 TRBE_EXC + 0b0000 NI + 0b0001 IMP +EndEnum +UnsignedEnum 23:20 SPE_nVM + 0b0000 NI + 0b0001 IMP +EndEnum +UnsignedEnum 19:16 SPE_EXC + 0b0000 NI + 0b0001 IMP +EndEnum +Res0 15:8 +UnsignedEnum 7:4 BWE + 0b0000 NI + 0b0001 FEAT_BWE + 0b0002 FEAT_BWE2 +EndEnum +UnsignedEnum 3:0 STEP + 0b0000 NI + 0b0001 IMP +EndEnum +EndSysreg + Sysreg ID_AA64AFR0_EL1 3 0 0 5 4 Res0 63:32 Field 31:28 IMPDEF7 @@ -2400,6 +2434,41 @@ Field 1 AFSR1_EL1 Field 0 AFSR0_EL1 EndSysregFields +Sysreg MDCR_EL2 3 4 1 1 1 +Res0 63:51 +Field 50 EnSTEPOP +Res0 49:44 +Field 43 EBWE +Res0 42 +Field 41:40 PMEE +Res0 39:37 +Field 36 HPMFZS +Res0 35:32 +Field 31:30 PMSSE +Field 29 HPMFZO +Field 28 MTPME +Field 27 TDCC +Field 26 HLP +Field 25:24 E2TB +Field 23 HCCD +Res0 22:20 +Field 19 TTRF +Res0 18 +Field 17 HPMD +Res0 16 +Field 15 EnSPM +Field 14 TPMS +Field 13:12 E2PB +Field 11 TDRA +Field 10 TDOSA +Field 9 TDA +Field 8 TDE +Field 7 HPME +Field 6 TPM +Field 5 TPMCR +Field 4:0 HPMN +EndSysreg + Sysreg HFGRTR_EL2 3 4 1 1 4 Fields HFGxTR_EL2 EndSysreg @@ -2749,6 +2818,126 @@ Field 1 E2SPE Field 0 E0HSPE EndSysreg +Sysreg MPAMHCR_EL2 3 4 10 4 0 +Res0 63:32 +Field 31 TRAP_MPAMIDR_EL1 +Res0 30:9 +Field 8 GSTAPP_PLK +Res0 7:2 +Field 1 EL1_VPMEN +Field 0 EL0_VPMEN +EndSysreg + +Sysreg MPAMVPMV_EL2 3 4 10 4 1 +Res0 63:32 +Field 31 VPM_V31 +Field 30 VPM_V30 +Field 29 VPM_V29 +Field 28 VPM_V28 +Field 27 VPM_V27 +Field 26 VPM_V26 +Field 25 VPM_V25 +Field 24 VPM_V24 +Field 23 VPM_V23 +Field 22 VPM_V22 +Field 21 VPM_V21 +Field 20 VPM_V20 +Field 19 VPM_V19 +Field 18 VPM_V18 +Field 17 VPM_V17 +Field 16 VPM_V16 +Field 15 VPM_V15 +Field 14 VPM_V14 +Field 13 VPM_V13 +Field 12 VPM_V12 +Field 11 VPM_V11 +Field 10 VPM_V10 +Field 9 VPM_V9 +Field 8 VPM_V8 +Field 7 VPM_V7 +Field 6 VPM_V6 +Field 5 VPM_V5 +Field 4 VPM_V4 +Field 3 VPM_V3 +Field 2 VPM_V2 +Field 1 VPM_V1 +Field 0 VPM_V0 +EndSysreg + +Sysreg MPAM2_EL2 3 4 10 5 0 +Field 63 MPAMEN +Res0 62:59 +Field 58 TIDR +Res0 57 +Field 56 ALTSP_HFC +Field 55 ALTSP_EL2 +Field 54 ALTSP_FRCD +Res0 53:51 +Field 50 EnMPAMSM +Field 49 TRAPMPAM0EL1 +Field 48 TRAPMPAM1EL1 +Field 47:40 PMG_D +Field 39:32 PMG_I +Field 31:16 PARTID_D +Field 15:0 PARTID_I +EndSysreg + +Sysreg MPAMVPM0_EL2 3 4 10 6 0 +Field 63:48 PhyPARTID3 +Field 47:32 PhyPARTID2 +Field 31:16 PhyPARTID1 +Field 15:0 PhyPARTID0 +EndSysreg + +Sysreg MPAMVPM1_EL2 3 4 10 6 1 +Field 63:48 PhyPARTID7 +Field 47:32 PhyPARTID6 +Field 31:16 PhyPARTID5 +Field 15:0 PhyPARTID4 +EndSysreg + +Sysreg MPAMVPM2_EL2 3 4 10 6 2 +Field 63:48 PhyPARTID11 +Field 47:32 PhyPARTID10 +Field 31:16 PhyPARTID9 +Field 15:0 PhyPARTID8 +EndSysreg + +Sysreg MPAMVPM3_EL2 3 4 10 6 3 +Field 63:48 PhyPARTID15 +Field 47:32 PhyPARTID14 +Field 31:16 PhyPARTID13 +Field 15:0 PhyPARTID12 +EndSysreg + +Sysreg MPAMVPM4_EL2 3 4 10 6 4 +Field 63:48 PhyPARTID19 +Field 47:32 PhyPARTID18 +Field 31:16 PhyPARTID17 +Field 15:0 PhyPARTID16 +EndSysreg + +Sysreg MPAMVPM5_EL2 3 4 10 6 5 +Field 63:48 PhyPARTID23 +Field 47:32 PhyPARTID22 +Field 31:16 PhyPARTID21 +Field 15:0 PhyPARTID20 +EndSysreg + +Sysreg MPAMVPM6_EL2 3 4 10 6 6 +Field 63:48 PhyPARTID27 +Field 47:32 PhyPARTID26 +Field 31:16 PhyPARTID25 +Field 15:0 PhyPARTID24 +EndSysreg + +Sysreg MPAMVPM7_EL2 3 4 10 6 7 +Field 63:48 PhyPARTID31 +Field 47:32 PhyPARTID30 +Field 31:16 PhyPARTID29 +Field 15:0 PhyPARTID28 +EndSysreg + Sysreg CONTEXTIDR_EL2 3 4 13 0 1 Fields CONTEXTIDR_ELx EndSysreg @@ -2781,6 +2970,10 @@ Sysreg FAR_EL12 3 5 6 0 0 Field 63:0 ADDR EndSysreg +Sysreg MPAM1_EL12 3 5 10 5 0 +Fields MPAM1_ELx +EndSysreg + Sysreg CONTEXTIDR_EL12 3 5 13 0 1 Fields CONTEXTIDR_ELx EndSysreg @@ -2831,8 +3024,7 @@ Field 13 AMEC1 Field 12 AMEC0 Field 11 HAFT Field 10 PTTWI -Field 9:8 SKL1 -Field 7:6 SKL0 +Res0 9:6 Field 5 D128 Field 4 AIE Field 3 POE @@ -2895,6 +3087,10 @@ Sysreg PIRE0_EL12 3 5 10 2 2 Fields PIRx_ELx EndSysreg +Sysreg PIRE0_EL2 3 4 10 2 2 +Fields PIRx_ELx +EndSysreg + Sysreg PIR_EL1 3 0 10 2 3 Fields PIRx_ELx EndSysreg @@ -2915,6 +3111,10 @@ Sysreg POR_EL1 3 0 10 2 4 Fields PIRx_ELx EndSysreg +Sysreg POR_EL2 3 4 10 2 4 +Fields PIRx_ELx +EndSysreg + Sysreg POR_EL12 3 5 10 2 4 Fields PIRx_ELx EndSysreg @@ -2953,6 +3153,22 @@ Res0 1 Field 0 EN EndSysreg +Sysreg MPAMIDR_EL1 3 0 10 4 4 +Res0 63:62 +Field 61 HAS_SDEFLT +Field 60 HAS_FORCE_NS +Field 59 SP4 +Field 58 HAS_TIDR +Field 57 HAS_ALTSP +Res0 56:40 +Field 39:32 PMG_MAX +Res0 31:21 +Field 20:18 VPMR_MAX +Field 17 HAS_HCR +Res0 16 +Field 15:0 PARTID_MAX +EndSysreg + Sysreg LORID_EL1 3 0 10 4 7 Res0 63:24 Field 23:16 LD @@ -2960,6 +3176,27 @@ Res0 15:8 Field 7:0 LR EndSysreg +Sysreg MPAM1_EL1 3 0 10 5 0 +Field 63 MPAMEN +Res0 62:61 +Field 60 FORCED_NS +Res0 59:55 +Field 54 ALTSP_FRCD +Res0 53:48 +Field 47:40 PMG_D +Field 39:32 PMG_I +Field 31:16 PARTID_D +Field 15:0 PARTID_I +EndSysreg + +Sysreg MPAM0_EL1 3 0 10 5 1 +Res0 63:48 +Field 47:40 PMG_D +Field 39:32 PMG_I +Field 31:16 PARTID_D +Field 15:0 PARTID_I +EndSysreg + Sysreg ISR_EL1 3 0 12 1 0 Res0 63:11 Field 10 IS diff --git a/arch/hexagon/include/asm/spinlock_types.h b/arch/hexagon/include/asm/spinlock_types.h index d5f66495b670f..63add2d863e81 100644 --- a/arch/hexagon/include/asm/spinlock_types.h +++ b/arch/hexagon/include/asm/spinlock_types.h @@ -9,7 +9,7 @@ #define _ASM_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif typedef struct { diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index d9fce0fd475a0..dae3a9104ca65 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -23,6 +23,7 @@ config LOONGARCH select ARCH_HAS_KERNEL_FPU_SUPPORT if CPU_HAS_FPU select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + select ARCH_HAS_PREEMPT_LAZY select ARCH_HAS_PTE_DEVMAP select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_MEMORY @@ -66,6 +67,7 @@ config LOONGARCH select ARCH_SUPPORTS_LTO_CLANG select ARCH_SUPPORTS_LTO_CLANG_THIN select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_RT select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF select ARCH_USE_QUEUED_RWLOCKS @@ -155,6 +157,7 @@ config LOONGARCH select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP + select HAVE_POSIX_CPU_TIMERS_TASK_WORK select HAVE_PREEMPT_DYNAMIC_KEY select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index ae3f80622f4c6..567bd122a9ee4 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -59,7 +59,7 @@ endif ifdef CONFIG_64BIT ld-emul = $(64bit-emul) -cflags-y += -mabi=lp64s +cflags-y += -mabi=lp64s -mcmodel=normal endif cflags-y += -pipe $(CC_FLAGS_NO_FPU) @@ -104,7 +104,7 @@ ifdef CONFIG_OBJTOOL KBUILD_CFLAGS += -fno-jump-tables endif -KBUILD_RUSTFLAGS += --target=loongarch64-unknown-none-softfloat +KBUILD_RUSTFLAGS += --target=loongarch64-unknown-none-softfloat -Ccode-model=small KBUILD_RUSTFLAGS_KERNEL += -Zdirect-access-external-data=yes KBUILD_RUSTFLAGS_MODULE += -Zdirect-access-external-data=no diff --git a/arch/loongarch/boot/dts/loongson-2k1000.dtsi b/arch/loongarch/boot/dts/loongson-2k1000.dtsi index 92180140eb56e..8dff2aa524171 100644 --- a/arch/loongarch/boot/dts/loongson-2k1000.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k1000.dtsi @@ -266,7 +266,7 @@ status = "disabled"; }; - dma-controller@1fe00c20 { + apbdma2: dma-controller@1fe00c20 { compatible = "loongson,ls2k1000-apbdma"; reg = <0x0 0x1fe00c20 0x0 0x8>; interrupt-parent = <&liointc1>; @@ -276,7 +276,7 @@ status = "disabled"; }; - dma-controller@1fe00c30 { + apbdma3: dma-controller@1fe00c30 { compatible = "loongson,ls2k1000-apbdma"; reg = <0x0 0x1fe00c30 0x0 0x8>; interrupt-parent = <&liointc1>; @@ -352,6 +352,19 @@ status = "disabled"; }; + i2s: i2s@1fe2d000 { + compatible = "loongson,ls2k1000-i2s"; + reg = <0 0x1fe2d000 0 0x14>, + <0 0x1fe00438 0 0x8>; + interrupt-parent = <&liointc0>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_APB_CLK>; + dmas = <&apbdma2 0>, <&apbdma3 0>; + dma-names = "tx", "rx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + spi0: spi@1fff0220 { compatible = "loongson,ls2k1000-spi"; reg = <0x0 0x1fff0220 0x0 0x10>; diff --git a/arch/loongarch/boot/dts/loongson-2k2000.dtsi b/arch/loongarch/boot/dts/loongson-2k2000.dtsi index 0953c57078256..b4ff55a33e90b 100644 --- a/arch/loongarch/boot/dts/loongson-2k2000.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k2000.dtsi @@ -173,6 +173,22 @@ status = "disabled"; }; + i2c@1fe00120 { + compatible = "loongson,ls2k-i2c"; + reg = <0x0 0x1fe00120 0x0 0x8>; + interrupt-parent = <&liointc>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c@1fe00130 { + compatible = "loongson,ls2k-i2c"; + reg = <0x0 0x1fe00130 0x0 0x8>; + interrupt-parent = <&liointc>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + uart0: serial@1fe001e0 { compatible = "ns16550a"; reg = <0x0 0x1fe001e0 0x0 0x10>; @@ -243,9 +259,11 @@ status = "disabled"; }; - hda@7,0 { + i2s@7,0 { reg = <0x3800 0x0 0x0 0x0 0x0>; - interrupts = <58 IRQ_TYPE_LEVEL_HIGH>; + interrupts = <78 IRQ_TYPE_LEVEL_HIGH>, + <79 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "tx", "rx"; interrupt-parent = <&pic>; status = "disabled"; }; diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 75b366407a60a..4dffc90192f78 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -1,4 +1,5 @@ # CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_ZSTD=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_NO_HZ=y @@ -70,6 +71,14 @@ CONFIG_ACPI_IPMI=m CONFIG_ACPI_HOTPLUG_CPU=y CONFIG_ACPI_PCI_SLOT=y CONFIG_ACPI_HOTPLUG_MEMORY=y +CONFIG_ACPI_BGRT=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_LOONGSON3_CPUFREQ=m CONFIG_VIRTUALIZATION=y CONFIG_KVM=m CONFIG_JUMP_LABEL=y @@ -78,6 +87,9 @@ CONFIG_MODULE_FORCE_LOAD=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y +CONFIG_MODULE_COMPRESS=y +CONFIG_MODULE_COMPRESS_ZSTD=y +CONFIG_MODULE_DECOMPRESS=y CONFIG_BLK_DEV_ZONED=y CONFIG_BLK_DEV_THROTTLING=y CONFIG_BLK_WBT=y @@ -85,6 +97,8 @@ CONFIG_BLK_CGROUP_IOLATENCY=y CONFIG_BLK_CGROUP_FC_APPID=y CONFIG_BLK_CGROUP_IOCOST=y CONFIG_BLK_CGROUP_IOPRIO=y +CONFIG_BLK_INLINE_ENCRYPTION=y +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y CONFIG_PARTITION_ADVANCED=y CONFIG_BSD_DISKLABEL=y CONFIG_UNIXWARE_DISKLABEL=y @@ -413,7 +427,16 @@ CONFIG_PARPORT_PC=y CONFIG_PARPORT_SERIAL=y CONFIG_PARPORT_PC_FIFO=y CONFIG_ZRAM=m +CONFIG_ZRAM_BACKEND_LZ4=y +CONFIG_ZRAM_BACKEND_LZ4HC=y +CONFIG_ZRAM_BACKEND_ZSTD=y +CONFIG_ZRAM_BACKEND_DEFLATE=y +CONFIG_ZRAM_BACKEND_842=y +CONFIG_ZRAM_BACKEND_LZO=y CONFIG_ZRAM_DEF_COMP_ZSTD=y +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_ZRAM_MULTI_COMP=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_DRBD=m CONFIG_BLK_DEV_NBD=m @@ -433,6 +456,9 @@ CONFIG_NVME_TARGET_RDMA=m CONFIG_NVME_TARGET_FC=m CONFIG_NVME_TARGET_TCP=m CONFIG_EEPROM_AT24=m +CONFIG_PVPANIC=y +CONFIG_PVPANIC_MMIO=m +CONFIG_PVPANIC_PCI=m CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y CONFIG_CHR_DEV_SG=y @@ -470,12 +496,10 @@ CONFIG_PATA_ATIIXP=y CONFIG_PATA_PCMCIA=m CONFIG_MD=y CONFIG_BLK_DEV_MD=m -CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m CONFIG_MD_RAID10=m CONFIG_MD_RAID456=m -CONFIG_MD_MULTIPATH=m CONFIG_BCACHE=m CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=m @@ -489,6 +513,16 @@ CONFIG_DM_ZERO=m CONFIG_DM_MULTIPATH=m CONFIG_DM_MULTIPATH_QL=m CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_MULTIPATH_HST=m +CONFIG_DM_MULTIPATH_IOA=m +CONFIG_DM_INIT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=m +CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y +CONFIG_DM_VERITY_FEC=y +CONFIG_DM_INTEGRITY=m +CONFIG_DM_ZONED=m +CONFIG_DM_VDO=m CONFIG_TARGET_CORE=m CONFIG_TCM_IBLOCK=m CONFIG_TCM_FILEIO=m @@ -500,6 +534,13 @@ CONFIG_NETDEVICES=y CONFIG_BONDING=m CONFIG_DUMMY=y CONFIG_WIREGUARD=m +CONFIG_IFB=m +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m CONFIG_MACVLAN=m CONFIG_MACVTAP=m CONFIG_IPVLAN=m @@ -580,12 +621,14 @@ CONFIG_PPP_ASYNC=m CONFIG_PPP_SYNC_TTY=m CONFIG_USB_RTL8150=m CONFIG_USB_RTL8152=m +CONFIG_USB_USBNET=m # CONFIG_USB_NET_AX8817X is not set # CONFIG_USB_NET_AX88179_178A is not set CONFIG_USB_NET_CDC_EEM=m CONFIG_USB_NET_HUAWEI_CDC_NCM=m CONFIG_USB_NET_CDC_MBIM=m # CONFIG_USB_NET_NET1080 is not set +CONFIG_USB_NET_RNDIS_HOST=m # CONFIG_USB_BELKIN is not set # CONFIG_USB_ARMLINUX is not set # CONFIG_USB_NET_ZAURUS is not set @@ -594,10 +637,11 @@ CONFIG_ATH9K_HTC=m CONFIG_IWLWIFI=m CONFIG_IWLDVM=m CONFIG_IWLMVM=m -CONFIG_HOSTAP=m CONFIG_MT7601U=m CONFIG_RT2X00=m CONFIG_RT2800USB=m +CONFIG_RTL8180=m +CONFIG_RTL8187=m CONFIG_RTL8192CE=m CONFIG_RTL8192SE=m CONFIG_RTL8192DE=m @@ -607,18 +651,26 @@ CONFIG_RTL8188EE=m CONFIG_RTL8192EE=m CONFIG_RTL8821AE=m CONFIG_RTL8192CU=m +CONFIG_RTL8192DU=m # CONFIG_RTLWIFI_DEBUG is not set CONFIG_RTL8XXXU=m CONFIG_RTW88=m CONFIG_RTW88_8822BE=m +CONFIG_RTW88_8822BU=m CONFIG_RTW88_8822CE=m +CONFIG_RTW88_8822CU=m CONFIG_RTW88_8723DE=m +CONFIG_RTW88_8723DU=m CONFIG_RTW88_8821CE=m +CONFIG_RTW88_8821CU=m CONFIG_RTW89=m +CONFIG_RTW89_8851BE=m CONFIG_RTW89_8852AE=m +CONFIG_RTW89_8852BE=m +CONFIG_RTW89_8852BTE=m CONFIG_RTW89_8852CE=m +CONFIG_RTW89_8922AE=m CONFIG_ZD1211RW=m -CONFIG_USB_NET_RNDIS_WLAN=m CONFIG_USB4_NET=m CONFIG_INPUT_MOUSEDEV=y CONFIG_INPUT_MOUSEDEV_PSAUX=y @@ -651,6 +703,9 @@ CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=m CONFIG_I2C_CHARDEV=y CONFIG_I2C_PIIX4=y +CONFIG_I2C_DESIGNWARE_CORE=y +CONFIG_I2C_DESIGNWARE_SLAVE=y +CONFIG_I2C_DESIGNWARE_PCI=y CONFIG_I2C_GPIO=y CONFIG_I2C_LS2X=y CONFIG_SPI=y @@ -727,11 +782,22 @@ CONFIG_SND_HDA_CODEC_CONEXANT=y CONFIG_SND_USB_AUDIO=m CONFIG_SND_SOC=m CONFIG_SND_SOC_LOONGSON_CARD=m +CONFIG_SND_SOC_ES7134=m +CONFIG_SND_SOC_ES7241=m +CONFIG_SND_SOC_ES8311=m +CONFIG_SND_SOC_ES8316=m +CONFIG_SND_SOC_ES8323=m +CONFIG_SND_SOC_ES8326=m +CONFIG_SND_SOC_ES8328_I2C=m +CONFIG_SND_SOC_ES8328_SPI=m +CONFIG_SND_SOC_UDA1334=m +CONFIG_SND_SOC_UDA1342=m CONFIG_SND_VIRTIO=m CONFIG_HIDRAW=y CONFIG_UHID=m CONFIG_HID_A4TECH=m CONFIG_HID_CHERRY=m +CONFIG_HID_ELAN=m CONFIG_HID_LOGITECH=m CONFIG_HID_LOGITECH_DJ=m CONFIG_LOGITECH_FF=y @@ -740,7 +806,11 @@ CONFIG_LOGIG940_FF=y CONFIG_HID_MICROSOFT=m CONFIG_HID_MULTITOUCH=m CONFIG_HID_SUNPLUS=m +CONFIG_HID_WACOM=m CONFIG_USB_HIDDEV=y +CONFIG_I2C_HID_ACPI=m +CONFIG_I2C_HID_OF=m +CONFIG_I2C_HID_OF_ELAN=m CONFIG_USB=y CONFIG_USB_OTG=y CONFIG_USB_MON=y @@ -775,7 +845,7 @@ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_EFI=y CONFIG_RTC_DRV_LOONGSON=y CONFIG_DMADEVICES=y -CONFIG_LS2X_APB_DMA=y +CONFIG_LOONGSON2_APB_DMA=y CONFIG_UDMABUF=y CONFIG_DMABUF_HEAPS=y CONFIG_DMABUF_HEAPS_SYSTEM=y @@ -852,6 +922,9 @@ CONFIG_F2FS_FS=m CONFIG_F2FS_FS_SECURITY=y CONFIG_F2FS_CHECK_FS=y CONFIG_F2FS_FS_COMPRESSION=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y +CONFIG_FS_VERITY=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA=y @@ -904,16 +977,14 @@ CONFIG_SQUASHFS_ZSTD=y CONFIG_MINIX_FS=m CONFIG_ROMFS_FS=m CONFIG_PSTORE=m -CONFIG_PSTORE_LZO_COMPRESS=m -CONFIG_PSTORE_LZ4_COMPRESS=m -CONFIG_PSTORE_LZ4HC_COMPRESS=m -CONFIG_PSTORE_842_COMPRESS=y -CONFIG_PSTORE_ZSTD_COMPRESS=y -CONFIG_PSTORE_ZSTD_COMPRESS_DEFAULT=y +CONFIG_PSTORE_COMPRESS=y CONFIG_SYSV_FS=m CONFIG_UFS_FS=m CONFIG_EROFS_FS=m CONFIG_EROFS_FS_ZIP_LZMA=y +CONFIG_EROFS_FS_ZIP_DEFLATE=y +CONFIG_EROFS_FS_ZIP_ZSTD=y +CONFIG_EROFS_FS_ONDEMAND=y CONFIG_EROFS_FS_PCPU_KTHREAD=y CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h index 9c2ca785faa9b..a0ca84da8541d 100644 --- a/arch/loongarch/include/asm/irq.h +++ b/arch/loongarch/include/asm/irq.h @@ -65,6 +65,7 @@ extern struct acpi_vector_group pch_group[MAX_IO_PICS]; extern struct acpi_vector_group msi_group[MAX_IO_PICS]; #define CORES_PER_EIO_NODE 4 +#define CORES_PER_VEIO_NODE 256 #define LOONGSON_CPU_UART0_VEC 10 /* CPU UART0 */ #define LOONGSON_CPU_THSENS_VEC 14 /* CPU Thsens */ diff --git a/arch/loongarch/include/asm/jump_label.h b/arch/loongarch/include/asm/jump_label.h index 29acfe3de3faa..8a924bd69d196 100644 --- a/arch/loongarch/include/asm/jump_label.h +++ b/arch/loongarch/include/asm/jump_label.h @@ -13,18 +13,22 @@ #define JUMP_LABEL_NOP_SIZE 4 -#define JUMP_TABLE_ENTRY \ +/* This macro is also expanded on the Rust side. */ +#define JUMP_TABLE_ENTRY(key, label) \ ".pushsection __jump_table, \"aw\" \n\t" \ ".align 3 \n\t" \ - ".long 1b - ., %l[l_yes] - . \n\t" \ - ".quad %0 - . \n\t" \ + ".long 1b - ., " label " - . \n\t" \ + ".quad " key " - . \n\t" \ ".popsection \n\t" +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1: nop \n\t" \ + JUMP_TABLE_ENTRY(key, label) + static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch) { asm goto( - "1: nop \n\t" - JUMP_TABLE_ENTRY + ARCH_STATIC_BRANCH_ASM("%0", "%l[l_yes]") : : "i"(&((char *)key)[branch]) : : l_yes); return false; @@ -37,7 +41,7 @@ static __always_inline bool arch_static_branch_jump(struct static_key * const ke { asm goto( "1: b %l[l_yes] \n\t" - JUMP_TABLE_ENTRY + JUMP_TABLE_ENTRY("%0", "%l[l_yes]") : : "i"(&((char *)key)[branch]) : : l_yes); return false; diff --git a/arch/loongarch/include/asm/kvm_eiointc.h b/arch/loongarch/include/asm/kvm_eiointc.h new file mode 100644 index 0000000000000..a3a40aba8acf2 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_eiointc.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#ifndef __ASM_KVM_EIOINTC_H +#define __ASM_KVM_EIOINTC_H + +#include + +#define EIOINTC_IRQS 256 +#define EIOINTC_ROUTE_MAX_VCPUS 256 +#define EIOINTC_IRQS_U8_NUMS (EIOINTC_IRQS / 8) +#define EIOINTC_IRQS_U16_NUMS (EIOINTC_IRQS_U8_NUMS / 2) +#define EIOINTC_IRQS_U32_NUMS (EIOINTC_IRQS_U8_NUMS / 4) +#define EIOINTC_IRQS_U64_NUMS (EIOINTC_IRQS_U8_NUMS / 8) +/* map to ipnum per 32 irqs */ +#define EIOINTC_IRQS_NODETYPE_COUNT 16 + +#define EIOINTC_BASE 0x1400 +#define EIOINTC_SIZE 0x900 + +#define EIOINTC_NODETYPE_START 0xa0 +#define EIOINTC_NODETYPE_END 0xbf +#define EIOINTC_IPMAP_START 0xc0 +#define EIOINTC_IPMAP_END 0xc7 +#define EIOINTC_ENABLE_START 0x200 +#define EIOINTC_ENABLE_END 0x21f +#define EIOINTC_BOUNCE_START 0x280 +#define EIOINTC_BOUNCE_END 0x29f +#define EIOINTC_ISR_START 0x300 +#define EIOINTC_ISR_END 0x31f +#define EIOINTC_COREISR_START 0x400 +#define EIOINTC_COREISR_END 0x41f +#define EIOINTC_COREMAP_START 0x800 +#define EIOINTC_COREMAP_END 0x8ff + +#define EIOINTC_VIRT_BASE (0x40000000) +#define EIOINTC_VIRT_SIZE (0x1000) + +#define EIOINTC_VIRT_FEATURES (0x0) +#define EIOINTC_HAS_VIRT_EXTENSION (0) +#define EIOINTC_HAS_ENABLE_OPTION (1) +#define EIOINTC_HAS_INT_ENCODE (2) +#define EIOINTC_HAS_CPU_ENCODE (3) +#define EIOINTC_VIRT_HAS_FEATURES ((1U << EIOINTC_HAS_VIRT_EXTENSION) \ + | (1U << EIOINTC_HAS_ENABLE_OPTION) \ + | (1U << EIOINTC_HAS_INT_ENCODE) \ + | (1U << EIOINTC_HAS_CPU_ENCODE)) +#define EIOINTC_VIRT_CONFIG (0x4) +#define EIOINTC_ENABLE (1) +#define EIOINTC_ENABLE_INT_ENCODE (2) +#define EIOINTC_ENABLE_CPU_ENCODE (3) + +#define LOONGSON_IP_NUM 8 + +struct loongarch_eiointc { + spinlock_t lock; + struct kvm *kvm; + struct kvm_io_device device; + struct kvm_io_device device_vext; + uint32_t num_cpu; + uint32_t features; + uint32_t status; + + /* hardware state */ + union nodetype { + u64 reg_u64[EIOINTC_IRQS_NODETYPE_COUNT / 4]; + u32 reg_u32[EIOINTC_IRQS_NODETYPE_COUNT / 2]; + u16 reg_u16[EIOINTC_IRQS_NODETYPE_COUNT]; + u8 reg_u8[EIOINTC_IRQS_NODETYPE_COUNT * 2]; + } nodetype; + + /* one bit shows the state of one irq */ + union bounce { + u64 reg_u64[EIOINTC_IRQS_U64_NUMS]; + u32 reg_u32[EIOINTC_IRQS_U32_NUMS]; + u16 reg_u16[EIOINTC_IRQS_U16_NUMS]; + u8 reg_u8[EIOINTC_IRQS_U8_NUMS]; + } bounce; + + union isr { + u64 reg_u64[EIOINTC_IRQS_U64_NUMS]; + u32 reg_u32[EIOINTC_IRQS_U32_NUMS]; + u16 reg_u16[EIOINTC_IRQS_U16_NUMS]; + u8 reg_u8[EIOINTC_IRQS_U8_NUMS]; + } isr; + union coreisr { + u64 reg_u64[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U64_NUMS]; + u32 reg_u32[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U32_NUMS]; + u16 reg_u16[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U16_NUMS]; + u8 reg_u8[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U8_NUMS]; + } coreisr; + union enable { + u64 reg_u64[EIOINTC_IRQS_U64_NUMS]; + u32 reg_u32[EIOINTC_IRQS_U32_NUMS]; + u16 reg_u16[EIOINTC_IRQS_U16_NUMS]; + u8 reg_u8[EIOINTC_IRQS_U8_NUMS]; + } enable; + + /* use one byte to config ipmap for 32 irqs at once */ + union ipmap { + u64 reg_u64; + u32 reg_u32[EIOINTC_IRQS_U32_NUMS / 4]; + u16 reg_u16[EIOINTC_IRQS_U16_NUMS / 4]; + u8 reg_u8[EIOINTC_IRQS_U8_NUMS / 4]; + } ipmap; + /* use one byte to config coremap for one irq */ + union coremap { + u64 reg_u64[EIOINTC_IRQS / 8]; + u32 reg_u32[EIOINTC_IRQS / 4]; + u16 reg_u16[EIOINTC_IRQS / 2]; + u8 reg_u8[EIOINTC_IRQS]; + } coremap; + + DECLARE_BITMAP(sw_coreisr[EIOINTC_ROUTE_MAX_VCPUS][LOONGSON_IP_NUM], EIOINTC_IRQS); + uint8_t sw_coremap[EIOINTC_IRQS]; +}; + +int kvm_loongarch_register_eiointc_device(void); +void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level); + +#endif /* __ASM_KVM_EIOINTC_H */ diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index d6bb72424027a..7b8367c39da85 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -18,8 +18,13 @@ #include #include +#include +#include +#include #include +#define __KVM_HAVE_ARCH_INTC_INITIALIZED + /* Loongarch KVM register ids */ #define KVM_GET_IOC_CSR_IDX(id) ((id & KVM_CSR_IDX_MASK) >> LOONGARCH_REG_SHIFT) #define KVM_GET_IOC_CPUCFG_IDX(id) ((id & KVM_CPUCFG_IDX_MASK) >> LOONGARCH_REG_SHIFT) @@ -44,6 +49,12 @@ struct kvm_vm_stat { struct kvm_vm_stat_generic generic; u64 pages; u64 hugepages; + u64 ipi_read_exits; + u64 ipi_write_exits; + u64 eiointc_read_exits; + u64 eiointc_write_exits; + u64 pch_pic_read_exits; + u64 pch_pic_write_exits; }; struct kvm_vcpu_stat { @@ -84,7 +95,7 @@ struct kvm_world_switch { * * For LOONGARCH_CSR_CPUID register, max CPUID size if 512 * For IPI hardware, max destination CPUID size 1024 - * For extioi interrupt controller, max destination CPUID size is 256 + * For eiointc interrupt controller, max destination CPUID size is 256 * For msgint interrupt controller, max supported CPUID size is 65536 * * Currently max CPUID is defined as 256 for KVM hypervisor, in future @@ -117,6 +128,9 @@ struct kvm_arch { s64 time_offset; struct kvm_context __percpu *vmcs; + struct loongarch_ipi *ipi; + struct loongarch_eiointc *eiointc; + struct loongarch_pch_pic *pch_pic; }; #define CSR_MAX_NUMS 0x800 @@ -221,6 +235,8 @@ struct kvm_vcpu_arch { int last_sched_cpu; /* mp state */ struct kvm_mp_state mp_state; + /* ipi state */ + struct ipi_state ipi_state; /* cpucfg */ u32 cpucfg[KVM_MAX_CPUCFG_REGS]; diff --git a/arch/loongarch/include/asm/kvm_ipi.h b/arch/loongarch/include/asm/kvm_ipi.h new file mode 100644 index 0000000000000..060163dfb4a38 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_ipi.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#ifndef __ASM_KVM_IPI_H +#define __ASM_KVM_IPI_H + +#include + +#define LARCH_INT_IPI 12 + +struct loongarch_ipi { + spinlock_t lock; + struct kvm *kvm; + struct kvm_io_device device; +}; + +struct ipi_state { + spinlock_t lock; + uint32_t status; + uint32_t en; + uint32_t set; + uint32_t clear; + uint64_t buf[4]; +}; + +#define IOCSR_IPI_BASE 0x1000 +#define IOCSR_IPI_SIZE 0x160 + +#define IOCSR_IPI_STATUS 0x000 +#define IOCSR_IPI_EN 0x004 +#define IOCSR_IPI_SET 0x008 +#define IOCSR_IPI_CLEAR 0x00c +#define IOCSR_IPI_BUF_20 0x020 +#define IOCSR_IPI_BUF_28 0x028 +#define IOCSR_IPI_BUF_30 0x030 +#define IOCSR_IPI_BUF_38 0x038 +#define IOCSR_IPI_SEND 0x040 +#define IOCSR_MAIL_SEND 0x048 +#define IOCSR_ANY_SEND 0x158 + +int kvm_loongarch_register_ipi_device(void); + +#endif diff --git a/arch/loongarch/include/asm/kvm_pch_pic.h b/arch/loongarch/include/asm/kvm_pch_pic.h new file mode 100644 index 0000000000000..e6df6a4c1c705 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_pch_pic.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#ifndef __ASM_KVM_PCH_PIC_H +#define __ASM_KVM_PCH_PIC_H + +#include + +#define PCH_PIC_SIZE 0x3e8 + +#define PCH_PIC_INT_ID_START 0x0 +#define PCH_PIC_INT_ID_END 0x7 +#define PCH_PIC_MASK_START 0x20 +#define PCH_PIC_MASK_END 0x27 +#define PCH_PIC_HTMSI_EN_START 0x40 +#define PCH_PIC_HTMSI_EN_END 0x47 +#define PCH_PIC_EDGE_START 0x60 +#define PCH_PIC_EDGE_END 0x67 +#define PCH_PIC_CLEAR_START 0x80 +#define PCH_PIC_CLEAR_END 0x87 +#define PCH_PIC_AUTO_CTRL0_START 0xc0 +#define PCH_PIC_AUTO_CTRL0_END 0xc7 +#define PCH_PIC_AUTO_CTRL1_START 0xe0 +#define PCH_PIC_AUTO_CTRL1_END 0xe7 +#define PCH_PIC_ROUTE_ENTRY_START 0x100 +#define PCH_PIC_ROUTE_ENTRY_END 0x13f +#define PCH_PIC_HTMSI_VEC_START 0x200 +#define PCH_PIC_HTMSI_VEC_END 0x23f +#define PCH_PIC_INT_IRR_START 0x380 +#define PCH_PIC_INT_IRR_END 0x38f +#define PCH_PIC_INT_ISR_START 0x3a0 +#define PCH_PIC_INT_ISR_END 0x3af +#define PCH_PIC_POLARITY_START 0x3e0 +#define PCH_PIC_POLARITY_END 0x3e7 +#define PCH_PIC_INT_ID_VAL 0x7000000UL +#define PCH_PIC_INT_ID_VER 0x1UL + +struct loongarch_pch_pic { + spinlock_t lock; + struct kvm *kvm; + struct kvm_io_device device; + uint64_t mask; /* 1:disable irq, 0:enable irq */ + uint64_t htmsi_en; /* 1:msi */ + uint64_t edge; /* 1:edge triggered, 0:level triggered */ + uint64_t auto_ctrl0; /* only use default value 00b */ + uint64_t auto_ctrl1; /* only use default value 00b */ + uint64_t last_intirr; /* edge detection */ + uint64_t irr; /* interrupt request register */ + uint64_t isr; /* interrupt service register */ + uint64_t polarity; /* 0: high level trigger, 1: low level trigger */ + uint8_t route_entry[64]; /* default value 0, route to int0: eiointc */ + uint8_t htmsi_vector[64]; /* irq route table for routing to eiointc */ + uint64_t pch_pic_base; +}; + +int kvm_loongarch_register_pch_pic_device(void); +void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level); +void pch_msi_set_irq(struct kvm *kvm, int irq, int level); + +#endif /* __ASM_KVM_PCH_PIC_H */ diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index 8bf0e6f515466..4f5a9441754e3 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -66,8 +66,9 @@ register unsigned long current_stack_pointer __asm__("$sp"); * - pending work-to-be-done flags are in LSW * - other flags in MSW */ -#define TIF_SIGPENDING 1 /* signal pending */ -#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ +#define TIF_NEED_RESCHED 0 /* rescheduling necessary */ +#define TIF_NEED_RESCHED_LAZY 1 /* lazy rescheduling necessary */ +#define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NOTIFY_RESUME 3 /* callback before returning to user */ #define TIF_NOTIFY_SIGNAL 4 /* signal notifications exist */ #define TIF_RESTORE_SIGMASK 5 /* restore signal mask in do_signal() */ @@ -88,8 +89,9 @@ register unsigned long current_stack_pointer __asm__("$sp"); #define TIF_LBT_CTX_LIVE 20 /* LBT context must be preserved */ #define TIF_PATCH_PENDING 21 /* pending live patching update */ -#define _TIF_SIGPENDING (1< +#define __KVM_HAVE_IRQ_LINE + /* * KVM LoongArch specific structures and definitions. * @@ -132,4 +134,22 @@ struct kvm_iocsr_entry { #define KVM_IRQCHIP_NUM_PINS 64 #define KVM_MAX_CORES 256 +#define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000001 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000002 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS 0x40000003 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE 0x2 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL 0x40000004 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED 0x3 + +#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005 +#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000006 +#define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index 46d7d40c87e38..a07d7eff4dc5f 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -127,7 +127,11 @@ void sync_counter(void) int constant_clockevent_init(void) { unsigned int cpu = smp_processor_id(); - unsigned long min_delta = 0x600; +#ifdef CONFIG_PREEMPT_RT + unsigned long min_delta = 100; +#else + unsigned long min_delta = 1000; +#endif unsigned long max_delta = (1UL << 48) - 1; struct clock_event_device *cd; static int irq = 0, timer_irq_installed = 0; diff --git a/arch/loongarch/kvm/Kconfig b/arch/loongarch/kvm/Kconfig index 248744b4d086d..97a811077ac3a 100644 --- a/arch/loongarch/kvm/Kconfig +++ b/arch/loongarch/kvm/Kconfig @@ -21,13 +21,16 @@ config KVM tristate "Kernel-based Virtual Machine (KVM) support" depends on AS_HAS_LVZ_EXTENSION select HAVE_KVM_DIRTY_RING_ACQ_REL + select HAVE_KVM_IRQ_ROUTING + select HAVE_KVM_IRQCHIP + select HAVE_KVM_MSI + select HAVE_KVM_READONLY_MEM select HAVE_KVM_VCPU_ASYNC_IOCTL select KVM_COMMON select KVM_GENERIC_DIRTYLOG_READ_PROTECT select KVM_GENERIC_HARDWARE_ENABLING select KVM_GENERIC_MMU_NOTIFIER select KVM_MMIO - select HAVE_KVM_READONLY_MEM select KVM_XFER_TO_GUEST_WORK select SCHED_INFO help diff --git a/arch/loongarch/kvm/Makefile b/arch/loongarch/kvm/Makefile index b2f4cbe01ae80..3a01292f71cc2 100644 --- a/arch/loongarch/kvm/Makefile +++ b/arch/loongarch/kvm/Makefile @@ -18,5 +18,9 @@ kvm-y += timer.o kvm-y += tlb.o kvm-y += vcpu.o kvm-y += vm.o +kvm-y += intc/ipi.o +kvm-y += intc/eiointc.o +kvm-y += intc/pch_pic.o +kvm-y += irqfd.o CFLAGS_exit.o += $(call cc-option,-Wno-override-init,) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index 90894f70ff4a5..69f3e3782cc9e 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -157,7 +157,7 @@ static int kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst) int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu) { int ret; - unsigned long val; + unsigned long *val; u32 addr, rd, rj, opcode; /* @@ -170,6 +170,7 @@ int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu) ret = EMULATE_DO_IOCSR; run->iocsr_io.phys_addr = addr; run->iocsr_io.is_write = 0; + val = &vcpu->arch.gprs[rd]; /* LoongArch is Little endian */ switch (opcode) { @@ -202,16 +203,25 @@ int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu) run->iocsr_io.is_write = 1; break; default: - ret = EMULATE_FAIL; - break; + return EMULATE_FAIL; } - if (ret == EMULATE_DO_IOCSR) { - if (run->iocsr_io.is_write) { - val = vcpu->arch.gprs[rd]; - memcpy(run->iocsr_io.data, &val, run->iocsr_io.len); - } - vcpu->arch.io_gpr = rd; + if (run->iocsr_io.is_write) { + if (!kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, run->iocsr_io.len, val)) + ret = EMULATE_DONE; + else + /* Save data and let user space to write it */ + memcpy(run->iocsr_io.data, val, run->iocsr_io.len); + + trace_kvm_iocsr(KVM_TRACE_IOCSR_WRITE, run->iocsr_io.len, addr, val); + } else { + if (!kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, run->iocsr_io.len, val)) + ret = EMULATE_DONE; + else + /* Save register id for iocsr read completion */ + vcpu->arch.io_gpr = rd; + + trace_kvm_iocsr(KVM_TRACE_IOCSR_READ, run->iocsr_io.len, addr, NULL); } return ret; @@ -447,19 +457,33 @@ int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst) } if (ret == EMULATE_DO_MMIO) { + trace_kvm_mmio(KVM_TRACE_MMIO_READ, run->mmio.len, run->mmio.phys_addr, NULL); + + /* + * If mmio device such as PCH-PIC is emulated in KVM, + * it need not return to user space to handle the mmio + * exception. + */ + ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, vcpu->arch.badv, + run->mmio.len, &vcpu->arch.gprs[rd]); + if (!ret) { + update_pc(&vcpu->arch); + vcpu->mmio_needed = 0; + return EMULATE_DONE; + } + /* Set for kvm_complete_mmio_read() use */ vcpu->arch.io_gpr = rd; run->mmio.is_write = 0; vcpu->mmio_is_write = 0; - trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, run->mmio.len, - run->mmio.phys_addr, NULL); - } else { - kvm_err("Read not supported Inst=0x%08x @%lx BadVaddr:%#lx\n", - inst.word, vcpu->arch.pc, vcpu->arch.badv); - kvm_arch_vcpu_dump_regs(vcpu); - vcpu->mmio_needed = 0; + return EMULATE_DO_MMIO; } + kvm_err("Read not supported Inst=0x%08x @%lx BadVaddr:%#lx\n", + inst.word, vcpu->arch.pc, vcpu->arch.badv); + kvm_arch_vcpu_dump_regs(vcpu); + vcpu->mmio_needed = 0; + return ret; } @@ -600,19 +624,29 @@ int kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst) } if (ret == EMULATE_DO_MMIO) { + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, run->mmio.len, run->mmio.phys_addr, data); + + /* + * If mmio device such as PCH-PIC is emulated in KVM, + * it need not return to user space to handle the mmio + * exception. + */ + ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, vcpu->arch.badv, run->mmio.len, data); + if (!ret) + return EMULATE_DONE; + run->mmio.is_write = 1; vcpu->mmio_needed = 1; vcpu->mmio_is_write = 1; - trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, run->mmio.len, - run->mmio.phys_addr, data); - } else { - vcpu->arch.pc = curr_pc; - kvm_err("Write not supported Inst=0x%08x @%lx BadVaddr:%#lx\n", - inst.word, vcpu->arch.pc, vcpu->arch.badv); - kvm_arch_vcpu_dump_regs(vcpu); - /* Rollback PC if emulation was unsuccessful */ + return EMULATE_DO_MMIO; } + vcpu->arch.pc = curr_pc; + kvm_err("Write not supported Inst=0x%08x @%lx BadVaddr:%#lx\n", + inst.word, vcpu->arch.pc, vcpu->arch.badv); + kvm_arch_vcpu_dump_regs(vcpu); + /* Rollback PC if emulation was unsuccessful */ + return ret; } diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c new file mode 100644 index 0000000000000..f39929d7bf8a2 --- /dev/null +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#include +#include +#include + +static void eiointc_set_sw_coreisr(struct loongarch_eiointc *s) +{ + int ipnum, cpu, irq_index, irq_mask, irq; + + for (irq = 0; irq < EIOINTC_IRQS; irq++) { + ipnum = s->ipmap.reg_u8[irq / 32]; + if (!(s->status & BIT(EIOINTC_ENABLE_INT_ENCODE))) { + ipnum = count_trailing_zeros(ipnum); + ipnum = (ipnum >= 0 && ipnum < 4) ? ipnum : 0; + } + irq_index = irq / 32; + irq_mask = BIT(irq & 0x1f); + + cpu = s->coremap.reg_u8[irq]; + if (!!(s->coreisr.reg_u32[cpu][irq_index] & irq_mask)) + set_bit(irq, s->sw_coreisr[cpu][ipnum]); + else + clear_bit(irq, s->sw_coreisr[cpu][ipnum]); + } +} + +static void eiointc_update_irq(struct loongarch_eiointc *s, int irq, int level) +{ + int ipnum, cpu, found, irq_index, irq_mask; + struct kvm_vcpu *vcpu; + struct kvm_interrupt vcpu_irq; + + ipnum = s->ipmap.reg_u8[irq / 32]; + if (!(s->status & BIT(EIOINTC_ENABLE_INT_ENCODE))) { + ipnum = count_trailing_zeros(ipnum); + ipnum = (ipnum >= 0 && ipnum < 4) ? ipnum : 0; + } + + cpu = s->sw_coremap[irq]; + vcpu = kvm_get_vcpu(s->kvm, cpu); + irq_index = irq / 32; + irq_mask = BIT(irq & 0x1f); + + if (level) { + /* if not enable return false */ + if (((s->enable.reg_u32[irq_index]) & irq_mask) == 0) + return; + s->coreisr.reg_u32[cpu][irq_index] |= irq_mask; + found = find_first_bit(s->sw_coreisr[cpu][ipnum], EIOINTC_IRQS); + set_bit(irq, s->sw_coreisr[cpu][ipnum]); + } else { + s->coreisr.reg_u32[cpu][irq_index] &= ~irq_mask; + clear_bit(irq, s->sw_coreisr[cpu][ipnum]); + found = find_first_bit(s->sw_coreisr[cpu][ipnum], EIOINTC_IRQS); + } + + if (found < EIOINTC_IRQS) + return; /* other irq is handling, needn't update parent irq */ + + vcpu_irq.irq = level ? (INT_HWI0 + ipnum) : -(INT_HWI0 + ipnum); + kvm_vcpu_ioctl_interrupt(vcpu, &vcpu_irq); +} + +static inline void eiointc_update_sw_coremap(struct loongarch_eiointc *s, + int irq, void *pvalue, u32 len, bool notify) +{ + int i, cpu; + u64 val = *(u64 *)pvalue; + + for (i = 0; i < len; i++) { + cpu = val & 0xff; + val = val >> 8; + + if (!(s->status & BIT(EIOINTC_ENABLE_CPU_ENCODE))) { + cpu = ffs(cpu) - 1; + cpu = (cpu >= 4) ? 0 : cpu; + } + + if (s->sw_coremap[irq + i] == cpu) + continue; + + if (notify && test_bit(irq + i, (unsigned long *)s->isr.reg_u8)) { + /* lower irq at old cpu and raise irq at new cpu */ + eiointc_update_irq(s, irq + i, 0); + s->sw_coremap[irq + i] = cpu; + eiointc_update_irq(s, irq + i, 1); + } else { + s->sw_coremap[irq + i] = cpu; + } + } +} + +void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level) +{ + unsigned long flags; + unsigned long *isr = (unsigned long *)s->isr.reg_u8; + + level ? set_bit(irq, isr) : clear_bit(irq, isr); + spin_lock_irqsave(&s->lock, flags); + eiointc_update_irq(s, irq, level); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void eiointc_enable_irq(struct kvm_vcpu *vcpu, + struct loongarch_eiointc *s, int index, u8 mask, int level) +{ + u8 val; + int irq; + + val = mask & s->isr.reg_u8[index]; + irq = ffs(val); + while (irq != 0) { + /* + * enable bit change from 0 to 1, + * need to update irq by pending bits + */ + eiointc_update_irq(s, irq - 1 + index * 8, level); + val &= ~BIT(irq - 1); + irq = ffs(val); + } +} + +static int loongarch_eiointc_readb(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, + gpa_t addr, int len, void *val) +{ + int index, ret = 0; + u8 data = 0; + gpa_t offset; + + offset = addr - EIOINTC_BASE; + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = offset - EIOINTC_NODETYPE_START; + data = s->nodetype.reg_u8[index]; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + index = offset - EIOINTC_IPMAP_START; + data = s->ipmap.reg_u8[index]; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = offset - EIOINTC_ENABLE_START; + data = s->enable.reg_u8[index]; + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + index = offset - EIOINTC_BOUNCE_START; + data = s->bounce.reg_u8[index]; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = offset - EIOINTC_COREISR_START; + data = s->coreisr.reg_u8[vcpu->vcpu_id][index]; + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + index = offset - EIOINTC_COREMAP_START; + data = s->coremap.reg_u8[index]; + break; + default: + ret = -EINVAL; + break; + } + *(u8 *)val = data; + + return ret; +} + +static int loongarch_eiointc_readw(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, + gpa_t addr, int len, void *val) +{ + int index, ret = 0; + u16 data = 0; + gpa_t offset; + + offset = addr - EIOINTC_BASE; + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START) >> 1; + data = s->nodetype.reg_u16[index]; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + index = (offset - EIOINTC_IPMAP_START) >> 1; + data = s->ipmap.reg_u16[index]; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START) >> 1; + data = s->enable.reg_u16[index]; + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + index = (offset - EIOINTC_BOUNCE_START) >> 1; + data = s->bounce.reg_u16[index]; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START) >> 1; + data = s->coreisr.reg_u16[vcpu->vcpu_id][index]; + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + index = (offset - EIOINTC_COREMAP_START) >> 1; + data = s->coremap.reg_u16[index]; + break; + default: + ret = -EINVAL; + break; + } + *(u16 *)val = data; + + return ret; +} + +static int loongarch_eiointc_readl(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, + gpa_t addr, int len, void *val) +{ + int index, ret = 0; + u32 data = 0; + gpa_t offset; + + offset = addr - EIOINTC_BASE; + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START) >> 2; + data = s->nodetype.reg_u32[index]; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + index = (offset - EIOINTC_IPMAP_START) >> 2; + data = s->ipmap.reg_u32[index]; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START) >> 2; + data = s->enable.reg_u32[index]; + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + index = (offset - EIOINTC_BOUNCE_START) >> 2; + data = s->bounce.reg_u32[index]; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START) >> 2; + data = s->coreisr.reg_u32[vcpu->vcpu_id][index]; + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + index = (offset - EIOINTC_COREMAP_START) >> 2; + data = s->coremap.reg_u32[index]; + break; + default: + ret = -EINVAL; + break; + } + *(u32 *)val = data; + + return ret; +} + +static int loongarch_eiointc_readq(struct kvm_vcpu *vcpu, struct loongarch_eiointc *s, + gpa_t addr, int len, void *val) +{ + int index, ret = 0; + u64 data = 0; + gpa_t offset; + + offset = addr - EIOINTC_BASE; + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START) >> 3; + data = s->nodetype.reg_u64[index]; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + index = (offset - EIOINTC_IPMAP_START) >> 3; + data = s->ipmap.reg_u64; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START) >> 3; + data = s->enable.reg_u64[index]; + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + index = (offset - EIOINTC_BOUNCE_START) >> 3; + data = s->bounce.reg_u64[index]; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START) >> 3; + data = s->coreisr.reg_u64[vcpu->vcpu_id][index]; + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + index = (offset - EIOINTC_COREMAP_START) >> 3; + data = s->coremap.reg_u64[index]; + break; + default: + ret = -EINVAL; + break; + } + *(u64 *)val = data; + + return ret; +} + +static int kvm_eiointc_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, void *val) +{ + int ret = -EINVAL; + unsigned long flags; + struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc; + + if (!eiointc) { + kvm_err("%s: eiointc irqchip not valid!\n", __func__); + return -EINVAL; + } + + vcpu->kvm->stat.eiointc_read_exits++; + spin_lock_irqsave(&eiointc->lock, flags); + switch (len) { + case 1: + ret = loongarch_eiointc_readb(vcpu, eiointc, addr, len, val); + break; + case 2: + ret = loongarch_eiointc_readw(vcpu, eiointc, addr, len, val); + break; + case 4: + ret = loongarch_eiointc_readl(vcpu, eiointc, addr, len, val); + break; + case 8: + ret = loongarch_eiointc_readq(vcpu, eiointc, addr, len, val); + break; + default: + WARN_ONCE(1, "%s: Abnormal address access: addr 0x%llx, size %d\n", + __func__, addr, len); + } + spin_unlock_irqrestore(&eiointc->lock, flags); + + return ret; +} + +static int loongarch_eiointc_writeb(struct kvm_vcpu *vcpu, + struct loongarch_eiointc *s, + gpa_t addr, int len, const void *val) +{ + int index, irq, bits, ret = 0; + u8 cpu; + u8 data, old_data; + u8 coreisr, old_coreisr; + gpa_t offset; + + data = *(u8 *)val; + offset = addr - EIOINTC_BASE; + + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START); + s->nodetype.reg_u8[index] = data; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + /* + * ipmap cannot be set at runtime, can be set only at the beginning + * of irqchip driver, need not update upper irq level + */ + index = (offset - EIOINTC_IPMAP_START); + s->ipmap.reg_u8[index] = data; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START); + old_data = s->enable.reg_u8[index]; + s->enable.reg_u8[index] = data; + /* + * 1: enable irq. + * update irq when isr is set. + */ + data = s->enable.reg_u8[index] & ~old_data & s->isr.reg_u8[index]; + eiointc_enable_irq(vcpu, s, index, data, 1); + /* + * 0: disable irq. + * update irq when isr is set. + */ + data = ~s->enable.reg_u8[index] & old_data & s->isr.reg_u8[index]; + eiointc_enable_irq(vcpu, s, index, data, 0); + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + /* do not emulate hw bounced irq routing */ + index = offset - EIOINTC_BOUNCE_START; + s->bounce.reg_u8[index] = data; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START); + /* use attrs to get current cpu index */ + cpu = vcpu->vcpu_id; + coreisr = data; + old_coreisr = s->coreisr.reg_u8[cpu][index]; + /* write 1 to clear interrupt */ + s->coreisr.reg_u8[cpu][index] = old_coreisr & ~coreisr; + coreisr &= old_coreisr; + bits = sizeof(data) * 8; + irq = find_first_bit((void *)&coreisr, bits); + while (irq < bits) { + eiointc_update_irq(s, irq + index * bits, 0); + bitmap_clear((void *)&coreisr, irq, 1); + irq = find_first_bit((void *)&coreisr, bits); + } + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + irq = offset - EIOINTC_COREMAP_START; + index = irq; + s->coremap.reg_u8[index] = data; + eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int loongarch_eiointc_writew(struct kvm_vcpu *vcpu, + struct loongarch_eiointc *s, + gpa_t addr, int len, const void *val) +{ + int i, index, irq, bits, ret = 0; + u8 cpu; + u16 data, old_data; + u16 coreisr, old_coreisr; + gpa_t offset; + + data = *(u16 *)val; + offset = addr - EIOINTC_BASE; + + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START) >> 1; + s->nodetype.reg_u16[index] = data; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + /* + * ipmap cannot be set at runtime, can be set only at the beginning + * of irqchip driver, need not update upper irq level + */ + index = (offset - EIOINTC_IPMAP_START) >> 1; + s->ipmap.reg_u16[index] = data; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START) >> 1; + old_data = s->enable.reg_u32[index]; + s->enable.reg_u16[index] = data; + /* + * 1: enable irq. + * update irq when isr is set. + */ + data = s->enable.reg_u16[index] & ~old_data & s->isr.reg_u16[index]; + index = index << 1; + for (i = 0; i < sizeof(data); i++) { + u8 mask = (data >> (i * 8)) & 0xff; + eiointc_enable_irq(vcpu, s, index + i, mask, 1); + } + /* + * 0: disable irq. + * update irq when isr is set. + */ + data = ~s->enable.reg_u16[index] & old_data & s->isr.reg_u16[index]; + for (i = 0; i < sizeof(data); i++) { + u8 mask = (data >> (i * 8)) & 0xff; + eiointc_enable_irq(vcpu, s, index, mask, 0); + } + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + /* do not emulate hw bounced irq routing */ + index = (offset - EIOINTC_BOUNCE_START) >> 1; + s->bounce.reg_u16[index] = data; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START) >> 1; + /* use attrs to get current cpu index */ + cpu = vcpu->vcpu_id; + coreisr = data; + old_coreisr = s->coreisr.reg_u16[cpu][index]; + /* write 1 to clear interrupt */ + s->coreisr.reg_u16[cpu][index] = old_coreisr & ~coreisr; + coreisr &= old_coreisr; + bits = sizeof(data) * 8; + irq = find_first_bit((void *)&coreisr, bits); + while (irq < bits) { + eiointc_update_irq(s, irq + index * bits, 0); + bitmap_clear((void *)&coreisr, irq, 1); + irq = find_first_bit((void *)&coreisr, bits); + } + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + irq = offset - EIOINTC_COREMAP_START; + index = irq >> 1; + s->coremap.reg_u16[index] = data; + eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int loongarch_eiointc_writel(struct kvm_vcpu *vcpu, + struct loongarch_eiointc *s, + gpa_t addr, int len, const void *val) +{ + int i, index, irq, bits, ret = 0; + u8 cpu; + u32 data, old_data; + u32 coreisr, old_coreisr; + gpa_t offset; + + data = *(u32 *)val; + offset = addr - EIOINTC_BASE; + + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START) >> 2; + s->nodetype.reg_u32[index] = data; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + /* + * ipmap cannot be set at runtime, can be set only at the beginning + * of irqchip driver, need not update upper irq level + */ + index = (offset - EIOINTC_IPMAP_START) >> 2; + s->ipmap.reg_u32[index] = data; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START) >> 2; + old_data = s->enable.reg_u32[index]; + s->enable.reg_u32[index] = data; + /* + * 1: enable irq. + * update irq when isr is set. + */ + data = s->enable.reg_u32[index] & ~old_data & s->isr.reg_u32[index]; + index = index << 2; + for (i = 0; i < sizeof(data); i++) { + u8 mask = (data >> (i * 8)) & 0xff; + eiointc_enable_irq(vcpu, s, index + i, mask, 1); + } + /* + * 0: disable irq. + * update irq when isr is set. + */ + data = ~s->enable.reg_u32[index] & old_data & s->isr.reg_u32[index]; + for (i = 0; i < sizeof(data); i++) { + u8 mask = (data >> (i * 8)) & 0xff; + eiointc_enable_irq(vcpu, s, index, mask, 0); + } + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + /* do not emulate hw bounced irq routing */ + index = (offset - EIOINTC_BOUNCE_START) >> 2; + s->bounce.reg_u32[index] = data; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START) >> 2; + /* use attrs to get current cpu index */ + cpu = vcpu->vcpu_id; + coreisr = data; + old_coreisr = s->coreisr.reg_u32[cpu][index]; + /* write 1 to clear interrupt */ + s->coreisr.reg_u32[cpu][index] = old_coreisr & ~coreisr; + coreisr &= old_coreisr; + bits = sizeof(data) * 8; + irq = find_first_bit((void *)&coreisr, bits); + while (irq < bits) { + eiointc_update_irq(s, irq + index * bits, 0); + bitmap_clear((void *)&coreisr, irq, 1); + irq = find_first_bit((void *)&coreisr, bits); + } + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + irq = offset - EIOINTC_COREMAP_START; + index = irq >> 2; + s->coremap.reg_u32[index] = data; + eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int loongarch_eiointc_writeq(struct kvm_vcpu *vcpu, + struct loongarch_eiointc *s, + gpa_t addr, int len, const void *val) +{ + int i, index, irq, bits, ret = 0; + u8 cpu; + u64 data, old_data; + u64 coreisr, old_coreisr; + gpa_t offset; + + data = *(u64 *)val; + offset = addr - EIOINTC_BASE; + + switch (offset) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + index = (offset - EIOINTC_NODETYPE_START) >> 3; + s->nodetype.reg_u64[index] = data; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + /* + * ipmap cannot be set at runtime, can be set only at the beginning + * of irqchip driver, need not update upper irq level + */ + index = (offset - EIOINTC_IPMAP_START) >> 3; + s->ipmap.reg_u64 = data; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + index = (offset - EIOINTC_ENABLE_START) >> 3; + old_data = s->enable.reg_u64[index]; + s->enable.reg_u64[index] = data; + /* + * 1: enable irq. + * update irq when isr is set. + */ + data = s->enable.reg_u64[index] & ~old_data & s->isr.reg_u64[index]; + index = index << 3; + for (i = 0; i < sizeof(data); i++) { + u8 mask = (data >> (i * 8)) & 0xff; + eiointc_enable_irq(vcpu, s, index + i, mask, 1); + } + /* + * 0: disable irq. + * update irq when isr is set. + */ + data = ~s->enable.reg_u64[index] & old_data & s->isr.reg_u64[index]; + for (i = 0; i < sizeof(data); i++) { + u8 mask = (data >> (i * 8)) & 0xff; + eiointc_enable_irq(vcpu, s, index, mask, 0); + } + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + /* do not emulate hw bounced irq routing */ + index = (offset - EIOINTC_BOUNCE_START) >> 3; + s->bounce.reg_u64[index] = data; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + index = (offset - EIOINTC_COREISR_START) >> 3; + /* use attrs to get current cpu index */ + cpu = vcpu->vcpu_id; + coreisr = data; + old_coreisr = s->coreisr.reg_u64[cpu][index]; + /* write 1 to clear interrupt */ + s->coreisr.reg_u64[cpu][index] = old_coreisr & ~coreisr; + coreisr &= old_coreisr; + bits = sizeof(data) * 8; + irq = find_first_bit((void *)&coreisr, bits); + while (irq < bits) { + eiointc_update_irq(s, irq + index * bits, 0); + bitmap_clear((void *)&coreisr, irq, 1); + irq = find_first_bit((void *)&coreisr, bits); + } + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + irq = offset - EIOINTC_COREMAP_START; + index = irq >> 3; + s->coremap.reg_u64[index] = data; + eiointc_update_sw_coremap(s, irq, (void *)&data, sizeof(data), true); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int kvm_eiointc_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, const void *val) +{ + int ret = -EINVAL; + unsigned long flags; + struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc; + + if (!eiointc) { + kvm_err("%s: eiointc irqchip not valid!\n", __func__); + return -EINVAL; + } + + vcpu->kvm->stat.eiointc_write_exits++; + spin_lock_irqsave(&eiointc->lock, flags); + switch (len) { + case 1: + ret = loongarch_eiointc_writeb(vcpu, eiointc, addr, len, val); + break; + case 2: + ret = loongarch_eiointc_writew(vcpu, eiointc, addr, len, val); + break; + case 4: + ret = loongarch_eiointc_writel(vcpu, eiointc, addr, len, val); + break; + case 8: + ret = loongarch_eiointc_writeq(vcpu, eiointc, addr, len, val); + break; + default: + WARN_ONCE(1, "%s: Abnormal address access: addr 0x%llx, size %d\n", + __func__, addr, len); + } + spin_unlock_irqrestore(&eiointc->lock, flags); + + return ret; +} + +static const struct kvm_io_device_ops kvm_eiointc_ops = { + .read = kvm_eiointc_read, + .write = kvm_eiointc_write, +}; + +static int kvm_eiointc_virt_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, void *val) +{ + unsigned long flags; + u32 *data = val; + struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc; + + if (!eiointc) { + kvm_err("%s: eiointc irqchip not valid!\n", __func__); + return -EINVAL; + } + + addr -= EIOINTC_VIRT_BASE; + spin_lock_irqsave(&eiointc->lock, flags); + switch (addr) { + case EIOINTC_VIRT_FEATURES: + *data = eiointc->features; + break; + case EIOINTC_VIRT_CONFIG: + *data = eiointc->status; + break; + default: + break; + } + spin_unlock_irqrestore(&eiointc->lock, flags); + + return 0; +} + +static int kvm_eiointc_virt_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, const void *val) +{ + int ret = 0; + unsigned long flags; + u32 value = *(u32 *)val; + struct loongarch_eiointc *eiointc = vcpu->kvm->arch.eiointc; + + if (!eiointc) { + kvm_err("%s: eiointc irqchip not valid!\n", __func__); + return -EINVAL; + } + + addr -= EIOINTC_VIRT_BASE; + spin_lock_irqsave(&eiointc->lock, flags); + switch (addr) { + case EIOINTC_VIRT_FEATURES: + ret = -EPERM; + break; + case EIOINTC_VIRT_CONFIG: + /* + * eiointc features can only be set at disabled status + */ + if ((eiointc->status & BIT(EIOINTC_ENABLE)) && value) { + ret = -EPERM; + break; + } + eiointc->status = value & eiointc->features; + break; + default: + break; + } + spin_unlock_irqrestore(&eiointc->lock, flags); + + return ret; +} + +static const struct kvm_io_device_ops kvm_eiointc_virt_ops = { + .read = kvm_eiointc_virt_read, + .write = kvm_eiointc_virt_write, +}; + +static int kvm_eiointc_ctrl_access(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + int ret = 0; + unsigned long flags; + unsigned long type = (unsigned long)attr->attr; + u32 i, start_irq; + void __user *data; + struct loongarch_eiointc *s = dev->kvm->arch.eiointc; + + data = (void __user *)attr->addr; + spin_lock_irqsave(&s->lock, flags); + switch (type) { + case KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU: + if (copy_from_user(&s->num_cpu, data, 4)) + ret = -EFAULT; + break; + case KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE: + if (copy_from_user(&s->features, data, 4)) + ret = -EFAULT; + if (!(s->features & BIT(EIOINTC_HAS_VIRT_EXTENSION))) + s->status |= BIT(EIOINTC_ENABLE); + break; + case KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED: + eiointc_set_sw_coreisr(s); + for (i = 0; i < (EIOINTC_IRQS / 4); i++) { + start_irq = i * 4; + eiointc_update_sw_coremap(s, start_irq, + (void *)&s->coremap.reg_u32[i], sizeof(u32), false); + } + break; + default: + break; + } + spin_unlock_irqrestore(&s->lock, flags); + + return ret; +} + +static int kvm_eiointc_regs_access(struct kvm_device *dev, + struct kvm_device_attr *attr, + bool is_write) +{ + int addr, cpuid, offset, ret = 0; + unsigned long flags; + void *p = NULL; + void __user *data; + struct loongarch_eiointc *s; + + s = dev->kvm->arch.eiointc; + addr = attr->attr; + cpuid = addr >> 16; + addr &= 0xffff; + data = (void __user *)attr->addr; + switch (addr) { + case EIOINTC_NODETYPE_START ... EIOINTC_NODETYPE_END: + offset = (addr - EIOINTC_NODETYPE_START) / 4; + p = &s->nodetype.reg_u32[offset]; + break; + case EIOINTC_IPMAP_START ... EIOINTC_IPMAP_END: + offset = (addr - EIOINTC_IPMAP_START) / 4; + p = &s->ipmap.reg_u32[offset]; + break; + case EIOINTC_ENABLE_START ... EIOINTC_ENABLE_END: + offset = (addr - EIOINTC_ENABLE_START) / 4; + p = &s->enable.reg_u32[offset]; + break; + case EIOINTC_BOUNCE_START ... EIOINTC_BOUNCE_END: + offset = (addr - EIOINTC_BOUNCE_START) / 4; + p = &s->bounce.reg_u32[offset]; + break; + case EIOINTC_ISR_START ... EIOINTC_ISR_END: + offset = (addr - EIOINTC_ISR_START) / 4; + p = &s->isr.reg_u32[offset]; + break; + case EIOINTC_COREISR_START ... EIOINTC_COREISR_END: + offset = (addr - EIOINTC_COREISR_START) / 4; + p = &s->coreisr.reg_u32[cpuid][offset]; + break; + case EIOINTC_COREMAP_START ... EIOINTC_COREMAP_END: + offset = (addr - EIOINTC_COREMAP_START) / 4; + p = &s->coremap.reg_u32[offset]; + break; + default: + kvm_err("%s: unknown eiointc register, addr = %d\n", __func__, addr); + return -EINVAL; + } + + spin_lock_irqsave(&s->lock, flags); + if (is_write) { + if (copy_from_user(p, data, 4)) + ret = -EFAULT; + } else { + if (copy_to_user(data, p, 4)) + ret = -EFAULT; + } + spin_unlock_irqrestore(&s->lock, flags); + + return ret; +} + +static int kvm_eiointc_sw_status_access(struct kvm_device *dev, + struct kvm_device_attr *attr, + bool is_write) +{ + int addr, ret = 0; + unsigned long flags; + void *p = NULL; + void __user *data; + struct loongarch_eiointc *s; + + s = dev->kvm->arch.eiointc; + addr = attr->attr; + addr &= 0xffff; + + data = (void __user *)attr->addr; + switch (addr) { + case KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU: + p = &s->num_cpu; + break; + case KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE: + p = &s->features; + break; + case KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE: + p = &s->status; + break; + default: + kvm_err("%s: unknown eiointc register, addr = %d\n", __func__, addr); + return -EINVAL; + } + spin_lock_irqsave(&s->lock, flags); + if (is_write) { + if (copy_from_user(p, data, 4)) + ret = -EFAULT; + } else { + if (copy_to_user(data, p, 4)) + ret = -EFAULT; + } + spin_unlock_irqrestore(&s->lock, flags); + + return ret; +} + +static int kvm_eiointc_get_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS: + return kvm_eiointc_regs_access(dev, attr, false); + case KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS: + return kvm_eiointc_sw_status_access(dev, attr, false); + default: + return -EINVAL; + } +} + +static int kvm_eiointc_set_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL: + return kvm_eiointc_ctrl_access(dev, attr); + case KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS: + return kvm_eiointc_regs_access(dev, attr, true); + case KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS: + return kvm_eiointc_sw_status_access(dev, attr, true); + default: + return -EINVAL; + } +} + +static int kvm_eiointc_create(struct kvm_device *dev, u32 type) +{ + int ret; + struct loongarch_eiointc *s; + struct kvm_io_device *device, *device1; + struct kvm *kvm = dev->kvm; + + /* eiointc has been created */ + if (kvm->arch.eiointc) + return -EINVAL; + + s = kzalloc(sizeof(struct loongarch_eiointc), GFP_KERNEL); + if (!s) + return -ENOMEM; + + spin_lock_init(&s->lock); + s->kvm = kvm; + + /* + * Initialize IOCSR device + */ + device = &s->device; + kvm_iodevice_init(device, &kvm_eiointc_ops); + mutex_lock(&kvm->slots_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, + EIOINTC_BASE, EIOINTC_SIZE, device); + mutex_unlock(&kvm->slots_lock); + if (ret < 0) { + kfree(s); + return ret; + } + + device1 = &s->device_vext; + kvm_iodevice_init(device1, &kvm_eiointc_virt_ops); + ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, + EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device1); + if (ret < 0) { + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); + kfree(s); + return ret; + } + kvm->arch.eiointc = s; + + return 0; +} + +static void kvm_eiointc_destroy(struct kvm_device *dev) +{ + struct kvm *kvm; + struct loongarch_eiointc *eiointc; + + if (!dev || !dev->kvm || !dev->kvm->arch.eiointc) + return; + + kvm = dev->kvm; + eiointc = kvm->arch.eiointc; + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device); + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext); + kfree(eiointc); +} + +static struct kvm_device_ops kvm_eiointc_dev_ops = { + .name = "kvm-loongarch-eiointc", + .create = kvm_eiointc_create, + .destroy = kvm_eiointc_destroy, + .set_attr = kvm_eiointc_set_attr, + .get_attr = kvm_eiointc_get_attr, +}; + +int kvm_loongarch_register_eiointc_device(void) +{ + return kvm_register_device_ops(&kvm_eiointc_dev_ops, KVM_DEV_TYPE_LOONGARCH_EIOINTC); +} diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c new file mode 100644 index 0000000000000..a233a323e2957 --- /dev/null +++ b/arch/loongarch/kvm/intc/ipi.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#include +#include +#include + +static void ipi_send(struct kvm *kvm, uint64_t data) +{ + int cpu, action; + uint32_t status; + struct kvm_vcpu *vcpu; + struct kvm_interrupt irq; + + cpu = ((data & 0xffffffff) >> 16) & 0x3ff; + vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu); + if (unlikely(vcpu == NULL)) { + kvm_err("%s: invalid target cpu: %d\n", __func__, cpu); + return; + } + + action = BIT(data & 0x1f); + spin_lock(&vcpu->arch.ipi_state.lock); + status = vcpu->arch.ipi_state.status; + vcpu->arch.ipi_state.status |= action; + spin_unlock(&vcpu->arch.ipi_state.lock); + if (status == 0) { + irq.irq = LARCH_INT_IPI; + kvm_vcpu_ioctl_interrupt(vcpu, &irq); + } +} + +static void ipi_clear(struct kvm_vcpu *vcpu, uint64_t data) +{ + uint32_t status; + struct kvm_interrupt irq; + + spin_lock(&vcpu->arch.ipi_state.lock); + vcpu->arch.ipi_state.status &= ~data; + status = vcpu->arch.ipi_state.status; + spin_unlock(&vcpu->arch.ipi_state.lock); + if (status == 0) { + irq.irq = -LARCH_INT_IPI; + kvm_vcpu_ioctl_interrupt(vcpu, &irq); + } +} + +static uint64_t read_mailbox(struct kvm_vcpu *vcpu, int offset, int len) +{ + uint64_t data = 0; + + spin_lock(&vcpu->arch.ipi_state.lock); + data = *(ulong *)((void *)vcpu->arch.ipi_state.buf + (offset - 0x20)); + spin_unlock(&vcpu->arch.ipi_state.lock); + + switch (len) { + case 1: + return data & 0xff; + case 2: + return data & 0xffff; + case 4: + return data & 0xffffffff; + case 8: + return data; + default: + kvm_err("%s: unknown data len: %d\n", __func__, len); + return 0; + } +} + +static void write_mailbox(struct kvm_vcpu *vcpu, int offset, uint64_t data, int len) +{ + void *pbuf; + + spin_lock(&vcpu->arch.ipi_state.lock); + pbuf = (void *)vcpu->arch.ipi_state.buf + (offset - 0x20); + + switch (len) { + case 1: + *(unsigned char *)pbuf = (unsigned char)data; + break; + case 2: + *(unsigned short *)pbuf = (unsigned short)data; + break; + case 4: + *(unsigned int *)pbuf = (unsigned int)data; + break; + case 8: + *(unsigned long *)pbuf = (unsigned long)data; + break; + default: + kvm_err("%s: unknown data len: %d\n", __func__, len); + } + spin_unlock(&vcpu->arch.ipi_state.lock); +} + +static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data) +{ + int i, ret; + uint32_t val = 0, mask = 0; + + /* + * Bit 27-30 is mask for byte writing. + * If the mask is 0, we need not to do anything. + */ + if ((data >> 27) & 0xf) { + /* Read the old val */ + ret = kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val); + if (unlikely(ret)) { + kvm_err("%s: : read date from addr %llx failed\n", __func__, addr); + return ret; + } + /* Construct the mask by scanning the bit 27-30 */ + for (i = 0; i < 4; i++) { + if (data & (BIT(27 + i))) + mask |= (0xff << (i * 8)); + } + /* Save the old part of val */ + val &= mask; + } + val |= ((uint32_t)(data >> 32) & ~mask); + ret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val); + if (unlikely(ret)) + kvm_err("%s: : write date to addr %llx failed\n", __func__, addr); + + return ret; +} + +static int mail_send(struct kvm *kvm, uint64_t data) +{ + int cpu, mailbox, offset; + struct kvm_vcpu *vcpu; + + cpu = ((data & 0xffffffff) >> 16) & 0x3ff; + vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu); + if (unlikely(vcpu == NULL)) { + kvm_err("%s: invalid target cpu: %d\n", __func__, cpu); + return -EINVAL; + } + mailbox = ((data & 0xffffffff) >> 2) & 0x7; + offset = IOCSR_IPI_BASE + IOCSR_IPI_BUF_20 + mailbox * 4; + + return send_ipi_data(vcpu, offset, data); +} + +static int any_send(struct kvm *kvm, uint64_t data) +{ + int cpu, offset; + struct kvm_vcpu *vcpu; + + cpu = ((data & 0xffffffff) >> 16) & 0x3ff; + vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu); + if (unlikely(vcpu == NULL)) { + kvm_err("%s: invalid target cpu: %d\n", __func__, cpu); + return -EINVAL; + } + offset = data & 0xffff; + + return send_ipi_data(vcpu, offset, data); +} + +static int loongarch_ipi_readl(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *val) +{ + int ret = 0; + uint32_t offset; + uint64_t res = 0; + + offset = (uint32_t)(addr & 0x1ff); + WARN_ON_ONCE(offset & (len - 1)); + + switch (offset) { + case IOCSR_IPI_STATUS: + spin_lock(&vcpu->arch.ipi_state.lock); + res = vcpu->arch.ipi_state.status; + spin_unlock(&vcpu->arch.ipi_state.lock); + break; + case IOCSR_IPI_EN: + spin_lock(&vcpu->arch.ipi_state.lock); + res = vcpu->arch.ipi_state.en; + spin_unlock(&vcpu->arch.ipi_state.lock); + break; + case IOCSR_IPI_SET: + res = 0; + break; + case IOCSR_IPI_CLEAR: + res = 0; + break; + case IOCSR_IPI_BUF_20 ... IOCSR_IPI_BUF_38 + 7: + if (offset + len > IOCSR_IPI_BUF_38 + 8) { + kvm_err("%s: invalid offset or len: offset = %d, len = %d\n", + __func__, offset, len); + ret = -EINVAL; + break; + } + res = read_mailbox(vcpu, offset, len); + break; + default: + kvm_err("%s: unknown addr: %llx\n", __func__, addr); + ret = -EINVAL; + break; + } + *(uint64_t *)val = res; + + return ret; +} + +static int loongarch_ipi_writel(struct kvm_vcpu *vcpu, gpa_t addr, int len, const void *val) +{ + int ret = 0; + uint64_t data; + uint32_t offset; + + data = *(uint64_t *)val; + + offset = (uint32_t)(addr & 0x1ff); + WARN_ON_ONCE(offset & (len - 1)); + + switch (offset) { + case IOCSR_IPI_STATUS: + ret = -EINVAL; + break; + case IOCSR_IPI_EN: + spin_lock(&vcpu->arch.ipi_state.lock); + vcpu->arch.ipi_state.en = data; + spin_unlock(&vcpu->arch.ipi_state.lock); + break; + case IOCSR_IPI_SET: + ret = -EINVAL; + break; + case IOCSR_IPI_CLEAR: + /* Just clear the status of the current vcpu */ + ipi_clear(vcpu, data); + break; + case IOCSR_IPI_BUF_20 ... IOCSR_IPI_BUF_38 + 7: + if (offset + len > IOCSR_IPI_BUF_38 + 8) { + kvm_err("%s: invalid offset or len: offset = %d, len = %d\n", + __func__, offset, len); + ret = -EINVAL; + break; + } + write_mailbox(vcpu, offset, data, len); + break; + case IOCSR_IPI_SEND: + ipi_send(vcpu->kvm, data); + break; + case IOCSR_MAIL_SEND: + ret = mail_send(vcpu->kvm, *(uint64_t *)val); + break; + case IOCSR_ANY_SEND: + ret = any_send(vcpu->kvm, *(uint64_t *)val); + break; + default: + kvm_err("%s: unknown addr: %llx\n", __func__, addr); + ret = -EINVAL; + break; + } + + return ret; +} + +static int kvm_ipi_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, void *val) +{ + int ret; + struct loongarch_ipi *ipi; + + ipi = vcpu->kvm->arch.ipi; + if (!ipi) { + kvm_err("%s: ipi irqchip not valid!\n", __func__); + return -EINVAL; + } + ipi->kvm->stat.ipi_read_exits++; + ret = loongarch_ipi_readl(vcpu, addr, len, val); + + return ret; +} + +static int kvm_ipi_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, const void *val) +{ + int ret; + struct loongarch_ipi *ipi; + + ipi = vcpu->kvm->arch.ipi; + if (!ipi) { + kvm_err("%s: ipi irqchip not valid!\n", __func__); + return -EINVAL; + } + ipi->kvm->stat.ipi_write_exits++; + ret = loongarch_ipi_writel(vcpu, addr, len, val); + + return ret; +} + +static const struct kvm_io_device_ops kvm_ipi_ops = { + .read = kvm_ipi_read, + .write = kvm_ipi_write, +}; + +static int kvm_ipi_regs_access(struct kvm_device *dev, + struct kvm_device_attr *attr, + bool is_write) +{ + int len = 4; + int cpu, addr; + uint64_t val; + void *p = NULL; + struct kvm_vcpu *vcpu; + + cpu = (attr->attr >> 16) & 0x3ff; + addr = attr->attr & 0xff; + + vcpu = kvm_get_vcpu(dev->kvm, cpu); + if (unlikely(vcpu == NULL)) { + kvm_err("%s: invalid target cpu: %d\n", __func__, cpu); + return -EINVAL; + } + + switch (addr) { + case IOCSR_IPI_STATUS: + p = &vcpu->arch.ipi_state.status; + break; + case IOCSR_IPI_EN: + p = &vcpu->arch.ipi_state.en; + break; + case IOCSR_IPI_SET: + p = &vcpu->arch.ipi_state.set; + break; + case IOCSR_IPI_CLEAR: + p = &vcpu->arch.ipi_state.clear; + break; + case IOCSR_IPI_BUF_20: + p = &vcpu->arch.ipi_state.buf[0]; + len = 8; + break; + case IOCSR_IPI_BUF_28: + p = &vcpu->arch.ipi_state.buf[1]; + len = 8; + break; + case IOCSR_IPI_BUF_30: + p = &vcpu->arch.ipi_state.buf[2]; + len = 8; + break; + case IOCSR_IPI_BUF_38: + p = &vcpu->arch.ipi_state.buf[3]; + len = 8; + break; + default: + kvm_err("%s: unknown ipi register, addr = %d\n", __func__, addr); + return -EINVAL; + } + + if (is_write) { + if (len == 4) { + if (get_user(val, (uint32_t __user *)attr->addr)) + return -EFAULT; + *(uint32_t *)p = (uint32_t)val; + } else if (len == 8) { + if (get_user(val, (uint64_t __user *)attr->addr)) + return -EFAULT; + *(uint64_t *)p = val; + } + } else { + if (len == 4) { + val = *(uint32_t *)p; + return put_user(val, (uint32_t __user *)attr->addr); + } else if (len == 8) { + val = *(uint64_t *)p; + return put_user(val, (uint64_t __user *)attr->addr); + } + } + + return 0; +} + +static int kvm_ipi_get_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_IPI_GRP_REGS: + return kvm_ipi_regs_access(dev, attr, false); + default: + kvm_err("%s: unknown group (%d)\n", __func__, attr->group); + return -EINVAL; + } +} + +static int kvm_ipi_set_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_IPI_GRP_REGS: + return kvm_ipi_regs_access(dev, attr, true); + default: + kvm_err("%s: unknown group (%d)\n", __func__, attr->group); + return -EINVAL; + } +} + +static int kvm_ipi_create(struct kvm_device *dev, u32 type) +{ + int ret; + struct kvm *kvm; + struct kvm_io_device *device; + struct loongarch_ipi *s; + + if (!dev) { + kvm_err("%s: kvm_device ptr is invalid!\n", __func__); + return -EINVAL; + } + + kvm = dev->kvm; + if (kvm->arch.ipi) { + kvm_err("%s: LoongArch IPI has already been created!\n", __func__); + return -EINVAL; + } + + s = kzalloc(sizeof(struct loongarch_ipi), GFP_KERNEL); + if (!s) + return -ENOMEM; + + spin_lock_init(&s->lock); + s->kvm = kvm; + + /* + * Initialize IOCSR device + */ + device = &s->device; + kvm_iodevice_init(device, &kvm_ipi_ops); + mutex_lock(&kvm->slots_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, IOCSR_IPI_BASE, IOCSR_IPI_SIZE, device); + mutex_unlock(&kvm->slots_lock); + if (ret < 0) { + kvm_err("%s: Initialize IOCSR dev failed, ret = %d\n", __func__, ret); + goto err; + } + + kvm->arch.ipi = s; + return 0; + +err: + kfree(s); + return -EFAULT; +} + +static void kvm_ipi_destroy(struct kvm_device *dev) +{ + struct kvm *kvm; + struct loongarch_ipi *ipi; + + if (!dev || !dev->kvm || !dev->kvm->arch.ipi) + return; + + kvm = dev->kvm; + ipi = kvm->arch.ipi; + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &ipi->device); + kfree(ipi); +} + +static struct kvm_device_ops kvm_ipi_dev_ops = { + .name = "kvm-loongarch-ipi", + .create = kvm_ipi_create, + .destroy = kvm_ipi_destroy, + .set_attr = kvm_ipi_set_attr, + .get_attr = kvm_ipi_get_attr, +}; + +int kvm_loongarch_register_ipi_device(void) +{ + return kvm_register_device_ops(&kvm_ipi_dev_ops, KVM_DEV_TYPE_LOONGARCH_IPI); +} diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c new file mode 100644 index 0000000000000..08fce845f6680 --- /dev/null +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include + +/* update the isr according to irq level and route irq to eiointc */ +static void pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level) +{ + u64 mask = BIT(irq); + + /* + * set isr and route irq to eiointc and + * the route table is in htmsi_vector[] + */ + if (level) { + if (mask & s->irr & ~s->mask) { + s->isr |= mask; + irq = s->htmsi_vector[irq]; + eiointc_set_irq(s->kvm->arch.eiointc, irq, level); + } + } else { + if (mask & s->isr & ~s->irr) { + s->isr &= ~mask; + irq = s->htmsi_vector[irq]; + eiointc_set_irq(s->kvm->arch.eiointc, irq, level); + } + } +} + +/* update batch irqs, the irq_mask is a bitmap of irqs */ +static void pch_pic_update_batch_irqs(struct loongarch_pch_pic *s, u64 irq_mask, int level) +{ + int irq, bits; + + /* find each irq by irqs bitmap and update each irq */ + bits = sizeof(irq_mask) * 8; + irq = find_first_bit((void *)&irq_mask, bits); + while (irq < bits) { + pch_pic_update_irq(s, irq, level); + bitmap_clear((void *)&irq_mask, irq, 1); + irq = find_first_bit((void *)&irq_mask, bits); + } +} + +/* called when a irq is triggered in pch pic */ +void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level) +{ + u64 mask = BIT(irq); + + spin_lock(&s->lock); + if (level) + s->irr |= mask; /* set irr */ + else { + /* + * In edge triggered mode, 0 does not mean to clear irq + * The irr register variable is cleared when cpu writes to the + * PCH_PIC_CLEAR_START address area + */ + if (s->edge & mask) { + spin_unlock(&s->lock); + return; + } + s->irr &= ~mask; + } + pch_pic_update_irq(s, irq, level); + spin_unlock(&s->lock); +} + +/* msi irq handler */ +void pch_msi_set_irq(struct kvm *kvm, int irq, int level) +{ + eiointc_set_irq(kvm->arch.eiointc, irq, level); +} + +/* + * pch pic register is 64-bit, but it is accessed by 32-bit, + * so we use high to get whether low or high 32 bits we want + * to read. + */ +static u32 pch_pic_read_reg(u64 *s, int high) +{ + u64 val = *s; + + /* read the high 32 bits when high is 1 */ + return high ? (u32)(val >> 32) : (u32)val; +} + +/* + * pch pic register is 64-bit, but it is accessed by 32-bit, + * so we use high to get whether low or high 32 bits we want + * to write. + */ +static u32 pch_pic_write_reg(u64 *s, int high, u32 v) +{ + u64 val = *s, data = v; + + if (high) { + /* + * Clear val high 32 bits + * Write the high 32 bits when the high is 1 + */ + *s = (val << 32 >> 32) | (data << 32); + val >>= 32; + } else + /* + * Clear val low 32 bits + * Write the low 32 bits when the high is 0 + */ + *s = (val >> 32 << 32) | v; + + return (u32)val; +} + +static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val) +{ + int offset, index, ret = 0; + u32 data = 0; + u64 int_id = 0; + + offset = addr - s->pch_pic_base; + + spin_lock(&s->lock); + switch (offset) { + case PCH_PIC_INT_ID_START ... PCH_PIC_INT_ID_END: + /* int id version */ + int_id |= (u64)PCH_PIC_INT_ID_VER << 32; + /* irq number */ + int_id |= (u64)31 << (32 + 16); + /* int id value */ + int_id |= PCH_PIC_INT_ID_VAL; + *(u64 *)val = int_id; + break; + case PCH_PIC_MASK_START ... PCH_PIC_MASK_END: + offset -= PCH_PIC_MASK_START; + index = offset >> 2; + /* read mask reg */ + data = pch_pic_read_reg(&s->mask, index); + *(u32 *)val = data; + break; + case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END: + offset -= PCH_PIC_HTMSI_EN_START; + index = offset >> 2; + /* read htmsi enable reg */ + data = pch_pic_read_reg(&s->htmsi_en, index); + *(u32 *)val = data; + break; + case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END: + offset -= PCH_PIC_EDGE_START; + index = offset >> 2; + /* read edge enable reg */ + data = pch_pic_read_reg(&s->edge, index); + *(u32 *)val = data; + break; + case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END: + case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END: + /* we only use default mode: fixed interrupt distribution mode */ + *(u32 *)val = 0; + break; + case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END: + /* only route to int0: eiointc */ + *(u8 *)val = 1; + break; + case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END: + offset -= PCH_PIC_HTMSI_VEC_START; + /* read htmsi vector */ + data = s->htmsi_vector[offset]; + *(u8 *)val = data; + break; + case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END: + /* we only use defalut value 0: high level triggered */ + *(u32 *)val = 0; + break; + default: + ret = -EINVAL; + } + spin_unlock(&s->lock); + + return ret; +} + +static int kvm_pch_pic_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, void *val) +{ + int ret; + struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic; + + if (!s) { + kvm_err("%s: pch pic irqchip not valid!\n", __func__); + return -EINVAL; + } + + /* statistics of pch pic reading */ + vcpu->kvm->stat.pch_pic_read_exits++; + ret = loongarch_pch_pic_read(s, addr, len, val); + + return ret; +} + +static int loongarch_pch_pic_write(struct loongarch_pch_pic *s, gpa_t addr, + int len, const void *val) +{ + int ret; + u32 old, data, offset, index; + u64 irq; + + ret = 0; + data = *(u32 *)val; + offset = addr - s->pch_pic_base; + + spin_lock(&s->lock); + switch (offset) { + case PCH_PIC_MASK_START ... PCH_PIC_MASK_END: + offset -= PCH_PIC_MASK_START; + /* get whether high or low 32 bits we want to write */ + index = offset >> 2; + old = pch_pic_write_reg(&s->mask, index, data); + /* enable irq when mask value change to 0 */ + irq = (old & ~data) << (32 * index); + pch_pic_update_batch_irqs(s, irq, 1); + /* disable irq when mask value change to 1 */ + irq = (~old & data) << (32 * index); + pch_pic_update_batch_irqs(s, irq, 0); + break; + case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END: + offset -= PCH_PIC_HTMSI_EN_START; + index = offset >> 2; + pch_pic_write_reg(&s->htmsi_en, index, data); + break; + case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END: + offset -= PCH_PIC_EDGE_START; + index = offset >> 2; + /* 1: edge triggered, 0: level triggered */ + pch_pic_write_reg(&s->edge, index, data); + break; + case PCH_PIC_CLEAR_START ... PCH_PIC_CLEAR_END: + offset -= PCH_PIC_CLEAR_START; + index = offset >> 2; + /* write 1 to clear edge irq */ + old = pch_pic_read_reg(&s->irr, index); + /* + * get the irq bitmap which is edge triggered and + * already set and to be cleared + */ + irq = old & pch_pic_read_reg(&s->edge, index) & data; + /* write irr to the new state where irqs have been cleared */ + pch_pic_write_reg(&s->irr, index, old & ~irq); + /* update cleared irqs */ + pch_pic_update_batch_irqs(s, irq, 0); + break; + case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END: + offset -= PCH_PIC_AUTO_CTRL0_START; + index = offset >> 2; + /* we only use default mode: fixed interrupt distribution mode */ + pch_pic_write_reg(&s->auto_ctrl0, index, 0); + break; + case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END: + offset -= PCH_PIC_AUTO_CTRL1_START; + index = offset >> 2; + /* we only use default mode: fixed interrupt distribution mode */ + pch_pic_write_reg(&s->auto_ctrl1, index, 0); + break; + case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END: + offset -= PCH_PIC_ROUTE_ENTRY_START; + /* only route to int0: eiointc */ + s->route_entry[offset] = 1; + break; + case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END: + /* route table to eiointc */ + offset -= PCH_PIC_HTMSI_VEC_START; + s->htmsi_vector[offset] = (u8)data; + break; + case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END: + offset -= PCH_PIC_POLARITY_START; + index = offset >> 2; + /* we only use defalut value 0: high level triggered */ + pch_pic_write_reg(&s->polarity, index, 0); + break; + default: + ret = -EINVAL; + break; + } + spin_unlock(&s->lock); + + return ret; +} + +static int kvm_pch_pic_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, const void *val) +{ + int ret; + struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic; + + if (!s) { + kvm_err("%s: pch pic irqchip not valid!\n", __func__); + return -EINVAL; + } + + /* statistics of pch pic writing */ + vcpu->kvm->stat.pch_pic_write_exits++; + ret = loongarch_pch_pic_write(s, addr, len, val); + + return ret; +} + +static const struct kvm_io_device_ops kvm_pch_pic_ops = { + .read = kvm_pch_pic_read, + .write = kvm_pch_pic_write, +}; + +static int kvm_pch_pic_init(struct kvm_device *dev, u64 addr) +{ + int ret; + struct kvm *kvm = dev->kvm; + struct kvm_io_device *device; + struct loongarch_pch_pic *s = dev->kvm->arch.pch_pic; + + s->pch_pic_base = addr; + device = &s->device; + /* init device by pch pic writing and reading ops */ + kvm_iodevice_init(device, &kvm_pch_pic_ops); + mutex_lock(&kvm->slots_lock); + /* register pch pic device */ + ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, addr, PCH_PIC_SIZE, device); + mutex_unlock(&kvm->slots_lock); + + return (ret < 0) ? -EFAULT : 0; +} + +/* used by user space to get or set pch pic registers */ +static int kvm_pch_pic_regs_access(struct kvm_device *dev, + struct kvm_device_attr *attr, + bool is_write) +{ + int addr, offset, len = 8, ret = 0; + void __user *data; + void *p = NULL; + struct loongarch_pch_pic *s; + + s = dev->kvm->arch.pch_pic; + addr = attr->attr; + data = (void __user *)attr->addr; + + /* get pointer to pch pic register by addr */ + switch (addr) { + case PCH_PIC_MASK_START: + p = &s->mask; + break; + case PCH_PIC_HTMSI_EN_START: + p = &s->htmsi_en; + break; + case PCH_PIC_EDGE_START: + p = &s->edge; + break; + case PCH_PIC_AUTO_CTRL0_START: + p = &s->auto_ctrl0; + break; + case PCH_PIC_AUTO_CTRL1_START: + p = &s->auto_ctrl1; + break; + case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END: + offset = addr - PCH_PIC_ROUTE_ENTRY_START; + p = &s->route_entry[offset]; + len = 1; + break; + case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END: + offset = addr - PCH_PIC_HTMSI_VEC_START; + p = &s->htmsi_vector[offset]; + len = 1; + break; + case PCH_PIC_INT_IRR_START: + p = &s->irr; + break; + case PCH_PIC_INT_ISR_START: + p = &s->isr; + break; + case PCH_PIC_POLARITY_START: + p = &s->polarity; + break; + default: + return -EINVAL; + } + + spin_lock(&s->lock); + /* write or read value according to is_write */ + if (is_write) { + if (copy_from_user(p, data, len)) + ret = -EFAULT; + } else { + if (copy_to_user(data, p, len)) + ret = -EFAULT; + } + spin_unlock(&s->lock); + + return ret; +} + +static int kvm_pch_pic_get_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS: + return kvm_pch_pic_regs_access(dev, attr, false); + default: + return -EINVAL; + } +} + +static int kvm_pch_pic_set_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + u64 addr; + void __user *uaddr = (void __user *)(long)attr->addr; + + switch (attr->group) { + case KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL: + switch (attr->attr) { + case KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT: + if (copy_from_user(&addr, uaddr, sizeof(addr))) + return -EFAULT; + + if (!dev->kvm->arch.pch_pic) { + kvm_err("%s: please create pch_pic irqchip first!\n", __func__); + return -ENODEV; + } + + return kvm_pch_pic_init(dev, addr); + default: + kvm_err("%s: unknown group (%d) attr (%lld)\n", __func__, attr->group, + attr->attr); + return -EINVAL; + } + case KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS: + return kvm_pch_pic_regs_access(dev, attr, true); + default: + return -EINVAL; + } +} + +static int kvm_setup_default_irq_routing(struct kvm *kvm) +{ + int i, ret; + u32 nr = KVM_IRQCHIP_NUM_PINS; + struct kvm_irq_routing_entry *entries; + + entries = kcalloc(nr, sizeof(*entries), GFP_KERNEL); + if (!entries) + return -ENOMEM; + + for (i = 0; i < nr; i++) { + entries[i].gsi = i; + entries[i].type = KVM_IRQ_ROUTING_IRQCHIP; + entries[i].u.irqchip.irqchip = 0; + entries[i].u.irqchip.pin = i; + } + ret = kvm_set_irq_routing(kvm, entries, nr, 0); + kfree(entries); + + return ret; +} + +static int kvm_pch_pic_create(struct kvm_device *dev, u32 type) +{ + int ret; + struct kvm *kvm = dev->kvm; + struct loongarch_pch_pic *s; + + /* pch pic should not has been created */ + if (kvm->arch.pch_pic) + return -EINVAL; + + ret = kvm_setup_default_irq_routing(kvm); + if (ret) + return -ENOMEM; + + s = kzalloc(sizeof(struct loongarch_pch_pic), GFP_KERNEL); + if (!s) + return -ENOMEM; + + spin_lock_init(&s->lock); + s->kvm = kvm; + kvm->arch.pch_pic = s; + + return 0; +} + +static void kvm_pch_pic_destroy(struct kvm_device *dev) +{ + struct kvm *kvm; + struct loongarch_pch_pic *s; + + if (!dev || !dev->kvm || !dev->kvm->arch.pch_pic) + return; + + kvm = dev->kvm; + s = kvm->arch.pch_pic; + /* unregister pch pic device and free it's memory */ + kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device); + kfree(s); +} + +static struct kvm_device_ops kvm_pch_pic_dev_ops = { + .name = "kvm-loongarch-pch-pic", + .create = kvm_pch_pic_create, + .destroy = kvm_pch_pic_destroy, + .set_attr = kvm_pch_pic_set_attr, + .get_attr = kvm_pch_pic_get_attr, +}; + +int kvm_loongarch_register_pch_pic_device(void) +{ + return kvm_register_device_ops(&kvm_pch_pic_dev_ops, KVM_DEV_TYPE_LOONGARCH_PCHPIC); +} diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c new file mode 100644 index 0000000000000..9a39627aecf07 --- /dev/null +++ b/arch/loongarch/kvm/irqfd.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#include +#include +#include + +static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, bool line_status) +{ + /* PCH-PIC pin (0 ~ 64) <---> GSI (0 ~ 64) */ + pch_pic_set_irq(kvm->arch.pch_pic, e->irqchip.pin, level); + + return 0; +} + +/* + * kvm_set_msi: inject the MSI corresponding to the + * MSI routing entry + * + * This is the entry point for irqfd MSI injection + * and userspace MSI injection. + */ +int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, bool line_status) +{ + if (!level) + return -1; + + pch_msi_set_irq(kvm, e->msi.data, level); + + return 0; +} + +/* + * kvm_set_routing_entry: populate a kvm routing entry + * from a user routing entry + * + * @kvm: the VM this entry is applied to + * @e: kvm kernel routing entry handle + * @ue: user api routing entry handle + * return 0 on success, -EINVAL on errors. + */ +int kvm_set_routing_entry(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) +{ + switch (ue->type) { + case KVM_IRQ_ROUTING_IRQCHIP: + e->set = kvm_set_pic_irq; + e->irqchip.irqchip = ue->u.irqchip.irqchip; + e->irqchip.pin = ue->u.irqchip.pin; + + if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) + return -EINVAL; + + return 0; + case KVM_IRQ_ROUTING_MSI: + e->set = kvm_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + return 0; + default: + return -EINVAL; + } +} + +int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, bool line_status) +{ + switch (e->type) { + case KVM_IRQ_ROUTING_IRQCHIP: + pch_pic_set_irq(kvm->arch.pch_pic, e->irqchip.pin, level); + return 0; + case KVM_IRQ_ROUTING_MSI: + pch_msi_set_irq(kvm, e->msi.data, level); + return 0; + default: + return -EWOULDBLOCK; + } +} + +bool kvm_arch_intc_initialized(struct kvm *kvm) +{ + return kvm_arch_irqchip_in_kernel(kvm); +} diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c index 27e9b94c0a0b6..396fed2665a51 100644 --- a/arch/loongarch/kvm/main.c +++ b/arch/loongarch/kvm/main.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "trace.h" unsigned long vpid_mask; @@ -313,7 +315,7 @@ void kvm_arch_disable_virtualization_cpu(void) static int kvm_loongarch_env_init(void) { - int cpu, order; + int cpu, order, ret; void *addr; struct kvm_context *context; @@ -368,7 +370,20 @@ static int kvm_loongarch_env_init(void) kvm_init_gcsr_flag(); - return 0; + /* Register LoongArch IPI interrupt controller interface. */ + ret = kvm_loongarch_register_ipi_device(); + if (ret) + return ret; + + /* Register LoongArch EIOINTC interrupt controller interface. */ + ret = kvm_loongarch_register_eiointc_device(); + if (ret) + return ret; + + /* Register LoongArch PCH-PIC interrupt controller interface. */ + ret = kvm_loongarch_register_pch_pic_device(); + + return ret; } static void kvm_loongarch_env_exit(void) diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c index 28681dfb4b859..4d203294767c5 100644 --- a/arch/loongarch/kvm/mmu.c +++ b/arch/loongarch/kvm/mmu.c @@ -552,12 +552,10 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, bool write) { int ret = 0; - kvm_pfn_t pfn = 0; kvm_pte_t *ptep, changed, new; gfn_t gfn = gpa >> PAGE_SHIFT; struct kvm *kvm = vcpu->kvm; struct kvm_memory_slot *slot; - struct page *page; spin_lock(&kvm->mmu_lock); @@ -570,8 +568,6 @@ static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, bool writ /* Track access to pages marked old */ new = kvm_pte_mkyoung(*ptep); - /* call kvm_set_pfn_accessed() after unlock */ - if (write && !kvm_pte_dirty(new)) { if (!kvm_pte_write(new)) { ret = -EFAULT; @@ -595,26 +591,14 @@ static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, bool writ } changed = new ^ (*ptep); - if (changed) { + if (changed) kvm_set_pte(ptep, new); - pfn = kvm_pte_pfn(new); - page = kvm_pfn_to_refcounted_page(pfn); - if (page) - get_page(page); - } + spin_unlock(&kvm->mmu_lock); - if (changed) { - if (kvm_pte_young(changed)) - kvm_set_pfn_accessed(pfn); + if (kvm_pte_dirty(changed)) + mark_page_dirty(kvm, gfn); - if (kvm_pte_dirty(changed)) { - mark_page_dirty(kvm, gfn); - kvm_set_pfn_dirty(pfn); - } - if (page) - put_page(page); - } return ret; out: spin_unlock(&kvm->mmu_lock); @@ -796,6 +780,7 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write) struct kvm *kvm = vcpu->kvm; struct kvm_memory_slot *memslot; struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; + struct page *page; /* Try the fast path to handle old / clean pages */ srcu_idx = srcu_read_lock(&kvm->srcu); @@ -823,7 +808,7 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write) mmu_seq = kvm->mmu_invalidate_seq; /* * Ensure the read of mmu_invalidate_seq isn't reordered with PTE reads in - * gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't + * kvm_faultin_pfn() (which calls get_user_pages()), so that we don't * risk the page we get a reference to getting unmapped before we have a * chance to grab the mmu_lock without mmu_invalidate_retry() noticing. * @@ -835,7 +820,7 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write) smp_rmb(); /* Slow path - ask KVM core whether we can access this GPA */ - pfn = gfn_to_pfn_prot(kvm, gfn, write, &writeable); + pfn = kvm_faultin_pfn(vcpu, gfn, write, &writeable, &page); if (is_error_noslot_pfn(pfn)) { err = -EFAULT; goto out; @@ -847,10 +832,10 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write) /* * This can happen when mappings are changed asynchronously, but * also synchronously if a COW is triggered by - * gfn_to_pfn_prot(). + * kvm_faultin_pfn(). */ spin_unlock(&kvm->mmu_lock); - kvm_release_pfn_clean(pfn); + kvm_release_page_unused(page); if (retry_no > 100) { retry_no = 0; schedule(); @@ -915,14 +900,13 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write) else ++kvm->stat.pages; kvm_set_pte(ptep, new_pte); + + kvm_release_faultin_page(kvm, page, false, writeable); spin_unlock(&kvm->mmu_lock); - if (prot_bits & _PAGE_DIRTY) { + if (prot_bits & _PAGE_DIRTY) mark_page_dirty_in_slot(kvm, memslot, gfn); - kvm_set_pfn_dirty(pfn); - } - kvm_release_pfn_clean(pfn); out: srcu_read_unlock(&kvm->srcu, srcu_idx); return err; diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 174734a23d0ac..cab1818be68d8 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1475,6 +1475,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) /* Init */ vcpu->arch.last_sched_cpu = -1; + /* Init ipi_state lock */ + spin_lock_init(&vcpu->arch.ipi_state.lock); + /* * Initialize guest register state to valid architectural reset state. */ diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c index 4ba734aaef87a..b8b3e1972d6ea 100644 --- a/arch/loongarch/kvm/vm.c +++ b/arch/loongarch/kvm/vm.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include const struct _kvm_stats_desc kvm_vm_stats_desc[] = { KVM_GENERIC_VM_STATS(), @@ -76,6 +78,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) int r; switch (ext) { + case KVM_CAP_IRQCHIP: case KVM_CAP_ONE_REG: case KVM_CAP_ENABLE_CAP: case KVM_CAP_READONLY_MEM: @@ -161,6 +164,8 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) struct kvm_device_attr attr; switch (ioctl) { + case KVM_CREATE_IRQCHIP: + return 0; case KVM_HAS_DEVICE_ATTR: if (copy_from_user(&attr, argp, sizeof(attr))) return -EFAULT; @@ -170,3 +175,19 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) return -ENOIOCTLCMD; } } + +int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event, bool line_status) +{ + if (!kvm_arch_irqchip_in_kernel(kvm)) + return -ENXIO; + + irq_event->status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, + irq_event->irq, irq_event->level, line_status); + + return 0; +} + +bool kvm_arch_irqchip_in_kernel(struct kvm *kvm) +{ + return (kvm->arch.ipi && kvm->arch.eiointc && kvm->arch.pch_pic); +} diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c index 5ac9beb5f0935..3b427b319db21 100644 --- a/arch/loongarch/mm/tlb.c +++ b/arch/loongarch/mm/tlb.c @@ -289,7 +289,7 @@ static void setup_tlb_handler(int cpu) /* Avoid lockdep warning */ rcutree_report_cpu_starting(cpu); -#ifdef CONFIG_NUMA +#if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT) vec_sz = sizeof(exception_handlers); if (pcpu_handlers[cpu]) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 7dbefd4ba2107..dd350cba1252f 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -179,7 +179,7 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) if (!is_tail_call) { /* Set return value */ - move_reg(ctx, LOONGARCH_GPR_A0, regmap[BPF_REG_0]); + emit_insn(ctx, addiw, LOONGARCH_GPR_A0, regmap[BPF_REG_0], 0); /* Return to the caller */ emit_insn(ctx, jirl, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); } else { diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index 40c1175823d61..fdde1bcd4e266 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -19,7 +19,7 @@ ccflags-vdso := \ cflags-vdso := $(ccflags-vdso) \ -isystem $(shell $(CC) -print-file-name=include) \ $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \ - -O2 -g -fno-strict-aliasing -fno-common -fno-builtin \ + -std=gnu11 -O2 -g -fno-strict-aliasing -fno-common -fno-builtin \ -fno-stack-protector -fno-jump-tables -DDISABLE_BRANCH_PROFILING \ $(call cc-option, -fno-asynchronous-unwind-tables) \ $(call cc-option, -fno-stack-protector) diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c index 7dab46728aeda..b6958ec2a220c 100644 --- a/arch/m68k/coldfire/device.c +++ b/arch/m68k/coldfire/device.c @@ -93,7 +93,7 @@ static struct platform_device mcf_uart = { .dev.platform_data = mcf_uart_platform_data, }; -#if IS_ENABLED(CONFIG_FEC) +#ifdef MCFFEC_BASE0 #ifdef CONFIG_M5441x #define FEC_NAME "enet-fec" @@ -145,6 +145,7 @@ static struct platform_device mcf_fec0 = { .platform_data = FEC_PDATA, } }; +#endif /* MCFFEC_BASE0 */ #ifdef MCFFEC_BASE1 static struct resource mcf_fec1_resources[] = { @@ -182,7 +183,6 @@ static struct platform_device mcf_fec1 = { } }; #endif /* MCFFEC_BASE1 */ -#endif /* CONFIG_FEC */ #if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) /* @@ -624,12 +624,12 @@ static struct platform_device mcf_flexcan0 = { static struct platform_device *mcf_devices[] __initdata = { &mcf_uart, -#if IS_ENABLED(CONFIG_FEC) +#ifdef MCFFEC_BASE0 &mcf_fec0, +#endif #ifdef MCFFEC_BASE1 &mcf_fec1, #endif -#endif #if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) &mcf_qspi, #endif diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig index f8edc90827242..20d877cb4e308 100644 --- a/arch/m68k/configs/multi_defconfig +++ b/arch/m68k/configs/multi_defconfig @@ -503,6 +503,7 @@ CONFIG_UHID=m # CONFIG_USB_SUPPORT is not set CONFIG_RTC_CLASS=y # CONFIG_RTC_NVMEM is not set +CONFIG_RTC_DRV_M48T59=m CONFIG_RTC_DRV_MSM6242=m CONFIG_RTC_DRV_RP5C01=m CONFIG_RTC_DRV_GENERIC=m diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig index 71fc71bb660e0..5e1c8d0d3da57 100644 --- a/arch/m68k/configs/mvme147_defconfig +++ b/arch/m68k/configs/mvme147_defconfig @@ -391,6 +391,7 @@ CONFIG_UHID=m # CONFIG_USB_SUPPORT is not set CONFIG_RTC_CLASS=y # CONFIG_RTC_NVMEM is not set +CONFIG_RTC_DRV_M48T59=y CONFIG_RTC_DRV_GENERIC=m # CONFIG_VIRTIO_MENU is not set # CONFIG_VHOST_MENU is not set diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig index 41072e68028e2..5d1409e6a1377 100644 --- a/arch/m68k/configs/mvme16x_defconfig +++ b/arch/m68k/configs/mvme16x_defconfig @@ -392,6 +392,7 @@ CONFIG_UHID=m # CONFIG_USB_SUPPORT is not set CONFIG_RTC_CLASS=y # CONFIG_RTC_NVMEM is not set +CONFIG_RTC_DRV_M48T59=y CONFIG_RTC_DRV_GENERIC=m # CONFIG_VIRTIO_MENU is not set # CONFIG_VHOST_MENU is not set diff --git a/arch/m68k/include/asm/mcfgpio.h b/arch/m68k/include/asm/mcfgpio.h index 019f244395464..9c91ecdafc453 100644 --- a/arch/m68k/include/asm/mcfgpio.h +++ b/arch/m68k/include/asm/mcfgpio.h @@ -136,7 +136,7 @@ static inline void gpio_free(unsigned gpio) * read-modify-write as well as those controlled by the EPORT and GPIO modules. */ #define MCFGPIO_SCR_START 40 -#elif defined(CONFIGM5441x) +#elif defined(CONFIG_M5441x) /* The m5441x EPORT doesn't have its own GPIO port, uses PORT C */ #define MCFGPIO_SCR_START 0 #else diff --git a/arch/m68k/include/asm/mvme147hw.h b/arch/m68k/include/asm/mvme147hw.h index dbf88059e47a4..6ad93bac06f9d 100644 --- a/arch/m68k/include/asm/mvme147hw.h +++ b/arch/m68k/include/asm/mvme147hw.h @@ -4,24 +4,7 @@ #include -typedef struct { - unsigned char - ctrl, - bcd_sec, - bcd_min, - bcd_hr, - bcd_dow, - bcd_dom, - bcd_mth, - bcd_year; -} MK48T02; - -#define RTC_WRITE 0x80 -#define RTC_READ 0x40 -#define RTC_STOP 0x20 - -#define m147_rtc ((MK48T02 * volatile)0xfffe07f8) - +#define MVME147_RTC_BASE 0xfffe0000 struct pcc_regs { volatile u_long dma_tadr; diff --git a/arch/m68k/include/asm/mvme16xhw.h b/arch/m68k/include/asm/mvme16xhw.h index cc7f5ae1220ff..ff1126a51fbec 100644 --- a/arch/m68k/include/asm/mvme16xhw.h +++ b/arch/m68k/include/asm/mvme16xhw.h @@ -24,23 +24,7 @@ typedef struct { #define mvmelp ((*(volatile MVMElpPtr)(MVME_LPR_BASE))) -typedef struct { - unsigned char - ctrl, - bcd_sec, - bcd_min, - bcd_hr, - bcd_dow, - bcd_dom, - bcd_mth, - bcd_year; -} MK48T08_t, *MK48T08ptr_t; - -#define RTC_WRITE 0x80 -#define RTC_READ 0x40 -#define RTC_STOP 0x20 - -#define MVME_RTC_BASE 0xfffc1ff8 +#define MVME_RTC_BASE 0xfffc0000 #define MVME_I596_BASE 0xfff46000 diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c index 824c42a302c6f..3054d3857efa2 100644 --- a/arch/m68k/mvme147/config.c +++ b/arch/m68k/mvme147/config.c @@ -19,8 +19,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -36,13 +37,9 @@ static void mvme147_get_model(char *model); static void __init mvme147_sched_init(void); -extern int mvme147_hwclk (int, struct rtc_time *); extern void mvme147_reset (void); -static int bcd2int (unsigned char b); - - int __init mvme147_parse_bootinfo(const struct bi_record *bi) { uint16_t tag = be16_to_cpu(bi->tag); @@ -80,7 +77,6 @@ void __init config_mvme147(void) { mach_sched_init = mvme147_sched_init; mach_init_IRQ = mvme147_init_IRQ; - mach_hwclk = mvme147_hwclk; mach_reset = mvme147_reset; mach_get_model = mvme147_get_model; @@ -89,6 +85,28 @@ void __init config_mvme147(void) vme_brdtype = VME_TYPE_MVME147; } +static struct resource m48t59_rsrc[] = { + DEFINE_RES_MEM(MVME147_RTC_BASE, 0x800), +}; + +static struct m48t59_plat_data m48t59_data = { + .type = M48T59RTC_TYPE_M48T02, + .yy_offset = 70, +}; + +static int __init mvme147_platform_init(void) +{ + if (!MACH_IS_MVME147) + return 0; + + platform_device_register_resndata(NULL, "rtc-m48t59", -1, + m48t59_rsrc, ARRAY_SIZE(m48t59_rsrc), + &m48t59_data, sizeof(m48t59_data)); + return 0; +} + +arch_initcall(mvme147_platform_init); + static u64 mvme147_read_clk(struct clocksource *cs); static struct clocksource mvme147_clk = { @@ -162,31 +180,6 @@ static u64 mvme147_read_clk(struct clocksource *cs) return ticks; } -static int bcd2int (unsigned char b) -{ - return ((b>>4)*10 + (b&15)); -} - -int mvme147_hwclk(int op, struct rtc_time *t) -{ - if (!op) { - m147_rtc->ctrl = RTC_READ; - t->tm_year = bcd2int (m147_rtc->bcd_year); - t->tm_mon = bcd2int(m147_rtc->bcd_mth) - 1; - t->tm_mday = bcd2int (m147_rtc->bcd_dom); - t->tm_hour = bcd2int (m147_rtc->bcd_hr); - t->tm_min = bcd2int (m147_rtc->bcd_min); - t->tm_sec = bcd2int (m147_rtc->bcd_sec); - m147_rtc->ctrl = 0; - if (t->tm_year < 70) - t->tm_year += 100; - } else { - /* FIXME Setting the time is not yet supported */ - return -EOPNOTSUPP; - } - return 0; -} - static void scc_delay(void) { __asm__ __volatile__ ("nop; nop;"); diff --git a/arch/m68k/mvme16x/Makefile b/arch/m68k/mvme16x/Makefile index a8a368c2cbea5..02f9e4ad8209a 100644 --- a/arch/m68k/mvme16x/Makefile +++ b/arch/m68k/mvme16x/Makefile @@ -3,4 +3,4 @@ # Makefile for Linux arch/m68k/mvme16x source directory # -obj-y := config.o rtc.o +obj-y := config.o diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c index d1fbd1704d658..99768fe8da73a 100644 --- a/arch/m68k/mvme16x/config.c +++ b/arch/m68k/mvme16x/config.c @@ -21,9 +21,10 @@ #include #include #include -#include #include #include +#include +#include #include #include @@ -39,16 +40,10 @@ extern t_bdid mvme_bdid; -static MK48T08ptr_t volatile rtc = (MK48T08ptr_t)MVME_RTC_BASE; - static void mvme16x_get_model(char *model); extern void mvme16x_sched_init(void); -extern int mvme16x_hwclk (int, struct rtc_time *); extern void mvme16x_reset (void); -int bcd2int (unsigned char b); - - unsigned short mvme16x_config; EXPORT_SYMBOL(mvme16x_config); @@ -268,7 +263,6 @@ void __init config_mvme16x(void) mach_sched_init = mvme16x_sched_init; mach_init_IRQ = mvme16x_init_IRQ; - mach_hwclk = mvme16x_hwclk; mach_reset = mvme16x_reset; mach_get_model = mvme16x_get_model; mach_get_hardware_list = mvme16x_get_hardware_list; @@ -312,6 +306,28 @@ void __init config_mvme16x(void) } } +static struct resource m48t59_rsrc[] = { + DEFINE_RES_MEM(MVME_RTC_BASE, 0x2000), +}; + +static struct m48t59_plat_data m48t59_data = { + .type = M48T59RTC_TYPE_M48T08, + .yy_offset = 70, +}; + +static int __init mvme16x_platform_init(void) +{ + if (!MACH_IS_MVME16x) + return 0; + + platform_device_register_resndata(NULL, "rtc-m48t59", -1, + m48t59_rsrc, ARRAY_SIZE(m48t59_rsrc), + &m48t59_data, sizeof(m48t59_data)); + return 0; +} + +arch_initcall(mvme16x_platform_init); + static irqreturn_t mvme16x_abort_int (int irq, void *dev_id) { unsigned long *new = (unsigned long *)vectors; @@ -426,28 +442,3 @@ static u64 mvme16x_read_clk(struct clocksource *cs) return ticks; } - -int bcd2int (unsigned char b) -{ - return ((b>>4)*10 + (b&15)); -} - -int mvme16x_hwclk(int op, struct rtc_time *t) -{ - if (!op) { - rtc->ctrl = RTC_READ; - t->tm_year = bcd2int (rtc->bcd_year); - t->tm_mon = bcd2int(rtc->bcd_mth) - 1; - t->tm_mday = bcd2int (rtc->bcd_dom); - t->tm_hour = bcd2int (rtc->bcd_hr); - t->tm_min = bcd2int (rtc->bcd_min); - t->tm_sec = bcd2int (rtc->bcd_sec); - rtc->ctrl = 0; - if (t->tm_year < 70) - t->tm_year += 100; - } else { - /* FIXME Setting the time is not yet supported */ - return -EOPNOTSUPP; - } - return 0; -} diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c deleted file mode 100644 index ccbaae1125e6f..0000000000000 --- a/arch/m68k/mvme16x/rtc.c +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Real Time Clock interface for Linux on the MVME16x - * - * Based on the PC driver by Paul Gortmaker. - */ - -#define RTC_VERSION "1.00" - -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For struct rtc_time and ioctls, etc */ -#include -#include - -#include -#include -#include - -/* - * We sponge a minor off of the misc major. No need slurping - * up another valuable major dev number for this. If you add - * an ioctl, make sure you don't conflict with SPARC's RTC - * ioctls. - */ - -static const unsigned char days_in_mo[] = -{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -static atomic_t rtc_ready = ATOMIC_INIT(1); - -static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - volatile MK48T08ptr_t rtc = (MK48T08ptr_t)MVME_RTC_BASE; - unsigned long flags; - struct rtc_time wtime; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case RTC_RD_TIME: /* Read the time/date from RTC */ - { - local_irq_save(flags); - /* Ensure clock and real-time-mode-register are accessible */ - rtc->ctrl = RTC_READ; - memset(&wtime, 0, sizeof(struct rtc_time)); - wtime.tm_sec = bcd2bin(rtc->bcd_sec); - wtime.tm_min = bcd2bin(rtc->bcd_min); - wtime.tm_hour = bcd2bin(rtc->bcd_hr); - wtime.tm_mday = bcd2bin(rtc->bcd_dom); - wtime.tm_mon = bcd2bin(rtc->bcd_mth)-1; - wtime.tm_year = bcd2bin(rtc->bcd_year); - if (wtime.tm_year < 70) - wtime.tm_year += 100; - wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1; - rtc->ctrl = 0; - local_irq_restore(flags); - return copy_to_user(argp, &wtime, sizeof wtime) ? - -EFAULT : 0; - } - case RTC_SET_TIME: /* Set the RTC */ - { - struct rtc_time rtc_tm; - unsigned char mon, day, hrs, min, sec, leap_yr; - unsigned int yrs; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (copy_from_user(&rtc_tm, argp, sizeof(struct rtc_time))) - return -EFAULT; - - yrs = rtc_tm.tm_year; - if (yrs < 1900) - yrs += 1900; - mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ - day = rtc_tm.tm_mday; - hrs = rtc_tm.tm_hour; - min = rtc_tm.tm_min; - sec = rtc_tm.tm_sec; - - leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); - - if ((mon > 12) || (day == 0)) - return -EINVAL; - - if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) - return -EINVAL; - - if ((hrs >= 24) || (min >= 60) || (sec >= 60)) - return -EINVAL; - - if (yrs >= 2070) - return -EINVAL; - - local_irq_save(flags); - rtc->ctrl = RTC_WRITE; - - rtc->bcd_sec = bin2bcd(sec); - rtc->bcd_min = bin2bcd(min); - rtc->bcd_hr = bin2bcd(hrs); - rtc->bcd_dom = bin2bcd(day); - rtc->bcd_mth = bin2bcd(mon); - rtc->bcd_year = bin2bcd(yrs%100); - - rtc->ctrl = 0; - local_irq_restore(flags); - return 0; - } - default: - return -EINVAL; - } -} - -/* - * We enforce only one user at a time here with the open/close. - */ -static int rtc_open(struct inode *inode, struct file *file) -{ - if( !atomic_dec_and_test(&rtc_ready) ) - { - atomic_inc( &rtc_ready ); - return -EBUSY; - } - return 0; -} - -static int rtc_release(struct inode *inode, struct file *file) -{ - atomic_inc( &rtc_ready ); - return 0; -} - -/* - * The various file operations we support. - */ - -static const struct file_operations rtc_fops = { - .unlocked_ioctl = rtc_ioctl, - .open = rtc_open, - .release = rtc_release, - .llseek = noop_llseek, -}; - -static struct miscdevice rtc_dev= -{ - .minor = RTC_MINOR, - .name = "rtc", - .fops = &rtc_fops -}; - -static int __init rtc_MK48T08_init(void) -{ - if (!MACH_IS_MVME16x) - return -ENODEV; - - pr_info("MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION); - return misc_register(&rtc_dev); -} -device_initcall(rtc_MK48T08_init); diff --git a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi index cce9428afc41f..ee71045883e7e 100644 --- a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi +++ b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi @@ -70,7 +70,6 @@ device_type = "pci"; #address-cells = <3>; #size-cells = <2>; - #interrupt-cells = <2>; msi-parent = <&msi>; reg = <0 0x1a000000 0 0x02000000>, @@ -234,7 +233,7 @@ }; }; - pci_bridge@9,0 { + pcie@9,0 { compatible = "pci0014,7a19.1", "pci0014,7a19", "pciclass060400", @@ -244,12 +243,16 @@ interrupts = <32 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 32 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@a,0 { + pcie@a,0 { compatible = "pci0014,7a09.1", "pci0014,7a09", "pciclass060400", @@ -259,12 +262,16 @@ interrupts = <33 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 33 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@b,0 { + pcie@b,0 { compatible = "pci0014,7a09.1", "pci0014,7a09", "pciclass060400", @@ -274,12 +281,16 @@ interrupts = <34 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 34 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@c,0 { + pcie@c,0 { compatible = "pci0014,7a09.1", "pci0014,7a09", "pciclass060400", @@ -289,12 +300,16 @@ interrupts = <35 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 35 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@d,0 { + pcie@d,0 { compatible = "pci0014,7a19.1", "pci0014,7a19", "pciclass060400", @@ -304,12 +319,16 @@ interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 36 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@e,0 { + pcie@e,0 { compatible = "pci0014,7a09.1", "pci0014,7a09", "pciclass060400", @@ -319,12 +338,16 @@ interrupts = <37 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 37 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@f,0 { + pcie@f,0 { compatible = "pci0014,7a29.1", "pci0014,7a29", "pciclass060400", @@ -334,12 +357,16 @@ interrupts = <40 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 40 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@10,0 { + pcie@10,0 { compatible = "pci0014,7a19.1", "pci0014,7a19", "pciclass060400", @@ -349,12 +376,16 @@ interrupts = <41 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 41 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@11,0 { + pcie@11,0 { compatible = "pci0014,7a29.1", "pci0014,7a29", "pciclass060400", @@ -364,12 +395,16 @@ interrupts = <42 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 42 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@12,0 { + pcie@12,0 { compatible = "pci0014,7a19.1", "pci0014,7a19", "pciclass060400", @@ -379,12 +414,16 @@ interrupts = <43 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 43 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@13,0 { + pcie@13,0 { compatible = "pci0014,7a29.1", "pci0014,7a29", "pciclass060400", @@ -394,12 +433,16 @@ interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 38 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; - pci_bridge@14,0 { + pcie@14,0 { compatible = "pci0014,7a19.1", "pci0014,7a19", "pciclass060400", @@ -409,9 +452,13 @@ interrupts = <39 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&pic>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; interrupt-map = <0 0 0 0 &pic 39 IRQ_TYPE_LEVEL_HIGH>; + ranges; }; }; diff --git a/arch/mips/boot/dts/mobileye/eyeq5-clocks.dtsi b/arch/mips/boot/dts/mobileye/eyeq5-clocks.dtsi deleted file mode 100644 index 17a342cc744e5..0000000000000 --- a/arch/mips/boot/dts/mobileye/eyeq5-clocks.dtsi +++ /dev/null @@ -1,270 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -/* - * Copyright 2023 Mobileye Vision Technologies Ltd. - */ - -#include - -/ { - /* Fixed clock */ - xtal: xtal { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <30000000>; - }; - -/* PLL_CPU derivatives */ - occ_cpu: occ-cpu { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_CPU>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - si_css0_ref_clk: si-css0-ref-clk { /* gate ClkRstGen_si_css0_ref */ - compatible = "fixed-factor-clock"; - clocks = <&occ_cpu>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - cpc_clk: cpc-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - core0_clk: core0-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - core1_clk: core1-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - core2_clk: core2-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - core3_clk: core3-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - cm_clk: cm-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - mem_clk: mem-clk { - compatible = "fixed-factor-clock"; - clocks = <&si_css0_ref_clk>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - occ_isram: occ-isram { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_CPU>; - #clock-cells = <0>; - clock-div = <2>; - clock-mult = <1>; - }; - isram_clk: isram-clk { /* gate ClkRstGen_isram */ - compatible = "fixed-factor-clock"; - clocks = <&occ_isram>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - occ_dbu: occ-dbu { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_CPU>; - #clock-cells = <0>; - clock-div = <10>; - clock-mult = <1>; - }; - si_dbu_tp_pclk: si-dbu-tp-pclk { /* gate ClkRstGen_dbu */ - compatible = "fixed-factor-clock"; - clocks = <&occ_dbu>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; -/* PLL_VDI derivatives */ - occ_vdi: occ-vdi { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_VDI>; - #clock-cells = <0>; - clock-div = <2>; - clock-mult = <1>; - }; - vdi_clk: vdi-clk { /* gate ClkRstGen_vdi */ - compatible = "fixed-factor-clock"; - clocks = <&occ_vdi>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - occ_can_ser: occ-can-ser { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_VDI>; - #clock-cells = <0>; - clock-div = <16>; - clock-mult = <1>; - }; - can_ser_clk: can-ser-clk { /* gate ClkRstGen_can_ser */ - compatible = "fixed-factor-clock"; - clocks = <&occ_can_ser>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - i2c_ser_clk: i2c-ser-clk { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_VDI>; - #clock-cells = <0>; - clock-div = <20>; - clock-mult = <1>; - }; -/* PLL_PER derivatives */ - occ_periph: occ-periph { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_PER>; - #clock-cells = <0>; - clock-div = <16>; - clock-mult = <1>; - }; - periph_clk: periph-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - can_clk: can-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - spi_clk: spi-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - uart_clk: uart-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - i2c_clk: i2c-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - clock-output-names = "i2c_clk"; - }; - timer_clk: timer-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - clock-output-names = "timer_clk"; - }; - gpio_clk: gpio-clk { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - clock-output-names = "gpio_clk"; - }; - emmc_sys_clk: emmc-sys-clk { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_PER>; - #clock-cells = <0>; - clock-div = <10>; - clock-mult = <1>; - clock-output-names = "emmc_sys_clk"; - }; - ccf_ctrl_clk: ccf-ctrl-clk { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_PER>; - #clock-cells = <0>; - clock-div = <4>; - clock-mult = <1>; - clock-output-names = "ccf_ctrl_clk"; - }; - occ_mjpeg_core: occ-mjpeg-core { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_PER>; - #clock-cells = <0>; - clock-div = <2>; - clock-mult = <1>; - clock-output-names = "occ_mjpeg_core"; - }; - hsm_clk: hsm-clk { /* gate ClkRstGen_hsm */ - compatible = "fixed-factor-clock"; - clocks = <&occ_mjpeg_core>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - clock-output-names = "hsm_clk"; - }; - mjpeg_core_clk: mjpeg-core-clk { /* gate ClkRstGen_mjpeg_gen */ - compatible = "fixed-factor-clock"; - clocks = <&occ_mjpeg_core>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - clock-output-names = "mjpeg_core_clk"; - }; - fcmu_a_clk: fcmu-a-clk { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_PER>; - #clock-cells = <0>; - clock-div = <20>; - clock-mult = <1>; - clock-output-names = "fcmu_a_clk"; - }; - occ_pci_sys: occ-pci-sys { - compatible = "fixed-factor-clock"; - clocks = <&olb EQ5C_PLL_PER>; - #clock-cells = <0>; - clock-div = <8>; - clock-mult = <1>; - clock-output-names = "occ_pci_sys"; - }; - pclk: pclk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <250000000>; /* 250MHz */ - }; - tsu_clk: tsu-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <125000000>; /* 125MHz */ - }; -}; diff --git a/arch/mips/boot/dts/mobileye/eyeq5.dtsi b/arch/mips/boot/dts/mobileye/eyeq5.dtsi index 0708771c193d0..5d73e8320b8ef 100644 --- a/arch/mips/boot/dts/mobileye/eyeq5.dtsi +++ b/arch/mips/boot/dts/mobileye/eyeq5.dtsi @@ -5,7 +5,7 @@ #include -#include "eyeq5-clocks.dtsi" +#include / { #address-cells = <2>; @@ -17,7 +17,7 @@ device_type = "cpu"; compatible = "img,i6500"; reg = <0>; - clocks = <&core0_clk>; + clocks = <&olb EQ5C_CPU_CORE0>; }; }; @@ -64,6 +64,24 @@ #interrupt-cells = <1>; }; + xtal: xtal { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <30000000>; + }; + + pclk: pclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <250000000>; /* 250MHz */ + }; + + tsu_clk: tsu-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; /* 125MHz */ + }; + soc: soc { #address-cells = <2>; #size-cells = <2>; @@ -76,7 +94,7 @@ reg-io-width = <4>; interrupt-parent = <&gic>; interrupts = ; - clocks = <&uart_clk>, <&occ_periph>; + clocks = <&olb EQ5C_PER_UART>, <&olb EQ5C_PER_OCC>; clock-names = "uartclk", "apb_pclk"; resets = <&olb 0 10>; pinctrl-names = "default"; @@ -89,7 +107,7 @@ reg-io-width = <4>; interrupt-parent = <&gic>; interrupts = ; - clocks = <&uart_clk>, <&occ_periph>; + clocks = <&olb EQ5C_PER_UART>, <&olb EQ5C_PER_OCC>; clock-names = "uartclk", "apb_pclk"; resets = <&olb 0 11>; pinctrl-names = "default"; @@ -102,7 +120,7 @@ reg-io-width = <4>; interrupt-parent = <&gic>; interrupts = ; - clocks = <&uart_clk>, <&occ_periph>; + clocks = <&olb EQ5C_PER_UART>, <&olb EQ5C_PER_OCC>; clock-names = "uartclk", "apb_pclk"; resets = <&olb 0 12>; pinctrl-names = "default"; @@ -135,7 +153,7 @@ timer { compatible = "mti,gic-timer"; interrupts = ; - clocks = <&core0_clk>; + clocks = <&olb EQ5C_CPU_CORE0>; }; }; }; diff --git a/arch/mips/boot/dts/mobileye/eyeq6h-fixed-clocks.dtsi b/arch/mips/boot/dts/mobileye/eyeq6h-fixed-clocks.dtsi deleted file mode 100644 index 5fa99e06fde7e..0000000000000 --- a/arch/mips/boot/dts/mobileye/eyeq6h-fixed-clocks.dtsi +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -/* - * Copyright 2023 Mobileye Vision Technologies Ltd. - */ - -#include - -/ { - xtal: clock-30000000 { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <30000000>; - }; - - pll_west: clock-2000000000-west { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <2000000000>; - }; - - pll_cpu: clock-2000000000-cpu { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <2000000000>; - }; - - /* pll-cpu derivatives */ - occ_cpu: clock-2000000000-occ-cpu { - compatible = "fixed-factor-clock"; - clocks = <&pll_cpu>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - - /* pll-west derivatives */ - occ_periph_w: clock-200000000 { - compatible = "fixed-factor-clock"; - clocks = <&pll_west>; - #clock-cells = <0>; - clock-div = <10>; - clock-mult = <1>; - }; - uart_clk: clock-200000000-uart { - compatible = "fixed-factor-clock"; - clocks = <&occ_periph_w>; - #clock-cells = <0>; - clock-div = <1>; - clock-mult = <1>; - }; - -}; diff --git a/arch/mips/boot/dts/mobileye/eyeq6h.dtsi b/arch/mips/boot/dts/mobileye/eyeq6h.dtsi index 1db3c3cda2e39..4a1a43f351d39 100644 --- a/arch/mips/boot/dts/mobileye/eyeq6h.dtsi +++ b/arch/mips/boot/dts/mobileye/eyeq6h.dtsi @@ -5,7 +5,7 @@ #include -#include "eyeq6h-fixed-clocks.dtsi" +#include / { #address-cells = <2>; @@ -17,7 +17,7 @@ device_type = "cpu"; compatible = "img,i6500"; reg = <0>; - clocks = <&occ_cpu>; + clocks = <&olb_central EQ6HC_CENTRAL_CPU_OCC>; }; }; @@ -32,19 +32,42 @@ #interrupt-cells = <1>; }; + xtal: clock-30000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <30000000>; + }; + soc: soc { compatible = "simple-bus"; #address-cells = <2>; #size-cells = <2>; ranges; + olb_acc: system-controller@d2003000 { + compatible = "mobileye,eyeq6h-acc-olb", "syscon"; + reg = <0x0 0xd2003000 0x0 0x1000>; + #reset-cells = <1>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + + olb_central: system-controller@d3100000 { + compatible = "mobileye,eyeq6h-central-olb", "syscon"; + reg = <0x0 0xd3100000 0x0 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + uart0: serial@d3331000 { compatible = "arm,pl011", "arm,primecell"; reg = <0 0xd3331000 0x0 0x1000>; reg-io-width = <4>; interrupt-parent = <&gic>; interrupts = ; - clocks = <&occ_periph_w>, <&occ_periph_w>; + clocks = <&olb_west EQ6HC_WEST_PER_UART>, <&olb_west EQ6HC_WEST_PER_OCC>; clock-names = "uartclk", "apb_pclk"; }; @@ -56,6 +79,15 @@ pinctrl-single,function-mask = <0xffff>; }; + olb_west: system-controller@d3338000 { + compatible = "mobileye,eyeq6h-west-olb", "syscon"; + reg = <0x0 0xd3338000 0x0 0x1000>; + #reset-cells = <1>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + pinctrl_east: pinctrl@d3357000 { compatible = "pinctrl-single"; reg = <0x0 0xd3357000 0x0 0xb0>; @@ -64,6 +96,23 @@ pinctrl-single,function-mask = <0xffff>; }; + olb_east: system-controller@d3358000 { + compatible = "mobileye,eyeq6h-east-olb", "syscon"; + reg = <0x0 0xd3358000 0x0 0x1000>; + #reset-cells = <1>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + + olb_south: system-controller@d8013000 { + compatible = "mobileye,eyeq6h-south-olb", "syscon"; + reg = <0x0 0xd8013000 0x0 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + pinctrl_south: pinctrl@d8014000 { compatible = "pinctrl-single"; reg = <0x0 0xd8014000 0x0 0xf8>; @@ -72,6 +121,22 @@ pinctrl-single,function-mask = <0xffff>; }; + olb_ddr0: system-controller@e4080000 { + compatible = "mobileye,eyeq6h-ddr0-olb", "syscon"; + reg = <0x0 0xe4080000 0x0 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + + olb_ddr1: system-controller@e4081000 { + compatible = "mobileye,eyeq6h-ddr1-olb", "syscon"; + reg = <0x0 0xe4081000 0x0 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + gic: interrupt-controller@f0920000 { compatible = "mti,gic"; reg = <0x0 0xf0920000 0x0 0x20000>; @@ -89,7 +154,7 @@ timer { compatible = "mti,gic-timer"; interrupts = ; - clocks = <&occ_cpu>; + clocks = <&olb_central EQ6HC_CENTRAL_CPU_OCC>; }; }; }; diff --git a/arch/mips/boot/dts/realtek/rtl930x.dtsi b/arch/mips/boot/dts/realtek/rtl930x.dtsi index 6a6f3f3fe389b..17577457d1598 100644 --- a/arch/mips/boot/dts/realtek/rtl930x.dtsi +++ b/arch/mips/boot/dts/realtek/rtl930x.dtsi @@ -61,6 +61,8 @@ }; &soc { + ranges = <0x0 0x18000000 0x20000>; + intc: interrupt-controller@3000 { compatible = "realtek,rtl9300-intc", "realtek,rtl-intc"; reg = <0x3000 0x18>, <0x3018 0x18>; @@ -88,6 +90,17 @@ interrupts = <7>, <8>, <9>, <10>, <11>; clocks = <&lx_clk>; }; + + snand: spi@1a400 { + compatible = "realtek,rtl9301-snand"; + reg = <0x1a400 0x44>; + interrupt-parent = <&intc>; + interrupts = <19>; + clocks = <&lx_clk>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; &uart0 { diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index b825ed4476c70..e3ff6179c99f9 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -59,6 +59,7 @@ #endif .endm + __HEAD #ifndef CONFIG_NO_EXCEPT_FILL /* * Reserved space for exception handlers. diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 9ff55cb80a645..2b708fac8d2c1 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -61,6 +61,7 @@ SECTIONS /* read-only */ _text = .; /* Text and read-only data */ .text : { + HEAD_TEXT TEXT_TEXT SCHED_TEXT LOCK_TEXT diff --git a/arch/mips/kvm/mmu.c b/arch/mips/kvm/mmu.c index c17157e700c03..d2c3b6b41f181 100644 --- a/arch/mips/kvm/mmu.c +++ b/arch/mips/kvm/mmu.c @@ -484,8 +484,6 @@ static int _kvm_mips_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, struct kvm *kvm = vcpu->kvm; gfn_t gfn = gpa >> PAGE_SHIFT; pte_t *ptep; - kvm_pfn_t pfn = 0; /* silence bogus GCC warning */ - bool pfn_valid = false; int ret = 0; spin_lock(&kvm->mmu_lock); @@ -498,12 +496,9 @@ static int _kvm_mips_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, } /* Track access to pages marked old */ - if (!pte_young(*ptep)) { + if (!pte_young(*ptep)) set_pte(ptep, pte_mkyoung(*ptep)); - pfn = pte_pfn(*ptep); - pfn_valid = true; - /* call kvm_set_pfn_accessed() after unlock */ - } + if (write_fault && !pte_dirty(*ptep)) { if (!pte_write(*ptep)) { ret = -EFAULT; @@ -512,9 +507,7 @@ static int _kvm_mips_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, /* Track dirtying of writeable pages */ set_pte(ptep, pte_mkdirty(*ptep)); - pfn = pte_pfn(*ptep); mark_page_dirty(kvm, gfn); - kvm_set_pfn_dirty(pfn); } if (out_entry) @@ -524,8 +517,6 @@ static int _kvm_mips_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, out: spin_unlock(&kvm->mmu_lock); - if (pfn_valid) - kvm_set_pfn_accessed(pfn); return ret; } @@ -566,6 +557,7 @@ static int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool writeable; unsigned long prot_bits; unsigned long mmu_seq; + struct page *page; /* Try the fast path to handle old / clean pages */ srcu_idx = srcu_read_lock(&kvm->srcu); @@ -587,7 +579,7 @@ static int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, mmu_seq = kvm->mmu_invalidate_seq; /* * Ensure the read of mmu_invalidate_seq isn't reordered with PTE reads - * in gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't + * in kvm_faultin_pfn() (which calls get_user_pages()), so that we don't * risk the page we get a reference to getting unmapped before we have a * chance to grab the mmu_lock without mmu_invalidate_retry() noticing. * @@ -599,7 +591,7 @@ static int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, smp_rmb(); /* Slow path - ask KVM core whether we can access this GPA */ - pfn = gfn_to_pfn_prot(kvm, gfn, write_fault, &writeable); + pfn = kvm_faultin_pfn(vcpu, gfn, write_fault, &writeable, &page); if (is_error_noslot_pfn(pfn)) { err = -EFAULT; goto out; @@ -611,10 +603,10 @@ static int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, /* * This can happen when mappings are changed asynchronously, but * also synchronously if a COW is triggered by - * gfn_to_pfn_prot(). + * kvm_faultin_pfn(). */ spin_unlock(&kvm->mmu_lock); - kvm_release_pfn_clean(pfn); + kvm_release_page_unused(page); goto retry; } @@ -628,7 +620,6 @@ static int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, if (write_fault) { prot_bits |= __WRITEABLE; mark_page_dirty(kvm, gfn); - kvm_set_pfn_dirty(pfn); } } entry = pfn_pte(pfn, __pgprot(prot_bits)); @@ -642,9 +633,8 @@ static int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, if (out_buddy) *out_buddy = *ptep_buddy(ptep); + kvm_release_faultin_page(kvm, page, false, writeable); spin_unlock(&kvm->mmu_lock); - kvm_release_pfn_clean(pfn); - kvm_set_pfn_accessed(pfn); out: srcu_read_unlock(&kvm->srcu, srcu_idx); return err; diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c index 3e34b4473d3a7..10fd5b3e63e75 100644 --- a/arch/parisc/kernel/ftrace.c +++ b/arch/parisc/kernel/ftrace.c @@ -87,7 +87,7 @@ int ftrace_enable_ftrace_graph_caller(void) int ftrace_disable_ftrace_graph_caller(void) { - static_key_enable(&ftrace_graph_enable.key); + static_key_disable(&ftrace_graph_enable.key); return 0; } #endif diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 41489483a602f..f3804103c56cc 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -403,10 +403,12 @@ PHONY += stack_protector_prepare stack_protector_prepare: prepare0 ifdef CONFIG_PPC64 $(eval KBUILD_CFLAGS += -mstack-protector-guard=tls -mstack-protector-guard-reg=r13 \ - -mstack-protector-guard-offset=$(shell awk '{if ($$2 == "PACA_CANARY") print $$3;}' include/generated/asm-offsets.h)) + -mstack-protector-guard-offset=$(shell awk '{if ($$2 == "PACA_CANARY") print $$3;}' \ + $(objtree)/include/generated/asm-offsets.h)) else $(eval KBUILD_CFLAGS += -mstack-protector-guard=tls -mstack-protector-guard-reg=r2 \ - -mstack-protector-guard-offset=$(shell awk '{if ($$2 == "TASK_CANARY") print $$3;}' include/generated/asm-offsets.h)) + -mstack-protector-guard-offset=$(shell awk '{if ($$2 == "TASK_CANARY") print $$3;}' \ + $(objtree)/include/generated/asm-offsets.h)) endif endif diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index 10618622d7efc..e1ff291ba8910 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -203,7 +203,7 @@ extern bool kvmppc_hv_handle_set_rc(struct kvm *kvm, bool nested, extern int kvmppc_book3s_instantiate_page(struct kvm_vcpu *vcpu, unsigned long gpa, struct kvm_memory_slot *memslot, - bool writing, bool kvm_ro, + bool writing, pte_t *inserted_pte, unsigned int *levelp); extern int kvmppc_init_vm_radix(struct kvm *kvm); extern void kvmppc_free_radix(struct kvm *kvm); @@ -235,7 +235,7 @@ extern void kvmppc_set_bat(struct kvm_vcpu *vcpu, struct kvmppc_bat *bat, extern void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr); extern int kvmppc_emulate_paired_single(struct kvm_vcpu *vcpu); extern kvm_pfn_t kvmppc_gpa_to_pfn(struct kvm_vcpu *vcpu, gpa_t gpa, - bool writing, bool *writable); + bool writing, bool *writable, struct page **page); extern void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev, unsigned long *rmap, long pte_index, int realmode); extern void kvmppc_update_dirty_map(const struct kvm_memory_slot *memslot, diff --git a/arch/powerpc/include/asm/simple_spinlock_types.h b/arch/powerpc/include/asm/simple_spinlock_types.h index 08243338069d2..391fc19f72723 100644 --- a/arch/powerpc/include/asm/simple_spinlock_types.h +++ b/arch/powerpc/include/asm/simple_spinlock_types.h @@ -3,7 +3,7 @@ #define _ASM_POWERPC_SIMPLE_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif typedef struct { diff --git a/arch/powerpc/include/asm/spinlock_types.h b/arch/powerpc/include/asm/spinlock_types.h index 40b01446cf755..569765fa16bc4 100644 --- a/arch/powerpc/include/asm/spinlock_types.h +++ b/arch/powerpc/include/asm/spinlock_types.h @@ -3,7 +3,7 @@ #define _ASM_POWERPC_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif #ifdef CONFIG_PPC_QUEUED_SPINLOCKS diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 73210e5bcfa77..8e776ba394978 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -2848,7 +2848,7 @@ static void __init fixup_device_tree_chrp(void) #endif #if defined(CONFIG_PPC64) && defined(CONFIG_PPC_PMAC) -static void __init fixup_device_tree_pmac(void) +static void __init fixup_device_tree_pmac64(void) { phandle u3, i2c, mpic; u32 u3_rev; @@ -2888,7 +2888,31 @@ static void __init fixup_device_tree_pmac(void) &parent, sizeof(parent)); } #else -#define fixup_device_tree_pmac() +#define fixup_device_tree_pmac64() +#endif + +#ifdef CONFIG_PPC_PMAC +static void __init fixup_device_tree_pmac(void) +{ + __be32 val = 1; + char type[8]; + phandle node; + + // Some pmacs are missing #size-cells on escc nodes + for (node = 0; prom_next_node(&node); ) { + type[0] = '\0'; + prom_getprop(node, "device_type", type, sizeof(type)); + if (prom_strcmp(type, "escc")) + continue; + + if (prom_getproplen(node, "#size-cells") != PROM_ERROR) + continue; + + prom_setprop(node, NULL, "#size-cells", &val, sizeof(val)); + } +} +#else +static inline void fixup_device_tree_pmac(void) { } #endif #ifdef CONFIG_PPC_EFIKA @@ -3111,6 +3135,7 @@ static void __init fixup_device_tree(void) { fixup_device_tree_chrp(); fixup_device_tree_pmac(); + fixup_device_tree_pmac64(); fixup_device_tree_efika(); fixup_device_tree_pasemi(); } diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index ff6c383739575..d79c5d1098c05 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -422,7 +422,7 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) EXPORT_SYMBOL_GPL(kvmppc_core_prepare_to_enter); kvm_pfn_t kvmppc_gpa_to_pfn(struct kvm_vcpu *vcpu, gpa_t gpa, bool writing, - bool *writable) + bool *writable, struct page **page) { ulong mp_pa = vcpu->arch.magic_page_pa & KVM_PAM; gfn_t gfn = gpa >> PAGE_SHIFT; @@ -437,13 +437,14 @@ kvm_pfn_t kvmppc_gpa_to_pfn(struct kvm_vcpu *vcpu, gpa_t gpa, bool writing, kvm_pfn_t pfn; pfn = (kvm_pfn_t)virt_to_phys((void*)shared_page) >> PAGE_SHIFT; - get_page(pfn_to_page(pfn)); + *page = pfn_to_page(pfn); + get_page(*page); if (writable) *writable = true; return pfn; } - return gfn_to_pfn_prot(vcpu->kvm, gfn, writing, writable); + return kvm_faultin_pfn(vcpu, gfn, writing, writable, page); } EXPORT_SYMBOL_GPL(kvmppc_gpa_to_pfn); diff --git a/arch/powerpc/kvm/book3s_32_mmu_host.c b/arch/powerpc/kvm/book3s_32_mmu_host.c index 4b3a8d80cfa35..5b7212edbb131 100644 --- a/arch/powerpc/kvm/book3s_32_mmu_host.c +++ b/arch/powerpc/kvm/book3s_32_mmu_host.c @@ -130,6 +130,7 @@ extern char etext[]; int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, bool iswrite) { + struct page *page; kvm_pfn_t hpaddr; u64 vpn; u64 vsid; @@ -145,7 +146,7 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, bool writable; /* Get host physical address for gpa */ - hpaddr = kvmppc_gpa_to_pfn(vcpu, orig_pte->raddr, iswrite, &writable); + hpaddr = kvmppc_gpa_to_pfn(vcpu, orig_pte->raddr, iswrite, &writable, &page); if (is_error_noslot_pfn(hpaddr)) { printk(KERN_INFO "Couldn't get guest page for gpa %lx!\n", orig_pte->raddr); @@ -232,7 +233,7 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, pte = kvmppc_mmu_hpte_cache_next(vcpu); if (!pte) { - kvm_release_pfn_clean(hpaddr >> PAGE_SHIFT); + kvm_release_page_unused(page); r = -EAGAIN; goto out; } @@ -250,7 +251,7 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, kvmppc_mmu_hpte_cache_map(vcpu, pte); - kvm_release_pfn_clean(hpaddr >> PAGE_SHIFT); + kvm_release_page_clean(page); out: return r; } diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c index bc6a381b53463..be20aee6fd7d0 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_host.c +++ b/arch/powerpc/kvm/book3s_64_mmu_host.c @@ -88,13 +88,14 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, struct hpte_cache *cpte; unsigned long gfn = orig_pte->raddr >> PAGE_SHIFT; unsigned long pfn; + struct page *page; /* used to check for invalidations in progress */ mmu_seq = kvm->mmu_invalidate_seq; smp_rmb(); /* Get host physical address for gpa */ - pfn = kvmppc_gpa_to_pfn(vcpu, orig_pte->raddr, iswrite, &writable); + pfn = kvmppc_gpa_to_pfn(vcpu, orig_pte->raddr, iswrite, &writable, &page); if (is_error_noslot_pfn(pfn)) { printk(KERN_INFO "Couldn't get guest page for gpa %lx!\n", orig_pte->raddr); @@ -121,13 +122,10 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, vpn = hpt_vpn(orig_pte->eaddr, map->host_vsid, MMU_SEGSIZE_256M); - kvm_set_pfn_accessed(pfn); if (!orig_pte->may_write || !writable) rflags |= PP_RXRX; - else { + else mark_page_dirty(vcpu->kvm, gfn); - kvm_set_pfn_dirty(pfn); - } if (!orig_pte->may_execute) rflags |= HPTE_R_N; @@ -202,8 +200,10 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, } out_unlock: + /* FIXME: Don't unconditionally pass unused=false. */ + kvm_release_faultin_page(kvm, page, false, + orig_pte->may_write && writable); spin_unlock(&kvm->mmu_lock); - kvm_release_pfn_clean(pfn); if (cpte) kvmppc_mmu_hpte_cache_free(cpte); diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 1b51b1c4713bf..f305395cf26e7 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -603,27 +603,10 @@ int kvmppc_book3s_hv_page_fault(struct kvm_vcpu *vcpu, write_ok = writing; hva = gfn_to_hva_memslot(memslot, gfn); - /* - * Do a fast check first, since __gfn_to_pfn_memslot doesn't - * do it with !atomic && !async, which is how we call it. - * We always ask for write permission since the common case - * is that the page is writable. - */ - if (get_user_page_fast_only(hva, FOLL_WRITE, &page)) { - write_ok = true; - } else { - /* Call KVM generic code to do the slow-path check */ - pfn = __gfn_to_pfn_memslot(memslot, gfn, false, false, NULL, - writing, &write_ok, NULL); - if (is_error_noslot_pfn(pfn)) - return -EFAULT; - page = NULL; - if (pfn_valid(pfn)) { - page = pfn_to_page(pfn); - if (PageReserved(page)) - page = NULL; - } - } + pfn = __kvm_faultin_pfn(memslot, gfn, writing ? FOLL_WRITE : 0, + &write_ok, &page); + if (is_error_noslot_pfn(pfn)) + return -EFAULT; /* * Read the PTE from the process' radix tree and use that diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c index 408d98f8a5147..b3e6e73d6a088 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_radix.c +++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c @@ -821,7 +821,7 @@ bool kvmppc_hv_handle_set_rc(struct kvm *kvm, bool nested, bool writing, int kvmppc_book3s_instantiate_page(struct kvm_vcpu *vcpu, unsigned long gpa, struct kvm_memory_slot *memslot, - bool writing, bool kvm_ro, + bool writing, pte_t *inserted_pte, unsigned int *levelp) { struct kvm *kvm = vcpu->kvm; @@ -829,40 +829,21 @@ int kvmppc_book3s_instantiate_page(struct kvm_vcpu *vcpu, unsigned long mmu_seq; unsigned long hva, gfn = gpa >> PAGE_SHIFT; bool upgrade_write = false; - bool *upgrade_p = &upgrade_write; pte_t pte, *ptep; unsigned int shift, level; int ret; bool large_enable; + kvm_pfn_t pfn; /* used to check for invalidations in progress */ mmu_seq = kvm->mmu_invalidate_seq; smp_rmb(); - /* - * Do a fast check first, since __gfn_to_pfn_memslot doesn't - * do it with !atomic && !async, which is how we call it. - * We always ask for write permission since the common case - * is that the page is writable. - */ hva = gfn_to_hva_memslot(memslot, gfn); - if (!kvm_ro && get_user_page_fast_only(hva, FOLL_WRITE, &page)) { - upgrade_write = true; - } else { - unsigned long pfn; - - /* Call KVM generic code to do the slow-path check */ - pfn = __gfn_to_pfn_memslot(memslot, gfn, false, false, NULL, - writing, upgrade_p, NULL); - if (is_error_noslot_pfn(pfn)) - return -EFAULT; - page = NULL; - if (pfn_valid(pfn)) { - page = pfn_to_page(pfn); - if (PageReserved(page)) - page = NULL; - } - } + pfn = __kvm_faultin_pfn(memslot, gfn, writing ? FOLL_WRITE : 0, + &upgrade_write, &page); + if (is_error_noslot_pfn(pfn)) + return -EFAULT; /* * Read the PTE from the process' radix tree and use that @@ -950,7 +931,6 @@ int kvmppc_book3s_radix_page_fault(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot; long ret; bool writing = !!(dsisr & DSISR_ISSTORE); - bool kvm_ro = false; /* Check for unusual errors */ if (dsisr & DSISR_UNSUPP_MMU) { @@ -1003,7 +983,6 @@ int kvmppc_book3s_radix_page_fault(struct kvm_vcpu *vcpu, ea, DSISR_ISSTORE | DSISR_PROTFAULT); return RESUME_GUEST; } - kvm_ro = true; } /* Failed to set the reference/change bits */ @@ -1021,7 +1000,7 @@ int kvmppc_book3s_radix_page_fault(struct kvm_vcpu *vcpu, /* Try to insert a pte */ ret = kvmppc_book3s_instantiate_page(vcpu, gpa, memslot, writing, - kvm_ro, NULL, NULL); + NULL, NULL); if (ret == 0 || ret == -EAGAIN) ret = RESUME_GUEST; diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3s_hv_nested.c index ef97f58d0d973..5f8c2321cfb52 100644 --- a/arch/powerpc/kvm/book3s_hv_nested.c +++ b/arch/powerpc/kvm/book3s_hv_nested.c @@ -1535,7 +1535,6 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu, unsigned long n_gpa, gpa, gfn, perm = 0UL; unsigned int shift, l1_shift, level; bool writing = !!(dsisr & DSISR_ISSTORE); - bool kvm_ro = false; long int ret; if (!gp->l1_gr_to_hr) { @@ -1615,7 +1614,6 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu, ea, DSISR_ISSTORE | DSISR_PROTFAULT); return RESUME_GUEST; } - kvm_ro = true; } /* 2. Find the host pte for this L1 guest real address */ @@ -1637,7 +1635,7 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu, if (!pte_present(pte) || (writing && !(pte_val(pte) & _PAGE_WRITE))) { /* No suitable pte found -> try to insert a mapping */ ret = kvmppc_book3s_instantiate_page(vcpu, gpa, memslot, - writing, kvm_ro, &pte, &level); + writing, &pte, &level); if (ret == -EAGAIN) return RESUME_GUEST; else if (ret) diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c index 92f33115144b2..3a6592a31a10a 100644 --- a/arch/powerpc/kvm/book3s_hv_uvmem.c +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c @@ -879,9 +879,8 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, unsigned long gpa, { int ret = H_PARAMETER; - struct page *uvmem_page; + struct page *page, *uvmem_page; struct kvmppc_uvmem_page_pvt *pvt; - unsigned long pfn; unsigned long gfn = gpa >> page_shift; int srcu_idx; unsigned long uvmem_pfn; @@ -901,8 +900,8 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, unsigned long gpa, retry: mutex_unlock(&kvm->arch.uvmem_lock); - pfn = gfn_to_pfn(kvm, gfn); - if (is_error_noslot_pfn(pfn)) + page = gfn_to_page(kvm, gfn); + if (!page) goto out; mutex_lock(&kvm->arch.uvmem_lock); @@ -911,16 +910,16 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, unsigned long gpa, pvt = uvmem_page->zone_device_data; pvt->skip_page_out = true; pvt->remove_gfn = false; /* it continues to be a valid GFN */ - kvm_release_pfn_clean(pfn); + kvm_release_page_unused(page); goto retry; } - if (!uv_page_in(kvm->arch.lpid, pfn << page_shift, gpa, 0, + if (!uv_page_in(kvm->arch.lpid, page_to_pfn(page) << page_shift, gpa, 0, page_shift)) { kvmppc_gfn_shared(gfn, kvm); ret = H_SUCCESS; } - kvm_release_pfn_clean(pfn); + kvm_release_page_clean(page); mutex_unlock(&kvm->arch.uvmem_lock); out: srcu_read_unlock(&kvm->srcu, srcu_idx); @@ -1083,21 +1082,21 @@ kvmppc_h_svm_page_out(struct kvm *kvm, unsigned long gpa, int kvmppc_send_page_to_uv(struct kvm *kvm, unsigned long gfn) { - unsigned long pfn; + struct page *page; int ret = U_SUCCESS; - pfn = gfn_to_pfn(kvm, gfn); - if (is_error_noslot_pfn(pfn)) + page = gfn_to_page(kvm, gfn); + if (!page) return -EFAULT; mutex_lock(&kvm->arch.uvmem_lock); if (kvmppc_gfn_is_uvmem_pfn(gfn, kvm, NULL)) goto out; - ret = uv_page_in(kvm->arch.lpid, pfn << PAGE_SHIFT, gfn << PAGE_SHIFT, - 0, PAGE_SHIFT); + ret = uv_page_in(kvm->arch.lpid, page_to_pfn(page) << PAGE_SHIFT, + gfn << PAGE_SHIFT, 0, PAGE_SHIFT); out: - kvm_release_pfn_clean(pfn); + kvm_release_page_clean(page); mutex_unlock(&kvm->arch.uvmem_lock); return (ret == U_SUCCESS) ? RESUME_GUEST : -EFAULT; } diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 7b8ae509328fe..83bcdc80ce51a 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -639,29 +639,27 @@ static void kvmppc_set_pvr_pr(struct kvm_vcpu *vcpu, u32 pvr) */ static void kvmppc_patch_dcbz(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte) { - struct page *hpage; + struct kvm_host_map map; u64 hpage_offset; u32 *page; - int i; + int i, r; - hpage = gfn_to_page(vcpu->kvm, pte->raddr >> PAGE_SHIFT); - if (is_error_page(hpage)) + r = kvm_vcpu_map(vcpu, pte->raddr >> PAGE_SHIFT, &map); + if (r) return; hpage_offset = pte->raddr & ~PAGE_MASK; hpage_offset &= ~0xFFFULL; hpage_offset /= 4; - get_page(hpage); - page = kmap_atomic(hpage); + page = map.hva; /* patch dcbz into reserved instruction, so we trap */ for (i=hpage_offset; i < hpage_offset + (HW_PAGE_SIZE / 4); i++) if ((be32_to_cpu(page[i]) & 0xff0007ff) == INS_DCBZ) page[i] &= cpu_to_be32(0xfffffff7); - kunmap_atomic(page); - put_page(hpage); + kvm_vcpu_unmap(vcpu, &map); } static bool kvmppc_visible_gpa(struct kvm_vcpu *vcpu, gpa_t gpa) diff --git a/arch/powerpc/kvm/book3s_xive_native.c b/arch/powerpc/kvm/book3s_xive_native.c index 6e2ebbd8aaace..d9bf1bc3ff610 100644 --- a/arch/powerpc/kvm/book3s_xive_native.c +++ b/arch/powerpc/kvm/book3s_xive_native.c @@ -654,7 +654,7 @@ static int kvmppc_xive_native_set_queue_config(struct kvmppc_xive *xive, } page = gfn_to_page(kvm, gfn); - if (is_error_page(page)) { + if (!page) { srcu_read_unlock(&kvm->srcu, srcu_idx); pr_err("Couldn't get queue page %llx!\n", kvm_eq.qaddr); return -EINVAL; diff --git a/arch/powerpc/kvm/e500_mmu_host.c b/arch/powerpc/kvm/e500_mmu_host.c index c664fdec75b1a..e5a145b578a47 100644 --- a/arch/powerpc/kvm/e500_mmu_host.c +++ b/arch/powerpc/kvm/e500_mmu_host.c @@ -242,7 +242,7 @@ static inline int tlbe_is_writable(struct kvm_book3e_206_tlb_entry *tlbe) return tlbe->mas7_3 & (MAS3_SW|MAS3_UW); } -static inline void kvmppc_e500_ref_setup(struct tlbe_ref *ref, +static inline bool kvmppc_e500_ref_setup(struct tlbe_ref *ref, struct kvm_book3e_206_tlb_entry *gtlbe, kvm_pfn_t pfn, unsigned int wimg) { @@ -252,11 +252,7 @@ static inline void kvmppc_e500_ref_setup(struct tlbe_ref *ref, /* Use guest supplied MAS2_G and MAS2_E */ ref->flags |= (gtlbe->mas2 & MAS2_ATTRIB_MASK) | wimg; - /* Mark the page accessed */ - kvm_set_pfn_accessed(pfn); - - if (tlbe_is_writable(gtlbe)) - kvm_set_pfn_dirty(pfn); + return tlbe_is_writable(gtlbe); } static inline void kvmppc_e500_ref_release(struct tlbe_ref *ref) @@ -326,6 +322,7 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, { struct kvm_memory_slot *slot; unsigned long pfn = 0; /* silence GCC warning */ + struct page *page = NULL; unsigned long hva; int pfnmap = 0; int tsize = BOOK3E_PAGESZ_4K; @@ -337,6 +334,7 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, unsigned int wimg = 0; pgd_t *pgdir; unsigned long flags; + bool writable = false; /* used to check for invalidations in progress */ mmu_seq = kvm->mmu_invalidate_seq; @@ -446,7 +444,7 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, if (likely(!pfnmap)) { tsize_pages = 1UL << (tsize + 10 - PAGE_SHIFT); - pfn = gfn_to_pfn_memslot(slot, gfn); + pfn = __kvm_faultin_pfn(slot, gfn, FOLL_WRITE, NULL, &page); if (is_error_noslot_pfn(pfn)) { if (printk_ratelimit()) pr_err("%s: real page not found for gfn %lx\n", @@ -490,7 +488,7 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, goto out; } } - kvmppc_e500_ref_setup(ref, gtlbe, pfn, wimg); + writable = kvmppc_e500_ref_setup(ref, gtlbe, pfn, wimg); kvmppc_e500_setup_stlbe(&vcpu_e500->vcpu, gtlbe, tsize, ref, gvaddr, stlbe); @@ -499,11 +497,8 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, kvmppc_mmu_flush_icache(pfn); out: + kvm_release_faultin_page(kvm, page, !!ret, writable); spin_unlock(&kvm->mmu_lock); - - /* Drop refcount on page, so that mmu notifiers can clear it */ - kvm_release_pfn_clean(pfn); - return ret; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index b3b37ea778496..ce1d91eed231b 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -612,9 +612,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = 8 | 4 | 2 | 1; } break; - case KVM_CAP_PPC_RMA: - r = 0; - break; case KVM_CAP_PPC_HWRNG: r = kvmppc_hwrng_present(); break; diff --git a/arch/powerpc/platforms/pseries/svm.c b/arch/powerpc/platforms/pseries/svm.c index c5d0f92c79699..384c9dc1899ab 100644 --- a/arch/powerpc/platforms/pseries/svm.c +++ b/arch/powerpc/platforms/pseries/svm.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index ff1e353b0d6fb..d4a7ca0388c07 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -32,6 +32,7 @@ config RISCV select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GIGANTIC_PAGE + select ARCH_HAS_HW_PTE_YOUNG select ARCH_HAS_KCOV select ARCH_HAS_KERNEL_FPU_SUPPORT if 64BIT && FPU select ARCH_HAS_MEMBARRIER_CALLBACKS @@ -83,6 +84,7 @@ config RISCV select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP select ARCH_WANTS_NO_INSTR select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE + select ARCH_WEAK_RELEASE_ACQUIRE if ARCH_USE_QUEUED_SPINLOCKS select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU select BUILDTIME_TABLE_SORT if MMU select CLINT_TIMER if RISCV_M_MODE @@ -116,6 +118,7 @@ config RISCV select GENERIC_VDSO_TIME_NS if HAVE_GENERIC_VDSO select HARDIRQS_SW_RESEND select HAS_IOPORT if MMU + select HAVE_ALIGNED_STRUCT_PAGE select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_HUGE_VMALLOC if HAVE_ARCH_HUGE_VMAP select HAVE_ARCH_HUGE_VMAP if MMU && 64BIT @@ -507,6 +510,39 @@ config NODES_SHIFT Specify the maximum number of NUMA Nodes available on the target system. Increases memory reserved to accommodate various tables. +choice + prompt "RISC-V spinlock type" + default RISCV_COMBO_SPINLOCKS + +config RISCV_TICKET_SPINLOCKS + bool "Using ticket spinlock" + +config RISCV_QUEUED_SPINLOCKS + bool "Using queued spinlock" + depends on SMP && MMU && NONPORTABLE + select ARCH_USE_QUEUED_SPINLOCKS + help + The queued spinlock implementation requires the forward progress + guarantee of cmpxchg()/xchg() atomic operations: CAS with Zabha or + LR/SC with Ziccrse provide such guarantee. + + Select this if and only if Zabha or Ziccrse is available on your + platform, RISCV_QUEUED_SPINLOCKS must not be selected for platforms + without one of those extensions. + + If unsure, select RISCV_COMBO_SPINLOCKS, which will use qspinlocks + when supported and otherwise ticket spinlocks. + +config RISCV_COMBO_SPINLOCKS + bool "Using combo spinlock" + depends on SMP && MMU + select ARCH_USE_QUEUED_SPINLOCKS + help + Embed both queued spinlock and ticket lock so that the spinlock + implementation can be chosen at runtime. + +endchoice + config RISCV_ALTERNATIVE bool depends on !XIP_KERNEL @@ -532,6 +568,17 @@ config RISCV_ISA_C If you don't know what to do here, say Y. +config RISCV_ISA_SUPM + bool "Supm extension for userspace pointer masking" + depends on 64BIT + default y + help + Add support for pointer masking in userspace (Supm) when the + underlying hardware extension (Smnpm or Ssnpm) is detected at boot. + + If this option is disabled, userspace will be unable to use + the prctl(PR_{SET,GET}_TAGGED_ADDR_CTRL) API. + config RISCV_ISA_SVNAPOT bool "Svnapot extension support for supervisor mode NAPOT pages" depends on 64BIT && MMU @@ -633,6 +680,40 @@ config RISCV_ISA_ZAWRS use of these instructions in the kernel when the Zawrs extension is detected at boot. +config TOOLCHAIN_HAS_ZABHA + bool + default y + depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zabha) + depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zabha) + depends on AS_HAS_OPTION_ARCH + +config RISCV_ISA_ZABHA + bool "Zabha extension support for atomic byte/halfword operations" + depends on TOOLCHAIN_HAS_ZABHA + depends on RISCV_ALTERNATIVE + default y + help + Enable the use of the Zabha ISA-extension to implement kernel + byte/halfword atomic memory operations when it is detected at boot. + + If you don't know what to do here, say Y. + +config TOOLCHAIN_HAS_ZACAS + bool + default y + depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zacas) + depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zacas) + depends on AS_HAS_OPTION_ARCH + +config RISCV_ISA_ZACAS + bool "Zacas extension support for atomic CAS" + depends on TOOLCHAIN_HAS_ZACAS + depends on RISCV_ALTERNATIVE + default y + help + Enable the use of the Zacas ISA-extension to implement kernel atomic + cmpxchg operations when it is detected at boot. + If you don't know what to do here, say Y. config TOOLCHAIN_HAS_ZBB @@ -786,10 +867,24 @@ config THREAD_SIZE_ORDER config RISCV_MISALIGNED bool + help + Embed support for detecting and emulating misaligned + scalar or vector loads and stores. + +config RISCV_SCALAR_MISALIGNED + bool + select RISCV_MISALIGNED select SYSCTL_ARCH_UNALIGN_ALLOW help Embed support for emulating misaligned loads and stores. +config RISCV_VECTOR_MISALIGNED + bool + select RISCV_MISALIGNED + depends on RISCV_ISA_V + help + Enable detecting support for vector misaligned loads and stores. + choice prompt "Unaligned Accesses Support" default RISCV_PROBE_UNALIGNED_ACCESS @@ -801,7 +896,7 @@ choice config RISCV_PROBE_UNALIGNED_ACCESS bool "Probe for hardware unaligned access support" - select RISCV_MISALIGNED + select RISCV_SCALAR_MISALIGNED help During boot, the kernel will run a series of tests to determine the speed of unaligned accesses. This probing will dynamically determine @@ -812,7 +907,7 @@ config RISCV_PROBE_UNALIGNED_ACCESS config RISCV_EMULATED_UNALIGNED_ACCESS bool "Emulate unaligned access where system support is missing" - select RISCV_MISALIGNED + select RISCV_SCALAR_MISALIGNED help If unaligned memory accesses trap into the kernel as they are not supported by the system, the kernel will emulate the unaligned @@ -841,6 +936,46 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS endchoice +choice + prompt "Vector unaligned Accesses Support" + depends on RISCV_ISA_V + default RISCV_PROBE_VECTOR_UNALIGNED_ACCESS + help + This determines the level of support for vector unaligned accesses. This + information is used by the kernel to perform optimizations. It is also + exposed to user space via the hwprobe syscall. The hardware will be + probed at boot by default. + +config RISCV_PROBE_VECTOR_UNALIGNED_ACCESS + bool "Probe speed of vector unaligned accesses" + select RISCV_VECTOR_MISALIGNED + depends on RISCV_ISA_V + help + During boot, the kernel will run a series of tests to determine the + speed of vector unaligned accesses if they are supported. This probing + will dynamically determine the speed of vector unaligned accesses on + the underlying system if they are supported. + +config RISCV_SLOW_VECTOR_UNALIGNED_ACCESS + bool "Assume the system supports slow vector unaligned memory accesses" + depends on NONPORTABLE + help + Assume that the system supports slow vector unaligned memory accesses. The + kernel and userspace programs may not be able to run at all on systems + that do not support unaligned memory accesses. + +config RISCV_EFFICIENT_VECTOR_UNALIGNED_ACCESS + bool "Assume the system supports fast vector unaligned memory accesses" + depends on NONPORTABLE + help + Assume that the system supports fast vector unaligned memory accesses. When + enabled, this option improves the performance of the kernel on such + systems. However, the kernel and userspace programs will run much more + slowly, or will not be able to run at all, on systems that do not + support efficient unaligned memory accesses. + +endchoice + source "arch/riscv/Kconfig.vendor" endmenu # "Platform type" diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index d469db9f46f42..13fbc0f942387 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -82,6 +82,12 @@ else riscv-march-$(CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI) := $(riscv-march-y)_zicsr_zifencei endif +# Check if the toolchain supports Zacas +riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZACAS) := $(riscv-march-y)_zacas + +# Check if the toolchain supports Zabha +riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZABHA) := $(riscv-march-y)_zabha + # Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by # matching non-v and non-multi-letter extensions out with the filter ([^v_]*) KBUILD_CFLAGS += -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/') @@ -129,7 +135,7 @@ stack_protector_prepare: prepare0 -mstack-protector-guard-reg=tp \ -mstack-protector-guard-offset=$(shell \ awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}' \ - include/generated/asm-offsets.h)) + $(objtree)/include/generated/asm-offsets.h)) endif # arch specific predefines for sparse diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index 1d5e13b148f21..b4a37345703eb 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -167,6 +167,7 @@ CONFIG_PINCTRL_SOPHGO_CV1800B=y CONFIG_PINCTRL_SOPHGO_CV1812H=y CONFIG_PINCTRL_SOPHGO_SG2000=y CONFIG_PINCTRL_SOPHGO_SG2002=y +CONFIG_GPIO_DWAPB=y CONFIG_GPIO_SIFIVE=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_SENSORS_SFCTEMP=m diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild index 1461af12da6e2..de13d5a234f88 100644 --- a/arch/riscv/include/asm/Kbuild +++ b/arch/riscv/include/asm/Kbuild @@ -6,10 +6,12 @@ generic-y += early_ioremap.h generic-y += flat.h generic-y += kvm_para.h generic-y += mmzone.h +generic-y += mcs_spinlock.h generic-y += parport.h -generic-y += spinlock.h generic-y += spinlock_types.h +generic-y += ticket_spinlock.h generic-y += qrwlock.h generic-y += qrwlock_types.h +generic-y += qspinlock.h generic-y += user.h generic-y += vmlinux.lds.h diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h index ebbce134917cc..4cadc56220fea 100644 --- a/arch/riscv/include/asm/cmpxchg.h +++ b/arch/riscv/include/asm/cmpxchg.h @@ -12,30 +12,43 @@ #include #include #include - -#define __arch_xchg_masked(sc_sfx, prepend, append, r, p, n) \ -({ \ - u32 *__ptr32b = (u32 *)((ulong)(p) & ~0x3); \ - ulong __s = ((ulong)(p) & (0x4 - sizeof(*p))) * BITS_PER_BYTE; \ - ulong __mask = GENMASK(((sizeof(*p)) * BITS_PER_BYTE) - 1, 0) \ - << __s; \ - ulong __newx = (ulong)(n) << __s; \ - ulong __retx; \ - ulong __rc; \ - \ - __asm__ __volatile__ ( \ - prepend \ - "0: lr.w %0, %2\n" \ - " and %1, %0, %z4\n" \ - " or %1, %1, %z3\n" \ - " sc.w" sc_sfx " %1, %1, %2\n" \ - " bnez %1, 0b\n" \ - append \ - : "=&r" (__retx), "=&r" (__rc), "+A" (*(__ptr32b)) \ - : "rJ" (__newx), "rJ" (~__mask) \ - : "memory"); \ - \ - r = (__typeof__(*(p)))((__retx & __mask) >> __s); \ +#include + +#define __arch_xchg_masked(sc_sfx, swap_sfx, prepend, sc_append, \ + swap_append, r, p, n) \ +({ \ + if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) && \ + riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) { \ + __asm__ __volatile__ ( \ + prepend \ + " amoswap" swap_sfx " %0, %z2, %1\n" \ + swap_append \ + : "=&r" (r), "+A" (*(p)) \ + : "rJ" (n) \ + : "memory"); \ + } else { \ + u32 *__ptr32b = (u32 *)((ulong)(p) & ~0x3); \ + ulong __s = ((ulong)(p) & (0x4 - sizeof(*p))) * BITS_PER_BYTE; \ + ulong __mask = GENMASK(((sizeof(*p)) * BITS_PER_BYTE) - 1, 0) \ + << __s; \ + ulong __newx = (ulong)(n) << __s; \ + ulong __retx; \ + ulong __rc; \ + \ + __asm__ __volatile__ ( \ + prepend \ + "0: lr.w %0, %2\n" \ + " and %1, %0, %z4\n" \ + " or %1, %1, %z3\n" \ + " sc.w" sc_sfx " %1, %1, %2\n" \ + " bnez %1, 0b\n" \ + sc_append \ + : "=&r" (__retx), "=&r" (__rc), "+A" (*(__ptr32b)) \ + : "rJ" (__newx), "rJ" (~__mask) \ + : "memory"); \ + \ + r = (__typeof__(*(p)))((__retx & __mask) >> __s); \ + } \ }) #define __arch_xchg(sfx, prepend, append, r, p, n) \ @@ -58,8 +71,13 @@ \ switch (sizeof(*__ptr)) { \ case 1: \ + __arch_xchg_masked(sc_sfx, ".b" swap_sfx, \ + prepend, sc_append, swap_append, \ + __ret, __ptr, __new); \ + break; \ case 2: \ - __arch_xchg_masked(sc_sfx, prepend, sc_append, \ + __arch_xchg_masked(sc_sfx, ".h" swap_sfx, \ + prepend, sc_append, swap_append, \ __ret, __ptr, __new); \ break; \ case 4: \ @@ -106,55 +124,90 @@ * store NEW in MEM. Return the initial value in MEM. Success is * indicated by comparing RETURN with OLD. */ - -#define __arch_cmpxchg_masked(sc_sfx, prepend, append, r, p, o, n) \ -({ \ - u32 *__ptr32b = (u32 *)((ulong)(p) & ~0x3); \ - ulong __s = ((ulong)(p) & (0x4 - sizeof(*p))) * BITS_PER_BYTE; \ - ulong __mask = GENMASK(((sizeof(*p)) * BITS_PER_BYTE) - 1, 0) \ - << __s; \ - ulong __newx = (ulong)(n) << __s; \ - ulong __oldx = (ulong)(o) << __s; \ - ulong __retx; \ - ulong __rc; \ - \ - __asm__ __volatile__ ( \ - prepend \ - "0: lr.w %0, %2\n" \ - " and %1, %0, %z5\n" \ - " bne %1, %z3, 1f\n" \ - " and %1, %0, %z6\n" \ - " or %1, %1, %z4\n" \ - " sc.w" sc_sfx " %1, %1, %2\n" \ - " bnez %1, 0b\n" \ - append \ - "1:\n" \ - : "=&r" (__retx), "=&r" (__rc), "+A" (*(__ptr32b)) \ - : "rJ" ((long)__oldx), "rJ" (__newx), \ - "rJ" (__mask), "rJ" (~__mask) \ - : "memory"); \ - \ - r = (__typeof__(*(p)))((__retx & __mask) >> __s); \ +#define __arch_cmpxchg_masked(sc_sfx, cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append, \ + r, p, o, n) \ +({ \ + if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) && \ + IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) && \ + riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA) && \ + riscv_has_extension_unlikely(RISCV_ISA_EXT_ZACAS)) { \ + r = o; \ + \ + __asm__ __volatile__ ( \ + cas_prepend \ + " amocas" cas_sfx " %0, %z2, %1\n" \ + cas_append \ + : "+&r" (r), "+A" (*(p)) \ + : "rJ" (n) \ + : "memory"); \ + } else { \ + u32 *__ptr32b = (u32 *)((ulong)(p) & ~0x3); \ + ulong __s = ((ulong)(p) & (0x4 - sizeof(*p))) * BITS_PER_BYTE; \ + ulong __mask = GENMASK(((sizeof(*p)) * BITS_PER_BYTE) - 1, 0) \ + << __s; \ + ulong __newx = (ulong)(n) << __s; \ + ulong __oldx = (ulong)(o) << __s; \ + ulong __retx; \ + ulong __rc; \ + \ + __asm__ __volatile__ ( \ + sc_prepend \ + "0: lr.w %0, %2\n" \ + " and %1, %0, %z5\n" \ + " bne %1, %z3, 1f\n" \ + " and %1, %0, %z6\n" \ + " or %1, %1, %z4\n" \ + " sc.w" sc_sfx " %1, %1, %2\n" \ + " bnez %1, 0b\n" \ + sc_append \ + "1:\n" \ + : "=&r" (__retx), "=&r" (__rc), "+A" (*(__ptr32b)) \ + : "rJ" ((long)__oldx), "rJ" (__newx), \ + "rJ" (__mask), "rJ" (~__mask) \ + : "memory"); \ + \ + r = (__typeof__(*(p)))((__retx & __mask) >> __s); \ + } \ }) -#define __arch_cmpxchg(lr_sfx, sc_sfx, prepend, append, r, p, co, o, n) \ +#define __arch_cmpxchg(lr_sfx, sc_sfx, cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append, \ + r, p, co, o, n) \ ({ \ - register unsigned int __rc; \ + if (IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) && \ + riscv_has_extension_unlikely(RISCV_ISA_EXT_ZACAS)) { \ + r = o; \ \ - __asm__ __volatile__ ( \ - prepend \ - "0: lr" lr_sfx " %0, %2\n" \ - " bne %0, %z3, 1f\n" \ - " sc" sc_sfx " %1, %z4, %2\n" \ - " bnez %1, 0b\n" \ - append \ - "1:\n" \ - : "=&r" (r), "=&r" (__rc), "+A" (*(p)) \ - : "rJ" (co o), "rJ" (n) \ - : "memory"); \ + __asm__ __volatile__ ( \ + cas_prepend \ + " amocas" cas_sfx " %0, %z2, %1\n" \ + cas_append \ + : "+&r" (r), "+A" (*(p)) \ + : "rJ" (n) \ + : "memory"); \ + } else { \ + register unsigned int __rc; \ + \ + __asm__ __volatile__ ( \ + sc_prepend \ + "0: lr" lr_sfx " %0, %2\n" \ + " bne %0, %z3, 1f\n" \ + " sc" sc_sfx " %1, %z4, %2\n" \ + " bnez %1, 0b\n" \ + sc_append \ + "1:\n" \ + : "=&r" (r), "=&r" (__rc), "+A" (*(p)) \ + : "rJ" (co o), "rJ" (n) \ + : "memory"); \ + } \ }) -#define _arch_cmpxchg(ptr, old, new, sc_sfx, prepend, append) \ +#define _arch_cmpxchg(ptr, old, new, sc_sfx, cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append) \ ({ \ __typeof__(ptr) __ptr = (ptr); \ __typeof__(*(__ptr)) __old = (old); \ @@ -163,17 +216,28 @@ \ switch (sizeof(*__ptr)) { \ case 1: \ + __arch_cmpxchg_masked(sc_sfx, ".b" cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append, \ + __ret, __ptr, __old, __new); \ + break; \ case 2: \ - __arch_cmpxchg_masked(sc_sfx, prepend, append, \ - __ret, __ptr, __old, __new); \ + __arch_cmpxchg_masked(sc_sfx, ".h" cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append, \ + __ret, __ptr, __old, __new); \ break; \ case 4: \ - __arch_cmpxchg(".w", ".w" sc_sfx, prepend, append, \ - __ret, __ptr, (long), __old, __new); \ + __arch_cmpxchg(".w", ".w" sc_sfx, ".w" cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append, \ + __ret, __ptr, (long), __old, __new); \ break; \ case 8: \ - __arch_cmpxchg(".d", ".d" sc_sfx, prepend, append, \ - __ret, __ptr, /**/, __old, __new); \ + __arch_cmpxchg(".d", ".d" sc_sfx, ".d" cas_sfx, \ + sc_prepend, sc_append, \ + cas_prepend, cas_append, \ + __ret, __ptr, /**/, __old, __new); \ break; \ default: \ BUILD_BUG(); \ @@ -181,17 +245,40 @@ (__typeof__(*(__ptr)))__ret; \ }) +/* + * These macros are here to improve the readability of the arch_cmpxchg_XXX() + * macros. + */ +#define SC_SFX(x) x +#define CAS_SFX(x) x +#define SC_PREPEND(x) x +#define SC_APPEND(x) x +#define CAS_PREPEND(x) x +#define CAS_APPEND(x) x + #define arch_cmpxchg_relaxed(ptr, o, n) \ - _arch_cmpxchg((ptr), (o), (n), "", "", "") + _arch_cmpxchg((ptr), (o), (n), \ + SC_SFX(""), CAS_SFX(""), \ + SC_PREPEND(""), SC_APPEND(""), \ + CAS_PREPEND(""), CAS_APPEND("")) #define arch_cmpxchg_acquire(ptr, o, n) \ - _arch_cmpxchg((ptr), (o), (n), "", "", RISCV_ACQUIRE_BARRIER) + _arch_cmpxchg((ptr), (o), (n), \ + SC_SFX(""), CAS_SFX(""), \ + SC_PREPEND(""), SC_APPEND(RISCV_ACQUIRE_BARRIER), \ + CAS_PREPEND(""), CAS_APPEND(RISCV_ACQUIRE_BARRIER)) #define arch_cmpxchg_release(ptr, o, n) \ - _arch_cmpxchg((ptr), (o), (n), "", RISCV_RELEASE_BARRIER, "") + _arch_cmpxchg((ptr), (o), (n), \ + SC_SFX(""), CAS_SFX(""), \ + SC_PREPEND(RISCV_RELEASE_BARRIER), SC_APPEND(""), \ + CAS_PREPEND(RISCV_RELEASE_BARRIER), CAS_APPEND("")) #define arch_cmpxchg(ptr, o, n) \ - _arch_cmpxchg((ptr), (o), (n), ".rl", "", " fence rw, rw\n") + _arch_cmpxchg((ptr), (o), (n), \ + SC_SFX(".rl"), CAS_SFX(".aqrl"), \ + SC_PREPEND(""), SC_APPEND(RISCV_FULL_BARRIER), \ + CAS_PREPEND(""), CAS_APPEND("")) #define arch_cmpxchg_local(ptr, o, n) \ arch_cmpxchg_relaxed((ptr), (o), (n)) @@ -226,6 +313,44 @@ arch_cmpxchg_release((ptr), (o), (n)); \ }) +#if defined(CONFIG_64BIT) && defined(CONFIG_RISCV_ISA_ZACAS) + +#define system_has_cmpxchg128() riscv_has_extension_unlikely(RISCV_ISA_EXT_ZACAS) + +union __u128_halves { + u128 full; + struct { + u64 low, high; + }; +}; + +#define __arch_cmpxchg128(p, o, n, cas_sfx) \ +({ \ + __typeof__(*(p)) __o = (o); \ + union __u128_halves __hn = { .full = (n) }; \ + union __u128_halves __ho = { .full = (__o) }; \ + register unsigned long t1 asm ("t1") = __hn.low; \ + register unsigned long t2 asm ("t2") = __hn.high; \ + register unsigned long t3 asm ("t3") = __ho.low; \ + register unsigned long t4 asm ("t4") = __ho.high; \ + \ + __asm__ __volatile__ ( \ + " amocas.q" cas_sfx " %0, %z3, %2" \ + : "+&r" (t3), "+&r" (t4), "+A" (*(p)) \ + : "rJ" (t1), "rJ" (t2) \ + : "memory"); \ + \ + ((u128)t4 << 64) | t3; \ +}) + +#define arch_cmpxchg128(ptr, o, n) \ + __arch_cmpxchg128((ptr), (o), (n), ".aqrl") + +#define arch_cmpxchg128_local(ptr, o, n) \ + __arch_cmpxchg128((ptr), (o), (n), "") + +#endif /* CONFIG_64BIT && CONFIG_RISCV_ISA_ZACAS */ + #ifdef CONFIG_RISCV_ISA_ZAWRS /* * Despite wrs.nto being "WRS-with-no-timeout", in the absence of changes to @@ -245,6 +370,11 @@ static __always_inline void __cmpwait(volatile void *ptr, : : : : no_zawrs); switch (size) { + case 1: + fallthrough; + case 2: + /* RISC-V doesn't have lr instructions on byte and half-word. */ + goto no_zawrs; case 4: asm volatile( " lr.w %0, %1\n" diff --git a/arch/riscv/include/asm/compat.h b/arch/riscv/include/asm/compat.h index aa103530a5c83..6081327e55f5b 100644 --- a/arch/riscv/include/asm/compat.h +++ b/arch/riscv/include/asm/compat.h @@ -9,7 +9,6 @@ */ #include #include -#include #include static inline int is_compat_task(void) diff --git a/arch/riscv/include/asm/cpufeature-macros.h b/arch/riscv/include/asm/cpufeature-macros.h new file mode 100644 index 0000000000000..a8103edbf51f8 --- /dev/null +++ b/arch/riscv/include/asm/cpufeature-macros.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2022-2024 Rivos, Inc + */ + +#ifndef _ASM_CPUFEATURE_MACROS_H +#define _ASM_CPUFEATURE_MACROS_H + +#include +#include + +#define STANDARD_EXT 0 + +bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, unsigned int bit); +#define riscv_isa_extension_available(isa_bitmap, ext) \ + __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_##ext) + +static __always_inline bool __riscv_has_extension_likely(const unsigned long vendor, + const unsigned long ext) +{ + asm goto(ALTERNATIVE("j %l[l_no]", "nop", %[vendor], %[ext], 1) + : + : [vendor] "i" (vendor), [ext] "i" (ext) + : + : l_no); + + return true; +l_no: + return false; +} + +static __always_inline bool __riscv_has_extension_unlikely(const unsigned long vendor, + const unsigned long ext) +{ + asm goto(ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1) + : + : [vendor] "i" (vendor), [ext] "i" (ext) + : + : l_yes); + + return false; +l_yes: + return true; +} + +static __always_inline bool riscv_has_extension_unlikely(const unsigned long ext) +{ + compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX"); + + if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) + return __riscv_has_extension_unlikely(STANDARD_EXT, ext); + + return __riscv_isa_extension_available(NULL, ext); +} + +static __always_inline bool riscv_has_extension_likely(const unsigned long ext) +{ + compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX"); + + if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) + return __riscv_has_extension_likely(STANDARD_EXT, ext); + + return __riscv_isa_extension_available(NULL, ext); +} + +#endif /* _ASM_CPUFEATURE_MACROS_H */ diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h index 45f9c1171a486..4bd054c54c21a 100644 --- a/arch/riscv/include/asm/cpufeature.h +++ b/arch/riscv/include/asm/cpufeature.h @@ -8,9 +8,12 @@ #include #include +#include +#include +#include +#include #include -#include -#include +#include /* * These are probed via a device_initcall(), via either the SBI or directly @@ -31,7 +34,7 @@ DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo); /* Per-cpu ISA extensions. */ extern struct riscv_isainfo hart_isa[NR_CPUS]; -void riscv_user_isa_enable(void); +void __init riscv_user_isa_enable(void); #define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size, _validate) { \ .name = #_name, \ @@ -58,8 +61,9 @@ void riscv_user_isa_enable(void); #define __RISCV_ISA_EXT_SUPERSET_VALIDATE(_name, _id, _sub_exts, _validate) \ _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts), _validate) -#if defined(CONFIG_RISCV_MISALIGNED) bool check_unaligned_access_emulated_all_cpus(void); +#if defined(CONFIG_RISCV_SCALAR_MISALIGNED) +void check_unaligned_access_emulated(struct work_struct *work __always_unused); void unaligned_emulation_finish(void); bool unaligned_ctl_available(void); DECLARE_PER_CPU(long, misaligned_access_speed); @@ -70,6 +74,12 @@ static inline bool unaligned_ctl_available(void) } #endif +bool check_vector_unaligned_access_emulated_all_cpus(void); +#if defined(CONFIG_RISCV_VECTOR_MISALIGNED) +void check_vector_unaligned_access_emulated(struct work_struct *work __always_unused); +DECLARE_PER_CPU(long, vector_misaligned_access); +#endif + #if defined(CONFIG_RISCV_PROBE_UNALIGNED_ACCESS) DECLARE_STATIC_KEY_FALSE(fast_unaligned_access_speed_key); @@ -103,61 +113,6 @@ extern const size_t riscv_isa_ext_count; extern bool riscv_isa_fallback; unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap); - -#define STANDARD_EXT 0 - -bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, unsigned int bit); -#define riscv_isa_extension_available(isa_bitmap, ext) \ - __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_##ext) - -static __always_inline bool __riscv_has_extension_likely(const unsigned long vendor, - const unsigned long ext) -{ - asm goto(ALTERNATIVE("j %l[l_no]", "nop", %[vendor], %[ext], 1) - : - : [vendor] "i" (vendor), [ext] "i" (ext) - : - : l_no); - - return true; -l_no: - return false; -} - -static __always_inline bool __riscv_has_extension_unlikely(const unsigned long vendor, - const unsigned long ext) -{ - asm goto(ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1) - : - : [vendor] "i" (vendor), [ext] "i" (ext) - : - : l_yes); - - return false; -l_yes: - return true; -} - -static __always_inline bool riscv_has_extension_unlikely(const unsigned long ext) -{ - compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX"); - - if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) - return __riscv_has_extension_unlikely(STANDARD_EXT, ext); - - return __riscv_isa_extension_available(NULL, ext); -} - -static __always_inline bool riscv_has_extension_likely(const unsigned long ext) -{ - compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX"); - - if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) - return __riscv_has_extension_likely(STANDARD_EXT, ext); - - return __riscv_isa_extension_available(NULL, ext); -} - static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext) { compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX"); diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 25966995da04e..37bdea65bbd8a 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -119,6 +119,10 @@ /* HSTATUS flags */ #ifdef CONFIG_64BIT +#define HSTATUS_HUPMM _AC(0x3000000000000, UL) +#define HSTATUS_HUPMM_PMLEN_0 _AC(0x0000000000000, UL) +#define HSTATUS_HUPMM_PMLEN_7 _AC(0x2000000000000, UL) +#define HSTATUS_HUPMM_PMLEN_16 _AC(0x3000000000000, UL) #define HSTATUS_VSXL _AC(0x300000000, UL) #define HSTATUS_VSXL_SHIFT 32 #endif @@ -195,6 +199,11 @@ /* xENVCFG flags */ #define ENVCFG_STCE (_AC(1, ULL) << 63) #define ENVCFG_PBMTE (_AC(1, ULL) << 62) +#define ENVCFG_ADUE (_AC(1, ULL) << 61) +#define ENVCFG_PMM (_AC(0x3, ULL) << 32) +#define ENVCFG_PMM_PMLEN_0 (_AC(0x0, ULL) << 32) +#define ENVCFG_PMM_PMLEN_7 (_AC(0x2, ULL) << 32) +#define ENVCFG_PMM_PMLEN_16 (_AC(0x3, ULL) << 32) #define ENVCFG_CBZE (_AC(1, UL) << 7) #define ENVCFG_CBCFE (_AC(1, UL) << 6) #define ENVCFG_CBIE_SHIFT 4 @@ -216,6 +225,12 @@ #define SMSTATEEN0_SSTATEEN0_SHIFT 63 #define SMSTATEEN0_SSTATEEN0 (_ULL(1) << SMSTATEEN0_SSTATEEN0_SHIFT) +/* mseccfg bits */ +#define MSECCFG_PMM ENVCFG_PMM +#define MSECCFG_PMM_PMLEN_0 ENVCFG_PMM_PMLEN_0 +#define MSECCFG_PMM_PMLEN_7 ENVCFG_PMM_PMLEN_7 +#define MSECCFG_PMM_PMLEN_16 ENVCFG_PMM_PMLEN_16 + /* symbolic CSR names: */ #define CSR_CYCLE 0xc00 #define CSR_TIME 0xc01 @@ -382,6 +397,8 @@ #define CSR_MIP 0x344 #define CSR_PMPCFG0 0x3a0 #define CSR_PMPADDR0 0x3b0 +#define CSR_MSECCFG 0x747 +#define CSR_MSECCFGH 0x757 #define CSR_MVENDORID 0xf11 #define CSR_MARCHID 0xf12 #define CSR_MIMPID 0xf13 diff --git a/arch/riscv/include/asm/entry-common.h b/arch/riscv/include/asm/entry-common.h index 2293e535f8659..b28ccc6cdeea4 100644 --- a/arch/riscv/include/asm/entry-common.h +++ b/arch/riscv/include/asm/entry-common.h @@ -33,6 +33,7 @@ static inline int handle_misaligned_load(struct pt_regs *regs) { return -1; } + static inline int handle_misaligned_store(struct pt_regs *regs) { return -1; diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index 46d9de54179ed..869da082252a4 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -93,6 +93,13 @@ #define RISCV_ISA_EXT_ZCMOP 84 #define RISCV_ISA_EXT_ZAWRS 85 #define RISCV_ISA_EXT_SVVPTC 86 +#define RISCV_ISA_EXT_SMMPM 87 +#define RISCV_ISA_EXT_SMNPM 88 +#define RISCV_ISA_EXT_SSNPM 89 +#define RISCV_ISA_EXT_ZABHA 90 +#define RISCV_ISA_EXT_ZICCRSE 91 +#define RISCV_ISA_EXT_SVADE 92 +#define RISCV_ISA_EXT_SVADU 93 #define RISCV_ISA_EXT_XLINUXENVCFG 127 @@ -101,8 +108,10 @@ #ifdef CONFIG_RISCV_M_MODE #define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA +#define RISCV_ISA_EXT_SUPM RISCV_ISA_EXT_SMNPM #else #define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SSAIA +#define RISCV_ISA_EXT_SUPM RISCV_ISA_EXT_SSNPM #endif #endif /* _ASM_RISCV_HWCAP_H */ diff --git a/arch/riscv/include/asm/hwprobe.h b/arch/riscv/include/asm/hwprobe.h index ffb9484531af7..1ce1df6d0ff3c 100644 --- a/arch/riscv/include/asm/hwprobe.h +++ b/arch/riscv/include/asm/hwprobe.h @@ -8,7 +8,7 @@ #include -#define RISCV_HWPROBE_MAX_KEY 9 +#define RISCV_HWPROBE_MAX_KEY 10 static inline bool riscv_hwprobe_key_is_valid(__s64 key) { diff --git a/arch/riscv/include/asm/jump_label.h b/arch/riscv/include/asm/jump_label.h index 1c768d02bd0c2..87a71cc6d146c 100644 --- a/arch/riscv/include/asm/jump_label.h +++ b/arch/riscv/include/asm/jump_label.h @@ -16,21 +16,28 @@ #define JUMP_LABEL_NOP_SIZE 4 +#define JUMP_TABLE_ENTRY(key, label) \ + ".pushsection __jump_table, \"aw\" \n\t" \ + ".align " RISCV_LGPTR " \n\t" \ + ".long 1b - ., " label " - . \n\t" \ + "" RISCV_PTR " " key " - . \n\t" \ + ".popsection \n\t" + +/* This macro is also expanded on the Rust side. */ +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + " .align 2 \n\t" \ + " .option push \n\t" \ + " .option norelax \n\t" \ + " .option norvc \n\t" \ + "1: nop \n\t" \ + " .option pop \n\t" \ + JUMP_TABLE_ENTRY(key, label) + static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch) { asm goto( - " .align 2 \n\t" - " .option push \n\t" - " .option norelax \n\t" - " .option norvc \n\t" - "1: nop \n\t" - " .option pop \n\t" - " .pushsection __jump_table, \"aw\" \n\t" - " .align " RISCV_LGPTR " \n\t" - " .long 1b - ., %l[label] - . \n\t" - " " RISCV_PTR " %0 - . \n\t" - " .popsection \n\t" + ARCH_STATIC_BRANCH_ASM("%0", "%l[label]") : : "i"(&((char *)key)[branch]) : : label); return false; @@ -38,21 +45,20 @@ static __always_inline bool arch_static_branch(struct static_key * const key, return true; } +#define ARCH_STATIC_BRANCH_JUMP_ASM(key, label) \ + " .align 2 \n\t" \ + " .option push \n\t" \ + " .option norelax \n\t" \ + " .option norvc \n\t" \ + "1: j " label " \n\t" \ + " .option pop \n\t" \ + JUMP_TABLE_ENTRY(key, label) + static __always_inline bool arch_static_branch_jump(struct static_key * const key, const bool branch) { asm goto( - " .align 2 \n\t" - " .option push \n\t" - " .option norelax \n\t" - " .option norvc \n\t" - "1: j %l[label] \n\t" - " .option pop \n\t" - " .pushsection __jump_table, \"aw\" \n\t" - " .align " RISCV_LGPTR " \n\t" - " .long 1b - ., %l[label] - . \n\t" - " " RISCV_PTR " %0 - . \n\t" - " .popsection \n\t" + ARCH_STATIC_BRANCH_JUMP_ASM("%0", "%l[label]") : : "i"(&((char *)key)[branch]) : : label); return false; diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 2e2254fd2a2aa..35eab6e0f4ae0 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -286,6 +286,16 @@ struct kvm_vcpu_arch { } sta; }; +/* + * Returns true if a Performance Monitoring Interrupt (PMI), a.k.a. perf event, + * arrived in guest context. For riscv, any event that arrives while a vCPU is + * loaded is considered to be "in guest". + */ +static inline bool kvm_arch_pmi_in_guest(struct kvm_vcpu *vcpu) +{ + return IS_ENABLED(CONFIG_GUEST_PERF_EVENTS) && !!vcpu; +} + static inline void kvm_arch_sync_events(struct kvm *kvm) {} #define KVM_RISCV_GSTAGE_TLB_MIN_ORDER 12 diff --git a/arch/riscv/include/asm/kvm_nacl.h b/arch/riscv/include/asm/kvm_nacl.h new file mode 100644 index 0000000000000..4124d5e06a0ff --- /dev/null +++ b/arch/riscv/include/asm/kvm_nacl.h @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 Ventana Micro Systems Inc. + */ + +#ifndef __KVM_NACL_H +#define __KVM_NACL_H + +#include +#include +#include +#include +#include + +struct kvm_vcpu_arch; + +DECLARE_STATIC_KEY_FALSE(kvm_riscv_nacl_available); +#define kvm_riscv_nacl_available() \ + static_branch_unlikely(&kvm_riscv_nacl_available) + +DECLARE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_csr_available); +#define kvm_riscv_nacl_sync_csr_available() \ + static_branch_unlikely(&kvm_riscv_nacl_sync_csr_available) + +DECLARE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_hfence_available); +#define kvm_riscv_nacl_sync_hfence_available() \ + static_branch_unlikely(&kvm_riscv_nacl_sync_hfence_available) + +DECLARE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_sret_available); +#define kvm_riscv_nacl_sync_sret_available() \ + static_branch_unlikely(&kvm_riscv_nacl_sync_sret_available) + +DECLARE_STATIC_KEY_FALSE(kvm_riscv_nacl_autoswap_csr_available); +#define kvm_riscv_nacl_autoswap_csr_available() \ + static_branch_unlikely(&kvm_riscv_nacl_autoswap_csr_available) + +struct kvm_riscv_nacl { + void *shmem; + phys_addr_t shmem_phys; +}; +DECLARE_PER_CPU(struct kvm_riscv_nacl, kvm_riscv_nacl); + +void __kvm_riscv_nacl_hfence(void *shmem, + unsigned long control, + unsigned long page_num, + unsigned long page_count); + +void __kvm_riscv_nacl_switch_to(struct kvm_vcpu_arch *vcpu_arch, + unsigned long sbi_ext_id, + unsigned long sbi_func_id); + +int kvm_riscv_nacl_enable(void); + +void kvm_riscv_nacl_disable(void); + +void kvm_riscv_nacl_exit(void); + +int kvm_riscv_nacl_init(void); + +#ifdef CONFIG_32BIT +#define lelong_to_cpu(__x) le32_to_cpu(__x) +#define cpu_to_lelong(__x) cpu_to_le32(__x) +#else +#define lelong_to_cpu(__x) le64_to_cpu(__x) +#define cpu_to_lelong(__x) cpu_to_le64(__x) +#endif + +#define nacl_shmem() \ + this_cpu_ptr(&kvm_riscv_nacl)->shmem + +#define nacl_scratch_read_long(__shmem, __offset) \ +({ \ + unsigned long *__p = (__shmem) + \ + SBI_NACL_SHMEM_SCRATCH_OFFSET + \ + (__offset); \ + lelong_to_cpu(*__p); \ +}) + +#define nacl_scratch_write_long(__shmem, __offset, __val) \ +do { \ + unsigned long *__p = (__shmem) + \ + SBI_NACL_SHMEM_SCRATCH_OFFSET + \ + (__offset); \ + *__p = cpu_to_lelong(__val); \ +} while (0) + +#define nacl_scratch_write_longs(__shmem, __offset, __array, __count) \ +do { \ + unsigned int __i; \ + unsigned long *__p = (__shmem) + \ + SBI_NACL_SHMEM_SCRATCH_OFFSET + \ + (__offset); \ + for (__i = 0; __i < (__count); __i++) \ + __p[__i] = cpu_to_lelong((__array)[__i]); \ +} while (0) + +#define nacl_sync_hfence(__e) \ + sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SYNC_HFENCE, \ + (__e), 0, 0, 0, 0, 0) + +#define nacl_hfence_mkconfig(__type, __order, __vmid, __asid) \ +({ \ + unsigned long __c = SBI_NACL_SHMEM_HFENCE_CONFIG_PEND; \ + __c |= ((__type) & SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_MASK) \ + << SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_SHIFT; \ + __c |= (((__order) - SBI_NACL_SHMEM_HFENCE_ORDER_BASE) & \ + SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_MASK) \ + << SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_SHIFT; \ + __c |= ((__vmid) & SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_MASK) \ + << SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_SHIFT; \ + __c |= ((__asid) & SBI_NACL_SHMEM_HFENCE_CONFIG_ASID_MASK); \ + __c; \ +}) + +#define nacl_hfence_mkpnum(__order, __addr) \ + ((__addr) >> (__order)) + +#define nacl_hfence_mkpcount(__order, __size) \ + ((__size) >> (__order)) + +#define nacl_hfence_gvma(__shmem, __gpa, __gpsz, __order) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_GVMA, \ + __order, 0, 0), \ + nacl_hfence_mkpnum(__order, __gpa), \ + nacl_hfence_mkpcount(__order, __gpsz)) + +#define nacl_hfence_gvma_all(__shmem) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_GVMA_ALL, \ + 0, 0, 0), 0, 0) + +#define nacl_hfence_gvma_vmid(__shmem, __vmid, __gpa, __gpsz, __order) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_GVMA_VMID, \ + __order, __vmid, 0), \ + nacl_hfence_mkpnum(__order, __gpa), \ + nacl_hfence_mkpcount(__order, __gpsz)) + +#define nacl_hfence_gvma_vmid_all(__shmem, __vmid) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_GVMA_VMID_ALL, \ + 0, __vmid, 0), 0, 0) + +#define nacl_hfence_vvma(__shmem, __vmid, __gva, __gvsz, __order) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_VVMA, \ + __order, __vmid, 0), \ + nacl_hfence_mkpnum(__order, __gva), \ + nacl_hfence_mkpcount(__order, __gvsz)) + +#define nacl_hfence_vvma_all(__shmem, __vmid) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_VVMA_ALL, \ + 0, __vmid, 0), 0, 0) + +#define nacl_hfence_vvma_asid(__shmem, __vmid, __asid, __gva, __gvsz, __order)\ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_VVMA_ASID, \ + __order, __vmid, __asid), \ + nacl_hfence_mkpnum(__order, __gva), \ + nacl_hfence_mkpcount(__order, __gvsz)) + +#define nacl_hfence_vvma_asid_all(__shmem, __vmid, __asid) \ +__kvm_riscv_nacl_hfence(__shmem, \ + nacl_hfence_mkconfig(SBI_NACL_SHMEM_HFENCE_TYPE_VVMA_ASID_ALL, \ + 0, __vmid, __asid), 0, 0) + +#define nacl_csr_read(__shmem, __csr) \ +({ \ + unsigned long *__a = (__shmem) + SBI_NACL_SHMEM_CSR_OFFSET; \ + lelong_to_cpu(__a[SBI_NACL_SHMEM_CSR_INDEX(__csr)]); \ +}) + +#define nacl_csr_write(__shmem, __csr, __val) \ +do { \ + void *__s = (__shmem); \ + unsigned int __i = SBI_NACL_SHMEM_CSR_INDEX(__csr); \ + unsigned long *__a = (__s) + SBI_NACL_SHMEM_CSR_OFFSET; \ + u8 *__b = (__s) + SBI_NACL_SHMEM_DBITMAP_OFFSET; \ + __a[__i] = cpu_to_lelong(__val); \ + __b[__i >> 3] |= 1U << (__i & 0x7); \ +} while (0) + +#define nacl_csr_swap(__shmem, __csr, __val) \ +({ \ + void *__s = (__shmem); \ + unsigned int __i = SBI_NACL_SHMEM_CSR_INDEX(__csr); \ + unsigned long *__a = (__s) + SBI_NACL_SHMEM_CSR_OFFSET; \ + u8 *__b = (__s) + SBI_NACL_SHMEM_DBITMAP_OFFSET; \ + unsigned long __r = lelong_to_cpu(__a[__i]); \ + __a[__i] = cpu_to_lelong(__val); \ + __b[__i >> 3] |= 1U << (__i & 0x7); \ + __r; \ +}) + +#define nacl_sync_csr(__csr) \ + sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SYNC_CSR, \ + (__csr), 0, 0, 0, 0, 0) + +/* + * Each ncsr_xyz() macro defined below has it's own static-branch so every + * use of ncsr_xyz() macro emits a patchable direct jump. This means multiple + * back-to-back ncsr_xyz() macro usage will emit multiple patchable direct + * jumps which is sub-optimal. + * + * Based on the above, it is recommended to avoid multiple back-to-back + * ncsr_xyz() macro usage. + */ + +#define ncsr_read(__csr) \ +({ \ + unsigned long __r; \ + if (kvm_riscv_nacl_available()) \ + __r = nacl_csr_read(nacl_shmem(), __csr); \ + else \ + __r = csr_read(__csr); \ + __r; \ +}) + +#define ncsr_write(__csr, __val) \ +do { \ + if (kvm_riscv_nacl_sync_csr_available()) \ + nacl_csr_write(nacl_shmem(), __csr, __val); \ + else \ + csr_write(__csr, __val); \ +} while (0) + +#define ncsr_swap(__csr, __val) \ +({ \ + unsigned long __r; \ + if (kvm_riscv_nacl_sync_csr_available()) \ + __r = nacl_csr_swap(nacl_shmem(), __csr, __val); \ + else \ + __r = csr_swap(__csr, __val); \ + __r; \ +}) + +#define nsync_csr(__csr) \ +do { \ + if (kvm_riscv_nacl_sync_csr_available()) \ + nacl_sync_csr(__csr); \ +} while (0) + +#endif diff --git a/arch/riscv/include/asm/mmu.h b/arch/riscv/include/asm/mmu.h index c9e03e9da3dc9..1cc90465d75b1 100644 --- a/arch/riscv/include/asm/mmu.h +++ b/arch/riscv/include/asm/mmu.h @@ -25,9 +25,16 @@ typedef struct { #ifdef CONFIG_BINFMT_ELF_FDPIC unsigned long exec_fdpic_loadmap; unsigned long interp_fdpic_loadmap; +#endif + unsigned long flags; +#ifdef CONFIG_RISCV_ISA_SUPM + u8 pmlen; #endif } mm_context_t; +/* Lock the pointer masking mode because this mm is multithreaded */ +#define MM_CONTEXT_LOCK_PMLEN 0 + #define cntx2asid(cntx) ((cntx) & SATP_ASID_MASK) #define cntx2version(cntx) ((cntx) & ~SATP_ASID_MASK) diff --git a/arch/riscv/include/asm/mmu_context.h b/arch/riscv/include/asm/mmu_context.h index 7030837adc1a2..8c4bc49a3a0f5 100644 --- a/arch/riscv/include/asm/mmu_context.h +++ b/arch/riscv/include/asm/mmu_context.h @@ -20,6 +20,9 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) { +#ifdef CONFIG_RISCV_ISA_SUPM + next->context.pmlen = 0; +#endif switch_mm(prev, next, NULL); } @@ -30,11 +33,21 @@ static inline int init_new_context(struct task_struct *tsk, #ifdef CONFIG_MMU atomic_long_set(&mm->context.id, 0); #endif + if (IS_ENABLED(CONFIG_RISCV_ISA_SUPM)) + clear_bit(MM_CONTEXT_LOCK_PMLEN, &mm->context.flags); return 0; } DECLARE_STATIC_KEY_FALSE(use_asid_allocator); +#ifdef CONFIG_RISCV_ISA_SUPM +#define mm_untag_mask mm_untag_mask +static inline unsigned long mm_untag_mask(struct mm_struct *mm) +{ + return -1UL >> mm->context.pmlen; +} +#endif + #include #endif /* _ASM_RISCV_MMU_CONTEXT_H */ diff --git a/arch/riscv/include/asm/perf_event.h b/arch/riscv/include/asm/perf_event.h index 665bbc9b2f840..bcc928fd3785c 100644 --- a/arch/riscv/include/asm/perf_event.h +++ b/arch/riscv/include/asm/perf_event.h @@ -8,6 +8,7 @@ #ifndef _ASM_RISCV_PERF_EVENT_H #define _ASM_RISCV_PERF_EVENT_H +#ifdef CONFIG_PERF_EVENTS #include #define perf_arch_bpf_user_pt_regs(regs) (struct user_regs_struct *)regs @@ -17,4 +18,6 @@ (regs)->sp = current_stack_pointer; \ (regs)->status = SR_PP; \ } +#endif + #endif /* _ASM_RISCV_PERF_EVENT_H */ diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 5d7f3e8c2e508..d4e99eef90acf 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -113,6 +113,7 @@ #include #include #include +#include #define __page_val_to_pfn(_val) (((_val) & _PAGE_PFN_MASK) >> _PAGE_PFN_SHIFT) @@ -284,7 +285,6 @@ static inline pte_t pud_pte(pud_t pud) } #ifdef CONFIG_RISCV_ISA_SVNAPOT -#include static __always_inline bool has_svnapot(void) { @@ -655,6 +655,17 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot) return __pgprot(prot); } +/* + * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By + * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in + * DT. + */ +#define arch_has_hw_pte_young arch_has_hw_pte_young +static inline bool arch_has_hw_pte_young(void) +{ + return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU); +} + /* * THP functions */ diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index efa1b3519b238..5f56eb9d114a9 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -102,6 +102,7 @@ struct thread_struct { unsigned long s[12]; /* s[0]: frame pointer */ struct __riscv_d_ext_state fstate; unsigned long bad_cause; + unsigned long envcfg; u32 riscv_v_flags; u32 vstate_ctrl; struct __riscv_v_ext_state vstate; @@ -177,6 +178,14 @@ extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val); #define RISCV_SET_ICACHE_FLUSH_CTX(arg1, arg2) riscv_set_icache_flush_ctx(arg1, arg2) extern int riscv_set_icache_flush_ctx(unsigned long ctx, unsigned long per_thread); +#ifdef CONFIG_RISCV_ISA_SUPM +/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */ +long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg); +long get_tagged_addr_ctrl(struct task_struct *task); +#define SET_TAGGED_ADDR_CTRL(arg) set_tagged_addr_ctrl(current, arg) +#define GET_TAGGED_ADDR_CTRL() get_tagged_addr_ctrl(current) +#endif + #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_PROCESSOR_H */ diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index 98f631b051dba..6c82318065cfd 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -34,6 +34,7 @@ enum sbi_ext_id { SBI_EXT_PMU = 0x504D55, SBI_EXT_DBCN = 0x4442434E, SBI_EXT_STA = 0x535441, + SBI_EXT_NACL = 0x4E41434C, /* Experimentals extensions must lie within this range */ SBI_EXT_EXPERIMENTAL_START = 0x08000000, @@ -281,6 +282,125 @@ struct sbi_sta_struct { #define SBI_SHMEM_DISABLE -1 +enum sbi_ext_nacl_fid { + SBI_EXT_NACL_PROBE_FEATURE = 0x0, + SBI_EXT_NACL_SET_SHMEM = 0x1, + SBI_EXT_NACL_SYNC_CSR = 0x2, + SBI_EXT_NACL_SYNC_HFENCE = 0x3, + SBI_EXT_NACL_SYNC_SRET = 0x4, +}; + +enum sbi_ext_nacl_feature { + SBI_NACL_FEAT_SYNC_CSR = 0x0, + SBI_NACL_FEAT_SYNC_HFENCE = 0x1, + SBI_NACL_FEAT_SYNC_SRET = 0x2, + SBI_NACL_FEAT_AUTOSWAP_CSR = 0x3, +}; + +#define SBI_NACL_SHMEM_ADDR_SHIFT 12 +#define SBI_NACL_SHMEM_SCRATCH_OFFSET 0x0000 +#define SBI_NACL_SHMEM_SCRATCH_SIZE 0x1000 +#define SBI_NACL_SHMEM_SRET_OFFSET 0x0000 +#define SBI_NACL_SHMEM_SRET_SIZE 0x0200 +#define SBI_NACL_SHMEM_AUTOSWAP_OFFSET (SBI_NACL_SHMEM_SRET_OFFSET + \ + SBI_NACL_SHMEM_SRET_SIZE) +#define SBI_NACL_SHMEM_AUTOSWAP_SIZE 0x0080 +#define SBI_NACL_SHMEM_UNUSED_OFFSET (SBI_NACL_SHMEM_AUTOSWAP_OFFSET + \ + SBI_NACL_SHMEM_AUTOSWAP_SIZE) +#define SBI_NACL_SHMEM_UNUSED_SIZE 0x0580 +#define SBI_NACL_SHMEM_HFENCE_OFFSET (SBI_NACL_SHMEM_UNUSED_OFFSET + \ + SBI_NACL_SHMEM_UNUSED_SIZE) +#define SBI_NACL_SHMEM_HFENCE_SIZE 0x0780 +#define SBI_NACL_SHMEM_DBITMAP_OFFSET (SBI_NACL_SHMEM_HFENCE_OFFSET + \ + SBI_NACL_SHMEM_HFENCE_SIZE) +#define SBI_NACL_SHMEM_DBITMAP_SIZE 0x0080 +#define SBI_NACL_SHMEM_CSR_OFFSET (SBI_NACL_SHMEM_DBITMAP_OFFSET + \ + SBI_NACL_SHMEM_DBITMAP_SIZE) +#define SBI_NACL_SHMEM_CSR_SIZE ((__riscv_xlen / 8) * 1024) +#define SBI_NACL_SHMEM_SIZE (SBI_NACL_SHMEM_CSR_OFFSET + \ + SBI_NACL_SHMEM_CSR_SIZE) + +#define SBI_NACL_SHMEM_CSR_INDEX(__csr_num) \ + ((((__csr_num) & 0xc00) >> 2) | ((__csr_num) & 0xff)) + +#define SBI_NACL_SHMEM_HFENCE_ENTRY_SZ ((__riscv_xlen / 8) * 4) +#define SBI_NACL_SHMEM_HFENCE_ENTRY_MAX \ + (SBI_NACL_SHMEM_HFENCE_SIZE / \ + SBI_NACL_SHMEM_HFENCE_ENTRY_SZ) +#define SBI_NACL_SHMEM_HFENCE_ENTRY(__num) \ + (SBI_NACL_SHMEM_HFENCE_OFFSET + \ + (__num) * SBI_NACL_SHMEM_HFENCE_ENTRY_SZ) +#define SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(__num) \ + SBI_NACL_SHMEM_HFENCE_ENTRY(__num) +#define SBI_NACL_SHMEM_HFENCE_ENTRY_PNUM(__num)\ + (SBI_NACL_SHMEM_HFENCE_ENTRY(__num) + (__riscv_xlen / 8)) +#define SBI_NACL_SHMEM_HFENCE_ENTRY_PCOUNT(__num)\ + (SBI_NACL_SHMEM_HFENCE_ENTRY(__num) + \ + ((__riscv_xlen / 8) * 3)) + +#define SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_BITS 1 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_SHIFT \ + (__riscv_xlen - SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_BITS) +#define SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_MASK \ + ((1UL << SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_BITS) - 1) +#define SBI_NACL_SHMEM_HFENCE_CONFIG_PEND \ + (SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_MASK << \ + SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_SHIFT) + +#define SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD1_BITS 3 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD1_SHIFT \ + (SBI_NACL_SHMEM_HFENCE_CONFIG_PEND_SHIFT - \ + SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD1_BITS) + +#define SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_BITS 4 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_SHIFT \ + (SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD1_SHIFT - \ + SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_BITS) +#define SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_MASK \ + ((1UL << SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_BITS) - 1) + +#define SBI_NACL_SHMEM_HFENCE_TYPE_GVMA 0x0 +#define SBI_NACL_SHMEM_HFENCE_TYPE_GVMA_ALL 0x1 +#define SBI_NACL_SHMEM_HFENCE_TYPE_GVMA_VMID 0x2 +#define SBI_NACL_SHMEM_HFENCE_TYPE_GVMA_VMID_ALL 0x3 +#define SBI_NACL_SHMEM_HFENCE_TYPE_VVMA 0x4 +#define SBI_NACL_SHMEM_HFENCE_TYPE_VVMA_ALL 0x5 +#define SBI_NACL_SHMEM_HFENCE_TYPE_VVMA_ASID 0x6 +#define SBI_NACL_SHMEM_HFENCE_TYPE_VVMA_ASID_ALL 0x7 + +#define SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD2_BITS 1 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD2_SHIFT \ + (SBI_NACL_SHMEM_HFENCE_CONFIG_TYPE_SHIFT - \ + SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD2_BITS) + +#define SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_BITS 7 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_SHIFT \ + (SBI_NACL_SHMEM_HFENCE_CONFIG_RSVD2_SHIFT - \ + SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_BITS) +#define SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_MASK \ + ((1UL << SBI_NACL_SHMEM_HFENCE_CONFIG_ORDER_BITS) - 1) +#define SBI_NACL_SHMEM_HFENCE_ORDER_BASE 12 + +#if __riscv_xlen == 32 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_ASID_BITS 9 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_BITS 7 +#else +#define SBI_NACL_SHMEM_HFENCE_CONFIG_ASID_BITS 16 +#define SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_BITS 14 +#endif +#define SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_SHIFT \ + SBI_NACL_SHMEM_HFENCE_CONFIG_ASID_BITS +#define SBI_NACL_SHMEM_HFENCE_CONFIG_ASID_MASK \ + ((1UL << SBI_NACL_SHMEM_HFENCE_CONFIG_ASID_BITS) - 1) +#define SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_MASK \ + ((1UL << SBI_NACL_SHMEM_HFENCE_CONFIG_VMID_BITS) - 1) + +#define SBI_NACL_SHMEM_AUTOSWAP_FLAG_HSTATUS BIT(0) +#define SBI_NACL_SHMEM_AUTOSWAP_HSTATUS ((__riscv_xlen / 8) * 1) + +#define SBI_NACL_SHMEM_SRET_X(__i) ((__riscv_xlen / 8) * (__i)) +#define SBI_NACL_SHMEM_SRET_X_LAST 31 + /* SBI spec version fields */ #define SBI_SPEC_VERSION_DEFAULT 0x1 #define SBI_SPEC_VERSION_MAJOR_SHIFT 24 diff --git a/arch/riscv/include/asm/spinlock.h b/arch/riscv/include/asm/spinlock.h new file mode 100644 index 0000000000000..e5121b89aceac --- /dev/null +++ b/arch/riscv/include/asm/spinlock.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_RISCV_SPINLOCK_H +#define __ASM_RISCV_SPINLOCK_H + +#ifdef CONFIG_RISCV_COMBO_SPINLOCKS +#define _Q_PENDING_LOOPS (1 << 9) + +#define __no_arch_spinlock_redefine +#include +#include +#include + +/* + * TODO: Use an alternative instead of a static key when we are able to parse + * the extensions string earlier in the boot process. + */ +DECLARE_STATIC_KEY_TRUE(qspinlock_key); + +#define SPINLOCK_BASE_DECLARE(op, type, type_lock) \ +static __always_inline type arch_spin_##op(type_lock lock) \ +{ \ + if (static_branch_unlikely(&qspinlock_key)) \ + return queued_spin_##op(lock); \ + return ticket_spin_##op(lock); \ +} + +SPINLOCK_BASE_DECLARE(lock, void, arch_spinlock_t *) +SPINLOCK_BASE_DECLARE(unlock, void, arch_spinlock_t *) +SPINLOCK_BASE_DECLARE(is_locked, int, arch_spinlock_t *) +SPINLOCK_BASE_DECLARE(is_contended, int, arch_spinlock_t *) +SPINLOCK_BASE_DECLARE(trylock, bool, arch_spinlock_t *) +SPINLOCK_BASE_DECLARE(value_unlocked, int, arch_spinlock_t) + +#elif defined(CONFIG_RISCV_QUEUED_SPINLOCKS) + +#include + +#else + +#include + +#endif + +#include + +#endif /* __ASM_RISCV_SPINLOCK_H */ diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 7594df37cc9ff..94e33216b2d94 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -70,6 +70,24 @@ static __always_inline bool has_fpu(void) { return false; } #define __switch_to_fpu(__prev, __next) do { } while (0) #endif +static inline void envcfg_update_bits(struct task_struct *task, + unsigned long mask, unsigned long val) +{ + unsigned long envcfg; + + envcfg = (task->thread.envcfg & ~mask) | val; + task->thread.envcfg = envcfg; + if (task == current) + csr_write(CSR_ENVCFG, envcfg); +} + +static inline void __switch_to_envcfg(struct task_struct *next) +{ + asm volatile (ALTERNATIVE("nop", "csrw " __stringify(CSR_ENVCFG) ", %0", + 0, RISCV_ISA_EXT_XLINUXENVCFG, 1) + :: "r" (next->thread.envcfg) : "memory"); +} + extern struct task_struct *__switch_to(struct task_struct *, struct task_struct *); @@ -103,6 +121,7 @@ do { \ __switch_to_vector(__prev, __next); \ if (switch_to_should_flush_icache(__next)) \ local_flush_icache_all(); \ + __switch_to_envcfg(__next); \ ((last) = __switch_to(__prev, __next)); \ } while (0) diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h index 72ec1d9bd3f31..fee56b0c80586 100644 --- a/arch/riscv/include/asm/uaccess.h +++ b/arch/riscv/include/asm/uaccess.h @@ -9,8 +9,41 @@ #define _ASM_RISCV_UACCESS_H #include +#include #include /* for TASK_SIZE */ +#ifdef CONFIG_RISCV_ISA_SUPM +static inline unsigned long __untagged_addr_remote(struct mm_struct *mm, unsigned long addr) +{ + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) { + u8 pmlen = mm->context.pmlen; + + /* Virtual addresses are sign-extended; physical addresses are zero-extended. */ + if (IS_ENABLED(CONFIG_MMU)) + return (long)(addr << pmlen) >> pmlen; + else + return (addr << pmlen) >> pmlen; + } + + return addr; +} + +#define untagged_addr(addr) ({ \ + unsigned long __addr = (__force unsigned long)(addr); \ + (__force __typeof__(addr))__untagged_addr_remote(current->mm, __addr); \ +}) + +#define untagged_addr_remote(mm, addr) ({ \ + unsigned long __addr = (__force unsigned long)(addr); \ + mmap_assert_locked(mm); \ + (__force __typeof__(addr))__untagged_addr_remote(mm, __addr); \ +}) + +#define access_ok(addr, size) likely(__access_ok(untagged_addr(addr), size)) +#else +#define untagged_addr(addr) (addr) +#endif + /* * User space memory access functions */ @@ -130,7 +163,7 @@ do { \ */ #define __get_user(x, ptr) \ ({ \ - const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \ + const __typeof__(*(ptr)) __user *__gu_ptr = untagged_addr(ptr); \ long __gu_err = 0; \ \ __chk_user_ptr(__gu_ptr); \ @@ -246,7 +279,7 @@ do { \ */ #define __put_user(x, ptr) \ ({ \ - __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \ + __typeof__(*(ptr)) __user *__gu_ptr = untagged_addr(ptr); \ __typeof__(*__gu_ptr) __val = (x); \ long __pu_err = 0; \ \ @@ -293,13 +326,13 @@ unsigned long __must_check __asm_copy_from_user(void *to, static inline unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n) { - return __asm_copy_from_user(to, from, n); + return __asm_copy_from_user(to, untagged_addr(from), n); } static inline unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n) { - return __asm_copy_to_user(to, from, n); + return __asm_copy_to_user(untagged_addr(to), from, n); } extern long strncpy_from_user(char *dest, const char __user *src, long count); @@ -314,7 +347,7 @@ unsigned long __must_check clear_user(void __user *to, unsigned long n) { might_fault(); return access_ok(to, n) ? - __clear_user(to, n) : n; + __clear_user(untagged_addr(to), n) : n; } #define __get_kernel_nofault(dst, src, type, err_label) \ diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index be7d309cca8a7..c7c023afbacd7 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -21,6 +21,7 @@ extern unsigned long riscv_v_vsize; int riscv_v_setup_vsize(void); +bool insn_is_vector(u32 insn_buf); bool riscv_v_first_use_handler(struct pt_regs *regs); void kernel_vector_begin(void); void kernel_vector_end(void); @@ -268,6 +269,7 @@ struct pt_regs; static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; } static __always_inline bool has_vector(void) { return false; } +static __always_inline bool insn_is_vector(u32 insn_buf) { return false; } static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; } static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; } static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; } diff --git a/arch/riscv/include/uapi/asm/hwprobe.h b/arch/riscv/include/uapi/asm/hwprobe.h index 1e153cda57db8..3af142b99f778 100644 --- a/arch/riscv/include/uapi/asm/hwprobe.h +++ b/arch/riscv/include/uapi/asm/hwprobe.h @@ -72,6 +72,7 @@ struct riscv_hwprobe { #define RISCV_HWPROBE_EXT_ZCF (1ULL << 46) #define RISCV_HWPROBE_EXT_ZCMOP (1ULL << 47) #define RISCV_HWPROBE_EXT_ZAWRS (1ULL << 48) +#define RISCV_HWPROBE_EXT_SUPM (1ULL << 49) #define RISCV_HWPROBE_KEY_CPUPERF_0 5 #define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) #define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0) @@ -88,6 +89,11 @@ struct riscv_hwprobe { #define RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW 2 #define RISCV_HWPROBE_MISALIGNED_SCALAR_FAST 3 #define RISCV_HWPROBE_MISALIGNED_SCALAR_UNSUPPORTED 4 +#define RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF 10 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN 0 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW 2 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_FAST 3 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED 4 /* Increase RISCV_HWPROBE_MAX_KEY when adding items. */ /* Flags */ diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index e97db3296456e..3482c9a73d1b6 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -175,6 +175,10 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_ZCF, KVM_RISCV_ISA_EXT_ZCMOP, KVM_RISCV_ISA_EXT_ZAWRS, + KVM_RISCV_ISA_EXT_SMNPM, + KVM_RISCV_ISA_EXT_SSNPM, + KVM_RISCV_ISA_EXT_SVADE, + KVM_RISCV_ISA_EXT_SVADU, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 69dc8aaab3fb3..063d1faf5a53d 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -75,7 +75,8 @@ obj-$(CONFIG_MMU) += vdso.o vdso/ obj-$(CONFIG_RISCV_MISALIGNED) += traps_misaligned.o obj-$(CONFIG_RISCV_MISALIGNED) += unaligned_access_speed.o -obj-$(CONFIG_RISCV_PROBE_UNALIGNED_ACCESS) += copy-unaligned.o +obj-$(CONFIG_RISCV_PROBE_UNALIGNED_ACCESS) += copy-unaligned.o +obj-$(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS) += vec-copy-unaligned.o obj-$(CONFIG_FPU) += fpu.o obj-$(CONFIG_FPU) += kernel_mode_fpu.o diff --git a/arch/riscv/kernel/copy-unaligned.h b/arch/riscv/kernel/copy-unaligned.h index e3d70d35b7081..85d4d11450cb6 100644 --- a/arch/riscv/kernel/copy-unaligned.h +++ b/arch/riscv/kernel/copy-unaligned.h @@ -10,4 +10,9 @@ void __riscv_copy_words_unaligned(void *dst, const void *src, size_t size); void __riscv_copy_bytes_unaligned(void *dst, const void *src, size_t size); +#ifdef CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS +void __riscv_copy_vec_words_unaligned(void *dst, const void *src, size_t size); +void __riscv_copy_vec_bytes_unaligned(void *dst, const void *src, size_t size); +#endif + #endif /* __RISCV_KERNEL_COPY_UNALIGNED_H */ diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 826f46b21f2e8..c0916ed318c20 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -29,6 +29,8 @@ #define NUM_ALPHA_EXTS ('z' - 'a' + 1) +static bool any_cpu_has_zicboz; + unsigned long elf_hwcap __read_mostly; /* Host ISA bitmap */ @@ -99,6 +101,7 @@ static int riscv_ext_zicboz_validate(const struct riscv_isa_ext_data *data, pr_err("Zicboz disabled as cboz-block-size present, but is not a power-of-2\n"); return -EINVAL; } + any_cpu_has_zicboz = true; return 0; } @@ -133,6 +136,16 @@ static int riscv_ext_zcf_validate(const struct riscv_isa_ext_data *data, return -EPROBE_DEFER; } +static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data, + const unsigned long *isa_bitmap) +{ + /* SVADE has already been detected, use SVADE only */ + if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE)) + return -EOPNOTSUPP; + + return 0; +} + static const unsigned int riscv_zk_bundled_exts[] = { RISCV_ISA_EXT_ZBKB, RISCV_ISA_EXT_ZBKC, @@ -315,6 +328,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = { riscv_ext_zicbom_validate), __RISCV_ISA_EXT_SUPERSET_VALIDATE(zicboz, RISCV_ISA_EXT_ZICBOZ, riscv_xlinuxenvcfg_exts, riscv_ext_zicboz_validate), + __RISCV_ISA_EXT_DATA(ziccrse, RISCV_ISA_EXT_ZICCRSE), __RISCV_ISA_EXT_DATA(zicntr, RISCV_ISA_EXT_ZICNTR), __RISCV_ISA_EXT_DATA(zicond, RISCV_ISA_EXT_ZICOND), __RISCV_ISA_EXT_DATA(zicsr, RISCV_ISA_EXT_ZICSR), @@ -323,6 +337,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = { __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE), __RISCV_ISA_EXT_DATA(zihpm, RISCV_ISA_EXT_ZIHPM), __RISCV_ISA_EXT_DATA(zimop, RISCV_ISA_EXT_ZIMOP), + __RISCV_ISA_EXT_DATA(zabha, RISCV_ISA_EXT_ZABHA), __RISCV_ISA_EXT_DATA(zacas, RISCV_ISA_EXT_ZACAS), __RISCV_ISA_EXT_DATA(zawrs, RISCV_ISA_EXT_ZAWRS), __RISCV_ISA_EXT_DATA(zfa, RISCV_ISA_EXT_ZFA), @@ -375,10 +390,15 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = { __RISCV_ISA_EXT_BUNDLE(zvksg, riscv_zvksg_bundled_exts), __RISCV_ISA_EXT_DATA(zvkt, RISCV_ISA_EXT_ZVKT), __RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA), + __RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM), + __RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts), __RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN), __RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA), __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF), + __RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts), __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC), + __RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE), + __RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate), __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL), __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT), __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT), @@ -918,10 +938,12 @@ unsigned long riscv_get_elf_hwcap(void) return hwcap; } -void riscv_user_isa_enable(void) +void __init riscv_user_isa_enable(void) { - if (riscv_cpu_has_extension_unlikely(smp_processor_id(), RISCV_ISA_EXT_ZICBOZ)) - csr_set(CSR_ENVCFG, ENVCFG_CBZE); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_ZICBOZ)) + current->thread.envcfg |= ENVCFG_CBZE; + else if (any_cpu_has_zicboz) + pr_warn("Zicboz disabled as it is unavailable on some harts\n"); } #ifdef CONFIG_RISCV_ALTERNATIVE diff --git a/arch/riscv/kernel/fpu.S b/arch/riscv/kernel/fpu.S index 327cf527dd7e1..f74f6b60e347f 100644 --- a/arch/riscv/kernel/fpu.S +++ b/arch/riscv/kernel/fpu.S @@ -170,7 +170,7 @@ SYM_FUNC_END(__fstate_restore) __access_func(f31) -#ifdef CONFIG_RISCV_MISALIGNED +#ifdef CONFIG_RISCV_SCALAR_MISALIGNED /* * Disable compressed instructions set to keep a constant offset between FP @@ -224,4 +224,4 @@ SYM_FUNC_START(get_f64_reg) fp_access_epilogue SYM_FUNC_END(get_f64_reg) -#endif /* CONFIG_RISCV_MISALIGNED */ +#endif /* CONFIG_RISCV_SCALAR_MISALIGNED */ diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c index c7468af77c663..b465bc9eb870e 100644 --- a/arch/riscv/kernel/perf_callchain.c +++ b/arch/riscv/kernel/perf_callchain.c @@ -28,11 +28,21 @@ static bool fill_callchain(void *entry, unsigned long pc) void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) { + if (perf_guest_state()) { + /* TODO: We don't support guest os callchain now */ + return; + } + arch_stack_walk_user(fill_callchain, entry, regs); } void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) { + if (perf_guest_state()) { + /* TODO: We don't support guest os callchain now */ + return; + } + walk_stackframe(NULL, regs, fill_callchain, entry); } diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index e3142d8a6e284..58b6482c2bf66 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -7,6 +7,7 @@ * Copyright (C) 2017 SiFive */ +#include #include #include #include @@ -180,6 +181,10 @@ void flush_thread(void) memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE); #endif +#ifdef CONFIG_RISCV_ISA_SUPM + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) + envcfg_update_bits(current, ENVCFG_PMM, ENVCFG_PMM_PMLEN_0); +#endif } void arch_release_task_struct(struct task_struct *tsk) @@ -208,6 +213,10 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) unsigned long tls = args->tls; struct pt_regs *childregs = task_pt_regs(p); + /* Ensure all threads in this mm have the same pointer masking mode. */ + if (IS_ENABLED(CONFIG_RISCV_ISA_SUPM) && p->mm && (clone_flags & CLONE_VM)) + set_bit(MM_CONTEXT_LOCK_PMLEN, &p->mm->context.flags); + memset(&p->thread.s, 0, sizeof(p->thread.s)); /* p->thread holds context to be restored by __switch_to() */ @@ -242,3 +251,148 @@ void __init arch_task_cache_init(void) { riscv_v_setup_ctx_cache(); } + +#ifdef CONFIG_RISCV_ISA_SUPM +enum { + PMLEN_0 = 0, + PMLEN_7 = 7, + PMLEN_16 = 16, +}; + +static bool have_user_pmlen_7; +static bool have_user_pmlen_16; + +/* + * Control the relaxed ABI allowing tagged user addresses into the kernel. + */ +static unsigned int tagged_addr_disabled; + +long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) +{ + unsigned long valid_mask = PR_PMLEN_MASK | PR_TAGGED_ADDR_ENABLE; + struct thread_info *ti = task_thread_info(task); + struct mm_struct *mm = task->mm; + unsigned long pmm; + u8 pmlen; + + if (is_compat_thread(ti)) + return -EINVAL; + + if (arg & ~valid_mask) + return -EINVAL; + + /* + * Prefer the smallest PMLEN that satisfies the user's request, + * in case choosing a larger PMLEN has a performance impact. + */ + pmlen = FIELD_GET(PR_PMLEN_MASK, arg); + if (pmlen == PMLEN_0) { + pmm = ENVCFG_PMM_PMLEN_0; + } else if (pmlen <= PMLEN_7 && have_user_pmlen_7) { + pmlen = PMLEN_7; + pmm = ENVCFG_PMM_PMLEN_7; + } else if (pmlen <= PMLEN_16 && have_user_pmlen_16) { + pmlen = PMLEN_16; + pmm = ENVCFG_PMM_PMLEN_16; + } else { + return -EINVAL; + } + + /* + * Do not allow the enabling of the tagged address ABI if globally + * disabled via sysctl abi.tagged_addr_disabled, if pointer masking + * is disabled for userspace. + */ + if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen)) + return -EINVAL; + + if (!(arg & PR_TAGGED_ADDR_ENABLE)) + pmlen = PMLEN_0; + + if (mmap_write_lock_killable(mm)) + return -EINTR; + + if (test_bit(MM_CONTEXT_LOCK_PMLEN, &mm->context.flags) && mm->context.pmlen != pmlen) { + mmap_write_unlock(mm); + return -EBUSY; + } + + envcfg_update_bits(task, ENVCFG_PMM, pmm); + mm->context.pmlen = pmlen; + + mmap_write_unlock(mm); + + return 0; +} + +long get_tagged_addr_ctrl(struct task_struct *task) +{ + struct thread_info *ti = task_thread_info(task); + long ret = 0; + + if (is_compat_thread(ti)) + return -EINVAL; + + /* + * The mm context's pmlen is set only when the tagged address ABI is + * enabled, so the effective PMLEN must be extracted from envcfg.PMM. + */ + switch (task->thread.envcfg & ENVCFG_PMM) { + case ENVCFG_PMM_PMLEN_7: + ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_7); + break; + case ENVCFG_PMM_PMLEN_16: + ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_16); + break; + } + + if (task->mm->context.pmlen) + ret |= PR_TAGGED_ADDR_ENABLE; + + return ret; +} + +static bool try_to_set_pmm(unsigned long value) +{ + csr_set(CSR_ENVCFG, value); + return (csr_read_clear(CSR_ENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value; +} + +/* + * Global sysctl to disable the tagged user addresses support. This control + * only prevents the tagged address ABI enabling via prctl() and does not + * disable it for tasks that already opted in to the relaxed ABI. + */ + +static struct ctl_table tagged_addr_sysctl_table[] = { + { + .procname = "tagged_addr_disabled", + .mode = 0644, + .data = &tagged_addr_disabled, + .maxlen = sizeof(int), + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, +}; + +static int __init tagged_addr_init(void) +{ + if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) + return 0; + + /* + * envcfg.PMM is a WARL field. Detect which values are supported. + * Assume the supported PMLEN values are the same on all harts. + */ + csr_clear(CSR_ENVCFG, ENVCFG_PMM); + have_user_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7); + have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16); + + if (!register_sysctl("abi", tagged_addr_sysctl_table)) + return -EINVAL; + + return 0; +} +core_initcall(tagged_addr_init); +#endif /* CONFIG_RISCV_ISA_SUPM */ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 92731ff8c79ad..ea67e9fb7a583 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -28,6 +28,9 @@ enum riscv_regset { #ifdef CONFIG_RISCV_ISA_V REGSET_V, #endif +#ifdef CONFIG_RISCV_ISA_SUPM + REGSET_TAGGED_ADDR_CTRL, +#endif }; static int riscv_gpr_get(struct task_struct *target, @@ -152,6 +155,35 @@ static int riscv_vr_set(struct task_struct *target, } #endif +#ifdef CONFIG_RISCV_ISA_SUPM +static int tagged_addr_ctrl_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + long ctrl = get_tagged_addr_ctrl(target); + + if (IS_ERR_VALUE(ctrl)) + return ctrl; + + return membuf_write(&to, &ctrl, sizeof(ctrl)); +} + +static int tagged_addr_ctrl_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + long ctrl; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, 0, -1); + if (ret) + return ret; + + return set_tagged_addr_ctrl(target, ctrl); +} +#endif + static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { .core_note_type = NT_PRSTATUS, @@ -182,6 +214,16 @@ static const struct user_regset riscv_user_regset[] = { .set = riscv_vr_set, }, #endif +#ifdef CONFIG_RISCV_ISA_SUPM + [REGSET_TAGGED_ADDR_CTRL] = { + .core_note_type = NT_RISCV_TAGGED_ADDR_CTRL, + .n = 1, + .size = sizeof(long), + .align = sizeof(long), + .regset_get = tagged_addr_ctrl_get, + .set = tagged_addr_ctrl_set, + }, +#endif }; static const struct user_regset_view riscv_user_native_view = { diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 26c886db4fb3d..016b48fcd6f27 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -244,6 +244,42 @@ static void __init parse_dtb(void) #endif } +#if defined(CONFIG_RISCV_COMBO_SPINLOCKS) +DEFINE_STATIC_KEY_TRUE(qspinlock_key); +EXPORT_SYMBOL(qspinlock_key); +#endif + +static void __init riscv_spinlock_init(void) +{ + char *using_ext = NULL; + + if (IS_ENABLED(CONFIG_RISCV_TICKET_SPINLOCKS)) { + pr_info("Ticket spinlock: enabled\n"); + return; + } + + if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) && + IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) && + riscv_isa_extension_available(NULL, ZABHA) && + riscv_isa_extension_available(NULL, ZACAS)) { + using_ext = "using Zabha"; + } else if (riscv_isa_extension_available(NULL, ZICCRSE)) { + using_ext = "using Ziccrse"; + } +#if defined(CONFIG_RISCV_COMBO_SPINLOCKS) + else { + static_branch_disable(&qspinlock_key); + pr_info("Ticket spinlock: enabled\n"); + return; + } +#endif + + if (!using_ext) + pr_err("Queued spinlock without Zabha or Ziccrse"); + else + pr_info("Queued spinlock %s: enabled\n", using_ext); +} + extern void __init init_rt_signal_env(void); void __init setup_arch(char **cmdline_p) @@ -297,6 +333,7 @@ void __init setup_arch(char **cmdline_p) riscv_set_dma_cache_alignment(); riscv_user_isa_enable(); + riscv_spinlock_init(); } bool arch_cpu_is_hotpluggable(int cpu) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 0f8f1c95ac38c..e36d20205bd7d 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -233,8 +233,6 @@ asmlinkage __visible void smp_callin(void) numa_add_cpu(curr_cpuid); set_cpu_online(curr_cpuid, true); - riscv_user_isa_enable(); - /* * Remote cache and TLB flushes are ignored while the CPU is offline, * so flush them both right now just in case. diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c index c8cec0cc5833c..9a8a0dc035b26 100644 --- a/arch/riscv/kernel/suspend.c +++ b/arch/riscv/kernel/suspend.c @@ -14,7 +14,7 @@ void suspend_save_csrs(struct suspend_context *context) { - if (riscv_cpu_has_extension_unlikely(smp_processor_id(), RISCV_ISA_EXT_XLINUXENVCFG)) + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_XLINUXENVCFG)) context->envcfg = csr_read(CSR_ENVCFG); context->tvec = csr_read(CSR_TVEC); context->ie = csr_read(CSR_IE); @@ -37,7 +37,7 @@ void suspend_save_csrs(struct suspend_context *context) void suspend_restore_csrs(struct suspend_context *context) { csr_write(CSR_SCRATCH, 0); - if (riscv_cpu_has_extension_unlikely(smp_processor_id(), RISCV_ISA_EXT_XLINUXENVCFG)) + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_XLINUXENVCFG)) csr_write(CSR_ENVCFG, context->envcfg); csr_write(CSR_TVEC, context->tvec); csr_write(CSR_IE, context->ie); diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c index 711a31f27c3d0..cb93adfffc486 100644 --- a/arch/riscv/kernel/sys_hwprobe.c +++ b/arch/riscv/kernel/sys_hwprobe.c @@ -150,6 +150,9 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair, EXT_KEY(ZFH); EXT_KEY(ZFHMIN); } + + if (IS_ENABLED(CONFIG_RISCV_ISA_SUPM)) + EXT_KEY(SUPM); #undef EXT_KEY } @@ -201,6 +204,43 @@ static u64 hwprobe_misaligned(const struct cpumask *cpus) } #endif +#ifdef CONFIG_RISCV_VECTOR_MISALIGNED +static u64 hwprobe_vec_misaligned(const struct cpumask *cpus) +{ + int cpu; + u64 perf = -1ULL; + + /* Return if supported or not even if speed wasn't probed */ + for_each_cpu(cpu, cpus) { + int this_perf = per_cpu(vector_misaligned_access, cpu); + + if (perf == -1ULL) + perf = this_perf; + + if (perf != this_perf) { + perf = RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN; + break; + } + } + + if (perf == -1ULL) + return RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN; + + return perf; +} +#else +static u64 hwprobe_vec_misaligned(const struct cpumask *cpus) +{ + if (IS_ENABLED(CONFIG_RISCV_EFFICIENT_VECTOR_UNALIGNED_ACCESS)) + return RISCV_HWPROBE_MISALIGNED_VECTOR_FAST; + + if (IS_ENABLED(CONFIG_RISCV_SLOW_VECTOR_UNALIGNED_ACCESS)) + return RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW; + + return RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN; +} +#endif + static void hwprobe_one_pair(struct riscv_hwprobe *pair, const struct cpumask *cpus) { @@ -229,6 +269,10 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair, pair->value = hwprobe_misaligned(cpus); break; + case RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: + pair->value = hwprobe_vec_misaligned(cpus); + break; + case RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE: pair->value = 0; if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOZ)) diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index 1b9867136b610..7cc108aed74e8 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -16,6 +16,7 @@ #include #include #include +#include #define INSN_MATCH_LB 0x3 #define INSN_MASK_LB 0x707f @@ -320,12 +321,37 @@ union reg_data { u64 data_u64; }; -static bool unaligned_ctl __read_mostly; - /* sysctl hooks */ int unaligned_enabled __read_mostly = 1; /* Enabled by default */ -int handle_misaligned_load(struct pt_regs *regs) +#ifdef CONFIG_RISCV_VECTOR_MISALIGNED +static int handle_vector_misaligned_load(struct pt_regs *regs) +{ + unsigned long epc = regs->epc; + unsigned long insn; + + if (get_insn(regs, epc, &insn)) + return -1; + + /* Only return 0 when in check_vector_unaligned_access_emulated */ + if (*this_cpu_ptr(&vector_misaligned_access) == RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN) { + *this_cpu_ptr(&vector_misaligned_access) = RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED; + regs->epc = epc + INSN_LEN(insn); + return 0; + } + + /* If vector instruction we don't emulate it yet */ + regs->epc = epc; + return -1; +} +#else +static int handle_vector_misaligned_load(struct pt_regs *regs) +{ + return -1; +} +#endif + +static int handle_scalar_misaligned_load(struct pt_regs *regs) { union reg_data val; unsigned long epc = regs->epc; @@ -433,7 +459,7 @@ int handle_misaligned_load(struct pt_regs *regs) return 0; } -int handle_misaligned_store(struct pt_regs *regs) +static int handle_scalar_misaligned_store(struct pt_regs *regs) { union reg_data val; unsigned long epc = regs->epc; @@ -524,11 +550,96 @@ int handle_misaligned_store(struct pt_regs *regs) return 0; } -static bool check_unaligned_access_emulated(int cpu) +int handle_misaligned_load(struct pt_regs *regs) +{ + unsigned long epc = regs->epc; + unsigned long insn; + + if (IS_ENABLED(CONFIG_RISCV_VECTOR_MISALIGNED)) { + if (get_insn(regs, epc, &insn)) + return -1; + + if (insn_is_vector(insn)) + return handle_vector_misaligned_load(regs); + } + + if (IS_ENABLED(CONFIG_RISCV_SCALAR_MISALIGNED)) + return handle_scalar_misaligned_load(regs); + + return -1; +} + +int handle_misaligned_store(struct pt_regs *regs) { + if (IS_ENABLED(CONFIG_RISCV_SCALAR_MISALIGNED)) + return handle_scalar_misaligned_store(regs); + + return -1; +} + +#ifdef CONFIG_RISCV_VECTOR_MISALIGNED +void check_vector_unaligned_access_emulated(struct work_struct *work __always_unused) +{ + long *mas_ptr = this_cpu_ptr(&vector_misaligned_access); + unsigned long tmp_var; + + *mas_ptr = RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN; + + kernel_vector_begin(); + /* + * In pre-13.0.0 versions of GCC, vector registers cannot appear in + * the clobber list. This inline asm clobbers v0, but since we do not + * currently build the kernel with V enabled, the v0 clobber arg is not + * needed (as the compiler will not emit vector code itself). If the kernel + * is changed to build with V enabled, the clobber arg will need to be + * added here. + */ + __asm__ __volatile__ ( + ".balign 4\n\t" + ".option push\n\t" + ".option arch, +zve32x\n\t" + " vsetivli zero, 1, e16, m1, ta, ma\n\t" // Vectors of 16b + " vle16.v v0, (%[ptr])\n\t" // Load bytes + ".option pop\n\t" + : : [ptr] "r" ((u8 *)&tmp_var + 1)); + kernel_vector_end(); +} + +bool check_vector_unaligned_access_emulated_all_cpus(void) +{ + int cpu; + + if (!has_vector()) { + for_each_online_cpu(cpu) + per_cpu(vector_misaligned_access, cpu) = RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED; + return false; + } + + schedule_on_each_cpu(check_vector_unaligned_access_emulated); + + for_each_online_cpu(cpu) + if (per_cpu(vector_misaligned_access, cpu) + == RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN) + return false; + + return true; +} +#else +bool check_vector_unaligned_access_emulated_all_cpus(void) +{ + return false; +} +#endif + +#ifdef CONFIG_RISCV_SCALAR_MISALIGNED + +static bool unaligned_ctl __read_mostly; + +void check_unaligned_access_emulated(struct work_struct *work __always_unused) +{ + int cpu = smp_processor_id(); long *mas_ptr = per_cpu_ptr(&misaligned_access_speed, cpu); unsigned long tmp_var, tmp_val; - bool misaligned_emu_detected; *mas_ptr = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN; @@ -536,19 +647,16 @@ static bool check_unaligned_access_emulated(int cpu) " "REG_L" %[tmp], 1(%[ptr])\n" : [tmp] "=r" (tmp_val) : [ptr] "r" (&tmp_var) : "memory"); - misaligned_emu_detected = (*mas_ptr == RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED); /* * If unaligned_ctl is already set, this means that we detected that all * CPUS uses emulated misaligned access at boot time. If that changed * when hotplugging the new cpu, this is something we don't handle. */ - if (unlikely(unaligned_ctl && !misaligned_emu_detected)) { + if (unlikely(unaligned_ctl && (*mas_ptr != RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED))) { pr_crit("CPU misaligned accesses non homogeneous (expected all emulated)\n"); while (true) cpu_relax(); } - - return misaligned_emu_detected; } bool check_unaligned_access_emulated_all_cpus(void) @@ -560,8 +668,11 @@ bool check_unaligned_access_emulated_all_cpus(void) * accesses emulated since tasks requesting such control can run on any * CPU. */ + schedule_on_each_cpu(check_unaligned_access_emulated); + for_each_online_cpu(cpu) - if (!check_unaligned_access_emulated(cpu)) + if (per_cpu(misaligned_access_speed, cpu) + != RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED) return false; unaligned_ctl = true; @@ -572,3 +683,9 @@ bool unaligned_ctl_available(void) { return unaligned_ctl; } +#else +bool check_unaligned_access_emulated_all_cpus(void) +{ + return false; +} +#endif diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 160628a2116de..91f189cf16113 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -6,11 +6,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "copy-unaligned.h" @@ -19,7 +21,8 @@ #define MISALIGNED_BUFFER_ORDER get_order(MISALIGNED_BUFFER_SIZE) #define MISALIGNED_COPY_SIZE ((MISALIGNED_BUFFER_SIZE / 2) - 0x80) -DEFINE_PER_CPU(long, misaligned_access_speed); +DEFINE_PER_CPU(long, misaligned_access_speed) = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN; +DEFINE_PER_CPU(long, vector_misaligned_access) = RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED; #ifdef CONFIG_RISCV_PROBE_UNALIGNED_ACCESS static cpumask_t fast_misaligned_access; @@ -191,6 +194,7 @@ static int riscv_online_cpu(unsigned int cpu) if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN) goto exit; + check_unaligned_access_emulated(NULL); buf = alloc_pages(GFP_KERNEL, MISALIGNED_BUFFER_ORDER); if (!buf) { pr_warn("Allocation failure, not measuring misaligned performance\n"); @@ -259,23 +263,159 @@ static int check_unaligned_access_speed_all_cpus(void) kfree(bufs); return 0; } +#else /* CONFIG_RISCV_PROBE_UNALIGNED_ACCESS */ +static int check_unaligned_access_speed_all_cpus(void) +{ + return 0; +} +#endif -static int check_unaligned_access_all_cpus(void) +#ifdef CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS +static void check_vector_unaligned_access(struct work_struct *work __always_unused) { - bool all_cpus_emulated = check_unaligned_access_emulated_all_cpus(); + int cpu = smp_processor_id(); + u64 start_cycles, end_cycles; + u64 word_cycles; + u64 byte_cycles; + int ratio; + unsigned long start_jiffies, now; + struct page *page; + void *dst; + void *src; + long speed = RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW; - if (!all_cpus_emulated) - return check_unaligned_access_speed_all_cpus(); + if (per_cpu(vector_misaligned_access, cpu) != RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN) + return; + + page = alloc_pages(GFP_KERNEL, MISALIGNED_BUFFER_ORDER); + if (!page) { + pr_warn("Allocation failure, not measuring vector misaligned performance\n"); + return; + } + + /* Make an unaligned destination buffer. */ + dst = (void *)((unsigned long)page_address(page) | 0x1); + /* Unalign src as well, but differently (off by 1 + 2 = 3). */ + src = dst + (MISALIGNED_BUFFER_SIZE / 2); + src += 2; + word_cycles = -1ULL; + + /* Do a warmup. */ + kernel_vector_begin(); + __riscv_copy_vec_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); + start_jiffies = jiffies; + while ((now = jiffies) == start_jiffies) + cpu_relax(); + + /* + * For a fixed amount of time, repeatedly try the function, and take + * the best time in cycles as the measurement. + */ + while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { + start_cycles = get_cycles64(); + /* Ensure the CSR read can't reorder WRT to the copy. */ + mb(); + __riscv_copy_vec_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); + /* Ensure the copy ends before the end time is snapped. */ + mb(); + end_cycles = get_cycles64(); + if ((end_cycles - start_cycles) < word_cycles) + word_cycles = end_cycles - start_cycles; + } + + byte_cycles = -1ULL; + __riscv_copy_vec_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); + start_jiffies = jiffies; + while ((now = jiffies) == start_jiffies) + cpu_relax(); + + while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { + start_cycles = get_cycles64(); + /* Ensure the CSR read can't reorder WRT to the copy. */ + mb(); + __riscv_copy_vec_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); + /* Ensure the copy ends before the end time is snapped. */ + mb(); + end_cycles = get_cycles64(); + if ((end_cycles - start_cycles) < byte_cycles) + byte_cycles = end_cycles - start_cycles; + } + + kernel_vector_end(); + + /* Don't divide by zero. */ + if (!word_cycles || !byte_cycles) { + pr_warn("cpu%d: rdtime lacks granularity needed to measure unaligned vector access speed\n", + cpu); + + return; + } + + if (word_cycles < byte_cycles) + speed = RISCV_HWPROBE_MISALIGNED_VECTOR_FAST; + + ratio = div_u64((byte_cycles * 100), word_cycles); + pr_info("cpu%d: Ratio of vector byte access time to vector unaligned word access is %d.%02d, unaligned accesses are %s\n", + cpu, + ratio / 100, + ratio % 100, + (speed == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST) ? "fast" : "slow"); + + per_cpu(vector_misaligned_access, cpu) = speed; +} + +static int riscv_online_cpu_vec(unsigned int cpu) +{ + if (!has_vector()) + return 0; + + if (per_cpu(vector_misaligned_access, cpu) != RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED) + return 0; + + check_vector_unaligned_access_emulated(NULL); + check_vector_unaligned_access(NULL); return 0; } -#else /* CONFIG_RISCV_PROBE_UNALIGNED_ACCESS */ -static int check_unaligned_access_all_cpus(void) + +/* Measure unaligned access speed on all CPUs present at boot in parallel. */ +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { - check_unaligned_access_emulated_all_cpus(); + schedule_on_each_cpu(check_vector_unaligned_access); + + /* + * Setup hotplug callbacks for any new CPUs that come online or go + * offline. + */ + cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "riscv:online", + riscv_online_cpu_vec, NULL); return 0; } +#else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +{ + return 0; +} #endif +static int check_unaligned_access_all_cpus(void) +{ + bool all_cpus_emulated, all_cpus_vec_unsupported; + + all_cpus_emulated = check_unaligned_access_emulated_all_cpus(); + all_cpus_vec_unsupported = check_vector_unaligned_access_emulated_all_cpus(); + + if (!all_cpus_vec_unsupported && + IS_ENABLED(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS)) { + kthread_run(vec_check_unaligned_access_speed_all_cpus, + NULL, "vec_check_unaligned_access_speed_all_cpus"); + } + + if (!all_cpus_emulated) + return check_unaligned_access_speed_all_cpus(); + + return 0; +} + arch_initcall(check_unaligned_access_all_cpus); diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 3f1c4b2d0b064..9a1b555e87331 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -45,7 +45,7 @@ $(obj)/vdso.o: $(obj)/vdso.so # link rule for the .so file, .lds has to be first $(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE - $(call if_changed,vdsold) + $(call if_changed,vdsold_and_check) LDFLAGS_vdso.so.dbg = -shared -soname=linux-vdso.so.1 \ --build-id=sha1 --hash-style=both --eh-frame-hdr @@ -65,7 +65,8 @@ include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE # actual build commands # The DSO images are built using a special linker script # Make sure only to export the intended __vdso_xxx symbol offsets. -quiet_cmd_vdsold = VDSOLD $@ - cmd_vdsold = $(LD) $(ld_flags) -T $(filter-out FORCE,$^) -o $@.tmp && \ +quiet_cmd_vdsold_and_check = VDSOLD $@ + cmd_vdsold_and_check = $(LD) $(ld_flags) -T $(filter-out FORCE,$^) -o $@.tmp && \ $(OBJCOPY) $(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@ && \ - rm $@.tmp + rm $@.tmp && \ + $(cmd_vdso_check) diff --git a/arch/riscv/kernel/vec-copy-unaligned.S b/arch/riscv/kernel/vec-copy-unaligned.S new file mode 100644 index 0000000000000..d16f19f1b3b65 --- /dev/null +++ b/arch/riscv/kernel/vec-copy-unaligned.S @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2024 Rivos Inc. */ + +#include +#include +#include + + .text + +#define WORD_EEW 32 + +#define WORD_SEW CONCATENATE(e, WORD_EEW) +#define VEC_L CONCATENATE(vle, WORD_EEW).v +#define VEC_S CONCATENATE(vle, WORD_EEW).v + +/* void __riscv_copy_vec_words_unaligned(void *, const void *, size_t) */ +/* Performs a memcpy without aligning buffers, using word loads and stores. */ +/* Note: The size is truncated to a multiple of WORD_EEW */ +SYM_FUNC_START(__riscv_copy_vec_words_unaligned) + andi a4, a2, ~(WORD_EEW-1) + beqz a4, 2f + add a3, a1, a4 + .option push + .option arch, +zve32x +1: + vsetivli t0, 8, WORD_SEW, m8, ta, ma + VEC_L v0, (a1) + VEC_S v0, (a0) + addi a0, a0, WORD_EEW + addi a1, a1, WORD_EEW + bltu a1, a3, 1b + +2: + .option pop + ret +SYM_FUNC_END(__riscv_copy_vec_words_unaligned) + +/* void __riscv_copy_vec_bytes_unaligned(void *, const void *, size_t) */ +/* Performs a memcpy without aligning buffers, using only byte accesses. */ +/* Note: The size is truncated to a multiple of 8 */ +SYM_FUNC_START(__riscv_copy_vec_bytes_unaligned) + andi a4, a2, ~(8-1) + beqz a4, 2f + add a3, a1, a4 + .option push + .option arch, +zve32x +1: + vsetivli t0, 8, e8, m8, ta, ma + vle8.v v0, (a1) + vse8.v v0, (a0) + addi a0, a0, 8 + addi a1, a1, 8 + bltu a1, a3, 1b + +2: + .option pop + ret +SYM_FUNC_END(__riscv_copy_vec_bytes_unaligned) diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index 682b3feee4511..821818886fab0 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -66,7 +66,7 @@ void __init riscv_v_setup_ctx_cache(void) #endif } -static bool insn_is_vector(u32 insn_buf) +bool insn_is_vector(u32 insn_buf) { u32 opcode = insn_buf & __INSN_OPCODE_MASK; u32 width, csr; diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig index 26d1727f0550d..0c3cbb0915ffa 100644 --- a/arch/riscv/kvm/Kconfig +++ b/arch/riscv/kvm/Kconfig @@ -32,6 +32,7 @@ config KVM select KVM_XFER_TO_GUEST_WORK select KVM_GENERIC_MMU_NOTIFIER select SCHED_INFO + select GUEST_PERF_EVENTS if PERF_EVENTS help Support hosting virtualized guest machines. diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index c2cacfbc06a06..0fb1840c3e0ad 100644 --- a/arch/riscv/kvm/Makefile +++ b/arch/riscv/kvm/Makefile @@ -9,27 +9,30 @@ include $(srctree)/virt/kvm/Makefile.kvm obj-$(CONFIG_KVM) += kvm.o +# Ordered alphabetically +kvm-y += aia.o +kvm-y += aia_aplic.o +kvm-y += aia_device.o +kvm-y += aia_imsic.o kvm-y += main.o -kvm-y += vm.o -kvm-y += vmid.o -kvm-y += tlb.o kvm-y += mmu.o +kvm-y += nacl.o +kvm-y += tlb.o kvm-y += vcpu.o kvm-y += vcpu_exit.o kvm-y += vcpu_fp.o -kvm-y += vcpu_vector.o kvm-y += vcpu_insn.o kvm-y += vcpu_onereg.o -kvm-y += vcpu_switch.o +kvm-$(CONFIG_RISCV_PMU_SBI) += vcpu_pmu.o kvm-y += vcpu_sbi.o -kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o kvm-y += vcpu_sbi_base.o -kvm-y += vcpu_sbi_replace.o kvm-y += vcpu_sbi_hsm.o +kvm-$(CONFIG_RISCV_PMU_SBI) += vcpu_sbi_pmu.o +kvm-y += vcpu_sbi_replace.o kvm-y += vcpu_sbi_sta.o +kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o +kvm-y += vcpu_switch.o kvm-y += vcpu_timer.o -kvm-$(CONFIG_RISCV_PMU_SBI) += vcpu_pmu.o vcpu_sbi_pmu.o -kvm-y += aia.o -kvm-y += aia_device.o -kvm-y += aia_aplic.o -kvm-y += aia_imsic.o +kvm-y += vcpu_vector.o +kvm-y += vm.o +kvm-y += vmid.o diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index 2967d305c4427..dcced4db7fe8c 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -16,6 +16,7 @@ #include #include #include +#include struct aia_hgei_control { raw_spinlock_t lock; @@ -51,7 +52,7 @@ static int aia_find_hgei(struct kvm_vcpu *owner) return hgei; } -static void aia_set_hvictl(bool ext_irq_pending) +static inline unsigned long aia_hvictl_value(bool ext_irq_pending) { unsigned long hvictl; @@ -62,7 +63,7 @@ static void aia_set_hvictl(bool ext_irq_pending) hvictl = (IRQ_S_EXT << HVICTL_IID_SHIFT) & HVICTL_IID; hvictl |= ext_irq_pending; - csr_write(CSR_HVICTL, hvictl); + return hvictl; } #ifdef CONFIG_32BIT @@ -88,7 +89,7 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; if (kvm_riscv_aia_available()) - csr->vsieh = csr_read(CSR_VSIEH); + csr->vsieh = ncsr_read(CSR_VSIEH); } #endif @@ -115,7 +116,7 @@ bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) hgei = aia_find_hgei(vcpu); if (hgei > 0) - return !!(csr_read(CSR_HGEIP) & BIT(hgei)); + return !!(ncsr_read(CSR_HGEIP) & BIT(hgei)); return false; } @@ -128,45 +129,73 @@ void kvm_riscv_vcpu_aia_update_hvip(struct kvm_vcpu *vcpu) return; #ifdef CONFIG_32BIT - csr_write(CSR_HVIPH, vcpu->arch.aia_context.guest_csr.hviph); + ncsr_write(CSR_HVIPH, vcpu->arch.aia_context.guest_csr.hviph); #endif - aia_set_hvictl(!!(csr->hvip & BIT(IRQ_VS_EXT))); + ncsr_write(CSR_HVICTL, aia_hvictl_value(!!(csr->hvip & BIT(IRQ_VS_EXT)))); } void kvm_riscv_vcpu_aia_load(struct kvm_vcpu *vcpu, int cpu) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; + void *nsh; if (!kvm_riscv_aia_available()) return; - csr_write(CSR_VSISELECT, csr->vsiselect); - csr_write(CSR_HVIPRIO1, csr->hviprio1); - csr_write(CSR_HVIPRIO2, csr->hviprio2); + if (kvm_riscv_nacl_sync_csr_available()) { + nsh = nacl_shmem(); + nacl_csr_write(nsh, CSR_VSISELECT, csr->vsiselect); + nacl_csr_write(nsh, CSR_HVIPRIO1, csr->hviprio1); + nacl_csr_write(nsh, CSR_HVIPRIO2, csr->hviprio2); +#ifdef CONFIG_32BIT + nacl_csr_write(nsh, CSR_VSIEH, csr->vsieh); + nacl_csr_write(nsh, CSR_HVIPH, csr->hviph); + nacl_csr_write(nsh, CSR_HVIPRIO1H, csr->hviprio1h); + nacl_csr_write(nsh, CSR_HVIPRIO2H, csr->hviprio2h); +#endif + } else { + csr_write(CSR_VSISELECT, csr->vsiselect); + csr_write(CSR_HVIPRIO1, csr->hviprio1); + csr_write(CSR_HVIPRIO2, csr->hviprio2); #ifdef CONFIG_32BIT - csr_write(CSR_VSIEH, csr->vsieh); - csr_write(CSR_HVIPH, csr->hviph); - csr_write(CSR_HVIPRIO1H, csr->hviprio1h); - csr_write(CSR_HVIPRIO2H, csr->hviprio2h); + csr_write(CSR_VSIEH, csr->vsieh); + csr_write(CSR_HVIPH, csr->hviph); + csr_write(CSR_HVIPRIO1H, csr->hviprio1h); + csr_write(CSR_HVIPRIO2H, csr->hviprio2h); #endif + } } void kvm_riscv_vcpu_aia_put(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; + void *nsh; if (!kvm_riscv_aia_available()) return; - csr->vsiselect = csr_read(CSR_VSISELECT); - csr->hviprio1 = csr_read(CSR_HVIPRIO1); - csr->hviprio2 = csr_read(CSR_HVIPRIO2); + if (kvm_riscv_nacl_available()) { + nsh = nacl_shmem(); + csr->vsiselect = nacl_csr_read(nsh, CSR_VSISELECT); + csr->hviprio1 = nacl_csr_read(nsh, CSR_HVIPRIO1); + csr->hviprio2 = nacl_csr_read(nsh, CSR_HVIPRIO2); #ifdef CONFIG_32BIT - csr->vsieh = csr_read(CSR_VSIEH); - csr->hviph = csr_read(CSR_HVIPH); - csr->hviprio1h = csr_read(CSR_HVIPRIO1H); - csr->hviprio2h = csr_read(CSR_HVIPRIO2H); + csr->vsieh = nacl_csr_read(nsh, CSR_VSIEH); + csr->hviph = nacl_csr_read(nsh, CSR_HVIPH); + csr->hviprio1h = nacl_csr_read(nsh, CSR_HVIPRIO1H); + csr->hviprio2h = nacl_csr_read(nsh, CSR_HVIPRIO2H); #endif + } else { + csr->vsiselect = csr_read(CSR_VSISELECT); + csr->hviprio1 = csr_read(CSR_HVIPRIO1); + csr->hviprio2 = csr_read(CSR_HVIPRIO2); +#ifdef CONFIG_32BIT + csr->vsieh = csr_read(CSR_VSIEH); + csr->hviph = csr_read(CSR_HVIPH); + csr->hviprio1h = csr_read(CSR_HVIPRIO1H); + csr->hviprio2h = csr_read(CSR_HVIPRIO2H); +#endif + } } int kvm_riscv_vcpu_aia_get_csr(struct kvm_vcpu *vcpu, @@ -250,20 +279,20 @@ static u8 aia_get_iprio8(struct kvm_vcpu *vcpu, unsigned int irq) switch (bitpos / BITS_PER_LONG) { case 0: - hviprio = csr_read(CSR_HVIPRIO1); + hviprio = ncsr_read(CSR_HVIPRIO1); break; case 1: #ifndef CONFIG_32BIT - hviprio = csr_read(CSR_HVIPRIO2); + hviprio = ncsr_read(CSR_HVIPRIO2); break; #else - hviprio = csr_read(CSR_HVIPRIO1H); + hviprio = ncsr_read(CSR_HVIPRIO1H); break; case 2: - hviprio = csr_read(CSR_HVIPRIO2); + hviprio = ncsr_read(CSR_HVIPRIO2); break; case 3: - hviprio = csr_read(CSR_HVIPRIO2H); + hviprio = ncsr_read(CSR_HVIPRIO2H); break; #endif default: @@ -283,20 +312,20 @@ static void aia_set_iprio8(struct kvm_vcpu *vcpu, unsigned int irq, u8 prio) switch (bitpos / BITS_PER_LONG) { case 0: - hviprio = csr_read(CSR_HVIPRIO1); + hviprio = ncsr_read(CSR_HVIPRIO1); break; case 1: #ifndef CONFIG_32BIT - hviprio = csr_read(CSR_HVIPRIO2); + hviprio = ncsr_read(CSR_HVIPRIO2); break; #else - hviprio = csr_read(CSR_HVIPRIO1H); + hviprio = ncsr_read(CSR_HVIPRIO1H); break; case 2: - hviprio = csr_read(CSR_HVIPRIO2); + hviprio = ncsr_read(CSR_HVIPRIO2); break; case 3: - hviprio = csr_read(CSR_HVIPRIO2H); + hviprio = ncsr_read(CSR_HVIPRIO2H); break; #endif default: @@ -308,20 +337,20 @@ static void aia_set_iprio8(struct kvm_vcpu *vcpu, unsigned int irq, u8 prio) switch (bitpos / BITS_PER_LONG) { case 0: - csr_write(CSR_HVIPRIO1, hviprio); + ncsr_write(CSR_HVIPRIO1, hviprio); break; case 1: #ifndef CONFIG_32BIT - csr_write(CSR_HVIPRIO2, hviprio); + ncsr_write(CSR_HVIPRIO2, hviprio); break; #else - csr_write(CSR_HVIPRIO1H, hviprio); + ncsr_write(CSR_HVIPRIO1H, hviprio); break; case 2: - csr_write(CSR_HVIPRIO2, hviprio); + ncsr_write(CSR_HVIPRIO2, hviprio); break; case 3: - csr_write(CSR_HVIPRIO2H, hviprio); + ncsr_write(CSR_HVIPRIO2H, hviprio); break; #endif default: @@ -377,7 +406,7 @@ int kvm_riscv_vcpu_aia_rmw_ireg(struct kvm_vcpu *vcpu, unsigned int csr_num, return KVM_INSN_ILLEGAL_TRAP; /* First try to emulate in kernel space */ - isel = csr_read(CSR_VSISELECT) & ISELECT_MASK; + isel = ncsr_read(CSR_VSISELECT) & ISELECT_MASK; if (isel >= ISELECT_IPRIO0 && isel <= ISELECT_IPRIO15) return aia_rmw_iprio(vcpu, isel, val, new_val, wr_mask); else if (isel >= IMSIC_FIRST && isel <= IMSIC_LAST && @@ -499,6 +528,10 @@ static int aia_hgei_init(void) hgctrl->free_bitmap = 0; } + /* Skip SGEI interrupt setup for zero guest external interrupts */ + if (!kvm_riscv_aia_nr_hgei) + goto skip_sgei_interrupt; + /* Find INTC irq domain */ domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); @@ -522,11 +555,16 @@ static int aia_hgei_init(void) return rc; } +skip_sgei_interrupt: return 0; } static void aia_hgei_exit(void) { + /* Do nothing for zero guest external interrupts */ + if (!kvm_riscv_aia_nr_hgei) + return; + /* Free per-CPU SGEI interrupt */ free_percpu_irq(hgei_parent_irq, &aia_hgei); } @@ -536,7 +574,7 @@ void kvm_riscv_aia_enable(void) if (!kvm_riscv_aia_available()) return; - aia_set_hvictl(false); + csr_write(CSR_HVICTL, aia_hvictl_value(false)); csr_write(CSR_HVIPRIO1, 0x0); csr_write(CSR_HVIPRIO2, 0x0); #ifdef CONFIG_32BIT @@ -572,7 +610,7 @@ void kvm_riscv_aia_disable(void) csr_clear(CSR_HIE, BIT(IRQ_S_GEXT)); disable_percpu_irq(hgei_parent_irq); - aia_set_hvictl(false); + csr_write(CSR_HVICTL, aia_hvictl_value(false)); raw_spin_lock_irqsave(&hgctrl->lock, flags); diff --git a/arch/riscv/kvm/aia_aplic.c b/arch/riscv/kvm/aia_aplic.c index da6ff1bade0df..f59d1c0c8c43a 100644 --- a/arch/riscv/kvm/aia_aplic.c +++ b/arch/riscv/kvm/aia_aplic.c @@ -143,7 +143,7 @@ static void aplic_write_pending(struct aplic *aplic, u32 irq, bool pending) if (sm == APLIC_SOURCECFG_SM_LEVEL_HIGH || sm == APLIC_SOURCECFG_SM_LEVEL_LOW) { if (!pending) - goto skip_write_pending; + goto noskip_write_pending; if ((irqd->state & APLIC_IRQ_STATE_INPUT) && sm == APLIC_SOURCECFG_SM_LEVEL_LOW) goto skip_write_pending; @@ -152,6 +152,7 @@ static void aplic_write_pending(struct aplic *aplic, u32 irq, bool pending) goto skip_write_pending; } +noskip_write_pending: if (pending) irqd->state |= APLIC_IRQ_STATE_PENDING; else diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c index f3427f6de6080..1fa8be5ee5097 100644 --- a/arch/riscv/kvm/main.c +++ b/arch/riscv/kvm/main.c @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #include long kvm_arch_dev_ioctl(struct file *filp, @@ -22,6 +22,12 @@ long kvm_arch_dev_ioctl(struct file *filp, int kvm_arch_enable_virtualization_cpu(void) { + int rc; + + rc = kvm_riscv_nacl_enable(); + if (rc) + return rc; + csr_write(CSR_HEDELEG, KVM_HEDELEG_DEFAULT); csr_write(CSR_HIDELEG, KVM_HIDELEG_DEFAULT); @@ -49,11 +55,21 @@ void kvm_arch_disable_virtualization_cpu(void) csr_write(CSR_HVIP, 0); csr_write(CSR_HEDELEG, 0); csr_write(CSR_HIDELEG, 0); + + kvm_riscv_nacl_disable(); +} + +static void kvm_riscv_teardown(void) +{ + kvm_riscv_aia_exit(); + kvm_riscv_nacl_exit(); + kvm_unregister_perf_callbacks(); } static int __init riscv_kvm_init(void) { int rc; + char slist[64]; const char *str; if (!riscv_isa_extension_available(NULL, h)) { @@ -71,16 +87,53 @@ static int __init riscv_kvm_init(void) return -ENODEV; } + rc = kvm_riscv_nacl_init(); + if (rc && rc != -ENODEV) + return rc; + kvm_riscv_gstage_mode_detect(); kvm_riscv_gstage_vmid_detect(); rc = kvm_riscv_aia_init(); - if (rc && rc != -ENODEV) + if (rc && rc != -ENODEV) { + kvm_riscv_nacl_exit(); return rc; + } kvm_info("hypervisor extension available\n"); + if (kvm_riscv_nacl_available()) { + rc = 0; + slist[0] = '\0'; + if (kvm_riscv_nacl_sync_csr_available()) { + if (rc) + strcat(slist, ", "); + strcat(slist, "sync_csr"); + rc++; + } + if (kvm_riscv_nacl_sync_hfence_available()) { + if (rc) + strcat(slist, ", "); + strcat(slist, "sync_hfence"); + rc++; + } + if (kvm_riscv_nacl_sync_sret_available()) { + if (rc) + strcat(slist, ", "); + strcat(slist, "sync_sret"); + rc++; + } + if (kvm_riscv_nacl_autoswap_csr_available()) { + if (rc) + strcat(slist, ", "); + strcat(slist, "autoswap_csr"); + rc++; + } + kvm_info("using SBI nested acceleration with %s\n", + (rc) ? slist : "no features"); + } + switch (kvm_riscv_gstage_mode()) { case HGATP_MODE_SV32X4: str = "Sv32x4"; @@ -105,9 +158,11 @@ static int __init riscv_kvm_init(void) kvm_info("AIA available with %d guest external interrupts\n", kvm_riscv_aia_nr_hgei); + kvm_register_perf_callbacks(NULL); + rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); if (rc) { - kvm_riscv_aia_exit(); + kvm_riscv_teardown(); return rc; } @@ -117,7 +172,7 @@ module_init(riscv_kvm_init); static void __exit riscv_kvm_exit(void) { - kvm_riscv_aia_exit(); + kvm_riscv_teardown(); kvm_exit(); } diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index b63650f9b966a..1087ea74567b4 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -601,6 +601,7 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, bool logging = (memslot->dirty_bitmap && !(memslot->flags & KVM_MEM_READONLY)) ? true : false; unsigned long vma_pagesize, mmu_seq; + struct page *page; /* We need minimum second+third level pages */ ret = kvm_mmu_topup_memory_cache(pcache, gstage_pgd_levels); @@ -631,7 +632,7 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, /* * Read mmu_invalidate_seq so that KVM can detect if the results of - * vma_lookup() or gfn_to_pfn_prot() become stale priort to acquiring + * vma_lookup() or __kvm_faultin_pfn() become stale prior to acquiring * kvm->mmu_lock. * * Rely on mmap_read_unlock() for an implicit smp_rmb(), which pairs @@ -647,7 +648,7 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, return -EFAULT; } - hfn = gfn_to_pfn_prot(kvm, gfn, is_write, &writable); + hfn = kvm_faultin_pfn(vcpu, gfn, is_write, &writable, &page); if (hfn == KVM_PFN_ERR_HWPOISON) { send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva, vma_pageshift, current); @@ -669,7 +670,6 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, goto out_unlock; if (writable) { - kvm_set_pfn_dirty(hfn); mark_page_dirty(kvm, gfn); ret = gstage_map_page(kvm, pcache, gpa, hfn << PAGE_SHIFT, vma_pagesize, false, true); @@ -682,9 +682,8 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, kvm_err("Failed to map in G-stage\n"); out_unlock: + kvm_release_faultin_page(kvm, page, ret && ret != -EEXIST, writable); spin_unlock(&kvm->mmu_lock); - kvm_set_pfn_accessed(hfn); - kvm_release_pfn_clean(hfn); return ret; } @@ -732,7 +731,7 @@ void kvm_riscv_gstage_update_hgatp(struct kvm_vcpu *vcpu) hgatp |= (READ_ONCE(k->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID; hgatp |= (k->pgd_phys >> PAGE_SHIFT) & HGATP_PPN; - csr_write(CSR_HGATP, hgatp); + ncsr_write(CSR_HGATP, hgatp); if (!kvm_riscv_gstage_vmid_bits()) kvm_riscv_local_hfence_gvma_all(); diff --git a/arch/riscv/kvm/nacl.c b/arch/riscv/kvm/nacl.c new file mode 100644 index 0000000000000..08a95ad9ada2c --- /dev/null +++ b/arch/riscv/kvm/nacl.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Ventana Micro Systems Inc. + */ + +#include +#include +#include + +DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_available); +DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_csr_available); +DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_hfence_available); +DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_sync_sret_available); +DEFINE_STATIC_KEY_FALSE(kvm_riscv_nacl_autoswap_csr_available); +DEFINE_PER_CPU(struct kvm_riscv_nacl, kvm_riscv_nacl); + +void __kvm_riscv_nacl_hfence(void *shmem, + unsigned long control, + unsigned long page_num, + unsigned long page_count) +{ + int i, ent = -1, try_count = 5; + unsigned long *entp; + +again: + for (i = 0; i < SBI_NACL_SHMEM_HFENCE_ENTRY_MAX; i++) { + entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i); + if (lelong_to_cpu(*entp) & SBI_NACL_SHMEM_HFENCE_CONFIG_PEND) + continue; + + ent = i; + break; + } + + if (ent < 0) { + if (try_count) { + nacl_sync_hfence(-1UL); + goto again; + } else { + pr_warn("KVM: No free entry in NACL shared memory\n"); + return; + } + } + + entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_CONFIG(i); + *entp = cpu_to_lelong(control); + entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PNUM(i); + *entp = cpu_to_lelong(page_num); + entp = shmem + SBI_NACL_SHMEM_HFENCE_ENTRY_PCOUNT(i); + *entp = cpu_to_lelong(page_count); +} + +int kvm_riscv_nacl_enable(void) +{ + int rc; + struct sbiret ret; + struct kvm_riscv_nacl *nacl; + + if (!kvm_riscv_nacl_available()) + return 0; + nacl = this_cpu_ptr(&kvm_riscv_nacl); + + ret = sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM, + nacl->shmem_phys, 0, 0, 0, 0, 0); + rc = sbi_err_map_linux_errno(ret.error); + if (rc) + return rc; + + return 0; +} + +void kvm_riscv_nacl_disable(void) +{ + if (!kvm_riscv_nacl_available()) + return; + + sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_SET_SHMEM, + SBI_SHMEM_DISABLE, SBI_SHMEM_DISABLE, 0, 0, 0, 0); +} + +void kvm_riscv_nacl_exit(void) +{ + int cpu; + struct kvm_riscv_nacl *nacl; + + if (!kvm_riscv_nacl_available()) + return; + + /* Allocate per-CPU shared memory */ + for_each_possible_cpu(cpu) { + nacl = per_cpu_ptr(&kvm_riscv_nacl, cpu); + if (!nacl->shmem) + continue; + + free_pages((unsigned long)nacl->shmem, + get_order(SBI_NACL_SHMEM_SIZE)); + nacl->shmem = NULL; + nacl->shmem_phys = 0; + } +} + +static long nacl_probe_feature(long feature_id) +{ + struct sbiret ret; + + if (!kvm_riscv_nacl_available()) + return 0; + + ret = sbi_ecall(SBI_EXT_NACL, SBI_EXT_NACL_PROBE_FEATURE, + feature_id, 0, 0, 0, 0, 0); + return ret.value; +} + +int kvm_riscv_nacl_init(void) +{ + int cpu; + struct page *shmem_page; + struct kvm_riscv_nacl *nacl; + + if (sbi_spec_version < sbi_mk_version(1, 0) || + sbi_probe_extension(SBI_EXT_NACL) <= 0) + return -ENODEV; + + /* Enable NACL support */ + static_branch_enable(&kvm_riscv_nacl_available); + + /* Probe NACL features */ + if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_CSR)) + static_branch_enable(&kvm_riscv_nacl_sync_csr_available); + if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_HFENCE)) + static_branch_enable(&kvm_riscv_nacl_sync_hfence_available); + if (nacl_probe_feature(SBI_NACL_FEAT_SYNC_SRET)) + static_branch_enable(&kvm_riscv_nacl_sync_sret_available); + if (nacl_probe_feature(SBI_NACL_FEAT_AUTOSWAP_CSR)) + static_branch_enable(&kvm_riscv_nacl_autoswap_csr_available); + + /* Allocate per-CPU shared memory */ + for_each_possible_cpu(cpu) { + nacl = per_cpu_ptr(&kvm_riscv_nacl, cpu); + + shmem_page = alloc_pages(GFP_KERNEL | __GFP_ZERO, + get_order(SBI_NACL_SHMEM_SIZE)); + if (!shmem_page) { + kvm_riscv_nacl_exit(); + return -ENOMEM; + } + nacl->shmem = page_to_virt(shmem_page); + nacl->shmem_phys = page_to_phys(shmem_page); + } + + return 0; +} diff --git a/arch/riscv/kvm/tlb.c b/arch/riscv/kvm/tlb.c index 23c0e82b5103c..2f91ea5f84932 100644 --- a/arch/riscv/kvm/tlb.c +++ b/arch/riscv/kvm/tlb.c @@ -14,6 +14,7 @@ #include #include #include +#include #define has_svinval() riscv_has_extension_unlikely(RISCV_ISA_EXT_SVINVAL) @@ -186,18 +187,24 @@ void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu) void kvm_riscv_hfence_gvma_vmid_all_process(struct kvm_vcpu *vcpu) { - struct kvm_vmid *vmid; + struct kvm_vmid *v = &vcpu->kvm->arch.vmid; + unsigned long vmid = READ_ONCE(v->vmid); - vmid = &vcpu->kvm->arch.vmid; - kvm_riscv_local_hfence_gvma_vmid_all(READ_ONCE(vmid->vmid)); + if (kvm_riscv_nacl_available()) + nacl_hfence_gvma_vmid_all(nacl_shmem(), vmid); + else + kvm_riscv_local_hfence_gvma_vmid_all(vmid); } void kvm_riscv_hfence_vvma_all_process(struct kvm_vcpu *vcpu) { - struct kvm_vmid *vmid; + struct kvm_vmid *v = &vcpu->kvm->arch.vmid; + unsigned long vmid = READ_ONCE(v->vmid); - vmid = &vcpu->kvm->arch.vmid; - kvm_riscv_local_hfence_vvma_all(READ_ONCE(vmid->vmid)); + if (kvm_riscv_nacl_available()) + nacl_hfence_vvma_all(nacl_shmem(), vmid); + else + kvm_riscv_local_hfence_vvma_all(vmid); } static bool vcpu_hfence_dequeue(struct kvm_vcpu *vcpu, @@ -251,6 +258,7 @@ static bool vcpu_hfence_enqueue(struct kvm_vcpu *vcpu, void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu) { + unsigned long vmid; struct kvm_riscv_hfence d = { 0 }; struct kvm_vmid *v = &vcpu->kvm->arch.vmid; @@ -259,26 +267,41 @@ void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu) case KVM_RISCV_HFENCE_UNKNOWN: break; case KVM_RISCV_HFENCE_GVMA_VMID_GPA: - kvm_riscv_local_hfence_gvma_vmid_gpa( - READ_ONCE(v->vmid), - d.addr, d.size, d.order); + vmid = READ_ONCE(v->vmid); + if (kvm_riscv_nacl_available()) + nacl_hfence_gvma_vmid(nacl_shmem(), vmid, + d.addr, d.size, d.order); + else + kvm_riscv_local_hfence_gvma_vmid_gpa(vmid, d.addr, + d.size, d.order); break; case KVM_RISCV_HFENCE_VVMA_ASID_GVA: kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD); - kvm_riscv_local_hfence_vvma_asid_gva( - READ_ONCE(v->vmid), d.asid, - d.addr, d.size, d.order); + vmid = READ_ONCE(v->vmid); + if (kvm_riscv_nacl_available()) + nacl_hfence_vvma_asid(nacl_shmem(), vmid, d.asid, + d.addr, d.size, d.order); + else + kvm_riscv_local_hfence_vvma_asid_gva(vmid, d.asid, d.addr, + d.size, d.order); break; case KVM_RISCV_HFENCE_VVMA_ASID_ALL: kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD); - kvm_riscv_local_hfence_vvma_asid_all( - READ_ONCE(v->vmid), d.asid); + vmid = READ_ONCE(v->vmid); + if (kvm_riscv_nacl_available()) + nacl_hfence_vvma_asid_all(nacl_shmem(), vmid, d.asid); + else + kvm_riscv_local_hfence_vvma_asid_all(vmid, d.asid); break; case KVM_RISCV_HFENCE_VVMA_GVA: kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_RCVD); - kvm_riscv_local_hfence_vvma_gva( - READ_ONCE(v->vmid), - d.addr, d.size, d.order); + vmid = READ_ONCE(v->vmid); + if (kvm_riscv_nacl_available()) + nacl_hfence_vvma(nacl_shmem(), vmid, + d.addr, d.size, d.order); + else + kvm_riscv_local_hfence_vvma_gva(vmid, d.addr, + d.size, d.order); break; default: break; diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index 8d7d381737ee5..e048dcc6e65e7 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -17,8 +17,8 @@ #include #include #include -#include #include +#include #include #define CREATE_TRACE_POINTS @@ -226,6 +226,13 @@ bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) return (vcpu->arch.guest_context.sstatus & SR_SPP) ? true : false; } +#ifdef CONFIG_GUEST_PERF_EVENTS +unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.guest_context.sepc; +} +#endif + vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) { return VM_FAULT_SIGBUS; @@ -361,10 +368,10 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; /* Read current HVIP and VSIE CSRs */ - csr->vsie = csr_read(CSR_VSIE); + csr->vsie = ncsr_read(CSR_VSIE); /* Sync-up HVIP.VSSIP bit changes does by Guest */ - hvip = csr_read(CSR_HVIP); + hvip = ncsr_read(CSR_HVIP); if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) { if (hvip & (1UL << IRQ_VS_SOFT)) { if (!test_and_set_bit(IRQ_VS_SOFT, @@ -544,6 +551,10 @@ static void kvm_riscv_vcpu_setup_config(struct kvm_vcpu *vcpu) if (riscv_isa_extension_available(isa, ZICBOZ)) cfg->henvcfg |= ENVCFG_CBZE; + if (riscv_isa_extension_available(isa, SVADU) && + !riscv_isa_extension_available(isa, SVADE)) + cfg->henvcfg |= ENVCFG_ADUE; + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { cfg->hstateen0 |= SMSTATEEN0_HSENVCFG; if (riscv_isa_extension_available(isa, SSAIA)) @@ -561,26 +572,49 @@ static void kvm_riscv_vcpu_setup_config(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + void *nsh; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; - csr_write(CSR_VSSTATUS, csr->vsstatus); - csr_write(CSR_VSIE, csr->vsie); - csr_write(CSR_VSTVEC, csr->vstvec); - csr_write(CSR_VSSCRATCH, csr->vsscratch); - csr_write(CSR_VSEPC, csr->vsepc); - csr_write(CSR_VSCAUSE, csr->vscause); - csr_write(CSR_VSTVAL, csr->vstval); - csr_write(CSR_HEDELEG, cfg->hedeleg); - csr_write(CSR_HVIP, csr->hvip); - csr_write(CSR_VSATP, csr->vsatp); - csr_write(CSR_HENVCFG, cfg->henvcfg); - if (IS_ENABLED(CONFIG_32BIT)) - csr_write(CSR_HENVCFGH, cfg->henvcfg >> 32); - if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { - csr_write(CSR_HSTATEEN0, cfg->hstateen0); + if (kvm_riscv_nacl_sync_csr_available()) { + nsh = nacl_shmem(); + nacl_csr_write(nsh, CSR_VSSTATUS, csr->vsstatus); + nacl_csr_write(nsh, CSR_VSIE, csr->vsie); + nacl_csr_write(nsh, CSR_VSTVEC, csr->vstvec); + nacl_csr_write(nsh, CSR_VSSCRATCH, csr->vsscratch); + nacl_csr_write(nsh, CSR_VSEPC, csr->vsepc); + nacl_csr_write(nsh, CSR_VSCAUSE, csr->vscause); + nacl_csr_write(nsh, CSR_VSTVAL, csr->vstval); + nacl_csr_write(nsh, CSR_HEDELEG, cfg->hedeleg); + nacl_csr_write(nsh, CSR_HVIP, csr->hvip); + nacl_csr_write(nsh, CSR_VSATP, csr->vsatp); + nacl_csr_write(nsh, CSR_HENVCFG, cfg->henvcfg); + if (IS_ENABLED(CONFIG_32BIT)) + nacl_csr_write(nsh, CSR_HENVCFGH, cfg->henvcfg >> 32); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { + nacl_csr_write(nsh, CSR_HSTATEEN0, cfg->hstateen0); + if (IS_ENABLED(CONFIG_32BIT)) + nacl_csr_write(nsh, CSR_HSTATEEN0H, cfg->hstateen0 >> 32); + } + } else { + csr_write(CSR_VSSTATUS, csr->vsstatus); + csr_write(CSR_VSIE, csr->vsie); + csr_write(CSR_VSTVEC, csr->vstvec); + csr_write(CSR_VSSCRATCH, csr->vsscratch); + csr_write(CSR_VSEPC, csr->vsepc); + csr_write(CSR_VSCAUSE, csr->vscause); + csr_write(CSR_VSTVAL, csr->vstval); + csr_write(CSR_HEDELEG, cfg->hedeleg); + csr_write(CSR_HVIP, csr->hvip); + csr_write(CSR_VSATP, csr->vsatp); + csr_write(CSR_HENVCFG, cfg->henvcfg); if (IS_ENABLED(CONFIG_32BIT)) - csr_write(CSR_HSTATEEN0H, cfg->hstateen0 >> 32); + csr_write(CSR_HENVCFGH, cfg->henvcfg >> 32); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { + csr_write(CSR_HSTATEEN0, cfg->hstateen0); + if (IS_ENABLED(CONFIG_32BIT)) + csr_write(CSR_HSTATEEN0H, cfg->hstateen0 >> 32); + } } kvm_riscv_gstage_update_hgatp(vcpu); @@ -603,6 +637,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) { + void *nsh; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; vcpu->cpu = -1; @@ -618,15 +653,28 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) vcpu->arch.isa); kvm_riscv_vcpu_host_vector_restore(&vcpu->arch.host_context); - csr->vsstatus = csr_read(CSR_VSSTATUS); - csr->vsie = csr_read(CSR_VSIE); - csr->vstvec = csr_read(CSR_VSTVEC); - csr->vsscratch = csr_read(CSR_VSSCRATCH); - csr->vsepc = csr_read(CSR_VSEPC); - csr->vscause = csr_read(CSR_VSCAUSE); - csr->vstval = csr_read(CSR_VSTVAL); - csr->hvip = csr_read(CSR_HVIP); - csr->vsatp = csr_read(CSR_VSATP); + if (kvm_riscv_nacl_available()) { + nsh = nacl_shmem(); + csr->vsstatus = nacl_csr_read(nsh, CSR_VSSTATUS); + csr->vsie = nacl_csr_read(nsh, CSR_VSIE); + csr->vstvec = nacl_csr_read(nsh, CSR_VSTVEC); + csr->vsscratch = nacl_csr_read(nsh, CSR_VSSCRATCH); + csr->vsepc = nacl_csr_read(nsh, CSR_VSEPC); + csr->vscause = nacl_csr_read(nsh, CSR_VSCAUSE); + csr->vstval = nacl_csr_read(nsh, CSR_VSTVAL); + csr->hvip = nacl_csr_read(nsh, CSR_HVIP); + csr->vsatp = nacl_csr_read(nsh, CSR_VSATP); + } else { + csr->vsstatus = csr_read(CSR_VSSTATUS); + csr->vsie = csr_read(CSR_VSIE); + csr->vstvec = csr_read(CSR_VSTVEC); + csr->vsscratch = csr_read(CSR_VSSCRATCH); + csr->vsepc = csr_read(CSR_VSEPC); + csr->vscause = csr_read(CSR_VSCAUSE); + csr->vstval = csr_read(CSR_VSTVAL); + csr->hvip = csr_read(CSR_HVIP); + csr->vsatp = csr_read(CSR_VSATP); + } } static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu) @@ -681,7 +729,7 @@ static void kvm_riscv_update_hvip(struct kvm_vcpu *vcpu) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - csr_write(CSR_HVIP, csr->hvip); + ncsr_write(CSR_HVIP, csr->hvip); kvm_riscv_vcpu_aia_update_hvip(vcpu); } @@ -691,6 +739,7 @@ static __always_inline void kvm_riscv_vcpu_swap_in_guest_state(struct kvm_vcpu * struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; + vcpu->arch.host_scounteren = csr_swap(CSR_SCOUNTEREN, csr->scounteren); vcpu->arch.host_senvcfg = csr_swap(CSR_SENVCFG, csr->senvcfg); if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN) && (cfg->hstateen0 & SMSTATEEN0_SSTATEEN0)) @@ -704,6 +753,7 @@ static __always_inline void kvm_riscv_vcpu_swap_in_host_state(struct kvm_vcpu *v struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; + csr->scounteren = csr_swap(CSR_SCOUNTEREN, vcpu->arch.host_scounteren); csr->senvcfg = csr_swap(CSR_SENVCFG, vcpu->arch.host_senvcfg); if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN) && (cfg->hstateen0 & SMSTATEEN0_SSTATEEN0)) @@ -718,11 +768,81 @@ static __always_inline void kvm_riscv_vcpu_swap_in_host_state(struct kvm_vcpu *v * This must be noinstr as instrumentation may make use of RCU, and this is not * safe during the EQS. */ -static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu) +static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu, + struct kvm_cpu_trap *trap) { + void *nsh; + struct kvm_cpu_context *gcntx = &vcpu->arch.guest_context; + struct kvm_cpu_context *hcntx = &vcpu->arch.host_context; + + /* + * We save trap CSRs (such as SEPC, SCAUSE, STVAL, HTVAL, and + * HTINST) here because we do local_irq_enable() after this + * function in kvm_arch_vcpu_ioctl_run() which can result in + * an interrupt immediately after local_irq_enable() and can + * potentially change trap CSRs. + */ + kvm_riscv_vcpu_swap_in_guest_state(vcpu); guest_state_enter_irqoff(); - __kvm_riscv_switch_to(&vcpu->arch); + + if (kvm_riscv_nacl_sync_sret_available()) { + nsh = nacl_shmem(); + + if (kvm_riscv_nacl_autoswap_csr_available()) { + hcntx->hstatus = + nacl_csr_read(nsh, CSR_HSTATUS); + nacl_scratch_write_long(nsh, + SBI_NACL_SHMEM_AUTOSWAP_OFFSET + + SBI_NACL_SHMEM_AUTOSWAP_HSTATUS, + gcntx->hstatus); + nacl_scratch_write_long(nsh, + SBI_NACL_SHMEM_AUTOSWAP_OFFSET, + SBI_NACL_SHMEM_AUTOSWAP_FLAG_HSTATUS); + } else if (kvm_riscv_nacl_sync_csr_available()) { + hcntx->hstatus = nacl_csr_swap(nsh, + CSR_HSTATUS, gcntx->hstatus); + } else { + hcntx->hstatus = csr_swap(CSR_HSTATUS, gcntx->hstatus); + } + + nacl_scratch_write_longs(nsh, + SBI_NACL_SHMEM_SRET_OFFSET + + SBI_NACL_SHMEM_SRET_X(1), + &gcntx->ra, + SBI_NACL_SHMEM_SRET_X_LAST); + + __kvm_riscv_nacl_switch_to(&vcpu->arch, SBI_EXT_NACL, + SBI_EXT_NACL_SYNC_SRET); + + if (kvm_riscv_nacl_autoswap_csr_available()) { + nacl_scratch_write_long(nsh, + SBI_NACL_SHMEM_AUTOSWAP_OFFSET, + 0); + gcntx->hstatus = nacl_scratch_read_long(nsh, + SBI_NACL_SHMEM_AUTOSWAP_OFFSET + + SBI_NACL_SHMEM_AUTOSWAP_HSTATUS); + } else { + gcntx->hstatus = csr_swap(CSR_HSTATUS, hcntx->hstatus); + } + + trap->htval = nacl_csr_read(nsh, CSR_HTVAL); + trap->htinst = nacl_csr_read(nsh, CSR_HTINST); + } else { + hcntx->hstatus = csr_swap(CSR_HSTATUS, gcntx->hstatus); + + __kvm_riscv_switch_to(&vcpu->arch); + + gcntx->hstatus = csr_swap(CSR_HSTATUS, hcntx->hstatus); + + trap->htval = csr_read(CSR_HTVAL); + trap->htinst = csr_read(CSR_HTINST); + } + + trap->sepc = gcntx->sepc; + trap->scause = csr_read(CSR_SCAUSE); + trap->stval = csr_read(CSR_STVAL); + vcpu->arch.last_exit_cpu = vcpu->cpu; guest_state_exit_irqoff(); kvm_riscv_vcpu_swap_in_host_state(vcpu); @@ -839,22 +959,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) guest_timing_enter_irqoff(); - kvm_riscv_vcpu_enter_exit(vcpu); + kvm_riscv_vcpu_enter_exit(vcpu, &trap); vcpu->mode = OUTSIDE_GUEST_MODE; vcpu->stat.exits++; - /* - * Save SCAUSE, STVAL, HTVAL, and HTINST because we might - * get an interrupt between __kvm_riscv_switch_to() and - * local_irq_enable() which can potentially change CSRs. - */ - trap.sepc = vcpu->arch.guest_context.sepc; - trap.scause = csr_read(CSR_SCAUSE); - trap.stval = csr_read(CSR_STVAL); - trap.htval = csr_read(CSR_HTVAL); - trap.htinst = csr_read(CSR_HTINST); - /* Syncup interrupts state with HW */ kvm_riscv_vcpu_sync_interrupts(vcpu); diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index b319c4c13c54c..753f66c8b70a7 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define KVM_RISCV_BASE_ISA_MASK GENMASK(25, 0) @@ -34,10 +35,14 @@ static const unsigned long kvm_isa_ext_arr[] = { [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, /* Multi letter extensions (alphabetically sorted) */ + [KVM_RISCV_ISA_EXT_SMNPM] = RISCV_ISA_EXT_SSNPM, KVM_ISA_EXT_ARR(SMSTATEEN), KVM_ISA_EXT_ARR(SSAIA), KVM_ISA_EXT_ARR(SSCOFPMF), + KVM_ISA_EXT_ARR(SSNPM), KVM_ISA_EXT_ARR(SSTC), + KVM_ISA_EXT_ARR(SVADE), + KVM_ISA_EXT_ARR(SVADU), KVM_ISA_EXT_ARR(SVINVAL), KVM_ISA_EXT_ARR(SVNAPOT), KVM_ISA_EXT_ARR(SVPBMT), @@ -110,6 +115,12 @@ static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext) case KVM_RISCV_ISA_EXT_SSCOFPMF: /* Sscofpmf depends on interrupt filtering defined in ssaia */ return __riscv_isa_extension_available(NULL, RISCV_ISA_EXT_SSAIA); + case KVM_RISCV_ISA_EXT_SVADU: + /* + * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero. + * Guest OS can use Svadu only when host OS enable Svadu. + */ + return arch_has_hw_pte_young(); case KVM_RISCV_ISA_EXT_V: return riscv_v_vstate_ctrl_user_allowed(); default: @@ -127,8 +138,10 @@ static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) case KVM_RISCV_ISA_EXT_C: case KVM_RISCV_ISA_EXT_I: case KVM_RISCV_ISA_EXT_M: + case KVM_RISCV_ISA_EXT_SMNPM: /* There is not architectural config bit to disable sscofpmf completely */ case KVM_RISCV_ISA_EXT_SSCOFPMF: + case KVM_RISCV_ISA_EXT_SSNPM: case KVM_RISCV_ISA_EXT_SSTC: case KVM_RISCV_ISA_EXT_SVINVAL: case KVM_RISCV_ISA_EXT_SVNAPOT: @@ -181,6 +194,12 @@ static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) /* Extensions which can be disabled using Smstateen */ case KVM_RISCV_ISA_EXT_SSAIA: return riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN); + case KVM_RISCV_ISA_EXT_SVADE: + /* + * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero. + * Svade is not allowed to disable when the platform use Svade. + */ + return arch_has_hw_pte_young(); default: break; } diff --git a/arch/riscv/kvm/vcpu_sbi.c b/arch/riscv/kvm/vcpu_sbi.c index 7de128be8db9b..6e704ed86a83a 100644 --- a/arch/riscv/kvm/vcpu_sbi.c +++ b/arch/riscv/kvm/vcpu_sbi.c @@ -486,19 +486,22 @@ void kvm_riscv_vcpu_sbi_init(struct kvm_vcpu *vcpu) struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; const struct kvm_riscv_sbi_extension_entry *entry; const struct kvm_vcpu_sbi_extension *ext; - int i; + int idx, i; for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { entry = &sbi_ext[i]; ext = entry->ext_ptr; + idx = entry->ext_idx; + + if (idx < 0 || idx >= ARRAY_SIZE(scontext->ext_status)) + continue; if (ext->probe && !ext->probe(vcpu)) { - scontext->ext_status[entry->ext_idx] = - KVM_RISCV_SBI_EXT_STATUS_UNAVAILABLE; + scontext->ext_status[idx] = KVM_RISCV_SBI_EXT_STATUS_UNAVAILABLE; continue; } - scontext->ext_status[entry->ext_idx] = ext->default_disabled ? + scontext->ext_status[idx] = ext->default_disabled ? KVM_RISCV_SBI_EXT_STATUS_DISABLED : KVM_RISCV_SBI_EXT_STATUS_ENABLED; } diff --git a/arch/riscv/kvm/vcpu_switch.S b/arch/riscv/kvm/vcpu_switch.S index 0c26189aa01cf..47686bcb21e0a 100644 --- a/arch/riscv/kvm/vcpu_switch.S +++ b/arch/riscv/kvm/vcpu_switch.S @@ -11,11 +11,7 @@ #include #include - .text - .altmacro - .option norelax - -SYM_FUNC_START(__kvm_riscv_switch_to) +.macro SAVE_HOST_GPRS /* Save Host GPRs (except A0 and T0-T6) */ REG_S ra, (KVM_ARCH_HOST_RA)(a0) REG_S sp, (KVM_ARCH_HOST_SP)(a0) @@ -40,39 +36,33 @@ SYM_FUNC_START(__kvm_riscv_switch_to) REG_S s9, (KVM_ARCH_HOST_S9)(a0) REG_S s10, (KVM_ARCH_HOST_S10)(a0) REG_S s11, (KVM_ARCH_HOST_S11)(a0) +.endm +.macro SAVE_HOST_AND_RESTORE_GUEST_CSRS __resume_addr /* Load Guest CSR values */ REG_L t0, (KVM_ARCH_GUEST_SSTATUS)(a0) - REG_L t1, (KVM_ARCH_GUEST_HSTATUS)(a0) - REG_L t2, (KVM_ARCH_GUEST_SCOUNTEREN)(a0) - la t4, .Lkvm_switch_return - REG_L t5, (KVM_ARCH_GUEST_SEPC)(a0) + la t1, \__resume_addr + REG_L t2, (KVM_ARCH_GUEST_SEPC)(a0) /* Save Host and Restore Guest SSTATUS */ csrrw t0, CSR_SSTATUS, t0 - /* Save Host and Restore Guest HSTATUS */ - csrrw t1, CSR_HSTATUS, t1 - - /* Save Host and Restore Guest SCOUNTEREN */ - csrrw t2, CSR_SCOUNTEREN, t2 - /* Save Host STVEC and change it to return path */ - csrrw t4, CSR_STVEC, t4 + csrrw t1, CSR_STVEC, t1 + + /* Restore Guest SEPC */ + csrw CSR_SEPC, t2 /* Save Host SSCRATCH and change it to struct kvm_vcpu_arch pointer */ csrrw t3, CSR_SSCRATCH, a0 - /* Restore Guest SEPC */ - csrw CSR_SEPC, t5 - /* Store Host CSR values */ REG_S t0, (KVM_ARCH_HOST_SSTATUS)(a0) - REG_S t1, (KVM_ARCH_HOST_HSTATUS)(a0) - REG_S t2, (KVM_ARCH_HOST_SCOUNTEREN)(a0) + REG_S t1, (KVM_ARCH_HOST_STVEC)(a0) REG_S t3, (KVM_ARCH_HOST_SSCRATCH)(a0) - REG_S t4, (KVM_ARCH_HOST_STVEC)(a0) +.endm +.macro RESTORE_GUEST_GPRS /* Restore Guest GPRs (except A0) */ REG_L ra, (KVM_ARCH_GUEST_RA)(a0) REG_L sp, (KVM_ARCH_GUEST_SP)(a0) @@ -107,13 +97,9 @@ SYM_FUNC_START(__kvm_riscv_switch_to) /* Restore Guest A0 */ REG_L a0, (KVM_ARCH_GUEST_A0)(a0) +.endm - /* Resume Guest */ - sret - - /* Back to Host */ - .align 2 -.Lkvm_switch_return: +.macro SAVE_GUEST_GPRS /* Swap Guest A0 with SSCRATCH */ csrrw a0, CSR_SSCRATCH, a0 @@ -148,39 +134,33 @@ SYM_FUNC_START(__kvm_riscv_switch_to) REG_S t4, (KVM_ARCH_GUEST_T4)(a0) REG_S t5, (KVM_ARCH_GUEST_T5)(a0) REG_S t6, (KVM_ARCH_GUEST_T6)(a0) +.endm +.macro SAVE_GUEST_AND_RESTORE_HOST_CSRS /* Load Host CSR values */ - REG_L t1, (KVM_ARCH_HOST_STVEC)(a0) - REG_L t2, (KVM_ARCH_HOST_SSCRATCH)(a0) - REG_L t3, (KVM_ARCH_HOST_SCOUNTEREN)(a0) - REG_L t4, (KVM_ARCH_HOST_HSTATUS)(a0) - REG_L t5, (KVM_ARCH_HOST_SSTATUS)(a0) - - /* Save Guest SEPC */ - csrr t0, CSR_SEPC + REG_L t0, (KVM_ARCH_HOST_STVEC)(a0) + REG_L t1, (KVM_ARCH_HOST_SSCRATCH)(a0) + REG_L t2, (KVM_ARCH_HOST_SSTATUS)(a0) /* Save Guest A0 and Restore Host SSCRATCH */ - csrrw t2, CSR_SSCRATCH, t2 + csrrw t1, CSR_SSCRATCH, t1 - /* Restore Host STVEC */ - csrw CSR_STVEC, t1 - - /* Save Guest and Restore Host SCOUNTEREN */ - csrrw t3, CSR_SCOUNTEREN, t3 + /* Save Guest SEPC */ + csrr t3, CSR_SEPC - /* Save Guest and Restore Host HSTATUS */ - csrrw t4, CSR_HSTATUS, t4 + /* Restore Host STVEC */ + csrw CSR_STVEC, t0 /* Save Guest and Restore Host SSTATUS */ - csrrw t5, CSR_SSTATUS, t5 + csrrw t2, CSR_SSTATUS, t2 /* Store Guest CSR values */ - REG_S t0, (KVM_ARCH_GUEST_SEPC)(a0) - REG_S t2, (KVM_ARCH_GUEST_A0)(a0) - REG_S t3, (KVM_ARCH_GUEST_SCOUNTEREN)(a0) - REG_S t4, (KVM_ARCH_GUEST_HSTATUS)(a0) - REG_S t5, (KVM_ARCH_GUEST_SSTATUS)(a0) + REG_S t1, (KVM_ARCH_GUEST_A0)(a0) + REG_S t2, (KVM_ARCH_GUEST_SSTATUS)(a0) + REG_S t3, (KVM_ARCH_GUEST_SEPC)(a0) +.endm +.macro RESTORE_HOST_GPRS /* Restore Host GPRs (except A0 and T0-T6) */ REG_L ra, (KVM_ARCH_HOST_RA)(a0) REG_L sp, (KVM_ARCH_HOST_SP)(a0) @@ -205,11 +185,68 @@ SYM_FUNC_START(__kvm_riscv_switch_to) REG_L s9, (KVM_ARCH_HOST_S9)(a0) REG_L s10, (KVM_ARCH_HOST_S10)(a0) REG_L s11, (KVM_ARCH_HOST_S11)(a0) +.endm + + .text + .altmacro + .option norelax + + /* + * Parameters: + * A0 <= Pointer to struct kvm_vcpu_arch + */ +SYM_FUNC_START(__kvm_riscv_switch_to) + SAVE_HOST_GPRS + + SAVE_HOST_AND_RESTORE_GUEST_CSRS .Lkvm_switch_return + + RESTORE_GUEST_GPRS + + /* Resume Guest using SRET */ + sret + + /* Back to Host */ + .align 2 +.Lkvm_switch_return: + SAVE_GUEST_GPRS + + SAVE_GUEST_AND_RESTORE_HOST_CSRS + + RESTORE_HOST_GPRS /* Return to C code */ ret SYM_FUNC_END(__kvm_riscv_switch_to) + /* + * Parameters: + * A0 <= Pointer to struct kvm_vcpu_arch + * A1 <= SBI extension ID + * A2 <= SBI function ID + */ +SYM_FUNC_START(__kvm_riscv_nacl_switch_to) + SAVE_HOST_GPRS + + SAVE_HOST_AND_RESTORE_GUEST_CSRS .Lkvm_nacl_switch_return + + /* Resume Guest using SBI nested acceleration */ + add a6, a2, zero + add a7, a1, zero + ecall + + /* Back to Host */ + .align 2 +.Lkvm_nacl_switch_return: + SAVE_GUEST_GPRS + + SAVE_GUEST_AND_RESTORE_HOST_CSRS + + RESTORE_HOST_GPRS + + /* Return to C code */ + ret +SYM_FUNC_END(__kvm_riscv_nacl_switch_to) + SYM_CODE_START(__kvm_riscv_unpriv_trap) /* * We assume that faulting unpriv load/store instruction is diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index 75486b25ac45a..96e7a4e463f7f 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include #include static u64 kvm_riscv_current_cycles(struct kvm_guest_timer *gt) @@ -72,12 +72,12 @@ static int kvm_riscv_vcpu_timer_cancel(struct kvm_vcpu_timer *t) static int kvm_riscv_vcpu_update_vstimecmp(struct kvm_vcpu *vcpu, u64 ncycles) { #if defined(CONFIG_32BIT) - csr_write(CSR_VSTIMECMP, ncycles & 0xFFFFFFFF); - csr_write(CSR_VSTIMECMPH, ncycles >> 32); + ncsr_write(CSR_VSTIMECMP, ncycles & 0xFFFFFFFF); + ncsr_write(CSR_VSTIMECMPH, ncycles >> 32); #else - csr_write(CSR_VSTIMECMP, ncycles); + ncsr_write(CSR_VSTIMECMP, ncycles); #endif - return 0; + return 0; } static int kvm_riscv_vcpu_update_hrtimer(struct kvm_vcpu *vcpu, u64 ncycles) @@ -289,10 +289,10 @@ static void kvm_riscv_vcpu_update_timedelta(struct kvm_vcpu *vcpu) struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; #if defined(CONFIG_32BIT) - csr_write(CSR_HTIMEDELTA, (u32)(gt->time_delta)); - csr_write(CSR_HTIMEDELTAH, (u32)(gt->time_delta >> 32)); + ncsr_write(CSR_HTIMEDELTA, (u32)(gt->time_delta)); + ncsr_write(CSR_HTIMEDELTAH, (u32)(gt->time_delta >> 32)); #else - csr_write(CSR_HTIMEDELTA, gt->time_delta); + ncsr_write(CSR_HTIMEDELTA, gt->time_delta); #endif } @@ -306,10 +306,10 @@ void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu) return; #if defined(CONFIG_32BIT) - csr_write(CSR_VSTIMECMP, (u32)t->next_cycles); - csr_write(CSR_VSTIMECMPH, (u32)(t->next_cycles >> 32)); + ncsr_write(CSR_VSTIMECMP, (u32)t->next_cycles); + ncsr_write(CSR_VSTIMECMPH, (u32)(t->next_cycles >> 32)); #else - csr_write(CSR_VSTIMECMP, t->next_cycles); + ncsr_write(CSR_VSTIMECMP, t->next_cycles); #endif /* timer should be enabled for the remaining operations */ @@ -327,10 +327,10 @@ void kvm_riscv_vcpu_timer_sync(struct kvm_vcpu *vcpu) return; #if defined(CONFIG_32BIT) - t->next_cycles = csr_read(CSR_VSTIMECMP); - t->next_cycles |= (u64)csr_read(CSR_VSTIMECMPH) << 32; + t->next_cycles = ncsr_read(CSR_VSTIMECMP); + t->next_cycles |= (u64)ncsr_read(CSR_VSTIMECMPH) << 32; #else - t->next_cycles = csr_read(CSR_VSTIMECMP); + t->next_cycles = ncsr_read(CSR_VSTIMECMP); #endif } diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index c64b2987d1081..0077969170e8b 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -87,6 +87,7 @@ config S390 select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_MEM_ENCRYPT select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS + select ARCH_HAS_PREEMPT_LAZY select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SCALED_CPUTIME select ARCH_HAS_SET_DIRECT_MAP @@ -218,6 +219,7 @@ config S390 select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP + select HAVE_PREEMPT_DYNAMIC_KEY select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RELIABLE_STACKTRACE select HAVE_RETHOOK diff --git a/arch/s390/include/asm/debug.h b/arch/s390/include/asm/debug.h index ccd4e148b5ed4..a7f7bdc9e19ca 100644 --- a/arch/s390/include/asm/debug.h +++ b/arch/s390/include/asm/debug.h @@ -66,14 +66,15 @@ typedef int (debug_header_proc_t) (debug_info_t *id, struct debug_view *view, int area, debug_entry_t *entry, - char *out_buf); + char *out_buf, size_t out_buf_size); typedef int (debug_format_proc_t) (debug_info_t *id, struct debug_view *view, char *out_buf, + size_t out_buf_size, const char *in_buf); typedef int (debug_prolog_proc_t) (debug_info_t *id, struct debug_view *view, - char *out_buf); + char *out_buf, size_t out_buf_size); typedef int (debug_input_proc_t) (debug_info_t *id, struct debug_view *view, struct file *file, @@ -81,7 +82,8 @@ typedef int (debug_input_proc_t) (debug_info_t *id, size_t in_buf_size, loff_t *offset); int debug_dflt_header_fn(debug_info_t *id, struct debug_view *view, - int area, debug_entry_t *entry, char *out_buf); + int area, debug_entry_t *entry, + char *out_buf, size_t out_buf_size); struct debug_view { char name[DEBUG_MAX_NAME_LEN]; diff --git a/arch/s390/include/asm/gmap.h b/arch/s390/include/asm/gmap.h index 64761c78f7747..13f51a6a5bb1b 100644 --- a/arch/s390/include/asm/gmap.h +++ b/arch/s390/include/asm/gmap.h @@ -17,8 +17,8 @@ #define GMAP_NOTIFY_MPROT 0x1 /* Status bits only for huge segment entries */ -#define _SEGMENT_ENTRY_GMAP_IN 0x8000 /* invalidation notify bit */ -#define _SEGMENT_ENTRY_GMAP_UC 0x4000 /* dirty (migration) */ +#define _SEGMENT_ENTRY_GMAP_IN 0x0800 /* invalidation notify bit */ +#define _SEGMENT_ENTRY_GMAP_UC 0x0002 /* dirty (migration) */ /** * struct gmap_struct - guest address space diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h index 6f815d4ba0cab..a40664b236e91 100644 --- a/arch/s390/include/asm/hugetlb.h +++ b/arch/s390/include/asm/hugetlb.h @@ -10,6 +10,8 @@ #define _ASM_S390_HUGETLB_H #include +#include +#include #include #define hugepages_supported() (MACHINE_HAS_EDAT1) @@ -78,7 +80,7 @@ static inline int huge_pte_none(pte_t pte) #define __HAVE_ARCH_HUGE_PTE_NONE_MOSTLY static inline int huge_pte_none_mostly(pte_t pte) { - return huge_pte_none(pte); + return huge_pte_none(pte) || is_pte_marker(pte); } #define __HAVE_ARCH_HUGE_PTE_MKUFFD_WP diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 51201b4ac93a5..97c7c81275434 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -94,11 +94,16 @@ union ipte_control { }; }; +/* + * Utility is defined as two bytes but having it four bytes wide + * generates more efficient code. Since the following bytes are + * reserved this makes no functional difference. + */ union sca_utility { - __u16 val; + __u32 val; struct { - __u16 mtcr : 1; - __u16 reserved : 15; + __u32 mtcr : 1; + __u32 : 31; }; }; @@ -107,7 +112,7 @@ struct bsca_block { __u64 reserved[5]; __u64 mcn; union sca_utility utility; - __u8 reserved2[6]; + __u8 reserved2[4]; struct bsca_entry cpu[KVM_S390_BSCA_CPU_SLOTS]; }; @@ -115,7 +120,7 @@ struct esca_block { union ipte_control ipte_control; __u64 reserved1[6]; union sca_utility utility; - __u8 reserved2[6]; + __u8 reserved2[4]; __u64 mcn[4]; __u64 reserved3[20]; struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS]; @@ -356,6 +361,7 @@ struct kvm_s390_sie_block { #define ECD_MEF 0x08000000 #define ECD_ETOKENF 0x02000000 #define ECD_ECC 0x00200000 +#define ECD_HMAC 0x00004000 __u32 ecd; /* 0x01c8 */ __u8 reserved1cc[18]; /* 0x01cc */ __u64 pp; /* 0x01de */ diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 8b67036edb697..48268095b0a35 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -277,7 +277,8 @@ static inline int is_module_addr(void *addr) #define _REGION1_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R1 | _REGION_ENTRY_INVALID) #define _REGION2_ENTRY (_REGION_ENTRY_TYPE_R2 | _REGION_ENTRY_LENGTH) #define _REGION2_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R2 | _REGION_ENTRY_INVALID) -#define _REGION3_ENTRY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_LENGTH) +#define _REGION3_ENTRY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_LENGTH | \ + _REGION3_ENTRY_PRESENT) #define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INVALID) #define _REGION3_ENTRY_HARDWARE_BITS 0xfffffffffffff6ffUL @@ -285,18 +286,27 @@ static inline int is_module_addr(void *addr) #define _REGION3_ENTRY_ORIGIN_LARGE ~0x7fffffffUL /* large page address */ #define _REGION3_ENTRY_DIRTY 0x2000 /* SW region dirty bit */ #define _REGION3_ENTRY_YOUNG 0x1000 /* SW region young bit */ +#define _REGION3_ENTRY_COMM 0x0010 /* Common-Region, marks swap entry */ #define _REGION3_ENTRY_LARGE 0x0400 /* RTTE-format control, large page */ -#define _REGION3_ENTRY_WRITE 0x0002 /* SW region write bit */ -#define _REGION3_ENTRY_READ 0x0001 /* SW region read bit */ +#define _REGION3_ENTRY_WRITE 0x8000 /* SW region write bit */ +#define _REGION3_ENTRY_READ 0x4000 /* SW region read bit */ #ifdef CONFIG_MEM_SOFT_DIRTY -#define _REGION3_ENTRY_SOFT_DIRTY 0x4000 /* SW region soft dirty bit */ +#define _REGION3_ENTRY_SOFT_DIRTY 0x0002 /* SW region soft dirty bit */ #else #define _REGION3_ENTRY_SOFT_DIRTY 0x0000 /* SW region soft dirty bit */ #endif #define _REGION_ENTRY_BITS 0xfffffffffffff22fUL +/* + * SW region present bit. For non-leaf region-third-table entries, bits 62-63 + * indicate the TABLE LENGTH and both must be set to 1. But such entries + * would always be considered as present, so it is safe to use bit 63 as + * PRESENT bit for PUD. + */ +#define _REGION3_ENTRY_PRESENT 0x0001 + /* Bits in the segment table entry */ #define _SEGMENT_ENTRY_BITS 0xfffffffffffffe3fUL #define _SEGMENT_ENTRY_HARDWARE_BITS 0xfffffffffffffe3cUL @@ -308,21 +318,29 @@ static inline int is_module_addr(void *addr) #define _SEGMENT_ENTRY_INVALID 0x20 /* invalid segment table entry */ #define _SEGMENT_ENTRY_TYPE_MASK 0x0c /* segment table type mask */ -#define _SEGMENT_ENTRY (0) +#define _SEGMENT_ENTRY (_SEGMENT_ENTRY_PRESENT) #define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INVALID) #define _SEGMENT_ENTRY_DIRTY 0x2000 /* SW segment dirty bit */ #define _SEGMENT_ENTRY_YOUNG 0x1000 /* SW segment young bit */ + +#define _SEGMENT_ENTRY_COMM 0x0010 /* Common-Segment, marks swap entry */ #define _SEGMENT_ENTRY_LARGE 0x0400 /* STE-format control, large page */ -#define _SEGMENT_ENTRY_WRITE 0x0002 /* SW segment write bit */ -#define _SEGMENT_ENTRY_READ 0x0001 /* SW segment read bit */ +#define _SEGMENT_ENTRY_WRITE 0x8000 /* SW segment write bit */ +#define _SEGMENT_ENTRY_READ 0x4000 /* SW segment read bit */ #ifdef CONFIG_MEM_SOFT_DIRTY -#define _SEGMENT_ENTRY_SOFT_DIRTY 0x4000 /* SW segment soft dirty bit */ +#define _SEGMENT_ENTRY_SOFT_DIRTY 0x0002 /* SW segment soft dirty bit */ #else #define _SEGMENT_ENTRY_SOFT_DIRTY 0x0000 /* SW segment soft dirty bit */ #endif +#define _SEGMENT_ENTRY_PRESENT 0x0001 /* SW segment present bit */ + +/* Common bits in region and segment table entries, for swap entries */ +#define _RST_ENTRY_COMM 0x0010 /* Common-Region/Segment, marks swap entry */ +#define _RST_ENTRY_INVALID 0x0020 /* invalid region/segment table entry */ + #define _CRST_ENTRIES 2048 /* number of region/segment table entries */ #define _PAGE_ENTRIES 256 /* number of page table entries */ @@ -454,17 +472,22 @@ static inline int is_module_addr(void *addr) /* * Segment entry (large page) protection definitions. */ -#define SEGMENT_NONE __pgprot(_SEGMENT_ENTRY_INVALID | \ +#define SEGMENT_NONE __pgprot(_SEGMENT_ENTRY_PRESENT | \ + _SEGMENT_ENTRY_INVALID | \ _SEGMENT_ENTRY_PROTECT) -#define SEGMENT_RO __pgprot(_SEGMENT_ENTRY_PROTECT | \ +#define SEGMENT_RO __pgprot(_SEGMENT_ENTRY_PRESENT | \ + _SEGMENT_ENTRY_PROTECT | \ _SEGMENT_ENTRY_READ | \ _SEGMENT_ENTRY_NOEXEC) -#define SEGMENT_RX __pgprot(_SEGMENT_ENTRY_PROTECT | \ +#define SEGMENT_RX __pgprot(_SEGMENT_ENTRY_PRESENT | \ + _SEGMENT_ENTRY_PROTECT | \ _SEGMENT_ENTRY_READ) -#define SEGMENT_RW __pgprot(_SEGMENT_ENTRY_READ | \ +#define SEGMENT_RW __pgprot(_SEGMENT_ENTRY_PRESENT | \ + _SEGMENT_ENTRY_READ | \ _SEGMENT_ENTRY_WRITE | \ _SEGMENT_ENTRY_NOEXEC) -#define SEGMENT_RWX __pgprot(_SEGMENT_ENTRY_READ | \ +#define SEGMENT_RWX __pgprot(_SEGMENT_ENTRY_PRESENT | \ + _SEGMENT_ENTRY_READ | \ _SEGMENT_ENTRY_WRITE) #define SEGMENT_KERNEL __pgprot(_SEGMENT_ENTRY | \ _SEGMENT_ENTRY_LARGE | \ @@ -491,6 +514,7 @@ static inline int is_module_addr(void *addr) */ #define REGION3_KERNEL __pgprot(_REGION_ENTRY_TYPE_R3 | \ + _REGION3_ENTRY_PRESENT | \ _REGION3_ENTRY_LARGE | \ _REGION3_ENTRY_READ | \ _REGION3_ENTRY_WRITE | \ @@ -498,12 +522,14 @@ static inline int is_module_addr(void *addr) _REGION3_ENTRY_DIRTY | \ _REGION_ENTRY_NOEXEC) #define REGION3_KERNEL_RO __pgprot(_REGION_ENTRY_TYPE_R3 | \ + _REGION3_ENTRY_PRESENT | \ _REGION3_ENTRY_LARGE | \ _REGION3_ENTRY_READ | \ _REGION3_ENTRY_YOUNG | \ _REGION_ENTRY_PROTECT | \ _REGION_ENTRY_NOEXEC) #define REGION3_KERNEL_EXEC __pgprot(_REGION_ENTRY_TYPE_R3 | \ + _REGION3_ENTRY_PRESENT | \ _REGION3_ENTRY_LARGE | \ _REGION3_ENTRY_READ | \ _REGION3_ENTRY_WRITE | \ @@ -746,7 +772,7 @@ static inline int pud_present(pud_t pud) { if (pud_folded(pud)) return 1; - return (pud_val(pud) & _REGION_ENTRY_ORIGIN) != 0UL; + return (pud_val(pud) & _REGION3_ENTRY_PRESENT) != 0; } static inline int pud_none(pud_t pud) @@ -761,13 +787,18 @@ static inline bool pud_leaf(pud_t pud) { if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) != _REGION_ENTRY_TYPE_R3) return 0; - return !!(pud_val(pud) & _REGION3_ENTRY_LARGE); + return (pud_present(pud) && (pud_val(pud) & _REGION3_ENTRY_LARGE) != 0); +} + +static inline int pmd_present(pmd_t pmd) +{ + return (pmd_val(pmd) & _SEGMENT_ENTRY_PRESENT) != 0; } #define pmd_leaf pmd_leaf static inline bool pmd_leaf(pmd_t pmd) { - return (pmd_val(pmd) & _SEGMENT_ENTRY_LARGE) != 0; + return (pmd_present(pmd) && (pmd_val(pmd) & _SEGMENT_ENTRY_LARGE) != 0); } static inline int pmd_bad(pmd_t pmd) @@ -799,11 +830,6 @@ static inline int p4d_bad(p4d_t p4d) return (p4d_val(p4d) & ~_REGION_ENTRY_BITS) != 0; } -static inline int pmd_present(pmd_t pmd) -{ - return pmd_val(pmd) != _SEGMENT_ENTRY_EMPTY; -} - static inline int pmd_none(pmd_t pmd) { return pmd_val(pmd) == _SEGMENT_ENTRY_EMPTY; @@ -1851,7 +1877,7 @@ static inline pmd_t pmdp_collapse_flush(struct vm_area_struct *vma, static inline int pmd_trans_huge(pmd_t pmd) { - return pmd_val(pmd) & _SEGMENT_ENTRY_LARGE; + return pmd_leaf(pmd); } #define has_transparent_hugepage has_transparent_hugepage @@ -1911,6 +1937,53 @@ static inline swp_entry_t __swp_entry(unsigned long type, unsigned long offset) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) +/* + * 64 bit swap entry format for REGION3 and SEGMENT table entries (RSTE) + * Bits 59 and 63 are used to indicate the swap entry. Bit 58 marks the rste + * as invalid. + * A swap entry is indicated by bit pattern (rste & 0x011) == 0x010 + * | offset |Xtype |11TT|S0| + * |0000000000111111111122222222223333333333444444444455|555555|5566|66| + * |0123456789012345678901234567890123456789012345678901|234567|8901|23| + * + * Bits 0-51 store the offset. + * Bits 53-57 store the type. + * Bit 62 (S) is used for softdirty tracking. + * Bits 60-61 (TT) indicate the table type: 0x01 for REGION3 and 0x00 for SEGMENT. + * Bit 52 (X) is unused. + */ + +#define __SWP_OFFSET_MASK_RSTE ((1UL << 52) - 1) +#define __SWP_OFFSET_SHIFT_RSTE 12 +#define __SWP_TYPE_MASK_RSTE ((1UL << 5) - 1) +#define __SWP_TYPE_SHIFT_RSTE 6 + +/* + * TT bits set to 0x00 == SEGMENT. For REGION3 entries, caller must add R3 + * bits 0x01. See also __set_huge_pte_at(). + */ +static inline unsigned long mk_swap_rste(unsigned long type, unsigned long offset) +{ + unsigned long rste; + + rste = _RST_ENTRY_INVALID | _RST_ENTRY_COMM; + rste |= (offset & __SWP_OFFSET_MASK_RSTE) << __SWP_OFFSET_SHIFT_RSTE; + rste |= (type & __SWP_TYPE_MASK_RSTE) << __SWP_TYPE_SHIFT_RSTE; + return rste; +} + +static inline unsigned long __swp_type_rste(swp_entry_t entry) +{ + return (entry.val >> __SWP_TYPE_SHIFT_RSTE) & __SWP_TYPE_MASK_RSTE; +} + +static inline unsigned long __swp_offset_rste(swp_entry_t entry) +{ + return (entry.val >> __SWP_OFFSET_SHIFT_RSTE) & __SWP_OFFSET_MASK_RSTE; +} + +#define __rste_to_swp_entry(rste) ((swp_entry_t) { rste }) + extern int vmem_add_mapping(unsigned long start, unsigned long size); extern void vmem_remove_mapping(unsigned long start, unsigned long size); extern int __vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t prot, bool alloc); diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h index 0cde7e2403736..2c29bdf121272 100644 --- a/arch/s390/include/asm/preempt.h +++ b/arch/s390/include/asm/preempt.h @@ -130,10 +130,24 @@ static __always_inline bool should_resched(int preempt_offset) #define init_idle_preempt_count(p, cpu) do { } while (0) #ifdef CONFIG_PREEMPTION -extern void preempt_schedule(void); -#define __preempt_schedule() preempt_schedule() -extern void preempt_schedule_notrace(void); -#define __preempt_schedule_notrace() preempt_schedule_notrace() + +void preempt_schedule(void); +void preempt_schedule_notrace(void); + +#ifdef CONFIG_PREEMPT_DYNAMIC + +void dynamic_preempt_schedule(void); +void dynamic_preempt_schedule_notrace(void); +#define __preempt_schedule() dynamic_preempt_schedule() +#define __preempt_schedule_notrace() dynamic_preempt_schedule_notrace() + +#else /* CONFIG_PREEMPT_DYNAMIC */ + +#define __preempt_schedule() preempt_schedule() +#define __preempt_schedule_notrace() preempt_schedule_notrace() + +#endif /* CONFIG_PREEMPT_DYNAMIC */ + #endif /* CONFIG_PREEMPTION */ #endif /* __ASM_PREEMPT_H */ diff --git a/arch/s390/include/asm/spinlock.h b/arch/s390/include/asm/spinlock.h index ac868a9bb0d18..f87dd0a84855d 100644 --- a/arch/s390/include/asm/spinlock.h +++ b/arch/s390/include/asm/spinlock.h @@ -82,9 +82,10 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp) kcsan_release(); asm_inline volatile( ALTERNATIVE("nop", ".insn rre,0xb2fa0000,7,0", ALT_FACILITY(49)) /* NIAI 7 */ - " sth %1,%0\n" - : "=R" (((unsigned short *) &lp->lock)[1]) - : "d" (0) : "cc", "memory"); + " mvhhi %[lock],0\n" + : [lock] "=Q" (((unsigned short *)&lp->lock)[1]) + : + : "memory"); } /* diff --git a/arch/s390/include/asm/spinlock_types.h b/arch/s390/include/asm/spinlock_types.h index b69695e399574..3653ff57d6d9a 100644 --- a/arch/s390/include/asm/spinlock_types.h +++ b/arch/s390/include/asm/spinlock_types.h @@ -3,7 +3,7 @@ #define __ASM_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif typedef struct { diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index 00ac01874a129..c33f7144d1b97 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -61,44 +61,45 @@ void arch_setup_new_exec(void); /* * thread information flags bit numbers */ -/* _TIF_WORK bits */ #define TIF_NOTIFY_RESUME 0 /* callback before returning to user */ #define TIF_SIGPENDING 1 /* signal pending */ #define TIF_NEED_RESCHED 2 /* rescheduling necessary */ -#define TIF_UPROBE 3 /* breakpointed or single-stepping */ -#define TIF_GUARDED_STORAGE 4 /* load guarded storage control block */ +#define TIF_NEED_RESCHED_LAZY 3 /* lazy rescheduling needed */ +#define TIF_UPROBE 4 /* breakpointed or single-stepping */ #define TIF_PATCH_PENDING 5 /* pending live patching update */ #define TIF_PGSTE 6 /* New mm's will use 4K page tables */ #define TIF_NOTIFY_SIGNAL 7 /* signal notifications exist */ +#define TIF_GUARDED_STORAGE 8 /* load guarded storage control block */ #define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests with isolated BP */ #define TIF_PER_TRAP 10 /* Need to handle PER trap on exit to usermode */ - #define TIF_31BIT 16 /* 32bit process */ #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ #define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */ #define TIF_SINGLE_STEP 19 /* This task is single stepped */ #define TIF_BLOCK_STEP 20 /* This task is block stepped */ #define TIF_UPROBE_SINGLESTEP 21 /* This task is uprobe single stepped */ - -/* _TIF_TRACE bits */ #define TIF_SYSCALL_TRACE 24 /* syscall trace active */ #define TIF_SYSCALL_AUDIT 25 /* syscall auditing active */ #define TIF_SECCOMP 26 /* secure computing */ #define TIF_SYSCALL_TRACEPOINT 27 /* syscall tracepoint instrumentation */ #define _TIF_NOTIFY_RESUME BIT(TIF_NOTIFY_RESUME) -#define _TIF_NOTIFY_SIGNAL BIT(TIF_NOTIFY_SIGNAL) #define _TIF_SIGPENDING BIT(TIF_SIGPENDING) #define _TIF_NEED_RESCHED BIT(TIF_NEED_RESCHED) +#define _TIF_NEED_RESCHED_LAZY BIT(TIF_NEED_RESCHED_LAZY) #define _TIF_UPROBE BIT(TIF_UPROBE) -#define _TIF_GUARDED_STORAGE BIT(TIF_GUARDED_STORAGE) #define _TIF_PATCH_PENDING BIT(TIF_PATCH_PENDING) +#define _TIF_PGSTE BIT(TIF_PGSTE) +#define _TIF_NOTIFY_SIGNAL BIT(TIF_NOTIFY_SIGNAL) +#define _TIF_GUARDED_STORAGE BIT(TIF_GUARDED_STORAGE) #define _TIF_ISOLATE_BP_GUEST BIT(TIF_ISOLATE_BP_GUEST) #define _TIF_PER_TRAP BIT(TIF_PER_TRAP) - #define _TIF_31BIT BIT(TIF_31BIT) +#define _TIF_MEMDIE BIT(TIF_MEMDIE) +#define _TIF_RESTORE_SIGMASK BIT(TIF_RESTORE_SIGMASK) #define _TIF_SINGLE_STEP BIT(TIF_SINGLE_STEP) - +#define _TIF_BLOCK_STEP BIT(TIF_BLOCK_STEP) +#define _TIF_UPROBE_SINGLESTEP BIT(TIF_UPROBE_SINGLESTEP) #define _TIF_SYSCALL_TRACE BIT(TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_AUDIT BIT(TIF_SYSCALL_AUDIT) #define _TIF_SECCOMP BIT(TIF_SECCOMP) diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h index a6e2cd89b6094..9dfd46dd03c64 100644 --- a/arch/s390/include/asm/tlbflush.h +++ b/arch/s390/include/asm/tlbflush.h @@ -46,11 +46,6 @@ static inline void __tlb_flush_mm(struct mm_struct *mm) { unsigned long gmap_asce; - /* - * If the machine has IDTE we prefer to do a per mm flush - * on all cpus instead of doing a local flush if the mm - * only ran on the local cpu. - */ preempt_disable(); atomic_inc(&mm->context.flush_count); /* Reset TLB flush mask */ diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h index 05eaf6db3ad4c..60345dd2cba2d 100644 --- a/arch/s390/include/uapi/asm/kvm.h +++ b/arch/s390/include/uapi/asm/kvm.h @@ -469,7 +469,8 @@ struct kvm_s390_vm_cpu_subfunc { __u8 kdsa[16]; /* with MSA9 */ __u8 sortl[32]; /* with STFLE.150 */ __u8 dfltcc[32]; /* with STFLE.151 */ - __u8 reserved[1728]; + __u8 pfcr[16]; /* with STFLE.201 */ + __u8 reserved[1712]; }; #define KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST 6 diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index b3f2103694e41..de19fd8a6a954 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -77,12 +77,14 @@ static debug_info_t *debug_info_create(const char *name, int pages_per_area, static void debug_info_get(debug_info_t *); static void debug_info_put(debug_info_t *); static int debug_prolog_level_fn(debug_info_t *id, - struct debug_view *view, char *out_buf); + struct debug_view *view, char *out_buf, + size_t out_buf_size); static int debug_input_level_fn(debug_info_t *id, struct debug_view *view, struct file *file, const char __user *user_buf, size_t user_buf_size, loff_t *offset); static int debug_prolog_pages_fn(debug_info_t *id, - struct debug_view *view, char *out_buf); + struct debug_view *view, char *out_buf, + size_t out_buf_size); static int debug_input_pages_fn(debug_info_t *id, struct debug_view *view, struct file *file, const char __user *user_buf, size_t user_buf_size, loff_t *offset); @@ -90,9 +92,11 @@ static int debug_input_flush_fn(debug_info_t *id, struct debug_view *view, struct file *file, const char __user *user_buf, size_t user_buf_size, loff_t *offset); static int debug_hex_ascii_format_fn(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *in_buf); + char *out_buf, size_t out_buf_size, + const char *in_buf); static int debug_sprintf_format_fn(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *inbuf); + char *out_buf, size_t out_buf_size, + const char *inbuf); static void debug_areas_swap(debug_info_t *a, debug_info_t *b); static void debug_events_append(debug_info_t *dest, debug_info_t *src); @@ -391,8 +395,10 @@ static int debug_format_entry(file_private_info_t *p_info) if (p_info->act_entry == DEBUG_PROLOG_ENTRY) { /* print prolog */ - if (view->prolog_proc) - len += view->prolog_proc(id_snap, view, p_info->temp_buf); + if (view->prolog_proc) { + len += view->prolog_proc(id_snap, view, p_info->temp_buf, + sizeof(p_info->temp_buf)); + } goto out; } if (!id_snap->areas) /* this is true, if we have a prolog only view */ @@ -402,12 +408,16 @@ static int debug_format_entry(file_private_info_t *p_info) if (act_entry->clock == 0LL) goto out; /* empty entry */ - if (view->header_proc) + if (view->header_proc) { len += view->header_proc(id_snap, view, p_info->act_area, - act_entry, p_info->temp_buf + len); - if (view->format_proc) + act_entry, p_info->temp_buf + len, + sizeof(p_info->temp_buf) - len); + } + if (view->format_proc) { len += view->format_proc(id_snap, view, p_info->temp_buf + len, + sizeof(p_info->temp_buf) - len, DEBUG_DATA(act_entry)); + } out: return len; } @@ -1292,9 +1302,9 @@ static inline int debug_get_uint(char *buf) */ static int debug_prolog_pages_fn(debug_info_t *id, struct debug_view *view, - char *out_buf) + char *out_buf, size_t out_buf_size) { - return sprintf(out_buf, "%i\n", id->pages_per_area); + return scnprintf(out_buf, out_buf_size, "%i\n", id->pages_per_area); } /* @@ -1341,14 +1351,14 @@ static int debug_input_pages_fn(debug_info_t *id, struct debug_view *view, * prints out actual debug level */ static int debug_prolog_level_fn(debug_info_t *id, struct debug_view *view, - char *out_buf) + char *out_buf, size_t out_buf_size) { int rc = 0; if (id->level == DEBUG_OFF_LEVEL) - rc = sprintf(out_buf, "-\n"); + rc = scnprintf(out_buf, out_buf_size, "-\n"); else - rc = sprintf(out_buf, "%i\n", id->level); + rc = scnprintf(out_buf, out_buf_size, "%i\n", id->level); return rc; } @@ -1465,22 +1475,24 @@ static int debug_input_flush_fn(debug_info_t *id, struct debug_view *view, * prints debug data in hex/ascii format */ static int debug_hex_ascii_format_fn(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *in_buf) + char *out_buf, size_t out_buf_size, const char *in_buf) { int i, rc = 0; - for (i = 0; i < id->buf_size; i++) - rc += sprintf(out_buf + rc, "%02x ", ((unsigned char *) in_buf)[i]); - rc += sprintf(out_buf + rc, "| "); + for (i = 0; i < id->buf_size; i++) { + rc += scnprintf(out_buf + rc, out_buf_size - rc, + "%02x ", ((unsigned char *)in_buf)[i]); + } + rc += scnprintf(out_buf + rc, out_buf_size - rc, "| "); for (i = 0; i < id->buf_size; i++) { unsigned char c = in_buf[i]; if (isascii(c) && isprint(c)) - rc += sprintf(out_buf + rc, "%c", c); + rc += scnprintf(out_buf + rc, out_buf_size - rc, "%c", c); else - rc += sprintf(out_buf + rc, "."); + rc += scnprintf(out_buf + rc, out_buf_size - rc, "."); } - rc += sprintf(out_buf + rc, "\n"); + rc += scnprintf(out_buf + rc, out_buf_size - rc, "\n"); return rc; } @@ -1488,7 +1500,8 @@ static int debug_hex_ascii_format_fn(debug_info_t *id, struct debug_view *view, * prints header for debug entry */ int debug_dflt_header_fn(debug_info_t *id, struct debug_view *view, - int area, debug_entry_t *entry, char *out_buf) + int area, debug_entry_t *entry, char *out_buf, + size_t out_buf_size) { unsigned long sec, usec; unsigned long caller; @@ -1505,9 +1518,9 @@ int debug_dflt_header_fn(debug_info_t *id, struct debug_view *view, else except_str = "-"; caller = (unsigned long) entry->caller; - rc += sprintf(out_buf, "%02i %011ld:%06lu %1u %1s %04u %px ", - area, sec, usec, level, except_str, - entry->cpu, (void *)caller); + rc += scnprintf(out_buf, out_buf_size, "%02i %011ld:%06lu %1u %1s %04u %px ", + area, sec, usec, level, except_str, + entry->cpu, (void *)caller); return rc; } EXPORT_SYMBOL(debug_dflt_header_fn); @@ -1520,7 +1533,7 @@ EXPORT_SYMBOL(debug_dflt_header_fn); #define DEBUG_SPRINTF_MAX_ARGS 10 static int debug_sprintf_format_fn(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *inbuf) + char *out_buf, size_t out_buf_size, const char *inbuf) { debug_sprintf_entry_t *curr_event = (debug_sprintf_entry_t *)inbuf; int num_longs, num_used_args = 0, i, rc = 0; @@ -1533,8 +1546,9 @@ static int debug_sprintf_format_fn(debug_info_t *id, struct debug_view *view, goto out; /* bufsize of entry too small */ if (num_longs == 1) { /* no args, we use only the string */ - strcpy(out_buf, curr_event->string); - rc = strlen(curr_event->string); + rc = strscpy(out_buf, curr_event->string, out_buf_size); + if (rc == -E2BIG) + rc = out_buf_size; goto out; } @@ -1546,12 +1560,13 @@ static int debug_sprintf_format_fn(debug_info_t *id, struct debug_view *view, for (i = 0; i < num_used_args; i++) index[i] = i; - rc = sprintf(out_buf, curr_event->string, curr_event->args[index[0]], - curr_event->args[index[1]], curr_event->args[index[2]], - curr_event->args[index[3]], curr_event->args[index[4]], - curr_event->args[index[5]], curr_event->args[index[6]], - curr_event->args[index[7]], curr_event->args[index[8]], - curr_event->args[index[9]]); + rc = scnprintf(out_buf, out_buf_size, + curr_event->string, curr_event->args[index[0]], + curr_event->args[index[1]], curr_event->args[index[2]], + curr_event->args[index[3]], curr_event->args[index[4]], + curr_event->args[index[5]], curr_event->args[index[6]], + curr_event->args[index[7]], curr_event->args[index[8]], + curr_event->args[index[9]]); out: return rc; } diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 1ff13239d4e58..960c08700cf69 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -430,9 +430,13 @@ SYM_CODE_START(\name) SYM_CODE_END(\name) .endm + .section .irqentry.text, "ax" + INT_HANDLER ext_int_handler,__LC_EXT_OLD_PSW,do_ext_irq INT_HANDLER io_int_handler,__LC_IO_OLD_PSW,do_io_irq + .section .kprobes.text, "ax" + /* * Machine check handler routines */ diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 6295faf0987d8..8b80ea57125f3 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -489,6 +489,12 @@ int __init arch_init_kprobes(void) return 0; } +int __init arch_populate_kprobe_blacklist(void) +{ + return kprobe_add_area_blacklist((unsigned long)__irqentry_text_start, + (unsigned long)__irqentry_text_end); +} + int arch_trampoline_kprobe(struct kprobe *p) { return 0; diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 0cde42f8af6ef..1e99514fb7ae3 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -180,39 +180,27 @@ static int sf_buffer_available(struct cpu_hw_sf *cpuhw) */ static void free_sampling_buffer(struct sf_buffer *sfb) { - unsigned long *sdbt, *curr; - - if (!sfb->sdbt) - return; + unsigned long *sdbt, *curr, *head; sdbt = sfb->sdbt; - curr = sdbt; - + if (!sdbt) + return; + sfb->sdbt = NULL; /* Free the SDBT after all SDBs are processed... */ - while (1) { - if (!*curr || !sdbt) - break; - - /* Process table-link entries */ + head = sdbt; + curr = sdbt; + do { if (is_link_entry(curr)) { + /* Process table-link entries */ curr = get_next_sdbt(curr); - if (sdbt) - free_page((unsigned long)sdbt); - - /* If the origin is reached, sampling buffer is freed */ - if (curr == sfb->sdbt) - break; - else - sdbt = curr; + free_page((unsigned long)sdbt); + sdbt = curr; } else { /* Process SDB pointer */ - if (*curr) { - free_page((unsigned long)phys_to_virt(*curr)); - curr++; - } + free_page((unsigned long)phys_to_virt(*curr)); + curr++; } - } - + } while (curr != head); memset(sfb, 0, sizeof(*sfb)); } diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 9f59837d159e0..40edfde25f5b9 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -151,7 +151,7 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo break; } if (!store_ip(consume_entry, cookie, entry, perf, ip)) - return; + break; first = false; } pagefault_enable(); diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index a688351f4ab52..9816b0060fbe5 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -129,8 +129,8 @@ static void ipte_lock_simple(struct kvm *kvm) retry: read_lock(&kvm->arch.sca_lock); ic = kvm_s390_get_ipte_control(kvm); + old = READ_ONCE(*ic); do { - old = READ_ONCE(*ic); if (old.k) { read_unlock(&kvm->arch.sca_lock); cond_resched(); @@ -138,7 +138,7 @@ static void ipte_lock_simple(struct kvm *kvm) } new = old; new.k = 1; - } while (cmpxchg(&ic->val, old.val, new.val) != old.val); + } while (!try_cmpxchg(&ic->val, &old.val, new.val)); read_unlock(&kvm->arch.sca_lock); out: mutex_unlock(&kvm->arch.ipte_mutex); @@ -154,11 +154,11 @@ static void ipte_unlock_simple(struct kvm *kvm) goto out; read_lock(&kvm->arch.sca_lock); ic = kvm_s390_get_ipte_control(kvm); + old = READ_ONCE(*ic); do { - old = READ_ONCE(*ic); new = old; new.k = 0; - } while (cmpxchg(&ic->val, old.val, new.val) != old.val); + } while (!try_cmpxchg(&ic->val, &old.val, new.val)); read_unlock(&kvm->arch.sca_lock); wake_up(&kvm->arch.ipte_wq); out: @@ -172,8 +172,8 @@ static void ipte_lock_siif(struct kvm *kvm) retry: read_lock(&kvm->arch.sca_lock); ic = kvm_s390_get_ipte_control(kvm); + old = READ_ONCE(*ic); do { - old = READ_ONCE(*ic); if (old.kg) { read_unlock(&kvm->arch.sca_lock); cond_resched(); @@ -182,7 +182,7 @@ static void ipte_lock_siif(struct kvm *kvm) new = old; new.k = 1; new.kh++; - } while (cmpxchg(&ic->val, old.val, new.val) != old.val); + } while (!try_cmpxchg(&ic->val, &old.val, new.val)); read_unlock(&kvm->arch.sca_lock); } @@ -192,13 +192,13 @@ static void ipte_unlock_siif(struct kvm *kvm) read_lock(&kvm->arch.sca_lock); ic = kvm_s390_get_ipte_control(kvm); + old = READ_ONCE(*ic); do { - old = READ_ONCE(*ic); new = old; new.kh--; if (!new.kh) new.k = 0; - } while (cmpxchg(&ic->val, old.val, new.val) != old.val); + } while (!try_cmpxchg(&ic->val, &old.val, new.val)); read_unlock(&kvm->arch.sca_lock); if (!new.kh) wake_up(&kvm->arch.ipte_wq); diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 4f0e7f61edf78..ea8dce299954a 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -118,8 +118,6 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id) static void sca_clear_ext_call(struct kvm_vcpu *vcpu) { - int rc, expect; - if (!kvm_s390_use_sca_entries()) return; kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND); @@ -128,23 +126,16 @@ static void sca_clear_ext_call(struct kvm_vcpu *vcpu) struct esca_block *sca = vcpu->kvm->arch.sca; union esca_sigp_ctrl *sigp_ctrl = &(sca->cpu[vcpu->vcpu_id].sigp_ctrl); - union esca_sigp_ctrl old; - old = READ_ONCE(*sigp_ctrl); - expect = old.value; - rc = cmpxchg(&sigp_ctrl->value, old.value, 0); + WRITE_ONCE(sigp_ctrl->value, 0); } else { struct bsca_block *sca = vcpu->kvm->arch.sca; union bsca_sigp_ctrl *sigp_ctrl = &(sca->cpu[vcpu->vcpu_id].sigp_ctrl); - union bsca_sigp_ctrl old; - old = READ_ONCE(*sigp_ctrl); - expect = old.value; - rc = cmpxchg(&sigp_ctrl->value, old.value, 0); + WRITE_ONCE(sigp_ctrl->value, 0); } read_unlock(&vcpu->kvm->arch.sca_lock); - WARN_ON(rc != expect); /* cannot clear? */ } int psw_extint_disabled(struct kvm_vcpu *vcpu) @@ -247,12 +238,12 @@ static inline int gisa_set_iam(struct kvm_s390_gisa *gisa, u8 iam) { u64 word, _word; + word = READ_ONCE(gisa->u64.word[0]); do { - word = READ_ONCE(gisa->u64.word[0]); if ((u64)gisa != word >> 32) return -EBUSY; _word = (word & ~0xffUL) | iam; - } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); + } while (!try_cmpxchg(&gisa->u64.word[0], &word, _word)); return 0; } @@ -270,10 +261,10 @@ static inline void gisa_clear_ipm(struct kvm_s390_gisa *gisa) { u64 word, _word; + word = READ_ONCE(gisa->u64.word[0]); do { - word = READ_ONCE(gisa->u64.word[0]); _word = word & ~(0xffUL << 24); - } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); + } while (!try_cmpxchg(&gisa->u64.word[0], &word, _word)); } /** @@ -291,14 +282,14 @@ static inline u8 gisa_get_ipm_or_restore_iam(struct kvm_s390_gisa_interrupt *gi) u8 pending_mask, alert_mask; u64 word, _word; + word = READ_ONCE(gi->origin->u64.word[0]); do { - word = READ_ONCE(gi->origin->u64.word[0]); alert_mask = READ_ONCE(gi->alert.mask); pending_mask = (u8)(word >> 24) & alert_mask; if (pending_mask) return pending_mask; _word = (word & ~0xffUL) | alert_mask; - } while (cmpxchg(&gi->origin->u64.word[0], word, _word) != word); + } while (!try_cmpxchg(&gi->origin->u64.word[0], &word, _word)); return 0; } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index deeb32034ad5e..d8080c27d45bd 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -348,6 +348,16 @@ static inline int plo_test_bit(unsigned char nr) return CC_TRANSFORM(cc) == 0; } +static __always_inline void pfcr_query(u8 (*query)[16]) +{ + asm volatile( + " lghi 0,0\n" + " .insn rsy,0xeb0000000016,0,0,%[query]\n" + : [query] "=QS" (*query) + : + : "cc", "0"); +} + static __always_inline void __sortl_query(u8 (*query)[32]) { asm volatile( @@ -429,6 +439,9 @@ static void __init kvm_s390_cpu_feat_init(void) if (test_facility(151)) /* DFLTCC */ __dfltcc_query(&kvm_s390_available_subfunc.dfltcc); + if (test_facility(201)) /* PFCR */ + pfcr_query(&kvm_s390_available_subfunc.pfcr); + if (MACHINE_HAS_ESOP) allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ESOP); /* @@ -799,6 +812,14 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) set_kvm_facility(kvm->arch.model.fac_mask, 192); set_kvm_facility(kvm->arch.model.fac_list, 192); } + if (test_facility(198)) { + set_kvm_facility(kvm->arch.model.fac_mask, 198); + set_kvm_facility(kvm->arch.model.fac_list, 198); + } + if (test_facility(199)) { + set_kvm_facility(kvm->arch.model.fac_mask, 199); + set_kvm_facility(kvm->arch.model.fac_list, 199); + } r = 0; } else r = -EINVAL; @@ -1543,6 +1564,9 @@ static int kvm_s390_set_processor_subfunc(struct kvm *kvm, ((unsigned long *) &kvm->arch.model.subfuncs.dfltcc)[1], ((unsigned long *) &kvm->arch.model.subfuncs.dfltcc)[2], ((unsigned long *) &kvm->arch.model.subfuncs.dfltcc)[3]); + VM_EVENT(kvm, 3, "GET: guest PFCR subfunc 0x%16.16lx.%16.16lx", + ((unsigned long *) &kvm_s390_available_subfunc.pfcr)[0], + ((unsigned long *) &kvm_s390_available_subfunc.pfcr)[1]); return 0; } @@ -1757,6 +1781,9 @@ static int kvm_s390_get_processor_subfunc(struct kvm *kvm, ((unsigned long *) &kvm->arch.model.subfuncs.dfltcc)[1], ((unsigned long *) &kvm->arch.model.subfuncs.dfltcc)[2], ((unsigned long *) &kvm->arch.model.subfuncs.dfltcc)[3]); + VM_EVENT(kvm, 3, "GET: guest PFCR subfunc 0x%16.16lx.%16.16lx", + ((unsigned long *) &kvm_s390_available_subfunc.pfcr)[0], + ((unsigned long *) &kvm_s390_available_subfunc.pfcr)[1]); return 0; } @@ -1825,6 +1852,9 @@ static int kvm_s390_get_machine_subfunc(struct kvm *kvm, ((unsigned long *) &kvm_s390_available_subfunc.dfltcc)[1], ((unsigned long *) &kvm_s390_available_subfunc.dfltcc)[2], ((unsigned long *) &kvm_s390_available_subfunc.dfltcc)[3]); + VM_EVENT(kvm, 3, "GET: host PFCR subfunc 0x%16.16lx.%16.16lx", + ((unsigned long *) &kvm_s390_available_subfunc.pfcr)[0], + ((unsigned long *) &kvm_s390_available_subfunc.pfcr)[1]); return 0; } @@ -1907,11 +1937,11 @@ static void kvm_s390_update_topology_change_report(struct kvm *kvm, bool val) read_lock(&kvm->arch.sca_lock); sca = kvm->arch.sca; + old = READ_ONCE(sca->utility); do { - old = READ_ONCE(sca->utility); new = old; new.mtcr = val; - } while (cmpxchg(&sca->utility.val, old.val, new.val) != old.val); + } while (!try_cmpxchg(&sca->utility.val, &old.val, new.val)); read_unlock(&kvm->arch.sca_lock); } @@ -3769,6 +3799,13 @@ static bool kvm_has_pckmo_ecc(struct kvm *kvm) } +static bool kvm_has_pckmo_hmac(struct kvm *kvm) +{ + /* At least one HMAC subfunction must be present */ + return kvm_has_pckmo_subfunc(kvm, 118) || + kvm_has_pckmo_subfunc(kvm, 122); +} + static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu) { /* @@ -3781,7 +3818,7 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->crycbd = vcpu->kvm->arch.crypto.crycbd; vcpu->arch.sie_block->ecb3 &= ~(ECB3_AES | ECB3_DEA); vcpu->arch.sie_block->eca &= ~ECA_APIE; - vcpu->arch.sie_block->ecd &= ~ECD_ECC; + vcpu->arch.sie_block->ecd &= ~(ECD_ECC | ECD_HMAC); if (vcpu->kvm->arch.crypto.apie) vcpu->arch.sie_block->eca |= ECA_APIE; @@ -3789,9 +3826,11 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu) /* Set up protected key support */ if (vcpu->kvm->arch.crypto.aes_kw) { vcpu->arch.sie_block->ecb3 |= ECB3_AES; - /* ecc is also wrapped with AES key */ + /* ecc/hmac is also wrapped with AES key */ if (kvm_has_pckmo_ecc(vcpu->kvm)) vcpu->arch.sie_block->ecd |= ECD_ECC; + if (kvm_has_pckmo_hmac(vcpu->kvm)) + vcpu->arch.sie_block->ecd |= ECD_HMAC; } if (vcpu->kvm->arch.crypto.dea_kw) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index a61518b549f00..9b9e7fdd53807 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -208,13 +208,12 @@ static inline int account_mem(unsigned long nr_pages) page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + cur_pages = atomic_long_read(&user->locked_vm); do { - cur_pages = atomic_long_read(&user->locked_vm); new_pages = cur_pages + nr_pages; if (new_pages > page_limit) return -ENOMEM; - } while (atomic_long_cmpxchg(&user->locked_vm, cur_pages, - new_pages) != cur_pages); + } while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages)); atomic64_add(nr_pages, ¤t->mm->pinned_vm); diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index d3cdde1b18e5b..150b9387860ad 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -335,7 +335,8 @@ static int shadow_crycb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) /* we may only allow it if enabled for guest 2 */ ecb3_flags = scb_o->ecb3 & vcpu->arch.sie_block->ecb3 & (ECB3_AES | ECB3_DEA); - ecd_flags = scb_o->ecd & vcpu->arch.sie_block->ecd & ECD_ECC; + ecd_flags = scb_o->ecd & vcpu->arch.sie_block->ecd & + (ECD_ECC | ECD_HMAC); if (!ecb3_flags && !ecd_flags) goto end; @@ -661,7 +662,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) struct page *page; page = gfn_to_page(kvm, gpa_to_gfn(gpa)); - if (is_error_page(page)) + if (!page) return -EINVAL; *hpa = (hpa_t)page_to_phys(page) + (gpa & ~PAGE_MASK); return 0; @@ -670,7 +671,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) /* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */ static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa) { - kvm_release_pfn_dirty(hpa >> PAGE_SHIFT); + kvm_release_page_dirty(pfn_to_page(hpa >> PAGE_SHIFT)); /* mark the page always as dirty for migration */ mark_page_dirty(kvm, gpa_to_gfn(gpa)); } diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 09d735010ee1b..a81a01c449272 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -15,6 +15,7 @@ #include #include #include +#include int spin_retry = -1; @@ -76,24 +77,43 @@ static inline int arch_load_niai4(int *lock) asm_inline volatile( ALTERNATIVE("nop", ".insn rre,0xb2fa0000,4,0", ALT_FACILITY(49)) /* NIAI 4 */ - " l %0,%1\n" - : "=d" (owner) : "Q" (*lock) : "memory"); + " l %[owner],%[lock]\n" + : [owner] "=d" (owner) : [lock] "R" (*lock) : "memory"); return owner; } -static inline int arch_cmpxchg_niai8(int *lock, int old, int new) +#ifdef __HAVE_ASM_FLAG_OUTPUTS__ + +static inline int arch_try_cmpxchg_niai8(int *lock, int old, int new) +{ + int cc; + + asm_inline volatile( + ALTERNATIVE("nop", ".insn rre,0xb2fa0000,8,0", ALT_FACILITY(49)) /* NIAI 8 */ + " cs %[old],%[new],%[lock]\n" + : [old] "+d" (old), [lock] "+Q" (*lock), "=@cc" (cc) + : [new] "d" (new) + : "memory"); + return cc == 0; +} + +#else /* __HAVE_ASM_FLAG_OUTPUTS__ */ + +static inline int arch_try_cmpxchg_niai8(int *lock, int old, int new) { int expected = old; asm_inline volatile( ALTERNATIVE("nop", ".insn rre,0xb2fa0000,8,0", ALT_FACILITY(49)) /* NIAI 8 */ - " cs %0,%3,%1\n" - : "=d" (old), "=Q" (*lock) - : "0" (old), "d" (new), "Q" (*lock) + " cs %[old],%[new],%[lock]\n" + : [old] "+d" (old), [lock] "+Q" (*lock) + : [new] "d" (new) : "cc", "memory"); return expected == old; } +#endif /* __HAVE_ASM_FLAG_OUTPUTS__ */ + static inline struct spin_wait *arch_spin_decode_tail(int lock) { int ix, cpu; @@ -226,7 +246,7 @@ static inline void arch_spin_lock_classic(arch_spinlock_t *lp) /* Try to get the lock if it is free. */ if (!owner) { new = (old & _Q_TAIL_MASK) | lockval; - if (arch_cmpxchg_niai8(&lp->lock, old, new)) { + if (arch_try_cmpxchg_niai8(&lp->lock, old, new)) { /* Got the lock */ return; } diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 646326fa0fad8..9b681f74dccc1 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -338,7 +338,8 @@ static void do_exception(struct pt_regs *regs, int access) handle_fault_error_nolock(regs, 0); else do_sigsegv(regs, SEGV_MAPERR); - } else if (fault & (VM_FAULT_SIGBUS | VM_FAULT_HWPOISON)) { + } else if (fault & (VM_FAULT_SIGBUS | VM_FAULT_HWPOISON | + VM_FAULT_HWPOISON_LARGE)) { if (!user_mode(regs)) handle_fault_error_nolock(regs, 0); else diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c index 643e47bfaddc7..16b8a36c56de1 100644 --- a/arch/s390/mm/gmap.c +++ b/arch/s390/mm/gmap.c @@ -587,7 +587,8 @@ int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr) if (pmd_leaf(*pmd)) { *table = (pmd_val(*pmd) & _SEGMENT_ENTRY_HARDWARE_BITS_LARGE) - | _SEGMENT_ENTRY_GMAP_UC; + | _SEGMENT_ENTRY_GMAP_UC + | _SEGMENT_ENTRY; } else *table = pmd_val(*pmd) & _SEGMENT_ENTRY_HARDWARE_BITS; @@ -2396,7 +2397,8 @@ static void gmap_pmdp_clear(struct mm_struct *mm, unsigned long vmaddr, gaddr = __gmap_segment_gaddr((unsigned long *)pmdp); pmdp_notify_gmap(gmap, pmdp, gaddr); WARN_ON(pmd_val(*pmdp) & ~(_SEGMENT_ENTRY_HARDWARE_BITS_LARGE | - _SEGMENT_ENTRY_GMAP_UC)); + _SEGMENT_ENTRY_GMAP_UC | + _SEGMENT_ENTRY)); if (purge) __pmdp_csp(pmdp); set_pmd(pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); @@ -2450,7 +2452,8 @@ void gmap_pmdp_idte_local(struct mm_struct *mm, unsigned long vmaddr) gaddr = __gmap_segment_gaddr(entry); pmdp_notify_gmap(gmap, pmdp, gaddr); WARN_ON(*entry & ~(_SEGMENT_ENTRY_HARDWARE_BITS_LARGE | - _SEGMENT_ENTRY_GMAP_UC)); + _SEGMENT_ENTRY_GMAP_UC | + _SEGMENT_ENTRY)); if (MACHINE_HAS_TLB_GUEST) __pmdp_idte(gaddr, pmdp, IDTE_GUEST_ASCE, gmap->asce, IDTE_LOCAL); @@ -2485,7 +2488,8 @@ void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr) gaddr = __gmap_segment_gaddr(entry); pmdp_notify_gmap(gmap, pmdp, gaddr); WARN_ON(*entry & ~(_SEGMENT_ENTRY_HARDWARE_BITS_LARGE | - _SEGMENT_ENTRY_GMAP_UC)); + _SEGMENT_ENTRY_GMAP_UC | + _SEGMENT_ENTRY)); if (MACHINE_HAS_TLB_GUEST) __pmdp_idte(gaddr, pmdp, IDTE_GUEST_ASCE, gmap->asce, IDTE_GLOBAL); diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 7c79cf1bc7d76..d9ce199953de9 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -24,6 +24,7 @@ static inline unsigned long __pte_to_rste(pte_t pte) { + swp_entry_t arch_entry; unsigned long rste; /* @@ -48,6 +49,7 @@ static inline unsigned long __pte_to_rste(pte_t pte) */ if (pte_present(pte)) { rste = pte_val(pte) & PAGE_MASK; + rste |= _SEGMENT_ENTRY_PRESENT; rste |= move_set_bit(pte_val(pte), _PAGE_READ, _SEGMENT_ENTRY_READ); rste |= move_set_bit(pte_val(pte), _PAGE_WRITE, @@ -66,6 +68,10 @@ static inline unsigned long __pte_to_rste(pte_t pte) #endif rste |= move_set_bit(pte_val(pte), _PAGE_NOEXEC, _SEGMENT_ENTRY_NOEXEC); + } else if (!pte_none(pte)) { + /* swap pte */ + arch_entry = __pte_to_swp_entry(pte); + rste = mk_swap_rste(__swp_type(arch_entry), __swp_offset(arch_entry)); } else rste = _SEGMENT_ENTRY_EMPTY; return rste; @@ -73,13 +79,18 @@ static inline unsigned long __pte_to_rste(pte_t pte) static inline pte_t __rste_to_pte(unsigned long rste) { + swp_entry_t arch_entry; unsigned long pteval; - int present; + int present, none; + pte_t pte; - if ((rste & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) + if ((rste & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) { present = pud_present(__pud(rste)); - else + none = pud_none(__pud(rste)); + } else { present = pmd_present(__pmd(rste)); + none = pmd_none(__pmd(rste)); + } /* * Convert encoding pmd / pud bits pte bits @@ -114,6 +125,11 @@ static inline pte_t __rste_to_pte(unsigned long rste) pteval |= move_set_bit(rste, _SEGMENT_ENTRY_SOFT_DIRTY, _PAGE_SOFT_DIRTY); #endif pteval |= move_set_bit(rste, _SEGMENT_ENTRY_NOEXEC, _PAGE_NOEXEC); + } else if (!none) { + /* swap rste */ + arch_entry = __rste_to_swp_entry(rste); + pte = mk_swap_pte(__swp_type_rste(arch_entry), __swp_offset_rste(arch_entry)); + pteval = pte_val(pte); } else pteval = _PAGE_INVALID; return __pte(pteval); @@ -148,8 +164,6 @@ void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, unsigned long rste; rste = __pte_to_rste(pte); - if (!MACHINE_HAS_NX) - rste &= ~_SEGMENT_ENTRY_NOEXEC; /* Set correct table type for 2G hugepages */ if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) { @@ -223,11 +237,10 @@ pte_t *huge_pte_offset(struct mm_struct *mm, p4dp = p4d_offset(pgdp, addr); if (p4d_present(*p4dp)) { pudp = pud_offset(p4dp, addr); - if (pud_present(*pudp)) { - if (pud_leaf(*pudp)) - return (pte_t *) pudp; + if (sz == PUD_SIZE) + return (pte_t *)pudp; + if (pud_present(*pudp)) pmdp = pmd_offset(pudp, addr); - } } } return (pte_t *) pmdp; diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index cbff587dc4e31..88f72745fa59e 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -779,8 +779,9 @@ int zpci_hot_reset_device(struct zpci_dev *zdev) * @fh: Current Function Handle of the device to be created * @state: Initial state after creation either Standby or Configured * - * Creates a new zpci device and adds it to its, possibly newly created, zbus - * as well as zpci_list. + * Allocates a new struct zpci_dev and queries the platform for its details. + * If successful the device can subsequently be added to the zPCI subsystem + * using zpci_add_device(). * * Returns: the zdev on success or an error pointer otherwise */ @@ -803,7 +804,6 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state) goto error; zdev->state = state; - kref_init(&zdev->kref); mutex_init(&zdev->state_lock); mutex_init(&zdev->fmb_lock); mutex_init(&zdev->kzdev_lock); @@ -816,6 +816,17 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state) return ERR_PTR(rc); } +/** + * zpci_add_device() - Add a previously created zPCI device to the zPCI subsystem + * @zdev: The zPCI device to be added + * + * A struct zpci_dev is added to the zPCI subsystem and to a virtual PCI bus creating + * a new one as necessary. A hotplug slot is created and events start to be handled. + * If successful from this point on zpci_zdev_get() and zpci_zdev_put() must be used. + * If adding the struct zpci_dev fails the device was not added and should be freed. + * + * Return: 0 on success, or an error code otherwise + */ int zpci_add_device(struct zpci_dev *zdev) { int rc; @@ -829,6 +840,7 @@ int zpci_add_device(struct zpci_dev *zdev) if (rc) goto error_destroy_iommu; + kref_init(&zdev->kref); spin_lock(&zpci_list_lock); list_add_tail(&zdev->entry, &zpci_list); spin_unlock(&zpci_list_lock); @@ -928,10 +940,8 @@ void zpci_device_reserved(struct zpci_dev *zdev) void zpci_release_device(struct kref *kref) { struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref); - int ret; - if (zdev->has_hp_slot) - zpci_exit_slot(zdev); + WARN_ON(zdev->state != ZPCI_FN_STATE_RESERVED); if (zdev->zbus->bus) zpci_bus_remove_device(zdev, false); @@ -939,28 +949,14 @@ void zpci_release_device(struct kref *kref) if (zdev_enabled(zdev)) zpci_disable_device(zdev); - switch (zdev->state) { - case ZPCI_FN_STATE_CONFIGURED: - ret = sclp_pci_deconfigure(zdev->fid); - zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret); - fallthrough; - case ZPCI_FN_STATE_STANDBY: - if (zdev->has_hp_slot) - zpci_exit_slot(zdev); - spin_lock(&zpci_list_lock); - list_del(&zdev->entry); - spin_unlock(&zpci_list_lock); - zpci_dbg(3, "rsv fid:%x\n", zdev->fid); - fallthrough; - case ZPCI_FN_STATE_RESERVED: - if (zdev->has_resources) - zpci_cleanup_bus_resources(zdev); - zpci_bus_device_unregister(zdev); - zpci_destroy_iommu(zdev); - fallthrough; - default: - break; - } + if (zdev->has_hp_slot) + zpci_exit_slot(zdev); + + if (zdev->has_resources) + zpci_cleanup_bus_resources(zdev); + + zpci_bus_device_unregister(zdev); + zpci_destroy_iommu(zdev); zpci_dbg(3, "rem fid:%x\n", zdev->fid); kfree_rcu(zdev, rcu); } @@ -1121,7 +1117,8 @@ static void zpci_add_devices(struct list_head *scan_list) list_sort(NULL, scan_list, &zpci_cmp_rid); list_for_each_entry_safe(zdev, tmp, scan_list, entry) { list_del_init(&zdev->entry); - zpci_add_device(zdev); + if (zpci_add_device(zdev)) + kfree(zdev); } } diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index 1b74a000ff645..d5ace00d10f04 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -53,7 +53,7 @@ static int zpci_bus_prepare_device(struct zpci_dev *zdev) zpci_setup_bus_resources(zdev); for (i = 0; i < PCI_STD_NUM_BARS; i++) { if (zdev->bars[i].res) - pci_bus_add_resource(zdev->zbus->bus, zdev->bars[i].res, 0); + pci_bus_add_resource(zdev->zbus->bus, zdev->bars[i].res); } } diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 47f934f4e828e..7f7b732b3f3ef 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -340,7 +340,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED); if (IS_ERR(zdev)) break; - zpci_add_device(zdev); + if (zpci_add_device(zdev)) { + kfree(zdev); + break; + } } else { /* the configuration request may be stale */ if (zdev->state != ZPCI_FN_STATE_STANDBY) @@ -354,7 +357,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY); if (IS_ERR(zdev)) break; - zpci_add_device(zdev); + if (zpci_add_device(zdev)) { + kfree(zdev); + break; + } } else { zpci_update_fh(zdev, ccdf->fh); } diff --git a/arch/s390/tools/gen_facilities.c b/arch/s390/tools/gen_facilities.c index 68580cbea4e62..855f818deb98e 100644 --- a/arch/s390/tools/gen_facilities.c +++ b/arch/s390/tools/gen_facilities.c @@ -109,10 +109,12 @@ static struct facility_def facility_defs[] = { 15, /* AP Facilities Test */ 156, /* etoken facility */ 165, /* nnpa facility */ + 170, /* ineffective-nonconstrained-transaction facility */ 193, /* bear enhancement facility */ 194, /* rdp enhancement facility */ 196, /* processor activity instrumentation facility */ 197, /* processor activity instrumentation extension 1 */ + 201, /* concurrent-functions facility */ -1 /* END */ } }, diff --git a/arch/sh/include/asm/spinlock_types.h b/arch/sh/include/asm/spinlock_types.h index 907bda4b1619a..7cb50e68448f4 100644 --- a/arch/sh/include/asm/spinlock_types.h +++ b/arch/sh/include/asm/spinlock_types.h @@ -3,7 +3,7 @@ #define __ASM_SH_SPINLOCK_TYPES_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif typedef struct { diff --git a/arch/sh/kernel/cpu/proc.c b/arch/sh/kernel/cpu/proc.c index a306bcd6b3413..5f6d0e827baeb 100644 --- a/arch/sh/kernel/cpu/proc.c +++ b/arch/sh/kernel/cpu/proc.c @@ -132,7 +132,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) static void *c_start(struct seq_file *m, loff_t *pos) { - return *pos < NR_CPUS ? cpu_data + *pos : NULL; + return *pos < nr_cpu_ids ? cpu_data + *pos : NULL; } static void *c_next(struct seq_file *m, void *v, loff_t *pos) { diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile index 757451c3ea1df..0400078076e58 100644 --- a/arch/sparc/Makefile +++ b/arch/sparc/Makefile @@ -29,7 +29,7 @@ UTS_MACHINE := sparc # versions of gcc. Some gcc versions won't pass -Av8 to binutils when you # give -mcpu=v8. This silently worked with older bintutils versions but # does not any more. -KBUILD_CFLAGS += -m32 -mcpu=v8 -pipe -mno-fpu -fcall-used-g5 -fcall-used-g7 +KBUILD_CFLAGS += -m32 -mcpu=v8 -pipe -mno-fpu $(call cc-option,-fcall-used-g5) $(call cc-option,-fcall-used-g7) KBUILD_CFLAGS += -Wa,-Av8 KBUILD_AFLAGS += -m32 -Wa,-Av8 @@ -45,7 +45,7 @@ export BITS := 64 UTS_MACHINE := sparc64 KBUILD_CFLAGS += -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow -KBUILD_CFLAGS += -ffixed-g4 -ffixed-g5 -fcall-used-g7 -Wno-sign-compare +KBUILD_CFLAGS += -ffixed-g4 -ffixed-g5 $(call cc-option,-fcall-used-g7) -Wno-sign-compare KBUILD_CFLAGS += -Wa,--undeclared-regs KBUILD_CFLAGS += $(call cc-option,-mtune=ultrasparc3) KBUILD_AFLAGS += -m64 -mcpu=ultrasparc -Wa,--undeclared-regs diff --git a/arch/sparc/include/asm/hvtramp.h b/arch/sparc/include/asm/hvtramp.h index 688ea43af0f5d..ce2453ea4f2be 100644 --- a/arch/sparc/include/asm/hvtramp.h +++ b/arch/sparc/include/asm/hvtramp.h @@ -17,7 +17,7 @@ struct hvtramp_descr { __u64 fault_info_va; __u64 fault_info_pa; __u64 thread_reg; - struct hvtramp_mapping maps[1]; + struct hvtramp_mapping maps[]; }; void hv_cpu_startup(unsigned long hvdescr_pa); diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index e40c395db2026..5cbd6ed5ef6fa 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -297,9 +297,7 @@ static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg, unsigned long hv_err; int i; - hdesc = kzalloc(sizeof(*hdesc) + - (sizeof(struct hvtramp_mapping) * - num_kernel_image_mappings - 1), + hdesc = kzalloc(struct_size(hdesc, maps, num_kernel_image_mappings), GFP_KERNEL); if (!hdesc) { printk(KERN_ERR "ldom_startcpu_cpuid: Cannot allocate " diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index 08bbdc4585969..578fd0d49f306 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c @@ -255,6 +255,7 @@ static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) static struct m48t59_plat_data m48t59_data = { .read_byte = mostek_read_byte, .write_byte = mostek_write_byte, + .yy_offset = 68, }; /* resource is set at runtime */ diff --git a/arch/sparc/kernel/time_64.c b/arch/sparc/kernel/time_64.c index 60f1c8cc5363e..b32f27f929d1a 100644 --- a/arch/sparc/kernel/time_64.c +++ b/arch/sparc/kernel/time_64.c @@ -544,6 +544,7 @@ static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) static struct m48t59_plat_data m48t59_data = { .read_byte = mostek_read_byte, .write_byte = mostek_write_byte, + .yy_offset = 68, }; static struct platform_device m48t59_rtc = { diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index d317a843f7ea9..f1b86eb303404 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -48,6 +48,11 @@ SECTIONS { _text = .; HEAD_TEXT + ALIGN_FUNCTION(); +#ifdef CONFIG_SPARC64 + /* Match text section symbols in head_64.S first */ + *head_64.o(.text) +#endif TEXT_TEXT SCHED_TEXT LOCK_TEXT diff --git a/arch/sparc/vdso/Makefile b/arch/sparc/vdso/Makefile index 243dbfc4609d8..50ec2978cda53 100644 --- a/arch/sparc/vdso/Makefile +++ b/arch/sparc/vdso/Makefile @@ -46,7 +46,7 @@ CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 -fasynchronous-unwind-tables -m64 -fno-omit-frame-pointer -foptimize-sibling-calls \ -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO -SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 -fcall-used-g5 -fcall-used-g7 +SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 $(call cc-option,-fcall-used-g5) $(call cc-option,-fcall-used-g7) $(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) diff --git a/arch/sparc/vdso/vclock_gettime.c b/arch/sparc/vdso/vclock_gettime.c index e794edde67554..79607804ea1b0 100644 --- a/arch/sparc/vdso/vclock_gettime.c +++ b/arch/sparc/vdso/vclock_gettime.c @@ -86,6 +86,11 @@ notrace static long vdso_fallback_gettimeofday(struct __kernel_old_timeval *tv, } #ifdef CONFIG_SPARC64 +notrace static __always_inline u64 __shr64(u64 val, int amt) +{ + return val >> amt; +} + notrace static __always_inline u64 vread_tick(void) { u64 ret; @@ -102,6 +107,21 @@ notrace static __always_inline u64 vread_tick_stick(void) return ret; } #else +notrace static __always_inline u64 __shr64(u64 val, int amt) +{ + u64 ret; + + __asm__ __volatile__("sllx %H1, 32, %%g1\n\t" + "srl %L1, 0, %L1\n\t" + "or %%g1, %L1, %%g1\n\t" + "srlx %%g1, %2, %L0\n\t" + "srlx %L0, 32, %H0" + : "=r" (ret) + : "r" (val), "r" (amt) + : "g1"); + return ret; +} + notrace static __always_inline u64 vread_tick(void) { register unsigned long long ret asm("o4"); @@ -154,7 +174,7 @@ notrace static __always_inline int do_realtime(struct vvar_data *vvar, ts->tv_sec = vvar->wall_time_sec; ns = vvar->wall_time_snsec; ns += vgetsns(vvar); - ns >>= vvar->clock.shift; + ns = __shr64(ns, vvar->clock.shift); } while (unlikely(vvar_read_retry(vvar, seq))); ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); @@ -174,7 +194,7 @@ notrace static __always_inline int do_realtime_stick(struct vvar_data *vvar, ts->tv_sec = vvar->wall_time_sec; ns = vvar->wall_time_snsec; ns += vgetsns_stick(vvar); - ns >>= vvar->clock.shift; + ns = __shr64(ns, vvar->clock.shift); } while (unlikely(vvar_read_retry(vvar, seq))); ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); @@ -194,7 +214,7 @@ notrace static __always_inline int do_monotonic(struct vvar_data *vvar, ts->tv_sec = vvar->monotonic_time_sec; ns = vvar->monotonic_time_snsec; ns += vgetsns(vvar); - ns >>= vvar->clock.shift; + ns = __shr64(ns, vvar->clock.shift); } while (unlikely(vvar_read_retry(vvar, seq))); ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); @@ -214,7 +234,7 @@ notrace static __always_inline int do_monotonic_stick(struct vvar_data *vvar, ts->tv_sec = vvar->monotonic_time_sec; ns = vvar->monotonic_time_snsec; ns += vgetsns_stick(vvar); - ns >>= vvar->clock.shift; + ns = __shr64(ns, vvar->clock.shift); } while (unlikely(vvar_read_retry(vvar, seq))); ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); diff --git a/arch/um/Kconfig b/arch/um/Kconfig index c89575d05021f..18051b1cfce0a 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -5,6 +5,7 @@ menu "UML-specific options" config UML bool default y + select ARCH_WANTS_DYNAMIC_TASK_STRUCT select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL @@ -32,6 +33,8 @@ config UML select HAVE_ARCH_VMAP_STACK select HAVE_RUST select ARCH_HAS_UBSAN + select HAVE_ARCH_TRACEHOOK + select THREAD_INFO_IN_TASK config MMU bool @@ -94,7 +97,7 @@ config MAY_HAVE_RUNTIME_DEPS config STATIC_LINK bool "Force a static link" - depends on CC_CAN_LINK_STATIC_NO_RUNTIME_DEPS || !MAY_HAVE_RUNTIME_DEPS + depends on !MAY_HAVE_RUNTIME_DEPS help This option gives you the ability to force a static link of UML. Normally, UML is linked as a shared binary. This is inconvenient for @@ -209,8 +212,8 @@ config MMAPPER config PGTABLE_LEVELS int - default 3 if 3_LEVEL_PGTABLES - default 2 + default 4 if 64BIT + default 2 if !64BIT config UML_TIME_TRAVEL_SUPPORT bool @@ -227,6 +230,21 @@ config UML_TIME_TRAVEL_SUPPORT It is safe to say Y, but you probably don't need this. +config UML_MAX_USERSPACE_ITERATIONS + int + prompt "Maximum number of unscheduled userspace iterations" + default 10000 + depends on UML_TIME_TRAVEL_SUPPORT + help + In UML inf-cpu and ext time-travel mode userspace can run without being + interrupted. This will eventually overwhelm the kernel and create OOM + situations (mainly RCU not running). This setting specifies the number + of kernel/userspace switches (minor/major page fault, signal or syscall) + for the same userspace thread before the sched_clock is advanced by a + jiffie to trigger scheduling. + + Setting it to zero disables the feature. + config KASAN_SHADOW_OFFSET hex depends on KASAN diff --git a/arch/um/Makefile b/arch/um/Makefile index 00b63bac5effb..1d36a613aad83 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -61,7 +61,8 @@ KBUILD_CFLAGS += $(CFLAGS) $(CFLAGS-y) -D__arch_um__ \ $(ARCH_INCLUDE) $(MODE_INCLUDE) -Dvmap=kernel_vmap \ -Dlongjmp=kernel_longjmp -Dsetjmp=kernel_setjmp \ -Din6addr_loopback=kernel_in6addr_loopback \ - -Din6addr_any=kernel_in6addr_any -Dstrrchr=kernel_strrchr + -Din6addr_any=kernel_in6addr_any -Dstrrchr=kernel_strrchr \ + -D__close_range=kernel__close_range KBUILD_RUSTFLAGS += -Crelocation-model=pie @@ -70,7 +71,9 @@ KBUILD_AFLAGS += $(ARCH_INCLUDE) USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -I%,,$(KBUILD_CFLAGS))) \ $(ARCH_INCLUDE) $(MODE_INCLUDE) $(filter -I%,$(CFLAGS)) \ -D_FILE_OFFSET_BITS=64 -idirafter $(srctree)/include \ - -idirafter $(objtree)/include -D__KERNEL__ -D__UM_HOST__ + -idirafter $(objtree)/include -D__KERNEL__ -D__UM_HOST__ \ + -include $(srctree)/include/linux/compiler-version.h \ + -include $(srctree)/include/linux/kconfig.h #This will adjust *FLAGS accordingly to the platform. include $(srctree)/$(ARCH_DIR)/Makefile-os-Linux diff --git a/arch/um/Makefile-skas b/arch/um/Makefile-skas index 67323b0289999..1a27e65bcb9c1 100644 --- a/arch/um/Makefile-skas +++ b/arch/um/Makefile-skas @@ -3,15 +3,15 @@ # Licensed under the GPL # -GPROF_OPT += -pg +export UM_GPROF_OPT += -pg ifdef CONFIG_CC_IS_CLANG -GCOV_OPT += -fprofile-instr-generate -fcoverage-mapping +export UM_GCOV_OPT += -fprofile-instr-generate -fcoverage-mapping else -GCOV_OPT += -fprofile-arcs -ftest-coverage +export UM_GCOV_OPT += -fprofile-arcs -ftest-coverage endif -CFLAGS-$(CONFIG_GCOV) += $(GCOV_OPT) -CFLAGS-$(CONFIG_GPROF) += $(GPROF_OPT) -LINK-$(CONFIG_GCOV) += $(GCOV_OPT) -LINK-$(CONFIG_GPROF) += $(GPROF_OPT) +CFLAGS-$(CONFIG_GCOV) += $(UM_GCOV_OPT) +CFLAGS-$(CONFIG_GPROF) += $(UM_GPROF_OPT) +LINK-$(CONFIG_GCOV) += $(UM_GCOV_OPT) +LINK-$(CONFIG_GPROF) += $(UM_GPROF_OPT) diff --git a/arch/um/configs/i386_defconfig b/arch/um/configs/i386_defconfig index 9c9c77f1255ac..1ffa088739f4f 100644 --- a/arch/um/configs/i386_defconfig +++ b/arch/um/configs/i386_defconfig @@ -1,4 +1,3 @@ -CONFIG_3_LEVEL_PGTABLES=y # CONFIG_COMPACTION is not set CONFIG_BINFMT_MISC=m CONFIG_HOSTFS=y diff --git a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c index a66e556012c48..35f9beeb19b3f 100644 --- a/arch/um/drivers/chan_user.c +++ b/arch/um/drivers/chan_user.c @@ -161,6 +161,8 @@ static __noreturn int winch_thread(void *arg) int count; char c = 1; + os_set_pdeathsig(); + pty_fd = data->pty_fd; pipe_fd = data->pipe_fd; count = write(pipe_fd, &c, sizeof(c)); diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c index 9d228878cea2e..0ac149de1ac01 100644 --- a/arch/um/drivers/hostaudio_kern.c +++ b/arch/um/drivers/hostaudio_kern.c @@ -48,6 +48,7 @@ MODULE_PARM_DESC(mixer, MIXER_HELP); #ifndef MODULE static int set_dsp(char *name, int *add) { + *add = 0; dsp = name; return 0; } @@ -56,6 +57,7 @@ __uml_setup("dsp=", set_dsp, "dsp=\n" DSP_HELP); static int set_mixer(char *name, int *add) { + *add = 0; mixer = name; return 0; } diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 77c4afb8ab907..75d04fb4994a0 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -336,7 +336,7 @@ static struct platform_driver uml_net_driver = { static void net_device_release(struct device *dev) { - struct uml_net *device = dev_get_drvdata(dev); + struct uml_net *device = container_of(dev, struct uml_net, pdev.dev); struct net_device *netdev = device->dev; struct uml_net_private *lp = netdev_priv(netdev); diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 7f28ec1929dc0..66c1a8835e362 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -779,7 +779,7 @@ static int ubd_open_dev(struct ubd *ubd_dev) static void ubd_device_release(struct device *dev) { - struct ubd *ubd_dev = dev_get_drvdata(dev); + struct ubd *ubd_dev = container_of(dev, struct ubd, pdev.dev); blk_mq_free_tag_set(&ubd_dev->tag_set); *ubd_dev = ((struct ubd) DEFAULT_UBD); @@ -898,6 +898,8 @@ static int ubd_add(int n, char **error_out) if (err) goto out_cleanup_disk; + ubd_dev->disk = disk; + return 0; out_cleanup_disk: @@ -1499,6 +1501,7 @@ int io_thread(void *arg) { int n, count, written, res; + os_set_pdeathsig(); os_fix_helper_signals(); while(1){ diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c index c992da83268dd..64c09db392c16 100644 --- a/arch/um/drivers/vector_kern.c +++ b/arch/um/drivers/vector_kern.c @@ -815,7 +815,8 @@ static struct platform_driver uml_net_driver = { static void vector_device_release(struct device *dev) { - struct vector_device *device = dev_get_drvdata(dev); + struct vector_device *device = + container_of(dev, struct vector_device, pdev.dev); struct net_device *netdev = device->dev; list_del(&device->list); diff --git a/arch/um/drivers/vhost_user.h b/arch/um/drivers/vhost_user.h index 6f147cd3c9f76..fcfa3b7e021ba 100644 --- a/arch/um/drivers/vhost_user.h +++ b/arch/um/drivers/vhost_user.h @@ -10,6 +10,7 @@ /* Feature bits */ #define VHOST_USER_F_PROTOCOL_FEATURES 30 /* Protocol feature bits */ +#define VHOST_USER_PROTOCOL_F_MQ 0 #define VHOST_USER_PROTOCOL_F_REPLY_ACK 3 #define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5 #define VHOST_USER_PROTOCOL_F_CONFIG 9 @@ -23,7 +24,8 @@ /* Supported transport features */ #define VHOST_USER_SUPPORTED_F BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES) /* Supported protocol features */ -#define VHOST_USER_SUPPORTED_PROTOCOL_F (BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK) | \ +#define VHOST_USER_SUPPORTED_PROTOCOL_F (BIT_ULL(VHOST_USER_PROTOCOL_F_MQ) | \ + BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK) | \ BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ) | \ BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG) | \ BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS)) diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c index 2b6e701776b6b..cc3be48a9d6eb 100644 --- a/arch/um/drivers/virtio_uml.c +++ b/arch/um/drivers/virtio_uml.c @@ -56,6 +56,7 @@ struct virtio_uml_device { int sock, req_fd, irq; u64 features; u64 protocol_features; + u64 max_vqs; u8 status; u8 registered:1; u8 suspended:1; @@ -72,8 +73,6 @@ struct virtio_uml_vq_info { bool suspended; }; -extern unsigned long long physmem_size, highmem; - #define vu_err(vu_dev, ...) dev_err(&(vu_dev)->pdev->dev, ##__VA_ARGS__) /* Vhost-user protocol */ @@ -343,6 +342,17 @@ static int vhost_user_set_protocol_features(struct virtio_uml_device *vu_dev, protocol_features); } +static int vhost_user_get_queue_num(struct virtio_uml_device *vu_dev, + u64 *queue_num) +{ + int rc = vhost_user_send_no_payload(vu_dev, true, + VHOST_USER_GET_QUEUE_NUM); + + if (rc) + return rc; + return vhost_user_recv_u64(vu_dev, queue_num); +} + static void vhost_user_reply(struct virtio_uml_device *vu_dev, struct vhost_user_msg *msg, int response) { @@ -516,6 +526,15 @@ static int vhost_user_init(struct virtio_uml_device *vu_dev) return rc; } + if (vu_dev->protocol_features & + BIT_ULL(VHOST_USER_PROTOCOL_F_MQ)) { + rc = vhost_user_get_queue_num(vu_dev, &vu_dev->max_vqs); + if (rc) + return rc; + } else { + vu_dev->max_vqs = U64_MAX; + } + return 0; } @@ -625,7 +644,7 @@ static int vhost_user_set_mem_table(struct virtio_uml_device *vu_dev) { struct vhost_user_msg msg = { .header.request = VHOST_USER_SET_MEM_TABLE, - .header.size = sizeof(msg.payload.mem_regions), + .header.size = offsetof(typeof(msg.payload.mem_regions), regions[1]), .payload.mem_regions.num = 1, }; unsigned long reserved = uml_reserved - uml_physmem; @@ -673,13 +692,6 @@ static int vhost_user_set_mem_table(struct virtio_uml_device *vu_dev) if (rc < 0) return rc; - if (highmem) { - msg.payload.mem_regions.num++; - rc = vhost_user_init_mem_region(__pa(end_iomem), highmem, - &fds[1], &msg.payload.mem_regions.regions[1]); - if (rc < 0) - return rc; - } return vhost_user_send(vu_dev, false, &msg, fds, msg.payload.mem_regions.num); @@ -897,7 +909,7 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev, { struct virtio_uml_vq_info *info = vq->priv; int call_fds[2]; - int rc; + int rc, irq; /* no call FD needed/desired in this case */ if (vu_dev->protocol_features & @@ -914,19 +926,23 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev, return rc; info->call_fd = call_fds[0]; - rc = um_request_irq(vu_dev->irq, info->call_fd, IRQ_READ, - vu_interrupt, IRQF_SHARED, info->name, vq); - if (rc < 0) + irq = um_request_irq(vu_dev->irq, info->call_fd, IRQ_READ, + vu_interrupt, IRQF_SHARED, info->name, vq); + if (irq < 0) { + rc = irq; goto close_both; + } rc = vhost_user_set_vring_call(vu_dev, vq->index, call_fds[1]); if (rc) goto release_irq; + vu_dev->irq = irq; + goto out; release_irq: - um_free_irq(vu_dev->irq, vq); + um_free_irq(irq, vq); close_both: os_close_file(call_fds[0]); out: @@ -1023,7 +1039,9 @@ static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vq; /* not supported for now */ - if (WARN_ON(nvqs > 64)) + if (WARN(nvqs > 64 || nvqs > vu_dev->max_vqs, + "%d VQs requested, only up to 64 or %lld supported\n", + nvqs, vu_dev->max_vqs)) return -EINVAL; rc = vhost_user_set_mem_table(vu_dev); @@ -1210,6 +1228,7 @@ static int virtio_uml_probe(struct platform_device *pdev) vu_dev->vdev.id.vendor = VIRTIO_DEV_ANY_ID; vu_dev->pdev = pdev; vu_dev->req_fd = -1; + vu_dev->irq = UM_IRQ_ALLOC; time_travel_propagate_time(); diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild index 18f902da8e997..428f2c5158c2a 100644 --- a/arch/um/include/asm/Kbuild +++ b/arch/um/include/asm/Kbuild @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 generic-y += bug.h generic-y += compat.h -generic-y += current.h generic-y += device.h generic-y += dma-mapping.h generic-y += emergency-restart.h diff --git a/arch/um/include/asm/current.h b/arch/um/include/asm/current.h new file mode 100644 index 0000000000000..de64e032d66c1 --- /dev/null +++ b/arch/um/include/asm/current.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_CURRENT_H +#define __ASM_CURRENT_H + +#include +#include + +#ifndef __ASSEMBLY__ + +struct task_struct; +extern struct task_struct *cpu_tasks[NR_CPUS]; + +static __always_inline struct task_struct *get_current(void) +{ + return cpu_tasks[0]; +} + + +#define current get_current() + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_CURRENT_H */ diff --git a/arch/um/include/asm/page.h b/arch/um/include/asm/page.h index 834313ecd3d65..3d516f3ca9c74 100644 --- a/arch/um/include/asm/page.h +++ b/arch/um/include/asm/page.h @@ -29,51 +29,35 @@ struct page; #define clear_user_page(page, vaddr, pg) clear_page(page) #define copy_user_page(to, from, vaddr, pg) copy_page(to, from) -#if defined(CONFIG_3_LEVEL_PGTABLES) && !defined(CONFIG_64BIT) - typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pmd; } pmd_t; typedef struct { unsigned long pgd; } pgd_t; -#define pte_val(p) ((p).pte) -#define pte_get_bits(p, bits) ((p).pte & (bits)) -#define pte_set_bits(p, bits) ((p).pte |= (bits)) -#define pte_clear_bits(p, bits) ((p).pte &= ~(bits)) -#define pte_copy(to, from) ({ (to).pte = (from).pte; }) -#define pte_is_zero(p) (!((p).pte & ~_PAGE_NEWPAGE)) -#define pte_set_val(p, phys, prot) \ - ({ (p).pte = (phys) | pgprot_val(prot); }) +#if CONFIG_PGTABLE_LEVELS > 2 +typedef struct { unsigned long pmd; } pmd_t; #define pmd_val(x) ((x).pmd) #define __pmd(x) ((pmd_t) { (x) } ) -typedef unsigned long long phys_t; +#if CONFIG_PGTABLE_LEVELS > 3 -#else - -typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pgd; } pgd_t; +typedef struct { unsigned long pud; } pud_t; +#define pud_val(x) ((x).pud) +#define __pud(x) ((pud_t) { (x) } ) -#ifdef CONFIG_3_LEVEL_PGTABLES -typedef struct { unsigned long pmd; } pmd_t; -#define pmd_val(x) ((x).pmd) -#define __pmd(x) ((pmd_t) { (x) } ) -#endif +#endif /* CONFIG_PGTABLE_LEVELS > 3 */ +#endif /* CONFIG_PGTABLE_LEVELS > 2 */ #define pte_val(x) ((x).pte) - #define pte_get_bits(p, bits) ((p).pte & (bits)) #define pte_set_bits(p, bits) ((p).pte |= (bits)) #define pte_clear_bits(p, bits) ((p).pte &= ~(bits)) #define pte_copy(to, from) ((to).pte = (from).pte) -#define pte_is_zero(p) (!((p).pte & ~_PAGE_NEWPAGE)) +#define pte_is_zero(p) (!((p).pte & ~_PAGE_NEEDSYNC)) #define pte_set_val(p, phys, prot) (p).pte = (phys | pgprot_val(prot)) typedef unsigned long phys_t; -#endif - typedef struct { unsigned long pgprot; } pgprot_t; typedef struct page *pgtable_t; diff --git a/arch/um/include/asm/pgalloc.h b/arch/um/include/asm/pgalloc.h index de5e31c64793d..04fb4e6969a46 100644 --- a/arch/um/include/asm/pgalloc.h +++ b/arch/um/include/asm/pgalloc.h @@ -31,7 +31,7 @@ do { \ tlb_remove_page_ptdesc((tlb), (page_ptdesc(pte))); \ } while (0) -#ifdef CONFIG_3_LEVEL_PGTABLES +#if CONFIG_PGTABLE_LEVELS > 2 #define __pmd_free_tlb(tlb, pmd, address) \ do { \ @@ -39,6 +39,15 @@ do { \ tlb_remove_page_ptdesc((tlb), virt_to_ptdesc(pmd)); \ } while (0) +#if CONFIG_PGTABLE_LEVELS > 3 + +#define __pud_free_tlb(tlb, pud, address) \ +do { \ + pagetable_pud_dtor(virt_to_ptdesc(pud)); \ + tlb_remove_page_ptdesc((tlb), virt_to_ptdesc(pud)); \ +} while (0) + +#endif #endif #endif diff --git a/arch/um/include/asm/pgtable-2level.h b/arch/um/include/asm/pgtable-2level.h index 8256ecc5b9198..ab0c8dd865648 100644 --- a/arch/um/include/asm/pgtable-2level.h +++ b/arch/um/include/asm/pgtable-2level.h @@ -31,7 +31,7 @@ printk("%s:%d: bad pgd %p(%08lx).\n", __FILE__, __LINE__, &(e), \ pgd_val(e)) -static inline int pgd_newpage(pgd_t pgd) { return 0; } +static inline int pgd_needsync(pgd_t pgd) { return 0; } static inline void pgd_mkuptodate(pgd_t pgd) { } #define set_pmd(pmdptr, pmdval) (*(pmdptr) = (pmdval)) diff --git a/arch/um/include/asm/pgtable-3level.h b/arch/um/include/asm/pgtable-4level.h similarity index 60% rename from arch/um/include/asm/pgtable-3level.h rename to arch/um/include/asm/pgtable-4level.h index 8a5032ec231fd..0d279caee93cc 100644 --- a/arch/um/include/asm/pgtable-3level.h +++ b/arch/um/include/asm/pgtable-4level.h @@ -4,21 +4,25 @@ * Derived from include/asm-i386/pgtable.h */ -#ifndef __UM_PGTABLE_3LEVEL_H -#define __UM_PGTABLE_3LEVEL_H +#ifndef __UM_PGTABLE_4LEVEL_H +#define __UM_PGTABLE_4LEVEL_H -#include +#include -/* PGDIR_SHIFT determines what a third-level page table entry can map */ +/* PGDIR_SHIFT determines what a fourth-level page table entry can map */ -#ifdef CONFIG_64BIT -#define PGDIR_SHIFT 30 -#else -#define PGDIR_SHIFT 31 -#endif +#define PGDIR_SHIFT 39 #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) +/* PUD_SHIFT determines the size of the area a third-level page table can + * map + */ + +#define PUD_SHIFT 30 +#define PUD_SIZE (1UL << PUD_SHIFT) +#define PUD_MASK (~(PUD_SIZE-1)) + /* PMD_SHIFT determines the size of the area a second-level page table can * map */ @@ -32,13 +36,9 @@ */ #define PTRS_PER_PTE 512 -#ifdef CONFIG_64BIT #define PTRS_PER_PMD 512 +#define PTRS_PER_PUD 512 #define PTRS_PER_PGD 512 -#else -#define PTRS_PER_PMD 1024 -#define PTRS_PER_PGD 1024 -#endif #define USER_PTRS_PER_PGD ((TASK_SIZE + (PGDIR_SIZE - 1)) / PGDIR_SIZE) @@ -48,11 +48,14 @@ #define pmd_ERROR(e) \ printk("%s:%d: bad pmd %p(%016lx).\n", __FILE__, __LINE__, &(e), \ pmd_val(e)) +#define pud_ERROR(e) \ + printk("%s:%d: bad pud %p(%016lx).\n", __FILE__, __LINE__, &(e), \ + pud_val(e)) #define pgd_ERROR(e) \ printk("%s:%d: bad pgd %p(%016lx).\n", __FILE__, __LINE__, &(e), \ pgd_val(e)) -#define pud_none(x) (!(pud_val(x) & ~_PAGE_NEWPAGE)) +#define pud_none(x) (!(pud_val(x) & ~_PAGE_NEEDSYNC)) #define pud_bad(x) ((pud_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) #define pud_present(x) (pud_val(x) & _PAGE_PRESENT) #define pud_populate(mm, pud, pmd) \ @@ -60,23 +63,40 @@ #define set_pud(pudptr, pudval) (*(pudptr) = (pudval)) -static inline int pgd_newpage(pgd_t pgd) +#define p4d_none(x) (!(p4d_val(x) & ~_PAGE_NEEDSYNC)) +#define p4d_bad(x) ((p4d_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) +#define p4d_present(x) (p4d_val(x) & _PAGE_PRESENT) +#define p4d_populate(mm, p4d, pud) \ + set_p4d(p4d, __p4d(_PAGE_TABLE + __pa(pud))) + +#define set_p4d(p4dptr, p4dval) (*(p4dptr) = (p4dval)) + + +static inline int pgd_needsync(pgd_t pgd) { - return(pgd_val(pgd) & _PAGE_NEWPAGE); + return pgd_val(pgd) & _PAGE_NEEDSYNC; } -static inline void pgd_mkuptodate(pgd_t pgd) { pgd_val(pgd) &= ~_PAGE_NEWPAGE; } +static inline void pgd_mkuptodate(pgd_t pgd) { pgd_val(pgd) &= ~_PAGE_NEEDSYNC; } #define set_pmd(pmdptr, pmdval) (*(pmdptr) = (pmdval)) static inline void pud_clear (pud_t *pud) { - set_pud(pud, __pud(_PAGE_NEWPAGE)); + set_pud(pud, __pud(_PAGE_NEEDSYNC)); +} + +static inline void p4d_clear (p4d_t *p4d) +{ + set_p4d(p4d, __p4d(_PAGE_NEEDSYNC)); } #define pud_page(pud) phys_to_page(pud_val(pud) & PAGE_MASK) #define pud_pgtable(pud) ((pmd_t *) __va(pud_val(pud) & PAGE_MASK)) +#define p4d_page(p4d) phys_to_page(p4d_val(p4d) & PAGE_MASK) +#define p4d_pgtable(p4d) ((pud_t *) __va(p4d_val(p4d) & PAGE_MASK)) + static inline unsigned long pte_pfn(pte_t pte) { return phys_to_pfn(pte_val(pte)); @@ -97,4 +117,3 @@ static inline pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot) } #endif - diff --git a/arch/um/include/asm/pgtable.h b/arch/um/include/asm/pgtable.h index faab5a2a4b061..0bd60afcc37d5 100644 --- a/arch/um/include/asm/pgtable.h +++ b/arch/um/include/asm/pgtable.h @@ -11,8 +11,7 @@ #include #define _PAGE_PRESENT 0x001 -#define _PAGE_NEWPAGE 0x002 -#define _PAGE_NEWPROT 0x004 +#define _PAGE_NEEDSYNC 0x002 #define _PAGE_RW 0x020 #define _PAGE_USER 0x040 #define _PAGE_ACCESSED 0x080 @@ -24,10 +23,12 @@ /* We borrow bit 10 to store the exclusive marker in swap PTEs. */ #define _PAGE_SWP_EXCLUSIVE 0x400 -#ifdef CONFIG_3_LEVEL_PGTABLES -#include -#else +#if CONFIG_PGTABLE_LEVELS == 4 +#include +#elif CONFIG_PGTABLE_LEVELS == 2 #include +#else +#error "Unsupported number of page table levels" #endif extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; @@ -78,22 +79,22 @@ extern unsigned long end_iomem; */ #define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page) -#define pte_clear(mm,addr,xp) pte_set_val(*(xp), (phys_t) 0, __pgprot(_PAGE_NEWPAGE)) +#define pte_clear(mm, addr, xp) pte_set_val(*(xp), (phys_t) 0, __pgprot(_PAGE_NEEDSYNC)) -#define pmd_none(x) (!((unsigned long)pmd_val(x) & ~_PAGE_NEWPAGE)) +#define pmd_none(x) (!((unsigned long)pmd_val(x) & ~_PAGE_NEEDSYNC)) #define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) #define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) -#define pmd_clear(xp) do { pmd_val(*(xp)) = _PAGE_NEWPAGE; } while (0) +#define pmd_clear(xp) do { pmd_val(*(xp)) = _PAGE_NEEDSYNC; } while (0) -#define pmd_newpage(x) (pmd_val(x) & _PAGE_NEWPAGE) -#define pmd_mkuptodate(x) (pmd_val(x) &= ~_PAGE_NEWPAGE) +#define pmd_needsync(x) (pmd_val(x) & _PAGE_NEEDSYNC) +#define pmd_mkuptodate(x) (pmd_val(x) &= ~_PAGE_NEEDSYNC) -#define pud_newpage(x) (pud_val(x) & _PAGE_NEWPAGE) -#define pud_mkuptodate(x) (pud_val(x) &= ~_PAGE_NEWPAGE) +#define pud_needsync(x) (pud_val(x) & _PAGE_NEEDSYNC) +#define pud_mkuptodate(x) (pud_val(x) &= ~_PAGE_NEEDSYNC) -#define p4d_newpage(x) (p4d_val(x) & _PAGE_NEWPAGE) -#define p4d_mkuptodate(x) (p4d_val(x) &= ~_PAGE_NEWPAGE) +#define p4d_needsync(x) (p4d_val(x) & _PAGE_NEEDSYNC) +#define p4d_mkuptodate(x) (p4d_val(x) &= ~_PAGE_NEEDSYNC) #define pmd_pfn(pmd) (pmd_val(pmd) >> PAGE_SHIFT) #define pmd_page(pmd) phys_to_page(pmd_val(pmd) & PAGE_MASK) @@ -144,14 +145,9 @@ static inline int pte_young(pte_t pte) return pte_get_bits(pte, _PAGE_ACCESSED); } -static inline int pte_newpage(pte_t pte) +static inline int pte_needsync(pte_t pte) { - return pte_get_bits(pte, _PAGE_NEWPAGE); -} - -static inline int pte_newprot(pte_t pte) -{ - return(pte_present(pte) && (pte_get_bits(pte, _PAGE_NEWPROT))); + return pte_get_bits(pte, _PAGE_NEEDSYNC); } /* @@ -160,12 +156,6 @@ static inline int pte_newprot(pte_t pte) * ================================= */ -static inline pte_t pte_mknewprot(pte_t pte) -{ - pte_set_bits(pte, _PAGE_NEWPROT); - return(pte); -} - static inline pte_t pte_mkclean(pte_t pte) { pte_clear_bits(pte, _PAGE_DIRTY); @@ -180,19 +170,14 @@ static inline pte_t pte_mkold(pte_t pte) static inline pte_t pte_wrprotect(pte_t pte) { - if (likely(pte_get_bits(pte, _PAGE_RW))) - pte_clear_bits(pte, _PAGE_RW); - else - return pte; - return(pte_mknewprot(pte)); + pte_clear_bits(pte, _PAGE_RW); + return pte; } static inline pte_t pte_mkread(pte_t pte) { - if (unlikely(pte_get_bits(pte, _PAGE_USER))) - return pte; pte_set_bits(pte, _PAGE_USER); - return(pte_mknewprot(pte)); + return pte; } static inline pte_t pte_mkdirty(pte_t pte) @@ -209,23 +194,19 @@ static inline pte_t pte_mkyoung(pte_t pte) static inline pte_t pte_mkwrite_novma(pte_t pte) { - if (unlikely(pte_get_bits(pte, _PAGE_RW))) - return pte; pte_set_bits(pte, _PAGE_RW); - return(pte_mknewprot(pte)); + return pte; } static inline pte_t pte_mkuptodate(pte_t pte) { - pte_clear_bits(pte, _PAGE_NEWPAGE); - if(pte_present(pte)) - pte_clear_bits(pte, _PAGE_NEWPROT); - return(pte); + pte_clear_bits(pte, _PAGE_NEEDSYNC); + return pte; } -static inline pte_t pte_mknewpage(pte_t pte) +static inline pte_t pte_mkneedsync(pte_t pte) { - pte_set_bits(pte, _PAGE_NEWPAGE); + pte_set_bits(pte, _PAGE_NEEDSYNC); return(pte); } @@ -233,13 +214,11 @@ static inline void set_pte(pte_t *pteptr, pte_t pteval) { pte_copy(*pteptr, pteval); - /* If it's a swap entry, it needs to be marked _PAGE_NEWPAGE so - * fix_range knows to unmap it. _PAGE_NEWPROT is specific to - * mapped pages. + /* If it's a swap entry, it needs to be marked _PAGE_NEEDSYNC so + * update_pte_range knows to unmap it. */ - *pteptr = pte_mknewpage(*pteptr); - if(pte_present(*pteptr)) *pteptr = pte_mknewprot(*pteptr); + *pteptr = pte_mkneedsync(*pteptr); } #define PFN_PTE_SHIFT PAGE_SHIFT @@ -279,7 +258,7 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr, #define __HAVE_ARCH_PTE_SAME static inline int pte_same(pte_t pte_a, pte_t pte_b) { - return !((pte_val(pte_a) ^ pte_val(pte_b)) & ~_PAGE_NEWPAGE); + return !((pte_val(pte_a) ^ pte_val(pte_b)) & ~_PAGE_NEEDSYNC); } /* @@ -294,8 +273,6 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b) ({ pte_t pte; \ \ pte_set_val(pte, page_to_phys(page), (pgprot)); \ - if (pte_present(pte)) \ - pte_mknewprot(pte_mknewpage(pte)); \ pte;}) static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) @@ -329,7 +306,7 @@ extern pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr); * <--------------- offset ----------------> E < type -> 0 0 0 1 0 * * E is the exclusive marker that is not stored in swap entries. - * _PAGE_NEWPAGE (bit 1) is always set to 1 in set_pte(). + * _PAGE_NEEDSYNC (bit 1) is always set to 1 in set_pte(). */ #define __swp_type(x) (((x).val >> 5) & 0x1f) #define __swp_offset(x) ((x).val >> 11) diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h index bce4595798dae..5d6356eafffee 100644 --- a/arch/um/include/asm/processor-generic.h +++ b/arch/um/include/asm/processor-generic.h @@ -20,10 +20,7 @@ struct task_struct; struct mm_struct; struct thread_struct { - struct pt_regs regs; struct pt_regs *segv_regs; - void *fault_addr; - jmp_buf *fault_catcher; struct task_struct *prev_sched; struct arch_thread arch; jmp_buf switch_buf; @@ -33,12 +30,14 @@ struct thread_struct { void *arg; } thread; } request; + + /* Contains variable sized FP registers */ + struct pt_regs regs; }; #define INIT_THREAD \ { \ .regs = EMPTY_REGS, \ - .fault_addr = NULL, \ .prev_sched = NULL, \ .arch = INIT_ARCH_THREAD, \ .request = { } \ diff --git a/arch/um/include/asm/thread_info.h b/arch/um/include/asm/thread_info.h index c7b4b49826a2a..f9ad06fcc991a 100644 --- a/arch/um/include/asm/thread_info.h +++ b/arch/um/include/asm/thread_info.h @@ -17,35 +17,17 @@ #include struct thread_info { - struct task_struct *task; /* main task structure */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ - struct thread_info *real_thread; /* Points to non-IRQ stack */ - unsigned long aux_fp_regs[FP_SIZE]; /* auxiliary fp_regs to save/restore - them out-of-band */ }; #define INIT_THREAD_INFO(tsk) \ { \ - .task = &tsk, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ - .real_thread = NULL, \ -} - -/* how to get the thread information struct from C */ -static inline struct thread_info *current_thread_info(void) -{ - struct thread_info *ti; - unsigned long mask = THREAD_SIZE - 1; - void *p; - - asm volatile ("" : "=r" (p) : "0" (&ti)); - ti = (struct thread_info *) (((unsigned long)p) & ~mask); - return ti; } #endif diff --git a/arch/um/include/asm/tlbflush.h b/arch/um/include/asm/tlbflush.h index db997976b6ead..13a3009942be6 100644 --- a/arch/um/include/asm/tlbflush.h +++ b/arch/um/include/asm/tlbflush.h @@ -9,8 +9,8 @@ #include /* - * In UML, we need to sync the TLB over by using mmap/munmap/mprotect syscalls - * from the process handling the MM (which can be the kernel itself). + * In UML, we need to sync the TLB over by using mmap/munmap syscalls from + * the process handling the MM (which can be the kernel itself). * * To track updates, we can hook into set_ptes and flush_tlb_*. With set_ptes * we catch all PTE transitions where memory that was unusable becomes usable. diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h index 06292fca5a4da..ea65f151bf484 100644 --- a/arch/um/include/shared/as-layout.h +++ b/arch/um/include/shared/as-layout.h @@ -30,25 +30,23 @@ #include -struct cpu_task { - void *task; -}; +struct task_struct; +extern struct task_struct *cpu_tasks[]; -extern struct cpu_task cpu_tasks[]; +extern unsigned long long physmem_size; extern unsigned long high_physmem; extern unsigned long uml_physmem; extern unsigned long uml_reserved; extern unsigned long end_vm; extern unsigned long start_vm; -extern unsigned long long highmem; extern unsigned long brk_start; extern unsigned long host_task_size; extern unsigned long stub_start; -extern int linux_main(int argc, char **argv); +extern int linux_main(int argc, char **argv, char **envp); extern void uml_finishsetup(void); struct siginfo; diff --git a/arch/um/include/shared/common-offsets.h b/arch/um/include/shared/common-offsets.h index 579ed946a3a9a..73f3a4792ed8b 100644 --- a/arch/um/include/shared/common-offsets.h +++ b/arch/um/include/shared/common-offsets.h @@ -6,7 +6,6 @@ DEFINE(KERNEL_MADV_REMOVE, MADV_REMOVE); DEFINE(UM_KERN_PAGE_SIZE, PAGE_SIZE); DEFINE(UM_KERN_PAGE_MASK, PAGE_MASK); DEFINE(UM_KERN_PAGE_SHIFT, PAGE_SHIFT); -DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC); DEFINE(UM_GFP_KERNEL, GFP_KERNEL); DEFINE(UM_GFP_ATOMIC, GFP_ATOMIC); @@ -15,17 +14,3 @@ DEFINE(UM_THREAD_SIZE, THREAD_SIZE); DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC); DEFINE(UM_NSEC_PER_USEC, NSEC_PER_USEC); - -#ifdef CONFIG_PRINTK -DEFINE(UML_CONFIG_PRINTK, CONFIG_PRINTK); -#endif -#ifdef CONFIG_UML_X86 -DEFINE(UML_CONFIG_UML_X86, CONFIG_UML_X86); -#endif -#ifdef CONFIG_64BIT -DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT); -#endif -#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT -DEFINE(UML_CONFIG_UML_TIME_TRAVEL_SUPPORT, CONFIG_UML_TIME_TRAVEL_SUPPORT); -#endif - diff --git a/arch/um/include/shared/kern_util.h b/arch/um/include/shared/kern_util.h index d8ffd2db168e0..f21dc85175381 100644 --- a/arch/um/include/shared/kern_util.h +++ b/arch/um/include/shared/kern_util.h @@ -60,7 +60,6 @@ extern unsigned long from_irq_stack(int nested); extern int singlestepping(void); extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); -extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs); extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); extern void fatal_sigsegv(void) __attribute__ ((noreturn)); diff --git a/arch/um/include/shared/mem_user.h b/arch/um/include/shared/mem_user.h index 11a723a58545e..adfa08062f88e 100644 --- a/arch/um/include/shared/mem_user.h +++ b/arch/um/include/shared/mem_user.h @@ -47,10 +47,9 @@ extern int iomem_size; #define ROUND_4M(n) ((((unsigned long) (n)) + (1 << 22)) & ~((1 << 22) - 1)) extern unsigned long find_iomem(char *driver, unsigned long *len_out); -extern void mem_total_pages(unsigned long physmem, unsigned long iomem, - unsigned long highmem); +extern void mem_total_pages(unsigned long physmem, unsigned long iomem); extern void setup_physmem(unsigned long start, unsigned long usable, - unsigned long len, unsigned long long highmem); + unsigned long len); extern void map_memory(unsigned long virt, unsigned long phys, unsigned long len, int r, int w, int x); diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index 9a039d6f1f748..5babad8c5f75e 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -145,7 +145,6 @@ extern int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg); extern int os_get_ifname(int fd, char *namebuf); extern int os_set_slip(int fd); extern int os_mode_fd(int fd, int mode); -extern int os_fsync_file(int fd); extern int os_seek_file(int fd, unsigned long long offset); extern int os_open_file(const char *file, struct openflags flags, int mode); @@ -199,15 +198,11 @@ extern int create_mem_file(unsigned long long len); extern void report_enomem(void); /* process.c */ -extern unsigned long os_process_pc(int pid); -extern int os_process_parent(int pid); extern void os_alarm_process(int pid); -extern void os_stop_process(int pid); extern void os_kill_process(int pid, int reap_child); extern void os_kill_ptraced_process(int pid, int reap_child); extern int os_getpid(void); -extern int os_getpgrp(void); extern void init_new_thread_signals(void); @@ -220,6 +215,8 @@ extern int os_drop_memory(void *addr, int length); extern int can_drop_memory(void); extern int os_mincore(void *addr, unsigned long len); +void os_set_pdeathsig(void); + /* execvp.c */ extern int execvp_noalloc(char *buf, const char *file, char *const argv[]); /* helper.c */ @@ -244,7 +241,6 @@ extern void block_signals(void); extern void unblock_signals(void); extern int um_set_signals(int enable); extern int um_set_signals_trace(int enable); -extern int os_is_signal_stack(void); extern void deliver_alarm(void); extern void register_pm_wake_signal(void); extern void block_signals_hard(void); @@ -283,13 +279,11 @@ int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot, int phys_fd, unsigned long long offset); int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len); -int protect(struct mm_id *mm_idp, unsigned long addr, - unsigned long len, unsigned int prot); /* skas/process.c */ extern int is_skas_winch(int pid, int fd, void *data); extern int start_userspace(unsigned long stub_stack); -extern void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs); +extern void userspace(struct uml_pt_regs *regs); extern void new_thread(void *stack, jmp_buf *buf, void (*handler)(void)); extern void switch_threads(jmp_buf *me, jmp_buf *you); extern int start_idle_thread(void *stack, jmp_buf *switch_buf); @@ -329,9 +323,6 @@ extern int __ignore_sigio_fd(int fd); /* tty.c */ extern int get_pty(void); -/* sys-$ARCH/task_size.c */ -extern unsigned long os_get_top_address(void); - long syscall(long number, ...); /* irqflags tracing */ diff --git a/arch/um/include/shared/registers.h b/arch/um/include/shared/registers.h index a0450326521cd..7d81b2339a48e 100644 --- a/arch/um/include/shared/registers.h +++ b/arch/um/include/shared/registers.h @@ -8,12 +8,6 @@ #include -extern int save_i387_registers(int pid, unsigned long *fp_regs); -extern int restore_i387_registers(int pid, unsigned long *fp_regs); -extern int save_fp_registers(int pid, unsigned long *fp_regs); -extern int restore_fp_registers(int pid, unsigned long *fp_regs); -extern int save_fpx_registers(int pid, unsigned long *fp_regs); -extern int restore_fpx_registers(int pid, unsigned long *fp_regs); extern int init_pid_registers(int pid); extern void get_safe_registers(unsigned long *regs, unsigned long *fp_regs); extern int get_fp_registers(int pid, unsigned long *regs); diff --git a/arch/um/include/shared/skas/stub-data.h b/arch/um/include/shared/skas/stub-data.h index 2b6b44759dfaf..81a4cace032c9 100644 --- a/arch/um/include/shared/skas/stub-data.h +++ b/arch/um/include/shared/skas/stub-data.h @@ -12,6 +12,17 @@ #include #include +struct stub_init_data { + unsigned long stub_start; + + int stub_code_fd; + unsigned long stub_code_offset; + int stub_data_fd; + unsigned long stub_data_offset; + + unsigned long segv_handler; +}; + #define STUB_NEXT_SYSCALL(s) \ ((struct stub_syscall *) (((unsigned long) s) + (s)->cmd_len)) @@ -19,7 +30,6 @@ enum stub_syscall_type { STUB_SYSCALL_UNSET = 0, STUB_SYSCALL_MMAP, STUB_SYSCALL_MUNMAP, - STUB_SYSCALL_MPROTECT, }; struct stub_syscall { diff --git a/arch/um/include/shared/timetravel.h b/arch/um/include/shared/timetravel.h index c8db2f213dbac..7c2b277b7eb09 100644 --- a/arch/um/include/shared/timetravel.h +++ b/arch/um/include/shared/timetravel.h @@ -12,14 +12,13 @@ enum time_travel_mode { TT_MODE_EXTERNAL, }; -#if defined(UML_CONFIG_UML_TIME_TRAVEL_SUPPORT) || \ - defined(CONFIG_UML_TIME_TRAVEL_SUPPORT) +#if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT) extern enum time_travel_mode time_travel_mode; extern int time_travel_should_print_bc_msg; #else #define time_travel_mode TT_MODE_OFF #define time_travel_should_print_bc_msg 0 -#endif /* (UML_)CONFIG_UML_TIME_TRAVEL_SUPPORT */ +#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */ void _time_travel_print_bc_msg(void); static inline void time_travel_print_bc_msg(void) diff --git a/arch/um/include/shared/user.h b/arch/um/include/shared/user.h index bbab79c0c074a..139eb78a47674 100644 --- a/arch/um/include/shared/user.h +++ b/arch/um/include/shared/user.h @@ -38,7 +38,7 @@ extern void panic(const char *fmt, ...) #define UM_KERN_DEBUG KERN_DEBUG #define UM_KERN_CONT KERN_CONT -#ifdef UML_CONFIG_PRINTK +#if IS_ENABLED(CONFIG_PRINTK) #define printk(...) _printk(__VA_ARGS__) extern int _printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/arch/um/kernel/dtb.c b/arch/um/kernel/dtb.c index 8d78ced9e08f6..15c342426489d 100644 --- a/arch/um/kernel/dtb.c +++ b/arch/um/kernel/dtb.c @@ -31,6 +31,7 @@ void uml_dtb_init(void) static int __init uml_dtb_setup(char *line, int *add) { + *add = 0; dtb = line; return 0; } diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S index 3385d653ebd0d..a36b7918a011a 100644 --- a/arch/um/kernel/dyn.lds.S +++ b/arch/um/kernel/dyn.lds.S @@ -116,8 +116,6 @@ SECTIONS .fini_array : { *(.fini_array) } .data : { INIT_TASK_DATA(KERNEL_STACK_SIZE) - . = ALIGN(KERNEL_STACK_SIZE); - *(.data..init_irqstack) DATA_DATA *(.data.* .gnu.linkonce.d.*) SORT(CONSTRUCTORS) @@ -178,3 +176,6 @@ SECTIONS DISCARDS } + +ASSERT(__syscall_stub_end - __syscall_stub_start <= PAGE_SIZE, + "STUB code must not be larger than one page"); diff --git a/arch/um/kernel/initrd.c b/arch/um/kernel/initrd.c index 47b8cb1a11561..99dba827461c8 100644 --- a/arch/um/kernel/initrd.c +++ b/arch/um/kernel/initrd.c @@ -34,6 +34,7 @@ int __init read_initrd(void) static int __init uml_initrd_setup(char *line, int *add) { + *add = 0; initrd = line; return 0; } diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 534e91797f892..338450741aac5 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -674,115 +674,3 @@ void __init init_IRQ(void) /* Initialize EPOLL Loop */ os_setup_epoll(); } - -/* - * IRQ stack entry and exit: - * - * Unlike i386, UML doesn't receive IRQs on the normal kernel stack - * and switch over to the IRQ stack after some preparation. We use - * sigaltstack to receive signals on a separate stack from the start. - * These two functions make sure the rest of the kernel won't be too - * upset by being on a different stack. The IRQ stack has a - * thread_info structure at the bottom so that current et al continue - * to work. - * - * to_irq_stack copies the current task's thread_info to the IRQ stack - * thread_info and sets the tasks's stack to point to the IRQ stack. - * - * from_irq_stack copies the thread_info struct back (flags may have - * been modified) and resets the task's stack pointer. - * - * Tricky bits - - * - * What happens when two signals race each other? UML doesn't block - * signals with sigprocmask, SA_DEFER, or sa_mask, so a second signal - * could arrive while a previous one is still setting up the - * thread_info. - * - * There are three cases - - * The first interrupt on the stack - sets up the thread_info and - * handles the interrupt - * A nested interrupt interrupting the copying of the thread_info - - * can't handle the interrupt, as the stack is in an unknown state - * A nested interrupt not interrupting the copying of the - * thread_info - doesn't do any setup, just handles the interrupt - * - * The first job is to figure out whether we interrupted stack setup. - * This is done by xchging the signal mask with thread_info->pending. - * If the value that comes back is zero, then there is no setup in - * progress, and the interrupt can be handled. If the value is - * non-zero, then there is stack setup in progress. In order to have - * the interrupt handled, we leave our signal in the mask, and it will - * be handled by the upper handler after it has set up the stack. - * - * Next is to figure out whether we are the outer handler or a nested - * one. As part of setting up the stack, thread_info->real_thread is - * set to non-NULL (and is reset to NULL on exit). This is the - * nesting indicator. If it is non-NULL, then the stack is already - * set up and the handler can run. - */ - -static unsigned long pending_mask; - -unsigned long to_irq_stack(unsigned long *mask_out) -{ - struct thread_info *ti; - unsigned long mask, old; - int nested; - - mask = xchg(&pending_mask, *mask_out); - if (mask != 0) { - /* - * If any interrupts come in at this point, we want to - * make sure that their bits aren't lost by our - * putting our bit in. So, this loop accumulates bits - * until xchg returns the same value that we put in. - * When that happens, there were no new interrupts, - * and pending_mask contains a bit for each interrupt - * that came in. - */ - old = *mask_out; - do { - old |= mask; - mask = xchg(&pending_mask, old); - } while (mask != old); - return 1; - } - - ti = current_thread_info(); - nested = (ti->real_thread != NULL); - if (!nested) { - struct task_struct *task; - struct thread_info *tti; - - task = cpu_tasks[ti->cpu].task; - tti = task_thread_info(task); - - *ti = *tti; - ti->real_thread = tti; - task->stack = ti; - } - - mask = xchg(&pending_mask, 0); - *mask_out |= mask | nested; - return 0; -} - -unsigned long from_irq_stack(int nested) -{ - struct thread_info *ti, *to; - unsigned long mask; - - ti = current_thread_info(); - - pending_mask = 1; - - to = ti->real_thread; - current->stack = to; - ti->real_thread = NULL; - *to = *ti; - - mask = xchg(&pending_mask, 0); - return mask & ~1; -} - diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index a5b4fe2ad9315..53248ed04771d 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -51,8 +50,6 @@ EXPORT_SYMBOL(empty_zero_page); pgd_t swapper_pg_dir[PTRS_PER_PGD]; /* Initialized at boot time, and readonly after that */ -unsigned long long highmem; -EXPORT_SYMBOL(highmem); int kmalloc_ok = 0; /* Used during early boot */ @@ -98,7 +95,7 @@ static void __init one_page_table_init(pmd_t *pmd) static void __init one_md_table_init(pud_t *pud) { -#ifdef CONFIG_3_LEVEL_PGTABLES +#if CONFIG_PGTABLE_LEVELS > 2 pmd_t *pmd_table = (pmd_t *) memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); if (!pmd_table) panic("%s: Failed to allocate %lu bytes align=%lx\n", @@ -109,6 +106,19 @@ static void __init one_md_table_init(pud_t *pud) #endif } +static void __init one_ud_table_init(p4d_t *p4d) +{ +#if CONFIG_PGTABLE_LEVELS > 3 + pud_t *pud_table = (pud_t *) memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); + if (!pud_table) + panic("%s: Failed to allocate %lu bytes align=%lx\n", + __func__, PAGE_SIZE, PAGE_SIZE); + + set_p4d(p4d, __p4d(_KERNPG_TABLE + (unsigned long) __pa(pud_table))); + BUG_ON(pud_table != pud_offset(p4d, 0)); +#endif +} + static void __init fixrange_init(unsigned long start, unsigned long end, pgd_t *pgd_base) { @@ -126,6 +136,8 @@ static void __init fixrange_init(unsigned long start, unsigned long end, for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) { p4d = p4d_offset(pgd, vaddr); + if (p4d_none(*p4d)) + one_ud_table_init(p4d); pud = pud_offset(p4d, vaddr); if (pud_none(*pud)) one_md_table_init(pud); diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index fb2adfb499452..a74f17b033c41 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c @@ -22,19 +22,14 @@ static int physmem_fd = -1; unsigned long high_physmem; EXPORT_SYMBOL(high_physmem); -extern unsigned long long physmem_size; - -void __init mem_total_pages(unsigned long physmem, unsigned long iomem, - unsigned long highmem) +void __init mem_total_pages(unsigned long physmem, unsigned long iomem) { - unsigned long phys_pages, highmem_pages; - unsigned long iomem_pages, total_pages; + unsigned long phys_pages, iomem_pages, total_pages; - phys_pages = physmem >> PAGE_SHIFT; - iomem_pages = iomem >> PAGE_SHIFT; - highmem_pages = highmem >> PAGE_SHIFT; + phys_pages = physmem >> PAGE_SHIFT; + iomem_pages = iomem >> PAGE_SHIFT; - total_pages = phys_pages + iomem_pages + highmem_pages; + total_pages = phys_pages + iomem_pages; max_mapnr = total_pages; } @@ -64,13 +59,12 @@ void map_memory(unsigned long virt, unsigned long phys, unsigned long len, * @reserve_end: end address of the physical kernel memory. * @len: Length of total physical memory that should be mapped/made * available, in bytes. - * @highmem: Number of highmem bytes that should be mapped/made available. * - * Creates an unlinked temporary file of size (len + highmem) and memory maps + * Creates an unlinked temporary file of size (len) and memory maps * it on the last executable image address (uml_reserved). * * The offset is needed as the length of the total physical memory - * (len + highmem) includes the size of the memory used be the executable image, + * (len) includes the size of the memory used be the executable image, * but the mapped-to address is the last address of the executable image * (uml_reserved == end address of executable image). * @@ -78,24 +72,24 @@ void map_memory(unsigned long virt, unsigned long phys, unsigned long len, * of all user space processes/kernel tasks. */ void __init setup_physmem(unsigned long start, unsigned long reserve_end, - unsigned long len, unsigned long long highmem) + unsigned long len) { unsigned long reserve = reserve_end - start; - long map_size = len - reserve; + unsigned long map_size = len - reserve; int err; - if(map_size <= 0) { + if (len <= reserve) { os_warn("Too few physical memory! Needed=%lu, given=%lu\n", reserve, len); exit(1); } - physmem_fd = create_mem_file(len + highmem); + physmem_fd = create_mem_file(len); err = os_map_memory((void *) reserve_end, physmem_fd, reserve, map_size, 1, 1, 1); if (err < 0) { - os_warn("setup_physmem - mapping %ld bytes of memory at 0x%p " + os_warn("setup_physmem - mapping %lu bytes of memory at 0x%p " "failed - errno = %d\n", map_size, (void *) reserve_end, err); exit(1); @@ -107,9 +101,8 @@ void __init setup_physmem(unsigned long start, unsigned long reserve_end, */ os_seek_file(physmem_fd, __pa(__syscall_stub_start)); os_write_file(physmem_fd, __syscall_stub_start, PAGE_SIZE); - os_fsync_file(physmem_fd); - memblock_add(__pa(start), len + highmem); + memblock_add(__pa(start), len); memblock_reserve(__pa(start), reserve); min_low_pfn = PFN_UP(__pa(reserve_end)); @@ -137,10 +130,6 @@ int phys_mapping(unsigned long phys, unsigned long long *offset_out) region = region->next; } } - else if (phys < __pa(end_iomem) + highmem) { - fd = physmem_fd; - *offset_out = phys - iomem_size; - } return fd; } @@ -149,6 +138,8 @@ EXPORT_SYMBOL(phys_mapping); static int __init uml_mem_setup(char *line, int *add) { char *retptr; + + *add = 0; physmem_size = memparse(line,&retptr); return 0; } diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index be2856af6d4c3..30bdc0a87dc85 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -43,7 +43,8 @@ * cares about its entry, so it's OK if another processor is modifying its * entry. */ -struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { NULL } }; +struct task_struct *cpu_tasks[NR_CPUS]; +EXPORT_SYMBOL(cpu_tasks); void free_stack(unsigned long stack, int order) { @@ -64,7 +65,7 @@ unsigned long alloc_stack(int order, int atomic) static inline void set_current(struct task_struct *task) { - cpu_tasks[task_thread_info(task)->cpu] = ((struct cpu_task) { task }); + cpu_tasks[task_thread_info(task)->cpu] = task; } struct task_struct *__switch_to(struct task_struct *from, struct task_struct *to) @@ -116,7 +117,7 @@ void new_thread_handler(void) * callback returns only if the kernel thread execs a process */ fn(arg); - userspace(¤t->thread.regs.regs, current_thread_info()->aux_fp_regs); + userspace(¤t->thread.regs.regs); } /* Called magically, see new_thread_handler above */ @@ -133,7 +134,7 @@ static void fork_handler(void) current->thread.prev_sched = NULL; - userspace(¤t->thread.regs.regs, current_thread_info()->aux_fp_regs); + userspace(¤t->thread.regs.regs); } int copy_thread(struct task_struct * p, const struct kernel_clone_args *args) @@ -187,6 +188,13 @@ void initial_thread_cb(void (*proc)(void *), void *arg) kmalloc_ok = save_kmalloc_ok; } +int arch_dup_task_struct(struct task_struct *dst, + struct task_struct *src) +{ + memcpy(dst, src, arch_task_struct_size); + return 0; +} + void um_idle_sleep(void) { if (time_travel_mode != TT_MODE_OFF) @@ -287,11 +295,3 @@ unsigned long __get_wchan(struct task_struct *p) return 0; } - -int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu) -{ - int cpu = current_thread_info()->cpu; - - return save_i387_registers(userspace_pid[cpu], (unsigned long *) fpu); -} - diff --git a/arch/um/kernel/skas/.gitignore b/arch/um/kernel/skas/.gitignore new file mode 100644 index 0000000000000..c3409ced0f38c --- /dev/null +++ b/arch/um/kernel/skas/.gitignore @@ -0,0 +1,2 @@ +stub_exe +stub_exe.dbg diff --git a/arch/um/kernel/skas/Makefile b/arch/um/kernel/skas/Makefile index 6f86d53e3d693..3384be42691f8 100644 --- a/arch/um/kernel/skas/Makefile +++ b/arch/um/kernel/skas/Makefile @@ -3,14 +3,48 @@ # Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) # -obj-y := stub.o mmu.o process.o syscall.o uaccess.o +obj-y := stub.o mmu.o process.o syscall.o uaccess.o \ + stub_exe_embed.o + +# Stub executable + +stub_exe_objs-y := stub_exe.o + +stub_exe_objs := $(foreach F,$(stub_exe_objs-y),$(obj)/$F) + +# Object file containing the ELF executable +$(obj)/stub_exe_embed.o: $(src)/stub_exe_embed.S $(obj)/stub_exe + +$(obj)/stub_exe.dbg: $(stub_exe_objs) FORCE + $(call if_changed,stub_exe) + +$(obj)/stub_exe: OBJCOPYFLAGS := -S +$(obj)/stub_exe: $(obj)/stub_exe.dbg FORCE + $(call if_changed,objcopy) + +quiet_cmd_stub_exe = STUB_EXE $@ + cmd_stub_exe = $(CC) -nostdlib -o $@ \ + $(filter-out $(UM_GPROF_OPT) $(UM_GCOV_OPT),$(KBUILD_CFLAGS)) $(STUB_EXE_LDFLAGS) \ + $(filter %.o,$^) + +STUB_EXE_LDFLAGS = -Wl,-n -static + +targets += stub_exe.dbg stub_exe $(stub_exe_objs-y) + +# end # stub.o is in the stub, so it can't be built with profiling # GCC hardened also auto-enables -fpic, but we need %ebx so it can't work -> # disable it CFLAGS_stub.o := $(CFLAGS_NO_HARDENING) -UNPROFILE_OBJS := stub.o +CFLAGS_stub_exe.o := $(CFLAGS_NO_HARDENING) + +# Clang will call memset() from __builtin_alloca() when stack variable +# initialization is enabled, which is used in stub_exe.c. +CFLAGS_stub_exe.o += $(call cc-option, -ftrivial-auto-var-init=uninitialized) + +UNPROFILE_OBJS := stub.o stub_exe.o KCOV_INSTRUMENT := n include $(srctree)/arch/um/scripts/Makefile.rules diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 886ed5e656743..0eb5a1d3ba701 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -40,35 +40,13 @@ int init_new_context(struct task_struct *task, struct mm_struct *mm) goto out_free; } - /* - * Ensure the new MM is clean and nothing unwanted is mapped. - * - * TODO: We should clear the memory up to STUB_START to ensure there is - * nothing mapped there, i.e. we (currently) have: - * - * |- user memory -|- unused -|- stub -|- unused -| - * ^ TASK_SIZE ^ STUB_START - * - * Meaning we have two unused areas where we may still have valid - * mappings from our internal clone(). That isn't really a problem as - * userspace is not going to access them, but it is definitely not - * correct. - * - * However, we are "lucky" and if rseq is configured, then on 32 bit - * it will fall into the first empty range while on 64 bit it is going - * to use an anonymous mapping in the second range. As such, things - * continue to work for now as long as we don't start unmapping these - * areas. - * - * Change this to STUB_START once we have a clean userspace. - */ - unmap(new_id, 0, TASK_SIZE); + /* Ensure the new MM is clean and nothing unwanted is mapped */ + unmap(new_id, 0, STUB_START); return 0; out_free: - if (new_id->stack != 0) - free_pages(new_id->stack, ilog2(STUB_DATA_PAGES)); + free_pages(new_id->stack, ilog2(STUB_DATA_PAGES)); out: return ret; } diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index 68657988c8d1a..05dcdc057af94 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -22,15 +22,13 @@ static int __init start_kernel_proc(void *unused) { block_signals_trace(); - cpu_tasks[0].task = current; - start_kernel(); return 0; } extern int userspace_pid[]; -extern char cpu0_irqstack[]; +static char cpu0_irqstack[THREAD_SIZE] __aligned(THREAD_SIZE); int __init start_uml(void) { diff --git a/arch/um/kernel/skas/stub.c b/arch/um/kernel/skas/stub.c index 5d52ffa682dc4..796fc266d3bb0 100644 --- a/arch/um/kernel/skas/stub.c +++ b/arch/um/kernel/skas/stub.c @@ -35,16 +35,6 @@ static __always_inline int syscall_handler(struct stub_data *d) return -1; } break; - case STUB_SYSCALL_MPROTECT: - res = stub_syscall3(__NR_mprotect, - sc->mem.addr, sc->mem.length, - sc->mem.prot); - if (res) { - d->err = res; - d->syscall_data_len = i; - return -1; - } - break; default: d->err = -95; /* EOPNOTSUPP */ d->syscall_data_len = i; diff --git a/arch/um/kernel/skas/stub_exe.c b/arch/um/kernel/skas/stub_exe.c new file mode 100644 index 0000000000000..23c99b285e82e --- /dev/null +++ b/arch/um/kernel/skas/stub_exe.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +void _start(void); + +noinline static void real_init(void) +{ + struct stub_init_data init_data; + unsigned long res; + struct { + void *ss_sp; + int ss_flags; + size_t ss_size; + } stack = { + .ss_size = STUB_DATA_PAGES * UM_KERN_PAGE_SIZE, + }; + struct { + void *sa_handler_; + unsigned long sa_flags; + void *sa_restorer; + unsigned long long sa_mask; + } sa = { + /* Need to set SA_RESTORER (but the handler never returns) */ + .sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO | 0x04000000, + /* no need to mask any signals */ + .sa_mask = 0, + }; + + /* set a nice name */ + stub_syscall2(__NR_prctl, PR_SET_NAME, (unsigned long)"uml-userspace"); + + /* Make sure this process dies if the kernel dies */ + stub_syscall2(__NR_prctl, PR_SET_PDEATHSIG, SIGKILL); + + /* read information from STDIN and close it */ + res = stub_syscall3(__NR_read, 0, + (unsigned long)&init_data, sizeof(init_data)); + if (res != sizeof(init_data)) + stub_syscall1(__NR_exit, 10); + + stub_syscall1(__NR_close, 0); + + /* map stub code + data */ + res = stub_syscall6(STUB_MMAP_NR, + init_data.stub_start, UM_KERN_PAGE_SIZE, + PROT_READ | PROT_EXEC, MAP_FIXED | MAP_SHARED, + init_data.stub_code_fd, init_data.stub_code_offset); + if (res != init_data.stub_start) + stub_syscall1(__NR_exit, 11); + + res = stub_syscall6(STUB_MMAP_NR, + init_data.stub_start + UM_KERN_PAGE_SIZE, + STUB_DATA_PAGES * UM_KERN_PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, + init_data.stub_data_fd, init_data.stub_data_offset); + if (res != init_data.stub_start + UM_KERN_PAGE_SIZE) + stub_syscall1(__NR_exit, 12); + + /* setup signal stack inside stub data */ + stack.ss_sp = (void *)init_data.stub_start + UM_KERN_PAGE_SIZE; + stub_syscall2(__NR_sigaltstack, (unsigned long)&stack, 0); + + /* register SIGSEGV handler */ + sa.sa_handler_ = (void *) init_data.segv_handler; + res = stub_syscall4(__NR_rt_sigaction, SIGSEGV, (unsigned long)&sa, 0, + sizeof(sa.sa_mask)); + if (res != 0) + stub_syscall1(__NR_exit, 13); + + stub_syscall4(__NR_ptrace, PTRACE_TRACEME, 0, 0, 0); + + stub_syscall2(__NR_kill, stub_syscall0(__NR_getpid), SIGSTOP); + + stub_syscall1(__NR_exit, 14); + + __builtin_unreachable(); +} + +__attribute__((naked)) void _start(void) +{ + /* + * Since the stack after exec() starts at the top-most address, + * but that's exactly where we also want to map the stub data + * and code, this must: + * - push the stack by 1 code and STUB_DATA_PAGES data pages + * - call real_init() + * This way, real_init() can use the stack normally, while the + * original stack further down (higher address) will become + * inaccessible after the mmap() calls above. + */ + stub_start(real_init); +} diff --git a/arch/um/kernel/skas/stub_exe_embed.S b/arch/um/kernel/skas/stub_exe_embed.S new file mode 100644 index 0000000000000..6d8914fbe8f12 --- /dev/null +++ b/arch/um/kernel/skas/stub_exe_embed.S @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +__INITDATA + +SYM_DATA_START(stub_exe_start) + .incbin "arch/um/kernel/skas/stub_exe" +SYM_DATA_END_LABEL(stub_exe_start, SYM_L_GLOBAL, stub_exe_end) + +__FINIT diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c index 4bb8622dc5122..13ee5666668dd 100644 --- a/arch/um/kernel/sysrq.c +++ b/arch/um/kernel/sysrq.c @@ -32,12 +32,6 @@ void show_stack(struct task_struct *task, unsigned long *stack, struct pt_regs *segv_regs = current->thread.segv_regs; int i; - if (!segv_regs && os_is_signal_stack()) { - pr_err("Received SIGSEGV in SIGSEGV handler," - " aborting stack trace!\n"); - return; - } - if (!stack) stack = get_stack_pointer(task, segv_regs); @@ -52,5 +46,5 @@ void show_stack(struct task_struct *task, unsigned long *stack, } printk("%sCall Trace:\n", loglvl); - dump_trace(current, &stackops, (void *)loglvl); + dump_trace(task ?: current, &stackops, (void *)loglvl); } diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index 29b27b90581fb..1394568c02106 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -25,6 +25,8 @@ #include #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT +#include + enum time_travel_mode time_travel_mode; EXPORT_SYMBOL_GPL(time_travel_mode); @@ -47,6 +49,15 @@ static u16 time_travel_shm_id; static struct um_timetravel_schedshm *time_travel_shm; static union um_timetravel_schedshm_client *time_travel_shm_client; +unsigned long tt_extra_sched_jiffies; + +notrace unsigned long long sched_clock(void) +{ + return (unsigned long long)(jiffies - INITIAL_JIFFIES + + tt_extra_sched_jiffies) + * (NSEC_PER_SEC / HZ); +} + static void time_travel_set_time(unsigned long long ns) { if (unlikely(ns < time_travel_time)) @@ -443,6 +454,11 @@ static void time_travel_periodic_timer(struct time_travel_event *e) { time_travel_add_event(&time_travel_timer_event, time_travel_time + time_travel_timer_interval); + + /* clock tick; decrease extra jiffies by keeping sched_clock constant */ + if (tt_extra_sched_jiffies > 0) + tt_extra_sched_jiffies -= 1; + deliver_alarm(); } @@ -594,6 +610,10 @@ EXPORT_SYMBOL_GPL(time_travel_add_irq_event); static void time_travel_oneshot_timer(struct time_travel_event *e) { + /* clock tick; decrease extra jiffies by keeping sched_clock constant */ + if (tt_extra_sched_jiffies > 0) + tt_extra_sched_jiffies -= 1; + deliver_alarm(); } diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c index 548af31d4111b..cf7e0d4407f2c 100644 --- a/arch/um/kernel/tlb.c +++ b/arch/um/kernel/tlb.c @@ -23,9 +23,6 @@ struct vm_ops { int phys_fd, unsigned long long offset); int (*unmap)(struct mm_id *mm_idp, unsigned long virt, unsigned long len); - int (*mprotect)(struct mm_id *mm_idp, - unsigned long virt, unsigned long len, - unsigned int prot); }; static int kern_map(struct mm_id *mm_idp, @@ -44,15 +41,6 @@ static int kern_unmap(struct mm_id *mm_idp, return os_unmap_memory((void *)virt, len); } -static int kern_mprotect(struct mm_id *mm_idp, - unsigned long virt, unsigned long len, - unsigned int prot) -{ - return os_protect_memory((void *)virt, len, - prot & UM_PROT_READ, prot & UM_PROT_WRITE, - 1); -} - void report_enomem(void) { printk(KERN_ERR "UML ran out of memory on the host side! " @@ -65,33 +53,37 @@ static inline int update_pte_range(pmd_t *pmd, unsigned long addr, struct vm_ops *ops) { pte_t *pte; - int r, w, x, prot, ret = 0; + int ret = 0; pte = pte_offset_kernel(pmd, addr); do { - r = pte_read(*pte); - w = pte_write(*pte); - x = pte_exec(*pte); - if (!pte_young(*pte)) { - r = 0; - w = 0; - } else if (!pte_dirty(*pte)) - w = 0; - - prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) | - (x ? UM_PROT_EXEC : 0)); - if (pte_newpage(*pte)) { - if (pte_present(*pte)) { - __u64 offset; - unsigned long phys = pte_val(*pte) & PAGE_MASK; - int fd = phys_mapping(phys, &offset); - - ret = ops->mmap(ops->mm_idp, addr, PAGE_SIZE, - prot, fd, offset); - } else - ret = ops->unmap(ops->mm_idp, addr, PAGE_SIZE); - } else if (pte_newprot(*pte)) - ret = ops->mprotect(ops->mm_idp, addr, PAGE_SIZE, prot); + if (!pte_needsync(*pte)) + continue; + + if (pte_present(*pte)) { + __u64 offset; + unsigned long phys = pte_val(*pte) & PAGE_MASK; + int fd = phys_mapping(phys, &offset); + int r, w, x, prot; + + r = pte_read(*pte); + w = pte_write(*pte); + x = pte_exec(*pte); + if (!pte_young(*pte)) { + r = 0; + w = 0; + } else if (!pte_dirty(*pte)) + w = 0; + + prot = (r ? UM_PROT_READ : 0) | + (w ? UM_PROT_WRITE : 0) | + (x ? UM_PROT_EXEC : 0); + + ret = ops->mmap(ops->mm_idp, addr, PAGE_SIZE, + prot, fd, offset); + } else + ret = ops->unmap(ops->mm_idp, addr, PAGE_SIZE); + *pte = pte_mkuptodate(*pte); } while (pte++, addr += PAGE_SIZE, ((addr < end) && !ret)); return ret; @@ -109,7 +101,7 @@ static inline int update_pmd_range(pud_t *pud, unsigned long addr, do { next = pmd_addr_end(addr, end); if (!pmd_present(*pmd)) { - if (pmd_newpage(*pmd)) { + if (pmd_needsync(*pmd)) { ret = ops->unmap(ops->mm_idp, addr, next - addr); pmd_mkuptodate(*pmd); @@ -132,7 +124,7 @@ static inline int update_pud_range(p4d_t *p4d, unsigned long addr, do { next = pud_addr_end(addr, end); if (!pud_present(*pud)) { - if (pud_newpage(*pud)) { + if (pud_needsync(*pud)) { ret = ops->unmap(ops->mm_idp, addr, next - addr); pud_mkuptodate(*pud); @@ -155,7 +147,7 @@ static inline int update_p4d_range(pgd_t *pgd, unsigned long addr, do { next = p4d_addr_end(addr, end); if (!p4d_present(*p4d)) { - if (p4d_newpage(*p4d)) { + if (p4d_needsync(*p4d)) { ret = ops->unmap(ops->mm_idp, addr, next - addr); p4d_mkuptodate(*p4d); @@ -180,18 +172,16 @@ int um_tlb_sync(struct mm_struct *mm) if (mm == &init_mm) { ops.mmap = kern_map; ops.unmap = kern_unmap; - ops.mprotect = kern_mprotect; } else { ops.mmap = map; ops.unmap = unmap; - ops.mprotect = protect; } pgd = pgd_offset(mm, addr); do { next = pgd_addr_end(addr, mm->context.sync_tlb_range_to); if (!pgd_present(*pgd)) { - if (pgd_newpage(*pgd)) { + if (pgd_needsync(*pgd)) { ret = ops.unmap(ops.mm_idp, addr, next - addr); pgd_mkuptodate(*pgd); diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index 97c8df9c44017..cdaee3e942734 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -201,7 +201,6 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, struct uml_pt_regs *regs) { - jmp_buf *catcher; int si_code; int err; int is_write = FAULT_WRITE(fi); @@ -246,15 +245,8 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, address = 0; } - catcher = current->thread.fault_catcher; if (!err) goto out; - else if (catcher != NULL) { - current->thread.fault_addr = (void *) address; - UML_LONGJMP(catcher, 1); - } - else if (current->thread.fault_addr != NULL) - panic("fault_addr set but no fault catcher"); else if (!is_user && arch_fixup(ip, regs)) goto out; @@ -310,14 +302,6 @@ void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs) } } -void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs) -{ - if (current->thread.fault_catcher != NULL) - UML_LONGJMP(current->thread.fault_catcher, 1); - else - relay_signal(sig, si, regs); -} - void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) { do_IRQ(WINCH_IRQ, regs); diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index e8e8b54b3037d..8037a967225d8 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -65,9 +65,6 @@ struct cpuinfo_um boot_cpu_data = { EXPORT_SYMBOL(boot_cpu_data); -union thread_union cpu0_irqstack - __section(".data..init_irqstack") = - { .thread_info = INIT_THREAD_INFO(init_task) }; /* Changed in setup_arch, which is called in early boot */ static char host_info[(__NEW_UTS_LEN + 1) * 5]; @@ -131,7 +128,7 @@ static int have_root __initdata; static int have_console __initdata; /* Set in uml_mem_setup and modified in linux_main */ -long long physmem_size = 64 * 1024 * 1024; +unsigned long long physmem_size = 64 * 1024 * 1024; EXPORT_SYMBOL(physmem_size); static const char *usage_string = @@ -167,19 +164,6 @@ __uml_setup("root=", uml_root_setup, " root=/dev/ubd5\n\n" ); -static int __init no_skas_debug_setup(char *line, int *add) -{ - os_warn("'debug' is not necessary to gdb UML in skas mode - run\n"); - os_warn("'gdb linux'\n"); - - return 0; -} - -__uml_setup("debug", no_skas_debug_setup, -"debug\n" -" this flag is not needed to run gdb on UML in skas mode\n\n" -); - static int __init uml_console_setup(char *line, int *add) { have_console = 1; @@ -257,6 +241,8 @@ static struct notifier_block panic_exit_notifier = { void uml_finishsetup(void) { + cpu_tasks[0] = &init_task; + atomic_notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); @@ -302,7 +288,24 @@ static void parse_cache_line(char *line) } } -int __init linux_main(int argc, char **argv) +static unsigned long get_top_address(char **envp) +{ + unsigned long top_addr = (unsigned long) &top_addr; + int i; + + /* The earliest variable should be after the program name in ELF */ + for (i = 0; envp[i]; i++) { + if ((unsigned long) envp[i] > top_addr) + top_addr = (unsigned long) envp[i]; + } + + top_addr &= ~(UM_KERN_PAGE_SIZE - 1); + top_addr += UM_KERN_PAGE_SIZE; + + return top_addr; +} + +int __init linux_main(int argc, char **argv, char **envp) { unsigned long avail, diff; unsigned long virtmem_size, max_physmem; @@ -324,20 +327,23 @@ int __init linux_main(int argc, char **argv) if (have_console == 0) add_arg(DEFAULT_COMMAND_LINE_CONSOLE); - host_task_size = os_get_top_address(); - /* reserve a few pages for the stubs (taking care of data alignment) */ - /* align the data portion */ - BUILD_BUG_ON(!is_power_of_2(STUB_DATA_PAGES)); - stub_start = (host_task_size - 1) & ~(STUB_DATA_PAGES * PAGE_SIZE - 1); + host_task_size = get_top_address(envp); + /* reserve a few pages for the stubs */ + stub_start = host_task_size - STUB_DATA_PAGES * PAGE_SIZE; /* another page for the code portion */ stub_start -= PAGE_SIZE; host_task_size = stub_start; + /* Limit TASK_SIZE to what is addressable by the page table */ + task_size = host_task_size; + if (task_size > (unsigned long long) PTRS_PER_PGD * PGDIR_SIZE) + task_size = PTRS_PER_PGD * PGDIR_SIZE; + /* * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps * out */ - task_size = host_task_size & PGDIR_MASK; + task_size = task_size & PGDIR_MASK; /* OS sanity checks that need to happen before the kernel runs */ os_early_checks(); @@ -366,18 +372,15 @@ int __init linux_main(int argc, char **argv) setup_machinename(init_utsname()->machine); - highmem = 0; + physmem_size = (physmem_size + PAGE_SIZE - 1) & PAGE_MASK; iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; + max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC; - /* - * Zones have to begin on a 1 << MAX_PAGE_ORDER page boundary, - * so this makes sure that's true for highmem - */ - max_physmem &= ~((1 << (PAGE_SHIFT + MAX_PAGE_ORDER)) - 1); if (physmem_size + iomem_size > max_physmem) { - highmem = physmem_size + iomem_size - max_physmem; - physmem_size -= highmem; + physmem_size = max_physmem - iomem_size; + os_info("Physical memory size shrunk to %llu bytes\n", + physmem_size); } high_physmem = uml_physmem + physmem_size; @@ -398,6 +401,8 @@ int __init linux_main(int argc, char **argv) os_info("Kernel virtual memory size shrunk to %lu bytes\n", virtmem_size); + arch_task_struct_size = sizeof(struct task_struct) + host_fp_size; + os_flush_stdout(); return start_uml(); @@ -412,9 +417,9 @@ void __init setup_arch(char **cmdline_p) { u8 rng_seed[32]; - stack_protections((unsigned long) &init_thread_info); - setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); - mem_total_pages(physmem_size, iomem_size, highmem); + stack_protections((unsigned long) init_task.stack); + setup_physmem(uml_physmem, uml_reserved, physmem_size); + mem_total_pages(physmem_size, iomem_size); uml_dtb_init(); read_initrd(); diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S index 5c92d58a78e89..a409d4b66114f 100644 --- a/arch/um/kernel/uml.lds.S +++ b/arch/um/kernel/uml.lds.S @@ -77,8 +77,6 @@ SECTIONS .data : { INIT_TASK_DATA(KERNEL_STACK_SIZE) - . = ALIGN(KERNEL_STACK_SIZE); - *(.data..init_irqstack) DATA_DATA *(.gnu.linkonce.d*) CONSTRUCTORS diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile index 544e0b344c754..049dfa5bc9c69 100644 --- a/arch/um/os-Linux/Makefile +++ b/arch/um/os-Linux/Makefile @@ -12,6 +12,8 @@ obj-y = execvp.o file.o helper.o irq.o main.o mem.o process.o \ CFLAGS_signal.o += -Wframe-larger-than=4096 +CFLAGS_main.o += -Wno-frame-larger-than + obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o USER_OBJS := $(user-objs-y) elf_aux.o execvp.o file.o helper.o irq.o \ diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c index f1d03cf3957fe..a0d01c68ce3ee 100644 --- a/arch/um/os-Linux/file.c +++ b/arch/um/os-Linux/file.c @@ -255,12 +255,6 @@ void os_close_file(int fd) { close(fd); } -int os_fsync_file(int fd) -{ - if (fsync(fd) < 0) - return -errno; - return 0; -} int os_seek_file(int fd, unsigned long long offset) { diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c index f98ff79cdbf79..0afcdeb8995b7 100644 --- a/arch/um/os-Linux/main.c +++ b/arch/um/os-Linux/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -108,6 +109,21 @@ int __init main(int argc, char **argv, char **envp) char **new_argv; int ret, i, err; + /* Disable randomization and re-exec if it was changed successfully */ + ret = personality(PER_LINUX | ADDR_NO_RANDOMIZE); + if (ret >= 0 && (ret & (PER_LINUX | ADDR_NO_RANDOMIZE)) != + (PER_LINUX | ADDR_NO_RANDOMIZE)) { + char buf[4096] = {}; + ssize_t ret; + + ret = readlink("/proc/self/exe", buf, sizeof(buf)); + if (ret < 0 || ret >= sizeof(buf)) { + perror("readlink failure"); + exit(1); + } + execve(buf, argv, envp); + } + set_stklim(); setup_env_path(); @@ -140,7 +156,7 @@ int __init main(int argc, char **argv, char **envp) #endif change_sig(SIGPIPE, 0); - ret = linux_main(argc, argv); + ret = linux_main(argc, argv, envp); /* * Disable SIGPROF - I have no idea why libc doesn't do this or turn @@ -182,6 +198,7 @@ int __init main(int argc, char **argv, char **envp) } extern void *__real_malloc(int); +extern void __real_free(void *); /* workaround for -Wmissing-prototypes warnings */ void *__wrap_malloc(int size); @@ -219,10 +236,6 @@ void *__wrap_calloc(int n, int size) return ptr; } -extern void __real_free(void *); - -extern unsigned long high_physmem; - void __wrap_free(void *ptr) { unsigned long addr = (unsigned long) ptr; diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index cf44d386f23ce..72f302f4d197f 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c @@ -39,10 +39,22 @@ void kasan_map_memory(void *start, size_t len) strerror(errno)); exit(1); } + + if (madvise(start, len, MADV_DONTDUMP)) { + os_info("Couldn't set MAD_DONTDUMP on shadow memory: %s\n.", + strerror(errno)); + exit(1); + } + + if (madvise(start, len, MADV_DONTFORK)) { + os_info("Couldn't set MADV_DONTFORK on shadow memory: %s\n.", + strerror(errno)); + exit(1); + } } /* Set by make_tempfile() during early boot. */ -static char *tempdir = NULL; +char *tempdir = NULL; /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ static int __init check_tmpfs(const char *dir) diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index e52dd37ddadcc..9f086f9394202 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -12,94 +12,18 @@ #include #include #include +#include #include #include #include #include #include -#define ARBITRARY_ADDR -1 -#define FAILURE_PID -1 - -#define STAT_PATH_LEN sizeof("/proc/#######/stat\0") -#define COMM_SCANF "%*[^)])" - -unsigned long os_process_pc(int pid) -{ - char proc_stat[STAT_PATH_LEN], buf[256]; - unsigned long pc = ARBITRARY_ADDR; - int fd, err; - - sprintf(proc_stat, "/proc/%d/stat", pid); - fd = open(proc_stat, O_RDONLY, 0); - if (fd < 0) { - printk(UM_KERN_ERR "os_process_pc - couldn't open '%s', " - "errno = %d\n", proc_stat, errno); - goto out; - } - CATCH_EINTR(err = read(fd, buf, sizeof(buf))); - if (err < 0) { - printk(UM_KERN_ERR "os_process_pc - couldn't read '%s', " - "err = %d\n", proc_stat, errno); - goto out_close; - } - os_close_file(fd); - pc = ARBITRARY_ADDR; - if (sscanf(buf, "%*d " COMM_SCANF " %*c %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %*d %lu", &pc) != 1) - printk(UM_KERN_ERR "os_process_pc - couldn't find pc in '%s'\n", - buf); - out_close: - close(fd); - out: - return pc; -} - -int os_process_parent(int pid) -{ - char stat[STAT_PATH_LEN]; - char data[256]; - int parent = FAILURE_PID, n, fd; - - if (pid == -1) - return parent; - - snprintf(stat, sizeof(stat), "/proc/%d/stat", pid); - fd = open(stat, O_RDONLY, 0); - if (fd < 0) { - printk(UM_KERN_ERR "Couldn't open '%s', errno = %d\n", stat, - errno); - return parent; - } - - CATCH_EINTR(n = read(fd, data, sizeof(data))); - close(fd); - - if (n < 0) { - printk(UM_KERN_ERR "Couldn't read '%s', errno = %d\n", stat, - errno); - return parent; - } - - parent = FAILURE_PID; - n = sscanf(data, "%*d " COMM_SCANF " %*c %d", &parent); - if (n != 1) - printk(UM_KERN_ERR "Failed to scan '%s'\n", data); - - return parent; -} - void os_alarm_process(int pid) { kill(pid, SIGALRM); } -void os_stop_process(int pid) -{ - kill(pid, SIGSTOP); -} - void os_kill_process(int pid, int reap_child) { kill(pid, SIGKILL); @@ -130,11 +54,6 @@ int os_getpid(void) return syscall(__NR_getpid); } -int os_getpgrp(void) -{ - return getpgrp(); -} - int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len, int r, int w, int x) { @@ -285,3 +204,8 @@ void init_new_thread_signals(void) set_handler(SIGIO); signal(SIGWINCH, SIG_IGN); } + +void os_set_pdeathsig(void) +{ + prctl(PR_SET_PDEATHSIG, SIGKILL); +} diff --git a/arch/um/os-Linux/registers.c b/arch/um/os-Linux/registers.c index bd80b921add06..d7ca148807b28 100644 --- a/arch/um/os-Linux/registers.c +++ b/arch/um/os-Linux/registers.c @@ -10,11 +10,12 @@ #include #include #include +#include /* This is set once at boot time and not changed thereafter */ static unsigned long exec_regs[MAX_REG_NR]; -static unsigned long exec_fp_regs[FP_SIZE]; +static unsigned long *exec_fp_regs; int init_pid_registers(int pid) { @@ -24,7 +25,11 @@ int init_pid_registers(int pid) if (err < 0) return -errno; - arch_init_registers(pid); + err = arch_init_registers(pid); + if (err < 0) + return err; + + exec_fp_regs = malloc(host_fp_size); get_fp_registers(pid, exec_fp_regs); return 0; } @@ -34,5 +39,5 @@ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs) memcpy(regs, exec_regs, sizeof(exec_regs)); if (fp_regs) - memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs)); + memcpy(fp_regs, exec_fp_regs, host_fp_size); } diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c index 9e71794839e87..9aac8def4d635 100644 --- a/arch/um/os-Linux/sigio.c +++ b/arch/um/os-Linux/sigio.c @@ -55,6 +55,7 @@ static int write_sigio_thread(void *unused) int i, n, respond_fd; char c; + os_set_pdeathsig(); os_fix_helper_signals(); fds = ¤t_poll; while (1) { diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index b11ed66c8bb0e..9ea7269ffb778 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -26,7 +26,7 @@ void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = { [SIGFPE] = relay_signal, [SIGILL] = relay_signal, [SIGWINCH] = winch, - [SIGBUS] = bus_handler, + [SIGBUS] = relay_signal, [SIGSEGV] = segv_handler, [SIGIO] = sigio_handler, }; @@ -65,7 +65,7 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc) #define SIGALRM_MASK (1 << SIGALRM_BIT) int signals_enabled; -#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT +#if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT) static int signals_blocked, signals_blocked_pending; #endif static unsigned int signals_pending; @@ -75,7 +75,7 @@ static void sig_handler(int sig, struct siginfo *si, mcontext_t *mc) { int enabled = signals_enabled; -#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT +#if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT) if ((signals_blocked || __atomic_load_n(&signals_blocked_pending, __ATOMIC_SEQ_CST)) && (sig == SIGIO)) { @@ -190,43 +190,8 @@ static void hard_handler(int sig, siginfo_t *si, void *p) { ucontext_t *uc = p; mcontext_t *mc = &uc->uc_mcontext; - unsigned long pending = 1UL << sig; - do { - int nested, bail; - - /* - * pending comes back with one bit set for each - * interrupt that arrived while setting up the stack, - * plus a bit for this interrupt, plus the zero bit is - * set if this is a nested interrupt. - * If bail is true, then we interrupted another - * handler setting up the stack. In this case, we - * have to return, and the upper handler will deal - * with this interrupt. - */ - bail = to_irq_stack(&pending); - if (bail) - return; - - nested = pending & 1; - pending &= ~1; - - while ((sig = ffs(pending)) != 0){ - sig--; - pending &= ~(1 << sig); - (*handlers[sig])(sig, (struct siginfo *)si, mc); - } - - /* - * Again, pending comes back with a mask of signals - * that arrived while tearing down the stack. If this - * is non-zero, we just go back, set up the stack - * again, and handle the new interrupts. - */ - if (!nested) - pending = from_irq_stack(nested); - } while (pending); + (*handlers[sig])(sig, (struct siginfo *)si, mc); } void set_handler(int sig) @@ -297,7 +262,7 @@ void unblock_signals(void) return; signals_enabled = 1; -#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT +#if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT) deliver_time_travel_irqs(); #endif @@ -389,7 +354,7 @@ int um_set_signals_trace(int enable) return ret; } -#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT +#if IS_ENABLED(CONFIG_UML_TIME_TRAVEL_SUPPORT) void mark_sigio_pending(void) { /* @@ -487,11 +452,3 @@ void unblock_signals_hard(void) unblocking = false; } #endif - -int os_is_signal_stack(void) -{ - stack_t ss; - sigaltstack(NULL, &ss); - - return ss.ss_flags & SS_ONSTACK; -} diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index 9a13ac23c6064..d7f1814b0e5ab 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -217,24 +217,3 @@ int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len) return 0; } - -int protect(struct mm_id *mm_idp, unsigned long addr, unsigned long len, - unsigned int prot) -{ - struct stub_syscall *sc; - - /* Compress with previous syscall if that is possible */ - sc = syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MPROTECT, addr); - if (sc && sc->mem.prot == prot) { - sc->mem.length += len; - return 0; - } - - sc = syscall_stub_alloc(mm_idp); - sc->syscall = STUB_SYSCALL_MPROTECT; - sc->mem.addr = addr; - sc->mem.length = len; - sc->mem.prot = prot; - - return 0; -} diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index b6f656bcffb17..f683cfc9e51a5 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -10,8 +10,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -141,16 +144,10 @@ void wait_stub_done(int pid) extern unsigned long current_stub_stack(void); -static void get_skas_faultinfo(int pid, struct faultinfo *fi, unsigned long *aux_fp_regs) +static void get_skas_faultinfo(int pid, struct faultinfo *fi) { int err; - err = get_fp_registers(pid, aux_fp_regs); - if (err < 0) { - printk(UM_KERN_ERR "save_fp_registers returned %d\n", - err); - fatal_sigsegv(); - } err = ptrace(PTRACE_CONT, pid, 0, SIGSEGV); if (err) { printk(UM_KERN_ERR "Failed to continue stub, pid = %d, " @@ -164,18 +161,11 @@ static void get_skas_faultinfo(int pid, struct faultinfo *fi, unsigned long *aux * the stub stack page. We just have to copy it. */ memcpy(fi, (void *)current_stub_stack(), sizeof(*fi)); - - err = put_fp_registers(pid, aux_fp_regs); - if (err < 0) { - printk(UM_KERN_ERR "put_fp_registers returned %d\n", - err); - fatal_sigsegv(); - } } -static void handle_segv(int pid, struct uml_pt_regs *regs, unsigned long *aux_fp_regs) +static void handle_segv(int pid, struct uml_pt_regs *regs) { - get_skas_faultinfo(pid, ®s->faultinfo, aux_fp_regs); + get_skas_faultinfo(pid, ®s->faultinfo); segv(regs->faultinfo, 0, 1, NULL); } @@ -189,69 +179,131 @@ static void handle_trap(int pid, struct uml_pt_regs *regs) extern char __syscall_stub_start[]; -/** - * userspace_tramp() - userspace trampoline - * @stack: pointer to the new userspace stack page - * - * The userspace trampoline is used to setup a new userspace process in start_userspace() after it was clone()'ed. - * This function will run on a temporary stack page. - * It ptrace()'es itself, then - * Two pages are mapped into the userspace address space: - * - STUB_CODE (with EXEC), which contains the skas stub code - * - STUB_DATA (with R/W), which contains a data page that is used to transfer certain data between the UML userspace process and the UML kernel. - * Also for the userspace process a SIGSEGV handler is installed to catch pagefaults in the userspace process. - * And last the process stops itself to give control to the UML kernel for this userspace process. - * - * Return: Always zero, otherwise the current userspace process is ended with non null exit() call - */ +static int stub_exe_fd; + static int userspace_tramp(void *stack) { - struct sigaction sa; - void *addr; - int fd; + char *const argv[] = { "uml-userspace", NULL }; + int pipe_fds[2]; unsigned long long offset; - unsigned long segv_handler = STUB_CODE + - (unsigned long) stub_segv_handler - - (unsigned long) __syscall_stub_start; - - ptrace(PTRACE_TRACEME, 0, 0, 0); - - signal(SIGTERM, SIG_DFL); - signal(SIGWINCH, SIG_IGN); - - fd = phys_mapping(uml_to_phys(__syscall_stub_start), &offset); - addr = mmap64((void *) STUB_CODE, UM_KERN_PAGE_SIZE, - PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset); - if (addr == MAP_FAILED) { - os_info("mapping mmap stub at 0x%lx failed, errno = %d\n", - STUB_CODE, errno); - exit(1); + struct stub_init_data init_data = { + .stub_start = STUB_START, + .segv_handler = STUB_CODE + + (unsigned long) stub_segv_handler - + (unsigned long) __syscall_stub_start, + }; + struct iomem_region *iomem; + int ret; + + init_data.stub_code_fd = phys_mapping(uml_to_phys(__syscall_stub_start), + &offset); + init_data.stub_code_offset = MMAP_OFFSET(offset); + + init_data.stub_data_fd = phys_mapping(uml_to_phys(stack), &offset); + init_data.stub_data_offset = MMAP_OFFSET(offset); + + /* Set CLOEXEC on all FDs and then unset on all memory related FDs */ + close_range(0, ~0U, CLOSE_RANGE_CLOEXEC); + + fcntl(init_data.stub_data_fd, F_SETFD, 0); + for (iomem = iomem_regions; iomem; iomem = iomem->next) + fcntl(iomem->fd, F_SETFD, 0); + + /* Create a pipe for init_data (no CLOEXEC) and dup2 to STDIN */ + if (pipe(pipe_fds)) + exit(2); + + if (dup2(pipe_fds[0], 0) < 0) + exit(3); + close(pipe_fds[0]); + + /* Write init_data and close write side */ + ret = write(pipe_fds[1], &init_data, sizeof(init_data)); + close(pipe_fds[1]); + + if (ret != sizeof(init_data)) + exit(4); + + execveat(stub_exe_fd, "", argv, NULL, AT_EMPTY_PATH); + + exit(5); +} + +extern char stub_exe_start[]; +extern char stub_exe_end[]; + +extern char *tempdir; + +#define STUB_EXE_NAME_TEMPLATE "/uml-userspace-XXXXXX" + +#ifndef MFD_EXEC +#define MFD_EXEC 0x0010U +#endif + +static int __init init_stub_exe_fd(void) +{ + size_t written = 0; + char *tmpfile = NULL; + + stub_exe_fd = memfd_create("uml-userspace", + MFD_EXEC | MFD_CLOEXEC | MFD_ALLOW_SEALING); + + if (stub_exe_fd < 0) { + printk(UM_KERN_INFO "Could not create executable memfd, using temporary file!"); + + tmpfile = malloc(strlen(tempdir) + + strlen(STUB_EXE_NAME_TEMPLATE) + 1); + if (tmpfile == NULL) + panic("Failed to allocate memory for stub binary name"); + + strcpy(tmpfile, tempdir); + strcat(tmpfile, STUB_EXE_NAME_TEMPLATE); + + stub_exe_fd = mkstemp(tmpfile); + if (stub_exe_fd < 0) + panic("Could not create temporary file for stub binary: %d", + -errno); } - fd = phys_mapping(uml_to_phys(stack), &offset); - addr = mmap((void *) STUB_DATA, - STUB_DATA_PAGES * UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED | MAP_SHARED, fd, offset); - if (addr == MAP_FAILED) { - os_info("mapping segfault stack at 0x%lx failed, errno = %d\n", - STUB_DATA, errno); - exit(1); + while (written < stub_exe_end - stub_exe_start) { + ssize_t res = write(stub_exe_fd, stub_exe_start + written, + stub_exe_end - stub_exe_start - written); + if (res < 0) { + if (errno == EINTR) + continue; + + if (tmpfile) + unlink(tmpfile); + panic("Failed write stub binary: %d", -errno); + } + + written += res; } - set_sigstack((void *) STUB_DATA, STUB_DATA_PAGES * UM_KERN_PAGE_SIZE); - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO; - sa.sa_sigaction = (void *) segv_handler; - sa.sa_restorer = NULL; - if (sigaction(SIGSEGV, &sa, NULL) < 0) { - os_info("%s - setting SIGSEGV handler failed - errno = %d\n", - __func__, errno); - exit(1); + if (!tmpfile) { + fcntl(stub_exe_fd, F_ADD_SEALS, + F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL); + } else { + if (fchmod(stub_exe_fd, 00500) < 0) { + unlink(tmpfile); + panic("Could not make stub binary executable: %d", + -errno); + } + + close(stub_exe_fd); + stub_exe_fd = open(tmpfile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); + if (stub_exe_fd < 0) { + unlink(tmpfile); + panic("Could not reopen stub binary: %d", -errno); + } + + unlink(tmpfile); + free(tmpfile); } - kill(os_getpid(), SIGSTOP); return 0; } +__initcall(init_stub_exe_fd); int userspace_pid[NR_CPUS]; @@ -270,7 +322,7 @@ int start_userspace(unsigned long stub_stack) { void *stack; unsigned long sp; - int pid, status, n, flags, err; + int pid, status, n, err; /* setup a temporary stack page */ stack = mmap(NULL, UM_KERN_PAGE_SIZE, @@ -286,10 +338,10 @@ int start_userspace(unsigned long stub_stack) /* set stack pointer to the end of the stack page, so it can grow downwards */ sp = (unsigned long)stack + UM_KERN_PAGE_SIZE; - flags = CLONE_FILES | SIGCHLD; - /* clone into new userspace process */ - pid = clone(userspace_tramp, (void *) sp, flags, (void *) stub_stack); + pid = clone(userspace_tramp, (void *) sp, + CLONE_VFORK | CLONE_VM | SIGCHLD, + (void *)stub_stack); if (pid < 0) { err = -errno; printk(UM_KERN_ERR "%s : clone failed, errno = %d\n", @@ -336,7 +388,10 @@ int start_userspace(unsigned long stub_stack) return err; } -void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs) +int unscheduled_userspace_iterations; +extern unsigned long tt_extra_sched_jiffies; + +void userspace(struct uml_pt_regs *regs) { int err, status, op, pid = userspace_pid[0]; siginfo_t si; @@ -345,6 +400,29 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs) interrupt_end(); while (1) { + /* + * When we are in time-travel mode, userspace can theoretically + * do a *lot* of work without being scheduled. The problem with + * this is that it will prevent kernel bookkeeping (primarily + * the RCU) from running and this can for example cause OOM + * situations. + * + * This code accounts a jiffie against the scheduling clock + * after the defined userspace iterations in the same thread. + * By doing so the situation is effectively prevented. + */ + if (time_travel_mode == TT_MODE_INFCPU || + time_travel_mode == TT_MODE_EXTERNAL) { +#ifdef CONFIG_UML_MAX_USERSPACE_ITERATIONS + if (CONFIG_UML_MAX_USERSPACE_ITERATIONS && + unscheduled_userspace_iterations++ > + CONFIG_UML_MAX_USERSPACE_ITERATIONS) { + tt_extra_sched_jiffies += 1; + unscheduled_userspace_iterations = 0; + } +#endif + } + time_travel_print_bc_msg(); current_mm_sync(); @@ -435,11 +513,11 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs) case SIGSEGV: if (PTRACE_FULL_FAULTINFO) { get_skas_faultinfo(pid, - ®s->faultinfo, aux_fp_regs); + ®s->faultinfo); (*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si, regs); } - else handle_segv(pid, regs, aux_fp_regs); + else handle_segv(pid, regs); break; case SIGTRAP + 0x80: handle_trap(pid, regs); @@ -487,6 +565,8 @@ void new_thread(void *stack, jmp_buf *buf, void (*handler)(void)) void switch_threads(jmp_buf *me, jmp_buf *you) { + unscheduled_userspace_iterations = 0; + if (UML_SETJMP(me) == 0) UML_LONGJMP(you, 1); } @@ -570,6 +650,7 @@ static bool noreboot; static int __init noreboot_cmd_param(char *str, int *add) { + *add = 0; noreboot = true; return 0; } diff --git a/arch/um/os-Linux/umid.c b/arch/um/os-Linux/umid.c index e09d65b05d1ca..eb523ab1e2189 100644 --- a/arch/um/os-Linux/umid.c +++ b/arch/um/os-Linux/umid.c @@ -358,6 +358,8 @@ char *get_umid(void) static int __init set_uml_dir(char *name, int *add) { + *add = 0; + if (*name == '\0') { os_warn("uml_dir can't be an empty string\n"); return 0; diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c index 1dca4ffbd572f..4193e04d7e4a7 100644 --- a/arch/um/os-Linux/util.c +++ b/arch/um/os-Linux/util.c @@ -52,8 +52,8 @@ void setup_machinename(char *machine_out) struct utsname host; uname(&host); -#ifdef UML_CONFIG_UML_X86 -# ifndef UML_CONFIG_64BIT +#if IS_ENABLED(CONFIG_UML_X86) +# if !IS_ENABLED(CONFIG_64BIT) if (!strcmp(host.machine, "x86_64")) { strcpy(machine_out, "i686"); return; diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6c633d93c6390..9d7bd0ae48c42 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -128,6 +128,8 @@ config X86 select ARCH_SUPPORTS_LTO_CLANG select ARCH_SUPPORTS_LTO_CLANG_THIN select ARCH_SUPPORTS_RT + select ARCH_SUPPORTS_AUTOFDO_CLANG + select ARCH_SUPPORTS_PROPELLER_CLANG if X86_64 select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF if X86_CMPXCHG64 select ARCH_USE_MEMTEST diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index ea33439a5d006..17b6590748c00 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -317,6 +317,9 @@ #define X86_FEATURE_ZEN1 (11*32+31) /* CPU based on Zen1 microarchitecture */ /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ +#define X86_FEATURE_SHA512 (12*32+ 0) /* SHA512 instructions */ +#define X86_FEATURE_SM3 (12*32+ 1) /* SM3 instructions */ +#define X86_FEATURE_SM4 (12*32+ 2) /* SM4 instructions */ #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* "avx_vnni" AVX VNNI instructions */ #define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* "avx512_bf16" AVX512 BFLOAT16 instructions */ #define X86_FEATURE_CMPCCXADD (12*32+ 7) /* CMPccXADD instructions */ diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h index 99d345b686fa2..6e2458088800a 100644 --- a/arch/x86/include/asm/futex.h +++ b/arch/x86/include/asm/futex.h @@ -48,7 +48,9 @@ do { \ static __always_inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) { - if (!user_access_begin(uaddr, sizeof(u32))) + if (can_do_masked_user_access()) + uaddr = masked_user_access_begin(uaddr); + else if (!user_access_begin(uaddr, sizeof(u32))) return -EFAULT; switch (op) { @@ -84,7 +86,9 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, { int ret = 0; - if (!user_access_begin(uaddr, sizeof(u32))) + if (can_do_masked_user_access()) + uaddr = masked_user_access_begin(uaddr); + else if (!user_access_begin(uaddr, sizeof(u32))) return -EFAULT; asm volatile("\n" "1:\t" LOCK_PREFIX "cmpxchgl %3, %2\n" diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h index cbbef32517f00..3f1c1d6c0da12 100644 --- a/arch/x86/include/asm/jump_label.h +++ b/arch/x86/include/asm/jump_label.h @@ -12,35 +12,28 @@ #include #include -#define JUMP_TABLE_ENTRY \ +#define JUMP_TABLE_ENTRY(key, label) \ ".pushsection __jump_table, \"aw\" \n\t" \ _ASM_ALIGN "\n\t" \ ".long 1b - . \n\t" \ - ".long %l[l_yes] - . \n\t" \ - _ASM_PTR "%c0 + %c1 - .\n\t" \ + ".long " label " - . \n\t" \ + _ASM_PTR " " key " - . \n\t" \ ".popsection \n\t" +/* This macro is also expanded on the Rust side. */ #ifdef CONFIG_HAVE_JUMP_LABEL_HACK - -static __always_inline bool arch_static_branch(struct static_key *key, bool branch) -{ - asm goto("1:" - "jmp %l[l_yes] # objtool NOPs this \n\t" - JUMP_TABLE_ENTRY - : : "i" (key), "i" (2 | branch) : : l_yes); - - return false; -l_yes: - return true; -} - +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1: jmp " label " # objtool NOPs this \n\t" \ + JUMP_TABLE_ENTRY(key " + 2", label) #else /* !CONFIG_HAVE_JUMP_LABEL_HACK */ +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1: .byte " __stringify(BYTES_NOP5) "\n\t" \ + JUMP_TABLE_ENTRY(key, label) +#endif /* CONFIG_HAVE_JUMP_LABEL_HACK */ static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch) { - asm goto("1:" - ".byte " __stringify(BYTES_NOP5) "\n\t" - JUMP_TABLE_ENTRY + asm goto(ARCH_STATIC_BRANCH_ASM("%c0 + %c1", "%l[l_yes]") : : "i" (key), "i" (branch) : : l_yes); return false; @@ -48,13 +41,11 @@ static __always_inline bool arch_static_branch(struct static_key * const key, co return true; } -#endif /* CONFIG_HAVE_JUMP_LABEL_HACK */ - static __always_inline bool arch_static_branch_jump(struct static_key * const key, const bool branch) { asm goto("1:" "jmp %l[l_yes]\n\t" - JUMP_TABLE_ENTRY + JUMP_TABLE_ENTRY("%c0 + %c1", "%l[l_yes]") : : "i" (key), "i" (branch) : : l_yes); return false; diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index 861d080ed4c6a..5aff7222e40fa 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -34,6 +34,7 @@ KVM_X86_OP(set_msr) KVM_X86_OP(get_segment_base) KVM_X86_OP(get_segment) KVM_X86_OP(get_cpl) +KVM_X86_OP(get_cpl_no_cache) KVM_X86_OP(set_segment) KVM_X86_OP(get_cs_db_l_bits) KVM_X86_OP(is_valid_cr0) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 6d9f763a7bb9d..e159e44a6a1b6 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1306,7 +1307,6 @@ struct kvm_arch { bool pre_fault_allowed; struct hlist_head mmu_page_hash[KVM_NUM_MMU_PAGES]; struct list_head active_mmu_pages; - struct list_head zapped_obsolete_pages; /* * A list of kvm_mmu_page structs that, if zapped, could possibly be * replaced by an NX huge page. A shadow page is on this list if its @@ -1443,7 +1443,8 @@ struct kvm_arch { bool sgx_provisioning_allowed; struct kvm_x86_pmu_event_filter __rcu *pmu_event_filter; - struct task_struct *nx_huge_page_recovery_thread; + struct vhost_task *nx_huge_page_recovery_thread; + u64 nx_huge_page_last; #ifdef CONFIG_X86_64 /* The number of TDP MMU pages across all roots. */ @@ -1656,6 +1657,7 @@ struct kvm_x86_ops { void (*get_segment)(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); int (*get_cpl)(struct kvm_vcpu *vcpu); + int (*get_cpl_no_cache)(struct kvm_vcpu *vcpu); void (*set_segment)(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); void (*get_cs_db_l_bits)(struct kvm_vcpu *vcpu, int *db, int *l); @@ -1955,8 +1957,8 @@ void kvm_mmu_try_split_huge_pages(struct kvm *kvm, const struct kvm_memory_slot *memslot, u64 start, u64 end, int target_level); -void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, - const struct kvm_memory_slot *memslot); +void kvm_mmu_recover_huge_pages(struct kvm *kvm, + const struct kvm_memory_slot *memslot); void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm, const struct kvm_memory_slot *memslot); void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen); @@ -2359,7 +2361,8 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages); KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT | \ KVM_X86_QUIRK_FIX_HYPERCALL_INSN | \ KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS | \ - KVM_X86_QUIRK_SLOT_ZAP_ALL) + KVM_X86_QUIRK_SLOT_ZAP_ALL | \ + KVM_X86_QUIRK_STUFF_FEATURE_MSRS) /* * KVM previously used a u32 field in kvm_run to indicate the hypercall was diff --git a/arch/x86/include/asm/tlb.h b/arch/x86/include/asm/tlb.h index 580636cdc257b..4d3c9d00d6b6b 100644 --- a/arch/x86/include/asm/tlb.h +++ b/arch/x86/include/asm/tlb.h @@ -34,4 +34,8 @@ static inline void __tlb_remove_table(void *table) free_page_and_swap_cache(table); } +static inline void invlpg(unsigned long addr) +{ + asm volatile("invlpg (%0)" ::"r" (addr) : "memory"); +} #endif /* _ASM_X86_TLB_H */ diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index a8debbf2f7028..88585c1de416f 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -440,6 +440,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_FIX_HYPERCALL_INSN (1 << 5) #define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6) #define KVM_X86_QUIRK_SLOT_ZAP_ALL (1 << 7) +#define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 823f44f7bc946..d8408aafeed98 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -798,6 +798,7 @@ static void init_amd_bd(struct cpuinfo_x86 *c) static const struct x86_cpu_desc erratum_1386_microcode[] = { AMD_CPU_DESC(0x17, 0x1, 0x2, 0x0800126e), AMD_CPU_DESC(0x17, 0x31, 0x0, 0x08301052), + {}, }; static void fix_erratum_1386(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 06a516f6795b3..a5c28975c608e 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -169,7 +169,7 @@ static void ppin_init(struct cpuinfo_x86 *c) } clear_ppin: - clear_cpu_cap(c, info->feature); + setup_clear_cpu_cap(info->feature); } static void default_init(struct cpuinfo_x86 *c) @@ -2389,12 +2389,12 @@ void __init arch_cpu_finalize_init(void) alternative_instructions(); if (IS_ENABLED(CONFIG_X86_64)) { - unsigned long USER_PTR_MAX = TASK_SIZE_MAX-1; + unsigned long USER_PTR_MAX = TASK_SIZE_MAX; /* * Enable this when LAM is gated on LASS support if (cpu_feature_enabled(X86_FEATURE_LAM)) - USER_PTR_MAX = (1ul << 63) - PAGE_SIZE - 1; + USER_PTR_MAX = (1ul << 63) - PAGE_SIZE; */ runtime_const_init(ptr, USER_PTR_MAX); diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 31a73715d7553..fb5d0c67fbab1 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "internal.h" @@ -483,11 +484,25 @@ static void scan_containers(u8 *ucode, size_t size, struct cont_desc *desc) } } -static int __apply_microcode_amd(struct microcode_amd *mc) +static int __apply_microcode_amd(struct microcode_amd *mc, unsigned int psize) { + unsigned long p_addr = (unsigned long)&mc->hdr.data_code; u32 rev, dummy; - native_wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc->hdr.data_code); + native_wrmsrl(MSR_AMD64_PATCH_LOADER, p_addr); + + if (x86_family(bsp_cpuid_1_eax) == 0x17) { + unsigned long p_addr_end = p_addr + psize - 1; + + invlpg(p_addr); + + /* + * Flush next page too if patch image is crossing a page + * boundary. + */ + if (p_addr >> PAGE_SHIFT != p_addr_end >> PAGE_SHIFT) + invlpg(p_addr_end); + } /* verify patch application was successful */ native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); @@ -529,7 +544,7 @@ static bool early_apply_microcode(u32 old_rev, void *ucode, size_t size) if (old_rev > mc->hdr.patch_id) return ret; - return !__apply_microcode_amd(mc); + return !__apply_microcode_amd(mc, desc.psize); } static bool get_builtin_microcode(struct cpio_data *cp) @@ -745,7 +760,7 @@ void reload_ucode_amd(unsigned int cpu) rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); if (rev < mc->hdr.patch_id) { - if (!__apply_microcode_amd(mc)) + if (!__apply_microcode_amd(mc, p->size)) pr_info_once("reload revision: 0x%08x\n", mc->hdr.patch_id); } } @@ -798,7 +813,7 @@ static enum ucode_state apply_microcode_amd(int cpu) goto out; } - if (__apply_microcode_amd(mc_amd)) { + if (__apply_microcode_amd(mc_amd, p->size)) { pr_err("CPU%d: update failed for patch_level=0x%08x\n", cpu, mc_amd->hdr.patch_id); return UCODE_ERROR; diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 68efd8cd8bf1b..fab3ac9a4574a 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -420,6 +420,10 @@ SECTIONS STABS_DEBUG DWARF_DEBUG +#ifdef CONFIG_PROPELLER_CLANG + .llvm_bb_addr_map : { *(.llvm_bb_addr_map) } +#endif + ELF_DETAILS DISCARDS diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index f09f13c01c6bb..ea2c4f21c1ca9 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -18,10 +18,10 @@ menuconfig VIRTUALIZATION if VIRTUALIZATION config KVM_X86 - def_tristate KVM if KVM_INTEL || KVM_AMD - depends on X86_LOCAL_APIC + def_tristate KVM if (KVM_INTEL != n || KVM_AMD != n) select KVM_COMMON select KVM_GENERIC_MMU_NOTIFIER + select KVM_ELIDE_TLB_FLUSH_IF_YOUNG select HAVE_KVM_IRQCHIP select HAVE_KVM_PFNCACHE select HAVE_KVM_DIRTY_RING_TSO @@ -29,6 +29,7 @@ config KVM_X86 select HAVE_KVM_IRQ_BYPASS select HAVE_KVM_IRQ_ROUTING select HAVE_KVM_READONLY_MEM + select VHOST_TASK select KVM_ASYNC_PF select USER_RETURN_NOTIFIER select KVM_MMIO @@ -49,6 +50,7 @@ config KVM_X86 config KVM tristate "Kernel-based Virtual Machine (KVM) support" + depends on X86_LOCAL_APIC help Support hosting fully virtualized guest machines using hardware virtualization extensions. You will need a fairly recent diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 41786b834b163..097bdc022d0f4 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -690,7 +690,9 @@ void kvm_set_cpu_caps(void) kvm_cpu_cap_set(X86_FEATURE_TSC_ADJUST); kvm_cpu_cap_set(X86_FEATURE_ARCH_CAPABILITIES); - if (boot_cpu_has(X86_FEATURE_IBPB) && boot_cpu_has(X86_FEATURE_IBRS)) + if (boot_cpu_has(X86_FEATURE_AMD_IBPB_RET) && + boot_cpu_has(X86_FEATURE_AMD_IBPB) && + boot_cpu_has(X86_FEATURE_AMD_IBRS)) kvm_cpu_cap_set(X86_FEATURE_SPEC_CTRL); if (boot_cpu_has(X86_FEATURE_STIBP)) kvm_cpu_cap_set(X86_FEATURE_INTEL_STIBP); @@ -698,14 +700,14 @@ void kvm_set_cpu_caps(void) kvm_cpu_cap_set(X86_FEATURE_SPEC_CTRL_SSBD); kvm_cpu_cap_mask(CPUID_7_1_EAX, - F(AVX_VNNI) | F(AVX512_BF16) | F(CMPCCXADD) | - F(FZRM) | F(FSRS) | F(FSRC) | - F(AMX_FP16) | F(AVX_IFMA) | F(LAM) + F(SHA512) | F(SM3) | F(SM4) | F(AVX_VNNI) | F(AVX512_BF16) | + F(CMPCCXADD) | F(FZRM) | F(FSRS) | F(FSRC) | F(AMX_FP16) | + F(AVX_IFMA) | F(LAM) ); kvm_cpu_cap_init_kvm_defined(CPUID_7_1_EDX, - F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(PREFETCHITI) | - F(AMX_COMPLEX) | F(AVX10) + F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(AMX_COMPLEX) | + F(AVX_VNNI_INT16) | F(PREFETCHITI) | F(AVX10) ); kvm_cpu_cap_init_kvm_defined(CPUID_7_2_EDX, @@ -755,7 +757,7 @@ void kvm_set_cpu_caps(void) F(CLZERO) | F(XSAVEERPTR) | F(WBNOINVD) | F(AMD_IBPB) | F(AMD_IBRS) | F(AMD_SSBD) | F(VIRT_SSBD) | F(AMD_SSB_NO) | F(AMD_STIBP) | F(AMD_STIBP_ALWAYS_ON) | - F(AMD_PSFD) + F(AMD_PSFD) | F(AMD_IBPB_RET) ); /* @@ -763,8 +765,12 @@ void kvm_set_cpu_caps(void) * arch/x86/kernel/cpu/bugs.c is kind enough to * record that in cpufeatures so use them. */ - if (boot_cpu_has(X86_FEATURE_IBPB)) + if (boot_cpu_has(X86_FEATURE_IBPB)) { kvm_cpu_cap_set(X86_FEATURE_AMD_IBPB); + if (boot_cpu_has(X86_FEATURE_SPEC_CTRL) && + !boot_cpu_has_bug(X86_BUG_EIBRS_PBRSB)) + kvm_cpu_cap_set(X86_FEATURE_AMD_IBPB_RET); + } if (boot_cpu_has(X86_FEATURE_IBRS)) kvm_cpu_cap_set(X86_FEATURE_AMD_IBRS); if (boot_cpu_has(X86_FEATURE_STIBP)) diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index 41697cca354e6..c8dc66eddefda 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -2,7 +2,6 @@ #ifndef ARCH_X86_KVM_CPUID_H #define ARCH_X86_KVM_CPUID_H -#include "x86.h" #include "reverse_cpuid.h" #include #include diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index e72aed25d7212..60986f67c35a8 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -651,9 +651,10 @@ static inline u8 ctxt_virt_addr_bits(struct x86_emulate_ctxt *ctxt) } static inline bool emul_is_noncanonical_address(u64 la, - struct x86_emulate_ctxt *ctxt) + struct x86_emulate_ctxt *ctxt, + unsigned int flags) { - return !__is_canonical_address(la, ctxt_virt_addr_bits(ctxt)); + return !ctxt->ops->is_canonical_addr(ctxt, la, flags); } /* @@ -1733,7 +1734,8 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, if (ret != X86EMUL_CONTINUE) return ret; if (emul_is_noncanonical_address(get_desc_base(&seg_desc) | - ((u64)base3 << 32), ctxt)) + ((u64)base3 << 32), ctxt, + X86EMUL_F_DT_LOAD)) return emulate_gp(ctxt, err_code); } @@ -2516,8 +2518,8 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt) ss_sel = cs_sel + 8; cs.d = 0; cs.l = 1; - if (emul_is_noncanonical_address(rcx, ctxt) || - emul_is_noncanonical_address(rdx, ctxt)) + if (emul_is_noncanonical_address(rcx, ctxt, 0) || + emul_is_noncanonical_address(rdx, ctxt, 0)) return emulate_gp(ctxt, 0); break; } @@ -3494,7 +3496,8 @@ static int em_lgdt_lidt(struct x86_emulate_ctxt *ctxt, bool lgdt) if (rc != X86EMUL_CONTINUE) return rc; if (ctxt->mode == X86EMUL_MODE_PROT64 && - emul_is_noncanonical_address(desc_ptr.address, ctxt)) + emul_is_noncanonical_address(desc_ptr.address, ctxt, + X86EMUL_F_DT_LOAD)) return emulate_gp(ctxt, 0); if (lgdt) ctxt->ops->set_gdt(ctxt, &desc_ptr); diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index b1eb46e26b2e6..36a8786db2916 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -43,6 +43,18 @@ BUILD_KVM_GPR_ACCESSORS(r14, R14) BUILD_KVM_GPR_ACCESSORS(r15, R15) #endif +/* + * Using the register cache from interrupt context is generally not allowed, as + * caching a register and marking it available/dirty can't be done atomically, + * i.e. accesses from interrupt context may clobber state or read stale data if + * the vCPU task is in the process of updating the cache. The exception is if + * KVM is handling a PMI IRQ/NMI VM-Exit, as that bound code sequence doesn't + * touch the cache, it runs after the cache is reset (post VM-Exit), and PMIs + * need to access several registers that are cacheable. + */ +#define kvm_assert_register_caching_allowed(vcpu) \ + lockdep_assert_once(in_task() || kvm_arch_pmi_in_guest(vcpu)) + /* * avail dirty * 0 0 register in VMCS/VMCB @@ -53,24 +65,28 @@ BUILD_KVM_GPR_ACCESSORS(r15, R15) static inline bool kvm_register_is_available(struct kvm_vcpu *vcpu, enum kvm_reg reg) { + kvm_assert_register_caching_allowed(vcpu); return test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); } static inline bool kvm_register_is_dirty(struct kvm_vcpu *vcpu, enum kvm_reg reg) { + kvm_assert_register_caching_allowed(vcpu); return test_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty); } static inline void kvm_register_mark_available(struct kvm_vcpu *vcpu, enum kvm_reg reg) { + kvm_assert_register_caching_allowed(vcpu); __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); } static inline void kvm_register_mark_dirty(struct kvm_vcpu *vcpu, enum kvm_reg reg) { + kvm_assert_register_caching_allowed(vcpu); __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); __set_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty); } @@ -84,6 +100,7 @@ static inline void kvm_register_mark_dirty(struct kvm_vcpu *vcpu, static __always_inline bool kvm_register_test_and_mark_available(struct kvm_vcpu *vcpu, enum kvm_reg reg) { + kvm_assert_register_caching_allowed(vcpu); return arch___test_and_set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); } diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index 55a18e2f2dcd9..10495fffb8905 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -94,6 +94,8 @@ struct x86_instruction_info { #define X86EMUL_F_FETCH BIT(1) #define X86EMUL_F_IMPLICIT BIT(2) #define X86EMUL_F_INVLPG BIT(3) +#define X86EMUL_F_MSR BIT(4) +#define X86EMUL_F_DT_LOAD BIT(5) struct x86_emulate_ops { void (*vm_bugged)(struct x86_emulate_ctxt *ctxt); @@ -235,6 +237,9 @@ struct x86_emulate_ops { gva_t (*get_untagged_addr)(struct x86_emulate_ctxt *ctxt, gva_t addr, unsigned int flags); + + bool (*is_canonical_addr)(struct x86_emulate_ctxt *ctxt, gva_t addr, + unsigned int flags); }; /* Type, address-of, and value of an instruction's operand. */ diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 95c6beb8ce279..3c83951c619ea 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -382,7 +382,7 @@ enum { DIRTY }; -void kvm_recalculate_apic_map(struct kvm *kvm) +static void kvm_recalculate_apic_map(struct kvm *kvm) { struct kvm_apic_map *new, *old = NULL; struct kvm_vcpu *vcpu; @@ -2577,7 +2577,7 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) return (tpr & 0xf0) >> 4; } -void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value) +static void __kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value) { u64 old_value = vcpu->arch.apic_base; struct kvm_lapic *apic = vcpu->arch.apic; @@ -2625,6 +2625,31 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value) } } +int kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value, bool host_initiated) +{ + enum lapic_mode old_mode = kvm_get_apic_mode(vcpu); + enum lapic_mode new_mode = kvm_apic_mode(value); + + if (vcpu->arch.apic_base == value) + return 0; + + u64 reserved_bits = kvm_vcpu_reserved_gpa_bits_raw(vcpu) | 0x2ff | + (guest_cpuid_has(vcpu, X86_FEATURE_X2APIC) ? 0 : X2APIC_ENABLE); + + if ((value & reserved_bits) != 0 || new_mode == LAPIC_MODE_INVALID) + return 1; + if (!host_initiated) { + if (old_mode == LAPIC_MODE_X2APIC && new_mode == LAPIC_MODE_XAPIC) + return 1; + if (old_mode == LAPIC_MODE_DISABLED && new_mode == LAPIC_MODE_X2APIC) + return 1; + } + + __kvm_apic_set_base(vcpu, value); + kvm_recalculate_apic_map(vcpu->kvm); + return 0; +} + void kvm_apic_update_apicv(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic = vcpu->arch.apic; @@ -2654,7 +2679,6 @@ void kvm_apic_update_apicv(struct kvm_vcpu *vcpu) int kvm_alloc_apic_access_page(struct kvm *kvm) { - struct page *page; void __user *hva; int ret = 0; @@ -2670,17 +2694,6 @@ int kvm_alloc_apic_access_page(struct kvm *kvm) goto out; } - page = gfn_to_page(kvm, APIC_DEFAULT_PHYS_BASE >> PAGE_SHIFT); - if (is_error_page(page)) { - ret = -EFAULT; - goto out; - } - - /* - * Do not pin the page in memory, so that memory hot-unplug - * is able to migrate it. - */ - put_page(page); kvm->arch.apic_access_memslot_enabled = true; out: mutex_unlock(&kvm->slots_lock); @@ -2735,7 +2748,14 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event) msr_val = APIC_DEFAULT_PHYS_BASE | MSR_IA32_APICBASE_ENABLE; if (kvm_vcpu_is_reset_bsp(vcpu)) msr_val |= MSR_IA32_APICBASE_BSP; - kvm_lapic_set_base(vcpu, msr_val); + + /* + * Use the inner helper to avoid an extra recalcuation of the + * optimized APIC map if some other task has dirtied the map. + * The recalculation needed for this vCPU will be done after + * all APIC state has been initialized (see below). + */ + __kvm_apic_set_base(vcpu, msr_val); } if (!apic) @@ -3076,7 +3096,6 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) kvm_x86_call(apicv_pre_state_restore)(vcpu); - kvm_lapic_set_base(vcpu, vcpu->arch.apic_base); /* set SPIV separately to get count of SW disabled APICs right */ apic_set_spiv(apic, *((u32 *)(s->regs + APIC_SPIV))); diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 1b8ef9856422a..24add38beaf0b 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -95,8 +95,6 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event); u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu); -void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value); -void kvm_recalculate_apic_map(struct kvm *kvm); void kvm_apic_set_version(struct kvm_vcpu *vcpu); void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu); bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, @@ -117,11 +115,9 @@ bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src, struct kvm_lapic_irq *irq, int *r, struct dest_map *dest_map); void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high); -u64 kvm_get_apic_base(struct kvm_vcpu *vcpu); -int kvm_set_apic_base(struct kvm_vcpu *vcpu, struct msr_data *msr_info); +int kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value, bool host_initiated); int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s); int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s); -enum lapic_mode kvm_get_apic_mode(struct kvm_vcpu *vcpu); int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); u64 kvm_get_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu); @@ -271,6 +267,11 @@ static inline enum lapic_mode kvm_apic_mode(u64 apic_base) return apic_base & (MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE); } +static inline enum lapic_mode kvm_get_apic_mode(struct kvm_vcpu *vcpu) +{ + return kvm_apic_mode(vcpu->arch.apic_base); +} + static inline u8 kvm_xapic_id(struct kvm_lapic *apic) { return kvm_lapic_get_reg(apic, APIC_ID) >> 24; diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 9dc5dd43ae7f2..e9322358678b6 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -4,6 +4,7 @@ #include #include "kvm_cache_regs.h" +#include "x86.h" #include "cpuid.h" extern bool __read_mostly enable_mmio_caching; diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 8e853a5fc867b..22e7ad2351231 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -179,7 +179,6 @@ struct kvm_shadow_walk_iterator { static struct kmem_cache *pte_list_desc_cache; struct kmem_cache *mmu_page_header_cache; -static struct percpu_counter kvm_total_used_mmu_pages; static void mmu_spte_set(u64 *sptep, u64 spte); @@ -485,11 +484,12 @@ static void mmu_spte_set(u64 *sptep, u64 new_spte) __set_spte(sptep, new_spte); } -/* - * Update the SPTE (excluding the PFN), but do not track changes in its - * accessed/dirty status. +/* Rules for using mmu_spte_update: + * Update the state bits, it means the mapped pfn is not changed. + * + * Returns true if the TLB needs to be flushed */ -static u64 mmu_spte_update_no_track(u64 *sptep, u64 new_spte) +static bool mmu_spte_update(u64 *sptep, u64 new_spte) { u64 old_spte = *sptep; @@ -498,7 +498,7 @@ static u64 mmu_spte_update_no_track(u64 *sptep, u64 new_spte) if (!is_shadow_present_pte(old_spte)) { mmu_spte_set(sptep, new_spte); - return old_spte; + return false; } if (!spte_has_volatile_bits(old_spte)) @@ -506,53 +506,10 @@ static u64 mmu_spte_update_no_track(u64 *sptep, u64 new_spte) else old_spte = __update_clear_spte_slow(sptep, new_spte); - WARN_ON_ONCE(spte_to_pfn(old_spte) != spte_to_pfn(new_spte)); + WARN_ON_ONCE(!is_shadow_present_pte(old_spte) || + spte_to_pfn(old_spte) != spte_to_pfn(new_spte)); - return old_spte; -} - -/* Rules for using mmu_spte_update: - * Update the state bits, it means the mapped pfn is not changed. - * - * Whenever an MMU-writable SPTE is overwritten with a read-only SPTE, remote - * TLBs must be flushed. Otherwise rmap_write_protect will find a read-only - * spte, even though the writable spte might be cached on a CPU's TLB. - * - * Returns true if the TLB needs to be flushed - */ -static bool mmu_spte_update(u64 *sptep, u64 new_spte) -{ - bool flush = false; - u64 old_spte = mmu_spte_update_no_track(sptep, new_spte); - - if (!is_shadow_present_pte(old_spte)) - return false; - - /* - * For the spte updated out of mmu-lock is safe, since - * we always atomically update it, see the comments in - * spte_has_volatile_bits(). - */ - if (is_mmu_writable_spte(old_spte) && - !is_writable_pte(new_spte)) - flush = true; - - /* - * Flush TLB when accessed/dirty states are changed in the page tables, - * to guarantee consistency between TLB and page tables. - */ - - if (is_accessed_spte(old_spte) && !is_accessed_spte(new_spte)) { - flush = true; - kvm_set_pfn_accessed(spte_to_pfn(old_spte)); - } - - if (is_dirty_spte(old_spte) && !is_dirty_spte(new_spte)) { - flush = true; - kvm_set_pfn_dirty(spte_to_pfn(old_spte)); - } - - return flush; + return leaf_spte_change_needs_tlb_flush(old_spte, new_spte); } /* @@ -563,10 +520,8 @@ static bool mmu_spte_update(u64 *sptep, u64 new_spte) */ static u64 mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) { - kvm_pfn_t pfn; u64 old_spte = *sptep; int level = sptep_to_sp(sptep)->role.level; - struct page *page; if (!is_shadow_present_pte(old_spte) || !spte_has_volatile_bits(old_spte)) @@ -578,24 +533,6 @@ static u64 mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) return old_spte; kvm_update_page_stats(kvm, level, -1); - - pfn = spte_to_pfn(old_spte); - - /* - * KVM doesn't hold a reference to any pages mapped into the guest, and - * instead uses the mmu_notifier to ensure that KVM unmaps any pages - * before they are reclaimed. Sanity check that, if the pfn is backed - * by a refcounted page, the refcount is elevated. - */ - page = kvm_pfn_to_refcounted_page(pfn); - WARN_ON_ONCE(page && !page_count(page)); - - if (is_accessed_spte(old_spte)) - kvm_set_pfn_accessed(pfn); - - if (is_dirty_spte(old_spte)) - kvm_set_pfn_dirty(pfn); - return old_spte; } @@ -1250,16 +1187,6 @@ static bool spte_clear_dirty(u64 *sptep) return mmu_spte_update(sptep, spte); } -static bool spte_wrprot_for_clear_dirty(u64 *sptep) -{ - bool was_writable = test_and_clear_bit(PT_WRITABLE_SHIFT, - (unsigned long *)sptep); - if (was_writable && !spte_ad_enabled(*sptep)) - kvm_set_pfn_dirty(spte_to_pfn(*sptep)); - - return was_writable; -} - /* * Gets the GFN ready for another round of dirty logging by clearing the * - D bit on ad-enabled SPTEs, and @@ -1275,7 +1202,8 @@ static bool __rmap_clear_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head, for_each_rmap_spte(rmap_head, &iter, sptep) if (spte_ad_need_write_protect(*sptep)) - flush |= spte_wrprot_for_clear_dirty(sptep); + flush |= test_and_clear_bit(PT_WRITABLE_SHIFT, + (unsigned long *)sptep); else flush |= spte_clear_dirty(sptep); @@ -1640,15 +1568,12 @@ static bool kvm_rmap_age_gfn_range(struct kvm *kvm, (unsigned long *)sptep); } else { /* - * Capture the dirty status of the page, so that - * it doesn't get lost when the SPTE is marked - * for access tracking. + * WARN if mmu_spte_update() signals the need + * for a TLB flush, as Access tracking a SPTE + * should never trigger an _immediate_ flush. */ - if (is_writable_pte(spte)) - kvm_set_pfn_dirty(spte_to_pfn(spte)); - spte = mark_spte_for_access_track(spte); - mmu_spte_update_no_track(sptep, spte); + WARN_ON_ONCE(mmu_spte_update(sptep, spte)); } young = true; } @@ -1696,27 +1621,15 @@ static void kvm_mmu_check_sptes_at_free(struct kvm_mmu_page *sp) #endif } -/* - * This value is the sum of all of the kvm instances's - * kvm->arch.n_used_mmu_pages values. We need a global, - * aggregate version in order to make the slab shrinker - * faster - */ -static inline void kvm_mod_used_mmu_pages(struct kvm *kvm, long nr) -{ - kvm->arch.n_used_mmu_pages += nr; - percpu_counter_add(&kvm_total_used_mmu_pages, nr); -} - static void kvm_account_mmu_page(struct kvm *kvm, struct kvm_mmu_page *sp) { - kvm_mod_used_mmu_pages(kvm, +1); + kvm->arch.n_used_mmu_pages++; kvm_account_pgtable_pages((void *)sp->spt, +1); } static void kvm_unaccount_mmu_page(struct kvm *kvm, struct kvm_mmu_page *sp) { - kvm_mod_used_mmu_pages(kvm, -1); + kvm->arch.n_used_mmu_pages--; kvm_account_pgtable_pages((void *)sp->spt, -1); } @@ -2802,7 +2715,7 @@ static void kvm_unsync_page(struct kvm *kvm, struct kvm_mmu_page *sp) * be write-protected. */ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, - gfn_t gfn, bool can_unsync, bool prefetch) + gfn_t gfn, bool synchronizing, bool prefetch) { struct kvm_mmu_page *sp; bool locked = false; @@ -2817,12 +2730,12 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, /* * The page is not write-tracked, mark existing shadow pages unsync - * unless KVM is synchronizing an unsync SP (can_unsync = false). In - * that case, KVM must complete emulation of the guest TLB flush before - * allowing shadow pages to become unsync (writable by the guest). + * unless KVM is synchronizing an unsync SP. In that case, KVM must + * complete emulation of the guest TLB flush before allowing shadow + * pages to become unsync (writable by the guest). */ for_each_gfn_valid_sp_with_gptes(kvm, sp, gfn) { - if (!can_unsync) + if (synchronizing) return -EPERM; if (sp->unsync) @@ -2926,6 +2839,9 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, } if (is_shadow_present_pte(*sptep)) { + if (prefetch) + return RET_PF_SPURIOUS; + /* * If we overwrite a PTE page pointer with a 2MB PMD, unlink * the parent of the now unreachable PTE. @@ -2945,7 +2861,7 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, } wrprot = make_spte(vcpu, sp, slot, pte_access, gfn, pfn, *sptep, prefetch, - true, host_writable, &spte); + false, host_writable, &spte); if (*sptep == spte) { ret = RET_PF_SPURIOUS; @@ -2971,32 +2887,51 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, return ret; } -static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu, - struct kvm_mmu_page *sp, - u64 *start, u64 *end) +static bool kvm_mmu_prefetch_sptes(struct kvm_vcpu *vcpu, gfn_t gfn, u64 *sptep, + int nr_pages, unsigned int access) { struct page *pages[PTE_PREFETCH_NUM]; struct kvm_memory_slot *slot; - unsigned int access = sp->role.access; - int i, ret; - gfn_t gfn; + int i; + + if (WARN_ON_ONCE(nr_pages > PTE_PREFETCH_NUM)) + return false; - gfn = kvm_mmu_page_get_gfn(sp, spte_index(start)); slot = gfn_to_memslot_dirty_bitmap(vcpu, gfn, access & ACC_WRITE_MASK); if (!slot) - return -1; + return false; - ret = gfn_to_page_many_atomic(slot, gfn, pages, end - start); - if (ret <= 0) - return -1; + nr_pages = kvm_prefetch_pages(slot, gfn, pages, nr_pages); + if (nr_pages <= 0) + return false; - for (i = 0; i < ret; i++, gfn++, start++) { - mmu_set_spte(vcpu, slot, start, access, gfn, + for (i = 0; i < nr_pages; i++, gfn++, sptep++) { + mmu_set_spte(vcpu, slot, sptep, access, gfn, page_to_pfn(pages[i]), NULL); - put_page(pages[i]); + + /* + * KVM always prefetches writable pages from the primary MMU, + * and KVM can make its SPTE writable in the fast page handler, + * without notifying the primary MMU. Mark pages/folios dirty + * now to ensure file data is written back if it ends up being + * written by the guest. Because KVM's prefetching GUPs + * writable PTEs, the probability of unnecessary writeback is + * extremely low. + */ + kvm_release_page_dirty(pages[i]); } - return 0; + return true; +} + +static bool direct_pte_prefetch_many(struct kvm_vcpu *vcpu, + struct kvm_mmu_page *sp, + u64 *start, u64 *end) +{ + gfn_t gfn = kvm_mmu_page_get_gfn(sp, spte_index(start)); + unsigned int access = sp->role.access; + + return kvm_mmu_prefetch_sptes(vcpu, gfn, start, end - start, access); } static void __direct_pte_prefetch(struct kvm_vcpu *vcpu, @@ -3014,8 +2949,9 @@ static void __direct_pte_prefetch(struct kvm_vcpu *vcpu, if (is_shadow_present_pte(*spte) || spte == sptep) { if (!start) continue; - if (direct_pte_prefetch_many(vcpu, sp, start, spte) < 0) + if (!direct_pte_prefetch_many(vcpu, sp, start, spte)) return; + start = NULL; } else if (!start) start = spte; @@ -3165,13 +3101,12 @@ static int __kvm_mmu_max_mapping_level(struct kvm *kvm, } int kvm_mmu_max_mapping_level(struct kvm *kvm, - const struct kvm_memory_slot *slot, gfn_t gfn, - int max_level) + const struct kvm_memory_slot *slot, gfn_t gfn) { bool is_private = kvm_slot_can_be_private(slot) && kvm_mem_is_private(kvm, gfn); - return __kvm_mmu_max_mapping_level(kvm, slot, gfn, max_level, is_private); + return __kvm_mmu_max_mapping_level(kvm, slot, gfn, PG_LEVEL_NUM, is_private); } void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) @@ -3322,7 +3257,6 @@ static int kvm_handle_noslot_fault(struct kvm_vcpu *vcpu, fault->slot = NULL; fault->pfn = KVM_PFN_NOSLOT; fault->map_writable = false; - fault->hva = KVM_HVA_ERR_BAD; /* * If MMIO caching is disabled, emulate immediately without @@ -3392,7 +3326,7 @@ static bool page_fault_can_be_fast(struct kvm *kvm, struct kvm_page_fault *fault * by setting the Writable bit, which can be done out of mmu_lock. */ if (!fault->present) - return !kvm_ad_enabled(); + return !kvm_ad_enabled; /* * Note, instruction fetches and writes are mutually exclusive, ignore @@ -3419,7 +3353,7 @@ static bool fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu, * harm. This also avoids the TLB flush needed after setting dirty bit * so non-PML cases won't be impacted. * - * Compare with set_spte where instead shadow_dirty_mask is set. + * Compare with make_spte() where instead shadow_dirty_mask is set. */ if (!try_cmpxchg64(sptep, &old_spte, new_spte)) return false; @@ -3527,8 +3461,9 @@ static int fast_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) * uses A/D bits for non-nested MMUs. Thus, if A/D bits are * enabled, the SPTE can't be an access-tracked SPTE. */ - if (unlikely(!kvm_ad_enabled()) && is_access_track_spte(spte)) - new_spte = restore_acc_track_spte(new_spte); + if (unlikely(!kvm_ad_enabled) && is_access_track_spte(spte)) + new_spte = restore_acc_track_spte(new_spte) | + shadow_accessed_mask; /* * To keep things simple, only SPTEs that are MMU-writable can @@ -4376,8 +4311,15 @@ static u8 kvm_max_private_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, return max_level; } -static int kvm_faultin_pfn_private(struct kvm_vcpu *vcpu, - struct kvm_page_fault *fault) +static void kvm_mmu_finish_page_fault(struct kvm_vcpu *vcpu, + struct kvm_page_fault *fault, int r) +{ + kvm_release_faultin_page(vcpu->kvm, fault->refcounted_page, + r == RET_PF_RETRY, fault->map_writable); +} + +static int kvm_mmu_faultin_pfn_private(struct kvm_vcpu *vcpu, + struct kvm_page_fault *fault) { int max_order, r; @@ -4387,7 +4329,7 @@ static int kvm_faultin_pfn_private(struct kvm_vcpu *vcpu, } r = kvm_gmem_get_pfn(vcpu->kvm, fault->slot, fault->gfn, &fault->pfn, - &max_order); + &fault->refcounted_page, &max_order); if (r) { kvm_mmu_prepare_memory_fault_exit(vcpu, fault); return r; @@ -4400,19 +4342,26 @@ static int kvm_faultin_pfn_private(struct kvm_vcpu *vcpu, return RET_PF_CONTINUE; } -static int __kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) +static int __kvm_mmu_faultin_pfn(struct kvm_vcpu *vcpu, + struct kvm_page_fault *fault) { - bool async; + unsigned int foll = fault->write ? FOLL_WRITE : 0; if (fault->is_private) - return kvm_faultin_pfn_private(vcpu, fault); + return kvm_mmu_faultin_pfn_private(vcpu, fault); - async = false; - fault->pfn = __gfn_to_pfn_memslot(fault->slot, fault->gfn, false, false, - &async, fault->write, - &fault->map_writable, &fault->hva); - if (!async) - return RET_PF_CONTINUE; /* *pfn has correct page already */ + foll |= FOLL_NOWAIT; + fault->pfn = __kvm_faultin_pfn(fault->slot, fault->gfn, foll, + &fault->map_writable, &fault->refcounted_page); + + /* + * If resolving the page failed because I/O is needed to fault-in the + * page, then either set up an asynchronous #PF to do the I/O, or if + * doing an async #PF isn't possible, retry with I/O allowed. All + * other failures are terminal, i.e. retrying won't help. + */ + if (fault->pfn != KVM_PFN_ERR_NEEDS_IO) + return RET_PF_CONTINUE; if (!fault->prefetch && kvm_can_do_async_pf(vcpu)) { trace_kvm_try_async_get_page(fault->addr, fault->gfn); @@ -4430,14 +4379,16 @@ static int __kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault * to wait for IO. Note, gup always bails if it is unable to quickly * get a page and a fatal signal, i.e. SIGKILL, is pending. */ - fault->pfn = __gfn_to_pfn_memslot(fault->slot, fault->gfn, false, true, - NULL, fault->write, - &fault->map_writable, &fault->hva); + foll |= FOLL_INTERRUPTIBLE; + foll &= ~FOLL_NOWAIT; + fault->pfn = __kvm_faultin_pfn(fault->slot, fault->gfn, foll, + &fault->map_writable, &fault->refcounted_page); + return RET_PF_CONTINUE; } -static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, - unsigned int access) +static int kvm_mmu_faultin_pfn(struct kvm_vcpu *vcpu, + struct kvm_page_fault *fault, unsigned int access) { struct kvm_memory_slot *slot = fault->slot; int ret; @@ -4520,7 +4471,7 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, if (mmu_invalidate_retry_gfn_unsafe(vcpu->kvm, fault->mmu_seq, fault->gfn)) return RET_PF_RETRY; - ret = __kvm_faultin_pfn(vcpu, fault); + ret = __kvm_mmu_faultin_pfn(vcpu, fault); if (ret != RET_PF_CONTINUE) return ret; @@ -4538,7 +4489,7 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, * mmu_lock is acquired. */ if (mmu_invalidate_retry_gfn_unsafe(vcpu->kvm, fault->mmu_seq, fault->gfn)) { - kvm_release_pfn_clean(fault->pfn); + kvm_mmu_finish_page_fault(vcpu, fault, RET_PF_RETRY); return RET_PF_RETRY; } @@ -4597,7 +4548,7 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r) return r; - r = kvm_faultin_pfn(vcpu, fault, ACC_ALL); + r = kvm_mmu_faultin_pfn(vcpu, fault, ACC_ALL); if (r != RET_PF_CONTINUE) return r; @@ -4614,8 +4565,8 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault r = direct_map(vcpu, fault); out_unlock: + kvm_mmu_finish_page_fault(vcpu, fault, r); write_unlock(&vcpu->kvm->mmu_lock); - kvm_release_pfn_clean(fault->pfn); return r; } @@ -4688,7 +4639,7 @@ static int kvm_tdp_mmu_page_fault(struct kvm_vcpu *vcpu, if (r) return r; - r = kvm_faultin_pfn(vcpu, fault, ACC_ALL); + r = kvm_mmu_faultin_pfn(vcpu, fault, ACC_ALL); if (r != RET_PF_CONTINUE) return r; @@ -4701,8 +4652,8 @@ static int kvm_tdp_mmu_page_fault(struct kvm_vcpu *vcpu, r = kvm_tdp_mmu_map(vcpu, fault); out_unlock: + kvm_mmu_finish_page_fault(vcpu, fault, r); read_unlock(&vcpu->kvm->mmu_lock); - kvm_release_pfn_clean(fault->pfn); return r; } #endif @@ -5488,7 +5439,7 @@ kvm_calc_tdp_mmu_root_page_role(struct kvm_vcpu *vcpu, role.efer_nx = true; role.smm = cpu_role.base.smm; role.guest_mode = cpu_role.base.guest_mode; - role.ad_disabled = !kvm_ad_enabled(); + role.ad_disabled = !kvm_ad_enabled; role.level = kvm_mmu_get_tdp_level(vcpu); role.direct = true; role.has_4_byte_gpte = false; @@ -6228,7 +6179,7 @@ void kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, /* It's actually a GPA for vcpu->arch.guest_mmu. */ if (mmu != &vcpu->arch.guest_mmu) { /* INVLPG on a non-canonical address is a NOP according to the SDM. */ - if (is_noncanonical_address(addr, vcpu)) + if (is_noncanonical_invlpg_address(addr, vcpu)) return; kvm_x86_call(flush_tlb_gva)(vcpu, addr); @@ -6416,8 +6367,11 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) { struct kvm_mmu_page *sp, *node; int nr_zapped, batch = 0; + LIST_HEAD(invalid_list); bool unstable; + lockdep_assert_held(&kvm->slots_lock); + restart: list_for_each_entry_safe_reverse(sp, node, &kvm->arch.active_mmu_pages, link) { @@ -6449,7 +6403,7 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) } unstable = __kvm_mmu_prepare_zap_page(kvm, sp, - &kvm->arch.zapped_obsolete_pages, &nr_zapped); + &invalid_list, &nr_zapped); batch += nr_zapped; if (unstable) @@ -6465,7 +6419,7 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) * kvm_mmu_load()), and the reload in the caller ensure no vCPUs are * running with an obsolete MMU. */ - kvm_mmu_commit_zap_page(kvm, &kvm->arch.zapped_obsolete_pages); + kvm_mmu_commit_zap_page(kvm, &invalid_list); } /* @@ -6528,16 +6482,10 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm) kvm_tdp_mmu_zap_invalidated_roots(kvm); } -static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm) -{ - return unlikely(!list_empty_careful(&kvm->arch.zapped_obsolete_pages)); -} - void kvm_mmu_init_vm(struct kvm *kvm) { kvm->arch.shadow_mmio_value = shadow_mmio_value; INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); - INIT_LIST_HEAD(&kvm->arch.zapped_obsolete_pages); INIT_LIST_HEAD(&kvm->arch.possible_nx_huge_pages); spin_lock_init(&kvm->arch.mmu_unsync_pages_lock); @@ -6771,7 +6719,7 @@ static void shadow_mmu_split_huge_page(struct kvm *kvm, continue; } - spte = make_huge_page_split_spte(kvm, huge_spte, sp->role, index); + spte = make_small_spte(kvm, huge_spte, sp->role, index); mmu_spte_set(sptep, spte); __rmap_add(kvm, cache, slot, sptep, gfn, sp->role.access); } @@ -6954,8 +6902,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm, * mapping if the indirect sp has level = 1. */ if (sp->role.direct && - sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn, - PG_LEVEL_NUM)) { + sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn)) { kvm_zap_one_rmap_spte(kvm, rmap_head, sptep); if (kvm_available_flush_remote_tlbs_range()) @@ -6983,8 +6930,8 @@ static void kvm_rmap_zap_collapsible_sptes(struct kvm *kvm, kvm_flush_remote_tlbs_memslot(kvm, slot); } -void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, - const struct kvm_memory_slot *slot) +void kvm_mmu_recover_huge_pages(struct kvm *kvm, + const struct kvm_memory_slot *slot) { if (kvm_memslots_have_rmaps(kvm)) { write_lock(&kvm->mmu_lock); @@ -6994,7 +6941,7 @@ void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, if (tdp_mmu_enabled) { read_lock(&kvm->mmu_lock); - kvm_tdp_mmu_zap_collapsible_sptes(kvm, slot); + kvm_tdp_mmu_recover_huge_pages(kvm, slot); read_unlock(&kvm->mmu_lock); } } @@ -7149,72 +7096,6 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) } } -static unsigned long mmu_shrink_scan(struct shrinker *shrink, - struct shrink_control *sc) -{ - struct kvm *kvm; - int nr_to_scan = sc->nr_to_scan; - unsigned long freed = 0; - - mutex_lock(&kvm_lock); - - list_for_each_entry(kvm, &vm_list, vm_list) { - int idx; - - /* - * Never scan more than sc->nr_to_scan VM instances. - * Will not hit this condition practically since we do not try - * to shrink more than one VM and it is very unlikely to see - * !n_used_mmu_pages so many times. - */ - if (!nr_to_scan--) - break; - /* - * n_used_mmu_pages is accessed without holding kvm->mmu_lock - * here. We may skip a VM instance errorneosly, but we do not - * want to shrink a VM that only started to populate its MMU - * anyway. - */ - if (!kvm->arch.n_used_mmu_pages && - !kvm_has_zapped_obsolete_pages(kvm)) - continue; - - idx = srcu_read_lock(&kvm->srcu); - write_lock(&kvm->mmu_lock); - - if (kvm_has_zapped_obsolete_pages(kvm)) { - kvm_mmu_commit_zap_page(kvm, - &kvm->arch.zapped_obsolete_pages); - goto unlock; - } - - freed = kvm_mmu_zap_oldest_mmu_pages(kvm, sc->nr_to_scan); - -unlock: - write_unlock(&kvm->mmu_lock); - srcu_read_unlock(&kvm->srcu, idx); - - /* - * unfair on small ones - * per-vm shrinkers cry out - * sadness comes quickly - */ - list_move_tail(&kvm->vm_list, &vm_list); - break; - } - - mutex_unlock(&kvm_lock); - return freed; -} - -static unsigned long mmu_shrink_count(struct shrinker *shrink, - struct shrink_control *sc) -{ - return percpu_counter_read_positive(&kvm_total_used_mmu_pages); -} - -static struct shrinker *mmu_shrinker; - static void mmu_destroy_caches(void) { kmem_cache_destroy(pte_list_desc_cache); @@ -7281,7 +7162,7 @@ static int set_nx_huge_pages(const char *val, const struct kernel_param *kp) kvm_mmu_zap_all_fast(kvm); mutex_unlock(&kvm->slots_lock); - wake_up_process(kvm->arch.nx_huge_page_recovery_thread); + vhost_task_wake(kvm->arch.nx_huge_page_recovery_thread); } mutex_unlock(&kvm_lock); } @@ -7341,23 +7222,8 @@ int kvm_mmu_vendor_module_init(void) if (!mmu_page_header_cache) goto out; - if (percpu_counter_init(&kvm_total_used_mmu_pages, 0, GFP_KERNEL)) - goto out; - - mmu_shrinker = shrinker_alloc(0, "x86-mmu"); - if (!mmu_shrinker) - goto out_shrinker; - - mmu_shrinker->count_objects = mmu_shrink_count; - mmu_shrinker->scan_objects = mmu_shrink_scan; - mmu_shrinker->seeks = DEFAULT_SEEKS * 10; - - shrinker_register(mmu_shrinker); - return 0; -out_shrinker: - percpu_counter_destroy(&kvm_total_used_mmu_pages); out: mmu_destroy_caches(); return ret; @@ -7374,8 +7240,6 @@ void kvm_mmu_destroy(struct kvm_vcpu *vcpu) void kvm_mmu_vendor_module_exit(void) { mmu_destroy_caches(); - percpu_counter_destroy(&kvm_total_used_mmu_pages); - shrinker_free(mmu_shrinker); } /* @@ -7427,7 +7291,7 @@ static int set_nx_huge_pages_recovery_param(const char *val, const struct kernel mutex_lock(&kvm_lock); list_for_each_entry(kvm, &vm_list, vm_list) - wake_up_process(kvm->arch.nx_huge_page_recovery_thread); + vhost_task_wake(kvm->arch.nx_huge_page_recovery_thread); mutex_unlock(&kvm_lock); } @@ -7530,62 +7394,56 @@ static void kvm_recover_nx_huge_pages(struct kvm *kvm) srcu_read_unlock(&kvm->srcu, rcu_idx); } -static long get_nx_huge_page_recovery_timeout(u64 start_time) +static void kvm_nx_huge_page_recovery_worker_kill(void *data) { - bool enabled; - uint period; - - enabled = calc_nx_huge_pages_recovery_period(&period); - - return enabled ? start_time + msecs_to_jiffies(period) - get_jiffies_64() - : MAX_SCHEDULE_TIMEOUT; } -static int kvm_nx_huge_page_recovery_worker(struct kvm *kvm, uintptr_t data) +static bool kvm_nx_huge_page_recovery_worker(void *data) { - u64 start_time; + struct kvm *kvm = data; + bool enabled; + uint period; long remaining_time; - while (true) { - start_time = get_jiffies_64(); - remaining_time = get_nx_huge_page_recovery_timeout(start_time); - - set_current_state(TASK_INTERRUPTIBLE); - while (!kthread_should_stop() && remaining_time > 0) { - schedule_timeout(remaining_time); - remaining_time = get_nx_huge_page_recovery_timeout(start_time); - set_current_state(TASK_INTERRUPTIBLE); - } - - set_current_state(TASK_RUNNING); - - if (kthread_should_stop()) - return 0; + enabled = calc_nx_huge_pages_recovery_period(&period); + if (!enabled) + return false; - kvm_recover_nx_huge_pages(kvm); + remaining_time = kvm->arch.nx_huge_page_last + msecs_to_jiffies(period) + - get_jiffies_64(); + if (remaining_time > 0) { + schedule_timeout(remaining_time); + /* check for signals and come back */ + return true; } + + __set_current_state(TASK_RUNNING); + kvm_recover_nx_huge_pages(kvm); + kvm->arch.nx_huge_page_last = get_jiffies_64(); + return true; } int kvm_mmu_post_init_vm(struct kvm *kvm) { - int err; - if (nx_hugepage_mitigation_hard_disabled) return 0; - err = kvm_vm_create_worker_thread(kvm, kvm_nx_huge_page_recovery_worker, 0, - "kvm-nx-lpage-recovery", - &kvm->arch.nx_huge_page_recovery_thread); - if (!err) - kthread_unpark(kvm->arch.nx_huge_page_recovery_thread); + kvm->arch.nx_huge_page_last = get_jiffies_64(); + kvm->arch.nx_huge_page_recovery_thread = vhost_task_create( + kvm_nx_huge_page_recovery_worker, kvm_nx_huge_page_recovery_worker_kill, + kvm, "kvm-nx-lpage-recovery"); - return err; + if (!kvm->arch.nx_huge_page_recovery_thread) + return -ENOMEM; + + vhost_task_start(kvm->arch.nx_huge_page_recovery_thread); + return 0; } void kvm_mmu_pre_destroy_vm(struct kvm *kvm) { if (kvm->arch.nx_huge_page_recovery_thread) - kthread_stop(kvm->arch.nx_huge_page_recovery_thread); + vhost_task_stop(kvm->arch.nx_huge_page_recovery_thread); } #ifdef CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index c98827840e07a..b00abbe3f6cfa 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -164,7 +164,7 @@ static inline gfn_t gfn_round_for_level(gfn_t gfn, int level) } int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, - gfn_t gfn, bool can_unsync, bool prefetch); + gfn_t gfn, bool synchronizing, bool prefetch); void kvm_mmu_gfn_disallow_lpage(const struct kvm_memory_slot *slot, gfn_t gfn); void kvm_mmu_gfn_allow_lpage(const struct kvm_memory_slot *slot, gfn_t gfn); @@ -235,10 +235,10 @@ struct kvm_page_fault { /* The memslot containing gfn. May be NULL. */ struct kvm_memory_slot *slot; - /* Outputs of kvm_faultin_pfn. */ + /* Outputs of kvm_mmu_faultin_pfn(). */ unsigned long mmu_seq; kvm_pfn_t pfn; - hva_t hva; + struct page *refcounted_page; bool map_writable; /* @@ -313,7 +313,6 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, .is_private = err & PFERR_PRIVATE_ACCESS, .pfn = KVM_PFN_ERR_FAULT, - .hva = KVM_HVA_ERR_BAD, }; int r; @@ -347,8 +346,7 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, } int kvm_mmu_max_mapping_level(struct kvm *kvm, - const struct kvm_memory_slot *slot, gfn_t gfn, - int max_level); + const struct kvm_memory_slot *slot, gfn_t gfn); void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault); void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_level); diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index ae7d39ff2d07f..f4711674c47bd 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -533,10 +533,8 @@ static bool FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, u64 *spte, pt_element_t gpte) { - struct kvm_memory_slot *slot; unsigned pte_access; gfn_t gfn; - kvm_pfn_t pfn; if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte)) return false; @@ -545,17 +543,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, pte_access = sp->role.access & FNAME(gpte_access)(gpte); FNAME(protect_clean_gpte)(vcpu->arch.mmu, &pte_access, gpte); - slot = gfn_to_memslot_dirty_bitmap(vcpu, gfn, pte_access & ACC_WRITE_MASK); - if (!slot) - return false; - - pfn = gfn_to_pfn_memslot_atomic(slot, gfn); - if (is_error_pfn(pfn)) - return false; - - mmu_set_spte(vcpu, slot, spte, pte_access, gfn, pfn, NULL); - kvm_release_pfn_clean(pfn); - return true; + return kvm_mmu_prefetch_sptes(vcpu, gfn, spte, 1, pte_access); } static bool FNAME(gpte_changed)(struct kvm_vcpu *vcpu, @@ -813,7 +801,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r) return r; - r = kvm_faultin_pfn(vcpu, fault, walker.pte_access); + r = kvm_mmu_faultin_pfn(vcpu, fault, walker.pte_access); if (r != RET_PF_CONTINUE) return r; @@ -848,8 +836,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault r = FNAME(fetch)(vcpu, fault, &walker); out_unlock: + kvm_mmu_finish_page_fault(vcpu, fault, r); write_unlock(&vcpu->kvm->mmu_lock); - kvm_release_pfn_clean(fault->pfn); return r; } @@ -892,9 +880,9 @@ static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, /* * Using the information in sp->shadowed_translation (kvm_mmu_page_get_gfn()) is - * safe because: - * - The spte has a reference to the struct page, so the pfn for a given gfn - * can't change unless all sptes pointing to it are nuked first. + * safe because SPTEs are protected by mmu_notifiers and memslot generations, so + * the pfn for a given gfn can't change unless all SPTEs pointing to the gfn are + * nuked first. * * Returns * < 0: failed to sync spte @@ -963,9 +951,14 @@ static int FNAME(sync_spte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, int host_writable = spte & shadow_host_writable_mask; slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); make_spte(vcpu, sp, slot, pte_access, gfn, - spte_to_pfn(spte), spte, true, false, + spte_to_pfn(spte), spte, true, true, host_writable, &spte); + /* + * There is no need to mark the pfn dirty, as the new protections must + * be a subset of the old protections, i.e. synchronizing a SPTE cannot + * change the SPTE from read-only to writable. + */ return mmu_spte_update(sptep, spte); } diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index 8f7eb3ad88fcb..22551e2f1d009 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -24,6 +24,8 @@ static bool __ro_after_init allow_mmio_caching; module_param_named(mmio_caching, enable_mmio_caching, bool, 0444); EXPORT_SYMBOL_GPL(enable_mmio_caching); +bool __read_mostly kvm_ad_enabled; + u64 __read_mostly shadow_host_writable_mask; u64 __read_mostly shadow_mmu_writable_mask; u64 __read_mostly shadow_nx_mask; @@ -133,12 +135,6 @@ static bool kvm_is_mmio_pfn(kvm_pfn_t pfn) */ bool spte_has_volatile_bits(u64 spte) { - /* - * Always atomically update spte if it can be updated - * out of mmu-lock, it can ensure dirty bit is not lost, - * also, it can help us to get a stable is_writable_pte() - * to ensure tlb flush is not missed. - */ if (!is_writable_pte(spte) && is_mmu_writable_spte(spte)) return true; @@ -157,7 +153,7 @@ bool spte_has_volatile_bits(u64 spte) bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, const struct kvm_memory_slot *slot, unsigned int pte_access, gfn_t gfn, kvm_pfn_t pfn, - u64 old_spte, bool prefetch, bool can_unsync, + u64 old_spte, bool prefetch, bool synchronizing, bool host_writable, u64 *new_spte) { int level = sp->role.level; @@ -178,8 +174,8 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, spte |= SPTE_TDP_AD_WRPROT_ONLY; spte |= shadow_present_mask; - if (!prefetch) - spte |= spte_shadow_accessed_mask(spte); + if (!prefetch || synchronizing) + spte |= shadow_accessed_mask; /* * For simplicity, enforce the NX huge page mitigation even if not @@ -223,41 +219,39 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, spte |= (u64)pfn << PAGE_SHIFT; if (pte_access & ACC_WRITE_MASK) { - spte |= PT_WRITABLE_MASK | shadow_mmu_writable_mask; - - /* - * Optimization: for pte sync, if spte was writable the hash - * lookup is unnecessary (and expensive). Write protection - * is responsibility of kvm_mmu_get_page / kvm_mmu_sync_roots. - * Same reasoning can be applied to dirty page accounting. - */ - if (is_writable_pte(old_spte)) - goto out; - /* * Unsync shadow pages that are reachable by the new, writable * SPTE. Write-protect the SPTE if the page can't be unsync'd, * e.g. it's write-tracked (upper-level SPs) or has one or more * shadow pages and unsync'ing pages is not allowed. + * + * When overwriting an existing leaf SPTE, and the old SPTE was + * writable, skip trying to unsync shadow pages as any relevant + * shadow pages must already be unsync, i.e. the hash lookup is + * unnecessary (and expensive). Note, this relies on KVM not + * changing PFNs without first zapping the old SPTE, which is + * guaranteed by both the shadow MMU and the TDP MMU. */ - if (mmu_try_to_unsync_pages(vcpu->kvm, slot, gfn, can_unsync, prefetch)) { + if ((!is_last_spte(old_spte, level) || !is_writable_pte(old_spte)) && + mmu_try_to_unsync_pages(vcpu->kvm, slot, gfn, synchronizing, prefetch)) wrprot = true; - pte_access &= ~ACC_WRITE_MASK; - spte &= ~(PT_WRITABLE_MASK | shadow_mmu_writable_mask); - } + else + spte |= PT_WRITABLE_MASK | shadow_mmu_writable_mask | + shadow_dirty_mask; } - if (pte_access & ACC_WRITE_MASK) - spte |= spte_shadow_dirty_mask(spte); - -out: - if (prefetch) + if (prefetch && !synchronizing) spte = mark_spte_for_access_track(spte); WARN_ONCE(is_rsvd_spte(&vcpu->arch.mmu->shadow_zero_check, spte, level), "spte = 0x%llx, level = %d, rsvd bits = 0x%llx", spte, level, get_rsvd_bits(&vcpu->arch.mmu->shadow_zero_check, spte, level)); + /* + * Mark the memslot dirty *after* modifying it for access tracking. + * Unlike folios, memslots can be safely marked dirty out of mmu_lock, + * i.e. in the fast page fault handler. + */ if ((spte & PT_WRITABLE_MASK) && kvm_slot_dirty_track_enabled(slot)) { /* Enforced by kvm_mmu_hugepage_adjust. */ WARN_ON_ONCE(level > PG_LEVEL_4K); @@ -268,15 +262,15 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, return wrprot; } -static u64 make_spte_executable(u64 spte) +static u64 modify_spte_protections(u64 spte, u64 set, u64 clear) { bool is_access_track = is_access_track_spte(spte); if (is_access_track) spte = restore_acc_track_spte(spte); - spte &= ~shadow_nx_mask; - spte |= shadow_x_mask; + KVM_MMU_WARN_ON(set & clear); + spte = (spte | set) & ~clear; if (is_access_track) spte = mark_spte_for_access_track(spte); @@ -284,6 +278,16 @@ static u64 make_spte_executable(u64 spte) return spte; } +static u64 make_spte_executable(u64 spte) +{ + return modify_spte_protections(spte, shadow_x_mask, shadow_nx_mask); +} + +static u64 make_spte_nonexecutable(u64 spte) +{ + return modify_spte_protections(spte, shadow_nx_mask, shadow_x_mask); +} + /* * Construct an SPTE that maps a sub-page of the given huge page SPTE where * `index` identifies which sub-page. @@ -291,8 +295,8 @@ static u64 make_spte_executable(u64 spte) * This is used during huge page splitting to build the SPTEs that make up the * new page table. */ -u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, - union kvm_mmu_page_role role, int index) +u64 make_small_spte(struct kvm *kvm, u64 huge_spte, + union kvm_mmu_page_role role, int index) { u64 child_spte = huge_spte; @@ -320,6 +324,26 @@ u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, return child_spte; } +u64 make_huge_spte(struct kvm *kvm, u64 small_spte, int level) +{ + u64 huge_spte; + + KVM_BUG_ON(!is_shadow_present_pte(small_spte) || level == PG_LEVEL_4K, kvm); + + huge_spte = small_spte | PT_PAGE_SIZE_MASK; + + /* + * huge_spte already has the address of the sub-page being collapsed + * from small_spte, so just clear the lower address bits to create the + * huge page address. + */ + huge_spte &= KVM_HPAGE_MASK(level) | ~PAGE_MASK; + + if (is_nx_huge_page_enabled(kvm)) + huge_spte = make_spte_nonexecutable(huge_spte); + + return huge_spte; +} u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled) { @@ -352,7 +376,7 @@ u64 mark_spte_for_access_track(u64 spte) spte |= (spte & SHADOW_ACC_TRACK_SAVED_BITS_MASK) << SHADOW_ACC_TRACK_SAVED_BITS_SHIFT; - spte &= ~shadow_acc_track_mask; + spte &= ~(shadow_acc_track_mask | shadow_accessed_mask); return spte; } @@ -422,9 +446,11 @@ EXPORT_SYMBOL_GPL(kvm_mmu_set_me_spte_mask); void kvm_mmu_set_ept_masks(bool has_ad_bits, bool has_exec_only) { + kvm_ad_enabled = has_ad_bits; + shadow_user_mask = VMX_EPT_READABLE_MASK; - shadow_accessed_mask = has_ad_bits ? VMX_EPT_ACCESS_BIT : 0ull; - shadow_dirty_mask = has_ad_bits ? VMX_EPT_DIRTY_BIT : 0ull; + shadow_accessed_mask = VMX_EPT_ACCESS_BIT; + shadow_dirty_mask = VMX_EPT_DIRTY_BIT; shadow_nx_mask = 0ull; shadow_x_mask = VMX_EPT_EXECUTABLE_MASK; /* VMX_EPT_SUPPRESS_VE_BIT is needed for W or X violation. */ @@ -455,6 +481,8 @@ void kvm_mmu_reset_all_pte_masks(void) u8 low_phys_bits; u64 mask; + kvm_ad_enabled = true; + /* * If the CPU has 46 or less physical address bits, then set an * appropriate mask to guard against L1TF attacks. Otherwise, it is diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 2cb816ea24307..f332b33bc8178 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -167,6 +167,15 @@ static_assert(!(SHADOW_NONPRESENT_VALUE & SPTE_MMU_PRESENT_MASK)); #define SHADOW_NONPRESENT_VALUE 0ULL #endif + +/* + * True if A/D bits are supported in hardware and are enabled by KVM. When + * enabled, KVM uses A/D bits for all non-nested MMUs. Because L1 can disable + * A/D bits in EPTP12, SP and SPTE variants are needed to handle the scenario + * where KVM is using A/D bits for L1, but not L2. + */ +extern bool __read_mostly kvm_ad_enabled; + extern u64 __read_mostly shadow_host_writable_mask; extern u64 __read_mostly shadow_mmu_writable_mask; extern u64 __read_mostly shadow_nx_mask; @@ -285,17 +294,6 @@ static inline bool is_ept_ve_possible(u64 spte) (spte & VMX_EPT_RWX_MASK) != VMX_EPT_MISCONFIG_WX_VALUE; } -/* - * Returns true if A/D bits are supported in hardware and are enabled by KVM. - * When enabled, KVM uses A/D bits for all non-nested MMUs. Because L1 can - * disable A/D bits in EPTP12, SP and SPTE variants are needed to handle the - * scenario where KVM is using A/D bits for L1, but not L2. - */ -static inline bool kvm_ad_enabled(void) -{ - return !!shadow_accessed_mask; -} - static inline bool sp_ad_disabled(struct kvm_mmu_page *sp) { return sp->role.ad_disabled; @@ -318,18 +316,6 @@ static inline bool spte_ad_need_write_protect(u64 spte) return (spte & SPTE_TDP_AD_MASK) != SPTE_TDP_AD_ENABLED; } -static inline u64 spte_shadow_accessed_mask(u64 spte) -{ - KVM_MMU_WARN_ON(!is_shadow_present_pte(spte)); - return spte_ad_enabled(spte) ? shadow_accessed_mask : 0; -} - -static inline u64 spte_shadow_dirty_mask(u64 spte) -{ - KVM_MMU_WARN_ON(!is_shadow_present_pte(spte)); - return spte_ad_enabled(spte) ? shadow_dirty_mask : 0; -} - static inline bool is_access_track_spte(u64 spte) { return !spte_ad_enabled(spte) && (spte & shadow_acc_track_mask) == 0; @@ -357,17 +343,7 @@ static inline kvm_pfn_t spte_to_pfn(u64 pte) static inline bool is_accessed_spte(u64 spte) { - u64 accessed_mask = spte_shadow_accessed_mask(spte); - - return accessed_mask ? spte & accessed_mask - : !is_access_track_spte(spte); -} - -static inline bool is_dirty_spte(u64 spte) -{ - u64 dirty_mask = spte_shadow_dirty_mask(spte); - - return dirty_mask ? spte & dirty_mask : spte & PT_WRITABLE_MASK; + return spte & shadow_accessed_mask; } static inline u64 get_rsvd_bits(struct rsvd_bits_validate *rsvd_check, u64 pte, @@ -485,6 +461,33 @@ static inline bool is_mmu_writable_spte(u64 spte) return spte & shadow_mmu_writable_mask; } +/* + * If the MMU-writable flag is cleared, i.e. the SPTE is write-protected for + * write-tracking, remote TLBs must be flushed, even if the SPTE was read-only, + * as KVM allows stale Writable TLB entries to exist. When dirty logging, KVM + * flushes TLBs based on whether or not dirty bitmap/ring entries were reaped, + * not whether or not SPTEs were modified, i.e. only the write-tracking case + * needs to flush at the time the SPTEs is modified, before dropping mmu_lock. + * + * Don't flush if the Accessed bit is cleared, as access tracking tolerates + * false negatives, e.g. KVM x86 omits TLB flushes even when aging SPTEs for a + * mmu_notifier.clear_flush_young() event. + * + * Lastly, don't flush if the Dirty bit is cleared, as KVM unconditionally + * flushes when enabling dirty logging (see kvm_mmu_slot_apply_flags()), and + * when clearing dirty logs, KVM flushes based on whether or not dirty entries + * were reaped from the bitmap/ring, not whether or not dirty SPTEs were found. + * + * Note, this logic only applies to shadow-present leaf SPTEs. The caller is + * responsible for checking that the old SPTE is shadow-present, and is also + * responsible for determining whether or not a TLB flush is required when + * modifying a shadow-present non-leaf SPTE. + */ +static inline bool leaf_spte_change_needs_tlb_flush(u64 old_spte, u64 new_spte) +{ + return is_mmu_writable_spte(old_spte) && !is_mmu_writable_spte(new_spte); +} + static inline u64 get_mmio_spte_generation(u64 spte) { u64 gen; @@ -499,10 +502,11 @@ bool spte_has_volatile_bits(u64 spte); bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, const struct kvm_memory_slot *slot, unsigned int pte_access, gfn_t gfn, kvm_pfn_t pfn, - u64 old_spte, bool prefetch, bool can_unsync, + u64 old_spte, bool prefetch, bool synchronizing, bool host_writable, u64 *new_spte); -u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, - union kvm_mmu_page_role role, int index); +u64 make_small_spte(struct kvm *kvm, u64 huge_spte, + union kvm_mmu_page_role role, int index); +u64 make_huge_spte(struct kvm *kvm, u64 small_spte, int level); u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled); u64 make_mmio_spte(struct kvm_vcpu *vcpu, u64 gfn, unsigned int access); u64 mark_spte_for_access_track(u64 spte); diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 3b996c1fdaabc..4508d868f1cdc 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -511,10 +511,6 @@ static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, if (is_leaf != was_leaf) kvm_update_page_stats(kvm, level, is_leaf ? 1 : -1); - if (was_leaf && is_dirty_spte(old_spte) && - (!is_present || !is_dirty_spte(new_spte) || pfn_changed)) - kvm_set_pfn_dirty(spte_to_pfn(old_spte)); - /* * Recursively handle child PTs if the change removed a subtree from * the paging structure. Note the WARN on the PFN changing without the @@ -524,10 +520,6 @@ static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, if (was_present && !was_leaf && (is_leaf || !is_present || WARN_ON_ONCE(pfn_changed))) handle_removed_pt(kvm, spte_to_child_pt(old_spte, level), shared); - - if (was_leaf && is_accessed_spte(old_spte) && - (!is_present || !is_accessed_spte(new_spte) || pfn_changed)) - kvm_set_pfn_accessed(spte_to_pfn(old_spte)); } static inline int __must_check __tdp_mmu_set_spte_atomic(struct tdp_iter *iter, @@ -591,48 +583,6 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm, return 0; } -static inline int __must_check tdp_mmu_zap_spte_atomic(struct kvm *kvm, - struct tdp_iter *iter) -{ - int ret; - - lockdep_assert_held_read(&kvm->mmu_lock); - - /* - * Freeze the SPTE by setting it to a special, non-present value. This - * will stop other threads from immediately installing a present entry - * in its place before the TLBs are flushed. - * - * Delay processing of the zapped SPTE until after TLBs are flushed and - * the FROZEN_SPTE is replaced (see below). - */ - ret = __tdp_mmu_set_spte_atomic(iter, FROZEN_SPTE); - if (ret) - return ret; - - kvm_flush_remote_tlbs_gfn(kvm, iter->gfn, iter->level); - - /* - * No other thread can overwrite the frozen SPTE as they must either - * wait on the MMU lock or use tdp_mmu_set_spte_atomic() which will not - * overwrite the special frozen SPTE value. Use the raw write helper to - * avoid an unnecessary check on volatile bits. - */ - __kvm_tdp_mmu_write_spte(iter->sptep, SHADOW_NONPRESENT_VALUE); - - /* - * Process the zapped SPTE after flushing TLBs, and after replacing - * FROZEN_SPTE with 0. This minimizes the amount of time vCPUs are - * blocked by the FROZEN_SPTE and reduces contention on the child - * SPTEs. - */ - handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte, - SHADOW_NONPRESENT_VALUE, iter->level, true); - - return 0; -} - - /* * tdp_mmu_set_spte - Set a TDP MMU SPTE and handle the associated bookkeeping * @kvm: KVM instance @@ -688,6 +638,16 @@ static inline void tdp_mmu_iter_set_spte(struct kvm *kvm, struct tdp_iter *iter, #define tdp_mmu_for_each_pte(_iter, _mmu, _start, _end) \ for_each_tdp_pte(_iter, root_to_sp(_mmu->root.hpa), _start, _end) +static inline bool __must_check tdp_mmu_iter_need_resched(struct kvm *kvm, + struct tdp_iter *iter) +{ + if (!need_resched() && !rwlock_needbreak(&kvm->mmu_lock)) + return false; + + /* Ensure forward progress has been made before yielding. */ + return iter->next_last_level_gfn != iter->yielded_gfn; +} + /* * Yield if the MMU lock is contended or this thread needs to return control * to the scheduler. @@ -706,31 +666,27 @@ static inline bool __must_check tdp_mmu_iter_cond_resched(struct kvm *kvm, struct tdp_iter *iter, bool flush, bool shared) { - WARN_ON_ONCE(iter->yielded); + KVM_MMU_WARN_ON(iter->yielded); - /* Ensure forward progress has been made before yielding. */ - if (iter->next_last_level_gfn == iter->yielded_gfn) + if (!tdp_mmu_iter_need_resched(kvm, iter)) return false; - if (need_resched() || rwlock_needbreak(&kvm->mmu_lock)) { - if (flush) - kvm_flush_remote_tlbs(kvm); - - rcu_read_unlock(); + if (flush) + kvm_flush_remote_tlbs(kvm); - if (shared) - cond_resched_rwlock_read(&kvm->mmu_lock); - else - cond_resched_rwlock_write(&kvm->mmu_lock); + rcu_read_unlock(); - rcu_read_lock(); + if (shared) + cond_resched_rwlock_read(&kvm->mmu_lock); + else + cond_resched_rwlock_write(&kvm->mmu_lock); - WARN_ON_ONCE(iter->gfn > iter->next_last_level_gfn); + rcu_read_lock(); - iter->yielded = true; - } + WARN_ON_ONCE(iter->gfn > iter->next_last_level_gfn); - return iter->yielded; + iter->yielded = true; + return true; } static inline gfn_t tdp_mmu_max_gfn_exclusive(void) @@ -1026,19 +982,23 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu, if (WARN_ON_ONCE(sp->role.level != fault->goal_level)) return RET_PF_RETRY; + if (fault->prefetch && is_shadow_present_pte(iter->old_spte)) + return RET_PF_SPURIOUS; + if (unlikely(!fault->slot)) new_spte = make_mmio_spte(vcpu, iter->gfn, ACC_ALL); else wrprot = make_spte(vcpu, sp, fault->slot, ACC_ALL, iter->gfn, - fault->pfn, iter->old_spte, fault->prefetch, true, - fault->map_writable, &new_spte); + fault->pfn, iter->old_spte, fault->prefetch, + false, fault->map_writable, &new_spte); if (new_spte == iter->old_spte) ret = RET_PF_SPURIOUS; else if (tdp_mmu_set_spte_atomic(vcpu->kvm, iter, new_spte)) return RET_PF_RETRY; else if (is_shadow_present_pte(iter->old_spte) && - !is_last_spte(iter->old_spte, iter->level)) + (!is_last_spte(iter->old_spte, iter->level) || + WARN_ON_ONCE(leaf_spte_change_needs_tlb_flush(iter->old_spte, new_spte)))) kvm_flush_remote_tlbs_gfn(vcpu->kvm, iter->gfn, iter->level); /* @@ -1078,7 +1038,7 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu, static int tdp_mmu_link_sp(struct kvm *kvm, struct tdp_iter *iter, struct kvm_mmu_page *sp, bool shared) { - u64 spte = make_nonleaf_spte(sp->spt, !kvm_ad_enabled()); + u64 spte = make_nonleaf_spte(sp->spt, !kvm_ad_enabled); int ret = 0; if (shared) { @@ -1195,33 +1155,6 @@ bool kvm_tdp_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range, return flush; } -typedef bool (*tdp_handler_t)(struct kvm *kvm, struct tdp_iter *iter, - struct kvm_gfn_range *range); - -static __always_inline bool kvm_tdp_mmu_handle_gfn(struct kvm *kvm, - struct kvm_gfn_range *range, - tdp_handler_t handler) -{ - struct kvm_mmu_page *root; - struct tdp_iter iter; - bool ret = false; - - /* - * Don't support rescheduling, none of the MMU notifiers that funnel - * into this helper allow blocking; it'd be dead, wasteful code. - */ - for_each_tdp_mmu_root(kvm, root, range->slot->as_id) { - rcu_read_lock(); - - tdp_root_for_each_leaf_pte(iter, root, range->start, range->end) - ret |= handler(kvm, &iter, range); - - rcu_read_unlock(); - } - - return ret; -} - /* * Mark the SPTEs range of GFNs [start, end) unaccessed and return non-zero * if any of the GFNs in the range have been accessed. @@ -1230,15 +1163,10 @@ static __always_inline bool kvm_tdp_mmu_handle_gfn(struct kvm *kvm, * from the clear_young() or clear_flush_young() notifier, which uses the * return value to determine if the page has been accessed. */ -static bool age_gfn_range(struct kvm *kvm, struct tdp_iter *iter, - struct kvm_gfn_range *range) +static void kvm_tdp_mmu_age_spte(struct tdp_iter *iter) { u64 new_spte; - /* If we have a non-accessed entry we don't need to change the pte. */ - if (!is_accessed_spte(iter->old_spte)) - return false; - if (spte_ad_enabled(iter->old_spte)) { iter->old_spte = tdp_mmu_clear_spte_bits(iter->sptep, iter->old_spte, @@ -1246,13 +1174,6 @@ static bool age_gfn_range(struct kvm *kvm, struct tdp_iter *iter, iter->level); new_spte = iter->old_spte & ~shadow_accessed_mask; } else { - /* - * Capture the dirty status of the page, so that it doesn't get - * lost when the SPTE is marked for access tracking. - */ - if (is_writable_pte(iter->old_spte)) - kvm_set_pfn_dirty(spte_to_pfn(iter->old_spte)); - new_spte = mark_spte_for_access_track(iter->old_spte); iter->old_spte = kvm_tdp_mmu_write_spte(iter->sptep, iter->old_spte, new_spte, @@ -1261,23 +1182,48 @@ static bool age_gfn_range(struct kvm *kvm, struct tdp_iter *iter, trace_kvm_tdp_mmu_spte_changed(iter->as_id, iter->gfn, iter->level, iter->old_spte, new_spte); - return true; } -bool kvm_tdp_mmu_age_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) +static bool __kvm_tdp_mmu_age_gfn_range(struct kvm *kvm, + struct kvm_gfn_range *range, + bool test_only) { - return kvm_tdp_mmu_handle_gfn(kvm, range, age_gfn_range); + struct kvm_mmu_page *root; + struct tdp_iter iter; + bool ret = false; + + /* + * Don't support rescheduling, none of the MMU notifiers that funnel + * into this helper allow blocking; it'd be dead, wasteful code. Note, + * this helper must NOT be used to unmap GFNs, as it processes only + * valid roots! + */ + for_each_valid_tdp_mmu_root(kvm, root, range->slot->as_id) { + guard(rcu)(); + + tdp_root_for_each_leaf_pte(iter, root, range->start, range->end) { + if (!is_accessed_spte(iter.old_spte)) + continue; + + if (test_only) + return true; + + ret = true; + kvm_tdp_mmu_age_spte(&iter); + } + } + + return ret; } -static bool test_age_gfn(struct kvm *kvm, struct tdp_iter *iter, - struct kvm_gfn_range *range) +bool kvm_tdp_mmu_age_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) { - return is_accessed_spte(iter->old_spte); + return __kvm_tdp_mmu_age_gfn_range(kvm, range, false); } bool kvm_tdp_mmu_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { - return kvm_tdp_mmu_handle_gfn(kvm, range, test_age_gfn); + return __kvm_tdp_mmu_age_gfn_range(kvm, range, true); } /* @@ -1368,7 +1314,7 @@ static int tdp_mmu_split_huge_page(struct kvm *kvm, struct tdp_iter *iter, * not been linked in yet and thus is not reachable from any other CPU. */ for (i = 0; i < SPTE_ENT_PER_PAGE; i++) - sp->spt[i] = make_huge_page_split_spte(kvm, huge_spte, sp->role, i); + sp->spt[i] = make_small_spte(kvm, huge_spte, sp->role, i); /* * Replace the huge spte with a pointer to the populated lower level @@ -1501,16 +1447,15 @@ static bool tdp_mmu_need_write_protect(struct kvm_mmu_page *sp) * from level, so it is valid to key off any shadow page to determine if * write protection is needed for an entire tree. */ - return kvm_mmu_page_ad_need_write_protect(sp) || !kvm_ad_enabled(); + return kvm_mmu_page_ad_need_write_protect(sp) || !kvm_ad_enabled; } -static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root, - gfn_t start, gfn_t end) +static void clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root, + gfn_t start, gfn_t end) { const u64 dbit = tdp_mmu_need_write_protect(root) ? PT_WRITABLE_MASK : shadow_dirty_mask; struct tdp_iter iter; - bool spte_set = false; rcu_read_lock(); @@ -1531,31 +1476,24 @@ static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root, if (tdp_mmu_set_spte_atomic(kvm, &iter, iter.old_spte & ~dbit)) goto retry; - - spte_set = true; } rcu_read_unlock(); - return spte_set; } /* * Clear the dirty status (D-bit or W-bit) of all the SPTEs mapping GFNs in the - * memslot. Returns true if an SPTE has been changed and the TLBs need to be - * flushed. + * memslot. */ -bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, +void kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, const struct kvm_memory_slot *slot) { struct kvm_mmu_page *root; - bool spte_set = false; lockdep_assert_held_read(&kvm->mmu_lock); for_each_valid_tdp_mmu_root_yield_safe(kvm, root, slot->as_id) - spte_set |= clear_dirty_gfn_range(kvm, root, slot->base_gfn, - slot->base_gfn + slot->npages); - - return spte_set; + clear_dirty_gfn_range(kvm, root, slot->base_gfn, + slot->base_gfn + slot->npages); } static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root, @@ -1593,7 +1531,6 @@ static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root, trace_kvm_tdp_mmu_spte_changed(iter.as_id, iter.gfn, iter.level, iter.old_spte, iter.old_spte & ~dbit); - kvm_set_pfn_dirty(spte_to_pfn(iter.old_spte)); } rcu_read_unlock(); @@ -1615,21 +1552,55 @@ void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm, clear_dirty_pt_masked(kvm, root, gfn, mask, wrprot); } -static void zap_collapsible_spte_range(struct kvm *kvm, - struct kvm_mmu_page *root, - const struct kvm_memory_slot *slot) +static int tdp_mmu_make_huge_spte(struct kvm *kvm, + struct tdp_iter *parent, + u64 *huge_spte) +{ + struct kvm_mmu_page *root = spte_to_child_sp(parent->old_spte); + gfn_t start = parent->gfn; + gfn_t end = start + KVM_PAGES_PER_HPAGE(parent->level); + struct tdp_iter iter; + + tdp_root_for_each_leaf_pte(iter, root, start, end) { + /* + * Use the parent iterator when checking for forward progress so + * that KVM doesn't get stuck continuously trying to yield (i.e. + * returning -EAGAIN here and then failing the forward progress + * check in the caller ad nauseam). + */ + if (tdp_mmu_iter_need_resched(kvm, parent)) + return -EAGAIN; + + *huge_spte = make_huge_spte(kvm, iter.old_spte, parent->level); + return 0; + } + + return -ENOENT; +} + +static void recover_huge_pages_range(struct kvm *kvm, + struct kvm_mmu_page *root, + const struct kvm_memory_slot *slot) { gfn_t start = slot->base_gfn; gfn_t end = start + slot->npages; struct tdp_iter iter; int max_mapping_level; + bool flush = false; + u64 huge_spte; + int r; + + if (WARN_ON_ONCE(kvm_slot_dirty_track_enabled(slot))) + return; rcu_read_lock(); for_each_tdp_pte_min_level(iter, root, PG_LEVEL_2M, start, end) { retry: - if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true)) + if (tdp_mmu_iter_cond_resched(kvm, &iter, flush, true)) { + flush = false; continue; + } if (iter.level > KVM_MAX_HUGEPAGE_LEVEL || !is_shadow_present_pte(iter.old_spte)) @@ -1653,31 +1624,40 @@ static void zap_collapsible_spte_range(struct kvm *kvm, if (iter.gfn < start || iter.gfn >= end) continue; - max_mapping_level = kvm_mmu_max_mapping_level(kvm, slot, - iter.gfn, PG_LEVEL_NUM); + max_mapping_level = kvm_mmu_max_mapping_level(kvm, slot, iter.gfn); if (max_mapping_level < iter.level) continue; - /* Note, a successful atomic zap also does a remote TLB flush. */ - if (tdp_mmu_zap_spte_atomic(kvm, &iter)) + r = tdp_mmu_make_huge_spte(kvm, &iter, &huge_spte); + if (r == -EAGAIN) + goto retry; + else if (r) + continue; + + if (tdp_mmu_set_spte_atomic(kvm, &iter, huge_spte)) goto retry; + + flush = true; } + if (flush) + kvm_flush_remote_tlbs_memslot(kvm, slot); + rcu_read_unlock(); } /* - * Zap non-leaf SPTEs (and free their associated page tables) which could - * be replaced by huge pages, for GFNs within the slot. + * Recover huge page mappings within the slot by replacing non-leaf SPTEs with + * huge SPTEs where possible. */ -void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm, - const struct kvm_memory_slot *slot) +void kvm_tdp_mmu_recover_huge_pages(struct kvm *kvm, + const struct kvm_memory_slot *slot) { struct kvm_mmu_page *root; lockdep_assert_held_read(&kvm->mmu_lock); for_each_valid_tdp_mmu_root_yield_safe(kvm, root, slot->as_id) - zap_collapsible_spte_range(kvm, root, slot); + recover_huge_pages_range(kvm, root, slot); } /* diff --git a/arch/x86/kvm/mmu/tdp_mmu.h b/arch/x86/kvm/mmu/tdp_mmu.h index 1b74e058a81c9..f03ca0dd13d9d 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.h +++ b/arch/x86/kvm/mmu/tdp_mmu.h @@ -34,14 +34,14 @@ bool kvm_tdp_mmu_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range); bool kvm_tdp_mmu_wrprot_slot(struct kvm *kvm, const struct kvm_memory_slot *slot, int min_level); -bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, +void kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, const struct kvm_memory_slot *slot); void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn, unsigned long mask, bool wrprot); -void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm, - const struct kvm_memory_slot *slot); +void kvm_tdp_mmu_recover_huge_pages(struct kvm *kvm, + const struct kvm_memory_slot *slot); bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn, diff --git a/arch/x86/kvm/mtrr.c b/arch/x86/kvm/mtrr.c index 05490b9d8a434..6f74e2b27c1ed 100644 --- a/arch/x86/kvm/mtrr.c +++ b/arch/x86/kvm/mtrr.c @@ -19,6 +19,7 @@ #include #include "cpuid.h" +#include "x86.h" static u64 *find_mtrr(struct kvm_vcpu *vcpu, unsigned int msr) { diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h index 0d17d6b706396..e46220ece83c6 100644 --- a/arch/x86/kvm/reverse_cpuid.h +++ b/arch/x86/kvm/reverse_cpuid.h @@ -46,6 +46,7 @@ enum kvm_only_cpuid_leafs { #define X86_FEATURE_AVX_VNNI_INT8 KVM_X86_FEATURE(CPUID_7_1_EDX, 4) #define X86_FEATURE_AVX_NE_CONVERT KVM_X86_FEATURE(CPUID_7_1_EDX, 5) #define X86_FEATURE_AMX_COMPLEX KVM_X86_FEATURE(CPUID_7_1_EDX, 8) +#define X86_FEATURE_AVX_VNNI_INT16 KVM_X86_FEATURE(CPUID_7_1_EDX, 10) #define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14) #define X86_FEATURE_AVX10 KVM_X86_FEATURE(CPUID_7_1_EDX, 19) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index cf84103ce38b9..b708bdf7eaffd 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -926,7 +926,7 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) nested_svm_vmexit(svm); out: - kvm_vcpu_unmap(vcpu, &map, true); + kvm_vcpu_unmap(vcpu, &map); return ret; } @@ -1130,7 +1130,7 @@ int nested_svm_vmexit(struct vcpu_svm *svm) vmcb12->control.exit_int_info_err, KVM_ISA_SVM); - kvm_vcpu_unmap(vcpu, &map, true); + kvm_vcpu_unmap(vcpu, &map); nested_svm_transition_tlb_flush(vcpu); diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 92d4711fd1e44..943bd074a5d37 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3458,7 +3458,7 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm) sev_es_sync_to_ghcb(svm); - kvm_vcpu_unmap(&svm->vcpu, &svm->sev_es.ghcb_map, true); + kvm_vcpu_unmap(&svm->vcpu, &svm->sev_es.ghcb_map); svm->sev_es.ghcb = NULL; } @@ -3839,6 +3839,7 @@ static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu) if (VALID_PAGE(svm->sev_es.snp_vmsa_gpa)) { gfn_t gfn = gpa_to_gfn(svm->sev_es.snp_vmsa_gpa); struct kvm_memory_slot *slot; + struct page *page; kvm_pfn_t pfn; slot = gfn_to_memslot(vcpu->kvm, gfn); @@ -3849,7 +3850,7 @@ static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu) * The new VMSA will be private memory guest memory, so * retrieve the PFN from the gmem backend. */ - if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, NULL)) + if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, &page, NULL)) return -EINVAL; /* @@ -3878,7 +3879,7 @@ static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu) * changes then care should be taken to ensure * svm->sev_es.vmsa is pinned through some other means. */ - kvm_release_pfn_clean(pfn); + kvm_release_page_clean(page); } /* @@ -4678,6 +4679,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) struct kvm_memory_slot *slot; struct kvm *kvm = vcpu->kvm; int order, rmp_level, ret; + struct page *page; bool assigned; kvm_pfn_t pfn; gfn_t gfn; @@ -4704,7 +4706,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) return; } - ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &order); + ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &page, &order); if (ret) { pr_warn_ratelimited("SEV: Unexpected RMP fault, no backing page for private GPA 0x%llx\n", gpa); @@ -4762,7 +4764,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) out: trace_kvm_rmp_fault(vcpu, gpa, pfn, error_code, rmp_level, ret); out_no_trace: - put_page(pfn_to_page(pfn)); + kvm_release_page_unused(page); } static bool is_pfn_range_shared(kvm_pfn_t start, kvm_pfn_t end) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 9df3e1e5ae81a..dd15cc6356553 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1390,7 +1390,9 @@ static void __svm_vcpu_reset(struct kvm_vcpu *vcpu) svm_vcpu_init_msrpm(vcpu, svm->msrpm); svm_init_osvw(vcpu); - vcpu->arch.microcode_version = 0x01000065; + + if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) + vcpu->arch.microcode_version = 0x01000065; svm->tsc_ratio_msr = kvm_caps.default_tsc_scaling_ratio; svm->nmi_masked = false; @@ -2299,7 +2301,7 @@ static int vmload_vmsave_interception(struct kvm_vcpu *vcpu, bool vmload) svm_copy_vmloadsave_state(vmcb12, svm->vmcb); } - kvm_vcpu_unmap(vcpu, &map, true); + kvm_vcpu_unmap(vcpu, &map); return ret; } @@ -4714,7 +4716,7 @@ static int svm_enter_smm(struct kvm_vcpu *vcpu, union kvm_smram *smram) svm_copy_vmrun_state(map_save.hva + 0x400, &svm->vmcb01.ptr->save); - kvm_vcpu_unmap(vcpu, &map_save, true); + kvm_vcpu_unmap(vcpu, &map_save); return 0; } @@ -4774,9 +4776,9 @@ static int svm_leave_smm(struct kvm_vcpu *vcpu, const union kvm_smram *smram) svm->nested.nested_run_pending = 1; unmap_save: - kvm_vcpu_unmap(vcpu, &map_save, true); + kvm_vcpu_unmap(vcpu, &map_save); unmap_map: - kvm_vcpu_unmap(vcpu, &map, true); + kvm_vcpu_unmap(vcpu, &map); return ret; } @@ -5031,6 +5033,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .get_segment = svm_get_segment, .set_segment = svm_set_segment, .get_cpl = svm_get_cpl, + .get_cpl_no_cache = svm_get_cpl, .get_cs_db_l_bits = svm_get_cs_db_l_bits, .is_valid_cr0 = svm_is_valid_cr0, .set_cr0 = svm_set_cr0, diff --git a/arch/x86/kvm/vmx/hyperv.c b/arch/x86/kvm/vmx/hyperv.c index fab6a1ad98dc1..fa41d036acd49 100644 --- a/arch/x86/kvm/vmx/hyperv.c +++ b/arch/x86/kvm/vmx/hyperv.c @@ -4,6 +4,7 @@ #include #include +#include "x86.h" #include "../cpuid.h" #include "hyperv.h" #include "nested.h" diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c index 7668e2fb8043e..92d35cc6cd15d 100644 --- a/arch/x86/kvm/vmx/main.c +++ b/arch/x86/kvm/vmx/main.c @@ -50,6 +50,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .get_segment = vmx_get_segment, .set_segment = vmx_set_segment, .get_cpl = vmx_get_cpl, + .get_cpl_no_cache = vmx_get_cpl_no_cache, .get_cs_db_l_bits = vmx_get_cs_db_l_bits, .is_valid_cr0 = vmx_is_valid_cr0, .set_cr0 = vmx_set_cr0, diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 931a7361c30f2..aa78b6f38dfef 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -7,6 +7,7 @@ #include #include +#include "x86.h" #include "cpuid.h" #include "hyperv.h" #include "mmu.h" @@ -16,7 +17,6 @@ #include "sgx.h" #include "trace.h" #include "vmx.h" -#include "x86.h" #include "smm.h" static bool __read_mostly enable_shadow_vmcs = 1; @@ -231,11 +231,8 @@ static inline void nested_release_evmcs(struct kvm_vcpu *vcpu) struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu); struct vcpu_vmx *vmx = to_vmx(vcpu); - if (nested_vmx_is_evmptr12_valid(vmx)) { - kvm_vcpu_unmap(vcpu, &vmx->nested.hv_evmcs_map, true); - vmx->nested.hv_evmcs = NULL; - } - + kvm_vcpu_unmap(vcpu, &vmx->nested.hv_evmcs_map); + vmx->nested.hv_evmcs = NULL; vmx->nested.hv_evmcs_vmptr = EVMPTR_INVALID; if (hv_vcpu) { @@ -317,6 +314,16 @@ static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs) vcpu->arch.regs_dirty = 0; } +static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + + kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map); + kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map); + kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map); + vmx->nested.pi_desc = NULL; +} + /* * Free whatever needs to be freed from vmx->nested when L1 goes down, or * just stops using VMX. @@ -349,15 +356,8 @@ static void free_nested(struct kvm_vcpu *vcpu) vmx->nested.cached_vmcs12 = NULL; kfree(vmx->nested.cached_shadow_vmcs12); vmx->nested.cached_shadow_vmcs12 = NULL; - /* - * Unpin physical memory we referred to in the vmcs02. The APIC access - * page's backing page (yeah, confusing) shouldn't actually be accessed, - * and if it is written, the contents are irrelevant. - */ - kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false); - kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true); - kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true); - vmx->nested.pi_desc = NULL; + + nested_put_vmcs12_pages(vcpu); kvm_mmu_free_roots(vcpu->kvm, &vcpu->arch.guest_mmu, KVM_MMU_ROOTS_ALL); @@ -624,7 +624,7 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, int msr; unsigned long *msr_bitmap_l1; unsigned long *msr_bitmap_l0 = vmx->nested.vmcs02.msr_bitmap; - struct kvm_host_map *map = &vmx->nested.msr_bitmap_map; + struct kvm_host_map map; /* Nothing to do if the MSR bitmap is not in use. */ if (!cpu_has_vmx_msr_bitmap() || @@ -647,10 +647,10 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, return true; } - if (kvm_vcpu_map(vcpu, gpa_to_gfn(vmcs12->msr_bitmap), map)) + if (kvm_vcpu_map_readonly(vcpu, gpa_to_gfn(vmcs12->msr_bitmap), &map)) return false; - msr_bitmap_l1 = (unsigned long *)map->hva; + msr_bitmap_l1 = (unsigned long *)map.hva; /* * To keep the control flow simple, pay eight 8-byte writes (sixteen @@ -714,7 +714,7 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, MSR_IA32_FLUSH_CMD, MSR_TYPE_W); - kvm_vcpu_unmap(vcpu, &vmx->nested.msr_bitmap_map, false); + kvm_vcpu_unmap(vcpu, &map); vmx->nested.force_msr_bitmap_recalc = false; @@ -3010,6 +3010,17 @@ static int nested_vmx_check_address_space_size(struct kvm_vcpu *vcpu, return 0; } +static bool is_l1_noncanonical_address_on_vmexit(u64 la, struct vmcs12 *vmcs12) +{ + /* + * Check that the given linear address is canonical after a VM exit + * from L2, based on HOST_CR4.LA57 value that will be loaded for L1. + */ + u8 l1_address_bits_on_exit = (vmcs12->host_cr4 & X86_CR4_LA57) ? 57 : 48; + + return !__is_canonical_address(la, l1_address_bits_on_exit); +} + static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { @@ -3020,8 +3031,8 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, CC(!kvm_vcpu_is_legal_cr3(vcpu, vmcs12->host_cr3))) return -EINVAL; - if (CC(is_noncanonical_address(vmcs12->host_ia32_sysenter_esp, vcpu)) || - CC(is_noncanonical_address(vmcs12->host_ia32_sysenter_eip, vcpu))) + if (CC(is_noncanonical_msr_address(vmcs12->host_ia32_sysenter_esp, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_sysenter_eip, vcpu))) return -EINVAL; if ((vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PAT) && @@ -3055,12 +3066,12 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, CC(vmcs12->host_ss_selector == 0 && !ia32e)) return -EINVAL; - if (CC(is_noncanonical_address(vmcs12->host_fs_base, vcpu)) || - CC(is_noncanonical_address(vmcs12->host_gs_base, vcpu)) || - CC(is_noncanonical_address(vmcs12->host_gdtr_base, vcpu)) || - CC(is_noncanonical_address(vmcs12->host_idtr_base, vcpu)) || - CC(is_noncanonical_address(vmcs12->host_tr_base, vcpu)) || - CC(is_noncanonical_address(vmcs12->host_rip, vcpu))) + if (CC(is_noncanonical_base_address(vmcs12->host_fs_base, vcpu)) || + CC(is_noncanonical_base_address(vmcs12->host_gs_base, vcpu)) || + CC(is_noncanonical_base_address(vmcs12->host_gdtr_base, vcpu)) || + CC(is_noncanonical_base_address(vmcs12->host_idtr_base, vcpu)) || + CC(is_noncanonical_base_address(vmcs12->host_tr_base, vcpu)) || + CC(is_l1_noncanonical_address_on_vmexit(vmcs12->host_rip, vmcs12))) return -EINVAL; /* @@ -3178,7 +3189,7 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, } if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS) && - (CC(is_noncanonical_address(vmcs12->guest_bndcfgs & PAGE_MASK, vcpu)) || + (CC(is_noncanonical_msr_address(vmcs12->guest_bndcfgs & PAGE_MASK, vcpu)) || CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) return -EINVAL; @@ -5027,11 +5038,7 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, vmx_update_cpu_dirty_logging(vcpu); } - /* Unpin physical memory we referred to in vmcs02 */ - kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false); - kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true); - kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true); - vmx->nested.pi_desc = NULL; + nested_put_vmcs12_pages(vcpu); if (vmx->nested.reload_vmcs01_apic_access_page) { vmx->nested.reload_vmcs01_apic_access_page = false; @@ -5167,7 +5174,7 @@ int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification, * non-canonical form. This is the only check on the memory * destination for long mode! */ - exn = is_noncanonical_address(*ret, vcpu); + exn = is_noncanonical_address(*ret, vcpu, 0); } else { /* * When not in long mode, the virtual/linear address is @@ -5978,7 +5985,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) * invalidation. */ if (!operand.vpid || - is_noncanonical_address(operand.gla, vcpu)) + is_noncanonical_invlpg_address(operand.gla, vcpu)) return nested_vmx_fail(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); vpid_sync_vcpu_addr(vpid02, operand.gla); diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 83382a4d1d66f..9c9d4a3361664 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -365,7 +365,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) } break; case MSR_IA32_DS_AREA: - if (is_noncanonical_address(data, vcpu)) + if (is_noncanonical_msr_address(data, vcpu)) return 1; pmu->ds_area = data; diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index a3c3d2a51f47d..b352a3ba7354a 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -4,12 +4,11 @@ #include -#include "cpuid.h" +#include "x86.h" #include "kvm_cache_regs.h" #include "nested.h" #include "sgx.h" #include "vmx.h" -#include "x86.h" bool __read_mostly enable_sgx = 1; module_param_named(sgx, enable_sgx, bool, 0444); @@ -38,7 +37,7 @@ static int sgx_get_encls_gva(struct kvm_vcpu *vcpu, unsigned long offset, fault = true; } else if (likely(is_64_bit_mode(vcpu))) { *gva = vmx_get_untagged_addr(vcpu, *gva, 0); - fault = is_noncanonical_address(*gva, vcpu); + fault = is_noncanonical_address(*gva, vcpu, 0); } else { *gva &= 0xffffffff; fault = (s.unusable) || diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d28618e9277ed..893366e537322 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -483,10 +483,9 @@ noinline void invvpid_error(unsigned long ext, u16 vpid, gva_t gva) ext, vpid, gva); } -noinline void invept_error(unsigned long ext, u64 eptp, gpa_t gpa) +noinline void invept_error(unsigned long ext, u64 eptp) { - vmx_insn_failed("invept failed: ext=0x%lx eptp=%llx gpa=0x%llx\n", - ext, eptp, gpa); + vmx_insn_failed("invept failed: ext=0x%lx eptp=%llx\n", ext, eptp); } static DEFINE_PER_CPU(struct vmcs *, vmxarea); @@ -2285,7 +2284,7 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) (!msr_info->host_initiated && !guest_cpuid_has(vcpu, X86_FEATURE_MPX))) return 1; - if (is_noncanonical_address(data & PAGE_MASK, vcpu) || + if (is_noncanonical_msr_address(data & PAGE_MASK, vcpu) || (data & MSR_IA32_BNDCFGS_RSVD)) return 1; @@ -2450,7 +2449,7 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; if (index >= 2 * vmx->pt_desc.num_address_ranges) return 1; - if (is_noncanonical_address(data, vcpu)) + if (is_noncanonical_msr_address(data, vcpu)) return 1; if (index % 2) vmx->pt_desc.guest.addr_b[index / 2] = data; @@ -2458,8 +2457,6 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vmx->pt_desc.guest.addr_a[index / 2] = data; break; case MSR_IA32_PERF_CAPABILITIES: - if (data && !vcpu_to_pmu(vcpu)->version) - return 1; if (data & PMU_CAP_LBR_FMT) { if ((data & PMU_CAP_LBR_FMT) != (kvm_caps.supported_perf_cap & PMU_CAP_LBR_FMT)) @@ -2551,28 +2548,6 @@ static bool cpu_has_sgx(void) return cpuid_eax(0) >= 0x12 && (cpuid_eax(0x12) & BIT(0)); } -/* - * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they - * can't be used due to errata where VM Exit may incorrectly clear - * IA32_PERF_GLOBAL_CTRL[34:32]. Work around the errata by using the - * MSR load mechanism to switch IA32_PERF_GLOBAL_CTRL. - */ -static bool cpu_has_perf_global_ctrl_bug(void) -{ - switch (boot_cpu_data.x86_vfm) { - case INTEL_NEHALEM_EP: /* AAK155 */ - case INTEL_NEHALEM: /* AAP115 */ - case INTEL_WESTMERE: /* AAT100 */ - case INTEL_WESTMERE_EP: /* BC86,AAY89,BD102 */ - case INTEL_NEHALEM_EX: /* BA97 */ - return true; - default: - break; - } - - return false; -} - static int adjust_vmx_controls(u32 ctl_min, u32 ctl_opt, u32 msr, u32 *result) { u32 vmx_msr_low, vmx_msr_high; @@ -2732,6 +2707,27 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, _vmexit_control &= ~x_ctrl; } + /* + * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they + * can't be used due to an errata where VM Exit may incorrectly clear + * IA32_PERF_GLOBAL_CTRL[34:32]. Workaround the errata by using the + * MSR load mechanism to switch IA32_PERF_GLOBAL_CTRL. + */ + switch (boot_cpu_data.x86_vfm) { + case INTEL_NEHALEM_EP: /* AAK155 */ + case INTEL_NEHALEM: /* AAP115 */ + case INTEL_WESTMERE: /* AAT100 */ + case INTEL_WESTMERE_EP: /* BC86,AAY89,BD102 */ + case INTEL_NEHALEM_EX: /* BA97 */ + _vmentry_control &= ~VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; + _vmexit_control &= ~VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL; + pr_warn_once("VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL " + "does not work properly. Using workaround\n"); + break; + default: + break; + } + rdmsrl(MSR_IA32_VMX_BASIC, basic_msr); /* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */ @@ -3570,16 +3566,29 @@ u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg) return vmx_read_guest_seg_base(to_vmx(vcpu), seg); } -int vmx_get_cpl(struct kvm_vcpu *vcpu) +static int __vmx_get_cpl(struct kvm_vcpu *vcpu, bool no_cache) { struct vcpu_vmx *vmx = to_vmx(vcpu); + int ar; if (unlikely(vmx->rmode.vm86_active)) return 0; - else { - int ar = vmx_read_guest_seg_ar(vmx, VCPU_SREG_SS); - return VMX_AR_DPL(ar); - } + + if (no_cache) + ar = vmcs_read32(GUEST_SS_AR_BYTES); + else + ar = vmx_read_guest_seg_ar(vmx, VCPU_SREG_SS); + return VMX_AR_DPL(ar); +} + +int vmx_get_cpl(struct kvm_vcpu *vcpu) +{ + return __vmx_get_cpl(vcpu, false); +} + +int vmx_get_cpl_no_cache(struct kvm_vcpu *vcpu) +{ + return __vmx_get_cpl(vcpu, true); } static u32 vmx_segment_access_rights(struct kvm_segment *var) @@ -4422,9 +4431,6 @@ static u32 vmx_vmentry_ctrl(void) VM_ENTRY_LOAD_IA32_EFER | VM_ENTRY_IA32E_MODE); - if (cpu_has_perf_global_ctrl_bug()) - vmentry_ctrl &= ~VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; - return vmentry_ctrl; } @@ -4442,10 +4448,6 @@ static u32 vmx_vmexit_ctrl(void) if (vmx_pt_mode_is_system()) vmexit_ctrl &= ~(VM_EXIT_PT_CONCEAL_PIP | VM_EXIT_CLEAR_IA32_RTIT_CTL); - - if (cpu_has_perf_global_ctrl_bug()) - vmexit_ctrl &= ~VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL; - /* Loading of EFER and PERF_GLOBAL_CTRL are toggled dynamically */ return vmexit_ctrl & ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER); @@ -4561,7 +4563,8 @@ vmx_adjust_secondary_exec_control(struct vcpu_vmx *vmx, u32 *exec_control, * Update the nested MSR settings so that a nested VMM can/can't set * controls for features that are/aren't exposed to the guest. */ - if (nested) { + if (nested && + kvm_check_has_quirk(vmx->vcpu.kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) { /* * All features that can be added or removed to VMX MSRs must * be supported in the first place for nested virtualization. @@ -4851,7 +4854,8 @@ static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu) init_vmcs(vmx); - if (nested) + if (nested && + kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) memcpy(&vmx->nested.msrs, &vmcs_config.nested, sizeof(vmx->nested.msrs)); vcpu_setup_sgx_lepubkeyhash(vcpu); @@ -4864,7 +4868,8 @@ static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu) vmx->nested.hv_evmcs_vmptr = EVMPTR_INVALID; #endif - vcpu->arch.microcode_version = 0x100000000ULL; + if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) + vcpu->arch.microcode_version = 0x100000000ULL; vmx->msr_ia32_feature_control_valid_bits = FEAT_CTL_LOCKED; /* @@ -6792,8 +6797,10 @@ void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu) struct kvm *kvm = vcpu->kvm; struct kvm_memslots *slots = kvm_memslots(kvm); struct kvm_memory_slot *slot; + struct page *refcounted_page; unsigned long mmu_seq; kvm_pfn_t pfn; + bool writable; /* Defer reload until vmcs01 is the current VMCS. */ if (is_guest_mode(vcpu)) { @@ -6829,30 +6836,30 @@ void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu) * controls the APIC-access page memslot, and only deletes the memslot * if APICv is permanently inhibited, i.e. the memslot won't reappear. */ - pfn = gfn_to_pfn_memslot(slot, gfn); + pfn = __kvm_faultin_pfn(slot, gfn, FOLL_WRITE, &writable, &refcounted_page); if (is_error_noslot_pfn(pfn)) return; read_lock(&vcpu->kvm->mmu_lock); - if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) { + if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu); - read_unlock(&vcpu->kvm->mmu_lock); - goto out; - } + else + vmcs_write64(APIC_ACCESS_ADDR, pfn_to_hpa(pfn)); - vmcs_write64(APIC_ACCESS_ADDR, pfn_to_hpa(pfn)); - read_unlock(&vcpu->kvm->mmu_lock); + /* + * Do not pin the APIC access page in memory so that it can be freely + * migrated, the MMU notifier will call us again if it is migrated or + * swapped out. KVM backs the memslot with anonymous memory, the pfn + * should always point at a refcounted page (if the pfn is valid). + */ + if (!WARN_ON_ONCE(!refcounted_page)) + kvm_release_page_clean(refcounted_page); /* * No need for a manual TLB flush at this point, KVM has already done a * flush if there were SPTEs pointing at the previous page. */ -out: - /* - * Do not pin apic access page in memory, the MMU notifier - * will call us again if it is migrated or swapped out. - */ - kvm_release_pfn_clean(pfn); + read_unlock(&vcpu->kvm->mmu_lock); } void vmx_hwapic_isr_update(int max_isr) @@ -8400,10 +8407,6 @@ __init int vmx_hardware_setup(void) if (setup_vmcs_config(&vmcs_config, &vmx_capability) < 0) return -EIO; - if (cpu_has_perf_global_ctrl_bug()) - pr_warn_once("VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL " - "does not work properly. Using workaround\n"); - if (boot_cpu_has(X86_FEATURE_NX)) kvm_enable_efer_bits(EFER_NX); diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 2325f773a20be..43f573f6ca46a 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -200,8 +200,6 @@ struct nested_vmx { struct kvm_host_map virtual_apic_map; struct kvm_host_map pi_desc_map; - struct kvm_host_map msr_bitmap_map; - struct pi_desc *pi_desc; bool pi_pending; u16 posted_intr_nv; @@ -385,6 +383,7 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu); void vmx_set_host_fs_gs(struct vmcs_host_state *host, u16 fs_sel, u16 gs_sel, unsigned long fs_base, unsigned long gs_base); int vmx_get_cpl(struct kvm_vcpu *vcpu); +int vmx_get_cpl_no_cache(struct kvm_vcpu *vcpu); bool vmx_emulation_required(struct kvm_vcpu *vcpu); unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu); void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h index 93e020dc88f65..633c87e2fd92e 100644 --- a/arch/x86/kvm/vmx/vmx_ops.h +++ b/arch/x86/kvm/vmx/vmx_ops.h @@ -15,7 +15,7 @@ void vmwrite_error(unsigned long field, unsigned long value); void vmclear_error(struct vmcs *vmcs, u64 phys_addr); void vmptrld_error(struct vmcs *vmcs, u64 phys_addr); void invvpid_error(unsigned long ext, u16 vpid, gva_t gva); -void invept_error(unsigned long ext, u64 eptp, gpa_t gpa); +void invept_error(unsigned long ext, u64 eptp); #ifndef CONFIG_CC_HAS_ASM_GOTO_OUTPUT /* @@ -312,13 +312,13 @@ static inline void __invvpid(unsigned long ext, u16 vpid, gva_t gva) vmx_asm2(invvpid, "r"(ext), "m"(operand), ext, vpid, gva); } -static inline void __invept(unsigned long ext, u64 eptp, gpa_t gpa) +static inline void __invept(unsigned long ext, u64 eptp) { struct { - u64 eptp, gpa; - } operand = {eptp, gpa}; - - vmx_asm2(invept, "r"(ext), "m"(operand), ext, eptp, gpa); + u64 eptp; + u64 reserved_0; + } operand = { eptp, 0 }; + vmx_asm2(invept, "r"(ext), "m"(operand), ext, eptp); } static inline void vpid_sync_vcpu_single(int vpid) @@ -355,13 +355,13 @@ static inline void vpid_sync_vcpu_addr(int vpid, gva_t addr) static inline void ept_sync_global(void) { - __invept(VMX_EPT_EXTENT_GLOBAL, 0, 0); + __invept(VMX_EPT_EXTENT_GLOBAL, 0); } static inline void ept_sync_context(u64 eptp) { if (cpu_has_vmx_invept_context()) - __invept(VMX_EPT_EXTENT_CONTEXT, eptp, 0); + __invept(VMX_EPT_EXTENT_CONTEXT, eptp); else ept_sync_global(); } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 83fe0a78146fc..2e713480933a1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -451,6 +451,7 @@ static const u32 msr_based_features_all_except_vmx[] = { MSR_IA32_UCODE_REV, MSR_IA32_ARCH_CAPABILITIES, MSR_IA32_PERF_CAPABILITIES, + MSR_PLATFORM_INFO, }; static u32 msr_based_features[ARRAY_SIZE(msr_based_features_all_except_vmx) + @@ -667,38 +668,6 @@ static void drop_user_return_notifiers(void) kvm_on_user_return(&msrs->urn); } -u64 kvm_get_apic_base(struct kvm_vcpu *vcpu) -{ - return vcpu->arch.apic_base; -} - -enum lapic_mode kvm_get_apic_mode(struct kvm_vcpu *vcpu) -{ - return kvm_apic_mode(kvm_get_apic_base(vcpu)); -} -EXPORT_SYMBOL_GPL(kvm_get_apic_mode); - -int kvm_set_apic_base(struct kvm_vcpu *vcpu, struct msr_data *msr_info) -{ - enum lapic_mode old_mode = kvm_get_apic_mode(vcpu); - enum lapic_mode new_mode = kvm_apic_mode(msr_info->data); - u64 reserved_bits = kvm_vcpu_reserved_gpa_bits_raw(vcpu) | 0x2ff | - (guest_cpuid_has(vcpu, X86_FEATURE_X2APIC) ? 0 : X2APIC_ENABLE); - - if ((msr_info->data & reserved_bits) != 0 || new_mode == LAPIC_MODE_INVALID) - return 1; - if (!msr_info->host_initiated) { - if (old_mode == LAPIC_MODE_X2APIC && new_mode == LAPIC_MODE_XAPIC) - return 1; - if (old_mode == LAPIC_MODE_DISABLED && new_mode == LAPIC_MODE_X2APIC) - return 1; - } - - kvm_lapic_set_base(vcpu, msr_info->data); - kvm_recalculate_apic_map(vcpu->kvm); - return 0; -} - /* * Handle a fault on a hardware virtualization (VMX or SVM) instruction. * @@ -1706,6 +1675,9 @@ static int kvm_get_feature_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, case MSR_IA32_PERF_CAPABILITIES: *data = kvm_caps.supported_perf_cap; break; + case MSR_PLATFORM_INFO: + *data = MSR_PLATFORM_INFO_CPUID_FAULT; + break; case MSR_IA32_UCODE_REV: rdmsrl_safe(index, data); break; @@ -1854,7 +1826,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, case MSR_KERNEL_GS_BASE: case MSR_CSTAR: case MSR_LSTAR: - if (is_noncanonical_address(data, vcpu)) + if (is_noncanonical_msr_address(data, vcpu)) return 1; break; case MSR_IA32_SYSENTER_EIP: @@ -1871,7 +1843,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, * value, and that something deterministic happens if the guest * invokes 64-bit SYSENTER. */ - data = __canonical_address(data, vcpu_virt_addr_bits(vcpu)); + data = __canonical_address(data, max_host_virt_addr_bits()); break; case MSR_TSC_AUX: if (!kvm_is_supported_user_return_msr(MSR_TSC_AUX)) @@ -2144,8 +2116,9 @@ EXPORT_SYMBOL_GPL(kvm_emulate_monitor); static inline bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu) { xfer_to_guest_mode_prepare(); - return vcpu->mode == EXITING_GUEST_MODE || kvm_request_pending(vcpu) || - xfer_to_guest_mode_work_pending(); + + return READ_ONCE(vcpu->mode) == EXITING_GUEST_MODE || + kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending(); } /* @@ -3793,13 +3766,16 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vcpu->arch.microcode_version = data; break; case MSR_IA32_ARCH_CAPABILITIES: - if (!msr_info->host_initiated) - return 1; + if (!msr_info->host_initiated || + !guest_cpuid_has(vcpu, X86_FEATURE_ARCH_CAPABILITIES)) + return KVM_MSR_RET_UNSUPPORTED; vcpu->arch.arch_capabilities = data; break; case MSR_IA32_PERF_CAPABILITIES: - if (!msr_info->host_initiated) - return 1; + if (!msr_info->host_initiated || + !guest_cpuid_has(vcpu, X86_FEATURE_PDCM)) + return KVM_MSR_RET_UNSUPPORTED; + if (data & ~kvm_caps.supported_perf_cap) return 1; @@ -3890,7 +3866,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_MTRRdefType: return kvm_mtrr_set_msr(vcpu, msr, data); case MSR_IA32_APICBASE: - return kvm_set_apic_base(vcpu, msr_info); + return kvm_apic_set_base(vcpu, data, msr_info->host_initiated); case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: return kvm_x2apic_msr_write(vcpu, msr, data); case MSR_IA32_TSC_DEADLINE: @@ -4111,9 +4087,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vcpu->arch.osvw.status = data; break; case MSR_PLATFORM_INFO: - if (!msr_info->host_initiated || - (!(data & MSR_PLATFORM_INFO_CPUID_FAULT) && - cpuid_fault_enabled(vcpu))) + if (!msr_info->host_initiated) return 1; vcpu->arch.msr_platform_info = data; break; @@ -4252,15 +4226,13 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = vcpu->arch.microcode_version; break; case MSR_IA32_ARCH_CAPABILITIES: - if (!msr_info->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_ARCH_CAPABILITIES)) - return 1; + if (!guest_cpuid_has(vcpu, X86_FEATURE_ARCH_CAPABILITIES)) + return KVM_MSR_RET_UNSUPPORTED; msr_info->data = vcpu->arch.arch_capabilities; break; case MSR_IA32_PERF_CAPABILITIES: - if (!msr_info->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_PDCM)) - return 1; + if (!guest_cpuid_has(vcpu, X86_FEATURE_PDCM)) + return KVM_MSR_RET_UNSUPPORTED; msr_info->data = vcpu->arch.perf_capabilities; break; case MSR_IA32_POWER_CTL: @@ -4314,7 +4286,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = 1 << 24; break; case MSR_IA32_APICBASE: - msr_info->data = kvm_get_apic_base(vcpu); + msr_info->data = vcpu->arch.apic_base; break; case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: return kvm_x2apic_msr_read(vcpu, msr_info->index, &msr_info->data); @@ -5094,7 +5066,13 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) int idx; if (vcpu->preempted) { - vcpu->arch.preempted_in_kernel = kvm_arch_vcpu_in_kernel(vcpu); + /* + * Assume protected guests are in-kernel. Inefficient yielding + * due to false positives is preferable to never yielding due + * to false negatives. + */ + vcpu->arch.preempted_in_kernel = vcpu->arch.guest_state_protected || + !kvm_x86_call(get_cpl_no_cache)(vcpu); /* * Take the srcu lock as memslots will be accessed to check the gfn @@ -8612,6 +8590,12 @@ static gva_t emulator_get_untagged_addr(struct x86_emulate_ctxt *ctxt, addr, flags); } +static bool emulator_is_canonical_addr(struct x86_emulate_ctxt *ctxt, + gva_t addr, unsigned int flags) +{ + return !is_noncanonical_address(addr, emul_to_vcpu(ctxt), flags); +} + static const struct x86_emulate_ops emulate_ops = { .vm_bugged = emulator_vm_bugged, .read_gpr = emulator_read_gpr, @@ -8658,6 +8642,7 @@ static const struct x86_emulate_ops emulate_ops = { .triple_fault = emulator_triple_fault, .set_xcr = emulator_set_xcr, .get_untagged_addr = emulator_get_untagged_addr, + .is_canonical_addr = emulator_is_canonical_addr, }; static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask) @@ -10159,7 +10144,7 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu) kvm_run->if_flag = kvm_x86_call(get_if_flag)(vcpu); kvm_run->cr8 = kvm_get_cr8(vcpu); - kvm_run->apic_base = kvm_get_apic_base(vcpu); + kvm_run->apic_base = vcpu->arch.apic_base; kvm_run->ready_for_interrupt_injection = pic_in_kernel(vcpu->kvm) || @@ -10576,8 +10561,8 @@ static void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu) * deleted if any vCPU has xAPIC virtualization and x2APIC enabled, but * and hardware doesn't support x2APIC virtualization. E.g. some AMD * CPUs support AVIC but not x2APIC. KVM still allows enabling AVIC in - * this case so that KVM can the AVIC doorbell to inject interrupts to - * running vCPUs, but KVM must not create SPTEs for the APIC base as + * this case so that KVM can use the AVIC doorbell to inject interrupts + * to running vCPUs, but KVM must not create SPTEs for the APIC base as * the vCPU would incorrectly be able to access the vAPIC page via MMIO * despite being in x2APIC mode. For simplicity, inhibiting the APIC * access page is sticky. @@ -10606,11 +10591,11 @@ void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm, if (!!old != !!new) { /* * Kick all vCPUs before setting apicv_inhibit_reasons to avoid - * false positives in the sanity check WARN in svm_vcpu_run(). + * false positives in the sanity check WARN in vcpu_enter_guest(). * This task will wait for all vCPUs to ack the kick IRQ before * updating apicv_inhibit_reasons, and all other vCPUs will * block on acquiring apicv_update_lock so that vCPUs can't - * redo svm_vcpu_run() without seeing the new inhibit state. + * redo vcpu_enter_guest() without seeing the new inhibit state. * * Note, holding apicv_update_lock and taking it in the read * side (handling the request) also prevents other vCPUs from @@ -11711,7 +11696,7 @@ static void __get_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) sregs->cr4 = kvm_read_cr4(vcpu); sregs->cr8 = kvm_get_cr8(vcpu); sregs->efer = vcpu->arch.efer; - sregs->apic_base = kvm_get_apic_base(vcpu); + sregs->apic_base = vcpu->arch.apic_base; } static void __get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) @@ -11888,16 +11873,13 @@ static bool kvm_is_valid_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, int *mmu_reset_needed, bool update_pdptrs) { - struct msr_data apic_base_msr; int idx; struct desc_ptr dt; if (!kvm_is_valid_sregs(vcpu, sregs)) return -EINVAL; - apic_base_msr.data = sregs->apic_base; - apic_base_msr.host_initiated = true; - if (kvm_set_apic_base(vcpu, &apic_base_msr)) + if (kvm_apic_set_base(vcpu, sregs->apic_base, true)) return -EINVAL; if (vcpu->arch.guest_state_protected) @@ -12299,7 +12281,11 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) kvm_async_pf_hash_reset(vcpu); - vcpu->arch.perf_capabilities = kvm_caps.supported_perf_cap; + if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) { + vcpu->arch.arch_capabilities = kvm_get_arch_capabilities(); + vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT; + vcpu->arch.perf_capabilities = kvm_caps.supported_perf_cap; + } kvm_pmu_init(vcpu); vcpu->arch.pending_external_vector = -1; @@ -12313,8 +12299,6 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) if (r) goto free_guest_fpu; - vcpu->arch.arch_capabilities = kvm_get_arch_capabilities(); - vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT; kvm_xen_init_vcpu(vcpu); vcpu_load(vcpu); kvm_set_tsc_khz(vcpu, vcpu->kvm->arch.default_tsc_khz); @@ -13104,19 +13088,15 @@ static void kvm_mmu_slot_apply_flags(struct kvm *kvm, if (!log_dirty_pages) { /* - * Dirty logging tracks sptes in 4k granularity, meaning that - * large sptes have to be split. If live migration succeeds, - * the guest in the source machine will be destroyed and large - * sptes will be created in the destination. However, if the - * guest continues to run in the source machine (for example if - * live migration fails), small sptes will remain around and - * cause bad performance. + * Recover huge page mappings in the slot now that dirty logging + * is disabled, i.e. now that KVM does not have to track guest + * writes at 4KiB granularity. * - * Scan sptes if dirty logging has been stopped, dropping those - * which can be collapsed into a single large-page spte. Later - * page faults will create the large-page sptes. + * Dirty logging might be disabled by userspace if an ongoing VM + * live migration is cancelled and the VM must continue running + * on the source. */ - kvm_mmu_zap_collapsible_sptes(kvm, new); + kvm_mmu_recover_huge_pages(kvm, new); } else { /* * Initially-all-set does not require write protecting any page, @@ -13207,6 +13187,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) { + WARN_ON_ONCE(!kvm_arch_pmi_in_guest(vcpu)); + if (vcpu->arch.guest_state_protected) return true; @@ -13215,6 +13197,11 @@ bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu) { + WARN_ON_ONCE(!kvm_arch_pmi_in_guest(vcpu)); + + if (vcpu->arch.guest_state_protected) + return 0; + return kvm_rip_read(vcpu); } @@ -13730,7 +13717,7 @@ int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva) * invalidation. */ if ((!pcid_enabled && (operand.pcid != 0)) || - is_noncanonical_address(operand.gla, vcpu)) { + is_noncanonical_invlpg_address(operand.gla, vcpu)) { kvm_inject_gp(vcpu, 0); return 1; } diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index a84c48ef52785..ec623d23d13d2 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -8,6 +8,7 @@ #include #include "kvm_cache_regs.h" #include "kvm_emulate.h" +#include "cpuid.h" struct kvm_caps { /* control of guest tsc rate supported? */ @@ -233,9 +234,52 @@ static inline u8 vcpu_virt_addr_bits(struct kvm_vcpu *vcpu) return kvm_is_cr4_bit_set(vcpu, X86_CR4_LA57) ? 57 : 48; } -static inline bool is_noncanonical_address(u64 la, struct kvm_vcpu *vcpu) +static inline u8 max_host_virt_addr_bits(void) { - return !__is_canonical_address(la, vcpu_virt_addr_bits(vcpu)); + return kvm_cpu_cap_has(X86_FEATURE_LA57) ? 57 : 48; +} + +/* + * x86 MSRs which contain linear addresses, x86 hidden segment bases, and + * IDT/GDT bases have static canonicality checks, the size of which depends + * only on the CPU's support for 5-level paging, rather than on the state of + * CR4.LA57. This applies to both WRMSR and to other instructions that set + * their values, e.g. SGDT. + * + * KVM passes through most of these MSRS and also doesn't intercept the + * instructions that set the hidden segment bases. + * + * Because of this, to be consistent with hardware, even if the guest doesn't + * have LA57 enabled in its CPUID, perform canonicality checks based on *host* + * support for 5 level paging. + * + * Finally, instructions which are related to MMU invalidation of a given + * linear address, also have a similar static canonical check on address. + * This allows for example to invalidate 5-level addresses of a guest from a + * host which uses 4-level paging. + */ +static inline bool is_noncanonical_address(u64 la, struct kvm_vcpu *vcpu, + unsigned int flags) +{ + if (flags & (X86EMUL_F_INVLPG | X86EMUL_F_MSR | X86EMUL_F_DT_LOAD)) + return !__is_canonical_address(la, max_host_virt_addr_bits()); + else + return !__is_canonical_address(la, vcpu_virt_addr_bits(vcpu)); +} + +static inline bool is_noncanonical_msr_address(u64 la, struct kvm_vcpu *vcpu) +{ + return is_noncanonical_address(la, vcpu, X86EMUL_F_MSR); +} + +static inline bool is_noncanonical_base_address(u64 la, struct kvm_vcpu *vcpu) +{ + return is_noncanonical_address(la, vcpu, X86EMUL_F_DT_LOAD); +} + +static inline bool is_noncanonical_invlpg_address(u64 la, struct kvm_vcpu *vcpu) +{ + return is_noncanonical_address(la, vcpu, X86EMUL_F_INVLPG); } static inline void vcpu_cache_mmio_info(struct kvm_vcpu *vcpu, diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index b0d5a644fc84d..a2becb85bea79 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "mm_internal.h" @@ -1140,7 +1141,7 @@ STATIC_NOPV void native_flush_tlb_one_user(unsigned long addr) bool cpu_pcide; /* Flush 'addr' from the kernel PCID: */ - asm volatile("invlpg (%0)" ::"r" (addr) : "memory"); + invlpg(addr); /* If PTI is off there is no user PCID and nothing to flush. */ if (!static_cpu_has(X86_FEATURE_PTI)) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 55c4b07ec1f63..0c316bae1726e 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -250,6 +250,125 @@ void __init pci_acpi_crs_quirks(void) pr_info("Please notify linux-pci@vger.kernel.org so future kernels can do this automatically\n"); } +/* + * Check if pdev is part of a PCIe switch that is directly below the + * specified bridge. + */ +static bool pcie_switch_directly_under(struct pci_dev *bridge, + struct pci_dev *pdev) +{ + struct pci_dev *parent = pci_upstream_bridge(pdev); + + /* If the device doesn't have a parent, it's not under anything */ + if (!parent) + return false; + + /* + * If the device has a PCIe type, check if it is below the + * corresponding PCIe switch components (if applicable). Then check + * if its upstream port is directly beneath the specified bridge. + */ + switch (pci_pcie_type(pdev)) { + case PCI_EXP_TYPE_UPSTREAM: + return parent == bridge; + + case PCI_EXP_TYPE_DOWNSTREAM: + if (pci_pcie_type(parent) != PCI_EXP_TYPE_UPSTREAM) + return false; + parent = pci_upstream_bridge(parent); + return parent == bridge; + + case PCI_EXP_TYPE_ENDPOINT: + if (pci_pcie_type(parent) != PCI_EXP_TYPE_DOWNSTREAM) + return false; + parent = pci_upstream_bridge(parent); + if (!parent || pci_pcie_type(parent) != PCI_EXP_TYPE_UPSTREAM) + return false; + parent = pci_upstream_bridge(parent); + return parent == bridge; + } + + return false; +} + +static bool pcie_has_usb4_host_interface(struct pci_dev *pdev) +{ + struct fwnode_handle *fwnode; + + /* + * For USB4, the tunneled PCIe Root or Downstream Ports are marked + * with the "usb4-host-interface" ACPI property, so we look for + * that first. This should cover most cases. + */ + fwnode = fwnode_find_reference(dev_fwnode(&pdev->dev), + "usb4-host-interface", 0); + if (!IS_ERR(fwnode)) { + fwnode_handle_put(fwnode); + return true; + } + + /* + * Any integrated Thunderbolt 3/4 PCIe Root Ports from Intel + * before Alder Lake do not have the "usb4-host-interface" + * property so we use their PCI IDs instead. All these are + * tunneled. This list is not expected to grow. + */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + switch (pdev->device) { + /* Ice Lake Thunderbolt 3 PCIe Root Ports */ + case 0x8a1d: + case 0x8a1f: + case 0x8a21: + case 0x8a23: + /* Tiger Lake-LP Thunderbolt 4 PCIe Root Ports */ + case 0x9a23: + case 0x9a25: + case 0x9a27: + case 0x9a29: + /* Tiger Lake-H Thunderbolt 4 PCIe Root Ports */ + case 0x9a2b: + case 0x9a2d: + case 0x9a2f: + case 0x9a31: + return true; + } + } + + return false; +} + +bool arch_pci_dev_is_removable(struct pci_dev *pdev) +{ + struct pci_dev *parent, *root; + + /* pdev without a parent or Root Port is never tunneled */ + parent = pci_upstream_bridge(pdev); + if (!parent) + return false; + root = pcie_find_root_port(pdev); + if (!root) + return false; + + /* Internal PCIe devices are not tunneled */ + if (!root->external_facing) + return false; + + /* Anything directly behind a "usb4-host-interface" is tunneled */ + if (pcie_has_usb4_host_interface(parent)) + return true; + + /* + * Check if this is a discrete Thunderbolt/USB4 controller that is + * directly behind the non-USB4 PCIe Root Port marked as + * "ExternalFacingPort". Those are not behind a PCIe tunnel. + */ + if (pcie_switch_directly_under(root, pdev)) + return false; + + /* PCIe devices after the discrete chip are tunneled */ + return true; +} + #ifdef CONFIG_PCI_MMCONFIG static int check_segment(u16 seg, struct device *dev, char *estr) { diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 98a9bb92d75c8..0681ecfe34300 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -757,7 +757,7 @@ static void pci_amd_enable_64bit_bar(struct pci_dev *dev) dev_info(&dev->dev, "adding root bus resource %pR (tainting kernel)\n", res); add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); - pci_bus_add_resource(dev->bus, res, 0); + pci_bus_add_resource(dev->bus, res); } base = ((res->start >> 8) & AMD_141b_MMIO_BASE_MMIOBASE_MASK) | diff --git a/arch/x86/um/Kconfig b/arch/x86/um/Kconfig index 186f13268401b..986045d5e6385 100644 --- a/arch/x86/um/Kconfig +++ b/arch/x86/um/Kconfig @@ -10,6 +10,7 @@ config UML_X86 def_bool y select ARCH_BINFMT_ELF_EXTRA_PHDRS if X86_32 select DCACHE_WORD_ACCESS + select HAVE_EFFICIENT_UNALIGNED_ACCESS config 64BIT bool "64-bit kernel" if "$(SUBARCH)" = "x86" @@ -28,17 +29,6 @@ config X86_64 def_bool 64BIT select MODULES_USE_ELF_RELA -config 3_LEVEL_PGTABLES - bool "Three-level pagetables" if !64BIT - default 64BIT - help - Three-level pagetables will let UML have more than 4G of physical - memory. All the memory that can't be mapped directly will be treated - as high memory. - - However, this it experimental on 32-bit architectures, so if unsure say - N (on x86-64 it's automatically enabled, instead, as it's safe there). - config ARCH_HAS_SC_SIGNALS def_bool !64BIT diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile index 36e67fc97c22f..b42c31cd2390c 100644 --- a/arch/x86/um/Makefile +++ b/arch/x86/um/Makefile @@ -10,7 +10,7 @@ else endif obj-y = bugs_$(BITS).o delay.o fault.o \ - ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ + ptrace.o ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ stub_segv.o \ sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \ mem_$(BITS).o subarch.o os-Linux/ diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h index 6052200fe9256..62ed5d68a9788 100644 --- a/arch/x86/um/asm/elf.h +++ b/arch/x86/um/asm/elf.h @@ -8,6 +8,8 @@ #include #include +#define CORE_DUMP_USE_REGSET + #ifdef CONFIG_X86_32 #define R_386_NONE 0 diff --git a/arch/x86/um/asm/ptrace.h b/arch/x86/um/asm/ptrace.h index 2fef3da555337..2641d28d115ca 100644 --- a/arch/x86/um/asm/ptrace.h +++ b/arch/x86/um/asm/ptrace.h @@ -2,6 +2,16 @@ #ifndef __UM_X86_PTRACE_H #define __UM_X86_PTRACE_H +/* This is here because signal.c needs the REGSET_FP_LEGACY definition */ +enum { + REGSET_GENERAL, +#ifdef CONFIG_X86_32 + REGSET_FP_LEGACY, +#endif + REGSET_FP, + REGSET_XSTATE, +}; + #include #ifndef CONFIG_X86_32 #define __FRAME_OFFSETS /* Needed to get the R* macros */ diff --git a/arch/x86/um/os-Linux/Makefile b/arch/x86/um/os-Linux/Makefile index 5249bbc30dcdb..77a308aaa5ec9 100644 --- a/arch/x86/um/os-Linux/Makefile +++ b/arch/x86/um/os-Linux/Makefile @@ -3,7 +3,7 @@ # Licensed under the GPL # -obj-y = registers.o task_size.o mcontext.o +obj-y = registers.o mcontext.o obj-$(CONFIG_X86_32) += tls.o diff --git a/arch/x86/um/os-Linux/registers.c b/arch/x86/um/os-Linux/registers.c index f3638dd09cece..76eaeb93928cc 100644 --- a/arch/x86/um/os-Linux/registers.c +++ b/arch/x86/um/os-Linux/registers.c @@ -16,133 +16,58 @@ #include #include #include +#include -static int have_xstate_support; +unsigned long host_fp_size; -int save_i387_registers(int pid, unsigned long *fp_regs) -{ - if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) - return -errno; - return 0; -} - -int save_fp_registers(int pid, unsigned long *fp_regs) +int get_fp_registers(int pid, unsigned long *regs) { -#ifdef PTRACE_GETREGSET - struct iovec iov; + struct iovec iov = { + .iov_base = regs, + .iov_len = host_fp_size, + }; - if (have_xstate_support) { - iov.iov_base = fp_regs; - iov.iov_len = FP_SIZE * sizeof(unsigned long); - if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0) - return -errno; - return 0; - } else -#endif - return save_i387_registers(pid, fp_regs); -} - -int restore_i387_registers(int pid, unsigned long *fp_regs) -{ - if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0) return -errno; return 0; } -int restore_fp_registers(int pid, unsigned long *fp_regs) -{ -#ifdef PTRACE_SETREGSET - struct iovec iov; - if (have_xstate_support) { - iov.iov_base = fp_regs; - iov.iov_len = FP_SIZE * sizeof(unsigned long); - if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0) - return -errno; - return 0; - } else -#endif - return restore_i387_registers(pid, fp_regs); -} - -#ifdef __i386__ -int have_fpx_regs = 1; -int save_fpx_registers(int pid, unsigned long *fp_regs) +int put_fp_registers(int pid, unsigned long *regs) { - if (ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs) < 0) - return -errno; - return 0; -} + struct iovec iov = { + .iov_base = regs, + .iov_len = host_fp_size, + }; -int restore_fpx_registers(int pid, unsigned long *fp_regs) -{ - if (ptrace(PTRACE_SETFPXREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0) return -errno; return 0; } -int get_fp_registers(int pid, unsigned long *regs) -{ - if (have_fpx_regs) - return save_fpx_registers(pid, regs); - else - return save_fp_registers(pid, regs); -} - -int put_fp_registers(int pid, unsigned long *regs) -{ - if (have_fpx_regs) - return restore_fpx_registers(pid, regs); - else - return restore_fp_registers(pid, regs); -} - -void arch_init_registers(int pid) -{ - struct user_fpxregs_struct fpx_regs; - int err; - - err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs); - if (!err) - return; - - if (errno != EIO) - panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", - errno); - - have_fpx_regs = 0; -} -#else - -int get_fp_registers(int pid, unsigned long *regs) +int arch_init_registers(int pid) { - return save_fp_registers(pid, regs); + struct iovec iov = { + /* Just use plenty of space, it does not cost us anything */ + .iov_len = 2 * 1024 * 1024, + }; + int ret; + + iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (iov.iov_base == MAP_FAILED) + return -ENOMEM; + + /* GDB has x86_xsave_length, which uses x86_cpuid_count */ + ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov); + if (ret) + ret = -errno; + munmap(iov.iov_base, 2 * 1024 * 1024); + + host_fp_size = iov.iov_len; + + return ret; } -int put_fp_registers(int pid, unsigned long *regs) -{ - return restore_fp_registers(pid, regs); -} - -void arch_init_registers(int pid) -{ -#ifdef PTRACE_GETREGSET - void * fp_regs; - struct iovec iov; - - fp_regs = malloc(FP_SIZE * sizeof(unsigned long)); - if(fp_regs == NULL) - return; - - iov.iov_base = fp_regs; - iov.iov_len = FP_SIZE * sizeof(unsigned long); - if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) == 0) - have_xstate_support = 1; - - free(fp_regs); -#endif -} -#endif - unsigned long get_thread_reg(int reg, jmp_buf *buf) { switch (reg) { diff --git a/arch/x86/um/os-Linux/task_size.c b/arch/x86/um/os-Linux/task_size.c deleted file mode 100644 index 1dc9adc20b1ca..0000000000000 --- a/arch/x86/um/os-Linux/task_size.c +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include - -#ifdef __i386__ - -static jmp_buf buf; - -static void segfault(int sig) -{ - longjmp(buf, 1); -} - -static int page_ok(unsigned long page) -{ - unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT); - unsigned long n = ~0UL; - void *mapped = NULL; - int ok = 0; - - /* - * First see if the page is readable. If it is, it may still - * be a VDSO, so we go on to see if it's writable. If not - * then try mapping memory there. If that fails, then we're - * still in the kernel area. As a sanity check, we'll fail if - * the mmap succeeds, but gives us an address different from - * what we wanted. - */ - if (setjmp(buf) == 0) - n = *address; - else { - mapped = mmap(address, UM_KERN_PAGE_SIZE, - PROT_READ | PROT_WRITE, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (mapped == MAP_FAILED) - return 0; - if (mapped != address) - goto out; - } - - /* - * Now, is it writeable? If so, then we're in user address - * space. If not, then try mprotecting it and try the write - * again. - */ - if (setjmp(buf) == 0) { - *address = n; - ok = 1; - goto out; - } else if (mprotect(address, UM_KERN_PAGE_SIZE, - PROT_READ | PROT_WRITE) != 0) - goto out; - - if (setjmp(buf) == 0) { - *address = n; - ok = 1; - } - - out: - if (mapped != NULL) - munmap(mapped, UM_KERN_PAGE_SIZE); - return ok; -} - -unsigned long os_get_top_address(void) -{ - struct sigaction sa, old; - unsigned long bottom = 0; - /* - * A 32-bit UML on a 64-bit host gets confused about the VDSO at - * 0xffffe000. It is mapped, is readable, can be reprotected writeable - * and written. However, exec discovers later that it can't be - * unmapped. So, just set the highest address to be checked to just - * below it. This might waste some address space on 4G/4G 32-bit - * hosts, but shouldn't hurt otherwise. - */ - unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT; - unsigned long test, original; - - printf("Locating the bottom of the address space ... "); - fflush(stdout); - - /* - * We're going to be longjmping out of the signal handler, so - * SA_DEFER needs to be set. - */ - sa.sa_handler = segfault; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_NODEFER; - if (sigaction(SIGSEGV, &sa, &old)) { - perror("os_get_top_address"); - exit(1); - } - - /* Manually scan the address space, bottom-up, until we find - * the first valid page (or run out of them). - */ - for (bottom = 0; bottom < top; bottom++) { - if (page_ok(bottom)) - break; - } - - /* If we've got this far, we ran out of pages. */ - if (bottom == top) { - fprintf(stderr, "Unable to determine bottom of address " - "space.\n"); - exit(1); - } - - printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT); - printf("Locating the top of the address space ... "); - fflush(stdout); - - original = bottom; - - /* This could happen with a 4G/4G split */ - if (page_ok(top)) - goto out; - - do { - test = bottom + (top - bottom) / 2; - if (page_ok(test)) - bottom = test; - else - top = test; - } while (top - bottom > 1); - -out: - /* Restore the old SIGSEGV handling */ - if (sigaction(SIGSEGV, &old, NULL)) { - perror("os_get_top_address"); - exit(1); - } - top <<= UM_KERN_PAGE_SHIFT; - printf("0x%lx\n", top); - - return top; -} - -#else - -unsigned long os_get_top_address(void) -{ - /* The old value of CONFIG_TOP_ADDR */ - return 0x7fc0002000; -} - -#endif diff --git a/arch/x86/um/ptrace.c b/arch/x86/um/ptrace.c new file mode 100644 index 0000000000000..57c504fd5626e --- /dev/null +++ b/arch/x86/um/ptrace.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#ifdef CONFIG_X86_32 +/* + * FPU tag word conversions. + */ + +static inline unsigned short twd_i387_to_fxsr(unsigned short twd) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; +} + +static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave) +{ + struct _fpxreg *st = NULL; + unsigned long twd = (unsigned long) fxsave->twd; + unsigned long tag; + unsigned long ret = 0xffff0000; + int i; + +#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16) + + for (i = 0; i < 8; i++) { + if (twd & 0x1) { + st = (struct _fpxreg *) FPREG_ADDR(fxsave, i); + + switch (st->exponent & 0x7fff) { + case 0x7fff: + tag = 2; /* Special */ + break; + case 0x0000: + if (!st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3]) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if (st->significand[3] & 0x8000) + tag = 0; /* Valid */ + else + tag = 2; /* Special */ + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +/* Get/set the old 32bit i387 registers (pre-FPX) */ +static int fpregs_legacy_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; + int i; + + membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul); + membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul); + membuf_store(&to, twd_fxsr_to_i387(fxsave)); + membuf_store(&to, fxsave->fip); + membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16)); + membuf_store(&to, fxsave->foo); + membuf_store(&to, fxsave->fos); + + for (i = 0; i < 8; i++) + membuf_write(&to, (void *)fxsave->st_space + i * 16, 10); + + return 0; +} + +static int fpregs_legacy_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; + const struct user_i387_struct *from; + struct user_i387_struct buf; + int i; + + if (ubuf) { + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + from = &buf; + } else { + from = kbuf; + } + + fxsave->cwd = (unsigned short)(from->cwd & 0xffff); + fxsave->swd = (unsigned short)(from->swd & 0xffff); + fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff)); + fxsave->fip = from->fip; + fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16); + fxsave->fcs = (from->fcs & 0xffff); + fxsave->foo = from->foo; + fxsave->fos = from->fos; + + for (i = 0; i < 8; i++) { + memcpy((void *)fxsave->st_space + i * 16, + (void *)from->st_space + i * 10, 10); + } + + return 0; +} +#endif + +static int genregs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + int reg; + + for (reg = 0; to.left; reg++) + membuf_store(&to, getreg(target, reg * sizeof(unsigned long))); + return 0; +} + +static int genregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret = 0; + + if (kbuf) { + const unsigned long *k = kbuf; + + while (count >= sizeof(*k) && !ret) { + ret = putreg(target, pos, *k++); + count -= sizeof(*k); + pos += sizeof(*k); + } + } else { + const unsigned long __user *u = ubuf; + + while (count >= sizeof(*u) && !ret) { + unsigned long word; + + ret = __get_user(word, u++); + if (ret) + break; + ret = putreg(target, pos, word); + count -= sizeof(*u); + pos += sizeof(*u); + } + } + return ret; +} + +static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset) +{ + return regset->n; +} + +static int generic_fpregs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + void *fpregs = task_pt_regs(target)->regs.fp; + + membuf_write(&to, fpregs, regset->size * regset->n); + return 0; +} + +static int generic_fpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + void *fpregs = task_pt_regs(target)->regs.fp; + + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, 0, regset->size * regset->n); +} + +static struct user_regset uml_regsets[] __ro_after_init = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = sizeof(struct user_regs_struct) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .regset_get = genregs_get, + .set = genregs_set + }, +#ifdef CONFIG_X86_32 + /* Old FP registers, they are needed in signal frames */ + [REGSET_FP_LEGACY] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(struct user_i387_ia32_struct) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .active = generic_fpregs_active, + .regset_get = fpregs_legacy_get, + .set = fpregs_legacy_set, + }, +#endif + [REGSET_FP] = { +#ifdef CONFIG_X86_32 + .core_note_type = NT_PRXFPREG, + .n = sizeof(struct user32_fxsr_struct) / sizeof(long), +#else + .core_note_type = NT_PRFPREG, + .n = sizeof(struct user_i387_struct) / sizeof(long), +#endif + .size = sizeof(long), + .align = sizeof(long), + .active = generic_fpregs_active, + .regset_get = generic_fpregs_get, + .set = generic_fpregs_set, + }, + [REGSET_XSTATE] = { + .core_note_type = NT_X86_XSTATE, + .size = sizeof(long), + .align = sizeof(long), + .active = generic_fpregs_active, + .regset_get = generic_fpregs_get, + .set = generic_fpregs_set, + }, + /* TODO: Add TLS regset for 32bit */ +}; + +static const struct user_regset_view user_uml_view = { +#ifdef CONFIG_X86_32 + .name = "i386", .e_machine = EM_386, +#else + .name = "x86_64", .e_machine = EM_X86_64, +#endif + .regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets) +}; + +const struct user_regset_view * +task_user_regset_view(struct task_struct *tsk) +{ + return &user_uml_view; +} + +static int __init init_regset_xstate_info(void) +{ + uml_regsets[REGSET_XSTATE].n = + host_fp_size / uml_regsets[REGSET_XSTATE].size; + + return 0; +} +arch_initcall(init_regset_xstate_info); diff --git a/arch/x86/um/ptrace_32.c b/arch/x86/um/ptrace_32.c index b0a71c6cdc6e6..3af3cb821524b 100644 --- a/arch/x86/um/ptrace_32.c +++ b/arch/x86/um/ptrace_32.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -168,65 +169,6 @@ int peek_user(struct task_struct *child, long addr, long data) return put_user(tmp, (unsigned long __user *) data); } -static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int err, n, cpu = task_cpu(child); - struct user_i387_struct fpregs; - - err = save_i387_registers(userspace_pid[cpu], - (unsigned long *) &fpregs); - if (err) - return err; - - n = copy_to_user(buf, &fpregs, sizeof(fpregs)); - if(n > 0) - return -EFAULT; - - return n; -} - -static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int n, cpu = task_cpu(child); - struct user_i387_struct fpregs; - - n = copy_from_user(&fpregs, buf, sizeof(fpregs)); - if (n > 0) - return -EFAULT; - - return restore_i387_registers(userspace_pid[cpu], - (unsigned long *) &fpregs); -} - -static int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) -{ - int err, n, cpu = task_cpu(child); - struct user_fxsr_struct fpregs; - - err = save_fpx_registers(userspace_pid[cpu], (unsigned long *) &fpregs); - if (err) - return err; - - n = copy_to_user(buf, &fpregs, sizeof(fpregs)); - if(n > 0) - return -EFAULT; - - return n; -} - -static int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) -{ - int n, cpu = task_cpu(child); - struct user_fxsr_struct fpregs; - - n = copy_from_user(&fpregs, buf, sizeof(fpregs)); - if (n > 0) - return -EFAULT; - - return restore_fpx_registers(userspace_pid[cpu], - (unsigned long *) &fpregs); -} - long subarch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -234,17 +176,25 @@ long subarch_ptrace(struct task_struct *child, long request, void __user *datap = (void __user *) data; switch (request) { case PTRACE_GETFPREGS: /* Get the child FPU state. */ - ret = get_fpregs(datap, child); - break; + return copy_regset_to_user(child, task_user_regset_view(child), + REGSET_FP_LEGACY, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_SETFPREGS: /* Set the child FPU state. */ - ret = set_fpregs(datap, child); - break; + return copy_regset_from_user(child, task_user_regset_view(child), + REGSET_FP_LEGACY, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_GETFPXREGS: /* Get the child FPU state. */ - ret = get_fpxregs(datap, child); - break; + return copy_regset_to_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_fxsr_struct), + datap); case PTRACE_SETFPXREGS: /* Set the child FPU state. */ - ret = set_fpxregs(datap, child); - break; + return copy_regset_from_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_fxsr_struct), + datap); default: ret = -EIO; } diff --git a/arch/x86/um/ptrace_64.c b/arch/x86/um/ptrace_64.c index aa68d83d3f441..e0d4120a45c8b 100644 --- a/arch/x86/um/ptrace_64.c +++ b/arch/x86/um/ptrace_64.c @@ -8,6 +8,7 @@ #include #include #include +#include #define __FRAME_OFFSETS #include #include @@ -188,36 +189,6 @@ int peek_user(struct task_struct *child, long addr, long data) return put_user(tmp, (unsigned long *) data); } -static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int err, n, cpu = ((struct thread_info *) child->stack)->cpu; - struct user_i387_struct fpregs; - - err = save_i387_registers(userspace_pid[cpu], - (unsigned long *) &fpregs); - if (err) - return err; - - n = copy_to_user(buf, &fpregs, sizeof(fpregs)); - if (n > 0) - return -EFAULT; - - return n; -} - -static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int n, cpu = ((struct thread_info *) child->stack)->cpu; - struct user_i387_struct fpregs; - - n = copy_from_user(&fpregs, buf, sizeof(fpregs)); - if (n > 0) - return -EFAULT; - - return restore_i387_registers(userspace_pid[cpu], - (unsigned long *) &fpregs); -} - long subarch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -226,11 +197,15 @@ long subarch_ptrace(struct task_struct *child, long request, switch (request) { case PTRACE_GETFPREGS: /* Get the child FPU state. */ - ret = get_fpregs(datap, child); - break; + return copy_regset_to_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_SETFPREGS: /* Set the child FPU state. */ - ret = set_fpregs(datap, child); - break; + return copy_regset_from_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_ARCH_PRCTL: /* XXX Calls ptrace on the host - needs some SMP thinking */ ret = arch_prctl(child, data, (void __user *) addr); diff --git a/arch/x86/um/shared/sysdep/ptrace.h b/arch/x86/um/shared/sysdep/ptrace.h index 6ca4ecabc55b5..2dd4ca6713f8b 100644 --- a/arch/x86/um/shared/sysdep/ptrace.h +++ b/arch/x86/um/shared/sysdep/ptrace.h @@ -56,12 +56,16 @@ struct syscall_args { UPT_SYSCALL_ARG5(r), \ UPT_SYSCALL_ARG6(r) } } ) +extern unsigned long host_fp_size; + struct uml_pt_regs { unsigned long gp[MAX_REG_NR]; - unsigned long fp[MAX_FP_NR]; struct faultinfo faultinfo; long syscall; int is_user; + + /* Dynamically sized FP registers (holds an XSTATE) */ + unsigned long fp[]; }; #define EMPTY_UML_PT_REGS { } @@ -72,4 +76,6 @@ struct uml_pt_regs { extern int user_context(unsigned long sp); +extern int arch_init_registers(int pid); + #endif /* __SYSDEP_X86_PTRACE_H */ diff --git a/arch/x86/um/shared/sysdep/ptrace_32.h b/arch/x86/um/shared/sysdep/ptrace_32.h index 0c4989842fbe6..2392470cac4db 100644 --- a/arch/x86/um/shared/sysdep/ptrace_32.h +++ b/arch/x86/um/shared/sysdep/ptrace_32.h @@ -6,8 +6,6 @@ #ifndef __SYSDEP_I386_PTRACE_H #define __SYSDEP_I386_PTRACE_H -#define MAX_FP_NR HOST_FPX_SIZE - #define UPT_SYSCALL_ARG1(r) UPT_BX(r) #define UPT_SYSCALL_ARG2(r) UPT_CX(r) #define UPT_SYSCALL_ARG3(r) UPT_DX(r) @@ -15,6 +13,4 @@ #define UPT_SYSCALL_ARG5(r) UPT_DI(r) #define UPT_SYSCALL_ARG6(r) UPT_BP(r) -extern void arch_init_registers(int pid); - #endif diff --git a/arch/x86/um/shared/sysdep/ptrace_64.h b/arch/x86/um/shared/sysdep/ptrace_64.h index 0dc223aa1c2db..e73573ac871fc 100644 --- a/arch/x86/um/shared/sysdep/ptrace_64.h +++ b/arch/x86/um/shared/sysdep/ptrace_64.h @@ -8,8 +8,6 @@ #ifndef __SYSDEP_X86_64_PTRACE_H #define __SYSDEP_X86_64_PTRACE_H -#define MAX_FP_NR HOST_FP_SIZE - #define REGS_R8(r) ((r)[HOST_R8]) #define REGS_R9(r) ((r)[HOST_R9]) #define REGS_R10(r) ((r)[HOST_R10]) @@ -57,6 +55,4 @@ #define UPT_SYSCALL_ARG5(r) UPT_R8(r) #define UPT_SYSCALL_ARG6(r) UPT_R9(r) -extern void arch_init_registers(int pid); - #endif diff --git a/arch/x86/um/shared/sysdep/ptrace_user.h b/arch/x86/um/shared/sysdep/ptrace_user.h index 1d1a824fa6528..98da231205386 100644 --- a/arch/x86/um/shared/sysdep/ptrace_user.h +++ b/arch/x86/um/shared/sysdep/ptrace_user.h @@ -11,12 +11,6 @@ #define REGS_IP_INDEX HOST_IP #define REGS_SP_INDEX HOST_SP -#ifdef __i386__ -#define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE) -#else -#define FP_SIZE HOST_FP_SIZE -#endif - /* * glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum, * ensure we have a definition by (re-)defining it here. diff --git a/arch/x86/um/shared/sysdep/stub_32.h b/arch/x86/um/shared/sysdep/stub_32.h index 0b44a86dd346e..390988132c0a7 100644 --- a/arch/x86/um/shared/sysdep/stub_32.h +++ b/arch/x86/um/shared/sysdep/stub_32.h @@ -112,11 +112,23 @@ static __always_inline void *get_stub_data(void) unsigned long ret; asm volatile ( - "movl %%esp,%0 ;" - "andl %1,%0" + "call _here_%=;" + "_here_%=:" + "popl %0;" + "andl %1, %0 ;" + "addl %2, %0 ;" : "=a" (ret) - : "g" (~(STUB_DATA_PAGES * UM_KERN_PAGE_SIZE - 1))); + : "g" (~(UM_KERN_PAGE_SIZE - 1)), + "g" (UM_KERN_PAGE_SIZE)); return (void *)ret; } + +#define stub_start(fn) \ + asm volatile ( \ + "subl %0,%%esp ;" \ + "movl %1, %%eax ; " \ + "call *%%eax ;" \ + :: "i" ((1 + STUB_DATA_PAGES) * UM_KERN_PAGE_SIZE), \ + "i" (&fn)) #endif diff --git a/arch/x86/um/shared/sysdep/stub_64.h b/arch/x86/um/shared/sysdep/stub_64.h index 67f44284f1aa4..294affbec7429 100644 --- a/arch/x86/um/shared/sysdep/stub_64.h +++ b/arch/x86/um/shared/sysdep/stub_64.h @@ -28,6 +28,17 @@ static __always_inline long stub_syscall0(long syscall) return ret; } +static __always_inline long stub_syscall1(long syscall, long arg1) +{ + long ret; + + __asm__ volatile (__syscall + : "=a" (ret) + : "0" (syscall), "D" (arg1) : __syscall_clobber ); + + return ret; +} + static __always_inline long stub_syscall2(long syscall, long arg1, long arg2) { long ret; @@ -106,11 +117,21 @@ static __always_inline void *get_stub_data(void) unsigned long ret; asm volatile ( - "movq %%rsp,%0 ;" - "andq %1,%0" + "lea 0(%%rip), %0;" + "andq %1, %0 ;" + "addq %2, %0 ;" : "=a" (ret) - : "g" (~(STUB_DATA_PAGES * UM_KERN_PAGE_SIZE - 1))); + : "g" (~(UM_KERN_PAGE_SIZE - 1)), + "g" (UM_KERN_PAGE_SIZE)); return (void *)ret; } + +#define stub_start(fn) \ + asm volatile ( \ + "subq %0,%%rsp ;" \ + "movq %1,%%rax ;" \ + "call *%%rax ;" \ + :: "i" ((1 + STUB_DATA_PAGES) * UM_KERN_PAGE_SIZE), \ + "i" (&fn)) #endif diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c index 2cc8c23090228..75087e85b6fdb 100644 --- a/arch/x86/um/signal.c +++ b/arch/x86/um/signal.c @@ -16,145 +16,24 @@ #include #include -#ifdef CONFIG_X86_32 - -/* - * FPU tag word conversions. - */ - -static inline unsigned short twd_i387_to_fxsr(unsigned short twd) -{ - unsigned int tmp; /* to avoid 16 bit prefixes in the code */ - - /* Transform each pair of bits into 01 (valid) or 00 (empty) */ - tmp = ~twd; - tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ - /* and move the valid bits to the lower byte. */ - tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ - tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ - tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ - return tmp; -} - -static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave) -{ - struct _fpxreg *st = NULL; - unsigned long twd = (unsigned long) fxsave->twd; - unsigned long tag; - unsigned long ret = 0xffff0000; - int i; - -#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16) - - for (i = 0; i < 8; i++) { - if (twd & 0x1) { - st = (struct _fpxreg *) FPREG_ADDR(fxsave, i); - - switch (st->exponent & 0x7fff) { - case 0x7fff: - tag = 2; /* Special */ - break; - case 0x0000: - if ( !st->significand[0] && - !st->significand[1] && - !st->significand[2] && - !st->significand[3] ) { - tag = 1; /* Zero */ - } else { - tag = 2; /* Special */ - } - break; - default: - if (st->significand[3] & 0x8000) { - tag = 0; /* Valid */ - } else { - tag = 2; /* Special */ - } - break; - } - } else { - tag = 3; /* Empty */ - } - ret |= (tag << (2 * i)); - twd = twd >> 1; - } - return ret; -} - -static int convert_fxsr_to_user(struct _fpstate __user *buf, - struct user_fxsr_struct *fxsave) -{ - unsigned long env[7]; - struct _fpreg __user *to; - struct _fpxreg *from; - int i; - - env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul; - env[1] = (unsigned long)fxsave->swd | 0xffff0000ul; - env[2] = twd_fxsr_to_i387(fxsave); - env[3] = fxsave->fip; - env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16); - env[5] = fxsave->foo; - env[6] = fxsave->fos; - - if (__copy_to_user(buf, env, 7 * sizeof(unsigned long))) - return 1; - - to = &buf->_st[0]; - from = (struct _fpxreg *) &fxsave->st_space[0]; - for (i = 0; i < 8; i++, to++, from++) { - unsigned long __user *t = (unsigned long __user *)to; - unsigned long *f = (unsigned long *)from; - - if (__put_user(*f, t) || - __put_user(*(f + 1), t + 1) || - __put_user(from->exponent, &to->exponent)) - return 1; - } - return 0; -} - -static int convert_fxsr_from_user(struct user_fxsr_struct *fxsave, - struct _fpstate __user *buf) -{ - unsigned long env[7]; - struct _fpxreg *to; - struct _fpreg __user *from; - int i; - - if (copy_from_user( env, buf, 7 * sizeof(long))) - return 1; - - fxsave->cwd = (unsigned short)(env[0] & 0xffff); - fxsave->swd = (unsigned short)(env[1] & 0xffff); - fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); - fxsave->fip = env[3]; - fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16); - fxsave->fcs = (env[4] & 0xffff); - fxsave->foo = env[5]; - fxsave->fos = env[6]; - - to = (struct _fpxreg *) &fxsave->st_space[0]; - from = &buf->_st[0]; - for (i = 0; i < 8; i++, to++, from++) { - unsigned long *t = (unsigned long *)to; - unsigned long __user *f = (unsigned long __user *)from; - - if (__get_user(*t, f) || - __get_user(*(t + 1), f + 1) || - __get_user(to->exponent, &from->exponent)) - return 1; - } - return 0; -} - -extern int have_fpx_regs; +#include +#include +#ifdef CONFIG_X86_32 +struct _xstate_64 { + struct _fpstate_64 fpstate; + struct _header xstate_hdr; + struct _ymmh_state ymmh; + /* New processor state extensions go here: */ +}; +#else +#define _xstate_64 _xstate #endif static int copy_sc_from_user(struct pt_regs *regs, struct sigcontext __user *from) { + struct _xstate_64 __user *from_fp64; struct sigcontext sc; int err; @@ -203,35 +82,27 @@ static int copy_sc_from_user(struct pt_regs *regs, #undef GETREG #ifdef CONFIG_X86_32 - if (have_fpx_regs) { - struct user_fxsr_struct fpx; - int pid = userspace_pid[current_thread_info()->cpu]; + from_fp64 = ((void __user *)sc.fpstate) + + offsetof(struct _fpstate_32, _fxsr_env); +#else + from_fp64 = (void __user *)sc.fpstate; +#endif - err = copy_from_user(&fpx, - &((struct _fpstate __user *)sc.fpstate)->_fxsr_env[0], - sizeof(struct user_fxsr_struct)); - if (err) - return 1; + err = copy_from_user(regs->regs.fp, from_fp64, host_fp_size); + if (err) + return 1; - err = convert_fxsr_from_user(&fpx, (void *)sc.fpstate); - if (err) - return 1; - - err = restore_fpx_registers(pid, (unsigned long *) &fpx); - if (err < 0) { - printk(KERN_ERR "copy_sc_from_user - " - "restore_fpx_registers failed, errno = %d\n", - -err); - return 1; - } - } else +#ifdef CONFIG_X86_32 + /* Data is duplicated and this copy is the important one */ + err = copy_regset_from_user(current, + task_user_regset_view(current), + REGSET_FP_LEGACY, 0, + sizeof(struct user_i387_struct), + (void __user *)sc.fpstate); + if (err < 0) + return err; #endif - { - err = copy_from_user(regs->regs.fp, (void *)sc.fpstate, - sizeof(struct _xstate)); - if (err) - return 1; - } + return 0; } @@ -239,6 +110,7 @@ static int copy_sc_to_user(struct sigcontext __user *to, struct _xstate __user *to_fp, struct pt_regs *regs, unsigned long mask) { + struct _xstate_64 __user *to_fp64; struct sigcontext sc; struct faultinfo * fi = ¤t->thread.arch.faultinfo; int err; @@ -290,35 +162,46 @@ static int copy_sc_to_user(struct sigcontext __user *to, return 1; #ifdef CONFIG_X86_32 - if (have_fpx_regs) { - int pid = userspace_pid[current_thread_info()->cpu]; - struct user_fxsr_struct fpx; - - err = save_fpx_registers(pid, (unsigned long *) &fpx); - if (err < 0){ - printk(KERN_ERR "copy_sc_to_user - save_fpx_registers " - "failed, errno = %d\n", err); - return 1; - } - - err = convert_fxsr_to_user(&to_fp->fpstate, &fpx); - if (err) - return 1; + err = copy_regset_to_user(current, + task_user_regset_view(current), + REGSET_FP_LEGACY, 0, + sizeof(struct _fpstate_32), to_fp); + if (err < 0) + return err; - err |= __put_user(fpx.swd, &to_fp->fpstate.status); - err |= __put_user(X86_FXSR_MAGIC, &to_fp->fpstate.magic); - if (err) - return 1; + __put_user(X86_FXSR_MAGIC, &to_fp->fpstate.magic); + + BUILD_BUG_ON(offsetof(struct _xstate, xstate_hdr) != + offsetof(struct _xstate_64, xstate_hdr) + + offsetof(struct _fpstate_32, _fxsr_env)); + to_fp64 = (void __user *)to_fp + + offsetof(struct _fpstate_32, _fxsr_env); +#else + to_fp64 = to_fp; +#endif /* CONFIG_X86_32 */ + + if (copy_to_user(to_fp64, regs->regs.fp, host_fp_size)) + return 1; - if (copy_to_user(&to_fp->fpstate._fxsr_env[0], &fpx, - sizeof(struct user_fxsr_struct))) - return 1; - } else + /* + * Put magic/size values for userspace. We do not bother to verify them + * later on, however, userspace needs them should it try to read the + * XSTATE data. And ptrace does not fill in these parts. + */ + BUILD_BUG_ON(sizeof(int) != FP_XSTATE_MAGIC2_SIZE); +#ifdef CONFIG_X86_32 + __put_user(offsetof(struct _fpstate_32, _fxsr_env) + + host_fp_size + FP_XSTATE_MAGIC2_SIZE, + &to_fp64->fpstate.sw_reserved.extended_size); +#else + __put_user(host_fp_size + FP_XSTATE_MAGIC2_SIZE, + &to_fp64->fpstate.sw_reserved.extended_size); #endif - { - if (copy_to_user(to_fp, regs->regs.fp, sizeof(struct _xstate))) - return 1; - } + __put_user(host_fp_size, &to_fp64->fpstate.sw_reserved.xstate_size); + + __put_user(FP_XSTATE_MAGIC1, &to_fp64->fpstate.sw_reserved.magic1); + __put_user(FP_XSTATE_MAGIC2, + (int __user *)((void __user *)to_fp64 + host_fp_size)); return 0; } @@ -336,34 +219,15 @@ static int copy_ucontext_to_user(struct ucontext __user *uc, return err; } -struct sigframe -{ - char __user *pretcode; - int sig; - struct sigcontext sc; - struct _xstate fpstate; - unsigned long extramask[_NSIG_WORDS-1]; - char retcode[8]; -}; - -struct rt_sigframe -{ - char __user *pretcode; - int sig; - struct siginfo __user *pinfo; - void __user *puc; - struct siginfo info; - struct ucontext uc; - struct _xstate fpstate; - char retcode[8]; -}; - int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig, struct pt_regs *regs, sigset_t *mask) { + size_t math_size = offsetof(struct _fpstate_32, _fxsr_env) + + host_fp_size + FP_XSTATE_MAGIC2_SIZE; struct sigframe __user *frame; void __user *restorer; int err = 0, sig = ksig->sig; + unsigned long fp_to; /* This is the same calculation as i386 - ((sp + 4) & 15) == 0 */ stack_top = ((stack_top + 4) & -16UL) - 4; @@ -371,13 +235,21 @@ int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig, if (!access_ok(frame, sizeof(*frame))) return 1; + /* Add required space for math frame */ + frame = (struct sigframe __user *)((unsigned long)frame - math_size); + restorer = frame->retcode; if (ksig->ka.sa.sa_flags & SA_RESTORER) restorer = ksig->ka.sa.sa_restorer; - err |= __put_user(restorer, &frame->pretcode); + err |= __put_user(restorer, (void __user * __user *)&frame->pretcode); err |= __put_user(sig, &frame->sig); - err |= copy_sc_to_user(&frame->sc, &frame->fpstate, regs, mask->sig[0]); + + fp_to = (unsigned long)frame + sizeof(*frame); + + err |= copy_sc_to_user(&frame->sc, + (struct _xstate __user *)fp_to, + regs, mask->sig[0]); if (_NSIG_WORDS > 1) err |= __copy_to_user(&frame->extramask, &mask->sig[1], sizeof(frame->extramask)); @@ -407,26 +279,35 @@ int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig, int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, struct pt_regs *regs, sigset_t *mask) { + size_t math_size = offsetof(struct _fpstate_32, _fxsr_env) + + host_fp_size + FP_XSTATE_MAGIC2_SIZE; struct rt_sigframe __user *frame; void __user *restorer; int err = 0, sig = ksig->sig; + unsigned long fp_to; stack_top &= -8UL; frame = (struct rt_sigframe __user *) stack_top - 1; if (!access_ok(frame, sizeof(*frame))) return 1; + /* Add required space for math frame */ + frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size); + restorer = frame->retcode; if (ksig->ka.sa.sa_flags & SA_RESTORER) restorer = ksig->ka.sa.sa_restorer; - err |= __put_user(restorer, &frame->pretcode); + err |= __put_user(restorer, (void __user * __user *)&frame->pretcode); err |= __put_user(sig, &frame->sig); - err |= __put_user(&frame->info, &frame->pinfo); - err |= __put_user(&frame->uc, &frame->puc); + err |= __put_user(&frame->info, (void __user * __user *)&frame->pinfo); + err |= __put_user(&frame->uc, (void __user * __user *)&frame->puc); err |= copy_siginfo_to_user(&frame->info, &ksig->info); - err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask, - PT_REGS_SP(regs)); + + fp_to = (unsigned long)frame + sizeof(*frame); + + err |= copy_ucontext_to_user(&frame->uc, (struct _xstate __user *)fp_to, + mask, PT_REGS_SP(regs)); /* * This is movl $,%eax ; int $0x80 @@ -478,27 +359,24 @@ SYSCALL_DEFINE0(sigreturn) #else -struct rt_sigframe -{ - char __user *pretcode; - struct ucontext uc; - struct siginfo info; - struct _xstate fpstate; -}; - int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, struct pt_regs *regs, sigset_t *set) { + unsigned long math_size = host_fp_size + FP_XSTATE_MAGIC2_SIZE; struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; unsigned long fp_to; frame = (struct rt_sigframe __user *) round_down(stack_top - sizeof(struct rt_sigframe), 16); + + /* Add required space for math frame */ + frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size); + /* Subtract 128 for a red zone and 8 for proper alignment */ frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8); - if (!access_ok(frame, sizeof(*frame))) + if (!access_ok(frame, sizeof(*frame) + math_size)) goto out; if (ksig->ka.sa.sa_flags & SA_SIGINFO) { @@ -509,12 +387,14 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(NULL, &frame->uc.uc_link); err |= __save_altstack(&frame->uc.uc_stack, PT_REGS_SP(regs)); - err |= copy_sc_to_user(&frame->uc.uc_mcontext, &frame->fpstate, regs, - set->sig[0]); - fp_to = (unsigned long)&frame->fpstate; + fp_to = (unsigned long)frame + sizeof(*frame); + + err |= copy_sc_to_user(&frame->uc.uc_mcontext, + (struct _xstate __user *)fp_to, + regs, set->sig[0]); err |= __put_user(fp_to, &frame->uc.uc_mcontext.fpstate); if (sizeof(*set) == 16) { @@ -531,7 +411,7 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, */ /* x86-64 should always use SA_RESTORER. */ if (ksig->ka.sa.sa_flags & SA_RESTORER) - err |= __put_user((void *)ksig->ka.sa.sa_restorer, + err |= __put_user((void __user *)ksig->ka.sa.sa_restorer, &frame->pretcode); else /* could use a vstub here */ diff --git a/arch/x86/um/user-offsets.c b/arch/x86/um/user-offsets.c index 1c77d99461998..d6e1cd9956bf9 100644 --- a/arch/x86/um/user-offsets.c +++ b/arch/x86/um/user-offsets.c @@ -20,9 +20,6 @@ void foo(void); void foo(void) { #ifdef __i386__ - DEFINE_LONGS(HOST_FP_SIZE, sizeof(struct user_fpregs_struct)); - DEFINE_LONGS(HOST_FPX_SIZE, sizeof(struct user_fpxregs_struct)); - DEFINE(HOST_IP, EIP); DEFINE(HOST_SP, UESP); DEFINE(HOST_EFLAGS, EFL); @@ -41,11 +38,6 @@ void foo(void) DEFINE(HOST_GS, GS); DEFINE(HOST_ORIG_AX, ORIG_EAX); #else -#ifdef FP_XSTATE_MAGIC1 - DEFINE_LONGS(HOST_FP_SIZE, 2696); -#else - DEFINE(HOST_FP_SIZE, sizeof(struct _fpstate) / sizeof(unsigned long)); -#endif DEFINE_LONGS(HOST_BX, RBX); DEFINE_LONGS(HOST_CX, RCX); DEFINE_LONGS(HOST_DI, RDI); diff --git a/arch/x86/um/vdso/Makefile b/arch/x86/um/vdso/Makefile index 6a77ea6434ffd..7478d11dacb71 100644 --- a/arch/x86/um/vdso/Makefile +++ b/arch/x86/um/vdso/Makefile @@ -56,7 +56,6 @@ CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage quiet_cmd_vdso = VDSO $@ cmd_vdso = $(CC) -nostdlib -o $@ \ $(CC_FLAGS_LTO) $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \ - -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) && \ - sh $(src)/checkundef.sh '$(NM)' '$@' + -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) -VDSO_LDFLAGS = -fPIC -shared -Wl,--hash-style=sysv -z noexecstack +VDSO_LDFLAGS = -fPIC -shared -Wl,--hash-style=sysv -z noexecstack -Wl,--no-undefined diff --git a/arch/x86/um/vdso/checkundef.sh b/arch/x86/um/vdso/checkundef.sh deleted file mode 100644 index 8e3ea6bb956fc..0000000000000 --- a/arch/x86/um/vdso/checkundef.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0 -nm="$1" -file="$2" -$nm "$file" | grep '^ *U' > /dev/null 2>&1 -if [ $? -eq 1 ]; then - exit 0 -else - echo "$file: undefined symbols found" >&2 - exit 1 -fi diff --git a/arch/xtensa/include/asm/spinlock_types.h b/arch/xtensa/include/asm/spinlock_types.h index 797aed7df3dd8..6baaeac6248b8 100644 --- a/arch/xtensa/include/asm/spinlock_types.h +++ b/arch/xtensa/include/asm/spinlock_types.h @@ -3,7 +3,7 @@ #define __ASM_SPINLOCK_TYPES_H #if !defined(__LINUX_SPINLOCK_TYPES_RAW_H) && !defined(__ASM_SPINLOCK_H) -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif #include diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c index e831aedb46432..9fb9f35331502 100644 --- a/block/bfq-cgroup.c +++ b/block/bfq-cgroup.c @@ -736,6 +736,7 @@ static void bfq_sync_bfqq_move(struct bfq_data *bfqd, */ bfq_put_cooperator(sync_bfqq); bic_set_bfqq(bic, NULL, true, act_idx); + bfq_release_process_ref(bfqd, sync_bfqq); } } diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 0747d9d0e48c8..95dd7b7959356 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -582,23 +582,31 @@ static struct request *bfq_choose_req(struct bfq_data *bfqd, #define BFQ_LIMIT_INLINE_DEPTH 16 #ifdef CONFIG_BFQ_GROUP_IOSCHED -static bool bfqq_request_over_limit(struct bfq_queue *bfqq, int limit) +static bool bfqq_request_over_limit(struct bfq_data *bfqd, + struct bfq_io_cq *bic, blk_opf_t opf, + unsigned int act_idx, int limit) { - struct bfq_data *bfqd = bfqq->bfqd; - struct bfq_entity *entity = &bfqq->entity; struct bfq_entity *inline_entities[BFQ_LIMIT_INLINE_DEPTH]; struct bfq_entity **entities = inline_entities; - int depth, level, alloc_depth = BFQ_LIMIT_INLINE_DEPTH; - int class_idx = bfqq->ioprio_class - 1; + int alloc_depth = BFQ_LIMIT_INLINE_DEPTH; struct bfq_sched_data *sched_data; + struct bfq_entity *entity; + struct bfq_queue *bfqq; unsigned long wsum; bool ret = false; - - if (!entity->on_st_or_in_serv) - return false; + int depth; + int level; retry: spin_lock_irq(&bfqd->lock); + bfqq = bic_to_bfqq(bic, op_is_sync(opf), act_idx); + if (!bfqq) + goto out; + + entity = &bfqq->entity; + if (!entity->on_st_or_in_serv) + goto out; + /* +1 for bfqq entity, root cgroup not included */ depth = bfqg_to_blkg(bfqq_group(bfqq))->blkcg->css.cgroup->level + 1; if (depth > alloc_depth) { @@ -643,7 +651,7 @@ static bool bfqq_request_over_limit(struct bfq_queue *bfqq, int limit) * class. */ wsum = 0; - for (i = 0; i <= class_idx; i++) { + for (i = 0; i <= bfqq->ioprio_class - 1; i++) { wsum = wsum * IOPRIO_BE_NR + sched_data->service_tree[i].wsum; } @@ -666,7 +674,9 @@ static bool bfqq_request_over_limit(struct bfq_queue *bfqq, int limit) return ret; } #else -static bool bfqq_request_over_limit(struct bfq_queue *bfqq, int limit) +static bool bfqq_request_over_limit(struct bfq_data *bfqd, + struct bfq_io_cq *bic, blk_opf_t opf, + unsigned int act_idx, int limit) { return false; } @@ -704,8 +714,9 @@ static void bfq_limit_depth(blk_opf_t opf, struct blk_mq_alloc_data *data) } for (act_idx = 0; bic && act_idx < bfqd->num_actuators; act_idx++) { - struct bfq_queue *bfqq = - bic_to_bfqq(bic, op_is_sync(opf), act_idx); + /* Fast path to check if bfqq is already allocated. */ + if (!bic_to_bfqq(bic, op_is_sync(opf), act_idx)) + continue; /* * Does queue (or any parent entity) exceed number of @@ -713,7 +724,7 @@ static void bfq_limit_depth(blk_opf_t opf, struct blk_mq_alloc_data *data) * limit depth so that it cannot consume more * available requests and thus starve other entities. */ - if (bfqq && bfqq_request_over_limit(bfqq, limit)) { + if (bfqq_request_over_limit(bfqd, bic, opf, act_idx, limit)) { depth = 1; break; } @@ -5434,8 +5445,6 @@ void bfq_put_cooperator(struct bfq_queue *bfqq) bfq_put_queue(__bfqq); __bfqq = next; } - - bfq_release_process_ref(bfqq->bfqd, bfqq); } static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) @@ -5448,6 +5457,8 @@ static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, bfqq->ref); bfq_put_cooperator(bfqq); + + bfq_release_process_ref(bfqd, bfqq); } static void bfq_exit_icq_bfqq(struct bfq_io_cq *bic, bool is_sync, @@ -6734,6 +6745,8 @@ bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq) bic_set_bfqq(bic, NULL, true, bfqq->actuator_idx); bfq_put_cooperator(bfqq); + + bfq_release_process_ref(bfqq->bfqd, bfqq); return NULL; } diff --git a/block/blk-merge.c b/block/blk-merge.c index e0b28e9298c9f..e01383c6e534b 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -864,17 +864,10 @@ static struct request *attempt_merge(struct request_queue *q, if (req_op(req) != req_op(next)) return NULL; - if (rq_data_dir(req) != rq_data_dir(next)) + if (req->bio->bi_write_hint != next->bio->bi_write_hint) + return NULL; + if (req->bio->bi_ioprio != next->bio->bi_ioprio) return NULL; - - if (req->bio && next->bio) { - /* Don't merge requests with different write hints. */ - if (req->bio->bi_write_hint != next->bio->bi_write_hint) - return NULL; - if (req->bio->bi_ioprio != next->bio->bi_ioprio) - return NULL; - } - if (!blk_atomic_write_mergeable_rqs(req, next)) return NULL; @@ -986,30 +979,16 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio) if (req_op(rq) != bio_op(bio)) return false; - /* different data direction or already started, don't merge */ - if (bio_data_dir(bio) != rq_data_dir(rq)) - return false; - - /* don't merge across cgroup boundaries */ if (!blk_cgroup_mergeable(rq, bio)) return false; - - /* only merge integrity protected bio into ditto rq */ if (blk_integrity_merge_bio(rq->q, rq, bio) == false) return false; - - /* Only merge if the crypt contexts are compatible */ if (!bio_crypt_rq_ctx_compatible(rq, bio)) return false; - - if (rq->bio) { - /* Don't merge requests with different write hints. */ - if (rq->bio->bi_write_hint != bio->bi_write_hint) - return false; - if (rq->bio->bi_ioprio != bio->bi_ioprio) - return false; - } - + if (rq->bio->bi_write_hint != bio->bi_write_hint) + return false; + if (rq->bio->bi_ioprio != bio->bi_ioprio) + return false; if (blk_atomic_write_mergeable_rq_bio(rq, bio) == false) return false; diff --git a/block/blk-mq.c b/block/blk-mq.c index 270cfd9fc6b0c..424239c075e28 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -388,7 +388,6 @@ void blk_rq_init(struct request_queue *q, struct request *rq) rq->tag = BLK_MQ_NO_TAG; rq->internal_tag = BLK_MQ_NO_TAG; rq->start_time_ns = blk_time_get_ns(); - rq->part = NULL; blk_crypto_rq_set_defaults(rq); } EXPORT_SYMBOL(blk_rq_init); @@ -3273,19 +3272,21 @@ int blk_rq_prep_clone(struct request *rq, struct request *rq_src, int (*bio_ctr)(struct bio *, struct bio *, void *), void *data) { - struct bio *bio, *bio_src; + struct bio *bio_src; if (!bs) bs = &fs_bio_set; __rq_for_each_bio(bio_src, rq_src) { - bio = bio_alloc_clone(rq->q->disk->part0, bio_src, gfp_mask, - bs); + struct bio *bio = bio_alloc_clone(rq->q->disk->part0, bio_src, + gfp_mask, bs); if (!bio) goto free_and_out; - if (bio_ctr && bio_ctr(bio, bio_src, data)) + if (bio_ctr && bio_ctr(bio, bio_src, data)) { + bio_put(bio); goto free_and_out; + } if (rq->bio) { rq->biotail->bi_next = bio; @@ -3293,7 +3294,6 @@ int blk_rq_prep_clone(struct request *rq, struct request *rq_src, } else { rq->bio = rq->biotail = bio; } - bio = NULL; } /* Copy attributes of the original request to the clone request. */ @@ -3311,8 +3311,6 @@ int blk_rq_prep_clone(struct request *rq, struct request *rq_src, return 0; free_and_out: - if (bio) - bio_put(bio); blk_rq_unprep_clone(rq); return -ENOMEM; diff --git a/block/blk-settings.c b/block/blk-settings.c index f1d4dfdc37a71..8f09e33f41f68 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -178,9 +178,26 @@ static void blk_validate_atomic_write_limits(struct queue_limits *lim) if (!lim->atomic_write_hw_max) goto unsupported; + if (WARN_ON_ONCE(!is_power_of_2(lim->atomic_write_hw_unit_min))) + goto unsupported; + + if (WARN_ON_ONCE(!is_power_of_2(lim->atomic_write_hw_unit_max))) + goto unsupported; + + if (WARN_ON_ONCE(lim->atomic_write_hw_unit_min > + lim->atomic_write_hw_unit_max)) + goto unsupported; + + if (WARN_ON_ONCE(lim->atomic_write_hw_unit_max > + lim->atomic_write_hw_max)) + goto unsupported; + boundary_sectors = lim->atomic_write_hw_boundary >> SECTOR_SHIFT; if (boundary_sectors) { + if (WARN_ON_ONCE(lim->atomic_write_hw_max > + lim->atomic_write_hw_boundary)) + goto unsupported; /* * A feature of boundary support is that it disallows bios to * be merged which would result in a merged request which @@ -248,6 +265,13 @@ int blk_validate_limits(struct queue_limits *lim) if (lim->io_min < lim->physical_block_size) lim->io_min = lim->physical_block_size; + /* + * The optimal I/O size may not be aligned to physical block size + * (because it may be limited by dma engines which have no clue about + * block size of the disks attached to them), so we round it down here. + */ + lim->io_opt = round_down(lim->io_opt, lim->physical_block_size); + /* * max_hw_sectors has a somewhat weird default for historical reason, * but driver really should set their own instead of relying on this @@ -458,8 +482,6 @@ static unsigned int queue_limit_discard_alignment( /* Why are these in bytes, not sectors? */ alignment = lim->discard_alignment >> SECTOR_SHIFT; granularity = lim->discard_granularity >> SECTOR_SHIFT; - if (!granularity) - return 0; /* Offset of the partition start in 'granularity' sectors */ offset = sector_div(sector, granularity); @@ -479,6 +501,119 @@ static unsigned int blk_round_down_sectors(unsigned int sectors, unsigned int lb return sectors; } +/* Check if second and later bottom devices are compliant */ +static bool blk_stack_atomic_writes_tail(struct queue_limits *t, + struct queue_limits *b) +{ + /* We're not going to support different boundary sizes.. yet */ + if (t->atomic_write_hw_boundary != b->atomic_write_hw_boundary) + return false; + + /* Can't support this */ + if (t->atomic_write_hw_unit_min > b->atomic_write_hw_unit_max) + return false; + + /* Or this */ + if (t->atomic_write_hw_unit_max < b->atomic_write_hw_unit_min) + return false; + + t->atomic_write_hw_max = min(t->atomic_write_hw_max, + b->atomic_write_hw_max); + t->atomic_write_hw_unit_min = max(t->atomic_write_hw_unit_min, + b->atomic_write_hw_unit_min); + t->atomic_write_hw_unit_max = min(t->atomic_write_hw_unit_max, + b->atomic_write_hw_unit_max); + return true; +} + +/* Check for valid boundary of first bottom device */ +static bool blk_stack_atomic_writes_boundary_head(struct queue_limits *t, + struct queue_limits *b) +{ + /* + * Ensure atomic write boundary is aligned with chunk sectors. Stacked + * devices store chunk sectors in t->io_min. + */ + if (b->atomic_write_hw_boundary > t->io_min && + b->atomic_write_hw_boundary % t->io_min) + return false; + if (t->io_min > b->atomic_write_hw_boundary && + t->io_min % b->atomic_write_hw_boundary) + return false; + + t->atomic_write_hw_boundary = b->atomic_write_hw_boundary; + return true; +} + + +/* Check stacking of first bottom device */ +static bool blk_stack_atomic_writes_head(struct queue_limits *t, + struct queue_limits *b) +{ + if (b->atomic_write_hw_boundary && + !blk_stack_atomic_writes_boundary_head(t, b)) + return false; + + if (t->io_min <= SECTOR_SIZE) { + /* No chunk sectors, so use bottom device values directly */ + t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max; + t->atomic_write_hw_unit_min = b->atomic_write_hw_unit_min; + t->atomic_write_hw_max = b->atomic_write_hw_max; + return true; + } + + /* + * Find values for limits which work for chunk size. + * b->atomic_write_hw_unit_{min, max} may not be aligned with chunk + * size (t->io_min), as chunk size is not restricted to a power-of-2. + * So we need to find highest power-of-2 which works for the chunk + * size. + * As an example scenario, we could have b->unit_max = 16K and + * t->io_min = 24K. For this case, reduce t->unit_max to a value + * aligned with both limits, i.e. 8K in this example. + */ + t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max; + while (t->io_min % t->atomic_write_hw_unit_max) + t->atomic_write_hw_unit_max /= 2; + + t->atomic_write_hw_unit_min = min(b->atomic_write_hw_unit_min, + t->atomic_write_hw_unit_max); + t->atomic_write_hw_max = min(b->atomic_write_hw_max, t->io_min); + + return true; +} + +static void blk_stack_atomic_writes_limits(struct queue_limits *t, + struct queue_limits *b) +{ + if (!(t->features & BLK_FEAT_ATOMIC_WRITES_STACKED)) + goto unsupported; + + if (!b->atomic_write_unit_min) + goto unsupported; + + /* + * If atomic_write_hw_max is set, we have already stacked 1x bottom + * device, so check for compliance. + */ + if (t->atomic_write_hw_max) { + if (!blk_stack_atomic_writes_tail(t, b)) + goto unsupported; + return; + } + + if (!blk_stack_atomic_writes_head(t, b)) + goto unsupported; + return; + +unsupported: + t->atomic_write_hw_max = 0; + t->atomic_write_hw_unit_max = 0; + t->atomic_write_hw_unit_min = 0; + t->atomic_write_hw_boundary = 0; + t->features &= ~BLK_FEAT_ATOMIC_WRITES_STACKED; +} + /** * blk_stack_limits - adjust queue_limits for stacked devices * @t: the stacking driver limits (top device) @@ -639,6 +774,8 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, t->zone_write_granularity = 0; t->max_zone_append_sectors = 0; } + blk_stack_atomic_writes_limits(t, b); + return ret; } EXPORT_SYMBOL(blk_stack_limits); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index d80a202cd1706..4241aea84161c 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -810,10 +810,8 @@ int blk_register_queue(struct gendisk *disk) * faster to shut down and is made fully functional here as * request_queues for non-existent devices never get registered. */ - if (!blk_queue_init_done(q)) { - blk_queue_flag_set(QUEUE_FLAG_INIT_DONE, q); - percpu_ref_switch_to_percpu(&q->q_usage_counter); - } + blk_queue_flag_set(QUEUE_FLAG_INIT_DONE, q); + percpu_ref_switch_to_percpu(&q->q_usage_counter); return ret; diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 70211751df16e..263e28b720538 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -1551,6 +1551,7 @@ static int disk_update_zone_resources(struct gendisk *disk, unsigned int nr_seq_zones, nr_conv_zones; unsigned int pool_size; struct queue_limits lim; + int ret; disk->nr_zones = args->nr_zones; disk->zone_capacity = args->zone_capacity; @@ -1601,7 +1602,11 @@ static int disk_update_zone_resources(struct gendisk *disk, } commit: - return queue_limits_commit_update(q, &lim); + blk_mq_freeze_queue(q); + ret = queue_limits_commit_update(q, &lim); + blk_mq_unfreeze_queue(q); + + return ret; } static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx, @@ -1816,14 +1821,15 @@ int blk_revalidate_disk_zones(struct gendisk *disk) * Set the new disk zone parameters only once the queue is frozen and * all I/Os are completed. */ - blk_mq_freeze_queue(q); if (ret > 0) ret = disk_update_zone_resources(disk, &args); else pr_warn("%s: failed to revalidate zones\n", disk->disk_name); - if (ret) + if (ret) { + blk_mq_freeze_queue(q); disk_free_zone_resources(disk); - blk_mq_unfreeze_queue(q); + blk_mq_unfreeze_queue(q); + } return ret; } diff --git a/block/fops.c b/block/fops.c index 2d01c90076813..13a67940d0408 100644 --- a/block/fops.c +++ b/block/fops.c @@ -677,6 +677,7 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *bd_inode = bdev_file_inode(file); struct block_device *bdev = I_BDEV(bd_inode); + bool atomic = iocb->ki_flags & IOCB_ATOMIC; loff_t size = bdev_nr_bytes(bdev); size_t shorted = 0; ssize_t ret; @@ -696,7 +697,7 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) if ((iocb->ki_flags & (IOCB_NOWAIT | IOCB_DIRECT)) == IOCB_NOWAIT) return -EOPNOTSUPP; - if (iocb->ki_flags & IOCB_ATOMIC) { + if (atomic) { ret = generic_atomic_write_valid(iocb, from); if (ret) return ret; @@ -704,6 +705,8 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) size -= iocb->ki_pos; if (iov_iter_count(from) > size) { + if (atomic) + return -EINVAL; shorted = iov_iter_count(from) - size; iov_iter_truncate(from, size); } diff --git a/block/genhd.c b/block/genhd.c index 9130e163e1913..79230c109fca0 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -742,13 +742,10 @@ void del_gendisk(struct gendisk *disk) * If the disk does not own the queue, allow using passthrough requests * again. Else leave the queue frozen to fail all I/O. */ - if (!test_bit(GD_OWNS_QUEUE, &disk->state)) { - blk_queue_flag_clear(QUEUE_FLAG_INIT_DONE, q); + if (!test_bit(GD_OWNS_QUEUE, &disk->state)) __blk_mq_unfreeze_queue(q, true); - } else { - if (queue_is_mq(q)) - blk_mq_exit_queue(q); - } + else if (queue_is_mq(q)) + blk_mq_exit_queue(q); if (start_drain) blk_unfreeze_release_lock(q, true, queue_dying); diff --git a/block/mq-deadline.c b/block/mq-deadline.c index acdc28756d9d7..91b3789f710e7 100644 --- a/block/mq-deadline.c +++ b/block/mq-deadline.c @@ -685,10 +685,9 @@ static void dd_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, prio = ioprio_class_to_prio[ioprio_class]; per_prio = &dd->per_prio[prio]; - if (!rq->elv.priv[0]) { + if (!rq->elv.priv[0]) per_prio->stats.inserted++; - rq->elv.priv[0] = (void *)(uintptr_t)1; - } + rq->elv.priv[0] = per_prio; if (blk_mq_sched_try_insert_merge(q, rq, free)) return; @@ -753,18 +752,14 @@ static void dd_prepare_request(struct request *rq) */ static void dd_finish_request(struct request *rq) { - struct request_queue *q = rq->q; - struct deadline_data *dd = q->elevator->elevator_data; - const u8 ioprio_class = dd_rq_ioclass(rq); - const enum dd_prio prio = ioprio_class_to_prio[ioprio_class]; - struct dd_per_prio *per_prio = &dd->per_prio[prio]; + struct dd_per_prio *per_prio = rq->elv.priv[0]; /* * The block layer core may call dd_finish_request() without having * called dd_insert_requests(). Skip requests that bypassed I/O * scheduling. See also blk_mq_request_bypass_insert(). */ - if (rq->elv.priv[0]) + if (per_prio) atomic_inc(&per_prio->stats.completed); } diff --git a/drivers/accessibility/speakup/Makefile b/drivers/accessibility/speakup/Makefile index 6f6a83565c0de..14ba1cca87f47 100644 --- a/drivers/accessibility/speakup/Makefile +++ b/drivers/accessibility/speakup/Makefile @@ -40,9 +40,7 @@ hostprogs += makemapdata makemapdata-objs := makemapdata.o quiet_cmd_mkmap = MKMAP $@ - cmd_mkmap = TOPDIR=$(srctree) \ - SPKDIR=$(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD),$(srctree)/drivers/accessibility/speakup) \ - $(obj)/makemapdata > $@ + cmd_mkmap = TOPDIR=$(srctree) SPKDIR=$(src) $(obj)/makemapdata > $@ $(obj)/mapdata.h: $(obj)/makemapdata $(call cmd,mkmap) diff --git a/drivers/acpi/arm64/init.c b/drivers/acpi/arm64/init.c index d0c8aed90fd16..7a47d8095a7dd 100644 --- a/drivers/acpi/arm64/init.c +++ b/drivers/acpi/arm64/init.c @@ -2,7 +2,7 @@ #include #include "init.h" -void __init acpi_arm_init(void) +void __init acpi_arch_init(void) { if (IS_ENABLED(CONFIG_ACPI_AGDI)) acpi_agdi_init(); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index aed4a37da03ea..3d5342f8d7b3a 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -853,6 +853,7 @@ static int sysfs_add_battery(struct acpi_battery *battery) struct power_supply_config psy_cfg = { .drv_data = battery, .attr_grp = acpi_battery_groups, + .no_wakeup_source = true, }; bool full_cap_broken = false; @@ -888,7 +889,7 @@ static int sysfs_add_battery(struct acpi_battery *battery) battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; battery->bat_desc.get_property = acpi_battery_get_property; - battery->bat = power_supply_register_no_ws(&battery->device->dev, + battery->bat = power_supply_register(&battery->device->dev, &battery->bat_desc, &psy_cfg); if (IS_ERR(battery->bat)) { diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 16917dc3ad604..058910af82bca 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1434,6 +1434,8 @@ static int __init acpi_bus_init(void) struct kobject *acpi_kobj; EXPORT_SYMBOL_GPL(acpi_kobj); +void __weak __init acpi_arch_init(void) { } + static int __init acpi_init(void) { int result; @@ -1461,8 +1463,7 @@ static int __init acpi_init(void) acpi_viot_early_init(); acpi_hest_init(); acpi_ghes_init(); - acpi_arm_init(); - acpi_riscv_init(); + acpi_arch_init(); acpi_scan_init(); acpi_ec_init(); acpi_debugfs_init(); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ce728cf7e3015..698897b29de24 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -800,12 +800,12 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) state->enter = acpi_idle_enter; state->flags = 0; - if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2 || - cx->type == ACPI_STATE_C3) { - state->enter_dead = acpi_idle_play_dead; - if (cx->type != ACPI_STATE_C3) - drv->safe_state_index = count; - } + + state->enter_dead = acpi_idle_play_dead; + + if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) + drv->safe_state_index = count; + /* * Halt-induced C1 is not good for ->enter_s2idle, because it * re-enables interrupts on exit. Moreover, C1 is generally not diff --git a/drivers/acpi/riscv/init.c b/drivers/acpi/riscv/init.c index 5ef97905a7275..673e4d5dd7527 100644 --- a/drivers/acpi/riscv/init.c +++ b/drivers/acpi/riscv/init.c @@ -7,7 +7,7 @@ #include #include "init.h" -void __init acpi_riscv_init(void) +void __init acpi_arch_init(void) { riscv_acpi_init_gsi_mapping(); } diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c index 423565c31d5ef..cb45ef5240dab 100644 --- a/drivers/acpi/x86/utils.c +++ b/drivers/acpi/x86/utils.c @@ -296,6 +296,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = { /* * 2. Devices which also have the skip i2c/serdev quirks and which * need the x86-android-tablets module to properly work. + * Sorted alphabetically. */ #if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS) { @@ -309,6 +310,19 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = { ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS), }, { + /* Acer Iconia One 8 A1-840 (non FHD version) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "BayTrail"), + /* Above strings are too generic also match BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "04/01/2014"), + }, + .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS | + ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY | + ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS), + }, + { + /* Asus ME176C tablet */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), @@ -319,23 +333,24 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = { ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS), }, { - /* Lenovo Yoga Book X90F/L */ + /* Asus TF103C transformer 2-in-1 */ .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), - DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), }, .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS | - ACPI_QUIRK_UART1_SKIP | ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY | ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS), }, { + /* Lenovo Yoga Book X90F/L */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), }, .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS | + ACPI_QUIRK_UART1_SKIP | ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY | ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS), }, @@ -425,6 +440,7 @@ static const struct acpi_device_id i2c_acpi_known_good_ids[] = { { "10EC5640", 0 }, /* RealTek ALC5640 audio codec */ { "10EC5651", 0 }, /* RealTek ALC5651 audio codec */ { "INT33F4", 0 }, /* X-Powers AXP288 PMIC */ + { "INT33F5", 0 }, /* TI Dollar Cove PMIC */ { "INT33FD", 0 }, /* Intel Crystal Cove PMIC */ { "INT34D3", 0 }, /* Intel Whiskey Cove PMIC */ { "NPCE69A", 0 }, /* Asus Transformer keyboard dock */ diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 0230c43377c1d..8ef259b4d0378 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -449,6 +449,12 @@ const struct bus_type amba_bustype = { }; EXPORT_SYMBOL_GPL(amba_bustype); +bool dev_is_amba(const struct device *dev) +{ + return dev->bus == &amba_bustype; +} +EXPORT_SYMBOL_GPL(dev_is_amba); + static int __init amba_init(void) { return bus_register(&amba_bustype); diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 978740537a1aa..ef353ca13c356 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1225,6 +1225,12 @@ static void binder_cleanup_ref_olocked(struct binder_ref *ref) binder_dequeue_work(ref->proc, &ref->death->work); binder_stats_deleted(BINDER_STAT_DEATH); } + + if (ref->freeze) { + binder_dequeue_work(ref->proc, &ref->freeze->work); + binder_stats_deleted(BINDER_STAT_FREEZE); + } + binder_stats_deleted(BINDER_STAT_REF); } @@ -3850,7 +3856,6 @@ binder_request_freeze_notification(struct binder_proc *proc, { struct binder_ref_freeze *freeze; struct binder_ref *ref; - bool is_frozen; freeze = kzalloc(sizeof(*freeze), GFP_KERNEL); if (!freeze) @@ -3866,32 +3871,31 @@ binder_request_freeze_notification(struct binder_proc *proc, } binder_node_lock(ref->node); - - if (ref->freeze || !ref->node->proc) { - binder_user_error("%d:%d invalid BC_REQUEST_FREEZE_NOTIFICATION %s\n", - proc->pid, thread->pid, - ref->freeze ? "already set" : "dead node"); + if (ref->freeze) { + binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION already set\n", + proc->pid, thread->pid); binder_node_unlock(ref->node); binder_proc_unlock(proc); kfree(freeze); return -EINVAL; } - binder_inner_proc_lock(ref->node->proc); - is_frozen = ref->node->proc->is_frozen; - binder_inner_proc_unlock(ref->node->proc); binder_stats_created(BINDER_STAT_FREEZE); INIT_LIST_HEAD(&freeze->work.entry); freeze->cookie = handle_cookie->cookie; freeze->work.type = BINDER_WORK_FROZEN_BINDER; - freeze->is_frozen = is_frozen; - ref->freeze = freeze; - binder_inner_proc_lock(proc); - binder_enqueue_work_ilocked(&ref->freeze->work, &proc->todo); - binder_wakeup_proc_ilocked(proc); - binder_inner_proc_unlock(proc); + if (ref->node->proc) { + binder_inner_proc_lock(ref->node->proc); + freeze->is_frozen = ref->node->proc->is_frozen; + binder_inner_proc_unlock(ref->node->proc); + + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked(&freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + binder_inner_proc_unlock(proc); + } binder_node_unlock(ref->node); binder_proc_unlock(proc); @@ -5151,6 +5155,16 @@ static void binder_release_work(struct binder_proc *proc, } break; case BINDER_WORK_NODE: break; + case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: { + struct binder_ref_freeze *freeze; + + freeze = container_of(w, struct binder_ref_freeze, work); + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "undelivered freeze notification, %016llx\n", + (u64)freeze->cookie); + kfree(freeze); + binder_stats_deleted(BINDER_STAT_FREEZE); + } break; default: pr_err("unexpected work type, %d, not freed\n", wtype); @@ -5552,6 +5566,7 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc) static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen) { + struct binder_node *prev = NULL; struct rb_node *n; struct binder_ref *ref; @@ -5560,7 +5575,10 @@ static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen) struct binder_node *node; node = rb_entry(n, struct binder_node, rb_node); + binder_inc_node_tmpref_ilocked(node); binder_inner_proc_unlock(proc); + if (prev) + binder_put_node(prev); binder_node_lock(node); hlist_for_each_entry(ref, &node->refs, node_entry) { /* @@ -5586,10 +5604,15 @@ static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen) } binder_inner_proc_unlock(ref->proc); } + prev = node; binder_node_unlock(node); binder_inner_proc_lock(proc); + if (proc->is_dead) + break; } binder_inner_proc_unlock(proc); + if (prev) + binder_put_node(prev); } static int binder_ioctl_freeze(struct binder_freeze_info *info, @@ -6260,6 +6283,7 @@ static void binder_deferred_release(struct binder_proc *proc) binder_release_work(proc, &proc->todo); binder_release_work(proc, &proc->delivered_death); + binder_release_work(proc, &proc->delivered_freeze); binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n", @@ -6393,6 +6417,12 @@ static void print_binder_work_ilocked(struct seq_file *m, case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: seq_printf(m, "%shas cleared death notification\n", prefix); break; + case BINDER_WORK_FROZEN_BINDER: + seq_printf(m, "%shas frozen binder\n", prefix); + break; + case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: + seq_printf(m, "%shas cleared freeze notification\n", prefix); + break; default: seq_printf(m, "%sunknown work: type %d\n", prefix, w->type); break; @@ -6539,6 +6569,10 @@ static void print_binder_proc(struct seq_file *m, seq_puts(m, " has delivered dead binder\n"); break; } + list_for_each_entry(w, &proc->delivered_freeze, entry) { + seq_puts(m, " has delivered freeze binder\n"); + break; + } binder_inner_proc_unlock(proc); if (!print_all && m->count == header_pos) m->count = start_pos; diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index 547f563417057..3999305b53560 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -370,7 +370,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id /* AHCI controllers often implement SFF compatible interface. * Grab all PCI BARs just in case. */ - rc = pcim_iomap_regions_request_all(pdev, 1 << AHCI_PCI_BAR, DRV_NAME); + rc = pcim_request_all_regions(pdev, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) @@ -386,7 +386,9 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) pci_enable_msi(pdev); - hpriv->mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; + hpriv->mmio = pcim_iomap(pdev, AHCI_PCI_BAR, 0); + if (!hpriv->mmio) + return -ENOMEM; /* save initial config */ ahci_save_initial_config(&pdev->dev, hpriv); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2d3d3d67b4d96..8d27c567be1c1 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1869,7 +1869,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* AHCI controllers often implement SFF compatible interface. * Grab all PCI BARs just in case. */ - rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); + rc = pcim_request_all_regions(pdev, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) @@ -1893,7 +1893,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (ahci_sb600_enable_64bit(pdev)) hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; - hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; + hpriv->mmio = pcim_iomap(pdev, ahci_pci_bar, 0); + if (!hpriv->mmio) + return -ENOMEM; /* detect remapped nvme devices */ ahci_remap_check(pdev, ahci_pci_bar, hpriv); diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index 7823888af4f68..afa4df4c5a3f3 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -92,7 +92,7 @@ * Auxiliary devices are created and registered by a subsystem-level core * device that needs to break up its functionality into smaller fragments. One * way to extend the scope of an auxiliary_device is to encapsulate it within a - * domain- pecific structure defined by the parent device. This structure + * domain-specific structure defined by the parent device. This structure * contains the auxiliary_device and any associated shared data/callbacks * needed to establish the connection with the parent. * @@ -335,35 +335,6 @@ int __auxiliary_device_add(struct auxiliary_device *auxdev, const char *modname) } EXPORT_SYMBOL_GPL(__auxiliary_device_add); -/** - * auxiliary_find_device - auxiliary device iterator for locating a particular device. - * @start: Device to begin with - * @data: Data to pass to match function - * @match: Callback function to check device - * - * This function returns a reference to a device that is 'found' - * for later use, as determined by the @match callback. - * - * The reference returned should be released with put_device(). - * - * The callback should return 0 if the device doesn't match and non-zero - * if it does. If the callback returns non-zero, this function will - * return to the caller and not iterate over any more devices. - */ -struct auxiliary_device *auxiliary_find_device(struct device *start, - const void *data, - device_match_t match) -{ - struct device *dev; - - dev = bus_find_device(&auxiliary_bus_type, start, data, match); - if (!dev) - return NULL; - - return to_auxiliary_dev(dev); -} -EXPORT_SYMBOL_GPL(auxiliary_find_device); - /** * __auxiliary_driver_register - register a driver for auxiliary bus devices * @auxdrv: auxiliary_driver structure diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 7a7609298e18b..609935ad50915 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -254,11 +254,11 @@ static int of_count_cache_leaves(struct device_node *np) { unsigned int leaves = 0; - if (of_property_read_bool(np, "cache-size")) + if (of_property_present(np, "cache-size")) ++leaves; - if (of_property_read_bool(np, "i-cache-size")) + if (of_property_present(np, "i-cache-size")) ++leaves; - if (of_property_read_bool(np, "d-cache-size")) + if (of_property_present(np, "d-cache-size")) ++leaves; if (!leaves) { @@ -367,9 +367,7 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); for_each_online_cpu(i) { - struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); - - if (i == cpu || !sib_cpu_ci->info_list) + if (i == cpu || !per_cpu_cacheinfo(i)) continue;/* skip if itself or no cacheinfo */ for (sib_index = 0; sib_index < cache_leaves(i); sib_index++) { sib_leaf = per_cpu_cacheinfo_idx(i, sib_index); @@ -409,10 +407,7 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) for (index = 0; index < cache_leaves(cpu); index++) { this_leaf = per_cpu_cacheinfo_idx(cpu, index); for_each_cpu(sibling, &this_leaf->shared_cpu_map) { - struct cpu_cacheinfo *sib_cpu_ci = - get_cpu_cacheinfo(sibling); - - if (sibling == cpu || !sib_cpu_ci->info_list) + if (sibling == cpu || !per_cpu_cacheinfo(sibling)) continue;/* skip if itself or no cacheinfo */ for (sib_index = 0; sib_index < cache_leaves(sibling); sib_index++) { diff --git a/drivers/base/class.c b/drivers/base/class.c index cb5359235c702..582b5a02a5c41 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -405,7 +405,7 @@ int class_for_each_device(const struct class *class, const struct device *start, if (!class) return -EINVAL; if (!sp) { - WARN(1, "%s called for class '%s' before it was initialized", + WARN(1, "%s called for class '%s' before it was registered", __func__, class->name); return -EINVAL; } @@ -453,7 +453,7 @@ struct device *class_find_device(const struct class *class, const struct device if (!class) return NULL; if (!sp) { - WARN(1, "%s called for class '%s' before it was initialized", + WARN(1, "%s called for class '%s' before it was registered", __func__, class->name); return NULL; } diff --git a/drivers/base/core.c b/drivers/base/core.c index 529af59e25ffb..94865c9d8adcf 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -552,7 +552,7 @@ void device_link_wait_removal(void) } EXPORT_SYMBOL_GPL(device_link_wait_removal); -static struct class devlink_class = { +static const struct class devlink_class = { .name = "devlink", .dev_groups = devlink_groups, .dev_release = devlink_dev_release, @@ -1971,7 +1971,7 @@ static struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwn /** * __fw_devlink_relax_cycles - Relax and mark dependency cycles. - * @con: Potential consumer device. + * @con_handle: Potential consumer device fwnode. * @sup_handle: Potential supplier's fwnode. * * Needs to be called with fwnode_lock and device link lock held. @@ -1989,10 +1989,10 @@ static struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwn * * Return true if one or more cycles were found. Otherwise, return false. */ -static bool __fw_devlink_relax_cycles(struct device *con, +static bool __fw_devlink_relax_cycles(struct fwnode_handle *con_handle, struct fwnode_handle *sup_handle) { - struct device *sup_dev = NULL, *par_dev = NULL; + struct device *sup_dev = NULL, *par_dev = NULL, *con_dev = NULL; struct fwnode_link *link; struct device_link *dev_link; bool ret = false; @@ -2009,22 +2009,22 @@ static bool __fw_devlink_relax_cycles(struct device *con, sup_handle->flags |= FWNODE_FLAG_VISITED; - sup_dev = get_dev_from_fwnode(sup_handle); - /* Termination condition. */ - if (sup_dev == con) { + if (sup_handle == con_handle) { pr_debug("----- cycle: start -----\n"); ret = true; goto out; } + sup_dev = get_dev_from_fwnode(sup_handle); + con_dev = get_dev_from_fwnode(con_handle); /* * If sup_dev is bound to a driver and @con hasn't started binding to a * driver, sup_dev can't be a consumer of @con. So, no need to check * further. */ if (sup_dev && sup_dev->links.status == DL_DEV_DRIVER_BOUND && - con->links.status == DL_DEV_NO_DRIVER) { + con_dev && con_dev->links.status == DL_DEV_NO_DRIVER) { ret = false; goto out; } @@ -2033,7 +2033,7 @@ static bool __fw_devlink_relax_cycles(struct device *con, if (link->flags & FWLINK_FLAG_IGNORE) continue; - if (__fw_devlink_relax_cycles(con, link->supplier)) { + if (__fw_devlink_relax_cycles(con_handle, link->supplier)) { __fwnode_link_cycle(link); ret = true; } @@ -2048,7 +2048,7 @@ static bool __fw_devlink_relax_cycles(struct device *con, else par_dev = fwnode_get_next_parent_dev(sup_handle); - if (par_dev && __fw_devlink_relax_cycles(con, par_dev->fwnode)) { + if (par_dev && __fw_devlink_relax_cycles(con_handle, par_dev->fwnode)) { pr_debug("%pfwf: cycle: child of %pfwf\n", sup_handle, par_dev->fwnode); ret = true; @@ -2066,7 +2066,7 @@ static bool __fw_devlink_relax_cycles(struct device *con, !(dev_link->flags & DL_FLAG_CYCLE)) continue; - if (__fw_devlink_relax_cycles(con, + if (__fw_devlink_relax_cycles(con_handle, dev_link->supplier->fwnode)) { pr_debug("%pfwf: cycle: depends on %pfwf\n", sup_handle, dev_link->supplier->fwnode); @@ -2114,11 +2114,6 @@ static int fw_devlink_create_devlink(struct device *con, if (link->flags & FWLINK_FLAG_IGNORE) return 0; - if (con->fwnode == link->consumer) - flags = fw_devlink_get_flags(link->flags); - else - flags = FW_DEVLINK_FLAGS_PERMISSIVE; - /* * In some cases, a device P might also be a supplier to its child node * C. However, this would defer the probe of C until the probe of P @@ -2139,25 +2134,23 @@ static int fw_devlink_create_devlink(struct device *con, return -EINVAL; /* - * SYNC_STATE_ONLY device links don't block probing and supports cycles. - * So, one might expect that cycle detection isn't necessary for them. - * However, if the device link was marked as SYNC_STATE_ONLY because - * it's part of a cycle, then we still need to do cycle detection. This - * is because the consumer and supplier might be part of multiple cycles - * and we need to detect all those cycles. + * Don't try to optimize by not calling the cycle detection logic under + * certain conditions. There's always some corner case that won't get + * detected. */ - if (!device_link_flag_is_sync_state_only(flags) || - flags & DL_FLAG_CYCLE) { - device_links_write_lock(); - if (__fw_devlink_relax_cycles(con, sup_handle)) { - __fwnode_link_cycle(link); - flags = fw_devlink_get_flags(link->flags); - pr_debug("----- cycle: end -----\n"); - dev_info(con, "Fixed dependency cycle(s) with %pfwf\n", - sup_handle); - } - device_links_write_unlock(); + device_links_write_lock(); + if (__fw_devlink_relax_cycles(link->consumer, sup_handle)) { + __fwnode_link_cycle(link); + pr_debug("----- cycle: end -----\n"); + pr_info("%pfwf: Fixed dependency cycle(s) with %pfwf\n", + link->consumer, sup_handle); } + device_links_write_unlock(); + + if (con->fwnode == link->consumer) + flags = fw_devlink_get_flags(link->flags); + else + flags = FW_DEVLINK_FLAGS_PERMISSIVE; if (sup_handle->flags & FWNODE_FLAG_NOT_DEVICE) sup_dev = fwnode_get_next_parent_dev(sup_handle); @@ -2180,8 +2173,8 @@ static int fw_devlink_create_devlink(struct device *con, } if (con != sup_dev && !device_link_add(con, sup_dev, flags)) { - dev_err(con, "Failed to create device link (0x%x) with %s\n", - flags, dev_name(sup_dev)); + dev_err(con, "Failed to create device link (0x%x) with supplier %s for %pfwf\n", + flags, dev_name(sup_dev), link->consumer); ret = -EINVAL; } diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 324a9a3c087aa..cb0912ea3e627 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -829,19 +829,18 @@ static void fw_log_firmware_info(const struct firmware *fw, const char *name, st shash->tfm = alg; if (crypto_shash_digest(shash, fw->data, fw->size, sha256buf) < 0) - goto out_shash; + goto out_free; for (int i = 0; i < SHA256_DIGEST_SIZE; i++) sprintf(&outbuf[i * 2], "%02x", sha256buf[i]); outbuf[SHA256_BLOCK_SIZE] = 0; dev_dbg(device, "Loaded FW: %s, sha256: %s\n", name, outbuf); -out_shash: - crypto_free_shash(alg); out_free: kfree(shash); kfree(outbuf); kfree(sha256buf); + crypto_free_shash(alg); } #else static void fw_log_firmware_info(const struct firmware *fw, const char *name, @@ -1075,8 +1074,8 @@ EXPORT_SYMBOL_GPL(firmware_request_platform); /** * firmware_request_cache() - cache firmware for suspend so resume can use it - * @name: name of firmware file * @device: device for which firmware should be cached for + * @name: name of firmware file * * There are some devices with an optimization that enables the device to not * require loading firmware on system reboot. This optimization may still diff --git a/drivers/base/node.c b/drivers/base/node.c index eb72580288e62..0ea653fa34330 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -27,7 +27,7 @@ static const struct bus_type node_subsys = { }; static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -45,10 +45,10 @@ static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj, return n; } -static BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES); static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -66,7 +66,7 @@ static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj, return n; } -static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES); /** * struct node_access_nodes - Access class device to hold user visible @@ -578,7 +578,7 @@ static struct attribute *node_dev_attrs[] = { NULL }; -static struct bin_attribute *node_dev_bin_attrs[] = { +static const struct bin_attribute *node_dev_bin_attrs[] = { &bin_attr_cpumap, &bin_attr_cpulist, NULL @@ -586,7 +586,7 @@ static struct bin_attribute *node_dev_bin_attrs[] = { static const struct attribute_group node_dev_group = { .attrs = node_dev_attrs, - .bin_attrs = node_dev_bin_attrs + .bin_attrs_new = node_dev_bin_attrs, }; static const struct attribute_group *node_dev_groups[] = { diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index a1474fb67db9b..f8163b559bf99 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -509,14 +509,6 @@ static ssize_t wakeup_last_time_ms_show(struct device *dev, return sysfs_emit(buf, "%lld\n", msec); } -static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid, - kgid_t kgid) -{ - if (dev->power.wakeup && dev->power.wakeup->dev) - return device_change_owner(dev->power.wakeup->dev, kuid, kgid); - return 0; -} - static DEVICE_ATTR_RO(wakeup_last_time_ms); #ifdef CONFIG_PM_AUTOSLEEP @@ -541,6 +533,15 @@ static ssize_t wakeup_prevent_sleep_time_ms_show(struct device *dev, static DEVICE_ATTR_RO(wakeup_prevent_sleep_time_ms); #endif /* CONFIG_PM_AUTOSLEEP */ + +static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid, + kgid_t kgid) +{ + if (dev->power.wakeup && dev->power.wakeup->dev) + return device_change_owner(dev->power.wakeup->dev, kuid, kgid); + return 0; +} + #else /* CONFIG_PM_SLEEP */ static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid) diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 89f98be5c5b99..cf160dd2c27bd 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -23,7 +23,7 @@ static ssize_t name##_show(struct device *dev, \ #define define_siblings_read_func(name, mask) \ static ssize_t name##_read(struct file *file, struct kobject *kobj, \ - struct bin_attribute *attr, char *buf, \ + const struct bin_attribute *attr, char *buf, \ loff_t off, size_t count) \ { \ struct device *dev = kobj_to_dev(kobj); \ @@ -33,7 +33,7 @@ static ssize_t name##_read(struct file *file, struct kobject *kobj, \ } \ \ static ssize_t name##_list_read(struct file *file, struct kobject *kobj, \ - struct bin_attribute *attr, char *buf, \ + const struct bin_attribute *attr, char *buf, \ loff_t off, size_t count) \ { \ struct device *dev = kobj_to_dev(kobj); \ @@ -62,50 +62,50 @@ define_id_show_func(ppin, "0x%llx"); static DEVICE_ATTR_ADMIN_RO(ppin); define_siblings_read_func(thread_siblings, sibling_cpumask); -static BIN_ATTR_RO(thread_siblings, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(thread_siblings_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(thread_siblings, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(thread_siblings_list, CPULIST_FILE_MAX_BYTES); define_siblings_read_func(core_cpus, sibling_cpumask); -static BIN_ATTR_RO(core_cpus, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(core_cpus_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(core_cpus, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(core_cpus_list, CPULIST_FILE_MAX_BYTES); define_siblings_read_func(core_siblings, core_cpumask); -static BIN_ATTR_RO(core_siblings, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(core_siblings_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(core_siblings, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(core_siblings_list, CPULIST_FILE_MAX_BYTES); #ifdef TOPOLOGY_CLUSTER_SYSFS define_siblings_read_func(cluster_cpus, cluster_cpumask); -static BIN_ATTR_RO(cluster_cpus, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(cluster_cpus_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(cluster_cpus, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(cluster_cpus_list, CPULIST_FILE_MAX_BYTES); #endif #ifdef TOPOLOGY_DIE_SYSFS define_siblings_read_func(die_cpus, die_cpumask); -static BIN_ATTR_RO(die_cpus, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(die_cpus_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(die_cpus, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(die_cpus_list, CPULIST_FILE_MAX_BYTES); #endif define_siblings_read_func(package_cpus, core_cpumask); -static BIN_ATTR_RO(package_cpus, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(package_cpus_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(package_cpus, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(package_cpus_list, CPULIST_FILE_MAX_BYTES); #ifdef TOPOLOGY_BOOK_SYSFS define_id_show_func(book_id, "%d"); static DEVICE_ATTR_RO(book_id); define_siblings_read_func(book_siblings, book_cpumask); -static BIN_ATTR_RO(book_siblings, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(book_siblings_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(book_siblings, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(book_siblings_list, CPULIST_FILE_MAX_BYTES); #endif #ifdef TOPOLOGY_DRAWER_SYSFS define_id_show_func(drawer_id, "%d"); static DEVICE_ATTR_RO(drawer_id); define_siblings_read_func(drawer_siblings, drawer_cpumask); -static BIN_ATTR_RO(drawer_siblings, CPUMAP_FILE_MAX_BYTES); -static BIN_ATTR_RO(drawer_siblings_list, CPULIST_FILE_MAX_BYTES); +static const BIN_ATTR_RO(drawer_siblings, CPUMAP_FILE_MAX_BYTES); +static const BIN_ATTR_RO(drawer_siblings_list, CPULIST_FILE_MAX_BYTES); #endif -static struct bin_attribute *bin_attrs[] = { +static const struct bin_attribute *const bin_attrs[] = { &bin_attr_core_cpus, &bin_attr_core_cpus_list, &bin_attr_thread_siblings, @@ -163,7 +163,7 @@ static umode_t topology_is_visible(struct kobject *kobj, static const struct attribute_group topology_attr_group = { .attrs = default_attrs, - .bin_attrs = bin_attrs, + .bin_attrs_new = bin_attrs, .is_visible = topology_is_visible, .name = "topology" }; diff --git a/drivers/base/trace.h b/drivers/base/trace.h index e52b6eae060dd..3b83b13a57ff1 100644 --- a/drivers/base/trace.h +++ b/drivers/base/trace.h @@ -24,18 +24,18 @@ DECLARE_EVENT_CLASS(devres, __field(struct device *, dev) __field(const char *, op) __field(void *, node) - __field(const char *, name) + __string(name, name) __field(size_t, size) ), TP_fast_assign( __assign_str(devname); __entry->op = op; __entry->node = node; - __entry->name = name; + __assign_str(name); __entry->size = size; ), TP_printk("%s %3s %p %s (%zu bytes)", __get_str(devname), - __entry->op, __entry->node, __entry->name, __entry->size) + __entry->op, __entry->node, __get_str(name), __entry->size) ); DEFINE_EVENT(devres, devres_log, diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 5a95671d81515..292f127cae0ab 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -231,8 +231,10 @@ static void brd_do_discard(struct brd_device *brd, sector_t sector, u32 size) xa_lock(&brd->brd_pages); while (size >= PAGE_SIZE && aligned_sector < rd_size * 2) { page = __xa_erase(&brd->brd_pages, aligned_sector >> PAGE_SECTORS_SHIFT); - if (page) + if (page) { __free_page(page); + brd->brd_nr_pages--; + } aligned_sector += PAGE_SECTORS; size -= PAGE_SIZE; } diff --git a/drivers/block/loop.c b/drivers/block/loop.c index fe9bb4fb5f1ba..8f6761c27c68b 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -770,12 +770,11 @@ static void loop_sysfs_exit(struct loop_device *lo) &loop_attribute_group); } -static void loop_config_discard(struct loop_device *lo, - struct queue_limits *lim) +static void loop_get_discard_config(struct loop_device *lo, + u32 *granularity, u32 *max_discard_sectors) { struct file *file = lo->lo_backing_file; struct inode *inode = file->f_mapping->host; - u32 granularity = 0, max_discard_sectors = 0; struct kstatfs sbuf; /* @@ -788,24 +787,17 @@ static void loop_config_discard(struct loop_device *lo, if (S_ISBLK(inode->i_mode)) { struct block_device *bdev = I_BDEV(inode); - max_discard_sectors = bdev_write_zeroes_sectors(bdev); - granularity = bdev_discard_granularity(bdev); + *max_discard_sectors = bdev_write_zeroes_sectors(bdev); + *granularity = bdev_discard_granularity(bdev); /* * We use punch hole to reclaim the free space used by the * image a.k.a. discard. */ } else if (file->f_op->fallocate && !vfs_statfs(&file->f_path, &sbuf)) { - max_discard_sectors = UINT_MAX >> 9; - granularity = sbuf.f_bsize; + *max_discard_sectors = UINT_MAX >> 9; + *granularity = sbuf.f_bsize; } - - lim->max_hw_discard_sectors = max_discard_sectors; - lim->max_write_zeroes_sectors = max_discard_sectors; - if (max_discard_sectors) - lim->discard_granularity = granularity; - else - lim->discard_granularity = 0; } struct loop_worker { @@ -991,6 +983,7 @@ static int loop_reconfigure_limits(struct loop_device *lo, unsigned int bsize) struct inode *inode = file->f_mapping->host; struct block_device *backing_bdev = NULL; struct queue_limits lim; + u32 granularity = 0, max_discard_sectors = 0; if (S_ISBLK(inode->i_mode)) backing_bdev = I_BDEV(inode); @@ -1000,6 +993,8 @@ static int loop_reconfigure_limits(struct loop_device *lo, unsigned int bsize) if (!bsize) bsize = loop_default_blocksize(lo, backing_bdev); + loop_get_discard_config(lo, &granularity, &max_discard_sectors); + lim = queue_limits_start_update(lo->lo_queue); lim.logical_block_size = bsize; lim.physical_block_size = bsize; @@ -1009,7 +1004,12 @@ static int loop_reconfigure_limits(struct loop_device *lo, unsigned int bsize) lim.features |= BLK_FEAT_WRITE_CACHE; if (backing_bdev && !bdev_nonrot(backing_bdev)) lim.features |= BLK_FEAT_ROTATIONAL; - loop_config_discard(lo, &lim); + lim.max_hw_discard_sectors = max_discard_sectors; + lim.max_write_zeroes_sectors = max_discard_sectors; + if (max_discard_sectors) + lim.discard_granularity = granularity; + else + lim.discard_granularity = 0; return queue_limits_commit_update(lo->lo_queue, &lim); } diff --git a/drivers/block/rnull.rs b/drivers/block/rnull.rs index b0227cf9ddd38..5de7223beb4d5 100644 --- a/drivers/block/rnull.rs +++ b/drivers/block/rnull.rs @@ -32,7 +32,7 @@ module! { } struct NullBlkModule { - _disk: Pin>>>, + _disk: Pin>>>, } impl kernel::Module for NullBlkModule { @@ -47,7 +47,7 @@ impl kernel::Module for NullBlkModule { .rotational(false) .build(format_args!("rnullb{}", 0), tagset)?; - let disk = Box::pin_init(new_mutex!(disk, "nullb:disk"), flags::GFP_KERNEL)?; + let disk = KBox::pin_init(new_mutex!(disk, "nullb:disk"), flags::GFP_KERNEL)?; Ok(Self { _disk: disk }) } diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index c6d18cd8af44e..d4aed12dd436b 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -3041,7 +3041,7 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd, ret = ublk_ctrl_end_recovery(ub, cmd); break; default: - ret = -ENOTSUPP; + ret = -EOPNOTSUPP; break; } diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index dedd29ca8db35..e8c92972f9df9 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -82,9 +82,9 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) * other cores to shutdown while we're collecting RDDM buffer. After * returning from this function, we expect the device to reset. * - * Normaly, we read/write pm_state only after grabbing the + * Normally, we read/write pm_state only after grabbing the * pm_lock, since we're in a panic, skipping it. Also there is no - * gurantee that this state change would take effect since + * guarantee that this state change would take effect since * we're setting it w/o grabbing pm_lock */ mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT; diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index d057e877932e3..3134f111be353 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -255,7 +255,7 @@ struct mhi_chan { /* * Important: When consuming, increment tre_ring first and when * releasing, decrement buf_ring first. If tre_ring has space, buf_ring - * is guranteed to have space so we do not need to check both rings. + * is guaranteed to have space so we do not need to check both rings. */ struct mhi_ring buf_ring; struct mhi_ring tre_ring; diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 9938bb034c1cb..07645ce2119a7 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -917,12 +917,12 @@ static int mhi_pci_claim(struct mhi_controller *mhi_cntrl, return err; } - err = pcim_iomap_regions(pdev, 1 << bar_num, pci_name(pdev)); - if (err) { + mhi_cntrl->regs = pcim_iomap_region(pdev, 1 << bar_num, pci_name(pdev)); + if (IS_ERR(mhi_cntrl->regs)) { + err = PTR_ERR(mhi_cntrl->regs); dev_err(&pdev->dev, "failed to map pci region: %d\n", err); return err; } - mhi_cntrl->regs = pcim_iomap_table(pdev)[bar_num]; mhi_cntrl->reg_len = pci_resource_len(pdev, bar_num); err = dma_set_mask_and_coherent(&pdev->dev, dma_mask); diff --git a/drivers/bus/mhi/host/trace.h b/drivers/bus/mhi/host/trace.h index 95613c8ebe069..3e0c41777429e 100644 --- a/drivers/bus/mhi/host/trace.h +++ b/drivers/bus/mhi/host/trace.h @@ -9,6 +9,7 @@ #if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_EVENT_MHI_HOST_H +#include #include #include #include "../common.h" @@ -97,18 +98,18 @@ TRACE_EVENT(mhi_gen_tre, __string(name, mhi_cntrl->mhi_dev->name) __field(int, ch_num) __field(void *, wp) - __field(__le64, tre_ptr) - __field(__le32, dword0) - __field(__le32, dword1) + __field(uint64_t, tre_ptr) + __field(uint32_t, dword0) + __field(uint32_t, dword1) ), TP_fast_assign( __assign_str(name); __entry->ch_num = mhi_chan->chan; __entry->wp = mhi_tre; - __entry->tre_ptr = mhi_tre->ptr; - __entry->dword0 = mhi_tre->dword[0]; - __entry->dword1 = mhi_tre->dword[1]; + __entry->tre_ptr = le64_to_cpu(mhi_tre->ptr); + __entry->dword0 = le32_to_cpu(mhi_tre->dword[0]); + __entry->dword1 = le32_to_cpu(mhi_tre->dword[1]); ), TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n", @@ -176,19 +177,19 @@ DECLARE_EVENT_CLASS(mhi_process_event_ring, TP_STRUCT__entry( __string(name, mhi_cntrl->mhi_dev->name) - __field(__le32, dword0) - __field(__le32, dword1) + __field(uint32_t, dword0) + __field(uint32_t, dword1) __field(int, state) - __field(__le64, ptr) + __field(uint64_t, ptr) __field(void *, rp) ), TP_fast_assign( __assign_str(name); __entry->rp = rp; - __entry->ptr = rp->ptr; - __entry->dword0 = rp->dword[0]; - __entry->dword1 = rp->dword[1]; + __entry->ptr = le64_to_cpu(rp->ptr); + __entry->dword0 = le32_to_cpu(rp->dword[0]); + __entry->dword1 = le32_to_cpu(rp->dword[1]); __entry->state = MHI_TRE_GET_EV_STATE(rp); ), diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 07371cb653d35..316bd89a95caf 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -707,7 +707,7 @@ static const struct vm_operations_struct cdx_phys_vm_ops = { * Return: true on success, false otherwise. */ static int cdx_mmap_resource(struct file *fp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { struct cdx_device *cdx_dev = to_cdx_device(kobj_to_dev(kobj)); diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c index f2cff1a6fed52..53467b0a6187f 100644 --- a/drivers/char/powernv-op-panel.c +++ b/drivers/char/powernv-op-panel.c @@ -213,7 +213,7 @@ static struct platform_driver oppanel_driver = { .of_match_table = oppanel_match, }, .probe = oppanel_probe, - .remove_new = oppanel_remove, + .remove = oppanel_remove, }; module_platform_driver(oppanel_driver); diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 0f8185e541ed4..f887569fd3d0f 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1467,7 +1467,7 @@ static struct platform_driver sonypi_driver = { .pm = SONYPI_PM, }, .probe = sonypi_probe, - .remove_new = sonypi_remove, + .remove = sonypi_remove, .shutdown = sonypi_shutdown, }; diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 4f6c3cb8aa413..34a345dc5e724 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -738,7 +738,7 @@ MODULE_DEVICE_TABLE(of, hwicap_of_match); static struct platform_driver hwicap_platform_driver = { .probe = hwicap_drv_probe, - .remove_new = hwicap_drv_remove, + .remove = hwicap_drv_remove, .driver = { .name = DRIVER_NAME, .of_match_table = hwicap_of_match, diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 8802e2a6fd20b..1a1e64133315b 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -74,7 +74,7 @@ static void xilly_drv_remove(struct platform_device *op) static struct platform_driver xillybus_platform_driver = { .probe = xilly_drv_probe, - .remove_new = xilly_drv_remove, + .remove = xilly_drv_remove, .driver = { .name = xillyname, .of_match_table = xillybus_of_match, diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 1b481731df964..b9df9b19d4bd9 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -2407,6 +2407,18 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma) start += PAGE_SIZE; } + +#ifdef CONFIG_MMU + /* + * Leaving behind a partial mapping of a buffer we're about to + * drop is unsafe, see remap_pfn_range_notrack(). + * We need to zap the range here ourselves instead of relying + * on the automatic zapping in remap_pfn_range() because we call + * remap_pfn_range() in a loop. + */ + if (retval) + zap_vma_ptes(vma, vma->vm_start, size); +#endif } if (retval == 0) { diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index 200876f3ec048..6ac4efb5658b7 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -311,6 +311,7 @@ static const struct of_device_id ftm_quaddec_match[] = { { .compatible = "fsl,ftm-quaddec" }, {}, }; +MODULE_DEVICE_TABLE(of, ftm_quaddec_match); static struct platform_driver ftm_quaddec_driver = { .driver = { diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index af5942e66f7d0..ee2bae27b7289 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -408,13 +408,9 @@ static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id) pci_set_master(pci); - ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); - if (ret) - return ret; - - regs = pcim_iomap_table(pci)[0]; - if (!regs) - return -ENOMEM; + regs = pcim_iomap_region(pci, 0, pci_name(pci)); + if (IS_ERR(regs)) + return PTR_ERR(regs); qep->dev = dev; qep->regs = regs; diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 186e73d6ccb45..87b6ec567b544 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -214,11 +214,17 @@ static int stm32_count_enable_write(struct counter_device *counter, { struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; + int ret; if (enable) { regmap_read(priv->regmap, TIM_CR1, &cr1); - if (!(cr1 & TIM_CR1_CEN)) - clk_enable(priv->clk); + if (!(cr1 & TIM_CR1_CEN)) { + ret = clk_enable(priv->clk); + if (ret) { + dev_err(counter->parent, "Cannot enable clock %d\n", ret); + return ret; + } + } regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); @@ -694,6 +700,7 @@ static int stm32_timer_cnt_probe_encoder(struct device *dev, } ret = of_property_read_u32(tnode, "reg", &idx); + of_node_put(tnode); if (ret) { dev_err(dev, "Can't get index (%d)\n", ret); return ret; @@ -816,7 +823,11 @@ static int __maybe_unused stm32_timer_cnt_resume(struct device *dev) return ret; if (priv->enabled) { - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) { + dev_err(dev, "Cannot enable clock %d\n", ret); + return ret; + } /* Restore registers that may have been lost */ regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c index 675447315cafb..b119aeede693e 100644 --- a/drivers/counter/ti-ecap-capture.c +++ b/drivers/counter/ti-ecap-capture.c @@ -574,8 +574,13 @@ static int ecap_cnt_resume(struct device *dev) { struct counter_device *counter_dev = dev_get_drvdata(dev); struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); + int ret; - clk_enable(ecap_dev->clk); + ret = clk_enable(ecap_dev->clk); + if (ret) { + dev_err(dev, "Cannot enable clock %d\n", ret); + return ret; + } ecap_cnt_capture_set_evmode(counter_dev, ecap_dev->pm_ctx.ev_mode); diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index d228b4d18d560..14462c092039e 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -26,6 +26,7 @@ #include #include +#include "cpuidle.h" #include "dt_idle_states.h" #include "dt_idle_genpd.h" @@ -329,6 +330,9 @@ static int sbi_cpuidle_init_cpu(struct device *dev, int cpu) return ret; } + if (cpuidle_disabled()) + return 0; + ret = cpuidle_register(drv, NULL); if (ret) goto deinit; @@ -538,7 +542,10 @@ static int sbi_cpuidle_probe(struct platform_device *pdev) /* Setup CPU hotplut notifiers */ sbi_idle_init_cpuhp(); - pr_info("idle driver registered for all CPUs\n"); + if (cpuidle_disabled()) + pr_info("cpuidle is disabled\n"); + else + pr_info("idle driver registered for all CPUs\n"); return 0; @@ -582,4 +589,4 @@ static int __init sbi_cpuidle_init(void) return 0; } -device_initcall(sbi_cpuidle_init); +arch_initcall(sbi_cpuidle_init); diff --git a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c index f49818a13013a..788a11cdb34b5 100644 --- a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c @@ -129,16 +129,21 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Find and map all the device's BARS */ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM) & ADF_GEN4_BAR_MASK; - ret = pcim_iomap_regions_request_all(pdev, bar_mask, pci_name(pdev)); + ret = pcim_request_all_regions(pdev, pci_name(pdev)); if (ret) { - dev_err(&pdev->dev, "Failed to map pci regions.\n"); + dev_err(&pdev->dev, "Failed to request PCI regions.\n"); goto out_err; } i = 0; for_each_set_bit(bar_nr, &bar_mask, PCI_STD_NUM_BARS) { bar = &accel_pci_dev->pci_bars[i++]; - bar->virt_addr = pcim_iomap_table(pdev)[bar_nr]; + bar->virt_addr = pcim_iomap(pdev, bar_nr, 0); + if (!bar->virt_addr) { + dev_err(&pdev->dev, "Failed to ioremap PCI region.\n"); + ret = -ENOMEM; + goto out_err; + } } pci_set_master(pdev); diff --git a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c index 659905e459503..115eabfd1f6ba 100644 --- a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c @@ -131,16 +131,21 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Find and map all the device's BARS */ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM) & ADF_GEN4_BAR_MASK; - ret = pcim_iomap_regions_request_all(pdev, bar_mask, pci_name(pdev)); + ret = pcim_request_all_regions(pdev, pci_name(pdev)); if (ret) { - dev_err(&pdev->dev, "Failed to map pci regions.\n"); + dev_err(&pdev->dev, "Failed to request PCI regions.\n"); goto out_err; } i = 0; for_each_set_bit(bar_nr, &bar_mask, PCI_STD_NUM_BARS) { bar = &accel_pci_dev->pci_bars[i++]; - bar->virt_addr = pcim_iomap_table(pdev)[bar_nr]; + bar->virt_addr = pcim_iomap(pdev, bar_nr, 0); + if (!bar->virt_addr) { + dev_err(&pdev->dev, "Failed to ioremap PCI region.\n"); + ret = -ENOMEM; + goto out_err; + } } pci_set_master(pdev); diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c b/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c index 400e36d9908f4..94d0e73e42de1 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c @@ -739,18 +739,22 @@ static int otx2_cptpf_probe(struct pci_dev *pdev, dev_err(dev, "Unable to get usable DMA configuration\n"); goto clear_drvdata; } - /* Map PF's configuration registers */ - err = pcim_iomap_regions_request_all(pdev, 1 << PCI_PF_REG_BAR_NUM, - OTX2_CPT_DRV_NAME); + err = pcim_request_all_regions(pdev, OTX2_CPT_DRV_NAME); if (err) { - dev_err(dev, "Couldn't get PCI resources 0x%x\n", err); + dev_err(dev, "Couldn't request PCI resources 0x%x\n", err); goto clear_drvdata; } pci_set_master(pdev); pci_set_drvdata(pdev, cptpf); cptpf->pdev = pdev; - cptpf->reg_base = pcim_iomap_table(pdev)[PCI_PF_REG_BAR_NUM]; + /* Map PF's configuration registers */ + cptpf->reg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0); + if (!cptpf->reg_base) { + err = -ENOMEM; + dev_err(dev, "Couldn't ioremap PCI resource 0x%x\n", err); + goto clear_drvdata; + } /* Check if AF driver is up, otherwise defer probe */ err = cpt_is_pf_usable(cptpf); diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c b/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c index 527d34cc258b4..d0b6ee901f620 100644 --- a/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c +++ b/drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c @@ -358,9 +358,8 @@ static int otx2_cptvf_probe(struct pci_dev *pdev, dev_err(dev, "Unable to get usable DMA configuration\n"); goto clear_drvdata; } - /* Map VF's configuration registers */ - ret = pcim_iomap_regions_request_all(pdev, 1 << PCI_PF_REG_BAR_NUM, - OTX2_CPTVF_DRV_NAME); + + ret = pcim_request_all_regions(pdev, OTX2_CPTVF_DRV_NAME); if (ret) { dev_err(dev, "Couldn't get PCI resources 0x%x\n", ret); goto clear_drvdata; @@ -369,7 +368,13 @@ static int otx2_cptvf_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, cptvf); cptvf->pdev = pdev; - cptvf->reg_base = pcim_iomap_table(pdev)[PCI_PF_REG_BAR_NUM]; + /* Map VF's configuration registers */ + cptvf->reg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0); + if (!cptvf->reg_base) { + ret = -ENOMEM; + dev_err(dev, "Couldn't ioremap PCI resource 0x%x\n", ret); + goto clear_drvdata; + } otx2_cpt_set_hw_caps(pdev, &cptvf->cap_flag); diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index 9dc394295e1fc..24041cf85cfbe 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -173,7 +173,7 @@ static ssize_t CDAT_read(struct file *filp, struct kobject *kobj, static BIN_ATTR_ADMIN_RO(CDAT, 0); static umode_t cxl_port_bin_attr_is_visible(struct kobject *kobj, - struct bin_attribute *attr, int i) + const struct bin_attribute *attr, int i) { struct device *dev = kobj_to_dev(kobj); struct cxl_port *port = to_cxl_port(dev); diff --git a/drivers/dax/pmem/Makefile b/drivers/dax/pmem/Makefile deleted file mode 100644 index 191c31f0d4f00..0000000000000 --- a/drivers/dax/pmem/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o -obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o - -dax_pmem-y := pmem.o -dax_pmem_core-y := core.o -dax_pmem_compat-y := compat.o diff --git a/drivers/dax/pmem/pmem.c b/drivers/dax/pmem/pmem.c deleted file mode 100644 index dfe91a2990fec..0000000000000 --- a/drivers/dax/pmem/pmem.c +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ -#include -#include -#include -#include -#include -#include "../bus.h" - - diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d9ec1e69e4283..e994d6e0779e0 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -378,6 +378,20 @@ config LOONGSON1_APB_DMA This selects support for the APB DMA controller in Loongson1 SoCs, which is required by Loongson1 NAND and audio support. +config LOONGSON2_APB_DMA + tristate "Loongson2 APB DMA support" + depends on LOONGARCH || COMPILE_TEST + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support for the Loongson2 APB DMA controller driver. The + DMA controller is having single DMA channel which can be + configured for different peripherals like audio, nand, sdio + etc which is in APB bus. + + This DMA controller transfers data from memory to peripheral fifo. + It does not support memory to memory data transfer. + config LPC18XX_DMAMUX bool "NXP LPC18xx/43xx DMA MUX for PL080" depends on ARCH_LPC18XX || COMPILE_TEST @@ -396,20 +410,6 @@ config LPC32XX_DMAMUX Support for PL080 multiplexed DMA request lines on LPC32XX platrofm. -config LS2X_APB_DMA - tristate "Loongson LS2X APB DMA support" - depends on LOONGARCH || COMPILE_TEST - select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS - help - Support for the Loongson LS2X APB DMA controller driver. The - DMA controller is having single DMA channel which can be - configured for different peripherals like audio, nand, sdio - etc which is in APB bus. - - This DMA controller transfers data from memory to peripheral fifo. - It does not support memory to memory data transfer. - config MCF_EDMA tristate "Freescale eDMA engine support, ColdFire mcf5441x SoCs" depends on M5441x || (COMPILE_TEST && FSL_EDMA=n) diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index ad6a03c052ec4..5b2a52f4f2ee9 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -50,9 +50,9 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-y += idxd/ obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o +obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o -obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o obj-$(CONFIG_MILBEAUT_XDMAC) += milbeaut-xdmac.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c index a58a1600dd659..2abbe11e797e9 100644 --- a/drivers/dma/acpi-dma.c +++ b/drivers/dma/acpi-dma.c @@ -9,18 +9,21 @@ * Mika Westerberg */ +#include +#include #include #include #include -#include +#include +#include +#include #include #include #include -#include -#include -#include -#include #include +#include +#include +#include static LIST_HEAD(acpi_dma_list); static DEFINE_MUTEX(acpi_dma_lock); @@ -236,7 +239,7 @@ int acpi_dma_controller_free(struct device *dev) } EXPORT_SYMBOL_GPL(acpi_dma_controller_free); -static void devm_acpi_dma_release(struct device *dev, void *res) +static void devm_acpi_dma_free(void *dev) { acpi_dma_controller_free(dev); } @@ -259,37 +262,15 @@ int devm_acpi_dma_controller_register(struct device *dev, (struct acpi_dma_spec *, struct acpi_dma *), void *data) { - void *res; int ret; - res = devres_alloc(devm_acpi_dma_release, 0, GFP_KERNEL); - if (!res) - return -ENOMEM; - ret = acpi_dma_controller_register(dev, acpi_dma_xlate, data); - if (ret) { - devres_free(res); + if (ret) return ret; - } - devres_add(dev, res); - return 0; -} -EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register); -/** - * devm_acpi_dma_controller_free - resource managed acpi_dma_controller_free() - * @dev: device that is unregistering as DMA controller - * - * Unregister a DMA controller registered with - * devm_acpi_dma_controller_register(). Normally this function will not need to - * be called and the resource management code will ensure that the resource is - * freed. - */ -void devm_acpi_dma_controller_free(struct device *dev) -{ - WARN_ON(devres_release(dev, devm_acpi_dma_release, NULL, NULL)); + return devm_add_action_or_reset(dev, devm_acpi_dma_free, dev); } -EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_free); +EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register); /** * acpi_dma_update_dma_spec - prepare dma specifier to pass to translation function diff --git a/drivers/dma/altera-msgdma.c b/drivers/dma/altera-msgdma.c index e6a6566b309ee..a203fdd84950e 100644 --- a/drivers/dma/altera-msgdma.c +++ b/drivers/dma/altera-msgdma.c @@ -954,7 +954,7 @@ static struct platform_driver msgdma_driver = { .of_match_table = of_match_ptr(msgdma_match), }, .probe = msgdma_probe, - .remove_new = msgdma_remove, + .remove = msgdma_remove, }; module_platform_driver(msgdma_driver); diff --git a/drivers/dma/amd/qdma/qdma.c b/drivers/dma/amd/qdma/qdma.c index b0a1f3ad851b1..6d9079458fe9c 100644 --- a/drivers/dma/amd/qdma/qdma.c +++ b/drivers/dma/amd/qdma/qdma.c @@ -1133,7 +1133,7 @@ static struct platform_driver amd_qdma_driver = { .name = "amd-qdma", }, .probe = amd_qdma_probe, - .remove_new = amd_qdma_remove, + .remove = amd_qdma_remove, }; module_platform_driver(amd_qdma_driver); diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c index 9588773dd2eb6..c499173d80b20 100644 --- a/drivers/dma/apple-admac.c +++ b/drivers/dma/apple-admac.c @@ -950,7 +950,7 @@ static struct platform_driver apple_admac_driver = { .of_match_table = admac_of_match, }, .probe = admac_probe, - .remove_new = admac_remove, + .remove = admac_remove, }; module_platform_driver(apple_admac_driver); diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index baebddc740b0d..2d147712cbc69 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -2250,7 +2250,7 @@ static const struct dev_pm_ops __maybe_unused at_dma_dev_pm_ops = { }; static struct platform_driver at_dma_driver = { - .remove_new = at_dma_remove, + .remove = at_dma_remove, .shutdown = at_dma_shutdown, .id_table = atdma_devtypes, .driver = { diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 299396121e6dc..9c7b402200040 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -2476,7 +2476,7 @@ MODULE_DEVICE_TABLE(of, atmel_xdmac_dt_ids); static struct platform_driver at_xdmac_driver = { .probe = at_xdmac_probe, - .remove_new = at_xdmac_remove, + .remove = at_xdmac_remove, .driver = { .name = "at_xdmac", .of_match_table = of_match_ptr(atmel_xdmac_dt_ids), diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c index cfa6e1167a1fd..7f0e76439ce58 100644 --- a/drivers/dma/bcm-sba-raid.c +++ b/drivers/dma/bcm-sba-raid.c @@ -1756,7 +1756,7 @@ MODULE_DEVICE_TABLE(of, sba_of_match); static struct platform_driver sba_driver = { .probe = sba_probe, - .remove_new = sba_remove, + .remove = sba_remove, .driver = { .name = "bcm-sba-raid", .of_match_table = sba_of_match, diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index e1b92b4d7b056..7ba52dee40a96 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -1029,7 +1029,7 @@ static void bcm2835_dma_remove(struct platform_device *pdev) static struct platform_driver bcm2835_dma_driver = { .probe = bcm2835_dma_probe, - .remove_new = bcm2835_dma_remove, + .remove = bcm2835_dma_remove, .driver = { .name = "bcm2835-dma", .of_match_table = of_match_ptr(bcm2835_dma_of_match), diff --git a/drivers/dma/bestcomm/bestcomm.c b/drivers/dma/bestcomm/bestcomm.c index 0bbaa7620bdd4..6c4d655ffe77f 100644 --- a/drivers/dma/bestcomm/bestcomm.c +++ b/drivers/dma/bestcomm/bestcomm.c @@ -486,7 +486,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_bcom_of_match); static struct platform_driver mpc52xx_bcom_of_platform_driver = { .probe = mpc52xx_bcom_probe, - .remove_new = mpc52xx_bcom_remove, + .remove = mpc52xx_bcom_remove, .driver = { .name = DRIVER_NAME, .of_match_table = mpc52xx_bcom_of_match, diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index c9cfa341db510..100057603fd4e 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -1122,7 +1122,7 @@ MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match); static struct platform_driver jz4780_dma_driver = { .probe = jz4780_dma_probe, - .remove_new = jz4780_dma_remove, + .remove = jz4780_dma_remove, .driver = { .name = "jz4780-dma", .of_match_table = jz4780_dma_dt_match, diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index fffafa86d964e..b23536645ff7c 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1676,7 +1676,7 @@ MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); static struct platform_driver dw_driver = { .probe = dw_probe, - .remove_new = dw_remove, + .remove = dw_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = dw_dma_of_id_table, diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 47c58ad468cbc..2606cf9cd4299 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -191,7 +191,7 @@ static const struct dev_pm_ops dw_dev_pm_ops = { static struct platform_driver dw_driver = { .probe = dw_probe, - .remove_new = dw_remove, + .remove = dw_remove, .shutdown = dw_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 6b98a23e33324..e424bb5c40e79 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -929,8 +929,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) /* Sanity check the channel parameters */ if (!edmac->edma->m2m) { - if (edmac->dma_cfg.port < EP93XX_DMA_I2S1 || - edmac->dma_cfg.port > EP93XX_DMA_IRDA) + if (edmac->dma_cfg.port > EP93XX_DMA_IRDA) return -EINVAL; if (edmac->dma_cfg.dir != ep93xx_dma_chan_direction(chan)) return -EINVAL; diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c index f9f1eda792546..60de1003193aa 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -740,7 +740,7 @@ static struct platform_driver fsl_edma_driver = { .pm = &fsl_edma_pm_ops, }, .probe = fsl_edma_probe, - .remove_new = fsl_edma_remove, + .remove = fsl_edma_remove, }; static int __init fsl_edma_init(void) diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index 5005e138fc239..823f5c6bc2e1a 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -1288,7 +1288,7 @@ static struct platform_driver fsl_qdma_driver = { .of_match_table = fsl_qdma_dt_ids, }, .probe = fsl_qdma_probe, - .remove_new = fsl_qdma_remove, + .remove = fsl_qdma_remove, }; module_platform_driver(fsl_qdma_driver); diff --git a/drivers/dma/fsl_raid.c b/drivers/dma/fsl_raid.c index 014ff523d5ec9..6aa97e258a557 100644 --- a/drivers/dma/fsl_raid.c +++ b/drivers/dma/fsl_raid.c @@ -886,7 +886,7 @@ static struct platform_driver fsl_re_driver = { .of_match_table = fsl_re_ids, }, .probe = fsl_re_probe, - .remove_new = fsl_re_remove, + .remove = fsl_re_remove, }; module_platform_driver(fsl_re_driver); diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 18a6c4bf6275c..b5e7d18b97669 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1404,7 +1404,7 @@ static struct platform_driver fsldma_of_driver = { #endif }, .probe = fsldma_of_probe, - .remove_new = fsldma_of_remove, + .remove = fsldma_of_remove, }; /*----------------------------------------------------------------------------*/ diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c index 3c648308a54a3..d147353d47ab7 100644 --- a/drivers/dma/idma64.c +++ b/drivers/dma/idma64.c @@ -693,7 +693,7 @@ static const struct dev_pm_ops idma64_dev_pm_ops = { static struct platform_driver idma64_platform_driver = { .probe = idma64_platform_probe, - .remove_new = idma64_platform_remove, + .remove = idma64_platform_remove, .driver = { .name = LPSS_IDMA64_DRIVER_NAME, .pm = &idma64_dev_pm_ops, diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h index e16dbf9ab324c..c426511f21048 100644 --- a/drivers/dma/idxd/registers.h +++ b/drivers/dma/idxd/registers.h @@ -6,6 +6,10 @@ #include /* PCI Config */ +#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb +#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 +#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 + #define DEVICE_VERSION_1 0x100 #define DEVICE_VERSION_2 0x200 diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c index 0532dd2640dce..4127c1bdcca7c 100644 --- a/drivers/dma/img-mdc-dma.c +++ b/drivers/dma/img-mdc-dma.c @@ -1076,7 +1076,7 @@ static struct platform_driver mdc_dma_driver = { .of_match_table = of_match_ptr(mdc_dma_of_match), }, .probe = mdc_dma_probe, - .remove_new = mdc_dma_remove, + .remove = mdc_dma_remove, }; module_platform_driver(mdc_dma_driver); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index e913f0db99dad..a651e0995ce8d 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -1233,7 +1233,7 @@ static struct platform_driver imxdma_driver = { .name = "imx-dma", .of_match_table = imx_dma_of_dev_id, }, - .remove_new = imxdma_remove, + .remove = imxdma_remove, }; static int __init imxdma_module_init(void) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 72299a08af441..3449006cd14b5 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -2440,7 +2440,7 @@ static struct platform_driver sdma_driver = { .name = "imx-sdma", .of_match_table = sdma_dt_ids, }, - .remove_new = sdma_remove, + .remove = sdma_remove, .probe = sdma_probe, }; diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 5de8c21d41e7d..acc2983e28e0d 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -1028,7 +1028,7 @@ static struct platform_driver k3_pdma_driver = { .of_match_table = k3_pdma_dt_ids, }, .probe = k3_dma_probe, - .remove_new = k3_dma_remove, + .remove = k3_dma_remove, }; module_platform_driver(k3_pdma_driver); diff --git a/drivers/dma/ls2x-apb-dma.c b/drivers/dma/loongson2-apb-dma.c similarity index 99% rename from drivers/dma/ls2x-apb-dma.c rename to drivers/dma/loongson2-apb-dma.c index 9652e86667224..367ed34ce4da5 100644 --- a/drivers/dma/ls2x-apb-dma.c +++ b/drivers/dma/loongson2-apb-dma.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Driver for the Loongson LS2X APB DMA Controller + * Driver for the Loongson-2 APB DMA Controller * * Copyright (C) 2017-2023 Loongson Corporation */ @@ -692,7 +692,7 @@ MODULE_DEVICE_TABLE(of, ls2x_dma_of_match_table); static struct platform_driver ls2x_dmac_driver = { .probe = ls2x_dma_probe, - .remove_new = ls2x_dma_remove, + .remove = ls2x_dma_remove, .driver = { .name = "ls2x-apbdma", .of_match_table = ls2x_dma_of_match_table, @@ -700,6 +700,6 @@ static struct platform_driver ls2x_dmac_driver = { }; module_platform_driver(ls2x_dmac_driver); -MODULE_DESCRIPTION("Loongson LS2X APB DMA Controller driver"); +MODULE_DESCRIPTION("Loongson-2 APB DMA Controller driver"); MODULE_AUTHOR("Loongson Technology Corporation Limited"); MODULE_LICENSE("GPL"); diff --git a/drivers/dma/mcf-edma-main.c b/drivers/dma/mcf-edma-main.c index 0c5862bf26f8a..9e1c6400c77be 100644 --- a/drivers/dma/mcf-edma-main.c +++ b/drivers/dma/mcf-edma-main.c @@ -267,7 +267,7 @@ static struct platform_driver mcf_edma_driver = { .name = "mcf-edma", }, .probe = mcf_edma_probe, - .remove_new = mcf_edma_remove, + .remove = mcf_edma_remove, }; bool mcf_edma_filter_fn(struct dma_chan *chan, void *param) diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cqdma.c index b69eabf12a24f..d5ddb4e30e715 100644 --- a/drivers/dma/mediatek/mtk-cqdma.c +++ b/drivers/dma/mediatek/mtk-cqdma.c @@ -922,7 +922,7 @@ static void mtk_cqdma_remove(struct platform_device *pdev) static struct platform_driver mtk_cqdma_driver = { .probe = mtk_cqdma_probe, - .remove_new = mtk_cqdma_remove, + .remove = mtk_cqdma_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = mtk_cqdma_match, diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hsdma.c index 58c7961ab9ad1..fa77bb24a4307 100644 --- a/drivers/dma/mediatek/mtk-hsdma.c +++ b/drivers/dma/mediatek/mtk-hsdma.c @@ -1038,7 +1038,7 @@ static void mtk_hsdma_remove(struct platform_device *pdev) static struct platform_driver mtk_hsdma_driver = { .probe = mtk_hsdma_probe, - .remove_new = mtk_hsdma_remove, + .remove = mtk_hsdma_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = mtk_hsdma_match, diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c index 1bdc1500be40f..08e15177427b9 100644 --- a/drivers/dma/mediatek/mtk-uart-apdma.c +++ b/drivers/dma/mediatek/mtk-uart-apdma.c @@ -637,7 +637,7 @@ static const struct dev_pm_ops mtk_uart_apdma_pm_ops = { static struct platform_driver mtk_uart_apdma_driver = { .probe = mtk_uart_apdma_probe, - .remove_new = mtk_uart_apdma_remove, + .remove = mtk_uart_apdma_remove, .driver = { .name = KBUILD_MODNAME, .pm = &mtk_uart_apdma_pm_ops, diff --git a/drivers/dma/milbeaut-hdmac.c b/drivers/dma/milbeaut-hdmac.c index 7b41c670970a6..9a5ec247ed6d9 100644 --- a/drivers/dma/milbeaut-hdmac.c +++ b/drivers/dma/milbeaut-hdmac.c @@ -571,7 +571,7 @@ MODULE_DEVICE_TABLE(of, milbeaut_hdmac_match); static struct platform_driver milbeaut_hdmac_driver = { .probe = milbeaut_hdmac_probe, - .remove_new = milbeaut_hdmac_remove, + .remove = milbeaut_hdmac_remove, .driver = { .name = "milbeaut-m10v-hdmac", .of_match_table = milbeaut_hdmac_match, diff --git a/drivers/dma/milbeaut-xdmac.c b/drivers/dma/milbeaut-xdmac.c index 2cce529b448eb..58d4fd6df0bf4 100644 --- a/drivers/dma/milbeaut-xdmac.c +++ b/drivers/dma/milbeaut-xdmac.c @@ -409,7 +409,7 @@ MODULE_DEVICE_TABLE(of, milbeaut_xdmac_match); static struct platform_driver milbeaut_xdmac_driver = { .probe = milbeaut_xdmac_probe, - .remove_new = milbeaut_xdmac_remove, + .remove = milbeaut_xdmac_remove, .driver = { .name = "milbeaut-m10v-xdmac", .of_match_table = milbeaut_xdmac_match, diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 136fcaeff8dda..a95d31103d306 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -1137,7 +1137,7 @@ static struct platform_driver mmp_pdma_driver = { }, .id_table = mmp_pdma_id_table, .probe = mmp_pdma_probe, - .remove_new = mmp_pdma_remove, + .remove = mmp_pdma_remove, }; module_platform_driver(mmp_pdma_driver); diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index b76fe99e11516..c8dc504510f1e 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -736,7 +736,7 @@ static struct platform_driver mmp_tdma_driver = { .of_match_table = mmp_tdma_dt_ids, }, .probe = mmp_tdma_probe, - .remove_new = mmp_tdma_remove, + .remove = mmp_tdma_remove, }; module_platform_driver(mmp_tdma_driver); diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c index 66dc6d31b603d..de09e1ab77676 100644 --- a/drivers/dma/moxart-dma.c +++ b/drivers/dma/moxart-dma.c @@ -644,7 +644,7 @@ MODULE_DEVICE_TABLE(of, moxart_dma_match); static struct platform_driver moxart_driver = { .probe = moxart_probe, - .remove_new = moxart_remove, + .remove = moxart_remove, .driver = { .name = "moxart-dma-engine", .of_match_table = moxart_dma_match, diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 68c247a46321c..bf131cb5db664 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -1110,7 +1110,7 @@ MODULE_DEVICE_TABLE(of, mpc_dma_match); static struct platform_driver mpc_dma_driver = { .probe = mpc_dma_probe, - .remove_new = mpc_dma_remove, + .remove = mpc_dma_remove, .driver = { .name = DRV_NAME, .of_match_table = mpc_dma_match, diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c index c8c67f4d982c0..cad4d4fb51ac3 100644 --- a/drivers/dma/mv_xor_v2.c +++ b/drivers/dma/mv_xor_v2.c @@ -635,7 +635,7 @@ static int mv_xor_v2_descq_init(struct mv_xor_v2_device *xor_dev) writel(MV_XOR_V2_DESC_NUM, xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_SIZE_OFF); - /* write the DESQ address to the DMA enngine*/ + /* write the DESQ address to the DMA engine*/ writel(lower_32_bits(xor_dev->hw_desq), xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_BALR_OFF); writel(upper_32_bits(xor_dev->hw_desq), @@ -884,7 +884,7 @@ static struct platform_driver mv_xor_v2_driver = { .probe = mv_xor_v2_probe, .suspend = mv_xor_v2_suspend, .resume = mv_xor_v2_resume, - .remove_new = mv_xor_v2_remove, + .remove = mv_xor_v2_remove, .driver = { .name = "mv_xor_v2", .of_match_table = of_match_ptr(mv_xor_v2_dt_ids), diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 3b011a91d48ec..0d6324c4e2be0 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -1515,7 +1515,7 @@ static struct platform_driver nbpf_driver = { }, .id_table = nbpf_ids, .probe = nbpf_probe, - .remove_new = nbpf_remove, + .remove = nbpf_remove, }; module_platform_driver(nbpf_driver); diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index aa436f9e35718..57cec757d8f56 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -1252,7 +1252,7 @@ static void owl_dma_remove(struct platform_device *pdev) static struct platform_driver owl_dma_driver = { .probe = owl_dma_probe, - .remove_new = owl_dma_remove, + .remove = owl_dma_remove, .driver = { .name = "dma-owl", .of_match_table = of_match_ptr(owl_dma_match), diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 7b78759ac734d..9d2a5a967a99c 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -4549,7 +4549,7 @@ MODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match); static struct platform_driver ppc440spe_adma_driver = { .probe = ppc440spe_adma_probe, - .remove_new = ppc440spe_adma_remove, + .remove = ppc440spe_adma_remove, .driver = { .name = "PPC440SP(E)-ADMA", .of_match_table = ppc440spe_adma_of_match, diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 31f8da810c055..e50cf3357e5ef 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -1442,7 +1442,7 @@ static struct platform_driver pxad_driver = { }, .id_table = pxad_id_table, .probe = pxad_probe, - .remove_new = pxad_remove, + .remove = pxad_remove, }; static bool pxad_filter_fn(struct dma_chan *chan, void *param) diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index d43a881e43b90..bbc3276992bb0 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -1469,7 +1469,7 @@ static const struct dev_pm_ops bam_dma_pm_ops = { static struct platform_driver bam_dma_driver = { .probe = bam_dma_probe, - .remove_new = bam_dma_remove, + .remove = bam_dma_remove, .driver = { .name = "bam-dma-engine", .pm = &bam_dma_pm_ops, diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 4d2cd8d9ec74a..c2b3e4452e715 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -948,7 +948,7 @@ MODULE_DEVICE_TABLE(acpi, hidma_acpi_ids); static struct platform_driver hidma_driver = { .probe = hidma_probe, - .remove_new = hidma_remove, + .remove = hidma_remove, .shutdown = hidma_shutdown, .driver = { .name = "hidma", diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c index c1db398adc84d..6be54fddcee18 100644 --- a/drivers/dma/qcom/qcom_adm.c +++ b/drivers/dma/qcom/qcom_adm.c @@ -937,7 +937,7 @@ MODULE_DEVICE_TABLE(of, adm_of_match); static struct platform_driver adm_dma_driver = { .probe = adm_dma_probe, - .remove_new = adm_dma_remove, + .remove = adm_dma_remove, .driver = { .name = "adm-dma-engine", .of_match_table = adm_of_match, diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index 01e656c69e6c1..dc1a9a05252ec 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -1079,7 +1079,7 @@ static struct platform_driver sa11x0_dma_driver = { .pm = &sa11x0_dma_pm_ops, }, .probe = sa11x0_dma_probe, - .remove_new = sa11x0_dma_remove, + .remove = sa11x0_dma_remove, }; static int __init sa11x0_dma_init(void) diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c index 428473611115d..7ad3c29be1467 100644 --- a/drivers/dma/sf-pdma/sf-pdma.c +++ b/drivers/dma/sf-pdma/sf-pdma.c @@ -354,7 +354,7 @@ static irqreturn_t sf_pdma_done_isr(int irq, void *dev_id) if (!residue) { tasklet_hi_schedule(&chan->done_tasklet); } else { - /* submit next trascatioin if possible */ + /* submit next transaction if possible */ struct sf_pdma_desc *desc = chan->desc; desc->src_addr += desc->xfer_size - residue; @@ -633,7 +633,7 @@ MODULE_DEVICE_TABLE(of, sf_pdma_dt_ids); static struct platform_driver sf_pdma_driver = { .probe = sf_pdma_probe, - .remove_new = sf_pdma_remove, + .remove = sf_pdma_remove, .driver = { .name = "sf-pdma", .of_match_table = sf_pdma_dt_ids, diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index c0b2997ab7fd9..6ea5a880b4339 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -49,10 +49,10 @@ config RENESAS_USB_DMAC SoCs. config RZ_DMAC - tristate "Renesas RZ/{G2L,V2L} DMA Controller" - depends on ARCH_RZG2L || COMPILE_TEST + tristate "Renesas RZ DMA Controller" + depends on ARCH_R7S72100 || ARCH_RZG2L || COMPILE_TEST select RENESAS_DMA select DMA_VIRTUAL_CHANNELS help - This driver supports the general purpose DMA controller found in the - Renesas RZ/{G2L,V2L} SoC variants. + This driver supports the general purpose DMA controller typically + found in the Renesas RZ SoC variants. diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 1094a2f821649..2679c1f09faf0 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -2037,7 +2037,7 @@ static struct platform_driver rcar_dmac_driver = { .of_match_table = rcar_dmac_of_ids, }, .probe = rcar_dmac_probe, - .remove_new = rcar_dmac_remove, + .remove = rcar_dmac_remove, .shutdown = rcar_dmac_shutdown, }; diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 811389fc9cb82..9235db5510266 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -893,7 +893,7 @@ static int rz_dmac_probe(struct platform_device *pdev) /* Initialize the channels. */ INIT_LIST_HEAD(&dmac->engine.channels); - dmac->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + dmac->rstc = devm_reset_control_array_get_optional_exclusive(&pdev->dev); if (IS_ERR(dmac->rstc)) return dev_err_probe(&pdev->dev, PTR_ERR(dmac->rstc), "failed to get resets\n"); @@ -1004,7 +1004,7 @@ static struct platform_driver rz_dmac_driver = { .of_match_table = of_rz_dmac_match, }, .probe = rz_dmac_probe, - .remove_new = rz_dmac_remove, + .remove = rz_dmac_remove, }; module_platform_driver(rz_dmac_driver); diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 588c5f409a808..fdd41e1c22636 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -961,7 +961,7 @@ void shdma_chan_probe(struct shdma_dev *sdev, spin_lock_init(&schan->chan_lock); - /* Init descripter manage list */ + /* Init descriptor manage list */ INIT_LIST_HEAD(&schan->ld_queue); INIT_LIST_HEAD(&schan->ld_free); diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 8ead0a1fd2371..093e449e19eee 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -906,7 +906,7 @@ static struct platform_driver sh_dmae_driver = { .pm = &sh_dmae_pm, .name = SH_DMAE_DRV_NAME, }, - .remove_new = sh_dmae_remove, + .remove = sh_dmae_remove, }; static int __init sh_dmae_init(void) diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index f7cd0cad056c1..7e2b6c97fa2f9 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -301,7 +301,7 @@ static struct usb_dmac_desc *usb_dmac_desc_get(struct usb_dmac_chan *chan, struct usb_dmac_desc *desc = NULL; unsigned long flags; - /* Get a freed descritpor */ + /* Get a freed descriptor */ spin_lock_irqsave(&chan->vc.lock, flags); list_for_each_entry(desc, &chan->desc_freed, node) { if (sg_len <= desc->sg_allocated_len) { @@ -899,7 +899,7 @@ static struct platform_driver usb_dmac_driver = { .of_match_table = usb_dmac_of_ids, }, .probe = usb_dmac_probe, - .remove_new = usb_dmac_remove, + .remove = usb_dmac_remove, .shutdown = usb_dmac_shutdown, }; diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 3f54ff37c5e05..187a090463ced 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -1298,7 +1298,7 @@ static const struct dev_pm_ops sprd_dma_pm_ops = { static struct platform_driver sprd_dma_driver = { .probe = sprd_dma_probe, - .remove_new = sprd_dma_remove, + .remove = sprd_dma_remove, .driver = { .name = "sprd-dma", .of_match_table = sprd_dma_match, diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c index 8880b5e336f8d..c65ee0c7bfbdb 100644 --- a/drivers/dma/st_fdma.c +++ b/drivers/dma/st_fdma.c @@ -858,7 +858,7 @@ static struct platform_driver st_fdma_platform_driver = { .of_match_table = st_fdma_match, }, .probe = st_fdma_probe, - .remove_new = st_fdma_remove, + .remove = st_fdma_remove, }; module_platform_driver(st_fdma_platform_driver); diff --git a/drivers/dma/stm32/stm32-dma3.c b/drivers/dma/stm32/stm32-dma3.c index 0be6e944df6fd..0c6c4258b1956 100644 --- a/drivers/dma/stm32/stm32-dma3.c +++ b/drivers/dma/stm32/stm32-dma3.c @@ -221,6 +221,8 @@ enum stm32_dma3_port_data_width { #define STM32_DMA3_DT_BREQ BIT(8) /* CTR2_BREQ */ #define STM32_DMA3_DT_PFREQ BIT(9) /* CTR2_PFREQ */ #define STM32_DMA3_DT_TCEM GENMASK(13, 12) /* CTR2_TCEM */ +#define STM32_DMA3_DT_NOPACK BIT(16) /* CTR1_PAM */ +#define STM32_DMA3_DT_NOREFACT BIT(17) /* struct stm32_dma3_chan .config_set bitfield */ #define STM32_DMA3_CFG_SET_DT BIT(0) @@ -228,6 +230,8 @@ enum stm32_dma3_port_data_width { #define STM32_DMA3_CFG_SET_BOTH (STM32_DMA3_CFG_SET_DT | STM32_DMA3_CFG_SET_DMA) #define STM32_DMA3_MAX_BLOCK_SIZE ALIGN_DOWN(CBR1_BNDT, 64) +#define STM32_DMA3_MAX_BURST_LEN (1 + min_t(u32, FIELD_MAX(CTR1_SBL_1), \ + FIELD_MAX(CTR1_DBL_1))) #define port_is_ahb(maxdw) ({ typeof(maxdw) (_maxdw) = (maxdw); \ ((_maxdw) != DW_INVALID) && ((_maxdw) == DW_32); }) #define port_is_axi(maxdw) ({ typeof(maxdw) (_maxdw) = (maxdw); \ @@ -293,6 +297,10 @@ struct stm32_dma3_chan { u32 dma_status; }; +struct stm32_dma3_pdata { + u32 axi_max_burst_len; +}; + struct stm32_dma3_ddata { struct dma_device dma_dev; void __iomem *base; @@ -301,6 +309,7 @@ struct stm32_dma3_ddata { u32 dma_channels; u32 dma_requests; enum stm32_dma3_port_data_width ports_max_dw[2]; + u32 axi_max_burst_len; }; static inline struct stm32_dma3_ddata *to_stm32_dma3_ddata(struct stm32_dma3_chan *chan) @@ -533,7 +542,8 @@ static enum dma_slave_buswidth stm32_dma3_get_max_dw(u32 chan_max_burst, return 1 << __ffs(len | addr | max_dw); } -static u32 stm32_dma3_get_max_burst(u32 len, enum dma_slave_buswidth dw, u32 chan_max_burst) +static u32 stm32_dma3_get_max_burst(u32 len, enum dma_slave_buswidth dw, + u32 chan_max_burst, u32 bus_max_burst) { u32 max_burst = chan_max_burst ? chan_max_burst / dw : 1; @@ -544,8 +554,9 @@ static u32 stm32_dma3_get_max_burst(u32 len, enum dma_slave_buswidth dw, u32 cha /* * HW doesn't modify the burst if burst size <= half of the fifo size. * If len is not a multiple of burst size, last burst is shortened by HW. + * Take care of maximum burst supported on interconnect bus. */ - return max_burst; + return min_t(u32, max_burst, bus_max_burst); } static int stm32_dma3_chan_prep_hw(struct stm32_dma3_chan *chan, enum dma_transfer_direction dir, @@ -554,6 +565,7 @@ static int stm32_dma3_chan_prep_hw(struct stm32_dma3_chan *chan, enum dma_transf { struct stm32_dma3_ddata *ddata = to_stm32_dma3_ddata(chan); struct dma_device dma_device = ddata->dma_dev; + u32 src_max_burst = STM32_DMA3_MAX_BURST_LEN, dst_max_burst = STM32_DMA3_MAX_BURST_LEN; u32 sdw, ddw, sbl_max, dbl_max, tcem, init_dw, init_bl_max; u32 _ctr1 = 0, _ctr2 = 0; u32 ch_conf = chan->dt_config.ch_conf; @@ -594,10 +606,14 @@ static int stm32_dma3_chan_prep_hw(struct stm32_dma3_chan *chan, enum dma_transf _ctr1 |= CTR1_SINC; if (sap) _ctr1 |= CTR1_SAP; + if (port_is_axi(sap_max_dw)) /* AXI - apply axi maximum burst limitation */ + src_max_burst = ddata->axi_max_burst_len; if (FIELD_GET(STM32_DMA3_DT_DINC, tr_conf)) _ctr1 |= CTR1_DINC; if (dap) _ctr1 |= CTR1_DAP; + if (port_is_axi(dap_max_dw)) /* AXI - apply axi maximum burst limitation */ + dst_max_burst = ddata->axi_max_burst_len; _ctr2 |= FIELD_PREP(CTR2_REQSEL, chan->dt_config.req_line) & ~CTR2_SWREQ; if (FIELD_GET(STM32_DMA3_DT_BREQ, tr_conf)) @@ -617,11 +633,16 @@ static int stm32_dma3_chan_prep_hw(struct stm32_dma3_chan *chan, enum dma_transf /* Set destination (device) data width and burst */ ddw = min_t(u32, ddw, stm32_dma3_get_max_dw(chan->max_burst, dap_max_dw, len, dst_addr)); - dbl_max = min_t(u32, dbl_max, stm32_dma3_get_max_burst(len, ddw, chan->max_burst)); + dbl_max = min_t(u32, dbl_max, stm32_dma3_get_max_burst(len, ddw, chan->max_burst, + dst_max_burst)); /* Set source (memory) data width and burst */ sdw = stm32_dma3_get_max_dw(chan->max_burst, sap_max_dw, len, src_addr); - sbl_max = stm32_dma3_get_max_burst(len, sdw, chan->max_burst); + sbl_max = stm32_dma3_get_max_burst(len, sdw, chan->max_burst, src_max_burst); + if (!!FIELD_GET(STM32_DMA3_DT_NOPACK, tr_conf)) { + sdw = ddw; + sbl_max = dbl_max; + } _ctr1 |= FIELD_PREP(CTR1_SDW_LOG2, ilog2(sdw)); _ctr1 |= FIELD_PREP(CTR1_SBL_1, sbl_max - 1); @@ -647,11 +668,17 @@ static int stm32_dma3_chan_prep_hw(struct stm32_dma3_chan *chan, enum dma_transf /* Set source (device) data width and burst */ sdw = min_t(u32, sdw, stm32_dma3_get_max_dw(chan->max_burst, sap_max_dw, len, src_addr)); - sbl_max = min_t(u32, sbl_max, stm32_dma3_get_max_burst(len, sdw, chan->max_burst)); + sbl_max = min_t(u32, sbl_max, stm32_dma3_get_max_burst(len, sdw, chan->max_burst, + src_max_burst)); /* Set destination (memory) data width and burst */ ddw = stm32_dma3_get_max_dw(chan->max_burst, dap_max_dw, len, dst_addr); - dbl_max = stm32_dma3_get_max_burst(len, ddw, chan->max_burst); + dbl_max = stm32_dma3_get_max_burst(len, ddw, chan->max_burst, dst_max_burst); + if (!!FIELD_GET(STM32_DMA3_DT_NOPACK, tr_conf) || + ((_ctr2 & CTR2_PFREQ) && ddw > sdw)) { /* Packing to wider ddw not supported */ + ddw = sdw; + dbl_max = sbl_max; + } _ctr1 |= FIELD_PREP(CTR1_SDW_LOG2, ilog2(sdw)); _ctr1 |= FIELD_PREP(CTR1_SBL_1, sbl_max - 1); @@ -678,22 +705,24 @@ static int stm32_dma3_chan_prep_hw(struct stm32_dma3_chan *chan, enum dma_transf init_dw = sdw; init_bl_max = sbl_max; sdw = stm32_dma3_get_max_dw(chan->max_burst, sap_max_dw, len, src_addr); - sbl_max = stm32_dma3_get_max_burst(len, sdw, chan->max_burst); + sbl_max = stm32_dma3_get_max_burst(len, sdw, chan->max_burst, src_max_burst); if (chan->config_set & STM32_DMA3_CFG_SET_DMA) { sdw = min_t(u32, init_dw, sdw); - sbl_max = min_t(u32, init_bl_max, - stm32_dma3_get_max_burst(len, sdw, chan->max_burst)); + sbl_max = min_t(u32, init_bl_max, stm32_dma3_get_max_burst(len, sdw, + chan->max_burst, + src_max_burst)); } /* Set destination (memory) data width and burst */ init_dw = ddw; init_bl_max = dbl_max; ddw = stm32_dma3_get_max_dw(chan->max_burst, dap_max_dw, len, dst_addr); - dbl_max = stm32_dma3_get_max_burst(len, ddw, chan->max_burst); + dbl_max = stm32_dma3_get_max_burst(len, ddw, chan->max_burst, dst_max_burst); if (chan->config_set & STM32_DMA3_CFG_SET_DMA) { ddw = min_t(u32, init_dw, ddw); - dbl_max = min_t(u32, init_bl_max, - stm32_dma3_get_max_burst(len, ddw, chan->max_burst)); + dbl_max = min_t(u32, init_bl_max, stm32_dma3_get_max_burst(len, ddw, + chan->max_burst, + dst_max_burst)); } _ctr1 |= FIELD_PREP(CTR1_SDW_LOG2, ilog2(sdw)); @@ -1116,6 +1145,28 @@ static void stm32_dma3_free_chan_resources(struct dma_chan *c) chan->config_set = 0; } +static u32 stm32_dma3_get_ll_count(struct stm32_dma3_chan *chan, size_t len, bool prevent_refactor) +{ + u32 count; + + if (prevent_refactor) + return DIV_ROUND_UP(len, STM32_DMA3_MAX_BLOCK_SIZE); + + count = len / STM32_DMA3_MAX_BLOCK_SIZE; + len -= (len / STM32_DMA3_MAX_BLOCK_SIZE) * STM32_DMA3_MAX_BLOCK_SIZE; + + if (len >= chan->max_burst) { + count += 1; /* len < STM32_DMA3_MAX_BLOCK_SIZE here, so it fits in one item */ + len -= (len / chan->max_burst) * chan->max_burst; + } + + /* Unaligned remainder fits in one extra item */ + if (len > 0) + count += 1; + + return count; +} + static void stm32_dma3_init_chan_config_for_memcpy(struct stm32_dma3_chan *chan, dma_addr_t dst, dma_addr_t src) { @@ -1150,8 +1201,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_dma_memcpy(struct dma_cha struct stm32_dma3_swdesc *swdesc; size_t next_size, offset; u32 count, i, ctr1, ctr2; + bool prevent_refactor = !!FIELD_GET(STM32_DMA3_DT_NOPACK, chan->dt_config.tr_conf) || + !!FIELD_GET(STM32_DMA3_DT_NOREFACT, chan->dt_config.tr_conf); - count = DIV_ROUND_UP(len, STM32_DMA3_MAX_BLOCK_SIZE); + count = stm32_dma3_get_ll_count(chan, len, prevent_refactor); swdesc = stm32_dma3_chan_desc_alloc(chan, count); if (!swdesc) @@ -1167,6 +1220,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_dma_memcpy(struct dma_cha remaining = len - offset; next_size = min_t(size_t, remaining, STM32_DMA3_MAX_BLOCK_SIZE); + if (!prevent_refactor && + (next_size < STM32_DMA3_MAX_BLOCK_SIZE && next_size >= chan->max_burst)) + next_size = chan->max_burst * (remaining / chan->max_burst); + ret = stm32_dma3_chan_prep_hw(chan, DMA_MEM_TO_MEM, &swdesc->ccr, &ctr1, &ctr2, src + offset, dst + offset, next_size); if (ret) @@ -1203,14 +1260,13 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_slave_sg(struct dma_chan size_t len; dma_addr_t sg_addr, dev_addr, src, dst; u32 i, j, count, ctr1, ctr2; + bool prevent_refactor = !!FIELD_GET(STM32_DMA3_DT_NOPACK, chan->dt_config.tr_conf) || + !!FIELD_GET(STM32_DMA3_DT_NOREFACT, chan->dt_config.tr_conf); int ret; - count = sg_len; - for_each_sg(sgl, sg, sg_len, i) { - len = sg_dma_len(sg); - if (len > STM32_DMA3_MAX_BLOCK_SIZE) - count += DIV_ROUND_UP(len, STM32_DMA3_MAX_BLOCK_SIZE) - 1; - } + count = 0; + for_each_sg(sgl, sg, sg_len, i) + count += stm32_dma3_get_ll_count(chan, sg_dma_len(sg), prevent_refactor); swdesc = stm32_dma3_chan_desc_alloc(chan, count); if (!swdesc) @@ -1227,6 +1283,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_slave_sg(struct dma_chan do { size_t chunk = min_t(size_t, len, STM32_DMA3_MAX_BLOCK_SIZE); + if (!prevent_refactor && + (chunk < STM32_DMA3_MAX_BLOCK_SIZE && chunk >= chan->max_burst)) + chunk = chan->max_burst * (len / chan->max_burst); + if (dir == DMA_MEM_TO_DEV) { src = sg_addr; dst = dev_addr; @@ -1259,6 +1319,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_slave_sg(struct dma_chan } while (len); } + if (count != sg_len && chan->tcem != CTR2_TCEM_CHANNEL) + dev_warn(chan2dev(chan), "Linked-list refactored, %d items instead of %d\n", + count, sg_len); + /* Enable Error interrupts */ swdesc->ccr |= CCR_USEIE | CCR_ULEIE | CCR_DTEIE; /* Enable Transfer state interrupts */ @@ -1601,8 +1665,12 @@ static u32 stm32_dma3_check_rif(struct stm32_dma3_ddata *ddata) return chan_reserved; } +static struct stm32_dma3_pdata stm32mp25_pdata = { + .axi_max_burst_len = 16, +}; + static const struct of_device_id stm32_dma3_of_match[] = { - { .compatible = "st,stm32mp25-dma3", }, + { .compatible = "st,stm32mp25-dma3", .data = &stm32mp25_pdata, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, stm32_dma3_of_match); @@ -1610,6 +1678,7 @@ MODULE_DEVICE_TABLE(of, stm32_dma3_of_match); static int stm32_dma3_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + const struct stm32_dma3_pdata *pdata; struct stm32_dma3_ddata *ddata; struct reset_control *reset; struct stm32_dma3_chan *chan; @@ -1704,6 +1773,16 @@ static int stm32_dma3_probe(struct platform_device *pdev) else /* Dual master ports */ ddata->ports_max_dw[1] = FIELD_GET(G_M1_DATA_WIDTH_ENC, hwcfgr); + /* axi_max_burst_len is optional, if not defined, use STM32_DMA3_MAX_BURST_LEN */ + ddata->axi_max_burst_len = STM32_DMA3_MAX_BURST_LEN; + pdata = device_get_match_data(&pdev->dev); + if (pdata && pdata->axi_max_burst_len) { + ddata->axi_max_burst_len = min_t(u32, pdata->axi_max_burst_len, + STM32_DMA3_MAX_BURST_LEN); + dev_dbg(&pdev->dev, "Burst is limited to %u beats through AXI port\n", + ddata->axi_max_burst_len); + } + ddata->chans = devm_kcalloc(&pdev->dev, ddata->dma_channels, sizeof(*ddata->chans), GFP_KERNEL); if (!ddata->chans) { @@ -1827,7 +1906,7 @@ static const struct dev_pm_ops stm32_dma3_pm_ops = { static struct platform_driver stm32_dma3_driver = { .probe = stm32_dma3_probe, - .remove_new = stm32_dma3_remove, + .remove = stm32_dma3_remove, .driver = { .name = "stm32-dma3", .of_match_table = stm32_dma3_of_match, diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c index 2e7f9b07fdd29..f37cdf6f21796 100644 --- a/drivers/dma/sun4i-dma.c +++ b/drivers/dma/sun4i-dma.c @@ -1292,7 +1292,7 @@ MODULE_DEVICE_TABLE(of, sun4i_dma_match); static struct platform_driver sun4i_dma_driver = { .probe = sun4i_dma_probe, - .remove_new = sun4i_dma_remove, + .remove = sun4i_dma_remove, .driver = { .name = "sun4i-dma", .of_match_table = sun4i_dma_match, diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 583bf49031cf2..95ecb12caaa5e 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -1488,7 +1488,7 @@ static void sun6i_dma_remove(struct platform_device *pdev) static struct platform_driver sun6i_dma_driver = { .probe = sun6i_dma_probe, - .remove_new = sun6i_dma_remove, + .remove = sun6i_dma_remove, .driver = { .name = "sun6i-dma", .of_match_table = sun6i_dma_match, diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 3642508e88bb2..cacf3757adc2e 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -1532,7 +1532,7 @@ static struct platform_driver tegra_dma_driver = { .of_match_table = tegra_dma_of_match, }, .probe = tegra_dma_probe, - .remove_new = tegra_dma_remove, + .remove = tegra_dma_remove, }; module_platform_driver(tegra_dma_driver); diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 7d1acda2d72b3..14a61e53a41b7 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1675,7 +1675,7 @@ static struct platform_driver tegra_dmac_driver = { .of_match_table = tegra_dma_of_match, }, .probe = tegra_dma_probe, - .remove_new = tegra_dma_remove, + .remove = tegra_dma_remove, }; module_platform_driver(tegra_dmac_driver); diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index 24ad7077c53ba..2953008d42efa 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -1008,7 +1008,7 @@ static struct platform_driver tegra_admac_driver = { .of_match_table = tegra_adma_of_match, }, .probe = tegra_adma_probe, - .remove_new = tegra_adma_remove, + .remove = tegra_adma_remove, }; module_platform_driver(tegra_admac_driver); diff --git a/drivers/dma/ti/cppi41.c b/drivers/dma/ti/cppi41.c index a8bb70c2d1099..8d8c3d6038fcc 100644 --- a/drivers/dma/ti/cppi41.c +++ b/drivers/dma/ti/cppi41.c @@ -1243,7 +1243,7 @@ static const struct dev_pm_ops cppi41_pm_ops = { static struct platform_driver cpp41_dma_driver = { .probe = cppi41_dma_probe, - .remove_new = cppi41_dma_remove, + .remove = cppi41_dma_remove, .driver = { .name = "cppi41-dma-engine", .pm = &cppi41_pm_ops, diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index 5f8d2e93ff3fb..343e986e66e7c 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -2636,7 +2636,7 @@ static const struct dev_pm_ops edma_pm_ops = { static struct platform_driver edma_driver = { .probe = edma_probe, - .remove_new = edma_remove, + .remove = edma_remove, .driver = { .name = "edma", .pm = &edma_pm_ops, diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c index 6ab9bfbdc4809..8c023c6e623a5 100644 --- a/drivers/dma/ti/omap-dma.c +++ b/drivers/dma/ti/omap-dma.c @@ -1915,7 +1915,7 @@ MODULE_DEVICE_TABLE(of, omap_dma_match); static struct platform_driver omap_dma_driver = { .probe = omap_dma_probe, - .remove_new = omap_dma_remove, + .remove = omap_dma_remove, .driver = { .name = "omap-dma-engine", .of_match_table = omap_dma_match, diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 7410025605e03..ecaf002558afc 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -761,7 +761,7 @@ static struct platform_driver td_driver = { .name = DRIVER_NAME, }, .probe = td_probe, - .remove_new = td_remove, + .remove = td_remove, }; module_platform_driver(td_driver); diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 44ba377b4b5a5..35d5221683b28 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -1260,14 +1260,14 @@ static const struct dev_pm_ops txx9dmac_dev_pm_ops = { }; static struct platform_driver txx9dmac_chan_driver = { - .remove_new = txx9dmac_chan_remove, + .remove = txx9dmac_chan_remove, .driver = { .name = "txx9dmac-chan", }, }; static struct platform_driver txx9dmac_driver = { - .remove_new = txx9dmac_remove, + .remove = txx9dmac_remove, .shutdown = txx9dmac_shutdown, .driver = { .name = "txx9dmac", diff --git a/drivers/dma/uniphier-mdmac.c b/drivers/dma/uniphier-mdmac.c index ad7125f6e2ca8..7a99f86ecb5a0 100644 --- a/drivers/dma/uniphier-mdmac.c +++ b/drivers/dma/uniphier-mdmac.c @@ -493,7 +493,7 @@ MODULE_DEVICE_TABLE(of, uniphier_mdmac_match); static struct platform_driver uniphier_mdmac_driver = { .probe = uniphier_mdmac_probe, - .remove_new = uniphier_mdmac_remove, + .remove = uniphier_mdmac_remove, .driver = { .name = "uniphier-mio-dmac", .of_match_table = uniphier_mdmac_match, diff --git a/drivers/dma/uniphier-xdmac.c b/drivers/dma/uniphier-xdmac.c index 3ce2dc2ad9de4..ceeb6171c9d11 100644 --- a/drivers/dma/uniphier-xdmac.c +++ b/drivers/dma/uniphier-xdmac.c @@ -603,7 +603,7 @@ MODULE_DEVICE_TABLE(of, uniphier_xdmac_match); static struct platform_driver uniphier_xdmac_driver = { .probe = uniphier_xdmac_probe, - .remove_new = uniphier_xdmac_remove, + .remove = uniphier_xdmac_remove, .driver = { .name = "uniphier-xdmac", .of_match_table = uniphier_xdmac_match, diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index 275848a9c4502..f64624ea44add 100644 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -1815,7 +1815,7 @@ MODULE_DEVICE_TABLE(of, xgene_dma_of_match_ptr); static struct platform_driver xgene_dma_driver = { .probe = xgene_dma_probe, - .remove_new = xgene_dma_remove, + .remove = xgene_dma_remove, .driver = { .name = "X-Gene-DMA", .of_match_table = xgene_dma_of_match_ptr, diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c index 718842fdaf98e..93772abc3b49f 100644 --- a/drivers/dma/xilinx/xdma.c +++ b/drivers/dma/xilinx/xdma.c @@ -1315,7 +1315,7 @@ static struct platform_driver xdma_driver = { }, .id_table = xdma_id_table, .probe = xdma_probe, - .remove_new = xdma_remove, + .remove = xdma_remove, }; module_platform_driver(xdma_driver); diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 5eb51ae93e89d..1bdd57de87a6e 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -3271,7 +3271,7 @@ static struct platform_driver xilinx_vdma_driver = { .of_match_table = xilinx_dma_of_ids, }, .probe = xilinx_dma_probe, - .remove_new = xilinx_dma_remove, + .remove = xilinx_dma_remove, }; module_platform_driver(xilinx_vdma_driver); diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index be87764af9e83..ee5d9fdbfd7fa 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -1863,7 +1863,7 @@ MODULE_DEVICE_TABLE(of, xilinx_dpdma_of_match); static struct platform_driver xilinx_dpdma_driver = { .probe = xilinx_dpdma_probe, - .remove_new = xilinx_dpdma_remove, + .remove = xilinx_dpdma_remove, .driver = { .name = "xilinx-zynqmp-dpdma", .of_match_table = xilinx_dpdma_of_match, diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index 9ae46f1198fe6..d05fc5fcc77dc 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -366,7 +366,7 @@ static void zynqmp_dma_init(struct zynqmp_dma_chan *chan) } writel(val, chan->regs + ZYNQMP_DMA_DATA_ATTR); - /* Clearing the interrupt account rgisters */ + /* Clearing the interrupt account registers */ val = readl(chan->regs + ZYNQMP_DMA_IRQ_SRC_ACCT); val = readl(chan->regs + ZYNQMP_DMA_IRQ_DST_ACCT); @@ -1192,7 +1192,7 @@ static struct platform_driver zynqmp_dma_driver = { .pm = &zynqmp_dma_dev_pm_ops, }, .probe = zynqmp_dma_probe, - .remove_new = zynqmp_dma_remove, + .remove = zynqmp_dma_remove, }; module_platform_driver(zynqmp_dma_driver); diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 892b94cfd6260..74a6aa7d8cc92 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -56,7 +56,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color) * two cases: either the path goes through this node, in which case * the hop count is the sum of the two biggest child depths plus 2. * Or it could be the case that the max hop path is entirely - * containted in a child tree, in which case the max hop count is just + * contained in a child tree, in which case the max hop count is just * the max hop count of this child. */ static void update_hop_count(struct fw_node *node) diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0ae2c84ecafed..9b298af1cac0b 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -80,7 +80,7 @@ struct fw_card_driver { /* * Allow the specified node ID to do direct DMA out and in of * host memory. The card will disable this for all node when - * a bus reset happens, so driver need to reenable this after + * a bus reset happens, so driver need to re-enable this after * bus reset. Returns 0 on success, -ENODEV if the card * doesn't support this, -ESTALE if the generation doesn't * match. diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 7ee55c2804ded..c02aed11b5902 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1384,7 +1384,7 @@ struct driver_data { }; /* - * This function apppends a packet to the DMA queue for transmission. + * This function appends a packet to the DMA queue for transmission. * Must always be called with the ochi->lock held to ensure proper * generation handling and locking around packet queue manipulation. */ @@ -2213,7 +2213,7 @@ static irqreturn_t irq_handler(int irq, void *data) if (unlikely(param_debug > 0)) { dev_notice_ratelimited(ohci->card.device, - "The debug parameter is superceded by tracepoints events, and deprecated."); + "The debug parameter is superseded by tracepoints events, and deprecated."); } /* @@ -2614,7 +2614,7 @@ static int ohci_set_config_rom(struct fw_card *card, * ConfigRomHeader and BusOptions doesn't honor the * noByteSwapData bit, so with a be32 config rom, the * controller will load be32 values in to these registers - * during the atomic update, even on litte endian + * during the atomic update, even on little endian * architectures. The workaround we use is to put a 0 in the * header quadlet; 0 is endian agnostic and means that the * config rom isn't ready yet. In the bus reset tasklet we @@ -3726,12 +3726,11 @@ static int pci_probe(struct pci_dev *dev, return -ENXIO; } - err = pcim_iomap_regions(dev, 1 << 0, ohci_driver_name); - if (err) { + ohci->registers = pcim_iomap_region(dev, 0, ohci_driver_name); + if (IS_ERR(ohci->registers)) { ohci_err(ohci, "request and map MMIO resource unavailable\n"); return -ENXIO; } - ohci->registers = pcim_iomap_table(dev)[0]; for (i = 0; i < ARRAY_SIZE(ohci_quirks); i++) if ((ohci_quirks[i].vendor == dev->vendor) && diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 1f53ca1f87e39..1b5fb2c4ce868 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3333,7 +3333,7 @@ static struct platform_driver scmi_driver = { .dev_groups = versions_groups, }, .probe = scmi_probe, - .remove_new = scmi_remove, + .remove = scmi_remove, }; static struct dentry *scmi_debugfs_init(void) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index f4d47577f83ee..87c323de17b90 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -1049,7 +1049,7 @@ static struct platform_driver scpi_driver = { .dev_groups = versions_groups, }, .probe = scpi_probe, - .remove_new = scpi_remove, + .remove = scpi_remove, }; module_platform_driver(scpi_driver); diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 208652a8087cd..882db32e51be9 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -220,7 +220,7 @@ MODULE_DEVICE_TABLE(of, coreboot_of_match); static struct platform_driver coreboot_table_driver = { .probe = coreboot_table_probe, - .remove_new = coreboot_table_remove, + .remove = coreboot_table_remove, .driver = { .name = "coreboot_table", .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match), diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c index 01c8ef14eaec3..ed79e823157af 100644 --- a/drivers/firmware/imx/imx-dsp.c +++ b/drivers/firmware/imx/imx-dsp.c @@ -180,7 +180,7 @@ static struct platform_driver imx_dsp_driver = { .name = "imx-dsp", }, .probe = imx_dsp_probe, - .remove_new = imx_dsp_remove, + .remove = imx_dsp_remove, }; builtin_platform_driver(imx_dsp_driver); diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 8e59be3782cbb..55b9cfad8a042 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -116,7 +116,7 @@ static void __meminit release_firmware_map_entry(struct kobject *kobj) kfree(entry); } -static struct kobj_type __refdata memmap_ktype = { +static const struct kobj_type memmap_ktype = { .release = release_firmware_map_entry, .sysfs_ops = &memmap_attr_ops, .default_groups = def_groups, diff --git a/drivers/firmware/microchip/mpfs-auto-update.c b/drivers/firmware/microchip/mpfs-auto-update.c index 0f7ec88482022..38a03698cec95 100644 --- a/drivers/firmware/microchip/mpfs-auto-update.c +++ b/drivers/firmware/microchip/mpfs-auto-update.c @@ -458,7 +458,7 @@ static struct platform_driver mpfs_auto_update_driver = { .name = "mpfs-auto-update", }, .probe = mpfs_auto_update_probe, - .remove_new = mpfs_auto_update_remove, + .remove = mpfs_auto_update_remove, }; module_platform_driver(mpfs_auto_update_driver); diff --git a/drivers/firmware/mtk-adsp-ipc.c b/drivers/firmware/mtk-adsp-ipc.c index a762302978de0..2b79371c61c9b 100644 --- a/drivers/firmware/mtk-adsp-ipc.c +++ b/drivers/firmware/mtk-adsp-ipc.c @@ -95,10 +95,9 @@ static int mtk_adsp_ipc_probe(struct platform_device *pdev) adsp_chan->idx = i; adsp_chan->ch = mbox_request_channel_byname(cl, adsp_mbox_ch_names[i]); if (IS_ERR(adsp_chan->ch)) { - ret = PTR_ERR(adsp_chan->ch); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to request mbox chan %s ret %d\n", - adsp_mbox_ch_names[i], ret); + ret = dev_err_probe(dev, PTR_ERR(adsp_chan->ch), + "Failed to request mbox channel %s\n", + adsp_mbox_ch_names[i]); for (j = 0; j < i; j++) { adsp_chan = &adsp_ipc->chans[j]; @@ -133,7 +132,7 @@ static struct platform_driver mtk_adsp_ipc_driver = { .name = "mtk-adsp-ipc", }, .probe = mtk_adsp_ipc_probe, - .remove_new = mtk_adsp_ipc_remove, + .remove = mtk_adsp_ipc_remove, }; builtin_platform_driver(mtk_adsp_ipc_driver); diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index 2328ca58bba61..a1ebbe9b73b13 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -78,6 +78,7 @@ struct psci_0_1_function_ids get_psci_0_1_function_ids(void) static u32 psci_cpu_suspend_feature; static bool psci_system_reset2_supported; +static bool psci_system_off2_hibernate_supported; static inline bool psci_has_ext_power_state(void) { @@ -333,6 +334,36 @@ static void psci_sys_poweroff(void) invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); } +#ifdef CONFIG_HIBERNATION +static int psci_sys_hibernate(struct sys_off_data *data) +{ + /* + * If no hibernate type is specified SYSTEM_OFF2 defaults to selecting + * HIBERNATE_OFF. + * + * There are hypervisors in the wild that do not align with the spec and + * reject calls that explicitly provide a hibernate type. For + * compatibility with these nonstandard implementations, pass 0 as the + * type. + */ + if (system_entering_hibernation()) + invoke_psci_fn(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2), 0, 0, 0); + return NOTIFY_DONE; +} + +static int __init psci_hibernate_init(void) +{ + if (psci_system_off2_hibernate_supported) { + /* Higher priority than EFI shutdown, but only for hibernate */ + register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_FIRMWARE + 2, + psci_sys_hibernate, NULL); + } + return 0; +} +subsys_initcall(psci_hibernate_init); +#endif + static int psci_features(u32 psci_func_id) { return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES, @@ -364,6 +395,7 @@ static const struct { PSCI_ID_NATIVE(1_1, SYSTEM_RESET2), PSCI_ID(1_1, MEM_PROTECT), PSCI_ID_NATIVE(1_1, MEM_PROTECT_CHECK_RANGE), + PSCI_ID_NATIVE(1_3, SYSTEM_OFF2), }; static int psci_debugfs_read(struct seq_file *s, void *data) @@ -525,6 +557,18 @@ static void __init psci_init_system_reset2(void) psci_system_reset2_supported = true; } +static void __init psci_init_system_off2(void) +{ + int ret; + + ret = psci_features(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2)); + if (ret < 0) + return; + + if (ret & PSCI_1_3_OFF_TYPE_HIBERNATE_OFF) + psci_system_off2_hibernate_supported = true; +} + static void __init psci_init_system_suspend(void) { int ret; @@ -655,6 +699,7 @@ static int __init psci_probe(void) psci_init_cpu_suspend(); psci_init_system_suspend(); psci_init_system_reset2(); + psci_init_system_off2(); kvm_init_hyp_services(); } diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 85c525745b311..d58da3e4500a5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -757,7 +757,7 @@ MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); static struct platform_driver fw_cfg_sysfs_driver = { .probe = fw_cfg_sysfs_probe, - .remove_new = fw_cfg_sysfs_remove, + .remove = fw_cfg_sysfs_remove, .driver = { .name = "fw_cfg", .of_match_table = fw_cfg_sysfs_mmio_match, diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index 18cc349871085..7ecde6921a0ac 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -406,7 +406,7 @@ static struct platform_driver rpi_firmware_driver = { }, .probe = rpi_firmware_probe, .shutdown = rpi_firmware_shutdown, - .remove_new = rpi_firmware_remove, + .remove = rpi_firmware_remove, }; module_platform_driver(rpi_firmware_driver); diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index e20cee9c2d320..1ea39a0a76c78 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -802,7 +802,7 @@ static void stratix10_rsu_remove(struct platform_device *pdev) static struct platform_driver stratix10_rsu_driver = { .probe = stratix10_rsu_probe, - .remove_new = stratix10_rsu_remove, + .remove = stratix10_rsu_remove, .driver = { .name = "stratix10-rsu", .dev_groups = rsu_groups, diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 528f37417aea4..c5c78b869561b 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1271,7 +1271,7 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev) static struct platform_driver stratix10_svc_driver = { .probe = stratix10_svc_drv_probe, - .remove_new = stratix10_svc_drv_remove, + .remove = stratix10_svc_drv_remove, .driver = { .name = "stratix10-svc", .of_match_table = stratix10_svc_drv_match, diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index cdb565c411198..720fa8b5d8e95 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -2118,6 +2118,6 @@ static struct platform_driver zynqmp_firmware_driver = { .dev_groups = zynqmp_firmware_groups, }, .probe = zynqmp_firmware_probe, - .remove_new = zynqmp_firmware_remove, + .remove = zynqmp_firmware_remove, }; module_platform_driver(zynqmp_firmware_driver); diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c index f4de3fea0b2dc..e41492988dd63 100644 --- a/drivers/fpga/altera-fpga2sdram.c +++ b/drivers/fpga/altera-fpga2sdram.c @@ -152,7 +152,7 @@ MODULE_DEVICE_TABLE(of, altera_fpga_of_match); static struct platform_driver altera_fpga_driver = { .probe = alt_fpga_bridge_probe, - .remove_new = alt_fpga_bridge_remove, + .remove = alt_fpga_bridge_remove, .driver = { .name = "altera_fpga2sdram_bridge", .of_match_table = of_match_ptr(altera_fpga_of_match), diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c index 44061cb16f877..594693ff786e2 100644 --- a/drivers/fpga/altera-freeze-bridge.c +++ b/drivers/fpga/altera-freeze-bridge.c @@ -262,7 +262,7 @@ static void altera_freeze_br_remove(struct platform_device *pdev) static struct platform_driver altera_freeze_br_driver = { .probe = altera_freeze_br_probe, - .remove_new = altera_freeze_br_remove, + .remove = altera_freeze_br_remove, .driver = { .name = "altera_freeze_br", .of_match_table = altera_freeze_br_of_match, diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index 6f8e24be19c6d..f2f1250689cb0 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c @@ -205,7 +205,7 @@ MODULE_DEVICE_TABLE(of, altera_fpga_of_match); static struct platform_driver alt_fpga_bridge_driver = { .probe = alt_fpga_bridge_probe, - .remove_new = alt_fpga_bridge_remove, + .remove = alt_fpga_bridge_remove, .driver = { .name = "altera_hps2fpga_bridge", .of_match_table = of_match_ptr(altera_fpga_of_match), diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 6b97c073849ea..2fd4f07ed081a 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -947,12 +947,12 @@ static const struct attribute_group *afu_dev_groups[] = { }; static struct platform_driver afu_driver = { - .driver = { - .name = DFL_FPGA_FEATURE_DEV_PORT, + .driver = { + .name = DFL_FPGA_FEATURE_DEV_PORT, .dev_groups = afu_dev_groups, }, - .probe = afu_probe, - .remove_new = afu_remove, + .probe = afu_probe, + .remove = afu_remove, }; static int __init afu_init(void) diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c index 0b01b38952776..950c606c59d44 100644 --- a/drivers/fpga/dfl-fme-br.c +++ b/drivers/fpga/dfl-fme-br.c @@ -92,11 +92,11 @@ static void fme_br_remove(struct platform_device *pdev) } static struct platform_driver fme_br_driver = { - .driver = { - .name = DFL_FPGA_FME_BRIDGE, + .driver = { + .name = DFL_FPGA_FME_BRIDGE, }, - .probe = fme_br_probe, - .remove_new = fme_br_remove, + .probe = fme_br_probe, + .remove = fme_br_remove, }; module_platform_driver(fme_br_driver); diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index 864924f68f5e2..f8d89a4a6ccbf 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -742,12 +742,12 @@ static const struct attribute_group *fme_dev_groups[] = { }; static struct platform_driver fme_driver = { - .driver = { - .name = DFL_FPGA_FEATURE_DEV_FME, + .driver = { + .name = DFL_FPGA_FEATURE_DEV_FME, .dev_groups = fme_dev_groups, }, - .probe = fme_probe, - .remove_new = fme_remove, + .probe = fme_probe, + .remove = fme_remove, }; module_platform_driver(fme_driver); diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c index 71616f8b4982d..c6cd63063c820 100644 --- a/drivers/fpga/dfl-fme-region.c +++ b/drivers/fpga/dfl-fme-region.c @@ -71,11 +71,11 @@ static void fme_region_remove(struct platform_device *pdev) } static struct platform_driver fme_region_driver = { - .driver = { - .name = DFL_FPGA_FME_REGION, + .driver = { + .name = DFL_FPGA_FME_REGION, }, - .probe = fme_region_probe, - .remove_new = fme_region_remove, + .probe = fme_region_probe, + .remove = fme_region_remove, }; module_platform_driver(fme_region_driver); diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c index 80cac3a5f9767..602807d6afcc0 100644 --- a/drivers/fpga/dfl-pci.c +++ b/drivers/fpga/dfl-pci.c @@ -39,14 +39,6 @@ struct cci_drvdata { struct dfl_fpga_cdev *cdev; /* container device */ }; -static void __iomem *cci_pci_ioremap_bar0(struct pci_dev *pcidev) -{ - if (pcim_iomap_regions(pcidev, BIT(0), DRV_NAME)) - return NULL; - - return pcim_iomap_table(pcidev)[0]; -} - static int cci_pci_alloc_irq(struct pci_dev *pcidev) { int ret, nvec = pci_msix_vec_count(pcidev); @@ -235,9 +227,9 @@ static int find_dfls_by_default(struct pci_dev *pcidev, u64 v; /* start to find Device Feature List from Bar 0 */ - base = cci_pci_ioremap_bar0(pcidev); - if (!base) - return -ENOMEM; + base = pcim_iomap_region(pcidev, 0, DRV_NAME); + if (IS_ERR(base)) + return PTR_ERR(base); /* * PF device has FME and Ports/AFUs, and VF device only has one @@ -296,7 +288,7 @@ static int find_dfls_by_default(struct pci_dev *pcidev, } /* release I/O mappings for next step enumeration */ - pcim_iounmap_regions(pcidev, BIT(0)); + pcim_iounmap_region(pcidev, 0); return ret; } diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 7ac9f9f5af129..dd515083bbdd5 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -759,7 +759,7 @@ MODULE_DEVICE_TABLE(platform, intel_m10bmc_sec_ids); static struct platform_driver intel_m10bmc_sec_driver = { .probe = m10bmc_sec_probe, - .remove_new = m10bmc_sec_remove, + .remove = m10bmc_sec_remove, .driver = { .name = "intel-m10bmc-sec-update", .dev_groups = m10bmc_sec_attr_groups, diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index 8526a5a86f0cb..43db4bb77138a 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -436,7 +436,7 @@ static void of_fpga_region_remove(struct platform_device *pdev) static struct platform_driver of_fpga_region_driver = { .probe = of_fpga_region_probe, - .remove_new = of_fpga_region_remove, + .remove = of_fpga_region_remove, .driver = { .name = "of-fpga-region", .of_match_table = of_match_ptr(fpga_region_of_match), diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c index 4c03513b8f03b..0165a3c869322 100644 --- a/drivers/fpga/socfpga-a10.c +++ b/drivers/fpga/socfpga-a10.c @@ -535,7 +535,7 @@ MODULE_DEVICE_TABLE(of, socfpga_a10_fpga_of_match); static struct platform_driver socfpga_a10_fpga_driver = { .probe = socfpga_a10_fpga_probe, - .remove_new = socfpga_a10_fpga_remove, + .remove = socfpga_a10_fpga_remove, .driver = { .name = "socfpga_a10_fpga_manager", .of_match_table = socfpga_a10_fpga_of_match, diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 2c0def7d7cbb1..0a295ccf1644a 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -455,7 +455,7 @@ MODULE_DEVICE_TABLE(of, s10_of_match); static struct platform_driver s10_driver = { .probe = s10_probe, - .remove_new = s10_remove, + .remove = s10_remove, .driver = { .name = "Stratix10 SoC FPGA manager", .of_match_table = of_match_ptr(s10_of_match), diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 788dd2f63a652..822751fad18ae 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -162,7 +162,7 @@ static void xlnx_pr_decoupler_remove(struct platform_device *pdev) static struct platform_driver xlnx_pr_decoupler_driver = { .probe = xlnx_pr_decoupler_probe, - .remove_new = xlnx_pr_decoupler_remove, + .remove = xlnx_pr_decoupler_remove, .driver = { .name = "xlnx_pr_decoupler", .of_match_table = xlnx_pr_decoupler_of_match, diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 4db3d80e10b09..f7e08f7ea9ef3 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -642,7 +642,7 @@ MODULE_DEVICE_TABLE(of, zynq_fpga_of_match); static struct platform_driver zynq_fpga_driver = { .probe = zynq_fpga_probe, - .remove_new = zynq_fpga_remove, + .remove = zynq_fpga_remove, .driver = { .name = "zynq_fpga_manager", .of_match_table = of_match_ptr(zynq_fpga_of_match), diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 039fef26546ec..73e660c5e38a1 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -261,6 +261,11 @@ static int altera_gpio_probe(struct platform_device *pdev) altera_gc->gc.set = altera_gpio_set; altera_gc->gc.owner = THIS_MODULE; altera_gc->gc.parent = &pdev->dev; + altera_gc->gc.base = -1; + + altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!altera_gc->gc.label) + return -ENOMEM; altera_gc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(altera_gc->regs)) diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 72755fee64784..34eb26298e324 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -420,7 +420,7 @@ static void aspeed_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p) int offset; irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); - seq_printf(p, dev_name(gpio->dev)); + seq_puts(p, dev_name(gpio->dev)); } static const struct irq_chip aspeed_sgpio_irq_chip = { diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 208f95fb585e1..40c1bd80f8b04 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -1049,7 +1049,7 @@ static void aspeed_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) if (rc) return; - seq_printf(p, dev_name(gpio->dev)); + seq_puts(p, dev_name(gpio->dev)); } static const struct irq_chip aspeed_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index ab798c848215a..58d2464c07bc3 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -249,7 +249,7 @@ static void ep93xx_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip gpio_eic_irq_chip = { diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 5170fe7599cdf..d5909a4f0433c 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -99,11 +99,13 @@ static void exar_set_value(struct gpio_chip *chip, unsigned int offset, struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); unsigned int bit = exar_offset_to_bit(exar_gpio, offset); + unsigned int bit_value = value ? BIT(bit) : 0; - if (value) - regmap_set_bits(exar_gpio->regmap, addr, BIT(bit)); - else - regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); + /* + * regmap_write_bits() forces value to be written when an external + * pull up/down might otherwise indicate value was already set. + */ + regmap_write_bits(exar_gpio->regmap, addr, BIT(bit), bit_value); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index 1bcfc1835dae8..0580f6712bea9 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -210,7 +210,7 @@ static void hlwd_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) struct hlwd_gpio *hlwd = gpiochip_get_data(irq_data_get_irq_chip_data(data)); - seq_printf(p, dev_name(hlwd->dev)); + seq_puts(p, dev_name(hlwd->dev)); } static const struct irq_chip hlwd_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 421d7e3a6c66e..cd20604f26de5 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -78,24 +78,25 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id if (retval) return retval; - retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev)); - if (retval) - return dev_err_probe(dev, retval, "I/O memory mapping error\n"); - - base = pcim_iomap_table(pdev)[1]; + base = pcim_iomap_region(pdev, 1, pci_name(pdev)); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "I/O memory mapping error\n"); irq_base = readl(base + 0 * sizeof(u32)); gpio_base = readl(base + 1 * sizeof(u32)); /* Release the IO mapping, since we already get the info from BAR1 */ - pcim_iounmap_regions(pdev, BIT(1)); + pcim_iounmap_region(pdev, 1); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; - priv->reg_base = pcim_iomap_table(pdev)[0]; + priv->reg_base = pcim_iomap_region(pdev, 0, pci_name(pdev)); + if (IS_ERR(priv->reg_base)) + return dev_err_probe(dev, PTR_ERR(priv->reg_base), + "I/O memory mapping error\n"); priv->pin_info.pin_ranges = mrfld_gpio_ranges; priv->pin_info.nranges = ARRAY_SIZE(mrfld_gpio_ranges); diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 6abe01bc39c3e..6f3dda6b635fa 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -331,7 +331,7 @@ static void mlxbf2_gpio_irq_print_chip(struct irq_data *irqd, struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); - seq_printf(p, dev_name(gs->dev)); + seq_puts(p, dev_name(gs->dev)); } static const struct irq_chip mlxbf2_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 9ef24449126a7..3ea32c5e33d1a 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -403,7 +403,7 @@ static void gpio_mpsse_ida_remove(void *data) { struct mpsse_priv *priv = data; - ida_simple_remove(&gpio_mpsse_ida, priv->id); + ida_free(&gpio_mpsse_ida, priv->id); } static int gpio_mpsse_probe(struct usb_interface *interface, @@ -422,7 +422,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface, priv->intf = interface; priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; - priv->id = ida_simple_get(&gpio_mpsse_ida, 0, 0, GFP_KERNEL); + priv->id = ida_alloc(&gpio_mpsse_ida, GFP_KERNEL); if (priv->id < 0) return priv->id; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 7ad4534054962..54c4bfdccf568 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -715,7 +715,7 @@ static void omap_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct gpio_bank *bank = omap_irq_data_get_bank(d); - seq_printf(p, dev_name(bank->dev)); + seq_puts(p, dev_name(bank->dev)); } static const struct irq_chip omap_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 3f2d33ee20cca..272febc3230e9 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -815,7 +815,7 @@ static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip pca953x_irq_chip = { diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index a211a02d4b4a6..1c273727ffa3a 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -291,7 +291,7 @@ static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip pl061_irq_chip = { diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 6d3a39a03f58e..9ad286adf2632 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -600,7 +600,7 @@ static void tegra_gpio_irq_print_chip(struct irq_data *d, struct seq_file *s) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - seq_printf(s, dev_name(chip->parent)); + seq_puts(s, dev_name(chip->parent)); } static const struct irq_chip tegra_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 1ecb733a5e88b..6895b65c86aff 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -610,7 +610,7 @@ static void tegra186_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static const struct irq_chip tegra186_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index f2e7e8754d95d..5e26eb3adabbf 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -275,7 +275,7 @@ static void tqmx86_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - seq_printf(p, gc->label); + seq_puts(p, gc->label); } static const struct irq_chip tqmx86_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-visconti.c b/drivers/gpio/gpio-visconti.c index ebc71ecdb6cf5..5bd965c18a465 100644 --- a/drivers/gpio/gpio-visconti.c +++ b/drivers/gpio/gpio-visconti.c @@ -142,7 +142,7 @@ static void visconti_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct visconti_gpio *priv = gpiochip_get_data(gc); - seq_printf(p, dev_name(priv->dev)); + seq_puts(p, dev_name(priv->dev)); } static const struct irq_chip visconti_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index 0f9d1f61fee36..93544e98ccbd3 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -198,7 +198,7 @@ static void iproc_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio_chip *chip = to_iproc_gpio(gc); - seq_printf(p, dev_name(chip->dev)); + seq_puts(p, dev_name(chip->dev)); } static const struct irq_chip iproc_gpio_irq_chip = { diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index 2de61337ad3b5..d7230fd83f5d6 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -169,6 +170,7 @@ static const struct gpio_chip zevio_gpio_chip = { /* Initialization */ static int zevio_gpio_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct zevio_gpio *controller; int status, i; @@ -180,6 +182,10 @@ static int zevio_gpio_probe(struct platform_device *pdev) controller->chip = zevio_gpio_chip; controller->chip.parent = &pdev->dev; + controller->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev)); + if (!controller->chip.label) + return -ENOMEM; + controller->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(controller->regs)) return dev_err_probe(&pdev->dev, PTR_ERR(controller->regs), diff --git a/drivers/gpu/drm/amd/amdgpu/aldebaran.c b/drivers/gpu/drm/amd/amdgpu/aldebaran.c index 3a588fecb0c58..f44de9d4b6a17 100644 --- a/drivers/gpu/drm/amd/amdgpu/aldebaran.c +++ b/drivers/gpu/drm/amd/amdgpu/aldebaran.c @@ -330,6 +330,8 @@ aldebaran_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl, } list_for_each_entry(tmp_adev, reset_device_list, reset_list) { + amdgpu_set_init_level(tmp_adev, + AMDGPU_INIT_LEVEL_RESET_RECOVERY); dev_info(tmp_adev->dev, "GPU reset succeeded, trying to resume\n"); r = aldebaran_mode2_restore_ip(tmp_adev); @@ -375,6 +377,8 @@ aldebaran_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl, tmp_adev); if (!r) { + amdgpu_set_init_level(tmp_adev, + AMDGPU_INIT_LEVEL_DEFAULT); amdgpu_irq_gpu_reset_resume_helper(tmp_adev); r = amdgpu_ib_ring_tests(tmp_adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index d8bc6da500161..4653a8d2823a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -839,6 +839,7 @@ struct amdgpu_mqd { enum amdgpu_init_lvl_id { AMDGPU_INIT_LEVEL_DEFAULT, AMDGPU_INIT_LEVEL_MINIMAL_XGMI, + AMDGPU_INIT_LEVEL_RESET_RECOVERY, }; struct amdgpu_init_level { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 0171d240fcb05..9095c05e0269f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -156,6 +156,11 @@ struct amdgpu_init_level amdgpu_init_default = { .hwini_ip_block_mask = AMDGPU_IP_BLK_MASK_ALL, }; +struct amdgpu_init_level amdgpu_init_recovery = { + .level = AMDGPU_INIT_LEVEL_RESET_RECOVERY, + .hwini_ip_block_mask = AMDGPU_IP_BLK_MASK_ALL, +}; + /* * Minimal blocks needed to be initialized before a XGMI hive can be reset. This * is used for cases like reset on initialization where the entire hive needs to @@ -182,6 +187,9 @@ void amdgpu_set_init_level(struct amdgpu_device *adev, case AMDGPU_INIT_LEVEL_MINIMAL_XGMI: adev->init_lvl = &amdgpu_init_minimal_xgmi; break; + case AMDGPU_INIT_LEVEL_RESET_RECOVERY: + adev->init_lvl = &amdgpu_init_recovery; + break; case AMDGPU_INIT_LEVEL_DEFAULT: fallthrough; default: @@ -3250,7 +3258,7 @@ static int amdgpu_device_ip_late_init(struct amdgpu_device *adev) return r; } - if (!amdgpu_in_reset(adev)) + if (!amdgpu_reset_in_recovery(adev)) amdgpu_ras_set_error_query_ready(adev, true); amdgpu_device_set_cg_state(adev, AMD_CG_STATE_GATE); @@ -4669,8 +4677,8 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev) int idx; bool px; - amdgpu_fence_driver_sw_fini(adev); amdgpu_device_ip_fini(adev); + amdgpu_fence_driver_sw_fini(adev); amdgpu_ucode_release(&adev->firmware.gpu_info_fw); adev->accel_working = false; dma_fence_put(rcu_dereference_protected(adev->gang_submit, true)); @@ -5419,7 +5427,7 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context) struct list_head *device_list_handle; bool full_reset, vram_lost = false; struct amdgpu_device *tmp_adev; - int r; + int r, init_level; device_list_handle = reset_context->reset_device_list; @@ -5428,10 +5436,18 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context) full_reset = test_bit(AMDGPU_NEED_FULL_RESET, &reset_context->flags); + /** + * If it's reset on init, it's default init level, otherwise keep level + * as recovery level. + */ + if (reset_context->method == AMD_RESET_METHOD_ON_INIT) + init_level = AMDGPU_INIT_LEVEL_DEFAULT; + else + init_level = AMDGPU_INIT_LEVEL_RESET_RECOVERY; + r = 0; list_for_each_entry(tmp_adev, device_list_handle, reset_list) { - /* After reset, it's default init level */ - amdgpu_set_init_level(tmp_adev, AMDGPU_INIT_LEVEL_DEFAULT); + amdgpu_set_init_level(tmp_adev, init_level); if (full_reset) { /* post card */ amdgpu_ras_set_fed(tmp_adev, false); @@ -5518,6 +5534,9 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context) out: if (!r) { + /* IP init is complete now, set level as default */ + amdgpu_set_init_level(tmp_adev, + AMDGPU_INIT_LEVEL_DEFAULT); amdgpu_irq_gpu_reset_resume_helper(tmp_adev); r = amdgpu_ib_ring_tests(tmp_adev); if (r) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index f57cc72c43cf4..69a6b6dba0a54 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1778,9 +1778,11 @@ int amdgpu_gfx_sysfs_init(struct amdgpu_device *adev) void amdgpu_gfx_sysfs_fini(struct amdgpu_device *adev) { - amdgpu_gfx_sysfs_xcp_fini(adev); - amdgpu_gfx_sysfs_isolation_shader_fini(adev); - amdgpu_gfx_sysfs_reset_mask_fini(adev); + if (adev->dev->kobj.sd) { + amdgpu_gfx_sysfs_xcp_fini(adev); + amdgpu_gfx_sysfs_isolation_shader_fini(adev); + amdgpu_gfx_sysfs_reset_mask_fini(adev); + } } int amdgpu_gfx_cleaner_shader_sw_init(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c index 04eb516745962..b6d2eb049f540 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c @@ -447,6 +447,8 @@ int amdgpu_jpeg_sysfs_reset_mask_init(struct amdgpu_device *adev) void amdgpu_jpeg_sysfs_reset_mask_fini(struct amdgpu_device *adev) { - if (adev->jpeg.num_jpeg_inst) - device_remove_file(adev->dev, &dev_attr_jpeg_reset_mask); + if (adev->dev->kobj.sd) { + if (adev->jpeg.num_jpeg_inst) + device_remove_file(adev->dev, &dev_attr_jpeg_reset_mask); + } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c index e8adfd0a570a2..34b5e22b44e5f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c @@ -137,7 +137,8 @@ void amdgpu_preempt_mgr_fini(struct amdgpu_device *adev) if (ret) return; - device_remove_file(adev->dev, &dev_attr_mem_info_preempt_used); + if (adev->dev->kobj.sd) + device_remove_file(adev->dev, &dev_attr_mem_info_preempt_used); ttm_resource_manager_cleanup(man); ttm_set_driver_manager(&adev->mman.bdev, AMDGPU_PL_PREEMPT, NULL); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 17cf10c0b72be..448f9e742983f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -4112,7 +4112,7 @@ static umode_t amdgpu_flash_attr_is_visible(struct kobject *kobj, struct attribu } static umode_t amdgpu_bin_flash_attr_is_visible(struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, int idx) { struct device *dev = kobj_to_dev(kobj); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 1bc95b0cdbb8d..4c9fa24dd9726 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -1298,7 +1298,7 @@ int amdgpu_ras_bind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk, struct ras_manager *obj; /* in resume phase, no need to create aca fs node */ - if (adev->in_suspend || amdgpu_in_reset(adev)) + if (adev->in_suspend || amdgpu_reset_in_recovery(adev)) return 0; obj = get_ras_manager(adev, blk); @@ -3610,7 +3610,7 @@ static void amdgpu_ras_event_mgr_init(struct amdgpu_device *adev) ras->event_mgr = hive ? &hive->event_mgr : &ras->__event_mgr; /* init event manager with node 0 on xgmi system */ - if (!amdgpu_in_reset(adev)) { + if (!amdgpu_reset_in_recovery(adev)) { if (!hive || adev->gmc.xgmi.node_id == 0) ras_event_mgr_init(ras->event_mgr); } @@ -3825,7 +3825,7 @@ int amdgpu_ras_block_late_init(struct amdgpu_device *adev, r = amdgpu_ras_feature_enable_on_boot(adev, ras_block, 1); if (r) { - if (adev->in_suspend || amdgpu_in_reset(adev)) { + if (adev->in_suspend || amdgpu_reset_in_recovery(adev)) { /* in resume phase, if fail to enable ras, * clean up all ras fs nodes, and disable ras */ goto cleanup; @@ -3837,7 +3837,7 @@ int amdgpu_ras_block_late_init(struct amdgpu_device *adev, amdgpu_persistent_edc_harvesting(adev, ras_block); /* in resume phase, no need to create ras fs node */ - if (adev->in_suspend || amdgpu_in_reset(adev)) + if (adev->in_suspend || amdgpu_reset_in_recovery(adev)) return 0; ras_obj = container_of(ras_block, struct amdgpu_ras_block_object, ras_comm); @@ -3967,7 +3967,7 @@ int amdgpu_ras_late_init(struct amdgpu_device *adev) amdgpu_ras_event_mgr_init(adev); if (amdgpu_ras_aca_is_supported(adev)) { - if (amdgpu_in_reset(adev)) { + if (amdgpu_reset_in_recovery(adev)) { if (amdgpu_aca_is_enabled(adev)) r = amdgpu_aca_reset(adev); else diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c index 24dae7cdbe954..a0acb65f4b40a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c @@ -342,3 +342,8 @@ void amdgpu_reset_get_desc(struct amdgpu_reset_context *rst_ctxt, char *buf, strscpy(buf, "unknown", len); } } + +bool amdgpu_reset_in_recovery(struct amdgpu_device *adev) +{ + return (adev->init_lvl->level == AMDGPU_INIT_LEVEL_RESET_RECOVERY); +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h index f8628bc898df4..4d9b9701139be 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h @@ -158,4 +158,6 @@ extern struct amdgpu_reset_handler xgmi_reset_on_init_handler; int amdgpu_reset_do_xgmi_reset_on_init( struct amdgpu_reset_context *reset_context); +bool amdgpu_reset_in_recovery(struct amdgpu_device *adev); + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 8c89b69edc201..113f0d2426187 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -451,6 +451,8 @@ void amdgpu_sdma_sysfs_reset_mask_fini(struct amdgpu_device *adev) if (!amdgpu_gpu_recovery) return; - if (adev->sdma.num_instances) - device_remove_file(adev->dev, &dev_attr_sdma_reset_mask); + if (adev->dev->kobj.sd) { + if (adev->sdma.num_instances) + device_remove_file(adev->dev, &dev_attr_sdma_reset_mask); + } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index 74fdbf71d95b7..599d3ca4e0ef9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -214,15 +214,15 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev) drm_sched_entity_destroy(&adev->vce.entity); - amdgpu_bo_free_kernel(&adev->vce.vcpu_bo, &adev->vce.gpu_addr, - (void **)&adev->vce.cpu_addr); - for (i = 0; i < adev->vce.num_rings; i++) amdgpu_ring_fini(&adev->vce.ring[i]); amdgpu_ucode_release(&adev->vce.fw); mutex_destroy(&adev->vce.idle_mutex); + amdgpu_bo_free_kernel(&adev->vce.vcpu_bo, &adev->vce.gpu_addr, + (void **)&adev->vce.cpu_addr); + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index aecb78e0519f6..3e94c3ba1ba2c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -1283,3 +1283,40 @@ int amdgpu_vcn_psp_update_sram(struct amdgpu_device *adev, int inst_idx, return psp_execute_ip_fw_load(&adev->psp, &ucode); } + +static ssize_t amdgpu_get_vcn_reset_mask(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + + if (!adev) + return -ENODEV; + + return amdgpu_show_reset_mask(buf, adev->vcn.supported_reset); +} + +static DEVICE_ATTR(vcn_reset_mask, 0444, + amdgpu_get_vcn_reset_mask, NULL); + +int amdgpu_vcn_sysfs_reset_mask_init(struct amdgpu_device *adev) +{ + int r = 0; + + if (adev->vcn.num_vcn_inst) { + r = device_create_file(adev->dev, &dev_attr_vcn_reset_mask); + if (r) + return r; + } + + return r; +} + +void amdgpu_vcn_sysfs_reset_mask_fini(struct amdgpu_device *adev) +{ + if (adev->dev->kobj.sd) { + if (adev->vcn.num_vcn_inst) + device_remove_file(adev->dev, &dev_attr_vcn_reset_mask); + } +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h index 765b809d48a25..1e32311c1dff7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h @@ -333,6 +333,8 @@ struct amdgpu_vcn { /* IP reg dump */ uint32_t *ip_dump; + + uint32_t supported_reset; }; struct amdgpu_fw_shared_rb_ptrs_struct { @@ -519,5 +521,7 @@ int amdgpu_vcn_ras_sw_init(struct amdgpu_device *adev); int amdgpu_vcn_psp_update_sram(struct amdgpu_device *adev, int inst_idx, enum AMDGPU_UCODE_ID ucode_id); int amdgpu_vcn_save_vcpu_bo(struct amdgpu_device *adev); +int amdgpu_vcn_sysfs_reset_mask_init(struct amdgpu_device *adev); +void amdgpu_vcn_sysfs_reset_mask_fini(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c index 3e6f9dfb61bb3..110b120d7375d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c @@ -904,8 +904,10 @@ int amdgpu_vpe_sysfs_reset_mask_init(struct amdgpu_device *adev) void amdgpu_vpe_sysfs_reset_mask_fini(struct amdgpu_device *adev) { - if (adev->vpe.num_instances) - device_remove_file(adev->dev, &dev_attr_vpe_reset_mask); + if (adev->dev->kobj.sd) { + if (adev->vpe.num_instances) + device_remove_file(adev->dev, &dev_attr_vpe_reset_mask); + } } static const struct amdgpu_ring_funcs vpe_ring_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c index b47422b0b5b10..74b4349e345a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c @@ -40,6 +40,11 @@ #define smnPCS_GOPX1_PCS_ERROR_STATUS 0x12200210 #define smnPCS_GOPX1_PCS_ERROR_NONCORRECTABLE_MASK 0x12200218 +#define XGMI_STATE_DISABLE 0xD1 +#define XGMI_STATE_LS0 0x81 +#define XGMI_LINK_ACTIVE 1 +#define XGMI_LINK_INACTIVE 0 + static DEFINE_MUTEX(xgmi_mutex); #define AMDGPU_MAX_XGMI_DEVICE_PER_HIVE 4 @@ -289,6 +294,42 @@ static const struct amdgpu_pcs_ras_field xgmi3x16_pcs_ras_fields[] = { SOC15_REG_FIELD(PCS_XGMI3X16_PCS_ERROR_STATUS, RxCMDPktErr)}, }; +static u32 xgmi_v6_4_get_link_status(struct amdgpu_device *adev, int global_link_num) +{ + const u32 smnpcs_xgmi3x16_pcs_state_hist1 = 0x11a00070; + const int xgmi_inst = 2; + u32 link_inst; + u64 addr; + + link_inst = global_link_num % xgmi_inst; + + addr = (smnpcs_xgmi3x16_pcs_state_hist1 | (link_inst << 20)) + + adev->asic_funcs->encode_ext_smn_addressing(global_link_num / xgmi_inst); + + return RREG32_PCIE_EXT(addr); +} + +int amdgpu_get_xgmi_link_status(struct amdgpu_device *adev, int global_link_num) +{ + u32 xgmi_state_reg_val; + + switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) { + case IP_VERSION(6, 4, 0): + xgmi_state_reg_val = xgmi_v6_4_get_link_status(adev, global_link_num); + break; + default: + return -EOPNOTSUPP; + } + + if ((xgmi_state_reg_val & 0xFF) == XGMI_STATE_DISABLE) + return -ENOLINK; + + if ((xgmi_state_reg_val & 0xFF) == XGMI_STATE_LS0) + return XGMI_LINK_ACTIVE; + + return XGMI_LINK_INACTIVE; +} + /** * DOC: AMDGPU XGMI Support * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h index 8cc7ab38db7c7..d1282b4c63488 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h @@ -84,5 +84,7 @@ int amdgpu_xgmi_reset_on_init(struct amdgpu_device *adev); int amdgpu_xgmi_request_nps_change(struct amdgpu_device *adev, struct amdgpu_hive_info *hive, int req_nps_mode); +int amdgpu_get_xgmi_link_status(struct amdgpu_device *adev, + int global_link_num); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/df_v3_6.c b/drivers/gpu/drm/amd/amdgpu/df_v3_6.c index 483a441b46aa1..621aeca538803 100644 --- a/drivers/gpu/drm/amd/amdgpu/df_v3_6.c +++ b/drivers/gpu/drm/amd/amdgpu/df_v3_6.c @@ -254,8 +254,8 @@ static void df_v3_6_sw_init(struct amdgpu_device *adev) static void df_v3_6_sw_fini(struct amdgpu_device *adev) { - - device_remove_file(adev->dev, &dev_attr_df_cntr_avail); + if (adev->dev->kobj.sd) + device_remove_file(adev->dev, &dev_attr_df_cntr_avail); } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 07f45f1a503ad..b6016f11956ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -87,9 +87,14 @@ static void gmc_v7_0_init_golden_registers(struct amdgpu_device *adev) static void gmc_v7_0_mc_stop(struct amdgpu_device *adev) { + struct amdgpu_ip_block *ip_block; u32 blackout; - gmc_v7_0_wait_for_idle((void *)adev); + ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GMC); + if (!ip_block) + return; + + gmc_v7_0_wait_for_idle(ip_block); blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL); if (REG_GET_FIELD(blackout, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE) != 1) { @@ -251,9 +256,14 @@ static void gmc_v7_0_vram_gtt_location(struct amdgpu_device *adev, */ static void gmc_v7_0_mc_program(struct amdgpu_device *adev) { + struct amdgpu_ip_block *ip_block; u32 tmp; int i, j; + ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GMC); + if (!ip_block) + return; + /* Initialize HDP */ for (i = 0, j = 0; i < 32; i++, j += 0x6) { WREG32((0xb05 + j), 0x00000000); @@ -264,7 +274,7 @@ static void gmc_v7_0_mc_program(struct amdgpu_device *adev) } WREG32(mmHDP_REG_COHERENCY_FLUSH_CNTL, 0); - if (gmc_v7_0_wait_for_idle((void *)adev)) + if (gmc_v7_0_wait_for_idle(ip_block)) dev_warn(adev->dev, "Wait for MC idle timedout !\n"); if (adev->mode_info.num_crtc) { @@ -288,7 +298,7 @@ static void gmc_v7_0_mc_program(struct amdgpu_device *adev) WREG32(mmMC_VM_AGP_BASE, 0); WREG32(mmMC_VM_AGP_TOP, adev->gmc.agp_end >> 22); WREG32(mmMC_VM_AGP_BOT, adev->gmc.agp_start >> 22); - if (gmc_v7_0_wait_for_idle((void *)adev)) + if (gmc_v7_0_wait_for_idle(ip_block)) dev_warn(adev->dev, "Wait for MC idle timedout !\n"); WREG32(mmBIF_FB_EN, BIF_FB_EN__FB_READ_EN_MASK | BIF_FB_EN__FB_WRITE_EN_MASK); @@ -1183,7 +1193,7 @@ static int gmc_v7_0_soft_reset(struct amdgpu_ip_block *ip_block) if (srbm_soft_reset) { gmc_v7_0_mc_stop(adev); - if (gmc_v7_0_wait_for_idle((void *)adev)) + if (gmc_v7_0_wait_for_idle(ip_block)) dev_warn(adev->dev, "Wait for GMC idle timed out !\n"); tmp = RREG32(mmSRBM_SOFT_RESET); diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v1_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v1_0.c index 03b8b7cd5229b..7319299f25aea 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v1_0.c @@ -604,7 +604,7 @@ static void jpeg_v1_0_set_irq_funcs(struct amdgpu_device *adev) static void jpeg_v1_0_ring_begin_use(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - bool set_clocks = !cancel_delayed_work_sync(&adev->vcn.idle_work); + bool set_clocks = !cancel_delayed_work_sync(&adev->jpeg.idle_work); int cnt = 0; mutex_lock(&adev->vcn.vcn1_jpeg1_workaround); diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c index d6823fb45d328..6e29b69894a57 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c @@ -150,7 +150,7 @@ static int jpeg_v2_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - cancel_delayed_work_sync(&adev->vcn.idle_work); + cancel_delayed_work_sync(&adev->jpeg.idle_work); if (adev->jpeg.cur_state != AMD_PG_STATE_GATE && RREG32_SOC15(JPEG, 0, mmUVD_JRBC_STATUS)) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c index 5063a38801d69..9ac421486f05f 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c @@ -211,7 +211,7 @@ static int jpeg_v2_5_hw_fini(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int i; - cancel_delayed_work_sync(&adev->vcn.idle_work); + cancel_delayed_work_sync(&adev->jpeg.idle_work); for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { if (adev->jpeg.harvest_config & (1 << i)) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c index 10adbb7cbf539..e0df6800502ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c @@ -164,7 +164,7 @@ static int jpeg_v3_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - cancel_delayed_work_sync(&adev->vcn.idle_work); + cancel_delayed_work_sync(&adev->jpeg.idle_work); if (adev->jpeg.cur_state != AMD_PG_STATE_GATE && RREG32_SOC15(JPEG, 0, mmUVD_JRBC_STATUS)) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c index 193dfac5dc76b..eca1963c33b6b 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c @@ -202,7 +202,7 @@ static int jpeg_v4_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - cancel_delayed_work_sync(&adev->vcn.idle_work); + cancel_delayed_work_sync(&adev->jpeg.idle_work); if (!amdgpu_sriov_vf(adev)) { if (adev->jpeg.cur_state != AMD_PG_STATE_GATE && RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS)) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c index b48e2412e6cc1..1d9e3b101c3a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c @@ -227,7 +227,7 @@ static int jpeg_v4_0_5_hw_fini(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int i; - cancel_delayed_work_sync(&adev->vcn.idle_work); + cancel_delayed_work_sync(&adev->jpeg.idle_work); for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { if (adev->jpeg.harvest_config & (1 << i)) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c index 686f9605239d0..58fb1e5fa89c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c @@ -168,7 +168,7 @@ static int jpeg_v5_0_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - cancel_delayed_work_sync(&adev->vcn.idle_work); + cancel_delayed_work_sync(&adev->jpeg.idle_work); if (adev->jpeg.cur_state != AMD_PG_STATE_GATE && RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS)) diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_11.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_11.c index 7a9adfda5814a..814ab59fdd4a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_11.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_11.c @@ -275,6 +275,15 @@ static void nbio_v7_11_init_registers(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regBIF_BIF256_CI256_RC3X4_USB4_PCIE_MST_CTRL_3, data); + switch (adev->ip_versions[NBIO_HWIP][0]) { + case IP_VERSION(7, 11, 0): + case IP_VERSION(7, 11, 1): + case IP_VERSION(7, 11, 2): + case IP_VERSION(7, 11, 3): + data = RREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF5_STRAP4) & ~BIT(23); + WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF5_STRAP4, data); + break; + } } static void nbio_v7_11_update_medium_grain_clock_gating(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/sienna_cichlid.c b/drivers/gpu/drm/amd/amdgpu/sienna_cichlid.c index 9b01e074af471..2594467bdd873 100644 --- a/drivers/gpu/drm/amd/amdgpu/sienna_cichlid.c +++ b/drivers/gpu/drm/amd/amdgpu/sienna_cichlid.c @@ -220,6 +220,7 @@ sienna_cichlid_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl, int r; struct amdgpu_device *tmp_adev = (struct amdgpu_device *)reset_ctl->handle; + amdgpu_set_init_level(tmp_adev, AMDGPU_INIT_LEVEL_RESET_RECOVERY); dev_info(tmp_adev->dev, "GPU reset succeeded, trying to resume\n"); r = sienna_cichlid_mode2_restore_ip(tmp_adev); @@ -237,6 +238,7 @@ sienna_cichlid_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl, amdgpu_irq_gpu_reset_resume_helper(tmp_adev); + amdgpu_set_init_level(tmp_adev, AMDGPU_INIT_LEVEL_DEFAULT); r = amdgpu_ib_ring_tests(tmp_adev); if (r) { dev_err(tmp_adev->dev, diff --git a/drivers/gpu/drm/amd/amdgpu/smu_v13_0_10.c b/drivers/gpu/drm/amd/amdgpu/smu_v13_0_10.c index e70ebad3f9fac..70569ea906bca 100644 --- a/drivers/gpu/drm/amd/amdgpu/smu_v13_0_10.c +++ b/drivers/gpu/drm/amd/amdgpu/smu_v13_0_10.c @@ -221,6 +221,7 @@ smu_v13_0_10_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl, int r; struct amdgpu_device *tmp_adev = (struct amdgpu_device *)reset_ctl->handle; + amdgpu_set_init_level(tmp_adev, AMDGPU_INIT_LEVEL_RESET_RECOVERY); dev_info(tmp_adev->dev, "GPU reset succeeded, trying to resume\n"); r = smu_v13_0_10_mode2_restore_ip(tmp_adev); @@ -234,6 +235,7 @@ smu_v13_0_10_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl, amdgpu_irq_gpu_reset_resume_helper(tmp_adev); + amdgpu_set_init_level(tmp_adev, AMDGPU_INIT_LEVEL_DEFAULT); r = amdgpu_ib_ring_tests(tmp_adev); if (r) { dev_err(tmp_adev->dev, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 5512259cac79d..fcc8511e91ee0 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -225,6 +225,10 @@ static int vcn_v4_0_sw_init(struct amdgpu_ip_block *ip_block) vcn_v4_0_fw_shared_init(adev, i); } + /* TODO: Add queue reset mask when FW fully supports it */ + adev->vcn.supported_reset = + amdgpu_get_soft_full_reset_mask(&adev->vcn.inst[0].ring_enc[0]); + if (amdgpu_sriov_vf(adev)) { r = amdgpu_virt_alloc_mm_table(adev); if (r) @@ -247,6 +251,10 @@ static int vcn_v4_0_sw_init(struct amdgpu_ip_block *ip_block) adev->vcn.ip_dump = ptr; } + r = amdgpu_vcn_sysfs_reset_mask_init(adev); + if (r) + return r; + return 0; } @@ -284,6 +292,7 @@ static int vcn_v4_0_sw_fini(struct amdgpu_ip_block *ip_block) if (r) return r; + amdgpu_vcn_sysfs_reset_mask_fini(adev); r = amdgpu_vcn_sw_fini(adev); kfree(adev->vcn.ip_dump); diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c index cf808a153fce7..3f69b9b2bcd07 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c @@ -123,6 +123,20 @@ static int vcn_v4_0_3_early_init(struct amdgpu_ip_block *ip_block) return amdgpu_vcn_early_init(adev); } +static int vcn_v4_0_3_fw_shared_init(struct amdgpu_device *adev, int inst_idx) +{ + struct amdgpu_vcn4_fw_shared *fw_shared; + + fw_shared = adev->vcn.inst[inst_idx].fw_shared.cpu_addr; + fw_shared->present_flag_0 = cpu_to_le32(AMDGPU_FW_SHARED_FLAG_0_UNIFIED_QUEUE); + fw_shared->sq.is_enabled = 1; + + if (amdgpu_vcnfw_log) + amdgpu_vcn_fwlog_init(&adev->vcn.inst[inst_idx]); + + return 0; +} + /** * vcn_v4_0_3_sw_init - sw init for VCN block * @@ -155,8 +169,6 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) return r; for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - volatile struct amdgpu_vcn4_fw_shared *fw_shared; - vcn_inst = GET_INST(VCN, i); ring = &adev->vcn.inst[i].ring_enc[0]; @@ -179,14 +191,13 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr; - fw_shared->present_flag_0 = cpu_to_le32(AMDGPU_FW_SHARED_FLAG_0_UNIFIED_QUEUE); - fw_shared->sq.is_enabled = true; - - if (amdgpu_vcnfw_log) - amdgpu_vcn_fwlog_init(&adev->vcn.inst[i]); + vcn_v4_0_3_fw_shared_init(adev, i); } + /* TODO: Add queue reset mask when FW fully supports it */ + adev->vcn.supported_reset = + amdgpu_get_soft_full_reset_mask(&adev->vcn.inst[0].ring_enc[0]); + if (amdgpu_sriov_vf(adev)) { r = amdgpu_virt_alloc_mm_table(adev); if (r) @@ -213,6 +224,10 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) adev->vcn.ip_dump = ptr; } + r = amdgpu_vcn_sysfs_reset_mask_init(adev); + if (r) + return r; + return 0; } @@ -246,6 +261,7 @@ static int vcn_v4_0_3_sw_fini(struct amdgpu_ip_block *ip_block) if (r) return r; + amdgpu_vcn_sysfs_reset_mask_fini(adev); r = amdgpu_vcn_sw_fini(adev); kfree(adev->vcn.ip_dump); @@ -280,6 +296,8 @@ static int vcn_v4_0_3_hw_init(struct amdgpu_ip_block *ip_block) } } else { for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { + struct amdgpu_vcn4_fw_shared *fw_shared; + vcn_inst = GET_INST(VCN, i); ring = &adev->vcn.inst[i].ring_enc[0]; @@ -303,6 +321,11 @@ static int vcn_v4_0_3_hw_init(struct amdgpu_ip_block *ip_block) regVCN_RB1_DB_CTRL); } + /* Re-init fw_shared when RAS fatal error occurred */ + fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr; + if (!fw_shared->sq.is_enabled) + vcn_v4_0_3_fw_shared_init(adev, i); + r = amdgpu_ring_test_helper(ring); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c index fe2cc1a80c13e..bd3d2bbdc16bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -170,6 +170,10 @@ static int vcn_v5_0_0_sw_init(struct amdgpu_ip_block *ip_block) amdgpu_vcn_fwlog_init(&adev->vcn.inst[i]); } + /* TODO: Add queue reset mask when FW fully supports it */ + adev->vcn.supported_reset = + amdgpu_get_soft_full_reset_mask(&adev->vcn.inst[0].ring_enc[0]); + if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) adev->vcn.pause_dpg_mode = vcn_v5_0_0_pause_dpg_mode; @@ -181,6 +185,11 @@ static int vcn_v5_0_0_sw_init(struct amdgpu_ip_block *ip_block) } else { adev->vcn.ip_dump = ptr; } + + r = amdgpu_vcn_sysfs_reset_mask_init(adev); + if (r) + return r; + return 0; } @@ -215,6 +224,7 @@ static int vcn_v5_0_0_sw_fini(struct amdgpu_ip_block *ip_block) if (r) return r; + amdgpu_vcn_sysfs_reset_mask_fini(adev); r = amdgpu_vcn_sw_fini(adev); kfree(adev->vcn.ip_dump); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c index 4843dcb9a5f79..2b0a830f5b294 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c @@ -125,7 +125,7 @@ static bool kq_initialize(struct kernel_queue *kq, struct kfd_node *dev, memset(kq->pq_kernel_addr, 0, queue_size); memset(kq->rptr_kernel, 0, sizeof(*kq->rptr_kernel)); - memset(kq->wptr_kernel, 0, sizeof(*kq->wptr_kernel)); + memset(kq->wptr_kernel, 0, dev->kfd->device_info.doorbell_size); prop.queue_size = queue_size; prop.is_interop = false; @@ -306,12 +306,17 @@ int kq_submit_packet(struct kernel_queue *kq) if (amdgpu_amdkfd_is_fed(kq->dev->adev)) return -EIO; + /* Make sure ring buffer is updated before wptr updated */ + mb(); + if (kq->dev->kfd->device_info.doorbell_size == 8) { *kq->wptr64_kernel = kq->pending_wptr64; + mb(); /* Make sure wptr updated before ring doorbell */ write_kernel_doorbell64(kq->queue->properties.doorbell_ptr, kq->pending_wptr64); } else { *kq->wptr_kernel = kq->pending_wptr; + mb(); /* Make sure wptr updated before ring doorbell */ write_kernel_doorbell(kq->queue->properties.doorbell_ptr, kq->pending_wptr); } diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig index df17e79c45c76..11e3f2f3b1745 100644 --- a/drivers/gpu/drm/amd/display/Kconfig +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -7,20 +7,21 @@ menu "Display Engine Configuration" config DRM_AMD_DC bool "AMD DC - Enable new display engine" default y - depends on BROKEN || !CC_IS_CLANG || ARM64 || RISCV || SPARC64 || X86_64 + depends on BROKEN || !CC_IS_CLANG || ARM64 || LOONGARCH || RISCV || SPARC64 || X86_64 select SND_HDA_COMPONENT if SND_HDA_CORE # !CC_IS_CLANG: https://github.com/ClangBuiltLinux/linux/issues/1752 - select DRM_AMD_DC_FP if ARCH_HAS_KERNEL_FPU_SUPPORT && !(CC_IS_CLANG && (ARM64 || RISCV)) + select DRM_AMD_DC_FP if ARCH_HAS_KERNEL_FPU_SUPPORT && !(CC_IS_CLANG && (ARM64 || LOONGARCH || RISCV)) help Choose this option if you want to use the new display engine support for AMDGPU. This adds required support for Vega and Raven ASICs. - calculate_bandwidth() is presently broken on all !(X86_64 || SPARC64 || ARM64) - architectures built with Clang (all released versions), whereby the stack - frame gets blown up to well over 5k. This would cause an immediate kernel - panic on most architectures. We'll revert this when the following bug report - has been resolved: https://github.com/llvm/llvm-project/issues/41896. + calculate_bandwidth() is presently broken on all !(X86_64 || SPARC64 || + ARM64 || LOONGARCH || RISCV) architectures built with Clang (all released + versions), whereby the stack frame gets blown up to well over 5k. This + would cause an immediate kernel panic on most architectures. We'll revert + this when the following bug report has been resolved: + https://github.com/llvm/llvm-project/issues/41896. config DRM_AMD_DC_FP def_bool n diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 7872c6cabb14c..1dd26d5df6b95 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -3141,7 +3141,10 @@ static void restore_planes_and_stream_state( return; for (i = 0; i < status->plane_count; i++) { + /* refcount will always be valid, restore everything else */ + struct kref refcount = status->plane_states[i]->refcount; *status->plane_states[i] = scratch->plane_states[i]; + status->plane_states[i]->refcount = refcount; } *stream = scratch->stream_state; } @@ -6097,11 +6100,11 @@ struct dc_power_profile dc_get_power_profile_for_dc_state(const struct dc_state { struct dc_power_profile profile = { 0 }; - if (!context || !context->clk_mgr || !context->clk_mgr->ctx || !context->clk_mgr->ctx->dc) + profile.power_level = !context->bw_ctx.bw.dcn.clk.p_state_change_support; + if (!context->clk_mgr || !context->clk_mgr->ctx || !context->clk_mgr->ctx->dc) return profile; struct dc *dc = context->clk_mgr->ctx->dc; - if (dc->res_pool->funcs->get_power_profile) profile.power_level = dc->res_pool->funcs->get_power_profile(context); return profile; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 0419ee7f22a53..252af83e34a53 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -898,6 +898,9 @@ void hwss_setup_dpp(union block_sequence_params *params) struct dpp *dpp = pipe_ctx->plane_res.dpp; struct dc_plane_state *plane_state = pipe_ctx->plane_state; + if (!plane_state) + return; + if (dpp && dpp->funcs->dpp_setup) { // program the input csc dpp->funcs->dpp_setup(dpp, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 33125b95c3a13..619fad17de554 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1501,6 +1501,10 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) res = spl_calculate_scaler_params(spl_in, spl_out); // Convert respective out params from SPL to scaler data translate_SPL_out_params_to_pipe_ctx(pipe_ctx, spl_out); + + /* Ignore scaler failure if pipe context plane is phantom plane */ + if (!res && plane_state->is_phantom) + res = true; } else { #endif /* depends on h_active */ @@ -1571,6 +1575,10 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) &plane_state->scaling_quality); } + /* Ignore scaler failure if pipe context plane is phantom plane */ + if (!res && plane_state->is_phantom) + res = true; + if (res && (pipe_ctx->plane_res.scl_data.taps.v_taps != temp.v_taps || pipe_ctx->plane_res.scl_data.taps.h_taps != temp.h_taps || pipe_ctx->plane_res.scl_data.taps.v_taps_c != temp.v_taps_c || diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index e143fab00a861..1040519358841 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -55,7 +55,7 @@ struct aux_payload; struct set_config_cmd_payload; struct dmub_notification; -#define DC_VER "3.2.309" +#define DC_VER "3.2.310" #define MAX_SURFACES 3 #define MAX_PLANES 6 diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c index 838d72eaa87fb..b363f5360818d 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c @@ -1392,10 +1392,10 @@ static void dccg35_set_dtbclk_dto( /* The recommended programming sequence to enable DTBCLK DTO to generate * valid pixel HPO DPSTREAM ENCODER, specifies that DTO source select should - * be set only after DTO is enabled + * be set only after DTO is enabled. + * PIPEx_DTO_SRC_SEL should not be programmed during DTBCLK update since OTG may still be on, and the + * programming is handled in program_pix_clk() regardless, so it can be removed from here. */ - REG_UPDATE(OTG_PIXEL_RATE_CNTL[params->otg_inst], - PIPE_DTO_SRC_SEL[params->otg_inst], 2); } else { switch (params->otg_inst) { case 0: @@ -1412,9 +1412,12 @@ static void dccg35_set_dtbclk_dto( break; } - REG_UPDATE_2(OTG_PIXEL_RATE_CNTL[params->otg_inst], - DTBCLK_DTO_ENABLE[params->otg_inst], 0, - PIPE_DTO_SRC_SEL[params->otg_inst], params->is_hdmi ? 0 : 1); + /** + * PIPEx_DTO_SRC_SEL should not be programmed during DTBCLK update since OTG may still be on, and the + * programming is handled in program_pix_clk() regardless, so it can be removed from here. + */ + REG_UPDATE(OTG_PIXEL_RATE_CNTL[params->otg_inst], + DTBCLK_DTO_ENABLE[params->otg_inst], 0); REG_WRITE(DTBCLK_DTO_MODULO[params->otg_inst], 0); REG_WRITE(DTBCLK_DTO_PHASE[params->otg_inst], 0); diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index 92e43a1e4dd46..601320b1be817 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -11,6 +11,7 @@ #define DML2_MAX_FMT_420_BUFFER_WIDTH 4096 #define DML_MAX_NUM_OF_SLICES_PER_DSC 4 +#define ALLOW_SDPIF_RATE_LIMIT_PRE_CSTATE const char *dml2_core_internal_bw_type_str(enum dml2_core_internal_bw_type bw_type) { @@ -3886,6 +3887,10 @@ static void CalculateSwathAndDETConfiguration(struct dml2_core_internal_scratch #endif *p->hw_debug5 = false; +#ifdef ALLOW_SDPIF_RATE_LIMIT_PRE_CSTATE + if (p->NumberOfActiveSurfaces > 1) + *p->hw_debug5 = true; +#else for (unsigned int k = 0; k < p->NumberOfActiveSurfaces; ++k) { if (!(p->mrq_present) && (!(*p->UnboundedRequestEnabled)) && (TotalActiveDPP == 1) && p->display_cfg->plane_descriptors[k].surface.dcc.enable @@ -3901,6 +3906,7 @@ static void CalculateSwathAndDETConfiguration(struct dml2_core_internal_scratch dml2_printf("DML::%s: k=%u hw_debug5 = %u\n", __func__, k, *p->hw_debug5); #endif } +#endif } static enum dml2_odm_mode DecideODMMode(unsigned int HActive, diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c index 6eccf0241d857..1ed21c1b86a5b 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c @@ -258,12 +258,25 @@ static unsigned int find_preferred_pipe_candidates(const struct dc_state *existi * However this condition comes with a caveat. We need to ignore pipes that will * require a change in OPP but still have the same stream id. For example during * an MPC to ODM transiton. + * + * Adding check to avoid pipe select on the head pipe by utilizing dc resource + * helper function resource_get_primary_dpp_pipe and comparing the pipe index. */ if (existing_state) { for (i = 0; i < pipe_count; i++) { if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) { + struct pipe_ctx *head_pipe = + resource_is_pipe_type(&existing_state->res_ctx.pipe_ctx[i], DPP_PIPE) ? + resource_get_primary_dpp_pipe(&existing_state->res_ctx.pipe_ctx[i]) : + NULL; + + // we should always respect the head pipe from selection + if (head_pipe && head_pipe->pipe_idx == i) + continue; if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp && - existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) + existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i && + (existing_state->res_ctx.pipe_ctx[i].prev_odm_pipe || + existing_state->res_ctx.pipe_ctx[i].next_odm_pipe)) continue; preferred_pipe_candidates[num_preferred_candidates++] = i; @@ -292,6 +305,14 @@ static unsigned int find_last_resort_pipe_candidates(const struct dc_state *exis */ if (existing_state) { for (i = 0; i < pipe_count; i++) { + struct pipe_ctx *head_pipe = + resource_is_pipe_type(&existing_state->res_ctx.pipe_ctx[i], DPP_PIPE) ? + resource_get_primary_dpp_pipe(&existing_state->res_ctx.pipe_ctx[i]) : + NULL; + + // we should always respect the head pipe from selection + if (head_pipe && head_pipe->pipe_idx == i) + continue; if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp && existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) || existing_state->res_ctx.pipe_ctx[i].stream_res.tg) diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index ebd5df1a36e8b..d9aaebfa3a0a7 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -1093,14 +1093,11 @@ static bool setup_dsc_config( if (!is_dsc_possible) goto done; - // Final decission: can we do DSC or not? - if (is_dsc_possible) { - // Fill out the rest of DSC settings - dsc_cfg->block_pred_enable = dsc_common_caps.is_block_pred_supported; - dsc_cfg->linebuf_depth = dsc_common_caps.lb_bit_depth; - dsc_cfg->version_minor = (dsc_common_caps.dsc_version & 0xf0) >> 4; - dsc_cfg->is_dp = dsc_sink_caps->is_dp; - } + /* Fill out the rest of DSC settings */ + dsc_cfg->block_pred_enable = dsc_common_caps.is_block_pred_supported; + dsc_cfg->linebuf_depth = dsc_common_caps.lb_bit_depth; + dsc_cfg->version_minor = (dsc_common_caps.dsc_version & 0xf0) >> 4; + dsc_cfg->is_dp = dsc_sink_caps->is_dp; done: if (!is_dsc_possible) diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h index 4bd1dda077196..9fbd45c7dfef2 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h @@ -200,6 +200,7 @@ struct dcn_hubbub_registers { uint32_t DCHUBBUB_ARB_FRAC_URG_BW_MALL_B; uint32_t DCHUBBUB_TIMEOUT_DETECTION_CTRL1; uint32_t DCHUBBUB_TIMEOUT_DETECTION_CTRL2; + uint32_t DCHUBBUB_CTRL_STATUS; }; #define HUBBUB_REG_FIELD_LIST_DCN32(type) \ @@ -320,7 +321,12 @@ struct dcn_hubbub_registers { type DCHUBBUB_TIMEOUT_REQ_STALL_THRESHOLD;\ type DCHUBBUB_TIMEOUT_PSTATE_STALL_THRESHOLD;\ type DCHUBBUB_TIMEOUT_DETECTION_EN;\ - type DCHUBBUB_TIMEOUT_TIMER_RESET + type DCHUBBUB_TIMEOUT_TIMER_RESET;\ + type ROB_UNDERFLOW_STATUS;\ + type ROB_OVERFLOW_STATUS;\ + type ROB_OVERFLOW_CLEAR;\ + type DCHUBBUB_HW_DEBUG;\ + type CSTATE_SWATH_CHK_GOOD_MODE #define HUBBUB_STUTTER_REG_FIELD_LIST(type) \ type DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A;\ diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.h index 036bb3e6c9575..46d8f5c70750a 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.h +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.h @@ -96,6 +96,7 @@ struct dcn20_hubbub { unsigned int det1_size; unsigned int det2_size; unsigned int det3_size; + bool allow_sdpif_rate_limit_when_cstate_req; }; void hubbub2_construct(struct dcn20_hubbub *hubbub, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c index 5d658e9bef640..92fab471b1836 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c @@ -1192,15 +1192,35 @@ static void dcn401_wait_for_det_update(struct hubbub *hubbub, int hubp_inst) } } -static void dcn401_program_timeout_thresholds(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs) +static bool dcn401_program_arbiter(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs, bool safe_to_lower) { struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub); + bool wm_pending = false; + uint32_t temp; + /* request backpressure and outstanding return threshold (unused)*/ //REG_UPDATE(DCHUBBUB_TIMEOUT_DETECTION_CTRL1, DCHUBBUB_TIMEOUT_REQ_STALL_THRESHOLD, arb_regs->req_stall_threshold); /* P-State stall threshold */ REG_UPDATE(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_PSTATE_STALL_THRESHOLD, arb_regs->pstate_stall_threshold); + + if (safe_to_lower || arb_regs->allow_sdpif_rate_limit_when_cstate_req > hubbub2->allow_sdpif_rate_limit_when_cstate_req) { + hubbub2->allow_sdpif_rate_limit_when_cstate_req = arb_regs->allow_sdpif_rate_limit_when_cstate_req; + + /* only update the required bits */ + REG_GET(DCHUBBUB_CTRL_STATUS, DCHUBBUB_HW_DEBUG, &temp); + if (hubbub2->allow_sdpif_rate_limit_when_cstate_req) { + temp |= (1 << 5); + } else { + temp &= ~(1 << 5); + } + REG_UPDATE(DCHUBBUB_CTRL_STATUS, DCHUBBUB_HW_DEBUG, temp); + } else { + wm_pending = true; + } + + return wm_pending; } static const struct hubbub_funcs hubbub4_01_funcs = { @@ -1226,7 +1246,7 @@ static const struct hubbub_funcs hubbub4_01_funcs = { .program_det_segments = dcn401_program_det_segments, .program_compbuf_segments = dcn401_program_compbuf_segments, .wait_for_det_update = dcn401_wait_for_det_update, - .program_timeout_thresholds = dcn401_program_timeout_thresholds, + .program_arbiter = dcn401_program_arbiter, }; void hubbub401_construct(struct dcn20_hubbub *hubbub2, diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h index 5f1960722ebdd..b1d9ea9d1c3d6 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h @@ -128,7 +128,12 @@ HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL1, DCHUBBUB_TIMEOUT_REQ_STALL_THRESHOLD, mask_sh),\ HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_PSTATE_STALL_THRESHOLD, mask_sh),\ HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_DETECTION_EN, mask_sh),\ - HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_TIMER_RESET, mask_sh) + HUBBUB_SF(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_TIMER_RESET, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_UNDERFLOW_STATUS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_OVERFLOW_STATUS, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_OVERFLOW_CLEAR, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, DCHUBBUB_HW_DEBUG, mask_sh),\ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, CSTATE_SWATH_CHK_GOOD_MODE, mask_sh) bool hubbub401_program_urgent_watermarks( struct hubbub *hubbub, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 05424a9af58bd..b029ec1b26d36 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -1925,9 +1925,9 @@ static void dcn20_program_pipe( dc->res_pool->hubbub, pipe_ctx->plane_res.hubp->inst, pipe_ctx->hubp_regs.det_size); } - if (pipe_ctx->update_flags.raw || - (pipe_ctx->plane_state && pipe_ctx->plane_state->update_flags.raw) || - pipe_ctx->stream->update_flags.raw) + if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw || + pipe_ctx->plane_state->update_flags.raw || + pipe_ctx->stream->update_flags.raw)) dcn20_update_dchubp_dpp(dc, pipe_ctx, context); if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable || diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index e8cc1bfa73f31..5de11e2837c01 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -1488,6 +1488,10 @@ void dcn401_prepare_bandwidth(struct dc *dc, &context->bw_ctx.bw.dcn.watermarks, dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, false); + /* update timeout thresholds */ + if (hubbub->funcs->program_arbiter) { + dc->wm_optimized_required |= hubbub->funcs->program_arbiter(hubbub, &context->bw_ctx.bw.dcn.arb_regs, false); + } /* decrease compbuf size */ if (hubbub->funcs->program_compbuf_segments) { @@ -1529,6 +1533,10 @@ void dcn401_optimize_bandwidth( &context->bw_ctx.bw.dcn.watermarks, dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, true); + /* update timeout thresholds */ + if (hubbub->funcs->program_arbiter) { + hubbub->funcs->program_arbiter(hubbub, &context->bw_ctx.bw.dcn.arb_regs, true); + } if (dc->clk_mgr->dc_mode_softmax_enabled) if (dc->clk_mgr->clks.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 && @@ -1554,11 +1562,6 @@ void dcn401_optimize_bandwidth( pipe_ctx->dlg_regs.min_dst_y_next_start); } } - - /* update timeout thresholds */ - if (hubbub->funcs->program_timeout_thresholds) { - hubbub->funcs->program_timeout_thresholds(hubbub, &context->bw_ctx.bw.dcn.arb_regs); - } } void dcn401_fams2_global_control_lock(struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h index 6c1d41c0f0992..52b745667ef75 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h @@ -228,7 +228,7 @@ struct hubbub_funcs { void (*program_det_segments)(struct hubbub *hubbub, int hubp_inst, unsigned det_buffer_size_seg); void (*program_compbuf_segments)(struct hubbub *hubbub, unsigned compbuf_size_seg, bool safe_to_increase); void (*wait_for_det_update)(struct hubbub *hubbub, int hubp_inst); - void (*program_timeout_thresholds)(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs); + bool (*program_arbiter)(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs, bool safe_to_lower); }; struct hubbub { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h index 7c8d61db153d3..19568c3596694 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h @@ -612,7 +612,8 @@ void dcn401_prepare_mcache_programming(struct dc *dc, struct dc_state *context); SR(DCHUBBUB_SDPIF_CFG1), \ SR(DCHUBBUB_MEM_PWR_MODE_CTRL), \ SR(DCHUBBUB_TIMEOUT_DETECTION_CTRL1), \ - SR(DCHUBBUB_TIMEOUT_DETECTION_CTRL2) + SR(DCHUBBUB_TIMEOUT_DETECTION_CTRL2), \ + SR(DCHUBBUB_CTRL_STATUS) /* DCCG */ diff --git a/drivers/gpu/drm/amd/display/dc/spl/dc_spl.c b/drivers/gpu/drm/amd/display/dc/spl/dc_spl.c index 614276200aa08..73a65913cb124 100644 --- a/drivers/gpu/drm/amd/display/dc/spl/dc_spl.c +++ b/drivers/gpu/drm/amd/display/dc/spl/dc_spl.c @@ -99,7 +99,7 @@ static struct spl_rect calculate_plane_rec_in_timing_active( * * recout_x = 128 + round(plane_x * 2304 / 1920) * recout_w = 128 + round((plane_x + plane_w) * 2304 / 1920) - recout_x - * recout_y = 0 + round(plane_y * 1440 / 1280) + * recout_y = 0 + round(plane_y * 1440 / 1200) * recout_h = 0 + round((plane_y + plane_h) * 1440 / 1200) - recout_y * * NOTE: fixed point division is not error free. To reduce errors @@ -739,14 +739,13 @@ static enum scl_mode spl_get_dscl_mode(const struct spl_in *spl_in, return SCL_MODE_SCALING_444_RGB_ENABLE; } - /* Bypass YUV if at 1:1 with no ISHARP or if doing 2:1 YUV - * downscale without EASF + /* + * Bypass YUV if Y is 1:1 with no ISHARP + * Do not bypass UV at 1:1 for cositing to be applied */ - if ((!enable_isharp) && (!enable_easf)) { + if (!enable_isharp) { if (data->ratios.horz.value == one && data->ratios.vert.value == one) return SCL_MODE_SCALING_420_LUMA_BYPASS; - if (data->ratios.horz_c.value == one && data->ratios.vert_c.value == one) - return SCL_MODE_SCALING_420_CHROMA_BYPASS; } return SCL_MODE_SCALING_420_YCBCR_ENABLE; @@ -933,6 +932,7 @@ static bool spl_get_optimal_number_of_taps( int min_taps_y, min_taps_c; enum lb_memory_config lb_config; bool skip_easf = false; + bool is_ycbcr = spl_dscl_is_video_format(spl_in->basic_in.format); if (spl_scratch->scl_data.viewport.width > spl_scratch->scl_data.h_active && max_downscale_src_width != 0 && @@ -1074,10 +1074,9 @@ static bool spl_get_optimal_number_of_taps( /* Sharpener requires scaler to be enabled, including for 1:1 * Check if ISHARP can be enabled - * If ISHARP is not enabled, for 1:1, set taps to 1 and disable - * EASF - * For case of 2:1 YUV where chroma is 1:1, set taps to 1 if - * EASF is not enabled + * If ISHARP is not enabled, set taps to 1 if ratio is 1:1 + * except for chroma taps. Keep previous taps so it can + * handle cositing */ *enable_isharp = spl_get_isharp_en(spl_in, spl_scratch); @@ -1087,20 +1086,28 @@ static bool spl_get_optimal_number_of_taps( spl_scratch->scl_data.taps.h_taps = 1; spl_scratch->scl_data.taps.v_taps = 1; - if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c)) + if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_ycbcr) spl_scratch->scl_data.taps.h_taps_c = 1; - if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c)) + if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_ycbcr) spl_scratch->scl_data.taps.v_taps_c = 1; *enable_easf_v = false; *enable_easf_h = false; } else { if ((!*enable_easf_h) && + (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz))) + spl_scratch->scl_data.taps.h_taps = 1; + + if ((!*enable_easf_v) && + (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert))) + spl_scratch->scl_data.taps.v_taps = 1; + + if ((!*enable_easf_h) && !is_ycbcr && (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c))) spl_scratch->scl_data.taps.h_taps_c = 1; - if ((!*enable_easf_v) && + if ((!*enable_easf_v) && !is_ycbcr && (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c))) spl_scratch->scl_data.taps.v_taps_c = 1; } @@ -1111,8 +1118,7 @@ static bool spl_get_optimal_number_of_taps( static void spl_set_black_color_data(enum spl_pixel_format format, struct scl_black_color *scl_black_color) { - bool ycbcr = format >= SPL_PIXEL_FORMAT_VIDEO_BEGIN - && format <= SPL_PIXEL_FORMAT_VIDEO_END; + bool ycbcr = spl_dscl_is_video_format(format); if (ycbcr) { scl_black_color->offset_rgb_y = BLACK_OFFSET_RGB_Y; scl_black_color->offset_rgb_cbcr = BLACK_OFFSET_CBCR; @@ -1746,6 +1752,32 @@ static void spl_set_isharp_data(struct dscl_prog_data *dscl_prog_data, spl_set_blur_scale_data(dscl_prog_data, data); } +/* Calculate recout, scaling ratio, and viewport, then get optimal number of taps */ +static bool spl_calculate_number_of_taps(struct spl_in *spl_in, struct spl_scratch *spl_scratch, struct spl_out *spl_out, + bool *enable_easf_v, bool *enable_easf_h, bool *enable_isharp) +{ + bool res = false; + + memset(spl_scratch, 0, sizeof(struct spl_scratch)); + spl_scratch->scl_data.h_active = spl_in->h_active; + spl_scratch->scl_data.v_active = spl_in->v_active; + + // All SPL calls + /* recout calculation */ + /* depends on h_active */ + spl_calculate_recout(spl_in, spl_scratch, spl_out); + /* depends on pixel format */ + spl_calculate_scaling_ratios(spl_in, spl_scratch, spl_out); + /* depends on scaling ratios and recout, does not calculate offset yet */ + spl_calculate_viewport_size(spl_in, spl_scratch); + + res = spl_get_optimal_number_of_taps( + spl_in->basic_out.max_downscale_src_width, spl_in, + spl_scratch, &spl_in->scaling_quality, enable_easf_v, + enable_easf_h, enable_isharp); + return res; +} + /* Calculate scaler parameters */ bool spl_calculate_scaler_params(struct spl_in *spl_in, struct spl_out *spl_out) { @@ -1760,23 +1792,9 @@ bool spl_calculate_scaler_params(struct spl_in *spl_in, struct spl_out *spl_out) bool enable_isharp = false; const struct spl_scaler_data *data = &spl_scratch.scl_data; - memset(&spl_scratch, 0, sizeof(struct spl_scratch)); - spl_scratch.scl_data.h_active = spl_in->h_active; - spl_scratch.scl_data.v_active = spl_in->v_active; + res = spl_calculate_number_of_taps(spl_in, &spl_scratch, spl_out, + &enable_easf_v, &enable_easf_h, &enable_isharp); - // All SPL calls - /* recout calculation */ - /* depends on h_active */ - spl_calculate_recout(spl_in, &spl_scratch, spl_out); - /* depends on pixel format */ - spl_calculate_scaling_ratios(spl_in, &spl_scratch, spl_out); - /* depends on scaling ratios and recout, does not calculate offset yet */ - spl_calculate_viewport_size(spl_in, &spl_scratch); - - res = spl_get_optimal_number_of_taps( - spl_in->basic_out.max_downscale_src_width, spl_in, - &spl_scratch, &spl_in->scaling_quality, &enable_easf_v, - &enable_easf_h, &enable_isharp); /* * Depends on recout, scaling ratios, h_active and taps * May need to re-check lb size after this in some obscure scenario @@ -1824,3 +1842,20 @@ bool spl_calculate_scaler_params(struct spl_in *spl_in, struct spl_out *spl_out) return res; } + +/* External interface to get number of taps only */ +bool spl_get_number_of_taps(struct spl_in *spl_in, struct spl_out *spl_out) +{ + bool res = false; + bool enable_easf_v = false; + bool enable_easf_h = false; + bool enable_isharp = false; + struct spl_scratch spl_scratch; + struct dscl_prog_data *dscl_prog_data = spl_out->dscl_prog_data; + const struct spl_scaler_data *data = &spl_scratch.scl_data; + + res = spl_calculate_number_of_taps(spl_in, &spl_scratch, spl_out, + &enable_easf_v, &enable_easf_h, &enable_isharp); + spl_set_taps_data(dscl_prog_data, data); + return res; +} diff --git a/drivers/gpu/drm/amd/display/dc/spl/dc_spl.h b/drivers/gpu/drm/amd/display/dc/spl/dc_spl.h index 205e59a2a8ee8..02a2d6725ed58 100644 --- a/drivers/gpu/drm/amd/display/dc/spl/dc_spl.h +++ b/drivers/gpu/drm/amd/display/dc/spl/dc_spl.h @@ -13,4 +13,6 @@ bool spl_calculate_scaler_params(struct spl_in *spl_in, struct spl_out *spl_out); +bool spl_get_number_of_taps(struct spl_in *spl_in, struct spl_out *spl_out); + #endif /* __DC_SPL_H__ */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h index 5ebe4cb40f9db..c38a01742d6f0 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h +++ b/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h @@ -7571,6 +7571,8 @@ // base address: 0x10100000 #define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0 0xd000 #define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_BASE_IDX 5 +#define regRCC_DEV0_EPF5_STRAP4 0xd284 +#define regRCC_DEV0_EPF5_STRAP4_BASE_IDX 5 // addressBlock: nbio_nbif0_bif_rst_bif_rst_regblk diff --git a/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_sh_mask.h index eb8c556d9c930..3b96f1e5a1802 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_sh_mask.h @@ -50665,6 +50665,19 @@ #define RCC_STRAP0_RCC_DEV0_EPF0_STRAP0__STRAP_D1_SUPPORT_DEV0_F0_MASK 0x40000000L #define RCC_STRAP0_RCC_DEV0_EPF0_STRAP0__STRAP_D2_SUPPORT_DEV0_F0_MASK 0x80000000L +//RCC_DEV0_EPF5_STRAP4 +#define RCC_DEV0_EPF5_STRAP4__STRAP_ATOMIC_64BIT_EN_DEV0_F5__SHIFT 0x14 +#define RCC_DEV0_EPF5_STRAP4__STRAP_ATOMIC_EN_DEV0_F5__SHIFT 0x15 +#define RCC_DEV0_EPF5_STRAP4__STRAP_FLR_EN_DEV0_F5__SHIFT 0x16 +#define RCC_DEV0_EPF5_STRAP4__STRAP_PME_SUPPORT_DEV0_F5__SHIFT 0x17 +#define RCC_DEV0_EPF5_STRAP4__STRAP_INTERRUPT_PIN_DEV0_F5__SHIFT 0x1c +#define RCC_DEV0_EPF5_STRAP4__STRAP_AUXPWR_SUPPORT_DEV0_F5__SHIFT 0x1f +#define RCC_DEV0_EPF5_STRAP4__STRAP_ATOMIC_64BIT_EN_DEV0_F5_MASK 0x00100000L +#define RCC_DEV0_EPF5_STRAP4__STRAP_ATOMIC_EN_DEV0_F5_MASK 0x00200000L +#define RCC_DEV0_EPF5_STRAP4__STRAP_FLR_EN_DEV0_F5_MASK 0x00400000L +#define RCC_DEV0_EPF5_STRAP4__STRAP_PME_SUPPORT_DEV0_F5_MASK 0x0F800000L +#define RCC_DEV0_EPF5_STRAP4__STRAP_INTERRUPT_PIN_DEV0_F5_MASK 0x70000000L +#define RCC_DEV0_EPF5_STRAP4__STRAP_AUXPWR_SUPPORT_DEV0_F5_MASK 0x80000000L // addressBlock: nbio_nbif0_bif_rst_bif_rst_regblk //HARD_RST_CTRL diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index bb27c0d2a9ae8..67a5de5739436 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -357,11 +357,22 @@ struct dpm_clocks; struct amdgpu_xcp_metrics { /* Utilization Instantaneous (%) */ - u32 gfx_busy_inst[MAX_XCC]; - u16 jpeg_busy[NUM_JPEG_ENG]; - u16 vcn_busy[NUM_VCN]; + uint32_t gfx_busy_inst[MAX_XCC]; + uint16_t jpeg_busy[NUM_JPEG_ENG]; + uint16_t vcn_busy[NUM_VCN]; /* Utilization Accumulated (%) */ - u64 gfx_busy_acc[MAX_XCC]; + uint64_t gfx_busy_acc[MAX_XCC]; +}; + +struct amdgpu_xcp_metrics_v1_1 { + /* Utilization Instantaneous (%) */ + uint32_t gfx_busy_inst[MAX_XCC]; + uint16_t jpeg_busy[NUM_JPEG_ENG]; + uint16_t vcn_busy[NUM_VCN]; + /* Utilization Accumulated (%) */ + uint64_t gfx_busy_acc[MAX_XCC]; + /* Total App Clock Counter Accumulated */ + uint64_t gfx_below_host_limit_acc[MAX_XCC]; }; struct amd_pm_funcs { @@ -977,6 +988,105 @@ struct gpu_metrics_v1_6 { uint32_t pcie_lc_perf_other_end_recovery; }; +struct gpu_metrics_v1_7 { + struct metrics_table_header common_header; + + /* Temperature (Celsius) */ + uint16_t temperature_hotspot; + uint16_t temperature_mem; + uint16_t temperature_vrsoc; + + /* Power (Watts) */ + uint16_t curr_socket_power; + + /* Utilization (%) */ + uint16_t average_gfx_activity; + uint16_t average_umc_activity; // memory controller + + /* VRAM max bandwidthi (in GB/sec) at max memory clock */ + uint64_t mem_max_bandwidth; + + /* Energy (15.259uJ (2^-16) units) */ + uint64_t energy_accumulator; + + /* Driver attached timestamp (in ns) */ + uint64_t system_clock_counter; + + /* Accumulation cycle counter */ + uint32_t accumulation_counter; + + /* Accumulated throttler residencies */ + uint32_t prochot_residency_acc; + uint32_t ppt_residency_acc; + uint32_t socket_thm_residency_acc; + uint32_t vr_thm_residency_acc; + uint32_t hbm_thm_residency_acc; + + /* Clock Lock Status. Each bit corresponds to clock instance */ + uint32_t gfxclk_lock_status; + + /* Link width (number of lanes) and speed (in 0.1 GT/s) */ + uint16_t pcie_link_width; + uint16_t pcie_link_speed; + + /* XGMI bus width and bitrate (in Gbps) */ + uint16_t xgmi_link_width; + uint16_t xgmi_link_speed; + + /* Utilization Accumulated (%) */ + uint32_t gfx_activity_acc; + uint32_t mem_activity_acc; + + /*PCIE accumulated bandwidth (GB/sec) */ + uint64_t pcie_bandwidth_acc; + + /*PCIE instantaneous bandwidth (GB/sec) */ + uint64_t pcie_bandwidth_inst; + + /* PCIE L0 to recovery state transition accumulated count */ + uint64_t pcie_l0_to_recov_count_acc; + + /* PCIE replay accumulated count */ + uint64_t pcie_replay_count_acc; + + /* PCIE replay rollover accumulated count */ + uint64_t pcie_replay_rover_count_acc; + + /* PCIE NAK sent accumulated count */ + uint32_t pcie_nak_sent_count_acc; + + /* PCIE NAK received accumulated count */ + uint32_t pcie_nak_rcvd_count_acc; + + /* XGMI accumulated data transfer size(KiloBytes) */ + uint64_t xgmi_read_data_acc[NUM_XGMI_LINKS]; + uint64_t xgmi_write_data_acc[NUM_XGMI_LINKS]; + + /* XGMI link status(active/inactive) */ + uint16_t xgmi_link_status[NUM_XGMI_LINKS]; + + uint16_t padding; + + /* PMFW attached timestamp (10ns resolution) */ + uint64_t firmware_timestamp; + + /* Current clocks (Mhz) */ + uint16_t current_gfxclk[MAX_GFX_CLKS]; + uint16_t current_socclk[MAX_CLKS]; + uint16_t current_vclk0[MAX_CLKS]; + uint16_t current_dclk0[MAX_CLKS]; + uint16_t current_uclk; + + /* Number of current partition */ + uint16_t num_partition; + + /* XCP metrics stats */ + struct amdgpu_xcp_metrics_v1_1 xcp_stats[NUM_XCP]; + + /* PCIE other end recovery counter */ + uint32_t pcie_lc_perf_other_end_recovery; +}; + /* * gpu_metrics_v2_0 is not recommended as it's not naturally aligned. * Use gpu_metrics_v2_1 or later instead. diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c index b52ce135d84d4..d3ff6a831ed5d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c @@ -257,20 +257,18 @@ static int vega12_smu_init(struct pp_hwmgr *hwmgr) priv->smu_tables.entry[TABLE_WATERMARKS].size = sizeof(Watermarks_t); tools_size = 0x19000; - if (tools_size) { - ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev, - tools_size, - PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM, - &priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle, - &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr, - &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table); - if (ret) - goto err1; + ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev, + tools_size, + PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM, + &priv->smu_tables.entry[TABLE_PMSTATUSLOG].handle, + &priv->smu_tables.entry[TABLE_PMSTATUSLOG].mc_addr, + &priv->smu_tables.entry[TABLE_PMSTATUSLOG].table); + if (ret) + goto err1; - priv->smu_tables.entry[TABLE_PMSTATUSLOG].version = 0x01; - priv->smu_tables.entry[TABLE_PMSTATUSLOG].size = tools_size; - } + priv->smu_tables.entry[TABLE_PMSTATUSLOG].version = 0x01; + priv->smu_tables.entry[TABLE_PMSTATUSLOG].size = tools_size; /* allocate space for AVFS Fuse table */ ret = amdgpu_bo_create_kernel((struct amdgpu_device *)hwmgr->adev, diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 64f9179595766..b8355293518f8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -1704,7 +1704,9 @@ static int smu_smc_hw_setup(struct smu_context *smu) return ret; } - if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN4) + if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN5) + pcie_gen = 4; + else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN4) pcie_gen = 3; else if (adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3) pcie_gen = 2; @@ -1717,7 +1719,9 @@ static int smu_smc_hw_setup(struct smu_context *smu) * Bit 15:8: PCIE GEN, 0 to 3 corresponds to GEN1 to GEN4 * Bit 7:0: PCIE lane width, 1 to 7 corresponds is x1 to x32 */ - if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X16) + if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X32) + pcie_width = 7; + else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X16) pcie_width = 6; else if (adev->pm.pcie_mlw_mask & CAIL_PCIE_LINK_WIDTH_SUPPORT_X12) pcie_width = 5; diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h index 0546b02e198dd..29a4583db8734 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h @@ -53,7 +53,7 @@ #define CTF_OFFSET_MEM 5 extern const int decoded_link_speed[5]; -extern const int decoded_link_width[7]; +extern const int decoded_link_width[8]; #define DECODE_GEN_SPEED(gen_speed_idx) (decoded_link_speed[gen_speed_idx]) #define DECODE_LANE_WIDTH(lane_width_idx) (decoded_link_width[lane_width_idx]) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 4b36c230e43a0..12125303bb799 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -1344,8 +1344,12 @@ static int arcturus_get_power_limit(struct smu_context *smu, *default_power_limit = power_limit; if (max_power_limit) *max_power_limit = power_limit; + /** + * No lower bound is imposed on the limit. Any unreasonable limit set + * will result in frequent throttling. + */ if (min_power_limit) - *min_power_limit = power_limit; + *min_power_limit = 0; return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index fa30a9e1f27af..ab3c93ddce46f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -96,7 +96,6 @@ MODULE_FIRMWARE("amdgpu/smu_13_0_14.bin"); #define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK 0xE0 #define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT 0x5 #define LINK_SPEED_MAX 4 - #define SMU_13_0_6_DSCLK_THRESHOLD 140 #define MCA_BANK_IPID(_ip, _hwid, _type) \ @@ -370,7 +369,7 @@ static int smu_v13_0_6_tables_init(struct smu_context *smu) return -ENOMEM; smu_table->metrics_time = 0; - smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_6); + smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_7); smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL); if (!smu_table->gpu_metrics_table) { @@ -2321,8 +2320,8 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table { bool per_inst, smu_13_0_6_per_inst, smu_13_0_14_per_inst, apu_per_inst; struct smu_table_context *smu_table = &smu->smu_table; - struct gpu_metrics_v1_6 *gpu_metrics = - (struct gpu_metrics_v1_6 *)smu_table->gpu_metrics_table; + struct gpu_metrics_v1_7 *gpu_metrics = + (struct gpu_metrics_v1_7 *)smu_table->gpu_metrics_table; bool flag = smu_v13_0_6_is_unified_metrics(smu); int ret = 0, xcc_id, inst, i, j, k, idx; struct amdgpu_device *adev = smu->adev; @@ -2341,7 +2340,7 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table metrics_a = (MetricsTableA_t *)metrics_x; - smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 6); + smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 7); gpu_metrics->temperature_hotspot = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, flag)); @@ -2448,6 +2447,9 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table SMUQ10_ROUND(GET_METRIC_FIELD(XgmiReadDataSizeAcc, flag)[i]); gpu_metrics->xgmi_write_data_acc[i] = SMUQ10_ROUND(GET_METRIC_FIELD(XgmiWriteDataSizeAcc, flag)[i]); + ret = amdgpu_get_xgmi_link_status(adev, i); + if (ret >= 0) + gpu_metrics->xgmi_link_status[i] = ret; } gpu_metrics->num_partition = adev->xcp_mgr->num_xcps; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index c5d3e25cc967e..4fd0354bd312f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2147,6 +2147,8 @@ static ssize_t smu_v13_0_7_get_gpu_metrics(struct smu_context *smu, gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency; gpu_metrics->current_gfxclk = metrics->CurrClock[PPCLK_GFXCLK]; + gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK]; + gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK]; gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0]; gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0]; gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_1]; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c index ecb0164d533ee..a87040cb2f2e5 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -49,7 +49,7 @@ #define regMP1_SMN_IH_SW_INT_CTRL_mp1_14_0_0_BASE_IDX 0 const int decoded_link_speed[5] = {1, 2, 3, 4, 5}; -const int decoded_link_width[7] = {0, 1, 2, 4, 8, 12, 16}; +const int decoded_link_width[8] = {0, 1, 2, 4, 8, 12, 16, 32}; /* * DO NOT use these for err/warn/info/debug messages. * Use dev_err, dev_warn, dev_info and dev_dbg instead. diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 59b369eff30fb..687a0f5ac94f5 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -1173,13 +1173,15 @@ static int smu_v14_0_2_print_clk_levels(struct smu_context *smu, (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," : (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," : (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," : - (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "", + (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : + (pcie_table->pcie_gen[i] == 4) ? "32.0GT/s," : "", (pcie_table->pcie_lane[i] == 1) ? "x1" : (pcie_table->pcie_lane[i] == 2) ? "x2" : (pcie_table->pcie_lane[i] == 3) ? "x4" : (pcie_table->pcie_lane[i] == 4) ? "x8" : (pcie_table->pcie_lane[i] == 5) ? "x12" : - (pcie_table->pcie_lane[i] == 6) ? "x16" : "", + (pcie_table->pcie_lane[i] == 6) ? "x16" : + (pcie_table->pcie_lane[i] == 7) ? "x32" : "", pcie_table->clk_freq[i], (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) && (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ? @@ -1463,15 +1465,35 @@ static int smu_v14_0_2_update_pcie_parameters(struct smu_context *smu, struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context; struct smu_14_0_pcie_table *pcie_table = &dpm_context->dpm_tables.pcie_table; + int num_of_levels = pcie_table->num_of_link_levels; uint32_t smu_pcie_arg; int ret, i; - for (i = 0; i < pcie_table->num_of_link_levels; i++) { - if (pcie_table->pcie_gen[i] > pcie_gen_cap) + if (!num_of_levels) + return 0; + + if (!(smu->adev->pm.pp_feature & PP_PCIE_DPM_MASK)) { + if (pcie_table->pcie_gen[num_of_levels - 1] < pcie_gen_cap) + pcie_gen_cap = pcie_table->pcie_gen[num_of_levels - 1]; + + if (pcie_table->pcie_lane[num_of_levels - 1] < pcie_width_cap) + pcie_width_cap = pcie_table->pcie_lane[num_of_levels - 1]; + + /* Force all levels to use the same settings */ + for (i = 0; i < num_of_levels; i++) { pcie_table->pcie_gen[i] = pcie_gen_cap; - if (pcie_table->pcie_lane[i] > pcie_width_cap) pcie_table->pcie_lane[i] = pcie_width_cap; + } + } else { + for (i = 0; i < num_of_levels; i++) { + if (pcie_table->pcie_gen[i] > pcie_gen_cap) + pcie_table->pcie_gen[i] = pcie_gen_cap; + if (pcie_table->pcie_lane[i] > pcie_width_cap) + pcie_table->pcie_lane[i] = pcie_width_cap; + } + } + for (i = 0; i < num_of_levels; i++) { smu_pcie_arg = i << 16; smu_pcie_arg |= pcie_table->pcie_gen[i] << 8; smu_pcie_arg |= pcie_table->pcie_lane[i]; @@ -2753,7 +2775,6 @@ static const struct pptable_funcs smu_v14_0_2_ppt_funcs = { .get_unique_id = smu_v14_0_2_get_unique_id, .get_power_limit = smu_v14_0_2_get_power_limit, .set_power_limit = smu_v14_0_2_set_power_limit, - .set_power_source = smu_v14_0_set_power_source, .get_power_profile_mode = smu_v14_0_2_get_power_profile_mode, .set_power_profile_mode = smu_v14_0_2_set_power_profile_mode, .run_btc = smu_v14_0_run_btc, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index f1ab1a6bb4670..dbbd3759bff37 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -1081,6 +1081,9 @@ void smu_cmn_init_soft_gpu_metrics(void *table, uint8_t frev, uint8_t crev) case METRICS_VERSION(1, 6): structure_size = sizeof(struct gpu_metrics_v1_6); break; + case METRICS_VERSION(1, 7): + structure_size = sizeof(struct gpu_metrics_v1_7); + break; case METRICS_VERSION(2, 0): structure_size = sizeof(struct gpu_metrics_v2_0); break; diff --git a/drivers/gpu/drm/display/drm_dp_aux_bus.c b/drivers/gpu/drm/display/drm_dp_aux_bus.c index d810529ebfb6e..ec7eac6b595f7 100644 --- a/drivers/gpu/drm/display/drm_dp_aux_bus.c +++ b/drivers/gpu/drm/display/drm_dp_aux_bus.c @@ -292,7 +292,7 @@ int of_dp_aux_populate_bus(struct drm_dp_aux *aux, aux_ep->dev.parent = aux->dev; aux_ep->dev.bus = &dp_aux_bus_type; aux_ep->dev.type = &dp_aux_device_type_type; - aux_ep->dev.of_node = of_node_get(np); + device_set_node(&aux_ep->dev, of_fwnode_handle(of_node_get(np))); dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev)); ret = device_register(&aux_ep->dev); diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 47e6e8577b62e..b781601946db8 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -870,7 +870,7 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, INIT_LIST_HEAD(&fb->filp_head); fb->funcs = funcs; - strcpy(fb->comm, current->comm); + strscpy(fb->comm, current->comm); ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB, false, drm_framebuffer_free); diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_qr.rs index 1ef56cb07dfbd..09500cddc009b 100644 --- a/drivers/gpu/drm/drm_panic_qr.rs +++ b/drivers/gpu/drm/drm_panic_qr.rs @@ -209,12 +209,9 @@ const FORMAT_INFOS_QR_L: [u16; 8] = [ impl Version { /// Returns the smallest QR version than can hold these segments. fn from_segments(segments: &[&Segment<'_>]) -> Option { - for v in (1..=40).map(|k| Version(k)) { - if v.max_data() * 8 >= segments.iter().map(|s| s.total_size_bits(v)).sum() { - return Some(v); - } - } - None + (1..=40) + .map(Version) + .find(|&v| v.max_data() * 8 >= segments.iter().map(|s| s.total_size_bits(v)).sum()) } fn width(&self) -> u8 { @@ -242,7 +239,7 @@ impl Version { } fn alignment_pattern(&self) -> &'static [u8] { - &ALIGNMENT_PATTERNS[self.0 - 1] + ALIGNMENT_PATTERNS[self.0 - 1] } fn poly(&self) -> &'static [u8] { @@ -479,7 +476,7 @@ struct EncodedMsg<'a> { /// Data to be put in the QR code, with correct segment encoding, padding, and /// Error Code Correction. impl EncodedMsg<'_> { - fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> Option> { + fn new<'a>(segments: &[&Segment<'_>], data: &'a mut [u8]) -> Option> { let version = Version::from_segments(segments)?; let ec_size = version.ec_size(); let g1_blocks = version.g1_blocks(); @@ -492,7 +489,7 @@ impl EncodedMsg<'_> { data.fill(0); let mut em = EncodedMsg { - data: data, + data, ec_size, g1_blocks, g2_blocks, @@ -722,7 +719,10 @@ impl QrImage<'_> { fn is_finder(&self, x: u8, y: u8) -> bool { let end = self.width - 8; - (x < 8 && y < 8) || (x < 8 && y >= end) || (x >= end && y < 8) + #[expect(clippy::nonminimal_bool)] + { + (x < 8 && y < 8) || (x < 8 && y >= end) || (x >= end && y < 8) + } } // Alignment pattern: 5x5 squares in a grid. @@ -979,10 +979,11 @@ pub unsafe extern "C" fn drm_panic_qr_generate( /// * `url_len`: Length of the URL. /// /// * If `url_len` > 0, remove the 2 segments header/length and also count the -/// conversion to numeric segments. +/// conversion to numeric segments. /// * If `url_len` = 0, only removes 3 bytes for 1 binary segment. #[no_mangle] pub extern "C" fn drm_panic_qr_max_data_size(version: u8, url_len: usize) -> usize { + #[expect(clippy::manual_range_contains)] if version < 1 || version > 40 { return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index f6d42ec6949e7..f57e4dba2873c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1503,6 +1503,8 @@ static int hdcp2_deauthenticate_port(struct intel_connector *connector) static int hdcp2_authentication_key_exchange(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector); + struct intel_digital_port *dig_port = + intel_attached_dig_port(connector); struct intel_hdcp *hdcp = &connector->hdcp; union { struct hdcp2_ake_init ake_init; @@ -1513,30 +1515,36 @@ static int hdcp2_authentication_key_exchange(struct intel_connector *connector) } msgs; const struct intel_hdcp_shim *shim = hdcp->shim; size_t size; - int ret, i; + int ret, i, max_retries; /* Init for seq_num */ hdcp->seq_num_v = 0; hdcp->seq_num_m = 0; + if (intel_encoder_is_dp(&dig_port->base) || + intel_encoder_is_mst(&dig_port->base)) + max_retries = 10; + else + max_retries = 1; + ret = hdcp2_prepare_ake_init(connector, &msgs.ake_init); if (ret < 0) return ret; /* * Retry the first read and write to downstream at least 10 times - * with a 50ms delay if not hdcp2 capable(dock decides to stop advertising - * hdcp2 capability for some reason). The reason being that - * during suspend resume dock usually keeps the HDCP2 registers inaccesible - * causing AUX error. This wouldn't be a big problem if the userspace - * just kept retrying with some delay while it continues to play low - * value content but most userpace applications end up throwing an error - * when it receives one from KMD. This makes sure we give the dock - * and the sink devices to complete its power cycle and then try HDCP - * authentication. The values of 10 and delay of 50ms was decided based - * on multiple trial and errors. + * with a 50ms delay if not hdcp2 capable for DP/DPMST encoders + * (dock decides to stop advertising hdcp2 capability for some reason). + * The reason being that during suspend resume dock usually keeps the + * HDCP2 registers inaccesible causing AUX error. This wouldn't be a + * big problem if the userspace just kept retrying with some delay while + * it continues to play low value content but most userpace applications + * end up throwing an error when it receives one from KMD. This makes + * sure we give the dock and the sink devices to complete its power cycle + * and then try HDCP authentication. The values of 10 and delay of 50ms + * was decided based on multiple trial and errors. */ - for (i = 0; i < 10; i++) { + for (i = 0; i < max_retries; i++) { if (!intel_hdcp2_get_capability(connector)) { msleep(50); continue; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 135ded17334e5..4eb58887819a1 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -1104,7 +1104,7 @@ i915_vma_coredump_create(const struct intel_gt *gt, } INIT_LIST_HEAD(&dst->page_list); - strcpy(dst->name, name); + strscpy(dst->name, name); dst->next = NULL; dst->gtt_offset = vma_res->start; @@ -1404,7 +1404,7 @@ static bool record_context(struct i915_gem_context_coredump *e, rcu_read_lock(); task = pid_task(ctx->pid, PIDTYPE_PID); if (task) { - strcpy(e->comm, task->comm); + strscpy(e->comm, task->comm); e->pid = task->pid; } rcu_read_unlock(); @@ -1450,7 +1450,7 @@ capture_vma_snapshot(struct intel_engine_capture_vma *next, return next; } - strcpy(c->name, name); + strscpy(c->name, name); c->vma_res = i915_vma_resource_get(vma_res); c->next = next; diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c index 47aa06a9a9422..5b69cc8011b42 100644 --- a/drivers/gpu/drm/radeon/radeon_audio.c +++ b/drivers/gpu/drm/radeon/radeon_audio.c @@ -760,16 +760,20 @@ static int radeon_audio_component_get_eld(struct device *kdev, int port, if (!rdev->audio.enabled || !rdev->mode_info.mode_config_initialized) return 0; - list_for_each_entry(encoder, &rdev_to_drm(rdev)->mode_config.encoder_list, head) { + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + const struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + encoder = connector_funcs->best_encoder(connector); + + if (!encoder) + continue; + if (!radeon_encoder_is_digital(encoder)) continue; radeon_encoder = to_radeon_encoder(encoder); dig = radeon_encoder->enc_priv; if (!dig->pin || dig->pin->id != port) continue; - connector = radeon_get_connector_for_encoder(encoder); - if (!connector) - continue; *enabled = true; ret = drm_eld_size(connector->eld); memcpy(buf, connector->eld, min(max_bytes, ret)); diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index f9c73c55f04f7..f9996304d9431 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -1255,16 +1255,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) goto exit; } } - - if (dret && radeon_connector->hpd.hpd != RADEON_HPD_NONE && - !radeon_hpd_sense(rdev, radeon_connector->hpd.hpd) && - connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) { - DRM_DEBUG_KMS("EDID is readable when HPD disconnected\n"); - schedule_delayed_work(&rdev->hotplug_work, msecs_to_jiffies(1000)); - ret = connector_status_disconnected; - goto exit; - } - if (dret) { radeon_connector->detected_by_load = false; radeon_connector_free_edid(connector); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 23d6d1a2586d1..5e958cc223f44 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -248,10 +248,9 @@ int radeon_cik_support = 1; MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled (default), 0 = disabled)"); module_param_named(cik_support, radeon_cik_support, int, 0444); -static struct pci_device_id pciidlist[] = { +static const struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; - MODULE_DEVICE_TABLE(pci, pciidlist); static const struct drm_driver kms_driver; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 69d0c12fa419f..616d25c8c2de7 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -219,8 +219,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, bool evict, if (old_mem->mem_type == TTM_PL_TT && new_mem->mem_type == TTM_PL_SYSTEM) { radeon_ttm_tt_unbind(bo->bdev, bo->ttm); - ttm_resource_free(bo, &bo->resource); - ttm_bo_assign_mem(bo, new_mem); + ttm_bo_move_null(bo, new_mem); goto out; } if (rdev->ring[radeon_copy_ring_index(rdev)].ready && diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c index 9c796ee4c303a..c8b362cc2b95f 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -82,7 +82,7 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder) * comment in rk_hdptx_phy_power_on() from * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c */ - phy_set_bus_width(hdmi->phy, rate / 100); + phy_set_bus_width(hdmi->phy, div_u64(rate, 100)); } } diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c index d2679c5d976b0..0b0cd6aa1d9fb 100644 --- a/drivers/gpu/drm/xe/xe_devcoredump.c +++ b/drivers/gpu/drm/xe/xe_devcoredump.c @@ -23,6 +23,7 @@ #include "xe_guc_submit.h" #include "xe_hw_engine.h" #include "xe_module.h" +#include "xe_pm.h" #include "xe_sched_job.h" #include "xe_vm.h" @@ -158,8 +159,11 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work) { struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work); struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot); + struct xe_device *xe = coredump_to_xe(coredump); unsigned int fw_ref; + xe_pm_runtime_get(xe); + /* keep going if fw fails as we still want to save the memory and SW data */ fw_ref = xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL); if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) @@ -168,6 +172,8 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work) xe_guc_exec_queue_snapshot_capture_delayed(ss->ge); xe_force_wake_put(gt_to_fw(ss->gt), fw_ref); + xe_pm_runtime_put(xe); + /* Calculate devcoredump size */ ss->read.size = __xe_devcoredump_read(NULL, INT_MAX, coredump); diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 0e2dd691bdae9..06d6db8b50f93 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -350,7 +350,8 @@ struct xe_device *xe_device_create(struct pci_dev *pdev, INIT_LIST_HEAD(&xe->pinned.external_vram); INIT_LIST_HEAD(&xe->pinned.evicted); - xe->preempt_fence_wq = alloc_ordered_workqueue("xe-preempt-fence-wq", 0); + xe->preempt_fence_wq = alloc_ordered_workqueue("xe-preempt-fence-wq", + WQ_MEM_RECLAIM); xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0); xe->unordered_wq = alloc_workqueue("xe-unordered-wq", 0, 0); xe->destroy_wq = alloc_workqueue("xe-destroy-wq", 0, 0); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 9a8564ea06b59..6f4a9812b4f4a 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -767,17 +767,19 @@ static void disable_scheduling_deregister(struct xe_guc *guc, struct xe_exec_queue *q) { MAKE_SCHED_CONTEXT_ACTION(q, DISABLE); - struct xe_device *xe = guc_to_xe(guc); int ret; set_min_preemption_timeout(guc, q); smp_rmb(); - ret = wait_event_timeout(guc->ct.wq, !exec_queue_pending_enable(q) || - xe_guc_read_stopped(guc), HZ * 5); + ret = wait_event_timeout(guc->ct.wq, + (!exec_queue_pending_enable(q) && + !exec_queue_pending_disable(q)) || + xe_guc_read_stopped(guc), + HZ * 5); if (!ret) { struct xe_gpu_scheduler *sched = &q->guc->sched; - drm_warn(&xe->drm, "Pending enable failed to respond"); + xe_gt_warn(q->gt, "Pending enable/disable failed to respond\n"); xe_sched_submission_start(sched); xe_gt_reset_async(q->gt); xe_sched_tdr_queue_imm(sched); @@ -1099,7 +1101,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) * modifying state */ ret = wait_event_timeout(guc->ct.wq, - !exec_queue_pending_enable(q) || + (!exec_queue_pending_enable(q) && + !exec_queue_pending_disable(q)) || xe_guc_read_stopped(guc), HZ * 5); if (!ret || xe_guc_read_stopped(guc)) goto trigger_reset; @@ -1328,8 +1331,8 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) && exec_queue_enabled(q)) { - wait_event(guc->ct.wq, q->guc->resume_time != RESUME_PENDING || - xe_guc_read_stopped(guc)); + wait_event(guc->ct.wq, (q->guc->resume_time != RESUME_PENDING || + xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q)); if (!xe_guc_read_stopped(guc)) { s64 since_resume_ms = @@ -1866,16 +1869,29 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, xe_gt_assert(guc_to_gt(guc), runnable_state == 0); xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q)); - clear_exec_queue_pending_disable(q); if (q->guc->suspend_pending) { suspend_fence_signal(q); + clear_exec_queue_pending_disable(q); } else { if (exec_queue_banned(q) || check_timeout) { smp_wmb(); wake_up_all(&guc->ct.wq); } - if (!check_timeout) + if (!check_timeout && exec_queue_destroyed(q)) { + /* + * Make sure to clear the pending_disable only + * after sampling the destroyed state. We want + * to ensure we don't trigger the unregister too + * early with something intending to only + * disable scheduling. The caller doing the + * destroy must wait for an ongoing + * pending_disable before marking as destroyed. + */ + clear_exec_queue_pending_disable(q); deregister_exec_queue(guc, q); + } else { + clear_exec_queue_pending_disable(q); + } } } } diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index cfd31ae49cc1f..1b97d90aaddaf 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -209,7 +209,8 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m, num_entries * XE_PAGE_SIZE, ttm_bo_type_kernel, XE_BO_FLAG_VRAM_IF_DGFX(tile) | - XE_BO_FLAG_PINNED); + XE_BO_FLAG_PINNED | + XE_BO_FLAG_PAGETABLE); if (IS_ERR(bo)) return PTR_ERR(bo); @@ -1350,6 +1351,7 @@ __xe_migrate_update_pgtables(struct xe_migrate *m, /* For sysmem PTE's, need to map them in our hole.. */ if (!IS_DGFX(xe)) { + u16 pat_index = xe->pat.idx[XE_CACHE_WB]; u32 ptes, ofs; ppgtt_ofs = NUM_KERNEL_PDE - 1; @@ -1409,7 +1411,7 @@ __xe_migrate_update_pgtables(struct xe_migrate *m, pt_bo->update_index = current_update; addr = vm->pt_ops->pte_encode_bo(pt_bo, 0, - XE_CACHE_WB, 0); + pat_index, 0); bb->cs[bb->len++] = lower_32_bits(addr); bb->cs[bb->len++] = upper_32_bits(addr); } diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index e6640283893f0..6b7f77425c7f9 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -174,7 +174,7 @@ static const struct xe_graphics_desc graphics_xelpg = { GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0) static const struct xe_graphics_desc graphics_xe2 = { - .name = "Xe2_LPG / Xe2_HPG", + .name = "Xe2_LPG / Xe2_HPG / Xe3_LPG", XE2_GFX_FEATURES, }; diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c index a90480c6aecf5..42f5bebd09e50 100644 --- a/drivers/gpu/drm/xe/xe_sync.c +++ b/drivers/gpu/drm/xe/xe_sync.c @@ -87,8 +87,12 @@ static void user_fence_worker(struct work_struct *w) drm_dbg(&ufence->xe->drm, "mmget_not_zero() failed, ufence wasn't signaled\n"); } - wake_up_all(&ufence->xe->ufence_wq); + /* + * Wake up waiters only after updating the ufence state, allowing the UMD + * to safely reuse the same ufence without encountering -EBUSY errors. + */ WRITE_ONCE(ufence->signalled, 1); + wake_up_all(&ufence->xe->ufence_wq); user_fence_put(ufence); } diff --git a/drivers/greybus/interface.c b/drivers/greybus/interface.c index d022bfb5e95d7..a0f3e94227215 100644 --- a/drivers/greybus/interface.c +++ b/drivers/greybus/interface.c @@ -780,7 +780,7 @@ const struct device_type greybus_interface_type = { * The position of interface within the Endo is encoded in "interface_id" * argument. * - * Returns a pointer to the new interfce or a null pointer if a + * Returns a pointer to the new interface or a null pointer if a * failure occurs due to memory exhaustion. */ struct gb_interface *gb_interface_create(struct gb_module *module, diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 0e611a9d79d75..702c22fae136a 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -79,7 +79,6 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, WRITE_RANGE(hid_device, name, true), WRITE_RANGE(hid_device, uniq, true), WRITE_RANGE(hid_device, phys, true), - WRITE_RANGE(hid_device, quirks, false), }; #undef WRITE_RANGE const struct btf_type *state = NULL; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 81d6c734c8bcb..98bef39642a9e 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2692,12 +2692,6 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) int ret; if (!hdev->bpf_rsize) { - unsigned int quirks; - - /* reset the quirks that has been previously set */ - quirks = hid_lookup_quirk(hdev); - hdev->quirks = quirks; - /* in case a bpf program gets detached, we need to free the old one */ hid_free_bpf_rdesc(hdev); @@ -2707,9 +2701,6 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, &hdev->bpf_rsize); - if (quirks ^ hdev->quirks) - hid_info(hdev, "HID-BPF toggled quirks on the device: %04x", - quirks ^ hdev->quirks); } if (!hid_check_device_match(hdev, hdrv, &id)) @@ -2719,6 +2710,8 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) if (!hdev->devres_group_id) return -ENOMEM; + /* reset the quirks that has been previously set */ + hdev->quirks = hid_lookup_quirk(hdev); hdev->driver = hdrv; if (hdrv->probe) { diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 34cac27e4ddec..0dcb8a3a691d6 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -597,7 +597,7 @@ struct aqc_data { /* Sensor values */ s32 temp_input[20]; /* Max 4 physical and 16 virtual or 8 physical and 12 virtual */ - s32 speed_input[8]; + s32 speed_input[9]; u32 speed_input_min[1]; u32 speed_input_target[1]; u32 speed_input_max[1]; diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index 1f36af2cd2d94..fbe6730091261 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -452,12 +452,7 @@ static int p3t1085_i3c_probe(struct i3c_device *i3cdev) struct device *dev = i3cdev_to_dev(i3cdev); struct regmap *regmap; -#ifdef CONFIG_REGMAP_I3C regmap = devm_regmap_init_i3c(i3cdev, &tmp108_regmap_config); -#else - regmap = ERR_PTR(-ENODEV); -#endif - if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register i3c regmap\n"); diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c index dfcfb09d9f3cd..80fb03f30c302 100644 --- a/drivers/hwmon/tps23861.c +++ b/drivers/hwmon/tps23861.c @@ -132,7 +132,7 @@ static int tps23861_read_temp(struct tps23861_data *data, long *val) if (err < 0) return err; - *val = (regval * TEMPERATURE_LSB) - 20000; + *val = ((long)regval * TEMPERATURE_LSB) - 20000; return 0; } diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 0d7b9839e5b66..e9d8d28e055f3 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -23,7 +23,6 @@ enum { TH_PCI_RTIT_BAR = 4, }; -#define BAR_MASK (BIT(TH_PCI_CONFIG_BAR) | BIT(TH_PCI_STH_SW_BAR)) #define PCI_REG_NPKDSC 0x80 #define NPKDSC_TSACT BIT(5) @@ -83,10 +82,16 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (err) return err; - err = pcim_iomap_regions_request_all(pdev, BAR_MASK, DRIVER_NAME); + err = pcim_request_all_regions(pdev, DRIVER_NAME); if (err) return err; + if (!pcim_iomap(pdev, TH_PCI_CONFIG_BAR, 0)) + return -ENOMEM; + + if (!pcim_iomap(pdev, TH_PCI_STH_SW_BAR, 0)) + return -ENOMEM; + if (pdev->resource[TH_PCI_RTIT_BAR].start) { resource[TH_MMIO_RTIT] = pdev->resource[TH_PCI_RTIT_BAR]; r++; diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index af95d6e7f0043..d27de18de46fe 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -9,6 +9,7 @@ i2c-core-y := i2c-core-base.o i2c-core-smbus.o i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o i2c-core-$(CONFIG_OF) += i2c-core-of.o +i2c-core-$(CONFIG_OF_DYNAMIC) += i2c-core-of-prober.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 443f946acf361..857783d458fbd 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -382,6 +382,11 @@ int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev) i2c_parse_fw_timings(device, t, false); + if (device_property_read_u32(device, "snps,bus-capacitance-pf", &dev->bus_capacitance_pF)) + dev->bus_capacitance_pF = 100; + + dev->clk_freq_optimized = device_property_read_bool(device, "snps,clk-freq-optimized"); + i2c_dw_adjust_bus_speed(dev); if (is_of_node(fwnode)) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 520f9699e3bd2..347843b4f5dd7 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -242,6 +242,10 @@ struct reset_control; * @set_sda_hold_time: callback to retrieve IP specific SDA hold timing * @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE * @rinfo: I²C GPIO recovery information + * @bus_capacitance_pF: bus capacitance in picofarads + * @clk_freq_optimized: if this value is true, it means the hardware reduces + * its internal clock frequency by reducing the internal latency required + * to generate the high period and low period of SCL line. * * HCNT and LCNT parameters can be used if the platform knows more accurate * values than the one computed based only on the input clock frequency. @@ -299,6 +303,8 @@ struct dw_i2c_dev { int (*set_sda_hold_time)(struct dw_i2c_dev *dev); int mode; struct i2c_bus_recovery_info rinfo; + u32 bus_capacitance_pF; + bool clk_freq_optimized; }; #define ACCESS_INTR_MASK BIT(0) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 528b969253a7f..eca8998d640fb 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -151,19 +151,38 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) dev->hs_hcnt = 0; dev->hs_lcnt = 0; } else if (!dev->hs_hcnt || !dev->hs_lcnt) { + u32 t_high, t_low; + + /* + * The legal values stated in the databook for bus + * capacitance are only 100pF and 400pF. + * If dev->bus_capacitance_pF is greater than or equals + * to 400, t_high and t_low are assumed to be + * appropriate values for 400pF, otherwise 100pF. + */ + if (dev->bus_capacitance_pF >= 400) { + /* assume bus capacitance is 400pF */ + t_high = dev->clk_freq_optimized ? 160 : 120; + t_low = 320; + } else { + /* assume bus capacitance is 100pF */ + t_high = 60; + t_low = dev->clk_freq_optimized ? 120 : 160; + } + ic_clk = i2c_dw_clk_rate(dev); dev->hs_hcnt = i2c_dw_scl_hcnt(dev, DW_IC_HS_SCL_HCNT, ic_clk, - 160, /* tHIGH = 160 ns */ + t_high, sda_falling_time, 0); /* No offset */ dev->hs_lcnt = i2c_dw_scl_lcnt(dev, DW_IC_HS_SCL_LCNT, ic_clk, - 320, /* tLOW = 320 ns */ + t_low, scl_falling_time, 0); /* No offset */ } diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 1643cad6489c9..3e12aab6bf2da 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -349,6 +349,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, { "AMDI0510", 0 }, { "APMC0D0F", 0 }, + { "FUJI200B", 0 }, { "HISI02A1", 0 }, { "HISI02A2", 0 }, { "HISI02A3", 0 }, diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index ad0f02acdb121..efb33802804fd 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -6,10 +6,10 @@ * I2C master mode controller driver, used in Nomadik 8815 * and Ux500 platforms. * - * The Mobileye EyeQ5 platform is also supported; it uses + * The Mobileye EyeQ5 and EyeQ6H platforms are also supported; they use * the same Ux500/DB8500 IP block with two quirks: * - The memory bus only supports 32-bit accesses. - * - A register must be configured for the I2C speed mode; + * - (only EyeQ5) A register must be configured for the I2C speed mode; * it is located in a shared register region called OLB. * * Author: Srinidhi Kasagar @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -396,7 +397,7 @@ static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *priv, u16 flags) */ static void setup_i2c_controller(struct nmk_i2c_dev *priv) { - u32 brcr1, brcr2; + u32 brcr; u32 i2c_clk, div; u32 ns; u16 slsu; @@ -443,7 +444,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *priv) /* * The spec says, in case of std. mode the divider is * 2 whereas it is 3 for fast and fastplus mode of - * operation. TODO - high speed support. + * operation. */ div = (priv->clk_freq > I2C_MAX_STANDARD_MODE_FREQ) ? 3 : 2; @@ -451,30 +452,23 @@ static void setup_i2c_controller(struct nmk_i2c_dev *priv) * generate the mask for baud rate counters. The controller * has two baud rate counters. One is used for High speed * operation, and the other is for std, fast mode, fast mode - * plus operation. Currently we do not supprt high speed mode - * so set brcr1 to 0. + * plus operation. + * + * BRCR is a clock divider amount. Pick highest value that + * leads to rate strictly below target. Eg when asking for + * 400kHz you want a bus rate <=400kHz (and not >=400kHz). */ - brcr1 = FIELD_PREP(I2C_BRCR_BRCNT1, 0); - brcr2 = FIELD_PREP(I2C_BRCR_BRCNT2, i2c_clk / (priv->clk_freq * div)); + brcr = DIV_ROUND_UP(i2c_clk, priv->clk_freq * div); + + if (priv->sm == I2C_FREQ_MODE_HIGH_SPEED) + brcr = FIELD_PREP(I2C_BRCR_BRCNT1, brcr); + else + brcr = FIELD_PREP(I2C_BRCR_BRCNT2, brcr); /* set the baud rate counter register */ - writel((brcr1 | brcr2), priv->virtbase + I2C_BRCR); + writel(brcr, priv->virtbase + I2C_BRCR); - /* - * set the speed mode. Currently we support - * only standard and fast mode of operation - * TODO - support for fast mode plus (up to 1Mb/s) - * and high speed (up to 3.4 Mb/s) - */ - if (priv->sm > I2C_FREQ_MODE_FAST) { - dev_err(&priv->adev->dev, - "do not support this mode defaulting to std. mode\n"); - brcr2 = FIELD_PREP(I2C_BRCR_BRCNT2, - i2c_clk / (I2C_MAX_STANDARD_MODE_FREQ * 2)); - writel((brcr1 | brcr2), priv->virtbase + I2C_BRCR); - writel(FIELD_PREP(I2C_CR_SM, I2C_FREQ_MODE_STANDARD), - priv->virtbase + I2C_CR); - } + /* set the speed mode */ writel(FIELD_PREP(I2C_CR_SM, priv->sm), priv->virtbase + I2C_CR); /* set the Tx and Rx FIFO threshold */ @@ -1015,11 +1009,14 @@ static void nmk_i2c_of_probe(struct device_node *np, if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq)) priv->clk_freq = I2C_MAX_STANDARD_MODE_FREQ; - /* This driver only supports 'standard' and 'fast' modes of operation. */ if (priv->clk_freq <= I2C_MAX_STANDARD_MODE_FREQ) priv->sm = I2C_FREQ_MODE_STANDARD; - else + else if (priv->clk_freq <= I2C_MAX_FAST_MODE_FREQ) priv->sm = I2C_FREQ_MODE_FAST; + else if (priv->clk_freq <= I2C_MAX_FAST_MODE_PLUS_FREQ) + priv->sm = I2C_FREQ_MODE_FAST_PLUS; + else + priv->sm = I2C_FREQ_MODE_HIGH_SPEED; priv->tft = 1; /* Tx FIFO threshold */ priv->rft = 8; /* Rx FIFO threshold */ @@ -1046,8 +1043,6 @@ static int nmk_i2c_eyeq5_probe(struct nmk_i2c_dev *priv) struct regmap *olb; unsigned int id; - priv->has_32b_bus = true; - olb = syscon_regmap_lookup_by_phandle_args(np, "mobileye,olb", 1, &id); if (IS_ERR(olb)) return PTR_ERR(olb); @@ -1068,15 +1063,39 @@ static int nmk_i2c_eyeq5_probe(struct nmk_i2c_dev *priv) return 0; } +#define NMK_I2C_EYEQ_FLAG_32B_BUS BIT(0) +#define NMK_I2C_EYEQ_FLAG_IS_EYEQ5 BIT(1) + +static const struct of_device_id nmk_i2c_eyeq_match_table[] = { + { + .compatible = "mobileye,eyeq5-i2c", + .data = (void *)(NMK_I2C_EYEQ_FLAG_32B_BUS | NMK_I2C_EYEQ_FLAG_IS_EYEQ5), + }, + { + .compatible = "mobileye,eyeq6h-i2c", + .data = (void *)NMK_I2C_EYEQ_FLAG_32B_BUS, + }, +}; + static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) { - int ret = 0; - struct nmk_i2c_dev *priv; + struct i2c_vendor_data *vendor = id->data; + u32 max_fifo_threshold = (vendor->fifodepth / 2) - 1; struct device_node *np = adev->dev.of_node; + const struct of_device_id *match; struct device *dev = &adev->dev; + unsigned long match_flags = 0; + struct nmk_i2c_dev *priv; struct i2c_adapter *adap; - struct i2c_vendor_data *vendor = id->data; - u32 max_fifo_threshold = (vendor->fifodepth / 2) - 1; + int ret = 0; + + /* + * We do not want to attach a .of_match_table to our amba driver. + * Do not convert to device_get_match_data(). + */ + match = of_match_device(nmk_i2c_eyeq_match_table, dev); + if (match) + match_flags = (unsigned long)match->data; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -1084,10 +1103,10 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) priv->vendor = vendor; priv->adev = adev; - priv->has_32b_bus = false; + priv->has_32b_bus = match_flags & NMK_I2C_EYEQ_FLAG_32B_BUS; nmk_i2c_of_probe(np, priv); - if (of_device_is_compatible(np, "mobileye,eyeq5-i2c")) { + if (match_flags & NMK_I2C_EYEQ_FLAG_IS_EYEQ5) { ret = nmk_i2c_eyeq5_probe(priv); if (ret) return dev_err_probe(dev, ret, "failed OLB lookup\n"); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 86ec391616b06..da20b4487c9a5 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -17,9 +17,9 @@ #include #include #include -#include #include #include +#include #include /* QUP Registers */ @@ -1683,7 +1683,7 @@ static int qup_i2c_probe(struct platform_device *pdev) } } - if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) { + if (device_is_compatible(&pdev->dev, "qcom,i2c-qup-v1.1.1")) { qup->adap.algo = &qup_i2c_algo; qup->adap.quirks = &qup_i2c_quirks; is_qup_v1 = true; diff --git a/drivers/i2c/i2c-core-of-prober.c b/drivers/i2c/i2c-core-of-prober.c new file mode 100644 index 0000000000000..b9ca785f8b178 --- /dev/null +++ b/drivers/i2c/i2c-core-of-prober.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core OF component prober code + * + * Copyright (C) 2024 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Some devices, such as Google Hana Chromebooks, are produced by multiple + * vendors each using their preferred components. Such components are all + * in the device tree. Instead of having all of them enabled and having each + * driver separately try and probe its device while fighting over shared + * resources, they can be marked as "fail-needs-probe" and have a prober + * figure out which one is actually used beforehand. + * + * This prober assumes such drop-in parts are on the same I2C bus, have + * non-conflicting addresses, and can be directly probed by seeing which + * address responds. + * + * TODO: + * - Support I2C muxes + */ + +static struct device_node *i2c_of_probe_get_i2c_node(struct device *dev, const char *type) +{ + struct device_node *node __free(device_node) = of_find_node_by_name(NULL, type); + if (!node) { + dev_err(dev, "Could not find %s device node\n", type); + return NULL; + } + + struct device_node *i2c_node __free(device_node) = of_get_parent(node); + if (!of_node_name_eq(i2c_node, "i2c")) { + dev_err(dev, "%s device isn't on I2C bus\n", type); + return NULL; + } + + if (!of_device_is_available(i2c_node)) { + dev_err(dev, "I2C controller not available\n"); + return NULL; + } + + return no_free_ptr(i2c_node); +} + +static int i2c_of_probe_enable_node(struct device *dev, struct device_node *node) +{ + int ret; + + dev_dbg(dev, "Enabling %pOF\n", node); + + struct of_changeset *ocs __free(kfree) = kzalloc(sizeof(*ocs), GFP_KERNEL); + if (!ocs) + return -ENOMEM; + + of_changeset_init(ocs); + ret = of_changeset_update_prop_string(ocs, node, "status", "okay"); + if (ret) + return ret; + + ret = of_changeset_apply(ocs); + if (ret) { + /* ocs needs to be explicitly cleaned up before being freed. */ + of_changeset_destroy(ocs); + } else { + /* + * ocs is intentionally kept around as it needs to + * exist as long as the change is applied. + */ + void *ptr __always_unused = no_free_ptr(ocs); + } + + return ret; +} + +static const struct i2c_of_probe_ops i2c_of_probe_dummy_ops; + +/** + * i2c_of_probe_component() - probe for devices of "type" on the same i2c bus + * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages. + * @cfg: Pointer to the &struct i2c_of_probe_cfg containing callbacks and other options + * for the prober. + * @ctx: Context data for callbacks. + * + * Probe for possible I2C components of the same "type" (&i2c_of_probe_cfg->type) + * on the same I2C bus that have their status marked as "fail-needs-probe". + * + * Assumes that across the entire device tree the only instances of nodes + * with "type" prefixed node names (not including the address portion) are + * the ones that need handling for second source components. In other words, + * if "type" is "touchscreen", then all device nodes named "touchscreen*" + * are the ones that need probing. There cannot be another "touchscreen*" + * node that is already enabled. + * + * Assumes that for each "type" of component, only one actually exists. In + * other words, only one matching and existing device will be enabled. + * + * Context: Process context only. Does non-atomic I2C transfers. + * Should only be used from a driver probe function, as the function + * can return -EPROBE_DEFER if the I2C adapter or other resources + * are unavailable. + * Return: 0 on success or no-op, error code otherwise. + * A no-op can happen when it seems like the device tree already + * has components of the type to be probed already enabled. This + * can happen when the device tree had not been updated to mark + * the status of the to-be-probed components as "fail-needs-probe". + * Or this function was already run with the same parameters and + * succeeded in enabling a component. The latter could happen if + * the user had multiple types of components to probe, and one of + * them down the list caused a deferred probe. This is expected + * behavior. + */ +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx) +{ + const struct i2c_of_probe_ops *ops; + const char *type; + struct i2c_adapter *i2c; + int ret; + + ops = cfg->ops ?: &i2c_of_probe_dummy_ops; + type = cfg->type; + + struct device_node *i2c_node __free(device_node) = i2c_of_probe_get_i2c_node(dev, type); + if (!i2c_node) + return -ENODEV; + + /* + * If any devices of the given "type" are already enabled then this function is a no-op. + * Either the device tree hasn't been modified to work with this probe function, or the + * function had already run before and enabled some component. + */ + for_each_child_of_node_with_prefix(i2c_node, node, type) + if (of_device_is_available(node)) + return 0; + + i2c = of_get_i2c_adapter_by_node(i2c_node); + if (!i2c) + return dev_err_probe(dev, -EPROBE_DEFER, "Couldn't get I2C adapter\n"); + + /* Grab and enable resources */ + ret = 0; + if (ops->enable) + ret = ops->enable(dev, i2c_node, ctx); + if (ret) + goto out_put_i2c_adapter; + + for_each_child_of_node_with_prefix(i2c_node, node, type) { + union i2c_smbus_data data; + u32 addr; + + if (of_property_read_u32(node, "reg", &addr)) + continue; + if (i2c_smbus_xfer(i2c, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data) < 0) + continue; + + /* Found a device that is responding */ + if (ops->cleanup_early) + ops->cleanup_early(dev, ctx); + ret = i2c_of_probe_enable_node(dev, node); + break; + } + + if (ops->cleanup) + ops->cleanup(dev, ctx); +out_put_i2c_adapter: + i2c_put_adapter(i2c); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(i2c_of_probe_component, I2C_OF_PROBER); + +static int i2c_of_probe_simple_get_supply(struct device *dev, struct device_node *node, + struct i2c_of_probe_simple_ctx *ctx) +{ + const char *supply_name; + struct regulator *supply; + + /* + * It's entirely possible for the component's device node to not have the + * regulator supplies. While it does not make sense from a hardware perspective, + * the supplies could be always on or otherwise not modeled in the device tree, + * but the device would still work. + */ + supply_name = ctx->opts->supply_name; + if (!supply_name) + return 0; + + supply = of_regulator_get_optional(dev, node, supply_name); + if (IS_ERR(supply)) { + return dev_err_probe(dev, PTR_ERR(supply), + "Failed to get regulator supply \"%s\" from %pOF\n", + supply_name, node); + } + + ctx->supply = supply; + + return 0; +} + +static void i2c_of_probe_simple_put_supply(struct i2c_of_probe_simple_ctx *ctx) +{ + regulator_put(ctx->supply); + ctx->supply = NULL; +} + +static int i2c_of_probe_simple_enable_regulator(struct device *dev, struct i2c_of_probe_simple_ctx *ctx) +{ + int ret; + + if (!ctx->supply) + return 0; + + dev_dbg(dev, "Enabling regulator supply \"%s\"\n", ctx->opts->supply_name); + + ret = regulator_enable(ctx->supply); + if (ret) + return ret; + + if (ctx->opts->post_power_on_delay_ms) + msleep(ctx->opts->post_power_on_delay_ms); + + return 0; +} + +static void i2c_of_probe_simple_disable_regulator(struct device *dev, struct i2c_of_probe_simple_ctx *ctx) +{ + if (!ctx->supply) + return; + + dev_dbg(dev, "Disabling regulator supply \"%s\"\n", ctx->opts->supply_name); + + regulator_disable(ctx->supply); +} + +static int i2c_of_probe_simple_get_gpiod(struct device *dev, struct device_node *node, + struct i2c_of_probe_simple_ctx *ctx) +{ + struct fwnode_handle *fwnode = of_fwnode_handle(node); + struct gpio_desc *gpiod; + const char *con_id; + + /* NULL signals no GPIO needed */ + if (!ctx->opts->gpio_name) + return 0; + + /* An empty string signals an unnamed GPIO */ + if (!ctx->opts->gpio_name[0]) + con_id = NULL; + else + con_id = ctx->opts->gpio_name; + + gpiod = fwnode_gpiod_get_index(fwnode, con_id, 0, GPIOD_ASIS, "i2c-of-prober"); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + + ctx->gpiod = gpiod; + + return 0; +} + +static void i2c_of_probe_simple_put_gpiod(struct i2c_of_probe_simple_ctx *ctx) +{ + gpiod_put(ctx->gpiod); + ctx->gpiod = NULL; +} + +static int i2c_of_probe_simple_set_gpio(struct device *dev, struct i2c_of_probe_simple_ctx *ctx) +{ + int ret; + + if (!ctx->gpiod) + return 0; + + dev_dbg(dev, "Configuring GPIO\n"); + + ret = gpiod_direction_output(ctx->gpiod, ctx->opts->gpio_assert_to_enable); + if (ret) + return ret; + + if (ctx->opts->post_gpio_config_delay_ms) + msleep(ctx->opts->post_gpio_config_delay_ms); + + return 0; +} + +static void i2c_of_probe_simple_disable_gpio(struct device *dev, struct i2c_of_probe_simple_ctx *ctx) +{ + gpiod_set_value(ctx->gpiod, !ctx->opts->gpio_assert_to_enable); +} + +/** + * i2c_of_probe_simple_enable - Simple helper for I2C OF prober to get and enable resources + * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages + * @bus_node: Pointer to the &struct device_node of the I2C adapter. + * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context. + * + * If &i2c_of_probe_simple_opts->supply_name is given, request the named regulator supply. + * If &i2c_of_probe_simple_opts->gpio_name is given, request the named GPIO. Or if it is + * the empty string, request the unnamed GPIO. + * If a regulator supply was found, enable that regulator. + * If a GPIO line was found, configure the GPIO line to output and set value + * according to given options. + * + * Return: %0 on success or no-op, or a negative error number on failure. + */ +int i2c_of_probe_simple_enable(struct device *dev, struct device_node *bus_node, void *data) +{ + struct i2c_of_probe_simple_ctx *ctx = data; + struct device_node *node; + const char *compat; + int ret; + + dev_dbg(dev, "Requesting resources for components under I2C bus %pOF\n", bus_node); + + if (!ctx || !ctx->opts) + return -EINVAL; + + compat = ctx->opts->res_node_compatible; + if (!compat) + return -EINVAL; + + node = of_get_compatible_child(bus_node, compat); + if (!node) + return dev_err_probe(dev, -ENODEV, "No device compatible with \"%s\" found\n", + compat); + + ret = i2c_of_probe_simple_get_supply(dev, node, ctx); + if (ret) + goto out_put_node; + + ret = i2c_of_probe_simple_get_gpiod(dev, node, ctx); + if (ret) + goto out_put_supply; + + ret = i2c_of_probe_simple_enable_regulator(dev, ctx); + if (ret) + goto out_put_gpiod; + + ret = i2c_of_probe_simple_set_gpio(dev, ctx); + if (ret) + goto out_disable_regulator; + + return 0; + +out_disable_regulator: + i2c_of_probe_simple_disable_regulator(dev, ctx); +out_put_gpiod: + i2c_of_probe_simple_put_gpiod(ctx); +out_put_supply: + i2c_of_probe_simple_put_supply(ctx); +out_put_node: + of_node_put(node); + return ret; +} +EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_enable, I2C_OF_PROBER); + +/** + * i2c_of_probe_simple_cleanup_early - \ + * Simple helper for I2C OF prober to release GPIOs before component is enabled + * @dev: Pointer to the &struct device of the caller; unused. + * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context. + * + * GPIO descriptors are exclusive and have to be released before the + * actual driver probes so that the latter can acquire them. + */ +void i2c_of_probe_simple_cleanup_early(struct device *dev, void *data) +{ + struct i2c_of_probe_simple_ctx *ctx = data; + + i2c_of_probe_simple_put_gpiod(ctx); +} +EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup_early, I2C_OF_PROBER); + +/** + * i2c_of_probe_simple_cleanup - Clean up and release resources for I2C OF prober simple helpers + * @dev: Pointer to the &struct device of the caller, only used for dev_printk() messages + * @data: Pointer to &struct i2c_of_probe_simple_ctx helper context. + * + * * If a GPIO line was found and not yet released, set its value to the opposite of that + * set in i2c_of_probe_simple_enable() and release it. + * * If a regulator supply was found, disable that regulator and release it. + */ +void i2c_of_probe_simple_cleanup(struct device *dev, void *data) +{ + struct i2c_of_probe_simple_ctx *ctx = data; + + /* GPIO operations here are no-ops if i2c_of_probe_simple_cleanup_early was called. */ + i2c_of_probe_simple_disable_gpio(dev, ctx); + i2c_of_probe_simple_put_gpiod(ctx); + + i2c_of_probe_simple_disable_regulator(dev, ctx); + i2c_of_probe_simple_put_supply(ctx); +} +EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_cleanup, I2C_OF_PROBER); + +struct i2c_of_probe_ops i2c_of_probe_simple_ops = { + .enable = i2c_of_probe_simple_enable, + .cleanup_early = i2c_of_probe_simple_cleanup_early, + .cleanup = i2c_of_probe_simple_cleanup, +}; +EXPORT_SYMBOL_NS_GPL(i2c_of_probe_simple_ops, I2C_OF_PROBER); diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 0a6bdc6df1723..e73afbefe2225 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -122,7 +122,7 @@ EXPORT_SYMBOL(i2c_smbus_read_byte); s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) { return i2c_smbus_xfer(client->adapter, client->addr, client->flags, - I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); } EXPORT_SYMBOL(i2c_smbus_write_byte); diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 6f3eb710a75d6..42310c9a00c2d 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -282,7 +282,8 @@ static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *e struct i3c_device_info devinfo; u16 manuf, part, ext; - i3c_device_get_info(i3cdev, &devinfo); + if (i3cdev->desc) + devinfo = i3cdev->desc->info; manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); @@ -345,10 +346,10 @@ const struct bus_type i3c_bus_type = { EXPORT_SYMBOL_GPL(i3c_bus_type); static enum i3c_addr_slot_status -i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr) +i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask) { unsigned long status; - int bitpos = addr * 2; + int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS; if (addr > I2C_MAX_ADDR) return I3C_ADDR_SLOT_RSVD; @@ -356,22 +357,33 @@ i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr) status = bus->addrslots[bitpos / BITS_PER_LONG]; status >>= bitpos % BITS_PER_LONG; - return status & I3C_ADDR_SLOT_STATUS_MASK; + return status & mask; } -static void i3c_bus_set_addr_slot_status(struct i3c_bus *bus, u16 addr, - enum i3c_addr_slot_status status) +static enum i3c_addr_slot_status +i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr) +{ + return i3c_bus_get_addr_slot_status_mask(bus, addr, I3C_ADDR_SLOT_STATUS_MASK); +} + +static void i3c_bus_set_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, + enum i3c_addr_slot_status status, u32 mask) { - int bitpos = addr * 2; + int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS; unsigned long *ptr; if (addr > I2C_MAX_ADDR) return; ptr = bus->addrslots + (bitpos / BITS_PER_LONG); - *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK << - (bitpos % BITS_PER_LONG)); - *ptr |= (unsigned long)status << (bitpos % BITS_PER_LONG); + *ptr &= ~((unsigned long)mask << (bitpos % BITS_PER_LONG)); + *ptr |= ((unsigned long)status & mask) << (bitpos % BITS_PER_LONG); +} + +static void i3c_bus_set_addr_slot_status(struct i3c_bus *bus, u16 addr, + enum i3c_addr_slot_status status) +{ + i3c_bus_set_addr_slot_status_mask(bus, addr, status, I3C_ADDR_SLOT_STATUS_MASK); } static bool i3c_bus_dev_addr_is_avail(struct i3c_bus *bus, u8 addr) @@ -383,13 +395,44 @@ static bool i3c_bus_dev_addr_is_avail(struct i3c_bus *bus, u8 addr) return status == I3C_ADDR_SLOT_FREE; } +/* + * ┌────┬─────────────┬───┬─────────┬───┐ + * │S/Sr│ 7'h7E RnW=0 │ACK│ ENTDAA │ T ├────┐ + * └────┴─────────────┴───┴─────────┴───┘ │ + * ┌─────────────────────────────────────────┘ + * │ ┌──┬─────────────┬───┬─────────────────┬────────────────┬───┬─────────┐ + * └─►│Sr│7'h7E RnW=1 │ACK│48bit UID BCR DCR│Assign 7bit Addr│PAR│ ACK/NACK│ + * └──┴─────────────┴───┴─────────────────┴────────────────┴───┴─────────┘ + * Some master controllers (such as HCI) need to prepare the entire above transaction before + * sending it out to the I3C bus. This means that a 7-bit dynamic address needs to be allocated + * before knowing the target device's UID information. + * + * However, some I3C targets may request specific addresses (called as "init_dyn_addr"), which is + * typically specified by the DT-'s assigned-address property. Lower addresses having higher IBI + * priority. If it is available, i3c_bus_get_free_addr() preferably return a free address that is + * not in the list of desired addresses (called as "init_dyn_addr"). This allows the device with + * the "init_dyn_addr" to switch to its "init_dyn_addr" when it hot-joins the I3C bus. Otherwise, + * if the "init_dyn_addr" is already in use by another I3C device, the target device will not be + * able to switch to its desired address. + * + * If the previous step fails, fallback returning one of the remaining unassigned address, + * regardless of its state in the desired list. + */ static int i3c_bus_get_free_addr(struct i3c_bus *bus, u8 start_addr) { enum i3c_addr_slot_status status; u8 addr; for (addr = start_addr; addr < I3C_MAX_ADDR; addr++) { - status = i3c_bus_get_addr_slot_status(bus, addr); + status = i3c_bus_get_addr_slot_status_mask(bus, addr, + I3C_ADDR_SLOT_EXT_STATUS_MASK); + if (status == I3C_ADDR_SLOT_FREE) + return addr; + } + + for (addr = start_addr; addr < I3C_MAX_ADDR; addr++) { + status = i3c_bus_get_addr_slot_status_mask(bus, addr, + I3C_ADDR_SLOT_STATUS_MASK); if (status == I3C_ADDR_SLOT_FREE) return addr; } @@ -1417,7 +1460,7 @@ static void i3c_master_put_i3c_addrs(struct i3c_dev_desc *dev) I3C_ADDR_SLOT_FREE); if (dev->boardinfo && dev->boardinfo->init_dyn_addr) - i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr, + i3c_bus_set_addr_slot_status(&master->bus, dev->boardinfo->init_dyn_addr, I3C_ADDR_SLOT_FREE); } @@ -1506,16 +1549,9 @@ static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr) { struct i3c_master_controller *master = i3c_dev_get_master(dev); - enum i3c_addr_slot_status status; int ret; - if (dev->info.dyn_addr != old_dyn_addr && - (!dev->boardinfo || - dev->info.dyn_addr != dev->boardinfo->init_dyn_addr)) { - status = i3c_bus_get_addr_slot_status(&master->bus, - dev->info.dyn_addr); - if (status != I3C_ADDR_SLOT_FREE) - return -EBUSY; + if (dev->info.dyn_addr != old_dyn_addr) { i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr, I3C_ADDR_SLOT_I3C_DEV); @@ -1918,9 +1954,11 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) goto err_rstdaa; } - i3c_bus_set_addr_slot_status(&master->bus, - i3cboardinfo->init_dyn_addr, - I3C_ADDR_SLOT_I3C_DEV); + /* Do not mark as occupied until real device exist in bus */ + i3c_bus_set_addr_slot_status_mask(&master->bus, + i3cboardinfo->init_dyn_addr, + I3C_ADDR_SLOT_EXT_DESIRED, + I3C_ADDR_SLOT_EXT_STATUS_MASK); /* * Only try to create/attach devices that have a static @@ -2051,11 +2089,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, ibireq.max_payload_len = olddev->ibi->max_payload_len; ibireq.num_slots = olddev->ibi->num_slots; - if (olddev->ibi->enabled) { + if (olddev->ibi->enabled) enable_ibi = true; - i3c_dev_disable_ibi_locked(olddev); - } - + /* + * The olddev should not receive any commands on the + * i3c bus as it does not exist and has been assigned + * a new address. This will result in NACK or timeout. + * So, update the olddev->ibi->enabled flag to false + * to avoid DISEC with OldAddr. + */ + olddev->ibi->enabled = false; i3c_dev_free_ibi_locked(olddev); } mutex_unlock(&olddev->ibi_lock); @@ -2083,7 +2126,8 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, else expected_dyn_addr = newdev->info.dyn_addr; - if (newdev->info.dyn_addr != expected_dyn_addr) { + if (newdev->info.dyn_addr != expected_dyn_addr && + i3c_bus_get_addr_slot_status(&master->bus, expected_dyn_addr) == I3C_ADDR_SLOT_FREE) { /* * Try to apply the expected dynamic address. If it fails, keep * the address assigned by the master. diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 8d694672c1104..5b5c2e4bdc516 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -220,6 +220,14 @@ #define XFER_TIMEOUT (msecs_to_jiffies(1000)) #define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */ + +/* Timing values to configure 12.5MHz frequency */ +#define AMD_I3C_OD_TIMING 0x4C007C +#define AMD_I3C_PP_TIMING 0x8001A + +/* List of quirks */ +#define AMD_I3C_OD_PP_TIMING BIT(1) + struct dw_i3c_cmd { u32 cmd_lo; u32 cmd_hi; @@ -794,6 +802,12 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) return ret; } +static void amd_configure_od_pp_quirk(struct dw_i3c_master *master) +{ + master->i3c_od_timing = AMD_I3C_OD_TIMING; + master->i3c_pp_timing = AMD_I3C_PP_TIMING; +} + static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, struct i3c_ccc_cmd *ccc) { @@ -803,6 +817,13 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, if (ccc->id == I3C_CCC_ENTDAA) return -EINVAL; + /* AMD platform specific OD and PP timings */ + if (master->quirks & AMD_I3C_OD_PP_TIMING) { + amd_configure_od_pp_quirk(master); + writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); + writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); + } + ret = pm_runtime_resume_and_get(master->dev); if (ret < 0) { dev_err(master->dev, @@ -1602,6 +1623,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->maxdevs = ret >> 16; master->free_pos = GENMASK(master->maxdevs - 1, 0); + master->quirks = (unsigned long)device_get_match_data(&pdev->dev); + INIT_WORK(&master->hj_work, dw_i3c_hj_work); ret = i3c_master_register(&master->base, &pdev->dev, &dw_mipi_i3c_ops, false); @@ -1675,6 +1698,10 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master) static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master) { + /* AMD platform specific OD and PP timings */ + if (master->quirks & AMD_I3C_OD_PP_TIMING) + amd_configure_od_pp_quirk(master); + writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING); writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); @@ -1748,12 +1775,19 @@ static const struct of_device_id dw_i3c_master_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match); +static const struct acpi_device_id amd_i3c_device_match[] = { + { "AMDI0015", AMD_I3C_OD_PP_TIMING }, + { } +}; +MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match); + static struct platform_driver dw_i3c_driver = { .probe = dw_i3c_probe, .remove_new = dw_i3c_remove, .driver = { .name = "dw-i3c-master", .of_match_table = dw_i3c_master_of_match, + .acpi_match_table = amd_i3c_device_match, .pm = &dw_i3c_pm_ops, }, }; diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index 219ff815d3a73..c5cb695c16ab8 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -50,6 +50,7 @@ struct dw_i3c_master { u32 bus_free_timing; u32 i2c_fm_timing; u32 i2c_fmp_timing; + u32 quirks; /* * Per-device hardware data, used to manage the device address table * (DAT) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index a82c47c9986d3..e6e482a259b4c 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -80,8 +80,6 @@ #define INTR_HC_CMD_SEQ_UFLOW_STAT BIT(12) /* Cmd Sequence Underflow */ #define INTR_HC_RESET_CANCEL BIT(11) /* HC Cancelled Reset */ #define INTR_HC_INTERNAL_ERR BIT(10) /* HC Internal Error */ -#define INTR_HC_PIO BIT(8) /* cascaded PIO interrupt */ -#define INTR_HC_RINGS GENMASK(7, 0) #define DAT_SECTION 0x30 /* Device Address Table */ #define DAT_ENTRY_SIZE GENMASK(31, 28) @@ -438,7 +436,8 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev) kfree(dev_data); return ret; } - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret, dev->info.dyn_addr); + mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret, + dev->info.dyn_addr ?: dev->info.static_addr); dev_data->dat_idx = ret; } i3c_dev_set_master_data(dev, dev_data); @@ -597,9 +596,6 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) if (val) { reg_write(INTR_STATUS, val); - } else { - /* v1.0 does not have PIO cascaded notification bits */ - val |= INTR_HC_PIO; } if (val & INTR_HC_RESET_CANCEL) { @@ -610,14 +606,9 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) dev_err(&hci->master.dev, "Host Controller Internal Error\n"); val &= ~INTR_HC_INTERNAL_ERR; } - if (val & INTR_HC_PIO) { - hci->io->irq_handler(hci, 0); - val &= ~INTR_HC_PIO; - } - if (val & INTR_HC_RINGS) { - hci->io->irq_handler(hci, val & INTR_HC_RINGS); - val &= ~INTR_HC_RINGS; - } + + hci->io->irq_handler(hci); + if (val) dev_err(&hci->master.dev, "unexpected INTR_STATUS %#x\n", val); else diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index a918e96b21fdd..e8e56a8d20573 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -159,10 +159,10 @@ static void hci_dma_cleanup(struct i3c_hci *hci) for (i = 0; i < rings->total; i++) { rh = &rings->headers[i]; + rh_reg_write(INTR_SIGNAL_ENABLE, 0); rh_reg_write(RING_CONTROL, 0); rh_reg_write(CR_SETUP, 0); rh_reg_write(IBI_SETUP, 0); - rh_reg_write(INTR_SIGNAL_ENABLE, 0); if (rh->xfer) dma_free_coherent(&hci->master.dev, @@ -733,20 +733,16 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) rh_reg_write(CHUNK_CONTROL, rh_reg_read(CHUNK_CONTROL) + ibi_chunks); } -static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask) +static bool hci_dma_irq_handler(struct i3c_hci *hci) { struct hci_rings_data *rings = hci->io_data; unsigned int i; bool handled = false; - for (i = 0; mask && i < rings->total; i++) { + for (i = 0; i < rings->total; i++) { struct hci_rh_data *rh; u32 status; - if (!(mask & BIT(i))) - continue; - mask &= ~BIT(i); - rh = &rings->headers[i]; status = rh_reg_read(INTR_STATUS); DBG("rh%d status: %#x", i, status); diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index aaa47ac473814..69ea1d10414b8 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -115,7 +115,7 @@ static inline void hci_free_xfer(struct hci_xfer *xfer, unsigned int n) /* This abstracts PIO vs DMA operations */ struct hci_io_ops { - bool (*irq_handler)(struct i3c_hci *hci, unsigned int mask); + bool (*irq_handler)(struct i3c_hci *hci); int (*queue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n); bool (*dequeue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n); int (*request_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev, diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index d0272aa93599c..2fc71e6969111 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -979,7 +979,7 @@ static void hci_pio_recycle_ibi_slot(struct i3c_hci *hci, i3c_generic_ibi_recycle_slot(dev_ibi->pool, slot); } -static bool hci_pio_irq_handler(struct i3c_hci *hci, unsigned int unused) +static bool hci_pio_irq_handler(struct i3c_hci *hci) { struct hci_pio_data *pio = hci->io_data; u32 status; diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index a7bfc678153e6..c1ee3828e7eec 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -130,8 +130,8 @@ #define SVC_I3C_PPBAUD_MAX 15 #define SVC_I3C_QUICK_I2C_CLK 4170000 -#define SVC_I3C_EVENT_IBI BIT(0) -#define SVC_I3C_EVENT_HOTJOIN BIT(1) +#define SVC_I3C_EVENT_IBI GENMASK(7, 0) +#define SVC_I3C_EVENT_HOTJOIN BIT(31) struct svc_i3c_cmd { u8 addr; @@ -214,7 +214,7 @@ struct svc_i3c_master { spinlock_t lock; } ibi; struct mutex lock; - int enabled_events; + u32 enabled_events; u32 mctrl_config; }; @@ -388,10 +388,11 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master, return 0; } -static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master, +static int svc_i3c_master_ack_ibi(struct svc_i3c_master *master, bool mandatory_byte) { unsigned int ibi_ack_nack; + u32 reg; ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK; if (mandatory_byte) @@ -400,13 +401,43 @@ static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master, ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE; writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL); + + return readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg, + SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000); + } -static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master) +static int svc_i3c_master_nack_ibi(struct svc_i3c_master *master) { + int ret; + u32 reg; + writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK | SVC_I3C_MCTRL_IBIRESP_NACK, master->regs + SVC_I3C_MCTRL); + + ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg, + SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000); + return ret; +} + +static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 mstatus) +{ + u32 ibitype; + int ret = 0; + + ibitype = SVC_I3C_MSTATUS_IBITYPE(mstatus); + + writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS); + + /* Hardware can't auto emit NACK for hot join and master request */ + switch (ibitype) { + case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: + case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: + ret = svc_i3c_master_nack_ibi(master); + } + + return ret; } static void svc_i3c_master_ibi_work(struct work_struct *work) @@ -418,7 +449,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) u32 status, val; int ret; - mutex_lock(&master->lock); + /* + * According to I3C spec ver 1.1, 09-Jun-2021, section 5.1.2.5: + * + * The I3C Controller shall hold SCL low while the Bus is in ACK/NACK Phase of I3C/I2C + * transfer. But maximum stall time is 100us. The IRQs have to be disabled to prevent + * schedule during the whole I3C transaction, otherwise, the I3C bus timeout may happen if + * any irq or schedule happen during transaction. + */ + guard(spinlock_irqsave)(&master->xferqueue.lock); + /* * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing * readl_relaxed_poll_timeout() to return immediately. Consequently, @@ -438,8 +478,8 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) master->regs + SVC_I3C_MCTRL); /* Wait for IBIWON, should take approximately 100us */ - ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val, - SVC_I3C_MSTATUS_IBIWON(val), 0, 1000); + ret = readl_relaxed_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, val, + SVC_I3C_MSTATUS_IBIWON(val), 0, 100); if (ret) { dev_err(master->dev, "Timeout when polling for IBIWON\n"); svc_i3c_master_emit_stop(master); @@ -511,7 +551,6 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) reenable_ibis: svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART); - mutex_unlock(&master->lock); } static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id) @@ -854,6 +893,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, int ret, i; while (true) { + /* clean SVC_I3C_MINT_IBIWON w1c bits */ + writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS); + /* SVC_I3C_MCTRL_REQUEST_PROC_DAA have two mode, ENTER DAA or PROCESS DAA. * * ENTER DAA: @@ -905,6 +947,11 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ret = svc_i3c_master_readb(master, data, 2); if (ret) break; + } else if (SVC_I3C_MSTATUS_IBIWON(reg)) { + ret = svc_i3c_master_handle_ibi_won(master, reg); + if (ret) + break; + continue; } else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) { if (SVC_I3C_MSTATUS_STATE_IDLE(reg) && SVC_I3C_MSTATUS_COMPLETE(reg)) { @@ -1056,12 +1103,27 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m) if (ret) goto rpm_out; - /* Register all devices who participated to the core */ - for (i = 0; i < dev_nb; i++) { - ret = i3c_master_add_i3c_dev_locked(m, addrs[i]); - if (ret) - goto rpm_out; - } + /* + * Register all devices who participated to the core + * + * If two devices (A and B) are detected in DAA and address 0xa is assigned to + * device A and 0xb to device B, a failure in i3c_master_add_i3c_dev_locked() + * for device A (addr: 0xa) could prevent device B (addr: 0xb) from being + * registered on the bus. The I3C stack might still consider 0xb a free + * address. If a subsequent Hotjoin occurs, 0xb might be assigned to Device A, + * causing both devices A and B to use the same address 0xb, violating the I3C + * specification. + * + * The return value for i3c_master_add_i3c_dev_locked() should not be checked + * because subsequent steps will scan the entire I3C bus, independent of + * whether i3c_master_add_i3c_dev_locked() returns success. + * + * If device A registration fails, there is still a chance to register device + * B. i3c_master_add_i3c_dev_locked() can reset DAA if a failure occurs while + * retrieving device information. + */ + for (i = 0; i < dev_nb; i++) + i3c_master_add_i3c_dev_locked(m, addrs[i]); /* Configure IBI auto-rules */ ret = svc_i3c_update_ibirules(master); @@ -1163,6 +1225,26 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, if (ret) goto emit_stop; + /* + * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a + * Frame with I3C Target Address. + * + * The I3C Controller normally should start a Frame, the Address may be arbitrated, + * and so the Controller shall monitor to see whether an In-Band Interrupt request, + * a Controller Role Request (i.e., Secondary Controller requests to become the + * Active Controller), or a Hot-Join Request has been made. + * + * If missed IBIWON check, the wrong data will be return. When IBIWON happen, issue + * repeat start. Address arbitrate only happen at START, never happen at REPEAT + * start. + */ + if (SVC_I3C_MSTATUS_IBIWON(reg)) { + ret = svc_i3c_master_handle_ibi_won(master, reg); + if (ret) + goto emit_stop; + continue; + } + if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) { /* * According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3. @@ -1196,24 +1278,6 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, } } - /* - * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame - * with I3C Target Address. - * - * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so - * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller - * Role Request (i.e., Secondary Controller requests to become the Active Controller), or - * a Hot-Join Request has been made. - * - * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure - * and yield the above events handler. - */ - if (SVC_I3C_MSTATUS_IBIWON(reg)) { - ret = -EAGAIN; - *actual_len = 0; - goto emit_stop; - } - if (rnw) ret = svc_i3c_master_read(master, in, xfer_len); else @@ -1624,7 +1688,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev) return ret; } - master->enabled_events |= SVC_I3C_EVENT_IBI; + master->enabled_events++; svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART); return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); @@ -1636,7 +1700,7 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev) struct svc_i3c_master *master = to_svc_i3c_master(m); int ret; - master->enabled_events &= ~SVC_I3C_EVENT_IBI; + master->enabled_events--; if (!master->enabled_events) svc_i3c_master_disable_interrupts(master); @@ -1827,8 +1891,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev) rpm_disable: pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); err_disable_clks: svc_i3c_master_unprepare_clks(master); diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index eabaefa92f19d..7ccd2f653b9ba 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -643,7 +643,7 @@ static irqreturn_t adxl355_trigger_handler(int irq, void *p) * The acceleration data is 24 bits and big endian. It has to be saved * in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer. * The buf array is 14 bytes as it includes 3x4=12 bytes for - * accelaration data of x, y, and z axis. It also includes 2 bytes for + * acceleration data of x, y, and z axis. It also includes 2 bytes for * temperature data. */ ret = regmap_bulk_read(data->regmap, ADXL355_XDATA3_REG, diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index e790a66d86c79..705375f3b56e6 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -1073,7 +1073,7 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { enum adxl367_activity_type act; diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index ef8dd557877bd..5b9eb364760a2 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -940,7 +940,7 @@ static int adxl372_read_event_config(struct iio_dev *indio_dev, const struct iio static int adxl372_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct adxl372_state *st = iio_priv(indio_dev); diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index f80527d899be4..a1460120d9da4 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -1181,7 +1181,7 @@ static int adxl380_read_raw(struct iio_dev *indio_dev, ret = adxl380_read_chn(st, chan->address); iio_device_release_direct_mode(indio_dev); - if (ret) + if (ret < 0) return ret; *val = sign_extend32(ret >> chan->scan_type.shift, @@ -1386,7 +1386,7 @@ static int adxl380_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct adxl380_state *st = iio_priv(indio_dev); enum adxl380_axis axis; @@ -1719,7 +1719,6 @@ static int adxl380_config_irq(struct iio_dev *indio_dev) { struct adxl380_state *st = iio_priv(indio_dev); unsigned long irq_flag; - struct irq_data *desc; u32 irq_type; u8 polarity; int ret; @@ -1737,11 +1736,7 @@ static int adxl380_config_irq(struct iio_dev *indio_dev) st->int_map[1] = ADXL380_INT1_MAP1_REG; } - desc = irq_get_irq_data(st->irq); - if (!desc) - return dev_err_probe(st->dev, -EINVAL, "Could not find IRQ %d\n", st->irq); - - irq_type = irqd_get_trigger_type(desc); + irq_type = irq_get_trigger_type(st->irq); if (irq_type == IRQ_TYPE_LEVEL_HIGH) { polarity = 0; irq_flag = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 2445a0f7bc2ba..128db14ba726a 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -144,7 +145,7 @@ struct bma180_data { /* Ensure timestamp is naturally aligned */ struct { s16 chan[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index fcbd695e46549..009e6243c6cba 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index e4fe36768216d..906d2577be2d6 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -115,7 +115,7 @@ struct bma400_data { struct { __le16 buff[3]; u8 temperature; - s64 ts __aligned(8); + aligned_s64 ts; } buffer __aligned(IIO_DMA_MINALIGN); __le16 status; __be16 duration; @@ -1293,7 +1293,7 @@ static int bma400_disable_adv_interrupt(struct bma400_data *data) static int bma400_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct bma400_data *data = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 0f32c1e92b4dc..158579350d596 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -804,7 +804,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct bmc150_accel_data *data = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h index 7775c5edaeefd..7a7baf52e5955 100644 --- a/drivers/iio/accel/bmc150-accel.h +++ b/drivers/iio/accel/bmc150-accel.h @@ -6,6 +6,7 @@ #include #include #include +#include #include struct regmap; @@ -69,7 +70,7 @@ struct bmc150_accel_data { */ struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; u8 bw_bits; u32 slope_dur; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index acadabec4df7a..f07fba17048e7 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -163,7 +164,7 @@ struct fxls8962af_data { const struct fxls8962af_chip_info *chip_info; struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ struct iio_mount_matrix orientation; @@ -616,7 +617,7 @@ static int fxls8962af_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct fxls8962af_data *data = iio_priv(indio_dev); u8 enable_event, enable_bits; @@ -1103,8 +1104,7 @@ static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq) if (ret) return ret; - irq_type = irqd_get_trigger_type(irq_get_irq_data(irq)); - + irq_type = irq_get_trigger_type(irq); switch (irq_type) { case IRQF_TRIGGER_HIGH: case IRQF_TRIGGER_RISING: diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 9b7a73a4c48a5..26b1033799fef 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -28,7 +28,7 @@ struct accel_3d_state { /* Ensure timestamp is naturally aligned */ struct { u32 accel_val[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -328,6 +328,7 @@ static int accel_3d_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_accel_3d_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret = 0; const char *name; struct iio_dev *indio_dev; @@ -335,8 +336,6 @@ static int hid_accel_3d_probe(struct platform_device *pdev) const struct iio_chan_spec *channel_spec; int channel_size; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct accel_3d_state)); if (indio_dev == NULL) @@ -424,7 +423,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_accel_3d_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct accel_3d_state *accel_state = iio_priv(indio_dev); @@ -452,7 +451,7 @@ static struct platform_driver hid_accel_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_accel_3d_probe, - .remove_new = hid_accel_3d_remove, + .remove = hid_accel_3d_remove, }; module_platform_driver(hid_accel_3d_platform_driver); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 53d59a04ae15e..53261e1d5d1fe 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -292,7 +293,7 @@ struct kx022a_data { __le16 buffer[8] __aligned(IIO_DMA_MINALIGN); struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -594,7 +595,7 @@ static int kx022a_get_axis(struct kx022a_data *data, if (ret) return ret; - *val = le16_to_cpu(data->buffer[0]); + *val = (s16)le16_to_cpu(data->buffer[0]); return IIO_VAL_INT; } diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index b76df88163232..f2496cad8ec25 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -4,13 +4,15 @@ * Copyright (c) 2014, Intel Corporation. */ -#include #include #include #include #include +#include +#include #include #include +#include #include #include #include @@ -168,14 +170,73 @@ #define KXCJK1013_DEFAULT_WAKE_THRES 1 -enum kx_chipset { - KXCJK1013, - KXCJ91008, - KXTJ21009, - KXTF9, - KX0221020, - KX0231025, - KX_MAX_CHIPS /* this must be last */ +/* Refer to section 4 of the specification */ +struct kx_odr_start_up_time { + int odr_bits; + int usec; +}; + +/* KXCJK-1013 */ +static const struct kx_odr_start_up_time kxcjk1013_odr_start_up_times[] = { + { 0x08, 100000 }, + { 0x09, 100000 }, + { 0x0A, 100000 }, + { 0x0B, 100000 }, + { 0x00, 80000 }, + { 0x01, 41000 }, + { 0x02, 21000 }, + { 0x03, 11000 }, + { 0x04, 6400 }, + { 0x05, 3900 }, + { 0x06, 2700 }, + { 0x07, 2100 }, + { } +}; + +/* KXCTJ2-1009 */ +static const struct kx_odr_start_up_time kxtj21009_odr_start_up_times[] = { + { 0x08, 1240000 }, + { 0x09, 621000 }, + { 0x0A, 309000 }, + { 0x0B, 151000 }, + { 0x00, 80000 }, + { 0x01, 41000 }, + { 0x02, 21000 }, + { 0x03, 11000 }, + { 0x04, 6000 }, + { 0x05, 4000 }, + { 0x06, 3000 }, + { 0x07, 2000 }, + { } +}; + +/* KXTF9 */ +static const struct kx_odr_start_up_time kxtf9_odr_start_up_times[] = { + { 0x01, 81000 }, + { 0x02, 41000 }, + { 0x03, 21000 }, + { 0x04, 11000 }, + { 0x05, 5100 }, + { 0x06, 2700 }, + { } +}; + +/* KX023-1025 */ +static const struct kx_odr_start_up_time kx0231025_odr_start_up_times[] = { + /* First 4 are not in datasheet, taken from KXCTJ2-1009 */ + { 0x08, 1240000 }, + { 0x09, 621000 }, + { 0x0A, 309000 }, + { 0x0B, 151000 }, + { 0x00, 81000 }, + { 0x01, 40000 }, + { 0x02, 22000 }, + { 0x03, 12000 }, + { 0x04, 7000 }, + { 0x05, 4400 }, + { 0x06, 3000 }, + { 0x07, 3000 }, + { } }; enum kx_acpi_type { @@ -234,6 +295,55 @@ static const struct kx_chipset_regs kx0231025_regs = { .wake_thres = KX023_REG_ATH, }; +struct kx_chipset_info { + const struct kx_chipset_regs *regs; + const struct kx_odr_start_up_time *times; + enum kx_acpi_type acpi_type; +}; + +static const struct kx_chipset_info kxcjk1013_info = { + .regs = &kxcjk1013_regs, + .times = pm_ptr(kxcjk1013_odr_start_up_times), +}; + +static const struct kx_chipset_info kxcj91008_info = { + .regs = &kxcjk1013_regs, + .times = pm_ptr(kxcjk1013_odr_start_up_times), +}; + +static const struct kx_chipset_info kxcj91008_kiox010a_info = { + .regs = &kxcjk1013_regs, + .times = pm_ptr(kxcjk1013_odr_start_up_times), + .acpi_type = ACPI_KIOX010A, +}; + +static const struct kx_chipset_info kxcj91008_kiox020a_info = { + .regs = &kxcjk1013_regs, + .times = pm_ptr(kxcjk1013_odr_start_up_times), + .acpi_type = ACPI_GENERIC, +}; + +static const struct kx_chipset_info kxcj91008_smo8500_info = { + .regs = &kxcjk1013_regs, + .times = pm_ptr(kxcjk1013_odr_start_up_times), + .acpi_type = ACPI_SMO8500, +}; + +static const struct kx_chipset_info kxtj21009_info = { + .regs = &kxcjk1013_regs, + .times = pm_ptr(kxtj21009_odr_start_up_times), +}; + +static const struct kx_chipset_info kxtf9_info = { + .regs = &kxtf9_regs, + .times = pm_ptr(kxtf9_odr_start_up_times), +}; + +static const struct kx_chipset_info kx0231025_info = { + .regs = &kx0231025_regs, + .times = pm_ptr(kx0231025_odr_start_up_times), +}; + enum kxcjk1013_axis { AXIS_X, AXIS_Y, @@ -250,7 +360,7 @@ struct kxcjk1013_data { /* Ensure timestamp naturally aligned */ struct { s16 chans[AXIS_MAX]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 odr_bits; u8 range; @@ -261,9 +371,7 @@ struct kxcjk1013_data { int ev_enable_state; bool motion_trigger_on; int64_t timestamp; - enum kx_chipset chipset; - enum kx_acpi_type acpi_type; - const struct kx_chipset_regs *regs; + const struct kx_chipset_info *info; }; enum kxcjk1013_mode { @@ -314,83 +422,6 @@ static const struct kx_odr_map kxtf9_samp_freq_table[] = { static const char *const kxtf9_samp_freq_avail = "25 50 100 200 400 800"; -/* Refer to section 4 of the specification */ -static __maybe_unused const struct { - int odr_bits; - int usec; -} odr_start_up_times[KX_MAX_CHIPS][12] = { - /* KXCJK-1013 */ - { - {0x08, 100000}, - {0x09, 100000}, - {0x0A, 100000}, - {0x0B, 100000}, - {0, 80000}, - {0x01, 41000}, - {0x02, 21000}, - {0x03, 11000}, - {0x04, 6400}, - {0x05, 3900}, - {0x06, 2700}, - {0x07, 2100}, - }, - /* KXCJ9-1008 */ - { - {0x08, 100000}, - {0x09, 100000}, - {0x0A, 100000}, - {0x0B, 100000}, - {0, 80000}, - {0x01, 41000}, - {0x02, 21000}, - {0x03, 11000}, - {0x04, 6400}, - {0x05, 3900}, - {0x06, 2700}, - {0x07, 2100}, - }, - /* KXCTJ2-1009 */ - { - {0x08, 1240000}, - {0x09, 621000}, - {0x0A, 309000}, - {0x0B, 151000}, - {0, 80000}, - {0x01, 41000}, - {0x02, 21000}, - {0x03, 11000}, - {0x04, 6000}, - {0x05, 4000}, - {0x06, 3000}, - {0x07, 2000}, - }, - /* KXTF9 */ - { - {0x01, 81000}, - {0x02, 41000}, - {0x03, 21000}, - {0x04, 11000}, - {0x05, 5100}, - {0x06, 2700}, - }, - /* KX023-1025 */ - { - /* First 4 are not in datasheet, taken from KXCTJ2-1009 */ - {0x08, 1240000}, - {0x09, 621000}, - {0x0A, 309000}, - {0x0B, 151000}, - {0, 81000}, - {0x01, 40000}, - {0x02, 22000}, - {0x03, 12000}, - {0x04, 7000}, - {0x05, 4400}, - {0x06, 3000}, - {0x07, 3000}, - }, -}; - static const struct { u16 scale; u8 gsel_0; @@ -424,30 +455,15 @@ static int kiox010a_dsm(struct device *dev, int fn_index) return 0; } -static const struct acpi_device_id kx_acpi_match[] = { - {"KXCJ1013", KXCJK1013}, - {"KXCJ1008", KXCJ91008}, - {"KXCJ9000", KXCJ91008}, - {"KIOX0008", KXCJ91008}, - {"KIOX0009", KXTJ21009}, - {"KIOX000A", KXCJ91008}, - {"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */ - {"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */ - {"KXTJ1009", KXTJ21009}, - {"KXJ2109", KXTJ21009}, - {"SMO8500", KXCJ91008}, - { } -}; -MODULE_DEVICE_TABLE(acpi, kx_acpi_match); - #endif static int kxcjk1013_set_mode(struct kxcjk1013_data *data, enum kxcjk1013_mode mode) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -458,7 +474,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data, else ret |= KXCJK1013_REG_CTRL1_BIT_PC1; - ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -470,9 +486,10 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data, static int kxcjk1013_get_mode(struct kxcjk1013_data *data, enum kxcjk1013_mode *mode) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -488,9 +505,10 @@ static int kxcjk1013_get_mode(struct kxcjk1013_data *data, static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -501,7 +519,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); - ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -514,10 +532,11 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) static int kxcjk1013_chip_init(struct kxcjk1013_data *data) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; #ifdef CONFIG_ACPI - if (data->acpi_type == ACPI_KIOX010A) { + if (data->info->acpi_type == ACPI_KIOX010A) { /* Make sure the kbd and touchpad on 2-in-1s using 2 KXCJ91008-s work */ kiox010a_dsm(&data->client->dev, KIOX010A_SET_LAPTOP_MODE); } @@ -535,7 +554,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -544,7 +563,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) /* Set 12 bit mode */ ret |= KXCJK1013_REG_CTRL1_BIT_RES; - ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl\n"); return ret; @@ -555,7 +574,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->data_ctrl); + ret = i2c_smbus_read_byte_data(data->client, regs->data_ctrl); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_data_ctrl\n"); return ret; @@ -564,7 +583,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) data->odr_bits = ret; /* Set up INT polarity */ - ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -575,14 +594,14 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEA; - ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - /* On KX023 and KX022, route all used interrupts to INT1 for now */ - if ((data->chipset == KX0231025 || data->chipset == KX0221020) && data->client->irq > 0) { + /* On KX023, route all used interrupts to INT1 for now */ + if (data->info == &kx0231025_info && data->client->irq > 0) { ret = i2c_smbus_write_byte_data(data->client, KX023_REG_INC4, KX023_REG_INC4_DRDY1 | KX023_REG_INC4_WUFI1); @@ -601,20 +620,17 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) return 0; } -#ifdef CONFIG_PM static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data) { - int i; - int idx = data->chipset; + const struct kx_odr_start_up_time *times; - for (i = 0; i < ARRAY_SIZE(odr_start_up_times[idx]); ++i) { - if (odr_start_up_times[idx][i].odr_bits == data->odr_bits) - return odr_start_up_times[idx][i].usec; + for (times = data->info->times; times->usec; times++) { + if (times->odr_bits == data->odr_bits) + return times->usec; } return KXCJK1013_MAX_STARTUP_TIME_US; } -#endif static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) { @@ -639,18 +655,17 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; - ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_timer, - data->wake_dur); + ret = i2c_smbus_write_byte_data(data->client, regs->wake_timer, data->wake_dur); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_wake_timer\n"); return ret; } - ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_thres, - data->wake_thres); + ret = i2c_smbus_write_byte_data(data->client, regs->wake_thres, data->wake_thres); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_wake_thres\n"); return ret; @@ -662,6 +677,7 @@ static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, bool status) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; enum kxcjk1013_mode store_mode; @@ -678,7 +694,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -689,13 +705,13 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -706,7 +722,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE; - ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -724,6 +740,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, bool status) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; enum kxcjk1013_mode store_mode; @@ -736,7 +753,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -747,13 +764,13 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); + ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -764,7 +781,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; - ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); + ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -811,6 +828,7 @@ static int kxcjk1013_convert_odr_value(const struct kx_odr_map *map, static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) { + const struct kx_chipset_regs *regs = data->info->regs; int ret; enum kxcjk1013_mode store_mode; const struct kx_odr_map *odr_setting; @@ -819,7 +837,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) if (ret < 0) return ret; - if (data->chipset == KXTF9) + if (data->info == &kxtf9_info) odr_setting = kxcjk1013_find_odr_value(kxtf9_samp_freq_table, ARRAY_SIZE(kxtf9_samp_freq_table), val, val2); @@ -836,7 +854,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) if (ret < 0) return ret; - ret = i2c_smbus_write_byte_data(data->client, data->regs->data_ctrl, + ret = i2c_smbus_write_byte_data(data->client, regs->data_ctrl, odr_setting->odr_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing data_ctrl\n"); @@ -845,7 +863,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) data->odr_bits = odr_setting->odr_bits; - ret = i2c_smbus_write_byte_data(data->client, data->regs->wuf_ctrl, + ret = i2c_smbus_write_byte_data(data->client, regs->wuf_ctrl, odr_setting->wuf_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl2\n"); @@ -863,7 +881,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) static int kxcjk1013_get_odr(struct kxcjk1013_data *data, int *val, int *val2) { - if (data->chipset == KXTF9) + if (data->info == &kxtf9_info) return kxcjk1013_convert_odr_value(kxtf9_samp_freq_table, ARRAY_SIZE(kxtf9_samp_freq_table), data->odr_bits, val, val2); @@ -1063,7 +1081,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; @@ -1130,7 +1148,7 @@ static ssize_t kxcjk1013_get_samp_freq_avail(struct device *dev, struct kxcjk1013_data *data = iio_priv(indio_dev); const char *str; - if (data->chipset == KXTF9) + if (data->info == &kxtf9_info) str = kxtf9_samp_freq_avail; else str = kxcjk1013_samp_freq_avail; @@ -1207,7 +1225,7 @@ static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = { .postdisable = kxcjk1013_buffer_postdisable, }; -static const struct iio_info kxcjk1013_info = { +static const struct iio_info kxcjk1013_iio_info = { .attrs = &kxcjk1013_attrs_group, .read_raw = kxcjk1013_read_raw, .write_raw = kxcjk1013_write_raw, @@ -1247,9 +1265,10 @@ static void kxcjk1013_trig_reen(struct iio_trigger *trig) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct kxcjk1013_data *data = iio_priv(indio_dev); + const struct kx_chipset_regs *regs = data->info->regs; int ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel); + ret = i2c_smbus_read_byte_data(data->client, regs->int_rel); if (ret < 0) dev_err(&data->client->dev, "Error reading reg_int_rel\n"); } @@ -1301,8 +1320,9 @@ static const struct iio_trigger_ops kxcjk1013_trigger_ops = { static void kxcjk1013_report_motion_event(struct iio_dev *indio_dev) { struct kxcjk1013_data *data = iio_priv(indio_dev); + const struct kx_chipset_regs *regs = data->info->regs; - int ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src2); + int ret = i2c_smbus_read_byte_data(data->client, regs->int_src2); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_src2\n"); return; @@ -1367,16 +1387,17 @@ static irqreturn_t kxcjk1013_event_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct kxcjk1013_data *data = iio_priv(indio_dev); + const struct kx_chipset_regs *regs = data->info->regs; int ret; - ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src1); + ret = i2c_smbus_read_byte_data(data->client, regs->int_src1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_src1\n"); goto ack_intr; } if (ret & KXCJK1013_REG_INT_SRC1_BIT_WUFS) { - if (data->chipset == KXTF9) + if (data->info == &kxtf9_info) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, @@ -1392,7 +1413,7 @@ static irqreturn_t kxcjk1013_event_handler(int irq, void *private) if (data->dready_trigger_on) return IRQ_HANDLED; - ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel); + ret = i2c_smbus_read_byte_data(data->client, regs->int_rel); if (ret < 0) dev_err(&data->client->dev, "Error reading reg_int_rel\n"); @@ -1417,31 +1438,6 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) return IRQ_HANDLED; } -static const char *kxcjk1013_match_acpi_device(struct device *dev, - enum kx_chipset *chipset, - enum kx_acpi_type *acpi_type, - const char **label) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - if (strcmp(id->id, "SMO8500") == 0) { - *acpi_type = ACPI_SMO8500; - } else if (strcmp(id->id, "KIOX010A") == 0) { - *acpi_type = ACPI_KIOX010A; - *label = "accel-display"; - } else if (strcmp(id->id, "KIOX020A") == 0) { - *label = "accel-base"; - } - - *chipset = (enum kx_chipset)id->driver_data; - - return dev_name(dev); -} - static int kxcjk1013_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -1449,6 +1445,7 @@ static int kxcjk1013_probe(struct i2c_client *client) struct kxcjk1013_data *data; struct iio_dev *indio_dev; struct kxcjk_1013_platform_data *pdata; + const void *ddata = NULL; const char *name; int ret; @@ -1489,32 +1486,18 @@ static int kxcjk1013_probe(struct i2c_client *client) msleep(20); if (id) { - data->chipset = (enum kx_chipset)(id->driver_data); name = id->name; - } else if (ACPI_HANDLE(&client->dev)) { - name = kxcjk1013_match_acpi_device(&client->dev, - &data->chipset, - &data->acpi_type, - &indio_dev->label); - } else - return -ENODEV; - - switch (data->chipset) { - case KXCJK1013: - case KXCJ91008: - case KXTJ21009: - data->regs = &kxcjk1013_regs; - break; - case KXTF9: - data->regs = &kxtf9_regs; - break; - case KX0221020: - case KX0231025: - data->regs = &kx0231025_regs; - break; - default: - return -EINVAL; + data->info = (const struct kx_chipset_info *)(id->driver_data); + } else { + name = iio_get_acpi_device_name_and_data(&client->dev, &ddata); + data->info = ddata; + if (data->info == &kxcj91008_kiox010a_info) + indio_dev->label = "accel-display"; + else if (data->info == &kxcj91008_kiox020a_info) + indio_dev->label = "accel-base"; } + if (!name) + return -ENODEV; ret = kxcjk1013_chip_init(data); if (ret < 0) @@ -1527,9 +1510,9 @@ static int kxcjk1013_probe(struct i2c_client *client) indio_dev->available_scan_masks = kxcjk1013_scan_masks; indio_dev->name = name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &kxcjk1013_info; + indio_dev->info = &kxcjk1013_iio_info; - if (client->irq > 0 && data->acpi_type != ACPI_SMO8500) { + if (client->irq > 0 && data->info->acpi_type != ACPI_SMO8500) { ret = devm_request_threaded_irq(&client->dev, client->irq, kxcjk1013_data_rdy_trig_poll, kxcjk1013_event_handler, @@ -1637,7 +1620,6 @@ static void kxcjk1013_remove(struct i2c_client *client) mutex_unlock(&data->mutex); } -#ifdef CONFIG_PM_SLEEP static int kxcjk1013_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1665,9 +1647,7 @@ static int kxcjk1013_resume(struct device *dev) return ret; } -#endif -#ifdef CONFIG_PM static int kxcjk1013_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -1701,44 +1681,56 @@ static int kxcjk1013_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops kxcjk1013_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(kxcjk1013_suspend, kxcjk1013_resume) - SET_RUNTIME_PM_OPS(kxcjk1013_runtime_suspend, - kxcjk1013_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(kxcjk1013_suspend, kxcjk1013_resume) + RUNTIME_PM_OPS(kxcjk1013_runtime_suspend, kxcjk1013_runtime_resume, NULL) }; static const struct i2c_device_id kxcjk1013_id[] = { - {"kxcjk1013", KXCJK1013}, - {"kxcj91008", KXCJ91008}, - {"kxtj21009", KXTJ21009}, - {"kxtf9", KXTF9}, - {"kx022-1020", KX0221020}, - {"kx023-1025", KX0231025}, - {"SMO8500", KXCJ91008}, - {} + { "kxcjk1013", (kernel_ulong_t)&kxcjk1013_info }, + { "kxcj91008", (kernel_ulong_t)&kxcj91008_info }, + { "kxtj21009", (kernel_ulong_t)&kxtj21009_info }, + { "kxtf9", (kernel_ulong_t)&kxtf9_info }, + { "kx023-1025", (kernel_ulong_t)&kx0231025_info }, + { } }; - MODULE_DEVICE_TABLE(i2c, kxcjk1013_id); static const struct of_device_id kxcjk1013_of_match[] = { - { .compatible = "kionix,kxcjk1013", }, - { .compatible = "kionix,kxcj91008", }, - { .compatible = "kionix,kxtj21009", }, - { .compatible = "kionix,kxtf9", }, - { .compatible = "kionix,kx022-1020", }, - { .compatible = "kionix,kx023-1025", }, + { .compatible = "kionix,kxcjk1013", &kxcjk1013_info }, + { .compatible = "kionix,kxcj91008", &kxcj91008_info }, + { .compatible = "kionix,kxtj21009", &kxtj21009_info }, + { .compatible = "kionix,kxtf9", &kxtf9_info }, + { .compatible = "kionix,kx023-1025", &kx0231025_info }, { } }; MODULE_DEVICE_TABLE(of, kxcjk1013_of_match); +static const struct acpi_device_id kx_acpi_match[] = { + { "KIOX0008", (kernel_ulong_t)&kxcj91008_info }, + { "KIOX0009", (kernel_ulong_t)&kxtj21009_info }, + { "KIOX000A", (kernel_ulong_t)&kxcj91008_info }, + /* KXCJ91008 in the display of a yoga 2-in-1 */ + { "KIOX010A", (kernel_ulong_t)&kxcj91008_kiox010a_info }, + /* KXCJ91008 in the base of a yoga 2-in-1 */ + { "KIOX020A", (kernel_ulong_t)&kxcj91008_kiox020a_info }, + { "KXCJ1008", (kernel_ulong_t)&kxcj91008_info }, + { "KXCJ1013", (kernel_ulong_t)&kxcjk1013_info }, + { "KXCJ9000", (kernel_ulong_t)&kxcj91008_info }, + { "KXJ2109", (kernel_ulong_t)&kxtj21009_info }, + { "KXTJ1009", (kernel_ulong_t)&kxtj21009_info }, + { "SMO8500", (kernel_ulong_t)&kxcj91008_smo8500_info }, + { } +}; +MODULE_DEVICE_TABLE(acpi, kx_acpi_match); + static struct i2c_driver kxcjk1013_driver = { .driver = { .name = KXCJK1013_DRV_NAME, - .acpi_match_table = ACPI_PTR(kx_acpi_match), + .acpi_match_table = kx_acpi_match, .of_match_table = kxcjk1013_of_match, - .pm = &kxcjk1013_pm_ops, + .pm = pm_ptr(&kxcjk1013_pm_ops), }, .probe = kxcjk1013_probe, .remove = kxcjk1013_remove, diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 70dfd6e354dbb..6d2b0a22e5508 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -215,7 +216,7 @@ static irqreturn_t kxsd9_trigger_handler(int irq, void *p) */ struct { __be16 chan[4]; - s64 ts __aligned(8); + aligned_s64 ts; } hw_values; int ret; diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index a34195b3215dd..50f7ac1845c69 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "mma7455.h" @@ -58,7 +59,7 @@ struct mma7455_data { */ struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 62e6369e22696..962d289065ab7 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -32,6 +32,7 @@ #include #include #include +#include #define MMA8452_STATUS 0x00 #define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) @@ -115,7 +116,7 @@ struct mma8452_data { /* Ensure correct alignment of time stamp when present */ struct { __be16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; }; @@ -973,7 +974,7 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct mma8452_data *data = iio_priv(indio_dev); int val, ret; diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index fa1799b0b0dff..6d73eec951263 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -4,11 +4,11 @@ * Copyright (c) 2014, Intel Corporation. */ -#include #include #include +#include +#include #include -#include #include #include #include @@ -45,7 +45,7 @@ enum mma9551_tilt_axis { struct mma9551_data { struct i2c_client *client; struct mutex mutex; - int event_enabled[3]; + bool event_enabled[3]; int irqs[MMA9551_GPIO_COUNT]; }; @@ -162,7 +162,7 @@ static int mma9551_read_event_config(struct iio_dev *indio_dev, static int mma9551_config_incli_event(struct iio_dev *indio_dev, enum iio_modifier axis, - int state) + bool state) { struct mma9551_data *data = iio_priv(indio_dev); enum mma9551_tilt_axis mma_axis; @@ -174,7 +174,7 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev, if (data->event_enabled[mma_axis] == state) return 0; - if (state == 0) { + if (!state) { ret = mma9551_gpio_config(data->client, (enum mma9551_gpio_pin)mma_axis, MMA9551_APPID_NONE, 0, 0); @@ -225,7 +225,7 @@ static int mma9551_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct mma9551_data *data = iio_priv(indio_dev); int ret; @@ -435,17 +435,6 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev) return 0; } -static const char *mma9551_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - return dev_name(dev); -} - static int mma9551_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -464,8 +453,8 @@ static int mma9551_probe(struct i2c_client *client) if (id) name = id->name; - else if (ACPI_HANDLE(&client->dev)) - name = mma9551_match_acpi_device(&client->dev); + else + name = iio_get_acpi_device_name(&client->dev); ret = mma9551_init(data); if (ret < 0) diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 86543f34ef17c..8536743a6886d 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -4,11 +4,11 @@ * Copyright (c) 2014, Intel Corporation. */ -#include #include #include +#include +#include #include -#include #include #include #include @@ -725,7 +725,8 @@ static int mma9553_read_event_config(struct iio_dev *indio_dev, static int mma9553_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { struct mma9553_data *data = iio_priv(indio_dev); struct mma9553_event *event; @@ -1030,9 +1031,9 @@ static irqreturn_t mma9553_event_handler(int irq, void *private) if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) { data->stepcnt = stepcnt; iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, - IIO_EV_DIR_NONE, - IIO_EV_TYPE_CHANGE, 0, 0, 0), + IIO_UNMOD_EVENT_CODE(IIO_STEPS, 0, + IIO_EV_TYPE_CHANGE, + IIO_EV_DIR_NONE), data->timestamp); } @@ -1041,20 +1042,18 @@ static irqreturn_t mma9553_event_handler(int irq, void *private) /* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */ if (ev_prev_activity && ev_prev_activity->enabled) iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_ACTIVITY, 0, - ev_prev_activity->info->mod, - IIO_EV_DIR_FALLING, - IIO_EV_TYPE_THRESH, 0, 0, - 0), + IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0, + ev_prev_activity->info->mod, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), data->timestamp); if (ev_activity && ev_activity->enabled) iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_ACTIVITY, 0, - ev_activity->info->mod, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, 0, 0, - 0), + IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0, + ev_activity->info->mod, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), data->timestamp); } mutex_unlock(&data->mutex); @@ -1062,17 +1061,6 @@ static irqreturn_t mma9553_event_handler(int irq, void *private) return IRQ_HANDLED; } -static const char *mma9553_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - return dev_name(dev); -} - static int mma9553_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -1091,9 +1079,9 @@ static int mma9553_probe(struct i2c_client *client) if (id) name = id->name; - else if (ACPI_HANDLE(&client->dev)) - name = mma9553_match_acpi_device(&client->dev); else + name = iio_get_acpi_device_name(&client->dev); + if (!name) return -ENOSYS; mutex_init(&data->mutex); diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index 57025354c7cd5..e7fb860f32337 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -893,7 +894,7 @@ static irqreturn_t msa311_buffer_thread(int irq, void *p) __le16 axis; struct { __le16 channels[MSA311_SI_Z + 1]; - s64 ts __aligned(8); + aligned_s64 ts; } buf; memset(&buf, 0, sizeof(buf)); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index fc54a2a4693c0..cb5c4e354fc04 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,7 @@ struct mxc4005_data { /* Ensure timestamp is naturally aligned */ struct { __be16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; bool trigger_enabled; unsigned int control; diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 87c54e41f6ccd..3fb0f386c3db6 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1158,7 +1158,7 @@ static int sca3000_read_event_config(struct iio_dev *indio_dev, return ret; } -static int sca3000_freefall_set_state(struct iio_dev *indio_dev, int state) +static int sca3000_freefall_set_state(struct iio_dev *indio_dev, bool state) { struct sca3000_state *st = iio_priv(indio_dev); int ret; @@ -1181,7 +1181,7 @@ static int sca3000_freefall_set_state(struct iio_dev *indio_dev, int state) } static int sca3000_motion_detect_set_state(struct iio_dev *indio_dev, int axis, - int state) + bool state) { struct sca3000_state *st = iio_priv(indio_dev); int ret, ctrlval; @@ -1253,7 +1253,7 @@ static int sca3000_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct sca3000_state *st = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index abead190254b5..471c154c3631b 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -105,7 +106,7 @@ struct stk8312_data { /* Ensure timestamp is naturally aligned */ struct { s8 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index a32a77324e92d..cab592a686225 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -94,7 +95,7 @@ struct stk8ba50_data { /* Ensure timestamp is naturally aligned */ struct { s16 chans[3]; - s64 timetamp __aligned(8); + aligned_s64 timetamp; } scan; }; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 6c4e74420fd25..849c90203071a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -226,12 +226,14 @@ config AD7606_IFACE_PARALLEL tristate "Analog Devices AD7606 ADC driver with parallel interface support" depends on HAS_IOPORT select AD7606 + select IIO_BACKEND help Say yes here to build parallel interface support for Analog Devices: ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + It also support iio_backended devices for AD7606B. To compile this driver as a module, choose M here: the - module will be called ad7606_parallel. + module will be called ad7606_par. config AD7606_IFACE_SPI tristate "Analog Devices AD7606 ADC driver with spi interface support" @@ -244,6 +246,22 @@ config AD7606_IFACE_SPI To compile this driver as a module, choose M here: the module will be called ad7606_spi. +config AD7625 + tristate "Analog Devices AD7625/AD7626 High Speed ADC driver" + depends on PWM + select IIO_BACKEND + help + Say yes here to build support for Analog Devices: + * AD7625 16-Bit, 6 MSPS PulSAR Analog-to-Digital Converter + * AD7626 16-Bit, 10 MSPS PulSAR Analog-to-Digital Converter + * AD7960 18-Bit, 5 MSPS PulSAR Analog-to-Digital Converter + * AD7961 16-Bit, 5 MSPS PulSAR Analog-to-Digital Converter + + The driver requires the assistance of the AXI ADC IP core to operate. + + To compile this driver as a module, choose M here: the module will be + called ad7625. + config AD7766 tristate "Analog Devices AD7766/AD7767 ADC driver" depends on SPI_MASTER @@ -269,6 +287,18 @@ config AD7768_1 To compile this driver as a module, choose M here: the module will be called ad7768-1. +config AD7779 + tristate "Analog Devices AD7779 ADC driver" + depends on SPI + select CRC8 + select IIO_BUFFER + help + Say yes here to build support for Analog Devices AD777X family + (AD7770, AD7771, AD7779) analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7779. + config AD7780 tristate "Analog Devices AD7780 and similar ADCs driver" depends on SPI @@ -575,6 +605,16 @@ config FSL_MX25_ADC Generic Conversion Queue driver used for general purpose ADC in the MX25. This driver supports single measurements using the MX25 ADC. +config GEHC_PMC_ADC + tristate "GE HealthCare PMC ADC driver" + depends on I2C + help + Say yes here to build support for the GE HealthCare PMC 16-bit + 16-Channel ADC. + + To compile this driver as a module, choose M here: the module will be + called gehc-pmc-adc. + config HI8435 tristate "Holt Integrated Circuits HI-8435 threshold detector" select IIO_TRIGGERED_EVENT @@ -1580,7 +1620,6 @@ config TWL4030_MADC config TWL6030_GPADC tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" depends on TWL4030_CORE - default n help Say yes here if you want support for the TWL6030/TWL6032 General Purpose A/D Converter. This will add support for battery type diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 7b91cd98c0e0b..ee19afba62b7f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -25,8 +25,10 @@ obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o obj-$(CONFIG_AD7606) += ad7606.o +obj-$(CONFIG_AD7625) += ad7625.o obj-$(CONFIG_AD7766) += ad7766.o obj-$(CONFIG_AD7768_1) += ad7768-1.o +obj-$(CONFIG_AD7779) += ad7779.o obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o @@ -52,6 +54,7 @@ obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o +obj-$(CONFIG_GEHC_PMC_ADC) += gehc-pmc-adc.o obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 59f66e9cb0e86..f3b057f923100 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -1194,7 +1194,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops, static struct platform_driver ab8500_gpadc_driver = { .probe = ab8500_gpadc_probe, - .remove_new = ab8500_gpadc_remove, + .remove = ab8500_gpadc_remove, .driver = { .name = "ab8500-gpadc", .pm = pm_ptr(&ab8500_gpadc_pm_ops), diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index 6ea4912450849..b3b82535f5c14 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -344,6 +344,8 @@ static int ad4000_single_conversion(struct iio_dev *indio_dev, if (chan->scan_type.sign == 's') *val = sign_extend32(sample, chan->scan_type.realbits - 1); + else + *val = sample; return IIO_VAL_INT; } @@ -637,7 +639,9 @@ static int ad4000_probe(struct spi_device *spi) indio_dev->name = chip->dev_name; indio_dev->num_channels = 1; - devm_mutex_init(dev, &st->lock); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; st->gain_milli = 1000; if (chip->has_hardware_gain) { diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index d6876259ad144..eb0a059b4b0e9 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -150,7 +150,8 @@ static int ad7091r_read_event_config(struct iio_dev *indio_dev, static int ad7091r_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { struct ad7091r_state *st = iio_priv(indio_dev); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index 696bf7a897bb5..092ddea0f395c 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -65,7 +65,7 @@ struct ad7091r_state { struct regulator *vref; const struct ad7091r_chip_info *chip_info; enum ad7091r_mode mode; - struct mutex lock; /*lock to prevent concurent reads */ + struct mutex lock; /*lock to prevent concurrent reads */ __be16 tx_buf __aligned(IIO_DMA_MINALIGN); __be16 rx_buf; }; diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 0702ec71aa293..a0fca16c3be07 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -3,7 +3,7 @@ * AD717x and AD411x family SPI ADC driver * * Supported devices: - * AD4111/AD4112/AD4114/AD4115/AD4116 + * AD4111/AD4112/AD4113/AD4114/AD4115/AD4116 * AD7172-2/AD7172-4/AD7173-8/AD7175-2 * AD7175-8/AD7176-2/AD7177-2 * @@ -76,14 +76,15 @@ (x) == AD7173_AIN_REF_NEG) #define AD7172_2_ID 0x00d0 -#define AD7175_ID 0x0cd0 #define AD7176_ID 0x0c90 +#define AD7175_ID 0x0cd0 #define AD7175_2_ID 0x0cd0 #define AD7172_4_ID 0x2050 #define AD7173_ID 0x30d0 #define AD4111_ID AD7173_ID #define AD4112_ID AD7173_ID #define AD4114_ID AD7173_ID +#define AD4113_ID 0x31d0 #define AD4116_ID 0x34d0 #define AD4115_ID 0x38d0 #define AD7175_8_ID 0x3cd0 @@ -170,6 +171,7 @@ struct ad7173_device_info { bool has_temp; /* ((AVDD1 − AVSS)/5) */ bool has_pow_supply_monitoring; + bool data_reg_only_16bit; bool has_input_buf; bool has_int_ref; bool has_ref2; @@ -294,6 +296,24 @@ static const struct ad7173_device_info ad4112_device_info = { .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), }; +static const struct ad7173_device_info ad4113_device_info = { + .name = "ad4113", + .id = AD4113_ID, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .data_reg_only_16bit = true, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + static const struct ad7173_device_info ad4114_device_info = { .name = "ad4114", .id = AD4114_ID, @@ -985,6 +1005,13 @@ static const struct iio_info ad7173_info = { .update_scan_mode = ad7173_update_scan_mode, }; +static const struct iio_scan_type ad4113_scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, +}; + static const struct iio_chan_spec ad7173_channel_template = { .type = IIO_VOLTAGE, .indexed = 1, @@ -1226,6 +1253,8 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; st->adc_mode |= AD7173_ADC_MODE_REF_EN; + if (st->info->data_reg_only_16bit) + chan_arr[chan_index].scan_type = ad4113_scan_type; chan_index++; } @@ -1306,6 +1335,9 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]); } + if (st->info->data_reg_only_16bit) + chan_arr[chan_index].scan_type = ad4113_scan_type; + chan_index++; } return 0; @@ -1434,6 +1466,7 @@ static int ad7173_probe(struct spi_device *spi) static const struct of_device_id ad7173_of_match[] = { { .compatible = "adi,ad4111", .data = &ad4111_device_info }, { .compatible = "adi,ad4112", .data = &ad4112_device_info }, + { .compatible = "adi,ad4113", .data = &ad4113_device_info }, { .compatible = "adi,ad4114", .data = &ad4114_device_info }, { .compatible = "adi,ad4115", .data = &ad4115_device_info }, { .compatible = "adi,ad4116", .data = &ad4116_device_info }, @@ -1451,6 +1484,7 @@ MODULE_DEVICE_TABLE(of, ad7173_of_match); static const struct spi_device_id ad7173_id_table[] = { { "ad4111", (kernel_ulong_t)&ad4111_device_info }, { "ad4112", (kernel_ulong_t)&ad4112_device_info }, + { "ad4113", (kernel_ulong_t)&ad4113_device_info }, { "ad4114", (kernel_ulong_t)&ad4114_device_info }, { "ad4115", (kernel_ulong_t)&ad4115_device_info }, { "ad4116", (kernel_ulong_t)&ad4116_device_info }, diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 7042ddfdfc03e..955e9eff0099e 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1394,6 +1394,9 @@ static int ad7192_probe(struct spi_device *spi) st->int_vref_mv = ret == -ENODEV ? avdd_mv : ret / MILLI; st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = st->chip_info->info; diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 7949b076fb87e..858c8be2ff1a0 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -383,7 +383,7 @@ static const char * const ad7266_gpio_labels[] = { static int ad7266_probe(struct spi_device *spi) { - struct ad7266_platform_data *pdata = spi->dev.platform_data; + const struct ad7266_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; struct ad7266_state *st; unsigned int i; diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index 35aa39fe4bde6..f9f32737db807 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -822,17 +822,15 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, channels[i]) <= AD7280A_CELL_VOLTAGE_6_REG) { if (val >= st->cell_threshhigh) { - u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, - 0, 0, 0); + u64 tmp = IIO_DIFF_EVENT_CODE(IIO_VOLTAGE, 0, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); iio_push_event(indio_dev, tmp, iio_get_time_ns(indio_dev)); } else if (val <= st->cell_threshlow) { - u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0, - IIO_EV_DIR_FALLING, - IIO_EV_TYPE_THRESH, - 0, 0, 0); + u64 tmp = IIO_DIFF_EVENT_CODE(IIO_VOLTAGE, 0, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); iio_push_event(indio_dev, tmp, iio_get_time_ns(indio_dev)); } diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index 4c7f887adbbf2..60e12faa32070 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -269,7 +269,7 @@ static int ad7291_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { int ret = 0; struct ad7291_chip_info *chip = iio_priv(indio_dev); diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index fb728570debe6..4f32cb22f1404 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -13,6 +13,8 @@ * ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf * ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf + * adaq4370-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4370-4.pdf + * adaq4380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4380-4.pdf */ #include @@ -22,11 +24,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include @@ -36,6 +41,8 @@ #define MAX_NUM_CHANNELS 8 /* 2.5V internal reference voltage */ #define AD7380_INTERNAL_REF_MV 2500 +/* 3.3V internal reference voltage for ADAQ */ +#define ADAQ4380_INTERNAL_REF_MV 3300 /* reading and writing registers is more reliable at lower than max speed */ #define AD7380_REG_WR_SPEED_HZ 10000000 @@ -77,6 +84,13 @@ #define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */ #define T_POWERUP_US 5000 /* Power up */ +/* + * AD738x support several SDO lines to increase throughput, but driver currently + * supports only 1 SDO line (standard SPI transaction) + */ +#define AD7380_NUM_SDO_LINES 1 +#define AD7380_DEFAULT_GAIN_MILLI 1000 + struct ad7380_timing_specs { const unsigned int t_csh_ns; /* CS minimum high time */ }; @@ -86,10 +100,12 @@ struct ad7380_chip_info { const struct iio_chan_spec *channels; unsigned int num_channels; unsigned int num_simult_channels; + bool has_hardware_gain; bool has_mux; const char * const *supplies; unsigned int num_supplies; bool external_ref_only; + bool adaq_internal_ref_only; const char * const *vcm_supplies; unsigned int num_vcm_supplies; const unsigned long *available_scan_masks; @@ -181,11 +197,12 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = { }, }; -#define AD7380_CHANNEL(index, bits, diff, sign) { \ +#define _AD7380_CHANNEL(index, bits, diff, sign, gain) { \ .type = IIO_VOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + ((gain) ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + .info_mask_shared_by_type = ((gain) ? 0 : BIT(IIO_CHAN_INFO_SCALE)) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_type_available = \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ @@ -199,6 +216,12 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = { .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \ } +#define AD7380_CHANNEL(index, bits, diff, sign) \ + _AD7380_CHANNEL(index, bits, diff, sign, false) + +#define ADAQ4380_CHANNEL(index, bits, diff, sign) \ + _AD7380_CHANNEL(index, bits, diff, sign, true) + #define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ AD7380_CHANNEL(0, bits, diff, sign), \ @@ -215,6 +238,15 @@ static const struct iio_chan_spec name[] = { \ IIO_CHAN_SOFT_TIMESTAMP(4), \ } +#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + ADAQ4380_CHANNEL(0, bits, diff, sign), \ + ADAQ4380_CHANNEL(1, bits, diff, sign), \ + ADAQ4380_CHANNEL(2, bits, diff, sign), \ + ADAQ4380_CHANNEL(3, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + #define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ AD7380_CHANNEL(0, bits, diff, sign), \ @@ -233,6 +265,7 @@ DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s); DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s); DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s); DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s); +DEFINE_ADAQ4380_4_CHANNEL(adaq4380_4_channels, 16, 1, s); /* pseudo differential */ DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s); DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s); @@ -251,6 +284,10 @@ static const char * const ad7380_supplies[] = { "vcc", "vlogic", }; +static const char * const adaq4380_supplies[] = { + "ldo", "vcc", "vlogic", "vs-p", "vs-n", "refin", +}; + static const char * const ad7380_2_channel_vcm_supplies[] = { "aina", "ainb", }; @@ -341,6 +378,11 @@ static const int ad7380_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32, }; +/* Gains stored as fractions of 1000 so they can be expressed by integers. */ +static const int ad7380_gains[] = { + 300, 600, 1000, 1600, +}; + static const struct ad7380_chip_info ad7380_chip_info = { .name = "ad7380", .channels = ad7380_channels, @@ -510,6 +552,32 @@ static const struct ad7380_chip_info ad7388_4_chip_info = { .timing_specs = &ad7380_4_timing, }; +static const struct ad7380_chip_info adaq4370_4_chip_info = { + .name = "adaq4370-4", + .channels = adaq4380_4_channels, + .num_channels = ARRAY_SIZE(adaq4380_4_channels), + .num_simult_channels = 4, + .supplies = adaq4380_supplies, + .num_supplies = ARRAY_SIZE(adaq4380_supplies), + .adaq_internal_ref_only = true, + .has_hardware_gain = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, +}; + +static const struct ad7380_chip_info adaq4380_4_chip_info = { + .name = "adaq4380-4", + .channels = adaq4380_4_channels, + .num_channels = ARRAY_SIZE(adaq4380_4_channels), + .num_simult_channels = 4, + .supplies = adaq4380_supplies, + .num_supplies = ARRAY_SIZE(adaq4380_supplies), + .adaq_internal_ref_only = true, + .has_hardware_gain = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, +}; + struct ad7380_state { const struct ad7380_chip_info *chip_info; struct spi_device *spi; @@ -520,6 +588,7 @@ struct ad7380_state { bool seq; unsigned int vref_mv; unsigned int vcm_mv[MAX_NUM_CHANNELS]; + unsigned int gain_milli[MAX_NUM_CHANNELS]; /* xfers, message an buffer for reading sample data */ struct spi_transfer normal_xfer[2]; struct spi_message normal_msg; @@ -649,7 +718,8 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) if (st->oversampling_ratio > 1) xfer.delay.value = T_CONVERT_0_NS + - T_CONVERT_X_NS * (st->oversampling_ratio - 1); + T_CONVERT_X_NS * (st->oversampling_ratio - 1) * + st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; return spi_sync_transfer(st->spi, &xfer, 1); } @@ -672,7 +742,8 @@ static void ad7380_update_xfers(struct ad7380_state *st, */ if (st->oversampling_ratio > 1) t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS * - (st->oversampling_ratio - 1); + (st->oversampling_ratio - 1) * + st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; if (st->seq) { xfer[0].delay.value = xfer[1].delay.value = t_convert; @@ -868,8 +939,15 @@ static int ad7380_read_raw(struct iio_dev *indio_dev, * * (2 × VREF) / 2^N, for differential chips * * VREF / 2^N, for pseudo-differential chips * where N is the ADC resolution (i.e realbits) + * + * The gain is stored as a fraction of 1000 and, as we need to + * divide vref_mv by the gain, we invert the gain/1000 fraction. */ - *val = st->vref_mv; + if (st->chip_info->has_hardware_gain) + *val = mult_frac(st->vref_mv, MILLI, + st->gain_milli[chan->scan_index]); + else + *val = st->vref_mv; *val2 = scan_type->realbits - chan->differential; return IIO_VAL_FRACTIONAL_LOG2; @@ -1021,17 +1099,19 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en) /* SPI 1-wire mode */ return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2, AD7380_CONFIG2_SDO, - FIELD_PREP(AD7380_CONFIG2_SDO, 1)); + FIELD_PREP(AD7380_CONFIG2_SDO, + AD7380_NUM_SDO_LINES)); } static int ad7380_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct ad7380_state *st; bool external_ref_en; int ret, i; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1039,21 +1119,32 @@ static int ad7380_probe(struct spi_device *spi) st->spi = spi; st->chip_info = spi_get_device_match_data(spi); if (!st->chip_info) - return dev_err_probe(&spi->dev, -EINVAL, "missing match data\n"); + return dev_err_probe(dev, -EINVAL, "missing match data\n"); - ret = devm_regulator_bulk_get_enable(&spi->dev, st->chip_info->num_supplies, + ret = devm_regulator_bulk_get_enable(dev, st->chip_info->num_supplies, st->chip_info->supplies); if (ret) - return dev_err_probe(&spi->dev, ret, + return dev_err_probe(dev, ret, "Failed to enable power supplies\n"); fsleep(T_POWERUP_US); - if (st->chip_info->external_ref_only) { - ret = devm_regulator_get_enable_read_voltage(&spi->dev, - "refin"); + if (st->chip_info->adaq_internal_ref_only) { + /* + * ADAQ chips use fixed internal reference but still + * require a specific reference supply to power it. + * "refin" is already enabled with other power supplies + * in bulk_get_enable(). + */ + + st->vref_mv = ADAQ4380_INTERNAL_REF_MV; + + /* these chips don't have a register bit for this */ + external_ref_en = false; + } else if (st->chip_info->external_ref_only) { + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); if (ret < 0) - return dev_err_probe(&spi->dev, ret, + return dev_err_probe(dev, ret, "Failed to get refin regulator\n"); st->vref_mv = ret / 1000; @@ -1065,10 +1156,9 @@ static int ad7380_probe(struct spi_device *spi) * If there is no REFIO supply, then it means that we are using * the internal reference, otherwise REFIO is reference voltage. */ - ret = devm_regulator_get_enable_read_voltage(&spi->dev, - "refio"); + ret = devm_regulator_get_enable_read_voltage(dev, "refio"); if (ret < 0 && ret != -ENODEV) - return dev_err_probe(&spi->dev, ret, + return dev_err_probe(dev, ret, "Failed to get refio regulator\n"); external_ref_en = ret != -ENODEV; @@ -1076,7 +1166,7 @@ static int ad7380_probe(struct spi_device *spi) } if (st->chip_info->num_vcm_supplies > ARRAY_SIZE(st->vcm_mv)) - return dev_err_probe(&spi->dev, -EINVAL, + return dev_err_probe(dev, -EINVAL, "invalid number of VCM supplies\n"); /* @@ -1086,18 +1176,54 @@ static int ad7380_probe(struct spi_device *spi) for (i = 0; i < st->chip_info->num_vcm_supplies; i++) { const char *vcm = st->chip_info->vcm_supplies[i]; - ret = devm_regulator_get_enable_read_voltage(&spi->dev, vcm); + ret = devm_regulator_get_enable_read_voltage(dev, vcm); if (ret < 0) - return dev_err_probe(&spi->dev, ret, + return dev_err_probe(dev, ret, "Failed to get %s regulator\n", vcm); st->vcm_mv[i] = ret / 1000; } - st->regmap = devm_regmap_init(&spi->dev, NULL, st, &ad7380_regmap_config); + for (i = 0; i < MAX_NUM_CHANNELS; i++) + st->gain_milli[i] = AD7380_DEFAULT_GAIN_MILLI; + + if (st->chip_info->has_hardware_gain) { + device_for_each_child_node_scoped(dev, node) { + unsigned int channel, gain; + int gain_idx; + + ret = fwnode_property_read_u32(node, "reg", &channel); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property\n"); + + if (channel >= st->chip_info->num_channels - 1) + return dev_err_probe(dev, -EINVAL, + "Invalid channel number %i\n", + channel); + + ret = fwnode_property_read_u32(node, "adi,gain-milli", + &gain); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, + "Failed to read gain for channel %i\n", + channel); + if (ret != -EINVAL) { + /* + * Match gain value from dt to one of supported + * gains + */ + gain_idx = find_closest(gain, ad7380_gains, + ARRAY_SIZE(ad7380_gains)); + st->gain_milli[channel] = ad7380_gains[gain_idx]; + } + } + } + + st->regmap = devm_regmap_init(dev, NULL, st, &ad7380_regmap_config); if (IS_ERR(st->regmap)) - return dev_err_probe(&spi->dev, PTR_ERR(st->regmap), + return dev_err_probe(dev, PTR_ERR(st->regmap), "failed to allocate register map\n"); /* @@ -1148,7 +1274,7 @@ static int ad7380_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->available_scan_masks = st->chip_info->available_scan_masks; - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, ad7380_trigger_handler, &ad7380_buffer_setup_ops); @@ -1159,7 +1285,7 @@ static int ad7380_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id ad7380_of_match_table[] = { @@ -1177,6 +1303,8 @@ static const struct of_device_id ad7380_of_match_table[] = { { .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info }, { .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info }, { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info }, + { .compatible = "adi,adaq4370-4", .data = &adaq4370_4_chip_info }, + { .compatible = "adi,adaq4380-4", .data = &adaq4380_4_chip_info }, { } }; @@ -1195,6 +1323,8 @@ static const struct spi_device_id ad7380_id_table[] = { { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info }, { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info }, { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info }, + { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info }, + { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7380_id_table); diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 9b457472d49c1..8b2046baaa3ec 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -13,14 +13,17 @@ #include #include #include +#include #include #include #include #include +#include #include -#include +#include #include +#include #include #include #include @@ -30,15 +33,52 @@ /* * Scales are computed as 5000/32768 and 10000/32768 respectively, - * so that when applied to the raw values they provide mV values + * so that when applied to the raw values they provide mV values. + * The scale arrays are kept as IIO_VAL_INT_PLUS_MICRO, so index + * X is the integer part and X + 1 is the fractional part. */ -static const unsigned int ad7606_scale_avail[2] = { - 152588, 305176 +static const unsigned int ad7606_16bit_hw_scale_avail[2][2] = { + { 0, 152588 }, { 0, 305176 } +}; + +static const unsigned int ad7606_18bit_hw_scale_avail[2][2] = { + { 0, 38147 }, { 0, 76294 } +}; + +static const unsigned int ad7606c_16bit_single_ended_unipolar_scale_avail[3][2] = { + { 0, 76294 }, { 0, 152588 }, { 0, 190735 } +}; + +static const unsigned int ad7606c_16bit_single_ended_bipolar_scale_avail[5][2] = { + { 0, 76294 }, { 0, 152588 }, { 0, 190735 }, { 0, 305176 }, { 0, 381470 } +}; + +static const unsigned int ad7606c_16bit_differential_bipolar_scale_avail[4][2] = { + { 0, 152588 }, { 0, 305176 }, { 0, 381470 }, { 0, 610352 } +}; + +static const unsigned int ad7606c_18bit_single_ended_unipolar_scale_avail[3][2] = { + { 0, 19073 }, { 0, 38147 }, { 0, 47684 } +}; + +static const unsigned int ad7606c_18bit_single_ended_bipolar_scale_avail[5][2] = { + { 0, 19073 }, { 0, 38147 }, { 0, 47684 }, { 0, 76294 }, { 0, 95367 } }; +static const unsigned int ad7606c_18bit_differential_bipolar_scale_avail[4][2] = { + { 0, 38147 }, { 0, 76294 }, { 0, 95367 }, { 0, 152588 } +}; + +static const unsigned int ad7606_16bit_sw_scale_avail[3][2] = { + { 0, 76293 }, { 0, 152588 }, { 0, 305176 } +}; -static const unsigned int ad7616_sw_scale_avail[3] = { - 76293, 152588, 305176 +static const unsigned int ad7607_hw_scale_avail[2][2] = { + { 0, 610352 }, { 1, 220703 } +}; + +static const unsigned int ad7609_hw_scale_avail[2][2] = { + { 0, 152588 }, { 0, 305176 } }; static const unsigned int ad7606_oversampling_avail[7] = { @@ -49,6 +89,227 @@ static const unsigned int ad7616_oversampling_avail[8] = { 1, 2, 4, 8, 16, 32, 64, 128, }; +static const struct iio_chan_spec ad7605_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(4), + AD7605_CHANNEL(0), + AD7605_CHANNEL(1), + AD7605_CHANNEL(2), + AD7605_CHANNEL(3), +}; + +static const struct iio_chan_spec ad7606_channels_16bit[] = { + IIO_CHAN_SOFT_TIMESTAMP(8), + AD7606_CHANNEL(0, 16), + AD7606_CHANNEL(1, 16), + AD7606_CHANNEL(2, 16), + AD7606_CHANNEL(3, 16), + AD7606_CHANNEL(4, 16), + AD7606_CHANNEL(5, 16), + AD7606_CHANNEL(6, 16), + AD7606_CHANNEL(7, 16), +}; + +static const struct iio_chan_spec ad7606_channels_18bit[] = { + IIO_CHAN_SOFT_TIMESTAMP(8), + AD7606_CHANNEL(0, 18), + AD7606_CHANNEL(1, 18), + AD7606_CHANNEL(2, 18), + AD7606_CHANNEL(3, 18), + AD7606_CHANNEL(4, 18), + AD7606_CHANNEL(5, 18), + AD7606_CHANNEL(6, 18), + AD7606_CHANNEL(7, 18), +}; + +static const struct iio_chan_spec ad7607_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(8), + AD7606_CHANNEL(0, 14), + AD7606_CHANNEL(1, 14), + AD7606_CHANNEL(2, 14), + AD7606_CHANNEL(3, 14), + AD7606_CHANNEL(4, 14), + AD7606_CHANNEL(5, 14), + AD7606_CHANNEL(6, 14), + AD7606_CHANNEL(7, 14), +}; + +static const struct iio_chan_spec ad7608_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(8), + AD7606_CHANNEL(0, 18), + AD7606_CHANNEL(1, 18), + AD7606_CHANNEL(2, 18), + AD7606_CHANNEL(3, 18), + AD7606_CHANNEL(4, 18), + AD7606_CHANNEL(5, 18), + AD7606_CHANNEL(6, 18), + AD7606_CHANNEL(7, 18), +}; + +/* + * The current assumption that this driver makes for AD7616, is that it's + * working in Hardware Mode with Serial, Burst and Sequencer modes activated. + * To activate them, following pins must be pulled high: + * -SER/PAR + * -SEQEN + * And following pins must be pulled low: + * -WR/BURST + * -DB4/SER1W + */ +static const struct iio_chan_spec ad7616_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(16), + AD7606_CHANNEL(0, 16), + AD7606_CHANNEL(1, 16), + AD7606_CHANNEL(2, 16), + AD7606_CHANNEL(3, 16), + AD7606_CHANNEL(4, 16), + AD7606_CHANNEL(5, 16), + AD7606_CHANNEL(6, 16), + AD7606_CHANNEL(7, 16), + AD7606_CHANNEL(8, 16), + AD7606_CHANNEL(9, 16), + AD7606_CHANNEL(10, 16), + AD7606_CHANNEL(11, 16), + AD7606_CHANNEL(12, 16), + AD7606_CHANNEL(13, 16), + AD7606_CHANNEL(14, 16), + AD7606_CHANNEL(15, 16), +}; + +static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); +static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); +static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); +static int ad7607_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); +static int ad7608_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); +static int ad7609_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); + +const struct ad7606_chip_info ad7605_4_info = { + .channels = ad7605_channels, + .name = "ad7605-4", + .num_adc_channels = 4, + .num_channels = 5, + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7605_4_info, IIO_AD7606); + +const struct ad7606_chip_info ad7606_8_info = { + .channels = ad7606_channels_16bit, + .name = "ad7606-8", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606_8_info, IIO_AD7606); + +const struct ad7606_chip_info ad7606_6_info = { + .channels = ad7606_channels_16bit, + .name = "ad7606-6", + .num_adc_channels = 6, + .num_channels = 7, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606_6_info, IIO_AD7606); + +const struct ad7606_chip_info ad7606_4_info = { + .channels = ad7606_channels_16bit, + .name = "ad7606-4", + .num_adc_channels = 4, + .num_channels = 5, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606_4_info, IIO_AD7606); + +const struct ad7606_chip_info ad7606b_info = { + .channels = ad7606_channels_16bit, + .max_samplerate = 800 * KILO, + .name = "ad7606b", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606b_info, IIO_AD7606); + +const struct ad7606_chip_info ad7606c_16_info = { + .channels = ad7606_channels_16bit, + .name = "ad7606c16", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606c_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, IIO_AD7606); + +const struct ad7606_chip_info ad7607_info = { + .channels = ad7607_channels, + .name = "ad7607", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7607_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7607_info, IIO_AD7606); + +const struct ad7606_chip_info ad7608_info = { + .channels = ad7608_channels, + .name = "ad7608", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7608_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7608_info, IIO_AD7606); + +const struct ad7606_chip_info ad7609_info = { + .channels = ad7608_channels, + .name = "ad7609", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7609_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7609_info, IIO_AD7606); + +const struct ad7606_chip_info ad7606c_18_info = { + .channels = ad7606_channels_18bit, + .name = "ad7606c18", + .num_adc_channels = 8, + .num_channels = 9, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606c_18bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, IIO_AD7606); + +const struct ad7606_chip_info ad7616_info = { + .channels = ad7616_channels, + .init_delay_ms = 15, + .name = "ad7616", + .num_adc_channels = 16, + .num_channels = 17, + .oversampling_avail = ad7616_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail), + .os_req_reset = true, + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7616_info, IIO_AD7606); + int ad7606_reset(struct ad7606_state *st) { if (st->gpio_reset) { @@ -62,6 +323,228 @@ int ad7606_reset(struct ad7606_state *st) } EXPORT_SYMBOL_NS_GPL(ad7606_reset, IIO_AD7606); +static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch) +{ + struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + + if (!st->sw_mode_en) { + /* tied to logic low, analog input range is +/- 5V */ + cs->range = 0; + cs->scale_avail = ad7606_16bit_hw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); + return 0; + } + + /* Scale of 0.076293 is only available in sw mode */ + /* After reset, in software mode, ±10 V is set by default */ + cs->range = 2; + cs->scale_avail = ad7606_16bit_sw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail); + + return 0; +} + +static int ad7606_get_chan_config(struct ad7606_state *st, int ch, + bool *bipolar, bool *differential) +{ + unsigned int num_channels = st->chip_info->num_channels - 1; + struct device *dev = st->dev; + int ret; + + *bipolar = false; + *differential = false; + + device_for_each_child_node_scoped(dev, child) { + u32 pins[2]; + int reg; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + continue; + + /* channel number (here) is from 1 to num_channels */ + if (reg == 0 || reg > num_channels) { + dev_warn(dev, + "Invalid channel number (ignoring): %d\n", reg); + continue; + } + + if (reg != (ch + 1)) + continue; + + *bipolar = fwnode_property_read_bool(child, "bipolar"); + + ret = fwnode_property_read_u32_array(child, "diff-channels", + pins, ARRAY_SIZE(pins)); + /* Channel is differential, if pins are the same as 'reg' */ + if (ret == 0 && (pins[0] != reg || pins[1] != reg)) { + dev_err(dev, + "Differential pins must be the same as 'reg'"); + return -EINVAL; + } + + *differential = (ret == 0); + + if (*differential && !*bipolar) { + dev_err(dev, + "'bipolar' must be added for diff channel %d\n", + reg); + return -EINVAL; + } + + return 0; + } + + return 0; +} + +static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch) +{ + struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + bool bipolar, differential; + int ret; + + if (!st->sw_mode_en) { + cs->range = 0; + cs->scale_avail = ad7606_18bit_hw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); + return 0; + } + + ret = ad7606_get_chan_config(st, ch, &bipolar, &differential); + if (ret) + return ret; + + if (differential) { + cs->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail; + cs->num_scales = + ARRAY_SIZE(ad7606c_18bit_differential_bipolar_scale_avail); + /* Bipolar differential ranges start at 8 (b1000) */ + cs->reg_offset = 8; + cs->range = 1; + chan->differential = 1; + chan->channel2 = chan->channel; + + return 0; + } + + chan->differential = 0; + + if (bipolar) { + cs->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail; + cs->num_scales = + ARRAY_SIZE(ad7606c_18bit_single_ended_bipolar_scale_avail); + /* Bipolar single-ended ranges start at 0 (b0000) */ + cs->reg_offset = 0; + cs->range = 3; + chan->scan_type.sign = 's'; + + return 0; + } + + cs->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail; + cs->num_scales = + ARRAY_SIZE(ad7606c_18bit_single_ended_unipolar_scale_avail); + /* Unipolar single-ended ranges start at 5 (b0101) */ + cs->reg_offset = 5; + cs->range = 1; + chan->scan_type.sign = 'u'; + + return 0; +} + +static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch) +{ + struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + bool bipolar, differential; + int ret; + + if (!st->sw_mode_en) { + cs->range = 0; + cs->scale_avail = ad7606_16bit_hw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); + return 0; + } + + ret = ad7606_get_chan_config(st, ch, &bipolar, &differential); + if (ret) + return ret; + + if (differential) { + cs->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail; + cs->num_scales = + ARRAY_SIZE(ad7606c_16bit_differential_bipolar_scale_avail); + /* Bipolar differential ranges start at 8 (b1000) */ + cs->reg_offset = 8; + cs->range = 1; + chan->differential = 1; + chan->channel2 = chan->channel; + chan->scan_type.sign = 's'; + + return 0; + } + + chan->differential = 0; + + if (bipolar) { + cs->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail; + cs->num_scales = + ARRAY_SIZE(ad7606c_16bit_single_ended_bipolar_scale_avail); + /* Bipolar single-ended ranges start at 0 (b0000) */ + cs->reg_offset = 0; + cs->range = 3; + chan->scan_type.sign = 's'; + + return 0; + } + + cs->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail; + cs->num_scales = + ARRAY_SIZE(ad7606c_16bit_single_ended_unipolar_scale_avail); + /* Unipolar single-ended ranges start at 5 (b0101) */ + cs->reg_offset = 5; + cs->range = 1; + chan->scan_type.sign = 'u'; + + return 0; +} + +static int ad7607_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch) +{ + struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + + cs->range = 0; + cs->scale_avail = ad7607_hw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail); + return 0; +} + +static int ad7608_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch) +{ + struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + + cs->range = 0; + cs->scale_avail = ad7606_18bit_hw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); + return 0; +} + +static int ad7609_chan_scale_setup(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch) +{ + struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + + cs->range = 0; + cs->scale_avail = ad7609_hw_scale_avail; + cs->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail); + return 0; +} + static int ad7606_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, @@ -83,12 +566,85 @@ static int ad7606_reg_access(struct iio_dev *indio_dev, } } +static int ad7606_pwm_set_high(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + int ret; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + cnvst_pwm_state.enabled = true; + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period; + + ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); + + return ret; +} + +static int ad7606_pwm_set_low(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + int ret; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + cnvst_pwm_state.enabled = true; + cnvst_pwm_state.duty_cycle = 0; + + ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); + + return ret; +} + +static int ad7606_pwm_set_swing(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + cnvst_pwm_state.enabled = true; + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2; + + return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); +} + +static bool ad7606_pwm_is_swinging(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + + return cnvst_pwm_state.duty_cycle != cnvst_pwm_state.period && + cnvst_pwm_state.duty_cycle != 0; +} + +static int ad7606_set_sampling_freq(struct ad7606_state *st, unsigned long freq) +{ + struct pwm_state cnvst_pwm_state; + bool is_swinging = ad7606_pwm_is_swinging(st); + bool is_high; + + if (freq == 0) + return -EINVAL; + + /* Retrieve the previous state. */ + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + is_high = cnvst_pwm_state.duty_cycle == cnvst_pwm_state.period; + + cnvst_pwm_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq); + cnvst_pwm_state.polarity = PWM_POLARITY_NORMAL; + if (is_high) + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period; + else if (is_swinging) + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2; + else + cnvst_pwm_state.duty_cycle = 0; + + return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); +} + static int ad7606_read_samples(struct ad7606_state *st) { - unsigned int num = st->chip_info->num_channels - 1; - u16 *data = st->data; + unsigned int num = st->chip_info->num_adc_channels; - return st->bops->read_block(st->dev, num, data); + return st->bops->read_block(st->dev, num, &st->data); } static irqreturn_t ad7606_trigger_handler(int irq, void *p) @@ -104,7 +660,7 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p) if (ret) goto error_ret; - iio_push_to_buffers_with_timestamp(indio_dev, st->data, + iio_push_to_buffers_with_timestamp(indio_dev, &st->data, iio_get_time_ns(indio_dev)); error_ret: iio_trigger_notify_done(indio_dev->trig); @@ -114,24 +670,62 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p) return IRQ_HANDLED; } -static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch) +static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch, + int *val) { struct ad7606_state *st = iio_priv(indio_dev); + unsigned int realbits = st->chip_info->channels[1].scan_type.realbits; + const struct iio_chan_spec *chan; int ret; - gpiod_set_value(st->gpio_convst, 1); - ret = wait_for_completion_timeout(&st->completion, - msecs_to_jiffies(1000)); - if (!ret) { - ret = -ETIMEDOUT; - goto error_ret; + if (st->gpio_convst) { + gpiod_set_value(st->gpio_convst, 1); + } else { + ret = ad7606_pwm_set_high(st); + if (ret < 0) + return ret; + } + + /* + * If no backend, wait for the interruption on busy pin, otherwise just add + * a delay to leave time for the data to be available. For now, the latter + * will not happen because IIO_CHAN_INFO_RAW is not supported for the backend. + * TODO: Add support for reading a single value when the backend is used. + */ + if (!st->back) { + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) { + ret = -ETIMEDOUT; + goto error_ret; + } + } else { + fsleep(1); } ret = ad7606_read_samples(st); - if (ret == 0) - ret = st->data[ch]; + if (ret) + goto error_ret; + + chan = &indio_dev->channels[ch + 1]; + if (chan->scan_type.sign == 'u') { + if (realbits > 16) + *val = st->data.buf32[ch]; + else + *val = st->data.buf16[ch]; + } else { + if (realbits > 16) + *val = sign_extend32(st->data.buf32[ch], realbits - 1); + else + *val = sign_extend32(st->data.buf16[ch], realbits - 1); + } error_ret: + if (!st->gpio_convst) { + ret = ad7606_pwm_set_low(st); + if (ret < 0) + return ret; + } gpiod_set_value(st->gpio_convst, 0); return ret; @@ -145,53 +739,57 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, { int ret, ch = 0; struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs; + struct pwm_state cnvst_pwm_state; switch (m) { case IIO_CHAN_INFO_RAW: iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = ad7606_scan_direct(indio_dev, chan->address); + ret = ad7606_scan_direct(indio_dev, chan->address, val); if (ret < 0) return ret; - *val = (short) ret; return IIO_VAL_INT; } unreachable(); case IIO_CHAN_INFO_SCALE: if (st->sw_mode_en) ch = chan->address; - *val = 0; - *val2 = st->scale_avail[st->range[ch]]; + cs = &st->chan_scales[ch]; + *val = cs->scale_avail[cs->range][0]; + *val2 = cs->scale_avail[cs->range][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = st->oversampling; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * TODO: return the real frequency intead of the requested one once + * pwm_get_state_hw comes upstream. + */ + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period); + return IIO_VAL_INT; } return -EINVAL; } -static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals, - unsigned int n, bool micros) -{ - size_t len = 0; - int i; - - for (i = 0; i < n; i++) { - len += scnprintf(buf + len, PAGE_SIZE - len, - micros ? "0.%06u " : "%u ", vals[i]); - } - buf[len - 1] = '\n'; - - return len; -} - static ssize_t in_voltage_scale_available_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[0]; + const unsigned int (*vals)[2] = cs->scale_avail; + unsigned int i; + size_t len = 0; + + for (i = 0; i < cs->num_scales; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", + vals[i][0], vals[i][1]); + buf[len - 1] = '\n'; - return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true); + return len; } static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); @@ -229,19 +827,27 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, long mask) { struct ad7606_state *st = iio_priv(indio_dev); + unsigned int scale_avail_uv[AD760X_MAX_SCALES]; + struct ad7606_chan_scale *cs; int i, ret, ch = 0; guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_SCALE: - i = find_closest(val2, st->scale_avail, st->num_scales); if (st->sw_mode_en) ch = chan->address; - ret = st->write_scale(indio_dev, ch, i); + cs = &st->chan_scales[ch]; + for (i = 0; i < cs->num_scales; i++) { + scale_avail_uv[i] = cs->scale_avail[i][0] * MICRO + + cs->scale_avail[i][1]; + } + val = (val * MICRO) + val2; + i = find_closest(val, scale_avail_uv, cs->num_scales); + ret = st->write_scale(indio_dev, ch, i + cs->reg_offset); if (ret < 0) return ret; - st->range[ch] = i; + cs->range = i; return 0; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -252,8 +858,13 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, ret = st->write_os(indio_dev, i); if (ret < 0) return ret; + st->oversampling = st->oversampling_avail[i]; return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 0 && val2 != 0) + return -EINVAL; + return ad7606_set_sampling_freq(st, val); default: return -EINVAL; } @@ -265,9 +876,15 @@ static ssize_t ad7606_oversampling_ratio_avail(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7606_state *st = iio_priv(indio_dev); + const unsigned int *vals = st->oversampling_avail; + unsigned int i; + size_t len = 0; - return ad7606_show_avail(buf, st->oversampling_avail, - st->num_os_ratios, false); + for (i = 0; i < st->num_os_ratios; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%u ", vals[i]); + buf[len - 1] = '\n'; + + return len; } static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444, @@ -301,102 +918,13 @@ static const struct attribute_group ad7606_attribute_group_range = { .attrs = ad7606_attributes_range, }; -static const struct iio_chan_spec ad7605_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(4), - AD7605_CHANNEL(0), - AD7605_CHANNEL(1), - AD7605_CHANNEL(2), - AD7605_CHANNEL(3), -}; - -static const struct iio_chan_spec ad7606_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_CHANNEL(0), - AD7606_CHANNEL(1), - AD7606_CHANNEL(2), - AD7606_CHANNEL(3), - AD7606_CHANNEL(4), - AD7606_CHANNEL(5), - AD7606_CHANNEL(6), - AD7606_CHANNEL(7), -}; - -/* - * The current assumption that this driver makes for AD7616, is that it's - * working in Hardware Mode with Serial, Burst and Sequencer modes activated. - * To activate them, following pins must be pulled high: - * -SER/PAR - * -SEQEN - * And following pins must be pulled low: - * -WR/BURST - * -DB4/SER1W - */ -static const struct iio_chan_spec ad7616_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(16), - AD7606_CHANNEL(0), - AD7606_CHANNEL(1), - AD7606_CHANNEL(2), - AD7606_CHANNEL(3), - AD7606_CHANNEL(4), - AD7606_CHANNEL(5), - AD7606_CHANNEL(6), - AD7606_CHANNEL(7), - AD7606_CHANNEL(8), - AD7606_CHANNEL(9), - AD7606_CHANNEL(10), - AD7606_CHANNEL(11), - AD7606_CHANNEL(12), - AD7606_CHANNEL(13), - AD7606_CHANNEL(14), - AD7606_CHANNEL(15), -}; - -static const struct ad7606_chip_info ad7606_chip_info_tbl[] = { - /* More devices added in future */ - [ID_AD7605_4] = { - .channels = ad7605_channels, - .num_channels = 5, - }, - [ID_AD7606_8] = { - .channels = ad7606_channels, - .num_channels = 9, - .oversampling_avail = ad7606_oversampling_avail, - .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), - }, - [ID_AD7606_6] = { - .channels = ad7606_channels, - .num_channels = 7, - .oversampling_avail = ad7606_oversampling_avail, - .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), - }, - [ID_AD7606_4] = { - .channels = ad7606_channels, - .num_channels = 5, - .oversampling_avail = ad7606_oversampling_avail, - .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), - }, - [ID_AD7606B] = { - .channels = ad7606_channels, - .num_channels = 9, - .oversampling_avail = ad7606_oversampling_avail, - .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), - }, - [ID_AD7616] = { - .channels = ad7616_channels, - .num_channels = 17, - .oversampling_avail = ad7616_oversampling_avail, - .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail), - .os_req_reset = true, - .init_delay_ms = 15, - }, -}; - static int ad7606_request_gpios(struct ad7606_state *st) { struct device *dev = st->dev; - st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start", - GPIOD_OUT_LOW); + st->gpio_convst = devm_gpiod_get_optional(dev, "adi,conversion-start", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_convst)) return PTR_ERR(st->gpio_convst); @@ -438,14 +966,24 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id) { struct iio_dev *indio_dev = dev_id; struct ad7606_state *st = iio_priv(indio_dev); + int ret; if (iio_buffer_enabled(indio_dev)) { - gpiod_set_value(st->gpio_convst, 0); + if (st->gpio_convst) { + gpiod_set_value(st->gpio_convst, 0); + } else { + ret = ad7606_pwm_set_low(st); + if (ret < 0) { + dev_err(st->dev, "PWM set low failed"); + goto done; + } + } iio_trigger_poll_nested(st->trig); } else { complete(&st->completion); } +done: return IRQ_HANDLED; }; @@ -478,14 +1016,81 @@ static int ad7606_buffer_predisable(struct iio_dev *indio_dev) return 0; } +static int ad7606_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs; + unsigned int ch = 0; + + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = st->oversampling_avail; + *length = st->num_os_ratios; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: + if (st->sw_mode_en) + ch = chan->address; + + cs = &st->chan_scales[ch]; + *vals = (int *)cs->scale_avail; + *length = cs->num_scales; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + } + return -EINVAL; +} + +static int ad7606_backend_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_pwm_set_swing(st); +} + +static int ad7606_backend_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_pwm_set_low(st); +} + +static int ad7606_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + /* + * The update scan mode is only for iio backend compatible drivers. + * If the specific update_scan_mode is not defined in the bus ops, + * just do nothing and return 0. + */ + if (!st->bops->update_scan_mode) + return 0; + + return st->bops->update_scan_mode(indio_dev, scan_mask); +} + static const struct iio_buffer_setup_ops ad7606_buffer_ops = { .postenable = &ad7606_buffer_postenable, .predisable = &ad7606_buffer_predisable, }; +static const struct iio_buffer_setup_ops ad7606_backend_buffer_ops = { + .postenable = &ad7606_backend_buffer_postenable, + .predisable = &ad7606_backend_buffer_predisable, +}; + static const struct iio_info ad7606_info_no_os_or_range = { .read_raw = &ad7606_read_raw, .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, }; static const struct iio_info ad7606_info_os_and_range = { @@ -493,14 +1098,16 @@ static const struct iio_info ad7606_info_os_and_range = { .write_raw = &ad7606_write_raw, .attrs = &ad7606_attribute_group_os_and_range, .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, }; -static const struct iio_info ad7606_info_os_range_and_debug = { +static const struct iio_info ad7606_info_sw_mode = { .read_raw = &ad7606_read_raw, .write_raw = &ad7606_write_raw, + .read_avail = &ad7606_read_avail, .debugfs_reg_access = &ad7606_reg_access, - .attrs = &ad7606_attribute_group_os_and_range, .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, }; static const struct iio_info ad7606_info_os = { @@ -508,6 +1115,7 @@ static const struct iio_info ad7606_info_os = { .write_raw = &ad7606_write_raw, .attrs = &ad7606_attribute_group_os, .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, }; static const struct iio_info ad7606_info_range = { @@ -515,14 +1123,60 @@ static const struct iio_info ad7606_info_range = { .write_raw = &ad7606_write_raw, .attrs = &ad7606_attribute_group_range, .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, }; static const struct iio_trigger_ops ad7606_trigger_ops = { .validate_device = iio_trigger_validate_own_device, }; +static int ad7606_sw_mode_setup(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + st->sw_mode_en = st->bops->sw_mode_config && + device_property_present(st->dev, "adi,sw-mode"); + if (!st->sw_mode_en) + return 0; + + indio_dev->info = &ad7606_info_sw_mode; + + return st->bops->sw_mode_config(indio_dev); +} + +static int ad7606_chan_scales_setup(struct iio_dev *indio_dev) +{ + unsigned int num_channels = indio_dev->num_channels - 1; + struct ad7606_state *st = iio_priv(indio_dev); + struct iio_chan_spec *chans; + size_t size; + int ch, ret; + + /* Clone IIO channels, since some may be differential */ + size = indio_dev->num_channels * sizeof(*indio_dev->channels); + chans = devm_kzalloc(st->dev, size, GFP_KERNEL); + if (!chans) + return -ENOMEM; + + memcpy(chans, indio_dev->channels, size); + indio_dev->channels = chans; + + for (ch = 0; ch < num_channels; ch++) { + ret = st->chip_info->scale_setup_cb(st, &chans[ch + 1], ch); + if (ret) + return ret; + } + + return 0; +} + +static void ad7606_pwm_disable(void *data) +{ + pwm_disable(data); +} + int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, - const char *name, unsigned int id, + const struct ad7606_chip_info *chip_info, const struct ad7606_bus_ops *bops) { struct ad7606_state *st; @@ -540,18 +1194,14 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, mutex_init(&st->lock); st->bops = bops; st->base_address = base_address; - /* tied to logic low, analog input range is +/- 5V */ - st->range[0] = 0; st->oversampling = 1; - st->scale_avail = ad7606_scale_avail; - st->num_scales = ARRAY_SIZE(ad7606_scale_avail); ret = devm_regulator_get_enable(dev, "avcc"); if (ret) return dev_err_probe(dev, ret, "Failed to enable specified AVcc supply\n"); - st->chip_info = &ad7606_chip_info_tbl[id]; + st->chip_info = chip_info; if (st->chip_info->oversampling_num) { st->oversampling_avail = st->chip_info->oversampling_avail; @@ -574,12 +1224,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, indio_dev->info = &ad7606_info_no_os_or_range; } indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->name = name; + indio_dev->name = chip_info->name; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; - init_completion(&st->completion); - ret = ad7606_reset(st); if (ret) dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); @@ -593,52 +1241,90 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, st->write_scale = ad7606_write_scale_hw; st->write_os = ad7606_write_os_hw; - if (st->bops->sw_mode_config) - st->sw_mode_en = device_property_present(st->dev, - "adi,sw-mode"); + ret = ad7606_sw_mode_setup(indio_dev); + if (ret) + return ret; + + ret = ad7606_chan_scales_setup(indio_dev); + if (ret) + return ret; + + /* If convst pin is not defined, setup PWM. */ + if (!st->gpio_convst) { + st->cnvst_pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnvst_pwm)) + return PTR_ERR(st->cnvst_pwm); - if (st->sw_mode_en) { - /* Scale of 0.076293 is only available in sw mode */ - st->scale_avail = ad7616_sw_scale_avail; - st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail); + /* The PWM is initialized at 1MHz to have a fast enough GPIO emulation. */ + ret = ad7606_set_sampling_freq(st, 1 * MEGA); + if (ret) + return ret; - /* After reset, in software mode, ±10 V is set by default */ - memset32(st->range, 2, ARRAY_SIZE(st->range)); - indio_dev->info = &ad7606_info_os_range_and_debug; + ret = ad7606_pwm_set_low(st); + if (ret) + return ret; - ret = st->bops->sw_mode_config(indio_dev); - if (ret < 0) + /* + * PWM is not disabled when sampling stops, but instead its duty cycle is set + * to 0% to be sure we have a "low" state. After we unload the driver, let's + * disable the PWM. + */ + ret = devm_add_action_or_reset(dev, ad7606_pwm_disable, + st->cnvst_pwm); + if (ret) return ret; } - st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); - if (!st->trig) - return -ENOMEM; + if (st->bops->iio_backend_config) { + /* + * If there is a backend, the PWM should not overpass the maximum sampling + * frequency the chip supports. + */ + ret = ad7606_set_sampling_freq(st, + chip_info->max_samplerate ? : 2 * KILO); + if (ret) + return ret; - st->trig->ops = &ad7606_trigger_ops; - iio_trigger_set_drvdata(st->trig, indio_dev); - ret = devm_iio_trigger_register(dev, st->trig); - if (ret) - return ret; + ret = st->bops->iio_backend_config(dev, indio_dev); + if (ret) + return ret; - indio_dev->trig = iio_trigger_get(st->trig); + indio_dev->setup_ops = &ad7606_backend_buffer_ops; + } else { - ret = devm_request_threaded_irq(dev, irq, - NULL, - &ad7606_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - name, indio_dev); - if (ret) - return ret; + /* Reserve the PWM use only for backend (force gpio_convst definition) */ + if (!st->gpio_convst) + return dev_err_probe(dev, -EINVAL, + "No backend, connect convst to a GPIO"); + + init_completion(&st->completion); + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7606_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return ret; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - &iio_pollfunc_store_time, - &ad7606_trigger_handler, - &ad7606_buffer_ops); - if (ret) - return ret; + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_request_threaded_irq(dev, irq, NULL, &ad7606_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + chip_info->name, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad7606_trigger_handler, + &ad7606_buffer_ops); + if (ret) + return ret; + } return devm_iio_device_register(dev, indio_dev); } @@ -665,7 +1351,7 @@ static int ad7606_resume(struct device *dev) struct ad7606_state *st = iio_priv(indio_dev); if (st->gpio_standby) { - gpiod_set_value(st->gpio_range, st->range[0]); + gpiod_set_value(st->gpio_range, st->chan_scales[0].range); gpiod_set_value(st->gpio_standby, 1); ad7606_reset(st); } diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 6649e84d25de6..998814a92b825 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -8,7 +8,9 @@ #ifndef IIO_ADC_AD7606_H_ #define IIO_ADC_AD7606_H_ -#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) { \ +#define AD760X_MAX_CHANNELS 16 + +#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, bits) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = num, \ @@ -19,56 +21,111 @@ .scan_index = num, \ .scan_type = { \ .sign = 's', \ - .realbits = 16, \ - .storagebits = 16, \ + .realbits = (bits), \ + .storagebits = (bits) > 16 ? 32 : 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define AD7606_SW_CHANNEL(num, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .address = num, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = num, \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = (bits) > 16 ? 32 : 16, \ .endianness = IIO_CPU, \ }, \ } #define AD7605_CHANNEL(num) \ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \ - BIT(IIO_CHAN_INFO_SCALE), 0) + BIT(IIO_CHAN_INFO_SCALE), 0, 16) -#define AD7606_CHANNEL(num) \ +#define AD7606_CHANNEL(num, bits) \ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \ BIT(IIO_CHAN_INFO_SCALE), \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO)) + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), bits) + +#define AD7616_CHANNEL(num) AD7606_SW_CHANNEL(num, 16) + +#define AD7606_BI_CHANNEL(num) \ + AD760X_CHANNEL(num, 0, \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 16) -#define AD7616_CHANNEL(num) \ - AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\ - 0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO)) +struct ad7606_state; + +typedef int (*ad7606_scale_setup_cb_t)(struct ad7606_state *st, + struct iio_chan_spec *chan, int ch); /** * struct ad7606_chip_info - chip specific information * @channels: channel specification + * @max_samplerate: maximum supported samplerate + * @name device name * @num_channels: number of channels + * @num_adc_channels the number of channels the ADC actually inputs. + * @scale_setup_cb: callback to setup the scales for each channel * @oversampling_avail pointer to the array which stores the available * oversampling ratios. * @oversampling_num number of elements stored in oversampling_avail array * @os_req_reset some devices require a reset to update oversampling - * @init_delay_ms required delay in miliseconds for initialization + * @init_delay_ms required delay in milliseconds for initialization * after a restart */ struct ad7606_chip_info { const struct iio_chan_spec *channels; + unsigned int max_samplerate; + const char *name; + unsigned int num_adc_channels; unsigned int num_channels; + ad7606_scale_setup_cb_t scale_setup_cb; const unsigned int *oversampling_avail; unsigned int oversampling_num; bool os_req_reset; unsigned long init_delay_ms; }; +/** + * struct ad7606_chan_scale - channel scale configuration + * @scale_avail pointer to the array which stores the available scales + * @num_scales number of elements stored in the scale_avail array + * @range voltage range selection, selects which scale to apply + * @reg_offset offset for the register value, to be applied when + * writing the value of 'range' to the register value + */ +struct ad7606_chan_scale { +#define AD760X_MAX_SCALES 16 + const unsigned int (*scale_avail)[2]; + unsigned int num_scales; + unsigned int range; + unsigned int reg_offset; +}; + /** * struct ad7606_state - driver instance specific data * @dev pointer to kernel device * @chip_info entry in the table of chips that describes this device * @bops bus operations (SPI or parallel) - * @range voltage range selection, selects which scale to apply + * @chan_scales scale configuration for channels * @oversampling oversampling selection + * @cnvst_pwm pointer to the PWM device connected to the cnvst pin * @base_address address from where to read data in parallel operation * @sw_mode_en software mode enabled - * @scale_avail pointer to the array which stores the available scales - * @num_scales number of elements stored in the scale_avail array * @oversampling_avail pointer to the array which stores the available * oversampling ratios. * @num_os_ratios number of elements stored in oversampling_avail array @@ -92,14 +149,14 @@ struct ad7606_state { struct device *dev; const struct ad7606_chip_info *chip_info; const struct ad7606_bus_ops *bops; - unsigned int range[16]; + struct ad7606_chan_scale chan_scales[AD760X_MAX_CHANNELS]; unsigned int oversampling; + struct pwm_device *cnvst_pwm; void __iomem *base_address; bool sw_mode_en; - const unsigned int *scale_avail; - unsigned int num_scales; const unsigned int *oversampling_avail; unsigned int num_os_ratios; + struct iio_backend *back; int (*write_scale)(struct iio_dev *indio_dev, int ch, int val); int (*write_os)(struct iio_dev *indio_dev, int val); @@ -116,24 +173,33 @@ struct ad7606_state { /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. - * 16 * 16-bit samples + 64-bit timestamp + * 16 * 16-bit samples + 64-bit timestamp - for AD7616 + * 8 * 32-bit samples + 64-bit timestamp - for AD7616C-18 (and similar) */ - unsigned short data[20] __aligned(IIO_DMA_MINALIGN); + union { + u16 buf16[20]; + u32 buf32[10]; + } data __aligned(IIO_DMA_MINALIGN); __be16 d16[2]; }; /** * struct ad7606_bus_ops - driver bus operations + * @iio_backend_config function pointer for configuring the iio_backend for + * the compatibles that use it * @read_block function pointer for reading blocks of data * @sw_mode_config: pointer to a function which configured the device * for software mode * @reg_read function pointer for reading spi register * @reg_write function pointer for writing spi register * @write_mask function pointer for write spi register with mask + * @update_scan_mode function pointer for handling the calls to iio_info's update_scan + * mode when enabling/disabling channels. * @rd_wr_cmd pointer to the function which calculates the spi address */ struct ad7606_bus_ops { /* more methods added in future? */ + int (*iio_backend_config)(struct device *dev, struct iio_dev *indio_dev); int (*read_block)(struct device *dev, int num, void *data); int (*sw_mode_config)(struct iio_dev *indio_dev); int (*reg_read)(struct ad7606_state *st, unsigned int addr); @@ -144,23 +210,37 @@ struct ad7606_bus_ops { unsigned int addr, unsigned long mask, unsigned int val); + int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask); u16 (*rd_wr_cmd)(int addr, char isWriteOp); }; +/** + * struct ad7606_bus_info - agregate ad7606_chip_info and ad7606_bus_ops + * @chip_info entry in the table of chips that describes this device + * @bops bus operations (SPI or parallel) + */ +struct ad7606_bus_info { + const struct ad7606_chip_info *chip_info; + const struct ad7606_bus_ops *bops; +}; + int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, - const char *name, unsigned int id, + const struct ad7606_chip_info *info, const struct ad7606_bus_ops *bops); int ad7606_reset(struct ad7606_state *st); -enum ad7606_supported_device_ids { - ID_AD7605_4, - ID_AD7606_8, - ID_AD7606_6, - ID_AD7606_4, - ID_AD7606B, - ID_AD7616, -}; +extern const struct ad7606_chip_info ad7605_4_info; +extern const struct ad7606_chip_info ad7606_8_info; +extern const struct ad7606_chip_info ad7606_6_info; +extern const struct ad7606_chip_info ad7606_4_info; +extern const struct ad7606_chip_info ad7606b_info; +extern const struct ad7606_chip_info ad7606c_16_info; +extern const struct ad7606_chip_info ad7606c_18_info; +extern const struct ad7606_chip_info ad7607_info; +extern const struct ad7606_chip_info ad7608_info; +extern const struct ad7606_chip_info ad7609_info; +extern const struct ad7606_chip_info ad7616_info; #ifdef CONFIG_PM_SLEEP extern const struct dev_pm_ops ad7606_pm_ops; diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c index 02d8c309304e2..a25182a3daa74 100644 --- a/drivers/iio/adc/ad7606_par.c +++ b/drivers/iio/adc/ad7606_par.c @@ -2,20 +2,95 @@ /* * AD7606 Parallel Interface ADC driver * - * Copyright 2011 Analog Devices Inc. + * Copyright 2011 - 2024 Analog Devices Inc. + * Copyright 2024 BayLibre SAS. */ +#include +#include +#include #include #include -#include #include +#include #include -#include -#include +#include #include + #include "ad7606.h" +static const struct iio_chan_spec ad7606b_bi_channels[] = { + AD7606_BI_CHANNEL(0), + AD7606_BI_CHANNEL(1), + AD7606_BI_CHANNEL(2), + AD7606_BI_CHANNEL(3), + AD7606_BI_CHANNEL(4), + AD7606_BI_CHANNEL(5), + AD7606_BI_CHANNEL(6), + AD7606_BI_CHANNEL(7), +}; + +static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int c, ret; + + for (c = 0; c < indio_dev->num_channels; c++) { + if (test_bit(c, scan_mask)) + ret = iio_backend_chan_enable(st->back, c); + else + ret = iio_backend_chan_disable(st->back, c); + if (ret) + return ret; + } + + return 0; +} + +static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int ret, c; + struct iio_backend_data_fmt data = { + .sign_extend = true, + .enable = true, + }; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + /* If the device is iio_backend powered the PWM is mandatory */ + if (!st->cnvst_pwm) + return dev_err_probe(st->dev, -EINVAL, + "A PWM is mandatory when using backend.\n"); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + for (c = 0; c < indio_dev->num_channels; c++) { + ret = iio_backend_data_format_set(st->back, c, &data); + if (ret) + return ret; + } + + indio_dev->channels = ad7606b_bi_channels; + indio_dev->num_channels = 8; + + return 0; +} + +static const struct ad7606_bus_ops ad7606_bi_bops = { + .iio_backend_config = ad7606_bi_setup_iio_backend, + .update_scan_mode = ad7606_bi_update_scan_mode, +}; + static int ad7606_par16_read_block(struct device *dev, int count, void *buf) { @@ -89,12 +164,32 @@ static const struct ad7606_bus_ops ad7606_par8_bops = { static int ad7606_par_probe(struct platform_device *pdev) { - const struct platform_device_id *id = platform_get_device_id(pdev); + const struct ad7606_chip_info *chip_info; + const struct platform_device_id *id; struct resource *res; void __iomem *addr; resource_size_t remap_size; int irq; + /* + * If a firmware node is available (ACPI or DT), platform_device_id is null + * and we must use get_match_data. + */ + if (dev_fwnode(&pdev->dev)) { + chip_info = device_get_match_data(&pdev->dev); + if (device_property_present(&pdev->dev, "io-backends")) + /* + * If a backend is available ,call the core probe with backend + * bops, otherwise use the former bops. + */ + return ad7606_probe(&pdev->dev, 0, NULL, + chip_info, + &ad7606_bi_bops); + } else { + id = platform_get_device_id(pdev); + chip_info = (const struct ad7606_chip_info *)id->driver_data; + } + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -105,26 +200,33 @@ static int ad7606_par_probe(struct platform_device *pdev) remap_size = resource_size(res); - return ad7606_probe(&pdev->dev, irq, addr, - id->name, id->driver_data, + return ad7606_probe(&pdev->dev, irq, addr, chip_info, remap_size > 1 ? &ad7606_par16_bops : &ad7606_par8_bops); } static const struct platform_device_id ad7606_driver_ids[] = { - { .name = "ad7605-4", .driver_data = ID_AD7605_4, }, - { .name = "ad7606-4", .driver_data = ID_AD7606_4, }, - { .name = "ad7606-6", .driver_data = ID_AD7606_6, }, - { .name = "ad7606-8", .driver_data = ID_AD7606_8, }, + { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_info, }, + { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_info, }, + { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_info, }, + { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_info, }, + { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_info, }, + { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_info, }, + { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_info, }, + { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_info, }, { } }; MODULE_DEVICE_TABLE(platform, ad7606_driver_ids); static const struct of_device_id ad7606_of_match[] = { - { .compatible = "adi,ad7605-4" }, - { .compatible = "adi,ad7606-4" }, - { .compatible = "adi,ad7606-6" }, - { .compatible = "adi,ad7606-8" }, + { .compatible = "adi,ad7605-4", .data = &ad7605_4_info }, + { .compatible = "adi,ad7606-4", .data = &ad7606_4_info }, + { .compatible = "adi,ad7606-6", .data = &ad7606_6_info }, + { .compatible = "adi,ad7606-8", .data = &ad7606_8_info }, + { .compatible = "adi,ad7606b", .data = &ad7606b_info }, + { .compatible = "adi,ad7607", .data = &ad7607_info }, + { .compatible = "adi,ad7608", .data = &ad7608_info }, + { .compatible = "adi,ad7609", .data = &ad7609_info }, { } }; MODULE_DEVICE_TABLE(of, ad7606_of_match); @@ -144,3 +246,4 @@ MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(IIO_AD7606); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index 62ec121953079..0662300cde8d1 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -5,10 +5,10 @@ * Copyright 2011 Analog Devices Inc. */ +#include #include #include #include -#include #include #include "ad7606.h" @@ -67,14 +67,26 @@ static const struct iio_chan_spec ad7616_sw_channels[] = { static const struct iio_chan_spec ad7606b_sw_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(8), - AD7616_CHANNEL(0), - AD7616_CHANNEL(1), - AD7616_CHANNEL(2), - AD7616_CHANNEL(3), - AD7616_CHANNEL(4), - AD7616_CHANNEL(5), - AD7616_CHANNEL(6), - AD7616_CHANNEL(7), + AD7606_SW_CHANNEL(0, 16), + AD7606_SW_CHANNEL(1, 16), + AD7606_SW_CHANNEL(2, 16), + AD7606_SW_CHANNEL(3, 16), + AD7606_SW_CHANNEL(4, 16), + AD7606_SW_CHANNEL(5, 16), + AD7606_SW_CHANNEL(6, 16), + AD7606_SW_CHANNEL(7, 16), +}; + +static const struct iio_chan_spec ad7606c_18_sw_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(8), + AD7606_SW_CHANNEL(0, 18), + AD7606_SW_CHANNEL(1, 18), + AD7606_SW_CHANNEL(2, 18), + AD7606_SW_CHANNEL(3, 18), + AD7606_SW_CHANNEL(4, 18), + AD7606_SW_CHANNEL(5, 18), + AD7606_SW_CHANNEL(6, 18), + AD7606_SW_CHANNEL(7, 18), }; static const unsigned int ad7606B_oversampling_avail[9] = { @@ -120,6 +132,32 @@ static int ad7606_spi_read_block(struct device *dev, return 0; } +static int ad7606_spi_read_block14to16(struct device *dev, + int count, void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_transfer xfer = { + .bits_per_word = 14, + .len = count * sizeof(u16), + .rx_buf = buf, + }; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static int ad7606_spi_read_block18to32(struct device *dev, + int count, void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_transfer xfer = { + .bits_per_word = 18, + .len = count * sizeof(u32), + .rx_buf = buf, + }; + + return spi_sync_transfer(spi, &xfer, 1); +} + static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr) { struct spi_device *spi = to_spi_device(st->dev); @@ -283,10 +321,31 @@ static int ad7606B_sw_mode_config(struct iio_dev *indio_dev) return 0; } +static int ad7606c_18_sw_mode_config(struct iio_dev *indio_dev) +{ + int ret; + + ret = ad7606B_sw_mode_config(indio_dev); + if (ret) + return ret; + + indio_dev->channels = ad7606c_18_sw_channels; + + return 0; +} + static const struct ad7606_bus_ops ad7606_spi_bops = { .read_block = ad7606_spi_read_block, }; +static const struct ad7606_bus_ops ad7607_spi_bops = { + .read_block = ad7606_spi_read_block14to16, +}; + +static const struct ad7606_bus_ops ad7608_spi_bops = { + .read_block = ad7606_spi_read_block18to32, +}; + static const struct ad7606_bus_ops ad7616_spi_bops = { .read_block = ad7606_spi_read_block, .reg_read = ad7606_spi_reg_read, @@ -296,7 +355,7 @@ static const struct ad7606_bus_ops ad7616_spi_bops = { .sw_mode_config = ad7616_sw_mode_config, }; -static const struct ad7606_bus_ops ad7606B_spi_bops = { +static const struct ad7606_bus_ops ad7606b_spi_bops = { .read_block = ad7606_spi_read_block, .reg_read = ad7606_spi_reg_read, .reg_write = ad7606_spi_reg_write, @@ -305,46 +364,106 @@ static const struct ad7606_bus_ops ad7606B_spi_bops = { .sw_mode_config = ad7606B_sw_mode_config, }; +static const struct ad7606_bus_ops ad7606c_18_spi_bops = { + .read_block = ad7606_spi_read_block18to32, + .reg_read = ad7606_spi_reg_read, + .reg_write = ad7606_spi_reg_write, + .write_mask = ad7606_spi_write_mask, + .rd_wr_cmd = ad7606B_spi_rd_wr_cmd, + .sw_mode_config = ad7606c_18_sw_mode_config, +}; + +static const struct ad7606_bus_info ad7605_4_bus_info = { + .chip_info = &ad7605_4_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606_8_bus_info = { + .chip_info = &ad7606_8_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606_6_bus_info = { + .chip_info = &ad7606_6_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606_4_bus_info = { + .chip_info = &ad7606_4_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606b_bus_info = { + .chip_info = &ad7606b_info, + .bops = &ad7606b_spi_bops, +}; + +static const struct ad7606_bus_info ad7606c_16_bus_info = { + .chip_info = &ad7606c_16_info, + .bops = &ad7606b_spi_bops, +}; + +static const struct ad7606_bus_info ad7606c_18_bus_info = { + .chip_info = &ad7606c_18_info, + .bops = &ad7606c_18_spi_bops, +}; + +static const struct ad7606_bus_info ad7607_bus_info = { + .chip_info = &ad7607_info, + .bops = &ad7607_spi_bops, +}; + +static const struct ad7606_bus_info ad7608_bus_info = { + .chip_info = &ad7608_info, + .bops = &ad7608_spi_bops, +}; + +static const struct ad7606_bus_info ad7609_bus_info = { + .chip_info = &ad7609_info, + .bops = &ad7608_spi_bops, +}; + +static const struct ad7606_bus_info ad7616_bus_info = { + .chip_info = &ad7616_info, + .bops = &ad7616_spi_bops, +}; + static int ad7606_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); - const struct ad7606_bus_ops *bops; - - switch (id->driver_data) { - case ID_AD7616: - bops = &ad7616_spi_bops; - break; - case ID_AD7606B: - bops = &ad7606B_spi_bops; - break; - default: - bops = &ad7606_spi_bops; - break; - } + const struct ad7606_bus_info *bus_info = spi_get_device_match_data(spi); return ad7606_probe(&spi->dev, spi->irq, NULL, - id->name, id->driver_data, - bops); + bus_info->chip_info, bus_info->bops); } static const struct spi_device_id ad7606_id_table[] = { - { "ad7605-4", ID_AD7605_4 }, - { "ad7606-4", ID_AD7606_4 }, - { "ad7606-6", ID_AD7606_6 }, - { "ad7606-8", ID_AD7606_8 }, - { "ad7606b", ID_AD7606B }, - { "ad7616", ID_AD7616 }, + { "ad7605-4", (kernel_ulong_t)&ad7605_4_bus_info }, + { "ad7606-4", (kernel_ulong_t)&ad7606_4_bus_info }, + { "ad7606-6", (kernel_ulong_t)&ad7606_6_bus_info }, + { "ad7606-8", (kernel_ulong_t)&ad7606_8_bus_info }, + { "ad7606b", (kernel_ulong_t)&ad7606b_bus_info }, + { "ad7606c-16", (kernel_ulong_t)&ad7606c_16_bus_info }, + { "ad7606c-18", (kernel_ulong_t)&ad7606c_18_bus_info }, + { "ad7607", (kernel_ulong_t)&ad7607_bus_info }, + { "ad7608", (kernel_ulong_t)&ad7608_bus_info }, + { "ad7609", (kernel_ulong_t)&ad7609_bus_info }, + { "ad7616", (kernel_ulong_t)&ad7616_bus_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7606_id_table); static const struct of_device_id ad7606_of_match[] = { - { .compatible = "adi,ad7605-4" }, - { .compatible = "adi,ad7606-4" }, - { .compatible = "adi,ad7606-6" }, - { .compatible = "adi,ad7606-8" }, - { .compatible = "adi,ad7606b" }, - { .compatible = "adi,ad7616" }, + { .compatible = "adi,ad7605-4", .data = &ad7605_4_bus_info }, + { .compatible = "adi,ad7606-4", .data = &ad7606_4_bus_info }, + { .compatible = "adi,ad7606-6", .data = &ad7606_6_bus_info }, + { .compatible = "adi,ad7606-8", .data = &ad7606_8_bus_info }, + { .compatible = "adi,ad7606b", .data = &ad7606b_bus_info }, + { .compatible = "adi,ad7606c-16", .data = &ad7606c_16_bus_info }, + { .compatible = "adi,ad7606c-18", .data = &ad7606c_18_bus_info }, + { .compatible = "adi,ad7607", .data = &ad7607_bus_info }, + { .compatible = "adi,ad7608", .data = &ad7608_bus_info }, + { .compatible = "adi,ad7609", .data = &ad7609_bus_info }, + { .compatible = "adi,ad7616", .data = &ad7616_bus_info }, { } }; MODULE_DEVICE_TABLE(of, ad7606_of_match); diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c new file mode 100644 index 0000000000000..ddd1e4a264298 --- /dev/null +++ b/drivers/iio/adc/ad7625.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Analog Devices Inc. AD7625 ADC driver + * + * Copyright 2024 Analog Devices Inc. + * Copyright 2024 BayLibre, SAS + * + * Note that this driver requires the AXI ADC IP block configured for + * LVDS to function. See Documentation/iio/ad7625.rst for more + * information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD7625_INTERNAL_REF_MV 4096 +#define AD7960_MAX_NBW_FREQ (2 * MEGA) + +struct ad7625_timing_spec { + /* Max conversion high time (t_{CNVH}). */ + unsigned int conv_high_ns; + /* Max conversion to MSB delay (t_{MSB}). */ + unsigned int conv_msb_ns; +}; + +struct ad7625_chip_info { + const char *name; + const unsigned int max_sample_freq_hz; + const struct ad7625_timing_spec *timing_spec; + const struct iio_chan_spec chan_spec; + const bool has_power_down_state; + const bool has_bandwidth_control; + const bool has_internal_vref; +}; + +/* AD7625_CHAN_SPEC - Define a chan spec structure for a specific chip */ +#define AD7625_CHAN_SPEC(_bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = 1, \ + .channel = 0, \ + .channel2 = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = 0, \ + .scan_type.sign = 's', \ + .scan_type.storagebits = (_bits) > 16 ? 32 : 16, \ + .scan_type.realbits = (_bits), \ +} + +struct ad7625_state { + const struct ad7625_chip_info *info; + struct iio_backend *back; + /* rate of the clock gated by the "clk_gate" PWM */ + u32 ref_clk_rate_hz; + /* PWM burst signal for transferring acquired data to the host */ + struct pwm_device *clk_gate_pwm; + /* + * PWM control signal for initiating data conversion. Analog + * inputs are sampled beginning on this signal's rising edge. + */ + struct pwm_device *cnv_pwm; + /* + * Waveforms containing the last-requested and rounded + * properties for the clk_gate and cnv PWMs + */ + struct pwm_waveform clk_gate_wf; + struct pwm_waveform cnv_wf; + unsigned int vref_mv; + u32 sampling_freq_hz; + /* + * Optional GPIOs for controlling device state. EN0 and EN1 + * determine voltage reference configuration and on/off state. + * EN2 controls the device -3dB bandwidth (and by extension, max + * sample rate). EN3 controls the VCM reference output. EN2 and + * EN3 are only present for the AD796x devices. + */ + struct gpio_desc *en_gpios[4]; + bool can_power_down; + bool can_refin; + bool can_ref_4v096; + /* + * Indicate whether the bandwidth can be narrow (9MHz). + * When true, device sample rate must also be < 2MSPS. + */ + bool can_narrow_bandwidth; + /* Indicate whether the bandwidth can be wide (28MHz). */ + bool can_wide_bandwidth; + bool can_ref_5v; + bool can_snooze; + bool can_test_pattern; + /* Indicate whether there is a REFIN supply connected */ + bool have_refin; +}; + +static const struct ad7625_timing_spec ad7625_timing_spec = { + .conv_high_ns = 40, + .conv_msb_ns = 145, +}; + +static const struct ad7625_timing_spec ad7626_timing_spec = { + .conv_high_ns = 40, + .conv_msb_ns = 80, +}; + +/* + * conv_msb_ns is set to 0 instead of the datasheet maximum of 200ns to + * avoid exceeding the minimum conversion time, i.e. it is effectively + * modulo 200 and offset by a full period. Values greater than or equal + * to the period would be rejected by the PWM API. + */ +static const struct ad7625_timing_spec ad7960_timing_spec = { + .conv_high_ns = 80, + .conv_msb_ns = 0, +}; + +static const struct ad7625_chip_info ad7625_chip_info = { + .name = "ad7625", + .max_sample_freq_hz = 6 * MEGA, + .timing_spec = &ad7625_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(16), + .has_power_down_state = false, + .has_bandwidth_control = false, + .has_internal_vref = true, +}; + +static const struct ad7625_chip_info ad7626_chip_info = { + .name = "ad7626", + .max_sample_freq_hz = 10 * MEGA, + .timing_spec = &ad7626_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(16), + .has_power_down_state = true, + .has_bandwidth_control = false, + .has_internal_vref = true, +}; + +static const struct ad7625_chip_info ad7960_chip_info = { + .name = "ad7960", + .max_sample_freq_hz = 5 * MEGA, + .timing_spec = &ad7960_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(18), + .has_power_down_state = true, + .has_bandwidth_control = true, + .has_internal_vref = false, +}; + +static const struct ad7625_chip_info ad7961_chip_info = { + .name = "ad7961", + .max_sample_freq_hz = 5 * MEGA, + .timing_spec = &ad7960_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(16), + .has_power_down_state = true, + .has_bandwidth_control = true, + .has_internal_vref = false, +}; + +enum ad7960_mode { + AD7960_MODE_POWER_DOWN, + AD7960_MODE_SNOOZE, + AD7960_MODE_NARROW_BANDWIDTH, + AD7960_MODE_WIDE_BANDWIDTH, + AD7960_MODE_TEST_PATTERN, +}; + +static int ad7625_set_sampling_freq(struct ad7625_state *st, u32 freq) +{ + u32 target; + struct pwm_waveform clk_gate_wf = { }, cnv_wf = { }; + int ret; + + target = DIV_ROUND_UP(NSEC_PER_SEC, freq); + cnv_wf.period_length_ns = clamp(target, 100, 10 * KILO); + + /* + * Use the maximum conversion time t_CNVH from the datasheet as + * the duty_cycle for ref_clk, cnv, and clk_gate + */ + cnv_wf.duty_length_ns = st->info->timing_spec->conv_high_ns; + + ret = pwm_round_waveform_might_sleep(st->cnv_pwm, &cnv_wf); + if (ret) + return ret; + + /* + * Set up the burst signal for transferring data. period and + * offset should mirror the CNV signal + */ + clk_gate_wf.period_length_ns = cnv_wf.period_length_ns; + + clk_gate_wf.duty_length_ns = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * + st->info->chan_spec.scan_type.realbits, + st->ref_clk_rate_hz); + + /* max t_MSB from datasheet */ + clk_gate_wf.duty_offset_ns = st->info->timing_spec->conv_msb_ns; + + ret = pwm_round_waveform_might_sleep(st->clk_gate_pwm, &clk_gate_wf); + if (ret) + return ret; + + st->cnv_wf = cnv_wf; + st->clk_gate_wf = clk_gate_wf; + + /* TODO: Add a rounding API for PWMs that can simplify this */ + target = DIV_ROUND_CLOSEST(st->ref_clk_rate_hz, freq); + st->sampling_freq_hz = DIV_ROUND_CLOSEST(st->ref_clk_rate_hz, + target); + + return 0; +} + +static int ad7625_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7625_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sampling_freq_hz; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mv; + *val2 = chan->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int ad7625_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7625_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return ad7625_set_sampling_freq(st, val); + unreachable(); + default: + return -EINVAL; + } +} + +static int ad7625_parse_mode(struct device *dev, struct ad7625_state *st, + int num_gpios) +{ + bool en_always_on[4], en_always_off[4]; + bool en_may_be_on[4], en_may_be_off[4]; + char en_gpio_buf[4]; + char always_on_buf[18]; + int i; + + for (i = 0; i < num_gpios; i++) { + snprintf(en_gpio_buf, sizeof(en_gpio_buf), "en%d", i); + snprintf(always_on_buf, sizeof(always_on_buf), + "adi,en%d-always-on", i); + /* Set the device to 0b0000 (power-down mode) by default */ + st->en_gpios[i] = devm_gpiod_get_optional(dev, en_gpio_buf, + GPIOD_OUT_LOW); + if (IS_ERR(st->en_gpios[i])) + return dev_err_probe(dev, PTR_ERR(st->en_gpios[i]), + "failed to get EN%d GPIO\n", i); + + en_always_on[i] = device_property_read_bool(dev, always_on_buf); + if (st->en_gpios[i] && en_always_on[i]) + return dev_err_probe(dev, -EINVAL, + "cannot have adi,en%d-always-on and en%d-gpios\n", i, i); + + en_may_be_off[i] = !en_always_on[i]; + en_may_be_on[i] = en_always_on[i] || st->en_gpios[i]; + en_always_off[i] = !en_always_on[i] && !st->en_gpios[i]; + } + + /* + * Power down is mode 0bXX00, but not all devices have a valid + * power down state. + */ + st->can_power_down = en_may_be_off[1] && en_may_be_off[0] && + st->info->has_power_down_state; + /* + * The REFIN pin can take a 1.2V (AD762x) or 2.048V (AD796x) + * external reference when the mode is 0bXX01. + */ + st->can_refin = en_may_be_off[1] && en_may_be_on[0]; + /* 4.096V can be applied to REF when the EN mode is 0bXX10. */ + st->can_ref_4v096 = en_may_be_on[1] && en_may_be_off[0]; + + /* Avoid AD796x-specific setup if the part is an AD762x */ + if (num_gpios == 2) + return 0; + + /* mode 0b1100 (AD796x) is invalid */ + if (en_always_on[3] && en_always_on[2] && + en_always_off[1] && en_always_off[0]) + return dev_err_probe(dev, -EINVAL, + "EN GPIOs set to invalid mode 0b1100\n"); + /* + * 5V can be applied to the AD796x REF pin when the EN mode is + * the same (0bX001 or 0bX101) as for can_refin, and REFIN is + * 0V. + */ + st->can_ref_5v = st->can_refin; + /* + * Bandwidth (AD796x) is controlled solely by EN2. If it's + * specified and not hard-wired, then we can configure it to + * change the bandwidth between 28MHz and 9MHz. + */ + st->can_narrow_bandwidth = en_may_be_on[2]; + /* Wide bandwidth mode is possible if EN2 can be 0. */ + st->can_wide_bandwidth = en_may_be_off[2]; + /* Snooze mode (AD796x) is 0bXX11 when REFIN = 0V. */ + st->can_snooze = en_may_be_on[1] && en_may_be_on[0]; + /* Test pattern mode (AD796x) is 0b0100. */ + st->can_test_pattern = en_may_be_off[3] && en_may_be_on[2] && + en_may_be_off[1] && en_may_be_off[0]; + + return 0; +} + +/* Set EN1 and EN0 based on reference voltage source */ +static void ad7625_set_en_gpios_for_vref(struct ad7625_state *st, + bool have_refin, int ref_mv) +{ + if (have_refin || ref_mv == 5000) { + gpiod_set_value_cansleep(st->en_gpios[1], 0); + gpiod_set_value_cansleep(st->en_gpios[0], 1); + } else if (ref_mv == 4096) { + gpiod_set_value_cansleep(st->en_gpios[1], 1); + gpiod_set_value_cansleep(st->en_gpios[0], 0); + } else { + /* + * Unreachable by AD796x, since the driver will error if + * neither REF nor REFIN is provided + */ + gpiod_set_value_cansleep(st->en_gpios[1], 1); + gpiod_set_value_cansleep(st->en_gpios[0], 1); + } +} + +static int ad7960_set_mode(struct ad7625_state *st, enum ad7960_mode mode, + bool have_refin, int ref_mv) +{ + switch (mode) { + case AD7960_MODE_POWER_DOWN: + if (!st->can_power_down) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[2], 0); + gpiod_set_value_cansleep(st->en_gpios[1], 0); + gpiod_set_value_cansleep(st->en_gpios[0], 0); + + return 0; + + case AD7960_MODE_SNOOZE: + if (!st->can_snooze) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[1], 1); + gpiod_set_value_cansleep(st->en_gpios[0], 1); + + return 0; + + case AD7960_MODE_NARROW_BANDWIDTH: + if (!st->can_narrow_bandwidth) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[2], 1); + ad7625_set_en_gpios_for_vref(st, have_refin, ref_mv); + + return 0; + + case AD7960_MODE_WIDE_BANDWIDTH: + if (!st->can_wide_bandwidth) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[2], 0); + ad7625_set_en_gpios_for_vref(st, have_refin, ref_mv); + + return 0; + + case AD7960_MODE_TEST_PATTERN: + if (!st->can_test_pattern) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[3], 0); + gpiod_set_value_cansleep(st->en_gpios[2], 1); + gpiod_set_value_cansleep(st->en_gpios[1], 0); + gpiod_set_value_cansleep(st->en_gpios[0], 0); + + return 0; + + default: + return -EINVAL; + } +} + +static int ad7625_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad7625_state *st = iio_priv(indio_dev); + int ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_pwm, &st->cnv_wf, false); + if (ret) + return ret; + + ret = pwm_set_waveform_might_sleep(st->clk_gate_pwm, + &st->clk_gate_wf, false); + if (ret) { + /* Disable cnv PWM if clk_gate setup failed */ + pwm_disable(st->cnv_pwm); + return ret; + } + + return 0; +} + +static int ad7625_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7625_state *st = iio_priv(indio_dev); + + pwm_disable(st->clk_gate_pwm); + pwm_disable(st->cnv_pwm); + + return 0; +} + +static const struct iio_info ad7625_info = { + .read_raw = ad7625_read_raw, + .write_raw = ad7625_write_raw, +}; + +static const struct iio_buffer_setup_ops ad7625_buffer_setup_ops = { + .preenable = &ad7625_buffer_preenable, + .postdisable = &ad7625_buffer_postdisable, +}; + +static int devm_ad7625_pwm_get(struct device *dev, + struct ad7625_state *st) +{ + struct clk *ref_clk; + u32 ref_clk_rate_hz; + + st->cnv_pwm = devm_pwm_get(dev, "cnv"); + if (IS_ERR(st->cnv_pwm)) + return dev_err_probe(dev, PTR_ERR(st->cnv_pwm), + "failed to get cnv pwm\n"); + + /* Preemptively disable the PWM in case it was enabled at boot */ + pwm_disable(st->cnv_pwm); + + st->clk_gate_pwm = devm_pwm_get(dev, "clk_gate"); + if (IS_ERR(st->clk_gate_pwm)) + return dev_err_probe(dev, PTR_ERR(st->clk_gate_pwm), + "failed to get clk_gate pwm\n"); + + /* Preemptively disable the PWM in case it was enabled at boot */ + pwm_disable(st->clk_gate_pwm); + + ref_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(ref_clk)) + return dev_err_probe(dev, PTR_ERR(ref_clk), + "failed to get ref_clk"); + + ref_clk_rate_hz = clk_get_rate(ref_clk); + if (!ref_clk_rate_hz) + return dev_err_probe(dev, -EINVAL, + "failed to get ref_clk rate"); + + st->ref_clk_rate_hz = ref_clk_rate_hz; + + return 0; +} + +/* + * There are three required input voltages for each device, plus two + * conditionally-optional (depending on part) REF and REFIN voltages + * where their validity depends upon the EN pin configuration. + * + * Power-up info for the device says to bring up vio, then vdd2, then + * vdd1, so list them in that order in the regulator_names array. + * + * The reference voltage source is determined like so: + * - internal reference: neither REF or REFIN is connected (invalid for + * AD796x) + * - internal buffer, external reference: REF not connected, REFIN + * connected + * - external reference: REF connected, REFIN not connected + */ +static int devm_ad7625_regulator_setup(struct device *dev, + struct ad7625_state *st) +{ + static const char * const regulator_names[] = { "vio", "vdd2", "vdd1" }; + int ret, ref_mv; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), + regulator_names); + if (ret) + return ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get REF voltage\n"); + + ref_mv = ret == -ENODEV ? 0 : ret / 1000; + + ret = devm_regulator_get_enable_optional(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get REFIN voltage\n"); + + st->have_refin = ret != -ENODEV; + + if (st->have_refin && !st->can_refin) + return dev_err_probe(dev, -EINVAL, + "REFIN provided in unsupported mode\n"); + + if (!st->info->has_internal_vref && !st->have_refin && !ref_mv) + return dev_err_probe(dev, -EINVAL, + "Need either REFIN or REF"); + + if (st->have_refin && ref_mv) + return dev_err_probe(dev, -EINVAL, + "cannot have both REFIN and REF supplies\n"); + + if (ref_mv == 4096 && !st->can_ref_4v096) + return dev_err_probe(dev, -EINVAL, + "REF is 4.096V in unsupported mode\n"); + + if (ref_mv == 5000 && !st->can_ref_5v) + return dev_err_probe(dev, -EINVAL, + "REF is 5V in unsupported mode\n"); + + st->vref_mv = ref_mv ?: AD7625_INTERNAL_REF_MV; + + return 0; +} + +static int ad7625_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct ad7625_state *st; + int ret; + u32 default_sample_freq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->info = device_get_match_data(dev); + if (!st->info) + return dev_err_probe(dev, -EINVAL, "no chip info\n"); + + if (device_property_read_bool(dev, "adi,no-dco")) + return dev_err_probe(dev, -EINVAL, + "self-clocked mode not supported\n"); + + if (st->info->has_bandwidth_control) + ret = ad7625_parse_mode(dev, st, 4); + else + ret = ad7625_parse_mode(dev, st, 2); + + if (ret) + return ret; + + ret = devm_ad7625_regulator_setup(dev, st); + if (ret) + return ret; + + /* Set the device mode based on detected EN configuration. */ + if (!st->info->has_bandwidth_control) { + ad7625_set_en_gpios_for_vref(st, st->have_refin, st->vref_mv); + } else { + /* + * If neither sampling mode is available, then report an error, + * since the other modes are not useful defaults. + */ + if (st->can_wide_bandwidth) { + ret = ad7960_set_mode(st, AD7960_MODE_WIDE_BANDWIDTH, + st->have_refin, st->vref_mv); + } else if (st->can_narrow_bandwidth) { + ret = ad7960_set_mode(st, AD7960_MODE_NARROW_BANDWIDTH, + st->have_refin, st->vref_mv); + } else { + return dev_err_probe(dev, -EINVAL, + "couldn't set device to wide or narrow bandwidth modes\n"); + } + + if (ret) + return dev_err_probe(dev, -EINVAL, + "failed to set EN pins\n"); + } + + ret = devm_ad7625_pwm_get(dev, st); + if (ret) + return ret; + + indio_dev->channels = &st->info->chan_spec; + indio_dev->num_channels = 1; + indio_dev->name = st->info->name; + indio_dev->info = &ad7625_info; + indio_dev->setup_ops = &ad7625_buffer_setup_ops; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return dev_err_probe(dev, PTR_ERR(st->back), + "failed to get IIO backend"); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + /* + * Set the initial sampling frequency to the maximum, unless the + * AD796x device is limited to narrow bandwidth by EN2 == 1, in + * which case the sampling frequency should be limited to 2MSPS + */ + default_sample_freq = st->info->max_sample_freq_hz; + if (st->info->has_bandwidth_control && !st->can_wide_bandwidth) + default_sample_freq = AD7960_MAX_NBW_FREQ; + + ret = ad7625_set_sampling_freq(st, default_sample_freq); + if (ret) + dev_err_probe(dev, ret, + "failed to set valid sampling frequency\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7625_of_match[] = { + { .compatible = "adi,ad7625", .data = &ad7625_chip_info }, + { .compatible = "adi,ad7626", .data = &ad7626_chip_info }, + { .compatible = "adi,ad7960", .data = &ad7960_chip_info }, + { .compatible = "adi,ad7961", .data = &ad7961_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7625_of_match); + +static const struct platform_device_id ad7625_device_ids[] = { + { .name = "ad7625", .driver_data = (kernel_ulong_t)&ad7625_chip_info }, + { .name = "ad7626", .driver_data = (kernel_ulong_t)&ad7626_chip_info }, + { .name = "ad7960", .driver_data = (kernel_ulong_t)&ad7960_chip_info }, + { .name = "ad7961", .driver_data = (kernel_ulong_t)&ad7961_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(platform, ad7625_device_ids); + +static struct platform_driver ad7625_driver = { + .probe = ad7625_probe, + .driver = { + .name = "ad7625", + .of_match_table = ad7625_of_match, + }, + .id_table = ad7625_device_ids, +}; +module_platform_driver(ad7625_driver); + +MODULE_AUTHOR("Trevor Gamblin "); +MODULE_DESCRIPTION("Analog Devices AD7625 ADC"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c new file mode 100644 index 0000000000000..2537dab69a35c --- /dev/null +++ b/drivers/iio/adc/ad7779.c @@ -0,0 +1,914 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD7770, AD7771, AD7779 ADC + * + * Copyright 2023-2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define AD7779_SPI_READ_CMD BIT(7) + +#define AD7779_DISABLE_SD BIT(7) + +#define AD7779_REG_CH_DISABLE 0x08 +#define AD7779_REG_CH_SYNC_OFFSET(ch) (0x09 + (ch)) +#define AD7779_REG_CH_CONFIG(ch) (0x00 + (ch)) +#define AD7779_REG_GENERAL_USER_CONFIG_1 0x11 +#define AD7779_REG_GENERAL_USER_CONFIG_2 0x12 +#define AD7779_REG_GENERAL_USER_CONFIG_3 0x13 +#define AD7779_REG_DOUT_FORMAT 0x14 +#define AD7779_REG_ADC_MUX_CONFIG 0x15 +#define AD7779_REG_GPIO_CONFIG 0x17 +#define AD7779_REG_BUFFER_CONFIG_1 0x19 +#define AD7779_REG_GLOBAL_MUX_CONFIG 0x16 +#define AD7779_REG_BUFFER_CONFIG_2 0x1A +#define AD7779_REG_GPIO_DATA 0x18 +#define AD7779_REG_CH_OFFSET_UPPER_BYTE(ch) (0x1C + (ch) * 6) +#define AD7779_REG_CH_OFFSET_LOWER_BYTE(ch) (0x1E + (ch) * 6) +#define AD7779_REG_CH_GAIN_UPPER_BYTE(ch) (0x1F + (ch) * 6) +#define AD7779_REG_CH_OFFSET_MID_BYTE(ch) (0x1D + (ch) * 6) +#define AD7779_REG_CH_GAIN_MID_BYTE(ch) (0x20 + (ch) * 6) +#define AD7779_REG_CH_ERR_REG(ch) (0x4C + (ch)) +#define AD7779_REG_CH0_1_SAT_ERR 0x54 +#define AD7779_REG_CH_GAIN_LOWER_BYTE(ch) (0x21 + (ch) * 6) +#define AD7779_REG_CH2_3_SAT_ERR 0x55 +#define AD7779_REG_CH4_5_SAT_ERR 0x56 +#define AD7779_REG_CH6_7_SAT_ERR 0x57 +#define AD7779_REG_CHX_ERR_REG_EN 0x58 +#define AD7779_REG_GEN_ERR_REG_1 0x59 +#define AD7779_REG_GEN_ERR_REG_1_EN 0x5A +#define AD7779_REG_GEN_ERR_REG_2 0x5B +#define AD7779_REG_GEN_ERR_REG_2_EN 0x5C +#define AD7779_REG_STATUS_REG_1 0x5D +#define AD7779_REG_STATUS_REG_2 0x5E +#define AD7779_REG_STATUS_REG_3 0x5F +#define AD7779_REG_SRC_N_MSB 0x60 +#define AD7779_REG_SRC_N_LSB 0x61 +#define AD7779_REG_SRC_IF_MSB 0x62 +#define AD7779_REG_SRC_IF_LSB 0x63 +#define AD7779_REG_SRC_UPDATE 0x64 + +#define AD7779_FILTER_MSK BIT(6) +#define AD7779_MOD_POWERMODE_MSK BIT(6) +#define AD7779_MOD_PDB_REFOUT_MSK BIT(4) +#define AD7779_MOD_SPI_EN_MSK BIT(4) +#define AD7779_USRMOD_INIT_MSK GENMASK(6, 4) + +/* AD7779_REG_DOUT_FORMAT */ +#define AD7779_DOUT_FORMAT_MSK GENMASK(7, 6) +#define AD7779_DOUT_HEADER_FORMAT BIT(5) +#define AD7779_DCLK_CLK_DIV_MSK GENMASK(3, 1) + +#define AD7779_REFMUX_CTRL_MSK GENMASK(7, 6) +#define AD7779_SPI_CRC_EN_MSK BIT(0) + +#define AD7779_MAXCLK_LOWPOWER (4096 * HZ_PER_KHZ) +#define AD7779_NUM_CHANNELS 8 +#define AD7779_RESET_BUF_SIZE 8 +#define AD7779_CHAN_DATA_SIZE 4 + +#define AD7779_LOWPOWER_DIV 512 +#define AD7779_HIGHPOWER_DIV 2048 + +#define AD7779_SINC3_MAXFREQ (16 * HZ_PER_KHZ) +#define AD7779_SINC5_MAXFREQ (128 * HZ_PER_KHZ) + +#define AD7779_DEFAULT_SAMPLING_FREQ (8 * HZ_PER_KHZ) +#define AD7779_DEFAULT_SAMPLING_2LINE (4 * HZ_PER_KHZ) +#define AD7779_DEFAULT_SAMPLING_1LINE (2 * HZ_PER_KHZ) + +#define AD7779_SPIMODE_MAX_SAMP_FREQ (16 * HZ_PER_KHZ) + +#define GAIN_REL 0x555555 +#define AD7779_FREQ_MSB_MSK GENMASK(15, 8) +#define AD7779_FREQ_LSB_MSK GENMASK(7, 0) +#define AD7779_UPPER GENMASK(23, 16) +#define AD7779_MID GENMASK(15, 8) +#define AD7779_LOWER GENMASK(7, 0) + +#define AD7779_REG_MSK GENMASK(6, 0) + +#define AD7779_CRC8_POLY 0x07 +DECLARE_CRC8_TABLE(ad7779_crc8_table); + +enum ad7779_filter { + AD7779_SINC3, + AD7779_SINC5, +}; + +enum ad7779_variant { + ad7770, + ad7771, + ad7779, +}; + +enum ad7779_power_mode { + AD7779_LOW_POWER, + AD7779_HIGH_POWER, +}; + +struct ad7779_chip_info { + const char *name; + struct iio_chan_spec const *channels; +}; + +struct ad7779_state { + struct spi_device *spi; + const struct ad7779_chip_info *chip_info; + struct clk *mclk; + struct iio_trigger *trig; + struct completion completion; + unsigned int sampling_freq; + enum ad7779_filter filter_enabled; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + u32 chans[8]; + aligned_s64 timestamp; + } data __aligned(IIO_DMA_MINALIGN); + u32 spidata_tx[8]; + u8 reg_rx_buf[3]; + u8 reg_tx_buf[3]; + u8 reset_buf[8]; +}; + +static const char * const ad7779_filter_type[] = { + [AD7779_SINC3] = "sinc3", + [AD7779_SINC5] = "sinc5", +}; + +static const char * const ad7779_power_supplies[] = { + "avdd1", "avdd2", "avdd4", +}; + +static int ad7779_spi_read(struct ad7779_state *st, u8 reg, u8 *rbuf) +{ + int ret; + u8 crc_buf[2]; + u8 exp_crc; + struct spi_transfer t = { + .tx_buf = st->reg_tx_buf, + .rx_buf = st->reg_rx_buf, + }; + + st->reg_tx_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg); + st->reg_tx_buf[1] = 0; + + if (reg == AD7779_REG_GEN_ERR_REG_1_EN) { + t.len = 2; + } else { + t.len = 3; + st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, + t.len - 1, 0); + } + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + crc_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg); + crc_buf[1] = st->reg_rx_buf[1]; + exp_crc = crc8(ad7779_crc8_table, crc_buf, ARRAY_SIZE(crc_buf), 0); + if (reg != AD7779_REG_GEN_ERR_REG_1_EN && exp_crc != st->reg_rx_buf[2]) { + dev_err(&st->spi->dev, "Bad CRC %x, expected %x", + st->reg_rx_buf[2], exp_crc); + return -EINVAL; + } + *rbuf = st->reg_rx_buf[1]; + + return 0; +} + +static int ad7779_spi_write(struct ad7779_state *st, u8 reg, u8 val) +{ + u8 length = 3; + + st->reg_tx_buf[0] = FIELD_GET(AD7779_REG_MSK, reg); + st->reg_tx_buf[1] = val; + if (reg == AD7779_REG_GEN_ERR_REG_1_EN) + length = 2; + else + st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, + length - 1, 0); + + return spi_write(st->spi, st->reg_tx_buf, length); +} + +static int ad7779_spi_write_mask(struct ad7779_state *st, u8 reg, u8 mask, + u8 val) +{ + int ret; + u8 regval, data; + + ret = ad7779_spi_read(st, reg, &data); + if (ret) + return ret; + + regval = (data & ~mask) | (val & mask); + + if (regval == data) + return 0; + + return ad7779_spi_write(st, reg, regval); +} + +static int ad7779_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7779_state *st = iio_priv(indio_dev); + u8 rval; + int ret; + + if (readval) { + ret = ad7779_spi_read(st, reg, &rval); + *readval = rval; + return ret; + } + + return ad7779_spi_write(st, reg, writeval); +} + +static int ad7779_set_sampling_frequency(struct ad7779_state *st, + unsigned int sampling_freq) +{ + int ret; + unsigned int dec; + unsigned int frac; + unsigned int div; + unsigned int decimal; + unsigned int freq_khz; + + if (st->filter_enabled == AD7779_SINC3 && + sampling_freq > AD7779_SINC3_MAXFREQ) + return -EINVAL; + + if (st->filter_enabled == AD7779_SINC5 && + sampling_freq > AD7779_SINC5_MAXFREQ) + return -EINVAL; + + if (sampling_freq > AD7779_SPIMODE_MAX_SAMP_FREQ) + return -EINVAL; + + div = AD7779_HIGHPOWER_DIV; + + freq_khz = sampling_freq / HZ_PER_KHZ; + dec = div / freq_khz; + frac = div % freq_khz; + + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, + FIELD_GET(AD7779_FREQ_MSB_MSK, dec)); + if (ret) + return ret; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, + FIELD_GET(AD7779_FREQ_LSB_MSK, dec)); + if (ret) + return ret; + + if (frac) { + /* + * In order to obtain the first three decimals of the decimation + * the initial number is multiplied with 10^3 prior to the + * division, then the original division result is subtracted and + * the number is divided by 10^3. + */ + decimal = ((mult_frac(div, KILO, freq_khz) - dec * KILO) << 16) + / KILO; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, + FIELD_GET(AD7779_FREQ_MSB_MSK, decimal)); + if (ret) + return ret; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, + FIELD_GET(AD7779_FREQ_LSB_MSK, decimal)); + if (ret) + return ret; + } else { + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, + FIELD_GET(AD7779_FREQ_MSB_MSK, 0x0)); + if (ret) + return ret; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, + FIELD_GET(AD7779_FREQ_LSB_MSK, 0x0)); + if (ret) + return ret; + } + ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, BIT(0)); + if (ret) + return ret; + + /* SRC update settling time */ + fsleep(15); + + ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, 0x0); + if (ret) + return ret; + + /* SRC update settling time */ + fsleep(15); + + st->sampling_freq = sampling_freq; + + return 0; +} + +static int ad7779_get_filter(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad7779_state *st = iio_priv(indio_dev); + u8 temp; + int ret; + + ret = ad7779_spi_read(st, AD7779_REG_GENERAL_USER_CONFIG_2, &temp); + if (ret) + return ret; + + return FIELD_GET(AD7779_FILTER_MSK, temp); +} + +static int ad7779_set_filter(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int mode) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7779_spi_write_mask(st, + AD7779_REG_GENERAL_USER_CONFIG_2, + AD7779_FILTER_MSK, + FIELD_PREP(AD7779_FILTER_MSK, mode)); + if (ret) + return ret; + + ret = ad7779_set_sampling_frequency(st, st->sampling_freq); + if (ret) + return ret; + + st->filter_enabled = mode; + + return 0; +} + +static int ad7779_get_calibscale(struct ad7779_state *st, int channel) +{ + int ret; + u8 calibscale[3]; + + ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel), + &calibscale[0]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_MID_BYTE(channel), + &calibscale[1]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel), + &calibscale[2]); + if (ret) + return ret; + + return get_unaligned_be24(calibscale); +} + +static int ad7779_set_calibscale(struct ad7779_state *st, int channel, int val) +{ + int ret; + unsigned int gain; + u8 gain_bytes[3]; + + /* + * The gain value is relative to 0x555555, which represents a gain of 1 + */ + gain = DIV_ROUND_CLOSEST_ULL((u64)val * 5592405LL, MEGA); + put_unaligned_be24(gain, gain_bytes); + ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel), + gain_bytes[0]); + if (ret) + return ret; + + ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_MID_BYTE(channel), + gain_bytes[1]); + if (ret) + return ret; + + return ad7779_spi_write(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel), + gain_bytes[2]); +} + +static int ad7779_get_calibbias(struct ad7779_state *st, int channel) +{ + int ret; + u8 calibbias[3]; + + ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel), + &calibbias[0]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel), + &calibbias[1]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel), + &calibbias[2]); + if (ret) + return ret; + + return get_unaligned_be24(calibbias); +} + +static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val) +{ + int ret; + u8 calibbias[3]; + + put_unaligned_be24(val, calibbias); + ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel), + calibbias[0]); + if (ret) + return ret; + + ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel), + calibbias[1]); + if (ret) + return ret; + + return ad7779_spi_write(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel), + calibbias[2]); +} + +static int ad7779_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad7779_get_calibscale(st, chan->channel); + if (ret < 0) + return ret; + *val = ret; + *val2 = GAIN_REL; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad7779_get_calibbias(st, chan->channel); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sampling_freq; + if (*val < 0) + return -EINVAL; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + unreachable(); +} + +static int ad7779_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + return ad7779_set_calibscale(st, chan->channel, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad7779_set_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7779_set_sampling_frequency(st, val); + default: + return -EINVAL; + } + } + unreachable(); +} + +static int ad7779_buffer_preenable(struct iio_dev *indio_dev) +{ + int ret; + struct ad7779_state *st = iio_priv(indio_dev); + + ret = ad7779_spi_write_mask(st, + AD7779_REG_GENERAL_USER_CONFIG_3, + AD7779_MOD_SPI_EN_MSK, + FIELD_PREP(AD7779_MOD_SPI_EN_MSK, 1)); + if (ret) + return ret; + + /* + * DRDY output cannot be disabled at device level therefore we mask + * the irq at host end. + */ + enable_irq(st->spi->irq); + + return 0; +} + +static int ad7779_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7779_state *st = iio_priv(indio_dev); + + disable_irq(st->spi->irq); + + return ad7779_spi_write(st, AD7779_REG_GENERAL_USER_CONFIG_3, + AD7779_DISABLE_SD); +} + +static irqreturn_t ad7779_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + struct spi_transfer t = { + .rx_buf = st->data.chans, + .tx_buf = st->spidata_tx, + .len = AD7779_NUM_CHANNELS * AD7779_CHAN_DATA_SIZE, + }; + + st->spidata_tx[0] = AD7779_SPI_READ_CMD; + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) { + dev_err(&st->spi->dev, "SPI transfer error in IRQ handler"); + goto exit_handler; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &st->data, pf->timestamp); + +exit_handler: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int ad7779_reset(struct iio_dev *indio_dev, struct gpio_desc *reset_gpio) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + struct spi_transfer t = { + .tx_buf = st->reset_buf, + .len = 8, + }; + + if (reset_gpio) { + gpiod_set_value(reset_gpio, 1); + /* Delay for reset to occur is 225 microseconds */ + fsleep(230); + ret = 0; + } else { + memset(st->reset_buf, 0xff, sizeof(st->reset_buf)); + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + } + + /* Delay for reset to occur is 225 microseconds */ + fsleep(230); + + return ret; +} + +static const struct iio_info ad7779_info = { + .read_raw = ad7779_read_raw, + .write_raw = ad7779_write_raw, + .debugfs_reg_access = &ad7779_reg_access, +}; + +static const struct iio_enum ad7779_filter_enum = { + .items = ad7779_filter_type, + .num_items = ARRAY_SIZE(ad7779_filter_type), + .get = ad7779_get_filter, + .set = ad7779_set_filter, +}; + +static const struct iio_chan_spec_ext_info ad7779_ext_filter[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7779_filter_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, + &ad7779_filter_enum), + { } +}; + +#define AD777x_CHAN_S(index, _ext_info) \ + { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .address = (index), \ + .indexed = 1, \ + .channel = (index), \ + .scan_index = (index), \ + .ext_info = (_ext_info), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD777x_CHAN_NO_FILTER_S(index) \ + AD777x_CHAN_S(index, NULL) + +#define AD777x_CHAN_FILTER_S(index) \ + AD777x_CHAN_S(index, ad7779_ext_filter) +static const struct iio_chan_spec ad7779_channels[] = { + AD777x_CHAN_NO_FILTER_S(0), + AD777x_CHAN_NO_FILTER_S(1), + AD777x_CHAN_NO_FILTER_S(2), + AD777x_CHAN_NO_FILTER_S(3), + AD777x_CHAN_NO_FILTER_S(4), + AD777x_CHAN_NO_FILTER_S(5), + AD777x_CHAN_NO_FILTER_S(6), + AD777x_CHAN_NO_FILTER_S(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad7779_channels_filter[] = { + AD777x_CHAN_FILTER_S(0), + AD777x_CHAN_FILTER_S(1), + AD777x_CHAN_FILTER_S(2), + AD777x_CHAN_FILTER_S(3), + AD777x_CHAN_FILTER_S(4), + AD777x_CHAN_FILTER_S(5), + AD777x_CHAN_FILTER_S(6), + AD777x_CHAN_FILTER_S(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_buffer_setup_ops ad7779_buffer_setup_ops = { + .preenable = ad7779_buffer_preenable, + .postdisable = ad7779_buffer_postdisable, +}; + +static const struct iio_trigger_ops ad7779_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad7779_conf(struct ad7779_state *st, struct gpio_desc *start_gpio) +{ + int ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_GEN_ERR_REG_1_EN, + AD7779_SPI_CRC_EN_MSK, + FIELD_PREP(AD7779_SPI_CRC_EN_MSK, 1)); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, + AD7779_USRMOD_INIT_MSK, + FIELD_PREP(AD7779_USRMOD_INIT_MSK, 5)); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, + AD7779_DCLK_CLK_DIV_MSK, + FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 1)); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_ADC_MUX_CONFIG, + AD7779_REFMUX_CTRL_MSK, + FIELD_PREP(AD7779_REFMUX_CTRL_MSK, 1)); + if (ret) + return ret; + + ret = ad7779_set_sampling_frequency(st, AD7779_DEFAULT_SAMPLING_FREQ); + if (ret) + return ret; + + gpiod_set_value(start_gpio, 0); + /* Start setup time */ + fsleep(15); + gpiod_set_value(start_gpio, 1); + /* Start setup time */ + fsleep(15); + gpiod_set_value(start_gpio, 0); + /* Start setup time */ + fsleep(15); + + return 0; +} + +static int ad7779_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad7779_state *st; + struct gpio_desc *reset_gpio, *start_gpio; + struct device *dev = &spi->dev; + int ret = -EINVAL; + + if (!spi->irq) + return dev_err_probe(dev, ret, "DRDY irq not present\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad7779_power_supplies), + ad7779_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + st->mclk = devm_clk_get_enabled(dev, "mclk"); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); + + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + + start_gpio = devm_gpiod_get(dev, "start", GPIOD_OUT_HIGH); + if (IS_ERR(start_gpio)) + return PTR_ERR(start_gpio); + + crc8_populate_msb(ad7779_crc8_table, AD7779_CRC8_POLY); + st->spi = spi; + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + ret = ad7779_reset(indio_dev, reset_gpio); + if (ret) + return ret; + + ret = ad7779_conf(st, start_gpio); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad7779_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = ARRAY_SIZE(ad7779_channels); + + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7779_trigger_ops; + + iio_trigger_set_drvdata(st->trig, st); + + ret = devm_request_irq(dev, spi->irq, iio_trigger_generic_data_rdy_poll, + IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name, + st->trig); + if (ret) + return dev_err_probe(dev, ret, "request IRQ %d failed\n", + st->spi->irq); + + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + init_completion(&st->completion); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad7779_trigger_handler, + &ad7779_buffer_setup_ops); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, + AD7779_DCLK_CLK_DIV_MSK, + FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 7)); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int ad7779_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7779_state *st = iio_priv(indio_dev); + + return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, + AD7779_MOD_POWERMODE_MSK, + FIELD_PREP(AD7779_MOD_POWERMODE_MSK, + AD7779_LOW_POWER)); +} + +static int ad7779_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7779_state *st = iio_priv(indio_dev); + + return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, + AD7779_MOD_POWERMODE_MSK, + FIELD_PREP(AD7779_MOD_POWERMODE_MSK, + AD7779_HIGH_POWER)); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ad7779_pm_ops, ad7779_suspend, ad7779_resume); + +static const struct ad7779_chip_info ad7770_chip_info = { + .name = "ad7770", + .channels = ad7779_channels, +}; + +static const struct ad7779_chip_info ad7771_chip_info = { + .name = "ad7771", + .channels = ad7779_channels_filter, +}; + +static const struct ad7779_chip_info ad7779_chip_info = { + .name = "ad7779", + .channels = ad7779_channels, +}; + +static const struct spi_device_id ad7779_id[] = { + { + .name = "ad7770", + .driver_data = (kernel_ulong_t)&ad7770_chip_info, + }, + { + .name = "ad7771", + .driver_data = (kernel_ulong_t)&ad7771_chip_info, + }, + { + .name = "ad7779", + .driver_data = (kernel_ulong_t)&ad7779_chip_info, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7779_id); + +static const struct of_device_id ad7779_of_table[] = { + { + .compatible = "adi,ad7770", + .data = &ad7770_chip_info, + }, + { + .compatible = "adi,ad7771", + .data = &ad7771_chip_info, + }, + { + .compatible = "adi,ad7779", + .data = &ad7779_chip_info, + }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7779_of_table); + +static struct spi_driver ad7779_driver = { + .driver = { + .name = "ad7779", + .pm = pm_sleep_ptr(&ad7779_pm_ops), + .of_match_table = ad7779_of_table, + }, + .probe = ad7779_probe, + .id_table = ad7779_id, +}; +module_spi_driver(ad7779_driver); + +MODULE_AUTHOR("Ramona Alexandra Nechita "); +MODULE_DESCRIPTION("Analog Devices AD7779 ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index e9b0c577c9cca..8ccb74f470309 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -152,7 +152,7 @@ static int ad7780_write_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_SCALE: - if (val != 0) + if (val != 0 || val2 == 0) return -EINVAL; vref = st->int_vref_mv * 1000000LL; diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index 86effe8501b44..5d2ad3dd6caa7 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -371,7 +371,7 @@ static const struct iio_info ad7791_no_filter_info = { }; static int ad7791_setup(struct ad7791_state *st, - struct ad7791_platform_data *pdata) + const struct ad7791_platform_data *pdata) { /* Set to poweron-reset default values */ st->mode = AD7791_MODE_BUFFER; @@ -401,7 +401,7 @@ static void ad7791_reg_disable(void *reg) static int ad7791_probe(struct spi_device *spi) { - struct ad7791_platform_data *pdata = spi->dev.platform_data; + const struct ad7791_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; struct ad7791_state *st; int ret; diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index abebd519cafa3..b86e89370e0d1 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -770,7 +770,7 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { static int ad7793_probe(struct spi_device *spi) { - const struct ad7793_platform_data *pdata = spi->dev.platform_data; + const struct ad7793_platform_data *pdata = dev_get_platdata(&spi->dev); struct ad7793_state *st; struct iio_dev *indio_dev; int ret, vref_mv = 0; diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 6265ce7df7030..69add1dc4b538 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -41,7 +41,7 @@ enum ad7887_channels { }; /** - * struct ad7887_chip_info - chip specifc information + * struct ad7887_chip_info - chip specific information * @int_vref_mv: the internal reference voltage * @channels: channels specification * @num_channels: number of channels @@ -234,7 +234,7 @@ static void ad7887_reg_disable(void *data) static int ad7887_probe(struct spi_device *spi) { - struct ad7887_platform_data *pdata = spi->dev.platform_data; + const struct ad7887_platform_data *pdata = dev_get_platdata(&spi->dev); struct ad7887_state *st; struct iio_dev *indio_dev; uint8_t mode; diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 09680015a7ab5..acc44cb34f824 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -48,7 +48,7 @@ struct ad7923_state { struct spi_device *spi; - struct spi_transfer ring_xfer[5]; + struct spi_transfer ring_xfer[9]; struct spi_transfer scan_single_xfer[2]; struct spi_message ring_msg; struct spi_message scan_single_msg; @@ -64,7 +64,7 @@ struct ad7923_state { * Length = 8 channels + 4 extra for 8 byte timestamp */ __be16 rx_buf[12] __aligned(IIO_DMA_MINALIGN); - __be16 tx_buf[4]; + __be16 tx_buf[8]; }; struct ad7923_chip_info { diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c index 0f36138a71445..a5aea4e9f1a7b 100644 --- a/drivers/iio/adc/ad7944.c +++ b/drivers/iio/adc/ad7944.c @@ -80,7 +80,7 @@ struct ad7944_adc { }; /* quite time before CNV rising edge */ -#define T_QUIET_NS 20 +#define AD7944_T_QUIET_NS 20 static const struct ad7944_timing_spec ad7944_timing_spec = { .conv_ns = 420, @@ -150,7 +150,7 @@ static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc * * CS is tied to CNV and we need a low to high transition to start the * conversion, so place CNV low for t_QUIET to prepare for this. */ - xfers[0].delay.value = T_QUIET_NS; + xfers[0].delay.value = AD7944_T_QUIET_NS; xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; /* diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 0f107e3fc2c85..aa44b4e2542b2 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -406,7 +406,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct ad799x_state *st = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index ea4aabd3960a0..2f3b61765055d 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -469,7 +469,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) /* * Data array after transfer will look like (if status is appended): * data[] = { [0][sample][sample][sample][status] } - * Keeping the first byte 0 shifts the status postion by 1 byte to the right. + * Keeping the first byte 0 shifts the status position by 1 byte to the right. */ status_pos = reg_size + 1; @@ -656,7 +656,7 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, sigma_delta->spi = spi; sigma_delta->info = info; - /* If the field is unset in ad_sigma_delta_info, asume there can only be 1 slot. */ + /* If the field is unset in ad_sigma_delta_info, assume there can only be 1 slot. */ if (!info->num_slots) sigma_delta->num_slots = 1; else diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index d7fd21e7c6e2a..8e5aaf15a9215 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2625,7 +2625,7 @@ MODULE_DEVICE_TABLE(of, at91_adc_dt_match); static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, - .remove_new = at91_adc_remove, + .remove = at91_adc_remove, .driver = { .name = "at91-sama5d2_adc", .of_match_table = at91_adc_dt_match, diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 9c39acff17e65..a3f0a23216661 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1341,7 +1341,7 @@ MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, - .remove_new = at91_adc_remove, + .remove = at91_adc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = at91_adc_dt_ids, diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 6c1a5d1b0a83d..9fd7027623d0c 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -155,52 +155,22 @@ enum axp813_adc_channel_v { AXP813_BATT_V, }; -static struct iio_map axp20x_maps[] = { - { - .consumer_dev_name = "axp20x-usb-power-supply", - .consumer_channel = "vbus_v", - .adc_channel_label = "vbus_v", - }, { - .consumer_dev_name = "axp20x-usb-power-supply", - .consumer_channel = "vbus_i", - .adc_channel_label = "vbus_i", - }, { - .consumer_dev_name = "axp20x-ac-power-supply", - .consumer_channel = "acin_v", - .adc_channel_label = "acin_v", - }, { - .consumer_dev_name = "axp20x-ac-power-supply", - .consumer_channel = "acin_i", - .adc_channel_label = "acin_i", - }, { - .consumer_dev_name = "axp20x-battery-power-supply", - .consumer_channel = "batt_v", - .adc_channel_label = "batt_v", - }, { - .consumer_dev_name = "axp20x-battery-power-supply", - .consumer_channel = "batt_chrg_i", - .adc_channel_label = "batt_chrg_i", - }, { - .consumer_dev_name = "axp20x-battery-power-supply", - .consumer_channel = "batt_dischrg_i", - .adc_channel_label = "batt_dischrg_i", - }, { /* sentinel */ } +static const struct iio_map axp20x_maps[] = { + IIO_MAP("vbus_v", "axp20x-usb-power-supply", "vbus_v"), + IIO_MAP("vbus_i", "axp20x-usb-power-supply", "vbus_i"), + IIO_MAP("acin_v", "axp20x-ac-power-supply", "acin_v"), + IIO_MAP("acin_i", "axp20x-ac-power-supply", "acin_i"), + IIO_MAP("batt_v", "axp20x-battery-power-supply", "batt_v"), + IIO_MAP("batt_chrg_i", "axp20x-battery-power-supply", "batt_chrg_i"), + IIO_MAP("batt_dischrg_i", "axp20x-battery-power-supply", "batt_dischrg_i"), + { /* sentinel */ } }; -static struct iio_map axp22x_maps[] = { - { - .consumer_dev_name = "axp20x-battery-power-supply", - .consumer_channel = "batt_v", - .adc_channel_label = "batt_v", - }, { - .consumer_dev_name = "axp20x-battery-power-supply", - .consumer_channel = "batt_chrg_i", - .adc_channel_label = "batt_chrg_i", - }, { - .consumer_dev_name = "axp20x-battery-power-supply", - .consumer_channel = "batt_dischrg_i", - .adc_channel_label = "batt_dischrg_i", - }, { /* sentinel */ } +static const struct iio_map axp22x_maps[] = { + IIO_MAP("batt_v", "axp20x-battery-power-supply", "batt_v"), + IIO_MAP("batt_chrg_i", "axp20x-battery-power-supply", "batt_chrg_i"), + IIO_MAP("batt_dischrg_i", "axp20x-battery-power-supply", "batt_dischrg_i"), + { /* sentinel */ } }; static struct iio_map axp717_maps[] = { @@ -1044,7 +1014,7 @@ struct axp_data { unsigned long adc_en2_mask; int (*adc_rate)(struct axp20x_adc_iio *info, int rate); - struct iio_map *maps; + const struct iio_map *maps; }; static const struct axp_data axp192_data = { @@ -1212,7 +1182,7 @@ static struct platform_driver axp20x_adc_driver = { }, .id_table = axp20x_adc_id_match, .probe = axp20x_probe, - .remove_new = axp20x_remove, + .remove = axp20x_remove, }; module_platform_driver(axp20x_adc_driver); diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 8c3acc0cd7e99..45542efc3ece0 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -103,7 +103,7 @@ static const struct iio_chan_spec axp288_adc_channels[] = { }; /* for consumer drivers */ -static struct iio_map axp288_adc_default_maps[] = { +static const struct iio_map axp288_adc_default_maps[] = { IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c index cdfe304eaa201..f258668b0dc71 100644 --- a/drivers/iio/adc/bcm_iproc_adc.c +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -611,10 +611,10 @@ static const struct of_device_id iproc_adc_of_match[] = { MODULE_DEVICE_TABLE(of, iproc_adc_of_match); static struct platform_driver iproc_adc_driver = { - .probe = iproc_adc_probe, - .remove_new = iproc_adc_remove, - .driver = { - .name = "iproc-static-adc", + .probe = iproc_adc_probe, + .remove = iproc_adc_remove, + .driver = { + .name = "iproc-static-adc", .of_match_table = iproc_adc_of_match, }, }; diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c index 8f0d3fb63b677..0290345ade841 100644 --- a/drivers/iio/adc/da9150-gpadc.c +++ b/drivers/iio/adc/da9150-gpadc.c @@ -291,27 +291,11 @@ static const struct iio_chan_spec da9150_gpadc_channels[] = { }; /* Default maps used by da9150-charger */ -static struct iio_map da9150_gpadc_default_maps[] = { - { - .consumer_dev_name = "da9150-charger", - .consumer_channel = "CHAN_IBUS", - .adc_channel_label = "IBUS", - }, - { - .consumer_dev_name = "da9150-charger", - .consumer_channel = "CHAN_VBUS", - .adc_channel_label = "VBUS", - }, - { - .consumer_dev_name = "da9150-charger", - .consumer_channel = "CHAN_TJUNC", - .adc_channel_label = "TJUNC_CORE", - }, - { - .consumer_dev_name = "da9150-charger", - .consumer_channel = "CHAN_VBAT", - .adc_channel_label = "VBAT", - }, +static const struct iio_map da9150_gpadc_default_maps[] = { + IIO_MAP("IBUS", "da9150-charger", "CHAN_IBUS"), + IIO_MAP("VBUS", "da9150-charger", "CHAN_VBUS"), + IIO_MAP("TJUNC_CORE", "da9150-charger", "CHAN_TJUNC"), + IIO_MAP("VBAT", "da9150-charger", "CHAN_VBAT"), {}, }; diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index de7252a10047d..30328626d9bee 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -700,7 +700,7 @@ static void dln2_adc_remove(struct platform_device *pdev) static struct platform_driver dln2_adc_driver = { .driver.name = DLN2_ADC_MOD_NAME, .probe = dln2_adc_probe, - .remove_new = dln2_adc_remove, + .remove = dln2_adc_remove, }; module_platform_driver(dln2_adc_driver); diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index cc38d5e0608ee..a3e9c697e2cbe 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -238,7 +238,7 @@ static struct platform_driver ep93xx_adc_driver = { .of_match_table = ep93xx_adc_of_ids, }, .probe = ep93xx_adc_probe, - .remove_new = ep93xx_adc_remove, + .remove = ep93xx_adc_remove, }; module_platform_driver(ep93xx_adc_driver); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 4d00ee8dd14d0..4614cf8485359 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -1008,7 +1008,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend, static struct platform_driver exynos_adc_driver = { .probe = exynos_adc_probe, - .remove_new = exynos_adc_remove, + .remove = exynos_adc_remove, .driver = { .name = "exynos-adc", .of_match_table = exynos_adc_match, diff --git a/drivers/iio/adc/gehc-pmc-adc.c b/drivers/iio/adc/gehc-pmc-adc.c new file mode 100644 index 0000000000000..d1167818b17d6 --- /dev/null +++ b/drivers/iio/adc/gehc-pmc-adc.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The GE HealthCare PMC ADC is a 16-Channel (Voltage and current), 16-Bit + * ADC with an I2C Interface. + * + * Copyright (C) 2024, GE HealthCare + * + * Authors: + * Herve Codina + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct pmc_adc { + struct i2c_client *client; +}; + +#define PMC_ADC_CMD_REQUEST_PROTOCOL_VERSION 0x01 +#define PMC_ADC_CMD_READ_VOLTAGE(_ch) (0x10 | (_ch)) +#define PMC_ADC_CMD_READ_CURRENT(_ch) (0x20 | (_ch)) + +#define PMC_ADC_VOLTAGE_CHANNEL(_ch, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_ch), \ + .address = PMC_ADC_CMD_READ_VOLTAGE(_ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = (_ds_name), \ +} + +#define PMC_ADC_CURRENT_CHANNEL(_ch, _ds_name) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .channel = (_ch), \ + .address = PMC_ADC_CMD_READ_CURRENT(_ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = (_ds_name), \ +} + +static const struct iio_chan_spec pmc_adc_channels[] = { + PMC_ADC_VOLTAGE_CHANNEL(0, "CH0_V"), + PMC_ADC_VOLTAGE_CHANNEL(1, "CH1_V"), + PMC_ADC_VOLTAGE_CHANNEL(2, "CH2_V"), + PMC_ADC_VOLTAGE_CHANNEL(3, "CH3_V"), + PMC_ADC_VOLTAGE_CHANNEL(4, "CH4_V"), + PMC_ADC_VOLTAGE_CHANNEL(5, "CH5_V"), + PMC_ADC_VOLTAGE_CHANNEL(6, "CH6_V"), + PMC_ADC_VOLTAGE_CHANNEL(7, "CH7_V"), + PMC_ADC_VOLTAGE_CHANNEL(8, "CH8_V"), + PMC_ADC_VOLTAGE_CHANNEL(9, "CH9_V"), + PMC_ADC_VOLTAGE_CHANNEL(10, "CH10_V"), + PMC_ADC_VOLTAGE_CHANNEL(11, "CH11_V"), + PMC_ADC_VOLTAGE_CHANNEL(12, "CH12_V"), + PMC_ADC_VOLTAGE_CHANNEL(13, "CH13_V"), + PMC_ADC_VOLTAGE_CHANNEL(14, "CH14_V"), + PMC_ADC_VOLTAGE_CHANNEL(15, "CH15_V"), + + PMC_ADC_CURRENT_CHANNEL(0, "CH0_I"), + PMC_ADC_CURRENT_CHANNEL(1, "CH1_I"), + PMC_ADC_CURRENT_CHANNEL(2, "CH2_I"), + PMC_ADC_CURRENT_CHANNEL(3, "CH3_I"), + PMC_ADC_CURRENT_CHANNEL(4, "CH4_I"), + PMC_ADC_CURRENT_CHANNEL(5, "CH5_I"), + PMC_ADC_CURRENT_CHANNEL(6, "CH6_I"), + PMC_ADC_CURRENT_CHANNEL(7, "CH7_I"), + PMC_ADC_CURRENT_CHANNEL(8, "CH8_I"), + PMC_ADC_CURRENT_CHANNEL(9, "CH9_I"), + PMC_ADC_CURRENT_CHANNEL(10, "CH10_I"), + PMC_ADC_CURRENT_CHANNEL(11, "CH11_I"), + PMC_ADC_CURRENT_CHANNEL(12, "CH12_I"), + PMC_ADC_CURRENT_CHANNEL(13, "CH13_I"), + PMC_ADC_CURRENT_CHANNEL(14, "CH14_I"), + PMC_ADC_CURRENT_CHANNEL(15, "CH15_I"), +}; + +static int pmc_adc_read_raw_ch(struct pmc_adc *pmc_adc, u8 cmd, int *val) +{ + s32 ret; + + ret = i2c_smbus_read_word_swapped(pmc_adc->client, cmd); + if (ret < 0) { + dev_err(&pmc_adc->client->dev, "i2c read word failed (%d)\n", ret); + return ret; + } + + *val = sign_extend32(ret, 15); + return 0; +} + +static int pmc_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct pmc_adc *pmc_adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + /* Values are directly read in mV or mA */ + ret = pmc_adc_read_raw_ch(pmc_adc, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int pmc_adc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + enum iio_chan_type expected_type; + unsigned int i; + + /* + * args[0]: Acquisition type (i.e. voltage or current) + * args[1]: PMC ADC channel number + */ + if (iiospec->nargs != 2) + return -EINVAL; + + switch (iiospec->args[0]) { + case GEHC_PMC_ADC_VOLTAGE: + expected_type = IIO_VOLTAGE; + break; + case GEHC_PMC_ADC_CURRENT: + expected_type = IIO_CURRENT; + break; + default: + dev_err(&indio_dev->dev, "Invalid channel type %llu\n", + iiospec->args[0]); + return -EINVAL; + } + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].type == expected_type && + indio_dev->channels[i].channel == iiospec->args[1]) + return i; + + dev_err(&indio_dev->dev, "Invalid channel type %llu number %llu\n", + iiospec->args[0], iiospec->args[1]); + return -EINVAL; +} + +static const struct iio_info pmc_adc_info = { + .read_raw = pmc_adc_read_raw, + .fwnode_xlate = pmc_adc_fwnode_xlate, +}; + +static const char *const pmc_adc_regulator_names[] = { + "vdd", + "vdda", + "vddio", + "vref", +}; + +static int pmc_adc_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct pmc_adc *pmc_adc; + struct clk *clk; + s32 val; + int ret; + + ret = devm_regulator_bulk_get_enable(&client->dev, ARRAY_SIZE(pmc_adc_regulator_names), + pmc_adc_regulator_names); + if (ret) + return dev_err_probe(&client->dev, ret, "Failed to get regulators\n"); + + clk = devm_clk_get_optional_enabled(&client->dev, "osc"); + if (IS_ERR(clk)) + return dev_err_probe(&client->dev, PTR_ERR(clk), "Failed to get osc clock\n"); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*pmc_adc)); + if (!indio_dev) + return -ENOMEM; + + pmc_adc = iio_priv(indio_dev); + pmc_adc->client = client; + + val = i2c_smbus_read_byte_data(pmc_adc->client, PMC_ADC_CMD_REQUEST_PROTOCOL_VERSION); + if (val < 0) + return dev_err_probe(&client->dev, val, "Failed to get protocol version\n"); + + if (val != 0x01) + return dev_err_probe(&client->dev, -EINVAL, + "Unsupported protocol version 0x%02x\n", val); + + indio_dev->name = "pmc_adc"; + indio_dev->info = &pmc_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = pmc_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(pmc_adc_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id pmc_adc_of_match[] = { + { .compatible = "gehc,pmc-adc"}, + { } +}; +MODULE_DEVICE_TABLE(of, pmc_adc_of_match); + +static const struct i2c_device_id pmc_adc_id_table[] = { + { "pmc-adc" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pmc_adc_id_table); + +static struct i2c_driver pmc_adc_i2c_driver = { + .driver = { + .name = "pmc-adc", + .of_match_table = pmc_adc_of_match, + }, + .id_table = pmc_adc_id_table, + .probe = pmc_adc_probe, +}; + +module_i2c_driver(pmc_adc_i2c_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("GE HealthCare PMC ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index fb635a7564403..689e34f069877 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -132,7 +132,7 @@ static int hi8435_read_event_config(struct iio_dev *idev, static int hi8435_write_event_config(struct iio_dev *idev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct hi8435_priv *priv = iio_priv(idev); int ret; diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c index fe82198170d59..3d19d7d744aa4 100644 --- a/drivers/iio/adc/imx8qxp-adc.c +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -487,7 +487,7 @@ MODULE_DEVICE_TABLE(of, imx8qxp_adc_match); static struct platform_driver imx8qxp_adc_driver = { .probe = imx8qxp_adc_probe, - .remove_new = imx8qxp_adc_remove, + .remove = imx8qxp_adc_remove, .driver = { .name = ADC_DRIVER_NAME, .of_match_table = imx8qxp_adc_match, diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c index 4ccf4819f1f13..002eb19587d67 100644 --- a/drivers/iio/adc/imx93_adc.c +++ b/drivers/iio/adc/imx93_adc.c @@ -470,7 +470,7 @@ MODULE_DEVICE_TABLE(of, imx93_adc_match); static struct platform_driver imx93_adc_driver = { .probe = imx93_adc_probe, - .remove_new = imx93_adc_remove, + .remove = imx93_adc_remove, .driver = { .name = IMX93_ADC_DRIVER_NAME, .of_match_table = imx93_adc_match, diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index 30733252aa56f..c178850eaaab1 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -164,7 +164,7 @@ static const struct iio_chan_spec mrfld_adc_channels[] = { BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6), }; -static struct iio_map iio_maps[] = { +static const struct iio_map iio_maps[] = { IIO_MAP("CH0", "bcove-battery", "VBATRSLT"), IIO_MAP("CH1", "bcove-battery", "BATTID"), IIO_MAP("CH2", "bcove-battery", "IBATRSLT"), diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c index 6d9b354bc705b..33bf8aef79e36 100644 --- a/drivers/iio/adc/lp8788_adc.c +++ b/drivers/iio/adc/lp8788_adc.c @@ -26,7 +26,7 @@ struct lp8788_adc { struct lp8788 *lp; - struct iio_map *map; + const struct iio_map *map; struct mutex lock; }; @@ -149,17 +149,9 @@ static const struct iio_chan_spec lp8788_adc_channels[] = { }; /* default maps used by iio consumer (lp8788-charger driver) */ -static struct iio_map lp8788_default_iio_maps[] = { - { - .consumer_dev_name = "lp8788-charger", - .consumer_channel = "lp8788_vbatt_5p0", - .adc_channel_label = "VBATT_5P0", - }, - { - .consumer_dev_name = "lp8788-charger", - .consumer_channel = "lp8788_adc1", - .adc_channel_label = "ADC1", - }, +static const struct iio_map lp8788_default_iio_maps[] = { + IIO_MAP("VBATT_5P0", "lp8788-charger", "lp8788_vbatt_5p0"), + IIO_MAP("ADC1", "lp8788-charger", "lp8788_adc1"), { } }; @@ -168,7 +160,7 @@ static int lp8788_iio_map_register(struct device *dev, struct lp8788_platform_data *pdata, struct lp8788_adc *adc) { - struct iio_map *map; + const struct iio_map *map; int ret; map = (!pdata || !pdata->adc_pdata) ? diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c index 996f6cbbed3ce..ad8ddf80310e9 100644 --- a/drivers/iio/adc/ltc2497-core.c +++ b/drivers/iio/adc/ltc2497-core.c @@ -168,6 +168,7 @@ static const struct iio_info ltc2497core_info = { int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) { struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + struct iio_map *plat_data = dev_get_platdata(dev); int ret; /* @@ -200,16 +201,10 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) return ret; } - if (dev->platform_data) { - struct iio_map *plat_data; - - plat_data = (struct iio_map *)dev->platform_data; - - ret = iio_map_array_register(indio_dev, plat_data); - if (ret) { - dev_err(&indio_dev->dev, "iio map err: %d\n", ret); - goto err_regulator_disable; - } + ret = iio_map_array_register(indio_dev, plat_data); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + goto err_regulator_disable; } ddata->addr_prev = LTC2497_CONFIG_DEFAULT; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index d0c6e94f7204e..9a0baea08ab61 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -392,7 +393,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, if (data < 0) return data; - data = (rxbuf[1] | rxbuf[0] << 8) & + data = get_unaligned_be16(rxbuf) & ((1 << st->chip_info->bits) - 1); } else { /* Get reading */ @@ -943,7 +944,7 @@ static inline int __max1363_check_event_mask(int thismask, int checkmask) static int max1363_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct max1363_state *st = iio_priv(indio_dev); diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c index ffec22be2d593..971e6e5dee9b1 100644 --- a/drivers/iio/adc/max34408.c +++ b/drivers/iio/adc/max34408.c @@ -161,7 +161,7 @@ static int max34408_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* - * calcluate current for 8bit ADC with Rsense + * calculate current for 8bit ADC with Rsense * value. * 10 mV * 1000 / Rsense uOhm = max current * (max current * adc val * 1000) / (2^8 - 1) mA diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index e16b0e28974e8..2d475b43e717f 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1483,7 +1483,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops, static struct platform_driver meson_sar_adc_driver = { .probe = meson_sar_adc_probe, - .remove_new = meson_sar_adc_remove, + .remove = meson_sar_adc_remove, .driver = { .name = "meson-saradc", .of_match_table = meson_sar_adc_of_match, diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 5fbf9b6abd9c7..1cb043b174377 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -52,7 +52,7 @@ static struct iio_chan_spec mp2629_channels[] = { MP2629_ADC_CHAN(INPUT_CURRENT, IIO_CURRENT) }; -static struct iio_map mp2629_adc_maps[] = { +static const struct iio_map mp2629_adc_maps[] = { MP2629_MAP(BATT_VOLT, "batt-volt"), MP2629_MAP(SYSTEM_VOLT, "system-volt"), MP2629_MAP(INPUT_VOLT, "input-volt"), @@ -195,7 +195,7 @@ static struct platform_driver mp2629_adc_driver = { .of_match_table = mp2629_adc_of_match, }, .probe = mp2629_adc_probe, - .remove_new = mp2629_adc_remove, + .remove = mp2629_adc_remove, }; module_platform_driver(mp2629_adc_driver); diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index 83161e6d29b90..4eb2455d6ffac 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -124,7 +124,7 @@ static int mt6360_adc_read_channel(struct mt6360_adc_data *mad, int channel, int usleep_range(ADC_LOOP_TIME_US / 2, ADC_LOOP_TIME_US); } - *val = rpt[1] << 8 | rpt[2]; + *val = get_unaligned_be16(&rpt[1]); ret = IIO_VAL_INT; out_adc_conv: diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index 8c7b64e78dbbc..152cbe265e1a2 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -819,10 +819,10 @@ static void mxs_lradc_adc_remove(struct platform_device *pdev) static struct platform_driver mxs_lradc_adc_driver = { .driver = { - .name = "mxs-lradc-adc", + .name = "mxs-lradc-adc", }, - .probe = mxs_lradc_adc_probe, - .remove_new = mxs_lradc_adc_remove, + .probe = mxs_lradc_adc_probe, + .remove = mxs_lradc_adc_remove, }; module_platform_driver(mxs_lradc_adc_driver); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index 3a55465951e79..7c1511ee3a4b4 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -337,7 +337,7 @@ static void npcm_adc_remove(struct platform_device *pdev) static struct platform_driver npcm_adc_driver = { .probe = npcm_adc_probe, - .remove_new = npcm_adc_remove, + .remove = npcm_adc_remove, .driver = { .name = "npcm_adc", .of_match_table = npcm_adc_match, diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index 36e813d9c73f1..b0f6727cfe383 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -241,7 +241,7 @@ static inline void pac1921_calc_scale(int dividend, int divisor, int *val, s64 tmp; tmp = div_s64(dividend * (s64)NANO, divisor); - *val = (int)div_s64_rem(tmp, NANO, val2); + *val = div_s64_rem(tmp, NANO, val2); } /* @@ -260,7 +260,7 @@ static void pac1921_calc_current_scales(struct pac1921_priv *priv) int max = (PAC1921_MAX_VSENSE_MV * MICRO) >> i; int vsense_lsb = DIV_ROUND_CLOSEST(max, PAC1921_RES_RESOLUTION); - pac1921_calc_scale(vsense_lsb, (int)priv->rshunt_uohm, + pac1921_calc_scale(vsense_lsb, priv->rshunt_uohm, &priv->current_scales[i][0], &priv->current_scales[i][1]); } @@ -314,7 +314,7 @@ static int pac1921_check_push_overflow(struct iio_dev *indio_dev, s64 timestamp) timestamp); } - priv->prev_ovf_flags = (u8)flags; + priv->prev_ovf_flags = flags; return 0; } @@ -329,8 +329,7 @@ static int pac1921_check_push_overflow(struct iio_dev *indio_dev, s64 timestamp) static int pac1921_read_res(struct pac1921_priv *priv, unsigned long reg, u16 *val) { - int ret = regmap_bulk_read(priv->regmap, (unsigned int)reg, val, - sizeof(*val)); + int ret = regmap_bulk_read(priv->regmap, reg, val, sizeof(*val)); if (ret) return ret; @@ -366,7 +365,7 @@ static int pac1921_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - *val = (int)res_val; + *val = res_val; return IIO_VAL_INT; } @@ -400,10 +399,10 @@ static int pac1921_read_raw(struct iio_dev *indio_dev, s64 tmp = curr_scale[0] * (s64)NANO + curr_scale[1]; /* Multiply by max_vbus (V) / dv_gain */ - tmp *= PAC1921_MAX_VBUS_V >> (int)priv->dv_gain; + tmp *= PAC1921_MAX_VBUS_V >> priv->dv_gain; /* Convert back to INT_PLUS_NANO */ - *val = (int)div_s64_rem(tmp, NANO, val2); + *val = div_s64_rem(tmp, NANO, val2); return IIO_VAL_INT_PLUS_NANO; } @@ -426,7 +425,7 @@ static int pac1921_read_raw(struct iio_dev *indio_dev, * 1/(integr_period_usecs/MICRO) = MICRO/integr_period_usecs */ *val = MICRO; - *val2 = (int)priv->integr_period_usecs; + *val2 = priv->integr_period_usecs; return IIO_VAL_FRACTIONAL; default: @@ -503,7 +502,7 @@ static int pac1921_lookup_scale(const int (*const scales_tbl)[2], size_t size, for (unsigned int i = 0; i < size; i++) if (scales_tbl[i][0] == scale_val && scales_tbl[i][1] == scale_val2) - return (int)i; + return i; return -EINVAL; } @@ -553,7 +552,7 @@ static int pac1921_update_gain_from_scale(struct pac1921_priv *priv, if (ret < 0) return ret; - return pac1921_update_gain(priv, &priv->dv_gain, (u8)ret, + return pac1921_update_gain(priv, &priv->dv_gain, ret, PAC1921_GAIN_DV_GAIN_MASK); case PAC1921_CHAN_VSENSE: ret = pac1921_lookup_scale(pac1921_vsense_scales, @@ -562,7 +561,7 @@ static int pac1921_update_gain_from_scale(struct pac1921_priv *priv, if (ret < 0) return ret; - return pac1921_update_gain(priv, &priv->di_gain, (u8)ret, + return pac1921_update_gain(priv, &priv->di_gain, ret, PAC1921_GAIN_DI_GAIN_MASK); case PAC1921_CHAN_CURRENT: ret = pac1921_lookup_scale(priv->current_scales, @@ -571,7 +570,7 @@ static int pac1921_update_gain_from_scale(struct pac1921_priv *priv, if (ret < 0) return ret; - return pac1921_update_gain(priv, &priv->di_gain, (u8)ret, + return pac1921_update_gain(priv, &priv->di_gain, ret, PAC1921_GAIN_DI_GAIN_MASK); default: return -EINVAL; @@ -586,7 +585,7 @@ static int pac1921_lookup_int_num_samples(int num_samples) { for (unsigned int i = 0; i < ARRAY_SIZE(pac1921_int_num_samples); i++) if (pac1921_int_num_samples[i] == num_samples) - return (int)i; + return i; return -EINVAL; } @@ -607,7 +606,7 @@ static int pac1921_update_int_num_samples(struct pac1921_priv *priv, if (ret < 0) return ret; - n_samples = (u8)ret; + n_samples = ret; if (priv->n_samples == n_samples) return 0; @@ -700,7 +699,8 @@ static int pac1921_read_event_config(struct iio_dev *indio_dev, static int pac1921_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { struct pac1921_priv *priv = iio_priv(indio_dev); u8 ovf_bit; @@ -770,7 +770,7 @@ static ssize_t pac1921_read_shunt_resistor(struct iio_dev *indio_dev, guard(mutex)(&priv->lock); - vals[0] = (int)priv->rshunt_uohm; + vals[0] = priv->rshunt_uohm; vals[1] = MICRO; return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); @@ -793,13 +793,13 @@ static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, if (ret) return ret; - rshunt_uohm = (u32)val * MICRO + (u32)val_fract; + rshunt_uohm = val * MICRO + val_fract; if (rshunt_uohm == 0 || rshunt_uohm > INT_MAX) return -EINVAL; guard(mutex)(&priv->lock); - priv->rshunt_uohm = (u32)rshunt_uohm; + priv->rshunt_uohm = rshunt_uohm; pac1921_calc_current_scales(priv); @@ -1077,7 +1077,7 @@ static int pac1921_init(struct pac1921_priv *priv) /* * Init control register: * - VPower free run integration mode - * - OUT pin full scale range: 3V (HW detault) + * - OUT pin full scale range: 3V (HW default) * - no timeout, no sleep, no sleep override, no recalc (HW defaults) */ val = FIELD_PREP(PAC1921_CONTROL_MXSL_MASK, @@ -1168,10 +1168,12 @@ static int pac1921_probe(struct i2c_client *client) priv->regmap = devm_regmap_init_i2c(client, &pac1921_regmap_config); if (IS_ERR(priv->regmap)) - return dev_err_probe(dev, (int)PTR_ERR(priv->regmap), + return dev_err_probe(dev, PTR_ERR(priv->regmap), "Cannot initialize register map\n"); - devm_mutex_init(dev, &priv->lock); + ret = devm_mutex_init(dev, &priv->lock); + if (ret) + return ret; priv->dv_gain = PAC1921_DEFAULT_DV_GAIN; priv->di_gain = PAC1921_DEFAULT_DI_GAIN; @@ -1191,7 +1193,7 @@ static int pac1921_probe(struct i2c_client *client) priv->vdd = devm_regulator_get(dev, "vdd"); if (IS_ERR(priv->vdd)) - return dev_err_probe(dev, (int)PTR_ERR(priv->vdd), + return dev_err_probe(dev, PTR_ERR(priv->vdd), "Cannot get vdd regulator\n"); ret = regulator_enable(priv->vdd); diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 7ef249d832866..20802b7f49ea8 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1507,7 +1507,7 @@ static int pac1934_probe(struct i2c_client *client) indio_dev->name = pac1934_chip_config[ret].name; } - if (acpi_match_device(dev->driver->acpi_match_table, dev)) + if (is_acpi_device_node(dev_fwnode(dev))) ret = pac1934_acpi_parse_channel_config(client, info); else /* diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 203cbbc707198..d283ee8fb1d2f 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -456,7 +456,7 @@ static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, * raw high threshold = (ideal threshold + INL) * gain error + offset error * * The gain error include both gain error, as specified in the datasheet, and - * the gain error drift. These paramenters vary depending on device and whether + * the gain error drift. These parameters vary depending on device and whether * the channel is calibrated (trimmed) or not. */ static int palmas_gpadc_threshold_with_tolerance(int val, const int INL, @@ -676,7 +676,7 @@ static int palmas_gpadc_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct palmas_gpadc *adc = iio_priv(indio_dev); int adc_chan = chan->channel; diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 9e1112f5acc6a..31f88cf7f7f18 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -821,7 +821,6 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc) { - struct fwnode_handle *child; struct pm8xxx_chan_info *ch; int ret; int i; @@ -844,16 +843,15 @@ static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc) return -ENOMEM; i = 0; - device_for_each_child_node(adc->dev, child) { + device_for_each_child_node_scoped(adc->dev, child) { ch = &adc->chans[i]; ret = pm8xxx_xoadc_parse_channel(adc->dev, child, adc->variant->channels, &adc->iio_chans[i], ch); - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } + i++; } @@ -1016,7 +1014,7 @@ static struct platform_driver pm8xxx_xoadc_driver = { .of_match_table = pm8xxx_xoadc_id_table, }, .probe = pm8xxx_xoadc_probe, - .remove_new = pm8xxx_xoadc_remove, + .remove = pm8xxx_xoadc_remove, }; module_platform_driver(pm8xxx_xoadc_driver); diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 9b69f40beed8e..af3c2f659f5e9 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -830,7 +830,7 @@ static int adc5_get_fw_data(struct adc5_chip *adc) adc->nchannels = device_get_child_node_count(adc->dev); if (!adc->nchannels) - return -EINVAL; + return dev_err_probe(adc->dev, -EINVAL, "no channels defined\n"); adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels, sizeof(*adc->iio_chans), GFP_KERNEL); @@ -903,7 +903,7 @@ static int adc5_probe(struct platform_device *pdev) ret = adc5_get_fw_data(adc); if (ret) - return dev_err_probe(dev, ret, "adc get dt data failed\n"); + return ret; irq_eoc = platform_get_irq(pdev, 0); if (irq_eoc < 0) { diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c index f5c6f1f27b2c7..00a7f09820254 100644 --- a/drivers/iio/adc/qcom-spmi-vadc.c +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -754,7 +754,6 @@ static int vadc_get_fw_data(struct vadc_priv *vadc) const struct vadc_channels *vadc_chan; struct iio_chan_spec *iio_chan; struct vadc_channel_prop prop; - struct fwnode_handle *child; unsigned int index = 0; int ret; @@ -774,12 +773,10 @@ static int vadc_get_fw_data(struct vadc_priv *vadc) iio_chan = vadc->iio_chans; - device_for_each_child_node(vadc->dev, child) { + device_for_each_child_node_scoped(vadc->dev, child) { ret = vadc_get_fw_channel_data(vadc->dev, &prop, child); - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type; vadc->chan_props[index] = prop; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 15a21d2860e75..11170b5852d17 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -592,7 +592,7 @@ static const struct dev_pm_ops rcar_gyroadc_pm_ops = { static struct platform_driver rcar_gyroadc_driver = { .probe = rcar_gyroadc_probe, - .remove_new = rcar_gyroadc_remove, + .remove = rcar_gyroadc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = rcar_gyroadc_match, diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c index ce5f3011fe00c..b33536157adc9 100644 --- a/drivers/iio/adc/rn5t618-adc.c +++ b/drivers/iio/adc/rn5t618-adc.c @@ -185,7 +185,7 @@ static const struct iio_chan_spec rn5t618_adc_iio_channels[] = { RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0") }; -static struct iio_map rn5t618_maps[] = { +static const struct iio_map rn5t618_maps[] = { IIO_MAP("VADP", "rn5t618-power", "vadp"), IIO_MAP("VUSB", "rn5t618-power", "vusb"), { /* sentinel */ } diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 616dd729666aa..2201ee9987ae4 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -906,7 +906,7 @@ MODULE_DEVICE_TABLE(of, stm32_adc_of_match); static struct platform_driver stm32_adc_driver = { .probe = stm32_adc_probe, - .remove_new = stm32_adc_remove, + .remove = stm32_adc_remove, .driver = { .name = "stm32-adc-core", .of_match_table = stm32_adc_of_match, diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 32ca26ed59f7a..9d3b23efcc068 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2644,7 +2644,7 @@ MODULE_DEVICE_TABLE(of, stm32_adc_of_match); static struct platform_driver stm32_adc_driver = { .probe = stm32_adc_probe, - .remove_new = stm32_adc_remove, + .remove = stm32_adc_remove, .driver = { .name = "stm32-adc", .of_match_table = stm32_adc_of_match, diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 2037f73426d4b..c2d4f5339cd45 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1890,7 +1890,7 @@ static struct platform_driver stm32_dfsdm_adc_driver = { .pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops), }, .probe = stm32_dfsdm_adc_probe, - .remove_new = stm32_dfsdm_adc_remove, + .remove = stm32_dfsdm_adc_remove, }; module_platform_driver(stm32_dfsdm_adc_driver); diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index bef59fcc0d807..041dc9ebc0482 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -506,7 +506,7 @@ static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = { static struct platform_driver stm32_dfsdm_driver = { .probe = stm32_dfsdm_probe, - .remove_new = stm32_dfsdm_core_remove, + .remove = stm32_dfsdm_core_remove, .driver = { .name = "stm32-dfsdm", .of_match_table = stm32_dfsdm_of_match, diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c index 6a893d484cf70..136b8d9c294f4 100644 --- a/drivers/iio/adc/sun20i-gpadc-iio.c +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -155,7 +155,6 @@ static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev, unsigned int channel; int num_channels, i, ret; struct iio_chan_spec *channels; - struct fwnode_handle *node; num_channels = device_get_child_node_count(dev); if (num_channels == 0) @@ -167,12 +166,10 @@ static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev, return -ENOMEM; i = 0; - device_for_each_child_node(dev, node) { + device_for_each_child_node_scoped(dev, node) { ret = fwnode_property_read_u32(node, "reg", &channel); - if (ret) { - fwnode_handle_put(node); + if (ret) return dev_err_probe(dev, ret, "invalid channel number\n"); - } channels[i].type = IIO_VOLTAGE; channels[i].indexed = 1; diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 100ecced5fc11..8b27458dcd661 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -114,11 +114,8 @@ struct sun4i_gpadc_iio { .datasheet_name = _name, \ } -static struct iio_map sun4i_gpadc_hwmon_maps[] = { - { - .adc_channel_label = "temp_adc", - .consumer_dev_name = "iio_hwmon.0", - }, +static const struct iio_map sun4i_gpadc_hwmon_maps[] = { + IIO_MAP("temp_adc", "iio_hwmon.0", NULL), { /* sentinel */ }, }; @@ -700,7 +697,7 @@ static struct platform_driver sun4i_gpadc_driver = { }, .id_table = sun4i_gpadc_id, .probe = sun4i_gpadc_probe, - .remove_new = sun4i_gpadc_remove, + .remove = sun4i_gpadc_remove, }; MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_id); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 6d1bc9659946d..47fe8e16aee42 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -806,7 +806,7 @@ static int ads1015_disable_event_config(struct ads1015_data *data, static int ads1015_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct ads1015_data *data = iio_priv(indio_dev); int ret; @@ -1032,8 +1032,7 @@ static int ads1015_probe(struct i2c_client *client) } if (client->irq && chip->has_comparator) { - unsigned long irq_trig = - irqd_get_trigger_type(irq_get_irq_data(client->irq)); + unsigned long irq_trig = irq_get_trigger_type(client->irq); unsigned int cfg_comp_mask = ADS1015_CFG_COMP_QUE_MASK | ADS1015_CFG_COMP_LAT_MASK | ADS1015_CFG_COMP_POL_MASK; unsigned int cfg_comp = diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 1c76063751492..e9d9d4d46d380 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -804,7 +804,7 @@ static const struct of_device_id __maybe_unused ads1119_of_match[] = { MODULE_DEVICE_TABLE(of, ads1119_of_match); static const struct i2c_device_id ads1119_id[] = { - { "ads1119", 0 }, + { "ads1119" }, { } }; MODULE_DEVICE_TABLE(i2c, ads1119_id); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 0f9f75baaebbf..36d43495f603a 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -294,7 +294,7 @@ static int ads1298_get_scale(struct ads1298_private *priv, if (ret) return ret; - /* Refererence in millivolts */ + /* Reference in millivolts */ *val = regval & ADS1298_MASK_CONFIG3_VREF_4V ? 4000 : 2400; } diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 426e3c9f88a16..fe1509d3b1e76 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -494,7 +494,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, /* * We check the complete FIFO. We programmed just one entry but in case * something went wrong we left empty handed (-EAGAIN previously) and - * then the value apeared somehow in the FIFO we would have two entries. + * then the value appeared somehow in the FIFO we would have two entries. * Therefore we read every item and keep only the latest version of the * requested channel. */ @@ -740,12 +740,12 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static struct platform_driver tiadc_driver = { .driver = { - .name = "TI-am335x-adc", - .pm = pm_sleep_ptr(&tiadc_pm_ops), + .name = "TI-am335x-adc", + .pm = pm_sleep_ptr(&tiadc_pm_ops), .of_match_table = ti_adc_dt_ids, }, - .probe = tiadc_probe, - .remove_new = tiadc_remove, + .probe = tiadc_probe, + .remove = tiadc_remove, }; module_platform_driver(tiadc_driver); diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 0253064fadec8..0ea51ddeaa0a0 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -248,7 +248,7 @@ static const struct s16_fract twl4030_divider_ratios[16] = { {15, 100}, /* CHANNEL 11 */ {1, 4}, /* CHANNEL 12 */ {1, 1}, /* CHANNEL 13 Reserved channels */ - {1, 1}, /* CHANNEL 14 Reseved channels */ + {1, 1}, /* CHANNEL 14 Reserved channels */ {5, 11}, /* CHANNEL 15 */ }; @@ -914,7 +914,7 @@ MODULE_DEVICE_TABLE(of, twl_madc_of_match); static struct platform_driver twl4030_madc_driver = { .probe = twl4030_madc_probe, - .remove_new = twl4030_madc_remove, + .remove = twl4030_madc_remove, .driver = { .name = "twl4030_madc", .of_match_table = twl_madc_of_match, diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 6a3db2bce4609..ef7430e6877dc 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -1003,7 +1003,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, static struct platform_driver twl6030_gpadc_driver = { .probe = twl6030_gpadc_probe, - .remove_new = twl6030_gpadc_remove, + .remove = twl6030_gpadc_remove, .driver = { .name = DRIVER_NAME, .pm = pm_sleep_ptr(&twl6030_gpadc_pm_ops), diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 5afd2feb8c3dd..4d83c12975c53 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -972,7 +972,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, static struct platform_driver vf610_adc_driver = { .probe = vf610_adc_probe, - .remove_new = vf610_adc_remove, + .remove = vf610_adc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = vf610_adc_match, diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index ebc583b07e0c0..76dd0343f5f76 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -905,7 +905,7 @@ static int ams_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct ams *ams = iio_priv(indio_dev); unsigned int alarm; diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c index 1bd375fb10e08..c188d3dcab48f 100644 --- a/drivers/iio/adc/xilinx-xadc-events.c +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -121,7 +121,7 @@ int xadc_read_event_config(struct iio_dev *indio_dev, int xadc_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { unsigned int alarm = xadc_get_alarm_mask(chan); struct xadc *xadc = iio_priv(indio_dev); @@ -220,7 +220,7 @@ int xadc_write_event_value(struct iio_dev *indio_dev, /* * Since we store the hysteresis as relative (to the threshold) * value, but the hardware expects an absolute value we need to - * recalcualte this value whenever the hysteresis or the + * recalculate this value whenever the hysteresis or the * threshold changes. */ if (xadc->threshold[offset] < xadc->temp_hysteresis) diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h index 3036f4d613ff5..b4d9d46831172 100644 --- a/drivers/iio/adc/xilinx-xadc.h +++ b/drivers/iio/adc/xilinx-xadc.h @@ -25,7 +25,7 @@ int xadc_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir); int xadc_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state); + enum iio_event_direction dir, bool state); int xadc_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, diff --git a/drivers/iio/addac/ad74115.c b/drivers/iio/addac/ad74115.c index 3ee0dd5537c16..a7e480f2472db 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -191,7 +191,7 @@ enum ad74115_gpio_mode { }; struct ad74115_channels { - struct iio_chan_spec *channels; + const struct iio_chan_spec *channels; unsigned int num_channels; }; @@ -1295,46 +1295,46 @@ static const struct iio_info ad74115_info = { _AD74115_ADC_CHANNEL(_type, index, BIT(IIO_CHAN_INFO_SCALE) \ | BIT(IIO_CHAN_INFO_OFFSET)) -static struct iio_chan_spec ad74115_voltage_input_channels[] = { +static const struct iio_chan_spec ad74115_voltage_input_channels[] = { AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV1), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_voltage_output_channels[] = { +static const struct iio_chan_spec ad74115_voltage_output_channels[] = { AD74115_DAC_CHANNEL(IIO_VOLTAGE, AD74115_DAC_CH_MAIN), AD74115_ADC_CHANNEL(IIO_CURRENT, AD74115_ADC_CH_CONV1), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_current_input_channels[] = { +static const struct iio_chan_spec ad74115_current_input_channels[] = { AD74115_ADC_CHANNEL(IIO_CURRENT, AD74115_ADC_CH_CONV1), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_current_output_channels[] = { +static const struct iio_chan_spec ad74115_current_output_channels[] = { AD74115_DAC_CHANNEL(IIO_CURRENT, AD74115_DAC_CH_MAIN), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV1), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_2_wire_resistance_input_channels[] = { +static const struct iio_chan_spec ad74115_2_wire_resistance_input_channels[] = { _AD74115_ADC_CHANNEL(IIO_RESISTANCE, AD74115_ADC_CH_CONV1, BIT(IIO_CHAN_INFO_PROCESSED)), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_3_4_wire_resistance_input_channels[] = { +static const struct iio_chan_spec ad74115_3_4_wire_resistance_input_channels[] = { AD74115_ADC_CHANNEL(IIO_RESISTANCE, AD74115_ADC_CH_CONV1), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_digital_input_logic_channels[] = { +static const struct iio_chan_spec ad74115_digital_input_logic_channels[] = { AD74115_DAC_CHANNEL(IIO_VOLTAGE, AD74115_DAC_CH_COMPARATOR), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV1), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV2), }; -static struct iio_chan_spec ad74115_digital_input_loop_channels[] = { +static const struct iio_chan_spec ad74115_digital_input_loop_channels[] = { AD74115_DAC_CHANNEL(IIO_CURRENT, AD74115_DAC_CH_MAIN), AD74115_DAC_CHANNEL(IIO_VOLTAGE, AD74115_DAC_CH_COMPARATOR), AD74115_ADC_CHANNEL(IIO_VOLTAGE, AD74115_ADC_CH_CONV1), diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 69c586525d215..daea2bde7acf9 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -6,9 +6,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -45,8 +47,8 @@ struct ad74413r_channel_config { }; struct ad74413r_channels { - struct iio_chan_spec *channels; - unsigned int num_channels; + const struct iio_chan_spec *channels; + unsigned int num_channels; }; struct ad74413r_state { @@ -59,7 +61,7 @@ struct ad74413r_state { unsigned int num_gpo_gpios; unsigned int num_comparator_gpios; u32 sense_resistor_ohms; - + int refin_reg_uv; /* * Synchronize consecutive operations when doing a one-shot * conversion and when updating the ADC samples SPI message. @@ -68,11 +70,9 @@ struct ad74413r_state { const struct ad74413r_chip_info *chip_info; struct spi_device *spi; - struct regulator *refin_reg; struct regmap *regmap; struct device *dev; struct iio_trigger *trig; - struct gpio_desc *reset_gpio; size_t adc_active_channels; struct spi_message adc_samples_msg; @@ -407,12 +407,16 @@ static int ad74413r_gpio_set_comp_config(struct gpio_chip *chip, static int ad74413r_reset(struct ad74413r_state *st) { + struct gpio_desc *reset_gpio; int ret; - if (st->reset_gpio) { - gpiod_set_value_cansleep(st->reset_gpio, 1); + reset_gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + + if (reset_gpio) { fsleep(50); - gpiod_set_value_cansleep(st->reset_gpio, 0); + gpiod_set_value_cansleep(reset_gpio, 0); return 0; } @@ -660,7 +664,7 @@ static int ad74413r_get_output_voltage_scale(struct ad74413r_state *st, static int ad74413r_get_output_current_scale(struct ad74413r_state *st, int *val, int *val2) { - *val = regulator_get_voltage(st->refin_reg); + *val = st->refin_reg_uv; *val2 = st->sense_resistor_ohms * AD74413R_DAC_CODE_MAX * 1000; return IIO_VAL_FRACTIONAL; @@ -861,19 +865,12 @@ static int ad74413r_get_single_adc_result(struct iio_dev *indio_dev, unsigned int channel, int *val) { struct ad74413r_state *st = iio_priv(indio_dev); - int ret; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - mutex_lock(&st->lock); - ret = _ad74413r_get_single_adc_result(st, channel, val); - mutex_unlock(&st->lock); - - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + guard(mutex)(&st->lock); + return _ad74413r_get_single_adc_result(st, channel, val); + } + unreachable(); } static void ad74413r_adc_to_resistance_result(int adc_result, int *val) @@ -895,7 +892,7 @@ static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, unsigned int channel; int ret = -EINVAL; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); spi_message_init(&st->adc_samples_msg); st->adc_active_channels = 0; @@ -903,11 +900,11 @@ static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, for_each_clear_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { ret = ad74413r_set_adc_channel_enable(st, channel, false); if (ret) - goto out; + return ret; } if (*active_scan_mask == 0) - goto out; + return ret; /* * The read select register is used to select which register's value @@ -925,7 +922,7 @@ static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, for_each_set_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { ret = ad74413r_set_adc_channel_enable(st, channel, true); if (ret) - goto out; + return ret; st->adc_active_channels++; @@ -956,11 +953,7 @@ static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, xfer->cs_change = 0; spi_message_add_tail(xfer, &st->adc_samples_msg); - -out: - mutex_unlock(&st->lock); - - return ret; + return 0; } static int ad74413r_buffer_postenable(struct iio_dev *indio_dev) @@ -1138,34 +1131,34 @@ static const struct iio_info ad74413r_info = { AD74413R_ADC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE) \ | BIT(IIO_CHAN_INFO_OFFSET)) -static struct iio_chan_spec ad74413r_voltage_output_channels[] = { +static const struct iio_chan_spec ad74413r_voltage_output_channels[] = { AD74413R_DAC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE)), AD74413R_ADC_CURRENT_CHANNEL, }; -static struct iio_chan_spec ad74413r_current_output_channels[] = { +static const struct iio_chan_spec ad74413r_current_output_channels[] = { AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), AD74413R_ADC_VOLTAGE_CHANNEL, }; -static struct iio_chan_spec ad74413r_voltage_input_channels[] = { +static const struct iio_chan_spec ad74413r_voltage_input_channels[] = { AD74413R_ADC_VOLTAGE_CHANNEL, }; -static struct iio_chan_spec ad74413r_current_input_channels[] = { +static const struct iio_chan_spec ad74413r_current_input_channels[] = { AD74413R_ADC_CURRENT_CHANNEL, }; -static struct iio_chan_spec ad74413r_current_input_loop_channels[] = { +static const struct iio_chan_spec ad74413r_current_input_loop_channels[] = { AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), AD74413R_ADC_CURRENT_CHANNEL, }; -static struct iio_chan_spec ad74413r_resistance_input_channels[] = { +static const struct iio_chan_spec ad74413r_resistance_input_channels[] = { AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), }; -static struct iio_chan_spec ad74413r_digital_input_channels[] = { +static const struct iio_chan_spec ad74413r_digital_input_channels[] = { AD74413R_ADC_VOLTAGE_CHANNEL, }; @@ -1270,7 +1263,8 @@ static int ad74413r_setup_channels(struct iio_dev *indio_dev) { struct ad74413r_state *st = iio_priv(indio_dev); struct ad74413r_channel_config *config; - struct iio_chan_spec *channels, *chans; + const struct iio_chan_spec *chans; + struct iio_chan_spec *channels; unsigned int i, num_chans, chan_i; int ret; @@ -1346,11 +1340,6 @@ static int ad74413r_setup_gpios(struct ad74413r_state *st) return 0; } -static void ad74413r_regulator_disable(void *regulator) -{ - regulator_disable(regulator); -} - static int ad74413r_probe(struct spi_device *spi) { struct ad74413r_state *st; @@ -1369,7 +1358,10 @@ static int ad74413r_probe(struct spi_device *spi) if (!st->chip_info) return -EINVAL; - mutex_init(&st->lock); + ret = devm_mutex_init(st->dev, &st->lock); + if (ret) + return ret; + init_completion(&st->adc_data_completion); st->regmap = devm_regmap_init(st->dev, NULL, st, @@ -1377,23 +1369,11 @@ static int ad74413r_probe(struct spi_device *spi) if (IS_ERR(st->regmap)) return PTR_ERR(st->regmap); - st->reset_gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(st->reset_gpio)) - return PTR_ERR(st->reset_gpio); - - st->refin_reg = devm_regulator_get(st->dev, "refin"); - if (IS_ERR(st->refin_reg)) - return dev_err_probe(st->dev, PTR_ERR(st->refin_reg), - "Failed to get refin regulator\n"); - - ret = regulator_enable(st->refin_reg); - if (ret) - return ret; - - ret = devm_add_action_or_reset(st->dev, ad74413r_regulator_disable, - st->refin_reg); - if (ret) - return ret; + ret = devm_regulator_get_enable_read_voltage(st->dev, "refin"); + if (ret < 0) + return dev_err_probe(st->dev, ret, + "Failed to get refin regulator voltage\n"); + st->refin_reg_uv = ret; st->sense_resistor_ohms = 100000000; device_property_read_u32(st->dev, "shunt-resistor-micro-ohms", diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index 4c03b9e834b88..e64a41bae32c6 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -232,7 +232,7 @@ static int ad7150_write_event_params(struct iio_dev *indio_dev, static int ad7150_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct ad7150_chip_info *chip = iio_priv(indio_dev); int ret = 0; diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 6c87223f58d90..330fe0af946f7 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -50,6 +50,8 @@ config BME680 select REGMAP select BME680_I2C if I2C select BME680_SPI if SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Bosch Sensortec BME680 sensor with temperature, pressure, humidity and gas sensing capability. diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index b2c547ac8d349..00ab89b3138b4 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -2,6 +2,8 @@ #ifndef BME680_H_ #define BME680_H_ +#include + #define BME680_REG_CHIP_ID 0xD0 #define BME680_CHIP_ID_VAL 0x61 #define BME680_REG_SOFT_RESET 0xE0 @@ -25,8 +27,6 @@ #define BME680_OSRS_TEMP_MASK GENMASK(7, 5) #define BME680_OSRS_PRESS_MASK GENMASK(4, 2) #define BME680_MODE_MASK GENMASK(1, 0) -#define BME680_MODE_FORCED 1 -#define BME680_MODE_SLEEP 0 #define BME680_REG_CONFIG 0x75 #define BME680_FILTER_MASK GENMASK(4, 2) @@ -42,6 +42,7 @@ #define BME680_RHRANGE_MASK GENMASK(5, 4) #define BME680_REG_RES_HEAT_VAL 0x00 #define BME680_RSERROR_MASK GENMASK(7, 4) +#define BME680_REG_IDAC_HEAT_0 0x50 #define BME680_REG_RES_HEAT_0 0x5A #define BME680_REG_GAS_WAIT_0 0x64 #define BME680_ADC_GAS_RES GENMASK(15, 6) @@ -63,7 +64,11 @@ #define BME680_MEAS_TRIM_MASK GENMASK(24, 4) -#define BME680_STARTUP_TIME_US 5000 +/* Datasheet Section 1.1, Table 1 */ +#define BME680_STARTUP_TIME_US 2000 + +#define BME680_NUM_CHANNELS 4 +#define BME680_NUM_BULK_READ_REGS 15 /* Calibration Parameters */ #define BME680_T2_LSB_REG 0x8A diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 0b96534c6867a..9783953e64e06 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -16,8 +16,11 @@ #include #include +#include #include #include +#include +#include #include @@ -95,6 +98,19 @@ struct bme680_calib { s8 range_sw_err; }; +/* values of CTRL_MEAS register */ +enum bme680_op_mode { + BME680_MODE_SLEEP = 0, + BME680_MODE_FORCED = 1, +}; + +enum bme680_scan { + BME680_TEMP, + BME680_PRESS, + BME680_HUMID, + BME680_GAS, +}; + struct bme680_data { struct regmap *regmap; struct bme680_calib bme680; @@ -102,11 +118,17 @@ struct bme680_data { u8 oversampling_temp; u8 oversampling_press; u8 oversampling_humid; + u8 preheat_curr_mA; u16 heater_dur; u16 heater_temp; + struct { + s32 chan[4]; + aligned_s64 ts; + } scan; + union { - u8 buf[3]; + u8 buf[BME680_NUM_BULK_READ_REGS]; unsigned int check; __be16 be16; u8 bme680_cal_buf_1[BME680_CALIB_RANGE_1_LEN]; @@ -138,22 +160,66 @@ EXPORT_SYMBOL_NS(bme680_regmap_config, IIO_BME680); static const struct iio_chan_spec bme680_channels[] = { { .type = IIO_TEMP, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, { .type = IIO_PRESSURE, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_HUMIDITYRELATIVE, + /* PROCESSED maintained for ABI backwards compatibility */ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_RESISTANCE, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = 3, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(4), + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .output = 1, + .scan_index = -1, }, }; @@ -224,7 +290,7 @@ static int bme680_read_calib(struct bme680_data *data, calib->res_heat_val = data->bme680_cal_buf_3[RES_HEAT_VAL]; calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, - data->bme680_cal_buf_3[RES_HEAT_RANGE]); + data->bme680_cal_buf_3[RES_HEAT_RANGE]); calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, data->bme680_cal_buf_3[RANGE_SW_ERR]); @@ -438,19 +504,19 @@ static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc, u32 calc_gas_res; /* Look up table for the possible gas range values */ - static const u32 lookupTable[16] = {2147483647u, 2147483647u, - 2147483647u, 2147483647u, 2147483647u, - 2126008810u, 2147483647u, 2130303777u, - 2147483647u, 2147483647u, 2143188679u, - 2136746228u, 2147483647u, 2126008810u, - 2147483647u, 2147483647u}; - - var1 = ((1340 + (5 * (s64) calib->range_sw_err)) * - ((s64) lookupTable[gas_range])) >> 16; + static const u32 lookup_table[16] = { + 2147483647u, 2147483647u, 2147483647u, 2147483647u, + 2147483647u, 2126008810u, 2147483647u, 2130303777u, + 2147483647u, 2147483647u, 2143188679u, 2136746228u, + 2147483647u, 2126008810u, 2147483647u, 2147483647u + }; + + var1 = ((1340LL + (5 * calib->range_sw_err)) * + (lookup_table[gas_range])) >> 16; var2 = ((gas_res_adc << 15) - 16777216) + var1; var3 = ((125000 << (15 - gas_range)) * var1) >> 9; var3 += (var2 >> 1); - calc_gas_res = div64_s64(var3, (s64) var2); + calc_gas_res = div64_s64(var3, (s64)var2); return calc_gas_res; } @@ -468,7 +534,7 @@ static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp) if (temp > 400) /* Cap temperature */ temp = 400; - var1 = (((s32) BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256; + var1 = (((s32)BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256; var2 = (calib->par_gh1 + 784) * (((((calib->par_gh2 + 154009) * temp * 5) / 100) + 3276800) / 10); @@ -502,23 +568,22 @@ static u8 bme680_calc_heater_dur(u16 dur) return durval; } -static int bme680_set_mode(struct bme680_data *data, bool mode) +/* Taken from datasheet, section 5.3.3 */ +static u8 bme680_calc_heater_preheat_current(u8 curr) +{ + return 8 * curr - 1; +} + +static int bme680_set_mode(struct bme680_data *data, enum bme680_op_mode mode) { struct device *dev = regmap_get_device(data->regmap); int ret; - if (mode) { - ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS, - BME680_MODE_MASK, BME680_MODE_FORCED); - if (ret < 0) - dev_err(dev, "failed to set forced mode\n"); - - } else { - ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS, - BME680_MODE_MASK, BME680_MODE_SLEEP); - if (ret < 0) - dev_err(dev, "failed to set sleep mode\n"); - + ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS, + BME680_MODE_MASK, mode); + if (ret < 0) { + dev_err(dev, "failed to set ctrl_meas register\n"); + return ret; } return ret; @@ -546,7 +611,7 @@ static int bme680_wait_for_eoc(struct bme680_data *data) data->oversampling_humid) * 1936) + (477 * 4) + (477 * 5) + 1000 + (data->heater_dur * 1000); - usleep_range(wait_eoc_us, wait_eoc_us + 100); + fsleep(wait_eoc_us); ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &data->check); if (ret) { @@ -571,9 +636,8 @@ static int bme680_chip_config(struct bme680_data *data) int ret; u8 osrs; - osrs = FIELD_PREP( - BME680_OSRS_HUMIDITY_MASK, - bme680_oversampling_to_reg(data->oversampling_humid)); + osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK, + bme680_oversampling_to_reg(data->oversampling_humid)); /* * Highly recommended to set oversampling of humidity before * temperature/pressure oversampling. @@ -587,8 +651,7 @@ static int bme680_chip_config(struct bme680_data *data) /* IIR filter settings */ ret = regmap_update_bits(data->regmap, BME680_REG_CONFIG, - BME680_FILTER_MASK, - BME680_FILTER_COEFF_VAL); + BME680_FILTER_MASK, BME680_FILTER_COEFF_VAL); if (ret < 0) { dev_err(dev, "failed to write config register\n"); return ret; @@ -609,14 +672,27 @@ static int bme680_chip_config(struct bme680_data *data) return 0; } +static int bme680_preheat_curr_config(struct bme680_data *data, u8 val) +{ + struct device *dev = regmap_get_device(data->regmap); + u8 heatr_curr; + int ret; + + heatr_curr = bme680_calc_heater_preheat_current(val); + ret = regmap_write(data->regmap, BME680_REG_IDAC_HEAT_0, heatr_curr); + if (ret < 0) + dev_err(dev, "failed to write idac_heat_0 register\n"); + + return ret; +} + static int bme680_gas_config(struct bme680_data *data) { struct device *dev = regmap_get_device(data->regmap); int ret; u8 heatr_res, heatr_dur; - /* Go to sleep */ - ret = bme680_set_mode(data, false); + ret = bme680_set_mode(data, BME680_MODE_SLEEP); if (ret < 0) return ret; @@ -638,6 +714,10 @@ static int bme680_gas_config(struct bme680_data *data) return ret; } + ret = bme680_preheat_curr_config(data, data->preheat_curr_mA); + if (ret) + return ret; + /* Enable the gas sensor and select heater profile set-point 0 */ ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1, BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK, @@ -649,23 +729,20 @@ static int bme680_gas_config(struct bme680_data *data) return ret; } -static int bme680_read_temp(struct bme680_data *data, int *val) +static int bme680_read_temp(struct bme680_data *data, s16 *comp_temp) { int ret; u32 adc_temp; - s16 comp_temp; ret = bme680_read_temp_adc(data, &adc_temp); if (ret) return ret; - comp_temp = bme680_compensate_temp(data, adc_temp); - *val = comp_temp * 10; /* Centidegrees to millidegrees */ - return IIO_VAL_INT; + *comp_temp = bme680_compensate_temp(data, adc_temp); + return 0; } -static int bme680_read_press(struct bme680_data *data, - int *val, int *val2) +static int bme680_read_press(struct bme680_data *data, u32 *comp_press) { int ret; u32 adc_press; @@ -679,16 +756,14 @@ static int bme680_read_press(struct bme680_data *data, if (ret) return ret; - *val = bme680_compensate_press(data, adc_press, t_fine); - *val2 = 1000; - return IIO_VAL_FRACTIONAL; + *comp_press = bme680_compensate_press(data, adc_press, t_fine); + return 0; } -static int bme680_read_humid(struct bme680_data *data, - int *val, int *val2) +static int bme680_read_humid(struct bme680_data *data, u32 *comp_humidity) { int ret; - u32 adc_humidity, comp_humidity; + u32 adc_humidity; s32 t_fine; ret = bme680_get_t_fine(data, &t_fine); @@ -699,15 +774,11 @@ static int bme680_read_humid(struct bme680_data *data, if (ret) return ret; - comp_humidity = bme680_compensate_humid(data, adc_humidity, t_fine); - - *val = comp_humidity; - *val2 = 1000; - return IIO_VAL_FRACTIONAL; + *comp_humidity = bme680_compensate_humid(data, adc_humidity, t_fine); + return 0; } -static int bme680_read_gas(struct bme680_data *data, - int *val) +static int bme680_read_gas(struct bme680_data *data, int *comp_gas_res) { struct device *dev = regmap_get_device(data->regmap); int ret; @@ -742,9 +813,8 @@ static int bme680_read_gas(struct bme680_data *data, } gas_range = FIELD_GET(BME680_GAS_RANGE_MASK, gas_regs_val); - - *val = bme680_compensate_gas(data, adc_gas_res, gas_range); - return IIO_VAL_INT; + *comp_gas_res = bme680_compensate_gas(data, adc_gas_res, gas_range); + return 0; } static int bme680_read_raw(struct iio_dev *indio_dev, @@ -752,12 +822,12 @@ static int bme680_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct bme680_data *data = iio_priv(indio_dev); - int ret; + int chan_val, ret; + s16 temp_chan_val; guard(mutex)(&data->lock); - /* set forced mode to trigger measurement */ - ret = bme680_set_mode(data, true); + ret = bme680_set_mode(data, BME680_MODE_FORCED); if (ret < 0) return ret; @@ -769,13 +839,77 @@ static int bme680_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: switch (chan->type) { case IIO_TEMP: - return bme680_read_temp(data, val); + ret = bme680_read_temp(data, &temp_chan_val); + if (ret) + return ret; + + *val = temp_chan_val * 10; + return IIO_VAL_INT; case IIO_PRESSURE: - return bme680_read_press(data, val, val2); + ret = bme680_read_press(data, &chan_val); + if (ret) + return ret; + + *val = chan_val; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; case IIO_HUMIDITYRELATIVE: - return bme680_read_humid(data, val, val2); + ret = bme680_read_humid(data, &chan_val); + if (ret) + return ret; + + *val = chan_val; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; case IIO_RESISTANCE: - return bme680_read_gas(data, val); + ret = bme680_read_gas(data, &chan_val); + if (ret) + return ret; + + *val = chan_val; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + ret = bme680_read_temp(data, (s16 *)&chan_val); + if (ret) + return ret; + + *val = chan_val; + return IIO_VAL_INT; + case IIO_PRESSURE: + ret = bme680_read_press(data, &chan_val); + if (ret) + return ret; + + *val = chan_val; + return IIO_VAL_INT; + case IIO_HUMIDITYRELATIVE: + ret = bme680_read_humid(data, &chan_val); + if (ret) + return ret; + + *val = chan_val; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = 10; + return IIO_VAL_INT; + case IIO_PRESSURE: + *val = 1; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_HUMIDITYRELATIVE: + *val = 1; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; default: return -EINVAL; } @@ -836,6 +970,15 @@ static int bme680_write_raw(struct iio_dev *indio_dev, return bme680_chip_config(data); } + case IIO_CHAN_INFO_PROCESSED: + { + switch (chan->type) { + case IIO_CURRENT: + return bme680_preheat_curr_config(data, (u8)val); + default: + return -EINVAL; + } + } default: return -EINVAL; } @@ -861,6 +1004,86 @@ static const struct iio_info bme680_info = { .attrs = &bme680_attribute_group, }; +static const unsigned long bme680_avail_scan_masks[] = { + BIT(BME680_GAS) | BIT(BME680_HUMID) | BIT(BME680_PRESS) | BIT(BME680_TEMP), + 0 +}; + +static irqreturn_t bme680_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + u32 adc_temp, adc_press, adc_humid; + u16 adc_gas_res, gas_regs_val; + u8 gas_range; + s32 t_fine; + int ret; + + guard(mutex)(&data->lock); + + ret = bme680_set_mode(data, BME680_MODE_FORCED); + if (ret < 0) + goto out; + + ret = bme680_wait_for_eoc(data); + if (ret) + goto out; + + ret = regmap_bulk_read(data->regmap, BME680_REG_MEAS_STAT_0, + data->buf, sizeof(data->buf)); + if (ret) { + dev_err(dev, "failed to burst read sensor data\n"); + goto out; + } + if (data->buf[0] & BME680_GAS_MEAS_BIT) { + dev_err(dev, "gas measurement incomplete\n"); + goto out; + } + + /* Temperature calculations */ + adc_temp = FIELD_GET(BME680_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[5])); + if (adc_temp == BME680_MEAS_SKIPPED) { + dev_err(dev, "reading temperature skipped\n"); + goto out; + } + data->scan.chan[0] = bme680_compensate_temp(data, adc_temp); + t_fine = bme680_calc_t_fine(data, adc_temp); + + /* Pressure calculations */ + adc_press = FIELD_GET(BME680_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[2])); + if (adc_press == BME680_MEAS_SKIPPED) { + dev_err(dev, "reading pressure skipped\n"); + goto out; + } + data->scan.chan[1] = bme680_compensate_press(data, adc_press, t_fine); + + /* Humidity calculations */ + adc_humid = get_unaligned_be16(&data->buf[8]); + if (adc_humid == BME680_MEAS_SKIPPED) { + dev_err(dev, "reading humidity skipped\n"); + goto out; + } + data->scan.chan[2] = bme680_compensate_humid(data, adc_humid, t_fine); + + /* Gas calculations */ + gas_regs_val = get_unaligned_be16(&data->buf[13]); + adc_gas_res = FIELD_GET(BME680_ADC_GAS_RES, gas_regs_val); + if ((gas_regs_val & BME680_GAS_STAB_BIT) == 0) { + dev_err(dev, "heater failed to reach the target temperature\n"); + goto out; + } + gas_range = FIELD_GET(BME680_GAS_RANGE_MASK, gas_regs_val); + data->scan.chan[3] = bme680_compensate_gas(data, adc_gas_res, gas_range); + + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + int bme680_core_probe(struct device *dev, struct regmap *regmap, const char *name) { @@ -879,6 +1102,7 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, indio_dev->name = name; indio_dev->channels = bme680_channels; indio_dev->num_channels = ARRAY_SIZE(bme680_channels); + indio_dev->available_scan_masks = bme680_avail_scan_masks; indio_dev->info = &bme680_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -888,13 +1112,13 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, data->oversampling_temp = 8; /* 8X oversampling rate */ data->heater_temp = 320; /* degree Celsius */ data->heater_dur = 150; /* milliseconds */ + data->preheat_curr_mA = 0; - ret = regmap_write(regmap, BME680_REG_SOFT_RESET, - BME680_CMD_SOFTRESET); + ret = regmap_write(regmap, BME680_REG_SOFT_RESET, BME680_CMD_SOFTRESET); if (ret < 0) return dev_err_probe(dev, ret, "Failed to reset chip\n"); - usleep_range(BME680_STARTUP_TIME_US, BME680_STARTUP_TIME_US + 1000); + fsleep(BME680_STARTUP_TIME_US); ret = regmap_read(regmap, BME680_REG_CHIP_ID, &data->check); if (ret < 0) @@ -922,6 +1146,14 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, return dev_err_probe(dev, ret, "failed to set gas config data\n"); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + bme680_trigger_handler, + NULL); + if (ret) + return dev_err_probe(dev, ret, + "iio triggered buffer setup failed\n"); + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(bme680_core_probe, IIO_BME680); diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index f44458c380d92..37d0bdaa8d824 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -70,6 +70,10 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, if (mult != ts->mult) ts->new_mult = mult; + /* When FIFO is off, directly apply the new ODR */ + if (!fifo) + inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + return 0; } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index a0df9250a69ff..a55967208cdc6 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -134,11 +134,11 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, iio_trigger_set_drvdata(sdata->trig, indio_dev); sdata->trig->ops = trigger_ops; - irq_trig = irqd_get_trigger_type(irq_get_irq_data(sdata->irq)); /* * If the IRQ is triggered on falling edge, we need to mark the * interrupt as active low, if the hardware supports this. */ + irq_trig = irq_get_trigger_type(sdata->irq); switch(irq_trig) { case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW: diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 9f5d5ebb86537..5d01ba4edbf30 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -6,9 +6,28 @@ menu "Digital to analog converters" +config AD3552R_HS + tristate "Analog Devices AD3552R DAC High Speed driver" + select AD3552R_LIB + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD3552R + Digital to Analog Converter High Speed driver. + + The driver requires the assistance of an IP core to operate, + since data is streamed into target device via DMA, sent over a + QSPI + DDR (Double Data Rate) bus. + + To compile this driver as a module, choose M here: the + module will be called ad3552r-hs. + +config AD3552R_LIB + tristate + config AD3552R tristate "Analog Devices AD3552R DAC driver" depends on SPI_MASTER + select AD3552R_LIB select IIO_BUFFER select IIO_TRIGGERED_BUFFER help @@ -306,6 +325,19 @@ config AD7303 To compile this driver as module choose M here: the module will be called ad7303. +config AD8460 + tristate "Analog Devices AD8460 DAC driver" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + help + Say yes here to build support for Analog Devices AD8460 Digital to + Analog Converters (DAC). + + To compile this driver as a module choose M here: the module will be called + ad8460. + config AD8801 tristate "Analog Devices AD8801/AD8803 DAC driver" depends on SPI_MASTER diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 2cf148f16306d..414c152be779d 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -4,6 +4,8 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o +obj-$(CONFIG_AD3552R_LIB) += ad3552r-common.o obj-$(CONFIG_AD3552R) += ad3552r.o obj-$(CONFIG_AD5360) += ad5360.o obj-$(CONFIG_AD5380) += ad5380.o @@ -28,6 +30,7 @@ obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o obj-$(CONFIG_AD7293) += ad7293.o obj-$(CONFIG_AD7303) += ad7303.o +obj-$(CONFIG_AD8460) += ad8460.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_AD9739A) += ad9739a.o obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c new file mode 100644 index 0000000000000..2dfeca3656d21 --- /dev/null +++ b/drivers/iio/dac/ad3552r-common.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2010-2024 Analog Devices Inc. +// Copyright (c) 2024 Baylibre, SAS + +#include +#include +#include +#include +#include + +#include "ad3552r.h" + +const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2] = { + [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 }, + [AD3552R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 }, + [AD3552R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 }, + [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 }, + [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = { -10000, 10000 } +}; +EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, IIO_AD3552R); + +const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = { + [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 }, + [AD3542R_CH_OUTPUT_RANGE_0__3V] = { 0, 3000 }, + [AD3542R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 }, + [AD3542R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 }, + [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 }, + [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 } +}; +EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, IIO_AD3552R); + +/* Gain * AD3552R_GAIN_SCALE */ +static const s32 gains_scaling_table[] = { + [AD3552R_CH_GAIN_SCALING_1] = 1000, + [AD3552R_CH_GAIN_SCALING_0_5] = 500, + [AD3552R_CH_GAIN_SCALING_0_25] = 250, + [AD3552R_CH_GAIN_SCALING_0_125] = 125 +}; + +u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs) +{ + return FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1) | + FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, p) | + FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, n) | + FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, abs(goffs)) | + FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, goffs < 0); +} +EXPORT_SYMBOL_NS_GPL(ad3552r_calc_custom_gain, IIO_AD3552R); + +static void ad3552r_get_custom_range(struct ad3552r_ch_data *ch_data, + s32 *v_min, s32 *v_max) +{ + s64 vref, tmp, common, offset, gn, gp; + /* + * From datasheet formula (In Volts): + * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03] + * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03] + * Calculus are converted to milivolts + */ + vref = 2500; + /* 2.5 * 1.03 * 1000 (To mV) */ + common = 2575 * ch_data->rfb; + offset = ch_data->gain_offset; + + gn = gains_scaling_table[ch_data->n]; + tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common; + tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); + *v_max = vref + tmp; + + gp = gains_scaling_table[ch_data->p]; + tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common; + tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); + *v_min = vref - tmp; +} + +void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data, + const struct ad3552r_model_data *model_data) +{ + s32 idx, v_max, v_min, span, rem; + s64 tmp; + + if (ch_data->range_override) { + ad3552r_get_custom_range(ch_data, &v_min, &v_max); + } else { + /* Normal range */ + idx = ch_data->range; + v_min = model_data->ranges_table[idx][0]; + v_max = model_data->ranges_table[idx][1]; + } + + /* + * From datasheet formula: + * Vout = Span * (D / 65536) + Vmin + * Converted to scale and offset: + * Scale = Span / 65536 + * Offset = 65536 * Vmin / Span + * + * Reminders are in micros in order to be printed as + * IIO_VAL_INT_PLUS_MICRO + */ + span = v_max - v_min; + ch_data->scale_int = div_s64_rem(span, 65536, &rem); + /* Do operations in microvolts */ + ch_data->scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000, 65536); + + ch_data->offset_int = div_s64_rem(v_min * 65536, span, &rem); + tmp = (s64)rem * 1000000; + ch_data->offset_dec = div_s64(tmp, span); +} +EXPORT_SYMBOL_NS_GPL(ad3552r_calc_gain_and_offset, IIO_AD3552R); + +int ad3552r_get_ref_voltage(struct device *dev, u32 *val) +{ + int voltage; + int delta = 100000; + + voltage = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (voltage < 0 && voltage != -ENODEV) + return dev_err_probe(dev, voltage, + "Error getting vref voltage\n"); + + if (voltage == -ENODEV) { + if (device_property_read_bool(dev, "adi,vref-out-en")) + *val = AD3552R_INTERNAL_VREF_PIN_2P5V; + else + *val = AD3552R_INTERNAL_VREF_PIN_FLOATING; + + return 0; + } + + if (voltage > 2500000 + delta || voltage < 2500000 - delta) { + dev_warn(dev, "vref-supply must be 2.5V"); + return -EINVAL; + } + + *val = AD3552R_EXTERNAL_VREF_PIN_INPUT; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad3552r_get_ref_voltage, IIO_AD3552R); + +int ad3552r_get_drive_strength(struct device *dev, u32 *val) +{ + int err; + u32 drive_strength; + + err = device_property_read_u32(dev, "adi,sdo-drive-strength", + &drive_strength); + if (err) + return err; + + if (drive_strength > 3) { + dev_err_probe(dev, -EINVAL, + "adi,sdo-drive-strength must be less than 4\n"); + return -EINVAL; + } + + *val = drive_strength; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad3552r_get_drive_strength, IIO_AD3552R); + +int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child, + u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs) +{ + int err; + u32 val; + struct fwnode_handle *gain_child __free(fwnode_handle) = + fwnode_get_named_child_node(child, + "custom-output-range-config"); + + if (!gain_child) + return dev_err_probe(dev, -EINVAL, + "custom-output-range-config mandatory\n"); + + err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val); + if (err) + return dev_err_probe(dev, err, + "adi,gain-scaling-p mandatory\n"); + *gs_p = val; + + err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val); + if (err) + return dev_err_probe(dev, err, + "adi,gain-scaling-n property mandatory\n"); + *gs_n = val; + + err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val); + if (err) + return dev_err_probe(dev, err, + "adi,rfb-ohms mandatory\n"); + *rfb = val; + + err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val); + if (err) + return dev_err_probe(dev, err, + "adi,gain-offset mandatory\n"); + *goffs = val; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad3552r_get_custom_gain, IIO_AD3552R); + +static int ad3552r_find_range(const struct ad3552r_model_data *model_info, + s32 *vals) +{ + int i; + + for (i = 0; i < model_info->num_ranges; i++) + if (vals[0] == model_info->ranges_table[i][0] * 1000 && + vals[1] == model_info->ranges_table[i][1] * 1000) + return i; + + return -EINVAL; +} + +int ad3552r_get_output_range(struct device *dev, + const struct ad3552r_model_data *model_info, + struct fwnode_handle *child, u32 *val) +{ + int ret; + s32 vals[2]; + + /* This property is optional, so returning -ENOENT if missing */ + if (!fwnode_property_present(child, "adi,output-range-microvolt")) + return -ENOENT; + + ret = fwnode_property_read_u32_array(child, + "adi,output-range-microvolt", + vals, 2); + if (ret) + return dev_err_probe(dev, ret, + "invalid adi,output-range-microvolt\n"); + + ret = ad3552r_find_range(model_info, vals); + if (ret < 0) + return dev_err_probe(dev, ret, + "invalid adi,output-range-microvolt value\n"); + + *val = ret; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad3552r_get_output_range, IIO_AD3552R); + +MODULE_DESCRIPTION("ad3552r common functions"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c new file mode 100644 index 0000000000000..d5c704adf5bfe --- /dev/null +++ b/drivers/iio/dac/ad3552r-hs.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD3552R + * Digital to Analog converter driver, High Speed version + * + * Copyright 2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad3552r.h" +#include "ad3552r-hs.h" + +struct ad3552r_hs_state { + const struct ad3552r_model_data *model_data; + struct gpio_desc *reset_gpio; + struct device *dev; + struct iio_backend *back; + bool single_channel; + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH]; + struct ad3552r_hs_platform_data *data; +}; + +static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st, + u32 reg, u32 mask, u32 val, + size_t xfer_size) +{ + u32 rval; + int ret; + + ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size); + if (ret) + return ret; + + rval = (rval & ~mask) | val; + + return st->data->bus_reg_write(st->back, reg, rval, xfer_size); +} + +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + int ret; + int ch = chan->channel; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * Using 4 lanes (QSPI), then using 2 as DDR mode is + * considered always on (considering buffering mode always). + */ + *val = DIV_ROUND_CLOSEST(st->data->bus_sample_data_clock_hz * + 4 * 2, chan->scan_type.realbits); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_RAW: + ret = st->data->bus_reg_read(st->back, + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel), + val, 2); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->ch_data[ch].scale_int; + *val2 = st->ch_data[ch].scale_dec; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = st->ch_data[ch].offset_int; + *val2 = st->ch_data[ch].offset_dec; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad3552r_hs_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + return st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel), + val, 2); + } + unreachable(); + default: + return -EINVAL; + } +} + +static int ad3552r_hs_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + struct iio_backend_data_fmt fmt = { + .type = IIO_BACKEND_DATA_UNSIGNED + }; + int loop_len, val, ret; + + switch (*indio_dev->active_scan_mask) { + case AD3552R_CH0_ACTIVE: + st->single_channel = true; + loop_len = 2; + val = AD3552R_REG_ADDR_CH_DAC_16B(0); + break; + case AD3552R_CH1_ACTIVE: + st->single_channel = true; + loop_len = 2; + val = AD3552R_REG_ADDR_CH_DAC_16B(1); + break; + case AD3552R_CH0_ACTIVE | AD3552R_CH1_ACTIVE: + st->single_channel = false; + loop_len = 4; + val = AD3552R_REG_ADDR_CH_DAC_16B(1); + break; + default: + return -EINVAL; + } + + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_STREAM_MODE, + loop_len, 1); + if (ret) + return ret; + + /* Inform DAC chip to switch into DDR mode */ + ret = ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SPI_CONFIG_DDR, + AD3552R_MASK_SPI_CONFIG_DDR, 1); + if (ret) + return ret; + + /* Inform DAC IP to go for DDR mode from now on */ + ret = iio_backend_ddr_enable(st->back); + if (ret) { + dev_err(st->dev, "could not set DDR mode, not streaming"); + goto exit_err; + } + + ret = iio_backend_data_transfer_addr(st->back, val); + if (ret) + goto exit_err; + + ret = iio_backend_data_format_set(st->back, 0, &fmt); + if (ret) + goto exit_err; + + ret = iio_backend_data_stream_enable(st->back); + if (ret) + goto exit_err; + + return 0; + +exit_err: + ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SPI_CONFIG_DDR, + 0, 1); + + iio_backend_ddr_disable(st->back); + + return ret; +} + +static int ad3552r_hs_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_backend_data_stream_disable(st->back); + if (ret) + return ret; + + /* Inform DAC to set in SDR mode */ + ret = ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SPI_CONFIG_DDR, + 0, 1); + if (ret) + return ret; + + ret = iio_backend_ddr_disable(st->back); + if (ret) + return ret; + + return 0; +} + +static inline int ad3552r_hs_set_output_range(struct ad3552r_hs_state *st, + int ch, unsigned int mode) +{ + int val; + + if (ch == 0) + val = FIELD_PREP(AD3552R_MASK_CH0_RANGE, mode); + else + val = FIELD_PREP(AD3552R_MASK_CH1_RANGE, mode); + + return ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch), + val, 1); +} + +static int ad3552r_hs_reset(struct ad3552r_hs_state *st) +{ + int ret; + + st->reset_gpio = devm_gpiod_get_optional(st->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) + return PTR_ERR(st->reset_gpio); + + if (st->reset_gpio) { + fsleep(10); + gpiod_set_value_cansleep(st->reset_gpio, 0); + } else { + ret = ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_SOFTWARE_RESET, + AD3552R_MASK_SOFTWARE_RESET, 1); + if (ret) + return ret; + } + msleep(100); + + return 0; +} + +static int ad3552r_hs_scratch_pad_test(struct ad3552r_hs_state *st) +{ + int ret, val; + + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD, + AD3552R_SCRATCH_PAD_TEST_VAL1, 1); + if (ret) + return ret; + + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD, + &val, 1); + if (ret) + return ret; + + if (val != AD3552R_SCRATCH_PAD_TEST_VAL1) + return dev_err_probe(st->dev, -EIO, + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read 0x%x\n", + AD3552R_SCRATCH_PAD_TEST_VAL1, val); + + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD, + AD3552R_SCRATCH_PAD_TEST_VAL2, 1); + if (ret) + return ret; + + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD, + &val, 1); + if (ret) + return ret; + + if (val != AD3552R_SCRATCH_PAD_TEST_VAL2) + return dev_err_probe(st->dev, -EIO, + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read 0x%x\n", + AD3552R_SCRATCH_PAD_TEST_VAL2, val); + + return 0; +} + +static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st, + int ch, u16 gain, u16 offset) +{ + int ret; + + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_OFFSET(ch), + offset, 1); + if (ret) + return ret; + + return st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch), + gain, 1); +} + +static int ad3552r_hs_setup(struct ad3552r_hs_state *st) +{ + u16 id; + u16 gain = 0, offset = 0; + u32 ch, val, range; + int ret; + + ret = ad3552r_hs_reset(st); + if (ret) + return ret; + + ret = iio_backend_ddr_disable(st->back); + if (ret) + return ret; + + ret = ad3552r_hs_scratch_pad_test(st); + if (ret) + return ret; + + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L, + &val, 1); + if (ret) + return ret; + + id = val; + + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H, + &val, 1); + if (ret) + return ret; + + id |= val << 8; + if (id != st->model_data->chip_id) + dev_info(st->dev, "Chip ID error. Expected 0x%x, Read 0x%x\n", + AD3552R_ID, id); + + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, + 0, 1); + if (ret) + return ret; + + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_TRANSFER_REGISTER, + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE, + AD3552R_QUAD_SPI) | + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1); + if (ret) + return ret; + + ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL); + if (ret) + return ret; + + ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL); + if (ret) + return ret; + + ret = ad3552r_get_ref_voltage(st->dev, &val); + if (ret < 0) + return ret; + + val = ret; + + ret = ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, + AD3552R_MASK_REFERENCE_VOLTAGE_SEL, + val, 1); + if (ret) + return ret; + + ret = ad3552r_get_drive_strength(st->dev, &val); + if (!ret) { + ret = ad3552r_qspi_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SDO_DRIVE_STRENGTH, + val, 1); + if (ret) + return ret; + } + + device_for_each_child_node_scoped(st->dev, child) { + ret = fwnode_property_read_u32(child, "reg", &ch); + if (ret) + return dev_err_probe(st->dev, ret, + "reg property missing\n"); + + ret = ad3552r_get_output_range(st->dev, st->model_data, child, + &range); + if (ret && ret != -ENOENT) + return ret; + if (ret == -ENOENT) { + ret = ad3552r_get_custom_gain(st->dev, child, + &st->ch_data[ch].p, + &st->ch_data[ch].n, + &st->ch_data[ch].rfb, + &st->ch_data[ch].gain_offset); + if (ret) + return ret; + + gain = ad3552r_calc_custom_gain(st->ch_data[ch].p, + st->ch_data[ch].n, + st->ch_data[ch].gain_offset); + offset = abs(st->ch_data[ch].gain_offset); + + st->ch_data[ch].range_override = 1; + + ret = ad3552r_hs_setup_custom_gain(st, ch, gain, + offset); + if (ret) + return ret; + } else { + st->ch_data[ch].range = range; + + ret = ad3552r_hs_set_output_range(st, ch, range); + if (ret) + return ret; + } + + ad3552r_calc_gain_and_offset(&st->ch_data[ch], st->model_data); + } + + return 0; +} + +static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = { + .postenable = ad3552r_hs_buffer_postenable, + .predisable = ad3552r_hs_buffer_predisable, +}; + +#define AD3552R_CHANNEL(ch) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .output = 1, \ + .indexed = 1, \ + .channel = (ch), \ + .scan_index = (ch), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + } \ +} + +static const struct iio_chan_spec ad3552r_hs_channels[] = { + AD3552R_CHANNEL(0), + AD3552R_CHANNEL(1), +}; + +static const struct iio_info ad3552r_hs_info = { + .read_raw = &ad3552r_hs_read_raw, + .write_raw = &ad3552r_hs_write_raw, +}; + +static int ad3552r_hs_probe(struct platform_device *pdev) +{ + struct ad3552r_hs_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->dev = &pdev->dev; + + st->data = dev_get_platdata(st->dev); + if (!st->data) + return dev_err_probe(st->dev, -ENODEV, "No platform data !"); + + st->back = devm_iio_backend_get(&pdev->dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret = devm_iio_backend_enable(&pdev->dev, st->back); + if (ret) + return ret; + + st->model_data = device_get_match_data(&pdev->dev); + if (!st->model_data) + return -ENODEV; + + indio_dev->name = "ad3552r"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->setup_ops = &ad3552r_hs_buffer_setup_ops; + indio_dev->channels = ad3552r_hs_channels; + indio_dev->num_channels = ARRAY_SIZE(ad3552r_hs_channels); + indio_dev->info = &ad3552r_hs_info; + + ret = devm_iio_backend_request_buffer(&pdev->dev, st->back, indio_dev); + if (ret) + return ret; + + ret = ad3552r_hs_setup(st); + if (ret) + return ret; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static const struct ad3552r_model_data ad3552r_model_data = { + .model_name = "ad3552r", + .chip_id = AD3552R_ID, + .num_hw_channels = 2, + .ranges_table = ad3552r_ch_ranges, + .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges), +}; + +static const struct of_device_id ad3552r_hs_of_id[] = { + { .compatible = "adi,ad3552r", .data = &ad3552r_model_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ad3552r_hs_of_id); + +static struct platform_driver ad3552r_hs_driver = { + .driver = { + .name = "ad3552r-hs", + .of_match_table = ad3552r_hs_of_id, + }, + .probe = ad3552r_hs_probe, +}; +module_platform_driver(ad3552r_hs_driver); + +MODULE_AUTHOR("Dragos Bogdan "); +MODULE_AUTHOR("Angelo Dureghello "); +MODULE_DESCRIPTION("AD3552R Driver - High Speed version"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BACKEND); +MODULE_IMPORT_NS(IIO_AD3552R); diff --git a/drivers/iio/dac/ad3552r-hs.h b/drivers/iio/dac/ad3552r-hs.h new file mode 100644 index 0000000000000..724261d38dea3 --- /dev/null +++ b/drivers/iio/dac/ad3552r-hs.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2024 Analog Devices Inc. + * Copyright (c) 2024 Baylibre, SAS + */ +#ifndef __LINUX_PLATFORM_DATA_AD3552R_HS_H__ +#define __LINUX_PLATFORM_DATA_AD3552R_HS_H__ + +struct iio_backend; + +struct ad3552r_hs_platform_data { + int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val, + size_t data_size); + int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val, + size_t data_size); + u32 bus_sample_data_clock_hz; +}; + +#endif /* __LINUX_PLATFORM_DATA_AD3552R_HS_H__ */ diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index 7d61b2fe66243..92688d958f4fe 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -6,271 +6,15 @@ * Copyright 2021 Analog Devices Inc. */ #include +#include #include #include #include #include #include -#include #include -/* Register addresses */ -/* Primary address space */ -#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00 -#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0)) -#define AD3552R_MASK_ADDR_ASCENSION BIT(5) -#define AD3552R_MASK_SDO_ACTIVE BIT(4) -#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01 -#define AD3552R_MASK_SINGLE_INST BIT(7) -#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3) -#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02 -#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n)) -#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2) -#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0) -#define AD3552R_REG_ADDR_CHIP_TYPE 0x03 -#define AD3552R_MASK_CLASS GENMASK(7, 0) -#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04 -#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05 -#define AD3552R_REG_ADDR_CHIP_GRADE 0x06 -#define AD3552R_MASK_GRADE GENMASK(7, 4) -#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0) -#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A -#define AD3552R_REG_ADDR_SPI_REVISION 0x0B -#define AD3552R_REG_ADDR_VENDOR_L 0x0C -#define AD3552R_REG_ADDR_VENDOR_H 0x0D -#define AD3552R_REG_ADDR_STREAM_MODE 0x0E -#define AD3552R_MASK_LENGTH GENMASK(7, 0) -#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F -#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6) -#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2) -#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10 -#define AD3552R_MASK_CRC_ENABLE (GENMASK(7, 6) |\ - GENMASK(1, 0)) -#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5) -#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11 -#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7) -#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5) -#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3) -#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2) -#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1) -#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0) -#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14 -#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6) -#define AD3552R_MASK_MEM_CRC_EN BIT(4) -#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2) -#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1) -#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0) -#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15 -#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6) -#define AD3552R_MASK_SAMPLE_HOLD_DIFFERENTIAL_USER_EN BIT(5) -#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3) -#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2) -#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0) -#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16 -#define AD3552R_MASK_REF_RANGE_ALARM BIT(6) -#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5) -#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4) -#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3) -#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2) -#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1) -#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0) -#define AD3552R_REG_ADDR_ERR_STATUS 0x17 -#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6) -#define AD3552R_MASK_DUAL_SPI_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5) -#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4) -#define AD3552R_MASK_RESET_STATUS BIT(0) -#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18 -#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch)) -#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch) -#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19 -#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) ((ch) ? GENMASK(7, 4) :\ - GENMASK(3, 0)) -#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2) -#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0) -#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2) -#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7) -#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5) -#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3) -#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2) -#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(0) -/* - * Secondary region - * For multibyte registers specify the highest address because the access is - * done in descending order - */ -#define AD3552R_SECONDARY_REGION_START 0x28 -#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28 -#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - ch) * 2) -#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E -#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F -#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31 -#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32 -#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - ch) * 2) -/* 3 bytes registers */ -#define AD3552R_REG_START_24B 0x37 -#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37 -#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - ch) * 3) -#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40 -#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41 -#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44 -#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45 -#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - ch) * 3) - -/* Useful defines */ -#define AD3552R_MAX_CH 2 -#define AD3552R_MASK_CH(ch) BIT(ch) -#define AD3552R_MASK_ALL_CH GENMASK(1, 0) -#define AD3552R_MAX_REG_SIZE 3 -#define AD3552R_READ_BIT BIT(7) -#define AD3552R_ADDR_MASK GENMASK(6, 0) -#define AD3552R_MASK_DAC_12B 0xFFF0 -#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8 -#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34 -#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2 -#define AD3552R_GAIN_SCALE 1000 -#define AD3552R_LDAC_PULSE_US 100 - -enum ad3552r_ch_vref_select { - /* Internal source with Vref I/O floating */ - AD3552R_INTERNAL_VREF_PIN_FLOATING, - /* Internal source with Vref I/O at 2.5V */ - AD3552R_INTERNAL_VREF_PIN_2P5V, - /* External source with Vref I/O as input */ - AD3552R_EXTERNAL_VREF_PIN_INPUT -}; - -enum ad3552r_id { - AD3541R_ID = 0x400b, - AD3542R_ID = 0x4009, - AD3551R_ID = 0x400a, - AD3552R_ID = 0x4008, -}; - -enum ad3552r_ch_output_range { - /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ - AD3552R_CH_OUTPUT_RANGE_0__2P5V, - /* Range from 0 V to 5 V. Requires Rfb1x connection */ - AD3552R_CH_OUTPUT_RANGE_0__5V, - /* Range from 0 V to 10 V. Requires Rfb2x connection */ - AD3552R_CH_OUTPUT_RANGE_0__10V, - /* Range from -5 V to 5 V. Requires Rfb2x connection */ - AD3552R_CH_OUTPUT_RANGE_NEG_5__5V, - /* Range from -10 V to 10 V. Requires Rfb4x connection */ - AD3552R_CH_OUTPUT_RANGE_NEG_10__10V, -}; - -static const s32 ad3552r_ch_ranges[][2] = { - [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500}, - [AD3552R_CH_OUTPUT_RANGE_0__5V] = {0, 5000}, - [AD3552R_CH_OUTPUT_RANGE_0__10V] = {0, 10000}, - [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000}, - [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = {-10000, 10000} -}; - -enum ad3542r_ch_output_range { - /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ - AD3542R_CH_OUTPUT_RANGE_0__2P5V, - /* Range from 0 V to 3 V. Requires Rfb1x connection */ - AD3542R_CH_OUTPUT_RANGE_0__3V, - /* Range from 0 V to 5 V. Requires Rfb1x connection */ - AD3542R_CH_OUTPUT_RANGE_0__5V, - /* Range from 0 V to 10 V. Requires Rfb2x connection */ - AD3542R_CH_OUTPUT_RANGE_0__10V, - /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */ - AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V, - /* Range from -5 V to 5 V. Requires Rfb2x connection */ - AD3542R_CH_OUTPUT_RANGE_NEG_5__5V, -}; - -static const s32 ad3542r_ch_ranges[][2] = { - [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500}, - [AD3542R_CH_OUTPUT_RANGE_0__3V] = {0, 3000}, - [AD3542R_CH_OUTPUT_RANGE_0__5V] = {0, 5000}, - [AD3542R_CH_OUTPUT_RANGE_0__10V] = {0, 10000}, - [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = {-2500, 7500}, - [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000} -}; - -enum ad3552r_ch_gain_scaling { - /* Gain scaling of 1 */ - AD3552R_CH_GAIN_SCALING_1, - /* Gain scaling of 0.5 */ - AD3552R_CH_GAIN_SCALING_0_5, - /* Gain scaling of 0.25 */ - AD3552R_CH_GAIN_SCALING_0_25, - /* Gain scaling of 0.125 */ - AD3552R_CH_GAIN_SCALING_0_125, -}; - -/* Gain * AD3552R_GAIN_SCALE */ -static const s32 gains_scaling_table[] = { - [AD3552R_CH_GAIN_SCALING_1] = 1000, - [AD3552R_CH_GAIN_SCALING_0_5] = 500, - [AD3552R_CH_GAIN_SCALING_0_25] = 250, - [AD3552R_CH_GAIN_SCALING_0_125] = 125 -}; - -enum ad3552r_dev_attributes { - /* - Direct register values */ - /* From 0-3 */ - AD3552R_SDO_DRIVE_STRENGTH, - /* - * 0 -> Internal Vref, vref_io pin floating (default) - * 1 -> Internal Vref, vref_io driven by internal vref - * 2 or 3 -> External Vref - */ - AD3552R_VREF_SELECT, - /* Read registers in ascending order if set. Else descending */ - AD3552R_ADDR_ASCENSION, -}; - -enum ad3552r_ch_attributes { - /* DAC powerdown */ - AD3552R_CH_DAC_POWERDOWN, - /* DAC amplifier powerdown */ - AD3552R_CH_AMPLIFIER_POWERDOWN, - /* Select the output range. Select from enum ad3552r_ch_output_range */ - AD3552R_CH_OUTPUT_RANGE_SEL, - /* - * Over-rider the range selector in order to manually set the output - * voltage range - */ - AD3552R_CH_RANGE_OVERRIDE, - /* Manually set the offset voltage */ - AD3552R_CH_GAIN_OFFSET, - /* Sets the polarity of the offset. */ - AD3552R_CH_GAIN_OFFSET_POLARITY, - /* PDAC gain scaling */ - AD3552R_CH_GAIN_SCALING_P, - /* NDAC gain scaling */ - AD3552R_CH_GAIN_SCALING_N, - /* Rfb value */ - AD3552R_CH_RFB, - /* Channel select. When set allow Input -> DAC and Mask -> DAC */ - AD3552R_CH_SELECT, -}; - -struct ad3552r_ch_data { - s32 scale_int; - s32 scale_dec; - s32 offset_int; - s32 offset_dec; - s16 gain_offset; - u16 rfb; - u8 n; - u8 p; - u8 range; - bool range_override; -}; - -struct ad3552r_model_data { - const char *model_name; - enum ad3552r_id chip_id; - unsigned int num_hw_channels; - const s32 (*ranges_table)[2]; - int num_ranges; - bool requires_output_range; -}; +#include "ad3552r.h" struct ad3552r_desc { const struct ad3552r_model_data *model_data; @@ -285,45 +29,6 @@ struct ad3552r_desc { unsigned int num_ch; }; -static const u16 addr_mask_map[][2] = { - [AD3552R_ADDR_ASCENSION] = { - AD3552R_REG_ADDR_INTERFACE_CONFIG_A, - AD3552R_MASK_ADDR_ASCENSION - }, - [AD3552R_SDO_DRIVE_STRENGTH] = { - AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SDO_DRIVE_STRENGTH - }, - [AD3552R_VREF_SELECT] = { - AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, - AD3552R_MASK_REFERENCE_VOLTAGE_SEL - }, -}; - -/* 0 -> reg addr, 1->ch0 mask, 2->ch1 mask */ -static const u16 addr_mask_map_ch[][3] = { - [AD3552R_CH_DAC_POWERDOWN] = { - AD3552R_REG_ADDR_POWERDOWN_CONFIG, - AD3552R_MASK_CH_DAC_POWERDOWN(0), - AD3552R_MASK_CH_DAC_POWERDOWN(1) - }, - [AD3552R_CH_AMPLIFIER_POWERDOWN] = { - AD3552R_REG_ADDR_POWERDOWN_CONFIG, - AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), - AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1) - }, - [AD3552R_CH_OUTPUT_RANGE_SEL] = { - AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, - AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), - AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1) - }, - [AD3552R_CH_SELECT] = { - AD3552R_REG_ADDR_CH_SELECT_16B, - AD3552R_MASK_CH(0), - AD3552R_MASK_CH(1) - } -}; - static u8 _ad3552r_reg_len(u8 addr) { switch (addr) { @@ -399,11 +104,6 @@ static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val) return 0; } -static u16 ad3552r_field_prep(u16 val, u16 mask) -{ - return (val << __ffs(mask)) & mask; -} - /* Update field of a register, shift val if needed */ static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask, u16 val) @@ -416,21 +116,11 @@ static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask, return ret; reg &= ~mask; - reg |= ad3552r_field_prep(val, mask); + reg |= val; return ad3552r_write_reg(dac, addr, reg); } -static int ad3552r_set_ch_value(struct ad3552r_desc *dac, - enum ad3552r_ch_attributes attr, - u8 ch, - u16 val) -{ - /* Update register related to attributes in chip */ - return ad3552r_update_reg_field(dac, addr_mask_map_ch[attr][0], - addr_mask_map_ch[attr][ch + 1], val); -} - #define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) { \ .type = IIO_VOLTAGE, \ .output = true, \ @@ -510,8 +200,14 @@ static int ad3552r_write_raw(struct iio_dev *indio_dev, val); break; case IIO_CHAN_INFO_ENABLE: - err = ad3552r_set_ch_value(dac, AD3552R_CH_DAC_POWERDOWN, - chan->channel, !val); + if (chan->channel == 0) + val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(0), !val); + else + val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(1), !val); + + err = ad3552r_update_reg_field(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG, + AD3552R_MASK_CH_DAC_POWERDOWN(chan->channel), + val); break; default: err = -EINVAL; @@ -715,83 +411,9 @@ static int ad3552r_reset(struct ad3552r_desc *dac) } return ad3552r_update_reg_field(dac, - addr_mask_map[AD3552R_ADDR_ASCENSION][0], - addr_mask_map[AD3552R_ADDR_ASCENSION][1], - val); -} - -static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min, - s32 *v_max) -{ - s64 vref, tmp, common, offset, gn, gp; - /* - * From datasheet formula (In Volts): - * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03] - * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03] - * Calculus are converted to milivolts - */ - vref = 2500; - /* 2.5 * 1.03 * 1000 (To mV) */ - common = 2575 * dac->ch_data[i].rfb; - offset = dac->ch_data[i].gain_offset; - - gn = gains_scaling_table[dac->ch_data[i].n]; - tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common; - tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); - *v_max = vref + tmp; - - gp = gains_scaling_table[dac->ch_data[i].p]; - tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common; - tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); - *v_min = vref - tmp; -} - -static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch) -{ - s32 idx, v_max, v_min, span, rem; - s64 tmp; - - if (dac->ch_data[ch].range_override) { - ad3552r_get_custom_range(dac, ch, &v_min, &v_max); - } else { - /* Normal range */ - idx = dac->ch_data[ch].range; - v_min = dac->model_data->ranges_table[idx][0]; - v_max = dac->model_data->ranges_table[idx][1]; - } - - /* - * From datasheet formula: - * Vout = Span * (D / 65536) + Vmin - * Converted to scale and offset: - * Scale = Span / 65536 - * Offset = 65536 * Vmin / Span - * - * Reminders are in micros in order to be printed as - * IIO_VAL_INT_PLUS_MICRO - */ - span = v_max - v_min; - dac->ch_data[ch].scale_int = div_s64_rem(span, 65536, &rem); - /* Do operations in microvolts */ - dac->ch_data[ch].scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000, - 65536); - - dac->ch_data[ch].offset_int = div_s64_rem(v_min * 65536, span, &rem); - tmp = (s64)rem * 1000000; - dac->ch_data[ch].offset_dec = div_s64(tmp, span); -} - -static int ad3552r_find_range(const struct ad3552r_model_data *model_data, - s32 *vals) -{ - int i; - - for (i = 0; i < model_data->num_ranges; i++) - if (vals[0] == model_data->ranges_table[i][0] * 1000 && - vals[1] == model_data->ranges_table[i][1] * 1000) - return i; - - return -EINVAL; + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_ADDR_ASCENSION, + FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val)); } static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, @@ -799,57 +421,30 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, u32 ch) { struct device *dev = &dac->spi->dev; - u32 val; int err; u8 addr; - u16 reg = 0, offset; - - struct fwnode_handle *gain_child __free(fwnode_handle) - = fwnode_get_named_child_node(child, - "custom-output-range-config"); - if (!gain_child) - return dev_err_probe(dev, -EINVAL, - "mandatory custom-output-range-config property missing\n"); - - dac->ch_data[ch].range_override = 1; - reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE); - - err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val); - if (err) - return dev_err_probe(dev, err, - "mandatory adi,gain-scaling-p property missing\n"); - reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P); - dac->ch_data[ch].p = val; - - err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val); - if (err) - return dev_err_probe(dev, err, - "mandatory adi,gain-scaling-n property missing\n"); - reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N); - dac->ch_data[ch].n = val; - - err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val); - if (err) - return dev_err_probe(dev, err, - "mandatory adi,rfb-ohms property missing\n"); - dac->ch_data[ch].rfb = val; + u16 reg; - err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val); + err = ad3552r_get_custom_gain(dev, child, + &dac->ch_data[ch].p, + &dac->ch_data[ch].n, + &dac->ch_data[ch].rfb, + &dac->ch_data[ch].gain_offset); if (err) - return dev_err_probe(dev, err, - "mandatory adi,gain-offset property missing\n"); - dac->ch_data[ch].gain_offset = val; + return err; - offset = abs((s32)val); - reg |= ad3552r_field_prep((offset >> 8), AD3552R_MASK_CH_OFFSET_BIT_8); + dac->ch_data[ch].range_override = 1; - reg |= ad3552r_field_prep((s32)val < 0, AD3552R_MASK_CH_OFFSET_POLARITY); addr = AD3552R_REG_ADDR_CH_GAIN(ch); err = ad3552r_write_reg(dac, addr, - offset & AD3552R_MASK_CH_OFFSET_BITS_0_7); + abs((s32)dac->ch_data[ch].gain_offset) & + AD3552R_MASK_CH_OFFSET_BITS_0_7); if (err) return dev_err_probe(dev, err, "Error writing register\n"); + reg = ad3552r_calc_custom_gain(dac->ch_data[ch].p, dac->ch_data[ch].n, + dac->ch_data[ch].gain_offset); + err = ad3552r_write_reg(dac, addr, reg); if (err) return dev_err_probe(dev, err, "Error writing register\n"); @@ -860,49 +455,31 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, static int ad3552r_configure_device(struct ad3552r_desc *dac) { struct device *dev = &dac->spi->dev; - int err, cnt = 0, voltage, delta = 100000; - u32 vals[2], val, ch; + int err, cnt = 0; + u32 val, ch; dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); if (IS_ERR(dac->gpio_ldac)) return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac), "Error getting gpio ldac"); - voltage = devm_regulator_get_enable_read_voltage(dev, "vref"); - if (voltage < 0 && voltage != -ENODEV) - return dev_err_probe(dev, voltage, "Error getting vref voltage\n"); - - if (voltage == -ENODEV) { - if (device_property_read_bool(dev, "adi,vref-out-en")) - val = AD3552R_INTERNAL_VREF_PIN_2P5V; - else - val = AD3552R_INTERNAL_VREF_PIN_FLOATING; - } else { - if (voltage > 2500000 + delta || voltage < 2500000 - delta) { - dev_warn(dev, "vref-supply must be 2.5V"); - return -EINVAL; - } - val = AD3552R_EXTERNAL_VREF_PIN_INPUT; - } + err = ad3552r_get_ref_voltage(dev, &val); + if (err < 0) + return err; err = ad3552r_update_reg_field(dac, - addr_mask_map[AD3552R_VREF_SELECT][0], - addr_mask_map[AD3552R_VREF_SELECT][1], - val); + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, + AD3552R_MASK_REFERENCE_VOLTAGE_SEL, + FIELD_PREP(AD3552R_MASK_REFERENCE_VOLTAGE_SEL, val)); if (err) return err; - err = device_property_read_u32(dev, "adi,sdo-drive-strength", &val); + err = ad3552r_get_drive_strength(dev, &val); if (!err) { - if (val > 3) { - dev_err(dev, "adi,sdo-drive-strength must be less than 4\n"); - return -EINVAL; - } - err = ad3552r_update_reg_field(dac, - addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][0], - addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][1], - val); + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SDO_DRIVE_STRENGTH, + FIELD_PREP(AD3552R_MASK_SDO_DRIVE_STRENGTH, val)); if (err) return err; } @@ -923,24 +500,21 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac) "reg must be less than %d\n", dac->model_data->num_hw_channels); - if (fwnode_property_present(child, "adi,output-range-microvolt")) { - err = fwnode_property_read_u32_array(child, - "adi,output-range-microvolt", - vals, - 2); - if (err) - return dev_err_probe(dev, err, - "adi,output-range-microvolt property could not be parsed\n"); - - err = ad3552r_find_range(dac->model_data, vals); - if (err < 0) - return dev_err_probe(dev, err, - "Invalid adi,output-range-microvolt value\n"); - - val = err; - err = ad3552r_set_ch_value(dac, - AD3552R_CH_OUTPUT_RANGE_SEL, - ch, val); + err = ad3552r_get_output_range(dev, dac->model_data, + child, &val); + if (err && err != -ENOENT) + return err; + + if (!err) { + if (ch == 0) + val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val); + else + val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1), val); + + err = ad3552r_update_reg_field(dac, + AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch), + val); if (err) return err; @@ -955,10 +529,17 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac) return err; } - ad3552r_calc_gain_and_offset(dac, ch); + ad3552r_calc_gain_and_offset(&dac->ch_data[ch], dac->model_data); dac->enabled_ch |= BIT(ch); - err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1); + if (ch == 0) + val = FIELD_PREP(AD3552R_MASK_CH(0), 1); + else + val = FIELD_PREP(AD3552R_MASK_CH(1), 1); + + err = ad3552r_update_reg_field(dac, + AD3552R_REG_ADDR_CH_SELECT_16B, + AD3552R_MASK_CH(ch), val); if (err < 0) return err; @@ -970,8 +551,15 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac) /* Disable unused channels */ for_each_clear_bit(ch, &dac->enabled_ch, dac->model_data->num_hw_channels) { - err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN, - ch, 1); + if (ch == 0) + val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), 1); + else + val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1), 1); + + err = ad3552r_update_reg_field(dac, + AD3552R_REG_ADDR_POWERDOWN_CONFIG, + AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch), + val); if (err) return err; } @@ -1140,3 +728,4 @@ module_spi_driver(ad3552r_driver); MODULE_AUTHOR("Mihail Chindris "); MODULE_DESCRIPTION("Analog Device AD3552R DAC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD3552R); diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h new file mode 100644 index 0000000000000..fd5a3dfd1d1cf --- /dev/null +++ b/drivers/iio/dac/ad3552r.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AD3552R Digital <-> Analog converters common header + * + * Copyright 2021-2024 Analog Devices Inc. + * Author: Angelo Dureghello + */ + +#ifndef __DRIVERS_IIO_DAC_AD3552R_H__ +#define __DRIVERS_IIO_DAC_AD3552R_H__ + +/* Register addresses */ +/* Primary address space */ +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00 +#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0)) +#define AD3552R_MASK_ADDR_ASCENSION BIT(5) +#define AD3552R_MASK_SDO_ACTIVE BIT(4) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01 +#define AD3552R_MASK_SINGLE_INST BIT(7) +#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3) +#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02 +#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n)) +#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2) +#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0) +#define AD3552R_REG_ADDR_CHIP_TYPE 0x03 +#define AD3552R_MASK_CLASS GENMASK(7, 0) +#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04 +#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05 +#define AD3552R_REG_ADDR_CHIP_GRADE 0x06 +#define AD3552R_MASK_GRADE GENMASK(7, 4) +#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0) +#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A +#define AD3552R_REG_ADDR_SPI_REVISION 0x0B +#define AD3552R_REG_ADDR_VENDOR_L 0x0C +#define AD3552R_REG_ADDR_VENDOR_H 0x0D +#define AD3552R_REG_ADDR_STREAM_MODE 0x0E +#define AD3552R_MASK_LENGTH GENMASK(7, 0) +#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F +#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6) +#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10 +#define AD3552R_MASK_CRC_ENABLE \ + (GENMASK(7, 6) | GENMASK(1, 0)) +#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5) +#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11 +#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7) +#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5) +#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3) +#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2) +#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1) +#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14 +#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6) +#define AD3552R_MASK_MEM_CRC_EN BIT(4) +#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2) +#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1) +#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0) +#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15 +#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6) +#define AD3552R_MASK_SAMPLE_HOLD_DIFF_USER_EN BIT(5) +#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3) +#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2) +#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0) +#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16 +#define AD3552R_MASK_REF_RANGE_ALARM BIT(6) +#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5) +#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4) +#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3) +#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2) +#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1) +#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0) +#define AD3552R_REG_ADDR_ERR_STATUS 0x17 +#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6) +#define AD3552R_MASK_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5) +#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4) +#define AD3552R_MASK_RESET_STATUS BIT(0) +#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18 +#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch)) +#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch) +#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19 +#define AD3552R_MASK_CH0_RANGE GENMASK(2, 0) +#define AD3552R_MASK_CH1_RANGE GENMASK(6, 4) +#define AD3552R_MASK_CH_OUTPUT_RANGE GENMASK(7, 0) +#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) \ + ((ch) ? GENMASK(7, 4) : GENMASK(3, 0)) +#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2) +#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0) +#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2) +#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7) +#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5) +#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3) +#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2) +#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(8) +/* + * Secondary region + * For multibyte registers specify the highest address because the access is + * done in descending order + */ +#define AD3552R_SECONDARY_REGION_START 0x28 +#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28 +#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - (ch)) * 2) +#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E +#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F +#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31 +#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32 +#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - (ch)) * 2) +/* 3 bytes registers */ +#define AD3552R_REG_START_24B 0x37 +#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37 +#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - (ch)) * 3) +#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40 +#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41 +#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44 +#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45 +#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - (ch)) * 3) + +#define AD3552R_MAX_CH 2 +#define AD3552R_MASK_CH(ch) BIT(ch) +#define AD3552R_MASK_ALL_CH GENMASK(1, 0) +#define AD3552R_MAX_REG_SIZE 3 +#define AD3552R_READ_BIT BIT(7) +#define AD3552R_ADDR_MASK GENMASK(6, 0) +#define AD3552R_MASK_DAC_12B GENMASK(15, 4) +#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8 +#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34 +#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2 +#define AD3552R_GAIN_SCALE 1000 +#define AD3552R_LDAC_PULSE_US 100 + +#define AD3552R_CH0_ACTIVE BIT(0) +#define AD3552R_CH1_ACTIVE BIT(1) + +#define AD3552R_MAX_RANGES 5 +#define AD3542R_MAX_RANGES 6 +#define AD3552R_QUAD_SPI 2 + +extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2]; +extern const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2]; + +enum ad3552r_id { + AD3541R_ID = 0x400b, + AD3542R_ID = 0x4009, + AD3551R_ID = 0x400a, + AD3552R_ID = 0x4008, +}; + +struct ad3552r_model_data { + const char *model_name; + enum ad3552r_id chip_id; + unsigned int num_hw_channels; + const s32 (*ranges_table)[2]; + int num_ranges; + bool requires_output_range; +}; + +struct ad3552r_ch_data { + s32 scale_int; + s32 scale_dec; + s32 offset_int; + s32 offset_dec; + s16 gain_offset; + u16 rfb; + u8 n; + u8 p; + u8 range; + bool range_override; +}; + +enum ad3552r_ch_gain_scaling { + /* Gain scaling of 1 */ + AD3552R_CH_GAIN_SCALING_1, + /* Gain scaling of 0.5 */ + AD3552R_CH_GAIN_SCALING_0_5, + /* Gain scaling of 0.25 */ + AD3552R_CH_GAIN_SCALING_0_25, + /* Gain scaling of 0.125 */ + AD3552R_CH_GAIN_SCALING_0_125, +}; + +enum ad3552r_ch_vref_select { + /* Internal source with Vref I/O floating */ + AD3552R_INTERNAL_VREF_PIN_FLOATING, + /* Internal source with Vref I/O at 2.5V */ + AD3552R_INTERNAL_VREF_PIN_2P5V, + /* External source with Vref I/O as input */ + AD3552R_EXTERNAL_VREF_PIN_INPUT +}; + +enum ad3542r_ch_output_range { + /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__2P5V, + /* Range from 0 V to 3 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__3V, + /* Range from 0 V to 5 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__5V, + /* Range from 0 V to 10 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_0__10V, + /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V, + /* Range from -5 V to 5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_5__5V, +}; + +enum ad3552r_ch_output_range { + /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ + AD3552R_CH_OUTPUT_RANGE_0__2P5V, + /* Range from 0 V to 5 V. Requires Rfb1x connection */ + AD3552R_CH_OUTPUT_RANGE_0__5V, + /* Range from 0 V to 10 V. Requires Rfb2x connection */ + AD3552R_CH_OUTPUT_RANGE_0__10V, + /* Range from -5 V to 5 V. Requires Rfb2x connection */ + AD3552R_CH_OUTPUT_RANGE_NEG_5__5V, + /* Range from -10 V to 10 V. Requires Rfb4x connection */ + AD3552R_CH_OUTPUT_RANGE_NEG_10__10V, +}; + +int ad3552r_get_output_range(struct device *dev, + const struct ad3552r_model_data *model_info, + struct fwnode_handle *child, u32 *val); +int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child, + u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs); +u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs); +int ad3552r_get_ref_voltage(struct device *dev, u32 *val); +int ad3552r_get_drive_strength(struct device *dev, u32 *val); +void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data, + const struct ad3552r_model_data *model_data); + +#endif /* __DRIVERS_IIO_DAC_AD3552R_H__ */ diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 2e3e33f92bc0a..392a1c7aee03b 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -47,7 +47,6 @@ struct ad5380_chip_info { * struct ad5380_state - driver instance specific data * @regmap: regmap instance used by the device * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulator * @vref: actual reference voltage used in uA * @pwr_down: whether the chip is currently in power down mode * @lock: lock to protect the data buffer during regmap ops @@ -55,7 +54,6 @@ struct ad5380_chip_info { struct ad5380_state { struct regmap *regmap; const struct ad5380_chip_info *chip_info; - struct regulator *vref_reg; int vref; bool pwr_down; struct mutex lock; @@ -341,14 +339,14 @@ static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { }, }; -static int ad5380_alloc_channels(struct iio_dev *indio_dev) +static int ad5380_alloc_channels(struct device *dev, struct iio_dev *indio_dev) { struct ad5380_state *st = iio_priv(indio_dev); struct iio_chan_spec *channels; unsigned int i; - channels = kcalloc(st->chip_info->num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); + channels = devm_kcalloc(dev, st->chip_info->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); if (!channels) return -ENOMEM; @@ -379,7 +377,6 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap, } st = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); st->chip_info = &ad5380_chip_info_tbl[type]; st->regmap = regmap; @@ -391,68 +388,32 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap, mutex_init(&st->lock); - ret = ad5380_alloc_channels(indio_dev); - if (ret) { - dev_err(dev, "Failed to allocate channel spec: %d\n", ret); - return ret; - } + ret = ad5380_alloc_channels(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to allocate channel spec\n"); if (st->chip_info->int_vref == 2500) ctrl |= AD5380_CTRL_INT_VREF_2V5; - st->vref_reg = devm_regulator_get(dev, "vref"); - if (!IS_ERR(st->vref_reg)) { - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(dev, "Failed to enable vref regulators: %d\n", - ret); - goto error_free_reg; - } - - ret = regulator_get_voltage(st->vref_reg); - if (ret < 0) - goto error_disable_reg; - - st->vref = ret / 1000; - } else { + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get vref voltage\n"); + if (ret == -ENODEV) { st->vref = st->chip_info->int_vref; ctrl |= AD5380_CTRL_INT_VREF_EN; + } else { + st->vref = ret / 1000; } ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl); - if (ret) { - dev_err(dev, "Failed to write to device: %d\n", ret); - goto error_disable_reg; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to write to device\n"); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); return 0; - -error_disable_reg: - if (!IS_ERR(st->vref_reg)) - regulator_disable(st->vref_reg); -error_free_reg: - kfree(indio_dev->channels); - - return ret; -} - -static void ad5380_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5380_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - kfree(indio_dev->channels); - - if (!IS_ERR(st->vref_reg)) - regulator_disable(st->vref_reg); } static bool ad5380_reg_false(struct device *dev, unsigned int reg) @@ -486,11 +447,6 @@ static int ad5380_spi_probe(struct spi_device *spi) return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name); } -static void ad5380_spi_remove(struct spi_device *spi) -{ - ad5380_remove(&spi->dev); -} - static const struct spi_device_id ad5380_spi_ids[] = { { "ad5380-3", ID_AD5380_3 }, { "ad5380-5", ID_AD5380_5 }, @@ -517,7 +473,6 @@ static struct spi_driver ad5380_spi_driver = { .name = "ad5380", }, .probe = ad5380_spi_probe, - .remove = ad5380_spi_remove, .id_table = ad5380_spi_ids, }; @@ -559,11 +514,6 @@ static int ad5380_i2c_probe(struct i2c_client *i2c) return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name); } -static void ad5380_i2c_remove(struct i2c_client *i2c) -{ - ad5380_remove(&i2c->dev); -} - static const struct i2c_device_id ad5380_i2c_ids[] = { { "ad5380-3", ID_AD5380_3 }, { "ad5380-5", ID_AD5380_5 }, @@ -590,7 +540,6 @@ static struct i2c_driver ad5380_i2c_driver = { .name = "ad5380", }, .probe = ad5380_i2c_probe, - .remove = ad5380_i2c_remove, .id_table = ad5380_i2c_ids, }; diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index 7644acfd879e0..1462ee640b168 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -384,7 +384,7 @@ static int ad5421_write_raw(struct iio_dev *indio_dev, static int ad5421_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct ad5421_state *st = iio_priv(indio_dev); unsigned int mask; diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 708629efc1573..6ad99f97eed55 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -32,7 +32,6 @@ * struct ad5446_state - driver instance specific data * @dev: this device * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator * @vref_mv: actual reference voltage used * @cached_val: store/retrieve values during power down * @pwr_down_mode: power down mode (1k, 100k or tristate) @@ -43,7 +42,6 @@ struct ad5446_state { struct device *dev; const struct ad5446_chip_info *chip_info; - struct regulator *reg; unsigned short vref_mv; unsigned cached_val; unsigned pwr_down_mode; @@ -226,32 +224,15 @@ static int ad5446_probe(struct device *dev, const char *name, { struct ad5446_state *st; struct iio_dev *indio_dev; - struct regulator *reg; - int ret, voltage_uv = 0; - - reg = devm_regulator_get(dev, "vcc"); - if (!IS_ERR(reg)) { - ret = regulator_enable(reg); - if (ret) - return ret; - - ret = regulator_get_voltage(reg); - if (ret < 0) - goto error_disable_reg; - - voltage_uv = ret; - } + int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_disable_reg; - } + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); st->chip_info = chip_info; - dev_set_drvdata(dev, indio_dev); - st->reg = reg; st->dev = dev; indio_dev->name = name; @@ -264,33 +245,19 @@ static int ad5446_probe(struct device *dev, const char *name, st->pwr_down_mode = MODE_PWRDWN_1k; - if (st->chip_info->int_vref_mv) - st->vref_mv = st->chip_info->int_vref_mv; - else if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - dev_warn(dev, "reference voltage unspecified\n"); - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(reg)) - regulator_disable(reg); - return ret; -} - -static void ad5446_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5446_state *st = iio_priv(indio_dev); + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + if (ret < 0 && ret != -ENODEV) + return ret; + if (ret == -ENODEV) { + if (chip_info->int_vref_mv) + st->vref_mv = chip_info->int_vref_mv; + else + dev_warn(dev, "reference voltage unspecified\n"); + } else { + st->vref_mv = ret / 1000; + } - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); + return devm_iio_device_register(dev, indio_dev); } #if IS_ENABLED(CONFIG_SPI_MASTER) @@ -491,18 +458,12 @@ static int ad5446_spi_probe(struct spi_device *spi) &ad5446_spi_chip_info[id->driver_data]); } -static void ad5446_spi_remove(struct spi_device *spi) -{ - ad5446_remove(&spi->dev); -} - static struct spi_driver ad5446_spi_driver = { .driver = { .name = "ad5446", .of_match_table = ad5446_of_ids, }, .probe = ad5446_spi_probe, - .remove = ad5446_spi_remove, .id_table = ad5446_spi_ids, }; @@ -575,11 +536,6 @@ static int ad5446_i2c_probe(struct i2c_client *i2c) &ad5446_i2c_chip_info[id->driver_data]); } -static void ad5446_i2c_remove(struct i2c_client *i2c) -{ - ad5446_remove(&i2c->dev); -} - static const struct i2c_device_id ad5446_i2c_ids[] = { {"ad5301", ID_AD5602}, {"ad5311", ID_AD5612}, @@ -596,7 +552,6 @@ static struct i2c_driver ad5446_i2c_driver = { .name = "ad5446", }, .probe = ad5446_i2c_probe, - .remove = ad5446_i2c_remove, .id_table = ad5446_i2c_ids, }; diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index e6c5be728bb21..ff0765c8af47f 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -270,38 +270,29 @@ static const struct iio_chan_spec ad5504_channels[] = { static int ad5504_probe(struct spi_device *spi) { - struct ad5504_platform_data *pdata = spi->dev.platform_data; + const struct ad5504_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; struct ad5504_state *st; - struct regulator *reg; - int ret, voltage_uv = 0; + int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; - reg = devm_regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(reg)) { - ret = regulator_enable(reg); - if (ret) - return ret; - ret = regulator_get_voltage(reg); - if (ret < 0) - goto error_disable_reg; + st = iio_priv(indio_dev); - voltage_uv = ret; + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc"); + if (ret < 0 && ret != -ENODEV) + return ret; + if (ret == -ENODEV) { + if (pdata->vref_mv) + st->vref_mv = pdata->vref_mv; + else + dev_warn(&spi->dev, "reference voltage unspecified\n"); + } else { + st->vref_mv = ret / 1000; } - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else if (pdata) - st->vref_mv = pdata->vref_mv; - else - dev_warn(&spi->dev, "reference voltage unspecified\n"); - - st->reg = reg; st->spi = spi; indio_dev->name = spi_get_device_id(st->spi)->name; indio_dev->info = &ad5504_info; @@ -320,31 +311,10 @@ static int ad5504_probe(struct spi_device *spi) spi_get_device_id(st->spi)->name, indio_dev); if (ret) - goto error_disable_reg; + return ret; } - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(reg)) - regulator_disable(reg); - - return ret; -} - -static void ad5504_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5504_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad5504_id[] = { @@ -359,7 +329,6 @@ static struct spi_driver ad5504_driver = { .name = "ad5504", }, .probe = ad5504_probe, - .remove = ad5504_remove, .id_table = ad5504_id, }; module_spi_driver(ad5504_driver); diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h index 13964f3a22a46..14a439b06eb6a 100644 --- a/drivers/iio/dac/ad5624r.h +++ b/drivers/iio/dac/ad5624r.h @@ -54,7 +54,6 @@ struct ad5624r_chip_info { struct ad5624r_state { struct spi_device *us; const struct ad5624r_chip_info *chip_info; - struct regulator *reg; unsigned short vref_mv; unsigned pwr_down_mask; unsigned pwr_down_mode; diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 9304d0499bae8..2fd38ac8f6988 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -223,50 +223,26 @@ static int ad5624r_probe(struct spi_device *spi) { struct ad5624r_state *st; struct iio_dev *indio_dev; - int ret, voltage_uv = 0; + bool external_vref; + int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - st->reg = devm_regulator_get_optional(&spi->dev, "vref"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg); - if (ret < 0) - goto error_disable_reg; - - voltage_uv = ret; - } else { - if (PTR_ERR(st->reg) != -ENODEV) - return PTR_ERR(st->reg); + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret == -ENODEV) /* Backwards compatibility. This naming is not correct */ - st->reg = devm_regulator_get_optional(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg); - if (ret < 0) - goto error_disable_reg; - - voltage_uv = ret; - } - } + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc"); + if (ret < 0 && ret != -ENODEV) + return ret; + + external_vref = ret != -ENODEV; + st->vref_mv = external_vref ? ret / 1000 : st->chip_info->int_vref_mv; - spi_set_drvdata(spi, indio_dev); st->chip_info = &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; - st->us = spi; indio_dev->name = spi_get_device_id(spi)->name; @@ -276,31 +252,11 @@ static int ad5624r_probe(struct spi_device *spi) indio_dev->num_channels = AD5624R_DAC_CHANNELS; ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 16); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); + external_vref, 16); if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); - - return ret; -} - -static void ad5624r_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5624r_state *st = iio_priv(indio_dev); + return ret; - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad5624r_id[] = { @@ -319,7 +275,6 @@ static struct spi_driver ad5624r_driver = { .name = "ad5624r", }, .probe = ad5624r_probe, - .remove = ad5624r_remove, .id_table = ad5624r_id, }; module_spi_driver(ad5624r_driver); diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 0b24cb19ac9d1..05e80b6ae2cc3 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -699,7 +699,6 @@ static const struct ad5755_platform_data ad5755_default_pdata = { static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) { - struct fwnode_handle *pp; struct ad5755_platform_data *pdata; unsigned int tmp; unsigned int tmparray[3]; @@ -746,11 +745,12 @@ static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) } devnr = 0; - device_for_each_child_node(dev, pp) { + device_for_each_child_node_scoped(dev, pp) { if (devnr >= AD5755_NUM_CHANNELS) { dev_err(dev, "There are too many channels defined in DT\n"); - goto error_out; + devm_kfree(dev, pdata); + return NULL; } pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; @@ -800,11 +800,6 @@ static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) } return pdata; - - error_out: - fwnode_handle_put(pp); - devm_kfree(dev, pdata); - return NULL; } static int ad5755_probe(struct spi_device *spi) diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index 6aa1a068adb06..0aa5ba7f46548 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -53,7 +53,6 @@ enum ad5761_supported_device_ids { /** * struct ad5761_state - driver instance specific data * @spi: spi_device - * @vref_reg: reference voltage regulator * @use_intref: true when the internal voltage reference is used * @vref: actual voltage reference in mVolts * @range: output range mode used @@ -62,7 +61,6 @@ enum ad5761_supported_device_ids { */ struct ad5761_state { struct spi_device *spi; - struct regulator *vref_reg; struct mutex lock; bool use_intref; @@ -287,63 +285,6 @@ static const struct ad5761_chip_info ad5761_chip_infos[] = { }, }; -static int ad5761_get_vref(struct ad5761_state *st, - const struct ad5761_chip_info *chip_info) -{ - int ret; - - st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref"); - if (PTR_ERR(st->vref_reg) == -ENODEV) { - /* Use Internal regulator */ - if (!chip_info->int_vref) { - dev_err(&st->spi->dev, - "Voltage reference not found\n"); - return -EIO; - } - - st->use_intref = true; - st->vref = chip_info->int_vref; - return 0; - } - - if (IS_ERR(st->vref_reg)) { - dev_err(&st->spi->dev, - "Error getting voltage reference regulator\n"); - return PTR_ERR(st->vref_reg); - } - - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(&st->spi->dev, - "Failed to enable voltage reference\n"); - return ret; - } - - ret = regulator_get_voltage(st->vref_reg); - if (ret < 0) { - dev_err(&st->spi->dev, - "Failed to get voltage reference value\n"); - goto disable_regulator_vref; - } - - if (ret < 2000000 || ret > 3000000) { - dev_warn(&st->spi->dev, - "Invalid external voltage ref. value %d uV\n", ret); - ret = -EIO; - goto disable_regulator_vref; - } - - st->vref = ret / 1000; - st->use_intref = false; - - return 0; - -disable_regulator_vref: - regulator_disable(st->vref_reg); - st->vref_reg = NULL; - return ret; -} - static int ad5761_probe(struct spi_device *spi) { struct iio_dev *iio_dev; @@ -361,11 +302,28 @@ static int ad5761_probe(struct spi_device *spi) st = iio_priv(iio_dev); st->spi = spi; - spi_set_drvdata(spi, iio_dev); - ret = ad5761_get_vref(st, chip_info); - if (ret) - return ret; + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, + "Failed to get voltage reference value\n"); + if (ret == -ENODEV) { + /* Use Internal regulator */ + if (!chip_info->int_vref) + return dev_err_probe(&spi->dev, -EIO, + "Voltage reference not found\n"); + + st->use_intref = true; + st->vref = chip_info->int_vref; + } else { + if (ret < 2000000 || ret > 3000000) + return dev_err_probe(&spi->dev, -EIO, + "Invalid external voltage ref. value %d uV\n", + ret); + + st->use_intref = false; + st->vref = ret / 1000; + } if (pdata) voltage_range = pdata->voltage_range; @@ -374,35 +332,15 @@ static int ad5761_probe(struct spi_device *spi) ret = ad5761_spi_set_range(st, voltage_range); if (ret) - goto disable_regulator_err; + return ret; iio_dev->info = &ad5761_info; iio_dev->modes = INDIO_DIRECT_MODE; iio_dev->channels = &chip_info->channel; iio_dev->num_channels = 1; iio_dev->name = spi_get_device_id(st->spi)->name; - ret = iio_device_register(iio_dev); - if (ret) - goto disable_regulator_err; - - return 0; - -disable_regulator_err: - if (!IS_ERR_OR_NULL(st->vref_reg)) - regulator_disable(st->vref_reg); - - return ret; -} - -static void ad5761_remove(struct spi_device *spi) -{ - struct iio_dev *iio_dev = spi_get_drvdata(spi); - struct ad5761_state *st = iio_priv(iio_dev); - - iio_device_unregister(iio_dev); - if (!IS_ERR_OR_NULL(st->vref_reg)) - regulator_disable(st->vref_reg); + return devm_iio_device_register(&spi->dev, iio_dev); } static const struct spi_device_id ad5761_id[] = { @@ -419,7 +357,6 @@ static struct spi_driver ad5761_driver = { .name = "ad5761", }, .probe = ad5761_probe, - .remove = ad5761_remove, .id_table = ad5761_id, }; module_spi_driver(ad5761_driver); diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index c360ebf5297ab..25cf11d0471b6 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -17,6 +17,7 @@ #include #include #include +#include #define ADI_SPI_IF_CONFIG_A 0x00 #define ADI_SPI_IF_CONFIG_B 0x01 @@ -121,7 +122,6 @@ struct ad5770r_out_range { * struct ad5770r_state - driver instance specific data * @spi: spi_device * @regmap: regmap - * @vref_reg: fixed regulator for reference configuration * @gpio_reset: gpio descriptor * @output_mode: array contains channels output ranges * @vref: reference value @@ -133,7 +133,6 @@ struct ad5770r_out_range { struct ad5770r_state { struct spi_device *spi; struct regmap *regmap; - struct regulator *vref_reg; struct gpio_desc *gpio_reset; struct ad5770r_out_range output_mode[AD5770R_MAX_CHANNELS]; int vref; @@ -325,7 +324,7 @@ static int ad5770r_read_raw(struct iio_dev *indio_dev, if (ret) return 0; - buf16 = st->transf_buf[0] + (st->transf_buf[1] << 8); + buf16 = get_unaligned_le16(st->transf_buf); *val = buf16 >> 2; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -590,13 +589,6 @@ static int ad5770r_init(struct ad5770r_state *st) return ret; } -static void ad5770r_disable_regulator(void *data) -{ - struct ad5770r_state *st = data; - - regulator_disable(st->vref_reg); -} - static int ad5770r_probe(struct spi_device *spi) { struct ad5770r_state *st; @@ -621,34 +613,12 @@ static int ad5770r_probe(struct spi_device *spi) } st->regmap = regmap; - st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); - if (!IS_ERR(st->vref_reg)) { - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(&spi->dev, - "Failed to enable vref regulators: %d\n", ret); - return ret; - } + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, "Failed to get vref voltage\n"); - ret = devm_add_action_or_reset(&spi->dev, - ad5770r_disable_regulator, - st); - if (ret < 0) - return ret; - - ret = regulator_get_voltage(st->vref_reg); - if (ret < 0) - return ret; - - st->vref = ret / 1000; - } else { - if (PTR_ERR(st->vref_reg) == -ENODEV) { - st->vref = AD5770R_LOW_VREF_mV; - st->internal_ref = true; - } else { - return PTR_ERR(st->vref_reg); - } - } + st->internal_ref = ret == -ENODEV; + st->vref = st->internal_ref ? AD5770R_LOW_VREF_mV : ret / 1000; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad5770r_info; diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 75b549827e15a..57374f78f6b88 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -61,11 +62,14 @@ /** * struct ad5791_chip_info - chip specific information + * @name: name of the dac chip + * @channel: channel specification * @get_lin_comp: function pointer to the device specific function */ - struct ad5791_chip_info { - int (*get_lin_comp) (unsigned int span); + const char *name; + const struct iio_chan_spec channel; + int (*get_lin_comp)(unsigned int span); }; /** @@ -73,6 +77,9 @@ struct ad5791_chip_info { * @spi: spi_device * @reg_vdd: positive supply regulator * @reg_vss: negative supply regulator + * @gpio_reset: reset gpio + * @gpio_clear: clear gpio + * @gpio_ldac: load dac gpio * @chip_info: chip model specific constants * @vref_mv: actual reference voltage used * @vref_neg_mv: voltage of the negative supply @@ -85,6 +92,9 @@ struct ad5791_state { struct spi_device *spi; struct regulator *reg_vdd; struct regulator *reg_vss; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_clear; + struct gpio_desc *gpio_ldac; const struct ad5791_chip_info *chip_info; unsigned short vref_mv; unsigned int vref_neg_mv; @@ -98,13 +108,6 @@ struct ad5791_state { } data[3] __aligned(IIO_DMA_MINALIGN); }; -enum ad5791_supported_device_ids { - ID_AD5760, - ID_AD5780, - ID_AD5781, - ID_AD5791, -}; - static int ad5791_spi_write(struct ad5791_state *st, u8 addr, u32 val) { st->data[0].d32 = cpu_to_be32(AD5791_CMD_WRITE | @@ -228,20 +231,6 @@ static int ad5780_get_lin_comp(unsigned int span) else return AD5780_LINCOMP_10_20; } -static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { - [ID_AD5760] = { - .get_lin_comp = ad5780_get_lin_comp, - }, - [ID_AD5780] = { - .get_lin_comp = ad5780_get_lin_comp, - }, - [ID_AD5781] = { - .get_lin_comp = ad5791_get_lin_comp, - }, - [ID_AD5791] = { - .get_lin_comp = ad5791_get_lin_comp, - }, -}; static int ad5791_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -289,30 +278,34 @@ static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { { }, }; -#define AD5791_CHAN(bits, _shift) { \ - .type = IIO_VOLTAGE, \ - .output = 1, \ - .indexed = 1, \ - .address = AD5791_ADDR_DAC0, \ - .channel = 0, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_OFFSET), \ - .scan_type = { \ - .sign = 'u', \ - .realbits = (bits), \ - .storagebits = 24, \ - .shift = (_shift), \ - }, \ - .ext_info = ad5791_ext_info, \ +#define AD5791_DEFINE_CHIP_INFO(_name, bits, _shift, _lin_comp) \ +static const struct ad5791_chip_info _name##_chip_info = { \ + .name = #_name, \ + .get_lin_comp = &(_lin_comp), \ + .channel = { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .address = AD5791_ADDR_DAC0, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 24, \ + .shift = (_shift), \ + }, \ + .ext_info = ad5791_ext_info, \ + }, \ } -static const struct iio_chan_spec ad5791_channels[] = { - [ID_AD5760] = AD5791_CHAN(16, 4), - [ID_AD5780] = AD5791_CHAN(18, 2), - [ID_AD5781] = AD5791_CHAN(18, 2), - [ID_AD5791] = AD5791_CHAN(20, 0) -}; +AD5791_DEFINE_CHIP_INFO(ad5760, 16, 4, ad5780_get_lin_comp); +AD5791_DEFINE_CHIP_INFO(ad5780, 18, 2, ad5780_get_lin_comp); +AD5791_DEFINE_CHIP_INFO(ad5781, 18, 2, ad5791_get_lin_comp); +AD5791_DEFINE_CHIP_INFO(ad5790, 20, 0, ad5791_get_lin_comp); +AD5791_DEFINE_CHIP_INFO(ad5791, 20, 0, ad5791_get_lin_comp); static int ad5791_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -341,7 +334,7 @@ static const struct iio_info ad5791_info = { static int ad5791_probe(struct spi_device *spi) { - struct ad5791_platform_data *pdata = spi->dev.platform_data; + const struct ad5791_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; struct ad5791_state *st; int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; @@ -351,31 +344,21 @@ static int ad5791_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - st->reg_vdd = devm_regulator_get(&spi->dev, "vdd"); - if (!IS_ERR(st->reg_vdd)) { - ret = regulator_enable(st->reg_vdd); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg_vdd); - if (ret < 0) - goto error_disable_reg_pos; - - pos_voltage_uv = ret; - } - st->reg_vss = devm_regulator_get(&spi->dev, "vss"); - if (!IS_ERR(st->reg_vss)) { - ret = regulator_enable(st->reg_vss); - if (ret) - goto error_disable_reg_pos; + st->gpio_reset = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); - ret = regulator_get_voltage(st->reg_vss); - if (ret < 0) - goto error_disable_reg_neg; + st->gpio_clear = devm_gpiod_get_optional(&spi->dev, "clear", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_clear)) + return PTR_ERR(st->gpio_clear); - neg_voltage_uv = ret; - } + st->gpio_ldac = devm_gpiod_get_optional(&spi->dev, "ldac", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_ldac)) + return PTR_ERR(st->gpio_ldac); st->pwr_down = true; st->spi = spi; @@ -386,7 +369,17 @@ static int ad5791_probe(struct spi_device *spi) use_rbuf_gain2 = device_property_read_bool(&spi->dev, "adi,rbuf-gain2-en"); - if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { + pos_voltage_uv = devm_regulator_get_enable_read_voltage(&spi->dev, "vdd"); + if (pos_voltage_uv < 0 && pos_voltage_uv != -ENODEV) + return dev_err_probe(&spi->dev, pos_voltage_uv, + "failed to get vdd voltage\n"); + + neg_voltage_uv = devm_regulator_get_enable_read_voltage(&spi->dev, "vss"); + if (neg_voltage_uv < 0 && neg_voltage_uv != -ENODEV) + return dev_err_probe(&spi->dev, neg_voltage_uv, + "failed to get vss voltage\n"); + + if (neg_voltage_uv >= 0 && pos_voltage_uv >= 0) { st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; st->vref_neg_mv = neg_voltage_uv / 1000; } else if (pdata) { @@ -396,13 +389,18 @@ static int ad5791_probe(struct spi_device *spi) dev_warn(&spi->dev, "reference voltage unspecified\n"); } - ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); - if (ret) - goto error_disable_reg_neg; - - st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) - ->driver_data]; + if (st->gpio_reset) { + fsleep(20); + gpiod_set_value_cansleep(st->gpio_reset, 0); + } else { + ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); + if (ret) + return dev_err_probe(&spi->dev, ret, "fail to reset\n"); + } + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return dev_err_probe(&spi->dev, -EINVAL, "no chip info\n"); st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) | (use_rbuf_gain2 ? 0 : AD5791_CTRL_RBUF) | @@ -411,59 +409,42 @@ static int ad5791_probe(struct spi_device *spi) ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl | AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); if (ret) - goto error_disable_reg_neg; + return dev_err_probe(&spi->dev, ret, "fail to write ctrl register\n"); - spi_set_drvdata(spi, indio_dev); indio_dev->info = &ad5791_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels - = &ad5791_channels[spi_get_device_id(spi)->driver_data]; + indio_dev->channels = &st->chip_info->channel; indio_dev->num_channels = 1; - indio_dev->name = spi_get_device_id(st->spi)->name; - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg_neg; - - return 0; - -error_disable_reg_neg: - if (!IS_ERR(st->reg_vss)) - regulator_disable(st->reg_vss); -error_disable_reg_pos: - if (!IS_ERR(st->reg_vdd)) - regulator_disable(st->reg_vdd); - return ret; + indio_dev->name = st->chip_info->name; + return devm_iio_device_register(&spi->dev, indio_dev); } -static void ad5791_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5791_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg_vdd)) - regulator_disable(st->reg_vdd); - - if (!IS_ERR(st->reg_vss)) - regulator_disable(st->reg_vss); -} +static const struct of_device_id ad5791_of_match[] = { + { .compatible = "adi,ad5760", .data = &ad5760_chip_info }, + { .compatible = "adi,ad5780", .data = &ad5780_chip_info }, + { .compatible = "adi,ad5781", .data = &ad5781_chip_info }, + { .compatible = "adi,ad5790", .data = &ad5790_chip_info }, + { .compatible = "adi,ad5791", .data = &ad5791_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5791_of_match); static const struct spi_device_id ad5791_id[] = { - {"ad5760", ID_AD5760}, - {"ad5780", ID_AD5780}, - {"ad5781", ID_AD5781}, - {"ad5790", ID_AD5791}, - {"ad5791", ID_AD5791}, - {} + { "ad5760", (kernel_ulong_t)&ad5760_chip_info }, + { "ad5780", (kernel_ulong_t)&ad5780_chip_info }, + { "ad5781", (kernel_ulong_t)&ad5781_chip_info }, + { "ad5790", (kernel_ulong_t)&ad5790_chip_info }, + { "ad5791", (kernel_ulong_t)&ad5791_chip_info }, + { } }; MODULE_DEVICE_TABLE(spi, ad5791_id); static struct spi_driver ad5791_driver = { .driver = { .name = "ad5791", + .of_match_table = ad5791_of_match, }, .probe = ad5791_probe, - .remove = ad5791_remove, .id_table = ad5791_id, }; module_spi_driver(ad5791_driver); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c new file mode 100644 index 0000000000000..f235394589dfb --- /dev/null +++ b/drivers/iio/dac/ad8460.c @@ -0,0 +1,951 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD8460 Waveform generator DAC Driver + * + * Copyright (C) 2024 Analog Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define AD8460_CTRL_REG(x) (x) +#define AD8460_HVDAC_DATA_WORD(x) (0x60 + (2 * (x))) + +#define AD8460_HV_RESET_MSK BIT(7) +#define AD8460_HV_SLEEP_MSK BIT(4) +#define AD8460_WAVE_GEN_MODE_MSK BIT(0) + +#define AD8460_HVDAC_SLEEP_MSK BIT(3) + +#define AD8460_FAULT_ARM_MSK BIT(7) +#define AD8460_FAULT_LIMIT_MSK GENMASK(6, 0) + +#define AD8460_APG_MODE_ENABLE_MSK BIT(5) +#define AD8460_PATTERN_DEPTH_MSK GENMASK(3, 0) + +#define AD8460_QUIESCENT_CURRENT_MSK GENMASK(7, 0) + +#define AD8460_SHUTDOWN_FLAG_MSK BIT(7) + +#define AD8460_DATA_BYTE_LOW_MSK GENMASK(7, 0) +#define AD8460_DATA_BYTE_HIGH_MSK GENMASK(5, 0) +#define AD8460_DATA_BYTE_FULL_MSK GENMASK(13, 0) + +#define AD8460_DEFAULT_FAULT_PROTECT 0x00 +#define AD8460_DATA_BYTE_WORD_LENGTH 2 +#define AD8460_NUM_DATA_WORDS 16 +#define AD8460_NOMINAL_VOLTAGE_SPAN 80 +#define AD8460_MIN_EXT_RESISTOR_OHMS 2000 +#define AD8460_MAX_EXT_RESISTOR_OHMS 20000 +#define AD8460_MIN_VREFIO_UV 120000 +#define AD8460_MAX_VREFIO_UV 1200000 +#define AD8460_ABS_MAX_OVERVOLTAGE_UV 55000000 +#define AD8460_ABS_MAX_OVERCURRENT_UA 1000000 +#define AD8460_MAX_OVERTEMPERATURE_MC 150000 +#define AD8460_MIN_OVERTEMPERATURE_MC 20000 +#define AD8460_CURRENT_LIMIT_CONV(x) ((x) / 15625) +#define AD8460_VOLTAGE_LIMIT_CONV(x) ((x) / 1953000) +#define AD8460_TEMP_LIMIT_CONV(x) (((x) + 266640) / 6510) + +enum ad8460_fault_type { + AD8460_OVERCURRENT_SRC, + AD8460_OVERCURRENT_SNK, + AD8460_OVERVOLTAGE_POS, + AD8460_OVERVOLTAGE_NEG, + AD8460_OVERTEMPERATURE, +}; + +struct ad8460_state { + struct spi_device *spi; + struct regmap *regmap; + struct iio_channel *tmp_adc_channel; + struct clk *sync_clk; + /* lock to protect against multiple access to the device and shared data */ + struct mutex lock; + int refio_1p2v_mv; + u32 ext_resistor_ohms; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __le16 spi_tx_buf __aligned(IIO_DMA_MINALIGN); +}; + +static int ad8460_hv_reset(struct ad8460_state *state) +{ + int ret; + + ret = regmap_set_bits(state->regmap, AD8460_CTRL_REG(0x00), + AD8460_HV_RESET_MSK); + if (ret) + return ret; + + fsleep(20); + + return regmap_clear_bits(state->regmap, AD8460_CTRL_REG(0x00), + AD8460_HV_RESET_MSK); +} + +static int ad8460_reset(const struct ad8460_state *state) +{ + struct device *dev = &state->spi->dev; + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "Failed to get reset gpio"); + if (reset) { + /* minimum duration of 10ns */ + ndelay(10); + gpiod_set_value_cansleep(reset, 1); + return 0; + } + + /* bring all registers to their default state */ + return regmap_write(state->regmap, AD8460_CTRL_REG(0x03), 1); +} + +static int ad8460_enable_apg_mode(struct ad8460_state *state, int val) +{ + int ret; + + ret = regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x02), + AD8460_APG_MODE_ENABLE_MSK, + FIELD_PREP(AD8460_APG_MODE_ENABLE_MSK, val)); + if (ret) + return ret; + + return regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x00), + AD8460_WAVE_GEN_MODE_MSK, + FIELD_PREP(AD8460_WAVE_GEN_MODE_MSK, val)); +} + +static int ad8460_read_shutdown_flag(struct ad8460_state *state, u64 *flag) +{ + int ret, val; + + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x0E), &val); + if (ret) + return ret; + + *flag = FIELD_GET(AD8460_SHUTDOWN_FLAG_MSK, val); + return 0; +} + +static int ad8460_get_hvdac_word(struct ad8460_state *state, int index, int *val) +{ + int ret; + + ret = regmap_bulk_read(state->regmap, AD8460_HVDAC_DATA_WORD(index), + &state->spi_tx_buf, AD8460_DATA_BYTE_WORD_LENGTH); + if (ret) + return ret; + + *val = le16_to_cpu(state->spi_tx_buf); + + return ret; +} + +static int ad8460_set_hvdac_word(struct ad8460_state *state, int index, int val) +{ + state->spi_tx_buf = cpu_to_le16(FIELD_PREP(AD8460_DATA_BYTE_FULL_MSK, val)); + + return regmap_bulk_write(state->regmap, AD8460_HVDAC_DATA_WORD(index), + &state->spi_tx_buf, AD8460_DATA_BYTE_WORD_LENGTH); +} + +static ssize_t ad8460_dac_input_read(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad8460_state *state = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = ad8460_get_hvdac_word(state, private, ®); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", reg); +} + +static ssize_t ad8460_dac_input_write(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad8460_state *state = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = kstrtou32(buf, 10, ®); + if (ret) + return ret; + + guard(mutex)(&state->lock); + + return ad8460_set_hvdac_word(state, private, reg); +} + +static ssize_t ad8460_read_symbol(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad8460_state *state = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x02), ®); + if (ret) + return ret; + + return sysfs_emit(buf, "%lu\n", FIELD_GET(AD8460_PATTERN_DEPTH_MSK, reg)); +} + +static ssize_t ad8460_write_symbol(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad8460_state *state = iio_priv(indio_dev); + uint16_t sym; + int ret; + + ret = kstrtou16(buf, 10, &sym); + if (ret) + return ret; + + guard(mutex)(&state->lock); + + return regmap_update_bits(state->regmap, + AD8460_CTRL_REG(0x02), + AD8460_PATTERN_DEPTH_MSK, + FIELD_PREP(AD8460_PATTERN_DEPTH_MSK, sym)); +} + +static ssize_t ad8460_read_toggle_en(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad8460_state *state = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x02), ®); + if (ret) + return ret; + + return sysfs_emit(buf, "%ld\n", FIELD_GET(AD8460_APG_MODE_ENABLE_MSK, reg)); +} + +static ssize_t ad8460_write_toggle_en(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad8460_state *state = iio_priv(indio_dev); + bool toggle_en; + int ret; + + ret = kstrtobool(buf, &toggle_en); + if (ret) + return ret; + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return ad8460_enable_apg_mode(state, toggle_en); + unreachable(); +} + +static ssize_t ad8460_read_powerdown(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad8460_state *state = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x01), ®); + if (ret) + return ret; + + return sysfs_emit(buf, "%ld\n", FIELD_GET(AD8460_HVDAC_SLEEP_MSK, reg)); +} + +static ssize_t ad8460_write_powerdown(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad8460_state *state = iio_priv(indio_dev); + bool pwr_down; + u64 sdn_flag; + int ret; + + ret = kstrtobool(buf, &pwr_down); + if (ret) + return ret; + + guard(mutex)(&state->lock); + + /* + * If powerdown is set, HVDAC is enabled and the HV driver is + * enabled via HV_RESET in case it is in shutdown mode, + * If powerdown is cleared, HVDAC is set to shutdown state + * as well as the HV driver. Quiescent current decreases and ouput is + * floating (high impedance). + */ + + ret = regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x01), + AD8460_HVDAC_SLEEP_MSK, + FIELD_PREP(AD8460_HVDAC_SLEEP_MSK, pwr_down)); + if (ret) + return ret; + + if (!pwr_down) { + ret = ad8460_read_shutdown_flag(state, &sdn_flag); + if (ret) + return ret; + + if (sdn_flag) { + ret = ad8460_hv_reset(state); + if (ret) + return ret; + } + } + + ret = regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x00), + AD8460_HV_SLEEP_MSK, + FIELD_PREP(AD8460_HV_SLEEP_MSK, !pwr_down)); + if (ret) + return ret; + + return len; +} + +static const char * const ad8460_powerdown_modes[] = { + "three_state", +}; + +static int ad8460_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + return 0; +} + +static int ad8460_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int type) +{ + return 0; +} + +static int ad8460_set_sample(struct ad8460_state *state, int val) +{ + int ret; + + ret = ad8460_enable_apg_mode(state, 1); + if (ret) + return ret; + + guard(mutex)(&state->lock); + ret = ad8460_set_hvdac_word(state, 0, val); + if (ret) + return ret; + + return regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x02), + AD8460_PATTERN_DEPTH_MSK, + FIELD_PREP(AD8460_PATTERN_DEPTH_MSK, 0)); +} + +static int ad8460_set_fault_threshold(struct ad8460_state *state, + enum ad8460_fault_type fault, + unsigned int threshold) +{ + return regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x08 + fault), + AD8460_FAULT_LIMIT_MSK, + FIELD_PREP(AD8460_FAULT_LIMIT_MSK, threshold)); +} + +static int ad8460_get_fault_threshold(struct ad8460_state *state, + enum ad8460_fault_type fault, + unsigned int *threshold) +{ + unsigned int val; + int ret; + + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x08 + fault), &val); + if (ret) + return ret; + + *threshold = FIELD_GET(AD8460_FAULT_LIMIT_MSK, val); + + return ret; +} + +static int ad8460_set_fault_threshold_en(struct ad8460_state *state, + enum ad8460_fault_type fault, bool en) +{ + return regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x08 + fault), + AD8460_FAULT_ARM_MSK, + FIELD_PREP(AD8460_FAULT_ARM_MSK, en)); +} + +static int ad8460_get_fault_threshold_en(struct ad8460_state *state, + enum ad8460_fault_type fault, bool *en) +{ + unsigned int val; + int ret; + + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x08 + fault), &val); + if (ret) + return ret; + + *en = FIELD_GET(AD8460_FAULT_ARM_MSK, val); + + return 0; +} + +static int ad8460_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct ad8460_state *state = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return ad8460_set_sample(state, val); + unreachable(); + case IIO_CURRENT: + return regmap_write(state->regmap, AD8460_CTRL_REG(0x04), + FIELD_PREP(AD8460_QUIESCENT_CURRENT_MSK, val)); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad8460_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad8460_state *state = iio_priv(indio_dev); + int data, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + scoped_guard(mutex, &state->lock) { + ret = ad8460_get_hvdac_word(state, 0, &data); + if (ret) + return ret; + } + *val = data; + return IIO_VAL_INT; + case IIO_CURRENT: + ret = regmap_read(state->regmap, AD8460_CTRL_REG(0x04), + &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_TEMP: + ret = iio_read_channel_raw(state->tmp_adc_channel, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(state->sync_clk); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * vCONV = vNOMINAL_SPAN * (DAC_CODE / 2**14) - 40V + * vMAX = vNOMINAL_SPAN * (2**14 / 2**14) - 40V + * vMIN = vNOMINAL_SPAN * (0 / 2**14) - 40V + * vADJ = vCONV * (2000 / rSET) * (vREF / 1.2) + * vSPAN = vADJ_MAX - vADJ_MIN + * See datasheet page 49, section FULL-SCALE REDUCTION + */ + *val = AD8460_NOMINAL_VOLTAGE_SPAN * 2000 * state->refio_1p2v_mv; + *val2 = state->ext_resistor_ohms * 1200; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int ad8460_select_fault_type(int chan_type, enum iio_event_direction dir) +{ + switch (chan_type) { + case IIO_VOLTAGE: + switch (dir) { + case IIO_EV_DIR_RISING: + return AD8460_OVERVOLTAGE_POS; + case IIO_EV_DIR_FALLING: + return AD8460_OVERVOLTAGE_NEG; + default: + return -EINVAL; + } + case IIO_CURRENT: + switch (dir) { + case IIO_EV_DIR_RISING: + return AD8460_OVERCURRENT_SRC; + case IIO_EV_DIR_FALLING: + return AD8460_OVERCURRENT_SNK; + default: + return -EINVAL; + } + case IIO_TEMP: + switch (dir) { + case IIO_EV_DIR_RISING: + return AD8460_OVERTEMPERATURE; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad8460_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ad8460_state *state = iio_priv(indio_dev); + int fault; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + fault = ad8460_select_fault_type(chan->type, dir); + if (fault < 0) + return fault; + + return ad8460_set_fault_threshold(state, fault, val); +} + +static int ad8460_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ad8460_state *state = iio_priv(indio_dev); + int fault; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + fault = ad8460_select_fault_type(chan->type, dir); + if (fault < 0) + return fault; + + return ad8460_get_fault_threshold(state, fault, val); +} + +static int ad8460_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool val) +{ + struct ad8460_state *state = iio_priv(indio_dev); + int fault; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + fault = ad8460_select_fault_type(chan->type, dir); + if (fault < 0) + return fault; + + return ad8460_set_fault_threshold_en(state, fault, val); +} + +static int ad8460_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad8460_state *state = iio_priv(indio_dev); + int fault, ret; + bool en; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + fault = ad8460_select_fault_type(chan->type, dir); + if (fault < 0) + return fault; + + ret = ad8460_get_fault_threshold_en(state, fault, &en); + if (ret) + return ret; + + return en; +} + +static int ad8460_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad8460_state *state = iio_priv(indio_dev); + + if (readval) + return regmap_read(state->regmap, reg, readval); + + return regmap_write(state->regmap, reg, writeval); +} + +static int ad8460_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad8460_state *state = iio_priv(indio_dev); + + return ad8460_enable_apg_mode(state, 0); +} + +static int ad8460_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad8460_state *state = iio_priv(indio_dev); + + return ad8460_enable_apg_mode(state, 1); +} + +static const struct iio_buffer_setup_ops ad8460_buffer_setup_ops = { + .preenable = &ad8460_buffer_preenable, + .postdisable = &ad8460_buffer_postdisable, +}; + +static const struct iio_info ad8460_info = { + .read_raw = &ad8460_read_raw, + .write_raw = &ad8460_write_raw, + .write_event_value = &ad8460_write_event_value, + .read_event_value = &ad8460_read_event_value, + .write_event_config = &ad8460_write_event_config, + .read_event_config = &ad8460_read_event_config, + .debugfs_reg_access = &ad8460_reg_access, +}; + +static const struct iio_enum ad8460_powerdown_mode_enum = { + .items = ad8460_powerdown_modes, + .num_items = ARRAY_SIZE(ad8460_powerdown_modes), + .get = ad8460_get_powerdown_mode, + .set = ad8460_set_powerdown_mode, +}; + +#define AD8460_CHAN_EXT_INFO(_name, _what, _read, _write) { \ + .name = (_name), \ + .read = (_read), \ + .write = (_write), \ + .private = (_what), \ + .shared = IIO_SEPARATE, \ +} + +static const struct iio_chan_spec_ext_info ad8460_ext_info[] = { + AD8460_CHAN_EXT_INFO("raw0", 0, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw1", 1, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw2", 2, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw3", 3, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw4", 4, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw5", 5, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw6", 6, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw7", 7, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw8", 8, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw9", 9, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw10", 10, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw11", 11, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw12", 12, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw13", 13, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw14", 14, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("raw15", 15, ad8460_dac_input_read, + ad8460_dac_input_write), + AD8460_CHAN_EXT_INFO("toggle_en", 0, ad8460_read_toggle_en, + ad8460_write_toggle_en), + AD8460_CHAN_EXT_INFO("symbol", 0, ad8460_read_symbol, + ad8460_write_symbol), + AD8460_CHAN_EXT_INFO("powerdown", 0, ad8460_read_powerdown, + ad8460_write_powerdown), + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad8460_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad8460_powerdown_mode_enum), + { } +}; + +static const struct iio_event_spec ad8460_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define AD8460_VOLTAGE_CHAN { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .output = 1, \ + .indexed = 1, \ + .channel = 0, \ + .scan_index = 0, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 14, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .ext_info = ad8460_ext_info, \ + .event_spec = ad8460_events, \ + .num_event_specs = ARRAY_SIZE(ad8460_events), \ +} + +#define AD8460_CURRENT_CHAN { \ + .type = IIO_CURRENT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .output = 1, \ + .indexed = 1, \ + .channel = 0, \ + .scan_index = -1, \ + .event_spec = ad8460_events, \ + .num_event_specs = ARRAY_SIZE(ad8460_events), \ +} + +#define AD8460_TEMP_CHAN { \ + .type = IIO_TEMP, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .channel = 0, \ + .scan_index = -1, \ + .event_spec = ad8460_events, \ + .num_event_specs = 1, \ +} + +static const struct iio_chan_spec ad8460_channels[] = { + AD8460_VOLTAGE_CHAN, + AD8460_CURRENT_CHAN, +}; + +static const struct iio_chan_spec ad8460_channels_with_tmp_adc[] = { + AD8460_VOLTAGE_CHAN, + AD8460_CURRENT_CHAN, + AD8460_TEMP_CHAN, +}; + +static const struct regmap_config ad8460_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7F, +}; + +static const char * const ad8460_supplies[] = { + "avdd_3p3v", "dvdd_3p3v", "vcc_5v", "hvcc", "hvee", "vref_5v" +}; + +static int ad8460_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad8460_state *state; + struct iio_dev *indio_dev; + u32 tmp[2], temp; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + + indio_dev->name = "ad8460"; + indio_dev->info = &ad8460_info; + + state->spi = spi; + + state->regmap = devm_regmap_init_spi(spi, &ad8460_regmap_config); + if (IS_ERR(state->regmap)) + return dev_err_probe(dev, PTR_ERR(state->regmap), + "Failed to initialize regmap"); + + ret = devm_mutex_init(dev, &state->lock); + if (ret) + return ret; + + state->sync_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(state->sync_clk)) + return dev_err_probe(dev, PTR_ERR(state->sync_clk), + "Failed to get sync clk\n"); + + state->tmp_adc_channel = devm_iio_channel_get(dev, "ad8460-tmp"); + if (IS_ERR(state->tmp_adc_channel)) { + if (PTR_ERR(state->tmp_adc_channel) == -EPROBE_DEFER) + return -EPROBE_DEFER; + indio_dev->channels = ad8460_channels; + indio_dev->num_channels = ARRAY_SIZE(ad8460_channels); + } else { + indio_dev->channels = ad8460_channels_with_tmp_adc; + indio_dev->num_channels = ARRAY_SIZE(ad8460_channels_with_tmp_adc); + } + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad8460_supplies), + ad8460_supplies); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable power supplies\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "refio_1p2v"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get reference voltage\n"); + + state->refio_1p2v_mv = ret == -ENODEV ? 1200 : ret / 1000; + + if (!in_range(state->refio_1p2v_mv, AD8460_MIN_VREFIO_UV / 1000, + AD8460_MAX_VREFIO_UV / 1000)) + return dev_err_probe(dev, -EINVAL, + "Invalid ref voltage range(%u mV) [%u mV, %u mV]\n", + state->refio_1p2v_mv, + AD8460_MIN_VREFIO_UV / 1000, + AD8460_MAX_VREFIO_UV / 1000); + + ret = device_property_read_u32(dev, "adi,external-resistor-ohms", + &state->ext_resistor_ohms); + if (ret) + state->ext_resistor_ohms = 2000; + else if (!in_range(state->ext_resistor_ohms, AD8460_MIN_EXT_RESISTOR_OHMS, + AD8460_MAX_EXT_RESISTOR_OHMS)) + return dev_err_probe(dev, -EINVAL, + "Invalid resistor set range(%u) [%u, %u]\n", + state->ext_resistor_ohms, + AD8460_MIN_EXT_RESISTOR_OHMS, + AD8460_MAX_EXT_RESISTOR_OHMS); + + ret = device_property_read_u32_array(dev, "adi,range-microamp", + tmp, ARRAY_SIZE(tmp)); + if (!ret) { + if (in_range(tmp[1], 0, AD8460_ABS_MAX_OVERCURRENT_UA)) + regmap_write(state->regmap, AD8460_CTRL_REG(0x08), + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) | + AD8460_CURRENT_LIMIT_CONV(tmp[1])); + + if (in_range(tmp[0], -AD8460_ABS_MAX_OVERCURRENT_UA, 0)) + regmap_write(state->regmap, AD8460_CTRL_REG(0x09), + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) | + AD8460_CURRENT_LIMIT_CONV(abs(tmp[0]))); + } + + ret = device_property_read_u32_array(dev, "adi,range-microvolt", + tmp, ARRAY_SIZE(tmp)); + if (!ret) { + if (in_range(tmp[1], 0, AD8460_ABS_MAX_OVERVOLTAGE_UV)) + regmap_write(state->regmap, AD8460_CTRL_REG(0x0A), + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) | + AD8460_VOLTAGE_LIMIT_CONV(tmp[1])); + + if (in_range(tmp[0], -AD8460_ABS_MAX_OVERVOLTAGE_UV, 0)) + regmap_write(state->regmap, AD8460_CTRL_REG(0x0B), + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) | + AD8460_VOLTAGE_LIMIT_CONV(abs(tmp[0]))); + } + + ret = device_property_read_u32(dev, "adi,max-millicelsius", &temp); + if (!ret) { + if (in_range(temp, AD8460_MIN_OVERTEMPERATURE_MC, + AD8460_MAX_OVERTEMPERATURE_MC)) + regmap_write(state->regmap, AD8460_CTRL_REG(0x0C), + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) | + AD8460_TEMP_LIMIT_CONV(abs(temp))); + } + + ret = ad8460_reset(state); + if (ret) + return ret; + + /* Enables DAC by default */ + ret = regmap_clear_bits(state->regmap, AD8460_CTRL_REG(0x01), + AD8460_HVDAC_SLEEP_MSK); + if (ret) + return ret; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->setup_ops = &ad8460_buffer_setup_ops; + + ret = devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, "tx", + IIO_BUFFER_DIRECTION_OUT); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get DMA buffer\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad8460_of_match[] = { + { .compatible = "adi,ad8460" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad8460_of_match); + +static const struct spi_device_id ad8460_spi_match[] = { + { .name = "ad8460" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad8460_spi_match); + +static struct spi_driver ad8460_driver = { + .driver = { + .name = "ad8460", + .of_match_table = ad8460_of_match, + }, + .probe = ad8460_probe, + .id_table = ad8460_spi_match, +}; +module_spi_driver(ad8460_driver); + +MODULE_AUTHOR("Mariel Tinaco #include +#include "ad3552r-hs.h" + /* * Register definitions: * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map */ /* Base controls */ -#define AXI_DAC_REG_CONFIG 0x0c -#define AXI_DDS_DISABLE BIT(6) +#define AXI_DAC_CONFIG_REG 0x0c +#define AXI_DAC_CONFIG_DDS_DISABLE BIT(6) /* DAC controls */ -#define AXI_DAC_REG_RSTN 0x0040 -#define AXI_DAC_RSTN_CE_N BIT(2) -#define AXI_DAC_RSTN_MMCM_RSTN BIT(1) -#define AXI_DAC_RSTN_RSTN BIT(0) -#define AXI_DAC_REG_CNTRL_1 0x0044 -#define AXI_DAC_SYNC BIT(0) -#define AXI_DAC_REG_CNTRL_2 0x0048 -#define ADI_DAC_R1_MODE BIT(4) -#define AXI_DAC_DRP_STATUS 0x0074 -#define AXI_DAC_DRP_LOCKED BIT(17) +#define AXI_DAC_RSTN_REG 0x0040 +#define AXI_DAC_RSTN_CE_N BIT(2) +#define AXI_DAC_RSTN_MMCM_RSTN BIT(1) +#define AXI_DAC_RSTN_RSTN BIT(0) +#define AXI_DAC_CNTRL_1_REG 0x0044 +#define AXI_DAC_CNTRL_1_SYNC BIT(0) +#define AXI_DAC_CNTRL_2_REG 0x0048 +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16) +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14) +#define ADI_DAC_CNTRL_2_R1_MODE BIT(5) +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4) +#define AXI_DAC_STATUS_1_REG 0x0054 +#define AXI_DAC_STATUS_2_REG 0x0058 +#define AXI_DAC_DRP_STATUS_REG 0x0074 +#define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17) +#define AXI_DAC_CUSTOM_RD_REG 0x0080 +#define AXI_DAC_CUSTOM_WR_REG 0x0084 +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16) +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8) +#define AXI_DAC_UI_STATUS_REG 0x0088 +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4) +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24) +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2) +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1) +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0) + +#define AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE (AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA | \ + AXI_DAC_CUSTOM_CTRL_STREAM) + /* DAC Channel controls */ -#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40) -#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0408 + (c) * 0x40) -#define AXI_DAC_SCALE_SIGN BIT(15) -#define AXI_DAC_SCALE_INT BIT(14) -#define AXI_DAC_SCALE GENMASK(14, 0) -#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40) -#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40) -#define AXI_DAC_PHASE GENMASK(31, 16) -#define AXI_DAC_FREQUENCY GENMASK(15, 0) -#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40) -#define AXI_DAC_DATA_SEL GENMASK(3, 0) +#define AXI_DAC_CHAN_CNTRL_1_REG(c) (0x0400 + (c) * 0x40) +#define AXI_DAC_CHAN_CNTRL_3_REG(c) (0x0408 + (c) * 0x40) +#define AXI_DAC_CHAN_CNTRL_3_SCALE_SIGN BIT(15) +#define AXI_DAC_CHAN_CNTRL_3_SCALE_INT BIT(14) +#define AXI_DAC_CHAN_CNTRL_3_SCALE GENMASK(14, 0) +#define AXI_DAC_CHAN_CNTRL_2_REG(c) (0x0404 + (c) * 0x40) +#define AXI_DAC_CHAN_CNTRL_2_PHASE GENMASK(31, 16) +#define AXI_DAC_CHAN_CNTRL_2_FREQUENCY GENMASK(15, 0) +#define AXI_DAC_CHAN_CNTRL_4_REG(c) (0x040c + (c) * 0x40) +#define AXI_DAC_CHAN_CNTRL_7_REG(c) (0x0418 + (c) * 0x40) +#define AXI_DAC_CHAN_CNTRL_7_DATA_SEL GENMASK(3, 0) + +#define AXI_DAC_RD_ADDR(x) (BIT(7) | (x)) /* 360 degrees in rad */ -#define AXI_DAC_2_PI_MEGA 6283190 +#define AXI_DAC_2_PI_MEGA 6283190 + enum { AXI_DAC_DATA_INTERNAL_TONE, AXI_DAC_DATA_DMA = 2, + AXI_DAC_DATA_INTERNAL_RAMP_16BIT = 11, +}; + +struct axi_dac_info { + unsigned int version; + const struct iio_backend_info *backend_info; + bool has_dac_clk; + bool has_child_nodes; }; struct axi_dac_state { @@ -77,9 +110,11 @@ struct axi_dac_state { * data/variables. */ struct mutex lock; + const struct axi_dac_info *info; u64 dac_clk; u32 reg_config; bool int_tone; + int dac_clk_rate; }; static int axi_dac_enable(struct iio_backend *back) @@ -89,7 +124,7 @@ static int axi_dac_enable(struct iio_backend *back) int ret; guard(mutex)(&st->lock); - ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN, + ret = regmap_set_bits(st->regmap, AXI_DAC_RSTN_REG, AXI_DAC_RSTN_MMCM_RSTN); if (ret) return ret; @@ -98,12 +133,14 @@ static int axi_dac_enable(struct iio_backend *back) * designs really use it but if they don't we still get the lock bit * set. So let's do it all the time so the code is generic. */ - ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val, - __val & AXI_DAC_DRP_LOCKED, 100, 1000); + ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS_REG, + __val, + __val & AXI_DAC_DRP_STATUS_DRP_LOCKED, + 100, 1000); if (ret) return ret; - return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN, + return regmap_set_bits(st->regmap, AXI_DAC_RSTN_REG, AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN); } @@ -112,7 +149,7 @@ static void axi_dac_disable(struct iio_backend *back) struct axi_dac_state *st = iio_backend_get_priv(back); guard(mutex)(&st->lock); - regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0); + regmap_write(st->regmap, AXI_DAC_RSTN_REG, 0); } static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back, @@ -155,15 +192,15 @@ static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan, } if (tone_2) - reg = AXI_DAC_REG_CHAN_CNTRL_4(chan); + reg = AXI_DAC_CHAN_CNTRL_4_REG(chan); else - reg = AXI_DAC_REG_CHAN_CNTRL_2(chan); + reg = AXI_DAC_CHAN_CNTRL_2_REG(chan); ret = regmap_read(st->regmap, reg, &raw); if (ret) return ret; - raw = FIELD_GET(AXI_DAC_FREQUENCY, raw); + raw = FIELD_GET(AXI_DAC_CHAN_CNTRL_2_FREQUENCY, raw); *freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16)); return 0; @@ -194,17 +231,18 @@ static int axi_dac_scale_get(struct axi_dac_state *st, u32 reg, raw; if (tone_2) - reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_3_REG(chan->channel); else - reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_1_REG(chan->channel); ret = regmap_read(st->regmap, reg, &raw); if (ret) return ret; - sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw); - raw = FIELD_GET(AXI_DAC_SCALE, raw); - scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT); + sign = FIELD_GET(AXI_DAC_CHAN_CNTRL_3_SCALE_SIGN, raw); + raw = FIELD_GET(AXI_DAC_CHAN_CNTRL_3_SCALE, raw); + scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, + AXI_DAC_CHAN_CNTRL_3_SCALE_INT); vals[0] = scale / MEGA; vals[1] = scale % MEGA; @@ -227,15 +265,15 @@ static int axi_dac_phase_get(struct axi_dac_state *st, int ret, vals[2]; if (tone_2) - reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_4_REG(chan->channel); else - reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_2_REG(chan->channel); ret = regmap_read(st->regmap, reg, &raw); if (ret) return ret; - raw = FIELD_GET(AXI_DAC_PHASE, raw); + raw = FIELD_GET(AXI_DAC_CHAN_CNTRL_2_PHASE, raw); phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX); vals[0] = phase / MEGA; @@ -260,18 +298,20 @@ static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan, } if (tone_2) - reg = AXI_DAC_REG_CHAN_CNTRL_4(chan); + reg = AXI_DAC_CHAN_CNTRL_4_REG(chan); else - reg = AXI_DAC_REG_CHAN_CNTRL_2(chan); + reg = AXI_DAC_CHAN_CNTRL_2_REG(chan); raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate); - ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, raw); + ret = regmap_update_bits(st->regmap, reg, + AXI_DAC_CHAN_CNTRL_2_FREQUENCY, raw); if (ret) return ret; /* synchronize channels */ - return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC); + return regmap_set_bits(st->regmap, AXI_DAC_CNTRL_1_REG, + AXI_DAC_CNTRL_1_SYNC); } static int axi_dac_frequency_set(struct axi_dac_state *st, @@ -312,16 +352,16 @@ static int axi_dac_scale_set(struct axi_dac_state *st, /* format is 1.1.14 (sign, integer and fractional bits) */ if (scale < 0) { - raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1); + raw = FIELD_PREP(AXI_DAC_CHAN_CNTRL_3_SCALE_SIGN, 1); scale *= -1; } - raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA); + raw |= div_u64((u64)scale * AXI_DAC_CHAN_CNTRL_3_SCALE_INT, MEGA); if (tone_2) - reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_3_REG(chan->channel); else - reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_1_REG(chan->channel); guard(mutex)(&st->lock); ret = regmap_write(st->regmap, reg, raw); @@ -329,7 +369,8 @@ static int axi_dac_scale_set(struct axi_dac_state *st, return ret; /* synchronize channels */ - ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC); + ret = regmap_set_bits(st->regmap, AXI_DAC_CNTRL_1_REG, + AXI_DAC_CNTRL_1_SYNC); if (ret) return ret; @@ -355,18 +396,19 @@ static int axi_dac_phase_set(struct axi_dac_state *st, raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA); if (tone_2) - reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_4_REG(chan->channel); else - reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel); + reg = AXI_DAC_CHAN_CNTRL_2_REG(chan->channel); guard(mutex)(&st->lock); - ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE, - FIELD_PREP(AXI_DAC_PHASE, raw)); + ret = regmap_update_bits(st->regmap, reg, AXI_DAC_CHAN_CNTRL_2_PHASE, + FIELD_PREP(AXI_DAC_CHAN_CNTRL_2_PHASE, raw)); if (ret) return ret; /* synchronize channels */ - ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC); + ret = regmap_set_bits(st->regmap, AXI_DAC_CNTRL_1_REG, + AXI_DAC_CNTRL_1_SYNC); if (ret) return ret; @@ -437,7 +479,7 @@ static int axi_dac_extend_chan(struct iio_backend *back, if (chan->type != IIO_ALTVOLTAGE) return -EINVAL; - if (st->reg_config & AXI_DDS_DISABLE) + if (st->reg_config & AXI_DAC_CONFIG_DDS_DISABLE) /* nothing to extend */ return 0; @@ -454,13 +496,19 @@ static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan, switch (data) { case IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE: return regmap_update_bits(st->regmap, - AXI_DAC_REG_CHAN_CNTRL_7(chan), - AXI_DAC_DATA_SEL, + AXI_DAC_CHAN_CNTRL_7_REG(chan), + AXI_DAC_CHAN_CNTRL_7_DATA_SEL, AXI_DAC_DATA_INTERNAL_TONE); case IIO_BACKEND_EXTERNAL: return regmap_update_bits(st->regmap, - AXI_DAC_REG_CHAN_CNTRL_7(chan), - AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA); + AXI_DAC_CHAN_CNTRL_7_REG(chan), + AXI_DAC_CHAN_CNTRL_7_DATA_SEL, + AXI_DAC_DATA_DMA); + case IIO_BACKEND_INTERNAL_RAMP_16BIT: + return regmap_update_bits(st->regmap, + AXI_DAC_CHAN_CNTRL_7_REG(chan), + AXI_DAC_CHAN_CNTRL_7_DATA_SEL, + AXI_DAC_DATA_INTERNAL_RAMP_16BIT); default: return -EINVAL; } @@ -475,7 +523,7 @@ static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan, if (!sample_rate) return -EINVAL; - if (st->reg_config & AXI_DDS_DISABLE) + if (st->reg_config & AXI_DAC_CONFIG_DDS_DISABLE) /* sample_rate has no meaning if DDS is disabled */ return 0; @@ -518,6 +566,184 @@ static int axi_dac_reg_access(struct iio_backend *back, unsigned int reg, return regmap_write(st->regmap, reg, writeval); } +static int axi_dac_ddr_enable(struct iio_backend *back) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + return regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + AXI_DAC_CNTRL_2_SDR_DDR_N); +} + +static int axi_dac_ddr_disable(struct iio_backend *back) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + return regmap_set_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + AXI_DAC_CNTRL_2_SDR_DDR_N); +} + +static int axi_dac_data_stream_enable(struct iio_backend *back) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + return regmap_set_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE); +} + +static int axi_dac_data_stream_disable(struct iio_backend *back) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + return regmap_clear_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE); +} + +static int axi_dac_data_transfer_addr(struct iio_backend *back, u32 address) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + if (address > FIELD_MAX(AXI_DAC_CUSTOM_CTRL_ADDRESS)) + return -EINVAL; + + /* + * Sample register address, when the DAC is configured, or stream + * start address when the FSM is in stream state. + */ + return regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_ADDRESS, + FIELD_PREP(AXI_DAC_CUSTOM_CTRL_ADDRESS, + address)); +} + +static int axi_dac_data_format_set(struct iio_backend *back, unsigned int ch, + const struct iio_backend_data_fmt *data) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + switch (data->type) { + case IIO_BACKEND_DATA_UNSIGNED: + return regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + AXI_DAC_CNTRL_2_UNSIGNED_DATA); + default: + return -EINVAL; + } +} + +static int __axi_dac_bus_reg_write(struct iio_backend *back, u32 reg, + u32 val, size_t data_size) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + int ret; + u32 ival; + + /* + * Both AXI_DAC_CNTRL_2_REG and AXI_DAC_CUSTOM_WR_REG need to know + * the data size. So keeping data size control here only, + * since data size is mandatory for the current transfer. + * DDR state handled separately by specific backend calls, + * generally all raw register writes are SDR. + */ + if (data_size == sizeof(u16)) + ival = FIELD_PREP(AXI_DAC_CUSTOM_WR_DATA_16, val); + else + ival = FIELD_PREP(AXI_DAC_CUSTOM_WR_DATA_8, val); + + ret = regmap_write(st->regmap, AXI_DAC_CUSTOM_WR_REG, ival); + if (ret) + return ret; + + if (data_size == sizeof(u8)) + ret = regmap_set_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + AXI_DAC_CNTRL_2_SYMB_8B); + else + ret = regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + AXI_DAC_CNTRL_2_SYMB_8B); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_ADDRESS, + FIELD_PREP(AXI_DAC_CUSTOM_CTRL_ADDRESS, reg)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA, + AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(st->regmap, + AXI_DAC_UI_STATUS_REG, ival, + FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0, + 10, 100 * KILO); + if (ret == -ETIMEDOUT) + dev_err(st->dev, "AXI read timeout\n"); + + /* Cleaning always AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA */ + return regmap_clear_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA); +} + +static int axi_dac_bus_reg_write(struct iio_backend *back, u32 reg, + u32 val, size_t data_size) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + guard(mutex)(&st->lock); + return __axi_dac_bus_reg_write(back, reg, val, data_size); +} + +static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val, + size_t data_size) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + int ret; + + guard(mutex)(&st->lock); + + /* + * SPI, we write with read flag, then we read just at the AXI + * io address space to get data read. + */ + ret = __axi_dac_bus_reg_write(back, AXI_DAC_RD_ADDR(reg), 0, + data_size); + if (ret) + return ret; + + return regmap_read(st->regmap, AXI_DAC_CUSTOM_RD_REG, val); +} + +static void axi_dac_child_remove(void *data) +{ + platform_device_unregister(data); +} + +static int axi_dac_create_platform_device(struct axi_dac_state *st, + struct fwnode_handle *child) +{ + struct ad3552r_hs_platform_data pdata = { + .bus_reg_read = axi_dac_bus_reg_read, + .bus_reg_write = axi_dac_bus_reg_write, + .bus_sample_data_clock_hz = st->dac_clk_rate, + }; + struct platform_device_info pi = { + .parent = st->dev, + .name = fwnode_get_name(child), + .id = PLATFORM_DEVID_AUTO, + .fwnode = child, + .data = &pdata, + .size_data = sizeof(pdata), + }; + struct platform_device *pdev; + + pdev = platform_device_register_full(&pi); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + return devm_add_action_or_reset(st->dev, axi_dac_child_remove, pdev); +} + static const struct iio_backend_ops axi_dac_generic_ops = { .enable = axi_dac_enable, .disable = axi_dac_disable, @@ -531,11 +757,30 @@ static const struct iio_backend_ops axi_dac_generic_ops = { .debugfs_reg_access = iio_backend_debugfs_ptr(axi_dac_reg_access), }; +static const struct iio_backend_ops axi_ad3552r_ops = { + .enable = axi_dac_enable, + .disable = axi_dac_disable, + .request_buffer = axi_dac_request_buffer, + .free_buffer = axi_dac_free_buffer, + .data_source_set = axi_dac_data_source_set, + .ddr_enable = axi_dac_ddr_enable, + .ddr_disable = axi_dac_ddr_disable, + .data_stream_enable = axi_dac_data_stream_enable, + .data_stream_disable = axi_dac_data_stream_disable, + .data_format_set = axi_dac_data_format_set, + .data_transfer_addr = axi_dac_data_transfer_addr, +}; + static const struct iio_backend_info axi_dac_generic = { .name = "axi-dac", .ops = &axi_dac_generic_ops, }; +static const struct iio_backend_info axi_ad3552r = { + .name = "axi-ad3552r", + .ops = &axi_ad3552r_ops, +}; + static const struct regmap_config axi_dac_regmap_config = { .val_bits = 32, .reg_bits = 32, @@ -545,7 +790,6 @@ static const struct regmap_config axi_dac_regmap_config = { static int axi_dac_probe(struct platform_device *pdev) { - const unsigned int *expected_ver; struct axi_dac_state *st; void __iomem *base; unsigned int ver; @@ -556,14 +800,29 @@ static int axi_dac_probe(struct platform_device *pdev) if (!st) return -ENOMEM; - expected_ver = device_get_match_data(&pdev->dev); - if (!expected_ver) + st->info = device_get_match_data(&pdev->dev); + if (!st->info) return -ENODEV; + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(clk)) { + /* Backward compat., old fdt versions without clock-names. */ + clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), + "failed to get clock\n"); + } - clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(clk), - "failed to get clock\n"); + if (st->info->has_dac_clk) { + struct clk *dac_clk; + + dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk"); + if (IS_ERR(dac_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk), + "failed to get dac_clk clock\n"); + + /* We only care about the streaming mode rate */ + st->dac_clk_rate = clk_get_rate(dac_clk) / 2; + } base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -580,7 +839,7 @@ static int axi_dac_probe(struct platform_device *pdev) * Force disable the core. Up to the frontend to enable us. And we can * still read/write registers... */ - ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0); + ret = regmap_write(st->regmap, AXI_DAC_RSTN_REG, 0); if (ret) return ret; @@ -588,12 +847,13 @@ static int axi_dac_probe(struct platform_device *pdev) if (ret) return ret; - if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) { + if (ADI_AXI_PCORE_VER_MAJOR(ver) != + ADI_AXI_PCORE_VER_MAJOR(st->info->version)) { dev_err(&pdev->dev, "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", - ADI_AXI_PCORE_VER_MAJOR(*expected_ver), - ADI_AXI_PCORE_VER_MINOR(*expected_ver), - ADI_AXI_PCORE_VER_PATCH(*expected_ver), + ADI_AXI_PCORE_VER_MAJOR(st->info->version), + ADI_AXI_PCORE_VER_MINOR(st->info->version), + ADI_AXI_PCORE_VER_PATCH(st->info->version), ADI_AXI_PCORE_VER_MAJOR(ver), ADI_AXI_PCORE_VER_MINOR(ver), ADI_AXI_PCORE_VER_PATCH(ver)); @@ -601,7 +861,7 @@ static int axi_dac_probe(struct platform_device *pdev) } /* Let's get the core read only configuration */ - ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config); + ret = regmap_read(st->regmap, AXI_DAC_CONFIG_REG, &st->reg_config); if (ret) return ret; @@ -613,16 +873,40 @@ static int axi_dac_probe(struct platform_device *pdev) * want independent channels let's override the core's default value and * set the R1_MODE bit. */ - ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE); + ret = regmap_set_bits(st->regmap, AXI_DAC_CNTRL_2_REG, + ADI_DAC_CNTRL_2_R1_MODE); if (ret) return ret; mutex_init(&st->lock); - ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st); + + ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st); if (ret) return dev_err_probe(&pdev->dev, ret, "failed to register iio backend\n"); + device_for_each_child_node_scoped(&pdev->dev, child) { + int val; + + if (!st->info->has_child_nodes) + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid fdt axi-dac compatible."); + + /* Processing only reg 0 node */ + ret = fwnode_property_read_u32(child, "reg", &val); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "invalid reg property."); + if (val != 0) + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid node address."); + + ret = axi_dac_create_platform_device(st, child); + if (ret) + return dev_err_probe(&pdev->dev, -EINVAL, + "cannot create device."); + } + dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n", ADI_AXI_PCORE_VER_MAJOR(ver), ADI_AXI_PCORE_VER_MINOR(ver), @@ -631,10 +915,21 @@ static int axi_dac_probe(struct platform_device *pdev) return 0; } -static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b'); +static const struct axi_dac_info dac_generic = { + .version = ADI_AXI_PCORE_VER(9, 1, 'b'), + .backend_info = &axi_dac_generic, +}; + +static const struct axi_dac_info dac_ad3552r = { + .version = ADI_AXI_PCORE_VER(9, 1, 'b'), + .backend_info = &axi_ad3552r, + .has_dac_clk = true, + .has_child_nodes = true, +}; static const struct of_device_id axi_dac_of_match[] = { - { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info }, + { .compatible = "adi,axi-dac-9.1.b", .data = &dac_generic }, + { .compatible = "adi,axi-ad3552r", .data = &dac_ad3552r }, {} }; MODULE_DEVICE_TABLE(of, axi_dac_of_match); diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index 7332064d0852d..f36f10bfb6be7 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -243,7 +243,7 @@ MODULE_DEVICE_TABLE(of, dpot_dac_match); static struct platform_driver dpot_dac_driver = { .probe = dpot_dac_probe, - .remove_new = dpot_dac_remove, + .remove = dpot_dac_remove, .driver = { .name = "iio-dpot-dac", .of_match_table = dpot_dac_match, diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index b3aa4443a6a48..2332b0c226915 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -184,9 +184,9 @@ static const struct of_device_id lpc18xx_dac_match[] = { MODULE_DEVICE_TABLE(of, lpc18xx_dac_match); static struct platform_driver lpc18xx_dac_driver = { - .probe = lpc18xx_dac_probe, - .remove_new = lpc18xx_dac_remove, - .driver = { + .probe = lpc18xx_dac_probe, + .remove = lpc18xx_dac_remove, + .driver = { .name = "lpc18xx-dac", .of_match_table = lpc18xx_dac_match, }, diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index ae53baccec914..3497513854d7d 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -201,7 +201,7 @@ static int m62332_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &m62332_info; - ret = iio_map_array_register(indio_dev, client->dev.platform_data); + ret = iio_map_array_register(indio_dev, dev_get_platdata(&client->dev)); if (ret < 0) return ret; diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 685980184d3c8..84336736a47bd 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -143,10 +143,10 @@ static const struct iio_chan_spec max517_channels[] = { static int max517_probe(struct i2c_client *client) { + const struct max517_platform_data *platform_data = dev_get_platdata(&client->dev); const struct i2c_device_id *id = i2c_client_get_device_id(client); struct max517_data *data; struct iio_dev *indio_dev; - struct max517_platform_data *platform_data = client->dev.platform_data; int chan; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 2d567073996b7..95ed5197d16f5 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -245,7 +245,7 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); static struct platform_driver stm32_dac_driver = { .probe = stm32_dac_probe, - .remove_new = stm32_dac_remove, + .remove = stm32_dac_remove, .driver = { .name = "stm32-dac-core", .of_match_table = stm32_dac_of_match, diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index 5a722f307e7e3..3bfb368b3a234 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -398,7 +398,7 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); static struct platform_driver stm32_dac_driver = { .probe = stm32_dac_probe, - .remove_new = stm32_dac_remove, + .remove = stm32_dac_remove, .driver = { .name = "stm32-dac", .of_match_table = stm32_dac_of_match, diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index de73bc5a1c93c..82a078fa98ad9 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -272,7 +272,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, static struct platform_driver vf610_dac_driver = { .probe = vf610_dac_probe, - .remove_new = vf610_dac_remove, + .remove = vf610_dac_remove, .driver = { .name = "vf610-dac", .of_match_table = vf610_dac_match, diff --git a/drivers/iio/dummy/iio_simple_dummy.h b/drivers/iio/dummy/iio_simple_dummy.h index a91622ac54e06..8246f25dbad04 100644 --- a/drivers/iio/dummy/iio_simple_dummy.h +++ b/drivers/iio/dummy/iio_simple_dummy.h @@ -60,7 +60,7 @@ int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state); + bool state); int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, diff --git a/drivers/iio/dummy/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c index 63a2b844be508..b51ec21b6309a 100644 --- a/drivers/iio/dummy/iio_simple_dummy_events.c +++ b/drivers/iio/dummy/iio_simple_dummy_events.c @@ -53,7 +53,7 @@ int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct iio_dummy_state *st = iio_priv(indio_dev); @@ -183,36 +183,34 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) switch (st->regs->reg_data) { case 0: iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, 0, 0, 0), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), st->event_timestamp); break; case 1: if (st->activity_running > st->event_val) iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_ACTIVITY, 0, - IIO_MOD_RUNNING, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, - 0, 0, 0), + IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0, + IIO_MOD_RUNNING, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), st->event_timestamp); break; case 2: if (st->activity_walking < st->event_val) iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_ACTIVITY, 0, - IIO_MOD_WALKING, - IIO_EV_DIR_FALLING, - IIO_EV_TYPE_THRESH, - 0, 0, 0), + IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0, + IIO_MOD_WALKING, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), st->event_timestamp); break; case 3: iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, - IIO_EV_DIR_NONE, - IIO_EV_TYPE_CHANGE, 0, 0, 0), + IIO_UNMOD_EVENT_CODE(IIO_STEPS, 0, + IIO_EV_TYPE_CHANGE, + IIO_EV_DIR_NONE), st->event_timestamp); break; default: diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index b391c6e27ab0d..b1554ced7a26b 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -970,7 +970,7 @@ static int ad9523_setup(struct iio_dev *indio_dev) static int ad9523_probe(struct spi_device *spi) { - struct ad9523_platform_data *pdata = spi->dev.platform_data; + struct ad9523_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; struct ad9523_state *st; int ret; diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index e13e64a5164c1..61828e61e2758 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -603,7 +603,7 @@ static int adf4350_probe(struct spi_device *spi) if (pdata == NULL) return -EINVAL; } else { - pdata = spi->dev.platform_data; + pdata = dev_get_platdata(&spi->dev); } if (!pdata) { diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index b270884648265..d752507e0c986 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -4,6 +4,7 @@ * * Copyright 2019 Analog Devices Inc. */ +#include "linux/dev_printk.h" #include #include #include @@ -150,6 +151,7 @@ static const struct regmap_config adf4371_regmap_config = { }; struct adf4371_chip_info { + const char *name; unsigned int num_channels; const struct iio_chan_spec *channels; }; @@ -157,7 +159,6 @@ struct adf4371_chip_info { struct adf4371_state { struct spi_device *spi; struct regmap *regmap; - struct clk *clkin; /* * Lock for accessing device registers. Some operations require * multiple consecutive R/W operations, during which the device @@ -444,15 +445,16 @@ static const struct iio_chan_spec adf4371_chan[] = { ADF4371_CHANNEL(ADF4371_CH_RF32), }; -static const struct adf4371_chip_info adf4371_chip_info[] = { - [ADF4371] = { - .channels = adf4371_chan, - .num_channels = 4, - }, - [ADF4372] = { - .channels = adf4371_chan, - .num_channels = 3, - } +static const struct adf4371_chip_info adf4371_chip_info = { + .name = "adf4371", + .channels = adf4371_chan, + .num_channels = 4, +}; + +static const struct adf4371_chip_info adf4372_chip_info = { + .name = "adf4372", + .channels = adf4371_chan, + .num_channels = 3, }; static int adf4371_reg_access(struct iio_dev *indio_dev, @@ -542,10 +544,10 @@ static int adf4371_setup(struct adf4371_state *st) static int adf4371_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); struct iio_dev *indio_dev; struct adf4371_state *st; struct regmap *regmap; + struct clk *clkin; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -553,50 +555,49 @@ static int adf4371_probe(struct spi_device *spi) return -ENOMEM; regmap = devm_regmap_init_spi(spi, &adf4371_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Error initializing spi regmap\n"); st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->spi = spi; st->regmap = regmap; mutex_init(&st->lock); - st->chip_info = &adf4371_chip_info[id->driver_data]; - indio_dev->name = id->name; + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + indio_dev->name = st->chip_info->name; indio_dev->info = &adf4371_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; - st->clkin = devm_clk_get_enabled(&spi->dev, "clkin"); - if (IS_ERR(st->clkin)) - return PTR_ERR(st->clkin); + clkin = devm_clk_get_enabled(&spi->dev, "clkin"); + if (IS_ERR(clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(clkin), + "Failed to get clkin\n"); - st->clkin_freq = clk_get_rate(st->clkin); + st->clkin_freq = clk_get_rate(clkin); ret = adf4371_setup(st); - if (ret < 0) { - dev_err(&spi->dev, "ADF4371 setup failed\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&spi->dev, ret, "ADF4371 setup failed\n"); return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id adf4371_id_table[] = { - { "adf4371", ADF4371 }, - { "adf4372", ADF4372 }, + { "adf4371", (kernel_ulong_t)&adf4371_chip_info }, + { "adf4372", (kernel_ulong_t)&adf4372_chip_info }, {} }; MODULE_DEVICE_TABLE(spi, adf4371_id_table); static const struct of_device_id adf4371_of_match[] = { - { .compatible = "adi,adf4371" }, - { .compatible = "adi,adf4372" }, + { .compatible = "adi,adf4371", .data = &adf4371_chip_info }, + { .compatible = "adi,adf4372", .data = &adf4372_chip_info}, { }, }; MODULE_DEVICE_TABLE(of, adf4371_of_match); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 97b86c4a53a61..3e193ee0fb614 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -27,7 +27,7 @@ config ADIS16136 select IIO_ADIS_LIB_BUFFER if IIO_BUFFER help Say yes here to build support for the Analog Devices ADIS16133, ADIS16135, - ADIS16136 gyroscope devices. + ADIS16136, ADIS16137 gyroscope devices. config ADIS16260 tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver" diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 10728d5ccae39..ba877d067afb7 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -444,7 +443,7 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, static int bmg160_get_bw(struct bmg160_data *data, int *val) { - struct device *dev = regmap_get_device(data->regmap); + struct device *dev = regmap_get_device(data->regmap); int i; unsigned int bw_bits; int ret; @@ -749,7 +748,7 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct bmg160_data *data = iio_priv(indio_dev); int ret; @@ -1055,17 +1054,6 @@ static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = { .postdisable = bmg160_buffer_postdisable, }; -static const char *bmg160_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - return dev_name(dev); -} - int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, const char *name) { @@ -1098,9 +1086,6 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, mutex_init(&data->mutex); - if (ACPI_HANDLE(dev)) - name = bmg160_match_acpi_device(dev); - indio_dev->channels = bmg160_channels; indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); indio_dev->name = name; diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c index 672d0b720f610..9c5d7e8ee99cc 100644 --- a/drivers/iio/gyro/bmg160_i2c.c +++ b/drivers/iio/gyro/bmg160_i2c.c @@ -17,7 +17,7 @@ static int bmg160_i2c_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); struct regmap *regmap; - const char *name = NULL; + const char *name; regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf); if (IS_ERR(regmap)) { @@ -28,6 +28,8 @@ static int bmg160_i2c_probe(struct i2c_client *client) if (id) name = id->name; + else + name = iio_get_acpi_device_name(&client->dev); return bmg160_core_probe(&client->dev, regmap, client->irq, name); } @@ -39,8 +41,6 @@ static void bmg160_i2c_remove(struct i2c_client *client) static const struct acpi_device_id bmg160_acpi_match[] = { {"BMG0160", 0}, - {"BMI055B", 0}, - {"BMI088B", 0}, {}, }; diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index c28d17ca6f5ee..688966129f704 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -849,8 +849,7 @@ static int fxas21002c_trigger_probe(struct fxas21002c_data *data) if (!data->dready_trig) return -ENOMEM; - irq_trig = irqd_get_trigger_type(irq_get_irq_data(data->irq)); - + irq_trig = irq_get_trigger_type(data->irq); if (irq_trig == IRQF_TRIGGER_RISING) { ret = regmap_field_write(data->regmap_fields[F_IPOL], 1); if (ret < 0) diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 59a38bf9459bd..0598f1d3fbb3d 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -27,7 +27,7 @@ struct gyro_3d_state { struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX]; struct { u32 gyro_val[GYRO_3D_CHANNEL_MAX]; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -279,11 +279,11 @@ static int gyro_3d_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_gyro_3d_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret = 0; static const char *name = "gyro_3d"; struct iio_dev *indio_dev; struct gyro_3d_state *gyro_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*gyro_state)); if (!indio_dev) @@ -361,7 +361,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_gyro_3d_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); @@ -386,7 +386,7 @@ static struct platform_driver hid_gyro_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_gyro_3d_probe, - .remove_new = hid_gyro_3d_remove, + .remove = hid_gyro_3d_remove, }; module_platform_driver(hid_gyro_3d_platform_driver); diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 35af68b41408f..b6883e8b2a8b5 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1059,12 +1059,12 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) /* Check if IRQ is open drain */ mpu3050->irq_opendrain = device_property_read_bool(dev, "drive-open-drain"); - irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); /* * Configure the interrupt generator hardware to supply whatever * the interrupt is configured for, edges low/high level low/high, * we can provide it all. */ + irq_trig = irq_get_trigger_type(irq); switch (irq_trig) { case IRQF_TRIGGER_RISING: dev_info(&indio_dev->dev, diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index bf6d2636a85e7..f2fa0e1631ffa 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -18,7 +18,7 @@ struct hid_humidity_state { struct hid_sensor_hub_attribute_info humidity_attr; struct { s32 humidity_data; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -287,7 +287,7 @@ static struct platform_driver hid_humidity_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_humidity_probe, - .remove_new = hid_humidity_remove, + .remove = hid_humidity_remove, }; module_platform_driver(hid_humidity_platform_driver); diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 11ef38994a955..4d03db19063ea 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -81,8 +81,7 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev) unsigned long irq_type; int err; - irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); - + irq_type = irq_get_trigger_type(hw->irq); switch (irq_type) { case IRQF_TRIGGER_HIGH: case IRQF_TRIGGER_RISING: diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 782fb80e44c23..ca0efecb5b5c1 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -53,6 +53,7 @@ config ADIS16480 ADIS16485, ADIS16488 inertial sensors. source "drivers/iio/imu/bmi160/Kconfig" +source "drivers/iio/imu/bmi270/Kconfig" source "drivers/iio/imu/bmi323/Kconfig" source "drivers/iio/imu/bno055/Kconfig" @@ -96,6 +97,20 @@ config KMX61 source "drivers/iio/imu/inv_icm42600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" + +config SMI240 + tristate "Bosch Sensor SMI240 Inertial Measurement Unit" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for SMI240 IMU on SPI with + accelerometer and gyroscope. + + This driver can also be built as a module. If so, the module will be + called smi240. + source "drivers/iio/imu/st_lsm6dsx/Kconfig" source "drivers/iio/imu/st_lsm9ds0/Kconfig" diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 7e2d7d5c3b7bc..04c77c2c4df8c 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o obj-y += bmi160/ +obj-y += bmi270/ obj-y += bmi323/ obj-y += bno055/ @@ -27,5 +28,7 @@ obj-y += inv_mpu6050/ obj-$(CONFIG_KMX61) += kmx61.o +obj-$(CONFIG_SMI240) += smi240.o + obj-y += st_lsm6dsx/ obj-y += st_lsm9ds0/ diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 495e8a74ac676..807c1a1476c29 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -690,18 +690,9 @@ static int bmi160_config_device_irq(struct iio_dev *indio_dev, int irq_type, static int bmi160_setup_irq(struct iio_dev *indio_dev, int irq, enum bmi160_int_pin pin) { - struct irq_data *desc; - u32 irq_type; + u32 irq_type = irq_get_trigger_type(irq); int ret; - desc = irq_get_irq_data(irq); - if (!desc) { - dev_err(&indio_dev->dev, "Could not find IRQ %d\n", irq); - return -EINVAL; - } - - irq_type = irqd_get_trigger_type(desc); - ret = bmi160_config_device_irq(indio_dev, irq_type, pin); if (ret) return ret; diff --git a/drivers/iio/imu/bmi270/Kconfig b/drivers/iio/imu/bmi270/Kconfig new file mode 100644 index 0000000000000..6362acc706da1 --- /dev/null +++ b/drivers/iio/imu/bmi270/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# BMI270 IMU driver +# + +config BMI270 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config BMI270_I2C + tristate "Bosch BMI270 I2C driver" + depends on I2C + select BMI270 + select REGMAP_I2C + help + Enable support for the Bosch BMI270 6-Axis IMU connected to I2C + interface. + + This driver can also be built as a module. If so, the module will be + called bmi270_i2c. + +config BMI270_SPI + tristate "Bosch BMI270 SPI driver" + depends on SPI + select BMI270 + select REGMAP_SPI + help + Enable support for the Bosch BMI270 6-Axis IMU connected to SPI + interface. + + This driver can also be built as a module. If so, the module will be + called bmi270_spi. diff --git a/drivers/iio/imu/bmi270/Makefile b/drivers/iio/imu/bmi270/Makefile new file mode 100644 index 0000000000000..d96c96fc3d832 --- /dev/null +++ b/drivers/iio/imu/bmi270/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Bosch BMI270 IMU +# +obj-$(CONFIG_BMI270) += bmi270_core.o +obj-$(CONFIG_BMI270_I2C) += bmi270_i2c.o +obj-$(CONFIG_BMI270_SPI) += bmi270_spi.o diff --git a/drivers/iio/imu/bmi270/bmi270.h b/drivers/iio/imu/bmi270/bmi270.h new file mode 100644 index 0000000000000..fdfad5784cc52 --- /dev/null +++ b/drivers/iio/imu/bmi270/bmi270.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef BMI270_H_ +#define BMI270_H_ + +#include +#include + +struct device; +struct bmi270_data { + struct device *dev; + struct regmap *regmap; + const struct bmi270_chip_info *chip_info; + + /* + * Where IIO_DMA_MINALIGN may be larger than 8 bytes, align to + * that to ensure a DMA safe buffer. + */ + struct { + __le16 channels[6]; + aligned_s64 timestamp; + } data __aligned(IIO_DMA_MINALIGN); +}; + +struct bmi270_chip_info { + const char *name; + int chip_id; + const char *fw_name; +}; + +extern const struct regmap_config bmi270_regmap_config; +extern const struct bmi270_chip_info bmi260_chip_info; +extern const struct bmi270_chip_info bmi270_chip_info; + +int bmi270_core_probe(struct device *dev, struct regmap *regmap, + const struct bmi270_chip_info *chip_info); + +#endif /* BMI270_H_ */ diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c new file mode 100644 index 0000000000000..70db83a202391 --- /dev/null +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -0,0 +1,734 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "bmi270.h" + +#define BMI270_CHIP_ID_REG 0x00 + +/* Checked to prevent sending incompatible firmware to BMI160 devices */ +#define BMI160_CHIP_ID_VAL 0xD1 + +#define BMI260_CHIP_ID_VAL 0x27 +#define BMI270_CHIP_ID_VAL 0x24 +#define BMI270_CHIP_ID_MSK GENMASK(7, 0) + +#define BMI270_ACCEL_X_REG 0x0c +#define BMI270_ANG_VEL_X_REG 0x12 + +#define BMI270_INTERNAL_STATUS_REG 0x21 +#define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) +#define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 + +#define BMI270_INTERNAL_STATUS_AXES_REMAP_ERR_MSK BIT(5) +#define BMI270_INTERNAL_STATUS_ODR_50HZ_ERR_MSK BIT(6) + +#define BMI270_ACC_CONF_REG 0x40 +#define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) +#define BMI270_ACC_CONF_ODR_100HZ 0x08 +#define BMI270_ACC_CONF_BWP_MSK GENMASK(6, 4) +#define BMI270_ACC_CONF_BWP_NORMAL_MODE 0x02 +#define BMI270_ACC_CONF_FILTER_PERF_MSK BIT(7) + +#define BMI270_ACC_CONF_RANGE_REG 0x41 +#define BMI270_ACC_CONF_RANGE_MSK GENMASK(1, 0) + +#define BMI270_GYR_CONF_REG 0x42 +#define BMI270_GYR_CONF_ODR_MSK GENMASK(3, 0) +#define BMI270_GYR_CONF_ODR_200HZ 0x09 +#define BMI270_GYR_CONF_BWP_MSK GENMASK(5, 4) +#define BMI270_GYR_CONF_BWP_NORMAL_MODE 0x02 +#define BMI270_GYR_CONF_NOISE_PERF_MSK BIT(6) +#define BMI270_GYR_CONF_FILTER_PERF_MSK BIT(7) + +#define BMI270_GYR_CONF_RANGE_REG 0x43 +#define BMI270_GYR_CONF_RANGE_MSK GENMASK(2, 0) + +#define BMI270_INIT_CTRL_REG 0x59 +#define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0) + +#define BMI270_INIT_DATA_REG 0x5e + +#define BMI270_PWR_CONF_REG 0x7c +#define BMI270_PWR_CONF_ADV_PWR_SAVE_MSK BIT(0) +#define BMI270_PWR_CONF_FIFO_WKUP_MSK BIT(1) +#define BMI270_PWR_CONF_FUP_EN_MSK BIT(2) + +#define BMI270_PWR_CTRL_REG 0x7d +#define BMI270_PWR_CTRL_AUX_EN_MSK BIT(0) +#define BMI270_PWR_CTRL_GYR_EN_MSK BIT(1) +#define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) +#define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) + +#define BMI260_INIT_DATA_FILE "bmi260-init-data.fw" +#define BMI270_INIT_DATA_FILE "bmi270-init-data.fw" + +enum bmi270_scan { + BMI270_SCAN_ACCEL_X, + BMI270_SCAN_ACCEL_Y, + BMI270_SCAN_ACCEL_Z, + BMI270_SCAN_GYRO_X, + BMI270_SCAN_GYRO_Y, + BMI270_SCAN_GYRO_Z, + BMI270_SCAN_TIMESTAMP, +}; + +static const unsigned long bmi270_avail_scan_masks[] = { + (BIT(BMI270_SCAN_ACCEL_X) | + BIT(BMI270_SCAN_ACCEL_Y) | + BIT(BMI270_SCAN_ACCEL_Z) | + BIT(BMI270_SCAN_GYRO_X) | + BIT(BMI270_SCAN_GYRO_Y) | + BIT(BMI270_SCAN_GYRO_Z)), + 0 +}; + +const struct bmi270_chip_info bmi260_chip_info = { + .name = "bmi260", + .chip_id = BMI260_CHIP_ID_VAL, + .fw_name = BMI260_INIT_DATA_FILE, +}; +EXPORT_SYMBOL_NS_GPL(bmi260_chip_info, IIO_BMI270); + +const struct bmi270_chip_info bmi270_chip_info = { + .name = "bmi270", + .chip_id = BMI270_CHIP_ID_VAL, + .fw_name = BMI270_INIT_DATA_FILE, +}; +EXPORT_SYMBOL_NS_GPL(bmi270_chip_info, IIO_BMI270); + +enum bmi270_sensor_type { + BMI270_ACCEL = 0, + BMI270_GYRO, +}; + +struct bmi270_scale { + int scale; + int uscale; +}; + +struct bmi270_odr { + int odr; + int uodr; +}; + +static const struct bmi270_scale bmi270_accel_scale[] = { + { 0, 598 }, + { 0, 1197 }, + { 0, 2394 }, + { 0, 4788 }, +}; + +static const struct bmi270_scale bmi270_gyro_scale[] = { + { 0, 1065 }, + { 0, 532 }, + { 0, 266 }, + { 0, 133 }, + { 0, 66 }, +}; + +struct bmi270_scale_item { + const struct bmi270_scale *tbl; + int num; +}; + +static const struct bmi270_scale_item bmi270_scale_table[] = { + [BMI270_ACCEL] = { + .tbl = bmi270_accel_scale, + .num = ARRAY_SIZE(bmi270_accel_scale), + }, + [BMI270_GYRO] = { + .tbl = bmi270_gyro_scale, + .num = ARRAY_SIZE(bmi270_gyro_scale), + }, +}; + +static const struct bmi270_odr bmi270_accel_odr[] = { + { 0, 781250 }, + { 1, 562500 }, + { 3, 125000 }, + { 6, 250000 }, + { 12, 500000 }, + { 25, 0 }, + { 50, 0 }, + { 100, 0 }, + { 200, 0 }, + { 400, 0 }, + { 800, 0 }, + { 1600, 0 }, +}; + +static const u8 bmi270_accel_odr_vals[] = { + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, +}; + +static const struct bmi270_odr bmi270_gyro_odr[] = { + { 25, 0 }, + { 50, 0 }, + { 100, 0 }, + { 200, 0 }, + { 400, 0 }, + { 800, 0 }, + { 1600, 0 }, + { 3200, 0 }, +}; + +static const u8 bmi270_gyro_odr_vals[] = { + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, +}; + +struct bmi270_odr_item { + const struct bmi270_odr *tbl; + const u8 *vals; + int num; +}; + +static const struct bmi270_odr_item bmi270_odr_table[] = { + [BMI270_ACCEL] = { + .tbl = bmi270_accel_odr, + .vals = bmi270_accel_odr_vals, + .num = ARRAY_SIZE(bmi270_accel_odr), + }, + [BMI270_GYRO] = { + .tbl = bmi270_gyro_odr, + .vals = bmi270_gyro_odr_vals, + .num = ARRAY_SIZE(bmi270_gyro_odr), + }, +}; + +static int bmi270_set_scale(struct bmi270_data *data, int chan_type, int uscale) +{ + int i; + int reg, mask; + struct bmi270_scale_item bmi270_scale_item; + + switch (chan_type) { + case IIO_ACCEL: + reg = BMI270_ACC_CONF_RANGE_REG; + mask = BMI270_ACC_CONF_RANGE_MSK; + bmi270_scale_item = bmi270_scale_table[BMI270_ACCEL]; + break; + case IIO_ANGL_VEL: + reg = BMI270_GYR_CONF_RANGE_REG; + mask = BMI270_GYR_CONF_RANGE_MSK; + bmi270_scale_item = bmi270_scale_table[BMI270_GYRO]; + break; + default: + return -EINVAL; + } + + for (i = 0; i < bmi270_scale_item.num; i++) { + if (bmi270_scale_item.tbl[i].uscale != uscale) + continue; + + return regmap_update_bits(data->regmap, reg, mask, i); + } + + return -EINVAL; +} + +static int bmi270_get_scale(struct bmi270_data *bmi270_device, int chan_type, + int *uscale) +{ + int ret; + unsigned int val; + struct bmi270_scale_item bmi270_scale_item; + + switch (chan_type) { + case IIO_ACCEL: + ret = regmap_read(bmi270_device->regmap, + BMI270_ACC_CONF_RANGE_REG, &val); + if (ret) + return ret; + + val = FIELD_GET(BMI270_ACC_CONF_RANGE_MSK, val); + bmi270_scale_item = bmi270_scale_table[BMI270_ACCEL]; + break; + case IIO_ANGL_VEL: + ret = regmap_read(bmi270_device->regmap, + BMI270_GYR_CONF_RANGE_REG, &val); + if (ret) + return ret; + + val = FIELD_GET(BMI270_GYR_CONF_RANGE_MSK, val); + bmi270_scale_item = bmi270_scale_table[BMI270_GYRO]; + break; + default: + return -EINVAL; + } + + if (val >= bmi270_scale_item.num) + return -EINVAL; + + *uscale = bmi270_scale_item.tbl[val].uscale; + return 0; +} + +static int bmi270_set_odr(struct bmi270_data *data, int chan_type, int odr, + int uodr) +{ + int i; + int reg, mask; + struct bmi270_odr_item bmi270_odr_item; + + switch (chan_type) { + case IIO_ACCEL: + reg = BMI270_ACC_CONF_REG; + mask = BMI270_ACC_CONF_ODR_MSK; + bmi270_odr_item = bmi270_odr_table[BMI270_ACCEL]; + break; + case IIO_ANGL_VEL: + reg = BMI270_GYR_CONF_REG; + mask = BMI270_GYR_CONF_ODR_MSK; + bmi270_odr_item = bmi270_odr_table[BMI270_GYRO]; + break; + default: + return -EINVAL; + } + + for (i = 0; i < bmi270_odr_item.num; i++) { + if (bmi270_odr_item.tbl[i].odr != odr || + bmi270_odr_item.tbl[i].uodr != uodr) + continue; + + return regmap_update_bits(data->regmap, reg, mask, + bmi270_odr_item.vals[i]); + } + + return -EINVAL; +} + +static int bmi270_get_odr(struct bmi270_data *data, int chan_type, int *odr, + int *uodr) +{ + int i, val, ret; + struct bmi270_odr_item bmi270_odr_item; + + switch (chan_type) { + case IIO_ACCEL: + ret = regmap_read(data->regmap, BMI270_ACC_CONF_REG, &val); + if (ret) + return ret; + + val = FIELD_GET(BMI270_ACC_CONF_ODR_MSK, val); + bmi270_odr_item = bmi270_odr_table[BMI270_ACCEL]; + break; + case IIO_ANGL_VEL: + ret = regmap_read(data->regmap, BMI270_GYR_CONF_REG, &val); + if (ret) + return ret; + + val = FIELD_GET(BMI270_GYR_CONF_ODR_MSK, val); + bmi270_odr_item = bmi270_odr_table[BMI270_GYRO]; + break; + default: + return -EINVAL; + } + + for (i = 0; i < bmi270_odr_item.num; i++) { + if (val != bmi270_odr_item.vals[i]) + continue; + + *odr = bmi270_odr_item.tbl[i].odr; + *uodr = bmi270_odr_item.tbl[i].uodr; + return 0; + } + + return -EINVAL; +} + +static irqreturn_t bmi270_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmi270_data *bmi270_device = iio_priv(indio_dev); + int ret; + + ret = regmap_bulk_read(bmi270_device->regmap, BMI270_ACCEL_X_REG, + &bmi270_device->data.channels, + sizeof(bmi270_device->data.channels)); + + if (ret) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, &bmi270_device->data, + pf->timestamp); +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int bmi270_get_data(struct bmi270_data *bmi270_device, + int chan_type, int axis, int *val) +{ + __le16 sample; + int reg; + int ret; + + switch (chan_type) { + case IIO_ACCEL: + reg = BMI270_ACCEL_X_REG + (axis - IIO_MOD_X) * 2; + break; + case IIO_ANGL_VEL: + reg = BMI270_ANG_VEL_X_REG + (axis - IIO_MOD_X) * 2; + break; + default: + return -EINVAL; + } + + ret = regmap_bulk_read(bmi270_device->regmap, reg, &sample, sizeof(sample)); + if (ret) + return ret; + + *val = sign_extend32(le16_to_cpu(sample), 15); + + return 0; +} + +static int bmi270_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct bmi270_data *bmi270_device = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = bmi270_get_data(bmi270_device, chan->type, chan->channel2, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + ret = bmi270_get_scale(bmi270_device, chan->type, val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = bmi270_get_odr(bmi270_device, chan->type, val, val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int bmi270_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bmi270_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return bmi270_set_scale(data, chan->type, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return bmi270_set_odr(data, chan->type, val, val2); + default: + return -EINVAL; + } +} + +static int bmi270_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_MICRO; + switch (chan->type) { + case IIO_ANGL_VEL: + *vals = (const int *)bmi270_gyro_scale; + *length = ARRAY_SIZE(bmi270_gyro_scale) * 2; + return IIO_AVAIL_LIST; + case IIO_ACCEL: + *vals = (const int *)bmi270_accel_scale; + *length = ARRAY_SIZE(bmi270_accel_scale) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + switch (chan->type) { + case IIO_ANGL_VEL: + *vals = (const int *)bmi270_gyro_odr; + *length = ARRAY_SIZE(bmi270_gyro_odr) * 2; + return IIO_AVAIL_LIST; + case IIO_ACCEL: + *vals = (const int *)bmi270_accel_odr; + *length = ARRAY_SIZE(bmi270_accel_odr) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info bmi270_info = { + .read_raw = bmi270_read_raw, + .write_raw = bmi270_write_raw, + .read_avail = bmi270_read_avail, +}; + +#define BMI270_ACCEL_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = BMI270_SCAN_ACCEL_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ +} + +#define BMI270_ANG_VEL_CHANNEL(_axis) { \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = BMI270_SCAN_GYRO_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ +} + +static const struct iio_chan_spec bmi270_channels[] = { + BMI270_ACCEL_CHANNEL(X), + BMI270_ACCEL_CHANNEL(Y), + BMI270_ACCEL_CHANNEL(Z), + BMI270_ANG_VEL_CHANNEL(X), + BMI270_ANG_VEL_CHANNEL(Y), + BMI270_ANG_VEL_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(BMI270_SCAN_TIMESTAMP), +}; + +static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) +{ + int chip_id; + int ret; + struct device *dev = bmi270_device->dev; + struct regmap *regmap = bmi270_device->regmap; + + ret = regmap_read(regmap, BMI270_CHIP_ID_REG, &chip_id); + if (ret) + return dev_err_probe(dev, ret, "Failed to read chip id"); + + /* + * Some manufacturers use "BMI0160" for both the BMI160 and + * BMI260. If the device is actually a BMI160, the bmi160 + * driver should handle it and this driver should not. + */ + if (chip_id == BMI160_CHIP_ID_VAL) + return -ENODEV; + + if (chip_id != bmi270_device->chip_info->chip_id) + dev_info(dev, "Unexpected chip id 0x%x", chip_id); + + if (chip_id == bmi260_chip_info.chip_id) + bmi270_device->chip_info = &bmi260_chip_info; + else if (chip_id == bmi270_chip_info.chip_id) + bmi270_device->chip_info = &bmi270_chip_info; + + return 0; +} + +static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) +{ + int ret; + int status = 0; + const struct firmware *init_data; + struct device *dev = bmi270_device->dev; + struct regmap *regmap = bmi270_device->regmap; + + ret = regmap_clear_bits(regmap, BMI270_PWR_CONF_REG, + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write power configuration"); + + /* + * After disabling advanced power save, all registers are accessible + * after a 450us delay. This delay is specified in table A of the + * datasheet. + */ + usleep_range(450, 1000); + + ret = regmap_clear_bits(regmap, BMI270_INIT_CTRL_REG, + BMI270_INIT_CTRL_LOAD_DONE_MSK); + if (ret) + return dev_err_probe(dev, ret, + "Failed to prepare device to load init data"); + + ret = request_firmware(&init_data, + bmi270_device->chip_info->fw_name, dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to load init data file"); + + ret = regmap_bulk_write(regmap, BMI270_INIT_DATA_REG, + init_data->data, init_data->size); + release_firmware(init_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to write init data"); + + ret = regmap_set_bits(regmap, BMI270_INIT_CTRL_REG, + BMI270_INIT_CTRL_LOAD_DONE_MSK); + if (ret) + return dev_err_probe(dev, ret, + "Failed to stop device initialization"); + + /* + * Wait at least 140ms for the device to complete configuration. + * This delay is specified in table C of the datasheet. + */ + usleep_range(140000, 160000); + + ret = regmap_read(regmap, BMI270_INTERNAL_STATUS_REG, &status); + if (ret) + return dev_err_probe(dev, ret, "Failed to read internal status"); + + if (status != BMI270_INTERNAL_STATUS_MSG_INIT_OK) + return dev_err_probe(dev, -ENODEV, "Device failed to initialize"); + + return 0; +} + +static int bmi270_configure_imu(struct bmi270_data *bmi270_device) +{ + int ret; + struct device *dev = bmi270_device->dev; + struct regmap *regmap = bmi270_device->regmap; + + ret = regmap_set_bits(regmap, BMI270_PWR_CTRL_REG, + BMI270_PWR_CTRL_AUX_EN_MSK | + BMI270_PWR_CTRL_GYR_EN_MSK | + BMI270_PWR_CTRL_ACCEL_EN_MSK); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable accelerometer and gyroscope"); + + ret = regmap_set_bits(regmap, BMI270_ACC_CONF_REG, + FIELD_PREP(BMI270_ACC_CONF_ODR_MSK, + BMI270_ACC_CONF_ODR_100HZ) | + FIELD_PREP(BMI270_ACC_CONF_BWP_MSK, + BMI270_ACC_CONF_BWP_NORMAL_MODE) | + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); + if (ret) + return dev_err_probe(dev, ret, "Failed to configure accelerometer"); + + ret = regmap_set_bits(regmap, BMI270_GYR_CONF_REG, + FIELD_PREP(BMI270_GYR_CONF_ODR_MSK, + BMI270_GYR_CONF_ODR_200HZ) | + FIELD_PREP(BMI270_GYR_CONF_BWP_MSK, + BMI270_GYR_CONF_BWP_NORMAL_MODE) | + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); + if (ret) + return dev_err_probe(dev, ret, "Failed to configure gyroscope"); + + /* Enable FIFO_WKUP, Disable ADV_PWR_SAVE and FUP_EN */ + ret = regmap_write(regmap, BMI270_PWR_CONF_REG, + BMI270_PWR_CONF_FIFO_WKUP_MSK); + if (ret) + return dev_err_probe(dev, ret, "Failed to set power configuration"); + + return 0; +} + +static int bmi270_chip_init(struct bmi270_data *bmi270_device) +{ + int ret; + + ret = bmi270_validate_chip_id(bmi270_device); + if (ret) + return ret; + + ret = bmi270_write_calibration_data(bmi270_device); + if (ret) + return ret; + + return bmi270_configure_imu(bmi270_device); +} + +int bmi270_core_probe(struct device *dev, struct regmap *regmap, + const struct bmi270_chip_info *chip_info) +{ + int ret; + struct bmi270_data *bmi270_device; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*bmi270_device)); + if (!indio_dev) + return -ENOMEM; + + bmi270_device = iio_priv(indio_dev); + bmi270_device->dev = dev; + bmi270_device->regmap = regmap; + bmi270_device->chip_info = chip_info; + + ret = bmi270_chip_init(bmi270_device); + if (ret) + return ret; + + indio_dev->channels = bmi270_channels; + indio_dev->num_channels = ARRAY_SIZE(bmi270_channels); + indio_dev->name = chip_info->name; + indio_dev->available_scan_masks = bmi270_avail_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &bmi270_info; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + bmi270_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(bmi270_core_probe, IIO_BMI270); + +MODULE_AUTHOR("Alex Lanzano"); +MODULE_DESCRIPTION("BMI270 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bmi270/bmi270_i2c.c b/drivers/iio/imu/bmi270/bmi270_i2c.c new file mode 100644 index 0000000000000..6bd82e4362ab1 --- /dev/null +++ b/drivers/iio/imu/bmi270/bmi270_i2c.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +#include +#include +#include +#include +#include + +#include "bmi270.h" + +static const struct regmap_config bmi270_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int bmi270_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + struct device *dev = &client->dev; + const struct bmi270_chip_info *chip_info; + + chip_info = i2c_get_match_data(client); + if (!chip_info) + return -ENODEV; + + regmap = devm_regmap_init_i2c(client, &bmi270_i2c_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init i2c regmap"); + + return bmi270_core_probe(dev, regmap, chip_info); +} + +static const struct i2c_device_id bmi270_i2c_id[] = { + { "bmi260", (kernel_ulong_t)&bmi260_chip_info }, + { "bmi270", (kernel_ulong_t)&bmi270_chip_info }, + { } +}; + +static const struct acpi_device_id bmi270_acpi_match[] = { + /* GPD Win Mini, Aya Neo AIR Pro, OXP Mini Pro, etc. */ + { "BMI0160", (kernel_ulong_t)&bmi260_chip_info }, + { } +}; + +static const struct of_device_id bmi270_of_match[] = { + { .compatible = "bosch,bmi260", .data = &bmi260_chip_info }, + { .compatible = "bosch,bmi270", .data = &bmi270_chip_info }, + { } +}; + +static struct i2c_driver bmi270_i2c_driver = { + .driver = { + .name = "bmi270_i2c", + .acpi_match_table = bmi270_acpi_match, + .of_match_table = bmi270_of_match, + }, + .probe = bmi270_i2c_probe, + .id_table = bmi270_i2c_id, +}; +module_i2c_driver(bmi270_i2c_driver); + +MODULE_AUTHOR("Alex Lanzano"); +MODULE_DESCRIPTION("BMI270 driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMI270); diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c new file mode 100644 index 0000000000000..30b6d13a329c2 --- /dev/null +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +#include +#include +#include +#include +#include + +#include "bmi270.h" + +/* + * The following two functions are taken from the BMI323 spi driver code. + * In section 6.4 of the BMI270 data it specifies that after a read + * operation the first data byte from the device is a dummy byte + */ +static int bmi270_regmap_spi_read(void *spi, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + return spi_write_then_read(spi, reg_buf, reg_size, val_buf, val_size); +} + +static int bmi270_regmap_spi_write(void *spi, const void *data, + size_t count) +{ + u8 *data_buff = (u8 *)data; + + /* + * Remove the extra pad byte since its only needed for the read + * operation + */ + data_buff[1] = data_buff[0]; + return spi_write_then_read(spi, data_buff + 1, count - 1, NULL, 0); +} + +static const struct regmap_bus bmi270_regmap_bus = { + .read = bmi270_regmap_spi_read, + .write = bmi270_regmap_spi_write, +}; + +static const struct regmap_config bmi270_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .pad_bits = 8, + .read_flag_mask = BIT(7), +}; + +static int bmi270_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + struct device *dev = &spi->dev; + const struct bmi270_chip_info *chip_info; + + chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return -ENODEV; + + regmap = devm_regmap_init(dev, &bmi270_regmap_bus, dev, + &bmi270_spi_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init i2c regmap"); + + return bmi270_core_probe(dev, regmap, chip_info); +} + +static const struct spi_device_id bmi270_spi_id[] = { + { "bmi260", (kernel_ulong_t)&bmi260_chip_info }, + { "bmi270", (kernel_ulong_t)&bmi270_chip_info }, + { } +}; + +static const struct of_device_id bmi270_of_match[] = { + { .compatible = "bosch,bmi260", .data = &bmi260_chip_info }, + { .compatible = "bosch,bmi270", .data = &bmi270_chip_info }, + { } +}; + +static struct spi_driver bmi270_spi_driver = { + .driver = { + .name = "bmi270", + .of_match_table = bmi270_of_match, + }, + .probe = bmi270_spi_probe, + .id_table = bmi270_spi_id, +}; +module_spi_driver(bmi270_spi_driver); + +MODULE_AUTHOR("Alex Lanzano"); +MODULE_DESCRIPTION("BMI270 driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMI270); diff --git a/drivers/iio/imu/bmi323/bmi323.h b/drivers/iio/imu/bmi323/bmi323.h index 209bccb1f335c..b4cfe92600a4f 100644 --- a/drivers/iio/imu/bmi323/bmi323.h +++ b/drivers/iio/imu/bmi323/bmi323.h @@ -141,7 +141,6 @@ #define BMI323_STEP_SC1_REG 0x10 #define BMI323_STEP_SC1_WTRMRK_MSK GENMASK(9, 0) #define BMI323_STEP_SC1_RST_CNT_MSK BIT(10) -#define BMI323_STEP_SC1_REG 0x10 #define BMI323_STEP_LEN 2 /* Tap gesture config registers */ diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index e1f3b0d778be0..161bb1d2e7616 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -467,7 +467,7 @@ static int bmi323_feature_engine_events(struct bmi323_data *data, BMI323_FEAT_IO_STATUS_MSK); } -static int bmi323_step_wtrmrk_en(struct bmi323_data *data, int state) +static int bmi323_step_wtrmrk_en(struct bmi323_data *data, bool state) { enum bmi323_irq_pin step_irq; int ret; @@ -484,7 +484,7 @@ static int bmi323_step_wtrmrk_en(struct bmi323_data *data, int state) ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, BMI323_STEP_SC1_WTRMRK_MSK, FIELD_PREP(BMI323_STEP_SC1_WTRMRK_MSK, - state ? 1 : 0)); + state)); if (ret) return ret; @@ -506,7 +506,7 @@ static int bmi323_motion_config_reg(enum iio_event_direction dir) } static int bmi323_motion_event_en(struct bmi323_data *data, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { unsigned int state_value = state ? BMI323_FEAT_XYZ_MSK : 0; int config, ret, msk, raw, field_value; @@ -570,7 +570,7 @@ static int bmi323_motion_event_en(struct bmi323_data *data, } static int bmi323_tap_event_en(struct bmi323_data *data, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { enum bmi323_irq_pin tap_irq; int ret, tap_enabled; @@ -785,7 +785,7 @@ static const struct attribute_group bmi323_event_attribute_group = { static int bmi323_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct bmi323_data *data = iio_priv(indio_dev); @@ -1881,7 +1881,6 @@ static int bmi323_trigger_probe(struct bmi323_data *data, struct fwnode_handle *fwnode; enum bmi323_irq_pin irq_pin; int ret, irq, irq_type; - struct irq_data *desc; fwnode = dev_fwnode(data->dev); if (!fwnode) @@ -1898,12 +1897,7 @@ static int bmi323_trigger_probe(struct bmi323_data *data, irq_pin = BMI323_IRQ_INT2; } - desc = irq_get_irq_data(irq); - if (!desc) - return dev_err_probe(data->dev, -EINVAL, - "Could not find IRQ %d\n", irq); - - irq_type = irqd_get_trigger_type(desc); + irq_type = irq_get_trigger_type(irq); switch (irq_type) { case IRQF_TRIGGER_RISING: latch = false; diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c index 6d189c4b9ff96..281ebfd9c15a8 100644 --- a/drivers/iio/imu/fxos8700_core.c +++ b/drivers/iio/imu/fxos8700_core.c @@ -8,7 +8,6 @@ */ #include #include -#include #include #include diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 56ac198142500..7968aa27f9fd7 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -200,7 +200,6 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); - struct inv_sensors_timestamp *ts = &accel_st->ts; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int fifo_en = 0; unsigned int sleep_temp = 0; @@ -229,7 +228,6 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, } /* update data FIFO write */ - inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); out_unlock: diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index c3924cc6190ee..93b5d7a3339cc 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -673,7 +673,6 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, { struct device *dev = regmap_get_device(regmap); struct inv_icm42600_state *st; - struct irq_data *irq_desc; int irq_type; bool open_drain; int ret; @@ -683,14 +682,7 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, return -ENODEV; } - /* get irq properties, set trigger falling by default */ - irq_desc = irq_get_irq_data(irq); - if (!irq_desc) { - dev_err(dev, "could not find IRQ %d\n", irq); - return -EINVAL; - } - - irq_type = irqd_get_trigger_type(irq_desc); + irq_type = irq_get_trigger_type(irq); if (!irq_type) irq_type = IRQF_TRIGGER_FALLING; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 938af5b640b00..c6bb68bf5e144 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -99,8 +99,6 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); - struct inv_sensors_timestamp *ts = &gyro_st->ts; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int fifo_en = 0; unsigned int sleep_gyro = 0; @@ -128,7 +126,6 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, } /* update data FIFO write */ - inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); out_unlock: diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index ebb31b385881e..19563c58b4b1d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -71,6 +71,22 @@ static int inv_icm42600_probe(struct i2c_client *client) inv_icm42600_i2c_bus_setup); } +/* + * device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_icm42600_id[] = { + { "icm42600", INV_CHIP_ICM42600 }, + { "icm42602", INV_CHIP_ICM42602 }, + { "icm42605", INV_CHIP_ICM42605 }, + { "icm42686", INV_CHIP_ICM42686 }, + { "icm42622", INV_CHIP_ICM42622 }, + { "icm42688", INV_CHIP_ICM42688 }, + { "icm42631", INV_CHIP_ICM42631 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, inv_icm42600_id); + static const struct of_device_id inv_icm42600_of_matches[] = { { .compatible = "invensense,icm42600", @@ -104,6 +120,7 @@ static struct i2c_driver inv_icm42600_driver = { .of_match_table = inv_icm42600_of_matches, .pm = pm_ptr(&inv_icm42600_pm_ops), }, + .id_table = inv_icm42600_id, .probe = inv_icm42600_probe, }; module_i2c_driver(inv_icm42600_driver); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index eae5ff7a3cc10..3b6d05fce65d5 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -67,6 +67,22 @@ static int inv_icm42600_probe(struct spi_device *spi) inv_icm42600_spi_bus_setup); } +/* + * device id table is used to identify what device can be + * supported by this driver + */ +static const struct spi_device_id inv_icm42600_id[] = { + { "icm42600", INV_CHIP_ICM42600 }, + { "icm42602", INV_CHIP_ICM42602 }, + { "icm42605", INV_CHIP_ICM42605 }, + { "icm42686", INV_CHIP_ICM42686 }, + { "icm42622", INV_CHIP_ICM42622 }, + { "icm42688", INV_CHIP_ICM42688 }, + { "icm42631", INV_CHIP_ICM42631 }, + { } +}; +MODULE_DEVICE_TABLE(spi, inv_icm42600_id); + static const struct of_device_id inv_icm42600_of_matches[] = { { .compatible = "invensense,icm42600", @@ -100,6 +116,7 @@ static struct spi_driver inv_icm42600_driver = { .of_match_table = inv_icm42600_of_matches, .pm = pm_ptr(&inv_icm42600_pm_ops), }, + .id_table = inv_icm42600_id, .probe = inv_icm42600_probe, }; module_spi_driver(inv_icm42600_driver); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index f7bce428d9eb4..373e59f6d91a5 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -10,6 +10,8 @@ #include #include #include +#include + #include "inv_mpu_iio.h" enum inv_mpu_product_name { @@ -102,14 +104,11 @@ static int inv_mpu_process_acpi_config(struct i2c_client *client, unsigned short *secondary_addr) { struct acpi_device *adev = ACPI_COMPANION(&client->dev); - const struct acpi_device_id *id; u32 i2c_addr = 0; LIST_HEAD(resources); int ret; - id = acpi_match_device(client->dev.driver->acpi_match_table, - &client->dev); - if (!id) + if (!is_acpi_device_node(dev_fwnode(&client->dev))) return -ENODEV; ret = acpi_dev_get_resources(adev, &resources, @@ -118,8 +117,8 @@ static int inv_mpu_process_acpi_config(struct i2c_client *client, return ret; acpi_dev_free_resource_list(&resources); - *primary_addr = i2c_addr & 0x0000ffff; - *secondary_addr = (i2c_addr & 0xffff0000) >> 16; + *primary_addr = lower_16_bits(i2c_addr); + *secondary_addr = upper_16_bits(i2c_addr); return 0; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 14d95f34e981c..40271352b02cf 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -286,6 +286,24 @@ static const struct inv_mpu6050_hw hw_info[] = { .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME}, }, + { + .whoami = INV_IAM20680HP_WHOAMI_VALUE, + .name = "IAM20680HP", + .reg = ®_set_6500, + .config = &chip_config_6500, + .fifo_size = 4 * 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME}, + }, + { + .whoami = INV_IAM20680HT_WHOAMI_VALUE, + .name = "IAM20680HT", + .reg = ®_set_6500, + .config = &chip_config_6500, + .fifo_size = 4 * 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME}, + }, }; static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep, @@ -510,6 +528,8 @@ static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st, return 0; case INV_ICM20689: case INV_ICM20690: + case INV_IAM20680HT: + case INV_IAM20680HP: /* set FIFO size to maximum value */ val |= INV_ICM20689_BITS_FIFO_SIZE_MAX; break; @@ -1153,24 +1173,21 @@ static int inv_mpu6050_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct inv_mpu6050_state *st = iio_priv(indio_dev); - int enable; /* support only WoM (accel roc rising) event */ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING) return -EINVAL; - enable = !!state; - guard(mutex)(&st->lock); - if (st->chip_config.wom_en == enable) + if (st->chip_config.wom_en == state) return 0; - return inv_mpu6050_enable_wom(st, enable); + return inv_mpu6050_enable_wom(st, state); } static int inv_mpu6050_read_event_value(struct iio_dev *indio_dev, @@ -1859,7 +1876,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, struct inv_mpu6050_platform_data *pdata; struct device *dev = regmap_get_device(regmap); int result; - struct irq_data *desc; int irq_type; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -1893,13 +1909,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, } if (irq > 0) { - desc = irq_get_irq_data(irq); - if (!desc) { - dev_err(dev, "Could not find IRQ %d\n", irq); - return -EINVAL; - } - - irq_type = irqd_get_trigger_type(desc); + irq_type = irq_get_trigger_type(irq); if (!irq_type) irq_type = IRQF_TRIGGER_RISING; } else { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 0e03137fb3d40..7a5926ba6b97d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -188,6 +188,8 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20602", INV_ICM20602}, {"icm20690", INV_ICM20690}, {"iam20680", INV_IAM20680}, + {"iam20680hp", INV_IAM20680HP}, + {"iam20680ht", INV_IAM20680HT}, {} }; @@ -254,6 +256,14 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 }, + { + .compatible = "invensense,iam20680hp", + .data = (void *)INV_IAM20680HP + }, + { + .compatible = "invensense,iam20680ht", + .data = (void *)INV_IAM20680HT + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index e1c0c51468761..a6862cf426396 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -85,6 +85,8 @@ enum inv_devices { INV_ICM20602, INV_ICM20690, INV_IAM20680, + INV_IAM20680HP, + INV_IAM20680HT, INV_NUM_PARTS }; @@ -424,6 +426,8 @@ struct inv_mpu6050_state { #define INV_ICM20602_WHOAMI_VALUE 0x12 #define INV_ICM20690_WHOAMI_VALUE 0x20 #define INV_IAM20680_WHOAMI_VALUE 0xA9 +#define INV_IAM20680HP_WHOAMI_VALUE 0xF8 +#define INV_IAM20680HT_WHOAMI_VALUE 0xFA /* scan element definition for generic MPU6xxx devices */ enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 05451ca1580b6..e6a291fcda958 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -80,6 +80,8 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20602", INV_ICM20602}, {"icm20690", INV_ICM20690}, {"iam20680", INV_IAM20680}, + {"iam20680hp", INV_IAM20680HP}, + {"iam20680ht", INV_IAM20680HT}, {} }; @@ -142,6 +144,14 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 }, + { + .compatible = "invensense,iam20680hp", + .data = (void *)INV_IAM20680HP + }, + { + .compatible = "invensense,iam20680ht", + .data = (void *)INV_IAM20680HT + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index 3bfeabab0ec4f..5b1088cc3704f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -112,7 +112,6 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) if (enable) { /* reset timestamping */ inv_sensors_timestamp_reset(&st->timestamp); - inv_sensors_timestamp_apply_odr(&st->timestamp, 0, 0, 0); /* reset FIFO */ d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; ret = regmap_write(st->map, st->reg->user_ctrl, d); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index c61c012e25bba..324c38764656a 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -7,12 +7,13 @@ * IIO driver for KMX61 (7-bit I2C slave address 0x0E or 0x0F). */ -#include #include -#include #include +#include +#include #include #include + #include #include #include @@ -941,7 +942,7 @@ static int kmx61_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct kmx61_data *data = kmx61_get_data(indio_dev); int ret = 0; @@ -1217,16 +1218,6 @@ static irqreturn_t kmx61_trigger_handler(int irq, void *p) return IRQ_HANDLED; } -static const char *kmx61_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - return dev_name(dev); -} - static struct iio_dev *kmx61_indiodev_setup(struct kmx61_data *data, const struct iio_info *info, const struct iio_chan_spec *chan, @@ -1293,8 +1284,6 @@ static int kmx61_probe(struct i2c_client *client) if (id) name = id->name; - else if (ACPI_HANDLE(&client->dev)) - name = kmx61_match_acpi_device(&client->dev); else return -ENODEV; @@ -1496,13 +1485,6 @@ static const struct dev_pm_ops kmx61_pm_ops = { RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL) }; -static const struct acpi_device_id kmx61_acpi_match[] = { - {"KMX61021", 0}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, kmx61_acpi_match); - static const struct i2c_device_id kmx61_id[] = { { "kmx611021" }, {} @@ -1513,7 +1495,6 @@ MODULE_DEVICE_TABLE(i2c, kmx61_id); static struct i2c_driver kmx61_driver = { .driver = { .name = KMX61_DRV_NAME, - .acpi_match_table = kmx61_acpi_match, .pm = pm_ptr(&kmx61_pm_ops), }, .probe = kmx61_probe, diff --git a/drivers/iio/imu/smi240.c b/drivers/iio/imu/smi240.c new file mode 100644 index 0000000000000..4492c4d013bd6 --- /dev/null +++ b/drivers/iio/imu/smi240.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (c) 2024 Robert Bosch GmbH. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SMI240_CHIP_ID 0x0024 + +#define SMI240_SOFT_CONFIG_EOC_MASK BIT(0) +#define SMI240_SOFT_CONFIG_GYR_BW_MASK BIT(1) +#define SMI240_SOFT_CONFIG_ACC_BW_MASK BIT(2) +#define SMI240_SOFT_CONFIG_BITE_AUTO_MASK BIT(3) +#define SMI240_SOFT_CONFIG_BITE_REP_MASK GENMASK(6, 4) + +#define SMI240_CHIP_ID_REG 0x00 +#define SMI240_SOFT_CONFIG_REG 0x0A +#define SMI240_TEMP_CUR_REG 0x10 +#define SMI240_ACCEL_X_CUR_REG 0x11 +#define SMI240_GYRO_X_CUR_REG 0x14 +#define SMI240_DATA_CAP_FIRST_REG 0x17 +#define SMI240_CMD_REG 0x2F + +#define SMI240_SOFT_RESET_CMD 0xB6 + +#define SMI240_BITE_SEQUENCE_DELAY_US 140000 +#define SMI240_FILTER_FLUSH_DELAY_US 60000 +#define SMI240_DIGITAL_STARTUP_DELAY_US 120000 +#define SMI240_MECH_STARTUP_DELAY_US 100000 + +#define SMI240_BUS_ID 0x00 +#define SMI240_CRC_INIT 0x05 +#define SMI240_CRC_POLY 0x0B +#define SMI240_CRC_MASK GENMASK(2, 0) + +#define SMI240_READ_SD_BIT_MASK BIT(31) +#define SMI240_READ_DATA_MASK GENMASK(19, 4) +#define SMI240_READ_CS_BIT_MASK BIT(3) + +#define SMI240_WRITE_BUS_ID_MASK GENMASK(31, 30) +#define SMI240_WRITE_ADDR_MASK GENMASK(29, 22) +#define SMI240_WRITE_BIT_MASK BIT(21) +#define SMI240_WRITE_CAP_BIT_MASK BIT(20) +#define SMI240_WRITE_DATA_MASK GENMASK(18, 3) + +/* T°C = (temp / 256) + 25 */ +#define SMI240_TEMP_OFFSET 6400 /* 25 * 256 */ +#define SMI240_TEMP_SCALE 3906250 /* (1 / 256) * 1e9 */ + +#define SMI240_ACCEL_SCALE 500 /* (1 / 2000) * 1e6 */ +#define SMI240_GYRO_SCALE 10000 /* (1 / 100) * 1e6 */ + +#define SMI240_LOW_BANDWIDTH_HZ 50 +#define SMI240_HIGH_BANDWIDTH_HZ 400 + +#define SMI240_BUILT_IN_SELF_TEST_COUNT 3 + +#define SMI240_DATA_CHANNEL(_type, _axis, _index) { \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define SMI240_TEMP_CHANNEL(_index) { \ + .type = IIO_TEMP, \ + .modified = 1, \ + .channel2 = IIO_MOD_TEMP_OBJECT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +enum capture_mode { SMI240_CAPTURE_OFF = 0, SMI240_CAPTURE_ON = 1 }; + +struct smi240_data { + struct regmap *regmap; + u16 accel_filter_freq; + u16 anglvel_filter_freq; + u8 built_in_self_test_count; + enum capture_mode capture; + /* + * Ensure natural alignment for timestamp if present. + * Channel size: 2 bytes. + * Max length needed: 2 * 3 channels + temp channel + 2 bytes padding + 8 byte ts. + * If fewer channels are enabled, less space may be needed, as + * long as the timestamp is still aligned to 8 bytes. + */ + s16 buf[12] __aligned(8); + + __be32 spi_buf __aligned(IIO_DMA_MINALIGN); +}; + +enum { + SMI240_TEMP_OBJECT, + SMI240_SCAN_ACCEL_X, + SMI240_SCAN_ACCEL_Y, + SMI240_SCAN_ACCEL_Z, + SMI240_SCAN_GYRO_X, + SMI240_SCAN_GYRO_Y, + SMI240_SCAN_GYRO_Z, + SMI240_SCAN_TIMESTAMP, +}; + +static const struct iio_chan_spec smi240_channels[] = { + SMI240_TEMP_CHANNEL(SMI240_TEMP_OBJECT), + SMI240_DATA_CHANNEL(IIO_ACCEL, X, SMI240_SCAN_ACCEL_X), + SMI240_DATA_CHANNEL(IIO_ACCEL, Y, SMI240_SCAN_ACCEL_Y), + SMI240_DATA_CHANNEL(IIO_ACCEL, Z, SMI240_SCAN_ACCEL_Z), + SMI240_DATA_CHANNEL(IIO_ANGL_VEL, X, SMI240_SCAN_GYRO_X), + SMI240_DATA_CHANNEL(IIO_ANGL_VEL, Y, SMI240_SCAN_GYRO_Y), + SMI240_DATA_CHANNEL(IIO_ANGL_VEL, Z, SMI240_SCAN_GYRO_Z), + IIO_CHAN_SOFT_TIMESTAMP(SMI240_SCAN_TIMESTAMP), +}; + +static const int smi240_low_pass_freqs[] = { SMI240_LOW_BANDWIDTH_HZ, + SMI240_HIGH_BANDWIDTH_HZ }; + +static u8 smi240_crc3(u32 data, u8 init, u8 poly) +{ + u8 crc = init; + u8 do_xor; + s8 i = 31; + + do { + do_xor = crc & 0x04; + crc <<= 1; + crc |= 0x01 & (data >> i); + if (do_xor) + crc ^= poly; + + crc &= SMI240_CRC_MASK; + } while (--i >= 0); + + return crc; +} + +static bool smi240_sensor_data_is_valid(u32 data) +{ + if (smi240_crc3(data, SMI240_CRC_INIT, SMI240_CRC_POLY) != 0) + return false; + + if (FIELD_GET(SMI240_READ_SD_BIT_MASK, data) & + FIELD_GET(SMI240_READ_CS_BIT_MASK, data)) + return false; + + return true; +} + +static int smi240_regmap_spi_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + int ret; + u32 request, response; + u16 *val = val_buf; + struct spi_device *spi = context; + struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct smi240_data *iio_priv_data = iio_priv(indio_dev); + + if (reg_size != 1 || val_size != 2) + return -EINVAL; + + request = FIELD_PREP(SMI240_WRITE_BUS_ID_MASK, SMI240_BUS_ID); + request |= FIELD_PREP(SMI240_WRITE_CAP_BIT_MASK, iio_priv_data->capture); + request |= FIELD_PREP(SMI240_WRITE_ADDR_MASK, *(u8 *)reg_buf); + request |= smi240_crc3(request, SMI240_CRC_INIT, SMI240_CRC_POLY); + + iio_priv_data->spi_buf = cpu_to_be32(request); + + /* + * SMI240 module consists of a 32Bit Out Of Frame (OOF) + * SPI protocol, where the slave interface responds to + * the Master request in the next frame. + * CS signal must toggle (> 700 ns) between the frames. + */ + ret = spi_write(spi, &iio_priv_data->spi_buf, sizeof(request)); + if (ret) + return ret; + + ret = spi_read(spi, &iio_priv_data->spi_buf, sizeof(response)); + if (ret) + return ret; + + response = be32_to_cpu(iio_priv_data->spi_buf); + + if (!smi240_sensor_data_is_valid(response)) + return -EIO; + + *val = FIELD_GET(SMI240_READ_DATA_MASK, response); + + return 0; +} + +static int smi240_regmap_spi_write(void *context, const void *data, + size_t count) +{ + u8 reg_addr; + u16 reg_data; + u32 request; + const u8 *data_ptr = data; + struct spi_device *spi = context; + struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct smi240_data *iio_priv_data = iio_priv(indio_dev); + + if (count < 2) + return -EINVAL; + + reg_addr = data_ptr[0]; + memcpy(®_data, &data_ptr[1], sizeof(reg_data)); + + request = FIELD_PREP(SMI240_WRITE_BUS_ID_MASK, SMI240_BUS_ID); + request |= FIELD_PREP(SMI240_WRITE_BIT_MASK, 1); + request |= FIELD_PREP(SMI240_WRITE_ADDR_MASK, reg_addr); + request |= FIELD_PREP(SMI240_WRITE_DATA_MASK, reg_data); + request |= smi240_crc3(request, SMI240_CRC_INIT, SMI240_CRC_POLY); + + iio_priv_data->spi_buf = cpu_to_be32(request); + + return spi_write(spi, &iio_priv_data->spi_buf, sizeof(request)); +} + +static const struct regmap_bus smi240_regmap_bus = { + .read = smi240_regmap_spi_read, + .write = smi240_regmap_spi_write, +}; + +static const struct regmap_config smi240_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static int smi240_soft_reset(struct smi240_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, SMI240_CMD_REG, SMI240_SOFT_RESET_CMD); + if (ret) + return ret; + fsleep(SMI240_DIGITAL_STARTUP_DELAY_US); + + return 0; +} + +static int smi240_soft_config(struct smi240_data *data) +{ + int ret; + u8 acc_bw, gyr_bw; + u16 request; + + switch (data->accel_filter_freq) { + case SMI240_LOW_BANDWIDTH_HZ: + acc_bw = 0x1; + break; + case SMI240_HIGH_BANDWIDTH_HZ: + acc_bw = 0x0; + break; + default: + return -EINVAL; + } + + switch (data->anglvel_filter_freq) { + case SMI240_LOW_BANDWIDTH_HZ: + gyr_bw = 0x1; + break; + case SMI240_HIGH_BANDWIDTH_HZ: + gyr_bw = 0x0; + break; + default: + return -EINVAL; + } + + request = FIELD_PREP(SMI240_SOFT_CONFIG_EOC_MASK, 1); + request |= FIELD_PREP(SMI240_SOFT_CONFIG_GYR_BW_MASK, gyr_bw); + request |= FIELD_PREP(SMI240_SOFT_CONFIG_ACC_BW_MASK, acc_bw); + request |= FIELD_PREP(SMI240_SOFT_CONFIG_BITE_AUTO_MASK, 1); + request |= FIELD_PREP(SMI240_SOFT_CONFIG_BITE_REP_MASK, + data->built_in_self_test_count - 1); + + ret = regmap_write(data->regmap, SMI240_SOFT_CONFIG_REG, request); + if (ret) + return ret; + + fsleep(SMI240_MECH_STARTUP_DELAY_US + + data->built_in_self_test_count * SMI240_BITE_SEQUENCE_DELAY_US + + SMI240_FILTER_FLUSH_DELAY_US); + + return 0; +} + +static int smi240_get_low_pass_filter_freq(struct smi240_data *data, + int chan_type, int *val) +{ + switch (chan_type) { + case IIO_ACCEL: + *val = data->accel_filter_freq; + return 0; + case IIO_ANGL_VEL: + *val = data->anglvel_filter_freq; + return 0; + default: + return -EINVAL; + } +} + +static int smi240_get_data(struct smi240_data *data, int chan_type, int axis, + int *val) +{ + u8 reg; + int ret, sample; + + switch (chan_type) { + case IIO_TEMP: + reg = SMI240_TEMP_CUR_REG; + break; + case IIO_ACCEL: + reg = SMI240_ACCEL_X_CUR_REG + (axis - IIO_MOD_X); + break; + case IIO_ANGL_VEL: + reg = SMI240_GYRO_X_CUR_REG + (axis - IIO_MOD_X); + break; + default: + return -EINVAL; + } + + ret = regmap_read(data->regmap, reg, &sample); + if (ret) + return ret; + + *val = sign_extend32(sample, 15); + + return 0; +} + +static irqreturn_t smi240_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct smi240_data *data = iio_priv(indio_dev); + int base = SMI240_DATA_CAP_FIRST_REG, i = 0; + int ret, chan, sample; + + data->capture = SMI240_CAPTURE_ON; + + iio_for_each_active_channel(indio_dev, chan) { + ret = regmap_read(data->regmap, base + chan, &sample); + data->capture = SMI240_CAPTURE_OFF; + if (ret) + goto out; + data->buf[i++] = sample; + } + + iio_push_to_buffers_with_timestamp(indio_dev, data->buf, pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int smi240_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = smi240_low_pass_freqs; + *length = ARRAY_SIZE(smi240_low_pass_freqs); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int smi240_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct smi240_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = smi240_get_data(data, chan->type, chan->channel2, val); + iio_device_release_direct_mode(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = smi240_get_low_pass_filter_freq(data, chan->type, val); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = SMI240_TEMP_SCALE / GIGA; + *val2 = SMI240_TEMP_SCALE % GIGA; + return IIO_VAL_INT_PLUS_NANO; + case IIO_ACCEL: + *val = 0; + *val2 = SMI240_ACCEL_SCALE; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ANGL_VEL: + *val = 0; + *val2 = SMI240_GYRO_SCALE; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + *val = SMI240_TEMP_OFFSET; + return IIO_VAL_INT; + } else { + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static int smi240_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + int ret, i; + struct smi240_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + for (i = 0; i < ARRAY_SIZE(smi240_low_pass_freqs); i++) { + if (val == smi240_low_pass_freqs[i]) + break; + } + + if (i == ARRAY_SIZE(smi240_low_pass_freqs)) + return -EINVAL; + + switch (chan->type) { + case IIO_ACCEL: + data->accel_filter_freq = val; + break; + case IIO_ANGL_VEL: + data->anglvel_filter_freq = val; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + /* Write access to soft config is locked until hard/soft reset */ + ret = smi240_soft_reset(data); + if (ret) + return ret; + + return smi240_soft_config(data); +} + +static int smi240_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int smi240_init(struct smi240_data *data) +{ + int ret; + + data->accel_filter_freq = SMI240_HIGH_BANDWIDTH_HZ; + data->anglvel_filter_freq = SMI240_HIGH_BANDWIDTH_HZ; + data->built_in_self_test_count = SMI240_BUILT_IN_SELF_TEST_COUNT; + + ret = smi240_soft_reset(data); + if (ret) + return ret; + + return smi240_soft_config(data); +} + +static const struct iio_info smi240_info = { + .read_avail = smi240_read_avail, + .read_raw = smi240_read_raw, + .write_raw = smi240_write_raw, + .write_raw_get_fmt = smi240_write_raw_get_fmt, +}; + +static int smi240_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct regmap *regmap; + struct smi240_data *data; + int ret, response; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init(dev, &smi240_regmap_bus, dev, + &smi240_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize SPI Regmap\n"); + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + data->regmap = regmap; + data->capture = SMI240_CAPTURE_OFF; + + ret = regmap_read(data->regmap, SMI240_CHIP_ID_REG, &response); + if (ret) + return dev_err_probe(dev, ret, "Read chip id failed\n"); + + if (response != SMI240_CHIP_ID) + dev_info(dev, "Unknown chip id: 0x%04x\n", response); + + ret = smi240_init(data); + if (ret) + return dev_err_probe(dev, ret, + "Device initialization failed\n"); + + indio_dev->channels = smi240_channels; + indio_dev->num_channels = ARRAY_SIZE(smi240_channels); + indio_dev->name = "smi240"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &smi240_info; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + smi240_trigger_handler, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Setup triggered buffer failed\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Register IIO device failed\n"); + + return 0; +} + +static const struct spi_device_id smi240_spi_id[] = { + { "smi240" }, + { } +}; +MODULE_DEVICE_TABLE(spi, smi240_spi_id); + +static const struct of_device_id smi240_of_match[] = { + { .compatible = "bosch,smi240" }, + { } +}; +MODULE_DEVICE_TABLE(of, smi240_of_match); + +static struct spi_driver smi240_spi_driver = { + .probe = smi240_probe, + .id_table = smi240_spi_id, + .driver = { + .of_match_table = smi240_of_match, + .name = "smi240", + }, +}; +module_spi_driver(smi240_spi_driver); + +MODULE_AUTHOR("Markus Lochmann "); +MODULE_AUTHOR("Stefan Gutmann "); +MODULE_DESCRIPTION("Bosch SMI240 SPI driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index a3b93566533bc..c225b246c8a55 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -447,7 +447,7 @@ struct st_lsm6dsx_hw { /* Ensure natural alignment of buffer elements */ struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan[ST_LSM6DSX_ID_MAX]; }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index ed02679297252..509e0169dcd54 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1865,7 +1865,7 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, return err; } -static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, int state) +static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, bool state) { const struct st_lsm6dsx_reg *reg; unsigned int data; @@ -1959,7 +1959,7 @@ static int st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; @@ -2132,14 +2132,11 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, const struct st_lsm6dsx_reg **drdy_reg) { struct device *dev = hw->dev; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); int err = 0, drdy_pin; - if (device_property_read_u32(dev, "st,drdy-int-pin", &drdy_pin) < 0) { - struct st_sensors_platform_data *pdata; - - pdata = (struct st_sensors_platform_data *)dev->platform_data; + if (device_property_read_u32(dev, "st,drdy-int-pin", &drdy_pin) < 0) drdy_pin = pdata ? pdata->drdy_int_pin : 1; - } switch (drdy_pin) { case 1: @@ -2162,14 +2159,13 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) { const struct st_lsm6dsx_shub_settings *hub_settings; - struct st_sensors_platform_data *pdata; struct device *dev = hw->dev; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); unsigned int data; int err = 0; hub_settings = &hw->settings->shub_settings; - pdata = (struct st_sensors_platform_data *)dev->platform_data; if (device_property_read_bool(dev, "st,pullups") || (pdata && pdata->pullups)) { if (hub_settings->pullup_en.sec_page) { @@ -2524,15 +2520,14 @@ static irqreturn_t st_lsm6dsx_sw_trigger_handler_thread(int irq, static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) { - struct st_sensors_platform_data *pdata; const struct st_lsm6dsx_reg *reg; struct device *dev = hw->dev; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); unsigned long irq_type; bool irq_active_low; int err; - irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); - + irq_type = irq_get_trigger_type(hw->irq); switch (irq_type) { case IRQF_TRIGGER_HIGH: case IRQF_TRIGGER_RISING: @@ -2554,7 +2549,6 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) if (err < 0) return err; - pdata = (struct st_sensors_platform_data *)dev->platform_data; if (device_property_read_bool(dev, "drive-open-drain") || (pdata && pdata->open_drain)) { reg = &hw->settings->irq_config.od; @@ -2639,7 +2633,7 @@ static int st_lsm6dsx_init_regulators(struct device *dev) int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { - struct st_sensors_platform_data *pdata = dev->platform_data; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); const struct st_lsm6dsx_shub_settings *hub_settings; struct st_lsm6dsx_hw *hw; const char *name = NULL; diff --git a/drivers/iio/industrialio-acpi.c b/drivers/iio/industrialio-acpi.c index 981b75d407802..d67a438437998 100644 --- a/drivers/iio/industrialio-acpi.c +++ b/drivers/iio/industrialio-acpi.c @@ -2,7 +2,8 @@ /* IIO ACPI helper functions */ #include -#include +#include +#include #include #include @@ -28,17 +29,21 @@ bool iio_read_acpi_mount_matrix(struct device *dev, char *acpi_method) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_device *adev = ACPI_COMPANION(dev); char *str; union acpi_object *obj, *elements; + acpi_handle handle; acpi_status status; int i, j, val[3]; bool ret = false; - if (!adev || !acpi_has_method(adev->handle, acpi_method)) + handle = ACPI_HANDLE(dev); + if (!handle) return false; - status = acpi_evaluate_object(adev->handle, acpi_method, NULL, &buffer); + if (!acpi_has_method(handle, acpi_method)) + return false; + + status = acpi_evaluate_object(handle, acpi_method, NULL, &buffer); if (ACPI_FAILURE(status)) { dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status); return false; @@ -83,3 +88,38 @@ bool iio_read_acpi_mount_matrix(struct device *dev, return ret; } EXPORT_SYMBOL_GPL(iio_read_acpi_mount_matrix); + +/** + * iio_get_acpi_device_name_and_data() - Return ACPI device instance name and driver data + * @dev: Device structure + * @data: Optional pointer to return driver data + * + * When device was enumerated by ACPI ID matching, the user might + * want to set description for the physical chip. In such cases + * the ACPI device instance name might be used. This call may be + * performed to retrieve this information. + * + * NOTE: This helper function exists only for backward compatibility, + * do not use in a new code! + * + * Returns: ACPI device instance name or %NULL. + */ +const char *iio_get_acpi_device_name_and_data(struct device *dev, const void **data) +{ + const struct acpi_device_id *id; + acpi_handle handle; + + handle = ACPI_HANDLE(dev); + if (!handle) + return NULL; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return NULL; + + if (data) + *data = (const void *)id->driver_data; + + return dev_name(dev); +} +EXPORT_SYMBOL_GPL(iio_get_acpi_device_name_and_data); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 20b3b5212da76..529b1087d3fb4 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -718,6 +718,84 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) return 0; } +/** + * iio_backend_ddr_enable - Enable interface DDR (Double Data Rate) mode + * @back: Backend device + * + * Enable DDR, data is generated by the IP at each front (raising and falling) + * of the bus clock signal. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_ddr_enable(struct iio_backend *back) +{ + return iio_backend_op_call(back, ddr_enable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_ddr_enable, IIO_BACKEND); + +/** + * iio_backend_ddr_disable - Disable interface DDR (Double Data Rate) mode + * @back: Backend device + * + * Disable DDR, setting into SDR mode (Single Data Rate). + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_ddr_disable(struct iio_backend *back) +{ + return iio_backend_op_call(back, ddr_disable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_ddr_disable, IIO_BACKEND); + +/** + * iio_backend_data_stream_enable - Enable data stream + * @back: Backend device + * + * Enable data stream over the bus interface. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_stream_enable(struct iio_backend *back) +{ + return iio_backend_op_call(back, data_stream_enable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_stream_enable, IIO_BACKEND); + +/** + * iio_backend_data_stream_disable - Disable data stream + * @back: Backend device + * + * Disable data stream over the bus interface. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_stream_disable(struct iio_backend *back) +{ + return iio_backend_op_call(back, data_stream_disable); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_stream_disable, IIO_BACKEND); + +/** + * iio_backend_data_transfer_addr - Set data address. + * @back: Backend device + * @address: Data register address + * + * Some devices may need to inform the backend about an address + * where to read or write the data. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_transfer_addr(struct iio_backend *back, u32 address) +{ + return iio_backend_op_call(back, data_transfer_addr, address); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_transfer_addr, IIO_BACKEND); + static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, struct fwnode_handle *fwnode) { @@ -737,8 +815,8 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con } fwnode_back = fwnode_find_reference(fwnode, "io-backends", index); - if (IS_ERR(fwnode)) - return dev_err_cast_probe(dev, fwnode, + if (IS_ERR(fwnode_back)) + return dev_err_cast_probe(dev, fwnode_back, "Cannot get Firmware reference\n"); guard(mutex)(&iio_back_lock); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 6a6568d4a2cb3..a2117ad1337d5 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -95,6 +95,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_DELTA_VELOCITY] = "deltavelocity", [IIO_COLORTEMP] = "colortemp", [IIO_CHROMATICITY] = "chromaticity", + [IIO_ATTENTION] = "attention", }; static const char * const iio_modifier_names[] = { @@ -1665,7 +1666,7 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) indio_dev = &iio_dev_opaque->indio_dev; if (sizeof_priv) - indio_dev->priv = (char *)iio_dev_opaque + + ACCESS_PRIVATE(indio_dev, priv) = (char *)iio_dev_opaque + ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN); indio_dev->dev.parent = parent; diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index 5f131bc1a01e9..291c0fc332c97 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -167,7 +167,7 @@ static int iio_gts_gain_cmp(const void *a, const void *b) static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) { - int ret, i, j, new_idx, time_idx; + int i, j, new_idx, time_idx, ret = 0; int *all_gains; size_t gain_bytes; @@ -205,7 +205,7 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) memcpy(all_gains, gains[time_idx], gain_bytes); new_idx = gts->num_hwgain; - while (time_idx--) { + while (time_idx-- > 0) { for (j = 0; j < gts->num_hwgain; j++) { int candidate = gains[time_idx][j]; int chk; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 151099be2863c..136b225b6bc8a 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -20,7 +20,7 @@ struct iio_map_internal { struct iio_dev *indio_dev; - struct iio_map *map; + const struct iio_map *map; struct list_head l; }; @@ -42,7 +42,7 @@ static int iio_map_array_unregister_locked(struct iio_dev *indio_dev) return ret; } -int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps) +int iio_map_array_register(struct iio_dev *indio_dev, const struct iio_map *maps) { struct iio_map_internal *mapi; int i = 0; @@ -86,7 +86,8 @@ static void iio_map_array_unregister_cb(void *indio_dev) iio_map_array_unregister(indio_dev); } -int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps) +int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, + const struct iio_map *maps) { int ret; @@ -269,7 +270,7 @@ struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, return ERR_PTR(-ENODEV); } - chan = __fwnode_iio_channel_get_by_name(fwnode, name); + chan = __fwnode_iio_channel_get_by_name(parent, name); if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) { fwnode_handle_put(parent); return chan; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index f2f3e414849ab..29ffa84919273 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -248,7 +248,6 @@ config SENSORS_ISL29018 tristate "Intersil 29018 light and proximity sensor" depends on I2C select REGMAP_I2C - default n help If you say yes here you get support for ambient light sensing and proximity infrared sensing from Intersil ISL29018. @@ -476,7 +475,7 @@ config OPT3001 depends on I2C help If you say Y or M here, you get support for Texas Instruments - OPT3001 Ambient Light Sensor. + OPT3001 Ambient Light Sensor, OPT3002 Light-to-Digital Sensor. If built as a dynamically linked module, it will be called opt3001. @@ -670,13 +669,24 @@ config VCNL4035 To compile this driver as a module, choose M here: the module will be called vcnl4035. +config VEML3235 + tristate "VEML3235 ambient light sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Vishay VEML3235 + ambient light sensor. + + To compile this driver as a module, choose M here: the + module will be called veml3235. + config VEML6030 - tristate "VEML6030 ambient light sensor" + tristate "VEML6030 and VEML6035 ambient light sensors" select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Vishay VEML6030 - ambient light sensor (ALS). + and VEML6035 ambient light sensors (ALS). To compile this driver as a module, choose M here: the module will be called veml6030. diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 321010fc0b938..f14a374427124 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_VCNL4035) += vcnl4035.o +obj-$(CONFIG_VEML3235) += veml3235.o obj-$(CONFIG_VEML6030) += veml6030.o obj-$(CONFIG_VEML6040) += veml6040.o obj-$(CONFIG_VEML6070) += veml6070.o diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 2e0170be077ae..593d614b1689b 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -502,7 +502,8 @@ static int adux1020_write_raw(struct iio_dev *indio_dev, static int adux1020_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { struct adux1020_data *data = iio_priv(indio_dev); int ret, mask; @@ -526,12 +527,11 @@ static int adux1020_write_event_config(struct iio_dev *indio_dev, mask = ADUX1020_PROX_OFF1_INT; if (state) - state = 0; + ret = regmap_clear_bits(data->regmap, + ADUX1020_REG_INT_MASK, mask); else - state = mask; - - ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, - mask, state); + ret = regmap_set_bits(data->regmap, + ADUX1020_REG_INT_MASK, mask); if (ret < 0) goto fail; diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 53569587ccb7b..7cbb8b2033009 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -87,7 +87,12 @@ static int al3010_init(struct al3010_data *data) int ret; ret = al3010_set_pwr(data->client, true); + if (ret < 0) + return ret; + ret = devm_add_action_or_reset(&data->client->dev, + al3010_set_pwr_off, + data); if (ret < 0) return ret; @@ -190,12 +195,6 @@ static int al3010_probe(struct i2c_client *client) return ret; } - ret = devm_add_action_or_reset(&client->dev, - al3010_set_pwr_off, - data); - if (ret < 0) - return ret; - return devm_iio_device_register(&client->dev, indio_dev); } diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index 11f2ab4ca2618..938d76f7e3129 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -46,10 +46,10 @@ struct apds9300_data { struct i2c_client *client; struct mutex mutex; - int power_state; + bool power_state; int thresh_low; int thresh_hi; - int intr_en; + bool intr_en; }; /* Lux calculation */ @@ -148,7 +148,7 @@ static int apds9300_set_thresh_hi(struct apds9300_data *data, int value) return 0; } -static int apds9300_set_intr_state(struct apds9300_data *data, int state) +static int apds9300_set_intr_state(struct apds9300_data *data, bool state) { int ret; u8 cmd; @@ -169,7 +169,7 @@ static int apds9300_set_intr_state(struct apds9300_data *data, int state) return 0; } -static int apds9300_set_power_state(struct apds9300_data *data, int state) +static int apds9300_set_power_state(struct apds9300_data *data, bool state) { int ret; u8 cmd; @@ -221,7 +221,7 @@ static int apds9300_chip_init(struct apds9300_data *data) * Disable interrupt to ensure thai it is doesn't enable * i.e. after device soft reset */ - ret = apds9300_set_intr_state(data, 0); + ret = apds9300_set_intr_state(data, false); if (ret < 0) goto err; @@ -321,7 +321,7 @@ static int apds9300_read_interrupt_config(struct iio_dev *indio_dev, static int apds9300_write_interrupt_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct apds9300_data *data = iio_priv(indio_dev); int ret; @@ -459,8 +459,8 @@ static void apds9300_remove(struct i2c_client *client) iio_device_unregister(indio_dev); /* Ensure that power off and interrupts are disabled */ - apds9300_set_intr_state(data, 0); - apds9300_set_power_state(data, 0); + apds9300_set_intr_state(data, false); + apds9300_set_power_state(data, false); } static int apds9300_suspend(struct device *dev) @@ -470,7 +470,7 @@ static int apds9300_suspend(struct device *dev) int ret; mutex_lock(&data->mutex); - ret = apds9300_set_power_state(data, 0); + ret = apds9300_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; @@ -483,7 +483,7 @@ static int apds9300_resume(struct device *dev) int ret; mutex_lock(&data->mutex); - ret = apds9300_set_power_state(data, 1); + ret = apds9300_set_power_state(data, true); mutex_unlock(&data->mutex); return ret; diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 079e02be10052..9c08e7c3ad0c1 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -1071,7 +1071,7 @@ static int apds9306_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct apds9306_data *data = iio_priv(indio_dev); struct apds9306_regfields *rf = &data->rf; @@ -1125,10 +1125,7 @@ static int apds9306_write_event_config(struct iio_dev *indio_dev, } } case IIO_EV_TYPE_THRESH_ADAPTIVE: - if (state) - return regmap_field_write(rf->int_thresh_var_en, 1); - else - return regmap_field_write(rf->int_thresh_var_en, 0); + return regmap_field_write(rf->int_thresh_var_en, state); default: return -EINVAL; } diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 3c14e4c30805e..d30441d337030 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -133,8 +133,8 @@ struct apds9960_data { struct regmap_field *reg_enable_pxs; /* state */ - int als_int; - int pxs_int; + bool als_int; + bool pxs_int; int gesture_mode_running; /* gain values */ @@ -749,21 +749,17 @@ static int apds9960_read_event_config(struct iio_dev *indio_dev, default: return -EINVAL; } - - return 0; } static int apds9960_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct apds9960_data *data = iio_priv(indio_dev); int ret; - state = !!state; - switch (chan->type) { case IIO_PROXIMITY: if (data->pxs_int == state) diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 2e458e9d5d853..23e9f16090ccd 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -638,46 +638,42 @@ static int bh1745_read_event_config(struct iio_dev *indio_dev, static int bh1745_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct bh1745_data *data = iio_priv(indio_dev); int value; - if (state == 0) + if (!state) return regmap_clear_bits(data->regmap, BH1745_INTR, BH1745_INTR_ENABLE); - if (state == 1) { - /* Latch is always enabled when enabling interrupt */ - value = BH1745_INTR_ENABLE; + /* Latch is always enabled when enabling interrupt */ + value = BH1745_INTR_ENABLE; - switch (chan->channel2) { - case IIO_MOD_LIGHT_RED: - return regmap_write(data->regmap, BH1745_INTR, - value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, - BH1745_INTR_SOURCE_RED)); + switch (chan->channel2) { + case IIO_MOD_LIGHT_RED: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_RED)); - case IIO_MOD_LIGHT_GREEN: - return regmap_write(data->regmap, BH1745_INTR, - value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, - BH1745_INTR_SOURCE_GREEN)); + case IIO_MOD_LIGHT_GREEN: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_GREEN)); - case IIO_MOD_LIGHT_BLUE: - return regmap_write(data->regmap, BH1745_INTR, - value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, - BH1745_INTR_SOURCE_BLUE)); + case IIO_MOD_LIGHT_BLUE: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_BLUE)); - case IIO_MOD_LIGHT_CLEAR: - return regmap_write(data->regmap, BH1745_INTR, - value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, - BH1745_INTR_SOURCE_CLEAR)); + case IIO_MOD_LIGHT_CLEAR: + return regmap_write(data->regmap, BH1745_INTR, + value | FIELD_PREP(BH1745_INTR_SOURCE_MASK, + BH1745_INTR_SOURCE_CLEAR)); - default: - return -EINVAL; - } + default: + return -EINVAL; } - - return -EINVAL; } static int bh1745_read_avail(struct iio_dev *indio_dev, diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 9df85b3999fa8..aeae0566ec12a 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -217,8 +217,7 @@ static int cm32181_reg_init(struct cm32181_chip *cm32181) cm32181->lux_per_bit = CM32181_LUX_PER_BIT; cm32181->lux_per_bit_base_it = CM32181_LUX_PER_BIT_BASE_IT; - if (ACPI_HANDLE(cm32181->dev)) - cm32181_acpi_parse_cpm_tables(cm32181); + cm32181_acpi_parse_cpm_tables(cm32181); /* Initialize registers*/ for_each_set_bit(i, &cm32181->init_regs_bitmap, CM32181_CONF_REG_NUM) { diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 22a63a89f289a..675c0fd44db45 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -318,7 +318,7 @@ static struct platform_driver cm3605_driver = { .pm = pm_sleep_ptr(&cm3605_dev_pm_ops), }, .probe = cm3605_probe, - .remove_new = cm3605_remove, + .remove = cm3605_remove, }; module_platform_driver(cm3605_driver); diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index a4a1505534c01..ae3fc3299eec6 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -529,7 +529,7 @@ static int cm36651_write_prox_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct cm36651_data *cm36651 = iio_priv(indio_dev); int cmd, ret; diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index f8b1d7dd6f5fc..d56ee217fe538 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -340,7 +340,7 @@ static int gp2ap002_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 81e718cdeae32..1a352c88598e5 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1159,7 +1159,7 @@ static int gp2ap020a00f_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); enum gp2ap020a00f_cmd cmd; diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 260281194f613..4eb6923224320 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -31,7 +31,7 @@ struct als_state { struct iio_chan_spec channels[CHANNEL_SCAN_INDEX_MAX + 1]; struct { u32 illum[CHANNEL_SCAN_INDEX_MAX]; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -356,11 +356,11 @@ static int als_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_als_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret = 0; static const char *name = "als"; struct iio_dev *indio_dev; struct als_state *als_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct als_state)); if (!indio_dev) @@ -438,7 +438,7 @@ static int hid_als_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_als_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct als_state *als_state = iio_priv(indio_dev); @@ -467,7 +467,7 @@ static struct platform_driver hid_als_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_als_probe, - .remove_new = hid_als_remove, + .remove = hid_als_remove, }; module_platform_driver(hid_als_platform_driver); diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 26c481d2998c1..e8e7b2999b4c9 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -13,16 +13,32 @@ #include #include "../common/hid-sensors/hid-sensor-trigger.h" -#define CHANNEL_SCAN_INDEX_PRESENCE 0 +static const u32 prox_usage_ids[] = { + HID_USAGE_SENSOR_HUMAN_PRESENCE, + HID_USAGE_SENSOR_HUMAN_PROXIMITY, + HID_USAGE_SENSOR_HUMAN_ATTENTION, +}; + +#define MAX_CHANNELS ARRAY_SIZE(prox_usage_ids) + +enum { + HID_HUMAN_PRESENCE, + HID_HUMAN_PROXIMITY, + HID_HUMAN_ATTENTION, +}; struct prox_state { struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_common common_attributes; - struct hid_sensor_hub_attribute_info prox_attr; - u32 human_presence; + struct hid_sensor_hub_attribute_info prox_attr[MAX_CHANNELS]; + struct iio_chan_spec channels[MAX_CHANNELS]; + u32 channel2usage[MAX_CHANNELS]; + u32 human_presence[MAX_CHANNELS]; int scale_pre_decml; int scale_post_decml; int scale_precision; + unsigned long scan_mask[2]; /* One entry plus one terminator. */ + int num_channels; }; static const u32 prox_sensitivity_addresses[] = { @@ -30,17 +46,24 @@ static const u32 prox_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_PRESENCE, }; -/* Channel definitions */ -static const struct iio_chan_spec prox_channels[] = { - { - .type = IIO_PROXIMITY, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | - BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_HYSTERESIS), - .scan_index = CHANNEL_SCAN_INDEX_PRESENCE, +#define PROX_CHANNEL(_is_proximity, _channel) \ + {\ + .type = _is_proximity ? IIO_PROXIMITY : IIO_ATTENTION,\ + .info_mask_separate = _is_proximity ? BIT(IIO_CHAN_INFO_RAW) :\ + BIT(IIO_CHAN_INFO_PROCESSED),\ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |\ + BIT(IIO_CHAN_INFO_SCALE) |\ + BIT(IIO_CHAN_INFO_SAMP_FREQ) |\ + BIT(IIO_CHAN_INFO_HYSTERESIS),\ + .indexed = _is_proximity,\ + .channel = _channel,\ } + +/* Channel definitions (same order as prox_usage_ids) */ +static const struct iio_chan_spec prox_channels[] = { + PROX_CHANNEL(true, HID_HUMAN_PRESENCE), + PROX_CHANNEL(true, HID_HUMAN_PROXIMITY), + PROX_CHANNEL(false, 0), }; /* Adjust channel real bits based on report descriptor */ @@ -62,7 +85,7 @@ static int prox_read_raw(struct iio_dev *indio_dev, { struct prox_state *prox_state = iio_priv(indio_dev); struct hid_sensor_hub_device *hsdev; - int report_id = -1; + int report_id; u32 address; int ret_type; s32 min; @@ -71,29 +94,23 @@ static int prox_read_raw(struct iio_dev *indio_dev, *val2 = 0; switch (mask) { case IIO_CHAN_INFO_RAW: - switch (chan->scan_index) { - case CHANNEL_SCAN_INDEX_PRESENCE: - report_id = prox_state->prox_attr.report_id; - min = prox_state->prox_attr.logical_minimum; - address = HID_USAGE_SENSOR_HUMAN_PRESENCE; - hsdev = prox_state->common_attributes.hsdev; - break; - default: - report_id = -1; - break; - } - if (report_id >= 0) { - hid_sensor_power_state(&prox_state->common_attributes, - true); - *val = sensor_hub_input_attr_get_raw_value( - hsdev, hsdev->usage, address, report_id, - SENSOR_HUB_SYNC, min < 0); - hid_sensor_power_state(&prox_state->common_attributes, - false); - } else { - *val = 0; + if (chan->scan_index >= prox_state->num_channels) return -EINVAL; - } + address = prox_state->channel2usage[chan->scan_index]; + report_id = prox_state->prox_attr[chan->scan_index].report_id; + hsdev = prox_state->common_attributes.hsdev; + min = prox_state->prox_attr[chan->scan_index].logical_minimum; + hid_sensor_power_state(&prox_state->common_attributes, true); + *val = sensor_hub_input_attr_get_raw_value(hsdev, + hsdev->usage, + address, + report_id, + SENSOR_HUB_SYNC, + min < 0); + if (prox_state->channel2usage[chan->scan_index] == + HID_USAGE_SENSOR_HUMAN_ATTENTION) + *val *= 100; + hid_sensor_power_state(&prox_state->common_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: @@ -103,7 +120,7 @@ static int prox_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_OFFSET: *val = hid_sensor_convert_exponent( - prox_state->prox_attr.unit_expo); + prox_state->prox_attr[chan->scan_index].unit_expo); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -153,14 +170,6 @@ static const struct iio_info prox_info = { .write_raw = &prox_write_raw, }; -/* Function to push data to buffer */ -static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, - int len) -{ - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - iio_push_to_buffers(indio_dev, data); -} - /* Callback handler to send event after all samples are received and captured */ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, unsigned usage_id, @@ -170,10 +179,10 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, struct prox_state *prox_state = iio_priv(indio_dev); dev_dbg(&indio_dev->dev, "prox_proc_event\n"); - if (atomic_read(&prox_state->common_attributes.data_ready)) - hid_sensor_push_data(indio_dev, - &prox_state->human_presence, - sizeof(prox_state->human_presence)); + if (atomic_read(&prox_state->common_attributes.data_ready)) { + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + iio_push_to_buffers(indio_dev, &prox_state->human_presence); + } return 0; } @@ -186,58 +195,77 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, { struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); - int ret = -EINVAL; - - switch (usage_id) { - case HID_USAGE_SENSOR_HUMAN_PRESENCE: - switch (raw_len) { - case 1: - prox_state->human_presence = *(u8 *)raw_data; - return 0; - case 4: - prox_state->human_presence = *(u32 *)raw_data; - return 0; - default: + int multiplier = 1; + int chan; + + for (chan = 0; chan < prox_state->num_channels; chan++) + if (prox_state->channel2usage[chan] == usage_id) break; - } - break; + if (chan == prox_state->num_channels) + return -EINVAL; + + if (usage_id == HID_USAGE_SENSOR_HUMAN_ATTENTION) + multiplier = 100; + + switch (raw_len) { + case 1: + prox_state->human_presence[chan] = *(u8 *)raw_data * multiplier; + return 0; + case 4: + prox_state->human_presence[chan] = *(u32 *)raw_data * multiplier; + return 0; } - return ret; + return -EINVAL; } /* Parse report which is specific to an usage id*/ static int prox_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, - unsigned usage_id, struct prox_state *st) { + struct iio_chan_spec *channels = st->channels; + int index = 0; int ret; + int i; + + for (i = 0; i < MAX_CHANNELS; i++) { + u32 usage_id = prox_usage_ids[i]; + + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + hsdev->usage, + usage_id, + &st->prox_attr[index]); + if (ret < 0) + continue; + st->channel2usage[index] = usage_id; + st->scan_mask[0] |= BIT(index); + channels[index] = prox_channels[i]; + channels[index].scan_index = index; + prox_adjust_channel_bit_mask(channels, index, + st->prox_attr[index].size); + dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr[index].index, + st->prox_attr[index].report_id); + index++; + } - ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, - usage_id, - HID_USAGE_SENSOR_HUMAN_PRESENCE, - &st->prox_attr); - if (ret < 0) + if (!index) return ret; - prox_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESENCE, - st->prox_attr.size); - dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index, - st->prox_attr.report_id); + st->num_channels = index; - return ret; + return 0; } /* Function to initialize the processing for usage id */ static int hid_prox_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret = 0; static const char *name = "prox"; struct iio_dev *indio_dev; struct prox_state *prox_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct prox_state)); @@ -258,22 +286,15 @@ static int hid_prox_probe(struct platform_device *pdev) return ret; } - indio_dev->channels = devm_kmemdup(&pdev->dev, prox_channels, - sizeof(prox_channels), GFP_KERNEL); - if (!indio_dev->channels) { - dev_err(&pdev->dev, "failed to duplicate channels\n"); - return -ENOMEM; - } - - ret = prox_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - hsdev->usage, prox_state); + ret = prox_parse_report(pdev, hsdev, prox_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); return ret; } - indio_dev->num_channels = ARRAY_SIZE(prox_channels); + indio_dev->num_channels = prox_state->num_channels; + indio_dev->channels = prox_state->channels; + indio_dev->available_scan_masks = prox_state->scan_mask; indio_dev->info = &prox_info; indio_dev->name = name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -315,7 +336,7 @@ static int hid_prox_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_prox_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct prox_state *prox_state = iio_priv(indio_dev); @@ -344,7 +365,7 @@ static struct platform_driver hid_prox_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_prox_probe, - .remove_new = hid_prox_remove, + .remove = hid_prox_remove, }; module_platform_driver(hid_prox_platform_driver); diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c index 6de33feada3a3..b9f230210f073 100644 --- a/drivers/iio/light/iqs621-als.c +++ b/drivers/iio/light/iqs621-als.c @@ -271,7 +271,7 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); struct iqs62x_core *iqs62x = iqs621_als->iqs62x; diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index 8dfc750e68c02..201eae1c45892 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -8,17 +8,18 @@ * Copyright (c) 2010, NVIDIA Corporation. */ -#include #include #include +#include +#include #include #include #include #include #include + #include #include -#include #define ISL29018_CONV_TIME_MS 100 @@ -687,20 +688,6 @@ static const struct isl29018_chip_info isl29018_chip_info_tbl[] = { }, }; -static const char *isl29018_match_acpi_device(struct device *dev, int *data) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - - if (!id) - return NULL; - - *data = (int)id->driver_data; - - return dev_name(dev); -} - static void isl29018_disable_regulator_action(void *_data) { struct isl29018_chip *chip = _data; @@ -716,9 +703,10 @@ static int isl29018_probe(struct i2c_client *client) const struct i2c_device_id *id = i2c_client_get_device_id(client); struct isl29018_chip *chip; struct iio_dev *indio_dev; + const void *ddata = NULL; + const char *name; + int dev_id; int err; - const char *name = NULL; - int dev_id = 0; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); if (!indio_dev) @@ -731,11 +719,11 @@ static int isl29018_probe(struct i2c_client *client) if (id) { name = id->name; dev_id = id->driver_data; + } else { + name = iio_get_acpi_device_name_and_data(&client->dev, &ddata); + dev_id = (intptr_t)ddata; } - if (ACPI_HANDLE(&client->dev)) - name = isl29018_match_acpi_device(&client->dev, &dev_id); - mutex_init(&chip->lock); chip->type = dev_id; @@ -832,15 +820,13 @@ static int isl29018_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume); -#ifdef CONFIG_ACPI static const struct acpi_device_id isl29018_acpi_match[] = { {"ISL29018", isl29018}, {"ISL29023", isl29023}, {"ISL29035", isl29035}, - {}, + {} }; MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match); -#endif static const struct i2c_device_id isl29018_id[] = { {"isl29018", isl29018}, @@ -854,14 +840,14 @@ static const struct of_device_id isl29018_of_match[] = { { .compatible = "isil,isl29018", }, { .compatible = "isil,isl29023", }, { .compatible = "isil,isl29035", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, isl29018_of_match); static struct i2c_driver isl29018_driver = { .driver = { .name = "isl29018", - .acpi_match_table = ACPI_PTR(isl29018_acpi_match), + .acpi_match_table = isl29018_acpi_match, .pm = pm_sleep_ptr(&isl29018_pm_ops), .of_match_table = isl29018_of_match, }, diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 7800f7fa51b76..99f0b903018cf 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -754,7 +754,7 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) } static int lm3533_als_setup(struct lm3533_als *als, - struct lm3533_als_platform_data *pdata) + const struct lm3533_als_platform_data *pdata) { int ret; @@ -828,8 +828,8 @@ static const struct iio_info lm3533_als_info = { static int lm3533_als_probe(struct platform_device *pdev) { + const struct lm3533_als_platform_data *pdata; struct lm3533 *lm3533; - struct lm3533_als_platform_data *pdata; struct lm3533_als *als; struct iio_dev *indio_dev; int ret; @@ -838,7 +838,7 @@ static int lm3533_als_probe(struct platform_device *pdev) if (!lm3533) return -EINVAL; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data\n"); return -EINVAL; @@ -912,7 +912,7 @@ static struct platform_driver lm3533_als_driver = { .name = "lm3533-als", }, .probe = lm3533_als_probe, - .remove_new = lm3533_als_remove, + .remove = lm3533_als_remove, }; module_platform_driver(lm3533_als_driver); diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index 4f6975e63a8fe..df664f3609030 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -18,14 +18,18 @@ * - Interrupt support */ +#include +#include #include +#include +#include #include #include #include #include -#include #include +#include #include @@ -33,18 +37,27 @@ #define LTR390_ALS_UVS_MEAS_RATE 0x04 #define LTR390_ALS_UVS_GAIN 0x05 #define LTR390_PART_ID 0x06 +#define LTR390_MAIN_STATUS 0x07 #define LTR390_ALS_DATA 0x0D #define LTR390_UVS_DATA 0x10 #define LTR390_INT_CFG 0x19 +#define LTR390_INT_PST 0x1A +#define LTR390_THRESH_UP 0x21 +#define LTR390_THRESH_LOW 0x24 #define LTR390_PART_NUMBER_ID 0xb -#define LTR390_ALS_UVS_GAIN_MASK 0x07 -#define LTR390_ALS_UVS_INT_TIME_MASK 0x70 +#define LTR390_ALS_UVS_GAIN_MASK GENMASK(2, 0) +#define LTR390_ALS_UVS_MEAS_RATE_MASK GENMASK(2, 0) +#define LTR390_ALS_UVS_INT_TIME_MASK GENMASK(6, 4) #define LTR390_ALS_UVS_INT_TIME(x) FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x)) +#define LTR390_INT_PST_MASK GENMASK(7, 4) +#define LTR390_INT_PST_VAL(x) FIELD_PREP(LTR390_INT_PST_MASK, (x)) #define LTR390_SW_RESET BIT(4) #define LTR390_UVS_MODE BIT(3) #define LTR390_SENSOR_ENABLE BIT(1) +#define LTR390_LS_INT_EN BIT(2) +#define LTR390_LS_INT_SEL_UVS BIT(5) #define LTR390_FRACTIONAL_PRECISION 100 @@ -70,6 +83,11 @@ enum ltr390_mode { LTR390_SET_UVS_MODE, }; +enum ltr390_meas_rate { + LTR390_GET_FREQ, + LTR390_GET_PERIOD, +}; + struct ltr390_data { struct regmap *regmap; struct i2c_client *client; @@ -87,6 +105,18 @@ static const struct regmap_config ltr390_regmap_config = { .val_bits = 8, }; +/* Sampling frequency is in mili Hz and mili Seconds */ +static const int ltr390_samp_freq_table[][2] = { + [0] = { 40000, 25 }, + [1] = { 20000, 50 }, + [2] = { 10000, 100 }, + [3] = { 5000, 200 }, + [4] = { 2000, 500 }, + [5] = { 1000, 1000 }, + [6] = { 500, 2000 }, + [7] = { 500, 2000 }, +}; + static int ltr390_register_read(struct ltr390_data *data, u8 register_address) { struct device *dev = &data->client->dev; @@ -135,6 +165,19 @@ static int ltr390_counts_per_uvi(struct ltr390_data *data) return DIV_ROUND_CLOSEST(23 * data->gain * data->int_time_us, 10 * orig_gain * orig_int_time); } +static int ltr390_get_samp_freq_or_period(struct ltr390_data *data, + enum ltr390_meas_rate option) +{ + int ret, value; + + ret = regmap_read(data->regmap, LTR390_ALS_UVS_MEAS_RATE, &value); + if (ret < 0) + return ret; + value = FIELD_GET(LTR390_ALS_UVS_MEAS_RATE_MASK, value); + + return ltr390_samp_freq_table[value][option]; +} + static int ltr390_read_raw(struct iio_dev *iio_device, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -191,6 +234,10 @@ static int ltr390_read_raw(struct iio_dev *iio_device, *val = data->int_time_us; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = ltr390_get_samp_freq_or_period(data, LTR390_GET_FREQ); + return IIO_VAL_INT; + default: return -EINVAL; } @@ -199,6 +246,24 @@ static int ltr390_read_raw(struct iio_dev *iio_device, /* integration time in us */ static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 25000, 12500 }; static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 }; +static const int ltr390_freq_map[] = { 40000, 20000, 10000, 5000, 2000, 1000, 500, 500 }; + +static const struct iio_event_spec ltr390_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD), + } +}; static const struct iio_chan_spec ltr390_channels[] = { /* UV sensor */ @@ -206,16 +271,24 @@ static const struct iio_chan_spec ltr390_channels[] = { .type = IIO_UVINDEX, .scan_index = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), - .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE) + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .event_spec = ltr390_event_spec, + .num_event_specs = ARRAY_SIZE(ltr390_event_spec), }, /* ALS sensor */ { .type = IIO_LIGHT, .scan_index = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), - .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE) + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .event_spec = ltr390_event_spec, + .num_event_specs = ARRAY_SIZE(ltr390_event_spec), }, }; @@ -264,6 +337,23 @@ static int ltr390_set_int_time(struct ltr390_data *data, int val) return -EINVAL; } +static int ltr390_set_samp_freq(struct ltr390_data *data, int val) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(ltr390_samp_freq_table); idx++) { + if (ltr390_samp_freq_table[idx][0] != val) + continue; + + guard(mutex)(&data->lock); + return regmap_update_bits(data->regmap, + LTR390_ALS_UVS_MEAS_RATE, + LTR390_ALS_UVS_MEAS_RATE_MASK, idx); + } + + return -EINVAL; +} + static int ltr390_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { @@ -278,6 +368,11 @@ static int ltr390_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec con *type = IIO_VAL_INT; *vals = ltr390_int_time_map_us; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *length = ARRAY_SIZE(ltr390_freq_map); + *type = IIO_VAL_INT; + *vals = ltr390_freq_map; + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -301,6 +396,191 @@ static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec cons return ltr390_set_int_time(data, val); + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2 != 0) + return -EINVAL; + + return ltr390_set_samp_freq(data, val); + + default: + return -EINVAL; + } +} + +static int ltr390_read_intr_prst(struct ltr390_data *data, int *val) +{ + int ret, prst, samp_period; + + samp_period = ltr390_get_samp_freq_or_period(data, LTR390_GET_PERIOD); + ret = regmap_read(data->regmap, LTR390_INT_PST, &prst); + if (ret < 0) + return ret; + *val = prst * samp_period; + + return IIO_VAL_INT; +} + +static int ltr390_write_intr_prst(struct ltr390_data *data, int val) +{ + int ret, samp_period, new_val; + + samp_period = ltr390_get_samp_freq_or_period(data, LTR390_GET_PERIOD); + + /* persist period should be greater than or equal to samp period */ + if (val < samp_period) + return -EINVAL; + + new_val = DIV_ROUND_UP(val, samp_period); + if (new_val < 0 || new_val > 0x0f) + return -EINVAL; + + guard(mutex)(&data->lock); + ret = regmap_update_bits(data->regmap, + LTR390_INT_PST, + LTR390_INT_PST_MASK, + LTR390_INT_PST_VAL(new_val)); + if (ret) + return ret; + + return 0; +} + +static int ltr390_read_threshold(struct iio_dev *indio_dev, + enum iio_event_direction dir, + int *val, int *val2) +{ + struct ltr390_data *data = iio_priv(indio_dev); + int ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = ltr390_register_read(data, LTR390_THRESH_UP); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + + case IIO_EV_DIR_FALLING: + ret = ltr390_register_read(data, LTR390_THRESH_LOW); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ltr390_write_threshold(struct iio_dev *indio_dev, + enum iio_event_direction dir, + int val, int val2) +{ + struct ltr390_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_bulk_write(data->regmap, LTR390_THRESH_UP, &val, 3); + + case IIO_EV_DIR_FALLING: + return regmap_bulk_write(data->regmap, LTR390_THRESH_LOW, &val, 3); + + default: + return -EINVAL; + } +} + +static int ltr390_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + return ltr390_read_threshold(indio_dev, dir, val, val2); + + case IIO_EV_INFO_PERIOD: + return ltr390_read_intr_prst(iio_priv(indio_dev), val); + + default: + return -EINVAL; + } +} + +static int ltr390_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + if (val2 != 0) + return -EINVAL; + + return ltr390_write_threshold(indio_dev, dir, val, val2); + + case IIO_EV_INFO_PERIOD: + if (val2 != 0) + return -EINVAL; + + return ltr390_write_intr_prst(iio_priv(indio_dev), val); + + default: + return -EINVAL; + } +} + +static int ltr390_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ltr390_data *data = iio_priv(indio_dev); + int ret, status; + + ret = regmap_read(data->regmap, LTR390_INT_CFG, &status); + if (ret < 0) + return ret; + + return FIELD_GET(LTR390_LS_INT_EN, status); +} + +static int ltr390_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ltr390_data *data = iio_priv(indio_dev); + int ret; + + if (!state) + return regmap_clear_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_EN); + + guard(mutex)(&data->lock); + ret = regmap_set_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_EN); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_LIGHT: + ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE); + if (ret < 0) + return ret; + + return regmap_clear_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_SEL_UVS); + + case IIO_UVINDEX: + ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE); + if (ret < 0) + return ret; + + return regmap_set_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_SEL_UVS); + default: return -EINVAL; } @@ -310,8 +590,44 @@ static const struct iio_info ltr390_info = { .read_raw = ltr390_read_raw, .write_raw = ltr390_write_raw, .read_avail = ltr390_read_avail, + .read_event_value = ltr390_read_event_value, + .read_event_config = ltr390_read_event_config, + .write_event_value = ltr390_write_event_value, + .write_event_config = ltr390_write_event_config, }; +static irqreturn_t ltr390_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ltr390_data *data = iio_priv(indio_dev); + int ret, status; + + /* Reading the status register to clear the interrupt flag, Datasheet pg: 17*/ + ret = regmap_read(data->regmap, LTR390_MAIN_STATUS, &status); + if (ret < 0) + return ret; + + switch (data->mode) { + case LTR390_SET_ALS_MODE: + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + break; + + case LTR390_SET_UVS_MODE: + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_UVINDEX, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + break; + } + + return IRQ_HANDLED; +} + static int ltr390_probe(struct i2c_client *client) { struct ltr390_data *data; @@ -365,9 +681,40 @@ static int ltr390_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "failed to enable the sensor\n"); + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, + NULL, ltr390_interrupt_handler, + IRQF_ONESHOT, + "ltr390_thresh_event", + indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "request irq (%d) failed\n", client->irq); + } + return devm_iio_device_register(dev, indio_dev); } +static int ltr390_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ltr390_data *data = iio_priv(indio_dev); + + return regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, + LTR390_SENSOR_ENABLE); +} + +static int ltr390_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ltr390_data *data = iio_priv(indio_dev); + + return regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, + LTR390_SENSOR_ENABLE); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ltr390_pm_ops, ltr390_suspend, ltr390_resume); + static const struct i2c_device_id ltr390_id[] = { { "ltr390" }, { /* Sentinel */ } @@ -384,6 +731,7 @@ static struct i2c_driver ltr390_driver = { .driver = { .name = "ltr390", .of_match_table = ltr390_of_table, + .pm = pm_sleep_ptr(<r390_pm_ops), }, .probe = ltr390_probe, .id_table = ltr390_id, diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 8c516ede91161..604f5f900a2ec 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -1078,15 +1077,11 @@ static int ltr501_read_event_config(struct iio_dev *indio_dev, static int ltr501_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct ltr501_data *data = iio_priv(indio_dev); int ret; - /* only 1 and 0 are valid inputs */ - if (state != 1 && state != 0) - return -EINVAL; - switch (chan->type) { case IIO_INTENSITY: mutex_lock(&data->lock_als); @@ -1422,17 +1417,6 @@ static int ltr501_powerdown(struct ltr501_data *data) data->ps_contr & ~LTR501_CONTR_ACTIVE); } -static const char *ltr501_match_acpi_device(struct device *dev, int *chip_idx) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - *chip_idx = id->driver_data; - return dev_name(dev); -} - static int ltr501_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -1440,8 +1424,10 @@ static int ltr501_probe(struct i2c_client *client) struct ltr501_data *data; struct iio_dev *indio_dev; struct regmap *regmap; - int ret, partid, chip_idx = 0; - const char *name = NULL; + const void *ddata = NULL; + int partid, chip_idx; + const char *name; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -1523,11 +1509,12 @@ static int ltr501_probe(struct i2c_client *client) if (id) { name = id->name; chip_idx = id->driver_data; - } else if (ACPI_HANDLE(&client->dev)) { - name = ltr501_match_acpi_device(&client->dev, &chip_idx); } else { - return -ENODEV; + name = iio_get_acpi_device_name_and_data(&client->dev, &ddata); + chip_idx = (intptr_t)ddata; } + if (!name) + return -ENODEV; data->chip_info = <r501_chip_info_tbl[chip_idx]; @@ -1610,9 +1597,9 @@ static int ltr501_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); static const struct acpi_device_id ltr_acpi_match[] = { - { "LTER0501", ltr501 }, - { "LTER0559", ltr559 }, { "LTER0301", ltr301 }, + /* https://www.catalog.update.microsoft.com/Search.aspx?q=lter0303 */ + { "LTER0303", ltr303 }, { }, }; MODULE_DEVICE_TABLE(acpi, ltr_acpi_match); diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index 37eecff571b96..dbec1e7cfeb86 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -561,6 +561,7 @@ MODULE_DEVICE_TABLE(i2c, ltrf216a_id); static const struct of_device_id ltrf216a_of_match[] = { { .compatible = "liteon,ltr308", .data = <r308_chip_info }, { .compatible = "liteon,ltrf216a", .data = <rf216a_chip_info }, + /* For Valve's Steamdeck device, an ACPI platform using PRP0001 */ { .compatible = "ltr,ltrf216a", .data = <rf216a_chip_info }, {} }; diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c index 3b92362675dc9..8cd7f5664e5b1 100644 --- a/drivers/iio/light/max44009.c +++ b/drivers/iio/light/max44009.c @@ -422,7 +422,7 @@ static int max44009_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct max44009_data *data = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 176e54bb48c33..65b295877b415 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -70,6 +70,35 @@ #define OPT3001_RESULT_READY_SHORT 150 #define OPT3001_RESULT_READY_LONG 1000 +struct opt3001_scale { + int val; + int val2; +}; + +struct opt3001_chip_info { + const struct iio_chan_spec (*channels)[2]; + enum iio_chan_type chan_type; + int num_channels; + + const struct opt3001_scale (*scales)[12]; + /* + * Factor as specified by conversion equation in datasheet. + * eg. 0.01 (scaled to integer 10) for opt3001. + */ + int factor_whole; + /* + * Factor to compensate for potentially scaled factor_whole. + */ + int factor_integer; + /* + * Factor used to align decimal part of proccessed value to six decimal + * places. + */ + int factor_decimal; + + bool has_id; +}; + struct opt3001 { struct i2c_client *client; struct device *dev; @@ -79,6 +108,7 @@ struct opt3001 { bool result_ready; wait_queue_head_t result_ready_queue; u16 result; + const struct opt3001_chip_info *chip_info; u32 int_time; u32 mode; @@ -92,11 +122,6 @@ struct opt3001 { bool use_irq; }; -struct opt3001_scale { - int val; - int val2; -}; - static const struct opt3001_scale opt3001_scales[] = { { .val = 40, @@ -148,21 +173,68 @@ static const struct opt3001_scale opt3001_scales[] = { }, }; +static const struct opt3001_scale opt3002_scales[] = { + { + .val = 4914, + .val2 = 0, + }, + { + .val = 9828, + .val2 = 0, + }, + { + .val = 19656, + .val2 = 0, + }, + { + .val = 39312, + .val2 = 0, + }, + { + .val = 78624, + .val2 = 0, + }, + { + .val = 157248, + .val2 = 0, + }, + { + .val = 314496, + .val2 = 0, + }, + { + .val = 628992, + .val2 = 0, + }, + { + .val = 1257984, + .val2 = 0, + }, + { + .val = 2515968, + .val2 = 0, + }, + { + .val = 5031936, + .val2 = 0, + }, + { + .val = 10063872, + .val2 = 0, + }, +}; + static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2, u8 *exponent) { int i; - - for (i = 0; i < ARRAY_SIZE(opt3001_scales); i++) { - const struct opt3001_scale *scale = &opt3001_scales[i]; - + for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { + const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i]; /* - * Combine the integer and micro parts for comparison - * purposes. Use milli lux precision to avoid 32-bit integer - * overflows. + * Compare the integer and micro parts to determine value scale. */ - if ((val * 1000 + val2 / 1000) <= - (scale->val * 1000 + scale->val2 / 1000)) { + if (val < scale->val || + (val == scale->val && val2 <= scale->val2)) { *exponent = i; return 0; } @@ -174,11 +246,14 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val, static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, u16 mantissa, int *val, int *val2) { - int lux; + int ret; + int whole = opt->chip_info->factor_whole; + int integer = opt->chip_info->factor_integer; + int decimal = opt->chip_info->factor_decimal; - lux = 10 * (mantissa << exponent); - *val = lux / 1000; - *val2 = (lux - (*val * 1000)) * 1000; + ret = whole * (mantissa << exponent); + *val = ret / integer; + *val2 = (ret - (*val * integer)) * decimal; } static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode) @@ -225,7 +300,18 @@ static const struct iio_chan_spec opt3001_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), }; -static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2) +static const struct iio_chan_spec opt3002_channels[] = { + { + .type = IIO_INTENSITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME), + .event_spec = opt3001_event_spec, + .num_event_specs = ARRAY_SIZE(opt3001_event_spec), + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) { int ret; u16 mantissa; @@ -397,14 +483,15 @@ static int opt3001_read_raw(struct iio_dev *iio, if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; - if (chan->type != IIO_LIGHT) + if (chan->type != opt->chip_info->chan_type) return -EINVAL; mutex_lock(&opt->lock); switch (mask) { + case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = opt3001_get_lux(opt, val, val2); + ret = opt3001_get_processed(opt, val, val2); break; case IIO_CHAN_INFO_INT_TIME: ret = opt3001_get_int_time(opt, val, val2); @@ -428,7 +515,7 @@ static int opt3001_write_raw(struct iio_dev *iio, if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) return -EBUSY; - if (chan->type != IIO_LIGHT) + if (chan->type != opt->chip_info->chan_type) return -EINVAL; if (mask != IIO_CHAN_INFO_INT_TIME) @@ -479,6 +566,9 @@ static int opt3001_write_event_value(struct iio_dev *iio, { struct opt3001 *opt = iio_priv(iio); int ret; + int whole; + int integer; + int decimal; u16 mantissa; u16 value; @@ -497,7 +587,12 @@ static int opt3001_write_event_value(struct iio_dev *iio, goto err; } - mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent; + whole = opt->chip_info->factor_whole; + integer = opt->chip_info->factor_integer; + decimal = opt->chip_info->factor_decimal; + + mantissa = (((val * integer) + (val2 / decimal)) / whole) >> exponent; + value = (exponent << 12) | mantissa; switch (dir) { @@ -539,7 +634,7 @@ static int opt3001_read_event_config(struct iio_dev *iio, static int opt3001_write_event_config(struct iio_dev *iio, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct opt3001 *opt = iio_priv(iio); int ret; @@ -610,7 +705,7 @@ static int opt3001_read_id(struct opt3001 *opt) ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID); if (ret < 0) { dev_err(opt->dev, "failed to read register %02x\n", - OPT3001_DEVICE_ID); + OPT3001_DEVICE_ID); return ret; } @@ -692,6 +787,7 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) struct opt3001 *opt = iio_priv(iio); int ret; bool wake_result_ready_queue = false; + enum iio_chan_type chan_type = opt->chip_info->chan_type; if (!opt->ok_to_ignore_lock) mutex_lock(&opt->lock); @@ -707,13 +803,13 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) OPT3001_CONFIGURATION_M_CONTINUOUS) { if (ret & OPT3001_CONFIGURATION_FH) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_UNMOD_EVENT_CODE(chan_type, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), iio_get_time_ns(iio)); if (ret & OPT3001_CONFIGURATION_FL) iio_push_event(iio, - IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_UNMOD_EVENT_CODE(chan_type, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), iio_get_time_ns(iio)); @@ -755,22 +851,25 @@ static int opt3001_probe(struct i2c_client *client) opt = iio_priv(iio); opt->client = client; opt->dev = dev; + opt->chip_info = i2c_get_match_data(client); mutex_init(&opt->lock); init_waitqueue_head(&opt->result_ready_queue); i2c_set_clientdata(client, iio); - ret = opt3001_read_id(opt); - if (ret) - return ret; + if (opt->chip_info->has_id) { + ret = opt3001_read_id(opt); + if (ret) + return ret; + } ret = opt3001_configure(opt); if (ret) return ret; iio->name = client->name; - iio->channels = opt3001_channels; - iio->num_channels = ARRAY_SIZE(opt3001_channels); + iio->channels = *opt->chip_info->channels; + iio->num_channels = opt->chip_info->num_channels; iio->modes = INDIO_DIRECT_MODE; iio->info = &opt3001_info; @@ -825,14 +924,38 @@ static void opt3001_remove(struct i2c_client *client) } } +static const struct opt3001_chip_info opt3001_chip_information = { + .channels = &opt3001_channels, + .chan_type = IIO_LIGHT, + .num_channels = ARRAY_SIZE(opt3001_channels), + .scales = &opt3001_scales, + .factor_whole = 10, + .factor_integer = 1000, + .factor_decimal = 1000, + .has_id = true, +}; + +static const struct opt3001_chip_info opt3002_chip_information = { + .channels = &opt3002_channels, + .chan_type = IIO_INTENSITY, + .num_channels = ARRAY_SIZE(opt3002_channels), + .scales = &opt3002_scales, + .factor_whole = 12, + .factor_integer = 10, + .factor_decimal = 100000, + .has_id = false, +}; + static const struct i2c_device_id opt3001_id[] = { - { "opt3001" }, + { "opt3001", (kernel_ulong_t)&opt3001_chip_information }, + { "opt3002", (kernel_ulong_t)&opt3002_chip_information }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(i2c, opt3001_id); static const struct of_device_id opt3001_of_match[] = { - { .compatible = "ti,opt3001" }, + { .compatible = "ti,opt3001", .data = &opt3001_chip_information }, + { .compatible = "ti,opt3002", .data = &opt3002_chip_information }, { } }; MODULE_DEVICE_TABLE(of, opt3001_of_match); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 78c08e0bd0776..56f5fbbf79ac7 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -438,18 +438,6 @@ static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private) return IRQ_NONE; } -static irqreturn_t rpr0521_trigger_consumer_store_time(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - - /* Other trigger polls store time here. */ - if (!iio_trigger_using_own(indio_dev)) - pf->timestamp = iio_get_time_ns(indio_dev); - - return IRQ_WAKE_THREAD; -} - static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -1016,7 +1004,7 @@ static int rpr0521_probe(struct i2c_client *client) /* Trigger consumer setup */ ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, - rpr0521_trigger_consumer_store_time, + iio_pollfunc_store_time, rpr0521_trigger_consumer_handler, &rpr0521_buffer_setup_ops); if (ret < 0) { diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index fba3997574bbf..f1fc8cb6f69a5 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -174,8 +174,7 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) unsigned long irq_type; int err; - irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); - + irq_type = irq_get_trigger_type(hw->irq); switch (irq_type) { case IRQF_TRIGGER_HIGH: case IRQF_TRIGGER_RISING: diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index ed20b67145463..b81cc44db43c3 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -324,15 +324,12 @@ static int stk3310_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { int ret; struct stk3310_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; - if (state < 0 || state > 7) - return -EINVAL; - /* Set INT_PS value */ mutex_lock(&data->lock); ret = regmap_field_write(data->reg_int_ps, state); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 04452b4664f30..4186aac04902e 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -327,7 +327,7 @@ static int tcs3472_read_event_config(struct iio_dev *indio_dev, static int tcs3472_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct tcs3472_data *data = iio_priv(indio_dev); int ret = 0; diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 1a6f514bced6e..f1fe7640fce63 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -630,7 +630,7 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private) static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct tsl2563_chip *chip = iio_priv(indio_dev); int ret = 0; diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index 850c2465992fa..b81ca6f73f927 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -985,7 +985,7 @@ static int tsl2591_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct tsl2591_chip *chip = iio_priv(indio_dev); struct i2c_client *client = chip->client; diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index cab468a82b616..349afdcbe30dd 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1081,14 +1081,14 @@ static int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int val) + bool val) { struct tsl2772_chip *chip = iio_priv(indio_dev); if (chan->type == IIO_INTENSITY) - chip->settings.als_interrupt_en = val ? true : false; + chip->settings.als_interrupt_en = val; else - chip->settings.prox_interrupt_en = val ? true : false; + chip->settings.prox_interrupt_en = val; return tsl2772_invoke_change(indio_dev); } diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index de6967ac3b0b3..c83114aed6b23 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -627,7 +627,7 @@ static int us5182d_read_event_config(struct iio_dev *indio_dev, static int us5182d_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct us5182d_data *data = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 4e3641ff2ed44..e19199b17f2ef 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1456,7 +1456,7 @@ static int vcnl4010_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { switch (chan->type) { case IIO_PROXIMITY: @@ -1501,7 +1501,8 @@ static int vcnl4040_read_event_config(struct iio_dev *indio_dev, static int vcnl4040_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { int ret = -EINVAL; u16 val, mask; diff --git a/drivers/iio/light/veml3235.c b/drivers/iio/light/veml3235.c new file mode 100644 index 0000000000000..66361c3012a3d --- /dev/null +++ b/drivers/iio/light/veml3235.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * VEML3235 Ambient Light Sensor + * + * Copyright (c) 2024, Javier Carrasco + * + * Datasheet: https://www.vishay.com/docs/80131/veml3235.pdf + * Appnote-80222: https://www.vishay.com/docs/80222/designingveml3235.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#define VEML3235_REG_CONF 0x00 +#define VEML3235_REG_WH_DATA 0x04 +#define VEML3235_REG_ALS_DATA 0x05 +#define VEML3235_REG_ID 0x09 + +#define VEML3235_CONF_SD BIT(0) +#define VEML3235_CONF_SD0 BIT(15) + +struct veml3235_rf { + struct regmap_field *it; + struct regmap_field *gain; + struct regmap_field *id; +}; + +struct veml3235_data { + struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + struct veml3235_rf rf; +}; + +static const int veml3235_it_times[][2] = { + { 0, 50000 }, + { 0, 100000 }, + { 0, 200000 }, + { 0, 400000 }, + { 0, 800000 }, +}; + +static const int veml3235_scale_vals[] = { 1, 2, 4, 8 }; + +static int veml3235_power_on(struct veml3235_data *data) +{ + int ret; + + ret = regmap_clear_bits(data->regmap, VEML3235_REG_CONF, + VEML3235_CONF_SD | VEML3235_CONF_SD0); + if (ret) + return ret; + + /* Wait 4 ms to let processor & oscillator start correctly */ + fsleep(4000); + + return 0; +} + +static int veml3235_shut_down(struct veml3235_data *data) +{ + return regmap_set_bits(data->regmap, VEML3235_REG_CONF, + VEML3235_CONF_SD | VEML3235_CONF_SD0); +} + +static void veml3235_shut_down_action(void *data) +{ + veml3235_shut_down(data); +} + +enum veml3235_chan { + CH_ALS, + CH_WHITE, +}; + +static const struct iio_chan_spec veml3235_channels[] = { + { + .type = IIO_LIGHT, + .channel = CH_ALS, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_INTENSITY, + .channel = CH_WHITE, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BOTH, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct regmap_config veml3235_regmap_config = { + .name = "veml3235_regmap", + .reg_bits = 8, + .val_bits = 16, + .max_register = VEML3235_REG_ID, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2) +{ + int ret, reg; + + ret = regmap_field_read(data->rf.it, ®); + if (ret) + return ret; + + switch (reg) { + case 0: + *val2 = 50000; + break; + case 1: + *val2 = 100000; + break; + case 2: + *val2 = 200000; + break; + case 3: + *val2 = 400000; + break; + case 4: + *val2 = 800000; + break; + default: + return -EINVAL; + } + + *val = 0; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int veml3235_set_it(struct iio_dev *indio_dev, int val, int val2) +{ + struct veml3235_data *data = iio_priv(indio_dev); + int ret, new_it; + + if (val) + return -EINVAL; + + switch (val2) { + case 50000: + new_it = 0x00; + break; + case 100000: + new_it = 0x01; + break; + case 200000: + new_it = 0x02; + break; + case 400000: + new_it = 0x03; + break; + case 800000: + new_it = 0x04; + break; + default: + return -EINVAL; + } + + ret = regmap_field_write(data->rf.it, new_it); + if (ret) { + dev_err(data->dev, + "failed to update integration time: %d\n", ret); + return ret; + } + + return 0; +} + +static int veml3235_set_gain(struct iio_dev *indio_dev, int val, int val2) +{ + struct veml3235_data *data = iio_priv(indio_dev); + int ret, new_gain; + + if (val2 != 0) + return -EINVAL; + + switch (val) { + case 1: + new_gain = 0x00; + break; + case 2: + new_gain = 0x01; + break; + case 4: + new_gain = 0x03; + break; + case 8: + new_gain = 0x07; + break; + default: + return -EINVAL; + } + + ret = regmap_field_write(data->rf.gain, new_gain); + if (ret) { + dev_err(data->dev, "failed to set gain: %d\n", ret); + return ret; + } + + return 0; +} + +static int veml3235_get_gain(struct veml3235_data *data, int *val) +{ + int ret, reg; + + ret = regmap_field_read(data->rf.gain, ®); + if (ret) { + dev_err(data->dev, "failed to read gain %d\n", ret); + return ret; + } + + switch (reg & 0x03) { + case 0: + *val = 1; + break; + case 1: + *val = 2; + break; + case 3: + *val = 4; + break; + default: + return -EINVAL; + } + + /* Double gain */ + if (reg & 0x04) + *val *= 2; + + return IIO_VAL_INT; +} + +static int veml3235_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct veml3235_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + int ret, reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + ret = regmap_read(regmap, VEML3235_REG_ALS_DATA, ®); + if (ret < 0) + return ret; + + *val = reg; + return IIO_VAL_INT; + case IIO_INTENSITY: + ret = regmap_read(regmap, VEML3235_REG_WH_DATA, ®); + if (ret < 0) + return ret; + + *val = reg; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_INT_TIME: + return veml3235_get_it(data, val, val2); + case IIO_CHAN_INFO_SCALE: + return veml3235_get_gain(data, val); + default: + return -EINVAL; + } +} + +static int veml3235_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *vals = (int *)&veml3235_it_times; + *length = 2 * ARRAY_SIZE(veml3235_it_times); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (int *)&veml3235_scale_vals; + *length = ARRAY_SIZE(veml3235_scale_vals); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int veml3235_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return veml3235_set_it(indio_dev, val, val2); + case IIO_CHAN_INFO_SCALE: + return veml3235_set_gain(indio_dev, val, val2); + } + + return -EINVAL; +} + +static void veml3235_read_id(struct veml3235_data *data) +{ + int ret, reg; + + ret = regmap_field_read(data->rf.id, ®); + if (ret) { + dev_info(data->dev, "failed to read ID\n"); + return; + } + + if (reg != 0x35) + dev_info(data->dev, "Unknown ID %d\n", reg); +} + +static const struct reg_field veml3235_rf_it = + REG_FIELD(VEML3235_REG_CONF, 4, 6); + +static const struct reg_field veml3235_rf_gain = + REG_FIELD(VEML3235_REG_CONF, 11, 13); + +static const struct reg_field veml3235_rf_id = + REG_FIELD(VEML3235_REG_ID, 0, 7); + +static int veml3235_regfield_init(struct veml3235_data *data) +{ + struct regmap *regmap = data->regmap; + struct device *dev = data->dev; + struct regmap_field *rm_field; + struct veml3235_rf *rf = &data->rf; + + rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_it); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->it = rm_field; + + rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_gain); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->gain = rm_field; + + rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_id); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->id = rm_field; + + return 0; +} + +static int veml3235_hw_init(struct iio_dev *indio_dev) +{ + struct veml3235_data *data = iio_priv(indio_dev); + struct device *dev = data->dev; + int ret; + + /* Set gain to 1 and integration time to 100 ms */ + ret = regmap_field_write(data->rf.gain, 0x00); + if (ret) + return dev_err_probe(data->dev, ret, "failed to set gain\n"); + + ret = regmap_field_write(data->rf.it, 0x01); + if (ret) + return dev_err_probe(data->dev, ret, + "failed to set integration time\n"); + + ret = veml3235_power_on(data); + if (ret) + return dev_err_probe(dev, ret, "failed to power on\n"); + + return devm_add_action_or_reset(dev, veml3235_shut_down_action, data); +} + +static const struct iio_info veml3235_info = { + .read_raw = veml3235_read_raw, + .read_avail = veml3235_read_avail, + .write_raw = veml3235_write_raw, +}; + +static int veml3235_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct veml3235_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(client, &veml3235_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to setup regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->dev = dev; + data->regmap = regmap; + + ret = veml3235_regfield_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to init regfield\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable regulator\n"); + + indio_dev->name = "veml3235"; + indio_dev->channels = veml3235_channels; + indio_dev->num_channels = ARRAY_SIZE(veml3235_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &veml3235_info; + + veml3235_read_id(data); + + ret = veml3235_hw_init(indio_dev); + if (ret < 0) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int veml3235_runtime_suspend(struct device *dev) +{ + struct veml3235_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = veml3235_shut_down(data); + if (ret < 0) + dev_err(data->dev, "failed to suspend: %d\n", ret); + + return ret; +} + +static int veml3235_runtime_resume(struct device *dev) +{ + struct veml3235_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = veml3235_power_on(data); + if (ret < 0) + dev_err(data->dev, "failed to resume: %d\n", ret); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(veml3235_pm_ops, veml3235_runtime_suspend, + veml3235_runtime_resume, NULL); + +static const struct of_device_id veml3235_of_match[] = { + { .compatible = "vishay,veml3235" }, + { } +}; +MODULE_DEVICE_TABLE(of, veml3235_of_match); + +static const struct i2c_device_id veml3235_id[] = { + { "veml3235" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, veml3235_id); + +static struct i2c_driver veml3235_driver = { + .driver = { + .name = "veml3235", + .of_match_table = veml3235_of_match, + .pm = pm_ptr(&veml3235_pm_ops), + }, + .probe = veml3235_probe, + .id_table = veml3235_id, +}; +module_i2c_driver(veml3235_driver); + +MODULE_AUTHOR("Javier Carrasco "); +MODULE_DESCRIPTION("VEML3235 Ambient Light Sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 621428885455c..ccb43dfd5cf78 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -1,19 +1,30 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * VEML6030 Ambient Light Sensor + * VEML6030, VMEL6035 and VEML7700 Ambient Light Sensors * * Copyright (c) 2019, Rishi Gupta * + * VEML6030: * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf * Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf + * + * VEML6035: + * Datasheet: https://www.vishay.com/docs/84889/veml6035.pdf + * Appnote-84944: https://www.vishay.com/docs/84944/designingveml6035.pdf + * + * VEML7700: + * Datasheet: https://www.vishay.com/docs/84286/veml7700.pdf + * Appnote-84323: https://www.vishay.com/docs/84323/designingveml7700.pdf */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -38,16 +49,35 @@ #define VEML6030_ALS_INT_EN BIT(1) #define VEML6030_ALS_SD BIT(0) +#define VEML6035_GAIN_M GENMASK(12, 10) +#define VEML6035_GAIN BIT(10) +#define VEML6035_DG BIT(11) +#define VEML6035_SENS BIT(12) +#define VEML6035_INT_CHAN BIT(3) +#define VEML6035_CHAN_EN BIT(2) + +struct veml603x_chip { + const char *name; + const int(*scale_vals)[][2]; + const int num_scale_vals; + const struct iio_chan_spec *channels; + const int num_channels; + int (*hw_init)(struct iio_dev *indio_dev, struct device *dev); + int (*set_info)(struct iio_dev *indio_dev); + int (*set_als_gain)(struct iio_dev *indio_dev, int val, int val2); + int (*get_als_gain)(struct iio_dev *indio_dev, int *val, int *val2); +}; + /* * The resolution depends on both gain and integration time. The * cur_resolution stores one of the resolution mentioned in the * table during startup and gets updated whenever integration time * or gain is changed. * - * Table 'resolution and maximum detection range' in appnote 84367 + * Table 'resolution and maximum detection range' in the appnotes * is visualized as a 2D array. The cur_gain stores index of gain - * in this table (0-3) while the cur_integration_time holds index - * of integration time (0-5). + * in this table (0-3 for VEML6030, 0-5 for VEML6035) while the + * cur_integration_time holds index of integration time (0-5). */ struct veml6030_data { struct i2c_client *client; @@ -55,27 +85,37 @@ struct veml6030_data { int cur_resolution; int cur_gain; int cur_integration_time; + const struct veml603x_chip *chip; }; -/* Integration time available in seconds */ -static IIO_CONST_ATTR(in_illuminance_integration_time_available, - "0.025 0.05 0.1 0.2 0.4 0.8"); +static const int veml6030_it_times[][2] = { + { 0, 25000 }, + { 0, 50000 }, + { 0, 100000 }, + { 0, 200000 }, + { 0, 400000 }, + { 0, 800000 }, +}; /* * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is - * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2. + * ALS gain x (1/4), 0.5 is ALS gain x (1/2), 1.0 is ALS gain x 1, + * 2.0 is ALS gain x2, and 4.0 is ALS gain x 4. */ -static IIO_CONST_ATTR(in_illuminance_scale_available, - "0.125 0.25 1.0 2.0"); - -static struct attribute *veml6030_attributes[] = { - &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, - &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, - NULL +static const int veml6030_scale_vals[][2] = { + { 0, 125000 }, + { 0, 250000 }, + { 1, 0 }, + { 2, 0 }, }; -static const struct attribute_group veml6030_attr_group = { - .attrs = veml6030_attributes, +static const int veml6035_scale_vals[][2] = { + { 0, 125000 }, + { 0, 250000 }, + { 0, 500000 }, + { 1, 0 }, + { 2, 0 }, + { 4, 0 }, }; /* @@ -143,14 +183,23 @@ static const struct attribute_group veml6030_event_attr_group = { static int veml6030_als_pwr_on(struct veml6030_data *data) { - return regmap_clear_bits(data->regmap, VEML6030_REG_ALS_CONF, - VEML6030_ALS_SD); + int ret; + + ret = regmap_clear_bits(data->regmap, VEML6030_REG_ALS_CONF, + VEML6030_ALS_SD); + if (ret) + return ret; + + /* Wait 4 ms to let processor & oscillator start correctly */ + fsleep(4000); + + return 0; } static int veml6030_als_shut_down(struct veml6030_data *data) { - return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, - VEML6030_ALS_SD, 1); + return regmap_set_bits(data->regmap, VEML6030_REG_ALS_CONF, + VEML6030_ALS_SD); } static void veml6030_als_shut_down_action(void *data) @@ -189,6 +238,8 @@ static const struct iio_chan_spec veml6030_channels[] = { BIT(IIO_CHAN_INFO_PROCESSED) | BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), .event_spec = veml6030_event_spec, .num_event_specs = ARRAY_SIZE(veml6030_event_spec), }, @@ -198,7 +249,34 @@ static const struct iio_chan_spec veml6030_channels[] = { .modified = 1, .channel2 = IIO_MOD_LIGHT_BOTH, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_PROCESSED), + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_chan_spec veml7700_channels[] = { + { + .type = IIO_LIGHT, + .channel = CH_ALS, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_INTENSITY, + .channel = CH_WHITE, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BOTH, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), }, }; @@ -371,6 +449,21 @@ static int veml6030_write_persistence(struct iio_dev *indio_dev, return ret; } +/* + * Cache currently set gain & update resolution. For every + * increase in the gain to next level, resolution is halved + * and vice-versa. + */ +static void veml6030_update_gain_res(struct veml6030_data *data, int gain_idx) +{ + if (data->cur_gain < gain_idx) + data->cur_resolution <<= gain_idx - data->cur_gain; + else if (data->cur_gain > gain_idx) + data->cur_resolution >>= data->cur_gain - gain_idx; + + data->cur_gain = gain_idx; +} + static int veml6030_set_als_gain(struct iio_dev *indio_dev, int val, int val2) { @@ -401,19 +494,49 @@ static int veml6030_set_als_gain(struct iio_dev *indio_dev, return ret; } - /* - * Cache currently set gain & update resolution. For every - * increase in the gain to next level, resolution is halved - * and vice-versa. - */ - if (data->cur_gain < gain_idx) - data->cur_resolution <<= gain_idx - data->cur_gain; - else if (data->cur_gain > gain_idx) - data->cur_resolution >>= data->cur_gain - gain_idx; + veml6030_update_gain_res(data, gain_idx); - data->cur_gain = gain_idx; + return 0; +} - return ret; +static int veml6035_set_als_gain(struct iio_dev *indio_dev, int val, int val2) +{ + int ret, new_gain, gain_idx; + struct veml6030_data *data = iio_priv(indio_dev); + + if (val == 0 && val2 == 125000) { + new_gain = VEML6035_SENS; + gain_idx = 5; + } else if (val == 0 && val2 == 250000) { + new_gain = VEML6035_SENS | VEML6035_GAIN; + gain_idx = 4; + } else if (val == 0 && val2 == 500000) { + new_gain = VEML6035_SENS | VEML6035_GAIN | + VEML6035_DG; + gain_idx = 3; + } else if (val == 1 && val2 == 0) { + new_gain = 0x0000; + gain_idx = 2; + } else if (val == 2 && val2 == 0) { + new_gain = VEML6035_GAIN; + gain_idx = 1; + } else if (val == 4 && val2 == 0) { + new_gain = VEML6035_GAIN | VEML6035_DG; + gain_idx = 0; + } else { + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, + VEML6035_GAIN_M, new_gain); + if (ret) { + dev_err(&data->client->dev, "can't set als gain %d\n", ret); + return ret; + } + + veml6030_update_gain_res(data, gain_idx); + + return 0; } static int veml6030_get_als_gain(struct iio_dev *indio_dev, @@ -453,6 +576,52 @@ static int veml6030_get_als_gain(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; } +static int veml6035_get_als_gain(struct iio_dev *indio_dev, int *val, int *val2) +{ + int ret, reg; + struct veml6030_data *data = iio_priv(indio_dev); + + ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); + if (ret) { + dev_err(&data->client->dev, + "can't read als conf register %d\n", ret); + return ret; + } + + switch (FIELD_GET(VEML6035_GAIN_M, reg)) { + case 0: + *val = 1; + *val2 = 0; + break; + case 1: + case 2: + *val = 2; + *val2 = 0; + break; + case 3: + *val = 4; + *val2 = 0; + break; + case 4: + *val = 0; + *val2 = 125000; + break; + case 5: + case 6: + *val = 0; + *val2 = 250000; + break; + case 7: + *val = 0; + *val2 = 500000; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT_PLUS_MICRO; +} + static int veml6030_read_thresh(struct iio_dev *indio_dev, int *val, int *val2, int dir) { @@ -533,48 +702,54 @@ static int veml6030_read_raw(struct iio_dev *indio_dev, dev_err(dev, "can't read white data %d\n", ret); return ret; } - if (mask == IIO_CHAN_INFO_PROCESSED) { - *val = (reg * data->cur_resolution) / 10000; - *val2 = (reg * data->cur_resolution) % 10000; - return IIO_VAL_INT_PLUS_MICRO; - } *val = reg; return IIO_VAL_INT; default: return -EINVAL; } case IIO_CHAN_INFO_INT_TIME: - if (chan->type == IIO_LIGHT) - return veml6030_get_intgrn_tm(indio_dev, val, val2); - return -EINVAL; + return veml6030_get_intgrn_tm(indio_dev, val, val2); case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_LIGHT) - return veml6030_get_als_gain(indio_dev, val, val2); - return -EINVAL; + return data->chip->get_als_gain(indio_dev, val, val2); default: return -EINVAL; } } +static int veml6030_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct veml6030_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *vals = (int *)&veml6030_it_times; + *length = 2 * ARRAY_SIZE(veml6030_it_times); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (int *)*data->chip->scale_vals; + *length = 2 * data->chip->num_scale_vals; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + static int veml6030_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { + struct veml6030_data *data = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_INT_TIME: - switch (chan->type) { - case IIO_LIGHT: - return veml6030_set_intgrn_tm(indio_dev, val, val2); - default: - return -EINVAL; - } + return veml6030_set_intgrn_tm(indio_dev, val, val2); case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_LIGHT: - return veml6030_set_als_gain(indio_dev, val, val2); - default: - return -EINVAL; - } + return data->chip->set_als_gain(indio_dev, val, val2); default: return -EINVAL; } @@ -646,14 +821,11 @@ static int veml6030_read_interrupt_config(struct iio_dev *indio_dev, */ static int veml6030_write_interrupt_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { int ret; struct veml6030_data *data = iio_priv(indio_dev); - if (state < 0 || state > 1) - return -EINVAL; - ret = veml6030_als_shut_down(data); if (ret < 0) { dev_err(&data->client->dev, @@ -673,19 +845,19 @@ static int veml6030_write_interrupt_config(struct iio_dev *indio_dev, static const struct iio_info veml6030_info = { .read_raw = veml6030_read_raw, + .read_avail = veml6030_read_avail, .write_raw = veml6030_write_raw, .read_event_value = veml6030_read_event_val, .write_event_value = veml6030_write_event_val, .read_event_config = veml6030_read_interrupt_config, .write_event_config = veml6030_write_interrupt_config, - .attrs = &veml6030_attr_group, .event_attrs = &veml6030_event_attr_group, }; static const struct iio_info veml6030_info_no_irq = { .read_raw = veml6030_read_raw, + .read_avail = veml6030_read_avail, .write_raw = veml6030_write_raw, - .attrs = &veml6030_attr_group, }; static irqreturn_t veml6030_event_handler(int irq, void *private) @@ -717,65 +889,82 @@ static irqreturn_t veml6030_event_handler(int irq, void *private) return IRQ_HANDLED; } +static int veml6030_set_info(struct iio_dev *indio_dev) +{ + struct veml6030_data *data = iio_priv(indio_dev); + struct i2c_client *client = data->client; + int ret; + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, veml6030_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "irq %d request failed\n", + client->irq); + + indio_dev->info = &veml6030_info; + } else { + indio_dev->info = &veml6030_info_no_irq; + } + + return 0; +} + +static int veml7700_set_info(struct iio_dev *indio_dev) +{ + indio_dev->info = &veml6030_info_no_irq; + + return 0; +} + /* * Set ALS gain to 1/8, integration time to 100 ms, PSM to mode 2, * persistence to 1 x integration time and the threshold * interrupt disabled by default. First shutdown the sensor, * update registers and then power on the sensor. */ -static int veml6030_hw_init(struct iio_dev *indio_dev) +static int veml6030_hw_init(struct iio_dev *indio_dev, struct device *dev) { int ret, val; struct veml6030_data *data = iio_priv(indio_dev); - struct i2c_client *client = data->client; ret = veml6030_als_shut_down(data); - if (ret) { - dev_err(&client->dev, "can't shutdown als %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't shutdown als\n"); ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, 0x1001); - if (ret) { - dev_err(&client->dev, "can't setup als configs %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't setup als configs\n"); ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM, VEML6030_PSM | VEML6030_PSM_EN, 0x03); - if (ret) { - dev_err(&client->dev, "can't setup default PSM %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't setup default PSM\n"); ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF); - if (ret) { - dev_err(&client->dev, "can't setup high threshold %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't setup high threshold\n"); ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000); - if (ret) { - dev_err(&client->dev, "can't setup low threshold %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't setup low threshold\n"); ret = veml6030_als_pwr_on(data); - if (ret) { - dev_err(&client->dev, "can't poweron als %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't poweron als\n"); - /* Wait 4 ms to let processor & oscillator start correctly */ - usleep_range(4000, 4002); + ret = devm_add_action_or_reset(dev, veml6030_als_shut_down_action, data); + if (ret < 0) + return ret; /* Clear stale interrupt status bits if any during start */ ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val); - if (ret < 0) { - dev_err(&client->dev, - "can't clear als interrupt status %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, + "can't clear als interrupt status\n"); /* Cache currently active measurement parameters */ data->cur_gain = 3; @@ -785,6 +974,62 @@ static int veml6030_hw_init(struct iio_dev *indio_dev) return ret; } +/* + * Set ALS gain to 1/8, integration time to 100 ms, ALS and WHITE + * channel enabled, ALS channel interrupt, PSM enabled, + * PSM_WAIT = 0.8 s, persistence to 1 x integration time and the + * threshold interrupt disabled by default. First shutdown the sensor, + * update registers and then power on the sensor. + */ +static int veml6035_hw_init(struct iio_dev *indio_dev, struct device *dev) +{ + int ret, val; + struct veml6030_data *data = iio_priv(indio_dev); + + ret = veml6030_als_shut_down(data); + if (ret) + return dev_err_probe(dev, ret, "can't shutdown als\n"); + + ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, + VEML6035_SENS | VEML6035_CHAN_EN | VEML6030_ALS_SD); + if (ret) + return dev_err_probe(dev, ret, "can't setup als configs\n"); + + ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM, + VEML6030_PSM | VEML6030_PSM_EN, 0x03); + if (ret) + return dev_err_probe(dev, ret, "can't setup default PSM\n"); + + ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF); + if (ret) + return dev_err_probe(dev, ret, "can't setup high threshold\n"); + + ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000); + if (ret) + return dev_err_probe(dev, ret, "can't setup low threshold\n"); + + ret = veml6030_als_pwr_on(data); + if (ret) + return dev_err_probe(dev, ret, "can't poweron als\n"); + + ret = devm_add_action_or_reset(dev, veml6030_als_shut_down_action, data); + if (ret < 0) + return ret; + + /* Clear stale interrupt status bits if any during start */ + ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val); + if (ret < 0) + return dev_err_probe(dev, ret, + "can't clear als interrupt status\n"); + + /* Cache currently active measurement parameters */ + data->cur_gain = 5; + data->cur_resolution = 1024; + data->cur_integration_time = 3; + + return 0; +} + static int veml6030_probe(struct i2c_client *client) { int ret; @@ -792,16 +1037,14 @@ static int veml6030_probe(struct i2c_client *client) struct iio_dev *indio_dev; struct regmap *regmap; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "i2c adapter doesn't support plain i2c\n"); - return -EOPNOTSUPP; - } + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return dev_err_probe(&client->dev, -EOPNOTSUPP, + "i2c adapter doesn't support plain i2c\n"); regmap = devm_regmap_init_i2c(client, &veml6030_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "can't setup regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "can't setup regmap\n"); indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -812,32 +1055,25 @@ static int veml6030_probe(struct i2c_client *client) data->client = client; data->regmap = regmap; - indio_dev->name = "veml6030"; - indio_dev->channels = veml6030_channels; - indio_dev->num_channels = ARRAY_SIZE(veml6030_channels); - indio_dev->modes = INDIO_DIRECT_MODE; + ret = devm_regulator_get_enable(&client->dev, "vdd"); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to enable regulator\n"); - if (client->irq) { - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, veml6030_event_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "veml6030", indio_dev); - if (ret < 0) { - dev_err(&client->dev, - "irq %d request failed\n", client->irq); - return ret; - } - indio_dev->info = &veml6030_info; - } else { - indio_dev->info = &veml6030_info_no_irq; - } + data->chip = i2c_get_match_data(client); + if (!data->chip) + return -EINVAL; + + indio_dev->name = data->chip->name; + indio_dev->channels = data->chip->channels; + indio_dev->num_channels = data->chip->num_channels; + indio_dev->modes = INDIO_DIRECT_MODE; - ret = veml6030_hw_init(indio_dev); + ret = data->chip->set_info(indio_dev); if (ret < 0) return ret; - ret = devm_add_action_or_reset(&client->dev, - veml6030_als_shut_down_action, data); + ret = data->chip->hw_init(indio_dev, &client->dev); if (ret < 0) return ret; @@ -873,14 +1109,63 @@ static int veml6030_runtime_resume(struct device *dev) static DEFINE_RUNTIME_DEV_PM_OPS(veml6030_pm_ops, veml6030_runtime_suspend, veml6030_runtime_resume, NULL); +static const struct veml603x_chip veml6030_chip = { + .name = "veml6030", + .scale_vals = &veml6030_scale_vals, + .num_scale_vals = ARRAY_SIZE(veml6030_scale_vals), + .channels = veml6030_channels, + .num_channels = ARRAY_SIZE(veml6030_channels), + .hw_init = veml6030_hw_init, + .set_info = veml6030_set_info, + .set_als_gain = veml6030_set_als_gain, + .get_als_gain = veml6030_get_als_gain, +}; + +static const struct veml603x_chip veml6035_chip = { + .name = "veml6035", + .scale_vals = &veml6035_scale_vals, + .num_scale_vals = ARRAY_SIZE(veml6035_scale_vals), + .channels = veml6030_channels, + .num_channels = ARRAY_SIZE(veml6030_channels), + .hw_init = veml6035_hw_init, + .set_info = veml6030_set_info, + .set_als_gain = veml6035_set_als_gain, + .get_als_gain = veml6035_get_als_gain, +}; + +static const struct veml603x_chip veml7700_chip = { + .name = "veml7700", + .scale_vals = &veml6030_scale_vals, + .num_scale_vals = ARRAY_SIZE(veml6030_scale_vals), + .channels = veml7700_channels, + .num_channels = ARRAY_SIZE(veml7700_channels), + .hw_init = veml6030_hw_init, + .set_info = veml7700_set_info, + .set_als_gain = veml6030_set_als_gain, + .get_als_gain = veml6030_get_als_gain, +}; + static const struct of_device_id veml6030_of_match[] = { - { .compatible = "vishay,veml6030" }, + { + .compatible = "vishay,veml6030", + .data = &veml6030_chip, + }, + { + .compatible = "vishay,veml6035", + .data = &veml6035_chip, + }, + { + .compatible = "vishay,veml7700", + .data = &veml7700_chip, + }, { } }; MODULE_DEVICE_TABLE(of, veml6030_of_match); static const struct i2c_device_id veml6030_id[] = { - { "veml6030" }, + { "veml6030", (kernel_ulong_t)&veml6030_chip}, + { "veml6035", (kernel_ulong_t)&veml6035_chip}, + { "veml7700", (kernel_ulong_t)&veml7700_chip}, { } }; MODULE_DEVICE_TABLE(i2c, veml6030_id); diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index f8321d346d775..6d4483c85f30c 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -6,14 +6,16 @@ * * IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39) * - * TODO: integration time, ACK signal + * TODO: ACK signal */ +#include #include #include #include #include #include +#include #include #include @@ -28,50 +30,113 @@ #define VEML6070_COMMAND_RSRVD BIT(1) /* reserved, set to 1 */ #define VEML6070_COMMAND_SD BIT(0) /* shutdown mode when set */ -#define VEML6070_IT_10 0x04 /* integration time 1x */ +#define VEML6070_IT_05 0x00 +#define VEML6070_IT_10 0x01 +#define VEML6070_IT_20 0x02 +#define VEML6070_IT_40 0x03 + +#define VEML6070_MIN_RSET_KOHM 75 +#define VEML6070_MIN_IT_US 15625 /* Rset = 75 kohm, IT = 1/2 */ struct veml6070_data { struct i2c_client *client1; struct i2c_client *client2; u8 config; struct mutex lock; + u32 rset; + int it[4][2]; }; +static int veml6070_calc_it(struct device *dev, struct veml6070_data *data) +{ + int i, tmp_it; + + data->rset = 270000; + device_property_read_u32(dev, "vishay,rset-ohms", &data->rset); + + if (data->rset < 75000 || data->rset > 1200000) + return dev_err_probe(dev, -EINVAL, "Rset out of range\n"); + + /* + * convert to kohm to avoid overflows and work with the same units as + * in the datasheet and simplify UVI operations. + */ + data->rset /= KILO; + + tmp_it = VEML6070_MIN_IT_US * data->rset / VEML6070_MIN_RSET_KOHM; + for (i = 0; i < ARRAY_SIZE(data->it); i++) { + data->it[i][0] = (tmp_it << i) / MICRO; + data->it[i][1] = (tmp_it << i) % MICRO; + } + + return 0; +} + +static int veml6070_get_it(struct veml6070_data *data, int *val, int *val2) +{ + int it_idx = FIELD_GET(VEML6070_COMMAND_IT, data->config); + + *val = data->it[it_idx][0]; + *val2 = data->it[it_idx][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int veml6070_set_it(struct veml6070_data *data, int val, int val2) +{ + int it_idx; + + for (it_idx = 0; it_idx < ARRAY_SIZE(data->it); it_idx++) { + if (data->it[it_idx][0] == val && data->it[it_idx][1] == val2) + break; + } + + if (it_idx >= ARRAY_SIZE(data->it)) + return -EINVAL; + + data->config = (data->config & ~VEML6070_COMMAND_IT) | + FIELD_PREP(VEML6070_COMMAND_IT, it_idx); + + return i2c_smbus_write_byte(data->client1, data->config); +} + static int veml6070_read(struct veml6070_data *data) { - int ret; + int ret, it_ms, val, val2; u8 msb, lsb; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); /* disable shutdown */ ret = i2c_smbus_write_byte(data->client1, data->config & ~VEML6070_COMMAND_SD); if (ret < 0) - goto out; + return ret; - msleep(125 + 10); /* measurement takes up to 125 ms for IT 1x */ + veml6070_get_it(data, &val, &val2); + it_ms = val * MILLI + val2 / (MICRO / MILLI); + msleep(it_ms + 10); ret = i2c_smbus_read_byte(data->client2); /* read MSB, address 0x39 */ if (ret < 0) - goto out; + return ret; + msb = ret; ret = i2c_smbus_read_byte(data->client1); /* read LSB, address 0x38 */ if (ret < 0) - goto out; + return ret; + lsb = ret; /* shutdown again */ ret = i2c_smbus_write_byte(data->client1, data->config); if (ret < 0) - goto out; + return ret; ret = (msb << 8) | lsb; -out: - mutex_unlock(&data->lock); - return ret; + return 0; } static const struct iio_chan_spec veml6070_channels[] = { @@ -80,26 +145,37 @@ static const struct iio_chan_spec veml6070_channels[] = { .modified = 1, .channel2 = IIO_MOD_LIGHT_UV, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), }, { .type = IIO_UVINDEX, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), } }; -static int veml6070_to_uv_index(unsigned val) +static int veml6070_to_uv_index(struct veml6070_data *data, unsigned int val) { /* * conversion of raw UV intensity values to UV index depends on * integration time (IT) and value of the resistor connected to - * the RSET pin (default: 270 KOhm) + * the RSET pin. */ - unsigned uvi[11] = { + unsigned int uvi[11] = { 187, 373, 560, /* low */ 746, 933, 1120, /* moderate */ 1308, 1494, /* high */ 1681, 1868, 2054}; /* very high */ - int i; + int i, it_idx; + + it_idx = FIELD_GET(VEML6070_COMMAND_IT, data->config); + + if (!it_idx) + val = (val * 270 / data->rset) << 1; + else + val = (val * 270 / data->rset) >> (it_idx - 1); for (i = 0; i < ARRAY_SIZE(uvi); i++) if (val <= uvi[i]) @@ -122,10 +198,44 @@ static int veml6070_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; if (mask == IIO_CHAN_INFO_PROCESSED) - *val = veml6070_to_uv_index(ret); + *val = veml6070_to_uv_index(data, ret); else *val = ret; return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + return veml6070_get_it(data, val, val2); + default: + return -EINVAL; + } +} + +static int veml6070_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct veml6070_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *vals = (int *)data->it; + *length = 2 * ARRAY_SIZE(data->it); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int veml6070_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct veml6070_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return veml6070_set_it(data, val, val2); default: return -EINVAL; } @@ -133,8 +243,17 @@ static int veml6070_read_raw(struct iio_dev *indio_dev, static const struct iio_info veml6070_info = { .read_raw = veml6070_read_raw, + .read_avail = veml6070_read_avail, + .write_raw = veml6070_write_raw, }; +static void veml6070_i2c_unreg(void *p) +{ + struct veml6070_data *data = p; + + i2c_unregister_device(data->client2); +} + static int veml6070_probe(struct i2c_client *client) { struct veml6070_data *data; @@ -156,36 +275,30 @@ static int veml6070_probe(struct i2c_client *client) indio_dev->name = VEML6070_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB); - if (IS_ERR(data->client2)) { - dev_err(&client->dev, "i2c device for second chip address failed\n"); - return PTR_ERR(data->client2); - } - - data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD | - VEML6070_COMMAND_SD; - ret = i2c_smbus_write_byte(data->client1, data->config); + ret = veml6070_calc_it(&client->dev, data); if (ret < 0) - goto fail; + return ret; - ret = iio_device_register(indio_dev); + ret = devm_regulator_get_enable(&client->dev, "vdd"); if (ret < 0) - goto fail; + return ret; - return ret; + data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB); + if (IS_ERR(data->client2)) + return dev_err_probe(&client->dev, PTR_ERR(data->client2), + "i2c device for second chip address failed\n"); -fail: - i2c_unregister_device(data->client2); - return ret; -} + data->config = FIELD_PREP(VEML6070_COMMAND_IT, VEML6070_IT_10) | + VEML6070_COMMAND_RSRVD | VEML6070_COMMAND_SD; + ret = i2c_smbus_write_byte(data->client1, data->config); + if (ret < 0) + return ret; -static void veml6070_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct veml6070_data *data = iio_priv(indio_dev); + ret = devm_add_action_or_reset(&client->dev, veml6070_i2c_unreg, data); + if (ret < 0) + return ret; - iio_device_unregister(indio_dev); - i2c_unregister_device(data->client2); + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id veml6070_id[] = { @@ -194,12 +307,18 @@ static const struct i2c_device_id veml6070_id[] = { }; MODULE_DEVICE_TABLE(i2c, veml6070_id); +static const struct of_device_id veml6070_of_match[] = { + { .compatible = "vishay,veml6070" }, + { } +}; +MODULE_DEVICE_TABLE(of, veml6070_of_match); + static struct i2c_driver veml6070_driver = { .driver = { .name = VEML6070_DRV_NAME, + .of_match_table = veml6070_of_match, }, .probe = veml6070_probe, - .remove = veml6070_remove, .id_table = veml6070_id, }; diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index a1b2b3c0b4c88..6e2183a4243e5 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -25,6 +25,10 @@ #include #include +#include +#include +#include +#include #define VL6180_DRV_NAME "vl6180" @@ -38,7 +42,9 @@ #define VL6180_OUT_OF_RESET 0x016 #define VL6180_HOLD 0x017 #define VL6180_RANGE_START 0x018 +#define VL6180_RANGE_INTER_MEAS_TIME 0x01b #define VL6180_ALS_START 0x038 +#define VL6180_ALS_INTER_MEAS_TIME 0x03e #define VL6180_ALS_GAIN 0x03f #define VL6180_ALS_IT 0x040 @@ -84,8 +90,17 @@ struct vl6180_data { struct i2c_client *client; struct mutex lock; + struct completion completion; + struct iio_trigger *trig; unsigned int als_gain_milli; unsigned int als_it_ms; + unsigned int als_meas_rate; + unsigned int range_meas_rate; + + struct { + u16 chan[2]; + aligned_s64 timestamp; + } scan; }; enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX }; @@ -207,29 +222,40 @@ static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val) static int vl6180_measure(struct vl6180_data *data, int addr) { struct i2c_client *client = data->client; + unsigned long time_left; int tries = 20, ret; u16 value; mutex_lock(&data->lock); + reinit_completion(&data->completion); + /* Start single shot measurement */ ret = vl6180_write_byte(client, vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP); if (ret < 0) goto fail; - while (tries--) { - ret = vl6180_read_byte(client, VL6180_INTR_STATUS); - if (ret < 0) + if (client->irq) { + time_left = wait_for_completion_timeout(&data->completion, HZ / 10); + if (time_left == 0) { + ret = -ETIMEDOUT; goto fail; + } + } else { + while (tries--) { + ret = vl6180_read_byte(client, VL6180_INTR_STATUS); + if (ret < 0) + goto fail; + + if (ret & vl6180_chan_regs_table[addr].drdy_mask) + break; + msleep(20); + } - if (ret & vl6180_chan_regs_table[addr].drdy_mask) - break; - msleep(20); - } - - if (tries < 0) { - ret = -EIO; - goto fail; + if (tries < 0) { + ret = -EIO; + goto fail; + } } /* Read result value from appropriate registers */ @@ -258,20 +284,41 @@ static const struct iio_chan_spec vl6180_channels[] = { { .type = IIO_LIGHT, .address = VL6180_ALS, + .scan_index = VL6180_ALS, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_HARDWAREGAIN), + BIT(IIO_CHAN_INFO_HARDWAREGAIN) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_DISTANCE, .address = VL6180_RANGE, + .scan_index = VL6180_RANGE, + .scan_type = { + .sign = 'u', + .realbits = 8, + .storagebits = 8, + }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_PROXIMITY, .address = VL6180_PROX, + .scan_index = VL6180_PROX, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - } + }, + IIO_CHAN_SOFT_TIMESTAMP(3), }; /* @@ -333,6 +380,18 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_DISTANCE: + *val = data->range_meas_rate; + return IIO_VAL_INT; + case IIO_LIGHT: + *val = data->als_meas_rate; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: return -EINVAL; } @@ -412,11 +471,23 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2) return ret; } +static int vl6180_meas_reg_val_from_mhz(unsigned int mhz) +{ + unsigned int period = DIV_ROUND_CLOSEST(1000 * 1000, mhz); + unsigned int reg_val = 0; + + if (period > 10) + reg_val = period < 2550 ? (DIV_ROUND_CLOSEST(period, 10) - 1) : 254; + + return reg_val; +} + static int vl6180_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct vl6180_data *data = iio_priv(indio_dev); + unsigned int reg_val; switch (mask) { case IIO_CHAN_INFO_INT_TIME: @@ -427,18 +498,126 @@ static int vl6180_write_raw(struct iio_dev *indio_dev, return -EINVAL; return vl6180_set_als_gain(data, val, val2); + + case IIO_CHAN_INFO_SAMP_FREQ: + { + guard(mutex)(&data->lock); + switch (chan->type) { + case IIO_DISTANCE: + data->range_meas_rate = val; + reg_val = vl6180_meas_reg_val_from_mhz(val); + return vl6180_write_byte(data->client, + VL6180_RANGE_INTER_MEAS_TIME, reg_val); + + case IIO_LIGHT: + data->als_meas_rate = val; + reg_val = vl6180_meas_reg_val_from_mhz(val); + return vl6180_write_byte(data->client, + VL6180_ALS_INTER_MEAS_TIME, reg_val); + + default: + return -EINVAL; + } + } + default: return -EINVAL; } } +static irqreturn_t vl6180_threaded_irq(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct vl6180_data *data = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll_nested(indio_dev->trig); + else + complete(&data->completion); + + return IRQ_HANDLED; +} + +static irqreturn_t vl6180_trigger_handler(int irq, void *priv) +{ + struct iio_poll_func *pf = priv; + struct iio_dev *indio_dev = pf->indio_dev; + struct vl6180_data *data = iio_priv(indio_dev); + s64 time_ns = iio_get_time_ns(indio_dev); + int ret, bit, i = 0; + + iio_for_each_active_channel(indio_dev, bit) { + if (vl6180_chan_regs_table[bit].word) + ret = vl6180_read_word(data->client, + vl6180_chan_regs_table[bit].value_reg); + else + ret = vl6180_read_byte(data->client, + vl6180_chan_regs_table[bit].value_reg); + + if (ret < 0) { + dev_err(&data->client->dev, + "failed to read from value regs: %d\n", ret); + return IRQ_HANDLED; + } + + data->scan.chan[i++] = ret; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns); + iio_trigger_notify_done(indio_dev->trig); + + /* Clear the interrupt flag after data read */ + ret = vl6180_write_byte(data->client, VL6180_INTR_CLEAR, + VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE); + if (ret < 0) + dev_err(&data->client->dev, "failed to clear irq: %d\n", ret); + + return IRQ_HANDLED; +} + static const struct iio_info vl6180_info = { .read_raw = vl6180_read_raw, .write_raw = vl6180_write_raw, .attrs = &vl6180_attribute_group, + .validate_trigger = iio_validate_own_trigger, +}; + +static int vl6180_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vl6180_data *data = iio_priv(indio_dev); + int bit; + + iio_for_each_active_channel(indio_dev, bit) + return vl6180_write_byte(data->client, + vl6180_chan_regs_table[bit].start_reg, + VL6180_MODE_CONT | VL6180_STARTSTOP); + + return -EINVAL; +} + +static int vl6180_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct vl6180_data *data = iio_priv(indio_dev); + int bit; + + iio_for_each_active_channel(indio_dev, bit) + return vl6180_write_byte(data->client, + vl6180_chan_regs_table[bit].start_reg, + VL6180_STARTSTOP); + + return -EINVAL; +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &vl6180_buffer_postenable, + .postdisable = &vl6180_buffer_postdisable, +}; + +static const struct iio_trigger_ops vl6180_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, }; -static int vl6180_init(struct vl6180_data *data) +static int vl6180_init(struct vl6180_data *data, struct iio_dev *indio_dev) { struct i2c_client *client = data->client; int ret; @@ -473,6 +652,26 @@ static int vl6180_init(struct vl6180_data *data) if (ret < 0) return ret; + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + &vl6180_trigger_handler, + &iio_triggered_buffer_setup_ops); + if (ret) + return ret; + + /* Default Range inter-measurement time: 50ms or 20000 mHz */ + ret = vl6180_write_byte(client, VL6180_RANGE_INTER_MEAS_TIME, + vl6180_meas_reg_val_from_mhz(20000)); + if (ret < 0) + return ret; + data->range_meas_rate = 20000; + + /* Default ALS inter-measurement time: 10ms or 100000 mHz */ + ret = vl6180_write_byte(client, VL6180_ALS_INTER_MEAS_TIME, + vl6180_meas_reg_val_from_mhz(100000)); + if (ret < 0) + return ret; + data->als_meas_rate = 100000; + /* ALS integration time: 100ms */ data->als_it_ms = 100; ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100); @@ -513,10 +712,34 @@ static int vl6180_probe(struct i2c_client *client) indio_dev->name = VL6180_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - ret = vl6180_init(data); + ret = vl6180_init(data, indio_dev); if (ret < 0) return ret; + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, vl6180_threaded_irq, + IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return dev_err_probe(&client->dev, ret, "devm_request_irq error \n"); + + init_completion(&data->completion); + + data->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &vl6180_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + ret = devm_iio_trigger_register(&client->dev, data->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(data->trig); + } + return devm_iio_device_register(&client->dev, indio_dev); } diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index f69ac75500f99..7177cd1d67cb2 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -54,6 +54,19 @@ config AK09911 help Deprecated: AK09911 is now supported by AK8975 driver. +config ALS31300 + tristate "Allegro MicroSystems ALS31300 3-D Linear Hall Effect Sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the Allegro MicroSystems + ALS31300 Hall Effect Sensor through its I2C interface. + + To compile this driver as a module, choose M here: the + module will be called als31300. + config BMC150_MAGN tristate select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index ec5c46fbf999b..3e4c2ecd9adf8 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AF8133J) += af8133j.o obj-$(CONFIG_AK8974) += ak8974.o obj-$(CONFIG_AK8975) += ak8975.o +obj-$(CONFIG_ALS31300) += als31300.o obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 961b1e0bfb138..8306a18706acc 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -910,7 +910,7 @@ static int ak8974_probe(struct i2c_client *i2c) /* If we have a valid DRDY IRQ, make use of it */ if (irq > 0) { - irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + irq_trig = irq_get_trigger_type(irq); if (irq_trig == IRQF_TRIGGER_RISING) { dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n"); } else if (irq_trig == IRQF_TRIGGER_FALLING) { diff --git a/drivers/iio/magnetometer/als31300.c b/drivers/iio/magnetometer/als31300.c new file mode 100644 index 0000000000000..87b60c4e81fa1 --- /dev/null +++ b/drivers/iio/magnetometer/als31300.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Allegro MicroSystems ALS31300 3-D Linear Hall Effect Sensor + * + * Copyright (c) 2024 Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * The Allegro MicroSystems ALS31300 has an EEPROM space to configure how + * the device works and how the interrupt line behaves. + * Only the default setup with external trigger is supported. + * + * While the bindings supports declaring an interrupt line, those + * events are not supported. + * + * It should be possible to adapt the driver to the current + * device EEPROM setup at runtime. + */ + +#define ALS31300_EEPROM_CONFIG 0x02 +#define ALS31300_EEPROM_INTERRUPT 0x03 +#define ALS31300_EEPROM_CUSTOMER_1 0x0d +#define ALS31300_EEPROM_CUSTOMER_2 0x0e +#define ALS31300_EEPROM_CUSTOMER_3 0x0f +#define ALS31300_VOL_MODE 0x27 +#define ALS31300_VOL_MODE_LPDCM GENMASK(6, 4) +#define ALS31300_LPDCM_INACTIVE_0_5_MS 0 +#define ALS31300_LPDCM_INACTIVE_1_0_MS 1 +#define ALS31300_LPDCM_INACTIVE_5_0_MS 2 +#define ALS31300_LPDCM_INACTIVE_10_0_MS 3 +#define ALS31300_LPDCM_INACTIVE_50_0_MS 4 +#define ALS31300_LPDCM_INACTIVE_100_0_MS 5 +#define ALS31300_LPDCM_INACTIVE_500_0_MS 6 +#define ALS31300_LPDCM_INACTIVE_1000_0_MS 7 +#define ALS31300_VOL_MODE_SLEEP GENMASK(1, 0) +#define ALS31300_VOL_MODE_ACTIVE_MODE 0 +#define ALS31300_VOL_MODE_SLEEP_MODE 1 +#define ALS31300_VOL_MODE_LPDCM_MODE 2 +#define ALS31300_VOL_MSB 0x28 +#define ALS31300_VOL_MSB_TEMPERATURE GENMASK(5, 0) +#define ALS31300_VOL_MSB_INTERRUPT BIT(6) +#define ALS31300_VOL_MSB_NEW_DATA BIT(7) +#define ALS31300_VOL_MSB_Z_AXIS GENMASK(15, 8) +#define ALS31300_VOL_MSB_Y_AXIS GENMASK(23, 16) +#define ALS31300_VOL_MSB_X_AXIS GENMASK(31, 24) +#define ALS31300_VOL_LSB 0x29 +#define ALS31300_VOL_LSB_TEMPERATURE GENMASK(5, 0) +#define ALS31300_VOL_LSB_HALL_STATUS GENMASK(7, 7) +#define ALS31300_VOL_LSB_Z_AXIS GENMASK(11, 8) +#define ALS31300_VOL_LSB_Y_AXIS GENMASK(15, 12) +#define ALS31300_VOL_LSB_X_AXIS GENMASK(19, 16) +#define ALS31300_VOL_LSB_INTERRUPT_WRITE BIT(20) +#define ALS31300_CUSTOMER_ACCESS 0x35 + +#define ALS31300_DATA_X_GET(b) \ + sign_extend32(FIELD_GET(ALS31300_VOL_MSB_X_AXIS, b[0]) << 4 | \ + FIELD_GET(ALS31300_VOL_LSB_X_AXIS, b[1]), 11) +#define ALS31300_DATA_Y_GET(b) \ + sign_extend32(FIELD_GET(ALS31300_VOL_MSB_Y_AXIS, b[0]) << 4 | \ + FIELD_GET(ALS31300_VOL_LSB_Y_AXIS, b[1]), 11) +#define ALS31300_DATA_Z_GET(b) \ + sign_extend32(FIELD_GET(ALS31300_VOL_MSB_Z_AXIS, b[0]) << 4 | \ + FIELD_GET(ALS31300_VOL_LSB_Z_AXIS, b[1]), 11) +#define ALS31300_TEMPERATURE_GET(b) \ + (FIELD_GET(ALS31300_VOL_MSB_TEMPERATURE, b[0]) << 6 | \ + FIELD_GET(ALS31300_VOL_LSB_TEMPERATURE, b[1])) + +enum als31300_channels { + TEMPERATURE = 0, + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +struct als31300_variant_info { + u8 sensitivity; +}; + +struct als31300_data { + struct device *dev; + /* protects power on/off the device and access HW */ + struct mutex mutex; + const struct als31300_variant_info *variant_info; + struct regmap *map; +}; + +/* The whole measure is split into 2x32-bit registers, we need to read them both at once */ +static int als31300_get_measure(struct als31300_data *data, + u16 *t, s16 *x, s16 *y, s16 *z) +{ + u32 buf[2]; + int ret, err; + + guard(mutex)(&data->mutex); + + ret = pm_runtime_resume_and_get(data->dev); + if (ret) + return ret; + + /* + * Loop until data is valid, new data should have the + * ALS31300_VOL_MSB_NEW_DATA bit set to 1. + * Max update rate is 2KHz, wait up to 1ms. + */ + ret = read_poll_timeout(regmap_bulk_read, err, + err || FIELD_GET(ALS31300_VOL_MSB_NEW_DATA, buf[0]), + 20, USEC_PER_MSEC, false, + data->map, ALS31300_VOL_MSB, buf, ARRAY_SIZE(buf)); + /* Bail out on read_poll_timeout() error */ + if (ret) + goto out; + + /* Bail out on regmap_bulk_read() error */ + if (err) { + dev_err(data->dev, "read data failed, error %d\n", ret); + ret = err; + goto out; + } + + *t = ALS31300_TEMPERATURE_GET(buf); + *x = ALS31300_DATA_X_GET(buf); + *y = ALS31300_DATA_Y_GET(buf); + *z = ALS31300_DATA_Z_GET(buf); + +out: + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + return ret; +} + +static int als31300_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + struct als31300_data *data = iio_priv(indio_dev); + s16 x, y, z; + u16 t; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + case IIO_CHAN_INFO_RAW: + ret = als31300_get_measure(data, &t, &x, &y, &z); + if (ret) + return ret; + + switch (chan->address) { + case TEMPERATURE: + *val = t; + return IIO_VAL_INT; + case AXIS_X: + *val = x; + return IIO_VAL_INT; + case AXIS_Y: + *val = y; + return IIO_VAL_INT; + case AXIS_Z: + *val = z; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* + * Fractional part of: + * 1000 * 302 * (value - 1708) + * temp = ---------------------------- + * 4096 + * to convert temperature in millicelcius. + */ + *val = MILLI * 302; + *val2 = 4096; + return IIO_VAL_FRACTIONAL; + case IIO_MAGN: + /* + * Devices are configured in factory + * with different sensitivities: + * - 500 GAUSS <-> 4 LSB/Gauss + * - 1000 GAUSS <-> 2 LSB/Gauss + * - 2000 GAUSS <-> 1 LSB/Gauss + * with translates by a division of the returned + * value to get Gauss value. + * The sensitivity cannot be read at runtime + * so the value depends on the model compatible + * or device id. + */ + *val = 1; + *val2 = data->variant_info->sensitivity; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = -1708; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static irqreturn_t als31300_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct als31300_data *data = iio_priv(indio_dev); + struct { + u16 temperature; + s16 channels[3]; + aligned_s64 timestamp; + } scan; + s16 x, y, z; + int ret; + u16 t; + + ret = als31300_get_measure(data, &t, &x, &y, &z); + if (ret) + goto trigger_out; + + scan.temperature = t; + scan.channels[0] = x; + scan.channels[1] = y; + scan.channels[2] = z; + iio_push_to_buffers_with_timestamp(indio_dev, &scan, + pf->timestamp); + +trigger_out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +#define ALS31300_AXIS_CHANNEL(axis, index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + } + +static const struct iio_chan_spec als31300_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = TEMPERATURE, + .scan_index = TEMPERATURE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + ALS31300_AXIS_CHANNEL(X, AXIS_X), + ALS31300_AXIS_CHANNEL(Y, AXIS_Y), + ALS31300_AXIS_CHANNEL(Z, AXIS_Z), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_info als31300_info = { + .read_raw = als31300_read_raw, +}; + +static int als31300_set_operating_mode(struct als31300_data *data, + unsigned int val) +{ + int ret; + + ret = regmap_update_bits(data->map, ALS31300_VOL_MODE, + ALS31300_VOL_MODE_SLEEP, val); + if (ret) { + dev_err(data->dev, "failed to set operating mode (%pe)\n", ERR_PTR(ret)); + return ret; + } + + /* The time it takes to exit sleep mode is equivalent to Power-On Delay Time */ + if (val == ALS31300_VOL_MODE_ACTIVE_MODE) + fsleep(600); + + return 0; +} + +static void als31300_power_down(void *data) +{ + als31300_set_operating_mode(data, ALS31300_VOL_MODE_SLEEP_MODE); +} + +static const struct iio_buffer_setup_ops als31300_setup_ops = {}; + +static const unsigned long als31300_scan_masks[] = { GENMASK(3, 0), 0 }; + +static bool als31300_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == ALS31300_VOL_MSB || reg == ALS31300_VOL_LSB; +} + +static const struct regmap_config als31300_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = ALS31300_CUSTOMER_ACCESS, + .volatile_reg = als31300_volatile_reg, +}; + +static int als31300_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct als31300_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->dev = dev; + i2c_set_clientdata(i2c, indio_dev); + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + data->variant_info = i2c_get_match_data(i2c); + if (!data->variant_info) + return -EINVAL; + + data->map = devm_regmap_init_i2c(i2c, &als31300_regmap_config); + if (IS_ERR(data->map)) + return dev_err_probe(dev, PTR_ERR(data->map), + "failed to allocate register map\n"); + + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable regulator\n"); + + ret = als31300_set_operating_mode(data, ALS31300_VOL_MODE_ACTIVE_MODE); + if (ret) + return dev_err_probe(dev, ret, "failed to power on device\n"); + + ret = devm_add_action_or_reset(dev, als31300_power_down, data); + if (ret) + return dev_err_probe(dev, ret, "failed to add powerdown action\n"); + + indio_dev->info = &als31300_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = i2c->name; + indio_dev->channels = als31300_channels; + indio_dev->num_channels = ARRAY_SIZE(als31300_channels); + indio_dev->available_scan_masks = als31300_scan_masks; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + als31300_trigger_handler, + &als31300_setup_ops); + if (ret < 0) + return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n"); + + ret = pm_runtime_set_active(dev); + if (ret < 0) + return ret; + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + pm_runtime_set_autosuspend_delay(dev, 200); + pm_runtime_use_autosuspend(dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "device register failed\n"); + + return 0; +} + +static int als31300_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct als31300_data *data = iio_priv(indio_dev); + + return als31300_set_operating_mode(data, ALS31300_VOL_MODE_SLEEP_MODE); +} + +static int als31300_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct als31300_data *data = iio_priv(indio_dev); + + return als31300_set_operating_mode(data, ALS31300_VOL_MODE_ACTIVE_MODE); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(als31300_pm_ops, + als31300_runtime_suspend, als31300_runtime_resume, + NULL); + +static const struct als31300_variant_info al31300_variant_500 = { + .sensitivity = 4, +}; + +static const struct als31300_variant_info al31300_variant_1000 = { + .sensitivity = 2, +}; + +static const struct als31300_variant_info al31300_variant_2000 = { + .sensitivity = 1, +}; + +static const struct i2c_device_id als31300_id[] = { + { + .name = "als31300-500", + .driver_data = (kernel_ulong_t)&al31300_variant_500, + }, + { + .name = "als31300-1000", + .driver_data = (kernel_ulong_t)&al31300_variant_1000, + }, + { + .name = "als31300-2000", + .driver_data = (kernel_ulong_t)&al31300_variant_2000, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, als31300_id); + +static const struct of_device_id als31300_of_match[] = { + { + .compatible = "allegromicro,als31300-500", + .data = &al31300_variant_500, + }, + { + .compatible = "allegromicro,als31300-1000", + .data = &al31300_variant_1000, + }, + { + .compatible = "allegromicro,als31300-2000", + .data = &al31300_variant_2000, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, als31300_of_match); + +static struct i2c_driver als31300_driver = { + .driver = { + .name = "als31300", + .of_match_table = als31300_of_match, + .pm = pm_ptr(&als31300_pm_ops), + }, + .probe = als31300_probe, + .id_table = als31300_id, +}; +module_i2c_driver(als31300_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ALS31300 3-D Linear Hall Effect Driver"); +MODULE_AUTHOR("Neil Armstrong "); diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 06d5a1ef1fbdf..7de18c4a0ccb3 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -855,17 +854,6 @@ static const struct iio_buffer_setup_ops bmc150_magn_buffer_setup_ops = { .postdisable = bmc150_magn_buffer_postdisable, }; -static const char *bmc150_magn_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - return dev_name(dev); -} - int bmc150_magn_probe(struct device *dev, struct regmap *regmap, int irq, const char *name) { @@ -894,9 +882,6 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; - if (!name && ACPI_HANDLE(dev)) - name = bmc150_magn_match_acpi_device(dev); - mutex_init(&data->mutex); ret = bmc150_magn_init(data); diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c index a28d46d59875f..17e10a462ac85 100644 --- a/drivers/iio/magnetometer/bmc150_magn_i2c.c +++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c @@ -38,14 +38,6 @@ static void bmc150_magn_i2c_remove(struct i2c_client *client) bmc150_magn_remove(&client->dev); } -static const struct acpi_device_id bmc150_magn_acpi_match[] = { - {"BMC150B", 0}, - {"BMC156B", 0}, - {"BMM150B", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); - static const struct i2c_device_id bmc150_magn_i2c_id[] = { { "bmc150_magn" }, { "bmc156_magn" }, @@ -67,7 +59,6 @@ static struct i2c_driver bmc150_magn_driver = { .driver = { .name = "bmc150_magn_i2c", .of_match_table = bmc150_magn_of_match, - .acpi_match_table = bmc150_magn_acpi_match, .pm = &bmc150_magn_pm_ops, }, .probe = bmc150_magn_i2c_probe, diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index abc75a05c46af..c850de1bc79b0 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -41,20 +41,11 @@ static const struct spi_device_id bmc150_magn_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); -static const struct acpi_device_id bmc150_magn_acpi_match[] = { - {"BMC150B", 0}, - {"BMC156B", 0}, - {"BMM150B", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); - static struct spi_driver bmc150_magn_spi_driver = { .probe = bmc150_magn_spi_probe, .remove = bmc150_magn_spi_remove, .id_table = bmc150_magn_spi_id, .driver = { - .acpi_match_table = bmc150_magn_acpi_match, .name = "bmc150_magn_spi", }, }; diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 5c795a430d09f..1d6fcbbae1c5d 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -466,11 +466,11 @@ static int magn_3d_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_magn_3d_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret = 0; static char *name = "magn_3d"; struct iio_dev *indio_dev; struct magn_3d_state *magn_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_chan_spec *channels; int chan_count = 0; @@ -549,7 +549,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_magn_3d_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct magn_3d_state *magn_state = iio_priv(indio_dev); @@ -574,7 +574,7 @@ static struct platform_driver hid_magn_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_magn_3d_probe, - .remove_new = hid_magn_3d_remove, + .remove = hid_magn_3d_remove, }; module_platform_driver(hid_magn_3d_platform_driver); diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 8943d5c78bc07..c74b92d53d4d1 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -29,7 +29,7 @@ struct incl_3d_state { struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX]; struct { u32 incl_val[INCLI_3D_CHANNEL_MAX]; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -299,11 +299,11 @@ static int incl_3d_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_incl_3d_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret; static char *name = "incli_3d"; struct iio_dev *indio_dev; struct incl_3d_state *incl_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct incl_3d_state)); @@ -385,7 +385,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_incl_3d_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct incl_3d_state *incl_state = iio_priv(indio_dev); @@ -410,7 +410,7 @@ static struct platform_driver hid_incl_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_incl_3d_probe, - .remove_new = hid_incl_3d_remove, + .remove = hid_incl_3d_remove, }; module_platform_driver(hid_incl_3d_platform_driver); diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 5e8cadd5177ae..343be43163e4c 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -20,7 +20,7 @@ struct dev_rot_state { struct hid_sensor_hub_attribute_info quaternion; struct { s32 sampled_vals[4] __aligned(16); - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -230,11 +230,11 @@ static int dev_rot_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_dev_rot_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret; char *name; struct iio_dev *indio_dev; struct dev_rot_state *rot_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct dev_rot_state)); @@ -329,7 +329,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_dev_rot_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct dev_rot_state *rot_state = iio_priv(indio_dev); @@ -362,7 +362,7 @@ static struct platform_driver hid_dev_rot_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_dev_rot_probe, - .remove_new = hid_dev_rot_remove, + .remove = hid_dev_rot_remove, }; module_platform_driver(hid_dev_rot_platform_driver); diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 76e173850a354..3a6c7e50cc70b 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -39,7 +39,7 @@ struct hinge_state { const char *labels[CHANNEL_SCAN_INDEX_MAX]; struct { u32 hinge_val[3]; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; @@ -263,9 +263,9 @@ static int hinge_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_hinge_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct hinge_state *st; struct iio_dev *indio_dev; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; int ret; int i; @@ -344,7 +344,7 @@ static int hid_hinge_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_hinge_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hinge_state *st = iio_priv(indio_dev); @@ -369,7 +369,7 @@ static struct platform_driver hid_hinge_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_hinge_probe, - .remove_new = hid_hinge_remove, + .remove = hid_hinge_remove, }; module_platform_driver(hid_hinge_platform_driver); diff --git a/drivers/iio/position/iqs624-pos.c b/drivers/iio/position/iqs624-pos.c index 4d7452314209f..8239239c6ee27 100644 --- a/drivers/iio/position/iqs624-pos.c +++ b/drivers/iio/position/iqs624-pos.c @@ -181,7 +181,7 @@ static int iqs624_pos_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev); struct iqs62x_core *iqs62x = iqs624_pos->iqs62x; diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index a8b97b9b04618..e5ec8137961fc 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -16,6 +16,11 @@ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf * + * Sensor API: + * https://github.com/boschsensortec/BME280_SensorAPI + * https://github.com/boschsensortec/BMP3_SensorAPI + * https://github.com/boschsensortec/BMP5_SensorAPI + * * Notice: * The link to the bmp180 datasheet points to an outdated version missing these changes: * - Changed document referral from ANP015 to BST-MPS-AN004-00 on page 26 @@ -37,12 +42,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -340,10 +347,19 @@ static int bmp280_read_calib(struct bmp280_data *data) return 0; } +/* + * These enums are used for indexing into the array of humidity parameters + * for BME280. Due to some weird indexing, unaligned BE/LE accesses co-exist in + * order to prepare the FIELD_{GET/PREP}() fields. Table 16 in Section 4.2.2 of + * the datasheet. + */ +enum { H2 = 0, H3 = 2, H4 = 3, H5 = 4, H6 = 6 }; + static int bme280_read_calib(struct bmp280_data *data) { struct bmp280_calib *calib = &data->calib.bmp280; struct device *dev = data->dev; + s16 h4_upper, h4_lower, tmp_1, tmp_2, tmp_3; unsigned int tmp; int ret; @@ -352,14 +368,6 @@ static int bme280_read_calib(struct bmp280_data *data) if (ret) return ret; - /* - * Read humidity calibration values. - * Due to some odd register addressing we cannot just - * do a big bulk read. Instead, we have to read each Hx - * value separately and sometimes do some bit shifting... - * Humidity data is only available on BME280. - */ - ret = regmap_read(data->regmap, BME280_REG_COMP_H1, &tmp); if (ret) { dev_err(dev, "failed to read H1 comp value\n"); @@ -368,43 +376,23 @@ static int bme280_read_calib(struct bmp280_data *data) calib->H1 = tmp; ret = regmap_bulk_read(data->regmap, BME280_REG_COMP_H2, - &data->le16, sizeof(data->le16)); + data->bme280_humid_cal_buf, + sizeof(data->bme280_humid_cal_buf)); if (ret) { - dev_err(dev, "failed to read H2 comp value\n"); + dev_err(dev, "failed to read humidity calibration values\n"); return ret; } - calib->H2 = sign_extend32(le16_to_cpu(data->le16), 15); - ret = regmap_read(data->regmap, BME280_REG_COMP_H3, &tmp); - if (ret) { - dev_err(dev, "failed to read H3 comp value\n"); - return ret; - } - calib->H3 = tmp; - - ret = regmap_bulk_read(data->regmap, BME280_REG_COMP_H4, - &data->be16, sizeof(data->be16)); - if (ret) { - dev_err(dev, "failed to read H4 comp value\n"); - return ret; - } - calib->H4 = sign_extend32(((be16_to_cpu(data->be16) >> 4) & 0xff0) | - (be16_to_cpu(data->be16) & 0xf), 11); - - ret = regmap_bulk_read(data->regmap, BME280_REG_COMP_H5, - &data->le16, sizeof(data->le16)); - if (ret) { - dev_err(dev, "failed to read H5 comp value\n"); - return ret; - } - calib->H5 = sign_extend32(FIELD_GET(BME280_COMP_H5_MASK, le16_to_cpu(data->le16)), 11); - - ret = regmap_read(data->regmap, BME280_REG_COMP_H6, &tmp); - if (ret) { - dev_err(dev, "failed to read H6 comp value\n"); - return ret; - } - calib->H6 = sign_extend32(tmp, 7); + calib->H2 = get_unaligned_le16(&data->bme280_humid_cal_buf[H2]); + calib->H3 = data->bme280_humid_cal_buf[H3]; + tmp_1 = get_unaligned_be16(&data->bme280_humid_cal_buf[H4]); + tmp_2 = FIELD_GET(BME280_COMP_H4_GET_MASK_UP, tmp_1); + h4_upper = FIELD_PREP(BME280_COMP_H4_PREP_MASK_UP, tmp_2); + h4_lower = FIELD_GET(BME280_COMP_H4_MASK_LOW, tmp_1); + calib->H4 = sign_extend32(h4_upper | h4_lower, 11); + tmp_3 = get_unaligned_le16(&data->bme280_humid_cal_buf[H5]); + calib->H5 = sign_extend32(FIELD_GET(BME280_COMP_H5_MASK, tmp_3), 11); + calib->H6 = data->bme280_humid_cal_buf[H6]; return 0; } @@ -635,6 +623,14 @@ static int bmp280_read_raw_impl(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_PROCESSED: + ret = data->chip_info->set_mode(data, BMP280_FORCED); + if (ret) + return ret; + + ret = data->chip_info->wait_conv(data); + if (ret) + return ret; + switch (chan->type) { case IIO_HUMIDITYRELATIVE: ret = data->chip_info->read_humid(data, &chan_value); @@ -664,6 +660,14 @@ static int bmp280_read_raw_impl(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_RAW: + ret = data->chip_info->set_mode(data, BMP280_FORCED); + if (ret) + return ret; + + ret = data->chip_info->wait_conv(data); + if (ret) + return ret; + switch (chan->type) { case IIO_HUMIDITYRELATIVE: ret = data->chip_info->read_humid(data, &chan_value); @@ -983,6 +987,92 @@ static const unsigned long bme280_avail_scan_masks[] = { 0 }; +static int bmp280_preinit(struct bmp280_data *data) +{ + struct device *dev = data->dev; + unsigned int reg; + int ret; + + ret = regmap_write(data->regmap, BMP280_REG_RESET, BMP280_RST_SOFT_CMD); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset device.\n"); + + /* + * According to the datasheet in Chapter 1: Specification, Table 2, + * after resetting, the device uses the complete power-on sequence so + * it needs to wait for the defined start-up time. + */ + fsleep(data->start_up_time); + + ret = regmap_read(data->regmap, BMP280_REG_STATUS, ®); + if (ret) + return dev_err_probe(dev, ret, "Failed to read status register.\n"); + + if (reg & BMP280_REG_STATUS_IM_UPDATE) + return dev_err_probe(dev, -EIO, "Failed to copy NVM contents.\n"); + + return 0; +} + +static const u8 bmp280_operation_mode[] = { + [BMP280_SLEEP] = BMP280_MODE_SLEEP, + [BMP280_FORCED] = BMP280_MODE_FORCED, + [BMP280_NORMAL] = BMP280_MODE_NORMAL, +}; + +static int bmp280_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS, + BMP280_MODE_MASK, bmp280_operation_mode[mode]); + if (ret) { + dev_err(data->dev, "failed to write ctrl_meas register.\n"); + return ret; + } + + data->op_mode = mode; + + return 0; +} + +static int bmp280_wait_conv(struct bmp280_data *data) +{ + unsigned int reg, meas_time_us; + int ret; + + /* Check if we are using a BME280 device */ + if (data->oversampling_humid) + meas_time_us = BMP280_PRESS_HUMID_MEAS_OFFSET + + BIT(data->oversampling_humid) * BMP280_MEAS_DUR; + + else + meas_time_us = 0; + + /* Pressure measurement time */ + meas_time_us += BMP280_PRESS_HUMID_MEAS_OFFSET + + BIT(data->oversampling_press) * BMP280_MEAS_DUR; + + /* Temperature measurement time */ + meas_time_us += BIT(data->oversampling_temp) * BMP280_MEAS_DUR; + + /* Waiting time according to the BM(P/E)2 Sensor API */ + fsleep(meas_time_us); + + ret = regmap_read(data->regmap, BMP280_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to read status register.\n"); + return ret; + } + + if (reg & BMP280_REG_STATUS_MEAS_BIT) { + dev_err(data->dev, "Measurement cycle didn't complete.\n"); + return -EBUSY; + } + + return 0; +} + static int bmp280_chip_config(struct bmp280_data *data) { u8 osrs = FIELD_PREP(BMP280_OSRS_TEMP_MASK, data->oversampling_temp + 1) | @@ -993,7 +1083,7 @@ static int bmp280_chip_config(struct bmp280_data *data) BMP280_OSRS_TEMP_MASK | BMP280_OSRS_PRESS_MASK | BMP280_MODE_MASK, - osrs | BMP280_MODE_NORMAL); + osrs | BMP280_MODE_SLEEP); if (ret) { dev_err(data->dev, "failed to write ctrl_meas register\n"); return ret; @@ -1015,7 +1105,9 @@ static irqreturn_t bmp280_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - s32 adc_temp, adc_press, t_fine; + u32 adc_temp, adc_press, comp_press; + s32 t_fine, comp_temp; + s32 *chans = (s32 *)data->sensor_data; int ret; guard(mutex)(&data->lock); @@ -1035,7 +1127,7 @@ static irqreturn_t bmp280_trigger_handler(int irq, void *p) goto out; } - data->sensor_data[1] = bmp280_compensate_temp(data, adc_temp); + comp_temp = bmp280_compensate_temp(data, adc_temp); /* Pressure calculations */ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0])); @@ -1045,10 +1137,12 @@ static irqreturn_t bmp280_trigger_handler(int irq, void *p) } t_fine = bmp280_calc_t_fine(data, adc_temp); + comp_press = bmp280_compensate_press(data, adc_press, t_fine); - data->sensor_data[0] = bmp280_compensate_press(data, adc_press, t_fine); + chans[0] = comp_press; + chans[1] = comp_temp; - iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, iio_get_time_ns(indio_dev)); out: @@ -1099,6 +1193,9 @@ const struct bmp280_chip_info bmp280_chip_info = { .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, .read_calib = bmp280_read_calib, + .set_mode = bmp280_set_mode, + .wait_conv = bmp280_wait_conv, + .preinit = bmp280_preinit, .trigger_handler = bmp280_trigger_handler, }; @@ -1128,7 +1225,9 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - s32 adc_temp, adc_press, adc_humidity, t_fine; + u32 adc_temp, adc_press, adc_humidity, comp_press, comp_humidity; + s32 t_fine, comp_temp; + s32 *chans = (s32 *)data->sensor_data; int ret; guard(mutex)(&data->lock); @@ -1148,7 +1247,7 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) goto out; } - data->sensor_data[1] = bmp280_compensate_temp(data, adc_temp); + comp_temp = bmp280_compensate_temp(data, adc_temp); /* Pressure calculations */ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0])); @@ -1158,8 +1257,7 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) } t_fine = bmp280_calc_t_fine(data, adc_temp); - - data->sensor_data[0] = bmp280_compensate_press(data, adc_press, t_fine); + comp_press = bmp280_compensate_press(data, adc_press, t_fine); /* Humidity calculations */ adc_humidity = get_unaligned_be16(&data->buf[6]); @@ -1168,9 +1266,14 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) dev_err(data->dev, "reading humidity skipped\n"); goto out; } - data->sensor_data[2] = bme280_compensate_humidity(data, adc_humidity, t_fine); - iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + comp_humidity = bme280_compensate_humidity(data, adc_humidity, t_fine); + + chans[0] = comp_press; + chans[1] = comp_temp; + chans[2] = comp_humidity; + + iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, iio_get_time_ns(indio_dev)); out: @@ -1179,6 +1282,63 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) return IRQ_HANDLED; } +static int __bmp280_trigger_probe(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trigger_ops, + int (*int_pin_config)(struct bmp280_data *data), + irq_handler_t irq_thread_handler) +{ + struct bmp280_data *data = iio_priv(indio_dev); + struct device *dev = data->dev; + u32 irq_type; + int ret, irq; + + irq = fwnode_irq_get(dev_fwnode(dev), 0); + if (irq < 0) + return dev_err_probe(dev, irq, "No interrupt found.\n"); + + irq_type = irq_get_trigger_type(irq); + switch (irq_type) { + case IRQF_TRIGGER_RISING: + data->trig_active_high = true; + break; + case IRQF_TRIGGER_FALLING: + data->trig_active_high = false; + break; + default: + return dev_err_probe(dev, -EINVAL, "Invalid interrupt type specified.\n"); + } + + data->trig_open_drain = + fwnode_property_read_bool(dev_fwnode(dev), "int-open-drain"); + + ret = int_pin_config(data); + if (ret) + return ret; + + data->trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = trigger_ops; + iio_trigger_set_drvdata(data->trig, data); + + ret = devm_request_threaded_irq(data->dev, irq, NULL, + irq_thread_handler, IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "request IRQ failed.\n"); + + ret = devm_iio_trigger_register(data->dev, data->trig); + if (ret) + return dev_err_probe(dev, ret, "iio trigger register failed.\n"); + + indio_dev->trig = iio_trigger_get(data->trig); + + return 0; +} + static const u8 bme280_chip_ids[] = { BME280_CHIP_ID }; static const int bme280_humid_coeffs[] = { 1000, 1024 }; @@ -1216,6 +1376,9 @@ const struct bmp280_chip_info bme280_chip_info = { .read_press = bmp280_read_press, .read_humid = bme280_read_humid, .read_calib = bme280_read_calib, + .set_mode = bmp280_set_mode, + .wait_conv = bmp280_wait_conv, + .preinit = bmp280_preinit, .trigger_handler = bme280_trigger_handler, }; @@ -1502,6 +1665,64 @@ static int bmp380_preinit(struct bmp280_data *data) return bmp380_cmd(data, BMP380_CMD_SOFT_RESET); } +static const u8 bmp380_operation_mode[] = { + [BMP280_SLEEP] = BMP380_MODE_SLEEP, + [BMP280_FORCED] = BMP380_MODE_FORCED, + [BMP280_NORMAL] = BMP380_MODE_NORMAL, +}; + +static int bmp380_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_MODE_MASK, + FIELD_PREP(BMP380_MODE_MASK, + bmp380_operation_mode[mode])); + if (ret) { + dev_err(data->dev, "failed to write power control register.\n"); + return ret; + } + + data->op_mode = mode; + + return 0; +} + +static int bmp380_wait_conv(struct bmp280_data *data) +{ + unsigned int reg; + int ret, meas_time_us; + + /* Offset measurement time */ + meas_time_us = BMP380_MEAS_OFFSET; + + /* Pressure measurement time */ + meas_time_us += BMP380_PRESS_MEAS_OFFSET + + BIT(data->oversampling_press) * BMP380_MEAS_DUR; + + /* Temperature measurement time */ + meas_time_us += BMP380_TEMP_MEAS_OFFSET + + BIT(data->oversampling_temp) * BMP380_MEAS_DUR; + + /* Measurement time defined in Datasheet Section 3.9.2 */ + fsleep(meas_time_us); + + ret = regmap_read(data->regmap, BMP380_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to read status register.\n"); + return ret; + } + + if (!((reg & BMP380_STATUS_DRDY_PRESS_MASK) && + (reg & BMP380_STATUS_DRDY_TEMP_MASK))) { + dev_err(data->dev, "Measurement cycle didn't complete.\n"); + return -EBUSY; + } + + return 0; +} + static int bmp380_chip_config(struct bmp280_data *data) { bool change = false, aux; @@ -1545,14 +1766,12 @@ static int bmp380_chip_config(struct bmp280_data *data) change = change || aux; /* Set filter data */ - ret = regmap_update_bits_check(data->regmap, BMP380_REG_CONFIG, BMP380_FILTER_MASK, - FIELD_PREP(BMP380_FILTER_MASK, data->iir_filter_coeff), - &aux); + ret = regmap_update_bits(data->regmap, BMP380_REG_CONFIG, BMP380_FILTER_MASK, + FIELD_PREP(BMP380_FILTER_MASK, data->iir_filter_coeff)); if (ret) { dev_err(data->dev, "failed to write config register\n"); return ret; } - change = change || aux; if (change) { /* @@ -1564,17 +1783,19 @@ static int bmp380_chip_config(struct bmp280_data *data) * Resets sensor measurement loop toggling between sleep and * normal operating modes. */ - ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, - BMP380_MODE_MASK, - FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_SLEEP)); + ret = bmp380_set_mode(data, BMP280_SLEEP); if (ret) { dev_err(data->dev, "failed to set sleep mode\n"); return ret; } - usleep_range(2000, 2500); - ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, - BMP380_MODE_MASK, - FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_NORMAL)); + + /* + * According to the BMP3 Sensor API, the sensor needs 5ms + * in order to go to the sleep mode. + */ + fsleep(5 * USEC_PER_MSEC); + + ret = bmp380_set_mode(data, BMP280_NORMAL); if (ret) { dev_err(data->dev, "failed to set normal mode\n"); return ret; @@ -1600,7 +1821,77 @@ static int bmp380_chip_config(struct bmp280_data *data) } } - return 0; + /* Dummy read to empty data registers. */ + ret = bmp380_read_press(data, &tmp); + if (ret) + return ret; + + ret = bmp380_set_mode(data, BMP280_SLEEP); + if (ret) + dev_err(data->dev, "failed to set sleep mode.\n"); + + return ret; +} + +static int bmp380_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct bmp280_data *data = iio_trigger_get_drvdata(trig); + int ret; + + guard(mutex)(&data->lock); + + ret = regmap_update_bits(data->regmap, BMP380_REG_INT_CONTROL, + BMP380_INT_CTRL_DRDY_EN, + FIELD_PREP(BMP380_INT_CTRL_DRDY_EN, !!state)); + if (ret) + dev_err(data->dev, + "Could not %s interrupt.\n", str_enable_disable(state)); + return ret; +} + +static const struct iio_trigger_ops bmp380_trigger_ops = { + .set_trigger_state = &bmp380_data_rdy_trigger_set_state, +}; + +static int bmp380_int_pin_config(struct bmp280_data *data) +{ + int pin_drive_cfg = FIELD_PREP(BMP380_INT_CTRL_OPEN_DRAIN, + data->trig_open_drain); + int pin_level_cfg = FIELD_PREP(BMP380_INT_CTRL_LEVEL, + data->trig_active_high); + int ret, int_pin_cfg = pin_drive_cfg | pin_level_cfg; + + ret = regmap_update_bits(data->regmap, BMP380_REG_INT_CONTROL, + BMP380_INT_CTRL_SETTINGS_MASK, int_pin_cfg); + if (ret) + dev_err(data->dev, "Could not set interrupt settings.\n"); + + return ret; +} + +static irqreturn_t bmp380_irq_thread_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct bmp280_data *data = iio_priv(indio_dev); + unsigned int int_ctrl; + int ret; + + ret = regmap_read(data->regmap, BMP380_REG_INT_STATUS, &int_ctrl); + if (ret) + return IRQ_NONE; + + if (FIELD_GET(BMP380_INT_STATUS_DRDY, int_ctrl)) + iio_trigger_poll_nested(data->trig); + + return IRQ_HANDLED; +} + +static int bmp380_trigger_probe(struct iio_dev *indio_dev) +{ + return __bmp280_trigger_probe(indio_dev, &bmp380_trigger_ops, + bmp380_int_pin_config, + bmp380_irq_thread_handler); } static irqreturn_t bmp380_trigger_handler(int irq, void *p) @@ -1608,7 +1899,9 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - s32 adc_temp, adc_press, t_fine; + u32 adc_temp, adc_press, comp_press; + s32 t_fine, comp_temp; + s32 *chans = (s32 *)data->sensor_data; int ret; guard(mutex)(&data->lock); @@ -1628,7 +1921,7 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) goto out; } - data->sensor_data[1] = bmp380_compensate_temp(data, adc_temp); + comp_temp = bmp380_compensate_temp(data, adc_temp); /* Pressure calculations */ adc_press = get_unaligned_le24(&data->buf[0]); @@ -1638,10 +1931,12 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) } t_fine = bmp380_calc_t_fine(data, adc_temp); + comp_press = bmp380_compensate_press(data, adc_press, t_fine); - data->sensor_data[0] = bmp380_compensate_press(data, adc_press, t_fine); + chans[0] = comp_press; + chans[1] = comp_temp; - iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, iio_get_time_ns(indio_dev)); out: @@ -1692,8 +1987,11 @@ const struct bmp280_chip_info bmp380_chip_info = { .read_temp = bmp380_read_temp, .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, + .set_mode = bmp380_set_mode, + .wait_conv = bmp380_wait_conv, .preinit = bmp380_preinit, + .trigger_probe = bmp380_trigger_probe, .trigger_handler = bmp380_trigger_handler, }; EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); @@ -2079,6 +2377,70 @@ static int bmp580_preinit(struct bmp280_data *data) return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config)); } +static const u8 bmp580_operation_mode[] = { + [BMP280_SLEEP] = BMP580_MODE_SLEEP, + [BMP280_FORCED] = BMP580_MODE_FORCED, + [BMP280_NORMAL] = BMP580_MODE_NORMAL, +}; + +static int bmp580_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode) +{ + struct device *dev = data->dev; + int ret; + + if (mode == BMP280_FORCED) { + ret = regmap_set_bits(data->regmap, BMP580_REG_DSP_CONFIG, + BMP580_DSP_IIR_FORCED_FLUSH); + if (ret) { + dev_err(dev, "Could not flush IIR filter constants.\n"); + return ret; + } + } + + ret = regmap_write_bits(data->regmap, BMP580_REG_ODR_CONFIG, + BMP580_MODE_MASK, + FIELD_PREP(BMP580_MODE_MASK, + bmp580_operation_mode[mode])); + if (ret) { + dev_err(dev, "failed to write power control register.\n"); + return ret; + } + + data->op_mode = mode; + + return 0; +} + +static int bmp580_wait_conv(struct bmp280_data *data) +{ + /* + * Taken from datasheet, Section 2 "Specification, Table 3 "Electrical + * characteristics. + */ + static const int time_conv_press[] = { + 0, 1050, 1785, 3045, 5670, 10920, 21420, 42420, + 84420, + }; + static const int time_conv_temp[] = { + 0, 1050, 1105, 1575, 2205, 3465, 6090, 11340, + 21840, + }; + int meas_time_us; + + meas_time_us = 4 * USEC_PER_MSEC + + time_conv_temp[data->oversampling_temp] + + time_conv_press[data->oversampling_press]; + + /* + * Measurement time mentioned in Chapter 2, Table 4 of the datasheet. + * The extra 4ms is the required mode change to start of measurement + * time. + */ + fsleep(meas_time_us); + + return 0; +} + static int bmp580_chip_config(struct bmp280_data *data) { bool change = false, aux; @@ -2141,26 +2503,13 @@ static int bmp580_chip_config(struct bmp280_data *data) reg_val = FIELD_PREP(BMP580_DSP_IIR_PRESS_MASK, data->iir_filter_coeff) | FIELD_PREP(BMP580_DSP_IIR_TEMP_MASK, data->iir_filter_coeff); - ret = regmap_update_bits_check(data->regmap, BMP580_REG_DSP_IIR, - BMP580_DSP_IIR_PRESS_MASK | - BMP580_DSP_IIR_TEMP_MASK, - reg_val, &aux); + ret = regmap_update_bits(data->regmap, BMP580_REG_DSP_IIR, + BMP580_DSP_IIR_PRESS_MASK | BMP580_DSP_IIR_TEMP_MASK, + reg_val); if (ret) { dev_err(data->dev, "failed to write config register\n"); return ret; } - change = change || aux; - - /* Restore sensor to normal operation mode */ - ret = regmap_write_bits(data->regmap, BMP580_REG_ODR_CONFIG, - BMP580_MODE_MASK, - FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_NORMAL)); - if (ret) { - dev_err(data->dev, "failed to set normal mode\n"); - return ret; - } - /* From datasheet's table 4: electrical characteristics */ - usleep_range(3000, 3500); if (change) { /* @@ -2185,12 +2534,80 @@ static int bmp580_chip_config(struct bmp280_data *data) return 0; } +static int bmp580_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct bmp280_data *data = iio_trigger_get_drvdata(trig); + int ret; + + guard(mutex)(&data->lock); + + ret = regmap_update_bits(data->regmap, BMP580_REG_INT_CONFIG, + BMP580_INT_CONFIG_INT_EN, + FIELD_PREP(BMP580_INT_CONFIG_INT_EN, !!state)); + if (ret) + dev_err(data->dev, + "Could not %s interrupt.\n", str_enable_disable(state)); + return ret; +} + +static const struct iio_trigger_ops bmp580_trigger_ops = { + .set_trigger_state = &bmp580_data_rdy_trigger_set_state, +}; + +static int bmp580_int_pin_config(struct bmp280_data *data) +{ + int pin_drive_cfg = FIELD_PREP(BMP580_INT_CONFIG_OPEN_DRAIN, + data->trig_open_drain); + int pin_level_cfg = FIELD_PREP(BMP580_INT_CONFIG_LEVEL, + data->trig_active_high); + int ret, int_pin_cfg = pin_drive_cfg | pin_level_cfg; + + ret = regmap_update_bits(data->regmap, BMP580_REG_INT_CONFIG, + BMP580_INT_CONFIG_MASK, int_pin_cfg); + if (ret) { + dev_err(data->dev, "Could not set interrupt settings.\n"); + return ret; + } + + ret = regmap_set_bits(data->regmap, BMP580_REG_INT_SOURCE, + BMP580_INT_SOURCE_DRDY); + if (ret) + dev_err(data->dev, "Could not set interrupt source.\n"); + + return ret; +} + +static irqreturn_t bmp580_irq_thread_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct bmp280_data *data = iio_priv(indio_dev); + unsigned int int_ctrl; + int ret; + + ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, &int_ctrl); + if (ret) + return IRQ_NONE; + + if (FIELD_GET(BMP580_INT_STATUS_DRDY_MASK, int_ctrl)) + iio_trigger_poll_nested(data->trig); + + return IRQ_HANDLED; +} + +static int bmp580_trigger_probe(struct iio_dev *indio_dev) +{ + return __bmp280_trigger_probe(indio_dev, &bmp580_trigger_ops, + bmp580_int_pin_config, + bmp580_irq_thread_handler); +} + static irqreturn_t bmp580_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - int ret; + int ret, offset; guard(mutex)(&data->lock); @@ -2202,13 +2619,17 @@ static irqreturn_t bmp580_trigger_handler(int irq, void *p) goto out; } - /* Temperature calculations */ - memcpy(&data->sensor_data[1], &data->buf[0], 3); + offset = 0; /* Pressure calculations */ - memcpy(&data->sensor_data[0], &data->buf[3], 3); + memcpy(&data->sensor_data[offset], &data->buf[3], 3); + + offset += sizeof(s32); - iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + /* Temperature calculations */ + memcpy(&data->sensor_data[offset], &data->buf[0], 3); + + iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, iio_get_time_ns(indio_dev)); out: @@ -2257,8 +2678,11 @@ const struct bmp280_chip_info bmp580_chip_info = { .chip_config = bmp580_chip_config, .read_temp = bmp580_read_temp, .read_press = bmp580_read_press, + .set_mode = bmp580_set_mode, + .wait_conv = bmp580_wait_conv, .preinit = bmp580_preinit, + .trigger_probe = bmp580_trigger_probe, .trigger_handler = bmp580_trigger_handler, }; EXPORT_SYMBOL_NS(bmp580_chip_info, IIO_BMP280); @@ -2504,6 +2928,19 @@ static int bmp180_read_press(struct bmp280_data *data, u32 *comp_press) return 0; } +/* Keep compatibility with newer generations of the sensor */ +static int bmp180_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode) +{ + return 0; +} + +/* Keep compatibility with newer generations of the sensor */ +static int bmp180_wait_conv(struct bmp280_data *data) +{ + return 0; +} + +/* Keep compatibility with newer generations of the sensor */ static int bmp180_chip_config(struct bmp280_data *data) { return 0; @@ -2514,23 +2951,24 @@ static irqreturn_t bmp180_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - int ret, chan_value; + int ret, comp_temp, comp_press; + s32 *chans = (s32 *)data->sensor_data; guard(mutex)(&data->lock); - ret = bmp180_read_temp(data, &chan_value); + ret = bmp180_read_temp(data, &comp_temp); if (ret) goto out; - data->sensor_data[1] = chan_value; - ret = bmp180_read_press(data, &chan_value); + ret = bmp180_read_press(data, &comp_press); if (ret) goto out; - data->sensor_data[0] = chan_value; + chans[0] = comp_press; + chans[1] = comp_temp; - iio_push_to_buffers_with_timestamp(indio_dev, &data->sensor_data, + iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, iio_get_time_ns(indio_dev)); out: @@ -2574,6 +3012,8 @@ const struct bmp280_chip_info bmp180_chip_info = { .read_temp = bmp180_read_temp, .read_press = bmp180_read_press, .read_calib = bmp180_read_calib, + .set_mode = bmp180_set_mode, + .wait_conv = bmp180_wait_conv, .trigger_handler = bmp180_trigger_handler, }; @@ -2588,15 +3028,18 @@ static irqreturn_t bmp085_eoc_irq(int irq, void *d) return IRQ_HANDLED; } -static int bmp085_fetch_eoc_irq(struct device *dev, - const char *name, - int irq, - struct bmp280_data *data) +static int bmp085_trigger_probe(struct iio_dev *indio_dev) { + struct bmp280_data *data = iio_priv(indio_dev); + struct device *dev = data->dev; unsigned long irq_trig; - int ret; + int ret, irq; - irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + irq = fwnode_irq_get(dev_fwnode(dev), 0); + if (irq < 0) + return dev_err_probe(dev, irq, "No interrupt found.\n"); + + irq_trig = irq_get_trigger_type(irq); if (irq_trig != IRQF_TRIGGER_RISING) { dev_err(dev, "non-rising trigger given for EOC interrupt, trying to enforce it\n"); irq_trig = IRQF_TRIGGER_RISING; @@ -2604,13 +3047,8 @@ static int bmp085_fetch_eoc_irq(struct device *dev, init_completion(&data->done); - ret = devm_request_threaded_irq(dev, - irq, - bmp085_eoc_irq, - NULL, - irq_trig, - name, - data); + ret = devm_request_irq(dev, irq, bmp085_eoc_irq, irq_trig, + indio_dev->name, data); if (ret) { /* Bail out without IRQ but keep the driver in place */ dev_err(dev, "unable to request DRDY IRQ\n"); @@ -2618,14 +3056,54 @@ static int bmp085_fetch_eoc_irq(struct device *dev, } data->use_eoc = true; + return 0; } +/* Identical to bmp180_chip_info + bmp085_trigger_probe */ +const struct bmp280_chip_info bmp085_chip_info = { + .id_reg = BMP280_REG_ID, + .chip_id = bmp180_chip_ids, + .num_chip_id = ARRAY_SIZE(bmp180_chip_ids), + .regmap_config = &bmp180_regmap_config, + .start_up_time = 2000, + .channels = bmp280_channels, + .num_channels = ARRAY_SIZE(bmp280_channels), + .avail_scan_masks = bmp280_avail_scan_masks, + + .oversampling_temp_avail = bmp180_oversampling_temp_avail, + .num_oversampling_temp_avail = + ARRAY_SIZE(bmp180_oversampling_temp_avail), + .oversampling_temp_default = 0, + + .oversampling_press_avail = bmp180_oversampling_press_avail, + .num_oversampling_press_avail = + ARRAY_SIZE(bmp180_oversampling_press_avail), + .oversampling_press_default = BMP180_MEAS_PRESS_8X, + + .temp_coeffs = bmp180_temp_coeffs, + .temp_coeffs_type = IIO_VAL_FRACTIONAL, + .press_coeffs = bmp180_press_coeffs, + .press_coeffs_type = IIO_VAL_FRACTIONAL, + + .chip_config = bmp180_chip_config, + .read_temp = bmp180_read_temp, + .read_press = bmp180_read_press, + .read_calib = bmp180_read_calib, + .set_mode = bmp180_set_mode, + .wait_conv = bmp180_wait_conv, + + .trigger_probe = bmp085_trigger_probe, + .trigger_handler = bmp180_trigger_handler, +}; +EXPORT_SYMBOL_NS(bmp085_chip_info, IIO_BMP280); + static int bmp280_buffer_preenable(struct iio_dev *indio_dev) { struct bmp280_data *data = iio_priv(indio_dev); pm_runtime_get_sync(data->dev); + data->chip_info->set_mode(data, BMP280_NORMAL); return 0; } @@ -2790,12 +3268,17 @@ int bmp280_common_probe(struct device *dev, * however as it happens, the BMP085 shares the chip ID of BMP180 * so we look for an IRQ if we have that. */ - if (irq > 0 && (chip_id == BMP180_CHIP_ID)) { - ret = bmp085_fetch_eoc_irq(dev, name, irq, data); + if (irq > 0) { + if (data->chip_info->trigger_probe) + ret = data->chip_info->trigger_probe(indio_dev); if (ret) return ret; } + ret = data->chip_info->set_mode(data, BMP280_SLEEP); + if (ret) + return dev_err_probe(dev, ret, "Failed to set sleep mode\n"); + /* Enable runtime PM */ pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); @@ -2821,6 +3304,9 @@ static int bmp280_runtime_suspend(struct device *dev) struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmp280_data *data = iio_priv(indio_dev); + data->chip_info->set_mode(data, BMP280_SLEEP); + + fsleep(data->start_up_time); return regulator_bulk_disable(BMP280_NUM_SUPPLIES, data->supplies); } @@ -2835,7 +3321,12 @@ static int bmp280_runtime_resume(struct device *dev) return ret; usleep_range(data->start_up_time, data->start_up_time + 100); - return data->chip_info->chip_config(data); + + ret = data->chip_info->chip_config(data); + if (ret) + return ret; + + return data->chip_info->set_mode(data, data->op_mode); } EXPORT_RUNTIME_DEV_PM_OPS(bmp280_dev_pm_ops, bmp280_runtime_suspend, diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 5c3a63b4327c8..2f7b25984c7b3 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -27,7 +27,7 @@ static int bmp280_i2c_probe(struct i2c_client *client) } static const struct of_device_id bmp280_of_i2c_match[] = { - { .compatible = "bosch,bmp085", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp085", .data = &bmp085_chip_info }, { .compatible = "bosch,bmp180", .data = &bmp180_chip_info }, { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, { .compatible = "bosch,bme280", .data = &bme280_chip_info }, @@ -38,7 +38,7 @@ static const struct of_device_id bmp280_of_i2c_match[] = { MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); static const struct i2c_device_id bmp280_i2c_id[] = { - {"bmp085", (kernel_ulong_t)&bmp180_chip_info }, + {"bmp085", (kernel_ulong_t)&bmp085_chip_info }, {"bmp180", (kernel_ulong_t)&bmp180_chip_info }, {"bmp280", (kernel_ulong_t)&bmp280_chip_info }, {"bme280", (kernel_ulong_t)&bme280_chip_info }, diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index d18549d9bb647..49aa8c2cd85bd 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -114,7 +114,7 @@ static int bmp280_spi_probe(struct spi_device *spi) } static const struct of_device_id bmp280_of_spi_match[] = { - { .compatible = "bosch,bmp085", .data = &bmp180_chip_info }, + { .compatible = "bosch,bmp085", .data = &bmp085_chip_info }, { .compatible = "bosch,bmp180", .data = &bmp180_chip_info }, { .compatible = "bosch,bmp181", .data = &bmp180_chip_info }, { .compatible = "bosch,bmp280", .data = &bmp280_chip_info }, @@ -126,7 +126,7 @@ static const struct of_device_id bmp280_of_spi_match[] = { MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); static const struct spi_device_id bmp280_spi_id[] = { - { "bmp085", (kernel_ulong_t)&bmp180_chip_info }, + { "bmp085", (kernel_ulong_t)&bmp085_chip_info }, { "bmp180", (kernel_ulong_t)&bmp180_chip_info }, { "bmp181", (kernel_ulong_t)&bmp180_chip_info }, { "bmp280", (kernel_ulong_t)&bmp280_chip_info }, diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index ccacc67c14731..2df1175b6b853 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -55,8 +55,17 @@ #define BMP580_CMD_NVM_WRITE_SEQ_1 0xA0 #define BMP580_CMD_SOFT_RESET 0xB6 +#define BMP580_INT_STATUS_DRDY_MASK BIT(0) #define BMP580_INT_STATUS_POR_MASK BIT(4) +#define BMP580_INT_SOURCE_DRDY BIT(0) + +#define BMP580_INT_CONFIG_MASK GENMASK(3, 0) +#define BMP580_INT_CONFIG_LATCH BIT(0) +#define BMP580_INT_CONFIG_LEVEL BIT(1) +#define BMP580_INT_CONFIG_OPEN_DRAIN BIT(2) +#define BMP580_INT_CONFIG_INT_EN BIT(3) + #define BMP580_STATUS_CORE_RDY_MASK BIT(0) #define BMP580_STATUS_NVM_RDY_MASK BIT(1) #define BMP580_STATUS_NVM_ERR_MASK BIT(2) @@ -170,6 +179,19 @@ #define BMP380_MODE_FORCED 1 #define BMP380_MODE_NORMAL 3 +#define BMP380_MEAS_OFFSET 234 +#define BMP380_MEAS_DUR 2020 +#define BMP380_TEMP_MEAS_OFFSET 163 +#define BMP380_PRESS_MEAS_OFFSET 392 + +#define BMP380_INT_STATUS_DRDY BIT(3) + +#define BMP380_INT_CTRL_SETTINGS_MASK GENMASK(2, 0) +#define BMP380_INT_CTRL_OPEN_DRAIN BIT(0) +#define BMP380_INT_CTRL_LEVEL BIT(1) +#define BMP380_INT_CTRL_LATCH BIT(2) +#define BMP380_INT_CTRL_DRDY_EN BIT(6) + #define BMP380_MIN_TEMP -4000 #define BMP380_MAX_TEMP 8500 #define BMP380_MIN_PRES 3000000 @@ -205,6 +227,10 @@ #define BMP280_REG_CONFIG 0xF5 #define BMP280_REG_CTRL_MEAS 0xF4 #define BMP280_REG_STATUS 0xF3 +#define BMP280_REG_STATUS_IM_UPDATE BIT(0) +#define BMP280_REG_STATUS_MEAS_BIT BIT(3) +#define BMP280_REG_RESET 0xE0 +#define BMP280_RST_SOFT_CMD 0xB6 #define BMP280_REG_COMP_TEMP_START 0x88 #define BMP280_COMP_TEMP_REG_COUNT 6 @@ -243,6 +269,10 @@ #define BMP280_MODE_FORCED 1 #define BMP280_MODE_NORMAL 3 +#define BMP280_MEAS_OFFSET 1250 +#define BMP280_MEAS_DUR 2300 +#define BMP280_PRESS_HUMID_MEAS_OFFSET 575 + /* BME280 specific registers */ #define BME280_REG_HUMIDITY_LSB 0xFE #define BME280_REG_HUMIDITY_MSB 0xFD @@ -257,8 +287,13 @@ #define BME280_REG_COMP_H5 0xE5 #define BME280_REG_COMP_H6 0xE7 +#define BME280_COMP_H4_GET_MASK_UP GENMASK(15, 8) +#define BME280_COMP_H4_PREP_MASK_UP GENMASK(11, 4) +#define BME280_COMP_H4_MASK_LOW GENMASK(3, 0) #define BME280_COMP_H5_MASK GENMASK(15, 4) +#define BME280_CONTIGUOUS_CALIB_REGS 7 + #define BME280_OSRS_HUMIDITY_MASK GENMASK(2, 0) #define BME280_OSRS_HUMIDITY_SKIP 0 #define BME280_OSRS_HUMIDITY_1X 1 @@ -314,6 +349,7 @@ BMP280_NUM_TEMP_BYTES + \ BME280_NUM_HUMIDITY_BYTES) +#define BME280_NUM_MAX_CHANNELS 3 /* Core exported structs */ static const char *const bmp280_supply_names[] = { @@ -376,12 +412,21 @@ struct bmp380_calib { s8 P11; }; +enum bmp280_op_mode { + BMP280_SLEEP, + BMP280_FORCED, + BMP280_NORMAL, +}; + struct bmp280_data { struct device *dev; struct mutex lock; struct regmap *regmap; struct completion done; bool use_eoc; + bool trig_open_drain; + bool trig_active_high; + struct iio_trigger *trig; const struct bmp280_chip_info *chip_info; union { struct bmp180_calib bmp180; @@ -411,7 +456,11 @@ struct bmp280_data { * Data to push to userspace triggered buffer. Up to 3 channels and * s64 timestamp, aligned. */ - s32 sensor_data[6] __aligned(8); + u8 sensor_data[ALIGN(sizeof(s32) * BME280_NUM_MAX_CHANNELS, sizeof(s64)) + + sizeof(s64)] __aligned(sizeof(s64)); + + /* Value to hold the current operation mode of the device */ + enum bmp280_op_mode op_mode; /* * DMA (thus cache coherency maintenance) may require the @@ -423,6 +472,7 @@ struct bmp280_data { /* Calibration data buffers */ __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + u8 bme280_humid_cal_buf[BME280_CONTIGUOUS_CALIB_REGS]; u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; /* Miscellaneous, endianness-aware data buffers */ __le16 le16; @@ -476,11 +526,15 @@ struct bmp280_chip_info { int (*read_humid)(struct bmp280_data *data, u32 *adc_humidity); int (*read_calib)(struct bmp280_data *data); int (*preinit)(struct bmp280_data *data); + int (*set_mode)(struct bmp280_data *data, enum bmp280_op_mode mode); + int (*wait_conv)(struct bmp280_data *data); + int (*trigger_probe)(struct iio_dev *indio_dev); irqreturn_t (*trigger_handler)(int irq, void *p); }; /* Chip infos for each variant */ +extern const struct bmp280_chip_info bmp085_chip_info; extern const struct bmp280_chip_info bmp180_chip_info; extern const struct bmp280_chip_info bmp280_chip_info; extern const struct bmp280_chip_info bme280_chip_info; diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 956045e2db293..dfc36430c467c 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -24,7 +24,7 @@ struct press_state { struct hid_sensor_hub_attribute_info press_attr; struct { u32 press_data; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -241,11 +241,11 @@ static int press_parse_report(struct platform_device *pdev, /* Function to initialize the processing for usage id */ static int hid_press_probe(struct platform_device *pdev) { + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); int ret = 0; static const char *name = "press"; struct iio_dev *indio_dev; struct press_state *press_state; - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state)); @@ -325,7 +325,7 @@ static int hid_press_probe(struct platform_device *pdev) /* Function to deinitialize the processing for usage id */ static void hid_press_remove(struct platform_device *pdev) { - struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct press_state *press_state = iio_priv(indio_dev); @@ -350,7 +350,7 @@ static struct platform_driver hid_press_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_press_probe, - .remove_new = hid_press_remove, + .remove = hid_press_remove, }; module_platform_driver(hid_press_platform_driver); diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index ccaa07a569c9a..f24d9f9276812 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -417,9 +417,6 @@ static int __bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples, return ret; } - if (ret) - return ret; - for (i = 0; i < smp_lvl; i++) { buffer[i].temp = temp; iio_push_to_buffers(idev, &buffer[i]); diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c index 707ba0a510aa5..cdd254da9e503 100644 --- a/drivers/iio/proximity/aw96103.c +++ b/drivers/iio/proximity/aw96103.c @@ -422,7 +422,7 @@ static int aw96103_read_event_config(struct iio_dev *indio_dev, static int aw96103_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct aw96103 *aw96103 = iio_priv(indio_dev); diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c index c25472b14d4be..667369be05553 100644 --- a/drivers/iio/proximity/cros_ec_mkbp_proximity.c +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -167,7 +167,7 @@ static int cros_ec_mkbp_proximity_read_event_config(struct iio_dev *indio_dev, static int cros_ec_mkbp_proximity_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); @@ -261,7 +261,7 @@ static struct platform_driver cros_ec_mkbp_proximity_driver = { .pm = pm_sleep_ptr(&cros_ec_mkbp_proximity_pm_ops), }, .probe = cros_ec_mkbp_proximity_probe, - .remove_new = cros_ec_mkbp_proximity_remove, + .remove = cros_ec_mkbp_proximity_remove, }; module_platform_driver(cros_ec_mkbp_proximity_driver); diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index d8fb34060d3db..4021feb7a7ac7 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -874,12 +874,12 @@ static int hx9023s_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct hx9023s_data *data = iio_priv(indio_dev); if (test_bit(chan->channel, &data->chan_in_use)) { - hx9023s_ch_en(data, chan->channel, !!state); + hx9023s_ch_en(data, chan->channel, state); __assign_bit(chan->channel, &data->chan_event, data->ch_data[chan->channel].enable); } diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index 6e96b764fed8b..b09d15230111e 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -648,7 +648,8 @@ static int irsd200_read_event_config(struct iio_dev *indio_dev, static int irsd200_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { struct irsd200_data *data = iio_priv(indio_dev); unsigned int tmp; @@ -662,7 +663,7 @@ static int irsd200_write_event_config(struct iio_dev *indio_dev, return ret; return regmap_field_write( - data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state); + data->regfields[IRS_REGF_INTR_COUNT_THR_OR], state); default: return -EINVAL; } diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 86c57672fc7e3..71ad29e441b23 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -389,7 +389,7 @@ static const struct dev_pm_ops srf04_pm_ops = { static struct platform_driver srf04_driver = { .probe = srf04_probe, - .remove_new = srf04_remove, + .remove = srf04_remove, .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index 629f83c37d595..40747d7f6e7e9 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -868,6 +868,26 @@ static u8 sx9324_parse_phase_prop(struct device *dev, return raw; } +static void sx_common_get_raw_register_config(struct device *dev, + struct sx_common_reg_default *reg_def) +{ +#ifdef CONFIG_ACPI + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 raw = 0, ret; + char prop[80]; + + if (!reg_def->property || !adev) + return; + + snprintf(prop, ARRAY_SIZE(prop), "%s,reg_%s", acpi_device_hid(adev), reg_def->property); + ret = device_property_read_u32(dev, prop, &raw); + if (ret) + return; + + reg_def->def = raw; +#endif +} + static const struct sx_common_reg_default * sx9324_get_default_reg(struct device *dev, int idx, struct sx_common_reg_default *reg_def) diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index 2b90bf45a2012..07551e0decbd9 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -7,7 +7,6 @@ * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf */ -#include #include #include #include diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 3f4eace05cfc6..c4e94d0fb1637 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -540,7 +540,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct sx9500_data *data = iio_priv(indio_dev); int ret; @@ -551,7 +551,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev, mutex_lock(&data->mutex); - if (state == 1) { + if (state) { ret = sx9500_inc_chan_users(data, chan->channel); if (ret < 0) goto out_unlock; @@ -571,7 +571,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev, goto out_unlock; out_undo_chan: - if (state == 1) + if (state) sx9500_dec_chan_users(data, chan->channel); else sx9500_inc_chan_users(data, chan->channel); diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index 71aa6dced7d34..76384c74fe012 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -268,7 +268,7 @@ EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, SEMTECH_PROX); int sx_common_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct sx_common_data *data = iio_priv(indio_dev); unsigned int eventirq = SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ; @@ -421,27 +421,6 @@ static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = { .postdisable = sx_common_buffer_postdisable, }; -void sx_common_get_raw_register_config(struct device *dev, - struct sx_common_reg_default *reg_def) -{ -#ifdef CONFIG_ACPI - struct acpi_device *adev = ACPI_COMPANION(dev); - u32 raw = 0, ret; - char prop[80]; - - if (!reg_def->property || !adev) - return; - - snprintf(prop, ARRAY_SIZE(prop), "%s,reg_%s", acpi_device_hid(adev), reg_def->property); - ret = device_property_read_u32(dev, prop, &raw); - if (ret) - return; - - reg_def->def = raw; -#endif -} -EXPORT_SYMBOL_NS_GPL(sx_common_get_raw_register_config, SEMTECH_PROX); - #define SX_COMMON_SOFT_RESET 0xde static int sx_common_init_device(struct device *dev, struct iio_dev *indio_dev) diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index 101b90e52ff26..fb14e6f06a6de 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -8,7 +8,6 @@ #ifndef IIO_SX_COMMON_H #define IIO_SX_COMMON_H -#include #include #include #include @@ -144,15 +143,12 @@ int sx_common_read_event_config(struct iio_dev *indio_dev, int sx_common_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state); + enum iio_event_direction dir, bool state); int sx_common_probe(struct i2c_client *client, const struct sx_common_chip_info *chip_info, const struct regmap_config *regmap_config); -void sx_common_get_raw_register_config(struct device *dev, - struct sx_common_reg_default *reg_def); - /* 3 is the number of events defined by a single phase. */ extern const struct iio_event_spec sx_common_events[3]; diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index d1ddf85f53836..bb6c9cc88b358 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -449,7 +449,7 @@ static int vcnl3020_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { switch (chan->type) { case IIO_PROXIMITY: diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index 8d4f3f849fe24..87d10faaff9b1 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -20,8 +20,13 @@ #include #include #include +#include #include +#include +#include +#include +#include #define VL_REG_SYSRANGE_START 0x00 @@ -39,21 +44,74 @@ #define VL_REG_RESULT_INT_STATUS 0x13 #define VL_REG_RESULT_RANGE_STATUS 0x14 +#define VL_REG_IDENTIFICATION_MODEL_ID 0xC0 #define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0) +#define VL53L0X_MODEL_ID_VAL 0xEE +#define VL53L0X_CONTINUOUS_MODE 0x02 +#define VL53L0X_SINGLE_MODE 0x01 + struct vl53l0x_data { struct i2c_client *client; struct completion completion; struct regulator *vdd_supply; struct gpio_desc *reset_gpio; + struct iio_trigger *trig; + + struct { + u16 chan; + aligned_s64 timestamp; + } scan; }; -static irqreturn_t vl53l0x_handle_irq(int irq, void *priv) +static int vl53l0x_clear_irq(struct vl53l0x_data *data) +{ int ret; + + ret = i2c_smbus_write_byte_data(data->client, + VL_REG_SYSTEM_INTERRUPT_CLEAR, 1); + if (ret < 0) { + dev_err(&data->client->dev, "failed to clear irq: %d\n", ret); + return -EINVAL; + } + + return 0; +} + +static irqreturn_t vl53l0x_trigger_handler(int irq, void *priv) +{ + struct iio_poll_func *pf = priv; + struct iio_dev *indio_dev = pf->indio_dev; + struct vl53l0x_data *data = iio_priv(indio_dev); + u8 buffer[12]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(data->client, + VL_REG_RESULT_RANGE_STATUS, + sizeof(buffer), buffer); + if (ret < 0) + return ret; + else if (ret != 12) + return -EREMOTEIO; + + data->scan.chan = get_unaligned_be16(&buffer[10]); + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + vl53l0x_clear_irq(data); + + return IRQ_HANDLED; +} + +static irqreturn_t vl53l0x_threaded_irq(int irq, void *priv) { struct iio_dev *indio_dev = priv; struct vl53l0x_data *data = iio_priv(indio_dev); - complete(&data->completion); + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll_nested(indio_dev->trig); + else + complete(&data->completion); return IRQ_HANDLED; } @@ -68,8 +126,9 @@ static int vl53l0x_configure_irq(struct i2c_client *client, if (!irq_flags) irq_flags = IRQF_TRIGGER_FALLING; - ret = devm_request_irq(&client->dev, client->irq, vl53l0x_handle_irq, - irq_flags, indio_dev->name, indio_dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, vl53l0x_threaded_irq, + irq_flags | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) { dev_err(&client->dev, "devm_request_irq error: %d\n", ret); return ret; @@ -84,26 +143,6 @@ static int vl53l0x_configure_irq(struct i2c_client *client, return ret; } -static void vl53l0x_clear_irq(struct vl53l0x_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - - ret = i2c_smbus_write_byte_data(data->client, - VL_REG_SYSTEM_INTERRUPT_CLEAR, 1); - if (ret < 0) - dev_err(dev, "failed to clear error irq: %d\n", ret); - - ret = i2c_smbus_write_byte_data(data->client, - VL_REG_SYSTEM_INTERRUPT_CLEAR, 0); - if (ret < 0) - dev_err(dev, "failed to clear range irq: %d\n", ret); - - ret = i2c_smbus_read_byte_data(data->client, VL_REG_RESULT_INT_STATUS); - if (ret < 0 || ret & 0x07) - dev_err(dev, "failed to clear irq: %d\n", ret); -} - static int vl53l0x_read_proximity(struct vl53l0x_data *data, const struct iio_chan_spec *chan, int *val) @@ -125,7 +164,9 @@ static int vl53l0x_read_proximity(struct vl53l0x_data *data, if (time_left == 0) return -ETIMEDOUT; - vl53l0x_clear_irq(data); + ret = vl53l0x_clear_irq(data); + if (ret < 0) + return ret; } else { do { ret = i2c_smbus_read_byte_data(client, @@ -150,7 +191,7 @@ static int vl53l0x_read_proximity(struct vl53l0x_data *data, return -EREMOTEIO; /* Values should be between 30~1200 in millimeters. */ - *val = (buffer[10] << 8) + buffer[11]; + *val = get_unaligned_be16(&buffer[10]); return 0; } @@ -160,7 +201,14 @@ static const struct iio_chan_spec vl53l0x_channels[] = { .type = IIO_DISTANCE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(1), }; static int vl53l0x_read_raw(struct iio_dev *indio_dev, @@ -190,8 +238,16 @@ static int vl53l0x_read_raw(struct iio_dev *indio_dev, } } +static int vl53l0x_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + + return data->trig == trig ? 0 : -EINVAL; +} + static const struct iio_info vl53l0x_info = { .read_raw = vl53l0x_read_raw, + .validate_trigger = vl53l0x_validate_trigger, }; static void vl53l0x_power_off(void *_data) @@ -218,11 +274,46 @@ static int vl53l0x_power_on(struct vl53l0x_data *data) return 0; } +static int vl53l0x_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + + return i2c_smbus_write_byte_data(data->client, VL_REG_SYSRANGE_START, + VL53L0X_CONTINUOUS_MODE); +} + +static int vl53l0x_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(data->client, VL_REG_SYSRANGE_START, + VL53L0X_SINGLE_MODE); + if (ret < 0) + return ret; + + /* Let the ongoing reading finish */ + reinit_completion(&data->completion); + wait_for_completion_timeout(&data->completion, HZ / 10); + + return vl53l0x_clear_irq(data); +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &vl53l0x_buffer_postenable, + .postdisable = &vl53l0x_buffer_postdisable, +}; + +static const struct iio_trigger_ops vl53l0x_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + static int vl53l0x_probe(struct i2c_client *client) { struct vl53l0x_data *data; struct iio_dev *indio_dev; int error; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -237,6 +328,13 @@ static int vl53l0x_probe(struct i2c_client *client) I2C_FUNC_SMBUS_BYTE_DATA)) return -EOPNOTSUPP; + ret = i2c_smbus_read_byte_data(data->client, VL_REG_IDENTIFICATION_MODEL_ID); + if (ret < 0) + return -EINVAL; + + if (ret != VL53L0X_MODEL_ID_VAL) + dev_info(&client->dev, "Unknown model id: 0x%x", ret); + data->vdd_supply = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(data->vdd_supply)) return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply), @@ -265,13 +363,33 @@ static int vl53l0x_probe(struct i2c_client *client) /* usage of interrupt is optional */ if (client->irq) { - int ret; - init_completion(&data->completion); + data->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &vl53l0x_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + ret = devm_iio_trigger_register(&client->dev, data->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(data->trig); + ret = vl53l0x_configure_irq(client, indio_dev); if (ret) return ret; + + ret = devm_iio_triggered_buffer_setup(&client->dev, + indio_dev, + NULL, + &vl53l0x_trigger_handler, + &iio_triggered_buffer_setup_ops); + if (ret) + return ret; } return devm_iio_device_register(&client->dev, indio_dev); diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index ed0e4963362f9..1244d8e17d504 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -91,6 +91,8 @@ config MLX90635 config TMP006 tristate "TMP006 infrared thermopile sensor" depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help If you say yes here you get support for the Texas Instruments TMP006 infrared thermopile sensor. diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 0143fd478933c..0e21217472abb 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -18,7 +18,7 @@ struct temperature_state { struct hid_sensor_hub_attribute_info temperature_attr; struct { s32 temperature_data; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int scale_pre_decml; int scale_post_decml; @@ -283,7 +283,7 @@ static struct platform_driver hid_temperature_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_temperature_probe, - .remove_new = hid_temperature_remove, + .remove = hid_temperature_remove, }; module_platform_driver(hid_temperature_platform_driver); diff --git a/drivers/iio/temperature/mcp9600.c b/drivers/iio/temperature/mcp9600.c index f1bb0976273d3..c2447860adfd6 100644 --- a/drivers/iio/temperature/mcp9600.c +++ b/drivers/iio/temperature/mcp9600.c @@ -200,7 +200,7 @@ static int mcp9600_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct mcp9600_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 6d8d661f0c82a..0c844137d7aa9 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -7,8 +7,6 @@ * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor * * (7-bit I2C slave address 0x40, changeable via ADR pins) - * - * TODO: data ready irq */ #include @@ -21,6 +19,9 @@ #include #include +#include +#include +#include #define TMP006_VOBJECT 0x00 #define TMP006_TAMBIENT 0x01 @@ -45,6 +46,7 @@ struct tmp006_data { struct i2c_client *client; u16 config; + struct iio_trigger *drdy_trig; }; static int tmp006_read_measurement(struct tmp006_data *data, u8 reg) @@ -83,15 +85,19 @@ static int tmp006_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: if (channel->type == IIO_VOLTAGE) { /* LSB is 156.25 nV */ - ret = tmp006_read_measurement(data, TMP006_VOBJECT); - if (ret < 0) - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = tmp006_read_measurement(data, TMP006_VOBJECT); + if (ret < 0) + return ret; + } *val = sign_extend32(ret, 15); } else if (channel->type == IIO_TEMP) { /* LSB is 0.03125 degrees Celsius */ - ret = tmp006_read_measurement(data, TMP006_TAMBIENT); - if (ret < 0) - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = tmp006_read_measurement(data, TMP006_TAMBIENT); + if (ret < 0) + return ret; + } *val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT; } else { break; @@ -128,7 +134,7 @@ static int tmp006_write_raw(struct iio_dev *indio_dev, long mask) { struct tmp006_data *data = iio_priv(indio_dev); - int i; + int ret, i; if (mask != IIO_CHAN_INFO_SAMP_FREQ) return -EINVAL; @@ -136,13 +142,19 @@ static int tmp006_write_raw(struct iio_dev *indio_dev, for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++) if ((val == tmp006_freqs[i][0]) && (val2 == tmp006_freqs[i][1])) { + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + data->config &= ~TMP006_CONFIG_CR_MASK; data->config |= i << TMP006_CONFIG_CR_SHIFT; - return i2c_smbus_write_word_swapped(data->client, - TMP006_CONFIG, - data->config); + ret = i2c_smbus_write_word_swapped(data->client, + TMP006_CONFIG, + data->config); + iio_device_release_direct_mode(indio_dev); + return ret; } return -EINVAL; } @@ -164,13 +176,29 @@ static const struct iio_chan_spec tmp006_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + } }, { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - } + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + .shift = TMP006_TAMBIENT_SHIFT, + .endianness = IIO_BE, + } + }, + IIO_CHAN_SOFT_TIMESTAMP(2), }; static const struct iio_info tmp006_info = { @@ -213,6 +241,54 @@ static void tmp006_powerdown_cleanup(void *dev) tmp006_power(dev, false); } +static irqreturn_t tmp006_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tmp006_data *data = iio_priv(indio_dev); + struct { + s16 channels[2]; + s64 ts __aligned(8); + } scan; + s32 ret; + + ret = i2c_smbus_read_word_data(data->client, TMP006_VOBJECT); + if (ret < 0) + goto err; + scan.channels[0] = ret; + + ret = i2c_smbus_read_word_data(data->client, TMP006_TAMBIENT); + if (ret < 0) + goto err; + scan.channels[1] = ret; + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, + iio_get_time_ns(indio_dev)); +err: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int tmp006_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct tmp006_data *data = iio_priv(indio_dev); + + if (state) + data->config |= TMP006_CONFIG_DRDY_EN; + else + data->config &= ~TMP006_CONFIG_DRDY_EN; + + return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG, + data->config); +} + +static const struct iio_trigger_ops tmp006_trigger_ops = { + .set_trigger_state = tmp006_set_trigger_state, +}; + +static const unsigned long tmp006_scan_masks[] = { 0x3, 0 }; + static int tmp006_probe(struct i2c_client *client) { struct iio_dev *indio_dev; @@ -241,6 +317,7 @@ static int tmp006_probe(struct i2c_client *client) indio_dev->channels = tmp006_channels; indio_dev->num_channels = ARRAY_SIZE(tmp006_channels); + indio_dev->available_scan_masks = tmp006_scan_masks; ret = i2c_smbus_read_word_swapped(data->client, TMP006_CONFIG); if (ret < 0) @@ -258,6 +335,37 @@ static int tmp006_probe(struct i2c_client *client) if (ret < 0) return ret; + if (client->irq > 0) { + data->drdy_trig = devm_iio_trigger_alloc(&client->dev, + "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->drdy_trig) + return -ENOMEM; + + data->drdy_trig->ops = &tmp006_trigger_ops; + iio_trigger_set_drvdata(data->drdy_trig, indio_dev); + ret = iio_trigger_register(data->drdy_trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(data->drdy_trig); + + ret = devm_request_threaded_irq(&client->dev, client->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_ONESHOT, + "tmp006_irq", + data->drdy_trig); + if (ret < 0) + return ret; + } + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + tmp006_trigger_handler, NULL); + if (ret < 0) + return ret; + return devm_iio_device_register(&client->dev, indio_dev); } diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index 9bdfa94234929..fd4d389ce1dfe 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -216,7 +216,7 @@ static irqreturn_t tmp007_interrupt_handler(int irq, void *private) static int tmp007_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct tmp007_data *data = iio_priv(indio_dev); unsigned int status_mask; diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index dec256bfbd73e..21c6b6292a721 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -96,7 +96,7 @@ static void iio_interrupt_trigger_remove(struct platform_device *pdev) static struct platform_driver iio_interrupt_trigger_driver = { .probe = iio_interrupt_trigger_probe, - .remove_new = iio_interrupt_trigger_remove, + .remove = iio_interrupt_trigger_remove, .driver = { .name = "iio_interrupt_trigger", }, diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 0684329956d95..bb60b2d7b2eca 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -900,7 +900,7 @@ MODULE_DEVICE_TABLE(of, stm32_trig_of_match); static struct platform_driver stm32_timer_trigger_driver = { .probe = stm32_timer_trigger_probe, - .remove_new = stm32_timer_trigger_remove, + .remove = stm32_timer_trigger_remove, .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c index 53ec7510e4ebf..ba2cd68b53e6c 100644 --- a/drivers/infiniband/hw/qib/qib_sysfs.c +++ b/drivers/infiniband/hw/qib/qib_sysfs.c @@ -283,7 +283,7 @@ static struct bin_attribute *port_ccmgta_attributes[] = { }; static umode_t qib_ccmgta_is_bin_visible(struct kobject *kobj, - struct bin_attribute *attr, int n) + const struct bin_attribute *attr, int n) { struct qib_pportdata *ppd = qib_get_pportdata_kobj(kobj); diff --git a/drivers/input/input.c b/drivers/input/input.c index c51858f1cdc55..7f0477e04ad21 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -605,6 +605,9 @@ int input_open_device(struct input_handle *handle) handle->open++; + if (handle->handler->passive_observer) + goto out; + if (dev->users++ || dev->inhibited) { /* * Device is already opened and/or inhibited, @@ -668,11 +671,13 @@ void input_close_device(struct input_handle *handle) __input_release_device(handle); - if (!--dev->users && !dev->inhibited) { - if (dev->poller) - input_dev_poller_stop(dev->poller); - if (dev->close) - dev->close(dev); + if (!handle->handler->passive_observer) { + if (!--dev->users && !dev->inhibited) { + if (dev->poller) + input_dev_poller_stop(dev->poller); + if (dev->close) + dev->close(dev); + } } if (!--handle->open) { diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 6373d7aa739a8..a9f1946cf0d6f 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -505,24 +505,22 @@ static int db9_open(struct input_dev *dev) { struct db9 *db9 = input_get_drvdata(dev); struct parport *port = db9->pd->port; - int err; - err = mutex_lock_interruptible(&db9->mutex); - if (err) - return err; - - if (!db9->used++) { - parport_claim(db9->pd); - parport_write_data(port, 0xff); - if (db9_modes[db9->mode].reverse) { - parport_data_reverse(port); - parport_write_control(port, DB9_NORMAL); + scoped_guard(mutex_intr, &db9->mutex) { + if (!db9->used++) { + parport_claim(db9->pd); + parport_write_data(port, 0xff); + if (db9_modes[db9->mode].reverse) { + parport_data_reverse(port); + parport_write_control(port, DB9_NORMAL); + } + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); } - mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); + + return 0; } - mutex_unlock(&db9->mutex); - return 0; + return -EINTR; } static void db9_close(struct input_dev *dev) @@ -530,14 +528,14 @@ static void db9_close(struct input_dev *dev) struct db9 *db9 = input_get_drvdata(dev); struct parport *port = db9->pd->port; - mutex_lock(&db9->mutex); + guard(mutex)(&db9->mutex); + if (!--db9->used) { del_timer_sync(&db9->timer); parport_write_control(port, 0x00); parport_data_forward(port); parport_release(db9->pd); } - mutex_unlock(&db9->mutex); } static void db9_attach(struct parport *pp) diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 2b553e2d838f2..b53cafd7a5ee1 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -765,33 +765,31 @@ static void gc_timer(struct timer_list *t) static int gc_open(struct input_dev *dev) { struct gc *gc = input_get_drvdata(dev); - int err; - err = mutex_lock_interruptible(&gc->mutex); - if (err) - return err; + scoped_guard(mutex_intr, &gc->mutex) { + if (!gc->used++) { + parport_claim(gc->pd); + parport_write_control(gc->pd->port, 0x04); + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); + } - if (!gc->used++) { - parport_claim(gc->pd); - parport_write_control(gc->pd->port, 0x04); - mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); + return 0; } - mutex_unlock(&gc->mutex); - return 0; + return -EINTR; } static void gc_close(struct input_dev *dev) { struct gc *gc = input_get_drvdata(dev); - mutex_lock(&gc->mutex); + guard(mutex)(&gc->mutex); + if (!--gc->used) { del_timer_sync(&gc->timer); parport_write_control(gc->pd->port, 0x00); parport_release(gc->pd); } - mutex_unlock(&gc->mutex); } static int gc_setup_pad(struct gc *gc, int idx, int pad_type) diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 95c0348843e6b..8c78cbe553c8c 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -21,14 +21,13 @@ static int make_magnitude_modifier(struct iforce* iforce, unsigned char data[3]; if (!no_alloc) { - mutex_lock(&iforce->mem_mutex); - if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - mutex_unlock(&iforce->mem_mutex); + guard(mutex)(&iforce->mem_mutex); + + if (allocate_resource(&iforce->device_memory, mod_chunk, 2, + iforce->device_memory.start, + iforce->device_memory.end, + 2L, NULL, NULL)) return -ENOSPC; - } - mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); @@ -54,14 +53,13 @@ static int make_period_modifier(struct iforce* iforce, period = TIME_SCALE(period); if (!no_alloc) { - mutex_lock(&iforce->mem_mutex); - if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - mutex_unlock(&iforce->mem_mutex); + guard(mutex)(&iforce->mem_mutex); + + if (allocate_resource(&iforce->device_memory, mod_chunk, 0x0c, + iforce->device_memory.start, + iforce->device_memory.end, + 2L, NULL, NULL)) return -ENOSPC; - } - mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); @@ -94,14 +92,13 @@ static int make_envelope_modifier(struct iforce* iforce, fade_duration = TIME_SCALE(fade_duration); if (!no_alloc) { - mutex_lock(&iforce->mem_mutex); + guard(mutex)(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - mutex_unlock(&iforce->mem_mutex); + iforce->device_memory.start, + iforce->device_memory.end, + 2L, NULL, NULL)) return -ENOSPC; - } - mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); @@ -131,14 +128,13 @@ static int make_condition_modifier(struct iforce* iforce, unsigned char data[10]; if (!no_alloc) { - mutex_lock(&iforce->mem_mutex); + guard(mutex)(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - mutex_unlock(&iforce->mem_mutex); + iforce->device_memory.start, + iforce->device_memory.end, + 2L, NULL, NULL)) return -ENOSPC; - } - mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 08c889a72f6c6..74181d5123cd0 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -31,49 +31,42 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) int c; int empty; int head, tail; - unsigned long flags; /* * Update head and tail of xmit buffer */ - spin_lock_irqsave(&iforce->xmit_lock, flags); - - head = iforce->xmit.head; - tail = iforce->xmit.tail; - - - if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) { - dev_warn(&iforce->dev->dev, - "not enough space in xmit buffer to send new packet\n"); - spin_unlock_irqrestore(&iforce->xmit_lock, flags); - return -1; - } + scoped_guard(spinlock_irqsave, &iforce->xmit_lock) { + head = iforce->xmit.head; + tail = iforce->xmit.tail; + + if (CIRC_SPACE(head, tail, XMIT_SIZE) < n + 2) { + dev_warn(&iforce->dev->dev, + "not enough space in xmit buffer to send new packet\n"); + return -1; + } - empty = head == tail; - XMIT_INC(iforce->xmit.head, n+2); + empty = head == tail; + XMIT_INC(iforce->xmit.head, n + 2); /* * Store packet in xmit buffer */ - iforce->xmit.buf[head] = HI(cmd); - XMIT_INC(head, 1); - iforce->xmit.buf[head] = LO(cmd); - XMIT_INC(head, 1); - - c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE); - if (n < c) c=n; - - memcpy(&iforce->xmit.buf[head], - data, - c); - if (n != c) { - memcpy(&iforce->xmit.buf[0], - data + c, - n - c); + iforce->xmit.buf[head] = HI(cmd); + XMIT_INC(head, 1); + iforce->xmit.buf[head] = LO(cmd); + XMIT_INC(head, 1); + + c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE); + if (n < c) + c = n; + + memcpy(&iforce->xmit.buf[head], data, c); + if (n != c) + memcpy(&iforce->xmit.buf[0], data + c, n - c); + + XMIT_INC(head, n); } - XMIT_INC(head, n); - spin_unlock_irqrestore(&iforce->xmit_lock, flags); /* * If necessary, start the transmission */ diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index 2380546d79782..75b85c46dfa41 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -28,45 +28,39 @@ static void iforce_serio_xmit(struct iforce *iforce) iforce); unsigned char cs; int i; - unsigned long flags; if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags); return; } - spin_lock_irqsave(&iforce->xmit_lock, flags); + guard(spinlock_irqsave)(&iforce->xmit_lock); -again: - if (iforce->xmit.head == iforce->xmit.tail) { - iforce_clear_xmit_and_wake(iforce); - spin_unlock_irqrestore(&iforce->xmit_lock, flags); - return; - } + do { + if (iforce->xmit.head == iforce->xmit.tail) + break; - cs = 0x2b; + cs = 0x2b; - serio_write(iforce_serio->serio, 0x2b); + serio_write(iforce_serio->serio, 0x2b); - serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]); - cs ^= iforce->xmit.buf[iforce->xmit.tail]; - XMIT_INC(iforce->xmit.tail, 1); - - for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); - } - serio_write(iforce_serio->serio, cs); + for (i = iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { + serio_write(iforce_serio->serio, + iforce->xmit.buf[iforce->xmit.tail]); + cs ^= iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + } - if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)) - goto again; + serio_write(iforce_serio->serio, cs); - iforce_clear_xmit_and_wake(iforce); + } while (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)); - spin_unlock_irqrestore(&iforce->xmit_lock, flags); + iforce_clear_xmit_and_wake(iforce); } static int iforce_serio_get_id(struct iforce *iforce, u8 id, diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index cba92bd590a8d..1f00f76b01745 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -25,13 +25,11 @@ static void __iforce_usb_xmit(struct iforce *iforce) struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb, iforce); int n, c; - unsigned long flags; - spin_lock_irqsave(&iforce->xmit_lock, flags); + guard(spinlock_irqsave)(&iforce->xmit_lock); if (iforce->xmit.head == iforce->xmit.tail) { iforce_clear_xmit_and_wake(iforce); - spin_unlock_irqrestore(&iforce->xmit_lock, flags); return; } @@ -45,7 +43,8 @@ static void __iforce_usb_xmit(struct iforce *iforce) /* Copy rest of data then */ c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE); - if (n < c) c=n; + if (n < c) + c = n; memcpy(iforce_usb->out->transfer_buffer + 1, &iforce->xmit.buf[iforce->xmit.tail], @@ -53,11 +52,12 @@ static void __iforce_usb_xmit(struct iforce *iforce) if (n != c) { memcpy(iforce_usb->out->transfer_buffer + 1 + c, &iforce->xmit.buf[0], - n-c); + n - c); } XMIT_INC(iforce->xmit.tail, n); - if ( (n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC)) ) { + n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC); + if (n) { dev_warn(&iforce_usb->intf->dev, "usb_submit_urb failed %d\n", n); iforce_clear_xmit_and_wake(iforce); @@ -66,7 +66,6 @@ static void __iforce_usb_xmit(struct iforce *iforce) /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended. * As long as the urb completion handler is not called, the transmiting * is considered to be running */ - spin_unlock_irqrestore(&iforce->xmit_lock, flags); } static void iforce_usb_xmit(struct iforce *iforce) diff --git a/drivers/input/joystick/n64joy.c b/drivers/input/joystick/n64joy.c index b0986d2195d68..c344dbc0c493e 100644 --- a/drivers/input/joystick/n64joy.c +++ b/drivers/input/joystick/n64joy.c @@ -191,35 +191,32 @@ static void n64joy_poll(struct timer_list *t) static int n64joy_open(struct input_dev *dev) { struct n64joy_priv *priv = input_get_drvdata(dev); - int err; - - err = mutex_lock_interruptible(&priv->n64joy_mutex); - if (err) - return err; - - if (!priv->n64joy_opened) { - /* - * We could use the vblank irq, but it's not important if - * the poll point slightly changes. - */ - timer_setup(&priv->timer, n64joy_poll, 0); - mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16)); - } - priv->n64joy_opened++; + scoped_guard(mutex_intr, &priv->n64joy_mutex) { + if (!priv->n64joy_opened) { + /* + * We could use the vblank irq, but it's not important + * if the poll point slightly changes. + */ + timer_setup(&priv->timer, n64joy_poll, 0); + mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16)); + } - mutex_unlock(&priv->n64joy_mutex); - return err; + priv->n64joy_opened++; + return 0; + } + + return -EINTR; } static void n64joy_close(struct input_dev *dev) { struct n64joy_priv *priv = input_get_drvdata(dev); - mutex_lock(&priv->n64joy_mutex); + guard(mutex)(&priv->n64joy_mutex); + if (!--priv->n64joy_opened) del_timer_sync(&priv->timer); - mutex_unlock(&priv->n64joy_mutex); } static const u64 __initconst scandata[] ____cacheline_aligned = { diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index 0a78dda3e0ea8..db696ba61a3bf 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -103,33 +103,31 @@ static void tgfx_timer(struct timer_list *t) static int tgfx_open(struct input_dev *dev) { struct tgfx *tgfx = input_get_drvdata(dev); - int err; - err = mutex_lock_interruptible(&tgfx->sem); - if (err) - return err; + scoped_guard(mutex_intr, &tgfx->sem) { + if (!tgfx->used++) { + parport_claim(tgfx->pd); + parport_write_control(tgfx->pd->port, 0x04); + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + } - if (!tgfx->used++) { - parport_claim(tgfx->pd); - parport_write_control(tgfx->pd->port, 0x04); - mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + return 0; } - mutex_unlock(&tgfx->sem); - return 0; + return -EINTR; } static void tgfx_close(struct input_dev *dev) { struct tgfx *tgfx = input_get_drvdata(dev); - mutex_lock(&tgfx->sem); + guard(mutex)(&tgfx->sem); + if (!--tgfx->used) { del_timer_sync(&tgfx->timer); parport_write_control(tgfx->pd->port, 0x00); parport_release(tgfx->pd); } - mutex_unlock(&tgfx->sem); } diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 22ea58bf76cb5..ff9bc87f2f709 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1292,9 +1292,8 @@ static void xpad_irq_out(struct urb *urb) struct device *dev = &xpad->intf->dev; int status = urb->status; int error; - unsigned long flags; - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); switch (status) { case 0: @@ -1328,8 +1327,6 @@ static void xpad_irq_out(struct urb *urb) xpad->irq_out_active = false; } } - - spin_unlock_irqrestore(&xpad->odata_lock, flags); } static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad, @@ -1394,10 +1391,8 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad) { struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_CMD_IDX]; - unsigned long flags; - int retval; - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); packet->data[0] = 0x08; packet->data[1] = 0x00; @@ -1416,17 +1411,12 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad) /* Reset the sequence so we send out presence first */ xpad->last_out_packet = -1; - retval = xpad_try_sending_next_out_packet(xpad); - - spin_unlock_irqrestore(&xpad->odata_lock, flags); - - return retval; + return xpad_try_sending_next_out_packet(xpad); } static int xpad_start_xbox_one(struct usb_xpad *xpad) { - unsigned long flags; - int retval; + int error; if (usb_ifnum_to_if(xpad->udev, GIP_WIRED_INTF_AUDIO)) { /* @@ -1435,15 +1425,15 @@ static int xpad_start_xbox_one(struct usb_xpad *xpad) * Controller for Series X|S (0x20d6:0x200e) to report the * guide button. */ - retval = usb_set_interface(xpad->udev, - GIP_WIRED_INTF_AUDIO, 0); - if (retval) + error = usb_set_interface(xpad->udev, + GIP_WIRED_INTF_AUDIO, 0); + if (error) dev_warn(&xpad->dev->dev, "unable to disable audio interface: %d\n", - retval); + error); } - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); /* * Begin the init sequence by attempting to send a packet. @@ -1451,16 +1441,11 @@ static int xpad_start_xbox_one(struct usb_xpad *xpad) * sending any packets from the output ring. */ xpad->init_seq = 0; - retval = xpad_try_sending_next_out_packet(xpad); - - spin_unlock_irqrestore(&xpad->odata_lock, flags); - - return retval; + return xpad_try_sending_next_out_packet(xpad); } static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num) { - unsigned long flags; struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_CMD_IDX]; static const u8 mode_report_ack[] = { @@ -1468,7 +1453,7 @@ static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num) 0x00, GIP_CMD_VIRTUAL_KEY, GIP_OPT_INTERNAL, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); packet->len = sizeof(mode_report_ack); memcpy(packet->data, mode_report_ack, packet->len); @@ -1478,8 +1463,6 @@ static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num) /* Reset the sequence so we send out the ack now */ xpad->last_out_packet = -1; xpad_try_sending_next_out_packet(xpad); - - spin_unlock_irqrestore(&xpad->odata_lock, flags); } #ifdef CONFIG_JOYSTICK_XPAD_FF @@ -1489,8 +1472,6 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_FF_IDX]; __u16 strong; __u16 weak; - int retval; - unsigned long flags; if (effect->type != FF_RUMBLE) return 0; @@ -1498,7 +1479,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect strong = effect->u.rumble.strong_magnitude; weak = effect->u.rumble.weak_magnitude; - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); switch (xpad->xtype) { case XTYPE_XBOX: @@ -1564,15 +1545,10 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect dev_dbg(&xpad->dev->dev, "%s - rumble command sent to unsupported xpad type: %d\n", __func__, xpad->xtype); - retval = -EINVAL; - goto out; + return -EINVAL; } - retval = xpad_try_sending_next_out_packet(xpad); - -out: - spin_unlock_irqrestore(&xpad->odata_lock, flags); - return retval; + return xpad_try_sending_next_out_packet(xpad); } static int xpad_init_ff(struct usb_xpad *xpad) @@ -1625,11 +1601,10 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command) { struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_LED_IDX]; - unsigned long flags; command %= 16; - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); switch (xpad->xtype) { case XTYPE_XBOX360: @@ -1659,8 +1634,6 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command) } xpad_try_sending_next_out_packet(xpad); - - spin_unlock_irqrestore(&xpad->odata_lock, flags); } /* @@ -1785,11 +1758,10 @@ static void xpad_stop_input(struct usb_xpad *xpad) static void xpad360w_poweroff_controller(struct usb_xpad *xpad) { - unsigned long flags; struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_CMD_IDX]; - spin_lock_irqsave(&xpad->odata_lock, flags); + guard(spinlock_irqsave)(&xpad->odata_lock); packet->data[0] = 0x00; packet->data[1] = 0x00; @@ -1809,8 +1781,6 @@ static void xpad360w_poweroff_controller(struct usb_xpad *xpad) /* Reset the sequence so we send out poweroff now */ xpad->last_out_packet = -1; xpad_try_sending_next_out_packet(xpad); - - spin_unlock_irqrestore(&xpad->odata_lock, flags); } static int xpad360w_start_input(struct usb_xpad *xpad) @@ -2234,10 +2204,10 @@ static int xpad_suspend(struct usb_interface *intf, pm_message_t message) if (auto_poweroff && xpad->pad_present) xpad360w_poweroff_controller(xpad); } else { - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); + if (input_device_enabled(input)) xpad_stop_input(xpad); - mutex_unlock(&input->mutex); } xpad_stop_output(xpad); @@ -2249,26 +2219,25 @@ static int xpad_resume(struct usb_interface *intf) { struct usb_xpad *xpad = usb_get_intfdata(intf); struct input_dev *input = xpad->dev; - int retval = 0; - if (xpad->xtype == XTYPE_XBOX360W) { - retval = xpad360w_start_input(xpad); - } else { - mutex_lock(&input->mutex); - if (input_device_enabled(input)) { - retval = xpad_start_input(xpad); - } else if (xpad->xtype == XTYPE_XBOXONE) { - /* - * Even if there are no users, we'll send Xbox One pads - * the startup sequence so they don't sit there and - * blink until somebody opens the input device again. - */ - retval = xpad_start_xbox_one(xpad); - } - mutex_unlock(&input->mutex); + if (xpad->xtype == XTYPE_XBOX360W) + return xpad360w_start_input(xpad); + + guard(mutex)(&input->mutex); + + if (input_device_enabled(input)) + return xpad_start_input(xpad); + + if (xpad->xtype == XTYPE_XBOXONE) { + /* + * Even if there are no users, we'll send Xbox One pads + * the startup sequence so they don't sit there and + * blink until somebody opens the input device again. + */ + return xpad_start_xbox_one(xpad); } - return retval; + return 0; } static struct usb_driver xpad_driver = { diff --git a/drivers/input/keyboard/adp5520-keys.c b/drivers/input/keyboard/adp5520-keys.c index 10c248f0c1fcd..980d739e45b87 100644 --- a/drivers/input/keyboard/adp5520-keys.c +++ b/drivers/input/keyboard/adp5520-keys.c @@ -181,7 +181,7 @@ static struct platform_driver adp5520_keys_driver = { .name = "adp5520-keys", }, .probe = adp5520_keys_probe, - .remove_new = adp5520_keys_remove, + .remove = adp5520_keys_remove, }; module_platform_driver(adp5520_keys_driver); diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 922d3ab998f3a..81d0876ee358e 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -411,7 +411,7 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip, unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - mutex_lock(&kpad->gpio_lock); + guard(mutex)(&kpad->gpio_lock); if (val) kpad->dat_out[bank] |= bit; @@ -420,8 +420,6 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip, adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + bank, kpad->dat_out[bank]); - - mutex_unlock(&kpad->gpio_lock); } static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) @@ -429,18 +427,13 @@ static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - int ret; - mutex_lock(&kpad->gpio_lock); + guard(mutex)(&kpad->gpio_lock); kpad->dir[bank] &= ~bit; - ret = adp5589_write(kpad->client, - kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, - kpad->dir[bank]); - - mutex_unlock(&kpad->gpio_lock); - - return ret; + return adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, + kpad->dir[bank]); } static int adp5589_gpio_direction_output(struct gpio_chip *chip, @@ -449,9 +442,9 @@ static int adp5589_gpio_direction_output(struct gpio_chip *chip, struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - int ret; + int error; - mutex_lock(&kpad->gpio_lock); + guard(mutex)(&kpad->gpio_lock); kpad->dir[bank] |= bit; @@ -460,15 +453,19 @@ static int adp5589_gpio_direction_output(struct gpio_chip *chip, else kpad->dat_out[bank] &= ~bit; - ret = adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) - + bank, kpad->dat_out[bank]); - ret |= adp5589_write(kpad->client, - kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, - kpad->dir[bank]); + error = adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + bank, + kpad->dat_out[bank]); + if (error) + return error; - mutex_unlock(&kpad->gpio_lock); + error = adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, + kpad->dir[bank]); + if (error) + return error; - return ret; + return 0; } static int adp5589_build_gpiomap(struct adp5589_kpad *kpad, diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index 2c993fa8306a0..b5ff71cd5a701 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -717,9 +717,7 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi); static void applespi_msg_complete(struct applespi_data *applespi, bool is_write_msg, bool is_read_compl) { - unsigned long flags; - - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); if (is_read_compl) applespi->read_active = false; @@ -733,8 +731,6 @@ static void applespi_msg_complete(struct applespi_data *applespi, applespi->cmd_msg_queued = 0; applespi_send_cmd_msg(applespi); } - - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); } static void applespi_async_write_complete(void *context) @@ -888,33 +884,22 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi) static void applespi_init(struct applespi_data *applespi, bool is_resume) { - unsigned long flags; - - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); if (is_resume) applespi->want_mt_init_cmd = true; else applespi->want_tp_info_cmd = true; applespi_send_cmd_msg(applespi); - - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); } static int applespi_set_capsl_led(struct applespi_data *applespi, bool capslock_on) { - unsigned long flags; - int sts; - - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); applespi->want_cl_led_on = capslock_on; - sts = applespi_send_cmd_msg(applespi); - - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); - - return sts; + return applespi_send_cmd_msg(applespi); } static void applespi_set_bl_level(struct led_classdev *led_cdev, @@ -922,9 +907,8 @@ static void applespi_set_bl_level(struct led_classdev *led_cdev, { struct applespi_data *applespi = container_of(led_cdev, struct applespi_data, backlight_info); - unsigned long flags; - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); if (value == 0) { applespi->want_bl_level = value; @@ -940,8 +924,6 @@ static void applespi_set_bl_level(struct led_classdev *led_cdev, } applespi_send_cmd_msg(applespi); - - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); } static int applespi_event(struct input_dev *dev, unsigned int type, @@ -1427,9 +1409,7 @@ static void applespi_got_data(struct applespi_data *applespi) /* process packet header */ if (!applespi_verify_crc(applespi, applespi->rx_buffer, APPLESPI_PACKET_SIZE)) { - unsigned long flags; - - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); if (applespi->drain) { applespi->read_active = false; @@ -1438,8 +1418,6 @@ static void applespi_got_data(struct applespi_data *applespi) wake_up_all(&applespi->drain_complete); } - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); - return; } @@ -1572,11 +1550,10 @@ static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context) { struct applespi_data *applespi = context; int sts; - unsigned long flags; trace_applespi_irq_received(ET_RD_IRQ, PT_READ); - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); if (!applespi->suspended) { sts = applespi_async(applespi, &applespi->rd_m, @@ -1589,8 +1566,6 @@ static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context) applespi->read_active = true; } - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); - return ACPI_INTERRUPT_HANDLED; } @@ -1818,29 +1793,21 @@ static int applespi_probe(struct spi_device *spi) static void applespi_drain_writes(struct applespi_data *applespi) { - unsigned long flags; - - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); applespi->drain = true; wait_event_lock_irq(applespi->drain_complete, !applespi->write_active, applespi->cmd_msg_lock); - - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); } static void applespi_drain_reads(struct applespi_data *applespi) { - unsigned long flags; - - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + guard(spinlock_irqsave)(&applespi->cmd_msg_lock); wait_event_lock_irq(applespi->drain_complete, !applespi->read_active, applespi->cmd_msg_lock); applespi->suspended = true; - - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); } static void applespi_remove(struct spi_device *spi) @@ -1907,21 +1874,18 @@ static int applespi_resume(struct device *dev) struct spi_device *spi = to_spi_device(dev); struct applespi_data *applespi = spi_get_drvdata(spi); acpi_status acpi_sts; - unsigned long flags; /* ensure our flags and state reflect a newly resumed device */ - spin_lock_irqsave(&applespi->cmd_msg_lock, flags); - - applespi->drain = false; - applespi->have_cl_led_on = false; - applespi->have_bl_level = 0; - applespi->cmd_msg_queued = 0; - applespi->read_active = false; - applespi->write_active = false; - - applespi->suspended = false; + scoped_guard(spinlock_irqsave, &applespi->cmd_msg_lock) { + applespi->drain = false; + applespi->have_cl_led_on = false; + applespi->have_bl_level = 0; + applespi->cmd_msg_queued = 0; + applespi->read_active = false; + applespi->write_active = false; - spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + applespi->suspended = false; + } /* switch on the SPI interface */ applespi_enable_spi(applespi); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 5855d4fc6e6a4..ec94fcfa4cdeb 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -713,9 +713,9 @@ static int atkbd_event(struct input_dev *dev, static inline void atkbd_enable(struct atkbd *atkbd) { - serio_pause_rx(atkbd->ps2dev.serio); + guard(serio_pause_rx)(atkbd->ps2dev.serio); + atkbd->enabled = true; - serio_continue_rx(atkbd->ps2dev.serio); } /* @@ -725,9 +725,9 @@ static inline void atkbd_enable(struct atkbd *atkbd) static inline void atkbd_disable(struct atkbd *atkbd) { - serio_pause_rx(atkbd->ps2dev.serio); + guard(serio_pause_rx)(atkbd->ps2dev.serio); + atkbd->enabled = false; - serio_continue_rx(atkbd->ps2dev.serio); } static int atkbd_activate(struct atkbd *atkbd) diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index b21ef9d6ff9d6..0c17cbaa3d27a 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -416,7 +416,7 @@ static int cap11xx_led_set(struct led_classdev *cdev, static int cap11xx_init_leds(struct device *dev, struct cap11xx_priv *priv, int num_leds) { - struct device_node *node = dev->of_node, *child; + struct device_node *node = dev->of_node; struct cap11xx_led *led; int cnt = of_get_child_count(node); int error; @@ -445,7 +445,7 @@ static int cap11xx_init_leds(struct device *dev, if (error) return error; - for_each_child_of_node(node, child) { + for_each_child_of_node_scoped(node, child) { u32 reg; led->cdev.name = @@ -458,19 +458,15 @@ static int cap11xx_init_leds(struct device *dev, led->cdev.brightness = LED_OFF; error = of_property_read_u32(child, "reg", ®); - if (error != 0 || reg >= num_leds) { - of_node_put(child); + if (error != 0 || reg >= num_leds) return -EINVAL; - } led->reg = reg; led->priv = priv; error = devm_led_classdev_register(dev, &led->cdev); - if (error) { - of_node_put(child); + if (error) return error; - } priv->num_leds++; led++; diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 4c81b20ff6af9..c1e53d87c8a75 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -770,7 +770,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); static struct platform_driver cros_ec_keyb_driver = { .probe = cros_ec_keyb_probe, - .remove_new = cros_ec_keyb_remove, + .remove = cros_ec_keyb_remove, .driver = { .name = "cros-ec-keyb", .dev_groups = cros_ec_keyb_groups, diff --git a/drivers/input/keyboard/cypress-sf.c b/drivers/input/keyboard/cypress-sf.c index eb1d0720784d9..335b72efc5aa1 100644 --- a/drivers/input/keyboard/cypress-sf.c +++ b/drivers/input/keyboard/cypress-sf.c @@ -208,7 +208,7 @@ static int cypress_sf_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops, cypress_sf_suspend, cypress_sf_resume); -static struct i2c_device_id cypress_sf_id_table[] = { +static const struct i2c_device_id cypress_sf_id_table[] = { { CYPRESS_SF_DEV_NAME }, { } }; diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index dcbc50304a5a4..817c23438f6e5 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -168,15 +168,13 @@ static int ep93xx_keypad_suspend(struct device *dev) struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (keypad->enabled) { clk_disable(keypad->clk); keypad->enabled = false; } - mutex_unlock(&input_dev->mutex); - return 0; } @@ -186,7 +184,7 @@ static int ep93xx_keypad_resume(struct device *dev) struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) { if (!keypad->enabled) { @@ -196,8 +194,6 @@ static int ep93xx_keypad_resume(struct device *dev) } } - mutex_unlock(&input_dev->mutex); - return 0; } @@ -289,7 +285,7 @@ static struct platform_driver ep93xx_keypad_driver = { .of_match_table = ep93xx_keypad_of_ids, }, .probe = ep93xx_keypad_probe, - .remove_new = ep93xx_keypad_remove, + .remove = ep93xx_keypad_remove, }; module_platform_driver(ep93xx_keypad_driver); diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index c1a4d5055de63..c8d8d0ea35b0c 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -180,9 +180,8 @@ static irqreturn_t hil_interrupt(int irq, void *handle) /* send a command to the HIL */ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) { - unsigned long flags; + guard(spinlock_irqsave)(&hil_dev.lock); - spin_lock_irqsave(&hil_dev.lock, flags); while (hil_busy()) /* wait */; hil_command(cmd); @@ -191,7 +190,6 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) /* wait */; hil_write_data(*(data++)); } - spin_unlock_irqrestore(&hil_dev.lock, flags); } diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index e15a93619e827..b92268ddfd842 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -521,13 +521,11 @@ static int __maybe_unused imx_kbd_noirq_suspend(struct device *dev) struct input_dev *input_dev = kbd->input_dev; unsigned short reg_val = readw(kbd->mmio_base + KPSR); - /* imx kbd can wake up system even clock is disabled */ - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - clk_disable_unprepare(kbd->clk); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + /* imx kbd can wake up system even clock is disabled */ + if (input_device_enabled(input_dev)) + clk_disable_unprepare(kbd->clk); + } if (device_may_wakeup(&pdev->dev)) { if (reg_val & KBD_STAT_KPKD) @@ -547,23 +545,20 @@ static int __maybe_unused imx_kbd_noirq_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct imx_keypad *kbd = platform_get_drvdata(pdev); struct input_dev *input_dev = kbd->input_dev; - int ret = 0; + int error; if (device_may_wakeup(&pdev->dev)) disable_irq_wake(kbd->irq); - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) { - ret = clk_prepare_enable(kbd->clk); - if (ret) - goto err_clk; + error = clk_prepare_enable(kbd->clk); + if (error) + return error; } -err_clk: - mutex_unlock(&input_dev->mutex); - - return ret; + return 0; } static const struct dev_pm_ops imx_kbd_pm_ops = { diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c index 1d71dd79ffd28..58631bf7ce556 100644 --- a/drivers/input/keyboard/ipaq-micro-keys.c +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -54,18 +54,18 @@ static void micro_key_receive(void *data, int len, unsigned char *msg) static void micro_key_start(struct ipaq_micro_keys *keys) { - spin_lock(&keys->micro->lock); + guard(spinlock)(&keys->micro->lock); + keys->micro->key = micro_key_receive; keys->micro->key_data = keys; - spin_unlock(&keys->micro->lock); } static void micro_key_stop(struct ipaq_micro_keys *keys) { - spin_lock(&keys->micro->lock); + guard(spinlock)(&keys->micro->lock); + keys->micro->key = NULL; keys->micro->key_data = NULL; - spin_unlock(&keys->micro->lock); } static int micro_key_open(struct input_dev *input) @@ -141,13 +141,11 @@ static int micro_key_resume(struct device *dev) struct ipaq_micro_keys *keys = dev_get_drvdata(dev); struct input_dev *input = keys->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) micro_key_start(keys); - mutex_unlock(&input->mutex); - return 0; } diff --git a/drivers/input/keyboard/iqs62x-keys.c b/drivers/input/keyboard/iqs62x-keys.c index 1315b0f0862fc..b086c7b28d5c1 100644 --- a/drivers/input/keyboard/iqs62x-keys.c +++ b/drivers/input/keyboard/iqs62x-keys.c @@ -323,7 +323,7 @@ static struct platform_driver iqs62x_keys_platform_driver = { .name = "iqs62x-keys", }, .probe = iqs62x_keys_probe, - .remove_new = iqs62x_keys_remove, + .remove = iqs62x_keys_remove, }; module_platform_driver(iqs62x_keys_platform_driver); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index cf67ba13477a4..e26bf29563446 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -350,11 +350,11 @@ static int lm8323_configure(struct lm8323_chip *lm) static void pwm_done(struct lm8323_pwm *pwm) { - mutex_lock(&pwm->lock); + guard(mutex)(&pwm->lock); + pwm->running = false; if (pwm->desired_brightness != pwm->brightness) schedule_work(&pwm->work); - mutex_unlock(&pwm->lock); } /* @@ -367,7 +367,7 @@ static irqreturn_t lm8323_irq(int irq, void *_lm) u8 ints; int i; - mutex_lock(&lm->lock); + guard(mutex)(&lm->lock); while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) { if (likely(ints & INT_KEYPAD)) @@ -394,8 +394,6 @@ static irqreturn_t lm8323_irq(int irq, void *_lm) } } - mutex_unlock(&lm->lock); - return IRQ_HANDLED; } @@ -445,7 +443,7 @@ static void lm8323_pwm_work(struct work_struct *work) u16 pwm_cmds[3]; int num_cmds = 0; - mutex_lock(&pwm->lock); + guard(mutex)(&pwm->lock); /* * Do nothing if we're already at the requested level, @@ -454,7 +452,7 @@ static void lm8323_pwm_work(struct work_struct *work) * finishes. */ if (pwm->running || pwm->desired_brightness == pwm->brightness) - goto out; + return; kill = (pwm->desired_brightness == 0); up = (pwm->desired_brightness > pwm->brightness); @@ -489,9 +487,6 @@ static void lm8323_pwm_work(struct work_struct *work) lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds); pwm->brightness = pwm->desired_brightness; - - out: - mutex_unlock(&pwm->lock); } static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev, @@ -500,9 +495,9 @@ static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev, struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev); struct lm8323_chip *lm = pwm->chip; - mutex_lock(&pwm->lock); - pwm->desired_brightness = brightness; - mutex_unlock(&pwm->lock); + scoped_guard(mutex, &pwm->lock) { + pwm->desired_brightness = brightness; + } if (in_interrupt()) { schedule_work(&pwm->work); @@ -510,12 +505,12 @@ static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev, /* * Schedule PWM work as usual unless we are going into suspend */ - mutex_lock(&lm->lock); - if (likely(!lm->pm_suspend)) - schedule_work(&pwm->work); - else - lm8323_pwm_work(&pwm->work); - mutex_unlock(&lm->lock); + scoped_guard(mutex, &lm->lock) { + if (likely(!lm->pm_suspend)) + schedule_work(&pwm->work); + else + lm8323_pwm_work(&pwm->work); + } } } @@ -608,9 +603,9 @@ static ssize_t lm8323_set_disable(struct device *dev, if (ret) return ret; - mutex_lock(&lm->lock); + guard(mutex)(&lm->lock); + lm->kp_enabled = !i; - mutex_unlock(&lm->lock); return count; } @@ -758,9 +753,9 @@ static int lm8323_suspend(struct device *dev) irq_set_irq_wake(client->irq, 0); disable_irq(client->irq); - mutex_lock(&lm->lock); - lm->pm_suspend = true; - mutex_unlock(&lm->lock); + scoped_guard(mutex, &lm->lock) { + lm->pm_suspend = true; + } for (i = 0; i < 3; i++) if (lm->pwm[i].enabled) @@ -775,9 +770,9 @@ static int lm8323_resume(struct device *dev) struct lm8323_chip *lm = i2c_get_clientdata(client); int i; - mutex_lock(&lm->lock); - lm->pm_suspend = false; - mutex_unlock(&lm->lock); + scoped_guard(mutex, &lm->lock) { + lm->pm_suspend = false; + } for (i = 0; i < 3; i++) if (lm->pwm[i].enabled) diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c index 4b0f8323c4922..c501a93a44178 100644 --- a/drivers/input/keyboard/locomokbd.c +++ b/drivers/input/keyboard/locomokbd.c @@ -112,11 +112,10 @@ static inline void locomokbd_reset_col(unsigned long membase, int col) static void locomokbd_scankeyboard(struct locomokbd *locomokbd) { unsigned int row, col, rowd; - unsigned long flags; unsigned int num_pressed; unsigned long membase = locomokbd->base; - spin_lock_irqsave(&locomokbd->lock, flags); + guard(spinlock_irqsave)(&locomokbd->lock); locomokbd_charge_all(membase); @@ -167,8 +166,6 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd) mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL); else locomokbd->count_cancel = 0; - - spin_unlock_irqrestore(&locomokbd->lock, flags); } /* diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 423035be86fb8..2392e7ec3b191 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -262,7 +262,7 @@ static int lpc32xx_kscan_suspend(struct device *dev) struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); struct input_dev *input = kscandat->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) { /* Clear IRQ and disable clock */ @@ -270,7 +270,6 @@ static int lpc32xx_kscan_suspend(struct device *dev) clk_disable_unprepare(kscandat->clk); } - mutex_unlock(&input->mutex); return 0; } @@ -279,19 +278,20 @@ static int lpc32xx_kscan_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); struct input_dev *input = kscandat->input; - int retval = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) { /* Enable clock and clear IRQ */ - retval = clk_prepare_enable(kscandat->clk); - if (retval == 0) - writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); + error = clk_prepare_enable(kscandat->clk); + if (error) + return error; + + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); } - mutex_unlock(&input->mutex); - return retval; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend, diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 91a1d29581094..1a8f1fa53fbb4 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -132,14 +132,11 @@ static void dc_kbd_callback(struct mapleq *mq) * We should always get the lock because the only * time it may be locked is if the driver is in the cleanup phase. */ - if (likely(mutex_trylock(&maple_keyb_mutex))) { - + scoped_guard(mutex_try, &maple_keyb_mutex) { if (buf[1] == mapledev->function) { memcpy(kbd->new, buf + 2, 8); dc_scan_kbd(kbd); } - - mutex_unlock(&maple_keyb_mutex); } } @@ -211,14 +208,12 @@ static int remove_maple_kbd(struct device *dev) struct maple_device *mdev = to_maple_dev(dev); struct dc_kbd *kbd = maple_get_drvdata(mdev); - mutex_lock(&maple_keyb_mutex); + guard(mutex)(&maple_keyb_mutex); input_unregister_device(kbd->dev); kfree(kbd); maple_set_drvdata(mdev, NULL); - - mutex_unlock(&maple_keyb_mutex); return 0; } diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 3c38bae576edd..2a3b3bfc2878b 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -158,18 +157,17 @@ static void matrix_keypad_scan(struct work_struct *work) activate_all_cols(keypad, true); /* Enable IRQs again */ - spin_lock_irq(&keypad->lock); - keypad->scan_pending = false; - enable_row_irqs(keypad); - spin_unlock_irq(&keypad->lock); + scoped_guard(spinlock_irq, &keypad->lock) { + keypad->scan_pending = false; + enable_row_irqs(keypad); + } } static irqreturn_t matrix_keypad_interrupt(int irq, void *id) { struct matrix_keypad *keypad = id; - unsigned long flags; - spin_lock_irqsave(&keypad->lock, flags); + guard(spinlock_irqsave)(&keypad->lock); /* * See if another IRQ beaten us to it and scheduled the @@ -185,7 +183,6 @@ static irqreturn_t matrix_keypad_interrupt(int irq, void *id) msecs_to_jiffies(keypad->debounce_ms)); out: - spin_unlock_irqrestore(&keypad->lock, flags); return IRQ_HANDLED; } @@ -209,9 +206,9 @@ static void matrix_keypad_stop(struct input_dev *dev) { struct matrix_keypad *keypad = input_get_drvdata(dev); - spin_lock_irq(&keypad->lock); - keypad->stopped = true; - spin_unlock_irq(&keypad->lock); + scoped_guard(spinlock_irq, &keypad->lock) { + keypad->stopped = true; + } flush_delayed_work(&keypad->work); /* diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 21827d2497fa7..bd1a944ded46f 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -82,42 +82,6 @@ static const struct mpr121_init_register init_reg_table[] = { { AUTO_CONFIG_CTRL_ADDR, 0x0b }, }; -static void mpr121_vdd_supply_disable(void *data) -{ - struct regulator *vdd_supply = data; - - regulator_disable(vdd_supply); -} - -static struct regulator *mpr121_vdd_supply_init(struct device *dev) -{ - struct regulator *vdd_supply; - int err; - - vdd_supply = devm_regulator_get(dev, "vdd"); - if (IS_ERR(vdd_supply)) { - dev_err(dev, "failed to get vdd regulator: %ld\n", - PTR_ERR(vdd_supply)); - return vdd_supply; - } - - err = regulator_enable(vdd_supply); - if (err) { - dev_err(dev, "failed to enable vdd regulator: %d\n", err); - return ERR_PTR(err); - } - - err = devm_add_action_or_reset(dev, mpr121_vdd_supply_disable, - vdd_supply); - if (err) { - dev_err(dev, "failed to add disable regulator action: %d\n", - err); - return ERR_PTR(err); - } - - return vdd_supply; -} - static void mpr_touchkey_report(struct input_dev *dev) { struct mpr121_touchkey *mpr121 = input_get_drvdata(dev); @@ -233,7 +197,6 @@ static int mpr121_phys_init(struct mpr121_touchkey *mpr121, static int mpr_touchkey_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct regulator *vdd_supply; int vdd_uv; struct mpr121_touchkey *mpr121; struct input_dev *input_dev; @@ -241,11 +204,9 @@ static int mpr_touchkey_probe(struct i2c_client *client) int error; int i; - vdd_supply = mpr121_vdd_supply_init(dev); - if (IS_ERR(vdd_supply)) - return PTR_ERR(vdd_supply); - - vdd_uv = regulator_get_voltage(vdd_supply); + vdd_uv = devm_regulator_get_enable_read_voltage(dev, "vdd"); + if (vdd_uv < 0) + return dev_err_probe(dev, vdd_uv, "failed to get vdd voltage\n"); mpr121 = devm_kzalloc(dev, sizeof(*mpr121), GFP_KERNEL); if (!mpr121) diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index 4364c3401ff1c..5ad6be9141603 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -307,7 +307,7 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) int error, index = 0; unsigned int keycount; struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent); - struct device_node *node = pdev->dev.of_node, *child; + struct device_node *node = pdev->dev.of_node; static const char *const irqnames[] = { "powerkey", "homekey" }; static const char *const irqnames_r[] = { "powerkey_r", "homekey_r" }; struct mtk_pmic_keys *keys; @@ -343,24 +343,20 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) return -EINVAL; } - for_each_child_of_node(node, child) { + for_each_child_of_node_scoped(node, child) { keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index]; keys->keys[index].irq = platform_get_irq_byname(pdev, irqnames[index]); - if (keys->keys[index].irq < 0) { - of_node_put(child); + if (keys->keys[index].irq < 0) return keys->keys[index].irq; - } if (of_device_is_compatible(node, "mediatek,mt6358-keys")) { keys->keys[index].irq_r = platform_get_irq_byname(pdev, irqnames_r[index]); - if (keys->keys[index].irq_r < 0) { - of_node_put(child); + if (keys->keys[index].irq_r < 0) return keys->keys[index].irq_r; - } } error = of_property_read_u32(child, @@ -369,7 +365,6 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) dev_err(keys->dev, "failed to read key:%d linux,keycode property: %d\n", index, error); - of_node_put(child); return error; } @@ -377,10 +372,8 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) keys->keys[index].wakeup = true; error = mtk_pmic_key_setup(keys, &keys->keys[index]); - if (error) { - of_node_put(child); + if (error) return error; - } index++; } diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 57587541110b6..9e13f3f70a81c 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -156,15 +156,15 @@ static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute if ((state != 1) && (state != 0)) return -EINVAL; - mutex_lock(&kp_enable_mutex); - if (state != kp_enable) { - if (state) - enable_irq(omap_kp->irq); - else - disable_irq(omap_kp->irq); - kp_enable = state; + scoped_guard(mutex, &kp_enable_mutex) { + if (state != kp_enable) { + if (state) + enable_irq(omap_kp->irq); + else + disable_irq(omap_kp->irq); + kp_enable = state; + } } - mutex_unlock(&kp_enable_mutex); return strnlen(buf, count); } @@ -290,7 +290,7 @@ static void omap_kp_remove(struct platform_device *pdev) static struct platform_driver omap_kp_driver = { .probe = omap_kp_probe, - .remove_new = omap_kp_remove, + .remove = omap_kp_remove, .driver = { .name = "omap-keypad", .dev_groups = omap_kp_groups, diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 040b340995d89..bffe89c0717ad 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -144,7 +144,7 @@ static void omap4_keypad_scan_keys(struct omap4_keypad *keypad_data, u64 keys) { u64 changed; - mutex_lock(&keypad_data->lock); + guard(mutex)(&keypad_data->lock); changed = keys ^ keypad_data->keys; @@ -158,8 +158,6 @@ static void omap4_keypad_scan_keys(struct omap4_keypad *keypad_data, u64 keys) omap4_keypad_report_keys(keypad_data, changed & keys, true); keypad_data->keys = keys; - - mutex_unlock(&keypad_data->lock); } /* Interrupt handlers */ @@ -487,7 +485,7 @@ MODULE_DEVICE_TABLE(of, omap_keypad_dt_match); static struct platform_driver omap4_keypad_driver = { .probe = omap4_keypad_probe, - .remove_new = omap4_keypad_remove, + .remove = omap4_keypad_remove, .driver = { .name = "omap4-keypad", .of_match_table = omap_keypad_dt_match, diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 26a005f9ace3b..35d1aa2a22a55 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -630,12 +630,10 @@ static int pmic8xxx_kp_suspend(struct device *dev) if (device_may_wakeup(dev)) { enable_irq_wake(kp->key_sense_irq); } else { - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) pmic8xxx_kp_disable(kp); - - mutex_unlock(&input_dev->mutex); } return 0; @@ -650,12 +648,10 @@ static int pmic8xxx_kp_resume(struct device *dev) if (device_may_wakeup(dev)) { disable_irq_wake(kp->key_sense_irq); } else { - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) pmic8xxx_kp_enable(kp); - - mutex_unlock(&input_dev->mutex); } return 0; diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 3724363d140e5..38ec619aa3590 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -682,7 +682,7 @@ static int pxa27x_keypad_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - int ret = 0; + int error; /* * If the keypad is used as wake up source, the clock is not turned @@ -691,19 +691,19 @@ static int pxa27x_keypad_resume(struct device *dev) if (device_may_wakeup(&pdev->dev)) { disable_irq_wake(keypad->irq); } else { - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) { /* Enable unit clock */ - ret = clk_prepare_enable(keypad->clk); - if (!ret) - pxa27x_keypad_config(keypad); - } + error = clk_prepare_enable(keypad->clk); + if (error) + return error; - mutex_unlock(&input_dev->mutex); + pxa27x_keypad_config(keypad); + } } - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops, diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index e212eff7687c0..9f1049aa3048c 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -587,7 +587,7 @@ MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); static struct platform_driver samsung_keypad_driver = { .probe = samsung_keypad_probe, - .remove_new = samsung_keypad_remove, + .remove = samsung_keypad_remove, .driver = { .name = "samsung-keypad", .of_match_table = of_match_ptr(samsung_keypad_dt_match), diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 4ea4fd25c5d20..159f41eedd410 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -319,7 +319,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops, static struct platform_driver sh_keysc_device_driver = { .probe = sh_keysc_probe, - .remove_new = sh_keysc_remove, + .remove = sh_keysc_remove, .driver = { .name = "sh_keysc", .pm = pm_sleep_ptr(&sh_keysc_dev_pm_ops), diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 1df4feb8ba017..2fae337562a23 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -274,7 +273,7 @@ static int spear_kbd_suspend(struct device *dev) struct input_dev *input_dev = kbd->input; unsigned int rate = 0, mode_ctl_reg, val; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); /* explicitly enable clock as we may program device */ clk_enable(kbd->clk); @@ -315,8 +314,6 @@ static int spear_kbd_suspend(struct device *dev) /* restore previous clk state */ clk_disable(kbd->clk); - mutex_unlock(&input_dev->mutex); - return 0; } @@ -326,7 +323,7 @@ static int spear_kbd_resume(struct device *dev) struct spear_kbd *kbd = platform_get_drvdata(pdev); struct input_dev *input_dev = kbd->input; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (device_may_wakeup(&pdev->dev)) { if (kbd->irq_wake_enabled) { @@ -342,8 +339,6 @@ static int spear_kbd_resume(struct device *dev) if (input_device_enabled(input_dev)) writel_relaxed(kbd->mode_ctl_reg, kbd->io_base + MODE_CTL_REG); - mutex_unlock(&input_dev->mutex); - return 0; } diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c index 0d27324af809b..e53ef4c670e4b 100644 --- a/drivers/input/keyboard/st-keyscan.c +++ b/drivers/input/keyboard/st-keyscan.c @@ -216,14 +216,13 @@ static int keyscan_suspend(struct device *dev) struct st_keyscan *keypad = platform_get_drvdata(pdev); struct input_dev *input = keypad->input_dev; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(dev)) enable_irq_wake(keypad->irq); else if (input_device_enabled(input)) keyscan_stop(keypad); - mutex_unlock(&input->mutex); return 0; } @@ -232,17 +231,19 @@ static int keyscan_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct st_keyscan *keypad = platform_get_drvdata(pdev); struct input_dev *input = keypad->input_dev; - int retval = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev)) { disable_irq_wake(keypad->irq); - else if (input_device_enabled(input)) - retval = keyscan_start(keypad); + } else if (input_device_enabled(input)) { + error = keyscan_start(keypad); + if (error) + return error; + } - mutex_unlock(&input->mutex); - return retval; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index ef2f440278943..0acded4fb9c9c 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -414,7 +414,7 @@ static void stmpe_keypad_remove(struct platform_device *pdev) static struct platform_driver stmpe_keypad_driver = { .driver.name = "stmpe-keypad", .probe = stmpe_keypad_probe, - .remove_new = stmpe_keypad_remove, + .remove = stmpe_keypad_remove, }; module_platform_driver(stmpe_keypad_driver); diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c index f304cab0ebdb8..5730f08f82d79 100644 --- a/drivers/input/keyboard/sun4i-lradc-keys.c +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -202,7 +201,7 @@ static void sun4i_lradc_close(struct input_dev *dev) static int sun4i_lradc_load_dt_keymap(struct device *dev, struct sun4i_lradc_data *lradc) { - struct device_node *np, *pp; + struct device_node *np; int i; int error; @@ -223,28 +222,25 @@ static int sun4i_lradc_load_dt_keymap(struct device *dev, return -ENOMEM; i = 0; - for_each_child_of_node(np, pp) { + for_each_child_of_node_scoped(np, pp) { struct sun4i_lradc_keymap *map = &lradc->chan0_map[i]; u32 channel; error = of_property_read_u32(pp, "channel", &channel); if (error || channel != 0) { dev_err(dev, "%pOFn: Inval channel prop\n", pp); - of_node_put(pp); return -EINVAL; } error = of_property_read_u32(pp, "voltage", &map->voltage); if (error) { dev_err(dev, "%pOFn: Inval voltage prop\n", pp); - of_node_put(pp); return -EINVAL; } error = of_property_read_u32(pp, "linux,code", &map->keycode); if (error) { dev_err(dev, "%pOFn: Inval linux,code prop\n", pp); - of_node_put(pp); return -EINVAL; } diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index 72fb46029710d..3299e1919b370 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -241,9 +241,8 @@ static void sunkbd_reinit(struct work_struct *work) static void sunkbd_enable(struct sunkbd *sunkbd, bool enable) { - serio_pause_rx(sunkbd->serio); - sunkbd->enabled = enable; - serio_continue_rx(sunkbd->serio); + scoped_guard(serio_pause_rx, sunkbd->serio) + sunkbd->enabled = enable; if (!enable) { wake_up_interruptible(&sunkbd->wait); diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c index 6477a41c4bac3..9159b5fec129c 100644 --- a/drivers/input/misc/88pm80x_onkey.c +++ b/drivers/input/misc/88pm80x_onkey.c @@ -141,7 +141,7 @@ static struct platform_driver pm80x_onkey_driver = { .pm = &pm80x_onkey_pm_ops, }, .probe = pm80x_onkey_probe, - .remove_new = pm80x_onkey_remove, + .remove = pm80x_onkey_remove, }; module_platform_driver(pm80x_onkey_driver); diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index 1acd8429c56c9..d106f37df6bcb 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -941,7 +941,7 @@ static irqreturn_t ad714x_interrupt_thread(int irq, void *data) struct ad714x_chip *ad714x = data; int i; - mutex_lock(&ad714x->mutex); + guard(mutex)(&ad714x->mutex); ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); @@ -954,8 +954,6 @@ static irqreturn_t ad714x_interrupt_thread(int irq, void *data) for (i = 0; i < ad714x->hw->touchpad_num; i++) ad714x_touchpad_state_machine(ad714x, i); - mutex_unlock(&ad714x->mutex); - return IRQ_HANDLED; } @@ -1169,13 +1167,11 @@ static int ad714x_suspend(struct device *dev) dev_dbg(ad714x->dev, "%s enter\n", __func__); - mutex_lock(&ad714x->mutex); + guard(mutex)(&ad714x->mutex); data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3; ad714x->write(ad714x, AD714X_PWR_CTRL, data); - mutex_unlock(&ad714x->mutex); - return 0; } @@ -1184,7 +1180,7 @@ static int ad714x_resume(struct device *dev) struct ad714x_chip *ad714x = dev_get_drvdata(dev); dev_dbg(ad714x->dev, "%s enter\n", __func__); - mutex_lock(&ad714x->mutex); + guard(mutex)(&ad714x->mutex); /* resume to non-shutdown mode */ @@ -1197,8 +1193,6 @@ static int ad714x_resume(struct device *dev) ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); - mutex_unlock(&ad714x->mutex); - return 0; } diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 795f69edb4b27..e84649af801dd 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -244,29 +244,21 @@ static int ati_remote2_open(struct input_dev *idev) if (r) { dev_err(&ar2->intf[0]->dev, "%s(): usb_autopm_get_interface() = %d\n", __func__, r); - goto fail1; + return r; } - mutex_lock(&ati_remote2_mutex); + scoped_guard(mutex, &ati_remote2_mutex) { + if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) { + r = ati_remote2_submit_urbs(ar2); + if (r) + break; + } - if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) { - r = ati_remote2_submit_urbs(ar2); - if (r) - goto fail2; + ar2->flags |= ATI_REMOTE2_OPENED; } - ar2->flags |= ATI_REMOTE2_OPENED; - - mutex_unlock(&ati_remote2_mutex); - usb_autopm_put_interface(ar2->intf[0]); - return 0; - - fail2: - mutex_unlock(&ati_remote2_mutex); - usb_autopm_put_interface(ar2->intf[0]); - fail1: return r; } @@ -276,14 +268,12 @@ static void ati_remote2_close(struct input_dev *idev) dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); - mutex_lock(&ati_remote2_mutex); + guard(mutex)(&ati_remote2_mutex); if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) ati_remote2_kill_urbs(ar2); ar2->flags &= ~ATI_REMOTE2_OPENED; - - mutex_unlock(&ati_remote2_mutex); } static void ati_remote2_input_mouse(struct ati_remote2 *ar2) @@ -713,16 +703,14 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev, return r; } - mutex_lock(&ati_remote2_mutex); - - if (mask != ar2->channel_mask) { - r = ati_remote2_setup(ar2, mask); - if (!r) - ar2->channel_mask = mask; + scoped_guard(mutex, &ati_remote2_mutex) { + if (mask != ar2->channel_mask) { + r = ati_remote2_setup(ar2, mask); + if (!r) + ar2->channel_mask = mask; + } } - mutex_unlock(&ati_remote2_mutex); - usb_autopm_put_interface(ar2->intf[0]); return r ? r : count; @@ -892,15 +880,13 @@ static int ati_remote2_suspend(struct usb_interface *interface, dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); - mutex_lock(&ati_remote2_mutex); + guard(mutex)(&ati_remote2_mutex); if (ar2->flags & ATI_REMOTE2_OPENED) ati_remote2_kill_urbs(ar2); ar2->flags |= ATI_REMOTE2_SUSPENDED; - mutex_unlock(&ati_remote2_mutex); - return 0; } @@ -917,7 +903,7 @@ static int ati_remote2_resume(struct usb_interface *interface) dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); - mutex_lock(&ati_remote2_mutex); + guard(mutex)(&ati_remote2_mutex); if (ar2->flags & ATI_REMOTE2_OPENED) r = ati_remote2_submit_urbs(ar2); @@ -925,8 +911,6 @@ static int ati_remote2_resume(struct usb_interface *interface) if (!r) ar2->flags &= ~ATI_REMOTE2_SUSPENDED; - mutex_unlock(&ati_remote2_mutex); - return r; } @@ -943,11 +927,11 @@ static int ati_remote2_reset_resume(struct usb_interface *interface) dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); - mutex_lock(&ati_remote2_mutex); + guard(mutex)(&ati_remote2_mutex); r = ati_remote2_setup(ar2, ar2->channel_mask); if (r) - goto out; + return r; if (ar2->flags & ATI_REMOTE2_OPENED) r = ati_remote2_submit_urbs(ar2); @@ -955,9 +939,6 @@ static int ati_remote2_reset_resume(struct usb_interface *interface) if (!r) ar2->flags &= ~ATI_REMOTE2_SUSPENDED; - out: - mutex_unlock(&ati_remote2_mutex); - return r; } diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index 728325a2d574a..0cfe5d4a573c6 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -355,6 +355,35 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev) __func__, error); } +static void cm109_submit_ctl(struct cm109_dev *dev) +{ + int error; + + guard(spinlock_irqsave)(&dev->ctl_submit_lock); + + dev->irq_urb_pending = 0; + + if (unlikely(dev->shutdown)) + return; + + if (dev->buzzer_state) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + dev->ctl_data->byte[HID_OR1] = dev->keybit; + dev->ctl_data->byte[HID_OR2] = dev->keybit; + + dev->buzzer_pending = 0; + dev->ctl_urb_pending = 1; + + error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); + if (error) + dev_err(&dev->intf->dev, + "%s: usb_submit_urb (urb_ctl) failed %d\n", + __func__, error); +} + /* * IRQ handler */ @@ -362,8 +391,6 @@ static void cm109_urb_irq_callback(struct urb *urb) { struct cm109_dev *dev = urb->context; const int status = urb->status; - int error; - unsigned long flags; dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n", dev->irq_data->byte[0], @@ -401,32 +428,7 @@ static void cm109_urb_irq_callback(struct urb *urb) } out: - - spin_lock_irqsave(&dev->ctl_submit_lock, flags); - - dev->irq_urb_pending = 0; - - if (likely(!dev->shutdown)) { - - if (dev->buzzer_state) - dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; - else - dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; - - dev->ctl_data->byte[HID_OR1] = dev->keybit; - dev->ctl_data->byte[HID_OR2] = dev->keybit; - - dev->buzzer_pending = 0; - dev->ctl_urb_pending = 1; - - error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); - if (error) - dev_err(&dev->intf->dev, - "%s: usb_submit_urb (urb_ctl) failed %d\n", - __func__, error); - } - - spin_unlock_irqrestore(&dev->ctl_submit_lock, flags); + cm109_submit_ctl(dev); } static void cm109_urb_ctl_callback(struct urb *urb) @@ -434,7 +436,6 @@ static void cm109_urb_ctl_callback(struct urb *urb) struct cm109_dev *dev = urb->context; const int status = urb->status; int error; - unsigned long flags; dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n", dev->ctl_data->byte[0], @@ -449,35 +450,31 @@ static void cm109_urb_ctl_callback(struct urb *urb) __func__, status); } - spin_lock_irqsave(&dev->ctl_submit_lock, flags); + guard(spinlock_irqsave)(&dev->ctl_submit_lock); dev->ctl_urb_pending = 0; - if (likely(!dev->shutdown)) { - - if (dev->buzzer_pending || status) { - dev->buzzer_pending = 0; - dev->ctl_urb_pending = 1; - cm109_submit_buzz_toggle(dev); - } else if (likely(!dev->irq_urb_pending)) { - /* ask for key data */ - dev->irq_urb_pending = 1; - error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC); - if (error) - dev_err(&dev->intf->dev, - "%s: usb_submit_urb (urb_irq) failed %d\n", - __func__, error); - } - } + if (unlikely(dev->shutdown)) + return; - spin_unlock_irqrestore(&dev->ctl_submit_lock, flags); + if (dev->buzzer_pending || status) { + dev->buzzer_pending = 0; + dev->ctl_urb_pending = 1; + cm109_submit_buzz_toggle(dev); + } else if (likely(!dev->irq_urb_pending)) { + /* ask for key data */ + dev->irq_urb_pending = 1; + error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC); + if (error) + dev_err(&dev->intf->dev, + "%s: usb_submit_urb (urb_irq) failed %d\n", + __func__, error); + } } static void cm109_toggle_buzzer_async(struct cm109_dev *dev) { - unsigned long flags; - - spin_lock_irqsave(&dev->ctl_submit_lock, flags); + guard(spinlock_irqsave)(&dev->ctl_submit_lock); if (dev->ctl_urb_pending) { /* URB completion will resubmit */ @@ -486,8 +483,6 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev) dev->ctl_urb_pending = 1; cm109_submit_buzz_toggle(dev); } - - spin_unlock_irqrestore(&dev->ctl_submit_lock, flags); } static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on) @@ -556,32 +551,30 @@ static int cm109_input_open(struct input_dev *idev) return error; } - mutex_lock(&dev->pm_mutex); - - dev->buzzer_state = 0; - dev->key_code = -1; /* no keys pressed */ - dev->keybit = 0xf; + scoped_guard(mutex, &dev->pm_mutex) { + dev->buzzer_state = 0; + dev->key_code = -1; /* no keys pressed */ + dev->keybit = 0xf; - /* issue INIT */ - dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF; - dev->ctl_data->byte[HID_OR1] = dev->keybit; - dev->ctl_data->byte[HID_OR2] = dev->keybit; - dev->ctl_data->byte[HID_OR3] = 0x00; + /* issue INIT */ + dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF; + dev->ctl_data->byte[HID_OR1] = dev->keybit; + dev->ctl_data->byte[HID_OR2] = dev->keybit; + dev->ctl_data->byte[HID_OR3] = 0x00; - dev->ctl_urb_pending = 1; - error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL); - if (error) { - dev->ctl_urb_pending = 0; - dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n", - __func__, error); - } else { - dev->open = 1; + dev->ctl_urb_pending = 1; + error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL); + if (!error) { + dev->open = 1; + return 0; + } } - mutex_unlock(&dev->pm_mutex); + dev->ctl_urb_pending = 0; + usb_autopm_put_interface(dev->intf); - if (error) - usb_autopm_put_interface(dev->intf); + dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n", + __func__, error); return error; } @@ -590,17 +583,15 @@ static void cm109_input_close(struct input_dev *idev) { struct cm109_dev *dev = input_get_drvdata(idev); - mutex_lock(&dev->pm_mutex); - - /* - * Once we are here event delivery is stopped so we - * don't need to worry about someone starting buzzer - * again - */ - cm109_stop_traffic(dev); - dev->open = 0; - - mutex_unlock(&dev->pm_mutex); + scoped_guard(mutex, &dev->pm_mutex) { + /* + * Once we are here event delivery is stopped so we + * don't need to worry about someone starting buzzer + * again + */ + cm109_stop_traffic(dev); + dev->open = 0; + } usb_autopm_put_interface(dev->intf); } @@ -823,9 +814,9 @@ static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message) dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event); - mutex_lock(&dev->pm_mutex); + guard(mutex)(&dev->pm_mutex); + cm109_stop_traffic(dev); - mutex_unlock(&dev->pm_mutex); return 0; } @@ -836,9 +827,9 @@ static int cm109_usb_resume(struct usb_interface *intf) dev_info(&intf->dev, "cm109: usb_resume\n"); - mutex_lock(&dev->pm_mutex); + guard(mutex)(&dev->pm_mutex); + cm109_restore_state(dev); - mutex_unlock(&dev->pm_mutex); return 0; } diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c index 0c68e924a1ccd..cfc12332bee14 100644 --- a/drivers/input/misc/cma3000_d0x.c +++ b/drivers/input/misc/cma3000_d0x.c @@ -217,15 +217,13 @@ static int cma3000_open(struct input_dev *input_dev) { struct cma3000_accl_data *data = input_get_drvdata(input_dev); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (!data->suspended) cma3000_poweron(data); data->opened = true; - mutex_unlock(&data->mutex); - return 0; } @@ -233,40 +231,34 @@ static void cma3000_close(struct input_dev *input_dev) { struct cma3000_accl_data *data = input_get_drvdata(input_dev); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (!data->suspended) cma3000_poweroff(data); data->opened = false; - - mutex_unlock(&data->mutex); } void cma3000_suspend(struct cma3000_accl_data *data) { - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (!data->suspended && data->opened) cma3000_poweroff(data); data->suspended = true; - - mutex_unlock(&data->mutex); } EXPORT_SYMBOL(cma3000_suspend); void cma3000_resume(struct cma3000_accl_data *data) { - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->suspended && data->opened) cma3000_poweron(data); data->suspended = false; - - mutex_unlock(&data->mutex); } EXPORT_SYMBOL(cma3000_resume); diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c index 03bdb7c26ec09..dce3b0ec8cf36 100644 --- a/drivers/input/misc/cs40l50-vibra.c +++ b/drivers/input/misc/cs40l50-vibra.c @@ -334,11 +334,12 @@ static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect, work_data.custom_len = effect->u.periodic.custom_len; work_data.vib = vib; work_data.effect = effect; - INIT_WORK(&work_data.work, cs40l50_add_worker); + INIT_WORK_ONSTACK(&work_data.work, cs40l50_add_worker); /* Push to the workqueue to serialize with playbacks */ queue_work(vib->vib_wq, &work_data.work); flush_work(&work_data.work); + destroy_work_on_stack(&work_data.work); kfree(work_data.custom_data); @@ -467,11 +468,12 @@ static int cs40l50_erase(struct input_dev *dev, int effect_id) work_data.vib = vib; work_data.effect = &dev->ff->effects[effect_id]; - INIT_WORK(&work_data.work, cs40l50_erase_worker); + INIT_WORK_ONSTACK(&work_data.work, cs40l50_erase_worker); /* Push to workqueue to serialize with playbacks */ queue_work(vib->vib_wq, &work_data.work); flush_work(&work_data.work); + destroy_work_on_stack(&work_data.work); return work_data.error; } diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c index 1629b7ea4cbd4..e4a605c6af150 100644 --- a/drivers/input/misc/da7280.c +++ b/drivers/input/misc/da7280.c @@ -1263,39 +1263,37 @@ static int da7280_suspend(struct device *dev) { struct da7280_haptic *haptics = dev_get_drvdata(dev); - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); /* * Make sure no new requests will be submitted while device is * suspended. */ - spin_lock_irq(&haptics->input_dev->event_lock); - haptics->suspended = true; - spin_unlock_irq(&haptics->input_dev->event_lock); + scoped_guard(spinlock_irq, &haptics->input_dev->event_lock) { + haptics->suspended = true; + } da7280_haptic_stop(haptics); - mutex_unlock(&haptics->input_dev->mutex); - return 0; } static int da7280_resume(struct device *dev) { struct da7280_haptic *haptics = dev_get_drvdata(dev); - int retval; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); - retval = da7280_haptic_start(haptics); - if (!retval) { - spin_lock_irq(&haptics->input_dev->event_lock); + error = da7280_haptic_start(haptics); + if (error) + return error; + + scoped_guard(spinlock_irq, &haptics->input_dev->event_lock) { haptics->suspended = false; - spin_unlock_irq(&haptics->input_dev->event_lock); } - mutex_unlock(&haptics->input_dev->mutex); - return retval; + return 0; } #ifdef CONFIG_OF diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 7a1122e1efb9c..cc23625019e34 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -140,8 +140,8 @@ static void da9052_onkey_remove(struct platform_device *pdev) static struct platform_driver da9052_onkey_driver = { .probe = da9052_onkey_probe, - .remove_new = da9052_onkey_remove, - .driver = { + .remove = da9052_onkey_remove, + .driver = { .name = "da9052-onkey", }, }; diff --git a/drivers/input/misc/da9055_onkey.c b/drivers/input/misc/da9055_onkey.c index 871812f1b398e..3e4fc2f010119 100644 --- a/drivers/input/misc/da9055_onkey.c +++ b/drivers/input/misc/da9055_onkey.c @@ -145,8 +145,8 @@ static void da9055_onkey_remove(struct platform_device *pdev) static struct platform_driver da9055_onkey_driver = { .probe = da9055_onkey_probe, - .remove_new = da9055_onkey_remove, - .driver = { + .remove = da9055_onkey_remove, + .driver = { .name = "da9055-onkey", }, }; diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 61b503835aa6d..96cd6a078c8ab 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -537,64 +537,62 @@ static int drv260x_probe(struct i2c_client *client) static int drv260x_suspend(struct device *dev) { struct drv260x_data *haptics = dev_get_drvdata(dev); - int ret = 0; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); if (input_device_enabled(haptics->input_dev)) { - ret = regmap_update_bits(haptics->regmap, - DRV260X_MODE, - DRV260X_STANDBY_MASK, - DRV260X_STANDBY); - if (ret) { + error = regmap_update_bits(haptics->regmap, + DRV260X_MODE, + DRV260X_STANDBY_MASK, + DRV260X_STANDBY); + if (error) { dev_err(dev, "Failed to set standby mode\n"); - goto out; + return error; } gpiod_set_value(haptics->enable_gpio, 0); - ret = regulator_disable(haptics->regulator); - if (ret) { + error = regulator_disable(haptics->regulator); + if (error) { dev_err(dev, "Failed to disable regulator\n"); regmap_update_bits(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY_MASK, 0); + return error; } } -out: - mutex_unlock(&haptics->input_dev->mutex); - return ret; + + return 0; } static int drv260x_resume(struct device *dev) { struct drv260x_data *haptics = dev_get_drvdata(dev); - int ret = 0; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); if (input_device_enabled(haptics->input_dev)) { - ret = regulator_enable(haptics->regulator); - if (ret) { + error = regulator_enable(haptics->regulator); + if (error) { dev_err(dev, "Failed to enable regulator\n"); - goto out; + return error; } - ret = regmap_update_bits(haptics->regmap, - DRV260X_MODE, - DRV260X_STANDBY_MASK, 0); - if (ret) { + error = regmap_update_bits(haptics->regmap, + DRV260X_MODE, + DRV260X_STANDBY_MASK, 0); + if (error) { dev_err(dev, "Failed to unset standby mode\n"); regulator_disable(haptics->regulator); - goto out; + return error; } gpiod_set_value(haptics->enable_gpio, 1); } -out: - mutex_unlock(&haptics->input_dev->mutex); - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c index f98e4d7653077..46842cb8ddd78 100644 --- a/drivers/input/misc/drv2665.c +++ b/drivers/input/misc/drv2665.c @@ -15,7 +15,7 @@ #include #include -/* Contol registers */ +/* Control registers */ #define DRV2665_STATUS 0x00 #define DRV2665_CTRL_1 0x01 #define DRV2665_CTRL_2 0x02 @@ -225,59 +225,57 @@ static int drv2665_probe(struct i2c_client *client) static int drv2665_suspend(struct device *dev) { struct drv2665_data *haptics = dev_get_drvdata(dev); - int ret = 0; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); if (input_device_enabled(haptics->input_dev)) { - ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, - DRV2665_STANDBY, DRV2665_STANDBY); - if (ret) { + error = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, DRV2665_STANDBY); + if (error) { dev_err(dev, "Failed to set standby mode\n"); regulator_disable(haptics->regulator); - goto out; + return error; } - ret = regulator_disable(haptics->regulator); - if (ret) { + error = regulator_disable(haptics->regulator); + if (error) { dev_err(dev, "Failed to disable regulator\n"); regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, DRV2665_STANDBY, 0); + return error; } } -out: - mutex_unlock(&haptics->input_dev->mutex); - return ret; + + return 0; } static int drv2665_resume(struct device *dev) { struct drv2665_data *haptics = dev_get_drvdata(dev); - int ret = 0; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); if (input_device_enabled(haptics->input_dev)) { - ret = regulator_enable(haptics->regulator); - if (ret) { + error = regulator_enable(haptics->regulator); + if (error) { dev_err(dev, "Failed to enable regulator\n"); - goto out; + return error; } - ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, - DRV2665_STANDBY, 0); - if (ret) { + error = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, 0); + if (error) { dev_err(dev, "Failed to unset standby mode\n"); regulator_disable(haptics->regulator); - goto out; + return error; } } -out: - mutex_unlock(&haptics->input_dev->mutex); - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume); diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index ad49845374b9b..f952a24ec595b 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -16,7 +16,7 @@ #include #include -/* Contol registers */ +/* Control registers */ #define DRV2667_STATUS 0x00 #define DRV2667_CTRL_1 0x01 #define DRV2667_CTRL_2 0x02 @@ -402,59 +402,57 @@ static int drv2667_probe(struct i2c_client *client) static int drv2667_suspend(struct device *dev) { struct drv2667_data *haptics = dev_get_drvdata(dev); - int ret = 0; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); if (input_device_enabled(haptics->input_dev)) { - ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, - DRV2667_STANDBY, DRV2667_STANDBY); - if (ret) { + error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, + DRV2667_STANDBY, DRV2667_STANDBY); + if (error) { dev_err(dev, "Failed to set standby mode\n"); regulator_disable(haptics->regulator); - goto out; + return error; } - ret = regulator_disable(haptics->regulator); - if (ret) { + error = regulator_disable(haptics->regulator); + if (error) { dev_err(dev, "Failed to disable regulator\n"); regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, DRV2667_STANDBY, 0); + return error; } } -out: - mutex_unlock(&haptics->input_dev->mutex); - return ret; + + return 0; } static int drv2667_resume(struct device *dev) { struct drv2667_data *haptics = dev_get_drvdata(dev); - int ret = 0; + int error; - mutex_lock(&haptics->input_dev->mutex); + guard(mutex)(&haptics->input_dev->mutex); if (input_device_enabled(haptics->input_dev)) { - ret = regulator_enable(haptics->regulator); - if (ret) { + error = regulator_enable(haptics->regulator); + if (error) { dev_err(dev, "Failed to enable regulator\n"); - goto out; + return error; } - ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, - DRV2667_STANDBY, 0); - if (ret) { + error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, + DRV2667_STANDBY, 0); + if (error) { dev_err(dev, "Failed to unset standby mode\n"); regulator_disable(haptics->regulator); - goto out; + return error; } } -out: - mutex_unlock(&haptics->input_dev->mutex); - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume); diff --git a/drivers/input/misc/ibm-panel.c b/drivers/input/misc/ibm-panel.c index 867ac7aa10d2c..aa48f62d7ea05 100644 --- a/drivers/input/misc/ibm-panel.c +++ b/drivers/input/misc/ibm-panel.c @@ -77,12 +77,11 @@ static void ibm_panel_process_command(struct ibm_panel *panel) static int ibm_panel_i2c_slave_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val) { - unsigned long flags; struct ibm_panel *panel = i2c_get_clientdata(client); dev_dbg(&panel->input->dev, "event: %u data: %02x\n", event, *val); - spin_lock_irqsave(&panel->lock, flags); + guard(spinlock_irqsave)(&panel->lock); switch (event) { case I2C_SLAVE_STOP: @@ -114,8 +113,6 @@ static int ibm_panel_i2c_slave_cb(struct i2c_client *client, break; } - spin_unlock_irqrestore(&panel->lock, flags); - return 0; } diff --git a/drivers/input/misc/ideapad_slidebar.c b/drivers/input/misc/ideapad_slidebar.c index fa4e7f67d7136..f6e5fc807b4df 100644 --- a/drivers/input/misc/ideapad_slidebar.c +++ b/drivers/input/misc/ideapad_slidebar.c @@ -23,7 +23,7 @@ * * The value is in byte range, however, I only figured out * how bits 0b10011001 work. Some other bits, probably, - * are meaningfull too. + * are meaningful too. * * Possible states: * @@ -95,41 +95,29 @@ static struct platform_device *slidebar_platform_dev; static u8 slidebar_pos_get(void) { - u8 res; - unsigned long flags; + guard(spinlock_irqsave)(&io_lock); - spin_lock_irqsave(&io_lock, flags); outb(0xf4, 0xff29); outb(0xbf, 0xff2a); - res = inb(0xff2b); - spin_unlock_irqrestore(&io_lock, flags); - - return res; + return inb(0xff2b); } static u8 slidebar_mode_get(void) { - u8 res; - unsigned long flags; + guard(spinlock_irqsave)(&io_lock); - spin_lock_irqsave(&io_lock, flags); outb(0xf7, 0xff29); outb(0x8b, 0xff2a); - res = inb(0xff2b); - spin_unlock_irqrestore(&io_lock, flags); - - return res; + return inb(0xff2b); } static void slidebar_mode_set(u8 mode) { - unsigned long flags; + guard(spinlock_irqsave)(&io_lock); - spin_lock_irqsave(&io_lock, flags); outb(0xf7, 0xff29); outb(0x8b, 0xff2a); outb(mode, 0xff2b); - spin_unlock_irqrestore(&io_lock, flags); } static bool slidebar_i8042_filter(unsigned char data, unsigned char str, @@ -267,7 +255,7 @@ static struct platform_driver slidebar_drv = { .driver = { .name = "ideapad_slidebar", }, - .remove_new = ideapad_remove, + .remove = ideapad_remove, }; static int __init ideapad_dmi_check(const struct dmi_system_id *id) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 4215f9b9c2b07..d9ee14b1f4518 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -739,7 +739,7 @@ static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu) { int error; - /* Execute jump to the bootoloader */ + /* Execute jump to the bootloader */ error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0); if (error) { dev_err(pcu->dev, diff --git a/drivers/input/misc/iqs269a.c b/drivers/input/misc/iqs269a.c index 843f8a3f3410d..1851848e2cd38 100644 --- a/drivers/input/misc/iqs269a.c +++ b/drivers/input/misc/iqs269a.c @@ -365,7 +365,7 @@ static int iqs269_ati_mode_set(struct iqs269_private *iqs269, if (mode > IQS269_CHx_ENG_A_ATI_MODE_MAX) return -EINVAL; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); engine_a = be16_to_cpu(ch_reg[ch_num].engine_a); @@ -375,8 +375,6 @@ static int iqs269_ati_mode_set(struct iqs269_private *iqs269, ch_reg[ch_num].engine_a = cpu_to_be16(engine_a); iqs269->ati_current = false; - mutex_unlock(&iqs269->lock); - return 0; } @@ -389,9 +387,9 @@ static int iqs269_ati_mode_get(struct iqs269_private *iqs269, if (ch_num >= IQS269_NUM_CH) return -EINVAL; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); + engine_a = be16_to_cpu(ch_reg[ch_num].engine_a); - mutex_unlock(&iqs269->lock); engine_a &= IQS269_CHx_ENG_A_ATI_MODE_MASK; *mode = (engine_a >> IQS269_CHx_ENG_A_ATI_MODE_SHIFT); @@ -429,7 +427,7 @@ static int iqs269_ati_base_set(struct iqs269_private *iqs269, return -EINVAL; } - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); engine_b = be16_to_cpu(ch_reg[ch_num].engine_b); @@ -439,8 +437,6 @@ static int iqs269_ati_base_set(struct iqs269_private *iqs269, ch_reg[ch_num].engine_b = cpu_to_be16(engine_b); iqs269->ati_current = false; - mutex_unlock(&iqs269->lock); - return 0; } @@ -453,9 +449,9 @@ static int iqs269_ati_base_get(struct iqs269_private *iqs269, if (ch_num >= IQS269_NUM_CH) return -EINVAL; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); + engine_b = be16_to_cpu(ch_reg[ch_num].engine_b); - mutex_unlock(&iqs269->lock); switch (engine_b & IQS269_CHx_ENG_B_ATI_BASE_MASK) { case IQS269_CHx_ENG_B_ATI_BASE_75: @@ -491,7 +487,7 @@ static int iqs269_ati_target_set(struct iqs269_private *iqs269, if (target > IQS269_CHx_ENG_B_ATI_TARGET_MAX) return -EINVAL; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); engine_b = be16_to_cpu(ch_reg[ch_num].engine_b); @@ -501,8 +497,6 @@ static int iqs269_ati_target_set(struct iqs269_private *iqs269, ch_reg[ch_num].engine_b = cpu_to_be16(engine_b); iqs269->ati_current = false; - mutex_unlock(&iqs269->lock); - return 0; } @@ -515,10 +509,9 @@ static int iqs269_ati_target_get(struct iqs269_private *iqs269, if (ch_num >= IQS269_NUM_CH) return -EINVAL; - mutex_lock(&iqs269->lock); - engine_b = be16_to_cpu(ch_reg[ch_num].engine_b); - mutex_unlock(&iqs269->lock); + guard(mutex)(&iqs269->lock); + engine_b = be16_to_cpu(ch_reg[ch_num].engine_b); *target = (engine_b & IQS269_CHx_ENG_B_ATI_TARGET_MASK) * 32; return 0; @@ -557,7 +550,6 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, const struct fwnode_handle *ch_node) { struct i2c_client *client = iqs269->client; - struct fwnode_handle *ev_node; struct iqs269_ch_reg *ch_reg; u16 engine_a, engine_b; unsigned int reg, val; @@ -734,8 +726,9 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, } for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) { - ev_node = fwnode_get_named_child_node(ch_node, - iqs269_events[i].name); + struct fwnode_handle *ev_node __free(fwnode_handle) = + fwnode_get_named_child_node(ch_node, + iqs269_events[i].name); if (!ev_node) continue; @@ -744,7 +737,6 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, dev_err(&client->dev, "Invalid channel %u threshold: %u\n", reg, val); - fwnode_handle_put(ev_node); return -EINVAL; } @@ -758,7 +750,6 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, dev_err(&client->dev, "Invalid channel %u hysteresis: %u\n", reg, val); - fwnode_handle_put(ev_node); return -EINVAL; } @@ -774,7 +765,6 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, } error = fwnode_property_read_u32(ev_node, "linux,code", &val); - fwnode_handle_put(ev_node); if (error == -EINVAL) { continue; } else if (error) { @@ -1199,7 +1189,7 @@ static int iqs269_dev_init(struct iqs269_private *iqs269) { int error; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); /* * Early revisions of silicon require the following workaround in order @@ -1210,19 +1200,19 @@ static int iqs269_dev_init(struct iqs269_private *iqs269) error = regmap_multi_reg_write(iqs269->regmap, iqs269_tws_init, ARRAY_SIZE(iqs269_tws_init)); if (error) - goto err_mutex; + return error; } error = regmap_update_bits(iqs269->regmap, IQS269_HALL_UI, IQS269_HALL_UI_ENABLE, iqs269->hall_enable ? ~0 : 0); if (error) - goto err_mutex; + return error; error = regmap_raw_write(iqs269->regmap, IQS269_SYS_SETTINGS, &iqs269->sys_reg, sizeof(iqs269->sys_reg)); if (error) - goto err_mutex; + return error; /* * The following delay gives the device time to deassert its RDY output @@ -1232,10 +1222,7 @@ static int iqs269_dev_init(struct iqs269_private *iqs269) iqs269->ati_current = true; -err_mutex: - mutex_unlock(&iqs269->lock); - - return error; + return 0; } static int iqs269_input_init(struct iqs269_private *iqs269) @@ -1580,13 +1567,11 @@ static ssize_t hall_enable_store(struct device *dev, if (error) return error; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); iqs269->hall_enable = val; iqs269->ati_current = false; - mutex_unlock(&iqs269->lock); - return count; } @@ -1643,13 +1628,11 @@ static ssize_t rx_enable_store(struct device *dev, if (val > 0xFF) return -EINVAL; - mutex_lock(&iqs269->lock); + guard(mutex)(&iqs269->lock); ch_reg[iqs269->ch_num].rx_enable = val; iqs269->ati_current = false; - mutex_unlock(&iqs269->lock); - return count; } diff --git a/drivers/input/misc/iqs626a.c b/drivers/input/misc/iqs626a.c index 0dab54d3a0603..7a6e6927f3312 100644 --- a/drivers/input/misc/iqs626a.c +++ b/drivers/input/misc/iqs626a.c @@ -462,7 +462,6 @@ iqs626_parse_events(struct iqs626_private *iqs626, { struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg; struct i2c_client *client = iqs626->client; - struct fwnode_handle *ev_node; const char *ev_name; u8 *thresh, *hyst; unsigned int val; @@ -501,6 +500,7 @@ iqs626_parse_events(struct iqs626_private *iqs626, if (!iqs626_channels[ch_id].events[i]) continue; + struct fwnode_handle *ev_node __free(fwnode_handle) = NULL; if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3) { /* * Trackpad touch events are simply described under the @@ -530,7 +530,6 @@ iqs626_parse_events(struct iqs626_private *iqs626, dev_err(&client->dev, "Invalid input type: %u\n", val); - fwnode_handle_put(ev_node); return -EINVAL; } @@ -545,7 +544,6 @@ iqs626_parse_events(struct iqs626_private *iqs626, dev_err(&client->dev, "Invalid %s channel hysteresis: %u\n", fwnode_get_name(ch_node), val); - fwnode_handle_put(ev_node); return -EINVAL; } @@ -566,7 +564,6 @@ iqs626_parse_events(struct iqs626_private *iqs626, dev_err(&client->dev, "Invalid %s channel threshold: %u\n", fwnode_get_name(ch_node), val); - fwnode_handle_put(ev_node); return -EINVAL; } @@ -575,8 +572,6 @@ iqs626_parse_events(struct iqs626_private *iqs626, else *(thresh + iqs626_events[i].th_offs) = val; } - - fwnode_handle_put(ev_node); } return 0; @@ -774,12 +769,12 @@ static int iqs626_parse_trackpad(struct iqs626_private *iqs626, for (i = 0; i < iqs626_channels[ch_id].num_ch; i++) { u8 *ati_base = &sys_reg->tp_grp_reg.ch_reg_tp[i].ati_base; u8 *thresh = &sys_reg->tp_grp_reg.ch_reg_tp[i].thresh; - struct fwnode_handle *tc_node; char tc_name[10]; snprintf(tc_name, sizeof(tc_name), "channel-%d", i); - tc_node = fwnode_get_named_child_node(ch_node, tc_name); + struct fwnode_handle *tc_node __free(fwnode_handle) = + fwnode_get_named_child_node(ch_node, tc_name); if (!tc_node) continue; @@ -790,7 +785,6 @@ static int iqs626_parse_trackpad(struct iqs626_private *iqs626, dev_err(&client->dev, "Invalid %s %s ATI base: %u\n", fwnode_get_name(ch_node), tc_name, val); - fwnode_handle_put(tc_node); return -EINVAL; } @@ -803,14 +797,11 @@ static int iqs626_parse_trackpad(struct iqs626_private *iqs626, dev_err(&client->dev, "Invalid %s %s threshold: %u\n", fwnode_get_name(ch_node), tc_name, val); - fwnode_handle_put(tc_node); return -EINVAL; } *thresh = val; } - - fwnode_handle_put(tc_node); } if (!fwnode_property_present(ch_node, "linux,keycodes")) @@ -1233,7 +1224,6 @@ static int iqs626_parse_prop(struct iqs626_private *iqs626) { struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg; struct i2c_client *client = iqs626->client; - struct fwnode_handle *ch_node; unsigned int val; int error, i; u16 general; @@ -1375,13 +1365,13 @@ static int iqs626_parse_prop(struct iqs626_private *iqs626) sys_reg->active = 0; for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) { - ch_node = device_get_named_child_node(&client->dev, - iqs626_channels[i].name); + struct fwnode_handle *ch_node __free(fwnode_handle) = + device_get_named_child_node(&client->dev, + iqs626_channels[i].name); if (!ch_node) continue; error = iqs626_parse_channel(iqs626, ch_node, i); - fwnode_handle_put(ch_node); if (error) return error; diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c index be80a31de9f8f..22022d11470db 100644 --- a/drivers/input/misc/iqs7222.c +++ b/drivers/input/misc/iqs7222.c @@ -2385,9 +2385,9 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, for (i = 0; i < ARRAY_SIZE(iqs7222_kp_events); i++) { const char *event_name = iqs7222_kp_events[i].name; u16 event_enable = iqs7222_kp_events[i].enable; - struct fwnode_handle *event_node; - event_node = fwnode_get_named_child_node(chan_node, event_name); + struct fwnode_handle *event_node __free(fwnode_handle) = + fwnode_get_named_child_node(chan_node, event_name); if (!event_node) continue; @@ -2408,7 +2408,6 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, dev_err(&client->dev, "Invalid %s press timeout: %u\n", fwnode_get_name(event_node), val); - fwnode_handle_put(event_node); return -EINVAL; } @@ -2418,7 +2417,6 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, dev_err(&client->dev, "Failed to read %s press timeout: %d\n", fwnode_get_name(event_node), error); - fwnode_handle_put(event_node); return error; } @@ -2429,7 +2427,6 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, dev_desc->touch_link - (i ? 0 : 2), &iqs7222->kp_type[chan_index][i], &iqs7222->kp_code[chan_index][i]); - fwnode_handle_put(event_node); if (error) return error; @@ -2604,10 +2601,10 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) { const char *event_name = iqs7222_sl_events[i].name; - struct fwnode_handle *event_node; enum iqs7222_reg_key_id reg_key; - event_node = fwnode_get_named_child_node(sldr_node, event_name); + struct fwnode_handle *event_node __free(fwnode_handle) = + fwnode_get_named_child_node(sldr_node, event_name); if (!event_node) continue; @@ -2639,7 +2636,6 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, : sldr_setup[4 + reg_offset], NULL, &iqs7222->sl_code[sldr_index][i]); - fwnode_handle_put(event_node); if (error) return error; @@ -2742,9 +2738,9 @@ static int iqs7222_parse_tpad(struct iqs7222_private *iqs7222, for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++) { const char *event_name = iqs7222_tp_events[i].name; - struct fwnode_handle *event_node; - event_node = fwnode_get_named_child_node(tpad_node, event_name); + struct fwnode_handle *event_node __free(fwnode_handle) = + fwnode_get_named_child_node(tpad_node, event_name); if (!event_node) continue; @@ -2760,7 +2756,6 @@ static int iqs7222_parse_tpad(struct iqs7222_private *iqs7222, iqs7222_tp_events[i].link, 1566, NULL, &iqs7222->tp_code[i]); - fwnode_handle_put(event_node); if (error) return error; @@ -2818,9 +2813,9 @@ static int iqs7222_parse_reg_grp(struct iqs7222_private *iqs7222, int reg_grp_index) { struct i2c_client *client = iqs7222->client; - struct fwnode_handle *reg_grp_node; int error; + struct fwnode_handle *reg_grp_node __free(fwnode_handle) = NULL; if (iqs7222_reg_grp_names[reg_grp]) { char reg_grp_name[16]; @@ -2838,14 +2833,17 @@ static int iqs7222_parse_reg_grp(struct iqs7222_private *iqs7222, error = iqs7222_parse_props(iqs7222, reg_grp_node, reg_grp_index, reg_grp, IQS7222_REG_KEY_NONE); + if (error) + return error; - if (!error && iqs7222_parse_extra[reg_grp]) + if (iqs7222_parse_extra[reg_grp]) { error = iqs7222_parse_extra[reg_grp](iqs7222, reg_grp_node, reg_grp_index); + if (error) + return error; + } - fwnode_handle_put(reg_grp_node); - - return error; + return 0; } static int iqs7222_parse_all(struct iqs7222_private *iqs7222) diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 837682cb2a7d3..eb9788ea52155 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -25,7 +25,7 @@ /* CONTROL REGISTER 1 BITS */ #define PC1_OFF 0x7F #define PC1_ON (1 << 7) -/* Data ready funtion enable bit: set during probe if using irq mode */ +/* Data ready function enable bit: set during probe if using irq mode */ #define DRDYE (1 << 5) /* DATA CONTROL REGISTER BITS */ #define ODR12_5F 0 @@ -314,9 +314,8 @@ static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr, return error; /* Lock the device to prevent races with open/close (and itself) */ - mutex_lock(&input_dev->mutex); - - disable_irq(client->irq); + guard(mutex)(&input_dev->mutex); + guard(disable_irq)(&client->irq); /* * Set current interval to the greater of the minimum interval or @@ -326,9 +325,6 @@ static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr, kxtj9_update_odr(tj9, tj9->last_poll_interval); - enable_irq(client->irq); - mutex_unlock(&input_dev->mutex); - return count; } @@ -504,12 +500,11 @@ static int kxtj9_suspend(struct device *dev) struct kxtj9_data *tj9 = i2c_get_clientdata(client); struct input_dev *input_dev = tj9->input_dev; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) kxtj9_disable(tj9); - mutex_unlock(&input_dev->mutex); return 0; } @@ -519,12 +514,11 @@ static int kxtj9_resume(struct device *dev) struct kxtj9_data *tj9 = i2c_get_clientdata(client); struct input_dev *input_dev = tj9->input_dev; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) kxtj9_enable(tj9); - mutex_unlock(&input_dev->mutex); return 0; } diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c index 3fe0a85c45e00..0542334df6246 100644 --- a/drivers/input/misc/m68kspkr.c +++ b/drivers/input/misc/m68kspkr.c @@ -95,7 +95,7 @@ static struct platform_driver m68kspkr_platform_driver = { .name = "m68kspkr", }, .probe = m68kspkr_probe, - .remove_new = m68kspkr_remove, + .remove = m68kspkr_remove, .shutdown = m68kspkr_shutdown, }; diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c index 11cac4b7dddc0..f97f341ee0bb2 100644 --- a/drivers/input/misc/max8997_haptic.c +++ b/drivers/input/misc/max8997_haptic.c @@ -153,19 +153,19 @@ static void max8997_haptic_enable(struct max8997_haptic *chip) { int error; - mutex_lock(&chip->mutex); + guard(mutex)(&chip->mutex); error = max8997_haptic_set_duty_cycle(chip); if (error) { dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); - goto out; + return; } if (!chip->enabled) { error = regulator_enable(chip->regulator); if (error) { dev_err(chip->dev, "Failed to enable regulator\n"); - goto out; + return; } max8997_haptic_configure(chip); if (chip->mode == MAX8997_EXTERNAL_MODE) { @@ -173,19 +173,16 @@ static void max8997_haptic_enable(struct max8997_haptic *chip) if (error) { dev_err(chip->dev, "Failed to enable PWM\n"); regulator_disable(chip->regulator); - goto out; + return; } } chip->enabled = true; } - -out: - mutex_unlock(&chip->mutex); } static void max8997_haptic_disable(struct max8997_haptic *chip) { - mutex_lock(&chip->mutex); + guard(mutex)(&chip->mutex); if (chip->enabled) { chip->enabled = false; @@ -194,8 +191,6 @@ static void max8997_haptic_disable(struct max8997_haptic *chip) pwm_disable(chip->pwm); regulator_disable(chip->regulator); } - - mutex_unlock(&chip->mutex); } static void max8997_haptic_play_effect_work(struct work_struct *work) @@ -389,7 +384,7 @@ static struct platform_driver max8997_haptic_driver = { .pm = pm_sleep_ptr(&max8997_haptic_pm_ops), }, .probe = max8997_haptic_probe, - .remove_new = max8997_haptic_remove, + .remove = max8997_haptic_remove, .id_table = max8997_haptic_id, }; module_platform_driver(max8997_haptic_driver); diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c index 1c8c939638f6c..1c7faa9b7afe0 100644 --- a/drivers/input/misc/mc13783-pwrbutton.c +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -253,7 +253,7 @@ static void mc13783_pwrbutton_remove(struct platform_device *pdev) static struct platform_driver mc13783_pwrbutton_driver = { .probe = mc13783_pwrbutton_probe, - .remove_new = mc13783_pwrbutton_remove, + .remove = mc13783_pwrbutton_remove, .driver = { .name = "mc13783-pwrbutton", }, diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index 06d5972e8e84d..39fc451c56e92 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -310,7 +310,7 @@ MODULE_DEVICE_TABLE(of, of_palmas_pwr_match); static struct platform_driver palmas_pwron_driver = { .probe = palmas_pwron_probe, - .remove_new = palmas_pwron_remove, + .remove = palmas_pwron_remove, .driver = { .name = "palmas_pwrbutton", .of_match_table = of_match_ptr(of_palmas_pwr_match), diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c index f8954a2cab24d..fe43fd72ba7b0 100644 --- a/drivers/input/misc/pcap_keys.c +++ b/drivers/input/misc/pcap_keys.c @@ -112,7 +112,7 @@ static void pcap_keys_remove(struct platform_device *pdev) static struct platform_driver pcap_keys_device_driver = { .probe = pcap_keys_probe, - .remove_new = pcap_keys_remove, + .remove = pcap_keys_remove, .driver = { .name = "pcap-keys", } diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c index c5c5fe236c182..6d046e236ba69 100644 --- a/drivers/input/misc/pcf50633-input.c +++ b/drivers/input/misc/pcf50633-input.c @@ -103,7 +103,7 @@ static struct platform_driver pcf50633_input_driver = { .name = "pcf50633-input", }, .probe = pcf50633_input_probe, - .remove_new = pcf50633_input_remove, + .remove = pcf50633_input_remove, }; module_platform_driver(pcf50633_input_driver); diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 897854fd245f1..0467808402f24 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -127,7 +127,7 @@ static struct platform_driver pcspkr_platform_driver = { .pm = &pcspkr_pm_ops, }, .probe = pcspkr_probe, - .remove_new = pcspkr_remove, + .remove = pcspkr_remove, .shutdown = pcspkr_shutdown, }; module_platform_driver(pcspkr_platform_driver); diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index bab710023d8f1..d0c46665e5270 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -465,7 +465,7 @@ MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); static struct platform_driver pm8941_pwrkey_driver = { .probe = pm8941_pwrkey_probe, - .remove_new = pm8941_pwrkey_remove, + .remove = pm8941_pwrkey_remove, .driver = { .name = "pm8941-pwrkey", .pm = pm_sleep_ptr(&pm8941_pwr_key_pm_ops), diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index 4b039abffc4b8..ecb92ee5ebbc0 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -194,22 +194,18 @@ static void powermate_sync_state(struct powermate_device *pm) static void powermate_config_complete(struct urb *urb) { struct powermate_device *pm = urb->context; - unsigned long flags; if (urb->status) printk(KERN_ERR "powermate: config urb returned %d\n", urb->status); - spin_lock_irqsave(&pm->lock, flags); + guard(spinlock_irqsave)(&pm->lock); powermate_sync_state(pm); - spin_unlock_irqrestore(&pm->lock, flags); } /* Set the LED up as described and begin the sync with the hardware if required */ static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, int pulse_table, int pulse_asleep, int pulse_awake) { - unsigned long flags; - if (pulse_speed < 0) pulse_speed = 0; if (pulse_table < 0) @@ -222,8 +218,7 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne pulse_asleep = !!pulse_asleep; pulse_awake = !!pulse_awake; - - spin_lock_irqsave(&pm->lock, flags); + guard(spinlock_irqsave)(&pm->lock); /* mark state updates which are required */ if (static_brightness != pm->static_brightness) { @@ -245,8 +240,6 @@ static void powermate_pulse_led(struct powermate_device *pm, int static_brightne } powermate_sync_state(pm); - - spin_unlock_irqrestore(&pm->lock, flags); } /* Callback from the Input layer when an event arrives from userspace to configure the LED */ diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 5b9aedf4362f4..0e19e97d98ec2 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -203,9 +203,9 @@ static int pwm_beeper_suspend(struct device *dev) * beeper->suspended, but to ensure that pwm_beeper_event * does not re-submit work once flag is set. */ - spin_lock_irq(&beeper->input->event_lock); - beeper->suspended = true; - spin_unlock_irq(&beeper->input->event_lock); + scoped_guard(spinlock_irq, &beeper->input->event_lock) { + beeper->suspended = true; + } pwm_beeper_stop(beeper); @@ -216,9 +216,9 @@ static int pwm_beeper_resume(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - spin_lock_irq(&beeper->input->event_lock); - beeper->suspended = false; - spin_unlock_irq(&beeper->input->event_lock); + scoped_guard(spinlock_irq, &beeper->input->event_lock) { + beeper->suspended = false; + } /* Let worker figure out if we should resume beeping */ schedule_work(&beeper->work); diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 02f73b7c04628..3666ba6d1f307 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -83,12 +83,10 @@ static void regulator_haptic_work(struct work_struct *work) struct regulator_haptic *haptic = container_of(work, struct regulator_haptic, work); - mutex_lock(&haptic->mutex); + guard(mutex)(&haptic->mutex); if (!haptic->suspended) regulator_haptic_set_voltage(haptic, haptic->magnitude); - - mutex_unlock(&haptic->mutex); } static int regulator_haptic_play_effect(struct input_dev *input, void *data, @@ -205,19 +203,15 @@ static int regulator_haptic_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct regulator_haptic *haptic = platform_get_drvdata(pdev); - int error; - error = mutex_lock_interruptible(&haptic->mutex); - if (error) - return error; - - regulator_haptic_set_voltage(haptic, 0); + scoped_guard(mutex_intr, &haptic->mutex) { + regulator_haptic_set_voltage(haptic, 0); + haptic->suspended = true; - haptic->suspended = true; - - mutex_unlock(&haptic->mutex); + return 0; + } - return 0; + return -EINTR; } static int regulator_haptic_resume(struct device *dev) @@ -226,7 +220,7 @@ static int regulator_haptic_resume(struct device *dev) struct regulator_haptic *haptic = platform_get_drvdata(pdev); unsigned int magnitude; - mutex_lock(&haptic->mutex); + guard(mutex)(&haptic->mutex); haptic->suspended = false; @@ -234,8 +228,6 @@ static int regulator_haptic_resume(struct device *dev) if (magnitude) regulator_haptic_set_voltage(haptic, magnitude); - mutex_unlock(&haptic->mutex); - return 0; } diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index e94cab8133bec..f706e19974171 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -106,7 +106,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) struct rotary_encoder *encoder = dev_id; unsigned int state; - mutex_lock(&encoder->access_mutex); + guard(mutex)(&encoder->access_mutex); state = rotary_encoder_get_state(encoder); @@ -129,8 +129,6 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; } - mutex_unlock(&encoder->access_mutex); - return IRQ_HANDLED; } @@ -139,7 +137,7 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) struct rotary_encoder *encoder = dev_id; unsigned int state; - mutex_lock(&encoder->access_mutex); + guard(mutex)(&encoder->access_mutex); state = rotary_encoder_get_state(encoder); @@ -152,8 +150,6 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) } } - mutex_unlock(&encoder->access_mutex); - return IRQ_HANDLED; } @@ -162,22 +158,19 @@ static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) struct rotary_encoder *encoder = dev_id; unsigned int state; - mutex_lock(&encoder->access_mutex); + guard(mutex)(&encoder->access_mutex); state = rotary_encoder_get_state(encoder); - if ((encoder->last_stable + 1) % 4 == state) + if ((encoder->last_stable + 1) % 4 == state) { encoder->dir = 1; - else if (encoder->last_stable == (state + 1) % 4) + rotary_encoder_report_event(encoder); + } else if (encoder->last_stable == (state + 1) % 4) { encoder->dir = -1; - else - goto out; - - rotary_encoder_report_event(encoder); + rotary_encoder_report_event(encoder); + } -out: encoder->last_stable = state; - mutex_unlock(&encoder->access_mutex); return IRQ_HANDLED; } diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 5c5d407fe9650..b8cad415c62ca 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -515,7 +515,7 @@ static const struct soc_device_data soc_device_INT33D3 = { }; /* - * Button info for Microsoft Surface 3 (non pro), this is indentical to + * Button info for Microsoft Surface 3 (non pro), this is identical to * the PNP0C40 info except that the home button is active-high. * * The Surface 3 Pro also has a MSHW0028 ACPI device, but that uses a custom @@ -612,7 +612,7 @@ MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); static struct platform_driver soc_button_driver = { .probe = soc_button_probe, - .remove_new = soc_button_remove, + .remove = soc_button_remove, .driver = { .name = KBUILD_MODNAME, .acpi_match_table = ACPI_PTR(soc_button_acpi_match), diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 20020cbc0752b..8d7303fc13bce 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -69,7 +69,6 @@ static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); struct bbc_beep_info *info = &state->u.bbc; unsigned int count = 0; - unsigned long flags; if (type != EV_SND) return -1; @@ -85,7 +84,7 @@ static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int count = bbc_count_to_reg(info, count); - spin_lock_irqsave(&state->lock, flags); + guard(spinlock_irqsave)(&state->lock); if (count) { sbus_writeb(0x01, info->regs + 0); @@ -97,8 +96,6 @@ static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int sbus_writeb(0x00, info->regs + 0); } - spin_unlock_irqrestore(&state->lock, flags); - return 0; } @@ -107,7 +104,6 @@ static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); struct grover_beep_info *info = &state->u.grover; unsigned int count = 0; - unsigned long flags; if (type != EV_SND) return -1; @@ -121,7 +117,7 @@ static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned if (value > 20 && value < 32767) count = 1193182 / value; - spin_lock_irqsave(&state->lock, flags); + guard(spinlock_irqsave)(&state->lock); if (count) { /* enable counter 2 */ @@ -136,8 +132,6 @@ static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned sbus_writeb(sbus_readb(info->enable_reg) & 0xFC, info->enable_reg); } - spin_unlock_irqrestore(&state->lock, flags); - return 0; } @@ -188,47 +182,38 @@ static int bbc_beep_probe(struct platform_device *op) { struct sparcspkr_state *state; struct bbc_beep_info *info; - struct device_node *dp; - int err = -ENOMEM; + int err; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(&op->dev, sizeof(*state), GFP_KERNEL); if (!state) - goto out_err; + return -ENOMEM; state->name = "Sparc BBC Speaker"; state->event = bbc_spkr_event; spin_lock_init(&state->lock); - dp = of_find_node_by_path("/"); - err = -ENODEV; + struct device_node *dp __free(device_node) = of_find_node_by_path("/"); if (!dp) - goto out_free; + return -ENODEV; info = &state->u.bbc; info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0); - of_node_put(dp); if (!info->clock_freq) - goto out_free; + return -ENODEV; info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep"); if (!info->regs) - goto out_free; + return -ENODEV; platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); - if (err) - goto out_clear_drvdata; + if (err) { + of_iounmap(&op->resource[0], info->regs, 6); + return err; + } return 0; - -out_clear_drvdata: - of_iounmap(&op->resource[0], info->regs, 6); - -out_free: - kfree(state); -out_err: - return err; } static void bbc_remove(struct platform_device *op) @@ -243,8 +228,6 @@ static void bbc_remove(struct platform_device *op) input_unregister_device(input_dev); of_iounmap(&op->resource[0], info->regs, 6); - - kfree(state); } static const struct of_device_id bbc_beep_match[] = { @@ -262,7 +245,7 @@ static struct platform_driver bbc_beep_driver = { .of_match_table = bbc_beep_match, }, .probe = bbc_beep_probe, - .remove_new = bbc_remove, + .remove = bbc_remove, .shutdown = sparcspkr_shutdown, }; @@ -272,9 +255,9 @@ static int grover_beep_probe(struct platform_device *op) struct grover_beep_info *info; int err = -ENOMEM; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(&op->dev, sizeof(*state), GFP_KERNEL); if (!state) - goto out_err; + return err; state->name = "Sparc Grover Speaker"; state->event = grover_spkr_event; @@ -283,7 +266,7 @@ static int grover_beep_probe(struct platform_device *op) info = &state->u.grover; info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq"); if (!info->freq_regs) - goto out_free; + return err; info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable"); if (!info->enable_reg) @@ -302,9 +285,7 @@ static int grover_beep_probe(struct platform_device *op) out_unmap_freq_regs: of_iounmap(&op->resource[2], info->freq_regs, 2); -out_free: - kfree(state); -out_err: + return err; } @@ -321,8 +302,6 @@ static void grover_remove(struct platform_device *op) of_iounmap(&op->resource[3], info->enable_reg, 1); of_iounmap(&op->resource[2], info->freq_regs, 2); - - kfree(state); } static const struct of_device_id grover_beep_match[] = { @@ -340,7 +319,7 @@ static struct platform_driver grover_beep_driver = { .of_match_table = grover_beep_match, }, .probe = grover_beep_probe, - .remove_new = grover_remove, + .remove = grover_remove, .shutdown = sparcspkr_shutdown, }; diff --git a/drivers/input/misc/tps65219-pwrbutton.c b/drivers/input/misc/tps65219-pwrbutton.c index eeb9f2334ab46..7a58bae4f1a03 100644 --- a/drivers/input/misc/tps65219-pwrbutton.c +++ b/drivers/input/misc/tps65219-pwrbutton.c @@ -137,7 +137,7 @@ MODULE_DEVICE_TABLE(platform, tps65219_pwrbtn_id_table); static struct platform_driver tps65219_pb_driver = { .probe = tps65219_pb_probe, - .remove_new = tps65219_pb_remove, + .remove = tps65219_pb_remove, .driver = { .name = "tps65219_pwrbutton", }, diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 101548b35ee31..5fa7d4a7da363 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -165,15 +165,10 @@ static DEFINE_SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, static bool twl4030_vibra_check_coexist(struct device_node *parent) { - struct device_node *node; + struct device_node *node __free(device_node) = + of_get_child_by_name(parent, "codec"); - node = of_get_child_by_name(parent, "codec"); - if (node) { - of_node_put(node); - return true; - } - - return false; + return node != NULL; } static int twl4030_vibra_probe(struct platform_device *pdev) diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 78f0b63e5c200..afed9af65bf92 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -229,14 +229,13 @@ static DEFINE_SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, static int twl6040_vibra_probe(struct platform_device *pdev) { struct device *twl6040_core_dev = pdev->dev.parent; - struct device_node *twl6040_core_node; struct vibra_info *info; int vddvibl_uV = 0; int vddvibr_uV = 0; int error; - twl6040_core_node = of_get_child_by_name(twl6040_core_dev->of_node, - "vibra"); + struct device_node *twl6040_core_node __free(device_node) = + of_get_child_by_name(twl6040_core_dev->of_node, "vibra"); if (!twl6040_core_node) { dev_err(&pdev->dev, "parent of node is missing?\n"); return -EINVAL; @@ -244,7 +243,6 @@ static int twl6040_vibra_probe(struct platform_device *pdev) info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) { - of_node_put(twl6040_core_node); dev_err(&pdev->dev, "couldn't allocate memory\n"); return -ENOMEM; } @@ -264,8 +262,6 @@ static int twl6040_vibra_probe(struct platform_device *pdev) of_property_read_u32(twl6040_core_node, "ti,vddvibl-uV", &vddvibl_uV); of_property_read_u32(twl6040_core_node, "ti,vddvibr-uV", &vddvibr_uV); - of_node_put(twl6040_core_node); - if ((!info->vibldrv_res && !info->viblmotor_res) || (!info->vibrdrv_res && !info->vibrmotor_res)) { dev_err(info->dev, "invalid vibra driver/motor resistance\n"); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 5a64557920fa5..1b2bd2139aaac 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -271,7 +271,7 @@ static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = { { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */ { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */ { KE_KEY, 0x36, {KEY_WWW} }, /* www button */ - { KE_WIFI, 0x78 }, /* satelite dish button */ + { KE_WIFI, 0x78 }, /* satellite dish button */ { KE_END, FE_WIFI_LED } }; @@ -1334,7 +1334,7 @@ static struct platform_driver wistron_driver = { .pm = pm_sleep_ptr(&wistron_pm_ops), }, .probe = wistron_probe, - .remove_new = wistron_remove, + .remove = wistron_remove, }; static int __init wb_module_init(void) diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c index e4a06c73b72d9..18eb319bc6f75 100644 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -134,7 +134,7 @@ static void wm831x_on_remove(struct platform_device *pdev) static struct platform_driver wm831x_on_driver = { .probe = wm831x_on_probe, - .remove_new = wm831x_on_remove, + .remove = wm831x_on_remove, .driver = { .name = "wm831x-on", }, diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index 8866bf65d347b..08dc53ae1b3c9 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -377,7 +377,7 @@ static int yealink_do_idle_tasks(struct yealink_dev *yld) if (len > sizeof(yld->ctl_data->data)) len = sizeof(yld->ctl_data->data); - /* Combine up to consecutive LCD bytes in a singe request + /* Combine up to consecutive LCD bytes in a single request */ yld->ctl_data->cmd = CMD_LCD; yld->ctl_data->offset = cpu_to_be16(ix); @@ -614,7 +614,7 @@ static ssize_t show_line3(struct device *dev, struct device_attribute *attr, return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET); } -/* Writing to /sys/../lineX will set the coresponding LCD line. +/* Writing to /sys/../lineX will set the corresponding LCD line. * - Excess characters are ignored. * - If less characters are written than allowed, the remaining digits are * unchanged. diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 4e37fc3f1a9e9..0728b5c08f021 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1585,7 +1585,7 @@ static void alps_flush_packet(struct timer_list *t) struct alps_data *priv = from_timer(priv, t, timer); struct psmouse *psmouse = priv->psmouse; - serio_pause_rx(psmouse->ps2dev.serio); + guard(serio_pause_rx)(psmouse->ps2dev.serio); if (psmouse->pktcnt == psmouse->pktsize) { @@ -1605,8 +1605,6 @@ static void alps_flush_packet(struct timer_list *t) } psmouse->pktcnt = 0; } - - serio_continue_rx(psmouse->ps2dev.serio); } static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index 2fbbaeb76d708..d203c2a6c438b 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -139,7 +139,7 @@ static void __exit amimouse_remove(struct platform_device *pdev) * triggering a section mismatch warning. */ static struct platform_driver amimouse_driver __refdata = { - .remove_new = __exit_p(amimouse_remove), + .remove = __exit_p(amimouse_remove), .driver = { .name = "amiga-mouse", }, diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c index 221a553f45cd8..654b38d249f32 100644 --- a/drivers/input/mouse/byd.c +++ b/drivers/input/mouse/byd.c @@ -254,13 +254,12 @@ static void byd_clear_touch(struct timer_list *t) struct byd_data *priv = from_timer(priv, t, timer); struct psmouse *psmouse = priv->psmouse; - serio_pause_rx(psmouse->ps2dev.serio); + guard(serio_pause_rx)(psmouse->ps2dev.serio); + priv->touch = false; byd_report_input(psmouse); - serio_continue_rx(psmouse->ps2dev.serio); - /* * Move cursor back to center of pad when we lose touch - this * specifically improves user experience when moving cursor with one diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 7521981274bd8..a841883660fb8 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -541,7 +541,8 @@ static int elan_update_firmware(struct elan_tp_data *data, dev_dbg(&client->dev, "Starting firmware update....\n"); - disable_irq(client->irq); + guard(disable_irq)(&client->irq); + data->in_fw_update = true; retval = __elan_update_firmware(data, fw); @@ -555,7 +556,6 @@ static int elan_update_firmware(struct elan_tp_data *data, } data->in_fw_update = false; - enable_irq(client->irq); return retval; } @@ -621,8 +621,6 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, const char *buf, size_t count) { struct elan_tp_data *data = dev_get_drvdata(dev); - const struct firmware *fw; - char *fw_name; int error; const u8 *fw_signature; static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF}; @@ -631,15 +629,16 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, return -EINVAL; /* Look for a firmware with the product id appended. */ - fw_name = kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id); + const char *fw_name __free(kfree) = + kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id); if (!fw_name) { dev_err(dev, "failed to allocate memory for firmware name\n"); return -ENOMEM; } dev_info(dev, "requesting fw '%s'\n", fw_name); + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, dev); - kfree(fw_name); if (error) { dev_err(dev, "failed to request firmware: %d\n", error); return error; @@ -651,46 +650,36 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n", (int)sizeof(signature), signature, (int)sizeof(signature), fw_signature); - error = -EBADF; - goto out_release_fw; + return -EBADF; } - error = mutex_lock_interruptible(&data->sysfs_mutex); - if (error) - goto out_release_fw; - - error = elan_update_firmware(data, fw); - - mutex_unlock(&data->sysfs_mutex); + scoped_cond_guard(mutex_intr, return -EINTR, &data->sysfs_mutex) { + error = elan_update_firmware(data, fw); + if (error) + return error; + } -out_release_fw: - release_firmware(fw); - return error ?: count; + return count; } -static ssize_t calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int elan_calibrate(struct elan_tp_data *data) { - struct i2c_client *client = to_i2c_client(dev); - struct elan_tp_data *data = i2c_get_clientdata(client); + struct i2c_client *client = data->client; + struct device *dev = &client->dev; int tries = 20; int retval; int error; u8 val[ETP_CALIBRATE_MAX_LEN]; - retval = mutex_lock_interruptible(&data->sysfs_mutex); - if (retval) - return retval; - - disable_irq(client->irq); + guard(disable_irq)(&client->irq); data->mode |= ETP_ENABLE_CALIBRATE; retval = data->ops->set_mode(client, data->mode); if (retval) { + data->mode &= ~ETP_ENABLE_CALIBRATE; dev_err(dev, "failed to enable calibration mode: %d\n", retval); - goto out; + return retval; } retval = data->ops->calibrate(client); @@ -728,10 +717,24 @@ static ssize_t calibrate_store(struct device *dev, if (!retval) retval = error; } -out: - enable_irq(client->irq); - mutex_unlock(&data->sysfs_mutex); - return retval ?: count; + return retval; +} + +static ssize_t calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elan_tp_data *data = i2c_get_clientdata(client); + int error; + + scoped_cond_guard(mutex_intr, return -EINTR, &data->sysfs_mutex) { + error = elan_calibrate(data); + if (error) + return error; + } + + return count; } static ssize_t elan_sysfs_read_mode(struct device *dev, @@ -743,16 +746,11 @@ static ssize_t elan_sysfs_read_mode(struct device *dev, int error; enum tp_mode mode; - error = mutex_lock_interruptible(&data->sysfs_mutex); - if (error) - return error; - - error = data->ops->iap_get_mode(data->client, &mode); - - mutex_unlock(&data->sysfs_mutex); - - if (error) - return error; + scoped_cond_guard(mutex_intr, return -EINTR, &data->sysfs_mutex) { + error = data->ops->iap_get_mode(data->client, &mode); + if (error) + return error; + } return sysfs_emit(buf, "%d\n", (int)mode); } @@ -783,44 +781,40 @@ static const struct attribute_group elan_sysfs_group = { .attrs = elan_sysfs_entries, }; -static ssize_t acquire_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int elan_acquire_baseline(struct elan_tp_data *data) { - struct i2c_client *client = to_i2c_client(dev); - struct elan_tp_data *data = i2c_get_clientdata(client); - int error; + struct i2c_client *client = data->client; + struct device *dev = &client->dev; int retval; + int error; - retval = mutex_lock_interruptible(&data->sysfs_mutex); - if (retval) - return retval; - - disable_irq(client->irq); + guard(disable_irq)(&client->irq); data->baseline_ready = false; data->mode |= ETP_ENABLE_CALIBRATE; - retval = data->ops->set_mode(data->client, data->mode); + retval = data->ops->set_mode(client, data->mode); if (retval) { + data->mode &= ~ETP_ENABLE_CALIBRATE; dev_err(dev, "Failed to enable calibration mode to get baseline: %d\n", retval); - goto out; + return retval; } msleep(250); - retval = data->ops->get_baseline_data(data->client, true, + retval = data->ops->get_baseline_data(client, true, &data->max_baseline); if (retval) { - dev_err(dev, "Failed to read max baseline form device: %d\n", + dev_err(dev, "Failed to read max baseline from device: %d\n", retval); goto out_disable_calibrate; } - retval = data->ops->get_baseline_data(data->client, false, + retval = data->ops->get_baseline_data(client, false, &data->min_baseline); if (retval) { - dev_err(dev, "Failed to read min baseline form device: %d\n", + dev_err(dev, "Failed to read min baseline from device: %d\n", retval); goto out_disable_calibrate; } @@ -829,17 +823,31 @@ static ssize_t acquire_store(struct device *dev, struct device_attribute *attr, out_disable_calibrate: data->mode &= ~ETP_ENABLE_CALIBRATE; - error = data->ops->set_mode(data->client, data->mode); + error = data->ops->set_mode(client, data->mode); if (error) { dev_err(dev, "Failed to disable calibration mode after acquiring baseline: %d\n", error); if (!retval) retval = error; } -out: - enable_irq(client->irq); - mutex_unlock(&data->sysfs_mutex); - return retval ?: count; + + return retval; +} + +static ssize_t acquire_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elan_tp_data *data = i2c_get_clientdata(client); + int error; + + scoped_cond_guard(mutex_intr, return -EINTR, &data->sysfs_mutex) { + error = elan_acquire_baseline(data); + if (error) + return error; + } + + return count; } static ssize_t min_show(struct device *dev, @@ -847,22 +855,15 @@ static ssize_t min_show(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - int retval; - retval = mutex_lock_interruptible(&data->sysfs_mutex); - if (retval) - return retval; + scoped_guard(mutex_intr, &data->sysfs_mutex) { + if (!data->baseline_ready) + return -ENODATA; - if (!data->baseline_ready) { - retval = -ENODATA; - goto out; + return sysfs_emit(buf, "%d", data->min_baseline); } - retval = sysfs_emit(buf, "%d", data->min_baseline); - -out: - mutex_unlock(&data->sysfs_mutex); - return retval; + return -EINTR; } static ssize_t max_show(struct device *dev, @@ -870,25 +871,17 @@ static ssize_t max_show(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - int retval; - retval = mutex_lock_interruptible(&data->sysfs_mutex); - if (retval) - return retval; + scoped_guard(mutex_intr, &data->sysfs_mutex) { + if (!data->baseline_ready) + return -ENODATA; - if (!data->baseline_ready) { - retval = -ENODATA; - goto out; + return sysfs_emit(buf, "%d", data->max_baseline); } - retval = sysfs_emit(buf, "%d", data->max_baseline); - -out: - mutex_unlock(&data->sysfs_mutex); - return retval; + return -EINTR; } - static DEVICE_ATTR_WO(acquire); static DEVICE_ATTR_RO(min); static DEVICE_ATTR_RO(max); @@ -1323,43 +1316,54 @@ static int elan_probe(struct i2c_client *client) return 0; } +static int __elan_suspend(struct elan_tp_data *data) +{ + struct i2c_client *client = data->client; + int error; + + if (device_may_wakeup(&client->dev)) + return elan_sleep(data); + + /* Touchpad is not a wakeup source */ + error = elan_set_power(data, false); + if (error) + return error; + + error = regulator_disable(data->vcc); + if (error) { + dev_err(&client->dev, + "failed to disable regulator when suspending: %d\n", + error); + /* Attempt to power the chip back up */ + elan_set_power(data, true); + return error; + } + + return 0; +} + static int elan_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - int ret; + int error; /* * We are taking the mutex to make sure sysfs operations are * complete before we attempt to bring the device into low[er] * power mode. */ - ret = mutex_lock_interruptible(&data->sysfs_mutex); - if (ret) - return ret; - - disable_irq(client->irq); + scoped_cond_guard(mutex_intr, return -EINTR, &data->sysfs_mutex) { + disable_irq(client->irq); - if (device_may_wakeup(dev)) { - ret = elan_sleep(data); - } else { - ret = elan_set_power(data, false); - if (ret) - goto err; - - ret = regulator_disable(data->vcc); - if (ret) { - dev_err(dev, "error %d disabling regulator\n", ret); - /* Attempt to power the chip back up */ - elan_set_power(data, true); + error = __elan_suspend(data); + if (error) { + enable_irq(client->irq); + return error; } } -err: - if (ret) - enable_irq(client->irq); - mutex_unlock(&data->sysfs_mutex); - return ret; + return 0; } static int elan_resume(struct device *dev) diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 15cf4463b64ea..a9057d124a888 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -628,12 +628,11 @@ static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, const u8 *page, u16 checksum, int idx) { struct device *dev = &client->dev; - u8 *page_store; u8 val[3]; u16 result; int ret, error; - page_store = kmalloc(fw_page_size + 4, GFP_KERNEL); + u8 *page_store __free(kfree) = kmalloc(fw_page_size + 4, GFP_KERNEL); if (!page_store) return -ENOMEM; @@ -647,7 +646,7 @@ static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, if (ret != fw_page_size + 4) { error = ret < 0 ? ret : -EIO; dev_err(dev, "Failed to write page %d: %d\n", idx, error); - goto exit; + return error; } /* Wait for F/W to update one page ROM data. */ @@ -656,20 +655,17 @@ static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val); if (error) { dev_err(dev, "Failed to read IAP write result: %d\n", error); - goto exit; + return error; } result = le16_to_cpup((__le16 *)val); if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) { dev_err(dev, "IAP reports failed write: %04hx\n", result); - error = -EIO; - goto exit; + return -EIO; } -exit: - kfree(page_store); - return error; + return 0; } static int elan_i2c_finish_fw_update(struct i2c_client *client, diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c index 2a2459b1b4f2c..93420f07b7d07 100644 --- a/drivers/input/mouse/psmouse-smbus.c +++ b/drivers/input/mouse/psmouse-smbus.c @@ -35,7 +35,7 @@ static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) return; - mutex_lock(&psmouse_smbus_mutex); + guard(mutex)(&psmouse_smbus_mutex); list_for_each_entry(smbdev, &psmouse_smbus_list, node) { if (smbdev->dead) @@ -55,15 +55,13 @@ static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter) "SMBus candidate adapter appeared, triggering rescan\n"); serio_rescan(smbdev->psmouse->ps2dev.serio); } - - mutex_unlock(&psmouse_smbus_mutex); } static void psmouse_smbus_detach_i2c_client(struct i2c_client *client) { struct psmouse_smbus_dev *smbdev, *tmp; - mutex_lock(&psmouse_smbus_mutex); + guard(mutex)(&psmouse_smbus_mutex); list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { if (smbdev->client != client) @@ -85,8 +83,6 @@ static void psmouse_smbus_detach_i2c_client(struct i2c_client *client) kfree(smbdev); } } - - mutex_unlock(&psmouse_smbus_mutex); } static int psmouse_smbus_notifier_call(struct notifier_block *nb, @@ -171,7 +167,7 @@ static void psmouse_smbus_disconnect(struct psmouse *psmouse) { struct psmouse_smbus_dev *smbdev = psmouse->private; - mutex_lock(&psmouse_smbus_mutex); + guard(mutex)(&psmouse_smbus_mutex); if (smbdev->dead) { list_del(&smbdev->node); @@ -186,8 +182,6 @@ static void psmouse_smbus_disconnect(struct psmouse *psmouse) psmouse_smbus_schedule_remove(smbdev->client); } - mutex_unlock(&psmouse_smbus_mutex); - psmouse->private = NULL; } @@ -219,7 +213,7 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse) { struct psmouse_smbus_dev *smbdev, *tmp; - mutex_lock(&psmouse_smbus_mutex); + guard(mutex)(&psmouse_smbus_mutex); list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { if (psmouse == smbdev->psmouse) { @@ -227,8 +221,6 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse) kfree(smbdev); } } - - mutex_unlock(&psmouse_smbus_mutex); } int psmouse_smbus_init(struct psmouse *psmouse, @@ -267,9 +259,9 @@ int psmouse_smbus_init(struct psmouse *psmouse, psmouse->disconnect = psmouse_smbus_disconnect; psmouse->resync_time = 0; - mutex_lock(&psmouse_smbus_mutex); - list_add_tail(&smbdev->node, &psmouse_smbus_list); - mutex_unlock(&psmouse_smbus_mutex); + scoped_guard(mutex, &psmouse_smbus_mutex) { + list_add_tail(&smbdev->node, &psmouse_smbus_list); + } /* Bind to already existing adapters right away */ error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion); @@ -293,9 +285,9 @@ int psmouse_smbus_init(struct psmouse *psmouse, smbdev->board.platform_data = NULL; if (error < 0 || !leave_breadcrumbs) { - mutex_lock(&psmouse_smbus_mutex); - list_del(&smbdev->node); - mutex_unlock(&psmouse_smbus_mutex); + scoped_guard(mutex, &psmouse_smbus_mutex) { + list_del(&smbdev->node); + } kfree(smbdev); } diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 380aa1614442f..2735f86c23cc8 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -650,9 +650,8 @@ static int synaptics_pt_start(struct serio *serio) struct psmouse *parent = psmouse_from_serio(serio->parent); struct synaptics_data *priv = parent->private; - serio_pause_rx(parent->ps2dev.serio); + guard(serio_pause_rx)(parent->ps2dev.serio); priv->pt_port = serio; - serio_continue_rx(parent->ps2dev.serio); return 0; } @@ -662,9 +661,8 @@ static void synaptics_pt_stop(struct serio *serio) struct psmouse *parent = psmouse_from_serio(serio->parent); struct synaptics_data *priv = parent->private; - serio_pause_rx(parent->ps2dev.serio); + guard(serio_pause_rx)(parent->ps2dev.serio); priv->pt_port = NULL; - serio_continue_rx(parent->ps2dev.serio); } static int synaptics_is_pt_packet(u8 *buf) diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 08533d1b1b16f..899aee598632b 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -21,7 +21,7 @@ #define SYN_QUE_EXT_MIN_COORDS 0x0f #define SYN_QUE_MEXT_CAPAB_10 0x10 -/* synatics modes */ +/* synaptics modes */ #define SYN_BIT_ABSOLUTE_MODE BIT(7) #define SYN_BIT_HIGH_RATE BIT(6) #define SYN_BIT_SLEEP_MODE BIT(3) diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 1e11ea30d7bdb..e1157ff0f00af 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -61,14 +61,14 @@ void rmi_f03_commit_buttons(struct rmi_function *fn) struct f03_data *f03 = dev_get_drvdata(&fn->dev); struct serio *serio = f03->serio; - serio_pause_rx(serio); + guard(serio_pause_rx)(serio); + if (serio->drv) { serio->drv->interrupt(serio, PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA); serio->drv->interrupt(serio, f03->overwrite_buttons, SERIO_OOB_DATA); } - serio_continue_rx(serio); } static int rmi_f03_pt_write(struct serio *id, unsigned char val) diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index e2468bc04a5cb..d760af4cc12ef 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -246,7 +246,6 @@ static int rmi_f34_update_firmware(struct f34_data *f34, (const struct rmi_f34_firmware *)fw->data; u32 image_size = le32_to_cpu(syn_fw->image_size); u32 config_size = le32_to_cpu(syn_fw->config_size); - int ret; BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) != F34_FW_IMAGE_OFFSET); @@ -267,8 +266,7 @@ static int rmi_f34_update_firmware(struct f34_data *f34, dev_err(&f34->fn->dev, "Bad firmware image: fw size %d, expected %d\n", image_size, f34->v5.fw_blocks * f34->v5.block_size); - ret = -EILSEQ; - goto out; + return -EILSEQ; } if (config_size && @@ -277,25 +275,18 @@ static int rmi_f34_update_firmware(struct f34_data *f34, "Bad firmware image: config size %d, expected %d\n", config_size, f34->v5.config_blocks * f34->v5.block_size); - ret = -EILSEQ; - goto out; + return -EILSEQ; } if (image_size && !config_size) { dev_err(&f34->fn->dev, "Bad firmware image: no config data\n"); - ret = -EILSEQ; - goto out; + return -EILSEQ; } dev_info(&f34->fn->dev, "Firmware image OK\n"); - mutex_lock(&f34->v5.flash_mutex); - - ret = rmi_f34_flash_firmware(f34, syn_fw); - mutex_unlock(&f34->v5.flash_mutex); - -out: - return ret; + guard(mutex)(&f34->v5.flash_mutex); + return rmi_f34_flash_firmware(f34, syn_fw); } static int rmi_f34_status(struct rmi_function *fn) @@ -461,9 +452,8 @@ static ssize_t rmi_driver_update_fw_store(struct device *dev, { struct rmi_driver_data *data = dev_get_drvdata(dev); char fw_name[NAME_MAX]; - const struct firmware *fw; size_t copy_count = count; - int ret; + int error; if (count == 0 || count >= NAME_MAX) return -EINVAL; @@ -474,17 +464,18 @@ static ssize_t rmi_driver_update_fw_store(struct device *dev, memcpy(fw_name, buf, copy_count); fw_name[copy_count] = '\0'; - ret = request_firmware(&fw, fw_name, dev); - if (ret) - return ret; + const struct firmware *fw __free(firmware) = NULL; + error = request_firmware(&fw, fw_name, dev); + if (error) + return error; dev_info(dev, "Flashing %s\n", fw_name); - ret = rmi_firmware_update(data, fw); - - release_firmware(fw); + error = rmi_firmware_update(data, fw); + if (error) + return error; - return ret ?: count; + return count; } static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store); diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index 611eb9fe2d047..aa445b1941e93 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -146,7 +146,7 @@ MODULE_DEVICE_TABLE(of, altera_ps2_match); */ static struct platform_driver altera_ps2_driver = { .probe = altera_ps2_probe, - .remove_new = altera_ps2_remove, + .remove = altera_ps2_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(altera_ps2_match), diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c index 0bd6ae1068099..81b3a053df810 100644 --- a/drivers/input/serio/ams_delta_serio.c +++ b/drivers/input/serio/ams_delta_serio.c @@ -182,7 +182,7 @@ static void ams_delta_serio_exit(struct platform_device *pdev) static struct platform_driver ams_delta_serio_driver = { .probe = ams_delta_serio_init, - .remove_new = ams_delta_serio_exit, + .remove = ams_delta_serio_exit, .driver = { .name = DRIVER_NAME }, diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c index 4015e75fcb903..b815337be2f43 100644 --- a/drivers/input/serio/apbps2.c +++ b/drivers/input/serio/apbps2.c @@ -208,7 +208,7 @@ static struct platform_driver apbps2_of_driver = { .of_match_table = apbps2_of_match, }, .probe = apbps2_of_probe, - .remove_new = apbps2_of_remove, + .remove = apbps2_of_remove, }; module_platform_driver(apbps2_of_driver); diff --git a/drivers/input/serio/arc_ps2.c b/drivers/input/serio/arc_ps2.c index a9180a0058720..e991c72296c93 100644 --- a/drivers/input/serio/arc_ps2.c +++ b/drivers/input/serio/arc_ps2.c @@ -260,7 +260,7 @@ static struct platform_driver arc_ps2_driver = { .of_match_table = of_match_ptr(arc_ps2_match), }, .probe = arc_ps2_probe, - .remove_new = arc_ps2_remove, + .remove = arc_ps2_remove, }; module_platform_driver(arc_ps2_driver); diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 6834440b37f66..053a15988c454 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -190,7 +190,7 @@ static struct platform_driver ct82c710_driver = { .name = "ct82c710", }, .probe = ct82c710_probe, - .remove_new = ct82c710_remove, + .remove = ct82c710_remove, }; diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index d94c01eb3fc97..4fada5bc2a382 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -145,7 +145,6 @@ static void gscps2_flush(struct gscps2port *ps2port) static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) { - unsigned long flags; char __iomem *addr = ps2port->addr; if (!wait_TBE(addr)) { @@ -156,9 +155,8 @@ static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) while (gscps2_readb_status(addr) & GSC_STAT_RBNE) /* wait */; - spin_lock_irqsave(&ps2port->lock, flags); - writeb(data, addr+GSC_XMTDATA); - spin_unlock_irqrestore(&ps2port->lock, flags); + scoped_guard(spinlock_irqsave, &ps2port->lock) + writeb(data, addr+GSC_XMTDATA); /* this is ugly, but due to timing of the port it seems to be necessary. */ mdelay(6); @@ -177,19 +175,19 @@ static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) static void gscps2_enable(struct gscps2port *ps2port, int enable) { - unsigned long flags; u8 data; /* now enable/disable the port */ - spin_lock_irqsave(&ps2port->lock, flags); - gscps2_flush(ps2port); - data = gscps2_readb_control(ps2port->addr); - if (enable) - data |= GSC_CTRL_ENBL; - else - data &= ~GSC_CTRL_ENBL; - gscps2_writeb_control(data, ps2port->addr); - spin_unlock_irqrestore(&ps2port->lock, flags); + scoped_guard(spinlock_irqsave, &ps2port->lock) { + gscps2_flush(ps2port); + data = gscps2_readb_control(ps2port->addr); + if (enable) + data |= GSC_CTRL_ENBL; + else + data &= ~GSC_CTRL_ENBL; + gscps2_writeb_control(data, ps2port->addr); + } + wait_TBE(ps2port->addr); gscps2_flush(ps2port); } @@ -200,18 +198,57 @@ static void gscps2_enable(struct gscps2port *ps2port, int enable) static void gscps2_reset(struct gscps2port *ps2port) { - unsigned long flags; - /* reset the interface */ - spin_lock_irqsave(&ps2port->lock, flags); + guard(spinlock_irqsave)(&ps2port->lock); gscps2_flush(ps2port); writeb(0xff, ps2port->addr + GSC_RESET); gscps2_flush(ps2port); - spin_unlock_irqrestore(&ps2port->lock, flags); } static LIST_HEAD(ps2port_list); +static void gscps2_read_data(struct gscps2port *ps2port) +{ + u8 status; + + do { + status = gscps2_readb_status(ps2port->addr); + if (!(status & GSC_STAT_RBNE)) + break; + + ps2port->buffer[ps2port->append].str = status; + ps2port->buffer[ps2port->append].data = + gscps2_readb_input(ps2port->addr); + } while (true); +} + +static bool gscps2_report_data(struct gscps2port *ps2port) +{ + unsigned int rxflags; + u8 data, status; + + while (ps2port->act != ps2port->append) { + /* + * Did new data arrived while we read existing data ? + * If yes, exit now and let the new irq handler start + * over again. + */ + if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR) + return true; + + status = ps2port->buffer[ps2port->act].str; + data = ps2port->buffer[ps2port->act].data; + + ps2port->act = (ps2port->act + 1) & BUFFER_SIZE; + rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | + ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); + + serio_interrupt(ps2port->port, data, rxflags); + } + + return false; +} + /** * gscps2_interrupt() - Interruption service routine * @@ -229,47 +266,18 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev) struct gscps2port *ps2port; list_for_each_entry(ps2port, &ps2port_list, node) { + guard(spinlock_irqsave)(&ps2port->lock); - unsigned long flags; - spin_lock_irqsave(&ps2port->lock, flags); - - while ( (ps2port->buffer[ps2port->append].str = - gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) { - ps2port->buffer[ps2port->append].data = - gscps2_readb_input(ps2port->addr); - ps2port->append = ((ps2port->append+1) & BUFFER_SIZE); - } - - spin_unlock_irqrestore(&ps2port->lock, flags); - + gscps2_read_data(ps2port); } /* list_for_each_entry */ /* all data was read from the ports - now report the data to upper layer */ - list_for_each_entry(ps2port, &ps2port_list, node) { - - while (ps2port->act != ps2port->append) { - - unsigned int rxflags; - u8 data, status; - - /* Did new data arrived while we read existing data ? - If yes, exit now and let the new irq handler start over again */ - if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR) - return IRQ_HANDLED; - - status = ps2port->buffer[ps2port->act].str; - data = ps2port->buffer[ps2port->act].data; - - ps2port->act = ((ps2port->act+1) & BUFFER_SIZE); - rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | - ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); - - serio_interrupt(ps2port->port, data, rxflags); - - } /* while() */ - - } /* list_for_each_entry */ + if (gscps2_report_data(ps2port)) { + /* More data ready - break early to restart interrupt */ + break; + } + } return IRQ_HANDLED; } diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 31d9dacd2fd18..0ee7505427ace 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -102,7 +102,6 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, { struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); struct synth_kbd_keystroke *ks_msg; - unsigned long flags; u32 msg_type = __le32_to_cpu(msg->header.type); u32 info; u16 scan_code; @@ -147,21 +146,22 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, /* * Inject the information through the serio interrupt. */ - spin_lock_irqsave(&kbd_dev->lock, flags); - if (kbd_dev->started) { - if (info & IS_E0) - serio_interrupt(kbd_dev->hv_serio, - XTKBD_EMUL0, 0); - if (info & IS_E1) - serio_interrupt(kbd_dev->hv_serio, - XTKBD_EMUL1, 0); - scan_code = __le16_to_cpu(ks_msg->make_code); - if (info & IS_BREAK) - scan_code |= XTKBD_RELEASE; + scoped_guard(spinlock_irqsave, &kbd_dev->lock) { + if (kbd_dev->started) { + if (info & IS_E0) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL0, 0); + if (info & IS_E1) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL1, 0); + scan_code = __le16_to_cpu(ks_msg->make_code); + if (info & IS_BREAK) + scan_code |= XTKBD_RELEASE; - serio_interrupt(kbd_dev->hv_serio, scan_code, 0); + serio_interrupt(kbd_dev->hv_serio, + scan_code, 0); + } } - spin_unlock_irqrestore(&kbd_dev->lock, flags); /* * Only trigger a wakeup on key down, otherwise @@ -292,11 +292,10 @@ static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) static int hv_kbd_start(struct serio *serio) { struct hv_kbd_dev *kbd_dev = serio->port_data; - unsigned long flags; - spin_lock_irqsave(&kbd_dev->lock, flags); + guard(spinlock_irqsave)(&kbd_dev->lock); + kbd_dev->started = true; - spin_unlock_irqrestore(&kbd_dev->lock, flags); return 0; } @@ -304,11 +303,10 @@ static int hv_kbd_start(struct serio *serio) static void hv_kbd_stop(struct serio *serio) { struct hv_kbd_dev *kbd_dev = serio->port_data; - unsigned long flags; - spin_lock_irqsave(&kbd_dev->lock, flags); + guard(spinlock_irqsave)(&kbd_dev->lock); + kbd_dev->started = false; - spin_unlock_irqrestore(&kbd_dev->lock, flags); } static int hv_kbd_probe(struct hv_device *hv_dev, diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index 34d1f07ea4c30..127cfdc8668a0 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -90,7 +90,7 @@ static inline void i8042_write_command(int val) * ORDERING IS IMPORTANT! The first match will be apllied and the rest ignored. * This allows entries to overwrite vendor wide quirks on a per device basis. * Where this is irrelevant, entries are sorted case sensitive by DMI_SYS_VENDOR - * and/or DMI_BOARD_VENDOR to make it easier to avoid dublicate entries. + * and/or DMI_BOARD_VENDOR to make it easier to avoid duplicate entries. */ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { { diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index c2fda54dc384f..0f97158fd14e6 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -101,23 +101,15 @@ static struct platform_driver sparc_i8042_driver = { .of_match_table = sparc_i8042_match, }, .probe = sparc_i8042_probe, - .remove_new = sparc_i8042_remove, + .remove = sparc_i8042_remove, }; static bool i8042_is_mr_coffee(void) { - struct device_node *root; - const char *name; - bool is_mr_coffee; + struct device_node *root __free(device_node) = of_find_node_by_path("/"); + const char *name = of_get_property(root, "name", NULL); - root = of_find_node_by_path("/"); - - name = of_get_property(root, "name", NULL); - is_mr_coffee = name && !strcmp(name, "SUNW,JavaStation-1"); - - of_node_put(root); - - return is_mr_coffee; + return name && !strcmp(name, "SUNW,JavaStation-1"); } static int __init i8042_platform_init(void) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 8ec4872b44714..509330a278802 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -178,7 +178,7 @@ static unsigned char i8042_suppress_kbd_ack; static struct platform_device *i8042_platform_device; static struct notifier_block i8042_kbd_bind_notifier_block; -static irqreturn_t i8042_interrupt(int irq, void *dev_id); +static bool i8042_handle_data(int irq); static bool (*i8042_platform_filter)(unsigned char data, unsigned char str, struct serio *serio); @@ -197,42 +197,26 @@ EXPORT_SYMBOL(i8042_unlock_chip); int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str, struct serio *serio)) { - unsigned long flags; - int ret = 0; + guard(spinlock_irqsave)(&i8042_lock); - spin_lock_irqsave(&i8042_lock, flags); - - if (i8042_platform_filter) { - ret = -EBUSY; - goto out; - } + if (i8042_platform_filter) + return -EBUSY; i8042_platform_filter = filter; - -out: - spin_unlock_irqrestore(&i8042_lock, flags); - return ret; + return 0; } EXPORT_SYMBOL(i8042_install_filter); int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str, struct serio *port)) { - unsigned long flags; - int ret = 0; + guard(spinlock_irqsave)(&i8042_lock); - spin_lock_irqsave(&i8042_lock, flags); - - if (i8042_platform_filter != filter) { - ret = -EINVAL; - goto out; - } + if (i8042_platform_filter != filter) + return -EINVAL; i8042_platform_filter = NULL; - -out: - spin_unlock_irqrestore(&i8042_lock, flags); - return ret; + return 0; } EXPORT_SYMBOL(i8042_remove_filter); @@ -271,28 +255,22 @@ static int i8042_wait_write(void) static int i8042_flush(void) { - unsigned long flags; unsigned char data, str; int count = 0; - int retval = 0; - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); while ((str = i8042_read_status()) & I8042_STR_OBF) { - if (count++ < I8042_BUFFER_SIZE) { - udelay(50); - data = i8042_read_data(); - dbg("%02x <- i8042 (flush, %s)\n", - data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); - } else { - retval = -EIO; - break; - } - } + if (count++ >= I8042_BUFFER_SIZE) + return -EIO; - spin_unlock_irqrestore(&i8042_lock, flags); + udelay(50); + data = i8042_read_data(); + dbg("%02x <- i8042 (flush, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + } - return retval; + return 0; } /* @@ -349,17 +327,12 @@ static int __i8042_command(unsigned char *param, int command) int i8042_command(unsigned char *param, int command) { - unsigned long flags; - int retval; - if (!i8042_present) return -1; - spin_lock_irqsave(&i8042_lock, flags); - retval = __i8042_command(param, command); - spin_unlock_irqrestore(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); - return retval; + return __i8042_command(param, command); } EXPORT_SYMBOL(i8042_command); @@ -369,19 +342,18 @@ EXPORT_SYMBOL(i8042_command); static int i8042_kbd_write(struct serio *port, unsigned char c) { - unsigned long flags; - int retval = 0; + int error; - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); - if (!(retval = i8042_wait_write())) { - dbg("%02x -> i8042 (kbd-data)\n", c); - i8042_write_data(c); - } + error = i8042_wait_write(); + if (error) + return error; - spin_unlock_irqrestore(&i8042_lock, flags); + dbg("%02x -> i8042 (kbd-data)\n", c); + i8042_write_data(c); - return retval; + return 0; } /* @@ -434,7 +406,7 @@ static void i8042_port_close(struct serio *serio) * See if there is any data appeared while we were messing with * port state. */ - i8042_interrupt(0, NULL); + i8042_handle_data(0); } /* @@ -460,9 +432,8 @@ static int i8042_start(struct serio *serio) device_set_wakeup_enable(&serio->dev, true); } - spin_lock_irq(&i8042_lock); + guard(spinlock_irq)(&i8042_lock); port->exists = true; - spin_unlock_irq(&i8042_lock); return 0; } @@ -476,10 +447,10 @@ static void i8042_stop(struct serio *serio) { struct i8042_port *port = serio->port_data; - spin_lock_irq(&i8042_lock); - port->exists = false; - port->serio = NULL; - spin_unlock_irq(&i8042_lock); + scoped_guard(spinlock_irq, &i8042_lock) { + port->exists = false; + port->serio = NULL; + } /* * We need to make sure that interrupt handler finishes using @@ -518,44 +489,10 @@ static bool i8042_filter(unsigned char data, unsigned char str, } /* - * i8042_interrupt() is the most important function in this driver - - * it handles the interrupts from the i8042, and sends incoming bytes - * to the upper layers. - */ - -static irqreturn_t i8042_interrupt(int irq, void *dev_id) -{ - struct i8042_port *port; - struct serio *serio; - unsigned long flags; - unsigned char str, data; - unsigned int dfl; - unsigned int port_no; - bool filtered; - int ret = 1; - - spin_lock_irqsave(&i8042_lock, flags); - - str = i8042_read_status(); - if (unlikely(~str & I8042_STR_OBF)) { - spin_unlock_irqrestore(&i8042_lock, flags); - if (irq) - dbg("Interrupt %d, without any data\n", irq); - ret = 0; - goto out; - } - - data = i8042_read_data(); - - if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { - static unsigned long last_transmit; - static unsigned char last_str; - - dfl = 0; - if (str & I8042_STR_MUXERR) { - dbg("MUX error, status is %02x, data is %02x\n", - str, data); -/* + * i8042_handle_mux() handles case when data is coming from one of + * the multiplexed ports. It would be simple if not for quirks with + * handling errors: + * * When MUXERR condition is signalled the data register can only contain * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately * it is not always the case. Some KBCs also report 0xfc when there is @@ -567,50 +504,106 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) * rest assume that the data came from the same serio last byte * was transmitted (if transmission happened not too long ago). */ - - switch (data) { - default: - if (time_before(jiffies, last_transmit + HZ/10)) { - str = last_str; - break; - } - fallthrough; /* report timeout */ - case 0xfc: - case 0xfd: - case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; - case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; +static int i8042_handle_mux(u8 str, u8 *data, unsigned int *dfl) +{ + static unsigned long last_transmit; + static unsigned long last_port; + unsigned int mux_port; + + mux_port = (str >> 6) & 3; + *dfl = 0; + + if (str & I8042_STR_MUXERR) { + dbg("MUX error, status is %02x, data is %02x\n", + str, *data); + + switch (*data) { + default: + if (time_before(jiffies, last_transmit + HZ/10)) { + mux_port = last_port; + break; } + fallthrough; /* report timeout */ + case 0xfc: + case 0xfd: + case 0xfe: + *dfl = SERIO_TIMEOUT; + *data = 0xfe; + break; + case 0xff: + *dfl = SERIO_PARITY; + *data = 0xfe; + break; } + } - port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3); - last_str = str; - last_transmit = jiffies; - } else { + last_port = mux_port; + last_transmit = jiffies; - dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | - ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); + return I8042_MUX_PORT_NO + mux_port; +} - port_no = (str & I8042_STR_AUXDATA) ? - I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; - } +/* + * i8042_handle_data() is the most important function in this driver - + * it reads the data from the i8042, determines its destination serio + * port, and sends received byte to the upper layers. + * + * Returns true if there was data waiting, false otherwise. + */ +static bool i8042_handle_data(int irq) +{ + struct i8042_port *port; + struct serio *serio; + unsigned char str, data; + unsigned int dfl; + unsigned int port_no; + bool filtered; + + scoped_guard(spinlock_irqsave, &i8042_lock) { + str = i8042_read_status(); + if (unlikely(~str & I8042_STR_OBF)) + return false; + + data = i8042_read_data(); + + if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { + port_no = i8042_handle_mux(str, &data, &dfl); + } else { + + dfl = (str & I8042_STR_PARITY) ? SERIO_PARITY : 0; + if ((str & I8042_STR_TIMEOUT) && !i8042_notimeout) + dfl |= SERIO_TIMEOUT; - port = &i8042_ports[port_no]; - serio = port->exists ? port->serio : NULL; + port_no = (str & I8042_STR_AUXDATA) ? + I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; + } - filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n", - port_no, irq, - dfl & SERIO_PARITY ? ", bad parity" : "", - dfl & SERIO_TIMEOUT ? ", timeout" : ""); + port = &i8042_ports[port_no]; + serio = port->exists ? port->serio : NULL; - filtered = i8042_filter(data, str, serio); + filter_dbg(port->driver_bound, + data, "<- i8042 (interrupt, %d, %d%s%s)\n", + port_no, irq, + dfl & SERIO_PARITY ? ", bad parity" : "", + dfl & SERIO_TIMEOUT ? ", timeout" : ""); - spin_unlock_irqrestore(&i8042_lock, flags); + filtered = i8042_filter(data, str, serio); + } if (likely(serio && !filtered)) serio_interrupt(serio, data, dfl); - out: - return IRQ_RETVAL(ret); + return true; +} + +static irqreturn_t i8042_interrupt(int irq, void *dev_id) +{ + if (unlikely(!i8042_handle_data(irq))) { + dbg("Interrupt %d, without any data\n", irq); + return IRQ_NONE; + } + + return IRQ_HANDLED; } /* @@ -753,24 +746,22 @@ static bool i8042_irq_being_tested; static irqreturn_t i8042_aux_test_irq(int irq, void *dev_id) { - unsigned long flags; unsigned char str, data; - int ret = 0; - spin_lock_irqsave(&i8042_lock, flags); + guard(spinlock_irqsave)(&i8042_lock); + str = i8042_read_status(); - if (str & I8042_STR_OBF) { - data = i8042_read_data(); - dbg("%02x <- i8042 (aux_test_irq, %s)\n", - data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); - if (i8042_irq_being_tested && - data == 0xa5 && (str & I8042_STR_AUXDATA)) - complete(&i8042_aux_irq_delivered); - ret = 1; - } - spin_unlock_irqrestore(&i8042_lock, flags); + if (!(str & I8042_STR_OBF)) + return IRQ_NONE; + + data = i8042_read_data(); + dbg("%02x <- i8042 (aux_test_irq, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + + if (i8042_irq_being_tested && data == 0xa5 && (str & I8042_STR_AUXDATA)) + complete(&i8042_aux_irq_delivered); - return IRQ_RETVAL(ret); + return IRQ_HANDLED; } /* @@ -811,7 +802,6 @@ static int i8042_check_aux(void) int retval = -1; bool irq_registered = false; bool aux_loop_broken = false; - unsigned long flags; unsigned char param; /* @@ -895,18 +885,15 @@ static int i8042_check_aux(void) if (i8042_enable_aux_port()) goto out; - spin_lock_irqsave(&i8042_lock, flags); - - init_completion(&i8042_aux_irq_delivered); - i8042_irq_being_tested = true; + scoped_guard(spinlock_irqsave, &i8042_lock) { + init_completion(&i8042_aux_irq_delivered); + i8042_irq_being_tested = true; - param = 0xa5; - retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); - - spin_unlock_irqrestore(&i8042_lock, flags); - - if (retval) - goto out; + param = 0xa5; + retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); + if (retval) + goto out; + } if (wait_for_completion_timeout(&i8042_aux_irq_delivered, msecs_to_jiffies(250)) == 0) { @@ -994,7 +981,6 @@ static int i8042_controller_selftest(void) static int i8042_controller_init(void) { - unsigned long flags; int n = 0; unsigned char ctr[2]; @@ -1031,14 +1017,14 @@ static int i8042_controller_init(void) * Handle keylock. */ - spin_lock_irqsave(&i8042_lock, flags); - if (~i8042_read_status() & I8042_STR_KEYLOCK) { - if (i8042_unlock) - i8042_ctr |= I8042_CTR_IGNKEYLOCK; - else - pr_warn("Warning: Keylock active\n"); + scoped_guard(spinlock_irqsave, &i8042_lock) { + if (~i8042_read_status() & I8042_STR_KEYLOCK) { + if (i8042_unlock) + i8042_ctr |= I8042_CTR_IGNKEYLOCK; + else + pr_warn("Warning: Keylock active\n"); + } } - spin_unlock_irqrestore(&i8042_lock, flags); /* * If the chip is configured into nontranslated mode by the BIOS, don't @@ -1216,13 +1202,14 @@ static int i8042_controller_resume(bool s2r_wants_reset) if (i8042_mux_present) { if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) pr_warn("failed to resume active multiplexor, mouse won't work\n"); - } else if (i8042_ports[I8042_AUX_PORT_NO].serio) + } else if (i8042_ports[I8042_AUX_PORT_NO].serio) { i8042_enable_aux_port(); + } if (i8042_ports[I8042_KBD_PORT_NO].serio) i8042_enable_kbd_port(); - i8042_interrupt(0, NULL); + i8042_handle_data(0); return 0; } @@ -1253,7 +1240,7 @@ static int i8042_pm_suspend(struct device *dev) static int i8042_pm_resume_noirq(struct device *dev) { if (i8042_forcenorestore || !pm_resume_via_firmware()) - i8042_interrupt(0, NULL); + i8042_handle_data(0); return 0; } @@ -1290,7 +1277,7 @@ static int i8042_pm_resume(struct device *dev) static int i8042_pm_thaw(struct device *dev) { - i8042_interrupt(0, NULL); + i8042_handle_data(0); return 0; } @@ -1603,7 +1590,7 @@ static struct platform_driver i8042_driver = { #endif }, .probe = i8042_probe, - .remove_new = i8042_remove, + .remove = i8042_remove, .shutdown = i8042_shutdown, }; diff --git a/drivers/input/serio/ioc3kbd.c b/drivers/input/serio/ioc3kbd.c index 676b0bda3d720..d2c7ffb9a946b 100644 --- a/drivers/input/serio/ioc3kbd.c +++ b/drivers/input/serio/ioc3kbd.c @@ -208,7 +208,7 @@ MODULE_DEVICE_TABLE(platform, ioc3kbd_id_table); static struct platform_driver ioc3kbd_driver = { .probe = ioc3kbd_probe, - .remove_new = ioc3kbd_remove, + .remove = ioc3kbd_remove, .id_table = ioc3kbd_id_table, .driver = { .name = "ioc3-kbd", diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 6d78a1fe00c1b..c22ea532276eb 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -108,13 +108,11 @@ int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) { int retval; - serio_pause_rx(ps2dev->serio); + guard(serio_pause_rx)(ps2dev->serio); retval = ps2_do_sendbyte(ps2dev, byte, timeout, 1); dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak); - serio_continue_rx(ps2dev->serio); - return retval; } EXPORT_SYMBOL(ps2_sendbyte); @@ -162,10 +160,10 @@ void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout) ps2_begin_command(ps2dev); - serio_pause_rx(ps2dev->serio); - ps2dev->flags = PS2_FLAG_CMD; - ps2dev->cmdcnt = maxbytes; - serio_continue_rx(ps2dev->serio); + scoped_guard(serio_pause_rx, ps2dev->serio) { + ps2dev->flags = PS2_FLAG_CMD; + ps2dev->cmdcnt = maxbytes; + } wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), @@ -224,9 +222,9 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, * use alternative probe to detect it. */ if (ps2dev->cmdbuf[1] == 0xaa) { - serio_pause_rx(ps2dev->serio); - ps2dev->flags = 0; - serio_continue_rx(ps2dev->serio); + scoped_guard(serio_pause_rx, ps2dev->serio) + ps2dev->flags = 0; + timeout = 0; } @@ -235,9 +233,9 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, * won't be 2nd byte of ID response. */ if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { - serio_pause_rx(ps2dev->serio); - ps2dev->flags = ps2dev->cmdcnt = 0; - serio_continue_rx(ps2dev->serio); + scoped_guard(serio_pause_rx, ps2dev->serio) + ps2dev->flags = ps2dev->cmdcnt = 0; + timeout = 0; } break; @@ -283,6 +281,10 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) memcpy(send_param, param, send); + /* + * Not using guard notation because we need to break critical + * section below while waiting for the response. + */ serio_pause_rx(ps2dev->serio); ps2dev->cmdcnt = receive; diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c index 42ac1eb948662..3d28a5cddd611 100644 --- a/drivers/input/serio/maceps2.c +++ b/drivers/input/serio/maceps2.c @@ -159,7 +159,7 @@ static struct platform_driver maceps2_driver = { .name = "maceps2", }, .probe = maceps2_probe, - .remove_new = maceps2_remove, + .remove = maceps2_remove, }; static int __init maceps2_init(void) diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c index 0ad95e880cc20..a243248300216 100644 --- a/drivers/input/serio/olpc_apsp.c +++ b/drivers/input/serio/olpc_apsp.c @@ -256,7 +256,7 @@ MODULE_DEVICE_TABLE(of, olpc_apsp_dt_ids); static struct platform_driver olpc_apsp_driver = { .probe = olpc_apsp_probe, - .remove_new = olpc_apsp_remove, + .remove = olpc_apsp_remove, .driver = { .name = "olpc-apsp", .of_match_table = olpc_apsp_dt_ids, diff --git a/drivers/input/serio/ps2-gpio.c b/drivers/input/serio/ps2-gpio.c index 3a431395c4646..93769910ce24e 100644 --- a/drivers/input/serio/ps2-gpio.c +++ b/drivers/input/serio/ps2-gpio.c @@ -133,12 +133,12 @@ static int ps2_gpio_write(struct serio *serio, unsigned char val) int ret = 0; if (in_task()) { - mutex_lock(&drvdata->tx.mutex); + guard(mutex)(&drvdata->tx.mutex); + __ps2_gpio_write(serio, val); if (!wait_for_completion_timeout(&drvdata->tx.complete, msecs_to_jiffies(10000))) ret = SERIO_TIMEOUT; - mutex_unlock(&drvdata->tx.mutex); } else { __ps2_gpio_write(serio, val); } @@ -491,7 +491,7 @@ MODULE_DEVICE_TABLE(of, ps2_gpio_match); static struct platform_driver ps2_gpio_driver = { .probe = ps2_gpio_probe, - .remove_new = ps2_gpio_remove, + .remove = ps2_gpio_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(ps2_gpio_match), diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c index 937ecdea491d1..b96cee52fc52e 100644 --- a/drivers/input/serio/ps2mult.c +++ b/drivers/input/serio/ps2mult.c @@ -76,9 +76,8 @@ static int ps2mult_serio_write(struct serio *serio, unsigned char data) struct ps2mult *psm = serio_get_drvdata(mx_port); struct ps2mult_port *port = serio->port_data; bool need_escape; - unsigned long flags; - spin_lock_irqsave(&psm->lock, flags); + guard(spinlock_irqsave)(&psm->lock); if (psm->out_port != port) ps2mult_select_port(psm, port); @@ -93,8 +92,6 @@ static int ps2mult_serio_write(struct serio *serio, unsigned char data) serio_write(mx_port, data); - spin_unlock_irqrestore(&psm->lock, flags); - return 0; } @@ -102,11 +99,10 @@ static int ps2mult_serio_start(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio->parent); struct ps2mult_port *port = serio->port_data; - unsigned long flags; - spin_lock_irqsave(&psm->lock, flags); + guard(spinlock_irqsave)(&psm->lock); + port->registered = true; - spin_unlock_irqrestore(&psm->lock, flags); return 0; } @@ -115,11 +111,10 @@ static void ps2mult_serio_stop(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio->parent); struct ps2mult_port *port = serio->port_data; - unsigned long flags; - spin_lock_irqsave(&psm->lock, flags); + guard(spinlock_irqsave)(&psm->lock); + port->registered = false; - spin_unlock_irqrestore(&psm->lock, flags); } static int ps2mult_create_port(struct ps2mult *psm, int i) @@ -148,16 +143,12 @@ static int ps2mult_create_port(struct ps2mult *psm, int i) static void ps2mult_reset(struct ps2mult *psm) { - unsigned long flags; - - spin_lock_irqsave(&psm->lock, flags); + guard(spinlock_irqsave)(&psm->lock); serio_write(psm->mx_serio, PS2MULT_SESSION_END); serio_write(psm->mx_serio, PS2MULT_SESSION_START); ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); - - spin_unlock_irqrestore(&psm->lock, flags); } static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) @@ -234,11 +225,10 @@ static irqreturn_t ps2mult_interrupt(struct serio *serio, { struct ps2mult *psm = serio_get_drvdata(serio); struct ps2mult_port *in_port; - unsigned long flags; dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); - spin_lock_irqsave(&psm->lock, flags); + guard(spinlock_irqsave)(&psm->lock); if (psm->escape) { psm->escape = false; @@ -285,7 +275,6 @@ static irqreturn_t ps2mult_interrupt(struct serio *serio, } out: - spin_unlock_irqrestore(&psm->lock, flags); return IRQ_HANDLED; } diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index cd4d5be946a36..ae55c4de092f9 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -39,17 +39,14 @@ struct q40kbd { static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) { struct q40kbd *q40kbd = dev_id; - unsigned long flags; - spin_lock_irqsave(&q40kbd->lock, flags); + guard(spinlock_irqsave)(&q40kbd->lock); if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0); master_outb(-1, KEYBOARD_UNLOCK_REG); - spin_unlock_irqrestore(&q40kbd->lock, flags); - return IRQ_HANDLED; } @@ -60,14 +57,11 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) static void q40kbd_flush(struct q40kbd *q40kbd) { int maxread = 100; - unsigned long flags; - spin_lock_irqsave(&q40kbd->lock, flags); + guard(spinlock_irqsave)(&q40kbd->lock); while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) master_inb(KEYCODE_REG); - - spin_unlock_irqrestore(&q40kbd->lock, flags); } static void q40kbd_stop(void) @@ -166,7 +160,7 @@ static struct platform_driver q40kbd_driver = { .driver = { .name = "q40kbd", }, - .remove_new = q40kbd_remove, + .remove = q40kbd_remove, }; module_platform_driver_probe(q40kbd_driver, q40kbd_probe); diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index e236bb7e10144..c65c552b0c45b 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -144,7 +144,7 @@ static void rpckbd_remove(struct platform_device *dev) static struct platform_driver rpckbd_driver = { .probe = rpckbd_probe, - .remove_new = rpckbd_remove, + .remove = rpckbd_remove, .driver = { .name = "kart", }, diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index 1311caf7dba4f..375c6f5f905c3 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -92,7 +92,8 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) struct ps2if *ps2if = dev_id; unsigned int status; - spin_lock(&ps2if->lock); + guard(spinlock)(&ps2if->lock); + status = readl_relaxed(ps2if->base + PS2STAT); if (ps2if->head == ps2if->tail) { disable_irq_nosync(irq); @@ -101,7 +102,6 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) writel_relaxed(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA); ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1); } - spin_unlock(&ps2if->lock); return IRQ_HANDLED; } @@ -113,10 +113,9 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) static int ps2_write(struct serio *io, unsigned char val) { struct ps2if *ps2if = io->port_data; - unsigned long flags; unsigned int head; - spin_lock_irqsave(&ps2if->lock, flags); + guard(spinlock_irqsave)(&ps2if->lock); /* * If the TX register is empty, we can go straight out. @@ -133,7 +132,6 @@ static int ps2_write(struct serio *io, unsigned char val) } } - spin_unlock_irqrestore(&ps2if->lock, flags); return 0; } diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 97d8eacb91127..4468018cef664 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -38,33 +38,27 @@ static void serio_attach_driver(struct serio_driver *drv); static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) { - int retval; - - mutex_lock(&serio->drv_mutex); - retval = drv->connect(serio, drv); - mutex_unlock(&serio->drv_mutex); + guard(mutex)(&serio->drv_mutex); - return retval; + return drv->connect(serio, drv); } static int serio_reconnect_driver(struct serio *serio) { - int retval = -1; + guard(mutex)(&serio->drv_mutex); - mutex_lock(&serio->drv_mutex); if (serio->drv && serio->drv->reconnect) - retval = serio->drv->reconnect(serio); - mutex_unlock(&serio->drv_mutex); + return serio->drv->reconnect(serio); - return retval; + return -1; } static void serio_disconnect_driver(struct serio *serio) { - mutex_lock(&serio->drv_mutex); + guard(mutex)(&serio->drv_mutex); + if (serio->drv) serio->drv->disconnect(serio); - mutex_unlock(&serio->drv_mutex); } static int serio_match_port(const struct serio_device_id *ids, struct serio *serio) @@ -147,9 +141,8 @@ static LIST_HEAD(serio_event_list); static struct serio_event *serio_get_event(void) { struct serio_event *event = NULL; - unsigned long flags; - spin_lock_irqsave(&serio_event_lock, flags); + guard(spinlock_irqsave)(&serio_event_lock); if (!list_empty(&serio_event_list)) { event = list_first_entry(&serio_event_list, @@ -157,7 +150,6 @@ static struct serio_event *serio_get_event(void) list_del_init(&event->node); } - spin_unlock_irqrestore(&serio_event_lock, flags); return event; } @@ -171,9 +163,8 @@ static void serio_remove_duplicate_events(void *object, enum serio_event_type type) { struct serio_event *e, *next; - unsigned long flags; - spin_lock_irqsave(&serio_event_lock, flags); + guard(spinlock_irqsave)(&serio_event_lock); list_for_each_entry_safe(e, next, &serio_event_list, node) { if (object == e->object) { @@ -189,15 +180,13 @@ static void serio_remove_duplicate_events(void *object, serio_free_event(e); } } - - spin_unlock_irqrestore(&serio_event_lock, flags); } static void serio_handle_event(struct work_struct *work) { struct serio_event *event; - mutex_lock(&serio_mutex); + guard(mutex)(&serio_mutex); while ((event = serio_get_event())) { @@ -228,8 +217,6 @@ static void serio_handle_event(struct work_struct *work) serio_remove_duplicate_events(event->object, event->type); serio_free_event(event); } - - mutex_unlock(&serio_mutex); } static DECLARE_WORK(serio_event_work, serio_handle_event); @@ -237,11 +224,9 @@ static DECLARE_WORK(serio_event_work, serio_handle_event); static int serio_queue_event(void *object, struct module *owner, enum serio_event_type event_type) { - unsigned long flags; struct serio_event *event; - int retval = 0; - spin_lock_irqsave(&serio_event_lock, flags); + guard(spinlock_irqsave)(&serio_event_lock); /* * Scan event list for the other events for the same serio port, @@ -253,7 +238,7 @@ static int serio_queue_event(void *object, struct module *owner, list_for_each_entry_reverse(event, &serio_event_list, node) { if (event->object == object) { if (event->type == event_type) - goto out; + return 0; break; } } @@ -261,16 +246,14 @@ static int serio_queue_event(void *object, struct module *owner, event = kmalloc(sizeof(*event), GFP_ATOMIC); if (!event) { pr_err("Not enough memory to queue event %d\n", event_type); - retval = -ENOMEM; - goto out; + return -ENOMEM; } if (!try_module_get(owner)) { pr_warn("Can't get module reference, dropping event %d\n", event_type); kfree(event); - retval = -EINVAL; - goto out; + return -EINVAL; } event->type = event_type; @@ -280,9 +263,7 @@ static int serio_queue_event(void *object, struct module *owner, list_add_tail(&event->node, &serio_event_list); queue_work(system_long_wq, &serio_event_work); -out: - spin_unlock_irqrestore(&serio_event_lock, flags); - return retval; + return 0; } /* @@ -292,9 +273,8 @@ static int serio_queue_event(void *object, struct module *owner, static void serio_remove_pending_events(void *object) { struct serio_event *event, *next; - unsigned long flags; - spin_lock_irqsave(&serio_event_lock, flags); + guard(spinlock_irqsave)(&serio_event_lock); list_for_each_entry_safe(event, next, &serio_event_list, node) { if (event->object == object) { @@ -302,8 +282,6 @@ static void serio_remove_pending_events(void *object) serio_free_event(event); } } - - spin_unlock_irqrestore(&serio_event_lock, flags); } /* @@ -315,23 +293,19 @@ static void serio_remove_pending_events(void *object) static struct serio *serio_get_pending_child(struct serio *parent) { struct serio_event *event; - struct serio *serio, *child = NULL; - unsigned long flags; + struct serio *serio; - spin_lock_irqsave(&serio_event_lock, flags); + guard(spinlock_irqsave)(&serio_event_lock); list_for_each_entry(event, &serio_event_list, node) { if (event->type == SERIO_REGISTER_PORT) { serio = event->object; - if (serio->parent == parent) { - child = serio; - break; - } + if (serio->parent == parent) + return serio; } } - spin_unlock_irqrestore(&serio_event_lock, flags); - return child; + return NULL; } /* @@ -382,29 +356,27 @@ static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, c struct device_driver *drv; int error; - error = mutex_lock_interruptible(&serio_mutex); - if (error) - return error; - - if (!strncmp(buf, "none", count)) { - serio_disconnect_port(serio); - } else if (!strncmp(buf, "reconnect", count)) { - serio_reconnect_subtree(serio); - } else if (!strncmp(buf, "rescan", count)) { - serio_disconnect_port(serio); - serio_find_driver(serio); - serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); - } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { - serio_disconnect_port(serio); - error = serio_bind_driver(serio, to_serio_driver(drv)); - serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); - } else { - error = -EINVAL; + scoped_cond_guard(mutex_intr, return -EINTR, &serio_mutex) { + if (!strncmp(buf, "none", count)) { + serio_disconnect_port(serio); + } else if (!strncmp(buf, "reconnect", count)) { + serio_reconnect_subtree(serio); + } else if (!strncmp(buf, "rescan", count)) { + serio_disconnect_port(serio); + serio_find_driver(serio); + serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); + } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { + serio_disconnect_port(serio); + error = serio_bind_driver(serio, to_serio_driver(drv)); + serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); + if (error) + return error; + } else { + return -EINVAL; + } } - mutex_unlock(&serio_mutex); - - return error ? error : count; + return count; } static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf) @@ -526,9 +498,9 @@ static void serio_add_port(struct serio *serio) int error; if (parent) { - serio_pause_rx(parent); + guard(serio_pause_rx)(parent); + list_add_tail(&serio->child_node, &parent->children); - serio_continue_rx(parent); } list_add_tail(&serio->node, &serio_list); @@ -560,9 +532,9 @@ static void serio_destroy_port(struct serio *serio) serio->stop(serio); if (serio->parent) { - serio_pause_rx(serio->parent); + guard(serio_pause_rx)(serio->parent); + list_del_init(&serio->child_node); - serio_continue_rx(serio->parent); serio->parent = NULL; } @@ -701,10 +673,10 @@ EXPORT_SYMBOL(__serio_register_port); */ void serio_unregister_port(struct serio *serio) { - mutex_lock(&serio_mutex); + guard(mutex)(&serio_mutex); + serio_disconnect_port(serio); serio_destroy_port(serio); - mutex_unlock(&serio_mutex); } EXPORT_SYMBOL(serio_unregister_port); @@ -715,12 +687,12 @@ void serio_unregister_child_port(struct serio *serio) { struct serio *s, *next; - mutex_lock(&serio_mutex); + guard(mutex)(&serio_mutex); + list_for_each_entry_safe(s, next, &serio->children, child_node) { serio_disconnect_port(s); serio_destroy_port(s); } - mutex_unlock(&serio_mutex); } EXPORT_SYMBOL(serio_unregister_child_port); @@ -784,10 +756,10 @@ static void serio_driver_remove(struct device *dev) static void serio_cleanup(struct serio *serio) { - mutex_lock(&serio->drv_mutex); + guard(mutex)(&serio->drv_mutex); + if (serio->drv && serio->drv->cleanup) serio->drv->cleanup(serio); - mutex_unlock(&serio->drv_mutex); } static void serio_shutdown(struct device *dev) @@ -850,7 +822,7 @@ void serio_unregister_driver(struct serio_driver *drv) { struct serio *serio; - mutex_lock(&serio_mutex); + guard(mutex)(&serio_mutex); drv->manual_bind = true; /* so serio_find_driver ignores it */ serio_remove_pending_events(drv); @@ -866,15 +838,14 @@ void serio_unregister_driver(struct serio_driver *drv) } driver_unregister(&drv->driver); - mutex_unlock(&serio_mutex); } EXPORT_SYMBOL(serio_unregister_driver); static void serio_set_drv(struct serio *serio, struct serio_driver *drv) { - serio_pause_rx(serio); + guard(serio_pause_rx)(serio); + serio->drv = drv; - serio_continue_rx(serio); } static int serio_bus_match(struct device *dev, const struct device_driver *drv) @@ -935,14 +906,14 @@ static int serio_resume(struct device *dev) struct serio *serio = to_serio_port(dev); int error = -ENOENT; - mutex_lock(&serio->drv_mutex); - if (serio->drv && serio->drv->fast_reconnect) { - error = serio->drv->fast_reconnect(serio); - if (error && error != -ENOENT) - dev_warn(dev, "fast reconnect failed with error %d\n", - error); + scoped_guard(mutex, &serio->drv_mutex) { + if (serio->drv && serio->drv->fast_reconnect) { + error = serio->drv->fast_reconnect(serio); + if (error && error != -ENOENT) + dev_warn(dev, "fast reconnect failed with error %d\n", + error); + } } - mutex_unlock(&serio->drv_mutex); if (error) { /* @@ -989,21 +960,17 @@ EXPORT_SYMBOL(serio_close); irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int dfl) { - unsigned long flags; - irqreturn_t ret = IRQ_NONE; + guard(spinlock_irqsave)(&serio->lock); - spin_lock_irqsave(&serio->lock, flags); + if (likely(serio->drv)) + return serio->drv->interrupt(serio, data, dfl); - if (likely(serio->drv)) { - ret = serio->drv->interrupt(serio, data, dfl); - } else if (!dfl && device_is_registered(&serio->dev)) { + if (!dfl && device_is_registered(&serio->dev)) { serio_rescan(serio); - ret = IRQ_HANDLED; + return IRQ_HANDLED; } - spin_unlock_irqrestore(&serio->lock, flags); - - return ret; + return IRQ_NONE; } EXPORT_SYMBOL(serio_interrupt); diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 0186d1b38f49f..4d63950889860 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -29,7 +29,7 @@ struct serio_raw { unsigned char queue[SERIO_RAW_QUEUE_LEN]; unsigned int tail, head; - char name[16]; + char name[20]; struct kref kref; struct serio *serio; struct miscdevice dev; @@ -75,41 +75,31 @@ static int serio_raw_open(struct inode *inode, struct file *file) { struct serio_raw *serio_raw; struct serio_raw_client *client; - int retval; - retval = mutex_lock_interruptible(&serio_raw_mutex); - if (retval) - return retval; + scoped_guard(mutex_intr, &serio_raw_mutex) { + serio_raw = serio_raw_locate(iminor(inode)); + if (!serio_raw) + return -ENODEV; - serio_raw = serio_raw_locate(iminor(inode)); - if (!serio_raw) { - retval = -ENODEV; - goto out; - } + if (serio_raw->dead) + return -ENODEV; - if (serio_raw->dead) { - retval = -ENODEV; - goto out; - } + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; - client = kzalloc(sizeof(*client), GFP_KERNEL); - if (!client) { - retval = -ENOMEM; - goto out; - } + client->serio_raw = serio_raw; + file->private_data = client; - client->serio_raw = serio_raw; - file->private_data = client; + kref_get(&serio_raw->kref); - kref_get(&serio_raw->kref); + scoped_guard(serio_pause_rx, serio_raw->serio) + list_add_tail(&client->node, &serio_raw->client_list); - serio_pause_rx(serio_raw->serio); - list_add_tail(&client->node, &serio_raw->client_list); - serio_continue_rx(serio_raw->serio); + return 0; + } -out: - mutex_unlock(&serio_raw_mutex); - return retval; + return -EINTR; } static void serio_raw_free(struct kref *kref) @@ -126,9 +116,8 @@ static int serio_raw_release(struct inode *inode, struct file *file) struct serio_raw_client *client = file->private_data; struct serio_raw *serio_raw = client->serio_raw; - serio_pause_rx(serio_raw->serio); - list_del(&client->node); - serio_continue_rx(serio_raw->serio); + scoped_guard(serio_pause_rx, serio_raw->serio) + list_del(&client->node); kfree(client); @@ -139,19 +128,15 @@ static int serio_raw_release(struct inode *inode, struct file *file) static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) { - bool empty; + guard(serio_pause_rx)(serio_raw->serio); - serio_pause_rx(serio_raw->serio); - - empty = serio_raw->head == serio_raw->tail; - if (!empty) { - *c = serio_raw->queue[serio_raw->tail]; - serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; - } + if (serio_raw->head == serio_raw->tail) + return false; /* queue is empty */ - serio_continue_rx(serio_raw->serio); + *c = serio_raw->queue[serio_raw->tail]; + serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; - return !empty; + return true; } static ssize_t serio_raw_read(struct file *file, char __user *buffer, @@ -200,40 +185,32 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, { struct serio_raw_client *client = file->private_data; struct serio_raw *serio_raw = client->serio_raw; - int retval = 0; + int written = 0; unsigned char c; - retval = mutex_lock_interruptible(&serio_raw_mutex); - if (retval) - return retval; + scoped_guard(mutex_intr, &serio_raw_mutex) { + if (serio_raw->dead) + return -ENODEV; - if (serio_raw->dead) { - retval = -ENODEV; - goto out; - } + if (count > 32) + count = 32; - if (count > 32) - count = 32; + while (count--) { + if (get_user(c, buffer++)) + return -EFAULT; - while (count--) { - if (get_user(c, buffer++)) { - retval = -EFAULT; - goto out; - } + if (serio_write(serio_raw->serio, c)) { + /* Either signal error or partial write */ + return written ?: -EIO; + } - if (serio_write(serio_raw->serio, c)) { - /* Either signal error or partial write */ - if (retval == 0) - retval = -EIO; - goto out; + written++; } - retval++; + return written; } -out: - mutex_unlock(&serio_raw_mutex); - return retval; + return -EINTR; } static __poll_t serio_raw_poll(struct file *file, poll_table *wait) @@ -300,7 +277,7 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) } snprintf(serio_raw->name, sizeof(serio_raw->name), - "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no)); + "serio_raw%u", atomic_inc_return(&serio_raw_no)); kref_init(&serio_raw->kref); INIT_LIST_HEAD(&serio_raw->client_list); init_waitqueue_head(&serio_raw->wait); @@ -379,10 +356,10 @@ static void serio_raw_hangup(struct serio_raw *serio_raw) { struct serio_raw_client *client; - serio_pause_rx(serio_raw->serio); - list_for_each_entry(client, &serio_raw->client_list, node) - kill_fasync(&client->fasync, SIGIO, POLL_HUP); - serio_continue_rx(serio_raw->serio); + scoped_guard(serio_pause_rx, serio_raw->serio) { + list_for_each_entry(client, &serio_raw->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + } wake_up_interruptible(&serio_raw->wait); } @@ -394,10 +371,10 @@ static void serio_raw_disconnect(struct serio *serio) misc_deregister(&serio_raw->dev); - mutex_lock(&serio_raw_mutex); - serio_raw->dead = true; - list_del_init(&serio_raw->node); - mutex_unlock(&serio_raw_mutex); + scoped_guard(mutex, &serio_raw_mutex) { + serio_raw->dead = true; + list_del_init(&serio_raw->node); + } serio_raw_hangup(serio_raw); diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 5a2b5404ffc21..74ac887961873 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -50,11 +50,9 @@ static int serport_serio_write(struct serio *serio, unsigned char data) static int serport_serio_open(struct serio *serio) { struct serport *serport = serio->port_data; - unsigned long flags; - spin_lock_irqsave(&serport->lock, flags); + guard(spinlock_irqsave)(&serport->lock); set_bit(SERPORT_ACTIVE, &serport->flags); - spin_unlock_irqrestore(&serport->lock, flags); return 0; } @@ -63,11 +61,9 @@ static int serport_serio_open(struct serio *serio) static void serport_serio_close(struct serio *serio) { struct serport *serport = serio->port_data; - unsigned long flags; - spin_lock_irqsave(&serport->lock, flags); + guard(spinlock_irqsave)(&serport->lock); clear_bit(SERPORT_ACTIVE, &serport->flags); - spin_unlock_irqrestore(&serport->lock, flags); } /* @@ -118,14 +114,13 @@ static void serport_ldisc_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count) { struct serport *serport = tty->disc_data; - unsigned long flags; unsigned int ch_flags = 0; int i; - spin_lock_irqsave(&serport->lock, flags); + guard(spinlock_irqsave)(&serport->lock); if (!test_bit(SERPORT_ACTIVE, &serport->flags)) - goto out; + return; for (i = 0; i < count; i++) { if (fp) { @@ -146,9 +141,6 @@ static void serport_ldisc_receive(struct tty_struct *tty, const u8 *cp, serio_interrupt(serport->serio, cp[i], ch_flags); } - -out: - spin_unlock_irqrestore(&serport->lock, flags); } /* @@ -246,11 +238,9 @@ static int serport_ldisc_compat_ioctl(struct tty_struct *tty, static void serport_ldisc_hangup(struct tty_struct *tty) { struct serport *serport = tty->disc_data; - unsigned long flags; - spin_lock_irqsave(&serport->lock, flags); - set_bit(SERPORT_DEAD, &serport->flags); - spin_unlock_irqrestore(&serport->lock, flags); + scoped_guard(spinlock_irqsave, &serport->lock) + set_bit(SERPORT_DEAD, &serport->flags); wake_up_interruptible(&serport->wait); } @@ -258,12 +248,11 @@ static void serport_ldisc_hangup(struct tty_struct *tty) static void serport_ldisc_write_wakeup(struct tty_struct * tty) { struct serport *serport = tty->disc_data; - unsigned long flags; - spin_lock_irqsave(&serport->lock, flags); + guard(spinlock_irqsave)(&serport->lock); + if (test_bit(SERPORT_ACTIVE, &serport->flags)) serio_drv_write_wakeup(serport->serio); - spin_unlock_irqrestore(&serport->lock, flags); } /* diff --git a/drivers/input/serio/sun4i-ps2.c b/drivers/input/serio/sun4i-ps2.c index 95cd8aaee65da..524929ce1cae1 100644 --- a/drivers/input/serio/sun4i-ps2.c +++ b/drivers/input/serio/sun4i-ps2.c @@ -101,7 +101,7 @@ static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id) unsigned int rxflags = 0; u32 rval; - spin_lock(&drvdata->lock); + guard(spinlock)(&drvdata->lock); /* Get the PS/2 interrupts and clear them */ intr_status = readl(drvdata->reg_base + PS2_REG_LSTS); @@ -134,8 +134,6 @@ static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id) writel(intr_status, drvdata->reg_base + PS2_REG_LSTS); writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS); - spin_unlock(&drvdata->lock); - return IRQ_HANDLED; } @@ -146,7 +144,6 @@ static int sun4i_ps2_open(struct serio *serio) u32 clk_scdf; u32 clk_pcdf; u32 rval; - unsigned long flags; /* Set line control and enable interrupt */ rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN @@ -171,9 +168,8 @@ static int sun4i_ps2_open(struct serio *serio) rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER | PS2_GCTL_BUSEN; - spin_lock_irqsave(&drvdata->lock, flags); + guard(spinlock_irqsave)(&drvdata->lock); writel(rval, drvdata->reg_base + PS2_REG_GCTL); - spin_unlock_irqrestore(&drvdata->lock, flags); return 0; } @@ -322,7 +318,7 @@ MODULE_DEVICE_TABLE(of, sun4i_ps2_match); static struct platform_driver sun4i_ps2_driver = { .probe = sun4i_ps2_probe, - .remove_new = sun4i_ps2_remove, + .remove = sun4i_ps2_remove, .driver = { .name = DRIVER_NAME, .of_match_table = sun4i_ps2_match, diff --git a/drivers/input/serio/userio.c b/drivers/input/serio/userio.c index 1ab12b247f987..7f627b08055e9 100644 --- a/drivers/input/serio/userio.c +++ b/drivers/input/serio/userio.c @@ -55,18 +55,15 @@ struct userio_device { static int userio_device_write(struct serio *id, unsigned char val) { struct userio_device *userio = id->port_data; - unsigned long flags; - spin_lock_irqsave(&userio->buf_lock, flags); + scoped_guard(spinlock_irqsave, &userio->buf_lock) { + userio->buf[userio->head] = val; + userio->head = (userio->head + 1) % USERIO_BUFSIZE; - userio->buf[userio->head] = val; - userio->head = (userio->head + 1) % USERIO_BUFSIZE; - - if (userio->head == userio->tail) - dev_warn(userio_misc.this_device, - "Buffer overflowed, userio client isn't keeping up"); - - spin_unlock_irqrestore(&userio->buf_lock, flags); + if (userio->head == userio->tail) + dev_warn(userio_misc.this_device, + "Buffer overflowed, userio client isn't keeping up"); + } wake_up_interruptible(&userio->waitq); @@ -75,9 +72,8 @@ static int userio_device_write(struct serio *id, unsigned char val) static int userio_char_open(struct inode *inode, struct file *file) { - struct userio_device *userio; - - userio = kzalloc(sizeof(*userio), GFP_KERNEL); + struct userio_device *userio __free(kfree) = + kzalloc(sizeof(*userio), GFP_KERNEL); if (!userio) return -ENOMEM; @@ -86,15 +82,13 @@ static int userio_char_open(struct inode *inode, struct file *file) init_waitqueue_head(&userio->waitq); userio->serio = kzalloc(sizeof(*userio->serio), GFP_KERNEL); - if (!userio->serio) { - kfree(userio); + if (!userio->serio) return -ENOMEM; - } userio->serio->write = userio_device_write; userio->serio->port_data = userio; - file->private_data = userio; + file->private_data = no_free_ptr(userio); return 0; } @@ -118,14 +112,32 @@ static int userio_char_release(struct inode *inode, struct file *file) return 0; } +static size_t userio_fetch_data(struct userio_device *userio, u8 *buf, + size_t count, size_t *copylen) +{ + size_t available, len; + + guard(spinlock_irqsave)(&userio->buf_lock); + + available = CIRC_CNT_TO_END(userio->head, userio->tail, + USERIO_BUFSIZE); + len = min(available, count); + if (len) { + memcpy(buf, &userio->buf[userio->tail], len); + userio->tail = (userio->tail + len) % USERIO_BUFSIZE; + } + + *copylen = len; + return available; +} + static ssize_t userio_char_read(struct file *file, char __user *user_buffer, size_t count, loff_t *ppos) { struct userio_device *userio = file->private_data; int error; - size_t nonwrap_len, copylen; - unsigned char buf[USERIO_BUFSIZE]; - unsigned long flags; + size_t available, copylen; + u8 buf[USERIO_BUFSIZE]; /* * By the time we get here, the data that was waiting might have @@ -135,21 +147,8 @@ static ssize_t userio_char_read(struct file *file, char __user *user_buffer, * of course). */ for (;;) { - spin_lock_irqsave(&userio->buf_lock, flags); - - nonwrap_len = CIRC_CNT_TO_END(userio->head, - userio->tail, - USERIO_BUFSIZE); - copylen = min(nonwrap_len, count); - if (copylen) { - memcpy(buf, &userio->buf[userio->tail], copylen); - userio->tail = (userio->tail + copylen) % - USERIO_BUFSIZE; - } - - spin_unlock_irqrestore(&userio->buf_lock, flags); - - if (nonwrap_len) + available = userio_fetch_data(userio, buf, count, ©len); + if (available) break; /* buffer was/is empty */ @@ -176,40 +175,21 @@ static ssize_t userio_char_read(struct file *file, char __user *user_buffer, return copylen; } -static ssize_t userio_char_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) +static int userio_execute_cmd(struct userio_device *userio, + const struct userio_cmd *cmd) { - struct userio_device *userio = file->private_data; - struct userio_cmd cmd; - int error; - - if (count != sizeof(cmd)) { - dev_warn(userio_misc.this_device, "Invalid payload size\n"); - return -EINVAL; - } - - if (copy_from_user(&cmd, buffer, sizeof(cmd))) - return -EFAULT; - - error = mutex_lock_interruptible(&userio->mutex); - if (error) - return error; - - switch (cmd.type) { + switch (cmd->type) { case USERIO_CMD_REGISTER: if (!userio->serio->id.type) { dev_warn(userio_misc.this_device, "No port type given on /dev/userio\n"); - - error = -EINVAL; - goto out; + return -EINVAL; } if (userio->running) { dev_warn(userio_misc.this_device, "Begin command sent, but we're already running\n"); - error = -EBUSY; - goto out; + return -EBUSY; } userio->running = true; @@ -220,32 +200,51 @@ static ssize_t userio_char_write(struct file *file, const char __user *buffer, if (userio->running) { dev_warn(userio_misc.this_device, "Can't change port type on an already running userio instance\n"); - error = -EBUSY; - goto out; + return -EBUSY; } - userio->serio->id.type = cmd.data; + userio->serio->id.type = cmd->data; break; case USERIO_CMD_SEND_INTERRUPT: if (!userio->running) { dev_warn(userio_misc.this_device, "The device must be registered before sending interrupts\n"); - error = -ENODEV; - goto out; + return -ENODEV; } - serio_interrupt(userio->serio, cmd.data, 0); + serio_interrupt(userio->serio, cmd->data, 0); break; default: - error = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; + } + + return 0; +} + +static ssize_t userio_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct userio_device *userio = file->private_data; + struct userio_cmd cmd; + int error; + + if (count != sizeof(cmd)) { + dev_warn(userio_misc.this_device, "Invalid payload size\n"); + return -EINVAL; + } + + if (copy_from_user(&cmd, buffer, sizeof(cmd))) + return -EFAULT; + + scoped_cond_guard(mutex_intr, return -EINTR, &userio->mutex) { + error = userio_execute_cmd(userio, &cmd); + if (error) + return error; } -out: - mutex_unlock(&userio->mutex); - return error ?: count; + return count; } static __poll_t userio_char_poll(struct file *file, poll_table *wait) diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 1543267d02acd..01433f0b48f16 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -155,22 +155,17 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id) static int sxps2_write(struct serio *pserio, unsigned char c) { struct xps2data *drvdata = pserio->port_data; - unsigned long flags; u32 sr; - int status = -1; - spin_lock_irqsave(&drvdata->lock, flags); + guard(spinlock_irqsave)(&drvdata->lock); /* If the PS/2 transmitter is empty send a byte of data */ sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); - if (!(sr & XPS2_STATUS_TX_FULL)) { - out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c); - status = 0; - } + if (sr & XPS2_STATUS_TX_FULL) + return -EAGAIN; - spin_unlock_irqrestore(&drvdata->lock, flags); - - return status; + out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c); + return 0; } /** @@ -358,7 +353,7 @@ static struct platform_driver xps2_of_driver = { .of_match_table = xps2_of_match, }, .probe = xps2_of_probe, - .remove_new = xps2_of_remove, + .remove = xps2_of_remove, }; module_platform_driver(xps2_of_driver); diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c index a68da2988f9cd..8d6b71d597931 100644 --- a/drivers/input/tablet/pegasus_notetaker.c +++ b/drivers/input/tablet/pegasus_notetaker.c @@ -214,6 +214,28 @@ static void pegasus_init(struct work_struct *work) error); } +static int __pegasus_open(struct pegasus *pegasus) +{ + int error; + + guard(mutex)(&pegasus->pm_mutex); + + pegasus->irq->dev = pegasus->usbdev; + if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) + return -EIO; + + error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE); + if (error) { + usb_kill_urb(pegasus->irq); + cancel_work_sync(&pegasus->init); + return error; + } + + pegasus->is_open = true; + + return 0; +} + static int pegasus_open(struct input_dev *dev) { struct pegasus *pegasus = input_get_drvdata(dev); @@ -223,39 +245,25 @@ static int pegasus_open(struct input_dev *dev) if (error) return error; - mutex_lock(&pegasus->pm_mutex); - pegasus->irq->dev = pegasus->usbdev; - if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) { - error = -EIO; - goto err_autopm_put; + error = __pegasus_open(pegasus); + if (error) { + usb_autopm_put_interface(pegasus->intf); + return error; } - error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE); - if (error) - goto err_kill_urb; - - pegasus->is_open = true; - mutex_unlock(&pegasus->pm_mutex); return 0; - -err_kill_urb: - usb_kill_urb(pegasus->irq); - cancel_work_sync(&pegasus->init); -err_autopm_put: - mutex_unlock(&pegasus->pm_mutex); - usb_autopm_put_interface(pegasus->intf); - return error; } static void pegasus_close(struct input_dev *dev) { struct pegasus *pegasus = input_get_drvdata(dev); - mutex_lock(&pegasus->pm_mutex); - usb_kill_urb(pegasus->irq); - cancel_work_sync(&pegasus->init); - pegasus->is_open = false; - mutex_unlock(&pegasus->pm_mutex); + scoped_guard(mutex, &pegasus->pm_mutex) { + usb_kill_urb(pegasus->irq); + cancel_work_sync(&pegasus->init); + + pegasus->is_open = false; + } usb_autopm_put_interface(pegasus->intf); } @@ -411,10 +419,10 @@ static int pegasus_suspend(struct usb_interface *intf, pm_message_t message) { struct pegasus *pegasus = usb_get_intfdata(intf); - mutex_lock(&pegasus->pm_mutex); + guard(mutex)(&pegasus->pm_mutex); + usb_kill_urb(pegasus->irq); cancel_work_sync(&pegasus->init); - mutex_unlock(&pegasus->pm_mutex); return 0; } @@ -422,31 +430,33 @@ static int pegasus_suspend(struct usb_interface *intf, pm_message_t message) static int pegasus_resume(struct usb_interface *intf) { struct pegasus *pegasus = usb_get_intfdata(intf); - int retval = 0; - mutex_lock(&pegasus->pm_mutex); + guard(mutex)(&pegasus->pm_mutex); + if (pegasus->is_open && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) - retval = -EIO; - mutex_unlock(&pegasus->pm_mutex); + return -EIO; - return retval; + return 0; } static int pegasus_reset_resume(struct usb_interface *intf) { struct pegasus *pegasus = usb_get_intfdata(intf); - int retval = 0; + int error; + + guard(mutex)(&pegasus->pm_mutex); - mutex_lock(&pegasus->pm_mutex); if (pegasus->is_open) { - retval = pegasus_set_mode(pegasus, PEN_MODE_XY, + error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE); - if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) - retval = -EIO; + if (error) + return error; + + if (usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) + return -EIO; } - mutex_unlock(&pegasus->pm_mutex); - return retval; + return 0; } static const struct usb_device_id pegasus_ids[] = { diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index 81a3ea4b9a3dd..0468ce2b216f6 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -117,13 +117,14 @@ static int pm860x_touch_dt_init(struct platform_device *pdev, struct pm860x_chip *chip, int *res_x) { - struct device_node *np = pdev->dev.parent->of_node; struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ : chip->companion; int data, n, ret; - if (!np) + if (!pdev->dev.parent->of_node) return -ENODEV; - np = of_get_child_by_name(np, "touch"); + + struct device_node *np __free(device_node) = + of_get_child_by_name(pdev->dev.parent->of_node, "touch"); if (!np) { dev_err(&pdev->dev, "Can't find touch node\n"); return -EINVAL; @@ -141,13 +142,13 @@ static int pm860x_touch_dt_init(struct platform_device *pdev, if (data) { ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); if (ret < 0) - goto err_put_node; + return -EINVAL; } /* set tsi prebias time */ if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) { ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); if (ret < 0) - goto err_put_node; + return -EINVAL; } /* set prebias & prechg time of pen detect */ data = 0; @@ -158,18 +159,11 @@ static int pm860x_touch_dt_init(struct platform_device *pdev, if (data) { ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); if (ret < 0) - goto err_put_node; + return -EINVAL; } of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x); - of_node_put(np); - return 0; - -err_put_node: - of_node_put(np); - - return -EINVAL; } #else #define pm860x_touch_dt_init(x, y, z) (-1) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1ac26fc2e3eb9..1a03de7fcfa66 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -420,6 +420,7 @@ config TOUCHSCREEN_GOODIX_BERLIN_SPI config TOUCHSCREEN_HIDEEP tristate "HiDeep Touch IC" depends on I2C + select REGMAP_I2C help Say Y here if you have a touchscreen using HiDeep. @@ -431,6 +432,7 @@ config TOUCHSCREEN_HIDEEP config TOUCHSCREEN_HYCON_HY46XX tristate "Hycon hy46xx touchscreen support" depends on I2C + select REGMAP_I2C help Say Y here if you have a touchscreen using Hycon hy46xx diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 607f18af70104..066dc04003fa8 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -331,7 +331,7 @@ struct ser_req { u8 ref_off; u16 scratch; struct spi_message msg; - struct spi_transfer xfer[6]; + struct spi_transfer xfer[8]; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -405,9 +405,19 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) req->xfer[5].rx_buf = &req->scratch; req->xfer[5].len = 2; - CS_CHANGE(req->xfer[5]); spi_message_add_tail(&req->xfer[5], &req->msg); + /* clear the command register */ + req->scratch = 0; + req->xfer[6].tx_buf = &req->scratch; + req->xfer[6].len = 1; + spi_message_add_tail(&req->xfer[6], &req->msg); + + req->xfer[7].rx_buf = &req->scratch; + req->xfer[7].len = 2; + CS_CHANGE(req->xfer[7]); + spi_message_add_tail(&req->xfer[7], &req->msg); + mutex_lock(&ts->lock); ads7846_stop(ts); status = spi_sync(spi, &req->msg); diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 8db2a112a4767..363a4a1f1560e 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -72,7 +72,7 @@ /* * Interrupt modes: - * periodical: interrupt is asserted periodicaly + * periodical: interrupt is asserted periodically * compare coordinates: interrupt is asserted when coordinates change * indicate touch: interrupt is asserted during touch */ diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c index 9c84235327bfb..e49bde50d77a2 100644 --- a/drivers/input/touchscreen/bcm_iproc_tsc.c +++ b/drivers/input/touchscreen/bcm_iproc_tsc.c @@ -217,7 +217,7 @@ static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) "pen up-down (%d)\n", priv->pen_status); } - /* coordinates in FIFO exceed the theshold */ + /* coordinates in FIFO exceed the threshold */ if (intr_status & TS_FIFO_INTR_MASK) { for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { regmap_read(priv->regmap, FIFO_DATA, &raw_coordinate); diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c index 52e0e834e76fb..c2d3252f84668 100644 --- a/drivers/input/touchscreen/da9052_tsi.c +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -326,7 +326,7 @@ static void da9052_ts_remove(struct platform_device *pdev) static struct platform_driver da9052_tsi_driver = { .probe = da9052_ts_probe, - .remove_new = da9052_ts_remove, + .remove = da9052_ts_remove, .driver = { .name = "da9052-tsi", }, diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 85c6d8ce003f3..0d7bf18e25087 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1237,7 +1237,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client) } /* - * Check which sleep modes we can support. Power-off requieres the + * Check which sleep modes we can support. Power-off requires the * reset-pin to ensure correct power-down/power-up behaviour. Start with * the EDT_PMODE_POWEROFF test since this is the deepest possible sleep * mode. diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index eb883db554205..ad209e6e82a64 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -225,10 +225,10 @@ static int elo_command_10(struct elo *elo, unsigned char *packet) mutex_lock(&elo->cmd_mutex); - serio_pause_rx(elo->serio); - elo->expected_packet = toupper(packet[0]); - init_completion(&elo->cmd_done); - serio_continue_rx(elo->serio); + scoped_guard(serio_pause_rx, elo->serio) { + elo->expected_packet = toupper(packet[0]); + init_completion(&elo->cmd_done); + } if (serio_write(elo->serio, ELO10_LEAD_BYTE)) goto out; diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 260c83dc23a2e..fa38d70aded7b 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -898,7 +898,7 @@ static umode_t ili210x_attributes_visible(struct kobject *kobj, if (attr == &dev_attr_calibrate.attr) return priv->chip->has_calibrate_reg ? attr->mode : 0; - /* Firmware/Kernel/Protocol/BootMode is implememted only for ILI251x */ + /* Firmware/Kernel/Protocol/BootMode is implemented only for ILI251x */ if (!priv->chip->has_firmware_proto) return 0; diff --git a/drivers/input/touchscreen/imagis.c b/drivers/input/touchscreen/imagis.c index aeabf8d057ded..abeae9102323c 100644 --- a/drivers/input/touchscreen/imagis.c +++ b/drivers/input/touchscreen/imagis.c @@ -395,6 +395,7 @@ static int imagis_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume); +#ifdef CONFIG_OF static const struct imagis_properties imagis_3032c_data = { .interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE, .touch_coord_cmd = IST3038C_REG_TOUCH_COORD, @@ -427,7 +428,6 @@ static const struct imagis_properties imagis_3038c_data = { .protocol_b = true, }; -#ifdef CONFIG_OF static const struct of_device_id imagis_of_match[] = { { .compatible = "imagis,ist3032c", .data = &imagis_3032c_data }, { .compatible = "imagis,ist3038", .data = &imagis_3038_data }, diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index bfbebe245040c..5abf164ae14c1 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -261,7 +261,7 @@ static void mainstone_wm97xx_remove(struct platform_device *pdev) static struct platform_driver mainstone_wm97xx_driver = { .probe = mainstone_wm97xx_probe, - .remove_new = mainstone_wm97xx_remove, + .remove = mainstone_wm97xx_remove, .driver = { .name = "wm97xx-touch", }, diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index cbcd6e34efb7d..33635da850799 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -226,7 +226,7 @@ static void mc13783_ts_remove(struct platform_device *pdev) } static struct platform_driver mc13783_ts_driver = { - .remove_new = mc13783_ts_remove, + .remove = mc13783_ts_remove, .driver = { .name = MC13783_TS_NAME, }, diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c index 0afee41ac9de0..44b58e0dc1ada 100644 --- a/drivers/input/touchscreen/novatek-nvt-ts.c +++ b/drivers/input/touchscreen/novatek-nvt-ts.c @@ -31,9 +31,6 @@ #define NVT_TS_PARAMS_CHIP_ID 0x0e #define NVT_TS_PARAMS_SIZE 0x0f -#define NVT_TS_SUPPORTED_WAKE_TYPE 0x05 -#define NVT_TS_SUPPORTED_CHIP_ID 0x05 - #define NVT_TS_MAX_TOUCHES 10 #define NVT_TS_MAX_SIZE 4096 @@ -51,10 +48,16 @@ static const int nvt_ts_irq_type[4] = { IRQF_TRIGGER_HIGH }; +struct nvt_ts_i2c_chip_data { + u8 wake_type; + u8 chip_id; +}; + struct nvt_ts_data { struct i2c_client *client; struct input_dev *input; struct gpio_desc *reset_gpio; + struct regulator_bulk_data regulators[2]; struct touchscreen_properties prop; int max_touches; u8 buf[NVT_TS_TOUCH_SIZE * NVT_TS_MAX_TOUCHES]; @@ -142,6 +145,13 @@ static irqreturn_t nvt_ts_irq(int irq, void *dev_id) static int nvt_ts_start(struct input_dev *dev) { struct nvt_ts_data *data = input_get_drvdata(dev); + int error; + + error = regulator_bulk_enable(ARRAY_SIZE(data->regulators), data->regulators); + if (error) { + dev_err(&data->client->dev, "failed to enable regulators\n"); + return error; + } enable_irq(data->client->irq); gpiod_set_value_cansleep(data->reset_gpio, 0); @@ -155,6 +165,7 @@ static void nvt_ts_stop(struct input_dev *dev) disable_irq(data->client->irq); gpiod_set_value_cansleep(data->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); } static int nvt_ts_suspend(struct device *dev) @@ -188,6 +199,7 @@ static int nvt_ts_probe(struct i2c_client *client) struct device *dev = &client->dev; int error, width, height, irq_type; struct nvt_ts_data *data; + const struct nvt_ts_i2c_chip_data *chip; struct input_dev *input; if (!client->irq) { @@ -199,12 +211,35 @@ static int nvt_ts_probe(struct i2c_client *client) if (!data) return -ENOMEM; + chip = device_get_match_data(&client->dev); + if (!chip) + return -EINVAL; + data->client = client; i2c_set_clientdata(client, data); + /* + * VCC is the analog voltage supply + * IOVCC is the digital voltage supply + */ + data->regulators[0].supply = "vcc"; + data->regulators[1].supply = "iovcc"; + error = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators), data->regulators); + if (error) { + dev_err(dev, "cannot get regulators: %d\n", error); + return error; + } + + error = regulator_bulk_enable(ARRAY_SIZE(data->regulators), data->regulators); + if (error) { + dev_err(dev, "failed to enable regulators: %d\n", error); + return error; + } + data->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); error = PTR_ERR_OR_ZERO(data->reset_gpio); if (error) { + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); dev_err(dev, "failed to request reset GPIO: %d\n", error); return error; } @@ -214,6 +249,7 @@ static int nvt_ts_probe(struct i2c_client *client) error = nvt_ts_read_data(data->client, NVT_TS_PARAMETERS_START, data->buf, NVT_TS_PARAMS_SIZE); gpiod_set_value_cansleep(data->reset_gpio, 1); /* Put back in reset */ + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); if (error) return error; @@ -225,8 +261,8 @@ static int nvt_ts_probe(struct i2c_client *client) if (width > NVT_TS_MAX_SIZE || height >= NVT_TS_MAX_SIZE || data->max_touches > NVT_TS_MAX_TOUCHES || irq_type >= ARRAY_SIZE(nvt_ts_irq_type) || - data->buf[NVT_TS_PARAMS_WAKE_TYPE] != NVT_TS_SUPPORTED_WAKE_TYPE || - data->buf[NVT_TS_PARAMS_CHIP_ID] != NVT_TS_SUPPORTED_CHIP_ID) { + data->buf[NVT_TS_PARAMS_WAKE_TYPE] != chip->wake_type || + data->buf[NVT_TS_PARAMS_CHIP_ID] != chip->chip_id) { dev_err(dev, "Unsupported touchscreen parameters: %*ph\n", NVT_TS_PARAMS_SIZE, data->buf); return -EIO; @@ -277,8 +313,26 @@ static int nvt_ts_probe(struct i2c_client *client) return 0; } +static const struct nvt_ts_i2c_chip_data nvt_nt11205_ts_data = { + .wake_type = 0x05, + .chip_id = 0x05, +}; + +static const struct nvt_ts_i2c_chip_data nvt_nt36672a_ts_data = { + .wake_type = 0x01, + .chip_id = 0x08, +}; + +static const struct of_device_id nvt_ts_of_match[] = { + { .compatible = "novatek,nt11205-ts", .data = &nvt_nt11205_ts_data }, + { .compatible = "novatek,nt36672a-ts", .data = &nvt_nt36672a_ts_data }, + { } +}; +MODULE_DEVICE_TABLE(of, nvt_ts_of_match); + static const struct i2c_device_id nvt_ts_i2c_id[] = { - { "NVT-ts" }, + { "nt11205-ts", (unsigned long) &nvt_nt11205_ts_data }, + { "nt36672a-ts", (unsigned long) &nvt_nt36672a_ts_data }, { } }; MODULE_DEVICE_TABLE(i2c, nvt_ts_i2c_id); @@ -287,6 +341,7 @@ static struct i2c_driver nvt_ts_driver = { .driver = { .name = "novatek-nvt-ts", .pm = pm_sleep_ptr(&nvt_ts_pm_ops), + .of_match_table = nvt_ts_of_match, }, .probe = nvt_ts_probe, .id_table = nvt_ts_i2c_id, diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c index 821245019feaa..083206a3457b8 100644 --- a/drivers/input/touchscreen/pcap_ts.c +++ b/drivers/input/touchscreen/pcap_ts.c @@ -238,7 +238,7 @@ static const struct dev_pm_ops pcap_ts_pm_ops = { static struct platform_driver pcap_ts_driver = { .probe = pcap_ts_probe, - .remove_new = pcap_ts_remove, + .remove = pcap_ts_remove, .driver = { .name = "pcap-ts", .pm = PCAP_TS_PM_OPS, diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 83bf27085ebc0..dad5786e82a46 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -44,7 +44,7 @@ enum pixcir_power_mode { /* * Interrupt modes: - * periodical: interrupt is asserted periodicaly + * periodical: interrupt is asserted periodically * diff coordinates: interrupt is asserted when coordinates change * level on touch: interrupt level asserted during touch * pulse on touch: interrupt pulse asserted during touch diff --git a/drivers/input/touchscreen/raspberrypi-ts.c b/drivers/input/touchscreen/raspberrypi-ts.c index 45c575df994e0..841d39a449b37 100644 --- a/drivers/input/touchscreen/raspberrypi-ts.c +++ b/drivers/input/touchscreen/raspberrypi-ts.c @@ -122,20 +122,18 @@ static int rpi_ts_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct input_dev *input; - struct device_node *fw_node; struct rpi_firmware *fw; struct rpi_ts *ts; u32 touchbuf; int error; - fw_node = of_get_parent(np); + struct device_node *fw_node __free(device_node) = of_get_parent(np); if (!fw_node) { dev_err(dev, "Missing firmware node\n"); return -ENOENT; } fw = devm_rpi_firmware_get(&pdev->dev, fw_node); - of_node_put(fw_node); if (!fw) return -EPROBE_DEFER; diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c index 0e5cc9fbad177..295d8d75ba322 100644 --- a/drivers/input/touchscreen/rohm_bu21023.c +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -388,13 +388,13 @@ static int rohm_ts_manual_calibration(struct rohm_ts_data *ts) err_y = (int)READ_CALIB_BUF(PRM1_Y_H) << 2 | READ_CALIB_BUF(PRM1_Y_L); - /* X axis ajust */ + /* X axis adjust */ if (err_x <= 4) calib_x -= AXIS_ADJUST; else if (err_x >= 60) calib_x += AXIS_ADJUST; - /* Y axis ajust */ + /* Y axis adjust */ if (err_y <= 4) calib_y -= AXIS_ADJUST; else if (err_y >= 60) diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index b204fdb2d22c6..a94a1997f96b7 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -107,7 +107,7 @@ static void stmpe_work(struct work_struct *work) /* * touch_det sometimes get desasserted or just get stuck. This appears - * to be a silicon bug, We still have to clearify this with the + * to be a silicon bug, We still have to clarify this with the * manufacture. As a workaround We release the key anyway if the * touch_det keeps coming in after 4ms, while the FIFO contains no value * during the whole time. @@ -140,7 +140,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) /* * The FIFO sometimes just crashes and stops generating interrupts. This - * appears to be a silicon bug. We still have to clearify this with + * appears to be a silicon bug. We still have to clarify this with * the manufacture. As a workaround we disable the TSC while we are * collecting data and flush the FIFO after reading */ @@ -362,7 +362,7 @@ static struct platform_driver stmpe_ts_driver = { .name = STMPE_TS_NAME, }, .probe = stmpe_input_probe, - .remove_new = stmpe_ts_remove, + .remove = stmpe_ts_remove, }; module_platform_driver(stmpe_ts_driver); diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index 92b2b840b4b7b..e8286060043ae 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -396,12 +396,12 @@ static const struct of_device_id sun4i_ts_of_match[] = { MODULE_DEVICE_TABLE(of, sun4i_ts_of_match); static struct platform_driver sun4i_ts_driver = { - .driver = { + .driver = { .name = "sun4i-ts", .of_match_table = sun4i_ts_of_match, }, .probe = sun4i_ts_probe, - .remove_new = sun4i_ts_remove, + .remove = sun4i_ts_remove, }; module_platform_driver(sun4i_ts_driver); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 294b7ceded272..93d659ff90aa9 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -550,9 +550,9 @@ MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids); static struct platform_driver ti_tsc_driver = { .probe = titsc_probe, - .remove_new = titsc_remove, + .remove = titsc_remove, .driver = { - .name = "TI-am335x-tsc", + .name = "TI-am335x-tsc", .pm = pm_sleep_ptr(&titsc_pm_ops), .of_match_table = ti_tsc_dt_ids, }, diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c index 6cf66aadc10ed..98422d1e80d6d 100644 --- a/drivers/input/touchscreen/ts4800-ts.c +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -110,18 +110,17 @@ static int ts4800_parse_dt(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct device_node *syscon_np; u32 reg, bit; int error; - syscon_np = of_parse_phandle(np, "syscon", 0); + struct device_node *syscon_np __free(device_node) = + of_parse_phandle(np, "syscon", 0); if (!syscon_np) { dev_err(dev, "no syscon property\n"); return -ENODEV; } ts->regmap = syscon_node_to_regmap(syscon_np); - of_node_put(syscon_np); if (IS_ERR(ts->regmap)) { dev_err(dev, "cannot get parent's regmap\n"); return PTR_ERR(ts->regmap); diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c index 9cee26b63341d..98f8ec408cad6 100644 --- a/drivers/input/touchscreen/wm831x-ts.c +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -387,7 +387,7 @@ static struct platform_driver wm831x_ts_driver = { .name = "wm831x-touch", }, .probe = wm831x_ts_probe, - .remove_new = wm831x_ts_remove, + .remove = wm831x_ts_remove, }; module_platform_driver(wm831x_ts_driver); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index f01f6cc9b59fa..b25771a8df2b3 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -222,7 +222,7 @@ EXPORT_SYMBOL_GPL(wm97xx_set_gpio); /* * Codec GPIO pin configuration, this sets pin direction, polarity, - * stickyness and wake up. + * stickiness and wake up. */ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky, @@ -403,7 +403,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) * is actively working with the touchscreen we * don't want to lose the quick response. So we * will slowly increase sleep time after the - * pen is up and quicky restore it to ~one task + * pen is up and quickly restore it to ~one task * switch when pen is down again. */ if (wm->ts_reader_interval < HZ / 10) @@ -876,7 +876,7 @@ static struct platform_driver wm97xx_mfd_driver = { .pm = pm_sleep_ptr(&wm97xx_pm_ops), }, .probe = wm97xx_mfd_probe, - .remove_new = wm97xx_mfd_remove, + .remove = wm97xx_mfd_remove, }; static int __init wm97xx_init(void) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 7e9b996b47c83..9d5404a07e8aa 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -808,7 +808,7 @@ void icc_put(struct icc_path *path) mutex_unlock(&icc_bw_lock); mutex_unlock(&icc_lock); - kfree_const(path->name); + kfree(path->name); kfree(path); } EXPORT_SYMBOL_GPL(icc_put); @@ -1081,7 +1081,7 @@ static int of_count_icc_providers(struct device_node *np) int count = 0; for_each_available_child_of_node(np, child) { - if (of_property_read_bool(child, "#interconnect-cells") && + if (of_property_present(child, "#interconnect-cells") && likely(!of_match_node(ignore_list, child))) count++; count += of_count_icc_providers(child); diff --git a/drivers/interconnect/imx/imx8mm.c b/drivers/interconnect/imx/imx8mm.c index a36aaaf106aee..efed12d635c1d 100644 --- a/drivers/interconnect/imx/imx8mm.c +++ b/drivers/interconnect/imx/imx8mm.c @@ -88,7 +88,7 @@ static int imx8mm_icc_probe(struct platform_device *pdev) static struct platform_driver imx8mm_icc_driver = { .probe = imx8mm_icc_probe, - .remove_new = imx_icc_unregister, + .remove = imx_icc_unregister, .driver = { .name = "imx8mm-interconnect", }, diff --git a/drivers/interconnect/imx/imx8mn.c b/drivers/interconnect/imx/imx8mn.c index 2a97c74e875b9..535fae791f2e9 100644 --- a/drivers/interconnect/imx/imx8mn.c +++ b/drivers/interconnect/imx/imx8mn.c @@ -77,7 +77,7 @@ static int imx8mn_icc_probe(struct platform_device *pdev) static struct platform_driver imx8mn_icc_driver = { .probe = imx8mn_icc_probe, - .remove_new = imx_icc_unregister, + .remove = imx_icc_unregister, .driver = { .name = "imx8mn-interconnect", }, diff --git a/drivers/interconnect/imx/imx8mp.c b/drivers/interconnect/imx/imx8mp.c index 86d4c1517b263..c5751ed18d519 100644 --- a/drivers/interconnect/imx/imx8mp.c +++ b/drivers/interconnect/imx/imx8mp.c @@ -241,7 +241,7 @@ static int imx8mp_icc_probe(struct platform_device *pdev) static struct platform_driver imx8mp_icc_driver = { .probe = imx8mp_icc_probe, - .remove_new = imx_icc_unregister, + .remove = imx_icc_unregister, .driver = { .name = "imx8mp-interconnect", }, diff --git a/drivers/interconnect/imx/imx8mq.c b/drivers/interconnect/imx/imx8mq.c index f817d24aeefb6..6aa4f06b46764 100644 --- a/drivers/interconnect/imx/imx8mq.c +++ b/drivers/interconnect/imx/imx8mq.c @@ -87,7 +87,7 @@ static int imx8mq_icc_probe(struct platform_device *pdev) static struct platform_driver imx8mq_icc_driver = { .probe = imx8mq_icc_probe, - .remove_new = imx_icc_unregister, + .remove = imx_icc_unregister, .driver = { .name = "imx8mq-interconnect", .sync_state = icc_sync_state, diff --git a/drivers/interconnect/mediatek/mt8183.c b/drivers/interconnect/mediatek/mt8183.c index 24245085c7a94..c212e79334cf5 100644 --- a/drivers/interconnect/mediatek/mt8183.c +++ b/drivers/interconnect/mediatek/mt8183.c @@ -133,7 +133,7 @@ static struct platform_driver mtk_emi_icc_mt8183_driver = { .sync_state = icc_sync_state, }, .probe = mtk_emi_icc_probe, - .remove_new = mtk_emi_icc_remove, + .remove = mtk_emi_icc_remove, }; module_platform_driver(mtk_emi_icc_mt8183_driver); diff --git a/drivers/interconnect/mediatek/mt8195.c b/drivers/interconnect/mediatek/mt8195.c index 710e14c5447cc..3ca23469ab18d 100644 --- a/drivers/interconnect/mediatek/mt8195.c +++ b/drivers/interconnect/mediatek/mt8195.c @@ -329,7 +329,7 @@ static struct platform_driver mtk_emi_icc_mt8195_driver = { .sync_state = icc_sync_state, }, .probe = mtk_emi_icc_probe, - .remove_new = mtk_emi_icc_remove, + .remove = mtk_emi_icc_remove, }; module_platform_driver(mtk_emi_icc_mt8195_driver); diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index de96d46613406..362fb9b0a198a 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -105,6 +105,26 @@ config INTERCONNECT_QCOM_QCS404 This is a driver for the Qualcomm Network-on-Chip on qcs404-based platforms. +config INTERCONNECT_QCOM_QCS615 + tristate "Qualcomm QCS615 interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on qcs615-based + platforms. + +config INTERCONNECT_QCOM_QCS8300 + tristate "Qualcomm QCS8300 interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Technologies, Inc. Network-on-Chip + on QCS8300-based platforms. The interconnect provider collects and + aggreagates the cosumer bandwidth requests to satisfy constraints + placed on Network-on-Chip performance states. + config INTERCONNECT_QCOM_QDU1000 tristate "Qualcomm QDU1000/QRU1000 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE @@ -137,6 +157,15 @@ config INTERCONNECT_QCOM_SA8775P This is a driver for the Qualcomm Network-on-Chip on sa8775p-based platforms. +config INTERCONNECT_QCOM_SAR2130P + tristate "Qualcomm SAR2130P interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on SAR2130P-based + platforms. + config INTERCONNECT_QCOM_SC7180 tristate "Qualcomm SC7180 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index bfeea8416fcf9..9997728c02bf1 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -15,9 +15,12 @@ qnoc-msm8996-objs := msm8996.o icc-osm-l3-objs := osm-l3.o qnoc-qcm2290-objs := qcm2290.o qnoc-qcs404-objs := qcs404.o +qnoc-qcs615-objs := qcs615.o +qnoc-qcs8300-objs := qcs8300.o qnoc-qdu1000-objs := qdu1000.o icc-rpmh-obj := icc-rpmh.o qnoc-sa8775p-objs := sa8775p.o +qnoc-sar2130p-objs := sar2130p.o qnoc-sc7180-objs := sc7180.o qnoc-sc7280-objs := sc7280.o qnoc-sc8180x-objs := sc8180x.o @@ -52,9 +55,12 @@ obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o +obj-$(CONFIG_INTERCONNECT_QCOM_QCS615) += qnoc-qcs615.o +obj-$(CONFIG_INTERCONNECT_QCOM_QCS8300) += qnoc-qcs8300.o obj-$(CONFIG_INTERCONNECT_QCOM_QDU1000) += qnoc-qdu1000.o obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o obj-$(CONFIG_INTERCONNECT_QCOM_SA8775P) += qnoc-sa8775p.o +obj-$(CONFIG_INTERCONNECT_QCOM_SAR2130P) += qnoc-sar2130p.o obj-$(CONFIG_INTERCONNECT_QCOM_SC7180) += qnoc-sc7180.o obj-$(CONFIG_INTERCONNECT_QCOM_SC7280) += qnoc-sc7280.o obj-$(CONFIG_INTERCONNECT_QCOM_SC8180X) += qnoc-sc8180x.o diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index f49a8e0cb03c0..f2d63745be54c 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -311,7 +311,10 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) } qp->num_clks = devm_clk_bulk_get_all(qp->dev, &qp->clks); - if (qp->num_clks < 0 || (!qp->num_clks && desc->qos_clks_required)) { + if (qp->num_clks == -EPROBE_DEFER) + return dev_err_probe(dev, qp->num_clks, "Failed to get QoS clocks\n"); + + if (qp->num_clks < 0 || (!qp->num_clks && desc->qos_requires_clocks)) { dev_info(dev, "Skipping QoS, failed to get clk: %d\n", qp->num_clks); goto skip_qos_config; } diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index 14db89850fb3d..82344c734091e 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -153,7 +153,7 @@ struct qcom_icc_desc { size_t num_nodes; struct qcom_icc_bcm * const *bcms; size_t num_bcms; - bool qos_clks_required; + bool qos_requires_clocks; }; int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, diff --git a/drivers/interconnect/qcom/msm8909.c b/drivers/interconnect/qcom/msm8909.c index 0d0cd7282f5b7..dd656ce7b64d1 100644 --- a/drivers/interconnect/qcom/msm8909.c +++ b/drivers/interconnect/qcom/msm8909.c @@ -1316,7 +1316,7 @@ MODULE_DEVICE_TABLE(of, msm8909_noc_of_match); static struct platform_driver msm8909_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8909", .of_match_table = msm8909_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c index 499b1a9ac413b..35148880b3e87 100644 --- a/drivers/interconnect/qcom/msm8916.c +++ b/drivers/interconnect/qcom/msm8916.c @@ -1344,7 +1344,7 @@ MODULE_DEVICE_TABLE(of, msm8916_noc_of_match); static struct platform_driver msm8916_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8916", .of_match_table = msm8916_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8937.c b/drivers/interconnect/qcom/msm8937.c index 052b14c28ef8b..58533d00266b4 100644 --- a/drivers/interconnect/qcom/msm8937.c +++ b/drivers/interconnect/qcom/msm8937.c @@ -1175,7 +1175,7 @@ static struct qcom_icc_node slv_lpass = { .qos.qos_mode = NOC_QOS_MODE_INVALID, }; -static struct qcom_icc_node *msm8937_bimc_nodes[] = { +static struct qcom_icc_node * const msm8937_bimc_nodes[] = { [MAS_APPS_PROC] = &mas_apps_proc, [MAS_OXILI] = &mas_oxili, [MAS_SNOC_BIMC_0] = &mas_snoc_bimc_0, @@ -1204,7 +1204,7 @@ static const struct qcom_icc_desc msm8937_bimc = { .ab_coeff = 154, }; -static struct qcom_icc_node *msm8937_pcnoc_nodes[] = { +static struct qcom_icc_node * const msm8937_pcnoc_nodes[] = { [MAS_SPDM] = &mas_spdm, [MAS_BLSP_1] = &mas_blsp_1, [MAS_BLSP_2] = &mas_blsp_2, @@ -1268,7 +1268,7 @@ static const struct qcom_icc_desc msm8937_pcnoc = { .regmap_cfg = &msm8937_pcnoc_regmap_config, }; -static struct qcom_icc_node *msm8937_snoc_nodes[] = { +static struct qcom_icc_node * const msm8937_snoc_nodes[] = { [MAS_QDSS_BAM] = &mas_qdss_bam, [MAS_BIMC_SNOC] = &mas_bimc_snoc, [MAS_PCNOC_SNOC] = &mas_pcnoc_snoc, @@ -1304,7 +1304,7 @@ static const struct qcom_icc_desc msm8937_snoc = { .qos_offset = 0x7000, }; -static struct qcom_icc_node *msm8937_snoc_mm_nodes[] = { +static struct qcom_icc_node * const msm8937_snoc_mm_nodes[] = { [MAS_JPEG] = &mas_jpeg, [MAS_MDP] = &mas_mdp, [MAS_VENUS] = &mas_venus, @@ -1337,7 +1337,7 @@ MODULE_DEVICE_TABLE(of, msm8937_noc_of_match); static struct platform_driver msm8937_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8937", .of_match_table = msm8937_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index 8ff2c23b1ca0d..b52c5ac1175c3 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -1421,7 +1421,7 @@ MODULE_DEVICE_TABLE(of, msm8939_noc_of_match); static struct platform_driver msm8939_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8939", .of_match_table = msm8939_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8953.c b/drivers/interconnect/qcom/msm8953.c index 62f8c0774b3ec..be2b1a606612c 100644 --- a/drivers/interconnect/qcom/msm8953.c +++ b/drivers/interconnect/qcom/msm8953.c @@ -1310,7 +1310,7 @@ static const struct of_device_id msm8953_noc_of_match[] = { static struct platform_driver msm8953_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8953", .of_match_table = msm8953_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index 241076b5f36b4..469fc48ebfe94 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -762,7 +762,7 @@ MODULE_DEVICE_TABLE(of, msm8974_noc_of_match); static struct platform_driver msm8974_noc_driver = { .probe = msm8974_icc_probe, - .remove_new = msm8974_icc_remove, + .remove = msm8974_icc_remove, .driver = { .name = "qnoc-msm8974", .of_match_table = msm8974_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8976.c b/drivers/interconnect/qcom/msm8976.c index ab963def77c3a..4e2ac7ebe7429 100644 --- a/drivers/interconnect/qcom/msm8976.c +++ b/drivers/interconnect/qcom/msm8976.c @@ -1427,7 +1427,7 @@ MODULE_DEVICE_TABLE(of, msm8976_noc_of_match); static struct platform_driver msm8976_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8976", .of_match_table = msm8976_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index 788131400cd13..b73566c9b21f9 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -2108,7 +2108,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8996", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index 61a8695a9adc7..6a656ed44d49b 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -290,7 +290,7 @@ MODULE_DEVICE_TABLE(of, osm_l3_of_match); static struct platform_driver osm_l3_driver = { .probe = qcom_osm_l3_probe, - .remove_new = qcom_osm_l3_remove, + .remove = qcom_osm_l3_remove, .driver = { .name = "osm-l3", .of_match_table = osm_l3_of_match, diff --git a/drivers/interconnect/qcom/qcm2290.c b/drivers/interconnect/qcom/qcm2290.c index ccbdc6202c07a..e120bc1395f35 100644 --- a/drivers/interconnect/qcom/qcm2290.c +++ b/drivers/interconnect/qcom/qcm2290.c @@ -1367,7 +1367,7 @@ MODULE_DEVICE_TABLE(of, qcm2290_noc_of_match); static struct platform_driver qcm2290_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-qcm2290", .of_match_table = qcm2290_noc_of_match, diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index 63e9ff223ac49..ceac7a6987695 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -1204,7 +1204,7 @@ MODULE_DEVICE_TABLE(of, qcs404_noc_of_match); static struct platform_driver qcs404_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-qcs404", .of_match_table = qcs404_noc_of_match, diff --git a/drivers/interconnect/qcom/qcs615.c b/drivers/interconnect/qcom/qcs615.c new file mode 100644 index 0000000000000..7e59e91ce886d --- /dev/null +++ b/drivers/interconnect/qcom/qcs615.c @@ -0,0 +1,1563 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" +#include "qcs615.h" + +static struct qcom_icc_node qhm_a1noc_cfg = { + .name = "qhm_a1noc_cfg", + .id = QCS615_MASTER_A1NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_SERVICE_A2NOC }, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = QCS615_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = QCS615_MASTER_QSPI, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = QCS615_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = QCS615_MASTER_BLSP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_cnoc = { + .name = "qnm_cnoc", + .id = QCS615_MASTER_CNOC_A2NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = QCS615_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = QCS615_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_LPASS_SNOC }, +}; + +static struct qcom_icc_node xm_emac_avb = { + .name = "xm_emac_avb", + .id = QCS615_MASTER_EMAC_EVB, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_pcie = { + .name = "xm_pcie", + .id = QCS615_MASTER_PCIE, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_ANOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr = { + .name = "xm_qdss_etr", + .id = QCS615_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .id = QCS615_MASTER_SDCC_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = QCS615_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = QCS615_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb2 = { + .name = "xm_usb2", + .id = QCS615_MASTER_USB2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = QCS615_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_camnoc_hf0_uncomp = { + .name = "qxm_camnoc_hf0_uncomp", + .id = QCS615_MASTER_CAMNOC_HF0_UNCOMP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_CAMNOC_UNCOMP }, +}; + +static struct qcom_icc_node qxm_camnoc_hf1_uncomp = { + .name = "qxm_camnoc_hf1_uncomp", + .id = QCS615_MASTER_CAMNOC_HF1_UNCOMP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_CAMNOC_UNCOMP }, +}; + +static struct qcom_icc_node qxm_camnoc_sf_uncomp = { + .name = "qxm_camnoc_sf_uncomp", + .id = QCS615_MASTER_CAMNOC_SF_UNCOMP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_CAMNOC_UNCOMP }, +}; + +static struct qcom_icc_node qhm_spdm = { + .name = "qhm_spdm", + .id = QCS615_MASTER_SPDM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_CNOC_A2NOC }, +}; + +static struct qcom_icc_node qnm_snoc = { + .name = "qnm_snoc", + .id = QCS615_MASTER_SNOC_CNOC, + .channels = 1, + .buswidth = 8, + .num_links = 39, + .links = { QCS615_SLAVE_A1NOC_CFG, QCS615_SLAVE_AHB2PHY_EAST, + QCS615_SLAVE_AHB2PHY_WEST, QCS615_SLAVE_AOP, + QCS615_SLAVE_AOSS, QCS615_SLAVE_CAMERA_CFG, + QCS615_SLAVE_CLK_CTL, QCS615_SLAVE_RBCPR_CX_CFG, + QCS615_SLAVE_RBCPR_MX_CFG, QCS615_SLAVE_CRYPTO_0_CFG, + QCS615_SLAVE_CNOC_DDRSS, QCS615_SLAVE_DISPLAY_CFG, + QCS615_SLAVE_EMAC_AVB_CFG, QCS615_SLAVE_GLM, + QCS615_SLAVE_GFX3D_CFG, QCS615_SLAVE_IMEM_CFG, + QCS615_SLAVE_IPA_CFG, QCS615_SLAVE_CNOC_MNOC_CFG, + QCS615_SLAVE_PCIE_CFG, QCS615_SLAVE_PIMEM_CFG, + QCS615_SLAVE_PRNG, QCS615_SLAVE_QDSS_CFG, + QCS615_SLAVE_QSPI, QCS615_SLAVE_QUP_0, + QCS615_SLAVE_QUP_1, QCS615_SLAVE_SDCC_1, + QCS615_SLAVE_SDCC_2, QCS615_SLAVE_SNOC_CFG, + QCS615_SLAVE_SPDM_WRAPPER, QCS615_SLAVE_TCSR, + QCS615_SLAVE_TLMM_EAST, QCS615_SLAVE_TLMM_SOUTH, + QCS615_SLAVE_TLMM_WEST, QCS615_SLAVE_UFS_MEM_CFG, + QCS615_SLAVE_USB2, QCS615_SLAVE_USB3, + QCS615_SLAVE_VENUS_CFG, QCS615_SLAVE_VSENSE_CTRL_CFG, + QCS615_SLAVE_SERVICE_CNOC }, +}; + +static struct qcom_icc_node xm_qdss_dap = { + .name = "xm_qdss_dap", + .id = QCS615_MASTER_QDSS_DAP, + .channels = 1, + .buswidth = 8, + .num_links = 40, + .links = { QCS615_SLAVE_A1NOC_CFG, QCS615_SLAVE_AHB2PHY_EAST, + QCS615_SLAVE_AHB2PHY_WEST, QCS615_SLAVE_AOP, + QCS615_SLAVE_AOSS, QCS615_SLAVE_CAMERA_CFG, + QCS615_SLAVE_CLK_CTL, QCS615_SLAVE_RBCPR_CX_CFG, + QCS615_SLAVE_RBCPR_MX_CFG, QCS615_SLAVE_CRYPTO_0_CFG, + QCS615_SLAVE_CNOC_DDRSS, QCS615_SLAVE_DISPLAY_CFG, + QCS615_SLAVE_EMAC_AVB_CFG, QCS615_SLAVE_GLM, + QCS615_SLAVE_GFX3D_CFG, QCS615_SLAVE_IMEM_CFG, + QCS615_SLAVE_IPA_CFG, QCS615_SLAVE_CNOC_MNOC_CFG, + QCS615_SLAVE_PCIE_CFG, QCS615_SLAVE_PIMEM_CFG, + QCS615_SLAVE_PRNG, QCS615_SLAVE_QDSS_CFG, + QCS615_SLAVE_QSPI, QCS615_SLAVE_QUP_0, + QCS615_SLAVE_QUP_1, QCS615_SLAVE_SDCC_1, + QCS615_SLAVE_SDCC_2, QCS615_SLAVE_SNOC_CFG, + QCS615_SLAVE_SPDM_WRAPPER, QCS615_SLAVE_TCSR, + QCS615_SLAVE_TLMM_EAST, QCS615_SLAVE_TLMM_SOUTH, + QCS615_SLAVE_TLMM_WEST, QCS615_SLAVE_UFS_MEM_CFG, + QCS615_SLAVE_USB2, QCS615_SLAVE_USB3, + QCS615_SLAVE_VENUS_CFG, QCS615_SLAVE_VSENSE_CTRL_CFG, + QCS615_SLAVE_CNOC_A2NOC, QCS615_SLAVE_SERVICE_CNOC }, +}; + +static struct qcom_icc_node qhm_cnoc = { + .name = "qhm_cnoc", + .id = QCS615_MASTER_CNOC_DC_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 2, + .links = { QCS615_SLAVE_DC_NOC_GEMNOC, QCS615_SLAVE_LLCC_CFG }, +}; + +static struct qcom_icc_node acm_apps = { + .name = "acm_apps", + .id = QCS615_MASTER_APPSS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { QCS615_SLAVE_GEM_NOC_SNOC, QCS615_SLAVE_LLCC, + QCS615_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node acm_gpu_tcu = { + .name = "acm_gpu_tcu", + .id = QCS615_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS615_SLAVE_GEM_NOC_SNOC, QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node acm_sys_tcu = { + .name = "acm_sys_tcu", + .id = QCS615_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS615_SLAVE_GEM_NOC_SNOC, QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qhm_gemnoc_cfg = { + .name = "qhm_gemnoc_cfg", + .id = QCS615_MASTER_GEM_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 2, + .links = { QCS615_SLAVE_MSS_PROC_MS_MPU_CFG, QCS615_SLAVE_SERVICE_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = QCS615_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { QCS615_SLAVE_GEM_NOC_SNOC, QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = QCS615_MASTER_MNOC_HF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = QCS615_MASTER_MNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 2, + .links = { QCS615_SLAVE_GEM_NOC_SNOC, QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_gc = { + .name = "qnm_snoc_gc", + .id = QCS615_MASTER_SNOC_GC_MEM_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = QCS615_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS615_SLAVE_LLCC }, +}; + +static struct qcom_icc_node ipa_core_master = { + .name = "ipa_core_master", + .id = QCS615_MASTER_IPA_CORE, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_IPA_CORE }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = QCS615_MASTER_LLCC, + .channels = 2, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qhm_mnoc_cfg = { + .name = "qhm_mnoc_cfg", + .id = QCS615_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_SERVICE_MNOC }, +}; + +static struct qcom_icc_node qxm_camnoc_hf0 = { + .name = "qxm_camnoc_hf0", + .id = QCS615_MASTER_CAMNOC_HF0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_camnoc_hf1 = { + .name = "qxm_camnoc_hf1", + .id = QCS615_MASTER_CAMNOC_HF1, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_camnoc_sf = { + .name = "qxm_camnoc_sf", + .id = QCS615_MASTER_CAMNOC_SF, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_mdp0 = { + .name = "qxm_mdp0", + .id = QCS615_MASTER_MDP0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_rot = { + .name = "qxm_rot", + .id = QCS615_MASTER_ROTATOR, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_venus0 = { + .name = "qxm_venus0", + .id = QCS615_MASTER_VIDEO_P0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_venus_arm9 = { + .name = "qxm_venus_arm9", + .id = QCS615_MASTER_VIDEO_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qhm_snoc_cfg = { + .name = "qhm_snoc_cfg", + .id = QCS615_MASTER_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_SLAVE_SERVICE_SNOC }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = QCS615_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 8, + .links = { QCS615_SLAVE_APPSS, QCS615_SLAVE_SNOC_CNOC, + QCS615_SLAVE_SNOC_GEM_NOC_SF, QCS615_SLAVE_IMEM, + QCS615_SLAVE_PIMEM, QCS615_SLAVE_PCIE_0, + QCS615_SLAVE_QDSS_STM, QCS615_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc = { + .name = "qnm_gemnoc", + .id = QCS615_MASTER_GEM_NOC_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 6, + .links = { QCS615_SLAVE_APPSS, QCS615_SLAVE_SNOC_CNOC, + QCS615_SLAVE_IMEM, QCS615_SLAVE_PIMEM, + QCS615_SLAVE_QDSS_STM, QCS615_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = QCS615_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_SLAVE_PCIE_0 }, +}; + +static struct qcom_icc_node qnm_lpass_anoc = { + .name = "qnm_lpass_anoc", + .id = QCS615_MASTER_LPASS_ANOC, + .channels = 1, + .buswidth = 8, + .num_links = 7, + .links = { QCS615_SLAVE_APPSS, QCS615_SLAVE_SNOC_CNOC, + QCS615_SLAVE_SNOC_GEM_NOC_SF, QCS615_SLAVE_IMEM, + QCS615_SLAVE_PIMEM, QCS615_SLAVE_PCIE_0, + QCS615_SLAVE_QDSS_STM }, +}; + +static struct qcom_icc_node qnm_pcie_anoc = { + .name = "qnm_pcie_anoc", + .id = QCS615_MASTER_ANOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 5, + .links = { QCS615_SLAVE_APPSS, QCS615_SLAVE_SNOC_CNOC, + QCS615_SLAVE_SNOC_GEM_NOC_SF, QCS615_SLAVE_IMEM, + QCS615_SLAVE_QDSS_STM }, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .id = QCS615_MASTER_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS615_SLAVE_SNOC_MEM_NOC_GC, QCS615_SLAVE_IMEM }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = QCS615_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS615_SLAVE_SNOC_MEM_NOC_GC, QCS615_SLAVE_IMEM }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = QCS615_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS615_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qns_lpass_snoc = { + .name = "qns_lpass_snoc", + .id = QCS615_SLAVE_LPASS_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_LPASS_ANOC }, +}; + +static struct qcom_icc_node qns_pcie_snoc = { + .name = "qns_pcie_snoc", + .id = QCS615_SLAVE_ANOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_ANOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node srvc_aggre2_noc = { + .name = "srvc_aggre2_noc", + .id = QCS615_SLAVE_SERVICE_A2NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_camnoc_uncomp = { + .name = "qns_camnoc_uncomp", + .id = QCS615_SLAVE_CAMNOC_UNCOMP, + .channels = 1, + .buswidth = 32, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_a1_noc_cfg = { + .name = "qhs_a1_noc_cfg", + .id = QCS615_SLAVE_A1NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_MASTER_A1NOC_CFG }, +}; + +static struct qcom_icc_node qhs_ahb2phy_east = { + .name = "qhs_ahb2phy_east", + .id = QCS615_SLAVE_AHB2PHY_EAST, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy_west = { + .name = "qhs_ahb2phy_west", + .id = QCS615_SLAVE_AHB2PHY_WEST, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aop = { + .name = "qhs_aop", + .id = QCS615_SLAVE_AOP, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = QCS615_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = QCS615_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = QCS615_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = QCS615_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mx = { + .name = "qhs_cpr_mx", + .id = QCS615_SLAVE_RBCPR_MX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = QCS615_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ddrss_cfg = { + .name = "qhs_ddrss_cfg", + .id = QCS615_SLAVE_CNOC_DDRSS, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_MASTER_CNOC_DC_NOC }, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = QCS615_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_emac_avb_cfg = { + .name = "qhs_emac_avb_cfg", + .id = QCS615_SLAVE_EMAC_AVB_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_glm = { + .name = "qhs_glm", + .id = QCS615_SLAVE_GLM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = QCS615_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = QCS615_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .id = QCS615_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mnoc_cfg = { + .name = "qhs_mnoc_cfg", + .id = QCS615_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qhs_pcie_config = { + .name = "qhs_pcie_config", + .id = QCS615_SLAVE_PCIE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .name = "qhs_pimem_cfg", + .id = QCS615_SLAVE_PIMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .id = QCS615_SLAVE_PRNG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = QCS615_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = QCS615_SLAVE_QSPI, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = QCS615_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = QCS615_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .id = QCS615_SLAVE_SDCC_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = QCS615_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_snoc_cfg = { + .name = "qhs_snoc_cfg", + .id = QCS615_SLAVE_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_MASTER_SNOC_CFG }, +}; + +static struct qcom_icc_node qhs_spdm = { + .name = "qhs_spdm", + .id = QCS615_SLAVE_SPDM_WRAPPER, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = QCS615_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm_east = { + .name = "qhs_tlmm_east", + .id = QCS615_SLAVE_TLMM_EAST, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm_south = { + .name = "qhs_tlmm_south", + .id = QCS615_SLAVE_TLMM_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm_west = { + .name = "qhs_tlmm_west", + .id = QCS615_SLAVE_TLMM_WEST, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = QCS615_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb2 = { + .name = "qhs_usb2", + .id = QCS615_SLAVE_USB2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3 = { + .name = "qhs_usb3", + .id = QCS615_SLAVE_USB3, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = QCS615_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .id = QCS615_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_cnoc_a2noc = { + .name = "qns_cnoc_a2noc", + .id = QCS615_SLAVE_CNOC_A2NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_CNOC_A2NOC }, +}; + +static struct qcom_icc_node srvc_cnoc = { + .name = "srvc_cnoc", + .id = QCS615_SLAVE_SERVICE_CNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_dc_noc_gemnoc = { + .name = "qhs_dc_noc_gemnoc", + .id = QCS615_SLAVE_DC_NOC_GEMNOC, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS615_MASTER_GEM_NOC_CFG }, +}; + +static struct qcom_icc_node qhs_llcc = { + .name = "qhs_llcc", + .id = QCS615_SLAVE_LLCC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mdsp_ms_mpu_cfg = { + .name = "qhs_mdsp_ms_mpu_cfg", + .id = QCS615_SLAVE_MSS_PROC_MS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_snoc = { + .name = "qns_gem_noc_snoc", + .id = QCS615_SLAVE_GEM_NOC_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_GEM_NOC_SNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = QCS615_SLAVE_LLCC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS615_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_sys_pcie = { + .name = "qns_sys_pcie", + .id = QCS615_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node srvc_gemnoc = { + .name = "srvc_gemnoc", + .id = QCS615_SLAVE_SERVICE_GEM_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node ipa_core_slave = { + .name = "ipa_core_slave", + .id = QCS615_SLAVE_IPA_CORE, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = QCS615_SLAVE_EBI1, + .channels = 2, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns2_mem_noc = { + .name = "qns2_mem_noc", + .id = QCS615_SLAVE_MNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = QCS615_SLAVE_MNOC_HF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS615_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = QCS615_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_apss = { + .name = "qhs_apss", + .id = QCS615_SLAVE_APPSS, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qns_cnoc = { + .name = "qns_cnoc", + .id = QCS615_SLAVE_SNOC_CNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_SNOC_CNOC }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = QCS615_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS615_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_memnoc_gc = { + .name = "qns_memnoc_gc", + .id = QCS615_SLAVE_SNOC_MEM_NOC_GC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS615_MASTER_SNOC_GC_MEM_NOC }, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = QCS615_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .id = QCS615_SLAVE_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_snoc = { + .name = "srvc_snoc", + .id = QCS615_SLAVE_SERVICE_SNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie = { + .name = "xs_pcie", + .id = QCS615_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = QCS615_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = QCS615_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .keepalive = true, + .num_nodes = 37, + .nodes = { &qhm_spdm, &qnm_snoc, + &qhs_a1_noc_cfg, &qhs_aop, + &qhs_aoss, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_cpr_cx, + &qhs_cpr_mx, &qhs_crypto0_cfg, + &qhs_ddrss_cfg, &qhs_display_cfg, + &qhs_emac_avb_cfg, &qhs_glm, + &qhs_gpuss_cfg, &qhs_imem_cfg, + &qhs_ipa, &qhs_mnoc_cfg, + &qhs_pcie_config, &qhs_pimem_cfg, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qup0, &qhs_qup1, + &qhs_snoc_cfg, &qhs_spdm, + &qhs_tcsr, &qhs_tlmm_east, + &qhs_tlmm_south, &qhs_tlmm_west, + &qhs_ufs_mem_cfg, &qhs_usb2, + &qhs_usb3, &qhs_venus_cfg, + &qhs_vsense_ctrl_cfg, &qns_cnoc_a2noc, + &srvc_cnoc }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 8, + .nodes = { &qhm_qspi, &xm_sdc1, + &xm_sdc2, &qhs_ahb2phy_east, + &qhs_ahb2phy_west, &qhs_qspi, + &qhs_sdc1, &qhs_sdc2 }, +}; + +static struct qcom_icc_bcm bcm_ip0 = { + .name = "IP0", + .num_nodes = 1, + .nodes = { &ipa_core_slave }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .num_nodes = 7, + .nodes = { &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, + &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, + &qxm_camnoc_hf1, &qxm_mdp0, + &qxm_rot }, +}; + +static struct qcom_icc_bcm bcm_mm2 = { + .name = "MM2", + .num_nodes = 2, + .nodes = { &qxm_camnoc_sf, &qns2_mem_noc }, +}; + +static struct qcom_icc_bcm bcm_mm3 = { + .name = "MM3", + .num_nodes = 2, + .nodes = { &qxm_venus0, &qxm_venus_arm9 }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 2, + .nodes = { &qhm_qup0, &qhm_qup1 }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh2 = { + .name = "SH2", + .num_nodes = 1, + .nodes = { &acm_apps }, +}; + +static struct qcom_icc_bcm bcm_sh3 = { + .name = "SH3", + .num_nodes = 1, + .nodes = { &qns_gem_noc_snoc }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn1 = { + .name = "SN1", + .num_nodes = 1, + .nodes = { &qxs_imem }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qns_memnoc_gc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 2, + .nodes = { &srvc_aggre2_noc, &qns_cnoc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qxs_pimem }, +}; + +static struct qcom_icc_bcm bcm_sn5 = { + .name = "SN5", + .num_nodes = 1, + .nodes = { &xs_qdss_stm }, +}; + +static struct qcom_icc_bcm bcm_sn8 = { + .name = "SN8", + .num_nodes = 2, + .nodes = { &qnm_gemnoc_pcie, &xs_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn9 = { + .name = "SN9", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn12 = { + .name = "SN12", + .num_nodes = 2, + .nodes = { &qxm_pimem, &xm_gic }, +}; + +static struct qcom_icc_bcm bcm_sn13 = { + .name = "SN13", + .num_nodes = 1, + .nodes = { &qnm_lpass_anoc }, +}; + +static struct qcom_icc_bcm bcm_sn14 = { + .name = "SN14", + .num_nodes = 1, + .nodes = { &qns_pcie_snoc }, +}; + +static struct qcom_icc_bcm bcm_sn15 = { + .name = "SN15", + .num_nodes = 1, + .nodes = { &qnm_gemnoc }, +}; + +static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { + &bcm_ce0, + &bcm_cn1, + &bcm_qup0, + &bcm_sn3, + &bcm_sn14, + &bcm_ip0, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_A1NOC_CFG] = &qhm_a1noc_cfg, + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QSPI] = &qhm_qspi, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_BLSP_1] = &qhm_qup1, + [MASTER_CNOC_A2NOC] = &qnm_cnoc, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_EMAC_EVB] = &xm_emac_avb, + [MASTER_PCIE] = &xm_pcie, + [MASTER_QDSS_ETR] = &xm_qdss_etr, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB2] = &xm_usb2, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, + [SLAVE_LPASS_SNOC] = &qns_lpass_snoc, + [SLAVE_ANOC_PCIE_SNOC] = &qns_pcie_snoc, + [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, +}; + +static const struct qcom_icc_desc qcs615_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), +}; + +static struct qcom_icc_bcm * const camnoc_virt_bcms[] = { + &bcm_mm1, +}; + +static struct qcom_icc_node * const camnoc_virt_nodes[] = { + [MASTER_CAMNOC_HF0_UNCOMP] = &qxm_camnoc_hf0_uncomp, + [MASTER_CAMNOC_HF1_UNCOMP] = &qxm_camnoc_hf1_uncomp, + [MASTER_CAMNOC_SF_UNCOMP] = &qxm_camnoc_sf_uncomp, + [SLAVE_CAMNOC_UNCOMP] = &qns_camnoc_uncomp, +}; + +static const struct qcom_icc_desc qcs615_camnoc_virt = { + .nodes = camnoc_virt_nodes, + .num_nodes = ARRAY_SIZE(camnoc_virt_nodes), + .bcms = camnoc_virt_bcms, + .num_bcms = ARRAY_SIZE(camnoc_virt_bcms), +}; + +static struct qcom_icc_bcm * const config_noc_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const config_noc_nodes[] = { + [MASTER_SPDM] = &qhm_spdm, + [MASTER_SNOC_CNOC] = &qnm_snoc, + [MASTER_QDSS_DAP] = &xm_qdss_dap, + [SLAVE_A1NOC_CFG] = &qhs_a1_noc_cfg, + [SLAVE_AHB2PHY_EAST] = &qhs_ahb2phy_east, + [SLAVE_AHB2PHY_WEST] = &qhs_ahb2phy_west, + [SLAVE_AOP] = &qhs_aop, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MX_CFG] = &qhs_cpr_mx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CNOC_DDRSS] = &qhs_ddrss_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_EMAC_AVB_CFG] = &qhs_emac_avb_cfg, + [SLAVE_GLM] = &qhs_glm, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_CNOC_MNOC_CFG] = &qhs_mnoc_cfg, + [SLAVE_PCIE_CFG] = &qhs_pcie_config, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_SDCC_1] = &qhs_sdc1, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SNOC_CFG] = &qhs_snoc_cfg, + [SLAVE_SPDM_WRAPPER] = &qhs_spdm, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM_EAST] = &qhs_tlmm_east, + [SLAVE_TLMM_SOUTH] = &qhs_tlmm_south, + [SLAVE_TLMM_WEST] = &qhs_tlmm_west, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB2] = &qhs_usb2, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_CNOC_A2NOC] = &qns_cnoc_a2noc, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc, +}; + +static const struct qcom_icc_desc qcs615_config_noc = { + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .bcms = config_noc_bcms, + .num_bcms = ARRAY_SIZE(config_noc_bcms), +}; + +static struct qcom_icc_node * const dc_noc_nodes[] = { + [MASTER_CNOC_DC_NOC] = &qhm_cnoc, + [SLAVE_DC_NOC_GEMNOC] = &qhs_dc_noc_gemnoc, + [SLAVE_LLCC_CFG] = &qhs_llcc, +}; + +static const struct qcom_icc_desc qcs615_dc_noc = { + .nodes = dc_noc_nodes, + .num_nodes = ARRAY_SIZE(dc_noc_nodes), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh2, + &bcm_sh3, + &bcm_mm1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_APPSS_PROC] = &acm_apps, + [MASTER_GPU_TCU] = &acm_gpu_tcu, + [MASTER_SYS_TCU] = &acm_sys_tcu, + [MASTER_GEM_NOC_CFG] = &qhm_gemnoc_cfg, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [SLAVE_MSS_PROC_MS_MPU_CFG] = &qhs_mdsp_ms_mpu_cfg, + [SLAVE_GEM_NOC_SNOC] = &qns_gem_noc_snoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_sys_pcie, + [SLAVE_SERVICE_GEM_NOC] = &srvc_gemnoc, +}; + +static const struct qcom_icc_desc qcs615_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm * const ipa_virt_bcms[] = { + &bcm_ip0, +}; + +static struct qcom_icc_node * const ipa_virt_nodes[] = { + [MASTER_IPA_CORE] = &ipa_core_master, + [SLAVE_IPA_CORE] = &ipa_core_slave, +}; + +static const struct qcom_icc_desc qcs615_ipa_virt = { + .nodes = ipa_virt_nodes, + .num_nodes = ARRAY_SIZE(ipa_virt_nodes), + .bcms = ipa_virt_bcms, + .num_bcms = ARRAY_SIZE(ipa_virt_bcms), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc qcs615_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, + &bcm_mm2, + &bcm_mm3, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CNOC_MNOC_CFG] = &qhm_mnoc_cfg, + [MASTER_CAMNOC_HF0] = &qxm_camnoc_hf0, + [MASTER_CAMNOC_HF1] = &qxm_camnoc_hf1, + [MASTER_CAMNOC_SF] = &qxm_camnoc_sf, + [MASTER_MDP0] = &qxm_mdp0, + [MASTER_ROTATOR] = &qxm_rot, + [MASTER_VIDEO_P0] = &qxm_venus0, + [MASTER_VIDEO_PROC] = &qxm_venus_arm9, + [SLAVE_MNOC_SF_MEM_NOC] = &qns2_mem_noc, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_SERVICE_MNOC] = &srvc_mnoc, +}; + +static const struct qcom_icc_desc qcs615_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn1, + &bcm_sn2, + &bcm_sn3, + &bcm_sn4, + &bcm_sn5, + &bcm_sn8, + &bcm_sn9, + &bcm_sn12, + &bcm_sn13, + &bcm_sn15, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_SNOC_CFG] = &qhm_snoc_cfg, + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_GEM_NOC_SNOC] = &qnm_gemnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [MASTER_LPASS_ANOC] = &qnm_lpass_anoc, + [MASTER_ANOC_PCIE_SNOC] = &qnm_pcie_anoc, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [SLAVE_APPSS] = &qhs_apss, + [SLAVE_SNOC_CNOC] = &qns_cnoc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, + [SLAVE_SNOC_MEM_NOC_GC] = &qns_memnoc_gc, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, + [SLAVE_PCIE_0] = &xs_pcie, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc qcs615_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,qcs615-aggre1-noc", + .data = &qcs615_aggre1_noc}, + { .compatible = "qcom,qcs615-camnoc-virt", + .data = &qcs615_camnoc_virt}, + { .compatible = "qcom,qcs615-config-noc", + .data = &qcs615_config_noc}, + { .compatible = "qcom,qcs615-dc-noc", + .data = &qcs615_dc_noc}, + { .compatible = "qcom,qcs615-gem-noc", + .data = &qcs615_gem_noc}, + { .compatible = "qcom,qcs615-ipa-virt", + .data = &qcs615_ipa_virt}, + { .compatible = "qcom,qcs615-mc-virt", + .data = &qcs615_mc_virt}, + { .compatible = "qcom,qcs615-mmss-noc", + .data = &qcs615_mmss_noc}, + { .compatible = "qcom,qcs615-system-noc", + .data = &qcs615_system_noc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-qcs615", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("qcs615 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/qcs615.h b/drivers/interconnect/qcom/qcs615.h new file mode 100644 index 0000000000000..66e66c7e23d4e --- /dev/null +++ b/drivers/interconnect/qcom/qcs615.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_QCS615_H +#define __DRIVERS_INTERCONNECT_QCOM_QCS615_H + +#define QCS615_MASTER_A1NOC_CFG 1 +#define QCS615_MASTER_A1NOC_SNOC 2 +#define QCS615_MASTER_ANOC_PCIE_SNOC 3 +#define QCS615_MASTER_APPSS_PROC 4 +#define QCS615_MASTER_BLSP_1 5 +#define QCS615_MASTER_CAMNOC_HF0 6 +#define QCS615_MASTER_CAMNOC_HF0_UNCOMP 7 +#define QCS615_MASTER_CAMNOC_HF1 8 +#define QCS615_MASTER_CAMNOC_HF1_UNCOMP 9 +#define QCS615_MASTER_CAMNOC_SF 10 +#define QCS615_MASTER_CAMNOC_SF_UNCOMP 11 +#define QCS615_MASTER_CNOC_A2NOC 12 +#define QCS615_MASTER_CNOC_DC_NOC 13 +#define QCS615_MASTER_CNOC_MNOC_CFG 14 +#define QCS615_MASTER_CRYPTO 15 +#define QCS615_MASTER_EMAC_EVB 16 +#define QCS615_MASTER_GEM_NOC_CFG 17 +#define QCS615_MASTER_GEM_NOC_PCIE_SNOC 18 +#define QCS615_MASTER_GEM_NOC_SNOC 19 +#define QCS615_MASTER_GFX3D 20 +#define QCS615_MASTER_GIC 21 +#define QCS615_MASTER_GPU_TCU 22 +#define QCS615_MASTER_IPA 23 +#define QCS615_MASTER_IPA_CORE 24 +#define QCS615_MASTER_LLCC 25 +#define QCS615_MASTER_LPASS_ANOC 26 +#define QCS615_MASTER_MDP0 27 +#define QCS615_MASTER_MNOC_HF_MEM_NOC 28 +#define QCS615_MASTER_MNOC_SF_MEM_NOC 29 +#define QCS615_MASTER_PCIE 30 +#define QCS615_MASTER_PIMEM 31 +#define QCS615_MASTER_QDSS_BAM 32 +#define QCS615_MASTER_QDSS_DAP 33 +#define QCS615_MASTER_QDSS_ETR 34 +#define QCS615_MASTER_QSPI 35 +#define QCS615_MASTER_QUP_0 36 +#define QCS615_MASTER_ROTATOR 37 +#define QCS615_MASTER_SDCC_1 38 +#define QCS615_MASTER_SDCC_2 39 +#define QCS615_MASTER_SNOC_CFG 40 +#define QCS615_MASTER_SNOC_CNOC 41 +#define QCS615_MASTER_SNOC_GC_MEM_NOC 42 +#define QCS615_MASTER_SNOC_SF_MEM_NOC 43 +#define QCS615_MASTER_SPDM 44 +#define QCS615_MASTER_SYS_TCU 45 +#define QCS615_MASTER_UFS_MEM 46 +#define QCS615_MASTER_USB2 47 +#define QCS615_MASTER_USB3_0 48 +#define QCS615_MASTER_VIDEO_P0 49 +#define QCS615_MASTER_VIDEO_PROC 50 +#define QCS615_SLAVE_A1NOC_CFG 51 +#define QCS615_SLAVE_A1NOC_SNOC 52 +#define QCS615_SLAVE_AHB2PHY_EAST 53 +#define QCS615_SLAVE_AHB2PHY_WEST 54 +#define QCS615_SLAVE_ANOC_PCIE_SNOC 55 +#define QCS615_SLAVE_AOP 56 +#define QCS615_SLAVE_AOSS 57 +#define QCS615_SLAVE_APPSS 58 +#define QCS615_SLAVE_CAMERA_CFG 59 +#define QCS615_SLAVE_CAMNOC_UNCOMP 60 +#define QCS615_SLAVE_CLK_CTL 61 +#define QCS615_SLAVE_CNOC_A2NOC 62 +#define QCS615_SLAVE_CNOC_DDRSS 63 +#define QCS615_SLAVE_CNOC_MNOC_CFG 64 +#define QCS615_SLAVE_CRYPTO_0_CFG 65 +#define QCS615_SLAVE_DC_NOC_GEMNOC 66 +#define QCS615_SLAVE_DISPLAY_CFG 67 +#define QCS615_SLAVE_EBI1 68 +#define QCS615_SLAVE_EMAC_AVB_CFG 69 +#define QCS615_SLAVE_GEM_NOC_SNOC 70 +#define QCS615_SLAVE_GFX3D_CFG 71 +#define QCS615_SLAVE_GLM 72 +#define QCS615_SLAVE_IMEM 73 +#define QCS615_SLAVE_IMEM_CFG 74 +#define QCS615_SLAVE_IPA_CFG 75 +#define QCS615_SLAVE_IPA_CORE 76 +#define QCS615_SLAVE_LLCC 77 +#define QCS615_SLAVE_LLCC_CFG 78 +#define QCS615_SLAVE_LPASS_SNOC 79 +#define QCS615_SLAVE_MEM_NOC_PCIE_SNOC 80 +#define QCS615_SLAVE_MNOC_HF_MEM_NOC 81 +#define QCS615_SLAVE_MNOC_SF_MEM_NOC 82 +#define QCS615_SLAVE_MSS_PROC_MS_MPU_CFG 83 +#define QCS615_SLAVE_PCIE_0 84 +#define QCS615_SLAVE_PCIE_CFG 85 +#define QCS615_SLAVE_PIMEM 86 +#define QCS615_SLAVE_PIMEM_CFG 87 +#define QCS615_SLAVE_PRNG 88 +#define QCS615_SLAVE_QDSS_CFG 89 +#define QCS615_SLAVE_QDSS_STM 90 +#define QCS615_SLAVE_QSPI 91 +#define QCS615_SLAVE_QUP_0 92 +#define QCS615_SLAVE_QUP_1 93 +#define QCS615_SLAVE_RBCPR_CX_CFG 94 +#define QCS615_SLAVE_RBCPR_MX_CFG 95 +#define QCS615_SLAVE_SDCC_1 96 +#define QCS615_SLAVE_SDCC_2 97 +#define QCS615_SLAVE_SERVICE_A2NOC 98 +#define QCS615_SLAVE_SERVICE_CNOC 99 +#define QCS615_SLAVE_SERVICE_GEM_NOC 100 +#define QCS615_SLAVE_SERVICE_MNOC 101 +#define QCS615_SLAVE_SERVICE_SNOC 102 +#define QCS615_SLAVE_SNOC_CFG 103 +#define QCS615_SLAVE_SNOC_CNOC 104 +#define QCS615_SLAVE_SNOC_GEM_NOC_SF 105 +#define QCS615_SLAVE_SNOC_MEM_NOC_GC 106 +#define QCS615_SLAVE_SPDM_WRAPPER 107 +#define QCS615_SLAVE_TCSR 108 +#define QCS615_SLAVE_TCU 109 +#define QCS615_SLAVE_TLMM_EAST 110 +#define QCS615_SLAVE_TLMM_SOUTH 111 +#define QCS615_SLAVE_TLMM_WEST 112 +#define QCS615_SLAVE_UFS_MEM_CFG 113 +#define QCS615_SLAVE_USB2 114 +#define QCS615_SLAVE_USB3 115 +#define QCS615_SLAVE_VENUS_CFG 116 +#define QCS615_SLAVE_VSENSE_CTRL_CFG 117 + +#endif + diff --git a/drivers/interconnect/qcom/qcs8300.c b/drivers/interconnect/qcom/qcs8300.c new file mode 100644 index 0000000000000..e7a1b2fc69bab --- /dev/null +++ b/drivers/interconnect/qcom/qcs8300.c @@ -0,0 +1,2088 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" +#include "qcs8300.h" + +static struct qcom_icc_node qxm_qup3 = { + .name = "qxm_qup3", + .id = QCS8300_MASTER_QUP_3, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_emac_0 = { + .name = "xm_emac_0", + .id = QCS8300_MASTER_EMAC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .id = QCS8300_MASTER_SDC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = QCS8300_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb2_2 = { + .name = "xm_usb2_2", + .id = QCS8300_MASTER_USB2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = QCS8300_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = QCS8300_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = QCS8300_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = QCS8300_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_cnoc_datapath = { + .name = "qnm_cnoc_datapath", + .id = QCS8300_MASTER_CNOC_A2NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto_0 = { + .name = "qxm_crypto_0", + .id = QCS8300_MASTER_CRYPTO_CORE0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto_1 = { + .name = "qxm_crypto_1", + .id = QCS8300_MASTER_CRYPTO_CORE1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = QCS8300_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .id = QCS8300_MASTER_QDSS_ETR_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .id = QCS8300_MASTER_QDSS_ETR_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = QCS8300_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = QCS8300_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qup3_core_master = { + .name = "qup3_core_master", + .id = QCS8300_MASTER_QUP_CORE_3, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_QUP_CORE_3 }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = QCS8300_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 71, + .links = { QCS8300_SLAVE_AHB2PHY_2, QCS8300_SLAVE_AHB2PHY_3, + QCS8300_SLAVE_ANOC_THROTTLE_CFG, QCS8300_SLAVE_AOSS, + QCS8300_SLAVE_APPSS, QCS8300_SLAVE_BOOT_ROM, + QCS8300_SLAVE_CAMERA_CFG, QCS8300_SLAVE_CAMERA_NRT_THROTTLE_CFG, + QCS8300_SLAVE_CAMERA_RT_THROTTLE_CFG, QCS8300_SLAVE_CLK_CTL, + QCS8300_SLAVE_CDSP_CFG, QCS8300_SLAVE_RBCPR_CX_CFG, + QCS8300_SLAVE_RBCPR_MMCX_CFG, QCS8300_SLAVE_RBCPR_MX_CFG, + QCS8300_SLAVE_CPR_NSPCX, QCS8300_SLAVE_CPR_NSPHMX, + QCS8300_SLAVE_CRYPTO_0_CFG, QCS8300_SLAVE_CX_RDPM, + QCS8300_SLAVE_DISPLAY_CFG, QCS8300_SLAVE_DISPLAY_RT_THROTTLE_CFG, + QCS8300_SLAVE_EMAC_CFG, QCS8300_SLAVE_GP_DSP0_CFG, + QCS8300_SLAVE_GPDSP0_THROTTLE_CFG, QCS8300_SLAVE_GPU_TCU_THROTTLE_CFG, + QCS8300_SLAVE_GFX3D_CFG, QCS8300_SLAVE_HWKM, + QCS8300_SLAVE_IMEM_CFG, QCS8300_SLAVE_IPA_CFG, + QCS8300_SLAVE_IPC_ROUTER_CFG, QCS8300_SLAVE_LPASS, + QCS8300_SLAVE_LPASS_THROTTLE_CFG, QCS8300_SLAVE_MX_RDPM, + QCS8300_SLAVE_MXC_RDPM, QCS8300_SLAVE_PCIE_0_CFG, + QCS8300_SLAVE_PCIE_1_CFG, QCS8300_SLAVE_PCIE_TCU_THROTTLE_CFG, + QCS8300_SLAVE_PCIE_THROTTLE_CFG, QCS8300_SLAVE_PDM, + QCS8300_SLAVE_PIMEM_CFG, QCS8300_SLAVE_PKA_WRAPPER_CFG, + QCS8300_SLAVE_QDSS_CFG, QCS8300_SLAVE_QM_CFG, + QCS8300_SLAVE_QM_MPU_CFG, QCS8300_SLAVE_QUP_0, + QCS8300_SLAVE_QUP_1, QCS8300_SLAVE_QUP_3, + QCS8300_SLAVE_SAIL_THROTTLE_CFG, QCS8300_SLAVE_SDC1, + QCS8300_SLAVE_SECURITY, QCS8300_SLAVE_SNOC_THROTTLE_CFG, + QCS8300_SLAVE_TCSR, QCS8300_SLAVE_TLMM, + QCS8300_SLAVE_TSC_CFG, QCS8300_SLAVE_UFS_MEM_CFG, + QCS8300_SLAVE_USB2, QCS8300_SLAVE_USB3_0, + QCS8300_SLAVE_VENUS_CFG, QCS8300_SLAVE_VENUS_CVP_THROTTLE_CFG, + QCS8300_SLAVE_VENUS_V_CPU_THROTTLE_CFG, + QCS8300_SLAVE_VENUS_VCODEC_THROTTLE_CFG, + QCS8300_SLAVE_DDRSS_CFG, QCS8300_SLAVE_GPDSP_NOC_CFG, + QCS8300_SLAVE_CNOC_MNOC_HF_CFG, QCS8300_SLAVE_CNOC_MNOC_SF_CFG, + QCS8300_SLAVE_PCIE_ANOC_CFG, QCS8300_SLAVE_SNOC_CFG, + QCS8300_SLAVE_BOOT_IMEM, QCS8300_SLAVE_IMEM, + QCS8300_SLAVE_PIMEM, QCS8300_SLAVE_QDSS_STM, + QCS8300_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = QCS8300_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 2, + .links = { QCS8300_SLAVE_PCIE_0, QCS8300_SLAVE_PCIE_1 }, +}; + +static struct qcom_icc_node qnm_cnoc_dc_noc = { + .name = "qnm_cnoc_dc_noc", + .id = QCS8300_MASTER_CNOC_DC_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 2, + .links = { QCS8300_SLAVE_LLCC_CFG, QCS8300_SLAVE_GEM_NOC_CFG }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = QCS8300_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_pcie_tcu = { + .name = "alm_pcie_tcu", + .id = QCS8300_MASTER_PCIE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = QCS8300_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = QCS8300_MASTER_APPSS_PROC, + .channels = 4, + .buswidth = 32, + .num_links = 3, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC, + QCS8300_SLAVE_GEM_NOC_PCIE_CNOC }, +}; + +static struct qcom_icc_node qnm_cmpnoc0 = { + .name = "qnm_cmpnoc0", + .id = QCS8300_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_gemnoc_cfg = { + .name = "qnm_gemnoc_cfg", + .id = QCS8300_MASTER_GEM_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 4, + .links = { QCS8300_SLAVE_SERVICE_GEM_NOC_1, QCS8300_SLAVE_SERVICE_GEM_NOC_2, + QCS8300_SLAVE_SERVICE_GEM_NOC, QCS8300_SLAVE_SERVICE_GEM_NOC2 }, +}; + +static struct qcom_icc_node qnm_gpdsp_sail = { + .name = "qnm_gpdsp_sail", + .id = QCS8300_MASTER_GPDSP_SAIL, + .channels = 1, + .buswidth = 16, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = QCS8300_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = QCS8300_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { QCS8300_SLAVE_LLCC, QCS8300_SLAVE_GEM_NOC_PCIE_CNOC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = QCS8300_MASTER_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 3, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC, + QCS8300_SLAVE_GEM_NOC_PCIE_CNOC }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = QCS8300_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 2, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_gc = { + .name = "qnm_snoc_gc", + .id = QCS8300_MASTER_SNOC_GC_MEM_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = QCS8300_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { QCS8300_SLAVE_GEM_NOC_CNOC, QCS8300_SLAVE_LLCC, + QCS8300_SLAVE_GEM_NOC_PCIE_CNOC }, +}; + +static struct qcom_icc_node qnm_sailss_md0 = { + .name = "qnm_sailss_md0", + .id = QCS8300_MASTER_SAILSS_MD0, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_SLAVE_GP_DSP_SAIL_NOC }, +}; + +static struct qcom_icc_node qxm_dsp0 = { + .name = "qxm_dsp0", + .id = QCS8300_MASTER_DSP0, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_SLAVE_GP_DSP_SAIL_NOC }, +}; + +static struct qcom_icc_node qhm_config_noc = { + .name = "qhm_config_noc", + .id = QCS8300_MASTER_CNOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 6, + .links = { QCS8300_SLAVE_LPASS_CORE_CFG, QCS8300_SLAVE_LPASS_LPI_CFG, + QCS8300_SLAVE_LPASS_MPU_CFG, QCS8300_SLAVE_LPASS_TOP_CFG, + QCS8300_SLAVE_SERVICES_LPASS_AML_NOC, QCS8300_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qxm_lpass_dsp = { + .name = "qxm_lpass_dsp", + .id = QCS8300_MASTER_LPASS_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 4, + .links = { QCS8300_SLAVE_LPASS_TOP_CFG, QCS8300_SLAVE_LPASS_SNOC, + QCS8300_SLAVE_SERVICES_LPASS_AML_NOC, QCS8300_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = QCS8300_MASTER_LLCC, + .channels = 8, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .id = QCS8300_MASTER_CAMNOC_HF, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_icp = { + .name = "qnm_camnoc_icp", + .id = QCS8300_MASTER_CAMNOC_ICP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .id = QCS8300_MASTER_CAMNOC_SF, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mdp0_0 = { + .name = "qnm_mdp0_0", + .id = QCS8300_MASTER_MDP0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mdp0_1 = { + .name = "qnm_mdp0_1", + .id = QCS8300_MASTER_MDP1, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf_cfg = { + .name = "qnm_mnoc_hf_cfg", + .id = QCS8300_MASTER_CNOC_MNOC_HF_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_SERVICE_MNOC_HF }, +}; + +static struct qcom_icc_node qnm_mnoc_sf_cfg = { + .name = "qnm_mnoc_sf_cfg", + .id = QCS8300_MASTER_CNOC_MNOC_SF_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_SERVICE_MNOC_SF }, +}; + +static struct qcom_icc_node qnm_video0 = { + .name = "qnm_video0", + .id = QCS8300_MASTER_VIDEO_P0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cvp = { + .name = "qnm_video_cvp", + .id = QCS8300_MASTER_VIDEO_PROC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .id = QCS8300_MASTER_VIDEO_V_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qhm_nsp_noc_config = { + .name = "qhm_nsp_noc_config", + .id = QCS8300_MASTER_CDSP_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_SERVICE_NSP_NOC }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .id = QCS8300_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { QCS8300_SLAVE_HCP_A, QCS8300_SLAVE_CDSP_MEM_NOC }, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .id = QCS8300_MASTER_PCIE_0, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .id = QCS8300_MASTER_PCIE_1, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qhm_gic = { + .name = "qhm_gic", + .id = QCS8300_MASTER_GIC_AHB, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = QCS8300_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = QCS8300_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_lpass_noc = { + .name = "qnm_lpass_noc", + .id = QCS8300_MASTER_LPASS_ANOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_snoc_cfg = { + .name = "qnm_snoc_cfg", + .id = QCS8300_MASTER_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_SLAVE_SERVICE_SNOC }, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .id = QCS8300_MASTER_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = QCS8300_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = QCS8300_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = QCS8300_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = QCS8300_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = QCS8300_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup3_core_slave = { + .name = "qup3_core_slave", + .id = QCS8300_SLAVE_QUP_CORE_3, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy2 = { + .name = "qhs_ahb2phy2", + .id = QCS8300_SLAVE_AHB2PHY_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy3 = { + .name = "qhs_ahb2phy3", + .id = QCS8300_SLAVE_AHB2PHY_3, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_anoc_throttle_cfg = { + .name = "qhs_anoc_throttle_cfg", + .id = QCS8300_SLAVE_ANOC_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = QCS8300_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_apss = { + .name = "qhs_apss", + .id = QCS8300_SLAVE_APPSS, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .name = "qhs_boot_rom", + .id = QCS8300_SLAVE_BOOT_ROM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = QCS8300_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_nrt_throttle_cfg = { + .name = "qhs_camera_nrt_throttle_cfg", + .id = QCS8300_SLAVE_CAMERA_NRT_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_rt_throttle_cfg = { + .name = "qhs_camera_rt_throttle_cfg", + .id = QCS8300_SLAVE_CAMERA_RT_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = QCS8300_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_compute0_cfg = { + .name = "qhs_compute0_cfg", + .id = QCS8300_SLAVE_CDSP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_CDSP_NOC_CFG }, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = QCS8300_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mmcx = { + .name = "qhs_cpr_mmcx", + .id = QCS8300_SLAVE_RBCPR_MMCX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mx = { + .name = "qhs_cpr_mx", + .id = QCS8300_SLAVE_RBCPR_MX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_nspcx = { + .name = "qhs_cpr_nspcx", + .id = QCS8300_SLAVE_CPR_NSPCX, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_nsphmx = { + .name = "qhs_cpr_nsphmx", + .id = QCS8300_SLAVE_CPR_NSPHMX, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = QCS8300_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cx_rdpm = { + .name = "qhs_cx_rdpm", + .id = QCS8300_SLAVE_CX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display0_cfg = { + .name = "qhs_display0_cfg", + .id = QCS8300_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display0_rt_throttle_cfg = { + .name = "qhs_display0_rt_throttle_cfg", + .id = QCS8300_SLAVE_DISPLAY_RT_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_emac0_cfg = { + .name = "qhs_emac0_cfg", + .id = QCS8300_SLAVE_EMAC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gp_dsp0_cfg = { + .name = "qhs_gp_dsp0_cfg", + .id = QCS8300_SLAVE_GP_DSP0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpdsp0_throttle_cfg = { + .name = "qhs_gpdsp0_throttle_cfg", + .id = QCS8300_SLAVE_GPDSP0_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpu_tcu_throttle_cfg = { + .name = "qhs_gpu_tcu_throttle_cfg", + .id = QCS8300_SLAVE_GPU_TCU_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = QCS8300_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_hwkm = { + .name = "qhs_hwkm", + .id = QCS8300_SLAVE_HWKM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = QCS8300_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .id = QCS8300_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = QCS8300_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_cfg = { + .name = "qhs_lpass_cfg", + .id = QCS8300_SLAVE_LPASS, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_CNOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qhs_lpass_throttle_cfg = { + .name = "qhs_lpass_throttle_cfg", + .id = QCS8300_SLAVE_LPASS_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_rdpm = { + .name = "qhs_mx_rdpm", + .id = QCS8300_SLAVE_MX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mxc_rdpm = { + .name = "qhs_mxc_rdpm", + .id = QCS8300_SLAVE_MXC_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .id = QCS8300_SLAVE_PCIE_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .id = QCS8300_SLAVE_PCIE_1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie_tcu_throttle_cfg = { + .name = "qhs_pcie_tcu_throttle_cfg", + .id = QCS8300_SLAVE_PCIE_TCU_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie_throttle_cfg = { + .name = "qhs_pcie_throttle_cfg", + .id = QCS8300_SLAVE_PCIE_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = QCS8300_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .name = "qhs_pimem_cfg", + .id = QCS8300_SLAVE_PIMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pke_wrapper_cfg = { + .name = "qhs_pke_wrapper_cfg", + .id = QCS8300_SLAVE_PKA_WRAPPER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = QCS8300_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qm_cfg = { + .name = "qhs_qm_cfg", + .id = QCS8300_SLAVE_QM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qm_mpu_cfg = { + .name = "qhs_qm_mpu_cfg", + .id = QCS8300_SLAVE_QM_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = QCS8300_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = QCS8300_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup3 = { + .name = "qhs_qup3", + .id = QCS8300_SLAVE_QUP_3, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sail_throttle_cfg = { + .name = "qhs_sail_throttle_cfg", + .id = QCS8300_SLAVE_SAIL_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .id = QCS8300_SLAVE_SDC1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_security = { + .name = "qhs_security", + .id = QCS8300_SLAVE_SECURITY, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_snoc_throttle_cfg = { + .name = "qhs_snoc_throttle_cfg", + .id = QCS8300_SLAVE_SNOC_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = QCS8300_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = QCS8300_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tsc_cfg = { + .name = "qhs_tsc_cfg", + .id = QCS8300_SLAVE_TSC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = QCS8300_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb2_0 = { + .name = "qhs_usb2_0", + .id = QCS8300_SLAVE_USB2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .id = QCS8300_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = QCS8300_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cvp_throttle_cfg = { + .name = "qhs_venus_cvp_throttle_cfg", + .id = QCS8300_SLAVE_VENUS_CVP_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_v_cpu_throttle_cfg = { + .name = "qhs_venus_v_cpu_throttle_cfg", + .id = QCS8300_SLAVE_VENUS_V_CPU_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_vcodec_throttle_cfg = { + .name = "qhs_venus_vcodec_throttle_cfg", + .id = QCS8300_SLAVE_VENUS_VCODEC_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_ddrss_cfg = { + .name = "qns_ddrss_cfg", + .id = QCS8300_SLAVE_DDRSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_CNOC_DC_NOC }, +}; + +static struct qcom_icc_node qns_gpdsp_noc_cfg = { + .name = "qns_gpdsp_noc_cfg", + .id = QCS8300_SLAVE_GPDSP_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mnoc_hf_cfg = { + .name = "qns_mnoc_hf_cfg", + .id = QCS8300_SLAVE_CNOC_MNOC_HF_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_CNOC_MNOC_HF_CFG }, +}; + +static struct qcom_icc_node qns_mnoc_sf_cfg = { + .name = "qns_mnoc_sf_cfg", + .id = QCS8300_SLAVE_CNOC_MNOC_SF_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_CNOC_MNOC_SF_CFG }, +}; + +static struct qcom_icc_node qns_pcie_anoc_cfg = { + .name = "qns_pcie_anoc_cfg", + .id = QCS8300_SLAVE_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_snoc_cfg = { + .name = "qns_snoc_cfg", + .id = QCS8300_SLAVE_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_SNOC_CFG }, +}; + +static struct qcom_icc_node qxs_boot_imem = { + .name = "qxs_boot_imem", + .id = QCS8300_SLAVE_BOOT_IMEM, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = QCS8300_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .id = QCS8300_SLAVE_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .id = QCS8300_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .id = QCS8300_SLAVE_PCIE_1, + .channels = 1, + .buswidth = 32, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = QCS8300_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = QCS8300_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_llcc = { + .name = "qhs_llcc", + .id = QCS8300_SLAVE_LLCC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc = { + .name = "qns_gemnoc", + .id = QCS8300_SLAVE_GEM_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { QCS8300_MASTER_GEM_NOC_CFG }, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = QCS8300_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = QCS8300_SLAVE_LLCC, + .channels = 4, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = QCS8300_SLAVE_GEM_NOC_PCIE_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node srvc_even_gemnoc = { + .name = "srvc_even_gemnoc", + .id = QCS8300_SLAVE_SERVICE_GEM_NOC_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_odd_gemnoc = { + .name = "srvc_odd_gemnoc", + .id = QCS8300_SLAVE_SERVICE_GEM_NOC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_sys_gemnoc = { + .name = "srvc_sys_gemnoc", + .id = QCS8300_SLAVE_SERVICE_GEM_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_sys_gemnoc_2 = { + .name = "srvc_sys_gemnoc_2", + .id = QCS8300_SLAVE_SERVICE_GEM_NOC2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gp_dsp_sail_noc = { + .name = "qns_gp_dsp_sail_noc", + .id = QCS8300_SLAVE_GP_DSP_SAIL_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_GPDSP_SAIL }, +}; + +static struct qcom_icc_node qhs_lpass_core = { + .name = "qhs_lpass_core", + .id = QCS8300_SLAVE_LPASS_CORE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_lpi = { + .name = "qhs_lpass_lpi", + .id = QCS8300_SLAVE_LPASS_LPI_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_mpu = { + .name = "qhs_lpass_mpu", + .id = QCS8300_SLAVE_LPASS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_top = { + .name = "qhs_lpass_top", + .id = QCS8300_SLAVE_LPASS_TOP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_sysnoc = { + .name = "qns_sysnoc", + .id = QCS8300_SLAVE_LPASS_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_LPASS_ANOC }, +}; + +static struct qcom_icc_node srvc_niu_aml_noc = { + .name = "srvc_niu_aml_noc", + .id = QCS8300_SLAVE_SERVICES_LPASS_AML_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_niu_lpass_agnoc = { + .name = "srvc_niu_lpass_agnoc", + .id = QCS8300_SLAVE_SERVICE_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = QCS8300_SLAVE_EBI1, + .channels = 8, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = QCS8300_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = QCS8300_SLAVE_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc_hf = { + .name = "srvc_mnoc_hf", + .id = QCS8300_SLAVE_SERVICE_MNOC_HF, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_mnoc_sf = { + .name = "srvc_mnoc_sf", + .id = QCS8300_SLAVE_SERVICE_MNOC_SF, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_hcp = { + .name = "qns_hcp", + .id = QCS8300_SLAVE_HCP_A, + .channels = 2, + .buswidth = 32, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = QCS8300_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node service_nsp_noc = { + .name = "service_nsp_noc", + .id = QCS8300_SLAVE_SERVICE_NSP_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = QCS8300_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { QCS8300_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qns_gemnoc_gc = { + .name = "qns_gemnoc_gc", + .id = QCS8300_SLAVE_SNOC_GEM_NOC_GC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { QCS8300_MASTER_SNOC_GC_MEM_NOC }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = QCS8300_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { QCS8300_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_snoc = { + .name = "srvc_snoc", + .id = QCS8300_SLAVE_SERVICE_SNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 2, + .nodes = { &qxm_crypto_0, &qxm_crypto_1 }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .keepalive = true, + .num_nodes = 2, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 66, + .nodes = { &qhs_ahb2phy2, &qhs_ahb2phy3, + &qhs_anoc_throttle_cfg, &qhs_aoss, + &qhs_apss, &qhs_boot_rom, + &qhs_camera_cfg, &qhs_camera_nrt_throttle_cfg, + &qhs_camera_rt_throttle_cfg, &qhs_clk_ctl, + &qhs_compute0_cfg, &qhs_cpr_cx, + &qhs_cpr_mmcx, &qhs_cpr_mx, + &qhs_cpr_nspcx, &qhs_cpr_nsphmx, + &qhs_crypto0_cfg, &qhs_cx_rdpm, + &qhs_display0_cfg, &qhs_display0_rt_throttle_cfg, + &qhs_emac0_cfg, &qhs_gp_dsp0_cfg, + &qhs_gpdsp0_throttle_cfg, &qhs_gpu_tcu_throttle_cfg, + &qhs_gpuss_cfg, &qhs_hwkm, + &qhs_imem_cfg, &qhs_ipa, + &qhs_ipc_router, &qhs_lpass_cfg, + &qhs_lpass_throttle_cfg, &qhs_mx_rdpm, + &qhs_mxc_rdpm, &qhs_pcie0_cfg, + &qhs_pcie1_cfg, &qhs_pcie_tcu_throttle_cfg, + &qhs_pcie_throttle_cfg, &qhs_pdm, + &qhs_pimem_cfg, &qhs_pke_wrapper_cfg, + &qhs_qdss_cfg, &qhs_qm_cfg, + &qhs_qm_mpu_cfg, &qhs_sail_throttle_cfg, + &qhs_sdc1, &qhs_security, + &qhs_snoc_throttle_cfg, &qhs_tcsr, + &qhs_tlmm, &qhs_tsc_cfg, + &qhs_ufs_mem_cfg, &qhs_usb2_0, + &qhs_usb3_0, &qhs_venus_cfg, + &qhs_venus_cvp_throttle_cfg, &qhs_venus_v_cpu_throttle_cfg, + &qhs_venus_vcodec_throttle_cfg, &qns_ddrss_cfg, + &qns_gpdsp_noc_cfg, &qns_mnoc_hf_cfg, + &qns_mnoc_sf_cfg, &qns_pcie_anoc_cfg, + &qns_snoc_cfg, &qxs_boot_imem, + &qxs_imem, &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_bcm bcm_cn2 = { + .name = "CN2", + .num_nodes = 3, + .nodes = { &qhs_qup0, &qhs_qup1, + &qhs_qup3 }, +}; + +static struct qcom_icc_bcm bcm_cn3 = { + .name = "CN3", + .num_nodes = 2, + .nodes = { &xs_pcie_0, &xs_pcie_1 }, +}; + +static struct qcom_icc_bcm bcm_gna0 = { + .name = "GNA0", + .num_nodes = 1, + .nodes = { &qxm_dsp0 }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .keepalive = true, + .num_nodes = 4, + .nodes = { &qnm_camnoc_hf, &qnm_mdp0_0, + &qnm_mdp0_1, &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .num_nodes = 6, + .nodes = { &qnm_camnoc_icp, &qnm_camnoc_sf, + &qnm_video0, &qnm_video_cvp, + &qnm_video_v_cpu, &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_nsa0 = { + .name = "NSA0", + .num_nodes = 2, + .nodes = { &qns_hcp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_nsa1 = { + .name = "NSA1", + .num_nodes = 1, + .nodes = { &qxm_nsp }, +}; + +static struct qcom_icc_bcm bcm_pci0 = { + .name = "PCI0", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh2 = { + .name = "SH2", + .num_nodes = 1, + .nodes = { &chm_apps }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn1 = { + .name = "SN1", + .num_nodes = 1, + .nodes = { &qns_gemnoc_gc }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qxs_pimem }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 2, + .nodes = { &qns_a1noc_snoc, &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 2, + .nodes = { &qns_a2noc_snoc, &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn9 = { + .name = "SN9", + .num_nodes = 2, + .nodes = { &qns_sysnoc, &qnm_lpass_noc }, +}; + +static struct qcom_icc_bcm bcm_sn10 = { + .name = "SN10", + .num_nodes = 1, + .nodes = { &xs_qdss_stm }, +}; + +static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { + &bcm_sn3, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QUP_3] = &qxm_qup3, + [MASTER_EMAC] = &xm_emac_0, + [MASTER_SDC] = &xm_sdc1, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB2] = &xm_usb2_2, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct qcom_icc_desc qcs8300_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), +}; + +static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { + &bcm_ce0, + &bcm_sn4, +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_CNOC_A2NOC] = &qnm_cnoc_datapath, + [MASTER_CRYPTO_CORE0] = &qxm_crypto_0, + [MASTER_CRYPTO_CORE1] = &qxm_crypto_1, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QDSS_ETR_0] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, +}; + +static const struct qcom_icc_desc qcs8300_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_3] = &qup3_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_3] = &qup3_core_slave, +}; + +static const struct qcom_icc_desc qcs8300_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const config_noc_bcms[] = { + &bcm_cn0, + &bcm_cn1, + &bcm_cn2, + &bcm_cn3, + &bcm_sn2, + &bcm_sn10, +}; + +static struct qcom_icc_node * const config_noc_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AHB2PHY_2] = &qhs_ahb2phy2, + [SLAVE_AHB2PHY_3] = &qhs_ahb2phy3, + [SLAVE_ANOC_THROTTLE_CFG] = &qhs_anoc_throttle_cfg, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_APPSS] = &qhs_apss, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CAMERA_NRT_THROTTLE_CFG] = &qhs_camera_nrt_throttle_cfg, + [SLAVE_CAMERA_RT_THROTTLE_CFG] = &qhs_camera_rt_throttle_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CDSP_CFG] = &qhs_compute0_cfg, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MMCX_CFG] = &qhs_cpr_mmcx, + [SLAVE_RBCPR_MX_CFG] = &qhs_cpr_mx, + [SLAVE_CPR_NSPCX] = &qhs_cpr_nspcx, + [SLAVE_CPR_NSPHMX] = &qhs_cpr_nsphmx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CX_RDPM] = &qhs_cx_rdpm, + [SLAVE_DISPLAY_CFG] = &qhs_display0_cfg, + [SLAVE_DISPLAY_RT_THROTTLE_CFG] = &qhs_display0_rt_throttle_cfg, + [SLAVE_EMAC_CFG] = &qhs_emac0_cfg, + [SLAVE_GP_DSP0_CFG] = &qhs_gp_dsp0_cfg, + [SLAVE_GPDSP0_THROTTLE_CFG] = &qhs_gpdsp0_throttle_cfg, + [SLAVE_GPU_TCU_THROTTLE_CFG] = &qhs_gpu_tcu_throttle_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_HWKM] = &qhs_hwkm, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_LPASS] = &qhs_lpass_cfg, + [SLAVE_LPASS_THROTTLE_CFG] = &qhs_lpass_throttle_cfg, + [SLAVE_MX_RDPM] = &qhs_mx_rdpm, + [SLAVE_MXC_RDPM] = &qhs_mxc_rdpm, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PCIE_TCU_THROTTLE_CFG] = &qhs_pcie_tcu_throttle_cfg, + [SLAVE_PCIE_THROTTLE_CFG] = &qhs_pcie_throttle_cfg, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PKA_WRAPPER_CFG] = &qhs_pke_wrapper_cfg, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QM_CFG] = &qhs_qm_cfg, + [SLAVE_QM_MPU_CFG] = &qhs_qm_mpu_cfg, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_3] = &qhs_qup3, + [SLAVE_SAIL_THROTTLE_CFG] = &qhs_sail_throttle_cfg, + [SLAVE_SDC1] = &qhs_sdc1, + [SLAVE_SECURITY] = &qhs_security, + [SLAVE_SNOC_THROTTLE_CFG] = &qhs_snoc_throttle_cfg, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_TSC_CFG] = &qhs_tsc_cfg, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB2] = &qhs_usb2_0, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VENUS_CVP_THROTTLE_CFG] = &qhs_venus_cvp_throttle_cfg, + [SLAVE_VENUS_V_CPU_THROTTLE_CFG] = &qhs_venus_v_cpu_throttle_cfg, + [SLAVE_VENUS_VCODEC_THROTTLE_CFG] = &qhs_venus_vcodec_throttle_cfg, + [SLAVE_DDRSS_CFG] = &qns_ddrss_cfg, + [SLAVE_GPDSP_NOC_CFG] = &qns_gpdsp_noc_cfg, + [SLAVE_CNOC_MNOC_HF_CFG] = &qns_mnoc_hf_cfg, + [SLAVE_CNOC_MNOC_SF_CFG] = &qns_mnoc_sf_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qns_pcie_anoc_cfg, + [SLAVE_SNOC_CFG] = &qns_snoc_cfg, + [SLAVE_BOOT_IMEM] = &qxs_boot_imem, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc qcs8300_config_noc = { + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .bcms = config_noc_bcms, + .num_bcms = ARRAY_SIZE(config_noc_bcms), +}; + +static struct qcom_icc_node * const dc_noc_nodes[] = { + [MASTER_CNOC_DC_NOC] = &qnm_cnoc_dc_noc, + [SLAVE_LLCC_CFG] = &qhs_llcc, + [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, +}; + +static const struct qcom_icc_desc qcs8300_dc_noc = { + .nodes = dc_noc_nodes, + .num_nodes = ARRAY_SIZE(dc_noc_nodes), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh2, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_PCIE_TCU] = &alm_pcie_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_COMPUTE_NOC] = &qnm_cmpnoc0, + [MASTER_GEM_NOC_CFG] = &qnm_gemnoc_cfg, + [MASTER_GPDSP_SAIL] = &qnm_gpdsp_sail, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_GEM_NOC_PCIE_CNOC] = &qns_pcie, + [SLAVE_SERVICE_GEM_NOC_1] = &srvc_even_gemnoc, + [SLAVE_SERVICE_GEM_NOC_2] = &srvc_odd_gemnoc, + [SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc, + [SLAVE_SERVICE_GEM_NOC2] = &srvc_sys_gemnoc_2, +}; + +static const struct qcom_icc_desc qcs8300_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm * const gpdsp_anoc_bcms[] = { + &bcm_gna0, +}; + +static struct qcom_icc_node * const gpdsp_anoc_nodes[] = { + [MASTER_SAILSS_MD0] = &qnm_sailss_md0, + [MASTER_DSP0] = &qxm_dsp0, + [SLAVE_GP_DSP_SAIL_NOC] = &qns_gp_dsp_sail_noc, +}; + +static const struct qcom_icc_desc qcs8300_gpdsp_anoc = { + .nodes = gpdsp_anoc_nodes, + .num_nodes = ARRAY_SIZE(gpdsp_anoc_nodes), + .bcms = gpdsp_anoc_bcms, + .num_bcms = ARRAY_SIZE(gpdsp_anoc_bcms), +}; + +static struct qcom_icc_bcm * const lpass_ag_noc_bcms[] = { + &bcm_sn9, +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_CNOC_LPASS_AG_NOC] = &qhm_config_noc, + [MASTER_LPASS_PROC] = &qxm_lpass_dsp, + [SLAVE_LPASS_CORE_CFG] = &qhs_lpass_core, + [SLAVE_LPASS_LPI_CFG] = &qhs_lpass_lpi, + [SLAVE_LPASS_MPU_CFG] = &qhs_lpass_mpu, + [SLAVE_LPASS_TOP_CFG] = &qhs_lpass_top, + [SLAVE_LPASS_SNOC] = &qns_sysnoc, + [SLAVE_SERVICES_LPASS_AML_NOC] = &srvc_niu_aml_noc, + [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, +}; + +static const struct qcom_icc_desc qcs8300_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), + .bcms = lpass_ag_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_ag_noc_bcms), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc qcs8300_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_ICP] = &qnm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP0] = &qnm_mdp0_0, + [MASTER_MDP1] = &qnm_mdp0_1, + [MASTER_CNOC_MNOC_HF_CFG] = &qnm_mnoc_hf_cfg, + [MASTER_CNOC_MNOC_SF_CFG] = &qnm_mnoc_sf_cfg, + [MASTER_VIDEO_P0] = &qnm_video0, + [MASTER_VIDEO_PROC] = &qnm_video_cvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC_HF] = &srvc_mnoc_hf, + [SLAVE_SERVICE_MNOC_SF] = &srvc_mnoc_sf, +}; + +static const struct qcom_icc_desc qcs8300_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nspa_noc_bcms[] = { + &bcm_nsa0, + &bcm_nsa1, +}; + +static struct qcom_icc_node * const nspa_noc_nodes[] = { + [MASTER_CDSP_NOC_CFG] = &qhm_nsp_noc_config, + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_HCP_A] = &qns_hcp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, + [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, +}; + +static const struct qcom_icc_desc qcs8300_nspa_noc = { + .nodes = nspa_noc_nodes, + .num_nodes = ARRAY_SIZE(nspa_noc_nodes), + .bcms = nspa_noc_bcms, + .num_bcms = ARRAY_SIZE(nspa_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_pci0, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, +}; + +static const struct qcom_icc_desc qcs8300_pcie_anoc = { + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn1, + &bcm_sn3, + &bcm_sn4, + &bcm_sn9, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_GIC_AHB] = &qhm_gic, + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_LPASS_ANOC] = &qnm_lpass_noc, + [MASTER_SNOC_CFG] = &qnm_snoc_cfg, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [SLAVE_SNOC_GEM_NOC_GC] = &qns_gemnoc_gc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, +}; + +static const struct qcom_icc_desc qcs8300_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,qcs8300-aggre1-noc", + .data = &qcs8300_aggre1_noc}, + { .compatible = "qcom,qcs8300-aggre2-noc", + .data = &qcs8300_aggre2_noc}, + { .compatible = "qcom,qcs8300-clk-virt", + .data = &qcs8300_clk_virt}, + { .compatible = "qcom,qcs8300-config-noc", + .data = &qcs8300_config_noc}, + { .compatible = "qcom,qcs8300-dc-noc", + .data = &qcs8300_dc_noc}, + { .compatible = "qcom,qcs8300-gem-noc", + .data = &qcs8300_gem_noc}, + { .compatible = "qcom,qcs8300-gpdsp-anoc", + .data = &qcs8300_gpdsp_anoc}, + { .compatible = "qcom,qcs8300-lpass-ag-noc", + .data = &qcs8300_lpass_ag_noc}, + { .compatible = "qcom,qcs8300-mc-virt", + .data = &qcs8300_mc_virt}, + { .compatible = "qcom,qcs8300-mmss-noc", + .data = &qcs8300_mmss_noc}, + { .compatible = "qcom,qcs8300-nspa-noc", + .data = &qcs8300_nspa_noc}, + { .compatible = "qcom,qcs8300-pcie-anoc", + .data = &qcs8300_pcie_anoc}, + { .compatible = "qcom,qcs8300-system-noc", + .data = &qcs8300_system_noc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-qcs8300", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("QCS8300 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/qcs8300.h b/drivers/interconnect/qcom/qcs8300.h new file mode 100644 index 0000000000000..6b9e2b424c2ad --- /dev/null +++ b/drivers/interconnect/qcom/qcs8300.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_QCS8300_H +#define __DRIVERS_INTERCONNECT_QCOM_QCS8300_H + +#define QCS8300_MASTER_GPU_TCU 0 +#define QCS8300_MASTER_PCIE_TCU 1 +#define QCS8300_MASTER_SYS_TCU 2 +#define QCS8300_MASTER_APPSS_PROC 3 +#define QCS8300_MASTER_LLCC 4 +#define QCS8300_MASTER_CNOC_LPASS_AG_NOC 5 +#define QCS8300_MASTER_GIC_AHB 6 +#define QCS8300_MASTER_CDSP_NOC_CFG 7 +#define QCS8300_MASTER_QDSS_BAM 8 +#define QCS8300_MASTER_QUP_0 9 +#define QCS8300_MASTER_QUP_1 10 +#define QCS8300_MASTER_A1NOC_SNOC 11 +#define QCS8300_MASTER_A2NOC_SNOC 12 +#define QCS8300_MASTER_CAMNOC_HF 13 +#define QCS8300_MASTER_CAMNOC_ICP 14 +#define QCS8300_MASTER_CAMNOC_SF 15 +#define QCS8300_MASTER_COMPUTE_NOC 16 +#define QCS8300_MASTER_CNOC_A2NOC 17 +#define QCS8300_MASTER_CNOC_DC_NOC 18 +#define QCS8300_MASTER_GEM_NOC_CFG 19 +#define QCS8300_MASTER_GEM_NOC_CNOC 20 +#define QCS8300_MASTER_GEM_NOC_PCIE_SNOC 21 +#define QCS8300_MASTER_GPDSP_SAIL 22 +#define QCS8300_MASTER_GFX3D 23 +#define QCS8300_MASTER_LPASS_ANOC 24 +#define QCS8300_MASTER_MDP0 25 +#define QCS8300_MASTER_MDP1 26 +#define QCS8300_MASTER_MNOC_HF_MEM_NOC 27 +#define QCS8300_MASTER_CNOC_MNOC_HF_CFG 28 +#define QCS8300_MASTER_MNOC_SF_MEM_NOC 29 +#define QCS8300_MASTER_CNOC_MNOC_SF_CFG 30 +#define QCS8300_MASTER_ANOC_PCIE_GEM_NOC 31 +#define QCS8300_MASTER_SAILSS_MD0 32 +#define QCS8300_MASTER_SNOC_CFG 33 +#define QCS8300_MASTER_SNOC_GC_MEM_NOC 34 +#define QCS8300_MASTER_SNOC_SF_MEM_NOC 35 +#define QCS8300_MASTER_VIDEO_P0 36 +#define QCS8300_MASTER_VIDEO_PROC 37 +#define QCS8300_MASTER_VIDEO_V_PROC 38 +#define QCS8300_MASTER_QUP_CORE_0 39 +#define QCS8300_MASTER_QUP_CORE_1 40 +#define QCS8300_MASTER_QUP_CORE_3 41 +#define QCS8300_MASTER_CRYPTO_CORE0 42 +#define QCS8300_MASTER_CRYPTO_CORE1 43 +#define QCS8300_MASTER_DSP0 44 +#define QCS8300_MASTER_IPA 45 +#define QCS8300_MASTER_LPASS_PROC 46 +#define QCS8300_MASTER_CDSP_PROC 47 +#define QCS8300_MASTER_PIMEM 48 +#define QCS8300_MASTER_QUP_3 49 +#define QCS8300_MASTER_EMAC 50 +#define QCS8300_MASTER_GIC 51 +#define QCS8300_MASTER_PCIE_0 52 +#define QCS8300_MASTER_PCIE_1 53 +#define QCS8300_MASTER_QDSS_ETR_0 54 +#define QCS8300_MASTER_QDSS_ETR_1 55 +#define QCS8300_MASTER_SDC 56 +#define QCS8300_MASTER_UFS_MEM 57 +#define QCS8300_MASTER_USB2 58 +#define QCS8300_MASTER_USB3_0 59 +#define QCS8300_SLAVE_EBI1 60 +#define QCS8300_SLAVE_AHB2PHY_2 61 +#define QCS8300_SLAVE_AHB2PHY_3 62 +#define QCS8300_SLAVE_ANOC_THROTTLE_CFG 63 +#define QCS8300_SLAVE_AOSS 64 +#define QCS8300_SLAVE_APPSS 65 +#define QCS8300_SLAVE_BOOT_ROM 66 +#define QCS8300_SLAVE_CAMERA_CFG 67 +#define QCS8300_SLAVE_CAMERA_NRT_THROTTLE_CFG 68 +#define QCS8300_SLAVE_CAMERA_RT_THROTTLE_CFG 69 +#define QCS8300_SLAVE_CLK_CTL 70 +#define QCS8300_SLAVE_CDSP_CFG 71 +#define QCS8300_SLAVE_RBCPR_CX_CFG 72 +#define QCS8300_SLAVE_RBCPR_MMCX_CFG 73 +#define QCS8300_SLAVE_RBCPR_MX_CFG 74 +#define QCS8300_SLAVE_CPR_NSPCX 75 +#define QCS8300_SLAVE_CPR_NSPHMX 76 +#define QCS8300_SLAVE_CRYPTO_0_CFG 77 +#define QCS8300_SLAVE_CX_RDPM 78 +#define QCS8300_SLAVE_DISPLAY_CFG 79 +#define QCS8300_SLAVE_DISPLAY_RT_THROTTLE_CFG 80 +#define QCS8300_SLAVE_EMAC_CFG 81 +#define QCS8300_SLAVE_GP_DSP0_CFG 82 +#define QCS8300_SLAVE_GPDSP0_THROTTLE_CFG 83 +#define QCS8300_SLAVE_GPU_TCU_THROTTLE_CFG 84 +#define QCS8300_SLAVE_GFX3D_CFG 85 +#define QCS8300_SLAVE_HWKM 86 +#define QCS8300_SLAVE_IMEM_CFG 87 +#define QCS8300_SLAVE_IPA_CFG 88 +#define QCS8300_SLAVE_IPC_ROUTER_CFG 89 +#define QCS8300_SLAVE_LLCC_CFG 90 +#define QCS8300_SLAVE_LPASS 91 +#define QCS8300_SLAVE_LPASS_CORE_CFG 92 +#define QCS8300_SLAVE_LPASS_LPI_CFG 93 +#define QCS8300_SLAVE_LPASS_MPU_CFG 94 +#define QCS8300_SLAVE_LPASS_THROTTLE_CFG 95 +#define QCS8300_SLAVE_LPASS_TOP_CFG 96 +#define QCS8300_SLAVE_MX_RDPM 97 +#define QCS8300_SLAVE_MXC_RDPM 98 +#define QCS8300_SLAVE_PCIE_0_CFG 99 +#define QCS8300_SLAVE_PCIE_1_CFG 100 +#define QCS8300_SLAVE_PCIE_TCU_THROTTLE_CFG 101 +#define QCS8300_SLAVE_PCIE_THROTTLE_CFG 102 +#define QCS8300_SLAVE_PDM 103 +#define QCS8300_SLAVE_PIMEM_CFG 104 +#define QCS8300_SLAVE_PKA_WRAPPER_CFG 105 +#define QCS8300_SLAVE_QDSS_CFG 106 +#define QCS8300_SLAVE_QM_CFG 107 +#define QCS8300_SLAVE_QM_MPU_CFG 108 +#define QCS8300_SLAVE_QUP_0 109 +#define QCS8300_SLAVE_QUP_1 110 +#define QCS8300_SLAVE_QUP_3 111 +#define QCS8300_SLAVE_SAIL_THROTTLE_CFG 112 +#define QCS8300_SLAVE_SDC1 113 +#define QCS8300_SLAVE_SECURITY 114 +#define QCS8300_SLAVE_SNOC_THROTTLE_CFG 115 +#define QCS8300_SLAVE_TCSR 116 +#define QCS8300_SLAVE_TLMM 117 +#define QCS8300_SLAVE_TSC_CFG 118 +#define QCS8300_SLAVE_UFS_MEM_CFG 119 +#define QCS8300_SLAVE_USB2 120 +#define QCS8300_SLAVE_USB3_0 121 +#define QCS8300_SLAVE_VENUS_CFG 122 +#define QCS8300_SLAVE_VENUS_CVP_THROTTLE_CFG 123 +#define QCS8300_SLAVE_VENUS_V_CPU_THROTTLE_CFG 124 +#define QCS8300_SLAVE_VENUS_VCODEC_THROTTLE_CFG 125 +#define QCS8300_SLAVE_A1NOC_SNOC 126 +#define QCS8300_SLAVE_A2NOC_SNOC 127 +#define QCS8300_SLAVE_DDRSS_CFG 128 +#define QCS8300_SLAVE_GEM_NOC_CNOC 129 +#define QCS8300_SLAVE_GEM_NOC_CFG 130 +#define QCS8300_SLAVE_SNOC_GEM_NOC_GC 131 +#define QCS8300_SLAVE_SNOC_GEM_NOC_SF 132 +#define QCS8300_SLAVE_GP_DSP_SAIL_NOC 133 +#define QCS8300_SLAVE_GPDSP_NOC_CFG 134 +#define QCS8300_SLAVE_HCP_A 135 +#define QCS8300_SLAVE_LLCC 136 +#define QCS8300_SLAVE_MNOC_HF_MEM_NOC 137 +#define QCS8300_SLAVE_MNOC_SF_MEM_NOC 138 +#define QCS8300_SLAVE_CNOC_MNOC_HF_CFG 139 +#define QCS8300_SLAVE_CNOC_MNOC_SF_CFG 140 +#define QCS8300_SLAVE_CDSP_MEM_NOC 141 +#define QCS8300_SLAVE_GEM_NOC_PCIE_CNOC 142 +#define QCS8300_SLAVE_PCIE_ANOC_CFG 143 +#define QCS8300_SLAVE_ANOC_PCIE_GEM_NOC 144 +#define QCS8300_SLAVE_SNOC_CFG 145 +#define QCS8300_SLAVE_LPASS_SNOC 146 +#define QCS8300_SLAVE_QUP_CORE_0 147 +#define QCS8300_SLAVE_QUP_CORE_1 148 +#define QCS8300_SLAVE_QUP_CORE_3 149 +#define QCS8300_SLAVE_BOOT_IMEM 150 +#define QCS8300_SLAVE_IMEM 151 +#define QCS8300_SLAVE_PIMEM 152 +#define QCS8300_SLAVE_SERVICE_NSP_NOC 153 +#define QCS8300_SLAVE_SERVICE_GEM_NOC_1 154 +#define QCS8300_SLAVE_SERVICE_MNOC_HF 155 +#define QCS8300_SLAVE_SERVICE_MNOC_SF 156 +#define QCS8300_SLAVE_SERVICES_LPASS_AML_NOC 157 +#define QCS8300_SLAVE_SERVICE_LPASS_AG_NOC 158 +#define QCS8300_SLAVE_SERVICE_GEM_NOC_2 159 +#define QCS8300_SLAVE_SERVICE_SNOC 160 +#define QCS8300_SLAVE_SERVICE_GEM_NOC 161 +#define QCS8300_SLAVE_SERVICE_GEM_NOC2 162 +#define QCS8300_SLAVE_PCIE_0 163 +#define QCS8300_SLAVE_PCIE_1 164 +#define QCS8300_SLAVE_QDSS_STM 165 +#define QCS8300_SLAVE_TCU 166 + +#endif diff --git a/drivers/interconnect/qcom/qdu1000.c b/drivers/interconnect/qcom/qdu1000.c index 9cb477d2bdfe2..a7392eb73d4a9 100644 --- a/drivers/interconnect/qcom/qdu1000.c +++ b/drivers/interconnect/qcom/qdu1000.c @@ -1046,7 +1046,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qnoc_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-qdu1000", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sa8775p.c b/drivers/interconnect/qcom/sa8775p.c index a729775c2aa45..e2826af3ea2e1 100644 --- a/drivers/interconnect/qcom/sa8775p.c +++ b/drivers/interconnect/qcom/sa8775p.c @@ -2519,7 +2519,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sa8775p", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sar2130p.c b/drivers/interconnect/qcom/sar2130p.c new file mode 100644 index 0000000000000..9eac0ac768127 --- /dev/null +++ b/drivers/interconnect/qcom/sar2130p.c @@ -0,0 +1,1930 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-common.h" +#include "icc-rpmh.h" + +enum { + SAR2130P_MASTER_QUP_CORE_0, + SAR2130P_MASTER_QUP_CORE_1, + SAR2130P_MASTER_GEM_NOC_CNOC, + SAR2130P_MASTER_GEM_NOC_PCIE_SNOC, + SAR2130P_MASTER_QDSS_DAP, + SAR2130P_MASTER_GPU_TCU, + SAR2130P_MASTER_SYS_TCU, + SAR2130P_MASTER_APPSS_PROC, + SAR2130P_MASTER_GFX3D, + SAR2130P_MASTER_MNOC_HF_MEM_NOC, + SAR2130P_MASTER_MNOC_SF_MEM_NOC, + SAR2130P_MASTER_COMPUTE_NOC, + SAR2130P_MASTER_ANOC_PCIE_GEM_NOC, + SAR2130P_MASTER_SNOC_GC_MEM_NOC, + SAR2130P_MASTER_SNOC_SF_MEM_NOC, + SAR2130P_MASTER_WLAN_Q6, + SAR2130P_MASTER_CNOC_LPASS_AG_NOC, + SAR2130P_MASTER_LPASS_PROC, + SAR2130P_MASTER_LLCC, + SAR2130P_MASTER_CAMNOC_HF, + SAR2130P_MASTER_CAMNOC_ICP, + SAR2130P_MASTER_CAMNOC_SF, + SAR2130P_MASTER_LSR, + SAR2130P_MASTER_MDP, + SAR2130P_MASTER_CNOC_MNOC_CFG, + SAR2130P_MASTER_VIDEO, + SAR2130P_MASTER_VIDEO_CV_PROC, + SAR2130P_MASTER_VIDEO_PROC, + SAR2130P_MASTER_VIDEO_V_PROC, + SAR2130P_MASTER_CDSP_NOC_CFG, + SAR2130P_MASTER_CDSP_PROC, + SAR2130P_MASTER_PCIE_0, + SAR2130P_MASTER_PCIE_1, + SAR2130P_MASTER_GIC_AHB, + SAR2130P_MASTER_QDSS_BAM, + SAR2130P_MASTER_QSPI_0, + SAR2130P_MASTER_QUP_0, + SAR2130P_MASTER_QUP_1, + SAR2130P_MASTER_A2NOC_SNOC, + SAR2130P_MASTER_CNOC_DATAPATH, + SAR2130P_MASTER_LPASS_ANOC, + SAR2130P_MASTER_SNOC_CFG, + SAR2130P_MASTER_CRYPTO, + SAR2130P_MASTER_PIMEM, + SAR2130P_MASTER_GIC, + SAR2130P_MASTER_QDSS_ETR, + SAR2130P_MASTER_QDSS_ETR_1, + SAR2130P_MASTER_SDCC_1, + SAR2130P_MASTER_USB3_0, + SAR2130P_SLAVE_QUP_CORE_0, + SAR2130P_SLAVE_QUP_CORE_1, + SAR2130P_SLAVE_AHB2PHY_SOUTH, + SAR2130P_SLAVE_AOSS, + SAR2130P_SLAVE_CAMERA_CFG, + SAR2130P_SLAVE_CLK_CTL, + SAR2130P_SLAVE_CDSP_CFG, + SAR2130P_SLAVE_RBCPR_CX_CFG, + SAR2130P_SLAVE_RBCPR_MMCX_CFG, + SAR2130P_SLAVE_RBCPR_MXA_CFG, + SAR2130P_SLAVE_RBCPR_MXC_CFG, + SAR2130P_SLAVE_CPR_NSPCX, + SAR2130P_SLAVE_CRYPTO_0_CFG, + SAR2130P_SLAVE_CX_RDPM, + SAR2130P_SLAVE_DISPLAY_CFG, + SAR2130P_SLAVE_GFX3D_CFG, + SAR2130P_SLAVE_IMEM_CFG, + SAR2130P_SLAVE_IPC_ROUTER_CFG, + SAR2130P_SLAVE_LPASS, + SAR2130P_SLAVE_MX_RDPM, + SAR2130P_SLAVE_PCIE_0_CFG, + SAR2130P_SLAVE_PCIE_1_CFG, + SAR2130P_SLAVE_PDM, + SAR2130P_SLAVE_PIMEM_CFG, + SAR2130P_SLAVE_PRNG, + SAR2130P_SLAVE_QDSS_CFG, + SAR2130P_SLAVE_QSPI_0, + SAR2130P_SLAVE_QUP_0, + SAR2130P_SLAVE_QUP_1, + SAR2130P_SLAVE_SDCC_1, + SAR2130P_SLAVE_TCSR, + SAR2130P_SLAVE_TLMM, + SAR2130P_SLAVE_TME_CFG, + SAR2130P_SLAVE_USB3_0, + SAR2130P_SLAVE_VENUS_CFG, + SAR2130P_SLAVE_VSENSE_CTRL_CFG, + SAR2130P_SLAVE_WLAN_Q6_CFG, + SAR2130P_SLAVE_DDRSS_CFG, + SAR2130P_SLAVE_CNOC_MNOC_CFG, + SAR2130P_SLAVE_SNOC_CFG, + SAR2130P_SLAVE_IMEM, + SAR2130P_SLAVE_PIMEM, + SAR2130P_SLAVE_SERVICE_CNOC, + SAR2130P_SLAVE_PCIE_0, + SAR2130P_SLAVE_PCIE_1, + SAR2130P_SLAVE_QDSS_STM, + SAR2130P_SLAVE_TCU, + SAR2130P_SLAVE_GEM_NOC_CNOC, + SAR2130P_SLAVE_LLCC, + SAR2130P_SLAVE_MEM_NOC_PCIE_SNOC, + SAR2130P_SLAVE_LPASS_CORE_CFG, + SAR2130P_SLAVE_LPASS_LPI_CFG, + SAR2130P_SLAVE_LPASS_MPU_CFG, + SAR2130P_SLAVE_LPASS_TOP_CFG, + SAR2130P_SLAVE_LPASS_SNOC, + SAR2130P_SLAVE_SERVICES_LPASS_AML_NOC, + SAR2130P_SLAVE_SERVICE_LPASS_AG_NOC, + SAR2130P_SLAVE_EBI1, + SAR2130P_SLAVE_MNOC_HF_MEM_NOC, + SAR2130P_SLAVE_MNOC_SF_MEM_NOC, + SAR2130P_SLAVE_SERVICE_MNOC, + SAR2130P_SLAVE_CDSP_MEM_NOC, + SAR2130P_SLAVE_SERVICE_NSP_NOC, + SAR2130P_SLAVE_ANOC_PCIE_GEM_NOC, + SAR2130P_SLAVE_A2NOC_SNOC, + SAR2130P_SLAVE_SNOC_GEM_NOC_GC, + SAR2130P_SLAVE_SNOC_GEM_NOC_SF, + SAR2130P_SLAVE_SERVICE_SNOC, +}; + +static const struct regmap_config icc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = SAR2130P_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = SAR2130P_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = SAR2130P_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 43, + .links = { SAR2130P_SLAVE_AHB2PHY_SOUTH, SAR2130P_SLAVE_AOSS, + SAR2130P_SLAVE_CAMERA_CFG, SAR2130P_SLAVE_CLK_CTL, + SAR2130P_SLAVE_CDSP_CFG, SAR2130P_SLAVE_RBCPR_CX_CFG, + SAR2130P_SLAVE_RBCPR_MMCX_CFG, SAR2130P_SLAVE_RBCPR_MXA_CFG, + SAR2130P_SLAVE_RBCPR_MXC_CFG, SAR2130P_SLAVE_CPR_NSPCX, + SAR2130P_SLAVE_CRYPTO_0_CFG, SAR2130P_SLAVE_CX_RDPM, + SAR2130P_SLAVE_DISPLAY_CFG, SAR2130P_SLAVE_GFX3D_CFG, + SAR2130P_SLAVE_IMEM_CFG, SAR2130P_SLAVE_IPC_ROUTER_CFG, + SAR2130P_SLAVE_LPASS, SAR2130P_SLAVE_MX_RDPM, + SAR2130P_SLAVE_PCIE_0_CFG, SAR2130P_SLAVE_PCIE_1_CFG, + SAR2130P_SLAVE_PDM, SAR2130P_SLAVE_PIMEM_CFG, + SAR2130P_SLAVE_PRNG, SAR2130P_SLAVE_QDSS_CFG, + SAR2130P_SLAVE_QSPI_0, SAR2130P_SLAVE_QUP_0, + SAR2130P_SLAVE_QUP_1, SAR2130P_SLAVE_SDCC_1, + SAR2130P_SLAVE_TCSR, SAR2130P_SLAVE_TLMM, + SAR2130P_SLAVE_TME_CFG, SAR2130P_SLAVE_USB3_0, + SAR2130P_SLAVE_VENUS_CFG, SAR2130P_SLAVE_VSENSE_CTRL_CFG, + SAR2130P_SLAVE_WLAN_Q6_CFG, SAR2130P_SLAVE_DDRSS_CFG, + SAR2130P_SLAVE_CNOC_MNOC_CFG, SAR2130P_SLAVE_SNOC_CFG, + SAR2130P_SLAVE_IMEM, SAR2130P_SLAVE_PIMEM, + SAR2130P_SLAVE_SERVICE_CNOC, SAR2130P_SLAVE_QDSS_STM, + SAR2130P_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = SAR2130P_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SAR2130P_SLAVE_PCIE_0, SAR2130P_SLAVE_PCIE_1 }, +}; + +static struct qcom_icc_node xm_qdss_dap = { + .name = "xm_qdss_dap", + .id = SAR2130P_MASTER_QDSS_DAP, + .channels = 1, + .buswidth = 8, + .num_links = 43, + .links = { SAR2130P_SLAVE_AHB2PHY_SOUTH, SAR2130P_SLAVE_AOSS, + SAR2130P_SLAVE_CAMERA_CFG, SAR2130P_SLAVE_CLK_CTL, + SAR2130P_SLAVE_CDSP_CFG, SAR2130P_SLAVE_RBCPR_CX_CFG, + SAR2130P_SLAVE_RBCPR_MMCX_CFG, SAR2130P_SLAVE_RBCPR_MXA_CFG, + SAR2130P_SLAVE_RBCPR_MXC_CFG, SAR2130P_SLAVE_CPR_NSPCX, + SAR2130P_SLAVE_CRYPTO_0_CFG, SAR2130P_SLAVE_CX_RDPM, + SAR2130P_SLAVE_DISPLAY_CFG, SAR2130P_SLAVE_GFX3D_CFG, + SAR2130P_SLAVE_IMEM_CFG, SAR2130P_SLAVE_IPC_ROUTER_CFG, + SAR2130P_SLAVE_LPASS, SAR2130P_SLAVE_MX_RDPM, + SAR2130P_SLAVE_PCIE_0_CFG, SAR2130P_SLAVE_PCIE_1_CFG, + SAR2130P_SLAVE_PDM, SAR2130P_SLAVE_PIMEM_CFG, + SAR2130P_SLAVE_PRNG, SAR2130P_SLAVE_QDSS_CFG, + SAR2130P_SLAVE_QSPI_0, SAR2130P_SLAVE_QUP_0, + SAR2130P_SLAVE_QUP_1, SAR2130P_SLAVE_SDCC_1, + SAR2130P_SLAVE_TCSR, SAR2130P_SLAVE_TLMM, + SAR2130P_SLAVE_TME_CFG, SAR2130P_SLAVE_USB3_0, + SAR2130P_SLAVE_VENUS_CFG, SAR2130P_SLAVE_VSENSE_CTRL_CFG, + SAR2130P_SLAVE_WLAN_Q6_CFG, SAR2130P_SLAVE_DDRSS_CFG, + SAR2130P_SLAVE_CNOC_MNOC_CFG, SAR2130P_SLAVE_SNOC_CFG, + SAR2130P_SLAVE_IMEM, SAR2130P_SLAVE_PIMEM, + SAR2130P_SLAVE_SERVICE_CNOC, SAR2130P_SLAVE_QDSS_STM, + SAR2130P_SLAVE_TCU }, +}; + +static const struct qcom_icc_qosbox alm_gpu_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0x9e000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = SAR2130P_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .qosbox = &alm_gpu_tcu_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox alm_sys_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0x9f000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = SAR2130P_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .qosbox = &alm_sys_tcu_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = SAR2130P_MASTER_APPSS_PROC, + .channels = 1, + .buswidth = 32, + .num_links = 3, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC, + SAR2130P_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static const struct qcom_icc_qosbox qnm_gpu_qos = { + .num_ports = 2, + .port_offsets = { 0xe000, 0x4e000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = SAR2130P_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_gpu_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox qnm_mnoc_hf_qos = { + .num_ports = 2, + .port_offsets = { 0xf000, 0x4f000 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = SAR2130P_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_hf_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox qnm_mnoc_sf_qos = { + .num_ports = 1, + .port_offsets = { 0x9d000 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = SAR2130P_MASTER_MNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .qosbox = &qnm_mnoc_sf_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox qnm_nsp_gemnoc_qos = { + .num_ports = 2, + .port_offsets = { 0x10000, 0x50000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .id = SAR2130P_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_nsp_gemnoc_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox qnm_pcie_qos = { + .num_ports = 1, + .port_offsets = { 0xa2000 }, + .prio = 2, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = SAR2130P_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_pcie_qos, + .num_links = 2, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox qnm_snoc_gc_qos = { + .num_ports = 1, + .port_offsets = { 0xa0000 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_snoc_gc = { + .name = "qnm_snoc_gc", + .id = SAR2130P_MASTER_SNOC_GC_MEM_NOC, + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_snoc_gc_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_LLCC }, +}; + +static const struct qcom_icc_qosbox qnm_snoc_sf_qos = { + .num_ports = 1, + .port_offsets = { 0xa1000 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = SAR2130P_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_snoc_sf_qos, + .num_links = 3, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC, + SAR2130P_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qxm_wlan_q6 = { + .name = "qxm_wlan_q6", + .id = SAR2130P_MASTER_WLAN_Q6, + .channels = 1, + .buswidth = 8, + .num_links = 3, + .links = { SAR2130P_SLAVE_GEM_NOC_CNOC, SAR2130P_SLAVE_LLCC, + SAR2130P_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qhm_config_noc = { + .name = "qhm_config_noc", + .id = SAR2130P_MASTER_CNOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 6, + .links = { SAR2130P_SLAVE_LPASS_CORE_CFG, SAR2130P_SLAVE_LPASS_LPI_CFG, + SAR2130P_SLAVE_LPASS_MPU_CFG, SAR2130P_SLAVE_LPASS_TOP_CFG, + SAR2130P_SLAVE_SERVICES_LPASS_AML_NOC, SAR2130P_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qxm_lpass_dsp = { + .name = "qxm_lpass_dsp", + .id = SAR2130P_MASTER_LPASS_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 4, + .links = { SAR2130P_SLAVE_LPASS_TOP_CFG, SAR2130P_SLAVE_LPASS_SNOC, + SAR2130P_SLAVE_SERVICES_LPASS_AML_NOC, SAR2130P_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = SAR2130P_MASTER_LLCC, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_SLAVE_EBI1 }, +}; + +static const struct qcom_icc_qosbox qnm_camnoc_hf_qos = { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .id = SAR2130P_MASTER_CAMNOC_HF, + .channels = 1, + .buswidth = 32, + .qosbox = &qnm_camnoc_hf_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_camnoc_icp_qos = { + .num_ports = 1, + .port_offsets = { 0x1c080 }, + .prio = 4, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_camnoc_icp = { + .name = "qnm_camnoc_icp", + .id = SAR2130P_MASTER_CAMNOC_ICP, + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_camnoc_icp_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_camnoc_sf_qos = { + .num_ports = 1, + .port_offsets = { 0x1c100 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .id = SAR2130P_MASTER_CAMNOC_SF, + .channels = 1, + .buswidth = 32, + .qosbox = &qnm_camnoc_sf_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_lsr_qos = { + .num_ports = 2, + .port_offsets = { 0x1f000, 0x1f080 }, + .prio = 3, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_lsr = { + .name = "qnm_lsr", + .id = SAR2130P_MASTER_LSR, + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_lsr_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_mdp_qos = { + .num_ports = 2, + .port_offsets = { 0x1d000, 0x1d080 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .id = SAR2130P_MASTER_MDP, + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mdp_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mnoc_cfg = { + .name = "qnm_mnoc_cfg", + .id = SAR2130P_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_SLAVE_SERVICE_MNOC }, +}; + +static const struct qcom_icc_qosbox qnm_video_qos = { + .num_ports = 2, + .port_offsets = { 0x1e000, 0x1e080 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_video = { + .name = "qnm_video", + .id = SAR2130P_MASTER_VIDEO, + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_video_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_video_cv_cpu_qos = { + .num_ports = 1, + .port_offsets = { 0x1e100 }, + .prio = 4, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .id = SAR2130P_MASTER_VIDEO_CV_PROC, + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_video_cv_cpu_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_video_cvp_qos = { + .num_ports = 1, + .port_offsets = { 0x1e180 }, + .prio = 0, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_video_cvp = { + .name = "qnm_video_cvp", + .id = SAR2130P_MASTER_VIDEO_PROC, + .channels = 1, + .buswidth = 32, + .qosbox = &qnm_video_cvp_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox qnm_video_v_cpu_qos = { + .num_ports = 1, + .port_offsets = { 0x1e200 }, + .prio = 4, + .urg_fwd = 1, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .id = SAR2130P_MASTER_VIDEO_V_PROC, + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_video_v_cpu_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qhm_nsp_noc_config = { + .name = "qhm_nsp_noc_config", + .id = SAR2130P_MASTER_CDSP_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_SLAVE_SERVICE_NSP_NOC }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .id = SAR2130P_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SAR2130P_SLAVE_CDSP_MEM_NOC }, +}; + +static const struct qcom_icc_qosbox xm_pcie3_0_qos = { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .id = SAR2130P_MASTER_PCIE_0, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_pcie3_0_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static const struct qcom_icc_qosbox xm_pcie3_1_qos = { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .id = SAR2130P_MASTER_PCIE_1, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_pcie3_1_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static const struct qcom_icc_qosbox qhm_gic_qos = { + .num_ports = 1, + .port_offsets = { 0x1d000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_gic = { + .name = "qhm_gic", + .id = SAR2130P_MASTER_GIC_AHB, + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_gic_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static const struct qcom_icc_qosbox qhm_qdss_bam_qos = { + .num_ports = 1, + .port_offsets = { 0x22000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = SAR2130P_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qdss_bam_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox qhm_qspi_qos = { + .num_ports = 1, + .port_offsets = { 0x23000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = SAR2130P_MASTER_QSPI_0, + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qspi_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox qhm_qup0_qos = { + .num_ports = 1, + .port_offsets = { 0x24000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = SAR2130P_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qup0_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox qhm_qup1_qos = { + .num_ports = 1, + .port_offsets = { 0x25000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = SAR2130P_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qup1_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = SAR2130P_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static const struct qcom_icc_qosbox qnm_cnoc_datapath_qos = { + .num_ports = 1, + .port_offsets = { 0x26000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_cnoc_datapath = { + .name = "qnm_cnoc_datapath", + .id = SAR2130P_MASTER_CNOC_DATAPATH, + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_cnoc_datapath_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox qnm_lpass_noc_qos = { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_lpass_noc = { + .name = "qnm_lpass_noc", + .id = SAR2130P_MASTER_LPASS_ANOC, + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_lpass_noc_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_snoc_cfg = { + .name = "qnm_snoc_cfg", + .id = SAR2130P_MASTER_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_SLAVE_SERVICE_SNOC }, +}; + +static const struct qcom_icc_qosbox qxm_crypto_qos = { + .num_ports = 1, + .port_offsets = { 0x27000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = SAR2130P_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .qosbox = &qxm_crypto_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox qxm_pimem_qos = { + .num_ports = 1, + .port_offsets = { 0x1f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .id = SAR2130P_MASTER_PIMEM, + .channels = 1, + .buswidth = 8, + .qosbox = &qxm_pimem_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static const struct qcom_icc_qosbox xm_gic_qos = { + .num_ports = 1, + .port_offsets = { 0x21000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = SAR2130P_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_gic_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static const struct qcom_icc_qosbox xm_qdss_etr_0_qos = { + .num_ports = 1, + .port_offsets = { 0x1b000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .id = SAR2130P_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_qdss_etr_0_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox xm_qdss_etr_1_qos = { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .id = SAR2130P_MASTER_QDSS_ETR_1, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_qdss_etr_1_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox xm_sdc1_qos = { + .num_ports = 1, + .port_offsets = { 0x29000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .id = SAR2130P_MASTER_SDCC_1, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_sdc1_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static const struct qcom_icc_qosbox xm_usb3_0_qos = { + .num_ports = 1, + .port_offsets = { 0x28000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = SAR2130P_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .qosbox = &xm_usb3_0_qos, + .num_links = 1, + .links = { SAR2130P_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = SAR2130P_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = SAR2130P_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .id = SAR2130P_SLAVE_AHB2PHY_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = SAR2130P_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = SAR2130P_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = SAR2130P_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_compute_cfg = { + .name = "qhs_compute_cfg", + .id = SAR2130P_SLAVE_CDSP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_MASTER_CDSP_NOC_CFG }, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = SAR2130P_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mmcx = { + .name = "qhs_cpr_mmcx", + .id = SAR2130P_SLAVE_RBCPR_MMCX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxa = { + .name = "qhs_cpr_mxa", + .id = SAR2130P_SLAVE_RBCPR_MXA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxc = { + .name = "qhs_cpr_mxc", + .id = SAR2130P_SLAVE_RBCPR_MXC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_nspcx = { + .name = "qhs_cpr_nspcx", + .id = SAR2130P_SLAVE_CPR_NSPCX, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = SAR2130P_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cx_rdpm = { + .name = "qhs_cx_rdpm", + .id = SAR2130P_SLAVE_CX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = SAR2130P_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = SAR2130P_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = SAR2130P_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = SAR2130P_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_cfg = { + .name = "qhs_lpass_cfg", + .id = SAR2130P_SLAVE_LPASS, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_MASTER_CNOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qhs_mx_rdpm = { + .name = "qhs_mx_rdpm", + .id = SAR2130P_SLAVE_MX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .id = SAR2130P_SLAVE_PCIE_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .id = SAR2130P_SLAVE_PCIE_1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = SAR2130P_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .name = "qhs_pimem_cfg", + .id = SAR2130P_SLAVE_PIMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .id = SAR2130P_SLAVE_PRNG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = SAR2130P_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = SAR2130P_SLAVE_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = SAR2130P_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = SAR2130P_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .id = SAR2130P_SLAVE_SDCC_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = SAR2130P_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = SAR2130P_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .id = SAR2130P_SLAVE_TME_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .id = SAR2130P_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = SAR2130P_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .id = SAR2130P_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_wlan_q6 = { + .name = "qhs_wlan_q6", + .id = SAR2130P_SLAVE_WLAN_Q6_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_ddrss_cfg = { + .name = "qns_ddrss_cfg", + .id = SAR2130P_SLAVE_DDRSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mnoc_cfg = { + .name = "qns_mnoc_cfg", + .id = SAR2130P_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qns_snoc_cfg = { + .name = "qns_snoc_cfg", + .id = SAR2130P_SLAVE_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SAR2130P_MASTER_SNOC_CFG }, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = SAR2130P_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .id = SAR2130P_SLAVE_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_cnoc = { + .name = "srvc_cnoc", + .id = SAR2130P_SLAVE_SERVICE_CNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .id = SAR2130P_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .id = SAR2130P_SLAVE_PCIE_1, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = SAR2130P_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = SAR2130P_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = SAR2130P_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = SAR2130P_SLAVE_LLCC, + .channels = 2, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = SAR2130P_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SAR2130P_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qhs_lpass_core = { + .name = "qhs_lpass_core", + .id = SAR2130P_SLAVE_LPASS_CORE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_lpi = { + .name = "qhs_lpass_lpi", + .id = SAR2130P_SLAVE_LPASS_LPI_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_mpu = { + .name = "qhs_lpass_mpu", + .id = SAR2130P_SLAVE_LPASS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_top = { + .name = "qhs_lpass_top", + .id = SAR2130P_SLAVE_LPASS_TOP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_sysnoc = { + .name = "qns_sysnoc", + .id = SAR2130P_SLAVE_LPASS_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_MASTER_LPASS_ANOC }, +}; + +static struct qcom_icc_node srvc_niu_aml_noc = { + .name = "srvc_niu_aml_noc", + .id = SAR2130P_SLAVE_SERVICES_LPASS_AML_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_niu_lpass_agnoc = { + .name = "srvc_niu_lpass_agnoc", + .id = SAR2130P_SLAVE_SERVICE_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = SAR2130P_SLAVE_EBI1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = SAR2130P_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SAR2130P_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = SAR2130P_SLAVE_MNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SAR2130P_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = SAR2130P_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = SAR2130P_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SAR2130P_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node service_nsp_noc = { + .name = "service_nsp_noc", + .id = SAR2130P_SLAVE_SERVICE_NSP_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = SAR2130P_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = SAR2130P_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qns_gemnoc_gc = { + .name = "qns_gemnoc_gc", + .id = SAR2130P_SLAVE_SNOC_GEM_NOC_GC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SAR2130P_MASTER_SNOC_GC_MEM_NOC }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = SAR2130P_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SAR2130P_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_snoc = { + .name = "srvc_snoc", + .id = SAR2130P_SLAVE_SERVICE_SNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 48, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &xm_qdss_dap, &qhs_ahb2phy0, + &qhs_aoss, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_compute_cfg, + &qhs_cpr_cx, &qhs_cpr_mmcx, + &qhs_cpr_mxa, &qhs_cpr_mxc, + &qhs_cpr_nspcx, &qhs_crypto0_cfg, + &qhs_cx_rdpm, &qhs_display_cfg, + &qhs_gpuss_cfg, &qhs_imem_cfg, + &qhs_ipc_router, &qhs_lpass_cfg, + &qhs_mx_rdpm, &qhs_pcie0_cfg, + &qhs_pcie1_cfg, &qhs_pdm, + &qhs_pimem_cfg, &qhs_prng, + &qhs_qdss_cfg, &qhs_qspi, + &qhs_qup0, &qhs_qup1, + &qhs_sdc1, &qhs_tcsr, + &qhs_tlmm, &qhs_tme_cfg, + &qhs_usb3_0, &qhs_venus_cfg, + &qhs_vsense_ctrl_cfg, &qhs_wlan_q6, + &qns_ddrss_cfg, &qns_mnoc_cfg, + &qns_snoc_cfg, &qxs_imem, + &qxs_pimem, &srvc_cnoc, + &xs_pcie_0, &xs_pcie_1, + &xs_qdss_stm, &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 11, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp, + &qnm_camnoc_sf, &qnm_lsr, + &qnm_mdp, &qnm_mnoc_cfg, + &qnm_video, &qnm_video_cv_cpu, + &qnm_video_cvp, &qnm_video_v_cpu, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 13, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &chm_apps, &qnm_gpu, + &qnm_mnoc_hf, &qnm_mnoc_sf, + &qnm_nsp_gemnoc, &qnm_pcie, + &qnm_snoc_gc, &qnm_snoc_sf, + &qxm_wlan_q6, &qns_gem_noc_cnoc, + &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn1 = { + .name = "SN1", + .enable_mask = BIT(0), + .num_nodes = 4, + .nodes = { &qhm_gic, &qxm_pimem, + &xm_gic, &qns_gemnoc_gc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qnm_lpass_noc }, +}; + +static struct qcom_icc_bcm bcm_sn7 = { + .name = "SN7", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, +}; + +static const struct qcom_icc_desc sar2130p_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const config_noc_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const config_noc_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [MASTER_QDSS_DAP] = &xm_qdss_dap, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CDSP_CFG] = &qhs_compute_cfg, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MMCX_CFG] = &qhs_cpr_mmcx, + [SLAVE_RBCPR_MXA_CFG] = &qhs_cpr_mxa, + [SLAVE_RBCPR_MXC_CFG] = &qhs_cpr_mxc, + [SLAVE_CPR_NSPCX] = &qhs_cpr_nspcx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CX_RDPM] = &qhs_cx_rdpm, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_LPASS] = &qhs_lpass_cfg, + [SLAVE_MX_RDPM] = &qhs_mx_rdpm, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_SDCC_1] = &qhs_sdc1, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_WLAN_Q6_CFG] = &qhs_wlan_q6, + [SLAVE_DDRSS_CFG] = &qns_ddrss_cfg, + [SLAVE_CNOC_MNOC_CFG] = &qns_mnoc_cfg, + [SLAVE_SNOC_CFG] = &qns_snoc_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc sar2130p_config_noc = { + .config = &icc_regmap_config, + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .bcms = config_noc_bcms, + .num_bcms = ARRAY_SIZE(config_noc_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qxm_wlan_q6, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct qcom_icc_desc sar2130p_gem_noc = { + .config = &icc_regmap_config, + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm * const lpass_ag_noc_bcms[] = { +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_CNOC_LPASS_AG_NOC] = &qhm_config_noc, + [MASTER_LPASS_PROC] = &qxm_lpass_dsp, + [SLAVE_LPASS_CORE_CFG] = &qhs_lpass_core, + [SLAVE_LPASS_LPI_CFG] = &qhs_lpass_lpi, + [SLAVE_LPASS_MPU_CFG] = &qhs_lpass_mpu, + [SLAVE_LPASS_TOP_CFG] = &qhs_lpass_top, + [SLAVE_LPASS_SNOC] = &qns_sysnoc, + [SLAVE_SERVICES_LPASS_AML_NOC] = &srvc_niu_aml_noc, + [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, +}; + +static const struct qcom_icc_desc sar2130p_lpass_ag_noc = { + .config = &icc_regmap_config, + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), + .bcms = lpass_ag_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_ag_noc_bcms), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc sar2130p_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_ICP] = &qnm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_LSR] = &qnm_lsr, + [MASTER_MDP] = &qnm_mdp, + [MASTER_CNOC_MNOC_CFG] = &qnm_mnoc_cfg, + [MASTER_VIDEO] = &qnm_video, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_PROC] = &qnm_video_cvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC] = &srvc_mnoc, +}; + +static const struct qcom_icc_desc sar2130p_mmss_noc = { + .config = &icc_regmap_config, + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_NOC_CFG] = &qhm_nsp_noc_config, + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, + [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, +}; + +static const struct qcom_icc_desc sar2130p_nsp_noc = { + .config = &icc_regmap_config, + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn7, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, +}; + +static const struct qcom_icc_desc sar2130p_pcie_anoc = { + .config = &icc_regmap_config, + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_ce0, + &bcm_sn0, + &bcm_sn1, + &bcm_sn3, + &bcm_sn4, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_GIC_AHB] = &qhm_gic, + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_CNOC_DATAPATH] = &qnm_cnoc_datapath, + [MASTER_LPASS_ANOC] = &qnm_lpass_noc, + [MASTER_SNOC_CFG] = &qnm_snoc_cfg, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, + [SLAVE_SNOC_GEM_NOC_GC] = &qns_gemnoc_gc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, +}; + +static const struct qcom_icc_desc sar2130p_system_noc = { + .config = &icc_regmap_config, + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,sar2130p-clk-virt", .data = &sar2130p_clk_virt}, + { .compatible = "qcom,sar2130p-config-noc", .data = &sar2130p_config_noc}, + { .compatible = "qcom,sar2130p-gem-noc", .data = &sar2130p_gem_noc}, + { .compatible = "qcom,sar2130p-lpass-ag-noc", .data = &sar2130p_lpass_ag_noc}, + { .compatible = "qcom,sar2130p-mc-virt", .data = &sar2130p_mc_virt}, + { .compatible = "qcom,sar2130p-mmss-noc", .data = &sar2130p_mmss_noc}, + { .compatible = "qcom,sar2130p-nsp-noc", .data = &sar2130p_nsp_noc}, + { .compatible = "qcom,sar2130p-pcie-anoc", .data = &sar2130p_pcie_anoc}, + { .compatible = "qcom,sar2130p-system-noc", .data = &sar2130p_system_noc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-sar2130p", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} + +module_exit(qnoc_driver_exit); +MODULE_DESCRIPTION("Qualcomm SAR2130P NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/sc7180.c b/drivers/interconnect/qcom/sc7180.c index 34a1d163d6e16..af2be15438403 100644 --- a/drivers/interconnect/qcom/sc7180.c +++ b/drivers/interconnect/qcom/sc7180.c @@ -1807,7 +1807,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sc7180", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sc7280.c b/drivers/interconnect/qcom/sc7280.c index 167971f8e8bec..346f18d70e9e5 100644 --- a/drivers/interconnect/qcom/sc7280.c +++ b/drivers/interconnect/qcom/sc7280.c @@ -1691,7 +1691,7 @@ static const struct qcom_icc_desc sc7280_aggre1_noc = { .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), - .qos_clks_required = true, + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -1723,7 +1723,7 @@ static const struct qcom_icc_desc sc7280_aggre2_noc = { .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), - .qos_clks_required = true, + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const clk_virt_bcms[] = { @@ -2111,7 +2111,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sc7280", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sc8180x.c b/drivers/interconnect/qcom/sc8180x.c index 03d626776ba17..a741badaa966e 100644 --- a/drivers/interconnect/qcom/sc8180x.c +++ b/drivers/interconnect/qcom/sc8180x.c @@ -1889,7 +1889,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sc8180x", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sc8280xp.c b/drivers/interconnect/qcom/sc8280xp.c index 7acd152bf0dd8..0270f6c64481a 100644 --- a/drivers/interconnect/qcom/sc8280xp.c +++ b/drivers/interconnect/qcom/sc8280xp.c @@ -2391,7 +2391,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sc8280xp", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index ab91de446da88..7392bebba3344 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -1714,7 +1714,7 @@ MODULE_DEVICE_TABLE(of, sdm660_noc_of_match); static struct platform_driver sdm660_noc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-sdm660", .of_match_table = sdm660_noc_of_match, diff --git a/drivers/interconnect/qcom/sdm670.c b/drivers/interconnect/qcom/sdm670.c index e5ee7fbaa641c..907e1ff4ff817 100644 --- a/drivers/interconnect/qcom/sdm670.c +++ b/drivers/interconnect/qcom/sdm670.c @@ -1533,7 +1533,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sdm670", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index 584800ac871a8..855802be93fea 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -1802,7 +1802,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sdm845", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sdx55.c b/drivers/interconnect/qcom/sdx55.c index e97f28b8d2b25..4117db046fa00 100644 --- a/drivers/interconnect/qcom/sdx55.c +++ b/drivers/interconnect/qcom/sdx55.c @@ -913,7 +913,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sdx55", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sdx65.c b/drivers/interconnect/qcom/sdx65.c index 2f3f5479d8a51..d3a6c6c148e5d 100644 --- a/drivers/interconnect/qcom/sdx65.c +++ b/drivers/interconnect/qcom/sdx65.c @@ -897,7 +897,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sdx65", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sdx75.c b/drivers/interconnect/qcom/sdx75.c index 7f422c27488d3..7ef1f17f3292e 100644 --- a/drivers/interconnect/qcom/sdx75.c +++ b/drivers/interconnect/qcom/sdx75.c @@ -1083,7 +1083,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sdx75", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm6115.c b/drivers/interconnect/qcom/sm6115.c index 271b07c74862d..3ee12c8a4d56b 100644 --- a/drivers/interconnect/qcom/sm6115.c +++ b/drivers/interconnect/qcom/sm6115.c @@ -1402,7 +1402,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qnoc_probe, - .remove_new = qnoc_remove, + .remove = qnoc_remove, .driver = { .name = "qnoc-sm6115", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm6350.c b/drivers/interconnect/qcom/sm6350.c index 20923e8e61102..f41d7e19ba269 100644 --- a/drivers/interconnect/qcom/sm6350.c +++ b/drivers/interconnect/qcom/sm6350.c @@ -1702,7 +1702,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm6350", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm7150.c b/drivers/interconnect/qcom/sm7150.c index dc0d1343f5100..c8c77407cd508 100644 --- a/drivers/interconnect/qcom/sm7150.c +++ b/drivers/interconnect/qcom/sm7150.c @@ -1730,7 +1730,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm7150", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm8150.c b/drivers/interconnect/qcom/sm8150.c index f29b77556a799..edfe824cad353 100644 --- a/drivers/interconnect/qcom/sm8150.c +++ b/drivers/interconnect/qcom/sm8150.c @@ -1864,7 +1864,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm8150", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c index 1879fa15761f5..cc1b14c135291 100644 --- a/drivers/interconnect/qcom/sm8250.c +++ b/drivers/interconnect/qcom/sm8250.c @@ -1991,7 +1991,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm8250", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm8350.c b/drivers/interconnect/qcom/sm8350.c index 4236a43dc256f..38105ead4f295 100644 --- a/drivers/interconnect/qcom/sm8350.c +++ b/drivers/interconnect/qcom/sm8350.c @@ -1807,7 +1807,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm8350", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c index b3cd0087377ca..eb7e17df32ba6 100644 --- a/drivers/interconnect/qcom/sm8450.c +++ b/drivers/interconnect/qcom/sm8450.c @@ -1884,7 +1884,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm8450", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm8550.c b/drivers/interconnect/qcom/sm8550.c index 4d0e6fa9e003b..fdb97d1f1d074 100644 --- a/drivers/interconnect/qcom/sm8550.c +++ b/drivers/interconnect/qcom/sm8550.c @@ -1645,7 +1645,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm8550", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/sm8650.c b/drivers/interconnect/qcom/sm8650.c index b962e6c233ef7..20ac5bc5e1fba 100644 --- a/drivers/interconnect/qcom/sm8650.c +++ b/drivers/interconnect/qcom/sm8650.c @@ -1650,7 +1650,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-sm8650", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/smd-rpm.c b/drivers/interconnect/qcom/smd-rpm.c index 3816bfb4e2f39..8316c87a2c60f 100644 --- a/drivers/interconnect/qcom/smd-rpm.c +++ b/drivers/interconnect/qcom/smd-rpm.c @@ -85,7 +85,7 @@ static struct platform_driver qcom_interconnect_rpm_smd_driver = { .name = "icc_smd_rpm", }, .probe = qcom_icc_rpm_smd_probe, - .remove_new = qcom_icc_rpm_smd_remove, + .remove = qcom_icc_rpm_smd_remove, }; module_platform_driver(qcom_interconnect_rpm_smd_driver); MODULE_AUTHOR("Georgi Djakov "); diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c index 654abb9ce08ee..2c46fdb4a0543 100644 --- a/drivers/interconnect/qcom/x1e80100.c +++ b/drivers/interconnect/qcom/x1e80100.c @@ -1964,7 +1964,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qcom_icc_rpmh_probe, - .remove_new = qcom_icc_rpmh_remove, + .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-x1e80100", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/samsung/exynos.c b/drivers/interconnect/samsung/exynos.c index c9e5361e17c5b..9e041365d9091 100644 --- a/drivers/interconnect/samsung/exynos.c +++ b/drivers/interconnect/samsung/exynos.c @@ -180,7 +180,7 @@ static struct platform_driver exynos_generic_icc_driver = { .sync_state = icc_sync_state, }, .probe = exynos_generic_icc_probe, - .remove_new = exynos_generic_icc_remove, + .remove = exynos_generic_icc_remove, }; module_platform_driver(exynos_generic_icc_driver); diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 5ce8e6504ba7e..3f691e1fd22ce 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -2407,9 +2407,8 @@ static struct iommu_domain *amd_iommu_domain_alloc(unsigned int type) } static struct iommu_domain * -amd_iommu_domain_alloc_user(struct device *dev, u32 flags, - struct iommu_domain *parent, - const struct iommu_user_data *user_data) +amd_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags, + const struct iommu_user_data *user_data) { unsigned int type = IOMMU_DOMAIN_UNMANAGED; @@ -2420,7 +2419,7 @@ amd_iommu_domain_alloc_user(struct device *dev, u32 flags, if (dev) iommu = get_amd_iommu_from_dev(dev); - if ((flags & ~supported_flags) || parent || user_data) + if ((flags & ~supported_flags) || user_data) return ERR_PTR(-EOPNOTSUPP); /* Allocate domain with v2 page table if IOMMU supports PASID. */ @@ -2884,7 +2883,7 @@ const struct iommu_ops amd_iommu_ops = { .release_domain = &release_domain, .identity_domain = &identity_domain.domain, .domain_alloc = amd_iommu_domain_alloc, - .domain_alloc_user = amd_iommu_domain_alloc_user, + .domain_alloc_paging_flags = amd_iommu_domain_alloc_paging_flags, .domain_alloc_sva = amd_iommu_domain_alloc_sva, .probe_device = amd_iommu_probe_device, .release_device = amd_iommu_release_device, diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 04630dbfedd92..e4ebd9e12ad46 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -3132,9 +3132,8 @@ static struct iommu_domain arm_smmu_blocked_domain = { }; static struct iommu_domain * -arm_smmu_domain_alloc_user(struct device *dev, u32 flags, - struct iommu_domain *parent, - const struct iommu_user_data *user_data) +arm_smmu_domain_alloc_paging_flags(struct device *dev, u32 flags, + const struct iommu_user_data *user_data) { struct arm_smmu_master *master = dev_iommu_priv_get(dev); const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING | @@ -3145,7 +3144,7 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags, if (flags & ~PAGING_FLAGS) return ERR_PTR(-EOPNOTSUPP); - if (parent || user_data) + if (user_data) return ERR_PTR(-EOPNOTSUPP); if (flags & IOMMU_HWPT_ALLOC_PASID) @@ -3546,7 +3545,7 @@ static struct iommu_ops arm_smmu_ops = { .hw_info = arm_smmu_hw_info, .domain_alloc_paging = arm_smmu_domain_alloc_paging, .domain_alloc_sva = arm_smmu_sva_domain_alloc, - .domain_alloc_user = arm_smmu_domain_alloc_user, + .domain_alloc_paging_flags = arm_smmu_domain_alloc_paging_flags, .probe_device = arm_smmu_probe_device, .release_device = arm_smmu_release_device, .device_group = arm_smmu_device_group, diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 527f6f89d8a1f..7d0acb74d5a54 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -3328,9 +3328,8 @@ static struct dmar_domain *paging_domain_alloc(struct device *dev, bool first_st } static struct iommu_domain * -intel_iommu_domain_alloc_user(struct device *dev, u32 flags, - struct iommu_domain *parent, - const struct iommu_user_data *user_data) +intel_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags, + const struct iommu_user_data *user_data) { struct device_domain_info *info = dev_iommu_priv_get(dev); bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; @@ -3340,13 +3339,6 @@ intel_iommu_domain_alloc_user(struct device *dev, u32 flags, struct iommu_domain *domain; bool first_stage; - /* Must be NESTING domain */ - if (parent) { - if (!nested_supported(iommu) || flags) - return ERR_PTR(-EOPNOTSUPP); - return intel_nested_domain_alloc(parent, user_data); - } - if (flags & (~(IOMMU_HWPT_ALLOC_NEST_PARENT | IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_FAULT_ID_VALID))) @@ -4472,9 +4464,10 @@ const struct iommu_ops intel_iommu_ops = { .identity_domain = &identity_domain, .capable = intel_iommu_capable, .hw_info = intel_iommu_hw_info, - .domain_alloc_user = intel_iommu_domain_alloc_user, + .domain_alloc_paging_flags = intel_iommu_domain_alloc_paging_flags, .domain_alloc_sva = intel_svm_domain_alloc, .domain_alloc_paging = intel_iommu_domain_alloc_paging, + .domain_alloc_nested = intel_iommu_domain_alloc_nested, .probe_device = intel_iommu_probe_device, .release_device = intel_iommu_release_device, .get_resv_regions = intel_iommu_get_resv_regions, diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h index 2cca094c259dc..6ea7bbe26b19d 100644 --- a/drivers/iommu/intel/iommu.h +++ b/drivers/iommu/intel/iommu.h @@ -1265,8 +1265,10 @@ int __domain_setup_first_level(struct intel_iommu *iommu, int dmar_ir_support(void); void iommu_flush_write_buffer(struct intel_iommu *iommu); -struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent, - const struct iommu_user_data *user_data); +struct iommu_domain * +intel_iommu_domain_alloc_nested(struct device *dev, struct iommu_domain *parent, + u32 flags, + const struct iommu_user_data *user_data); struct device *device_rbtree_find(struct intel_iommu *iommu, u16 rid); enum cache_tag_type { diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c index 42c4533a6ea21..aba92c00b4274 100644 --- a/drivers/iommu/intel/nested.c +++ b/drivers/iommu/intel/nested.c @@ -186,14 +186,21 @@ static const struct iommu_domain_ops intel_nested_domain_ops = { .cache_invalidate_user = intel_nested_cache_invalidate_user, }; -struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent, - const struct iommu_user_data *user_data) +struct iommu_domain * +intel_iommu_domain_alloc_nested(struct device *dev, struct iommu_domain *parent, + u32 flags, + const struct iommu_user_data *user_data) { + struct device_domain_info *info = dev_iommu_priv_get(dev); struct dmar_domain *s2_domain = to_dmar_domain(parent); + struct intel_iommu *iommu = info->iommu; struct iommu_hwpt_vtd_s1 vtd; struct dmar_domain *domain; int ret; + if (!nested_supported(iommu) || flags) + return ERR_PTR(-EOPNOTSUPP); + /* Must be nested domain */ if (user_data->type != IOMMU_HWPT_DATA_VTD_S1) return ERR_PTR(-EOPNOTSUPP); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 7618e9c65d3fa..9bc0c74cca3c7 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1987,8 +1987,8 @@ __iommu_paging_domain_alloc_flags(struct device *dev, unsigned int type, if (ops->domain_alloc_paging && !flags) domain = ops->domain_alloc_paging(dev); - else if (ops->domain_alloc_user) - domain = ops->domain_alloc_user(dev, flags, NULL, NULL); + else if (ops->domain_alloc_paging_flags) + domain = ops->domain_alloc_paging_flags(dev, flags, NULL); else if (ops->domain_alloc && !flags) domain = ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); else diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c index 9236e8ca9aa86..ce03c38046515 100644 --- a/drivers/iommu/iommufd/hw_pagetable.c +++ b/drivers/iommu/iommufd/hw_pagetable.c @@ -119,7 +119,7 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, lockdep_assert_held(&ioas->mutex); - if ((flags || user_data) && !ops->domain_alloc_user) + if ((flags || user_data) && !ops->domain_alloc_paging_flags) return ERR_PTR(-EOPNOTSUPP); if (flags & ~valid_flags) return ERR_PTR(-EOPNOTSUPP); @@ -139,9 +139,9 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, hwpt_paging->ioas = ioas; hwpt_paging->nest_parent = flags & IOMMU_HWPT_ALLOC_NEST_PARENT; - if (ops->domain_alloc_user) { - hwpt->domain = ops->domain_alloc_user(idev->dev, flags, NULL, - user_data); + if (ops->domain_alloc_paging_flags) { + hwpt->domain = ops->domain_alloc_paging_flags(idev->dev, flags, + user_data); if (IS_ERR(hwpt->domain)) { rc = PTR_ERR(hwpt->domain); hwpt->domain = NULL; @@ -227,7 +227,7 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx, int rc; if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) || - !user_data->len || !ops->domain_alloc_user) + !user_data->len || !ops->domain_alloc_nested) return ERR_PTR(-EOPNOTSUPP); if (parent->auto_domain || !parent->nest_parent || parent->common.domain->owner != ops) @@ -242,9 +242,9 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx, refcount_inc(&parent->common.obj.users); hwpt_nested->parent = parent; - hwpt->domain = ops->domain_alloc_user(idev->dev, - flags & ~IOMMU_HWPT_FAULT_ID_VALID, - parent->common.domain, user_data); + hwpt->domain = ops->domain_alloc_nested( + idev->dev, parent->common.domain, + flags & ~IOMMU_HWPT_FAULT_ID_VALID, user_data); if (IS_ERR(hwpt->domain)) { rc = PTR_ERR(hwpt->domain); hwpt->domain = NULL; diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index 2f9de177dffc7..a0de6d6d4e689 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -356,8 +356,8 @@ __mock_domain_alloc_nested(const struct iommu_user_data *user_data) } static struct iommu_domain * -mock_domain_alloc_nested(struct iommu_domain *parent, u32 flags, - const struct iommu_user_data *user_data) +mock_domain_alloc_nested(struct device *dev, struct iommu_domain *parent, + u32 flags, const struct iommu_user_data *user_data) { struct mock_iommu_domain_nested *mock_nested; struct mock_iommu_domain *mock_parent; @@ -379,9 +379,8 @@ mock_domain_alloc_nested(struct iommu_domain *parent, u32 flags, } static struct iommu_domain * -mock_domain_alloc_user(struct device *dev, u32 flags, - struct iommu_domain *parent, - const struct iommu_user_data *user_data) +mock_domain_alloc_paging_flags(struct device *dev, u32 flags, + const struct iommu_user_data *user_data) { bool has_dirty_flag = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING | @@ -390,9 +389,6 @@ mock_domain_alloc_user(struct device *dev, u32 flags, MOCK_FLAGS_DEVICE_NO_DIRTY; struct iommu_domain *domain; - if (parent) - return mock_domain_alloc_nested(parent, flags, user_data); - if (user_data) return ERR_PTR(-EOPNOTSUPP); if ((flags & ~PAGING_FLAGS) || (has_dirty_flag && no_dirty_ops)) @@ -718,7 +714,8 @@ static const struct iommu_ops mock_ops = { .pgsize_bitmap = MOCK_IO_PAGE_SIZE, .hw_info = mock_domain_hw_info, .domain_alloc_paging = mock_domain_alloc_paging, - .domain_alloc_user = mock_domain_alloc_user, + .domain_alloc_paging_flags = mock_domain_alloc_paging_flags, + .domain_alloc_nested = mock_domain_alloc_nested, .capable = mock_domain_capable, .device_group = generic_device_group, .probe_device = mock_probe_device, diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 16acce0f102d4..92244cfa04647 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -47,6 +47,7 @@ #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) #define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) #define ITS_FLAGS_FORCE_NON_SHAREABLE (1ULL << 3) +#define ITS_FLAGS_WORKAROUND_HISILICON_162100801 (1ULL << 4) #define RD_LOCAL_LPI_ENABLED BIT(0) #define RD_LOCAL_PENDTABLE_PREALLOCATED BIT(1) @@ -64,6 +65,7 @@ static u32 lpi_id_bits; #define LPI_PENDBASE_SZ ALIGN(BIT(LPI_NRBITS) / 8, SZ_64K) static u8 __ro_after_init lpi_prop_prio; +static struct its_node *find_4_1_its(void); /* * Collection structure - just an ID, and a redistributor address to @@ -3883,6 +3885,20 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to) raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags); } +static void its_vpe_4_1_invall_locked(int cpu, struct its_vpe *vpe) +{ + void __iomem *rdbase; + u64 val; + + val = GICR_INVALLR_V; + val |= FIELD_PREP(GICR_INVALLR_VPEID, vpe->vpe_id); + + guard(raw_spinlock)(&gic_data_rdist_cpu(cpu)->rd_lock); + rdbase = per_cpu_ptr(gic_rdists->rdist, cpu)->rd_base; + gic_write_lpir(val, rdbase + GICR_INVALLR); + wait_for_syncr(rdbase); +} + static int its_vpe_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) @@ -3890,6 +3906,7 @@ static int its_vpe_set_affinity(struct irq_data *d, struct its_vpe *vpe = irq_data_get_irq_chip_data(d); unsigned int from, cpu = nr_cpu_ids; struct cpumask *table_mask; + struct its_node *its; unsigned long flags; /* @@ -3952,6 +3969,11 @@ static int its_vpe_set_affinity(struct irq_data *d, vpe->col_idx = cpu; its_send_vmovp(vpe); + + its = find_4_1_its(); + if (its && its->flags & ITS_FLAGS_WORKAROUND_HISILICON_162100801) + its_vpe_4_1_invall_locked(cpu, vpe); + its_vpe_db_proxy_move(vpe, from, cpu); out: @@ -4259,22 +4281,12 @@ static void its_vpe_4_1_deschedule(struct its_vpe *vpe, static void its_vpe_4_1_invall(struct its_vpe *vpe) { - void __iomem *rdbase; unsigned long flags; - u64 val; int cpu; - val = GICR_INVALLR_V; - val |= FIELD_PREP(GICR_INVALLR_VPEID, vpe->vpe_id); - /* Target the redistributor this vPE is currently known on */ cpu = vpe_to_cpuid_lock(vpe, &flags); - raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock); - rdbase = per_cpu_ptr(gic_rdists->rdist, cpu)->rd_base; - gic_write_lpir(val, rdbase + GICR_INVALLR); - - wait_for_syncr(rdbase); - raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock); + its_vpe_4_1_invall_locked(cpu, vpe); vpe_to_cpuid_unlock(vpe, flags); } @@ -4867,6 +4879,14 @@ static bool its_set_non_coherent(void *data) return true; } +static bool __maybe_unused its_enable_quirk_hip09_162100801(void *data) +{ + struct its_node *its = data; + + its->flags |= ITS_FLAGS_WORKAROUND_HISILICON_162100801; + return true; +} + static const struct gic_quirk its_quirks[] = { #ifdef CONFIG_CAVIUM_ERRATUM_22375 { @@ -4913,6 +4933,14 @@ static const struct gic_quirk its_quirks[] = { .init = its_enable_quirk_hip07_161600802, }, #endif +#ifdef CONFIG_HISILICON_ERRATUM_162100801 + { + .desc = "ITS: Hip09 erratum 162100801", + .iidr = 0x00051736, + .mask = 0xffffffff, + .init = its_enable_quirk_hip09_162100801, + }, +#endif #ifdef CONFIG_ROCKCHIP_ERRATUM_3588001 { .desc = "ITS: Rockchip erratum RK3588001", diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 3be7bd8cd8cde..8fae6dc010241 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -400,7 +400,7 @@ static void gic_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gic_chip_data *gic = irq_data_get_irq_chip_data(d); if (gic->domain->pm_dev) - seq_printf(p, gic->domain->pm_dev->of_node->name); + seq_puts(p, gic->domain->pm_dev->of_node->name); else seq_printf(p, "GIC-%d", (int)(gic - &gic_data[0])); } diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index b42ed68acfa66..85f80bac09615 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -479,7 +479,7 @@ static struct platform_driver pdc_intc_driver = { .of_match_table = pdc_intc_match, }, .probe = pdc_intc_probe, - .remove_new = pdc_intc_remove, + .remove = pdc_intc_remove, }; static int __init pdc_intc_init(void) diff --git a/drivers/irqchip/irq-imx-intmux.c b/drivers/irqchip/irq-imx-intmux.c index 511adfaeec822..787543d07565b 100644 --- a/drivers/irqchip/irq-imx-intmux.c +++ b/drivers/irqchip/irq-imx-intmux.c @@ -361,6 +361,6 @@ static struct platform_driver imx_intmux_driver = { .pm = &imx_intmux_pm_ops, }, .probe = imx_intmux_probe, - .remove_new = imx_intmux_remove, + .remove = imx_intmux_remove, }; builtin_platform_driver(imx_intmux_driver); diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 75a0e980ff352..b0e9788c00454 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -328,6 +328,6 @@ static struct platform_driver imx_irqsteer_driver = { .pm = &imx_irqsteer_pm_ops, }, .probe = imx_irqsteer_probe, - .remove_new = imx_irqsteer_remove, + .remove = imx_irqsteer_remove, }; builtin_platform_driver(imx_irqsteer_driver); diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c index 30f1979fa1240..808c781e25483 100644 --- a/drivers/irqchip/irq-keystone.c +++ b/drivers/irqchip/irq-keystone.c @@ -211,7 +211,7 @@ MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids); static struct platform_driver keystone_irq_device_driver = { .probe = keystone_irq_probe, - .remove_new = keystone_irq_remove, + .remove = keystone_irq_remove, .driver = { .name = "keystone_irq", .of_match_table = of_match_ptr(keystone_irq_dt_ids), diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index e24db71a8783c..bb79e19dfb590 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -26,15 +27,37 @@ #define EIOINTC_REG_ISR 0x1800 #define EIOINTC_REG_ROUTE 0x1c00 +#define EXTIOI_VIRT_FEATURES 0x40000000 +#define EXTIOI_HAS_VIRT_EXTENSION BIT(0) +#define EXTIOI_HAS_ENABLE_OPTION BIT(1) +#define EXTIOI_HAS_INT_ENCODE BIT(2) +#define EXTIOI_HAS_CPU_ENCODE BIT(3) +#define EXTIOI_VIRT_CONFIG 0x40000004 +#define EXTIOI_ENABLE BIT(1) +#define EXTIOI_ENABLE_INT_ENCODE BIT(2) +#define EXTIOI_ENABLE_CPU_ENCODE BIT(3) + #define VEC_REG_COUNT 4 #define VEC_COUNT_PER_REG 64 #define VEC_COUNT (VEC_REG_COUNT * VEC_COUNT_PER_REG) #define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG) #define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG) #define EIOINTC_ALL_ENABLE 0xffffffff +#define EIOINTC_ALL_ENABLE_VEC_MASK(vector) (EIOINTC_ALL_ENABLE & ~BIT(vector & 0x1f)) +#define EIOINTC_REG_ENABLE_VEC(vector) (EIOINTC_REG_ENABLE + ((vector >> 5) << 2)) +#define EIOINTC_USE_CPU_ENCODE BIT(0) #define MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE) +/* + * Routing registers are 32bit, and there is 8-bit route setting for every + * interrupt vector. So one Route register contains four vectors routing + * information. + */ +#define EIOINTC_REG_ROUTE_VEC(vector) (EIOINTC_REG_ROUTE + (vector & ~0x03)) +#define EIOINTC_REG_ROUTE_VEC_SHIFT(vector) ((vector & 0x03) << 3) +#define EIOINTC_REG_ROUTE_VEC_MASK(vector) (0xff << EIOINTC_REG_ROUTE_VEC_SHIFT(vector)) + static int nr_pics; struct eiointc_priv { @@ -44,6 +67,7 @@ struct eiointc_priv { cpumask_t cpuspan_map; struct fwnode_handle *domain_handle; struct irq_domain *eiointc_domain; + int flags; }; static struct eiointc_priv *eiointc_priv[MAX_IO_PICS]; @@ -59,7 +83,10 @@ static void eiointc_enable(void) static int cpu_to_eio_node(int cpu) { - return cpu_logical_map(cpu) / CORES_PER_EIO_NODE; + if (!kvm_para_has_feature(KVM_FEATURE_VIRT_EXTIOI)) + return cpu_logical_map(cpu) / CORES_PER_EIO_NODE; + else + return cpu_logical_map(cpu) / CORES_PER_VEIO_NODE; } #ifdef CONFIG_SMP @@ -89,6 +116,17 @@ static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode, } } +static void veiointc_set_irq_route(unsigned int vector, unsigned int cpu) +{ + unsigned long reg = EIOINTC_REG_ROUTE_VEC(vector); + unsigned int data; + + data = iocsr_read32(reg); + data &= ~EIOINTC_REG_ROUTE_VEC_MASK(vector); + data |= cpu_logical_map(cpu) << EIOINTC_REG_ROUTE_VEC_SHIFT(vector); + iocsr_write32(data, reg); +} + static DEFINE_RAW_SPINLOCK(affinity_lock); static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force) @@ -107,18 +145,24 @@ static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *af } vector = d->hwirq; - regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2); - - /* Mask target vector */ - csr_any_send(regaddr, EIOINTC_ALL_ENABLE & (~BIT(vector & 0x1F)), - 0x0, priv->node * CORES_PER_EIO_NODE); - - /* Set route for target vector */ - eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map); - - /* Unmask target vector */ - csr_any_send(regaddr, EIOINTC_ALL_ENABLE, - 0x0, priv->node * CORES_PER_EIO_NODE); + regaddr = EIOINTC_REG_ENABLE_VEC(vector); + + if (priv->flags & EIOINTC_USE_CPU_ENCODE) { + iocsr_write32(EIOINTC_ALL_ENABLE_VEC_MASK(vector), regaddr); + veiointc_set_irq_route(vector, cpu); + iocsr_write32(EIOINTC_ALL_ENABLE, regaddr); + } else { + /* Mask target vector */ + csr_any_send(regaddr, EIOINTC_ALL_ENABLE_VEC_MASK(vector), + 0x0, priv->node * CORES_PER_EIO_NODE); + + /* Set route for target vector */ + eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map); + + /* Unmask target vector */ + csr_any_send(regaddr, EIOINTC_ALL_ENABLE, + 0x0, priv->node * CORES_PER_EIO_NODE); + } irq_data_update_effective_affinity(d, cpumask_of(cpu)); @@ -142,17 +186,23 @@ static int eiointc_index(int node) static int eiointc_router_init(unsigned int cpu) { - int i, bit; - uint32_t data; - uint32_t node = cpu_to_eio_node(cpu); - int index = eiointc_index(node); + int i, bit, cores, index, node; + unsigned int data; + + node = cpu_to_eio_node(cpu); + index = eiointc_index(node); if (index < 0) { pr_err("Error: invalid nodemap!\n"); - return -1; + return -EINVAL; } - if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) { + if (!(eiointc_priv[index]->flags & EIOINTC_USE_CPU_ENCODE)) + cores = CORES_PER_EIO_NODE; + else + cores = CORES_PER_VEIO_NODE; + + if ((cpu_logical_map(cpu) % cores) == 0) { eiointc_enable(); for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) { @@ -168,7 +218,9 @@ static int eiointc_router_init(unsigned int cpu) for (i = 0; i < eiointc_priv[0]->vec_count / 4; i++) { /* Route to Node-0 Core-0 */ - if (index == 0) + if (eiointc_priv[index]->flags & EIOINTC_USE_CPU_ENCODE) + bit = cpu_logical_map(0); + else if (index == 0) bit = BIT(cpu_logical_map(0)); else bit = (eiointc_priv[index]->node << 4) | 1; @@ -375,7 +427,7 @@ static int __init acpi_cascade_irqdomain_init(void) static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq, u64 node_map) { - int i; + int i, val; node_map = node_map ? node_map : -1ULL; for_each_possible_cpu(i) { @@ -395,6 +447,20 @@ static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq, return -ENOMEM; } + if (kvm_para_has_feature(KVM_FEATURE_VIRT_EXTIOI)) { + val = iocsr_read32(EXTIOI_VIRT_FEATURES); + /* + * With EXTIOI_ENABLE_CPU_ENCODE set + * interrupts can route to 256 vCPUs. + */ + if (val & EXTIOI_HAS_CPU_ENCODE) { + val = iocsr_read32(EXTIOI_VIRT_CONFIG); + val |= EXTIOI_ENABLE_CPU_ENCODE; + iocsr_write32(val, EXTIOI_VIRT_CONFIG); + priv->flags = EIOINTC_USE_CPU_ENCODE; + } + } + eiointc_priv[nr_pics++] = priv; eiointc_router_init(0); irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv); diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c index 1aef5c4d27c63..c0e1aafe468c3 100644 --- a/drivers/irqchip/irq-ls-scfg-msi.c +++ b/drivers/irqchip/irq-ls-scfg-msi.c @@ -418,7 +418,7 @@ static struct platform_driver ls_scfg_msi_driver = { .of_match_table = ls_scfg_msi_id, }, .probe = ls_scfg_msi_probe, - .remove_new = ls_scfg_msi_remove, + .remove = ls_scfg_msi_remove, }; module_platform_driver(ls_scfg_msi_driver); diff --git a/drivers/irqchip/irq-madera.c b/drivers/irqchip/irq-madera.c index acceb6e7fa95f..b32982c11515f 100644 --- a/drivers/irqchip/irq-madera.c +++ b/drivers/irqchip/irq-madera.c @@ -236,7 +236,7 @@ static void madera_irq_remove(struct platform_device *pdev) static struct platform_driver madera_irq_driver = { .probe = madera_irq_probe, - .remove_new = madera_irq_remove, + .remove = madera_irq_remove, .driver = { .name = "madera-irq", .pm = &madera_irq_pm_ops, diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c index 08b0cc862adf0..3888b75859814 100644 --- a/drivers/irqchip/irq-mvebu-pic.c +++ b/drivers/irqchip/irq-mvebu-pic.c @@ -71,7 +71,7 @@ static void mvebu_pic_print_chip(struct irq_data *d, struct seq_file *p) { struct mvebu_pic *pic = irq_data_get_irq_chip_data(d); - seq_printf(p, dev_name(&pic->pdev->dev)); + seq_puts(p, dev_name(&pic->pdev->dev)); } static const struct irq_chip mvebu_pic_chip = { @@ -183,7 +183,7 @@ MODULE_DEVICE_TABLE(of, mvebu_pic_of_match); static struct platform_driver mvebu_pic_driver = { .probe = mvebu_pic_probe, - .remove_new = mvebu_pic_remove, + .remove = mvebu_pic_remove, .driver = { .name = "mvebu-pic", .of_match_table = mvebu_pic_of_match, diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c index f8c70f2d100a1..065166ab5dbc0 100644 --- a/drivers/irqchip/irq-mvebu-sei.c +++ b/drivers/irqchip/irq-mvebu-sei.c @@ -192,7 +192,6 @@ static void mvebu_sei_domain_free(struct irq_domain *domain, unsigned int virq, } static const struct irq_domain_ops mvebu_sei_domain_ops = { - .select = msi_lib_irq_domain_select, .alloc = mvebu_sei_domain_alloc, .free = mvebu_sei_domain_free, }; @@ -306,6 +305,7 @@ static void mvebu_sei_cp_domain_free(struct irq_domain *domain, } static const struct irq_domain_ops mvebu_sei_cp_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = mvebu_sei_cp_domain_alloc, .free = mvebu_sei_cp_domain_free, }; diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c index 060eb000e9d35..bee01980b4630 100644 --- a/drivers/irqchip/irq-pruss-intc.c +++ b/drivers/irqchip/irq-pruss-intc.c @@ -648,7 +648,7 @@ static struct platform_driver pruss_intc_driver = { .suppress_bind_attrs = true, }, .probe = pruss_intc_probe, - .remove_new = pruss_intc_remove, + .remove = pruss_intc_remove, }; module_platform_driver(pruss_intc_driver); diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 9ad37237ba954..954419f2460d1 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -584,7 +584,7 @@ static SIMPLE_DEV_PM_OPS(intc_irqpin_pm_ops, intc_irqpin_suspend, NULL); static struct platform_driver intc_irqpin_device_driver = { .probe = intc_irqpin_probe, - .remove_new = intc_irqpin_remove, + .remove = intc_irqpin_remove, .driver = { .name = "renesas_intc_irqpin", .of_match_table = intc_irqpin_dt_ids, diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 76026e0b8e201..cbce8ffc7de4a 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -247,7 +247,7 @@ MODULE_DEVICE_TABLE(of, irqc_dt_ids); static struct platform_driver irqc_device_driver = { .probe = irqc_probe, - .remove_new = irqc_remove, + .remove = irqc_remove, .driver = { .name = "renesas_irqc", .of_match_table = irqc_dt_ids, diff --git a/drivers/irqchip/irq-renesas-rza1.c b/drivers/irqchip/irq-renesas-rza1.c index f05afe82db4d1..d4e6a68889ec1 100644 --- a/drivers/irqchip/irq-renesas-rza1.c +++ b/drivers/irqchip/irq-renesas-rza1.c @@ -259,7 +259,7 @@ MODULE_DEVICE_TABLE(of, rza1_irqc_dt_ids); static struct platform_driver rza1_irqc_device_driver = { .probe = rza1_irqc_probe, - .remove_new = rza1_irqc_remove, + .remove = rza1_irqc_remove, .driver = { .name = "renesas_rza1_irqc", .of_match_table = rza1_irqc_dt_ids, diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index b5dddb3c1568e..cc219f28d317f 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -154,7 +154,7 @@ MODULE_DEVICE_TABLE(of, ts4800_ic_of_match); static struct platform_driver ts4800_ic_driver = { .probe = ts4800_ic_probe, - .remove_new = ts4800_ic_remove, + .remove = ts4800_ic_remove, .driver = { .name = "ts4800-irqc", .of_match_table = ts4800_ic_of_match, diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c index ca471c6fee998..0abc8934c2ee0 100644 --- a/drivers/irqchip/irq-versatile-fpga.c +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -69,7 +69,7 @@ static void fpga_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); - seq_printf(p, irq_domain_get_of_node(f->domain)->name); + seq_puts(p, irq_domain_get_of_node(f->domain)->name); } static const struct irq_chip fpga_chip = { diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 6fb995778636a..8ecba7fb999e9 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -127,7 +127,7 @@ config STI_MBOX config TI_MESSAGE_MANAGER tristate "Texas Instruments Message Manager Driver" - depends on ARCH_KEYSTONE || ARCH_K3 + depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST default ARCH_K3 help An implementation of Message Manager slave driver for Keystone @@ -168,6 +168,7 @@ config MAILBOX_TEST config POLARFIRE_SOC_MAILBOX tristate "PolarFire SoC (MPFS) Mailbox" depends on HAS_IOMEM + depends on MFD_SYSCON depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST help This driver adds support for the PolarFire SoC (MPFS) mailbox controller. @@ -295,4 +296,14 @@ config QCOM_IPCC acts as an interrupt controller for receiving interrupts from clients. Say Y here if you want to build this driver. +config THEAD_TH1520_MBOX + tristate "T-head TH1520 Mailbox" + depends on ARCH_THEAD || COMPILE_TEST + help + Mailbox driver implementation for the Thead TH-1520 platform. Enables + two cores within the SoC to communicate and coordinate by passing + messages. Could be used to communicate between E910 core, on which the + kernel is running, and E902 core used for power management among other + things. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 3c3c27d54c13d..5f4f5b0ce2ccf 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o obj-$(CONFIG_QCOM_CPUCP_MBOX) += qcom-cpucp-mbox.o obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + +obj-$(CONFIG_THEAD_TH1520_MBOX) += mailbox-th1520.o diff --git a/drivers/mailbox/arm_mhuv2.c b/drivers/mailbox/arm_mhuv2.c index 0ec21dcdbde72..cff7c343ee082 100644 --- a/drivers/mailbox/arm_mhuv2.c +++ b/drivers/mailbox/arm_mhuv2.c @@ -500,7 +500,7 @@ static const struct mhuv2_protocol_ops mhuv2_data_transfer_ops = { static struct mbox_chan *get_irq_chan_comb(struct mhuv2 *mhu, u32 __iomem *reg) { struct mbox_chan *chans = mhu->mbox.chans; - int channel = 0, i, offset = 0, windows, protocol, ch_wn; + int channel = 0, i, j, offset = 0, windows, protocol, ch_wn; u32 stat; for (i = 0; i < MHUV2_CMB_INT_ST_REG_CNT; i++) { @@ -510,9 +510,9 @@ static struct mbox_chan *get_irq_chan_comb(struct mhuv2 *mhu, u32 __iomem *reg) ch_wn = i * MHUV2_STAT_BITS + __builtin_ctz(stat); - for (i = 0; i < mhu->length; i += 2) { - protocol = mhu->protocols[i]; - windows = mhu->protocols[i + 1]; + for (j = 0; j < mhu->length; j += 2) { + protocol = mhu->protocols[j]; + windows = mhu->protocols[j + 1]; if (ch_wn >= offset + windows) { if (protocol == DOORBELL) diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c index b1abc2a0c971a..41f79e51d9e5a 100644 --- a/drivers/mailbox/bcm-flexrm-mailbox.c +++ b/drivers/mailbox/bcm-flexrm-mailbox.c @@ -1675,7 +1675,7 @@ static struct platform_driver flexrm_mbox_driver = { .of_match_table = flexrm_mbox_of_match, }, .probe = flexrm_mbox_probe, - .remove_new = flexrm_mbox_remove, + .remove = flexrm_mbox_remove, }; module_platform_driver(flexrm_mbox_driver); diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index a873672a9082d..406bc41cba604 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -1618,7 +1618,7 @@ static void pdc_remove(struct platform_device *pdev) static struct platform_driver pdc_mbox_driver = { .probe = pdc_probe, - .remove_new = pdc_remove, + .remove = pdc_remove, .driver = { .name = "brcm-iproc-pdc-mbox", .of_match_table = pdc_mbox_of_match, diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index f815dab3be50c..6ef8338add0d6 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -782,7 +782,7 @@ static int imx_mu_init_generic(struct imx_mu_priv *priv) cp->chan = &priv->mbox_chans[i]; priv->mbox_chans[i].con_priv = cp; snprintf(cp->irq_desc, sizeof(cp->irq_desc), - "%s[%i-%i]", dev_name(priv->dev), cp->type, cp->idx); + "%s[%i-%u]", dev_name(priv->dev), cp->type, cp->idx); } priv->mbox.num_chans = IMX_MU_CHANS; @@ -819,7 +819,7 @@ static int imx_mu_init_specific(struct imx_mu_priv *priv) cp->chan = &priv->mbox_chans[i]; priv->mbox_chans[i].con_priv = cp; snprintf(cp->irq_desc, sizeof(cp->irq_desc), - "%s[%i-%i]", dev_name(priv->dev), cp->type, cp->idx); + "%s[%i-%u]", dev_name(priv->dev), cp->type, cp->idx); } priv->mbox.num_chans = num_chans; @@ -1120,7 +1120,7 @@ static const struct dev_pm_ops imx_mu_pm_ops = { static struct platform_driver imx_mu_driver = { .probe = imx_mu_probe, - .remove_new = imx_mu_remove, + .remove = imx_mu_remove, .driver = { .name = "imx_mu", .of_match_table = imx_mu_dt_ids, diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index 20ee283a04cc6..4df546e3b7eae 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -13,12 +13,15 @@ #include #include #include +#include #include +#include #include #include #include #include +#define MESSAGE_INT_OFFSET 0x18cu #define SERVICES_CR_OFFSET 0x50u #define SERVICES_SR_OFFSET 0x54u #define MAILBOX_REG_OFFSET 0x800u @@ -68,6 +71,7 @@ struct mpfs_mbox { void __iomem *int_reg; struct mbox_chan chans[1]; struct mpfs_mss_response *response; + struct regmap *sysreg_scb, *control_scb; u16 resp_offset; }; @@ -75,7 +79,10 @@ static bool mpfs_mbox_busy(struct mpfs_mbox *mbox) { u32 status; - status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + if (mbox->control_scb) + regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &status); + else + status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); return status & SCB_STATUS_BUSY_MASK; } @@ -95,7 +102,11 @@ static bool mpfs_mbox_last_tx_done(struct mbox_chan *chan) * Failed services are intended to generated interrupts, but in reality * this does not happen, so the status must be checked here. */ - val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + if (mbox->control_scb) + regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &val); + else + val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + response->resp_status = (val & SCB_STATUS_MASK) >> SCB_STATUS_POS; return true; @@ -143,7 +154,12 @@ static int mpfs_mbox_send_data(struct mbox_chan *chan, void *data) tx_trigger = (opt_sel << SCB_CTRL_POS) & SCB_CTRL_MASK; tx_trigger |= SCB_CTRL_REQ_MASK | SCB_STATUS_NOTIFY_MASK; - writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET); + + if (mbox->control_scb) + regmap_write(mbox->control_scb, SERVICES_CR_OFFSET, tx_trigger); + else + writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET); + return 0; } @@ -185,7 +201,10 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) struct mbox_chan *chan = data; struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; - writel_relaxed(0, mbox->int_reg); + if (mbox->control_scb) + regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0); + else + writel_relaxed(0, mbox->int_reg); mpfs_mbox_rx_data(chan); @@ -221,28 +240,62 @@ static const struct mbox_chan_ops mpfs_mbox_ops = { .last_tx_done = mpfs_mbox_last_tx_done, }; -static int mpfs_mbox_probe(struct platform_device *pdev) +static inline int mpfs_mbox_syscon_probe(struct mpfs_mbox *mbox, struct platform_device *pdev) { - struct mpfs_mbox *mbox; - struct resource *regs; - int ret; + mbox->control_scb = syscon_regmap_lookup_by_compatible("microchip,mpfs-control-scb"); + if (IS_ERR(mbox->control_scb)) + return PTR_ERR(mbox->control_scb); - mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); - if (!mbox) - return -ENOMEM; + mbox->sysreg_scb = syscon_regmap_lookup_by_compatible("microchip,mpfs-sysreg-scb"); + if (IS_ERR(mbox->sysreg_scb)) + return PTR_ERR(mbox->sysreg_scb); + + mbox->mbox_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mbox->ctrl_base)) + return PTR_ERR(mbox->mbox_base); + + return 0; +} + +static inline int mpfs_mbox_old_format_probe(struct mpfs_mbox *mbox, struct platform_device *pdev) +{ + dev_warn(&pdev->dev, "falling back to old devicetree format"); - mbox->ctrl_base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); + mbox->ctrl_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mbox->ctrl_base)) return PTR_ERR(mbox->ctrl_base); - mbox->int_reg = devm_platform_get_and_ioremap_resource(pdev, 1, ®s); + mbox->int_reg = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(mbox->int_reg)) return PTR_ERR(mbox->int_reg); - mbox->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 2, ®s); + mbox->mbox_base = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(mbox->mbox_base)) // account for the old dt-binding w/ 2 regs mbox->mbox_base = mbox->ctrl_base + MAILBOX_REG_OFFSET; + return 0; +} + +static int mpfs_mbox_probe(struct platform_device *pdev) +{ + struct mpfs_mbox *mbox; + int ret; + + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + ret = mpfs_mbox_syscon_probe(mbox, pdev); + if (ret) { + /* + * set this to null, so it can be used as the decision for to + * regmap or not to regmap + */ + mbox->control_scb = NULL; + ret = mpfs_mbox_old_format_probe(mbox, pdev); + if (ret) + return ret; + } mbox->irq = platform_get_irq(pdev, 0); if (mbox->irq < 0) return mbox->irq; diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 3386b4e72551c..c9dd8c42c0cdf 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -441,8 +441,8 @@ static struct platform_driver mbox_test_driver = { .name = "mailbox_test", .of_match_table = mbox_test_match, }, - .probe = mbox_test_probe, - .remove_new = mbox_test_remove, + .probe = mbox_test_probe, + .remove = mbox_test_remove, }; module_platform_driver(mbox_test_driver); diff --git a/drivers/mailbox/mailbox-th1520.c b/drivers/mailbox/mailbox-th1520.c new file mode 100644 index 0000000000000..4e84640ac3b87 --- /dev/null +++ b/drivers/mailbox/mailbox-th1520.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Alibaba Group Holding Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Status Register */ +#define TH_1520_MBOX_STA 0x0 +#define TH_1520_MBOX_CLR 0x4 +#define TH_1520_MBOX_MASK 0xc + +/* Transmit/receive data register: + * INFO0 ~ INFO6 + */ +#define TH_1520_MBOX_INFO_NUM 8 +#define TH_1520_MBOX_DATA_INFO_NUM 7 +#define TH_1520_MBOX_INFO0 0x14 +/* Transmit ack register: INFO7 */ +#define TH_1520_MBOX_INFO7 0x30 + +/* Generate remote icu IRQ Register */ +#define TH_1520_MBOX_GEN 0x10 +#define TH_1520_MBOX_GEN_RX_DATA BIT(6) +#define TH_1520_MBOX_GEN_TX_ACK BIT(7) + +#define TH_1520_MBOX_CHAN_RES_SIZE 0x1000 +#define TH_1520_MBOX_CHANS 4 +#define TH_1520_MBOX_CHAN_NAME_SIZE 20 + +#define TH_1520_MBOX_ACK_MAGIC 0xdeadbeaf + +#ifdef CONFIG_PM_SLEEP +/* store MBOX context across system-wide suspend/resume transitions */ +struct th1520_mbox_context { + u32 intr_mask[TH_1520_MBOX_CHANS - 1]; +}; +#endif + +enum th1520_mbox_icu_cpu_id { + TH_1520_MBOX_ICU_KERNEL_CPU0, /* 910T */ + TH_1520_MBOX_ICU_CPU1, /* 902 */ + TH_1520_MBOX_ICU_CPU2, /* 906 */ + TH_1520_MBOX_ICU_CPU3, /* 910R */ +}; + +struct th1520_mbox_con_priv { + enum th1520_mbox_icu_cpu_id idx; + void __iomem *comm_local_base; + void __iomem *comm_remote_base; + char irq_desc[TH_1520_MBOX_CHAN_NAME_SIZE]; + struct mbox_chan *chan; +}; + +struct th1520_mbox_priv { + struct device *dev; + void __iomem *local_icu[TH_1520_MBOX_CHANS]; + void __iomem *remote_icu[TH_1520_MBOX_CHANS - 1]; + void __iomem *cur_cpu_ch_base; + spinlock_t mbox_lock; /* control register lock */ + + struct mbox_controller mbox; + struct mbox_chan mbox_chans[TH_1520_MBOX_CHANS]; + struct clk_bulk_data clocks[TH_1520_MBOX_CHANS]; + struct th1520_mbox_con_priv con_priv[TH_1520_MBOX_CHANS]; + int irq; +#ifdef CONFIG_PM_SLEEP + struct th1520_mbox_context *ctx; +#endif +}; + +static struct th1520_mbox_priv * +to_th1520_mbox_priv(struct mbox_controller *mbox) +{ + return container_of(mbox, struct th1520_mbox_priv, mbox); +} + +static void th1520_mbox_write(struct th1520_mbox_priv *priv, u32 val, u32 offs) +{ + iowrite32(val, priv->cur_cpu_ch_base + offs); +} + +static u32 th1520_mbox_read(struct th1520_mbox_priv *priv, u32 offs) +{ + return ioread32(priv->cur_cpu_ch_base + offs); +} + +static u32 th1520_mbox_rmw(struct th1520_mbox_priv *priv, u32 off, u32 set, + u32 clr) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->mbox_lock, flags); + val = th1520_mbox_read(priv, off); + val &= ~clr; + val |= set; + th1520_mbox_write(priv, val, off); + spin_unlock_irqrestore(&priv->mbox_lock, flags); + + return val; +} + +static void th1520_mbox_chan_write(struct th1520_mbox_con_priv *cp, u32 val, + u32 offs, bool is_remote) +{ + if (is_remote) + iowrite32(val, cp->comm_remote_base + offs); + else + iowrite32(val, cp->comm_local_base + offs); +} + +static u32 th1520_mbox_chan_read(struct th1520_mbox_con_priv *cp, u32 offs, + bool is_remote) +{ + if (is_remote) + return ioread32(cp->comm_remote_base + offs); + else + return ioread32(cp->comm_local_base + offs); +} + +static void th1520_mbox_chan_rmw(struct th1520_mbox_con_priv *cp, u32 off, + u32 set, u32 clr, bool is_remote) +{ + struct th1520_mbox_priv *priv = to_th1520_mbox_priv(cp->chan->mbox); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->mbox_lock, flags); + val = th1520_mbox_chan_read(cp, off, is_remote); + val &= ~clr; + val |= set; + th1520_mbox_chan_write(cp, val, off, is_remote); + spin_unlock_irqrestore(&priv->mbox_lock, flags); +} + +static void th1520_mbox_chan_rd_data(struct th1520_mbox_con_priv *cp, + void *data, bool is_remote) +{ + u32 off = TH_1520_MBOX_INFO0; + u32 *arg = data; + u32 i; + + /* read info0 ~ info6, totally 28 bytes + * requires data memory size is 28 bytes + */ + for (i = 0; i < TH_1520_MBOX_DATA_INFO_NUM; i++) { + *arg = th1520_mbox_chan_read(cp, off, is_remote); + off += 4; + arg++; + } +} + +static void th1520_mbox_chan_wr_data(struct th1520_mbox_con_priv *cp, + void *data, bool is_remote) +{ + u32 off = TH_1520_MBOX_INFO0; + u32 *arg = data; + u32 i; + + /* write info0 ~ info6, totally 28 bytes + * requires data memory is 28 bytes valid data + */ + for (i = 0; i < TH_1520_MBOX_DATA_INFO_NUM; i++) { + th1520_mbox_chan_write(cp, *arg, off, is_remote); + off += 4; + arg++; + } +} + +static void th1520_mbox_chan_wr_ack(struct th1520_mbox_con_priv *cp, void *data, + bool is_remote) +{ + u32 off = TH_1520_MBOX_INFO7; + u32 *arg = data; + + th1520_mbox_chan_write(cp, *arg, off, is_remote); +} + +static int th1520_mbox_chan_id_to_mapbit(struct th1520_mbox_con_priv *cp) +{ + int mapbit = 0; + int i; + + for (i = 0; i < TH_1520_MBOX_CHANS; i++) { + if (i == cp->idx) + return mapbit; + + if (i != TH_1520_MBOX_ICU_KERNEL_CPU0) + mapbit++; + } + + if (i == TH_1520_MBOX_CHANS) + dev_err(cp->chan->mbox->dev, "convert to mapbit failed\n"); + + return 0; +} + +static irqreturn_t th1520_mbox_isr(int irq, void *p) +{ + struct mbox_chan *chan = p; + struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox); + struct th1520_mbox_con_priv *cp = chan->con_priv; + int mapbit = th1520_mbox_chan_id_to_mapbit(cp); + u32 sta, dat[TH_1520_MBOX_DATA_INFO_NUM]; + u32 ack_magic = TH_1520_MBOX_ACK_MAGIC; + u32 info0_data, info7_data; + + sta = th1520_mbox_read(priv, TH_1520_MBOX_STA); + if (!(sta & BIT(mapbit))) + return IRQ_NONE; + + /* clear chan irq bit in STA register */ + th1520_mbox_rmw(priv, TH_1520_MBOX_CLR, BIT(mapbit), 0); + + /* info0 is the protocol word, should not be zero! */ + info0_data = th1520_mbox_chan_read(cp, TH_1520_MBOX_INFO0, false); + if (info0_data) { + /* read info0~info6 data */ + th1520_mbox_chan_rd_data(cp, dat, false); + + /* clear local info0 */ + th1520_mbox_chan_write(cp, 0x0, TH_1520_MBOX_INFO0, false); + + /* notify remote cpu */ + th1520_mbox_chan_wr_ack(cp, &ack_magic, true); + /* CPU1 902/906 use polling mode to monitor info7 */ + if (cp->idx != TH_1520_MBOX_ICU_CPU1 && + cp->idx != TH_1520_MBOX_ICU_CPU2) + th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, + TH_1520_MBOX_GEN_TX_ACK, 0, true); + + /* transfer the data to client */ + mbox_chan_received_data(chan, (void *)dat); + } + + /* info7 magic value mean the real ack signal, not generate bit7 */ + info7_data = th1520_mbox_chan_read(cp, TH_1520_MBOX_INFO7, false); + if (info7_data == TH_1520_MBOX_ACK_MAGIC) { + /* clear local info7 */ + th1520_mbox_chan_write(cp, 0x0, TH_1520_MBOX_INFO7, false); + + /* notify framework the last TX has completed */ + mbox_chan_txdone(chan, 0); + } + + if (!info0_data && !info7_data) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int th1520_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct th1520_mbox_con_priv *cp = chan->con_priv; + + th1520_mbox_chan_wr_data(cp, data, true); + th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, TH_1520_MBOX_GEN_RX_DATA, 0, + true); + return 0; +} + +static int th1520_mbox_startup(struct mbox_chan *chan) +{ + struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox); + struct th1520_mbox_con_priv *cp = chan->con_priv; + u32 data[8] = {}; + int mask_bit; + int ret; + + /* clear local and remote generate and info0~info7 */ + th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, 0x0, 0xff, true); + th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, 0x0, 0xff, false); + th1520_mbox_chan_wr_ack(cp, &data[7], true); + th1520_mbox_chan_wr_ack(cp, &data[7], false); + th1520_mbox_chan_wr_data(cp, &data[0], true); + th1520_mbox_chan_wr_data(cp, &data[0], false); + + /* enable the chan mask */ + mask_bit = th1520_mbox_chan_id_to_mapbit(cp); + th1520_mbox_rmw(priv, TH_1520_MBOX_MASK, BIT(mask_bit), 0); + + /* + * Mixing devm_ managed resources with manual IRQ handling is generally + * discouraged due to potential complexities with resource management, + * especially when dealing with shared interrupts. However, in this case, + * the approach is safe and effective because: + * + * 1. Each mailbox channel requests its IRQ within the .startup() callback + * and frees it within the .shutdown() callback. + * 2. During device unbinding, the devm_ managed mailbox controller first + * iterates through all channels, ensuring that their IRQs are freed before + * any other devm_ resources are released. + * + * This ordering guarantees that no interrupts can be triggered from the device + * while it is being unbound, preventing race conditions and ensuring system + * stability. + */ + ret = request_irq(priv->irq, th1520_mbox_isr, + IRQF_SHARED | IRQF_NO_SUSPEND, cp->irq_desc, chan); + if (ret) { + dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq); + return ret; + } + + return 0; +} + +static void th1520_mbox_shutdown(struct mbox_chan *chan) +{ + struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox); + struct th1520_mbox_con_priv *cp = chan->con_priv; + int mask_bit; + + free_irq(priv->irq, chan); + + /* clear the chan mask */ + mask_bit = th1520_mbox_chan_id_to_mapbit(cp); + th1520_mbox_rmw(priv, TH_1520_MBOX_MASK, 0, BIT(mask_bit)); +} + +static const struct mbox_chan_ops th1520_mbox_ops = { + .send_data = th1520_mbox_send_data, + .startup = th1520_mbox_startup, + .shutdown = th1520_mbox_shutdown, +}; + +static int th1520_mbox_init_generic(struct th1520_mbox_priv *priv) +{ +#ifdef CONFIG_PM_SLEEP + priv->ctx = devm_kzalloc(priv->dev, sizeof(*priv->ctx), GFP_KERNEL); + if (!priv->ctx) + return -ENOMEM; +#endif + /* Set default configuration */ + th1520_mbox_write(priv, 0xff, TH_1520_MBOX_CLR); + th1520_mbox_write(priv, 0x0, TH_1520_MBOX_MASK); + return 0; +} + +static struct mbox_chan *th1520_mbox_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + u32 chan; + + if (sp->args_count != 1) { + dev_err(mbox->dev, "Invalid argument count %d\n", + sp->args_count); + return ERR_PTR(-EINVAL); + } + + chan = sp->args[0]; /* comm remote channel */ + + if (chan >= mbox->num_chans) { + dev_err(mbox->dev, "Not supported channel number: %d\n", chan); + return ERR_PTR(-EINVAL); + } + + if (chan == TH_1520_MBOX_ICU_KERNEL_CPU0) { + dev_err(mbox->dev, "Cannot communicate with yourself\n"); + return ERR_PTR(-EINVAL); + } + + return &mbox->chans[chan]; +} + +static void __iomem *th1520_map_mmio(struct platform_device *pdev, + char *res_name, size_t offset) +{ + void __iomem *mapped; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); + + if (!res) { + dev_err(&pdev->dev, "Failed to get resource: %s\n", res_name); + return ERR_PTR(-EINVAL); + } + + mapped = devm_ioremap(&pdev->dev, res->start + offset, + resource_size(res) - offset); + if (IS_ERR(mapped)) + dev_err(&pdev->dev, "Failed to map resource: %s\n", res_name); + + return mapped; +} + +static void th1520_disable_clk(void *data) +{ + struct th1520_mbox_priv *priv = data; + + clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks); +} + +static int th1520_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct th1520_mbox_priv *priv; + unsigned int remote_idx = 0; + unsigned int i; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + + priv->clocks[0].id = "clk-local"; + priv->clocks[1].id = "clk-remote-icu0"; + priv->clocks[2].id = "clk-remote-icu1"; + priv->clocks[3].id = "clk-remote-icu2"; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(priv->clocks), + priv->clocks); + if (ret) { + dev_err(dev, "Failed to get clocks\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks); + if (ret) { + dev_err(dev, "Failed to enable clocks\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, th1520_disable_clk, priv); + if (ret) { + clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks); + return ret; + } + + /* + * The address mappings in the device tree align precisely with those + * outlined in the manual. However, register offsets within these + * mapped regions are irregular, particularly for remote-icu0. + * Consequently, th1520_map_mmio() requires an additional parameter to + * handle this quirk. + */ + priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0] = + th1520_map_mmio(pdev, "local", 0x0); + if (IS_ERR(priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0])) + return PTR_ERR(priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0]); + + priv->remote_icu[0] = th1520_map_mmio(pdev, "remote-icu0", 0x4000); + if (IS_ERR(priv->remote_icu[0])) + return PTR_ERR(priv->remote_icu[0]); + + priv->remote_icu[1] = th1520_map_mmio(pdev, "remote-icu1", 0x0); + if (IS_ERR(priv->remote_icu[1])) + return PTR_ERR(priv->remote_icu[1]); + + priv->remote_icu[2] = th1520_map_mmio(pdev, "remote-icu2", 0x0); + if (IS_ERR(priv->remote_icu[2])) + return PTR_ERR(priv->remote_icu[2]); + + priv->local_icu[TH_1520_MBOX_ICU_CPU1] = + priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0] + + TH_1520_MBOX_CHAN_RES_SIZE; + priv->local_icu[TH_1520_MBOX_ICU_CPU2] = + priv->local_icu[TH_1520_MBOX_ICU_CPU1] + + TH_1520_MBOX_CHAN_RES_SIZE; + priv->local_icu[TH_1520_MBOX_ICU_CPU3] = + priv->local_icu[TH_1520_MBOX_ICU_CPU2] + + TH_1520_MBOX_CHAN_RES_SIZE; + + priv->cur_cpu_ch_base = priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0]; + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) + return priv->irq; + + /* init the chans */ + for (i = 0; i < TH_1520_MBOX_CHANS; i++) { + struct th1520_mbox_con_priv *cp = &priv->con_priv[i]; + + cp->idx = i; + cp->chan = &priv->mbox_chans[i]; + priv->mbox_chans[i].con_priv = cp; + snprintf(cp->irq_desc, sizeof(cp->irq_desc), + "th1520_mbox_chan[%i]", cp->idx); + + cp->comm_local_base = priv->local_icu[i]; + if (i != TH_1520_MBOX_ICU_KERNEL_CPU0) { + cp->comm_remote_base = priv->remote_icu[remote_idx]; + remote_idx++; + } + } + + spin_lock_init(&priv->mbox_lock); + + priv->mbox.dev = dev; + priv->mbox.ops = &th1520_mbox_ops; + priv->mbox.chans = priv->mbox_chans; + priv->mbox.num_chans = TH_1520_MBOX_CHANS; + priv->mbox.of_xlate = th1520_mbox_xlate; + priv->mbox.txdone_irq = true; + + platform_set_drvdata(pdev, priv); + + ret = th1520_mbox_init_generic(priv); + if (ret) { + dev_err(dev, "Failed to init mailbox context\n"); + return ret; + } + + return devm_mbox_controller_register(dev, &priv->mbox); +} + +static const struct of_device_id th1520_mbox_dt_ids[] = { + { .compatible = "thead,th1520-mbox" }, + {} +}; +MODULE_DEVICE_TABLE(of, th1520_mbox_dt_ids); + +#ifdef CONFIG_PM_SLEEP +static int __maybe_unused th1520_mbox_suspend_noirq(struct device *dev) +{ + struct th1520_mbox_priv *priv = dev_get_drvdata(dev); + struct th1520_mbox_context *ctx = priv->ctx; + u32 i; + /* + * ONLY interrupt mask bit should be stored and restores. + * INFO data all assumed to be lost. + */ + for (i = 0; i < TH_1520_MBOX_CHANS; i++) { + ctx->intr_mask[i] = + ioread32(priv->local_icu[i] + TH_1520_MBOX_MASK); + } + return 0; +} + +static int __maybe_unused th1520_mbox_resume_noirq(struct device *dev) +{ + struct th1520_mbox_priv *priv = dev_get_drvdata(dev); + struct th1520_mbox_context *ctx = priv->ctx; + u32 i; + + for (i = 0; i < TH_1520_MBOX_CHANS; i++) { + iowrite32(ctx->intr_mask[i], + priv->local_icu[i] + TH_1520_MBOX_MASK); + } + + return 0; +} +#endif + +static int __maybe_unused th1520_mbox_runtime_suspend(struct device *dev) +{ + struct th1520_mbox_priv *priv = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks); + + return 0; +} + +static int __maybe_unused th1520_mbox_runtime_resume(struct device *dev) +{ + struct th1520_mbox_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks); + if (ret) + dev_err(dev, "Failed to enable clocks in runtime resume\n"); + + return ret; +} + +static const struct dev_pm_ops th1520_mbox_pm_ops = { +#ifdef CONFIG_PM_SLEEP + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(th1520_mbox_suspend_noirq, + th1520_mbox_resume_noirq) +#endif + SET_RUNTIME_PM_OPS(th1520_mbox_runtime_suspend, + th1520_mbox_runtime_resume, NULL) +}; + +static struct platform_driver th1520_mbox_driver = { + .probe = th1520_mbox_probe, + .driver = { + .name = "th1520-mbox", + .of_match_table = th1520_mbox_dt_ids, + .pm = &th1520_mbox_pm_ops, + }, +}; +module_platform_driver(th1520_mbox_driver); + +MODULE_DESCRIPTION("Thead TH-1520 mailbox IPC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 4bff73532085b..d186865b8dce6 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -397,7 +397,7 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) task = kzalloc(sizeof(*task), GFP_ATOMIC); if (!task) { - pm_runtime_put_autosuspend(cmdq->mbox.dev); + __pm_runtime_put_autosuspend(cmdq->mbox.dev); return -ENOMEM; } @@ -447,7 +447,7 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) list_move_tail(&task->list_entry, &thread->task_busy_list); pm_runtime_mark_last_busy(cmdq->mbox.dev); - pm_runtime_put_autosuspend(cmdq->mbox.dev); + __pm_runtime_put_autosuspend(cmdq->mbox.dev); return 0; } @@ -495,7 +495,7 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan) spin_unlock_irqrestore(&thread->chan->lock, flags); pm_runtime_mark_last_busy(cmdq->mbox.dev); - pm_runtime_put_autosuspend(cmdq->mbox.dev); + __pm_runtime_put_autosuspend(cmdq->mbox.dev); } static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) @@ -535,7 +535,7 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) out: spin_unlock_irqrestore(&thread->chan->lock, flags); pm_runtime_mark_last_busy(cmdq->mbox.dev); - pm_runtime_put_autosuspend(cmdq->mbox.dev); + __pm_runtime_put_autosuspend(cmdq->mbox.dev); return 0; @@ -550,7 +550,7 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) return -EFAULT; } pm_runtime_mark_last_busy(cmdq->mbox.dev); - pm_runtime_put_autosuspend(cmdq->mbox.dev); + __pm_runtime_put_autosuspend(cmdq->mbox.dev); return 0; } @@ -584,7 +584,7 @@ static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq) struct clk_bulk_data *clks; cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num, - sizeof(cmdq->clocks), GFP_KERNEL); + sizeof(*cmdq->clocks), GFP_KERNEL); if (!cmdq->clocks) return -ENOMEM; @@ -796,7 +796,7 @@ MODULE_DEVICE_TABLE(of, cmdq_of_ids); static struct platform_driver cmdq_drv = { .probe = cmdq_probe, - .remove_new = cmdq_remove, + .remove = cmdq_remove, .driver = { .name = "mtk_cmdq", .pm = &cmdq_pm_ops, diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 6797770474a55..680243751d625 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 94885e411085a..82102a4c5d688 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -269,6 +269,35 @@ static bool pcc_mbox_cmd_complete_check(struct pcc_chan_info *pchan) return !!val; } +static void check_and_ack(struct pcc_chan_info *pchan, struct mbox_chan *chan) +{ + struct acpi_pcct_ext_pcc_shared_memory pcc_hdr; + + if (pchan->type != ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) + return; + /* If the memory region has not been mapped, we cannot + * determine if we need to send the message, but we still + * need to set the cmd_update flag before returning. + */ + if (pchan->chan.shmem == NULL) { + pcc_chan_reg_read_modify_write(&pchan->cmd_update); + return; + } + memcpy_fromio(&pcc_hdr, pchan->chan.shmem, + sizeof(struct acpi_pcct_ext_pcc_shared_memory)); + /* + * The PCC slave subspace channel needs to set the command complete bit + * after processing message. If the PCC_ACK_FLAG is set, it should also + * ring the doorbell. + * + * The PCC master subspace channel clears chan_in_use to free channel. + */ + if (le32_to_cpup(&pcc_hdr.flags) & PCC_ACK_FLAG_MASK) + pcc_send_data(chan, NULL); + else + pcc_chan_reg_read_modify_write(&pchan->cmd_update); +} + /** * pcc_mbox_irq - PCC mailbox interrupt handler * @irq: interrupt number @@ -306,14 +335,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p) mbox_chan_received_data(chan, NULL); - /* - * The PCC slave subspace channel needs to set the command complete bit - * and ring doorbell after processing message. - * - * The PCC master subspace channel clears chan_in_use to free channel. - */ - if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) - pcc_send_data(chan, NULL); + check_and_ack(pchan, chan); pchan->chan_in_use = false; return IRQ_HANDLED; @@ -365,14 +387,37 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan) { struct mbox_chan *chan = pchan->mchan; + struct pcc_chan_info *pchan_info; + struct pcc_mbox_chan *pcc_mbox_chan; if (!chan || !chan->cl) return; + pchan_info = chan->con_priv; + pcc_mbox_chan = &pchan_info->chan; + if (pcc_mbox_chan->shmem) { + iounmap(pcc_mbox_chan->shmem); + pcc_mbox_chan->shmem = NULL; + } mbox_free_channel(chan); } EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); +int pcc_mbox_ioremap(struct mbox_chan *chan) +{ + struct pcc_chan_info *pchan_info; + struct pcc_mbox_chan *pcc_mbox_chan; + + if (!chan || !chan->cl) + return -1; + pchan_info = chan->con_priv; + pcc_mbox_chan = &pchan_info->chan; + pcc_mbox_chan->shmem = ioremap(pcc_mbox_chan->shmem_base_addr, + pcc_mbox_chan->shmem_size); + return 0; +} +EXPORT_SYMBOL_GPL(pcc_mbox_ioremap); + /** * pcc_send_data - Called from Mailbox Controller code. Used * here only to ring the channel doorbell. The PCC client diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index 7d91e7c016ba7..f0d1fc0fb9ffc 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -167,7 +167,7 @@ MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match); static struct platform_driver qcom_apcs_ipc_driver = { .probe = qcom_apcs_ipc_probe, - .remove_new = qcom_apcs_ipc_remove, + .remove = qcom_apcs_ipc_remove, .driver = { .name = "qcom_apcs_ipc", .of_match_table = qcom_apcs_ipc_of_match, diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c index d537cc9c4d4be..14c7907c66326 100644 --- a/drivers/mailbox/qcom-ipcc.c +++ b/drivers/mailbox/qcom-ipcc.c @@ -346,7 +346,7 @@ static const struct dev_pm_ops qcom_ipcc_dev_pm_ops = { static struct platform_driver qcom_ipcc_driver = { .probe = qcom_ipcc_probe, - .remove_new = qcom_ipcc_remove, + .remove = qcom_ipcc_remove, .driver = { .name = "qcom-ipcc", .of_match_table = qcom_ipcc_of_match, diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index 1442f275782bd..4f63f1a14ca65 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -379,7 +379,7 @@ static struct platform_driver stm32_ipcc_driver = { .of_match_table = stm32_ipcc_of_match, }, .probe = stm32_ipcc_probe, - .remove_new = stm32_ipcc_remove, + .remove = stm32_ipcc_remove, }; module_platform_driver(stm32_ipcc_driver); diff --git a/drivers/mailbox/sun6i-msgbox.c b/drivers/mailbox/sun6i-msgbox.c index 3dcc54dc83b2d..6ba6920f4645e 100644 --- a/drivers/mailbox/sun6i-msgbox.c +++ b/drivers/mailbox/sun6i-msgbox.c @@ -307,8 +307,8 @@ static struct platform_driver sun6i_msgbox_driver = { .name = "sun6i-msgbox", .of_match_table = sun6i_msgbox_of_match, }, - .probe = sun6i_msgbox_probe, - .remove_new = sun6i_msgbox_remove, + .probe = sun6i_msgbox_probe, + .remove = sun6i_msgbox_remove, }; module_platform_driver(sun6i_msgbox_driver); diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index 19ef56cbcfd39..8d5e2d7dc03b2 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -951,7 +951,7 @@ static struct platform_driver tegra_hsp_driver = { .pm = &tegra_hsp_pm_ops, }, .probe = tegra_hsp_probe, - .remove_new = tegra_hsp_remove, + .remove = tegra_hsp_remove, }; static int __init tegra_hsp_init(void) diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index 9d2d4ff6cda40..8eb8df8d95a4c 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -920,7 +920,7 @@ static struct platform_driver ti_msgmgr_driver = { .probe = ti_msgmgr_probe, .driver = { .name = "ti-msgmgr", - .of_match_table = of_match_ptr(ti_msgmgr_of_match), + .of_match_table = ti_msgmgr_of_match, .pm = &ti_msgmgr_pm_ops, }, }; diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index 521d08b9ab47e..aa5249da59b2f 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -940,10 +940,10 @@ static int zynqmp_ipi_probe(struct platform_device *pdev) pdata->num_mboxes = num_mboxes; mbox = pdata->ipi_mboxes; - mbox->setup_ipi_fn = ipi_fn; - for_each_available_child_of_node(np, nc) { mbox->pdata = pdata; + mbox->setup_ipi_fn = ipi_fn; + ret = zynqmp_ipi_mbox_probe(mbox, nc); if (ret) { of_node_put(nc); @@ -1015,7 +1015,7 @@ MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match); static struct platform_driver zynqmp_ipi_driver = { .probe = zynqmp_ipi_probe, - .remove_new = zynqmp_ipi_remove, + .remove = zynqmp_ipi_remove, .driver = { .name = "zynqmp-ipi", .of_match_table = of_match_ptr(zynqmp_ipi_of_match), diff --git a/drivers/md/bcache/Kconfig b/drivers/md/bcache/Kconfig index b2d10063d35fb..d4697e79d5a39 100644 --- a/drivers/md/bcache/Kconfig +++ b/drivers/md/bcache/Kconfig @@ -5,6 +5,7 @@ config BCACHE select BLOCK_HOLDER_DEPRECATED if SYSFS select CRC64 select CLOSURES + select MIN_HEAP help Allows a block device to be used as cache for other devices; uses a btree for indexing and the layout is optimized for SSDs. diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index da50f6661bae4..8998e61efa406 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -189,23 +189,16 @@ static inline bool new_bucket_min_cmp(const void *l, const void *r, void *args) return new_bucket_prio(ca, *lhs) < new_bucket_prio(ca, *rhs); } -static inline void new_bucket_swap(void *l, void *r, void __always_unused *args) -{ - struct bucket **lhs = l, **rhs = r; - - swap(*lhs, *rhs); -} - static void invalidate_buckets_lru(struct cache *ca) { struct bucket *b; const struct min_heap_callbacks bucket_max_cmp_callback = { .less = new_bucket_max_cmp, - .swp = new_bucket_swap, + .swp = NULL, }; const struct min_heap_callbacks bucket_min_cmp_callback = { .less = new_bucket_min_cmp, - .swp = new_bucket_swap, + .swp = NULL, }; ca->heap.nr = 0; diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c index bd97d86268874..68258a16e125c 100644 --- a/drivers/md/bcache/bset.c +++ b/drivers/md/bcache/bset.c @@ -1093,14 +1093,6 @@ static inline bool new_btree_iter_cmp(const void *l, const void *r, void __alway return bkey_cmp(_l->k, _r->k) <= 0; } -static inline void new_btree_iter_swap(void *iter1, void *iter2, void __always_unused *args) -{ - struct btree_iter_set *_iter1 = iter1; - struct btree_iter_set *_iter2 = iter2; - - swap(*_iter1, *_iter2); -} - static inline bool btree_iter_end(struct btree_iter *iter) { return !iter->heap.nr; @@ -1111,7 +1103,7 @@ void bch_btree_iter_push(struct btree_iter *iter, struct bkey *k, { const struct min_heap_callbacks callbacks = { .less = new_btree_iter_cmp, - .swp = new_btree_iter_swap, + .swp = NULL, }; if (k != end) @@ -1157,7 +1149,7 @@ static inline struct bkey *__bch_btree_iter_next(struct btree_iter *iter, struct bkey *ret = NULL; const struct min_heap_callbacks callbacks = { .less = cmp, - .swp = new_btree_iter_swap, + .swp = NULL, }; if (!btree_iter_end(iter)) { @@ -1231,7 +1223,7 @@ static void btree_mergesort(struct btree_keys *b, struct bset *out, : bch_ptr_invalid; const struct min_heap_callbacks callbacks = { .less = b->ops->sort_cmp, - .swp = new_btree_iter_swap, + .swp = NULL, }; /* Heapify the iterator, using our comparison function */ diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c index a7221e5dbe817..4b84fda1530a7 100644 --- a/drivers/md/bcache/extents.c +++ b/drivers/md/bcache/extents.c @@ -266,20 +266,12 @@ static bool new_bch_extent_sort_cmp(const void *l, const void *r, void __always_ return !(c ? c > 0 : _l->k < _r->k); } -static inline void new_btree_iter_swap(void *iter1, void *iter2, void __always_unused *args) -{ - struct btree_iter_set *_iter1 = iter1; - struct btree_iter_set *_iter2 = iter2; - - swap(*_iter1, *_iter2); -} - static struct bkey *bch_extent_sort_fixup(struct btree_iter *iter, struct bkey *tmp) { const struct min_heap_callbacks callbacks = { .less = new_bch_extent_sort_cmp, - .swp = new_btree_iter_swap, + .swp = NULL, }; while (iter->heap.nr > 1) { struct btree_iter_set *top = iter->heap.data, *i = top + 1; diff --git a/drivers/md/bcache/movinggc.c b/drivers/md/bcache/movinggc.c index 7f482729c56de..ef6abf33f9260 100644 --- a/drivers/md/bcache/movinggc.c +++ b/drivers/md/bcache/movinggc.c @@ -190,14 +190,6 @@ static bool new_bucket_cmp(const void *l, const void *r, void __always_unused *a return GC_SECTORS_USED(*_l) >= GC_SECTORS_USED(*_r); } -static void new_bucket_swap(void *l, void *r, void __always_unused *args) -{ - struct bucket **_l = l; - struct bucket **_r = r; - - swap(*_l, *_r); -} - static unsigned int bucket_heap_top(struct cache *ca) { struct bucket *b; @@ -212,7 +204,7 @@ void bch_moving_gc(struct cache_set *c) unsigned long sectors_to_move, reserve_sectors; const struct min_heap_callbacks callbacks = { .less = new_bucket_cmp, - .swp = new_bucket_swap, + .swp = NULL, }; if (!c->copy_gc_enabled) diff --git a/drivers/md/dm-bio-prison-v1.c b/drivers/md/dm-bio-prison-v1.c index bca0f39e15b87..b4d1c4329df34 100644 --- a/drivers/md/dm-bio-prison-v1.c +++ b/drivers/md/dm-bio-prison-v1.c @@ -198,15 +198,6 @@ int dm_bio_detain(struct dm_bio_prison *prison, } EXPORT_SYMBOL_GPL(dm_bio_detain); -int dm_get_cell(struct dm_bio_prison *prison, - struct dm_cell_key *key, - struct dm_bio_prison_cell *cell_prealloc, - struct dm_bio_prison_cell **cell_result) -{ - return bio_detain(prison, key, NULL, cell_prealloc, cell_result); -} -EXPORT_SYMBOL_GPL(dm_get_cell); - /* * @inmates must have been initialised prior to this call */ @@ -288,32 +279,6 @@ void dm_cell_visit_release(struct dm_bio_prison *prison, } EXPORT_SYMBOL_GPL(dm_cell_visit_release); -static int __promote_or_release(struct rb_root *root, - struct dm_bio_prison_cell *cell) -{ - if (bio_list_empty(&cell->bios)) { - rb_erase(&cell->node, root); - return 1; - } - - cell->holder = bio_list_pop(&cell->bios); - return 0; -} - -int dm_cell_promote_or_release(struct dm_bio_prison *prison, - struct dm_bio_prison_cell *cell) -{ - int r; - unsigned l = lock_nr(&cell->key, prison->num_locks); - - spin_lock_irq(&prison->regions[l].lock); - r = __promote_or_release(&prison->regions[l].cell, cell); - spin_unlock_irq(&prison->regions[l].lock); - - return r; -} -EXPORT_SYMBOL_GPL(dm_cell_promote_or_release); - /*----------------------------------------------------------------*/ #define DEFERRED_SET_SIZE 64 diff --git a/drivers/md/dm-bio-prison-v1.h b/drivers/md/dm-bio-prison-v1.h index 2a097ed0d85e9..d39706c484476 100644 --- a/drivers/md/dm-bio-prison-v1.h +++ b/drivers/md/dm-bio-prison-v1.h @@ -72,17 +72,6 @@ struct dm_bio_prison_cell *dm_bio_prison_alloc_cell(struct dm_bio_prison *prison void dm_bio_prison_free_cell(struct dm_bio_prison *prison, struct dm_bio_prison_cell *cell); -/* - * Creates, or retrieves a cell that overlaps the given key. - * - * Returns 1 if pre-existing cell returned, zero if new cell created using - * @cell_prealloc. - */ -int dm_get_cell(struct dm_bio_prison *prison, - struct dm_cell_key *key, - struct dm_bio_prison_cell *cell_prealloc, - struct dm_bio_prison_cell **cell_result); - /* * Returns false if key is beyond BIO_PRISON_MAX_RANGE or spans a boundary. */ @@ -117,19 +106,6 @@ void dm_cell_visit_release(struct dm_bio_prison *prison, void (*visit_fn)(void *, struct dm_bio_prison_cell *), void *context, struct dm_bio_prison_cell *cell); -/* - * Rather than always releasing the prisoners in a cell, the client may - * want to promote one of them to be the new holder. There is a race here - * though between releasing an empty cell, and other threads adding new - * inmates. So this function makes the decision with its lock held. - * - * This function can have two outcomes: - * i) An inmate is promoted to be the holder of the cell (return value of 0). - * ii) The cell has no inmate for promotion and is released (return value of 1). - */ -int dm_cell_promote_or_release(struct dm_bio_prison *prison, - struct dm_bio_prison_cell *cell); - /*----------------------------------------------------------------*/ /* diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 23e0b71b991e7..aab8240429b0b 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -318,9 +318,10 @@ static struct lru_entry *lru_evict(struct lru *lru, le_predicate pred, void *con */ enum data_mode { DATA_MODE_SLAB = 0, - DATA_MODE_GET_FREE_PAGES = 1, - DATA_MODE_VMALLOC = 2, - DATA_MODE_LIMIT = 3 + DATA_MODE_KMALLOC = 1, + DATA_MODE_GET_FREE_PAGES = 2, + DATA_MODE_VMALLOC = 3, + DATA_MODE_LIMIT = 4 }; struct dm_buffer { @@ -1062,6 +1063,7 @@ static unsigned long dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES; static unsigned long dm_bufio_peak_allocated; static unsigned long dm_bufio_allocated_kmem_cache; +static unsigned long dm_bufio_allocated_kmalloc; static unsigned long dm_bufio_allocated_get_free_pages; static unsigned long dm_bufio_allocated_vmalloc; static unsigned long dm_bufio_current_allocated; @@ -1104,6 +1106,7 @@ static void adjust_total_allocated(struct dm_buffer *b, bool unlink) static unsigned long * const class_ptr[DATA_MODE_LIMIT] = { &dm_bufio_allocated_kmem_cache, + &dm_bufio_allocated_kmalloc, &dm_bufio_allocated_get_free_pages, &dm_bufio_allocated_vmalloc, }; @@ -1181,6 +1184,11 @@ static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask, return kmem_cache_alloc(c->slab_cache, gfp_mask); } + if (unlikely(c->block_size < PAGE_SIZE)) { + *data_mode = DATA_MODE_KMALLOC; + return kmalloc(c->block_size, gfp_mask | __GFP_RECLAIMABLE); + } + if (c->block_size <= KMALLOC_MAX_SIZE && gfp_mask & __GFP_NORETRY) { *data_mode = DATA_MODE_GET_FREE_PAGES; @@ -1204,6 +1212,10 @@ static void free_buffer_data(struct dm_bufio_client *c, kmem_cache_free(c->slab_cache, data); break; + case DATA_MODE_KMALLOC: + kfree(data); + break; + case DATA_MODE_GET_FREE_PAGES: free_pages((unsigned long)data, c->sectors_per_block_bits - (PAGE_SHIFT - SECTOR_SHIFT)); @@ -2519,8 +2531,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign goto bad_dm_io; } - if (block_size <= KMALLOC_MAX_SIZE && - (block_size < PAGE_SIZE || !is_power_of_2(block_size))) { + if (block_size <= KMALLOC_MAX_SIZE && !is_power_of_2(block_size)) { unsigned int align = min(1U << __ffs(block_size), (unsigned int)PAGE_SIZE); snprintf(slab_name, sizeof(slab_name), "dm_bufio_cache-%u-%u", @@ -2902,6 +2913,7 @@ static int __init dm_bufio_init(void) __u64 mem; dm_bufio_allocated_kmem_cache = 0; + dm_bufio_allocated_kmalloc = 0; dm_bufio_allocated_get_free_pages = 0; dm_bufio_allocated_vmalloc = 0; dm_bufio_current_allocated = 0; @@ -2990,6 +3002,9 @@ MODULE_PARM_DESC(peak_allocated_bytes, "Tracks the maximum allocated memory"); module_param_named(allocated_kmem_cache_bytes, dm_bufio_allocated_kmem_cache, ulong, 0444); MODULE_PARM_DESC(allocated_kmem_cache_bytes, "Memory allocated with kmem_cache_alloc"); +module_param_named(allocated_kmalloc_bytes, dm_bufio_allocated_kmalloc, ulong, 0444); +MODULE_PARM_DESC(allocated_kmalloc_bytes, "Memory allocated with kmalloc_alloc"); + module_param_named(allocated_get_free_pages_bytes, dm_bufio_allocated_get_free_pages, ulong, 0444); MODULE_PARM_DESC(allocated_get_free_pages_bytes, "Memory allocated with get_free_pages"); diff --git a/drivers/md/dm-cache-background-tracker.c b/drivers/md/dm-cache-background-tracker.c index f3051bd7d2df0..b4165f172d62f 100644 --- a/drivers/md/dm-cache-background-tracker.c +++ b/drivers/md/dm-cache-background-tracker.c @@ -143,12 +143,6 @@ static void update_stats(struct background_tracker *b, struct policy_work *w, in } } -unsigned int btracker_nr_writebacks_queued(struct background_tracker *b) -{ - return atomic_read(&b->pending_writebacks); -} -EXPORT_SYMBOL_GPL(btracker_nr_writebacks_queued); - unsigned int btracker_nr_demotions_queued(struct background_tracker *b) { return atomic_read(&b->pending_demotes); diff --git a/drivers/md/dm-cache-background-tracker.h b/drivers/md/dm-cache-background-tracker.h index 09c8fc59f7bb7..47156c14a44ab 100644 --- a/drivers/md/dm-cache-background-tracker.h +++ b/drivers/md/dm-cache-background-tracker.h @@ -50,7 +50,6 @@ struct background_tracker *btracker_create(unsigned int max_work); */ void btracker_destroy(struct background_tracker *b); -unsigned int btracker_nr_writebacks_queued(struct background_tracker *b); unsigned int btracker_nr_demotions_queued(struct background_tracker *b); /* diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 24cd87fddf752..a9a1ab284076a 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -1218,15 +1218,6 @@ int dm_cache_load_discards(struct dm_cache_metadata *cmd, return r; } -int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result) -{ - READ_LOCK(cmd); - *result = cmd->cache_blocks; - READ_UNLOCK(cmd); - - return 0; -} - static int __remove(struct dm_cache_metadata *cmd, dm_cblock_t cblock) { int r; @@ -1507,30 +1498,6 @@ int dm_cache_load_mappings(struct dm_cache_metadata *cmd, return r; } -static int __dump_mapping(void *context, uint64_t cblock, void *leaf) -{ - __le64 value; - dm_oblock_t oblock; - unsigned int flags; - - memcpy(&value, leaf, sizeof(value)); - unpack_value(value, &oblock, &flags); - - return 0; -} - -static int __dump_mappings(struct dm_cache_metadata *cmd) -{ - return dm_array_walk(&cmd->info, cmd->root, __dump_mapping, NULL); -} - -void dm_cache_dump(struct dm_cache_metadata *cmd) -{ - READ_LOCK_VOID(cmd); - __dump_mappings(cmd); - READ_UNLOCK(cmd); -} - int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd) { int r; diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h index 57afc70479472..5f77890207fed 100644 --- a/drivers/md/dm-cache-metadata.h +++ b/drivers/md/dm-cache-metadata.h @@ -71,7 +71,6 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd); * origin blocks to map to. */ int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size); -int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result); int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd, sector_t discard_block_size, @@ -123,8 +122,6 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd, int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd, dm_block_t *result); -void dm_cache_dump(struct dm_cache_metadata *cmd); - /* * The policy is invited to save a 32bit hint value for every cblock (eg, * for a hit count). These are stored against the policy name. If diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index f299ff393a6a2..d42eac944eb54 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1912,7 +1912,7 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user, if ((kernel_params->version[0] != DM_VERSION_MAJOR) || (kernel_params->version[1] > DM_VERSION_MINOR)) { - DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + DMERR_LIMIT("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, kernel_params->version[0], @@ -1961,7 +1961,7 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kern if (unlikely(param_kernel->data_size < minimum_data_size) || unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) { - DMERR("Invalid data size in the ioctl structure: %u", + DMERR_LIMIT("Invalid data size in the ioctl structure: %u", param_kernel->data_size); return -EINVAL; } diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index dbd39b9722b91..bd8b796ae683b 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1033,11 +1033,6 @@ struct dm_target *dm_table_get_wildcard_target(struct dm_table *t) return NULL; } -bool dm_table_bio_based(struct dm_table *t) -{ - return __table_type_bio_based(dm_table_get_type(t)); -} - bool dm_table_request_based(struct dm_table *t) { return __table_type_request_based(dm_table_get_type(t)); diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 9095f19a84f3c..bf0f9dddd146a 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -2484,6 +2484,7 @@ static void pool_work_wait(struct pool_work *pw, struct pool *pool, init_completion(&pw->complete); queue_work(pool->wq, &pw->worker); wait_for_completion(&pw->complete); + destroy_work_on_stack(&pw->worker); } /*----------------------------------------------------------------*/ diff --git a/drivers/md/dm-vdo/Kconfig b/drivers/md/dm-vdo/Kconfig index 111ecd2c2a249..2400b2bc4bc72 100644 --- a/drivers/md/dm-vdo/Kconfig +++ b/drivers/md/dm-vdo/Kconfig @@ -7,6 +7,7 @@ config DM_VDO select DM_BUFIO select LZ4_COMPRESS select LZ4_DECOMPRESS + select MIN_HEAP help This device mapper target presents a block device with deduplication, compression and thin-provisioning. diff --git a/drivers/md/dm-vdo/block-map.c b/drivers/md/dm-vdo/block-map.c index a0a7c1bd634e8..89cb7942ec5cc 100644 --- a/drivers/md/dm-vdo/block-map.c +++ b/drivers/md/dm-vdo/block-map.c @@ -209,8 +209,6 @@ static int initialize_info(struct vdo_page_cache *cache) /** * allocate_cache_components() - Allocate components of the cache which require their own * allocation. - * @maximum_age: The number of journal blocks before a dirtied page is considered old and must be - * written out. * * The caller is responsible for all clean up on errors. * diff --git a/drivers/md/dm-vdo/data-vio.c b/drivers/md/dm-vdo/data-vio.c index 0d502f6a86ad0..810002747091f 100644 --- a/drivers/md/dm-vdo/data-vio.c +++ b/drivers/md/dm-vdo/data-vio.c @@ -327,8 +327,9 @@ static u32 __must_check pack_status(struct data_vio_compression_status status) /** * set_data_vio_compression_status() - Set the compression status of a data_vio. - * @state: The expected current status of the data_vio. - * @new_state: The status to set. + * @data_vio: The data_vio to change. + * @status: The expected current status of the data_vio. + * @new_status: The status to set. * * Return: true if the new status was set, false if the data_vio's compression status did not * match the expected state, and so was left unchanged. @@ -836,7 +837,7 @@ static void destroy_data_vio(struct data_vio *data_vio) * @vdo: The vdo to which the pool will belong. * @pool_size: The number of data_vios in the pool. * @discard_limit: The maximum number of data_vios which may be used for discards. - * @pool: A pointer to hold the newly allocated pool. + * @pool_ptr: A pointer to hold the newly allocated pool. */ int make_data_vio_pool(struct vdo *vdo, data_vio_count_t pool_size, data_vio_count_t discard_limit, struct data_vio_pool **pool_ptr) @@ -1074,35 +1075,6 @@ void dump_data_vio_pool(struct data_vio_pool *pool, bool dump_vios) spin_unlock(&pool->lock); } -data_vio_count_t get_data_vio_pool_active_discards(struct data_vio_pool *pool) -{ - return READ_ONCE(pool->discard_limiter.busy); -} - -data_vio_count_t get_data_vio_pool_discard_limit(struct data_vio_pool *pool) -{ - return READ_ONCE(pool->discard_limiter.limit); -} - -data_vio_count_t get_data_vio_pool_maximum_discards(struct data_vio_pool *pool) -{ - return READ_ONCE(pool->discard_limiter.max_busy); -} - -int set_data_vio_pool_discard_limit(struct data_vio_pool *pool, data_vio_count_t limit) -{ - if (get_data_vio_pool_request_limit(pool) < limit) { - // The discard limit may not be higher than the data_vio limit. - return -EINVAL; - } - - spin_lock(&pool->lock); - pool->discard_limiter.limit = limit; - spin_unlock(&pool->lock); - - return VDO_SUCCESS; -} - data_vio_count_t get_data_vio_pool_active_requests(struct data_vio_pool *pool) { return READ_ONCE(pool->limiter.busy); diff --git a/drivers/md/dm-vdo/data-vio.h b/drivers/md/dm-vdo/data-vio.h index 25926b6cd98bb..067b983bb291d 100644 --- a/drivers/md/dm-vdo/data-vio.h +++ b/drivers/md/dm-vdo/data-vio.h @@ -336,11 +336,6 @@ void drain_data_vio_pool(struct data_vio_pool *pool, struct vdo_completion *comp void resume_data_vio_pool(struct data_vio_pool *pool, struct vdo_completion *completion); void dump_data_vio_pool(struct data_vio_pool *pool, bool dump_vios); -data_vio_count_t get_data_vio_pool_active_discards(struct data_vio_pool *pool); -data_vio_count_t get_data_vio_pool_discard_limit(struct data_vio_pool *pool); -data_vio_count_t get_data_vio_pool_maximum_discards(struct data_vio_pool *pool); -int __must_check set_data_vio_pool_discard_limit(struct data_vio_pool *pool, - data_vio_count_t limit); data_vio_count_t get_data_vio_pool_active_requests(struct data_vio_pool *pool); data_vio_count_t get_data_vio_pool_request_limit(struct data_vio_pool *pool); data_vio_count_t get_data_vio_pool_maximum_requests(struct data_vio_pool *pool); diff --git a/drivers/md/dm-vdo/dedupe.c b/drivers/md/dm-vdo/dedupe.c index 80628ae93fbac..b6f8e2dc7729f 100644 --- a/drivers/md/dm-vdo/dedupe.c +++ b/drivers/md/dm-vdo/dedupe.c @@ -565,7 +565,7 @@ static void wait_on_hash_lock(struct hash_lock *lock, struct data_vio *data_vio) * @waiter: The data_vio's waiter link. * @context: Not used. */ -static void abort_waiter(struct vdo_waiter *waiter, void *context __always_unused) +static void abort_waiter(struct vdo_waiter *waiter, void __always_unused *context) { write_data_vio(vdo_waiter_as_data_vio(waiter)); } @@ -1727,7 +1727,7 @@ static void report_bogus_lock_state(struct hash_lock *lock, struct data_vio *dat /** * vdo_continue_hash_lock() - Continue the processing state after writing, compressing, or * deduplicating. - * @data_vio: The data_vio to continue processing in its hash lock. + * @completion: The data_vio completion to continue processing in its hash lock. * * Asynchronously continue processing a data_vio in its hash lock after it has finished writing, * compressing, or deduplicating, so it can share the result with any data_vios waiting in the hash @@ -1825,7 +1825,7 @@ static inline int assert_hash_lock_preconditions(const struct data_vio *data_vio /** * vdo_acquire_hash_lock() - Acquire or share a lock on a record name. - * @data_vio: The data_vio acquiring a lock on its record name. + * @completion: The data_vio completion acquiring a lock on its record name. * * Acquire or share a lock on the hash (record name) of the data in a data_vio, updating the * data_vio to reference the lock. This must only be called in the correct thread for the zone. In @@ -2679,7 +2679,8 @@ static void get_index_statistics(struct hash_zones *zones, /** * vdo_get_dedupe_statistics() - Tally the statistics from all the hash zones and the UDS index. - * @hash_zones: The hash zones to query + * @zones: The hash zones to query + * @stats: A structure to store the statistics * * Return: The sum of the hash lock statistics from all hash zones plus the statistics from the UDS * index diff --git a/drivers/md/dm-vdo/encodings.c b/drivers/md/dm-vdo/encodings.c index a34ea0229d536..100e92f8f866c 100644 --- a/drivers/md/dm-vdo/encodings.c +++ b/drivers/md/dm-vdo/encodings.c @@ -858,7 +858,7 @@ static int __must_check make_partition(struct layout *layout, enum partition_id /** * vdo_initialize_layout() - Lay out the partitions of a vdo. * @size: The entire size of the vdo. - * @origin: The start of the layout on the underlying storage in blocks. + * @offset: The start of the layout on the underlying storage in blocks. * @block_map_blocks: The size of the block map partition. * @journal_blocks: The size of the journal partition. * @summary_blocks: The size of the slab summary partition. diff --git a/drivers/md/dm-vdo/indexer/index-layout.c b/drivers/md/dm-vdo/indexer/index-layout.c index 627adc24af3b7..af8fab83b0f3e 100644 --- a/drivers/md/dm-vdo/indexer/index-layout.c +++ b/drivers/md/dm-vdo/indexer/index-layout.c @@ -248,32 +248,6 @@ static int __must_check compute_sizes(const struct uds_configuration *config, return UDS_SUCCESS; } -int uds_compute_index_size(const struct uds_parameters *parameters, u64 *index_size) -{ - int result; - struct uds_configuration *index_config; - struct save_layout_sizes sizes; - - if (index_size == NULL) { - vdo_log_error("Missing output size pointer"); - return -EINVAL; - } - - result = uds_make_configuration(parameters, &index_config); - if (result != UDS_SUCCESS) { - vdo_log_error_strerror(result, "cannot compute index size"); - return uds_status_to_errno(result); - } - - result = compute_sizes(index_config, &sizes); - uds_free_configuration(index_config); - if (result != UDS_SUCCESS) - return uds_status_to_errno(result); - - *index_size = sizes.total_size; - return UDS_SUCCESS; -} - /* Create unique data using the current time and a pseudorandom number. */ static void create_unique_nonce_data(u8 *buffer) { diff --git a/drivers/md/dm-vdo/indexer/indexer.h b/drivers/md/dm-vdo/indexer/indexer.h index 3744aaf625b05..183a94eb7e923 100644 --- a/drivers/md/dm-vdo/indexer/indexer.h +++ b/drivers/md/dm-vdo/indexer/indexer.h @@ -283,10 +283,6 @@ struct uds_request { enum uds_index_region location; }; -/* Compute the number of bytes needed to store an index. */ -int __must_check uds_compute_index_size(const struct uds_parameters *parameters, - u64 *index_size); - /* A session is required for most index operations. */ int __must_check uds_create_index_session(struct uds_index_session **session); diff --git a/drivers/md/dm-vdo/int-map.c b/drivers/md/dm-vdo/int-map.c index f6fe58e437b3a..aeb690415dbd1 100644 --- a/drivers/md/dm-vdo/int-map.c +++ b/drivers/md/dm-vdo/int-map.c @@ -70,7 +70,7 @@ * it's crucial to keep the hop fields near the buckets that they use them so they'll tend to share * cache lines. */ -struct __packed bucket { +struct bucket { /** * @first_hop: The biased offset of the first entry in the hop list of the neighborhood * that hashes to this bucket. @@ -82,7 +82,7 @@ struct __packed bucket { u64 key; /** @value: The value stored in this bucket (NULL if empty). */ void *value; -}; +} __packed; /** * struct int_map - The concrete definition of the opaque int_map type. @@ -310,7 +310,6 @@ static struct bucket *select_bucket(const struct int_map *map, u64 key) /** * search_hop_list() - Search the hop list associated with given hash bucket for a given search * key. - * @map: The map being searched. * @bucket: The map bucket to search for the key. * @key: The mapping key. * @previous_ptr: Output. if not NULL, a pointer in which to store the bucket in the list preceding @@ -321,9 +320,7 @@ static struct bucket *select_bucket(const struct int_map *map, u64 key) * * Return: An entry that matches the key, or NULL if not found. */ -static struct bucket *search_hop_list(struct int_map *map __always_unused, - struct bucket *bucket, - u64 key, +static struct bucket *search_hop_list(struct bucket *bucket, u64 key, struct bucket **previous_ptr) { struct bucket *previous = NULL; @@ -357,7 +354,7 @@ static struct bucket *search_hop_list(struct int_map *map __always_unused, */ void *vdo_int_map_get(struct int_map *map, u64 key) { - struct bucket *match = search_hop_list(map, select_bucket(map, key), key, NULL); + struct bucket *match = search_hop_list(select_bucket(map, key), key, NULL); return ((match != NULL) ? match->value : NULL); } @@ -443,7 +440,6 @@ find_empty_bucket(struct int_map *map, struct bucket *bucket, unsigned int max_p /** * move_empty_bucket() - Move an empty bucket closer to the start of the bucket array. - * @map: The map containing the bucket. * @hole: The empty bucket to fill with an entry that precedes it in one of its enclosing * neighborhoods. * @@ -454,8 +450,7 @@ find_empty_bucket(struct int_map *map, struct bucket *bucket, unsigned int max_p * Return: The bucket that was vacated by moving its entry to the provided hole, or NULL if no * entry could be moved. */ -static struct bucket *move_empty_bucket(struct int_map *map __always_unused, - struct bucket *hole) +static struct bucket *move_empty_bucket(struct bucket *hole) { /* * Examine every neighborhood that the empty bucket is part of, starting with the one in @@ -516,7 +511,6 @@ static struct bucket *move_empty_bucket(struct int_map *map __always_unused, /** * update_mapping() - Find and update any existing mapping for a given key, returning the value * associated with the key in the provided pointer. - * @map: The int_map to attempt to modify. * @neighborhood: The first bucket in the neighborhood that would contain the search key * @key: The key with which to associate the new value. * @new_value: The value to be associated with the key. @@ -525,10 +519,10 @@ static struct bucket *move_empty_bucket(struct int_map *map __always_unused, * * Return: true if the map contains a mapping for the key, false if it does not. */ -static bool update_mapping(struct int_map *map, struct bucket *neighborhood, - u64 key, void *new_value, bool update, void **old_value_ptr) +static bool update_mapping(struct bucket *neighborhood, u64 key, void *new_value, + bool update, void **old_value_ptr) { - struct bucket *bucket = search_hop_list(map, neighborhood, key, NULL); + struct bucket *bucket = search_hop_list(neighborhood, key, NULL); if (bucket == NULL) { /* There is no bucket containing the key in the neighborhood. */ @@ -584,7 +578,7 @@ static struct bucket *find_or_make_vacancy(struct int_map *map, * The nearest empty bucket isn't within the neighborhood that must contain the new * entry, so try to swap it with bucket that is closer. */ - hole = move_empty_bucket(map, hole); + hole = move_empty_bucket(hole); } return NULL; @@ -625,7 +619,7 @@ int vdo_int_map_put(struct int_map *map, u64 key, void *new_value, bool update, * Check whether the neighborhood already contains an entry for the key, in which case we * optionally update it, returning the old value. */ - if (update_mapping(map, neighborhood, key, new_value, update, old_value_ptr)) + if (update_mapping(neighborhood, key, new_value, update, old_value_ptr)) return VDO_SUCCESS; /* @@ -679,7 +673,7 @@ void *vdo_int_map_remove(struct int_map *map, u64 key) /* Select the bucket to search and search it for an existing entry. */ struct bucket *bucket = select_bucket(map, key); struct bucket *previous; - struct bucket *victim = search_hop_list(map, bucket, key, &previous); + struct bucket *victim = search_hop_list(bucket, key, &previous); if (victim == NULL) { /* There is no matching entry to remove. */ diff --git a/drivers/md/dm-vdo/io-submitter.c b/drivers/md/dm-vdo/io-submitter.c index ab62abe18827b..421e5436c32c9 100644 --- a/drivers/md/dm-vdo/io-submitter.c +++ b/drivers/md/dm-vdo/io-submitter.c @@ -367,7 +367,7 @@ void __submit_metadata_vio(struct vio *vio, physical_block_number_t physical, * completions. * @max_requests_active: Number of bios for merge tracking. * @vdo: The vdo which will use this submitter. - * @io_submitter: pointer to the new data structure. + * @io_submitter_ptr: pointer to the new data structure. * * Return: VDO_SUCCESS or an error. */ diff --git a/drivers/md/dm-vdo/murmurhash3.c b/drivers/md/dm-vdo/murmurhash3.c index 13008b0892062..b0b0587d85f37 100644 --- a/drivers/md/dm-vdo/murmurhash3.c +++ b/drivers/md/dm-vdo/murmurhash3.c @@ -44,14 +44,11 @@ void murmurhash3_128(const void *key, const int len, const u32 seed, void *out) u64 *hash_out = out; /* body */ - - const u64 *blocks = (const u64 *)(data); - int i; for (i = 0; i < nblocks; i++) { - u64 k1 = get_unaligned_le64(&blocks[i * 2]); - u64 k2 = get_unaligned_le64(&blocks[i * 2 + 1]); + u64 k1 = get_unaligned_le64(&data[i * 16]); + u64 k2 = get_unaligned_le64(&data[i * 16 + 8]); k1 *= c1; k1 = ROTL64(k1, 31); diff --git a/drivers/md/dm-vdo/packer.c b/drivers/md/dm-vdo/packer.c index 16cf29b4c90a5..f70f5edabc100 100644 --- a/drivers/md/dm-vdo/packer.c +++ b/drivers/md/dm-vdo/packer.c @@ -250,7 +250,6 @@ static void abort_packing(struct data_vio *data_vio) /** * release_compressed_write_waiter() - Update a data_vio for which a successful compressed write * has completed and send it on its way. - * @data_vio: The data_vio to release. * @allocation: The allocation to which the compressed block was written. */ @@ -383,7 +382,7 @@ static void initialize_compressed_block(struct compressed_block *block, u16 size * @compression: The agent's compression_state to pack in to. * @data_vio: The data_vio to pack. * @offset: The offset into the compressed block at which to pack the fragment. - * @compressed_block: The compressed block which will be written out when batch is fully packed. + * @block: The compressed block which will be written out when batch is fully packed. * * Return: The new amount of space used. */ diff --git a/drivers/md/dm-vdo/physical-zone.c b/drivers/md/dm-vdo/physical-zone.c index 2fee3a7c1191b..a43b5c45fab7c 100644 --- a/drivers/md/dm-vdo/physical-zone.c +++ b/drivers/md/dm-vdo/physical-zone.c @@ -517,7 +517,7 @@ static int allocate_and_lock_block(struct allocation *allocation) * @waiter: The allocating_vio that was waiting to allocate. * @context: The context (unused). */ -static void retry_allocation(struct vdo_waiter *waiter, void *context __always_unused) +static void retry_allocation(struct vdo_waiter *waiter, void __always_unused *context) { struct data_vio *data_vio = vdo_waiter_as_data_vio(waiter); diff --git a/drivers/md/dm-vdo/recovery-journal.c b/drivers/md/dm-vdo/recovery-journal.c index ee6321a3e5238..de58184f538f5 100644 --- a/drivers/md/dm-vdo/recovery-journal.c +++ b/drivers/md/dm-vdo/recovery-journal.c @@ -1365,7 +1365,7 @@ static void add_queued_recovery_entries(struct recovery_journal_block *block) * * Implements waiter_callback_fn. */ -static void write_block(struct vdo_waiter *waiter, void *context __always_unused) +static void write_block(struct vdo_waiter *waiter, void __always_unused *context) { struct recovery_journal_block *block = container_of(waiter, struct recovery_journal_block, write_waiter); diff --git a/drivers/md/dm-vdo/repair.c b/drivers/md/dm-vdo/repair.c index ffff2c999518e..8c006fb3afcf0 100644 --- a/drivers/md/dm-vdo/repair.c +++ b/drivers/md/dm-vdo/repair.c @@ -166,7 +166,7 @@ static void swap_mappings(void *item1, void *item2, void __always_unused *args) static const struct min_heap_callbacks repair_min_heap = { .less = mapping_is_less_than, - .swp = swap_mappings, + .swp = NULL, }; static struct numbered_block_mapping *sort_next_heap_element(struct repair_completion *repair) diff --git a/drivers/md/dm-vdo/slab-depot.c b/drivers/md/dm-vdo/slab-depot.c index 274f9ccd072f0..8f0a35c63af68 100644 --- a/drivers/md/dm-vdo/slab-depot.c +++ b/drivers/md/dm-vdo/slab-depot.c @@ -1287,7 +1287,7 @@ static struct reference_block * __must_check get_reference_block(struct vdo_slab * slab_block_number_from_pbn() - Determine the index within the slab of a particular physical * block number. * @slab: The slab. - * @physical_block_number: The physical block number. + * @pbn: The physical block number. * @slab_block_number_ptr: A pointer to the slab block number. * * Return: VDO_SUCCESS or an error code. @@ -1459,7 +1459,6 @@ static int increment_for_data(struct vdo_slab *slab, struct reference_block *blo * @block_number: The block to update. * @old_status: The reference status of the data block before this decrement. * @updater: The reference updater doing this operation in case we need to look up the pbn lock. - * @lock: The pbn_lock associated with the block being decremented (may be NULL). * @counter_ptr: A pointer to the count for the data block (in, out). * @adjust_block_count: Whether to update the allocator's free block count. * @@ -3232,8 +3231,7 @@ int vdo_enqueue_clean_slab_waiter(struct block_allocator *allocator, /** * vdo_modify_reference_count() - Modify the reference count of a block by first making a slab * journal entry and then updating the reference counter. - * - * @data_vio: The data_vio for which to add the entry. + * @completion: The data_vio completion for which to add the entry. * @updater: Which of the data_vio's reference updaters is being submitted. */ void vdo_modify_reference_count(struct vdo_completion *completion, @@ -3301,17 +3299,9 @@ static bool slab_status_is_less_than(const void *item1, const void *item2, return info1->slab_number < info2->slab_number; } -static void swap_slab_statuses(void *item1, void *item2, void __always_unused *args) -{ - struct slab_status *info1 = item1; - struct slab_status *info2 = item2; - - swap(*info1, *info2); -} - static const struct min_heap_callbacks slab_status_min_heap = { .less = slab_status_is_less_than, - .swp = swap_slab_statuses, + .swp = NULL, }; /* Inform the slab actor that a action has finished on some slab; used by apply_to_slabs(). */ @@ -4750,8 +4740,7 @@ void vdo_use_new_slabs(struct slab_depot *depot, struct vdo_completion *parent) /** * stop_scrubbing() - Tell the scrubber to stop scrubbing after it finishes the slab it is * currently working on. - * @scrubber: The scrubber to stop. - * @parent: The completion to notify when scrubbing has stopped. + * @allocator: The block allocator owning the scrubber to stop. */ static void stop_scrubbing(struct block_allocator *allocator) { diff --git a/drivers/md/dm-vdo/vdo.c b/drivers/md/dm-vdo/vdo.c index fff847767755a..a7e32baab4afd 100644 --- a/drivers/md/dm-vdo/vdo.c +++ b/drivers/md/dm-vdo/vdo.c @@ -643,7 +643,7 @@ static void finish_vdo(struct vdo *vdo) /** * free_listeners() - Free the list of read-only listeners associated with a thread. - * @thread_data: The thread holding the list to free. + * @thread: The thread holding the list to free. */ static void free_listeners(struct vdo_thread *thread) { @@ -852,7 +852,7 @@ int vdo_synchronous_flush(struct vdo *vdo) /** * vdo_get_state() - Get the current state of the vdo. * @vdo: The vdo. - + * * Context: This method may be called from any thread. * * Return: The current state of the vdo. diff --git a/drivers/md/dm-vdo/vio.c b/drivers/md/dm-vdo/vio.c index b291578f726f5..e710f3c5a972d 100644 --- a/drivers/md/dm-vdo/vio.c +++ b/drivers/md/dm-vdo/vio.c @@ -202,6 +202,7 @@ int vio_reset_bio(struct vio *vio, char *data, bio_end_io_t callback, if (data == NULL) return VDO_SUCCESS; + bio->bi_ioprio = 0; bio->bi_io_vec = bio->bi_inline_vecs; bio->bi_max_vecs = vio->block_count + 1; len = VDO_BLOCK_SIZE * vio->block_count; diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index c142ec5458b70..47d595f6a76e5 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -93,7 +93,7 @@ static void dm_bufio_alloc_callback(struct dm_buffer *buf) */ static sector_t verity_map_sector(struct dm_verity *v, sector_t bi_sector) { - return v->data_start + dm_target_offset(v->ti, bi_sector); + return dm_target_offset(v->ti, bi_sector); } /* @@ -952,7 +952,7 @@ static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev *bdev = v->data_dev->bdev; - if (v->data_start || ti->len != bdev_nr_sectors(v->data_dev->bdev)) + if (ti->len != bdev_nr_sectors(v->data_dev->bdev)) return 1; return 0; } @@ -962,7 +962,7 @@ static int verity_iterate_devices(struct dm_target *ti, { struct dm_verity *v = ti->private; - return fn(ti, v->data_dev, v->data_start, ti->len, data); + return fn(ti, v->data_dev, 0, ti->len, data); } static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index c996140bda94d..8cbb57862ae19 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -50,7 +50,6 @@ struct dm_verity { unsigned int sig_size; /* root digest signature size */ #endif /* CONFIG_SECURITY */ unsigned int salt_size; - sector_t data_start; /* data offset in 512-byte sectors */ sector_t hash_start; /* hash start in blocks */ sector_t data_blocks; /* the number of data blocks */ sector_t hash_blocks; /* the number of hash blocks */ diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index 8156881a31de9..deff22ecccbb8 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -245,11 +245,6 @@ unsigned int dmz_zone_nr_blocks(struct dmz_metadata *zmd) return zmd->zone_nr_blocks; } -unsigned int dmz_zone_nr_blocks_shift(struct dmz_metadata *zmd) -{ - return zmd->zone_nr_blocks_shift; -} - unsigned int dmz_zone_nr_sectors(struct dmz_metadata *zmd) { return zmd->zone_nr_sectors; @@ -3005,48 +3000,3 @@ void dmz_dtr_metadata(struct dmz_metadata *zmd) dmz_cleanup_metadata(zmd); kfree(zmd); } - -/* - * Check zone information on resume. - */ -int dmz_resume_metadata(struct dmz_metadata *zmd) -{ - struct dm_zone *zone; - sector_t wp_block; - unsigned int i; - int ret; - - /* Check zones */ - for (i = 0; i < zmd->nr_zones; i++) { - zone = dmz_get(zmd, i); - if (!zone) { - dmz_zmd_err(zmd, "Unable to get zone %u", i); - return -EIO; - } - wp_block = zone->wp_block; - - ret = dmz_update_zone(zmd, zone); - if (ret) { - dmz_zmd_err(zmd, "Broken zone %u", i); - return ret; - } - - if (dmz_is_offline(zone)) { - dmz_zmd_warn(zmd, "Zone %u is offline", i); - continue; - } - - /* Check write pointer */ - if (!dmz_is_seq(zone)) - zone->wp_block = 0; - else if (zone->wp_block != wp_block) { - dmz_zmd_err(zmd, "Zone %u: Invalid wp (%llu / %llu)", - i, (u64)zone->wp_block, (u64)wp_block); - zone->wp_block = wp_block; - dmz_invalidate_blocks(zmd, zone, zone->wp_block, - zmd->zone_nr_blocks - zone->wp_block); - } - } - - return 0; -} diff --git a/drivers/md/dm-zoned.h b/drivers/md/dm-zoned.h index 265494d3f7114..59ba0aaa95313 100644 --- a/drivers/md/dm-zoned.h +++ b/drivers/md/dm-zoned.h @@ -192,7 +192,6 @@ enum { int dmz_ctr_metadata(struct dmz_dev *dev, int num_dev, struct dmz_metadata **zmd, const char *devname); void dmz_dtr_metadata(struct dmz_metadata *zmd); -int dmz_resume_metadata(struct dmz_metadata *zmd); void dmz_lock_map(struct dmz_metadata *zmd); void dmz_unlock_map(struct dmz_metadata *zmd); @@ -230,7 +229,6 @@ unsigned int dmz_nr_unmap_rnd_zones(struct dmz_metadata *zmd, int idx); unsigned int dmz_nr_seq_zones(struct dmz_metadata *zmd, int idx); unsigned int dmz_nr_unmap_seq_zones(struct dmz_metadata *zmd, int idx); unsigned int dmz_zone_nr_blocks(struct dmz_metadata *zmd); -unsigned int dmz_zone_nr_blocks_shift(struct dmz_metadata *zmd); unsigned int dmz_zone_nr_sectors(struct dmz_metadata *zmd); unsigned int dmz_zone_nr_sectors_shift(struct dmz_metadata *zmd); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 19230404d8c2b..12ecf07a38410 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2517,12 +2517,6 @@ void dm_unlock_md_type(struct mapped_device *md) mutex_unlock(&md->type_lock); } -void dm_set_md_type(struct mapped_device *md, enum dm_queue_mode type) -{ - BUG_ON(!mutex_is_locked(&md->type_lock)); - md->type = type; -} - enum dm_queue_mode dm_get_md_type(struct mapped_device *md) { return md->type; @@ -3349,6 +3343,59 @@ void dm_free_md_mempools(struct dm_md_mempools *pools) kfree(pools); } +struct dm_blkdev_id { + u8 *id; + enum blk_unique_id type; +}; + +static int __dm_get_unique_id(struct dm_target *ti, struct dm_dev *dev, + sector_t start, sector_t len, void *data) +{ + struct dm_blkdev_id *dm_id = data; + const struct block_device_operations *fops = dev->bdev->bd_disk->fops; + + if (!fops->get_unique_id) + return 0; + + return fops->get_unique_id(dev->bdev->bd_disk, dm_id->id, dm_id->type); +} + +/* + * Allow access to get_unique_id() for the first device returning a + * non-zero result. Reasonable use expects all devices to have the + * same unique id. + */ +static int dm_blk_get_unique_id(struct gendisk *disk, u8 *id, + enum blk_unique_id type) +{ + struct mapped_device *md = disk->private_data; + struct dm_table *table; + struct dm_target *ti; + int ret = 0, srcu_idx; + + struct dm_blkdev_id dm_id = { + .id = id, + .type = type, + }; + + table = dm_get_live_table(md, &srcu_idx); + if (!table || !dm_table_get_size(table)) + goto out; + + /* We only support devices that have a single target */ + if (table->num_targets != 1) + goto out; + ti = dm_table_get_target(table, 0); + + if (!ti->type->iterate_devices) + goto out; + + ret = ti->type->iterate_devices(ti, __dm_get_unique_id, &dm_id); +out: + dm_put_live_table(md, srcu_idx); + return ret; +} + struct dm_pr { u64 old_key; u64 new_key; @@ -3674,6 +3721,7 @@ static const struct block_device_operations dm_blk_dops = { .ioctl = dm_blk_ioctl, .getgeo = dm_blk_getgeo, .report_zones = dm_blk_report_zones, + .get_unique_id = dm_blk_get_unique_id, .pr_ops = &dm_pr_ops, .owner = THIS_MODULE }; @@ -3683,6 +3731,7 @@ static const struct block_device_operations dm_rq_blk_dops = { .release = dm_blk_close, .ioctl = dm_blk_ioctl, .getgeo = dm_blk_getgeo, + .get_unique_id = dm_blk_get_unique_id, .pr_ops = &dm_pr_ops, .owner = THIS_MODULE }; diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 8ad782249af88..a0a8ff1198158 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -71,12 +71,10 @@ enum dm_queue_mode dm_table_get_type(struct dm_table *t); struct target_type *dm_table_get_immutable_target_type(struct dm_table *t); struct dm_target *dm_table_get_immutable_target(struct dm_table *t); struct dm_target *dm_table_get_wildcard_target(struct dm_table *t); -bool dm_table_bio_based(struct dm_table *t); bool dm_table_request_based(struct dm_table *t); void dm_lock_md_type(struct mapped_device *md); void dm_unlock_md_type(struct mapped_device *md); -void dm_set_md_type(struct mapped_device *md, enum dm_queue_mode type); enum dm_queue_mode dm_get_md_type(struct mapped_device *md); struct target_type *dm_get_immutable_target_type(struct mapped_device *md); diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c index 3a19124ee2793..22a551c407da4 100644 --- a/drivers/md/persistent-data/dm-space-map-common.c +++ b/drivers/md/persistent-data/dm-space-map-common.c @@ -51,7 +51,7 @@ static int index_check(const struct dm_block_validator *v, block_size - sizeof(__le32), INDEX_CSUM_XOR)); if (csum_disk != mi_le->csum) { - DMERR_LIMIT("i%s failed: csum %u != wanted %u", __func__, + DMERR_LIMIT("%s failed: csum %u != wanted %u", __func__, le32_to_cpu(csum_disk), le32_to_cpu(mi_le->csum)); return -EILSEQ; } diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index baaf5f8b80ae1..7049ec7fb8eb4 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -384,6 +384,7 @@ static int raid0_set_limits(struct mddev *mddev) lim.max_write_zeroes_sectors = mddev->chunk_sectors; lim.io_min = mddev->chunk_sectors << 9; lim.io_opt = lim.io_min * mddev->raid_disks; + lim.features |= BLK_FEAT_ATOMIC_WRITES_STACKED; err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY); if (err) { queue_limits_cancel_update(mddev->gendisk->queue); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a5adf08ee1741..519c56f0ee3d4 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1571,7 +1571,21 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, continue; } if (is_bad) { - int good_sectors = first_bad - r1_bio->sector; + int good_sectors; + + /* + * We cannot atomically write this, so just + * error in that case. It could be possible to + * atomically write other mirrors, but the + * complexity of supporting that is not worth + * the benefit. + */ + if (bio->bi_opf & REQ_ATOMIC) { + error = -EIO; + goto err_handle; + } + + good_sectors = first_bad - r1_bio->sector; if (good_sectors < max_sectors) max_sectors = good_sectors; } @@ -1657,7 +1671,8 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, mbio->bi_iter.bi_sector = (r1_bio->sector + rdev->data_offset); mbio->bi_end_io = raid1_end_write_request; - mbio->bi_opf = bio_op(bio) | (bio->bi_opf & (REQ_SYNC | REQ_FUA)); + mbio->bi_opf = bio_op(bio) | + (bio->bi_opf & (REQ_SYNC | REQ_FUA | REQ_ATOMIC)); if (test_bit(FailFast, &rdev->flags) && !test_bit(WriteMostly, &rdev->flags) && conf->raid_disks - mddev->degraded > 1) @@ -3224,6 +3239,7 @@ static int raid1_set_limits(struct mddev *mddev) md_init_stacking_limits(&lim); lim.max_write_zeroes_sectors = 0; + lim.features |= BLK_FEAT_ATOMIC_WRITES_STACKED; err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY); if (err) { queue_limits_cancel_update(mddev->gendisk->queue); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 18989231791a0..7d7a8a2524dca 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1255,6 +1255,7 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio, const enum req_op op = bio_op(bio); const blk_opf_t do_sync = bio->bi_opf & REQ_SYNC; const blk_opf_t do_fua = bio->bi_opf & REQ_FUA; + const blk_opf_t do_atomic = bio->bi_opf & REQ_ATOMIC; unsigned long flags; struct r10conf *conf = mddev->private; struct md_rdev *rdev; @@ -1273,7 +1274,7 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio, mbio->bi_iter.bi_sector = (r10_bio->devs[n_copy].addr + choose_data_offset(r10_bio, rdev)); mbio->bi_end_io = raid10_end_write_request; - mbio->bi_opf = op | do_sync | do_fua; + mbio->bi_opf = op | do_sync | do_fua | do_atomic; if (!replacement && test_bit(FailFast, &conf->mirrors[devnum].rdev->flags) && enough(conf, devnum)) @@ -1468,7 +1469,21 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, continue; } if (is_bad) { - int good_sectors = first_bad - dev_sector; + int good_sectors; + + /* + * We cannot atomically write this, so just + * error in that case. It could be possible to + * atomically write other mirrors, but the + * complexity of supporting that is not worth + * the benefit. + */ + if (bio->bi_opf & REQ_ATOMIC) { + error = -EIO; + goto err_handle; + } + + good_sectors = first_bad - dev_sector; if (good_sectors < max_sectors) max_sectors = good_sectors; } @@ -4025,6 +4040,7 @@ static int raid10_set_queue_limits(struct mddev *mddev) lim.max_write_zeroes_sectors = 0; lim.io_min = mddev->chunk_sectors << 9; lim.io_opt = lim.io_min * raid10_nr_stripes(conf); + lim.features |= BLK_FEAT_ATOMIC_WRITES_STACKED; err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY); if (err) { queue_limits_cancel_update(mddev->gendisk->queue); diff --git a/drivers/message/fusion/mptlan.h b/drivers/message/fusion/mptlan.h index a1ec7e84d6fe4..40b34f670065c 100644 --- a/drivers/message/fusion/mptlan.h +++ b/drivers/message/fusion/mptlan.h @@ -51,10 +51,7 @@ #define LINUX_MPTLAN_H_INCLUDED /*****************************************************************************/ -#if !defined(__GENKSYMS__) #include -#endif - #include #include // #include diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index a0bcb0864ecd2..a798e26c6402d 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -4231,10 +4231,8 @@ mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 phys_disk_num, static void mptsas_reprobe_lun(struct scsi_device *sdev, void *data) { - int rc; - sdev->no_uld_attach = data ? 1 : 0; - rc = scsi_device_reprobe(sdev); + WARN_ON(scsi_device_reprobe(sdev)); } static void diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 902c306bc9722..09cbe3f0ab1e5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -637,7 +637,6 @@ config MCHP_LAN966X_PCI source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" -source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 885b22989580a..40bf953185c77 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -40,7 +40,6 @@ obj-y += eeprom/ obj-y += cb710/ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o -obj-y += ti-st/ obj-y += lis3lv02d/ obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 6d4edd69db126..e7d73c972f65d 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -1147,7 +1147,7 @@ static int apds990x_probe(struct i2c_client *client) err = chip->pdata->setup_resources(); if (err) { err = -EINVAL; - goto fail3; + goto fail4; } } @@ -1155,7 +1155,7 @@ static int apds990x_probe(struct i2c_client *client) apds990x_attribute_group); if (err < 0) { dev_err(&chip->client->dev, "Sysfs registration failed\n"); - goto fail4; + goto fail5; } err = request_threaded_irq(client->irq, NULL, @@ -1166,15 +1166,17 @@ static int apds990x_probe(struct i2c_client *client) if (err) { dev_err(&client->dev, "could not get IRQ %d\n", client->irq); - goto fail5; + goto fail6; } return err; -fail5: +fail6: sysfs_remove_group(&chip->client->dev.kobj, &apds990x_attribute_group[0]); -fail4: +fail5: if (chip->pdata && chip->pdata->release_resources) chip->pdata->release_resources(); +fail4: + pm_runtime_disable(&client->dev); fail3: regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); fail2: diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 6eac0f3359152..1d0322dfaf795 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -153,7 +153,7 @@ static int ssc_sound_dai_probe(struct ssc_device *ssc) ssc->sound_dai = false; - if (!of_property_read_bool(np, "#sound-dai-cells")) + if (!of_property_present(np, "#sound-dai-cells")) return 0; id = of_alias_get_id(np, "ssc"); @@ -176,7 +176,7 @@ static void ssc_sound_dai_remove(struct ssc_device *ssc) #else static inline int ssc_sound_dai_probe(struct ssc_device *ssc) { - if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells")) + if (of_property_present(ssc->pdev->dev.of_node, "#sound-dai-cells")) return -ENOTSUPP; return 0; diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 0142c4bf4f42d..a5549eaf52d08 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -17,8 +17,6 @@ #include -#define DRV_NAME_ALCOR_PCI "alcor_pci" - static DEFINE_IDA(alcor_pci_idr); static struct mfd_cell alcor_pci_cells[] = { diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index f150d8769f198..77b0490a1b38d 100644 --- a/drivers/misc/cardreader/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -20,11 +20,11 @@ MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); static const struct mfd_cell rtsx_usb_cells[] = { [RTSX_USB_SD_CARD] = { - .name = "rtsx_usb_sdmmc", + .name = DRV_NAME_RTSX_USB_SDMMC, .pdata_size = 0, }, [RTSX_USB_MS_CARD] = { - .name = "rtsx_usb_ms", + .name = DRV_NAME_RTSX_USB_MS, .pdata_size = 0, }, }; @@ -780,7 +780,7 @@ static const struct usb_device_id rtsx_usb_usb_ids[] = { MODULE_DEVICE_TABLE(usb, rtsx_usb_usb_ids); static struct usb_driver rtsx_usb_driver = { - .name = "rtsx_usb", + .name = DRV_NAME_RTSX_USB, .probe = rtsx_usb_probe, .disconnect = rtsx_usb_disconnect, .suspend = rtsx_usb_suspend, diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 9df12399bda37..cb1c4b8e7fd37 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -97,11 +97,11 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. config EEPROM_IDT_89HPESX - tristate "IDT 89HPESx PCIe-swtiches EEPROM / CSR support" + tristate "IDT 89HPESx PCIe-switches EEPROM / CSR support" depends on I2C && SYSFS help Enable this driver to get read/write access to EEPROM / CSRs - over IDT PCIe-swtich i2c-slave interface. + over IDT PCIe-switch i2c-slave interface. This driver can also be built as a module. If so, the module will be called idt_89hpesx. diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c index 9627294fe3e95..e6f0e0fc1ca20 100644 --- a/drivers/misc/eeprom/eeprom_93cx6.c +++ b/drivers/misc/eeprom/eeprom_93cx6.c @@ -8,6 +8,7 @@ * Supported chipsets: 93c46 & 93c66. */ +#include #include #include #include @@ -102,7 +103,7 @@ static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom, /* * Check if this bit needs to be set. */ - eeprom->reg_data_in = !!(data & (1 << (i - 1))); + eeprom->reg_data_in = !!(data & BIT(i - 1)); /* * Write the bit to the eeprom register. @@ -152,7 +153,7 @@ static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom, * Read if the bit has been set. */ if (eeprom->reg_data_out) - buf |= (1 << (i - 1)); + buf |= BIT(i - 1); eeprom_93cx6_pulse_low(eeprom); } @@ -186,6 +187,11 @@ void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word, eeprom_93cx6_write_bits(eeprom, command, PCI_EEPROM_WIDTH_OPCODE + eeprom->width); + if (has_quirk_extra_read_cycle(eeprom)) { + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); + } + /* * Read the requested 16 bits. */ @@ -252,6 +258,11 @@ void eeprom_93cx6_readb(struct eeprom_93cx6 *eeprom, const u8 byte, eeprom_93cx6_write_bits(eeprom, command, PCI_EEPROM_WIDTH_OPCODE + eeprom->width + 1); + if (has_quirk_extra_read_cycle(eeprom)) { + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); + } + /* * Read the requested 8 bits. */ diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index e2221be884458..9cae6f530679b 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -229,7 +229,7 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) static ssize_t eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, - const char *buf, unsigned off) + const char *buf, unsigned int off) { struct spi_message m; struct spi_transfer t[2] = {}; diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index 1643ba2ff9644..c288aeec16c09 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -68,7 +68,7 @@ static ssize_t als_lux_input_data_show(struct device *dev, if (val < 0) return val; lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536; - return sprintf(buf, "%ld\n", lux); + return sprintf(buf, "%lu\n", lux); } static ssize_t als_sensing_range_store(struct device *dev, diff --git a/drivers/misc/keba/Kconfig b/drivers/misc/keba/Kconfig index 5fbcbc2252ac1..d6d47197a963b 100644 --- a/drivers/misc/keba/Kconfig +++ b/drivers/misc/keba/Kconfig @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 config KEBA_CP500 tristate "KEBA CP500 system FPGA support" + depends on X86_64 || ARM64 || COMPILE_TEST depends on PCI + depends on I2C select AUXILIARY_BUS help This driver supports the KEBA CP500 system FPGA, which is used in @@ -11,3 +13,14 @@ config KEBA_CP500 This driver can also be built as a module. If so, the module will be called cp500. + +config KEBA_LAN9252 + tristate "KEBA CP500 LAN9252 configuration" + depends on SPI + depends on KEBA_CP500 || COMPILE_TEST + help + This driver is used for updating the configuration of the LAN9252 + controller on KEBA CP500 devices. + + This driver can also be built as a module. If so, the module will be + called lan9252. diff --git a/drivers/misc/keba/Makefile b/drivers/misc/keba/Makefile index 0a8b846cda7dc..05e9efcad54f2 100644 --- a/drivers/misc/keba/Makefile +++ b/drivers/misc/keba/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_KEBA_CP500) += cp500.o +obj-$(CONFIG_KEBA_LAN9252) += lan9252.o diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index ae09228178810..255d3022dae8f 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -12,7 +12,12 @@ #include #include #include +#include +#include +#include #include +#include +#include #define CP500 "cp500" @@ -27,6 +32,7 @@ /* BAR 0 registers */ #define CP500_VERSION_REG 0x00 #define CP500_RECONFIG_REG 0x11 /* upper 8-bits of STARTUP register */ +#define CP500_PRESENT_REG 0x20 #define CP500_AXI_REG 0x40 /* Bits in BUILD_REG */ @@ -35,14 +41,35 @@ /* Bits in RECONFIG_REG */ #define CP500_RECFG_REQ 0x01 /* reconfigure FPGA on next reset */ +/* Bits in PRESENT_REG */ +#define CP500_PRESENT_FAN0 0x01 + /* MSIX */ #define CP500_AXI_MSIX 3 +#define CP500_RFB_UART_MSIX 4 +#define CP500_DEBUG_UART_MSIX 5 +#define CP500_SI1_UART_MSIX 6 #define CP500_NUM_MSIX 8 #define CP500_NUM_MSIX_NO_MMI 2 #define CP500_NUM_MSIX_NO_AXI 3 /* EEPROM */ -#define CP500_HW_CPU_EEPROM_NAME "cp500_cpu_eeprom" +#define CP500_EEPROM_DA_OFFSET 0x016F +#define CP500_EEPROM_DA_ESC_TYPE_MASK 0x01 +#define CP500_EEPROM_ESC_LAN9252 0x00 +#define CP500_EEPROM_ESC_ET1100 0x01 +#define CP500_EEPROM_CPU_NAME "cpu_eeprom" +#define CP500_EEPROM_CPU_OFFSET 0 +#define CP500_EEPROM_CPU_SIZE 3072 +#define CP500_EEPROM_USER_NAME "user_eeprom" +#define CP500_EEPROM_USER_OFFSET 3072 +#define CP500_EEPROM_USER_SIZE 1024 + +/* SPI flash running at full speed */ +#define CP500_FLASH_HZ (33 * 1000 * 1000) + +/* LAN9252 */ +#define CP500_LAN9252_HZ (10 * 1000 * 1000) #define CP500_IS_CP035(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP035) #define CP500_IS_CP505(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP505) @@ -51,29 +78,56 @@ struct cp500_dev_info { off_t offset; size_t size; + unsigned int msix; }; struct cp500_devs { struct cp500_dev_info startup; + struct cp500_dev_info spi; struct cp500_dev_info i2c; + struct cp500_dev_info fan; + struct cp500_dev_info batt; + struct cp500_dev_info uart0_rfb; + struct cp500_dev_info uart1_dbg; + struct cp500_dev_info uart2_si1; }; /* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ static struct cp500_devs cp035_devices = { .startup = { 0x0000, SZ_4K }, + .spi = { 0x1000, SZ_4K }, .i2c = { 0x4000, SZ_4K }, + .fan = { 0x9000, SZ_4K }, + .batt = { 0xA000, SZ_4K }, + .uart0_rfb = { 0xB000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart2_si1 = { 0xD000, SZ_4K, CP500_SI1_UART_MSIX }, }; /* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ static struct cp500_devs cp505_devices = { .startup = { 0x0000, SZ_4K }, + .spi = { 0x4000, SZ_4K }, .i2c = { 0x5000, SZ_4K }, + .fan = { 0x9000, SZ_4K }, + .batt = { 0xA000, SZ_4K }, + .uart0_rfb = { 0xB000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart2_si1 = { 0xD000, SZ_4K, CP500_SI1_UART_MSIX }, }; /* list of devices within FPGA of CP520 family (CP520, CP530) */ static struct cp500_devs cp520_devices = { - .startup = { 0x0000, SZ_4K }, - .i2c = { 0x5000, SZ_4K }, + .startup = { 0x0000, SZ_4K }, + .spi = { 0x4000, SZ_4K }, + .i2c = { 0x5000, SZ_4K }, + .fan = { 0x8000, SZ_4K }, + .batt = { 0x9000, SZ_4K }, + .uart0_rfb = { 0xC000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart1_dbg = { 0xD000, SZ_4K, CP500_DEBUG_UART_MSIX }, +}; + +struct cp500_nvmem { + struct nvmem_device *nvmem; + unsigned int offset; }; struct cp500 { @@ -85,18 +139,31 @@ struct cp500 { int minor; int build; } version; + struct notifier_block nvmem_notifier; + atomic_t nvmem_notified; /* system FPGA BAR */ resource_size_t sys_hwbase; + struct keba_spi_auxdev *spi; struct keba_i2c_auxdev *i2c; + struct keba_fan_auxdev *fan; + struct keba_batt_auxdev *batt; + struct keba_uart_auxdev *uart0_rfb; + struct keba_uart_auxdev *uart1_dbg; + struct keba_uart_auxdev *uart2_si1; /* ECM EtherCAT BAR */ resource_size_t ecm_hwbase; + /* NVMEM devices */ + struct cp500_nvmem nvmem_cpu; + struct cp500_nvmem nvmem_user; + void __iomem *system_startup_addr; }; /* I2C devices */ +#define CP500_EEPROM_ADDR 0x50 static struct i2c_board_info cp500_i2c_info[] = { { /* temperature sensor */ I2C_BOARD_INFO("emc1403", 0x4c), @@ -107,30 +174,66 @@ static struct i2c_board_info cp500_i2c_info[] = { * CP505 family: bridge board * CP520 family: carrier board */ - I2C_BOARD_INFO("24c32", 0x50), - .dev_name = CP500_HW_CPU_EEPROM_NAME, + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR), }, { /* interface board EEPROM */ - I2C_BOARD_INFO("24c32", 0x51), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 1), }, { /* * EEPROM (optional) * CP505 family: CPU board * CP520 family: MMI board */ - I2C_BOARD_INFO("24c32", 0x52), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 2), }, { /* extension module 0 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x53), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 3), }, { /* extension module 1 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x54), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 4), }, { /* extension module 2 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x55), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 5), }, { /* extension module 3 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x56), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 6), + } +}; + +/* SPI devices */ +static struct mtd_partition cp500_partitions[] = { + { + .name = "system-flash-parts", + .size = MTDPART_SIZ_FULL, + .offset = 0, + .mask_flags = 0 + } +}; +static const struct flash_platform_data cp500_w25q32 = { + .type = "w25q32", + .name = "system-flash", + .parts = cp500_partitions, + .nr_parts = ARRAY_SIZE(cp500_partitions), +}; +static const struct flash_platform_data cp500_m25p16 = { + .type = "m25p16", + .name = "system-flash", + .parts = cp500_partitions, + .nr_parts = ARRAY_SIZE(cp500_partitions), +}; +static struct spi_board_info cp500_spi_info[] = { + { /* system FPGA configuration bitstream flash */ + .modalias = "m25p80", + .platform_data = &cp500_m25p16, + .max_speed_hz = CP500_FLASH_HZ, + .chip_select = 0, + .mode = SPI_MODE_3, + }, { /* LAN9252 EtherCAT slave controller */ + .modalias = "lan9252", + .platform_data = NULL, + .max_speed_hz = CP500_LAN9252_HZ, + .chip_select = 1, + .mode = SPI_MODE_3, } }; @@ -229,7 +332,7 @@ static void cp500_i2c_release(struct device *dev) static int cp500_register_i2c(struct cp500 *cp500) { - int retval; + int ret; cp500->i2c = kzalloc(sizeof(*cp500->i2c), GFP_KERNEL); if (!cp500->i2c) @@ -251,30 +354,412 @@ static int cp500_register_i2c(struct cp500 *cp500) cp500->i2c->info_size = ARRAY_SIZE(cp500_i2c_info); cp500->i2c->info = cp500_i2c_info; - retval = auxiliary_device_init(&cp500->i2c->auxdev); - if (retval) { + ret = auxiliary_device_init(&cp500->i2c->auxdev); + if (ret) { kfree(cp500->i2c); cp500->i2c = NULL; - return retval; + return ret; } - retval = __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); - if (retval) { + ret = __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); + if (ret) { auxiliary_device_uninit(&cp500->i2c->auxdev); cp500->i2c = NULL; - return retval; + return ret; + } + + return 0; +} + +static void cp500_spi_release(struct device *dev) +{ + struct keba_spi_auxdev *spi = + container_of(dev, struct keba_spi_auxdev, auxdev.dev); + + kfree(spi); +} + +static int cp500_register_spi(struct cp500 *cp500, u8 esc_type) +{ + int info_size; + int ret; + + cp500->spi = kzalloc(sizeof(*cp500->spi), GFP_KERNEL); + if (!cp500->spi) + return -ENOMEM; + + if (CP500_IS_CP035(cp500)) + cp500_spi_info[0].platform_data = &cp500_w25q32; + if (esc_type == CP500_EEPROM_ESC_LAN9252) + info_size = ARRAY_SIZE(cp500_spi_info); + else + info_size = ARRAY_SIZE(cp500_spi_info) - 1; + + cp500->spi->auxdev.name = "spi"; + cp500->spi->auxdev.id = 0; + cp500->spi->auxdev.dev.release = cp500_spi_release; + cp500->spi->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->spi->io = (struct resource) { + /* SPI register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->spi.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->spi.offset + + cp500->devs->spi.size - 1, + .flags = IORESOURCE_MEM, + }; + cp500->spi->info_size = info_size; + cp500->spi->info = cp500_spi_info; + + ret = auxiliary_device_init(&cp500->spi->auxdev); + if (ret) { + kfree(cp500->spi); + cp500->spi = NULL; + + return ret; + } + ret = __auxiliary_device_add(&cp500->spi->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->spi->auxdev); + cp500->spi = NULL; + + return ret; + } + + return 0; +} + +static void cp500_fan_release(struct device *dev) +{ + struct keba_fan_auxdev *fan = + container_of(dev, struct keba_fan_auxdev, auxdev.dev); + + kfree(fan); +} + +static int cp500_register_fan(struct cp500 *cp500) +{ + int ret; + + cp500->fan = kzalloc(sizeof(*cp500->fan), GFP_KERNEL); + if (!cp500->fan) + return -ENOMEM; + + cp500->fan->auxdev.name = "fan"; + cp500->fan->auxdev.id = 0; + cp500->fan->auxdev.dev.release = cp500_fan_release; + cp500->fan->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->fan->io = (struct resource) { + /* fan register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->fan.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->fan.offset + + cp500->devs->fan.size - 1, + .flags = IORESOURCE_MEM, + }; + + ret = auxiliary_device_init(&cp500->fan->auxdev); + if (ret) { + kfree(cp500->fan); + cp500->fan = NULL; + + return ret; + } + ret = __auxiliary_device_add(&cp500->fan->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->fan->auxdev); + cp500->fan = NULL; + + return ret; + } + + return 0; +} + +static void cp500_batt_release(struct device *dev) +{ + struct keba_batt_auxdev *fan = + container_of(dev, struct keba_batt_auxdev, auxdev.dev); + + kfree(fan); +} + +static int cp500_register_batt(struct cp500 *cp500) +{ + int ret; + + cp500->batt = kzalloc(sizeof(*cp500->batt), GFP_KERNEL); + if (!cp500->batt) + return -ENOMEM; + + cp500->batt->auxdev.name = "batt"; + cp500->batt->auxdev.id = 0; + cp500->batt->auxdev.dev.release = cp500_batt_release; + cp500->batt->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->batt->io = (struct resource) { + /* battery register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->batt.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->batt.offset + + cp500->devs->batt.size - 1, + .flags = IORESOURCE_MEM, + }; + + ret = auxiliary_device_init(&cp500->batt->auxdev); + if (ret) { + kfree(cp500->batt); + cp500->batt = NULL; + + return ret; + } + ret = __auxiliary_device_add(&cp500->batt->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->batt->auxdev); + cp500->batt = NULL; + + return ret; } return 0; } +static void cp500_uart_release(struct device *dev) +{ + struct keba_uart_auxdev *uart = + container_of(dev, struct keba_uart_auxdev, auxdev.dev); + + kfree(uart); +} + +static int cp500_register_uart(struct cp500 *cp500, + struct keba_uart_auxdev **uart, const char *name, + struct cp500_dev_info *info, unsigned int irq) +{ + int ret; + + *uart = kzalloc(sizeof(**uart), GFP_KERNEL); + if (!*uart) + return -ENOMEM; + + (*uart)->auxdev.name = name; + (*uart)->auxdev.id = 0; + (*uart)->auxdev.dev.release = cp500_uart_release; + (*uart)->auxdev.dev.parent = &cp500->pci_dev->dev; + (*uart)->io = (struct resource) { + /* UART register area */ + .start = (resource_size_t) cp500->sys_hwbase + info->offset, + .end = (resource_size_t) cp500->sys_hwbase + info->offset + + info->size - 1, + .flags = IORESOURCE_MEM, + }; + (*uart)->irq = irq; + + ret = auxiliary_device_init(&(*uart)->auxdev); + if (ret) { + kfree(*uart); + *uart = NULL; + + return ret; + } + ret = __auxiliary_device_add(&(*uart)->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&(*uart)->auxdev); + *uart = NULL; + + return ret; + } + + return 0; +} + +static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct cp500_nvmem *nvmem = priv; + int ret; + + ret = nvmem_device_read(nvmem->nvmem, nvmem->offset + offset, bytes, + val); + if (ret != bytes) + return ret; + + return 0; +} + +static int cp500_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct cp500_nvmem *nvmem = priv; + int ret; + + ret = nvmem_device_write(nvmem->nvmem, nvmem->offset + offset, bytes, + val); + if (ret != bytes) + return ret; + + return 0; +} + +static int cp500_nvmem_register(struct cp500 *cp500, struct nvmem_device *nvmem) +{ + struct device *dev = &cp500->pci_dev->dev; + struct nvmem_config nvmem_config = {}; + struct nvmem_device *tmp; + + /* + * The main EEPROM of CP500 devices is logically split into two EEPROMs. + * The first logical EEPROM with 3 kB contains the type label which is + * programmed during production of the device. The second logical EEPROM + * with 1 kB is not programmed during production and can be used for + * arbitrary user data. + */ + + nvmem_config.dev = dev; + nvmem_config.owner = THIS_MODULE; + nvmem_config.id = NVMEM_DEVID_NONE; + nvmem_config.type = NVMEM_TYPE_EEPROM; + nvmem_config.root_only = true; + nvmem_config.reg_read = cp500_nvmem_read; + nvmem_config.reg_write = cp500_nvmem_write; + + cp500->nvmem_cpu.nvmem = nvmem; + cp500->nvmem_cpu.offset = CP500_EEPROM_CPU_OFFSET; + nvmem_config.name = CP500_EEPROM_CPU_NAME; + nvmem_config.size = CP500_EEPROM_CPU_SIZE; + nvmem_config.priv = &cp500->nvmem_cpu; + tmp = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + cp500->nvmem_user.nvmem = nvmem; + cp500->nvmem_user.offset = CP500_EEPROM_USER_OFFSET; + nvmem_config.name = CP500_EEPROM_USER_NAME; + nvmem_config.size = CP500_EEPROM_USER_SIZE; + nvmem_config.priv = &cp500->nvmem_user; + tmp = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + return 0; +} + +static int cp500_nvmem_match(struct device *dev, const void *data) +{ + const struct cp500 *cp500 = data; + struct i2c_client *client; + + /* match only CPU EEPROM below the cp500 device */ + dev = dev->parent; + client = i2c_verify_client(dev); + if (!client || client->addr != CP500_EEPROM_ADDR) + return 0; + while ((dev = dev->parent)) + if (dev == &cp500->pci_dev->dev) + return 1; + + return 0; +} + +static void cp500_devm_nvmem_put(void *data) +{ + struct nvmem_device *nvmem = data; + + nvmem_device_put(nvmem); +} + +static int cp500_nvmem(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct nvmem_device *nvmem; + struct cp500 *cp500; + struct device *dev; + int notified; + u8 esc_type; + int ret; + + if (action != NVMEM_ADD) + return NOTIFY_DONE; + cp500 = container_of(nb, struct cp500, nvmem_notifier); + dev = &cp500->pci_dev->dev; + + /* process CPU EEPROM content only once */ + notified = atomic_read(&cp500->nvmem_notified); + if (notified) + return NOTIFY_DONE; + nvmem = nvmem_device_find(cp500, cp500_nvmem_match); + if (IS_ERR_OR_NULL(nvmem)) + return NOTIFY_DONE; + if (!atomic_try_cmpxchg_relaxed(&cp500->nvmem_notified, ¬ified, 1)) { + nvmem_device_put(nvmem); + + return NOTIFY_DONE; + } + + ret = devm_add_action_or_reset(dev, cp500_devm_nvmem_put, nvmem); + if (ret) + return ret; + + ret = cp500_nvmem_register(cp500, nvmem); + if (ret) + return ret; + + ret = nvmem_device_read(nvmem, CP500_EEPROM_DA_OFFSET, sizeof(esc_type), + (void *)&esc_type); + if (ret != sizeof(esc_type)) { + dev_warn(dev, "Failed to read device assembly!\n"); + + return NOTIFY_DONE; + } + esc_type &= CP500_EEPROM_DA_ESC_TYPE_MASK; + + if (cp500_register_spi(cp500, esc_type)) + dev_warn(dev, "Failed to register SPI!\n"); + + return NOTIFY_OK; +} + static void cp500_register_auxiliary_devs(struct cp500 *cp500) { struct device *dev = &cp500->pci_dev->dev; + u8 present = ioread8(cp500->system_startup_addr + CP500_PRESENT_REG); if (cp500_register_i2c(cp500)) - dev_warn(dev, "Failed to register i2c!\n"); + dev_warn(dev, "Failed to register I2C!\n"); + if (present & CP500_PRESENT_FAN0) + if (cp500_register_fan(cp500)) + dev_warn(dev, "Failed to register fan!\n"); + if (cp500_register_batt(cp500)) + dev_warn(dev, "Failed to register battery!\n"); + if (cp500->devs->uart0_rfb.size && + cp500->devs->uart0_rfb.msix < cp500->msix_num) { + int irq = pci_irq_vector(cp500->pci_dev, + cp500->devs->uart0_rfb.msix); + + if (cp500_register_uart(cp500, &cp500->uart0_rfb, "rs485-uart", + &cp500->devs->uart0_rfb, irq)) + dev_warn(dev, "Failed to register RFB UART!\n"); + } + if (cp500->devs->uart1_dbg.size && + cp500->devs->uart1_dbg.msix < cp500->msix_num) { + int irq = pci_irq_vector(cp500->pci_dev, + cp500->devs->uart1_dbg.msix); + + if (cp500_register_uart(cp500, &cp500->uart1_dbg, "rs232-uart", + &cp500->devs->uart1_dbg, irq)) + dev_warn(dev, "Failed to register debug UART!\n"); + } + if (cp500->devs->uart2_si1.size && + cp500->devs->uart2_si1.msix < cp500->msix_num) { + int irq = pci_irq_vector(cp500->pci_dev, + cp500->devs->uart2_si1.msix); + + if (cp500_register_uart(cp500, &cp500->uart2_si1, "uart", + &cp500->devs->uart2_si1, irq)) + dev_warn(dev, "Failed to register SI1 UART!\n"); + } } static void cp500_unregister_dev(struct auxiliary_device *auxdev) @@ -285,11 +770,34 @@ static void cp500_unregister_dev(struct auxiliary_device *auxdev) static void cp500_unregister_auxiliary_devs(struct cp500 *cp500) { - + if (cp500->spi) { + cp500_unregister_dev(&cp500->spi->auxdev); + cp500->spi = NULL; + } if (cp500->i2c) { cp500_unregister_dev(&cp500->i2c->auxdev); cp500->i2c = NULL; } + if (cp500->fan) { + cp500_unregister_dev(&cp500->fan->auxdev); + cp500->fan = NULL; + } + if (cp500->batt) { + cp500_unregister_dev(&cp500->batt->auxdev); + cp500->batt = NULL; + } + if (cp500->uart0_rfb) { + cp500_unregister_dev(&cp500->uart0_rfb->auxdev); + cp500->uart0_rfb = NULL; + } + if (cp500->uart1_dbg) { + cp500_unregister_dev(&cp500->uart1_dbg->auxdev); + cp500->uart1_dbg = NULL; + } + if (cp500->uart2_si1) { + cp500_unregister_dev(&cp500->uart2_si1->auxdev); + cp500->uart2_si1 = NULL; + } } static irqreturn_t cp500_axi_handler(int irq, void *dev) @@ -396,15 +904,21 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) pci_set_drvdata(pci_dev, cp500); + cp500->nvmem_notifier.notifier_call = cp500_nvmem; + ret = nvmem_register_notifier(&cp500->nvmem_notifier); + if (ret != 0) + goto out_free_irq; ret = cp500_enable(cp500); if (ret != 0) - goto out_free_irq; + goto out_unregister_nvmem; cp500_register_auxiliary_devs(cp500); return 0; +out_unregister_nvmem: + nvmem_unregister_notifier(&cp500->nvmem_notifier); out_free_irq: pci_free_irq_vectors(pci_dev); out_disable: @@ -422,6 +936,8 @@ static void cp500_remove(struct pci_dev *pci_dev) cp500_disable(cp500); + nvmem_unregister_notifier(&cp500->nvmem_notifier); + pci_set_drvdata(pci_dev, 0); pci_free_irq_vectors(pci_dev); diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c new file mode 100644 index 0000000000000..fc54afd1d05b8 --- /dev/null +++ b/drivers/misc/keba/lan9252.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) KEBA Industrial Automation Gmbh 2024 + * + * Driver for LAN9252 on KEBA CP500 devices + * + * This driver is used for updating the configuration of the LAN9252 controller + * on KEBA CP500 devices. The LAN9252 is connected over SPI, which is also named + * PDI. + */ + +#include +#include + +/* SPI commands */ +#define LAN9252_SPI_READ 0x3 +#define LAN9252_SPI_WRITE 0x2 + +struct lan9252_read_cmd { + u8 cmd; + u8 addr_0; + u8 addr_1; +} __packed; + +struct lan9252_write_cmd { + u8 cmd; + u8 addr_0; + u8 addr_1; + u32 data; +} __packed; + +/* byte test register */ +#define LAN9252_BYTE_TEST 0x64 +#define LAN9252_BYTE_TEST_VALUE 0x87654321 + +/* hardware configuration register */ +#define LAN9252_HW_CFG 0x74 +#define LAN9252_HW_CFG_READY 0x08000000 + +/* EtherCAT CSR interface data register */ +#define LAN9252_ECAT_CSR_DATA 0x300 + +/* EtherCAT CSR interface command register */ +#define LAN9252_ECAT_CSR_CMD 0x304 +#define LAN9252_ECAT_CSR_BUSY 0x80000000 +#define LAN9252_ECAT_CSR_READ 0x40000000 + +/* EtherCAT slave controller MII register */ +#define LAN9252_ESC_MII 0x510 +#define LAN9252_ESC_MII_BUSY 0x8000 +#define LAN9252_ESC_MII_CMD_ERR 0x4000 +#define LAN9252_ESC_MII_READ_ERR 0x2000 +#define LAN9252_ESC_MII_ERR_MASK (LAN9252_ESC_MII_CMD_ERR | \ + LAN9252_ESC_MII_READ_ERR) +#define LAN9252_ESC_MII_WRITE 0x0200 +#define LAN9252_ESC_MII_READ 0x0100 + +/* EtherCAT slave controller PHY address register */ +#define LAN9252_ESC_PHY_ADDR 0x512 + +/* EtherCAT slave controller PHY register address register */ +#define LAN9252_ESC_PHY_REG_ADDR 0x513 + +/* EtherCAT slave controller PHY data register */ +#define LAN9252_ESC_PHY_DATA 0x514 + +/* EtherCAT slave controller PDI access state register */ +#define LAN9252_ESC_MII_PDI 0x517 +#define LAN9252_ESC_MII_ACCESS_PDI 0x01 +#define LAN9252_ESC_MII_ACCESS_ECAT 0x00 + +/* PHY address */ +#define PHY_ADDRESS 2 + +#define SPI_RETRY_COUNT 10 +#define SPI_WAIT_US 100 +#define SPI_CSR_WAIT_US 500 + +static int lan9252_spi_read(struct spi_device *spi, u16 addr, u32 *data) +{ + struct lan9252_read_cmd cmd; + + cmd.cmd = LAN9252_SPI_READ; + cmd.addr_0 = (addr >> 8) & 0xFF; + cmd.addr_1 = addr & 0xFF; + + return spi_write_then_read(spi, (u8 *)&cmd, + sizeof(struct lan9252_read_cmd), + (u8 *)data, sizeof(u32)); +} + +static int lan9252_spi_write(struct spi_device *spi, u16 addr, u32 data) +{ + struct lan9252_write_cmd cmd; + + cmd.cmd = LAN9252_SPI_WRITE; + cmd.addr_0 = (addr >> 8) & 0xFF; + cmd.addr_1 = addr & 0xFF; + cmd.data = data; + + return spi_write(spi, (u8 *)&cmd, sizeof(struct lan9252_write_cmd)); +} + +static bool lan9252_init(struct spi_device *spi) +{ + u32 data; + int ret; + + ret = lan9252_spi_read(spi, LAN9252_BYTE_TEST, &data); + if (ret || data != LAN9252_BYTE_TEST_VALUE) + return false; + + ret = lan9252_spi_read(spi, LAN9252_HW_CFG, &data); + if (ret || !(data & LAN9252_HW_CFG_READY)) + return false; + + return true; +} + +static u8 lan9252_esc_get_size(u16 addr) +{ + if (addr == LAN9252_ESC_MII || addr == LAN9252_ESC_PHY_DATA) + return 2; + + return 1; +} + +static int lan9252_esc_wait(struct spi_device *spi) +{ + ktime_t timeout = ktime_add_us(ktime_get(), SPI_WAIT_US); + u32 data; + int ret; + + /* wait while CSR command is busy */ + for (;;) { + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); + if (ret) + return ret; + if (!(data & LAN9252_ECAT_CSR_BUSY)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); + if (ret) + return ret; + break; + } + } + + return (!(data & LAN9252_ECAT_CSR_BUSY)) ? 0 : -ETIMEDOUT; +} + +static int lan9252_esc_read(struct spi_device *spi, u16 addr, u32 *data) +{ + u32 csr_cmd; + u8 size; + int ret; + + size = lan9252_esc_get_size(addr); + csr_cmd = LAN9252_ECAT_CSR_BUSY | LAN9252_ECAT_CSR_READ; + csr_cmd |= (size << 16) | addr; + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); + if (ret) + return ret; + + ret = lan9252_esc_wait(spi); + if (ret) + return ret; + + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_DATA, data); + if (ret) + return ret; + + return 0; +} + +static int lan9252_esc_write(struct spi_device *spi, u16 addr, u32 data) +{ + u32 csr_cmd; + u8 size; + int ret; + + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_DATA, data); + if (ret) + return ret; + + size = lan9252_esc_get_size(addr); + csr_cmd = LAN9252_ECAT_CSR_BUSY; + csr_cmd |= (size << 16) | addr; + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); + if (ret) + return ret; + + ret = lan9252_esc_wait(spi); + if (ret) + return ret; + + return 0; +} + +static int lan9252_access_mii(struct spi_device *spi, bool access) +{ + u32 data; + + if (access) + data = LAN9252_ESC_MII_ACCESS_PDI; + else + data = LAN9252_ESC_MII_ACCESS_ECAT; + + return lan9252_esc_write(spi, LAN9252_ESC_MII_PDI, data); +} + +static int lan9252_mii_wait(struct spi_device *spi) +{ + ktime_t timeout = ktime_add_us(ktime_get(), SPI_CSR_WAIT_US); + u32 data; + int ret; + + /* wait while MII control state machine is busy */ + for (;;) { + ret = lan9252_esc_read(spi, LAN9252_ESC_MII, &data); + if (ret) + return ret; + if (data & LAN9252_ESC_MII_ERR_MASK) + return -EIO; + if (!(data & LAN9252_ESC_MII_BUSY)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret = lan9252_esc_read(spi, LAN9252_ESC_MII, &data); + if (ret) + return ret; + if (data & LAN9252_ESC_MII_ERR_MASK) + return -EIO; + break; + } + } + + return (!(data & LAN9252_ESC_MII_BUSY)) ? 0 : -ETIMEDOUT; +} + +static int lan9252_mii_read(struct spi_device *spi, u8 phy_addr, u8 reg_addr, + u32 *data) +{ + int ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); + if (ret) + return ret; + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); + if (ret) + return ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_READ); + if (ret) + return ret; + + ret = lan9252_mii_wait(spi); + if (ret) + return ret; + + return lan9252_esc_read(spi, LAN9252_ESC_PHY_DATA, data); +} + +static int lan9252_mii_write(struct spi_device *spi, u8 phy_addr, u8 reg_addr, + u32 data) +{ + int ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); + if (ret) + return ret; + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); + if (ret) + return ret; + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_DATA, data); + if (ret) + return ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_WRITE); + if (ret) + return ret; + + return lan9252_mii_wait(spi); +} + +static int lan9252_probe(struct spi_device *spi) +{ + u32 data; + int retry = SPI_RETRY_COUNT; + int ret; + + /* execute specified initialization sequence */ + while (retry && !lan9252_init(spi)) + retry--; + if (retry == 0) { + dev_err(&spi->dev, + "Can't initialize LAN9252 SPI communication!"); + return -EIO; + } + + /* enable access to MII management for PDI */ + ret = lan9252_access_mii(spi, true); + if (ret) { + dev_err(&spi->dev, "Can't enable access to MII management!"); + return ret; + } + + /* + * check PHY configuration and configure if necessary + * - full duplex + * - auto negotiation disabled + * - 100 Mbps + */ + ret = lan9252_mii_read(spi, PHY_ADDRESS, MII_BMCR, &data); + if (ret) { + dev_err(&spi->dev, "Can't read LAN9252 configuration!"); + goto out; + } + if (!(data & BMCR_FULLDPLX) || (data & BMCR_ANENABLE) || + !(data & BMCR_SPEED100)) { + /* + */ + data &= ~(BMCR_ANENABLE); + data |= (BMCR_FULLDPLX | BMCR_SPEED100); + ret = lan9252_mii_write(spi, PHY_ADDRESS, MII_BMCR, data); + if (ret) + dev_err(&spi->dev, + "Can't write LAN9252 configuration!"); + } + + dev_info(&spi->dev, "LAN9252 PHY configuration"); + +out: + /* disable access to MII management for PDI */ + lan9252_access_mii(spi, false); + + return ret; +} + +static const struct spi_device_id lan9252_id[] = { + {"lan9252"}, + {} +}; +MODULE_DEVICE_TABLE(spi, lan9252_id); + +static struct spi_driver lan9252_driver = { + .driver = { + .name = "lan9252", + }, + .probe = lan9252_probe, + .id_table = lan9252_id, +}; +module_spi_driver(lan9252_driver); + +MODULE_AUTHOR("Petar Bojanic "); +MODULE_AUTHOR("Gerhard Engleder "); +MODULE_DESCRIPTION("KEBA LAN9252 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 62ba015254797..376047beea3d6 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -445,7 +445,7 @@ static void lkdtm_FAM_BOUNDS(void) pr_err("FAIL: survived access of invalid flexible array member index!\n"); - if (!__has_attribute(__counted_by__)) + if (!IS_ENABLED(CONFIG_CC_HAS_COUNTED_BY)) pr_warn("This is expected since this %s was built with a compiler that does not support __counted_by\n", lkdtm_kernel_info); else if (IS_ENABLED(CONFIG_UBSAN_BOUNDS)) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 5576146ab13bc..718ec5d81d940 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -145,8 +145,8 @@ ssize_t __mei_cl_send_timeout(struct mei_cl *cl, const u8 *buf, size_t length, u * @cl: host client * @buf: buffer to receive * @length: buffer length - * @mode: io mode * @vtag: virtual tag + * @mode: io mode * @timeout: recv timeout, 0 for infinite timeout * * Return: read size in bytes of < 0 on error diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 20a11b299bcd0..71f9994da2cc4 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -256,8 +256,6 @@ static int mei_vsc_hw_reset(struct mei_device *mei_dev, bool intr_enable) vsc_tp_reset(hw->tp); - vsc_tp_intr_disable(hw->tp); - return vsc_tp_init(hw->tp, mei_dev->dev); } diff --git a/drivers/misc/mei/vsc-fw-loader.c b/drivers/misc/mei/vsc-fw-loader.c index 9f129bc641f69..308b090d81bbb 100644 --- a/drivers/misc/mei/vsc-fw-loader.c +++ b/drivers/misc/mei/vsc-fw-loader.c @@ -317,28 +317,34 @@ static int vsc_identify_silicon(struct vsc_fw_loader *fw_loader) cmd->data.dump_mem.addr = cpu_to_le32(VSC_EFUSE_ADDR); cmd->data.dump_mem.len = cpu_to_le16(sizeof(__le32)); ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); - if (ret) - return ret; - if (ack->token == VSC_TOKEN_ERROR) - return -EINVAL; + if (ret || ack->token == VSC_TOKEN_ERROR) { + dev_err(fw_loader->dev, "CMD_DUMP_MEM error %d token %d\n", ret, ack->token); + return ret ?: -EINVAL; + } cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); cmd->cmd_id = VSC_CMD_GET_CONT; ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); - if (ret) - return ret; - if (ack->token != VSC_TOKEN_DUMP_RESP) - return -EINVAL; + if (ret || ack->token != VSC_TOKEN_DUMP_RESP) { + dev_err(fw_loader->dev, "CMD_GETCONT error %d token %d\n", ret, ack->token); + return ret ?: -EINVAL; + } version = FIELD_GET(VSC_MAINSTEPPING_VERSION_MASK, ack->payload[0]); sub_version = FIELD_GET(VSC_SUBSTEPPING_VERSION_MASK, ack->payload[0]); - if (version != VSC_MAINSTEPPING_VERSION_A) + if (version != VSC_MAINSTEPPING_VERSION_A) { + dev_err(fw_loader->dev, "mainstepping mismatch expected %d got %d\n", + VSC_MAINSTEPPING_VERSION_A, version); return -EINVAL; + } if (sub_version != VSC_SUBSTEPPING_VERSION_0 && - sub_version != VSC_SUBSTEPPING_VERSION_1) + sub_version != VSC_SUBSTEPPING_VERSION_1) { + dev_err(fw_loader->dev, "substepping %d is out of supported range %d - %d\n", + sub_version, VSC_SUBSTEPPING_VERSION_0, VSC_SUBSTEPPING_VERSION_1); return -EINVAL; + } dev_info(fw_loader->dev, "silicon stepping version is %u:%u\n", version, sub_version); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 1618cca9a7317..107177b05dcd4 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -364,8 +364,6 @@ void vsc_tp_reset(struct vsc_tp *tp) gpiod_set_value_cansleep(tp->wakeupfw, 1); atomic_set(&tp->assert_cnt, 0); - - enable_irq(tp->spi->irq); } EXPORT_SYMBOL_NS_GPL(vsc_tp_reset, VSC_TP); diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c index 405180d47d9bf..07520d6e6dc55 100644 --- a/drivers/misc/ocxl/sysfs.c +++ b/drivers/misc/ocxl/sysfs.c @@ -125,7 +125,7 @@ static const struct vm_operations_struct global_mmio_vmops = { }; static int global_mmio_mmap(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, struct vm_area_struct *vma) { struct ocxl_afu *afu = to_afu(kobj_to_dev(kobj)); diff --git a/drivers/misc/rpmb-core.c b/drivers/misc/rpmb-core.c index bc68cde1a8bfd..2d653926cdbbb 100644 --- a/drivers/misc/rpmb-core.c +++ b/drivers/misc/rpmb-core.c @@ -13,7 +13,6 @@ #include static DEFINE_IDA(rpmb_ida); -static DEFINE_MUTEX(rpmb_mutex); /** * rpmb_dev_get() - increase rpmb device ref counter @@ -63,9 +62,7 @@ static void rpmb_dev_release(struct device *dev) { struct rpmb_dev *rdev = to_rpmb_dev(dev); - mutex_lock(&rpmb_mutex); - ida_simple_remove(&rpmb_ida, rdev->id); - mutex_unlock(&rpmb_mutex); + ida_free(&rpmb_ida, rdev->id); kfree(rdev->descr.dev_id); kfree(rdev); } @@ -175,9 +172,7 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, goto err_free_rdev; } - mutex_lock(&rpmb_mutex); - ret = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL); - mutex_unlock(&rpmb_mutex); + ret = ida_alloc(&rpmb_ida, GFP_KERNEL); if (ret < 0) goto err_free_dev_id; rdev->id = ret; diff --git a/drivers/misc/ti-st/Kconfig b/drivers/misc/ti-st/Kconfig deleted file mode 100644 index 1503a6496f632..0000000000000 --- a/drivers/misc/ti-st/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# TI's shared transport line discipline and the protocol -# drivers (BT, FM and GPS) -# -menu "Texas Instruments shared transport line discipline" -config TI_ST - tristate "Shared transport core driver" - depends on NET && TTY - depends on GPIOLIB || COMPILE_TEST - select FW_LOADER - help - This enables the shared transport core driver for TI - BT / FM and GPS combo chips. This enables protocol drivers - to register themselves with core and send data, the responses - are returned to relevant protocol drivers based on their - packet types. - -endmenu diff --git a/drivers/misc/ti-st/Makefile b/drivers/misc/ti-st/Makefile deleted file mode 100644 index 93393100952ed..0000000000000 --- a/drivers/misc/ti-st/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for TI's shared transport line discipline -# and its protocol drivers (BT, FM, GPS) -# -obj-$(CONFIG_TI_ST) += st_drv.o -st_drv-objs := st_core.o st_kim.o st_ll.o diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c deleted file mode 100644 index b878431553abc..0000000000000 --- a/drivers/misc/ti-st/st_core.c +++ /dev/null @@ -1,918 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Shared Transport Line discipline driver Core - * This hooks up ST KIM driver and ST LL driver - * Copyright (C) 2009-2010 Texas Instruments - * Author: Pavan Savoy - */ - -#define pr_fmt(fmt) "(stc): " fmt -#include -#include -#include - -#include -#include - -#include -#include - -/* - * function pointer pointing to either, - * st_kim_recv during registration to receive fw download responses - * st_int_recv after registration to receive proto stack responses - */ -static void (*st_recv)(void *disc_data, const u8 *ptr, size_t count); - -/********************************************************************/ -static void add_channel_to_table(struct st_data_s *st_gdata, - struct st_proto_s *new_proto) -{ - pr_info("%s: id %d\n", __func__, new_proto->chnl_id); - /* list now has the channel id as index itself */ - st_gdata->list[new_proto->chnl_id] = new_proto; - st_gdata->is_registered[new_proto->chnl_id] = true; -} - -static void remove_channel_from_table(struct st_data_s *st_gdata, - struct st_proto_s *proto) -{ - pr_info("%s: id %d\n", __func__, proto->chnl_id); -/* st_gdata->list[proto->chnl_id] = NULL; */ - st_gdata->is_registered[proto->chnl_id] = false; -} - -/* - * called from KIM during firmware download. - * - * This is a wrapper function to tty->ops->write_room. - * It returns number of free space available in - * uart tx buffer. - */ -int st_get_uart_wr_room(struct st_data_s *st_gdata) -{ - if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { - pr_err("tty unavailable to perform write"); - return -1; - } - - return tty_write_room(st_gdata->tty); -} - -/* - * can be called in from - * -- KIM (during fw download) - * -- ST Core (during st_write) - * - * This is the internal write function - a wrapper - * to tty->ops->write - */ -int st_int_write(struct st_data_s *st_gdata, - const unsigned char *data, int count) -{ - struct tty_struct *tty; - if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { - pr_err("tty unavailable to perform write"); - return -EINVAL; - } - tty = st_gdata->tty; -#ifdef VERBOSE - print_hex_dump(KERN_DEBUG, "ops->write(tty, data, count); - -} - -/* - * push the skb received to relevant - * protocol stacks - */ -static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) -{ - pr_debug(" %s(prot:%d) ", __func__, chnl_id); - - if (unlikely - (st_gdata == NULL || st_gdata->rx_skb == NULL - || st_gdata->is_registered[chnl_id] == false)) { - pr_err("chnl_id %d not registered, no data to send?", - chnl_id); - kfree_skb(st_gdata->rx_skb); - return; - } - /* - * this cannot fail - * this shouldn't take long - * - should be just skb_queue_tail for the - * protocol stack driver - */ - if (likely(st_gdata->list[chnl_id]->recv != NULL)) { - if (unlikely - (st_gdata->list[chnl_id]->recv - (st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb) - != 0)) { - pr_err(" proto stack %d's ->recv failed", chnl_id); - kfree_skb(st_gdata->rx_skb); - return; - } - } else { - pr_err(" proto stack %d's ->recv null", chnl_id); - kfree_skb(st_gdata->rx_skb); - } - return; -} - -/* - * st_reg_complete - to call registration complete callbacks - * of all protocol stack drivers - * This function is being called with spin lock held, protocol drivers are - * only expected to complete their waits and do nothing more than that. - */ -static void st_reg_complete(struct st_data_s *st_gdata, int err) -{ - unsigned char i = 0; - pr_info(" %s ", __func__); - for (i = 0; i < ST_MAX_CHANNELS; i++) { - if (likely(st_gdata != NULL && - st_gdata->is_registered[i] == true && - st_gdata->list[i]->reg_complete_cb != NULL)) { - st_gdata->list[i]->reg_complete_cb - (st_gdata->list[i]->priv_data, err); - pr_info("protocol %d's cb sent %d\n", i, err); - if (err) { /* cleanup registered protocol */ - st_gdata->is_registered[i] = false; - if (st_gdata->protos_registered) - st_gdata->protos_registered--; - } - } - } -} - -static inline int st_check_data_len(struct st_data_s *st_gdata, - unsigned char chnl_id, int len) -{ - int room = skb_tailroom(st_gdata->rx_skb); - - pr_debug("len %d room %d", len, room); - - if (!len) { - /* - * Received packet has only packet header and - * has zero length payload. So, ask ST CORE to - * forward the packet to protocol driver (BT/FM/GPS) - */ - st_send_frame(chnl_id, st_gdata); - - } else if (len > room) { - /* - * Received packet's payload length is larger. - * We can't accommodate it in created skb. - */ - pr_err("Data length is too large len %d room %d", len, - room); - kfree_skb(st_gdata->rx_skb); - } else { - /* - * Packet header has non-zero payload length and - * we have enough space in created skb. Lets read - * payload data */ - st_gdata->rx_state = ST_W4_DATA; - st_gdata->rx_count = len; - return len; - } - - /* Change ST state to continue to process next packet */ - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_skb = NULL; - st_gdata->rx_count = 0; - st_gdata->rx_chnl = 0; - - return 0; -} - -/* - * st_wakeup_ack - internal function for action when wake-up ack - * received - */ -static inline void st_wakeup_ack(struct st_data_s *st_gdata, - unsigned char cmd) -{ - struct sk_buff *waiting_skb; - unsigned long flags = 0; - - spin_lock_irqsave(&st_gdata->lock, flags); - /* - * de-Q from waitQ and Q in txQ now that the - * chip is awake - */ - while ((waiting_skb = skb_dequeue(&st_gdata->tx_waitq))) - skb_queue_tail(&st_gdata->txq, waiting_skb); - - /* state forwarded to ST LL */ - st_ll_sleep_state(st_gdata, (unsigned long)cmd); - spin_unlock_irqrestore(&st_gdata->lock, flags); - - /* wake up to send the recently copied skbs from waitQ */ - st_tx_wakeup(st_gdata); -} - -/* - * st_int_recv - ST's internal receive function. - * Decodes received RAW data and forwards to corresponding - * client drivers (Bluetooth,FM,GPS..etc). - * This can receive various types of packets, - * HCI-Events, ACL, SCO, 4 types of HCI-LL PM packets - * CH-8 packets from FM, CH-9 packets from GPS cores. - */ -static void st_int_recv(void *disc_data, const u8 *ptr, size_t count) -{ - struct st_proto_s *proto; - unsigned short payload_len = 0; - int len = 0; - unsigned char type = 0; - unsigned char *plen; - struct st_data_s *st_gdata = (struct st_data_s *)disc_data; - unsigned long flags; - - if (st_gdata == NULL) { - pr_err(" received null from TTY "); - return; - } - - pr_debug("count %zu rx_state %ld" - "rx_count %ld", count, st_gdata->rx_state, - st_gdata->rx_count); - - spin_lock_irqsave(&st_gdata->lock, flags); - /* Decode received bytes here */ - while (count) { - if (st_gdata->rx_count) { - len = min_t(unsigned int, st_gdata->rx_count, count); - skb_put_data(st_gdata->rx_skb, ptr, len); - st_gdata->rx_count -= len; - count -= len; - ptr += len; - - if (st_gdata->rx_count) - continue; - - /* Check ST RX state machine , where are we? */ - switch (st_gdata->rx_state) { - /* Waiting for complete packet ? */ - case ST_W4_DATA: - pr_debug("Complete pkt received"); - /* - * Ask ST CORE to forward - * the packet to protocol driver - */ - st_send_frame(st_gdata->rx_chnl, st_gdata); - - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_skb = NULL; - continue; - /* parse the header to know details */ - case ST_W4_HEADER: - proto = st_gdata->list[st_gdata->rx_chnl]; - plen = - &st_gdata->rx_skb->data - [proto->offset_len_in_hdr]; - pr_debug("plen pointing to %x\n", *plen); - if (proto->len_size == 1) /* 1 byte len field */ - payload_len = *(unsigned char *)plen; - else if (proto->len_size == 2) - payload_len = - __le16_to_cpu(*(unsigned short *)plen); - else - pr_info("%s: invalid length " - "for id %d\n", - __func__, proto->chnl_id); - st_check_data_len(st_gdata, proto->chnl_id, - payload_len); - pr_debug("off %d, pay len %d\n", - proto->offset_len_in_hdr, payload_len); - continue; - } /* end of switch rx_state */ - } - - /* end of if rx_count */ - - /* - * Check first byte of packet and identify module - * owner (BT/FM/GPS) - */ - switch (*ptr) { - case LL_SLEEP_IND: - case LL_SLEEP_ACK: - case LL_WAKE_UP_IND: - pr_debug("PM packet"); - /* - * this takes appropriate action based on - * sleep state received -- - */ - st_ll_sleep_state(st_gdata, *ptr); - /* - * if WAKEUP_IND collides copy from waitq to txq - * and assume chip awake - */ - spin_unlock_irqrestore(&st_gdata->lock, flags); - if (st_ll_getstate(st_gdata) == ST_LL_AWAKE) - st_wakeup_ack(st_gdata, LL_WAKE_UP_ACK); - spin_lock_irqsave(&st_gdata->lock, flags); - - ptr++; - count--; - continue; - case LL_WAKE_UP_ACK: - pr_debug("PM packet"); - - spin_unlock_irqrestore(&st_gdata->lock, flags); - /* wake up ack received */ - st_wakeup_ack(st_gdata, *ptr); - spin_lock_irqsave(&st_gdata->lock, flags); - - ptr++; - count--; - continue; - /* Unknown packet? */ - default: - type = *ptr; - - /* - * Default case means non-HCILL packets, - * possibilities are packets for: - * (a) valid protocol - Supported Protocols within - * the ST_MAX_CHANNELS. - * (b) registered protocol - Checked by - * "st_gdata->list[type] == NULL)" are supported - * protocols only. - * Rules out any invalid protocol and - * unregistered protocols with channel ID < 16. - */ - - if ((type >= ST_MAX_CHANNELS) || - (st_gdata->list[type] == NULL)) { - pr_err("chip/interface misbehavior: " - "dropping frame starting " - "with 0x%02x\n", type); - goto done; - } - - st_gdata->rx_skb = alloc_skb( - st_gdata->list[type]->max_frame_size, - GFP_ATOMIC); - if (st_gdata->rx_skb == NULL) { - pr_err("out of memory: dropping\n"); - goto done; - } - - skb_reserve(st_gdata->rx_skb, - st_gdata->list[type]->reserve); - /* next 2 required for BT only */ - st_gdata->rx_skb->cb[0] = type; /*pkt_type*/ - st_gdata->rx_skb->cb[1] = 0; /*incoming*/ - st_gdata->rx_chnl = *ptr; - st_gdata->rx_state = ST_W4_HEADER; - st_gdata->rx_count = st_gdata->list[type]->hdr_len; - pr_debug("rx_count %ld\n", st_gdata->rx_count); - } - ptr++; - count--; - } -done: - spin_unlock_irqrestore(&st_gdata->lock, flags); - pr_debug("done %s", __func__); - return; -} - -/* - * st_int_dequeue - internal de-Q function. - * If the previous data set was not written - * completely, return that skb which has the pending data. - * In normal cases, return top of txq. - */ -static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) -{ - struct sk_buff *returning_skb; - - pr_debug("%s", __func__); - if (st_gdata->tx_skb != NULL) { - returning_skb = st_gdata->tx_skb; - st_gdata->tx_skb = NULL; - return returning_skb; - } - return skb_dequeue(&st_gdata->txq); -} - -/* - * st_int_enqueue - internal Q-ing function. - * Will either Q the skb to txq or the tx_waitq - * depending on the ST LL state. - * If the chip is asleep, then Q it onto waitq and - * wakeup the chip. - * txq and waitq needs protection since the other contexts - * may be sending data, waking up chip. - */ -static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) -{ - unsigned long flags = 0; - - pr_debug("%s", __func__); - spin_lock_irqsave(&st_gdata->lock, flags); - - switch (st_ll_getstate(st_gdata)) { - case ST_LL_AWAKE: - pr_debug("ST LL is AWAKE, sending normally"); - skb_queue_tail(&st_gdata->txq, skb); - break; - case ST_LL_ASLEEP_TO_AWAKE: - skb_queue_tail(&st_gdata->tx_waitq, skb); - break; - case ST_LL_AWAKE_TO_ASLEEP: - pr_err("ST LL is illegal state(%ld)," - "purging received skb.", st_ll_getstate(st_gdata)); - dev_kfree_skb_irq(skb); - break; - case ST_LL_ASLEEP: - skb_queue_tail(&st_gdata->tx_waitq, skb); - st_ll_wakeup(st_gdata); - break; - default: - pr_err("ST LL is illegal state(%ld)," - "purging received skb.", st_ll_getstate(st_gdata)); - dev_kfree_skb_irq(skb); - break; - } - - spin_unlock_irqrestore(&st_gdata->lock, flags); - pr_debug("done %s", __func__); - return; -} - -/* - * internal wakeup function - * called from either - * - TTY layer when write's finished - * - st_write (in context of the protocol stack) - */ -static void work_fn_write_wakeup(struct work_struct *work) -{ - struct st_data_s *st_gdata = container_of(work, struct st_data_s, - work_write_wakeup); - - st_tx_wakeup((void *)st_gdata); -} -void st_tx_wakeup(struct st_data_s *st_data) -{ - struct sk_buff *skb; - unsigned long flags; /* for irq save flags */ - pr_debug("%s", __func__); - /* check for sending & set flag sending here */ - if (test_and_set_bit(ST_TX_SENDING, &st_data->tx_state)) { - pr_debug("ST already sending"); - /* keep sending */ - set_bit(ST_TX_WAKEUP, &st_data->tx_state); - return; - /* TX_WAKEUP will be checked in another - * context - */ - } - do { /* come back if st_tx_wakeup is set */ - /* woke-up to write */ - clear_bit(ST_TX_WAKEUP, &st_data->tx_state); - while ((skb = st_int_dequeue(st_data))) { - int len; - spin_lock_irqsave(&st_data->lock, flags); - /* enable wake-up from TTY */ - set_bit(TTY_DO_WRITE_WAKEUP, &st_data->tty->flags); - len = st_int_write(st_data, skb->data, skb->len); - skb_pull(skb, len); - /* if skb->len = len as expected, skb->len=0 */ - if (skb->len) { - /* would be the next skb to be sent */ - st_data->tx_skb = skb; - spin_unlock_irqrestore(&st_data->lock, flags); - break; - } - dev_kfree_skb_irq(skb); - spin_unlock_irqrestore(&st_data->lock, flags); - } - /* if wake-up is set in another context- restart sending */ - } while (test_bit(ST_TX_WAKEUP, &st_data->tx_state)); - - /* clear flag sending */ - clear_bit(ST_TX_SENDING, &st_data->tx_state); -} - -/********************************************************************/ -/* functions called from ST KIM -*/ -void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf) -{ - seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n", - st_gdata->protos_registered, - st_gdata->is_registered[0x04] == true ? 'R' : 'U', - st_gdata->is_registered[0x08] == true ? 'R' : 'U', - st_gdata->is_registered[0x09] == true ? 'R' : 'U'); -} - -/********************************************************************/ -/* - * functions called from protocol stack drivers - * to be EXPORT-ed - */ -long st_register(struct st_proto_s *new_proto) -{ - struct st_data_s *st_gdata; - long err = 0; - unsigned long flags = 0; - - st_kim_ref(&st_gdata, 0); - if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL - || new_proto->reg_complete_cb == NULL) { - pr_err("gdata/new_proto/recv or reg_complete_cb not ready"); - return -EINVAL; - } - - if (new_proto->chnl_id >= ST_MAX_CHANNELS) { - pr_err("chnl_id %d not supported", new_proto->chnl_id); - return -EPROTONOSUPPORT; - } - - if (st_gdata->is_registered[new_proto->chnl_id] == true) { - pr_err("chnl_id %d already registered", new_proto->chnl_id); - return -EALREADY; - } - - /* can be from process context only */ - spin_lock_irqsave(&st_gdata->lock, flags); - - if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) { - pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id); - /* fw download in progress */ - - add_channel_to_table(st_gdata, new_proto); - st_gdata->protos_registered++; - new_proto->write = st_write; - - set_bit(ST_REG_PENDING, &st_gdata->st_state); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EINPROGRESS; - } else if (st_gdata->protos_registered == ST_EMPTY) { - pr_info(" chnl_id list empty :%d ", new_proto->chnl_id); - set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); - st_recv = st_kim_recv; - - /* enable the ST LL - to set default chip state */ - st_ll_enable(st_gdata); - - /* release lock previously held - re-locked below */ - spin_unlock_irqrestore(&st_gdata->lock, flags); - - /* - * this may take a while to complete - * since it involves BT fw download - */ - err = st_kim_start(st_gdata->kim_data); - if (err != 0) { - clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); - if ((st_gdata->protos_registered != ST_EMPTY) && - (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_err(" KIM failure complete callback "); - spin_lock_irqsave(&st_gdata->lock, flags); - st_reg_complete(st_gdata, err); - spin_unlock_irqrestore(&st_gdata->lock, flags); - clear_bit(ST_REG_PENDING, &st_gdata->st_state); - } - return -EINVAL; - } - - spin_lock_irqsave(&st_gdata->lock, flags); - - clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); - st_recv = st_int_recv; - - /* - * this is where all pending registration - * are signalled to be complete by calling callback functions - */ - if ((st_gdata->protos_registered != ST_EMPTY) && - (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_debug(" call reg complete callback "); - st_reg_complete(st_gdata, 0); - } - clear_bit(ST_REG_PENDING, &st_gdata->st_state); - - /* - * check for already registered once more, - * since the above check is old - */ - if (st_gdata->is_registered[new_proto->chnl_id] == true) { - pr_err(" proto %d already registered ", - new_proto->chnl_id); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EALREADY; - } - - add_channel_to_table(st_gdata, new_proto); - st_gdata->protos_registered++; - new_proto->write = st_write; - spin_unlock_irqrestore(&st_gdata->lock, flags); - return err; - } - /* if fw is already downloaded & new stack registers protocol */ - else { - add_channel_to_table(st_gdata, new_proto); - st_gdata->protos_registered++; - new_proto->write = st_write; - - /* lock already held before entering else */ - spin_unlock_irqrestore(&st_gdata->lock, flags); - return err; - } -} -EXPORT_SYMBOL_GPL(st_register); - -/* - * to unregister a protocol - - * to be called from protocol stack driver - */ -long st_unregister(struct st_proto_s *proto) -{ - long err = 0; - unsigned long flags = 0; - struct st_data_s *st_gdata; - - pr_debug("%s: %d ", __func__, proto->chnl_id); - - st_kim_ref(&st_gdata, 0); - if (!st_gdata || proto->chnl_id >= ST_MAX_CHANNELS) { - pr_err(" chnl_id %d not supported", proto->chnl_id); - return -EPROTONOSUPPORT; - } - - spin_lock_irqsave(&st_gdata->lock, flags); - - if (st_gdata->is_registered[proto->chnl_id] == false) { - pr_err(" chnl_id %d not registered", proto->chnl_id); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EPROTONOSUPPORT; - } - - if (st_gdata->protos_registered) - st_gdata->protos_registered--; - - remove_channel_from_table(st_gdata, proto); - spin_unlock_irqrestore(&st_gdata->lock, flags); - - if ((st_gdata->protos_registered == ST_EMPTY) && - (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_info(" all chnl_ids unregistered "); - - /* stop traffic on tty */ - if (st_gdata->tty) { - tty_ldisc_flush(st_gdata->tty); - stop_tty(st_gdata->tty); - } - - /* all chnl_ids now unregistered */ - st_kim_stop(st_gdata->kim_data); - /* disable ST LL */ - st_ll_disable(st_gdata); - } - return err; -} - -/* - * called in protocol stack drivers - * via the write function pointer - */ -long st_write(struct sk_buff *skb) -{ - struct st_data_s *st_gdata; - long len; - - st_kim_ref(&st_gdata, 0); - if (unlikely(skb == NULL || st_gdata == NULL - || st_gdata->tty == NULL)) { - pr_err("data/tty unavailable to perform write"); - return -EINVAL; - } - - pr_debug("%d to be written", skb->len); - len = skb->len; - - /* st_ll to decide where to enqueue the skb */ - st_int_enqueue(st_gdata, skb); - /* wake up */ - st_tx_wakeup(st_gdata); - - /* return number of bytes written */ - return len; -} - -/* for protocols making use of shared transport */ -EXPORT_SYMBOL_GPL(st_unregister); - -/********************************************************************/ -/* - * functions called from TTY layer - */ -static int st_tty_open(struct tty_struct *tty) -{ - struct st_data_s *st_gdata; - pr_info("%s ", __func__); - - st_kim_ref(&st_gdata, 0); - st_gdata->tty = tty; - tty->disc_data = st_gdata; - - /* don't do an wakeup for now */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - /* mem already allocated - */ - tty->receive_room = 65536; - /* Flush any pending characters in the driver and discipline. */ - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - /* - * signal to UIM via KIM that - - * installation of N_TI_WL ldisc is complete - */ - st_kim_complete(st_gdata->kim_data); - pr_debug("done %s", __func__); - - return 0; -} - -static void st_tty_close(struct tty_struct *tty) -{ - unsigned char i; - unsigned long flags; - struct st_data_s *st_gdata = tty->disc_data; - - pr_info("%s ", __func__); - - /* - * TODO: - * if a protocol has been registered & line discipline - * un-installed for some reason - what should be done ? - */ - spin_lock_irqsave(&st_gdata->lock, flags); - for (i = ST_BT; i < ST_MAX_CHANNELS; i++) { - if (st_gdata->is_registered[i] == true) - pr_err("%d not un-registered", i); - st_gdata->list[i] = NULL; - st_gdata->is_registered[i] = false; - } - st_gdata->protos_registered = 0; - spin_unlock_irqrestore(&st_gdata->lock, flags); - /* - * signal to UIM via KIM that - - * N_TI_WL ldisc is un-installed - */ - st_kim_complete(st_gdata->kim_data); - st_gdata->tty = NULL; - /* Flush any pending characters in the driver and discipline. */ - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - - spin_lock_irqsave(&st_gdata->lock, flags); - /* empty out txq and tx_waitq */ - skb_queue_purge(&st_gdata->txq); - skb_queue_purge(&st_gdata->tx_waitq); - /* reset the TTY Rx states of ST */ - st_gdata->rx_count = 0; - st_gdata->rx_state = ST_W4_PACKET_TYPE; - kfree_skb(st_gdata->rx_skb); - st_gdata->rx_skb = NULL; - spin_unlock_irqrestore(&st_gdata->lock, flags); - - pr_debug("%s: done ", __func__); -} - -static void st_tty_receive(struct tty_struct *tty, const u8 *data, - const u8 *tty_flags, size_t count) -{ -#ifdef VERBOSE - print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE, - 16, 1, data, count, 0); -#endif - - /* - * if fw download is in progress then route incoming data - * to KIM for validation - */ - st_recv(tty->disc_data, data, count); - pr_debug("done %s", __func__); -} - -/* - * wake-up function called in from the TTY layer - * inside the internal wakeup function will be called - */ -static void st_tty_wakeup(struct tty_struct *tty) -{ - struct st_data_s *st_gdata = tty->disc_data; - pr_debug("%s ", __func__); - /* don't do an wakeup for now */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - /* - * schedule the internal wakeup instead of calling directly to - * avoid lockup (port->lock needed in tty->ops->write is - * already taken here - */ - schedule_work(&st_gdata->work_write_wakeup); -} - -static void st_tty_flush_buffer(struct tty_struct *tty) -{ - struct st_data_s *st_gdata = tty->disc_data; - pr_debug("%s ", __func__); - - kfree_skb(st_gdata->tx_skb); - st_gdata->tx_skb = NULL; - - tty_driver_flush_buffer(tty); - return; -} - -static struct tty_ldisc_ops st_ldisc_ops = { - .num = N_TI_WL, - .name = "n_st", - .open = st_tty_open, - .close = st_tty_close, - .receive_buf = st_tty_receive, - .write_wakeup = st_tty_wakeup, - .flush_buffer = st_tty_flush_buffer, - .owner = THIS_MODULE -}; - -/********************************************************************/ -int st_core_init(struct st_data_s **core_data) -{ - struct st_data_s *st_gdata; - long err; - - err = tty_register_ldisc(&st_ldisc_ops); - if (err) { - pr_err("error registering %d line discipline %ld", - N_TI_WL, err); - return err; - } - pr_debug("registered n_shared line discipline"); - - st_gdata = kzalloc(sizeof(struct st_data_s), GFP_KERNEL); - if (!st_gdata) { - pr_err("memory allocation failed"); - err = -ENOMEM; - goto err_unreg_ldisc; - } - - /* Initialize ST TxQ and Tx waitQ queue head. All BT/FM/GPS module skb's - * will be pushed in this queue for actual transmission. - */ - skb_queue_head_init(&st_gdata->txq); - skb_queue_head_init(&st_gdata->tx_waitq); - - /* Locking used in st_int_enqueue() to avoid multiple execution */ - spin_lock_init(&st_gdata->lock); - - err = st_ll_init(st_gdata); - if (err) { - pr_err("error during st_ll initialization(%ld)", err); - goto err_free_gdata; - } - - INIT_WORK(&st_gdata->work_write_wakeup, work_fn_write_wakeup); - - *core_data = st_gdata; - return 0; -err_free_gdata: - kfree(st_gdata); -err_unreg_ldisc: - tty_unregister_ldisc(&st_ldisc_ops); - return err; -} - -void st_core_exit(struct st_data_s *st_gdata) -{ - long err; - /* internal module cleanup */ - err = st_ll_deinit(st_gdata); - if (err) - pr_err("error during deinit of ST LL %ld", err); - - if (st_gdata != NULL) { - /* Free ST Tx Qs and skbs */ - skb_queue_purge(&st_gdata->txq); - skb_queue_purge(&st_gdata->tx_waitq); - kfree_skb(st_gdata->rx_skb); - kfree_skb(st_gdata->tx_skb); - /* TTY ldisc cleanup */ - tty_unregister_ldisc(&st_ldisc_ops); - /* free the global data pointer */ - kfree(st_gdata); - } -} diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c deleted file mode 100644 index ff172cf4614d6..0000000000000 --- a/drivers/misc/ti-st/st_kim.c +++ /dev/null @@ -1,839 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Shared Transport Line discipline driver Core - * Init Manager module responsible for GPIO control - * and firmware download - * Copyright (C) 2009-2010 Texas Instruments - * Author: Pavan Savoy - */ - -#define pr_fmt(fmt) "(stk) :" fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */ -static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; - -/**********************************************************************/ -/* internal functions */ - -/* - * st_get_plat_device - - * function which returns the reference to the platform device - * requested by id. As of now only 1 such device exists (id=0) - * the context requesting for reference can get the id to be - * requested by a. The protocol driver which is registering or - * b. the tty device which is opened. - */ -static struct platform_device *st_get_plat_device(int id) -{ - return st_kim_devices[id]; -} - -/* - * validate_firmware_response - - * function to return whether the firmware response was proper - * in case of error don't complete so that waiting for proper - * response times out - */ -static void validate_firmware_response(struct kim_data_s *kim_gdata) -{ - struct sk_buff *skb = kim_gdata->rx_skb; - if (!skb) - return; - - /* - * these magic numbers are the position in the response buffer which - * allows us to distinguish whether the response is for the read - * version info. command - */ - if (skb->data[2] == 0x01 && skb->data[3] == 0x01 && - skb->data[4] == 0x10 && skb->data[5] == 0x00) { - /* fw version response */ - memcpy(kim_gdata->resp_buffer, - kim_gdata->rx_skb->data, - kim_gdata->rx_skb->len); - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_skb = NULL; - kim_gdata->rx_count = 0; - } else if (unlikely(skb->data[5] != 0)) { - pr_err("no proper response during fw download"); - pr_err("data6 %x", skb->data[5]); - kfree_skb(skb); - return; /* keep waiting for the proper response */ - } - /* becos of all the script being downloaded */ - complete_all(&kim_gdata->kim_rcvd); - kfree_skb(skb); -} - -/* - * check for data len received inside kim_int_recv - * most often hit the last case to update state to waiting for data - */ -static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) -{ - register int room = skb_tailroom(kim_gdata->rx_skb); - - pr_debug("len %d room %d", len, room); - - if (!len) { - validate_firmware_response(kim_gdata); - } else if (len > room) { - /* - * Received packet's payload length is larger. - * We can't accommodate it in created skb. - */ - pr_err("Data length is too large len %d room %d", len, - room); - kfree_skb(kim_gdata->rx_skb); - } else { - /* - * Packet header has non-zero payload length and - * we have enough space in created skb. Lets read - * payload data */ - kim_gdata->rx_state = ST_W4_DATA; - kim_gdata->rx_count = len; - return len; - } - - /* - * Change ST LL state to continue to process next - * packet - */ - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_skb = NULL; - kim_gdata->rx_count = 0; - - return 0; -} - -/* - * kim_int_recv - receive function called during firmware download - * firmware download responses on different UART drivers - * have been observed to come in bursts of different - * tty_receive and hence the logic - */ -static void kim_int_recv(struct kim_data_s *kim_gdata, const u8 *ptr, - size_t count) -{ - int len = 0; - unsigned char *plen; - - pr_debug("%s", __func__); - /* Decode received bytes here */ - while (count) { - if (kim_gdata->rx_count) { - len = min_t(unsigned int, kim_gdata->rx_count, count); - skb_put_data(kim_gdata->rx_skb, ptr, len); - kim_gdata->rx_count -= len; - count -= len; - ptr += len; - - if (kim_gdata->rx_count) - continue; - - /* Check ST RX state machine , where are we? */ - switch (kim_gdata->rx_state) { - /* Waiting for complete packet ? */ - case ST_W4_DATA: - pr_debug("Complete pkt received"); - validate_firmware_response(kim_gdata); - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_skb = NULL; - continue; - /* Waiting for Bluetooth event header ? */ - case ST_W4_HEADER: - plen = - (unsigned char *)&kim_gdata->rx_skb->data[1]; - pr_debug("event hdr: plen 0x%02x\n", *plen); - kim_check_data_len(kim_gdata, *plen); - continue; - } /* end of switch */ - } /* end of if rx_state */ - switch (*ptr) { - /* Bluetooth event packet? */ - case 0x04: - kim_gdata->rx_state = ST_W4_HEADER; - kim_gdata->rx_count = 2; - break; - default: - pr_info("unknown packet"); - ptr++; - count--; - continue; - } - ptr++; - count--; - kim_gdata->rx_skb = - alloc_skb(1024+8, GFP_ATOMIC); - if (!kim_gdata->rx_skb) { - pr_err("can't allocate mem for new packet"); - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_count = 0; - return; - } - skb_reserve(kim_gdata->rx_skb, 8); - kim_gdata->rx_skb->cb[0] = 4; - kim_gdata->rx_skb->cb[1] = 0; - - } - return; -} - -static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) -{ - unsigned short version = 0, chip = 0, min_ver = 0, maj_ver = 0; - static const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 }; - long time_left; - - pr_debug("%s", __func__); - - reinit_completion(&kim_gdata->kim_rcvd); - if (4 != st_int_write(kim_gdata->core_data, read_ver_cmd, 4)) { - pr_err("kim: couldn't write 4 bytes"); - return -EIO; - } - - time_left = wait_for_completion_interruptible_timeout( - &kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME)); - if (time_left <= 0) { - pr_err(" waiting for ver info- timed out or received signal"); - return time_left ? -ERESTARTSYS : -ETIMEDOUT; - } - reinit_completion(&kim_gdata->kim_rcvd); - /* - * the positions 12 & 13 in the response buffer provide with the - * chip, major & minor numbers - */ - - version = - MAKEWORD(kim_gdata->resp_buffer[12], - kim_gdata->resp_buffer[13]); - chip = (version & 0x7C00) >> 10; - min_ver = (version & 0x007F); - maj_ver = (version & 0x0380) >> 7; - - if (version & 0x8000) - maj_ver |= 0x0008; - - sprintf(bts_scr_name, "ti-connectivity/TIInit_%d.%d.%d.bts", - chip, maj_ver, min_ver); - - /* to be accessed later via sysfs entry */ - kim_gdata->version.full = version; - kim_gdata->version.chip = chip; - kim_gdata->version.maj_ver = maj_ver; - kim_gdata->version.min_ver = min_ver; - - pr_info("%s", bts_scr_name); - return 0; -} - -static void skip_change_remote_baud(unsigned char **ptr, long *len) -{ - unsigned char *nxt_action, *cur_action; - cur_action = *ptr; - - nxt_action = cur_action + sizeof(struct bts_action) + - ((struct bts_action *) cur_action)->size; - - if (((struct bts_action *) nxt_action)->type != ACTION_WAIT_EVENT) { - pr_err("invalid action after change remote baud command"); - } else { - *ptr = *ptr + sizeof(struct bts_action) + - ((struct bts_action *)cur_action)->size; - *len = *len - (sizeof(struct bts_action) + - ((struct bts_action *)cur_action)->size); - /* warn user on not commenting these in firmware */ - pr_warn("skipping the wait event of change remote baud"); - } -} - -/* - * download_firmware - - * internal function which parses through the .bts firmware - * script file intreprets SEND, DELAY actions only as of now - */ -static long download_firmware(struct kim_data_s *kim_gdata) -{ - long err = 0; - long len = 0; - unsigned char *ptr = NULL; - unsigned char *action_ptr = NULL; - unsigned char bts_scr_name[40] = { 0 }; /* 40 char long bts scr name? */ - int wr_room_space; - int cmd_size; - unsigned long timeout; - - err = read_local_version(kim_gdata, bts_scr_name); - if (err != 0) { - pr_err("kim: failed to read local ver"); - return err; - } - err = - request_firmware(&kim_gdata->fw_entry, bts_scr_name, - &kim_gdata->kim_pdev->dev); - if (unlikely((err != 0) || (kim_gdata->fw_entry->data == NULL) || - (kim_gdata->fw_entry->size == 0))) { - pr_err(" request_firmware failed(errno %ld) for %s", err, - bts_scr_name); - return -EINVAL; - } - ptr = (void *)kim_gdata->fw_entry->data; - len = kim_gdata->fw_entry->size; - /* - * bts_header to remove out magic number and - * version - */ - ptr += sizeof(struct bts_header); - len -= sizeof(struct bts_header); - - while (len > 0 && ptr) { - pr_debug(" action size %d, type %d ", - ((struct bts_action *)ptr)->size, - ((struct bts_action *)ptr)->type); - - switch (((struct bts_action *)ptr)->type) { - case ACTION_SEND_COMMAND: /* action send */ - pr_debug("S"); - action_ptr = &(((struct bts_action *)ptr)->data[0]); - if (unlikely - (((struct hci_command *)action_ptr)->opcode == - 0xFF36)) { - /* - * ignore remote change - * baud rate HCI VS command - */ - pr_warn("change remote baud" - " rate command in firmware"); - skip_change_remote_baud(&ptr, &len); - break; - } - /* - * Make sure we have enough free space in uart - * tx buffer to write current firmware command - */ - cmd_size = ((struct bts_action *)ptr)->size; - timeout = jiffies + msecs_to_jiffies(CMD_WR_TIME); - do { - wr_room_space = - st_get_uart_wr_room(kim_gdata->core_data); - if (wr_room_space < 0) { - pr_err("Unable to get free " - "space info from uart tx buffer"); - release_firmware(kim_gdata->fw_entry); - return wr_room_space; - } - mdelay(1); /* wait 1ms before checking room */ - } while ((wr_room_space < cmd_size) && - time_before(jiffies, timeout)); - - /* Timeout happened ? */ - if (time_after_eq(jiffies, timeout)) { - pr_err("Timeout while waiting for free " - "free space in uart tx buffer"); - release_firmware(kim_gdata->fw_entry); - return -ETIMEDOUT; - } - /* - * reinit completion before sending for the - * relevant wait - */ - reinit_completion(&kim_gdata->kim_rcvd); - - /* - * Free space found in uart buffer, call st_int_write - * to send current firmware command to the uart tx - * buffer. - */ - err = st_int_write(kim_gdata->core_data, - ((struct bts_action_send *)action_ptr)->data, - ((struct bts_action *)ptr)->size); - if (unlikely(err < 0)) { - release_firmware(kim_gdata->fw_entry); - return err; - } - /* - * Check number of bytes written to the uart tx buffer - * and requested command write size - */ - if (err != cmd_size) { - pr_err("Number of bytes written to uart " - "tx buffer are not matching with " - "requested cmd write size"); - release_firmware(kim_gdata->fw_entry); - return -EIO; - } - break; - case ACTION_WAIT_EVENT: /* wait */ - pr_debug("W"); - err = wait_for_completion_interruptible_timeout( - &kim_gdata->kim_rcvd, - msecs_to_jiffies(CMD_RESP_TIME)); - if (err <= 0) { - pr_err("response timeout/signaled during fw download "); - /* timed out */ - release_firmware(kim_gdata->fw_entry); - return err ? -ERESTARTSYS : -ETIMEDOUT; - } - reinit_completion(&kim_gdata->kim_rcvd); - break; - case ACTION_DELAY: /* sleep */ - pr_info("sleep command in scr"); - action_ptr = &(((struct bts_action *)ptr)->data[0]); - mdelay(((struct bts_action_delay *)action_ptr)->msec); - break; - } - len = - len - (sizeof(struct bts_action) + - ((struct bts_action *)ptr)->size); - ptr = - ptr + sizeof(struct bts_action) + - ((struct bts_action *)ptr)->size; - } - /* fw download complete */ - release_firmware(kim_gdata->fw_entry); - return 0; -} - -/**********************************************************************/ -/* functions called from ST core */ -/* called from ST Core, when REG_IN_PROGRESS (registration in progress) - * can be because of - * 1. response to read local version - * 2. during send/recv's of firmware download - */ -void st_kim_recv(void *disc_data, const u8 *data, size_t count) -{ - struct st_data_s *st_gdata = (struct st_data_s *)disc_data; - struct kim_data_s *kim_gdata = st_gdata->kim_data; - - /* - * proceed to gather all data and distinguish read fw version response - * from other fw responses when data gathering is complete - */ - kim_int_recv(kim_gdata, data, count); - return; -} - -/* - * to signal completion of line discipline installation - * called from ST Core, upon tty_open - */ -void st_kim_complete(void *kim_data) -{ - struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - complete(&kim_gdata->ldisc_installed); -} - -/* - * st_kim_start - called from ST Core upon 1st registration - * This involves toggling the chip enable gpio, reading - * the firmware version from chip, forming the fw file name - * based on the chip version, requesting the fw, parsing it - * and perform download(send/recv). - */ -long st_kim_start(void *kim_data) -{ - long err = 0; - long retry = POR_RETRY_COUNT; - struct ti_st_plat_data *pdata; - struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - - pr_info(" %s", __func__); - pdata = kim_gdata->kim_pdev->dev.platform_data; - - do { - /* platform specific enabling code here */ - if (pdata->chip_enable) - pdata->chip_enable(kim_gdata); - - /* Configure BT nShutdown to HIGH state */ - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); - mdelay(5); /* FIXME: a proper toggle */ - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH); - mdelay(100); - /* re-initialize the completion */ - reinit_completion(&kim_gdata->ldisc_installed); - /* send notification to UIM */ - kim_gdata->ldisc_install = 1; - pr_info("ldisc_install = 1"); - sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, - NULL, "install"); - /* wait for ldisc to be installed */ - err = wait_for_completion_interruptible_timeout( - &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); - if (!err) { - /* - * ldisc installation timeout, - * flush uart, power cycle BT_EN - */ - pr_err("ldisc installation timeout"); - err = st_kim_stop(kim_gdata); - continue; - } else { - /* ldisc installed now */ - pr_info("line discipline installed"); - err = download_firmware(kim_gdata); - if (err != 0) { - /* - * ldisc installed but fw download failed, - * flush uart & power cycle BT_EN - */ - pr_err("download firmware failed"); - err = st_kim_stop(kim_gdata); - continue; - } else { /* on success don't retry */ - break; - } - } - } while (retry--); - return err; -} - -/* - * st_kim_stop - stop communication with chip. - * This can be called from ST Core/KIM, on the- - * (a) last un-register when chip need not be powered there-after, - * (b) upon failure to either install ldisc or download firmware. - * The function is responsible to (a) notify UIM about un-installation, - * (b) flush UART if the ldisc was installed. - * (c) reset BT_EN - pull down nshutdown at the end. - * (d) invoke platform's chip disabling routine. - */ -long st_kim_stop(void *kim_data) -{ - long err = 0; - struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - struct ti_st_plat_data *pdata = - kim_gdata->kim_pdev->dev.platform_data; - struct tty_struct *tty = kim_gdata->core_data->tty; - - reinit_completion(&kim_gdata->ldisc_installed); - - if (tty) { /* can be called before ldisc is installed */ - /* Flush any pending characters in the driver and discipline. */ - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - } - - /* send uninstall notification to UIM */ - pr_info("ldisc_install = 0"); - kim_gdata->ldisc_install = 0; - sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install"); - - /* wait for ldisc to be un-installed */ - err = wait_for_completion_interruptible_timeout( - &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); - if (!err) { /* timeout */ - pr_err(" timed out waiting for ldisc to be un-installed"); - err = -ETIMEDOUT; - } - - /* By default configure BT nShutdown to LOW state */ - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); - mdelay(1); - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH); - mdelay(1); - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); - - /* platform specific disable */ - if (pdata->chip_disable) - pdata->chip_disable(kim_gdata); - return err; -} - -/**********************************************************************/ -/* functions called from subsystems */ -/* called when debugfs entry is read from */ - -static int version_show(struct seq_file *s, void *unused) -{ - struct kim_data_s *kim_gdata = s->private; - seq_printf(s, "%04X %d.%d.%d\n", kim_gdata->version.full, - kim_gdata->version.chip, kim_gdata->version.maj_ver, - kim_gdata->version.min_ver); - return 0; -} - -static int list_show(struct seq_file *s, void *unused) -{ - struct kim_data_s *kim_gdata = s->private; - kim_st_list_protocols(kim_gdata->core_data, s); - return 0; -} - -static ssize_t show_install(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", kim_data->ldisc_install); -} - -#ifdef DEBUG -static ssize_t store_dev_name(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - pr_debug("storing dev name >%s<", buf); - strscpy(kim_data->dev_name, buf, sizeof(kim_data->dev_name)); - pr_debug("stored dev name >%s<", kim_data->dev_name); - return count; -} - -static ssize_t store_baud_rate(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - pr_debug("storing baud rate >%s<", buf); - sscanf(buf, "%ld", &kim_data->baud_rate); - pr_debug("stored baud rate >%ld<", kim_data->baud_rate); - return count; -} -#endif /* if DEBUG */ - -static ssize_t show_dev_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", kim_data->dev_name); -} - -static ssize_t show_baud_rate(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", kim_data->baud_rate); -} - -static ssize_t show_flow_cntrl(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", kim_data->flow_cntrl); -} - -/* structures specific for sysfs entries */ -static struct kobj_attribute ldisc_install = -__ATTR(install, 0444, (void *)show_install, NULL); - -static struct kobj_attribute uart_dev_name = -#ifdef DEBUG /* TODO: move this to debug-fs if possible */ -__ATTR(dev_name, 0644, (void *)show_dev_name, (void *)store_dev_name); -#else -__ATTR(dev_name, 0444, (void *)show_dev_name, NULL); -#endif - -static struct kobj_attribute uart_baud_rate = -#ifdef DEBUG /* TODO: move to debugfs */ -__ATTR(baud_rate, 0644, (void *)show_baud_rate, (void *)store_baud_rate); -#else -__ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL); -#endif - -static struct kobj_attribute uart_flow_cntrl = -__ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL); - -static struct attribute *uim_attrs[] = { - &ldisc_install.attr, - &uart_dev_name.attr, - &uart_baud_rate.attr, - &uart_flow_cntrl.attr, - NULL, -}; - -static const struct attribute_group uim_attr_grp = { - .attrs = uim_attrs, -}; - -/* - * st_kim_ref - reference the core's data - * This references the per-ST platform device in the arch/xx/ - * board-xx.c file. - * This would enable multiple such platform devices to exist - * on a given platform - */ -void st_kim_ref(struct st_data_s **core_data, int id) -{ - struct platform_device *pdev; - struct kim_data_s *kim_gdata; - /* get kim_gdata reference from platform device */ - pdev = st_get_plat_device(id); - if (!pdev) - goto err; - kim_gdata = platform_get_drvdata(pdev); - if (!kim_gdata) - goto err; - - *core_data = kim_gdata->core_data; - return; -err: - *core_data = NULL; -} - -DEFINE_SHOW_ATTRIBUTE(version); -DEFINE_SHOW_ATTRIBUTE(list); - -/**********************************************************************/ -/* functions called from platform device driver subsystem - * need to have a relevant platform device entry in the platform's - * board-*.c file - */ - -static struct dentry *kim_debugfs_dir; -static int kim_probe(struct platform_device *pdev) -{ - struct kim_data_s *kim_gdata; - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - int err; - - if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { - /* multiple devices could exist */ - st_kim_devices[pdev->id] = pdev; - } else { - /* platform's sure about existence of 1 device */ - st_kim_devices[0] = pdev; - } - - kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_KERNEL); - if (!kim_gdata) { - pr_err("no mem to allocate"); - return -ENOMEM; - } - platform_set_drvdata(pdev, kim_gdata); - - err = st_core_init(&kim_gdata->core_data); - if (err != 0) { - pr_err(" ST core init failed"); - err = -EIO; - goto err_core_init; - } - /* refer to itself */ - kim_gdata->core_data->kim_data = kim_gdata; - - /* Claim the chip enable nShutdown gpio from the system */ - kim_gdata->nshutdown = pdata->nshutdown_gpio; - err = gpio_request(kim_gdata->nshutdown, "kim"); - if (unlikely(err)) { - pr_err(" gpio %d request failed ", kim_gdata->nshutdown); - goto err_sysfs_group; - } - - /* Configure nShutdown GPIO as output=0 */ - err = gpio_direction_output(kim_gdata->nshutdown, 0); - if (unlikely(err)) { - pr_err(" unable to configure gpio %d", kim_gdata->nshutdown); - goto err_sysfs_group; - } - /* get reference of pdev for request_firmware */ - kim_gdata->kim_pdev = pdev; - init_completion(&kim_gdata->kim_rcvd); - init_completion(&kim_gdata->ldisc_installed); - - err = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp); - if (err) { - pr_err("failed to create sysfs entries"); - goto err_sysfs_group; - } - - /* copying platform data */ - strscpy(kim_gdata->dev_name, pdata->dev_name, - sizeof(kim_gdata->dev_name)); - kim_gdata->flow_cntrl = pdata->flow_cntrl; - kim_gdata->baud_rate = pdata->baud_rate; - pr_info("sysfs entries created\n"); - - kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); - - debugfs_create_file("version", S_IRUGO, kim_debugfs_dir, - kim_gdata, &version_fops); - debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir, - kim_gdata, &list_fops); - return 0; - -err_sysfs_group: - st_core_exit(kim_gdata->core_data); - -err_core_init: - kfree(kim_gdata); - - return err; -} - -static void kim_remove(struct platform_device *pdev) -{ - /* free the GPIOs requested */ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - struct kim_data_s *kim_gdata; - - kim_gdata = platform_get_drvdata(pdev); - - /* - * Free the Bluetooth/FM/GPIO - * nShutdown gpio from the system - */ - gpio_free(pdata->nshutdown_gpio); - pr_info("nshutdown GPIO Freed"); - - debugfs_remove_recursive(kim_debugfs_dir); - sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); - pr_info("sysfs entries removed"); - - kim_gdata->kim_pdev = NULL; - st_core_exit(kim_gdata->core_data); - - kfree(kim_gdata); - kim_gdata = NULL; -} - -static int kim_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - - if (pdata->suspend) - return pdata->suspend(pdev, state); - - return 0; -} - -static int kim_resume(struct platform_device *pdev) -{ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - - if (pdata->resume) - return pdata->resume(pdev); - - return 0; -} - -/**********************************************************************/ -/* entry point for ST KIM module, called in from ST Core */ -static struct platform_driver kim_platform_driver = { - .probe = kim_probe, - .remove_new = kim_remove, - .suspend = kim_suspend, - .resume = kim_resume, - .driver = { - .name = "kim", - }, -}; - -module_platform_driver(kim_platform_driver); - -MODULE_AUTHOR("Pavan Savoy "); -MODULE_DESCRIPTION("Shared Transport Driver for TI BT/FM/GPS combo chips "); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c deleted file mode 100644 index 07406140d2770..0000000000000 --- a/drivers/misc/ti-st/st_ll.c +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Shared Transport driver - * HCI-LL module responsible for TI proprietary HCI_LL protocol - * Copyright (C) 2009-2010 Texas Instruments - * Author: Pavan Savoy - */ - -#define pr_fmt(fmt) "(stll) :" fmt -#include -#include -#include -#include - -/**********************************************************************/ -/* internal functions */ -static void send_ll_cmd(struct st_data_s *st_data, - unsigned char cmd) -{ - - pr_debug("%s: writing %x", __func__, cmd); - st_int_write(st_data, &cmd, 1); - return; -} - -static void ll_device_want_to_sleep(struct st_data_s *st_data) -{ - struct kim_data_s *kim_data; - struct ti_st_plat_data *pdata; - - pr_debug("%s", __func__); - /* sanity check */ - if (st_data->ll_state != ST_LL_AWAKE) - pr_err("ERR hcill: ST_LL_GO_TO_SLEEP_IND" - "in state %ld", st_data->ll_state); - - send_ll_cmd(st_data, LL_SLEEP_ACK); - /* update state */ - st_data->ll_state = ST_LL_ASLEEP; - - /* communicate to platform about chip asleep */ - kim_data = st_data->kim_data; - pdata = kim_data->kim_pdev->dev.platform_data; - if (pdata->chip_asleep) - pdata->chip_asleep(NULL); -} - -static void ll_device_want_to_wakeup(struct st_data_s *st_data) -{ - struct kim_data_s *kim_data; - struct ti_st_plat_data *pdata; - - /* diff actions in diff states */ - switch (st_data->ll_state) { - case ST_LL_ASLEEP: - send_ll_cmd(st_data, LL_WAKE_UP_ACK); /* send wake_ack */ - break; - case ST_LL_ASLEEP_TO_AWAKE: - /* duplicate wake_ind */ - pr_err("duplicate wake_ind while waiting for Wake ack"); - break; - case ST_LL_AWAKE: - /* duplicate wake_ind */ - pr_err("duplicate wake_ind already AWAKE"); - break; - case ST_LL_AWAKE_TO_ASLEEP: - /* duplicate wake_ind */ - pr_err("duplicate wake_ind"); - break; - } - /* update state */ - st_data->ll_state = ST_LL_AWAKE; - - /* communicate to platform about chip wakeup */ - kim_data = st_data->kim_data; - pdata = kim_data->kim_pdev->dev.platform_data; - if (pdata->chip_awake) - pdata->chip_awake(NULL); -} - -/**********************************************************************/ -/* functions invoked by ST Core */ - -/* called when ST Core wants to - * enable ST LL */ -void st_ll_enable(struct st_data_s *ll) -{ - ll->ll_state = ST_LL_AWAKE; -} - -/* called when ST Core /local module wants to - * disable ST LL */ -void st_ll_disable(struct st_data_s *ll) -{ - ll->ll_state = ST_LL_INVALID; -} - -/* called when ST Core wants to update the state */ -void st_ll_wakeup(struct st_data_s *ll) -{ - if (likely(ll->ll_state != ST_LL_AWAKE)) { - send_ll_cmd(ll, LL_WAKE_UP_IND); /* WAKE_IND */ - ll->ll_state = ST_LL_ASLEEP_TO_AWAKE; - } else { - /* don't send the duplicate wake_indication */ - pr_err(" Chip already AWAKE "); - } -} - -/* called when ST Core wants the state */ -unsigned long st_ll_getstate(struct st_data_s *ll) -{ - pr_debug(" returning state %ld", ll->ll_state); - return ll->ll_state; -} - -/* called from ST Core, when a PM related packet arrives */ -unsigned long st_ll_sleep_state(struct st_data_s *st_data, - unsigned char cmd) -{ - switch (cmd) { - case LL_SLEEP_IND: /* sleep ind */ - pr_debug("sleep indication recvd"); - ll_device_want_to_sleep(st_data); - break; - case LL_SLEEP_ACK: /* sleep ack */ - pr_err("sleep ack rcvd: host shouldn't"); - break; - case LL_WAKE_UP_IND: /* wake ind */ - pr_debug("wake indication recvd"); - ll_device_want_to_wakeup(st_data); - break; - case LL_WAKE_UP_ACK: /* wake ack */ - pr_debug("wake ack rcvd"); - st_data->ll_state = ST_LL_AWAKE; - break; - default: - pr_err(" unknown input/state "); - return -EINVAL; - } - return 0; -} - -/* Called from ST CORE to initialize ST LL */ -long st_ll_init(struct st_data_s *ll) -{ - /* set state to invalid */ - ll->ll_state = ST_LL_INVALID; - return 0; -} - -/* Called from ST CORE to de-initialize ST LL */ -long st_ll_deinit(struct st_data_s *ll) -{ - return 0; -} diff --git a/drivers/mtd/spi-nor/sysfs.c b/drivers/mtd/spi-nor/sysfs.c index 96064e4babf01..5e9eb268073d1 100644 --- a/drivers/mtd/spi-nor/sysfs.c +++ b/drivers/mtd/spi-nor/sysfs.c @@ -87,7 +87,7 @@ static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj, } static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj, - struct bin_attribute *attr, int n) + const struct bin_attribute *attr, int n) { struct spi_device *spi = to_spi_device(kobj_to_dev(kobj)); struct spi_mem *spimem = spi_get_drvdata(spi); diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index ae5abe492b52a..adc47b87b38a5 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -1447,7 +1447,7 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai, return err; } -static struct ubi_attach_info *alloc_ai(void) +static struct ubi_attach_info *alloc_ai(const char *slab_name) { struct ubi_attach_info *ai; @@ -1461,7 +1461,7 @@ static struct ubi_attach_info *alloc_ai(void) INIT_LIST_HEAD(&ai->alien); INIT_LIST_HEAD(&ai->fastmap); ai->volumes = RB_ROOT; - ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache", + ai->aeb_slab_cache = kmem_cache_create(slab_name, sizeof(struct ubi_ainf_peb), 0, 0, NULL); if (!ai->aeb_slab_cache) { @@ -1491,7 +1491,7 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai) err = -ENOMEM; - scan_ai = alloc_ai(); + scan_ai = alloc_ai("ubi_aeb_slab_cache_fastmap"); if (!scan_ai) goto out; @@ -1557,7 +1557,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) int err; struct ubi_attach_info *ai; - ai = alloc_ai(); + ai = alloc_ai("ubi_aeb_slab_cache"); if (!ai) return -ENOMEM; @@ -1575,7 +1575,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) if (err > 0 || mtd_is_eccerr(err)) { if (err != UBI_NO_FASTMAP) { destroy_ai(ai); - ai = alloc_ai(); + ai = alloc_ai("ubi_aeb_slab_cache"); if (!ai) return -ENOMEM; @@ -1614,7 +1614,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) if (ubi->fm && ubi_dbg_chk_fastmap(ubi)) { struct ubi_attach_info *scan_ai; - scan_ai = alloc_ai(); + scan_ai = alloc_ai("ubi_aeb_slab_cache_dbg_chk_fastmap"); if (!scan_ai) { err = -ENOMEM; goto out_wl; diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c index 2a9cc9413c427..9bdb6525f1281 100644 --- a/drivers/mtd/ubi/fastmap-wl.c +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -346,14 +346,27 @@ int ubi_wl_get_peb(struct ubi_device *ubi) * WL sub-system. * * @ubi: UBI device description object + * @need_fill: whether to fill wear-leveling pool when no PEBs are found */ -static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi) +static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi, + bool need_fill) { struct ubi_fm_pool *pool = &ubi->fm_wl_pool; int pnum; - if (pool->used == pool->size) + if (pool->used == pool->size) { + if (need_fill && !ubi->fm_work_scheduled) { + /* + * We cannot update the fastmap here because this + * function is called in atomic context. + * Let's fail here and refill/update it as soon as + * possible. + */ + ubi->fm_work_scheduled = 1; + schedule_work(&ubi->fm_work); + } return NULL; + } pnum = pool->pebs[pool->used]; return ubi->lookuptbl[pnum]; @@ -375,7 +388,7 @@ static bool need_wear_leveling(struct ubi_device *ubi) if (!ubi->used.rb_node) return false; - e = next_peb_for_wl(ubi); + e = next_peb_for_wl(ubi, false); if (!e) { if (!ubi->free.rb_node) return false; diff --git a/drivers/mtd/ubi/nvmem.c b/drivers/mtd/ubi/nvmem.c index a94a1a9aaec10..34f8c1d3cdeed 100644 --- a/drivers/mtd/ubi/nvmem.c +++ b/drivers/mtd/ubi/nvmem.c @@ -55,7 +55,7 @@ static int ubi_nvmem_reg_read(void *priv, unsigned int from, if (err) return err; - return bytes_left == 0 ? 0 : -EIO; + return 0; } static int ubi_nvmem_add(struct ubi_volume_info *vi) diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 1c9e874e8edea..26cc53ad34ec7 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -549,6 +549,7 @@ struct ubi_debug_info { * @peb_buf: a buffer of PEB size used for different purposes * @buf_mutex: protects @peb_buf * @ckvol_mutex: serializes static volume checking when opening + * @wl_reboot_notifier: close all wear-leveling work before reboot * * @dbg: debugging information for this UBI device */ @@ -651,6 +652,7 @@ struct ubi_device { void *peb_buf; struct mutex buf_mutex; struct mutex ckvol_mutex; + struct notifier_block wl_reboot_notifier; struct ubi_debug_info dbg; }; @@ -831,7 +833,6 @@ void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av); struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, struct ubi_attach_info *ai); int ubi_attach(struct ubi_device *ubi, int force_scan); -void ubi_destroy_ai(struct ubi_attach_info *ai); /* vtbl.c */ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 5a3558bbb9035..e5cf3bdca3b01 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -143,8 +143,10 @@ static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol) vol->vol_id != volid) continue; + fwnode_handle_put(fw_vols); return fw_vol; } + fwnode_handle_put(fw_vols); return NULL; } diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index a357f3d27f2f3..4f6f339d8fb8a 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -89,6 +89,7 @@ #include #include #include +#include #include "ubi.h" #include "wl.h" @@ -127,6 +128,8 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi, struct ubi_wl_entry *e, struct rb_root *root); static int self_check_in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e); +static int ubi_wl_reboot_notifier(struct notifier_block *n, + unsigned long state, void *cmd); /** * wl_tree_add - add a wear-leveling entry to a WL RB-tree. @@ -683,7 +686,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, ubi_assert(!ubi->move_to_put); #ifdef CONFIG_MTD_UBI_FASTMAP - if (!next_peb_for_wl(ubi) || + if (!next_peb_for_wl(ubi, true) || #else if (!ubi->free.rb_node || #endif @@ -846,7 +849,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, goto out_not_moved; } if (err == MOVE_RETRY) { - scrubbing = 1; + /* + * For source PEB: + * 1. The scrubbing is set for scrub type PEB, it will + * be put back into ubi->scrub list. + * 2. Non-scrub type PEB will be put back into ubi->used + * list. + */ + keep = 1; dst_leb_clean = 1; goto out_not_moved; } @@ -1943,6 +1953,13 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) if (!ubi->ro_mode && !ubi->fm_disabled) ubi_ensure_anchor_pebs(ubi); #endif + + if (!ubi->wl_reboot_notifier.notifier_call) { + ubi->wl_reboot_notifier.notifier_call = ubi_wl_reboot_notifier; + ubi->wl_reboot_notifier.priority = 1; /* Higher than MTD */ + register_reboot_notifier(&ubi->wl_reboot_notifier); + } + return 0; out_free: @@ -1988,6 +2005,17 @@ void ubi_wl_close(struct ubi_device *ubi) kfree(ubi->lookuptbl); } +static int ubi_wl_reboot_notifier(struct notifier_block *n, + unsigned long state, void *cmd) +{ + struct ubi_device *ubi; + + ubi = container_of(n, struct ubi_device, wl_reboot_notifier); + ubi_wl_close(ubi); + + return NOTIFY_DONE; +} + /** * self_check_ec - make sure that the erase counter of a PEB is correct. * @ubi: UBI device description object diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h index 7b6715ef6d4a3..a69169c35e310 100644 --- a/drivers/mtd/ubi/wl.h +++ b/drivers/mtd/ubi/wl.h @@ -5,7 +5,8 @@ static void update_fastmap_work_fn(struct work_struct *wrk); static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root); static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi); -static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi); +static struct ubi_wl_entry *next_peb_for_wl(struct ubi_device *ubi, + bool need_fill); static bool need_wear_leveling(struct ubi_device *ubi); static void ubi_fastmap_close(struct ubi_device *ubi); static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b826d0255ecf6..4ec4934a4eddd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4661,7 +4661,7 @@ int bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode) struct net_device *dev = bp->dev; if (page_mode) { - bp->flags &= ~BNXT_FLAG_AGG_RINGS; + bp->flags &= ~(BNXT_FLAG_AGG_RINGS | BNXT_FLAG_NO_AGG_RINGS); bp->flags |= BNXT_FLAG_RX_PAGE_MODE; if (bp->xdp_prog->aux->xdp_has_frags) @@ -9299,7 +9299,6 @@ static int __bnxt_hwrm_ptp_qcfg(struct bnxt *bp) struct hwrm_port_mac_ptp_qcfg_output *resp; struct hwrm_port_mac_ptp_qcfg_input *req; struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; - bool phc_cfg; u8 flags; int rc; @@ -9346,8 +9345,9 @@ static int __bnxt_hwrm_ptp_qcfg(struct bnxt *bp) rc = -ENODEV; goto exit; } - phc_cfg = (flags & PORT_MAC_PTP_QCFG_RESP_FLAGS_RTC_CONFIGURED) != 0; - rc = bnxt_ptp_init(bp, phc_cfg); + ptp->rtc_configured = + (flags & PORT_MAC_PTP_QCFG_RESP_FLAGS_RTC_CONFIGURED) != 0; + rc = bnxt_ptp_init(bp); if (rc) netdev_warn(bp->dev, "PTP initialization failed.\n"); exit: @@ -14746,6 +14746,14 @@ static int bnxt_change_mtu(struct net_device *dev, int new_mtu) bnxt_close_nic(bp, true, false); WRITE_ONCE(dev->mtu, new_mtu); + + /* MTU change may change the AGG ring settings if an XDP multi-buffer + * program is attached. We need to set the AGG rings settings and + * rx_skb_func accordingly. + */ + if (READ_ONCE(bp->xdp_prog)) + bnxt_set_rx_skb_mode(bp, true); + bnxt_set_ring_params(bp); if (netif_running(dev)) @@ -15483,6 +15491,13 @@ static int bnxt_queue_start(struct net_device *dev, void *qmem, int idx) for (i = 0; i <= BNXT_VNIC_NTUPLE; i++) { vnic = &bp->vnic_info[i]; + + rc = bnxt_hwrm_vnic_set_rss_p5(bp, vnic, true); + if (rc) { + netdev_err(bp->dev, "hwrm vnic %d set rss failure rc: %d\n", + vnic->vnic_id, rc); + return rc; + } vnic->mru = bp->dev->mtu + ETH_HLEN + VLAN_HLEN; bnxt_hwrm_vnic_update(bp, vnic, VNIC_UPDATE_REQ_ENABLES_MRU_VALID); @@ -16236,6 +16251,7 @@ static void bnxt_shutdown(struct pci_dev *pdev) if (netif_running(dev)) dev_close(dev); + bnxt_ptp_clear(bp); bnxt_clear_int_mode(bp); pci_disable_device(pdev); @@ -16263,6 +16279,7 @@ static int bnxt_suspend(struct device *device) rc = bnxt_close(dev); } bnxt_hwrm_func_drv_unrgtr(bp); + bnxt_ptp_clear(bp); pci_disable_device(bp->pdev); bnxt_free_ctx_mem(bp, false); rtnl_unlock(); @@ -16306,6 +16323,10 @@ static int bnxt_resume(struct device *device) if (bp->fw_crash_mem) bnxt_hwrm_crash_dump_mem_cfg(bp); + if (bnxt_ptp_init(bp)) { + kfree(bp->ptp_cfg); + bp->ptp_cfg = NULL; + } bnxt_get_wol_settings(bp); if (netif_running(dev)) { rc = bnxt_open(dev); @@ -16484,8 +16505,12 @@ static void bnxt_io_resume(struct pci_dev *pdev) rtnl_lock(); err = bnxt_hwrm_func_qcaps(bp); - if (!err && netif_running(netdev)) - err = bnxt_open(netdev); + if (!err) { + if (netif_running(netdev)) + err = bnxt_open(netdev); + else + err = bnxt_reserve_rings(bp, true); + } if (!err) netif_device_attach(netdev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 2f4987ec74643..f1f6bb328a55b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2837,19 +2837,24 @@ static int bnxt_get_link_ksettings(struct net_device *dev, } base->port = PORT_NONE; - if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP) { + if (media == BNXT_MEDIA_TP) { base->port = PORT_TP; linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, lk_ksettings->link_modes.supported); linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, lk_ksettings->link_modes.advertising); + } else if (media == BNXT_MEDIA_KR) { + linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, + lk_ksettings->link_modes.supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, + lk_ksettings->link_modes.advertising); } else { linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, lk_ksettings->link_modes.supported); linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, lk_ksettings->link_modes.advertising); - if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_DAC) + if (media == BNXT_MEDIA_CR) base->port = PORT_DA; else base->port = PORT_FIBRE; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 075ccd589845c..2d4e19b96ee74 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -1038,7 +1038,7 @@ static void bnxt_ptp_free(struct bnxt *bp) } } -int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg) +int bnxt_ptp_init(struct bnxt *bp) { struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; int rc; @@ -1061,7 +1061,7 @@ int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg) if (BNXT_PTP_USE_RTC(bp)) { bnxt_ptp_timecounter_init(bp, false); - rc = bnxt_ptp_init_rtc(bp, phc_cfg); + rc = bnxt_ptp_init_rtc(bp, ptp->rtc_configured); if (rc) goto out; } else { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h index c7851f8c971c5..a95f05e9c579b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h @@ -135,6 +135,7 @@ struct bnxt_ptp_cfg { BNXT_PTP_MSG_PDELAY_REQ | \ BNXT_PTP_MSG_PDELAY_RESP) u8 tx_tstamp_en:1; + u8 rtc_configured:1; int rx_filter; u32 tstamp_filters; @@ -168,7 +169,7 @@ void bnxt_tx_ts_cmp(struct bnxt *bp, struct bnxt_napi *bnapi, struct tx_ts_cmp *tscmp); void bnxt_ptp_rtc_timecounter_init(struct bnxt_ptp_cfg *ptp, u64 ns); int bnxt_ptp_init_rtc(struct bnxt *bp, bool phc_cfg); -int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg); +int bnxt_ptp_init(struct bnxt *bp); void bnxt_ptp_clear(struct bnxt *bp); static inline u64 bnxt_timecounter_cyc2time(struct bnxt_ptp_cfg *ptp, u64 ts) { diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 01dfec1159428..9cc8db10a8d60 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -17839,6 +17839,9 @@ static int tg3_init_one(struct pci_dev *pdev, } else persist_dma_mask = dma_mask = DMA_BIT_MASK(64); + if (tg3_asic_rev(tp) == ASIC_REV_57766) + persist_dma_mask = DMA_BIT_MASK(31); + /* Configure DMA attributes. */ if (dma_mask > DMA_BIT_MASK(32)) { err = dma_set_mask(&pdev->dev, dma_mask); diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.c b/drivers/net/ethernet/cavium/common/cavium_ptp.c index 9fd717b9cf69e..984f0dd7b62e1 100644 --- a/drivers/net/ethernet/cavium/common/cavium_ptp.c +++ b/drivers/net/ethernet/cavium/common/cavium_ptp.c @@ -239,12 +239,11 @@ static int cavium_ptp_probe(struct pci_dev *pdev, if (err) goto error_free; - err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev)); + clock->reg_base = pcim_iomap_region(pdev, PCI_PTP_BAR_NO, pci_name(pdev)); + err = PTR_ERR_OR_ZERO(clock->reg_base); if (err) goto error_free; - clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO]; - spin_lock_init(&clock->spin_lock); cc = &clock->cycle_counter; @@ -292,7 +291,7 @@ static int cavium_ptp_probe(struct pci_dev *pdev, clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG); clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN; writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG); - pcim_iounmap_regions(pdev, 1 << PCI_PTP_BAR_NO); + pcim_iounmap_region(pdev, PCI_PTP_BAR_NO); error_free: devm_kfree(dev, clock); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 27935c54b91bc..8216f843a7cd5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -112,6 +112,11 @@ struct mac_ops *get_mac_ops(void *cgxd) return ((struct cgx *)cgxd)->mac_ops; } +u32 cgx_get_fifo_len(void *cgxd) +{ + return ((struct cgx *)cgxd)->fifo_len; +} + void cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val) { writeq(val, cgx->reg_base + (lmac << cgx->mac_ops->lmac_offset) + @@ -209,6 +214,24 @@ u8 cgx_lmac_get_p2x(int cgx_id, int lmac_id) return (cfg & CMR_P2X_SEL_MASK) >> CMR_P2X_SEL_SHIFT; } +static u8 cgx_get_nix_resetbit(struct cgx *cgx) +{ + int first_lmac; + u8 p2x; + + /* non 98XX silicons supports only NIX0 block */ + if (cgx->pdev->subsystem_device != PCI_SUBSYS_DEVID_98XX) + return CGX_NIX0_RESET; + + first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); + p2x = cgx_lmac_get_p2x(cgx->cgx_id, first_lmac); + + if (p2x == CMR_P2X_SEL_NIX1) + return CGX_NIX1_RESET; + else + return CGX_NIX0_RESET; +} + /* Ensure the required lock for event queue(where asynchronous events are * posted) is acquired before calling this API. Else an asynchronous event(with * latest link status) can reach the destination before this function returns @@ -501,7 +524,7 @@ static u32 cgx_get_lmac_fifo_len(void *cgxd, int lmac_id) u8 num_lmacs; u32 fifo_len; - fifo_len = cgx->mac_ops->fifo_len; + fifo_len = cgx->fifo_len; num_lmacs = cgx->mac_ops->get_nr_lmacs(cgx); switch (num_lmacs) { @@ -1719,6 +1742,8 @@ static int cgx_lmac_init(struct cgx *cgx) lmac->lmac_type = cgx->mac_ops->get_lmac_type(cgx, lmac->lmac_id); } + /* Start X2P reset on given MAC block */ + cgx->mac_ops->mac_x2p_reset(cgx, true); return cgx_lmac_verify_fwi_version(cgx); err_bitmap_free: @@ -1764,7 +1789,7 @@ static void cgx_populate_features(struct cgx *cgx) u64 cfg; cfg = cgx_read(cgx, 0, CGX_CONST); - cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); + cgx->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); cgx->max_lmac_per_mac = FIELD_GET(CGX_CONST_MAX_LMACS, cfg); if (is_dev_rpm(cgx)) @@ -1784,6 +1809,45 @@ static u8 cgx_get_rxid_mapoffset(struct cgx *cgx) return 0x60; } +static void cgx_x2p_reset(void *cgxd, bool enable) +{ + struct cgx *cgx = cgxd; + int lmac_id; + u64 cfg; + + if (enable) { + for_each_set_bit(lmac_id, &cgx->lmac_bmap, cgx->max_lmac_per_mac) + cgx->mac_ops->mac_enadis_rx(cgx, lmac_id, false); + + usleep_range(1000, 2000); + + cfg = cgx_read(cgx, 0, CGXX_CMR_GLOBAL_CONFIG); + cfg |= cgx_get_nix_resetbit(cgx) | CGX_NSCI_DROP; + cgx_write(cgx, 0, CGXX_CMR_GLOBAL_CONFIG, cfg); + } else { + cfg = cgx_read(cgx, 0, CGXX_CMR_GLOBAL_CONFIG); + cfg &= ~(cgx_get_nix_resetbit(cgx) | CGX_NSCI_DROP); + cgx_write(cgx, 0, CGXX_CMR_GLOBAL_CONFIG, cfg); + } +} + +static int cgx_enadis_rx(void *cgxd, int lmac_id, bool enable) +{ + struct cgx *cgx = cgxd; + u64 cfg; + + if (!is_lmac_valid(cgx, lmac_id)) + return -ENODEV; + + cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); + if (enable) + cfg |= DATA_PKT_RX_EN; + else + cfg &= ~DATA_PKT_RX_EN; + cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); + return 0; +} + static struct mac_ops cgx_mac_ops = { .name = "cgx", .csr_offset = 0, @@ -1815,6 +1879,8 @@ static struct mac_ops cgx_mac_ops = { .mac_get_pfc_frm_cfg = cgx_lmac_get_pfc_frm_cfg, .mac_reset = cgx_lmac_reset, .mac_stats_reset = cgx_stats_reset, + .mac_x2p_reset = cgx_x2p_reset, + .mac_enadis_rx = cgx_enadis_rx, }; static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index dc9ace30554af..1cf12e5c7da87 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -32,6 +32,10 @@ #define CGX_LMAC_TYPE_MASK 0xF #define CGXX_CMRX_INT 0x040 #define FW_CGX_INT BIT_ULL(1) +#define CGXX_CMR_GLOBAL_CONFIG 0x08 +#define CGX_NIX0_RESET BIT_ULL(2) +#define CGX_NIX1_RESET BIT_ULL(3) +#define CGX_NSCI_DROP BIT_ULL(9) #define CGXX_CMRX_INT_ENA_W1S 0x058 #define CGXX_CMRX_RX_ID_MAP 0x060 #define CGXX_CMRX_RX_STAT0 0x070 @@ -185,4 +189,5 @@ int cgx_lmac_get_pfc_frm_cfg(void *cgxd, int lmac_id, u8 *tx_pause, int verify_lmac_fc_cfg(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause, int pfvf_idx); int cgx_lmac_reset(void *cgxd, int lmac_id, u8 pf_req_flr); +u32 cgx_get_fifo_len(void *cgxd); #endif /* CGX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h index 9ffc6790c5130..6180e68e1765a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h @@ -72,7 +72,6 @@ struct mac_ops { u8 irq_offset; u8 int_ena_bit; u8 lmac_fwi; - u32 fifo_len; bool non_contiguous_serdes_lane; /* RPM & CGX differs in number of Receive/transmit stats */ u8 rx_stats_cnt; @@ -133,6 +132,8 @@ struct mac_ops { int (*get_fec_stats)(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp); int (*mac_stats_reset)(void *cgxd, int lmac_id); + void (*mac_x2p_reset)(void *cgxd, bool enable); + int (*mac_enadis_rx)(void *cgxd, int lmac_id, bool enable); }; struct cgx { @@ -142,6 +143,10 @@ struct cgx { u8 lmac_count; /* number of LMACs per MAC could be 4 or 8 */ u8 max_lmac_per_mac; + /* length of fifo varies depending on the number + * of LMACS + */ + u32 fifo_len; #define MAX_LMAC_COUNT 8 struct lmac *lmac_idmap[MAX_LMAC_COUNT]; struct work_struct cgx_cmd_work; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index 1b34cf9c97035..2e9945446199e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -39,6 +39,8 @@ static struct mac_ops rpm_mac_ops = { .mac_get_pfc_frm_cfg = rpm_lmac_get_pfc_frm_cfg, .mac_reset = rpm_lmac_reset, .mac_stats_reset = rpm_stats_reset, + .mac_x2p_reset = rpm_x2p_reset, + .mac_enadis_rx = rpm_enadis_rx, }; static struct mac_ops rpm2_mac_ops = { @@ -72,6 +74,8 @@ static struct mac_ops rpm2_mac_ops = { .mac_get_pfc_frm_cfg = rpm_lmac_get_pfc_frm_cfg, .mac_reset = rpm_lmac_reset, .mac_stats_reset = rpm_stats_reset, + .mac_x2p_reset = rpm_x2p_reset, + .mac_enadis_rx = rpm_enadis_rx, }; bool is_dev_rpm2(void *rpmd) @@ -467,7 +471,7 @@ u8 rpm_get_lmac_type(void *rpmd, int lmac_id) int err; req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_LINK_STS, req); - err = cgx_fwi_cmd_generic(req, &resp, rpm, 0); + err = cgx_fwi_cmd_generic(req, &resp, rpm, lmac_id); if (!err) return FIELD_GET(RESP_LINKSTAT_LMAC_TYPE, resp); return err; @@ -480,7 +484,7 @@ u32 rpm_get_lmac_fifo_len(void *rpmd, int lmac_id) u8 num_lmacs; u32 fifo_len; - fifo_len = rpm->mac_ops->fifo_len; + fifo_len = rpm->fifo_len; num_lmacs = rpm->mac_ops->get_nr_lmacs(rpm); switch (num_lmacs) { @@ -533,9 +537,9 @@ u32 rpm2_get_lmac_fifo_len(void *rpmd, int lmac_id) */ max_lmac = (rpm_read(rpm, 0, CGX_CONST) >> 24) & 0xFF; if (max_lmac > 4) - fifo_len = rpm->mac_ops->fifo_len / 2; + fifo_len = rpm->fifo_len / 2; else - fifo_len = rpm->mac_ops->fifo_len; + fifo_len = rpm->fifo_len; if (lmac_id < 4) { num_lmacs = hweight8(lmac_info & 0xF); @@ -699,46 +703,51 @@ int rpm_get_fec_stats(void *rpmd, int lmac_id, struct cgx_fec_stats_rsp *rsp) if (rpm->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_NONE) return 0; + /* latched registers FCFECX_CW_HI/RSFEC_STAT_FAST_DATA_HI_CDC are common + * for all counters. Acquire lock to ensure serialized reads + */ + mutex_lock(&rpm->lock); if (rpm->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_BASER) { - val_lo = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_VL0_CCW_LO); - val_hi = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_CW_HI); + val_lo = rpm_read(rpm, 0, RPMX_MTI_FCFECX_VL0_CCW_LO(lmac_id)); + val_hi = rpm_read(rpm, 0, RPMX_MTI_FCFECX_CW_HI(lmac_id)); rsp->fec_corr_blks = (val_hi << 16 | val_lo); - val_lo = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_VL0_NCCW_LO); - val_hi = rpm_read(rpm, lmac_id, RPMX_MTI_FCFECX_CW_HI); + val_lo = rpm_read(rpm, 0, RPMX_MTI_FCFECX_VL0_NCCW_LO(lmac_id)); + val_hi = rpm_read(rpm, 0, RPMX_MTI_FCFECX_CW_HI(lmac_id)); rsp->fec_uncorr_blks = (val_hi << 16 | val_lo); /* 50G uses 2 Physical serdes lines */ if (rpm->lmac_idmap[lmac_id]->link_info.lmac_type_id == LMAC_MODE_50G_R) { - val_lo = rpm_read(rpm, lmac_id, - RPMX_MTI_FCFECX_VL1_CCW_LO); - val_hi = rpm_read(rpm, lmac_id, - RPMX_MTI_FCFECX_CW_HI); + val_lo = rpm_read(rpm, 0, + RPMX_MTI_FCFECX_VL1_CCW_LO(lmac_id)); + val_hi = rpm_read(rpm, 0, + RPMX_MTI_FCFECX_CW_HI(lmac_id)); rsp->fec_corr_blks += (val_hi << 16 | val_lo); - val_lo = rpm_read(rpm, lmac_id, - RPMX_MTI_FCFECX_VL1_NCCW_LO); - val_hi = rpm_read(rpm, lmac_id, - RPMX_MTI_FCFECX_CW_HI); + val_lo = rpm_read(rpm, 0, + RPMX_MTI_FCFECX_VL1_NCCW_LO(lmac_id)); + val_hi = rpm_read(rpm, 0, + RPMX_MTI_FCFECX_CW_HI(lmac_id)); rsp->fec_uncorr_blks += (val_hi << 16 | val_lo); } } else { /* enable RS-FEC capture */ - cfg = rpm_read(rpm, 0, RPMX_MTI_STAT_STATN_CONTROL); + cfg = rpm_read(rpm, 0, RPMX_MTI_RSFEC_STAT_STATN_CONTROL); cfg |= RPMX_RSFEC_RX_CAPTURE | BIT(lmac_id); - rpm_write(rpm, 0, RPMX_MTI_STAT_STATN_CONTROL, cfg); + rpm_write(rpm, 0, RPMX_MTI_RSFEC_STAT_STATN_CONTROL, cfg); val_lo = rpm_read(rpm, 0, RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_2); - val_hi = rpm_read(rpm, 0, RPMX_MTI_STAT_DATA_HI_CDC); + val_hi = rpm_read(rpm, 0, RPMX_MTI_RSFEC_STAT_FAST_DATA_HI_CDC); rsp->fec_corr_blks = (val_hi << 32 | val_lo); val_lo = rpm_read(rpm, 0, RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_3); - val_hi = rpm_read(rpm, 0, RPMX_MTI_STAT_DATA_HI_CDC); + val_hi = rpm_read(rpm, 0, RPMX_MTI_RSFEC_STAT_FAST_DATA_HI_CDC); rsp->fec_uncorr_blks = (val_hi << 32 | val_lo); } + mutex_unlock(&rpm->lock); return 0; } @@ -763,3 +772,41 @@ int rpm_lmac_reset(void *rpmd, int lmac_id, u8 pf_req_flr) return 0; } + +void rpm_x2p_reset(void *rpmd, bool enable) +{ + rpm_t *rpm = rpmd; + int lmac_id; + u64 cfg; + + if (enable) { + for_each_set_bit(lmac_id, &rpm->lmac_bmap, rpm->max_lmac_per_mac) + rpm->mac_ops->mac_enadis_rx(rpm, lmac_id, false); + + usleep_range(1000, 2000); + + cfg = rpm_read(rpm, 0, RPMX_CMR_GLOBAL_CFG); + rpm_write(rpm, 0, RPMX_CMR_GLOBAL_CFG, cfg | RPM_NIX0_RESET); + } else { + cfg = rpm_read(rpm, 0, RPMX_CMR_GLOBAL_CFG); + cfg &= ~RPM_NIX0_RESET; + rpm_write(rpm, 0, RPMX_CMR_GLOBAL_CFG, cfg); + } +} + +int rpm_enadis_rx(void *rpmd, int lmac_id, bool enable) +{ + rpm_t *rpm = rpmd; + u64 cfg; + + if (!is_lmac_valid(rpm, lmac_id)) + return -ENODEV; + + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + if (enable) + cfg |= RPM_RX_EN; + else + cfg &= ~RPM_RX_EN; + rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h index 34b11deb0f3c1..b8d3972e096ae 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h @@ -17,6 +17,8 @@ /* Registers */ #define RPMX_CMRX_CFG 0x00 +#define RPMX_CMR_GLOBAL_CFG 0x08 +#define RPM_NIX0_RESET BIT_ULL(3) #define RPMX_RX_TS_PREPEND BIT_ULL(22) #define RPMX_TX_PTP_1S_SUPPORT BIT_ULL(17) #define RPMX_CMRX_RX_ID_MAP 0x80 @@ -84,16 +86,18 @@ /* FEC stats */ #define RPMX_MTI_STAT_STATN_CONTROL 0x10018 #define RPMX_MTI_STAT_DATA_HI_CDC 0x10038 -#define RPMX_RSFEC_RX_CAPTURE BIT_ULL(27) +#define RPMX_RSFEC_RX_CAPTURE BIT_ULL(28) #define RPMX_CMD_CLEAR_RX BIT_ULL(30) #define RPMX_CMD_CLEAR_TX BIT_ULL(31) +#define RPMX_MTI_RSFEC_STAT_STATN_CONTROL 0x40018 +#define RPMX_MTI_RSFEC_STAT_FAST_DATA_HI_CDC 0x40000 #define RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_2 0x40050 #define RPMX_MTI_RSFEC_STAT_COUNTER_CAPTURE_3 0x40058 -#define RPMX_MTI_FCFECX_VL0_CCW_LO 0x38618 -#define RPMX_MTI_FCFECX_VL0_NCCW_LO 0x38620 -#define RPMX_MTI_FCFECX_VL1_CCW_LO 0x38628 -#define RPMX_MTI_FCFECX_VL1_NCCW_LO 0x38630 -#define RPMX_MTI_FCFECX_CW_HI 0x38638 +#define RPMX_MTI_FCFECX_VL0_CCW_LO(a) (0x38618 + ((a) * 0x40)) +#define RPMX_MTI_FCFECX_VL0_NCCW_LO(a) (0x38620 + ((a) * 0x40)) +#define RPMX_MTI_FCFECX_VL1_CCW_LO(a) (0x38628 + ((a) * 0x40)) +#define RPMX_MTI_FCFECX_VL1_NCCW_LO(a) (0x38630 + ((a) * 0x40)) +#define RPMX_MTI_FCFECX_CW_HI(a) (0x38638 + ((a) * 0x40)) /* CN10KB CSR Declaration */ #define RPM2_CMRX_SW_INT 0x1b0 @@ -137,4 +141,6 @@ bool is_dev_rpm2(void *rpmd); int rpm_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp); int rpm_lmac_reset(void *rpmd, int lmac_id, u8 pf_req_flr); int rpm_stats_reset(void *rpmd, int lmac_id); +void rpm_x2p_reset(void *rpmd, bool enable); +int rpm_enadis_rx(void *rpmd, int lmac_id, bool enable); #endif /* RPM_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 1a97fb9032fa4..cd0d7b7774f1a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -1162,6 +1162,7 @@ static int rvu_setup_hw_resources(struct rvu *rvu) } rvu_program_channels(rvu); + cgx_start_linkup(rvu); err = rvu_mcs_init(rvu); if (err) { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index b897845e25fd3..a383b5ef5b2d8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -1025,6 +1025,7 @@ int rvu_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_ int rvu_cgx_cfg_pause_frm(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause); void rvu_mac_reset(struct rvu *rvu, u16 pcifunc); u32 rvu_cgx_get_lmac_fifolen(struct rvu *rvu, int cgx, int lmac); +void cgx_start_linkup(struct rvu *rvu); int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, u16 pcifunc, int nixlf, int type); bool is_mcam_entry_enabled(struct rvu *rvu, struct npc_mcam *mcam, int blkaddr, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 266ecbc1b97a6..992fa0b82e8d2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -349,6 +349,7 @@ static void rvu_cgx_wq_destroy(struct rvu *rvu) int rvu_cgx_init(struct rvu *rvu) { + struct mac_ops *mac_ops; int cgx, err; void *cgxd; @@ -375,6 +376,15 @@ int rvu_cgx_init(struct rvu *rvu) if (err) return err; + /* Clear X2P reset on all MAC blocks */ + for (cgx = 0; cgx < rvu->cgx_cnt_max; cgx++) { + cgxd = rvu_cgx_pdata(cgx, rvu); + if (!cgxd) + continue; + mac_ops = get_mac_ops(cgxd); + mac_ops->mac_x2p_reset(cgxd, false); + } + /* Register for CGX events */ err = cgx_lmac_event_handler_init(rvu); if (err) @@ -382,10 +392,26 @@ int rvu_cgx_init(struct rvu *rvu) mutex_init(&rvu->cgx_cfg_lock); - /* Ensure event handler registration is completed, before - * we turn on the links - */ - mb(); + return 0; +} + +void cgx_start_linkup(struct rvu *rvu) +{ + unsigned long lmac_bmap; + struct mac_ops *mac_ops; + int cgx, lmac, err; + void *cgxd; + + /* Enable receive on all LMACS */ + for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) { + cgxd = rvu_cgx_pdata(cgx, rvu); + if (!cgxd) + continue; + mac_ops = get_mac_ops(cgxd); + lmac_bmap = cgx_get_lmac_bmap(cgxd); + for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx) + mac_ops->mac_enadis_rx(cgxd, lmac, true); + } /* Do link up for all CGX ports */ for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) { @@ -398,8 +424,6 @@ int rvu_cgx_init(struct rvu *rvu) "Link up process failed to start on cgx %d\n", cgx); } - - return 0; } int rvu_cgx_exit(struct rvu *rvu) @@ -923,13 +947,12 @@ int rvu_mbox_handler_cgx_features_get(struct rvu *rvu, u32 rvu_cgx_get_fifolen(struct rvu *rvu) { - struct mac_ops *mac_ops; - u32 fifo_len; + void *cgxd = rvu_first_cgx_pdata(rvu); - mac_ops = get_mac_ops(rvu_first_cgx_pdata(rvu)); - fifo_len = mac_ops ? mac_ops->fifo_len : 0; + if (!cgxd) + return 0; - return fifo_len; + return cgx_get_fifo_len(cgxd); } u32 rvu_cgx_get_lmac_fifolen(struct rvu *rvu, int cgx, int lmac) diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index fe38426ec42d4..2bf426cea6ddc 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1394,18 +1394,15 @@ static int pxa168_eth_probe(struct platform_device *pdev) printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n"); - clk = devm_clk_get(&pdev->dev, NULL); + clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Fast Ethernet failed to get clock\n"); + dev_err(&pdev->dev, "Fast Ethernet failed to get and enable clock\n"); return -ENODEV; } - clk_prepare_enable(clk); dev = alloc_etherdev(sizeof(struct pxa168_eth_private)); - if (!dev) { - err = -ENOMEM; - goto err_clk; - } + if (!dev) + return -ENOMEM; platform_set_drvdata(pdev, dev); pep = netdev_priv(dev); @@ -1523,8 +1520,6 @@ static int pxa168_eth_probe(struct platform_device *pdev) mdiobus_free(pep->smi_bus); err_netdev: free_netdev(dev); -err_clk: - clk_disable_unprepare(clk); return err; } @@ -1542,7 +1537,6 @@ static void pxa168_eth_remove(struct platform_device *pdev) if (dev->phydev) phy_disconnect(dev->phydev); - clk_disable_unprepare(pep->clk); mdiobus_unregister(pep->smi_bus); mdiobus_free(pep->smi_bus); unregister_netdev(dev); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 7251121ab196e..16eb3de60eb6d 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -366,12 +366,13 @@ static void vcap_api_iterator_init_test(struct kunit *test) struct vcap_typegroup typegroups[] = { { .offset = 0, .width = 2, .value = 2, }, { .offset = 156, .width = 1, .value = 0, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; struct vcap_typegroup typegroups2[] = { { .offset = 0, .width = 3, .value = 4, }, { .offset = 49, .width = 2, .value = 0, }, { .offset = 98, .width = 2, .value = 0, }, + { } }; vcap_iter_init(&iter, 52, typegroups, 86); @@ -399,6 +400,7 @@ static void vcap_api_iterator_next_test(struct kunit *test) { .offset = 147, .width = 3, .value = 0, }, { .offset = 196, .width = 2, .value = 0, }, { .offset = 245, .width = 1, .value = 0, }, + { } }; int idx; @@ -433,7 +435,7 @@ static void vcap_api_encode_typegroups_test(struct kunit *test) { .offset = 147, .width = 3, .value = 5, }, { .offset = 196, .width = 2, .value = 2, }, { .offset = 245, .width = 5, .value = 27, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; vcap_encode_typegroups(stream, 49, typegroups, false); @@ -463,6 +465,7 @@ static void vcap_api_encode_bit_test(struct kunit *test) { .offset = 147, .width = 3, .value = 5, }, { .offset = 196, .width = 2, .value = 2, }, { .offset = 245, .width = 1, .value = 0, }, + { } }; vcap_iter_init(&iter, 49, typegroups, 44); @@ -489,7 +492,7 @@ static void vcap_api_encode_field_test(struct kunit *test) { .offset = 147, .width = 3, .value = 5, }, { .offset = 196, .width = 2, .value = 2, }, { .offset = 245, .width = 5, .value = 27, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; struct vcap_field rf = { .type = VCAP_FIELD_U32, @@ -538,7 +541,7 @@ static void vcap_api_encode_short_field_test(struct kunit *test) { .offset = 0, .width = 3, .value = 7, }, { .offset = 21, .width = 2, .value = 3, }, { .offset = 42, .width = 1, .value = 1, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; struct vcap_field rf = { .type = VCAP_FIELD_U32, @@ -608,7 +611,7 @@ static void vcap_api_encode_keyfield_test(struct kunit *test) struct vcap_typegroup tgt[] = { { .offset = 0, .width = 2, .value = 2, }, { .offset = 156, .width = 1, .value = 1, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; vcap_test_api_init(&admin); @@ -671,7 +674,7 @@ static void vcap_api_encode_max_keyfield_test(struct kunit *test) struct vcap_typegroup tgt[] = { { .offset = 0, .width = 2, .value = 2, }, { .offset = 156, .width = 1, .value = 1, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; u32 keyres[] = { 0x928e8a84, @@ -732,7 +735,7 @@ static void vcap_api_encode_actionfield_test(struct kunit *test) { .offset = 0, .width = 2, .value = 2, }, { .offset = 21, .width = 1, .value = 1, }, { .offset = 42, .width = 1, .value = 0, }, - { .offset = 0, .width = 0, .value = 0, }, + { } }; vcap_encode_actionfield(&rule, &caf, &rf, tgt); diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h index 942f1e531a851..dbc3f92eebc48 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase.h +++ b/drivers/net/ethernet/realtek/rtase/rtase.h @@ -9,7 +9,10 @@ #ifndef RTASE_H #define RTASE_H -#define RTASE_HW_VER_MASK 0x7C800000 +#define RTASE_HW_VER_MASK 0x7C800000 +#define RTASE_HW_VER_906X_7XA 0x00800000 +#define RTASE_HW_VER_906X_7XC 0x04000000 +#define RTASE_HW_VER_907XD_V1 0x04800000 #define RTASE_RX_DMA_BURST_256 4 #define RTASE_TX_DMA_BURST_UNLIMITED 7 @@ -327,6 +330,8 @@ struct rtase_private { u16 int_nums; u16 tx_int_mit; u16 rx_int_mit; + + u32 hw_ver; }; #define RTASE_LSO_64K 64000 diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 874994d9ceb95..de7f11232593f 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -1714,10 +1714,21 @@ static int rtase_get_settings(struct net_device *dev, struct ethtool_link_ksettings *cmd) { u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause; + const struct rtase_private *tp = netdev_priv(dev); ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, supported); - cmd->base.speed = SPEED_5000; + + switch (tp->hw_ver) { + case RTASE_HW_VER_906X_7XA: + case RTASE_HW_VER_906X_7XC: + cmd->base.speed = SPEED_5000; + break; + case RTASE_HW_VER_907XD_V1: + cmd->base.speed = SPEED_10000; + break; + } + cmd->base.duplex = DUPLEX_FULL; cmd->base.port = PORT_MII; cmd->base.autoneg = AUTONEG_DISABLE; @@ -1972,20 +1983,21 @@ static void rtase_init_software_variable(struct pci_dev *pdev, tp->dev->max_mtu = RTASE_MAX_JUMBO_SIZE; } -static bool rtase_check_mac_version_valid(struct rtase_private *tp) +static int rtase_check_mac_version_valid(struct rtase_private *tp) { - u32 hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & RTASE_HW_VER_MASK; - bool known_ver = false; + int ret = -ENODEV; + + tp->hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & RTASE_HW_VER_MASK; - switch (hw_ver) { - case 0x00800000: - case 0x04000000: - case 0x04800000: - known_ver = true; + switch (tp->hw_ver) { + case RTASE_HW_VER_906X_7XA: + case RTASE_HW_VER_906X_7XC: + case RTASE_HW_VER_907XD_V1: + ret = 0; break; } - return known_ver; + return ret; } static int rtase_init_board(struct pci_dev *pdev, struct net_device **dev_out, @@ -2105,9 +2117,13 @@ static int rtase_init_one(struct pci_dev *pdev, tp->pdev = pdev; /* identify chip attached to board */ - if (!rtase_check_mac_version_valid(tp)) - return dev_err_probe(&pdev->dev, -ENODEV, - "unknown chip version, contact rtase maintainers (see MAINTAINERS file)\n"); + ret = rtase_check_mac_version_valid(tp); + if (ret != 0) { + dev_err(&pdev->dev, + "unknown chip version: 0x%08x, contact rtase maintainers (see MAINTAINERS file)\n", + tp->hw_ver); + goto err_out_release_board; + } rtase_init_software_variable(pdev, tp); rtase_init_hardware(tp); @@ -2181,6 +2197,7 @@ static int rtase_init_one(struct pci_dev *pdev, netif_napi_del(&ivec->napi); } +err_out_release_board: rtase_release_board(pdev, dev, ioaddr); return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 248b30d7b864b..16020b72dec83 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -487,6 +487,8 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) plat_dat->select_pcs = socfpga_dwmac_select_pcs; plat_dat->has_gmac = true; + plat_dat->riwt_off = 1; + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); if (ret) return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3cdc3910f3a08..9b262cdad60b2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1177,6 +1177,9 @@ static int stmmac_init_phy(struct net_device *dev) return -ENODEV; } + if (priv->dma_cap.eee) + phy_support_eee(phydev); + ret = phylink_connect_phy(priv->phylink, phydev); } else { fwnode_handle_put(phy_fwnode); diff --git a/drivers/net/mdio/mdio-ipq4019.c b/drivers/net/mdio/mdio-ipq4019.c index dd3ed2d6430b1..d9a94df482d9f 100644 --- a/drivers/net/mdio/mdio-ipq4019.c +++ b/drivers/net/mdio/mdio-ipq4019.c @@ -352,8 +352,11 @@ static int ipq4019_mdio_probe(struct platform_device *pdev) /* The platform resource is provided on the chipset IPQ5018 */ /* This resource is optional */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) + if (res) { priv->eth_ldo_rdy = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->eth_ldo_rdy)) + return PTR_ERR(priv->eth_ldo_rdy); + } bus->name = "ipq4019_mdio"; bus->read = ipq4019_mdio_read_c22; diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 96d0b3a5a9d3d..944ae98ad1106 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -1530,7 +1530,7 @@ int genphy_c45_ethtool_get_eee(struct phy_device *phydev, return ret; data->eee_enabled = is_enabled; - data->eee_active = ret; + data->eee_active = phydev->eee_active; linkmode_copy(data->supported, phydev->supported_eee); return 0; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4f3e742907cb6..0d20b534122b2 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -990,14 +990,14 @@ static int phy_check_link_status(struct phy_device *phydev) phydev->state = PHY_RUNNING; err = genphy_c45_eee_is_active(phydev, NULL, NULL, NULL); - if (err <= 0) - phydev->enable_tx_lpi = false; - else - phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled; + phydev->eee_active = err > 0; + phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled && + phydev->eee_active; phy_link_up(phydev); } else if (!phydev->link && phydev->state != PHY_NOLINK) { phydev->state = PHY_NOLINK; + phydev->eee_active = false; phydev->enable_tx_lpi = false; phy_link_down(phydev); } @@ -1672,7 +1672,7 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); * phy_ethtool_set_eee_noneg - Adjusts MAC LPI configuration without PHY * renegotiation * @phydev: pointer to the target PHY device structure - * @data: pointer to the ethtool_keee structure containing the new EEE settings + * @old_cfg: pointer to the eee_config structure containing the old EEE settings * * This function updates the Energy Efficient Ethernet (EEE) configuration * for cases where only the MAC's Low Power Idle (LPI) configuration changes, @@ -1683,18 +1683,23 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); * configuration. */ static void phy_ethtool_set_eee_noneg(struct phy_device *phydev, - struct ethtool_keee *data) + const struct eee_config *old_cfg) { - if (phydev->eee_cfg.tx_lpi_enabled != data->tx_lpi_enabled || - phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) { - eee_to_eeecfg(&phydev->eee_cfg, data); - phydev->enable_tx_lpi = eeecfg_mac_can_tx_lpi(&phydev->eee_cfg); - if (phydev->link) { - phydev->link = false; - phy_link_down(phydev); - phydev->link = true; - phy_link_up(phydev); - } + bool enable_tx_lpi; + + if (!phydev->link) + return; + + enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled && phydev->eee_active; + + if (phydev->enable_tx_lpi != enable_tx_lpi || + phydev->eee_cfg.tx_lpi_timer != old_cfg->tx_lpi_timer) { + phydev->enable_tx_lpi = false; + phydev->link = false; + phy_link_down(phydev); + phydev->enable_tx_lpi = enable_tx_lpi; + phydev->link = true; + phy_link_up(phydev); } } @@ -1707,18 +1712,23 @@ static void phy_ethtool_set_eee_noneg(struct phy_device *phydev, */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_keee *data) { + struct eee_config old_cfg; int ret; if (!phydev->drv) return -EIO; mutex_lock(&phydev->lock); + + old_cfg = phydev->eee_cfg; + eee_to_eeecfg(&phydev->eee_cfg, data); + ret = genphy_c45_ethtool_set_eee(phydev, data); - if (ret >= 0) { - if (ret == 0) - phy_ethtool_set_eee_noneg(phydev, data); - eee_to_eeecfg(&phydev->eee_cfg, data); - } + if (ret == 0) + phy_ethtool_set_eee_noneg(phydev, &old_cfg); + else if (ret < 0) + phydev->eee_cfg = old_cfg; + mutex_unlock(&phydev->lock); return ret < 0 ? ret : 0; diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 8adf77e3557e7..531b1b6a37d19 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1652,13 +1652,13 @@ static int lan78xx_set_wol(struct net_device *netdev, struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); int ret; + if (wol->wolopts & ~WAKE_ALL) + return -EINVAL; + ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret; - if (wol->wolopts & ~WAKE_ALL) - return -EINVAL; - pdata->wol = wol->wolopts; device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts); @@ -2380,6 +2380,7 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) if (dev->chipid == ID_REV_CHIP_ID_7801_) { if (phy_is_pseudo_fixed_link(phydev)) { fixed_phy_unregister(phydev); + phy_device_free(phydev); } else { phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0); @@ -4246,8 +4247,10 @@ static void lan78xx_disconnect(struct usb_interface *intf) phy_disconnect(net->phydev); - if (phy_is_pseudo_fixed_link(phydev)) + if (phy_is_pseudo_fixed_link(phydev)) { fixed_phy_unregister(phydev); + phy_device_free(phydev); + } usb_scuttle_anchored_urbs(&dev->deferred); @@ -4414,29 +4417,30 @@ static int lan78xx_probe(struct usb_interface *intf, period = ep_intr->desc.bInterval; maxp = usb_maxpacket(dev->udev, dev->pipe_intr); - buf = kmalloc(maxp, GFP_KERNEL); - if (!buf) { + + dev->urb_intr = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb_intr) { ret = -ENOMEM; goto out5; } - dev->urb_intr = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->urb_intr) { + buf = kmalloc(maxp, GFP_KERNEL); + if (!buf) { ret = -ENOMEM; - goto out6; - } else { - usb_fill_int_urb(dev->urb_intr, dev->udev, - dev->pipe_intr, buf, maxp, - intr_complete, dev, period); - dev->urb_intr->transfer_flags |= URB_FREE_BUFFER; + goto free_urbs; } + usb_fill_int_urb(dev->urb_intr, dev->udev, + dev->pipe_intr, buf, maxp, + intr_complete, dev, period); + dev->urb_intr->transfer_flags |= URB_FREE_BUFFER; + dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out); /* Reject broken descriptors. */ if (dev->maxpacket == 0) { ret = -ENODEV; - goto out6; + goto free_urbs; } /* driver requires remote-wakeup capability during autosuspend. */ @@ -4444,7 +4448,7 @@ static int lan78xx_probe(struct usb_interface *intf, ret = lan78xx_phy_init(dev); if (ret < 0) - goto out7; + goto free_urbs; ret = register_netdev(netdev); if (ret != 0) { @@ -4466,10 +4470,8 @@ static int lan78xx_probe(struct usb_interface *intf, out8: phy_disconnect(netdev->phydev); -out7: +free_urbs: usb_free_urb(dev->urb_intr); -out6: - kfree(buf); out5: lan78xx_unbind(dev, intf); out4: diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d19b3bd0866bd..86f1d87a909c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3535,7 +3535,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, struct iwl_trans_pcie *trans_pcie, **priv; struct iwl_trans *trans; int ret, addr_size; - void __iomem * const *table; u32 bar0; /* reassign our BAR 0 if invalid due to possible runtime PM races */ @@ -3661,22 +3660,15 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } } - ret = pcim_iomap_regions_request_all(pdev, BIT(0), DRV_NAME); + ret = pcim_request_all_regions(pdev, DRV_NAME); if (ret) { - dev_err(&pdev->dev, "pcim_iomap_regions_request_all failed\n"); + dev_err(&pdev->dev, "Requesting all PCI BARs failed.\n"); goto out_no_pci; } - table = pcim_iomap_table(pdev); - if (!table) { - dev_err(&pdev->dev, "pcim_iomap_table failed\n"); - ret = -ENOMEM; - goto out_no_pci; - } - - trans_pcie->hw_base = table[0]; + trans_pcie->hw_base = pcim_iomap(pdev, 0, 0); if (!trans_pcie->hw_base) { - dev_err(&pdev->dev, "couldn't find IO mem in first BAR\n"); + dev_err(&pdev->dev, "Could not ioremap PCI BAR 0.\n"); ret = -ENODEV; goto out_no_pci; } diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index 6fc9dfe824747..544d8a4d2af59 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2671,15 +2671,20 @@ static int idt_init_pci(struct idt_ntb_dev *ndev) */ pci_set_master(pdev); - /* Request all BARs resources and map BAR0 only */ - ret = pcim_iomap_regions_request_all(pdev, 1, NTB_NAME); + /* Request all BARs resources */ + ret = pcim_request_all_regions(pdev, NTB_NAME); if (ret != 0) { dev_err(&pdev->dev, "Failed to request resources\n"); goto err_clear_master; } - /* Retrieve virtual address of BAR0 - PCI configuration space */ - ndev->cfgspc = pcim_iomap_table(pdev)[0]; + /* ioremap BAR0 - PCI configuration space */ + ndev->cfgspc = pcim_iomap(pdev, 0, 0); + if (!ndev->cfgspc) { + dev_err(&pdev->dev, "Failed to ioremap BAR 0\n"); + ret = -ENOMEM; + goto err_clear_master; + } /* Put the IDT driver data pointer to the PCI-device private pointer */ pci_set_drvdata(pdev, ndev); diff --git a/drivers/nvdimm/dax_devs.c b/drivers/nvdimm/dax_devs.c index 6b4922de30477..37b743acbb7ba 100644 --- a/drivers/nvdimm/dax_devs.c +++ b/drivers/nvdimm/dax_devs.c @@ -106,12 +106,12 @@ int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns) nvdimm_bus_lock(&ndns->dev); nd_dax = nd_dax_alloc(nd_region); - nd_pfn = &nd_dax->nd_pfn; - dax_dev = nd_pfn_devinit(nd_pfn, ndns); + dax_dev = nd_dax_devinit(nd_dax, ndns); nvdimm_bus_unlock(&ndns->dev); if (!dax_dev) return -ENOMEM; pfn_sb = devm_kmalloc(dev, sizeof(*pfn_sb), GFP_KERNEL); + nd_pfn = &nd_dax->nd_pfn; nd_pfn->pfn_sb = pfn_sb; rc = nd_pfn_validate(nd_pfn, DAX_SIG); dev_dbg(dev, "dax: %s\n", rc == 0 ? dev_name(dax_dev) : ""); diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 2dbb1dca17b53..5ca06e9a2d292 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -600,6 +600,13 @@ struct nd_dax *to_nd_dax(struct device *dev); int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns); bool is_nd_dax(const struct device *dev); struct device *nd_dax_create(struct nd_region *nd_region); +static inline struct device *nd_dax_devinit(struct nd_dax *nd_dax, + struct nd_namespace_common *ndns) +{ + if (!nd_dax) + return NULL; + return nd_pfn_devinit(&nd_dax->nd_pfn, ndns); +} #else static inline int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns) diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c index f55d60922b87d..c3f07be4aa22a 100644 --- a/drivers/nvdimm/nd_virtio.c +++ b/drivers/nvdimm/nd_virtio.c @@ -97,7 +97,7 @@ static int virtio_pmem_flush(struct nd_region *nd_region) dev_info(&vdev->dev, "failed to send command to virtio pmem device\n"); err = -EIO; } else { - /* A host repsonse results in "host_ack" getting called */ + /* A host response results in "host_ack" getting called */ wait_event(req_data->host_acked, req_data->done); err = le32_to_cpu(req_data->resp.ret); } diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 586348125b61a..cfdfe0eaa5121 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -539,7 +539,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig) if (!nd_pfn->uuid) { /* - * When probing a namepace via nd_pfn_probe() the uuid + * When probing a namespace via nd_pfn_probe() the uuid * is NULL (see: nd_pfn_devinit()) we init settings from * pfn_sb */ diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 210fb77f51ba0..d81faa9d89c93 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -316,7 +316,7 @@ static long pmem_dax_direct_access(struct dax_device *dax_dev, * range, filesystem turns the normal pwrite to a dax_recovery_write. * * The recovery write consists of clearing media poison, clearing page - * HWPoison bit, reenable page-wide read-write permission, flush the + * HWPoison bit, re-enable page-wide read-write permission, flush the * caches and finally write. A competing pread thread will be held * off during the recovery process since data read back might not be * valid, and this is achieved by clearing the badblock records after diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c index c9b97aeabf854..2396d19ce5496 100644 --- a/drivers/nvdimm/virtio_pmem.c +++ b/drivers/nvdimm/virtio_pmem.c @@ -143,6 +143,28 @@ static void virtio_pmem_remove(struct virtio_device *vdev) virtio_reset_device(vdev); } +static int virtio_pmem_freeze(struct virtio_device *vdev) +{ + vdev->config->del_vqs(vdev); + virtio_reset_device(vdev); + + return 0; +} + +static int virtio_pmem_restore(struct virtio_device *vdev) +{ + int ret; + + ret = init_vq(vdev->priv); + if (ret) { + dev_err(&vdev->dev, "failed to initialize virtio pmem's vq\n"); + return ret; + } + virtio_device_ready(vdev); + + return 0; +} + static unsigned int features[] = { VIRTIO_PMEM_F_SHMEM_REGION, }; @@ -155,6 +177,8 @@ static struct virtio_driver virtio_pmem_driver = { .validate = virtio_pmem_validate, .probe = virtio_pmem_probe, .remove = virtio_pmem_remove, + .freeze = virtio_pmem_freeze, + .restore = virtio_pmem_restore, }; module_virtio_driver(virtio_pmem_driver); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 1a8d32a4a5c31..40e7be3b0339d 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1305,9 +1305,10 @@ static void nvme_queue_keep_alive_work(struct nvme_ctrl *ctrl) queue_delayed_work(nvme_wq, &ctrl->ka_work, delay); } -static void nvme_keep_alive_finish(struct request *rq, - blk_status_t status, struct nvme_ctrl *ctrl) +static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, + blk_status_t status) { + struct nvme_ctrl *ctrl = rq->end_io_data; unsigned long rtt = jiffies - (rq->deadline - rq->timeout); unsigned long delay = nvme_keep_alive_work_period(ctrl); enum nvme_ctrl_state state = nvme_ctrl_state(ctrl); @@ -1324,17 +1325,20 @@ static void nvme_keep_alive_finish(struct request *rq, delay = 0; } + blk_mq_free_request(rq); + if (status) { dev_err(ctrl->device, "failed nvme_keep_alive_end_io error=%d\n", status); - return; + return RQ_END_IO_NONE; } ctrl->ka_last_check_time = jiffies; ctrl->comp_seen = false; if (state == NVME_CTRL_LIVE || state == NVME_CTRL_CONNECTING) queue_delayed_work(nvme_wq, &ctrl->ka_work, delay); + return RQ_END_IO_NONE; } static void nvme_keep_alive_work(struct work_struct *work) @@ -1343,7 +1347,6 @@ static void nvme_keep_alive_work(struct work_struct *work) struct nvme_ctrl, ka_work); bool comp_seen = ctrl->comp_seen; struct request *rq; - blk_status_t status; ctrl->ka_last_check_time = jiffies; @@ -1366,9 +1369,9 @@ static void nvme_keep_alive_work(struct work_struct *work) nvme_init_request(rq, &ctrl->ka_cmd); rq->timeout = ctrl->kato * HZ; - status = blk_execute_rq(rq, false); - nvme_keep_alive_finish(rq, status, ctrl); - blk_mq_free_request(rq); + rq->end_io = nvme_keep_alive_end_io; + rq->end_io_data = ctrl; + blk_execute_rq_nowait(rq, false); } static void nvme_start_keep_alive(struct nvme_ctrl *ctrl) @@ -4600,6 +4603,11 @@ EXPORT_SYMBOL_GPL(nvme_alloc_admin_tag_set); void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl) { + /* + * As we're about to destroy the queue and free tagset + * we can not have keep-alive work running. + */ + nvme_stop_keep_alive(ctrl); blk_mq_destroy_queue(ctrl->admin_q); blk_put_queue(ctrl->admin_q); if (ctrl->ops->flags & NVME_F_FABRICS) { diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 6522ae16531c5..e8930146847af 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -120,12 +120,20 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, struct nvme_ns *ns = q->queuedata; struct block_device *bdev = ns ? ns->disk->part0 : NULL; bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk); + struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; bool has_metadata = meta_buffer && meta_len; struct bio *bio = NULL; int ret; - if (has_metadata && !supports_metadata) - return -EINVAL; + if (!nvme_ctrl_sgl_supported(ctrl)) + dev_warn_once(ctrl->device, "using unchecked data buffer\n"); + if (has_metadata) { + if (!supports_metadata) + return -EINVAL; + if (!nvme_ctrl_meta_sgl_supported(ctrl)) + dev_warn_once(ctrl->device, + "using unchecked metadata buffer\n"); + } if (ioucmd && (ioucmd->flags & IORING_URING_CMD_FIXED)) { struct iov_iter iter; diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index f04cfe3fb936c..a85d190942bdf 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -165,7 +165,8 @@ void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl) int srcu_idx; srcu_idx = srcu_read_lock(&ctrl->srcu); - list_for_each_entry_rcu(ns, &ctrl->namespaces, list) { + list_for_each_entry_srcu(ns, &ctrl->namespaces, list, + srcu_read_lock_held(&ctrl->srcu)) { if (!ns->head->disk) continue; kblockd_schedule_work(&ns->head->requeue_work); @@ -209,7 +210,8 @@ void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl) int srcu_idx; srcu_idx = srcu_read_lock(&ctrl->srcu); - list_for_each_entry_rcu(ns, &ctrl->namespaces, list) { + list_for_each_entry_srcu(ns, &ctrl->namespaces, list, + srcu_read_lock_held(&ctrl->srcu)) { nvme_mpath_clear_current_path(ns); kblockd_schedule_work(&ns->head->requeue_work); } @@ -224,7 +226,8 @@ void nvme_mpath_revalidate_paths(struct nvme_ns *ns) int srcu_idx; srcu_idx = srcu_read_lock(&head->srcu); - list_for_each_entry_rcu(ns, &head->list, siblings) { + list_for_each_entry_srcu(ns, &head->list, siblings, + srcu_read_lock_held(&head->srcu)) { if (capacity != get_capacity(ns->disk)) clear_bit(NVME_NS_READY, &ns->flags); } @@ -257,7 +260,8 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node) int found_distance = INT_MAX, fallback_distance = INT_MAX, distance; struct nvme_ns *found = NULL, *fallback = NULL, *ns; - list_for_each_entry_rcu(ns, &head->list, siblings) { + list_for_each_entry_srcu(ns, &head->list, siblings, + srcu_read_lock_held(&head->srcu)) { if (nvme_path_is_disabled(ns)) continue; @@ -356,7 +360,8 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head) unsigned int min_depth_opt = UINT_MAX, min_depth_nonopt = UINT_MAX; unsigned int depth; - list_for_each_entry_rcu(ns, &head->list, siblings) { + list_for_each_entry_srcu(ns, &head->list, siblings, + srcu_read_lock_held(&head->srcu)) { if (nvme_path_is_disabled(ns)) continue; @@ -424,7 +429,8 @@ static bool nvme_available_path(struct nvme_ns_head *head) if (!test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) return NULL; - list_for_each_entry_rcu(ns, &head->list, siblings) { + list_for_each_entry_srcu(ns, &head->list, siblings, + srcu_read_lock_held(&head->srcu)) { if (test_bit(NVME_CTRL_FAILFAST_EXPIRED, &ns->ctrl->flags)) continue; switch (nvme_ctrl_state(ns->ctrl)) { @@ -783,7 +789,8 @@ static int nvme_update_ana_state(struct nvme_ctrl *ctrl, return 0; srcu_idx = srcu_read_lock(&ctrl->srcu); - list_for_each_entry_rcu(ns, &ctrl->namespaces, list) { + list_for_each_entry_srcu(ns, &ctrl->namespaces, list, + srcu_read_lock_held(&ctrl->srcu)) { unsigned nsid; again: nsid = le32_to_cpu(desc->nsids[n]); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 900719c4c70c1..611b02c8a8b37 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -1123,7 +1123,15 @@ static inline void nvme_start_request(struct request *rq) static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl) { - return ctrl->sgls & ((1 << 0) | (1 << 1)); + return ctrl->sgls & (NVME_CTRL_SGLS_BYTE_ALIGNED | + NVME_CTRL_SGLS_DWORD_ALIGNED); +} + +static inline bool nvme_ctrl_meta_sgl_supported(struct nvme_ctrl *ctrl) +{ + if (ctrl->ops->flags & NVME_F_FABRICS) + return true; + return ctrl->sgls & NVME_CTRL_SGLS_MSDS; } #ifdef CONFIG_NVME_HOST_AUTH diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 5f2e3ad2cc521..4c644bb7f0692 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -43,6 +43,7 @@ */ #define NVME_MAX_KB_SZ 8192 #define NVME_MAX_SEGS 128 +#define NVME_MAX_META_SEGS 15 #define NVME_MAX_NR_ALLOCATIONS 5 static int use_threaded_interrupts; @@ -144,6 +145,7 @@ struct nvme_dev { struct sg_table *hmb_sgt; mempool_t *iod_mempool; + mempool_t *iod_meta_mempool; /* shadow doorbell buffer support: */ __le32 *dbbuf_dbs; @@ -239,6 +241,8 @@ struct nvme_iod { dma_addr_t first_dma; dma_addr_t meta_dma; struct sg_table sgt; + struct sg_table meta_sgt; + union nvme_descriptor meta_list; union nvme_descriptor list[NVME_MAX_NR_ALLOCATIONS]; }; @@ -506,6 +510,15 @@ static void nvme_commit_rqs(struct blk_mq_hw_ctx *hctx) spin_unlock(&nvmeq->sq_lock); } +static inline bool nvme_pci_metadata_use_sgls(struct nvme_dev *dev, + struct request *req) +{ + if (!nvme_ctrl_meta_sgl_supported(&dev->ctrl)) + return false; + return req->nr_integrity_segments > 1 || + nvme_req(req)->flags & NVME_REQ_USERCMD; +} + static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req, int nseg) { @@ -518,8 +531,10 @@ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req, return false; if (!nvmeq->qid) return false; + if (nvme_pci_metadata_use_sgls(dev, req)) + return true; if (!sgl_threshold || avg_seg_size < sgl_threshold) - return false; + return nvme_req(req)->flags & NVME_REQ_USERCMD; return true; } @@ -780,7 +795,8 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req, struct bio_vec bv = req_bvec(req); if (!is_pci_p2pdma_page(bv.bv_page)) { - if ((bv.bv_offset & (NVME_CTRL_PAGE_SIZE - 1)) + + if (!nvme_pci_metadata_use_sgls(dev, req) && + (bv.bv_offset & (NVME_CTRL_PAGE_SIZE - 1)) + bv.bv_len <= NVME_CTRL_PAGE_SIZE * 2) return nvme_setup_prp_simple(dev, req, &cmnd->rw, &bv); @@ -824,11 +840,69 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req, return ret; } -static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req, - struct nvme_command *cmnd) +static blk_status_t nvme_pci_setup_meta_sgls(struct nvme_dev *dev, + struct request *req) +{ + struct nvme_iod *iod = blk_mq_rq_to_pdu(req); + struct nvme_rw_command *cmnd = &iod->cmd.rw; + struct nvme_sgl_desc *sg_list; + struct scatterlist *sgl, *sg; + unsigned int entries; + dma_addr_t sgl_dma; + int rc, i; + + iod->meta_sgt.sgl = mempool_alloc(dev->iod_meta_mempool, GFP_ATOMIC); + if (!iod->meta_sgt.sgl) + return BLK_STS_RESOURCE; + + sg_init_table(iod->meta_sgt.sgl, req->nr_integrity_segments); + iod->meta_sgt.orig_nents = blk_rq_map_integrity_sg(req, + iod->meta_sgt.sgl); + if (!iod->meta_sgt.orig_nents) + goto out_free_sg; + + rc = dma_map_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req), + DMA_ATTR_NO_WARN); + if (rc) + goto out_free_sg; + + sg_list = dma_pool_alloc(dev->prp_small_pool, GFP_ATOMIC, &sgl_dma); + if (!sg_list) + goto out_unmap_sg; + + entries = iod->meta_sgt.nents; + iod->meta_list.sg_list = sg_list; + iod->meta_dma = sgl_dma; + + cmnd->flags = NVME_CMD_SGL_METASEG; + cmnd->metadata = cpu_to_le64(sgl_dma); + + sgl = iod->meta_sgt.sgl; + if (entries == 1) { + nvme_pci_sgl_set_data(sg_list, sgl); + return BLK_STS_OK; + } + + sgl_dma += sizeof(*sg_list); + nvme_pci_sgl_set_seg(sg_list, sgl_dma, entries); + for_each_sg(sgl, sg, entries, i) + nvme_pci_sgl_set_data(&sg_list[i + 1], sg); + + return BLK_STS_OK; + +out_unmap_sg: + dma_unmap_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req), 0); +out_free_sg: + mempool_free(iod->meta_sgt.sgl, dev->iod_meta_mempool); + return BLK_STS_RESOURCE; +} + +static blk_status_t nvme_pci_setup_meta_mptr(struct nvme_dev *dev, + struct request *req) { struct nvme_iod *iod = blk_mq_rq_to_pdu(req); struct bio_vec bv = rq_integrity_vec(req); + struct nvme_command *cmnd = &iod->cmd; iod->meta_dma = dma_map_bvec(dev->dev, &bv, rq_dma_dir(req), 0); if (dma_mapping_error(dev->dev, iod->meta_dma)) @@ -837,6 +911,13 @@ static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req, return BLK_STS_OK; } +static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req) +{ + if (nvme_pci_metadata_use_sgls(dev, req)) + return nvme_pci_setup_meta_sgls(dev, req); + return nvme_pci_setup_meta_mptr(dev, req); +} + static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req) { struct nvme_iod *iod = blk_mq_rq_to_pdu(req); @@ -845,6 +926,7 @@ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req) iod->aborted = false; iod->nr_allocations = -1; iod->sgt.nents = 0; + iod->meta_sgt.nents = 0; ret = nvme_setup_cmd(req->q->queuedata, req); if (ret) @@ -857,7 +939,7 @@ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req) } if (blk_integrity_rq(req)) { - ret = nvme_map_metadata(dev, req, &iod->cmd); + ret = nvme_map_metadata(dev, req); if (ret) goto out_unmap_data; } @@ -955,17 +1037,31 @@ static void nvme_queue_rqs(struct rq_list *rqlist) *rqlist = requeue_list; } +static __always_inline void nvme_unmap_metadata(struct nvme_dev *dev, + struct request *req) +{ + struct nvme_iod *iod = blk_mq_rq_to_pdu(req); + + if (!iod->meta_sgt.nents) { + dma_unmap_page(dev->dev, iod->meta_dma, + rq_integrity_vec(req).bv_len, + rq_dma_dir(req)); + return; + } + + dma_pool_free(dev->prp_small_pool, iod->meta_list.sg_list, + iod->meta_dma); + dma_unmap_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req), 0); + mempool_free(iod->meta_sgt.sgl, dev->iod_meta_mempool); +} + static __always_inline void nvme_pci_unmap_rq(struct request *req) { struct nvme_queue *nvmeq = req->mq_hctx->driver_data; struct nvme_dev *dev = nvmeq->dev; - if (blk_integrity_rq(req)) { - struct nvme_iod *iod = blk_mq_rq_to_pdu(req); - - dma_unmap_page(dev->dev, iod->meta_dma, - rq_integrity_vec(req).bv_len, rq_dma_dir(req)); - } + if (blk_integrity_rq(req)) + nvme_unmap_metadata(dev, req); if (blk_rq_nr_phys_segments(req)) nvme_unmap_data(dev, req); @@ -2761,6 +2857,7 @@ static void nvme_release_prp_pools(struct nvme_dev *dev) static int nvme_pci_alloc_iod_mempool(struct nvme_dev *dev) { + size_t meta_size = sizeof(struct scatterlist) * (NVME_MAX_META_SEGS + 1); size_t alloc_size = sizeof(struct scatterlist) * NVME_MAX_SEGS; dev->iod_mempool = mempool_create_node(1, @@ -2769,7 +2866,18 @@ static int nvme_pci_alloc_iod_mempool(struct nvme_dev *dev) dev_to_node(dev->dev)); if (!dev->iod_mempool) return -ENOMEM; + + dev->iod_meta_mempool = mempool_create_node(1, + mempool_kmalloc, mempool_kfree, + (void *)meta_size, GFP_KERNEL, + dev_to_node(dev->dev)); + if (!dev->iod_meta_mempool) + goto free; + return 0; +free: + mempool_destroy(dev->iod_mempool); + return -ENOMEM; } static void nvme_free_tagset(struct nvme_dev *dev) @@ -2834,6 +2942,11 @@ static void nvme_reset_work(struct work_struct *work) if (result) goto out; + if (nvme_ctrl_meta_sgl_supported(&dev->ctrl)) + dev->ctrl.max_integrity_segments = NVME_MAX_META_SEGS; + else + dev->ctrl.max_integrity_segments = 1; + nvme_dbbuf_dma_alloc(dev); result = nvme_setup_host_mem(dev); @@ -3101,11 +3214,6 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev, dev->ctrl.max_hw_sectors = min_t(u32, NVME_MAX_KB_SZ << 1, dma_opt_mapping_size(&pdev->dev) >> 9); dev->ctrl.max_segments = NVME_MAX_SEGS; - - /* - * There is no support for SGLs for metadata (yet), so we are limited to - * a single integrity segment for the separate metadata pointer. - */ dev->ctrl.max_integrity_segments = 1; return dev; @@ -3168,6 +3276,11 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (result) goto out_disable; + if (nvme_ctrl_meta_sgl_supported(&dev->ctrl)) + dev->ctrl.max_integrity_segments = NVME_MAX_META_SEGS; + else + dev->ctrl.max_integrity_segments = 1; + nvme_dbbuf_dma_alloc(dev); result = nvme_setup_host_mem(dev); @@ -3210,6 +3323,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) nvme_free_queues(dev, 0); out_release_iod_mempool: mempool_destroy(dev->iod_mempool); + mempool_destroy(dev->iod_meta_mempool); out_release_prp_pools: nvme_release_prp_pools(dev); out_dev_unmap: @@ -3275,6 +3389,7 @@ static void nvme_remove(struct pci_dev *pdev) nvme_dbbuf_dma_free(dev); nvme_free_queues(dev, 0); mempool_destroy(dev->iod_mempool); + mempool_destroy(dev->iod_meta_mempool); nvme_release_prp_pools(dev); nvme_dev_unmap(dev); nvme_uninit_ctrl(&dev->ctrl); diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index dc7922f226004..cf2d2c5039ddb 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -94,109 +94,137 @@ static int nvme_status_to_pr_err(int status) } } -static int nvme_send_pr_command(struct block_device *bdev, - struct nvme_command *c, void *data, unsigned int data_len) +static int __nvme_send_pr_command(struct block_device *bdev, u32 cdw10, + u32 cdw11, u8 op, void *data, unsigned int data_len) { - if (nvme_disk_is_ns_head(bdev->bd_disk)) - return nvme_send_ns_head_pr_command(bdev, c, data, data_len); + struct nvme_command c = { 0 }; - return nvme_send_ns_pr_command(bdev->bd_disk->private_data, c, data, - data_len); + c.common.opcode = op; + c.common.cdw10 = cpu_to_le32(cdw10); + c.common.cdw11 = cpu_to_le32(cdw11); + + if (nvme_disk_is_ns_head(bdev->bd_disk)) + return nvme_send_ns_head_pr_command(bdev, &c, data, data_len); + return nvme_send_ns_pr_command(bdev->bd_disk->private_data, &c, + data, data_len); } -static int nvme_pr_command(struct block_device *bdev, u32 cdw10, - u64 key, u64 sa_key, u8 op) +static int nvme_send_pr_command(struct block_device *bdev, u32 cdw10, u32 cdw11, + u8 op, void *data, unsigned int data_len) { - struct nvme_command c = { }; - u8 data[16] = { 0, }; int ret; - put_unaligned_le64(key, &data[0]); - put_unaligned_le64(sa_key, &data[8]); - - c.common.opcode = op; - c.common.cdw10 = cpu_to_le32(cdw10); - - ret = nvme_send_pr_command(bdev, &c, data, sizeof(data)); - if (ret < 0) - return ret; - - return nvme_status_to_pr_err(ret); + ret = __nvme_send_pr_command(bdev, cdw10, cdw11, op, data, data_len); + return ret < 0 ? ret : nvme_status_to_pr_err(ret); } -static int nvme_pr_register(struct block_device *bdev, u64 old, - u64 new, unsigned flags) +static int nvme_pr_register(struct block_device *bdev, u64 old_key, u64 new_key, + unsigned int flags) { + struct nvmet_pr_register_data data = { 0 }; u32 cdw10; if (flags & ~PR_FL_IGNORE_KEY) return -EOPNOTSUPP; - cdw10 = old ? 2 : 0; - cdw10 |= (flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0; - cdw10 |= (1 << 30) | (1 << 31); /* PTPL=1 */ - return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_register); + data.crkey = cpu_to_le64(old_key); + data.nrkey = cpu_to_le64(new_key); + + cdw10 = old_key ? NVME_PR_REGISTER_ACT_REPLACE : + NVME_PR_REGISTER_ACT_REG; + cdw10 |= (flags & PR_FL_IGNORE_KEY) ? NVME_PR_IGNORE_KEY : 0; + cdw10 |= NVME_PR_CPTPL_PERSIST; + + return nvme_send_pr_command(bdev, cdw10, 0, nvme_cmd_resv_register, + &data, sizeof(data)); } static int nvme_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type, unsigned flags) { + struct nvmet_pr_acquire_data data = { 0 }; u32 cdw10; if (flags & ~PR_FL_IGNORE_KEY) return -EOPNOTSUPP; - cdw10 = nvme_pr_type_from_blk(type) << 8; - cdw10 |= ((flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0); - return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_acquire); + data.crkey = cpu_to_le64(key); + + cdw10 = NVME_PR_ACQUIRE_ACT_ACQUIRE; + cdw10 |= nvme_pr_type_from_blk(type) << 8; + cdw10 |= (flags & PR_FL_IGNORE_KEY) ? NVME_PR_IGNORE_KEY : 0; + + return nvme_send_pr_command(bdev, cdw10, 0, nvme_cmd_resv_acquire, + &data, sizeof(data)); } static int nvme_pr_preempt(struct block_device *bdev, u64 old, u64 new, enum pr_type type, bool abort) { - u32 cdw10 = nvme_pr_type_from_blk(type) << 8 | (abort ? 2 : 1); + struct nvmet_pr_acquire_data data = { 0 }; + u32 cdw10; + + data.crkey = cpu_to_le64(old); + data.prkey = cpu_to_le64(new); - return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_acquire); + cdw10 = abort ? NVME_PR_ACQUIRE_ACT_PREEMPT_AND_ABORT : + NVME_PR_ACQUIRE_ACT_PREEMPT; + cdw10 |= nvme_pr_type_from_blk(type) << 8; + + return nvme_send_pr_command(bdev, cdw10, 0, nvme_cmd_resv_acquire, + &data, sizeof(data)); } static int nvme_pr_clear(struct block_device *bdev, u64 key) { - u32 cdw10 = 1 | (key ? 0 : 1 << 3); + struct nvmet_pr_release_data data = { 0 }; + u32 cdw10; + + data.crkey = cpu_to_le64(key); - return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); + cdw10 = NVME_PR_RELEASE_ACT_CLEAR; + cdw10 |= key ? 0 : NVME_PR_IGNORE_KEY; + + return nvme_send_pr_command(bdev, cdw10, 0, nvme_cmd_resv_release, + &data, sizeof(data)); } static int nvme_pr_release(struct block_device *bdev, u64 key, enum pr_type type) { - u32 cdw10 = nvme_pr_type_from_blk(type) << 8 | (key ? 0 : 1 << 3); + struct nvmet_pr_release_data data = { 0 }; + u32 cdw10; + + data.crkey = cpu_to_le64(key); - return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); + cdw10 = NVME_PR_RELEASE_ACT_RELEASE; + cdw10 |= nvme_pr_type_from_blk(type) << 8; + cdw10 |= key ? 0 : NVME_PR_IGNORE_KEY; + + return nvme_send_pr_command(bdev, cdw10, 0, nvme_cmd_resv_release, + &data, sizeof(data)); } static int nvme_pr_resv_report(struct block_device *bdev, void *data, u32 data_len, bool *eds) { - struct nvme_command c = { }; + u32 cdw10, cdw11; int ret; - c.common.opcode = nvme_cmd_resv_report; - c.common.cdw10 = cpu_to_le32(nvme_bytes_to_numd(data_len)); - c.common.cdw11 = cpu_to_le32(NVME_EXTENDED_DATA_STRUCT); + cdw10 = nvme_bytes_to_numd(data_len); + cdw11 = NVME_EXTENDED_DATA_STRUCT; *eds = true; retry: - ret = nvme_send_pr_command(bdev, &c, data, data_len); + ret = __nvme_send_pr_command(bdev, cdw10, cdw11, nvme_cmd_resv_report, + data, data_len); if (ret == NVME_SC_HOST_ID_INCONSIST && - c.common.cdw11 == cpu_to_le32(NVME_EXTENDED_DATA_STRUCT)) { - c.common.cdw11 = 0; + cdw11 == NVME_EXTENDED_DATA_STRUCT) { + cdw11 = 0; *eds = false; goto retry; } - if (ret < 0) - return ret; - - return nvme_status_to_pr_err(ret); + return ret < 0 ? ret : nvme_status_to_pr_err(ret); } static int nvme_pr_read_keys(struct block_device *bdev, diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 24a2759798d01..baf7d24901528 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1019,7 +1019,7 @@ static int nvme_rdma_setup_ctrl(struct nvme_rdma_ctrl *ctrl, bool new) goto destroy_admin; } - if (!(ctrl->ctrl.sgls & (1 << 2))) { + if (!(ctrl->ctrl.sgls & NVME_CTRL_SGLS_KSDBDS)) { ret = -EOPNOTSUPP; dev_err(ctrl->ctrl.device, "Mandatory keyed sgls are not supported!\n"); @@ -1051,7 +1051,7 @@ static int nvme_rdma_setup_ctrl(struct nvme_rdma_ctrl *ctrl, bool new) ctrl->ctrl.sqsize = ctrl->ctrl.maxcmd - 1; } - if (ctrl->ctrl.sgls & (1 << 20)) + if (ctrl->ctrl.sgls & NVME_CTRL_SGLS_SAOS) ctrl->use_inline_data = true; if (ctrl->ctrl.queue_count > 1) { diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 934b401fbc2ff..4fa8496a4d967 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -601,11 +601,12 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req) id->awun = 0; id->awupf = 0; - id->sgls = cpu_to_le32(1 << 0); /* we always support SGLs */ + /* we always support SGLs */ + id->sgls = cpu_to_le32(NVME_CTRL_SGLS_BYTE_ALIGNED); if (ctrl->ops->flags & NVMF_KEYED_SGLS) - id->sgls |= cpu_to_le32(1 << 2); + id->sgls |= cpu_to_le32(NVME_CTRL_SGLS_KSDBDS); if (req->port->inline_data_size) - id->sgls |= cpu_to_le32(1 << 20); + id->sgls |= cpu_to_le32(NVME_CTRL_SGLS_SAOS); strscpy(id->subnqn, ctrl->subsys->subsysnqn, sizeof(id->subnqn)); diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index d2c384f58028d..8671b7c974b93 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -246,6 +246,17 @@ config NVMEM_RAVE_SP_EEPROM help Say y here to enable Rave SP EEPROM support. +config NVMEM_RCAR_EFUSE + tristate "Renesas R-Car Gen4 E-FUSE support" + depends on (ARCH_RENESAS && ARM64) || COMPILE_TEST + depends on NVMEM + help + Enable support for reading the fuses in the E-FUSE or OTP + non-volatile memory block on Renesas R-Car Gen4 SoCs. + + This driver can also be built as a module. If so, the module + will be called nvmem-rcar-efuse. + config NVMEM_RMEM tristate "Reserved Memory Based Driver Support" depends on HAS_IOMEM diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index cdd01fbf1313b..5b77bbb6488bf 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -52,6 +52,8 @@ obj-$(CONFIG_NVMEM_QCOM_SEC_QFPROM) += nvmem_sec_qfprom.o nvmem_sec_qfprom-y := sec-qfprom.o obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o +obj-$(CONFIG_NVMEM_RCAR_EFUSE) += nvmem-rcar-efuse.o +nvmem-rcar-efuse-y := rcar-efuse.o obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o nvmem-rmem-y := rmem.o obj-$(CONFIG_NVMEM_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index 3d8c87835f4d6..b810df727b446 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -22,7 +22,7 @@ * * @dev: NVMEM device pointer * @nvmem_size: Size of the whole space available for NVRAM - * @data: NVRAM data copy stored to avoid poking underlaying flash controller + * @data: NVRAM data copy stored to avoid poking underlying flash controller * @data_len: NVRAM data size * @padding_byte: Padding value used to fill remaining space * @cells: Array of discovered NVMEM cells diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 33ffa2aa4c115..d6494dfc20a73 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -267,7 +267,7 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, count = round_down(count, nvmem->word_size); - if (!nvmem->reg_write) + if (!nvmem->reg_write || nvmem->read_only) return -EPERM; rc = nvmem_reg_write(nvmem, pos, buf, count); @@ -298,16 +298,25 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) } static umode_t nvmem_bin_attr_is_visible(struct kobject *kobj, - struct bin_attribute *attr, int i) + const struct bin_attribute *attr, + int i) { struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); - attr->size = nvmem->size; - return nvmem_bin_attr_get_umode(nvmem); } +static size_t nvmem_bin_attr_size(struct kobject *kobj, + const struct bin_attribute *attr, + int i) +{ + struct device *dev = kobj_to_dev(kobj); + struct nvmem_device *nvmem = to_nvmem_device(dev); + + return nvmem->size; +} + static umode_t nvmem_attr_is_visible(struct kobject *kobj, struct attribute *attr, int i) { @@ -383,6 +392,7 @@ static const struct attribute_group nvmem_bin_group = { .bin_attrs = nvmem_bin_attributes, .attrs = nvmem_attrs, .is_bin_visible = nvmem_bin_attr_is_visible, + .bin_size = nvmem_bin_attr_size, .is_visible = nvmem_attr_is_visible, }; @@ -1247,7 +1257,7 @@ static void devm_nvmem_device_release(struct device *dev, void *res) } /** - * devm_nvmem_device_put() - put alredy got nvmem device + * devm_nvmem_device_put() - put already got nvmem device * * @dev: Device that uses the nvmem device. * @nvmem: pointer to nvmem device allocated by devm_nvmem_cell_get(), @@ -1265,7 +1275,7 @@ void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem) EXPORT_SYMBOL_GPL(devm_nvmem_device_put); /** - * nvmem_device_put() - put alredy got nvmem device + * nvmem_device_put() - put already got nvmem device * * @nvmem: pointer to nvmem device that needs to be released. */ diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c index f13bbd1640868..8cfbe55a56cb3 100644 --- a/drivers/nvmem/imx-iim.c +++ b/drivers/nvmem/imx-iim.c @@ -115,11 +115,11 @@ static int imx_iim_probe(struct platform_device *pdev) if (IS_ERR(iim->clk)) return PTR_ERR(iim->clk); - cfg.name = "imx-iim", - cfg.read_only = true, - cfg.word_size = 1, - cfg.stride = 1, - cfg.reg_read = imx_iim_read, + cfg.name = "imx-iim"; + cfg.read_only = true; + cfg.word_size = 1; + cfg.stride = 1; + cfg.reg_read = imx_iim_read; cfg.dev = dev; cfg.size = drvdata->nregs; cfg.priv = iim; diff --git a/drivers/nvmem/lpc18xx_otp.c b/drivers/nvmem/lpc18xx_otp.c index adc9948e7b2ec..c41a0c58bec76 100644 --- a/drivers/nvmem/lpc18xx_otp.c +++ b/drivers/nvmem/lpc18xx_otp.c @@ -21,7 +21,7 @@ * LPC18xx OTP memory contains 4 banks with 4 32-bit words. Bank 0 starts * at offset 0 from the base. * - * Bank 0 contains the part ID for Flashless devices and is reseverd for + * Bank 0 contains the part ID for Flashless devices and is reserved for * devices with Flash. * Bank 1/2 is generale purpose or AES key storage for secure devices. * Bank 3 contains control data, USB ID and generale purpose words. diff --git a/drivers/nvmem/microchip-otpc.c b/drivers/nvmem/microchip-otpc.c index 7cf81738a3e0a..df979e8549fdb 100644 --- a/drivers/nvmem/microchip-otpc.c +++ b/drivers/nvmem/microchip-otpc.c @@ -156,7 +156,7 @@ static int mchp_otpc_read(void *priv, unsigned int off, void *val, /* * We reach this point with off being multiple of stride = 4 to * be able to cross the subsystem. Inside the driver we use continuous - * unsigned integer numbers for packet id, thus devide off by 4 + * unsigned integer numbers for packet id, thus divide off by 4 * before passing it to mchp_otpc_id_to_packet(). */ packet = mchp_otpc_id_to_packet(otpc, off / 4); diff --git a/drivers/nvmem/rcar-efuse.c b/drivers/nvmem/rcar-efuse.c new file mode 100644 index 0000000000000..f24bdb9cb5a72 --- /dev/null +++ b/drivers/nvmem/rcar-efuse.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Renesas R-Car E-FUSE/OTP Driver + * + * Copyright (C) 2024 Glider bv + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct rcar_fuse { + struct nvmem_keepout keepouts[2]; + struct nvmem_device *nvmem; + struct device *dev; + void __iomem *base; +}; + +struct rcar_fuse_data { + unsigned int bank; /* 0: PFC + E-FUSE, 1: OPT_MEM + E-FUSE */ + unsigned int start; /* inclusive */ + unsigned int end; /* exclusive */ +}; + +static int rcar_fuse_reg_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct rcar_fuse *fuse = priv; + int ret; + + ret = pm_runtime_resume_and_get(fuse->dev); + if (ret < 0) + return ret; + + __ioread32_copy(val, fuse->base + offset, bytes / 4); + + pm_runtime_put(fuse->dev); + + return 0; +} + +static int rcar_fuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct rcar_fuse_data *data = device_get_match_data(dev); + struct nvmem_config config = { + .dev = dev, + .name = "rcar-fuse", + .id = NVMEM_DEVID_NONE, + .owner = THIS_MODULE, + .type = NVMEM_TYPE_OTP, + .read_only = true, + .root_only = true, + .reg_read = rcar_fuse_reg_read, + .word_size = 4, + .stride = 4, + }; + struct rcar_fuse *fuse; + struct resource *res; + int ret; + + ret = devm_pm_runtime_enable(dev); + if (ret < 0) + return ret; + + fuse = devm_kzalloc(dev, sizeof(*fuse), GFP_KERNEL); + if (!fuse) + return -ENOMEM; + + fuse->base = devm_platform_get_and_ioremap_resource(pdev, data->bank, + &res); + if (IS_ERR(fuse->base)) + return PTR_ERR(fuse->base); + + fuse->dev = dev; + fuse->keepouts[0].start = 0; + fuse->keepouts[0].end = data->start; + fuse->keepouts[1].start = data->end; + fuse->keepouts[1].end = resource_size(res); + + config.keepout = fuse->keepouts; + config.nkeepout = ARRAY_SIZE(fuse->keepouts); + config.size = resource_size(res); + config.priv = fuse; + + fuse->nvmem = devm_nvmem_register(dev, &config); + if (IS_ERR(fuse->nvmem)) + return dev_err_probe(dev, PTR_ERR(fuse->nvmem), + "Failed to register NVMEM device\n"); + + return 0; +} + +static const struct rcar_fuse_data rcar_fuse_v3u = { + .bank = 0, + .start = 0x0c0, + .end = 0x0e8, +}; + +static const struct rcar_fuse_data rcar_fuse_s4 = { + .bank = 0, + .start = 0x0c0, + .end = 0x14c, +}; + +static const struct rcar_fuse_data rcar_fuse_v4h = { + .bank = 1, + .start = 0x100, + .end = 0x1a0, +}; + +static const struct rcar_fuse_data rcar_fuse_v4m = { + .bank = 1, + .start = 0x100, + .end = 0x110, +}; + +static const struct of_device_id rcar_fuse_match[] = { + { .compatible = "renesas,r8a779a0-efuse", .data = &rcar_fuse_v3u }, + { .compatible = "renesas,r8a779f0-efuse", .data = &rcar_fuse_s4 }, + { .compatible = "renesas,r8a779g0-otp", .data = &rcar_fuse_v4h }, + { .compatible = "renesas,r8a779h0-otp", .data = &rcar_fuse_v4m }, + { /* sentinel */ } +}; + +static struct platform_driver rcar_fuse_driver = { + .probe = rcar_fuse_probe, + .driver = { + .name = "rcar_fuse", + .of_match_table = rcar_fuse_match, + }, +}; +module_platform_driver(rcar_fuse_driver); + +MODULE_DESCRIPTION("Renesas R-Car E-FUSE/OTP driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Geert Uytterhoeven"); diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index f0b8cc23c46d3..50697cc3b07eb 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -2,6 +2,12 @@ config DTC bool +config GENERIC_BUILTIN_DTB + bool + +config BUILTIN_DTB_ALL + bool + menuconfig OF bool "Device Tree and Open Firmware support" help diff --git a/drivers/of/base.c b/drivers/of/base.c index a8b0c42bdc8e9..7dc394255a0a1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -644,6 +644,42 @@ struct device_node *of_get_next_child(const struct device_node *node, } EXPORT_SYMBOL(of_get_next_child); +/** + * of_get_next_child_with_prefix - Find the next child node with prefix + * @node: parent node + * @prev: previous child of the parent node, or NULL to get first + * @prefix: prefix that the node name should have + * + * This function is like of_get_next_child(), except that it automatically + * skips any nodes whose name doesn't have the given prefix. + * + * Return: A node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_get_next_child_with_prefix(const struct device_node *node, + struct device_node *prev, + const char *prefix) +{ + struct device_node *next; + unsigned long flags; + + if (!node) + return NULL; + + raw_spin_lock_irqsave(&devtree_lock, flags); + next = prev ? prev->sibling : node->child; + for (; next; next = next->sibling) { + if (!of_node_name_prefix(next, prefix)) + continue; + if (of_node_get(next)) + break; + } + of_node_put(prev); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return next; +} +EXPORT_SYMBOL(of_get_next_child_with_prefix); + static struct device_node *of_get_next_status_child(const struct device_node *node, struct device_node *prev, bool (*checker)(const struct device_node *)) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index d45a8df613801..0aba760f7577e 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -1072,3 +1072,47 @@ int of_changeset_add_prop_bool(struct of_changeset *ocs, struct device_node *np, return of_changeset_add_prop_helper(ocs, np, &prop); } EXPORT_SYMBOL_GPL(of_changeset_add_prop_bool); + +static int of_changeset_update_prop_helper(struct of_changeset *ocs, + struct device_node *np, + const struct property *pp) +{ + struct property *new_pp; + int ret; + + new_pp = __of_prop_dup(pp, GFP_KERNEL); + if (!new_pp) + return -ENOMEM; + + ret = of_changeset_update_property(ocs, np, new_pp); + if (ret) + __of_prop_free(new_pp); + + return ret; +} + +/** + * of_changeset_update_prop_string - Add a string property update to a changeset + * + * @ocs: changeset pointer + * @np: device node pointer + * @prop_name: name of the property to be updated + * @str: pointer to null terminated string + * + * Create a string property to be updated and add it to a changeset. + * + * Return: 0 on success, a negative error value in case of an error. + */ +int of_changeset_update_prop_string(struct of_changeset *ocs, + struct device_node *np, + const char *prop_name, const char *str) +{ + struct property prop = { + .name = (char *)prop_name, + .length = strlen(str) + 1, + .value = (void *)str, + }; + + return of_changeset_update_prop_helper(ocs, np, &prop); +} +EXPORT_SYMBOL_GPL(of_changeset_update_prop_string); diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 4bdb79ffa1017..430651e7424aa 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -263,9 +263,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *o int _opp_add_v1(struct opp_table *opp_table, struct device *dev, struct dev_pm_opp_data *data, bool dynamic); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk); -void _put_opp_list_kref(struct opp_table *opp_table); void _required_opps_available(struct dev_pm_opp *opp, int count); -void _update_set_required_opps(struct opp_table *opp_table); static inline bool lazy_linking_pending(struct opp_table *opp_table) { diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0d94e4a967d81..2fbd379923fd1 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -173,6 +173,15 @@ config PCI_PASID If unsure, say N. +config PCIE_TPH + bool "TLP Processing Hints" + help + This option adds support for PCIe TLP Processing Hints (TPH). + TPH allows endpoint devices to provide optimization hints, such as + desired caching behavior, for requests that target memory space. + These hints, called Steering Tags, can empower the system hardware + to optimize the utilization of platform resources. + config PCI_P2PDMA bool "PCI peer-to-peer transfer support" depends on ZONE_DEVICE @@ -305,6 +314,6 @@ source "drivers/pci/hotplug/Kconfig" source "drivers/pci/controller/Kconfig" source "drivers/pci/endpoint/Kconfig" source "drivers/pci/switch/Kconfig" -source "drivers/pci/pwrctl/Kconfig" +source "drivers/pci/pwrctrl/Kconfig" endif diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 374c5c06d92f9..67647f1880fb8 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \ obj-$(CONFIG_PCI) += msi/ obj-$(CONFIG_PCI) += pcie/ -obj-$(CONFIG_PCI) += pwrctl/ +obj-$(CONFIG_PCI) += pwrctrl/ ifdef CONFIG_PCI obj-$(CONFIG_PROC_FS) += proc.o @@ -36,6 +36,7 @@ obj-$(CONFIG_VGA_ARB) += vgaarb.o obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o +obj-$(CONFIG_PCIE_TPH) += tph.o # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 55c8536860518..98910bc0fcc4e 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -13,11 +13,24 @@ #include #include #include +#include #include #include #include "pci.h" +/* + * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond + * to P2P or CardBus bridge windows) go in a table. Additional ones (for + * buses below host bridges or subtractive decode bridges) go in the list. + * Use pci_bus_for_each_resource() to iterate through all the resources. + */ + +struct pci_bus_resource { + struct list_head list; + struct resource *res; +}; + void pci_add_resource_offset(struct list_head *resources, struct resource *res, resource_size_t offset) { @@ -46,8 +59,7 @@ void pci_free_resource_list(struct list_head *resources) } EXPORT_SYMBOL(pci_free_resource_list); -void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, - unsigned int flags) +void pci_bus_add_resource(struct pci_bus *bus, struct resource *res) { struct pci_bus_resource *bus_res; @@ -58,7 +70,6 @@ void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, } bus_res->res = res; - bus_res->flags = flags; list_add_tail(&bus_res->list, &bus->resources); } @@ -320,6 +331,47 @@ void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } +/* + * Create pwrctrl devices (if required) for the PCI devices to handle the power + * state. + */ +static void pci_pwrctrl_create_devices(struct pci_dev *dev) +{ + struct device_node *np = dev_of_node(&dev->dev); + struct device *parent = &dev->dev; + struct platform_device *pdev; + + /* + * First ensure that we are starting from a PCI bridge and it has a + * corresponding devicetree node. + */ + if (np && pci_is_bridge(dev)) { + /* + * Now look for the child PCI device nodes and create pwrctrl + * devices for them. The pwrctrl device drivers will manage the + * power state of the devices. + */ + for_each_available_child_of_node_scoped(np, child) { + /* + * First check whether the pwrctrl device really + * needs to be created or not. This is decided + * based on at least one of the power supplies + * being defined in the devicetree node of the + * device. + */ + if (!of_pci_supply_present(child)) { + pci_dbg(dev, "skipping OF node: %s\n", child->name); + return; + } + + /* Now create the pwrctrl device */ + pdev = of_platform_device_create(child, NULL, parent); + if (!pdev) + pci_err(dev, "failed to create OF node: %s\n", child->name); + } + } +} + /** * pci_bus_add_device - start driver for a single device * @dev: device to add @@ -329,6 +381,7 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } void pci_bus_add_device(struct pci_dev *dev) { struct device_node *dn = dev->dev.of_node; + struct platform_device *pdev; int retval; /* @@ -343,20 +396,28 @@ void pci_bus_add_device(struct pci_dev *dev) pci_proc_attach_device(dev); pci_bridge_d3_update(dev); + pci_pwrctrl_create_devices(dev); + + /* + * If the PCI device is associated with a pwrctrl device with a + * power supply, create a device link between the PCI device and + * pwrctrl device. This ensures that pwrctrl drivers are probed + * before PCI client drivers. + */ + pdev = of_find_device_by_node(dn); + if (pdev && of_pci_supply_present(dn)) { + if (!device_link_add(&dev->dev, &pdev->dev, + DL_FLAG_AUTOREMOVE_CONSUMER)) + pci_err(dev, "failed to add device link to power control device %s\n", + pdev->name); + } + dev->match_driver = !dn || of_device_is_available(dn); retval = device_attach(&dev->dev); if (retval < 0 && retval != -EPROBE_DEFER) pci_warn(dev, "device attach failed (%d)\n", retval); - pci_dev_assign_added(dev, true); - - if (dev_of_node(&dev->dev) && pci_is_bridge(dev)) { - retval = of_platform_populate(dev_of_node(&dev->dev), NULL, NULL, - &dev->dev); - if (retval) - pci_err(dev, "failed to populate child OF nodes (%d)\n", - retval); - } + pci_dev_assign_added(dev); } EXPORT_SYMBOL_GPL(pci_bus_add_device); @@ -389,41 +450,23 @@ void pci_bus_add_devices(const struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_add_devices); -static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), - void *userdata, bool locked) +static int __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), + void *userdata) { struct pci_dev *dev; - struct pci_bus *bus; - struct list_head *next; - int retval; + int ret = 0; - bus = top; - if (!locked) - down_read(&pci_bus_sem); - next = top->devices.next; - for (;;) { - if (next == &bus->devices) { - /* end of this bus, go up or finish */ - if (bus == top) + list_for_each_entry(dev, &top->devices, bus_list) { + ret = cb(dev, userdata); + if (ret) + break; + if (dev->subordinate) { + ret = __pci_walk_bus(dev->subordinate, cb, userdata); + if (ret) break; - next = bus->self->bus_list.next; - bus = bus->self->bus; - continue; } - dev = list_entry(next, struct pci_dev, bus_list); - if (dev->subordinate) { - /* this is a pci-pci bridge, do its devices next */ - next = dev->subordinate->devices.next; - bus = dev->subordinate; - } else - next = dev->bus_list.next; - - retval = cb(dev, userdata); - if (retval) - break; } - if (!locked) - up_read(&pci_bus_sem); + return ret; } /** @@ -441,7 +484,9 @@ static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void */ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata) { - __pci_walk_bus(top, cb, userdata, false); + down_read(&pci_bus_sem); + __pci_walk_bus(top, cb, userdata); + up_read(&pci_bus_sem); } EXPORT_SYMBOL_GPL(pci_walk_bus); @@ -449,9 +494,8 @@ void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void * { lockdep_assert_held(&pci_bus_sem); - __pci_walk_bus(top, cb, userdata, true); + __pci_walk_bus(top, cb, userdata); } -EXPORT_SYMBOL_GPL(pci_walk_bus_locked); struct pci_bus *pci_bus_get(struct pci_bus *bus) { diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 284f2e0e4d261..0341d51d6aed1 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -386,6 +386,13 @@ static const struct j721e_pcie_data j784s4_pcie_ep_data = { .max_lanes = 4, }; +static const struct j721e_pcie_data j722s_pcie_rc_data = { + .mode = PCI_MODE_RC, + .linkdown_irq_regfield = J7200_LINK_DOWN, + .byte_access_allowed = true, + .max_lanes = 1, +}; + static const struct of_device_id of_j721e_pcie_match[] = { { .compatible = "ti,j721e-pcie-host", @@ -419,6 +426,10 @@ static const struct of_device_id of_j721e_pcie_match[] = { .compatible = "ti,j784s4-pcie-ep", .data = &j784s4_pcie_ep_data, }, + { + .compatible = "ti,j722s-pcie-host", + .data = &j722s_pcie_rc_data, + }, {}, }; @@ -572,15 +583,14 @@ static int j721e_pcie_probe(struct platform_device *pdev) pcie->refclk = clk; /* - * The "Power Sequencing and Reset Signal Timings" table of the - * PCI Express Card Electromechanical Specification, Revision - * 5.1, Section 2.9.2, Symbol "T_PERST-CLK", indicates PERST# - * should be deasserted after minimum of 100us once REFCLK is - * stable. The REFCLK to the connector in RC mode is selected - * while enabling the PHY. So deassert PERST# after 100 us. + * Section 2.2 of the PCI Express Card Electromechanical + * Specification (Revision 5.1) mandates that the deassertion + * of the PERST# signal should be delayed by 100 ms (TPVPERL). + * This shall ensure that the power and the reference clock + * are stable. */ if (gpiod) { - fsleep(PCIE_T_PERST_CLK_US); + msleep(PCIE_T_PVPERL_MS); gpiod_set_value_cansleep(gpiod, 1); } @@ -671,15 +681,14 @@ static int j721e_pcie_resume_noirq(struct device *dev) return ret; /* - * The "Power Sequencing and Reset Signal Timings" table of the - * PCI Express Card Electromechanical Specification, Revision - * 5.1, Section 2.9.2, Symbol "T_PERST-CLK", indicates PERST# - * should be deasserted after minimum of 100us once REFCLK is - * stable. The REFCLK to the connector in RC mode is selected - * while enabling the PHY. So deassert PERST# after 100 us. + * Section 2.2 of the PCI Express Card Electromechanical + * Specification (Revision 5.1) mandates that the deassertion + * of the PERST# signal should be delayed by 100 ms (TPVPERL). + * This shall ensure that the power and the reference clock + * are stable. */ if (pcie->reset_gpio) { - fsleep(PCIE_T_PERST_CLK_US); + msleep(PCIE_T_PVPERL_MS); gpiod_set_value_cansleep(pcie->reset_gpio, 1); } @@ -712,7 +721,7 @@ static DEFINE_NOIRQ_DEV_PM_OPS(j721e_pcie_pm_ops, static struct platform_driver j721e_pcie_driver = { .probe = j721e_pcie_probe, - .remove_new = j721e_pcie_remove, + .remove = j721e_pcie_remove, .driver = { .name = "j721e-pcie", .of_match_table = of_j721e_pcie_match, diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index 4251fac5e3106..204e045aed8c3 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c @@ -197,7 +197,7 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie) phy_count = of_property_count_strings(np, "phy-names"); if (phy_count < 1) { - dev_err(dev, "no phy-names. PHY will not be initialized\n"); + dev_info(dev, "no \"phy-names\" property found; PHY will not be initialized\n"); pcie->phy_count = 0; return 0; } @@ -260,7 +260,7 @@ static int cdns_pcie_resume_noirq(struct device *dev) ret = cdns_pcie_enable_phy(pcie); if (ret) { - dev_err(dev, "failed to enable phy\n"); + dev_err(dev, "failed to enable PHY\n"); return ret; } diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index fa45da28a218b..6a830166d37ff 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -383,7 +383,7 @@ static const struct of_device_id exynos_pcie_of_match[] = { static struct platform_driver exynos_pcie_driver = { .probe = exynos_pcie_probe, - .remove_new = exynos_pcie_remove, + .remove = exynos_pcie_remove, .driver = { .name = "exynos-pcie", .of_match_table = exynos_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 808d1f1054173..c8d5c90aa4d45 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -82,6 +82,11 @@ enum imx_pcie_variants { #define IMX_PCIE_FLAG_HAS_SERDES BIT(6) #define IMX_PCIE_FLAG_SUPPORT_64BIT BIT(7) #define IMX_PCIE_FLAG_CPU_ADDR_FIXUP BIT(8) +/* + * Because of ERR005723 (PCIe does not support L2 power down) we need to + * workaround suspend resume on some devices which are affected by this errata. + */ +#define IMX_PCIE_FLAG_BROKEN_SUSPEND BIT(9) #define imx_check_flag(pci, val) (pci->drvdata->flags & val) @@ -1237,9 +1242,19 @@ static int imx_pcie_suspend_noirq(struct device *dev) return 0; imx_pcie_msi_save_restore(imx_pcie, true); - imx_pcie_pm_turnoff(imx_pcie); - imx_pcie_stop_link(imx_pcie->pci); - imx_pcie_host_exit(pp); + if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_BROKEN_SUSPEND)) { + /* + * The minimum for a workaround would be to set PERST# and to + * set the PCIE_TEST_PD flag. However, we can also disable the + * clock which saves some power. + */ + imx_pcie_assert_core_reset(imx_pcie); + imx_pcie->drvdata->enable_ref_clk(imx_pcie, false); + } else { + imx_pcie_pm_turnoff(imx_pcie); + imx_pcie_stop_link(imx_pcie->pci); + imx_pcie_host_exit(pp); + } return 0; } @@ -1253,14 +1268,32 @@ static int imx_pcie_resume_noirq(struct device *dev) if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_SUPPORTS_SUSPEND)) return 0; - ret = imx_pcie_host_init(pp); - if (ret) - return ret; - imx_pcie_msi_save_restore(imx_pcie, false); - dw_pcie_setup_rc(pp); + if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_BROKEN_SUSPEND)) { + ret = imx_pcie->drvdata->enable_ref_clk(imx_pcie, true); + if (ret) + return ret; + ret = imx_pcie_deassert_core_reset(imx_pcie); + if (ret) + return ret; + /* + * Using PCIE_TEST_PD seems to disable MSI and powers down the + * root complex. This is why we have to setup the rc again and + * why we have to restore the MSI register. + */ + ret = dw_pcie_setup_rc(&imx_pcie->pci->pp); + if (ret) + return ret; + imx_pcie_msi_save_restore(imx_pcie, false); + } else { + ret = imx_pcie_host_init(pp); + if (ret) + return ret; + imx_pcie_msi_save_restore(imx_pcie, false); + dw_pcie_setup_rc(pp); - if (imx_pcie->link_is_up) - imx_pcie_start_link(imx_pcie->pci); + if (imx_pcie->link_is_up) + imx_pcie_start_link(imx_pcie->pci); + } return 0; } @@ -1485,7 +1518,9 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, .flags = IMX_PCIE_FLAG_IMX_PHY | - IMX_PCIE_FLAG_IMX_SPEED_CHANGE, + IMX_PCIE_FLAG_IMX_SPEED_CHANGE | + IMX_PCIE_FLAG_BROKEN_SUSPEND | + IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", .clk_names = imx6q_clks, diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 2219b1a866faf..63bd5003da458 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -455,6 +455,17 @@ static void __iomem *ks_pcie_other_map_bus(struct pci_bus *bus, struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); u32 reg; + /* + * Checking whether the link is up here is a last line of defense + * against platforms that forward errors on the system bus as + * SError upon PCI configuration transactions issued when the link + * is down. This check is racy by definition and does not stop + * the system from triggering an SError if the link goes down + * after this check is performed. + */ + if (!dw_pcie_link_up(pci)) + return NULL; + reg = CFG_BUS(bus->number) | CFG_DEVICE(PCI_SLOT(devfn)) | CFG_FUNC(PCI_FUNC(devfn)); if (!pci_is_root_bus(bus->parent)) @@ -1093,6 +1104,7 @@ static int ks_pcie_am654_set_mode(struct device *dev, static const struct ks_pcie_of_data ks_pcie_rc_of_data = { .host_ops = &ks_pcie_host_ops, + .mode = DW_PCIE_RC_TYPE, .version = DW_PCIE_VER_365A, }; @@ -1363,7 +1375,7 @@ static void ks_pcie_remove(struct platform_device *pdev) static struct platform_driver ks_pcie_driver = { .probe = ks_pcie_probe, - .remove_new = ks_pcie_remove, + .remove = ks_pcie_remove, .driver = { .name = "keystone-pcie", .of_match_table = ks_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c index 76d0ddea80075..1340edc18d128 100644 --- a/drivers/pci/controller/dwc/pcie-bt1.c +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -632,7 +632,7 @@ MODULE_DEVICE_TABLE(of, bt1_pcie_of_match); static struct platform_driver bt1_pcie_driver = { .probe = bt1_pcie_probe, - .remove_new = bt1_pcie_remove, + .remove = bt1_pcie_remove, .driver = { .name = "bt1-pcie", .of_match_table = bt1_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 43ba5c6738df1..f3ac7d46a855b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -268,6 +268,20 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, return -EINVAL; } +static u64 dw_pcie_ep_align_addr(struct pci_epc *epc, u64 pci_addr, + size_t *pci_size, size_t *offset) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u64 mask = pci->region_align - 1; + size_t ofst = pci_addr & mask; + + *pci_size = ALIGN(ofst + *pci_size, epc->mem->window.page_size); + *offset = ofst; + + return pci_addr & ~mask; +} + static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t addr) { @@ -280,6 +294,7 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (ret < 0) return; + ep->outbound_addr[atu_index] = 0; dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index); clear_bit(atu_index, ep->ob_window_map); } @@ -444,6 +459,7 @@ static const struct pci_epc_ops epc_ops = { .write_header = dw_pcie_ep_write_header, .set_bar = dw_pcie_ep_set_bar, .clear_bar = dw_pcie_ep_clear_bar, + .align_addr = dw_pcie_ep_align_addr, .map_addr = dw_pcie_ep_map_addr, .unmap_addr = dw_pcie_ep_unmap_addr, .set_msi = dw_pcie_ep_set_msi, @@ -488,7 +504,8 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u32 msg_addr_lower, msg_addr_upper, reg; struct dw_pcie_ep_func *ep_func; struct pci_epc *epc = ep->epc; - unsigned int aligned_offset; + size_t map_size = sizeof(u32); + size_t offset; u16 msg_ctrl, msg_data; bool has_upper; u64 msg_addr; @@ -516,14 +533,13 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, } msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower; - aligned_offset = msg_addr & (epc->mem->window.page_size - 1); - msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size); + msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, - epc->mem->window.page_size); + map_size); if (ret) return ret; - writel(msg_data | (interrupt_num - 1), ep->msi_mem + aligned_offset); + writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset); dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); @@ -574,8 +590,9 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, struct pci_epf_msix_tbl *msix_tbl; struct dw_pcie_ep_func *ep_func; struct pci_epc *epc = ep->epc; + size_t map_size = sizeof(u32); + size_t offset; u32 reg, msg_data, vec_ctrl; - unsigned int aligned_offset; u32 tbl_offset; u64 msg_addr; int ret; @@ -600,14 +617,13 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, return -EPERM; } - aligned_offset = msg_addr & (epc->mem->window.page_size - 1); - msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size); + msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, - epc->mem->window.page_size); + map_size); if (ret) return ret; - writel(msg_data, ep->msi_mem + aligned_offset); + writel(msg_data, ep->msi_mem + offset); dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); @@ -689,7 +705,7 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) * for 1 MB BAR size only. */ for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) - dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0); + dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4)); } dw_pcie_setup(pci); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 3e41865c72904..d2291c3ceb8be 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -474,8 +474,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (pci_msi_enabled()) { pp->has_msi_ctrl = !(pp->ops->msi_init || - of_property_read_bool(np, "msi-parent") || - of_property_read_bool(np, "msi-map")); + of_property_present(np, "msi-parent") || + of_property_present(np, "msi-map")); /* * For the has_msi_ctrl case the default assignment is handled diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 7a11c618b9d9c..615a0e3e6d7eb 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -439,7 +439,7 @@ MODULE_DEVICE_TABLE(of, histb_pcie_of_match); static struct platform_driver histb_pcie_platform_driver = { .probe = histb_pcie_probe, - .remove_new = histb_pcie_remove, + .remove = histb_pcie_remove, .driver = { .name = "histb-pcie", .of_match_table = histb_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 676d2aba4fbde..9b53b8f6f268e 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -443,7 +443,7 @@ static const struct of_device_id of_intel_pcie_match[] = { static struct platform_driver intel_pcie_driver = { .probe = intel_pcie_probe, - .remove_new = intel_pcie_remove, + .remove = intel_pcie_remove, .driver = { .name = "intel-gw-pcie", .of_match_table = of_intel_pcie_match, diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index 85a2c77b1835a..1b2088acb538b 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -769,7 +769,7 @@ static int kirin_pcie_probe(struct platform_device *pdev) static struct platform_driver kirin_pcie_driver = { .probe = kirin_pcie_probe, - .remove_new = kirin_pcie_remove, + .remove = kirin_pcie_remove, .driver = { .name = "kirin-pcie", .of_match_table = kirin_pcie_match, diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index e588fcc545893..c08f64d7a825f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -396,6 +396,10 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) return ret; } + /* Perform cleanup that requires refclk */ + pci_epc_deinit_notify(pci->ep.epc); + dw_pcie_ep_cleanup(&pci->ep); + /* Assert WAKE# to RC to indicate device is ready */ gpiod_set_value_cansleep(pcie_ep->wake, 1); usleep_range(WAKE_DELAY_US, WAKE_DELAY_US + 500); @@ -540,8 +544,6 @@ static void qcom_pcie_perst_assert(struct dw_pcie *pci) { struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci); - pci_epc_deinit_notify(pci->ep.epc); - dw_pcie_ep_cleanup(&pci->ep); qcom_pcie_disable_resources(pcie_ep); pcie_ep->link_status = QCOM_PCIE_EP_LINK_DISABLED; } @@ -937,7 +939,7 @@ MODULE_DEVICE_TABLE(of, qcom_pcie_ep_match); static struct platform_driver qcom_pcie_ep_driver = { .probe = qcom_pcie_ep_probe, - .remove_new = qcom_pcie_ep_remove, + .remove = qcom_pcie_ep_remove, .driver = { .name = "qcom-pcie-ep", .of_match_table = qcom_pcie_ep_match, diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index ef44a82be058b..dc102d8bd58c6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -133,6 +133,7 @@ /* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */ #define PARF_INT_ALL_LINK_UP BIT(13) +#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23) /* PARF_NO_SNOOP_OVERIDE register fields */ #define WR_NO_SNOOP_OVERIDE_EN BIT(1) @@ -1364,6 +1365,16 @@ static const struct qcom_pcie_ops ops_1_9_0 = { .config_sid = qcom_pcie_config_sid_1_9_0, }; +/* Qcom IP rev.: 1.21.0 Synopsys IP rev.: 5.60a */ +static const struct qcom_pcie_ops ops_1_21_0 = { + .get_resources = qcom_pcie_get_resources_2_7_0, + .init = qcom_pcie_init_2_7_0, + .post_init = qcom_pcie_post_init_2_7_0, + .host_post_init = qcom_pcie_host_post_init_2_7_0, + .deinit = qcom_pcie_deinit_2_7_0, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, +}; + /* Qcom IP rev.: 2.9.0 Synopsys IP rev.: 5.00a */ static const struct qcom_pcie_ops ops_2_9_0 = { .get_resources = qcom_pcie_get_resources_2_9_0, @@ -1411,7 +1422,7 @@ static const struct qcom_pcie_cfg cfg_2_9_0 = { }; static const struct qcom_pcie_cfg cfg_sc8280xp = { - .ops = &ops_1_9_0, + .ops = &ops_1_21_0, .no_l0s = true, }; @@ -1716,7 +1727,8 @@ static int qcom_pcie_probe(struct platform_device *pdev) goto err_host_deinit; } - writel_relaxed(PARF_INT_ALL_LINK_UP, pcie->parf + PARF_INT_ALL_MASK); + writel_relaxed(PARF_INT_ALL_LINK_UP | PARF_INT_MSI_DEV_0_7, + pcie->parf + PARF_INT_ALL_MASK); } qcom_pcie_icc_opp_update(pcie); @@ -1828,6 +1840,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 }, { .compatible = "qcom,pcie-ipq8074", .data = &cfg_2_3_3 }, { .compatible = "qcom,pcie-ipq8074-gen3", .data = &cfg_2_9_0 }, + { .compatible = "qcom,pcie-ipq9574", .data = &cfg_2_9_0 }, { .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 }, { .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 }, { .compatible = "qcom,pcie-sa8540p", .data = &cfg_sc8280xp }, @@ -1843,7 +1856,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-sm8450-pcie0", .data = &cfg_1_9_0 }, { .compatible = "qcom,pcie-sm8450-pcie1", .data = &cfg_1_9_0 }, { .compatible = "qcom,pcie-sm8550", .data = &cfg_1_9_0 }, - { .compatible = "qcom,pcie-x1e80100", .data = &cfg_1_9_0 }, + { .compatible = "qcom,pcie-x1e80100", .data = &cfg_sc8280xp }, { } }; diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 3a5511c3f7d97..fc872dd35029c 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -775,7 +775,7 @@ static struct platform_driver rcar_gen4_pcie_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = rcar_gen4_pcie_probe, - .remove_new = rcar_gen4_pcie_remove, + .remove = rcar_gen4_pcie_remove, }; module_platform_driver(rcar_gen4_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index c1394f2ab63ff..5103995cd6c75 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1704,9 +1704,6 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) if (ret) dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret); - pci_epc_deinit_notify(pcie->pci.ep.epc); - dw_pcie_ep_cleanup(&pcie->pci.ep); - reset_control_assert(pcie->core_rst); tegra_pcie_disable_phy(pcie); @@ -1785,6 +1782,10 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) goto fail_phy; } + /* Perform cleanup that requires refclk */ + pci_epc_deinit_notify(pcie->pci.ep.epc); + dw_pcie_ep_cleanup(&pcie->pci.ep); + /* Clear any stale interrupt statuses */ appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0); appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0); @@ -2493,7 +2494,7 @@ static const struct dev_pm_ops tegra_pcie_dw_pm_ops = { static struct platform_driver tegra_pcie_dw_driver = { .probe = tegra_pcie_dw_probe, - .remove_new = tegra_pcie_dw_remove, + .remove = tegra_pcie_dw_remove, .shutdown = tegra_pcie_dw_shutdown, .driver = { .name = "tegra194-pcie", diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index a598a98247ce9..a29796cce420a 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1996,7 +1996,7 @@ static struct platform_driver advk_pcie_driver = { .of_match_table = advk_pcie_of_match_table, }, .probe = advk_pcie_probe, - .remove_new = advk_pcie_remove, + .remove = advk_pcie_remove, }; module_platform_driver(advk_pcie_driver); diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c index 5f06f94db7b17..4051b9b61dace 100644 --- a/drivers/pci/controller/pci-host-generic.c +++ b/drivers/pci/controller/pci-host-generic.c @@ -82,7 +82,7 @@ static struct platform_driver gen_pci_driver = { .of_match_table = gen_pci_of_match, }, .probe = pci_host_common_probe, - .remove_new = pci_host_common_remove, + .remove = pci_host_common_remove, }; module_platform_driver(gen_pci_driver); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 29fe09c99e7d9..46d3afe1d3081 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1727,7 +1727,7 @@ static struct platform_driver mvebu_pcie_driver = { .pm = &mvebu_pcie_pm_ops, }, .probe = mvebu_pcie_probe, - .remove_new = mvebu_pcie_remove, + .remove = mvebu_pcie_remove, }; module_platform_driver(mvebu_pcie_driver); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index d7517c3976e7f..b3cdbc5927de3 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1460,7 +1460,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) pcie->cs = *res; /* constrain configuration space to 4 KiB */ - pcie->cs.end = pcie->cs.start + SZ_4K - 1; + resource_set_size(&pcie->cs, SZ_4K); pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); if (IS_ERR(pcie->cfg)) { @@ -2800,6 +2800,6 @@ static struct platform_driver tegra_pcie_driver = { .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, - .remove_new = tegra_pcie_remove, + .remove = tegra_pcie_remove, }; module_platform_driver(tegra_pcie_driver); diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c index 06a9855cb431c..f1bd5de67997c 100644 --- a/drivers/pci/controller/pci-thunder-pem.c +++ b/drivers/pci/controller/pci-thunder-pem.c @@ -400,9 +400,9 @@ static int thunder_pem_acpi_init(struct pci_config_window *cfg) * Reserve 64K size PEM specific resources. The full 16M range * size is required for thunder_pem_init() call. */ - res_pem->end = res_pem->start + SZ_64K - 1; + resource_set_size(res_pem, SZ_64K); thunder_pem_reserve_range(dev, root->segment, res_pem); - res_pem->end = res_pem->start + SZ_16M - 1; + resource_set_size(res_pem, SZ_16M); /* Reserve PCI configuration space as well. */ thunder_pem_reserve_range(dev, root->segment, &cfg->res); diff --git a/drivers/pci/controller/pci-xgene-msi.c b/drivers/pci/controller/pci-xgene-msi.c index 3ce38dfd0d29f..88c0977bc41a4 100644 --- a/drivers/pci/controller/pci-xgene-msi.c +++ b/drivers/pci/controller/pci-xgene-msi.c @@ -518,7 +518,7 @@ static struct platform_driver xgene_msi_driver = { .of_match_table = xgene_msi_match_table, }, .probe = xgene_msi_probe, - .remove_new = xgene_msi_remove, + .remove = xgene_msi_remove, }; static int __init xgene_pcie_msi_init(void) diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c index e36a6e158d23c..e1cee3c0575fb 100644 --- a/drivers/pci/controller/pcie-altera-msi.c +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -267,7 +267,7 @@ static struct platform_driver altera_msi_driver = { .of_match_table = altera_msi_of_match, }, .probe = altera_msi_probe, - .remove_new = altera_msi_remove, + .remove = altera_msi_remove, }; static int __init altera_msi_init(void) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 650b2dd81c482..eb55a7f8573a8 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -815,10 +815,10 @@ static void altera_pcie_remove(struct platform_device *pdev) } static struct platform_driver altera_pcie_driver = { - .probe = altera_pcie_probe, - .remove_new = altera_pcie_remove, + .probe = altera_pcie_probe, + .remove = altera_pcie_remove, .driver = { - .name = "altera-pcie", + .name = "altera-pcie", .of_match_table = altera_pcie_of_match, }, }; diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 9321280f6edba..e733a27dc8df8 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1928,7 +1928,7 @@ static const struct dev_pm_ops brcm_pcie_pm_ops = { static struct platform_driver brcm_pcie_driver = { .probe = brcm_pcie_probe, - .remove_new = brcm_pcie_remove, + .remove = brcm_pcie_remove, .driver = { .name = "brcm-pcie", .of_match_table = brcm_pcie_match, diff --git a/drivers/pci/controller/pcie-hisi-error.c b/drivers/pci/controller/pcie-hisi-error.c index ad9d5ffcd9e34..aaf1ed2b6e597 100644 --- a/drivers/pci/controller/pcie-hisi-error.c +++ b/drivers/pci/controller/pcie-hisi-error.c @@ -317,7 +317,7 @@ static struct platform_driver hisi_pcie_error_handler_driver = { .acpi_match_table = hisi_pcie_acpi_match, }, .probe = hisi_pcie_error_handler_probe, - .remove_new = hisi_pcie_error_handler_remove, + .remove = hisi_pcie_error_handler_remove, }; module_platform_driver(hisi_pcie_error_handler_driver); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 4e6aa882a567a..0cb78c583c7ea 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -134,7 +134,7 @@ static struct platform_driver iproc_pltfm_pcie_driver = { .of_match_table = of_match_ptr(iproc_pcie_of_match_table), }, .probe = iproc_pltfm_pcie_probe, - .remove_new = iproc_pltfm_pcie_remove, + .remove = iproc_pltfm_pcie_remove, .shutdown = iproc_pltfm_pcie_shutdown, }; module_platform_driver(iproc_pltfm_pcie_driver); diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 66ce4b5d309bb..be52e3a123abd 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -28,7 +28,12 @@ #include "../pci.h" +#define PCIE_BASE_CFG_REG 0x14 +#define PCIE_BASE_CFG_SPEED GENMASK(15, 8) + #define PCIE_SETTING_REG 0x80 +#define PCIE_SETTING_LINK_WIDTH GENMASK(11, 8) +#define PCIE_SETTING_GEN_SUPPORT GENMASK(14, 12) #define PCIE_PCI_IDS_1 0x9c #define PCI_CLASS(class) (class << 8) #define PCIE_RC_MODE BIT(0) @@ -125,6 +130,9 @@ struct mtk_gen3_pcie; +#define PCIE_CONF_LINK2_CTL_STS (PCIE_CFG_OFFSET_ADDR + 0xb0) +#define PCIE_CONF_LINK2_LCR2_LINK_SPEED GENMASK(3, 0) + /** * struct mtk_gen3_pcie_pdata - differentiate between host generations * @power_up: pcie power_up callback @@ -160,6 +168,8 @@ struct mtk_msi_set { * @phy: PHY controller block * @clks: PCIe clocks * @num_clks: PCIe clocks count for this port + * @max_link_speed: Maximum link speed (PCIe Gen) for this port + * @num_lanes: Number of PCIe lanes for this port * @irq: PCIe controller interrupt number * @saved_irq_state: IRQ enable state saved at suspend time * @irq_lock: lock protecting IRQ register access @@ -180,6 +190,8 @@ struct mtk_gen3_pcie { struct phy *phy; struct clk_bulk_data *clks; int num_clks; + u8 max_link_speed; + u8 num_lanes; int irq; u32 saved_irq_state; @@ -381,11 +393,35 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) int err; u32 val; - /* Set as RC mode */ + /* Set as RC mode and set controller PCIe Gen speed restriction, if any */ val = readl_relaxed(pcie->base + PCIE_SETTING_REG); val |= PCIE_RC_MODE; + if (pcie->max_link_speed) { + val &= ~PCIE_SETTING_GEN_SUPPORT; + + /* Can enable link speed support only from Gen2 onwards */ + if (pcie->max_link_speed >= 2) + val |= FIELD_PREP(PCIE_SETTING_GEN_SUPPORT, + GENMASK(pcie->max_link_speed - 2, 0)); + } + if (pcie->num_lanes) { + val &= ~PCIE_SETTING_LINK_WIDTH; + + /* Zero means one lane, each bit activates x2/x4/x8/x16 */ + if (pcie->num_lanes > 1) + val |= FIELD_PREP(PCIE_SETTING_LINK_WIDTH, + GENMASK(fls(pcie->num_lanes >> 2), 0)); + } writel_relaxed(val, pcie->base + PCIE_SETTING_REG); + /* Set Link Control 2 (LNKCTL2) speed restriction, if any */ + if (pcie->max_link_speed) { + val = readl_relaxed(pcie->base + PCIE_CONF_LINK2_CTL_STS); + val &= ~PCIE_CONF_LINK2_LCR2_LINK_SPEED; + val |= FIELD_PREP(PCIE_CONF_LINK2_LCR2_LINK_SPEED, pcie->max_link_speed); + writel_relaxed(val, pcie->base + PCIE_CONF_LINK2_CTL_STS); + } + /* Set class code */ val = readl_relaxed(pcie->base + PCIE_PCI_IDS_1); val &= ~GENMASK(31, 8); @@ -813,6 +849,7 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *regs; + u32 num_lanes; regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcie-mac"); if (!regs) @@ -858,6 +895,14 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) return pcie->num_clks; } + ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes); + if (ret == 0) { + if (num_lanes == 0 || num_lanes > 16 || (num_lanes != 1 && num_lanes % 2)) + dev_warn(dev, "invalid num-lanes, using controller defaults\n"); + else + pcie->num_lanes = num_lanes; + } + return 0; } @@ -1004,9 +1049,21 @@ static void mtk_pcie_power_down(struct mtk_gen3_pcie *pcie) reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); } +static int mtk_pcie_get_controller_max_link_speed(struct mtk_gen3_pcie *pcie) +{ + u32 val; + int ret; + + val = readl_relaxed(pcie->base + PCIE_BASE_CFG_REG); + val = FIELD_GET(PCIE_BASE_CFG_SPEED, val); + ret = fls(val); + + return ret > 0 ? ret : -EINVAL; +} + static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) { - int err; + int err, max_speed; err = mtk_pcie_parse_port(pcie); if (err) @@ -1031,6 +1088,20 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) if (err) return err; + err = of_pci_get_max_link_speed(pcie->dev->of_node); + if (err) { + /* Get the maximum speed supported by the controller */ + max_speed = mtk_pcie_get_controller_max_link_speed(pcie); + + /* Set max_link_speed only if the controller supports it */ + if (max_speed >= 0 && max_speed <= err) { + pcie->max_link_speed = err; + dev_info(pcie->dev, + "maximum controller link speed Gen%d, overriding to Gen%u", + max_speed, pcie->max_link_speed); + } + } + /* Try link up */ err = mtk_pcie_startup_port(pcie); if (err) @@ -1225,7 +1296,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_of_match); static struct platform_driver mtk_pcie_driver = { .probe = mtk_pcie_probe, - .remove_new = mtk_pcie_remove, + .remove = mtk_pcie_remove, .driver = { .name = "mtk-pcie-gen3", .of_match_table = mtk_pcie_of_match, diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 7f7d04c2ea573..3bcfc4e58ba24 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1235,7 +1235,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_ids); static struct platform_driver mtk_pcie_driver = { .probe = mtk_pcie_probe, - .remove_new = mtk_pcie_remove, + .remove = mtk_pcie_remove, .driver = { .name = "mtk-pcie", .of_match_table = mtk_pcie_ids, diff --git a/drivers/pci/controller/pcie-mt7621.c b/drivers/pci/controller/pcie-mt7621.c index 9b4754a45515f..776caa0b10115 100644 --- a/drivers/pci/controller/pcie-mt7621.c +++ b/drivers/pci/controller/pcie-mt7621.c @@ -541,7 +541,7 @@ MODULE_DEVICE_TABLE(of, mt7621_pcie_ids); static struct platform_driver mt7621_pcie_driver = { .probe = mt7621_pcie_probe, - .remove_new = mt7621_pcie_remove, + .remove = mt7621_pcie_remove, .driver = { .name = "mt7621-pci", .of_match_table = mt7621_pcie_ids, diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 3dd653f3d7841..7c92eada04af4 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -796,8 +796,8 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) rcar_pci_write_reg(pcie, 0, PCIEMSIIER); /* - * Setup MSI data target using RC base address address, which - * is guaranteed to be in the low 32bit range on any R-Car HW. + * Setup MSI data target using RC base address, which is guaranteed + * to be in the low 32bit range on any R-Car HW. */ rcar_pci_write_reg(pcie, lower_32_bits(res.start) | MSIFE, PCIEMSIALR); rcar_pci_write_reg(pcie, upper_32_bits(res.start), PCIEMSIAUR); diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 1362745336568..1064b7b06cef6 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -10,12 +10,16 @@ #include #include +#include +#include #include +#include #include #include #include #include #include +#include #include "pcie-rockchip.h" @@ -48,6 +52,10 @@ struct rockchip_pcie_ep { u64 irq_pci_addr; u8 irq_pci_fn; u8 irq_pending; + int perst_irq; + bool perst_asserted; + bool link_up; + struct delayed_work link_training; }; static void rockchip_pcie_clear_ep_ob_atu(struct rockchip_pcie *rockchip, @@ -63,15 +71,25 @@ static void rockchip_pcie_clear_ep_ob_atu(struct rockchip_pcie *rockchip, ROCKCHIP_PCIE_AT_OB_REGION_DESC1(region)); } +static int rockchip_pcie_ep_ob_atu_num_bits(struct rockchip_pcie *rockchip, + u64 pci_addr, size_t size) +{ + int num_pass_bits = fls64(pci_addr ^ (pci_addr + size - 1)); + + return clamp(num_pass_bits, + ROCKCHIP_PCIE_AT_MIN_NUM_BITS, + ROCKCHIP_PCIE_AT_MAX_NUM_BITS); +} + static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, u32 r, u64 cpu_addr, u64 pci_addr, size_t size) { - int num_pass_bits = fls64(size - 1); + int num_pass_bits; u32 addr0, addr1, desc0; - if (num_pass_bits < 8) - num_pass_bits = 8; + num_pass_bits = rockchip_pcie_ep_ob_atu_num_bits(rockchip, + pci_addr, size); addr0 = ((num_pass_bits - 1) & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | (lower_32_bits(pci_addr) & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); @@ -228,6 +246,28 @@ static inline u32 rockchip_ob_region(phys_addr_t addr) return (addr >> ilog2(SZ_1M)) & 0x1f; } +static u64 rockchip_pcie_ep_align_addr(struct pci_epc *epc, u64 pci_addr, + size_t *pci_size, size_t *addr_offset) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + size_t size = *pci_size; + u64 offset, mask; + int num_bits; + + num_bits = rockchip_pcie_ep_ob_atu_num_bits(&ep->rockchip, + pci_addr, size); + mask = (1ULL << num_bits) - 1; + + offset = pci_addr & mask; + if (size + offset > SZ_1M) + size = SZ_1M - offset; + + *pci_size = ALIGN(offset + size, ROCKCHIP_PCIE_AT_SIZE_ALIGN); + *addr_offset = offset; + + return pci_addr & ~mask; +} + static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr, u64 pci_addr, size_t size) @@ -236,6 +276,9 @@ static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, struct rockchip_pcie *pcie = &ep->rockchip; u32 r = rockchip_ob_region(addr); + if (test_bit(r, &ep->ob_region_map)) + return -EBUSY; + rockchip_pcie_prog_ep_ob_atu(pcie, fn, r, addr, pci_addr, size); set_bit(r, &ep->ob_region_map); @@ -249,13 +292,9 @@ static void rockchip_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie *rockchip = &ep->rockchip; - u32 r; - - for (r = 0; r < ep->max_regions; r++) - if (ep->ob_addr[r] == addr) - break; + u32 r = rockchip_ob_region(addr); - if (r == ep->max_regions) + if (addr != ep->ob_addr[r] || !test_bit(r, &ep->ob_region_map)) return; rockchip_pcie_clear_ep_ob_atu(rockchip, r); @@ -351,9 +390,10 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, { struct rockchip_pcie *rockchip = &ep->rockchip; u32 flags, mme, data, data_mask; + size_t irq_pci_size, offset; + u64 irq_pci_addr; u8 msi_count; u64 pci_addr; - u32 r; /* Check MSI enable bit */ flags = rockchip_pcie_read(&ep->rockchip, @@ -389,18 +429,21 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, PCI_MSI_ADDRESS_LO); /* Set the outbound region if needed. */ - if (unlikely(ep->irq_pci_addr != (pci_addr & PCIE_ADDR_MASK) || + irq_pci_size = ~PCIE_ADDR_MASK + 1; + irq_pci_addr = rockchip_pcie_ep_align_addr(ep->epc, + pci_addr & PCIE_ADDR_MASK, + &irq_pci_size, &offset); + if (unlikely(ep->irq_pci_addr != irq_pci_addr || ep->irq_pci_fn != fn)) { - r = rockchip_ob_region(ep->irq_phys_addr); - rockchip_pcie_prog_ep_ob_atu(rockchip, fn, r, - ep->irq_phys_addr, - pci_addr & PCIE_ADDR_MASK, - ~PCIE_ADDR_MASK + 1); - ep->irq_pci_addr = (pci_addr & PCIE_ADDR_MASK); + rockchip_pcie_prog_ep_ob_atu(rockchip, fn, + rockchip_ob_region(ep->irq_phys_addr), + ep->irq_phys_addr, + irq_pci_addr, irq_pci_size); + ep->irq_pci_addr = irq_pci_addr; ep->irq_pci_fn = fn; } - writew(data, ep->irq_cpu_addr + (pci_addr & ~PCIE_ADDR_MASK)); + writew(data, ep->irq_cpu_addr + offset + (pci_addr & ~PCIE_ADDR_MASK)); return 0; } @@ -432,14 +475,222 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc) rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG); + if (rockchip->perst_gpio) + enable_irq(ep->perst_irq); + + /* Enable configuration and start link training */ + rockchip_pcie_write(rockchip, + PCIE_CLIENT_LINK_TRAIN_ENABLE | + PCIE_CLIENT_CONF_ENABLE, + PCIE_CLIENT_CONFIG); + + if (!rockchip->perst_gpio) + schedule_delayed_work(&ep->link_training, 0); + + return 0; +} + +static void rockchip_pcie_ep_stop(struct pci_epc *epc) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + + if (rockchip->perst_gpio) { + ep->perst_asserted = true; + disable_irq(ep->perst_irq); + } + + cancel_delayed_work_sync(&ep->link_training); + + /* Stop link training and disable configuration */ + rockchip_pcie_write(rockchip, + PCIE_CLIENT_CONF_DISABLE | + PCIE_CLIENT_LINK_TRAIN_DISABLE, + PCIE_CLIENT_CONFIG); +} + +static void rockchip_pcie_ep_retrain_link(struct rockchip_pcie *rockchip) +{ + u32 status; + + status = rockchip_pcie_read(rockchip, PCIE_EP_CONFIG_LCS); + status |= PCI_EXP_LNKCTL_RL; + rockchip_pcie_write(rockchip, status, PCIE_EP_CONFIG_LCS); +} + +static bool rockchip_pcie_ep_link_up(struct rockchip_pcie *rockchip) +{ + u32 val = rockchip_pcie_read(rockchip, PCIE_CLIENT_BASIC_STATUS1); + + return PCIE_LINK_UP(val); +} + +static void rockchip_pcie_ep_link_training(struct work_struct *work) +{ + struct rockchip_pcie_ep *ep = + container_of(work, struct rockchip_pcie_ep, link_training.work); + struct rockchip_pcie *rockchip = &ep->rockchip; + struct device *dev = rockchip->dev; + u32 val; + int ret; + + /* Enable Gen1 training and wait for its completion */ + ret = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL, + val, PCIE_LINK_TRAINING_DONE(val), 50, + LINK_TRAIN_TIMEOUT); + if (ret) + goto again; + + /* Make sure that the link is up */ + ret = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1, + val, PCIE_LINK_UP(val), 50, + LINK_TRAIN_TIMEOUT); + if (ret) + goto again; + + /* + * Check the current speed: if gen2 speed was requested and we are not + * at gen2 speed yet, retrain again for gen2. + */ + val = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL); + if (!PCIE_LINK_IS_GEN2(val) && rockchip->link_gen == 2) { + /* Enable retrain for gen2 */ + rockchip_pcie_ep_retrain_link(rockchip); + readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL, + val, PCIE_LINK_IS_GEN2(val), 50, + LINK_TRAIN_TIMEOUT); + } + + /* Check again that the link is up */ + if (!rockchip_pcie_ep_link_up(rockchip)) + goto again; + + /* + * If PERST# was asserted while polling the link, do not notify + * the function. + */ + if (ep->perst_asserted) + return; + + val = rockchip_pcie_read(rockchip, PCIE_CLIENT_BASIC_STATUS0); + dev_info(dev, + "link up (negotiated speed: %sGT/s, width: x%lu)\n", + (val & PCIE_CLIENT_NEG_LINK_SPEED) ? "5" : "2.5", + ((val & PCIE_CLIENT_NEG_LINK_WIDTH_MASK) >> + PCIE_CLIENT_NEG_LINK_WIDTH_SHIFT) << 1); + + /* Notify the function */ + pci_epc_linkup(ep->epc); + ep->link_up = true; + + return; + +again: + schedule_delayed_work(&ep->link_training, msecs_to_jiffies(5)); +} + +static void rockchip_pcie_ep_perst_assert(struct rockchip_pcie_ep *ep) +{ + struct rockchip_pcie *rockchip = &ep->rockchip; + + dev_dbg(rockchip->dev, "PERST# asserted, link down\n"); + + if (ep->perst_asserted) + return; + + ep->perst_asserted = true; + + cancel_delayed_work_sync(&ep->link_training); + + if (ep->link_up) { + pci_epc_linkdown(ep->epc); + ep->link_up = false; + } +} + +static void rockchip_pcie_ep_perst_deassert(struct rockchip_pcie_ep *ep) +{ + struct rockchip_pcie *rockchip = &ep->rockchip; + + dev_dbg(rockchip->dev, "PERST# de-asserted, starting link training\n"); + + if (!ep->perst_asserted) + return; + + ep->perst_asserted = false; + + /* Enable link re-training */ + rockchip_pcie_ep_retrain_link(rockchip); + + /* Start link training */ + schedule_delayed_work(&ep->link_training, 0); +} + +static irqreturn_t rockchip_pcie_ep_perst_irq_thread(int irq, void *data) +{ + struct pci_epc *epc = data; + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + u32 perst = gpiod_get_value(rockchip->perst_gpio); + + if (perst) + rockchip_pcie_ep_perst_assert(ep); + else + rockchip_pcie_ep_perst_deassert(ep); + + irq_set_irq_type(ep->perst_irq, + (perst ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW)); + + return IRQ_HANDLED; +} + +static int rockchip_pcie_ep_setup_irq(struct pci_epc *epc) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + struct device *dev = rockchip->dev; + int ret; + + if (!rockchip->perst_gpio) + return 0; + + /* PCIe reset interrupt */ + ep->perst_irq = gpiod_to_irq(rockchip->perst_gpio); + if (ep->perst_irq < 0) { + dev_err(dev, + "failed to get IRQ for PERST# GPIO: %d\n", + ep->perst_irq); + + return ep->perst_irq; + } + + /* + * The perst_gpio is active low, so when it is inactive on start, it + * is high and will trigger the perst_irq handler. So treat this initial + * IRQ as a dummy one by faking the host asserting PERST#. + */ + ep->perst_asserted = true; + irq_set_status_flags(ep->perst_irq, IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(dev, ep->perst_irq, NULL, + rockchip_pcie_ep_perst_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "pcie-ep-perst", epc); + if (ret) { + dev_err(dev, + "failed to request IRQ for PERST# GPIO: %d\n", + ret); + + return ret; + } + return 0; } static const struct pci_epc_features rockchip_pcie_epc_features = { - .linkup_notifier = false, + .linkup_notifier = true, .msi_capable = true, .msix_capable = false, - .align = 256, + .align = ROCKCHIP_PCIE_AT_SIZE_ALIGN, }; static const struct pci_epc_features* @@ -452,17 +703,19 @@ static const struct pci_epc_ops rockchip_pcie_epc_ops = { .write_header = rockchip_pcie_ep_write_header, .set_bar = rockchip_pcie_ep_set_bar, .clear_bar = rockchip_pcie_ep_clear_bar, + .align_addr = rockchip_pcie_ep_align_addr, .map_addr = rockchip_pcie_ep_map_addr, .unmap_addr = rockchip_pcie_ep_unmap_addr, .set_msi = rockchip_pcie_ep_set_msi, .get_msi = rockchip_pcie_ep_get_msi, .raise_irq = rockchip_pcie_ep_raise_irq, .start = rockchip_pcie_ep_start, + .stop = rockchip_pcie_ep_stop, .get_features = rockchip_pcie_ep_get_features, }; -static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, - struct rockchip_pcie_ep *ep) +static int rockchip_pcie_ep_get_resources(struct rockchip_pcie *rockchip, + struct rockchip_pcie_ep *ep) { struct device *dev = rockchip->dev; int err; @@ -496,91 +749,63 @@ static const struct of_device_id rockchip_pcie_ep_of_match[] = { {}, }; -static int rockchip_pcie_ep_probe(struct platform_device *pdev) +static int rockchip_pcie_ep_init_ob_mem(struct rockchip_pcie_ep *ep) { - struct device *dev = &pdev->dev; - struct rockchip_pcie_ep *ep; - struct rockchip_pcie *rockchip; - struct pci_epc *epc; - size_t max_regions; + struct rockchip_pcie *rockchip = &ep->rockchip; + struct device *dev = rockchip->dev; struct pci_epc_mem_window *windows = NULL; int err, i; - u32 cfg_msi, cfg_msix_cp; - - ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - rockchip = &ep->rockchip; - rockchip->is_rc = false; - rockchip->dev = dev; - - epc = devm_pci_epc_create(dev, &rockchip_pcie_epc_ops); - if (IS_ERR(epc)) { - dev_err(dev, "failed to create epc device\n"); - return PTR_ERR(epc); - } - - ep->epc = epc; - epc_set_drvdata(epc, ep); - - err = rockchip_pcie_parse_ep_dt(rockchip, ep); - if (err) - return err; - - err = rockchip_pcie_enable_clocks(rockchip); - if (err) - return err; - - err = rockchip_pcie_init_port(rockchip); - if (err) - goto err_disable_clocks; - - /* Establish the link automatically */ - rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE, - PCIE_CLIENT_CONFIG); - - max_regions = ep->max_regions; - ep->ob_addr = devm_kcalloc(dev, max_regions, sizeof(*ep->ob_addr), + ep->ob_addr = devm_kcalloc(dev, ep->max_regions, sizeof(*ep->ob_addr), GFP_KERNEL); - if (!ep->ob_addr) { - err = -ENOMEM; - goto err_uninit_port; - } - - /* Only enable function 0 by default */ - rockchip_pcie_write(rockchip, BIT(0), PCIE_CORE_PHY_FUNC_CFG); + if (!ep->ob_addr) + return -ENOMEM; windows = devm_kcalloc(dev, ep->max_regions, sizeof(struct pci_epc_mem_window), GFP_KERNEL); - if (!windows) { - err = -ENOMEM; - goto err_uninit_port; - } + if (!windows) + return -ENOMEM; + for (i = 0; i < ep->max_regions; i++) { windows[i].phys_base = rockchip->mem_res->start + (SZ_1M * i); windows[i].size = SZ_1M; windows[i].page_size = SZ_1M; } - err = pci_epc_multi_mem_init(epc, windows, ep->max_regions); + err = pci_epc_multi_mem_init(ep->epc, windows, ep->max_regions); devm_kfree(dev, windows); if (err < 0) { dev_err(dev, "failed to initialize the memory space\n"); - goto err_uninit_port; + return err; } - ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, + ep->irq_cpu_addr = pci_epc_mem_alloc_addr(ep->epc, &ep->irq_phys_addr, SZ_1M); if (!ep->irq_cpu_addr) { dev_err(dev, "failed to reserve memory space for MSI\n"); - err = -ENOMEM; goto err_epc_mem_exit; } ep->irq_pci_addr = ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR; + return 0; + +err_epc_mem_exit: + pci_epc_mem_exit(ep->epc); + + return err; +} + +static void rockchip_pcie_ep_exit_ob_mem(struct rockchip_pcie_ep *ep) +{ + pci_epc_mem_exit(ep->epc); +} + +static void rockchip_pcie_ep_hide_broken_msix_cap(struct rockchip_pcie *rockchip) +{ + u32 cfg_msi, cfg_msix_cp; + /* * MSI-X is not supported but the controller still advertises the MSI-X * capability by default, which can lead to the Root Complex side @@ -603,19 +828,68 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) rockchip_pcie_write(rockchip, cfg_msi, PCIE_EP_CONFIG_BASE + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); +} - rockchip_pcie_write(rockchip, PCIE_CLIENT_CONF_ENABLE, - PCIE_CLIENT_CONFIG); +static int rockchip_pcie_ep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_pcie_ep *ep; + struct rockchip_pcie *rockchip; + struct pci_epc *epc; + int err; + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + rockchip = &ep->rockchip; + rockchip->is_rc = false; + rockchip->dev = dev; + INIT_DELAYED_WORK(&ep->link_training, rockchip_pcie_ep_link_training); + + epc = devm_pci_epc_create(dev, &rockchip_pcie_epc_ops); + if (IS_ERR(epc)) { + dev_err(dev, "failed to create EPC device\n"); + return PTR_ERR(epc); + } + + ep->epc = epc; + epc_set_drvdata(epc, ep); + + err = rockchip_pcie_ep_get_resources(rockchip, ep); + if (err) + return err; + + err = rockchip_pcie_ep_init_ob_mem(ep); + if (err) + return err; + + err = rockchip_pcie_enable_clocks(rockchip); + if (err) + goto err_exit_ob_mem; + + err = rockchip_pcie_init_port(rockchip); + if (err) + goto err_disable_clocks; + + rockchip_pcie_ep_hide_broken_msix_cap(rockchip); + + /* Only enable function 0 by default */ + rockchip_pcie_write(rockchip, BIT(0), PCIE_CORE_PHY_FUNC_CFG); pci_epc_init_notify(epc); + err = rockchip_pcie_ep_setup_irq(epc); + if (err < 0) + goto err_uninit_port; + return 0; -err_epc_mem_exit: - pci_epc_mem_exit(epc); err_uninit_port: rockchip_pcie_deinit_phys(rockchip); err_disable_clocks: rockchip_pcie_disable_clocks(rockchip); +err_exit_ob_mem: + rockchip_pcie_ep_exit_ob_mem(ep); return err; } diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index cbec711148253..5adac6adc046f 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -294,7 +294,7 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) int err, i = MAX_LANE_NUM; u32 status; - gpiod_set_value_cansleep(rockchip->ep_gpio, 0); + gpiod_set_value_cansleep(rockchip->perst_gpio, 0); err = rockchip_pcie_init_port(rockchip); if (err) @@ -323,7 +323,7 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) PCIE_CLIENT_CONFIG); msleep(PCIE_T_PVPERL_MS); - gpiod_set_value_cansleep(rockchip->ep_gpio, 1); + gpiod_set_value_cansleep(rockchip->perst_gpio, 1); msleep(PCIE_T_RRS_READY_MS); @@ -1050,7 +1050,7 @@ static struct platform_driver rockchip_pcie_driver = { .pm = &rockchip_pcie_pm_ops, }, .probe = rockchip_pcie_probe, - .remove_new = rockchip_pcie_remove, + .remove = rockchip_pcie_remove, }; module_platform_driver(rockchip_pcie_driver); diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c index c07d7129f1c7c..b9ade7632e113 100644 --- a/drivers/pci/controller/pcie-rockchip.c +++ b/drivers/pci/controller/pcie-rockchip.c @@ -119,13 +119,15 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) return PTR_ERR(rockchip->aclk_rst); } - if (rockchip->is_rc) { - rockchip->ep_gpio = devm_gpiod_get_optional(dev, "ep", - GPIOD_OUT_LOW); - if (IS_ERR(rockchip->ep_gpio)) - return dev_err_probe(dev, PTR_ERR(rockchip->ep_gpio), - "failed to get ep GPIO\n"); - } + if (rockchip->is_rc) + rockchip->perst_gpio = devm_gpiod_get_optional(dev, "ep", + GPIOD_OUT_LOW); + else + rockchip->perst_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_IN); + if (IS_ERR(rockchip->perst_gpio)) + return dev_err_probe(dev, PTR_ERR(rockchip->perst_gpio), + "failed to get PERST# GPIO\n"); rockchip->aclk_pcie = devm_clk_get(dev, "aclk"); if (IS_ERR(rockchip->aclk_pcie)) { @@ -244,11 +246,12 @@ int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1, PCIE_CLIENT_CONFIG); - regs = PCIE_CLIENT_LINK_TRAIN_ENABLE | PCIE_CLIENT_ARI_ENABLE | + regs = PCIE_CLIENT_ARI_ENABLE | PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes); if (rockchip->is_rc) - regs |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC; + regs |= PCIE_CLIENT_LINK_TRAIN_ENABLE | + PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC; else regs |= PCIE_CLIENT_CONF_DISABLE | PCIE_CLIENT_MODE_EP; diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 6111de35f84ca..a51b087ce8786 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -26,12 +26,14 @@ #define MAX_LANE_NUM 4 #define MAX_REGION_LIMIT 32 #define MIN_EP_APERTURE 28 +#define LINK_TRAIN_TIMEOUT (500 * USEC_PER_MSEC) #define PCIE_CLIENT_BASE 0x0 #define PCIE_CLIENT_CONFIG (PCIE_CLIENT_BASE + 0x00) #define PCIE_CLIENT_CONF_ENABLE HIWORD_UPDATE_BIT(0x0001) #define PCIE_CLIENT_CONF_DISABLE HIWORD_UPDATE(0x0001, 0) #define PCIE_CLIENT_LINK_TRAIN_ENABLE HIWORD_UPDATE_BIT(0x0002) +#define PCIE_CLIENT_LINK_TRAIN_DISABLE HIWORD_UPDATE(0x0002, 0) #define PCIE_CLIENT_ARI_ENABLE HIWORD_UPDATE_BIT(0x0008) #define PCIE_CLIENT_CONF_LANE_NUM(x) HIWORD_UPDATE(0x0030, ENCODE_LANES(x)) #define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040) @@ -49,6 +51,10 @@ #define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0) #define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18 #define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19 +#define PCIE_CLIENT_BASIC_STATUS0 (PCIE_CLIENT_BASE + 0x44) +#define PCIE_CLIENT_NEG_LINK_WIDTH_MASK GENMASK(7, 6) +#define PCIE_CLIENT_NEG_LINK_WIDTH_SHIFT 6 +#define PCIE_CLIENT_NEG_LINK_SPEED BIT(5) #define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48) #define PCIE_CLIENT_LINK_STATUS_UP 0x00300000 #define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000 @@ -86,6 +92,8 @@ #define PCIE_CORE_CTRL_MGMT_BASE 0x900000 #define PCIE_CORE_CTRL (PCIE_CORE_CTRL_MGMT_BASE + 0x000) +#define PCIE_CORE_PL_CONF_LS_MASK 0x00000001 +#define PCIE_CORE_PL_CONF_LS_READY 0x00000001 #define PCIE_CORE_PL_CONF_SPEED_5G 0x00000008 #define PCIE_CORE_PL_CONF_SPEED_MASK 0x00000018 #define PCIE_CORE_PL_CONF_LANE_MASK 0x00000006 @@ -143,6 +151,7 @@ #define PCIE_RC_CONFIG_BASE 0xa00000 #define PCIE_EP_CONFIG_BASE 0xa00000 #define PCIE_EP_CONFIG_DID_VID (PCIE_EP_CONFIG_BASE + 0x00) +#define PCIE_EP_CONFIG_LCS (PCIE_EP_CONFIG_BASE + 0xd0) #define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08) #define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4) #define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18 @@ -154,6 +163,7 @@ #define PCIE_RC_CONFIG_LINK_CAP (PCIE_RC_CONFIG_BASE + 0xcc) #define PCIE_RC_CONFIG_LINK_CAP_L0S BIT(10) #define PCIE_RC_CONFIG_LCS (PCIE_RC_CONFIG_BASE + 0xd0) +#define PCIE_EP_CONFIG_LCS (PCIE_EP_CONFIG_BASE + 0xd0) #define PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 (PCIE_RC_CONFIG_BASE + 0x90c) #define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274) #define PCIE_RC_CONFIG_THP_CAP_NEXT_MASK GENMASK(31, 20) @@ -191,6 +201,8 @@ #define ROCKCHIP_VENDOR_ID 0x1d87 #define PCIE_LINK_IS_L2(x) \ (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2) +#define PCIE_LINK_TRAINING_DONE(x) \ + (((x) & PCIE_CORE_PL_CONF_LS_MASK) == PCIE_CORE_PL_CONF_LS_READY) #define PCIE_LINK_UP(x) \ (((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP) #define PCIE_LINK_IS_GEN2(x) \ @@ -241,10 +253,20 @@ #define ROCKCHIP_PCIE_EP_MSIX_CAP_CP_MASK GENMASK(15, 8) #define ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR 0x1 #define ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR 0x3 + +#define ROCKCHIP_PCIE_AT_MIN_NUM_BITS 8 +#define ROCKCHIP_PCIE_AT_MAX_NUM_BITS 20 +#define ROCKCHIP_PCIE_AT_SIZE_ALIGN (1UL << ROCKCHIP_PCIE_AT_MIN_NUM_BITS) + #define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) \ (PCIE_EP_PF_CONFIG_REGS_BASE + (((fn) << 12) & GENMASK(19, 12))) #define ROCKCHIP_PCIE_EP_VIRT_FUNC_BASE(fn) \ (PCIE_EP_PF_CONFIG_REGS_BASE + 0x10000 + (((fn) << 12) & GENMASK(19, 12))) + +#define ROCKCHIP_PCIE_AT_MIN_NUM_BITS 8 +#define ROCKCHIP_PCIE_AT_MAX_NUM_BITS 20 +#define ROCKCHIP_PCIE_AT_SIZE_ALIGN (1UL << ROCKCHIP_PCIE_AT_MIN_NUM_BITS) + #define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ (PCIE_CORE_AXI_CONF_BASE + 0x0828 + (fn) * 0x0040 + (bar) * 0x0008) #define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ @@ -307,7 +329,7 @@ struct rockchip_pcie { struct regulator *vpcie3v3; /* 3.3V power supply */ struct regulator *vpcie1v8; /* 1.8V power supply */ struct regulator *vpcie0v9; /* 0.9V power supply */ - struct gpio_desc *ep_gpio; + struct gpio_desc *perst_gpio; u32 lanes; u8 lanes_map; int link_gen; diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index a8ae14474dd0a..8d6e2a89b067b 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -916,6 +916,6 @@ static struct platform_driver nwl_pcie_driver = { .of_match_table = nwl_pcie_of_match, }, .probe = nwl_pcie_probe, - .remove_new = nwl_pcie_remove, + .remove = nwl_pcie_remove, }; builtin_platform_driver(nwl_pcie_driver); diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c index 48f60a04b740b..6630cacef3010 100644 --- a/drivers/pci/controller/plda/pcie-microchip-host.c +++ b/drivers/pci/controller/plda/pcie-microchip-host.c @@ -25,9 +25,6 @@ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u #define MC_PCIE1_CTRL_ADDR 0x0000a000u -#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) -#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) - /* PCIe Controller Phy Regs */ #define SEC_ERROR_EVENT_CNT 0x20 #define DED_ERROR_EVENT_CNT 0x24 @@ -128,7 +125,6 @@ [EVENT_LOCAL_ ## x] = { __stringify(x), s } #define PCIE_EVENT(x) \ - .base = MC_PCIE_CTRL_ADDR, \ .offset = PCIE_EVENT_INT, \ .mask_offset = PCIE_EVENT_INT, \ .mask_high = 1, \ @@ -136,7 +132,6 @@ .enb_mask = PCIE_EVENT_INT_ENB_MASK #define SEC_EVENT(x) \ - .base = MC_PCIE_CTRL_ADDR, \ .offset = SEC_ERROR_INT, \ .mask_offset = SEC_ERROR_INT_MASK, \ .mask = SEC_ERROR_INT_ ## x ## _INT, \ @@ -144,7 +139,6 @@ .enb_mask = 0 #define DED_EVENT(x) \ - .base = MC_PCIE_CTRL_ADDR, \ .offset = DED_ERROR_INT, \ .mask_offset = DED_ERROR_INT_MASK, \ .mask_high = 1, \ @@ -152,7 +146,6 @@ .enb_mask = 0 #define LOCAL_EVENT(x) \ - .base = MC_PCIE_BRIDGE_ADDR, \ .offset = ISTATUS_LOCAL, \ .mask_offset = IMASK_LOCAL, \ .mask_high = 0, \ @@ -179,7 +172,8 @@ struct event_map { struct mc_pcie { struct plda_pcie_rp plda; - void __iomem *axi_base_addr; + void __iomem *bridge_base_addr; + void __iomem *ctrl_base_addr; }; struct cause { @@ -253,7 +247,6 @@ static struct event_map local_status_to_event[] = { }; static struct { - u32 base; u32 offset; u32 mask; u32 shift; @@ -325,8 +318,7 @@ static inline u32 reg_to_event(u32 reg, struct event_map field) static u32 pcie_events(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); + u32 reg = readl_relaxed(port->ctrl_base_addr + PCIE_EVENT_INT); u32 val = 0; int i; @@ -338,8 +330,7 @@ static u32 pcie_events(struct mc_pcie *port) static u32 sec_errors(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); + u32 reg = readl_relaxed(port->ctrl_base_addr + SEC_ERROR_INT); u32 val = 0; int i; @@ -351,8 +342,7 @@ static u32 sec_errors(struct mc_pcie *port) static u32 ded_errors(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); + u32 reg = readl_relaxed(port->ctrl_base_addr + DED_ERROR_INT); u32 val = 0; int i; @@ -364,8 +354,7 @@ static u32 ded_errors(struct mc_pcie *port) static u32 local_events(struct mc_pcie *port) { - void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); + u32 reg = readl_relaxed(port->bridge_base_addr + ISTATUS_LOCAL); u32 val = 0; int i; @@ -412,8 +401,12 @@ static void mc_ack_event_irq(struct irq_data *data) void __iomem *addr; u32 mask; - addr = mc_port->axi_base_addr + event_descs[event].base + - event_descs[event].offset; + if (event_descs[event].offset == ISTATUS_LOCAL) + addr = mc_port->bridge_base_addr; + else + addr = mc_port->ctrl_base_addr; + + addr += event_descs[event].offset; mask = event_descs[event].mask; mask |= event_descs[event].enb_mask; @@ -429,8 +422,12 @@ static void mc_mask_event_irq(struct irq_data *data) u32 mask; u32 val; - addr = mc_port->axi_base_addr + event_descs[event].base + - event_descs[event].mask_offset; + if (event_descs[event].offset == ISTATUS_LOCAL) + addr = mc_port->bridge_base_addr; + else + addr = mc_port->ctrl_base_addr; + + addr += event_descs[event].mask_offset; mask = event_descs[event].mask; if (event_descs[event].enb_mask) { mask <<= PCIE_EVENT_INT_ENB_SHIFT; @@ -460,8 +457,12 @@ static void mc_unmask_event_irq(struct irq_data *data) u32 mask; u32 val; - addr = mc_port->axi_base_addr + event_descs[event].base + - event_descs[event].mask_offset; + if (event_descs[event].offset == ISTATUS_LOCAL) + addr = mc_port->bridge_base_addr; + else + addr = mc_port->ctrl_base_addr; + + addr += event_descs[event].mask_offset; mask = event_descs[event].mask; if (event_descs[event].enb_mask) @@ -554,26 +555,20 @@ static const struct plda_event mc_event = { static inline void mc_clear_secs(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - - writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + - SEC_ERROR_INT); - writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); + writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, + port->ctrl_base_addr + SEC_ERROR_INT); + writel_relaxed(0, port->ctrl_base_addr + SEC_ERROR_EVENT_CNT); } static inline void mc_clear_deds(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - - writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + - DED_ERROR_INT); - writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); + writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, + port->ctrl_base_addr + DED_ERROR_INT); + writel_relaxed(0, port->ctrl_base_addr + DED_ERROR_EVENT_CNT); } static void mc_disable_interrupts(struct mc_pcie *port) { - void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; u32 val; /* Ensure ECC bypass is enabled */ @@ -581,22 +576,22 @@ static void mc_disable_interrupts(struct mc_pcie *port) ECC_CONTROL_RX_RAM_ECC_BYPASS | ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; - writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); + writel_relaxed(val, port->ctrl_base_addr + ECC_CONTROL); /* Disable SEC errors and clear any outstanding */ - writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + - SEC_ERROR_INT_MASK); + writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, + port->ctrl_base_addr + SEC_ERROR_INT_MASK); mc_clear_secs(port); /* Disable DED errors and clear any outstanding */ - writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + - DED_ERROR_INT_MASK); + writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, + port->ctrl_base_addr + DED_ERROR_INT_MASK); mc_clear_deds(port); /* Disable local interrupts and clear any outstanding */ - writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); + writel_relaxed(0, port->bridge_base_addr + IMASK_LOCAL); + writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_LOCAL); + writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_MSI); /* Disable PCIe events and clear any outstanding */ val = PCIE_EVENT_INT_L2_EXIT_INT | @@ -605,11 +600,11 @@ static void mc_disable_interrupts(struct mc_pcie *port) PCIE_EVENT_INT_L2_EXIT_INT_MASK | PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; - writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); + writel_relaxed(val, port->ctrl_base_addr + PCIE_EVENT_INT); /* Disable host interrupts and clear any outstanding */ - writel_relaxed(0, bridge_base_addr + IMASK_HOST); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + writel_relaxed(0, port->bridge_base_addr + IMASK_HOST); + writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_HOST); } static int mc_platform_init(struct pci_config_window *cfg) @@ -617,12 +612,10 @@ static int mc_platform_init(struct pci_config_window *cfg) struct device *dev = cfg->parent; struct platform_device *pdev = to_platform_device(dev); struct pci_host_bridge *bridge = platform_get_drvdata(pdev); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; int ret; /* Configure address translation table 0 for PCIe config space */ - plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, + plda_pcie_setup_window(port->bridge_base_addr, 0, cfg->res.start, cfg->res.start, resource_size(&cfg->res)); @@ -649,7 +642,7 @@ static int mc_platform_init(struct pci_config_window *cfg) static int mc_host_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - void __iomem *bridge_base_addr; + void __iomem *apb_base_addr; struct plda_pcie_rp *plda; int ret; u32 val; @@ -661,30 +654,45 @@ static int mc_host_probe(struct platform_device *pdev) plda = &port->plda; plda->dev = dev; - port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(port->axi_base_addr)) - return PTR_ERR(port->axi_base_addr); + port->bridge_base_addr = devm_platform_ioremap_resource_byname(pdev, + "bridge"); + port->ctrl_base_addr = devm_platform_ioremap_resource_byname(pdev, + "ctrl"); + if (!IS_ERR(port->bridge_base_addr) && !IS_ERR(port->ctrl_base_addr)) + goto addrs_set; + + /* + * The original, incorrect, binding that lumped the control and + * bridge addresses together still needs to be handled by the driver. + */ + apb_base_addr = devm_platform_ioremap_resource_byname(pdev, "apb"); + if (IS_ERR(apb_base_addr)) + return dev_err_probe(dev, PTR_ERR(apb_base_addr), + "both legacy apb register and ctrl/bridge regions missing"); + + port->bridge_base_addr = apb_base_addr + MC_PCIE1_BRIDGE_ADDR; + port->ctrl_base_addr = apb_base_addr + MC_PCIE1_CTRL_ADDR; +addrs_set: mc_disable_interrupts(port); - bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - plda->bridge_addr = bridge_base_addr; + plda->bridge_addr = port->bridge_base_addr; plda->num_events = NUM_EVENTS; /* Allow enabling MSI by disabling MSI-X */ - val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); + val = readl(port->bridge_base_addr + PCIE_PCI_IRQ_DW0); val &= ~MSIX_CAP_MASK; - writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); + writel(val, port->bridge_base_addr + PCIE_PCI_IRQ_DW0); /* Pick num vectors from bitfile programmed onto FPGA fabric */ - val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); + val = readl(port->bridge_base_addr + PCIE_PCI_IRQ_DW0); val &= NUM_MSI_MSGS_MASK; val >>= NUM_MSI_MSGS_SHIFT; plda->msi.num_vectors = 1 << val; /* Pick vector address from design */ - plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); + plda->msi.vector_phy = readl_relaxed(port->bridge_base_addr + IMSI_ADDR); ret = mc_pcie_init_clks(dev); if (ret) { diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c index c9933ecf68338..e73c1b7bc8efc 100644 --- a/drivers/pci/controller/plda/pcie-starfive.c +++ b/drivers/pci/controller/plda/pcie-starfive.c @@ -404,6 +404,9 @@ static int starfive_pcie_probe(struct platform_device *pdev) if (ret) return ret; + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + plda->host_ops = &sf_host_ops; plda->num_events = PLDA_MAX_EVENT_NUM; /* mask doorbell event */ @@ -413,11 +416,12 @@ static int starfive_pcie_probe(struct platform_device *pdev) plda->events_bitmap <<= PLDA_NUM_DMA_EVENTS; ret = plda_pcie_host_init(&pcie->plda, &starfive_pcie_ops, &stf_pcie_event); - if (ret) + if (ret) { + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); return ret; + } - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); platform_set_drvdata(pdev, pcie); return 0; @@ -480,7 +484,7 @@ static struct platform_driver starfive_pcie_driver = { .pm = pm_sleep_ptr(&starfive_pcie_pm_ops), }, .probe = starfive_pcie_probe, - .remove_new = starfive_pcie_remove, + .remove = starfive_pcie_remove, }; module_platform_driver(starfive_pcie_driver); diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 264a180403a0e..9d9596947350f 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -740,11 +740,9 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) if (!(features & VMD_FEAT_BIOS_PM_QUIRK)) return 0; - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR); if (!pos) - return 0; + goto out_state_change; /* * Skip if the max snoop LTR is non-zero, indicating BIOS has set it @@ -752,7 +750,7 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) */ pci_read_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, <r_reg); if (!!(ltr_reg & (PCI_LTR_VALUE_MASK | PCI_LTR_SCALE_MASK))) - return 0; + goto out_state_change; /* * Set the default values to the maximum required by the platform to @@ -764,6 +762,13 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) pci_write_config_dword(pdev, pos + PCI_LTR_MAX_SNOOP_LAT, ltr_reg); pci_info(pdev, "VMD: Default LTR value set by driver\n"); +out_state_change: + /* + * Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per + * PCIe r6.0, sec 5.5.4. + */ + pci_set_power_state_locked(pdev, PCI_D0); + pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); return 0; } @@ -1100,6 +1105,10 @@ static const struct pci_device_id vmd_ids[] = { .driver_data = VMD_FEATS_CLIENT,}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, 0xb60b), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, 0xb06f), + .driver_data = VMD_FEATS_CLIENT,}, {0,} }; MODULE_DEVICE_TABLE(pci, vmd_ids); diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index b133967faef84..3b59a86a764b1 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -773,7 +773,7 @@ EXPORT_SYMBOL(pcim_iomap_region); * Unmap a BAR and release its region manually. Only pass BARs that were * previously mapped by pcim_iomap_region(). */ -static void pcim_iounmap_region(struct pci_dev *pdev, int bar) +void pcim_iounmap_region(struct pci_dev *pdev, int bar) { struct pcim_addr_devres res_searched; @@ -784,6 +784,7 @@ static void pcim_iounmap_region(struct pci_dev *pdev, int bar) devres_release(&pdev->dev, pcim_addr_resource_release, pcim_addr_resources_match, &res_searched); } +EXPORT_SYMBOL(pcim_iounmap_region); /** * pcim_iomap_regions - Request and iomap PCI BARs (DEPRECATED) @@ -939,7 +940,7 @@ static void pcim_release_all_regions(struct pci_dev *pdev) * desired, release individual regions with pcim_release_region() or all of * them at once with pcim_release_all_regions(). */ -static int pcim_request_all_regions(struct pci_dev *pdev, const char *name) +int pcim_request_all_regions(struct pci_dev *pdev, const char *name) { int ret; int bar; @@ -957,69 +958,17 @@ static int pcim_request_all_regions(struct pci_dev *pdev, const char *name) return ret; } +EXPORT_SYMBOL(pcim_request_all_regions); /** - * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones - * (DEPRECATED) - * @pdev: PCI device to map IO resources for - * @mask: Mask of BARs to iomap - * @name: Name associated with the requests - * - * Returns: 0 on success, negative error code on failure. - * - * Request all PCI BARs and iomap regions specified by @mask. - * - * To release these resources manually, call pcim_release_region() for the - * regions and pcim_iounmap() for the mappings. - * - * This function is DEPRECATED. Don't use it in new code. Instead, use one - * of the pcim_* region request functions in combination with a pcim_* - * mapping function. - */ -int pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask, - const char *name) -{ - int bar; - int ret; - void __iomem **legacy_iomap_table; - - ret = pcim_request_all_regions(pdev, name); - if (ret != 0) - return ret; - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { - if (!mask_contains_bar(mask, bar)) - continue; - if (!pcim_iomap(pdev, bar, 0)) - goto err; - } - - return 0; - -err: - /* - * If bar is larger than 0, then pcim_iomap() above has most likely - * failed because of -EINVAL. If it is equal 0, most likely the table - * couldn't be created, indicating -ENOMEM. - */ - ret = bar > 0 ? -EINVAL : -ENOMEM; - legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); - - while (--bar >= 0) - pcim_iounmap(pdev, legacy_iomap_table[bar]); - - pcim_release_all_regions(pdev); - - return ret; -} -EXPORT_SYMBOL(pcim_iomap_regions_request_all); - -/** - * pcim_iounmap_regions - Unmap and release PCI BARs + * pcim_iounmap_regions - Unmap and release PCI BARs (DEPRECATED) * @pdev: PCI device to map IO resources for * @mask: Mask of BARs to unmap and release * * Unmap and release regions specified by @mask. + * + * This function is DEPRECATED. Do not use it in new code. + * Use pcim_iounmap_region() instead. */ void pcim_iounmap_regions(struct pci_dev *pdev, int mask) { diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 652d63df9d22e..7bd7892c52228 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -146,6 +146,7 @@ static int pci_doe_send_req(struct pci_doe_mb *doe_mb, { struct pci_dev *pdev = doe_mb->pdev; int offset = doe_mb->cap_offset; + unsigned long timeout_jiffies; size_t length, remainder; u32 val; int i; @@ -155,8 +156,19 @@ static int pci_doe_send_req(struct pci_doe_mb *doe_mb, * someone other than Linux (e.g. firmware) is using the mailbox. Note * it is expected that firmware and OS will negotiate access rights via * an, as yet to be defined, method. + * + * Wait up to one PCI_DOE_TIMEOUT period to allow the prior command to + * finish. Otherwise, simply error out as unable to field the request. + * + * PCIe r6.2 sec 6.30.3 states no interrupt is raised when the DOE Busy + * bit is cleared, so polling here is our best option for the moment. */ - pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + timeout_jiffies = jiffies + PCI_DOE_TIMEOUT; + do { + pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); + } while (FIELD_GET(PCI_DOE_STATUS_BUSY, val) && + !time_after(jiffies, timeout_jiffies)); + if (FIELD_GET(PCI_DOE_STATUS_BUSY, val)) return -EBUSY; diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index 1c40d2506aef3..260b7de2dbd57 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -55,7 +55,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev, bus_range_max = resource_size(cfgres) >> bus_shift; if (bus_range > bus_range_max) { bus_range = bus_range_max; - cfg->busr.end = busr->start + bus_range - 1; + resource_set_size(&cfg->busr, bus_range); dev_warn(dev, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n", cfgres, &cfg->busr, busr); } diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 7d070b1def116..54286a40bdfbf 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -867,12 +867,18 @@ static int pci_epf_mhi_bind(struct pci_epf *epf) { struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; struct platform_device *pdev = to_platform_device(epc->dev.parent); struct resource *res; int ret; /* Get MMIO base address from Endpoint controller */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmio"); + if (!res) { + dev_err(dev, "Failed to get \"mmio\" resource\n"); + return -ENODEV; + } + epf_mhi->mmio_phys = res->start; epf_mhi->mmio_size = resource_size(res); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 7c2ed6eae53ad..ef6677f34116e 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -291,8 +291,6 @@ static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test) dma_release_channel(epf_test->dma_chan_rx); epf_test->dma_chan_rx = NULL; - - return; } static void pci_epf_test_print_rate(struct pci_epf_test *epf_test, @@ -317,91 +315,92 @@ static void pci_epf_test_print_rate(struct pci_epf_test *epf_test, static void pci_epf_test_copy(struct pci_epf_test *epf_test, struct pci_epf_test_reg *reg) { - int ret; - void __iomem *src_addr; - void __iomem *dst_addr; - phys_addr_t src_phys_addr; - phys_addr_t dst_phys_addr; + int ret = 0; struct timespec64 start, end; struct pci_epf *epf = epf_test->epf; - struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; + struct pci_epc_map src_map, dst_map; + u64 src_addr = reg->src_addr; + u64 dst_addr = reg->dst_addr; + size_t copy_size = reg->size; + ssize_t map_size = 0; + void *copy_buf = NULL, *buf; - src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size); - if (!src_addr) { - dev_err(dev, "Failed to allocate source address\n"); - reg->status = STATUS_SRC_ADDR_INVALID; - ret = -ENOMEM; - goto err; - } - - ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr, - reg->src_addr, reg->size); - if (ret) { - dev_err(dev, "Failed to map source address\n"); - reg->status = STATUS_SRC_ADDR_INVALID; - goto err_src_addr; - } - - dst_addr = pci_epc_mem_alloc_addr(epc, &dst_phys_addr, reg->size); - if (!dst_addr) { - dev_err(dev, "Failed to allocate destination address\n"); - reg->status = STATUS_DST_ADDR_INVALID; - ret = -ENOMEM; - goto err_src_map_addr; - } - - ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr, - reg->dst_addr, reg->size); - if (ret) { - dev_err(dev, "Failed to map destination address\n"); - reg->status = STATUS_DST_ADDR_INVALID; - goto err_dst_addr; - } - - ktime_get_ts64(&start); if (reg->flags & FLAG_USE_DMA) { if (epf_test->dma_private) { dev_err(dev, "Cannot transfer data using DMA\n"); ret = -EINVAL; - goto err_map_addr; + goto set_status; } - - ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr, - src_phys_addr, reg->size, 0, - DMA_MEM_TO_MEM); - if (ret) - dev_err(dev, "Data transfer failed\n"); } else { - void *buf; - - buf = kzalloc(reg->size, GFP_KERNEL); - if (!buf) { + copy_buf = kzalloc(copy_size, GFP_KERNEL); + if (!copy_buf) { ret = -ENOMEM; - goto err_map_addr; + goto set_status; } - - memcpy_fromio(buf, src_addr, reg->size); - memcpy_toio(dst_addr, buf, reg->size); - kfree(buf); + buf = copy_buf; } - ktime_get_ts64(&end); - pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start, &end, - reg->flags & FLAG_USE_DMA); -err_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr); + while (copy_size) { + ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no, + src_addr, copy_size, &src_map); + if (ret) { + dev_err(dev, "Failed to map source address\n"); + reg->status = STATUS_SRC_ADDR_INVALID; + goto free_buf; + } + + ret = pci_epc_mem_map(epf->epc, epf->func_no, epf->vfunc_no, + dst_addr, copy_size, &dst_map); + if (ret) { + dev_err(dev, "Failed to map destination address\n"); + reg->status = STATUS_DST_ADDR_INVALID; + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, + &src_map); + goto free_buf; + } -err_dst_addr: - pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size); + map_size = min_t(size_t, dst_map.pci_size, src_map.pci_size); -err_src_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr); + ktime_get_ts64(&start); + if (reg->flags & FLAG_USE_DMA) { + ret = pci_epf_test_data_transfer(epf_test, + dst_map.phys_addr, src_map.phys_addr, + map_size, 0, DMA_MEM_TO_MEM); + if (ret) { + dev_err(dev, "Data transfer failed\n"); + goto unmap; + } + } else { + memcpy_fromio(buf, src_map.virt_addr, map_size); + memcpy_toio(dst_map.virt_addr, buf, map_size); + buf += map_size; + } + ktime_get_ts64(&end); -err_src_addr: - pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size); + copy_size -= map_size; + src_addr += map_size; + dst_addr += map_size; -err: + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map); + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map); + map_size = 0; + } + + pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start, + &end, reg->flags & FLAG_USE_DMA); + +unmap: + if (map_size) { + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map); + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map); + } + +free_buf: + kfree(copy_buf); + +set_status: if (!ret) reg->status |= STATUS_COPY_SUCCESS; else @@ -411,82 +410,89 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, static void pci_epf_test_read(struct pci_epf_test *epf_test, struct pci_epf_test_reg *reg) { - int ret; - void __iomem *src_addr; - void *buf; + int ret = 0; + void *src_buf, *buf; u32 crc32; - phys_addr_t phys_addr; + struct pci_epc_map map; phys_addr_t dst_phys_addr; struct timespec64 start, end; struct pci_epf *epf = epf_test->epf; - struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; struct device *dma_dev = epf->epc->dev.parent; + u64 src_addr = reg->src_addr; + size_t src_size = reg->size; + ssize_t map_size = 0; - src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size); - if (!src_addr) { - dev_err(dev, "Failed to allocate address\n"); - reg->status = STATUS_SRC_ADDR_INVALID; + src_buf = kzalloc(src_size, GFP_KERNEL); + if (!src_buf) { ret = -ENOMEM; - goto err; + goto set_status; } + buf = src_buf; - ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr, - reg->src_addr, reg->size); - if (ret) { - dev_err(dev, "Failed to map address\n"); - reg->status = STATUS_SRC_ADDR_INVALID; - goto err_addr; - } - - buf = kzalloc(reg->size, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto err_map_addr; - } + while (src_size) { + ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no, + src_addr, src_size, &map); + if (ret) { + dev_err(dev, "Failed to map address\n"); + reg->status = STATUS_SRC_ADDR_INVALID; + goto free_buf; + } - if (reg->flags & FLAG_USE_DMA) { - dst_phys_addr = dma_map_single(dma_dev, buf, reg->size, - DMA_FROM_DEVICE); - if (dma_mapping_error(dma_dev, dst_phys_addr)) { - dev_err(dev, "Failed to map destination buffer addr\n"); - ret = -ENOMEM; - goto err_dma_map; + map_size = map.pci_size; + if (reg->flags & FLAG_USE_DMA) { + dst_phys_addr = dma_map_single(dma_dev, buf, map_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dev, dst_phys_addr)) { + dev_err(dev, + "Failed to map destination buffer addr\n"); + ret = -ENOMEM; + goto unmap; + } + + ktime_get_ts64(&start); + ret = pci_epf_test_data_transfer(epf_test, + dst_phys_addr, map.phys_addr, + map_size, src_addr, DMA_DEV_TO_MEM); + if (ret) + dev_err(dev, "Data transfer failed\n"); + ktime_get_ts64(&end); + + dma_unmap_single(dma_dev, dst_phys_addr, map_size, + DMA_FROM_DEVICE); + + if (ret) + goto unmap; + } else { + ktime_get_ts64(&start); + memcpy_fromio(buf, map.virt_addr, map_size); + ktime_get_ts64(&end); } - ktime_get_ts64(&start); - ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr, - phys_addr, reg->size, - reg->src_addr, DMA_DEV_TO_MEM); - if (ret) - dev_err(dev, "Data transfer failed\n"); - ktime_get_ts64(&end); + src_size -= map_size; + src_addr += map_size; + buf += map_size; - dma_unmap_single(dma_dev, dst_phys_addr, reg->size, - DMA_FROM_DEVICE); - } else { - ktime_get_ts64(&start); - memcpy_fromio(buf, src_addr, reg->size); - ktime_get_ts64(&end); + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map); + map_size = 0; } - pci_epf_test_print_rate(epf_test, "READ", reg->size, &start, &end, - reg->flags & FLAG_USE_DMA); + pci_epf_test_print_rate(epf_test, "READ", reg->size, &start, + &end, reg->flags & FLAG_USE_DMA); - crc32 = crc32_le(~0, buf, reg->size); + crc32 = crc32_le(~0, src_buf, reg->size); if (crc32 != reg->checksum) ret = -EIO; -err_dma_map: - kfree(buf); - -err_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr); +unmap: + if (map_size) + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map); -err_addr: - pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size); +free_buf: + kfree(src_buf); -err: +set_status: if (!ret) reg->status |= STATUS_READ_SUCCESS; else @@ -496,71 +502,79 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, static void pci_epf_test_write(struct pci_epf_test *epf_test, struct pci_epf_test_reg *reg) { - int ret; - void __iomem *dst_addr; - void *buf; - phys_addr_t phys_addr; + int ret = 0; + void *dst_buf, *buf; + struct pci_epc_map map; phys_addr_t src_phys_addr; struct timespec64 start, end; struct pci_epf *epf = epf_test->epf; - struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; struct device *dma_dev = epf->epc->dev.parent; + u64 dst_addr = reg->dst_addr; + size_t dst_size = reg->size; + ssize_t map_size = 0; - dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size); - if (!dst_addr) { - dev_err(dev, "Failed to allocate address\n"); - reg->status = STATUS_DST_ADDR_INVALID; + dst_buf = kzalloc(dst_size, GFP_KERNEL); + if (!dst_buf) { ret = -ENOMEM; - goto err; + goto set_status; } + get_random_bytes(dst_buf, dst_size); + reg->checksum = crc32_le(~0, dst_buf, dst_size); + buf = dst_buf; - ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr, - reg->dst_addr, reg->size); - if (ret) { - dev_err(dev, "Failed to map address\n"); - reg->status = STATUS_DST_ADDR_INVALID; - goto err_addr; - } - - buf = kzalloc(reg->size, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto err_map_addr; - } - - get_random_bytes(buf, reg->size); - reg->checksum = crc32_le(~0, buf, reg->size); - - if (reg->flags & FLAG_USE_DMA) { - src_phys_addr = dma_map_single(dma_dev, buf, reg->size, - DMA_TO_DEVICE); - if (dma_mapping_error(dma_dev, src_phys_addr)) { - dev_err(dev, "Failed to map source buffer addr\n"); - ret = -ENOMEM; - goto err_dma_map; + while (dst_size) { + ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no, + dst_addr, dst_size, &map); + if (ret) { + dev_err(dev, "Failed to map address\n"); + reg->status = STATUS_DST_ADDR_INVALID; + goto free_buf; } - ktime_get_ts64(&start); + map_size = map.pci_size; + if (reg->flags & FLAG_USE_DMA) { + src_phys_addr = dma_map_single(dma_dev, buf, map_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dma_dev, src_phys_addr)) { + dev_err(dev, + "Failed to map source buffer addr\n"); + ret = -ENOMEM; + goto unmap; + } + + ktime_get_ts64(&start); + + ret = pci_epf_test_data_transfer(epf_test, + map.phys_addr, src_phys_addr, + map_size, dst_addr, + DMA_MEM_TO_DEV); + if (ret) + dev_err(dev, "Data transfer failed\n"); + ktime_get_ts64(&end); + + dma_unmap_single(dma_dev, src_phys_addr, map_size, + DMA_TO_DEVICE); + + if (ret) + goto unmap; + } else { + ktime_get_ts64(&start); + memcpy_toio(map.virt_addr, buf, map_size); + ktime_get_ts64(&end); + } - ret = pci_epf_test_data_transfer(epf_test, phys_addr, - src_phys_addr, reg->size, - reg->dst_addr, - DMA_MEM_TO_DEV); - if (ret) - dev_err(dev, "Data transfer failed\n"); - ktime_get_ts64(&end); + dst_size -= map_size; + dst_addr += map_size; + buf += map_size; - dma_unmap_single(dma_dev, src_phys_addr, reg->size, - DMA_TO_DEVICE); - } else { - ktime_get_ts64(&start); - memcpy_toio(dst_addr, buf, reg->size); - ktime_get_ts64(&end); + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map); + map_size = 0; } - pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start, &end, - reg->flags & FLAG_USE_DMA); + pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start, + &end, reg->flags & FLAG_USE_DMA); /* * wait 1ms inorder for the write to complete. Without this delay L3 @@ -568,16 +582,14 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, */ usleep_range(1000, 2000); -err_dma_map: - kfree(buf); - -err_map_addr: - pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr); +unmap: + if (map_size) + pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map); -err_addr: - pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size); +free_buf: + kfree(dst_buf); -err: +set_status: if (!ret) reg->status |= STATUS_WRITE_SUCCESS; else @@ -786,7 +798,7 @@ static void pci_epf_test_epc_deinit(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); - cancel_delayed_work(&epf_test->cmd_handler); + cancel_delayed_work_sync(&epf_test->cmd_handler); pci_epf_test_clean_dma_chan(epf_test); pci_epf_test_clear_bar(epf); } @@ -917,7 +929,7 @@ static void pci_epf_test_unbind(struct pci_epf *epf) struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epc *epc = epf->epc; - cancel_delayed_work(&epf_test->cmd_handler); + cancel_delayed_work_sync(&epf_test->cmd_handler); if (epc->init_complete) { pci_epf_test_clean_dma_chan(epf_test); pci_epf_test_clear_bar(epf); diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 17f0071092550..bed7c7d1fe3c3 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -128,6 +128,18 @@ enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features } EXPORT_SYMBOL_GPL(pci_epc_get_next_free_bar); +static bool pci_epc_function_is_valid(struct pci_epc *epc, + u8 func_no, u8 vfunc_no) +{ + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + return false; + + if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + return false; + + return true; +} + /** * pci_epc_get_features() - get the features supported by EPC * @epc: the features supported by *this* EPC device will be returned @@ -145,10 +157,7 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, { const struct pci_epc_features *epc_features; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return NULL; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return NULL; if (!epc->ops->get_features) @@ -218,10 +227,7 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { int ret; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return -EINVAL; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return -EINVAL; if (!epc->ops->raise_irq) @@ -262,10 +268,7 @@ int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { int ret; - if (IS_ERR_OR_NULL(epc)) - return -EINVAL; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return -EINVAL; if (!epc->ops->map_msi_irq) @@ -293,10 +296,7 @@ int pci_epc_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { int interrupt; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return 0; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return 0; if (!epc->ops->get_msi) @@ -329,11 +329,10 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u8 interrupts) int ret; u8 encode_int; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || - interrupts < 1 || interrupts > 32) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return -EINVAL; - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (interrupts < 1 || interrupts > 32) return -EINVAL; if (!epc->ops->set_msi) @@ -361,10 +360,7 @@ int pci_epc_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { int interrupt; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return 0; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return 0; if (!epc->ops->get_msix) @@ -397,11 +393,10 @@ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { int ret; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || - interrupts < 1 || interrupts > 2048) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return -EINVAL; - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (interrupts < 1 || interrupts > 2048) return -EINVAL; if (!epc->ops->set_msix) @@ -428,10 +423,7 @@ EXPORT_SYMBOL_GPL(pci_epc_set_msix); void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr) { - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return; if (!epc->ops->unmap_addr) @@ -459,10 +451,7 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { int ret; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return -EINVAL; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return -EINVAL; if (!epc->ops->map_addr) @@ -477,6 +466,109 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, } EXPORT_SYMBOL_GPL(pci_epc_map_addr); +/** + * pci_epc_mem_map() - allocate and map a PCI address to a CPU address + * @epc: the EPC device on which the CPU address is to be allocated and mapped + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function + * @pci_addr: PCI address to which the CPU address should be mapped + * @pci_size: the number of bytes to map starting from @pci_addr + * @map: where to return the mapping information + * + * Allocate a controller memory address region and map it to a RC PCI address + * region, taking into account the controller physical address mapping + * constraints using the controller operation align_addr(). If this operation is + * not defined, we assume that there are no alignment constraints for the + * mapping. + * + * The effective size of the PCI address range mapped from @pci_addr is + * indicated by @map->pci_size. This size may be less than the requested + * @pci_size. The local virtual CPU address for the mapping is indicated by + * @map->virt_addr (@map->phys_addr indicates the physical address). + * The size and CPU address of the controller memory allocated and mapped are + * respectively indicated by @map->map_size and @map->virt_base (and + * @map->phys_base for the physical address of @map->virt_base). + * + * Returns 0 on success and a negative error code in case of error. + */ +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u64 pci_addr, size_t pci_size, struct pci_epc_map *map) +{ + size_t map_size = pci_size; + size_t map_offset = 0; + int ret; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!pci_size || !map) + return -EINVAL; + + /* + * Align the PCI address to map. If the controller defines the + * .align_addr() operation, use it to determine the PCI address to map + * and the size of the mapping. Otherwise, assume that the controller + * has no alignment constraint. + */ + memset(map, 0, sizeof(*map)); + map->pci_addr = pci_addr; + if (epc->ops->align_addr) + map->map_pci_addr = + epc->ops->align_addr(epc, pci_addr, + &map_size, &map_offset); + else + map->map_pci_addr = pci_addr; + map->map_size = map_size; + if (map->map_pci_addr + map->map_size < pci_addr + pci_size) + map->pci_size = map->map_pci_addr + map->map_size - pci_addr; + else + map->pci_size = pci_size; + + map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base, + map->map_size); + if (!map->virt_base) + return -ENOMEM; + + map->phys_addr = map->phys_base + map_offset; + map->virt_addr = map->virt_base + map_offset; + + ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base, + map->map_pci_addr, map->map_size); + if (ret) { + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base, + map->map_size); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pci_epc_mem_map); + +/** + * pci_epc_mem_unmap() - unmap and free a CPU address region + * @epc: the EPC device on which the CPU address is allocated and mapped + * @func_no: the physical endpoint function number in the EPC device + * @vfunc_no: the virtual endpoint function number in the physical function + * @map: the mapping information + * + * Unmap and free a CPU address region that was allocated and mapped with + * pci_epc_mem_map(). + */ +void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_map *map) +{ + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return; + + if (!map || !map->virt_base) + return; + + pci_epc_unmap_addr(epc, func_no, vfunc_no, map->phys_base); + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base, + map->map_size); +} +EXPORT_SYMBOL_GPL(pci_epc_mem_unmap); + /** * pci_epc_clear_bar() - reset the BAR * @epc: the EPC device for which the BAR has to be cleared @@ -489,12 +581,11 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr); void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || - (epf_bar->barno == BAR_5 && - epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return; - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (epf_bar->barno == BAR_5 && + epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) return; if (!epc->ops->clear_bar) @@ -521,18 +612,16 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, int ret; int flags = epf_bar->flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || - (epf_bar->barno == BAR_5 && - flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || (flags & PCI_BASE_ADDRESS_SPACE_IO && flags & PCI_BASE_ADDRESS_IO_MASK) || (upper_32_bits(epf_bar->size) && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))) return -EINVAL; - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) - return -EINVAL; - if (!epc->ops->set_bar) return 0; @@ -561,10 +650,7 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { int ret; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) - return -EINVAL; - - if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no])) + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) return -EINVAL; /* Only Virtual Function #1 has deviceID */ @@ -660,18 +746,18 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, if (IS_ERR_OR_NULL(epc) || !epf) return; + mutex_lock(&epc->list_lock); if (type == PRIMARY_INTERFACE) { func_no = epf->func_no; list = &epf->list; + epf->epc = NULL; } else { func_no = epf->sec_epc_func_no; list = &epf->sec_epc_list; + epf->sec_epc = NULL; } - - mutex_lock(&epc->list_lock); clear_bit(func_no, &epc->function_num_map); list_del(list); - epf->epc = NULL; mutex_unlock(&epc->list_lock); } EXPORT_SYMBOL_GPL(pci_epc_remove_epf); @@ -837,11 +923,10 @@ EXPORT_SYMBOL_GPL(pci_epc_bus_master_enable_notify); void pci_epc_destroy(struct pci_epc *epc) { pci_ep_cfs_remove_epc_group(epc->group); - device_unregister(&epc->dev); - #ifdef CONFIG_PCI_DOMAINS_GENERIC - pci_bus_release_domain_nr(&epc->dev, epc->domain_nr); + pci_bus_release_domain_nr(epc->dev.parent, epc->domain_nr); #endif + device_unregister(&epc->dev); } EXPORT_SYMBOL_GPL(pci_epc_destroy); diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c index a9c028f58da17..218a60e945db6 100644 --- a/drivers/pci/endpoint/pci-epc-mem.c +++ b/drivers/pci/endpoint/pci-epc-mem.c @@ -178,7 +178,7 @@ EXPORT_SYMBOL_GPL(pci_epc_mem_exit); void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, phys_addr_t *phys_addr, size_t size) { - void __iomem *virt_addr = NULL; + void __iomem *virt_addr; struct pci_epc_mem *mem; unsigned int page_shift; size_t align_size; @@ -188,10 +188,13 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, for (i = 0; i < epc->num_windows; i++) { mem = epc->windows[i]; - mutex_lock(&mem->lock); + if (size > mem->window.size) + continue; + align_size = ALIGN(size, mem->window.page_size); order = pci_epc_mem_get_order(mem, align_size); + mutex_lock(&mem->lock); pageno = bitmap_find_free_region(mem->bitmap, mem->pages, order); if (pageno >= 0) { @@ -211,7 +214,7 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, mutex_unlock(&mem->lock); } - return virt_addr; + return NULL; } EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr); diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 1472aef0fb812..123c4c7c2ab59 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -118,6 +118,16 @@ config HOTPLUG_PCI_CPCI_GENERIC When in doubt, say N. +config HOTPLUG_PCI_OCTEONEP + bool "Marvell OCTEON PCI Hotplug driver" + depends on HOTPLUG_PCI + help + Say Y here if you have an OCTEON PCIe device with a hotplug + controller. This driver enables the non-controller functions of the + device to be registered as hotplug slots. + + When in doubt, say N. + config HOTPLUG_PCI_SHPC bool "SHPC PCI Hotplug driver" help diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 240c99517d5e9..40aaf31fe3384 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o +obj-$(CONFIG_HOTPLUG_PCI_OCTEONEP) += octep_hp.o # acpiphp_ibm extends acpiphp, so should be linked afterwards. diff --git a/drivers/pci/hotplug/acpiphp_ampere_altra.c b/drivers/pci/hotplug/acpiphp_ampere_altra.c index f5c9e741c1d41..70dbc0431fc69 100644 --- a/drivers/pci/hotplug/acpiphp_ampere_altra.c +++ b/drivers/pci/hotplug/acpiphp_ampere_altra.c @@ -119,7 +119,7 @@ static struct platform_driver altra_led_driver = { .acpi_match_table = altra_led_ids, }, .probe = altra_led_probe, - .remove_new = altra_led_remove, + .remove = altra_led_remove, }; module_platform_driver(altra_led_driver); diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h index 6d8970d8c3f29..03fa39ab0c884 100644 --- a/drivers/pci/hotplug/cpci_hotplug.h +++ b/drivers/pci/hotplug/cpci_hotplug.h @@ -44,7 +44,6 @@ struct cpci_hp_controller_ops { int (*enable_irq)(void); int (*disable_irq)(void); int (*check_irq)(void *dev_id); - int (*hardware_test)(struct slot *slot, u32 value); u8 (*get_power)(struct slot *slot); int (*set_power)(struct slot *slot, int value); }; diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 718bc6cf12cb3..ef7534a3ca40f 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -12,8 +12,11 @@ * */ +#define pr_fmt(fmt) "cpqphp: " fmt + #include #include +#include #include #include #include @@ -132,18 +135,6 @@ int cpqhp_unconfigure_device(struct pci_func *func) return 0; } -static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value) -{ - u32 vendID = 0; - - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendID) == -1) - return -1; - if (PCI_POSSIBLE_ERROR(vendID)) - return -1; - return pci_bus_read_config_dword(bus, devfn, offset, value); -} - - /* * cpqhp_set_irq * @@ -202,13 +193,16 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 *dev_ { u16 tdevice; u32 work; - u8 tbus; + int ret = -1; ctrl->pci_bus->number = bus_num; for (tdevice = 0; tdevice < 0xFF; tdevice++) { /* Scan for access first */ - if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) + if (!pci_bus_read_dev_vendor_id(ctrl->pci_bus, tdevice, &work, 0)) + continue; + ret = pci_bus_read_config_dword(ctrl->pci_bus, tdevice, PCI_CLASS_REVISION, &work); + if (ret) continue; dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); /* Yep we got one. Not a bridge ? */ @@ -216,23 +210,20 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 *dev_ *dev_num = tdevice; dbg("found it !\n"); return 0; - } - } - for (tdevice = 0; tdevice < 0xFF; tdevice++) { - /* Scan for access first */ - if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) - continue; - dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); - /* Yep we got one. bridge ? */ - if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { - pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus); - /* XXX: no recursion, wtf? */ - dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); - return 0; + } else { + /* + * XXX: Code whose debug printout indicated + * recursion to buses underneath bridges might be + * necessary was removed because it never did + * any recursion. + */ + ret = 0; + pr_warn("missing feature: bridge scan recursion not implemented\n"); } } - return -1; + + return ret; } diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c index fed1360ee9b18..6143ebf71f211 100644 --- a/drivers/pci/hotplug/cpqphp_sysfs.c +++ b/drivers/pci/hotplug/cpqphp_sysfs.c @@ -123,7 +123,6 @@ static int spew_debug_info(struct controller *ctrl, char *data, int size) struct ctrl_dbg { int size; char *data; - struct controller *ctrl; }; #define MAX_OUTPUT (4*PAGE_SIZE) diff --git a/drivers/pci/hotplug/octep_hp.c b/drivers/pci/hotplug/octep_hp.c new file mode 100644 index 0000000000000..2bce7296c0504 --- /dev/null +++ b/drivers/pci/hotplug/octep_hp.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2024 Marvell. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OCTEP_HP_INTR_OFFSET(x) (0x20400 + ((x) << 4)) +#define OCTEP_HP_INTR_VECTOR(x) (16 + (x)) +#define OCTEP_HP_DRV_NAME "octep_hp" + +/* + * Type of MSI-X interrupts. OCTEP_HP_INTR_VECTOR() and + * OCTEP_HP_INTR_OFFSET() generate the vector and offset for an interrupt + * type. + */ +enum octep_hp_intr_type { + OCTEP_HP_INTR_INVALID = -1, + OCTEP_HP_INTR_ENA = 0, + OCTEP_HP_INTR_DIS = 1, + OCTEP_HP_INTR_MAX = 2, +}; + +struct octep_hp_cmd { + struct list_head list; + enum octep_hp_intr_type intr_type; + u64 intr_val; +}; + +struct octep_hp_slot { + struct list_head list; + struct hotplug_slot slot; + u16 slot_number; + struct pci_dev *hp_pdev; + unsigned int hp_devfn; + struct octep_hp_controller *ctrl; +}; + +struct octep_hp_intr_info { + enum octep_hp_intr_type type; + int number; + char name[16]; +}; + +struct octep_hp_controller { + void __iomem *base; + struct pci_dev *pdev; + struct octep_hp_intr_info intr[OCTEP_HP_INTR_MAX]; + struct work_struct work; + struct list_head slot_list; + struct mutex slot_lock; /* Protects slot_list */ + struct list_head hp_cmd_list; + spinlock_t hp_cmd_lock; /* Protects hp_cmd_list */ +}; + +static void octep_hp_enable_pdev(struct octep_hp_controller *hp_ctrl, + struct octep_hp_slot *hp_slot) +{ + guard(mutex)(&hp_ctrl->slot_lock); + if (hp_slot->hp_pdev) { + pci_dbg(hp_slot->hp_pdev, "Slot %s is already enabled\n", + hotplug_slot_name(&hp_slot->slot)); + return; + } + + /* Scan the device and add it to the bus */ + hp_slot->hp_pdev = pci_scan_single_device(hp_ctrl->pdev->bus, + hp_slot->hp_devfn); + pci_bus_assign_resources(hp_ctrl->pdev->bus); + pci_bus_add_device(hp_slot->hp_pdev); + + dev_dbg(&hp_slot->hp_pdev->dev, "Enabled slot %s\n", + hotplug_slot_name(&hp_slot->slot)); +} + +static void octep_hp_disable_pdev(struct octep_hp_controller *hp_ctrl, + struct octep_hp_slot *hp_slot) +{ + guard(mutex)(&hp_ctrl->slot_lock); + if (!hp_slot->hp_pdev) { + pci_dbg(hp_ctrl->pdev, "Slot %s is already disabled\n", + hotplug_slot_name(&hp_slot->slot)); + return; + } + + pci_dbg(hp_slot->hp_pdev, "Disabling slot %s\n", + hotplug_slot_name(&hp_slot->slot)); + + /* Remove the device from the bus */ + pci_stop_and_remove_bus_device_locked(hp_slot->hp_pdev); + hp_slot->hp_pdev = NULL; +} + +static int octep_hp_enable_slot(struct hotplug_slot *slot) +{ + struct octep_hp_slot *hp_slot = + container_of(slot, struct octep_hp_slot, slot); + + octep_hp_enable_pdev(hp_slot->ctrl, hp_slot); + return 0; +} + +static int octep_hp_disable_slot(struct hotplug_slot *slot) +{ + struct octep_hp_slot *hp_slot = + container_of(slot, struct octep_hp_slot, slot); + + octep_hp_disable_pdev(hp_slot->ctrl, hp_slot); + return 0; +} + +static struct hotplug_slot_ops octep_hp_slot_ops = { + .enable_slot = octep_hp_enable_slot, + .disable_slot = octep_hp_disable_slot, +}; + +#define SLOT_NAME_SIZE 16 +static struct octep_hp_slot * +octep_hp_register_slot(struct octep_hp_controller *hp_ctrl, + struct pci_dev *pdev, u16 slot_number) +{ + char slot_name[SLOT_NAME_SIZE]; + struct octep_hp_slot *hp_slot; + int ret; + + hp_slot = kzalloc(sizeof(*hp_slot), GFP_KERNEL); + if (!hp_slot) + return ERR_PTR(-ENOMEM); + + hp_slot->ctrl = hp_ctrl; + hp_slot->hp_pdev = pdev; + hp_slot->hp_devfn = pdev->devfn; + hp_slot->slot_number = slot_number; + hp_slot->slot.ops = &octep_hp_slot_ops; + + snprintf(slot_name, sizeof(slot_name), "octep_hp_%u", slot_number); + ret = pci_hp_register(&hp_slot->slot, hp_ctrl->pdev->bus, + PCI_SLOT(pdev->devfn), slot_name); + if (ret) { + kfree(hp_slot); + return ERR_PTR(ret); + } + + pci_info(pdev, "Registered slot %s for device %s\n", + slot_name, pci_name(pdev)); + + list_add_tail(&hp_slot->list, &hp_ctrl->slot_list); + octep_hp_disable_pdev(hp_ctrl, hp_slot); + + return hp_slot; +} + +static void octep_hp_deregister_slot(void *data) +{ + struct octep_hp_slot *hp_slot = data; + struct octep_hp_controller *hp_ctrl = hp_slot->ctrl; + + pci_hp_deregister(&hp_slot->slot); + octep_hp_enable_pdev(hp_ctrl, hp_slot); + list_del(&hp_slot->list); + kfree(hp_slot); +} + +static const char *octep_hp_cmd_name(enum octep_hp_intr_type type) +{ + switch (type) { + case OCTEP_HP_INTR_ENA: + return "hotplug enable"; + case OCTEP_HP_INTR_DIS: + return "hotplug disable"; + default: + return "invalid"; + } +} + +static void octep_hp_cmd_handler(struct octep_hp_controller *hp_ctrl, + struct octep_hp_cmd *hp_cmd) +{ + struct octep_hp_slot *hp_slot; + + /* + * Enable or disable the slots based on the slot mask. + * intr_val is a bit mask where each bit represents a slot. + */ + list_for_each_entry(hp_slot, &hp_ctrl->slot_list, list) { + if (!(hp_cmd->intr_val & BIT(hp_slot->slot_number))) + continue; + + pci_info(hp_ctrl->pdev, "Received %s command for slot %s\n", + octep_hp_cmd_name(hp_cmd->intr_type), + hotplug_slot_name(&hp_slot->slot)); + + switch (hp_cmd->intr_type) { + case OCTEP_HP_INTR_ENA: + octep_hp_enable_pdev(hp_ctrl, hp_slot); + break; + case OCTEP_HP_INTR_DIS: + octep_hp_disable_pdev(hp_ctrl, hp_slot); + break; + default: + break; + } + } +} + +static void octep_hp_work_handler(struct work_struct *work) +{ + struct octep_hp_controller *hp_ctrl; + struct octep_hp_cmd *hp_cmd; + unsigned long flags; + + hp_ctrl = container_of(work, struct octep_hp_controller, work); + + /* Process all the hotplug commands */ + spin_lock_irqsave(&hp_ctrl->hp_cmd_lock, flags); + while (!list_empty(&hp_ctrl->hp_cmd_list)) { + hp_cmd = list_first_entry(&hp_ctrl->hp_cmd_list, + struct octep_hp_cmd, list); + list_del(&hp_cmd->list); + spin_unlock_irqrestore(&hp_ctrl->hp_cmd_lock, flags); + + octep_hp_cmd_handler(hp_ctrl, hp_cmd); + kfree(hp_cmd); + + spin_lock_irqsave(&hp_ctrl->hp_cmd_lock, flags); + } + spin_unlock_irqrestore(&hp_ctrl->hp_cmd_lock, flags); +} + +static enum octep_hp_intr_type octep_hp_intr_type(struct octep_hp_intr_info *intr, + int irq) +{ + enum octep_hp_intr_type type; + + for (type = OCTEP_HP_INTR_ENA; type < OCTEP_HP_INTR_MAX; type++) { + if (intr[type].number == irq) + return type; + } + + return OCTEP_HP_INTR_INVALID; +} + +static irqreturn_t octep_hp_intr_handler(int irq, void *data) +{ + struct octep_hp_controller *hp_ctrl = data; + struct pci_dev *pdev = hp_ctrl->pdev; + enum octep_hp_intr_type type; + struct octep_hp_cmd *hp_cmd; + u64 intr_val; + + type = octep_hp_intr_type(hp_ctrl->intr, irq); + if (type == OCTEP_HP_INTR_INVALID) { + pci_err(pdev, "Invalid interrupt %d\n", irq); + return IRQ_HANDLED; + } + + /* Read and clear the interrupt */ + intr_val = readq(hp_ctrl->base + OCTEP_HP_INTR_OFFSET(type)); + writeq(intr_val, hp_ctrl->base + OCTEP_HP_INTR_OFFSET(type)); + + hp_cmd = kzalloc(sizeof(*hp_cmd), GFP_ATOMIC); + if (!hp_cmd) + return IRQ_HANDLED; + + hp_cmd->intr_val = intr_val; + hp_cmd->intr_type = type; + + /* Add the command to the list and schedule the work */ + spin_lock(&hp_ctrl->hp_cmd_lock); + list_add_tail(&hp_cmd->list, &hp_ctrl->hp_cmd_list); + spin_unlock(&hp_ctrl->hp_cmd_lock); + schedule_work(&hp_ctrl->work); + + return IRQ_HANDLED; +} + +static void octep_hp_irq_cleanup(void *data) +{ + struct octep_hp_controller *hp_ctrl = data; + + pci_free_irq_vectors(hp_ctrl->pdev); + flush_work(&hp_ctrl->work); +} + +static int octep_hp_request_irq(struct octep_hp_controller *hp_ctrl, + enum octep_hp_intr_type type) +{ + struct pci_dev *pdev = hp_ctrl->pdev; + struct octep_hp_intr_info *intr; + int irq; + + irq = pci_irq_vector(pdev, OCTEP_HP_INTR_VECTOR(type)); + if (irq < 0) + return irq; + + intr = &hp_ctrl->intr[type]; + intr->number = irq; + intr->type = type; + snprintf(intr->name, sizeof(intr->name), "octep_hp_%d", type); + + return devm_request_irq(&pdev->dev, irq, octep_hp_intr_handler, + IRQF_SHARED, intr->name, hp_ctrl); +} + +static int octep_hp_controller_setup(struct pci_dev *pdev, + struct octep_hp_controller *hp_ctrl) +{ + struct device *dev = &pdev->dev; + enum octep_hp_intr_type type; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable PCI device\n"); + + hp_ctrl->base = pcim_iomap_region(pdev, 0, OCTEP_HP_DRV_NAME); + if (IS_ERR(hp_ctrl->base)) + return dev_err_probe(dev, PTR_ERR(hp_ctrl->base), + "Failed to map PCI device region\n"); + + pci_set_master(pdev); + pci_set_drvdata(pdev, hp_ctrl); + + INIT_LIST_HEAD(&hp_ctrl->slot_list); + INIT_LIST_HEAD(&hp_ctrl->hp_cmd_list); + mutex_init(&hp_ctrl->slot_lock); + spin_lock_init(&hp_ctrl->hp_cmd_lock); + INIT_WORK(&hp_ctrl->work, octep_hp_work_handler); + hp_ctrl->pdev = pdev; + + ret = pci_alloc_irq_vectors(pdev, 1, + OCTEP_HP_INTR_VECTOR(OCTEP_HP_INTR_MAX), + PCI_IRQ_MSIX); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to alloc MSI-X vectors\n"); + + ret = devm_add_action(&pdev->dev, octep_hp_irq_cleanup, hp_ctrl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to add IRQ cleanup action\n"); + + for (type = OCTEP_HP_INTR_ENA; type < OCTEP_HP_INTR_MAX; type++) { + ret = octep_hp_request_irq(hp_ctrl, type); + if (ret) + return dev_err_probe(dev, ret, + "Failed to request IRQ for vector %d\n", + OCTEP_HP_INTR_VECTOR(type)); + } + + return 0; +} + +static int octep_hp_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct octep_hp_controller *hp_ctrl; + struct pci_dev *tmp_pdev, *next; + struct octep_hp_slot *hp_slot; + u16 slot_number = 0; + int ret; + + hp_ctrl = devm_kzalloc(&pdev->dev, sizeof(*hp_ctrl), GFP_KERNEL); + if (!hp_ctrl) + return -ENOMEM; + + ret = octep_hp_controller_setup(pdev, hp_ctrl); + if (ret) + return ret; + + /* + * Register all hotplug slots. Hotplug controller is the first function + * of the PCI device. The hotplug slots are the remaining functions of + * the PCI device. The hotplug slot functions are logically removed from + * the bus during probing and are re-enabled by the driver when a + * hotplug event is received. + */ + list_for_each_entry_safe(tmp_pdev, next, &pdev->bus->devices, bus_list) { + if (tmp_pdev == pdev) + continue; + + hp_slot = octep_hp_register_slot(hp_ctrl, tmp_pdev, slot_number); + if (IS_ERR(hp_slot)) + return dev_err_probe(&pdev->dev, PTR_ERR(hp_slot), + "Failed to register hotplug slot %u\n", + slot_number); + + ret = devm_add_action(&pdev->dev, octep_hp_deregister_slot, + hp_slot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to add action for deregistering slot %u\n", + slot_number); + slot_number++; + } + + return 0; +} + +#define PCI_DEVICE_ID_CAVIUM_OCTEP_HP_CTLR 0xa0e3 +static struct pci_device_id octep_hp_pci_map[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_OCTEP_HP_CTLR) }, + { }, +}; + +static struct pci_driver octep_hp = { + .name = OCTEP_HP_DRV_NAME, + .id_table = octep_hp_pci_map, + .probe = octep_hp_pci_probe, +}; + +module_pci_driver(octep_hp); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marvell"); +MODULE_DESCRIPTION("Marvell OCTEON PCI Hotplug driver"); diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 058d5937d8a95..36236ac88fd56 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -388,8 +388,8 @@ static struct hotplug_slot *get_slot_from_name(const char *name) /** * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem - * @bus: bus this slot is on * @slot: pointer to the &struct hotplug_slot to register + * @bus: bus this slot is on * @devnr: device number * @name: name registered with kobject core * @owner: caller module owner @@ -498,8 +498,6 @@ EXPORT_SYMBOL_GPL(pci_hp_add); * * The @slot must have been registered with the pci hotplug subsystem * previously with a call to pci_hp_register(). - * - * Returns 0 if successful, anything else for an error. */ void pci_hp_deregister(struct hotplug_slot *slot) { @@ -513,8 +511,6 @@ EXPORT_SYMBOL_GPL(pci_hp_deregister); * @slot: pointer to the &struct hotplug_slot to unpublish * * Remove a hotplug slot's sysfs interface. - * - * Returns 0 on success or a negative int on error. */ void pci_hp_del(struct hotplug_slot *slot) { @@ -545,8 +541,6 @@ EXPORT_SYMBOL_GPL(pci_hp_del); * the driver may no longer invoke hotplug_slot_name() to get the slot's * unique name. The driver no longer needs to handle a ->reset_slot callback * from this point on. - * - * Returns 0 on success or a negative int on error. */ void pci_hp_destroy(struct hotplug_slot *slot) { diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index dcdbfcf404ddf..d603a7aa74838 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -19,6 +19,8 @@ #include #include #include + +#include "../pci.h" #include "pciehp.h" /* The following routines constitute the bulk of the @@ -127,6 +129,9 @@ static void remove_board(struct controller *ctrl, bool safe_removal) pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, INDICATOR_NOOP); + + /* Don't carry LBMS indications across */ + pcie_reset_lbms_count(ctrl->pcie->port); } static int pciehp_enable_slot(struct controller *ctrl); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 736ad8baa2a55..bb5a8d9f03ad9 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -319,7 +319,7 @@ int pciehp_check_link_status(struct controller *ctrl) return -1; } - pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); + __pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); if (!found) { ctrl_info(ctrl, "Slot(%s): No device found\n", diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index aaa33e8dc4c97..4be402fe9ab94 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -327,8 +327,8 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) virtfn->resource[i].name = pci_name(virtfn); virtfn->resource[i].flags = res->flags; size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); - virtfn->resource[i].start = res->start + size * id; - virtfn->resource[i].end = virtfn->resource[i].start + size - 1; + resource_set_range(&virtfn->resource[i], + res->start + size * id, size); rc = request_resource(res, &virtfn->resource[i]); BUG_ON(rc); } @@ -804,7 +804,7 @@ static int sriov_init(struct pci_dev *dev, int pos) goto failed; } iov->barsz[i] = resource_size(res); - res->end = res->start + resource_size(res) * total - 1; + resource_set_size(res, resource_size(res) * total); pci_info(dev, "%s %pR: contains BAR %d for %d VFs\n", res_name, res, i, total); i += bar64; diff --git a/drivers/pci/of.c b/drivers/pci/of.c index dacea3fc5128f..52f770bcc4819 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -728,6 +728,33 @@ void of_pci_make_dev_node(struct pci_dev *pdev) } #endif +/** + * of_pci_supply_present() - Check if the power supply is present for the PCI + * device + * @np: Device tree node + * + * Check if the power supply for the PCI device is present in the device tree + * node or not. + * + * Return: true if at least one power supply exists; false otherwise. + */ +bool of_pci_supply_present(struct device_node *np) +{ + struct property *prop; + char *supply; + + if (!np) + return false; + + for_each_property_of_node(np, prop) { + supply = strrchr(prop->name, '-'); + if (supply && !strcmp(supply, "-supply")) + return true; + } + + return false; +} + #endif /* CONFIG_PCI */ /** diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index 5a0b98e697954..886c236e5de6e 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -126,7 +126,7 @@ static int of_pci_prop_ranges(struct pci_dev *pdev, struct of_changeset *ocs, if (of_pci_get_addr_flags(&res[j], &flags)) continue; - val64 = res[j].start; + val64 = pci_bus_address(pdev, &res[j] - pdev->resource); of_pci_set_address(pdev, rp[i].parent_addr, val64, 0, flags, false); if (pci_is_bridge(pdev)) { diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4f47a13cb500f..7abd4f546d3c0 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -90,7 +90,7 @@ static ssize_t published_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(published); static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, struct vm_area_struct *vma) + const struct bin_attribute *attr, struct vm_area_struct *vma) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); size_t len = vma->vm_end - vma->vm_start; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5d0f4db1cab78..7679d75d71e53 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -521,6 +521,31 @@ static ssize_t bus_rescan_store(struct device *dev, static struct device_attribute dev_attr_bus_rescan = __ATTR(rescan, 0200, NULL, bus_rescan_store); +static ssize_t reset_subordinate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_bus *bus = pdev->subordinate; + unsigned long val; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + int ret = __pci_reset_bus(bus); + + if (ret) + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(reset_subordinate); + #if defined(CONFIG_PM) && defined(CONFIG_ACPI) static ssize_t d3cold_allowed_store(struct device *dev, struct device_attribute *attr, @@ -625,6 +650,7 @@ static struct attribute *pci_dev_attrs[] = { static struct attribute *pci_bridge_attrs[] = { &dev_attr_subordinate_bus_number.attr, &dev_attr_secondary_bus_number.attr, + &dev_attr_reset_subordinate.attr, NULL, }; @@ -818,21 +844,20 @@ static struct bin_attribute *pci_dev_config_attrs[] = { NULL, }; -static umode_t pci_dev_config_attr_is_visible(struct kobject *kobj, - struct bin_attribute *a, int n) +static size_t pci_dev_config_attr_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int n) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - a->size = PCI_CFG_SPACE_SIZE; if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) - a->size = PCI_CFG_SPACE_EXP_SIZE; - - return a->attr.mode; + return PCI_CFG_SPACE_EXP_SIZE; + return PCI_CFG_SPACE_SIZE; } static const struct attribute_group pci_dev_config_attr_group = { .bin_attrs = pci_dev_config_attrs, - .is_bin_visible = pci_dev_config_attr_is_visible, + .bin_size = pci_dev_config_attr_bin_size, }; /* @@ -842,7 +867,7 @@ static const struct attribute_group pci_dev_config_attr_group = { static __maybe_unused loff_t pci_llseek_resource(struct file *filep, struct kobject *kobj __always_unused, - struct bin_attribute *attr, + const struct bin_attribute *attr, loff_t offset, int whence) { return fixed_size_llseek(filep, offset, whence, attr->size); @@ -911,7 +936,7 @@ static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj, * memory space. */ static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); @@ -931,7 +956,7 @@ static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, * memory space. Returns -ENOSYS if the operation isn't supported */ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); @@ -1035,7 +1060,7 @@ void pci_remove_legacy_files(struct pci_bus *b) * * Use the regular PCI mapping routines to map a PCI resource into userspace. */ -static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, +static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *attr, struct vm_area_struct *vma, int write_combine) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); @@ -1060,14 +1085,14 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, } static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 0); } static int pci_mmap_resource_wc(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 1); @@ -1327,24 +1352,29 @@ static struct bin_attribute *pci_dev_rom_attrs[] = { }; static umode_t pci_dev_rom_attr_is_visible(struct kobject *kobj, - struct bin_attribute *a, int n) + const struct bin_attribute *a, int n) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - size_t rom_size; /* If the device has a ROM, try to expose it in sysfs. */ - rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); - if (!rom_size) + if (!pci_resource_end(pdev, PCI_ROM_RESOURCE)) return 0; - a->size = rom_size; - return a->attr.mode; } +static size_t pci_dev_rom_attr_bin_size(struct kobject *kobj, + const struct bin_attribute *a, int n) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + return pci_resource_len(pdev, PCI_ROM_RESOURCE); +} + static const struct attribute_group pci_dev_rom_attr_group = { .bin_attrs = pci_dev_rom_attrs, .is_bin_visible = pci_dev_rom_attr_is_visible, + .bin_size = pci_dev_rom_attr_bin_size, }; static ssize_t reset_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 63690375d36be..0b29ec6e8e5e2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1832,6 +1832,7 @@ int pci_save_state(struct pci_dev *dev) pci_save_dpc_state(dev); pci_save_aer_state(dev); pci_save_ptm_state(dev); + pci_save_tph_state(dev); return pci_save_vc_state(dev); } EXPORT_SYMBOL(pci_save_state); @@ -1937,6 +1938,7 @@ void pci_restore_state(struct pci_dev *dev) pci_restore_rebar_state(dev); pci_restore_dpc_state(dev); pci_restore_ptm_state(dev); + pci_restore_tph_state(dev); pci_aer_clear_status(dev); pci_restore_aer_state(dev); @@ -4744,7 +4746,7 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt) * to track link speed or width changes made by hardware itself * in attempt to correct unreliable link operation. */ - pcie_capability_write_word(pdev, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS); + pcie_reset_lbms_count(pdev); return rc; } @@ -5162,6 +5164,8 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) */ if (err_handler && err_handler->reset_prepare) err_handler->reset_prepare(dev); + else if (dev->driver) + pci_warn(dev, "resetting"); /* * Wake-up device prior to save. PM registers default to D0 after @@ -5195,6 +5199,8 @@ static void pci_dev_restore(struct pci_dev *dev) */ if (err_handler && err_handler->reset_done) err_handler->reset_done(dev); + else if (dev->driver) + pci_warn(dev, "reset done"); } /* dev->reset_methods[] is a 0-terminated list of indices into this array */ @@ -5248,7 +5254,7 @@ static ssize_t reset_method_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - char *options, *name; + char *options, *tmp_options, *name; int m, n; u8 reset_methods[PCI_NUM_RESET_METHODS] = { 0 }; @@ -5268,7 +5274,8 @@ static ssize_t reset_method_store(struct device *dev, return -ENOMEM; n = 0; - while ((name = strsep(&options, " ")) != NULL) { + tmp_options = options; + while ((name = strsep(&tmp_options, " ")) != NULL) { if (sysfs_streq(name, "")) continue; @@ -5884,7 +5891,7 @@ EXPORT_SYMBOL_GPL(pci_probe_reset_bus); * * Same as above except return -EAGAIN if the bus cannot be locked */ -static int __pci_reset_bus(struct pci_bus *bus) +int __pci_reset_bus(struct pci_bus *bus) { int rc; @@ -6193,38 +6200,64 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev, EXPORT_SYMBOL(pcie_bandwidth_available); /** - * pcie_get_speed_cap - query for the PCI device's link speed capability + * pcie_get_supported_speeds - query Supported Link Speed Vector * @dev: PCI device to query * - * Query the PCI device speed capability. Return the maximum link speed - * supported by the device. + * Query @dev supported link speeds. + * + * Implementation Note in PCIe r6.0 sec 7.5.3.18 recommends determining + * supported link speeds using the Supported Link Speeds Vector in the Link + * Capabilities 2 Register (when available). + * + * Link Capabilities 2 was added in PCIe r3.0, sec 7.8.18. + * + * Without Link Capabilities 2, i.e., prior to PCIe r3.0, Supported Link + * Speeds field in Link Capabilities is used and only 2.5 GT/s and 5.0 GT/s + * speeds were defined. + * + * For @dev without Supported Link Speed Vector, the field is synthesized + * from the Max Link Speed field in the Link Capabilities Register. + * + * Return: Supported Link Speeds Vector (+ reserved 0 at LSB). */ -enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev) +u8 pcie_get_supported_speeds(struct pci_dev *dev) { u32 lnkcap2, lnkcap; + u8 speeds; /* - * Link Capabilities 2 was added in PCIe r3.0, sec 7.8.18. The - * implementation note there recommends using the Supported Link - * Speeds Vector in Link Capabilities 2 when supported. - * - * Without Link Capabilities 2, i.e., prior to PCIe r3.0, software - * should use the Supported Link Speeds field in Link Capabilities, - * where only 2.5 GT/s and 5.0 GT/s speeds were defined. + * Speeds retain the reserved 0 at LSB before PCIe Supported Link + * Speeds Vector to allow using SLS Vector bit defines directly. */ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2); + speeds = lnkcap2 & PCI_EXP_LNKCAP2_SLS; /* PCIe r3.0-compliant */ - if (lnkcap2) - return PCIE_LNKCAP2_SLS2SPEED(lnkcap2); + if (speeds) + return speeds; pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + + /* Synthesize from the Max Link Speed field */ if ((lnkcap & PCI_EXP_LNKCAP_SLS) == PCI_EXP_LNKCAP_SLS_5_0GB) - return PCIE_SPEED_5_0GT; + speeds = PCI_EXP_LNKCAP2_SLS_5_0GB | PCI_EXP_LNKCAP2_SLS_2_5GB; else if ((lnkcap & PCI_EXP_LNKCAP_SLS) == PCI_EXP_LNKCAP_SLS_2_5GB) - return PCIE_SPEED_2_5GT; + speeds = PCI_EXP_LNKCAP2_SLS_2_5GB; - return PCI_SPEED_UNKNOWN; + return speeds; +} + +/** + * pcie_get_speed_cap - query for the PCI device's link speed capability + * @dev: PCI device to query + * + * Query the PCI device speed capability. + * + * Return: the maximum link speed supported by the device. + */ +enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev) +{ + return PCIE_LNKCAP2_SLS2SPEED(dev->supported_speeds); } EXPORT_SYMBOL(pcie_get_speed_cap); @@ -6653,8 +6686,7 @@ static void pci_request_resource_alignment(struct pci_dev *dev, int bar, } else { r->flags &= ~IORESOURCE_SIZEALIGN; r->flags |= IORESOURCE_STARTALIGN; - r->start = align; - r->end = r->start + size - 1; + resource_set_range(r, align, size); } r->flags |= IORESOURCE_UNSET; } @@ -6900,6 +6932,8 @@ static int __init pci_setup(char *str) pci_no_domains(); } else if (!strncmp(str, "noari", 5)) { pcie_ari_disabled = true; + } else if (!strncmp(str, "notph", 5)) { + pci_no_tph(); } else if (!strncmp(str, "cbiosize=", 9)) { pci_cardbus_io_size = memparse(str + 9, &str); } else if (!strncmp(str, "cbmemsize=", 10)) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 14d00ce45bfa9..2e40fc63ba315 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -104,6 +104,7 @@ bool pci_reset_supported(struct pci_dev *dev); void pci_init_reset_methods(struct pci_dev *dev); int pci_bridge_secondary_bus_reset(struct pci_dev *dev); int pci_bus_error_reset(struct pci_dev *dev); +int __pci_reset_bus(struct pci_bus *bus); struct pci_cap_saved_data { u16 cap_nr; @@ -323,6 +324,9 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, struct list_head *realloc_head, struct list_head *fail_head); bool pci_bus_clip_resource(struct pci_dev *dev, int idx); +void pci_walk_bus_locked(struct pci_bus *top, + int (*cb)(struct pci_dev *, void *), + void *userdata); const char *pci_resource_name(struct pci_dev *dev, unsigned int i); @@ -331,6 +335,17 @@ void pci_disable_bridge_window(struct pci_dev *dev); struct pci_bus *pci_bus_get(struct pci_bus *bus); void pci_bus_put(struct pci_bus *bus); +#define PCIE_LNKCAP_SLS2SPEED(lnkcap) \ +({ \ + ((lnkcap) == PCI_EXP_LNKCAP_SLS_64_0GB ? PCIE_SPEED_64_0GT : \ + (lnkcap) == PCI_EXP_LNKCAP_SLS_32_0GB ? PCIE_SPEED_32_0GT : \ + (lnkcap) == PCI_EXP_LNKCAP_SLS_16_0GB ? PCIE_SPEED_16_0GT : \ + (lnkcap) == PCI_EXP_LNKCAP_SLS_8_0GB ? PCIE_SPEED_8_0GT : \ + (lnkcap) == PCI_EXP_LNKCAP_SLS_5_0GB ? PCIE_SPEED_5_0GT : \ + (lnkcap) == PCI_EXP_LNKCAP_SLS_2_5GB ? PCIE_SPEED_2_5GT : \ + PCI_SPEED_UNKNOWN); \ +}) + /* PCIe link information from Link Capabilities 2 */ #define PCIE_LNKCAP2_SLS2SPEED(lnkcap2) \ ((lnkcap2) & PCI_EXP_LNKCAP2_SLS_64_0GB ? PCIE_SPEED_64_0GT : \ @@ -341,6 +356,15 @@ void pci_bus_put(struct pci_bus *bus); (lnkcap2) & PCI_EXP_LNKCAP2_SLS_2_5GB ? PCIE_SPEED_2_5GT : \ PCI_SPEED_UNKNOWN) +#define PCIE_LNKCTL2_TLS2SPEED(lnkctl2) \ + ((lnkctl2) == PCI_EXP_LNKCTL2_TLS_64_0GT ? PCIE_SPEED_64_0GT : \ + (lnkctl2) == PCI_EXP_LNKCTL2_TLS_32_0GT ? PCIE_SPEED_32_0GT : \ + (lnkctl2) == PCI_EXP_LNKCTL2_TLS_16_0GT ? PCIE_SPEED_16_0GT : \ + (lnkctl2) == PCI_EXP_LNKCTL2_TLS_8_0GT ? PCIE_SPEED_8_0GT : \ + (lnkctl2) == PCI_EXP_LNKCTL2_TLS_5_0GT ? PCIE_SPEED_5_0GT : \ + (lnkctl2) == PCI_EXP_LNKCTL2_TLS_2_5GT ? PCIE_SPEED_2_5GT : \ + PCI_SPEED_UNKNOWN) + /* PCIe speed to Mb/s reduced by encoding overhead */ #define PCIE_SPEED2MBS_ENC(speed) \ ((speed) == PCIE_SPEED_64_0GT ? 64000*1/1 : \ @@ -373,12 +397,16 @@ static inline int pcie_dev_speed_mbps(enum pci_bus_speed speed) return -EINVAL; } +u8 pcie_get_supported_speeds(struct pci_dev *dev); const char *pci_speed_string(enum pci_bus_speed speed); -enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev); -enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev); void __pcie_print_link_status(struct pci_dev *dev, bool verbose); void pcie_report_downtraining(struct pci_dev *dev); -void pcie_update_link_speed(struct pci_bus *bus, u16 link_status); + +static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta) +{ + bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS]; +} +void pcie_update_link_speed(struct pci_bus *bus); /* Single Root I/O Virtualization */ struct pci_sriov { @@ -469,10 +497,18 @@ static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused) #define PCI_DEV_ADDED 0 #define PCI_DPC_RECOVERED 1 #define PCI_DPC_RECOVERING 2 +#define PCI_DEV_REMOVED 3 -static inline void pci_dev_assign_added(struct pci_dev *dev, bool added) +static inline void pci_dev_assign_added(struct pci_dev *dev) { - assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added); + smp_mb__before_atomic(); + set_bit(PCI_DEV_ADDED, &dev->priv_flags); + smp_mb__after_atomic(); +} + +static inline bool pci_dev_test_and_clear_added(struct pci_dev *dev) +{ + return test_and_clear_bit(PCI_DEV_ADDED, &dev->priv_flags); } static inline bool pci_dev_is_added(const struct pci_dev *dev) @@ -480,6 +516,11 @@ static inline bool pci_dev_is_added(const struct pci_dev *dev) return test_bit(PCI_DEV_ADDED, &dev->priv_flags); } +static inline bool pci_dev_test_and_set_removed(struct pci_dev *dev) +{ + return test_and_set_bit(PCI_DEV_REMOVED, &dev->priv_flags); +} + #ifdef CONFIG_PCIEAER #include @@ -597,6 +638,18 @@ static inline int pci_iov_bus_range(struct pci_bus *bus) #endif /* CONFIG_PCI_IOV */ +#ifdef CONFIG_PCIE_TPH +void pci_restore_tph_state(struct pci_dev *dev); +void pci_save_tph_state(struct pci_dev *dev); +void pci_no_tph(void); +void pci_tph_init(struct pci_dev *dev); +#else +static inline void pci_restore_tph_state(struct pci_dev *dev) { } +static inline void pci_save_tph_state(struct pci_dev *dev) { } +static inline void pci_no_tph(void) { } +static inline void pci_tph_init(struct pci_dev *dev) { } +#endif + #ifdef CONFIG_PCIE_PTM void pci_ptm_init(struct pci_dev *dev); void pci_save_ptm_state(struct pci_dev *dev); @@ -692,6 +745,17 @@ static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { } static inline void pcie_ecrc_get_policy(char *str) { } #endif +#ifdef CONFIG_PCIEPORTBUS +void pcie_reset_lbms_count(struct pci_dev *port); +int pcie_lbms_count(struct pci_dev *port, unsigned long *val); +#else +static inline void pcie_reset_lbms_count(struct pci_dev *port) {} +static inline int pcie_lbms_count(struct pci_dev *port, unsigned long *val) +{ + return -EOPNOTSUPP; +} +#endif + struct pci_dev_reset_methods { u16 vendor; u16 device; @@ -746,6 +810,7 @@ void pci_set_bus_of_node(struct pci_bus *bus); void pci_release_bus_of_node(struct pci_bus *bus); int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge); +bool of_pci_supply_present(struct device_node *np); #else static inline int @@ -793,6 +858,10 @@ static inline int devm_of_pci_bridge_init(struct device *dev, struct pci_host_br return 0; } +static inline bool of_pci_supply_present(struct device_node *np) +{ + return false; +} #endif /* CONFIG_OF */ struct of_changeset; diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 6461aa93fe76e..53ccab62314d9 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -4,7 +4,7 @@ pcieportdrv-y := portdrv.o rcec.o -obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o +obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o bwctrl.o obj-y += aspm.o obj-$(CONFIG_PCIEAER) += aer.o err.o diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 13b8586924ead..80c5ba8d82962 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -180,7 +180,8 @@ static int disable_ecrc_checking(struct pci_dev *dev) } /** - * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy + * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based + * on global policy * @dev: the PCI device */ void pcie_set_ecrc_checking(struct pci_dev *dev) @@ -1148,14 +1149,16 @@ static void aer_recover_work_func(struct work_struct *work) continue; } pci_print_aer(pdev, entry.severity, entry.regs); + /* - * Memory for aer_capability_regs(entry.regs) is being allocated from the - * ghes_estatus_pool to protect it from overwriting when multiple sections - * are present in the error status. Thus free the same after processing - * the data. + * Memory for aer_capability_regs(entry.regs) is being + * allocated from the ghes_estatus_pool to protect it from + * overwriting when multiple sections are present in the + * error status. Thus free the same after processing the + * data. */ ghes_estatus_pool_region_free((unsigned long)entry.regs, - sizeof(struct aer_capability_regs)); + sizeof(struct aer_capability_regs)); if (entry.severity == AER_NONFATAL) pcie_do_recovery(pdev, pci_channel_io_normal, diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index cee2365e54b8b..28567d457613b 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -805,6 +805,15 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl); pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl); + /* Disable L0s/L1 before updating L1SS config */ + if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || + FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { + pcie_capability_write_word(child, PCI_EXP_LNKCTL, + child_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); + pcie_capability_write_word(parent, PCI_EXP_LNKCTL, + parent_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); + } + /* * Setup L0s state * @@ -829,6 +838,13 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) aspm_l1ss_init(link); + /* Restore L0s/L1 if they were enabled */ + if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || + FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { + pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_lnkctl); + pcie_capability_write_word(child, PCI_EXP_LNKCTL, child_lnkctl); + } + /* Save default state */ link->aspm_default = link->aspm_enabled; @@ -845,25 +861,28 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } -/* Configure the ASPM L1 substates */ +/* Configure the ASPM L1 substates. Caller must disable L1 first. */ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) { - u32 val, enable_req; + u32 val; struct pci_dev *child = link->downstream, *parent = link->pdev; - enable_req = (link->aspm_enabled ^ state) & state; + val = 0; + if (state & PCIE_LINK_STATE_L1_1) + val |= PCI_L1SS_CTL1_ASPM_L1_1; + if (state & PCIE_LINK_STATE_L1_2) + val |= PCI_L1SS_CTL1_ASPM_L1_2; + if (state & PCIE_LINK_STATE_L1_1_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_1; + if (state & PCIE_LINK_STATE_L1_2_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_2; /* - * Here are the rules specified in the PCIe spec for enabling L1SS: - * - When enabling L1.x, enable bit at parent first, then at child - * - When disabling L1.x, disable bit at child first, then at parent - * - When enabling ASPM L1.x, need to disable L1 - * (at child followed by parent). - * - The ASPM/PCIPM L1.2 must be disabled while programming timing + * PCIe r6.2, sec 5.5.4, rules for enabling L1 PM Substates: + * - Clear L1.x enable bits at child first, then at parent + * - Set L1.x enable bits at parent first, then at child + * - ASPM/PCIPM L1.2 must be disabled while programming timing * parameters - * - * To keep it simple, disable all L1SS bits first, and later enable - * what is needed. */ /* Disable all L1 substates */ @@ -871,26 +890,6 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) PCI_L1SS_CTL1_L1SS_MASK, 0); pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, PCI_L1SS_CTL1_L1SS_MASK, 0); - /* - * If needed, disable L1, and it gets enabled later - * in pcie_config_aspm_link(). - */ - if (enable_req & (PCIE_LINK_STATE_L1_1 | PCIE_LINK_STATE_L1_2)) { - pcie_capability_clear_word(child, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPM_L1); - pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPM_L1); - } - - val = 0; - if (state & PCIE_LINK_STATE_L1_1) - val |= PCI_L1SS_CTL1_ASPM_L1_1; - if (state & PCIE_LINK_STATE_L1_2) - val |= PCI_L1SS_CTL1_ASPM_L1_2; - if (state & PCIE_LINK_STATE_L1_1_PCIPM) - val |= PCI_L1SS_CTL1_PCIPM_L1_1; - if (state & PCIE_LINK_STATE_L1_2_PCIPM) - val |= PCI_L1SS_CTL1_PCIPM_L1_2; /* Enable what we need to enable */ pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, @@ -937,21 +936,30 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) dwstream |= PCI_EXP_LNKCTL_ASPM_L1; } + /* + * Per PCIe r6.2, sec 5.5.4, setting either or both of the enable + * bits for ASPM L1 PM Substates must be done while ASPM L1 is + * disabled. Disable L1 here and apply new configuration after L1SS + * configuration has been completed. + * + * Per sec 7.5.3.7, when disabling ASPM L1, software must disable + * it in the Downstream component prior to disabling it in the + * Upstream component, and ASPM L1 must be enabled in the Upstream + * component prior to enabling it in the Downstream component. + * + * Sec 7.5.3.7 also recommends programming the same ASPM Control + * value for all functions of a multi-function device. + */ + list_for_each_entry(child, &linkbus->devices, bus_list) + pcie_config_aspm_dev(child, 0); + pcie_config_aspm_dev(parent, 0); + if (link->aspm_capable & PCIE_LINK_STATE_L1SS) pcie_config_aspm_l1ss(link, state); - /* - * Spec 2.0 suggests all functions should be configured the - * same setting for ASPM. Enabling ASPM L1 should be done in - * upstream component first and then downstream, and vice - * versa for disabling ASPM L1. Spec doesn't mention L0S. - */ - if (state & PCIE_LINK_STATE_L1) - pcie_config_aspm_dev(parent, upstream); + pcie_config_aspm_dev(parent, upstream); list_for_each_entry(child, &linkbus->devices, bus_list) pcie_config_aspm_dev(child, dwstream); - if (!(state & PCIE_LINK_STATE_L1)) - pcie_config_aspm_dev(parent, upstream); link->aspm_enabled = state; @@ -1442,6 +1450,9 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) * touch the LNKCTL register. Also note that this does not enable states * disabled by pci_disable_link_state(). Return 0 or a negative errno. * + * Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per + * PCIe r6.0, sec 5.5.4. + * * @pdev: PCI device * @state: Mask of ASPM link states to enable */ @@ -1458,6 +1469,9 @@ EXPORT_SYMBOL(pci_enable_link_state); * can't touch the LNKCTL register. Also note that this does not enable states * disabled by pci_disable_link_state(). Return 0 or a negative errno. * + * Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per + * PCIe r6.0, sec 5.5.4. + * * @pdev: PCI device * @state: Mask of ASPM link states to enable * diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c new file mode 100644 index 0000000000000..b59cacc740fa2 --- /dev/null +++ b/drivers/pci/pcie/bwctrl.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe bandwidth controller + * + * Author: Alexandru Gagniuc + * + * Copyright (C) 2019 Dell Inc + * Copyright (C) 2023-2024 Intel Corporation + * + * The PCIe bandwidth controller provides a way to alter PCIe Link Speeds + * and notify the operating system when the Link Width or Speed changes. The + * notification capability is required for all Root Ports and Downstream + * Ports supporting Link Width wider than x1 and/or multiple Link Speeds. + * + * This service port driver hooks into the Bandwidth Notification interrupt + * watching for changes or links becoming degraded in operation. It updates + * the cached Current Link Speed that is exposed to user space through sysfs. + */ + +#define dev_fmt(fmt) "bwctrl: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "portdrv.h" + +/** + * struct pcie_bwctrl_data - PCIe bandwidth controller + * @set_speed_mutex: Serializes link speed changes + * @lbms_count: Count for LBMS (since last reset) + * @cdev: Thermal cooling device associated with the port + */ +struct pcie_bwctrl_data { + struct mutex set_speed_mutex; + atomic_t lbms_count; + struct thermal_cooling_device *cdev; +}; + +/* + * Prevent port removal during LBMS count accessors and Link Speed changes. + * + * These have to be differentiated because pcie_bwctrl_change_speed() calls + * pcie_retrain_link() which uses LBMS count reset accessor on success + * (using just one rwsem triggers "possible recursive locking detected" + * warning). + */ +static DECLARE_RWSEM(pcie_bwctrl_lbms_rwsem); +static DECLARE_RWSEM(pcie_bwctrl_setspeed_rwsem); + +static bool pcie_valid_speed(enum pci_bus_speed speed) +{ + return (speed >= PCIE_SPEED_2_5GT) && (speed <= PCIE_SPEED_64_0GT); +} + +static u16 pci_bus_speed2lnkctl2(enum pci_bus_speed speed) +{ + static const u8 speed_conv[] = { + [PCIE_SPEED_2_5GT] = PCI_EXP_LNKCTL2_TLS_2_5GT, + [PCIE_SPEED_5_0GT] = PCI_EXP_LNKCTL2_TLS_5_0GT, + [PCIE_SPEED_8_0GT] = PCI_EXP_LNKCTL2_TLS_8_0GT, + [PCIE_SPEED_16_0GT] = PCI_EXP_LNKCTL2_TLS_16_0GT, + [PCIE_SPEED_32_0GT] = PCI_EXP_LNKCTL2_TLS_32_0GT, + [PCIE_SPEED_64_0GT] = PCI_EXP_LNKCTL2_TLS_64_0GT, + }; + + if (WARN_ON_ONCE(!pcie_valid_speed(speed))) + return 0; + + return speed_conv[speed]; +} + +static inline u16 pcie_supported_speeds2target_speed(u8 supported_speeds) +{ + return __fls(supported_speeds); +} + +/** + * pcie_bwctrl_select_speed - Select Target Link Speed + * @port: PCIe Port + * @speed_req: Requested PCIe Link Speed + * + * Select Target Link Speed by take into account Supported Link Speeds of + * both the Root Port and the Endpoint. + * + * Return: Target Link Speed (1=2.5GT/s, 2=5GT/s, 3=8GT/s, etc.) + */ +static u16 pcie_bwctrl_select_speed(struct pci_dev *port, enum pci_bus_speed speed_req) +{ + struct pci_bus *bus = port->subordinate; + u8 desired_speeds, supported_speeds; + struct pci_dev *dev; + + desired_speeds = GENMASK(pci_bus_speed2lnkctl2(speed_req), + __fls(PCI_EXP_LNKCAP2_SLS_2_5GB)); + + supported_speeds = port->supported_speeds; + if (bus) { + down_read(&pci_bus_sem); + dev = list_first_entry_or_null(&bus->devices, struct pci_dev, bus_list); + if (dev) + supported_speeds &= dev->supported_speeds; + up_read(&pci_bus_sem); + } + if (!supported_speeds) + return PCI_EXP_LNKCAP2_SLS_2_5GB; + + return pcie_supported_speeds2target_speed(supported_speeds & desired_speeds); +} + +static int pcie_bwctrl_change_speed(struct pci_dev *port, u16 target_speed, bool use_lt) +{ + int ret; + + ret = pcie_capability_clear_and_set_word(port, PCI_EXP_LNKCTL2, + PCI_EXP_LNKCTL2_TLS, target_speed); + if (ret != PCIBIOS_SUCCESSFUL) + return pcibios_err_to_errno(ret); + + ret = pcie_retrain_link(port, use_lt); + if (ret < 0) + return ret; + + /* + * Ensure link speed updates also with platforms that have problems + * with notifications. + */ + if (port->subordinate) + pcie_update_link_speed(port->subordinate); + + return 0; +} + +/** + * pcie_set_target_speed - Set downstream Link Speed for PCIe Port + * @port: PCIe Port + * @speed_req: Requested PCIe Link Speed + * @use_lt: Wait for the LT or DLLLA bit to detect the end of link training + * + * Attempt to set PCIe Port Link Speed to @speed_req. @speed_req may be + * adjusted downwards to the best speed supported by both the Port and PCIe + * Device underneath it. + * + * Return: + * * 0 - on success + * * -EINVAL - @speed_req is not a PCIe Link Speed + * * -ENODEV - @port is not controllable + * * -ETIMEDOUT - changing Link Speed took too long + * * -EAGAIN - Link Speed was changed but @speed_req was not achieved + */ +int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req, + bool use_lt) +{ + struct pci_bus *bus = port->subordinate; + u16 target_speed; + int ret; + + if (WARN_ON_ONCE(!pcie_valid_speed(speed_req))) + return -EINVAL; + + if (bus && bus->cur_bus_speed == speed_req) + return 0; + + target_speed = pcie_bwctrl_select_speed(port, speed_req); + + scoped_guard(rwsem_read, &pcie_bwctrl_setspeed_rwsem) { + struct pcie_bwctrl_data *data = port->link_bwctrl; + + /* + * port->link_bwctrl is NULL during initial scan when called + * e.g. from the Target Speed quirk. + */ + if (data) + mutex_lock(&data->set_speed_mutex); + + ret = pcie_bwctrl_change_speed(port, target_speed, use_lt); + + if (data) + mutex_unlock(&data->set_speed_mutex); + } + + /* + * Despite setting higher speed into the Target Link Speed, empty + * bus won't train to 5GT+ speeds. + */ + if (!ret && bus && bus->cur_bus_speed != speed_req && + !list_empty(&bus->devices)) + ret = -EAGAIN; + + return ret; +} + +static void pcie_bwnotif_enable(struct pcie_device *srv) +{ + struct pcie_bwctrl_data *data = srv->port->link_bwctrl; + struct pci_dev *port = srv->port; + u16 link_status; + int ret; + + /* Count LBMS seen so far as one */ + ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status); + if (ret == PCIBIOS_SUCCESSFUL && link_status & PCI_EXP_LNKSTA_LBMS) + atomic_inc(&data->lbms_count); + + pcie_capability_set_word(port, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE); + pcie_capability_write_word(port, PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS); + + /* + * Update after enabling notifications & clearing status bits ensures + * link speed is up to date. + */ + pcie_update_link_speed(port->subordinate); +} + +static void pcie_bwnotif_disable(struct pci_dev *port) +{ + pcie_capability_clear_word(port, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE); +} + +static irqreturn_t pcie_bwnotif_irq(int irq, void *context) +{ + struct pcie_device *srv = context; + struct pcie_bwctrl_data *data = srv->port->link_bwctrl; + struct pci_dev *port = srv->port; + u16 link_status, events; + int ret; + + ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status); + if (ret != PCIBIOS_SUCCESSFUL) + return IRQ_NONE; + + events = link_status & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS); + if (!events) + return IRQ_NONE; + + if (events & PCI_EXP_LNKSTA_LBMS) + atomic_inc(&data->lbms_count); + + pcie_capability_write_word(port, PCI_EXP_LNKSTA, events); + + /* + * Interrupts will not be triggered from any further Link Speed + * change until LBMS is cleared by the write. Therefore, re-read the + * speed (inside pcie_update_link_speed()) after LBMS has been + * cleared to avoid missing link speed changes. + */ + pcie_update_link_speed(port->subordinate); + + return IRQ_HANDLED; +} + +void pcie_reset_lbms_count(struct pci_dev *port) +{ + struct pcie_bwctrl_data *data; + + guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem); + data = port->link_bwctrl; + if (data) + atomic_set(&data->lbms_count, 0); + else + pcie_capability_write_word(port, PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_LBMS); +} + +int pcie_lbms_count(struct pci_dev *port, unsigned long *val) +{ + struct pcie_bwctrl_data *data; + + guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem); + data = port->link_bwctrl; + if (!data) + return -ENOTTY; + + *val = atomic_read(&data->lbms_count); + + return 0; +} + +static int pcie_bwnotif_probe(struct pcie_device *srv) +{ + struct pci_dev *port = srv->port; + int ret; + + struct pcie_bwctrl_data *data = devm_kzalloc(&srv->device, + sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = devm_mutex_init(&srv->device, &data->set_speed_mutex); + if (ret) + return ret; + + ret = devm_request_irq(&srv->device, srv->irq, pcie_bwnotif_irq, + IRQF_SHARED, "PCIe bwctrl", srv); + if (ret) + return ret; + + scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) { + scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) { + port->link_bwctrl = no_free_ptr(data); + pcie_bwnotif_enable(srv); + } + } + + pci_dbg(port, "enabled with IRQ %d\n", srv->irq); + + /* Don't fail on errors. Don't leave IS_ERR() "pointer" into ->cdev */ + port->link_bwctrl->cdev = pcie_cooling_device_register(port); + if (IS_ERR(port->link_bwctrl->cdev)) + port->link_bwctrl->cdev = NULL; + + return 0; +} + +static void pcie_bwnotif_remove(struct pcie_device *srv) +{ + struct pcie_bwctrl_data *data = srv->port->link_bwctrl; + + pcie_cooling_device_unregister(data->cdev); + + pcie_bwnotif_disable(srv->port); + + scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) + scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) + srv->port->link_bwctrl = NULL; +} + +static int pcie_bwnotif_suspend(struct pcie_device *srv) +{ + pcie_bwnotif_disable(srv->port); + return 0; +} + +static int pcie_bwnotif_resume(struct pcie_device *srv) +{ + pcie_bwnotif_enable(srv); + return 0; +} + +static struct pcie_port_service_driver pcie_bwctrl_driver = { + .name = "pcie_bwctrl", + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_BWCTRL, + .probe = pcie_bwnotif_probe, + .suspend = pcie_bwnotif_suspend, + .resume = pcie_bwnotif_resume, + .remove = pcie_bwnotif_remove, +}; + +int __init pcie_bwctrl_init(void) +{ + return pcie_port_service_register(&pcie_bwctrl_driver); +} diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 6af5e04258728..5e10306b63081 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -68,7 +68,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask, */ if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | - PCIE_PORT_SERVICE_BWNOTIF)) { + PCIE_PORT_SERVICE_BWCTRL)) { pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); *pme = FIELD_GET(PCI_EXP_FLAGS_IRQ, reg16); nvec = *pme + 1; @@ -150,11 +150,11 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */ if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | - PCIE_PORT_SERVICE_BWNOTIF)) { + PCIE_PORT_SERVICE_BWCTRL)) { pcie_irq = pci_irq_vector(dev, pme); irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq; irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq; - irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq; + irqs[PCIE_PORT_SERVICE_BWCTRL_SHIFT] = pcie_irq; } if (mask & PCIE_PORT_SERVICE_AER) @@ -271,7 +271,7 @@ static int get_port_device_capability(struct pci_dev *dev) pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &linkcap); if (linkcap & PCI_EXP_LNKCAP_LBNC) - services |= PCIE_PORT_SERVICE_BWNOTIF; + services |= PCIE_PORT_SERVICE_BWCTRL; } return services; @@ -828,6 +828,7 @@ static void __init pcie_init_services(void) pcie_aer_init(); pcie_pme_init(); pcie_dpc_init(); + pcie_bwctrl_init(); pcie_hp_init(); } diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 12c89ea0313b9..bd29d1cc7b8bd 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -20,8 +20,8 @@ #define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) #define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */ #define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) -#define PCIE_PORT_SERVICE_BWNOTIF_SHIFT 4 /* Bandwidth notification */ -#define PCIE_PORT_SERVICE_BWNOTIF (1 << PCIE_PORT_SERVICE_BWNOTIF_SHIFT) +#define PCIE_PORT_SERVICE_BWCTRL_SHIFT 4 /* Bandwidth Controller (notifications) */ +#define PCIE_PORT_SERVICE_BWCTRL (1 << PCIE_PORT_SERVICE_BWCTRL_SHIFT) #define PCIE_PORT_DEVICE_MAXSERVICES 5 @@ -51,6 +51,8 @@ int pcie_dpc_init(void); static inline int pcie_dpc_init(void) { return 0; } #endif +int pcie_bwctrl_init(void); + /* Port Type */ #define PCIE_ANY_PORT (~0) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f1615805f5b07..2e81ab0f5a25c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -543,15 +543,15 @@ void pci_read_bridge_bases(struct pci_bus *child) pci_read_bridge_mmio(child->self, child->resource[1], false); pci_read_bridge_mmio_pref(child->self, child->resource[2], false); - if (dev->transparent) { - pci_bus_for_each_resource(child->parent, res) { - if (res && res->flags) { - pci_bus_add_resource(child, res, - PCI_SUBTRACTIVE_DECODE); - pci_info(dev, " bridge window %pR (subtractive decode)\n", - res); - } - } + if (!dev->transparent) + return; + + pci_bus_for_each_resource(child->parent, res) { + if (!res || !res->flags) + continue; + + pci_bus_add_resource(child, res); + pci_info(dev, " bridge window %pR (subtractive decode)\n", res); } } @@ -742,9 +742,13 @@ const char *pci_speed_string(enum pci_bus_speed speed) } EXPORT_SYMBOL_GPL(pci_speed_string); -void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) +void pcie_update_link_speed(struct pci_bus *bus) { - bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS]; + struct pci_dev *bridge = bus->self; + u16 linksta; + + pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); + __pcie_update_link_speed(bus, linksta); } EXPORT_SYMBOL_GPL(pcie_update_link_speed); @@ -827,13 +831,11 @@ static void pci_set_bus_speed(struct pci_bus *bus) if (pci_is_pcie(bridge)) { u32 linkcap; - u16 linksta; pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; - pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); - pcie_update_link_speed(bus, linksta); + pcie_update_link_speed(bus); } } @@ -1032,7 +1034,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) if (res->flags & IORESOURCE_BUS) pci_bus_insert_busn_res(bus, bus->number, res->end); else - pci_bus_add_resource(bus, res, 0); + pci_bus_add_resource(bus, res); if (offset) { if (resource_type(res) == IORESOURCE_IO) @@ -1633,23 +1635,33 @@ static void set_pcie_thunderbolt(struct pci_dev *dev) static void set_pcie_untrusted(struct pci_dev *dev) { - struct pci_dev *parent; + struct pci_dev *parent = pci_upstream_bridge(dev); + if (!parent) + return; /* - * If the upstream bridge is untrusted we treat this device + * If the upstream bridge is untrusted we treat this device as * untrusted as well. */ - parent = pci_upstream_bridge(dev); - if (parent && (parent->untrusted || parent->external_facing)) + if (parent->untrusted) { + dev->untrusted = true; + return; + } + + if (arch_pci_dev_is_removable(dev)) { + pci_dbg(dev, "marking as untrusted\n"); dev->untrusted = true; + } } static void pci_set_removable(struct pci_dev *dev) { struct pci_dev *parent = pci_upstream_bridge(dev); + if (!parent) + return; /* - * We (only) consider everything downstream from an external_facing + * We (only) consider everything tunneled below an external_facing * device to be removable by the user. We're mainly concerned with * consumer platforms with user accessible thunderbolt ports that are * vulnerable to DMA attacks, and we expect those ports to be marked by @@ -1659,9 +1671,15 @@ static void pci_set_removable(struct pci_dev *dev) * accessible to user / may not be removed by end user, and thus not * exposed as "removable" to userspace. */ - if (parent && - (parent->external_facing || dev_is_removable(&parent->dev))) + if (dev_is_removable(&parent->dev)) { + dev_set_removable(&dev->dev, DEVICE_REMOVABLE); + return; + } + + if (arch_pci_dev_is_removable(dev)) { + pci_dbg(dev, "marking as removable\n"); dev_set_removable(&dev->dev, DEVICE_REMOVABLE); + } } /** @@ -1947,6 +1965,9 @@ int pci_setup_device(struct pci_dev *dev) set_pcie_untrusted(dev); + if (pci_is_pcie(dev)) + dev->supported_speeds = pcie_get_supported_speeds(dev); + /* "Unknown power state" */ dev->current_state = PCI_UNKNOWN; @@ -2495,6 +2516,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_dpc_init(dev); /* Downstream Port Containment */ pci_rcec_init(dev); /* Root Complex Event Collector */ pci_doe_init(dev); /* Data Object Exchange */ + pci_tph_init(dev); /* TLP Processing Hints */ pcie_report_downtraining(dev); pci_init_reset_methods(dev); @@ -3108,6 +3130,17 @@ int pci_host_probe(struct pci_host_bridge *bridge) pci_lock_rescan_remove(); pci_bus_add_devices(bus); pci_unlock_rescan_remove(); + + /* + * Ensure pm_runtime_enable() is called for the controller drivers + * before calling pci_host_probe(). The PM framework expects that + * if the parent device supports runtime PM, it will be enabled + * before child runtime PM is enabled. + */ + pm_runtime_set_active(&bridge->dev); + pm_runtime_no_callbacks(&bridge->dev); + devm_pm_runtime_enable(&bridge->dev); + return 0; } EXPORT_SYMBOL_GPL(pci_host_probe); diff --git a/drivers/pci/pwrctl/Makefile b/drivers/pci/pwrctl/Makefile deleted file mode 100644 index d308aae4800cd..0000000000000 --- a/drivers/pci/pwrctl/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctl-core.o -pci-pwrctl-core-y := core.o - -obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctl-pwrseq.o diff --git a/drivers/pci/pwrctl/core.c b/drivers/pci/pwrctl/core.c deleted file mode 100644 index 01d913b603160..0000000000000 --- a/drivers/pci/pwrctl/core.c +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2024 Linaro Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include - -static int pci_pwrctl_notify(struct notifier_block *nb, unsigned long action, - void *data) -{ - struct pci_pwrctl *pwrctl = container_of(nb, struct pci_pwrctl, nb); - struct device *dev = data; - - if (dev_fwnode(dev) != dev_fwnode(pwrctl->dev)) - return NOTIFY_DONE; - - switch (action) { - case BUS_NOTIFY_ADD_DEVICE: - /* - * We will have two struct device objects bound to two different - * drivers on different buses but consuming the same DT node. We - * must not bind the pins twice in this case but only once for - * the first device to be added. - * - * If we got here then the PCI device is the second after the - * power control platform device. Mark its OF node as reused. - */ - dev->of_node_reused = true; - break; - case BUS_NOTIFY_BOUND_DRIVER: - pwrctl->link = device_link_add(dev, pwrctl->dev, - DL_FLAG_AUTOREMOVE_CONSUMER); - if (!pwrctl->link) - dev_err(pwrctl->dev, "Failed to add device link\n"); - break; - case BUS_NOTIFY_UNBOUND_DRIVER: - if (pwrctl->link) - device_link_remove(dev, pwrctl->dev); - break; - } - - return NOTIFY_DONE; -} - -static void rescan_work_func(struct work_struct *work) -{ - struct pci_pwrctl *pwrctl = container_of(work, struct pci_pwrctl, work); - - pci_lock_rescan_remove(); - pci_rescan_bus(to_pci_dev(pwrctl->dev->parent)->bus); - pci_unlock_rescan_remove(); -} - -/** - * pci_pwrctl_init() - Initialize the PCI power control context struct - * - * @pwrctl: PCI power control data - * @dev: Parent device - */ -void pci_pwrctl_init(struct pci_pwrctl *pwrctl, struct device *dev) -{ - pwrctl->dev = dev; - INIT_WORK(&pwrctl->work, rescan_work_func); -} -EXPORT_SYMBOL_GPL(pci_pwrctl_init); - -/** - * pci_pwrctl_device_set_ready() - Notify the pwrctl subsystem that the PCI - * device is powered-up and ready to be detected. - * - * @pwrctl: PCI power control data. - * - * Returns: - * 0 on success, negative error number on error. - * - * Note: - * This function returning 0 doesn't mean the device was detected. It means, - * that the bus rescan was successfully started. The device will get bound to - * its PCI driver asynchronously. - */ -int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl) -{ - int ret; - - if (!pwrctl->dev) - return -ENODEV; - - pwrctl->nb.notifier_call = pci_pwrctl_notify; - ret = bus_register_notifier(&pci_bus_type, &pwrctl->nb); - if (ret) - return ret; - - schedule_work(&pwrctl->work); - - return 0; -} -EXPORT_SYMBOL_GPL(pci_pwrctl_device_set_ready); - -/** - * pci_pwrctl_device_unset_ready() - Notify the pwrctl subsystem that the PCI - * device is about to be powered-down. - * - * @pwrctl: PCI power control data. - */ -void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl) -{ - /* - * We don't have to delete the link here. Typically, this function - * is only called when the power control device is being detached. If - * it is being detached then the child PCI device must have already - * been unbound too or the device core wouldn't let us unbind. - */ - bus_unregister_notifier(&pci_bus_type, &pwrctl->nb); -} -EXPORT_SYMBOL_GPL(pci_pwrctl_device_unset_ready); - -static void devm_pci_pwrctl_device_unset_ready(void *data) -{ - struct pci_pwrctl *pwrctl = data; - - pci_pwrctl_device_unset_ready(pwrctl); -} - -/** - * devm_pci_pwrctl_device_set_ready - Managed variant of - * pci_pwrctl_device_set_ready(). - * - * @dev: Device managing this pwrctl provider. - * @pwrctl: PCI power control data. - * - * Returns: - * 0 on success, negative error number on error. - */ -int devm_pci_pwrctl_device_set_ready(struct device *dev, - struct pci_pwrctl *pwrctl) -{ - int ret; - - ret = pci_pwrctl_device_set_ready(pwrctl); - if (ret) - return ret; - - return devm_add_action_or_reset(dev, - devm_pci_pwrctl_device_unset_ready, - pwrctl); -} -EXPORT_SYMBOL_GPL(devm_pci_pwrctl_device_set_ready); - -MODULE_AUTHOR("Bartosz Golaszewski "); -MODULE_DESCRIPTION("PCI Device Power Control core driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/pci/pwrctl/Kconfig b/drivers/pci/pwrctrl/Kconfig similarity index 100% rename from drivers/pci/pwrctl/Kconfig rename to drivers/pci/pwrctrl/Kconfig diff --git a/drivers/pci/pwrctrl/Makefile b/drivers/pci/pwrctrl/Makefile new file mode 100644 index 0000000000000..75c7ce531c7e2 --- /dev/null +++ b/drivers/pci/pwrctrl/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctrl-core.o +pci-pwrctrl-core-y := core.o + +obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctrl-pwrseq.o diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c new file mode 100644 index 0000000000000..2fb174db91e58 --- /dev/null +++ b/drivers/pci/pwrctrl/core.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct pci_pwrctrl *pwrctrl = container_of(nb, struct pci_pwrctrl, nb); + struct device *dev = data; + + if (dev_fwnode(dev) != dev_fwnode(pwrctrl->dev)) + return NOTIFY_DONE; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + /* + * We will have two struct device objects bound to two different + * drivers on different buses but consuming the same DT node. We + * must not bind the pins twice in this case but only once for + * the first device to be added. + * + * If we got here then the PCI device is the second after the + * power control platform device. Mark its OF node as reused. + */ + dev->of_node_reused = true; + break; + } + + return NOTIFY_DONE; +} + +static void rescan_work_func(struct work_struct *work) +{ + struct pci_pwrctrl *pwrctrl = container_of(work, + struct pci_pwrctrl, work); + + pci_lock_rescan_remove(); + pci_rescan_bus(to_pci_dev(pwrctrl->dev->parent)->bus); + pci_unlock_rescan_remove(); +} + +/** + * pci_pwrctrl_init() - Initialize the PCI power control context struct + * + * @pwrctrl: PCI power control data + * @dev: Parent device + */ +void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev) +{ + pwrctrl->dev = dev; + INIT_WORK(&pwrctrl->work, rescan_work_func); +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_init); + +/** + * pci_pwrctrl_device_set_ready() - Notify the pwrctrl subsystem that the PCI + * device is powered-up and ready to be detected. + * + * @pwrctrl: PCI power control data. + * + * Returns: + * 0 on success, negative error number on error. + * + * Note: + * This function returning 0 doesn't mean the device was detected. It means, + * that the bus rescan was successfully started. The device will get bound to + * its PCI driver asynchronously. + */ +int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl) +{ + int ret; + + if (!pwrctrl->dev) + return -ENODEV; + + pwrctrl->nb.notifier_call = pci_pwrctrl_notify; + ret = bus_register_notifier(&pci_bus_type, &pwrctrl->nb); + if (ret) + return ret; + + schedule_work(&pwrctrl->work); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready); + +/** + * pci_pwrctrl_device_unset_ready() - Notify the pwrctrl subsystem that the PCI + * device is about to be powered-down. + * + * @pwrctrl: PCI power control data. + */ +void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl) +{ + /* + * We don't have to delete the link here. Typically, this function + * is only called when the power control device is being detached. If + * it is being detached then the child PCI device must have already + * been unbound too or the device core wouldn't let us unbind. + */ + bus_unregister_notifier(&pci_bus_type, &pwrctrl->nb); +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_device_unset_ready); + +static void devm_pci_pwrctrl_device_unset_ready(void *data) +{ + struct pci_pwrctrl *pwrctrl = data; + + pci_pwrctrl_device_unset_ready(pwrctrl); +} + +/** + * devm_pci_pwrctrl_device_set_ready - Managed variant of + * pci_pwrctrl_device_set_ready(). + * + * @dev: Device managing this pwrctrl provider. + * @pwrctrl: PCI power control data. + * + * Returns: + * 0 on success, negative error number on error. + */ +int devm_pci_pwrctrl_device_set_ready(struct device *dev, + struct pci_pwrctrl *pwrctrl) +{ + int ret; + + ret = pci_pwrctrl_device_set_ready(pwrctrl); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, + devm_pci_pwrctrl_device_unset_ready, + pwrctrl); +} +EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); + +MODULE_AUTHOR("Bartosz Golaszewski "); +MODULE_DESCRIPTION("PCI Device Power Control core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/pwrctl/pci-pwrctl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c similarity index 65% rename from drivers/pci/pwrctl/pci-pwrctl-pwrseq.c rename to drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c index 0e6bd47671c2e..4e664e7b8dd23 100644 --- a/drivers/pci/pwrctl/pci-pwrctl-pwrseq.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c @@ -6,19 +6,19 @@ #include #include #include -#include +#include #include #include #include #include #include -struct pci_pwrctl_pwrseq_data { - struct pci_pwrctl ctx; +struct pci_pwrctrl_pwrseq_data { + struct pci_pwrctrl ctx; struct pwrseq_desc *pwrseq; }; -struct pci_pwrctl_pwrseq_pdata { +struct pci_pwrctrl_pwrseq_pdata { const char *target; /* * Called before doing anything else to perform device-specific @@ -27,7 +27,7 @@ struct pci_pwrctl_pwrseq_pdata { int (*validate_device)(struct device *dev); }; -static int pci_pwrctl_pwrseq_qcm_wcn_validate_device(struct device *dev) +static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev) { /* * Old device trees for some platforms already define wifi nodes for @@ -47,22 +47,22 @@ static int pci_pwrctl_pwrseq_qcm_wcn_validate_device(struct device *dev) return 0; } -static const struct pci_pwrctl_pwrseq_pdata pci_pwrctl_pwrseq_qcom_wcn_pdata = { +static const struct pci_pwrctrl_pwrseq_pdata pci_pwrctrl_pwrseq_qcom_wcn_pdata = { .target = "wlan", - .validate_device = pci_pwrctl_pwrseq_qcm_wcn_validate_device, + .validate_device = pci_pwrctrl_pwrseq_qcm_wcn_validate_device, }; -static void devm_pci_pwrctl_pwrseq_power_off(void *data) +static void devm_pci_pwrctrl_pwrseq_power_off(void *data) { struct pwrseq_desc *pwrseq = data; pwrseq_power_off(pwrseq); } -static int pci_pwrctl_pwrseq_probe(struct platform_device *pdev) +static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) { - const struct pci_pwrctl_pwrseq_pdata *pdata; - struct pci_pwrctl_pwrseq_data *data; + const struct pci_pwrctrl_pwrseq_pdata *pdata; + struct pci_pwrctrl_pwrseq_data *data; struct device *dev = &pdev->dev; int ret; @@ -90,49 +90,49 @@ static int pci_pwrctl_pwrseq_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to power-on the device\n"); - ret = devm_add_action_or_reset(dev, devm_pci_pwrctl_pwrseq_power_off, + ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off, data->pwrseq); if (ret) return ret; - pci_pwrctl_init(&data->ctx, dev); + pci_pwrctrl_init(&data->ctx, dev); - ret = devm_pci_pwrctl_device_set_ready(dev, &data->ctx); + ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx); if (ret) return dev_err_probe(dev, ret, - "Failed to register the pwrctl wrapper\n"); + "Failed to register the pwrctrl wrapper\n"); return 0; } -static const struct of_device_id pci_pwrctl_pwrseq_of_match[] = { +static const struct of_device_id pci_pwrctrl_pwrseq_of_match[] = { { /* ATH11K in QCA6390 package. */ .compatible = "pci17cb,1101", - .data = &pci_pwrctl_pwrseq_qcom_wcn_pdata, + .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata, }, { /* ATH11K in WCN6855 package. */ .compatible = "pci17cb,1103", - .data = &pci_pwrctl_pwrseq_qcom_wcn_pdata, + .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata, }, { /* ATH12K in WCN7850 package. */ .compatible = "pci17cb,1107", - .data = &pci_pwrctl_pwrseq_qcom_wcn_pdata, + .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata, }, { } }; -MODULE_DEVICE_TABLE(of, pci_pwrctl_pwrseq_of_match); +MODULE_DEVICE_TABLE(of, pci_pwrctrl_pwrseq_of_match); -static struct platform_driver pci_pwrctl_pwrseq_driver = { +static struct platform_driver pci_pwrctrl_pwrseq_driver = { .driver = { - .name = "pci-pwrctl-pwrseq", - .of_match_table = pci_pwrctl_pwrseq_of_match, + .name = "pci-pwrctrl-pwrseq", + .of_match_table = pci_pwrctrl_pwrseq_of_match, }, - .probe = pci_pwrctl_pwrseq_probe, + .probe = pci_pwrctrl_pwrseq_probe, }; -module_platform_driver(pci_pwrctl_pwrseq_driver); +module_platform_driver(pci_pwrctrl_pwrseq_driver); MODULE_AUTHOR("Bartosz Golaszewski "); MODULE_DESCRIPTION("Generic PCI Power Control module for power sequenced devices"); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 41dec625ed7b6..76f4df75b08a1 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -12,6 +12,7 @@ * file, where their drivers can use them. */ +#include #include #include #include @@ -29,10 +30,23 @@ #include #include #include +#include #include #include #include "pci.h" +static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) +{ + unsigned long count; + int ret; + + ret = pcie_lbms_count(dev, &count); + if (ret < 0) + return lnksta & PCI_EXP_LNKSTA_LBMS; + + return count > 0; +} + /* * Retrain the link of a downstream PCIe port by hand if necessary. * @@ -96,22 +110,16 @@ int pcie_failed_link_retrain(struct pci_dev *dev) pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); - if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) == - PCI_EXP_LNKSTA_LBMS) { + if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) { u16 oldlnkctl2 = lnkctl2; pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); - lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; - lnkctl2 |= PCI_EXP_LNKCTL2_TLS_2_5GT; - pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2); - - ret = pcie_retrain_link(dev, false); + ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false); if (ret) { pci_info(dev, "retraining failed\n"); - pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, - oldlnkctl2); - pcie_retrain_link(dev, true); + pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), + true); return ret; } @@ -125,11 +133,7 @@ int pcie_failed_link_retrain(struct pci_dev *dev) pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); - lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; - lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS; - pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2); - - ret = pcie_retrain_link(dev, false); + ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false); if (ret) { pci_info(dev, "retraining failed\n"); return ret; @@ -586,8 +590,7 @@ static void quirk_extend_bar_to_page(struct pci_dev *dev) const char *r_name = pci_resource_name(dev, i); if (r->flags & IORESOURCE_MEM && resource_size(r) < PAGE_SIZE) { - r->end = PAGE_SIZE - 1; - r->start = 0; + resource_set_range(r, 0, PAGE_SIZE); r->flags |= IORESOURCE_UNSET; pci_info(dev, "%s %pR: expanded to page size\n", r_name, r); @@ -604,10 +607,9 @@ static void quirk_s3_64M(struct pci_dev *dev) { struct resource *r = &dev->resource[0]; - if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) { + if (!IS_ALIGNED(r->start, SZ_64M) || resource_size(r) != SZ_64M) { r->flags |= IORESOURCE_UNSET; - r->start = 0; - r->end = 0x3ffffff; + resource_set_range(r, 0, SZ_64M); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_868, quirk_s3_64M); @@ -1342,8 +1344,7 @@ static void quirk_dunord(struct pci_dev *dev) struct resource *r = &dev->resource[1]; r->flags |= IORESOURCE_UNSET; - r->start = 0; - r->end = 0xffffff; + resource_set_range(r, 0, SZ_16M); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DUNORD, PCI_DEVICE_ID_DUNORD_I3000, quirk_dunord); @@ -2340,8 +2341,7 @@ static void quirk_tc86c001_ide(struct pci_dev *dev) if (r->start & 0x8) { r->flags |= IORESOURCE_UNSET; - r->start = 0; - r->end = 0xf; + resource_set_range(r, 0, SZ_16); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2, @@ -2369,8 +2369,7 @@ static void quirk_plx_pci9050(struct pci_dev *dev) pci_info(dev, "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n", bar); r->flags |= IORESOURCE_UNSET; - r->start = 0; - r->end = 0xff; + resource_set_range(r, 0, SZ_256); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, @@ -3522,13 +3521,13 @@ static void quirk_intel_ntb(struct pci_dev *dev) if (rc) return; - dev->resource[2].end = dev->resource[2].start + ((u64) 1 << val) - 1; + resource_set_size(&dev->resource[2], (resource_size_t)1 << val); rc = pci_read_config_byte(dev, 0x00D1, &val); if (rc) return; - dev->resource[4].end = dev->resource[4].start + ((u64) 1 << val) - 1; + resource_set_size(&dev->resource[4], (resource_size_t)1 << val); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); @@ -4996,18 +4995,21 @@ static int pci_quirk_brcm_acs(struct pci_dev *dev, u16 acs_flags) } /* - * Wangxun 10G/1G NICs have no ACS capability, and on multi-function - * devices, peer-to-peer transactions are not be used between the functions. - * So add an ACS quirk for below devices to isolate functions. + * Wangxun 40G/25G/10G/1G NICs have no ACS capability, but on + * multi-function devices, the hardware isolates the functions by + * directing all peer-to-peer traffic upstream as though PCI_ACS_RR and + * PCI_ACS_CR were set. * SFxxx 1G NICs(em). * RP1000/RP2000 10G NICs(sp). + * FF5xxx 40G/25G/10G NICs(aml). */ static int pci_quirk_wangxun_nic_acs(struct pci_dev *dev, u16 acs_flags) { switch (dev->device) { - case 0x0100 ... 0x010F: - case 0x1001: - case 0x2001: + case 0x0100 ... 0x010F: /* EM */ + case 0x1001: case 0x2001: /* SP */ + case 0x5010: case 0x5025: case 0x5040: /* AML */ + case 0x5110: case 0x5125: case 0x5140: /* AML */ return pci_acs_ctrl_enabled(acs_flags, PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF); } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index e4ce1145aa3ee..efc37fcb73e24 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -17,37 +17,40 @@ static void pci_free_resources(struct pci_dev *dev) } } -static int pci_pwrctl_unregister(struct device *dev, void *data) +static void pci_pwrctrl_unregister(struct device *dev) { - struct device_node *pci_node = data, *plat_node = dev_of_node(dev); + struct device_node *np; + struct platform_device *pdev; - if (dev_is_platform(dev) && plat_node && plat_node == pci_node) { - of_device_unregister(to_platform_device(dev)); - of_node_clear_flag(plat_node, OF_POPULATED); - } + np = dev_of_node(dev); + if (!np) + return; + + pdev = of_find_device_by_node(np); + if (!pdev) + return; - return 0; + of_device_unregister(pdev); + of_node_clear_flag(np, OF_POPULATED); } static void pci_stop_dev(struct pci_dev *dev) { pci_pme_active(dev, false); - if (pci_dev_is_added(dev)) { - device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev), - pci_pwrctl_unregister); - device_release_driver(&dev->dev); - pci_proc_detach_device(dev); - pci_remove_sysfs_dev_files(dev); - of_pci_remove_node(dev); + if (!pci_dev_test_and_clear_added(dev)) + return; - pci_dev_assign_added(dev, false); - } + pci_pwrctrl_unregister(&dev->dev); + device_release_driver(&dev->dev); + pci_proc_detach_device(dev); + pci_remove_sysfs_dev_files(dev); + of_pci_remove_node(dev); } static void pci_destroy_dev(struct pci_dev *dev) { - if (!dev->dev.kobj.parent) + if (pci_dev_test_and_set_removed(dev)) return; pci_npem_remove(dev); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 23082bc0ca37a..5e00cecf1f1af 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -134,6 +134,7 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) int i; pci_dev_for_each_resource(dev, r, i) { + const char *r_name = pci_resource_name(dev, i); struct pci_dev_resource *dev_res, *tmp; resource_size_t r_align; struct list_head *n; @@ -146,8 +147,8 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) r_align = pci_resource_alignment(dev, r); if (!r_align) { - pci_warn(dev, "BAR %d: %pR has bogus alignment\n", - i, r); + pci_warn(dev, "%s %pR: alignment must not be zero\n", + r_name, r); continue; } @@ -246,8 +247,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, add_size = add_res->add_size; align = add_res->min_align; if (!resource_size(res)) { - res->start = align; - res->end = res->start + add_size - 1; + resource_set_range(res, align, add_size); if (pci_assign_resource(add_res->dev, idx)) reset_resource(res); } else { @@ -938,8 +938,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, return; } - b_res->start = min_align; - b_res->end = b_res->start + size0 - 1; + resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, @@ -1202,8 +1201,7 @@ static void pci_bus_size_cardbus(struct pci_bus *bus, * Reserve some resources for CardBus. We reserve a fixed amount * of bus space for CardBus bridges. */ - b_res->start = pci_cardbus_io_size; - b_res->end = b_res->start + pci_cardbus_io_size - 1; + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; if (realloc_head) { b_res->end -= pci_cardbus_io_size; @@ -1215,8 +1213,7 @@ static void pci_bus_size_cardbus(struct pci_bus *bus, b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; if (b_res->parent) goto handle_b_res_2; - b_res->start = pci_cardbus_io_size; - b_res->end = b_res->start + pci_cardbus_io_size - 1; + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; if (realloc_head) { b_res->end -= pci_cardbus_io_size; @@ -1249,8 +1246,8 @@ static void pci_bus_size_cardbus(struct pci_bus *bus, * Otherwise, allocate one region of twice the size. */ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - b_res->start = pci_cardbus_mem_size; - b_res->end = b_res->start + pci_cardbus_mem_size - 1; + resource_set_range(b_res, pci_cardbus_mem_size, + pci_cardbus_mem_size); b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_STARTALIGN; if (realloc_head) { @@ -1267,8 +1264,7 @@ static void pci_bus_size_cardbus(struct pci_bus *bus, b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; if (b_res->parent) goto handle_done; - b_res->start = pci_cardbus_mem_size; - b_res->end = b_res->start + b_res_3_size - 1; + resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size); b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; if (realloc_head) { b_res->end -= b_res_3_size; @@ -1847,7 +1843,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, return; } - res->end = res->start + new_size - 1; + resource_set_size(res, new_size); /* If the resource is part of the add_list, remove it now */ if (add_list) @@ -1899,6 +1895,9 @@ static void remove_dev_resources(struct pci_dev *dev, struct resource *io, } } +#define ALIGN_DOWN_IF_NONZERO(addr, align) \ + ((align) ? ALIGN_DOWN((addr), (align)) : (addr)) + /* * io, mmio and mmio_pref contain the total amount of bridge window space * available. This includes the minimal space needed to cover all the @@ -2010,8 +2009,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * what is available). */ align = pci_resource_alignment(dev, res); - io.end = align ? io.start + ALIGN_DOWN(io_per_b, align) - 1 - : io.start + io_per_b - 1; + resource_set_size(&io, ALIGN_DOWN_IF_NONZERO(io_per_b, align)); /* * The x_per_b holds the extra resource space that can be @@ -2023,15 +2021,14 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, res = &dev->resource[PCI_BRIDGE_MEM_WINDOW]; align = pci_resource_alignment(dev, res); - mmio.end = align ? mmio.start + ALIGN_DOWN(mmio_per_b, align) - 1 - : mmio.start + mmio_per_b - 1; + resource_set_size(&mmio, + ALIGN_DOWN_IF_NONZERO(mmio_per_b,align)); mmio.start -= resource_size(res); res = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; align = pci_resource_alignment(dev, res); - mmio_pref.end = align ? mmio_pref.start + - ALIGN_DOWN(mmio_pref_per_b, align) - 1 - : mmio_pref.start + mmio_pref_per_b - 1; + resource_set_size(&mmio_pref, + ALIGN_DOWN_IF_NONZERO(mmio_pref_per_b, align)); mmio_pref.start -= resource_size(res); pci_bus_distribute_available_resources(b, add_list, io, mmio, diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index c6d933ddfd464..ca14576bf2bf2 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -211,8 +211,7 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, start = res->start; end = res->end; - res->start = fw_addr; - res->end = res->start + size - 1; + resource_set_range(res, fw_addr, size); res->flags &= ~IORESOURCE_UNSET; root = pci_find_parent_resource(dev, res); @@ -463,7 +462,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) if (ret) return ret; - res->end = res->start + pci_rebar_size_to_bytes(size) - 1; + resource_set_size(res, pci_rebar_size_to_bytes(size)); /* Check if the new config works by trying to assign everything. */ if (dev->bus->self) { @@ -475,7 +474,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) error_resize: pci_rebar_set_size(dev, resno, old); - res->end = res->start + pci_rebar_size_to_bytes(old) - 1; + resource_set_size(res, pci_rebar_size_to_bytes(old)); return ret; } EXPORT_SYMBOL(pci_resize_resource); diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 0f87cade10f74..36b44be0489d6 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -79,6 +79,7 @@ static void pci_slot_release(struct kobject *kobj) up_read(&pci_bus_sem); list_del(&slot->list); + pci_bus_put(slot->bus); kfree(slot); } @@ -244,12 +245,13 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, slot = get_slot(parent, slot_nr); if (slot) { if (hotplug) { - if ((err = slot->hotplug ? -EBUSY : 0) - || (err = rename_slot(slot, name))) { - kobject_put(&slot->kobj); - slot = NULL; - goto err; + if (slot->hotplug) { + err = -EBUSY; + goto put_slot; } + err = rename_slot(slot, name); + if (err) + goto put_slot; } goto out; } @@ -261,7 +263,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, goto err; } - slot->bus = parent; + slot->bus = pci_bus_get(parent); slot->number = slot_nr; slot->kobj.kset = pci_slots_kset; @@ -269,6 +271,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, slot_name = make_slot_name(name); if (!slot_name) { err = -ENOMEM; + pci_bus_put(slot->bus); kfree(slot); goto err; } @@ -278,10 +281,8 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL, "%s", slot_name); - if (err) { - kobject_put(&slot->kobj); - goto err; - } + if (err) + goto put_slot; down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) @@ -296,6 +297,9 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, kfree(slot_name); mutex_unlock(&pci_slot_mutex); return slot; + +put_slot: + kobject_put(&slot->kobj); err: slot = ERR_PTR(err); goto out; diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c new file mode 100644 index 0000000000000..1e604fbbda657 --- /dev/null +++ b/drivers/pci/tph.c @@ -0,0 +1,547 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TPH (TLP Processing Hints) support + * + * Copyright (C) 2024 Advanced Micro Devices, Inc. + * Eric Van Tassell + * Wei Huang + */ +#include +#include +#include +#include +#include + +#include "pci.h" + +/* System-wide TPH disabled */ +static bool pci_tph_disabled; + +#ifdef CONFIG_ACPI +/* + * The st_info struct defines the Steering Tag (ST) info returned by the + * firmware PCI ACPI _DSM method (rev=0x7, func=0xF, "_DSM to Query Cache + * Locality TPH Features"), as specified in the approved ECN for PCI Firmware + * Spec and available at https://members.pcisig.com/wg/PCI-SIG/document/15470. + * + * @vm_st_valid: 8-bit ST for volatile memory is valid + * @vm_xst_valid: 16-bit extended ST for volatile memory is valid + * @vm_ph_ignore: 1 => PH was and will be ignored, 0 => PH should be supplied + * @vm_st: 8-bit ST for volatile mem + * @vm_xst: 16-bit extended ST for volatile mem + * @pm_st_valid: 8-bit ST for persistent memory is valid + * @pm_xst_valid: 16-bit extended ST for persistent memory is valid + * @pm_ph_ignore: 1 => PH was and will be ignored, 0 => PH should be supplied + * @pm_st: 8-bit ST for persistent mem + * @pm_xst: 16-bit extended ST for persistent mem + */ +union st_info { + struct { + u64 vm_st_valid : 1; + u64 vm_xst_valid : 1; + u64 vm_ph_ignore : 1; + u64 rsvd1 : 5; + u64 vm_st : 8; + u64 vm_xst : 16; + u64 pm_st_valid : 1; + u64 pm_xst_valid : 1; + u64 pm_ph_ignore : 1; + u64 rsvd2 : 5; + u64 pm_st : 8; + u64 pm_xst : 16; + }; + u64 value; +}; + +static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type, + union st_info *info) +{ + switch (req_type) { + case PCI_TPH_REQ_TPH_ONLY: /* 8-bit tag */ + switch (mem_type) { + case TPH_MEM_TYPE_VM: + if (info->vm_st_valid) + return info->vm_st; + break; + case TPH_MEM_TYPE_PM: + if (info->pm_st_valid) + return info->pm_st; + break; + } + break; + case PCI_TPH_REQ_EXT_TPH: /* 16-bit tag */ + switch (mem_type) { + case TPH_MEM_TYPE_VM: + if (info->vm_xst_valid) + return info->vm_xst; + break; + case TPH_MEM_TYPE_PM: + if (info->pm_xst_valid) + return info->pm_xst; + break; + } + break; + default: + return 0; + } + + return 0; +} + +#define TPH_ST_DSM_FUNC_INDEX 0xF +static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid, + union st_info *st_out) +{ + union acpi_object arg3[3], in_obj, *out_obj; + + if (!acpi_check_dsm(handle, &pci_acpi_dsm_guid, 7, + BIT(TPH_ST_DSM_FUNC_INDEX))) + return AE_ERROR; + + /* DWORD: feature ID (0 for processor cache ST query) */ + arg3[0].integer.type = ACPI_TYPE_INTEGER; + arg3[0].integer.value = 0; + + /* DWORD: target UID */ + arg3[1].integer.type = ACPI_TYPE_INTEGER; + arg3[1].integer.value = cpu_uid; + + /* QWORD: properties, all 0's */ + arg3[2].integer.type = ACPI_TYPE_INTEGER; + arg3[2].integer.value = 0; + + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = ARRAY_SIZE(arg3); + in_obj.package.elements = arg3; + + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 7, + TPH_ST_DSM_FUNC_INDEX, &in_obj); + if (!out_obj) + return AE_ERROR; + + if (out_obj->type != ACPI_TYPE_BUFFER) { + ACPI_FREE(out_obj); + return AE_ERROR; + } + + st_out->value = *((u64 *)(out_obj->buffer.pointer)); + + ACPI_FREE(out_obj); + + return AE_OK; +} +#endif + +/* Update the TPH Requester Enable field of TPH Control Register */ +static void set_ctrl_reg_req_en(struct pci_dev *pdev, u8 req_type) +{ + u32 reg; + + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®); + + reg &= ~PCI_TPH_CTRL_REQ_EN_MASK; + reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, req_type); + + pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg); +} + +static u8 get_st_modes(struct pci_dev *pdev) +{ + u32 reg; + + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®); + reg &= PCI_TPH_CAP_ST_NS | PCI_TPH_CAP_ST_IV | PCI_TPH_CAP_ST_DS; + + return reg; +} + +static u32 get_st_table_loc(struct pci_dev *pdev) +{ + u32 reg; + + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®); + + return FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg); +} + +/* + * Return the size of ST table. If ST table is not in TPH Requester Extended + * Capability space, return 0. Otherwise return the ST Table Size + 1. + */ +static u16 get_st_table_size(struct pci_dev *pdev) +{ + u32 reg; + u32 loc; + + /* Check ST table location first */ + loc = get_st_table_loc(pdev); + + /* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */ + loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc); + if (loc != PCI_TPH_LOC_CAP) + return 0; + + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®); + + return FIELD_GET(PCI_TPH_CAP_ST_MASK, reg) + 1; +} + +/* Return device's Root Port completer capability */ +static u8 get_rp_completer_type(struct pci_dev *pdev) +{ + struct pci_dev *rp; + u32 reg; + int ret; + + rp = pcie_find_root_port(pdev); + if (!rp) + return 0; + + ret = pcie_capability_read_dword(rp, PCI_EXP_DEVCAP2, ®); + if (ret) + return 0; + + return FIELD_GET(PCI_EXP_DEVCAP2_TPH_COMP_MASK, reg); +} + +/* Write ST to MSI-X vector control reg - Return 0 if OK, otherwise -errno */ +static int write_tag_to_msix(struct pci_dev *pdev, int msix_idx, u16 tag) +{ +#ifdef CONFIG_PCI_MSI + struct msi_desc *msi_desc = NULL; + void __iomem *vec_ctrl; + u32 val; + int err = 0; + + msi_lock_descs(&pdev->dev); + + /* Find the msi_desc entry with matching msix_idx */ + msi_for_each_desc(msi_desc, &pdev->dev, MSI_DESC_ASSOCIATED) { + if (msi_desc->msi_index == msix_idx) + break; + } + + if (!msi_desc) { + err = -ENXIO; + goto err_out; + } + + /* Get the vector control register (offset 0xc) pointed by msix_idx */ + vec_ctrl = pdev->msix_base + msix_idx * PCI_MSIX_ENTRY_SIZE; + vec_ctrl += PCI_MSIX_ENTRY_VECTOR_CTRL; + + val = readl(vec_ctrl); + val &= ~PCI_MSIX_ENTRY_CTRL_ST; + val |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag); + writel(val, vec_ctrl); + + /* Read back to flush the update */ + val = readl(vec_ctrl); + +err_out: + msi_unlock_descs(&pdev->dev); + return err; +#else + return -ENODEV; +#endif +} + +/* Write tag to ST table - Return 0 if OK, otherwise -errno */ +static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag) +{ + int st_table_size; + int offset; + + /* Check if index is out of bound */ + st_table_size = get_st_table_size(pdev); + if (index >= st_table_size) + return -ENXIO; + + offset = pdev->tph_cap + PCI_TPH_BASE_SIZEOF + index * sizeof(u16); + + return pci_write_config_word(pdev, offset, tag); +} + +/** + * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated + * with a specific CPU + * @pdev: PCI device + * @mem_type: target memory type (volatile or persistent RAM) + * @cpu_uid: associated CPU id + * @tag: Steering Tag to be returned + * + * Return the Steering Tag for a target memory that is associated with a + * specific CPU as indicated by cpu_uid. + * + * Return: 0 if success, otherwise negative value (-errno) + */ +int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type, + unsigned int cpu_uid, u16 *tag) +{ +#ifdef CONFIG_ACPI + struct pci_dev *rp; + acpi_handle rp_acpi_handle; + union st_info info; + + rp = pcie_find_root_port(pdev); + if (!rp || !rp->bus || !rp->bus->bridge) + return -ENODEV; + + rp_acpi_handle = ACPI_HANDLE(rp->bus->bridge); + + if (tph_invoke_dsm(rp_acpi_handle, cpu_uid, &info) != AE_OK) { + *tag = 0; + return -EINVAL; + } + + *tag = tph_extract_tag(mem_type, pdev->tph_req_type, &info); + + pci_dbg(pdev, "get steering tag: mem_type=%s, cpu_uid=%d, tag=%#04x\n", + (mem_type == TPH_MEM_TYPE_VM) ? "volatile" : "persistent", + cpu_uid, *tag); + + return 0; +#else + return -ENODEV; +#endif +} +EXPORT_SYMBOL(pcie_tph_get_cpu_st); + +/** + * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry + * @pdev: PCI device + * @index: ST table entry index + * @tag: Steering Tag to be written + * + * Figure out the proper location of ST table, either in the MSI-X table or + * in the TPH Extended Capability space, and write the Steering Tag into + * the ST entry pointed by index. + * + * Return: 0 if success, otherwise negative value (-errno) + */ +int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag) +{ + u32 loc; + int err = 0; + + if (!pdev->tph_cap) + return -EINVAL; + + if (!pdev->tph_enabled) + return -EINVAL; + + /* No need to write tag if device is in "No ST Mode" */ + if (pdev->tph_mode == PCI_TPH_ST_NS_MODE) + return 0; + + /* + * Disable TPH before updating ST to avoid potential instability as + * cautioned in PCIe r6.2, sec 6.17.3, "ST Modes of Operation" + */ + set_ctrl_reg_req_en(pdev, PCI_TPH_REQ_DISABLE); + + loc = get_st_table_loc(pdev); + /* Convert loc to match with PCI_TPH_LOC_* */ + loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc); + + switch (loc) { + case PCI_TPH_LOC_MSIX: + err = write_tag_to_msix(pdev, index, tag); + break; + case PCI_TPH_LOC_CAP: + err = write_tag_to_st_table(pdev, index, tag); + break; + default: + err = -EINVAL; + } + + if (err) { + pcie_disable_tph(pdev); + return err; + } + + set_ctrl_reg_req_en(pdev, pdev->tph_mode); + + pci_dbg(pdev, "set steering tag: %s table, index=%d, tag=%#04x\n", + (loc == PCI_TPH_LOC_MSIX) ? "MSI-X" : "ST", index, tag); + + return 0; +} +EXPORT_SYMBOL(pcie_tph_set_st_entry); + +/** + * pcie_disable_tph - Turn off TPH support for device + * @pdev: PCI device + * + * Return: none + */ +void pcie_disable_tph(struct pci_dev *pdev) +{ + if (!pdev->tph_cap) + return; + + if (!pdev->tph_enabled) + return; + + pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, 0); + + pdev->tph_mode = 0; + pdev->tph_req_type = 0; + pdev->tph_enabled = 0; +} +EXPORT_SYMBOL(pcie_disable_tph); + +/** + * pcie_enable_tph - Enable TPH support for device using a specific ST mode + * @pdev: PCI device + * @mode: ST mode to enable. Current supported modes include: + * + * - PCI_TPH_ST_NS_MODE: NO ST Mode + * - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode + * - PCI_TPH_ST_DS_MODE: Device Specific Mode + * + * Check whether the mode is actually supported by the device before enabling + * and return an error if not. Additionally determine what types of requests, + * TPH or extended TPH, can be issued by the device based on its TPH requester + * capability and the Root Port's completer capability. + * + * Return: 0 on success, otherwise negative value (-errno) + */ +int pcie_enable_tph(struct pci_dev *pdev, int mode) +{ + u32 reg; + u8 dev_modes; + u8 rp_req_type; + + /* Honor "notph" kernel parameter */ + if (pci_tph_disabled) + return -EINVAL; + + if (!pdev->tph_cap) + return -EINVAL; + + if (pdev->tph_enabled) + return -EBUSY; + + /* Sanitize and check ST mode compatibility */ + mode &= PCI_TPH_CTRL_MODE_SEL_MASK; + dev_modes = get_st_modes(pdev); + if (!((1 << mode) & dev_modes)) + return -EINVAL; + + pdev->tph_mode = mode; + + /* Get req_type supported by device and its Root Port */ + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®); + if (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg)) + pdev->tph_req_type = PCI_TPH_REQ_EXT_TPH; + else + pdev->tph_req_type = PCI_TPH_REQ_TPH_ONLY; + + rp_req_type = get_rp_completer_type(pdev); + + /* Final req_type is the smallest value of two */ + pdev->tph_req_type = min(pdev->tph_req_type, rp_req_type); + + if (pdev->tph_req_type == PCI_TPH_REQ_DISABLE) + return -EINVAL; + + /* Write them into TPH control register */ + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®); + + reg &= ~PCI_TPH_CTRL_MODE_SEL_MASK; + reg |= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK, pdev->tph_mode); + + reg &= ~PCI_TPH_CTRL_REQ_EN_MASK; + reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, pdev->tph_req_type); + + pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg); + + pdev->tph_enabled = 1; + + return 0; +} +EXPORT_SYMBOL(pcie_enable_tph); + +void pci_restore_tph_state(struct pci_dev *pdev) +{ + struct pci_cap_saved_state *save_state; + int num_entries, i, offset; + u16 *st_entry; + u32 *cap; + + if (!pdev->tph_cap) + return; + + if (!pdev->tph_enabled) + return; + + save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_TPH); + if (!save_state) + return; + + /* Restore control register and all ST entries */ + cap = &save_state->cap.data[0]; + pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, *cap++); + st_entry = (u16 *)cap; + offset = PCI_TPH_BASE_SIZEOF; + num_entries = get_st_table_size(pdev); + for (i = 0; i < num_entries; i++) { + pci_write_config_word(pdev, pdev->tph_cap + offset, + *st_entry++); + offset += sizeof(u16); + } +} + +void pci_save_tph_state(struct pci_dev *pdev) +{ + struct pci_cap_saved_state *save_state; + int num_entries, i, offset; + u16 *st_entry; + u32 *cap; + + if (!pdev->tph_cap) + return; + + if (!pdev->tph_enabled) + return; + + save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_TPH); + if (!save_state) + return; + + /* Save control register */ + cap = &save_state->cap.data[0]; + pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, cap++); + + /* Save all ST entries in extended capability structure */ + st_entry = (u16 *)cap; + offset = PCI_TPH_BASE_SIZEOF; + num_entries = get_st_table_size(pdev); + for (i = 0; i < num_entries; i++) { + pci_read_config_word(pdev, pdev->tph_cap + offset, + st_entry++); + offset += sizeof(u16); + } +} + +void pci_no_tph(void) +{ + pci_tph_disabled = true; + + pr_info("PCIe TPH is disabled\n"); +} + +void pci_tph_init(struct pci_dev *pdev) +{ + int num_entries; + u32 save_size; + + pdev->tph_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_TPH); + if (!pdev->tph_cap) + return; + + num_entries = get_st_table_size(pdev); + save_size = sizeof(u32) + num_entries * sizeof(u16); + pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size); +} diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index e4300f5f304f3..a469bcbc0da7f 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -325,7 +325,7 @@ static struct bin_attribute *vpd_attrs[] = { }; static umode_t vpd_attr_is_visible(struct kobject *kobj, - struct bin_attribute *a, int n) + const struct bin_attribute *a, int n) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); diff --git a/drivers/peci/controller/peci-npcm.c b/drivers/peci/controller/peci-npcm.c index ec613d35c7962..fa91be58f6f37 100644 --- a/drivers/peci/controller/peci-npcm.c +++ b/drivers/peci/controller/peci-npcm.c @@ -224,7 +224,7 @@ static const struct regmap_config npcm_peci_regmap_config = { .fast_io = true, }; -static struct peci_controller_ops npcm_ops = { +static const struct peci_controller_ops npcm_ops = { .xfer = npcm_peci_xfer, }; diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 49bd811c6fd6e..b20fa600e510c 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -2178,8 +2178,6 @@ static int arm_cmn_init_dtcs(struct arm_cmn *cmn) continue; xp = arm_cmn_node_to_xp(cmn, dn); - dn->portid_bits = xp->portid_bits; - dn->deviceid_bits = xp->deviceid_bits; dn->dtc = xp->dtc; dn->dtm = xp->dtm; if (cmn->multi_dtm) @@ -2420,6 +2418,8 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) } arm_cmn_init_node_info(cmn, reg & CMN_CHILD_NODE_ADDR, dn); + dn->portid_bits = xp->portid_bits; + dn->deviceid_bits = xp->deviceid_bits; switch (dn->type) { case CMN_TYPE_DTC: diff --git a/drivers/perf/arm-ni.c b/drivers/perf/arm-ni.c index 90fcfe693439e..fd7a5e60e9630 100644 --- a/drivers/perf/arm-ni.c +++ b/drivers/perf/arm-ni.c @@ -247,7 +247,6 @@ static struct attribute *arm_ni_other_attrs[] = { static const struct attribute_group arm_ni_other_attr_group = { .attrs = arm_ni_other_attrs, - NULL }; static const struct attribute_group *arm_ni_attr_groups[] = { diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index b1510f660c7a6..621f02a7f43be 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -431,6 +431,17 @@ static int smmu_pmu_event_init(struct perf_event *event) return -EINVAL; } + /* + * Ensure all events are on the same cpu so all events are in the + * same cpu context, to avoid races on pmu_enable etc. + */ + event->cpu = smmu_pmu->on_cpu; + + hwc->idx = -1; + + if (event->group_leader == event) + return 0; + for_each_sibling_event(sibling, event->group_leader) { if (is_software_event(sibling)) continue; @@ -442,14 +453,6 @@ static int smmu_pmu_event_init(struct perf_event *event) return -EINVAL; } - hwc->idx = -1; - - /* - * Ensure all events are on the same cpu so all events are in the - * same cpu context, to avoid races on pmu_enable etc. - */ - event->cpu = smmu_pmu->on_cpu; - return 0; } diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index f73abff416bed..8d58efe998ec5 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -82,6 +82,17 @@ config PHY_AIROHA_PCIE This driver create the basic PHY instance and provides initialize callback for PCIe GEN3 port. +config PHY_NXP_PTN3222 + tristate "NXP PTN3222 1-port eUSB2 to USB2 redriver" + depends on I2C + depends on OF + select GENERIC_PHY + help + Enable this to support NXP PTN3222 1-port eUSB2 to USB2 Redriver. + This redriver performs translation between eUSB2 and USB2 signalling + schemes. It supports all three USB 2.0 data rates: Low Speed, Full + Speed and High Speed. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index ebc399560da4f..e281442acc752 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o +obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o obj-y += allwinner/ \ amlogic/ \ broadcom/ \ diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index b0f19e9506010..cd159a71b23c3 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -1049,11 +1049,11 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = { MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); static struct platform_driver sun4i_usb_phy_driver = { - .probe = sun4i_usb_phy_probe, - .remove_new = sun4i_usb_phy_remove, + .probe = sun4i_usb_phy_probe, + .remove = sun4i_usb_phy_remove, .driver = { - .of_match_table = sun4i_usb_phy_of_match, - .name = "sun4i-usb-phy", + .of_match_table= sun4i_usb_phy_of_match, + .name = "sun4i-usb-phy", } }; module_platform_driver(sun4i_usb_phy_driver); diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c index 5213c75b6da6a..c5d35031b3983 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb2.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c @@ -24,9 +24,6 @@ struct bcm_ns_usb2 { struct phy *phy; struct regmap *clkset; void __iomem *base; - - /* Deprecated binding */ - void __iomem *dmu; }; static int bcm_ns_usb2_phy_init(struct phy *phy) @@ -49,10 +46,7 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) goto err_clk_off; } - if (usb2->base) - usb2ctl = readl(usb2->base); - else - usb2ctl = readl(usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL); + usb2ctl = readl(usb2->base); if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { usb_pll_pdiv = usb2ctl; @@ -66,24 +60,16 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; /* Unlock DMU PLL settings with some magic value */ - if (usb2->clkset) - regmap_write(usb2->clkset, 0, 0x0000ea68); - else - writel(0x0000ea68, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY); + regmap_write(usb2->clkset, 0, 0x0000ea68); /* Write USB 2.0 PLL control setting */ usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; - if (usb2->base) - writel(usb2ctl, usb2->base); - else - writel(usb2ctl, usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL); + + writel(usb2ctl, usb2->base); /* Lock DMU PLL settings */ - if (usb2->clkset) - regmap_write(usb2->clkset, 0, 0x00000000); - else - writel(0x00000000, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY); + regmap_write(usb2->clkset, 0, 0x00000000); err_clk_off: clk_disable_unprepare(usb2->ref_clk); @@ -107,27 +93,17 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev) return -ENOMEM; usb2->dev = dev; - if (of_property_present(dev->of_node, "brcm,syscon-clkset")) { - usb2->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(usb2->base)) { - dev_err(dev, "Failed to map control reg\n"); - return PTR_ERR(usb2->base); - } - - usb2->clkset = syscon_regmap_lookup_by_phandle(dev->of_node, - "brcm,syscon-clkset"); - if (IS_ERR(usb2->clkset)) { - dev_err(dev, "Failed to lookup clkset regmap\n"); - return PTR_ERR(usb2->clkset); - } - } else { - usb2->dmu = devm_platform_ioremap_resource_byname(pdev, "dmu"); - if (IS_ERR(usb2->dmu)) { - dev_err(dev, "Failed to map DMU regs\n"); - return PTR_ERR(usb2->dmu); - } + usb2->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usb2->base)) { + dev_err(dev, "Failed to map control reg\n"); + return PTR_ERR(usb2->base); + } - dev_warn(dev, "using deprecated DT binding\n"); + usb2->clkset = syscon_regmap_lookup_by_phandle(dev->of_node, + "brcm,syscon-clkset"); + if (IS_ERR(usb2->clkset)) { + dev_err(dev, "Failed to lookup clkset regmap\n"); + return PTR_ERR(usb2->clkset); } usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 5ebb3a6161157..da23078878a9b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -193,256 +193,251 @@ static const u32 usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { /* 3390B0 */ [BRCM_FAMILY_3390A0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, + [USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR] = + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 4908 */ [BRCM_FAMILY_4908] = { - 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ - 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ - 0, /* USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ - 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK */ - 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK */ - 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK */ - 0, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, }, /* 7250b0 */ [BRCM_FAMILY_7250B0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, - 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR] = + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7271a0 */ [BRCM_FAMILY_7271A0] = { - 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ - 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, - USB_CTRL_USB_PM_SOFT_RESET_MASK, - USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK, - USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK, - USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, + [USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR] = + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + [USB_CTRL_USB_PM_SOFT_RESET_SELECTOR] = + USB_CTRL_USB_PM_SOFT_RESET_MASK, + [USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_SELECTOR] = + USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK, + [USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7364a0 */ [BRCM_FAMILY_7364A0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, - 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR] = + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7366c0 */ [BRCM_FAMILY_7366C0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 74371A0 */ [BRCM_FAMILY_74371A0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, - 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ - 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK */ - 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK */ - 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ - USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ - USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB30_CTL1_USB3_IOC_MASK, - USB_CTRL_USB30_CTL1_USB3_IPP_MASK, - 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_MASK */ - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, + [USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR] = + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + [USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB30_CTL1_USB3_IOC_SELECTOR] = + USB_CTRL_USB30_CTL1_USB3_IOC_MASK, + [USB_CTRL_USB30_CTL1_USB3_IPP_SELECTOR] = + USB_CTRL_USB30_CTL1_USB3_IPP_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7439B0 */ [BRCM_FAMILY_7439B0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, + [USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR] = + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7445d0 */ [BRCM_FAMILY_7445D0] = { - USB_CTRL_SETUP_SCB1_EN_MASK, - USB_CTRL_SETUP_SCB2_EN_MASK, - USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, - 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, - 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ - USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK, - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ - 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SCB1_EN_SELECTOR] = + USB_CTRL_SETUP_SCB1_EN_MASK, + [USB_CTRL_SETUP_SCB2_EN_SELECTOR] = + USB_CTRL_SETUP_SCB2_EN_MASK, + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR] = + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + [USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7260a0 */ [BRCM_FAMILY_7260A0] = { - 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ - 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ - USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, - USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, - USB_CTRL_USB_PM_SOFT_RESET_MASK, - USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK, - USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK, - USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, - ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR] = + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + [USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, + [USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR] = + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + [USB_CTRL_USB_PM_SOFT_RESET_SELECTOR] = + USB_CTRL_USB_PM_SOFT_RESET_MASK, + [USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_SELECTOR] = + USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK, + [USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK, + [USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR] = + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + [USB_CTRL_SETUP_ENDIAN_SELECTOR] = ENDIAN_SETTINGS, }, /* 7278a0 */ [BRCM_FAMILY_7278A0] = { - 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ - 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ - 0, /*USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ - USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, - USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, - USB_CTRL_SETUP_OC3_DISABLE_MASK, - 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ - USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, - USB_CTRL_USB_PM_USB_PWRDN_MASK, - 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ - 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ - USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, - USB_CTRL_USB_PM_SOFT_RESET_MASK, - 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ - 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ - 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_MASK */ - 0, /* USB_CTRL_SETUP ENDIAN bits */ + [USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR] = + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, + [USB_CTRL_SETUP_OC3_DISABLE_SELECTOR] = + USB_CTRL_SETUP_OC3_DISABLE_MASK, + [USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR] = + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + [USB_CTRL_USB_PM_USB_PWRDN_SELECTOR] = + USB_CTRL_USB_PM_USB_PWRDN_MASK, + [USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR] = + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + [USB_CTRL_USB_PM_SOFT_RESET_SELECTOR] = + USB_CTRL_USB_PM_SOFT_RESET_MASK, }, }; diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index ad2eec0956016..6362ca5b7fb6b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -667,7 +667,7 @@ MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); static struct platform_driver brcm_usb_driver = { .probe = brcm_usb_phy_probe, - .remove_new = brcm_usb_phy_remove, + .remove = brcm_usb_phy_remove, .driver = { .name = "brcmstb-usb-phy", .pm = &brcm_usb_phy_pm_ops, diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index dddb66de6dba1..ed87a3970f834 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -472,7 +472,7 @@ MODULE_DEVICE_TABLE(of, cdns_dphy_of_match); static struct platform_driver cdns_dphy_platform_driver = { .probe = cdns_dphy_probe, - .remove_new = cdns_dphy_remove, + .remove = cdns_dphy_remove, .driver = { .name = "cdns-mipi-dphy", .of_match_table = cdns_dphy_of_match, diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index dfc4f55d112e1..45a5c00843bf6 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -2731,7 +2731,7 @@ MODULE_DEVICE_TABLE(of, cdns_sierra_id_table); static struct platform_driver cdns_sierra_driver = { .probe = cdns_sierra_phy_probe, - .remove_new = cdns_sierra_phy_remove, + .remove = cdns_sierra_phy_remove, .driver = { .name = "cdns-sierra-phy", .of_match_table = cdns_sierra_id_table, diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 8bbbbb87bb22e..a281c0dfae974 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -5440,8 +5440,8 @@ MODULE_DEVICE_TABLE(of, cdns_torrent_phy_of_match); static struct platform_driver cdns_torrent_phy_driver = { .probe = cdns_torrent_phy_probe, - .remove_new = cdns_torrent_phy_remove, - .driver = { + .remove = cdns_torrent_phy_remove, + .driver = { .name = "cdns-torrent-phy", .of_match_table = cdns_torrent_phy_of_match, .pm = pm_sleep_ptr(&cdns_torrent_phy_pm_ops), diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index 38388dd04bdc1..7aef2f59e8eb4 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -433,12 +433,12 @@ static const struct of_device_id mixel_lvds_phy_of_match[] = { MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); static struct platform_driver mixel_lvds_phy_driver = { - .probe = mixel_lvds_phy_probe, - .remove_new = mixel_lvds_phy_remove, + .probe = mixel_lvds_phy_probe, + .remove = mixel_lvds_phy_remove, .driver = { .pm = &mixel_lvds_phy_pm_ops, .name = "mixel-lvds-phy", - .of_match_table = mixel_lvds_phy_of_match, + .of_match_table = mixel_lvds_phy_of_match, } }; module_platform_driver(mixel_lvds_phy_driver); diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index b86da8e9daa46..f7994e8983c8e 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -631,9 +631,9 @@ static const struct of_device_id lynx_28g_of_match_table[] = { MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table); static struct platform_driver lynx_28g_driver = { - .probe = lynx_28g_probe, - .remove_new = lynx_28g_remove, - .driver = { + .probe = lynx_28g_probe, + .remove = lynx_28g_remove, + .driver = { .name = "lynx-28g", .of_match_table = lynx_28g_of_match_table, }, diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c index 9048cdc760c21..2c8038864357b 100644 --- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c +++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c @@ -14,352 +14,265 @@ #include #include -#define PHY_REG_00 0x00 -#define PHY_REG_01 0x04 -#define PHY_REG_02 0x08 -#define PHY_REG_08 0x20 -#define PHY_REG_09 0x24 -#define PHY_REG_10 0x28 -#define PHY_REG_11 0x2c - -#define PHY_REG_12 0x30 -#define REG12_CK_DIV_MASK GENMASK(5, 4) - -#define PHY_REG_13 0x34 -#define REG13_TG_CODE_LOW_MASK GENMASK(7, 0) - -#define PHY_REG_14 0x38 -#define REG14_TOL_MASK GENMASK(7, 4) -#define REG14_RP_CODE_MASK GENMASK(3, 1) -#define REG14_TG_CODE_HIGH_MASK GENMASK(0, 0) - -#define PHY_REG_15 0x3c -#define PHY_REG_16 0x40 -#define PHY_REG_17 0x44 -#define PHY_REG_18 0x48 -#define PHY_REG_19 0x4c -#define PHY_REG_20 0x50 - -#define PHY_REG_21 0x54 -#define REG21_SEL_TX_CK_INV BIT(7) -#define REG21_PMS_S_MASK GENMASK(3, 0) - -#define PHY_REG_22 0x58 -#define PHY_REG_23 0x5c -#define PHY_REG_24 0x60 -#define PHY_REG_25 0x64 -#define PHY_REG_26 0x68 -#define PHY_REG_27 0x6c -#define PHY_REG_28 0x70 -#define PHY_REG_29 0x74 -#define PHY_REG_30 0x78 -#define PHY_REG_31 0x7c -#define PHY_REG_32 0x80 +#define PHY_REG(reg) (reg * 4) +#define REG01_PMS_P_MASK GENMASK(3, 0) +#define REG03_PMS_S_MASK GENMASK(7, 4) +#define REG12_CK_DIV_MASK GENMASK(5, 4) + +#define REG13_TG_CODE_LOW_MASK GENMASK(7, 0) + +#define REG14_TOL_MASK GENMASK(7, 4) +#define REG14_RP_CODE_MASK GENMASK(3, 1) +#define REG14_TG_CODE_HIGH_MASK GENMASK(0, 0) + +#define REG21_SEL_TX_CK_INV BIT(7) +#define REG21_PMS_S_MASK GENMASK(3, 0) /* * REG33 does not match the ref manual. According to Sandor Yu from NXP, * "There is a doc issue on the i.MX8MP latest RM" * REG33 is being used per guidance from Sandor */ +#define REG33_MODE_SET_DONE BIT(7) +#define REG33_FIX_DA BIT(1) + +#define REG34_PHY_READY BIT(7) +#define REG34_PLL_LOCK BIT(6) +#define REG34_PHY_CLK_READY BIT(5) -#define PHY_REG_33 0x84 -#define REG33_MODE_SET_DONE BIT(7) -#define REG33_FIX_DA BIT(1) - -#define PHY_REG_34 0x88 -#define REG34_PHY_READY BIT(7) -#define REG34_PLL_LOCK BIT(6) -#define REG34_PHY_CLK_READY BIT(5) - -#define PHY_REG_35 0x8c -#define PHY_REG_36 0x90 -#define PHY_REG_37 0x94 -#define PHY_REG_38 0x98 -#define PHY_REG_39 0x9c -#define PHY_REG_40 0xa0 -#define PHY_REG_41 0xa4 -#define PHY_REG_42 0xa8 -#define PHY_REG_43 0xac -#define PHY_REG_44 0xb0 -#define PHY_REG_45 0xb4 -#define PHY_REG_46 0xb8 -#define PHY_REG_47 0xbc - -#define PHY_PLL_DIV_REGS_NUM 6 +#ifndef MHZ +#define MHZ (1000UL * 1000UL) +#endif + +#define PHY_PLL_DIV_REGS_NUM 7 struct phy_config { u32 pixclk; u8 pll_div_regs[PHY_PLL_DIV_REGS_NUM]; }; +/* + * The calculated_phy_pll_cfg only handles integer divider for PMS, + * meaning the last four entries will be fixed, but the first three will + * be calculated by the PMS calculator. + */ +static struct phy_config calculated_phy_pll_cfg = { + .pixclk = 0, + .pll_div_regs = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }, +}; + +/* The lookup table contains values for which the fractional divder is used */ static const struct phy_config phy_pll_cfg[] = { { .pixclk = 22250000, - .pll_div_regs = { 0x4b, 0xf1, 0x89, 0x88, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x4b, 0xf1, 0x89, 0x88, 0x80, 0x40 }, }, { .pixclk = 23750000, - .pll_div_regs = { 0x50, 0xf1, 0x86, 0x85, 0x80, 0x40 }, - }, { - .pixclk = 24000000, - .pll_div_regs = { 0x50, 0xf0, 0x00, 0x00, 0x80, 0x00 }, + .pll_div_regs = { 0xd1, 0x50, 0xf1, 0x86, 0x85, 0x80, 0x40 }, }, { .pixclk = 24024000, - .pll_div_regs = { 0x50, 0xf1, 0x99, 0x02, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x50, 0xf1, 0x99, 0x02, 0x80, 0x40 }, }, { .pixclk = 25175000, - .pll_div_regs = { 0x54, 0xfc, 0xcc, 0x91, 0x80, 0x40 }, - }, { - .pixclk = 25200000, - .pll_div_regs = { 0x54, 0xf0, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x54, 0xfc, 0xcc, 0x91, 0x80, 0x40 }, + }, { .pixclk = 26750000, - .pll_div_regs = { 0x5a, 0xf2, 0x89, 0x88, 0x80, 0x40 }, - }, { - .pixclk = 27000000, - .pll_div_regs = { 0x5a, 0xf0, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x5a, 0xf2, 0x89, 0x88, 0x80, 0x40 }, + }, { .pixclk = 27027000, - .pll_div_regs = { 0x5a, 0xf2, 0xfd, 0x0c, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x5a, 0xf2, 0xfd, 0x0c, 0x80, 0x40 }, }, { .pixclk = 29500000, - .pll_div_regs = { 0x62, 0xf4, 0x95, 0x08, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x62, 0xf4, 0x95, 0x08, 0x80, 0x40 }, }, { .pixclk = 30750000, - .pll_div_regs = { 0x66, 0xf4, 0x82, 0x01, 0x88, 0x45 }, + .pll_div_regs = { 0xd1, 0x66, 0xf4, 0x82, 0x01, 0x88, 0x45 }, }, { .pixclk = 30888000, - .pll_div_regs = { 0x66, 0xf4, 0x99, 0x18, 0x88, 0x45 }, + .pll_div_regs = { 0xd1, 0x66, 0xf4, 0x99, 0x18, 0x88, 0x45 }, }, { .pixclk = 33750000, - .pll_div_regs = { 0x70, 0xf4, 0x82, 0x01, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x70, 0xf4, 0x82, 0x01, 0x80, 0x40 }, }, { .pixclk = 35000000, - .pll_div_regs = { 0x58, 0xb8, 0x8b, 0x88, 0x80, 0x40 }, - }, { - .pixclk = 36000000, - .pll_div_regs = { 0x5a, 0xb0, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x58, 0xb8, 0x8b, 0x88, 0x80, 0x40 }, + }, { .pixclk = 36036000, - .pll_div_regs = { 0x5a, 0xb2, 0xfd, 0x0c, 0x80, 0x40 }, - }, { - .pixclk = 40000000, - .pll_div_regs = { 0x64, 0xb0, 0x00, 0x00, 0x80, 0x00 }, - }, { - .pixclk = 43200000, - .pll_div_regs = { 0x5a, 0x90, 0x00, 0x00, 0x80, 0x00 }, + .pll_div_regs = { 0xd1, 0x5a, 0xb2, 0xfd, 0x0c, 0x80, 0x40 }, }, { .pixclk = 43243200, - .pll_div_regs = { 0x5a, 0x92, 0xfd, 0x0c, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x5a, 0x92, 0xfd, 0x0c, 0x80, 0x40 }, }, { .pixclk = 44500000, - .pll_div_regs = { 0x5c, 0x92, 0x98, 0x11, 0x84, 0x41 }, + .pll_div_regs = { 0xd1, 0x5c, 0x92, 0x98, 0x11, 0x84, 0x41 }, }, { .pixclk = 47000000, - .pll_div_regs = { 0x62, 0x94, 0x95, 0x82, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x62, 0x94, 0x95, 0x82, 0x80, 0x40 }, }, { .pixclk = 47500000, - .pll_div_regs = { 0x63, 0x96, 0xa1, 0x82, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x63, 0x96, 0xa1, 0x82, 0x80, 0x40 }, }, { .pixclk = 50349650, - .pll_div_regs = { 0x54, 0x7c, 0xc3, 0x8f, 0x80, 0x40 }, - }, { - .pixclk = 50400000, - .pll_div_regs = { 0x54, 0x70, 0x00, 0x00, 0x80, 0x00 }, + .pll_div_regs = { 0xd1, 0x54, 0x7c, 0xc3, 0x8f, 0x80, 0x40 }, }, { .pixclk = 53250000, - .pll_div_regs = { 0x58, 0x72, 0x84, 0x03, 0x82, 0x41 }, + .pll_div_regs = { 0xd1, 0x58, 0x72, 0x84, 0x03, 0x82, 0x41 }, }, { .pixclk = 53500000, - .pll_div_regs = { 0x5a, 0x72, 0x89, 0x88, 0x80, 0x40 }, - }, { - .pixclk = 54000000, - .pll_div_regs = { 0x5a, 0x70, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x5a, 0x72, 0x89, 0x88, 0x80, 0x40 }, + }, { .pixclk = 54054000, - .pll_div_regs = { 0x5a, 0x72, 0xfd, 0x0c, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x5a, 0x72, 0xfd, 0x0c, 0x80, 0x40 }, }, { .pixclk = 59000000, - .pll_div_regs = { 0x62, 0x74, 0x95, 0x08, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x62, 0x74, 0x95, 0x08, 0x80, 0x40 }, }, { .pixclk = 59340659, - .pll_div_regs = { 0x62, 0x74, 0xdb, 0x52, 0x88, 0x47 }, - }, { - .pixclk = 59400000, - .pll_div_regs = { 0x63, 0x70, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x62, 0x74, 0xdb, 0x52, 0x88, 0x47 }, + }, { .pixclk = 61500000, - .pll_div_regs = { 0x66, 0x74, 0x82, 0x01, 0x88, 0x45 }, + .pll_div_regs = { 0xd1, 0x66, 0x74, 0x82, 0x01, 0x88, 0x45 }, }, { .pixclk = 63500000, - .pll_div_regs = { 0x69, 0x74, 0x89, 0x08, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x69, 0x74, 0x89, 0x08, 0x80, 0x40 }, }, { .pixclk = 67500000, - .pll_div_regs = { 0x54, 0x52, 0x87, 0x03, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x54, 0x52, 0x87, 0x03, 0x80, 0x40 }, }, { .pixclk = 70000000, - .pll_div_regs = { 0x58, 0x58, 0x8b, 0x88, 0x80, 0x40 }, - }, { - .pixclk = 72000000, - .pll_div_regs = { 0x5a, 0x50, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x58, 0x58, 0x8b, 0x88, 0x80, 0x40 }, + }, { .pixclk = 72072000, - .pll_div_regs = { 0x5a, 0x52, 0xfd, 0x0c, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x5a, 0x52, 0xfd, 0x0c, 0x80, 0x40 }, }, { .pixclk = 74176000, - .pll_div_regs = { 0x5d, 0x58, 0xdb, 0xA2, 0x88, 0x41 }, + .pll_div_regs = { 0xd1, 0x5d, 0x58, 0xdb, 0xA2, 0x88, 0x41 }, }, { .pixclk = 74250000, - .pll_div_regs = { 0x5c, 0x52, 0x90, 0x0d, 0x84, 0x41 }, + .pll_div_regs = { 0xd1, 0x5c, 0x52, 0x90, 0x0d, 0x84, 0x41 }, }, { .pixclk = 78500000, - .pll_div_regs = { 0x62, 0x54, 0x87, 0x01, 0x80, 0x40 }, - }, { - .pixclk = 80000000, - .pll_div_regs = { 0x64, 0x50, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x62, 0x54, 0x87, 0x01, 0x80, 0x40 }, + }, { .pixclk = 82000000, - .pll_div_regs = { 0x66, 0x54, 0x82, 0x01, 0x88, 0x45 }, + .pll_div_regs = { 0xd1, 0x66, 0x54, 0x82, 0x01, 0x88, 0x45 }, }, { .pixclk = 82500000, - .pll_div_regs = { 0x67, 0x54, 0x88, 0x01, 0x90, 0x49 }, + .pll_div_regs = { 0xd1, 0x67, 0x54, 0x88, 0x01, 0x90, 0x49 }, }, { .pixclk = 89000000, - .pll_div_regs = { 0x70, 0x54, 0x84, 0x83, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x70, 0x54, 0x84, 0x83, 0x80, 0x40 }, }, { .pixclk = 90000000, - .pll_div_regs = { 0x70, 0x54, 0x82, 0x01, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x70, 0x54, 0x82, 0x01, 0x80, 0x40 }, }, { .pixclk = 94000000, - .pll_div_regs = { 0x4e, 0x32, 0xa7, 0x10, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x4e, 0x32, 0xa7, 0x10, 0x80, 0x40 }, }, { .pixclk = 95000000, - .pll_div_regs = { 0x50, 0x31, 0x86, 0x85, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x50, 0x31, 0x86, 0x85, 0x80, 0x40 }, }, { .pixclk = 98901099, - .pll_div_regs = { 0x52, 0x3a, 0xdb, 0x4c, 0x88, 0x47 }, + .pll_div_regs = { 0xd1, 0x52, 0x3a, 0xdb, 0x4c, 0x88, 0x47 }, }, { .pixclk = 99000000, - .pll_div_regs = { 0x52, 0x32, 0x82, 0x01, 0x88, 0x47 }, + .pll_div_regs = { 0xd1, 0x52, 0x32, 0x82, 0x01, 0x88, 0x47 }, }, { .pixclk = 100699300, - .pll_div_regs = { 0x54, 0x3c, 0xc3, 0x8f, 0x80, 0x40 }, - }, { - .pixclk = 100800000, - .pll_div_regs = { 0x54, 0x30, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x54, 0x3c, 0xc3, 0x8f, 0x80, 0x40 }, + }, { .pixclk = 102500000, - .pll_div_regs = { 0x55, 0x32, 0x8c, 0x05, 0x90, 0x4b }, + .pll_div_regs = { 0xd1, 0x55, 0x32, 0x8c, 0x05, 0x90, 0x4b }, }, { .pixclk = 104750000, - .pll_div_regs = { 0x57, 0x32, 0x98, 0x07, 0x90, 0x49 }, + .pll_div_regs = { 0xd1, 0x57, 0x32, 0x98, 0x07, 0x90, 0x49 }, }, { .pixclk = 106500000, - .pll_div_regs = { 0x58, 0x32, 0x84, 0x03, 0x82, 0x41 }, + .pll_div_regs = { 0xd1, 0x58, 0x32, 0x84, 0x03, 0x82, 0x41 }, }, { .pixclk = 107000000, - .pll_div_regs = { 0x5a, 0x32, 0x89, 0x88, 0x80, 0x40 }, - }, { - .pixclk = 108000000, - .pll_div_regs = { 0x5a, 0x30, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x5a, 0x32, 0x89, 0x88, 0x80, 0x40 }, + }, { .pixclk = 108108000, - .pll_div_regs = { 0x5a, 0x32, 0xfd, 0x0c, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x5a, 0x32, 0xfd, 0x0c, 0x80, 0x40 }, }, { .pixclk = 118000000, - .pll_div_regs = { 0x62, 0x34, 0x95, 0x08, 0x80, 0x40 }, - }, { - .pixclk = 118800000, - .pll_div_regs = { 0x63, 0x30, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x62, 0x34, 0x95, 0x08, 0x80, 0x40 }, + }, { .pixclk = 123000000, - .pll_div_regs = { 0x66, 0x34, 0x82, 0x01, 0x88, 0x45 }, + .pll_div_regs = { 0xd1, 0x66, 0x34, 0x82, 0x01, 0x88, 0x45 }, }, { .pixclk = 127000000, - .pll_div_regs = { 0x69, 0x34, 0x89, 0x08, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x69, 0x34, 0x89, 0x08, 0x80, 0x40 }, }, { .pixclk = 135000000, - .pll_div_regs = { 0x70, 0x34, 0x82, 0x01, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x70, 0x34, 0x82, 0x01, 0x80, 0x40 }, }, { .pixclk = 135580000, - .pll_div_regs = { 0x71, 0x39, 0xe9, 0x82, 0x9c, 0x5b }, + .pll_div_regs = { 0xd1, 0x71, 0x39, 0xe9, 0x82, 0x9c, 0x5b }, }, { .pixclk = 137520000, - .pll_div_regs = { 0x72, 0x38, 0x99, 0x10, 0x85, 0x41 }, + .pll_div_regs = { 0xd1, 0x72, 0x38, 0x99, 0x10, 0x85, 0x41 }, }, { .pixclk = 138750000, - .pll_div_regs = { 0x73, 0x35, 0x88, 0x05, 0x90, 0x4d }, + .pll_div_regs = { 0xd1, 0x73, 0x35, 0x88, 0x05, 0x90, 0x4d }, }, { .pixclk = 140000000, - .pll_div_regs = { 0x75, 0x36, 0xa7, 0x90, 0x80, 0x40 }, - }, { - .pixclk = 144000000, - .pll_div_regs = { 0x78, 0x30, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x75, 0x36, 0xa7, 0x90, 0x80, 0x40 }, + }, { .pixclk = 148352000, - .pll_div_regs = { 0x7b, 0x35, 0xdb, 0x39, 0x90, 0x45 }, + .pll_div_regs = { 0xd1, 0x7b, 0x35, 0xdb, 0x39, 0x90, 0x45 }, }, { .pixclk = 148500000, - .pll_div_regs = { 0x7b, 0x35, 0x84, 0x03, 0x90, 0x45 }, + .pll_div_regs = { 0xd1, 0x7b, 0x35, 0x84, 0x03, 0x90, 0x45 }, }, { .pixclk = 154000000, - .pll_div_regs = { 0x40, 0x18, 0x83, 0x01, 0x00, 0x40 }, + .pll_div_regs = { 0xd1, 0x40, 0x18, 0x83, 0x01, 0x00, 0x40 }, }, { .pixclk = 157000000, - .pll_div_regs = { 0x41, 0x11, 0xa7, 0x14, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x41, 0x11, 0xa7, 0x14, 0x80, 0x40 }, }, { .pixclk = 160000000, - .pll_div_regs = { 0x42, 0x12, 0xa1, 0x20, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x42, 0x12, 0xa1, 0x20, 0x80, 0x40 }, }, { .pixclk = 162000000, - .pll_div_regs = { 0x43, 0x18, 0x8b, 0x08, 0x96, 0x55 }, + .pll_div_regs = { 0xd1, 0x43, 0x18, 0x8b, 0x08, 0x96, 0x55 }, }, { .pixclk = 164000000, - .pll_div_regs = { 0x45, 0x11, 0x83, 0x82, 0x90, 0x4b }, + .pll_div_regs = { 0xd1, 0x45, 0x11, 0x83, 0x82, 0x90, 0x4b }, }, { .pixclk = 165000000, - .pll_div_regs = { 0x45, 0x11, 0x84, 0x81, 0x90, 0x4b }, - }, { - .pixclk = 180000000, - .pll_div_regs = { 0x4b, 0x10, 0x00, 0x00, 0x80, 0x00 }, + .pll_div_regs = { 0xd1, 0x45, 0x11, 0x84, 0x81, 0x90, 0x4b }, }, { .pixclk = 185625000, - .pll_div_regs = { 0x4e, 0x12, 0x9a, 0x95, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x4e, 0x12, 0x9a, 0x95, 0x80, 0x40 }, }, { .pixclk = 188000000, - .pll_div_regs = { 0x4e, 0x12, 0xa7, 0x10, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x4e, 0x12, 0xa7, 0x10, 0x80, 0x40 }, }, { .pixclk = 198000000, - .pll_div_regs = { 0x52, 0x12, 0x82, 0x01, 0x88, 0x47 }, + .pll_div_regs = { 0xd1, 0x52, 0x12, 0x82, 0x01, 0x88, 0x47 }, }, { .pixclk = 205000000, - .pll_div_regs = { 0x55, 0x12, 0x8c, 0x05, 0x90, 0x4b }, + .pll_div_regs = { 0xd1, 0x55, 0x12, 0x8c, 0x05, 0x90, 0x4b }, }, { .pixclk = 209500000, - .pll_div_regs = { 0x57, 0x12, 0x98, 0x07, 0x90, 0x49 }, + .pll_div_regs = { 0xd1, 0x57, 0x12, 0x98, 0x07, 0x90, 0x49 }, }, { .pixclk = 213000000, - .pll_div_regs = { 0x58, 0x12, 0x84, 0x03, 0x82, 0x41 }, - }, { - .pixclk = 216000000, - .pll_div_regs = { 0x5a, 0x10, 0x00, 0x00, 0x80, 0x00 }, + .pll_div_regs = { 0xd1, 0x58, 0x12, 0x84, 0x03, 0x82, 0x41 }, }, { .pixclk = 216216000, - .pll_div_regs = { 0x5a, 0x12, 0xfd, 0x0c, 0x80, 0x40 }, - }, { - .pixclk = 237600000, - .pll_div_regs = { 0x63, 0x10, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x5a, 0x12, 0xfd, 0x0c, 0x80, 0x40 }, + }, { .pixclk = 254000000, - .pll_div_regs = { 0x69, 0x14, 0x89, 0x08, 0x80, 0x40 }, + .pll_div_regs = { 0xd1, 0x69, 0x14, 0x89, 0x08, 0x80, 0x40 }, }, { .pixclk = 277500000, - .pll_div_regs = { 0x73, 0x15, 0x88, 0x05, 0x90, 0x4d }, - }, { - .pixclk = 288000000, - .pll_div_regs = { 0x78, 0x10, 0x00, 0x00, 0x80, 0x00 }, - }, { + .pll_div_regs = { 0xd1, 0x73, 0x15, 0x88, 0x05, 0x90, 0x4d }, + }, { .pixclk = 297000000, - .pll_div_regs = { 0x7b, 0x15, 0x84, 0x03, 0x90, 0x45 }, + .pll_div_regs = { 0xd1, 0x7b, 0x15, 0x84, 0x03, 0x90, 0x45 }, }, }; @@ -369,29 +282,30 @@ struct reg_settings { }; static const struct reg_settings common_phy_cfg[] = { - { PHY_REG_00, 0x00 }, { PHY_REG_01, 0xd1 }, - { PHY_REG_08, 0x4f }, { PHY_REG_09, 0x30 }, - { PHY_REG_10, 0x33 }, { PHY_REG_11, 0x65 }, + { PHY_REG(0), 0x00 }, + /* PHY_REG(1-7) pix clk specific */ + { PHY_REG(8), 0x4f }, { PHY_REG(9), 0x30 }, + { PHY_REG(10), 0x33 }, { PHY_REG(11), 0x65 }, /* REG12 pixclk specific */ /* REG13 pixclk specific */ /* REG14 pixclk specific */ - { PHY_REG_15, 0x80 }, { PHY_REG_16, 0x6c }, - { PHY_REG_17, 0xf2 }, { PHY_REG_18, 0x67 }, - { PHY_REG_19, 0x00 }, { PHY_REG_20, 0x10 }, + { PHY_REG(15), 0x80 }, { PHY_REG(16), 0x6c }, + { PHY_REG(17), 0xf2 }, { PHY_REG(18), 0x67 }, + { PHY_REG(19), 0x00 }, { PHY_REG(20), 0x10 }, /* REG21 pixclk specific */ - { PHY_REG_22, 0x30 }, { PHY_REG_23, 0x32 }, - { PHY_REG_24, 0x60 }, { PHY_REG_25, 0x8f }, - { PHY_REG_26, 0x00 }, { PHY_REG_27, 0x00 }, - { PHY_REG_28, 0x08 }, { PHY_REG_29, 0x00 }, - { PHY_REG_30, 0x00 }, { PHY_REG_31, 0x00 }, - { PHY_REG_32, 0x00 }, { PHY_REG_33, 0x80 }, - { PHY_REG_34, 0x00 }, { PHY_REG_35, 0x00 }, - { PHY_REG_36, 0x00 }, { PHY_REG_37, 0x00 }, - { PHY_REG_38, 0x00 }, { PHY_REG_39, 0x00 }, - { PHY_REG_40, 0x00 }, { PHY_REG_41, 0xe0 }, - { PHY_REG_42, 0x83 }, { PHY_REG_43, 0x0f }, - { PHY_REG_44, 0x3E }, { PHY_REG_45, 0xf8 }, - { PHY_REG_46, 0x00 }, { PHY_REG_47, 0x00 } + { PHY_REG(22), 0x30 }, { PHY_REG(23), 0x32 }, + { PHY_REG(24), 0x60 }, { PHY_REG(25), 0x8f }, + { PHY_REG(26), 0x00 }, { PHY_REG(27), 0x00 }, + { PHY_REG(28), 0x08 }, { PHY_REG(29), 0x00 }, + { PHY_REG(30), 0x00 }, { PHY_REG(31), 0x00 }, + { PHY_REG(32), 0x00 }, { PHY_REG(33), 0x80 }, + { PHY_REG(34), 0x00 }, { PHY_REG(35), 0x00 }, + { PHY_REG(36), 0x00 }, { PHY_REG(37), 0x00 }, + { PHY_REG(38), 0x00 }, { PHY_REG(39), 0x00 }, + { PHY_REG(40), 0x00 }, { PHY_REG(41), 0xe0 }, + { PHY_REG(42), 0x83 }, { PHY_REG(43), 0x0f }, + { PHY_REG(44), 0x3E }, { PHY_REG(45), 0xf8 }, + { PHY_REG(46), 0x00 }, { PHY_REG(47), 0x00 } }; struct fsl_samsung_hdmi_phy { @@ -411,40 +325,6 @@ to_fsl_samsung_hdmi_phy(struct clk_hw *hw) return container_of(hw, struct fsl_samsung_hdmi_phy, hw); } -static void -fsl_samsung_hdmi_phy_configure_pixclk(struct fsl_samsung_hdmi_phy *phy, - const struct phy_config *cfg) -{ - u8 div = 0x1; - - switch (cfg->pixclk) { - case 22250000 ... 33750000: - div = 0xf; - break; - case 35000000 ... 40000000: - div = 0xb; - break; - case 43200000 ... 47500000: - div = 0x9; - break; - case 50349650 ... 63500000: - div = 0x7; - break; - case 67500000 ... 90000000: - div = 0x5; - break; - case 94000000 ... 148500000: - div = 0x3; - break; - case 154000000 ... 297000000: - div = 0x1; - break; - } - - writeb(REG21_SEL_TX_CK_INV | FIELD_PREP(REG21_PMS_S_MASK, div), - phy->regs + PHY_REG_21); -} - static void fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy, const struct phy_config *cfg) @@ -469,7 +349,7 @@ fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy, break; } - writeb(FIELD_PREP(REG12_CK_DIV_MASK, ilog2(div)), phy->regs + PHY_REG_12); + writeb(FIELD_PREP(REG12_CK_DIV_MASK, ilog2(div)), phy->regs + PHY_REG(12)); /* * Calculation for the frequency lock detector target code (fld_tg_code) @@ -489,11 +369,88 @@ fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy, /* FLD_TOL and FLD_RP_CODE taken from downstream driver */ writeb(FIELD_PREP(REG13_TG_CODE_LOW_MASK, fld_tg_code), - phy->regs + PHY_REG_13); + phy->regs + PHY_REG(13)); writeb(FIELD_PREP(REG14_TOL_MASK, 2) | FIELD_PREP(REG14_RP_CODE_MASK, 2) | FIELD_PREP(REG14_TG_CODE_HIGH_MASK, fld_tg_code >> 8), - phy->regs + PHY_REG_14); + phy->regs + PHY_REG(14)); +} + +static unsigned long fsl_samsung_hdmi_phy_find_pms(unsigned long fout, u8 *p, u16 *m, u8 *s) +{ + unsigned long best_freq = 0; + u32 min_delta = 0xffffffff; + u8 _p, best_p; + u16 _m, best_m; + u8 _s, best_s; + + /* + * Figure 13-78 of the reference manual states the PLL should be TMDS x 5 + * while the TMDS_CLKO should be the PLL / 5. So to calculate the PLL, + * take the pix clock x 5, then return the value of the PLL / 5. + */ + fout *= 5; + + /* The ref manual states the values of 'P' range from 1 to 11 */ + for (_p = 1; _p <= 11; ++_p) { + for (_s = 1; _s <= 16; ++_s) { + u64 tmp; + u32 delta; + + /* s must be one or even */ + if (_s > 1 && (_s & 0x01) == 1) + _s++; + + /* _s cannot be 14 per the TRM */ + if (_s == 14) + continue; + + /* + * TODO: Ref Manual doesn't state the range of _m + * so this should be further refined if possible. + * This range was set based on the original values + * in the lookup table + */ + tmp = (u64)fout * (_p * _s); + do_div(tmp, 24 * MHZ); + _m = tmp; + if (_m < 0x30 || _m > 0x7b) + continue; + + /* + * Rev 2 of the Ref Manual states the + * VCO can range between 750MHz and + * 3GHz. The VCO is assumed to be + * Fvco = (M * f_ref) / P, + * where f_ref is 24MHz. + */ + tmp = (u64)_m * 24 * MHZ; + do_div(tmp, _p); + if (tmp < 750 * MHZ || + tmp > 3000 * MHZ) + continue; + + /* Final frequency after post-divider */ + do_div(tmp, _s); + + delta = abs(fout - tmp); + if (delta < min_delta) { + best_p = _p; + best_s = _s; + best_m = _m; + min_delta = delta; + best_freq = tmp; + } + } + } + + if (best_freq) { + *p = best_p; + *m = best_m; + *s = best_s; + } + + return best_freq / 5; } static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy, @@ -503,22 +460,25 @@ static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy, u8 val; /* HDMI PHY init */ - writeb(REG33_FIX_DA, phy->regs + PHY_REG_33); + writeb(REG33_FIX_DA, phy->regs + PHY_REG(33)); /* common PHY registers */ for (i = 0; i < ARRAY_SIZE(common_phy_cfg); i++) writeb(common_phy_cfg[i].val, phy->regs + common_phy_cfg[i].reg); - /* set individual PLL registers PHY_REG2 ... PHY_REG7 */ + /* set individual PLL registers PHY_REG1 ... PHY_REG7 */ for (i = 0; i < PHY_PLL_DIV_REGS_NUM; i++) - writeb(cfg->pll_div_regs[i], phy->regs + PHY_REG_02 + i * 4); + writeb(cfg->pll_div_regs[i], phy->regs + PHY_REG(1) + i * 4); + + /* High nibble of PHY_REG3 and low nibble of PHY_REG21 both contain 'S' */ + writeb(REG21_SEL_TX_CK_INV | FIELD_PREP(REG21_PMS_S_MASK, + cfg->pll_div_regs[2] >> 4), phy->regs + PHY_REG(21)); - fsl_samsung_hdmi_phy_configure_pixclk(phy, cfg); fsl_samsung_hdmi_phy_configure_pll_lock_det(phy, cfg); - writeb(REG33_FIX_DA | REG33_MODE_SET_DONE, phy->regs + PHY_REG_33); + writeb(REG33_FIX_DA | REG33_MODE_SET_DONE, phy->regs + PHY_REG(33)); - ret = readb_poll_timeout(phy->regs + PHY_REG_34, val, + ret = readb_poll_timeout(phy->regs + PHY_REG(34), val, val & REG34_PLL_LOCK, 50, 20000); if (ret) dev_err(phy->dev, "PLL failed to lock\n"); @@ -537,34 +497,120 @@ static unsigned long phy_clk_recalc_rate(struct clk_hw *hw, return phy->cur_cfg->pixclk; } -static long phy_clk_round_rate(struct clk_hw *hw, - unsigned long rate, unsigned long *parent_rate) +/* Helper function to lookup the available fractional-divider rate */ +static const struct phy_config *fsl_samsung_hdmi_phy_lookup_rate(unsigned long rate) { int i; + /* Search the lookup table */ for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--) if (phy_pll_cfg[i].pixclk <= rate) - return phy_pll_cfg[i].pixclk; + break; - return -EINVAL; + return &phy_pll_cfg[i]; +} + +static void fsl_samsung_hdmi_calculate_phy(struct phy_config *cal_phy, unsigned long rate, + u8 p, u16 m, u8 s) +{ + cal_phy->pixclk = rate; + cal_phy->pll_div_regs[0] = FIELD_PREP(REG01_PMS_P_MASK, p); + cal_phy->pll_div_regs[1] = m; + cal_phy->pll_div_regs[2] = FIELD_PREP(REG03_PMS_S_MASK, s-1); + /* pll_div_regs 3-6 are fixed and pre-defined already */ +} + +static u32 fsl_samsung_hdmi_phy_get_closest_rate(unsigned long rate, + u32 int_div_clk, u32 frac_div_clk) +{ + /* Calculate the absolute value of the differences and return whichever is closest */ + if (abs((long)rate - (long)int_div_clk) < abs((long)(rate - (long)frac_div_clk))) + return int_div_clk; + + return frac_div_clk; +} + +static long phy_clk_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate) +{ + const struct phy_config *fract_div_phy; + u32 int_div_clk; + u16 m; + u8 p, s; + + /* If the clock is out of range return error instead of searching */ + if (rate > 297000000 || rate < 22250000) + return -EINVAL; + + /* Search the fractional divider lookup table */ + fract_div_phy = fsl_samsung_hdmi_phy_lookup_rate(rate); + + /* If the rate is an exact match, return that value */ + if (rate == fract_div_phy->pixclk) + return fract_div_phy->pixclk; + + /* If the exact match isn't found, calculate the integer divider */ + int_div_clk = fsl_samsung_hdmi_phy_find_pms(rate, &p, &m, &s); + + /* If the int_div_clk rate is an exact match, return that value */ + if (int_div_clk == rate) + return int_div_clk; + + /* If neither rate is an exact match, use the value from the LUT */ + return fract_div_phy->pixclk; +} + +static int phy_use_fract_div(struct fsl_samsung_hdmi_phy *phy, const struct phy_config *fract_div_phy) +{ + phy->cur_cfg = fract_div_phy; + dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: using fractional divider rate = %u\n", + phy->cur_cfg->pixclk); + return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg); +} + +static int phy_use_integer_div(struct fsl_samsung_hdmi_phy *phy, + const struct phy_config *int_div_clk) +{ + phy->cur_cfg = &calculated_phy_pll_cfg; + dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: integer divider rate = %u\n", + phy->cur_cfg->pixclk); + return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg); } static int phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw); - int i; + const struct phy_config *fract_div_phy; + u32 int_div_clk; + u16 m; + u8 p, s; - for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--) - if (phy_pll_cfg[i].pixclk <= rate) - break; + /* Search the fractional divider lookup table */ + fract_div_phy = fsl_samsung_hdmi_phy_lookup_rate(rate); - if (i < 0) - return -EINVAL; + /* If the rate is an exact match, use that value */ + if (fract_div_phy->pixclk == rate) + return phy_use_fract_div(phy, fract_div_phy); - phy->cur_cfg = &phy_pll_cfg[i]; + /* + * If the rate from the fractional divider is not exact, check the integer divider, + * and use it if that value is an exact match. + */ + int_div_clk = fsl_samsung_hdmi_phy_find_pms(rate, &p, &m, &s); + fsl_samsung_hdmi_calculate_phy(&calculated_phy_pll_cfg, int_div_clk, p, m, s); + if (int_div_clk == rate) + return phy_use_integer_div(phy, &calculated_phy_pll_cfg); - return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg); + /* + * Compare the difference between the integer clock and the fractional clock against + * the desired clock and which whichever is closest. + */ + if (fsl_samsung_hdmi_phy_get_closest_rate(rate, int_div_clk, + fract_div_phy->pixclk) == fract_div_phy->pixclk) + return phy_use_fract_div(phy, fract_div_phy); + else + return phy_use_integer_div(phy, &calculated_phy_pll_cfg); } static const struct clk_ops phy_clk_ops = { @@ -703,8 +749,8 @@ static const struct of_device_id fsl_samsung_hdmi_phy_of_match[] = { MODULE_DEVICE_TABLE(of, fsl_samsung_hdmi_phy_of_match); static struct platform_driver fsl_samsung_hdmi_phy_driver = { - .probe = fsl_samsung_hdmi_phy_probe, - .remove_new = fsl_samsung_hdmi_phy_remove, + .probe = fsl_samsung_hdmi_phy_probe, + .remove = fsl_samsung_hdmi_phy_remove, .driver = { .name = "fsl-samsung-hdmi-phy", .of_match_table = fsl_samsung_hdmi_phy_of_match, diff --git a/drivers/phy/intel/phy-intel-lgm-combo.c b/drivers/phy/intel/phy-intel-lgm-combo.c index f8e3054a9e597..9ee3cf61cdd00 100644 --- a/drivers/phy/intel/phy-intel-lgm-combo.c +++ b/drivers/phy/intel/phy-intel-lgm-combo.c @@ -605,7 +605,7 @@ static const struct of_device_id of_intel_cbphy_match[] = { static struct platform_driver intel_cbphy_driver = { .probe = intel_cbphy_probe, - .remove_new = intel_cbphy_remove, + .remove = intel_cbphy_remove, .driver = { .name = "intel-combo-phy", .of_match_table = of_intel_cbphy_match, diff --git a/drivers/phy/marvell/phy-mvebu-cp110-utmi.c b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c index 4922a5f3327d5..59903f86b13f5 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c @@ -62,6 +62,8 @@ #define SQ_AMP_CAL_MASK GENMASK(2, 0) #define SQ_AMP_CAL_VAL 1 #define SQ_AMP_CAL_EN BIT(3) +#define UTMI_DIG_CTRL1_REG 0x20 +#define SWAP_DPDM BIT(15) #define UTMI_CTRL_STATUS0_REG 0x24 #define SUSPENDM BIT(22) #define TEST_SEL BIT(25) @@ -99,11 +101,13 @@ struct mvebu_cp110_utmi { * @priv: PHY driver data * @id: PHY port ID * @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL + * @swap_dx: whether to swap d+/d- signals */ struct mvebu_cp110_utmi_port { struct mvebu_cp110_utmi *priv; u32 id; enum usb_dr_mode dr_mode; + bool swap_dx; }; static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port) @@ -159,6 +163,13 @@ static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port) reg &= ~(VDAT_MASK | VSRC_MASK); reg |= (VDAT_VAL << VDAT_OFFSET) | (VSRC_VAL << VSRC_OFFSET); writel(reg, PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG); + + /* Swap D+/D- */ + reg = readl(PORT_REGS(port) + UTMI_DIG_CTRL1_REG); + reg &= ~(SWAP_DPDM); + if (port->swap_dx) + reg |= SWAP_DPDM; + writel(reg, PORT_REGS(port) + UTMI_DIG_CTRL1_REG); } static int mvebu_cp110_utmi_phy_power_off(struct phy *phy) @@ -286,6 +297,7 @@ static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev) struct phy_provider *provider; struct device_node *child; u32 usb_devices = 0; + u32 swap_dx = 0; utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL); if (!utmi) @@ -345,6 +357,10 @@ static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev) } } + of_property_for_each_u32(dev->of_node, "swap-dx-lanes", swap_dx) + if (swap_dx == port_id) + port->swap_dx = 1; + /* Retrieve PHY capabilities */ utmi->ops = &mvebu_cp110_utmi_phy_ops; diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c index 7cb85029fab39..320cf5b50a8cc 100644 --- a/drivers/phy/microchip/sparx5_serdes.c +++ b/drivers/phy/microchip/sparx5_serdes.c @@ -21,23 +21,33 @@ #include "sparx5_serdes.h" -#define SPX5_CMU_MAX 14 - #define SPX5_SERDES_10G_START 13 #define SPX5_SERDES_25G_START 25 #define SPX5_SERDES_6G10G_CNT SPX5_SERDES_25G_START +#define LAN969X_SERDES_10G_CNT 10 + /* Optimal power settings from GUC */ #define SPX5_SERDES_QUIET_MODE_VAL 0x01ef4e0c -enum sparx5_10g28cmu_mode { - SPX5_SD10G28_CMU_MAIN = 0, - SPX5_SD10G28_CMU_AUX1 = 1, - SPX5_SD10G28_CMU_AUX2 = 3, - SPX5_SD10G28_CMU_NONE = 4, - SPX5_SD10G28_CMU_MAX, +/* Register target sizes */ +const unsigned int sparx5_serdes_tsize[TSIZE_LAST] = { + [TC_SD10G_LANE] = 12, + [TC_SD_CMU] = 14, + [TC_SD_CMU_CFG] = 14, + [TC_SD_LANE] = 25, +}; + +const unsigned int lan969x_serdes_tsize[TSIZE_LAST] = { + [TC_SD10G_LANE] = 10, + [TC_SD_CMU] = 6, + [TC_SD_CMU_CFG] = 6, + [TC_SD_LANE] = 10, }; +/* Pointer to the register target size table */ +const unsigned int *tsize; + enum sparx5_sd25g28_mode_preset_type { SPX5_SD25G28_MODE_PRESET_25000, SPX5_SD25G28_MODE_PRESET_10000, @@ -1095,13 +1105,31 @@ static int sparx5_serdes_cmu_get(enum sparx5_10g28cmu_mode mode, int sd_index) return sparx5_serdes_cmu_map[mode][sd_index]; } +/* Map of 6G/10G serdes mode and index to CMU index. */ +static const int +lan969x_serdes_cmu_map[SPX5_SD10G28_CMU_MAX][LAN969X_SERDES_10G_CNT] = { + [SPX5_SD10G28_CMU_MAIN] = { 2, 2, 2, 2, 2, + 2, 2, 2, 5, 5 }, + [SPX5_SD10G28_CMU_AUX1] = { 0, 0, 3, 3, 3, + 3, 3, 3, 3, 3 }, + [SPX5_SD10G28_CMU_AUX2] = { 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4 }, + [SPX5_SD10G28_CMU_NONE] = { 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4 }, +}; + +static int lan969x_serdes_cmu_get(enum sparx5_10g28cmu_mode mode, int sd_index) +{ + return lan969x_serdes_cmu_map[mode][sd_index]; +} + static void sparx5_serdes_cmu_power_off(struct sparx5_serdes_private *priv) { void __iomem *cmu_inst, *cmu_cfg_inst; int i; /* Power down each CMU */ - for (i = 0; i < SPX5_CMU_MAX; i++) { + for (i = 0; i < priv->data->consts.cmu_max; i++) { cmu_inst = sdx5_inst_get(priv, TARGET_SD_CMU, i); cmu_cfg_inst = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, i); @@ -1650,7 +1678,7 @@ static int sparx5_sd10g28_apply_params(struct sparx5_serdes_macro *macro, if (params->skip_cmu_cfg) return 0; - cmu_idx = sparx5_serdes_cmu_get(params->cmu_sel, lane_index); + cmu_idx = priv->data->ops.serdes_cmu_get(params->cmu_sel, macro->sidx); err = sparx5_cmu_cfg(priv, cmu_idx); if (err) return err; @@ -2183,6 +2211,10 @@ static int sparx5_serdes_clock_config(struct sparx5_serdes_macro *macro) { struct sparx5_serdes_private *priv = macro->priv; + /* Clock is auto-detected in 100Base-FX mode on lan969x */ + if (priv->data->type == SPX5_TARGET_LAN969X) + return 0; + if (macro->serdesmode == SPX5_SD_MODE_100FX) { u32 freq = priv->coreclock == 250000000 ? 2 : priv->coreclock == 500000000 ? 1 : 0; @@ -2297,10 +2329,12 @@ static int sparx5_serdes_set_speed(struct phy *phy, int speed) { struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); - if (macro->sidx < SPX5_SERDES_10G_START && speed > SPEED_5000) - return -EINVAL; - if (macro->sidx < SPX5_SERDES_25G_START && speed > SPEED_10000) - return -EINVAL; + if (macro->priv->data->type == SPX5_TARGET_SPARX5) { + if (macro->sidx < SPX5_SERDES_10G_START && speed > SPEED_5000) + return -EINVAL; + if (macro->sidx < SPX5_SERDES_25G_START && speed > SPEED_10000) + return -EINVAL; + } if (speed != macro->speed) { macro->speed = speed; if (macro->serdesmode != SPX5_SD_MODE_NONE) @@ -2337,11 +2371,14 @@ static int sparx5_serdes_validate(struct phy *phy, enum phy_mode mode, if (macro->speed == 0) return -EINVAL; - if (macro->sidx < SPX5_SERDES_10G_START && macro->speed > SPEED_5000) - return -EINVAL; - if (macro->sidx < SPX5_SERDES_25G_START && macro->speed > SPEED_10000) - return -EINVAL; - + if (macro->priv->data->type == SPX5_TARGET_SPARX5) { + if (macro->sidx < SPX5_SERDES_10G_START && + macro->speed > SPEED_5000) + return -EINVAL; + if (macro->sidx < SPX5_SERDES_25G_START && + macro->speed > SPEED_10000) + return -EINVAL; + } switch (submode) { case PHY_INTERFACE_MODE_1000BASEX: if (macro->speed != SPEED_100 && /* This is for 100BASE-FX */ @@ -2375,6 +2412,26 @@ static const struct phy_ops sparx5_serdes_ops = { .owner = THIS_MODULE, }; +static void sparx5_serdes_type_set(struct sparx5_serdes_macro *macro, int sidx) +{ + if (sidx < SPX5_SERDES_10G_START) { + macro->serdestype = SPX5_SDT_6G; + macro->stpidx = macro->sidx; + } else if (sidx < SPX5_SERDES_25G_START) { + macro->serdestype = SPX5_SDT_10G; + macro->stpidx = macro->sidx - SPX5_SERDES_10G_START; + } else { + macro->serdestype = SPX5_SDT_25G; + macro->stpidx = macro->sidx - SPX5_SERDES_25G_START; + } +} + +static void lan969x_serdes_type_set(struct sparx5_serdes_macro *macro, int sidx) +{ + macro->serdestype = SPX5_SDT_10G; + macro->stpidx = macro->sidx; +} + static int sparx5_phy_create(struct sparx5_serdes_private *priv, int idx, struct phy **phy) { @@ -2391,16 +2448,8 @@ static int sparx5_phy_create(struct sparx5_serdes_private *priv, macro->sidx = idx; macro->priv = priv; macro->speed = SPEED_UNKNOWN; - if (idx < SPX5_SERDES_10G_START) { - macro->serdestype = SPX5_SDT_6G; - macro->stpidx = macro->sidx; - } else if (idx < SPX5_SERDES_25G_START) { - macro->serdestype = SPX5_SDT_10G; - macro->stpidx = macro->sidx - SPX5_SERDES_10G_START; - } else { - macro->serdestype = SPX5_SDT_25G; - macro->stpidx = macro->sidx - SPX5_SERDES_25G_START; - } + + priv->data->ops.serdes_type_set(macro, idx); phy_set_drvdata(*phy, macro); @@ -2507,6 +2556,71 @@ static struct sparx5_serdes_io_resource sparx5_serdes_iomap[] = { { TARGET_SD_LANE_25G + 7, 0x5c8000 }, /* 0x610dd0000: sd_lane_25g_32 */ }; +static const struct sparx5_serdes_io_resource lan969x_serdes_iomap[] = { + { TARGET_SD_CMU, 0x0 }, /* 0xe3410000 */ + { TARGET_SD_CMU + 1, 0x8000 }, /* 0xe3418000 */ + { TARGET_SD_CMU + 2, 0x10000 }, /* 0xe3420000 */ + { TARGET_SD_CMU + 3, 0x18000 }, /* 0xe3428000 */ + { TARGET_SD_CMU + 4, 0x20000 }, /* 0xe3430000 */ + { TARGET_SD_CMU + 5, 0x28000 }, /* 0xe3438000 */ + { TARGET_SD_CMU_CFG, 0x30000 }, /* 0xe3440000 */ + { TARGET_SD_CMU_CFG + 1, 0x38000 }, /* 0xe3448000 */ + { TARGET_SD_CMU_CFG + 2, 0x40000 }, /* 0xe3450000 */ + { TARGET_SD_CMU_CFG + 3, 0x48000 }, /* 0xe3458000 */ + { TARGET_SD_CMU_CFG + 4, 0x50000 }, /* 0xe3460000 */ + { TARGET_SD_CMU_CFG + 5, 0x58000 }, /* 0xe3468000 */ + { TARGET_SD10G_LANE, 0x60000 }, /* 0xe3470000 */ + { TARGET_SD10G_LANE + 1, 0x68000 }, /* 0xe3478000 */ + { TARGET_SD10G_LANE + 2, 0x70000 }, /* 0xe3480000 */ + { TARGET_SD10G_LANE + 3, 0x78000 }, /* 0xe3488000 */ + { TARGET_SD10G_LANE + 4, 0x80000 }, /* 0xe3490000 */ + { TARGET_SD10G_LANE + 5, 0x88000 }, /* 0xe3498000 */ + { TARGET_SD10G_LANE + 6, 0x90000 }, /* 0xe34a0000 */ + { TARGET_SD10G_LANE + 7, 0x98000 }, /* 0xe34a8000 */ + { TARGET_SD10G_LANE + 8, 0xa0000 }, /* 0xe34b0000 */ + { TARGET_SD10G_LANE + 9, 0xa8000 }, /* 0xe34b8000 */ + { TARGET_SD_LANE, 0x100000 }, /* 0xe3510000 */ + { TARGET_SD_LANE + 1, 0x108000 }, /* 0xe3518000 */ + { TARGET_SD_LANE + 2, 0x110000 }, /* 0xe3520000 */ + { TARGET_SD_LANE + 3, 0x118000 }, /* 0xe3528000 */ + { TARGET_SD_LANE + 4, 0x120000 }, /* 0xe3530000 */ + { TARGET_SD_LANE + 5, 0x128000 }, /* 0xe3538000 */ + { TARGET_SD_LANE + 6, 0x130000 }, /* 0xe3540000 */ + { TARGET_SD_LANE + 7, 0x138000 }, /* 0xe3548000 */ + { TARGET_SD_LANE + 8, 0x140000 }, /* 0xe3550000 */ + { TARGET_SD_LANE + 9, 0x148000 }, /* 0xe3558000 */ +}; + +static const struct sparx5_serdes_match_data sparx5_desc = { + .type = SPX5_TARGET_SPARX5, + .iomap = sparx5_serdes_iomap, + .iomap_size = ARRAY_SIZE(sparx5_serdes_iomap), + .tsize = sparx5_serdes_tsize, + .consts = { + .sd_max = 33, + .cmu_max = 14, + }, + .ops = { + .serdes_type_set = &sparx5_serdes_type_set, + .serdes_cmu_get = &sparx5_serdes_cmu_get, + }, +}; + +static const struct sparx5_serdes_match_data lan969x_desc = { + .type = SPX5_TARGET_LAN969X, + .iomap = lan969x_serdes_iomap, + .iomap_size = ARRAY_SIZE(lan969x_serdes_iomap), + .tsize = lan969x_serdes_tsize, + .consts = { + .sd_max = 10, + .cmu_max = 6, + }, + .ops = { + .serdes_type_set = &lan969x_serdes_type_set, + .serdes_cmu_get = &lan969x_serdes_cmu_get, + } +}; + /* Client lookup function, uses serdes index */ static struct phy *sparx5_serdes_xlate(struct device *dev, const struct of_phandle_args *args) @@ -2521,7 +2635,7 @@ static struct phy *sparx5_serdes_xlate(struct device *dev, sidx = args->args[0]; /* Check validity: ERR_PTR(-ENODEV) if not valid */ - for (idx = 0; idx < SPX5_SERDES_MAX; idx++) { + for (idx = 0; idx < priv->data->consts.sd_max; idx++) { struct sparx5_serdes_macro *macro = phy_get_drvdata(priv->phys[idx]); @@ -2555,6 +2669,12 @@ static int sparx5_serdes_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->dev = &pdev->dev; + priv->data = device_get_match_data(priv->dev); + if (!priv->data) + return -EINVAL; + + tsize = priv->data->tsize; + /* Get coreclock */ clk = devm_clk_get(priv->dev, NULL); if (IS_ERR(clk)) { @@ -2579,19 +2699,21 @@ static int sparx5_serdes_probe(struct platform_device *pdev) iores->name); return -ENOMEM; } - for (idx = 0; idx < ARRAY_SIZE(sparx5_serdes_iomap); idx++) { - struct sparx5_serdes_io_resource *iomap = &sparx5_serdes_iomap[idx]; + for (idx = 0; idx < priv->data->iomap_size; idx++) { + const struct sparx5_serdes_io_resource *iomap = + &priv->data->iomap[idx]; priv->regs[iomap->id] = iomem + iomap->offset; } - for (idx = 0; idx < SPX5_SERDES_MAX; idx++) { + for (idx = 0; idx < priv->data->consts.sd_max; idx++) { err = sparx5_phy_create(priv, idx, &priv->phys[idx]); if (err) return err; } - /* Power down all CMUs by default */ - sparx5_serdes_cmu_power_off(priv); + /* Power down all CMU's by default */ + if (priv->data->type == SPX5_TARGET_SPARX5) + sparx5_serdes_cmu_power_off(priv); provider = devm_of_phy_provider_register(priv->dev, sparx5_serdes_xlate); @@ -2599,7 +2721,8 @@ static int sparx5_serdes_probe(struct platform_device *pdev) } static const struct of_device_id sparx5_serdes_match[] = { - { .compatible = "microchip,sparx5-serdes" }, + { .compatible = "microchip,sparx5-serdes", .data = &sparx5_desc }, + { .compatible = "microchip,lan9691-serdes", .data = &lan969x_desc }, { } }; MODULE_DEVICE_TABLE(of, sparx5_serdes_match); diff --git a/drivers/phy/microchip/sparx5_serdes.h b/drivers/phy/microchip/sparx5_serdes.h index 13f94a29225a4..d7093d0b09c0c 100644 --- a/drivers/phy/microchip/sparx5_serdes.h +++ b/drivers/phy/microchip/sparx5_serdes.h @@ -26,11 +26,18 @@ enum sparx5_serdes_mode { SPX5_SD_MODE_SFI, }; -struct sparx5_serdes_private { - struct device *dev; - void __iomem *regs[NUM_TARGETS]; - struct phy *phys[SPX5_SERDES_MAX]; - unsigned long coreclock; +enum sparx5_10g28cmu_mode { + SPX5_SD10G28_CMU_MAIN = 0, + SPX5_SD10G28_CMU_AUX1 = 1, + SPX5_SD10G28_CMU_AUX2 = 3, + SPX5_SD10G28_CMU_NONE = 4, + SPX5_SD10G28_CMU_MAX, +}; + +enum sparx5_target { + SPX5_TARGET_SPARX5, + SPX5_TARGET_LAN969X, + }; struct sparx5_serdes_macro { @@ -44,6 +51,33 @@ struct sparx5_serdes_macro { enum phy_media media; }; +struct sparx5_serdes_consts { + int sd_max; + int cmu_max; +}; + +struct sparx5_serdes_ops { + void (*serdes_type_set)(struct sparx5_serdes_macro *macro, int sidx); + int (*serdes_cmu_get)(enum sparx5_10g28cmu_mode mode, int sd_index); +}; + +struct sparx5_serdes_match_data { + enum sparx5_target type; + const struct sparx5_serdes_consts consts; + const struct sparx5_serdes_ops ops; + const struct sparx5_serdes_io_resource *iomap; + int iomap_size; + const unsigned int *tsize; +}; + +struct sparx5_serdes_private { + struct device *dev; + void __iomem *regs[NUM_TARGETS]; + struct phy *phys[SPX5_SERDES_MAX]; + unsigned long coreclock; + const struct sparx5_serdes_match_data *data; +}; + /* Read, Write and modify registers content. * The register definition macros start at the id */ diff --git a/drivers/phy/microchip/sparx5_serdes_regs.h b/drivers/phy/microchip/sparx5_serdes_regs.h index d0543fd3dc94d..11c4fdc593fad 100644 --- a/drivers/phy/microchip/sparx5_serdes_regs.h +++ b/drivers/phy/microchip/sparx5_serdes_regs.h @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ * Microchip Sparx5 SerDes driver * - * Copyright (c) 2020 Microchip Technology Inc. + * Copyright (c) 2024 Microchip Technology Inc. */ -/* This file is autogenerated by cml-utils 2020-11-16 13:11:27 +0100. - * Commit ID: 13bdf073131d8bf40c54901df6988ae4e9c8f29f +/* This file is autogenerated by cml-utils 2023-04-13 15:02:00 +0200. + * Commit ID: 5ac560288d46048f872ecdb8add53717f1efc0e1 */ #ifndef _SPARX5_SERDES_REGS_H_ @@ -26,10 +26,25 @@ enum sparx5_serdes_target { NUM_TARGETS = 332 }; +enum sparx5_serdes_tsize_enum { + TC_SD10G_LANE, + TC_SD_CMU, + TC_SD_CMU_CFG, + TC_SD_LANE, + TSIZE_LAST, +}; + +/* sparx5_serdes.c */ +extern const unsigned int *tsize; + +#define TSIZE(o) tsize[o] + #define __REG(...) __VA_ARGS__ -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_01 */ -#define SD10G_LANE_LANE_01(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 4, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_01 */ +#define SD10G_LANE_LANE_01(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 4, 0, \ + 1, 4) #define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0 GENMASK(2, 0) #define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_SET(x)\ @@ -49,8 +64,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_01_CFG_RXDET_STR_GET(x)\ FIELD_GET(SD10G_LANE_LANE_01_CFG_RXDET_STR, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_02 */ -#define SD10G_LANE_LANE_02(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 8, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_02 */ +#define SD10G_LANE_LANE_02(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 8, 0, \ + 1, 4) #define SD10G_LANE_LANE_02_CFG_EN_ADV BIT(0) #define SD10G_LANE_LANE_02_CFG_EN_ADV_SET(x)\ @@ -82,8 +99,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_03 */ -#define SD10G_LANE_LANE_03(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 12, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_03 */ +#define SD10G_LANE_LANE_03(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 12, 0, \ + 1, 4) #define SD10G_LANE_LANE_03_CFG_TAP_MAIN BIT(0) #define SD10G_LANE_LANE_03_CFG_TAP_MAIN_SET(x)\ @@ -91,8 +110,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_03_CFG_TAP_MAIN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_03_CFG_TAP_MAIN, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_04 */ -#define SD10G_LANE_LANE_04(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 16, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_04 */ +#define SD10G_LANE_LANE_04(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 16, 0, \ + 1, 4) #define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0 GENMASK(4, 0) #define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_SET(x)\ @@ -100,8 +121,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_06 */ -#define SD10G_LANE_LANE_06(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 24, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_06 */ +#define SD10G_LANE_LANE_06(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 24, 0, \ + 1, 4) #define SD10G_LANE_LANE_06_CFG_PD_DRIVER BIT(0) #define SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(x)\ @@ -139,8 +162,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_06_CFG_EN_PREEMPH_GET(x)\ FIELD_GET(SD10G_LANE_LANE_06_CFG_EN_PREEMPH, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0B */ -#define SD10G_LANE_LANE_0B(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 44, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0B */ +#define SD10G_LANE_LANE_0B(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 44, 0, \ + 1, 4) #define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0 GENMASK(3, 0) #define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_SET(x)\ @@ -172,8 +197,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ_GET(x)\ FIELD_GET(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0C */ -#define SD10G_LANE_LANE_0C(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 48, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0C */ +#define SD10G_LANE_LANE_0C(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 48, 0, \ + 1, 4) #define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE BIT(0) #define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_SET(x)\ @@ -223,8 +250,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12_GET(x)\ FIELD_GET(SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0D */ -#define SD10G_LANE_LANE_0D(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 52, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0D */ +#define SD10G_LANE_LANE_0D(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 52, 0, \ + 1, 4) #define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0 GENMASK(1, 0) #define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0_SET(x)\ @@ -238,8 +267,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_0D_CFG_EQR_BYP_GET(x)\ FIELD_GET(SD10G_LANE_LANE_0D_CFG_EQR_BYP, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0E */ -#define SD10G_LANE_LANE_0E(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 56, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0E */ +#define SD10G_LANE_LANE_0E(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 56, 0, \ + 1, 4) #define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0 GENMASK(3, 0) #define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_SET(x)\ @@ -265,8 +296,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0F */ -#define SD10G_LANE_LANE_0F(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 60, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0F */ +#define SD10G_LANE_LANE_0F(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 60, 0, \ + 1, 4) #define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0 GENMASK(7, 0) #define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_SET(x)\ @@ -274,8 +307,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_13 */ -#define SD10G_LANE_LANE_13(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 76, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_13 */ +#define SD10G_LANE_LANE_13(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 76, 0, \ + 1, 4) #define SD10G_LANE_LANE_13_CFG_DCDR_PD BIT(0) #define SD10G_LANE_LANE_13_CFG_DCDR_PD_SET(x)\ @@ -295,8 +330,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_13_CFG_CDRCK_EN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_13_CFG_CDRCK_EN, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_14 */ -#define SD10G_LANE_LANE_14(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 80, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_14 */ +#define SD10G_LANE_LANE_14(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 80, 0, \ + 1, 4) #define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0 GENMASK(7, 0) #define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_SET(x)\ @@ -304,8 +341,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_15 */ -#define SD10G_LANE_LANE_15(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 84, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_15 */ +#define SD10G_LANE_LANE_15(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 84, 0, \ + 1, 4) #define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8 GENMASK(7, 0) #define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_SET(x)\ @@ -313,8 +352,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_GET(x)\ FIELD_GET(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_16 */ -#define SD10G_LANE_LANE_16(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 88, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_16 */ +#define SD10G_LANE_LANE_16(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 88, 0, \ + 1, 4) #define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16 GENMASK(7, 0) #define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_SET(x)\ @@ -322,8 +363,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_GET(x)\ FIELD_GET(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_1A */ -#define SD10G_LANE_LANE_1A(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 104, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_1A */ +#define SD10G_LANE_LANE_1A(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 104, 0,\ + 1, 4) #define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN BIT(0) #define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN_SET(x)\ @@ -355,8 +398,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_22 */ -#define SD10G_LANE_LANE_22(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 136, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_22 */ +#define SD10G_LANE_LANE_22(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 136, 0,\ + 1, 4) #define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1 GENMASK(4, 0) #define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_SET(x)\ @@ -364,8 +409,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_GET(x)\ FIELD_GET(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_23 */ -#define SD10G_LANE_LANE_23(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 140, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_23 */ +#define SD10G_LANE_LANE_23(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 140, 0,\ + 1, 4) #define SD10G_LANE_LANE_23_CFG_DFE_PD BIT(0) #define SD10G_LANE_LANE_23_CFG_DFE_PD_SET(x)\ @@ -397,8 +444,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_24 */ -#define SD10G_LANE_LANE_24(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 144, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_24 */ +#define SD10G_LANE_LANE_24(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 144, 0,\ + 1, 4) #define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0 GENMASK(3, 0) #define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_SET(x)\ @@ -412,8 +461,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_26 */ -#define SD10G_LANE_LANE_26(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 152, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_26 */ +#define SD10G_LANE_LANE_26(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 152, 0,\ + 1, 4) #define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0 GENMASK(7, 0) #define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_SET(x)\ @@ -421,8 +472,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_2F */ -#define SD10G_LANE_LANE_2F(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 188, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_2F */ +#define SD10G_LANE_LANE_2F(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 188, 0,\ + 1, 4) #define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0 GENMASK(2, 0) #define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_SET(x)\ @@ -436,8 +489,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_30 */ -#define SD10G_LANE_LANE_30(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 192, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_30 */ +#define SD10G_LANE_LANE_30(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 192, 0,\ + 1, 4) #define SD10G_LANE_LANE_30_CFG_SUMMER_EN BIT(0) #define SD10G_LANE_LANE_30_CFG_SUMMER_EN_SET(x)\ @@ -451,8 +506,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_31 */ -#define SD10G_LANE_LANE_31(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 196, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_31 */ +#define SD10G_LANE_LANE_31(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 196, 0,\ + 1, 4) #define SD10G_LANE_LANE_31_CFG_PI_RSTN BIT(0) #define SD10G_LANE_LANE_31_CFG_PI_RSTN_SET(x)\ @@ -490,8 +547,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_31_CFG_R50_EN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_31_CFG_R50_EN, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_32 */ -#define SD10G_LANE_LANE_32(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 200, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_32 */ +#define SD10G_LANE_LANE_32(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 200, 0,\ + 1, 4) #define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0 GENMASK(1, 0) #define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0_SET(x)\ @@ -505,8 +564,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_33 */ -#define SD10G_LANE_LANE_33(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 204, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_33 */ +#define SD10G_LANE_LANE_33(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 204, 0,\ + 1, 4) #define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0 GENMASK(2, 0) #define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_SET(x)\ @@ -520,8 +581,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_35 */ -#define SD10G_LANE_LANE_35(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 212, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_35 */ +#define SD10G_LANE_LANE_35(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 212, 0,\ + 1, 4) #define SD10G_LANE_LANE_35_CFG_TXRATE_1_0 GENMASK(1, 0) #define SD10G_LANE_LANE_35_CFG_TXRATE_1_0_SET(x)\ @@ -535,8 +598,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_35_CFG_RXRATE_1_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_35_CFG_RXRATE_1_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_36 */ -#define SD10G_LANE_LANE_36(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 216, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_36 */ +#define SD10G_LANE_LANE_36(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 216, 0,\ + 1, 4) #define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0 GENMASK(1, 0) #define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_SET(x)\ @@ -568,8 +633,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_36_CFG_PRBS_SETB_GET(x)\ FIELD_GET(SD10G_LANE_LANE_36_CFG_PRBS_SETB, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_37 */ -#define SD10G_LANE_LANE_37(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 220, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_37 */ +#define SD10G_LANE_LANE_37(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 220, 0,\ + 1, 4) #define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD BIT(0) #define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD_SET(x)\ @@ -595,8 +662,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_39 */ -#define SD10G_LANE_LANE_39(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 228, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_39 */ +#define SD10G_LANE_LANE_39(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 228, 0,\ + 1, 4) #define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0 GENMASK(2, 0) #define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0_SET(x)\ @@ -610,8 +679,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_39_CFG_RX_SSC_LH_GET(x)\ FIELD_GET(SD10G_LANE_LANE_39_CFG_RX_SSC_LH, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3A */ -#define SD10G_LANE_LANE_3A(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 232, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3A */ +#define SD10G_LANE_LANE_3A(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 232, 0,\ + 1, 4) #define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0 GENMASK(3, 0) #define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0_SET(x)\ @@ -625,8 +696,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3C */ -#define SD10G_LANE_LANE_3C(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 240, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3C */ +#define SD10G_LANE_LANE_3C(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 240, 0,\ + 1, 4) #define SD10G_LANE_LANE_3C_CFG_DIS_ACC BIT(0) #define SD10G_LANE_LANE_3C_CFG_DIS_ACC_SET(x)\ @@ -640,8 +713,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_GET(x)\ FIELD_GET(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_40 */ -#define SD10G_LANE_LANE_40(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 256, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_40 */ +#define SD10G_LANE_LANE_40(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 256, 0,\ + 1, 4) #define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0 GENMASK(7, 0) #define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_SET(x)\ @@ -649,8 +724,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_41 */ -#define SD10G_LANE_LANE_41(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 260, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_41 */ +#define SD10G_LANE_LANE_41(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 260, 0,\ + 1, 4) #define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8 GENMASK(7, 0) #define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_SET(x)\ @@ -658,8 +735,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_GET(x)\ FIELD_GET(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8, x) -/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_42 */ -#define SD10G_LANE_LANE_42(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 264, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_42 */ +#define SD10G_LANE_LANE_42(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 0, 0, 1, 288, 264, 0,\ + 1, 4) #define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0 GENMASK(2, 0) #define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_SET(x)\ @@ -673,8 +752,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_48 */ -#define SD10G_LANE_LANE_48(t) __REG(TARGET_SD10G_LANE, t, 12, 288, 0, 1, 40, 0, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_48 */ +#define SD10G_LANE_LANE_48(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 288, 0, 1, 40, 0, 0, \ + 1, 4) #define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0 GENMASK(3, 0) #define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_SET(x)\ @@ -694,8 +775,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_48_CFG_CLK_ENQ_GET(x)\ FIELD_GET(SD10G_LANE_LANE_48_CFG_CLK_ENQ, x) -/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_50 */ -#define SD10G_LANE_LANE_50(t) __REG(TARGET_SD10G_LANE, t, 12, 288, 0, 1, 40, 32, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_50 */ +#define SD10G_LANE_LANE_50(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 288, 0, 1, 40, 32, 0,\ + 1, 4) #define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0 GENMASK(1, 0) #define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0_SET(x)\ @@ -727,8 +810,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_50_CFG_JT_EN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_50_CFG_JT_EN, x) -/* SD10G_LANE_TARGET:LANE_GRP_2:LANE_52 */ -#define SD10G_LANE_LANE_52(t) __REG(TARGET_SD10G_LANE, t, 12, 328, 0, 1, 24, 0, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_2:LANE_52 */ +#define SD10G_LANE_LANE_52(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 328, 0, 1, 24, 0, 0, \ + 1, 4) #define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0 GENMASK(5, 0) #define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_SET(x)\ @@ -736,8 +821,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_4:LANE_83 */ -#define SD10G_LANE_LANE_83(t) __REG(TARGET_SD10G_LANE, t, 12, 464, 0, 1, 112, 60, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_4:LANE_83 */ +#define SD10G_LANE_LANE_83(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 464, 0, 1, 112, 60, \ + 0, 1, 4) #define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE BIT(0) #define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE_SET(x)\ @@ -781,8 +868,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_83_R_CTLE_RSTN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_83_R_CTLE_RSTN, x) -/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_93 */ -#define SD10G_LANE_LANE_93(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 12, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_93 */ +#define SD10G_LANE_LANE_93(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 576, 0, 1, 64, 12, 0,\ + 1, 4) #define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN BIT(0) #define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN_SET(x)\ @@ -832,8 +921,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT_GET(x)\ FIELD_GET(SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT, x) -/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_94 */ -#define SD10G_LANE_LANE_94(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 16, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_94 */ +#define SD10G_LANE_LANE_94(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 576, 0, 1, 64, 16, 0,\ + 1, 4) #define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0 GENMASK(2, 0) #define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_SET(x)\ @@ -865,8 +956,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_94_R_SWING_REG_GET(x)\ FIELD_GET(SD10G_LANE_LANE_94_R_SWING_REG, x) -/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_9E */ -#define SD10G_LANE_LANE_9E(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 56, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_9E */ +#define SD10G_LANE_LANE_9E(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 576, 0, 1, 64, 56, 0,\ + 1, 4) #define SD10G_LANE_LANE_9E_R_RXEQ_REG BIT(0) #define SD10G_LANE_LANE_9E_R_RXEQ_REG_SET(x)\ @@ -886,8 +979,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_GET(x)\ FIELD_GET(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN, x) -/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A1 */ -#define SD10G_LANE_LANE_A1(t) __REG(TARGET_SD10G_LANE, t, 12, 640, 0, 1, 128, 4, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A1 */ +#define SD10G_LANE_LANE_A1(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 640, 0, 1, 128, 4, 0,\ + 1, 4) #define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0 GENMASK(1, 0) #define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0_SET(x)\ @@ -919,8 +1014,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_A1_R_PCLK_GATING_GET(x)\ FIELD_GET(SD10G_LANE_LANE_A1_R_PCLK_GATING, x) -/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A2 */ -#define SD10G_LANE_LANE_A2(t) __REG(TARGET_SD10G_LANE, t, 12, 640, 0, 1, 128, 8, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A2 */ +#define SD10G_LANE_LANE_A2(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 640, 0, 1, 128, 8, 0,\ + 1, 4) #define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0 GENMASK(4, 0) #define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_SET(x)\ @@ -928,8 +1025,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_GET(x)\ FIELD_GET(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0, x) -/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */ -#define SD10G_LANE_LANE_DF(t) __REG(TARGET_SD10G_LANE, t, 12, 832, 0, 1, 84, 60, 0, 1, 4) +/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */ +#define SD10G_LANE_LANE_DF(t) \ + __REG(TARGET_SD10G_LANE, t, TSIZE(TC_SD10G_LANE), 832, 0, 1, 84, 60, 0,\ + 1, 4) #define SD10G_LANE_LANE_DF_LOL_UDL BIT(0) #define SD10G_LANE_LANE_DF_LOL_UDL_SET(x)\ @@ -955,8 +1054,10 @@ enum sparx5_serdes_target { #define SD10G_LANE_LANE_DF_SQUELCH_GET(x)\ FIELD_GET(SD10G_LANE_LANE_DF_SQUELCH, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_09 */ -#define SD25G_LANE_CMU_09(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 36, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_09 */ +#define SD25G_LANE_CMU_09(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 36, 0, 1, 4) #define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN BIT(0) #define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN_SET(x)\ @@ -988,8 +1089,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_0B */ -#define SD25G_LANE_CMU_0B(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 44, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_0B */ +#define SD25G_LANE_CMU_0B(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 44, 0, 1, 4) #define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT BIT(0) #define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT_SET(x)\ @@ -1039,8 +1142,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_GET(x)\ FIELD_GET(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_0C */ -#define SD25G_LANE_CMU_0C(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 48, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_0C */ +#define SD25G_LANE_CMU_0C(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 48, 0, 1, 4) #define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET BIT(0) #define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_SET(x)\ @@ -1072,8 +1177,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_0D */ -#define SD25G_LANE_CMU_0D(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 52, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_0D */ +#define SD25G_LANE_CMU_0D(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 52, 0, 1, 4) #define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD BIT(0) #define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD_SET(x)\ @@ -1105,8 +1212,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_0E */ -#define SD25G_LANE_CMU_0E(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 56, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_0E */ +#define SD25G_LANE_CMU_0E(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 56, 0, 1, 4) #define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0 GENMASK(3, 0) #define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_SET(x)\ @@ -1120,8 +1229,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD_GET(x)\ FIELD_GET(SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_13 */ -#define SD25G_LANE_CMU_13(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 76, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_13 */ +#define SD25G_LANE_CMU_13(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 76, 0, 1, 4) #define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0 GENMASK(3, 0) #define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_SET(x)\ @@ -1135,8 +1246,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_13_CFG_JT_EN_GET(x)\ FIELD_GET(SD25G_LANE_CMU_13_CFG_JT_EN, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_18 */ -#define SD25G_LANE_CMU_18(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 96, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_18 */ +#define SD25G_LANE_CMU_18(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 96, 0, 1, 4) #define SD25G_LANE_CMU_18_R_PLL_RSTN BIT(0) #define SD25G_LANE_CMU_18_R_PLL_RSTN_SET(x)\ @@ -1162,8 +1275,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_19 */ -#define SD25G_LANE_CMU_19(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 100, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_19 */ +#define SD25G_LANE_CMU_19(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 100, 0, 1, 4) #define SD25G_LANE_CMU_19_R_CK_RESETB BIT(0) #define SD25G_LANE_CMU_19_R_CK_RESETB_SET(x)\ @@ -1177,8 +1292,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_19_R_PLL_DLOL_EN_GET(x)\ FIELD_GET(SD25G_LANE_CMU_19_R_PLL_DLOL_EN, x) -/* SD25G_TARGET:CMU_GRP_0:CMU_1A */ -#define SD25G_LANE_CMU_1A(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 104, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_0:CMU_1A */ +#define SD25G_LANE_CMU_1A(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 104, 0, 1, 4) #define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0 GENMASK(2, 0) #define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_SET(x)\ @@ -1204,8 +1321,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_1A_R_REG_MANUAL_GET(x)\ FIELD_GET(SD25G_LANE_CMU_1A_R_REG_MANUAL, x) -/* SD25G_TARGET:CMU_GRP_1:CMU_2A */ -#define SD25G_LANE_CMU_2A(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 36, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_1:CMU_2A */ +#define SD25G_LANE_CMU_2A(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 36, 0, 1, 4) #define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0 GENMASK(1, 0) #define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0_SET(x)\ @@ -1225,8 +1344,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_GET(x)\ FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS, x) -/* SD25G_TARGET:CMU_GRP_1:CMU_30 */ -#define SD25G_LANE_CMU_30(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 60, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_1:CMU_30 */ +#define SD25G_LANE_CMU_30(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 60, 0, 1, 4) #define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0 GENMASK(2, 0) #define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_SET(x)\ @@ -1240,8 +1361,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0, x) -/* SD25G_TARGET:CMU_GRP_1:CMU_31 */ -#define SD25G_LANE_CMU_31(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 64, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_1:CMU_31 */ +#define SD25G_LANE_CMU_31(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 64, 0, 1, 4) #define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0 GENMASK(7, 0) #define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_SET(x)\ @@ -1249,8 +1372,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0, x) -/* SD25G_TARGET:CMU_GRP_2:CMU_40 */ -#define SD25G_LANE_CMU_40(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 0, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_2:CMU_40 */ +#define SD25G_LANE_CMU_40(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 0, 0, 1, 4) #define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL BIT(0) #define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL_SET(x)\ @@ -1288,8 +1413,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST_GET(x)\ FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST, x) -/* SD25G_TARGET:CMU_GRP_2:CMU_45 */ -#define SD25G_LANE_CMU_45(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 20, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_2:CMU_45 */ +#define SD25G_LANE_CMU_45(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 20, 0, 1, 4) #define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0 GENMASK(7, 0) #define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_SET(x)\ @@ -1297,8 +1424,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_GET(x)\ FIELD_GET(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0, x) -/* SD25G_TARGET:CMU_GRP_2:CMU_46 */ -#define SD25G_LANE_CMU_46(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 24, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_2:CMU_46 */ +#define SD25G_LANE_CMU_46(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 24, 0, 1, 4) #define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8 GENMASK(7, 0) #define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_SET(x)\ @@ -1306,8 +1435,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_GET(x)\ FIELD_GET(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8, x) -/* SD25G_TARGET:CMU_GRP_3:CMU_C0 */ -#define SD25G_LANE_CMU_C0(t) __REG(TARGET_SD25G_LANE, t, 8, 768, 0, 1, 252, 0, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_3:CMU_C0 */ +#define SD25G_LANE_CMU_C0(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 768, 0, 1, 252, 0, 0, 1, 4) #define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0 GENMASK(3, 0) #define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0_SET(x)\ @@ -1321,8 +1452,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_C0_PLL_LOL_UDL_GET(x)\ FIELD_GET(SD25G_LANE_CMU_C0_PLL_LOL_UDL, x) -/* SD25G_TARGET:CMU_GRP_4:CMU_FF */ -#define SD25G_LANE_CMU_FF(t) __REG(TARGET_SD25G_LANE, t, 8, 1020, 0, 1, 4, 0, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:CMU_GRP_4:CMU_FF */ +#define SD25G_LANE_CMU_FF(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1020, 0, 1, 4, 0, 0, 1, 4) #define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX GENMASK(7, 0) #define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(x)\ @@ -1330,8 +1463,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_GET(x)\ FIELD_GET(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_00 */ -#define SD25G_LANE_LANE_00(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 0, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_00 */ +#define SD25G_LANE_LANE_00(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 0, 0, 1, 4) #define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0 GENMASK(3, 0) #define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0_SET(x)\ @@ -1345,8 +1480,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_01 */ -#define SD25G_LANE_LANE_01(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 4, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_01 */ +#define SD25G_LANE_LANE_01(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 4, 0, 1, 4) #define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0 GENMASK(2, 0) #define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_SET(x)\ @@ -1360,8 +1497,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_03 */ -#define SD25G_LANE_LANE_03(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 12, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_03 */ +#define SD25G_LANE_LANE_03(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 12, 0, 1, 4) #define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0 GENMASK(4, 0) #define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_SET(x)\ @@ -1369,8 +1508,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_04 */ -#define SD25G_LANE_LANE_04(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 16, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_04 */ +#define SD25G_LANE_LANE_04(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 16, 0, 1, 4) #define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN BIT(0) #define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_SET(x)\ @@ -1408,8 +1549,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN_GET(x)\ FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_05 */ -#define SD25G_LANE_LANE_05(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 20, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_05 */ +#define SD25G_LANE_LANE_05(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 20, 0, 1, 4) #define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0 GENMASK(3, 0) #define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0_SET(x)\ @@ -1423,8 +1566,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_05_LN_CFG_BW_1_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_05_LN_CFG_BW_1_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_06 */ -#define SD25G_LANE_LANE_06(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 24, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_06 */ +#define SD25G_LANE_LANE_06(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 24, 0, 1, 4) #define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN BIT(0) #define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN_SET(x)\ @@ -1438,8 +1583,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_07 */ -#define SD25G_LANE_LANE_07(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 28, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_07 */ +#define SD25G_LANE_LANE_07(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 28, 0, 1, 4) #define SD25G_LANE_LANE_07_LN_CFG_EN_ADV BIT(0) #define SD25G_LANE_LANE_07_LN_CFG_EN_ADV_SET(x)\ @@ -1459,8 +1606,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_07_LN_CFG_EN_DLY_GET(x)\ FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_DLY, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_09 */ -#define SD25G_LANE_LANE_09(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 36, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_09 */ +#define SD25G_LANE_LANE_09(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 36, 0, 1, 4) #define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0 GENMASK(3, 0) #define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_SET(x)\ @@ -1468,8 +1617,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_0A */ -#define SD25G_LANE_LANE_0A(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 40, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_0A */ +#define SD25G_LANE_LANE_0A(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 40, 0, 1, 4) #define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0 GENMASK(5, 0) #define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_SET(x)\ @@ -1477,8 +1628,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_0B */ -#define SD25G_LANE_LANE_0B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 44, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_0B */ +#define SD25G_LANE_LANE_0B(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 44, 0, 1, 4) #define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN BIT(0) #define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_SET(x)\ @@ -1498,8 +1651,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_0C */ -#define SD25G_LANE_LANE_0C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 48, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_0C */ +#define SD25G_LANE_LANE_0C(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 48, 0, 1, 4) #define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0 GENMASK(2, 0) #define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_SET(x)\ @@ -1519,8 +1674,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD_GET(x)\ FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_0D */ -#define SD25G_LANE_LANE_0D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 52, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_0D */ +#define SD25G_LANE_LANE_0D(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 52, 0, 1, 4) #define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0 GENMASK(2, 0) #define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_SET(x)\ @@ -1552,8 +1709,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_GET(x)\ FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_0E */ -#define SD25G_LANE_LANE_0E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 56, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_0E */ +#define SD25G_LANE_LANE_0E(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 56, 0, 1, 4) #define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN BIT(0) #define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN_SET(x)\ @@ -1579,8 +1738,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_0F */ -#define SD25G_LANE_LANE_0F(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 60, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_0F */ +#define SD25G_LANE_LANE_0F(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 60, 0, 1, 4) #define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1 GENMASK(4, 0) #define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_SET(x)\ @@ -1588,8 +1749,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_GET(x)\ FIELD_GET(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_18 */ -#define SD25G_LANE_LANE_18(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 96, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_18 */ +#define SD25G_LANE_LANE_18(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 96, 0, 1, 4) #define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN BIT(0) #define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_SET(x)\ @@ -1621,8 +1784,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_19 */ -#define SD25G_LANE_LANE_19(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 100, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_19 */ +#define SD25G_LANE_LANE_19(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 100, 0, 1, 4) #define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD BIT(0) #define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD_SET(x)\ @@ -1672,8 +1837,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE_GET(x)\ FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_PD_CTLE, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_1A */ -#define SD25G_LANE_LANE_1A(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 104, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_1A */ +#define SD25G_LANE_LANE_1A(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 104, 0, 1, 4) #define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN BIT(0) #define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN_SET(x)\ @@ -1687,8 +1854,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_1B */ -#define SD25G_LANE_LANE_1B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 108, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_1B */ +#define SD25G_LANE_LANE_1B(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 108, 0, 1, 4) #define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0 GENMASK(7, 0) #define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_SET(x)\ @@ -1696,8 +1865,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_1C */ -#define SD25G_LANE_LANE_1C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 112, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_1C */ +#define SD25G_LANE_LANE_1C(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 112, 0, 1, 4) #define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN BIT(0) #define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(x)\ @@ -1723,8 +1894,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_1D */ -#define SD25G_LANE_LANE_1D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 116, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_1D */ +#define SD25G_LANE_LANE_1D(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 116, 0, 1, 4) #define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR BIT(0) #define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR_SET(x)\ @@ -1774,8 +1947,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD_GET(x)\ FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_1E */ -#define SD25G_LANE_LANE_1E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 120, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_1E */ +#define SD25G_LANE_LANE_1E(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 120, 0, 1, 4) #define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0 GENMASK(1, 0) #define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0_SET(x)\ @@ -1807,8 +1982,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD_GET(x)\ FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_21 */ -#define SD25G_LANE_LANE_21(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 132, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_21 */ +#define SD25G_LANE_LANE_21(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 132, 0, 1, 4) #define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0 GENMASK(4, 0) #define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_SET(x)\ @@ -1816,8 +1993,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_22 */ -#define SD25G_LANE_LANE_22(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 136, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_22 */ +#define SD25G_LANE_LANE_22(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 136, 0, 1, 4) #define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0 GENMASK(3, 0) #define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_SET(x)\ @@ -1825,8 +2004,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_25 */ -#define SD25G_LANE_LANE_25(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 148, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_25 */ +#define SD25G_LANE_LANE_25(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 148, 0, 1, 4) #define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0 GENMASK(6, 0) #define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_SET(x)\ @@ -1834,8 +2015,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_26 */ -#define SD25G_LANE_LANE_26(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 152, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_26 */ +#define SD25G_LANE_LANE_26(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 152, 0, 1, 4) #define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0 GENMASK(6, 0) #define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_SET(x)\ @@ -1843,8 +2026,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_28 */ -#define SD25G_LANE_LANE_28(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 160, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_28 */ +#define SD25G_LANE_LANE_28(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 160, 0, 1, 4) #define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN BIT(0) #define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN_SET(x)\ @@ -1870,8 +2055,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_2B */ -#define SD25G_LANE_LANE_2B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 172, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_2B */ +#define SD25G_LANE_LANE_2B(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 172, 0, 1, 4) #define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0 GENMASK(3, 0) #define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_SET(x)\ @@ -1891,8 +2078,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU_GET(x)\ FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_2C */ -#define SD25G_LANE_LANE_2C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 176, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_2C */ +#define SD25G_LANE_LANE_2C(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 176, 0, 1, 4) #define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0 GENMASK(2, 0) #define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_SET(x)\ @@ -1906,8 +2095,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_GET(x)\ FIELD_GET(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_2D */ -#define SD25G_LANE_LANE_2D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 180, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_2D */ +#define SD25G_LANE_LANE_2D(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 180, 0, 1, 4) #define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0 GENMASK(2, 0) #define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_SET(x)\ @@ -1921,8 +2112,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_2E */ -#define SD25G_LANE_LANE_2E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 184, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_2E */ +#define SD25G_LANE_LANE_2E(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 184, 0, 1, 4) #define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN BIT(0) #define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN_SET(x)\ @@ -1972,8 +2165,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_GET(x)\ FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_40 */ -#define SD25G_LANE_LANE_40(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 256, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_40 */ +#define SD25G_LANE_LANE_40(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 256, 0, 1, 4) #define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE BIT(0) #define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE_SET(x)\ @@ -2017,8 +2212,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN_GET(x)\ FIELD_GET(SD25G_LANE_LANE_40_LN_R_CTLE_RSTN, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_42 */ -#define SD25G_LANE_LANE_42(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 264, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_42 */ +#define SD25G_LANE_LANE_42(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 264, 0, 1, 4) #define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0 GENMASK(7, 0) #define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_SET(x)\ @@ -2026,8 +2223,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_43 */ -#define SD25G_LANE_LANE_43(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 268, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_43 */ +#define SD25G_LANE_LANE_43(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 268, 0, 1, 4) #define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8 GENMASK(7, 0) #define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_SET(x)\ @@ -2035,8 +2234,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_GET(x)\ FIELD_GET(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_44 */ -#define SD25G_LANE_LANE_44(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 272, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_44 */ +#define SD25G_LANE_LANE_44(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 272, 0, 1, 4) #define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0 GENMASK(7, 0) #define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_SET(x)\ @@ -2044,8 +2245,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_GET(x)\ FIELD_GET(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0, x) -/* SD25G_TARGET:LANE_GRP_0:LANE_45 */ -#define SD25G_LANE_LANE_45(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 276, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_0:LANE_45 */ +#define SD25G_LANE_LANE_45(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 276, 0, 1, 4) #define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8 GENMASK(7, 0) #define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_SET(x)\ @@ -2053,8 +2256,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_GET(x)\ FIELD_GET(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8, x) -/* SD25G_TARGET:LANE_GRP_1:LANE_DE */ -#define SD25G_LANE_LANE_DE(t) __REG(TARGET_SD25G_LANE, t, 8, 1792, 0, 1, 128, 120, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_TARGET:LANE_GRP_1:LANE_DE */ +#define SD25G_LANE_LANE_DE(t) \ + __REG(TARGET_SD25G_LANE, t, 8, 1792, 0, 1, 128, 120, 0, 1, 4) #define SD25G_LANE_LANE_DE_LN_LOL_UDL BIT(0) #define SD25G_LANE_LANE_DE_LN_LOL_UDL_SET(x)\ @@ -2080,8 +2285,10 @@ enum sparx5_serdes_target { #define SD25G_LANE_LANE_DE_LN_PMA_RXEI_GET(x)\ FIELD_GET(SD25G_LANE_LANE_DE_LN_PMA_RXEI, x) -/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */ -#define SD6G_LANE_LANE_DF(t) __REG(TARGET_SD6G_LANE, t, 13, 832, 0, 1, 84, 60, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */ +#define SD6G_LANE_LANE_DF(t) \ + __REG(TARGET_SD6G_LANE, t, 13, 832, 0, 1, 84, 60, 0, 1, 4) #define SD6G_LANE_LANE_DF_LOL_UDL BIT(0) #define SD6G_LANE_LANE_DF_LOL_UDL_SET(x)\ @@ -2107,8 +2314,9 @@ enum sparx5_serdes_target { #define SD6G_LANE_LANE_DF_SQUELCH_GET(x)\ FIELD_GET(SD6G_LANE_LANE_DF_SQUELCH, x) -/* SD10G_CMU_TARGET:CMU_GRP_0:CMU_00 */ -#define SD_CMU_CMU_00(t) __REG(TARGET_SD_CMU, t, 14, 0, 0, 1, 20, 0, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_0:CMU_00 */ +#define SD_CMU_CMU_00(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 0, 0, 1, 20, 0, 0, 1, 4) #define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE BIT(0) #define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE_SET(x)\ @@ -2134,8 +2342,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_GET(x)\ FIELD_GET(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, x) -/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_05 */ -#define SD_CMU_CMU_05(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 0, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_05 */ +#define SD_CMU_CMU_05(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 20, 0, 1, 72, 0, 0, 1, 4) #define SD_CMU_CMU_05_CFG_REFCK_TERM_EN BIT(0) #define SD_CMU_CMU_05_CFG_REFCK_TERM_EN_SET(x)\ @@ -2149,9 +2358,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_GET(x)\ FIELD_GET(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x) -/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_06 */ -#define SD_CMU_CMU_06(t) \ - __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 4, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_06 */ +#define SD_CMU_CMU_06(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 20, 0, 1, 72, 4, 0, 1, 4) #define SD_CMU_CMU_06_CFG_DISLOS BIT(0) #define SD_CMU_CMU_06_CFG_DISLOS_SET(x)\ @@ -2201,9 +2410,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_06_CFG_VCO_CAL_BYP_GET(x)\ FIELD_GET(SD_CMU_CMU_06_CFG_VCO_CAL_BYP, x) -/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_08 */ -#define SD_CMU_CMU_08(t) \ - __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 12, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_08 */ +#define SD_CMU_CMU_08(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 20, 0, 1, 72, 12, 0, 1, 4) #define SD_CMU_CMU_08_CFG_VFILT2PAD BIT(0) #define SD_CMU_CMU_08_CFG_VFILT2PAD_SET(x)\ @@ -2235,8 +2444,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN_GET(x)\ FIELD_GET(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN, x) -/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_09 */ -#define SD_CMU_CMU_09(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 16, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_09 */ +#define SD_CMU_CMU_09(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 20, 0, 1, 72, 16, 0, 1, 4) #define SD_CMU_CMU_09_CFG_EN_TX_CK_UP BIT(0) #define SD_CMU_CMU_09_CFG_EN_TX_CK_UP_SET(x)\ @@ -2262,8 +2472,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_09_CFG_SW_10G_GET(x)\ FIELD_GET(SD_CMU_CMU_09_CFG_SW_10G, x) -/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_0D */ -#define SD_CMU_CMU_0D(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 32, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_0D */ +#define SD_CMU_CMU_0D(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 20, 0, 1, 72, 32, 0, 1, 4) #define SD_CMU_CMU_0D_CFG_PD_DIV64 BIT(0) #define SD_CMU_CMU_0D_CFG_PD_DIV64_SET(x)\ @@ -2295,8 +2506,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_0D_CFG_REFCK_PD_GET(x)\ FIELD_GET(SD_CMU_CMU_0D_CFG_REFCK_PD, x) -/* SD10G_CMU_TARGET:CMU_GRP_3:CMU_1B */ -#define SD_CMU_CMU_1B(t) __REG(TARGET_SD_CMU, t, 14, 104, 0, 1, 20, 4, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_3:CMU_1B */ +#define SD_CMU_CMU_1B(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 104, 0, 1, 20, 4, 0, 1, 4) #define SD_CMU_CMU_1B_CFG_RESERVE_7_0 GENMASK(7, 0) #define SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(x)\ @@ -2304,8 +2516,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_1B_CFG_RESERVE_7_0_GET(x)\ FIELD_GET(SD_CMU_CMU_1B_CFG_RESERVE_7_0, x) -/* SD10G_CMU_TARGET:CMU_GRP_4:CMU_1F */ -#define SD_CMU_CMU_1F(t) __REG(TARGET_SD_CMU, t, 14, 124, 0, 1, 68, 0, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_4:CMU_1F */ +#define SD_CMU_CMU_1F(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 124, 0, 1, 68, 0, 0, 1, 4) #define SD_CMU_CMU_1F_CFG_BIAS_DN_EN BIT(0) #define SD_CMU_CMU_1F_CFG_BIAS_DN_EN_SET(x)\ @@ -2331,8 +2544,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_1F_CFG_VTUNE_SEL_GET(x)\ FIELD_GET(SD_CMU_CMU_1F_CFG_VTUNE_SEL, x) -/* SD10G_CMU_TARGET:CMU_GRP_5:CMU_30 */ -#define SD_CMU_CMU_30(t) __REG(TARGET_SD_CMU, t, 14, 192, 0, 1, 72, 0, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_5:CMU_30 */ +#define SD_CMU_CMU_30(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 192, 0, 1, 72, 0, 0, 1, 4) #define SD_CMU_CMU_30_R_PLL_DLOL_EN BIT(0) #define SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(x)\ @@ -2340,8 +2554,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_30_R_PLL_DLOL_EN_GET(x)\ FIELD_GET(SD_CMU_CMU_30_R_PLL_DLOL_EN, x) -/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_44 */ -#define SD_CMU_CMU_44(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 8, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_44 */ +#define SD_CMU_CMU_44(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 264, 0, 1, 632, 8, 0, 1, 4) #define SD_CMU_CMU_44_R_PLL_RSTN BIT(0) #define SD_CMU_CMU_44_R_PLL_RSTN_SET(x)\ @@ -2355,8 +2570,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_44_R_CK_RESETB_GET(x)\ FIELD_GET(SD_CMU_CMU_44_R_CK_RESETB, x) -/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_45 */ -#define SD_CMU_CMU_45(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 12, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_45 */ +#define SD_CMU_CMU_45(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 264, 0, 1, 632, 12, 0, 1, 4) #define SD_CMU_CMU_45_R_EN_RATECHG_CTRL BIT(0) #define SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(x)\ @@ -2406,8 +2622,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN_GET(x)\ FIELD_GET(SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN, x) -/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_47 */ -#define SD_CMU_CMU_47(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 20, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_47 */ +#define SD_CMU_CMU_47(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 264, 0, 1, 632, 20, 0, 1, 4) #define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0 GENMASK(4, 0) #define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(x)\ @@ -2415,8 +2632,9 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_GET(x)\ FIELD_GET(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, x) -/* SD10G_CMU_TARGET:CMU_GRP_7:CMU_E0 */ -#define SD_CMU_CMU_E0(t) __REG(TARGET_SD_CMU, t, 14, 896, 0, 1, 8, 0, 0, 1, 4) +/* SD10G_CMU_TARGET:CMU_GRP_7:CMU_E0 */ +#define SD_CMU_CMU_E0(t) \ + __REG(TARGET_SD_CMU, t, TSIZE(TC_SD_CMU), 896, 0, 1, 8, 0, 0, 1, 4) #define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0 GENMASK(3, 0) #define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0_SET(x)\ @@ -2430,8 +2648,10 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_E0_PLL_LOL_UDL_GET(x)\ FIELD_GET(SD_CMU_CMU_E0_PLL_LOL_UDL, x) -/* SD_CMU_TARGET:SD_CMU_CFG:SD_CMU_CFG */ -#define SD_CMU_CFG_SD_CMU_CFG(t) __REG(TARGET_SD_CMU_CFG, t, 14, 0, 0, 1, 8, 0, 0, 1, 4) +/* SD_CMU_TARGET:SD_CMU_CFG:SD_CMU_CFG */ +#define SD_CMU_CFG_SD_CMU_CFG(t) \ + __REG(TARGET_SD_CMU_CFG, t, TSIZE(TC_SD_CMU_CFG), 0, 0, 1, 8, 0, 0, 1, \ + 4) #define SD_CMU_CFG_SD_CMU_CFG_CMU_RST BIT(0) #define SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(x)\ @@ -2445,8 +2665,9 @@ enum sparx5_serdes_target { #define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_GET(x)\ FIELD_GET(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, x) -/* SD_LANE_TARGET:SD_RESET:SD_SER_RST */ -#define SD_LANE_SD_SER_RST(t) __REG(TARGET_SD_LANE, t, 25, 0, 0, 1, 8, 0, 0, 1, 4) +/* SD_LANE_TARGET:SD_RESET:SD_SER_RST */ +#define SD_LANE_SD_SER_RST(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 0, 0, 1, 8, 0, 0, 1, 4) #define SD_LANE_SD_SER_RST_SER_RST BIT(0) #define SD_LANE_SD_SER_RST_SER_RST_SET(x)\ @@ -2454,8 +2675,9 @@ enum sparx5_serdes_target { #define SD_LANE_SD_SER_RST_SER_RST_GET(x)\ FIELD_GET(SD_LANE_SD_SER_RST_SER_RST, x) -/* SD_LANE_TARGET:SD_RESET:SD_DES_RST */ -#define SD_LANE_SD_DES_RST(t) __REG(TARGET_SD_LANE, t, 25, 0, 0, 1, 8, 4, 0, 1, 4) +/* SD_LANE_TARGET:SD_RESET:SD_DES_RST */ +#define SD_LANE_SD_DES_RST(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 0, 0, 1, 8, 4, 0, 1, 4) #define SD_LANE_SD_DES_RST_DES_RST BIT(0) #define SD_LANE_SD_DES_RST_DES_RST_SET(x)\ @@ -2463,8 +2685,9 @@ enum sparx5_serdes_target { #define SD_LANE_SD_DES_RST_DES_RST_GET(x)\ FIELD_GET(SD_LANE_SD_DES_RST_DES_RST, x) -/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */ -#define SD_LANE_SD_LANE_CFG(t) __REG(TARGET_SD_LANE, t, 25, 8, 0, 1, 8, 0, 0, 1, 4) +/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */ +#define SD_LANE_SD_LANE_CFG(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 8, 0, 1, 8, 0, 0, 1, 4) #define SD_LANE_SD_LANE_CFG_MACRO_RST BIT(0) #define SD_LANE_SD_LANE_CFG_MACRO_RST_SET(x)\ @@ -2508,8 +2731,9 @@ enum sparx5_serdes_target { #define SD_LANE_SD_LANE_CFG_LANE_RX_RST_GET(x)\ FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_RX_RST, x) -/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */ -#define SD_LANE_SD_LANE_STAT(t) __REG(TARGET_SD_LANE, t, 25, 8, 0, 1, 8, 4, 0, 1, 4) +/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */ +#define SD_LANE_SD_LANE_STAT(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 8, 0, 1, 8, 4, 0, 1, 4) #define SD_LANE_SD_LANE_STAT_PMA_RST_DONE BIT(0) #define SD_LANE_SD_LANE_STAT_PMA_RST_DONE_SET(x)\ @@ -2529,9 +2753,9 @@ enum sparx5_serdes_target { #define SD_LANE_SD_LANE_STAT_DBG_OBS_GET(x)\ FIELD_GET(SD_LANE_SD_LANE_STAT_DBG_OBS, x) -/* SD_LANE_TARGET:SD_PWR_CFG:QUIET_MODE_6G */ -#define SD_LANE_QUIET_MODE_6G(t) \ - __REG(TARGET_SD_LANE, t, 25, 24, 0, 1, 8, 4, 0, 1, 4) +/* SD_LANE_TARGET:SD_PWR_CFG:QUIET_MODE_6G */ +#define SD_LANE_QUIET_MODE_6G(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 24, 0, 1, 8, 4, 0, 1, 4) #define SD_LANE_QUIET_MODE_6G_QUIET_MODE GENMASK(24, 0) #define SD_LANE_QUIET_MODE_6G_QUIET_MODE_SET(x)\ @@ -2539,8 +2763,9 @@ enum sparx5_serdes_target { #define SD_LANE_QUIET_MODE_6G_QUIET_MODE_GET(x)\ FIELD_GET(SD_LANE_QUIET_MODE_6G_QUIET_MODE, x) -/* SD_LANE_TARGET:CFG_STAT_FX100:MISC */ -#define SD_LANE_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 0, 0, 1, 4) +/* SD_LANE_TARGET:CFG_STAT_FX100:MISC */ +#define SD_LANE_MISC(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 56, 0, 1, 56, 0, 0, 1, 4) #define SD_LANE_MISC_SD_125_RST_DIS BIT(0) #define SD_LANE_MISC_SD_125_RST_DIS_SET(x)\ @@ -2560,14 +2785,16 @@ enum sparx5_serdes_target { #define SD_LANE_MISC_MUX_ENA_GET(x)\ FIELD_GET(SD_LANE_MISC_MUX_ENA, x) +/* SPARX5 ONLY */ #define SD_LANE_MISC_CORE_CLK_FREQ GENMASK(5, 4) #define SD_LANE_MISC_CORE_CLK_FREQ_SET(x)\ FIELD_PREP(SD_LANE_MISC_CORE_CLK_FREQ, x) #define SD_LANE_MISC_CORE_CLK_FREQ_GET(x)\ FIELD_GET(SD_LANE_MISC_CORE_CLK_FREQ, x) -/* SD_LANE_TARGET:CFG_STAT_FX100:M_STAT_MISC */ -#define SD_LANE_M_STAT_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 36, 0, 1, 4) +/* SD_LANE_TARGET:CFG_STAT_FX100:M_STAT_MISC */ +#define SD_LANE_M_STAT_MISC(t) \ + __REG(TARGET_SD_LANE, t, TSIZE(TC_SD_LANE), 56, 0, 1, 56, 36, 0, 1, 4) #define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM GENMASK(21, 0) #define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM_SET(x)\ @@ -2581,8 +2808,10 @@ enum sparx5_serdes_target { #define SD_LANE_M_STAT_MISC_M_LOCK_CNT_GET(x)\ FIELD_GET(SD_LANE_M_STAT_MISC_M_LOCK_CNT, x) -/* SD25G_CFG_TARGET:SD_RESET:SD_SER_RST */ -#define SD_LANE_25G_SD_SER_RST(t) __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 0, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_CFG_TARGET:SD_RESET:SD_SER_RST */ +#define SD_LANE_25G_SD_SER_RST(t) \ + __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 0, 0, 1, 4) #define SD_LANE_25G_SD_SER_RST_SER_RST BIT(0) #define SD_LANE_25G_SD_SER_RST_SER_RST_SET(x)\ @@ -2590,8 +2819,10 @@ enum sparx5_serdes_target { #define SD_LANE_25G_SD_SER_RST_SER_RST_GET(x)\ FIELD_GET(SD_LANE_25G_SD_SER_RST_SER_RST, x) -/* SD25G_CFG_TARGET:SD_RESET:SD_DES_RST */ -#define SD_LANE_25G_SD_DES_RST(t) __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 4, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_CFG_TARGET:SD_RESET:SD_DES_RST */ +#define SD_LANE_25G_SD_DES_RST(t) \ + __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 4, 0, 1, 4) #define SD_LANE_25G_SD_DES_RST_DES_RST BIT(0) #define SD_LANE_25G_SD_DES_RST_DES_RST_SET(x)\ @@ -2599,8 +2830,10 @@ enum sparx5_serdes_target { #define SD_LANE_25G_SD_DES_RST_DES_RST_GET(x)\ FIELD_GET(SD_LANE_25G_SD_DES_RST_DES_RST, x) -/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */ -#define SD_LANE_25G_SD_LANE_CFG(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 0, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */ +#define SD_LANE_25G_SD_LANE_CFG(t) \ + __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 0, 0, 1, 4) #define SD_LANE_25G_SD_LANE_CFG_MACRO_RST BIT(0) #define SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(x)\ @@ -2698,8 +2931,10 @@ enum sparx5_serdes_target { #define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN_GET(x)\ FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN, x) -/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG2 */ -#define SD_LANE_25G_SD_LANE_CFG2(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 4, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG2 */ +#define SD_LANE_25G_SD_LANE_CFG2(t) \ + __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 4, 0, 1, 4) #define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL GENMASK(2, 0) #define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL_SET(x)\ @@ -2767,8 +3002,10 @@ enum sparx5_serdes_target { #define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL_GET(x)\ FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL, x) -/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */ -#define SD_LANE_25G_SD_LANE_STAT(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 8, 0, 1, 4) +/* SPARX5 ONLY */ +/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */ +#define SD_LANE_25G_SD_LANE_STAT(t) \ + __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 8, 0, 1, 4) #define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE BIT(0) #define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_SET(x)\ @@ -2788,8 +3025,9 @@ enum sparx5_serdes_target { #define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_GET(x)\ FIELD_GET(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x) -/* SD25G_CFG_TARGET:SD_PWR_CFG:QUIET_MODE_6G */ -#define SD_LANE_25G_QUIET_MODE_6G(t) \ +/* SPARX5 ONLY */ +/* SD25G_CFG_TARGET:SD_PWR_CFG:QUIET_MODE_6G */ +#define SD_LANE_25G_QUIET_MODE_6G(t) \ __REG(TARGET_SD_LANE_25G, t, 8, 28, 0, 1, 8, 4, 0, 1, 4) #define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE GENMASK(24, 0) diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 7bbf729a7c90a..7cb020dd3423c 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -704,7 +704,7 @@ static void cpcap_usb_phy_remove(struct platform_device *pdev) static struct platform_driver cpcap_usb_phy_driver = { .probe = cpcap_usb_phy_probe, - .remove_new = cpcap_usb_phy_remove, + .remove = cpcap_usb_phy_remove, .driver = { .name = "cpcap-usb-phy", .of_match_table = of_match_ptr(cpcap_usb_phy_id_table), diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 376d023a0aa90..152344e4f7e44 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -655,7 +655,7 @@ static void phy_mdm6600_remove(struct platform_device *pdev) static struct platform_driver phy_mdm6600_driver = { .probe = phy_mdm6600_probe, - .remove_new = phy_mdm6600_remove, + .remove = phy_mdm6600_remove, .driver = { .name = "phy-mapphone-mdm6600", .pm = &phy_mdm6600_pm_ops, diff --git a/drivers/phy/phy-airoha-pcie-regs.h b/drivers/phy/phy-airoha-pcie-regs.h index bb1f679ca1dfa..b938a7b468fee 100644 --- a/drivers/phy/phy-airoha-pcie-regs.h +++ b/drivers/phy/phy-airoha-pcie-regs.h @@ -197,9 +197,9 @@ #define CSR_2L_PXP_TX1_MULTLANE_EN BIT(0) #define REG_CSR_2L_RX0_REV0 0x00fc -#define CSR_2L_PXP_VOS_PNINV GENMASK(3, 2) -#define CSR_2L_PXP_FE_GAIN_NORMAL_MODE GENMASK(6, 4) -#define CSR_2L_PXP_FE_GAIN_TRAIN_MODE GENMASK(10, 8) +#define CSR_2L_PXP_VOS_PNINV GENMASK(19, 18) +#define CSR_2L_PXP_FE_GAIN_NORMAL_MODE GENMASK(22, 20) +#define CSR_2L_PXP_FE_GAIN_TRAIN_MODE GENMASK(26, 24) #define REG_CSR_2L_RX0_PHYCK_DIV 0x0100 #define CSR_2L_PXP_RX0_PHYCK_SEL GENMASK(9, 8) diff --git a/drivers/phy/phy-airoha-pcie.c b/drivers/phy/phy-airoha-pcie.c index 1e410eb410580..56e9ade8a9fd3 100644 --- a/drivers/phy/phy-airoha-pcie.c +++ b/drivers/phy/phy-airoha-pcie.c @@ -459,7 +459,7 @@ static void airoha_pcie_phy_init_clk_out(struct airoha_pcie_phy *pcie_phy) airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, CSR_2L_PXP_CLKTX1_SR); airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_PLL_CMN_RESERVE0, - CSR_2L_PXP_PLL_RESERVE_MASK, 0xdd); + CSR_2L_PXP_PLL_RESERVE_MASK, 0xd0d); } static void airoha_pcie_phy_init_csr_2l(struct airoha_pcie_phy *pcie_phy) @@ -471,9 +471,9 @@ static void airoha_pcie_phy_init_csr_2l(struct airoha_pcie_phy *pcie_phy) PCIE_SW_XFI_RXPCS_RST | PCIE_SW_REF_RST | PCIE_SW_RX_RST); airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, - PCIE_TX_TOP_RST | REG_PCIE_PMA_TX_RESET); + PCIE_TX_TOP_RST | PCIE_TX_CAL_RST); airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, - PCIE_TX_TOP_RST | REG_PCIE_PMA_TX_RESET); + PCIE_TX_TOP_RST | PCIE_TX_CAL_RST); } static void airoha_pcie_phy_init_rx(struct airoha_pcie_phy *pcie_phy) @@ -802,7 +802,7 @@ static void airoha_pcie_phy_init_ssc_jcpll(struct airoha_pcie_phy *pcie_phy) airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_IFM, CSR_2L_PXP_JCPLL_SDM_IFM); airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, - REG_CSR_2L_JCPLL_SDM_HREN); + CSR_2L_PXP_JCPLL_SDM_HREN); airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, CSR_2L_PXP_JCPLL_SDM_DI_EN); airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, diff --git a/drivers/phy/phy-lgm-usb.c b/drivers/phy/phy-lgm-usb.c index 410729c7f513d..eb7c6fed20d34 100644 --- a/drivers/phy/phy-lgm-usb.c +++ b/drivers/phy/phy-lgm-usb.c @@ -271,7 +271,7 @@ static struct platform_driver lgm_phy_driver = { .of_match_table = intel_usb_phy_dt_ids, }, .probe = phy_probe, - .remove_new = phy_remove, + .remove = phy_remove, }; module_platform_driver(lgm_phy_driver); diff --git a/drivers/phy/phy-nxp-ptn3222.c b/drivers/phy/phy-nxp-ptn3222.c new file mode 100644 index 0000000000000..c6179d8701e61 --- /dev/null +++ b/drivers/phy/phy-nxp-ptn3222.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NUM_SUPPLIES 2 + +struct ptn3222 { + struct i2c_client *client; + struct phy *phy; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +}; + +static int ptn3222_init(struct phy *phy) +{ + struct ptn3222 *ptn3222 = phy_get_drvdata(phy); + int ret; + + ret = regulator_bulk_enable(NUM_SUPPLIES, ptn3222->supplies); + if (ret) + return ret; + + gpiod_set_value_cansleep(ptn3222->reset_gpio, 0); + + return 0; +} + +static int ptn3222_exit(struct phy *phy) +{ + struct ptn3222 *ptn3222 = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(ptn3222->reset_gpio, 1); + + return regulator_bulk_disable(NUM_SUPPLIES, ptn3222->supplies); +} + +static const struct phy_ops ptn3222_ops = { + .init = ptn3222_init, + .exit = ptn3222_exit, + .owner = THIS_MODULE, +}; + +static const struct regulator_bulk_data ptn3222_supplies[NUM_SUPPLIES] = { + { + .supply = "vdd3v3", + .init_load_uA = 11000, + }, { + .supply = "vdd1v8", + .init_load_uA = 55000, + } +}; + +static int ptn3222_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct phy_provider *phy_provider; + struct ptn3222 *ptn3222; + int ret; + + ptn3222 = devm_kzalloc(dev, sizeof(*ptn3222), GFP_KERNEL); + if (!ptn3222) + return -ENOMEM; + + ptn3222->client = client; + + ptn3222->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ptn3222->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ptn3222->reset_gpio), + "unable to acquire reset gpio\n"); + + ret = devm_regulator_bulk_get_const(dev, NUM_SUPPLIES, ptn3222_supplies, + &ptn3222->supplies); + if (ret) + return ret; + + ptn3222->phy = devm_phy_create(dev, dev->of_node, &ptn3222_ops); + if (IS_ERR(ptn3222->phy)) { + dev_err(dev, "failed to create PHY: %d\n", ret); + return PTR_ERR(ptn3222->phy); + } + + phy_set_drvdata(ptn3222->phy, ptn3222); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct i2c_device_id ptn3222_table[] = { + { "ptn3222" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ptn3222_table); + +static const struct of_device_id ptn3222_of_table[] = { + { .compatible = "nxp,ptn3222" }, + { } +}; +MODULE_DEVICE_TABLE(of, ptn3222_of_table); + +static struct i2c_driver ptn3222_driver = { + .driver = { + .name = "ptn3222", + .of_match_table = ptn3222_of_table, + }, + .probe = ptn3222_probe, + .id_table = ptn3222_table, +}; + +module_i2c_driver(ptn3222_driver); + +MODULE_DESCRIPTION("NXP PTN3222 eUSB2 Redriver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c index 3642a5d4f2f3b..cae290a6e19fc 100644 --- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -257,12 +257,12 @@ static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); static struct platform_driver qcom_apq8064_sata_phy_driver = { - .probe = qcom_apq8064_sata_phy_probe, - .remove_new = qcom_apq8064_sata_phy_remove, + .probe = qcom_apq8064_sata_phy_probe, + .remove = qcom_apq8064_sata_phy_remove, .driver = { - .name = "qcom-apq8064-sata-phy", - .of_match_table = qcom_apq8064_sata_phy_of_match, - } + .name = "qcom-apq8064-sata-phy", + .of_match_table = qcom_apq8064_sata_phy_of_match, + }, }; module_platform_driver(qcom_apq8064_sata_phy_driver); diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c index da2b32fb5b451..f1b51018683d5 100644 --- a/drivers/phy/qualcomm/phy-qcom-edp.c +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -32,16 +32,8 @@ #define DP_PHY_PD_CTL 0x001c #define DP_PHY_MODE 0x0020 -#define DP_PHY_AUX_CFG0 0x0024 -#define DP_PHY_AUX_CFG1 0x0028 -#define DP_PHY_AUX_CFG2 0x002C -#define DP_PHY_AUX_CFG3 0x0030 -#define DP_PHY_AUX_CFG4 0x0034 -#define DP_PHY_AUX_CFG5 0x0038 -#define DP_PHY_AUX_CFG6 0x003C -#define DP_PHY_AUX_CFG7 0x0040 -#define DP_PHY_AUX_CFG8 0x0044 -#define DP_PHY_AUX_CFG9 0x0048 +#define DP_AUX_CFG_SIZE 10 +#define DP_PHY_AUX_CFG(n) (0x24 + (0x04 * (n))) #define DP_PHY_AUX_INTERRUPT_MASK 0x0058 @@ -90,6 +82,7 @@ struct phy_ver_ops { struct qcom_edp_phy_cfg { bool is_edp; + const u8 *aux_cfg; const struct qcom_edp_swing_pre_emph_cfg *swing_pre_emph_cfg; const struct phy_ver_ops *ver_ops; }; @@ -186,11 +179,40 @@ static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = { .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3, }; +static const u8 edp_phy_aux_cfg_v4[10] = { + 0x00, 0x13, 0x24, 0x00, 0x0a, 0x26, 0x0a, 0x03, 0x37, 0x03 +}; + +static const u8 edp_pre_emp_hbr_rbr_v5[4][4] = { + { 0x05, 0x11, 0x17, 0x1d }, + { 0x05, 0x11, 0x18, 0xff }, + { 0x06, 0x11, 0xff, 0xff }, + { 0x00, 0xff, 0xff, 0xff } +}; + +static const u8 edp_pre_emp_hbr2_hbr3_v5[4][4] = { + { 0x0c, 0x15, 0x19, 0x1e }, + { 0x0b, 0x15, 0x19, 0xff }, + { 0x0e, 0x14, 0xff, 0xff }, + { 0x0d, 0xff, 0xff, 0xff } +}; + +static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg_v5 = { + .swing_hbr_rbr = &edp_swing_hbr_rbr, + .swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3, + .pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr_v5, + .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3_v5, +}; + +static const u8 edp_phy_aux_cfg_v5[10] = { + 0x00, 0x13, 0xa4, 0x00, 0x0a, 0x26, 0x0a, 0x03, 0x37, 0x03 +}; + static int qcom_edp_phy_init(struct phy *phy) { struct qcom_edp *edp = phy_get_drvdata(phy); + u8 aux_cfg[DP_AUX_CFG_SIZE]; int ret; - u8 cfg8; ret = regulator_bulk_enable(ARRAY_SIZE(edp->supplies), edp->supplies); if (ret) @@ -200,6 +222,8 @@ static int qcom_edp_phy_init(struct phy *phy) if (ret) goto out_disable_supplies; + memcpy(aux_cfg, edp->cfg->aux_cfg, sizeof(aux_cfg)); + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, edp->edp + DP_PHY_PD_CTL); @@ -222,22 +246,12 @@ static int qcom_edp_phy_init(struct phy *phy) * even needed. */ if (edp->cfg->swing_pre_emph_cfg && !edp->is_edp) - cfg8 = 0xb7; - else - cfg8 = 0x37; + aux_cfg[8] = 0xb7; writel(0xfc, edp->edp + DP_PHY_MODE); - writel(0x00, edp->edp + DP_PHY_AUX_CFG0); - writel(0x13, edp->edp + DP_PHY_AUX_CFG1); - writel(0x24, edp->edp + DP_PHY_AUX_CFG2); - writel(0x00, edp->edp + DP_PHY_AUX_CFG3); - writel(0x0a, edp->edp + DP_PHY_AUX_CFG4); - writel(0x26, edp->edp + DP_PHY_AUX_CFG5); - writel(0x0a, edp->edp + DP_PHY_AUX_CFG6); - writel(0x03, edp->edp + DP_PHY_AUX_CFG7); - writel(cfg8, edp->edp + DP_PHY_AUX_CFG8); - writel(0x03, edp->edp + DP_PHY_AUX_CFG9); + for (int i = 0; i < DP_AUX_CFG_SIZE; i++) + writel(aux_cfg[i], edp->edp + DP_PHY_AUX_CFG(i)); writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | @@ -518,17 +532,27 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v4 = { .com_configure_ssc = qcom_edp_com_configure_ssc_v4, }; +static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = { + .is_edp = false, + .aux_cfg = edp_phy_aux_cfg_v5, + .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v5, + .ver_ops = &qcom_edp_phy_ops_v4, +}; + static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = { + .aux_cfg = edp_phy_aux_cfg_v4, .ver_ops = &qcom_edp_phy_ops_v4, }; static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = { + .aux_cfg = edp_phy_aux_cfg_v4, .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg, .ver_ops = &qcom_edp_phy_ops_v4, }; static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = { .is_edp = true, + .aux_cfg = edp_phy_aux_cfg_v4, .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg, .ver_ops = &qcom_edp_phy_ops_v4, }; @@ -707,6 +731,7 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v6 = { }; static struct qcom_edp_phy_cfg x1e80100_phy_cfg = { + .aux_cfg = edp_phy_aux_cfg_v4, .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg, .ver_ops = &qcom_edp_phy_ops_v6, }; @@ -1108,6 +1133,7 @@ static int qcom_edp_phy_probe(struct platform_device *pdev) } static const struct of_device_id qcom_edp_phy_match_table[] = { + { .compatible = "qcom,sa8775p-edp-phy", .data = &sa8775p_dp_phy_cfg, }, { .compatible = "qcom,sc7280-edp-phy", .data = &sc7280_dp_phy_cfg, }, { .compatible = "qcom,sc8180x-edp-phy", .data = &sc7280_dp_phy_cfg, }, { .compatible = "qcom,sc8280xp-dp-phy", .data = &sc8280xp_dp_phy_cfg, }, diff --git a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c index 68cc8e24f3836..6bd1b3c75c779 100644 --- a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c +++ b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c @@ -294,7 +294,7 @@ MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table); static struct platform_driver eusb2_repeater_driver = { .probe = eusb2_repeater_probe, - .remove_new = eusb2_repeater_remove, + .remove = eusb2_repeater_remove, .driver = { .name = "qcom-eusb2-repeater", .of_match_table = eusb2_repeater_of_match_table, diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c index f0a72b82c770b..f5eb0bdac418b 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c @@ -184,11 +184,11 @@ static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); static struct platform_driver qcom_ipq806x_sata_phy_driver = { - .probe = qcom_ipq806x_sata_phy_probe, - .remove_new = qcom_ipq806x_sata_phy_remove, + .probe = qcom_ipq806x_sata_phy_probe, + .remove = qcom_ipq806x_sata_phy_remove, .driver = { - .name = "qcom-ipq806x-sata-phy", - .of_match_table = qcom_ipq806x_sata_phy_of_match, + .name = "qcom-ipq806x-sata-phy", + .of_match_table = qcom_ipq806x_sata_phy_of_match, } }; module_platform_driver(qcom_ipq806x_sata_phy_driver); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 643045c9024ea..3bae39381fd08 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -3483,7 +3483,7 @@ static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) } #endif -static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_node *np) +static int qmp_combo_parse_dt_legacy_dp(struct qmp_combo *qmp, struct device_node *np) { struct device *dev = qmp->dev; @@ -3510,7 +3510,7 @@ static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_nod return 0; } -static int qmp_combo_parse_dt_lecacy_usb(struct qmp_combo *qmp, struct device_node *np) +static int qmp_combo_parse_dt_legacy_usb(struct qmp_combo *qmp, struct device_node *np) { const struct qmp_phy_cfg *cfg = qmp->cfg; struct device *dev = qmp->dev; @@ -3576,11 +3576,11 @@ static int qmp_combo_parse_dt_legacy(struct qmp_combo *qmp, struct device_node * if (IS_ERR(qmp->dp_serdes)) return PTR_ERR(qmp->dp_serdes); - ret = qmp_combo_parse_dt_lecacy_usb(qmp, usb_np); + ret = qmp_combo_parse_dt_legacy_usb(qmp, usb_np); if (ret) return ret; - ret = qmp_combo_parse_dt_lecacy_dp(qmp, dp_np); + ret = qmp_combo_parse_dt_legacy_dp(qmp, dp_np); if (ret) return ret; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 36aaac34e6c6c..873f2f9844c66 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -34,6 +34,8 @@ #include "phy-qcom-qmp-pcs-pcie-v5_20.h" #include "phy-qcom-qmp-pcs-pcie-v6.h" #include "phy-qcom-qmp-pcs-pcie-v6_20.h" +#include "phy-qcom-qmp-pcs-pcie-v6_30.h" +#include "phy-qcom-qmp-pcs-v6_30.h" #include "phy-qcom-qmp-pcie-qhp.h" #define PHY_INIT_COMPLETE_TIMEOUT 10000 @@ -1344,6 +1346,154 @@ static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x2_pcie_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_PCIE_V6_20_PCS_G4_FOM_EQ_CONFIG5, 0x8a), }; +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0x26), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0, 0xf8), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CORE_CLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_POST_DIV_MUX, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYS_CLK_CTRL, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_MISC_1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_VCO_DC_LEVEL_CTRL, 0x0f), +}; + +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_ln_shrd_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RXCLK_DIV2_CTRL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_SUMMER_CAL_SPD_MODE, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_DFE_DAC_ENABLE1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_TX_ADAPT_POST_THRESH1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_TX_ADAPT_POST_THRESH2, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B0, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B1, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B2, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B3, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B4, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B5, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MODE_RATE_0_1_B6, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH1_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH1_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH2_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH2_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH3_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH3_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH4_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH5_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_LN_SHRD_RX_MARG_COARSE_THRESH6_RATE3, 0x1f), +}; + +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_txz_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_20_TX_RES_CODE_LANE_OFFSET_TX, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_TX_RES_CODE_LANE_OFFSET_RX, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_20_TX_LANE_MODE_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_20_TX_LANE_MODE_2, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V6_20_TX_LANE_MODE_3, 0x51), + QMP_PHY_INIT_CFG(QSERDES_V6_20_TX_TRAN_DRVR_EMP_EN, 0x34), +}; + +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_rxz_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_FO_GAIN_RATE_2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_SO_GAIN_RATE_2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_FO_GAIN_RATE_3, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_IVCM_CAL_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_IVCM_POSTCAL_OFFSET, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_BKUP_CTRL1, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_DFE_3, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_VGA_CAL_MAN_VAL, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_20_VGA_CAL_CNTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_GM_CAL, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_EQU_ADAPTOR_CNTRL4, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x39), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B0, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B1, 0x23), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B2, 0x58), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B3, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B4, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B5, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B6, 0xee), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B0, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B1, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B2, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B3, 0xdf), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B4, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B5, 0x76), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B6, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_TX_ADPT_CTRL, 0x10), +}; + +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG_LANE(QSERDES_V6_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x3a, BIT(0)), +}; + +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_LOCK_DETECT_CONFIG2, 0x00), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_RX_SIGDET_LVL, 0x99), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_ALIGN_DETECT_CONFIG7, 0x00), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_EQ_CONFIG4, 0x00), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_EQ_CONFIG5, 0x22), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_TX_RX_CONFIG, 0x04), + QMP_PHY_INIT_CFG(QPHY_V6_30_PCS_TX_RX_CONFIG2, 0x02), +}; + +static const struct qmp_phy_init_tbl x1e80100_qmp_gen4x8_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_EQ_CONFIG1, 0x16), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_G4_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_RX_MARGINING_CONFIG1, 0x03), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_RX_MARGINING_CONFIG3, 0x28), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_RX_MARGINING_CONFIG5, 0x18), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_G3_FOM_EQ_CONFIG5, 0x7a), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_G4_FOM_EQ_CONFIG5, 0x8a), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_G3_RXEQEVAL_TIME, 0x27), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_G4_RXEQEVAL_TIME, 0x27), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_TX_RX_CONFIG, 0xc0), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_30_PCS_POWER_STATE_CONFIG2, 0x1d), +}; + static const struct qmp_phy_init_tbl sm8250_qmp_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08), QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34), @@ -2582,6 +2732,8 @@ struct qmp_pcie_offsets { u16 rx; u16 tx2; u16 rx2; + u16 txz; + u16 rxz; u16 ln_shrd; }; @@ -2592,6 +2744,10 @@ struct qmp_phy_cfg_tbls { int tx_num; const struct qmp_phy_init_tbl *rx; int rx_num; + const struct qmp_phy_init_tbl *txz; + int txz_num; + const struct qmp_phy_init_tbl *rxz; + int rxz_num; const struct qmp_phy_init_tbl *pcs; int pcs_num; const struct qmp_phy_init_tbl *pcs_misc; @@ -2659,6 +2815,8 @@ struct qmp_pcie { void __iomem *rx; void __iomem *tx2; void __iomem *rx2; + void __iomem *txz; + void __iomem *rxz; void __iomem *ln_shrd; void __iomem *port_b; @@ -2826,6 +2984,17 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_20 = { .ln_shrd = 0x0e00, }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_30 = { + .serdes = 0x8800, + .pcs = 0x9000, + .pcs_misc = 0x9800, + .tx = 0x0000, + .rx = 0x0200, + .txz = 0xe000, + .rxz = 0xe200, + .ln_shrd = 0x8000, +}; + static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .lanes = 1, @@ -3704,6 +3873,38 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x4_pciephy_cfg = { .has_nocsr_reset = true, }; +static const struct qmp_phy_cfg x1e80100_qmp_gen4x8_pciephy_cfg = { + .lanes = 8, + + .offsets = &qmp_pcie_offsets_v6_30, + .tbls = { + .serdes = x1e80100_qmp_gen4x8_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_serdes_tbl), + .rx = x1e80100_qmp_gen4x8_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_rx_tbl), + .txz = x1e80100_qmp_gen4x8_pcie_txz_tbl, + .txz_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_txz_tbl), + .rxz = x1e80100_qmp_gen4x8_pcie_rxz_tbl, + .rxz_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_rxz_tbl), + .pcs = x1e80100_qmp_gen4x8_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_pcs_tbl), + .pcs_misc = x1e80100_qmp_gen4x8_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_pcs_misc_tbl), + .ln_shrd = x1e80100_qmp_gen4x8_pcie_ln_shrd_tbl, + .ln_shrd_num = ARRAY_SIZE(x1e80100_qmp_gen4x8_pcie_ln_shrd_tbl), + }, + + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = sm8550_qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8550_qmp_phy_vreg_l), + .regs = pciephy_v6_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, + .has_nocsr_reset = true, +}; + static void qmp_pcie_init_port_b(struct qmp_pcie *qmp, const struct qmp_phy_cfg_tbls *tbls) { const struct qmp_phy_cfg *cfg = qmp->cfg; @@ -3751,6 +3952,13 @@ static void qmp_pcie_init_registers(struct qmp_pcie *qmp, const struct qmp_phy_c qmp_configure(qmp->dev, serdes, tbls->serdes, tbls->serdes_num); + /* + * Tx/Rx registers that require different settings than + * txz/rxz must be programmed after txz/rxz. + */ + qmp_configure(qmp->dev, qmp->txz, tbls->txz, tbls->txz_num); + qmp_configure(qmp->dev, qmp->rxz, tbls->rxz, tbls->rxz_num); + qmp_configure_lane(qmp->dev, tx, tbls->tx, tbls->tx_num, 1); qmp_configure_lane(qmp->dev, rx, tbls->rx, tbls->rx_num, 1); @@ -4293,6 +4501,9 @@ static int qmp_pcie_parse_dt(struct qmp_pcie *qmp) return PTR_ERR(qmp->port_b); } + qmp->txz = base + offs->txz; + qmp->rxz = base + offs->rxz; + if (cfg->tbls.ln_shrd) qmp->ln_shrd = base + offs->ln_shrd; @@ -4478,6 +4689,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,x1e80100-qmp-gen4x4-pcie-phy", .data = &x1e80100_qmp_gen4x4_pciephy_cfg, + }, { + .compatible = "qcom,x1e80100-qmp-gen4x8-pcie-phy", + .data = &x1e80100_qmp_gen4x8_pciephy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_30.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_30.h new file mode 100644 index 0000000000000..5a58ff197e6e0 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_30.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V6_30_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V6_30_H_ + +/* Only for QMP V6_30 PHY - PCIE have different offsets than V6 */ +#define QPHY_PCIE_V6_30_PCS_POWER_STATE_CONFIG2 0x014 +#define QPHY_PCIE_V6_30_PCS_TX_RX_CONFIG 0x020 +#define QPHY_PCIE_V6_30_PCS_ENDPOINT_REFCLK_DRIVE 0x024 +#define QPHY_PCIE_V6_30_PCS_OSC_DTCT_ACTIONS 0x098 +#define QPHY_PCIE_V6_30_PCS_EQ_CONFIG1 0x0a8 +#define QPHY_PCIE_V6_30_PCS_G3_RXEQEVAL_TIME 0x0f8 +#define QPHY_PCIE_V6_30_PCS_G4_RXEQEVAL_TIME 0x0fc +#define QPHY_PCIE_V6_30_PCS_G4_EQ_CONFIG5 0x110 +#define QPHY_PCIE_V6_30_PCS_G4_PRE_GAIN 0x164 +#define QPHY_PCIE_V6_30_PCS_RX_MARGINING_CONFIG1 0x184 +#define QPHY_PCIE_V6_30_PCS_RX_MARGINING_CONFIG3 0x18c +#define QPHY_PCIE_V6_30_PCS_RX_MARGINING_CONFIG5 0x194 +#define QPHY_PCIE_V6_30_PCS_G3_FOM_EQ_CONFIG5 0x1b4 +#define QPHY_PCIE_V6_30_PCS_G4_FOM_EQ_CONFIG5 0x1c8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_30.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_30.h new file mode 100644 index 0000000000000..369120d88bc27 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_30.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_V6_30_H_ +#define QCOM_PHY_QMP_PCS_V6_30_H_ + +/* Only for QMP V6_30 PHY - PCIe PCS registers */ +#define QPHY_V6_30_PCS_LOCK_DETECT_CONFIG2 0x0cc +#define QPHY_V6_30_PCS_G3S2_PRE_GAIN 0x17c +#define QPHY_V6_30_PCS_RX_SIGDET_LVL 0x194 +#define QPHY_V6_30_PCS_ALIGN_DETECT_CONFIG7 0x1dc +#define QPHY_V6_30_PCS_TX_RX_CONFIG 0x1e0 +#define QPHY_V6_30_PCS_TX_RX_CONFIG2 0x1e4 +#define QPHY_V6_30_PCS_EQ_CONFIG4 0x1fc +#define QPHY_V6_30_PCS_EQ_CONFIG5 0x200 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index 1246d3bc8b92f..acd6075bf6d96 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -871,6 +871,16 @@ static const struct qmp_phy_init_tbl sdx75_usb3_uniphy_pcs_usb_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), }; +static const struct qmp_phy_init_tbl qcs8300_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0xf2), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), +}; + static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82), @@ -989,6 +999,40 @@ static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), }; +static const struct qmp_phy_init_tbl qcs8300_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00), +}; + static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_rx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdc), QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd), @@ -1462,6 +1506,24 @@ static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = { .regs = qmp_v5_usb3phy_regs_layout, }; +static const struct qmp_phy_cfg qcs8300_usb3_uniphy_cfg = { + .offsets = &qmp_usb_offsets_v5, + + .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl), + .tx_tbl = qcs8300_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qcs8300_usb3_uniphy_tx_tbl), + .rx_tbl = qcs8300_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qcs8300_usb3_uniphy_rx_tbl), + .pcs_tbl = sa8775p_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sa8775p_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_usb_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v5_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg sc8280xp_usb3_uniphy_cfg = { .offsets = &qmp_usb_offsets_v5, @@ -2247,6 +2309,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { }, { .compatible = "qcom,msm8996-qmp-usb3-phy", .data = &msm8996_usb3phy_cfg, + }, { + .compatible = "qcom,qcs8300-qmp-usb3-uni-phy", + .data = &qcs8300_usb3_uniphy_cfg, }, { .compatible = "qcom,qdu1000-qmp-usb3-uni-phy", .data = &qdu1000_usb3_uniphy_cfg, diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c index e3ad7cea51099..248550ef98cab 100644 --- a/drivers/phy/realtek/phy-rtk-usb2.c +++ b/drivers/phy/realtek/phy-rtk-usb2.c @@ -1023,6 +1023,8 @@ static int rtk_usb2phy_probe(struct platform_device *pdev) rtk_phy->dev = &pdev->dev; rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); + if (!rtk_phy->phy_cfg) + return -ENOMEM; memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); @@ -1298,7 +1300,7 @@ MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); static struct platform_driver rtk_usb2phy_driver = { .probe = rtk_usb2phy_probe, - .remove_new = rtk_usb2phy_remove, + .remove = rtk_usb2phy_remove, .driver = { .name = "rtk-usb2phy", .of_match_table = usbphy_rtk_dt_match, diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c index dfcf4b921bba6..cce453686db21 100644 --- a/drivers/phy/realtek/phy-rtk-usb3.c +++ b/drivers/phy/realtek/phy-rtk-usb3.c @@ -577,6 +577,8 @@ static int rtk_usb3phy_probe(struct platform_device *pdev) rtk_phy->dev = &pdev->dev; rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); + if (!rtk_phy->phy_cfg) + return -ENOMEM; memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); @@ -734,7 +736,7 @@ MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); static struct platform_driver rtk_usb3phy_driver = { .probe = rtk_usb3phy_probe, - .remove_new = rtk_usb3phy_remove, + .remove = rtk_usb3phy_remove, .driver = { .name = "rtk-usb3phy", .of_match_table = usbphy_rtk_dt_match, diff --git a/drivers/phy/renesas/phy-rcar-gen3-pcie.c b/drivers/phy/renesas/phy-rcar-gen3-pcie.c index 0ce7e9c944447..feca4cb2ff4d1 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-pcie.c +++ b/drivers/phy/renesas/phy-rcar-gen3-pcie.c @@ -132,11 +132,11 @@ static void rcar_gen3_phy_pcie_remove(struct platform_device *pdev) static struct platform_driver rcar_gen3_phy_driver = { .driver = { - .name = "phy_rcar_gen3_pcie", - .of_match_table = rcar_gen3_phy_pcie_match_table, + .name = "phy_rcar_gen3_pcie", + .of_match_table = rcar_gen3_phy_pcie_match_table, }, - .probe = rcar_gen3_phy_pcie_probe, - .remove_new = rcar_gen3_phy_pcie_remove, + .probe = rcar_gen3_phy_pcie_probe, + .remove = rcar_gen3_phy_pcie_remove, }; module_platform_driver(rcar_gen3_phy_driver); diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 58e1233051526..775f4f973a6cc 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -825,7 +825,7 @@ static struct platform_driver rcar_gen3_phy_usb2_driver = { .of_match_table = rcar_gen3_phy_usb2_match_table, }, .probe = rcar_gen3_phy_usb2_probe, - .remove_new = rcar_gen3_phy_usb2_remove, + .remove = rcar_gen3_phy_usb2_remove, }; module_platform_driver(rcar_gen3_phy_usb2_driver); diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb3.c b/drivers/phy/renesas/phy-rcar-gen3-usb3.c index e2d630edd992d..5c267d148c90b 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb3.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb3.c @@ -206,11 +206,11 @@ static void rcar_gen3_phy_usb3_remove(struct platform_device *pdev) static struct platform_driver rcar_gen3_phy_usb3_driver = { .driver = { - .name = "phy_rcar_gen3_usb3", - .of_match_table = rcar_gen3_phy_usb3_match_table, + .name = "phy_rcar_gen3_usb3", + .of_match_table = rcar_gen3_phy_usb3_match_table, }, - .probe = rcar_gen3_phy_usb3_probe, - .remove_new = rcar_gen3_phy_usb3_remove, + .probe = rcar_gen3_phy_usb3_probe, + .remove = rcar_gen3_phy_usb3_remove, }; module_platform_driver(rcar_gen3_phy_usb3_driver); diff --git a/drivers/phy/renesas/r8a779f0-ether-serdes.c b/drivers/phy/renesas/r8a779f0-ether-serdes.c index f1f1da4a0b1fe..3b2d8cef75e52 100644 --- a/drivers/phy/renesas/r8a779f0-ether-serdes.c +++ b/drivers/phy/renesas/r8a779f0-ether-serdes.c @@ -404,7 +404,7 @@ static void r8a779f0_eth_serdes_remove(struct platform_device *pdev) static struct platform_driver r8a779f0_eth_serdes_driver_platform = { .probe = r8a779f0_eth_serdes_probe, - .remove_new = r8a779f0_eth_serdes_remove, + .remove = r8a779f0_eth_serdes_remove, .driver = { .name = "r8a779f0_eth_serdes", .of_match_table = r8a779f0_eth_serdes_of_table, diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c index 98c92d6c482fe..2ab99e1d47ebe 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c @@ -472,7 +472,7 @@ static struct platform_driver rockchip_inno_csidphy_driver = { .of_match_table = rockchip_inno_csidphy_match_id, }, .probe = rockchip_inno_csidphy_probe, - .remove_new = rockchip_inno_csidphy_remove, + .remove = rockchip_inno_csidphy_remove, }; module_platform_driver(rockchip_inno_csidphy_driver); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c index 6405943a26767..d5b1a4e2f7d34 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -784,7 +784,7 @@ static struct platform_driver inno_dsidphy_driver = { .of_match_table = of_match_ptr(inno_dsidphy_of_match), }, .probe = inno_dsidphy_probe, - .remove_new = inno_dsidphy_remove, + .remove = inno_dsidphy_remove, }; module_platform_driver(inno_dsidphy_driver); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index 053bd62e31ba7..8dcc2bb777b54 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -1424,8 +1424,8 @@ static const struct of_device_id inno_hdmi_phy_of_match[] = { MODULE_DEVICE_TABLE(of, inno_hdmi_phy_of_match); static struct platform_driver inno_hdmi_phy_driver = { - .probe = inno_hdmi_phy_probe, - .remove_new = inno_hdmi_phy_remove, + .probe = inno_hdmi_phy_probe, + .remove = inno_hdmi_phy_remove, .driver = { .name = "inno-hdmi-phy", .of_match_table = inno_hdmi_phy_of_match, diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 4f71373ae6e1a..96f3d868a526f 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -229,9 +229,10 @@ struct rockchip_usb2phy_port { * @dev: pointer to device. * @grf: General Register Files regmap. * @usbgrf: USB General Register Files regmap. - * @clk: clock struct of phy input clk. + * @clks: array of phy input clocks. * @clk480m: clock struct of phy output clk. * @clk480m_hw: clock struct of phy output clk management. + * @num_clks: number of phy input clocks. * @phy_reset: phy reset control. * @chg_state: states involved in USB charger detection. * @chg_type: USB charger types. @@ -246,9 +247,10 @@ struct rockchip_usb2phy { struct device *dev; struct regmap *grf; struct regmap *usbgrf; - struct clk *clk; + struct clk_bulk_data *clks; struct clk *clk480m; struct clk_hw clk480m_hw; + int num_clks; struct reset_control *phy_reset; enum usb_chg_state chg_state; enum power_supply_type chg_type; @@ -310,6 +312,13 @@ static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy) return 0; } +static void rockchip_usb2phy_clk_bulk_disable(void *data) +{ + struct rockchip_usb2phy *rphy = data; + + clk_bulk_disable_unprepare(rphy->num_clks, rphy->clks); +} + static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = @@ -376,7 +385,9 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) { struct device_node *node = rphy->dev->of_node; struct clk_init_data init; + struct clk *refclk = NULL; const char *clk_name; + int i; int ret = 0; init.flags = 0; @@ -386,8 +397,15 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) /* optional override of the clockname */ of_property_read_string(node, "clock-output-names", &init.name); - if (rphy->clk) { - clk_name = __clk_get_name(rphy->clk); + for (i = 0; i < rphy->num_clks; i++) { + if (!strncmp(rphy->clks[i].id, "phyclk", 6)) { + refclk = rphy->clks[i].clk; + break; + } + } + + if (!IS_ERR(refclk)) { + clk_name = __clk_get_name(refclk); init.parent_names = &clk_name; init.num_parents = 1; } else { @@ -418,30 +436,28 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) { - int ret; struct device_node *node = rphy->dev->of_node; struct extcon_dev *edev; + int ret; if (of_property_read_bool(node, "extcon")) { edev = extcon_get_edev_by_phandle(rphy->dev, 0); - if (IS_ERR(edev)) { - if (PTR_ERR(edev) != -EPROBE_DEFER) - dev_err(rphy->dev, "Invalid or missing extcon\n"); - return PTR_ERR(edev); - } + if (IS_ERR(edev)) + return dev_err_probe(rphy->dev, PTR_ERR(edev), + "invalid or missing extcon\n"); } else { /* Initialize extcon device */ edev = devm_extcon_dev_allocate(rphy->dev, rockchip_usb2phy_extcon_cable); if (IS_ERR(edev)) - return -ENOMEM; + return dev_err_probe(rphy->dev, PTR_ERR(edev), + "failed to allocate extcon device\n"); ret = devm_extcon_dev_register(rphy->dev, edev); - if (ret) { - dev_err(rphy->dev, "failed to register extcon device\n"); - return ret; - } + if (ret) + return dev_err_probe(rphy->dev, ret, + "failed to register extcon device\n"); } rphy->edev = edev; @@ -1327,7 +1343,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) struct rockchip_usb2phy *rphy; const struct rockchip_usb2phy_cfg *phy_cfgs; unsigned int reg; - int index, ret; + int index = 0, ret; rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL); if (!rphy) @@ -1339,9 +1355,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) dev_err(dev, "failed to locate usbgrf\n"); return PTR_ERR(rphy->grf); } - } - - else { + } else { rphy->grf = syscon_node_to_regmap(dev->parent->of_node); if (IS_ERR(rphy->grf)) return PTR_ERR(rphy->grf); @@ -1358,16 +1372,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) } if (of_property_read_u32_index(np, "reg", 0, ®)) { - dev_err(dev, "the reg property is not assigned in %pOFn node\n", - np); + dev_err(dev, "the reg property is not assigned in %pOFn node\n", np); return -EINVAL; } /* support address_cells=2 */ if (of_property_count_u32_elems(np, "reg") > 2 && reg == 0) { if (of_property_read_u32_index(np, "reg", 1, ®)) { - dev_err(dev, "the reg property is not assigned in %pOFn node\n", - np); + dev_err(dev, "the reg property is not assigned in %pOFn node\n", np); return -EINVAL; } } @@ -1386,8 +1398,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) if (ret) return ret; - /* find out a proper config which can be matched with dt. */ - index = 0; + /* find a proper config that can be matched with the DT */ do { if (phy_cfgs[index].reg == reg) { rphy->phy_cfg = &phy_cfgs[index]; @@ -1406,17 +1417,25 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) if (IS_ERR(rphy->phy_reset)) return PTR_ERR(rphy->phy_reset); - rphy->clk = devm_clk_get_optional_enabled(dev, "phyclk"); - if (IS_ERR(rphy->clk)) { - return dev_err_probe(&pdev->dev, PTR_ERR(rphy->clk), - "failed to get phyclk\n"); - } + ret = devm_clk_bulk_get_all(dev, &rphy->clks); + if (ret == -EPROBE_DEFER) + return dev_err_probe(&pdev->dev, -EPROBE_DEFER, + "failed to get phy clock\n"); + + /* Clocks are optional */ + rphy->num_clks = ret < 0 ? 0 : ret; ret = rockchip_usb2phy_clk480m_register(rphy); - if (ret) { - dev_err(dev, "failed to register 480m output clock\n"); + if (ret) + return dev_err_probe(dev, ret, "failed to register 480m output clock\n"); + + ret = clk_bulk_prepare_enable(rphy->num_clks, rphy->clks); + if (ret) + return dev_err_probe(dev, ret, "failed to enable phy clock\n"); + + ret = devm_add_action_or_reset(dev, rockchip_usb2phy_clk_bulk_disable, rphy); + if (ret) return ret; - } if (rphy->phy_cfg->phy_tuning) { ret = rphy->phy_cfg->phy_tuning(rphy); @@ -1436,8 +1455,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); if (IS_ERR(phy)) { - dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); - ret = PTR_ERR(phy); + ret = dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); goto put_child; } @@ -1446,13 +1464,11 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) /* initialize otg/host port separately */ if (of_node_name_eq(child_np, "host-port")) { - ret = rockchip_usb2phy_host_port_init(rphy, rport, - child_np); + ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np); if (ret) goto put_child; } else { - ret = rockchip_usb2phy_otg_port_init(rphy, rport, - child_np); + ret = rockchip_usb2phy_otg_port_init(rphy, rport, child_np); if (ret) goto put_child; } @@ -1474,8 +1490,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) "rockchip_usb2phy", rphy); if (ret) { - dev_err(rphy->dev, - "failed to request usb2phy irq handle\n"); + dev_err_probe(rphy->dev, ret, "failed to request usb2phy irq handle\n"); goto put_child; } } @@ -1495,6 +1510,30 @@ static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) BIT(2) << BIT_WRITEABLE_SHIFT | 0); } +static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + int ret; + u32 reg = rphy->phy_cfg->reg; + + /* Deassert SIDDQ to power on analog block */ + ret = regmap_write(rphy->grf, reg + 0x0010, GENMASK(29, 29) | 0x0000); + if (ret) + return ret; + + /* Do reset after exit IDDQ mode */ + ret = rockchip_usb2phy_reset(rphy); + if (ret) + return ret; + + /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ + ret |= regmap_write(rphy->grf, reg + 0x000c, GENMASK(27, 24) | 0x0900); + + /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ + ret |= regmap_write(rphy->grf, reg + 0x0010, GENMASK(20, 19) | 0x0010); + + return ret; +} + static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) { int ret; @@ -1923,6 +1962,84 @@ static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3576_phy_cfgs[] = { + { + .reg = 0x0, + .num_ports = 1, + .phy_tuning = rk3576_usb2phy_tuning, + .clkout_ctl = { 0x0008, 0, 0, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x00c0, 1, 1, 0, 1 }, + .bvalid_det_st = { 0x00c4, 1, 1, 0, 1 }, + .bvalid_det_clr = { 0x00c8, 1, 1, 0, 1 }, + .ls_det_en = { 0x00c0, 0, 0, 0, 1 }, + .ls_det_st = { 0x00c4, 0, 0, 0, 1 }, + .ls_det_clr = { 0x00c8, 0, 0, 0, 1 }, + .disfall_en = { 0x00c0, 6, 6, 0, 1 }, + .disfall_st = { 0x00c4, 6, 6, 0, 1 }, + .disfall_clr = { 0x00c8, 6, 6, 0, 1 }, + .disrise_en = { 0x00c0, 5, 5, 0, 1 }, + .disrise_st = { 0x00c4, 5, 5, 0, 1 }, + .disrise_clr = { 0x00c8, 5, 5, 0, 1 }, + .utmi_avalid = { 0x0080, 1, 1, 0, 1 }, + .utmi_bvalid = { 0x0080, 0, 0, 0, 1 }, + .utmi_ls = { 0x0080, 5, 4, 0, 1 }, + } + }, + .chg_det = { + .cp_det = { 0x0080, 8, 8, 0, 1 }, + .dcp_det = { 0x0080, 8, 8, 0, 1 }, + .dp_det = { 0x0080, 9, 9, 1, 0 }, + .idm_sink_en = { 0x0010, 5, 5, 1, 0 }, + .idp_sink_en = { 0x0010, 5, 5, 0, 1 }, + .idp_src_en = { 0x0010, 14, 14, 0, 1 }, + .rdm_pdwn_en = { 0x0010, 14, 14, 0, 1 }, + .vdm_src_en = { 0x0010, 7, 6, 0, 3 }, + .vdp_src_en = { 0x0010, 7, 6, 0, 3 }, + }, + }, + { + .reg = 0x2000, + .num_ports = 1, + .phy_tuning = rk3576_usb2phy_tuning, + .clkout_ctl = { 0x2008, 0, 0, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x2000, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x20c0, 1, 1, 0, 1 }, + .bvalid_det_st = { 0x20c4, 1, 1, 0, 1 }, + .bvalid_det_clr = { 0x20c8, 1, 1, 0, 1 }, + .ls_det_en = { 0x20c0, 0, 0, 0, 1 }, + .ls_det_st = { 0x20c4, 0, 0, 0, 1 }, + .ls_det_clr = { 0x20c8, 0, 0, 0, 1 }, + .disfall_en = { 0x20c0, 6, 6, 0, 1 }, + .disfall_st = { 0x20c4, 6, 6, 0, 1 }, + .disfall_clr = { 0x20c8, 6, 6, 0, 1 }, + .disrise_en = { 0x20c0, 5, 5, 0, 1 }, + .disrise_st = { 0x20c4, 5, 5, 0, 1 }, + .disrise_clr = { 0x20c8, 5, 5, 0, 1 }, + .utmi_avalid = { 0x2080, 1, 1, 0, 1 }, + .utmi_bvalid = { 0x2080, 0, 0, 0, 1 }, + .utmi_ls = { 0x2080, 5, 4, 0, 1 }, + } + }, + .chg_det = { + .cp_det = { 0x2080, 8, 8, 0, 1 }, + .dcp_det = { 0x2080, 8, 8, 0, 1 }, + .dp_det = { 0x2080, 9, 9, 1, 0 }, + .idm_sink_en = { 0x2010, 5, 5, 1, 0 }, + .idp_sink_en = { 0x2010, 5, 5, 0, 1 }, + .idp_src_en = { 0x2010, 14, 14, 0, 1 }, + .rdm_pdwn_en = { 0x2010, 14, 14, 0, 1 }, + .vdm_src_en = { 0x2010, 7, 6, 0, 3 }, + .vdp_src_en = { 0x2010, 7, 6, 0, 3 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = { { .reg = 0x0000, @@ -2094,6 +2211,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, + { .compatible = "rockchip,rk3576-usb2phy", .data = &rk3576_phy_cfgs }, { .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs }, { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, {} diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 9f084697dd05c..ceab9c71d3b53 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -256,13 +256,10 @@ struct ropll_config { }; enum rk_hdptx_reset { - RST_PHY = 0, - RST_APB, + RST_APB = 0, RST_INIT, RST_CMN, RST_LANE, - RST_ROPLL, - RST_LCPLL, RST_MAX }; @@ -665,11 +662,6 @@ static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx) { u32 val; - /* reset phy and apb, or phy locked flag may keep 1 */ - reset_control_assert(hdptx->rsts[RST_PHY].rstc); - usleep_range(20, 30); - reset_control_deassert(hdptx->rsts[RST_PHY].rstc); - reset_control_assert(hdptx->rsts[RST_APB].rstc); usleep_range(20, 30); reset_control_deassert(hdptx->rsts[RST_APB].rstc); @@ -792,10 +784,6 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx, rk_hdptx_pre_power_up(hdptx); - reset_control_assert(hdptx->rsts[RST_ROPLL].rstc); - usleep_range(20, 30); - reset_control_deassert(hdptx->rsts[RST_ROPLL].rstc); - rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_cmn_init_seq); rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_cmn_init_seq); @@ -1098,13 +1086,10 @@ static int rk_hdptx_phy_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(hdptx->regmap), "Failed to init regmap\n"); - hdptx->rsts[RST_PHY].id = "phy"; hdptx->rsts[RST_APB].id = "apb"; hdptx->rsts[RST_INIT].id = "init"; hdptx->rsts[RST_CMN].id = "cmn"; hdptx->rsts[RST_LANE].id = "lane"; - hdptx->rsts[RST_ROPLL].id = "ropll"; - hdptx->rsts[RST_LCPLL].id = "lcpll"; ret = devm_reset_control_bulk_get_exclusive(dev, RST_MAX, hdptx->rsts); if (ret) diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 4efcb78b0ab18..122ae0fdc785e 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -1210,7 +1210,7 @@ MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); static struct platform_driver rockchip_typec_phy_driver = { .probe = rockchip_typec_phy_probe, - .remove_new = rockchip_typec_phy_remove, + .remove = rockchip_typec_phy_remove, .driver = { .name = "rockchip-typec-phy", .of_match_table = rockchip_typec_phy_dt_ids, diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c index 2c51e5c62d3eb..5b1e8a3806ed4 100644 --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c @@ -1538,6 +1538,43 @@ static const char * const rk_udphy_rst_list[] = { "init", "cmn", "lane", "pcs_apb", "pma_apb" }; +static const struct rk_udphy_cfg rk3576_udphy_cfgs = { + .num_phys = 1, + .phy_ids = { 0x2b010000 }, + .num_rsts = ARRAY_SIZE(rk_udphy_rst_list), + .rst_list = rk_udphy_rst_list, + .grfcfg = { + /* u2phy-grf */ + .bvalid_phy_con = RK_UDPHY_GEN_GRF_REG(0x0010, 1, 0, 0x2, 0x3), + .bvalid_grf_con = RK_UDPHY_GEN_GRF_REG(0x0000, 15, 14, 0x1, 0x3), + + /* usb-grf */ + .usb3otg0_cfg = RK_UDPHY_GEN_GRF_REG(0x0030, 15, 0, 0x1100, 0x0188), + + /* usbdpphy-grf */ + .low_pwrn = RK_UDPHY_GEN_GRF_REG(0x0004, 13, 13, 0, 1), + .rx_lfps = RK_UDPHY_GEN_GRF_REG(0x0004, 14, 14, 0, 1), + }, + .vogrfcfg = { + { + .hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3), + .dp_lane_reg = 0x0000, + }, + }, + .dp_tx_ctrl_cfg = { + rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, + rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, + rk3588_dp_tx_drv_ctrl_hbr2, + rk3588_dp_tx_drv_ctrl_hbr3, + }, + .dp_tx_ctrl_cfg_typec = { + rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, + rk3588_dp_tx_drv_ctrl_rbr_hbr_typec, + rk3588_dp_tx_drv_ctrl_hbr2, + rk3588_dp_tx_drv_ctrl_hbr3, + }, +}; + static const struct rk_udphy_cfg rk3588_udphy_cfgs = { .num_phys = 2, .phy_ids = { @@ -1584,6 +1621,10 @@ static const struct rk_udphy_cfg rk3588_udphy_cfgs = { }; static const struct of_device_id rk_udphy_dt_match[] = { + { + .compatible = "rockchip,rk3576-usbdp-phy", + .data = &rk3576_udphy_cfgs + }, { .compatible = "rockchip,rk3588-usbdp-phy", .data = &rk3588_udphy_cfgs diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig index 3fc3d0781fb8a..304614b6dabfc 100644 --- a/drivers/phy/st/Kconfig +++ b/drivers/phy/st/Kconfig @@ -33,6 +33,17 @@ config PHY_STIH407_USB Enable this support to enable the picoPHY device used by USB2 and USB3 controllers on STMicroelectronics STiH407 SoC families. +config PHY_STM32_COMBOPHY + tristate "STMicroelectronics COMBOPHY driver for STM32MP25" + depends on ARCH_STM32 || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support the COMBOPHY device used by USB3 or PCIe + controllers on STMicroelectronics STM32MP25 SoC. + This driver controls the COMBOPHY block to generate the PCIe 100Mhz + reference clock from either the external clock generator or HSE + internal SoC clock source. + config PHY_STM32_USBPHYC tristate "STMicroelectronics STM32 USB HS PHY Controller driver" depends on ARCH_STM32 || COMPILE_TEST diff --git a/drivers/phy/st/Makefile b/drivers/phy/st/Makefile index c862dd937b647..cb80e954ea9f0 100644 --- a/drivers/phy/st/Makefile +++ b/drivers/phy/st/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o +obj-$(CONFIG_PHY_STM32_COMBOPHY) += phy-stm32-combophy.o obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o diff --git a/drivers/phy/st/phy-stm32-combophy.c b/drivers/phy/st/phy-stm32-combophy.c new file mode 100644 index 0000000000000..765bb34fe3589 --- /dev/null +++ b/drivers/phy/st/phy-stm32-combophy.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics COMBOPHY STM32MP25 Controller driver. + * + * Copyright (C) 2024 STMicroelectronics + * Author: Christian Bruel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYSCFG_COMBOPHY_CR1 0x4c00 +#define SYSCFG_COMBOPHY_CR2 0x4c04 +#define SYSCFG_COMBOPHY_CR4 0x4c0c +#define SYSCFG_COMBOPHY_CR5 0x4c10 +#define SYSCFG_COMBOPHY_SR 0x4c14 +#define SYSCFG_PCIEPRGCR 0x6080 + +/* SYSCFG PCIEPRGCR */ +#define STM32MP25_PCIEPRGCR_EN BIT(0) +#define STM32MP25_PCIEPRG_IMPCTRL_OHM GENMASK(3, 1) +#define STM32MP25_PCIEPRG_IMPCTRL_VSWING GENMASK(5, 4) + +/* SYSCFG SYSCFG_COMBOPHY_SR */ +#define STM32MP25_PIPE0_PHYSTATUS BIT(1) + +/* SYSCFG CR1 */ +#define SYSCFG_COMBOPHY_CR1_REFUSEPAD BIT(0) +#define SYSCFG_COMBOPHY_CR1_MPLLMULT GENMASK(7, 1) +#define SYSCFG_COMBOPHY_CR1_REFCLKSEL GENMASK(16, 8) +#define SYSCFG_COMBOPHY_CR1_REFCLKDIV2 BIT(17) +#define SYSCFG_COMBOPHY_CR1_REFSSPEN BIT(18) +#define SYSCFG_COMBOPHY_CR1_SSCEN BIT(19) + +/* SYSCFG CR4 */ +#define SYSCFG_COMBOPHY_CR4_RX0_EQ GENMASK(2, 0) + +#define MPLLMULT_19_2 (0x02u << 1) +#define MPLLMULT_20 (0x7du << 1) +#define MPLLMULT_24 (0x68u << 1) +#define MPLLMULT_25 (0x64u << 1) +#define MPLLMULT_26 (0x60u << 1) +#define MPLLMULT_38_4 (0x41u << 1) +#define MPLLMULT_48 (0x6cu << 1) +#define MPLLMULT_50 (0x32u << 1) +#define MPLLMULT_52 (0x30u << 1) +#define MPLLMULT_100 (0x19u << 1) + +#define REFCLKSEL_0 0 +#define REFCLKSEL_1 (0x108u << 8) + +#define REFCLDIV_0 0 + +/* SYSCFG CR2 */ +#define SYSCFG_COMBOPHY_CR2_MODESEL GENMASK(1, 0) +#define SYSCFG_COMBOPHY_CR2_ISO_DIS BIT(15) + +#define COMBOPHY_MODESEL_PCIE 0 +#define COMBOPHY_MODESEL_USB 3 + +/* SYSCFG CR5 */ +#define SYSCFG_COMBOPHY_CR5_COMMON_CLOCKS BIT(12) + +#define COMBOPHY_SUP_ANA_MPLL_LOOP_CTL 0xc0 +#define COMBOPHY_PROP_CNTRL GENMASK(7, 4) + +/* Required apb/ker clocks first, optional pad last. */ +static const char * const combophy_clks[] = {"apb", "ker", "pad"}; +#define APB_CLK 0 +#define KER_CLK 1 +#define PAD_CLK 2 + +struct stm32_combophy { + struct phy *phy; + struct regmap *regmap; + struct device *dev; + void __iomem *base; + struct reset_control *phy_reset; + struct clk_bulk_data clks[ARRAY_SIZE(combophy_clks)]; + int num_clks; + bool have_pad_clk; + unsigned int type; + bool is_init; + int irq_wakeup; +}; + +struct clk_impedance { + u32 microohm; + u32 vswing[4]; +}; + +/* + * lookup table to hold the settings needed for a ref clock frequency + * impedance, the offset is used to set the IMP_CTL and DE_EMP bit of the + * PRG_IMP_CTRL register. Use ordered discrete values in the table + */ +static const struct clk_impedance imp_lookup[] = { + { 6090000, { 442000, 564000, 684000, 802000 } }, + { 5662000, { 528000, 621000, 712000, 803000 } }, + { 5292000, { 491000, 596000, 700000, 802000 } }, + { 4968000, { 558000, 640000, 722000, 803000 } }, + { 4684000, { 468000, 581000, 692000, 802000 } }, + { 4429000, { 554000, 613000, 717000, 803000 } }, + { 4204000, { 511000, 609000, 706000, 802000 } }, + { 3999000, { 571000, 648000, 726000, 803000 } } +}; + +static int stm32_impedance_tune(struct stm32_combophy *combophy) +{ + u8 imp_size = ARRAY_SIZE(imp_lookup); + u8 vswing_size = ARRAY_SIZE(imp_lookup[0].vswing); + u8 imp_of, vswing_of; + u32 max_imp = imp_lookup[0].microohm; + u32 min_imp = imp_lookup[imp_size - 1].microohm; + u32 max_vswing = imp_lookup[imp_size - 1].vswing[vswing_size - 1]; + u32 min_vswing = imp_lookup[0].vswing[0]; + u32 val; + + if (!of_property_read_u32(combophy->dev->of_node, "st,output-micro-ohms", &val)) { + if (val < min_imp || val > max_imp) { + dev_err(combophy->dev, "Invalid value %u for output ohm\n", val); + return -EINVAL; + } + + for (imp_of = 0; imp_of < ARRAY_SIZE(imp_lookup); imp_of++) + if (imp_lookup[imp_of].microohm <= val) + break; + + dev_dbg(combophy->dev, "Set %u micro-ohms output impedance\n", + imp_lookup[imp_of].microohm); + + regmap_update_bits(combophy->regmap, SYSCFG_PCIEPRGCR, + STM32MP25_PCIEPRG_IMPCTRL_OHM, + FIELD_PREP(STM32MP25_PCIEPRG_IMPCTRL_OHM, imp_of)); + } else { + regmap_read(combophy->regmap, SYSCFG_PCIEPRGCR, &val); + imp_of = FIELD_GET(STM32MP25_PCIEPRG_IMPCTRL_OHM, val); + } + + if (!of_property_read_u32(combophy->dev->of_node, "st,output-vswing-microvolt", &val)) { + if (val < min_vswing || val > max_vswing) { + dev_err(combophy->dev, "Invalid value %u for output vswing\n", val); + return -EINVAL; + } + + for (vswing_of = 0; vswing_of < ARRAY_SIZE(imp_lookup[imp_of].vswing); vswing_of++) + if (imp_lookup[imp_of].vswing[vswing_of] >= val) + break; + + dev_dbg(combophy->dev, "Set %u microvolt swing\n", + imp_lookup[imp_of].vswing[vswing_of]); + + regmap_update_bits(combophy->regmap, SYSCFG_PCIEPRGCR, + STM32MP25_PCIEPRG_IMPCTRL_VSWING, + FIELD_PREP(STM32MP25_PCIEPRG_IMPCTRL_VSWING, vswing_of)); + } + + return 0; +} + +static int stm32_combophy_pll_init(struct stm32_combophy *combophy) +{ + int ret; + u32 refclksel, pllmult, propcntrl, val; + u32 clk_rate; + struct clk *clk; + u32 cr1_val = 0, cr1_mask = 0; + + if (combophy->have_pad_clk) + clk = combophy->clks[PAD_CLK].clk; + else + clk = combophy->clks[KER_CLK].clk; + + clk_rate = clk_get_rate(clk); + + dev_dbg(combophy->dev, "%s pll init rate %d\n", + combophy->have_pad_clk ? "External" : "Ker", clk_rate); + + if (combophy->type != PHY_TYPE_PCIE) { + cr1_mask |= SYSCFG_COMBOPHY_CR1_REFSSPEN; + cr1_val |= SYSCFG_COMBOPHY_CR1_REFSSPEN; + } + + if (of_property_present(combophy->dev->of_node, "st,ssc-on")) { + dev_dbg(combophy->dev, "Enabling clock with SSC\n"); + cr1_mask |= SYSCFG_COMBOPHY_CR1_SSCEN; + cr1_val |= SYSCFG_COMBOPHY_CR1_SSCEN; + } + + switch (clk_rate) { + case 100000000: + pllmult = MPLLMULT_100; + refclksel = REFCLKSEL_0; + propcntrl = 0x8u << 4; + break; + case 19200000: + pllmult = MPLLMULT_19_2; + refclksel = REFCLKSEL_1; + propcntrl = 0x8u << 4; + break; + case 25000000: + pllmult = MPLLMULT_25; + refclksel = REFCLKSEL_0; + propcntrl = 0xeu << 4; + break; + case 24000000: + pllmult = MPLLMULT_24; + refclksel = REFCLKSEL_1; + propcntrl = 0xeu << 4; + break; + case 20000000: + pllmult = MPLLMULT_20; + refclksel = REFCLKSEL_0; + propcntrl = 0xeu << 4; + break; + default: + dev_err(combophy->dev, "Invalid rate 0x%x\n", clk_rate); + return -EINVAL; + } + + cr1_mask |= SYSCFG_COMBOPHY_CR1_REFCLKDIV2; + cr1_val |= REFCLDIV_0; + + cr1_mask |= SYSCFG_COMBOPHY_CR1_REFCLKSEL; + cr1_val |= refclksel; + + cr1_mask |= SYSCFG_COMBOPHY_CR1_MPLLMULT; + cr1_val |= pllmult; + + /* + * vddcombophy is interconnected with vddcore. Isolation bit should be unset + * before using the ComboPHY. + */ + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR2, + SYSCFG_COMBOPHY_CR2_ISO_DIS, SYSCFG_COMBOPHY_CR2_ISO_DIS); + + reset_control_assert(combophy->phy_reset); + + if (combophy->type == PHY_TYPE_PCIE) { + ret = stm32_impedance_tune(combophy); + if (ret) + goto out_iso; + + cr1_mask |= SYSCFG_COMBOPHY_CR1_REFUSEPAD; + cr1_val |= combophy->have_pad_clk ? SYSCFG_COMBOPHY_CR1_REFUSEPAD : 0; + } + + if (!of_property_read_u32(combophy->dev->of_node, "st,rx-equalizer", &val)) { + dev_dbg(combophy->dev, "Set RX equalizer %u\n", val); + if (val > SYSCFG_COMBOPHY_CR4_RX0_EQ) { + dev_err(combophy->dev, "Invalid value %u for rx0 equalizer\n", val); + ret = -EINVAL; + goto out_iso; + } + + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR4, + SYSCFG_COMBOPHY_CR4_RX0_EQ, val); + } + + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR1, cr1_mask, cr1_val); + + /* + * Force elasticity buffer to be tuned for the reference clock as + * the separated clock model is not supported + */ + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR5, + SYSCFG_COMBOPHY_CR5_COMMON_CLOCKS, SYSCFG_COMBOPHY_CR5_COMMON_CLOCKS); + + reset_control_deassert(combophy->phy_reset); + + ret = regmap_read_poll_timeout(combophy->regmap, SYSCFG_COMBOPHY_SR, val, + !(val & STM32MP25_PIPE0_PHYSTATUS), + 10, 1000); + if (ret) { + dev_err(combophy->dev, "timeout, cannot lock PLL\n"); + if (combophy->type == PHY_TYPE_PCIE && !combophy->have_pad_clk) + regmap_update_bits(combophy->regmap, SYSCFG_PCIEPRGCR, + STM32MP25_PCIEPRGCR_EN, 0); + + if (combophy->type != PHY_TYPE_PCIE) + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR1, + SYSCFG_COMBOPHY_CR1_REFSSPEN, 0); + + goto out; + } + + + if (combophy->type == PHY_TYPE_PCIE) { + if (!combophy->have_pad_clk) + regmap_update_bits(combophy->regmap, SYSCFG_PCIEPRGCR, + STM32MP25_PCIEPRGCR_EN, STM32MP25_PCIEPRGCR_EN); + + val = readl_relaxed(combophy->base + COMBOPHY_SUP_ANA_MPLL_LOOP_CTL); + val &= ~COMBOPHY_PROP_CNTRL; + val |= propcntrl; + writel_relaxed(val, combophy->base + COMBOPHY_SUP_ANA_MPLL_LOOP_CTL); + } + + return 0; + +out_iso: + reset_control_deassert(combophy->phy_reset); + +out: + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR2, + SYSCFG_COMBOPHY_CR2_ISO_DIS, 0); + + return ret; +} + +static struct phy *stm32_combophy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct stm32_combophy *combophy = dev_get_drvdata(dev); + unsigned int type; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + type = args->args[0]; + if (type != PHY_TYPE_USB3 && type != PHY_TYPE_PCIE) { + dev_err(dev, "unsupported device type: %d\n", type); + return ERR_PTR(-EINVAL); + } + + if (combophy->have_pad_clk && type != PHY_TYPE_PCIE) { + dev_err(dev, "Invalid use of clk_pad for USB3 mode\n"); + return ERR_PTR(-EINVAL); + } + + combophy->type = type; + + return combophy->phy; +} + +static int stm32_combophy_set_mode(struct stm32_combophy *combophy) +{ + int type = combophy->type; + u32 val; + + switch (type) { + case PHY_TYPE_PCIE: + dev_dbg(combophy->dev, "setting PCIe ComboPHY\n"); + val = COMBOPHY_MODESEL_PCIE; + break; + case PHY_TYPE_USB3: + dev_dbg(combophy->dev, "setting USB3 ComboPHY\n"); + val = COMBOPHY_MODESEL_USB; + break; + default: + dev_err(combophy->dev, "Invalid PHY mode %d\n", type); + return -EINVAL; + } + + return regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR2, + SYSCFG_COMBOPHY_CR2_MODESEL, val); +} + +static int stm32_combophy_suspend_noirq(struct device *dev) +{ + struct stm32_combophy *combophy = dev_get_drvdata(dev); + + /* + * Clocks should be turned off since it is not needed for + * wakeup capability. In case usb-remote wakeup is not enabled, + * combo-phy is already turned off by HCD driver using exit callback + */ + if (combophy->is_init) { + clk_bulk_disable_unprepare(combophy->num_clks, combophy->clks); + + /* since wakeup is enabled for ctrl */ + enable_irq_wake(combophy->irq_wakeup); + } + + return 0; +} + +static int stm32_combophy_resume_noirq(struct device *dev) +{ + struct stm32_combophy *combophy = dev_get_drvdata(dev); + int ret; + + /* + * If clocks was turned off by suspend call for wakeup then needs + * to be turned back ON in resume. In case usb-remote wakeup is not + * enabled, clocks already turned ON by HCD driver using init callback + */ + if (combophy->is_init) { + /* since wakeup was enabled for ctrl */ + disable_irq_wake(combophy->irq_wakeup); + + ret = clk_bulk_prepare_enable(combophy->num_clks, combophy->clks); + if (ret) { + dev_err(dev, "can't enable clocks (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static int stm32_combophy_exit(struct phy *phy) +{ + struct stm32_combophy *combophy = phy_get_drvdata(phy); + struct device *dev = combophy->dev; + + combophy->is_init = false; + + if (combophy->type == PHY_TYPE_PCIE && !combophy->have_pad_clk) + regmap_update_bits(combophy->regmap, SYSCFG_PCIEPRGCR, + STM32MP25_PCIEPRGCR_EN, 0); + + if (combophy->type != PHY_TYPE_PCIE) + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR1, + SYSCFG_COMBOPHY_CR1_REFSSPEN, 0); + + regmap_update_bits(combophy->regmap, SYSCFG_COMBOPHY_CR2, + SYSCFG_COMBOPHY_CR2_ISO_DIS, 0); + + clk_bulk_disable_unprepare(combophy->num_clks, combophy->clks); + + pm_runtime_put_noidle(dev); + + return 0; +} + +static int stm32_combophy_init(struct phy *phy) +{ + struct stm32_combophy *combophy = phy_get_drvdata(phy); + struct device *dev = combophy->dev; + int ret; + + pm_runtime_get_noresume(dev); + + ret = clk_bulk_prepare_enable(combophy->num_clks, combophy->clks); + if (ret) { + dev_err(dev, "can't enable clocks (%d)\n", ret); + pm_runtime_put_noidle(dev); + return ret; + } + + ret = stm32_combophy_set_mode(combophy); + if (ret) { + dev_err(dev, "combophy mode not set\n"); + clk_bulk_disable_unprepare(combophy->num_clks, combophy->clks); + pm_runtime_put_noidle(dev); + return ret; + } + + ret = stm32_combophy_pll_init(combophy); + if (ret) { + clk_bulk_disable_unprepare(combophy->num_clks, combophy->clks); + pm_runtime_put_noidle(dev); + return ret; + } + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + combophy->is_init = true; + + return ret; +} + +static const struct phy_ops stm32_combophy_phy_data = { + .init = stm32_combophy_init, + .exit = stm32_combophy_exit, + .owner = THIS_MODULE +}; + +static irqreturn_t stm32_combophy_irq_wakeup_handler(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static int stm32_combophy_get_clocks(struct stm32_combophy *combophy) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(combophy_clks); i++) + combophy->clks[i].id = combophy_clks[i]; + + combophy->num_clks = ARRAY_SIZE(combophy_clks) - 1; + + ret = devm_clk_bulk_get(combophy->dev, combophy->num_clks, combophy->clks); + if (ret) + return ret; + + ret = devm_clk_bulk_get_optional(combophy->dev, 1, combophy->clks + combophy->num_clks); + if (ret) + return ret; + + if (combophy->clks[combophy->num_clks].clk != NULL) { + combophy->have_pad_clk = true; + combophy->num_clks++; + } + + return 0; +} + +static int stm32_combophy_probe(struct platform_device *pdev) +{ + struct stm32_combophy *combophy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + int ret, irq; + + combophy = devm_kzalloc(dev, sizeof(*combophy), GFP_KERNEL); + if (!combophy) + return -ENOMEM; + + combophy->dev = dev; + + dev_set_drvdata(dev, combophy); + + combophy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(combophy->base)) + return PTR_ERR(combophy->base); + + ret = stm32_combophy_get_clocks(combophy); + if (ret) + return ret; + + combophy->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(combophy->phy_reset)) + return dev_err_probe(dev, PTR_ERR(combophy->phy_reset), + "Failed to get PHY reset\n"); + + combophy->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg"); + if (IS_ERR(combophy->regmap)) + return dev_err_probe(dev, PTR_ERR(combophy->regmap), + "No syscfg specified\n"); + + combophy->phy = devm_phy_create(dev, NULL, &stm32_combophy_phy_data); + if (IS_ERR(combophy->phy)) + return dev_err_probe(dev, PTR_ERR(combophy->phy), + "failed to create PCIe/USB3 ComboPHY\n"); + + if (device_property_read_bool(dev, "wakeup-source")) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get IRQ\n"); + combophy->irq_wakeup = irq; + + ret = devm_request_threaded_irq(dev, combophy->irq_wakeup, NULL, + stm32_combophy_irq_wakeup_handler, IRQF_ONESHOT, + NULL, NULL); + if (ret) + return dev_err_probe(dev, ret, "unable to request wake IRQ %d\n", + combophy->irq_wakeup); + } + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n"); + + phy_set_drvdata(combophy->phy, combophy); + + phy_provider = devm_of_phy_provider_register(dev, stm32_combophy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct dev_pm_ops stm32_combophy_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_combophy_suspend_noirq, + stm32_combophy_resume_noirq) +}; + +static const struct of_device_id stm32_combophy_of_match[] = { + { .compatible = "st,stm32mp25-combophy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, stm32_combophy_of_match); + +static struct platform_driver stm32_combophy_driver = { + .probe = stm32_combophy_probe, + .driver = { + .name = "stm32-combophy", + .of_match_table = stm32_combophy_of_match, + .pm = pm_sleep_ptr(&stm32_combophy_pm_ops) + } +}; + +module_platform_driver(stm32_combophy_driver); + +MODULE_AUTHOR("Christian Bruel "); +MODULE_DESCRIPTION("STM32MP25 Combophy USB3/PCIe controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 9dbe60dcf3190..b917cd413de73 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -812,7 +812,7 @@ MODULE_DEVICE_TABLE(of, stm32_usbphyc_of_match); static struct platform_driver stm32_usbphyc_driver = { .probe = stm32_usbphyc_probe, - .remove_new = stm32_usbphyc_remove, + .remove = stm32_usbphyc_remove, .driver = { .of_match_table = stm32_usbphyc_of_match, .name = "stm32-usbphyc", diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 342f5ccf611d8..79d4814d758d5 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -543,7 +543,7 @@ static int tegra_xusb_port_init(struct tegra_xusb_port *port, device_initialize(&port->dev); port->dev.type = &tegra_xusb_port_type; - port->dev.of_node = of_node_get(np); + device_set_node(&port->dev, of_fwnode_handle(of_node_get(np))); port->dev.parent = padctl->dev; err = dev_set_name(&port->dev, "%s-%u", name, index); @@ -1327,7 +1327,7 @@ static struct platform_driver tegra_xusb_padctl_driver = { .pm = &tegra_xusb_padctl_pm_ops, }, .probe = tegra_xusb_padctl_probe, - .remove_new = tegra_xusb_padctl_remove, + .remove = tegra_xusb_padctl_remove, }; module_platform_driver(tegra_xusb_padctl_driver); diff --git a/drivers/phy/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c index 3bf3aff4b1c74..431b223996e0a 100644 --- a/drivers/phy/ti/phy-am654-serdes.c +++ b/drivers/phy/ti/phy-am654-serdes.c @@ -837,7 +837,7 @@ static void serdes_am654_remove(struct platform_device *pdev) static struct platform_driver serdes_am654_driver = { .probe = serdes_am654_probe, - .remove_new = serdes_am654_remove, + .remove = serdes_am654_remove, .driver = { .name = "phy-am654", .of_match_table = serdes_am654_id_table, diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c index 68aa595b6ad8d..1d81a1e6ec6b6 100644 --- a/drivers/phy/ti/phy-da8xx-usb.c +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -277,11 +277,11 @@ MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); static struct platform_driver da8xx_usb_phy_driver = { .probe = da8xx_usb_phy_probe, - .remove_new = da8xx_usb_phy_remove, + .remove = da8xx_usb_phy_remove, .driver = { .name = "da8xx-usb-phy", .pm = &da8xx_usb_phy_pm_ops, - .of_match_table = da8xx_usb_phy_ids, + .of_match_table = da8xx_usb_phy_ids, }, }; diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index d5ae972a31fbc..e8f842d4e841b 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -259,7 +259,7 @@ static void dm816x_usb_phy_remove(struct platform_device *pdev) static struct platform_driver dm816x_usb_phy_driver = { .probe = dm816x_usb_phy_probe, - .remove_new = dm816x_usb_phy_remove, + .remove = dm816x_usb_phy_remove, .driver = { .name = "dm816x-usb-phy", .pm = &dm816x_usb_phy_pm_ops, diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 103b266fec771..e0ca59ae31531 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -230,7 +230,8 @@ static const struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = { .use_of_data = true, .regfields = phy_gmii_sel_fields_am654, - .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII), + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII) | + BIT(PHY_INTERFACE_MODE_USXGMII), .num_ports = 4, .num_qsgmii_main_ports = 1, }; diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index c6e846d385d28..ab2a4f2c0a5bf 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -1685,7 +1685,7 @@ static DEFINE_NOIRQ_DEV_PM_OPS(wiz_pm_ops, NULL, wiz_resume_noirq); static struct platform_driver wiz_driver = { .probe = wiz_probe, - .remove_new = wiz_remove, + .remove = wiz_remove, .driver = { .name = "wiz", .of_match_table = wiz_id_table, diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index 78e19b128962a..c1a0ef979142c 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -511,7 +511,7 @@ static void omap_usb2_remove(struct platform_device *pdev) static struct platform_driver omap_usb2_driver = { .probe = omap_usb2_probe, - .remove_new = omap_usb2_remove, + .remove = omap_usb2_remove, .driver = { .name = "omap-usb2", .of_match_table = omap_usb2_id_table, diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index 874c1a25ce36e..da2cbacb982c6 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -920,7 +920,7 @@ MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); static struct platform_driver ti_pipe3_driver = { .probe = ti_pipe3_probe, - .remove_new = ti_pipe3_remove, + .remove = ti_pipe3_remove, .driver = { .name = "ti-pipe3", .of_match_table = ti_pipe3_id_table, diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c index 6b265992d988f..6f12b38cd894f 100644 --- a/drivers/phy/ti/phy-twl4030-usb.c +++ b/drivers/phy/ti/phy-twl4030-usb.c @@ -834,7 +834,7 @@ MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); static struct platform_driver twl4030_usb_driver = { .probe = twl4030_usb_probe, - .remove_new = twl4030_usb_remove, + .remove = twl4030_usb_remove, .driver = { .name = "twl4030_usb", .pm = &twl4030_usb_pm_ops, diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index e6579002f1146..05a4a59f7c407 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -1071,7 +1071,7 @@ MODULE_DEVICE_TABLE(of, xpsgtr_of_match); static struct platform_driver xpsgtr_driver = { .probe = xpsgtr_probe, - .remove_new = xpsgtr_remove, + .remove = xpsgtr_remove, .driver = { .name = "xilinx-psgtr", .of_match_table = xpsgtr_of_match, diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 354536de564b6..95a8e2b9a614a 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -268,6 +268,17 @@ config PINCTRL_K210 Add support for the Canaan Kendryte K210 RISC-V SOC Field Programmable IO Array (FPIOA) controller. +config PINCTRL_K230 + bool "Pinctrl driver for the Canaan Kendryte K230 SoC" + depends on OF + depends on ARCH_CANAAN || COMPILE_TEST + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select REGMAP_MMIO + help + Add support for the Canaan Kendryte K230 RISC-V SOC pin controller. + config PINCTRL_KEEMBAY tristate "Pinctrl driver for Intel Keem Bay SoC" depends on ARCH_KEEMBAY || (ARM64 && COMPILE_TEST) @@ -551,6 +562,20 @@ config PINCTRL_TPS6594 This driver can also be built as a module called tps6594-pinctrl. +config PINCTRL_TH1520 + tristate "Pinctrl driver for the T-Head TH1520 SoC" + depends on ARCH_THEAD || COMPILE_TEST + depends on OF + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select PINMUX + help + This is the driver for the pin controller blocks on the + T-Head TH1520 SoC. + + This driver is needed for RISC-V development boards like + the BeagleV Ahead and the LicheePi 4A. + config PINCTRL_ZYNQ bool "Pinctrl driver for Xilinx Zynq" depends on ARCH_ZYNQ @@ -606,6 +631,7 @@ source "drivers/pinctrl/realtek/Kconfig" source "drivers/pinctrl/renesas/Kconfig" source "drivers/pinctrl/samsung/Kconfig" source "drivers/pinctrl/sophgo/Kconfig" +source "drivers/pinctrl/spacemit/Kconfig" source "drivers/pinctrl/spear/Kconfig" source "drivers/pinctrl/sprd/Kconfig" source "drivers/pinctrl/starfive/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 97823f52b972a..fba1c56624c0d 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_EYEQ5) += pinctrl-eyeq5.o obj-$(CONFIG_PINCTRL_GEMINI) += pinctrl-gemini.o obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o obj-$(CONFIG_PINCTRL_K210) += pinctrl-k210.o +obj-$(CONFIG_PINCTRL_K230) += pinctrl-k230.o obj-$(CONFIG_PINCTRL_KEEMBAY) += pinctrl-keembay.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o @@ -54,6 +55,7 @@ obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o obj-$(CONFIG_PINCTRL_TPS6594) += pinctrl-tps6594.o +obj-$(CONFIG_PINCTRL_TH1520) += pinctrl-th1520.o obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o @@ -76,6 +78,7 @@ obj-$(CONFIG_ARCH_REALTEK) += realtek/ obj-$(CONFIG_PINCTRL_RENESAS) += renesas/ obj-$(CONFIG_PINCTRL_SAMSUNG) += samsung/ obj-y += sophgo/ +obj-y += spacemit/ obj-$(CONFIG_PINCTRL_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_SOC_STARFIVE) += starfive/ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c index 6ecc656abc44f..5a7cd0a886876 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c @@ -2607,6 +2607,10 @@ static struct aspeed_pin_config aspeed_g6_configs[] = { { PIN_CONFIG_DRIVE_STRENGTH, { AB8, AB8 }, SCU454, GENMASK(27, 26)}, /* LAD0 */ { PIN_CONFIG_DRIVE_STRENGTH, { AB7, AB7 }, SCU454, GENMASK(25, 24)}, + /* GPIOF */ + { PIN_CONFIG_DRIVE_STRENGTH, { D22, A23 }, SCU458, GENMASK(9, 8)}, + /* GPIOG */ + { PIN_CONFIG_DRIVE_STRENGTH, { E21, B21 }, SCU458, GENMASK(11, 10)}, /* MAC3 */ { PIN_CONFIG_POWER_SOURCE, { H24, E26 }, SCU458, BIT_MASK(4)}, diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index fd5ce52d05b1d..c9a3d3aa8c108 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -309,7 +309,7 @@ static void iproc_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio *chip = gpiochip_get_data(gc); - seq_printf(p, dev_name(chip->dev)); + seq_puts(p, dev_name(chip->dev)); } static const struct irq_chip iproc_gpio_irq_chip = { diff --git a/drivers/pinctrl/cirrus/pinctrl-madera-core.c b/drivers/pinctrl/cirrus/pinctrl-madera-core.c index 2932d7aba7255..73ec5b9beb49f 100644 --- a/drivers/pinctrl/cirrus/pinctrl-madera-core.c +++ b/drivers/pinctrl/cirrus/pinctrl-madera-core.c @@ -1091,7 +1091,7 @@ static void madera_pin_remove(struct platform_device *pdev) static struct platform_driver madera_pin_driver = { .probe = madera_pin_probe, - .remove_new = madera_pin_remove, + .remove = madera_pin_remove, .driver = { .name = "madera-pinctrl", }, diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 4061890a17483..b3eec63c00ba0 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -220,6 +220,9 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, /* Set owner */ pindesc->pctldev = pctldev; +#ifdef CONFIG_PINMUX + mutex_init(&pindesc->mux_lock); +#endif /* Copy basic pin info */ if (pin->name) { diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 4e07707d2435b..d6c24978e7081 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -177,6 +177,7 @@ struct pin_desc { const char *mux_owner; const struct pinctrl_setting_mux *mux_setting; const char *gpio_owner; + struct mutex mux_lock; #endif }; diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig index 3b59d71890045..4c420b21b804d 100644 --- a/drivers/pinctrl/freescale/Kconfig +++ b/drivers/pinctrl/freescale/Kconfig @@ -9,7 +9,7 @@ config PINCTRL_IMX config PINCTRL_IMX_SCMI tristate "i.MX95 pinctrl driver using SCMI protocol interface" - depends on ARM_SCMI_PROTOCOL && OF || COMPILE_TEST + depends on ARM_SCMI_PROTOCOL && OF select PINMUX select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS @@ -20,7 +20,7 @@ config PINCTRL_IMX_SCMI config PINCTRL_IMX_SCU tristate - depends on IMX_SCU + depends on IMX_SCU || COMPILE_TEST select PINCTRL_IMX config PINCTRL_IMX1_CORE @@ -30,14 +30,18 @@ config PINCTRL_IMX1_CORE config PINCTRL_IMX1 bool "IMX1 pinctrl driver" - depends on SOC_IMX1 + depends on OF + depends on SOC_IMX1 || COMPILE_TEST + default SOC_IMX1 select PINCTRL_IMX1_CORE help Say Y here to enable the imx1 pinctrl driver config PINCTRL_IMX27 bool "IMX27 pinctrl driver" - depends on SOC_IMX27 + depends on OF + depends on SOC_IMX27 || COMPILE_TEST + default SOC_IMX27 select PINCTRL_IMX1_CORE help Say Y here to enable the imx27 pinctrl driver @@ -46,84 +50,107 @@ config PINCTRL_IMX27 config PINCTRL_IMX25 bool "IMX25 pinctrl driver" depends on OF - depends on SOC_IMX25 + depends on SOC_IMX25 || COMPILE_TEST + default SOC_IMX25 select PINCTRL_IMX help Say Y here to enable the imx25 pinctrl driver config PINCTRL_IMX35 bool "IMX35 pinctrl driver" - depends on SOC_IMX35 + depends on OF + depends on SOC_IMX35 || COMPILE_TEST + default SOC_IMX35 select PINCTRL_IMX help Say Y here to enable the imx35 pinctrl driver config PINCTRL_IMX50 bool "IMX50 pinctrl driver" - depends on SOC_IMX50 + depends on OF + depends on SOC_IMX50 || COMPILE_TEST + default SOC_IMX50 select PINCTRL_IMX help Say Y here to enable the imx50 pinctrl driver config PINCTRL_IMX51 bool "IMX51 pinctrl driver" - depends on SOC_IMX51 + depends on OF + depends on SOC_IMX51 || COMPILE_TEST + default SOC_IMX51 select PINCTRL_IMX help Say Y here to enable the imx51 pinctrl driver config PINCTRL_IMX53 bool "IMX53 pinctrl driver" - depends on SOC_IMX53 + depends on OF + depends on SOC_IMX53 || COMPILE_TEST + default SOC_IMX53 select PINCTRL_IMX help Say Y here to enable the imx53 pinctrl driver config PINCTRL_IMX6Q bool "IMX6Q/DL pinctrl driver" - depends on SOC_IMX6Q + depends on OF + depends on SOC_IMX6Q || COMPILE_TEST + default SOC_IMX6Q select PINCTRL_IMX help Say Y here to enable the imx6q/dl pinctrl driver config PINCTRL_IMX6SL bool "IMX6SL pinctrl driver" - depends on SOC_IMX6SL + depends on OF + depends on SOC_IMX6SL || COMPILE_TEST + default SOC_IMX6SL select PINCTRL_IMX help Say Y here to enable the imx6sl pinctrl driver config PINCTRL_IMX6SLL bool "IMX6SLL pinctrl driver" - depends on SOC_IMX6SLL + depends on OF + depends on SOC_IMX6SLL || COMPILE_TEST + default SOC_IMX6SLL select PINCTRL_IMX help Say Y here to enable the imx6sll pinctrl driver config PINCTRL_IMX6SX bool "IMX6SX pinctrl driver" - depends on SOC_IMX6SX + depends on OF + depends on SOC_IMX6SX || COMPILE_TEST + default SOC_IMX6SX select PINCTRL_IMX help Say Y here to enable the imx6sx pinctrl driver config PINCTRL_IMX6UL bool "IMX6UL pinctrl driver" - depends on SOC_IMX6UL + depends on OF + depends on SOC_IMX6UL || COMPILE_TEST + default SOC_IMX6UL select PINCTRL_IMX help Say Y here to enable the imx6ul pinctrl driver config PINCTRL_IMX7D bool "IMX7D pinctrl driver" - depends on SOC_IMX7D + depends on OF + depends on SOC_IMX7D || COMPILE_TEST + default SOC_IMX7D select PINCTRL_IMX help Say Y here to enable the imx7d pinctrl driver config PINCTRL_IMX7ULP bool "IMX7ULP pinctrl driver" - depends on SOC_IMX7ULP + depends on OF + depends on SOC_IMX7ULP || COMPILE_TEST + default SOC_IMX7ULP select PINCTRL_IMX help Say Y here to enable the imx7ulp pinctrl driver @@ -131,7 +158,7 @@ config PINCTRL_IMX7ULP config PINCTRL_IMX8MM tristate "IMX8MM pinctrl driver" depends on OF - depends on SOC_IMX8M + depends on SOC_IMX8M || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imx8mm pinctrl driver @@ -139,7 +166,7 @@ config PINCTRL_IMX8MM config PINCTRL_IMX8MN tristate "IMX8MN pinctrl driver" depends on OF - depends on SOC_IMX8M + depends on SOC_IMX8M || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imx8mn pinctrl driver @@ -147,7 +174,7 @@ config PINCTRL_IMX8MN config PINCTRL_IMX8MP tristate "IMX8MP pinctrl driver" depends on OF - depends on SOC_IMX8M + depends on SOC_IMX8M || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imx8mp pinctrl driver @@ -155,42 +182,48 @@ config PINCTRL_IMX8MP config PINCTRL_IMX8MQ tristate "IMX8MQ pinctrl driver" depends on OF - depends on SOC_IMX8M + depends on SOC_IMX8M || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imx8mq pinctrl driver config PINCTRL_IMX8QM tristate "IMX8QM pinctrl driver" - depends on IMX_SCU && ARCH_MXC && ARM64 + depends on OF + depends on (IMX_SCU && ARCH_MXC && ARM64) || COMPILE_TEST select PINCTRL_IMX_SCU help Say Y here to enable the imx8qm pinctrl driver config PINCTRL_IMX8QXP tristate "IMX8QXP pinctrl driver" - depends on IMX_SCU && ARCH_MXC && ARM64 + depends on OF + depends on (IMX_SCU && ARCH_MXC && ARM64) || COMPILE_TEST select PINCTRL_IMX_SCU help Say Y here to enable the imx8qxp pinctrl driver config PINCTRL_IMX8DXL tristate "IMX8DXL pinctrl driver" - depends on IMX_SCU && ARCH_MXC && ARM64 + depends on OF + depends on (IMX_SCU && ARCH_MXC && ARM64) || COMPILE_TEST select PINCTRL_IMX_SCU help Say Y here to enable the imx8dxl pinctrl driver config PINCTRL_IMX8ULP tristate "IMX8ULP pinctrl driver" - depends on ARCH_MXC + depends on OF + depends on ARCH_MXC || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imx8ulp pinctrl driver config PINCTRL_IMXRT1050 bool "IMXRT1050 pinctrl driver" - depends on ARCH_MXC + depends on OF + depends on SOC_IMXRT || COMPILE_TEST + default SOC_IMXRT select PINCTRL_IMX help Say Y here to enable the imxrt1050 pinctrl driver @@ -204,14 +237,17 @@ config PINCTRL_IMX91 config PINCTRL_IMX93 tristate "IMX93 pinctrl driver" - depends on ARCH_MXC + depends on OF + depends on ARCH_MXC || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imx93 pinctrl driver config PINCTRL_VF610 bool "Freescale Vybrid VF610 pinctrl driver" - depends on SOC_VF610 + depends on OF + depends on SOC_VF610 || COMPILE_TEST + default SOC_VF610 select PINCTRL_IMX help Say Y here to enable the Freescale Vybrid VF610 pinctrl driver @@ -231,7 +267,8 @@ config PINCTRL_IMX28 config PINCTRL_IMXRT1170 bool "IMXRT1170 pinctrl driver" - depends on ARCH_MXC + depends on OF + depends on SOC_IMXRT || COMPILE_TEST select PINCTRL_IMX help Say Y here to enable the imxrt1170 pinctrl driver diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index d05c2c478e795..842a1e6cbfc41 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -633,11 +633,11 @@ static int imx_pinctrl_parse_functions(struct device_node *np, static bool imx_pinctrl_dt_is_flat_functions(struct device_node *np) { for_each_child_of_node_scoped(np, function_np) { - if (of_property_read_bool(function_np, "fsl,pins")) + if (of_property_present(function_np, "fsl,pins")) return true; for_each_child_of_node_scoped(function_np, pinctrl_np) { - if (of_property_read_bool(pinctrl_np, "fsl,pins")) + if (of_property_present(pinctrl_np, "fsl,pins")) return false; } } @@ -746,7 +746,7 @@ int imx_pinctrl_probe(struct platform_device *pdev, if (IS_ERR(ipctl->base)) return PTR_ERR(ipctl->base); - if (of_property_read_bool(dev_np, "fsl,input-sel")) { + if (of_property_present(dev_np, "fsl,input-sel")) { np = of_parse_phandle(dev_np, "fsl,input-sel", 0); if (!np) { dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n"); diff --git a/drivers/pinctrl/freescale/pinctrl-imx1.c b/drivers/pinctrl/freescale/pinctrl-imx1.c index 1e2b0fe9ffd6f..bd39cadf1f34d 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx1.c +++ b/drivers/pinctrl/freescale/pinctrl-imx1.c @@ -12,122 +12,122 @@ #include "pinctrl-imx1.h" #define PAD_ID(port, pin) ((port) * 32 + (pin)) -#define PA 0 -#define PB 1 -#define PC 2 -#define PD 3 +#define IMX1_PA 0 +#define IMX1_PB 1 +#define IMX1_PC 2 +#define IMX1_PD 3 enum imx1_pads { - MX1_PAD_A24 = PAD_ID(PA, 0), - MX1_PAD_TIN = PAD_ID(PA, 1), - MX1_PAD_PWMO = PAD_ID(PA, 2), - MX1_PAD_CSI_MCLK = PAD_ID(PA, 3), - MX1_PAD_CSI_D0 = PAD_ID(PA, 4), - MX1_PAD_CSI_D1 = PAD_ID(PA, 5), - MX1_PAD_CSI_D2 = PAD_ID(PA, 6), - MX1_PAD_CSI_D3 = PAD_ID(PA, 7), - MX1_PAD_CSI_D4 = PAD_ID(PA, 8), - MX1_PAD_CSI_D5 = PAD_ID(PA, 9), - MX1_PAD_CSI_D6 = PAD_ID(PA, 10), - MX1_PAD_CSI_D7 = PAD_ID(PA, 11), - MX1_PAD_CSI_VSYNC = PAD_ID(PA, 12), - MX1_PAD_CSI_HSYNC = PAD_ID(PA, 13), - MX1_PAD_CSI_PIXCLK = PAD_ID(PA, 14), - MX1_PAD_I2C_SDA = PAD_ID(PA, 15), - MX1_PAD_I2C_SCL = PAD_ID(PA, 16), - MX1_PAD_DTACK = PAD_ID(PA, 17), - MX1_PAD_BCLK = PAD_ID(PA, 18), - MX1_PAD_LBA = PAD_ID(PA, 19), - MX1_PAD_ECB = PAD_ID(PA, 20), - MX1_PAD_A0 = PAD_ID(PA, 21), - MX1_PAD_CS4 = PAD_ID(PA, 22), - MX1_PAD_CS5 = PAD_ID(PA, 23), - MX1_PAD_A16 = PAD_ID(PA, 24), - MX1_PAD_A17 = PAD_ID(PA, 25), - MX1_PAD_A18 = PAD_ID(PA, 26), - MX1_PAD_A19 = PAD_ID(PA, 27), - MX1_PAD_A20 = PAD_ID(PA, 28), - MX1_PAD_A21 = PAD_ID(PA, 29), - MX1_PAD_A22 = PAD_ID(PA, 30), - MX1_PAD_A23 = PAD_ID(PA, 31), - MX1_PAD_SD_DAT0 = PAD_ID(PB, 8), - MX1_PAD_SD_DAT1 = PAD_ID(PB, 9), - MX1_PAD_SD_DAT2 = PAD_ID(PB, 10), - MX1_PAD_SD_DAT3 = PAD_ID(PB, 11), - MX1_PAD_SD_SCLK = PAD_ID(PB, 12), - MX1_PAD_SD_CMD = PAD_ID(PB, 13), - MX1_PAD_SIM_SVEN = PAD_ID(PB, 14), - MX1_PAD_SIM_PD = PAD_ID(PB, 15), - MX1_PAD_SIM_TX = PAD_ID(PB, 16), - MX1_PAD_SIM_RX = PAD_ID(PB, 17), - MX1_PAD_SIM_RST = PAD_ID(PB, 18), - MX1_PAD_SIM_CLK = PAD_ID(PB, 19), - MX1_PAD_USBD_AFE = PAD_ID(PB, 20), - MX1_PAD_USBD_OE = PAD_ID(PB, 21), - MX1_PAD_USBD_RCV = PAD_ID(PB, 22), - MX1_PAD_USBD_SUSPND = PAD_ID(PB, 23), - MX1_PAD_USBD_VP = PAD_ID(PB, 24), - MX1_PAD_USBD_VM = PAD_ID(PB, 25), - MX1_PAD_USBD_VPO = PAD_ID(PB, 26), - MX1_PAD_USBD_VMO = PAD_ID(PB, 27), - MX1_PAD_UART2_CTS = PAD_ID(PB, 28), - MX1_PAD_UART2_RTS = PAD_ID(PB, 29), - MX1_PAD_UART2_TXD = PAD_ID(PB, 30), - MX1_PAD_UART2_RXD = PAD_ID(PB, 31), - MX1_PAD_SSI_RXFS = PAD_ID(PC, 3), - MX1_PAD_SSI_RXCLK = PAD_ID(PC, 4), - MX1_PAD_SSI_RXDAT = PAD_ID(PC, 5), - MX1_PAD_SSI_TXDAT = PAD_ID(PC, 6), - MX1_PAD_SSI_TXFS = PAD_ID(PC, 7), - MX1_PAD_SSI_TXCLK = PAD_ID(PC, 8), - MX1_PAD_UART1_CTS = PAD_ID(PC, 9), - MX1_PAD_UART1_RTS = PAD_ID(PC, 10), - MX1_PAD_UART1_TXD = PAD_ID(PC, 11), - MX1_PAD_UART1_RXD = PAD_ID(PC, 12), - MX1_PAD_SPI1_RDY = PAD_ID(PC, 13), - MX1_PAD_SPI1_SCLK = PAD_ID(PC, 14), - MX1_PAD_SPI1_SS = PAD_ID(PC, 15), - MX1_PAD_SPI1_MISO = PAD_ID(PC, 16), - MX1_PAD_SPI1_MOSI = PAD_ID(PC, 17), - MX1_PAD_BT13 = PAD_ID(PC, 19), - MX1_PAD_BT12 = PAD_ID(PC, 20), - MX1_PAD_BT11 = PAD_ID(PC, 21), - MX1_PAD_BT10 = PAD_ID(PC, 22), - MX1_PAD_BT9 = PAD_ID(PC, 23), - MX1_PAD_BT8 = PAD_ID(PC, 24), - MX1_PAD_BT7 = PAD_ID(PC, 25), - MX1_PAD_BT6 = PAD_ID(PC, 26), - MX1_PAD_BT5 = PAD_ID(PC, 27), - MX1_PAD_BT4 = PAD_ID(PC, 28), - MX1_PAD_BT3 = PAD_ID(PC, 29), - MX1_PAD_BT2 = PAD_ID(PC, 30), - MX1_PAD_BT1 = PAD_ID(PC, 31), - MX1_PAD_LSCLK = PAD_ID(PD, 6), - MX1_PAD_REV = PAD_ID(PD, 7), - MX1_PAD_CLS = PAD_ID(PD, 8), - MX1_PAD_PS = PAD_ID(PD, 9), - MX1_PAD_SPL_SPR = PAD_ID(PD, 10), - MX1_PAD_CONTRAST = PAD_ID(PD, 11), - MX1_PAD_ACD_OE = PAD_ID(PD, 12), - MX1_PAD_LP_HSYNC = PAD_ID(PD, 13), - MX1_PAD_FLM_VSYNC = PAD_ID(PD, 14), - MX1_PAD_LD0 = PAD_ID(PD, 15), - MX1_PAD_LD1 = PAD_ID(PD, 16), - MX1_PAD_LD2 = PAD_ID(PD, 17), - MX1_PAD_LD3 = PAD_ID(PD, 18), - MX1_PAD_LD4 = PAD_ID(PD, 19), - MX1_PAD_LD5 = PAD_ID(PD, 20), - MX1_PAD_LD6 = PAD_ID(PD, 21), - MX1_PAD_LD7 = PAD_ID(PD, 22), - MX1_PAD_LD8 = PAD_ID(PD, 23), - MX1_PAD_LD9 = PAD_ID(PD, 24), - MX1_PAD_LD10 = PAD_ID(PD, 25), - MX1_PAD_LD11 = PAD_ID(PD, 26), - MX1_PAD_LD12 = PAD_ID(PD, 27), - MX1_PAD_LD13 = PAD_ID(PD, 28), - MX1_PAD_LD14 = PAD_ID(PD, 29), - MX1_PAD_LD15 = PAD_ID(PD, 30), - MX1_PAD_TMR2OUT = PAD_ID(PD, 31), + MX1_PAD_A24 = PAD_ID(IMX1_PA, 0), + MX1_PAD_TIN = PAD_ID(IMX1_PA, 1), + MX1_PAD_PWMO = PAD_ID(IMX1_PA, 2), + MX1_PAD_CSI_MCLK = PAD_ID(IMX1_PA, 3), + MX1_PAD_CSI_D0 = PAD_ID(IMX1_PA, 4), + MX1_PAD_CSI_D1 = PAD_ID(IMX1_PA, 5), + MX1_PAD_CSI_D2 = PAD_ID(IMX1_PA, 6), + MX1_PAD_CSI_D3 = PAD_ID(IMX1_PA, 7), + MX1_PAD_CSI_D4 = PAD_ID(IMX1_PA, 8), + MX1_PAD_CSI_D5 = PAD_ID(IMX1_PA, 9), + MX1_PAD_CSI_D6 = PAD_ID(IMX1_PA, 10), + MX1_PAD_CSI_D7 = PAD_ID(IMX1_PA, 11), + MX1_PAD_CSI_VSYNC = PAD_ID(IMX1_PA, 12), + MX1_PAD_CSI_HSYNC = PAD_ID(IMX1_PA, 13), + MX1_PAD_CSI_PIXCLK = PAD_ID(IMX1_PA, 14), + MX1_PAD_I2C_SDA = PAD_ID(IMX1_PA, 15), + MX1_PAD_I2C_SCL = PAD_ID(IMX1_PA, 16), + MX1_PAD_DTACK = PAD_ID(IMX1_PA, 17), + MX1_PAD_BCLK = PAD_ID(IMX1_PA, 18), + MX1_PAD_LBA = PAD_ID(IMX1_PA, 19), + MX1_PAD_ECB = PAD_ID(IMX1_PA, 20), + MX1_PAD_A0 = PAD_ID(IMX1_PA, 21), + MX1_PAD_CS4 = PAD_ID(IMX1_PA, 22), + MX1_PAD_CS5 = PAD_ID(IMX1_PA, 23), + MX1_PAD_A16 = PAD_ID(IMX1_PA, 24), + MX1_PAD_A17 = PAD_ID(IMX1_PA, 25), + MX1_PAD_A18 = PAD_ID(IMX1_PA, 26), + MX1_PAD_A19 = PAD_ID(IMX1_PA, 27), + MX1_PAD_A20 = PAD_ID(IMX1_PA, 28), + MX1_PAD_A21 = PAD_ID(IMX1_PA, 29), + MX1_PAD_A22 = PAD_ID(IMX1_PA, 30), + MX1_PAD_A23 = PAD_ID(IMX1_PA, 31), + MX1_PAD_SD_DAT0 = PAD_ID(IMX1_PB, 8), + MX1_PAD_SD_DAT1 = PAD_ID(IMX1_PB, 9), + MX1_PAD_SD_DAT2 = PAD_ID(IMX1_PB, 10), + MX1_PAD_SD_DAT3 = PAD_ID(IMX1_PB, 11), + MX1_PAD_SD_SCLK = PAD_ID(IMX1_PB, 12), + MX1_PAD_SD_CMD = PAD_ID(IMX1_PB, 13), + MX1_PAD_SIM_SVEN = PAD_ID(IMX1_PB, 14), + MX1_PAD_SIM_PD = PAD_ID(IMX1_PB, 15), + MX1_PAD_SIM_TX = PAD_ID(IMX1_PB, 16), + MX1_PAD_SIM_RX = PAD_ID(IMX1_PB, 17), + MX1_PAD_SIM_RST = PAD_ID(IMX1_PB, 18), + MX1_PAD_SIM_CLK = PAD_ID(IMX1_PB, 19), + MX1_PAD_USBD_AFE = PAD_ID(IMX1_PB, 20), + MX1_PAD_USBD_OE = PAD_ID(IMX1_PB, 21), + MX1_PAD_USBD_RCV = PAD_ID(IMX1_PB, 22), + MX1_PAD_USBD_SUSPND = PAD_ID(IMX1_PB, 23), + MX1_PAD_USBD_VP = PAD_ID(IMX1_PB, 24), + MX1_PAD_USBD_VM = PAD_ID(IMX1_PB, 25), + MX1_PAD_USBD_VPO = PAD_ID(IMX1_PB, 26), + MX1_PAD_USBD_VMO = PAD_ID(IMX1_PB, 27), + MX1_PAD_UART2_CTS = PAD_ID(IMX1_PB, 28), + MX1_PAD_UART2_RTS = PAD_ID(IMX1_PB, 29), + MX1_PAD_UART2_TXD = PAD_ID(IMX1_PB, 30), + MX1_PAD_UART2_RXD = PAD_ID(IMX1_PB, 31), + MX1_PAD_SSI_RXFS = PAD_ID(IMX1_PC, 3), + MX1_PAD_SSI_RXCLK = PAD_ID(IMX1_PC, 4), + MX1_PAD_SSI_RXDAT = PAD_ID(IMX1_PC, 5), + MX1_PAD_SSI_TXDAT = PAD_ID(IMX1_PC, 6), + MX1_PAD_SSI_TXFS = PAD_ID(IMX1_PC, 7), + MX1_PAD_SSI_TXCLK = PAD_ID(IMX1_PC, 8), + MX1_PAD_UART1_CTS = PAD_ID(IMX1_PC, 9), + MX1_PAD_UART1_RTS = PAD_ID(IMX1_PC, 10), + MX1_PAD_UART1_TXD = PAD_ID(IMX1_PC, 11), + MX1_PAD_UART1_RXD = PAD_ID(IMX1_PC, 12), + MX1_PAD_SPI1_RDY = PAD_ID(IMX1_PC, 13), + MX1_PAD_SPI1_SCLK = PAD_ID(IMX1_PC, 14), + MX1_PAD_SPI1_SS = PAD_ID(IMX1_PC, 15), + MX1_PAD_SPI1_MISO = PAD_ID(IMX1_PC, 16), + MX1_PAD_SPI1_MOSI = PAD_ID(IMX1_PC, 17), + MX1_PAD_BT13 = PAD_ID(IMX1_PC, 19), + MX1_PAD_BT12 = PAD_ID(IMX1_PC, 20), + MX1_PAD_BT11 = PAD_ID(IMX1_PC, 21), + MX1_PAD_BT10 = PAD_ID(IMX1_PC, 22), + MX1_PAD_BT9 = PAD_ID(IMX1_PC, 23), + MX1_PAD_BT8 = PAD_ID(IMX1_PC, 24), + MX1_PAD_BT7 = PAD_ID(IMX1_PC, 25), + MX1_PAD_BT6 = PAD_ID(IMX1_PC, 26), + MX1_PAD_BT5 = PAD_ID(IMX1_PC, 27), + MX1_PAD_BT4 = PAD_ID(IMX1_PC, 28), + MX1_PAD_BT3 = PAD_ID(IMX1_PC, 29), + MX1_PAD_BT2 = PAD_ID(IMX1_PC, 30), + MX1_PAD_BT1 = PAD_ID(IMX1_PC, 31), + MX1_PAD_LSCLK = PAD_ID(IMX1_PD, 6), + MX1_PAD_REV = PAD_ID(IMX1_PD, 7), + MX1_PAD_CLS = PAD_ID(IMX1_PD, 8), + MX1_PAD_PS = PAD_ID(IMX1_PD, 9), + MX1_PAD_SPL_SPR = PAD_ID(IMX1_PD, 10), + MX1_PAD_CONTRAST = PAD_ID(IMX1_PD, 11), + MX1_PAD_ACD_OE = PAD_ID(IMX1_PD, 12), + MX1_PAD_LP_HSYNC = PAD_ID(IMX1_PD, 13), + MX1_PAD_FLM_VSYNC = PAD_ID(IMX1_PD, 14), + MX1_PAD_LD0 = PAD_ID(IMX1_PD, 15), + MX1_PAD_LD1 = PAD_ID(IMX1_PD, 16), + MX1_PAD_LD2 = PAD_ID(IMX1_PD, 17), + MX1_PAD_LD3 = PAD_ID(IMX1_PD, 18), + MX1_PAD_LD4 = PAD_ID(IMX1_PD, 19), + MX1_PAD_LD5 = PAD_ID(IMX1_PD, 20), + MX1_PAD_LD6 = PAD_ID(IMX1_PD, 21), + MX1_PAD_LD7 = PAD_ID(IMX1_PD, 22), + MX1_PAD_LD8 = PAD_ID(IMX1_PD, 23), + MX1_PAD_LD9 = PAD_ID(IMX1_PD, 24), + MX1_PAD_LD10 = PAD_ID(IMX1_PD, 25), + MX1_PAD_LD11 = PAD_ID(IMX1_PD, 26), + MX1_PAD_LD12 = PAD_ID(IMX1_PD, 27), + MX1_PAD_LD13 = PAD_ID(IMX1_PD, 28), + MX1_PAD_LD14 = PAD_ID(IMX1_PD, 29), + MX1_PAD_LD15 = PAD_ID(IMX1_PD, 30), + MX1_PAD_TMR2OUT = PAD_ID(IMX1_PD, 31), }; /* Pad names for the pinmux subsystem */ diff --git a/drivers/pinctrl/freescale/pinctrl-imx27.c b/drivers/pinctrl/freescale/pinctrl-imx27.c index 1738df4612351..afeb399572036 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx27.c +++ b/drivers/pinctrl/freescale/pinctrl-imx27.c @@ -16,188 +16,188 @@ #include "pinctrl-imx1.h" #define PAD_ID(port, pin) (port*32 + pin) -#define PA 0 -#define PB 1 -#define PC 2 -#define PD 3 -#define PE 4 -#define PF 5 +#define MX27_PA 0 +#define MX27_PB 1 +#define MX27_PC 2 +#define MX27_PD 3 +#define MX27_PE 4 +#define MX27_PF 5 enum imx27_pads { - MX27_PAD_USBH2_CLK = PAD_ID(PA, 0), - MX27_PAD_USBH2_DIR = PAD_ID(PA, 1), - MX27_PAD_USBH2_DATA7 = PAD_ID(PA, 2), - MX27_PAD_USBH2_NXT = PAD_ID(PA, 3), - MX27_PAD_USBH2_STP = PAD_ID(PA, 4), - MX27_PAD_LSCLK = PAD_ID(PA, 5), - MX27_PAD_LD0 = PAD_ID(PA, 6), - MX27_PAD_LD1 = PAD_ID(PA, 7), - MX27_PAD_LD2 = PAD_ID(PA, 8), - MX27_PAD_LD3 = PAD_ID(PA, 9), - MX27_PAD_LD4 = PAD_ID(PA, 10), - MX27_PAD_LD5 = PAD_ID(PA, 11), - MX27_PAD_LD6 = PAD_ID(PA, 12), - MX27_PAD_LD7 = PAD_ID(PA, 13), - MX27_PAD_LD8 = PAD_ID(PA, 14), - MX27_PAD_LD9 = PAD_ID(PA, 15), - MX27_PAD_LD10 = PAD_ID(PA, 16), - MX27_PAD_LD11 = PAD_ID(PA, 17), - MX27_PAD_LD12 = PAD_ID(PA, 18), - MX27_PAD_LD13 = PAD_ID(PA, 19), - MX27_PAD_LD14 = PAD_ID(PA, 20), - MX27_PAD_LD15 = PAD_ID(PA, 21), - MX27_PAD_LD16 = PAD_ID(PA, 22), - MX27_PAD_LD17 = PAD_ID(PA, 23), - MX27_PAD_REV = PAD_ID(PA, 24), - MX27_PAD_CLS = PAD_ID(PA, 25), - MX27_PAD_PS = PAD_ID(PA, 26), - MX27_PAD_SPL_SPR = PAD_ID(PA, 27), - MX27_PAD_HSYNC = PAD_ID(PA, 28), - MX27_PAD_VSYNC = PAD_ID(PA, 29), - MX27_PAD_CONTRAST = PAD_ID(PA, 30), - MX27_PAD_OE_ACD = PAD_ID(PA, 31), + MX27_PAD_USBH2_CLK = PAD_ID(MX27_PA, 0), + MX27_PAD_USBH2_DIR = PAD_ID(MX27_PA, 1), + MX27_PAD_USBH2_DATA7 = PAD_ID(MX27_PA, 2), + MX27_PAD_USBH2_NXT = PAD_ID(MX27_PA, 3), + MX27_PAD_USBH2_STP = PAD_ID(MX27_PA, 4), + MX27_PAD_LSCLK = PAD_ID(MX27_PA, 5), + MX27_PAD_LD0 = PAD_ID(MX27_PA, 6), + MX27_PAD_LD1 = PAD_ID(MX27_PA, 7), + MX27_PAD_LD2 = PAD_ID(MX27_PA, 8), + MX27_PAD_LD3 = PAD_ID(MX27_PA, 9), + MX27_PAD_LD4 = PAD_ID(MX27_PA, 10), + MX27_PAD_LD5 = PAD_ID(MX27_PA, 11), + MX27_PAD_LD6 = PAD_ID(MX27_PA, 12), + MX27_PAD_LD7 = PAD_ID(MX27_PA, 13), + MX27_PAD_LD8 = PAD_ID(MX27_PA, 14), + MX27_PAD_LD9 = PAD_ID(MX27_PA, 15), + MX27_PAD_LD10 = PAD_ID(MX27_PA, 16), + MX27_PAD_LD11 = PAD_ID(MX27_PA, 17), + MX27_PAD_LD12 = PAD_ID(MX27_PA, 18), + MX27_PAD_LD13 = PAD_ID(MX27_PA, 19), + MX27_PAD_LD14 = PAD_ID(MX27_PA, 20), + MX27_PAD_LD15 = PAD_ID(MX27_PA, 21), + MX27_PAD_LD16 = PAD_ID(MX27_PA, 22), + MX27_PAD_LD17 = PAD_ID(MX27_PA, 23), + MX27_PAD_REV = PAD_ID(MX27_PA, 24), + MX27_PAD_CLS = PAD_ID(MX27_PA, 25), + MX27_PAD_PS = PAD_ID(MX27_PA, 26), + MX27_PAD_SPL_SPR = PAD_ID(MX27_PA, 27), + MX27_PAD_HSYNC = PAD_ID(MX27_PA, 28), + MX27_PAD_VSYNC = PAD_ID(MX27_PA, 29), + MX27_PAD_CONTRAST = PAD_ID(MX27_PA, 30), + MX27_PAD_OE_ACD = PAD_ID(MX27_PA, 31), - MX27_PAD_SD2_D0 = PAD_ID(PB, 4), - MX27_PAD_SD2_D1 = PAD_ID(PB, 5), - MX27_PAD_SD2_D2 = PAD_ID(PB, 6), - MX27_PAD_SD2_D3 = PAD_ID(PB, 7), - MX27_PAD_SD2_CMD = PAD_ID(PB, 8), - MX27_PAD_SD2_CLK = PAD_ID(PB, 9), - MX27_PAD_CSI_D0 = PAD_ID(PB, 10), - MX27_PAD_CSI_D1 = PAD_ID(PB, 11), - MX27_PAD_CSI_D2 = PAD_ID(PB, 12), - MX27_PAD_CSI_D3 = PAD_ID(PB, 13), - MX27_PAD_CSI_D4 = PAD_ID(PB, 14), - MX27_PAD_CSI_MCLK = PAD_ID(PB, 15), - MX27_PAD_CSI_PIXCLK = PAD_ID(PB, 16), - MX27_PAD_CSI_D5 = PAD_ID(PB, 17), - MX27_PAD_CSI_D6 = PAD_ID(PB, 18), - MX27_PAD_CSI_D7 = PAD_ID(PB, 19), - MX27_PAD_CSI_VSYNC = PAD_ID(PB, 20), - MX27_PAD_CSI_HSYNC = PAD_ID(PB, 21), - MX27_PAD_USBH1_SUSP = PAD_ID(PB, 22), - MX27_PAD_USB_PWR = PAD_ID(PB, 23), - MX27_PAD_USB_OC_B = PAD_ID(PB, 24), - MX27_PAD_USBH1_RCV = PAD_ID(PB, 25), - MX27_PAD_USBH1_FS = PAD_ID(PB, 26), - MX27_PAD_USBH1_OE_B = PAD_ID(PB, 27), - MX27_PAD_USBH1_TXDM = PAD_ID(PB, 28), - MX27_PAD_USBH1_TXDP = PAD_ID(PB, 29), - MX27_PAD_USBH1_RXDM = PAD_ID(PB, 30), - MX27_PAD_USBH1_RXDP = PAD_ID(PB, 31), + MX27_PAD_SD2_D0 = PAD_ID(MX27_PB, 4), + MX27_PAD_SD2_D1 = PAD_ID(MX27_PB, 5), + MX27_PAD_SD2_D2 = PAD_ID(MX27_PB, 6), + MX27_PAD_SD2_D3 = PAD_ID(MX27_PB, 7), + MX27_PAD_SD2_CMD = PAD_ID(MX27_PB, 8), + MX27_PAD_SD2_CLK = PAD_ID(MX27_PB, 9), + MX27_PAD_CSI_D0 = PAD_ID(MX27_PB, 10), + MX27_PAD_CSI_D1 = PAD_ID(MX27_PB, 11), + MX27_PAD_CSI_D2 = PAD_ID(MX27_PB, 12), + MX27_PAD_CSI_D3 = PAD_ID(MX27_PB, 13), + MX27_PAD_CSI_D4 = PAD_ID(MX27_PB, 14), + MX27_PAD_CSI_MCLK = PAD_ID(MX27_PB, 15), + MX27_PAD_CSI_PIXCLK = PAD_ID(MX27_PB, 16), + MX27_PAD_CSI_D5 = PAD_ID(MX27_PB, 17), + MX27_PAD_CSI_D6 = PAD_ID(MX27_PB, 18), + MX27_PAD_CSI_D7 = PAD_ID(MX27_PB, 19), + MX27_PAD_CSI_VSYNC = PAD_ID(MX27_PB, 20), + MX27_PAD_CSI_HSYNC = PAD_ID(MX27_PB, 21), + MX27_PAD_USBH1_SUSP = PAD_ID(MX27_PB, 22), + MX27_PAD_USB_PWR = PAD_ID(MX27_PB, 23), + MX27_PAD_USB_OC_B = PAD_ID(MX27_PB, 24), + MX27_PAD_USBH1_RCV = PAD_ID(MX27_PB, 25), + MX27_PAD_USBH1_FS = PAD_ID(MX27_PB, 26), + MX27_PAD_USBH1_OE_B = PAD_ID(MX27_PB, 27), + MX27_PAD_USBH1_TXDM = PAD_ID(MX27_PB, 28), + MX27_PAD_USBH1_TXDP = PAD_ID(MX27_PB, 29), + MX27_PAD_USBH1_RXDM = PAD_ID(MX27_PB, 30), + MX27_PAD_USBH1_RXDP = PAD_ID(MX27_PB, 31), - MX27_PAD_I2C2_SDA = PAD_ID(PC, 5), - MX27_PAD_I2C2_SCL = PAD_ID(PC, 6), - MX27_PAD_USBOTG_DATA5 = PAD_ID(PC, 7), - MX27_PAD_USBOTG_DATA6 = PAD_ID(PC, 8), - MX27_PAD_USBOTG_DATA0 = PAD_ID(PC, 9), - MX27_PAD_USBOTG_DATA2 = PAD_ID(PC, 10), - MX27_PAD_USBOTG_DATA1 = PAD_ID(PC, 11), - MX27_PAD_USBOTG_DATA4 = PAD_ID(PC, 12), - MX27_PAD_USBOTG_DATA3 = PAD_ID(PC, 13), - MX27_PAD_TOUT = PAD_ID(PC, 14), - MX27_PAD_TIN = PAD_ID(PC, 15), - MX27_PAD_SSI4_FS = PAD_ID(PC, 16), - MX27_PAD_SSI4_RXDAT = PAD_ID(PC, 17), - MX27_PAD_SSI4_TXDAT = PAD_ID(PC, 18), - MX27_PAD_SSI4_CLK = PAD_ID(PC, 19), - MX27_PAD_SSI1_FS = PAD_ID(PC, 20), - MX27_PAD_SSI1_RXDAT = PAD_ID(PC, 21), - MX27_PAD_SSI1_TXDAT = PAD_ID(PC, 22), - MX27_PAD_SSI1_CLK = PAD_ID(PC, 23), - MX27_PAD_SSI2_FS = PAD_ID(PC, 24), - MX27_PAD_SSI2_RXDAT = PAD_ID(PC, 25), - MX27_PAD_SSI2_TXDAT = PAD_ID(PC, 26), - MX27_PAD_SSI2_CLK = PAD_ID(PC, 27), - MX27_PAD_SSI3_FS = PAD_ID(PC, 28), - MX27_PAD_SSI3_RXDAT = PAD_ID(PC, 29), - MX27_PAD_SSI3_TXDAT = PAD_ID(PC, 30), - MX27_PAD_SSI3_CLK = PAD_ID(PC, 31), + MX27_PAD_I2C2_SDA = PAD_ID(MX27_PC, 5), + MX27_PAD_I2C2_SCL = PAD_ID(MX27_PC, 6), + MX27_PAD_USBOTG_DATA5 = PAD_ID(MX27_PC, 7), + MX27_PAD_USBOTG_DATA6 = PAD_ID(MX27_PC, 8), + MX27_PAD_USBOTG_DATA0 = PAD_ID(MX27_PC, 9), + MX27_PAD_USBOTG_DATA2 = PAD_ID(MX27_PC, 10), + MX27_PAD_USBOTG_DATA1 = PAD_ID(MX27_PC, 11), + MX27_PAD_USBOTG_DATA4 = PAD_ID(MX27_PC, 12), + MX27_PAD_USBOTG_DATA3 = PAD_ID(MX27_PC, 13), + MX27_PAD_TOUT = PAD_ID(MX27_PC, 14), + MX27_PAD_TIN = PAD_ID(MX27_PC, 15), + MX27_PAD_SSI4_FS = PAD_ID(MX27_PC, 16), + MX27_PAD_SSI4_RXDAT = PAD_ID(MX27_PC, 17), + MX27_PAD_SSI4_TXDAT = PAD_ID(MX27_PC, 18), + MX27_PAD_SSI4_CLK = PAD_ID(MX27_PC, 19), + MX27_PAD_SSI1_FS = PAD_ID(MX27_PC, 20), + MX27_PAD_SSI1_RXDAT = PAD_ID(MX27_PC, 21), + MX27_PAD_SSI1_TXDAT = PAD_ID(MX27_PC, 22), + MX27_PAD_SSI1_CLK = PAD_ID(MX27_PC, 23), + MX27_PAD_SSI2_FS = PAD_ID(MX27_PC, 24), + MX27_PAD_SSI2_RXDAT = PAD_ID(MX27_PC, 25), + MX27_PAD_SSI2_TXDAT = PAD_ID(MX27_PC, 26), + MX27_PAD_SSI2_CLK = PAD_ID(MX27_PC, 27), + MX27_PAD_SSI3_FS = PAD_ID(MX27_PC, 28), + MX27_PAD_SSI3_RXDAT = PAD_ID(MX27_PC, 29), + MX27_PAD_SSI3_TXDAT = PAD_ID(MX27_PC, 30), + MX27_PAD_SSI3_CLK = PAD_ID(MX27_PC, 31), - MX27_PAD_SD3_CMD = PAD_ID(PD, 0), - MX27_PAD_SD3_CLK = PAD_ID(PD, 1), - MX27_PAD_ATA_DATA0 = PAD_ID(PD, 2), - MX27_PAD_ATA_DATA1 = PAD_ID(PD, 3), - MX27_PAD_ATA_DATA2 = PAD_ID(PD, 4), - MX27_PAD_ATA_DATA3 = PAD_ID(PD, 5), - MX27_PAD_ATA_DATA4 = PAD_ID(PD, 6), - MX27_PAD_ATA_DATA5 = PAD_ID(PD, 7), - MX27_PAD_ATA_DATA6 = PAD_ID(PD, 8), - MX27_PAD_ATA_DATA7 = PAD_ID(PD, 9), - MX27_PAD_ATA_DATA8 = PAD_ID(PD, 10), - MX27_PAD_ATA_DATA9 = PAD_ID(PD, 11), - MX27_PAD_ATA_DATA10 = PAD_ID(PD, 12), - MX27_PAD_ATA_DATA11 = PAD_ID(PD, 13), - MX27_PAD_ATA_DATA12 = PAD_ID(PD, 14), - MX27_PAD_ATA_DATA13 = PAD_ID(PD, 15), - MX27_PAD_ATA_DATA14 = PAD_ID(PD, 16), - MX27_PAD_I2C_DATA = PAD_ID(PD, 17), - MX27_PAD_I2C_CLK = PAD_ID(PD, 18), - MX27_PAD_CSPI2_SS2 = PAD_ID(PD, 19), - MX27_PAD_CSPI2_SS1 = PAD_ID(PD, 20), - MX27_PAD_CSPI2_SS0 = PAD_ID(PD, 21), - MX27_PAD_CSPI2_SCLK = PAD_ID(PD, 22), - MX27_PAD_CSPI2_MISO = PAD_ID(PD, 23), - MX27_PAD_CSPI2_MOSI = PAD_ID(PD, 24), - MX27_PAD_CSPI1_RDY = PAD_ID(PD, 25), - MX27_PAD_CSPI1_SS2 = PAD_ID(PD, 26), - MX27_PAD_CSPI1_SS1 = PAD_ID(PD, 27), - MX27_PAD_CSPI1_SS0 = PAD_ID(PD, 28), - MX27_PAD_CSPI1_SCLK = PAD_ID(PD, 29), - MX27_PAD_CSPI1_MISO = PAD_ID(PD, 30), - MX27_PAD_CSPI1_MOSI = PAD_ID(PD, 31), + MX27_PAD_SD3_CMD = PAD_ID(MX27_PD, 0), + MX27_PAD_SD3_CLK = PAD_ID(MX27_PD, 1), + MX27_PAD_ATA_DATA0 = PAD_ID(MX27_PD, 2), + MX27_PAD_ATA_DATA1 = PAD_ID(MX27_PD, 3), + MX27_PAD_ATA_DATA2 = PAD_ID(MX27_PD, 4), + MX27_PAD_ATA_DATA3 = PAD_ID(MX27_PD, 5), + MX27_PAD_ATA_DATA4 = PAD_ID(MX27_PD, 6), + MX27_PAD_ATA_DATA5 = PAD_ID(MX27_PD, 7), + MX27_PAD_ATA_DATA6 = PAD_ID(MX27_PD, 8), + MX27_PAD_ATA_DATA7 = PAD_ID(MX27_PD, 9), + MX27_PAD_ATA_DATA8 = PAD_ID(MX27_PD, 10), + MX27_PAD_ATA_DATA9 = PAD_ID(MX27_PD, 11), + MX27_PAD_ATA_DATA10 = PAD_ID(MX27_PD, 12), + MX27_PAD_ATA_DATA11 = PAD_ID(MX27_PD, 13), + MX27_PAD_ATA_DATA12 = PAD_ID(MX27_PD, 14), + MX27_PAD_ATA_DATA13 = PAD_ID(MX27_PD, 15), + MX27_PAD_ATA_DATA14 = PAD_ID(MX27_PD, 16), + MX27_PAD_I2C_DATA = PAD_ID(MX27_PD, 17), + MX27_PAD_I2C_CLK = PAD_ID(MX27_PD, 18), + MX27_PAD_CSPI2_SS2 = PAD_ID(MX27_PD, 19), + MX27_PAD_CSPI2_SS1 = PAD_ID(MX27_PD, 20), + MX27_PAD_CSPI2_SS0 = PAD_ID(MX27_PD, 21), + MX27_PAD_CSPI2_SCLK = PAD_ID(MX27_PD, 22), + MX27_PAD_CSPI2_MISO = PAD_ID(MX27_PD, 23), + MX27_PAD_CSPI2_MOSI = PAD_ID(MX27_PD, 24), + MX27_PAD_CSPI1_RDY = PAD_ID(MX27_PD, 25), + MX27_PAD_CSPI1_SS2 = PAD_ID(MX27_PD, 26), + MX27_PAD_CSPI1_SS1 = PAD_ID(MX27_PD, 27), + MX27_PAD_CSPI1_SS0 = PAD_ID(MX27_PD, 28), + MX27_PAD_CSPI1_SCLK = PAD_ID(MX27_PD, 29), + MX27_PAD_CSPI1_MISO = PAD_ID(MX27_PD, 30), + MX27_PAD_CSPI1_MOSI = PAD_ID(MX27_PD, 31), - MX27_PAD_USBOTG_NXT = PAD_ID(PE, 0), - MX27_PAD_USBOTG_STP = PAD_ID(PE, 1), - MX27_PAD_USBOTG_DIR = PAD_ID(PE, 2), - MX27_PAD_UART2_CTS = PAD_ID(PE, 3), - MX27_PAD_UART2_RTS = PAD_ID(PE, 4), - MX27_PAD_PWMO = PAD_ID(PE, 5), - MX27_PAD_UART2_TXD = PAD_ID(PE, 6), - MX27_PAD_UART2_RXD = PAD_ID(PE, 7), - MX27_PAD_UART3_TXD = PAD_ID(PE, 8), - MX27_PAD_UART3_RXD = PAD_ID(PE, 9), - MX27_PAD_UART3_CTS = PAD_ID(PE, 10), - MX27_PAD_UART3_RTS = PAD_ID(PE, 11), - MX27_PAD_UART1_TXD = PAD_ID(PE, 12), - MX27_PAD_UART1_RXD = PAD_ID(PE, 13), - MX27_PAD_UART1_CTS = PAD_ID(PE, 14), - MX27_PAD_UART1_RTS = PAD_ID(PE, 15), - MX27_PAD_RTCK = PAD_ID(PE, 16), - MX27_PAD_RESET_OUT_B = PAD_ID(PE, 17), - MX27_PAD_SD1_D0 = PAD_ID(PE, 18), - MX27_PAD_SD1_D1 = PAD_ID(PE, 19), - MX27_PAD_SD1_D2 = PAD_ID(PE, 20), - MX27_PAD_SD1_D3 = PAD_ID(PE, 21), - MX27_PAD_SD1_CMD = PAD_ID(PE, 22), - MX27_PAD_SD1_CLK = PAD_ID(PE, 23), - MX27_PAD_USBOTG_CLK = PAD_ID(PE, 24), - MX27_PAD_USBOTG_DATA7 = PAD_ID(PE, 25), + MX27_PAD_USBOTG_NXT = PAD_ID(MX27_PE, 0), + MX27_PAD_USBOTG_STP = PAD_ID(MX27_PE, 1), + MX27_PAD_USBOTG_DIR = PAD_ID(MX27_PE, 2), + MX27_PAD_UART2_CTS = PAD_ID(MX27_PE, 3), + MX27_PAD_UART2_RTS = PAD_ID(MX27_PE, 4), + MX27_PAD_PWMO = PAD_ID(MX27_PE, 5), + MX27_PAD_UART2_TXD = PAD_ID(MX27_PE, 6), + MX27_PAD_UART2_RXD = PAD_ID(MX27_PE, 7), + MX27_PAD_UART3_TXD = PAD_ID(MX27_PE, 8), + MX27_PAD_UART3_RXD = PAD_ID(MX27_PE, 9), + MX27_PAD_UART3_CTS = PAD_ID(MX27_PE, 10), + MX27_PAD_UART3_RTS = PAD_ID(MX27_PE, 11), + MX27_PAD_UART1_TXD = PAD_ID(MX27_PE, 12), + MX27_PAD_UART1_RXD = PAD_ID(MX27_PE, 13), + MX27_PAD_UART1_CTS = PAD_ID(MX27_PE, 14), + MX27_PAD_UART1_RTS = PAD_ID(MX27_PE, 15), + MX27_PAD_RTCK = PAD_ID(MX27_PE, 16), + MX27_PAD_RESET_OUT_B = PAD_ID(MX27_PE, 17), + MX27_PAD_SD1_D0 = PAD_ID(MX27_PE, 18), + MX27_PAD_SD1_D1 = PAD_ID(MX27_PE, 19), + MX27_PAD_SD1_D2 = PAD_ID(MX27_PE, 20), + MX27_PAD_SD1_D3 = PAD_ID(MX27_PE, 21), + MX27_PAD_SD1_CMD = PAD_ID(MX27_PE, 22), + MX27_PAD_SD1_CLK = PAD_ID(MX27_PE, 23), + MX27_PAD_USBOTG_CLK = PAD_ID(MX27_PE, 24), + MX27_PAD_USBOTG_DATA7 = PAD_ID(MX27_PE, 25), - MX27_PAD_NFRB = PAD_ID(PF, 0), - MX27_PAD_NFCLE = PAD_ID(PF, 1), - MX27_PAD_NFWP_B = PAD_ID(PF, 2), - MX27_PAD_NFCE_B = PAD_ID(PF, 3), - MX27_PAD_NFALE = PAD_ID(PF, 4), - MX27_PAD_NFRE_B = PAD_ID(PF, 5), - MX27_PAD_NFWE_B = PAD_ID(PF, 6), - MX27_PAD_PC_POE = PAD_ID(PF, 7), - MX27_PAD_PC_RW_B = PAD_ID(PF, 8), - MX27_PAD_IOIS16 = PAD_ID(PF, 9), - MX27_PAD_PC_RST = PAD_ID(PF, 10), - MX27_PAD_PC_BVD2 = PAD_ID(PF, 11), - MX27_PAD_PC_BVD1 = PAD_ID(PF, 12), - MX27_PAD_PC_VS2 = PAD_ID(PF, 13), - MX27_PAD_PC_VS1 = PAD_ID(PF, 14), - MX27_PAD_CLKO = PAD_ID(PF, 15), - MX27_PAD_PC_PWRON = PAD_ID(PF, 16), - MX27_PAD_PC_READY = PAD_ID(PF, 17), - MX27_PAD_PC_WAIT_B = PAD_ID(PF, 18), - MX27_PAD_PC_CD2_B = PAD_ID(PF, 19), - MX27_PAD_PC_CD1_B = PAD_ID(PF, 20), - MX27_PAD_CS4_B = PAD_ID(PF, 21), - MX27_PAD_CS5_B = PAD_ID(PF, 22), - MX27_PAD_ATA_DATA15 = PAD_ID(PF, 23), + MX27_PAD_NFRB = PAD_ID(MX27_PF, 0), + MX27_PAD_NFCLE = PAD_ID(MX27_PF, 1), + MX27_PAD_NFWP_B = PAD_ID(MX27_PF, 2), + MX27_PAD_NFCE_B = PAD_ID(MX27_PF, 3), + MX27_PAD_NFALE = PAD_ID(MX27_PF, 4), + MX27_PAD_NFRE_B = PAD_ID(MX27_PF, 5), + MX27_PAD_NFWE_B = PAD_ID(MX27_PF, 6), + MX27_PAD_PC_POE = PAD_ID(MX27_PF, 7), + MX27_PAD_PC_RW_B = PAD_ID(MX27_PF, 8), + MX27_PAD_IOIS16 = PAD_ID(MX27_PF, 9), + MX27_PAD_PC_RST = PAD_ID(MX27_PF, 10), + MX27_PAD_PC_BVD2 = PAD_ID(MX27_PF, 11), + MX27_PAD_PC_BVD1 = PAD_ID(MX27_PF, 12), + MX27_PAD_PC_VS2 = PAD_ID(MX27_PF, 13), + MX27_PAD_PC_VS1 = PAD_ID(MX27_PF, 14), + MX27_PAD_CLKO = PAD_ID(MX27_PF, 15), + MX27_PAD_PC_PWRON = PAD_ID(MX27_PF, 16), + MX27_PAD_PC_READY = PAD_ID(MX27_PF, 17), + MX27_PAD_PC_WAIT_B = PAD_ID(MX27_PF, 18), + MX27_PAD_PC_CD2_B = PAD_ID(MX27_PF, 19), + MX27_PAD_PC_CD1_B = PAD_ID(MX27_PF, 20), + MX27_PAD_CS4_B = PAD_ID(MX27_PF, 21), + MX27_PAD_CS5_B = PAD_ID(MX27_PF, 22), + MX27_PAD_ATA_DATA15 = PAD_ID(MX27_PF, 23), }; /* Pad names for the pinmux subsystem */ diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 067b0d344f0e5..9f938718927bd 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1792,7 +1792,7 @@ MODULE_DEVICE_TABLE(acpi, chv_pinctrl_acpi_match); static struct platform_driver chv_pinctrl_driver = { .probe = chv_pinctrl_probe, - .remove_new = chv_pinctrl_remove, + .remove = chv_pinctrl_remove, .driver = { .name = "cherryview-pinctrl", .pm = pm_sleep_ptr(&chv_pinctrl_pm_ops), diff --git a/drivers/pinctrl/intel/pinctrl-elkhartlake.c b/drivers/pinctrl/intel/pinctrl-elkhartlake.c index 1678634ebc06c..3e45d7fb003ac 100644 --- a/drivers/pinctrl/intel/pinctrl-elkhartlake.c +++ b/drivers/pinctrl/intel/pinctrl-elkhartlake.c @@ -264,6 +264,43 @@ static const struct intel_pinctrl_soc_data ehl_community1_soc_data = { .ncommunities = ARRAY_SIZE(ehl_community1), }; +static const struct pinctrl_pin_desc ehl_community2_pins[] = { + /* DSW */ + PINCTRL_PIN(0, "BATLOWB"), + PINCTRL_PIN(1, "ACPRESENT"), + PINCTRL_PIN(2, "LAN_WAKEB"), + PINCTRL_PIN(3, "PWRBTNB"), + PINCTRL_PIN(4, "SLP_S3B"), + PINCTRL_PIN(5, "SLP_S4B"), + PINCTRL_PIN(6, "SLP_AB"), + PINCTRL_PIN(7, "GPD_7"), + PINCTRL_PIN(8, "SUSCLK"), + PINCTRL_PIN(9, "SLP_WLANB"), + PINCTRL_PIN(10, "SLP_S5B"), + PINCTRL_PIN(11, "LANPHYPC"), + PINCTRL_PIN(12, "INPUT3VSEL"), + PINCTRL_PIN(13, "SLP_LANB"), + PINCTRL_PIN(14, "SLP_SUSB"), + PINCTRL_PIN(15, "WAKEB"), + PINCTRL_PIN(16, "DRAM_RESETB"), +}; + +static const struct intel_padgroup ehl_community2_gpps[] = { + EHL_GPP(0, 0, 16), /* DSW */ +}; + +static const struct intel_community ehl_community2[] = { + EHL_COMMUNITY(0, 0, 16, ehl_community2_gpps), +}; + +static const struct intel_pinctrl_soc_data ehl_community2_soc_data = { + .uid = "2", + .pins = ehl_community2_pins, + .npins = ARRAY_SIZE(ehl_community2_pins), + .communities = ehl_community2, + .ncommunities = ARRAY_SIZE(ehl_community2), +}; + static const struct pinctrl_pin_desc ehl_community3_pins[] = { /* CPU */ PINCTRL_PIN(0, "HDACPU_SDI"), @@ -474,6 +511,7 @@ static const struct intel_pinctrl_soc_data ehl_community5_soc_data = { static const struct intel_pinctrl_soc_data *ehl_soc_data_array[] = { &ehl_community0_soc_data, &ehl_community1_soc_data, + &ehl_community2_soc_data, &ehl_community3_soc_data, &ehl_community4_soc_data, &ehl_community5_soc_data, diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 928607a21d36d..04b438f63ccbb 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -85,6 +85,18 @@ #define PADCFG1_TERM_UP BIT(13) #define PADCFG1_TERM_SHIFT 10 #define PADCFG1_TERM_MASK GENMASK(12, 10) +/* + * Bit 0 Bit 1 Bit 2 Value, Ohms + * + * 0 0 0 - + * 0 0 1 20000 + * 0 1 0 5000 + * 0 1 1 ~4000 + * 1 0 0 1000 (if supported) + * 1 0 1 ~952 (if supported) + * 1 1 0 ~833 (if supported) + * 1 1 1 ~800 (if supported) + */ #define PADCFG1_TERM_20K BIT(2) #define PADCFG1_TERM_5K BIT(1) #define PADCFG1_TERM_4K (BIT(2) | BIT(1)) diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig index 7af287252834a..a417a031659c6 100644 --- a/drivers/pinctrl/mediatek/Kconfig +++ b/drivers/pinctrl/mediatek/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "MediaTek pinctrl drivers" - depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST + depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST config EINT_MTK tristate "MediaTek External Interrupt Support" @@ -126,6 +126,21 @@ config PINCTRL_MT8127 select PINCTRL_MTK # For ARMv8 SoCs +config PINCTRL_AIROHA + tristate "Airoha EN7581 pin control" + depends on OF + depends on ARM64 || COMPILE_TEST + select PINMUX + select GENERIC_PINCONF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GPIOLIB + select GPIOLIB_IRQCHIP + select REGMAP_MMIO + help + Say yes here to support pin controller and gpio driver + on Airoha EN7581 SoC. + config PINCTRL_MT2712 bool "MediaTek MT2712 pin control" depends on OF diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile index 680f7e8526e00..1405d434218ea 100644 --- a/drivers/pinctrl/mediatek/Makefile +++ b/drivers/pinctrl/mediatek/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE) += pinctrl-moore.o obj-$(CONFIG_PINCTRL_MTK_PARIS) += pinctrl-paris.o # SoC Drivers +obj-$(CONFIG_PINCTRL_AIROHA) += pinctrl-airoha.o obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o obj-$(CONFIG_PINCTRL_MT7621) += pinctrl-mt7621.o obj-$(CONFIG_PINCTRL_MT76X8) += pinctrl-mt76x8.o diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c new file mode 100644 index 0000000000000..547a798b71c8a --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -0,0 +1,2971 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Author: Lorenzo Bianconi + * Author: Benjamin Larsson + * Author: Markus Gothe + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinmux.h" + +#define PINCTRL_PIN_GROUP(id) \ + PINCTRL_PINGROUP(#id, id##_pins, ARRAY_SIZE(id##_pins)) + +#define PINCTRL_FUNC_DESC(id) \ + { \ + .desc = { \ + .func = { \ + .name = #id, \ + .groups = id##_groups, \ + .ngroups = ARRAY_SIZE(id##_groups), \ + } \ + }, \ + .groups = id##_func_group, \ + .group_size = ARRAY_SIZE(id##_func_group), \ + } + +#define PINCTRL_CONF_DESC(p, offset, mask) \ + { \ + .pin = p, \ + .reg = { offset, mask }, \ + } + +/* MUX */ +#define REG_GPIO_2ND_I2C_MODE 0x0214 +#define GPIO_MDC_IO_MASTER_MODE_MODE BIT(14) +#define GPIO_I2C_MASTER_MODE_MODE BIT(13) +#define GPIO_I2S_MODE_MASK BIT(12) +#define GPIO_I2C_SLAVE_MODE_MODE BIT(11) +#define GPIO_LAN3_LED1_MODE_MASK BIT(10) +#define GPIO_LAN3_LED0_MODE_MASK BIT(9) +#define GPIO_LAN2_LED1_MODE_MASK BIT(8) +#define GPIO_LAN2_LED0_MODE_MASK BIT(7) +#define GPIO_LAN1_LED1_MODE_MASK BIT(6) +#define GPIO_LAN1_LED0_MODE_MASK BIT(5) +#define GPIO_LAN0_LED1_MODE_MASK BIT(4) +#define GPIO_LAN0_LED0_MODE_MASK BIT(3) +#define PON_TOD_1PPS_MODE_MASK BIT(2) +#define GSW_TOD_1PPS_MODE_MASK BIT(1) +#define GPIO_2ND_I2C_MODE_MASK BIT(0) + +#define REG_GPIO_SPI_CS1_MODE 0x0218 +#define GPIO_PCM_SPI_CS4_MODE_MASK BIT(21) +#define GPIO_PCM_SPI_CS3_MODE_MASK BIT(20) +#define GPIO_PCM_SPI_CS2_MODE_P156_MASK BIT(19) +#define GPIO_PCM_SPI_CS2_MODE_P128_MASK BIT(18) +#define GPIO_PCM_SPI_CS1_MODE_MASK BIT(17) +#define GPIO_PCM_SPI_MODE_MASK BIT(16) +#define GPIO_PCM2_MODE_MASK BIT(13) +#define GPIO_PCM1_MODE_MASK BIT(12) +#define GPIO_PCM_INT_MODE_MASK BIT(9) +#define GPIO_PCM_RESET_MODE_MASK BIT(8) +#define GPIO_SPI_QUAD_MODE_MASK BIT(4) +#define GPIO_SPI_CS4_MODE_MASK BIT(3) +#define GPIO_SPI_CS3_MODE_MASK BIT(2) +#define GPIO_SPI_CS2_MODE_MASK BIT(1) +#define GPIO_SPI_CS1_MODE_MASK BIT(0) + +#define REG_GPIO_PON_MODE 0x021c +#define GPIO_PARALLEL_NAND_MODE_MASK BIT(14) +#define GPIO_SGMII_MDIO_MODE_MASK BIT(13) +#define GPIO_PCIE_RESET2_MASK BIT(12) +#define SIPO_RCLK_MODE_MASK BIT(11) +#define GPIO_PCIE_RESET1_MASK BIT(10) +#define GPIO_PCIE_RESET0_MASK BIT(9) +#define GPIO_UART5_MODE_MASK BIT(8) +#define GPIO_UART4_MODE_MASK BIT(7) +#define GPIO_HSUART_CTS_RTS_MODE_MASK BIT(6) +#define GPIO_HSUART_MODE_MASK BIT(5) +#define GPIO_UART2_CTS_RTS_MODE_MASK BIT(4) +#define GPIO_UART2_MODE_MASK BIT(3) +#define GPIO_SIPO_MODE_MASK BIT(2) +#define GPIO_EMMC_MODE_MASK BIT(1) +#define GPIO_PON_MODE_MASK BIT(0) + +#define REG_NPU_UART_EN 0x0224 +#define JTAG_UDI_EN_MASK BIT(4) +#define JTAG_DFD_EN_MASK BIT(3) + +/* LED MAP */ +#define REG_LAN_LED0_MAPPING 0x027c +#define REG_LAN_LED1_MAPPING 0x0280 + +#define LAN4_LED_MAPPING_MASK GENMASK(18, 16) +#define LAN4_PHY4_LED_MAP BIT(18) +#define LAN4_PHY2_LED_MAP BIT(17) +#define LAN4_PHY1_LED_MAP BIT(16) +#define LAN4_PHY0_LED_MAP 0 +#define LAN4_PHY3_LED_MAP GENMASK(17, 16) + +#define LAN3_LED_MAPPING_MASK GENMASK(14, 12) +#define LAN3_PHY4_LED_MAP BIT(14) +#define LAN3_PHY2_LED_MAP BIT(13) +#define LAN3_PHY1_LED_MAP BIT(12) +#define LAN3_PHY0_LED_MAP 0 +#define LAN3_PHY3_LED_MAP GENMASK(13, 12) + +#define LAN2_LED_MAPPING_MASK GENMASK(10, 8) +#define LAN2_PHY4_LED_MAP BIT(12) +#define LAN2_PHY2_LED_MAP BIT(11) +#define LAN2_PHY1_LED_MAP BIT(10) +#define LAN2_PHY0_LED_MAP 0 +#define LAN2_PHY3_LED_MAP GENMASK(11, 10) + +#define LAN1_LED_MAPPING_MASK GENMASK(6, 4) +#define LAN1_PHY4_LED_MAP BIT(6) +#define LAN1_PHY2_LED_MAP BIT(5) +#define LAN1_PHY1_LED_MAP BIT(4) +#define LAN1_PHY0_LED_MAP 0 +#define LAN1_PHY3_LED_MAP GENMASK(5, 4) + +#define LAN0_LED_MAPPING_MASK GENMASK(2, 0) +#define LAN0_PHY4_LED_MAP BIT(3) +#define LAN0_PHY2_LED_MAP BIT(2) +#define LAN0_PHY1_LED_MAP BIT(1) +#define LAN0_PHY0_LED_MAP 0 +#define LAN0_PHY3_LED_MAP GENMASK(2, 1) + +/* CONF */ +#define REG_I2C_SDA_E2 0x001c +#define SPI_MISO_E2_MASK BIT(14) +#define SPI_MOSI_E2_MASK BIT(13) +#define SPI_CLK_E2_MASK BIT(12) +#define SPI_CS0_E2_MASK BIT(11) +#define PCIE2_RESET_E2_MASK BIT(10) +#define PCIE1_RESET_E2_MASK BIT(9) +#define PCIE0_RESET_E2_MASK BIT(8) +#define UART1_RXD_E2_MASK BIT(3) +#define UART1_TXD_E2_MASK BIT(2) +#define I2C_SCL_E2_MASK BIT(1) +#define I2C_SDA_E2_MASK BIT(0) + +#define REG_I2C_SDA_E4 0x0020 +#define SPI_MISO_E4_MASK BIT(14) +#define SPI_MOSI_E4_MASK BIT(13) +#define SPI_CLK_E4_MASK BIT(12) +#define SPI_CS0_E4_MASK BIT(11) +#define PCIE2_RESET_E4_MASK BIT(10) +#define PCIE1_RESET_E4_MASK BIT(9) +#define PCIE0_RESET_E4_MASK BIT(8) +#define UART1_RXD_E4_MASK BIT(3) +#define UART1_TXD_E4_MASK BIT(2) +#define I2C_SCL_E4_MASK BIT(1) +#define I2C_SDA_E4_MASK BIT(0) + +#define REG_GPIO_L_E2 0x0024 +#define REG_GPIO_L_E4 0x0028 +#define REG_GPIO_H_E2 0x002c +#define REG_GPIO_H_E4 0x0030 + +#define REG_I2C_SDA_PU 0x0044 +#define SPI_MISO_PU_MASK BIT(14) +#define SPI_MOSI_PU_MASK BIT(13) +#define SPI_CLK_PU_MASK BIT(12) +#define SPI_CS0_PU_MASK BIT(11) +#define PCIE2_RESET_PU_MASK BIT(10) +#define PCIE1_RESET_PU_MASK BIT(9) +#define PCIE0_RESET_PU_MASK BIT(8) +#define UART1_RXD_PU_MASK BIT(3) +#define UART1_TXD_PU_MASK BIT(2) +#define I2C_SCL_PU_MASK BIT(1) +#define I2C_SDA_PU_MASK BIT(0) + +#define REG_I2C_SDA_PD 0x0048 +#define SPI_MISO_PD_MASK BIT(14) +#define SPI_MOSI_PD_MASK BIT(13) +#define SPI_CLK_PD_MASK BIT(12) +#define SPI_CS0_PD_MASK BIT(11) +#define PCIE2_RESET_PD_MASK BIT(10) +#define PCIE1_RESET_PD_MASK BIT(9) +#define PCIE0_RESET_PD_MASK BIT(8) +#define UART1_RXD_PD_MASK BIT(3) +#define UART1_TXD_PD_MASK BIT(2) +#define I2C_SCL_PD_MASK BIT(1) +#define I2C_SDA_PD_MASK BIT(0) + +#define REG_GPIO_L_PU 0x004c +#define REG_GPIO_L_PD 0x0050 +#define REG_GPIO_H_PU 0x0054 +#define REG_GPIO_H_PD 0x0058 + +#define REG_PCIE_RESET_OD 0x018c +#define PCIE2_RESET_OD_MASK BIT(2) +#define PCIE1_RESET_OD_MASK BIT(1) +#define PCIE0_RESET_OD_MASK BIT(0) + +/* GPIOs */ +#define REG_GPIO_CTRL 0x0000 +#define REG_GPIO_DATA 0x0004 +#define REG_GPIO_INT 0x0008 +#define REG_GPIO_INT_EDGE 0x000c +#define REG_GPIO_INT_LEVEL 0x0010 +#define REG_GPIO_OE 0x0014 +#define REG_GPIO_CTRL1 0x0020 + +/* PWM MODE CONF */ +#define REG_GPIO_FLASH_MODE_CFG 0x0034 +#define GPIO15_FLASH_MODE_CFG BIT(15) +#define GPIO14_FLASH_MODE_CFG BIT(14) +#define GPIO13_FLASH_MODE_CFG BIT(13) +#define GPIO12_FLASH_MODE_CFG BIT(12) +#define GPIO11_FLASH_MODE_CFG BIT(11) +#define GPIO10_FLASH_MODE_CFG BIT(10) +#define GPIO9_FLASH_MODE_CFG BIT(9) +#define GPIO8_FLASH_MODE_CFG BIT(8) +#define GPIO7_FLASH_MODE_CFG BIT(7) +#define GPIO6_FLASH_MODE_CFG BIT(6) +#define GPIO5_FLASH_MODE_CFG BIT(5) +#define GPIO4_FLASH_MODE_CFG BIT(4) +#define GPIO3_FLASH_MODE_CFG BIT(3) +#define GPIO2_FLASH_MODE_CFG BIT(2) +#define GPIO1_FLASH_MODE_CFG BIT(1) +#define GPIO0_FLASH_MODE_CFG BIT(0) + +#define REG_GPIO_CTRL2 0x0060 +#define REG_GPIO_CTRL3 0x0064 + +/* PWM MODE CONF EXT */ +#define REG_GPIO_FLASH_MODE_CFG_EXT 0x0068 +#define GPIO51_FLASH_MODE_CFG BIT(31) +#define GPIO50_FLASH_MODE_CFG BIT(30) +#define GPIO49_FLASH_MODE_CFG BIT(29) +#define GPIO48_FLASH_MODE_CFG BIT(28) +#define GPIO47_FLASH_MODE_CFG BIT(27) +#define GPIO46_FLASH_MODE_CFG BIT(26) +#define GPIO45_FLASH_MODE_CFG BIT(25) +#define GPIO44_FLASH_MODE_CFG BIT(24) +#define GPIO43_FLASH_MODE_CFG BIT(23) +#define GPIO42_FLASH_MODE_CFG BIT(22) +#define GPIO41_FLASH_MODE_CFG BIT(21) +#define GPIO40_FLASH_MODE_CFG BIT(20) +#define GPIO39_FLASH_MODE_CFG BIT(19) +#define GPIO38_FLASH_MODE_CFG BIT(18) +#define GPIO37_FLASH_MODE_CFG BIT(17) +#define GPIO36_FLASH_MODE_CFG BIT(16) +#define GPIO31_FLASH_MODE_CFG BIT(15) +#define GPIO30_FLASH_MODE_CFG BIT(14) +#define GPIO29_FLASH_MODE_CFG BIT(13) +#define GPIO28_FLASH_MODE_CFG BIT(12) +#define GPIO27_FLASH_MODE_CFG BIT(11) +#define GPIO26_FLASH_MODE_CFG BIT(10) +#define GPIO25_FLASH_MODE_CFG BIT(9) +#define GPIO24_FLASH_MODE_CFG BIT(8) +#define GPIO23_FLASH_MODE_CFG BIT(7) +#define GPIO22_FLASH_MODE_CFG BIT(6) +#define GPIO21_FLASH_MODE_CFG BIT(5) +#define GPIO20_FLASH_MODE_CFG BIT(4) +#define GPIO19_FLASH_MODE_CFG BIT(3) +#define GPIO18_FLASH_MODE_CFG BIT(2) +#define GPIO17_FLASH_MODE_CFG BIT(1) +#define GPIO16_FLASH_MODE_CFG BIT(0) + +#define REG_GPIO_DATA1 0x0070 +#define REG_GPIO_OE1 0x0078 +#define REG_GPIO_INT1 0x007c +#define REG_GPIO_INT_EDGE1 0x0080 +#define REG_GPIO_INT_EDGE2 0x0084 +#define REG_GPIO_INT_EDGE3 0x0088 +#define REG_GPIO_INT_LEVEL1 0x008c +#define REG_GPIO_INT_LEVEL2 0x0090 +#define REG_GPIO_INT_LEVEL3 0x0094 + +#define AIROHA_NUM_PINS 64 +#define AIROHA_PIN_BANK_SIZE (AIROHA_NUM_PINS / 2) +#define AIROHA_REG_GPIOCTRL_NUM_PIN (AIROHA_NUM_PINS / 4) + +static const u32 gpio_data_regs[] = { + REG_GPIO_DATA, + REG_GPIO_DATA1 +}; + +static const u32 gpio_out_regs[] = { + REG_GPIO_OE, + REG_GPIO_OE1 +}; + +static const u32 gpio_dir_regs[] = { + REG_GPIO_CTRL, + REG_GPIO_CTRL1, + REG_GPIO_CTRL2, + REG_GPIO_CTRL3 +}; + +static const u32 irq_status_regs[] = { + REG_GPIO_INT, + REG_GPIO_INT1 +}; + +static const u32 irq_level_regs[] = { + REG_GPIO_INT_LEVEL, + REG_GPIO_INT_LEVEL1, + REG_GPIO_INT_LEVEL2, + REG_GPIO_INT_LEVEL3 +}; + +static const u32 irq_edge_regs[] = { + REG_GPIO_INT_EDGE, + REG_GPIO_INT_EDGE1, + REG_GPIO_INT_EDGE2, + REG_GPIO_INT_EDGE3 +}; + +struct airoha_pinctrl_reg { + u32 offset; + u32 mask; +}; + +enum airoha_pinctrl_mux_func { + AIROHA_FUNC_MUX, + AIROHA_FUNC_PWM_MUX, + AIROHA_FUNC_PWM_EXT_MUX, +}; + +struct airoha_pinctrl_func_group { + const char *name; + struct { + enum airoha_pinctrl_mux_func mux; + u32 offset; + u32 mask; + u32 val; + } regmap[2]; + int regmap_size; +}; + +struct airoha_pinctrl_func { + const struct function_desc desc; + const struct airoha_pinctrl_func_group *groups; + u8 group_size; +}; + +struct airoha_pinctrl_conf { + u32 pin; + struct airoha_pinctrl_reg reg; +}; + +struct airoha_pinctrl_gpiochip { + struct gpio_chip chip; + + /* gpio */ + const u32 *data; + const u32 *dir; + const u32 *out; + /* irq */ + const u32 *status; + const u32 *level; + const u32 *edge; + + u32 irq_type[AIROHA_NUM_PINS]; +}; + +struct airoha_pinctrl { + struct pinctrl_dev *ctrl; + + struct regmap *chip_scu; + struct regmap *regmap; + + struct airoha_pinctrl_gpiochip gpiochip; +}; + +static struct pinctrl_pin_desc airoha_pinctrl_pins[] = { + PINCTRL_PIN(0, "uart1_txd"), + PINCTRL_PIN(1, "uart1_rxd"), + PINCTRL_PIN(2, "i2c_scl"), + PINCTRL_PIN(3, "i2c_sda"), + PINCTRL_PIN(4, "spi_cs0"), + PINCTRL_PIN(5, "spi_clk"), + PINCTRL_PIN(6, "spi_mosi"), + PINCTRL_PIN(7, "spi_miso"), + PINCTRL_PIN(13, "gpio0"), + PINCTRL_PIN(14, "gpio1"), + PINCTRL_PIN(15, "gpio2"), + PINCTRL_PIN(16, "gpio3"), + PINCTRL_PIN(17, "gpio4"), + PINCTRL_PIN(18, "gpio5"), + PINCTRL_PIN(19, "gpio6"), + PINCTRL_PIN(20, "gpio7"), + PINCTRL_PIN(21, "gpio8"), + PINCTRL_PIN(22, "gpio9"), + PINCTRL_PIN(23, "gpio10"), + PINCTRL_PIN(24, "gpio11"), + PINCTRL_PIN(25, "gpio12"), + PINCTRL_PIN(26, "gpio13"), + PINCTRL_PIN(27, "gpio14"), + PINCTRL_PIN(28, "gpio15"), + PINCTRL_PIN(29, "gpio16"), + PINCTRL_PIN(30, "gpio17"), + PINCTRL_PIN(31, "gpio18"), + PINCTRL_PIN(32, "gpio19"), + PINCTRL_PIN(33, "gpio20"), + PINCTRL_PIN(34, "gpio21"), + PINCTRL_PIN(35, "gpio22"), + PINCTRL_PIN(36, "gpio23"), + PINCTRL_PIN(37, "gpio24"), + PINCTRL_PIN(38, "gpio25"), + PINCTRL_PIN(39, "gpio26"), + PINCTRL_PIN(40, "gpio27"), + PINCTRL_PIN(41, "gpio28"), + PINCTRL_PIN(42, "gpio29"), + PINCTRL_PIN(43, "gpio30"), + PINCTRL_PIN(44, "gpio31"), + PINCTRL_PIN(45, "gpio32"), + PINCTRL_PIN(46, "gpio33"), + PINCTRL_PIN(47, "gpio34"), + PINCTRL_PIN(48, "gpio35"), + PINCTRL_PIN(49, "gpio36"), + PINCTRL_PIN(50, "gpio37"), + PINCTRL_PIN(51, "gpio38"), + PINCTRL_PIN(52, "gpio39"), + PINCTRL_PIN(53, "gpio40"), + PINCTRL_PIN(54, "gpio41"), + PINCTRL_PIN(55, "gpio42"), + PINCTRL_PIN(56, "gpio43"), + PINCTRL_PIN(57, "gpio44"), + PINCTRL_PIN(58, "gpio45"), + PINCTRL_PIN(59, "gpio46"), + PINCTRL_PIN(61, "pcie_reset0"), + PINCTRL_PIN(62, "pcie_reset1"), + PINCTRL_PIN(63, "pcie_reset2"), +}; + +static const int pon_pins[] = { 49, 50, 51, 52, 53, 54 }; +static const int pon_tod_1pps_pins[] = { 46 }; +static const int gsw_tod_1pps_pins[] = { 46 }; +static const int sipo_pins[] = { 16, 17 }; +static const int sipo_rclk_pins[] = { 16, 17, 43 }; +static const int mdio_pins[] = { 14, 15 }; +static const int uart2_pins[] = { 48, 55 }; +static const int uart2_cts_rts_pins[] = { 46, 47 }; +static const int hsuart_pins[] = { 28, 29 }; +static const int hsuart_cts_rts_pins[] = { 26, 27 }; +static const int uart4_pins[] = { 38, 39 }; +static const int uart5_pins[] = { 18, 19 }; +static const int i2c0_pins[] = { 2, 3 }; +static const int i2c1_pins[] = { 14, 15 }; +static const int jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; +static const int jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; +static const int i2s_pins[] = { 26, 27, 28, 29 }; +static const int pcm1_pins[] = { 22, 23, 24, 25 }; +static const int pcm2_pins[] = { 18, 19, 20, 21 }; +static const int spi_quad_pins[] = { 32, 33 }; +static const int spi_pins[] = { 4, 5, 6, 7 }; +static const int spi_cs1_pins[] = { 34 }; +static const int pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; +static const int pcm_spi_int_pins[] = { 14 }; +static const int pcm_spi_rst_pins[] = { 15 }; +static const int pcm_spi_cs1_pins[] = { 43 }; +static const int pcm_spi_cs2_pins[] = { 40 }; +static const int pcm_spi_cs2_p128_pins[] = { 40 }; +static const int pcm_spi_cs2_p156_pins[] = { 40 }; +static const int pcm_spi_cs3_pins[] = { 41 }; +static const int pcm_spi_cs4_pins[] = { 42 }; +static const int emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; +static const int pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; +static const int gpio0_pins[] = { 13 }; +static const int gpio1_pins[] = { 14 }; +static const int gpio2_pins[] = { 15 }; +static const int gpio3_pins[] = { 16 }; +static const int gpio4_pins[] = { 17 }; +static const int gpio5_pins[] = { 18 }; +static const int gpio6_pins[] = { 19 }; +static const int gpio7_pins[] = { 20 }; +static const int gpio8_pins[] = { 21 }; +static const int gpio9_pins[] = { 22 }; +static const int gpio10_pins[] = { 23 }; +static const int gpio11_pins[] = { 24 }; +static const int gpio12_pins[] = { 25 }; +static const int gpio13_pins[] = { 26 }; +static const int gpio14_pins[] = { 27 }; +static const int gpio15_pins[] = { 28 }; +static const int gpio16_pins[] = { 29 }; +static const int gpio17_pins[] = { 30 }; +static const int gpio18_pins[] = { 31 }; +static const int gpio19_pins[] = { 32 }; +static const int gpio20_pins[] = { 33 }; +static const int gpio21_pins[] = { 34 }; +static const int gpio22_pins[] = { 35 }; +static const int gpio23_pins[] = { 36 }; +static const int gpio24_pins[] = { 37 }; +static const int gpio25_pins[] = { 38 }; +static const int gpio26_pins[] = { 39 }; +static const int gpio27_pins[] = { 40 }; +static const int gpio28_pins[] = { 41 }; +static const int gpio29_pins[] = { 42 }; +static const int gpio30_pins[] = { 43 }; +static const int gpio31_pins[] = { 44 }; +static const int gpio33_pins[] = { 46 }; +static const int gpio34_pins[] = { 47 }; +static const int gpio35_pins[] = { 48 }; +static const int gpio36_pins[] = { 49 }; +static const int gpio37_pins[] = { 50 }; +static const int gpio38_pins[] = { 51 }; +static const int gpio39_pins[] = { 52 }; +static const int gpio40_pins[] = { 53 }; +static const int gpio41_pins[] = { 54 }; +static const int gpio42_pins[] = { 55 }; +static const int gpio43_pins[] = { 56 }; +static const int gpio44_pins[] = { 57 }; +static const int gpio45_pins[] = { 58 }; +static const int gpio46_pins[] = { 59 }; +static const int pcie_reset0_pins[] = { 61 }; +static const int pcie_reset1_pins[] = { 62 }; +static const int pcie_reset2_pins[] = { 63 }; + +static const struct pingroup airoha_pinctrl_groups[] = { + PINCTRL_PIN_GROUP(pon), + PINCTRL_PIN_GROUP(pon_tod_1pps), + PINCTRL_PIN_GROUP(gsw_tod_1pps), + PINCTRL_PIN_GROUP(sipo), + PINCTRL_PIN_GROUP(sipo_rclk), + PINCTRL_PIN_GROUP(mdio), + PINCTRL_PIN_GROUP(uart2), + PINCTRL_PIN_GROUP(uart2_cts_rts), + PINCTRL_PIN_GROUP(hsuart), + PINCTRL_PIN_GROUP(hsuart_cts_rts), + PINCTRL_PIN_GROUP(uart4), + PINCTRL_PIN_GROUP(uart5), + PINCTRL_PIN_GROUP(i2c0), + PINCTRL_PIN_GROUP(i2c1), + PINCTRL_PIN_GROUP(jtag_udi), + PINCTRL_PIN_GROUP(jtag_dfd), + PINCTRL_PIN_GROUP(i2s), + PINCTRL_PIN_GROUP(pcm1), + PINCTRL_PIN_GROUP(pcm2), + PINCTRL_PIN_GROUP(spi), + PINCTRL_PIN_GROUP(spi_quad), + PINCTRL_PIN_GROUP(spi_cs1), + PINCTRL_PIN_GROUP(pcm_spi), + PINCTRL_PIN_GROUP(pcm_spi_int), + PINCTRL_PIN_GROUP(pcm_spi_rst), + PINCTRL_PIN_GROUP(pcm_spi_cs1), + PINCTRL_PIN_GROUP(pcm_spi_cs2_p128), + PINCTRL_PIN_GROUP(pcm_spi_cs2_p156), + PINCTRL_PIN_GROUP(pcm_spi_cs2), + PINCTRL_PIN_GROUP(pcm_spi_cs3), + PINCTRL_PIN_GROUP(pcm_spi_cs4), + PINCTRL_PIN_GROUP(emmc), + PINCTRL_PIN_GROUP(pnand), + PINCTRL_PIN_GROUP(gpio0), + PINCTRL_PIN_GROUP(gpio1), + PINCTRL_PIN_GROUP(gpio2), + PINCTRL_PIN_GROUP(gpio3), + PINCTRL_PIN_GROUP(gpio4), + PINCTRL_PIN_GROUP(gpio5), + PINCTRL_PIN_GROUP(gpio6), + PINCTRL_PIN_GROUP(gpio7), + PINCTRL_PIN_GROUP(gpio8), + PINCTRL_PIN_GROUP(gpio9), + PINCTRL_PIN_GROUP(gpio10), + PINCTRL_PIN_GROUP(gpio11), + PINCTRL_PIN_GROUP(gpio12), + PINCTRL_PIN_GROUP(gpio13), + PINCTRL_PIN_GROUP(gpio14), + PINCTRL_PIN_GROUP(gpio15), + PINCTRL_PIN_GROUP(gpio16), + PINCTRL_PIN_GROUP(gpio17), + PINCTRL_PIN_GROUP(gpio18), + PINCTRL_PIN_GROUP(gpio19), + PINCTRL_PIN_GROUP(gpio20), + PINCTRL_PIN_GROUP(gpio21), + PINCTRL_PIN_GROUP(gpio22), + PINCTRL_PIN_GROUP(gpio23), + PINCTRL_PIN_GROUP(gpio24), + PINCTRL_PIN_GROUP(gpio25), + PINCTRL_PIN_GROUP(gpio26), + PINCTRL_PIN_GROUP(gpio27), + PINCTRL_PIN_GROUP(gpio28), + PINCTRL_PIN_GROUP(gpio29), + PINCTRL_PIN_GROUP(gpio30), + PINCTRL_PIN_GROUP(gpio31), + PINCTRL_PIN_GROUP(gpio33), + PINCTRL_PIN_GROUP(gpio34), + PINCTRL_PIN_GROUP(gpio35), + PINCTRL_PIN_GROUP(gpio36), + PINCTRL_PIN_GROUP(gpio37), + PINCTRL_PIN_GROUP(gpio38), + PINCTRL_PIN_GROUP(gpio39), + PINCTRL_PIN_GROUP(gpio40), + PINCTRL_PIN_GROUP(gpio41), + PINCTRL_PIN_GROUP(gpio42), + PINCTRL_PIN_GROUP(gpio43), + PINCTRL_PIN_GROUP(gpio44), + PINCTRL_PIN_GROUP(gpio45), + PINCTRL_PIN_GROUP(gpio46), + PINCTRL_PIN_GROUP(pcie_reset0), + PINCTRL_PIN_GROUP(pcie_reset1), + PINCTRL_PIN_GROUP(pcie_reset2), +}; + +static const char *const pon_groups[] = { "pon" }; +static const char *const tod_1pps_groups[] = { "pon_tod_1pps", "gsw_tod_1pps" }; +static const char *const sipo_groups[] = { "sipo", "sipo_rclk" }; +static const char *const mdio_groups[] = { "mdio" }; +static const char *const uart_groups[] = { "uart2", "uart2_cts_rts", "hsuart", + "hsuart_cts_rts", "uart4", + "uart5" }; +static const char *const i2c_groups[] = { "i2c1" }; +static const char *const jtag_groups[] = { "jtag_udi", "jtag_dfd" }; +static const char *const pcm_groups[] = { "pcm1", "pcm2" }; +static const char *const spi_groups[] = { "spi_quad", "spi_cs1" }; +static const char *const pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", + "pcm_spi_rst", "pcm_spi_cs1", + "pcm_spi_cs2_p156", + "pcm_spi_cs2_p128", + "pcm_spi_cs3", "pcm_spi_cs4" }; +static const char *const i2s_groups[] = { "i2s" }; +static const char *const emmc_groups[] = { "emmc" }; +static const char *const pnand_groups[] = { "pnand" }; +static const char *const pcie_reset_groups[] = { "pcie_reset0", "pcie_reset1", + "pcie_reset2" }; +static const char *const pwm_groups[] = { "gpio0", "gpio1", + "gpio2", "gpio3", + "gpio4", "gpio5", + "gpio6", "gpio7", + "gpio8", "gpio9", + "gpio10", "gpio11", + "gpio12", "gpio13", + "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio18", "gpio19", + "gpio20", "gpio21", + "gpio22", "gpio23", + "gpio24", "gpio25", + "gpio26", "gpio27", + "gpio28", "gpio29", + "gpio30", "gpio31", + "gpio36", "gpio37", + "gpio38", "gpio39", + "gpio40", "gpio41", + "gpio42", "gpio43", + "gpio44", "gpio45", + "gpio46", "gpio47" }; +static const char *const phy1_led0_groups[] = { "gpio33", "gpio34", + "gpio35", "gpio42" }; +static const char *const phy2_led0_groups[] = { "gpio33", "gpio34", + "gpio35", "gpio42" }; +static const char *const phy3_led0_groups[] = { "gpio33", "gpio34", + "gpio35", "gpio42" }; +static const char *const phy4_led0_groups[] = { "gpio33", "gpio34", + "gpio35", "gpio42" }; +static const char *const phy1_led1_groups[] = { "gpio43", "gpio44", + "gpio45", "gpio46" }; +static const char *const phy2_led1_groups[] = { "gpio43", "gpio44", + "gpio45", "gpio46" }; +static const char *const phy3_led1_groups[] = { "gpio43", "gpio44", + "gpio45", "gpio46" }; +static const char *const phy4_led1_groups[] = { "gpio43", "gpio44", + "gpio45", "gpio46" }; + +static const struct airoha_pinctrl_func_group pon_func_group[] = { + { + .name = "pon", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_PON_MODE_MASK, + GPIO_PON_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group tod_1pps_func_group[] = { + { + .name = "pon_tod_1pps", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + PON_TOD_1PPS_MODE_MASK, + PON_TOD_1PPS_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "gsw_tod_1pps", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GSW_TOD_1PPS_MODE_MASK, + GSW_TOD_1PPS_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group sipo_func_group[] = { + { + .name = "sipo", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK, + GPIO_SIPO_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "sipo_rclk", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK, + GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group mdio_func_group[] = { + { + .name = "mdio", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_SGMII_MDIO_MODE_MASK, + GPIO_SGMII_MDIO_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_MDC_IO_MASTER_MODE_MODE, + GPIO_MDC_IO_MASTER_MODE_MODE + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group uart_func_group[] = { + { + .name = "uart2", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_UART2_MODE_MASK, + GPIO_UART2_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "uart2_cts_rts", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK, + GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "hsuart", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK, + GPIO_HSUART_MODE_MASK + }, + .regmap_size = 1, + }, + { + .name = "hsuart_cts_rts", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK, + GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "uart4", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_UART4_MODE_MASK, + GPIO_UART4_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "uart5", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_UART5_MODE_MASK, + GPIO_UART5_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group i2c_func_group[] = { + { + .name = "i2c1", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_2ND_I2C_MODE_MASK, + GPIO_2ND_I2C_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group jtag_func_group[] = { + { + .name = "jtag_udi", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_NPU_UART_EN, + JTAG_UDI_EN_MASK, + JTAG_UDI_EN_MASK + }, + .regmap_size = 1, + }, { + .name = "jtag_dfd", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_NPU_UART_EN, + JTAG_DFD_EN_MASK, + JTAG_DFD_EN_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group pcm_func_group[] = { + { + .name = "pcm1", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM1_MODE_MASK, + GPIO_PCM1_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm2", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM2_MODE_MASK, + GPIO_PCM2_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group spi_func_group[] = { + { + .name = "spi_quad", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_SPI_QUAD_MODE_MASK, + GPIO_SPI_QUAD_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "spi_cs1", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_SPI_CS1_MODE_MASK, + GPIO_SPI_CS1_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "spi_cs2", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_SPI_CS2_MODE_MASK, + GPIO_SPI_CS2_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "spi_cs3", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_SPI_CS3_MODE_MASK, + GPIO_SPI_CS3_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "spi_cs4", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_SPI_CS4_MODE_MASK, + GPIO_SPI_CS4_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group pcm_spi_func_group[] = { + { + .name = "pcm_spi", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_SPI_MODE_MASK, + GPIO_PCM_SPI_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_int", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_INT_MODE_MASK, + GPIO_PCM_INT_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_rst", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_RESET_MODE_MASK, + GPIO_PCM_RESET_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_cs1", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_SPI_CS1_MODE_MASK, + GPIO_PCM_SPI_CS1_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_cs2_p128", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_SPI_CS2_MODE_P128_MASK, + GPIO_PCM_SPI_CS2_MODE_P128_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_cs2_p156", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_SPI_CS2_MODE_P156_MASK, + GPIO_PCM_SPI_CS2_MODE_P156_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_cs3", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_SPI_CS3_MODE_MASK, + GPIO_PCM_SPI_CS3_MODE_MASK + }, + .regmap_size = 1, + }, { + .name = "pcm_spi_cs4", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_SPI_CS1_MODE, + GPIO_PCM_SPI_CS4_MODE_MASK, + GPIO_PCM_SPI_CS4_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group i2s_func_group[] = { + { + .name = "i2s", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_I2S_MODE_MASK, + GPIO_I2S_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group emmc_func_group[] = { + { + .name = "emmc", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_EMMC_MODE_MASK, + GPIO_EMMC_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group pnand_func_group[] = { + { + .name = "pnand", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_PARALLEL_NAND_MODE_MASK, + GPIO_PARALLEL_NAND_MODE_MASK + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group pcie_reset_func_group[] = { + { + .name = "pcie_reset0", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_PCIE_RESET0_MASK, + GPIO_PCIE_RESET0_MASK + }, + .regmap_size = 1, + }, { + .name = "pcie_reset1", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_PCIE_RESET1_MASK, + GPIO_PCIE_RESET1_MASK + }, + .regmap_size = 1, + }, { + .name = "pcie_reset2", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_PON_MODE, + GPIO_PCIE_RESET2_MASK, + GPIO_PCIE_RESET2_MASK + }, + .regmap_size = 1, + }, +}; + +/* PWM */ +static const struct airoha_pinctrl_func_group pwm_func_group[] = { + { + .name = "gpio0", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO0_FLASH_MODE_CFG, + GPIO0_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio1", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO1_FLASH_MODE_CFG, + GPIO1_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio2", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO2_FLASH_MODE_CFG, + GPIO2_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio3", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO3_FLASH_MODE_CFG, + GPIO3_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio4", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO4_FLASH_MODE_CFG, + GPIO4_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio5", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO5_FLASH_MODE_CFG, + GPIO5_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio6", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO6_FLASH_MODE_CFG, + GPIO6_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio7", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO7_FLASH_MODE_CFG, + GPIO7_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio8", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO8_FLASH_MODE_CFG, + GPIO8_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio9", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO9_FLASH_MODE_CFG, + GPIO9_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio10", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO10_FLASH_MODE_CFG, + GPIO10_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio11", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO11_FLASH_MODE_CFG, + GPIO11_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio12", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO12_FLASH_MODE_CFG, + GPIO12_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio13", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO13_FLASH_MODE_CFG, + GPIO13_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio14", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO14_FLASH_MODE_CFG, + GPIO14_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio15", + .regmap[0] = { + AIROHA_FUNC_PWM_MUX, + REG_GPIO_FLASH_MODE_CFG, + GPIO15_FLASH_MODE_CFG, + GPIO15_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio16", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO16_FLASH_MODE_CFG, + GPIO16_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio17", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO17_FLASH_MODE_CFG, + GPIO17_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio18", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO18_FLASH_MODE_CFG, + GPIO18_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio19", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO19_FLASH_MODE_CFG, + GPIO19_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio20", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO20_FLASH_MODE_CFG, + GPIO20_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio21", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO21_FLASH_MODE_CFG, + GPIO21_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio22", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO22_FLASH_MODE_CFG, + GPIO22_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio23", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO23_FLASH_MODE_CFG, + GPIO23_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio24", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO24_FLASH_MODE_CFG, + GPIO24_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio25", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO25_FLASH_MODE_CFG, + GPIO25_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio26", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO26_FLASH_MODE_CFG, + GPIO26_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio27", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO27_FLASH_MODE_CFG, + GPIO27_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio28", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO28_FLASH_MODE_CFG, + GPIO28_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio29", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO29_FLASH_MODE_CFG, + GPIO29_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio30", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO30_FLASH_MODE_CFG, + GPIO30_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio31", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO31_FLASH_MODE_CFG, + GPIO31_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio36", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO36_FLASH_MODE_CFG, + GPIO36_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio37", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO37_FLASH_MODE_CFG, + GPIO37_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio38", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO38_FLASH_MODE_CFG, + GPIO38_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio39", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO39_FLASH_MODE_CFG, + GPIO39_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio40", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO40_FLASH_MODE_CFG, + GPIO40_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio41", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO41_FLASH_MODE_CFG, + GPIO41_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio42", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO42_FLASH_MODE_CFG, + GPIO42_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio43", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO43_FLASH_MODE_CFG, + GPIO43_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio44", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO44_FLASH_MODE_CFG, + GPIO44_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio45", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO45_FLASH_MODE_CFG, + GPIO45_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio46", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO46_FLASH_MODE_CFG, + GPIO46_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, { + .name = "gpio47", + .regmap[0] = { + AIROHA_FUNC_PWM_EXT_MUX, + REG_GPIO_FLASH_MODE_CFG_EXT, + GPIO47_FLASH_MODE_CFG, + GPIO47_FLASH_MODE_CFG + }, + .regmap_size = 1, + }, +}; + +static const struct airoha_pinctrl_func_group phy1_led0_func_group[] = { + { + .name = "gpio33", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED0_MODE_MASK, + GPIO_LAN0_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY1_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio34", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED0_MODE_MASK, + GPIO_LAN1_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY1_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio35", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED0_MODE_MASK, + GPIO_LAN2_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY1_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio42", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY1_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy2_led0_func_group[] = { + { + .name = "gpio33", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED0_MODE_MASK, + GPIO_LAN0_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY2_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio34", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED0_MODE_MASK, + GPIO_LAN1_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY2_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio35", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED0_MODE_MASK, + GPIO_LAN2_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY2_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio42", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY2_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy3_led0_func_group[] = { + { + .name = "gpio33", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED0_MODE_MASK, + GPIO_LAN0_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY3_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio34", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED0_MODE_MASK, + GPIO_LAN1_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY3_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio35", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED0_MODE_MASK, + GPIO_LAN2_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY3_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio42", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY3_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy4_led0_func_group[] = { + { + .name = "gpio33", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED0_MODE_MASK, + GPIO_LAN0_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY4_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio34", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED0_MODE_MASK, + GPIO_LAN1_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY4_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio35", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED0_MODE_MASK, + GPIO_LAN2_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY4_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio42", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY4_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy1_led1_func_group[] = { + { + .name = "gpio43", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED1_MODE_MASK, + GPIO_LAN0_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY1_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio44", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED1_MODE_MASK, + GPIO_LAN1_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY1_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio45", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED1_MODE_MASK, + GPIO_LAN2_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY1_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio46", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY1_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy2_led1_func_group[] = { + { + .name = "gpio43", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED1_MODE_MASK, + GPIO_LAN0_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY2_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio44", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED1_MODE_MASK, + GPIO_LAN1_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY2_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio45", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED1_MODE_MASK, + GPIO_LAN2_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY2_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio46", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY2_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = { + { + .name = "gpio43", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED1_MODE_MASK, + GPIO_LAN0_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY3_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio44", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED1_MODE_MASK, + GPIO_LAN1_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY3_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio45", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED1_MODE_MASK, + GPIO_LAN2_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY3_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio46", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY3_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { + { + .name = "gpio43", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN0_LED1_MODE_MASK, + GPIO_LAN0_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN1_LED_MAPPING_MASK, + LAN1_PHY4_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio44", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN1_LED1_MODE_MASK, + GPIO_LAN1_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN2_LED_MAPPING_MASK, + LAN2_PHY4_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio45", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN2_LED1_MODE_MASK, + GPIO_LAN2_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN3_LED_MAPPING_MASK, + LAN3_PHY4_LED_MAP + }, + .regmap_size = 2, + }, { + .name = "gpio46", + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_LAN3_LED0_MODE_MASK, + GPIO_LAN3_LED0_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, + LAN4_LED_MAPPING_MASK, + LAN4_PHY4_LED_MAP + }, + .regmap_size = 2, + }, +}; + +static const struct airoha_pinctrl_func airoha_pinctrl_funcs[] = { + PINCTRL_FUNC_DESC(pon), + PINCTRL_FUNC_DESC(tod_1pps), + PINCTRL_FUNC_DESC(sipo), + PINCTRL_FUNC_DESC(mdio), + PINCTRL_FUNC_DESC(uart), + PINCTRL_FUNC_DESC(i2c), + PINCTRL_FUNC_DESC(jtag), + PINCTRL_FUNC_DESC(pcm), + PINCTRL_FUNC_DESC(spi), + PINCTRL_FUNC_DESC(pcm_spi), + PINCTRL_FUNC_DESC(i2s), + PINCTRL_FUNC_DESC(emmc), + PINCTRL_FUNC_DESC(pnand), + PINCTRL_FUNC_DESC(pcie_reset), + PINCTRL_FUNC_DESC(pwm), + PINCTRL_FUNC_DESC(phy1_led0), + PINCTRL_FUNC_DESC(phy2_led0), + PINCTRL_FUNC_DESC(phy3_led0), + PINCTRL_FUNC_DESC(phy4_led0), + PINCTRL_FUNC_DESC(phy1_led1), + PINCTRL_FUNC_DESC(phy2_led1), + PINCTRL_FUNC_DESC(phy3_led1), + PINCTRL_FUNC_DESC(phy4_led1), +}; + +static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_PU, I2C_SDA_PU_MASK), + PINCTRL_CONF_DESC(3, REG_I2C_SDA_PU, I2C_SCL_PU_MASK), + PINCTRL_CONF_DESC(4, REG_I2C_SDA_PU, SPI_CS0_PU_MASK), + PINCTRL_CONF_DESC(5, REG_I2C_SDA_PU, SPI_CLK_PU_MASK), + PINCTRL_CONF_DESC(6, REG_I2C_SDA_PU, SPI_MOSI_PU_MASK), + PINCTRL_CONF_DESC(7, REG_I2C_SDA_PU, SPI_MISO_PU_MASK), + PINCTRL_CONF_DESC(13, REG_GPIO_L_PU, BIT(0)), + PINCTRL_CONF_DESC(14, REG_GPIO_L_PU, BIT(1)), + PINCTRL_CONF_DESC(15, REG_GPIO_L_PU, BIT(2)), + PINCTRL_CONF_DESC(16, REG_GPIO_L_PU, BIT(3)), + PINCTRL_CONF_DESC(17, REG_GPIO_L_PU, BIT(4)), + PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(5)), + PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(6)), + PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(7)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(8)), + PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(9)), + PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(10)), + PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(11)), + PINCTRL_CONF_DESC(25, REG_GPIO_L_PU, BIT(12)), + PINCTRL_CONF_DESC(26, REG_GPIO_L_PU, BIT(13)), + PINCTRL_CONF_DESC(27, REG_GPIO_L_PU, BIT(14)), + PINCTRL_CONF_DESC(28, REG_GPIO_L_PU, BIT(15)), + PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)), + PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)), + PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)), + PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)), + PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)), + PINCTRL_CONF_DESC(36, REG_GPIO_L_PU, BIT(23)), + PINCTRL_CONF_DESC(37, REG_GPIO_L_PU, BIT(24)), + PINCTRL_CONF_DESC(38, REG_GPIO_L_PU, BIT(25)), + PINCTRL_CONF_DESC(39, REG_GPIO_L_PU, BIT(26)), + PINCTRL_CONF_DESC(40, REG_GPIO_L_PU, BIT(27)), + PINCTRL_CONF_DESC(41, REG_GPIO_L_PU, BIT(28)), + PINCTRL_CONF_DESC(42, REG_GPIO_L_PU, BIT(29)), + PINCTRL_CONF_DESC(43, REG_GPIO_L_PU, BIT(30)), + PINCTRL_CONF_DESC(44, REG_GPIO_L_PU, BIT(31)), + PINCTRL_CONF_DESC(45, REG_GPIO_H_PU, BIT(0)), + PINCTRL_CONF_DESC(46, REG_GPIO_H_PU, BIT(1)), + PINCTRL_CONF_DESC(47, REG_GPIO_H_PU, BIT(2)), + PINCTRL_CONF_DESC(48, REG_GPIO_H_PU, BIT(3)), + PINCTRL_CONF_DESC(49, REG_GPIO_H_PU, BIT(4)), + PINCTRL_CONF_DESC(50, REG_GPIO_H_PU, BIT(5)), + PINCTRL_CONF_DESC(51, REG_GPIO_H_PU, BIT(6)), + PINCTRL_CONF_DESC(52, REG_GPIO_H_PU, BIT(7)), + PINCTRL_CONF_DESC(53, REG_GPIO_H_PU, BIT(8)), + PINCTRL_CONF_DESC(54, REG_GPIO_H_PU, BIT(9)), + PINCTRL_CONF_DESC(55, REG_GPIO_H_PU, BIT(10)), + PINCTRL_CONF_DESC(56, REG_GPIO_H_PU, BIT(11)), + PINCTRL_CONF_DESC(57, REG_GPIO_H_PU, BIT(12)), + PINCTRL_CONF_DESC(58, REG_GPIO_H_PU, BIT(13)), + PINCTRL_CONF_DESC(59, REG_GPIO_H_PU, BIT(14)), + PINCTRL_CONF_DESC(61, REG_I2C_SDA_PU, PCIE0_RESET_PU_MASK), + PINCTRL_CONF_DESC(62, REG_I2C_SDA_PU, PCIE1_RESET_PU_MASK), + PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK), +}; + +static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_PD, I2C_SDA_PD_MASK), + PINCTRL_CONF_DESC(3, REG_I2C_SDA_PD, I2C_SCL_PD_MASK), + PINCTRL_CONF_DESC(4, REG_I2C_SDA_PD, SPI_CS0_PD_MASK), + PINCTRL_CONF_DESC(5, REG_I2C_SDA_PD, SPI_CLK_PD_MASK), + PINCTRL_CONF_DESC(6, REG_I2C_SDA_PD, SPI_MOSI_PD_MASK), + PINCTRL_CONF_DESC(7, REG_I2C_SDA_PD, SPI_MISO_PD_MASK), + PINCTRL_CONF_DESC(13, REG_GPIO_L_PD, BIT(0)), + PINCTRL_CONF_DESC(14, REG_GPIO_L_PD, BIT(1)), + PINCTRL_CONF_DESC(15, REG_GPIO_L_PD, BIT(2)), + PINCTRL_CONF_DESC(16, REG_GPIO_L_PD, BIT(3)), + PINCTRL_CONF_DESC(17, REG_GPIO_L_PD, BIT(4)), + PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(5)), + PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(6)), + PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(7)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(8)), + PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(9)), + PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(10)), + PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(11)), + PINCTRL_CONF_DESC(25, REG_GPIO_L_PD, BIT(12)), + PINCTRL_CONF_DESC(26, REG_GPIO_L_PD, BIT(13)), + PINCTRL_CONF_DESC(27, REG_GPIO_L_PD, BIT(14)), + PINCTRL_CONF_DESC(28, REG_GPIO_L_PD, BIT(15)), + PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)), + PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)), + PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)), + PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)), + PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)), + PINCTRL_CONF_DESC(36, REG_GPIO_L_PD, BIT(23)), + PINCTRL_CONF_DESC(37, REG_GPIO_L_PD, BIT(24)), + PINCTRL_CONF_DESC(38, REG_GPIO_L_PD, BIT(25)), + PINCTRL_CONF_DESC(39, REG_GPIO_L_PD, BIT(26)), + PINCTRL_CONF_DESC(40, REG_GPIO_L_PD, BIT(27)), + PINCTRL_CONF_DESC(41, REG_GPIO_L_PD, BIT(28)), + PINCTRL_CONF_DESC(42, REG_GPIO_L_PD, BIT(29)), + PINCTRL_CONF_DESC(43, REG_GPIO_L_PD, BIT(30)), + PINCTRL_CONF_DESC(44, REG_GPIO_L_PD, BIT(31)), + PINCTRL_CONF_DESC(45, REG_GPIO_H_PD, BIT(0)), + PINCTRL_CONF_DESC(46, REG_GPIO_H_PD, BIT(1)), + PINCTRL_CONF_DESC(47, REG_GPIO_H_PD, BIT(2)), + PINCTRL_CONF_DESC(48, REG_GPIO_H_PD, BIT(3)), + PINCTRL_CONF_DESC(49, REG_GPIO_H_PD, BIT(4)), + PINCTRL_CONF_DESC(50, REG_GPIO_H_PD, BIT(5)), + PINCTRL_CONF_DESC(51, REG_GPIO_H_PD, BIT(6)), + PINCTRL_CONF_DESC(52, REG_GPIO_H_PD, BIT(7)), + PINCTRL_CONF_DESC(53, REG_GPIO_H_PD, BIT(8)), + PINCTRL_CONF_DESC(54, REG_GPIO_H_PD, BIT(9)), + PINCTRL_CONF_DESC(55, REG_GPIO_H_PD, BIT(10)), + PINCTRL_CONF_DESC(56, REG_GPIO_H_PD, BIT(11)), + PINCTRL_CONF_DESC(57, REG_GPIO_H_PD, BIT(12)), + PINCTRL_CONF_DESC(58, REG_GPIO_H_PD, BIT(13)), + PINCTRL_CONF_DESC(59, REG_GPIO_H_PD, BIT(14)), + PINCTRL_CONF_DESC(61, REG_I2C_SDA_PD, PCIE0_RESET_PD_MASK), + PINCTRL_CONF_DESC(62, REG_I2C_SDA_PD, PCIE1_RESET_PD_MASK), + PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK), +}; + +static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_E2, I2C_SDA_E2_MASK), + PINCTRL_CONF_DESC(3, REG_I2C_SDA_E2, I2C_SCL_E2_MASK), + PINCTRL_CONF_DESC(4, REG_I2C_SDA_E2, SPI_CS0_E2_MASK), + PINCTRL_CONF_DESC(5, REG_I2C_SDA_E2, SPI_CLK_E2_MASK), + PINCTRL_CONF_DESC(6, REG_I2C_SDA_E2, SPI_MOSI_E2_MASK), + PINCTRL_CONF_DESC(7, REG_I2C_SDA_E2, SPI_MISO_E2_MASK), + PINCTRL_CONF_DESC(13, REG_GPIO_L_E2, BIT(0)), + PINCTRL_CONF_DESC(14, REG_GPIO_L_E2, BIT(1)), + PINCTRL_CONF_DESC(15, REG_GPIO_L_E2, BIT(2)), + PINCTRL_CONF_DESC(16, REG_GPIO_L_E2, BIT(3)), + PINCTRL_CONF_DESC(17, REG_GPIO_L_E2, BIT(4)), + PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(5)), + PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(6)), + PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(7)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(8)), + PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(9)), + PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(10)), + PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(11)), + PINCTRL_CONF_DESC(25, REG_GPIO_L_E2, BIT(12)), + PINCTRL_CONF_DESC(26, REG_GPIO_L_E2, BIT(13)), + PINCTRL_CONF_DESC(27, REG_GPIO_L_E2, BIT(14)), + PINCTRL_CONF_DESC(28, REG_GPIO_L_E2, BIT(15)), + PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)), + PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)), + PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)), + PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)), + PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)), + PINCTRL_CONF_DESC(36, REG_GPIO_L_E2, BIT(23)), + PINCTRL_CONF_DESC(37, REG_GPIO_L_E2, BIT(24)), + PINCTRL_CONF_DESC(38, REG_GPIO_L_E2, BIT(25)), + PINCTRL_CONF_DESC(39, REG_GPIO_L_E2, BIT(26)), + PINCTRL_CONF_DESC(40, REG_GPIO_L_E2, BIT(27)), + PINCTRL_CONF_DESC(41, REG_GPIO_L_E2, BIT(28)), + PINCTRL_CONF_DESC(42, REG_GPIO_L_E2, BIT(29)), + PINCTRL_CONF_DESC(43, REG_GPIO_L_E2, BIT(30)), + PINCTRL_CONF_DESC(44, REG_GPIO_L_E2, BIT(31)), + PINCTRL_CONF_DESC(45, REG_GPIO_H_E2, BIT(0)), + PINCTRL_CONF_DESC(46, REG_GPIO_H_E2, BIT(1)), + PINCTRL_CONF_DESC(47, REG_GPIO_H_E2, BIT(2)), + PINCTRL_CONF_DESC(48, REG_GPIO_H_E2, BIT(3)), + PINCTRL_CONF_DESC(49, REG_GPIO_H_E2, BIT(4)), + PINCTRL_CONF_DESC(50, REG_GPIO_H_E2, BIT(5)), + PINCTRL_CONF_DESC(51, REG_GPIO_H_E2, BIT(6)), + PINCTRL_CONF_DESC(52, REG_GPIO_H_E2, BIT(7)), + PINCTRL_CONF_DESC(53, REG_GPIO_H_E2, BIT(8)), + PINCTRL_CONF_DESC(54, REG_GPIO_H_E2, BIT(9)), + PINCTRL_CONF_DESC(55, REG_GPIO_H_E2, BIT(10)), + PINCTRL_CONF_DESC(56, REG_GPIO_H_E2, BIT(11)), + PINCTRL_CONF_DESC(57, REG_GPIO_H_E2, BIT(12)), + PINCTRL_CONF_DESC(58, REG_GPIO_H_E2, BIT(13)), + PINCTRL_CONF_DESC(59, REG_GPIO_H_E2, BIT(14)), + PINCTRL_CONF_DESC(61, REG_I2C_SDA_E2, PCIE0_RESET_E2_MASK), + PINCTRL_CONF_DESC(62, REG_I2C_SDA_E2, PCIE1_RESET_E2_MASK), + PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK), +}; + +static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_E4, I2C_SDA_E4_MASK), + PINCTRL_CONF_DESC(3, REG_I2C_SDA_E4, I2C_SCL_E4_MASK), + PINCTRL_CONF_DESC(4, REG_I2C_SDA_E4, SPI_CS0_E4_MASK), + PINCTRL_CONF_DESC(5, REG_I2C_SDA_E4, SPI_CLK_E4_MASK), + PINCTRL_CONF_DESC(6, REG_I2C_SDA_E4, SPI_MOSI_E4_MASK), + PINCTRL_CONF_DESC(7, REG_I2C_SDA_E4, SPI_MISO_E4_MASK), + PINCTRL_CONF_DESC(13, REG_GPIO_L_E4, BIT(0)), + PINCTRL_CONF_DESC(14, REG_GPIO_L_E4, BIT(1)), + PINCTRL_CONF_DESC(15, REG_GPIO_L_E4, BIT(2)), + PINCTRL_CONF_DESC(16, REG_GPIO_L_E4, BIT(3)), + PINCTRL_CONF_DESC(17, REG_GPIO_L_E4, BIT(4)), + PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(5)), + PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(6)), + PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(7)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(8)), + PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(9)), + PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(10)), + PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(11)), + PINCTRL_CONF_DESC(25, REG_GPIO_L_E4, BIT(12)), + PINCTRL_CONF_DESC(26, REG_GPIO_L_E4, BIT(13)), + PINCTRL_CONF_DESC(27, REG_GPIO_L_E4, BIT(14)), + PINCTRL_CONF_DESC(28, REG_GPIO_L_E4, BIT(15)), + PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)), + PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)), + PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)), + PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)), + PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)), + PINCTRL_CONF_DESC(36, REG_GPIO_L_E4, BIT(23)), + PINCTRL_CONF_DESC(37, REG_GPIO_L_E4, BIT(24)), + PINCTRL_CONF_DESC(38, REG_GPIO_L_E4, BIT(25)), + PINCTRL_CONF_DESC(39, REG_GPIO_L_E4, BIT(26)), + PINCTRL_CONF_DESC(40, REG_GPIO_L_E4, BIT(27)), + PINCTRL_CONF_DESC(41, REG_GPIO_L_E4, BIT(28)), + PINCTRL_CONF_DESC(42, REG_GPIO_L_E4, BIT(29)), + PINCTRL_CONF_DESC(43, REG_GPIO_L_E4, BIT(30)), + PINCTRL_CONF_DESC(44, REG_GPIO_L_E4, BIT(31)), + PINCTRL_CONF_DESC(45, REG_GPIO_H_E4, BIT(0)), + PINCTRL_CONF_DESC(46, REG_GPIO_H_E4, BIT(1)), + PINCTRL_CONF_DESC(47, REG_GPIO_H_E4, BIT(2)), + PINCTRL_CONF_DESC(48, REG_GPIO_H_E4, BIT(3)), + PINCTRL_CONF_DESC(49, REG_GPIO_H_E4, BIT(4)), + PINCTRL_CONF_DESC(50, REG_GPIO_H_E4, BIT(5)), + PINCTRL_CONF_DESC(51, REG_GPIO_H_E4, BIT(6)), + PINCTRL_CONF_DESC(52, REG_GPIO_H_E4, BIT(7)), + PINCTRL_CONF_DESC(53, REG_GPIO_H_E4, BIT(8)), + PINCTRL_CONF_DESC(54, REG_GPIO_H_E4, BIT(9)), + PINCTRL_CONF_DESC(55, REG_GPIO_H_E4, BIT(10)), + PINCTRL_CONF_DESC(56, REG_GPIO_H_E4, BIT(11)), + PINCTRL_CONF_DESC(57, REG_GPIO_H_E4, BIT(12)), + PINCTRL_CONF_DESC(58, REG_GPIO_H_E4, BIT(13)), + PINCTRL_CONF_DESC(59, REG_GPIO_H_E4, BIT(14)), + PINCTRL_CONF_DESC(61, REG_I2C_SDA_E4, PCIE0_RESET_E4_MASK), + PINCTRL_CONF_DESC(62, REG_I2C_SDA_E4, PCIE1_RESET_E4_MASK), + PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK), +}; + +static const struct airoha_pinctrl_conf airoha_pinctrl_pcie_rst_od_conf[] = { + PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), + PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), + PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK), +}; + +static int airoha_convert_pin_to_reg_offset(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + int pin) +{ + if (!range) + range = pinctrl_find_gpio_range_from_pin_nolock(pctrl_dev, + pin); + if (!range) + return -EINVAL; + + return pin - range->pin_base; +} + +/* gpio callbacks */ +static void airoha_gpio_set(struct gpio_chip *chip, unsigned int gpio, + int value) +{ + struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); + u32 offset = gpio % AIROHA_PIN_BANK_SIZE; + u8 index = gpio / AIROHA_PIN_BANK_SIZE; + + regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.data[index], + BIT(offset), value ? BIT(offset) : 0); +} + +static int airoha_gpio_get(struct gpio_chip *chip, unsigned int gpio) +{ + struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); + u32 val, pin = gpio % AIROHA_PIN_BANK_SIZE; + u8 index = gpio / AIROHA_PIN_BANK_SIZE; + int err; + + err = regmap_read(pinctrl->regmap, + pinctrl->gpiochip.data[index], &val); + + return err ? err : !!(val & BIT(pin)); +} + +static int airoha_gpio_direction_output(struct gpio_chip *chip, + unsigned int gpio, int value) +{ + int err; + + err = pinctrl_gpio_direction_output(chip, gpio); + if (err) + return err; + + airoha_gpio_set(chip, gpio, value); + + return 0; +} + +/* irq callbacks */ +static void airoha_irq_unmask(struct irq_data *data) +{ + u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; + u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; + u32 mask = GENMASK(2 * offset + 1, 2 * offset); + struct airoha_pinctrl_gpiochip *gpiochip; + struct airoha_pinctrl *pinctrl; + u32 val = BIT(2 * offset); + + gpiochip = irq_data_get_irq_chip_data(data); + if (WARN_ON_ONCE(data->hwirq >= ARRAY_SIZE(gpiochip->irq_type))) + return; + + pinctrl = container_of(gpiochip, struct airoha_pinctrl, gpiochip); + switch (gpiochip->irq_type[data->hwirq]) { + case IRQ_TYPE_LEVEL_LOW: + val = val << 1; + fallthrough; + case IRQ_TYPE_LEVEL_HIGH: + regmap_update_bits(pinctrl->regmap, gpiochip->level[index], + mask, val); + break; + case IRQ_TYPE_EDGE_FALLING: + val = val << 1; + fallthrough; + case IRQ_TYPE_EDGE_RISING: + regmap_update_bits(pinctrl->regmap, gpiochip->edge[index], + mask, val); + break; + case IRQ_TYPE_EDGE_BOTH: + regmap_set_bits(pinctrl->regmap, gpiochip->edge[index], mask); + break; + default: + break; + } +} + +static void airoha_irq_mask(struct irq_data *data) +{ + u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; + u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; + u32 mask = GENMASK(2 * offset + 1, 2 * offset); + struct airoha_pinctrl_gpiochip *gpiochip; + struct airoha_pinctrl *pinctrl; + + gpiochip = irq_data_get_irq_chip_data(data); + pinctrl = container_of(gpiochip, struct airoha_pinctrl, gpiochip); + + regmap_clear_bits(pinctrl->regmap, gpiochip->level[index], mask); + regmap_clear_bits(pinctrl->regmap, gpiochip->edge[index], mask); +} + +static int airoha_irq_type(struct irq_data *data, unsigned int type) +{ + struct airoha_pinctrl_gpiochip *gpiochip; + + gpiochip = irq_data_get_irq_chip_data(data); + if (data->hwirq >= ARRAY_SIZE(gpiochip->irq_type)) + return -EINVAL; + + if (type == IRQ_TYPE_PROBE) { + if (gpiochip->irq_type[data->hwirq]) + return 0; + + type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; + } + gpiochip->irq_type[data->hwirq] = type & IRQ_TYPE_SENSE_MASK; + + return 0; +} + +static irqreturn_t airoha_irq_handler(int irq, void *data) +{ + struct airoha_pinctrl *pinctrl = data; + bool handled = false; + int i; + + for (i = 0; i < ARRAY_SIZE(irq_status_regs); i++) { + struct gpio_irq_chip *girq = &pinctrl->gpiochip.chip.irq; + u32 regmap; + unsigned long status; + int irq; + + if (regmap_read(pinctrl->regmap, pinctrl->gpiochip.status[i], + ®map)) + continue; + + status = regmap; + for_each_set_bit(irq, &status, AIROHA_PIN_BANK_SIZE) { + u32 offset = irq + i * AIROHA_PIN_BANK_SIZE; + + generic_handle_irq(irq_find_mapping(girq->domain, + offset)); + regmap_write(pinctrl->regmap, + pinctrl->gpiochip.status[i], BIT(irq)); + } + handled |= !!status; + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct irq_chip airoha_gpio_irq_chip = { + .name = "airoha-gpio-irq", + .irq_unmask = airoha_irq_unmask, + .irq_mask = airoha_irq_mask, + .irq_mask_ack = airoha_irq_mask, + .irq_set_type = airoha_irq_type, + .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_IMMUTABLE, +}; + +static int airoha_pinctrl_add_gpiochip(struct airoha_pinctrl *pinctrl, + struct platform_device *pdev) +{ + struct airoha_pinctrl_gpiochip *chip = &pinctrl->gpiochip; + struct gpio_chip *gc = &chip->chip; + struct gpio_irq_chip *girq = &gc->irq; + struct device *dev = &pdev->dev; + int irq, err; + + chip->data = gpio_data_regs; + chip->dir = gpio_dir_regs; + chip->out = gpio_out_regs; + chip->status = irq_status_regs; + chip->level = irq_level_regs; + chip->edge = irq_edge_regs; + + gc->parent = dev; + gc->label = dev_name(dev); + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; + gc->direction_input = pinctrl_gpio_direction_input; + gc->direction_output = airoha_gpio_direction_output; + gc->set = airoha_gpio_set; + gc->get = airoha_gpio_get; + gc->base = -1; + gc->ngpio = AIROHA_NUM_PINS; + + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + gpio_irq_chip_set_chip(girq, &airoha_gpio_irq_chip); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_irq(dev, irq, airoha_irq_handler, IRQF_SHARED, + dev_name(dev), pinctrl); + if (err) { + dev_err(dev, "error requesting irq %d: %d\n", irq, err); + return err; + } + + return devm_gpiochip_add_data(dev, gc, pinctrl); +} + +/* pinmux callbacks */ +static int airoha_pinmux_set_mux(struct pinctrl_dev *pctrl_dev, + unsigned int selector, + unsigned int group) +{ + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct airoha_pinctrl_func *func; + struct function_desc *desc; + struct group_desc *grp; + int i; + + desc = pinmux_generic_get_function(pctrl_dev, selector); + if (!desc) + return -EINVAL; + + grp = pinctrl_generic_get_group(pctrl_dev, group); + if (!grp) + return -EINVAL; + + dev_dbg(pctrl_dev->dev, "enable function %s group %s\n", + desc->func.name, grp->grp.name); + + func = desc->data; + for (i = 0; i < func->group_size; i++) { + const struct airoha_pinctrl_func_group *group; + int j; + + group = &func->groups[i]; + if (strcmp(group->name, grp->grp.name)) + continue; + + for (j = 0; j < group->regmap_size; j++) { + switch (group->regmap[j].mux) { + case AIROHA_FUNC_PWM_EXT_MUX: + case AIROHA_FUNC_PWM_MUX: + regmap_update_bits(pinctrl->regmap, + group->regmap[j].offset, + group->regmap[j].mask, + group->regmap[j].val); + break; + default: + regmap_update_bits(pinctrl->chip_scu, + group->regmap[j].offset, + group->regmap[j].mask, + group->regmap[j].val); + break; + } + } + return 0; + } + + return -EINVAL; +} + +static int airoha_pinmux_set_direction(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned int p, bool input) +{ + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + u32 mask, index; + int err, pin; + + pin = airoha_convert_pin_to_reg_offset(pctrl_dev, range, p); + if (pin < 0) + return pin; + + /* set output enable */ + mask = BIT(pin % AIROHA_PIN_BANK_SIZE); + index = pin / AIROHA_PIN_BANK_SIZE; + err = regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.out[index], + mask, !input ? mask : 0); + if (err) + return err; + + /* set direction */ + mask = BIT(2 * (pin % AIROHA_REG_GPIOCTRL_NUM_PIN)); + index = pin / AIROHA_REG_GPIOCTRL_NUM_PIN; + return regmap_update_bits(pinctrl->regmap, + pinctrl->gpiochip.dir[index], mask, + !input ? mask : 0); +} + +static const struct pinmux_ops airoha_pmxops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .gpio_set_direction = airoha_pinmux_set_direction, + .set_mux = airoha_pinmux_set_mux, + .strict = true, +}; + +/* pinconf callbacks */ +static const struct airoha_pinctrl_reg * +airoha_pinctrl_get_conf_reg(const struct airoha_pinctrl_conf *conf, + int conf_size, int pin) +{ + int i; + + for (i = 0; i < conf_size; i++) { + if (conf[i].pin == pin) + return &conf[i].reg; + } + + return NULL; +} + +static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, + const struct airoha_pinctrl_conf *conf, + int conf_size, int pin, u32 *val) +{ + const struct airoha_pinctrl_reg *reg; + + reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); + if (!reg) + return -EINVAL; + + if (regmap_read(pinctrl->chip_scu, reg->offset, val)) + return -EINVAL; + + *val = (*val & reg->mask) >> __ffs(reg->mask); + + return 0; +} + +static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, + const struct airoha_pinctrl_conf *conf, + int conf_size, int pin, u32 val) +{ + const struct airoha_pinctrl_reg *reg = NULL; + + reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); + if (!reg) + return -EINVAL; + + + if (regmap_update_bits(pinctrl->chip_scu, reg->offset, reg->mask, + val << __ffs(reg->mask))) + return -EINVAL; + + return 0; +} + +#define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val) \ + airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pullup_conf, \ + ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ + (pin), (val)) +#define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val) \ + airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ + ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ + (pin), (val)) +#define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val) \ + airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ + ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ + (pin), (val)) +#define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val) \ + airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ + ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ + (pin), (val)) +#define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val) \ + airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ + ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ + (pin), (val)) +#define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val) \ + airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pullup_conf, \ + ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ + (pin), (val)) +#define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val) \ + airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ + ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ + (pin), (val)) +#define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val) \ + airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ + ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ + (pin), (val)) +#define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val) \ + airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ + ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ + (pin), (val)) +#define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val) \ + airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ + ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ + (pin), (val)) + +static int airoha_pinconf_get_direction(struct pinctrl_dev *pctrl_dev, u32 p) +{ + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + u32 val, mask; + int err, pin; + u8 index; + + pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); + if (pin < 0) + return pin; + + index = pin / AIROHA_REG_GPIOCTRL_NUM_PIN; + err = regmap_read(pinctrl->regmap, pinctrl->gpiochip.dir[index], &val); + if (err) + return err; + + mask = BIT(2 * (pin % AIROHA_REG_GPIOCTRL_NUM_PIN)); + return val & mask ? PIN_CONFIG_OUTPUT_ENABLE : PIN_CONFIG_INPUT_ENABLE; +} + +static int airoha_pinconf_get(struct pinctrl_dev *pctrl_dev, + unsigned int pin, unsigned long *config) +{ + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 arg; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: { + u32 pull_up, pull_down; + + if (airoha_pinctrl_get_pullup_conf(pinctrl, pin, &pull_up) || + airoha_pinctrl_get_pulldown_conf(pinctrl, pin, &pull_down)) + return -EINVAL; + + if (param == PIN_CONFIG_BIAS_PULL_UP && + !(pull_up && !pull_down)) + return -EINVAL; + else if (param == PIN_CONFIG_BIAS_PULL_DOWN && + !(pull_down && !pull_up)) + return -EINVAL; + else if (pull_up || pull_down) + return -EINVAL; + + arg = 1; + break; + } + case PIN_CONFIG_DRIVE_STRENGTH: { + u32 e2, e4; + + if (airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, &e2) || + airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, &e4)) + return -EINVAL; + + arg = e4 << 1 | e2; + break; + } + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, &arg)) + return -EINVAL; + break; + case PIN_CONFIG_OUTPUT_ENABLE: + case PIN_CONFIG_INPUT_ENABLE: + arg = airoha_pinconf_get_direction(pctrl_dev, pin); + if (arg != param) + return -EINVAL; + + arg = 1; + break; + default: + return -EOPNOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int airoha_pinconf_set_pin_value(struct pinctrl_dev *pctrl_dev, + unsigned int p, bool value) +{ + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + int pin; + + pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); + if (pin < 0) + return pin; + + airoha_gpio_set(&pinctrl->gpiochip.chip, pin, value); + + return 0; +} + +static int airoha_pinconf_set(struct pinctrl_dev *pctrl_dev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + int i; + + for (i = 0; i < num_configs; i++) { + u32 param = pinconf_to_config_param(configs[i]); + u32 arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); + airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); + break; + case PIN_CONFIG_BIAS_PULL_UP: + airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); + airoha_pinctrl_set_pullup_conf(pinctrl, pin, 1); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 1); + airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); + break; + case PIN_CONFIG_DRIVE_STRENGTH: { + u32 e2 = 0, e4 = 0; + + switch (arg) { + case MTK_DRIVE_2mA: + break; + case MTK_DRIVE_4mA: + e2 = 1; + break; + case MTK_DRIVE_6mA: + e4 = 1; + break; + case MTK_DRIVE_8mA: + e2 = 1; + e4 = 1; + break; + default: + return -EINVAL; + } + + airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, e2); + airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, e4); + break; + } + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, !!arg); + break; + case PIN_CONFIG_OUTPUT_ENABLE: + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: { + bool input = param == PIN_CONFIG_INPUT_ENABLE; + int err; + + err = airoha_pinmux_set_direction(pctrl_dev, NULL, pin, + input); + if (err) + return err; + + if (param == PIN_CONFIG_OUTPUT) { + err = airoha_pinconf_set_pin_value(pctrl_dev, + pin, !!arg); + if (err) + return err; + } + break; + } + default: + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, + unsigned int group, unsigned long *config) +{ + u32 cur_config = 0; + int i; + + for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { + if (airoha_pinconf_get(pctrl_dev, + airoha_pinctrl_groups[group].pins[i], + config)) + return -EOPNOTSUPP; + + if (i && cur_config != *config) + return -EOPNOTSUPP; + + cur_config = *config; + } + + return 0; +} + +static int airoha_pinconf_group_set(struct pinctrl_dev *pctrl_dev, + unsigned int group, unsigned long *configs, + unsigned int num_configs) +{ + int i; + + for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { + int err; + + err = airoha_pinconf_set(pctrl_dev, + airoha_pinctrl_groups[group].pins[i], + configs, num_configs); + if (err) + return err; + } + + return 0; +} + +static const struct pinconf_ops airoha_confops = { + .is_generic = true, + .pin_config_get = airoha_pinconf_get, + .pin_config_set = airoha_pinconf_set, + .pin_config_group_get = airoha_pinconf_group_get, + .pin_config_group_set = airoha_pinconf_group_set, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +static const struct pinctrl_ops airoha_pctlops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static struct pinctrl_desc airoha_pinctrl_desc = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pctlops = &airoha_pctlops, + .pmxops = &airoha_pmxops, + .confops = &airoha_confops, + .pins = airoha_pinctrl_pins, + .npins = ARRAY_SIZE(airoha_pinctrl_pins), +}; + +static int airoha_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct airoha_pinctrl *pinctrl; + struct regmap *map; + int err, i; + + pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + + pinctrl->regmap = device_node_to_regmap(dev->parent->of_node); + if (IS_ERR(pinctrl->regmap)) + return PTR_ERR(pinctrl->regmap); + + map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) + return PTR_ERR(map); + + pinctrl->chip_scu = map; + + err = devm_pinctrl_register_and_init(dev, &airoha_pinctrl_desc, + pinctrl, &pinctrl->ctrl); + if (err) + return err; + + /* build pin groups */ + for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_groups); i++) { + const struct pingroup *grp = &airoha_pinctrl_groups[i]; + + err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name, + grp->pins, grp->npins, + (void *)grp); + if (err < 0) { + dev_err(&pdev->dev, "Failed to register group %s\n", + grp->name); + return err; + } + } + + /* build functions */ + for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_funcs); i++) { + const struct airoha_pinctrl_func *func; + + func = &airoha_pinctrl_funcs[i]; + err = pinmux_generic_add_function(pinctrl->ctrl, + func->desc.func.name, + func->desc.func.groups, + func->desc.func.ngroups, + (void *)func); + if (err < 0) { + dev_err(dev, "Failed to register function %s\n", + func->desc.func.name); + return err; + } + } + + err = pinctrl_enable(pinctrl->ctrl); + if (err) + return err; + + /* build gpio-chip */ + return airoha_pinctrl_add_gpiochip(pinctrl, pdev); +} + +static const struct of_device_id airoha_pinctrl_of_match[] = { + { .compatible = "airoha,en7581-pinctrl" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, airoha_pinctrl_of_match); + +static struct platform_driver airoha_pinctrl_driver = { + .probe = airoha_pinctrl_probe, + .driver = { + .name = "pinctrl-airoha", + .of_match_table = airoha_pinctrl_of_match, + }, +}; +module_platform_driver(airoha_pinctrl_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Benjamin Larsson "); +MODULE_AUTHOR("Markus Gothe "); +MODULE_DESCRIPTION("Pinctrl driver for Airoha SoC"); diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 4c4ada06423d7..335744ac83105 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -734,7 +734,7 @@ static void armada_37xx_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *chip = irq_data_get_irq_chip_data(d); struct armada_37xx_pinctrl *info = gpiochip_get_data(chip); - seq_printf(p, info->data->name); + seq_puts(p, info->data->name); } static const struct irq_chip armada_37xx_irqchip = { diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c index 68750b6f8e57a..4ce2e35a63733 100644 --- a/drivers/pinctrl/nomadik/pinctrl-abx500.c +++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c @@ -1089,7 +1089,7 @@ static struct platform_driver abx500_gpio_driver = { .of_match_table = abx500_gpio_match, }, .probe = abx500_gpio_probe, - .remove_new = abx500_gpio_remove, + .remove = abx500_gpio_remove, }; static int __init abx500_gpio_init(void) diff --git a/drivers/pinctrl/nxp/pinctrl-s32g2.c b/drivers/pinctrl/nxp/pinctrl-s32g2.c index 440ff18794249..c49d28793b696 100644 --- a/drivers/pinctrl/nxp/pinctrl-s32g2.c +++ b/drivers/pinctrl/nxp/pinctrl-s32g2.c @@ -216,6 +216,12 @@ enum s32_pins { S32G_IMCR_CAN1_RXD = 631, S32G_IMCR_CAN2_RXD = 632, S32G_IMCR_CAN3_RXD = 633, + + /* JTAG IMCRs */ + S32G_IMCR_JTAG_TMS = 562, + S32G_IMCR_JTAG_TCK = 572, + S32G_IMCR_JTAG_TDI = 573, + /* GMAC0 */ S32G_IMCR_Ethernet_MDIO = 527, S32G_IMCR_Ethernet_CRS = 526, @@ -229,7 +235,21 @@ enum s32_pins { S32G_IMCR_Ethernet_RX_DV = 530, S32G_IMCR_Ethernet_TX_CLK = 538, S32G_IMCR_Ethernet_REF_CLK = 535, + /* PFE EMAC 0 MII */ + S32G_IMCR_PFE_EMAC_0_MDIO = 837, + S32G_IMCR_PFE_EMAC_0_CRS = 836, + S32G_IMCR_PFE_EMAC_0_COL = 835, + S32G_IMCR_PFE_EMAC_0_RX_D0 = 841, + S32G_IMCR_PFE_EMAC_0_RX_D1 = 842, + S32G_IMCR_PFE_EMAC_0_RX_D2 = 843, + S32G_IMCR_PFE_EMAC_0_RX_D3 = 844, + S32G_IMCR_PFE_EMAC_0_RX_ER = 840, + S32G_IMCR_PFE_EMAC_0_RX_CLK = 839, + S32G_IMCR_PFE_EMAC_0_RX_DV = 845, + S32G_IMCR_PFE_EMAC_0_TX_CLK = 846, + S32G_IMCR_PFE_EMAC_0_REF_CLK = 838, + /* PFE EMAC 1 MII */ S32G_IMCR_PFE_EMAC_1_MDIO = 857, S32G_IMCR_PFE_EMAC_1_CRS = 856, @@ -317,6 +337,13 @@ enum s32_pins { S32G_IMCR_LLCE_CAN13_RXD = 758, S32G_IMCR_LLCE_CAN14_RXD = 759, S32G_IMCR_LLCE_CAN15_RXD = 760, + S32G_IMCR_LLCE_UART0_RXD = 790, + S32G_IMCR_LLCE_UART1_RXD = 791, + S32G_IMCR_LLCE_UART2_RXD = 792, + S32G_IMCR_LLCE_UART3_RXD = 793, + S32G_IMCR_LLCE_LPSPI2_PCS0 = 811, + S32G_IMCR_LLCE_LPSPI2_SCK = 816, + S32G_IMCR_LLCE_LPSPI2_SIN = 817, S32G_IMCR_USB_CLK = 895, S32G_IMCR_USB_DATA0 = 896, S32G_IMCR_USB_DATA1 = 897, @@ -503,6 +530,12 @@ static const struct pinctrl_pin_desc s32_pinctrl_pads_siul2[] = { S32_PINCTRL_PIN(S32G_IMCR_USDHC_DAT7), S32_PINCTRL_PIN(S32G_IMCR_USDHC_DQS), S32_PINCTRL_PIN(S32G_IMCR_CAN0_RXD), + + /* JTAG IMCRs */ + S32_PINCTRL_PIN(S32G_IMCR_JTAG_TMS), + S32_PINCTRL_PIN(S32G_IMCR_JTAG_TCK), + S32_PINCTRL_PIN(S32G_IMCR_JTAG_TDI), + /* GMAC0 */ S32_PINCTRL_PIN(S32G_IMCR_Ethernet_MDIO), S32_PINCTRL_PIN(S32G_IMCR_Ethernet_CRS), @@ -638,6 +671,13 @@ static const struct pinctrl_pin_desc s32_pinctrl_pads_siul2[] = { S32_PINCTRL_PIN(S32G_IMCR_LLCE_CAN13_RXD), S32_PINCTRL_PIN(S32G_IMCR_LLCE_CAN14_RXD), S32_PINCTRL_PIN(S32G_IMCR_LLCE_CAN15_RXD), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_UART0_RXD), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_UART1_RXD), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_UART2_RXD), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_UART3_RXD), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_LPSPI2_PCS0), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_LPSPI2_SCK), + S32_PINCTRL_PIN(S32G_IMCR_LLCE_LPSPI2_SIN), S32_PINCTRL_PIN(S32G_IMCR_CAN1_RXD), S32_PINCTRL_PIN(S32G_IMCR_CAN2_RXD), S32_PINCTRL_PIN(S32G_IMCR_CAN3_RXD), @@ -652,6 +692,18 @@ static const struct pinctrl_pin_desc s32_pinctrl_pads_siul2[] = { S32_PINCTRL_PIN(S32G_IMCR_USB_DATA7), S32_PINCTRL_PIN(S32G_IMCR_USB_DIR), S32_PINCTRL_PIN(S32G_IMCR_USB_NXT), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_MDIO), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_CRS), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_COL), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_D0), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_D1), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_D2), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_D3), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_ER), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_CLK), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_RX_DV), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_TX_CLK), + S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_0_REF_CLK), S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_1_MDIO), S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_1_CRS), S32_PINCTRL_PIN(S32G_IMCR_PFE_EMAC_1_COL), diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 7f66ec73199a9..fff6d4209ad57 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -506,7 +506,7 @@ static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type) case IRQ_TYPE_EDGE_BOTH: pin_reg &= ~BIT(LEVEL_TRIG_OFF); pin_reg &= ~(ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF); - pin_reg |= BOTH_EADGE << ACTIVE_LEVEL_OFF; + pin_reg |= BOTH_EDGES << ACTIVE_LEVEL_OFF; irq_set_handler_locked(d, handle_edge_irq); break; @@ -1204,7 +1204,7 @@ static struct platform_driver amd_gpio_driver = { #endif }, .probe = amd_gpio_probe, - .remove_new = amd_gpio_remove, + .remove = amd_gpio_remove, }; module_platform_driver(amd_gpio_driver); diff --git a/drivers/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h index cf59089f27763..667be49c3f48d 100644 --- a/drivers/pinctrl/pinctrl-amd.h +++ b/drivers/pinctrl/pinctrl-amd.h @@ -60,12 +60,12 @@ #define DB_TYPE_PRESERVE_HIGH_GLITCH 0x2UL #define DB_TYPE_REMOVE_GLITCH 0x3UL -#define EDGE_TRAGGER 0x0UL +#define EDGE_TRIGGER 0x0UL #define LEVEL_TRIGGER 0x1UL #define ACTIVE_HIGH 0x0UL #define ACTIVE_LOW 0x1UL -#define BOTH_EADGE 0x2UL +#define BOTH_EDGES 0x2UL #define ENABLE_INTERRUPT 0x1UL #define DISABLE_INTERRUPT 0x0UL diff --git a/drivers/pinctrl/pinctrl-artpec6.c b/drivers/pinctrl/pinctrl-artpec6.c index dd93f124e0a09..717f9592b28b5 100644 --- a/drivers/pinctrl/pinctrl-artpec6.c +++ b/drivers/pinctrl/pinctrl-artpec6.c @@ -988,7 +988,7 @@ static struct platform_driver artpec6_pmx_driver = { .of_match_table = artpec6_pinctrl_match, }, .probe = artpec6_pmx_probe, - .remove_new = artpec6_pmx_remove, + .remove = artpec6_pmx_remove, }; static int __init artpec6_pmx_init(void) diff --git a/drivers/pinctrl/pinctrl-aw9523.c b/drivers/pinctrl/pinctrl-aw9523.c index 1374f30166bc3..9bf53de20be87 100644 --- a/drivers/pinctrl/pinctrl-aw9523.c +++ b/drivers/pinctrl/pinctrl-aw9523.c @@ -80,7 +80,7 @@ struct aw9523 { struct regmap *regmap; struct mutex i2c_lock; struct gpio_desc *reset_gpio; - struct regulator *vio_vreg; + int vio_vreg; struct pinctrl_dev *pctl; struct gpio_chip gpio; struct aw9523_irq *irq; @@ -550,10 +550,10 @@ static int aw9523_gpio_get(struct gpio_chip *chip, unsigned int offset) /** * _aw9523_gpio_get_multiple - Get I/O state for an entire port - * @regmap: Regmap structure - * @pin: gpiolib pin number + * @awi: Controller data * @regbit: hw pin index, used to retrieve port number * @state: returned port I/O state + * @mask: lines to read values for * * Return: Zero for success or negative number for error */ @@ -972,29 +972,23 @@ static int aw9523_probe(struct i2c_client *client) if (IS_ERR(awi->regmap)) return PTR_ERR(awi->regmap); - awi->vio_vreg = devm_regulator_get_optional(dev, "vio"); - if (IS_ERR(awi->vio_vreg)) { - if (PTR_ERR(awi->vio_vreg) == -EPROBE_DEFER) - return -EPROBE_DEFER; - awi->vio_vreg = NULL; - } else { - ret = regulator_enable(awi->vio_vreg); - if (ret) - return ret; - } + awi->vio_vreg = devm_regulator_get_enable_optional(dev, "vio"); + if (awi->vio_vreg && awi->vio_vreg != -ENODEV) + return awi->vio_vreg; + + ret = devm_mutex_init(dev, &awi->i2c_lock); + if (ret) + return ret; - mutex_init(&awi->i2c_lock); lockdep_set_subclass(&awi->i2c_lock, i2c_adapter_depth(client->adapter)); pdesc = devm_kzalloc(dev, sizeof(*pdesc), GFP_KERNEL); - if (!pdesc) { - ret = -ENOMEM; - goto err_disable_vregs; - } + if (!pdesc) + return -ENOMEM; ret = aw9523_hw_init(awi); if (ret) - goto err_disable_vregs; + return ret; pdesc->name = dev_name(dev); pdesc->owner = THIS_MODULE; @@ -1006,31 +1000,20 @@ static int aw9523_probe(struct i2c_client *client) ret = aw9523_init_gpiochip(awi, pdesc->npins); if (ret) - goto err_disable_vregs; + return ret; if (client->irq) { ret = aw9523_init_irq(awi, client->irq); if (ret) - goto err_disable_vregs; + return ret; } awi->pctl = devm_pinctrl_register(dev, pdesc, awi); - if (IS_ERR(awi->pctl)) { - ret = dev_err_probe(dev, PTR_ERR(awi->pctl), "Cannot register pinctrl"); - goto err_disable_vregs; - } - - ret = devm_gpiochip_add_data(dev, &awi->gpio, awi); - if (ret) - goto err_disable_vregs; - - return ret; + if (IS_ERR(awi->pctl)) + return dev_err_probe(dev, PTR_ERR(awi->pctl), + "Cannot register pinctrl"); -err_disable_vregs: - if (awi->vio_vreg) - regulator_disable(awi->vio_vreg); - mutex_destroy(&awi->i2c_lock); - return ret; + return devm_gpiochip_add_data(dev, &awi->gpio, awi); } static void aw9523_remove(struct i2c_client *client) @@ -1043,19 +1026,15 @@ static void aw9523_remove(struct i2c_client *client) * set the pins to hardware defaults before removing the driver * to leave it in a clean, safe and predictable state. */ - if (awi->vio_vreg) { - regulator_disable(awi->vio_vreg); - } else { + if (awi->vio_vreg == -ENODEV) { mutex_lock(&awi->i2c_lock); aw9523_hw_init(awi); mutex_unlock(&awi->i2c_lock); } - - mutex_destroy(&awi->i2c_lock); } static const struct i2c_device_id aw9523_i2c_id_table[] = { - { "aw9523_i2c", 0 }, + { "aw9523_i2c" }, { } }; MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_table); diff --git a/drivers/pinctrl/pinctrl-cy8c95x0.c b/drivers/pinctrl/pinctrl-cy8c95x0.c index 5096ccdd459ea..0d6c2027d4c18 100644 --- a/drivers/pinctrl/pinctrl-cy8c95x0.c +++ b/drivers/pinctrl/pinctrl-cy8c95x0.c @@ -141,7 +141,6 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_irq_info[] = { * @nport: Number of Gports in this chip * @gpio_chip: gpiolib chip * @driver_data: private driver data - * @regulator: Pointer to the regulator for the IC * @dev: struct device * @pctldev: pin controller device * @pinctrl_desc: pin controller description @@ -160,10 +159,9 @@ struct cy8c95x0_pinctrl { DECLARE_BITMAP(irq_trig_high, MAX_LINE); DECLARE_BITMAP(push_pull, MAX_LINE); DECLARE_BITMAP(shiftmask, MAX_LINE); - int nport; + unsigned int nport; struct gpio_chip gpio_chip; unsigned long driver_data; - struct regulator *regulator; struct device *dev; struct pinctrl_dev *pctldev; struct pinctrl_desc pinctrl_desc; @@ -612,9 +610,8 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, DECLARE_BITMAP(tmask, MAX_LINE); DECLARE_BITMAP(tval, MAX_LINE); int write_val; - int ret = 0; - int i; u8 bits; + int ret; /* Add the 4 bit gap of Gport2 */ bitmap_andnot(tmask, mask, chip->shiftmask, MAX_LINE); @@ -625,7 +622,7 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, bitmap_shift_left(tval, tval, 4, MAX_LINE); bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3); - for (i = 0; i < chip->nport; i++) { + for (unsigned int i = 0; i < chip->nport; i++) { /* Skip over unused banks */ bits = bitmap_get_value8(tmask, i * BANK_SZ); if (!bits) @@ -634,15 +631,13 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, write_val = bitmap_get_value8(tval, i * BANK_SZ); ret = cy8c95x0_regmap_update_bits(chip, reg, i, bits, write_val); - if (ret < 0) - goto out; + if (ret < 0) { + dev_err(chip->dev, "failed writing register %d, port %u: err %d\n", reg, i, ret); + return ret; + } } -out: - if (ret < 0) - dev_err(chip->dev, "failed writing register %d, port %d: err %d\n", reg, i, ret); - - return ret; + return 0; } static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, @@ -652,9 +647,8 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, DECLARE_BITMAP(tval, MAX_LINE); DECLARE_BITMAP(tmp, MAX_LINE); int read_val; - int ret = 0; - int i; u8 bits; + int ret; /* Add the 4 bit gap of Gport2 */ bitmap_andnot(tmask, mask, chip->shiftmask, MAX_LINE); @@ -665,15 +659,17 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, bitmap_shift_left(tval, tval, 4, MAX_LINE); bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3); - for (i = 0; i < chip->nport; i++) { + for (unsigned int i = 0; i < chip->nport; i++) { /* Skip over unused banks */ bits = bitmap_get_value8(tmask, i * BANK_SZ); if (!bits) continue; ret = cy8c95x0_regmap_read(chip, reg, i, &read_val); - if (ret < 0) - goto out; + if (ret < 0) { + dev_err(chip->dev, "failed reading register %d, port %u: err %d\n", reg, i, ret); + return ret; + } read_val &= bits; read_val |= bitmap_get_value8(tval, i * BANK_SZ) & ~bits; @@ -684,11 +680,7 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, bitmap_shift_right(tmp, tval, 4, MAX_LINE); bitmap_replace(val, tmp, tval, chip->shiftmask, MAX_LINE); -out: - if (ret < 0) - dev_err(chip->dev, "failed reading register %d, port %d: err %d\n", reg, i, ret); - - return ret; + return 0; } static int cy8c95x0_gpio_direction_input(struct gpio_chip *gc, unsigned int off) @@ -754,14 +746,12 @@ static int cy8c95x0_gpio_get_direction(struct gpio_chip *gc, unsigned int off) ret = cy8c95x0_regmap_read(chip, CY8C95X0_DIRECTION, port, ®_val); if (ret < 0) - goto out; + return ret; if (reg_val & bit) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; -out: - return ret; } static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, @@ -823,8 +813,7 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, case PIN_CONFIG_SLEEP_HARDWARE_STATE: case PIN_CONFIG_SLEW_RATE: default: - ret = -ENOTSUPP; - goto out; + return -ENOTSUPP; } /* * Writing 1 to one of the drive mode registers will automatically @@ -832,7 +821,7 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, */ ret = cy8c95x0_regmap_read(chip, reg, port, ®_val); if (ret < 0) - goto out; + return ret; if (reg_val & bit) arg = 1; @@ -840,8 +829,7 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, arg = !arg; *config = pinconf_to_config_packed(param, (u16)arg); -out: - return ret; + return 0; } static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip, @@ -853,7 +841,6 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip, unsigned long param = pinconf_to_config_param(config); unsigned long arg = pinconf_to_config_argument(config); unsigned int reg; - int ret; switch (param) { case PIN_CONFIG_BIAS_PULL_UP: @@ -884,22 +871,17 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip, reg = CY8C95X0_PWMSEL; break; case PIN_CONFIG_OUTPUT_ENABLE: - ret = cy8c95x0_pinmux_direction(chip, off, !arg); - goto out; + return cy8c95x0_pinmux_direction(chip, off, !arg); case PIN_CONFIG_INPUT_ENABLE: - ret = cy8c95x0_pinmux_direction(chip, off, arg); - goto out; + return cy8c95x0_pinmux_direction(chip, off, arg); default: - ret = -ENOTSUPP; - goto out; + return -ENOTSUPP; } /* * Writing 1 to one of the drive mode registers will automatically * clear conflicting set bits in the other drive mode registers. */ - ret = cy8c95x0_regmap_write_bits(chip, reg, port, bit, bit); -out: - return ret; + return cy8c95x0_regmap_write_bits(chip, reg, port, bit, bit); } static int cy8c95x0_gpio_get_multiple(struct gpio_chip *gc, @@ -1424,32 +1406,30 @@ static int cy8c95x0_detect(struct i2c_client *client, } dev_info(&client->dev, "Found a %s chip at 0x%02x.\n", name, client->addr); - strscpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name); return 0; } static int cy8c95x0_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct cy8c95x0_pinctrl *chip; struct regmap_config regmap_conf; struct regmap_range_cfg regmap_range_conf; - struct regulator *reg; int ret; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; - chip->dev = &client->dev; + chip->dev = dev; /* Set the device type */ chip->driver_data = (uintptr_t)i2c_get_match_data(client); if (!chip->driver_data) return -ENODEV; - i2c_set_clientdata(client, chip); - chip->tpin = chip->driver_data & CY8C95X0_GPIO_MASK; chip->nport = DIV_ROUND_UP(CY8C95X0_PIN_TO_OFFSET(chip->tpin), BANK_SZ); @@ -1457,46 +1437,34 @@ static int cy8c95x0_probe(struct i2c_client *client) switch (chip->tpin) { case 20: - strscpy(chip->name, cy8c95x0_id[0].name, I2C_NAME_SIZE); + strscpy(chip->name, cy8c95x0_id[0].name); regmap_range_conf.range_max = CY8C95X0_VIRTUAL + 3 * MUXED_STRIDE; break; case 40: - strscpy(chip->name, cy8c95x0_id[1].name, I2C_NAME_SIZE); + strscpy(chip->name, cy8c95x0_id[1].name); regmap_range_conf.range_max = CY8C95X0_VIRTUAL + 6 * MUXED_STRIDE; break; case 60: - strscpy(chip->name, cy8c95x0_id[2].name, I2C_NAME_SIZE); + strscpy(chip->name, cy8c95x0_id[2].name); regmap_range_conf.range_max = CY8C95X0_VIRTUAL + 8 * MUXED_STRIDE; break; default: return -ENODEV; } - reg = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(reg)) { - if (PTR_ERR(reg) == -EPROBE_DEFER) - return -EPROBE_DEFER; - } else { - ret = regulator_enable(reg); - if (ret) { - dev_err(&client->dev, "failed to enable regulator vdd: %d\n", ret); - return ret; - } - chip->regulator = reg; - } + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable regulator vdd\n"); /* bring the chip out of reset if reset pin is provided */ - chip->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(chip->gpio_reset)) { - ret = dev_err_probe(chip->dev, PTR_ERR(chip->gpio_reset), - "Failed to get GPIO 'reset'\n"); - goto err_exit; - } else if (chip->gpio_reset) { - usleep_range(1000, 2000); + chip->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(chip->gpio_reset)) + return dev_err_probe(dev, PTR_ERR(chip->gpio_reset), "Failed to get GPIO 'reset'\n"); + gpiod_set_consumer_name(chip->gpio_reset, "CY8C95X0 RESET"); + if (chip->gpio_reset) { + fsleep(1000); gpiod_set_value_cansleep(chip->gpio_reset, 0); - usleep_range(250000, 300000); - - gpiod_set_consumer_name(chip->gpio_reset, "CY8C95X0 RESET"); + fsleep(250000); } /* Regmap for direct and paged registers */ @@ -1506,10 +1474,8 @@ static int cy8c95x0_probe(struct i2c_client *client) regmap_conf.num_reg_defaults_raw = regmap_range_conf.range_max; chip->regmap = devm_regmap_init_i2c(client, ®map_conf); - if (IS_ERR(chip->regmap)) { - ret = PTR_ERR(chip->regmap); - goto err_exit; - } + if (IS_ERR(chip->regmap)) + return PTR_ERR(chip->regmap); bitmap_zero(chip->push_pull, MAX_LINE); bitmap_zero(chip->shiftmask, MAX_LINE); @@ -1525,31 +1491,14 @@ static int cy8c95x0_probe(struct i2c_client *client) if (client->irq) { ret = cy8c95x0_irq_setup(chip, client->irq); if (ret) - goto err_exit; + return ret; } ret = cy8c95x0_setup_pinctrl(chip); if (ret) - goto err_exit; - - ret = cy8c95x0_setup_gpiochip(chip); - if (ret) - goto err_exit; - - return 0; - -err_exit: - if (!IS_ERR_OR_NULL(chip->regulator)) - regulator_disable(chip->regulator); - return ret; -} - -static void cy8c95x0_remove(struct i2c_client *client) -{ - struct cy8c95x0_pinctrl *chip = i2c_get_clientdata(client); + return ret; - if (!IS_ERR_OR_NULL(chip->regulator)) - regulator_disable(chip->regulator); + return cy8c95x0_setup_gpiochip(chip); } static const struct acpi_device_id cy8c95x0_acpi_ids[] = { @@ -1565,7 +1514,6 @@ static struct i2c_driver cy8c95x0_driver = { .acpi_match_table = cy8c95x0_acpi_ids, }, .probe = cy8c95x0_probe, - .remove = cy8c95x0_remove, .id_table = cy8c95x0_id, .detect = cy8c95x0_detect, }; diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c index 0f6b55fec31de..eddb01796a83e 100644 --- a/drivers/pinctrl/pinctrl-k210.c +++ b/drivers/pinctrl/pinctrl-k210.c @@ -96,8 +96,6 @@ struct k210_fpioa_data { struct k210_fpioa __iomem *fpioa; struct regmap *sysctl_map; u32 power_offset; - struct clk *clk; - struct clk *pclk; }; #define K210_PIN_NAME(i) ("IO_" #i) @@ -183,7 +181,7 @@ static const u32 k210_pinconf_mode_id_to_mode[] = { [K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU, }; -#undef DEFAULT +#undef K210_PC_DEFAULT /* * Pin functions configuration information. @@ -925,6 +923,7 @@ static int k210_fpioa_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct k210_fpioa_data *pdata; + struct clk *clk, *pclk; dev_info(dev, "K210 FPIOA pin controller\n"); @@ -939,13 +938,13 @@ static int k210_fpioa_probe(struct platform_device *pdev) if (IS_ERR(pdata->fpioa)) return PTR_ERR(pdata->fpioa); - pdata->clk = devm_clk_get_enabled(dev, "ref"); - if (IS_ERR(pdata->clk)) - return PTR_ERR(pdata->clk); + clk = devm_clk_get_enabled(dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); - pdata->pclk = devm_clk_get_optional_enabled(dev, "pclk"); - if (IS_ERR(pdata->pclk)) - return PTR_ERR(pdata->pclk); + pclk = devm_clk_get_optional_enabled(dev, "pclk"); + if (IS_ERR(pclk)) + return PTR_ERR(pclk); pdata->sysctl_map = syscon_regmap_lookup_by_phandle_args(np, diff --git a/drivers/pinctrl/pinctrl-k230.c b/drivers/pinctrl/pinctrl-k230.c new file mode 100644 index 0000000000000..a9b4627b46b01 --- /dev/null +++ b/drivers/pinctrl/pinctrl-k230.c @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/* + * Copyright (C) 2024 Canaan Bright Sight Co. Ltd + * Copyright (C) 2024 Ze Huang <18771902331@163.com> + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" + +#define K230_NPINS 64 + +#define K230_SHIFT_ST (0) +#define K230_SHIFT_DS (1) +#define K230_SHIFT_BIAS (5) +#define K230_SHIFT_PD (5) +#define K230_SHIFT_PU (6) +#define K230_SHIFT_OE (7) +#define K230_SHIFT_IE (8) +#define K230_SHIFT_MSC (9) +#define K230_SHIFT_SL (10) +#define K230_SHIFT_SEL (11) + +#define K230_PC_ST BIT(0) +#define K230_PC_DS GENMASK(4, 1) +#define K230_PC_PD BIT(5) +#define K230_PC_PU BIT(6) +#define K230_PC_BIAS GENMASK(6, 5) +#define K230_PC_OE BIT(7) +#define K230_PC_IE BIT(8) +#define K230_PC_MSC BIT(9) +#define K230_PC_SL BIT(10) +#define K230_PC_SEL GENMASK(13, 11) + +struct k230_pin_conf { + unsigned int func; + unsigned long *configs; + unsigned int nconfigs; +}; + +struct k230_pin_group { + const char *name; + unsigned int *pins; + unsigned int num_pins; + + struct k230_pin_conf *data; +}; + +struct k230_pmx_func { + const char *name; + const char **groups; + unsigned int *group_idx; + unsigned int ngroups; +}; + +struct k230_pinctrl { + struct pinctrl_desc pctl; + struct pinctrl_dev *pctl_dev; + struct regmap *regmap_base; + void __iomem *base; + struct k230_pin_group *groups; + unsigned int ngroups; + struct k230_pmx_func *functions; + unsigned int nfunctions; +}; + +static const struct regmap_config k230_regmap_config = { + .name = "canaan,pinctrl", + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x100, + .reg_stride = 4, +}; + +static int k230_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *k230_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int k230_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pins; + *num_pins = info->groups[selector].num_pins; + + return 0; +} + +static inline const struct k230_pmx_func *k230_name_to_funtion( + const struct k230_pinctrl *info, const char *name) +{ + unsigned int i; + + for (i = 0; i < info->nfunctions; i++) { + if (!strcmp(info->functions[i].name, name)) + return &info->functions[i]; + } + + return NULL; +} + +static struct pinctrl_pin_desc k230_pins[] = { + PINCTRL_PIN(0, "IO0"), PINCTRL_PIN(1, "IO1"), PINCTRL_PIN(2, "IO2"), + PINCTRL_PIN(3, "IO3"), PINCTRL_PIN(4, "IO4"), PINCTRL_PIN(5, "IO5"), + PINCTRL_PIN(6, "IO6"), PINCTRL_PIN(7, "IO7"), PINCTRL_PIN(8, "IO8"), + PINCTRL_PIN(9, "IO9"), PINCTRL_PIN(10, "IO10"), PINCTRL_PIN(11, "IO11"), + PINCTRL_PIN(12, "IO12"), PINCTRL_PIN(13, "IO13"), PINCTRL_PIN(14, "IO14"), + PINCTRL_PIN(15, "IO15"), PINCTRL_PIN(16, "IO16"), PINCTRL_PIN(17, "IO17"), + PINCTRL_PIN(18, "IO18"), PINCTRL_PIN(19, "IO19"), PINCTRL_PIN(20, "IO20"), + PINCTRL_PIN(21, "IO21"), PINCTRL_PIN(22, "IO22"), PINCTRL_PIN(23, "IO23"), + PINCTRL_PIN(24, "IO24"), PINCTRL_PIN(25, "IO25"), PINCTRL_PIN(26, "IO26"), + PINCTRL_PIN(27, "IO27"), PINCTRL_PIN(28, "IO28"), PINCTRL_PIN(29, "IO29"), + PINCTRL_PIN(30, "IO30"), PINCTRL_PIN(31, "IO31"), PINCTRL_PIN(32, "IO32"), + PINCTRL_PIN(33, "IO33"), PINCTRL_PIN(34, "IO34"), PINCTRL_PIN(35, "IO35"), + PINCTRL_PIN(36, "IO36"), PINCTRL_PIN(37, "IO37"), PINCTRL_PIN(38, "IO38"), + PINCTRL_PIN(39, "IO39"), PINCTRL_PIN(40, "IO40"), PINCTRL_PIN(41, "IO41"), + PINCTRL_PIN(42, "IO42"), PINCTRL_PIN(43, "IO43"), PINCTRL_PIN(44, "IO44"), + PINCTRL_PIN(45, "IO45"), PINCTRL_PIN(46, "IO46"), PINCTRL_PIN(47, "IO47"), + PINCTRL_PIN(48, "IO48"), PINCTRL_PIN(49, "IO49"), PINCTRL_PIN(50, "IO50"), + PINCTRL_PIN(51, "IO51"), PINCTRL_PIN(52, "IO52"), PINCTRL_PIN(53, "IO53"), + PINCTRL_PIN(54, "IO54"), PINCTRL_PIN(55, "IO55"), PINCTRL_PIN(56, "IO56"), + PINCTRL_PIN(57, "IO57"), PINCTRL_PIN(58, "IO58"), PINCTRL_PIN(59, "IO59"), + PINCTRL_PIN(60, "IO60"), PINCTRL_PIN(61, "IO61"), PINCTRL_PIN(62, "IO62"), + PINCTRL_PIN(63, "IO63") +}; + +static void k230_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int offset) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + u32 val, bias, drive, input, slew, schmitt, power; + struct k230_pin_group *grp = k230_pins[offset].drv_data; + static const char * const biasing[] = { + "pull none", "pull down", "pull up", "" }; + static const char * const enable[] = { + "disable", "enable" }; + static const char * const power_source[] = { + "3V3", "1V8" }; + + regmap_read(info->regmap_base, offset * 4, &val); + + drive = (val & K230_PC_DS) >> K230_SHIFT_DS; + bias = (val & K230_PC_BIAS) >> K230_SHIFT_BIAS; + input = (val & K230_PC_IE) >> K230_SHIFT_IE; + slew = (val & K230_PC_SL) >> K230_SHIFT_SL; + schmitt = (val & K230_PC_ST) >> K230_SHIFT_ST; + power = (val & K230_PC_MSC) >> K230_SHIFT_MSC; + + seq_printf(s, "%s - strength %d - %s - %s - slewrate %s - schmitt %s - %s", + grp ? grp->name : "unknown", + drive, + biasing[bias], + input ? "input" : "output", + enable[slew], + enable[schmitt], + power_source[power]); +} + +static int k230_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = info->pctl_dev->dev; + const struct k230_pmx_func *func; + const struct k230_pin_group *grp; + struct pinctrl_map *new_map; + int map_num, i, j, idx; + unsigned int grp_id; + + func = k230_name_to_funtion(info, np_config->name); + if (!func) { + dev_err(dev, "function %s not found\n", np_config->name); + return -EINVAL; + } + + map_num = 0; + for (i = 0; i < func->ngroups; ++i) { + grp_id = func->group_idx[i]; + /* npins of config map plus a mux map */ + map_num += info->groups[grp_id].num_pins + 1; + } + + new_map = kcalloc(map_num, sizeof(*new_map), GFP_KERNEL); + if (!new_map) + return -ENOMEM; + *map = new_map; + *num_maps = map_num; + + idx = 0; + for (i = 0; i < func->ngroups; ++i) { + grp_id = func->group_idx[i]; + grp = &info->groups[grp_id]; + new_map[idx].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[idx].data.mux.group = grp->name; + new_map[idx].data.mux.function = np_config->name; + idx++; + + for (j = 0; j < grp->num_pins; ++j) { + new_map[idx].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[idx].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[j]); + new_map[idx].data.configs.configs = + grp->data[j].configs; + new_map[idx].data.configs.num_configs = + grp->data[j].nconfigs; + idx++; + } + } + + return 0; +} + +static void k230_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned int num_maps) +{ + kfree(map); +} + +static const struct pinctrl_ops k230_pctrl_ops = { + .get_groups_count = k230_get_groups_count, + .get_group_name = k230_get_group_name, + .get_group_pins = k230_get_group_pins, + .pin_dbg_show = k230_pinctrl_pin_dbg_show, + .dt_node_to_map = k230_dt_node_to_map, + .dt_free_map = k230_dt_free_map, +}; + +static int k230_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned int val, arg; + + regmap_read(info->regmap_base, pin * 4, &val); + + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + arg = (val & K230_PC_ST) ? 1 : 0; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + arg = (val & K230_PC_DS) >> K230_SHIFT_DS; + break; + case PIN_CONFIG_BIAS_DISABLE: + arg = (val & K230_PC_BIAS) ? 0 : 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = (val & K230_PC_PD) ? 1 : 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = (val & K230_PC_PU) ? 1 : 0; + break; + case PIN_CONFIG_OUTPUT_ENABLE: + arg = (val & K230_PC_OE) ? 1 : 0; + break; + case PIN_CONFIG_INPUT_ENABLE: + arg = (val & K230_PC_IE) ? 1 : 0; + break; + case PIN_CONFIG_POWER_SOURCE: + arg = (val & K230_PC_MSC) ? 1 : 0; + break; + case PIN_CONFIG_SLEW_RATE: + arg = (val & K230_PC_SL) ? 1 : 0; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int k230_pinconf_set_param(struct pinctrl_dev *pctldev, unsigned int pin, + enum pin_config_param param, unsigned int arg) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + unsigned int val; + + regmap_read(info->regmap_base, pin * 4, &val); + + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (arg) + val |= K230_PC_ST; + else + val &= ~K230_PC_ST; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + val &= ~K230_PC_DS; + val |= (arg << K230_SHIFT_DS) & K230_PC_DS; + break; + case PIN_CONFIG_BIAS_DISABLE: + val &= ~K230_PC_BIAS; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!arg) + return -EINVAL; + val |= K230_PC_PD; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (!arg) + return -EINVAL; + val |= K230_PC_PU; + break; + case PIN_CONFIG_OUTPUT_ENABLE: + if (!arg) + return -EINVAL; + val |= K230_PC_OE; + break; + case PIN_CONFIG_INPUT_ENABLE: + if (!arg) + return -EINVAL; + val |= K230_PC_IE; + break; + case PIN_CONFIG_POWER_SOURCE: + if (arg) + val |= K230_PC_MSC; + else + val &= ~K230_PC_MSC; + break; + case PIN_CONFIG_SLEW_RATE: + if (arg) + val |= K230_PC_SL; + else + val &= ~K230_PC_SL; + break; + default: + return -EINVAL; + } + + regmap_write(info->regmap_base, pin * 4, val); + + return 0; +} + +static int k230_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = info->pctl_dev->dev; + enum pin_config_param param; + unsigned int arg, i; + int ret; + + if (pin >= K230_NPINS) { + dev_err(dev, "pin number out of range\n"); + return -EINVAL; + } + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + ret = k230_pinconf_set_param(pctldev, pin, param, arg); + if (ret) + return ret; + } + + return 0; +} + +static void k230_pconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + unsigned int val; + + regmap_read(info->regmap_base, pin * 4, &val); + + seq_printf(s, " 0x%08x", val); +} + +static const struct pinconf_ops k230_pinconf_ops = { + .is_generic = true, + .pin_config_get = k230_pinconf_get, + .pin_config_set = k230_pinconf_set, + .pin_config_dbg_show = k230_pconf_dbg_show, +}; + +static int k230_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *k230_get_fname(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int k230_get_groups(struct pinctrl_dev *pctldev, unsigned int selector, + const char * const **groups, unsigned int *num_groups) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +static int k230_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct k230_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct k230_pin_conf *data = info->groups[group].data; + struct k230_pin_group *grp = &info->groups[group]; + const unsigned int *pins = grp->pins; + struct regmap *regmap; + unsigned int value, mask; + int cnt, reg; + + regmap = info->regmap_base; + + for (cnt = 0; cnt < grp->num_pins; cnt++) { + reg = pins[cnt] * 4; + value = data[cnt].func << K230_SHIFT_SEL; + mask = K230_PC_SEL; + regmap_update_bits(regmap, reg, mask, value); + k230_pins[pins[cnt]].drv_data = grp; + } + + return 0; +} + +static const struct pinmux_ops k230_pmxops = { + .get_functions_count = k230_get_functions_count, + .get_function_name = k230_get_fname, + .get_function_groups = k230_get_groups, + .set_mux = k230_set_mux, + .strict = true, +}; + +static int k230_pinctrl_parse_groups(struct device_node *np, + struct k230_pin_group *grp, + struct k230_pinctrl *info, + unsigned int index) +{ + struct device *dev = info->pctl_dev->dev; + const __be32 *list; + int size, i, ret; + + grp->name = np->name; + + list = of_get_property(np, "pinmux", &size); + size /= sizeof(*list); + + grp->num_pins = size; + grp->pins = devm_kcalloc(dev, grp->num_pins, sizeof(*grp->pins), + GFP_KERNEL); + grp->data = devm_kcalloc(dev, grp->num_pins, sizeof(*grp->data), + GFP_KERNEL); + if (!grp->pins || !grp->data) + return -ENOMEM; + + for (i = 0; i < size; i++) { + unsigned int mux_data = be32_to_cpu(*list++); + + grp->pins[i] = (mux_data >> 8); + grp->data[i].func = (mux_data & 0xff); + + ret = pinconf_generic_parse_dt_config(np, NULL, + &grp->data[i].configs, + &grp->data[i].nconfigs); + if (ret) + return ret; + } + + return 0; +} + +static int k230_pinctrl_parse_functions(struct device_node *np, + struct k230_pinctrl *info, + unsigned int index) +{ + struct device *dev = info->pctl_dev->dev; + struct k230_pmx_func *func; + struct k230_pin_group *grp; + static unsigned int idx, i; + int ret; + + func = &info->functions[index]; + + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) + return 0; + + func->groups = devm_kcalloc(dev, func->ngroups, + sizeof(*func->groups), GFP_KERNEL); + func->group_idx = devm_kcalloc(dev, func->ngroups, + sizeof(*func->group_idx), GFP_KERNEL); + if (!func->groups || !func->group_idx) + return -ENOMEM; + + i = 0; + + for_each_child_of_node_scoped(np, child) { + func->groups[i] = child->name; + func->group_idx[i] = idx; + grp = &info->groups[idx]; + idx++; + ret = k230_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static void k230_pinctrl_child_count(struct k230_pinctrl *info, + struct device_node *np) +{ + for_each_child_of_node_scoped(np, child) { + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } +} + +static int k230_pinctrl_parse_dt(struct platform_device *pdev, + struct k230_pinctrl *info) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + unsigned int i; + int ret; + + k230_pinctrl_child_count(info, np); + + info->functions = devm_kcalloc(dev, info->nfunctions, + sizeof(*info->functions), GFP_KERNEL); + info->groups = devm_kcalloc(dev, info->ngroups, + sizeof(*info->groups), GFP_KERNEL); + if (!info->functions || !info->groups) + return -ENOMEM; + + i = 0; + + for_each_child_of_node_scoped(np, child) { + ret = k230_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int k230_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k230_pinctrl *info; + struct pinctrl_desc *pctl; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + pctl = &info->pctl; + + pctl->name = "k230-pinctrl"; + pctl->owner = THIS_MODULE; + pctl->pins = k230_pins; + pctl->npins = ARRAY_SIZE(k230_pins); + pctl->pctlops = &k230_pctrl_ops; + pctl->pmxops = &k230_pmxops; + pctl->confops = &k230_pinconf_ops; + + info->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->base)) + return PTR_ERR(info->base); + + info->regmap_base = devm_regmap_init_mmio(dev, info->base, + &k230_regmap_config); + if (IS_ERR(info->regmap_base)) + return dev_err_probe(dev, PTR_ERR(info->regmap_base), + "failed to init regmap\n"); + + info->pctl_dev = devm_pinctrl_register(dev, pctl, info); + if (IS_ERR(info->pctl_dev)) + return dev_err_probe(dev, PTR_ERR(info->pctl_dev), + "devm_pinctrl_register failed\n"); + + k230_pinctrl_parse_dt(pdev, info); + + return 0; +} + +static const struct of_device_id k230_dt_ids[] = { + { .compatible = "canaan,k230-pinctrl", }, + { /* sintenel */ } +}; +MODULE_DEVICE_TABLE(of, k230_dt_ids); + +static struct platform_driver k230_pinctrl_driver = { + .probe = k230_pinctrl_probe, + .driver = { + .name = "k230-pinctrl", + .of_match_table = k230_dt_ids, + }, +}; +module_platform_driver(k230_pinctrl_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ze Huang <18771902331@163.com>"); +MODULE_DESCRIPTION("Canaan K230 pinctrl driver"); diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index 737d0ae3d0b66..d66c3a3e84292 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -569,7 +569,7 @@ static void mcp23s08_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct mcp23s08 *mcp = gpiochip_get_data(gc); - seq_printf(p, dev_name(mcp->dev)); + seq_puts(p, dev_name(mcp->dev)); } static const struct irq_chip mcp23s08_irq_chip = { diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c index d1ab8450ea93e..61532a7a612ad 100644 --- a/drivers/pinctrl/pinctrl-ocelot.c +++ b/drivers/pinctrl/pinctrl-ocelot.c @@ -57,6 +57,8 @@ enum { FUNC_CAN1, FUNC_CLKMON, FUNC_NONE, + FUNC_FAN, + FUNC_FC, FUNC_FC0_a, FUNC_FC0_b, FUNC_FC0_c, @@ -71,6 +73,7 @@ enum { FUNC_FC4_a, FUNC_FC4_b, FUNC_FC4_c, + FUNC_FC_SHRD, FUNC_FC_SHRD0, FUNC_FC_SHRD1, FUNC_FC_SHRD2, @@ -92,6 +95,7 @@ enum { FUNC_FC_SHRD18, FUNC_FC_SHRD19, FUNC_FC_SHRD20, + FUNC_FUSA, FUNC_GPIO, FUNC_IB_TRG_a, FUNC_IB_TRG_b, @@ -108,6 +112,8 @@ enum { FUNC_IRQ1, FUNC_IRQ1_IN, FUNC_IRQ1_OUT, + FUNC_IRQ3, + FUNC_IRQ4, FUNC_EXT_IRQ, FUNC_MIIM, FUNC_MIIM_a, @@ -115,12 +121,14 @@ enum { FUNC_MIIM_c, FUNC_MIIM_Sa, FUNC_MIIM_Sb, + FUNC_MIIM_IRQ, FUNC_OB_TRG, FUNC_OB_TRG_a, FUNC_OB_TRG_b, FUNC_PHY_LED, FUNC_PCI_WAKE, FUNC_MD, + FUNC_PCIE_PERST, FUNC_PTP0, FUNC_PTP1, FUNC_PTP2, @@ -152,6 +160,7 @@ enum { FUNC_SGPIO_b, FUNC_SI, FUNC_SI2, + FUNC_SYNCE, FUNC_TACHO, FUNC_TACHO_a, FUNC_TACHO_b, @@ -170,6 +179,10 @@ enum { FUNC_USB_S_a, FUNC_USB_S_b, FUNC_USB_S_c, + FUNC_USB_POWER, + FUNC_USB2PHY_RST, + FUNC_USB_OVER_DETECT, + FUNC_USB_ULPI, FUNC_PLL_STAT, FUNC_EMMC, FUNC_EMMC_SD, @@ -184,6 +197,8 @@ static const char *const ocelot_function_names[] = { [FUNC_CAN1] = "can1", [FUNC_CLKMON] = "clkmon", [FUNC_NONE] = "none", + [FUNC_FAN] = "fan", + [FUNC_FC] = "fc", [FUNC_FC0_a] = "fc0_a", [FUNC_FC0_b] = "fc0_b", [FUNC_FC0_c] = "fc0_c", @@ -198,6 +213,7 @@ static const char *const ocelot_function_names[] = { [FUNC_FC4_a] = "fc4_a", [FUNC_FC4_b] = "fc4_b", [FUNC_FC4_c] = "fc4_c", + [FUNC_FC_SHRD] = "fc_shrd", [FUNC_FC_SHRD0] = "fc_shrd0", [FUNC_FC_SHRD1] = "fc_shrd1", [FUNC_FC_SHRD2] = "fc_shrd2", @@ -219,6 +235,7 @@ static const char *const ocelot_function_names[] = { [FUNC_FC_SHRD18] = "fc_shrd18", [FUNC_FC_SHRD19] = "fc_shrd19", [FUNC_FC_SHRD20] = "fc_shrd20", + [FUNC_FUSA] = "fusa", [FUNC_GPIO] = "gpio", [FUNC_IB_TRG_a] = "ib_trig_a", [FUNC_IB_TRG_b] = "ib_trig_b", @@ -235,6 +252,8 @@ static const char *const ocelot_function_names[] = { [FUNC_IRQ1] = "irq1", [FUNC_IRQ1_IN] = "irq1_in", [FUNC_IRQ1_OUT] = "irq1_out", + [FUNC_IRQ3] = "irq3", + [FUNC_IRQ4] = "irq4", [FUNC_EXT_IRQ] = "ext_irq", [FUNC_MIIM] = "miim", [FUNC_MIIM_a] = "miim_a", @@ -242,8 +261,10 @@ static const char *const ocelot_function_names[] = { [FUNC_MIIM_c] = "miim_c", [FUNC_MIIM_Sa] = "miim_slave_a", [FUNC_MIIM_Sb] = "miim_slave_b", + [FUNC_MIIM_IRQ] = "miim_irq", [FUNC_PHY_LED] = "phy_led", [FUNC_PCI_WAKE] = "pci_wake", + [FUNC_PCIE_PERST] = "pcie_perst", [FUNC_MD] = "md", [FUNC_OB_TRG] = "ob_trig", [FUNC_OB_TRG_a] = "ob_trig_a", @@ -279,6 +300,7 @@ static const char *const ocelot_function_names[] = { [FUNC_SGPIO_b] = "sgpio_b", [FUNC_SI] = "si", [FUNC_SI2] = "si2", + [FUNC_SYNCE] = "synce", [FUNC_TACHO] = "tacho", [FUNC_TACHO_a] = "tacho_a", [FUNC_TACHO_b] = "tacho_b", @@ -294,6 +316,10 @@ static const char *const ocelot_function_names[] = { [FUNC_USB_S_a] = "usb_slave_a", [FUNC_USB_S_b] = "usb_slave_b", [FUNC_USB_S_c] = "usb_slave_c", + [FUNC_USB_POWER] = "usb_power", + [FUNC_USB2PHY_RST] = "usb2phy_rst", + [FUNC_USB_OVER_DETECT] = "usb_over_detect", + [FUNC_USB_ULPI] = "usb_ulpi", [FUNC_UART] = "uart", [FUNC_UART2] = "uart2", [FUNC_UART3] = "uart3", @@ -1136,6 +1162,165 @@ static const struct pinctrl_pin_desc lan966x_pins[] = { LAN966X_PIN(77), }; +#define LAN969X_P(p, f0, f1, f2, f3, f4, f5, f6, f7) \ +static struct ocelot_pin_caps lan969x_pin_##p = { \ + .pin = p, \ + .functions = { \ + FUNC_##f0, FUNC_##f1, FUNC_##f2, \ + FUNC_##f3 \ + }, \ + .a_functions = { \ + FUNC_##f4, FUNC_##f5, FUNC_##f6, \ + FUNC_##f7 \ + }, \ +} + +/* Pinmuxing table taken from data sheet */ +/* Pin FUNC0 FUNC1 FUNC2 FUNC3 FUNC4 FUNC5 FUNC6 FUNC7 */ +LAN969X_P(0, GPIO, IRQ0, FC_SHRD, PCIE_PERST, NONE, NONE, NONE, R); +LAN969X_P(1, GPIO, IRQ1, FC_SHRD, USB_POWER, NONE, NONE, NONE, R); +LAN969X_P(2, GPIO, FC, NONE, NONE, NONE, NONE, NONE, R); +LAN969X_P(3, GPIO, FC, NONE, NONE, NONE, NONE, NONE, R); +LAN969X_P(4, GPIO, FC, NONE, NONE, NONE, NONE, NONE, R); +LAN969X_P(5, GPIO, SGPIO_a, NONE, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(6, GPIO, SGPIO_a, NONE, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(7, GPIO, SGPIO_a, NONE, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(8, GPIO, SGPIO_a, NONE, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(9, GPIO, MIIM, MIIM_Sa, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(10, GPIO, MIIM, MIIM_Sa, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(11, GPIO, MIIM_IRQ, MIIM_Sa, CLKMON, NONE, NONE, NONE, R); +LAN969X_P(12, GPIO, IRQ3, FC_SHRD, USB2PHY_RST, NONE, NONE, NONE, R); +LAN969X_P(13, GPIO, IRQ4, FC_SHRD, USB_OVER_DETECT, NONE, NONE, NONE, R); +LAN969X_P(14, GPIO, EMMC_SD, QSPI1, FC, NONE, NONE, NONE, R); +LAN969X_P(15, GPIO, EMMC_SD, QSPI1, FC, NONE, NONE, NONE, R); +LAN969X_P(16, GPIO, EMMC_SD, QSPI1, FC, NONE, NONE, NONE, R); +LAN969X_P(17, GPIO, EMMC_SD, QSPI1, PTPSYNC_0, USB_POWER, NONE, NONE, R); +LAN969X_P(18, GPIO, EMMC_SD, QSPI1, PTPSYNC_1, USB2PHY_RST, NONE, NONE, R); +LAN969X_P(19, GPIO, EMMC_SD, QSPI1, PTPSYNC_2, USB_OVER_DETECT, NONE, NONE, R); +LAN969X_P(20, GPIO, EMMC_SD, NONE, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(21, GPIO, EMMC_SD, NONE, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(22, GPIO, EMMC_SD, NONE, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(23, GPIO, EMMC_SD, NONE, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(24, GPIO, EMMC_SD, NONE, NONE, NONE, NONE, NONE, R); +LAN969X_P(25, GPIO, FAN, FUSA, CAN0_a, QSPI1, NONE, NONE, R); +LAN969X_P(26, GPIO, FAN, FUSA, CAN0_a, QSPI1, NONE, NONE, R); +LAN969X_P(27, GPIO, SYNCE, FC, MIIM, QSPI1, NONE, NONE, R); +LAN969X_P(28, GPIO, SYNCE, FC, MIIM, QSPI1, NONE, NONE, R); +LAN969X_P(29, GPIO, SYNCE, FC, MIIM_IRQ, QSPI1, NONE, NONE, R); +LAN969X_P(30, GPIO, PTPSYNC_0, USB_ULPI, FC_SHRD, QSPI1, NONE, NONE, R); +LAN969X_P(31, GPIO, PTPSYNC_1, USB_ULPI, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(32, GPIO, PTPSYNC_2, USB_ULPI, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(33, GPIO, SD, USB_ULPI, FC_SHRD, NONE, NONE, NONE, R); +LAN969X_P(34, GPIO, SD, USB_ULPI, CAN1, FC_SHRD, NONE, NONE, R); +LAN969X_P(35, GPIO, SD, USB_ULPI, CAN1, FC_SHRD, NONE, NONE, R); +LAN969X_P(36, GPIO, SD, USB_ULPI, PCIE_PERST, FC_SHRD, NONE, NONE, R); +LAN969X_P(37, GPIO, SD, USB_ULPI, CAN0_b, NONE, NONE, NONE, R); +LAN969X_P(38, GPIO, SD, USB_ULPI, CAN0_b, NONE, NONE, NONE, R); +LAN969X_P(39, GPIO, SD, USB_ULPI, MIIM, NONE, NONE, NONE, R); +LAN969X_P(40, GPIO, SD, USB_ULPI, MIIM, NONE, NONE, NONE, R); +LAN969X_P(41, GPIO, SD, USB_ULPI, MIIM_IRQ, NONE, NONE, NONE, R); +LAN969X_P(42, GPIO, PTPSYNC_3, CAN1, NONE, NONE, NONE, NONE, R); +LAN969X_P(43, GPIO, PTPSYNC_4, CAN1, NONE, NONE, NONE, NONE, R); +LAN969X_P(44, GPIO, PTPSYNC_5, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(45, GPIO, PTPSYNC_6, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(46, GPIO, PTPSYNC_7, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(47, GPIO, NONE, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(48, GPIO, NONE, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(49, GPIO, NONE, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(50, GPIO, NONE, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(51, GPIO, NONE, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(52, GPIO, FAN, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(53, GPIO, FAN, SFP_SD, NONE, NONE, NONE, NONE, R); +LAN969X_P(54, GPIO, SYNCE, FC, NONE, NONE, NONE, NONE, R); +LAN969X_P(55, GPIO, SYNCE, FC, NONE, NONE, NONE, NONE, R); +LAN969X_P(56, GPIO, SYNCE, FC, NONE, NONE, NONE, NONE, R); +LAN969X_P(57, GPIO, SFP_SD, FC_SHRD, TWI, PTPSYNC_3, NONE, NONE, R); +LAN969X_P(58, GPIO, SFP_SD, FC_SHRD, TWI, PTPSYNC_4, NONE, NONE, R); +LAN969X_P(59, GPIO, SFP_SD, FC_SHRD, TWI, PTPSYNC_5, NONE, NONE, R); +LAN969X_P(60, GPIO, SFP_SD, FC_SHRD, TWI, PTPSYNC_6, NONE, NONE, R); +LAN969X_P(61, GPIO, MIIM, FC_SHRD, TWI, NONE, NONE, NONE, R); +LAN969X_P(62, GPIO, MIIM, FC_SHRD, TWI, NONE, NONE, NONE, R); +LAN969X_P(63, GPIO, MIIM_IRQ, FC_SHRD, TWI, NONE, NONE, NONE, R); +LAN969X_P(64, GPIO, FC, FC_SHRD, TWI, NONE, NONE, NONE, R); +LAN969X_P(65, GPIO, FC, FC_SHRD, TWI, NONE, NONE, NONE, R); +LAN969X_P(66, GPIO, FC, FC_SHRD, TWI, NONE, NONE, NONE, R); + +#define LAN969X_PIN(n) { \ + .number = n, \ + .name = "GPIO_"#n, \ + .drv_data = &lan969x_pin_##n \ +} + +static const struct pinctrl_pin_desc lan969x_pins[] = { + LAN969X_PIN(0), + LAN969X_PIN(1), + LAN969X_PIN(2), + LAN969X_PIN(3), + LAN969X_PIN(4), + LAN969X_PIN(5), + LAN969X_PIN(6), + LAN969X_PIN(7), + LAN969X_PIN(8), + LAN969X_PIN(9), + LAN969X_PIN(10), + LAN969X_PIN(11), + LAN969X_PIN(12), + LAN969X_PIN(13), + LAN969X_PIN(14), + LAN969X_PIN(15), + LAN969X_PIN(16), + LAN969X_PIN(17), + LAN969X_PIN(18), + LAN969X_PIN(19), + LAN969X_PIN(20), + LAN969X_PIN(21), + LAN969X_PIN(22), + LAN969X_PIN(23), + LAN969X_PIN(24), + LAN969X_PIN(25), + LAN969X_PIN(26), + LAN969X_PIN(27), + LAN969X_PIN(28), + LAN969X_PIN(29), + LAN969X_PIN(30), + LAN969X_PIN(31), + LAN969X_PIN(32), + LAN969X_PIN(33), + LAN969X_PIN(34), + LAN969X_PIN(35), + LAN969X_PIN(36), + LAN969X_PIN(37), + LAN969X_PIN(38), + LAN969X_PIN(39), + LAN969X_PIN(40), + LAN969X_PIN(41), + LAN969X_PIN(42), + LAN969X_PIN(43), + LAN969X_PIN(44), + LAN969X_PIN(45), + LAN969X_PIN(46), + LAN969X_PIN(47), + LAN969X_PIN(48), + LAN969X_PIN(49), + LAN969X_PIN(50), + LAN969X_PIN(51), + LAN969X_PIN(52), + LAN969X_PIN(53), + LAN969X_PIN(54), + LAN969X_PIN(55), + LAN969X_PIN(56), + LAN969X_PIN(57), + LAN969X_PIN(58), + LAN969X_PIN(59), + LAN969X_PIN(60), + LAN969X_PIN(61), + LAN969X_PIN(62), + LAN969X_PIN(63), + LAN969X_PIN(64), + LAN969X_PIN(65), + LAN969X_PIN(66), +}; + static int ocelot_get_functions_count(struct pinctrl_dev *pctldev) { return ARRAY_SIZE(ocelot_function_names); @@ -1682,6 +1867,23 @@ static struct ocelot_match_data lan966x_desc = { }, }; +static struct ocelot_match_data lan969x_desc = { + .desc = { + .name = "lan969x-pinctrl", + .pins = lan969x_pins, + .npins = ARRAY_SIZE(lan969x_pins), + .pctlops = &ocelot_pctl_ops, + .pmxops = &lan966x_pmx_ops, + .confops = &ocelot_confops, + .owner = THIS_MODULE, + }, + .pincfg_data = { + .pd_bit = BIT(3), + .pu_bit = BIT(2), + .drive_bits = GENMASK(1, 0), + }, +}; + static int ocelot_create_group_func_map(struct device *dev, struct ocelot_pinctrl *info) { @@ -2014,6 +2216,7 @@ static const struct of_device_id ocelot_pinctrl_of_match[] = { { .compatible = "mscc,servalt-pinctrl", .data = &servalt_desc }, { .compatible = "microchip,sparx5-pinctrl", .data = &sparx5_desc }, { .compatible = "microchip,lan966x-pinctrl", .data = &lan966x_desc }, + { .compatible = "microchip,lan9691-pinctrl", .data = &lan969x_desc }, {}, }; MODULE_DEVICE_TABLE(of, ocelot_pinctrl_of_match); diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 5c1bc4d5b662e..36d4eaf0ebd15 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -3227,7 +3227,9 @@ static int rockchip_pinctrl_parse_groups(struct device_node *np, /* we do not check return since it's safe node passed down */ size /= sizeof(*list); if (!size || size % 4) - return dev_err_probe(dev, -EINVAL, "wrong pins number or pins and configs should be by 4\n"); + return dev_err_probe(dev, -EINVAL, + "%pOF: rockchip,pins: expected one or more of , got %d args instead\n", + np, size); grp->npins = size / 4; @@ -4219,7 +4221,7 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = { static struct platform_driver rockchip_pinctrl_driver = { .probe = rockchip_pinctrl_probe, - .remove_new = rockchip_pinctrl_remove, + .remove = rockchip_pinctrl_remove, .driver = { .name = "rockchip-pinctrl", .pm = &rockchip_pinctrl_dev_pm_ops, diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 2ec599e383e4b..5be14dc979e26 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1966,6 +1966,7 @@ static const struct pcs_soc_data pinconf_single = { }; static const struct of_device_id pcs_of_match[] = { + { .compatible = "marvell,pxa1908-padconf", .data = &pinconf_single }, { .compatible = "ti,am437-padconf", .data = &pinctrl_single_am437x }, { .compatible = "ti,am654-padconf", .data = &pinctrl_single_am654 }, { .compatible = "ti,dra7-padconf", .data = &pinctrl_single_dra7 }, @@ -1981,7 +1982,7 @@ MODULE_DEVICE_TABLE(of, pcs_of_match); static struct platform_driver pcs_driver = { .probe = pcs_probe, - .remove_new = pcs_remove, + .remove = pcs_remove, .driver = { .name = DRIVER_NAME, .of_match_table = pcs_of_match, diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c index d2c5321dd025f..521f6fef0b9f6 100644 --- a/drivers/pinctrl/pinctrl-stmfx.c +++ b/drivers/pinctrl/pinctrl-stmfx.c @@ -599,7 +599,7 @@ static void stmfx_pinctrl_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(d); struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); - seq_printf(p, dev_name(pctl->dev)); + seq_puts(p, dev_name(pctl->dev)); } static const struct irq_chip stmfx_pinctrl_irq_chip = { @@ -855,7 +855,7 @@ static struct platform_driver stmfx_pinctrl_driver = { .pm = &stmfx_pinctrl_dev_pm_ops, }, .probe = stmfx_pinctrl_probe, - .remove_new = stmfx_pinctrl_remove, + .remove = stmfx_pinctrl_remove, }; module_platform_driver(stmfx_pinctrl_driver); diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c index fd0331a87cda2..98262b8ce43ac 100644 --- a/drivers/pinctrl/pinctrl-sx150x.c +++ b/drivers/pinctrl/pinctrl-sx150x.c @@ -584,7 +584,7 @@ static void sx150x_irq_print_chip(struct irq_data *d, struct seq_file *p) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct sx150x_pinctrl *pctl = gpiochip_get_data(gc); - seq_printf(p, pctl->client->name); + seq_puts(p, pctl->client->name); } static const struct irq_chip sx150x_irq_chip = { @@ -1105,7 +1105,7 @@ static const struct regmap_config sx150x_regmap_config = { .reg_bits = 8, .val_bits = 32, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_read = sx150x_regmap_reg_read, .reg_write = sx150x_regmap_reg_write, diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c index 4f98f72565f48..d6bb8f58978df 100644 --- a/drivers/pinctrl/pinctrl-tb10x.c +++ b/drivers/pinctrl/pinctrl-tb10x.c @@ -820,7 +820,7 @@ MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids); static struct platform_driver tb10x_pinctrl_pdrv = { .probe = tb10x_pinctrl_probe, - .remove_new = tb10x_pinctrl_remove, + .remove = tb10x_pinctrl_remove, .driver = { .name = "tb10x_pinctrl", .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids), diff --git a/drivers/pinctrl/pinctrl-th1520.c b/drivers/pinctrl/pinctrl-th1520.c new file mode 100644 index 0000000000000..e641bad6728cc --- /dev/null +++ b/drivers/pinctrl/pinctrl-th1520.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl driver for the T-Head TH1520 SoC + * + * Copyright (C) 2023 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "core.h" +#include "pinmux.h" +#include "pinconf.h" + +#define TH1520_PADCFG_IE BIT(9) +#define TH1520_PADCFG_SL BIT(8) +#define TH1520_PADCFG_ST BIT(7) +#define TH1520_PADCFG_SPU BIT(6) +#define TH1520_PADCFG_PS BIT(5) +#define TH1520_PADCFG_PE BIT(4) +#define TH1520_PADCFG_BIAS (TH1520_PADCFG_SPU | TH1520_PADCFG_PS | TH1520_PADCFG_PE) +#define TH1520_PADCFG_DS GENMASK(3, 0) + +#define TH1520_PULL_DOWN_OHM 44000 /* typ. 44kOhm */ +#define TH1520_PULL_UP_OHM 48000 /* typ. 48kOhm */ +#define TH1520_PULL_STRONG_OHM 2100 /* typ. 2.1kOhm */ + +#define TH1520_PAD_NO_PADCFG BIT(30) +#define TH1520_PAD_MUXDATA GENMASK(29, 0) + +struct th1520_pad_group { + const char *name; + const struct pinctrl_pin_desc *pins; + unsigned int npins; +}; + +struct th1520_pinctrl { + struct pinctrl_desc desc; + struct mutex mutex; /* serialize adding functions */ + raw_spinlock_t lock; /* serialize register access */ + void __iomem *base; + struct pinctrl_dev *pctl; +}; + +static void __iomem *th1520_padcfg(struct th1520_pinctrl *thp, + unsigned int pin) +{ + return thp->base + 4 * (pin / 2); +} + +static unsigned int th1520_padcfg_shift(unsigned int pin) +{ + return 16 * (pin & BIT(0)); +} + +static void __iomem *th1520_muxcfg(struct th1520_pinctrl *thp, + unsigned int pin) +{ + return thp->base + 0x400 + 4 * (pin / 8); +} + +static unsigned int th1520_muxcfg_shift(unsigned int pin) +{ + return 4 * (pin & GENMASK(2, 0)); +} + +enum th1520_muxtype { + TH1520_MUX_____, + TH1520_MUX_GPIO, + TH1520_MUX_PWM, + TH1520_MUX_UART, + TH1520_MUX_IR, + TH1520_MUX_I2C, + TH1520_MUX_SPI, + TH1520_MUX_QSPI, + TH1520_MUX_SDIO, + TH1520_MUX_AUD, + TH1520_MUX_I2S, + TH1520_MUX_MAC0, + TH1520_MUX_MAC1, + TH1520_MUX_DPU0, + TH1520_MUX_DPU1, + TH1520_MUX_ISP, + TH1520_MUX_HDMI, + TH1520_MUX_BSEL, + TH1520_MUX_DBG, + TH1520_MUX_CLK, + TH1520_MUX_JTAG, + TH1520_MUX_ISO, + TH1520_MUX_FUSE, + TH1520_MUX_RST, +}; + +static const char *const th1520_muxtype_string[] = { + [TH1520_MUX_GPIO] = "gpio", + [TH1520_MUX_PWM] = "pwm", + [TH1520_MUX_UART] = "uart", + [TH1520_MUX_IR] = "ir", + [TH1520_MUX_I2C] = "i2c", + [TH1520_MUX_SPI] = "spi", + [TH1520_MUX_QSPI] = "qspi", + [TH1520_MUX_SDIO] = "sdio", + [TH1520_MUX_AUD] = "audio", + [TH1520_MUX_I2S] = "i2s", + [TH1520_MUX_MAC0] = "gmac0", + [TH1520_MUX_MAC1] = "gmac1", + [TH1520_MUX_DPU0] = "dpu0", + [TH1520_MUX_DPU1] = "dpu1", + [TH1520_MUX_ISP] = "isp", + [TH1520_MUX_HDMI] = "hdmi", + [TH1520_MUX_BSEL] = "bootsel", + [TH1520_MUX_DBG] = "debug", + [TH1520_MUX_CLK] = "clock", + [TH1520_MUX_JTAG] = "jtag", + [TH1520_MUX_ISO] = "iso7816", + [TH1520_MUX_FUSE] = "efuse", + [TH1520_MUX_RST] = "reset", +}; + +static enum th1520_muxtype th1520_muxtype_get(const char *str) +{ + enum th1520_muxtype mt; + + for (mt = TH1520_MUX_GPIO; mt < ARRAY_SIZE(th1520_muxtype_string); mt++) { + if (!strcmp(str, th1520_muxtype_string[mt])) + return mt; + } + return TH1520_MUX_____; +} + +#define TH1520_PAD(_nr, _name, m0, m1, m2, m3, m4, m5, _flags) \ + { .number = _nr, .name = #_name, .drv_data = (void *)((_flags) | \ + (TH1520_MUX_##m0 << 0) | (TH1520_MUX_##m1 << 5) | (TH1520_MUX_##m2 << 10) | \ + (TH1520_MUX_##m3 << 15) | (TH1520_MUX_##m4 << 20) | (TH1520_MUX_##m5 << 25)) } + +static unsigned long th1520_pad_muxdata(void *drv_data) +{ + return (uintptr_t)drv_data & TH1520_PAD_MUXDATA; +} + +static bool th1520_pad_no_padcfg(void *drv_data) +{ + return (uintptr_t)drv_data & TH1520_PAD_NO_PADCFG; +} + +static const struct pinctrl_pin_desc th1520_group1_pins[] = { + TH1520_PAD(0, OSC_CLK_IN, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(1, OSC_CLK_OUT, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(2, SYS_RST_N, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(3, RTC_CLK_IN, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(4, RTC_CLK_OUT, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + /* skip number 5 so we can calculate register offsets and shifts from the pin number */ + TH1520_PAD(6, TEST_MODE, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(7, DEBUG_MODE, DBG, ____, ____, GPIO, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(8, POR_SEL, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG), + TH1520_PAD(9, I2C_AON_SCL, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(10, I2C_AON_SDA, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(11, CPU_JTG_TCLK, JTAG, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(12, CPU_JTG_TMS, JTAG, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(13, CPU_JTG_TDI, JTAG, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(14, CPU_JTG_TDO, JTAG, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(15, CPU_JTG_TRST, JTAG, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(16, AOGPIO_7, CLK, AUD, ____, GPIO, ____, ____, 0), + TH1520_PAD(17, AOGPIO_8, UART, AUD, IR, GPIO, ____, ____, 0), + TH1520_PAD(18, AOGPIO_9, UART, AUD, IR, GPIO, ____, ____, 0), + TH1520_PAD(19, AOGPIO_10, CLK, AUD, ____, GPIO, ____, ____, 0), + TH1520_PAD(20, AOGPIO_11, GPIO, AUD, ____, ____, ____, ____, 0), + TH1520_PAD(21, AOGPIO_12, GPIO, AUD, ____, ____, ____, ____, 0), + TH1520_PAD(22, AOGPIO_13, GPIO, AUD, ____, ____, ____, ____, 0), + TH1520_PAD(23, AOGPIO_14, GPIO, AUD, ____, ____, ____, ____, 0), + TH1520_PAD(24, AOGPIO_15, GPIO, AUD, ____, ____, ____, ____, 0), + TH1520_PAD(25, AUDIO_PA0, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(26, AUDIO_PA1, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(27, AUDIO_PA2, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(28, AUDIO_PA3, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(29, AUDIO_PA4, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(30, AUDIO_PA5, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(31, AUDIO_PA6, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(32, AUDIO_PA7, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(33, AUDIO_PA8, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(34, AUDIO_PA9, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(35, AUDIO_PA10, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(36, AUDIO_PA11, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(37, AUDIO_PA12, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(38, AUDIO_PA13, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(39, AUDIO_PA14, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(40, AUDIO_PA15, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(41, AUDIO_PA16, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(42, AUDIO_PA17, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(43, AUDIO_PA27, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(44, AUDIO_PA28, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(45, AUDIO_PA29, AUD, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(46, AUDIO_PA30, AUD, RST, ____, GPIO, ____, ____, 0), +}; + +static const struct pinctrl_pin_desc th1520_group2_pins[] = { + TH1520_PAD(0, QSPI1_SCLK, QSPI, ISO, ____, GPIO, FUSE, ____, 0), + TH1520_PAD(1, QSPI1_CSN0, QSPI, ____, I2C, GPIO, FUSE, ____, 0), + TH1520_PAD(2, QSPI1_D0_MOSI, QSPI, ISO, I2C, GPIO, FUSE, ____, 0), + TH1520_PAD(3, QSPI1_D1_MISO, QSPI, ISO, ____, GPIO, FUSE, ____, 0), + TH1520_PAD(4, QSPI1_D2_WP, QSPI, ISO, UART, GPIO, FUSE, ____, 0), + TH1520_PAD(5, QSPI1_D3_HOLD, QSPI, ISO, UART, GPIO, ____, ____, 0), + TH1520_PAD(6, I2C0_SCL, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(7, I2C0_SDA, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(8, I2C1_SCL, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(9, I2C1_SDA, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(10, UART1_TXD, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(11, UART1_RXD, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(12, UART4_TXD, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(13, UART4_RXD, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(14, UART4_CTSN, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(15, UART4_RTSN, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(16, UART3_TXD, DBG, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(17, UART3_RXD, DBG, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(18, GPIO0_18, GPIO, I2C, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(19, GPIO0_19, GPIO, I2C, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(20, GPIO0_20, GPIO, UART, IR, ____, DPU0, DPU1, 0), + TH1520_PAD(21, GPIO0_21, GPIO, UART, IR, ____, DPU0, DPU1, 0), + TH1520_PAD(22, GPIO0_22, GPIO, JTAG, I2C, ____, DPU0, DPU1, 0), + TH1520_PAD(23, GPIO0_23, GPIO, JTAG, I2C, ____, DPU0, DPU1, 0), + TH1520_PAD(24, GPIO0_24, GPIO, JTAG, QSPI, ____, DPU0, DPU1, 0), + TH1520_PAD(25, GPIO0_25, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(26, GPIO0_26, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(27, GPIO0_27, GPIO, ____, I2C, ____, DPU0, DPU1, 0), + TH1520_PAD(28, GPIO0_28, GPIO, ____, I2C, ____, DPU0, DPU1, 0), + TH1520_PAD(29, GPIO0_29, GPIO, ____, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(30, GPIO0_30, GPIO, ____, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(31, GPIO0_31, GPIO, ____, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(32, GPIO1_0, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(33, GPIO1_1, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(34, GPIO1_2, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(35, GPIO1_3, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(36, GPIO1_4, GPIO, JTAG, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(37, GPIO1_5, GPIO, ____, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(38, GPIO1_6, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(39, GPIO1_7, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(40, GPIO1_8, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(41, GPIO1_9, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(42, GPIO1_10, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(43, GPIO1_11, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(44, GPIO1_12, GPIO, QSPI, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(45, GPIO1_13, GPIO, UART, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(46, GPIO1_14, GPIO, UART, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(47, GPIO1_15, GPIO, UART, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(48, GPIO1_16, GPIO, UART, ____, ____, DPU0, DPU1, 0), + TH1520_PAD(49, CLK_OUT_0, BSEL, CLK, ____, GPIO, ____, ____, 0), + TH1520_PAD(50, CLK_OUT_1, BSEL, CLK, ____, GPIO, ____, ____, 0), + TH1520_PAD(51, CLK_OUT_2, BSEL, CLK, ____, GPIO, ____, ____, 0), + TH1520_PAD(52, CLK_OUT_3, BSEL, CLK, ____, GPIO, ____, ____, 0), + TH1520_PAD(53, GPIO1_21, JTAG, ____, ISP, GPIO, ____, ____, 0), + TH1520_PAD(54, GPIO1_22, JTAG, ____, ISP, GPIO, ____, ____, 0), + TH1520_PAD(55, GPIO1_23, JTAG, ____, ISP, GPIO, ____, ____, 0), + TH1520_PAD(56, GPIO1_24, JTAG, ____, ISP, GPIO, ____, ____, 0), + TH1520_PAD(57, GPIO1_25, JTAG, ____, ISP, GPIO, ____, ____, 0), + TH1520_PAD(58, GPIO1_26, GPIO, ____, ISP, ____, ____, ____, 0), + TH1520_PAD(59, GPIO1_27, GPIO, ____, ISP, ____, ____, ____, 0), + TH1520_PAD(60, GPIO1_28, GPIO, ____, ISP, ____, ____, ____, 0), + TH1520_PAD(61, GPIO1_29, GPIO, ____, ISP, ____, ____, ____, 0), + TH1520_PAD(62, GPIO1_30, GPIO, ____, ISP, ____, ____, ____, 0), +}; + +static const struct pinctrl_pin_desc th1520_group3_pins[] = { + TH1520_PAD(0, UART0_TXD, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(1, UART0_RXD, UART, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(2, QSPI0_SCLK, QSPI, PWM, I2S, GPIO, ____, ____, 0), + TH1520_PAD(3, QSPI0_CSN0, QSPI, PWM, I2S, GPIO, ____, ____, 0), + TH1520_PAD(4, QSPI0_CSN1, QSPI, PWM, I2S, GPIO, ____, ____, 0), + TH1520_PAD(5, QSPI0_D0_MOSI, QSPI, PWM, I2S, GPIO, ____, ____, 0), + TH1520_PAD(6, QSPI0_D1_MISO, QSPI, PWM, I2S, GPIO, ____, ____, 0), + TH1520_PAD(7, QSPI0_D2_WP, QSPI, PWM, I2S, GPIO, ____, ____, 0), + TH1520_PAD(8, QSPI1_D3_HOLD, QSPI, ____, I2S, GPIO, ____, ____, 0), + TH1520_PAD(9, I2C2_SCL, I2C, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(10, I2C2_SDA, I2C, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(11, I2C3_SCL, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(12, I2C3_SDA, I2C, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(13, GPIO2_13, GPIO, SPI, ____, ____, ____, ____, 0), + TH1520_PAD(14, SPI_SCLK, SPI, UART, IR, GPIO, ____, ____, 0), + TH1520_PAD(15, SPI_CSN, SPI, UART, IR, GPIO, ____, ____, 0), + TH1520_PAD(16, SPI_MOSI, SPI, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(17, SPI_MISO, SPI, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(18, GPIO2_18, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(19, GPIO2_19, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(20, GPIO2_20, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(21, GPIO2_21, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(22, GPIO2_22, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(23, GPIO2_23, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(24, GPIO2_24, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(25, GPIO2_25, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(26, SDIO0_WPRTN, SDIO, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(27, SDIO0_DETN, SDIO, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(28, SDIO1_WPRTN, SDIO, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(29, SDIO1_DETN, SDIO, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(30, GPIO2_30, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(31, GPIO2_31, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(32, GPIO3_0, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(33, GPIO3_1, GPIO, MAC1, ____, ____, ____, ____, 0), + TH1520_PAD(34, GPIO3_2, GPIO, PWM, ____, ____, ____, ____, 0), + TH1520_PAD(35, GPIO3_3, GPIO, PWM, ____, ____, ____, ____, 0), + TH1520_PAD(36, HDMI_SCL, HDMI, PWM, ____, GPIO, ____, ____, 0), + TH1520_PAD(37, HDMI_SDA, HDMI, PWM, ____, GPIO, ____, ____, 0), + TH1520_PAD(38, HDMI_CEC, HDMI, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(39, GMAC0_TX_CLK, MAC0, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(40, GMAC0_RX_CLK, MAC0, ____, ____, GPIO, ____, ____, 0), + TH1520_PAD(41, GMAC0_TXEN, MAC0, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(42, GMAC0_TXD0, MAC0, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(43, GMAC0_TXD1, MAC0, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(44, GMAC0_TXD2, MAC0, UART, ____, GPIO, ____, ____, 0), + TH1520_PAD(45, GMAC0_TXD3, MAC0, I2C, ____, GPIO, ____, ____, 0), + TH1520_PAD(46, GMAC0_RXDV, MAC0, I2C, ____, GPIO, ____, ____, 0), + TH1520_PAD(47, GMAC0_RXD0, MAC0, I2C, ____, GPIO, ____, ____, 0), + TH1520_PAD(48, GMAC0_RXD1, MAC0, I2C, ____, GPIO, ____, ____, 0), + TH1520_PAD(49, GMAC0_RXD2, MAC0, SPI, ____, GPIO, ____, ____, 0), + TH1520_PAD(50, GMAC0_RXD3, MAC0, SPI, ____, GPIO, ____, ____, 0), + TH1520_PAD(51, GMAC0_MDC, MAC0, SPI, MAC1, GPIO, ____, ____, 0), + TH1520_PAD(52, GMAC0_MDIO, MAC0, SPI, MAC1, GPIO, ____, ____, 0), + TH1520_PAD(53, GMAC0_COL, MAC0, PWM, ____, GPIO, ____, ____, 0), + TH1520_PAD(54, GMAC0_CRS, MAC0, PWM, ____, GPIO, ____, ____, 0), +}; + +static const struct th1520_pad_group th1520_group1 = { + .name = "th1520-group1", + .pins = th1520_group1_pins, + .npins = ARRAY_SIZE(th1520_group1_pins), +}; + +static const struct th1520_pad_group th1520_group2 = { + .name = "th1520-group2", + .pins = th1520_group2_pins, + .npins = ARRAY_SIZE(th1520_group2_pins), +}; + +static const struct th1520_pad_group th1520_group3 = { + .name = "th1520-group3", + .pins = th1520_group3_pins, + .npins = ARRAY_SIZE(th1520_group3_pins), +}; + +static int th1520_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + return thp->desc.npins; +} + +static const char *th1520_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int gsel) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + return thp->desc.pins[gsel].name; +} + +static int th1520_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int gsel, + const unsigned int **pins, + unsigned int *npins) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + *pins = &thp->desc.pins[gsel].number; + *npins = 1; + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void th1520_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + void __iomem *padcfg = th1520_padcfg(thp, pin); + void __iomem *muxcfg = th1520_muxcfg(thp, pin); + u32 pad; + u32 mux; + + scoped_guard(raw_spinlock_irqsave, &thp->lock) { + pad = readl_relaxed(padcfg); + mux = readl_relaxed(muxcfg); + } + + seq_printf(s, "[PADCFG_%03u:0x%x=0x%07x MUXCFG_%03u:0x%x=0x%08x]", + 1 + pin / 2, 0x000 + 4 * (pin / 2), pad, + 1 + pin / 8, 0x400 + 4 * (pin / 8), mux); +} +#else +#define th1520_pin_dbg_show NULL +#endif + +static void th1520_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned int nmaps) +{ + unsigned long *seen = NULL; + unsigned int i; + + for (i = 0; i < nmaps; i++) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN && + map[i].data.configs.configs != seen) { + seen = map[i].data.configs.configs; + kfree(seen); + } + } + + kfree(map); +} + +static int th1520_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + struct pinctrl_map *map; + unsigned long *configs; + unsigned int nconfigs; + unsigned int nmaps; + int ret; + + nmaps = 0; + for_each_available_child_of_node_scoped(np, child) { + int npins = of_property_count_strings(child, "pins"); + + if (npins <= 0) { + dev_err(thp->pctl->dev, "no pins selected for %pOFn.%pOFn\n", + np, child); + return -EINVAL; + } + nmaps += npins; + if (of_property_present(child, "function")) + nmaps += npins; + } + + map = kcalloc(nmaps, sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + nmaps = 0; + guard(mutex)(&thp->mutex); + for_each_available_child_of_node_scoped(np, child) { + unsigned int rollback = nmaps; + enum th1520_muxtype muxtype; + struct property *prop; + const char *funcname; + const char **pgnames; + const char *pinname; + int npins; + + ret = pinconf_generic_parse_dt_config(child, pctldev, &configs, &nconfigs); + if (ret) { + dev_err(thp->pctl->dev, "%pOFn.%pOFn: error parsing pin config\n", + np, child); + goto free_map; + } + + if (!of_property_read_string(child, "function", &funcname)) { + muxtype = th1520_muxtype_get(funcname); + if (!muxtype) { + dev_err(thp->pctl->dev, "%pOFn.%pOFn: unknown function '%s'\n", + np, child, funcname); + ret = -EINVAL; + goto free_configs; + } + + funcname = devm_kasprintf(thp->pctl->dev, GFP_KERNEL, "%pOFn.%pOFn", + np, child); + if (!funcname) { + ret = -ENOMEM; + goto free_configs; + } + + npins = of_property_count_strings(child, "pins"); + pgnames = devm_kcalloc(thp->pctl->dev, npins, sizeof(*pgnames), GFP_KERNEL); + if (!pgnames) { + ret = -ENOMEM; + goto free_configs; + } + } else { + funcname = NULL; + } + + npins = 0; + of_property_for_each_string(child, "pins", prop, pinname) { + unsigned int i; + + for (i = 0; i < thp->desc.npins; i++) { + if (!strcmp(pinname, thp->desc.pins[i].name)) + break; + } + if (i == thp->desc.npins) { + nmaps = rollback; + dev_err(thp->pctl->dev, "%pOFn.%pOFn: unknown pin '%s'\n", + np, child, pinname); + ret = -EINVAL; + goto free_configs; + } + + if (nconfigs) { + map[nmaps].type = PIN_MAP_TYPE_CONFIGS_PIN; + map[nmaps].data.configs.group_or_pin = thp->desc.pins[i].name; + map[nmaps].data.configs.configs = configs; + map[nmaps].data.configs.num_configs = nconfigs; + nmaps += 1; + } + if (funcname) { + pgnames[npins++] = thp->desc.pins[i].name; + map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP; + map[nmaps].data.mux.function = funcname; + map[nmaps].data.mux.group = thp->desc.pins[i].name; + nmaps += 1; + } + } + + if (funcname) { + ret = pinmux_generic_add_function(pctldev, funcname, pgnames, + npins, (void *)muxtype); + if (ret < 0) { + dev_err(thp->pctl->dev, "error adding function %s\n", funcname); + goto free_map; + } + } + } + + *maps = map; + *num_maps = nmaps; + return 0; + +free_configs: + kfree(configs); +free_map: + th1520_pinctrl_dt_free_map(pctldev, map, nmaps); + return ret; +} + +static const struct pinctrl_ops th1520_pinctrl_ops = { + .get_groups_count = th1520_pinctrl_get_groups_count, + .get_group_name = th1520_pinctrl_get_group_name, + .get_group_pins = th1520_pinctrl_get_group_pins, + .pin_dbg_show = th1520_pin_dbg_show, + .dt_node_to_map = th1520_pinctrl_dt_node_to_map, + .dt_free_map = th1520_pinctrl_dt_free_map, +}; + +static const u8 th1520_drive_strength_in_ma[16] = { + 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 16, 18, 20, 21, 23, 25, +}; + +static u16 th1520_drive_strength_from_ma(u32 arg) +{ + u16 ds; + + for (ds = 0; ds < TH1520_PADCFG_DS; ds++) { + if (arg <= th1520_drive_strength_in_ma[ds]) + return ds; + } + return TH1520_PADCFG_DS; +} + +static int th1520_padcfg_rmw(struct th1520_pinctrl *thp, unsigned int pin, + u32 mask, u32 value) +{ + void __iomem *padcfg = th1520_padcfg(thp, pin); + unsigned int shift = th1520_padcfg_shift(pin); + u32 tmp; + + mask <<= shift; + value <<= shift; + + scoped_guard(raw_spinlock_irqsave, &thp->lock) { + tmp = readl_relaxed(padcfg); + tmp = (tmp & ~mask) | value; + writel_relaxed(tmp, padcfg); + } + return 0; +} + +static int th1520_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, pin); + bool enabled; + int param; + u32 value; + u32 arg; + + if (th1520_pad_no_padcfg(desc->drv_data)) + return -ENOTSUPP; + + value = readl_relaxed(th1520_padcfg(thp, pin)); + value = (value >> th1520_padcfg_shift(pin)) & GENMASK(9, 0); + + param = pinconf_to_config_param(*config); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + enabled = !(value & (TH1520_PADCFG_SPU | TH1520_PADCFG_PE)); + arg = 0; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + enabled = (value & TH1520_PADCFG_BIAS) == TH1520_PADCFG_PE; + arg = enabled ? TH1520_PULL_DOWN_OHM : 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (value & TH1520_PADCFG_SPU) { + enabled = true; + arg = TH1520_PULL_STRONG_OHM; + } else if ((value & (TH1520_PADCFG_PE | TH1520_PADCFG_PS)) == + (TH1520_PADCFG_PE | TH1520_PADCFG_PS)) { + enabled = true; + arg = TH1520_PULL_UP_OHM; + } else { + enabled = false; + arg = 0; + } + break; + case PIN_CONFIG_DRIVE_STRENGTH: + enabled = true; + arg = th1520_drive_strength_in_ma[value & TH1520_PADCFG_DS]; + break; + case PIN_CONFIG_INPUT_ENABLE: + enabled = value & TH1520_PADCFG_IE; + arg = enabled ? 1 : 0; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + enabled = value & TH1520_PADCFG_ST; + arg = enabled ? 1 : 0; + break; + case PIN_CONFIG_SLEW_RATE: + enabled = value & TH1520_PADCFG_SL; + arg = enabled ? 1 : 0; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return enabled ? 0 : -EINVAL; +} + +static int th1520_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int gsel, unsigned long *config) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + unsigned int pin = thp->desc.pins[gsel].number; + + return th1520_pinconf_get(pctldev, pin, config); +} + +static int th1520_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, pin); + unsigned int i; + u16 mask, value; + + if (th1520_pad_no_padcfg(desc->drv_data)) + return -ENOTSUPP; + + mask = 0; + value = 0; + for (i = 0; i < num_configs; i++) { + int param = pinconf_to_config_param(configs[i]); + u32 arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + mask |= TH1520_PADCFG_BIAS; + value &= ~TH1520_PADCFG_BIAS; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg == 0) + return -ENOTSUPP; + mask |= TH1520_PADCFG_BIAS; + value &= ~TH1520_PADCFG_BIAS; + value |= TH1520_PADCFG_PE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (arg == 0) + return -ENOTSUPP; + mask |= TH1520_PADCFG_BIAS; + value &= ~TH1520_PADCFG_BIAS; + if (arg == TH1520_PULL_STRONG_OHM) + value |= TH1520_PADCFG_SPU; + else + value |= TH1520_PADCFG_PE | TH1520_PADCFG_PS; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + mask |= TH1520_PADCFG_DS; + value &= ~TH1520_PADCFG_DS; + value |= th1520_drive_strength_from_ma(arg); + break; + case PIN_CONFIG_INPUT_ENABLE: + mask |= TH1520_PADCFG_IE; + if (arg) + value |= TH1520_PADCFG_IE; + else + value &= ~TH1520_PADCFG_IE; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask |= TH1520_PADCFG_ST; + if (arg) + value |= TH1520_PADCFG_ST; + else + value &= ~TH1520_PADCFG_ST; + break; + case PIN_CONFIG_SLEW_RATE: + mask |= TH1520_PADCFG_SL; + if (arg) + value |= TH1520_PADCFG_SL; + else + value &= ~TH1520_PADCFG_SL; + break; + default: + return -ENOTSUPP; + } + } + + return th1520_padcfg_rmw(thp, pin, mask, value); +} + +static int th1520_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int gsel, + unsigned long *configs, + unsigned int num_configs) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + unsigned int pin = thp->desc.pins[gsel].number; + + return th1520_pinconf_set(pctldev, pin, configs, num_configs); +} + +#ifdef CONFIG_DEBUG_FS +static void th1520_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + u32 value = readl_relaxed(th1520_padcfg(thp, pin)); + + value = (value >> th1520_padcfg_shift(pin)) & GENMASK(9, 0); + + seq_printf(s, " [0x%03x]", value); +} +#else +#define th1520_pinconf_dbg_show NULL +#endif + +static const struct pinconf_ops th1520_pinconf_ops = { + .pin_config_get = th1520_pinconf_get, + .pin_config_group_get = th1520_pinconf_group_get, + .pin_config_set = th1520_pinconf_set, + .pin_config_group_set = th1520_pinconf_group_set, + .pin_config_dbg_show = th1520_pinconf_dbg_show, + .is_generic = true, +}; + +static int th1520_pinmux_set(struct th1520_pinctrl *thp, unsigned int pin, + unsigned long muxdata, enum th1520_muxtype muxtype) +{ + void __iomem *muxcfg = th1520_muxcfg(thp, pin); + unsigned int shift = th1520_muxcfg_shift(pin); + u32 mask, value, tmp; + + for (value = 0; muxdata; muxdata >>= 5, value++) { + if ((muxdata & GENMASK(4, 0)) == muxtype) + break; + } + if (!muxdata) { + dev_err(thp->pctl->dev, "invalid mux %s for pin %s\n", + th1520_muxtype_string[muxtype], pin_get_name(thp->pctl, pin)); + return -EINVAL; + } + + mask = GENMASK(3, 0) << shift; + value = value << shift; + + scoped_guard(raw_spinlock_irqsave, &thp->lock) { + tmp = readl_relaxed(muxcfg); + tmp = (tmp & ~mask) | value; + writel_relaxed(tmp, muxcfg); + } + return 0; +} + +static int th1520_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int fsel, unsigned int gsel) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + const struct function_desc *func = pinmux_generic_get_function(pctldev, fsel); + enum th1520_muxtype muxtype; + + if (!func) + return -EINVAL; + + muxtype = (uintptr_t)func->data; + return th1520_pinmux_set(thp, thp->desc.pins[gsel].number, + th1520_pad_muxdata(thp->desc.pins[gsel].drv_data), + muxtype); +} + +static int th1520_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, offset); + + return th1520_pinmux_set(thp, offset, + th1520_pad_muxdata(desc->drv_data), + TH1520_MUX_GPIO); +} + +static int th1520_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + struct th1520_pinctrl *thp = pinctrl_dev_get_drvdata(pctldev); + + return th1520_padcfg_rmw(thp, offset, TH1520_PADCFG_IE, + input ? TH1520_PADCFG_IE : 0); +} + +static const struct pinmux_ops th1520_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = th1520_pinmux_set_mux, + .gpio_request_enable = th1520_gpio_request_enable, + .gpio_set_direction = th1520_gpio_set_direction, + .strict = true, +}; + +static int th1520_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct th1520_pad_group *group; + struct device_node *np = dev->of_node; + struct th1520_pinctrl *thp; + struct clk *clk; + u32 pin_group; + int ret; + + thp = devm_kzalloc(dev, sizeof(*thp), GFP_KERNEL); + if (!thp) + return -ENOMEM; + + thp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(thp->base)) + return PTR_ERR(thp->base); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "error getting clock\n"); + + ret = of_property_read_u32(np, "thead,pad-group", &pin_group); + if (ret) + return dev_err_probe(dev, ret, "failed to read the thead,pad-group property\n"); + + if (pin_group == 1) + group = &th1520_group1; + else if (pin_group == 2) + group = &th1520_group2; + else if (pin_group == 3) + group = &th1520_group3; + else + return dev_err_probe(dev, -EINVAL, "unit address did not match any pad group\n"); + + thp->desc.name = group->name; + thp->desc.pins = group->pins; + thp->desc.npins = group->npins; + thp->desc.pctlops = &th1520_pinctrl_ops; + thp->desc.pmxops = &th1520_pinmux_ops; + thp->desc.confops = &th1520_pinconf_ops; + thp->desc.owner = THIS_MODULE; + mutex_init(&thp->mutex); + raw_spin_lock_init(&thp->lock); + + ret = devm_pinctrl_register_and_init(dev, &thp->desc, thp, &thp->pctl); + if (ret) + return dev_err_probe(dev, ret, "could not register pinctrl driver\n"); + + return pinctrl_enable(thp->pctl); +} + +static const struct of_device_id th1520_pinctrl_of_match[] = { + { .compatible = "thead,th1520-pinctrl"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, th1520_pinctrl_of_match); + +static struct platform_driver th1520_pinctrl_driver = { + .probe = th1520_pinctrl_probe, + .driver = { + .name = "pinctrl-th1520", + .of_match_table = th1520_pinctrl_of_match, + }, +}; +module_platform_driver(th1520_pinctrl_driver); + +MODULE_DESCRIPTION("Pinctrl driver for the T-Head TH1520 SoC"); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c index f4256a918165f..48f8aabf3bfa3 100644 --- a/drivers/pinctrl/pinctrl-xway.c +++ b/drivers/pinctrl/pinctrl-xway.c @@ -1524,7 +1524,7 @@ static int pinmux_xway_probe(struct platform_device *pdev) * files which don't set the "gpio-ranges" property or systems that * utilize ACPI the driver has to call gpiochip_add_pin_range(). */ - if (!of_property_read_bool(pdev->dev.of_node, "gpio-ranges")) { + if (!of_property_present(pdev->dev.of_node, "gpio-ranges")) { /* finish with registering the gpio range in pinctrl */ xway_gpio_range.npins = xway_chip.ngpio; xway_gpio_range.base = xway_chip.base; diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c index 3c6d56fdb8c96..fddf0fef4b13b 100644 --- a/drivers/pinctrl/pinctrl-zynqmp.c +++ b/drivers/pinctrl/pinctrl-zynqmp.c @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -44,12 +45,17 @@ #define DRIVE_STRENGTH_8MA 8 #define DRIVE_STRENGTH_12MA 12 +#define VERSAL_LPD_PIN_PREFIX "LPD_MIO" +#define VERSAL_PMC_PIN_PREFIX "PMC_MIO" + +#define VERSAL_PINCTRL_ATTR_NODETYPE_MASK GENMASK(19, 14) +#define VERSAL_PINCTRL_NODETYPE_LPD_MIO BIT(0) + /** * struct zynqmp_pmux_function - a pinmux function * @name: Name of the pin mux function * @groups: List of pin groups for this function * @ngroups: Number of entries in @groups - * @node: Firmware node matching with the function * * This structure holds information about pin control function * and function group names supporting that function. @@ -93,6 +99,8 @@ struct zynqmp_pctrl_group { }; static struct pinctrl_desc zynqmp_desc; +static u32 family_code; +static u32 sub_family_code; static int zynqmp_pctrl_get_groups_count(struct pinctrl_dev *pctldev) { @@ -596,8 +604,12 @@ static int zynqmp_pinctrl_prepare_func_groups(struct device *dev, u32 fid, if (!groups[resp[i]].name) return -ENOMEM; - for (pin = 0; pin < groups[resp[i]].npins; pin++) - __set_bit(groups[resp[i]].pins[pin], used_pins); + for (pin = 0; pin < groups[resp[i]].npins; pin++) { + if (family_code == ZYNQMP_FAMILY_CODE) + __set_bit(groups[resp[i]].pins[pin], used_pins); + else + __set_bit((u8)groups[resp[i]].pins[pin] - 1, used_pins); + } } } done: @@ -873,6 +885,70 @@ static int zynqmp_pinctrl_prepare_pin_desc(struct device *dev, return 0; } +static int versal_pinctrl_get_attributes(u32 pin_idx, u32 *response) +{ + struct zynqmp_pm_query_data qdata = {0}; + u32 payload[PAYLOAD_ARG_CNT]; + int ret; + + qdata.qid = PM_QID_PINCTRL_GET_ATTRIBUTES; + qdata.arg1 = pin_idx; + + ret = zynqmp_pm_query_data(qdata, payload); + if (ret) + return ret; + + memcpy(response, &payload[1], sizeof(*response)); + + return 0; +} + +static int versal_pinctrl_prepare_pin_desc(struct device *dev, + const struct pinctrl_pin_desc **zynqmp_pins, + unsigned int *npins) +{ + u32 lpd_mio_pins = 0, attr, nodetype; + struct pinctrl_pin_desc *pins, *pin; + int ret, i; + + ret = zynqmp_pm_is_function_supported(PM_QUERY_DATA, PM_QID_PINCTRL_GET_ATTRIBUTES); + if (ret) + return ret; + + ret = zynqmp_pinctrl_get_num_pins(npins); + if (ret) + return ret; + + pins = devm_kzalloc(dev, sizeof(*pins) * *npins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < *npins; i++) { + ret = versal_pinctrl_get_attributes(i, &attr); + if (ret) + return ret; + + pin = &pins[i]; + pin->number = attr; + nodetype = FIELD_GET(VERSAL_PINCTRL_ATTR_NODETYPE_MASK, attr); + if (nodetype == VERSAL_PINCTRL_NODETYPE_LPD_MIO) { + pin->name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", + VERSAL_LPD_PIN_PREFIX, i); + lpd_mio_pins++; + } else { + pin->name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", + VERSAL_PMC_PIN_PREFIX, i - lpd_mio_pins); + } + + if (!pin->name) + return -ENOMEM; + } + + *zynqmp_pins = pins; + + return 0; +} + static int zynqmp_pinctrl_probe(struct platform_device *pdev) { struct zynqmp_pinctrl *pctrl; @@ -882,9 +958,18 @@ static int zynqmp_pinctrl_probe(struct platform_device *pdev) if (!pctrl) return -ENOMEM; - ret = zynqmp_pinctrl_prepare_pin_desc(&pdev->dev, - &zynqmp_desc.pins, - &zynqmp_desc.npins); + ret = zynqmp_pm_get_family_info(&family_code, &sub_family_code); + if (ret < 0) + return ret; + + if (family_code == ZYNQMP_FAMILY_CODE) { + ret = zynqmp_pinctrl_prepare_pin_desc(&pdev->dev, &zynqmp_desc.pins, + &zynqmp_desc.npins); + } else { + ret = versal_pinctrl_prepare_pin_desc(&pdev->dev, &zynqmp_desc.pins, + &zynqmp_desc.npins); + } + if (ret) { dev_err(&pdev->dev, "pin desc prepare fail with %d\n", ret); return ret; @@ -907,6 +992,7 @@ static int zynqmp_pinctrl_probe(struct platform_device *pdev) static const struct of_device_id zynqmp_pinctrl_of_match[] = { { .compatible = "xlnx,zynqmp-pinctrl" }, + { .compatible = "xlnx,versal-pinctrl" }, { } }; MODULE_DEVICE_TABLE(of, zynqmp_pinctrl_of_match); diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 02033ea1c6438..0743190da59e8 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -93,6 +94,7 @@ bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned int pin) if (!desc || !ops) return true; + guard(mutex)(&desc->mux_lock); if (ops->strict && desc->mux_usecount) return false; @@ -127,29 +129,31 @@ static int pin_request(struct pinctrl_dev *pctldev, dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n", pin, desc->name, owner); - if ((!gpio_range || ops->strict) && - desc->mux_usecount && strcmp(desc->mux_owner, owner)) { - dev_err(pctldev->dev, - "pin %s already requested by %s; cannot claim for %s\n", - desc->name, desc->mux_owner, owner); - goto out; - } + scoped_guard(mutex, &desc->mux_lock) { + if ((!gpio_range || ops->strict) && + desc->mux_usecount && strcmp(desc->mux_owner, owner)) { + dev_err(pctldev->dev, + "pin %s already requested by %s; cannot claim for %s\n", + desc->name, desc->mux_owner, owner); + goto out; + } - if ((gpio_range || ops->strict) && desc->gpio_owner) { - dev_err(pctldev->dev, - "pin %s already requested by %s; cannot claim for %s\n", - desc->name, desc->gpio_owner, owner); - goto out; - } + if ((gpio_range || ops->strict) && desc->gpio_owner) { + dev_err(pctldev->dev, + "pin %s already requested by %s; cannot claim for %s\n", + desc->name, desc->gpio_owner, owner); + goto out; + } - if (gpio_range) { - desc->gpio_owner = owner; - } else { - desc->mux_usecount++; - if (desc->mux_usecount > 1) - return 0; + if (gpio_range) { + desc->gpio_owner = owner; + } else { + desc->mux_usecount++; + if (desc->mux_usecount > 1) + return 0; - desc->mux_owner = owner; + desc->mux_owner = owner; + } } /* Let each pin increase references to this module */ @@ -178,12 +182,14 @@ static int pin_request(struct pinctrl_dev *pctldev, out_free_pin: if (status) { - if (gpio_range) { - desc->gpio_owner = NULL; - } else { - desc->mux_usecount--; - if (!desc->mux_usecount) - desc->mux_owner = NULL; + scoped_guard(mutex, &desc->mux_lock) { + if (gpio_range) { + desc->gpio_owner = NULL; + } else { + desc->mux_usecount--; + if (!desc->mux_usecount) + desc->mux_owner = NULL; + } } } out: @@ -219,15 +225,17 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, return NULL; } - if (!gpio_range) { - /* - * A pin should not be freed more times than allocated. - */ - if (WARN_ON(!desc->mux_usecount)) - return NULL; - desc->mux_usecount--; - if (desc->mux_usecount) - return NULL; + scoped_guard(mutex, &desc->mux_lock) { + if (!gpio_range) { + /* + * A pin should not be freed more times than allocated. + */ + if (WARN_ON(!desc->mux_usecount)) + return NULL; + desc->mux_usecount--; + if (desc->mux_usecount) + return NULL; + } } /* @@ -239,13 +247,15 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, else if (ops->free) ops->free(pctldev, pin); - if (gpio_range) { - owner = desc->gpio_owner; - desc->gpio_owner = NULL; - } else { - owner = desc->mux_owner; - desc->mux_owner = NULL; - desc->mux_setting = NULL; + scoped_guard(mutex, &desc->mux_lock) { + if (gpio_range) { + owner = desc->gpio_owner; + desc->gpio_owner = NULL; + } else { + owner = desc->mux_owner; + desc->mux_owner = NULL; + desc->mux_setting = NULL; + } } module_put(pctldev->owner); @@ -458,7 +468,8 @@ int pinmux_enable_setting(const struct pinctrl_setting *setting) pins[i]); continue; } - desc->mux_setting = &(setting->data.mux); + scoped_guard(mutex, &desc->mux_lock) + desc->mux_setting = &(setting->data.mux); } ret = ops->set_mux(pctldev, setting->data.mux.func, @@ -472,8 +483,10 @@ int pinmux_enable_setting(const struct pinctrl_setting *setting) err_set_mux: for (i = 0; i < num_pins; i++) { desc = pin_desc_get(pctldev, pins[i]); - if (desc) - desc->mux_setting = NULL; + if (desc) { + scoped_guard(mutex, &desc->mux_lock) + desc->mux_setting = NULL; + } } err_pin_request: /* On error release all taken pins */ @@ -492,6 +505,7 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting) unsigned int num_pins = 0; int i; struct pin_desc *desc; + bool is_equal; if (pctlops->get_group_pins) ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, @@ -517,7 +531,10 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting) pins[i]); continue; } - if (desc->mux_setting == &(setting->data.mux)) { + scoped_guard(mutex, &desc->mux_lock) + is_equal = (desc->mux_setting == &(setting->data.mux)); + + if (is_equal) { pin_free(pctldev, pins[i], NULL); } else { const char *gname; @@ -608,40 +625,42 @@ static int pinmux_pins_show(struct seq_file *s, void *what) if (desc == NULL) continue; - if (desc->mux_owner && - !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev))) - is_hog = true; - - if (pmxops->strict) { - if (desc->mux_owner) - seq_printf(s, "pin %d (%s): device %s%s", - pin, desc->name, desc->mux_owner, + scoped_guard(mutex, &desc->mux_lock) { + if (desc->mux_owner && + !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev))) + is_hog = true; + + if (pmxops->strict) { + if (desc->mux_owner) + seq_printf(s, "pin %d (%s): device %s%s", + pin, desc->name, desc->mux_owner, + is_hog ? " (HOG)" : ""); + else if (desc->gpio_owner) + seq_printf(s, "pin %d (%s): GPIO %s", + pin, desc->name, desc->gpio_owner); + else + seq_printf(s, "pin %d (%s): UNCLAIMED", + pin, desc->name); + } else { + /* For non-strict controllers */ + seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name, + desc->mux_owner ? desc->mux_owner + : "(MUX UNCLAIMED)", + desc->gpio_owner ? desc->gpio_owner + : "(GPIO UNCLAIMED)", is_hog ? " (HOG)" : ""); - else if (desc->gpio_owner) - seq_printf(s, "pin %d (%s): GPIO %s", - pin, desc->name, desc->gpio_owner); + } + + /* If mux: print function+group claiming the pin */ + if (desc->mux_setting) + seq_printf(s, " function %s group %s\n", + pmxops->get_function_name(pctldev, + desc->mux_setting->func), + pctlops->get_group_name(pctldev, + desc->mux_setting->group)); else - seq_printf(s, "pin %d (%s): UNCLAIMED", - pin, desc->name); - } else { - /* For non-strict controllers */ - seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name, - desc->mux_owner ? desc->mux_owner - : "(MUX UNCLAIMED)", - desc->gpio_owner ? desc->gpio_owner - : "(GPIO UNCLAIMED)", - is_hog ? " (HOG)" : ""); + seq_putc(s, '\n'); } - - /* If mux: print function+group claiming the pin */ - if (desc->mux_setting) - seq_printf(s, " function %s group %s\n", - pmxops->get_function_name(pctldev, - desc->mux_setting->func), - pctlops->get_group_name(pctldev, - desc->mux_setting->group)); - else - seq_putc(s, '\n'); } mutex_unlock(&pctldev->mutex); diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 8fe459d082ede..206226318e452 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -46,6 +46,15 @@ config PINCTRL_IPQ5332 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc IPQ5332 platform. +config PINCTRL_IPQ5424 + tristate "Qualcomm Technologies, Inc. IPQ5424 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for + the Qualcomm Technologies Inc. TLMM block found on the + Qualcomm Technologies Inc. IPQ5424 platform. Select this for + IPQ5424. + config PINCTRL_IPQ8074 tristate "Qualcomm Technologies, Inc. IPQ8074 pin controller driver" depends on ARM64 || COMPILE_TEST @@ -182,6 +191,20 @@ config PINCTRL_QCS404 This is the pinctrl, pinmux, pinconf and gpiolib driver for the TLMM block found in the Qualcomm QCS404 platform. +config PINCTRL_QCS615 + tristate "Qualcomm Technologies QCS615 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + TLMM block found on the Qualcomm QCS615 platform. + +config PINCTRL_QCS8300 + tristate "Qualcomm Technologies QCS8300 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux and pinconf driver for the Qualcomm + TLMM block found on the Qualcomm QCS8300 platform. + config PINCTRL_QDF2XXX tristate "Qualcomm Technologies QDF2xxx pin controller driver" depends on ACPI @@ -204,6 +227,14 @@ config PINCTRL_SA8775P This is the pinctrl, pinmux and pinconf driver for the Qualcomm TLMM block found on the Qualcomm SA8775P platforms. +config PINCTRL_SAR2130P + tristate "Qualcomm Technologies Inc SAR2130P pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc TLMM block found on the Qualcomm + Technologies Inc SAR2130P platform. + config PINCTRL_SC7180 tristate "Qualcomm Technologies Inc SC7180 pin controller driver" depends on ARM64 || COMPILE_TEST @@ -382,6 +413,14 @@ config PINCTRL_SM8650 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SM8650 platform. +config PINCTRL_SM8750 + tristate "Qualcomm Technologies Inc SM8750 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc TLMM block found on the Qualcomm + Technologies Inc SM8750 platform. + config PINCTRL_X1E80100 tristate "Qualcomm Technologies Inc X1E80100 pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index eb04297b63889..9a23d41d801c4 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_PINCTRL_IPQ4019) += pinctrl-ipq4019.o obj-$(CONFIG_PINCTRL_IPQ5018) += pinctrl-ipq5018.o obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o obj-$(CONFIG_PINCTRL_IPQ5332) += pinctrl-ipq5332.o +obj-$(CONFIG_PINCTRL_IPQ5424) += pinctrl-ipq5424.o obj-$(CONFIG_PINCTRL_IPQ8074) += pinctrl-ipq8074.o obj-$(CONFIG_PINCTRL_IPQ6018) += pinctrl-ipq6018.o obj-$(CONFIG_PINCTRL_IPQ9574) += pinctrl-ipq9574.o @@ -23,6 +24,8 @@ obj-$(CONFIG_PINCTRL_MSM8996) += pinctrl-msm8996.o obj-$(CONFIG_PINCTRL_MSM8998) += pinctrl-msm8998.o obj-$(CONFIG_PINCTRL_QCM2290) += pinctrl-qcm2290.o obj-$(CONFIG_PINCTRL_QCS404) += pinctrl-qcs404.o +obj-$(CONFIG_PINCTRL_QCS615) += pinctrl-qcs615.o +obj-$(CONFIG_PINCTRL_QCS8300) += pinctrl-qcs8300.o obj-$(CONFIG_PINCTRL_QDF2XXX) += pinctrl-qdf2xxx.o obj-$(CONFIG_PINCTRL_MDM9607) += pinctrl-mdm9607.o obj-$(CONFIG_PINCTRL_MDM9615) += pinctrl-mdm9615.o @@ -32,6 +35,7 @@ obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o obj-$(CONFIG_PINCTRL_QDU1000) += pinctrl-qdu1000.o obj-$(CONFIG_PINCTRL_SA8775P) += pinctrl-sa8775p.o +obj-$(CONFIG_PINCTRL_SAR2130P) += pinctrl-sar2130p.o obj-$(CONFIG_PINCTRL_SC7180) += pinctrl-sc7180.o obj-$(CONFIG_PINCTRL_SC7280) += pinctrl-sc7280.o obj-$(CONFIG_PINCTRL_SC7280_LPASS_LPI) += pinctrl-sc7280-lpass-lpi.o @@ -62,6 +66,7 @@ obj-$(CONFIG_PINCTRL_SM8550) += pinctrl-sm8550.o obj-$(CONFIG_PINCTRL_SM8550_LPASS_LPI) += pinctrl-sm8550-lpass-lpi.o obj-$(CONFIG_PINCTRL_SM8650) += pinctrl-sm8650.o obj-$(CONFIG_PINCTRL_SM8650_LPASS_LPI) += pinctrl-sm8650-lpass-lpi.o +obj-$(CONFIG_PINCTRL_SM8750) += pinctrl-sm8750.o obj-$(CONFIG_PINCTRL_SC8280XP_LPASS_LPI) += pinctrl-sc8280xp-lpass-lpi.o obj-$(CONFIG_PINCTRL_LPASS_LPI) += pinctrl-lpass-lpi.o obj-$(CONFIG_PINCTRL_X1E80100) += pinctrl-x1e80100.o diff --git a/drivers/pinctrl/qcom/pinctrl-apq8064.c b/drivers/pinctrl/qcom/pinctrl-apq8064.c index a18df41622999..20c3b90250445 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8064.c @@ -629,7 +629,7 @@ static struct platform_driver apq8064_pinctrl_driver = { .of_match_table = apq8064_pinctrl_of_match, }, .probe = apq8064_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init apq8064_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-apq8084.c b/drivers/pinctrl/qcom/pinctrl-apq8084.c index afada80e52a23..3fc0a40762b63 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8084.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8084.c @@ -1207,7 +1207,7 @@ static struct platform_driver apq8084_pinctrl_driver = { .of_match_table = apq8084_pinctrl_of_match, }, .probe = apq8084_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init apq8084_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c index cb13576ad6cfb..1f7944dd829d1 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c @@ -710,7 +710,7 @@ static struct platform_driver ipq4019_pinctrl_driver = { .of_match_table = ipq4019_pinctrl_of_match, }, .probe = ipq4019_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq4019_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5018.c b/drivers/pinctrl/qcom/pinctrl-ipq5018.c index 68f65b57003e9..e2951f81c3eeb 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq5018.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq5018.c @@ -754,7 +754,7 @@ static struct platform_driver ipq5018_pinctrl_driver = { .of_match_table = ipq5018_pinctrl_of_match, }, .probe = ipq5018_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq5018_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5332.c b/drivers/pinctrl/qcom/pinctrl-ipq5332.c index 8821751189708..625f8014051f6 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq5332.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq5332.c @@ -834,7 +834,7 @@ static struct platform_driver ipq5332_pinctrl_driver = { .of_match_table = ipq5332_pinctrl_of_match, }, .probe = ipq5332_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq5332_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5424.c b/drivers/pinctrl/qcom/pinctrl-ipq5424.c new file mode 100644 index 0000000000000..796299cd2e4ed --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-ipq5424.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2018,2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + }, \ + .nfuncs = 10, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +static const struct pinctrl_pin_desc ipq5424_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); + +enum ipq5424_functions { + msm_mux_atest_char, + msm_mux_atest_char0, + msm_mux_atest_char1, + msm_mux_atest_char2, + msm_mux_atest_char3, + msm_mux_atest_tic, + msm_mux_audio_pri, + msm_mux_audio_pri0, + msm_mux_audio_pri1, + msm_mux_audio_sec, + msm_mux_audio_sec0, + msm_mux_audio_sec1, + msm_mux_core_voltage, + msm_mux_cri_trng0, + msm_mux_cri_trng1, + msm_mux_cri_trng2, + msm_mux_cri_trng3, + msm_mux_cxc_clk, + msm_mux_cxc_data, + msm_mux_dbg_out, + msm_mux_gcc_plltest, + msm_mux_gcc_tlmm, + msm_mux_gpio, + msm_mux_i2c0_scl, + msm_mux_i2c0_sda, + msm_mux_i2c1_scl, + msm_mux_i2c1_sda, + msm_mux_i2c11, + msm_mux_mac0, + msm_mux_mac1, + msm_mux_mdc_mst, + msm_mux_mdc_slv, + msm_mux_mdio_mst, + msm_mux_mdio_slv, + msm_mux_pcie0_clk, + msm_mux_pcie0_wake, + msm_mux_pcie1_clk, + msm_mux_pcie1_wake, + msm_mux_pcie2_clk, + msm_mux_pcie2_wake, + msm_mux_pcie3_clk, + msm_mux_pcie3_wake, + msm_mux_pll_test, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_PTA0_0, + msm_mux_PTA0_1, + msm_mux_PTA0_2, + msm_mux_PTA10, + msm_mux_PTA11, + msm_mux_pwm0, + msm_mux_pwm1, + msm_mux_pwm2, + msm_mux_qdss_cti_trig_in_a0, + msm_mux_qdss_cti_trig_out_a0, + msm_mux_qdss_cti_trig_in_a1, + msm_mux_qdss_cti_trig_out_a1, + msm_mux_qdss_cti_trig_in_b0, + msm_mux_qdss_cti_trig_out_b0, + msm_mux_qdss_cti_trig_in_b1, + msm_mux_qdss_cti_trig_out_b1, + msm_mux_qdss_traceclk_a, + msm_mux_qdss_tracectl_a, + msm_mux_qdss_tracedata_a, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_qspi_data, + msm_mux_resout, + msm_mux_rx0, + msm_mux_rx1, + msm_mux_rx2, + msm_mux_sdc_clk, + msm_mux_sdc_cmd, + msm_mux_sdc_data, + msm_mux_spi0, + msm_mux_spi1, + msm_mux_spi10, + msm_mux_spi11, + msm_mux_tsens_max, + msm_mux_uart0, + msm_mux_uart1, + msm_mux_wci_txd, + msm_mux_wci_rxd, + msm_mux_wsi_clk, + msm_mux_wsi_data, + msm_mux__, +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", +}; + +static const char * const sdc_data_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char * const qspi_data_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char * const pwm2_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char * const wci_txd_groups[] = { + "gpio0", "gpio1", "gpio8", "gpio10", "gpio11", "gpio40", "gpio41", +}; + +static const char * const wci_rxd_groups[] = { + "gpio0", "gpio1", "gpio8", "gpio10", "gpio11", "gpio40", "gpio41", +}; + +static const char * const sdc_cmd_groups[] = { + "gpio4", +}; + +static const char * const qspi_cs_groups[] = { + "gpio4", +}; + +static const char * const qdss_cti_trig_out_a1_groups[] = { + "gpio27", +}; + +static const char * const sdc_clk_groups[] = { + "gpio5", +}; + +static const char * const qspi_clk_groups[] = { + "gpio5", +}; + +static const char * const spi0_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", +}; + +static const char * const pwm1_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", +}; + +static const char * const cri_trng0_groups[] = { + "gpio6", +}; + +static const char * const qdss_tracedata_a_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", + "gpio13", "gpio14", "gpio15", "gpio20", "gpio21", "gpio36", "gpio37", + "gpio38", "gpio39", +}; + +static const char * const cri_trng1_groups[] = { + "gpio7", +}; + +static const char * const cri_trng2_groups[] = { + "gpio8", +}; + +static const char * const cri_trng3_groups[] = { + "gpio9", +}; + +static const char * const uart0_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio13", +}; + +static const char * const pwm0_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio13", +}; + +static const char * const prng_rosc0_groups[] = { + "gpio12", +}; + +static const char * const prng_rosc1_groups[] = { + "gpio13", +}; + +static const char * const i2c0_scl_groups[] = { + "gpio14", +}; + +static const char * const tsens_max_groups[] = { + "gpio14", +}; + +static const char * const prng_rosc2_groups[] = { + "gpio14", +}; + +static const char * const i2c0_sda_groups[] = { + "gpio15", +}; + +static const char * const prng_rosc3_groups[] = { + "gpio15", +}; + +static const char * const core_voltage_groups[] = { + "gpio16", "gpio17", +}; + +static const char * const i2c1_scl_groups[] = { + "gpio16", +}; + +static const char * const i2c1_sda_groups[] = { + "gpio17", +}; + +static const char * const mdc_slv_groups[] = { + "gpio20", +}; + +static const char * const atest_char0_groups[] = { + "gpio20", +}; + +static const char * const mdio_slv_groups[] = { + "gpio21", +}; + +static const char * const atest_char1_groups[] = { + "gpio21", +}; + +static const char * const mdc_mst_groups[] = { + "gpio22", +}; + +static const char * const atest_char2_groups[] = { + "gpio22", +}; + +static const char * const mdio_mst_groups[] = { + "gpio23", +}; + +static const char * const atest_char3_groups[] = { + "gpio23", +}; + +static const char * const pcie0_clk_groups[] = { + "gpio24", +}; + +static const char * const PTA10_groups[] = { + "gpio24", "gpio26", "gpio27", +}; + +static const char * const mac0_groups[] = { + "gpio24", "gpio26", +}; + +static const char * const atest_char_groups[] = { + "gpio24", +}; + +static const char * const pcie0_wake_groups[] = { + "gpio26", +}; + +static const char * const pcie1_clk_groups[] = { + "gpio27", +}; + +static const char * const i2c11_groups[] = { + "gpio27", "gpio29", +}; + +static const char * const pcie1_wake_groups[] = { + "gpio29", +}; + +static const char * const pcie2_clk_groups[] = { + "gpio30", +}; + +static const char * const mac1_groups[] = { + "gpio30", "gpio32", +}; + +static const char * const pcie2_wake_groups[] = { + "gpio32", +}; + +static const char * const PTA11_groups[] = { + "gpio30", "gpio32", "gpio33", +}; + +static const char * const audio_pri0_groups[] = { + "gpio32", "gpio32", +}; + +static const char * const pcie3_clk_groups[] = { + "gpio33", +}; + +static const char * const audio_pri1_groups[] = { + "gpio33", "gpio33", +}; + +static const char * const pcie3_wake_groups[] = { + "gpio35", +}; + +static const char * const audio_sec1_groups[] = { + "gpio35", "gpio35", +}; + +static const char * const audio_pri_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char * const spi1_groups[] = { + "gpio11", "gpio36", "gpio37", "gpio38", "gpio46", +}; + +static const char * const audio_sec0_groups[] = { + "gpio36", "gpio36", +}; + +static const char * const rx1_groups[] = { + "gpio38", "gpio46", +}; + +static const char * const pll_test_groups[] = { + "gpio38", +}; + +static const char * const dbg_out_groups[] = { + "gpio46", +}; + +static const char * const PTA0_0_groups[] = { + "gpio40", +}; + +static const char * const atest_tic_groups[] = { + "gpio40", +}; + +static const char * const PTA0_1_groups[] = { + "gpio41", +}; + +static const char * const cxc_data_groups[] = { + "gpio41", +}; + +static const char * const PTA0_2_groups[] = { + "gpio42", +}; + +static const char * const cxc_clk_groups[] = { + "gpio42", +}; + +static const char * const uart1_groups[] = { + "gpio43", "gpio44", +}; + +static const char * const audio_sec_groups[] = { + "gpio45", "gpio46", "gpio47", "gpio48", +}; + +static const char * const gcc_plltest_groups[] = { + "gpio43", "gpio45", +}; + +static const char * const gcc_tlmm_groups[] = { + "gpio44", +}; + +static const char * const qdss_cti_trig_out_b1_groups[] = { + "gpio33", +}; + +static const char * const rx0_groups[] = { + "gpio39", "gpio47", +}; + +static const char * const qdss_traceclk_a_groups[] = { + "gpio45", +}; + +static const char * const qdss_tracectl_a_groups[] = { + "gpio46", +}; + +static const char * const qdss_cti_trig_out_a0_groups[] = { + "gpio24", +}; + +static const char * const qdss_cti_trig_in_a0_groups[] = { + "gpio26", +}; + +static const char * const resout_groups[] = { + "gpio49", +}; + +static const char * const qdss_cti_trig_in_a1_groups[] = { + "gpio29", +}; + +static const char * const qdss_cti_trig_out_b0_groups[] = { + "gpio30", +}; + +static const char * const qdss_cti_trig_in_b0_groups[] = { + "gpio32", +}; + +static const char * const qdss_cti_trig_in_b1_groups[] = { + "gpio35", +}; + +static const char * const spi10_groups[] = { + "gpio45", "gpio47", "gpio48", +}; + +static const char * const spi11_groups[] = { + "gpio10", "gpio12", "gpio13", +}; + +static const char * const wsi_clk_groups[] = { + "gpio24", "gpio27", +}; + +static const char * const wsi_data_groups[] = { + "gpio26", "gpio29", +}; + +static const char * const rx2_groups[] = { + "gpio37", "gpio45", +}; + +static const struct pinfunction ipq5424_functions[] = { + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_char0), + MSM_PIN_FUNCTION(atest_char1), + MSM_PIN_FUNCTION(atest_char2), + MSM_PIN_FUNCTION(atest_char3), + MSM_PIN_FUNCTION(atest_tic), + MSM_PIN_FUNCTION(audio_pri), + MSM_PIN_FUNCTION(audio_pri0), + MSM_PIN_FUNCTION(audio_pri1), + MSM_PIN_FUNCTION(audio_sec), + MSM_PIN_FUNCTION(audio_sec0), + MSM_PIN_FUNCTION(audio_sec1), + MSM_PIN_FUNCTION(core_voltage), + MSM_PIN_FUNCTION(cri_trng0), + MSM_PIN_FUNCTION(cri_trng1), + MSM_PIN_FUNCTION(cri_trng2), + MSM_PIN_FUNCTION(cri_trng3), + MSM_PIN_FUNCTION(cxc_clk), + MSM_PIN_FUNCTION(cxc_data), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(gcc_plltest), + MSM_PIN_FUNCTION(gcc_tlmm), + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(i2c0_scl), + MSM_PIN_FUNCTION(i2c0_sda), + MSM_PIN_FUNCTION(i2c1_scl), + MSM_PIN_FUNCTION(i2c1_sda), + MSM_PIN_FUNCTION(i2c11), + MSM_PIN_FUNCTION(mac0), + MSM_PIN_FUNCTION(mac1), + MSM_PIN_FUNCTION(mdc_mst), + MSM_PIN_FUNCTION(mdc_slv), + MSM_PIN_FUNCTION(mdio_mst), + MSM_PIN_FUNCTION(mdio_slv), + MSM_PIN_FUNCTION(pcie0_clk), + MSM_PIN_FUNCTION(pcie0_wake), + MSM_PIN_FUNCTION(pcie1_clk), + MSM_PIN_FUNCTION(pcie1_wake), + MSM_PIN_FUNCTION(pcie2_clk), + MSM_PIN_FUNCTION(pcie2_wake), + MSM_PIN_FUNCTION(pcie3_clk), + MSM_PIN_FUNCTION(pcie3_wake), + MSM_PIN_FUNCTION(pll_test), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(PTA0_0), + MSM_PIN_FUNCTION(PTA0_1), + MSM_PIN_FUNCTION(PTA0_2), + MSM_PIN_FUNCTION(PTA10), + MSM_PIN_FUNCTION(PTA11), + MSM_PIN_FUNCTION(pwm0), + MSM_PIN_FUNCTION(pwm1), + MSM_PIN_FUNCTION(pwm2), + MSM_PIN_FUNCTION(qdss_cti_trig_in_a0), + MSM_PIN_FUNCTION(qdss_cti_trig_out_a0), + MSM_PIN_FUNCTION(qdss_cti_trig_in_a1), + MSM_PIN_FUNCTION(qdss_cti_trig_out_a1), + MSM_PIN_FUNCTION(qdss_cti_trig_in_b0), + MSM_PIN_FUNCTION(qdss_cti_trig_out_b0), + MSM_PIN_FUNCTION(qdss_cti_trig_in_b1), + MSM_PIN_FUNCTION(qdss_cti_trig_out_b1), + MSM_PIN_FUNCTION(qdss_traceclk_a), + MSM_PIN_FUNCTION(qdss_tracectl_a), + MSM_PIN_FUNCTION(qdss_tracedata_a), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(qspi_data), + MSM_PIN_FUNCTION(resout), + MSM_PIN_FUNCTION(rx0), + MSM_PIN_FUNCTION(rx1), + MSM_PIN_FUNCTION(rx2), + MSM_PIN_FUNCTION(sdc_clk), + MSM_PIN_FUNCTION(sdc_cmd), + MSM_PIN_FUNCTION(sdc_data), + MSM_PIN_FUNCTION(spi0), + MSM_PIN_FUNCTION(spi1), + MSM_PIN_FUNCTION(spi10), + MSM_PIN_FUNCTION(spi11), + MSM_PIN_FUNCTION(tsens_max), + MSM_PIN_FUNCTION(uart0), + MSM_PIN_FUNCTION(uart1), + MSM_PIN_FUNCTION(wci_txd), + MSM_PIN_FUNCTION(wci_rxd), + MSM_PIN_FUNCTION(wsi_clk), + MSM_PIN_FUNCTION(wsi_data), +}; + +static const struct msm_pingroup ipq5424_groups[] = { + PINGROUP(0, sdc_data, qspi_data, pwm2, wci_txd, wci_rxd, _, _, _, _), + PINGROUP(1, sdc_data, qspi_data, pwm2, wci_txd, wci_rxd, _, _, _, _), + PINGROUP(2, sdc_data, qspi_data, pwm2, _, _, _, _, _, _), + PINGROUP(3, sdc_data, qspi_data, pwm2, _, _, _, _, _, _), + PINGROUP(4, sdc_cmd, qspi_cs, _, _, _, _, _, _, _), + PINGROUP(5, sdc_clk, qspi_clk, _, _, _, _, _, _, _), + PINGROUP(6, spi0, pwm1, _, cri_trng0, qdss_tracedata_a, _, _, _, _), + PINGROUP(7, spi0, pwm1, _, cri_trng1, qdss_tracedata_a, _, _, _, _), + PINGROUP(8, spi0, pwm1, wci_txd, wci_rxd, _, cri_trng2, qdss_tracedata_a, _, _), + PINGROUP(9, spi0, pwm1, _, cri_trng3, qdss_tracedata_a, _, _, _, _), + PINGROUP(10, uart0, pwm0, spi11, _, wci_txd, wci_rxd, _, qdss_tracedata_a, _), + PINGROUP(11, uart0, pwm0, spi1, _, wci_txd, wci_rxd, _, qdss_tracedata_a, _), + PINGROUP(12, uart0, pwm0, spi11, _, prng_rosc0, qdss_tracedata_a, _, _, _), + PINGROUP(13, uart0, pwm0, spi11, _, prng_rosc1, qdss_tracedata_a, _, _, _), + PINGROUP(14, i2c0_scl, tsens_max, _, prng_rosc2, qdss_tracedata_a, _, _, _, _), + PINGROUP(15, i2c0_sda, _, prng_rosc3, qdss_tracedata_a, _, _, _, _, _), + PINGROUP(16, core_voltage, i2c1_scl, _, _, _, _, _, _, _), + PINGROUP(17, core_voltage, i2c1_sda, _, _, _, _, _, _, _), + PINGROUP(18, _, _, _, _, _, _, _, _, _), + PINGROUP(19, _, _, _, _, _, _, _, _, _), + PINGROUP(20, mdc_slv, atest_char0, _, qdss_tracedata_a, _, _, _, _, _), + PINGROUP(21, mdio_slv, atest_char1, _, qdss_tracedata_a, _, _, _, _, _), + PINGROUP(22, mdc_mst, atest_char2, _, _, _, _, _, _, _), + PINGROUP(23, mdio_mst, atest_char3, _, _, _, _, _, _, _), + PINGROUP(24, pcie0_clk, PTA10, mac0, _, wsi_clk, _, atest_char, qdss_cti_trig_out_a0, _), + PINGROUP(25, _, _, _, _, _, _, _, _, _), + PINGROUP(26, pcie0_wake, PTA10, mac0, _, wsi_data, _, qdss_cti_trig_in_a0, _, _), + PINGROUP(27, pcie1_clk, i2c11, PTA10, wsi_clk, qdss_cti_trig_out_a1, _, _, _, _), + PINGROUP(28, _, _, _, _, _, _, _, _, _), + PINGROUP(29, pcie1_wake, i2c11, wsi_data, qdss_cti_trig_in_a1, _, _, _, _, _), + PINGROUP(30, pcie2_clk, PTA11, mac1, qdss_cti_trig_out_b0, _, _, _, _, _), + PINGROUP(31, _, _, _, _, _, _, _, _, _), + PINGROUP(32, pcie2_wake, PTA11, mac1, audio_pri0, audio_pri0, qdss_cti_trig_in_b0, _, _, _), + PINGROUP(33, pcie3_clk, PTA11, audio_pri1, audio_pri1, qdss_cti_trig_out_b1, _, _, _, _), + PINGROUP(34, _, _, _, _, _, _, _, _, _), + PINGROUP(35, pcie3_wake, audio_sec1, audio_sec1, qdss_cti_trig_in_b1, _, _, _, _, _), + PINGROUP(36, audio_pri, spi1, audio_sec0, audio_sec0, qdss_tracedata_a, _, _, _, _), + PINGROUP(37, audio_pri, spi1, rx2, qdss_tracedata_a, _, _, _, _, _), + PINGROUP(38, audio_pri, spi1, pll_test, rx1, qdss_tracedata_a, _, _, _, _), + PINGROUP(39, audio_pri, rx0, _, qdss_tracedata_a, _, _, _, _, _), + PINGROUP(40, PTA0_0, wci_txd, wci_rxd, _, atest_tic, _, _, _, _), + PINGROUP(41, PTA0_1, wci_txd, wci_rxd, cxc_data, _, _, _, _, _), + PINGROUP(42, PTA0_2, cxc_clk, _, _, _, _, _, _, _), + PINGROUP(43, uart1, gcc_plltest, _, _, _, _, _, _, _), + PINGROUP(44, uart1, gcc_tlmm, _, _, _, _, _, _, _), + PINGROUP(45, spi10, rx2, audio_sec, gcc_plltest, _, qdss_traceclk_a, _, _, _), + PINGROUP(46, spi1, rx1, audio_sec, dbg_out, qdss_tracectl_a, _, _, _, _), + PINGROUP(47, spi10, rx0, audio_sec, _, _, _, _, _, _), + PINGROUP(48, spi10, audio_sec, _, _, _, _, _, _, _), + PINGROUP(49, resout, _, _, _, _, _, _, _, _), +}; + +static const struct msm_pinctrl_soc_data ipq5424_pinctrl = { + .pins = ipq5424_pins, + .npins = ARRAY_SIZE(ipq5424_pins), + .functions = ipq5424_functions, + .nfunctions = ARRAY_SIZE(ipq5424_functions), + .groups = ipq5424_groups, + .ngroups = ARRAY_SIZE(ipq5424_groups), + .ngpios = 50, +}; + +static int ipq5424_pinctrl_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &ipq5424_pinctrl); +} + +static const struct of_device_id ipq5424_pinctrl_of_match[] = { + { .compatible = "qcom,ipq5424-tlmm", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ipq5424_pinctrl_of_match); + +static struct platform_driver ipq5424_pinctrl_driver = { + .driver = { + .name = "ipq5424-tlmm", + .of_match_table = ipq5424_pinctrl_of_match, + }, + .probe = ipq5424_pinctrl_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init ipq5424_pinctrl_init(void) +{ + return platform_driver_register(&ipq5424_pinctrl_driver); +} +arch_initcall(ipq5424_pinctrl_init); + +static void __exit ipq5424_pinctrl_exit(void) +{ + platform_driver_unregister(&ipq5424_pinctrl_driver); +} +module_exit(ipq5424_pinctrl_exit); + +MODULE_DESCRIPTION("QTI IPQ5424 TLMM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq6018.c b/drivers/pinctrl/qcom/pinctrl-ipq6018.c index ac330d8712b5c..0ad08647dbcdf 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq6018.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq6018.c @@ -1080,7 +1080,7 @@ static struct platform_driver ipq6018_pinctrl_driver = { .of_match_table = ipq6018_pinctrl_of_match, }, .probe = ipq6018_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq6018_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c index e10e1bc4c9113..e2bb94e86aef6 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c @@ -631,7 +631,7 @@ static struct platform_driver ipq8064_pinctrl_driver = { .of_match_table = ipq8064_pinctrl_of_match, }, .probe = ipq8064_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq8064_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8074.c b/drivers/pinctrl/qcom/pinctrl-ipq8074.c index fee32c1d1d3e9..337f3a1c92c19 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8074.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8074.c @@ -1041,7 +1041,7 @@ static struct platform_driver ipq8074_pinctrl_driver = { .of_match_table = ipq8074_pinctrl_of_match, }, .probe = ipq8074_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq8074_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9574.c b/drivers/pinctrl/qcom/pinctrl-ipq9574.c index 20ab59cb621bc..e2491617b2364 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq9574.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq9574.c @@ -799,7 +799,7 @@ static struct platform_driver ipq9574_pinctrl_driver = { .of_match_table = ipq9574_pinctrl_of_match, }, .probe = ipq9574_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init ipq9574_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-mdm9607.c b/drivers/pinctrl/qcom/pinctrl-mdm9607.c index 415d24e16267d..e7cd3ef1cf3e8 100644 --- a/drivers/pinctrl/qcom/pinctrl-mdm9607.c +++ b/drivers/pinctrl/qcom/pinctrl-mdm9607.c @@ -1059,7 +1059,7 @@ static struct platform_driver mdm9607_pinctrl_driver = { .of_match_table = mdm9607_pinctrl_of_match, }, .probe = mdm9607_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init mdm9607_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-mdm9615.c b/drivers/pinctrl/qcom/pinctrl-mdm9615.c index 3f2eafea0b246..0a2ae383d3d57 100644 --- a/drivers/pinctrl/qcom/pinctrl-mdm9615.c +++ b/drivers/pinctrl/qcom/pinctrl-mdm9615.c @@ -446,7 +446,7 @@ static struct platform_driver mdm9615_pinctrl_driver = { .of_match_table = mdm9615_pinctrl_of_match, }, .probe = mdm9615_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init mdm9615_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index aeaf0d1958f56..ec913c2e200f5 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1457,7 +1457,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) * files which don't set the "gpio-ranges" property or systems that * utilize ACPI the driver has to call gpiochip_add_pin_range(). */ - if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) { + if (!of_property_present(pctrl->dev->of_node, "gpio-ranges")) { ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio); if (ret) { diff --git a/drivers/pinctrl/qcom/pinctrl-msm8226.c b/drivers/pinctrl/qcom/pinctrl-msm8226.c index 40806c0650ef2..64fee70f1772c 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8226.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8226.c @@ -654,7 +654,7 @@ static struct platform_driver msm8226_pinctrl_driver = { .of_match_table = msm8226_pinctrl_of_match, }, .probe = msm8226_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8226_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8660.c b/drivers/pinctrl/qcom/pinctrl-msm8660.c index dba6d531b4a14..999a5f867eb50 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8660.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8660.c @@ -981,7 +981,7 @@ static struct platform_driver msm8660_pinctrl_driver = { .of_match_table = msm8660_pinctrl_of_match, }, .probe = msm8660_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8660_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8909.c b/drivers/pinctrl/qcom/pinctrl-msm8909.c index 14b17ba9f9061..756856d20d6b5 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8909.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8909.c @@ -929,7 +929,7 @@ static struct platform_driver msm8909_pinctrl_driver = { .of_match_table = msm8909_pinctrl_of_match, }, .probe = msm8909_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8909_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8916.c b/drivers/pinctrl/qcom/pinctrl-msm8916.c index 184dcf8422735..cea5c54f92fec 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8916.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8916.c @@ -969,7 +969,7 @@ static struct platform_driver msm8916_pinctrl_driver = { .of_match_table = msm8916_pinctrl_of_match, }, .probe = msm8916_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8916_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8953.c b/drivers/pinctrl/qcom/pinctrl-msm8953.c index c2253821ae8d3..998351bdfee13 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8953.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8953.c @@ -1816,7 +1816,7 @@ static struct platform_driver msm8953_pinctrl_driver = { .of_match_table = msm8953_pinctrl_of_match, }, .probe = msm8953_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8953_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8960.c b/drivers/pinctrl/qcom/pinctrl-msm8960.c index 6b9148d226e9b..ebe230b3b437c 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8960.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8960.c @@ -1246,7 +1246,7 @@ static struct platform_driver msm8960_pinctrl_driver = { .of_match_table = msm8960_pinctrl_of_match, }, .probe = msm8960_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8960_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8976.c b/drivers/pinctrl/qcom/pinctrl-msm8976.c index 9a951888e8a1b..c30d80e4e98ca 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8976.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8976.c @@ -1096,7 +1096,7 @@ static struct platform_driver msm8976_pinctrl_driver = { .of_match_table = msm8976_pinctrl_of_match, }, .probe = msm8976_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8976_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8994.c b/drivers/pinctrl/qcom/pinctrl-msm8994.c index 1ed1dd32d6c79..b1a6759ab4a5e 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8994.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8994.c @@ -1343,7 +1343,7 @@ static struct platform_driver msm8994_pinctrl_driver = { .of_match_table = msm8994_pinctrl_of_match, }, .probe = msm8994_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8994_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8996.c b/drivers/pinctrl/qcom/pinctrl-msm8996.c index 777c2a74036ed..1b5d80eaab83c 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8996.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8996.c @@ -1920,7 +1920,7 @@ static struct platform_driver msm8996_pinctrl_driver = { .of_match_table = msm8996_pinctrl_of_match, }, .probe = msm8996_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8996_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8998.c b/drivers/pinctrl/qcom/pinctrl-msm8998.c index 4aaf45e54f3a7..b7cbf32b3125a 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8998.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8998.c @@ -1535,7 +1535,7 @@ static struct platform_driver msm8998_pinctrl_driver = { .of_match_table = msm8998_pinctrl_of_match, }, .probe = msm8998_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8998_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-msm8x74.c b/drivers/pinctrl/qcom/pinctrl-msm8x74.c index 750a8272ded7f..238c83f6ec4f4 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8x74.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8x74.c @@ -1083,7 +1083,7 @@ static struct platform_driver msm8x74_pinctrl_driver = { .of_match_table = msm8x74_pinctrl_of_match, }, .probe = msm8x74_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init msm8x74_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c index f5c1c427b44e9..ba699eac9ee8b 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c +++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c @@ -1113,7 +1113,7 @@ static struct platform_driver qcm2290_pinctrl_driver = { .of_match_table = qcm2290_pinctrl_of_match, }, .probe = qcm2290_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init qcm2290_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index 9a875b7dc9989..ae7224012f8aa 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -1644,7 +1644,7 @@ static struct platform_driver qcs404_pinctrl_driver = { .of_match_table = qcs404_pinctrl_of_match, }, .probe = qcs404_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init qcs404_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-qcs615.c b/drivers/pinctrl/qcom/pinctrl-qcs615.c new file mode 100644 index 0000000000000..23015b055f6a9 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-qcs615.c @@ -0,0 +1,1107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +enum { + SOUTH, + EAST, + WEST +}; + +static const char * const qcs615_tiles[] = { + [SOUTH] = "south", + [EAST] = "east", + [WEST] = "west" +}; + +#define PINGROUP(id, _tile, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + }, \ + .nfuncs = 10, \ + .ctl_reg = 0x1000 * id, \ + .io_reg = 0x1000 * id + 0x4, \ + .intr_cfg_reg = 0x1000 * id + 0x8, \ + .intr_status_reg = 0x1000 * id + 0xc, \ + .intr_target_reg = 0x1000 * id + 0x8, \ + .tile = _tile, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, _tile, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .tile = _tile, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .tile = WEST, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc qcs615_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "UFS_RESET"), + PINCTRL_PIN(124, "SDC1_RCLK"), + PINCTRL_PIN(125, "SDC1_CLK"), + PINCTRL_PIN(126, "SDC1_CMD"), + PINCTRL_PIN(127, "SDC1_DATA"), + PINCTRL_PIN(128, "SDC2_CLK"), + PINCTRL_PIN(129, "SDC2_CMD"), + PINCTRL_PIN(130, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); + +static const unsigned int ufs_reset_pins[] = { 123 }; +static const unsigned int sdc1_rclk_pins[] = { 124 }; +static const unsigned int sdc1_clk_pins[] = { 125 }; +static const unsigned int sdc1_cmd_pins[] = { 126 }; +static const unsigned int sdc1_data_pins[] = { 127 }; +static const unsigned int sdc2_clk_pins[] = { 128 }; +static const unsigned int sdc2_cmd_pins[] = { 129 }; +static const unsigned int sdc2_data_pins[] = { 130 }; + +enum qcs615_functions { + msm_mux_gpio, + msm_mux_adsp_ext, + msm_mux_agera_pll, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_tsens, + msm_mux_atest_usb, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c, + msm_mux_cci_timer, + msm_mux_copy_gp, + msm_mux_copy_phase, + msm_mux_cri_trng, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist, + msm_mux_ddr_pxi, + msm_mux_dp_hot, + msm_mux_edp_hot, + msm_mux_edp_lcd, + msm_mux_emac_gcc, + msm_mux_emac_phy_intr, + msm_mux_forced_usb, + msm_mux_gcc_gp, + msm_mux_gp_pdm, + msm_mux_gps_tx, + msm_mux_hs0_mi2s, + msm_mux_hs1_mi2s, + msm_mux_jitter_bist, + msm_mux_ldo_en, + msm_mux_ldo_update, + msm_mux_m_voc, + msm_mux_mclk1, + msm_mux_mclk2, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync0_out, + msm_mux_mdp_vsync1_out, + msm_mux_mdp_vsync2_out, + msm_mux_mdp_vsync3_out, + msm_mux_mdp_vsync4_out, + msm_mux_mdp_vsync5_out, + msm_mux_mi2s_1, + msm_mux_mss_lte, + msm_mux_nav_pps_in, + msm_mux_nav_pps_out, + msm_mux_pa_indicator_or, + msm_mux_pcie_clk_req, + msm_mux_pcie_ep_rst, + msm_mux_phase_flag, + msm_mux_pll_bist, + msm_mux_pll_bypassnl, + msm_mux_pll_reset_n, + msm_mux_prng_rosc, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qlink_enable, + msm_mux_qlink_request, + msm_mux_qspi, + msm_mux_qup0, + msm_mux_qup1, + msm_mux_rgmii, + msm_mux_sd_write_protect, + msm_mux_sp_cmu, + msm_mux_ter_mi2s, + msm_mux_tgu_ch, + msm_mux_uim1, + msm_mux_uim2, + msm_mux_usb0_hs, + msm_mux_usb1_hs, + msm_mux_usb_phy_ps, + msm_mux_vfr_1, + msm_mux_vsense_trigger_mirnat, + msm_mux_wlan, + msm_mux_wsa_clk, + msm_mux_wsa_data, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", + "gpio54", "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", + "gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", + "gpio114", "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", "gpio122", +}; + +static const char *const adsp_ext_groups[] = { + "gpio118", +}; + +static const char *const agera_pll_groups[] = { + "gpio28", +}; + +static const char *const aoss_cti_groups[] = { + "gpio76", +}; + +static const char *const atest_char_groups[] = { + "gpio84", "gpio85", "gpio86", "gpio87", + "gpio115", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", +}; + +static const char *const atest_tsens_groups[] = { + "gpio7", "gpio29", +}; + +static const char *const atest_usb_groups[] = { + "gpio7", "gpio10", "gpio11", "gpio54", + "gpio55", "gpio67", "gpio68", "gpio76", + "gpio75", "gpio77", +}; + +static const char *const cam_mclk_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", +}; + +static const char *const cci_async_groups[] = { + "gpio26", "gpio41", "gpio42", +}; + +static const char *const cci_i2c_groups[] = { + "gpio32", "gpio33", "gpio34", "gpio35", +}; + +static const char *const cci_timer_groups[] = { + "gpio37", "gpio38", "gpio39", "gpio41", + "gpio42", +}; + +static const char *const copy_gp_groups[] = { + "gpio86", +}; + +static const char *const copy_phase_groups[] = { + "gpio103", +}; + +static const char *const cri_trng_groups[] = { + "gpio60", "gpio61", "gpio62", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio11", +}; + +static const char *const ddr_bist_groups[] = { + "gpio7", "gpio8", "gpio9", "gpio10", +}; + +static const char *const ddr_pxi_groups[] = { + "gpio6", "gpio7", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio54", "gpio55", +}; + +static const char *const dp_hot_groups[] = { + "gpio102", "gpio103", "gpio104", +}; + +static const char *const edp_hot_groups[] = { + "gpio113", +}; + +static const char *const edp_lcd_groups[] = { + "gpio119", +}; + +static const char *const emac_gcc_groups[] = { + "gpio101", "gpio102", +}; + +static const char *const emac_phy_intr_groups[] = { + "gpio89", +}; + +static const char *const forced_usb_groups[] = { + "gpio43", +}; + +static const char *const gcc_gp_groups[] = { + "gpio21", "gpio22", "gpio57", "gpio58", + "gpio59", "gpio78", +}; + +static const char *const gp_pdm_groups[] = { + "gpio8", "gpio54", "gpio63", "gpio66", + "gpio79", "gpio95", +}; + +static const char *const gps_tx_groups[] = { + "gpio53", "gpio54", "gpio56", "gpio57", + "gpio59", "gpio60", +}; + +static const char *const hs0_mi2s_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const hs1_mi2s_groups[] = { + "gpio24", "gpio25", "gpio26", "gpio27", +}; + +static const char *const jitter_bist_groups[] = { + "gpio12", "gpio26", +}; + +static const char *const ldo_en_groups[] = { + "gpio97", +}; + +static const char *const ldo_update_groups[] = { + "gpio98", +}; + +static const char *const m_voc_groups[] = { + "gpio120", +}; + +static const char *const mclk1_groups[] = { + "gpio121", +}; + +static const char *const mclk2_groups[] = { + "gpio122", +}; + +static const char *const mdp_vsync_groups[] = { + "gpio81", "gpio82", "gpio83", "gpio90", + "gpio97", "gpio98", +}; + +static const char *const mdp_vsync0_out_groups[] = { + "gpio90", +}; + +static const char *const mdp_vsync1_out_groups[] = { + "gpio90", +}; + +static const char *const mdp_vsync2_out_groups[] = { + "gpio90", +}; + +static const char *const mdp_vsync3_out_groups[] = { + "gpio90", +}; + +static const char *const mdp_vsync4_out_groups[] = { + "gpio90", +}; + +static const char *const mdp_vsync5_out_groups[] = { + "gpio90", +}; + +static const char *const mi2s_1_groups[] = { + "gpio108", "gpio109", "gpio110", "gpio111", +}; + +static const char *const mss_lte_groups[] = { + "gpio106", "gpio107", +}; + +static const char *const nav_pps_in_groups[] = { + "gpio53", "gpio56", "gpio57", "gpio59", + "gpio60", +}; + +static const char *const nav_pps_out_groups[] = { + "gpio53", "gpio56", "gpio57", "gpio59", + "gpio60", +}; + +static const char *const pa_indicator_or_groups[] = { + "gpio53", +}; + +static const char *const pcie_clk_req_groups[] = { + "gpio90", +}; + +static const char *const pcie_ep_rst_groups[] = { + "gpio89", +}; + +static const char *const phase_flag_groups[] = { + "gpio10", "gpio18", "gpio19", "gpio20", + "gpio23", "gpio24", "gpio25", "gpio38", + "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", "gpio45", "gpio53", "gpio54", + "gpio55", "gpio67", "gpio68", "gpio75", + "gpio76", "gpio77", "gpio78", "gpio79", + "gpio80", "gpio82", "gpio84", "gpio92", + "gpio116", "gpio117", "gpio118", "gpio119", +}; + +static const char *const pll_bist_groups[] = { + "gpio27", +}; + +static const char *const pll_bypassnl_groups[] = { + "gpio13", +}; + +static const char *const pll_reset_n_groups[] = { + "gpio14", +}; + +static const char *const prng_rosc_groups[] = { + "gpio99", "gpio102", +}; + +static const char *const qdss_cti_groups[] = { + "gpio83", "gpio96", "gpio97", "gpio98", + "gpio103", "gpio104", "gpio112", "gpio113", +}; + +static const char *const qdss_gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio14", "gpio15", "gpio20", "gpio21", + "gpio28", "gpio29", "gpio30", "gpio31", + "gpio32", "gpio33", "gpio34", "gpio35", + "gpio44", "gpio45", "gpio46", "gpio47", + "gpio81", "gpio82", "gpio92", "gpio93", + "gpio94", "gpio95", "gpio108", "gpio109", + "gpio117", "gpio118", "gpio119", "gpio120", +}; + +static const char *const qlink_enable_groups[] = { + "gpio52", +}; + +static const char *const qlink_request_groups[] = { + "gpio51", +}; + +static const char *const qspi_groups[] = { + "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", +}; + +static const char *const qup0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio16", "gpio17", + "gpio18", "gpio19", +}; + +static const char *const qup1_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio20", "gpio21", + "gpio22", "gpio23", +}; + +static const char *const rgmii_groups[] = { + "gpio81", "gpio82", "gpio83", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio102", "gpio103", + "gpio112", "gpio113", "gpio114", +}; + +static const char *const sd_write_protect_groups[] = { + "gpio24", +}; + +static const char *const sp_cmu_groups[] = { + "gpio64", +}; + +static const char *const ter_mi2s_groups[] = { + "gpio115", "gpio116", "gpio117", "gpio118", +}; + +static const char *const tgu_ch_groups[] = { + "gpio89", "gpio90", "gpio91", "gpio92", +}; + +static const char *const uim1_groups[] = { + "gpio77", "gpio78", "gpio79", "gpio80", +}; + +static const char *const uim2_groups[] = { + "gpio73", "gpio74", "gpio75", "gpio76", +}; + +static const char *const usb0_hs_groups[] = { + "gpio88", +}; + +static const char *const usb1_hs_groups[] = { + "gpio89", +}; + +static const char *const usb_phy_ps_groups[] = { + "gpio104", +}; + +static const char *const vfr_1_groups[] = { + "gpio92", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio7", +}; + +static const char *const wlan_groups[] = { + "gpio16", "gpio17", "gpio47", "gpio48", +}; + +static const char *const wsa_clk_groups[] = { + "gpio111", +}; + +static const char *const wsa_data_groups[] = { + "gpio110", +}; + +static const struct pinfunction qcs615_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(adsp_ext), + MSM_PIN_FUNCTION(agera_pll), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_tsens), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(cci_i2c), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(copy_gp), + MSM_PIN_FUNCTION(copy_phase), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi), + MSM_PIN_FUNCTION(dp_hot), + MSM_PIN_FUNCTION(edp_hot), + MSM_PIN_FUNCTION(edp_lcd), + MSM_PIN_FUNCTION(emac_gcc), + MSM_PIN_FUNCTION(emac_phy_intr), + MSM_PIN_FUNCTION(forced_usb), + MSM_PIN_FUNCTION(gcc_gp), + MSM_PIN_FUNCTION(gp_pdm), + MSM_PIN_FUNCTION(gps_tx), + MSM_PIN_FUNCTION(hs0_mi2s), + MSM_PIN_FUNCTION(hs1_mi2s), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(ldo_en), + MSM_PIN_FUNCTION(ldo_update), + MSM_PIN_FUNCTION(m_voc), + MSM_PIN_FUNCTION(mclk1), + MSM_PIN_FUNCTION(mclk2), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mdp_vsync0_out), + MSM_PIN_FUNCTION(mdp_vsync1_out), + MSM_PIN_FUNCTION(mdp_vsync2_out), + MSM_PIN_FUNCTION(mdp_vsync3_out), + MSM_PIN_FUNCTION(mdp_vsync4_out), + MSM_PIN_FUNCTION(mdp_vsync5_out), + MSM_PIN_FUNCTION(mi2s_1), + MSM_PIN_FUNCTION(mss_lte), + MSM_PIN_FUNCTION(nav_pps_in), + MSM_PIN_FUNCTION(nav_pps_out), + MSM_PIN_FUNCTION(pa_indicator_or), + MSM_PIN_FUNCTION(pcie_clk_req), + MSM_PIN_FUNCTION(pcie_ep_rst), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist), + MSM_PIN_FUNCTION(pll_bypassnl), + MSM_PIN_FUNCTION(pll_reset_n), + MSM_PIN_FUNCTION(prng_rosc), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qlink_enable), + MSM_PIN_FUNCTION(qlink_request), + MSM_PIN_FUNCTION(qspi), + MSM_PIN_FUNCTION(qup0), + MSM_PIN_FUNCTION(qup1), + MSM_PIN_FUNCTION(rgmii), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(sp_cmu), + MSM_PIN_FUNCTION(ter_mi2s), + MSM_PIN_FUNCTION(tgu_ch), + MSM_PIN_FUNCTION(uim1), + MSM_PIN_FUNCTION(uim2), + MSM_PIN_FUNCTION(usb0_hs), + MSM_PIN_FUNCTION(usb1_hs), + MSM_PIN_FUNCTION(usb_phy_ps), + MSM_PIN_FUNCTION(vfr_1), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wlan), + MSM_PIN_FUNCTION(wsa_clk), + MSM_PIN_FUNCTION(wsa_data), +}; + +/* Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup qcs615_groups[] = { + [0] = PINGROUP(0, WEST, qup0, _, qdss_gpio, _, _, _, _, _, _), + [1] = PINGROUP(1, WEST, qup0, _, qdss_gpio, _, _, _, _, _, _), + [2] = PINGROUP(2, WEST, qup0, _, qdss_gpio, _, _, _, _, _, _), + [3] = PINGROUP(3, WEST, qup0, _, qdss_gpio, _, _, _, _, _, _), + [4] = PINGROUP(4, WEST, qup0, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, WEST, qup0, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, EAST, qup1, qdss_gpio, ddr_pxi, _, _, _, _, _, _), + [7] = PINGROUP(7, EAST, qup1, ddr_bist, qdss_gpio, atest_tsens, + vsense_trigger_mirnat, atest_usb, ddr_pxi, _, _), + [8] = PINGROUP(8, EAST, qup1, gp_pdm, ddr_bist, qdss_gpio, _, _, _, _, _), + [9] = PINGROUP(9, EAST, qup1, ddr_bist, qdss_gpio, _, _, _, _, _, _), + [10] = PINGROUP(10, EAST, qup1, ddr_bist, _, phase_flag, atest_usb, ddr_pxi, _, _, _), + [11] = PINGROUP(11, EAST, qup1, dbg_out_clk, atest_usb, ddr_pxi, _, _, _, _, _), + [12] = PINGROUP(12, EAST, qup1, jitter_bist, ddr_pxi, _, _, _, _, _, _), + [13] = PINGROUP(13, EAST, qup1, pll_bypassnl, _, ddr_pxi, _, _, _, _, _), + [14] = PINGROUP(14, EAST, qup1, pll_reset_n, _, qdss_gpio, _, _, _, _, _), + [15] = PINGROUP(15, EAST, qup1, qdss_gpio, _, _, _, _, _, _, _), + [16] = PINGROUP(16, WEST, qup0, _, wlan, _, _, _, _, _, _), + [17] = PINGROUP(17, WEST, qup0, _, wlan, _, _, _, _, _, _), + [18] = PINGROUP(18, WEST, qup0, _, phase_flag, _, _, _, _, _, _), + [19] = PINGROUP(19, WEST, qup0, _, phase_flag, _, _, _, _, _, _), + [20] = PINGROUP(20, SOUTH, qup1, _, phase_flag, qdss_gpio, _, _, _, _, _), + [21] = PINGROUP(21, SOUTH, qup1, gcc_gp, _, qdss_gpio, _, _, _, _, _), + [22] = PINGROUP(22, SOUTH, qup1, gcc_gp, _, _, _, _, _, _, _), + [23] = PINGROUP(23, SOUTH, qup1, _, phase_flag, _, _, _, _, _, _), + [24] = PINGROUP(24, EAST, hs1_mi2s, sd_write_protect, _, phase_flag, _, _, _, _, _), + [25] = PINGROUP(25, EAST, hs1_mi2s, _, phase_flag, _, _, _, _, _, _), + [26] = PINGROUP(26, EAST, cci_async, hs1_mi2s, jitter_bist, _, _, _, _, _, _), + [27] = PINGROUP(27, EAST, hs1_mi2s, pll_bist, _, _, _, _, _, _, _), + [28] = PINGROUP(28, EAST, cam_mclk, agera_pll, qdss_gpio, _, _, _, _, _, _), + [29] = PINGROUP(29, EAST, cam_mclk, _, qdss_gpio, atest_tsens, _, _, _, _, _), + [30] = PINGROUP(30, EAST, cam_mclk, qdss_gpio, _, _, _, _, _, _, _), + [31] = PINGROUP(31, EAST, cam_mclk, _, qdss_gpio, _, _, _, _, _, _), + [32] = PINGROUP(32, EAST, cci_i2c, _, qdss_gpio, _, _, _, _, _, _), + [33] = PINGROUP(33, EAST, cci_i2c, _, qdss_gpio, _, _, _, _, _, _), + [34] = PINGROUP(34, EAST, cci_i2c, _, qdss_gpio, _, _, _, _, _, _), + [35] = PINGROUP(35, EAST, cci_i2c, _, qdss_gpio, _, _, _, _, _, _), + [36] = PINGROUP(36, EAST, hs0_mi2s, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, EAST, cci_timer, hs0_mi2s, _, _, _, _, _, _, _), + [38] = PINGROUP(38, EAST, cci_timer, hs0_mi2s, _, phase_flag, _, _, _, _, _), + [39] = PINGROUP(39, EAST, cci_timer, hs0_mi2s, _, _, _, _, _, _, _), + [40] = PINGROUP(40, EAST, _, phase_flag, _, _, _, _, _, _, _), + [41] = PINGROUP(41, EAST, cci_async, cci_timer, _, phase_flag, _, _, _, _, _), + [42] = PINGROUP(42, EAST, cci_async, cci_timer, _, phase_flag, _, _, _, _, _), + [43] = PINGROUP(43, SOUTH, _, phase_flag, forced_usb, _, _, _, _, _, _), + [44] = PINGROUP(44, EAST, qspi, _, phase_flag, qdss_gpio, _, _, _, _, _), + [45] = PINGROUP(45, EAST, qspi, _, phase_flag, qdss_gpio, _, _, _, _, _), + [46] = PINGROUP(46, EAST, qspi, _, qdss_gpio, _, _, _, _, _, _), + [47] = PINGROUP(47, EAST, qspi, _, qdss_gpio, wlan, _, _, _, _, _), + [48] = PINGROUP(48, EAST, qspi, _, wlan, _, _, _, _, _, _), + [49] = PINGROUP(49, EAST, qspi, _, _, _, _, _, _, _, _), + [50] = PINGROUP(50, EAST, qspi, _, _, _, _, _, _, _, _), + [51] = PINGROUP(51, SOUTH, qlink_request, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, SOUTH, qlink_enable, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, SOUTH, pa_indicator_or, nav_pps_in, nav_pps_out, gps_tx, _, + phase_flag, _, _, _), + [54] = PINGROUP(54, SOUTH, _, gps_tx, gp_pdm, _, phase_flag, atest_usb, ddr_pxi, _, _), + [55] = PINGROUP(55, SOUTH, _, _, phase_flag, atest_usb, ddr_pxi, _, _, _, _), + [56] = PINGROUP(56, SOUTH, _, nav_pps_in, nav_pps_out, gps_tx, _, _, _, _, _), + [57] = PINGROUP(57, SOUTH, _, nav_pps_in, gps_tx, nav_pps_out, gcc_gp, _, _, _, _), + [58] = PINGROUP(58, SOUTH, _, gcc_gp, _, _, _, _, _, _, _), + [59] = PINGROUP(59, SOUTH, _, nav_pps_in, nav_pps_out, gps_tx, gcc_gp, _, _, _, _), + [60] = PINGROUP(60, SOUTH, _, nav_pps_in, nav_pps_out, gps_tx, cri_trng, _, _, _, _), + [61] = PINGROUP(61, SOUTH, _, cri_trng, _, _, _, _, _, _, _), + [62] = PINGROUP(62, SOUTH, _, cri_trng, _, _, _, _, _, _, _), + [63] = PINGROUP(63, SOUTH, _, _, gp_pdm, _, _, _, _, _, _), + [64] = PINGROUP(64, SOUTH, _, sp_cmu, _, _, _, _, _, _, _), + [65] = PINGROUP(65, SOUTH, _, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, SOUTH, _, gp_pdm, _, _, _, _, _, _, _), + [67] = PINGROUP(67, SOUTH, _, _, _, phase_flag, atest_usb, _, _, _, _), + [68] = PINGROUP(68, SOUTH, _, _, _, phase_flag, atest_usb, _, _, _, _), + [69] = PINGROUP(69, SOUTH, _, _, _, _, _, _, _, _, _), + [70] = PINGROUP(70, SOUTH, _, _, _, _, _, _, _, _, _), + [71] = PINGROUP(71, SOUTH, _, _, _, _, _, _, _, _, _), + [72] = PINGROUP(72, SOUTH, _, _, _, _, _, _, _, _, _), + [73] = PINGROUP(73, SOUTH, uim2, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, SOUTH, uim2, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, SOUTH, uim2, _, phase_flag, atest_usb, _, _, _, _, _), + [76] = PINGROUP(76, SOUTH, uim2, _, phase_flag, atest_usb, aoss_cti, _, _, _, _), + [77] = PINGROUP(77, SOUTH, uim1, _, phase_flag, atest_usb, _, _, _, _, _), + [78] = PINGROUP(78, SOUTH, uim1, gcc_gp, _, phase_flag, _, _, _, _, _), + [79] = PINGROUP(79, SOUTH, uim1, gp_pdm, _, phase_flag, _, _, _, _, _), + [80] = PINGROUP(80, SOUTH, uim1, _, phase_flag, _, _, _, _, _, _), + [81] = PINGROUP(81, WEST, rgmii, mdp_vsync, _, qdss_gpio, _, _, _, _, _), + [82] = PINGROUP(82, WEST, rgmii, mdp_vsync, _, phase_flag, qdss_gpio, _, _, _, _), + [83] = PINGROUP(83, WEST, rgmii, mdp_vsync, _, qdss_cti, _, _, _, _, _), + [84] = PINGROUP(84, SOUTH, _, phase_flag, atest_char, _, _, _, _, _, _), + [85] = PINGROUP(85, SOUTH, _, atest_char, _, _, _, _, _, _, _), + [86] = PINGROUP(86, SOUTH, copy_gp, _, atest_char, _, _, _, _, _, _), + [87] = PINGROUP(87, SOUTH, _, atest_char, _, _, _, _, _, _, _), + [88] = PINGROUP(88, WEST, _, usb0_hs, _, _, _, _, _, _, _), + [89] = PINGROUP(89, WEST, emac_phy_intr, pcie_ep_rst, tgu_ch, usb1_hs, _, _, _, _, _), + [90] = PINGROUP(90, WEST, mdp_vsync, mdp_vsync0_out, mdp_vsync1_out, + mdp_vsync2_out, mdp_vsync3_out, mdp_vsync4_out, mdp_vsync5_out, + pcie_clk_req, tgu_ch), + [91] = PINGROUP(91, WEST, rgmii, tgu_ch, _, _, _, _, _, _, _), + [92] = PINGROUP(92, WEST, rgmii, vfr_1, tgu_ch, _, phase_flag, qdss_gpio, _, _, _), + [93] = PINGROUP(93, WEST, rgmii, qdss_gpio, _, _, _, _, _, _, _), + [94] = PINGROUP(94, WEST, rgmii, qdss_gpio, _, _, _, _, _, _, _), + [95] = PINGROUP(95, WEST, rgmii, gp_pdm, qdss_gpio, _, _, _, _, _, _), + [96] = PINGROUP(96, WEST, rgmii, qdss_cti, _, _, _, _, _, _, _), + [97] = PINGROUP(97, WEST, rgmii, mdp_vsync, ldo_en, qdss_cti, _, _, _, _, _), + [98] = PINGROUP(98, WEST, mdp_vsync, ldo_update, qdss_cti, _, _, _, _, _, _), + [99] = PINGROUP(99, EAST, prng_rosc, _, _, _, _, _, _, _, _), + [100] = PINGROUP(100, WEST, _, _, _, _, _, _, _, _, _), + [101] = PINGROUP(101, WEST, emac_gcc, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, WEST, rgmii, dp_hot, emac_gcc, prng_rosc, _, _, _, _, _), + [103] = PINGROUP(103, WEST, rgmii, dp_hot, copy_phase, qdss_cti, _, _, _, _, _), + [104] = PINGROUP(104, WEST, usb_phy_ps, _, qdss_cti, dp_hot, _, _, _, _, _), + [105] = PINGROUP(105, SOUTH, _, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, EAST, mss_lte, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, EAST, mss_lte, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, SOUTH, mi2s_1, _, qdss_gpio, _, _, _, _, _, _), + [109] = PINGROUP(109, SOUTH, mi2s_1, _, qdss_gpio, _, _, _, _, _, _), + [110] = PINGROUP(110, SOUTH, wsa_data, mi2s_1, _, _, _, _, _, _, _), + [111] = PINGROUP(111, SOUTH, wsa_clk, mi2s_1, _, _, _, _, _, _, _), + [112] = PINGROUP(112, WEST, rgmii, _, qdss_cti, _, _, _, _, _, _), + [113] = PINGROUP(113, WEST, rgmii, edp_hot, _, qdss_cti, _, _, _, _, _), + [114] = PINGROUP(114, WEST, rgmii, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, SOUTH, ter_mi2s, atest_char, _, _, _, _, _, _, _), + [116] = PINGROUP(116, SOUTH, ter_mi2s, _, phase_flag, _, _, _, _, _, _), + [117] = PINGROUP(117, SOUTH, ter_mi2s, _, phase_flag, qdss_gpio, atest_char, _, _, _, _), + [118] = PINGROUP(118, SOUTH, ter_mi2s, adsp_ext, _, phase_flag, qdss_gpio, atest_char, + _, _, _), + [119] = PINGROUP(119, SOUTH, edp_lcd, _, phase_flag, qdss_gpio, atest_char, _, _, _, _), + [120] = PINGROUP(120, SOUTH, m_voc, qdss_gpio, atest_char, _, _, _, _, _, _), + [121] = PINGROUP(121, SOUTH, mclk1, atest_char, _, _, _, _, _, _, _), + [122] = PINGROUP(122, SOUTH, mclk2, _, _, _, _, _, _, _, _), + [123] = UFS_RESET(ufs_reset, 0x9f000), + [124] = SDC_QDSD_PINGROUP(sdc1_rclk, WEST, 0x9a000, 15, 0), + [125] = SDC_QDSD_PINGROUP(sdc1_clk, WEST, 0x9a000, 13, 6), + [126] = SDC_QDSD_PINGROUP(sdc1_cmd, WEST, 0x9a000, 11, 3), + [127] = SDC_QDSD_PINGROUP(sdc1_data, WEST, 0x9a000, 9, 0), + [128] = SDC_QDSD_PINGROUP(sdc2_clk, SOUTH, 0x98000, 14, 6), + [129] = SDC_QDSD_PINGROUP(sdc2_cmd, SOUTH, 0x98000, 11, 3), + [130] = SDC_QDSD_PINGROUP(sdc2_data, SOUTH, 0x98000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map qcs615_pdc_map[] = { + { 1, 45 }, { 3, 31 }, { 7, 55 }, { 9, 110 }, { 11, 34 }, + { 13, 33 }, { 14, 35 }, { 17, 46 }, { 19, 48 }, { 21, 83 }, + { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 125 }, { 41, 47 }, + { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 123 }, { 55, 56 }, + { 56, 57 }, { 57, 58 }, { 60, 60 }, { 71, 54 }, { 80, 73 }, + { 81, 64 }, { 82, 50 }, { 83, 65 }, { 84, 92 }, { 85, 99 }, + { 86, 67 }, { 87, 84 }, { 88, 124 }, { 89, 122 }, { 90, 69 }, + { 92, 88 }, { 93, 75 }, { 94, 91 }, { 95, 72 }, { 96, 82 }, + { 97, 74 }, { 98, 95 }, { 99, 94 }, { 100, 100 }, { 101, 40 }, + { 102, 93 }, { 103, 77 }, { 104, 78 }, { 105, 96 }, { 107, 97 }, + { 108, 111 }, { 112, 112 }, { 113, 113 }, { 117, 85 }, { 118, 102 }, + { 119, 87 }, { 120, 114 }, { 121, 89 }, { 122, 90 }, +}; + +static const struct msm_pinctrl_soc_data qcs615_tlmm = { + .pins = qcs615_pins, + .npins = ARRAY_SIZE(qcs615_pins), + .functions = qcs615_functions, + .nfunctions = ARRAY_SIZE(qcs615_functions), + .groups = qcs615_groups, + .ngroups = ARRAY_SIZE(qcs615_groups), + .ngpios = 123, + .tiles = qcs615_tiles, + .ntiles = ARRAY_SIZE(qcs615_tiles), + .wakeirq_map = qcs615_pdc_map, + .nwakeirq_map = ARRAY_SIZE(qcs615_pdc_map), +}; + +static const struct of_device_id qcs615_tlmm_of_match[] = { + { + .compatible = "qcom,qcs615-tlmm", + }, + {}, +}; + +static int qcs615_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &qcs615_tlmm); +} + +static struct platform_driver qcs615_tlmm_driver = { + .driver = { + .name = "qcs615-tlmm", + .of_match_table = qcs615_tlmm_of_match, + }, + .probe = qcs615_tlmm_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init qcs615_tlmm_init(void) +{ + return platform_driver_register(&qcs615_tlmm_driver); +} +arch_initcall(qcs615_tlmm_init); + +static void __exit qcs615_tlmm_exit(void) +{ + platform_driver_unregister(&qcs615_tlmm_driver); +} +module_exit(qcs615_tlmm_exit); + +MODULE_DESCRIPTION("QTI QCS615 TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, qcs615_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-qcs8300.c b/drivers/pinctrl/qcom/pinctrl-qcs8300.c new file mode 100644 index 0000000000000..ba6de944a859a --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-qcs8300.c @@ -0,0 +1,1246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11)\ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10, \ + msm_mux_##f11 /* egpio mode */ \ + }, \ + .nfuncs = 12, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define QUP_I3C(qup_mode, qup_offset) \ + { \ + .mode = qup_mode, \ + .offset = qup_offset, \ + } + +#define QUP_I3C_6_MODE_OFFSET 0xaf000 +#define QUP_I3C_7_MODE_OFFSET 0xb0000 +#define QUP_I3C_13_MODE_OFFSET 0xb1000 +#define QUP_I3C_14_MODE_OFFSET 0xb2000 + +static const struct pinctrl_pin_desc qcs8300_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "UFS_RESET"), + PINCTRL_PIN(134, "SDC1_RCLK"), + PINCTRL_PIN(135, "SDC1_CLK"), + PINCTRL_PIN(136, "SDC1_CMD"), + PINCTRL_PIN(137, "SDC1_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); + +static const unsigned int ufs_reset_pins[] = { 133 }; +static const unsigned int sdc1_rclk_pins[] = { 134 }; +static const unsigned int sdc1_clk_pins[] = { 135 }; +static const unsigned int sdc1_cmd_pins[] = { 136 }; +static const unsigned int sdc1_data_pins[] = { 137 }; + +enum qcs8300_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_usb2, + msm_mux_audio_ref, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c_scl, + msm_mux_cci_i2c_sda, + msm_mux_cci_timer, + msm_mux_cri_trng, + msm_mux_dbg_out, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_edp0_hot, + msm_mux_edp0_lcd, + msm_mux_edp1_lcd, + msm_mux_egpio, + msm_mux_emac0_mcg0, + msm_mux_emac0_mcg1, + msm_mux_emac0_mcg2, + msm_mux_emac0_mcg3, + msm_mux_emac0_mdc, + msm_mux_emac0_mdio, + msm_mux_emac0_ptp_aux, + msm_mux_emac0_ptp_pps, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gcc_gp4, + msm_mux_gcc_gp5, + msm_mux_hs0_mi2s, + msm_mux_hs1_mi2s, + msm_mux_hs2_mi2s, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp0_vsync0, + msm_mux_mdp0_vsync1, + msm_mux_mdp0_vsync3, + msm_mux_mdp0_vsync6, + msm_mux_mdp0_vsync7, + msm_mux_mdp_vsync, + msm_mux_mi2s1_data0, + msm_mux_mi2s1_data1, + msm_mux_mi2s1_sck, + msm_mux_mi2s1_ws, + msm_mux_mi2s2_data0, + msm_mux_mi2s2_data1, + msm_mux_mi2s2_sck, + msm_mux_mi2s2_ws, + msm_mux_mi2s_mclk0, + msm_mux_mi2s_mclk1, + msm_mux_pcie0_clkreq, + msm_mux_pcie1_clkreq, + msm_mux_phase_flag, + msm_mux_pll_bist, + msm_mux_pll_clk, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se2, + msm_mux_qup0_se3, + msm_mux_qup0_se4, + msm_mux_qup0_se5, + msm_mux_qup0_se6, + msm_mux_qup0_se7, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup1_se7, + msm_mux_qup2_se0, + msm_mux_sailss_emac0, + msm_mux_sailss_ospi, + msm_mux_sgmii_phy, + msm_mux_tb_trig, + msm_mux_tgu_ch0, + msm_mux_tgu_ch1, + msm_mux_tgu_ch2, + msm_mux_tgu_ch3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_tsense_pwm4, + msm_mux_usb2phy_ac, + msm_mux_vsense_trigger, + msm_mux__, +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", + "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", + "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", + "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116", + "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122", + "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128", + "gpio129", "gpio130", "gpio131", "gpio132", +}; + +static const char *const aoss_cti_groups[] = { + "gpio37", "gpio38", "gpio39", "gpio40", +}; + +static const char * const atest_char_groups[] = { + "gpio66", "gpio70", "gpio71", "gpio72", "gpio93", +}; + +static const char * const atest_usb2_groups[] = { + "gpio63", "gpio83", "gpio92", "gpio74", "gpio84", "gpio87", "gpio67", + "gpio75", "gpio85", "gpio65", "gpio68", "gpio80", "gpio64", "gpio69", + "gpio81", +}; + +static const char * const audio_ref_groups[] = { + "gpio105", +}; + +static const char * const cam_mclk_groups[] = { + "gpio67", "gpio68", "gpio69", +}; + +static const char * const cci_async_groups[] = { + "gpio63", "gpio64", "gpio65", "gpio29", "gpio30", "gpio31", +}; + +static const char * const cci_i2c_scl_groups[] = { + "gpio58", "gpio30", "gpio60", "gpio32", "gpio62", "gpio55", +}; + +static const char * const cci_i2c_sda_groups[] = { + "gpio57", "gpio29", "gpio59", "gpio31", "gpio61", "gpio54", +}; + +static const char *const cci_timer_groups[] = { + "gpio63", "gpio64", "gpio65", "gpio49", "gpio50", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char *const cri_trng_groups[] = { + "gpio92", "gpio90", "gpio91", +}; + +static const char *const dbg_out_groups[] = { + "gpio75", +}; + +static const char * const ddr_bist_groups[] = { + "gpio53", "gpio54", "gpio55", "gpio56", +}; + +static const char *const ddr_pxi0_groups[] = { + "gpio68", "gpio69", +}; + +static const char *const ddr_pxi1_groups[] = { + "gpio49", "gpio50", +}; + +static const char *const ddr_pxi2_groups[] = { + "gpio52", "gpio83", +}; + +static const char *const ddr_pxi3_groups[] = { + "gpio80", "gpio81", +}; + +static const char *const edp0_hot_groups[] = { + "gpio94", +}; + +static const char *const edp0_lcd_groups[] = { + "gpio48", +}; + +static const char *const edp1_lcd_groups[] = { + "gpio49", +}; + +static const char *const egpio_groups[] = { + "gpio110", "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", + "gpio116", "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", + "gpio122", "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", + "gpio128", "gpio129", "gpio130", "gpio131", "gpio132", +}; + +static const char *const emac0_mcg0_groups[] = { + "gpio10", +}; + +static const char *const emac0_mcg1_groups[] = { + "gpio11", +}; + +static const char *const emac0_mcg2_groups[] = { + "gpio24", +}; + +static const char *const emac0_mcg3_groups[] = { + "gpio79", +}; + +static const char *const emac0_mdc_groups[] = { + "gpio5", +}; + +static const char *const emac0_mdio_groups[] = { + "gpio6", +}; + +static const char * const emac0_ptp_aux_groups[] = { + "gpio24", "gpio31", "gpio32", "gpio79", +}; + +static const char * const emac0_ptp_pps_groups[] = { + "gpio24", "gpio29", "gpio30", "gpio79", +}; + +static const char *const gcc_gp1_groups[] = { + "gpio35", "gpio84", +}; + +static const char *const gcc_gp2_groups[] = { + "gpio36", "gpio81", +}; + +static const char *const gcc_gp3_groups[] = { + "gpio69", "gpio82", +}; + +static const char *const gcc_gp4_groups[] = { + "gpio68", "gpio83", +}; + +static const char *const gcc_gp5_groups[] = { + "gpio76", "gpio77", +}; + +static const char * const hs0_mi2s_groups[] = { + "gpio106", "gpio107", "gpio108", "gpio109", +}; + +static const char * const hs1_mi2s_groups[] = { + "gpio45", "gpio46", "gpio47", "gpio48", +}; + +static const char * const hs2_mi2s_groups[] = { + "gpio49", "gpio50", "gpio51", "gpio52", +}; + +static const char * const ibi_i3c_groups[] = { + "gpio17", "gpio18", "gpio19", "gpio20", "gpio37", "gpio38", + "gpio39", "gpio40", +}; + +static const char *const jitter_bist_groups[] = { + "gpio97", +}; + +static const char *const mdp0_vsync0_groups[] = { + "gpio89", +}; + +static const char *const mdp0_vsync1_groups[] = { + "gpio90", +}; + +static const char *const mdp0_vsync3_groups[] = { + "gpio91", +}; + +static const char *const mdp0_vsync6_groups[] = { + "gpio80", +}; + +static const char *const mdp0_vsync7_groups[] = { + "gpio81", +}; + +static const char *const mdp_vsync_groups[] = { + "gpio42", "gpio52", "gpio32", +}; + +static const char *const mi2s1_data0_groups[] = { + "gpio100", +}; + +static const char *const mi2s1_data1_groups[] = { + "gpio101", +}; + +static const char *const mi2s1_sck_groups[] = { + "gpio98", +}; + +static const char *const mi2s1_ws_groups[] = { + "gpio99", +}; + +static const char *const mi2s2_data0_groups[] = { + "gpio104", +}; + +static const char *const mi2s2_data1_groups[] = { + "gpio105", +}; + +static const char *const mi2s2_sck_groups[] = { + "gpio102", +}; + +static const char *const mi2s2_ws_groups[] = { + "gpio103", +}; + +static const char *const mi2s_mclk0_groups[] = { + "gpio97", +}; + +static const char *const mi2s_mclk1_groups[] = { + "gpio109", +}; + +static const char *const pcie0_clkreq_groups[] = { + "gpio1", +}; + +static const char *const pcie1_clkreq_groups[] = { + "gpio22", +}; + +static const char *const phase_flag_groups[] = { + "gpio66", "gpio56", "gpio118", "gpio117", "gpio116", + "gpio3", "gpio114", "gpio113", "gpio112", "gpio111", + "gpio110", "gpio28", "gpio55", "gpio108", "gpio107", + "gpio106", "gpio105", "gpio104", "gpio103", "gpio102", + "gpio101", "gpio100", "gpio99", "gpio125", "gpio98", + "gpio54", "gpio25", "gpio26", "gpio122", "gpio121", + "gpio120", "gpio9", +}; + +static const char *const pll_bist_groups[] = { + "gpio107", +}; + +static const char *const pll_clk_groups[] = { + "gpio74", +}; + +static const char *const prng_rosc0_groups[] = { + "gpio57", +}; + +static const char *const prng_rosc1_groups[] = { + "gpio58", +}; + +static const char *const prng_rosc2_groups[] = { + "gpio59", +}; + +static const char *const prng_rosc3_groups[] = { + "gpio60", +}; + +static const char *const qdss_cti_groups[] = { + "gpio4", "gpio5", "gpio23", "gpio24", "gpio49", "gpio50", + "gpio51", "gpio52", +}; + +static const char *const qdss_gpio_groups[] = { + "gpio57", "gpio58", "gpio97", "gpio106", + "gpio59", "gpio107", "gpio60", "gpio108", + "gpio36", "gpio100", "gpio61", "gpio101", + "gpio62", "gpio102", "gpio33", "gpio103", + "gpio34", "gpio104", "gpio75", "gpio105", + "gpio72", "gpio109", "gpio71", "gpio110", + "gpio70", "gpio111", "gpio63", "gpio112", + "gpio64", "gpio113", "gpio65", "gpio114", + "gpio73", "gpio98", "gpio74", "gpio99", +}; + +static const char *const qup0_se0_groups[] = { + "gpio17", "gpio18", "gpio19", "gpio20", +}; + +static const char *const qup0_se1_groups[] = { + "gpio19", "gpio20", "gpio17", "gpio18", +}; + +static const char *const qup0_se2_groups[] = { + "gpio33", "gpio34", "gpio35", "gpio36", +}; + +static const char *const qup0_se3_groups[] = { + "gpio25", "gpio26", "gpio27", "gpio28", +}; + +static const char *const qup0_se4_groups[] = { + "gpio29", "gpio30", "gpio31", "gpio32", +}; + +static const char *const qup0_se5_groups[] = { + "gpio21", "gpio22", "gpio23", "gpio24", +}; + +static const char *const qup0_se6_groups[] = { + "gpio80", "gpio81", "gpio82", "gpio83", +}; + +static const char *const qup0_se7_groups[] = { + "gpio43", "gpio44", "gpio43", "gpio44", +}; + +static const char *const qup1_se0_groups[] = { + "gpio37", "gpio38", "gpio39", "gpio40", +}; + +static const char *const qup1_se1_groups[] = { + "gpio39", "gpio40", "gpio37", "gpio38", +}; + +static const char *const qup1_se2_groups[] = { + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", +}; + +static const char *const qup1_se3_groups[] = { + "gpio41", "gpio42", "gpio41", "gpio42", +}; + +static const char *const qup1_se4_groups[] = { + "gpio45", "gpio46", "gpio47", "gpio48", +}; + +static const char *const qup1_se5_groups[] = { + "gpio49", "gpio50", "gpio51", "gpio52", +}; + +static const char *const qup1_se6_groups[] = { + "gpio89", "gpio90", "gpio91", "gpio92", +}; + +static const char *const qup1_se7_groups[] = { + "gpio91", "gpio92", "gpio89", "gpio90", +}; + +static const char *const qup2_se0_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", +}; + +static const char *const sailss_emac0_groups[] = { + "gpio15", "gpio16", +}; + +static const char *const sailss_ospi_groups[] = { + "gpio15", "gpio16", +}; + +static const char *const sgmii_phy_groups[] = { + "gpio4", +}; + +static const char *const tb_trig_groups[] = { + "gpio14", +}; + +static const char *const tgu_ch0_groups[] = { + "gpio43", +}; + +static const char *const tgu_ch1_groups[] = { + "gpio44", +}; + +static const char *const tgu_ch2_groups[] = { + "gpio29", +}; + +static const char *const tgu_ch3_groups[] = { + "gpio30", +}; + +static const char *const tsense_pwm1_groups[] = { + "gpio79", +}; + +static const char *const tsense_pwm2_groups[] = { + "gpio78", +}; + +static const char *const tsense_pwm3_groups[] = { + "gpio77", +}; + +static const char *const tsense_pwm4_groups[] = { + "gpio76", +}; + +static const char *const usb2phy_ac_groups[] = { + "gpio7", "gpio8", +}; + +static const char *const vsense_trigger_groups[] = { + "gpio67", +}; + +static const struct pinfunction qcs8300_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb2), + MSM_PIN_FUNCTION(audio_ref), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(cci_i2c_scl), + MSM_PIN_FUNCTION(cci_i2c_sda), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(edp0_hot), + MSM_PIN_FUNCTION(edp0_lcd), + MSM_PIN_FUNCTION(edp1_lcd), + MSM_PIN_FUNCTION(egpio), + MSM_PIN_FUNCTION(emac0_mcg0), + MSM_PIN_FUNCTION(emac0_mcg1), + MSM_PIN_FUNCTION(emac0_mcg2), + MSM_PIN_FUNCTION(emac0_mcg3), + MSM_PIN_FUNCTION(emac0_mdc), + MSM_PIN_FUNCTION(emac0_mdio), + MSM_PIN_FUNCTION(emac0_ptp_aux), + MSM_PIN_FUNCTION(emac0_ptp_pps), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gcc_gp4), + MSM_PIN_FUNCTION(gcc_gp5), + MSM_PIN_FUNCTION(hs0_mi2s), + MSM_PIN_FUNCTION(hs1_mi2s), + MSM_PIN_FUNCTION(hs2_mi2s), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp0_vsync0), + MSM_PIN_FUNCTION(mdp0_vsync1), + MSM_PIN_FUNCTION(mdp0_vsync3), + MSM_PIN_FUNCTION(mdp0_vsync6), + MSM_PIN_FUNCTION(mdp0_vsync7), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mi2s1_data0), + MSM_PIN_FUNCTION(mi2s1_data1), + MSM_PIN_FUNCTION(mi2s1_sck), + MSM_PIN_FUNCTION(mi2s1_ws), + MSM_PIN_FUNCTION(mi2s2_data0), + MSM_PIN_FUNCTION(mi2s2_data1), + MSM_PIN_FUNCTION(mi2s2_sck), + MSM_PIN_FUNCTION(mi2s2_ws), + MSM_PIN_FUNCTION(mi2s_mclk0), + MSM_PIN_FUNCTION(mi2s_mclk1), + MSM_PIN_FUNCTION(pcie0_clkreq), + MSM_PIN_FUNCTION(pcie1_clkreq), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist), + MSM_PIN_FUNCTION(pll_clk), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3), + MSM_PIN_FUNCTION(qup0_se4), + MSM_PIN_FUNCTION(qup0_se5), + MSM_PIN_FUNCTION(qup0_se6), + MSM_PIN_FUNCTION(qup0_se7), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(sailss_emac0), + MSM_PIN_FUNCTION(sailss_ospi), + MSM_PIN_FUNCTION(sgmii_phy), + MSM_PIN_FUNCTION(tb_trig), + MSM_PIN_FUNCTION(tgu_ch0), + MSM_PIN_FUNCTION(tgu_ch1), + MSM_PIN_FUNCTION(tgu_ch2), + MSM_PIN_FUNCTION(tgu_ch3), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(tsense_pwm4), + MSM_PIN_FUNCTION(usb2phy_ac), + MSM_PIN_FUNCTION(vsense_trigger), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup qcs8300_groups[] = { + [0] = PINGROUP(0, _, _, _, _, _, _, _, _, _, _, _), + [1] = PINGROUP(1, pcie0_clkreq, _, _, _, _, _, _, _, _, _, _), + [2] = PINGROUP(2, _, _, _, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, phase_flag, _, _, _, _, _, _, _, _, _, _), + [4] = PINGROUP(4, sgmii_phy, qdss_cti, _, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, emac0_mdc, qdss_cti, _, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, emac0_mdio, _, _, _, _, _, _, _, _, _, _), + [7] = PINGROUP(7, usb2phy_ac, _, _, _, _, _, _, _, _, _, _), + [8] = PINGROUP(8, usb2phy_ac, _, _, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, phase_flag, _, _, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, qup2_se0, emac0_mcg0, _, _, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup2_se0, emac0_mcg1, _, _, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup2_se0, _, _, _, _, _, _, _, _, _, _), + [13] = PINGROUP(13, qup2_se0, _, _, _, _, _, _, _, _, _, _), + [14] = PINGROUP(14, qup2_se0, tb_trig, _, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, qup2_se0, _, sailss_ospi, sailss_emac0, _, _, _, _, _, _, _), + [16] = PINGROUP(16, qup2_se0, _, _, sailss_ospi, sailss_emac0, _, _, _, _, _, _), + [17] = PINGROUP(17, qup0_se0, qup0_se1, ibi_i3c, _, _, _, _, _, _, _, _), + [18] = PINGROUP(18, qup0_se0, qup0_se1, ibi_i3c, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, qup0_se1, qup0_se0, cci_timer, ibi_i3c, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup0_se1, qup0_se0, cci_timer, ibi_i3c, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup0_se5, cci_timer, _, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, pcie1_clkreq, qup0_se5, cci_timer, _, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup0_se5, cci_timer, qdss_cti, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup0_se5, emac0_ptp_aux, emac0_ptp_pps, qdss_cti, + emac0_mcg2, _, _, _, _, _, _), + [25] = PINGROUP(25, qup0_se3, phase_flag, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup0_se3, phase_flag, _, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup0_se3, _, _, _, _, _, _, _, _, _, _), + [28] = PINGROUP(28, qup0_se3, phase_flag, _, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, qup0_se4, cci_i2c_sda, cci_async, emac0_ptp_pps, + tgu_ch2, _, _, _, _, _, _), + [30] = PINGROUP(30, qup0_se4, cci_i2c_scl, cci_async, emac0_ptp_pps, + tgu_ch3, _, _, _, _, _, _), + [31] = PINGROUP(31, qup0_se4, cci_i2c_sda, cci_async, emac0_ptp_aux, _, _, _, _, _, _, _), + [32] = PINGROUP(32, qup0_se4, cci_i2c_scl, emac0_ptp_aux, mdp_vsync, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qup0_se2, qdss_gpio, _, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, qup0_se2, qdss_gpio, _, _, _, _, _, _, _, _, _), + [35] = PINGROUP(35, qup0_se2, gcc_gp1, _, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup0_se2, gcc_gp2, qdss_gpio, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se0, ibi_i3c, qup1_se1, aoss_cti, _, _, _, _, _, _, _), + [38] = PINGROUP(38, qup1_se0, ibi_i3c, qup1_se1, aoss_cti, _, _, _, _, _, _, _), + [39] = PINGROUP(39, qup1_se1, ibi_i3c, qup1_se0, aoss_cti, _, _, _, _, _, _, _), + [40] = PINGROUP(40, qup1_se1, ibi_i3c, qup1_se0, aoss_cti, _, _, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se3, _, _, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se3, _, mdp_vsync, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, qup0_se7, _, tgu_ch0, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, qup0_se7, _, tgu_ch1, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se4, hs1_mi2s, _, _, _, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se4, hs1_mi2s, _, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se4, hs1_mi2s, _, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, qup1_se4, hs1_mi2s, edp0_lcd, _, _, _, _, _, _, _, _), + [49] = PINGROUP(49, qup1_se5, hs2_mi2s, cci_timer, qdss_cti, edp1_lcd, + ddr_pxi1, _, _, _, _, _), + [50] = PINGROUP(50, qup1_se5, hs2_mi2s, cci_timer, qdss_cti, _, ddr_pxi1, _, _, _, _, _), + [51] = PINGROUP(51, qup1_se5, hs2_mi2s, qdss_cti, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, qup1_se5, hs2_mi2s, qdss_cti, mdp_vsync, ddr_pxi2, _, _, _, _, _, _), + [53] = PINGROUP(53, ddr_bist, _, _, _, _, _, _, _, _, _, _), + [54] = PINGROUP(54, cci_i2c_sda, phase_flag, ddr_bist, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, cci_i2c_scl, phase_flag, ddr_bist, _, _, _, _, _, _, _, _), + [56] = PINGROUP(56, phase_flag, ddr_bist, _, _, _, _, _, _, _, _, _), + [57] = PINGROUP(57, cci_i2c_sda, prng_rosc0, qdss_gpio, _, _, _, _, _, _, _, _), + [58] = PINGROUP(58, cci_i2c_scl, prng_rosc1, qdss_gpio, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, cci_i2c_sda, prng_rosc2, qdss_gpio, _, _, _, _, _, _, _, _), + [60] = PINGROUP(60, cci_i2c_scl, prng_rosc3, qdss_gpio, _, _, _, _, _, _, _, _), + [61] = PINGROUP(61, cci_i2c_sda, qdss_gpio, _, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, cci_i2c_scl, qdss_gpio, _, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, cci_timer, cci_async, qdss_gpio, atest_usb2, _, _, _, _, _, _, _), + [64] = PINGROUP(64, cci_timer, cci_async, qdss_gpio, atest_usb2, _, _, _, _, _, _, _), + [65] = PINGROUP(65, cci_timer, cci_async, qdss_gpio, atest_usb2, _, _, _, _, _, _, _), + [66] = PINGROUP(66, phase_flag, _, atest_char, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, cam_mclk, vsense_trigger, atest_usb2, _, _, _, _, _, _, _, _), + [68] = PINGROUP(68, cam_mclk, gcc_gp4, atest_usb2, ddr_pxi0, _, _, _, _, _, _, _), + [69] = PINGROUP(69, cam_mclk, gcc_gp3, atest_usb2, ddr_pxi0, _, _, _, _, _, _, _), + [70] = PINGROUP(70, qdss_gpio, atest_char, _, _, _, _, _, _, _, _, _), + [71] = PINGROUP(71, qdss_gpio, atest_char, _, _, _, _, _, _, _, _, _), + [72] = PINGROUP(72, qdss_gpio, atest_char, _, _, _, _, _, _, _, _, _), + [73] = PINGROUP(73, _, qdss_gpio, _, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, pll_clk, qdss_gpio, atest_usb2, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, _, dbg_out, qdss_gpio, atest_usb2, _, _, _, _, _, _, _), + [76] = PINGROUP(76, gcc_gp5, tsense_pwm4, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, gcc_gp5, tsense_pwm3, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, tsense_pwm2, _, _, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, emac0_ptp_aux, emac0_ptp_pps, emac0_mcg3, _, + tsense_pwm1, _, _, _, _, _, _), + [80] = PINGROUP(80, qup0_se6, mdp0_vsync6, _, atest_usb2, ddr_pxi3, _, _, _, _, _, _), + [81] = PINGROUP(81, qup0_se6, mdp0_vsync7, gcc_gp2, _, atest_usb2, ddr_pxi3, _, _, _, _, _), + [82] = PINGROUP(82, qup0_se6, gcc_gp3, _, _, _, _, _, _, _, _, _), + [83] = PINGROUP(83, qup0_se6, gcc_gp4, _, atest_usb2, ddr_pxi2, _, _, _, _, _, _), + [84] = PINGROUP(84, qup1_se2, gcc_gp1, _, atest_usb2, _, _, _, _, _, _, _), + [85] = PINGROUP(85, qup1_se2, _, atest_usb2, _, _, _, _, _, _, _, _), + [86] = PINGROUP(86, qup1_se2, _, _, _, _, _, _, _, _, _, _), + [87] = PINGROUP(87, qup1_se2, _, atest_usb2, _, _, _, _, _, _, _, _), + [88] = PINGROUP(88, qup1_se2, _, _, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, qup1_se6, qup1_se7, mdp0_vsync0, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, qup1_se6, qup1_se7, mdp0_vsync1, cri_trng, _, _, _, _, _, _, _), + [91] = PINGROUP(91, qup1_se7, qup1_se6, mdp0_vsync3, cri_trng, _, _, _, _, _, _, _), + [92] = PINGROUP(92, qup1_se7, qup1_se6, cri_trng, _, atest_usb2, _, _, _, _, _, _), + [93] = PINGROUP(93, atest_char, _, _, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, edp0_hot, _, _, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, _, _, _, _, _, _, _, _, _, _, _), + [96] = PINGROUP(96, _, _, _, _, _, _, _, _, _, _, _), + [97] = PINGROUP(97, mi2s_mclk0, jitter_bist, qdss_gpio, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, mi2s1_sck, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [99] = PINGROUP(99, mi2s1_ws, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [100] = PINGROUP(100, mi2s1_data0, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [101] = PINGROUP(101, mi2s1_data1, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [102] = PINGROUP(102, mi2s2_sck, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [103] = PINGROUP(103, mi2s2_ws, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [104] = PINGROUP(104, mi2s2_data0, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [105] = PINGROUP(105, mi2s2_data1, audio_ref, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [106] = PINGROUP(106, hs0_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [107] = PINGROUP(107, hs0_mi2s, pll_bist, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [108] = PINGROUP(108, hs0_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _), + [109] = PINGROUP(109, hs0_mi2s, mi2s_mclk1, qdss_gpio, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _, egpio), + [111] = PINGROUP(111, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _, egpio), + [112] = PINGROUP(112, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _, egpio), + [113] = PINGROUP(113, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _, egpio), + [114] = PINGROUP(114, phase_flag, _, qdss_gpio, _, _, _, _, _, _, _, egpio), + [115] = PINGROUP(115, _, _, _, _, _, _, _, _, _, _, egpio), + [116] = PINGROUP(116, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [117] = PINGROUP(117, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [118] = PINGROUP(118, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [119] = PINGROUP(119, _, _, _, _, _, _, _, _, _, _, egpio), + [120] = PINGROUP(120, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [121] = PINGROUP(121, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [122] = PINGROUP(122, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [123] = PINGROUP(123, _, _, _, _, _, _, _, _, _, _, egpio), + [124] = PINGROUP(124, _, _, _, _, _, _, _, _, _, _, egpio), + [125] = PINGROUP(125, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [126] = PINGROUP(126, _, _, _, _, _, _, _, _, _, _, egpio), + [127] = PINGROUP(127, _, _, _, _, _, _, _, _, _, _, egpio), + [128] = PINGROUP(128, _, _, _, _, _, _, _, _, _, _, egpio), + [129] = PINGROUP(129, _, _, _, _, _, _, _, _, _, _, egpio), + [130] = PINGROUP(130, _, _, _, _, _, _, _, _, _, _, egpio), + [131] = PINGROUP(131, _, _, _, _, _, _, _, _, _, _, egpio), + [132] = PINGROUP(132, _, _, _, _, _, _, _, _, _, _, egpio), + [133] = UFS_RESET(ufs_reset, 0x92000), + [134] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x89000, 15, 0), + [135] = SDC_QDSD_PINGROUP(sdc1_clk, 0x89000, 13, 6), + [136] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x89000, 11, 3), + [137] = SDC_QDSD_PINGROUP(sdc1_data, 0x89000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map qcs8300_pdc_map[] = { + { 0, 169 }, { 1, 174 }, { 2, 221 }, { 3, 176 }, { 4, 171 }, + { 9, 198 }, { 10, 187 }, { 11, 188 }, { 13, 211 }, { 16, 203 }, + { 17, 213 }, { 18, 209 }, { 19, 201 }, { 20, 230 }, { 21, 231 }, + { 22, 175 }, { 23, 170 }, { 24, 232 }, { 28, 235 }, { 29, 216 }, + { 31, 208 }, { 32, 200 }, { 36, 212 }, { 37, 177 }, { 38, 178 }, + { 39, 184 }, { 40, 185 }, { 42, 186 }, { 44, 194 }, { 45, 173 }, + { 48, 195 }, { 51, 215 }, { 52, 197 }, { 53, 192 }, { 56, 193 }, + { 66, 238 }, { 67, 172 }, { 68, 182 }, { 69, 179 }, { 70, 181 }, + { 71, 202 }, { 72, 183 }, { 73, 189 }, { 74, 196 }, { 75, 190 }, + { 76, 191 }, { 77, 204 }, { 78, 206 }, { 79, 207 }, { 83, 214 }, + { 84, 205 }, { 87, 237 }, { 89, 225 }, { 90, 217 }, { 91, 218 }, + { 92, 226 }, { 93, 227 }, { 94, 228 }, { 95, 236 }, { 97, 199 }, + { 98, 229 }, { 99, 180 }, { 100, 220 }, { 101, 239 }, { 102, 219 }, + { 103, 233 }, { 104, 234 }, { 105, 223 }, { 129, 210 }, { 130, 222 }, +}; + +static const struct msm_pinctrl_soc_data qcs8300_pinctrl = { + .pins = qcs8300_pins, + .npins = ARRAY_SIZE(qcs8300_pins), + .functions = qcs8300_functions, + .nfunctions = ARRAY_SIZE(qcs8300_functions), + .groups = qcs8300_groups, + .ngroups = ARRAY_SIZE(qcs8300_groups), + .ngpios = 133, + .wakeirq_map = qcs8300_pdc_map, + .nwakeirq_map = ARRAY_SIZE(qcs8300_pdc_map), + .egpio_func = 11, +}; + +static int qcs8300_pinctrl_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &qcs8300_pinctrl); +} + +static const struct of_device_id qcs8300_pinctrl_of_match[] = { + { .compatible = "qcom,qcs8300-tlmm", }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcs8300_pinctrl_of_match); + +static struct platform_driver qcs8300_pinctrl_driver = { + .driver = { + .name = "qcs8300-tlmm", + .of_match_table = qcs8300_pinctrl_of_match, + }, + .probe = qcs8300_pinctrl_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init qcs8300_pinctrl_init(void) +{ + return platform_driver_register(&qcs8300_pinctrl_driver); +} +arch_initcall(qcs8300_pinctrl_init); + +static void __exit qcs8300_pinctrl_exit(void) +{ + platform_driver_unregister(&qcs8300_pinctrl_driver); +} +module_exit(qcs8300_pinctrl_exit); + +MODULE_DESCRIPTION("QTI QCS8300 pinctrl driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c b/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c index 4d2f6f495163b..b5808fcfb13cd 100644 --- a/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c +++ b/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c @@ -145,7 +145,7 @@ static struct platform_driver qdf2xxx_pinctrl_driver = { .acpi_match_table = qdf2xxx_acpi_ids, }, .probe = qdf2xxx_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init qdf2xxx_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-qdu1000.c b/drivers/pinctrl/qcom/pinctrl-qdu1000.c index da4f940bc8d4e..47bc529ef550d 100644 --- a/drivers/pinctrl/qcom/pinctrl-qdu1000.c +++ b/drivers/pinctrl/qcom/pinctrl-qdu1000.c @@ -1248,7 +1248,7 @@ static struct platform_driver qdu1000_tlmm_driver = { .of_match_table = qdu1000_tlmm_of_match, }, .probe = qdu1000_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init qdu1000_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sa8775p.c b/drivers/pinctrl/qcom/pinctrl-sa8775p.c index 5459c0c681a23..8fdea25d8d67e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sa8775p.c +++ b/drivers/pinctrl/qcom/pinctrl-sa8775p.c @@ -1530,7 +1530,7 @@ static struct platform_driver sa8775p_pinctrl_driver = { .of_match_table = sa8775p_pinctrl_of_match, }, .probe = sa8775p_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sa8775p_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sar2130p.c b/drivers/pinctrl/qcom/pinctrl-sar2130p.c new file mode 100644 index 0000000000000..19a2e37826c7b --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sar2130p.c @@ -0,0 +1,1505 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Limited + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + }, \ + .nfuncs = 10, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 4, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc sar2130p_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "SDC1_RCLK"), + PINCTRL_PIN(157, "SDC1_CLK"), + PINCTRL_PIN(158, "SDC1_CMD"), + PINCTRL_PIN(159, "SDC1_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); + +static const unsigned int sdc1_rclk_pins[] = { 156 }; +static const unsigned int sdc1_clk_pins[] = { 157 }; +static const unsigned int sdc1_cmd_pins[] = { 158 }; +static const unsigned int sdc1_data_pins[] = { 159 }; + +enum sar2130p_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_char0, + msm_mux_atest_char1, + msm_mux_atest_char2, + msm_mux_atest_char3, + msm_mux_atest_usb0, + msm_mux_atest_usb00, + msm_mux_atest_usb01, + msm_mux_atest_usb02, + msm_mux_atest_usb03, + msm_mux_audio_ref, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c, + msm_mux_cci_timer0, + msm_mux_cci_timer1, + msm_mux_cci_timer2, + msm_mux_cci_timer3, + msm_mux_cci_timer4, + msm_mux_cri_trng, + msm_mux_cri_trng0, + msm_mux_cri_trng1, + msm_mux_dbg_out, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_dp0_hot, + msm_mux_ext_mclk0, + msm_mux_ext_mclk1, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_host2wlan_sol, + msm_mux_i2s0_data0, + msm_mux_i2s0_data1, + msm_mux_i2s0_sck, + msm_mux_i2s0_ws, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync0, + msm_mux_mdp_vsync1, + msm_mux_mdp_vsync2, + msm_mux_mdp_vsync3, + msm_mux_pcie0_clkreqn, + msm_mux_pcie1_clkreqn, + msm_mux_phase_flag0, + msm_mux_phase_flag1, + msm_mux_phase_flag10, + msm_mux_phase_flag11, + msm_mux_phase_flag12, + msm_mux_phase_flag13, + msm_mux_phase_flag14, + msm_mux_phase_flag15, + msm_mux_phase_flag16, + msm_mux_phase_flag17, + msm_mux_phase_flag18, + msm_mux_phase_flag19, + msm_mux_phase_flag2, + msm_mux_phase_flag20, + msm_mux_phase_flag21, + msm_mux_phase_flag22, + msm_mux_phase_flag23, + msm_mux_phase_flag24, + msm_mux_phase_flag25, + msm_mux_phase_flag26, + msm_mux_phase_flag27, + msm_mux_phase_flag28, + msm_mux_phase_flag29, + msm_mux_phase_flag3, + msm_mux_phase_flag30, + msm_mux_phase_flag31, + msm_mux_phase_flag4, + msm_mux_phase_flag5, + msm_mux_phase_flag6, + msm_mux_phase_flag7, + msm_mux_phase_flag8, + msm_mux_phase_flag9, + msm_mux_pll_bist, + msm_mux_pll_clk, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qdss_gpio0, + msm_mux_qdss_gpio1, + msm_mux_qdss_gpio10, + msm_mux_qdss_gpio11, + msm_mux_qdss_gpio12, + msm_mux_qdss_gpio13, + msm_mux_qdss_gpio14, + msm_mux_qdss_gpio15, + msm_mux_qdss_gpio2, + msm_mux_qdss_gpio3, + msm_mux_qdss_gpio4, + msm_mux_qdss_gpio5, + msm_mux_qdss_gpio6, + msm_mux_qdss_gpio7, + msm_mux_qdss_gpio8, + msm_mux_qdss_gpio9, + msm_mux_qspi0, + msm_mux_qspi1, + msm_mux_qspi2, + msm_mux_qspi3, + msm_mux_qspi_clk, + msm_mux_qspi_cs0, + msm_mux_qspi_cs1, + msm_mux_qup0, + msm_mux_qup1, + msm_mux_qup2, + msm_mux_qup3, + msm_mux_qup4, + msm_mux_qup5, + msm_mux_qup6, + msm_mux_qup7, + msm_mux_qup8, + msm_mux_qup9, + msm_mux_qup10, + msm_mux_qup11, + msm_mux_tb_trig, + msm_mux_tgu_ch0, + msm_mux_tgu_ch1, + msm_mux_tgu_ch2, + msm_mux_tgu_ch3, + msm_mux_tmess_prng0, + msm_mux_tmess_prng1, + msm_mux_tmess_prng2, + msm_mux_tmess_prng3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_usb0_phy, + msm_mux_vsense_trigger, + msm_mux__, +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", + "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", + "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", + "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116", + "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122", + "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128", + "gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134", + "gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140", + "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146", + "gpio147", "gpio148", "gpio149", "gpio150", "gpio151", "gpio152", + "gpio153", "gpio154", "gpio155", +}; + +static const char * const aoss_cti_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char * const atest_char_groups[] = { + "gpio45", +}; + +static const char * const atest_char0_groups[] = { + "gpio90", +}; + +static const char * const atest_char1_groups[] = { + "gpio89", +}; + +static const char * const atest_char2_groups[] = { + "gpio88", +}; + +static const char * const atest_char3_groups[] = { + "gpio87", +}; + +static const char * const atest_usb0_groups[] = { + "gpio26", +}; + +static const char * const atest_usb00_groups[] = { + "gpio110", +}; + +static const char * const atest_usb01_groups[] = { + "gpio109", +}; + +static const char * const atest_usb02_groups[] = { + "gpio27", +}; + +static const char * const atest_usb03_groups[] = { + "gpio60", +}; + +static const char * const audio_ref_groups[] = { + "gpio103", +}; + +static const char * const cam_mclk_groups[] = { + "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", + "gpio76", +}; + +static const char * const cci_async_groups[] = { + "gpio80", "gpio81", "gpio82", +}; + +static const char * const cci_i2c_groups[] = { + "gpio67", "gpio68", "gpio78", "gpio79", "gpio80", "gpio81", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", + "gpio91", "gpio92", +}; + +static const char * const cci_timer0_groups[] = { + "gpio77", +}; + +static const char * const cci_timer1_groups[] = { + "gpio78", +}; + +static const char * const cci_timer2_groups[] = { + "gpio79", +}; + +static const char * const cci_timer3_groups[] = { + "gpio80", +}; + +static const char * const cci_timer4_groups[] = { + "gpio81", +}; + +static const char * const cri_trng_groups[] = { + "gpio60", +}; + +static const char * const cri_trng0_groups[] = { + "gpio70", +}; + +static const char * const cri_trng1_groups[] = { + "gpio71", +}; + +static const char * const dbg_out_groups[] = { + "gpio59", +}; + +static const char * const ddr_bist_groups[] = { + "gpio4", "gpio5", "gpio100", "gpio103", +}; + +static const char * const ddr_pxi0_groups[] = { + "gpio56", "gpio57", +}; + +static const char * const ddr_pxi1_groups[] = { + "gpio41", "gpio45", +}; + +static const char * const ddr_pxi2_groups[] = { + "gpio48", "gpio55", +}; + +static const char * const ddr_pxi3_groups[] = { + "gpio46", "gpio47", +}; + +static const char * const dp0_hot_groups[] = { + "gpio35", "gpio103", +}; + +static const char * const ext_mclk0_groups[] = { + "gpio104", +}; + +static const char * const ext_mclk1_groups[] = { + "gpio103", +}; + +static const char * const gcc_gp1_groups[] = { + "gpio129", "gpio132", +}; + +static const char * const gcc_gp2_groups[] = { + "gpio130", "gpio135", +}; + +static const char * const gcc_gp3_groups[] = { + "gpio131", "gpio136", +}; + +static const char * const host2wlan_sol_groups[] = { + "gpio111", +}; + +static const char * const i2s0_data0_groups[] = { + "gpio106", +}; + +static const char * const i2s0_data1_groups[] = { + "gpio107", +}; + +static const char * const i2s0_sck_groups[] = { + "gpio105", +}; + +static const char * const i2s0_ws_groups[] = { + "gpio108", +}; + +static const char * const ibi_i3c_groups[] = { + "gpio0", "gpio1", "gpio91", "gpio92", +}; + +static const char * const jitter_bist_groups[] = { + "gpio0", +}; + +static const char * const mdp_vsync_groups[] = { + "gpio12", "gpio13", "gpio41", "gpio49", "gpio50", +}; + +static const char * const mdp_vsync0_groups[] = { + "gpio49", +}; + +static const char * const mdp_vsync1_groups[] = { + "gpio49", +}; + +static const char * const mdp_vsync2_groups[] = { + "gpio50", +}; + +static const char * const mdp_vsync3_groups[] = { + "gpio50", +}; + +static const char * const pcie0_clkreqn_groups[] = { + "gpio56", +}; + +static const char * const pcie1_clkreqn_groups[] = { + "gpio59", +}; + +static const char * const phase_flag0_groups[] = { + "gpio133", +}; + +static const char * const phase_flag1_groups[] = { + "gpio128", +}; + +static const char * const phase_flag10_groups[] = { + "gpio94", +}; + +static const char * const phase_flag11_groups[] = { + "gpio93", +}; + +static const char * const phase_flag12_groups[] = { + "gpio134", +}; + +static const char * const phase_flag13_groups[] = { + "gpio139", +}; + +static const char * const phase_flag14_groups[] = { + "gpio138", +}; + +static const char * const phase_flag15_groups[] = { + "gpio137", +}; + +static const char * const phase_flag16_groups[] = { + "gpio62", +}; + +static const char * const phase_flag17_groups[] = { + "gpio61", +}; + +static const char * const phase_flag18_groups[] = { + "gpio41", +}; + +static const char * const phase_flag19_groups[] = { + "gpio23", +}; + +static const char * const phase_flag2_groups[] = { + "gpio127", +}; + +static const char * const phase_flag20_groups[] = { + "gpio22", +}; + +static const char * const phase_flag21_groups[] = { + "gpio21", +}; + +static const char * const phase_flag22_groups[] = { + "gpio19", +}; + +static const char * const phase_flag23_groups[] = { + "gpio18", +}; + +static const char * const phase_flag24_groups[] = { + "gpio17", +}; + +static const char * const phase_flag25_groups[] = { + "gpio16", +}; + +static const char * const phase_flag26_groups[] = { + "gpio13", +}; + +static const char * const phase_flag27_groups[] = { + "gpio12", +}; + +static const char * const phase_flag28_groups[] = { + "gpio3", +}; + +static const char * const phase_flag29_groups[] = { + "gpio2", +}; + +static const char * const phase_flag3_groups[] = { + "gpio126", +}; + +static const char * const phase_flag30_groups[] = { + "gpio149", +}; + +static const char * const phase_flag31_groups[] = { + "gpio148", +}; + +static const char * const phase_flag4_groups[] = { + "gpio151", +}; + +static const char * const phase_flag5_groups[] = { + "gpio150", +}; + +static const char * const phase_flag6_groups[] = { + "gpio98", +}; + +static const char * const phase_flag7_groups[] = { + "gpio97", +}; + +static const char * const phase_flag8_groups[] = { + "gpio96", +}; + +static const char * const phase_flag9_groups[] = { + "gpio95", +}; + +static const char * const pll_bist_groups[] = { + "gpio8", +}; + +static const char * const pll_clk_groups[] = { + "gpio54", +}; + +static const char * const prng_rosc0_groups[] = { + "gpio72", +}; + +static const char * const prng_rosc1_groups[] = { + "gpio73", +}; + +static const char * const prng_rosc2_groups[] = { + "gpio74", +}; + +static const char * const prng_rosc3_groups[] = { + "gpio75", +}; + +static const char * const qdss_cti_groups[] = { + "gpio28", "gpio29", "gpio36", "gpio37", "gpio38", "gpio38", "gpio47", + "gpio48", "gpio53", "gpio53", "gpio105", "gpio106", "gpio154", + "gpio155", +}; + +static const char * const qdss_gpio_groups[] = { + "gpio89", "gpio90", "gpio109", "gpio110", +}; + +static const char * const qdss_gpio0_groups[] = { + "gpio24", "gpio65", +}; + +static const char * const qdss_gpio1_groups[] = { + "gpio25", "gpio66", +}; + +static const char * const qdss_gpio10_groups[] = { + "gpio63", "gpio83", +}; + +static const char * const qdss_gpio11_groups[] = { + "gpio64", "gpio84", +}; + +static const char * const qdss_gpio12_groups[] = { + "gpio39", "gpio85", +}; + +static const char * const qdss_gpio13_groups[] = { + "gpio10", "gpio86", +}; + +static const char * const qdss_gpio14_groups[] = { + "gpio45", "gpio87", +}; + +static const char * const qdss_gpio15_groups[] = { + "gpio11", "gpio88", +}; + +static const char * const qdss_gpio2_groups[] = { + "gpio26", "gpio67", +}; + +static const char * const qdss_gpio3_groups[] = { + "gpio27", "gpio68", +}; + +static const char * const qdss_gpio4_groups[] = { + "gpio30", "gpio77", +}; + +static const char * const qdss_gpio5_groups[] = { + "gpio31", "gpio78", +}; + +static const char * const qdss_gpio6_groups[] = { + "gpio4", "gpio79", +}; + +static const char * const qdss_gpio7_groups[] = { + "gpio5", "gpio80", +}; + +static const char * const qdss_gpio8_groups[] = { + "gpio6", "gpio81", +}; + +static const char * const qdss_gpio9_groups[] = { + "gpio7", "gpio82", +}; + +static const char * const qspi0_groups[] = { + "gpio32", +}; + +static const char * const qspi1_groups[] = { + "gpio33", +}; + +static const char * const qspi2_groups[] = { + "gpio36", +}; + +static const char * const qspi3_groups[] = { + "gpio37", +}; + +static const char * const qspi_clk_groups[] = { + "gpio34", +}; + +static const char * const qspi_cs0_groups[] = { + "gpio35", +}; + +static const char * const qspi_cs1_groups[] = { + "gpio38", +}; + +static const char * const qup0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio93", +}; + +static const char * const qup1_groups[] = { + "gpio2", "gpio3", "gpio61", "gpio62", +}; + +static const char * const qup2_groups[] = { + "gpio12", "gpio13", "gpio22", "gpio23", +}; + +static const char * const qup3_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", "gpio41", +}; + +static const char * const qup4_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", "gpio94", +}; + +static const char * const qup5_groups[] = { + "gpio95", "gpio96", "gpio97", "gpio98", +}; + +static const char * const qup6_groups[] = { + "gpio63", "gpio64", "gpio91", "gpio92", +}; + +static const char * const qup7_groups[] = { + "gpio24", "gpio25", "gpio26", "gpio27", +}; + +static const char * const qup8_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11", +}; + +static const char * const qup9_groups[] = { + "gpio34", "gpio35", "gpio109", "gpio110", +}; + +static const char * const qup10_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; + +static const char * const qup11_groups[] = { + "gpio14", "gpio15", "gpio28", "gpio30", +}; + +static const char * const tb_trig_groups[] = { + "gpio69", +}; + +static const char * const tgu_ch0_groups[] = { + "gpio20", +}; + +static const char * const tgu_ch1_groups[] = { + "gpio21", +}; + +static const char * const tgu_ch2_groups[] = { + "gpio22", +}; + +static const char * const tgu_ch3_groups[] = { + "gpio23", +}; + +static const char * const tmess_prng0_groups[] = { + "gpio80", +}; + +static const char * const tmess_prng1_groups[] = { + "gpio79", +}; + +static const char * const tmess_prng2_groups[] = { + "gpio83", +}; + +static const char * const tmess_prng3_groups[] = { + "gpio81", +}; + +static const char * const tsense_pwm1_groups[] = { + "gpio86", +}; + +static const char * const tsense_pwm2_groups[] = { + "gpio86", +}; + +static const char * const usb0_phy_groups[] = { + "gpio100", +}; + +static const char * const vsense_trigger_groups[] = { + "gpio36", +}; + +static const struct pinfunction sar2130p_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(qup0), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(qup1), + MSM_PIN_FUNCTION(phase_flag29), + MSM_PIN_FUNCTION(phase_flag28), + MSM_PIN_FUNCTION(qup10), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(qdss_gpio6), + MSM_PIN_FUNCTION(qdss_gpio7), + MSM_PIN_FUNCTION(qdss_gpio8), + MSM_PIN_FUNCTION(qdss_gpio9), + MSM_PIN_FUNCTION(qup8), + MSM_PIN_FUNCTION(pll_bist), + MSM_PIN_FUNCTION(qdss_gpio13), + MSM_PIN_FUNCTION(qdss_gpio15), + MSM_PIN_FUNCTION(qup2), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(phase_flag27), + MSM_PIN_FUNCTION(phase_flag26), + MSM_PIN_FUNCTION(qup11), + MSM_PIN_FUNCTION(qup3), + MSM_PIN_FUNCTION(phase_flag25), + MSM_PIN_FUNCTION(phase_flag24), + MSM_PIN_FUNCTION(phase_flag23), + MSM_PIN_FUNCTION(phase_flag22), + MSM_PIN_FUNCTION(qup4), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(tgu_ch0), + MSM_PIN_FUNCTION(phase_flag21), + MSM_PIN_FUNCTION(tgu_ch1), + MSM_PIN_FUNCTION(phase_flag20), + MSM_PIN_FUNCTION(tgu_ch2), + MSM_PIN_FUNCTION(phase_flag19), + MSM_PIN_FUNCTION(tgu_ch3), + MSM_PIN_FUNCTION(qup7), + MSM_PIN_FUNCTION(qdss_gpio0), + MSM_PIN_FUNCTION(qdss_gpio1), + MSM_PIN_FUNCTION(qdss_gpio2), + MSM_PIN_FUNCTION(atest_usb0), + MSM_PIN_FUNCTION(qdss_gpio3), + MSM_PIN_FUNCTION(atest_usb02), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio4), + MSM_PIN_FUNCTION(qdss_gpio5), + MSM_PIN_FUNCTION(qspi0), + MSM_PIN_FUNCTION(qspi1), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qup9), + MSM_PIN_FUNCTION(qspi_cs0), + MSM_PIN_FUNCTION(dp0_hot), + MSM_PIN_FUNCTION(qspi2), + MSM_PIN_FUNCTION(vsense_trigger), + MSM_PIN_FUNCTION(qspi3), + MSM_PIN_FUNCTION(qspi_cs1), + MSM_PIN_FUNCTION(qdss_gpio12), + MSM_PIN_FUNCTION(phase_flag18), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(qdss_gpio14), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(mdp_vsync0), + MSM_PIN_FUNCTION(mdp_vsync1), + MSM_PIN_FUNCTION(mdp_vsync2), + MSM_PIN_FUNCTION(mdp_vsync3), + MSM_PIN_FUNCTION(pll_clk), + MSM_PIN_FUNCTION(pcie0_clkreqn), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(pcie1_clkreqn), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(atest_usb03), + MSM_PIN_FUNCTION(phase_flag17), + MSM_PIN_FUNCTION(phase_flag16), + MSM_PIN_FUNCTION(qup6), + MSM_PIN_FUNCTION(qdss_gpio10), + MSM_PIN_FUNCTION(qdss_gpio11), + MSM_PIN_FUNCTION(cci_i2c), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(tb_trig), + MSM_PIN_FUNCTION(cri_trng0), + MSM_PIN_FUNCTION(cri_trng1), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(cci_timer0), + MSM_PIN_FUNCTION(cci_timer1), + MSM_PIN_FUNCTION(cci_timer2), + MSM_PIN_FUNCTION(tmess_prng1), + MSM_PIN_FUNCTION(cci_timer3), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(tmess_prng0), + MSM_PIN_FUNCTION(cci_timer4), + MSM_PIN_FUNCTION(tmess_prng3), + MSM_PIN_FUNCTION(tmess_prng2), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(atest_char3), + MSM_PIN_FUNCTION(atest_char2), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(atest_char1), + MSM_PIN_FUNCTION(atest_char0), + MSM_PIN_FUNCTION(phase_flag11), + MSM_PIN_FUNCTION(phase_flag10), + MSM_PIN_FUNCTION(qup5), + MSM_PIN_FUNCTION(phase_flag9), + MSM_PIN_FUNCTION(phase_flag8), + MSM_PIN_FUNCTION(phase_flag7), + MSM_PIN_FUNCTION(phase_flag6), + MSM_PIN_FUNCTION(usb0_phy), + MSM_PIN_FUNCTION(ext_mclk1), + MSM_PIN_FUNCTION(audio_ref), + MSM_PIN_FUNCTION(ext_mclk0), + MSM_PIN_FUNCTION(i2s0_sck), + MSM_PIN_FUNCTION(i2s0_data0), + MSM_PIN_FUNCTION(i2s0_data1), + MSM_PIN_FUNCTION(i2s0_ws), + MSM_PIN_FUNCTION(atest_usb01), + MSM_PIN_FUNCTION(atest_usb00), + MSM_PIN_FUNCTION(host2wlan_sol), + MSM_PIN_FUNCTION(phase_flag3), + MSM_PIN_FUNCTION(phase_flag2), + MSM_PIN_FUNCTION(phase_flag1), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(phase_flag0), + MSM_PIN_FUNCTION(phase_flag12), + MSM_PIN_FUNCTION(phase_flag15), + MSM_PIN_FUNCTION(phase_flag14), + MSM_PIN_FUNCTION(phase_flag13), + MSM_PIN_FUNCTION(phase_flag31), + MSM_PIN_FUNCTION(phase_flag30), + MSM_PIN_FUNCTION(phase_flag5), + MSM_PIN_FUNCTION(phase_flag4), +}; + +/* Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup sar2130p_groups[] = { + [0] = PINGROUP(0, qup0, ibi_i3c, jitter_bist, _, _, _, _, _, _), + [1] = PINGROUP(1, qup0, ibi_i3c, _, _, _, _, _, _, _), + [2] = PINGROUP(2, qup0, qup1, phase_flag29, _, _, _, _, _, _), + [3] = PINGROUP(3, qup0, qup1, phase_flag28, _, _, _, _, _, _), + [4] = PINGROUP(4, qup10, ddr_bist, qdss_gpio6, _, _, _, _, _, _), + [5] = PINGROUP(5, qup10, ddr_bist, qdss_gpio7, _, _, _, _, _, _), + [6] = PINGROUP(6, qup10, qdss_gpio8, _, _, _, _, _, _, _), + [7] = PINGROUP(7, qup10, qdss_gpio9, _, _, _, _, _, _, _), + [8] = PINGROUP(8, qup8, pll_bist, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qup8, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, qup8, qdss_gpio13, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup8, qdss_gpio15, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup2, mdp_vsync, phase_flag27, _, _, _, _, _, _), + [13] = PINGROUP(13, qup2, mdp_vsync, phase_flag26, _, _, _, _, _, _), + [14] = PINGROUP(14, qup11, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, qup11, _, _, _, _, _, _, _, _), + [16] = PINGROUP(16, qup3, phase_flag25, _, _, _, _, _, _, _), + [17] = PINGROUP(17, qup3, phase_flag24, _, _, _, _, _, _, _), + [18] = PINGROUP(18, qup3, phase_flag23, _, _, _, _, _, _, _), + [19] = PINGROUP(19, qup3, phase_flag22, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup4, aoss_cti, tgu_ch0, _, _, _, _, _, _), + [21] = PINGROUP(21, qup4, aoss_cti, phase_flag21, tgu_ch1, _, _, _, _, _), + [22] = PINGROUP(22, qup4, qup2, aoss_cti, phase_flag20, tgu_ch2, _, _, _, _), + [23] = PINGROUP(23, qup4, qup2, aoss_cti, phase_flag19, tgu_ch3, _, _, _, _), + [24] = PINGROUP(24, qup7, qdss_gpio0, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup7, qdss_gpio1, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup7, qdss_gpio2, atest_usb0, _, _, _, _, _, _), + [27] = PINGROUP(27, qup7, qdss_gpio3, atest_usb02, _, _, _, _, _, _), + [28] = PINGROUP(28, qup11, qdss_cti, _, _, _, _, _, _, _), + [29] = PINGROUP(29, qdss_cti, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, qup11, qdss_gpio4, _, _, _, _, _, _, _), + [31] = PINGROUP(31, qdss_gpio5, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, qspi0, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qspi1, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, qspi_clk, qup9, _, _, _, _, _, _, _), + [35] = PINGROUP(35, qspi_cs0, qup9, dp0_hot, _, _, _, _, _, _), + [36] = PINGROUP(36, qspi2, qdss_cti, vsense_trigger, _, _, _, _, _, _), + [37] = PINGROUP(37, qspi3, qdss_cti, _, _, _, _, _, _, _), + [38] = PINGROUP(38, qspi_cs1, qdss_cti, qdss_cti, _, _, _, _, _, _), + [39] = PINGROUP(39, qdss_gpio12, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, _, _, _, _, _, _, _, _, _), + [41] = PINGROUP(41, qup3, mdp_vsync, phase_flag18, _, ddr_pxi1, _, _, _, _), + [42] = PINGROUP(42, _, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, _, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, _, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, qdss_gpio14, ddr_pxi1, atest_char, _, _, _, _, _, _), + [46] = PINGROUP(46, ddr_pxi3, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, qdss_cti, ddr_pxi3, _, _, _, _, _, _, _), + [48] = PINGROUP(48, qdss_cti, ddr_pxi2, _, _, _, _, _, _, _), + [49] = PINGROUP(49, mdp_vsync, mdp_vsync0, mdp_vsync1, _, _, _, _, _, _), + [50] = PINGROUP(50, mdp_vsync, mdp_vsync2, mdp_vsync3, _, _, _, _, _, _), + [51] = PINGROUP(51, _, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, _, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, qdss_cti, qdss_cti, _, _, _, _, _, _, _), + [54] = PINGROUP(54, pll_clk, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, _, ddr_pxi2, _, _, _, _, _, _, _), + [56] = PINGROUP(56, pcie0_clkreqn, _, ddr_pxi0, _, _, _, _, _, _), + [57] = PINGROUP(57, ddr_pxi0, _, _, _, _, _, _, _, _), + [58] = PINGROUP(58, _, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, pcie1_clkreqn, dbg_out, _, _, _, _, _, _, _), + [60] = PINGROUP(60, cri_trng, atest_usb03, _, _, _, _, _, _, _), + [61] = PINGROUP(61, qup1, phase_flag17, _, _, _, _, _, _, _), + [62] = PINGROUP(62, qup1, phase_flag16, _, _, _, _, _, _, _), + [63] = PINGROUP(63, qup6, qdss_gpio10, _, _, _, _, _, _, _), + [64] = PINGROUP(64, qup6, qdss_gpio11, _, _, _, _, _, _, _), + [65] = PINGROUP(65, qdss_gpio0, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, qdss_gpio1, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, cci_i2c, qdss_gpio2, _, _, _, _, _, _, _), + [68] = PINGROUP(68, cci_i2c, qdss_gpio3, _, _, _, _, _, _, _), + [69] = PINGROUP(69, cam_mclk, tb_trig, _, _, _, _, _, _, _), + [70] = PINGROUP(70, cam_mclk, cri_trng0, _, _, _, _, _, _, _), + [71] = PINGROUP(71, cam_mclk, cri_trng1, _, _, _, _, _, _, _), + [72] = PINGROUP(72, cam_mclk, prng_rosc0, _, _, _, _, _, _, _), + [73] = PINGROUP(73, cam_mclk, prng_rosc1, _, _, _, _, _, _, _), + [74] = PINGROUP(74, cam_mclk, prng_rosc2, _, _, _, _, _, _, _), + [75] = PINGROUP(75, cam_mclk, prng_rosc3, _, _, _, _, _, _, _), + [76] = PINGROUP(76, cam_mclk, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, cci_timer0, qdss_gpio4, _, _, _, _, _, _, _), + [78] = PINGROUP(78, cci_timer1, cci_i2c, qdss_gpio5, _, _, _, _, _, _), + [79] = PINGROUP(79, cci_timer2, cci_i2c, tmess_prng1, qdss_gpio6, _, _, _, _, _), + [80] = PINGROUP(80, cci_timer3, cci_i2c, cci_async, tmess_prng0, qdss_gpio7, _, _, _, _), + [81] = PINGROUP(81, cci_timer4, cci_i2c, cci_async, tmess_prng3, qdss_gpio8, _, _, _, _), + [82] = PINGROUP(82, cci_async, qdss_gpio9, _, _, _, _, _, _, _), + [83] = PINGROUP(83, cci_i2c, tmess_prng2, qdss_gpio10, _, _, _, _, _, _), + [84] = PINGROUP(84, cci_i2c, qdss_gpio11, _, _, _, _, _, _, _), + [85] = PINGROUP(85, cci_i2c, qdss_gpio12, _, _, _, _, _, _, _), + [86] = PINGROUP(86, cci_i2c, qdss_gpio13, tsense_pwm1, tsense_pwm2, _, _, _, _, _), + [87] = PINGROUP(87, cci_i2c, qdss_gpio14, atest_char3, _, _, _, _, _, _), + [88] = PINGROUP(88, cci_i2c, qdss_gpio15, atest_char2, _, _, _, _, _, _), + [89] = PINGROUP(89, cci_i2c, qdss_gpio, atest_char1, _, _, _, _, _, _), + [90] = PINGROUP(90, cci_i2c, qdss_gpio, atest_char0, _, _, _, _, _, _), + [91] = PINGROUP(91, cci_i2c, qup6, ibi_i3c, _, _, _, _, _, _), + [92] = PINGROUP(92, cci_i2c, qup6, ibi_i3c, _, _, _, _, _, _), + [93] = PINGROUP(93, qup0, phase_flag11, _, _, _, _, _, _, _), + [94] = PINGROUP(94, qup4, phase_flag10, _, _, _, _, _, _, _), + [95] = PINGROUP(95, qup5, phase_flag9, _, _, _, _, _, _, _), + [96] = PINGROUP(96, qup5, phase_flag8, _, _, _, _, _, _, _), + [97] = PINGROUP(97, qup5, phase_flag7, _, _, _, _, _, _, _), + [98] = PINGROUP(98, qup5, phase_flag6, _, _, _, _, _, _, _), + [99] = PINGROUP(99, _, _, _, _, _, _, _, _, _), + [100] = PINGROUP(100, usb0_phy, ddr_bist, _, _, _, _, _, _, _), + [101] = PINGROUP(101, _, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, _, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, ext_mclk1, audio_ref, dp0_hot, ddr_bist, _, _, _, _, _), + [104] = PINGROUP(104, ext_mclk0, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, i2s0_sck, _, qdss_cti, _, _, _, _, _, _), + [106] = PINGROUP(106, i2s0_data0, _, qdss_cti, _, _, _, _, _, _), + [107] = PINGROUP(107, i2s0_data1, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, i2s0_ws, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, qup9, qdss_gpio, atest_usb01, _, _, _, _, _, _), + [110] = PINGROUP(110, qup9, qdss_gpio, atest_usb00, _, _, _, _, _, _), + [111] = PINGROUP(111, host2wlan_sol, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, _, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, _, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, _, _, _, _, _, _, _, _, _), + [116] = PINGROUP(116, _, _, _, _, _, _, _, _, _), + [117] = PINGROUP(117, _, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, _, _, _, _, _, _, _, _, _), + [119] = PINGROUP(119, _, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, _, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, _, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, _, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, _, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, phase_flag3, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, phase_flag2, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, phase_flag1, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, gcc_gp1, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, gcc_gp2, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, gcc_gp3, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, gcc_gp1, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, phase_flag0, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, phase_flag12, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, gcc_gp2, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, gcc_gp3, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, phase_flag15, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, phase_flag14, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, phase_flag13, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, _, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, phase_flag31, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, phase_flag30, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, phase_flag5, _, _, _, _, _, _, _, _), + [151] = PINGROUP(151, phase_flag4, _, _, _, _, _, _, _, _), + [152] = PINGROUP(152, _, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, _, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, qdss_cti, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, qdss_cti, _, _, _, _, _, _, _, _), + [156] = SDC_QDSD_PINGROUP(sdc1_rclk, 0xa1000, 0, 0), + [157] = SDC_QDSD_PINGROUP(sdc1_clk, 0xa0000, 13, 6), + [158] = SDC_QDSD_PINGROUP(sdc1_cmd, 0xa0000, 11, 3), + [159] = SDC_QDSD_PINGROUP(sdc1_data, 0xa0000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map sar2130p_pdc_map[] = { + { 0, 50 }, { 3, 68 }, { 6, 88 }, { 7, 55 }, { 10, 66 }, { 11, 96 }, + { 12, 48 }, { 13, 49 }, { 15, 62 }, { 18, 57 }, { 19, 59 }, { 23, 51 }, + { 27, 74 }, { 28, 67 }, { 29, 84 }, { 30, 58 }, { 31, 94 }, { 32, 60 }, + { 33, 61 }, { 35, 69 }, { 37, 70 }, { 38, 64 }, { 39, 65 }, { 40, 63 }, + { 41, 92 }, { 42, 82 }, { 44, 83 }, { 45, 43 }, { 46, 72 }, { 47, 45 }, + { 48, 44 }, { 49, 71 }, { 50, 87 }, { 53, 77 }, { 54, 78 }, + { 55, 106 }, { 56, 79 }, { 57, 80 }, { 58, 107 }, { 59, 81 }, + { 60, 89 }, { 61, 54 }, { 62, 73 }, { 63, 93 }, { 64, 86 }, { 65, 75 }, + { 67, 42 }, { 68, 76 }, { 76, 116 }, { 77, 12 }, { 83, 13 }, + { 91, 90 }, { 94, 95 }, { 95, 91 }, { 98, 47 }, { 100, 85 }, + { 101, 52 }, { 102, 53 }, { 103, 97 }, { 104, 98 }, { 105, 99 }, + { 106, 100 }, { 107, 101 }, { 108, 102 }, { 109, 103 }, { 111, 104 }, + { 113, 46 }, { 114, 56 }, { 115, 108 }, { 116, 109 }, { 117, 110 }, + { 118, 111 }, { 121, 112 }, { 122, 113 }, { 124, 114 }, { 127, 115 }, + { 132, 118 }, { 134, 119 }, { 135, 120 }, { 136, 121 }, { 139, 122 }, + { 140, 123 }, { 141, 124 }, { 143, 128 }, { 144, 129 }, { 145, 130 }, + { 146, 131 }, { 148, 132 }, { 150, 133 }, { 151, 134 }, { 153, 135 }, + { 155, 137 }, +}; + +static const struct msm_pinctrl_soc_data sar2130p_tlmm = { + .pins = sar2130p_pins, + .npins = ARRAY_SIZE(sar2130p_pins), + .functions = sar2130p_functions, + .nfunctions = ARRAY_SIZE(sar2130p_functions), + .groups = sar2130p_groups, + .ngroups = ARRAY_SIZE(sar2130p_groups), + .ngpios = 156, + .wakeirq_map = sar2130p_pdc_map, + .nwakeirq_map = ARRAY_SIZE(sar2130p_pdc_map), +}; + +static int sar2130p_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &sar2130p_tlmm); +} + +static const struct of_device_id sar2130p_tlmm_of_match[] = { + { .compatible = "qcom,sar2130p-tlmm", .data = &sar2130p_tlmm}, + { }, +}; +MODULE_DEVICE_TABLE(of, sar2130p_tlmm_of_match); + +static struct platform_driver sar2130p_tlmm_driver = { + .driver = { + .name = "sar2130p-tlmm", + .of_match_table = sar2130p_tlmm_of_match, + }, + .probe = sar2130p_tlmm_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init sar2130p_tlmm_init(void) +{ + return platform_driver_register(&sar2130p_tlmm_driver); +} +arch_initcall(sar2130p_tlmm_init); + +static void __exit sar2130p_tlmm_exit(void) +{ + platform_driver_unregister(&sar2130p_tlmm_driver); +} +module_exit(sar2130p_tlmm_exit); + +MODULE_DESCRIPTION("QTI SAR2130P TLMM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-sc7180.c b/drivers/pinctrl/qcom/pinctrl-sc7180.c index c27aaa599b917..6eb0c73791c0b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7180.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7180.c @@ -1159,7 +1159,7 @@ static struct platform_driver sc7180_pinctrl_driver = { .of_match_table = sc7180_pinctrl_of_match, }, .probe = sc7180_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sc7180_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c index 6bb39812e1d8b..1161f0a91a002 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c @@ -142,7 +142,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c index c2db663e396eb..0c10eeb60b55e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c @@ -1505,7 +1505,7 @@ static struct platform_driver sc7280_pinctrl_driver = { .of_match_table = sc7280_pinctrl_of_match, }, .probe = sc7280_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sc7280_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sc8180x.c b/drivers/pinctrl/qcom/pinctrl-sc8180x.c index cfa7c8be9770c..d6a79ad41a40a 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8180x.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8180x.c @@ -1720,7 +1720,7 @@ static struct platform_driver sc8180x_pinctrl_driver = { .acpi_match_table = sc8180x_pinctrl_acpi_match, }, .probe = sc8180x_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sc8180x_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c index c0369baf33989..0e839b6aaaf4b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c @@ -179,7 +179,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c index 4b1c49697698d..96f4fb5a5d297 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c @@ -1926,7 +1926,7 @@ static struct platform_driver sc8280xp_pinctrl_driver = { .of_match_table = sc8280xp_pinctrl_of_match, }, .probe = sc8280xp_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sc8280xp_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sdm660.c b/drivers/pinctrl/qcom/pinctrl-sdm660.c index b93837c00954a..907e4ffca5e7d 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm660.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm660.c @@ -1442,7 +1442,7 @@ static struct platform_driver sdm660_pinctrl_driver = { .of_match_table = sdm660_pinctrl_of_match, }, .probe = sdm660_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sdm660_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670.c b/drivers/pinctrl/qcom/pinctrl-sdm670.c index 894c042cb5249..c76183ba95e17 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm670.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c @@ -1337,7 +1337,7 @@ static struct platform_driver sdm670_pinctrl_driver = { .of_match_table = sdm670_pinctrl_of_match, }, .probe = sdm670_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sdm670_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index 3f3265e0018d6..cc05c415ed155 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -1351,7 +1351,7 @@ static struct platform_driver sdm845_pinctrl_driver = { .acpi_match_table = ACPI_PTR(sdm845_pinctrl_acpi_match), }, .probe = sdm845_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sdm845_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sdx55.c b/drivers/pinctrl/qcom/pinctrl-sdx55.c index c88b8bfcacb6a..8826db9d21d04 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx55.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx55.c @@ -990,7 +990,7 @@ static struct platform_driver sdx55_pinctrl_driver = { .of_match_table = sdx55_pinctrl_of_match, }, .probe = sdx55_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sdx55_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sdx65.c b/drivers/pinctrl/qcom/pinctrl-sdx65.c index bd44ec0fcab43..f6f319c997fc7 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx65.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx65.c @@ -939,7 +939,7 @@ static struct platform_driver sdx65_pinctrl_driver = { .of_match_table = sdx65_pinctrl_of_match, }, .probe = sdx65_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sdx65_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sdx75.c b/drivers/pinctrl/qcom/pinctrl-sdx75.c index 396f6fc779a2e..3cfe8c7f04df8 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx75.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx75.c @@ -1124,7 +1124,7 @@ static struct platform_driver sdx75_pinctrl_driver = { .of_match_table = sdx75_pinctrl_of_match, }, .probe = sdx75_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sdx75_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c index 2d2c636a3e207..c0e178be9cfc3 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c @@ -227,7 +227,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm4450.c b/drivers/pinctrl/qcom/pinctrl-sm4450.c index 27317b86d8359..622f20e6f6f85 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm4450.c +++ b/drivers/pinctrl/qcom/pinctrl-sm4450.c @@ -994,7 +994,7 @@ static struct platform_driver sm4450_tlmm_driver = { .of_match_table = sm4450_tlmm_of_match, }, .probe = sm4450_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; MODULE_DEVICE_TABLE(of, sm4450_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c index 316d6fc69131b..b7d9186861a2f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c @@ -147,7 +147,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c index 7ce04144b6ed3..4e91c75ad9524 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c @@ -907,7 +907,7 @@ static struct platform_driver sm6115_tlmm_driver = { .of_match_table = sm6115_tlmm_of_match, }, .probe = sm6115_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm6115_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm6125.c b/drivers/pinctrl/qcom/pinctrl-sm6125.c index 65de34c307594..c188842047aae 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6125.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6125.c @@ -1266,7 +1266,7 @@ static struct platform_driver sm6125_tlmm_driver = { .of_match_table = sm6125_tlmm_of_match, }, .probe = sm6125_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm6125_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm6350.c b/drivers/pinctrl/qcom/pinctrl-sm6350.c index 4aeb1ba43ee3d..f3828c07b1345 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6350.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6350.c @@ -1373,7 +1373,7 @@ static struct platform_driver sm6350_tlmm_driver = { .of_match_table = sm6350_tlmm_of_match, }, .probe = sm6350_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm6350_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm6375.c b/drivers/pinctrl/qcom/pinctrl-sm6375.c index d86630d7125c2..c82c8516932ea 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6375.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6375.c @@ -1516,7 +1516,7 @@ static struct platform_driver sm6375_tlmm_driver = { .of_match_table = sm6375_tlmm_of_match, }, .probe = sm6375_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm6375_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm7150.c b/drivers/pinctrl/qcom/pinctrl-sm7150.c index 095a1ca758490..3c7fd8af6635b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm7150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm7150.c @@ -1255,7 +1255,7 @@ static struct platform_driver sm7150_tlmm_driver = { .of_match_table = sm7150_tlmm_of_match, }, .probe = sm7150_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm7150_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index f8f5bee74f1dc..01aea9c70b7a7 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -1542,7 +1542,7 @@ static struct platform_driver sm8150_pinctrl_driver = { .of_match_table = sm8150_pinctrl_of_match, }, .probe = sm8150_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm8150_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c index 9791d9ba5087c..64494a86490e2 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c @@ -140,7 +140,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index 54fda77bf2968..e9961a49ff981 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -1351,7 +1351,7 @@ static struct platform_driver sm8250_pinctrl_driver = { .of_match_table = sm8250_pinctrl_of_match, }, .probe = sm8250_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm8250_pinctrl_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c index 5b9a2cb216bd8..7b146b4acfdf4 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c @@ -142,7 +142,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8350.c b/drivers/pinctrl/qcom/pinctrl-sm8350.c index ac7f2820f2cbf..9c69458bd9109 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8350.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8350.c @@ -1642,7 +1642,7 @@ static struct platform_driver sm8350_tlmm_driver = { .of_match_table = sm8350_tlmm_of_match, }, .probe = sm8350_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm8350_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c index a028cbb499472..439f6541622e9 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c @@ -208,7 +208,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450.c b/drivers/pinctrl/qcom/pinctrl-sm8450.c index 6172867116952..d11bb1ee9e3d8 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8450.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8450.c @@ -1677,7 +1677,7 @@ static struct platform_driver sm8450_tlmm_driver = { .of_match_table = sm8450_tlmm_of_match, }, .probe = sm8450_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm8450_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c index 852192b044e17..73065919c8c26 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c @@ -216,7 +216,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550.c b/drivers/pinctrl/qcom/pinctrl-sm8550.c index 9184e0183755d..3c847d9cb5d93 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550.c @@ -1762,7 +1762,7 @@ static struct platform_driver sm8550_tlmm_driver = { .of_match_table = sm8550_tlmm_of_match, }, .probe = sm8550_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm8550_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c index 04400c832327a..f9fcedf5a65d7 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c @@ -223,7 +223,7 @@ static struct platform_driver lpi_pinctrl_driver = { .of_match_table = lpi_pinctrl_of_match, }, .probe = lpi_pinctrl_probe, - .remove_new = lpi_pinctrl_remove, + .remove = lpi_pinctrl_remove, }; module_platform_driver(lpi_pinctrl_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c index adaddd728662f..104708252d12a 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c @@ -1742,7 +1742,7 @@ static struct platform_driver sm8650_tlmm_driver = { .of_match_table = sm8650_tlmm_of_match, }, .probe = sm8650_tlmm_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init sm8650_tlmm_init(void) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8750.c b/drivers/pinctrl/qcom/pinctrl-sm8750.c new file mode 100644 index 0000000000000..1af11cd95fb0e --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sm8750.c @@ -0,0 +1,1729 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10, \ + msm_mux_##f11 /* egpio mode */ \ + }, \ + .nfuncs = 12, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, ctl, io) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = io, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc sm8750_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), + PINCTRL_PIN(178, "GPIO_178"), + PINCTRL_PIN(179, "GPIO_179"), + PINCTRL_PIN(180, "GPIO_180"), + PINCTRL_PIN(181, "GPIO_181"), + PINCTRL_PIN(182, "GPIO_182"), + PINCTRL_PIN(183, "GPIO_183"), + PINCTRL_PIN(184, "GPIO_184"), + PINCTRL_PIN(185, "GPIO_185"), + PINCTRL_PIN(186, "GPIO_186"), + PINCTRL_PIN(187, "GPIO_187"), + PINCTRL_PIN(188, "GPIO_188"), + PINCTRL_PIN(189, "GPIO_189"), + PINCTRL_PIN(190, "GPIO_190"), + PINCTRL_PIN(191, "GPIO_191"), + PINCTRL_PIN(192, "GPIO_192"), + PINCTRL_PIN(193, "GPIO_193"), + PINCTRL_PIN(194, "GPIO_194"), + PINCTRL_PIN(195, "GPIO_195"), + PINCTRL_PIN(196, "GPIO_196"), + PINCTRL_PIN(197, "GPIO_197"), + PINCTRL_PIN(198, "GPIO_198"), + PINCTRL_PIN(199, "GPIO_199"), + PINCTRL_PIN(200, "GPIO_200"), + PINCTRL_PIN(201, "GPIO_201"), + PINCTRL_PIN(202, "GPIO_202"), + PINCTRL_PIN(203, "GPIO_203"), + PINCTRL_PIN(204, "GPIO_204"), + PINCTRL_PIN(205, "GPIO_205"), + PINCTRL_PIN(206, "GPIO_206"), + PINCTRL_PIN(207, "GPIO_207"), + PINCTRL_PIN(208, "GPIO_208"), + PINCTRL_PIN(209, "GPIO_209"), + PINCTRL_PIN(210, "GPIO_210"), + PINCTRL_PIN(211, "GPIO_211"), + PINCTRL_PIN(212, "GPIO_212"), + PINCTRL_PIN(213, "GPIO_213"), + PINCTRL_PIN(214, "GPIO_214"), + PINCTRL_PIN(215, "UFS_RESET"), + PINCTRL_PIN(216, "SDC2_CLK"), + PINCTRL_PIN(217, "SDC2_CMD"), + PINCTRL_PIN(218, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); +DECLARE_MSM_GPIO_PINS(166); +DECLARE_MSM_GPIO_PINS(167); +DECLARE_MSM_GPIO_PINS(168); +DECLARE_MSM_GPIO_PINS(169); +DECLARE_MSM_GPIO_PINS(170); +DECLARE_MSM_GPIO_PINS(171); +DECLARE_MSM_GPIO_PINS(172); +DECLARE_MSM_GPIO_PINS(173); +DECLARE_MSM_GPIO_PINS(174); +DECLARE_MSM_GPIO_PINS(175); +DECLARE_MSM_GPIO_PINS(176); +DECLARE_MSM_GPIO_PINS(177); +DECLARE_MSM_GPIO_PINS(178); +DECLARE_MSM_GPIO_PINS(179); +DECLARE_MSM_GPIO_PINS(180); +DECLARE_MSM_GPIO_PINS(181); +DECLARE_MSM_GPIO_PINS(182); +DECLARE_MSM_GPIO_PINS(183); +DECLARE_MSM_GPIO_PINS(184); +DECLARE_MSM_GPIO_PINS(185); +DECLARE_MSM_GPIO_PINS(186); +DECLARE_MSM_GPIO_PINS(187); +DECLARE_MSM_GPIO_PINS(188); +DECLARE_MSM_GPIO_PINS(189); +DECLARE_MSM_GPIO_PINS(190); +DECLARE_MSM_GPIO_PINS(191); +DECLARE_MSM_GPIO_PINS(192); +DECLARE_MSM_GPIO_PINS(193); +DECLARE_MSM_GPIO_PINS(194); +DECLARE_MSM_GPIO_PINS(195); +DECLARE_MSM_GPIO_PINS(196); +DECLARE_MSM_GPIO_PINS(197); +DECLARE_MSM_GPIO_PINS(198); +DECLARE_MSM_GPIO_PINS(199); +DECLARE_MSM_GPIO_PINS(200); +DECLARE_MSM_GPIO_PINS(201); +DECLARE_MSM_GPIO_PINS(202); +DECLARE_MSM_GPIO_PINS(203); +DECLARE_MSM_GPIO_PINS(204); +DECLARE_MSM_GPIO_PINS(205); +DECLARE_MSM_GPIO_PINS(206); +DECLARE_MSM_GPIO_PINS(207); +DECLARE_MSM_GPIO_PINS(208); +DECLARE_MSM_GPIO_PINS(209); +DECLARE_MSM_GPIO_PINS(210); +DECLARE_MSM_GPIO_PINS(211); +DECLARE_MSM_GPIO_PINS(212); +DECLARE_MSM_GPIO_PINS(213); +DECLARE_MSM_GPIO_PINS(214); + +static const unsigned int ufs_reset_pins[] = { 215 }; +static const unsigned int sdc2_clk_pins[] = { 216 }; +static const unsigned int sdc2_cmd_pins[] = { 217 }; +static const unsigned int sdc2_data_pins[] = { 218 }; + +enum sm8750_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_usb, + msm_mux_audio_ext_mclk0, + msm_mux_audio_ext_mclk1, + msm_mux_audio_ref_clk, + msm_mux_cam_aon_mclk2, + msm_mux_cam_aon_mclk4, + msm_mux_cam_mclk, + msm_mux_cci_async_in, + msm_mux_cci_i2c_scl, + msm_mux_cci_i2c_sda, + msm_mux_cci_timer, + msm_mux_cmu_rng, + msm_mux_coex_uart1_rx, + msm_mux_coex_uart1_tx, + msm_mux_coex_uart2_rx, + msm_mux_coex_uart2_tx, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist_complete, + msm_mux_ddr_bist_fail, + msm_mux_ddr_bist_start, + msm_mux_ddr_bist_stop, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_dp_hot, + msm_mux_egpio, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gnss_adc0, + msm_mux_gnss_adc1, + msm_mux_i2chub0_se0, + msm_mux_i2chub0_se1, + msm_mux_i2chub0_se2, + msm_mux_i2chub0_se3, + msm_mux_i2chub0_se4, + msm_mux_i2chub0_se5, + msm_mux_i2chub0_se6, + msm_mux_i2chub0_se7, + msm_mux_i2chub0_se8, + msm_mux_i2chub0_se9, + msm_mux_i2s0_data0, + msm_mux_i2s0_data1, + msm_mux_i2s0_sck, + msm_mux_i2s0_ws, + msm_mux_i2s1_data0, + msm_mux_i2s1_data1, + msm_mux_i2s1_sck, + msm_mux_i2s1_ws, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp_esync0_out, + msm_mux_mdp_esync1_out, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync0_out, + msm_mux_mdp_vsync1_out, + msm_mux_mdp_vsync2_out, + msm_mux_mdp_vsync3_out, + msm_mux_mdp_vsync5_out, + msm_mux_mdp_vsync_e, + msm_mux_nav_gpio0, + msm_mux_nav_gpio1, + msm_mux_nav_gpio2, + msm_mux_nav_gpio3, + msm_mux_pcie0_clk_req_n, + msm_mux_phase_flag, + msm_mux_pll_bist_sync, + msm_mux_pll_clk_aux, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qlink_big_enable, + msm_mux_qlink_big_request, + msm_mux_qlink_little_enable, + msm_mux_qlink_little_request, + msm_mux_qlink_wmss, + msm_mux_qspi0, + msm_mux_qspi1, + msm_mux_qspi2, + msm_mux_qspi3, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup1_se7, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup2_se7, + msm_mux_sd_write_protect, + msm_mux_sdc40, + msm_mux_sdc41, + msm_mux_sdc42, + msm_mux_sdc43, + msm_mux_sdc4_clk, + msm_mux_sdc4_cmd, + msm_mux_tb_trig_sdc2, + msm_mux_tb_trig_sdc4, + msm_mux_tmess_prng0, + msm_mux_tmess_prng1, + msm_mux_tmess_prng2, + msm_mux_tmess_prng3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_tsense_pwm4, + msm_mux_uim0_clk, + msm_mux_uim0_data, + msm_mux_uim0_present, + msm_mux_uim0_reset, + msm_mux_uim1_clk, + msm_mux_uim1_data, + msm_mux_uim1_present, + msm_mux_uim1_reset, + msm_mux_usb1_hs, + msm_mux_usb_phy, + msm_mux_vfr_0, + msm_mux_vfr_1, + msm_mux_vsense_trigger_mirnat, + msm_mux_wcn_sw, + msm_mux_wcn_sw_ctrl, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", + "gpio54", "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", + "gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", + "gpio114", "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", "gpio122", "gpio123", "gpio124", "gpio125", + "gpio126", "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", + "gpio132", "gpio133", "gpio134", "gpio135", "gpio136", "gpio137", + "gpio138", "gpio139", "gpio140", "gpio141", "gpio142", "gpio143", + "gpio144", "gpio145", "gpio146", "gpio147", "gpio148", "gpio149", + "gpio150", "gpio151", "gpio152", "gpio153", "gpio154", "gpio155", + "gpio156", "gpio157", "gpio158", "gpio159", "gpio160", "gpio161", + "gpio162", "gpio163", "gpio164", "gpio165", "gpio166", "gpio167", + "gpio168", "gpio169", "gpio170", "gpio171", "gpio172", "gpio173", + "gpio174", "gpio175", "gpio176", "gpio177", "gpio178", "gpio179", + "gpio180", "gpio181", "gpio182", "gpio183", "gpio184", "gpio185", + "gpio186", "gpio187", "gpio188", "gpio189", "gpio190", "gpio191", + "gpio192", "gpio193", "gpio194", "gpio195", "gpio196", "gpio197", + "gpio198", "gpio199", "gpio200", "gpio201", "gpio202", "gpio203", + "gpio204", "gpio205", "gpio206", "gpio207", "gpio208", "gpio209", + "gpio210", "gpio211", "gpio212", "gpio213", "gpio214", +}; + +static const char *const egpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio105", "gpio106", "gpio107", "gpio108", + "gpio165", "gpio166", "gpio167", "gpio168", "gpio169", "gpio170", + "gpio171", "gpio172", "gpio173", "gpio174", "gpio175", "gpio176", + "gpio177", "gpio178", "gpio179", "gpio180", "gpio181", "gpio182", + "gpio183", "gpio184", "gpio185", "gpio186", "gpio187", "gpio188", + "gpio189", "gpio190", "gpio191", "gpio192", "gpio193", "gpio194", + "gpio195", "gpio196", "gpio197", "gpio198", "gpio199", "gpio200", + "gpio201", "gpio202", "gpio203", "gpio204", "gpio205", "gpio206", + "gpio207", "gpio208", "gpio209", "gpio210", "gpio211", "gpio212", + "gpio213", "gpio214", +}; + +static const char *const aoss_cti_groups[] = { + "gpio50", "gpio51", "gpio60", "gpio61", +}; + +static const char *const atest_char_groups[] = { + "gpio130", "gpio131", "gpio132", "gpio133", "gpio137", +}; + +static const char *const atest_usb_groups[] = { + "gpio70", "gpio71", "gpio72", "gpio73", "gpio76", +}; + +static const char *const audio_ext_mclk0_groups[] = { + "gpio125", +}; + +static const char *const audio_ext_mclk1_groups[] = { + "gpio124", +}; + +static const char *const audio_ref_clk_groups[] = { + "gpio124", +}; + +static const char *const cam_aon_mclk2_groups[] = { + "gpio91", +}; + +static const char *const cam_aon_mclk4_groups[] = { + "gpio93", +}; + +static const char *const cam_mclk_groups[] = { + "gpio89", "gpio90", "gpio92", "gpio94", "gpio95", "gpio96", +}; + +static const char *const cci_async_in_groups[] = { + "gpio10", "gpio11", "gpio15", +}; + +static const char *const cci_i2c_scl_groups[] = { + "gpio114", "gpio116", "gpio118", "gpio120", "gpio153", "gpio164", +}; + +static const char *const cci_i2c_sda_groups[] = { + "gpio111", "gpio112", "gpio113", "gpio115", "gpio117", "gpio119", +}; + +static const char *const cci_timer_groups[] = { + "gpio109", "gpio110", "gpio111", "gpio163", "gpio164", +}; + +static const char *const cmu_rng_groups[] = { + "gpio40", "gpio41", "gpio41", "gpio43", "gpio148", "gpio149", + "gpio150", "gpio151", +}; + +static const char *const coex_uart1_rx_groups[] = { + "gpio148", +}; + +static const char *const coex_uart1_tx_groups[] = { + "gpio149", +}; + +static const char *const coex_uart2_rx_groups[] = { + "gpio150", +}; + +static const char *const coex_uart2_tx_groups[] = { + "gpio151", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio78", +}; + +static const char *const ddr_bist_complete_groups[] = { + "gpio44", +}; + +static const char *const ddr_bist_fail_groups[] = { + "gpio40", +}; + +static const char *const ddr_bist_start_groups[] = { + "gpio41", +}; + +static const char *const ddr_bist_stop_groups[] = { + "gpio45", +}; + +static const char *const ddr_pxi0_groups[] = { + "gpio54", "gpio55", +}; + +static const char *const ddr_pxi1_groups[] = { + "gpio44", "gpio45", +}; + +static const char *const ddr_pxi2_groups[] = { + "gpio43", "gpio52", +}; + +static const char *const ddr_pxi3_groups[] = { + "gpio46", "gpio53", +}; + +static const char *const dp_hot_groups[] = { + "gpio47", +}; + +static const char *const gcc_gp1_groups[] = { + "gpio86", "gpio134", +}; + +static const char *const gcc_gp2_groups[] = { + "gpio87", "gpio135", +}; + +static const char *const gcc_gp3_groups[] = { + "gpio88", "gpio136", +}; + +static const char *const gnss_adc0_groups[] = { + "gpio78", "gpio79", +}; + +static const char *const gnss_adc1_groups[] = { + "gpio77", "gpio99", +}; + +static const char *const i2chub0_se0_groups[] = { + "gpio64", "gpio65", +}; + +static const char *const i2chub0_se1_groups[] = { + "gpio66", "gpio67", +}; + +static const char *const i2chub0_se2_groups[] = { + "gpio68", "gpio69", +}; + +static const char *const i2chub0_se3_groups[] = { + "gpio70", "gpio71", +}; + +static const char *const i2chub0_se4_groups[] = { + "gpio72", "gpio73", +}; + +static const char *const i2chub0_se5_groups[] = { + "gpio74", "gpio75", +}; + +static const char *const i2chub0_se6_groups[] = { + "gpio76", "gpio77", +}; + +static const char *const i2chub0_se7_groups[] = { + "gpio82", "gpio83", +}; + +static const char *const i2chub0_se8_groups[] = { + "gpio206", "gpio207", +}; + +static const char *const i2chub0_se9_groups[] = { + "gpio80", "gpio81", +}; + +static const char *const i2s0_data0_groups[] = { + "gpio127", +}; + +static const char *const i2s0_data1_groups[] = { + "gpio128", +}; + +static const char *const i2s0_sck_groups[] = { + "gpio126", +}; + +static const char *const i2s0_ws_groups[] = { + "gpio129", +}; + +static const char *const i2s1_data0_groups[] = { + "gpio122", +}; + +static const char *const i2s1_data1_groups[] = { + "gpio124", +}; + +static const char *const i2s1_sck_groups[] = { + "gpio121", +}; + +static const char *const i2s1_ws_groups[] = { + "gpio123", +}; + +static const char *const ibi_i3c_groups[] = { + "gpio0", "gpio1", "gpio4", "gpio5", "gpio8", "gpio9", + "gpio12", "gpio13", "gpio28", "gpio29", "gpio32", "gpio33", + "gpio36", "gpio37", "gpio48", "gpio49", +}; + +static const char *const jitter_bist_groups[] = { + "gpio73", +}; + +static const char *const mdp_esync0_out_groups[] = { + "gpio88", +}; + +static const char *const mdp_esync1_out_groups[] = { + "gpio100", +}; + +static const char *const mdp_vsync_groups[] = { + "gpio86", "gpio87", "gpio97", "gpio98", +}; + +static const char *const mdp_vsync0_out_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync1_out_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync2_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync3_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync5_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync_e_groups[] = { + "gpio88", +}; + +static const char *const nav_gpio0_groups[] = { + "gpio154", +}; + +static const char *const nav_gpio1_groups[] = { + "gpio155", +}; + +static const char *const nav_gpio2_groups[] = { + "gpio152", +}; + +static const char *const nav_gpio3_groups[] = { + "gpio154", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio103", +}; + +static const char *const phase_flag_groups[] = { + "gpio10", "gpio11", "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", + "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", + "gpio26", "gpio27", "gpio28", "gpio29", "gpio31", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio82", "gpio83", "gpio85", + "gpio101", "gpio102", "gpio103", "gpio104", +}; + +static const char *const pll_bist_sync_groups[] = { + "gpio104", +}; + +static const char *const pll_clk_aux_groups[] = { + "gpio95", +}; + +static const char *const prng_rosc0_groups[] = { + "gpio85", +}; + +static const char *const prng_rosc1_groups[] = { + "gpio64", +}; + +static const char *const prng_rosc2_groups[] = { + "gpio65", +}; + +static const char *const prng_rosc3_groups[] = { + "gpio66", +}; + +static const char *const qdss_cti_groups[] = { + "gpio27", "gpio31", "gpio72", "gpio73", "gpio82", "gpio83", "gpio159", + "gpio162", +}; + +static const char *const qlink_big_enable_groups[] = { + "gpio160", +}; + +static const char *const qlink_big_request_groups[] = { + "gpio159", +}; + +static const char *const qlink_little_enable_groups[] = { + "gpio157", +}; + +static const char *const qlink_little_request_groups[] = { + "gpio156", +}; + +static const char *const qlink_wmss_groups[] = { + "gpio158", +}; + +static const char *const qspi0_groups[] = { + "gpio52", +}; + +static const char *const qspi1_groups[] = { + "gpio53", +}; + +static const char *const qspi2_groups[] = { + "gpio55", +}; + +static const char *const qspi3_groups[] = { + "gpio56", +}; + +static const char *const qspi_clk_groups[] = { + "gpio54", +}; + +static const char *const qspi_cs_groups[] = { + "gpio57", "gpio58", +}; + +static const char *const qup1_se0_groups[] = { + "gpio32", "gpio33", "gpio34", "gpio35", +}; + +static const char *const qup1_se1_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const qup1_se2_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", "gpio134", "gpio135", "gpio136", +}; + +static const char *const qup1_se3_groups[] = { + "gpio44", "gpio45", "gpio46", "gpio47", +}; + +static const char *const qup1_se4_groups[] = { + "gpio48", "gpio49", "gpio50", "gpio51", +}; + +static const char *const qup1_se5_groups[] = { + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char *const qup1_se6_groups[] = { + "gpio56", "gpio57", "gpio58", "gpio59", +}; + +static const char *const qup1_se7_groups[] = { + "gpio60", "gpio61", "gpio62", "gpio63", +}; + +static const char *const qup2_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qup2_se1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; + +static const char *const qup2_se2_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio15", +}; + +static const char *const qup2_se3_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio15", +}; + +static const char *const qup2_se4_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", +}; + +static const char *const qup2_se5_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char *const qup2_se6_groups[] = { + "gpio24", "gpio25", "gpio26", "gpio27", +}; + +static const char *const qup2_se7_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", +}; + +static const char *const sd_write_protect_groups[] = { + "gpio85", +}; + +static const char *const sdc40_groups[] = { + "gpio36", "gpio49", +}; + +static const char *const sdc41_groups[] = { + "gpio37", "gpio51", +}; + +static const char *const sdc42_groups[] = { + "gpio38", "gpio60", +}; + +static const char *const sdc43_groups[] = { + "gpio39", "gpio61", +}; + +static const char *const sdc4_clk_groups[] = { + "gpio50", "gpio150", +}; + +static const char *const sdc4_cmd_groups[] = { + "gpio48", "gpio151", +}; + +static const char *const tb_trig_sdc2_groups[] = { + "gpio89", +}; + +static const char *const tb_trig_sdc4_groups[] = { + "gpio147", +}; + +static const char *const tmess_prng0_groups[] = { + "gpio85", +}; + +static const char *const tmess_prng1_groups[] = { + "gpio64", +}; + +static const char *const tmess_prng2_groups[] = { + "gpio65", +}; + +static const char *const tmess_prng3_groups[] = { + "gpio66", +}; + +static const char *const tsense_pwm1_groups[] = { + "gpio57", +}; + +static const char *const tsense_pwm2_groups[] = { + "gpio57", +}; + +static const char *const tsense_pwm3_groups[] = { + "gpio57", +}; + +static const char *const tsense_pwm4_groups[] = { + "gpio57", +}; + +static const char *const uim0_clk_groups[] = { + "gpio131", +}; + +static const char *const uim0_data_groups[] = { + "gpio130", +}; + +static const char *const uim0_present_groups[] = { + "gpio133", +}; + +static const char *const uim0_reset_groups[] = { + "gpio132", +}; + +static const char *const uim1_clk_groups[] = { + "gpio37", "gpio55", "gpio71", "gpio135", +}; + +static const char *const uim1_data_groups[] = { + "gpio134", "gpio36", "gpio54", "gpio70", +}; + +static const char *const uim1_present_groups[] = { + "gpio137", +}; + +static const char *const uim1_reset_groups[] = { + "gpio39", "gpio56", "gpio72", "gpio136", +}; + +static const char *const usb1_hs_groups[] = { + "gpio79", +}; + +static const char *const usb_phy_groups[] = { + "gpio59", "gpio61", +}; + +static const char *const vfr_0_groups[] = { + "gpio150", +}; + +static const char *const vfr_1_groups[] = { + "gpio155", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio59", +}; + +static const char *const wcn_sw_groups[] = { + "gpio19", +}; + +static const char *const wcn_sw_ctrl_groups[] = { + "gpio18", +}; + +static const struct pinfunction sm8750_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(audio_ext_mclk0), + MSM_PIN_FUNCTION(audio_ext_mclk1), + MSM_PIN_FUNCTION(audio_ref_clk), + MSM_PIN_FUNCTION(cam_aon_mclk2), + MSM_PIN_FUNCTION(cam_aon_mclk4), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async_in), + MSM_PIN_FUNCTION(cci_i2c_scl), + MSM_PIN_FUNCTION(cci_i2c_sda), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(cmu_rng), + MSM_PIN_FUNCTION(coex_uart1_rx), + MSM_PIN_FUNCTION(coex_uart1_tx), + MSM_PIN_FUNCTION(coex_uart2_rx), + MSM_PIN_FUNCTION(coex_uart2_tx), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist_complete), + MSM_PIN_FUNCTION(ddr_bist_fail), + MSM_PIN_FUNCTION(ddr_bist_start), + MSM_PIN_FUNCTION(ddr_bist_stop), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(dp_hot), + MSM_PIN_FUNCTION(egpio), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gnss_adc0), + MSM_PIN_FUNCTION(gnss_adc1), + MSM_PIN_FUNCTION(i2chub0_se0), + MSM_PIN_FUNCTION(i2chub0_se1), + MSM_PIN_FUNCTION(i2chub0_se2), + MSM_PIN_FUNCTION(i2chub0_se3), + MSM_PIN_FUNCTION(i2chub0_se4), + MSM_PIN_FUNCTION(i2chub0_se5), + MSM_PIN_FUNCTION(i2chub0_se6), + MSM_PIN_FUNCTION(i2chub0_se7), + MSM_PIN_FUNCTION(i2chub0_se8), + MSM_PIN_FUNCTION(i2chub0_se9), + MSM_PIN_FUNCTION(i2s0_data0), + MSM_PIN_FUNCTION(i2s0_data1), + MSM_PIN_FUNCTION(i2s0_sck), + MSM_PIN_FUNCTION(i2s0_ws), + MSM_PIN_FUNCTION(i2s1_data0), + MSM_PIN_FUNCTION(i2s1_data1), + MSM_PIN_FUNCTION(i2s1_sck), + MSM_PIN_FUNCTION(i2s1_ws), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp_esync0_out), + MSM_PIN_FUNCTION(mdp_esync1_out), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mdp_vsync0_out), + MSM_PIN_FUNCTION(mdp_vsync1_out), + MSM_PIN_FUNCTION(mdp_vsync2_out), + MSM_PIN_FUNCTION(mdp_vsync3_out), + MSM_PIN_FUNCTION(mdp_vsync5_out), + MSM_PIN_FUNCTION(mdp_vsync_e), + MSM_PIN_FUNCTION(nav_gpio0), + MSM_PIN_FUNCTION(nav_gpio1), + MSM_PIN_FUNCTION(nav_gpio2), + MSM_PIN_FUNCTION(nav_gpio3), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_clk_aux), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qlink_big_enable), + MSM_PIN_FUNCTION(qlink_big_request), + MSM_PIN_FUNCTION(qlink_little_enable), + MSM_PIN_FUNCTION(qlink_little_request), + MSM_PIN_FUNCTION(qlink_wmss), + MSM_PIN_FUNCTION(qspi0), + MSM_PIN_FUNCTION(qspi1), + MSM_PIN_FUNCTION(qspi2), + MSM_PIN_FUNCTION(qspi3), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup2_se7), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(sdc40), + MSM_PIN_FUNCTION(sdc41), + MSM_PIN_FUNCTION(sdc42), + MSM_PIN_FUNCTION(sdc43), + MSM_PIN_FUNCTION(sdc4_clk), + MSM_PIN_FUNCTION(sdc4_cmd), + MSM_PIN_FUNCTION(tb_trig_sdc2), + MSM_PIN_FUNCTION(tb_trig_sdc4), + MSM_PIN_FUNCTION(tmess_prng0), + MSM_PIN_FUNCTION(tmess_prng1), + MSM_PIN_FUNCTION(tmess_prng2), + MSM_PIN_FUNCTION(tmess_prng3), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(tsense_pwm4), + MSM_PIN_FUNCTION(uim0_clk), + MSM_PIN_FUNCTION(uim0_data), + MSM_PIN_FUNCTION(uim0_present), + MSM_PIN_FUNCTION(uim0_reset), + MSM_PIN_FUNCTION(uim1_clk), + MSM_PIN_FUNCTION(uim1_data), + MSM_PIN_FUNCTION(uim1_present), + MSM_PIN_FUNCTION(uim1_reset), + MSM_PIN_FUNCTION(usb1_hs), + MSM_PIN_FUNCTION(usb_phy), + MSM_PIN_FUNCTION(vfr_0), + MSM_PIN_FUNCTION(vfr_1), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wcn_sw), + MSM_PIN_FUNCTION(wcn_sw_ctrl), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup sm8750_groups[] = { + [0] = PINGROUP(0, qup2_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [1] = PINGROUP(1, qup2_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [2] = PINGROUP(2, qup2_se0, _, _, _, _, _, _, _, _, _, egpio), + [3] = PINGROUP(3, qup2_se0, _, _, _, _, _, _, _, _, _, egpio), + [4] = PINGROUP(4, qup2_se1, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [5] = PINGROUP(5, qup2_se1, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [6] = PINGROUP(6, qup2_se1, _, _, _, _, _, _, _, _, _, egpio), + [7] = PINGROUP(7, qup2_se1, _, _, _, _, _, _, _, _, _, egpio), + [8] = PINGROUP(8, qup2_se2, ibi_i3c, _, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qup2_se2, ibi_i3c, _, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, qup2_se2, cci_async_in, phase_flag, _, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup2_se2, cci_async_in, phase_flag, _, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup2_se3, ibi_i3c, qup2_se2, _, _, _, _, _, _, _, _), + [13] = PINGROUP(13, qup2_se3, ibi_i3c, qup2_se2, _, _, _, _, _, _, _, _), + [14] = PINGROUP(14, qup2_se3, phase_flag, _, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, qup2_se3, cci_async_in, qup2_se2, phase_flag, _, _, _, _, _, _, _), + [16] = PINGROUP(16, qup2_se4, phase_flag, _, _, _, _, _, _, _, _, _), + [17] = PINGROUP(17, qup2_se4, phase_flag, _, _, _, _, _, _, _, _, _), + [18] = PINGROUP(18, wcn_sw_ctrl, qup2_se4, phase_flag, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, wcn_sw, qup2_se4, phase_flag, _, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup2_se5, phase_flag, _, _, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup2_se5, phase_flag, _, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, qup2_se5, phase_flag, _, _, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup2_se5, qup2_se5, phase_flag, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup2_se6, phase_flag, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup2_se6, phase_flag, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup2_se6, phase_flag, _, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup2_se6, qdss_cti, phase_flag, _, _, _, _, _, _, _, _), + [28] = PINGROUP(28, qup2_se7, ibi_i3c, phase_flag, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, qup2_se7, ibi_i3c, phase_flag, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, qup2_se7, _, _, _, _, _, _, _, _, _, _), + [31] = PINGROUP(31, qup2_se7, qdss_cti, phase_flag, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, qup1_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [33] = PINGROUP(33, qup1_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [34] = PINGROUP(34, qup1_se0, _, _, _, _, _, _, _, _, _, egpio), + [35] = PINGROUP(35, qup1_se0, _, _, _, _, _, _, _, _, _, egpio), + [36] = PINGROUP(36, qup1_se1, uim1_data, ibi_i3c, sdc40, _, _, _, _, _, _, egpio), + [37] = PINGROUP(37, qup1_se1, uim1_clk, ibi_i3c, sdc41, _, _, _, _, _, _, egpio), + [38] = PINGROUP(38, qup1_se1, sdc42, _, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, qup1_se1, uim1_reset, sdc43, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, qup1_se2, cmu_rng, ddr_bist_fail, _, _, _, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se2, cmu_rng, ddr_bist_start, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se2, cmu_rng, _, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, qup1_se2, cmu_rng, _, ddr_pxi2, _, _, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se3, ddr_bist_complete, ddr_pxi1, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se3, ddr_bist_stop, ddr_pxi1, _, _, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, ddr_pxi3, _, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, dp_hot, _, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, qup1_se4, ibi_i3c, sdc4_cmd, _, _, _, _, _, _, _, _), + [49] = PINGROUP(49, qup1_se4, ibi_i3c, sdc40, _, _, _, _, _, _, _, _), + [50] = PINGROUP(50, qup1_se4, aoss_cti, sdc4_clk, _, _, _, _, _, _, _, _), + [51] = PINGROUP(51, qup1_se4, aoss_cti, sdc41, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, qup1_se5, qspi0, ddr_pxi2, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, qup1_se5, qspi1, _, ddr_pxi3, _, _, _, _, _, _, _), + [54] = PINGROUP(54, qup1_se5, qspi_clk, uim1_data, ddr_pxi0, _, _, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se5, qspi2, uim1_clk, ddr_pxi0, _, _, _, _, _, _, _), + [56] = PINGROUP(56, qup1_se6, qspi3, uim1_reset, _, _, _, _, _, _, _, _), + [57] = PINGROUP(57, qup1_se6, qspi_cs, tsense_pwm1, tsense_pwm2, tsense_pwm3, tsense_pwm4, + _, _, _, _, _), + [58] = PINGROUP(58, qup1_se6, qspi_cs, _, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, qup1_se6, usb_phy, vsense_trigger_mirnat, _, _, _, _, _, _, _, _), + [60] = PINGROUP(60, qup1_se7, aoss_cti, sdc42, _, _, _, _, _, _, _, _), + [61] = PINGROUP(61, qup1_se7, usb_phy, aoss_cti, sdc43, _, _, _, _, _, _, _), + [62] = PINGROUP(62, qup1_se7, _, _, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, qup1_se7, _, _, _, _, _, _, _, _, _, _), + [64] = PINGROUP(64, i2chub0_se0, prng_rosc1, tmess_prng1, phase_flag, _, _, _, _, _, _, _), + [65] = PINGROUP(65, i2chub0_se0, prng_rosc2, tmess_prng2, phase_flag, _, _, _, _, _, _, _), + [66] = PINGROUP(66, i2chub0_se1, prng_rosc3, tmess_prng3, phase_flag, _, _, _, _, _, _, _), + [67] = PINGROUP(67, i2chub0_se1, phase_flag, _, _, _, _, _, _, _, _, _), + [68] = PINGROUP(68, i2chub0_se2, phase_flag, _, _, _, _, _, _, _, _, _), + [69] = PINGROUP(69, i2chub0_se2, phase_flag, _, _, _, _, _, _, _, _, _), + [70] = PINGROUP(70, i2chub0_se3, uim1_data, _, atest_usb, _, _, _, _, _, _, _), + [71] = PINGROUP(71, i2chub0_se3, uim1_clk, _, atest_usb, _, _, _, _, _, _, _), + [72] = PINGROUP(72, i2chub0_se4, uim1_reset, qdss_cti, _, atest_usb, _, _, _, _, _, _), + [73] = PINGROUP(73, i2chub0_se4, qdss_cti, jitter_bist, atest_usb, _, _, _, _, _, _, _), + [74] = PINGROUP(74, i2chub0_se5, _, _, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, i2chub0_se5, _, _, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, i2chub0_se6, atest_usb, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, i2chub0_se6, gnss_adc1, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, dbg_out_clk, gnss_adc0, _, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, usb1_hs, gnss_adc0, _, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, i2chub0_se9, _, _, _, _, _, _, _, _, _, _), + [81] = PINGROUP(81, i2chub0_se9, _, _, _, _, _, _, _, _, _, _), + [82] = PINGROUP(82, i2chub0_se7, qdss_cti, phase_flag, _, _, _, _, _, _, _, _), + [83] = PINGROUP(83, i2chub0_se7, qdss_cti, phase_flag, _, _, _, _, _, _, _, _), + [84] = PINGROUP(84, _, _, _, _, _, _, _, _, _, _, _), + [85] = PINGROUP(85, sd_write_protect, prng_rosc0, tmess_prng0, phase_flag, _, _, _, _, _, + _, _), + [86] = PINGROUP(86, mdp_vsync, mdp_vsync0_out, mdp_vsync1_out, gcc_gp1, _, _, _, _, _, _, + _), + [87] = PINGROUP(87, mdp_vsync, mdp_vsync2_out, mdp_vsync3_out, mdp_vsync5_out, gcc_gp2, _, + _, _, _, _, _), + [88] = PINGROUP(88, mdp_vsync_e, mdp_esync0_out, gcc_gp3, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, cam_mclk, tb_trig_sdc2, _, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [91] = PINGROUP(91, cam_aon_mclk2, _, _, _, _, _, _, _, _, _, _), + [92] = PINGROUP(92, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, cam_aon_mclk4, _, _, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, cam_mclk, pll_clk_aux, _, _, _, _, _, _, _, _, _), + [96] = PINGROUP(96, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [97] = PINGROUP(97, mdp_vsync, _, _, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, mdp_vsync, _, _, _, _, _, _, _, _, _, _), + [99] = PINGROUP(99, gnss_adc1, _, _, _, _, _, _, _, _, _, _), + [100] = PINGROUP(100, mdp_esync1_out, _, _, _, _, _, _, _, _, _, _), + [101] = PINGROUP(101, phase_flag, _, _, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, phase_flag, _, _, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, pcie0_clk_req_n, phase_flag, _, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, pll_bist_sync, phase_flag, _, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, _, _, _, _, _, _, _, _, _, _, egpio), + [106] = PINGROUP(106, _, _, _, _, _, _, _, _, _, _, egpio), + [107] = PINGROUP(107, _, _, _, _, _, _, _, _, _, _, egpio), + [108] = PINGROUP(108, _, _, _, _, _, _, _, _, _, _, egpio), + [109] = PINGROUP(109, cci_timer, _, _, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, cci_timer, _, _, _, _, _, _, _, _, _, _), + [111] = PINGROUP(111, cci_timer, cci_i2c_sda, _, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, cci_i2c_sda, _, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, cci_i2c_sda, _, _, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, cci_i2c_scl, _, _, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, cci_i2c_sda, _, _, _, _, _, _, _, _, _, _), + [116] = PINGROUP(116, cci_i2c_scl, _, _, _, _, _, _, _, _, _, _), + [117] = PINGROUP(117, cci_i2c_sda, _, _, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, cci_i2c_scl, _, _, _, _, _, _, _, _, _, _), + [119] = PINGROUP(119, cci_i2c_sda, _, _, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, cci_i2c_scl, _, _, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, i2s1_sck, _, _, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, i2s1_data0, _, _, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, i2s1_ws, _, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, i2s1_data1, audio_ext_mclk1, audio_ref_clk, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, audio_ext_mclk0, _, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, i2s0_sck, _, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, i2s0_data0, _, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, i2s0_data1, _, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, i2s0_ws, _, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, uim0_data, atest_char, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, uim0_clk, atest_char, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, uim0_reset, atest_char, _, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, uim0_present, atest_char, _, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, uim1_data, qup1_se2, gcc_gp1, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, uim1_clk, qup1_se2, gcc_gp2, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, uim1_reset, qup1_se2, gcc_gp3, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, uim1_present, atest_char, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, _, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, _, _, _, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, _, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, _, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, _, tb_trig_sdc4, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, coex_uart1_rx, cmu_rng, _, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, coex_uart1_tx, cmu_rng, _, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, _, vfr_0, coex_uart2_rx, cmu_rng, sdc4_clk, _, _, _, _, _, _), + [151] = PINGROUP(151, _, coex_uart2_tx, cmu_rng, sdc4_cmd, _, _, _, _, _, _, _), + [152] = PINGROUP(152, nav_gpio2, _, _, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, cci_i2c_scl, _, _, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, nav_gpio0, nav_gpio3, _, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, nav_gpio1, vfr_1, _, _, _, _, _, _, _, _, _), + [156] = PINGROUP(156, qlink_little_request, _, _, _, _, _, _, _, _, _, _), + [157] = PINGROUP(157, qlink_little_enable, _, _, _, _, _, _, _, _, _, _), + [158] = PINGROUP(158, qlink_wmss, _, _, _, _, _, _, _, _, _, _), + [159] = PINGROUP(159, qlink_big_request, qdss_cti, _, _, _, _, _, _, _, _, _), + [160] = PINGROUP(160, qlink_big_enable, _, _, _, _, _, _, _, _, _, _), + [161] = PINGROUP(161, _, _, _, _, _, _, _, _, _, _, _), + [162] = PINGROUP(162, qdss_cti, _, _, _, _, _, _, _, _, _, _), + [163] = PINGROUP(163, cci_timer, _, _, _, _, _, _, _, _, _, _), + [164] = PINGROUP(164, cci_timer, cci_i2c_scl, _, _, _, _, _, _, _, _, _), + [165] = PINGROUP(165, _, _, _, _, _, _, _, _, _, _, egpio), + [166] = PINGROUP(166, _, _, _, _, _, _, _, _, _, _, egpio), + [167] = PINGROUP(167, _, _, _, _, _, _, _, _, _, _, egpio), + [168] = PINGROUP(168, _, _, _, _, _, _, _, _, _, _, egpio), + [169] = PINGROUP(169, _, _, _, _, _, _, _, _, _, _, egpio), + [170] = PINGROUP(170, _, _, _, _, _, _, _, _, _, _, egpio), + [171] = PINGROUP(171, _, _, _, _, _, _, _, _, _, _, egpio), + [172] = PINGROUP(172, _, _, _, _, _, _, _, _, _, _, egpio), + [173] = PINGROUP(173, _, _, _, _, _, _, _, _, _, _, egpio), + [174] = PINGROUP(174, _, _, _, _, _, _, _, _, _, _, egpio), + [175] = PINGROUP(175, _, _, _, _, _, _, _, _, _, _, egpio), + [176] = PINGROUP(176, _, _, _, _, _, _, _, _, _, _, egpio), + [177] = PINGROUP(177, _, _, _, _, _, _, _, _, _, _, egpio), + [178] = PINGROUP(178, _, _, _, _, _, _, _, _, _, _, egpio), + [179] = PINGROUP(179, _, _, _, _, _, _, _, _, _, _, egpio), + [180] = PINGROUP(180, _, _, _, _, _, _, _, _, _, _, egpio), + [181] = PINGROUP(181, _, _, _, _, _, _, _, _, _, _, egpio), + [182] = PINGROUP(182, _, _, _, _, _, _, _, _, _, _, egpio), + [183] = PINGROUP(183, _, _, _, _, _, _, _, _, _, _, egpio), + [184] = PINGROUP(184, _, _, _, _, _, _, _, _, _, _, egpio), + [185] = PINGROUP(185, _, _, _, _, _, _, _, _, _, _, egpio), + [186] = PINGROUP(186, _, _, _, _, _, _, _, _, _, _, egpio), + [187] = PINGROUP(187, _, _, _, _, _, _, _, _, _, _, egpio), + [188] = PINGROUP(188, _, _, _, _, _, _, _, _, _, _, egpio), + [189] = PINGROUP(189, _, _, _, _, _, _, _, _, _, _, egpio), + [190] = PINGROUP(190, _, _, _, _, _, _, _, _, _, _, egpio), + [191] = PINGROUP(191, _, _, _, _, _, _, _, _, _, _, egpio), + [192] = PINGROUP(192, _, _, _, _, _, _, _, _, _, _, egpio), + [193] = PINGROUP(193, _, _, _, _, _, _, _, _, _, _, egpio), + [194] = PINGROUP(194, _, _, _, _, _, _, _, _, _, _, egpio), + [195] = PINGROUP(195, _, _, _, _, _, _, _, _, _, _, egpio), + [196] = PINGROUP(196, _, _, _, _, _, _, _, _, _, _, egpio), + [197] = PINGROUP(197, _, _, _, _, _, _, _, _, _, _, egpio), + [198] = PINGROUP(198, _, _, _, _, _, _, _, _, _, _, egpio), + [199] = PINGROUP(199, _, _, _, _, _, _, _, _, _, _, egpio), + [200] = PINGROUP(200, _, _, _, _, _, _, _, _, _, _, egpio), + [201] = PINGROUP(201, _, _, _, _, _, _, _, _, _, _, egpio), + [202] = PINGROUP(202, _, _, _, _, _, _, _, _, _, _, egpio), + [203] = PINGROUP(203, _, _, _, _, _, _, _, _, _, _, egpio), + [204] = PINGROUP(204, _, _, _, _, _, _, _, _, _, _, egpio), + [205] = PINGROUP(205, _, _, _, _, _, _, _, _, _, _, egpio), + [206] = PINGROUP(206, i2chub0_se8, _, _, _, _, _, _, _, _, _, egpio), + [207] = PINGROUP(207, i2chub0_se8, _, _, _, _, _, _, _, _, _, egpio), + [208] = PINGROUP(208, _, _, _, _, _, _, _, _, _, _, egpio), + [209] = PINGROUP(209, _, _, _, _, _, _, _, _, _, _, egpio), + [210] = PINGROUP(210, _, _, _, _, _, _, _, _, _, _, egpio), + [211] = PINGROUP(211, _, _, _, _, _, _, _, _, _, _, egpio), + [212] = PINGROUP(212, _, _, _, _, _, _, _, _, _, _, egpio), + [213] = PINGROUP(213, _, _, _, _, _, _, _, _, _, _, egpio), + [214] = PINGROUP(214, _, _, _, _, _, _, _, _, _, _, egpio), + [215] = UFS_RESET(ufs_reset, 0xe2004, 0xe3000), + [216] = SDC_QDSD_PINGROUP(sdc2_clk, 0xdb000, 14, 6), + [217] = SDC_QDSD_PINGROUP(sdc2_cmd, 0xdb000, 11, 3), + [218] = SDC_QDSD_PINGROUP(sdc2_data, 0xdb000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map sm8750_pdc_map[] = { + { 0, 72 }, { 3, 80 }, { 4, 73 }, { 7, 74 }, { 8, 75 }, + { 11, 76 }, { 12, 87 }, { 15, 98 }, { 18, 110 }, { 19, 79 }, + { 23, 82 }, { 24, 83 }, { 27, 84 }, { 28, 85 }, { 31, 86 }, + { 32, 92 }, { 35, 68 }, { 36, 93 }, { 39, 94 }, { 43, 95 }, + { 46, 96 }, { 47, 121 }, { 48, 97 }, { 51, 118 }, { 54, 102 }, + { 55, 71 }, { 56, 103 }, { 57, 104 }, { 59, 105 }, { 61, 81 }, + { 63, 91 }, { 64, 77 }, { 65, 90 }, { 66, 106 }, { 67, 99 }, + { 68, 112 }, { 69, 113 }, { 75, 114 }, { 78, 115 }, { 79, 116 }, + { 80, 122 }, { 81, 123 }, { 84, 101 }, { 85, 124 }, { 86, 125 }, + { 87, 126 }, { 88, 127 }, { 95, 128 }, { 96, 129 }, { 97, 100 }, + { 98, 117 }, { 99, 78 }, { 102, 130 }, { 103, 131 }, { 104, 132 }, + { 108, 133 }, { 133, 134 }, { 137, 67 }, { 148, 135 }, { 150, 136 }, + { 152, 137 }, { 154, 138 }, { 155, 89 }, { 156, 139 }, { 159, 140 }, + { 162, 109 }, { 163, 108 }, { 166, 141 }, { 169, 142 }, { 171, 143 }, + { 172, 144 }, { 174, 145 }, { 176, 146 }, { 177, 120 }, { 181, 147 }, + { 182, 148 }, { 185, 149 }, { 188, 111 }, { 190, 88 }, { 191, 150 }, + { 192, 151 }, { 193, 152 }, { 196, 153 }, { 197, 154 }, { 198, 70 }, + { 199, 119 }, { 200, 69 }, { 201, 155 }, { 202, 156 }, { 203, 157 }, + { 204, 158 }, { 205, 107 }, { 209, 159 }, +}; + +static const struct msm_pinctrl_soc_data sm8750_tlmm = { + .pins = sm8750_pins, + .npins = ARRAY_SIZE(sm8750_pins), + .functions = sm8750_functions, + .nfunctions = ARRAY_SIZE(sm8750_functions), + .groups = sm8750_groups, + .ngroups = ARRAY_SIZE(sm8750_groups), + .ngpios = 216, + .wakeirq_map = sm8750_pdc_map, + .nwakeirq_map = ARRAY_SIZE(sm8750_pdc_map), + .egpio_func = 11, +}; + +static int sm8750_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &sm8750_tlmm); +} + +static const struct of_device_id sm8750_tlmm_of_match[] = { + { .compatible = "qcom,sm8750-tlmm", }, + {}, +}; + +static struct platform_driver sm8750_tlmm_driver = { + .driver = { + .name = "sm8750-tlmm", + .of_match_table = sm8750_tlmm_of_match, + }, + .probe = sm8750_tlmm_probe, + .remove = msm_pinctrl_remove, +}; + +static int __init sm8750_tlmm_init(void) +{ + return platform_driver_register(&sm8750_tlmm_driver); +} +arch_initcall(sm8750_tlmm_init); + +static void __exit sm8750_tlmm_exit(void) +{ + platform_driver_unregister(&sm8750_tlmm_driver); +} +module_exit(sm8750_tlmm_exit); + +MODULE_DESCRIPTION("QTI SM8750 TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, sm8750_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index d2dd66769aa89..0c806b8128b63 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -667,7 +667,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, "push-pull", "open-drain", "open-source" }; static const char *const strengths[] = { - "no", "high", "medium", "low" + "no", "low", "medium", "high" }; pad = pctldev->desc->pins[pin].drv_data; @@ -1169,7 +1169,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) * files which don't set the "gpio-ranges" property or systems that * utilize ACPI the driver has to call gpiochip_add_pin_range(). */ - if (!of_property_read_bool(dev->of_node, "gpio-ranges")) { + if (!of_property_present(dev->of_node, "gpio-ranges")) { ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins); if (ret) { @@ -1226,6 +1226,8 @@ static const struct of_device_id pmic_gpio_of_match[] = { { .compatible = "qcom,pm8550ve-gpio", .data = (void *) 8 }, { .compatible = "qcom,pm8550vs-gpio", .data = (void *) 6 }, { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, + /* pm8937 has 8 GPIOs with holes on 3, 4 and 6 */ + { .compatible = "qcom,pm8937-gpio", .data = (void *) 8 }, { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, /* pm8950 has 8 GPIOs with holes on 3 */ { .compatible = "qcom,pm8950-gpio", .data = (void *) 8 }, @@ -1268,7 +1270,7 @@ static struct platform_driver pmic_gpio_driver = { .of_match_table = pmic_gpio_of_match, }, .probe = pmic_gpio_probe, - .remove_new = pmic_gpio_remove, + .remove = pmic_gpio_remove, }; module_platform_driver(pmic_gpio_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c index d16ece90d926c..84de584cf7ebb 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c @@ -983,6 +983,7 @@ static const struct of_device_id pmic_mpp_of_match[] = { { .compatible = "qcom,pm8226-mpp", .data = (void *) 8 }, { .compatible = "qcom,pm8841-mpp", .data = (void *) 4 }, { .compatible = "qcom,pm8916-mpp", .data = (void *) 4 }, + { .compatible = "qcom,pm8937-mpp", .data = (void *) 4 }, { .compatible = "qcom,pm8941-mpp", .data = (void *) 8 }, { .compatible = "qcom,pm8950-mpp", .data = (void *) 4 }, { .compatible = "qcom,pmi8950-mpp", .data = (void *) 4 }, @@ -1000,7 +1001,7 @@ static struct platform_driver pmic_mpp_driver = { .of_match_table = pmic_mpp_of_match, }, .probe = pmic_mpp_probe, - .remove_new = pmic_mpp_remove, + .remove = pmic_mpp_remove, }; module_platform_driver(pmic_mpp_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index 9cd5247ea5742..2225dc49d477c 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -832,7 +832,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) * files which don't set the "gpio-ranges" property or systems that * utilize ACPI the driver has to call gpiochip_add_pin_range(). */ - if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) { + if (!of_property_present(pctrl->dev->of_node, "gpio-ranges")) { ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, pctrl->chip.ngpio); if (ret) { @@ -866,7 +866,7 @@ static struct platform_driver pm8xxx_gpio_driver = { .of_match_table = pm8xxx_gpio_of_match, }, .probe = pm8xxx_gpio_probe, - .remove_new = pm8xxx_gpio_remove, + .remove = pm8xxx_gpio_remove, }; module_platform_driver(pm8xxx_gpio_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c index 3aee6835a2de0..9b1039c08aa6d 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c @@ -949,7 +949,7 @@ static struct platform_driver pm8xxx_mpp_driver = { .of_match_table = pm8xxx_mpp_of_match, }, .probe = pm8xxx_mpp_probe, - .remove_new = pm8xxx_mpp_remove, + .remove = pm8xxx_mpp_remove, }; module_platform_driver(pm8xxx_mpp_driver); diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c index abfcdd3da9e82..419cb8facb2f3 100644 --- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -1861,7 +1861,7 @@ static struct platform_driver x1e80100_pinctrl_driver = { .of_match_table = x1e80100_pinctrl_of_match, }, .probe = x1e80100_pinctrl_probe, - .remove_new = msm_pinctrl_remove, + .remove = msm_pinctrl_remove, }; static int __init x1e80100_pinctrl_init(void) diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig index 14bd55d647319..7f3f41c7fe54c 100644 --- a/drivers/pinctrl/renesas/Kconfig +++ b/drivers/pinctrl/renesas/Kconfig @@ -41,6 +41,7 @@ config PINCTRL_RENESAS select PINCTRL_PFC_R8A779H0 if ARCH_R8A779H0 select PINCTRL_RZG2L if ARCH_RZG2L select PINCTRL_RZV2M if ARCH_R9A09G011 + select PINCTRL_RZG2L if ARCH_R9A09G057 select PINCTRL_PFC_SH7203 if CPU_SUBTYPE_SH7203 select PINCTRL_PFC_SH7264 if CPU_SUBTYPE_SH7264 select PINCTRL_PFC_SH7269 if CPU_SUBTYPE_SH7269 diff --git a/drivers/pinctrl/renesas/pinctrl-rza1.c b/drivers/pinctrl/renesas/pinctrl-rza1.c index 6527872813dc5..b1058504e0bb3 100644 --- a/drivers/pinctrl/renesas/pinctrl-rza1.c +++ b/drivers/pinctrl/renesas/pinctrl-rza1.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -750,6 +751,11 @@ static int rza1_pin_mux_single(struct rza1_pinctrl *rza1_pctl, static int rza1_gpio_request(struct gpio_chip *chip, unsigned int gpio) { struct rza1_port *port = gpiochip_get_data(chip); + int ret; + + ret = pinctrl_gpio_request(chip, gpio); + if (ret) + return ret; rza1_pin_reset(port, gpio); @@ -771,6 +777,7 @@ static void rza1_gpio_free(struct gpio_chip *chip, unsigned int gpio) struct rza1_port *port = gpiochip_get_data(chip); rza1_pin_reset(port, gpio); + pinctrl_gpio_free(chip, gpio); } static int rza1_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) diff --git a/drivers/pinctrl/renesas/pinctrl-rza2.c b/drivers/pinctrl/renesas/pinctrl-rza2.c index af689d7c117f3..dd1f8c29d3e75 100644 --- a/drivers/pinctrl/renesas/pinctrl-rza2.c +++ b/drivers/pinctrl/renesas/pinctrl-rza2.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -229,6 +230,8 @@ static const char * const rza2_gpio_names[] = { static struct gpio_chip chip = { .names = rza2_gpio_names, .base = -1, + .request = pinctrl_gpio_request, + .free = pinctrl_gpio_free, .get_direction = rza2_chip_get_direction, .direction_input = rza2_chip_direction_input, .direction_output = rza2_chip_direction_output, diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 5a403915fed2c..1df9cec2873ff 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -139,6 +139,8 @@ #define IEN(off) (0x1800 + (off) * 8) #define PUPD(off) (0x1C00 + (off) * 8) #define ISEL(off) (0x2C00 + (off) * 8) +#define NOD(off) (0x3000 + (off) * 8) +#define SMT(off) (0x3400 + (off) * 8) #define SD_CH(off, ch) ((off) + (ch) * 4) #define ETH_POC(off, ch) ((off) + (ch) * 4) #define QSPI (0x3008) @@ -160,6 +162,8 @@ #define IOLH_MASK 0x03 #define SR_MASK 0x01 #define PUPD_MASK 0x03 +#define NOD_MASK 0x01 +#define SMT_MASK 0x01 #define PM_INPUT 0x1 #define PM_OUTPUT 0x2 @@ -168,7 +172,6 @@ #define RZG2L_PIN_ID_TO_PIN(id) ((id) % RZG2L_PINS_PER_PORT) #define RZG2L_TINT_MAX_INTERRUPT 32 -#define RZG2L_TINT_IRQ_START_INDEX 9 #define RZG2L_PACK_HWIRQ(t, i) (((t) << 16) | (i)) /* Custom pinconf parameters */ @@ -247,6 +250,7 @@ enum rzg2l_iolh_index { * @iolh_groupb_ua: IOLH group B uA specific values * @iolh_groupc_ua: IOLH group C uA specific values * @iolh_groupb_oi: IOLH group B output impedance specific values + * @tint_start_index: the start index for the TINT interrupts * @drive_strength_ua: drive strength in uA is supported (otherwise mA is supported) * @func_base: base number for port function (see register PFC) * @oen_max_pin: the maximum pin number supporting output enable @@ -258,6 +262,7 @@ struct rzg2l_hwcfg { u16 iolh_groupb_ua[RZG2L_IOLH_IDX_MAX]; u16 iolh_groupc_ua[RZG2L_IOLH_IDX_MAX]; u16 iolh_groupb_oi[4]; + u16 tint_start_index; bool drive_strength_ua; u8 func_base; u8 oen_max_pin; @@ -1337,6 +1342,27 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, break; } + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (!(cfg & PIN_CFG_NOD)) + return -EINVAL; + + arg = rzg2l_read_pin_config(pctrl, NOD(off), bit, NOD_MASK); + if (!arg && param != PIN_CONFIG_DRIVE_PUSH_PULL) + return -EINVAL; + if (arg && param != PIN_CONFIG_DRIVE_OPEN_DRAIN) + return -EINVAL; + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (!(cfg & PIN_CFG_SMT)) + return -EINVAL; + + arg = rzg2l_read_pin_config(pctrl, SMT(off), bit, SMT_MASK); + if (!arg) + return -EINVAL; + break; + case RENESAS_RZV2H_PIN_CONFIG_OUTPUT_IMPEDANCE: if (!(cfg & PIN_CFG_IOLH_RZV2H)) return -EINVAL; @@ -1466,6 +1492,22 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, rzg2l_rmw_pin_config(pctrl, IOLH(off), bit, IOLH_MASK, index); break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (!(cfg & PIN_CFG_NOD)) + return -EINVAL; + + rzg2l_rmw_pin_config(pctrl, NOD(off), bit, NOD_MASK, + param == PIN_CONFIG_DRIVE_OPEN_DRAIN ? 1 : 0); + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (!(cfg & PIN_CFG_SMT)) + return -EINVAL; + + rzg2l_rmw_pin_config(pctrl, SMT(off), bit, SMT_MASK, arg); + break; + case RENESAS_RZV2H_PIN_CONFIG_OUTPUT_IMPEDANCE: if (!(cfg & PIN_CFG_IOLH_RZV2H)) return -EINVAL; @@ -2290,7 +2332,7 @@ static void rzg2l_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); - seq_printf(p, dev_name(gc->parent)); + seq_puts(p, dev_name(gc->parent)); } static int rzg2l_gpio_irq_set_wake(struct irq_data *data, unsigned int on) @@ -2379,7 +2421,7 @@ static int rzg2l_gpio_child_to_parent_hwirq(struct gpio_chip *gc, rzg2l_gpio_irq_endisable(pctrl, child, true); pctrl->hwirq[irq] = child; - irq += RZG2L_TINT_IRQ_START_INDEX; + irq += pctrl->data->hwcfg->tint_start_index; /* All these interrupts are level high in the CPU */ *parent_type = IRQ_TYPE_LEVEL_HIGH; @@ -2391,21 +2433,6 @@ static int rzg2l_gpio_child_to_parent_hwirq(struct gpio_chip *gc, return ret; } -static int rzg2l_gpio_populate_parent_fwspec(struct gpio_chip *chip, - union gpio_irq_fwspec *gfwspec, - unsigned int parent_hwirq, - unsigned int parent_type) -{ - struct irq_fwspec *fwspec = &gfwspec->fwspec; - - fwspec->fwnode = chip->irq.parent_domain->fwnode; - fwspec->param_count = 2; - fwspec->param[0] = parent_hwirq; - fwspec->param[1] = parent_type; - - return 0; -} - static void rzg2l_gpio_irq_restore(struct rzg2l_pinctrl *pctrl) { struct irq_domain *domain = pctrl->gpio_chip.irq.domain; @@ -2607,7 +2634,7 @@ static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl) girq->fwnode = dev_fwnode(pctrl->dev); girq->parent_domain = parent_domain; girq->child_to_parent_hwirq = rzg2l_gpio_child_to_parent_hwirq; - girq->populate_parent_alloc_arg = rzg2l_gpio_populate_parent_fwspec; + girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; girq->child_irq_domain_ops.free = rzg2l_gpio_irq_domain_free; girq->init_valid_mask = rzg2l_init_irq_valid_mask; @@ -2710,7 +2737,7 @@ static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl) ret = pinctrl_enable(pctrl->pctl); if (ret) - dev_err_probe(pctrl->dev, ret, "pinctrl enable failed\n"); + return dev_err_probe(pctrl->dev, ret, "pinctrl enable failed\n"); ret = rzg2l_gpio_register(pctrl); if (ret) @@ -3034,6 +3061,7 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = { [RZG2L_IOLH_IDX_3V3] = 2000, 4000, 8000, 12000, }, .iolh_groupb_oi = { 100, 66, 50, 33, }, + .tint_start_index = 9, .oen_max_pin = 0, }; @@ -3063,6 +3091,7 @@ static const struct rzg2l_hwcfg rzg3s_hwcfg = { /* 3v3 power source */ [RZG2L_IOLH_IDX_3V3] = 4500, 5200, 5700, 6050, }, + .tint_start_index = 9, .drive_strength_ua = true, .func_base = 1, .oen_max_pin = 1, /* Pin 1 of P0 and P7 is the maximum OEN pin. */ @@ -3073,6 +3102,7 @@ static const struct rzg2l_hwcfg rzv2h_hwcfg = { .regs = { .pwpr = 0x3c04, }, + .tint_start_index = 17, }; static struct rzg2l_pinctrl_data r9a07g043_data = { diff --git a/drivers/pinctrl/renesas/pinctrl-rzn1.c b/drivers/pinctrl/renesas/pinctrl-rzn1.c index 39af1fe79c846..d442d4f9981c9 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzn1.c +++ b/drivers/pinctrl/renesas/pinctrl-rzn1.c @@ -925,7 +925,7 @@ MODULE_DEVICE_TABLE(of, rzn1_pinctrl_match); static struct platform_driver rzn1_pinctrl_driver = { .probe = rzn1_pinctrl_probe, - .remove_new = rzn1_pinctrl_remove, + .remove = rzn1_pinctrl_remove, .driver = { .name = "rzn1-pinctrl", .of_match_table = rzn1_pinctrl_match, diff --git a/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c b/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c index 5480e0884abec..3ea7106ce5eae 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c @@ -58,6 +58,15 @@ static const struct samsung_pin_bank_type exynos850_bank_type_alive = { .reg_offset = { 0x00, 0x04, 0x08, 0x0c, }, }; +/* + * Bank type for non-alive type. Bit fields: + * CON: 4, DAT: 1, PUD: 2, DRV: 3, CONPDN: 2, PUDPDN: 2 + */ +static const struct samsung_pin_bank_type exynos8895_bank_type_off = { + .fld_width = { 4, 1, 2, 3, 2, 2, }, + .reg_offset = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, }, +}; + /* Pad retention control code for accessing PMU regmap */ static atomic_t exynos_shared_retention_refcnt; @@ -618,6 +627,300 @@ const struct samsung_pinctrl_of_match_data exynos850_of_data __initconst = { .num_ctrl = ARRAY_SIZE(exynos850_pin_ctrl), }; +/* pin banks of exynos990 pin-controller 0 (ALIVE) */ +static struct samsung_pin_bank_data exynos990_pin_banks0[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTW(8, 0x000, "gpa0", 0x00), + EXYNOS850_PIN_BANK_EINTW(8, 0x020, "gpa1", 0x04), + EXYNOS850_PIN_BANK_EINTW(8, 0x040, "gpa2", 0x08), + EXYNOS850_PIN_BANK_EINTW(8, 0x060, "gpa3", 0x0c), + EXYNOS850_PIN_BANK_EINTW(2, 0x080, "gpa4", 0x10), + EXYNOS850_PIN_BANK_EINTN(7, 0x0A0, "gpq0"), +}; + +/* pin banks of exynos990 pin-controller 1 (CMGP) */ +static struct samsung_pin_bank_data exynos990_pin_banks1[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTN(1, 0x000, "gpm0"), + EXYNOS850_PIN_BANK_EINTN(1, 0x020, "gpm1"), + EXYNOS850_PIN_BANK_EINTN(1, 0x040, "gpm2"), + EXYNOS850_PIN_BANK_EINTN(1, 0x060, "gpm3"), + EXYNOS850_PIN_BANK_EINTW(1, 0x080, "gpm4", 0x00), + EXYNOS850_PIN_BANK_EINTW(1, 0x0A0, "gpm5", 0x04), + EXYNOS850_PIN_BANK_EINTW(1, 0x0C0, "gpm6", 0x08), + EXYNOS850_PIN_BANK_EINTW(1, 0x0E0, "gpm7", 0x0c), + EXYNOS850_PIN_BANK_EINTW(1, 0x100, "gpm8", 0x10), + EXYNOS850_PIN_BANK_EINTW(1, 0x120, "gpm9", 0x14), + EXYNOS850_PIN_BANK_EINTW(1, 0x140, "gpm10", 0x18), + EXYNOS850_PIN_BANK_EINTW(1, 0x160, "gpm11", 0x1c), + EXYNOS850_PIN_BANK_EINTW(1, 0x180, "gpm12", 0x20), + EXYNOS850_PIN_BANK_EINTW(1, 0x1A0, "gpm13", 0x24), + EXYNOS850_PIN_BANK_EINTW(1, 0x1C0, "gpm14", 0x28), + EXYNOS850_PIN_BANK_EINTW(1, 0x1E0, "gpm15", 0x2c), + EXYNOS850_PIN_BANK_EINTW(1, 0x200, "gpm16", 0x30), + EXYNOS850_PIN_BANK_EINTW(1, 0x220, "gpm17", 0x34), + EXYNOS850_PIN_BANK_EINTW(1, 0x240, "gpm18", 0x38), + EXYNOS850_PIN_BANK_EINTW(1, 0x260, "gpm19", 0x3c), + EXYNOS850_PIN_BANK_EINTW(1, 0x280, "gpm20", 0x40), + EXYNOS850_PIN_BANK_EINTW(1, 0x2A0, "gpm21", 0x44), + EXYNOS850_PIN_BANK_EINTW(1, 0x2C0, "gpm22", 0x48), + EXYNOS850_PIN_BANK_EINTW(1, 0x2E0, "gpm23", 0x4c), + EXYNOS850_PIN_BANK_EINTW(1, 0x300, "gpm24", 0x50), + EXYNOS850_PIN_BANK_EINTW(1, 0x320, "gpm25", 0x54), + EXYNOS850_PIN_BANK_EINTW(1, 0x340, "gpm26", 0x58), + EXYNOS850_PIN_BANK_EINTW(1, 0x360, "gpm27", 0x5c), + EXYNOS850_PIN_BANK_EINTW(1, 0x380, "gpm28", 0x60), + EXYNOS850_PIN_BANK_EINTW(1, 0x3A0, "gpm29", 0x64), + EXYNOS850_PIN_BANK_EINTW(1, 0x3C0, "gpm30", 0x68), + EXYNOS850_PIN_BANK_EINTW(1, 0x3E0, "gpm31", 0x6c), + EXYNOS850_PIN_BANK_EINTW(1, 0x400, "gpm32", 0x70), + EXYNOS850_PIN_BANK_EINTW(1, 0x420, "gpm33", 0x74), + +}; + +/* pin banks of exynos990 pin-controller 2 (HSI1) */ +static struct samsung_pin_bank_data exynos990_pin_banks2[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTG(4, 0x000, "gpf0", 0x00), + EXYNOS850_PIN_BANK_EINTG(6, 0x020, "gpf1", 0x04), + EXYNOS850_PIN_BANK_EINTG(3, 0x040, "gpf2", 0x08), +}; + +/* pin banks of exynos990 pin-controller 3 (HSI2) */ +static struct samsung_pin_bank_data exynos990_pin_banks3[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTG(2, 0x000, "gpf3", 0x00), +}; + +/* pin banks of exynos990 pin-controller 4 (PERIC0) */ +static struct samsung_pin_bank_data exynos990_pin_banks4[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTG(8, 0x000, "gpp0", 0x00), + EXYNOS850_PIN_BANK_EINTG(8, 0x020, "gpp1", 0x04), + EXYNOS850_PIN_BANK_EINTG(8, 0x040, "gpp2", 0x08), + EXYNOS850_PIN_BANK_EINTG(8, 0x060, "gpp3", 0x0C), + EXYNOS850_PIN_BANK_EINTG(8, 0x080, "gpp4", 0x10), + EXYNOS850_PIN_BANK_EINTG(2, 0x0A0, "gpg0", 0x14), +}; + +/* pin banks of exynos990 pin-controller 5 (PERIC1) */ +static struct samsung_pin_bank_data exynos990_pin_banks5[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTG(8, 0x000, "gpp5", 0x00), + EXYNOS850_PIN_BANK_EINTG(8, 0x020, "gpp6", 0x04), + EXYNOS850_PIN_BANK_EINTG(8, 0x040, "gpp7", 0x08), + EXYNOS850_PIN_BANK_EINTG(8, 0x060, "gpp8", 0x0C), + EXYNOS850_PIN_BANK_EINTG(8, 0x080, "gpp9", 0x10), + EXYNOS850_PIN_BANK_EINTG(6, 0x0A0, "gpc0", 0x14), + EXYNOS850_PIN_BANK_EINTG(4, 0x0C0, "gpg1", 0x18), + EXYNOS850_PIN_BANK_EINTG(8, 0x0E0, "gpb0", 0x1C), + EXYNOS850_PIN_BANK_EINTG(8, 0x100, "gpb1", 0x20), + EXYNOS850_PIN_BANK_EINTG(8, 0x120, "gpb2", 0x24), +}; + +/* pin banks of exynos990 pin-controller 6 (VTS) */ +static struct samsung_pin_bank_data exynos990_pin_banks6[] = { + /* Must start with EINTG banks, ordered by EINT group number. */ + EXYNOS850_PIN_BANK_EINTG(7, 0x000, "gpv0", 0x00), +}; + +static const struct samsung_pin_ctrl exynos990_pin_ctrl[] __initconst = { + { + /* pin-controller instance 0 ALIVE data */ + .pin_banks = exynos990_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks0), + .eint_wkup_init = exynos_eint_wkup_init, + }, { + /* pin-controller instance 1 CMGP data */ + .pin_banks = exynos990_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks1), + .eint_wkup_init = exynos_eint_wkup_init, + }, { + /* pin-controller instance 2 HSI1 data */ + .pin_banks = exynos990_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks2), + .eint_gpio_init = exynos_eint_gpio_init, + }, { + /* pin-controller instance 3 HSI2 data */ + .pin_banks = exynos990_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks3), + .eint_gpio_init = exynos_eint_gpio_init, + }, { + /* pin-controller instance 4 PERIC0 data */ + .pin_banks = exynos990_pin_banks4, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks4), + .eint_gpio_init = exynos_eint_gpio_init, + }, { + /* pin-controller instance 5 PERIC1 data */ + .pin_banks = exynos990_pin_banks5, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks5), + .eint_gpio_init = exynos_eint_gpio_init, + }, { + /* pin-controller instance 6 VTS data */ + .pin_banks = exynos990_pin_banks6, + .nr_banks = ARRAY_SIZE(exynos990_pin_banks6), + }, +}; + +const struct samsung_pinctrl_of_match_data exynos990_of_data __initconst = { + .ctrl = exynos990_pin_ctrl, + .num_ctrl = ARRAY_SIZE(exynos990_pin_ctrl), +}; + +/* pin banks of exynos9810 pin-controller 0 (ALIVE) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks0[] __initconst = { + EXYNOS850_PIN_BANK_EINTN(6, 0x000, "etc1"), + EXYNOS850_PIN_BANK_EINTW(8, 0x020, "gpa0", 0x00), + EXYNOS850_PIN_BANK_EINTW(8, 0x040, "gpa1", 0x04), + EXYNOS850_PIN_BANK_EINTW(8, 0x060, "gpa2", 0x08), + EXYNOS850_PIN_BANK_EINTW(8, 0x080, "gpa3", 0x0c), + EXYNOS850_PIN_BANK_EINTN(6, 0x0A0, "gpq0"), + EXYNOS850_PIN_BANK_EINTW(2, 0x0C0, "gpa4", 0x10), +}; + +/* pin banks of exynos9810 pin-controller 1 (AUD) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks1[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(5, 0x000, "gpb0", 0x00), + EXYNOS850_PIN_BANK_EINTG(8, 0x020, "gpb1", 0x04), + EXYNOS850_PIN_BANK_EINTG(4, 0x040, "gpb2", 0x08), +}; + +/* pin banks of exynos9810 pin-controller 2 (CHUB) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks2[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(8, 0x000, "gph0", 0x00), + EXYNOS850_PIN_BANK_EINTG(5, 0x020, "gph1", 0x04), +}; + +/* pin banks of exynos9810 pin-controller 3 (CMGP) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks3[] __initconst = { + EXYNOS850_PIN_BANK_EINTW(1, 0x000, "gpm0", 0x00), + EXYNOS850_PIN_BANK_EINTW(1, 0x020, "gpm1", 0x04), + EXYNOS850_PIN_BANK_EINTW(1, 0x040, "gpm2", 0x08), + EXYNOS850_PIN_BANK_EINTW(1, 0x060, "gpm3", 0x0C), + EXYNOS850_PIN_BANK_EINTW(1, 0x080, "gpm4", 0x10), + EXYNOS850_PIN_BANK_EINTW(1, 0x0A0, "gpm5", 0x14), + EXYNOS850_PIN_BANK_EINTW(1, 0x0C0, "gpm6", 0x18), + EXYNOS850_PIN_BANK_EINTW(1, 0x0E0, "gpm7", 0x1C), + EXYNOS850_PIN_BANK_EINTW(1, 0x100, "gpm10", 0x20), + EXYNOS850_PIN_BANK_EINTW(1, 0x120, "gpm11", 0x24), + EXYNOS850_PIN_BANK_EINTW(1, 0x140, "gpm12", 0x28), + EXYNOS850_PIN_BANK_EINTW(1, 0x160, "gpm13", 0x2C), + EXYNOS850_PIN_BANK_EINTW(1, 0x180, "gpm14", 0x30), + EXYNOS850_PIN_BANK_EINTW(1, 0x1A0, "gpm15", 0x34), + EXYNOS850_PIN_BANK_EINTW(1, 0x1C0, "gpm16", 0x38), + EXYNOS850_PIN_BANK_EINTW(1, 0x1E0, "gpm17", 0x3C), + EXYNOS850_PIN_BANK_EINTW(1, 0x200, "gpm40", 0x40), + EXYNOS850_PIN_BANK_EINTW(1, 0x220, "gpm41", 0x44), + EXYNOS850_PIN_BANK_EINTW(1, 0x240, "gpm42", 0x48), + EXYNOS850_PIN_BANK_EINTW(1, 0x260, "gpm43", 0x4C), +}; + +/* pin banks of exynos9810 pin-controller 4 (FSYS0) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks4[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(2, 0x000, "gpf0", 0x00), +}; + +/* pin banks of exynos9810 pin-controller 5 (FSYS1) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks5[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(7, 0x000, "gpf1", 0x00), + EXYNOS850_PIN_BANK_EINTG(6, 0x020, "gpf2", 0x04), +}; + +/* pin banks of exynos9810 pin-controller 6 (PERIC0) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks6[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(8, 0x000, "gpp0", 0x00), + EXYNOS850_PIN_BANK_EINTG(8, 0x020, "gpp1", 0x04), + EXYNOS850_PIN_BANK_EINTG(8, 0x040, "gpp2", 0x08), + EXYNOS850_PIN_BANK_EINTG(4, 0x060, "gpp3", 0x0C), + EXYNOS850_PIN_BANK_EINTG(8, 0x080, "gpg0", 0x10), + EXYNOS850_PIN_BANK_EINTG(8, 0x0A0, "gpg1", 0x14), + EXYNOS850_PIN_BANK_EINTG(8, 0x0C0, "gpg2", 0x18), +}; + +/* pin banks of exynos9810 pin-controller 7 (PERIC1) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks7[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(8, 0x000, "gpp4", 0x00), + EXYNOS850_PIN_BANK_EINTG(8, 0x020, "gpp5", 0x04), + EXYNOS850_PIN_BANK_EINTG(4, 0x040, "gpp6", 0x08), + EXYNOS850_PIN_BANK_EINTG(8, 0x060, "gpc0", 0x0C), + EXYNOS850_PIN_BANK_EINTG(8, 0x080, "gpc1", 0x10), + EXYNOS850_PIN_BANK_EINTG(4, 0x0A0, "gpd0", 0x14), + EXYNOS850_PIN_BANK_EINTG(7, 0x0C0, "gpg3", 0x18), +}; + +/* pin banks of exynos9810 pin-controller 8 (VTS) */ +static const struct samsung_pin_bank_data exynos9810_pin_banks8[] __initconst = { + EXYNOS850_PIN_BANK_EINTG(3, 0x000, "gpt0", 0x00), +}; + +static const struct samsung_pin_ctrl exynos9810_pin_ctrl[] __initconst = { + { + /* pin-controller instance 0 ALIVE data */ + .pin_banks = exynos9810_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks0), + .eint_wkup_init = exynos_eint_wkup_init, + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 1 AUD data */ + .pin_banks = exynos9810_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks1), + }, { + /* pin-controller instance 2 CHUB data */ + .pin_banks = exynos9810_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks2), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 3 CMGP data */ + .pin_banks = exynos9810_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks3), + .eint_wkup_init = exynos_eint_wkup_init, + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 4 FSYS0 data */ + .pin_banks = exynos9810_pin_banks4, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks4), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 5 FSYS1 data */ + .pin_banks = exynos9810_pin_banks5, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks5), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 6 PERIC0 data */ + .pin_banks = exynos9810_pin_banks6, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks6), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 7 PERIC1 data */ + .pin_banks = exynos9810_pin_banks7, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks7), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 8 VTS data */ + .pin_banks = exynos9810_pin_banks8, + .nr_banks = ARRAY_SIZE(exynos9810_pin_banks8), + }, +}; + +const struct samsung_pinctrl_of_match_data exynos9810_of_data __initconst = { + .ctrl = exynos9810_pin_ctrl, + .num_ctrl = ARRAY_SIZE(exynos9810_pin_ctrl), +}; + /* pin banks of exynosautov9 pin-controller 0 (ALIVE) */ static const struct samsung_pin_bank_data exynosautov9_pin_banks0[] __initconst = { EXYNOS850_PIN_BANK_EINTW(8, 0x000, "gpa0", 0x00), @@ -866,6 +1169,134 @@ const struct samsung_pinctrl_of_match_data exynosautov920_of_data __initconst = .num_ctrl = ARRAY_SIZE(exynosautov920_pin_ctrl), }; +/* pin banks of exynos8895 pin-controller 0 (ALIVE) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks0[] __initconst = { + EXYNOS_PIN_BANK_EINTW(8, 0x020, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0x040, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0x060, "gpa2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0x080, "gpa3", 0x0c), + EXYNOS_PIN_BANK_EINTW(7, 0x0a0, "gpa4", 0x24), +}; + +/* pin banks of exynos8895 pin-controller 1 (ABOX) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks1[] __initconst = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gph0", 0x00), + EXYNOS_PIN_BANK_EINTG(7, 0x020, "gph1", 0x04), + EXYNOS_PIN_BANK_EINTG(4, 0x040, "gph3", 0x08), +}; + +/* pin banks of exynos8895 pin-controller 2 (VTS) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks2[] __initconst = { + EXYNOS_PIN_BANK_EINTG(3, 0x000, "gph2", 0x00), +}; + +/* pin banks of exynos8895 pin-controller 3 (FSYS0) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks3[] __initconst = { + EXYNOS8895_PIN_BANK_EINTG(3, 0x000, "gpi0", 0x00), + EXYNOS8895_PIN_BANK_EINTG(8, 0x020, "gpi1", 0x04), +}; + +/* pin banks of exynos8895 pin-controller 4 (FSYS1) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks4[] __initconst = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpj1", 0x00), + EXYNOS_PIN_BANK_EINTG(7, 0x020, "gpj0", 0x04), +}; + +/* pin banks of exynos8895 pin-controller 5 (BUSC) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks5[] __initconst = { + EXYNOS_PIN_BANK_EINTG(2, 0x000, "gpb2", 0x00), +}; + +/* pin banks of exynos8895 pin-controller 6 (PERIC0) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks6[] __initconst = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpd0", 0x00), + EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpd1", 0x04), + EXYNOS_PIN_BANK_EINTG(4, 0x040, "gpd2", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpd3", 0x0C), + EXYNOS_PIN_BANK_EINTG(4, 0x080, "gpb1", 0x10), + EXYNOS_PIN_BANK_EINTG(8, 0x0a0, "gpe7", 0x14), + EXYNOS_PIN_BANK_EINTG(8, 0x0c0, "gpf1", 0x18), +}; + +/* pin banks of exynos8895 pin-controller 7 (PERIC1) */ +static const struct samsung_pin_bank_data exynos8895_pin_banks7[] __initconst = { + EXYNOS_PIN_BANK_EINTG(3, 0x000, "gpb0", 0x00), + EXYNOS_PIN_BANK_EINTG(5, 0x020, "gpc0", 0x04), + EXYNOS_PIN_BANK_EINTG(5, 0x040, "gpc1", 0x08), + EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpc2", 0x0C), + EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpc3", 0x10), + EXYNOS_PIN_BANK_EINTG(4, 0x0a0, "gpk0", 0x14), + EXYNOS_PIN_BANK_EINTG(8, 0x0c0, "gpe5", 0x18), + EXYNOS_PIN_BANK_EINTG(8, 0x0e0, "gpe6", 0x1C), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpe2", 0x20), + EXYNOS_PIN_BANK_EINTG(8, 0x120, "gpe3", 0x24), + EXYNOS_PIN_BANK_EINTG(8, 0x140, "gpe4", 0x28), + EXYNOS_PIN_BANK_EINTG(4, 0x160, "gpf0", 0x2C), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpe1", 0x30), + EXYNOS_PIN_BANK_EINTG(2, 0x1a0, "gpg0", 0x34), +}; + +static const struct samsung_pin_ctrl exynos8895_pin_ctrl[] __initconst = { + { + /* pin-controller instance 0 ALIVE data */ + .pin_banks = exynos8895_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks0), + .eint_gpio_init = exynos_eint_gpio_init, + .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 1 ABOX data */ + .pin_banks = exynos8895_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks1), + }, { + /* pin-controller instance 2 VTS data */ + .pin_banks = exynos8895_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks2), + .eint_gpio_init = exynos_eint_gpio_init, + }, { + /* pin-controller instance 3 FSYS0 data */ + .pin_banks = exynos8895_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks3), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 4 FSYS1 data */ + .pin_banks = exynos8895_pin_banks4, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks4), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 5 BUSC data */ + .pin_banks = exynos8895_pin_banks5, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks5), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 6 PERIC0 data */ + .pin_banks = exynos8895_pin_banks6, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks6), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 7 PERIC1 data */ + .pin_banks = exynos8895_pin_banks7, + .nr_banks = ARRAY_SIZE(exynos8895_pin_banks7), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, +}; + +const struct samsung_pinctrl_of_match_data exynos8895_of_data __initconst = { + .ctrl = exynos8895_pin_ctrl, + .num_ctrl = ARRAY_SIZE(exynos8895_pin_ctrl), +}; + /* * Pinctrl driver data for Tesla FSD SoC. FSD SoC includes three * gpio/pin-mux/pinconfig controllers. diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.h b/drivers/pinctrl/samsung/pinctrl-exynos.h index 305cb1d31de49..7b7ff7ffeb56b 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.h +++ b/drivers/pinctrl/samsung/pinctrl-exynos.h @@ -141,6 +141,16 @@ .name = id \ } +#define EXYNOS8895_PIN_BANK_EINTG(pins, reg, id, offs) \ + { \ + .type = &exynos8895_bank_type_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_GPIO, \ + .eint_offset = offs, \ + .name = id \ + } + #define EXYNOSV920_PIN_BANK_EINTG(pins, reg, id, con_offs, mask_offs, pend_offs) \ { \ .type = &exynos850_bank_type_off, \ diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c index 675efa5d86a9a..bbedd980ec672 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.c +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c @@ -1477,6 +1477,12 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = &exynos7885_of_data }, { .compatible = "samsung,exynos850-pinctrl", .data = &exynos850_of_data }, + { .compatible = "samsung,exynos8895-pinctrl", + .data = &exynos8895_of_data }, + { .compatible = "samsung,exynos9810-pinctrl", + .data = &exynos9810_of_data }, + { .compatible = "samsung,exynos990-pinctrl", + .data = &exynos990_of_data }, { .compatible = "samsung,exynosautov9-pinctrl", .data = &exynosautov9_of_data }, { .compatible = "samsung,exynosautov920-pinctrl", diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h index a1e7377bd890b..bb0689d52ea0b 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.h +++ b/drivers/pinctrl/samsung/pinctrl-samsung.h @@ -384,6 +384,9 @@ extern const struct samsung_pinctrl_of_match_data exynos5433_of_data; extern const struct samsung_pinctrl_of_match_data exynos7_of_data; extern const struct samsung_pinctrl_of_match_data exynos7885_of_data; extern const struct samsung_pinctrl_of_match_data exynos850_of_data; +extern const struct samsung_pinctrl_of_match_data exynos8895_of_data; +extern const struct samsung_pinctrl_of_match_data exynos9810_of_data; +extern const struct samsung_pinctrl_of_match_data exynos990_of_data; extern const struct samsung_pinctrl_of_match_data exynosautov9_of_data; extern const struct samsung_pinctrl_of_match_data exynosautov920_of_data; extern const struct samsung_pinctrl_of_match_data fsd_of_data; diff --git a/drivers/pinctrl/sophgo/Kconfig b/drivers/pinctrl/sophgo/Kconfig index b14792ee46fc3..c05f909a88389 100644 --- a/drivers/pinctrl/sophgo/Kconfig +++ b/drivers/pinctrl/sophgo/Kconfig @@ -43,7 +43,7 @@ config PINCTRL_SOPHGO_SG2000 pinctrl-sg2000. config PINCTRL_SOPHGO_SG2002 - tristate "Sophgo SG2000 SoC Pinctrl driver" + tristate "Sophgo SG2002 SoC Pinctrl driver" depends on ARCH_SOPHGO || COMPILE_TEST depends on OF select PINCTRL_SOPHGO_CV18XX diff --git a/drivers/pinctrl/spacemit/Kconfig b/drivers/pinctrl/spacemit/Kconfig new file mode 100644 index 0000000000000..168f8a5ffbb95 --- /dev/null +++ b/drivers/pinctrl/spacemit/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Sophgo SoC PINCTRL drivers +# + +config PINCTRL_SPACEMIT_K1 + tristate "SpacemiT K1 SoC Pinctrl driver" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + help + Say Y to select the pinctrl driver for K1 SoC. + This pin controller allows selecting the mux function for + each pin. This driver can also be built as a module called + pinctrl-k1. diff --git a/drivers/pinctrl/spacemit/Makefile b/drivers/pinctrl/spacemit/Makefile new file mode 100644 index 0000000000000..fede1e80fe0ff --- /dev/null +++ b/drivers/pinctrl/spacemit/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PINCTRL_SPACEMIT_K1) += pinctrl-k1.o diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c new file mode 100644 index 0000000000000..a32579d736130 --- /dev/null +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Yixun Lan */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../core.h" +#include "../pinctrl-utils.h" +#include "../pinconf.h" +#include "../pinmux.h" +#include "pinctrl-k1.h" + +/* + * +---------+----------+-----------+--------+--------+----------+--------+ + * | pull | drive | schmitter | slew | edge | strong | mux | + * | up/down | strength | trigger | rate | detect | pull | mode | + * +---------+----------+-----------+--------+--------+----------+--------+ + * 3 bits 3 bits 2 bits 1 bit 3 bits 1 bit 3 bits + */ + +#define PAD_MUX GENMASK(2, 0) +#define PAD_STRONG_PULL BIT(3) +#define PAD_EDGE_RISE BIT(4) +#define PAD_EDGE_FALL BIT(5) +#define PAD_EDGE_CLEAR BIT(6) +#define PAD_SLEW_RATE GENMASK(12, 11) +#define PAD_SLEW_RATE_EN BIT(7) +#define PAD_SCHMITT GENMASK(9, 8) +#define PAD_DRIVE GENMASK(12, 10) +#define PAD_PULLDOWN BIT(13) +#define PAD_PULLUP BIT(14) +#define PAD_PULL_EN BIT(15) + +struct spacemit_pin { + u16 pin; + u16 flags; + u8 gpiofunc; +}; + +struct spacemit_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl_dev; + const struct spacemit_pinctrl_data *data; + struct pinctrl_desc pdesc; + + struct mutex mutex; + raw_spinlock_t lock; + + void __iomem *regs; +}; + +struct spacemit_pinctrl_data { + const struct pinctrl_pin_desc *pins; + const struct spacemit_pin *data; + u16 npins; +}; + +struct spacemit_pin_mux_config { + const struct spacemit_pin *pin; + u32 config; +}; + +struct spacemit_pin_drv_strength { + u8 val; + u32 mA; +}; + +/* map pin id to pinctrl register offset, refer MFPR definition */ +static unsigned int spacemit_pin_to_offset(unsigned int pin) +{ + unsigned int offset = 0; + + switch (pin) { + case 0 ... 85: + offset = pin + 1; + break; + case 86 ... 92: + offset = pin + 37; + break; + case 93 ... 97: + offset = pin + 24; + break; + case 98: + offset = 93; + break; + case 99: + offset = 92; + break; + case 100: + offset = 91; + break; + case 101: + offset = 90; + break; + case 102: + offset = 95; + break; + case 103: + offset = 94; + break; + case 104 ... 110: + offset = pin + 6; + break; + case 111 ... 127: + offset = pin + 20; + break; + default: + break; + } + + return offset << 2; +} + +static inline void __iomem *spacemit_pin_to_reg(struct spacemit_pinctrl *pctrl, + unsigned int pin) +{ + return pctrl->regs + spacemit_pin_to_offset(pin); +} + +static u16 spacemit_dt_get_pin(u32 value) +{ + return value >> 16; +} + +static u16 spacemit_dt_get_pin_mux(u32 value) +{ + return value & GENMASK(15, 0); +} + +static const struct spacemit_pin *spacemit_get_pin(struct spacemit_pinctrl *pctrl, + unsigned long pin) +{ + const struct spacemit_pin *pdata = pctrl->data->data; + int i; + + for (i = 0; i < pctrl->data->npins; i++) { + if (pin == pdata[i].pin) + return &pdata[i]; + } + + return NULL; +} + +static inline enum spacemit_pin_io_type spacemit_to_pin_io_type( + const struct spacemit_pin *pin) +{ + return K1_PIN_GET_IO_TYPE(pin->flags); +} + +/* External: IO voltage via external source, can be 1.8V or 3.3V */ +static const char * const io_type_desc[] = { + "None", + "Fixed/1V8", + "Fixed/3V3", + "External", +}; + +static void spacemit_pctrl_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *seq, unsigned int pin) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); + enum spacemit_pin_io_type type = spacemit_to_pin_io_type(spin); + void __iomem *reg; + u32 value; + + seq_printf(seq, "offset: 0x%04x ", spacemit_pin_to_offset(pin)); + seq_printf(seq, "type: %s ", io_type_desc[type]); + + reg = spacemit_pin_to_reg(pctrl, pin); + value = readl(reg); + seq_printf(seq, "mux: %ld reg: 0x%04x", (value & PAD_MUX), value); +} + +/* use IO high level output current as the table */ +static struct spacemit_pin_drv_strength spacemit_ds_1v8_tbl[4] = { + { 0, 11 }, + { 2, 21 }, + { 4, 32 }, + { 6, 42 }, +}; + +static struct spacemit_pin_drv_strength spacemit_ds_3v3_tbl[8] = { + { 0, 7 }, + { 2, 10 }, + { 4, 13 }, + { 6, 16 }, + { 1, 19 }, + { 3, 23 }, + { 5, 26 }, + { 7, 29 }, +}; + +static inline u8 spacemit_get_ds_value(struct spacemit_pin_drv_strength *tbl, + u32 num, u32 mA) +{ + int i; + + for (i = 0; i < num; i++) + if (mA <= tbl[i].mA) + return tbl[i].val; + + return tbl[num - 1].val; +} + +static inline u32 spacemit_get_ds_mA(struct spacemit_pin_drv_strength *tbl, + u32 num, u32 val) +{ + int i; + + for (i = 0; i < num; i++) + if (val == tbl[i].val) + return tbl[i].mA; + + return 0; +} + +static inline u8 spacemit_get_driver_strength(enum spacemit_pin_io_type type, + u32 mA) +{ + switch (type) { + case IO_TYPE_1V8: + return spacemit_get_ds_value(spacemit_ds_1v8_tbl, + ARRAY_SIZE(spacemit_ds_1v8_tbl), + mA); + case IO_TYPE_3V3: + return spacemit_get_ds_value(spacemit_ds_3v3_tbl, + ARRAY_SIZE(spacemit_ds_3v3_tbl), + mA); + default: + return 0; + } +} + +static inline u32 spacemit_get_drive_strength_mA(enum spacemit_pin_io_type type, + u32 value) +{ + switch (type) { + case IO_TYPE_1V8: + return spacemit_get_ds_mA(spacemit_ds_1v8_tbl, + ARRAY_SIZE(spacemit_ds_1v8_tbl), + value & 0x6); + case IO_TYPE_3V3: + return spacemit_get_ds_mA(spacemit_ds_3v3_tbl, + ARRAY_SIZE(spacemit_ds_3v3_tbl), + value); + default: + return 0; + } +} + +static int spacemit_pctrl_check_power(struct pinctrl_dev *pctldev, + struct device_node *dn, + struct spacemit_pin_mux_config *pinmuxs, + int num_pins, const char *grpname) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = pctrl->dev; + enum spacemit_pin_io_type type; + u32 power = 0, i; + + of_property_read_u32(dn, "power-source", &power); + + for (i = 0; i < num_pins; i++) { + type = spacemit_to_pin_io_type(pinmuxs[i].pin); + + if (type != IO_TYPE_EXTERNAL) + continue; + + switch (power) { + case PIN_POWER_STATE_1V8: + case PIN_POWER_STATE_3V3: + break; + default: + dev_err(dev, "group %s has unsupported power\n", + grpname); + return -ENOTSUPP; + } + } + + return 0; +} + +static int spacemit_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = pctrl->dev; + struct device_node *child; + struct pinctrl_map *map; + const char **grpnames; + const char *grpname; + int ngroups = 0; + int nmaps = 0; + int ret; + + for_each_available_child_of_node(np, child) + ngroups += 1; + + grpnames = devm_kcalloc(dev, ngroups, sizeof(*grpnames), GFP_KERNEL); + if (!grpnames) + return -ENOMEM; + + map = kcalloc(ngroups * 2, sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + ngroups = 0; + guard(mutex)(&pctrl->mutex); + for_each_available_child_of_node_scoped(np, child) { + struct spacemit_pin_mux_config *pinmuxs; + unsigned int config, *pins; + int i, npins; + + npins = of_property_count_u32_elems(child, "pinmux"); + + if (npins < 1) { + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn\n", + np, child); + return -EINVAL; + } + + grpname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", + np, child); + if (!grpname) + return -ENOMEM; + + grpnames[ngroups++] = grpname; + + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + pinmuxs = devm_kcalloc(dev, npins, sizeof(*pinmuxs), GFP_KERNEL); + if (!pinmuxs) + return -ENOMEM; + + for (i = 0; i < npins; i++) { + ret = of_property_read_u32_index(child, "pinmux", + i, &config); + + if (ret) + return -EINVAL; + + pins[i] = spacemit_dt_get_pin(config); + pinmuxs[i].config = config; + pinmuxs[i].pin = spacemit_get_pin(pctrl, pins[i]); + + if (!pinmuxs[i].pin) + return dev_err_probe(dev, -ENODEV, "failed to get pin %d\n", pins[i]); + } + + ret = spacemit_pctrl_check_power(pctldev, child, pinmuxs, + npins, grpname); + if (ret < 0) + return ret; + + map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP; + map[nmaps].data.mux.function = np->name; + map[nmaps].data.mux.group = grpname; + nmaps += 1; + + ret = pinctrl_generic_add_group(pctldev, grpname, + pins, npins, pinmuxs); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add group %s: %d\n", grpname, ret); + + ret = pinconf_generic_parse_dt_config(child, pctldev, + &map[nmaps].data.configs.configs, + &map[nmaps].data.configs.num_configs); + if (ret) + return dev_err_probe(dev, ret, "failed to parse pin config of group %s\n", + grpname); + + if (map[nmaps].data.configs.num_configs == 0) + continue; + + map[nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP; + map[nmaps].data.configs.group_or_pin = grpname; + nmaps += 1; + } + + ret = pinmux_generic_add_function(pctldev, np->name, + grpnames, ngroups, NULL); + if (ret < 0) { + pinctrl_utils_free_map(pctldev, map, nmaps); + return dev_err_probe(dev, ret, "error adding function %s\n", np->name); + } + + *maps = map; + *num_maps = nmaps; + + return 0; +} + +static const struct pinctrl_ops spacemit_pctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .pin_dbg_show = spacemit_pctrl_dbg_show, + .dt_node_to_map = spacemit_pctrl_dt_node_to_map, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int spacemit_pmx_set_mux(struct pinctrl_dev *pctldev, + unsigned int fsel, unsigned int gsel) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct group_desc *group; + const struct spacemit_pin_mux_config *configs; + unsigned int i, mux; + void __iomem *reg; + + group = pinctrl_generic_get_group(pctldev, gsel); + if (!group) + return -EINVAL; + + configs = group->data; + + for (i = 0; i < group->grp.npins; i++) { + const struct spacemit_pin *spin = configs[i].pin; + u32 value = configs[i].config; + + reg = spacemit_pin_to_reg(pctrl, spin->pin); + mux = spacemit_dt_get_pin_mux(value); + + guard(raw_spinlock_irqsave)(&pctrl->lock); + value = readl_relaxed(reg) & ~PAD_MUX; + writel_relaxed(mux | value, reg); + } + + return 0; +} + +static int spacemit_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); + void __iomem *reg; + + reg = spacemit_pin_to_reg(pctrl, pin); + guard(raw_spinlock_irqsave)(&pctrl->lock); + writel_relaxed(spin->gpiofunc, reg); + + return 0; +} + +static const struct pinmux_ops spacemit_pmx_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = spacemit_pmx_set_mux, + .gpio_request_enable = spacemit_request_gpio, + .strict = true, +}; + +static int spacemit_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + int param = pinconf_to_config_param(*config); + u32 value, arg = 0; + + if (!pin) + return -EINVAL; + + value = readl(spacemit_pin_to_reg(pctrl, pin)); + + switch (param) { + case PIN_CONFIG_SLEW_RATE: + if (FIELD_GET(PAD_SLEW_RATE_EN, value)) + arg = FIELD_GET(PAD_SLEW_RATE, value) + 2; + else + arg = 0; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +#define ENABLE_DRV_STRENGTH BIT(1) +#define ENABLE_SLEW_RATE BIT(2) +static int spacemit_pinconf_generate_config(const struct spacemit_pin *spin, + unsigned long *configs, + unsigned int num_configs, + u32 *value) +{ + enum spacemit_pin_io_type type; + int i, param; + u32 v = 0, voltage = 0, arg, val; + u32 flag = 0, drv_strength, slew_rate; + + if (!spin) + return -EINVAL; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + v &= ~(PAD_PULL_EN | PAD_PULLDOWN | PAD_PULLUP); + v &= ~PAD_STRONG_PULL; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + v &= ~(PAD_PULLUP | PAD_STRONG_PULL); + v |= (PAD_PULL_EN | PAD_PULLDOWN); + break; + case PIN_CONFIG_BIAS_PULL_UP: + v &= ~PAD_PULLDOWN; + v |= (PAD_PULL_EN | PAD_PULLUP); + + if (arg == 1) + v |= PAD_STRONG_PULL; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + flag |= ENABLE_DRV_STRENGTH; + drv_strength = arg; + break; + case PIN_CONFIG_INPUT_SCHMITT: + v &= ~PAD_SCHMITT; + v |= FIELD_PREP(PAD_SCHMITT, arg); + break; + case PIN_CONFIG_POWER_SOURCE: + voltage = arg; + break; + case PIN_CONFIG_SLEW_RATE: + if (arg) { + flag |= ENABLE_SLEW_RATE; + v |= PAD_SLEW_RATE_EN; + slew_rate = arg; + } else { + v &= ~PAD_SLEW_RATE_EN; + } + break; + default: + return -EINVAL; + } + } + + if (flag & ENABLE_DRV_STRENGTH) { + type = spacemit_to_pin_io_type(spin); + + /* fix external io type */ + if (type == IO_TYPE_EXTERNAL) { + switch (voltage) { + case 1800: + type = IO_TYPE_1V8; + break; + case 3300: + type = IO_TYPE_3V3; + break; + default: + return -EINVAL; + } + } + + val = spacemit_get_driver_strength(type, drv_strength); + + v &= ~PAD_DRIVE; + v |= FIELD_PREP(PAD_DRIVE, val); + } + + if (flag & ENABLE_SLEW_RATE) { + /* check, driver strength & slew rate */ + if (flag & ENABLE_DRV_STRENGTH) { + val = FIELD_GET(PAD_SLEW_RATE, v) + 2; + if (slew_rate > 1 && slew_rate != val) { + pr_err("slew rate conflict with drive strength\n"); + return -EINVAL; + } + } else { + v &= ~PAD_SLEW_RATE; + slew_rate = slew_rate > 1 ? (slew_rate - 2) : 0; + v |= FIELD_PREP(PAD_SLEW_RATE, slew_rate); + } + } + + *value = v; + + return 0; +} + +static int spacemit_pin_set_config(struct spacemit_pinctrl *pctrl, + unsigned int pin, u32 value) +{ + const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); + void __iomem *reg; + unsigned int mux; + + if (!pin) + return -EINVAL; + + reg = spacemit_pin_to_reg(pctrl, spin->pin); + + guard(raw_spinlock_irqsave)(&pctrl->lock); + mux = readl_relaxed(reg) & PAD_MUX; + writel_relaxed(mux | value, reg); + + return 0; +} + +static int spacemit_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); + u32 value; + + if (spacemit_pinconf_generate_config(spin, configs, num_configs, &value)) + return -EINVAL; + + return spacemit_pin_set_config(pctrl, pin, value); +} + +static int spacemit_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int gsel, + unsigned long *configs, + unsigned int num_configs) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct spacemit_pin *spin; + const struct group_desc *group; + u32 value; + int i; + + group = pinctrl_generic_get_group(pctldev, gsel); + if (!group) + return -EINVAL; + + spin = spacemit_get_pin(pctrl, group->grp.pins[0]); + if (spacemit_pinconf_generate_config(spin, configs, num_configs, &value)) + return -EINVAL; + + for (i = 0; i < group->grp.npins; i++) + spacemit_pin_set_config(pctrl, group->grp.pins[i], value); + + return 0; +} + +static void spacemit_pinconf_dbg_pull(struct seq_file *seq, unsigned int value) +{ + u32 normal, strong; + + if (!FIELD_GET(PAD_PULL_EN, value)) { + seq_puts(seq, ", bias pull disabled"); + return; + } + + if (FIELD_GET(PAD_PULLDOWN, value)) + seq_puts(seq, ", bias pull down"); + + normal = FIELD_GET(PAD_PULLUP, value); + strong = FIELD_GET(PAD_STRONG_PULL, value); + + if (normal && strong) + seq_puts(seq, ", bias strong pull up"); + else if (normal) + seq_puts(seq, ", bias normal pull up"); +} + +static void spacemit_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *seq, unsigned int pin) +{ + struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); + enum spacemit_pin_io_type type = spacemit_to_pin_io_type(spin); + void __iomem *reg = spacemit_pin_to_reg(pctrl, pin); + u32 value, tmp, mA; + + value = readl(reg); + spacemit_pinconf_dbg_pull(seq, value); + + seq_printf(seq, ", io type (%s)", io_type_desc[type]); + + tmp = FIELD_GET(PAD_DRIVE, value); + if (type == IO_TYPE_1V8 || type == IO_TYPE_3V3) { + mA = spacemit_get_drive_strength_mA(type, tmp); + seq_printf(seq, ", drive strength (%d mA)", mA); + } + + /* drive strength depend on power source, so show all values */ + if (type == IO_TYPE_EXTERNAL) + seq_printf(seq, ", drive strength (%d or %d mA)", + spacemit_get_drive_strength_mA(IO_TYPE_1V8, tmp), + spacemit_get_drive_strength_mA(IO_TYPE_3V3, tmp)); + + seq_printf(seq, ", register (0x%04x)\n", value); +} + +static const struct pinconf_ops spacemit_pinconf_ops = { + .pin_config_get = spacemit_pinconf_get, + .pin_config_set = spacemit_pinconf_set, + .pin_config_group_set = spacemit_pinconf_group_set, + .pin_config_dbg_show = spacemit_pinconf_dbg_show, + .is_generic = true, +}; + +static int spacemit_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spacemit_pinctrl *pctrl; + const struct spacemit_pinctrl_data *pctrl_data; + int ret; + + pctrl_data = device_get_match_data(dev); + if (!pctrl_data) + return -ENODEV; + + if (pctrl_data->npins == 0) + return dev_err_probe(dev, -EINVAL, "invalid pin data\n"); + + pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pctrl->regs)) + return PTR_ERR(pctrl->regs); + + pctrl->pdesc.name = dev_name(dev); + pctrl->pdesc.pins = pctrl_data->pins; + pctrl->pdesc.npins = pctrl_data->npins; + pctrl->pdesc.pctlops = &spacemit_pctrl_ops; + pctrl->pdesc.pmxops = &spacemit_pmx_ops; + pctrl->pdesc.confops = &spacemit_pinconf_ops; + pctrl->pdesc.owner = THIS_MODULE; + + pctrl->data = pctrl_data; + pctrl->dev = dev; + raw_spin_lock_init(&pctrl->lock); + mutex_init(&pctrl->mutex); + + platform_set_drvdata(pdev, pctrl); + + ret = devm_pinctrl_register_and_init(dev, &pctrl->pdesc, + pctrl, &pctrl->pctl_dev); + if (ret) + return dev_err_probe(dev, ret, + "fail to register pinctrl driver\n"); + + return pinctrl_enable(pctrl->pctl_dev); +} + +static const struct pinctrl_pin_desc k1_pin_desc[] = { + PINCTRL_PIN(0, "GPIO_00"), + PINCTRL_PIN(1, "GPIO_01"), + PINCTRL_PIN(2, "GPIO_02"), + PINCTRL_PIN(3, "GPIO_03"), + PINCTRL_PIN(4, "GPIO_04"), + PINCTRL_PIN(5, "GPIO_05"), + PINCTRL_PIN(6, "GPIO_06"), + PINCTRL_PIN(7, "GPIO_07"), + PINCTRL_PIN(8, "GPIO_08"), + PINCTRL_PIN(9, "GPIO_09"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70/PRI_DTI"), + PINCTRL_PIN(71, "GPIO_71/PRI_TMS"), + PINCTRL_PIN(72, "GPIO_72/PRI_TCK"), + PINCTRL_PIN(73, "GPIO_73/PRI_TDO"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93/PWR_SCL"), + PINCTRL_PIN(94, "GPIO_94/PWR_SDA"), + PINCTRL_PIN(95, "GPIO_95/VCX0_EN"), + PINCTRL_PIN(96, "GPIO_96/DVL0"), + PINCTRL_PIN(97, "GPIO_97/DVL1"), + PINCTRL_PIN(98, "GPIO_98/QSPI_DAT3"), + PINCTRL_PIN(99, "GPIO_99/QSPI_DAT2"), + PINCTRL_PIN(100, "GPIO_100/QSPI_DAT1"), + PINCTRL_PIN(101, "GPIO_101/QSPI_DAT0"), + PINCTRL_PIN(102, "GPIO_102/QSPI_CLK"), + PINCTRL_PIN(103, "GPIO_103/QSPI_CS1"), + PINCTRL_PIN(104, "GPIO_104/MMC1_DAT3"), + PINCTRL_PIN(105, "GPIO_105/MMC1_DAT2"), + PINCTRL_PIN(106, "GPIO_106/MMC1_DAT1"), + PINCTRL_PIN(107, "GPIO_107/MMC1_DAT0"), + PINCTRL_PIN(108, "GPIO_108/MMC1_CMD"), + PINCTRL_PIN(109, "GPIO_109/MMC1_CLK"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), +}; + +static const struct spacemit_pin k1_pin_data[ARRAY_SIZE(k1_pin_desc)] = { + K1_FUNC_PIN(0, 0, IO_TYPE_1V8), + K1_FUNC_PIN(1, 0, IO_TYPE_1V8), + K1_FUNC_PIN(2, 0, IO_TYPE_1V8), + K1_FUNC_PIN(3, 0, IO_TYPE_1V8), + K1_FUNC_PIN(4, 0, IO_TYPE_1V8), + K1_FUNC_PIN(5, 0, IO_TYPE_1V8), + K1_FUNC_PIN(6, 0, IO_TYPE_1V8), + K1_FUNC_PIN(7, 0, IO_TYPE_1V8), + K1_FUNC_PIN(8, 0, IO_TYPE_1V8), + K1_FUNC_PIN(9, 0, IO_TYPE_1V8), + K1_FUNC_PIN(10, 0, IO_TYPE_1V8), + K1_FUNC_PIN(11, 0, IO_TYPE_1V8), + K1_FUNC_PIN(12, 0, IO_TYPE_1V8), + K1_FUNC_PIN(13, 0, IO_TYPE_1V8), + K1_FUNC_PIN(14, 0, IO_TYPE_1V8), + K1_FUNC_PIN(15, 0, IO_TYPE_1V8), + K1_FUNC_PIN(16, 0, IO_TYPE_1V8), + K1_FUNC_PIN(17, 0, IO_TYPE_1V8), + K1_FUNC_PIN(18, 0, IO_TYPE_1V8), + K1_FUNC_PIN(19, 0, IO_TYPE_1V8), + K1_FUNC_PIN(20, 0, IO_TYPE_1V8), + K1_FUNC_PIN(21, 0, IO_TYPE_1V8), + K1_FUNC_PIN(22, 0, IO_TYPE_1V8), + K1_FUNC_PIN(23, 0, IO_TYPE_1V8), + K1_FUNC_PIN(24, 0, IO_TYPE_1V8), + K1_FUNC_PIN(25, 0, IO_TYPE_1V8), + K1_FUNC_PIN(26, 0, IO_TYPE_1V8), + K1_FUNC_PIN(27, 0, IO_TYPE_1V8), + K1_FUNC_PIN(28, 0, IO_TYPE_1V8), + K1_FUNC_PIN(29, 0, IO_TYPE_1V8), + K1_FUNC_PIN(30, 0, IO_TYPE_1V8), + K1_FUNC_PIN(31, 0, IO_TYPE_1V8), + K1_FUNC_PIN(32, 0, IO_TYPE_1V8), + K1_FUNC_PIN(33, 0, IO_TYPE_1V8), + K1_FUNC_PIN(34, 0, IO_TYPE_1V8), + K1_FUNC_PIN(35, 0, IO_TYPE_1V8), + K1_FUNC_PIN(36, 0, IO_TYPE_1V8), + K1_FUNC_PIN(37, 0, IO_TYPE_1V8), + K1_FUNC_PIN(38, 0, IO_TYPE_1V8), + K1_FUNC_PIN(39, 0, IO_TYPE_1V8), + K1_FUNC_PIN(40, 0, IO_TYPE_1V8), + K1_FUNC_PIN(41, 0, IO_TYPE_1V8), + K1_FUNC_PIN(42, 0, IO_TYPE_1V8), + K1_FUNC_PIN(43, 0, IO_TYPE_1V8), + K1_FUNC_PIN(44, 0, IO_TYPE_1V8), + K1_FUNC_PIN(45, 0, IO_TYPE_1V8), + K1_FUNC_PIN(46, 0, IO_TYPE_1V8), + K1_FUNC_PIN(47, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(48, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(49, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(50, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(51, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(52, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(53, 0, IO_TYPE_1V8), + K1_FUNC_PIN(54, 0, IO_TYPE_1V8), + K1_FUNC_PIN(55, 0, IO_TYPE_1V8), + K1_FUNC_PIN(56, 0, IO_TYPE_1V8), + K1_FUNC_PIN(57, 0, IO_TYPE_1V8), + K1_FUNC_PIN(58, 0, IO_TYPE_1V8), + K1_FUNC_PIN(59, 0, IO_TYPE_1V8), + K1_FUNC_PIN(60, 0, IO_TYPE_1V8), + K1_FUNC_PIN(61, 0, IO_TYPE_1V8), + K1_FUNC_PIN(62, 0, IO_TYPE_1V8), + K1_FUNC_PIN(63, 0, IO_TYPE_1V8), + K1_FUNC_PIN(64, 0, IO_TYPE_1V8), + K1_FUNC_PIN(65, 0, IO_TYPE_1V8), + K1_FUNC_PIN(66, 0, IO_TYPE_1V8), + K1_FUNC_PIN(67, 0, IO_TYPE_1V8), + K1_FUNC_PIN(68, 0, IO_TYPE_1V8), + K1_FUNC_PIN(69, 0, IO_TYPE_1V8), + K1_FUNC_PIN(70, 1, IO_TYPE_1V8), + K1_FUNC_PIN(71, 1, IO_TYPE_1V8), + K1_FUNC_PIN(72, 1, IO_TYPE_1V8), + K1_FUNC_PIN(73, 1, IO_TYPE_1V8), + K1_FUNC_PIN(74, 0, IO_TYPE_1V8), + K1_FUNC_PIN(75, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(76, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(77, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(78, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(79, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(80, 0, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(81, 0, IO_TYPE_1V8), + K1_FUNC_PIN(82, 0, IO_TYPE_1V8), + K1_FUNC_PIN(83, 0, IO_TYPE_1V8), + K1_FUNC_PIN(84, 0, IO_TYPE_1V8), + K1_FUNC_PIN(85, 0, IO_TYPE_1V8), + K1_FUNC_PIN(86, 0, IO_TYPE_1V8), + K1_FUNC_PIN(87, 0, IO_TYPE_1V8), + K1_FUNC_PIN(88, 0, IO_TYPE_1V8), + K1_FUNC_PIN(89, 0, IO_TYPE_1V8), + K1_FUNC_PIN(90, 0, IO_TYPE_1V8), + K1_FUNC_PIN(91, 0, IO_TYPE_1V8), + K1_FUNC_PIN(92, 0, IO_TYPE_1V8), + K1_FUNC_PIN(93, 1, IO_TYPE_1V8), + K1_FUNC_PIN(94, 1, IO_TYPE_1V8), + K1_FUNC_PIN(95, 1, IO_TYPE_1V8), + K1_FUNC_PIN(96, 1, IO_TYPE_1V8), + K1_FUNC_PIN(97, 1, IO_TYPE_1V8), + K1_FUNC_PIN(98, 1, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(99, 1, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(100, 1, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(101, 1, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(102, 1, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(103, 1, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(104, 4, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(105, 4, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(106, 4, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(107, 4, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(108, 4, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(109, 4, IO_TYPE_EXTERNAL), + K1_FUNC_PIN(110, 0, IO_TYPE_1V8), + K1_FUNC_PIN(111, 0, IO_TYPE_1V8), + K1_FUNC_PIN(112, 0, IO_TYPE_1V8), + K1_FUNC_PIN(113, 0, IO_TYPE_1V8), + K1_FUNC_PIN(114, 0, IO_TYPE_1V8), + K1_FUNC_PIN(115, 0, IO_TYPE_1V8), + K1_FUNC_PIN(116, 0, IO_TYPE_1V8), + K1_FUNC_PIN(117, 0, IO_TYPE_1V8), + K1_FUNC_PIN(118, 0, IO_TYPE_1V8), + K1_FUNC_PIN(119, 0, IO_TYPE_1V8), + K1_FUNC_PIN(120, 0, IO_TYPE_1V8), + K1_FUNC_PIN(121, 0, IO_TYPE_1V8), + K1_FUNC_PIN(122, 0, IO_TYPE_1V8), + K1_FUNC_PIN(123, 0, IO_TYPE_1V8), + K1_FUNC_PIN(124, 0, IO_TYPE_1V8), + K1_FUNC_PIN(125, 0, IO_TYPE_1V8), + K1_FUNC_PIN(126, 0, IO_TYPE_1V8), + K1_FUNC_PIN(127, 0, IO_TYPE_1V8), +}; + +static const struct spacemit_pinctrl_data k1_pinctrl_data = { + .pins = k1_pin_desc, + .data = k1_pin_data, + .npins = ARRAY_SIZE(k1_pin_desc), +}; + +static const struct of_device_id k1_pinctrl_ids[] = { + { .compatible = "spacemit,k1-pinctrl", .data = &k1_pinctrl_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, k1_pinctrl_ids); + +static struct platform_driver k1_pinctrl_driver = { + .probe = spacemit_pinctrl_probe, + .driver = { + .name = "k1-pinctrl", + .suppress_bind_attrs = true, + .of_match_table = k1_pinctrl_ids, + }, +}; +module_platform_driver(k1_pinctrl_driver); + +MODULE_AUTHOR("Yixun Lan "); +MODULE_DESCRIPTION("Pinctrl driver for the SpacemiT K1 SoC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.h b/drivers/pinctrl/spacemit/pinctrl-k1.h new file mode 100644 index 0000000000000..16143fea469eb --- /dev/null +++ b/drivers/pinctrl/spacemit/pinctrl-k1.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Yixun Lan */ + +#ifndef _PINCTRL_SPACEMIT_K1_H +#define _PINCTRL_SPACEMIT_K1_H + +#include +#include +#include +#include +#include +#include +#include +#include + +enum spacemit_pin_io_type { + IO_TYPE_NONE = 0, + IO_TYPE_1V8, + IO_TYPE_3V3, + IO_TYPE_EXTERNAL, +}; + +#define PIN_POWER_STATE_1V8 1800 +#define PIN_POWER_STATE_3V3 3300 + +#define K1_PIN_IO_TYPE GENMASK(2, 1) + +#define K1_PIN_CAP_IO_TYPE(type) \ + FIELD_PREP_CONST(K1_PIN_IO_TYPE, type) +#define K1_PIN_GET_IO_TYPE(val) \ + FIELD_GET(K1_PIN_IO_TYPE, val) + +#define K1_FUNC_PIN(_id, _gpiofunc, _io) \ + { \ + .pin = (_id), \ + .gpiofunc = (_gpiofunc), \ + .flags = (K1_PIN_CAP_IO_TYPE(_io)), \ + } + +#endif /* _PINCTRL_SPACEMIT_K1_H */ diff --git a/drivers/pinctrl/sprd/pinctrl-sprd-sc9860.c b/drivers/pinctrl/sprd/pinctrl-sprd-sc9860.c index 9c3c39dc6550c..d14f382f2392c 100644 --- a/drivers/pinctrl/sprd/pinctrl-sprd-sc9860.c +++ b/drivers/pinctrl/sprd/pinctrl-sprd-sc9860.c @@ -943,7 +943,7 @@ static struct platform_driver sprd_pinctrl_driver = { .of_match_table = sprd_pinctrl_of_match, }, .probe = sprd_pinctrl_probe, - .remove_new = sprd_pinctrl_remove, + .remove = sprd_pinctrl_remove, .shutdown = sprd_pinctrl_shutdown, }; module_platform_driver(sprd_pinctrl_driver); diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 7dbeb786352af..b7dbaf77b6db4 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -61,6 +61,17 @@ config CHROMEOS_TBMC To compile this driver as a module, choose M here: the module will be called chromeos_tbmc. +config CHROMEOS_OF_HW_PROBER + tristate "ChromeOS Device Tree Hardware Prober" + depends on OF + depends on I2C + select OF_DYNAMIC + default OF + help + This option enables the device tree hardware prober for ChromeOS + devices. The driver will probe the correct component variant in + devices that have multiple drop-in options for one component. + config CROS_EC tristate "ChromeOS Embedded Controller" select CROS_EC_PROTO diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 2dcc6ccc23022..fb8335458a221 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -6,6 +6,7 @@ CFLAGS_cros_ec_sensorhub_ring.o:= -I$(src) obj-$(CONFIG_CHROMEOS_ACPI) += chromeos_acpi.o obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o +obj-$(CONFIG_CHROMEOS_OF_HW_PROBER) += chromeos_of_hw_prober.o obj-$(CONFIG_CHROMEOS_PRIVACY_SCREEN) += chromeos_privacy_screen.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o diff --git a/drivers/platform/chrome/chromeos_of_hw_prober.c b/drivers/platform/chrome/chromeos_of_hw_prober.c new file mode 100644 index 0000000000000..297d4704b75fd --- /dev/null +++ b/drivers/platform/chrome/chromeos_of_hw_prober.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ChromeOS Device Tree Hardware Prober + * + * Copyright (c) 2024 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "chromeos_of_hw_prober" + +/** + * struct hw_prober_entry - Holds an entry for the hardware prober + * + * @compatible: compatible string to match against the machine + * @prober: prober function to call when machine matches + * @data: extra data for the prober function + */ +struct hw_prober_entry { + const char *compatible; + int (*prober)(struct device *dev, const void *data); + const void *data; +}; + +struct chromeos_i2c_probe_data { + const struct i2c_of_probe_cfg *cfg; + const struct i2c_of_probe_simple_opts *opts; +}; + +static int chromeos_i2c_component_prober(struct device *dev, const void *_data) +{ + const struct chromeos_i2c_probe_data *data = _data; + struct i2c_of_probe_simple_ctx ctx = { + .opts = data->opts, + }; + + return i2c_of_probe_component(dev, data->cfg, &ctx); +} + +#define DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(_type) \ + static const struct i2c_of_probe_cfg chromeos_i2c_probe_simple_ ## _type ## _cfg = { \ + .type = #_type, \ + .ops = &i2c_of_probe_simple_ops, \ + } + +#define DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(_type) \ + static const struct chromeos_i2c_probe_data chromeos_i2c_probe_dumb_ ## _type = { \ + .cfg = &(const struct i2c_of_probe_cfg) { \ + .type = #_type, \ + }, \ + } + +DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(touchscreen); + +DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(trackpad); + +static const struct chromeos_i2c_probe_data chromeos_i2c_probe_hana_trackpad = { + .cfg = &chromeos_i2c_probe_simple_trackpad_cfg, + .opts = &(const struct i2c_of_probe_simple_opts) { + .res_node_compatible = "elan,ekth3000", + .supply_name = "vcc", + /* + * ELAN trackpad needs 2 ms for H/W init and 100 ms for F/W init. + * Synaptics trackpad needs 100 ms. + * However, the regulator is set to "always-on", presumably to + * avoid this delay. The ELAN driver is also missing delays. + */ + .post_power_on_delay_ms = 0, + }, +}; + +static const struct hw_prober_entry hw_prober_platforms[] = { + { + .compatible = "google,hana", + .prober = chromeos_i2c_component_prober, + .data = &chromeos_i2c_probe_dumb_touchscreen, + }, { + .compatible = "google,hana", + .prober = chromeos_i2c_component_prober, + .data = &chromeos_i2c_probe_hana_trackpad, + }, +}; + +static int chromeos_of_hw_prober_probe(struct platform_device *pdev) +{ + for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) { + int ret; + + if (!of_machine_is_compatible(hw_prober_platforms[i].compatible)) + continue; + + ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data); + /* Ignore unrecoverable errors and keep going through other probers */ + if (ret == -EPROBE_DEFER) + return ret; + } + + return 0; +} + +static struct platform_driver chromeos_of_hw_prober_driver = { + .probe = chromeos_of_hw_prober_probe, + .driver = { + .name = DRV_NAME, + }, +}; + +static struct platform_device *chromeos_of_hw_prober_pdev; + +static int chromeos_of_hw_prober_driver_init(void) +{ + size_t i; + int ret; + + for (i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) + if (of_machine_is_compatible(hw_prober_platforms[i].compatible)) + break; + if (i == ARRAY_SIZE(hw_prober_platforms)) + return -ENODEV; + + ret = platform_driver_register(&chromeos_of_hw_prober_driver); + if (ret) + return ret; + + chromeos_of_hw_prober_pdev = + platform_device_register_simple(DRV_NAME, PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(chromeos_of_hw_prober_pdev)) + goto err; + + return 0; + +err: + platform_driver_unregister(&chromeos_of_hw_prober_driver); + + return PTR_ERR(chromeos_of_hw_prober_pdev); +} +module_init(chromeos_of_hw_prober_driver_init); + +static void chromeos_of_hw_prober_driver_exit(void) +{ + platform_device_unregister(chromeos_of_hw_prober_pdev); + platform_driver_unregister(&chromeos_of_hw_prober_driver); +} +module_exit(chromeos_of_hw_prober_driver_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS device tree hardware prober"); +MODULE_IMPORT_NS(I2C_OF_PROBER); diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index c2aab0cfab338..ca78e58331360 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -61,7 +61,6 @@ #include #include #include -#include #include #include "goldfish_pipe_qemu.h" @@ -940,7 +939,7 @@ static struct platform_driver goldfish_pipe_driver = { .driver = { .name = "goldfish_pipe", .of_match_table = goldfish_pipe_of_match, - .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match), + .acpi_match_table = goldfish_pipe_acpi_match, } }; diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 4aa4d66f491af..dd5b5773328a9 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -236,7 +236,7 @@ static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj } static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, - struct bin_attribute *battr, int id) + const struct bin_attribute *battr, int id) { if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) return battr->attr.mode; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index f8e74c0392ba0..748bbc3564847 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -75,7 +75,7 @@ static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj } static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, - struct bin_attribute *battr, int id) + const struct bin_attribute *battr, int id) { u16 sock_ind; diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 375695cc0d60c..3c53cab033277 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -105,7 +105,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, static int intel_pmt_mmap(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, struct vm_area_struct *vma) + const struct bin_attribute *attr, struct vm_area_struct *vma) { struct intel_pmt_entry *entry = container_of(attr, struct intel_pmt_entry, diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 9d137621f0e6e..33f33b1070fdc 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -541,7 +541,7 @@ static struct bin_attribute *sdsi_bin_attrs[] = { }; static umode_t -sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n) +sdsi_battr_is_visible(struct kobject *kobj, const struct bin_attribute *attr, int n) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index 725948044da4a..735df818f76bf 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -41,7 +41,7 @@ static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { /* Novatek NVT-ts touchscreen */ .board_info = { - .type = "NVT-ts", + .type = "nt11205-ts", .addr = 0x34, .dev_name = "NVT-ts", }, diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 389d5a193e5dc..f5fc33a8bf443 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -79,6 +79,7 @@ config POWER_RESET_EP93XX bool "Cirrus EP93XX reset driver" if COMPILE_TEST depends on MFD_SYSCON default ARCH_EP93XX + select AUXILIARY_BUS help This driver provides restart support for Cirrus EP93XX SoC. diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index 93eece0278652..7bc779055cf3f 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -223,7 +223,7 @@ MODULE_DEVICE_TABLE(of, at91_poweroff_of_match); static struct platform_driver at91_poweroff_driver = { .probe = at91_poweroff_probe, - .remove_new = at91_poweroff_remove, + .remove = at91_poweroff_remove, .driver = { .name = "at91-poweroff", .of_match_table = at91_poweroff_of_match, diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 16512654295f5..036b18a1f90f8 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -427,7 +427,7 @@ static void at91_reset_remove(struct platform_device *pdev) static struct platform_driver at91_reset_driver = { .probe = at91_reset_probe, - .remove_new = at91_reset_remove, + .remove = at91_reset_remove, .driver = { .name = "at91-reset", .of_match_table = at91_reset_of_match, diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 959ce0dbe91d1..edb0df86aff45 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -441,7 +441,7 @@ static void at91_shdwc_remove(struct platform_device *pdev) static struct platform_driver at91_shdwc_driver = { .probe = at91_shdwc_probe, - .remove_new = at91_shdwc_remove, + .remove = at91_shdwc_remove, .driver = { .name = "at91-shdwc", .of_match_table = at91_shdwc_of_match, diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index dbc4ff61cd748..cfaa54ced0d0a 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -16,7 +16,6 @@ #include #include -#define RSTYPE_RG 0x0 #define RSCTRL_RG 0x4 #define RSCFG_RG 0x8 #define RSISO_RG 0xc @@ -28,7 +27,6 @@ #define RSMUX_OMODE_MASK 0xe #define RSMUX_OMODE_RESET_ON 0xa #define RSMUX_OMODE_RESET_OFF 0x0 -#define RSMUX_LOCK_MASK 0x1 #define RSMUX_LOCK_SET 0x1 #define RSCFG_RSTYPE_SOFT 0x300f diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index fa25fbd539343..1a6fc8d38e206 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -305,7 +305,7 @@ MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match); static struct platform_driver ltc2952_poweroff_driver = { .probe = ltc2952_poweroff_probe, - .remove_new = ltc2952_poweroff_remove, + .remove = ltc2952_poweroff_remove, .driver = { .name = "ltc2952-poweroff", .of_match_table = of_ltc2952_poweroff_match, diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c index e0f2ff6b147c1..f7d8fc935d519 100644 --- a/drivers/power/reset/qnap-poweroff.c +++ b/drivers/power/reset/qnap-poweroff.c @@ -118,7 +118,7 @@ static void qnap_power_off_remove(struct platform_device *pdev) static struct platform_driver qnap_power_off_driver = { .probe = qnap_power_off_probe, - .remove_new = qnap_power_off_remove, + .remove = qnap_power_off_remove, .driver = { .name = "qnap_power_off", .of_match_table = of_match_ptr(qnap_power_off_of_match_table), diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c index 4d622c19bc486..d623d77e657e4 100644 --- a/drivers/power/reset/syscon-reboot.c +++ b/drivers/power/reset/syscon-reboot.c @@ -61,7 +61,8 @@ static int syscon_reboot_probe(struct platform_device *pdev) priority = 192; if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) - return -EINVAL; + if (of_property_read_u32(pdev->dev.of_node, "reg", &ctx->offset)) + return -EINVAL; value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value); mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask); diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c index 34619c4d4eced..b7938fbb24a51 100644 --- a/drivers/power/supply/88pm860x_battery.c +++ b/drivers/power/supply/88pm860x_battery.c @@ -422,7 +422,7 @@ static irqreturn_t pm860x_batt_handler(int irq, void *data) info->temp_type = PM860X_TEMP_TINT; } mutex_unlock(&info->lock); - /* clear ccnt since battery is attached or dettached */ + /* clear ccnt since battery is attached or detached */ clear_ccnt(info, &ccnt_data); return IRQ_HANDLED; } @@ -566,7 +566,7 @@ static int measure_temp(struct pm860x_battery_info *info, int *data) ret = measure_12bit_voltage(info, PM8607_GPADC1_MEAS1, data); if (ret) return ret; - /* meausered Vtbat(mV) / Ibias_current(11uA)*/ + /* measured Vtbat(mV) / Ibias_current(11uA)*/ *data = (*data * 1000) / GPBIAS2_GPADC1_UA; if (*data > TBAT_NEG_25D) { diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index bcfa63fb9f1e2..9f2eef6787f7a 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -493,6 +493,16 @@ config CHARGER_TWL4030 help Say Y here to enable support for TWL4030 Battery Charge Interface. +config CHARGER_TWL6030 + tristate "OMAP TWL6030 BCI charger driver" + depends on IIO && TWL4030_CORE + help + Say Y here to enable support for TWL6030/6032 Battery Charge + Interface. + + This driver can be build as a module. If so, the module will be + called twl6030_charger. + config CHARGER_LP8727 tristate "TI/National Semiconductor LP8727 charger driver" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 8dcb415453171..59c4a9f40d28a 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o +obj-$(CONFIG_CHARGER_TWL6030) += twl6030_charger.o obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index 3e6ea22372b2d..19ed528528048 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -16,7 +16,7 @@ /* Default: temperature hysteresis */ #define AB8500_TEMP_HYSTERESIS 3 -static struct power_supply_battery_ocv_table ocv_cap_tbl[] = { +static const struct power_supply_battery_ocv_table ocv_cap_tbl[] = { { .ocv = 4186000, .capacity = 100}, { .ocv = 4163000, .capacity = 99}, { .ocv = 4114000, .capacity = 95}, @@ -48,7 +48,7 @@ static struct power_supply_battery_ocv_table ocv_cap_tbl[] = { * temperature values to work. Factory resistance is 300 mOhm and the * resistance values to the right are percentages of 300 mOhm. */ -static struct power_supply_resistance_temp_table temp_to_batres_tbl_thermistor[] = { +static const struct power_supply_resistance_temp_table temp_to_batres_tbl_thermistor[] = { { .temp = 40, .resistance = 40 /* 120 mOhm */ }, { .temp = 30, .resistance = 45 /* 135 mOhm */ }, { .temp = 20, .resistance = 55 /* 165 mOhm */ }, diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 56f136b2d071d..37039e28fc4bd 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -283,7 +283,7 @@ static void ab8500_btemp_periodic_work(struct work_struct *work) dev_warn(di->dev, "failed to identify the battery\n"); } - /* Failover if a reading is erroneous, use last meausurement */ + /* Failover if a reading is erroneous, use last measurement */ ret = thermal_zone_get_temp(di->tz, &bat_temp); if (ret) { dev_err(di->dev, "error reading temperature\n"); @@ -818,7 +818,7 @@ MODULE_DEVICE_TABLE(of, ab8500_btemp_match); struct platform_driver ab8500_btemp_driver = { .probe = ab8500_btemp_probe, - .remove_new = ab8500_btemp_remove, + .remove = ab8500_btemp_remove, .driver = { .name = "ab8500-btemp", .of_match_table = ab8500_btemp_match, diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 854491ad3ecd5..14e1b448bd39b 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -1837,7 +1837,7 @@ static const struct of_device_id ab8500_chargalg_match[] = { struct platform_driver ab8500_chargalg_driver = { .probe = ab8500_chargalg_probe, - .remove_new = ab8500_chargalg_remove, + .remove = ab8500_chargalg_remove, .driver = { .name = "ab8500_chargalg", .of_match_table = ab8500_chargalg_match, diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 93181ebfb3247..cece8d6753ac1 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3711,7 +3711,7 @@ MODULE_DEVICE_TABLE(of, ab8500_charger_match); static struct platform_driver ab8500_charger_driver = { .probe = ab8500_charger_probe, - .remove_new = ab8500_charger_remove, + .remove = ab8500_charger_remove, .driver = { .name = "ab8500-charger", .of_match_table = ab8500_charger_match, diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index a71903b1bf781..78871a2143de3 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3242,7 +3242,7 @@ MODULE_DEVICE_TABLE(of, ab8500_fg_match); struct platform_driver ab8500_fg_driver = { .probe = ab8500_fg_probe, - .remove_new = ab8500_fg_remove, + .remove = ab8500_fg_remove, .driver = { .name = "ab8500-fg", .of_match_table = ab8500_fg_match, diff --git a/drivers/power/supply/acer_a500_battery.c b/drivers/power/supply/acer_a500_battery.c index ef5c419b1b7f2..39d85b11a13c2 100644 --- a/drivers/power/supply/acer_a500_battery.c +++ b/drivers/power/supply/acer_a500_battery.c @@ -233,14 +233,15 @@ static int a500_battery_probe(struct platform_device *pdev) psy_cfg.of_node = pdev->dev.parent->of_node; psy_cfg.drv_data = bat; + psy_cfg.no_wakeup_source = true; bat->regmap = dev_get_regmap(pdev->dev.parent, "KB930"); if (!bat->regmap) return -EINVAL; - bat->psy = devm_power_supply_register_no_ws(&pdev->dev, - &a500_battery_desc, - &psy_cfg); + bat->psy = devm_power_supply_register(&pdev->dev, + &a500_battery_desc, + &psy_cfg); if (IS_ERR(bat->psy)) return dev_err_probe(&pdev->dev, PTR_ERR(bat->psy), "failed to register battery\n"); @@ -285,7 +286,7 @@ static struct platform_driver a500_battery_driver = { .pm = &a500_battery_pm_ops, }, .probe = a500_battery_probe, - .remove_new = a500_battery_remove, + .remove = a500_battery_remove, }; module_platform_driver(a500_battery_driver); diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c index 51122bfbf196c..b2b82f97a4712 100644 --- a/drivers/power/supply/act8945a_charger.c +++ b/drivers/power/supply/act8945a_charger.c @@ -651,7 +651,7 @@ static struct platform_driver act8945a_charger_driver = { .name = "act8945a-charger", }, .probe = act8945a_charger_probe, - .remove_new = act8945a_charger_remove, + .remove = act8945a_charger_remove, }; module_platform_driver(act8945a_charger_driver); diff --git a/drivers/power/supply/adp5061.c b/drivers/power/supply/adp5061.c index dac9875d993c9..458fd3024373f 100644 --- a/drivers/power/supply/adp5061.c +++ b/drivers/power/supply/adp5061.c @@ -590,7 +590,7 @@ static int adp5061_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_AVG: /* * This property is used to set the VWEAK threshold - * bellow this value, weak charge mode is entered + * below this value, weak charge mode is entered * above this value, fast chargerge mode is entered */ return adp5061_get_vweak_th(st, val); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index f71cc90fea127..fa27195f074e7 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -354,17 +354,18 @@ static int axp20x_battery_get_prop(struct power_supply *psy, if (ret) return ret; + /* IIO framework gives mA but Power Supply framework gives uA */ if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { - ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval); + ret = iio_read_channel_processed_scale(axp20x_batt->batt_chrg_i, + &val->intval, 1000); } else { - ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, &val1); + ret = iio_read_channel_processed_scale(axp20x_batt->batt_dischrg_i, + &val1, 1000); val->intval = -val1; } if (ret) return ret; - /* IIO framework gives mA but Power Supply framework gives uA */ - val->intval *= 1000; break; case POWER_SUPPLY_PROP_CAPACITY: @@ -406,13 +407,12 @@ static int axp20x_battery_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = iio_read_channel_processed(axp20x_batt->batt_v, - &val->intval); + /* IIO framework gives mV but Power Supply framework gives uV */ + ret = iio_read_channel_processed_scale(axp20x_batt->batt_v, + &val->intval, 1000); if (ret) return ret; - /* IIO framework gives mV but Power Supply framework gives uV */ - val->intval *= 1000; break; default: @@ -519,13 +519,15 @@ static int axp717_battery_get_prop(struct power_supply *psy, * The offset of this value is currently unknown and is * not documented in the datasheet. Based on * observation it's assumed to be somewhere around - * 450ma. I will leave the value raw for now. + * 450ma. I will leave the value raw for now. Note that + * IIO framework gives mA but Power Supply framework + * gives uA. */ - ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval); + ret = iio_read_channel_processed_scale(axp20x_batt->batt_chrg_i, + &val->intval, 1000); if (ret) return ret; - /* IIO framework gives mA but Power Supply framework gives uA */ - val->intval *= 1000; + return 0; case POWER_SUPPLY_PROP_CAPACITY: @@ -564,13 +566,12 @@ static int axp717_battery_get_prop(struct power_supply *psy, return 0; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = iio_read_channel_processed(axp20x_batt->batt_v, - &val->intval); + /* IIO framework gives mV but Power Supply framework gives uV */ + ret = iio_read_channel_processed_scale(axp20x_batt->batt_v, + &val->intval, 1000); if (ret) return ret; - /* IIO framework gives mV but Power Supply framework gives uV */ - val->intval *= 1000; return 0; case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 2766352ab737d..9722912268fe8 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -220,16 +220,15 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, return 0; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (IS_ENABLED(CONFIG_AXP20X_ADC)) { - ret = iio_read_channel_processed(power->vbus_v, - &val->intval); - if (ret) - return ret; - /* * IIO framework gives mV but Power Supply framework * gives uV. */ - val->intval *= 1000; + ret = iio_read_channel_processed_scale(power->vbus_v, + &val->intval, 1000); + if (ret) + return ret; + return 0; } @@ -253,16 +252,15 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, return 0; case POWER_SUPPLY_PROP_CURRENT_NOW: if (IS_ENABLED(CONFIG_AXP20X_ADC)) { - ret = iio_read_channel_processed(power->vbus_i, - &val->intval); - if (ret) - return ret; - /* * IIO framework gives mA but Power Supply framework * gives uA. */ - val->intval *= 1000; + ret = iio_read_channel_processed_scale(power->vbus_i, + &val->intval, 1000); + if (ret) + return ret; + return 0; } @@ -374,16 +372,15 @@ static int axp717_usb_power_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (IS_ENABLED(CONFIG_AXP20X_ADC)) { - ret = iio_read_channel_processed(power->vbus_v, - &val->intval); - if (ret) - return ret; - /* * IIO framework gives mV but Power Supply framework * gives uV. */ - val->intval *= 1000; + ret = iio_read_channel_processed_scale(power->vbus_v, + &val->intval, 1000); + if (ret) + return ret; + return 0; } diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 750fda543308c..40c5ac7a11188 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -449,9 +449,29 @@ static u8 [BQ27XXX_REG_AP] = 0x18, BQ27XXX_DM_REG_ROWS, }, + bq27426_regs[BQ27XXX_REG_MAX] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x02, + [BQ27XXX_REG_INT_TEMP] = 0x1e, + [BQ27XXX_REG_VOLT] = 0x04, + [BQ27XXX_REG_AI] = 0x10, + [BQ27XXX_REG_FLAGS] = 0x06, + [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = 0x08, + [BQ27XXX_REG_RC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x0e, + [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x1c, + [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, + [BQ27XXX_REG_AP] = 0x18, + BQ27XXX_DM_REG_ROWS, + }, #define bq27411_regs bq27421_regs #define bq27425_regs bq27421_regs -#define bq27426_regs bq27421_regs #define bq27441_regs bq27421_regs #define bq27621_regs bq27421_regs bq27z561_regs[BQ27XXX_REG_MAX] = { @@ -769,10 +789,23 @@ static enum power_supply_property bq27421_props[] = { }; #define bq27411_props bq27421_props #define bq27425_props bq27421_props -#define bq27426_props bq27421_props #define bq27441_props bq27421_props #define bq27621_props bq27421_props +static enum power_supply_property bq27426_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + static enum power_supply_property bq27z561_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, @@ -2131,6 +2164,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) struct power_supply_config psy_cfg = { .of_node = di->dev->of_node, .drv_data = di, + .no_wakeup_source = true, }; int ret; @@ -2157,7 +2191,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) psy_desc->get_property = bq27xxx_battery_get_property; psy_desc->external_power_changed = bq27xxx_external_power_changed; - di->bat = devm_power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); + di->bat = devm_power_supply_register(di->dev, psy_desc, &psy_cfg); if (IS_ERR(di->bat)) return dev_err_probe(di->dev, PTR_ERR(di->bat), "failed to register battery\n"); diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 09ec0ecf14863..a69faef444c04 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -221,7 +221,7 @@ static bool is_charging(struct charger_manager *cm) /* If at least one of the charger is charging, return yes */ for (i = 0; cm->desc->psy_charger_stat[i]; i++) { - /* 1. The charger sholuld not be DISABLED */ + /* 1. The charger should not be DISABLED */ if (cm->emergency_stop) continue; if (!cm->charger_enabled) @@ -1739,7 +1739,7 @@ static struct platform_driver charger_manager_driver = { .of_match_table = charger_manager_match, }, .probe = charger_manager_probe, - .remove_new = charger_manager_remove, + .remove = charger_manager_remove, .id_table = charger_manager_id, }; diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 30ec76cdf34b0..813037c00ded5 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1169,7 +1169,7 @@ static struct platform_driver cpcap_battery_driver = { .of_match_table = of_match_ptr(cpcap_battery_id_table), }, .probe = cpcap_battery_probe, - .remove_new = cpcap_battery_remove, + .remove = cpcap_battery_remove, }; module_platform_driver(cpcap_battery_driver); diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 91e7292d86bb7..7781b45a67a7d 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -969,7 +969,7 @@ static struct platform_driver cpcap_charger_driver = { .of_match_table = cpcap_charger_id_table, }, .shutdown = cpcap_charger_shutdown, - .remove_new = cpcap_charger_remove, + .remove = cpcap_charger_remove, }; module_platform_driver(cpcap_charger_driver); diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c index bed3e2e9bfea9..47d3f58aa15ce 100644 --- a/drivers/power/supply/cros_usbpd-charger.c +++ b/drivers/power/supply/cros_usbpd-charger.c @@ -618,6 +618,7 @@ static int cros_usbpd_charger_probe(struct platform_device *pd) psy_desc->external_power_changed = cros_usbpd_charger_power_changed; psy_cfg.drv_data = port; + psy_cfg.no_wakeup_source = true; if (cros_usbpd_charger_port_is_dedicated(port)) { sprintf(port->name, CHARGER_DEDICATED_DIR_NAME); @@ -644,8 +645,7 @@ static int cros_usbpd_charger_probe(struct platform_device *pd) psy_desc->name = port->name; - psy = devm_power_supply_register_no_ws(dev, psy_desc, - &psy_cfg); + psy = devm_power_supply_register(dev, psy_desc, &psy_cfg); if (IS_ERR(psy)) { dev_err(dev, "Failed to register power supply\n"); continue; diff --git a/drivers/power/supply/da9030_battery.c b/drivers/power/supply/da9030_battery.c index 04e0f4162d42b..34328f5d556e2 100644 --- a/drivers/power/supply/da9030_battery.c +++ b/drivers/power/supply/da9030_battery.c @@ -269,7 +269,7 @@ static void da9030_charger_check_state(struct da9030_charger *charger) } if (charger->adc.vchmax_res > charger->thresholds.vcharge_max || charger->adc.vchmin_res < charger->thresholds.vcharge_min || - /* Tempreture readings are negative */ + /* Temperature readings are negative */ charger->adc.tbat_res < charger->thresholds.tbat_high || charger->adc.tbat_res > charger->thresholds.tbat_low) { /* disable charger */ @@ -470,7 +470,7 @@ static int da9030_battery_charger_init(struct da9030_charger *charger) if (ret) return ret; - /* enable auto ADC measuremnts */ + /* enable auto ADC measurements */ return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL, DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON | DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE | @@ -571,7 +571,7 @@ static struct platform_driver da903x_battery_driver = { .name = "da903x-battery", }, .probe = da9030_battery_probe, - .remove_new = da9030_battery_remove, + .remove = da9030_battery_remove, }; module_platform_driver(da903x_battery_driver); diff --git a/drivers/power/supply/da9052-battery.c b/drivers/power/supply/da9052-battery.c index 0d84c42c624e0..6f17e2fa1a28e 100644 --- a/drivers/power/supply/da9052-battery.c +++ b/drivers/power/supply/da9052-battery.c @@ -648,7 +648,7 @@ static void da9052_bat_remove(struct platform_device *pdev) static struct platform_driver da9052_bat_driver = { .probe = da9052_bat_probe, - .remove_new = da9052_bat_remove, + .remove = da9052_bat_remove, .driver = { .name = "da9052-bat", }, diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index b13cecd84f589..27f36ef5b88dc 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -636,7 +636,7 @@ static struct platform_driver da9150_charger_driver = { .name = "da9150-charger", }, .probe = da9150_charger_probe, - .remove_new = da9150_charger_remove, + .remove = da9150_charger_remove, }; module_platform_driver(da9150_charger_driver); diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index 7bdc6b2636090..d5d215f5ad8b1 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Generic battery driver using IIO - * Copyright (C) 2012, Anish Kumar + * Copyright (C) 2012, Anish Kumar * Copyright (c) 2023, Sebastian Reichel */ #include @@ -295,6 +295,6 @@ static struct platform_driver gab_driver = { }; module_platform_driver(gab_driver); -MODULE_AUTHOR("anish kumar "); +MODULE_AUTHOR("anish kumar "); MODULE_DESCRIPTION("generic battery driver using IIO"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/ipaq_micro_battery.c b/drivers/power/supply/ipaq_micro_battery.c index 66cc649f702af..7e0568a5353f1 100644 --- a/drivers/power/supply/ipaq_micro_battery.c +++ b/drivers/power/supply/ipaq_micro_battery.c @@ -302,7 +302,7 @@ static struct platform_driver micro_batt_device_driver = { .pm = µ_batt_dev_pm_ops, }, .probe = micro_batt_probe, - .remove_new = micro_batt_remove, + .remove = micro_batt_remove, }; module_platform_driver(micro_batt_device_driver); diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c index 860d8614c98fd..237912a922724 100644 --- a/drivers/power/supply/isp1704_charger.c +++ b/drivers/power/supply/isp1704_charger.c @@ -501,7 +501,7 @@ static struct platform_driver isp1704_charger_driver = { .of_match_table = of_match_ptr(omap_isp1704_of_match), }, .probe = isp1704_charger_probe, - .remove_new = isp1704_charger_remove, + .remove = isp1704_charger_remove, }; module_platform_driver(isp1704_charger_driver); diff --git a/drivers/power/supply/lenovo_yoga_c630_battery.c b/drivers/power/supply/lenovo_yoga_c630_battery.c index f98f65e00831a..7a6c8af9e8c24 100644 --- a/drivers/power/supply/lenovo_yoga_c630_battery.c +++ b/drivers/power/supply/lenovo_yoga_c630_battery.c @@ -368,11 +368,12 @@ static int yoga_c630_psy_register_bat_psy(struct yoga_c630_psy *ecbat) bat_cfg.drv_data = ecbat; bat_cfg.fwnode = ecbat->fwnode; - ecbat->bat_psy = power_supply_register_no_ws(ecbat->dev, - ecbat->unit_mA ? - &yoga_c630_psy_bat_psy_desc_mA : - &yoga_c630_psy_bat_psy_desc_mWh, - &bat_cfg); + bat_cfg.no_wakeup_source = true; + ecbat->bat_psy = power_supply_register(ecbat->dev, + ecbat->unit_mA ? + &yoga_c630_psy_bat_psy_desc_mA : + &yoga_c630_psy_bat_psy_desc_mWh, + &bat_cfg); if (IS_ERR(ecbat->bat_psy)) { dev_err(ecbat->dev, "failed to register battery supply\n"); return PTR_ERR(ecbat->bat_psy); @@ -442,7 +443,8 @@ static int yoga_c630_psy_probe(struct auxiliary_device *adev, adp_cfg.fwnode = ecbat->fwnode; adp_cfg.supplied_to = (char **)&yoga_c630_psy_bat_psy_desc_mA.name; adp_cfg.num_supplicants = 1; - ecbat->adp_psy = devm_power_supply_register_no_ws(dev, &yoga_c630_psy_adpt_psy_desc, &adp_cfg); + adp_cfg.no_wakeup_source = true; + ecbat->adp_psy = devm_power_supply_register(dev, &yoga_c630_psy_adpt_psy_desc, &adp_cfg); if (IS_ERR(ecbat->adp_psy)) { dev_err(dev, "failed to register AC adapter supply\n"); return PTR_ERR(ecbat->adp_psy); diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 72b170b4ac46a..f0a680c155c48 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -716,7 +716,7 @@ static void lp8788_charger_remove(struct platform_device *pdev) static struct platform_driver lp8788_charger_driver = { .probe = lp8788_charger_probe, - .remove_new = lp8788_charger_remove, + .remove = lp8788_charger_remove, .driver = { .name = LP8788_DEV_CHARGER, }, diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c index b28c04157709a..1cef2f860b5fc 100644 --- a/drivers/power/supply/max14577_charger.c +++ b/drivers/power/supply/max14577_charger.c @@ -634,7 +634,7 @@ static struct platform_driver max14577_charger_driver = { .of_match_table = of_max14577_charger_dt_match, }, .probe = max14577_charger_probe, - .remove_new = max14577_charger_remove, + .remove = max14577_charger_remove, .id_table = max14577_charger_id, }; module_platform_driver(max14577_charger_driver); diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c index 818e13c613e35..5f58c0c24b4d6 100644 --- a/drivers/power/supply/max77650-charger.c +++ b/drivers/power/supply/max77650-charger.c @@ -364,7 +364,7 @@ static struct platform_driver max77650_charger_driver = { .of_match_table = max77650_charger_of_match, }, .probe = max77650_charger_probe, - .remove_new = max77650_charger_remove, + .remove = max77650_charger_remove, }; module_platform_driver(max77650_charger_driver); diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c index 4caac142c4285..cdea35c0d1de1 100644 --- a/drivers/power/supply/max77693_charger.c +++ b/drivers/power/supply/max77693_charger.c @@ -798,7 +798,7 @@ static struct platform_driver max77693_charger_driver = { .name = "max77693-charger", }, .probe = max77693_charger_probe, - .remove_new = max77693_charger_remove, + .remove = max77693_charger_remove, .id_table = max77693_charger_id, }; module_platform_driver(max77693_charger_driver); diff --git a/drivers/power/supply/max77976_charger.c b/drivers/power/supply/max77976_charger.c index d7e520da76886..e6fe68cebc32b 100644 --- a/drivers/power/supply/max77976_charger.c +++ b/drivers/power/supply/max77976_charger.c @@ -452,6 +452,7 @@ static int max77976_probe(struct i2c_client *client) i2c_set_clientdata(client, chg); psy_cfg.drv_data = chg; + psy_cfg.no_wakeup_source = true; chg->client = client; chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config); @@ -475,7 +476,7 @@ static int max77976_probe(struct i2c_client *client) if (err) return err; - psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg); + psy = devm_power_supply_register(dev, &max77976_psy_desc, &psy_cfg); if (IS_ERR(psy)) return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n"); diff --git a/drivers/power/supply/max8925_power.c b/drivers/power/supply/max8925_power.c index 621a006d52a96..d753145de3bb3 100644 --- a/drivers/power/supply/max8925_power.c +++ b/drivers/power/supply/max8925_power.c @@ -73,7 +73,7 @@ struct max8925_power_info { unsigned usb_online:1; unsigned bat_online:1; unsigned chg_mode:2; - unsigned batt_detect:1; /* detecing MB by ID pin */ + unsigned batt_detect:1; /* detecting MB by ID pin */ unsigned topoff_threshold:2; unsigned fast_charge:3; unsigned no_temp_support:1; @@ -563,7 +563,7 @@ static void max8925_power_remove(struct platform_device *pdev) static struct platform_driver max8925_power_driver = { .probe = max8925_power_probe, - .remove_new = max8925_power_remove, + .remove = max8925_power_remove, .driver = { .name = "max8925-power", }, diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c index 0e980522fee51..0136bc87b105e 100644 --- a/drivers/power/supply/pcf50633-charger.c +++ b/drivers/power/supply/pcf50633-charger.c @@ -455,7 +455,7 @@ static struct platform_driver pcf50633_mbc_driver = { .name = "pcf50633-mbc", }, .probe = pcf50633_mbc_probe, - .remove_new = pcf50633_mbc_remove, + .remove = pcf50633_mbc_remove, }; module_platform_driver(pcf50633_mbc_driver); diff --git a/drivers/power/supply/pmu_battery.c b/drivers/power/supply/pmu_battery.c index eaab7500d99b5..ed83c5e05ca32 100644 --- a/drivers/power/supply/pmu_battery.c +++ b/drivers/power/supply/pmu_battery.c @@ -170,6 +170,7 @@ static int __init pmu_bat_init(void) pbat->bat_desc.properties = pmu_bat_props; pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props); pbat->bat_desc.get_property = pmu_bat_get_property; + pbat->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; pbat->pbi = &pmu_batteries[i]; psy_cfg.drv_data = pbat; diff --git a/drivers/power/supply/power_supply.h b/drivers/power/supply/power_supply.h index 3cbafc58bdad0..7434a6f247750 100644 --- a/drivers/power/supply/power_supply.h +++ b/drivers/power/supply/power_supply.h @@ -13,9 +13,12 @@ struct device; struct device_type; struct power_supply; +extern int power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp); + #ifdef CONFIG_SYSFS -extern void power_supply_init_attrs(void); +extern void __init power_supply_init_attrs(void); extern int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env); extern const struct attribute_group *power_supply_attr_groups[]; @@ -41,3 +44,20 @@ static inline int power_supply_create_triggers(struct power_supply *psy) static inline void power_supply_remove_triggers(struct power_supply *psy) {} #endif /* CONFIG_LEDS_TRIGGERS */ + +#ifdef CONFIG_POWER_SUPPLY_HWMON + +int power_supply_add_hwmon_sysfs(struct power_supply *psy); +void power_supply_remove_hwmon_sysfs(struct power_supply *psy); + +#else + +static inline int power_supply_add_hwmon_sysfs(struct power_supply *psy) +{ + return 0; +} + +static inline +void power_supply_remove_hwmon_sysfs(struct power_supply *psy) {} + +#endif /* CONFIG_POWER_SUPPLY_HWMON */ diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 49534458a9f7d..16085eff00844 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -152,7 +152,7 @@ static void power_supply_deferred_register_work(struct work_struct *work) deferred_register_work.work); if (psy->dev.parent) { - while (!mutex_trylock(&psy->dev.parent->mutex)) { + while (!device_trylock(psy->dev.parent)) { if (psy->removing) return; msleep(10); @@ -162,7 +162,7 @@ static void power_supply_deferred_register_work(struct work_struct *work) power_supply_changed(psy); if (psy->dev.parent) - mutex_unlock(&psy->dev.parent->mutex); + device_unlock(psy->dev.parent); } #ifdef CONFIG_OF @@ -484,8 +484,6 @@ EXPORT_SYMBOL_GPL(power_supply_get_by_name); */ void power_supply_put(struct power_supply *psy) { - might_sleep(); - atomic_dec(&psy->use_cnt); put_device(&psy->dev); } @@ -777,7 +775,7 @@ int power_supply_get_battery_info(struct power_supply *psy, tab_len = size / (2 * sizeof(__be32)); info->ocv_table_size[index] = tab_len; - table = info->ocv_table[index] = + info->ocv_table[index] = table = devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL); if (!info->ocv_table[index]) { power_supply_put_battery_info(psy, info); @@ -798,7 +796,7 @@ int power_supply_get_battery_info(struct power_supply *psy, goto out_ret_pointer; info->resist_table_size = len / (2 * sizeof(__be32)); - resist_table = info->resist_table = devm_kcalloc(&psy->dev, + info->resist_table = resist_table = devm_kcalloc(&psy->dev, info->resist_table_size, sizeof(*resist_table), GFP_KERNEL); @@ -982,7 +980,7 @@ EXPORT_SYMBOL_GPL(power_supply_battery_info_get_prop); * * Return: the battery internal resistance percent */ -int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table, +int power_supply_temp2resist_simple(const struct power_supply_resistance_temp_table *table, int table_len, int temp) { int i, high, low; @@ -1093,7 +1091,7 @@ EXPORT_SYMBOL_GPL(power_supply_get_maintenance_charging_setting); * * Return: the battery capacity. */ -int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table, +int power_supply_ocv2cap_simple(const struct power_supply_battery_ocv_table *table, int table_len, int ocv) { int i, high, low; @@ -1118,7 +1116,7 @@ int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table, } EXPORT_SYMBOL_GPL(power_supply_ocv2cap_simple); -struct power_supply_battery_ocv_table * +const struct power_supply_battery_ocv_table * power_supply_find_ocv2cap_table(struct power_supply_battery_info *info, int temp, int *table_len) { @@ -1149,7 +1147,7 @@ EXPORT_SYMBOL_GPL(power_supply_find_ocv2cap_table); int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info, int ocv, int temp) { - struct power_supply_battery_ocv_table *table; + const struct power_supply_battery_ocv_table *table; int table_len; table = power_supply_find_ocv2cap_table(info, temp, &table_len); @@ -1233,7 +1231,6 @@ int power_supply_property_is_writeable(struct power_supply *psy, { return psy->desc->property_is_writeable && psy->desc->property_is_writeable(psy, psp); } -EXPORT_SYMBOL_GPL(power_supply_property_is_writeable); void power_supply_external_power_changed(struct power_supply *psy) { @@ -1342,8 +1339,7 @@ static void psy_unregister_thermal(struct power_supply *psy) static struct power_supply *__must_check __power_supply_register(struct device *parent, const struct power_supply_desc *desc, - const struct power_supply_config *cfg, - bool ws) + const struct power_supply_config *cfg) { struct device *dev; struct power_supply *psy; @@ -1410,7 +1406,7 @@ __power_supply_register(struct device *parent, if (rc) goto device_add_failed; - rc = device_init_wakeup(dev, ws); + rc = device_init_wakeup(dev, cfg ? !cfg->no_wakeup_source : true); if (rc) goto wakeup_init_failed; @@ -1476,33 +1472,10 @@ struct power_supply *__must_check power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg) { - return __power_supply_register(parent, desc, cfg, true); + return __power_supply_register(parent, desc, cfg); } EXPORT_SYMBOL_GPL(power_supply_register); -/** - * power_supply_register_no_ws() - Register new non-waking-source power supply - * @parent: Device to be a parent of power supply's device, usually - * the device which probe function calls this - * @desc: Description of power supply, must be valid through whole - * lifetime of this power supply - * @cfg: Run-time specific configuration accessed during registering, - * may be NULL - * - * Return: A pointer to newly allocated power_supply on success - * or ERR_PTR otherwise. - * Use power_supply_unregister() on returned power_supply pointer to release - * resources. - */ -struct power_supply *__must_check -power_supply_register_no_ws(struct device *parent, - const struct power_supply_desc *desc, - const struct power_supply_config *cfg) -{ - return __power_supply_register(parent, desc, cfg, false); -} -EXPORT_SYMBOL_GPL(power_supply_register_no_ws); - static void devm_power_supply_release(struct device *dev, void *res) { struct power_supply **psy = res; @@ -1535,7 +1508,7 @@ devm_power_supply_register(struct device *parent, if (!ptr) return ERR_PTR(-ENOMEM); - psy = __power_supply_register(parent, desc, cfg, true); + psy = __power_supply_register(parent, desc, cfg); if (IS_ERR(psy)) { devres_free(ptr); } else { @@ -1546,42 +1519,6 @@ devm_power_supply_register(struct device *parent, } EXPORT_SYMBOL_GPL(devm_power_supply_register); -/** - * devm_power_supply_register_no_ws() - Register managed non-waking-source power supply - * @parent: Device to be a parent of power supply's device, usually - * the device which probe function calls this - * @desc: Description of power supply, must be valid through whole - * lifetime of this power supply - * @cfg: Run-time specific configuration accessed during registering, - * may be NULL - * - * Return: A pointer to newly allocated power_supply on success - * or ERR_PTR otherwise. - * The returned power_supply pointer will be automatically unregistered - * on driver detach. - */ -struct power_supply *__must_check -devm_power_supply_register_no_ws(struct device *parent, - const struct power_supply_desc *desc, - const struct power_supply_config *cfg) -{ - struct power_supply **ptr, *psy; - - ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); - - if (!ptr) - return ERR_PTR(-ENOMEM); - psy = __power_supply_register(parent, desc, cfg, false); - if (IS_ERR(psy)) { - devres_free(ptr); - } else { - *ptr = psy; - devres_add(parent, ptr); - } - return psy; -} -EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws); - /** * power_supply_unregister() - Remove this power supply from system * @psy: Pointer to power supply to unregister diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index 6fbbfb1c685e6..01be04903d7d2 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -7,6 +7,7 @@ #include #include #include +#include "power_supply.h" struct power_supply_hwmon { struct power_supply *psy; diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 16b3c5880cd8c..571de43fcca98 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -142,7 +142,7 @@ static const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = { [POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge", }; -static struct power_supply_attr power_supply_attrs[] = { +static struct power_supply_attr power_supply_attrs[] __ro_after_init = { /* Properties of type `int' */ POWER_SUPPLY_ENUM_ATTR(STATUS), POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE), @@ -225,9 +225,9 @@ static struct power_supply_attr power_supply_attrs[] = { #define POWER_SUPPLY_ATTR_CNT ARRAY_SIZE(power_supply_attrs) static struct attribute * -__power_supply_attrs[POWER_SUPPLY_ATTR_CNT + 1]; +__power_supply_attrs[POWER_SUPPLY_ATTR_CNT + 1] __ro_after_init; -static struct power_supply_attr *to_ps_attr(struct device_attribute *attr) +static const struct power_supply_attr *to_ps_attr(struct device_attribute *attr) { return container_of(attr, struct power_supply_attr, dev_attr); } @@ -273,7 +273,7 @@ static ssize_t power_supply_show_property(struct device *dev, char *buf) { ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); - struct power_supply_attr *ps_attr = to_ps_attr(attr); + const struct power_supply_attr *ps_attr = to_ps_attr(attr); enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; @@ -326,7 +326,7 @@ static ssize_t power_supply_store_property(struct device *dev, const char *buf, size_t count) { ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); - struct power_supply_attr *ps_attr = to_ps_attr(attr); + const struct power_supply_attr *ps_attr = to_ps_attr(attr); enum power_supply_property psp = dev_attr_psp(attr); union power_supply_propval value; @@ -401,7 +401,7 @@ const struct attribute_group *power_supply_attr_groups[] = { NULL }; -void power_supply_init_attrs(void) +void __init power_supply_init_attrs(void) { int i; diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c index f0a64c00ddaae..47d29271ddf40 100644 --- a/drivers/power/supply/qcom_battmgr.c +++ b/drivers/power/supply/qcom_battmgr.c @@ -151,7 +151,7 @@ struct qcom_battmgr_message { __le32 capacity_low; __le32 capacity_warning; __le32 cycle_count; - /* thousandth of persent */ + /* thousandth of percent */ __le32 accuracy; __le32 max_sample_time_ms; __le32 min_sample_time_ms; diff --git a/drivers/power/supply/qcom_pmi8998_charger.c b/drivers/power/supply/qcom_pmi8998_charger.c index 81acbd8b2169c..3b4132376649e 100644 --- a/drivers/power/supply/qcom_pmi8998_charger.c +++ b/drivers/power/supply/qcom_pmi8998_charger.c @@ -832,7 +832,7 @@ static const struct smb2_register smb2_init_seq[] = { AUTO_RECHG_BIT | EN_ANALOG_DROP_IN_VBATT_BIT | CHARGER_INHIBIT_BIT, .val = CHARGER_INHIBIT_BIT }, - /* STAT pin software override, match downstream. Parallell charging? */ + /* STAT pin software override, match downstream. Parallel charging? */ { .addr = STAT_CFG, .mask = STAT_SW_OVERRIDE_CFG_BIT, .val = STAT_SW_OVERRIDE_CFG_BIT }, diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index 4e57762e27bad..a79563f6ff7a0 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -1017,10 +1017,10 @@ static const struct of_device_id smbb_charger_id_table[] = { MODULE_DEVICE_TABLE(of, smbb_charger_id_table); static struct platform_driver smbb_charger_driver = { - .probe = smbb_charger_probe, - .remove_new = smbb_charger_remove, - .driver = { - .name = "qcom-smbb", + .probe = smbb_charger_probe, + .remove = smbb_charger_remove, + .driver = { + .name = "qcom-smbb", .of_match_table = smbb_charger_id_table, }, }; diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index 57b6ddefad28d..e5f35d083c23d 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -240,9 +240,32 @@ static int rk817_record_battery_nvram_values(struct rk817_charger *charger) static int rk817_bat_calib_cap(struct rk817_charger *charger) { struct rk808 *rk808 = charger->rk808; - int tmp, charge_now, charge_now_adc, volt_avg; + int charge_now, charge_now_adc; u8 bulk_reg[4]; + /* Don't do anything if there's no battery. */ + if (!charger->battery_present) + return 0; + + /* + * When resuming from suspend, sometimes the voltage value would be + * incorrect. BSP would simply wait two seconds and try reading the + * values again. Do not do any sort of calibration activity when the + * reported value is incorrect. The next scheduled update of battery + * vaules should then return valid data and the driver can continue. + * Use 2.7v as the sanity value because per the datasheet the PMIC + * can in no way support a battery voltage lower than this. BSP only + * checked for values too low, but I'm adding in a check for values + * too high just in case; again the PMIC can in no way support + * voltages above 4.45v, so this seems like a good value. + */ + if ((charger->volt_avg_uv < 2700000) || (charger->volt_avg_uv > 4450000)) { + dev_dbg(charger->dev, + "Battery voltage of %d is invalid, ignoring.\n", + charger->volt_avg_uv); + return -EINVAL; + } + /* Calibrate the soc and fcc on a fully charged battery */ if (charger->charge_status == CHARGE_FINISH && (!charger->soc_cal)) { @@ -304,51 +327,6 @@ static int rk817_bat_calib_cap(struct rk817_charger *charger) } } - /* - * Calibrate the fully charged capacity when we previously had a full - * battery (soc_cal = 1) and are now empty (at or below minimum design - * voltage). If our columb counter is still positive, subtract that - * from our fcc value to get a calibrated fcc, and if our columb - * counter is negative add that to our fcc (but not to exceed our - * design capacity). - */ - regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H, - bulk_reg, 2); - tmp = get_unaligned_be16(bulk_reg); - volt_avg = (charger->voltage_k * tmp) + 1000 * charger->voltage_b; - if (volt_avg <= charger->bat_voltage_min_design_uv && - charger->soc_cal) { - regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, - bulk_reg, 4); - charge_now_adc = get_unaligned_be32(bulk_reg); - charge_now = ADC_TO_CHARGE_UAH(charge_now_adc, - charger->res_div); - /* - * Note, if charge_now is negative this will add it (what we - * want) and if it's positive this will subtract (also what - * we want). - */ - charger->fcc_mah = charger->fcc_mah - (charge_now / 1000); - - dev_dbg(charger->dev, - "Recalibrating full charge capacity to %d uah\n", - charger->fcc_mah * 1000); - } - - /* - * Set the SOC to 0 if we are below the minimum system voltage. - */ - if (volt_avg <= charger->bat_voltage_min_design_uv) { - charger->soc = 0; - charge_now_adc = CHARGE_TO_ADC(0, charger->res_div); - put_unaligned_be32(charge_now_adc, bulk_reg); - regmap_bulk_write(rk808->regmap, - RK817_GAS_GAUGE_Q_INIT_H3, bulk_reg, 4); - dev_warn(charger->dev, - "Battery voltage %d below minimum voltage %d\n", - volt_avg, charger->bat_voltage_min_design_uv); - } - rk817_record_battery_nvram_values(charger); return 0; @@ -648,6 +626,24 @@ static irqreturn_t rk817_plug_out_isr(int irq, void *cg) return IRQ_HANDLED; } +static int rk817_bat_set_prop(struct power_supply *ps, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct rk817_charger *charger = power_supply_get_drvdata(ps); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + if ((val->intval < 500000) || + (val->intval > charger->bat_charge_full_design_uah)) + return -EINVAL; + charger->fcc_mah = val->intval / 1000; + return rk817_bat_calib_cap(charger); + default: + return -EINVAL; + } +} + static enum power_supply_property rk817_bat_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_STATUS, @@ -673,12 +669,25 @@ static enum power_supply_property rk817_chg_props[] = { POWER_SUPPLY_PROP_VOLTAGE_AVG, }; +static int rk817_bat_prop_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + return 1; + default: + return 0; + } +} + static const struct power_supply_desc rk817_bat_desc = { .name = "rk817-battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = rk817_bat_props, + .property_is_writeable = rk817_bat_prop_writeable, .num_properties = ARRAY_SIZE(rk817_bat_props), .get_property = rk817_bat_get_prop, + .set_property = rk817_bat_set_prop, }; static const struct power_supply_desc rk817_chg_desc = { @@ -1202,6 +1211,15 @@ static int rk817_charger_probe(struct platform_device *pdev) return 0; } +static int __maybe_unused rk817_suspend(struct device *dev) +{ + struct rk817_charger *charger = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&charger->work); + + return 0; +} + static int __maybe_unused rk817_resume(struct device *dev) { @@ -1213,7 +1231,7 @@ static int __maybe_unused rk817_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rk817_charger_pm, NULL, rk817_resume); +static SIMPLE_DEV_PM_OPS(rk817_charger_pm, rk817_suspend, rk817_resume); static struct platform_driver rk817_charger_driver = { .probe = rk817_charger_probe, diff --git a/drivers/power/supply/rt9471.c b/drivers/power/supply/rt9471.c index c04af1ee89c67..67b86ac91a21d 100644 --- a/drivers/power/supply/rt9471.c +++ b/drivers/power/supply/rt9471.c @@ -139,6 +139,19 @@ enum { RT9471_PORTSTAT_DCP, }; +enum { + RT9471_ICSTAT_SLEEP = 0, + RT9471_ICSTAT_VBUSRDY, + RT9471_ICSTAT_TRICKLECHG, + RT9471_ICSTAT_PRECHG, + RT9471_ICSTAT_FASTCHG, + RT9471_ICSTAT_IEOC, + RT9471_ICSTAT_BGCHG, + RT9471_ICSTAT_CHGDONE, + RT9471_ICSTAT_CHGFAULT, + RT9471_ICSTAT_OTG = 15, +}; + struct rt9471_chip { struct device *dev; struct regmap *regmap; @@ -153,8 +166,8 @@ struct rt9471_chip { }; static const struct reg_field rt9471_reg_fields[F_MAX_FIELDS] = { - [F_WDT] = REG_FIELD(RT9471_REG_TOP, 0, 0), - [F_WDT_RST] = REG_FIELD(RT9471_REG_TOP, 1, 1), + [F_WDT] = REG_FIELD(RT9471_REG_TOP, 0, 1), + [F_WDT_RST] = REG_FIELD(RT9471_REG_TOP, 2, 2), [F_CHG_EN] = REG_FIELD(RT9471_REG_FUNC, 0, 0), [F_HZ] = REG_FIELD(RT9471_REG_FUNC, 5, 5), [F_BATFET_DIS] = REG_FIELD(RT9471_REG_FUNC, 7, 7), @@ -255,31 +268,32 @@ static int rt9471_get_ieoc(struct rt9471_chip *chip, int *microamp) static int rt9471_get_status(struct rt9471_chip *chip, int *status) { - unsigned int chg_ready, chg_done, fault_stat; + unsigned int ic_stat; int ret; - ret = regmap_field_read(chip->rm_fields[F_ST_CHG_RDY], &chg_ready); - if (ret) - return ret; - - ret = regmap_field_read(chip->rm_fields[F_ST_CHG_DONE], &chg_done); + ret = regmap_field_read(chip->rm_fields[F_IC_STAT], &ic_stat); if (ret) return ret; - ret = regmap_read(chip->regmap, RT9471_REG_STAT1, &fault_stat); - if (ret) - return ret; - - fault_stat &= RT9471_CHGFAULT_MASK; - - if (chg_ready && chg_done) - *status = POWER_SUPPLY_STATUS_FULL; - else if (chg_ready && fault_stat) + switch (ic_stat) { + case RT9471_ICSTAT_VBUSRDY: + case RT9471_ICSTAT_CHGFAULT: *status = POWER_SUPPLY_STATUS_NOT_CHARGING; - else if (chg_ready && !fault_stat) + break; + case RT9471_ICSTAT_TRICKLECHG ... RT9471_ICSTAT_BGCHG: *status = POWER_SUPPLY_STATUS_CHARGING; - else + break; + case RT9471_ICSTAT_CHGDONE: + *status = POWER_SUPPLY_STATUS_FULL; + break; + case RT9471_ICSTAT_SLEEP: + case RT9471_ICSTAT_OTG: *status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + *status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } return 0; } diff --git a/drivers/power/supply/samsung-sdi-battery.c b/drivers/power/supply/samsung-sdi-battery.c index b63fd2758c2f1..33565002ee270 100644 --- a/drivers/power/supply/samsung-sdi-battery.c +++ b/drivers/power/supply/samsung-sdi-battery.c @@ -431,7 +431,7 @@ static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb58515 * temperature compensation tables so we just state 100% for every temperature. * If you have the datasheets, please provide these tables. */ -static struct power_supply_resistance_temp_table samsung_temp2res[] = { +static const struct power_supply_resistance_temp_table samsung_temp2res[] = { { .temp = 50, .resistance = 100 }, { .temp = 40, .resistance = 100 }, { .temp = 30, .resistance = 100 }, @@ -447,7 +447,7 @@ static struct power_supply_resistance_temp_table samsung_temp2res[] = { * These must be sorted by falling OCV value. */ -static struct power_supply_battery_ocv_table samsung_ocv_cap_eb485159lu[] = { +static const struct power_supply_battery_ocv_table samsung_ocv_cap_eb485159lu[] = { { .ocv = 4330000, .capacity = 100}, { .ocv = 4320000, .capacity = 99}, { .ocv = 4283000, .capacity = 95}, @@ -499,7 +499,7 @@ static struct power_supply_battery_ocv_table samsung_ocv_cap_eb485159lu[] = { }; /* Same capacity table is used by eb-l1m7flu, eb425161la, eb425161lu */ -static struct power_supply_battery_ocv_table samsung_ocv_cap_1500mah[] = { +static const struct power_supply_battery_ocv_table samsung_ocv_cap_1500mah[] = { { .ocv = 4328000, .capacity = 100}, { .ocv = 4299000, .capacity = 99}, { .ocv = 4281000, .capacity = 98}, @@ -540,7 +540,7 @@ static struct power_supply_battery_ocv_table samsung_ocv_cap_1500mah[] = { { .ocv = 3300000, .capacity = 0}, }; -static struct power_supply_battery_ocv_table samsung_ocv_cap_eb535151vu[] = { +static const struct power_supply_battery_ocv_table samsung_ocv_cap_eb535151vu[] = { { .ocv = 4178000, .capacity = 100}, { .ocv = 4148000, .capacity = 99}, { .ocv = 4105000, .capacity = 95}, @@ -572,7 +572,7 @@ static struct power_supply_battery_ocv_table samsung_ocv_cap_eb535151vu[] = { { .ocv = 3300000, .capacity = 0}, }; -static struct power_supply_battery_ocv_table samsung_ocv_cap_eb585157lu[] = { +static const struct power_supply_battery_ocv_table samsung_ocv_cap_eb585157lu[] = { { .ocv = 4320000, .capacity = 100}, { .ocv = 4296000, .capacity = 99}, { .ocv = 4283000, .capacity = 98}, diff --git a/drivers/power/supply/sc2731_charger.c b/drivers/power/supply/sc2731_charger.c index b3d8b1ca97da7..50d5157af9277 100644 --- a/drivers/power/supply/sc2731_charger.c +++ b/drivers/power/supply/sc2731_charger.c @@ -530,7 +530,7 @@ static struct platform_driver sc2731_charger_driver = { .of_match_table = sc2731_charger_of_match, }, .probe = sc2731_charger_probe, - .remove_new = sc2731_charger_remove, + .remove = sc2731_charger_remove, }; module_platform_driver(sc2731_charger_driver); diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index bd23c4d9fed43..f36edc2ba7087 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -992,7 +992,7 @@ static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) { struct power_supply_battery_info *info; - struct power_supply_battery_ocv_table *table; + const struct power_supply_battery_ocv_table *table; int ret, delta_clbcnt, alarm_adc; ret = power_supply_get_battery_info(data->battery, &info); @@ -1183,10 +1183,14 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) return PTR_ERR(data->charge_chan); } - data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); + data->gpiod = devm_gpiod_get(dev, "battery-detect", GPIOD_IN); if (IS_ERR(data->gpiod)) { - dev_err(dev, "failed to get battery detection GPIO\n"); - return PTR_ERR(data->gpiod); + data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); + if (IS_ERR(data->gpiod)) { + dev_err(dev, "failed to get battery detection GPIO\n"); + return PTR_ERR(data->gpiod); + } + dev_warn(dev, "bat-detect is deprecated, please use battery-detect\n"); } ret = gpiod_get_value_cansleep(data->gpiod); diff --git a/drivers/power/supply/tps65090-charger.c b/drivers/power/supply/tps65090-charger.c index d41595764caa5..d65193e410a62 100644 --- a/drivers/power/supply/tps65090-charger.c +++ b/drivers/power/supply/tps65090-charger.c @@ -343,7 +343,7 @@ static struct platform_driver tps65090_charger_driver = { .of_match_table = of_tps65090_charger_match, }, .probe = tps65090_charger_probe, - .remove_new = tps65090_charger_remove, + .remove = tps65090_charger_remove, }; module_platform_driver(tps65090_charger_driver); diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c index 2382749a2f531..6fff44e1ecac8 100644 --- a/drivers/power/supply/tps65217_charger.c +++ b/drivers/power/supply/tps65217_charger.c @@ -269,7 +269,7 @@ MODULE_DEVICE_TABLE(of, tps65217_charger_match_table); static struct platform_driver tps65217_charger_driver = { .probe = tps65217_charger_probe, - .remove_new = tps65217_charger_remove, + .remove = tps65217_charger_remove, .driver = { .name = "tps65217-charger", .of_match_table = of_match_ptr(tps65217_charger_match_table), diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index f3f1a0862e935..9dcb5457bef41 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -1133,7 +1133,7 @@ MODULE_DEVICE_TABLE(of, twl_bci_of_match); static struct platform_driver twl4030_bci_driver = { .probe = twl4030_bci_probe, - .remove_new = twl4030_bci_remove, + .remove = twl4030_bci_remove, .driver = { .name = "twl4030_bci", .of_match_table = of_match_ptr(twl_bci_of_match), diff --git a/drivers/power/supply/twl6030_charger.c b/drivers/power/supply/twl6030_charger.c new file mode 100644 index 0000000000000..b4ec26ff257cc --- /dev/null +++ b/drivers/power/supply/twl6030_charger.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TWL6030 charger + * + * Copyright (C) 2024 Andreas Kemnade + * + * based on older 6030 driver found in a v3.0 vendor kernel + * + * based on twl4030_bci_battery.c by TI + * Copyright (C) 2008 Texas Instruments, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONTROLLER_INT_MASK 0x00 +#define CONTROLLER_CTRL1 0x01 +#define CONTROLLER_WDG 0x02 +#define CONTROLLER_STAT1 0x03 +#define CHARGERUSB_INT_STATUS 0x04 +#define CHARGERUSB_INT_MASK 0x05 +#define CHARGERUSB_STATUS_INT1 0x06 +#define CHARGERUSB_STATUS_INT2 0x07 +#define CHARGERUSB_CTRL1 0x08 +#define CHARGERUSB_CTRL2 0x09 +#define CHARGERUSB_CTRL3 0x0A +#define CHARGERUSB_STAT1 0x0B +#define CHARGERUSB_VOREG 0x0C +#define CHARGERUSB_VICHRG 0x0D +#define CHARGERUSB_CINLIMIT 0x0E +#define CHARGERUSB_CTRLLIMIT1 0x0F +#define CHARGERUSB_CTRLLIMIT2 0x10 +#define ANTICOLLAPSE_CTRL1 0x11 +#define ANTICOLLAPSE_CTRL2 0x12 + +/* TWL6032 registers 0xDA to 0xDE - TWL6032_MODULE_CHARGER */ +#define CONTROLLER_CTRL2 0x00 +#define CONTROLLER_VSEL_COMP 0x01 +#define CHARGERUSB_VSYSREG 0x02 +#define CHARGERUSB_VICHRG_PC 0x03 +#define LINEAR_CHRG_STS 0x04 + +#define LINEAR_CHRG_STS_CRYSTL_OSC_OK 0x40 +#define LINEAR_CHRG_STS_END_OF_CHARGE 0x20 +#define LINEAR_CHRG_STS_VBATOV 0x10 +#define LINEAR_CHRG_STS_VSYSOV 0x08 +#define LINEAR_CHRG_STS_DPPM_STS 0x04 +#define LINEAR_CHRG_STS_CV_STS 0x02 +#define LINEAR_CHRG_STS_CC_STS 0x01 + +#define FG_REG_00 0x00 +#define FG_REG_01 0x01 +#define FG_REG_02 0x02 +#define FG_REG_03 0x03 +#define FG_REG_04 0x04 +#define FG_REG_05 0x05 +#define FG_REG_06 0x06 +#define FG_REG_07 0x07 +#define FG_REG_08 0x08 +#define FG_REG_09 0x09 +#define FG_REG_10 0x0A +#define FG_REG_11 0x0B + +/* CONTROLLER_INT_MASK */ +#define MVAC_FAULT BIT(7) +#define MAC_EOC BIT(6) +#define LINCH_GATED BIT(5) +#define MBAT_REMOVED BIT(4) +#define MFAULT_WDG BIT(3) +#define MBAT_TEMP BIT(2) +#define MVBUS_DET BIT(1) +#define MVAC_DET BIT(0) + +/* CONTROLLER_CTRL1 */ +#define CONTROLLER_CTRL1_EN_LINCH BIT(5) +#define CONTROLLER_CTRL1_EN_CHARGER BIT(4) +#define CONTROLLER_CTRL1_SEL_CHARGER BIT(3) + +/* CONTROLLER_STAT1 */ +#define CONTROLLER_STAT1_EXTCHRG_STATZ BIT(7) +#define CONTROLLER_STAT1_LINCH_GATED BIT(6) +#define CONTROLLER_STAT1_CHRG_DET_N BIT(5) +#define CONTROLLER_STAT1_FAULT_WDG BIT(4) +#define CONTROLLER_STAT1_VAC_DET BIT(3) +#define VAC_DET BIT(3) +#define CONTROLLER_STAT1_VBUS_DET BIT(2) +#define VBUS_DET BIT(2) +#define CONTROLLER_STAT1_BAT_REMOVED BIT(1) +#define CONTROLLER_STAT1_BAT_TEMP_OVRANGE BIT(0) + +/* CHARGERUSB_INT_STATUS */ +#define EN_LINCH BIT(4) +#define CURRENT_TERM_INT BIT(3) +#define CHARGERUSB_STAT BIT(2) +#define CHARGERUSB_THMREG BIT(1) +#define CHARGERUSB_FAULT BIT(0) + +/* CHARGERUSB_INT_MASK */ +#define MASK_MCURRENT_TERM BIT(3) +#define MASK_MCHARGERUSB_STAT BIT(2) +#define MASK_MCHARGERUSB_THMREG BIT(1) +#define MASK_MCHARGERUSB_FAULT BIT(0) + +/* CHARGERUSB_STATUS_INT1 */ +#define CHARGERUSB_STATUS_INT1_TMREG BIT(7) +#define CHARGERUSB_STATUS_INT1_NO_BAT BIT(6) +#define CHARGERUSB_STATUS_INT1_BST_OCP BIT(5) +#define CHARGERUSB_STATUS_INT1_TH_SHUTD BIT(4) +#define CHARGERUSB_STATUS_INT1_BAT_OVP BIT(3) +#define CHARGERUSB_STATUS_INT1_POOR_SRC BIT(2) +#define CHARGERUSB_STATUS_INT1_SLP_MODE BIT(1) +#define CHARGERUSB_STATUS_INT1_VBUS_OVP BIT(0) + +/* CHARGERUSB_STATUS_INT2 */ +#define ICCLOOP BIT(3) +#define CURRENT_TERM BIT(2) +#define CHARGE_DONE BIT(1) +#define ANTICOLLAPSE BIT(0) + +/* CHARGERUSB_CTRL1 */ +#define SUSPEND_BOOT BIT(7) +#define OPA_MODE BIT(6) +#define HZ_MODE BIT(5) +#define TERM BIT(4) + +/* CHARGERUSB_CTRL2 */ +#define UA_TO_VITERM(x) (((x) / 50000 - 1) << 5) + +/* CHARGERUSB_CTRL3 */ +#define VBUSCHRG_LDO_OVRD BIT(7) +#define CHARGE_ONCE BIT(6) +#define BST_HW_PR_DIS BIT(5) +#define AUTOSUPPLY BIT(3) +#define BUCK_HSILIM BIT(0) + +/* CHARGERUSB_VOREG */ +#define UV_TO_VOREG(x) (((x) - 3500000) / 20000) +#define VOREG_TO_UV(x) (((x) & 0x3F) * 20000 + 3500000) +#define CHARGERUSB_VOREG_3P52 0x01 +#define CHARGERUSB_VOREG_4P0 0x19 +#define CHARGERUSB_VOREG_4P2 0x23 +#define CHARGERUSB_VOREG_4P76 0x3F + +/* CHARGERUSB_VICHRG */ +/* + * might be inaccurate for < 500 mA, diffent scale might apply, + * either starting from 100 mA or 300 mA + */ +#define UA_TO_VICHRG(x) (((x) / 100000) - 1) +#define VICHRG_TO_UA(x) (((x) & 0xf) * 100000 + 100000) + +/* CHARGERUSB_CINLIMIT */ +#define CHARGERUSB_CIN_LIMIT_100 0x1 +#define CHARGERUSB_CIN_LIMIT_300 0x5 +#define CHARGERUSB_CIN_LIMIT_500 0x9 +#define CHARGERUSB_CIN_LIMIT_NONE 0xF + +/* CHARGERUSB_CTRLLIMIT2 */ +#define CHARGERUSB_CTRLLIMIT2_1500 0x0E +#define LOCK_LIMIT BIT(4) + +/* ANTICOLLAPSE_CTRL2 */ +#define BUCK_VTH_SHIFT 5 + +/* FG_REG_00 */ +#define CC_ACTIVE_MODE_SHIFT 6 +#define CC_AUTOCLEAR BIT(2) +#define CC_CAL_EN BIT(1) +#define CC_PAUSE BIT(0) + +#define REG_TOGGLE1 0x90 +#define REG_PWDNSTATUS1 0x93 +#define FGDITHS BIT(7) +#define FGDITHR BIT(6) +#define FGS BIT(5) +#define FGR BIT(4) +#define BBSPOR_CFG 0xE6 +#define BB_CHG_EN BIT(3) + +struct twl6030_charger_info { + struct device *dev; + struct power_supply *usb; + struct power_supply_battery_info *binfo; + struct work_struct work; + int irq_chg; + int input_current_limit; + struct iio_channel *channel_vusb; + struct delayed_work charger_monitor; + bool extended_current_range; +}; + +struct twl6030_charger_chip_data { + bool extended_current_range; +}; + +static int twl6030_charger_read(u8 reg, u8 *val) +{ + return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg); +} + +static int twl6030_charger_write(u8 reg, u8 val) +{ + return twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, val, reg); +} + +static int twl6030_config_cinlimit_reg(struct twl6030_charger_info *charger, + unsigned int ua) +{ + if (ua >= 50000 && ua <= 750000) { + ua = (ua - 50000) / 50000; + } else if ((ua > 750000) && (ua <= 1500000) && charger->extended_current_range) { + ua = ((ua % 100000) ? 0x30 : 0x20) + ((ua - 100000) / 100000); + } else { + if (ua < 50000) { + dev_err(charger->dev, "invalid input current limit\n"); + return -EINVAL; + } + /* This is no current limit */ + ua = 0x0F; + } + + return twl6030_charger_write(CHARGERUSB_CINLIMIT, ua); +} + +/* + * rewriting all stuff here, resets to extremely conservative defaults were + * seen under some circumstances, like charge voltage to 3.5V + */ +static int twl6030_enable_usb(struct twl6030_charger_info *charger) +{ + int ret; + + ret = twl6030_charger_write(CHARGERUSB_VICHRG, + UA_TO_VICHRG(charger->binfo->constant_charge_current_max_ua)); + if (ret < 0) + return ret; + + ret = twl6030_charger_write(CONTROLLER_WDG, 0xff); + if (ret < 0) + return ret; + + charger->input_current_limit = 500000; + ret = twl6030_config_cinlimit_reg(charger, charger->input_current_limit); + if (ret < 0) + return ret; + + ret = twl6030_charger_write(CHARGERUSB_CINLIMIT, CHARGERUSB_CIN_LIMIT_500); + if (ret < 0) + return ret; + + ret = twl6030_charger_write(CHARGERUSB_VOREG, + UV_TO_VOREG(charger->binfo->constant_charge_voltage_max_uv)); + if (ret < 0) + return ret; + + ret = twl6030_charger_write(CHARGERUSB_CTRL1, TERM); + if (ret < 0) + return ret; + + if (charger->binfo->charge_term_current_ua != -EINVAL) { + ret = twl6030_charger_write(CHARGERUSB_CTRL2, + UA_TO_VITERM(charger->binfo->charge_term_current_ua)); + if (ret < 0) + return ret; + } + + return twl6030_charger_write(CONTROLLER_CTRL1, CONTROLLER_CTRL1_EN_CHARGER); +} + +static void twl6030_charger_wdg(struct work_struct *data) +{ + struct twl6030_charger_info *charger = + container_of(data, struct twl6030_charger_info, + charger_monitor.work); + + u8 val; + u8 int_stat; + u8 stat_int1; + u8 stat_int2; + + twl6030_charger_read(CONTROLLER_STAT1, &val); + twl6030_charger_read(CHARGERUSB_INT_STATUS, &int_stat); + twl6030_charger_read(CHARGERUSB_STATUS_INT1, &stat_int1); + twl6030_charger_read(CHARGERUSB_STATUS_INT2, &stat_int2); + dev_dbg(charger->dev, + "wdg: stat1: %02x %s INT_STATUS %02x STATUS_INT1 %02x STATUS_INT2 %02x\n", + val, (val & VBUS_DET) ? "usb online" : "usb offline", + int_stat, stat_int1, stat_int2); + + twl6030_charger_write(CONTROLLER_WDG, 0xff); + schedule_delayed_work(&charger->charger_monitor, + msecs_to_jiffies(10000)); +} + +static irqreturn_t twl6030_charger_interrupt(int irq, void *arg) +{ + struct twl6030_charger_info *charger = arg; + u8 val; + u8 int_stat; + u8 stat_int1; + u8 stat_int2; + + if (twl6030_charger_read(CONTROLLER_STAT1, &val) < 0) + return IRQ_HANDLED; + + if (twl6030_charger_read(CHARGERUSB_INT_STATUS, &int_stat) < 0) + return IRQ_HANDLED; + + if (twl6030_charger_read(CHARGERUSB_STATUS_INT1, &stat_int1) < 0) + return IRQ_HANDLED; + + if (twl6030_charger_read(CHARGERUSB_STATUS_INT2, &stat_int2) < 0) + return IRQ_HANDLED; + + dev_dbg(charger->dev, + "charger irq: stat1: %02x %s INT_STATUS %02x STATUS_INT1 %02x STATUS_INT2 %02x\n", + val, (val & VBUS_DET) ? "usb online" : "usb offline", + int_stat, stat_int1, stat_int2); + power_supply_changed(charger->usb); + + if (val & VBUS_DET) { + if (twl6030_charger_read(CONTROLLER_CTRL1, &val) < 0) + return IRQ_HANDLED; + + if (!(val & CONTROLLER_CTRL1_EN_CHARGER)) { + if (twl6030_enable_usb(charger) < 0) + return IRQ_HANDLED; + + schedule_delayed_work(&charger->charger_monitor, + msecs_to_jiffies(10000)); + } + } else { + cancel_delayed_work(&charger->charger_monitor); + } + return IRQ_HANDLED; +} + +static int twl6030_charger_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct twl6030_charger_info *charger = power_supply_get_drvdata(psy); + int ret; + u8 stat1; + u8 intstat; + + ret = twl6030_charger_read(CONTROLLER_STAT1, &stat1); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (!(stat1 & VBUS_DET)) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + } + ret = twl6030_charger_read(CHARGERUSB_STATUS_INT2, &intstat); + if (ret) + return ret; + + if (intstat & CHARGE_DONE) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (intstat & CURRENT_TERM) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (!charger->channel_vusb) + return -ENODATA; + + ret = iio_read_channel_processed_scale(charger->channel_vusb, &val->intval, 1000); + if (ret < 0) + return ret; + + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(stat1 & VBUS_DET); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + val->intval = charger->input_current_limit; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int twl6030_charger_usb_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct twl6030_charger_info *charger = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + charger->input_current_limit = val->intval; + return twl6030_config_cinlimit_reg(charger, charger->input_current_limit); + default: + return -EINVAL; + } + + return 0; +} + +static int twl6030_charger_usb_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + dev_info(&psy->dev, "is %d writeable?\n", (int)psp); + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return true; + default: + return false; + } +} + +static enum power_supply_property twl6030_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, +}; + +static const struct power_supply_desc twl6030_charger_usb_desc = { + .name = "twl6030_usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = twl6030_charger_props, + .num_properties = ARRAY_SIZE(twl6030_charger_props), + .get_property = twl6030_charger_usb_get_property, + .set_property = twl6030_charger_usb_set_property, + .property_is_writeable = twl6030_charger_usb_property_is_writeable, +}; + +static int twl6030_charger_probe(struct platform_device *pdev) +{ + struct twl6030_charger_info *charger; + const struct twl6030_charger_chip_data *chip_data; + struct power_supply_config psy_cfg = {}; + int ret; + u8 val; + + charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + charger->dev = &pdev->dev; + charger->irq_chg = platform_get_irq(pdev, 0); + + chip_data = device_get_match_data(&pdev->dev); + if (!chip_data) + return dev_err_probe(&pdev->dev, -EINVAL, "missing chip data\n"); + + charger->extended_current_range = chip_data->extended_current_range; + platform_set_drvdata(pdev, charger); + psy_cfg.drv_data = charger; + psy_cfg.fwnode = dev_fwnode(&pdev->dev); + + charger->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb"); + if (IS_ERR(charger->channel_vusb)) { + ret = PTR_ERR(charger->channel_vusb); + if (ret == -EPROBE_DEFER) + return ret; /* iio not ready */ + dev_warn(&pdev->dev, "could not request vusb iio channel (%d)", + ret); + charger->channel_vusb = NULL; + } + + charger->usb = devm_power_supply_register(&pdev->dev, + &twl6030_charger_usb_desc, + &psy_cfg); + if (IS_ERR(charger->usb)) + return dev_err_probe(&pdev->dev, PTR_ERR(charger->usb), + "Failed to register usb\n"); + + ret = power_supply_get_battery_info(charger->usb, &charger->binfo); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to get battery info\n"); + + dev_info(&pdev->dev, "battery with vmax %d imax: %d\n", + charger->binfo->constant_charge_voltage_max_uv, + charger->binfo->constant_charge_current_max_ua); + + if (charger->binfo->constant_charge_voltage_max_uv == -EINVAL) { + ret = twl6030_charger_read(CHARGERUSB_CTRLLIMIT1, &val); + if (ret < 0) + return ret; + + charger->binfo->constant_charge_voltage_max_uv = + VOREG_TO_UV(val); + } + + if (charger->binfo->constant_charge_voltage_max_uv > 4760000 || + charger->binfo->constant_charge_voltage_max_uv < 350000) + return dev_err_probe(&pdev->dev, -EINVAL, + "Invalid charge voltage\n"); + + if (charger->binfo->constant_charge_current_max_ua == -EINVAL) { + ret = twl6030_charger_read(CHARGERUSB_CTRLLIMIT2, &val); + if (ret < 0) + return ret; + + charger->binfo->constant_charge_current_max_ua = VICHRG_TO_UA(val); + } + + if (charger->binfo->constant_charge_current_max_ua < 100000 || + charger->binfo->constant_charge_current_max_ua > 1500000) { + return dev_err_probe(&pdev->dev, -EINVAL, + "Invalid charge current\n"); + } + + if ((charger->binfo->charge_term_current_ua != -EINVAL) && + (charger->binfo->charge_term_current_ua > 400000 || + charger->binfo->charge_term_current_ua < 50000)) { + return dev_err_probe(&pdev->dev, -EINVAL, + "Invalid charge termination current\n"); + } + + ret = devm_delayed_work_autocancel(&pdev->dev, + &charger->charger_monitor, + twl6030_charger_wdg); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to register delayed work\n"); + + ret = devm_request_threaded_irq(&pdev->dev, charger->irq_chg, NULL, + twl6030_charger_interrupt, + IRQF_ONESHOT, pdev->name, + charger); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "could not request irq %d\n", + charger->irq_chg); + + /* turing to charging to configure things */ + twl6030_charger_write(CONTROLLER_CTRL1, 0); + twl6030_charger_interrupt(0, charger); + + return 0; +} + +static const struct twl6030_charger_chip_data twl6030_data = { + .extended_current_range = false, +}; + +static const struct twl6030_charger_chip_data twl6032_data = { + .extended_current_range = true, +}; + +static const struct of_device_id twl_charger_of_match[] = { + {.compatible = "ti,twl6030-charger", .data = &twl6030_data}, + {.compatible = "ti,twl6032-charger", .data = &twl6032_data}, + { } +}; +MODULE_DEVICE_TABLE(of, twl_charger_of_match); + +static struct platform_driver twl6030_charger_driver = { + .probe = twl6030_charger_probe, + .driver = { + .name = "twl6030_charger", + .of_match_table = twl_charger_of_match, + }, +}; +module_platform_driver(twl6030_charger_driver); + +MODULE_DESCRIPTION("TWL6030 Battery Charger Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/wm831x_power.c b/drivers/power/supply/wm831x_power.c index d56e499ac59fb..538055b29decb 100644 --- a/drivers/power/supply/wm831x_power.c +++ b/drivers/power/supply/wm831x_power.c @@ -720,7 +720,7 @@ static void wm831x_power_remove(struct platform_device *pdev) static struct platform_driver wm831x_power_driver = { .probe = wm831x_power_probe, - .remove_new = wm831x_power_remove, + .remove = wm831x_power_remove, .driver = { .name = "wm831x-power", }, diff --git a/drivers/power/supply/wm8350_power.c b/drivers/power/supply/wm8350_power.c index 3f79ab6f6abf1..b0eb6e0ce8bcf 100644 --- a/drivers/power/supply/wm8350_power.c +++ b/drivers/power/supply/wm8350_power.c @@ -577,7 +577,7 @@ static void wm8350_power_remove(struct platform_device *pdev) static struct platform_driver wm8350_power_driver = { .probe = wm8350_power_probe, - .remove_new = wm8350_power_remove, + .remove = wm8350_power_remove, .driver = { .name = "wm8350-power", }, diff --git a/drivers/power/supply/wm97xx_battery.c b/drivers/power/supply/wm97xx_battery.c index 1cc38d1437d91..b3b0c37a9dd2d 100644 --- a/drivers/power/supply/wm97xx_battery.c +++ b/drivers/power/supply/wm97xx_battery.c @@ -265,7 +265,7 @@ static struct platform_driver wm97xx_bat_driver = { #endif }, .probe = wm97xx_bat_probe, - .remove_new = wm97xx_bat_remove, + .remove = wm97xx_bat_remove, }; module_platform_driver(wm97xx_bat_driver); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 84d48e310aa86..8cb948a91e60d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -497,7 +497,8 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, return -EPERM; } - if (*max_uA > rdev->constraints->max_uA) + if (*max_uA > rdev->constraints->max_uA && + rdev->constraints->max_uA) *max_uA = rdev->constraints->max_uA; if (*min_uA < rdev->constraints->min_uA) *min_uA = rdev->constraints->min_uA; diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 6c343b4b9d15a..7870722b6ee21 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -843,26 +843,15 @@ static const struct rpmh_vreg_hw_data pmic5_ftsmps520 = { .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, }; -static const struct rpmh_vreg_hw_data pmic5_ftsmps525_lv = { +static const struct rpmh_vreg_hw_data pmic5_ftsmps525 = { .regulator_type = VRM, .ops = &rpmh_regulator_vrm_ops, .voltage_ranges = (struct linear_range[]) { REGULATOR_LINEAR_RANGE(300000, 0, 267, 4000), + REGULATOR_LINEAR_RANGE(1376000, 268, 438, 8000), }, - .n_linear_ranges = 1, - .n_voltages = 268, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_ftsmps525_mv = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(600000, 0, 267, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 268, + .n_linear_ranges = 2, + .n_voltages = 439, .pmic_mode_map = pmic_mode_map_pmic5_smps, .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, }; @@ -1190,12 +1179,12 @@ static const struct rpmh_vreg_init_data pm8550_vreg_data[] = { }; static const struct rpmh_vreg_init_data pm8550vs_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_lv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_mv, "vdd-s6"), + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), @@ -1203,14 +1192,14 @@ static const struct rpmh_vreg_init_data pm8550vs_vreg_data[] = { }; static const struct rpmh_vreg_init_data pm8550ve_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525, "vdd-s8"), RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), @@ -1218,14 +1207,14 @@ static const struct rpmh_vreg_init_data pm8550ve_vreg_data[] = { }; static const struct rpmh_vreg_init_data pmc8380_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525, "vdd-s8"), RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), @@ -1409,16 +1398,16 @@ static const struct rpmh_vreg_init_data pmx65_vreg_data[] = { }; static const struct rpmh_vreg_init_data pmx75_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps525_lv, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps525_lv, "vdd-s10"), + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps525, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps525, "vdd-s10"), RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-18"), RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 955e4e38477e6..83962a114dc9f 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -329,7 +329,8 @@ config STM32_RPROC config TI_K3_DSP_REMOTEPROC tristate "TI K3 DSP remoteproc support" - depends on ARCH_K3 + depends on ARCH_K3 || COMPILE_TEST + depends on TI_SCI_PROTOCOL || (COMPILE_TEST && TI_SCI_PROTOCOL=n) depends on OMAP2PLUS_MBOX help Say m here to support TI's C66x and C71x DSP remote processor @@ -341,9 +342,9 @@ config TI_K3_DSP_REMOTEPROC config TI_K3_M4_REMOTEPROC tristate "TI K3 M4 remoteproc support" - depends on ARCH_OMAP2PLUS || ARCH_K3 - select MAILBOX - select OMAP2PLUS_MBOX + depends on ARCH_K3 || COMPILE_TEST + depends on TI_SCI_PROTOCOL || (COMPILE_TEST && TI_SCI_PROTOCOL=n) + depends on OMAP2PLUS_MBOX help Say m here to support TI's M4 remote processor subsystems on various TI K3 family of SoCs through the remote processor @@ -354,7 +355,8 @@ config TI_K3_M4_REMOTEPROC config TI_K3_R5_REMOTEPROC tristate "TI K3 R5 remoteproc support" - depends on ARCH_K3 + depends on ARCH_K3 || COMPILE_TEST + depends on TI_SCI_PROTOCOL || (COMPILE_TEST && TI_SCI_PROTOCOL=n) depends on OMAP2PLUS_MBOX help Say m here to support TI's R5F remote processor subsystems diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 8770d0cf1255f..93031f0867d10 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -251,10 +251,8 @@ static int da8xx_rproc_probe(struct platform_device *pdev) return irq; irq_data = irq_get_irq_data(irq); - if (!irq_data) { - dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq); - return -EINVAL; - } + if (!irq_data) + return dev_err_probe(dev, -EINVAL, "irq_get_irq_data(%d): NULL\n", irq); bootreg = devm_platform_ioremap_resource_byname(pdev, "host1cfg"); if (IS_ERR(bootreg)) @@ -265,28 +263,17 @@ static int da8xx_rproc_probe(struct platform_device *pdev) return PTR_ERR(chipsig); dsp_clk = devm_clk_get(dev, NULL); - if (IS_ERR(dsp_clk)) { - dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); - - return PTR_ERR(dsp_clk); - } + if (IS_ERR(dsp_clk)) + return dev_err_probe(dev, PTR_ERR(dsp_clk), "clk_get error\n"); dsp_reset = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(dsp_reset)) { - if (PTR_ERR(dsp_reset) != -EPROBE_DEFER) - dev_err(dev, "unable to get reset control: %ld\n", - PTR_ERR(dsp_reset)); - - return PTR_ERR(dsp_reset); - } + if (IS_ERR(dsp_reset)) + return dev_err_probe(dev, PTR_ERR(dsp_reset), "unable to get reset control\n"); if (dev->of_node) { ret = of_reserved_mem_device_init(dev); - if (ret) { - dev_err(dev, "device does not have specific CMA pool: %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "device does not have specific CMA pool\n"); } rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name, @@ -378,7 +365,7 @@ MODULE_DEVICE_TABLE(of, davinci_rproc_of_match); static struct platform_driver da8xx_rproc_driver = { .probe = da8xx_rproc_probe, - .remove_new = da8xx_rproc_remove, + .remove = da8xx_rproc_remove, .driver = { .name = "davinci-rproc", .of_match_table = of_match_ptr(davinci_rproc_of_match), diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 376187ad5754c..ea5024919c2ff 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -1258,7 +1258,7 @@ MODULE_DEVICE_TABLE(of, imx_dsp_rproc_of_match); static struct platform_driver imx_dsp_rproc_driver = { .probe = imx_dsp_rproc_probe, - .remove_new = imx_dsp_rproc_remove, + .remove = imx_dsp_rproc_remove, .driver = { .name = "imx-dsp-rproc", .of_match_table = imx_dsp_rproc_of_match, diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 800015ff7ff92..74299af1d7f10 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -1198,7 +1198,7 @@ MODULE_DEVICE_TABLE(of, imx_rproc_of_match); static struct platform_driver imx_rproc_driver = { .probe = imx_rproc_probe, - .remove_new = imx_rproc_remove, + .remove = imx_rproc_remove, .driver = { .name = "imx-rproc", .of_match_table = imx_rproc_of_match, diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 8f0f7a4cfef26..6e54093d17323 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -490,7 +490,7 @@ MODULE_DEVICE_TABLE(of, keystone_rproc_of_match); static struct platform_driver keystone_rproc_driver = { .probe = keystone_rproc_probe, - .remove_new = keystone_rproc_remove, + .remove = keystone_rproc_remove, .driver = { .name = "keystone-rproc", .of_match_table = keystone_rproc_of_match, diff --git a/drivers/remoteproc/meson_mx_ao_arc.c b/drivers/remoteproc/meson_mx_ao_arc.c index f6744b5383230..7dfdf11b00368 100644 --- a/drivers/remoteproc/meson_mx_ao_arc.c +++ b/drivers/remoteproc/meson_mx_ao_arc.c @@ -246,7 +246,7 @@ MODULE_DEVICE_TABLE(of, meson_mx_ao_arc_rproc_match); static struct platform_driver meson_mx_ao_arc_rproc_driver = { .probe = meson_mx_ao_arc_rproc_probe, - .remove_new = meson_mx_ao_arc_rproc_remove, + .remove = meson_mx_ao_arc_rproc_remove, .driver = { .name = "meson-mx-ao-arc-rproc", .of_match_table = meson_mx_ao_arc_rproc_match, diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index e744c07507eed..0f4a7065d0bd9 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -1521,7 +1521,7 @@ MODULE_DEVICE_TABLE(of, mtk_scp_of_match); static struct platform_driver mtk_scp_driver = { .probe = scp_probe, - .remove_new = scp_remove, + .remove = scp_remove, .driver = { .name = "mtk-scp", .of_match_table = mtk_scp_of_match, diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 327f0c7ee3d6b..1656574b73175 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -1132,7 +1132,7 @@ static struct platform_driver pru_rproc_driver = { .suppress_bind_attrs = true, }, .probe = pru_rproc_probe, - .remove_new = pru_rproc_remove, + .remove = pru_rproc_remove, }; module_platform_driver(pru_rproc_driver); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 572dcb0f055b7..94af77baa7a1c 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -534,15 +534,11 @@ static const struct rproc_ops adsp_ops = { static int adsp_init_clock(struct qcom_adsp *adsp, const char **clk_ids) { int num_clks = 0; - int i, ret; + int i; adsp->xo = devm_clk_get(adsp->dev, "xo"); - if (IS_ERR(adsp->xo)) { - ret = PTR_ERR(adsp->xo); - if (ret != -EPROBE_DEFER) - dev_err(adsp->dev, "failed to get xo clock"); - return ret; - } + if (IS_ERR(adsp->xo)) + return dev_err_probe(adsp->dev, PTR_ERR(adsp->xo), "failed to get xo clock"); for (i = 0; clk_ids[i]; i++) num_clks++; @@ -708,10 +704,9 @@ static int adsp_probe(struct platform_device *pdev) return ret; ret = qcom_rproc_pds_attach(adsp, desc->pd_names, desc->num_pds); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to attach proxy power domains\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to attach proxy power domains\n"); ret = adsp_init_reset(adsp); if (ret) @@ -734,15 +729,22 @@ static int adsp_probe(struct platform_device *pdev) desc->ssctl_id); if (IS_ERR(adsp->sysmon)) { ret = PTR_ERR(adsp->sysmon); - goto disable_pm; + goto deinit_remove_glink_pdm_ssr; } ret = rproc_add(rproc); if (ret) - goto disable_pm; + goto remove_sysmon; return 0; +remove_sysmon: + qcom_remove_sysmon_subdev(adsp->sysmon); +deinit_remove_glink_pdm_ssr: + qcom_q6v5_deinit(&adsp->q6v5); + qcom_remove_glink_subdev(rproc, &adsp->glink_subdev); + qcom_remove_pdm_subdev(rproc, &adsp->pdm_subdev); + qcom_remove_ssr_subdev(rproc, &adsp->ssr_subdev); disable_pm: qcom_rproc_pds_detach(adsp); @@ -840,7 +842,7 @@ MODULE_DEVICE_TABLE(of, adsp_of_match); static struct platform_driver adsp_pil_driver = { .probe = adsp_probe, - .remove_new = adsp_remove, + .remove = adsp_remove, .driver = { .name = "qcom_q6v5_adsp", .of_match_table = adsp_of_match, diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 2a42215ce8e07..e78bd986dc3f2 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -261,7 +261,6 @@ enum { static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, const struct qcom_mss_reg_res *reg_res) { - int rc; int i; if (!reg_res) @@ -269,13 +268,10 @@ static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, for (i = 0; reg_res[i].supply; i++) { regs[i].reg = devm_regulator_get(dev, reg_res[i].supply); - if (IS_ERR(regs[i].reg)) { - rc = PTR_ERR(regs[i].reg); - if (rc != -EPROBE_DEFER) - dev_err(dev, "Failed to get %s\n regulator", - reg_res[i].supply); - return rc; - } + if (IS_ERR(regs[i].reg)) + return dev_err_probe(dev, PTR_ERR(regs[i].reg), + "Failed to get %s\n regulator", + reg_res[i].supply); regs[i].uV = reg_res[i].uV; regs[i].uA = reg_res[i].uA; @@ -1162,6 +1158,9 @@ static int q6v5_mba_load(struct q6v5 *qproc) goto disable_active_clks; } + if (qproc->has_mba_logs) + qcom_pil_info_store("mba", qproc->mba_phys, MBA_LOG_SIZE); + writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); if (qproc->dp_size) { writel(qproc->mba_phys + SZ_1M, qproc->rmb_base + RMB_PMI_CODE_START_REG); @@ -1172,9 +1171,6 @@ static int q6v5_mba_load(struct q6v5 *qproc) if (ret) goto reclaim_mba; - if (qproc->has_mba_logs) - qcom_pil_info_store("mba", qproc->mba_phys, MBA_LOG_SIZE); - ret = q6v5_rmb_mba_wait(qproc, 0, 5000); if (ret == -ETIMEDOUT) { dev_err(qproc->dev, "MBA boot timed out\n"); @@ -1813,14 +1809,10 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks, for (i = 0; clk_names[i]; i++) { clks[i] = devm_clk_get(dev, clk_names[i]); - if (IS_ERR(clks[i])) { - int rc = PTR_ERR(clks[i]); - - if (rc != -EPROBE_DEFER) - dev_err(dev, "Failed to get %s clock\n", - clk_names[i]); - return rc; - } + if (IS_ERR(clks[i])) + return dev_err_probe(dev, PTR_ERR(clks[i]), + "Failed to get %s clock\n", + clk_names[i]); } return i; @@ -2028,42 +2020,32 @@ static int q6v5_probe(struct platform_device *pdev) ret = q6v5_init_clocks(&pdev->dev, qproc->proxy_clks, desc->proxy_clk_names); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get proxy clocks.\n"); + if (ret < 0) return ret; - } qproc->proxy_clk_count = ret; ret = q6v5_init_clocks(&pdev->dev, qproc->reset_clks, desc->reset_clk_names); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get reset clocks.\n"); + if (ret < 0) return ret; - } qproc->reset_clk_count = ret; ret = q6v5_init_clocks(&pdev->dev, qproc->active_clks, desc->active_clk_names); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get active clocks.\n"); + if (ret < 0) return ret; - } qproc->active_clk_count = ret; ret = q6v5_regulator_init(&pdev->dev, qproc->proxy_regs, desc->proxy_supply); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get proxy regulators.\n"); + if (ret < 0) return ret; - } qproc->proxy_reg_count = ret; ret = q6v5_regulator_init(&pdev->dev, qproc->active_regs, desc->active_supply); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get active regulators.\n"); + if (ret < 0) return ret; - } qproc->active_reg_count = ret; ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, @@ -2073,10 +2055,8 @@ static int q6v5_probe(struct platform_device *pdev) ret = q6v5_regulator_init(&pdev->dev, qproc->fallback_proxy_regs, desc->fallback_proxy_supply); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get fallback proxy regulators.\n"); + if (ret < 0) return ret; - } qproc->fallback_proxy_reg_count = ret; } else if (ret < 0) { dev_err(&pdev->dev, "Failed to init power domains\n"); @@ -2533,7 +2513,7 @@ MODULE_DEVICE_TABLE(of, q6v5_of_match); static struct platform_driver q6v5_driver = { .probe = q6v5_probe, - .remove_new = q6v5_remove, + .remove = q6v5_remove, .driver = { .name = "qcom-q6v5-mss", .of_match_table = q6v5_of_match, diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index ef82835e98a4e..97c4bdd9222a8 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -453,24 +453,16 @@ static const struct rproc_ops adsp_minidump_ops = { static int adsp_init_clock(struct qcom_adsp *adsp) { - int ret; - adsp->xo = devm_clk_get(adsp->dev, "xo"); - if (IS_ERR(adsp->xo)) { - ret = PTR_ERR(adsp->xo); - if (ret != -EPROBE_DEFER) - dev_err(adsp->dev, "failed to get xo clock"); - return ret; - } + if (IS_ERR(adsp->xo)) + return dev_err_probe(adsp->dev, PTR_ERR(adsp->xo), + "failed to get xo clock"); + adsp->aggre2_clk = devm_clk_get_optional(adsp->dev, "aggre2"); - if (IS_ERR(adsp->aggre2_clk)) { - ret = PTR_ERR(adsp->aggre2_clk); - if (ret != -EPROBE_DEFER) - dev_err(adsp->dev, - "failed to get aggre2 clock"); - return ret; - } + if (IS_ERR(adsp->aggre2_clk)) + return dev_err_probe(adsp->dev, PTR_ERR(adsp->aggre2_clk), + "failed to get aggre2 clock"); return 0; } @@ -716,7 +708,7 @@ static int adsp_probe(struct platform_device *pdev) if (desc->minidump_id) ops = &adsp_minidump_ops; - rproc = devm_rproc_alloc(&pdev->dev, pdev->name, ops, fw_name, sizeof(*adsp)); + rproc = devm_rproc_alloc(&pdev->dev, desc->sysmon_name, ops, fw_name, sizeof(*adsp)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); @@ -759,16 +751,16 @@ static int adsp_probe(struct platform_device *pdev) ret = adsp_init_clock(adsp); if (ret) - goto free_rproc; + goto unassign_mem; ret = adsp_init_regulator(adsp); if (ret) - goto free_rproc; + goto unassign_mem; ret = adsp_pds_attach(&pdev->dev, adsp->proxy_pds, desc->proxy_pd_names); if (ret < 0) - goto free_rproc; + goto unassign_mem; adsp->proxy_pd_count = ret; ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, desc->load_state, @@ -784,18 +776,28 @@ static int adsp_probe(struct platform_device *pdev) desc->ssctl_id); if (IS_ERR(adsp->sysmon)) { ret = PTR_ERR(adsp->sysmon); - goto detach_proxy_pds; + goto deinit_remove_pdm_smd_glink; } qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); ret = rproc_add(rproc); if (ret) - goto detach_proxy_pds; + goto remove_ssr_sysmon; return 0; +remove_ssr_sysmon: + qcom_remove_ssr_subdev(rproc, &adsp->ssr_subdev); + qcom_remove_sysmon_subdev(adsp->sysmon); +deinit_remove_pdm_smd_glink: + qcom_remove_pdm_subdev(rproc, &adsp->pdm_subdev); + qcom_remove_smd_subdev(rproc, &adsp->smd_subdev); + qcom_remove_glink_subdev(rproc, &adsp->glink_subdev); + qcom_q6v5_deinit(&adsp->q6v5); detach_proxy_pds: adsp_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count); +unassign_mem: + adsp_unassign_memory_region(adsp); free_rproc: device_init_wakeup(adsp->dev, false); @@ -907,6 +909,7 @@ static const struct adsp_data sm8250_adsp_resource = { .crash_reason_smem = 423, .firmware_name = "adsp.mdt", .pas_id = 1, + .minidump_id = 5, .auto_boot = true, .proxy_pd_names = (char*[]){ "lcx", @@ -1124,6 +1127,7 @@ static const struct adsp_data sm8350_cdsp_resource = { .crash_reason_smem = 601, .firmware_name = "cdsp.mdt", .pas_id = 18, + .minidump_id = 7, .auto_boot = true, .proxy_pd_names = (char*[]){ "cx", @@ -1344,7 +1348,7 @@ static const struct adsp_data sc7280_wpss_resource = { .crash_reason_smem = 626, .firmware_name = "wpss.mdt", .pas_id = 6, - .auto_boot = true, + .auto_boot = false, .proxy_pd_names = (char*[]){ "cx", "mx", @@ -1421,6 +1425,7 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sa8775p-cdsp1-pas", .data = &sa8775p_cdsp1_resource}, { .compatible = "qcom,sa8775p-gpdsp0-pas", .data = &sa8775p_gpdsp0_resource}, { .compatible = "qcom,sa8775p-gpdsp1-pas", .data = &sa8775p_gpdsp1_resource}, + { .compatible = "qcom,sar2130p-adsp-pas", .data = &sm8350_adsp_resource}, { .compatible = "qcom,sc7180-adsp-pas", .data = &sm8250_adsp_resource}, { .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init}, { .compatible = "qcom,sc7280-adsp-pas", .data = &sm8350_adsp_resource}, @@ -1477,7 +1482,7 @@ MODULE_DEVICE_TABLE(of, adsp_of_match); static struct platform_driver adsp_driver = { .probe = adsp_probe, - .remove_new = adsp_remove, + .remove = adsp_remove, .driver = { .name = "qcom_q6v5_pas", .of_match_table = adsp_of_match, diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index e913dabae9924..93648734a2f25 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -902,90 +902,58 @@ static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss) static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss) { - int ret; - wcss->xo = devm_clk_get(wcss->dev, "xo"); - if (IS_ERR(wcss->xo)) { - ret = PTR_ERR(wcss->xo); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get xo clock"); - return ret; - } + if (IS_ERR(wcss->xo)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->xo), + "failed to get xo clock"); wcss->gcc_abhs_cbcr = devm_clk_get(wcss->dev, "gcc_abhs_cbcr"); - if (IS_ERR(wcss->gcc_abhs_cbcr)) { - ret = PTR_ERR(wcss->gcc_abhs_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get gcc abhs clock"); - return ret; - } + if (IS_ERR(wcss->gcc_abhs_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->gcc_abhs_cbcr), + "failed to get gcc abhs clock"); wcss->gcc_axim_cbcr = devm_clk_get(wcss->dev, "gcc_axim_cbcr"); - if (IS_ERR(wcss->gcc_axim_cbcr)) { - ret = PTR_ERR(wcss->gcc_axim_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get gcc axim clock\n"); - return ret; - } + if (IS_ERR(wcss->gcc_axim_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->gcc_axim_cbcr), + "failed to get gcc axim clock\n"); wcss->ahbfabric_cbcr_clk = devm_clk_get(wcss->dev, "lcc_ahbfabric_cbc"); - if (IS_ERR(wcss->ahbfabric_cbcr_clk)) { - ret = PTR_ERR(wcss->ahbfabric_cbcr_clk); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get ahbfabric clock\n"); - return ret; - } + if (IS_ERR(wcss->ahbfabric_cbcr_clk)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->ahbfabric_cbcr_clk), + "failed to get ahbfabric clock\n"); wcss->lcc_csr_cbcr = devm_clk_get(wcss->dev, "tcsr_lcc_cbc"); - if (IS_ERR(wcss->lcc_csr_cbcr)) { - ret = PTR_ERR(wcss->lcc_csr_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get csr cbcr clk\n"); - return ret; - } + if (IS_ERR(wcss->lcc_csr_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->lcc_csr_cbcr), + "failed to get csr cbcr clk\n"); wcss->ahbs_cbcr = devm_clk_get(wcss->dev, "lcc_abhs_cbc"); - if (IS_ERR(wcss->ahbs_cbcr)) { - ret = PTR_ERR(wcss->ahbs_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get ahbs_cbcr clk\n"); - return ret; - } + if (IS_ERR(wcss->ahbs_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->ahbs_cbcr), + "failed to get ahbs_cbcr clk\n"); wcss->tcm_slave_cbcr = devm_clk_get(wcss->dev, "lcc_tcm_slave_cbc"); - if (IS_ERR(wcss->tcm_slave_cbcr)) { - ret = PTR_ERR(wcss->tcm_slave_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get tcm cbcr clk\n"); - return ret; - } + if (IS_ERR(wcss->tcm_slave_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->tcm_slave_cbcr), + "failed to get tcm cbcr clk\n"); wcss->qdsp6ss_abhm_cbcr = devm_clk_get(wcss->dev, "lcc_abhm_cbc"); - if (IS_ERR(wcss->qdsp6ss_abhm_cbcr)) { - ret = PTR_ERR(wcss->qdsp6ss_abhm_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get abhm cbcr clk\n"); - return ret; - } + if (IS_ERR(wcss->qdsp6ss_abhm_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->qdsp6ss_abhm_cbcr), + "failed to get abhm cbcr clk\n"); wcss->qdsp6ss_axim_cbcr = devm_clk_get(wcss->dev, "lcc_axim_cbc"); - if (IS_ERR(wcss->qdsp6ss_axim_cbcr)) { - ret = PTR_ERR(wcss->qdsp6ss_axim_cbcr); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get axim cbcr clk\n"); - return ret; - } + if (IS_ERR(wcss->qdsp6ss_axim_cbcr)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->qdsp6ss_axim_cbcr), + "failed to get axim cbcr clk\n"); wcss->lcc_bcr_sleep = devm_clk_get(wcss->dev, "lcc_bcr_sleep"); - if (IS_ERR(wcss->lcc_bcr_sleep)) { - ret = PTR_ERR(wcss->lcc_bcr_sleep); - if (ret != -EPROBE_DEFER) - dev_err(wcss->dev, "failed to get bcr cbcr clk\n"); - return ret; - } + if (IS_ERR(wcss->lcc_bcr_sleep)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->lcc_bcr_sleep), + "failed to get bcr cbcr clk\n"); return 0; } @@ -1021,7 +989,6 @@ static int q6v5_wcss_probe(struct platform_device *pdev) wcss = rproc->priv; wcss->dev = &pdev->dev; - wcss->version = desc->version; wcss->version = desc->version; wcss->requires_force_stop = desc->requires_force_stop; @@ -1056,18 +1023,33 @@ static int q6v5_wcss_probe(struct platform_device *pdev) qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev); qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); - if (desc->ssctl_id) + if (desc->ssctl_id) { wcss->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(wcss->sysmon)) { + ret = PTR_ERR(wcss->sysmon); + goto deinit_remove_subdevs; + } + } ret = rproc_add(rproc); if (ret) - return ret; + goto remove_sysmon_subdev; platform_set_drvdata(pdev, rproc); return 0; + +remove_sysmon_subdev: + if (desc->ssctl_id) + qcom_remove_sysmon_subdev(wcss->sysmon); +deinit_remove_subdevs: + qcom_q6v5_deinit(&wcss->q6v5); + qcom_remove_glink_subdev(rproc, &wcss->glink_subdev); + qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev); + qcom_remove_ssr_subdev(rproc, &wcss->ssr_subdev); + return ret; } static void q6v5_wcss_remove(struct platform_device *pdev) @@ -1111,7 +1093,7 @@ MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match); static struct platform_driver q6v5_wcss_driver = { .probe = q6v5_wcss_probe, - .remove_new = q6v5_wcss_remove, + .remove = q6v5_wcss_remove, .driver = { .name = "qcom-q6v5-wcss-pil", .of_match_table = q6v5_wcss_of_match, diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index a7bb9da27029d..5b5664603eed2 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -682,7 +682,7 @@ MODULE_DEVICE_TABLE(of, wcnss_of_match); static struct platform_driver wcnss_driver = { .probe = wcnss_probe, - .remove_new = wcnss_remove, + .remove = wcnss_remove, .driver = { .name = "qcom-wcnss-pil", .of_match_table = wcnss_of_match, diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c index dd36fd077911a..b989718776bdb 100644 --- a/drivers/remoteproc/qcom_wcnss_iris.c +++ b/drivers/remoteproc/qcom_wcnss_iris.c @@ -155,9 +155,8 @@ struct qcom_iris *qcom_iris_probe(struct device *parent, bool *use_48mhz_xo) iris->xo_clk = devm_clk_get(&iris->dev, "xo"); if (IS_ERR(iris->xo_clk)) { - ret = PTR_ERR(iris->xo_clk); - if (ret != -EPROBE_DEFER) - dev_err(&iris->dev, "failed to acquire xo clk\n"); + ret = dev_err_probe(&iris->dev, PTR_ERR(iris->xo_clk), + "failed to acquire xo clk\n"); goto err_device_del; } diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c index cc17e8421f65b..921d853594f42 100644 --- a/drivers/remoteproc/rcar_rproc.c +++ b/drivers/remoteproc/rcar_rproc.c @@ -214,7 +214,7 @@ MODULE_DEVICE_TABLE(of, rcar_rproc_of_match); static struct platform_driver rcar_rproc_driver = { .probe = rcar_rproc_probe, - .remove_new = rcar_rproc_remove, + .remove = rcar_rproc_remove, .driver = { .name = "rcar-rproc", .of_match_table = rcar_rproc_of_match, diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index d3f39009b28ed..25a655f33ec0e 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -593,7 +593,7 @@ static void rproc_virtio_remove(struct platform_device *pdev) /* Platform driver */ static struct platform_driver rproc_virtio_driver = { .probe = rproc_virtio_probe, - .remove_new = rproc_virtio_remove, + .remove = rproc_virtio_remove, .driver = { .name = "rproc-virtio", }, diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index 1340be9d01101..5df99bae7131a 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -457,7 +457,7 @@ static void st_rproc_remove(struct platform_device *pdev) static struct platform_driver st_rproc_driver = { .probe = st_rproc_probe, - .remove_new = st_rproc_remove, + .remove = st_rproc_remove, .driver = { .name = "st-rproc", .of_match_table = of_match_ptr(st_rproc_match), diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 8c7f7950b80ee..b02b36a3f5156 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -946,7 +946,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stm32_rproc_pm_ops, static struct platform_driver stm32_rproc_driver = { .probe = stm32_rproc_probe, - .remove_new = stm32_rproc_remove, + .remove = stm32_rproc_remove, .driver = { .name = "stm32-rproc", .pm = pm_ptr(&stm32_rproc_pm_ops), diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index 8be3f631c1920..a695890254ff7 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -403,7 +403,7 @@ static struct resource_table *k3_dsp_get_loaded_rsc_table(struct rproc *rproc, * the hard-coded value suffices to support the IPC-only mode. */ *rsc_table_sz = 256; - return (struct resource_table *)kproc->rmem[0].cpu_addr; + return (__force struct resource_table *)kproc->rmem[0].cpu_addr; } /* @@ -576,11 +576,9 @@ static int k3_dsp_reserved_mem_init(struct k3_dsp_rproc *kproc) return -EINVAL; rmem = of_reserved_mem_lookup(rmem_np); - if (!rmem) { - of_node_put(rmem_np); - return -EINVAL; - } of_node_put(rmem_np); + if (!rmem) + return -EINVAL; kproc->rmem[i].bus_addr = rmem->base; /* 64-bit address regions currently not supported */ @@ -793,7 +791,7 @@ MODULE_DEVICE_TABLE(of, k3_dsp_of_match); static struct platform_driver k3_dsp_rproc_driver = { .probe = k3_dsp_rproc_probe, - .remove_new = k3_dsp_rproc_remove, + .remove = k3_dsp_rproc_remove, .driver = { .name = "k3-dsp-rproc", .of_match_table = k3_dsp_of_match, diff --git a/drivers/remoteproc/ti_k3_m4_remoteproc.c b/drivers/remoteproc/ti_k3_m4_remoteproc.c index 09f0484a90e10..a16fb165fcedd 100644 --- a/drivers/remoteproc/ti_k3_m4_remoteproc.c +++ b/drivers/remoteproc/ti_k3_m4_remoteproc.c @@ -433,11 +433,9 @@ static int k3_m4_reserved_mem_init(struct k3_m4_rproc *kproc) return -EINVAL; rmem = of_reserved_mem_lookup(rmem_np); - if (!rmem) { - of_node_put(rmem_np); - return -EINVAL; - } of_node_put(rmem_np); + if (!rmem) + return -EINVAL; kproc->rmem[i].bus_addr = rmem->base; /* 64-bit address regions currently not supported */ diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 747ee467da88c..6560b7954027f 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -487,10 +487,10 @@ static int k3_r5_rproc_prepare(struct rproc *rproc) * can be effective on all TCM addresses. */ dev_dbg(dev, "zeroing out ATCM memory\n"); - memset(core->mem[0].cpu_addr, 0x00, core->mem[0].size); + memset_io(core->mem[0].cpu_addr, 0x00, core->mem[0].size); dev_dbg(dev, "zeroing out BTCM memory\n"); - memset(core->mem[1].cpu_addr, 0x00, core->mem[1].size); + memset_io(core->mem[1].cpu_addr, 0x00, core->mem[1].size); return 0; } @@ -717,7 +717,7 @@ static struct resource_table *k3_r5_get_loaded_rsc_table(struct rproc *rproc, * the hard-coded value suffices to support the IPC-only mode. */ *rsc_table_sz = 256; - return (struct resource_table *)kproc->rmem[0].cpu_addr; + return (__force struct resource_table *)kproc->rmem[0].cpu_addr; } /* @@ -1001,12 +1001,11 @@ static int k3_r5_reserved_mem_init(struct k3_r5_rproc *kproc) } rmem = of_reserved_mem_lookup(rmem_np); + of_node_put(rmem_np); if (!rmem) { - of_node_put(rmem_np); ret = -EINVAL; goto unmap_rmem; } - of_node_put(rmem_np); kproc->rmem[i].bus_addr = rmem->base; /* @@ -1558,11 +1557,7 @@ static int k3_r5_core_of_init(struct platform_device *pdev) core->ti_sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); if (IS_ERR(core->ti_sci)) { - ret = PTR_ERR(core->ti_sci); - if (ret != -EPROBE_DEFER) { - dev_err(dev, "failed to get ti-sci handle, ret = %d\n", - ret); - } + ret = dev_err_probe(dev, PTR_ERR(core->ti_sci), "failed to get ti-sci handle\n"); core->ti_sci = NULL; goto err; } @@ -1578,18 +1573,14 @@ static int k3_r5_core_of_init(struct platform_device *pdev) ret = PTR_ERR_OR_ZERO(core->reset); if (!ret) ret = -ENODEV; - if (ret != -EPROBE_DEFER) { - dev_err(dev, "failed to get reset handle, ret = %d\n", - ret); - } + dev_err_probe(dev, ret, "failed to get reset handle\n"); goto err; } core->tsp = ti_sci_proc_of_get_tsp(dev, core->ti_sci); if (IS_ERR(core->tsp)) { - ret = PTR_ERR(core->tsp); - dev_err(dev, "failed to construct ti-sci proc control, ret = %d\n", - ret); + ret = dev_err_probe(dev, PTR_ERR(core->tsp), + "failed to construct ti-sci proc control\n"); goto err; } @@ -1659,16 +1650,14 @@ static int k3_r5_cluster_of_init(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev_of_node(dev); struct platform_device *cpdev; - struct device_node *child; struct k3_r5_core *core; int ret; - for_each_available_child_of_node(np, child) { + for_each_available_child_of_node_scoped(np, child) { cpdev = of_find_device_by_node(child); if (!cpdev) { ret = -ENODEV; dev_err(dev, "could not get R5 core platform device\n"); - of_node_put(child); goto fail; } @@ -1677,7 +1666,6 @@ static int k3_r5_cluster_of_init(struct platform_device *pdev) dev_err(dev, "k3_r5_core_of_init failed, ret = %d\n", ret); put_device(&cpdev->dev); - of_node_put(child); goto fail; } @@ -1718,11 +1706,8 @@ static int k3_r5_probe(struct platform_device *pdev) init_waitqueue_head(&cluster->core_transition); ret = of_property_read_u32(np, "ti,cluster-mode", &cluster->mode); - if (ret < 0 && ret != -EINVAL) { - dev_err(dev, "invalid format for ti,cluster-mode, ret = %d\n", - ret); - return ret; - } + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, "invalid format for ti,cluster-mode\n"); if (ret == -EINVAL) { /* @@ -1741,49 +1726,39 @@ static int k3_r5_probe(struct platform_device *pdev) } if ((cluster->mode == CLUSTER_MODE_SINGLECPU && !data->single_cpu_mode) || - (cluster->mode == CLUSTER_MODE_SINGLECORE && !data->is_single_core)) { - dev_err(dev, "Cluster mode = %d is not supported on this SoC\n", cluster->mode); - return -EINVAL; - } + (cluster->mode == CLUSTER_MODE_SINGLECORE && !data->is_single_core)) + return dev_err_probe(dev, -EINVAL, + "Cluster mode = %d is not supported on this SoC\n", + cluster->mode); num_cores = of_get_available_child_count(np); - if (num_cores != 2 && !data->is_single_core) { - dev_err(dev, "MCU cluster requires both R5F cores to be enabled but num_cores is set to = %d\n", - num_cores); - return -ENODEV; - } + if (num_cores != 2 && !data->is_single_core) + return dev_err_probe(dev, -ENODEV, + "MCU cluster requires both R5F cores to be enabled but num_cores is set to = %d\n", + num_cores); - if (num_cores != 1 && data->is_single_core) { - dev_err(dev, "SoC supports only single core R5 but num_cores is set to %d\n", - num_cores); - return -ENODEV; - } + if (num_cores != 1 && data->is_single_core) + return dev_err_probe(dev, -ENODEV, + "SoC supports only single core R5 but num_cores is set to %d\n", + num_cores); platform_set_drvdata(pdev, cluster); ret = devm_of_platform_populate(dev); - if (ret) { - dev_err(dev, "devm_of_platform_populate failed, ret = %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "devm_of_platform_populate failed\n"); ret = k3_r5_cluster_of_init(pdev); - if (ret) { - dev_err(dev, "k3_r5_cluster_of_init failed, ret = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "k3_r5_cluster_of_init failed\n"); ret = devm_add_action_or_reset(dev, k3_r5_cluster_of_exit, pdev); if (ret) return ret; ret = k3_r5_cluster_rproc_init(pdev); - if (ret) { - dev_err(dev, "k3_r5_cluster_rproc_init failed, ret = %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "k3_r5_cluster_rproc_init failed\n"); ret = devm_add_action_or_reset(dev, k3_r5_cluster_rproc_exit, pdev); if (ret) diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c index 36a55f7ffa64d..d8be21e717212 100644 --- a/drivers/remoteproc/wkup_m3_rproc.c +++ b/drivers/remoteproc/wkup_m3_rproc.c @@ -251,7 +251,7 @@ static const struct dev_pm_ops wkup_m3_rproc_pm_ops = { static struct platform_driver wkup_m3_rproc_driver = { .probe = wkup_m3_rproc_probe, - .remove_new = wkup_m3_rproc_remove, + .remove = wkup_m3_rproc_remove, .driver = { .name = "wkup_m3_rproc", .of_match_table = wkup_m3_rproc_of_match, diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index d3af1dfa3c7d7..a2f9d85c7156d 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1204,7 +1204,8 @@ void qcom_glink_native_rx(struct qcom_glink *glink) ret = qcom_glink_rx_open_ack(glink, param1); break; case GLINK_CMD_OPEN: - ret = qcom_glink_rx_defer(glink, param2); + /* upper 16 bits of param2 are the "prio" field */ + ret = qcom_glink_rx_defer(glink, param2 & 0xffff); break; case GLINK_CMD_TX_DATA: case GLINK_CMD_TX_DATA_CONT: diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 66eb1122248b6..a60bcc791a480 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -182,6 +182,16 @@ config RTC_DRV_88PM80X This driver can also be built as a module. If so, the module will be called rtc-88pm80x. +config RTC_DRV_88PM886 + tristate "Marvell 88PM886 RTC driver" + depends on MFD_88PM886_PMIC + help + If you say yes here you will get support for the RTC function in the + Marvell 88PM886 chip. + + This driver can also be built as a module. If so, the module + will be called rtc-88pm886. + config RTC_DRV_ABB5ZES3 select REGMAP_I2C tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3" @@ -496,6 +506,7 @@ config RTC_DRV_PCF85363 config RTC_DRV_PCF8563 tristate "Philips PCF8563/Epson RTC8564" + select REGMAP_I2C help If you say yes here you get support for the Philips PCF8563 RTC chip. The Epson RTC8564 @@ -2005,6 +2016,16 @@ config RTC_DRV_MA35D1 This driver can also be built as a module, if so, the module will be called "rtc-ma35d1". +config RTC_DRV_RENESAS_RTCA3 + tristate "Renesas RTCA-3 RTC" + depends on ARCH_RENESAS + help + If you say yes here you get support for the Renesas RTCA-3 RTC + available on the Renesas RZ/G3S SoC. + + This driver can also be built as a module, if so, the module + will be called "rtc-rtca3". + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME @@ -2070,4 +2091,16 @@ config RTC_DRV_SSD202D This driver can also be built as a module, if so, the module will be called "rtc-ssd20xd". +config RTC_DRV_AMLOGIC_A4 + tristate "Amlogic RTC" + depends on ARCH_MESON || COMPILE_TEST + select REGMAP_MMIO + default y + help + If you say yes here you get support for the RTC block on the + Amlogic A113L2(A4) and A113X2(A5) SoCs. + + This driver can also be built as a module. If so, the module + will be called "rtc-amlogic-a4". + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f62340ecc5348..489b4ab07068c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -21,11 +21,13 @@ obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += lib_test.o obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o +obj-$(CONFIG_RTC_DRV_88PM886) += rtc-88pm886.o obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o obj-$(CONFIG_RTC_DRV_ABEOZ9) += rtc-ab-eoz9.o obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o +obj-$(CONFIG_RTC_DRV_AMLOGIC_A4) += rtc-amlogic-a4.o obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o @@ -158,13 +160,14 @@ obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o +obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o -obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o +obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index cca650b2e0b94..aaf76406cd7d7 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -904,13 +904,18 @@ void rtc_timer_do_work(struct work_struct *work) struct timerqueue_node *next; ktime_t now; struct rtc_time tm; + int err; struct rtc_device *rtc = container_of(work, struct rtc_device, irqwork); mutex_lock(&rtc->ops_lock); again: - __rtc_read_time(rtc, &tm); + err = __rtc_read_time(rtc, &tm); + if (err) { + mutex_unlock(&rtc->ops_lock); + return; + } now = rtc_tm_to_ktime(tm); while ((next = timerqueue_getnext(&rtc->timerqueue))) { if (next->expires > now) diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c index f40cc06b09797..5c39cf252392d 100644 --- a/drivers/rtc/rtc-88pm80x.c +++ b/drivers/rtc/rtc-88pm80x.c @@ -329,7 +329,7 @@ static struct platform_driver pm80x_rtc_driver = { .pm = &pm80x_rtc_pm_ops, }, .probe = pm80x_rtc_probe, - .remove_new = pm80x_rtc_remove, + .remove = pm80x_rtc_remove, }; module_platform_driver(pm80x_rtc_driver); diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index 0f124ed5b3e56..814230d618427 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -371,7 +371,7 @@ static struct platform_driver pm860x_rtc_driver = { .pm = &pm860x_rtc_pm_ops, }, .probe = pm860x_rtc_probe, - .remove_new = pm860x_rtc_remove, + .remove = pm860x_rtc_remove, }; module_platform_driver(pm860x_rtc_driver); diff --git a/drivers/rtc/rtc-88pm886.c b/drivers/rtc/rtc-88pm886.c new file mode 100644 index 0000000000000..57e9b0a66eed4 --- /dev/null +++ b/drivers/rtc/rtc-88pm886.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include + +#include + +/* + * Time is calculated as the sum of a 32-bit read-only advancing counter and a + * writeable constant offset stored in the chip's spare registers. + */ + +static int pm886_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 time; + u32 buf; + int ret; + + ret = regmap_bulk_read(regmap, PM886_REG_RTC_SPARE1, &buf, 4); + if (ret) + return ret; + time = buf; + + ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4); + if (ret) + return ret; + time += buf; + + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int pm886_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 buf; + int ret; + + ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4); + if (ret) + return ret; + + buf = rtc_tm_to_time64(tm) - buf; + + return regmap_bulk_write(regmap, PM886_REG_RTC_SPARE1, &buf, 4); +} + +static const struct rtc_class_ops pm886_rtc_ops = { + .read_time = pm886_rtc_read_time, + .set_time = pm886_rtc_set_time, +}; + +static int pm886_rtc_probe(struct platform_device *pdev) +{ + struct pm886_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct rtc_device *rtc; + int ret; + + platform_set_drvdata(pdev, chip->regmap); + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return dev_err_probe(dev, PTR_ERR(rtc), + "Failed to allocate RTC device\n"); + + rtc->ops = &pm886_rtc_ops; + rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(rtc); + if (ret) + return dev_err_probe(dev, ret, "Failed to register RTC device\n"); + + return 0; +} + +static const struct platform_device_id pm886_rtc_id_table[] = { + { "88pm886-rtc", }, + { } +}; +MODULE_DEVICE_TABLE(platform, pm886_rtc_id_table); + +static struct platform_driver pm886_rtc_driver = { + .driver = { + .name = "88pm886-rtc", + }, + .probe = pm886_rtc_probe, + .id_table = pm886_rtc_id_table, +}; +module_platform_driver(pm886_rtc_driver); + +MODULE_DESCRIPTION("Marvell 88PM886 RTC driver"); +MODULE_AUTHOR("Karel Balej "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c index 02f7d07112877..d2b60487d4623 100644 --- a/drivers/rtc/rtc-ab-eoz9.c +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -64,7 +64,7 @@ #define ABEOZ9_BIT_ALARM_MIN GENMASK(6, 0) #define ABEOZ9_REG_ALARM_HOURS 0x12 #define ABEOZ9_BIT_ALARM_HOURS_PM BIT(5) -#define ABEOZ9_BIT_ALARM_HOURS GENMASK(4, 0) +#define ABEOZ9_BIT_ALARM_HOURS GENMASK(5, 0) #define ABEOZ9_REG_ALARM_DAYS 0x13 #define ABEOZ9_BIT_ALARM_DAYS GENMASK(5, 0) #define ABEOZ9_REG_ALARM_WEEKDAYS 0x14 @@ -231,8 +231,6 @@ static int abeoz9_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) alarm->time.tm_sec = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_SEC, regs[0])); alarm->time.tm_min = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_MIN, regs[1])); alarm->time.tm_hour = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_HOURS, regs[2])); - if (FIELD_GET(ABEOZ9_BIT_ALARM_HOURS_PM, regs[2])) - alarm->time.tm_hour += 12; alarm->time.tm_mday = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_DAYS, regs[3])); @@ -396,13 +394,6 @@ static int abeoz9z3_temp_read(struct device *dev, if (ret < 0) return ret; - if ((val & ABEOZ9_REG_CTRL_STATUS_V1F) || - (val & ABEOZ9_REG_CTRL_STATUS_V2F)) { - dev_err(dev, - "thermometer might be disabled due to low voltage\n"); - return -EINVAL; - } - switch (attr) { case hwmon_temp_input: ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val); diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 75bb2ac9005c2..2dcda96f4a8ef 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -403,7 +403,7 @@ static struct platform_driver ab8500_rtc_driver = { .name = "ab8500-rtc", }, .probe = ab8500_rtc_probe, - .remove_new = ab8500_rtc_remove, + .remove = ab8500_rtc_remove, .id_table = ab85xx_rtc_ids, }; diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 1298962402ff4..3fee27914ba80 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -39,7 +39,7 @@ #define ABX8XX_REG_STATUS 0x0f #define ABX8XX_STATUS_AF BIT(2) #define ABX8XX_STATUS_BLF BIT(4) -#define ABX8XX_STATUS_WDT BIT(6) +#define ABX8XX_STATUS_WDT BIT(5) #define ABX8XX_REG_CTRL1 0x10 #define ABX8XX_CTRL_WRITE BIT(0) diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index fa642bba3cee0..33626311fa781 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -628,7 +628,7 @@ MODULE_DEVICE_TABLE(of, ac100_rtc_match); static struct platform_driver ac100_rtc_driver = { .probe = ac100_rtc_probe, - .remove_new = ac100_rtc_remove, + .remove = ac100_rtc_remove, .driver = { .name = "ac100-rtc", .of_match_table = of_match_ptr(ac100_rtc_match), diff --git a/drivers/rtc/rtc-amlogic-a4.c b/drivers/rtc/rtc-amlogic-a4.c new file mode 100644 index 0000000000000..2278b4c98a711 --- /dev/null +++ b/drivers/rtc/rtc-amlogic-a4.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + * Author: Yiting Deng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* rtc oscillator rate */ +#define OSC_32K 32768 +#define OSC_24M 24000000 + +#define RTC_CTRL (0x0 << 2) /* Control RTC */ +#define RTC_ALRM0_EN BIT(0) +#define RTC_OSC_SEL BIT(8) +#define RTC_ENABLE BIT(12) + +#define RTC_COUNTER_REG (0x1 << 2) /* Program RTC counter initial value */ + +#define RTC_ALARM0_REG (0x2 << 2) /* Program RTC alarm0 value */ + +#define RTC_SEC_ADJUST_REG (0x6 << 2) /* Control second-based timing adjustment */ +#define RTC_MATCH_COUNTER GENMASK(18, 0) +#define RTC_SEC_ADJUST_CTRL GENMASK(20, 19) +#define RTC_ADJ_VALID BIT(23) + +#define RTC_INT_MASK (0x8 << 2) /* RTC interrupt mask */ +#define RTC_ALRM0_IRQ_MSK BIT(0) + +#define RTC_INT_CLR (0x9 << 2) /* Clear RTC interrupt */ +#define RTC_ALRM0_IRQ_CLR BIT(0) + +#define RTC_OSCIN_CTRL0 (0xa << 2) /* Control RTC clk from 24M */ +#define RTC_OSCIN_CTRL1 (0xb << 2) /* Control RTC clk from 24M */ +#define RTC_OSCIN_IN_EN BIT(31) +#define RTC_OSCIN_OUT_CFG GENMASK(29, 28) +#define RTC_OSCIN_OUT_N0M0 GENMASK(11, 0) +#define RTC_OSCIN_OUT_N1M1 GENMASK(23, 12) + +#define RTC_INT_STATUS (0xc << 2) /* RTC interrupt status */ +#define RTC_ALRM0_IRQ_STATUS BIT(0) + +#define RTC_REAL_TIME (0xd << 2) /* RTC time value */ + +#define RTC_OSCIN_OUT_32K_N0 0x2dc +#define RTC_OSCIN_OUT_32K_N1 0x2db +#define RTC_OSCIN_OUT_32K_M0 0x1 +#define RTC_OSCIN_OUT_32K_M1 0x2 + +#define RTC_SWALLOW_SECOND 0x2 +#define RTC_INSERT_SECOND 0x3 + +struct aml_rtc_config { + bool gray_stored; +}; + +struct aml_rtc_data { + struct regmap *map; + struct rtc_device *rtc_dev; + int irq; + struct clk *rtc_clk; + struct clk *sys_clk; + int rtc_enabled; + const struct aml_rtc_config *config; +}; + +static const struct regmap_config aml_rtc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = RTC_REAL_TIME, +}; + +static inline u32 gray_to_binary(u32 gray) +{ + u32 bcd = gray; + int size = sizeof(bcd) * 8; + int i; + + for (i = 0; (1 << i) < size; i++) + bcd ^= bcd >> (1 << i); + + return bcd; +} + +static inline u32 binary_to_gray(u32 bcd) +{ + return bcd ^ (bcd >> 1); +} + +static int aml_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 time_sec; + + /* if RTC disabled, read time failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_read(rtc->map, RTC_REAL_TIME, &time_sec); + if (rtc->config->gray_stored) + time_sec = gray_to_binary(time_sec); + rtc_time64_to_tm(time_sec, tm); + dev_dbg(dev, "%s: read time = %us\n", __func__, time_sec); + + return 0; +} + +static int aml_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 time_sec; + + /* if RTC disabled, first enable it */ + if (!rtc->rtc_enabled) { + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, RTC_ENABLE); + usleep_range(100, 200); + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE); + if (!rtc->rtc_enabled) + return -EINVAL; + } + + time_sec = rtc_tm_to_time64(tm); + if (rtc->config->gray_stored) + time_sec = binary_to_gray(time_sec); + regmap_write(rtc->map, RTC_COUNTER_REG, time_sec); + dev_dbg(dev, "%s: set time = %us\n", __func__, time_sec); + + return 0; +} + +static int aml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + time64_t alarm_sec; + + /* if RTC disabled, set alarm failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_update_bits(rtc->map, RTC_CTRL, + RTC_ALRM0_EN, RTC_ALRM0_EN); + regmap_update_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, 0); + + alarm_sec = rtc_tm_to_time64(&alarm->time); + if (rtc->config->gray_stored) + alarm_sec = binary_to_gray(alarm_sec); + regmap_write(rtc->map, RTC_ALARM0_REG, alarm_sec); + + dev_dbg(dev, "%s: alarm->enabled=%d alarm_set=%llds\n", __func__, + alarm->enabled, alarm_sec); + + return 0; +} + +static int aml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 alarm_sec; + int alarm_enable; + int alarm_mask; + + /* if RTC disabled, read alarm failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_read(rtc->map, RTC_ALARM0_REG, &alarm_sec); + if (rtc->config->gray_stored) + alarm_sec = gray_to_binary(alarm_sec); + rtc_time64_to_tm(alarm_sec, &alarm->time); + + alarm_enable = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN); + alarm_mask = regmap_test_bits(rtc->map, RTC_INT_MASK, RTC_ALRM0_IRQ_MSK); + alarm->enabled = (alarm_enable && !alarm_mask) ? 1 : 0; + dev_dbg(dev, "%s: alarm->enabled=%d alarm=%us\n", __func__, + alarm->enabled, alarm_sec); + + return 0; +} + +static int aml_rtc_read_offset(struct device *dev, long *offset) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 reg_val; + long val; + int sign, match_counter, enable; + + /* if RTC disabled, read offset failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_read(rtc->map, RTC_SEC_ADJUST_REG, ®_val); + enable = FIELD_GET(RTC_ADJ_VALID, reg_val); + if (!enable) { + val = 0; + } else { + sign = FIELD_GET(RTC_SEC_ADJUST_CTRL, reg_val); + match_counter = FIELD_GET(RTC_MATCH_COUNTER, reg_val); + val = 1000000000 / (match_counter + 1); + if (sign == RTC_SWALLOW_SECOND) + val = -val; + } + *offset = val; + + return 0; +} + +static int aml_rtc_set_offset(struct device *dev, long offset) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + int sign = 0; + int match_counter = 0; + int enable = 0; + u32 reg_val; + + /* if RTC disabled, set offset failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + if (offset) { + enable = 1; + sign = offset < 0 ? RTC_SWALLOW_SECOND : RTC_INSERT_SECOND; + match_counter = 1000000000 / abs(offset) - 1; + if (match_counter < 0 || match_counter > RTC_MATCH_COUNTER) + return -EINVAL; + } + + reg_val = FIELD_PREP(RTC_ADJ_VALID, enable) | + FIELD_PREP(RTC_SEC_ADJUST_CTRL, sign) | + FIELD_PREP(RTC_MATCH_COUNTER, match_counter); + regmap_write(rtc->map, RTC_SEC_ADJUST_REG, reg_val); + + return 0; +} + +static int aml_rtc_alarm_enable(struct device *dev, unsigned int enabled) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + + if (enabled) { + regmap_update_bits(rtc->map, RTC_CTRL, + RTC_ALRM0_EN, RTC_ALRM0_EN); + regmap_update_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, 0); + } else { + regmap_update_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK); + regmap_update_bits(rtc->map, RTC_CTRL, + RTC_ALRM0_EN, 0); + } + + return 0; +} + +static const struct rtc_class_ops aml_rtc_ops = { + .read_time = aml_rtc_read_time, + .set_time = aml_rtc_set_time, + .read_alarm = aml_rtc_read_alarm, + .set_alarm = aml_rtc_set_alarm, + .alarm_irq_enable = aml_rtc_alarm_enable, + .read_offset = aml_rtc_read_offset, + .set_offset = aml_rtc_set_offset, +}; + +static irqreturn_t aml_rtc_handler(int irq, void *data) +{ + struct aml_rtc_data *rtc = (struct aml_rtc_data *)data; + + regmap_write(rtc->map, RTC_ALARM0_REG, 0); + regmap_write(rtc->map, RTC_INT_CLR, RTC_ALRM0_IRQ_STATUS); + + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; +} + +static void aml_rtc_init(struct aml_rtc_data *rtc) +{ + u32 reg_val = 0; + + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE); + if (!rtc->rtc_enabled) { + if (clk_get_rate(rtc->rtc_clk) == OSC_24M) { + /* select 24M oscillator */ + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, RTC_OSC_SEL); + + /* + * Set RTC oscillator to freq_out to freq_in/((N0*M0+N1*M1)/(M0+M1)) + * Enable clock_in gate of oscillator 24MHz + * Set N0 to 733, N1 to 732 + */ + reg_val = FIELD_PREP(RTC_OSCIN_IN_EN, 1) + | FIELD_PREP(RTC_OSCIN_OUT_CFG, 1) + | FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_N0) + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_N1); + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL0, RTC_OSCIN_IN_EN + | RTC_OSCIN_OUT_CFG | RTC_OSCIN_OUT_N0M0 + | RTC_OSCIN_OUT_N1M1, reg_val); + + /* Set M0 to 2, M1 to 3, so freq_out = 32768 Hz*/ + reg_val = FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_M0) + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_M1); + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL1, RTC_OSCIN_OUT_N0M0 + | RTC_OSCIN_OUT_N1M1, reg_val); + } else { + /* select 32K oscillator */ + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, 0); + } + } + regmap_write_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK); + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN, 0); +} + +static int aml_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aml_rtc_data *rtc; + void __iomem *base; + int ret = 0; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->config = of_device_get_match_data(dev); + if (!rtc->config) + return -ENODEV; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n"); + + rtc->map = devm_regmap_init_mmio(dev, base, &aml_rtc_regmap_config); + if (IS_ERR(rtc->map)) + return dev_err_probe(dev, PTR_ERR(rtc->map), "regmap init failed\n"); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->rtc_clk = devm_clk_get(dev, "osc"); + if (IS_ERR(rtc->rtc_clk)) + return dev_err_probe(dev, PTR_ERR(rtc->rtc_clk), + "failed to find rtc clock\n"); + if (clk_get_rate(rtc->rtc_clk) != OSC_32K && clk_get_rate(rtc->rtc_clk) != OSC_24M) + return dev_err_probe(dev, -EINVAL, "Invalid clock configuration\n"); + + rtc->sys_clk = devm_clk_get_enabled(dev, "sys"); + if (IS_ERR(rtc->sys_clk)) + return dev_err_probe(dev, PTR_ERR(rtc->sys_clk), + "failed to get_enable rtc sys clk\n"); + aml_rtc_init(rtc); + + device_init_wakeup(dev, 1); + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + goto err_clk; + } + + ret = devm_request_irq(dev, rtc->irq, aml_rtc_handler, + IRQF_ONESHOT, "aml-rtc alarm", rtc); + if (ret) { + dev_err_probe(dev, ret, "IRQ%d request failed, ret = %d\n", + rtc->irq, ret); + goto err_clk; + } + + rtc->rtc_dev->ops = &aml_rtc_ops; + rtc->rtc_dev->range_min = 0; + rtc->rtc_dev->range_max = U32_MAX; + + ret = devm_rtc_register_device(rtc->rtc_dev); + if (ret) { + dev_err_probe(&pdev->dev, ret, "Failed to register RTC device: %d\n", ret); + goto err_clk; + } + + return 0; +err_clk: + clk_disable_unprepare(rtc->sys_clk); + device_init_wakeup(dev, 0); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int aml_rtc_suspend(struct device *dev) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + + return 0; +} + +static int aml_rtc_resume(struct device *dev) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(aml_rtc_pm_ops, + aml_rtc_suspend, aml_rtc_resume); + +static void aml_rtc_remove(struct platform_device *pdev) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(rtc->sys_clk); + device_init_wakeup(&pdev->dev, 0); +} + +static const struct aml_rtc_config a5_rtc_config = { +}; + +static const struct aml_rtc_config a4_rtc_config = { + .gray_stored = true, +}; + +static const struct of_device_id aml_rtc_device_id[] = { + { + .compatible = "amlogic,a4-rtc", + .data = &a4_rtc_config, + }, + { + .compatible = "amlogic,a5-rtc", + .data = &a5_rtc_config, + }, + { } +}; +MODULE_DEVICE_TABLE(of, aml_rtc_device_id); + +static struct platform_driver aml_rtc_driver = { + .probe = aml_rtc_probe, + .remove = aml_rtc_remove, + .driver = { + .name = "aml-rtc", + .pm = &aml_rtc_pm_ops, + .of_match_table = aml_rtc_device_id, + }, +}; + +module_platform_driver(aml_rtc_driver); +MODULE_DESCRIPTION("Amlogic RTC driver"); +MODULE_AUTHOR("Yiting Deng "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-asm9260.c b/drivers/rtc/rtc-asm9260.c index a83b47e0d8f53..705470ae84280 100644 --- a/drivers/rtc/rtc-asm9260.c +++ b/drivers/rtc/rtc-asm9260.c @@ -325,7 +325,7 @@ MODULE_DEVICE_TABLE(of, asm9260_dt_ids); static struct platform_driver asm9260_rtc_driver = { .probe = asm9260_rtc_probe, - .remove_new = asm9260_rtc_remove, + .remove = asm9260_rtc_remove, .driver = { .name = "asm9260-rtc", .of_match_table = asm9260_dt_ids, diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index c16fe711a0d94..9b3898b8de7cf 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -640,7 +640,7 @@ static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); * triggering a section mismatch warning. */ static struct platform_driver at91_rtc_driver __refdata = { - .remove_new = __exit_p(at91_rtc_remove), + .remove = __exit_p(at91_rtc_remove), .shutdown = at91_rtc_shutdown, .driver = { .name = "at91_rtc", diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 993c0878fb660..15b21da2788f6 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); static struct platform_driver at91_rtc_driver = { .probe = at91_rtc_probe, - .remove_new = at91_rtc_remove, + .remove = at91_rtc_remove, .shutdown = at91_rtc_shutdown, .driver = { .name = "rtc-at91sam9", diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c index 1a65a4e0dc003..fb47c32ab5ff4 100644 --- a/drivers/rtc/rtc-brcmstb-waketimer.c +++ b/drivers/rtc/rtc-brcmstb-waketimer.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -417,7 +416,7 @@ static const __maybe_unused struct of_device_id brcmstb_waketmr_of_match[] = { static struct platform_driver brcmstb_waketmr_driver = { .probe = brcmstb_waketmr_probe, - .remove_new = brcmstb_waketmr_remove, + .remove = brcmstb_waketmr_remove, .driver = { .name = "brcmstb-waketimer", .pm = &brcmstb_waketmr_pm_ops, diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c index 4ca60b5198365..bf2a9a1fdea74 100644 --- a/drivers/rtc/rtc-cadence.c +++ b/drivers/rtc/rtc-cadence.c @@ -402,7 +402,7 @@ static struct platform_driver cdns_rtc_driver = { .pm = &cdns_rtc_pm_ops, }, .probe = cdns_rtc_probe, - .remove_new = cdns_rtc_remove, + .remove = cdns_rtc_remove, }; module_platform_driver(cdns_rtc_driver); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 35dca2accbb8d..78f2ce12c75a7 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -645,18 +645,17 @@ static int cmos_nvram_read(void *priv, unsigned int off, void *val, unsigned char *buf = val; off += NVRAM_OFFSET; - spin_lock_irq(&rtc_lock); - for (; count; count--, off++) { + for (; count; count--, off++, buf++) { + guard(spinlock_irq)(&rtc_lock); if (off < 128) - *buf++ = CMOS_READ(off); + *buf = CMOS_READ(off); else if (can_bank2) - *buf++ = cmos_read_bank2(off); + *buf = cmos_read_bank2(off); else - break; + return -EIO; } - spin_unlock_irq(&rtc_lock); - return count ? -EIO : 0; + return 0; } static int cmos_nvram_write(void *priv, unsigned int off, void *val, @@ -671,23 +670,23 @@ static int cmos_nvram_write(void *priv, unsigned int off, void *val, * NVRAM to update, updating checksums is also part of its job. */ off += NVRAM_OFFSET; - spin_lock_irq(&rtc_lock); - for (; count; count--, off++) { + for (; count; count--, off++, buf++) { /* don't trash RTC registers */ if (off == cmos->day_alrm || off == cmos->mon_alrm || off == cmos->century) - buf++; - else if (off < 128) - CMOS_WRITE(*buf++, off); + continue; + + guard(spinlock_irq)(&rtc_lock); + if (off < 128) + CMOS_WRITE(*buf, off); else if (can_bank2) - cmos_write_bank2(*buf++, off); + cmos_write_bank2(*buf, off); else - break; + return -EIO; } - spin_unlock_irq(&rtc_lock); - return count ? -EIO : 0; + return 0; } /*----------------------------------------------------------------*/ @@ -1528,7 +1527,7 @@ static void cmos_platform_shutdown(struct platform_device *pdev) MODULE_ALIAS("platform:rtc_cmos"); static struct platform_driver cmos_platform_driver = { - .remove_new = cmos_platform_remove, + .remove = cmos_platform_remove, .shutdown = cmos_platform_shutdown, .driver = { .name = driver_name, diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c index f57462c7b2c64..60a48c3ba3ca5 100644 --- a/drivers/rtc/rtc-cros-ec.c +++ b/drivers/rtc/rtc-cros-ec.c @@ -401,7 +401,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id); static struct platform_driver cros_ec_rtc_driver = { .probe = cros_ec_rtc_probe, - .remove_new = cros_ec_rtc_remove, + .remove = cros_ec_rtc_remove, .driver = { .name = DRV_NAME, .pm = &cros_ec_rtc_pm_ops, diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c index 04dbf35cf3b70..38e25f63597ab 100644 --- a/drivers/rtc/rtc-ds1685.c +++ b/drivers/rtc/rtc-ds1685.c @@ -1354,7 +1354,7 @@ static struct platform_driver ds1685_rtc_driver = { .name = "rtc-ds1685", }, .probe = ds1685_rtc_probe, - .remove_new = ds1685_rtc_remove, + .remove = ds1685_rtc_remove, }; module_platform_driver(ds1685_rtc_driver); /* ----------------------------------------------------------------------- */ diff --git a/drivers/rtc/rtc-ftrtc010.c b/drivers/rtc/rtc-ftrtc010.c index 8bfe7378f6533..cb4a5d101f537 100644 --- a/drivers/rtc/rtc-ftrtc010.c +++ b/drivers/rtc/rtc-ftrtc010.c @@ -214,7 +214,7 @@ static struct platform_driver ftrtc010_rtc_driver = { .of_match_table = ftrtc010_rtc_dt_match, }, .probe = ftrtc010_rtc_probe, - .remove_new = ftrtc010_rtc_remove, + .remove = ftrtc010_rtc_remove, }; module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe); diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c index b81cea505ee93..e30f80dc93199 100644 --- a/drivers/rtc/rtc-hid-sensor-time.c +++ b/drivers/rtc/rtc-hid-sensor-time.c @@ -319,7 +319,7 @@ static struct platform_driver hid_time_platform_driver = { .name = KBUILD_MODNAME, }, .probe = hid_time_probe, - .remove_new = hid_time_remove, + .remove = hid_time_remove, }; module_platform_driver(hid_time_platform_driver); diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index 284011c419db9..ca4a0af95e8c5 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -860,7 +860,7 @@ static struct platform_driver dryice_rtc_driver __refdata = { .name = "imxdi_rtc", .of_match_table = dryice_dt_ids, }, - .remove_new = __exit_p(dryice_rtc_remove), + .remove = __exit_p(dryice_rtc_remove), }; module_platform_driver_probe(dryice_rtc_driver, dryice_rtc_probe); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 6fa9a68af9d98..9b44839a7402c 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -21,7 +21,7 @@ #include -/* ISL register offsets */ +/* RTC - Real time clock registers */ #define ISL12022_REG_SC 0x00 #define ISL12022_REG_MN 0x01 #define ISL12022_REG_HR 0x02 @@ -30,21 +30,36 @@ #define ISL12022_REG_YR 0x05 #define ISL12022_REG_DW 0x06 +/* CSR - Control and status registers */ #define ISL12022_REG_SR 0x07 #define ISL12022_REG_INT 0x08 - #define ISL12022_REG_PWR_VBAT 0x0a - #define ISL12022_REG_BETA 0x0d + +/* ALARM - Alarm registers */ +#define ISL12022_REG_SCA0 0x10 +#define ISL12022_REG_MNA0 0x11 +#define ISL12022_REG_HRA0 0x12 +#define ISL12022_REG_DTA0 0x13 +#define ISL12022_REG_MOA0 0x14 +#define ISL12022_REG_DWA0 0x15 +#define ISL12022_ALARM ISL12022_REG_SCA0 +#define ISL12022_ALARM_LEN (ISL12022_REG_DWA0 - ISL12022_REG_SCA0 + 1) + +/* TEMP - Temperature sensor registers */ #define ISL12022_REG_TEMP_L 0x28 /* ISL register bits */ #define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */ +#define ISL12022_SR_ALM (1 << 4) #define ISL12022_SR_LBAT85 (1 << 2) #define ISL12022_SR_LBAT75 (1 << 1) +#define ISL12022_INT_ARST (1 << 7) #define ISL12022_INT_WRTC (1 << 6) +#define ISL12022_INT_IM (1 << 5) +#define ISL12022_INT_FOBATB (1 << 4) #define ISL12022_INT_FO_MASK GENMASK(3, 0) #define ISL12022_INT_FO_OFF 0x0 #define ISL12022_INT_FO_32K 0x1 @@ -52,8 +67,19 @@ #define ISL12022_REG_VB85_MASK GENMASK(5, 3) #define ISL12022_REG_VB75_MASK GENMASK(2, 0) +#define ISL12022_ALARM_ENABLE (1 << 7) /* for all ALARM registers */ + #define ISL12022_BETA_TSE (1 << 7) +static struct i2c_driver isl12022_driver; + +struct isl12022 { + struct rtc_device *rtc; + struct regmap *regmap; + int irq; + bool irq_enabled; +}; + static umode_t isl12022_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) @@ -116,7 +142,8 @@ static const struct hwmon_chip_info isl12022_hwmon_chip_info = { static void isl12022_hwmon_register(struct device *dev) { - struct regmap *regmap = dev_get_drvdata(dev); + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; struct device *hwmon; int ret; @@ -143,8 +170,9 @@ static void isl12022_hwmon_register(struct device *dev) */ static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct regmap *regmap = dev_get_drvdata(dev); - uint8_t buf[ISL12022_REG_INT + 1]; + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u8 buf[ISL12022_REG_INT + 1]; int ret; ret = regmap_bulk_read(regmap, ISL12022_REG_SC, buf, sizeof(buf)); @@ -178,9 +206,10 @@ static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct regmap *regmap = dev_get_drvdata(dev); + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; int ret; - uint8_t buf[ISL12022_REG_DW + 1]; + u8 buf[ISL12022_REG_DW + 1]; dev_dbg(dev, "%s: %ptR\n", __func__, tm); @@ -208,9 +237,198 @@ static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) return regmap_bulk_write(regmap, ISL12022_REG_SC, buf, sizeof(buf)); } +static int isl12022_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc_time *tm = &alarm->time; + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u8 buf[ISL12022_ALARM_LEN]; + unsigned int i, yr; + int ret; + + ret = regmap_bulk_read(regmap, ISL12022_ALARM, buf, sizeof(buf)); + if (ret) { + dev_dbg(dev, "%s: reading ALARM registers failed\n", + __func__); + return ret; + } + + /* The alarm doesn't store the year so get it from the rtc section */ + ret = regmap_read(regmap, ISL12022_REG_YR, &yr); + if (ret) { + dev_dbg(dev, "%s: reading YR register failed\n", __func__); + return ret; + } + + dev_dbg(dev, + "%s: sc=%02x, mn=%02x, hr=%02x, dt=%02x, mo=%02x, dw=%02x yr=%u\n", + __func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], yr); + + tm->tm_sec = bcd2bin(buf[ISL12022_REG_SCA0 - ISL12022_ALARM] & 0x7F); + tm->tm_min = bcd2bin(buf[ISL12022_REG_MNA0 - ISL12022_ALARM] & 0x7F); + tm->tm_hour = bcd2bin(buf[ISL12022_REG_HRA0 - ISL12022_ALARM] & 0x3F); + tm->tm_mday = bcd2bin(buf[ISL12022_REG_DTA0 - ISL12022_ALARM] & 0x3F); + tm->tm_mon = bcd2bin(buf[ISL12022_REG_MOA0 - ISL12022_ALARM] & 0x1F) - 1; + tm->tm_wday = buf[ISL12022_REG_DWA0 - ISL12022_ALARM] & 0x07; + tm->tm_year = bcd2bin(yr) + 100; + + for (i = 0; i < ISL12022_ALARM_LEN; i++) { + if (buf[i] & ISL12022_ALARM_ENABLE) { + alarm->enabled = 1; + break; + } + } + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + return 0; +} + +static int isl12022_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc_time *alarm_tm = &alarm->time; + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u8 regs[ISL12022_ALARM_LEN] = { 0, }; + struct rtc_time rtc_tm; + int ret, enable, dw; + + ret = isl12022_rtc_read_time(dev, &rtc_tm); + if (ret) + return ret; + + /* If the alarm time is before the current time disable the alarm */ + if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0) + enable = 0; + else + enable = ISL12022_ALARM_ENABLE; + + /* + * Set non-matching day of the week to safeguard against early false + * matching while setting all the alarm registers (this rtc lacks a + * general alarm/irq enable/disable bit). + */ + ret = regmap_read(regmap, ISL12022_REG_DW, &dw); + if (ret) { + dev_dbg(dev, "%s: reading DW failed\n", __func__); + return ret; + } + /* ~4 days into the future should be enough to avoid match */ + dw = ((dw + 4) % 7) | ISL12022_ALARM_ENABLE; + ret = regmap_write(regmap, ISL12022_REG_DWA0, dw); + if (ret) { + dev_dbg(dev, "%s: writing DWA0 failed\n", __func__); + return ret; + } + + /* Program the alarm and enable it for each setting */ + regs[ISL12022_REG_SCA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_sec) | enable; + regs[ISL12022_REG_MNA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_min) | enable; + regs[ISL12022_REG_HRA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_hour) | enable; + regs[ISL12022_REG_DTA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mday) | enable; + regs[ISL12022_REG_MOA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mon + 1) | enable; + regs[ISL12022_REG_DWA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_wday & 7) | enable; + + /* write ALARM registers */ + ret = regmap_bulk_write(regmap, ISL12022_ALARM, ®s, sizeof(regs)); + if (ret) { + dev_dbg(dev, "%s: writing ALARM registers failed\n", __func__); + return ret; + } + + return 0; +} + +static irqreturn_t isl12022_rtc_interrupt(int irq, void *data) +{ + struct isl12022 *isl12022 = data; + struct rtc_device *rtc = isl12022->rtc; + struct device *dev = &rtc->dev; + struct regmap *regmap = isl12022->regmap; + u32 val = 0; + unsigned long events = 0; + int ret; + + ret = regmap_read(regmap, ISL12022_REG_SR, &val); + if (ret) { + dev_dbg(dev, "%s: reading SR failed\n", __func__); + return IRQ_HANDLED; + } + + if (val & ISL12022_SR_ALM) + events |= RTC_IRQF | RTC_AF; + + if (events & RTC_AF) + dev_dbg(dev, "alarm!\n"); + + if (!events) + return IRQ_NONE; + + rtc_update_irq(rtc, 1, events); + return IRQ_HANDLED; +} + +static int isl12022_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + + /* Make sure enabled is 0 or 1 */ + enabled = !!enabled; + + if (isl12022->irq_enabled == enabled) + return 0; + + if (enabled) + enable_irq(isl12022->irq); + else + disable_irq(isl12022->irq); + + isl12022->irq_enabled = enabled; + + return 0; +} + +static int isl12022_setup_irq(struct device *dev, int irq) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + unsigned int reg_mask, reg_val; + u8 buf[ISL12022_ALARM_LEN] = { 0, }; + int ret; + + /* Clear and disable all alarm registers */ + ret = regmap_bulk_write(regmap, ISL12022_ALARM, buf, sizeof(buf)); + if (ret) + return ret; + + /* + * Enable automatic reset of ALM bit and enable single event interrupt + * mode. + */ + reg_mask = ISL12022_INT_ARST | ISL12022_INT_IM | ISL12022_INT_FO_MASK; + reg_val = ISL12022_INT_ARST | ISL12022_INT_FO_OFF; + ret = regmap_write_bits(regmap, ISL12022_REG_INT, + reg_mask, reg_val); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, irq, NULL, + isl12022_rtc_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + isl12022_driver.driver.name, + isl12022); + if (ret) + return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq); + + isl12022->irq = irq; + return 0; +} + static int isl12022_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { - struct regmap *regmap = dev_get_drvdata(dev); + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; u32 user, val; int ret; @@ -238,6 +456,9 @@ static const struct rtc_class_ops isl12022_rtc_ops = { .ioctl = isl12022_rtc_ioctl, .read_time = isl12022_rtc_read_time, .set_time = isl12022_rtc_set_time, + .read_alarm = isl12022_rtc_read_alarm, + .set_alarm = isl12022_rtc_set_alarm, + .alarm_irq_enable = isl12022_rtc_alarm_irq_enable, }; static const struct regmap_config regmap_config = { @@ -248,7 +469,8 @@ static const struct regmap_config regmap_config = { static int isl12022_register_clock(struct device *dev) { - struct regmap *regmap = dev_get_drvdata(dev); + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; struct clk_hw *hw; int ret; @@ -288,7 +510,8 @@ static const u32 trip_levels[2][7] = { static void isl12022_set_trip_levels(struct device *dev) { - struct regmap *regmap = dev_get_drvdata(dev); + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; u32 levels[2] = {0, 0}; int ret, i, j, x[2]; u8 val, mask; @@ -325,6 +548,7 @@ static void isl12022_set_trip_levels(struct device *dev) static int isl12022_probe(struct i2c_client *client) { + struct isl12022 *isl12022; struct rtc_device *rtc; struct regmap *regmap; int ret; @@ -332,13 +556,17 @@ static int isl12022_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; + /* Allocate driver state */ + isl12022 = devm_kzalloc(&client->dev, sizeof(*isl12022), GFP_KERNEL); + if (!isl12022) + return -ENOMEM; + regmap = devm_regmap_init_i2c(client, ®map_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "regmap allocation failed\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), "regmap allocation failed\n"); + isl12022->regmap = regmap; - dev_set_drvdata(&client->dev, regmap); + dev_set_drvdata(&client->dev, isl12022); ret = isl12022_register_clock(&client->dev); if (ret) @@ -350,11 +578,20 @@ static int isl12022_probe(struct i2c_client *client) rtc = devm_rtc_allocate_device(&client->dev); if (IS_ERR(rtc)) return PTR_ERR(rtc); + isl12022->rtc = rtc; rtc->ops = &isl12022_rtc_ops; rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->range_max = RTC_TIMESTAMP_END_2099; + if (client->irq > 0) { + ret = isl12022_setup_irq(&client->dev, client->irq); + if (ret) + return ret; + } else { + clear_bit(RTC_FEATURE_ALARM, rtc->features); + } + return devm_rtc_register_device(rtc); } diff --git a/drivers/rtc/rtc-loongson.c b/drivers/rtc/rtc-loongson.c index e8ffc1ab90b02..8d713e563d7c0 100644 --- a/drivers/rtc/rtc-loongson.c +++ b/drivers/rtc/rtc-loongson.c @@ -381,7 +381,7 @@ MODULE_DEVICE_TABLE(acpi, loongson_rtc_acpi_match); static struct platform_driver loongson_rtc_driver = { .probe = loongson_rtc_probe, - .remove_new = loongson_rtc_remove, + .remove = loongson_rtc_remove, .driver = { .name = "loongson-rtc", .of_match_table = loongson_rtc_of_match, diff --git a/drivers/rtc/rtc-lpc24xx.c b/drivers/rtc/rtc-lpc24xx.c index df17c48ff086e..2dcdc77ff646c 100644 --- a/drivers/rtc/rtc-lpc24xx.c +++ b/drivers/rtc/rtc-lpc24xx.c @@ -285,7 +285,7 @@ MODULE_DEVICE_TABLE(of, lpc24xx_rtc_match); static struct platform_driver lpc24xx_rtc_driver = { .probe = lpc24xx_rtc_probe, - .remove_new = lpc24xx_rtc_remove, + .remove = lpc24xx_rtc_remove, .driver = { .name = "lpc24xx-rtc", .of_match_table = lpc24xx_rtc_match, diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 5d30ce8e13ca0..4e608bc8bbd36 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -71,7 +71,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) /* Issue the READ command */ M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); - tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)); + tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)) + pdata->yy_offset; /* tm_mon is 0-11 */ tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1; tm->tm_mday = bcd2bin(M48T59_READ(M48T59_MDAY)); @@ -82,10 +82,6 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) dev_dbg(dev, "Century bit is enabled\n"); tm->tm_year += 100; /* one century */ } -#ifdef CONFIG_SPARC - /* Sun SPARC machines count years since 1968 */ - tm->tm_year += 68; -#endif tm->tm_wday = bcd2bin(val & 0x07); tm->tm_hour = bcd2bin(M48T59_READ(M48T59_HOUR) & 0x3F); @@ -106,12 +102,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) struct m48t59_private *m48t59 = dev_get_drvdata(dev); unsigned long flags; u8 val = 0; - int year = tm->tm_year; - -#ifdef CONFIG_SPARC - /* Sun SPARC machines count years since 1968 */ - year -= 68; -#endif + int year = tm->tm_year - pdata->yy_offset; dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n", year + 1900, tm->tm_mon, tm->tm_mday, @@ -162,11 +153,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) /* Issue the READ command */ M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); - tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)); -#ifdef CONFIG_SPARC - /* Sun SPARC machines count years since 1968 */ - tm->tm_year += 68; -#endif + tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)) + pdata->yy_offset; /* tm_mon is 0-11 */ tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1; @@ -197,12 +184,7 @@ static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) struct rtc_time *tm = &alrm->time; u8 mday, hour, min, sec; unsigned long flags; - int year = tm->tm_year; - -#ifdef CONFIG_SPARC - /* Sun SPARC machines count years since 1968 */ - year -= 68; -#endif + int year = tm->tm_year - pdata->yy_offset; /* If no irq, we don't support ALARM */ if (m48t59->irq == NO_IRQ) diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 35a6021d9ba4c..a8f4b645c09d2 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -875,7 +875,7 @@ static struct platform_driver max77686_rtc_driver = { .pm = &max77686_rtc_pm_ops, }, .probe = max77686_rtc_probe, - .remove_new = max77686_rtc_remove, + .remove = max77686_rtc_remove, .id_table = rtc_id, }; diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 763a42f422eb2..e7b87130e6248 100644 --- a/drivers/rtc/rtc-mc13xxx.c +++ b/drivers/rtc/rtc-mc13xxx.c @@ -350,7 +350,7 @@ MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable); static struct platform_driver mc13xxx_rtc_driver = { .id_table = mc13xxx_rtc_idtable, - .remove_new = mc13xxx_rtc_remove, + .remove = mc13xxx_rtc_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index 651bf3c279c74..dbd2d5835f002 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -216,7 +216,7 @@ int mc146818_set_time(struct rtc_time *time) unsigned char save_control, save_freq_select; unsigned int yrs; #ifdef CONFIG_MACH_DECSTATION - unsigned int real_yrs, leap_yr; + unsigned int real_yrs; #endif unsigned char century = 0; @@ -232,8 +232,6 @@ int mc146818_set_time(struct rtc_time *time) #ifdef CONFIG_MACH_DECSTATION real_yrs = yrs; - leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) || - !((yrs + 1900) % 400)); yrs = 72; /* @@ -241,7 +239,7 @@ int mc146818_set_time(struct rtc_time *time) * for non-leap years, so that Feb, 29th is handled * correctly. */ - if (!leap_yr && mon < 3) { + if (!is_leap_year(real_yrs + 1900) && mon < 3) { real_yrs--; yrs = 73; } diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 71eafe4fbc72b..6003281316031 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -398,7 +398,7 @@ static struct platform_driver mpc5121_rtc_driver = { .of_match_table = of_match_ptr(mpc5121_rtc_match), }, .probe = mpc5121_rtc_probe, - .remove_new = mpc5121_rtc_remove, + .remove = mpc5121_rtc_remove, }; module_platform_driver(mpc5121_rtc_driver); diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index 5b96a6d39210c..3892b0f9917fa 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE(of, mpfs_rtc_of_match); static struct platform_driver mpfs_rtc_driver = { .probe = mpfs_rtc_probe, - .remove_new = mpfs_rtc_remove, + .remove = mpfs_rtc_remove, .driver = { .name = "mpfs_rtc", .of_match_table = mpfs_rtc_of_match, diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c index 1617063669cc5..152699219a2b9 100644 --- a/drivers/rtc/rtc-mt6397.c +++ b/drivers/rtc/rtc-mt6397.c @@ -75,6 +75,7 @@ static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, tm->tm_min = data[RTC_OFFSET_MIN]; tm->tm_hour = data[RTC_OFFSET_HOUR]; tm->tm_mday = data[RTC_OFFSET_DOM]; + tm->tm_wday = data[RTC_OFFSET_DOW]; tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_TC_MTH_MASK; tm->tm_year = data[RTC_OFFSET_YEAR]; @@ -86,9 +87,8 @@ static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm) { - time64_t time; struct mt6397_rtc *rtc = dev_get_drvdata(dev); - int days, sec, ret; + int sec, ret; do { ret = __mtk_rtc_read_time(rtc, tm, &sec); @@ -96,21 +96,9 @@ static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm) goto exit; } while (sec < tm->tm_sec); - /* HW register use 7 bits to store year data, minus - * RTC_MIN_YEAR_OFFSET before write year data to register, and plus - * RTC_MIN_YEAR_OFFSET back after read year from register - */ - tm->tm_year += RTC_MIN_YEAR_OFFSET; - - /* HW register start mon from one, but tm_mon start from zero. */ + /* HW register start mon/wday from one, but tm_mon/tm_wday start from zero. */ tm->tm_mon--; - time = rtc_tm_to_time64(tm); - - /* rtc_tm_to_time64 covert Gregorian date to seconds since - * 01-01-1970 00:00:00, and this date is Thursday. - */ - days = div_s64(time, 86400); - tm->tm_wday = (days + 4) % 7; + tm->tm_wday--; exit: return ret; @@ -122,13 +110,14 @@ static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm) int ret; u16 data[RTC_OFFSET_COUNT]; - tm->tm_year -= RTC_MIN_YEAR_OFFSET; tm->tm_mon++; + tm->tm_wday++; data[RTC_OFFSET_SEC] = tm->tm_sec; data[RTC_OFFSET_MIN] = tm->tm_min; data[RTC_OFFSET_HOUR] = tm->tm_hour; data[RTC_OFFSET_DOM] = tm->tm_mday; + data[RTC_OFFSET_DOW] = tm->tm_wday; data[RTC_OFFSET_MTH] = tm->tm_mon; data[RTC_OFFSET_YEAR] = tm->tm_year; @@ -178,7 +167,6 @@ static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK; tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK; - tm->tm_year += RTC_MIN_YEAR_OFFSET; tm->tm_mon--; return 0; @@ -194,7 +182,6 @@ static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) int ret; u16 data[RTC_OFFSET_COUNT]; - tm->tm_year -= RTC_MIN_YEAR_OFFSET; tm->tm_mon++; mutex_lock(&rtc->lock); @@ -302,6 +289,10 @@ static int mtk_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); rtc->rtc_dev->ops = &mtk_rtc_ops; + rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc->rtc_dev->range_max = mktime64(2027, 12, 31, 23, 59, 59); + rtc->rtc_dev->start_secs = mktime64(1968, 1, 2, 0, 0, 0); + rtc->rtc_dev->set_start_time = true; return devm_rtc_register_device(rtc->rtc_dev); } diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c index 094c649fc137f..4cf0cbb31a313 100644 --- a/drivers/rtc/rtc-mt7622.c +++ b/drivers/rtc/rtc-mt7622.c @@ -394,7 +394,7 @@ static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume); static struct platform_driver mtk_rtc_driver = { .probe = mtk_rtc_probe, - .remove_new = mtk_rtc_remove, + .remove = mtk_rtc_remove, .driver = { .name = MTK_RTC_DEV, .of_match_table = mtk_rtc_match, diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index db31da56bfa74..51029c5362441 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -308,7 +308,7 @@ MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table); * triggering a section mismatch warning. */ static struct platform_driver mv_rtc_driver __refdata = { - .remove_new = __exit_p(mv_rtc_remove), + .remove = __exit_p(mv_rtc_remove), .driver = { .name = "rtc-mv", .of_match_table = of_match_ptr(rtc_mv_of_match_table), diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c index 6934bce4b29fd..13c041bb79f16 100644 --- a/drivers/rtc/rtc-mxc_v2.c +++ b/drivers/rtc/rtc-mxc_v2.c @@ -381,7 +381,7 @@ static struct platform_driver mxc_rtc_driver = { .of_match_table = mxc_ids, }, .probe = mxc_rtc_probe, - .remove_new = mxc_rtc_remove, + .remove = mxc_rtc_remove, }; module_platform_driver(mxc_rtc_driver); diff --git a/drivers/rtc/rtc-nxp-bbnsm.c b/drivers/rtc/rtc-nxp-bbnsm.c index acbfbeb8b0700..fa3b0328c7a25 100644 --- a/drivers/rtc/rtc-nxp-bbnsm.c +++ b/drivers/rtc/rtc-nxp-bbnsm.c @@ -197,13 +197,28 @@ static int bbnsm_rtc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to request irq %d: %d\n", bbnsm->irq, ret); - return ret; + goto err; } bbnsm->rtc->ops = &bbnsm_rtc_ops; bbnsm->rtc->range_max = U32_MAX; - return devm_rtc_register_device(bbnsm->rtc); + ret = devm_rtc_register_device(bbnsm->rtc); + if (ret) + goto err; + + return 0; + +err: + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + return ret; +} + +static void bbnsm_rtc_remove(struct platform_device *pdev) +{ + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); } static const struct of_device_id bbnsm_dt_ids[] = { @@ -218,6 +233,7 @@ static struct platform_driver bbnsm_rtc_driver = { .of_match_table = bbnsm_dt_ids, }, .probe = bbnsm_rtc_probe, + .remove = bbnsm_rtc_remove, }; module_platform_driver(bbnsm_rtc_driver); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index e6b2a9c15b542..c123778e2d9bc 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -1014,7 +1014,7 @@ static void omap_rtc_shutdown(struct platform_device *pdev) static struct platform_driver omap_rtc_driver = { .probe = omap_rtc_probe, - .remove_new = omap_rtc_remove, + .remove = omap_rtc_remove, .shutdown = omap_rtc_shutdown, .driver = { .name = "omap_rtc", diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c index 6971e47c6021e..7256a88b490c9 100644 --- a/drivers/rtc/rtc-palmas.c +++ b/drivers/rtc/rtc-palmas.c @@ -346,7 +346,7 @@ MODULE_DEVICE_TABLE(of, of_palmas_rtc_match); static struct platform_driver palmas_rtc_driver = { .probe = palmas_rtc_probe, - .remove_new = palmas_rtc_remove, + .remove = palmas_rtc_remove, .driver = { .name = "palmas-rtc", .pm = &palmas_rtc_pm_ops, diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c index 23edd11aa40c5..c019c4d91c7d7 100644 --- a/drivers/rtc/rtc-pcf50633.c +++ b/drivers/rtc/rtc-pcf50633.c @@ -273,7 +273,7 @@ static struct platform_driver pcf50633_rtc_driver = { .name = "pcf50633-rtc", }, .probe = pcf50633_rtc_probe, - .remove_new = pcf50633_rtc_remove, + .remove = pcf50633_rtc_remove, }; module_platform_driver(pcf50633_rtc_driver); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 647d52f1f5c5e..5a084d426e58d 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -11,14 +11,15 @@ * https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf */ +#include #include +#include #include -#include -#include -#include #include #include -#include +#include +#include +#include #define PCF8563_REG_ST1 0x00 /* status */ #define PCF8563_REG_ST2 0x01 @@ -77,64 +78,18 @@ struct pcf8563 { */ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ - struct i2c_client *client; + struct regmap *regmap; #ifdef CONFIG_COMMON_CLK struct clk_hw clkout_hw; #endif }; -static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, - unsigned char length, unsigned char *buf) -{ - struct i2c_msg msgs[] = { - {/* setup read ptr */ - .addr = client->addr, - .len = 1, - .buf = ®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = length, - .buf = buf - }, - }; - - if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { - dev_err(&client->dev, "%s: read error\n", __func__); - return -EIO; - } - - return 0; -} - -static int pcf8563_write_block_data(struct i2c_client *client, - unsigned char reg, unsigned char length, - unsigned char *buf) -{ - int i, err; - - for (i = 0; i < length; i++) { - unsigned char data[2] = { reg + i, buf[i] }; - - err = i2c_master_send(client, data, sizeof(data)); - if (err != sizeof(data)) { - dev_err(&client->dev, - "%s: err=%d addr=%02x, data=%02x\n", - __func__, err, data[0], data[1]); - return -EIO; - } - } - - return 0; -} - -static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on) +static int pcf8563_set_alarm_mode(struct pcf8563 *pcf8563, bool on) { - unsigned char buf; + u32 buf; int err; - err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf); + err = regmap_read(pcf8563->regmap, PCF8563_REG_ST2, &buf); if (err < 0) return err; @@ -145,23 +100,17 @@ static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on) buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N); - err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf); - if (err < 0) { - dev_err(&client->dev, "%s: write error\n", __func__); - return -EIO; - } - - return 0; + return regmap_write(pcf8563->regmap, PCF8563_REG_ST2, buf); } -static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en, +static int pcf8563_get_alarm_mode(struct pcf8563 *pcf8563, unsigned char *en, unsigned char *pen) { - unsigned char buf; + u32 buf; int err; - err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf); - if (err) + err = regmap_read(pcf8563->regmap, PCF8563_REG_ST2, &buf); + if (err < 0) return err; if (en) @@ -174,17 +123,17 @@ static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en, static irqreturn_t pcf8563_irq(int irq, void *dev_id) { - struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id); - int err; + struct pcf8563 *pcf8563 = dev_id; char pending; + int err; - err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending); + err = pcf8563_get_alarm_mode(pcf8563, NULL, &pending); if (err) return IRQ_NONE; if (pending) { rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF); - pcf8563_set_alarm_mode(pcf8563->client, 1); + pcf8563_set_alarm_mode(pcf8563, 1); return IRQ_HANDLED; } @@ -197,22 +146,22 @@ static irqreturn_t pcf8563_irq(int irq, void *dev_id) */ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct i2c_client *client = to_i2c_client(dev); - struct pcf8563 *pcf8563 = i2c_get_clientdata(client); + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); unsigned char buf[9]; int err; - err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf); - if (err) + err = regmap_bulk_read(pcf8563->regmap, PCF8563_REG_ST1, buf, + sizeof(buf)); + if (err < 0) return err; if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) { - dev_err(&client->dev, + dev_err(dev, "low voltage detected, date/time is not reliable.\n"); return -EINVAL; } - dev_dbg(&client->dev, + dev_dbg(dev, "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, " "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", __func__, @@ -220,7 +169,6 @@ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) buf[4], buf[5], buf[6], buf[7], buf[8]); - tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F); tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F); tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */ @@ -232,7 +180,7 @@ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ? (tm->tm_year >= 100) : (tm->tm_year < 100); - dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, @@ -243,11 +191,10 @@ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct i2c_client *client = to_i2c_client(dev); - struct pcf8563 *pcf8563 = i2c_get_clientdata(client); + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); unsigned char buf[9]; - dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, @@ -270,22 +217,24 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) buf[PCF8563_REG_DW] = tm->tm_wday & 0x07; - return pcf8563_write_block_data(client, PCF8563_REG_SC, - 9 - PCF8563_REG_SC, buf + PCF8563_REG_SC); + return regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, + buf + PCF8563_REG_SC, + sizeof(buf) - PCF8563_REG_SC); } static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { - struct i2c_client *client = to_i2c_client(dev); + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); int ret; switch (cmd) { case RTC_VL_READ: - ret = i2c_smbus_read_byte_data(client, PCF8563_REG_SC); + ret = regmap_test_bits(pcf8563->regmap, PCF8563_REG_SC, + PCF8563_SC_LV); if (ret < 0) return ret; - return put_user(ret & PCF8563_SC_LV ? RTC_VL_DATA_INVALID : 0, + return put_user(ret ? RTC_VL_DATA_INVALID : 0, (unsigned int __user *)arg); default: return -ENOIOCTLCMD; @@ -294,15 +243,16 @@ static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) { - struct i2c_client *client = to_i2c_client(dev); + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); unsigned char buf[4]; int err; - err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf); - if (err) + err = regmap_bulk_read(pcf8563->regmap, PCF8563_REG_AMN, buf, + sizeof(buf)); + if (err < 0) return err; - dev_dbg(&client->dev, + dev_dbg(dev, "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n", __func__, buf[0], buf[1], buf[2], buf[3]); @@ -312,11 +262,11 @@ static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) tm->time.tm_mday = bcd2bin(buf[2] & 0x3F); tm->time.tm_wday = bcd2bin(buf[3] & 0x7); - err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending); + err = pcf8563_get_alarm_mode(pcf8563, &tm->enabled, &tm->pending); if (err < 0) return err; - dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d," + dev_dbg(dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d," " enabled=%d, pending=%d\n", __func__, tm->time.tm_min, tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday, tm->enabled, tm->pending); @@ -326,7 +276,7 @@ static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) { - struct i2c_client *client = to_i2c_client(dev); + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); unsigned char buf[4]; int err; @@ -335,17 +285,20 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) buf[2] = bin2bcd(tm->time.tm_mday); buf[3] = tm->time.tm_wday & 0x07; - err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf); + err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, buf, + sizeof(buf)); if (err) return err; - return pcf8563_set_alarm_mode(client, !!tm->enabled); + return pcf8563_set_alarm_mode(pcf8563, !!tm->enabled); } static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) { + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + dev_dbg(dev, "%s: en=%d\n", __func__, enabled); - return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); + return pcf8563_set_alarm_mode(pcf8563, !!enabled); } #ifdef CONFIG_COMMON_CLK @@ -366,10 +319,10 @@ static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); - struct i2c_client *client = pcf8563->client; - unsigned char buf; - int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + u32 buf; + int ret; + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); if (ret < 0) return 0; @@ -393,11 +346,10 @@ static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); - struct i2c_client *client = pcf8563->client; - unsigned char buf; - int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); - int i; + int i, ret; + u32 buf; + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); if (ret < 0) return ret; @@ -405,10 +357,10 @@ static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, if (clkout_rates[i] == rate) { buf &= ~PCF8563_REG_CLKO_F_MASK; buf |= i; - ret = pcf8563_write_block_data(client, - PCF8563_REG_CLKO, 1, - &buf); - return ret; + return regmap_update_bits(pcf8563->regmap, + PCF8563_REG_CLKO, + PCF8563_REG_CLKO_F_MASK, + buf); } return -EINVAL; @@ -417,10 +369,10 @@ static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) { struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); - struct i2c_client *client = pcf8563->client; - unsigned char buf; - int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + u32 buf; + int ret; + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); if (ret < 0) return ret; @@ -429,8 +381,8 @@ static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) else buf &= ~PCF8563_REG_CLKO_FE; - ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); - return ret; + return regmap_update_bits(pcf8563->regmap, PCF8563_REG_CLKO, + PCF8563_REG_CLKO_FE, buf); } static int pcf8563_clkout_prepare(struct clk_hw *hw) @@ -446,10 +398,10 @@ static void pcf8563_clkout_unprepare(struct clk_hw *hw) static int pcf8563_clkout_is_prepared(struct clk_hw *hw) { struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); - struct i2c_client *client = pcf8563->client; - unsigned char buf; - int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + u32 buf; + int ret; + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); if (ret < 0) return ret; @@ -467,16 +419,14 @@ static const struct clk_ops pcf8563_clkout_ops = { static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) { - struct i2c_client *client = pcf8563->client; - struct device_node *node = client->dev.of_node; - struct clk *clk; + struct device_node *node = pcf8563->rtc->dev.of_node; struct clk_init_data init; + struct clk *clk; int ret; - unsigned char buf; /* disable the clkout output */ - buf = 0; - ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); + ret = regmap_clear_bits(pcf8563->regmap, PCF8563_REG_CLKO, + PCF8563_REG_CLKO_FE); if (ret < 0) return ERR_PTR(ret); @@ -491,7 +441,7 @@ static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) of_property_read_string(node, "clock-output-names", &init.name); /* register the clock */ - clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw); + clk = devm_clk_register(&pcf8563->rtc->dev, &pcf8563->clkout_hw); if (!IS_ERR(clk)) of_clk_add_provider(node, of_clk_src_simple_get, clk); @@ -509,11 +459,16 @@ static const struct rtc_class_ops pcf8563_rtc_ops = { .alarm_irq_enable = pcf8563_irq_enable, }; +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xF, +}; + static int pcf8563_probe(struct i2c_client *client) { struct pcf8563 *pcf8563; int err; - unsigned char buf; dev_dbg(&client->dev, "%s\n", __func__); @@ -525,20 +480,23 @@ static int pcf8563_probe(struct i2c_client *client) if (!pcf8563) return -ENOMEM; + pcf8563->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(pcf8563->regmap)) + return PTR_ERR(pcf8563->regmap); + i2c_set_clientdata(client, pcf8563); - pcf8563->client = client; + device_set_wakeup_capable(&client->dev, 1); /* Set timer to lowest frequency to save power (ref Haoyu datasheet) */ - buf = PCF8563_TMRC_1_60; - err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf); + err = regmap_set_bits(pcf8563->regmap, PCF8563_REG_TMRC, + PCF8563_TMRC_1_60); if (err < 0) { dev_err(&client->dev, "%s: write error\n", __func__); return err; } /* Clear flags and disable interrupts */ - buf = 0; - err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf); + err = regmap_write(pcf8563->regmap, PCF8563_REG_ST2, 0); if (err < 0) { dev_err(&client->dev, "%s: write error\n", __func__); return err; diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c index 4f85e0c3d7572..bed3c27e665f3 100644 --- a/drivers/rtc/rtc-pic32.c +++ b/drivers/rtc/rtc-pic32.c @@ -371,7 +371,7 @@ MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids); static struct platform_driver pic32_rtc_driver = { .probe = pic32_rtc_probe, - .remove_new = pic32_rtc_remove, + .remove = pic32_rtc_remove, .driver = { .name = "pic32-rtc", .of_match_table = of_match_ptr(pic32_rtc_dt_ids), diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index c32fba550c8e0..2f32187ecc8d3 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -537,7 +537,7 @@ static void pm8xxx_remove(struct platform_device *pdev) static struct platform_driver pm8xxx_rtc_driver = { .probe = pm8xxx_rtc_probe, - .remove_new = pm8xxx_remove, + .remove = pm8xxx_remove, .driver = { .name = "rtc-pm8xxx", .of_match_table = pm8xxx_id_table, diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index cdb39fc4cab52..34d8545c8e155 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -409,7 +409,7 @@ static SIMPLE_DEV_PM_OPS(pxa_rtc_pm_ops, pxa_rtc_suspend, pxa_rtc_resume); * triggering a section mismatch warning. */ static struct platform_driver pxa_rtc_driver __refdata = { - .remove_new = __exit_p(pxa_rtc_remove), + .remove = __exit_p(pxa_rtc_remove), .driver = { .name = "pxa-rtc", .of_match_table = of_match_ptr(pxa_rtc_dt_ids), diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c index 115c46f862f95..eecb49bab56ad 100644 --- a/drivers/rtc/rtc-rc5t583.c +++ b/drivers/rtc/rtc-rc5t583.c @@ -298,7 +298,7 @@ static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend, static struct platform_driver rc5t583_rtc_driver = { .probe = rc5t583_rtc_probe, - .remove_new = rc5t583_rtc_remove, + .remove = rc5t583_rtc_remove, .driver = { .name = "rtc-rc5t583", .pm = &rc5t583_rtc_pm_ops, diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c new file mode 100644 index 0000000000000..d127933bfc8ad --- /dev/null +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * On-Chip RTC Support available on RZ/G3S SoC + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Counter registers. */ +#define RTCA3_RSECCNT 0x2 +#define RTCA3_RSECCNT_SEC GENMASK(6, 0) +#define RTCA3_RMINCNT 0x4 +#define RTCA3_RMINCNT_MIN GENMASK(6, 0) +#define RTCA3_RHRCNT 0x6 +#define RTCA3_RHRCNT_HR GENMASK(5, 0) +#define RTCA3_RHRCNT_PM BIT(6) +#define RTCA3_RWKCNT 0x8 +#define RTCA3_RWKCNT_WK GENMASK(2, 0) +#define RTCA3_RDAYCNT 0xa +#define RTCA3_RDAYCNT_DAY GENMASK(5, 0) +#define RTCA3_RMONCNT 0xc +#define RTCA3_RMONCNT_MONTH GENMASK(4, 0) +#define RTCA3_RYRCNT 0xe +#define RTCA3_RYRCNT_YEAR GENMASK(7, 0) + +/* Alarm registers. */ +#define RTCA3_RSECAR 0x10 +#define RTCA3_RSECAR_SEC GENMASK(6, 0) +#define RTCA3_RMINAR 0x12 +#define RTCA3_RMINAR_MIN GENMASK(6, 0) +#define RTCA3_RHRAR 0x14 +#define RTCA3_RHRAR_HR GENMASK(5, 0) +#define RTCA3_RHRAR_PM BIT(6) +#define RTCA3_RWKAR 0x16 +#define RTCA3_RWKAR_DAYW GENMASK(2, 0) +#define RTCA3_RDAYAR 0x18 +#define RTCA3_RDAYAR_DATE GENMASK(5, 0) +#define RTCA3_RMONAR 0x1a +#define RTCA3_RMONAR_MON GENMASK(4, 0) +#define RTCA3_RYRAR 0x1c +#define RTCA3_RYRAR_YR GENMASK(7, 0) +#define RTCA3_RYRAREN 0x1e + +/* Alarm enable bit (for all alarm registers). */ +#define RTCA3_AR_ENB BIT(7) + +/* Control registers. */ +#define RTCA3_RCR1 0x22 +#define RTCA3_RCR1_AIE BIT(0) +#define RTCA3_RCR1_CIE BIT(1) +#define RTCA3_RCR1_PIE BIT(2) +#define RTCA3_RCR1_PES GENMASK(7, 4) +#define RTCA3_RCR1_PES_1_64_SEC 0x8 +#define RTCA3_RCR2 0x24 +#define RTCA3_RCR2_START BIT(0) +#define RTCA3_RCR2_RESET BIT(1) +#define RTCA3_RCR2_AADJE BIT(4) +#define RTCA3_RCR2_ADJP BIT(5) +#define RTCA3_RCR2_HR24 BIT(6) +#define RTCA3_RCR2_CNTMD BIT(7) +#define RTCA3_RSR 0x20 +#define RTCA3_RSR_AF BIT(0) +#define RTCA3_RSR_CF BIT(1) +#define RTCA3_RSR_PF BIT(2) +#define RTCA3_RADJ 0x2e +#define RTCA3_RADJ_ADJ GENMASK(5, 0) +#define RTCA3_RADJ_ADJ_MAX 0x3f +#define RTCA3_RADJ_PMADJ GENMASK(7, 6) +#define RTCA3_RADJ_PMADJ_NONE 0 +#define RTCA3_RADJ_PMADJ_ADD 1 +#define RTCA3_RADJ_PMADJ_SUB 2 + +/* Polling operation timeouts. */ +#define RTCA3_DEFAULT_TIMEOUT_US 150 +#define RTCA3_IRQSET_TIMEOUT_US 5000 +#define RTCA3_START_TIMEOUT_US 150000 +#define RTCA3_RESET_TIMEOUT_US 200000 + +/** + * enum rtca3_alrm_set_step - RTCA3 alarm set steps + * @RTCA3_ALRM_SSTEP_DONE: alarm setup done step + * @RTCA3_ALRM_SSTEP_IRQ: two 1/64 periodic IRQs were generated step + * @RTCA3_ALRM_SSTEP_INIT: alarm setup initialization step + */ +enum rtca3_alrm_set_step { + RTCA3_ALRM_SSTEP_DONE = 0, + RTCA3_ALRM_SSTEP_IRQ = 1, + RTCA3_ALRM_SSTEP_INIT = 3, +}; + +/** + * struct rtca3_ppb_per_cycle - PPB per cycle + * @ten_sec: PPB per cycle in 10 seconds adjutment mode + * @sixty_sec: PPB per cycle in 60 seconds adjustment mode + */ +struct rtca3_ppb_per_cycle { + int ten_sec; + int sixty_sec; +}; + +/** + * struct rtca3_priv - RTCA3 private data structure + * @base: base address + * @rtc_dev: RTC device + * @rstc: reset control + * @set_alarm_completion: alarm setup completion + * @alrm_sstep: alarm setup step (see enum rtca3_alrm_set_step) + * @lock: device lock + * @ppb: ppb per cycle for each the available adjustment modes + * @wakeup_irq: wakeup IRQ + */ +struct rtca3_priv { + void __iomem *base; + struct rtc_device *rtc_dev; + struct reset_control *rstc; + struct completion set_alarm_completion; + atomic_t alrm_sstep; + spinlock_t lock; + struct rtca3_ppb_per_cycle ppb; + int wakeup_irq; +}; + +static void rtca3_byte_update_bits(struct rtca3_priv *priv, u8 off, u8 mask, u8 val) +{ + u8 tmp; + + tmp = readb(priv->base + off); + tmp &= ~mask; + tmp |= (val & mask); + writeb(tmp, priv->base + off); +} + +static u8 rtca3_alarm_handler_helper(struct rtca3_priv *priv) +{ + u8 val, pending; + + val = readb(priv->base + RTCA3_RSR); + pending = val & RTCA3_RSR_AF; + writeb(val & ~pending, priv->base + RTCA3_RSR); + + if (pending) + rtc_update_irq(priv->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return pending; +} + +static irqreturn_t rtca3_alarm_handler(int irq, void *dev_id) +{ + struct rtca3_priv *priv = dev_id; + u8 pending; + + guard(spinlock)(&priv->lock); + + pending = rtca3_alarm_handler_helper(priv); + + return IRQ_RETVAL(pending); +} + +static irqreturn_t rtca3_periodic_handler(int irq, void *dev_id) +{ + struct rtca3_priv *priv = dev_id; + u8 val, pending; + + guard(spinlock)(&priv->lock); + + val = readb(priv->base + RTCA3_RSR); + pending = val & RTCA3_RSR_PF; + + if (pending) { + writeb(val & ~pending, priv->base + RTCA3_RSR); + + if (atomic_read(&priv->alrm_sstep) > RTCA3_ALRM_SSTEP_IRQ) { + /* Alarm setup in progress. */ + atomic_dec(&priv->alrm_sstep); + + if (atomic_read(&priv->alrm_sstep) == RTCA3_ALRM_SSTEP_IRQ) { + /* + * We got 2 * 1/64 periodic interrupts. Disable + * interrupt and let alarm setup continue. + */ + rtca3_byte_update_bits(priv, RTCA3_RCR1, + RTCA3_RCR1_PIE, 0); + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, val, + !(val & RTCA3_RCR1_PIE), + 10, RTCA3_DEFAULT_TIMEOUT_US); + complete(&priv->set_alarm_completion); + } + } + } + + return IRQ_RETVAL(pending); +} + +static void rtca3_prepare_cntalrm_regs_for_read(struct rtca3_priv *priv, bool cnt) +{ + /* Offset b/w time and alarm registers. */ + u8 offset = cnt ? 0 : 0xe; + + /* + * According to HW manual (section 22.6.4. Notes on writing to and + * reading from registers) after writing to count registers, alarm + * registers, year alarm enable register, bits RCR2.AADJE, AADJP, + * and HR24 register, we need to do 3 empty reads before being + * able to fetch the registers content. + */ + for (u8 i = 0; i < 3; i++) { + readb(priv->base + RTCA3_RSECCNT + offset); + readb(priv->base + RTCA3_RMINCNT + offset); + readb(priv->base + RTCA3_RHRCNT + offset); + readb(priv->base + RTCA3_RWKCNT + offset); + readb(priv->base + RTCA3_RDAYCNT + offset); + readw(priv->base + RTCA3_RYRCNT + offset); + if (!cnt) + readb(priv->base + RTCA3_RYRAREN); + } +} + +static int rtca3_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 sec, min, hour, wday, mday, month, tmp; + u8 trials = 0; + u32 year100; + u16 year; + + guard(spinlock_irqsave)(&priv->lock); + + tmp = readb(priv->base + RTCA3_RCR2); + if (!(tmp & RTCA3_RCR2_START)) + return -EINVAL; + + do { + /* Clear carry interrupt. */ + rtca3_byte_update_bits(priv, RTCA3_RSR, RTCA3_RSR_CF, 0); + + /* Read counters. */ + sec = readb(priv->base + RTCA3_RSECCNT); + min = readb(priv->base + RTCA3_RMINCNT); + hour = readb(priv->base + RTCA3_RHRCNT); + wday = readb(priv->base + RTCA3_RWKCNT); + mday = readb(priv->base + RTCA3_RDAYCNT); + month = readb(priv->base + RTCA3_RMONCNT); + year = readw(priv->base + RTCA3_RYRCNT); + + tmp = readb(priv->base + RTCA3_RSR); + + /* + * We cannot generate carries due to reading 64Hz counter as + * the driver doesn't implement carry, thus, carries will be + * generated once per seconds. Add a timeout of 5 trials here + * to avoid infinite loop, if any. + */ + } while ((tmp & RTCA3_RSR_CF) && ++trials < 5); + + if (trials >= 5) + return -ETIMEDOUT; + + tm->tm_sec = bcd2bin(FIELD_GET(RTCA3_RSECCNT_SEC, sec)); + tm->tm_min = bcd2bin(FIELD_GET(RTCA3_RMINCNT_MIN, min)); + tm->tm_hour = bcd2bin(FIELD_GET(RTCA3_RHRCNT_HR, hour)); + tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKCNT_WK, wday)); + tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYCNT_DAY, mday)); + tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONCNT_MONTH, month)) - 1; + year = FIELD_GET(RTCA3_RYRCNT_YEAR, year); + year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20); + tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900; + + return 0; +} + +static int rtca3_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 rcr2, tmp; + int ret; + + guard(spinlock_irqsave)(&priv->lock); + + /* Stop the RTC. */ + rcr2 = readb(priv->base + RTCA3_RCR2); + writeb(rcr2 & ~RTCA3_RCR2_START, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp, + !(tmp & RTCA3_RCR2_START), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Update time. */ + writeb(bin2bcd(tm->tm_sec), priv->base + RTCA3_RSECCNT); + writeb(bin2bcd(tm->tm_min), priv->base + RTCA3_RMINCNT); + writeb(bin2bcd(tm->tm_hour), priv->base + RTCA3_RHRCNT); + writeb(bin2bcd(tm->tm_wday), priv->base + RTCA3_RWKCNT); + writeb(bin2bcd(tm->tm_mday), priv->base + RTCA3_RDAYCNT); + writeb(bin2bcd(tm->tm_mon + 1), priv->base + RTCA3_RMONCNT); + writew(bin2bcd(tm->tm_year % 100), priv->base + RTCA3_RYRCNT); + + /* Make sure we can read back the counters. */ + rtca3_prepare_cntalrm_regs_for_read(priv, true); + + /* Start RTC. */ + writeb(rcr2 | RTCA3_RCR2_START, priv->base + RTCA3_RCR2); + return readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp, + (tmp & RTCA3_RCR2_START), + 10, RTCA3_DEFAULT_TIMEOUT_US); +} + +static int rtca3_alarm_irq_set_helper(struct rtca3_priv *priv, + u8 interrupts, + unsigned int enabled) +{ + u8 tmp, val; + + if (enabled) { + /* + * AIE, CIE, PIE bit indexes in RSR corresponds with + * those on RCR1. Same interrupts mask can be used. + */ + rtca3_byte_update_bits(priv, RTCA3_RSR, interrupts, 0); + val = interrupts; + } else { + val = 0; + } + + rtca3_byte_update_bits(priv, RTCA3_RCR1, interrupts, val); + return readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, + ((tmp & interrupts) == val), + 10, RTCA3_IRQSET_TIMEOUT_US); +} + +static int rtca3_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + + guard(spinlock_irqsave)(&priv->lock); + + return rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE, enabled); +} + +static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 sec, min, hour, wday, mday, month; + struct rtc_time *tm = &wkalrm->time; + u32 year100; + u16 year; + + guard(spinlock_irqsave)(&priv->lock); + + sec = readb(priv->base + RTCA3_RSECAR); + min = readb(priv->base + RTCA3_RMINAR); + hour = readb(priv->base + RTCA3_RHRAR); + wday = readb(priv->base + RTCA3_RWKAR); + mday = readb(priv->base + RTCA3_RDAYAR); + month = readb(priv->base + RTCA3_RMONAR); + year = readw(priv->base + RTCA3_RYRAR); + + tm->tm_sec = bcd2bin(FIELD_GET(RTCA3_RSECAR_SEC, sec)); + tm->tm_min = bcd2bin(FIELD_GET(RTCA3_RMINAR_MIN, min)); + tm->tm_hour = bcd2bin(FIELD_GET(RTCA3_RHRAR_HR, hour)); + tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKAR_DAYW, wday)); + tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYAR_DATE, mday)); + tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONAR_MON, month)) - 1; + year = FIELD_GET(RTCA3_RYRAR_YR, year); + year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20); + tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900; + + wkalrm->enabled = !!(readb(priv->base + RTCA3_RCR1) & RTCA3_RCR1_AIE); + + return 0; +} + +static int rtca3_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + u8 rcr1, tmp; + int ret; + + scoped_guard(spinlock_irqsave, &priv->lock) { + tmp = readb(priv->base + RTCA3_RCR2); + if (!(tmp & RTCA3_RCR2_START)) + return -EPERM; + + /* Disable AIE to prevent false interrupts. */ + rcr1 = readb(priv->base + RTCA3_RCR1); + rcr1 &= ~RTCA3_RCR1_AIE; + writeb(rcr1, priv->base + RTCA3_RCR1); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, + !(tmp & RTCA3_RCR1_AIE), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Set the time and enable the alarm. */ + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_sec), priv->base + RTCA3_RSECAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_min), priv->base + RTCA3_RMINAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_hour), priv->base + RTCA3_RHRAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_wday), priv->base + RTCA3_RWKAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_mday), priv->base + RTCA3_RDAYAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_mon + 1), priv->base + RTCA3_RMONAR); + + writew(bin2bcd(tm->tm_year % 100), priv->base + RTCA3_RYRAR); + writeb(RTCA3_AR_ENB, priv->base + RTCA3_RYRAREN); + + /* Make sure we can read back the counters. */ + rtca3_prepare_cntalrm_regs_for_read(priv, false); + + /* Need to wait for 2 * 1/64 periodic interrupts to be generated. */ + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_INIT); + reinit_completion(&priv->set_alarm_completion); + + /* Enable periodic interrupt. */ + rcr1 |= RTCA3_RCR1_PIE; + writeb(rcr1, priv->base + RTCA3_RCR1); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, + (tmp & RTCA3_RCR1_PIE), + 10, RTCA3_IRQSET_TIMEOUT_US); + } + + if (ret) + goto setup_failed; + + /* Wait for the 2 * 1/64 periodic interrupts. */ + ret = wait_for_completion_interruptible_timeout(&priv->set_alarm_completion, + msecs_to_jiffies(500)); + if (ret <= 0) { + ret = -ETIMEDOUT; + goto setup_failed; + } + + scoped_guard(spinlock_irqsave, &priv->lock) { + ret = rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE, wkalrm->enabled); + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); + } + + return ret; + +setup_failed: + scoped_guard(spinlock_irqsave, &priv->lock) { + /* + * Disable PIE to avoid interrupt storm in case HW needed more than + * specified timeout for setup. + */ + writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1); + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & ~RTCA3_RCR1_PIE), + 10, RTCA3_DEFAULT_TIMEOUT_US); + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); + } + + return ret; +} + +static int rtca3_read_offset(struct device *dev, long *offset) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 val, radj, cycles; + u32 ppb_per_cycle; + + scoped_guard(spinlock_irqsave, &priv->lock) { + radj = readb(priv->base + RTCA3_RADJ); + val = readb(priv->base + RTCA3_RCR2); + } + + cycles = FIELD_GET(RTCA3_RADJ_ADJ, radj); + + if (!cycles) { + *offset = 0; + return 0; + } + + if (val & RTCA3_RCR2_ADJP) + ppb_per_cycle = priv->ppb.ten_sec; + else + ppb_per_cycle = priv->ppb.sixty_sec; + + *offset = cycles * ppb_per_cycle; + val = FIELD_GET(RTCA3_RADJ_PMADJ, radj); + if (val == RTCA3_RADJ_PMADJ_SUB) + *offset = -(*offset); + + return 0; +} + +static int rtca3_set_offset(struct device *dev, long offset) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + int cycles, cycles10, cycles60; + u8 radj, adjp, tmp; + int ret; + + /* + * Automatic time error adjustment could be set at intervals of 10 + * or 60 seconds. + */ + cycles10 = DIV_ROUND_CLOSEST(offset, priv->ppb.ten_sec); + cycles60 = DIV_ROUND_CLOSEST(offset, priv->ppb.sixty_sec); + + /* We can set b/w 1 and 63 clock cycles. */ + if (cycles60 >= -RTCA3_RADJ_ADJ_MAX && + cycles60 <= RTCA3_RADJ_ADJ_MAX) { + cycles = cycles60; + adjp = 0; + } else if (cycles10 >= -RTCA3_RADJ_ADJ_MAX && + cycles10 <= RTCA3_RADJ_ADJ_MAX) { + cycles = cycles10; + adjp = RTCA3_RCR2_ADJP; + } else { + return -ERANGE; + } + + radj = FIELD_PREP(RTCA3_RADJ_ADJ, abs(cycles)); + if (!cycles) + radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_NONE); + else if (cycles > 0) + radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_ADD); + else + radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_SUB); + + guard(spinlock_irqsave)(&priv->lock); + + tmp = readb(priv->base + RTCA3_RCR2); + + if ((tmp & RTCA3_RCR2_ADJP) != adjp) { + /* RADJ.PMADJ need to be set to zero before setting RCR2.ADJP. */ + writeb(0, priv->base + RTCA3_RADJ); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RADJ, tmp, !tmp, + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + rtca3_byte_update_bits(priv, RTCA3_RCR2, RTCA3_RCR2_ADJP, adjp); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp, + ((tmp & RTCA3_RCR2_ADJP) == adjp), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + } + + writeb(radj, priv->base + RTCA3_RADJ); + return readb_poll_timeout_atomic(priv->base + RTCA3_RADJ, tmp, (tmp == radj), + 10, RTCA3_DEFAULT_TIMEOUT_US); +} + +static const struct rtc_class_ops rtca3_ops = { + .read_time = rtca3_read_time, + .set_time = rtca3_set_time, + .read_alarm = rtca3_read_alarm, + .set_alarm = rtca3_set_alarm, + .alarm_irq_enable = rtca3_alarm_irq_enable, + .set_offset = rtca3_set_offset, + .read_offset = rtca3_read_offset, +}; + +static int rtca3_initial_setup(struct clk *clk, struct rtca3_priv *priv) +{ + unsigned long osc32k_rate; + u8 val, tmp, mask; + u32 sleep_us; + int ret; + + osc32k_rate = clk_get_rate(clk); + if (!osc32k_rate) + return -EINVAL; + + sleep_us = DIV_ROUND_UP_ULL(1000000ULL, osc32k_rate) * 6; + + priv->ppb.ten_sec = DIV_ROUND_CLOSEST_ULL(1000000000ULL, (osc32k_rate * 10)); + priv->ppb.sixty_sec = DIV_ROUND_CLOSEST_ULL(1000000000ULL, (osc32k_rate * 60)); + + /* + * According to HW manual (section 22.4.2. Clock and count mode setting procedure) + * we need to wait at least 6 cycles of the 32KHz clock after clock was enabled. + */ + usleep_range(sleep_us, sleep_us + 10); + + /* Disable all interrupts. */ + mask = RTCA3_RCR1_AIE | RTCA3_RCR1_CIE | RTCA3_RCR1_PIE; + ret = rtca3_alarm_irq_set_helper(priv, mask, 0); + if (ret) + return ret; + + mask = RTCA3_RCR2_START | RTCA3_RCR2_HR24; + val = readb(priv->base + RTCA3_RCR2); + /* Nothing to do if already started in 24 hours and calendar count mode. */ + if ((val & mask) == mask) + return 0; + + /* Reconfigure the RTC in 24 hours and calendar count mode. */ + mask = RTCA3_RCR2_START | RTCA3_RCR2_CNTMD; + writeb(0, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, !(tmp & mask), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* + * Set 24 hours mode. According to HW manual (section 22.3.19. RTC Control + * Register 2) this needs to be done separate from stop operation. + */ + mask = RTCA3_RCR2_HR24; + val = RTCA3_RCR2_HR24; + writeb(val, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, (tmp & mask), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Execute reset. */ + mask = RTCA3_RCR2_RESET; + writeb(val | RTCA3_RCR2_RESET, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, !(tmp & mask), + 10, RTCA3_RESET_TIMEOUT_US); + if (ret) + return ret; + + /* + * According to HW manual (section 22.6.3. Notes on writing to and reading + * from registers) after reset we need to wait 6 clock cycles before + * writing to RTC registers. + */ + usleep_range(sleep_us, sleep_us + 10); + + /* Set no adjustment. */ + writeb(0, priv->base + RTCA3_RADJ); + ret = readb_poll_timeout(priv->base + RTCA3_RADJ, tmp, !tmp, 10, + RTCA3_DEFAULT_TIMEOUT_US); + + /* Start the RTC and enable automatic time error adjustment. */ + mask = RTCA3_RCR2_START | RTCA3_RCR2_AADJE; + val |= RTCA3_RCR2_START | RTCA3_RCR2_AADJE; + writeb(val, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, ((tmp & mask) == mask), + 10, RTCA3_START_TIMEOUT_US); + if (ret) + return ret; + + /* + * According to HW manual (section 22.6.4. Notes on writing to and reading + * from registers) we need to wait 1/128 seconds while the clock is operating + * (RCR2.START bit = 1) to be able to read the counters after a return from + * reset. + */ + usleep_range(8000, 9000); + + /* Set period interrupt to 1/64 seconds. It is necessary for alarm setup. */ + val = FIELD_PREP(RTCA3_RCR1_PES, RTCA3_RCR1_PES_1_64_SEC); + rtca3_byte_update_bits(priv, RTCA3_RCR1, RTCA3_RCR1_PES, val); + return readb_poll_timeout(priv->base + RTCA3_RCR1, tmp, ((tmp & RTCA3_RCR1_PES) == val), + 10, RTCA3_DEFAULT_TIMEOUT_US); +} + +static int rtca3_request_irqs(struct platform_device *pdev, struct rtca3_priv *priv) +{ + struct device *dev = &pdev->dev; + int ret, irq; + + irq = platform_get_irq_byname(pdev, "alarm"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get alarm IRQ!\n"); + + ret = devm_request_irq(dev, irq, rtca3_alarm_handler, 0, "rtca3-alarm", priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to request alarm IRQ!\n"); + priv->wakeup_irq = irq; + + irq = platform_get_irq_byname(pdev, "period"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get period IRQ!\n"); + + ret = devm_request_irq(dev, irq, rtca3_periodic_handler, 0, "rtca3-period", priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to request period IRQ!\n"); + + /* + * Driver doesn't implement carry handler. Just get the IRQ here + * for backward compatibility, in case carry support will be added later. + */ + irq = platform_get_irq_byname(pdev, "carry"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get carry IRQ!\n"); + + return 0; +} + +static void rtca3_action(void *data) +{ + struct device *dev = data; + struct rtca3_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = reset_control_assert(priv->rstc); + if (ret) + dev_err(dev, "Failed to de-assert reset!"); + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_err(dev, "Failed to runtime suspend!"); +} + +static int rtca3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtca3_priv *priv; + struct clk *clk; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + priv->rstc = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(priv->rstc)) + return PTR_ERR(priv->rstc); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) { + pm_runtime_put_sync(dev); + return ret; + } + + dev_set_drvdata(dev, priv); + ret = devm_add_action_or_reset(dev, rtca3_action, dev); + if (ret) + return ret; + + /* + * This must be an always-on clock to keep the RTC running even after + * driver is unbinded. + */ + clk = devm_clk_get_enabled(dev, "counter"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + spin_lock_init(&priv->lock); + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); + init_completion(&priv->set_alarm_completion); + + ret = rtca3_initial_setup(clk, priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup the RTC!\n"); + + ret = rtca3_request_irqs(pdev, priv); + if (ret) + return ret; + + device_init_wakeup(&pdev->dev, 1); + + priv->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->rtc_dev->ops = &rtca3_ops; + priv->rtc_dev->max_user_freq = 256; + priv->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + priv->rtc_dev->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(priv->rtc_dev); +} + +static void rtca3_remove(struct platform_device *pdev) +{ + struct rtca3_priv *priv = platform_get_drvdata(pdev); + + guard(spinlock_irqsave)(&priv->lock); + + /* + * Disable alarm, periodic interrupts. The RTC device cannot + * power up the system. + */ + rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE | RTCA3_RCR1_PIE, 0); +} + +static int rtca3_suspend(struct device *dev) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + + if (!device_may_wakeup(dev)) + return 0; + + /* Alarm setup in progress. */ + if (atomic_read(&priv->alrm_sstep) != RTCA3_ALRM_SSTEP_DONE) + return -EBUSY; + + enable_irq_wake(priv->wakeup_irq); + + return 0; +} + +static int rtca3_clean_alarm(struct rtca3_priv *priv) +{ + struct rtc_device *rtc_dev = priv->rtc_dev; + time64_t alarm_time, now; + struct rtc_wkalrm alarm; + struct rtc_time tm; + u8 pending; + int ret; + + ret = rtc_read_alarm(rtc_dev, &alarm); + if (ret) + return ret; + + if (!alarm.enabled) + return 0; + + ret = rtc_read_time(rtc_dev, &tm); + if (ret) + return ret; + + alarm_time = rtc_tm_to_time64(&alarm.time); + now = rtc_tm_to_time64(&tm); + if (alarm_time >= now) + return 0; + + /* + * Heuristically, it has been determined that when returning from deep + * sleep state the RTCA3_RSR.AF is zero even though the alarm expired. + * Call again the rtc_update_irq() if alarm helper detects this. + */ + + guard(spinlock_irqsave)(&priv->lock); + + pending = rtca3_alarm_handler_helper(priv); + if (!pending) + rtc_update_irq(priv->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return 0; +} + +static int rtca3_resume(struct device *dev) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + + if (!device_may_wakeup(dev)) + return 0; + + disable_irq_wake(priv->wakeup_irq); + + /* + * According to the HW manual (section 22.6.4 Notes on writing to + * and reading from registers) we need to wait 1/128 seconds while + * RCR2.START = 1 to be able to read the counters after a return from low + * power consumption state. + */ + mdelay(8); + + /* + * The alarm cannot wake the system from deep sleep states. In case + * we return from deep sleep states and the alarm expired we need + * to disable it to avoid failures when setting another alarm. + */ + return rtca3_clean_alarm(priv); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rtca3_pm_ops, rtca3_suspend, rtca3_resume); + +static const struct of_device_id rtca3_of_match[] = { + { .compatible = "renesas,rz-rtca3", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtca3_of_match); + +static struct platform_driver rtca3_platform_driver = { + .driver = { + .name = "rtc-rtca3", + .pm = pm_ptr(&rtca3_pm_ops), + .of_match_table = rtca3_of_match, + }, + .probe = rtca3_probe, + .remove = rtca3_remove, +}; +module_platform_driver(rtca3_platform_driver); + +MODULE_DESCRIPTION("Renesas RTCA-3 RTC driver"); +MODULE_AUTHOR("Claudiu Beznea "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rtd119x.c b/drivers/rtc/rtc-rtd119x.c index 29662dfd56fef..85a138db81d78 100644 --- a/drivers/rtc/rtc-rtd119x.c +++ b/drivers/rtc/rtc-rtd119x.c @@ -228,7 +228,7 @@ static void rtd119x_rtc_remove(struct platform_device *pdev) static struct platform_driver rtd119x_rtc_driver = { .probe = rtd119x_rtc_probe, - .remove_new = rtd119x_rtc_remove, + .remove = rtd119x_rtc_remove, .driver = { .name = "rtd1295-rtc", .of_match_table = rtd119x_rtc_dt_ids, diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c index 2f001c59c61d5..868d1b1eb0f42 100644 --- a/drivers/rtc/rtc-rv3028.c +++ b/drivers/rtc/rtc-rv3028.c @@ -120,8 +120,9 @@ static ssize_t timestamp0_show(struct device *dev, { struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); struct rtc_time tm; - int ret, count; + unsigned int count; u8 date[6]; + int ret; ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); if (ret) @@ -156,7 +157,8 @@ static ssize_t timestamp0_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); - int ret, count; + unsigned int count; + int ret; ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); if (ret) diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c index 56ebbd4d04814..cb220807d925b 100644 --- a/drivers/rtc/rtc-rzn1.c +++ b/drivers/rtc/rtc-rzn1.c @@ -7,7 +7,7 @@ * - 2022 Schneider Electric * * Authors: - * - Michel Pollet , + * - Michel Pollet * - Miquel Raynal */ @@ -35,13 +35,13 @@ #define RZN1_RTC_CTL2_WUST BIT(5) #define RZN1_RTC_CTL2_STOPPED (RZN1_RTC_CTL2_WAIT | RZN1_RTC_CTL2_WST) -#define RZN1_RTC_SEC 0x14 -#define RZN1_RTC_MIN 0x18 -#define RZN1_RTC_HOUR 0x1c -#define RZN1_RTC_WEEK 0x20 -#define RZN1_RTC_DAY 0x24 -#define RZN1_RTC_MONTH 0x28 -#define RZN1_RTC_YEAR 0x2c +#define RZN1_RTC_TIME 0x30 +#define RZN1_RTC_TIME_MIN_SHIFT 8 +#define RZN1_RTC_TIME_HOUR_SHIFT 16 +#define RZN1_RTC_CAL 0x34 +#define RZN1_RTC_CAL_DAY_SHIFT 8 +#define RZN1_RTC_CAL_MON_SHIFT 16 +#define RZN1_RTC_CAL_YEAR_SHIFT 24 #define RZN1_RTC_SUBU 0x38 #define RZN1_RTC_SUBU_DEV BIT(7) @@ -52,12 +52,8 @@ #define RZN1_RTC_ALW 0x48 #define RZN1_RTC_SECC 0x4c -#define RZN1_RTC_MINC 0x50 -#define RZN1_RTC_HOURC 0x54 -#define RZN1_RTC_WEEKC 0x58 -#define RZN1_RTC_DAYC 0x5c -#define RZN1_RTC_MONTHC 0x60 -#define RZN1_RTC_YEARC 0x64 +#define RZN1_RTC_TIMEC 0x68 +#define RZN1_RTC_CALC 0x6c struct rzn1_rtc { struct rtc_device *rtcdev; @@ -66,26 +62,18 @@ struct rzn1_rtc { static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm) { - tm->tm_sec = readl(rtc->base + RZN1_RTC_SECC); - tm->tm_min = readl(rtc->base + RZN1_RTC_MINC); - tm->tm_hour = readl(rtc->base + RZN1_RTC_HOURC); - tm->tm_wday = readl(rtc->base + RZN1_RTC_WEEKC); - tm->tm_mday = readl(rtc->base + RZN1_RTC_DAYC); - tm->tm_mon = readl(rtc->base + RZN1_RTC_MONTHC); - tm->tm_year = readl(rtc->base + RZN1_RTC_YEARC); -} - -static unsigned int rzn1_rtc_tm_to_wday(struct rtc_time *tm) -{ - time64_t time; - unsigned int days; - u32 secs; + u32 val; - time = rtc_tm_to_time64(tm); - days = div_s64_rem(time, 86400, &secs); + val = readl(rtc->base + RZN1_RTC_TIMEC); + tm->tm_sec = bcd2bin(val); + tm->tm_min = bcd2bin(val >> RZN1_RTC_TIME_MIN_SHIFT); + tm->tm_hour = bcd2bin(val >> RZN1_RTC_TIME_HOUR_SHIFT); - /* day of the week, 1970-01-01 was a Thursday */ - return (days + 4) % 7; + val = readl(rtc->base + RZN1_RTC_CALC); + tm->tm_wday = val & 0x0f; + tm->tm_mday = bcd2bin(val >> RZN1_RTC_CAL_DAY_SHIFT); + tm->tm_mon = bcd2bin(val >> RZN1_RTC_CAL_MON_SHIFT) - 1; + tm->tm_year = bcd2bin(val >> RZN1_RTC_CAL_YEAR_SHIFT) + 100; } static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -103,17 +91,9 @@ static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm) rzn1_rtc_get_time_snapshot(rtc, tm); secs = readl(rtc->base + RZN1_RTC_SECC); - if (tm->tm_sec != secs) + if (tm->tm_sec != bcd2bin(secs)) rzn1_rtc_get_time_snapshot(rtc, tm); - tm->tm_sec = bcd2bin(tm->tm_sec); - tm->tm_min = bcd2bin(tm->tm_min); - tm->tm_hour = bcd2bin(tm->tm_hour); - tm->tm_wday = bcd2bin(tm->tm_wday); - tm->tm_mday = bcd2bin(tm->tm_mday); - tm->tm_mon = bcd2bin(tm->tm_mon); - tm->tm_year = bcd2bin(tm->tm_year); - return 0; } @@ -123,14 +103,6 @@ static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm) u32 val; int ret; - tm->tm_sec = bin2bcd(tm->tm_sec); - tm->tm_min = bin2bcd(tm->tm_min); - tm->tm_hour = bin2bcd(tm->tm_hour); - tm->tm_wday = bin2bcd(rzn1_rtc_tm_to_wday(tm)); - tm->tm_mday = bin2bcd(tm->tm_mday); - tm->tm_mon = bin2bcd(tm->tm_mon); - tm->tm_year = bin2bcd(tm->tm_year); - val = readl(rtc->base + RZN1_RTC_CTL2); if (!(val & RZN1_RTC_CTL2_STOPPED)) { /* Hold the counter if it was counting up */ @@ -144,13 +116,17 @@ static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm) return ret; } - writel(tm->tm_sec, rtc->base + RZN1_RTC_SEC); - writel(tm->tm_min, rtc->base + RZN1_RTC_MIN); - writel(tm->tm_hour, rtc->base + RZN1_RTC_HOUR); - writel(tm->tm_wday, rtc->base + RZN1_RTC_WEEK); - writel(tm->tm_mday, rtc->base + RZN1_RTC_DAY); - writel(tm->tm_mon, rtc->base + RZN1_RTC_MONTH); - writel(tm->tm_year, rtc->base + RZN1_RTC_YEAR); + val = bin2bcd(tm->tm_sec); + val |= bin2bcd(tm->tm_min) << RZN1_RTC_TIME_MIN_SHIFT; + val |= bin2bcd(tm->tm_hour) << RZN1_RTC_TIME_HOUR_SHIFT; + writel(val, rtc->base + RZN1_RTC_TIME); + + val = tm->tm_wday; + val |= bin2bcd(tm->tm_mday) << RZN1_RTC_CAL_DAY_SHIFT; + val |= bin2bcd(tm->tm_mon + 1) << RZN1_RTC_CAL_MON_SHIFT; + val |= bin2bcd(tm->tm_year - 100) << RZN1_RTC_CAL_YEAR_SHIFT; + writel(val, rtc->base + RZN1_RTC_CAL); + writel(0, rtc->base + RZN1_RTC_CTL2); return 0; @@ -405,7 +381,7 @@ MODULE_DEVICE_TABLE(of, rzn1_rtc_of_match); static struct platform_driver rzn1_rtc_driver = { .probe = rzn1_rtc_probe, - .remove_new = rzn1_rtc_remove, + .remove = rzn1_rtc_remove, .driver = { .name = "rzn1-rtc", .of_match_table = rzn1_rtc_of_match, @@ -413,7 +389,7 @@ static struct platform_driver rzn1_rtc_driver = { }; module_platform_driver(rzn1_rtc_driver); -MODULE_AUTHOR("Michel Pollet "); MODULE_AUTHOR("Miquel Raynal dev, rtc->irq, st_rtc_handler, 0, - pdev->name, rtc); + ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, + IRQF_NO_AUTOEN, pdev->name, rtc); if (ret) { dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq); return ret; } enable_irq_wake(rtc->irq); - disable_irq(rtc->irq); rtc->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(rtc->clk)) diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 3e4f2ee22b0bc..9f1a019ec8afa 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -1287,7 +1287,7 @@ static const struct dev_pm_ops stm32_rtc_pm_ops = { static struct platform_driver stm32_rtc_driver = { .probe = stm32_rtc_probe, - .remove_new = stm32_rtc_remove, + .remove = stm32_rtc_remove, .driver = { .name = DRIVER_NAME, .pm = &stm32_rtc_pm_ops, diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 7566d0a44af82..7afcd14aeee56 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -403,7 +403,7 @@ MODULE_DEVICE_TABLE(of, rtc_dt_ids); static struct platform_driver stmp3xxx_rtcdrv = { .probe = stmp3xxx_rtc_probe, - .remove_new = stmp3xxx_rtc_remove, + .remove = stmp3xxx_rtc_remove, .driver = { .name = "stmp3xxx-rtc", .pm = &stmp3xxx_rtc_pm_ops, diff --git a/drivers/rtc/rtc-sunplus.c b/drivers/rtc/rtc-sunplus.c index 20c7e97c2fc81..9b1ce0e8ba27e 100644 --- a/drivers/rtc/rtc-sunplus.c +++ b/drivers/rtc/rtc-sunplus.c @@ -344,7 +344,7 @@ static SIMPLE_DEV_PM_OPS(sp_rtc_pm_ops, sp_rtc_suspend, sp_rtc_resume); static struct platform_driver sp_rtc_driver = { .probe = sp_rtc_probe, - .remove_new = sp_rtc_remove, + .remove = sp_rtc_remove, .driver = { .name = "sp7021-rtc", .of_match_table = sp_rtc_of_match, diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c index 441e0a66b215b..79a3102c83549 100644 --- a/drivers/rtc/rtc-tegra.c +++ b/drivers/rtc/rtc-tegra.c @@ -399,7 +399,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev) static struct platform_driver tegra_rtc_driver = { .probe = tegra_rtc_probe, - .remove_new = tegra_rtc_remove, + .remove = tegra_rtc_remove, .shutdown = tegra_rtc_shutdown, .driver = { .name = "tegra_rtc", diff --git a/drivers/rtc/rtc-tps6586x.c b/drivers/rtc/rtc-tps6586x.c index 20faf08c254cc..e796729fc817c 100644 --- a/drivers/rtc/rtc-tps6586x.c +++ b/drivers/rtc/rtc-tps6586x.c @@ -317,7 +317,7 @@ static struct platform_driver tps6586x_rtc_driver = { .pm = &tps6586x_pm_ops, }, .probe = tps6586x_rtc_probe, - .remove_new = tps6586x_rtc_remove, + .remove = tps6586x_rtc_remove, }; module_platform_driver(tps6586x_rtc_driver); diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 4e24c12004f16..794429182b348 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -673,7 +673,7 @@ MODULE_DEVICE_TABLE(of, twl_rtc_of_match); static struct platform_driver twl4030rtc_driver = { .probe = twl_rtc_probe, - .remove_new = twl_rtc_remove, + .remove = twl_rtc_remove, .shutdown = twl_rtc_shutdown, .driver = { .name = "twl_rtc", diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index ccfa76513a2ca..c8b568498016f 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -251,7 +251,7 @@ MODULE_DEVICE_TABLE(of, wmt_dt_ids); static struct platform_driver vt8500_rtc_driver = { .probe = vt8500_rtc_probe, - .remove_new = vt8500_rtc_remove, + .remove = vt8500_rtc_remove, .driver = { .name = "vt8500-rtc", .of_match_table = wmt_dt_ids, diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 3c773cff2b393..6797eb4d2e493 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -459,7 +459,7 @@ static SIMPLE_DEV_PM_OPS(wm8350_rtc_pm_ops, wm8350_rtc_suspend, static struct platform_driver wm8350_rtc_driver = { .probe = wm8350_rtc_probe, - .remove_new = wm8350_rtc_remove, + .remove = wm8350_rtc_remove, .driver = { .name = "wm8350-rtc", .pm = &wm8350_rtc_pm_ops, diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c index f78efc9760c0e..0813ea1a03c27 100644 --- a/drivers/rtc/rtc-xgene.c +++ b/drivers/rtc/rtc-xgene.c @@ -263,7 +263,7 @@ MODULE_DEVICE_TABLE(of, xgene_rtc_of_match); static struct platform_driver xgene_rtc_driver = { .probe = xgene_rtc_probe, - .remove_new = xgene_rtc_remove, + .remove = xgene_rtc_remove, .driver = { .name = "xgene-rtc", .pm = &xgene_rtc_pm_ops, diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index 08ed171bdab43..af1abb69d1e32 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -382,7 +382,7 @@ MODULE_DEVICE_TABLE(of, xlnx_rtc_of_match); static struct platform_driver xlnx_rtc_driver = { .probe = xlnx_rtc_probe, - .remove_new = xlnx_rtc_remove, + .remove = xlnx_rtc_remove, .driver = { .name = KBUILD_MODNAME, .pm = &xlnx_rtc_pm_ops, diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index dcb3c32f027af..c763c50d14546 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -803,7 +803,6 @@ static struct attribute *con3215_drv_attrs[] = { static struct attribute_group con3215_drv_attr_group = { .attrs = con3215_drv_attrs, - NULL, }; static const struct attribute_group *con3215_drv_attr_groups[] = { diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index e14638936de6b..26e1ea1940ecb 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -453,7 +453,7 @@ static void ap_tasklet_fn(unsigned long dummy) * important that no requests on any AP get lost. */ if (ap_irq_flag) - xchg(ap_airq.lsi_ptr, 0); + WRITE_ONCE(*ap_airq.lsi_ptr, 0); spin_lock_bh(&ap_queues_lock); hash_for_each(ap_queues, bkt, aq, hnode) { diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 8c0b40d8eb39e..a52c2690933fd 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -360,10 +360,26 @@ static int vfio_ap_validate_nib(struct kvm_vcpu *vcpu, dma_addr_t *nib) return 0; } -static int ensure_nib_shared(unsigned long addr, struct gmap *gmap) +/** + * ensure_nib_shared() - Ensure the address of the NIB is secure and shared + * @addr: the physical (absolute) address of the NIB + * + * This function checks whether the NIB page, which has been pinned with + * vfio_pin_pages(), is a shared page belonging to a secure guest. + * + * It will call uv_pin_shared() on it; if the page was already pinned shared + * (i.e. if the NIB belongs to a secure guest and is shared), then 0 + * (success) is returned. If the NIB was not shared, vfio_pin_pages() had + * exported it and now it does not belong to the secure guest anymore. In + * that case, an error is returned. + * + * Context: the NIB (at physical address @addr) has to be pinned with + * vfio_pin_pages() before calling this function. + * + * Return: 0 in case of success, otherwise an error < 0. + */ +static int ensure_nib_shared(unsigned long addr) { - int ret; - /* * The nib has to be located in shared storage since guest and * host access it. vfio_pin_pages() will do a pin shared and @@ -374,12 +390,7 @@ static int ensure_nib_shared(unsigned long addr, struct gmap *gmap) * * If the page is already pinned shared the UV will return a success. */ - ret = uv_pin_shared(addr); - if (ret) { - /* vfio_pin_pages() likely exported the page so let's re-import */ - gmap_convert_to_secure(gmap, addr); - } - return ret; + return uv_pin_shared(addr); } /** @@ -425,6 +436,7 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q, return status; } + /* The pin will probably be successful even if the NIB was not shared */ ret = vfio_pin_pages(&q->matrix_mdev->vdev, nib, 1, IOMMU_READ | IOMMU_WRITE, &h_page); switch (ret) { @@ -447,7 +459,7 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q, /* NIB in non-shared storage is a rc 6 for PV guests */ if (kvm_s390_pv_cpu_is_protected(vcpu) && - ensure_nib_shared(h_nib & PAGE_MASK, kvm->arch.gmap)) { + ensure_nib_shared(h_nib & PAGE_MASK)) { vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1); status.response_code = AP_RESPONSE_INVALID_ADDRESS; return status; diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index ad39797890e59..bf054dd7682bf 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -302,9 +302,9 @@ static void __exit amiga_a3000_scsi_remove(struct platform_device *pdev) * triggering a section mismatch warning. */ static struct platform_driver amiga_a3000_scsi_driver __refdata = { - .remove_new = __exit_p(amiga_a3000_scsi_remove), - .driver = { - .name = "amiga-a3000-scsi", + .remove = __exit_p(amiga_a3000_scsi_remove), + .driver = { + .name = "amiga-a3000-scsi", }, }; diff --git a/drivers/scsi/a4000t.c b/drivers/scsi/a4000t.c index d9103adc87fef..75b43047a1558 100644 --- a/drivers/scsi/a4000t.c +++ b/drivers/scsi/a4000t.c @@ -115,9 +115,9 @@ static void __exit amiga_a4000t_scsi_remove(struct platform_device *pdev) * triggering a section mismatch warning. */ static struct platform_driver amiga_a4000t_scsi_driver __refdata = { - .remove_new = __exit_p(amiga_a4000t_scsi_remove), - .driver = { - .name = "amiga-a4000t-scsi", + .remove = __exit_p(amiga_a4000t_scsi_remove), + .driver = { + .name = "amiga-a4000t-scsi", }, }; diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 1d09d3ac6aa45..8c384c25dca17 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -2736,7 +2736,6 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 Index, int isAif, int isFastResponse, struct hw_fib *aif_fib); int aac_reset_adapter(struct aac_dev *dev, int forced, u8 reset_type); -int aac_check_health(struct aac_dev * dev); int aac_command_thread(void *data); int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx); int aac_fib_adapter_complete(struct fib * fibptr, unsigned short size); diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 47287559c768f..ffef61c4aa015 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1698,127 +1698,6 @@ int aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type) return retval; } -int aac_check_health(struct aac_dev * aac) -{ - int BlinkLED; - unsigned long time_now, flagv = 0; - struct list_head * entry; - - /* Extending the scope of fib_lock slightly to protect aac->in_reset */ - if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0) - return 0; - - if (aac->in_reset || !(BlinkLED = aac_adapter_check_health(aac))) { - spin_unlock_irqrestore(&aac->fib_lock, flagv); - return 0; /* OK */ - } - - aac->in_reset = 1; - - /* Fake up an AIF: - * aac_aifcmd.command = AifCmdEventNotify = 1 - * aac_aifcmd.seqnum = 0xFFFFFFFF - * aac_aifcmd.data[0] = AifEnExpEvent = 23 - * aac_aifcmd.data[1] = AifExeFirmwarePanic = 3 - * aac.aifcmd.data[2] = AifHighPriority = 3 - * aac.aifcmd.data[3] = BlinkLED - */ - - time_now = jiffies/HZ; - entry = aac->fib_list.next; - - /* - * For each Context that is on the - * fibctxList, make a copy of the - * fib, and then set the event to wake up the - * thread that is waiting for it. - */ - while (entry != &aac->fib_list) { - /* - * Extract the fibctx - */ - struct aac_fib_context *fibctx = list_entry(entry, struct aac_fib_context, next); - struct hw_fib * hw_fib; - struct fib * fib; - /* - * Check if the queue is getting - * backlogged - */ - if (fibctx->count > 20) { - /* - * It's *not* jiffies folks, - * but jiffies / HZ, so do not - * panic ... - */ - u32 time_last = fibctx->jiffies; - /* - * Has it been > 2 minutes - * since the last read off - * the queue? - */ - if ((time_now - time_last) > aif_timeout) { - entry = entry->next; - aac_close_fib_context(aac, fibctx); - continue; - } - } - /* - * Warning: no sleep allowed while - * holding spinlock - */ - hw_fib = kzalloc(sizeof(struct hw_fib), GFP_ATOMIC); - fib = kzalloc(sizeof(struct fib), GFP_ATOMIC); - if (fib && hw_fib) { - struct aac_aifcmd * aif; - - fib->hw_fib_va = hw_fib; - fib->dev = aac; - aac_fib_init(fib); - fib->type = FSAFS_NTC_FIB_CONTEXT; - fib->size = sizeof (struct fib); - fib->data = hw_fib->data; - aif = (struct aac_aifcmd *)hw_fib->data; - aif->command = cpu_to_le32(AifCmdEventNotify); - aif->seqnum = cpu_to_le32(0xFFFFFFFF); - ((__le32 *)aif->data)[0] = cpu_to_le32(AifEnExpEvent); - ((__le32 *)aif->data)[1] = cpu_to_le32(AifExeFirmwarePanic); - ((__le32 *)aif->data)[2] = cpu_to_le32(AifHighPriority); - ((__le32 *)aif->data)[3] = cpu_to_le32(BlinkLED); - - /* - * Put the FIB onto the - * fibctx's fibs - */ - list_add_tail(&fib->fiblink, &fibctx->fib_list); - fibctx->count++; - /* - * Set the event to wake up the - * thread that will waiting. - */ - complete(&fibctx->completion); - } else { - printk(KERN_WARNING "aifd: didn't allocate NewFib.\n"); - kfree(fib); - kfree(hw_fib); - } - entry = entry->next; - } - - spin_unlock_irqrestore(&aac->fib_lock, flagv); - - if (BlinkLED < 0) { - printk(KERN_ERR "%s: Host adapter is dead (or got a PCI error) %d\n", - aac->name, BlinkLED); - goto out; - } - - printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); - -out: - aac->in_reset = 0; - return BlinkLED; -} - static inline int is_safw_raid_volume(struct aac_dev *aac, int bus, int target) { return bus == CONTAINER_CHANNEL && target < aac->maximum_num_containers; diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c index 176704b24e6ab..f1ce02cd569e3 100644 --- a/drivers/scsi/aic7xxx/aic7770.c +++ b/drivers/scsi/aic7xxx/aic7770.c @@ -99,21 +99,6 @@ struct aic7770_identity aic7770_ident_table[] = ahc_aic7770_EISA_setup } }; -const int ahc_num_aic7770_devs = ARRAY_SIZE(aic7770_ident_table); - -struct aic7770_identity * -aic7770_find_device(uint32_t id) -{ - struct aic7770_identity *entry; - int i; - - for (i = 0; i < ahc_num_aic7770_devs; i++) { - entry = &aic7770_ident_table[i]; - if (entry->full_id == (id & entry->id_mask)) - return (entry); - } - return (NULL); -} int aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io) diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h index 9bc755a0a2d38..20857c213c726 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.h +++ b/drivers/scsi/aic7xxx/aic7xxx.h @@ -1119,7 +1119,6 @@ struct aic7770_identity { ahc_device_setup_t *setup; }; extern struct aic7770_identity aic7770_ident_table[]; -extern const int ahc_num_aic7770_devs; #define AHC_EISA_SLOT_OFFSET 0xc00 #define AHC_EISA_IOSIZE 0x100 @@ -1135,7 +1134,6 @@ int ahc_pci_test_register_access(struct ahc_softc *); void __maybe_unused ahc_pci_resume(struct ahc_softc *ahc); /*************************** EISA/VL Front End ********************************/ -struct aic7770_identity *aic7770_find_device(uint32_t); int aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *, u_int port); diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index 98a1b966a0b07..85055677666c5 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -885,7 +885,7 @@ static void __exit atari_scsi_remove(struct platform_device *pdev) * triggering a section mismatch warning. */ static struct platform_driver atari_scsi_driver __refdata = { - .remove_new = __exit_p(atari_scsi_remove), + .remove = __exit_p(atari_scsi_remove), .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/scsi/bfa/bfa.h b/drivers/scsi/bfa/bfa.h index 4cb9249e583cc..a6b8c4ddea19d 100644 --- a/drivers/scsi/bfa/bfa.h +++ b/drivers/scsi/bfa/bfa.h @@ -138,14 +138,6 @@ bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg), } while (0) -/* - * PCI devices supported by the current BFA - */ -struct bfa_pciid_s { - u16 device_id; - u16 vendor_id; -}; - extern char bfa_version[]; struct bfa_iocfc_regs_s { @@ -408,9 +400,7 @@ int bfa_iocfc_get_pbc_vports(struct bfa_s *bfa, (((&(_bfa)->modules.dconf_mod)->min_cfg) \ ? BFA_LUNMASK_MINCFG : ((bfa_get_lun_mask(_bfa))->status)) -void bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids); void bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg); -void bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg); void bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo, struct bfa_s *bfa); diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c index 3438d0b8ba062..a99a101b95efe 100644 --- a/drivers/scsi/bfa/bfa_core.c +++ b/drivers/scsi/bfa/bfa_core.c @@ -1933,24 +1933,6 @@ bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q) } } -/* - * Return the list of PCI vendor/device id lists supported by this - * BFA instance. - */ -void -bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids) -{ - static struct bfa_pciid_s __pciids[] = { - {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G2P}, - {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G1P}, - {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_CT}, - {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_CT_FC}, - }; - - *npciids = ARRAY_SIZE(__pciids); - *pciids = __pciids; -} - /* * Use this function query the default struct bfa_iocfc_cfg_s value (compiled * into BFA layer). The OS driver can then turn back and overwrite entries that @@ -1987,20 +1969,3 @@ bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg) cfg->drvcfg.delay_comp = BFA_FALSE; } - -void -bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg) -{ - bfa_cfg_get_default(cfg); - cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN; - cfg->fwcfg.num_tskim_reqs = BFA_TSKIM_MIN; - cfg->fwcfg.num_fcxp_reqs = BFA_FCXP_MIN; - cfg->fwcfg.num_uf_bufs = BFA_UF_MIN; - cfg->fwcfg.num_rports = BFA_RPORT_MIN; - cfg->fwcfg.num_fwtio_reqs = 0; - - cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN; - cfg->drvcfg.num_reqq_elems = BFA_REQQ_NELEMS_MIN; - cfg->drvcfg.num_rspq_elems = BFA_RSPQ_NELEMS_MIN; - cfg->drvcfg.min_cfg = BFA_TRUE; -} diff --git a/drivers/scsi/bfa/bfa_defs_fcs.h b/drivers/scsi/bfa/bfa_defs_fcs.h index 5e36620425ac7..760606062c660 100644 --- a/drivers/scsi/bfa/bfa_defs_fcs.h +++ b/drivers/scsi/bfa/bfa_defs_fcs.h @@ -124,28 +124,6 @@ enum bfa_lport_offline_reason { BFA_LPORT_OFFLINE_FAB_LOGOUT, }; -/* - * FCS lport info. - */ -struct bfa_lport_info_s { - u8 port_type; /* bfa_lport_type_t : physical or - * virtual */ - u8 port_state; /* one of bfa_lport_state values */ - u8 offline_reason; /* one of bfa_lport_offline_reason_t - * values */ - wwn_t port_wwn; - wwn_t node_wwn; - - /* - * following 4 feilds are valid for Physical Ports only - */ - u32 max_vports_supp; /* Max supported vports */ - u32 num_vports_inuse; /* Num of in use vports */ - u32 max_rports_supp; /* Max supported rports */ - u32 num_rports_inuse; /* Num of doscovered rports */ - -}; - /* * FCS port statistics */ diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c index 28ae4dc14dc9c..2518e36d70d35 100644 --- a/drivers/scsi/bfa/bfa_fcpim.c +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -3412,15 +3412,6 @@ bfa_tskim_iocdisable_ios(struct bfa_tskim_s *tskim) } } -/* - * Notification on completions from related ioim. - */ -void -bfa_tskim_iodone(struct bfa_tskim_s *tskim) -{ - bfa_wc_down(&tskim->wc); -} - /* * Handle IOC h/w failure notification from itnim. */ diff --git a/drivers/scsi/bfa/bfa_fcpim.h b/drivers/scsi/bfa/bfa_fcpim.h index 4499f84c2d812..64e4485b226e9 100644 --- a/drivers/scsi/bfa/bfa_fcpim.h +++ b/drivers/scsi/bfa/bfa_fcpim.h @@ -339,7 +339,6 @@ void bfa_ioim_tov(struct bfa_ioim_s *ioim); void bfa_tskim_attach(struct bfa_fcpim_s *fcpim); void bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); -void bfa_tskim_iodone(struct bfa_tskim_s *tskim); void bfa_tskim_iocdisable(struct bfa_tskim_s *tskim); void bfa_tskim_cleanup(struct bfa_tskim_s *tskim); void bfa_tskim_res_recfg(struct bfa_s *bfa, u16 num_tskim_fw); diff --git a/drivers/scsi/bfa/bfa_fcs.h b/drivers/scsi/bfa/bfa_fcs.h index 9788354b90da7..19903539a08dc 100644 --- a/drivers/scsi/bfa/bfa_fcs.h +++ b/drivers/scsi/bfa/bfa_fcs.h @@ -373,15 +373,11 @@ bfa_boolean_t bfa_fcs_lport_is_online(struct bfa_fcs_lport_s *port); struct bfa_fcs_lport_s *bfa_fcs_get_base_port(struct bfa_fcs_s *fcs); void bfa_fcs_lport_get_rport_quals(struct bfa_fcs_lport_s *port, struct bfa_rport_qualifier_s rport[], int *nrports); -wwn_t bfa_fcs_lport_get_rport(struct bfa_fcs_lport_s *port, wwn_t wwn, - int index, int nrports, bfa_boolean_t bwwn); struct bfa_fcs_lport_s *bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn); void bfa_fcs_lport_set_symname(struct bfa_fcs_lport_s *port, char *symname); -void bfa_fcs_lport_get_info(struct bfa_fcs_lport_s *port, - struct bfa_lport_info_s *port_info); void bfa_fcs_lport_get_attr(struct bfa_fcs_lport_s *port, struct bfa_lport_attr_s *port_attr); void bfa_fcs_lport_get_stats(struct bfa_fcs_lport_s *fcs_port, @@ -416,8 +412,6 @@ struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_old_pid( struct bfa_fcs_lport_s *port, u32 pid); struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_pwwn( struct bfa_fcs_lport_s *port, wwn_t pwwn); -struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_nwwn( - struct bfa_fcs_lport_s *port, wwn_t nwwn); struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_qualifier( struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 pid); void bfa_fcs_lport_add_rport(struct bfa_fcs_lport_s *port, @@ -486,7 +480,6 @@ bfa_status_t bfa_fcs_pbc_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs, u16 vf_id, struct bfa_lport_cfg_s *port_cfg, struct bfad_vport_s *vport_drv); -bfa_boolean_t bfa_fcs_is_pbc_vport(struct bfa_fcs_vport_s *vport); bfa_status_t bfa_fcs_vport_delete(struct bfa_fcs_vport_s *vport); bfa_status_t bfa_fcs_vport_start(struct bfa_fcs_vport_s *vport); bfa_status_t bfa_fcs_vport_stop(struct bfa_fcs_vport_s *vport); @@ -494,7 +487,6 @@ void bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport, struct bfa_vport_attr_s *vport_attr); struct bfa_fcs_vport_s *bfa_fcs_vport_lookup(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t vpwwn); -void bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport); void bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport); void bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport); void bfa_fcs_vport_delete_comp(struct bfa_fcs_vport_s *vport); @@ -623,8 +615,6 @@ void bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, struct bfa_rport_attr_s *attr); struct bfa_fcs_rport_s *bfa_fcs_rport_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn); -struct bfa_fcs_rport_s *bfa_fcs_rport_lookup_by_nwwn( - struct bfa_fcs_lport_s *port, wwn_t rnwwn); void bfa_fcs_rport_set_del_timeout(u8 rport_tmo); void bfa_fcs_rport_set_max_logins(u32 max_logins); void bfa_fcs_rport_uf_recv(struct bfa_fcs_rport_s *rport, @@ -633,8 +623,6 @@ void bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport); struct bfa_fcs_rport_s *bfa_fcs_rport_create(struct bfa_fcs_lport_s *port, u32 pid); -void bfa_fcs_rport_start(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs, - struct fc_logi_s *plogi_rsp); void bfa_fcs_rport_plogi_create(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs, struct fc_logi_s *plogi); diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 966bf6cc6dd90..9a85f417018f9 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -937,25 +937,6 @@ bfa_fcs_lport_get_rport_by_pwwn(struct bfa_fcs_lport_s *port, wwn_t pwwn) return NULL; } -/* - * NWWN based Lookup for a R-Port in the Port R-Port Queue - */ -struct bfa_fcs_rport_s * -bfa_fcs_lport_get_rport_by_nwwn(struct bfa_fcs_lport_s *port, wwn_t nwwn) -{ - struct bfa_fcs_rport_s *rport; - struct list_head *qe; - - list_for_each(qe, &port->rport_q) { - rport = (struct bfa_fcs_rport_s *) qe; - if (wwn_is_equal(rport->nwwn, nwwn)) - return rport; - } - - bfa_trc(port->fcs, nwwn); - return NULL; -} - /* * PWWN & PID based Lookup for a R-Port in the Port R-Port Queue */ @@ -5645,54 +5626,6 @@ bfa_fcs_get_base_port(struct bfa_fcs_s *fcs) return &fcs->fabric.bport; } -wwn_t -bfa_fcs_lport_get_rport(struct bfa_fcs_lport_s *port, wwn_t wwn, int index, - int nrports, bfa_boolean_t bwwn) -{ - struct list_head *qh, *qe; - struct bfa_fcs_rport_s *rport = NULL; - int i; - struct bfa_fcs_s *fcs; - - if (port == NULL || nrports == 0) - return (wwn_t) 0; - - fcs = port->fcs; - bfa_trc(fcs, (u32) nrports); - - i = 0; - qh = &port->rport_q; - qe = bfa_q_first(qh); - - while ((qe != qh) && (i < nrports)) { - rport = (struct bfa_fcs_rport_s *) qe; - if (bfa_ntoh3b(rport->pid) > 0xFFF000) { - qe = bfa_q_next(qe); - bfa_trc(fcs, (u32) rport->pwwn); - bfa_trc(fcs, rport->pid); - bfa_trc(fcs, i); - continue; - } - - if (bwwn) { - if (!memcmp(&wwn, &rport->pwwn, 8)) - break; - } else { - if (i == index) - break; - } - - i++; - qe = bfa_q_next(qe); - } - - bfa_trc(fcs, i); - if (rport) - return rport->pwwn; - else - return (wwn_t) 0; -} - void bfa_fcs_lport_get_rport_quals(struct bfa_fcs_lport_s *port, struct bfa_rport_qualifier_s rports[], int *nrports) @@ -5823,54 +5756,6 @@ bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn) return NULL; } -/* - * API corresponding to NPIV_VPORT_GETINFO. - */ -void -bfa_fcs_lport_get_info(struct bfa_fcs_lport_s *port, - struct bfa_lport_info_s *port_info) -{ - - bfa_trc(port->fcs, port->fabric->fabric_name); - - if (port->vport == NULL) { - /* - * This is a Physical port - */ - port_info->port_type = BFA_LPORT_TYPE_PHYSICAL; - - /* - * @todo : need to fix the state & reason - */ - port_info->port_state = 0; - port_info->offline_reason = 0; - - port_info->port_wwn = bfa_fcs_lport_get_pwwn(port); - port_info->node_wwn = bfa_fcs_lport_get_nwwn(port); - - port_info->max_vports_supp = - bfa_lps_get_max_vport(port->fcs->bfa); - port_info->num_vports_inuse = - port->fabric->num_vports; - port_info->max_rports_supp = BFA_FCS_MAX_RPORTS_SUPP; - port_info->num_rports_inuse = port->num_rports; - } else { - /* - * This is a virtual port - */ - port_info->port_type = BFA_LPORT_TYPE_VIRTUAL; - - /* - * @todo : need to fix the state & reason - */ - port_info->port_state = 0; - port_info->offline_reason = 0; - - port_info->port_wwn = bfa_fcs_lport_get_pwwn(port); - port_info->node_wwn = bfa_fcs_lport_get_nwwn(port); - } -} - void bfa_fcs_lport_get_stats(struct bfa_fcs_lport_s *fcs_port, struct bfa_lport_stats_s *port_stats) @@ -6567,15 +6452,6 @@ bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport) bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_OFFLINE); } -/* - * Cleanup notification from fabric SM on link timer expiry. - */ -void -bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport) -{ - vport->vport_stats.fab_cleanup++; -} - /* * Stop notification from fabric SM. To be invoked from within FCS. */ @@ -6698,24 +6574,6 @@ bfa_fcs_pbc_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs, return rc; } -/* - * Use this function to findout if this is a pbc vport or not. - * - * @param[in] vport - pointer to bfa_fcs_vport_t. - * - * @returns None - */ -bfa_boolean_t -bfa_fcs_is_pbc_vport(struct bfa_fcs_vport_s *vport) -{ - - if (vport && (vport->lport.port_cfg.preboot_vp == BFA_TRUE)) - return BFA_TRUE; - else - return BFA_FALSE; - -} - /* * Use this function initialize the vport. * diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c index ce52a9c88ae63..d4bde9bbe75ba 100644 --- a/drivers/scsi/bfa/bfa_fcs_rport.c +++ b/drivers/scsi/bfa/bfa_fcs_rport.c @@ -2646,27 +2646,6 @@ bfa_fcs_rport_create_by_wwn(struct bfa_fcs_lport_s *port, wwn_t rpwwn) bfa_sm_send_event(rport, RPSM_EVENT_ADDRESS_DISC); return rport; } -/* - * Called by bport in private loop topology to indicate that a - * rport has been discovered and plogi has been completed. - * - * @param[in] port - base port or vport - * @param[in] rpid - remote port ID - */ -void -bfa_fcs_rport_start(struct bfa_fcs_lport_s *port, struct fchs_s *fchs, - struct fc_logi_s *plogi) -{ - struct bfa_fcs_rport_s *rport; - - rport = bfa_fcs_rport_alloc(port, WWN_NULL, fchs->s_id); - if (!rport) - return; - - bfa_fcs_rport_update(rport, plogi); - - bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_COMP); -} /* * Called by bport/vport to handle PLOGI received from a new remote port. @@ -3089,21 +3068,6 @@ bfa_fcs_rport_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn) return rport; } -struct bfa_fcs_rport_s * -bfa_fcs_rport_lookup_by_nwwn(struct bfa_fcs_lport_s *port, wwn_t rnwwn) -{ - struct bfa_fcs_rport_s *rport; - - rport = bfa_fcs_lport_get_rport_by_nwwn(port, rnwwn); - if (rport == NULL) { - /* - * TBD Error handling - */ - } - - return rport; -} - /* * Remote port features (RPF) implementation. */ diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index ea2f107f564cd..aa68d61a2d0d3 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -2254,33 +2254,12 @@ bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_env) return status; } -/* - * Enable/disable IOC failure auto recovery. - */ -void -bfa_ioc_auto_recover(bfa_boolean_t auto_recover) -{ - bfa_auto_recover = auto_recover; -} - - - bfa_boolean_t bfa_ioc_is_operational(struct bfa_ioc_s *ioc) { return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_op); } -bfa_boolean_t -bfa_ioc_is_initialized(struct bfa_ioc_s *ioc) -{ - u32 r32 = bfa_ioc_get_cur_ioc_fwstate(ioc); - - return ((r32 != BFI_IOC_UNINIT) && - (r32 != BFI_IOC_INITING) && - (r32 != BFI_IOC_MEMTEST)); -} - bfa_boolean_t bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg) { diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h index 3ec10503caff9..d70332e9ad6d7 100644 --- a/drivers/scsi/bfa/bfa_ioc.h +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -919,7 +919,6 @@ void bfa_ioc_ct2_poweron(struct bfa_ioc_s *ioc); void bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, struct bfa_ioc_cbfn_s *cbfn, struct bfa_timer_mod_s *timer_mod); -void bfa_ioc_auto_recover(bfa_boolean_t auto_recover); void bfa_ioc_detach(struct bfa_ioc_s *ioc); void bfa_ioc_suspend(struct bfa_ioc_s *ioc); void bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev, @@ -934,7 +933,6 @@ bfa_status_t bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, void bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *msg); void bfa_ioc_error_isr(struct bfa_ioc_s *ioc); bfa_boolean_t bfa_ioc_is_operational(struct bfa_ioc_s *ioc); -bfa_boolean_t bfa_ioc_is_initialized(struct bfa_ioc_s *ioc); bfa_boolean_t bfa_ioc_is_disabled(struct bfa_ioc_s *ioc); bfa_boolean_t bfa_ioc_is_acq_addr(struct bfa_ioc_s *ioc); bfa_boolean_t bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc); diff --git a/drivers/scsi/bfa/bfa_modules.h b/drivers/scsi/bfa/bfa_modules.h index 578e7678b056e..ed29ebda30da2 100644 --- a/drivers/scsi/bfa/bfa_modules.h +++ b/drivers/scsi/bfa/bfa_modules.h @@ -113,7 +113,6 @@ void bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *, struct bfa_s *); void bfa_sgpg_attach(struct bfa_s *, void *bfad, struct bfa_iocfc_cfg_s *, struct bfa_pcidev_s *); -void bfa_uf_iocdisable(struct bfa_s *); void bfa_uf_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *, struct bfa_s *); void bfa_uf_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *, diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c index 9f33aa303b189..df33afaaa6735 100644 --- a/drivers/scsi/bfa/bfa_svc.c +++ b/drivers/scsi/bfa/bfa_svc.c @@ -913,14 +913,6 @@ bfa_fcxp_get_reqbuf(struct bfa_fcxp_s *fcxp) return reqbuf; } -u32 -bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp) -{ - struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; - - return mod->req_pld_sz; -} - /* * Get the internal response buffer pointer * @@ -1023,21 +1015,6 @@ bfa_fcxp_send(struct bfa_fcxp_s *fcxp, struct bfa_rport_s *rport, bfa_fcxp_queue(fcxp, send_req); } -/* - * Abort a BFA FCXP - * - * @param[in] fcxp BFA fcxp pointer - * - * @return void - */ -bfa_status_t -bfa_fcxp_abort(struct bfa_fcxp_s *fcxp) -{ - bfa_trc(fcxp->fcxp_mod->bfa, fcxp->fcxp_tag); - WARN_ON(1); - return BFA_STATUS_OK; -} - void bfa_fcxp_req_rsp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe, bfa_fcxp_alloc_cbfn_t alloc_cbfn, void *alloc_cbarg, @@ -3857,15 +3834,6 @@ bfa_fcport_clr_hardalpa(struct bfa_s *bfa) return BFA_STATUS_OK; } -bfa_boolean_t -bfa_fcport_get_hardalpa(struct bfa_s *bfa, u8 *alpa) -{ - struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); - - *alpa = fcport->cfg.hardalpa; - return fcport->cfg.cfg_hardalpa; -} - u8 bfa_fcport_get_myalpa(struct bfa_s *bfa) { @@ -3923,17 +3891,6 @@ bfa_fcport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit) /* * Get port attributes. */ - -wwn_t -bfa_fcport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node) -{ - struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); - if (node) - return fcport->nwwn; - else - return fcport->pwwn; -} - void bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr) { @@ -4105,18 +4062,6 @@ bfa_fcport_is_ratelim(struct bfa_s *bfa) } -/* - * Enable/Disable FAA feature in port config - */ -void -bfa_fcport_cfg_faa(struct bfa_s *bfa, u8 state) -{ - struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); - - bfa_trc(bfa, state); - fcport->cfg.faa_state = state; -} - /* * Get default minimum ratelim speed */ @@ -5528,23 +5473,6 @@ uf_recv(struct bfa_s *bfa, struct bfi_uf_frm_rcvd_s *m) bfa_cb_queue(bfa, &uf->hcb_qe, __bfa_cb_uf_recv, uf); } -void -bfa_uf_iocdisable(struct bfa_s *bfa) -{ - struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); - struct bfa_uf_s *uf; - struct list_head *qe, *qen; - - /* Enqueue unused uf resources to free_q */ - list_splice_tail_init(&ufm->uf_unused_q, &ufm->uf_free_q); - - list_for_each_safe(qe, qen, &ufm->uf_posted_q) { - uf = (struct bfa_uf_s *) qe; - list_del(&uf->qe); - bfa_uf_put(ufm, uf); - } -} - void bfa_uf_start(struct bfa_s *bfa) { diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h index 26eeee82bedc6..99a960a8a2dbb 100644 --- a/drivers/scsi/bfa/bfa_svc.h +++ b/drivers/scsi/bfa/bfa_svc.h @@ -587,14 +587,12 @@ bfa_status_t bfa_fcport_cfg_topology(struct bfa_s *bfa, enum bfa_port_topology bfa_fcport_get_topology(struct bfa_s *bfa); enum bfa_port_topology bfa_fcport_get_cfg_topology(struct bfa_s *bfa); bfa_status_t bfa_fcport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa); -bfa_boolean_t bfa_fcport_get_hardalpa(struct bfa_s *bfa, u8 *alpa); u8 bfa_fcport_get_myalpa(struct bfa_s *bfa); bfa_status_t bfa_fcport_clr_hardalpa(struct bfa_s *bfa); bfa_status_t bfa_fcport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxsize); u16 bfa_fcport_get_maxfrsize(struct bfa_s *bfa); u8 bfa_fcport_get_rx_bbcredit(struct bfa_s *bfa); void bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr); -wwn_t bfa_fcport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node); void bfa_fcport_event_register(struct bfa_s *bfa, void (*event_cbfn) (void *cbarg, enum bfa_port_linkstate event), void *event_cbarg); @@ -619,7 +617,6 @@ bfa_boolean_t bfa_fcport_is_trunk_enabled(struct bfa_s *bfa); void bfa_fcport_dportenable(struct bfa_s *bfa); void bfa_fcport_dportdisable(struct bfa_s *bfa); bfa_status_t bfa_fcport_is_pbcdisabled(struct bfa_s *bfa); -void bfa_fcport_cfg_faa(struct bfa_s *bfa, u8 state); bfa_status_t bfa_fcport_cfg_bbcr(struct bfa_s *bfa, bfa_boolean_t on_off, u8 bb_scn); bfa_status_t bfa_fcport_get_bbcr_attr(struct bfa_s *bfa, @@ -687,8 +684,6 @@ void bfa_fcxp_send(struct bfa_fcxp_s *fcxp, struct bfa_rport_s *rport, bfa_cb_fcxp_send_t cbfn, void *cbarg, u32 rsp_maxlen, u8 rsp_timeout); -bfa_status_t bfa_fcxp_abort(struct bfa_fcxp_s *fcxp); -u32 bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp); u32 bfa_fcxp_get_maxrsp(struct bfa_s *bfa); void bfa_fcxp_res_recfg(struct bfa_s *bfa, u16 num_fcxp_fw); diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index 62cb7a864fd53..6aa1d3a7e24bc 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -842,26 +842,6 @@ bfad_drv_init(struct bfad_s *bfad) return BFA_STATUS_OK; } -void -bfad_drv_uninit(struct bfad_s *bfad) -{ - unsigned long flags; - - spin_lock_irqsave(&bfad->bfad_lock, flags); - init_completion(&bfad->comp); - bfa_iocfc_stop(&bfad->bfa); - spin_unlock_irqrestore(&bfad->bfad_lock, flags); - wait_for_completion(&bfad->comp); - - del_timer_sync(&bfad->hal_tmo); - bfa_isr_disable(&bfad->bfa); - bfa_detach(&bfad->bfa); - bfad_remove_intr(bfad); - bfad_hal_mem_release(bfad); - - bfad->bfad_flags &= ~BFAD_DRV_INIT_DONE; -} - void bfad_drv_start(struct bfad_s *bfad) { @@ -1693,9 +1673,8 @@ bfad_init(void) error = bfad_im_module_init(); if (error) { - error = -ENOMEM; printk(KERN_WARNING "bfad_im_module_init failure\n"); - goto ext; + return -ENOMEM; } if (strcmp(FCPI_NAME, " fcpim") == 0) diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h index da42e3261237e..ce2ec51089472 100644 --- a/drivers/scsi/bfa/bfad_drv.h +++ b/drivers/scsi/bfa/bfad_drv.h @@ -312,7 +312,6 @@ void bfad_bfa_tmo(struct timer_list *t); void bfad_init_timer(struct bfad_s *bfad); int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad); void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad); -void bfad_drv_uninit(struct bfad_s *bfad); int bfad_worker(void *ptr); void bfad_debugfs_init(struct bfad_port_s *port); void bfad_debugfs_exit(struct bfad_port_s *port); diff --git a/drivers/scsi/bfa/bfi.h b/drivers/scsi/bfa/bfi.h index 41e6b4dac056a..e1e0e967bcc3f 100644 --- a/drivers/scsi/bfa/bfi.h +++ b/drivers/scsi/bfa/bfi.h @@ -1148,7 +1148,7 @@ struct bfi_diag_dport_scn_testcomp_s { u16 numbuffer; /* from switch */ u8 subtest_status[DPORT_TEST_MAX]; /* 4 bytes */ u32 latency; /* from switch */ - u32 distance; /* from swtich unit in meters */ + u32 distance; /* from switch unit in meters */ /* Buffers required to saturate the link */ u16 frm_sz; /* from switch for buf_reqd */ u8 rsvd[2]; diff --git a/drivers/scsi/bvme6000_scsi.c b/drivers/scsi/bvme6000_scsi.c index f893e9779e9de..baf5f4e479378 100644 --- a/drivers/scsi/bvme6000_scsi.c +++ b/drivers/scsi/bvme6000_scsi.c @@ -106,7 +106,7 @@ static struct platform_driver bvme6000_scsi_driver = { .name = "bvme6000-scsi", }, .probe = bvme6000_probe, - .remove_new = bvme6000_device_remove, + .remove = bvme6000_device_remove, }; static int __init bvme6000_scsi_init(void) diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h index 8a133254c4f65..1e2d7c63a8e36 100644 --- a/drivers/scsi/esas2r/esas2r.h +++ b/drivers/scsi/esas2r/esas2r.h @@ -1045,10 +1045,6 @@ void esas2r_build_mgt_req(struct esas2r_adapter *a, u32 length, void *data); void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq); -void esas2r_build_cli_req(struct esas2r_adapter *a, - struct esas2r_request *rq, - u32 length, - u32 cmd_rsp_len); void esas2r_build_ioctl_req(struct esas2r_adapter *a, struct esas2r_request *rq, u32 length, diff --git a/drivers/scsi/esas2r/esas2r_vda.c b/drivers/scsi/esas2r/esas2r_vda.c index 30028e56df637..5aa728704dfcb 100644 --- a/drivers/scsi/esas2r/esas2r_vda.c +++ b/drivers/scsi/esas2r/esas2r_vda.c @@ -444,23 +444,6 @@ void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq) } } -/* Build a VDA CLI request. */ -void esas2r_build_cli_req(struct esas2r_adapter *a, - struct esas2r_request *rq, - u32 length, - u32 cmd_rsp_len) -{ - struct atto_vda_cli_req *vrq = &rq->vrq->cli; - - clear_vda_request(rq); - - rq->vrq->scsi.function = VDA_FUNC_CLI; - - vrq->length = cpu_to_le32(length); - vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len); - vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge); -} - /* Build a VDA IOCTL request. */ void esas2r_build_ioctl_req(struct esas2r_adapter *a, struct esas2r_request *rq, diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index d223f482488fc..a44768bceb9ab 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -307,6 +307,7 @@ enum { struct hisi_sas_hw { int (*hw_init)(struct hisi_hba *hisi_hba); + int (*fw_info_check)(struct hisi_hba *hisi_hba); int (*interrupt_preinit)(struct hisi_hba *hisi_hba); void (*setup_itct)(struct hisi_hba *hisi_hba, struct hisi_sas_device *device); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 6219807ce3b9e..53cb15f6714bd 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1321,6 +1321,7 @@ static int hisi_sas_softreset_ata_disk(struct domain_device *device) } if (rc == TMF_RESP_FUNC_COMPLETE) { + usleep_range(900, 1000); ata_for_each_link(link, ap, EDGE) { int pmp = sata_srst_pmp(link); @@ -1384,6 +1385,7 @@ static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba) static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 state) { + u32 new_state = hisi_hba->hw->get_phys_state(hisi_hba); struct asd_sas_port *_sas_port = NULL; int phy_no; @@ -1397,7 +1399,7 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 state) continue; /* Report PHY state change to libsas */ - if (state & BIT(phy_no)) { + if (new_state & BIT(phy_no)) { if (do_port_check && sas_port && sas_port->port_dev) { struct domain_device *dev = sas_port->port_dev; @@ -1410,6 +1412,16 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 state) } } else { hisi_sas_phy_down(hisi_hba, phy_no, 0, GFP_KERNEL); + + /* + * The new_state is not ready but old_state is ready, + * the two possible causes: + * 1. The connected device is removed + * 2. Device exists but phyup timed out + */ + if (state & BIT(phy_no)) + hisi_sas_notify_phy_event(phy, + HISI_PHYE_LINK_RESET); } } } @@ -1545,9 +1557,15 @@ void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba) /* Init and wait for PHYs to come up and all libsas event finished. */ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) { struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; - if (!(hisi_hba->phy_state & BIT(phy_no))) + if (!sas_phy->phy->enabled) + continue; + + if (!(hisi_hba->phy_state & BIT(phy_no))) { + hisi_sas_phy_enable(hisi_hba, phy_no, 1); continue; + } async_schedule_domain(hisi_sas_async_init_wait_phyup, phy, &async); @@ -2450,6 +2468,11 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, if (hisi_sas_get_fw_info(hisi_hba) < 0) goto err_out; + if (hisi_hba->hw->fw_info_check) { + if (hisi_hba->hw->fw_info_check(hisi_hba)) + goto err_out; + } + error = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (error) { dev_err(dev, "No usable DMA addressing method\n"); @@ -2630,10 +2653,10 @@ static __init int hisi_sas_init(void) static __exit void hisi_sas_exit(void) { - sas_release_transport(hisi_sas_stt); - if (hisi_sas_debugfs_enable) debugfs_remove(hisi_sas_debugfs_dir); + + sas_release_transport(hisi_sas_stt); } module_init(hisi_sas_init); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 71b5008c3552c..c3e571be22220 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1734,6 +1734,23 @@ static struct attribute *host_v1_hw_attrs[] = { ATTRIBUTE_GROUPS(host_v1_hw); +static int check_fw_info_v1_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + + if (hisi_hba->n_phy < 0 || hisi_hba->n_phy > 9) { + dev_err(dev, "invalid phy number from FW\n"); + return -EINVAL; + } + + if (hisi_hba->queue_count < 0 || hisi_hba->queue_count > 32) { + dev_err(dev, "invalid queue count from FW\n"); + return -EINVAL; + } + + return 0; +} + static const struct scsi_host_template sht_v1_hw = { LIBSAS_SHT_BASE_NO_SLAVE_INIT .device_configure = hisi_sas_device_configure, @@ -1747,6 +1764,7 @@ static const struct scsi_host_template sht_v1_hw = { static const struct hisi_sas_hw hisi_sas_v1_hw = { .hw_init = hisi_sas_v1_init, + .fw_info_check = check_fw_info_v1_hw, .setup_itct = setup_itct_v1_hw, .sl_notify_ssp = sl_notify_ssp_v1_hw, .clear_itct = clear_itct_v1_hw, @@ -1784,7 +1802,7 @@ MODULE_DEVICE_TABLE(acpi, sas_v1_acpi_match); static struct platform_driver hisi_sas_v1_driver = { .probe = hisi_sas_v1_probe, - .remove_new = hisi_sas_remove, + .remove = hisi_sas_remove, .driver = { .name = DRV_NAME, .of_match_table = sas_v1_of_match, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 342d75f12051d..1a62b5d15eca7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3566,6 +3566,23 @@ static void map_queues_v2_hw(struct Scsi_Host *shost) } } +static int check_fw_info_v2_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + + if (hisi_hba->n_phy < 0 || hisi_hba->n_phy > 9) { + dev_err(dev, "invalid phy number from FW\n"); + return -EINVAL; + } + + if (hisi_hba->queue_count < 0 || hisi_hba->queue_count > 16) { + dev_err(dev, "invalid queue count from FW\n"); + return -EINVAL; + } + + return 0; +} + static const struct scsi_host_template sht_v2_hw = { LIBSAS_SHT_BASE_NO_SLAVE_INIT .device_configure = hisi_sas_device_configure, @@ -3582,6 +3599,7 @@ static const struct scsi_host_template sht_v2_hw = { static const struct hisi_sas_hw hisi_sas_v2_hw = { .hw_init = hisi_sas_v2_init, + .fw_info_check = check_fw_info_v2_hw, .interrupt_preinit = hisi_sas_v2_interrupt_preinit, .setup_itct = setup_itct_v2_hw, .slot_index_alloc = slot_index_alloc_quirk_v2_hw, @@ -3631,7 +3649,7 @@ MODULE_DEVICE_TABLE(acpi, sas_v2_acpi_match); static struct platform_driver hisi_sas_v2_driver = { .probe = hisi_sas_v2_probe, - .remove_new = hisi_sas_remove, + .remove = hisi_sas_remove, .driver = { .name = DRV_NAME, .of_match_table = sas_v2_of_match, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 4cd3a3eab6f1c..5db931663ae4e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -43,6 +43,7 @@ #define CQ_INT_CONVERGE_EN 0xb0 #define CFG_AGING_TIME 0xbc #define HGC_DFX_CFG2 0xc0 +#define CFG_ICT_TIMER_STEP_TRSH 0xc8 #define CFG_ABT_SET_QUERY_IPTT 0xd4 #define CFG_SET_ABORTED_IPTT_OFF 0 #define CFG_SET_ABORTED_IPTT_MSK (0xfff << CFG_SET_ABORTED_IPTT_OFF) @@ -638,9 +639,12 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, TRANS_LOCK_ICT_TIME, 0x4A817C80); hisi_sas_write32(hisi_hba, HGC_SAS_TXFAIL_RETRY_CTRL, 0x108); hisi_sas_write32(hisi_hba, CFG_AGING_TIME, 0x1); - hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x1); - hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0x1); - hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0x1); + hisi_sas_write32(hisi_hba, CFG_ICT_TIMER_STEP_TRSH, 0xf4240); + hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x3); + /* configure the interrupt coalescing timeout period 10us */ + hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0xa); + /* configure the count of CQ entries 10 */ + hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0xa); hisi_sas_write32(hisi_hba, CQ_INT_CONVERGE_EN, hisi_sas_intr_conv); hisi_sas_write32(hisi_hba, OQ_INT_SRC, 0xffff); @@ -682,7 +686,7 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_DWS_RESET_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_OOB_RESTART_MSK, 0x1); - hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7f7a120); + hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7ffffff); hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x2a0a01); hisi_sas_phy_write32(hisi_hba, i, SAS_EC_INT_COAL_TIME, 0x30f4240); @@ -2493,6 +2497,7 @@ static int complete_v3_hw(struct hisi_sas_cq *cq) /* update rd_point */ cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); + cond_resched(); return completed; } @@ -2796,14 +2801,15 @@ static void config_intr_coal_v3_hw(struct hisi_hba *hisi_hba) { /* config those registers between enable and disable PHYs */ hisi_sas_stop_phys(hisi_hba); + hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x3); if (hisi_hba->intr_coal_ticks == 0 || hisi_hba->intr_coal_count == 0) { - hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x1); - hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0x1); - hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0x1); + /* configure the interrupt coalescing timeout period 10us */ + hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0xa); + /* configure the count of CQ entries 10 */ + hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0xa); } else { - hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x3); hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, hisi_hba->intr_coal_ticks); hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, @@ -3371,6 +3377,23 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .debugfs_snapshot_regs = debugfs_snapshot_regs_v3_hw, }; +static int check_fw_info_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + + if (hisi_hba->n_phy < 0 || hisi_hba->n_phy > 8) { + dev_err(dev, "invalid phy number from FW\n"); + return -EINVAL; + } + + if (hisi_hba->queue_count < 0 || hisi_hba->queue_count > 16) { + dev_err(dev, "invalid queue count from FW\n"); + return -EINVAL; + } + + return 0; +} + static struct Scsi_Host * hisi_sas_shost_alloc_pci(struct pci_dev *pdev) { @@ -3401,6 +3424,9 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev) if (hisi_sas_get_fw_info(hisi_hba) < 0) goto err_out; + if (check_fw_info_v3_hw(hisi_hba) < 0) + goto err_out; + if (experimental_iopoll_q_cnt < 0 || experimental_iopoll_q_cnt >= hisi_hba->queue_count) dev_err(dev, "iopoll queue count %d cannot exceed or equal 16, using default 0\n", @@ -3550,6 +3576,11 @@ debugfs_to_reg_name_v3_hw(int off, int base_off, return NULL; } +static bool debugfs_dump_is_generated_v3_hw(void *p) +{ + return p ? true : false; +} + static void debugfs_print_reg_v3_hw(u32 *regs_val, struct seq_file *s, const struct hisi_sas_debugfs_reg *reg) { @@ -3575,6 +3606,9 @@ static int debugfs_global_v3_hw_show(struct seq_file *s, void *p) { struct hisi_sas_debugfs_regs *global = s->private; + if (!debugfs_dump_is_generated_v3_hw(global->data)) + return -EPERM; + debugfs_print_reg_v3_hw(global->data, s, &debugfs_global_reg); @@ -3586,6 +3620,9 @@ static int debugfs_axi_v3_hw_show(struct seq_file *s, void *p) { struct hisi_sas_debugfs_regs *axi = s->private; + if (!debugfs_dump_is_generated_v3_hw(axi->data)) + return -EPERM; + debugfs_print_reg_v3_hw(axi->data, s, &debugfs_axi_reg); @@ -3597,6 +3634,9 @@ static int debugfs_ras_v3_hw_show(struct seq_file *s, void *p) { struct hisi_sas_debugfs_regs *ras = s->private; + if (!debugfs_dump_is_generated_v3_hw(ras->data)) + return -EPERM; + debugfs_print_reg_v3_hw(ras->data, s, &debugfs_ras_reg); @@ -3609,6 +3649,9 @@ static int debugfs_port_v3_hw_show(struct seq_file *s, void *p) struct hisi_sas_debugfs_port *port = s->private; const struct hisi_sas_debugfs_reg *reg_port = &debugfs_port_reg; + if (!debugfs_dump_is_generated_v3_hw(port->data)) + return -EPERM; + debugfs_print_reg_v3_hw(port->data, s, reg_port); return 0; @@ -3664,6 +3707,9 @@ static int debugfs_cq_v3_hw_show(struct seq_file *s, void *p) struct hisi_sas_debugfs_cq *debugfs_cq = s->private; int slot; + if (!debugfs_dump_is_generated_v3_hw(debugfs_cq->complete_hdr)) + return -EPERM; + for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) debugfs_cq_show_slot_v3_hw(s, slot, debugfs_cq); @@ -3685,8 +3731,12 @@ static void debugfs_dq_show_slot_v3_hw(struct seq_file *s, int slot, static int debugfs_dq_v3_hw_show(struct seq_file *s, void *p) { + struct hisi_sas_debugfs_dq *debugfs_dq = s->private; int slot; + if (!debugfs_dump_is_generated_v3_hw(debugfs_dq->hdr)) + return -EPERM; + for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) debugfs_dq_show_slot_v3_hw(s, slot, s->private); @@ -3700,6 +3750,9 @@ static int debugfs_iost_v3_hw_show(struct seq_file *s, void *p) struct hisi_sas_iost *iost = debugfs_iost->iost; int i, max_command_entries = HISI_SAS_MAX_COMMANDS; + if (!debugfs_dump_is_generated_v3_hw(iost)) + return -EPERM; + for (i = 0; i < max_command_entries; i++, iost++) { __le64 *data = &iost->qw0; @@ -3719,6 +3772,9 @@ static int debugfs_iost_cache_v3_hw_show(struct seq_file *s, void *p) int i, tab_idx; __le64 *iost; + if (!debugfs_dump_is_generated_v3_hw(iost_cache)) + return -EPERM; + for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, iost_cache++) { /* * Data struct of IOST cache: @@ -3742,6 +3798,9 @@ static int debugfs_itct_v3_hw_show(struct seq_file *s, void *p) struct hisi_sas_debugfs_itct *debugfs_itct = s->private; struct hisi_sas_itct *itct = debugfs_itct->itct; + if (!debugfs_dump_is_generated_v3_hw(itct)) + return -EPERM; + for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) { __le64 *data = &itct->qw0; @@ -3761,6 +3820,9 @@ static int debugfs_itct_cache_v3_hw_show(struct seq_file *s, void *p) int i, tab_idx; __le64 *itct; + if (!debugfs_dump_is_generated_v3_hw(itct_cache)) + return -EPERM; + for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, itct_cache++) { /* * Data struct of ITCT cache: @@ -3778,10 +3840,9 @@ static int debugfs_itct_cache_v3_hw_show(struct seq_file *s, void *p) } DEFINE_SHOW_ATTRIBUTE(debugfs_itct_cache_v3_hw); -static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba) +static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba, int index) { u64 *debugfs_timestamp; - int dump_index = hisi_hba->debugfs_dump_index; struct dentry *dump_dentry; struct dentry *dentry; char name[256]; @@ -3789,17 +3850,17 @@ static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba) int c; int d; - snprintf(name, 256, "%d", dump_index); + snprintf(name, 256, "%d", index); dump_dentry = debugfs_create_dir(name, hisi_hba->debugfs_dump_dentry); - debugfs_timestamp = &hisi_hba->debugfs_timestamp[dump_index]; + debugfs_timestamp = &hisi_hba->debugfs_timestamp[index]; debugfs_create_u64("timestamp", 0400, dump_dentry, debugfs_timestamp); debugfs_create_file("global", 0400, dump_dentry, - &hisi_hba->debugfs_regs[dump_index][DEBUGFS_GLOBAL], + &hisi_hba->debugfs_regs[index][DEBUGFS_GLOBAL], &debugfs_global_v3_hw_fops); /* Create port dir and files */ @@ -3808,7 +3869,7 @@ static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba) snprintf(name, 256, "%d", p); debugfs_create_file(name, 0400, dentry, - &hisi_hba->debugfs_port_reg[dump_index][p], + &hisi_hba->debugfs_port_reg[index][p], &debugfs_port_v3_hw_fops); } @@ -3818,7 +3879,7 @@ static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba) snprintf(name, 256, "%d", c); debugfs_create_file(name, 0400, dentry, - &hisi_hba->debugfs_cq[dump_index][c], + &hisi_hba->debugfs_cq[index][c], &debugfs_cq_v3_hw_fops); } @@ -3828,32 +3889,32 @@ static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba) snprintf(name, 256, "%d", d); debugfs_create_file(name, 0400, dentry, - &hisi_hba->debugfs_dq[dump_index][d], + &hisi_hba->debugfs_dq[index][d], &debugfs_dq_v3_hw_fops); } debugfs_create_file("iost", 0400, dump_dentry, - &hisi_hba->debugfs_iost[dump_index], + &hisi_hba->debugfs_iost[index], &debugfs_iost_v3_hw_fops); debugfs_create_file("iost_cache", 0400, dump_dentry, - &hisi_hba->debugfs_iost_cache[dump_index], + &hisi_hba->debugfs_iost_cache[index], &debugfs_iost_cache_v3_hw_fops); debugfs_create_file("itct", 0400, dump_dentry, - &hisi_hba->debugfs_itct[dump_index], + &hisi_hba->debugfs_itct[index], &debugfs_itct_v3_hw_fops); debugfs_create_file("itct_cache", 0400, dump_dentry, - &hisi_hba->debugfs_itct_cache[dump_index], + &hisi_hba->debugfs_itct_cache[index], &debugfs_itct_cache_v3_hw_fops); debugfs_create_file("axi", 0400, dump_dentry, - &hisi_hba->debugfs_regs[dump_index][DEBUGFS_AXI], + &hisi_hba->debugfs_regs[index][DEBUGFS_AXI], &debugfs_axi_v3_hw_fops); debugfs_create_file("ras", 0400, dump_dentry, - &hisi_hba->debugfs_regs[dump_index][DEBUGFS_RAS], + &hisi_hba->debugfs_regs[index][DEBUGFS_RAS], &debugfs_ras_v3_hw_fops); } @@ -4516,22 +4577,34 @@ static void debugfs_release_v3_hw(struct hisi_hba *hisi_hba, int dump_index) int i; devm_kfree(dev, hisi_hba->debugfs_iost_cache[dump_index].cache); + hisi_hba->debugfs_iost_cache[dump_index].cache = NULL; devm_kfree(dev, hisi_hba->debugfs_itct_cache[dump_index].cache); + hisi_hba->debugfs_itct_cache[dump_index].cache = NULL; devm_kfree(dev, hisi_hba->debugfs_iost[dump_index].iost); + hisi_hba->debugfs_iost[dump_index].iost = NULL; devm_kfree(dev, hisi_hba->debugfs_itct[dump_index].itct); + hisi_hba->debugfs_itct[dump_index].itct = NULL; - for (i = 0; i < hisi_hba->queue_count; i++) + for (i = 0; i < hisi_hba->queue_count; i++) { devm_kfree(dev, hisi_hba->debugfs_dq[dump_index][i].hdr); + hisi_hba->debugfs_dq[dump_index][i].hdr = NULL; + } - for (i = 0; i < hisi_hba->queue_count; i++) + for (i = 0; i < hisi_hba->queue_count; i++) { devm_kfree(dev, hisi_hba->debugfs_cq[dump_index][i].complete_hdr); + hisi_hba->debugfs_cq[dump_index][i].complete_hdr = NULL; + } - for (i = 0; i < DEBUGFS_REGS_NUM; i++) + for (i = 0; i < DEBUGFS_REGS_NUM; i++) { devm_kfree(dev, hisi_hba->debugfs_regs[dump_index][i].data); + hisi_hba->debugfs_regs[dump_index][i].data = NULL; + } - for (i = 0; i < hisi_hba->n_phy; i++) + for (i = 0; i < hisi_hba->n_phy; i++) { devm_kfree(dev, hisi_hba->debugfs_port_reg[dump_index][i].data); + hisi_hba->debugfs_port_reg[dump_index][i].data = NULL; + } } static const struct hisi_sas_debugfs_reg *debugfs_reg_array_v3_hw[DEBUGFS_REGS_NUM] = { @@ -4658,8 +4731,6 @@ static int debugfs_snapshot_regs_v3_hw(struct hisi_hba *hisi_hba) debugfs_snapshot_itct_reg_v3_hw(hisi_hba); debugfs_snapshot_iost_reg_v3_hw(hisi_hba); - debugfs_create_files_v3_hw(hisi_hba); - debugfs_snapshot_restore_v3_hw(hisi_hba); hisi_hba->debugfs_dump_index++; @@ -4743,6 +4814,34 @@ static void debugfs_bist_init_v3_hw(struct hisi_hba *hisi_hba) hisi_hba->debugfs_bist_linkrate = SAS_LINK_RATE_1_5_GBPS; } +static int debugfs_dump_index_v3_hw_show(struct seq_file *s, void *p) +{ + int *debugfs_dump_index = s->private; + + if (*debugfs_dump_index > 0) + seq_printf(s, "%d\n", *debugfs_dump_index - 1); + else + seq_puts(s, "dump not triggered\n"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(debugfs_dump_index_v3_hw); + +static void debugfs_dump_init_v3_hw(struct hisi_hba *hisi_hba) +{ + int i; + + hisi_hba->debugfs_dump_dentry = + debugfs_create_dir("dump", hisi_hba->debugfs_dir); + + debugfs_create_file("latest_dump", 0400, hisi_hba->debugfs_dump_dentry, + &hisi_hba->debugfs_dump_index, + &debugfs_dump_index_v3_hw_fops); + + for (i = 0; i < hisi_sas_debugfs_dump_count; i++) + debugfs_create_files_v3_hw(hisi_hba, i); +} + static void debugfs_exit_v3_hw(struct hisi_hba *hisi_hba) { debugfs_remove_recursive(hisi_hba->debugfs_dir); @@ -4755,19 +4854,17 @@ static void debugfs_init_v3_hw(struct hisi_hba *hisi_hba) hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev), hisi_sas_debugfs_dir); - debugfs_create_file("trigger_dump", 0200, - hisi_hba->debugfs_dir, - hisi_hba, - &debugfs_trigger_dump_v3_hw_fops); - /* create bist structures */ debugfs_bist_init_v3_hw(hisi_hba); - hisi_hba->debugfs_dump_dentry = - debugfs_create_dir("dump", hisi_hba->debugfs_dir); + debugfs_dump_init_v3_hw(hisi_hba); debugfs_phy_down_cnt_init_v3_hw(hisi_hba); debugfs_fifo_init_v3_hw(hisi_hba); + debugfs_create_file("trigger_dump", 0200, + hisi_hba->debugfs_dir, + hisi_hba, + &debugfs_trigger_dump_v3_hw_fops); } static int @@ -4860,16 +4957,13 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) SHOST_DIX_GUARD_CRC); } - if (hisi_sas_debugfs_enable) - debugfs_init_v3_hw(hisi_hba); - rc = interrupt_preinit_v3_hw(hisi_hba); if (rc) - goto err_out_undo_debugfs; + goto err_out_free_host; rc = scsi_add_host(shost, dev); if (rc) - goto err_out_undo_debugfs; + goto err_out_free_host; rc = sas_register_ha(sha); if (rc) @@ -4880,6 +4974,8 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_out_unregister_ha; scsi_scan_host(shost); + if (hisi_sas_debugfs_enable) + debugfs_init_v3_hw(hisi_hba); pm_runtime_set_autosuspend_delay(dev, 5000); pm_runtime_use_autosuspend(dev); @@ -4900,9 +4996,6 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) sas_unregister_ha(sha); err_out_remove_host: scsi_remove_host(shost); -err_out_undo_debugfs: - if (hisi_sas_debugfs_enable) - debugfs_exit_v3_hw(hisi_hba); err_out_free_host: hisi_sas_free(hisi_hba); scsi_host_put(shost); @@ -4934,6 +5027,8 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) struct Scsi_Host *shost = sha->shost; pm_runtime_get_noresume(dev); + if (hisi_sas_debugfs_enable) + debugfs_exit_v3_hw(hisi_hba); sas_unregister_ha(sha); flush_workqueue(hisi_hba->wq); @@ -4941,9 +5036,6 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) hisi_sas_v3_destroy_irqs(pdev, hisi_hba); hisi_sas_free(hisi_hba); - if (hisi_sas_debugfs_enable) - debugfs_exit_v3_hw(hisi_hba); - scsi_host_put(shost); } @@ -5034,7 +5126,8 @@ static int _suspend_v3_hw(struct device *device) interrupt_disable_v3_hw(hisi_hba); #ifdef CONFIG_PM - if (atomic_read(&device->power.usage_count)) { + if ((device->power.runtime_status == RPM_SUSPENDING) && + atomic_read(&device->power.usage_count)) { dev_err(dev, "PM suspend: host status cannot be suspended\n"); rc = -EBUSY; goto err_out; diff --git a/drivers/scsi/jazz_esp.c b/drivers/scsi/jazz_esp.c index fb04b0b515ab1..35137f5cfb3a8 100644 --- a/drivers/scsi/jazz_esp.c +++ b/drivers/scsi/jazz_esp.c @@ -196,7 +196,7 @@ MODULE_ALIAS("platform:jazz_esp"); static struct platform_driver esp_jazz_driver = { .probe = esp_jazz_probe, - .remove_new = esp_jazz_remove, + .remove = esp_jazz_remove, .driver = { .name = "jazz_esp", }, diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 85059b83ea6b4..1c6b024160da7 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -398,7 +398,11 @@ lpfc_bsg_send_mgmt_cmd(struct bsg_job *job) /* in case no data is transferred */ bsg_reply->reply_payload_rcv_len = 0; - if (ndlp->nlp_flag & NLP_ELS_SND_MASK) + if (test_bit(NLP_PLOGI_SND, &ndlp->nlp_flag) || + test_bit(NLP_PRLI_SND, &ndlp->nlp_flag) || + test_bit(NLP_ADISC_SND, &ndlp->nlp_flag) || + test_bit(NLP_LOGO_SND, &ndlp->nlp_flag) || + test_bit(NLP_RNID_SND, &ndlp->nlp_flag)) return -ENODEV; /* allocate our bsg tracking structure */ diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index d4e46a08f94da..efeb61b15a5b5 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2023 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -571,7 +571,7 @@ int lpfc_issue_reg_vfi(struct lpfc_vport *); int lpfc_issue_unreg_vfi(struct lpfc_vport *); int lpfc_selective_reset(struct lpfc_hba *); int lpfc_sli4_read_config(struct lpfc_hba *); -void lpfc_sli4_node_prep(struct lpfc_hba *); +void lpfc_sli4_node_rpi_restore(struct lpfc_hba *phba); int lpfc_sli4_els_sgl_update(struct lpfc_hba *phba); int lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba); int lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *sglist); @@ -660,6 +660,7 @@ void lpfc_wqe_cmd_template(void); void lpfc_nvmet_cmd_template(void); void lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, uint32_t stat, uint32_t param); +void lpfc_nvmels_flush_cmd(struct lpfc_hba *phba); extern int lpfc_enable_nvmet_cnt; extern unsigned long long lpfc_enable_nvmet[]; extern int lpfc_no_hba_reset_cnt; diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 134bc96dd1340..30891ad17e2a4 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -735,7 +735,7 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0238 Process x%06x NameServer Rsp " - "Data: x%x x%x x%x x%lx x%x\n", Did, + "Data: x%lx x%x x%x x%lx x%x\n", Did, ndlp->nlp_flag, ndlp->nlp_fc4_type, ndlp->nlp_state, vport->fc_flag, vport->fc_rscn_id_cnt); @@ -744,7 +744,7 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) * state of ndlp hit devloss, change state to * allow rediscovery. */ - if (ndlp->nlp_flag & NLP_NPR_2B_DISC && + if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) && ndlp->nlp_state == NLP_STE_UNUSED_NODE) { lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); @@ -832,12 +832,10 @@ lpfc_ns_rsp_audit_did(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) if (ndlp->nlp_type != NLP_NVME_INITIATOR || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) continue; - spin_lock_irq(&ndlp->lock); if (ndlp->nlp_DID == Did) - ndlp->nlp_flag &= ~NLP_NVMET_RECOV; + clear_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag); else - ndlp->nlp_flag |= NLP_NVMET_RECOV; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag); } } } @@ -894,13 +892,11 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type, */ if (vport->phba->nvmet_support) { list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { - if (!(ndlp->nlp_flag & NLP_NVMET_RECOV)) + if (!test_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag)) continue; lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RECOVERY); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NVMET_RECOV; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag); } } @@ -1440,7 +1436,7 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (ndlp) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0242 Process x%x GFF " - "NameServer Rsp Data: x%x x%lx x%x\n", + "NameServer Rsp Data: x%lx x%lx x%x\n", did, ndlp->nlp_flag, vport->fc_flag, vport->fc_rscn_id_cnt); } else { @@ -2226,6 +2222,11 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ulp_status, ulp_word4, latt); if (latt || ulp_status) { + lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY, + "0229 FDMI cmd %04x failed, latt = %d " + "ulp_status: (x%x/x%x), sli_flag x%x\n", + be16_to_cpu(fdmi_cmd), latt, ulp_status, + ulp_word4, phba->sli.sli_flag); /* Look for a retryable error */ if (ulp_status == IOSTAT_LOCAL_REJECT) { @@ -2234,8 +2235,16 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, case IOERR_SLI_DOWN: /* Driver aborted this IO. No retry as error * is likely Offline->Online or some adapter - * error. Recovery will try again. + * error. Recovery will try again, but if port + * is not active there's no point to continue + * issuing follow up FDMI commands. */ + if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) { + free_ndlp = cmdiocb->ndlp; + lpfc_ct_free_iocb(phba, cmdiocb); + lpfc_nlp_put(free_ndlp); + return; + } break; case IOERR_ABORT_IN_PROGRESS: case IOERR_SEQUENCE_TIMEOUT: @@ -2256,12 +2265,6 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, break; } } - - lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, - "0229 FDMI cmd %04x latt = %d " - "ulp_status: x%x, rid x%x\n", - be16_to_cpu(fdmi_cmd), latt, ulp_status, - ulp_word4); } free_ndlp = cmdiocb->ndlp; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index a2d2b02b34187..3fd1aa5cc78cc 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -870,8 +870,8 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) wwn_to_u64(ndlp->nlp_nodename.u.wwn)); len += scnprintf(buf+len, size-len, "RPI:x%04x ", ndlp->nlp_rpi); - len += scnprintf(buf+len, size-len, "flag:x%08x ", - ndlp->nlp_flag); + len += scnprintf(buf+len, size-len, "flag:x%08lx ", + ndlp->nlp_flag); if (!ndlp->nlp_type) len += scnprintf(buf+len, size-len, "UNKNOWN_TYPE "); if (ndlp->nlp_type & NLP_FC_NODE) diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index f5ae8cc158205..3e173b5d00e00 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -102,7 +102,7 @@ struct lpfc_nodelist { spinlock_t lock; /* Node management lock */ - uint32_t nlp_flag; /* entry flags */ + unsigned long nlp_flag; /* entry flags */ uint32_t nlp_DID; /* FC D_ID of entry */ uint32_t nlp_last_elscmd; /* Last ELS cmd sent */ uint16_t nlp_type; @@ -182,37 +182,37 @@ struct lpfc_node_rrq { #define lpfc_ndlp_check_qdepth(phba, ndlp) \ (ndlp->cmd_qdepth < phba->sli4_hba.max_cfg_param.max_xri) -/* Defines for nlp_flag (uint32) */ -#define NLP_IGNR_REG_CMPL 0x00000001 /* Rcvd rscn before we cmpl reg login */ -#define NLP_REG_LOGIN_SEND 0x00000002 /* sent reglogin to adapter */ -#define NLP_RELEASE_RPI 0x00000004 /* Release RPI to free pool */ -#define NLP_SUPPRESS_RSP 0x00000010 /* Remote NPort supports suppress rsp */ -#define NLP_PLOGI_SND 0x00000020 /* sent PLOGI request for this entry */ -#define NLP_PRLI_SND 0x00000040 /* sent PRLI request for this entry */ -#define NLP_ADISC_SND 0x00000080 /* sent ADISC request for this entry */ -#define NLP_LOGO_SND 0x00000100 /* sent LOGO request for this entry */ -#define NLP_RNID_SND 0x00000400 /* sent RNID request for this entry */ -#define NLP_ELS_SND_MASK 0x000007e0 /* sent ELS request for this entry */ -#define NLP_NVMET_RECOV 0x00001000 /* NVMET auditing node for recovery. */ -#define NLP_UNREG_INP 0x00008000 /* UNREG_RPI cmd is in progress */ -#define NLP_DROPPED 0x00010000 /* Init ref count has been dropped */ -#define NLP_DELAY_TMO 0x00020000 /* delay timeout is running for node */ -#define NLP_NPR_2B_DISC 0x00040000 /* node is included in num_disc_nodes */ -#define NLP_RCV_PLOGI 0x00080000 /* Rcv'ed PLOGI from remote system */ -#define NLP_LOGO_ACC 0x00100000 /* Process LOGO after ACC completes */ -#define NLP_TGT_NO_SCSIID 0x00200000 /* good PRLI but no binding for scsid */ -#define NLP_ISSUE_LOGO 0x00400000 /* waiting to issue a LOGO */ -#define NLP_IN_DEV_LOSS 0x00800000 /* devloss in progress */ -#define NLP_ACC_REGLOGIN 0x01000000 /* Issue Reg Login after successful +/* nlp_flag mask bits */ +enum lpfc_nlp_flag { + NLP_IGNR_REG_CMPL = 0, /* Rcvd rscn before we cmpl reg login */ + NLP_REG_LOGIN_SEND = 1, /* sent reglogin to adapter */ + NLP_SUPPRESS_RSP = 4, /* Remote NPort supports suppress rsp */ + NLP_PLOGI_SND = 5, /* sent PLOGI request for this entry */ + NLP_PRLI_SND = 6, /* sent PRLI request for this entry */ + NLP_ADISC_SND = 7, /* sent ADISC request for this entry */ + NLP_LOGO_SND = 8, /* sent LOGO request for this entry */ + NLP_RNID_SND = 10, /* sent RNID request for this entry */ + NLP_NVMET_RECOV = 12, /* NVMET auditing node for recovery. */ + NLP_UNREG_INP = 15, /* UNREG_RPI cmd is in progress */ + NLP_DROPPED = 16, /* Init ref count has been dropped */ + NLP_DELAY_TMO = 17, /* delay timeout is running for node */ + NLP_NPR_2B_DISC = 18, /* node is included in num_disc_nodes */ + NLP_RCV_PLOGI = 19, /* Rcv'ed PLOGI from remote system */ + NLP_LOGO_ACC = 20, /* Process LOGO after ACC completes */ + NLP_TGT_NO_SCSIID = 21, /* good PRLI but no binding for scsid */ + NLP_ISSUE_LOGO = 22, /* waiting to issue a LOGO */ + NLP_IN_DEV_LOSS = 23, /* devloss in progress */ + NLP_ACC_REGLOGIN = 24, /* Issue Reg Login after successful ACC */ -#define NLP_NPR_ADISC 0x02000000 /* Issue ADISC when dq'ed from + NLP_NPR_ADISC = 25, /* Issue ADISC when dq'ed from NPR list */ -#define NLP_RM_DFLT_RPI 0x04000000 /* need to remove leftover dflt RPI */ -#define NLP_NODEV_REMOVE 0x08000000 /* Defer removal till discovery ends */ -#define NLP_TARGET_REMOVE 0x10000000 /* Target remove in process */ -#define NLP_SC_REQ 0x20000000 /* Target requires authentication */ -#define NLP_FIRSTBURST 0x40000000 /* Target supports FirstBurst */ -#define NLP_RPI_REGISTERED 0x80000000 /* nlp_rpi is valid */ + NLP_RM_DFLT_RPI = 26, /* need to remove leftover dflt RPI */ + NLP_NODEV_REMOVE = 27, /* Defer removal till discovery ends */ + NLP_TARGET_REMOVE = 28, /* Target remove in process */ + NLP_SC_REQ = 29, /* Target requires authentication */ + NLP_FIRSTBURST = 30, /* Target supports FirstBurst */ + NLP_RPI_REGISTERED = 31 /* nlp_rpi is valid */ +}; /* There are 4 different double linked lists nodelist entries can reside on. * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index d737b897ddd82..37f0a930d4691 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -725,11 +725,9 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, list_for_each_entry_safe(np, next_np, &vport->fc_nodes, nlp_listp) { if ((np->nlp_state != NLP_STE_NPR_NODE) || - !(np->nlp_flag & NLP_NPR_ADISC)) + !test_bit(NLP_NPR_ADISC, &np->nlp_flag)) continue; - spin_lock_irq(&np->lock); - np->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&np->lock); + clear_bit(NLP_NPR_ADISC, &np->nlp_flag); lpfc_unreg_rpi(vport, np); } lpfc_cleanup_pending_mbox(vport); @@ -864,9 +862,7 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, sizeof(struct lpfc_name)); /* Set state will put ndlp onto node list if not already done */ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) @@ -1018,7 +1014,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * registered with the SCSI transport, remove the initial * reference to trigger node release. */ - if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS) && + if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag) && !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) lpfc_nlp_put(ndlp); @@ -1236,9 +1232,9 @@ lpfc_cmpl_els_link_down(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "6445 ELS completes after LINK_DOWN: " - " Status %x/%x cmd x%x flg x%x\n", + " Status %x/%x cmd x%x flg x%x iotag x%x\n", ulp_status, ulp_word4, cmd, - cmdiocb->cmd_flag); + cmdiocb->cmd_flag, cmdiocb->iotag); if (cmdiocb->cmd_flag & LPFC_IO_FABRIC) { cmdiocb->cmd_flag &= ~LPFC_IO_FABRIC; @@ -1548,7 +1544,7 @@ lpfc_initial_flogi(struct lpfc_vport *vport) * Otherwise, decrement node reference to trigger release. */ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) && - !(ndlp->nlp_flag & NLP_IN_DEV_LOSS)) + !test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag)) lpfc_nlp_put(ndlp); return 0; } @@ -1597,7 +1593,7 @@ lpfc_initial_fdisc(struct lpfc_vport *vport) * Otherwise, decrement node reference to trigger release. */ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) && - !(ndlp->nlp_flag & NLP_IN_DEV_LOSS)) + !test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag)) lpfc_nlp_put(ndlp); return 0; } @@ -1675,9 +1671,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, struct lpfc_nodelist *new_ndlp; struct serv_parm *sp; uint8_t name[sizeof(struct lpfc_name)]; - uint32_t keepDID = 0, keep_nlp_flag = 0; + uint32_t keepDID = 0; int rc; - uint32_t keep_new_nlp_flag = 0; + unsigned long keep_nlp_flag = 0, keep_new_nlp_flag = 0; uint16_t keep_nlp_state; u32 keep_nlp_fc4_type = 0; struct lpfc_nvme_rport *keep_nrport = NULL; @@ -1704,8 +1700,8 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, } lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE, - "3178 PLOGI confirm: ndlp x%x x%x x%x: " - "new_ndlp x%x x%x x%x\n", + "3178 PLOGI confirm: ndlp x%x x%lx x%x: " + "new_ndlp x%x x%lx x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_fc4_type, (new_ndlp ? new_ndlp->nlp_DID : 0), (new_ndlp ? new_ndlp->nlp_flag : 0), @@ -1769,48 +1765,48 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, new_ndlp->nlp_flag = ndlp->nlp_flag; /* if new_ndlp had NLP_UNREG_INP set, keep it */ - if (keep_new_nlp_flag & NLP_UNREG_INP) - new_ndlp->nlp_flag |= NLP_UNREG_INP; + if (test_bit(NLP_UNREG_INP, &keep_new_nlp_flag)) + set_bit(NLP_UNREG_INP, &new_ndlp->nlp_flag); else - new_ndlp->nlp_flag &= ~NLP_UNREG_INP; + clear_bit(NLP_UNREG_INP, &new_ndlp->nlp_flag); /* if new_ndlp had NLP_RPI_REGISTERED set, keep it */ - if (keep_new_nlp_flag & NLP_RPI_REGISTERED) - new_ndlp->nlp_flag |= NLP_RPI_REGISTERED; + if (test_bit(NLP_RPI_REGISTERED, &keep_new_nlp_flag)) + set_bit(NLP_RPI_REGISTERED, &new_ndlp->nlp_flag); else - new_ndlp->nlp_flag &= ~NLP_RPI_REGISTERED; + clear_bit(NLP_RPI_REGISTERED, &new_ndlp->nlp_flag); /* * Retain the DROPPED flag. This will take care of the init * refcount when affecting the state change */ - if (keep_new_nlp_flag & NLP_DROPPED) - new_ndlp->nlp_flag |= NLP_DROPPED; + if (test_bit(NLP_DROPPED, &keep_new_nlp_flag)) + set_bit(NLP_DROPPED, &new_ndlp->nlp_flag); else - new_ndlp->nlp_flag &= ~NLP_DROPPED; + clear_bit(NLP_DROPPED, &new_ndlp->nlp_flag); ndlp->nlp_flag = keep_new_nlp_flag; /* if ndlp had NLP_UNREG_INP set, keep it */ - if (keep_nlp_flag & NLP_UNREG_INP) - ndlp->nlp_flag |= NLP_UNREG_INP; + if (test_bit(NLP_UNREG_INP, &keep_nlp_flag)) + set_bit(NLP_UNREG_INP, &ndlp->nlp_flag); else - ndlp->nlp_flag &= ~NLP_UNREG_INP; + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); /* if ndlp had NLP_RPI_REGISTERED set, keep it */ - if (keep_nlp_flag & NLP_RPI_REGISTERED) - ndlp->nlp_flag |= NLP_RPI_REGISTERED; + if (test_bit(NLP_RPI_REGISTERED, &keep_nlp_flag)) + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); else - ndlp->nlp_flag &= ~NLP_RPI_REGISTERED; + clear_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); /* * Retain the DROPPED flag. This will take care of the init * refcount when affecting the state change */ - if (keep_nlp_flag & NLP_DROPPED) - ndlp->nlp_flag |= NLP_DROPPED; + if (test_bit(NLP_DROPPED, &keep_nlp_flag)) + set_bit(NLP_DROPPED, &ndlp->nlp_flag); else - ndlp->nlp_flag &= ~NLP_DROPPED; + clear_bit(NLP_DROPPED, &ndlp->nlp_flag); spin_unlock_irq(&new_ndlp->lock); spin_unlock_irq(&ndlp->lock); @@ -1888,7 +1884,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, phba->active_rrq_pool); lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE, - "3173 PLOGI confirm exit: new_ndlp x%x x%x x%x\n", + "3173 PLOGI confirm exit: new_ndlp x%x x%lx x%x\n", new_ndlp->nlp_DID, new_ndlp->nlp_flag, new_ndlp->nlp_fc4_type); @@ -2009,7 +2005,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, IOCB_t *irsp; struct lpfc_nodelist *ndlp, *free_ndlp; struct lpfc_dmabuf *prsp; - int disc; + bool disc; struct serv_parm *sp = NULL; u32 ulp_status, ulp_word4, did, iotag; bool release_node = false; @@ -2044,10 +2040,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Since ndlp can be freed in the disc state machine, note if this node * is being used during discovery. */ - spin_lock_irq(&ndlp->lock); - disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + disc = test_and_clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); /* PLOGI completes to NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, @@ -2060,9 +2053,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(vport)) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); goto out; } @@ -2070,11 +2061,8 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Check for retry */ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { /* ELS command is being retried */ - if (disc) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); - } + if (disc) + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); goto out; } /* Warn PLOGI status Don't print the vport to vport rjts */ @@ -2097,7 +2085,8 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * with the reglogin process. */ spin_lock_irq(&ndlp->lock); - if ((ndlp->nlp_flag & (NLP_ACC_REGLOGIN | NLP_RCV_PLOGI)) && + if ((test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag) || + test_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag)) && ndlp->nlp_state == NLP_STE_REG_LOGIN_ISSUE) { spin_unlock_irq(&ndlp->lock); goto out; @@ -2108,8 +2097,8 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * start the device remove process. */ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) { - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS)) + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); + if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag)) release_node = true; } spin_unlock_irq(&ndlp->lock); @@ -2212,12 +2201,13 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry) * outstanding UNREG_RPI mbox command completes, unless we * are going offline. This logic does not apply for Fabric DIDs */ - if ((ndlp->nlp_flag & (NLP_IGNR_REG_CMPL | NLP_UNREG_INP)) && + if ((test_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag) || + test_bit(NLP_UNREG_INP, &ndlp->nlp_flag)) && ((ndlp->nlp_DID & Fabric_DID_MASK) != Fabric_DID_MASK) && !test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "4110 Issue PLOGI x%x deferred " - "on NPort x%x rpi x%x flg x%x Data:" + "on NPort x%x rpi x%x flg x%lx Data:" " x%px\n", ndlp->nlp_defer_did, ndlp->nlp_DID, ndlp->nlp_rpi, ndlp->nlp_flag, ndlp); @@ -2335,10 +2325,10 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ulp_status = get_job_ulpstatus(phba, rspiocb); ulp_word4 = get_job_word4(phba, rspiocb); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_PRLI_SND; + clear_bit(NLP_PRLI_SND, &ndlp->nlp_flag); /* Driver supports multiple FC4 types. Counters matter. */ + spin_lock_irq(&ndlp->lock); vport->fc_prli_sent--; ndlp->fc4_prli_sent--; spin_unlock_irq(&ndlp->lock); @@ -2379,7 +2369,7 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Warn PRLI status */ lpfc_printf_vlog(vport, mode, LOG_ELS, "2754 PRLI DID:%06X Status:x%x/x%x, " - "data: x%x x%x x%x\n", + "data: x%x x%x x%lx\n", ndlp->nlp_DID, ulp_status, ulp_word4, ndlp->nlp_state, ndlp->fc4_prli_sent, ndlp->nlp_flag); @@ -2396,10 +2386,10 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if ((ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE && ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE) || (ndlp->nlp_state == NLP_STE_NPR_NODE && - ndlp->nlp_flag & NLP_DELAY_TMO)) { - lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, + test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag))) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, "2784 PRLI cmpl: Allow Node recovery " - "DID x%06x nstate x%x nflag x%x\n", + "DID x%06x nstate x%x nflag x%lx\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag); goto out; @@ -2420,8 +2410,8 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, spin_lock_irq(&ndlp->lock); if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) && !ndlp->fc4_prli_sent) { - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS)) + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); + if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag)) release_node = true; } spin_unlock_irq(&ndlp->lock); @@ -2496,7 +2486,8 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_type &= ~(NLP_NVME_TARGET | NLP_NVME_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; - ndlp->nlp_flag &= ~(NLP_FIRSTBURST | NLP_NPR_2B_DISC); + clear_bit(NLP_FIRSTBURST, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); ndlp->nvme_fb_size = 0; send_next_prli: @@ -2627,8 +2618,8 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, * the ndlp is used to track outstanding PRLIs for different * FC4 types. */ + set_bit(NLP_PRLI_SND, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_PRLI_SND; vport->fc_prli_sent++; ndlp->fc4_prli_sent++; spin_unlock_irq(&ndlp->lock); @@ -2789,7 +2780,7 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_vport *vport = cmdiocb->vport; IOCB_t *irsp; struct lpfc_nodelist *ndlp; - int disc; + bool disc; u32 ulp_status, ulp_word4, tmo, iotag; bool release_node = false; @@ -2818,10 +2809,8 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Since ndlp can be freed in the disc state machine, note if this node * is being used during discovery. */ - spin_lock_irq(&ndlp->lock); - disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); - ndlp->nlp_flag &= ~(NLP_ADISC_SND | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); + disc = test_and_clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); + clear_bit(NLP_ADISC_SND, &ndlp->nlp_flag); /* ADISC completes to NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0104 ADISC completes to NPort x%x " @@ -2832,9 +2821,7 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(vport)) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); goto out; } @@ -2843,9 +2830,7 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { /* ELS command is being retried */ if (disc) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_set_disctmo(vport); } goto out; @@ -2864,8 +2849,8 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, */ spin_lock_irq(&ndlp->lock); if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) { - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS)) + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); + if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag)) release_node = true; } spin_unlock_irq(&ndlp->lock); @@ -2938,9 +2923,7 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, phba->fc_stat.elsXmitADISC++; elsiocb->cmd_cmpl = lpfc_cmpl_els_adisc; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_ADISC_SND; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_ADISC_SND, &ndlp->nlp_flag); elsiocb->ndlp = lpfc_nlp_get(ndlp); if (!elsiocb->ndlp) { lpfc_els_free_iocb(phba, elsiocb); @@ -2961,9 +2944,7 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, return 0; err: - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_ADISC_SND; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_ADISC_SND, &ndlp->nlp_flag); return 1; } @@ -2985,7 +2966,6 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_nodelist *ndlp = cmdiocb->ndlp; struct lpfc_vport *vport = ndlp->vport; IOCB_t *irsp; - unsigned long flags; uint32_t skip_recovery = 0; int wake_up_waiter = 0; u32 ulp_status; @@ -3007,8 +2987,8 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, iotag = irsp->ulpIoTag; } + clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_LOGO_SND; if (ndlp->save_flags & NLP_WAIT_FOR_LOGO) { wake_up_waiter = 1; ndlp->save_flags &= ~NLP_WAIT_FOR_LOGO; @@ -3023,7 +3003,7 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* LOGO completes to NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0105 LOGO completes to NPort x%x " - "IoTag x%x refcnt %d nflags x%x xflags x%x " + "IoTag x%x refcnt %d nflags x%lx xflags x%x " "Data: x%x x%x x%x x%x\n", ndlp->nlp_DID, iotag, kref_read(&ndlp->kref), ndlp->nlp_flag, @@ -3061,12 +3041,8 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* The driver sets this flag for an NPIV instance that doesn't want to * log into the remote port. */ - if (ndlp->nlp_flag & NLP_TARGET_REMOVE) { - spin_lock_irq(&ndlp->lock); - if (phba->sli_rev == LPFC_SLI_REV4) - ndlp->nlp_flag |= NLP_RELEASE_RPI; - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_TARGET_REMOVE, &ndlp->nlp_flag)) { + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_DEVICE_RM); goto out_rsrc_free; @@ -3089,9 +3065,7 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET) && skip_recovery == 0) { lpfc_cancel_retry_delay_tmo(vport, ndlp); - spin_lock_irqsave(&ndlp->lock, flags); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irqrestore(&ndlp->lock, flags); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "3187 LOGO completes to NPort x%x: Start " @@ -3113,9 +3087,7 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * register with the transport. */ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_DEVICE_RM); } @@ -3156,12 +3128,8 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, uint16_t cmdsize; int rc; - spin_lock_irq(&ndlp->lock); - if (ndlp->nlp_flag & NLP_LOGO_SND) { - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_LOGO_SND, &ndlp->nlp_flag)) return 0; - } - spin_unlock_irq(&ndlp->lock); cmdsize = (2 * sizeof(uint32_t)) + sizeof(struct lpfc_name); elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, @@ -3180,10 +3148,8 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, phba->fc_stat.elsXmitLOGO++; elsiocb->cmd_cmpl = lpfc_cmpl_els_logo; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_SND; - ndlp->nlp_flag &= ~NLP_ISSUE_LOGO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_SND, &ndlp->nlp_flag); + clear_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag); elsiocb->ndlp = lpfc_nlp_get(ndlp); if (!elsiocb->ndlp) { lpfc_els_free_iocb(phba, elsiocb); @@ -3208,9 +3174,7 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, return 0; err: - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_LOGO_SND; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag); return 1; } @@ -3286,13 +3250,13 @@ lpfc_cmpl_els_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, static int lpfc_reg_fab_ctrl_node(struct lpfc_vport *vport, struct lpfc_nodelist *fc_ndlp) { - int rc = 0; + int rc; struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ns_ndlp; LPFC_MBOXQ_t *mbox; - if (fc_ndlp->nlp_flag & NLP_RPI_REGISTERED) - return rc; + if (test_bit(NLP_RPI_REGISTERED, &fc_ndlp->nlp_flag)) + return 0; ns_ndlp = lpfc_findnode_did(vport, NameServer_DID); if (!ns_ndlp) @@ -3309,7 +3273,7 @@ lpfc_reg_fab_ctrl_node(struct lpfc_vport *vport, struct lpfc_nodelist *fc_ndlp) if (!mbox) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, "0936 %s: no memory for reg_login " - "Data: x%x x%x x%x x%x\n", __func__, + "Data: x%x x%x x%lx x%x\n", __func__, fc_ndlp->nlp_DID, fc_ndlp->nlp_state, fc_ndlp->nlp_flag, fc_ndlp->nlp_rpi); return -ENOMEM; @@ -3321,7 +3285,7 @@ lpfc_reg_fab_ctrl_node(struct lpfc_vport *vport, struct lpfc_nodelist *fc_ndlp) goto out; } - fc_ndlp->nlp_flag |= NLP_REG_LOGIN_SEND; + set_bit(NLP_REG_LOGIN_SEND, &fc_ndlp->nlp_flag); mbox->mbox_cmpl = lpfc_mbx_cmpl_fc_reg_login; mbox->ctx_ndlp = lpfc_nlp_get(fc_ndlp); if (!mbox->ctx_ndlp) { @@ -3345,7 +3309,7 @@ lpfc_reg_fab_ctrl_node(struct lpfc_vport *vport, struct lpfc_nodelist *fc_ndlp) lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED); lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, "0938 %s: failed to format reg_login " - "Data: x%x x%x x%x x%x\n", __func__, + "Data: x%x x%x x%lx x%x\n", __func__, fc_ndlp->nlp_DID, fc_ndlp->nlp_state, fc_ndlp->nlp_flag, fc_ndlp->nlp_rpi); return rc; @@ -4384,11 +4348,8 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp) { struct lpfc_work_evt *evtp; - if (!(nlp->nlp_flag & NLP_DELAY_TMO)) + if (!test_and_clear_bit(NLP_DELAY_TMO, &nlp->nlp_flag)) return; - spin_lock_irq(&nlp->lock); - nlp->nlp_flag &= ~NLP_DELAY_TMO; - spin_unlock_irq(&nlp->lock); del_timer_sync(&nlp->nlp_delayfunc); nlp->nlp_last_elscmd = 0; if (!list_empty(&nlp->els_retry_evt.evt_listp)) { @@ -4397,10 +4358,7 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp) evtp = &nlp->els_retry_evt; lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1); } - if (nlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&nlp->lock); - nlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&nlp->lock); + if (test_and_clear_bit(NLP_NPR_2B_DISC, &nlp->nlp_flag)) { if (vport->num_disc_nodes) { if (vport->port_state < LPFC_VPORT_READY) { /* Check if there are more ADISCs to be sent */ @@ -4480,14 +4438,11 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) spin_lock_irq(&ndlp->lock); cmd = ndlp->nlp_last_elscmd; ndlp->nlp_last_elscmd = 0; + spin_unlock_irq(&ndlp->lock); - if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) { - spin_unlock_irq(&ndlp->lock); + if (!test_and_clear_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) return; - } - ndlp->nlp_flag &= ~NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); /* * If a discovery event readded nlp_delayfunc after timer * firing and before processing the timer, cancel the @@ -5010,9 +4965,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* delay is specified in milliseconds */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(delay)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_prev_state = ndlp->nlp_state; if ((cmd == ELS_CMD_PRLI) || @@ -5072,7 +5025,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0108 No retry ELS command x%x to remote " "NPORT x%x Retried:%d Error:x%x/%x " - "IoTag x%x nflags x%x\n", + "IoTag x%x nflags x%lx\n", cmd, did, cmdiocb->retry, ulp_status, ulp_word4, cmdiocb->iotag, (ndlp ? ndlp->nlp_flag : 0)); @@ -5239,7 +5192,7 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* ACC to LOGO completes to NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0109 ACC to LOGO completes to NPort x%x refcnt %d " - "last els x%x Data: x%x x%x x%x\n", + "last els x%x Data: x%lx x%x x%x\n", ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp->nlp_last_elscmd, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); @@ -5254,16 +5207,14 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, goto out; if (ndlp->nlp_state == NLP_STE_NPR_NODE) { - if (ndlp->nlp_flag & NLP_RPI_REGISTERED) + if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) lpfc_unreg_rpi(vport, ndlp); /* If came from PRLO, then PRLO_ACC is done. * Start rediscovery now. */ if (ndlp->nlp_last_elscmd == ELS_CMD_PRLO) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); @@ -5300,7 +5251,7 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (ndlp) { lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, - "0006 rpi x%x DID:%x flg:%x %d x%px " + "0006 rpi x%x DID:%x flg:%lx %d x%px " "mbx_cmd x%x mbx_flag x%x x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref), ndlp, mbx_cmd, @@ -5311,11 +5262,9 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) * first on an UNREG_LOGIN and then release the final * references. */ - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); if (mbx_cmd == MBX_UNREG_LOGIN) - ndlp->nlp_flag &= ~NLP_UNREG_INP; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); lpfc_nlp_put(ndlp); lpfc_drop_node(ndlp->vport, ndlp); } @@ -5381,23 +5330,23 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* ELS response tag completes */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0110 ELS response tag x%x completes " - "Data: x%x x%x x%x x%x x%x x%x x%x x%x %p %p\n", + "Data: x%x x%x x%x x%x x%lx x%x x%x x%x %p %p\n", iotag, ulp_status, ulp_word4, tmo, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi, kref_read(&ndlp->kref), mbox, ndlp); if (mbox) { - if (ulp_status == 0 - && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { + if (ulp_status == 0 && + test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag)) { if (!lpfc_unreg_rpi(vport, ndlp) && !test_bit(FC_PT2PT, &vport->fc_flag)) { - if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || + if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || ndlp->nlp_state == NLP_STE_REG_LOGIN_ISSUE) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0314 PLOGI recov " "DID x%x " - "Data: x%x x%x x%x\n", + "Data: x%x x%x x%lx\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_rpi, @@ -5414,18 +5363,17 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, goto out_free_mbox; mbox->vport = vport; - if (ndlp->nlp_flag & NLP_RM_DFLT_RPI) { + if (test_bit(NLP_RM_DFLT_RPI, &ndlp->nlp_flag)) { mbox->mbox_flag |= LPFC_MBX_IMED_UNREG; mbox->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi; - } - else { + } else { mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE); } - ndlp->nlp_flag |= NLP_REG_LOGIN_SEND; + set_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) != MBX_NOT_FINISHED) goto out; @@ -5434,12 +5382,12 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * set for this failed mailbox command. */ lpfc_nlp_put(ndlp); - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); /* ELS rsp: Cannot issue reg_login for */ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0138 ELS rsp: Cannot issue reg_login for x%x " - "Data: x%x x%x x%x\n", + "Data: x%lx x%x x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); } @@ -5448,32 +5396,20 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } out: if (ndlp && shost) { - spin_lock_irq(&ndlp->lock); if (mbox) - ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN; - ndlp->nlp_flag &= ~NLP_RM_DFLT_RPI; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag); + clear_bit(NLP_RM_DFLT_RPI, &ndlp->nlp_flag); } /* An SLI4 NPIV instance wants to drop the node at this point under - * these conditions and release the RPI. + * these conditions because it doesn't need the login. */ if (phba->sli_rev == LPFC_SLI_REV4 && vport && vport->port_type == LPFC_NPIV_PORT && !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) { - if (ndlp->nlp_flag & NLP_RELEASE_RPI) { - if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE && - ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE) { - lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; - ndlp->nlp_flag &= ~NLP_RELEASE_RPI; - spin_unlock_irq(&ndlp->lock); - } - lpfc_drop_node(vport, ndlp); - } else if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE && - ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE && - ndlp->nlp_state != NLP_STE_PRLI_ISSUE) { + if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE && + ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE && + ndlp->nlp_state != NLP_STE_PRLI_ISSUE) { /* Drop ndlp if there is no planned or outstanding * issued PRLI. * @@ -5540,9 +5476,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp, ndlp->nlp_DID, ELS_CMD_ACC); if (!elsiocb) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); return 1; } @@ -5570,7 +5504,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, pcmd += sizeof(uint32_t); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC: did:x%x flg:x%x", + "Issue ACC: did:x%x flg:x%lx", ndlp->nlp_DID, ndlp->nlp_flag, 0); break; case ELS_CMD_FLOGI: @@ -5649,7 +5583,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC FLOGI/PLOGI: did:x%x flg:x%x", + "Issue ACC FLOGI/PLOGI: did:x%x flg:x%lx", ndlp->nlp_DID, ndlp->nlp_flag, 0); break; case ELS_CMD_PRLO: @@ -5687,7 +5621,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, els_pkt_ptr->un.prlo.acceptRspCode = PRLO_REQ_EXECUTED; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC PRLO: did:x%x flg:x%x", + "Issue ACC PRLO: did:x%x flg:x%lx", ndlp->nlp_DID, ndlp->nlp_flag, 0); break; case ELS_CMD_RDF: @@ -5732,12 +5666,10 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, default: return 1; } - if (ndlp->nlp_flag & NLP_LOGO_ACC) { - spin_lock_irq(&ndlp->lock); - if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED || - ndlp->nlp_flag & NLP_REG_LOGIN_SEND)) - ndlp->nlp_flag &= ~NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_LOGO_ACC, &ndlp->nlp_flag)) { + if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag) && + !test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag)) + clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); elsiocb->cmd_cmpl = lpfc_cmpl_els_logo_acc; } else { elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp; @@ -5760,7 +5692,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, /* Xmit ELS ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0128 Xmit ELS ACC response Status: x%x, IoTag: x%x, " - "XRI: x%x, DID: x%x, nlp_flag: x%x nlp_state: x%x " + "XRI: x%x, DID: x%x, nlp_flag: x%lx nlp_state: x%x " "RPI: x%x, fc_flag x%lx refcnt %d\n", rc, elsiocb->iotag, elsiocb->sli4_xritag, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, @@ -5835,13 +5767,13 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError, /* Xmit ELS RJT response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0129 Xmit ELS RJT x%x response tag x%x " - "xri x%x, did x%x, nlp_flag x%x, nlp_state x%x, " + "xri x%x, did x%x, nlp_flag x%lx, nlp_state x%x, " "rpi x%x\n", rejectError, elsiocb->iotag, get_job_ulpcontext(phba, elsiocb), ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue LS_RJT: did:x%x flg:x%x err:x%x", + "Issue LS_RJT: did:x%x flg:x%lx err:x%x", ndlp->nlp_DID, ndlp->nlp_flag, rejectError); phba->fc_stat.elsXmitLSRJT++; @@ -5852,18 +5784,6 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError, return 1; } - /* The NPIV instance is rejecting this unsolicited ELS. Make sure the - * node's assigned RPI gets released provided this node is not already - * registered with the transport. - */ - if (phba->sli_rev == LPFC_SLI_REV4 && - vport->port_type == LPFC_NPIV_PORT && - !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_RELEASE_RPI; - spin_unlock_irq(&ndlp->lock); - } - rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); @@ -5944,7 +5864,7 @@ lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, lpfc_format_edc_lft_desc(phba, tlv); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue EDC ACC: did:x%x flg:x%x refcnt %d", + "Issue EDC ACC: did:x%x flg:x%lx refcnt %d", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp; @@ -5966,7 +5886,7 @@ lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, /* Xmit ELS ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0152 Xmit EDC ACC response Status: x%x, IoTag: x%x, " - "XRI: x%x, DID: x%x, nlp_flag: x%x nlp_state: x%x " + "XRI: x%x, DID: x%x, nlp_flag: x%lx nlp_state: x%x " "RPI: x%x, fc_flag x%lx\n", rc, elsiocb->iotag, elsiocb->sli4_xritag, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, @@ -6035,7 +5955,7 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, /* Xmit ADISC ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0130 Xmit ADISC ACC response iotag x%x xri: " - "x%x, did x%x, nlp_flag x%x, nlp_state x%x rpi x%x\n", + "x%x, did x%x, nlp_flag x%lx, nlp_state x%x rpi x%x\n", elsiocb->iotag, ulp_context, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); @@ -6051,7 +5971,7 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, ap->DID = be32_to_cpu(vport->fc_myDID); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC ADISC: did:x%x flg:x%x refcnt %d", + "Issue ACC ADISC: did:x%x flg:x%lx refcnt %d", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); phba->fc_stat.elsXmitACC++; @@ -6157,7 +6077,7 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, /* Xmit PRLI ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0131 Xmit PRLI ACC response tag x%x xri x%x, " - "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x\n", + "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x\n", elsiocb->iotag, ulp_context, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); @@ -6228,7 +6148,7 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, "6015 NVME issue PRLI ACC word1 x%08x " - "word4 x%08x word5 x%08x flag x%x, " + "word4 x%08x word5 x%08x flag x%lx, " "fcp_info x%x nlp_type x%x\n", npr_nvme->word1, npr_nvme->word4, npr_nvme->word5, ndlp->nlp_flag, @@ -6243,7 +6163,7 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, ndlp->nlp_DID); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC PRLI: did:x%x flg:x%x", + "Issue ACC PRLI: did:x%x flg:x%lx", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); phba->fc_stat.elsXmitACC++; @@ -6357,7 +6277,7 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format, } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC RNID: did:x%x flg:x%x refcnt %d", + "Issue ACC RNID: did:x%x flg:x%lx refcnt %d", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); phba->fc_stat.elsXmitACC++; @@ -6414,7 +6334,7 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport, get_job_ulpcontext(phba, iocb)); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Clear RRQ: did:x%x flg:x%x exchg:x%.08x", + "Clear RRQ: did:x%x flg:x%lx exchg:x%.08x", ndlp->nlp_DID, ndlp->nlp_flag, rrq->rrq_exchg); if (vport->fc_myDID == be32_to_cpu(bf_get(rrq_did, rrq))) xri = bf_get(rrq_oxid, rrq); @@ -6491,7 +6411,7 @@ lpfc_els_rsp_echo_acc(struct lpfc_vport *vport, uint8_t *data, memcpy(pcmd, data, cmdsize - sizeof(uint32_t)); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, - "Issue ACC ECHO: did:x%x flg:x%x refcnt %d", + "Issue ACC ECHO: did:x%x flg:x%lx refcnt %d", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); phba->fc_stat.elsXmitACC++; @@ -6541,14 +6461,12 @@ lpfc_els_disc_adisc(struct lpfc_vport *vport) list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { if (ndlp->nlp_state != NLP_STE_NPR_NODE || - !(ndlp->nlp_flag & NLP_NPR_ADISC)) + !test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) continue; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); - if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC)) { + if (!test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { /* This node was marked for ADISC but was not picked * for discovery. This is possible if the node was * missing in gidft response. @@ -6606,9 +6524,9 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport) /* go thru NPR nodes and issue any remaining ELS PLOGIs */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { if (ndlp->nlp_state == NLP_STE_NPR_NODE && - (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 && - (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 && - (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) { + test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) && + !test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag) && + !test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) { ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); @@ -7104,7 +7022,7 @@ lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context, lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "2171 Xmit RDP response tag x%x xri x%x, " - "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x", + "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x", elsiocb->iotag, ulp_context, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); @@ -8078,7 +7996,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, */ if (vport->port_state <= LPFC_NS_QRY) { lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RSCN ignore: did:x%x/ste:x%x flg:x%x", + "RCV RSCN ignore: did:x%x/ste:x%x flg:x%lx", ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag); lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); @@ -8108,7 +8026,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, vport->fc_flag, payload_len, *lp, vport->fc_rscn_id_cnt); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RSCN vport: did:x%x/ste:x%x flg:x%x", + "RCV RSCN vport: did:x%x/ste:x%x flg:x%lx", ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag); @@ -8145,7 +8063,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, if (test_bit(FC_RSCN_MODE, &vport->fc_flag) || test_bit(FC_NDISC_ACTIVE, &vport->fc_flag)) { lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RSCN defer: did:x%x/ste:x%x flg:x%x", + "RCV RSCN defer: did:x%x/ste:x%x flg:x%lx", ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag); set_bit(FC_RSCN_DEFERRED, &vport->fc_flag); @@ -8201,7 +8119,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, return 0; } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RSCN: did:x%x/ste:x%x flg:x%x", + "RCV RSCN: did:x%x/ste:x%x flg:x%lx", ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag); set_bit(FC_RSCN_MODE, &vport->fc_flag); @@ -8707,7 +8625,7 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) /* Xmit ELS RLS ACC response tag */ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_ELS, "2874 Xmit ELS RLS ACC response tag x%x xri x%x, " - "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x\n", + "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x\n", elsiocb->iotag, ulp_context, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); @@ -8869,7 +8787,7 @@ lpfc_els_rcv_rtv(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, /* Xmit ELS RLS ACC response tag */ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_ELS, "2875 Xmit ELS RTV ACC response tag x%x xri x%x, " - "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x, " + "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x, " "Data: x%x x%x x%x\n", elsiocb->iotag, ulp_context, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, @@ -9066,7 +8984,7 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize, /* Xmit ELS RPL ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0120 Xmit ELS RPL ACC response tag x%x " - "xri x%x, did x%x, nlp_flag x%x, nlp_state x%x, " + "xri x%x, did x%x, nlp_flag x%lx, nlp_state x%x, " "rpi x%x\n", elsiocb->iotag, ulp_context, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, @@ -9642,14 +9560,24 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) mbx_tmo_err = test_bit(MBX_TMO_ERR, &phba->bit_flags); /* First we need to issue aborts to outstanding cmds on txcmpl */ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { - if (piocb->cmd_flag & LPFC_IO_LIBDFC && !mbx_tmo_err) - continue; + lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, + "2243 iotag = 0x%x cmd_flag = 0x%x " + "ulp_command = 0x%x this_vport %x " + "sli_flag = 0x%x\n", + piocb->iotag, piocb->cmd_flag, + get_job_cmnd(phba, piocb), + (piocb->vport == vport), + phba->sli.sli_flag); if (piocb->vport != vport) continue; - if (piocb->cmd_flag & LPFC_DRIVER_ABORTED && !mbx_tmo_err) - continue; + if ((phba->sli.sli_flag & LPFC_SLI_ACTIVE) && !mbx_tmo_err) { + if (piocb->cmd_flag & LPFC_IO_LIBDFC) + continue; + if (piocb->cmd_flag & LPFC_DRIVER_ABORTED) + continue; + } /* On the ELS ring we can have ELS_REQUESTs, ELS_RSPs, * or GEN_REQUESTs waiting for a CQE response. @@ -10411,14 +10339,11 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, * Do not process any unsolicited ELS commands * if the ndlp is in DEV_LOSS */ - spin_lock_irq(&ndlp->lock); - if (ndlp->nlp_flag & NLP_IN_DEV_LOSS) { - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag)) { if (newnode) lpfc_nlp_put(ndlp); goto dropit; } - spin_unlock_irq(&ndlp->lock); elsiocb->ndlp = lpfc_nlp_get(ndlp); if (!elsiocb->ndlp) @@ -10447,7 +10372,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, switch (cmd) { case ELS_CMD_PLOGI: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV PLOGI: did:x%x/ste:x%x flg:x%x", + "RCV PLOGI: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvPLOGI++; @@ -10486,9 +10411,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } } - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_TARGET_REMOVE; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_TARGET_REMOVE, &ndlp->nlp_flag); lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PLOGI); @@ -10496,7 +10419,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_FLOGI: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV FLOGI: did:x%x/ste:x%x flg:x%x", + "RCV FLOGI: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvFLOGI++; @@ -10523,7 +10446,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_LOGO: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV LOGO: did:x%x/ste:x%x flg:x%x", + "RCV LOGO: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvLOGO++; @@ -10540,7 +10463,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_PRLO: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV PRLO: did:x%x/ste:x%x flg:x%x", + "RCV PRLO: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvPRLO++; @@ -10569,7 +10492,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_ADISC: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV ADISC: did:x%x/ste:x%x flg:x%x", + "RCV ADISC: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); lpfc_send_els_event(vport, ndlp, payload); @@ -10584,7 +10507,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_PDISC: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV PDISC: did:x%x/ste:x%x flg:x%x", + "RCV PDISC: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvPDISC++; @@ -10598,7 +10521,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_FARPR: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV FARPR: did:x%x/ste:x%x flg:x%x", + "RCV FARPR: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvFARPR++; @@ -10606,7 +10529,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_FARP: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV FARP: did:x%x/ste:x%x flg:x%x", + "RCV FARP: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvFARP++; @@ -10614,7 +10537,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_FAN: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV FAN: did:x%x/ste:x%x flg:x%x", + "RCV FAN: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvFAN++; @@ -10623,7 +10546,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, case ELS_CMD_PRLI: case ELS_CMD_NVMEPRLI: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV PRLI: did:x%x/ste:x%x flg:x%x", + "RCV PRLI: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvPRLI++; @@ -10637,7 +10560,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_LIRR: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV LIRR: did:x%x/ste:x%x flg:x%x", + "RCV LIRR: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvLIRR++; @@ -10648,7 +10571,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_RLS: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RLS: did:x%x/ste:x%x flg:x%x", + "RCV RLS: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvRLS++; @@ -10659,7 +10582,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_RPL: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RPL: did:x%x/ste:x%x flg:x%x", + "RCV RPL: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvRPL++; @@ -10670,7 +10593,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_RNID: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RNID: did:x%x/ste:x%x flg:x%x", + "RCV RNID: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvRNID++; @@ -10681,7 +10604,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_RTV: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RTV: did:x%x/ste:x%x flg:x%x", + "RCV RTV: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvRTV++; lpfc_els_rcv_rtv(vport, elsiocb, ndlp); @@ -10691,7 +10614,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_RRQ: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV RRQ: did:x%x/ste:x%x flg:x%x", + "RCV RRQ: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvRRQ++; @@ -10702,7 +10625,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_ECHO: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV ECHO: did:x%x/ste:x%x flg:x%x", + "RCV ECHO: did:x%x/ste:x%x flg:x%lx", did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvECHO++; @@ -10718,7 +10641,8 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; case ELS_CMD_FPIN: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, - "RCV FPIN: did:x%x/ste:x%x flg:x%x", + "RCV FPIN: did:x%x/ste:x%x " + "flg:x%lx", did, vport->port_state, ndlp->nlp_flag); lpfc_els_rcv_fpin(vport, (struct fc_els_fpin *)payload, @@ -11226,9 +11150,7 @@ lpfc_retry_pport_discovery(struct lpfc_hba *phba) return; mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_FLOGI; phba->pport->port_state = LPFC_FLOGI; return; @@ -11359,11 +11281,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, list_for_each_entry_safe(np, next_np, &vport->fc_nodes, nlp_listp) { if ((np->nlp_state != NLP_STE_NPR_NODE) || - !(np->nlp_flag & NLP_NPR_ADISC)) + !test_bit(NLP_NPR_ADISC, &np->nlp_flag)) continue; - spin_lock_irq(&ndlp->lock); - np->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_ADISC, &np->nlp_flag); lpfc_unreg_rpi(vport, np); } lpfc_cleanup_pending_mbox(vport); @@ -11566,7 +11486,7 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* NPIV LOGO completes to NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "2928 NPIV LOGO completes to NPort x%x " - "Data: x%x x%x x%x x%x x%x x%x x%x\n", + "Data: x%x x%x x%x x%x x%x x%lx x%x\n", ndlp->nlp_DID, ulp_status, ulp_word4, tmo, vport->num_disc_nodes, kref_read(&ndlp->kref), ndlp->nlp_flag, @@ -11582,8 +11502,9 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Wake up lpfc_vport_delete if waiting...*/ if (ndlp->logo_waitq) wake_up(ndlp->logo_waitq); + clear_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag); + clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_ISSUE_LOGO | NLP_LOGO_SND); ndlp->save_flags &= ~NLP_WAIT_FOR_LOGO; spin_unlock_irq(&ndlp->lock); } @@ -11633,13 +11554,11 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) memcpy(pcmd, &vport->fc_portname, sizeof(struct lpfc_name)); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD, - "Issue LOGO npiv did:x%x flg:x%x", + "Issue LOGO npiv did:x%x flg:x%lx", ndlp->nlp_DID, ndlp->nlp_flag, 0); elsiocb->cmd_cmpl = lpfc_cmpl_els_npiv_logo; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_SND; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_SND, &ndlp->nlp_flag); elsiocb->ndlp = lpfc_nlp_get(ndlp); if (!elsiocb->ndlp) { lpfc_els_free_iocb(phba, elsiocb); @@ -11655,9 +11574,7 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) return 0; err: - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_LOGO_SND; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag); return 1; } @@ -12138,7 +12055,7 @@ lpfc_sli_abts_recover_port(struct lpfc_vport *vport, lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "3094 Start rport recovery on shost id 0x%x " "fc_id 0x%06x vpi 0x%x rpi 0x%x state 0x%x " - "flags 0x%x\n", + "flag 0x%lx\n", shost->host_no, ndlp->nlp_DID, vport->vpi, ndlp->nlp_rpi, ndlp->nlp_state, ndlp->nlp_flag); @@ -12148,8 +12065,8 @@ lpfc_sli_abts_recover_port(struct lpfc_vport *vport, */ spin_lock_irqsave(&ndlp->lock, flags); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; - ndlp->nlp_flag |= NLP_ISSUE_LOGO; spin_unlock_irqrestore(&ndlp->lock, flags); + set_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag); lpfc_unreg_rpi(vport, ndlp); } diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 9241075f72fa4..4036a9838bb59 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -100,6 +100,12 @@ lpfc_rport_invalid(struct fc_rport *rport) return -EINVAL; } + if (rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) { + pr_info("**** %s: devloss_callbk_done rport x%px SID x%x\n", + __func__, rport, rport->scsi_target_id); + return -EINVAL; + } + rdata = rport->dd_data; if (!rdata) { pr_err("**** %s: NULL dd_data on rport x%px SID x%x\n", @@ -137,7 +143,7 @@ lpfc_terminate_rport_io(struct fc_rport *rport) ndlp = rdata->pnode; vport = ndlp->vport; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, - "rport terminate: sid:x%x did:x%x flg:x%x", + "rport terminate: sid:x%x did:x%x flg:x%lx", ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag); if (ndlp->nlp_sid != NLP_NO_SID) @@ -155,6 +161,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) struct lpfc_hba *phba; struct lpfc_work_evt *evtp; unsigned long iflags; + bool nvme_reg = false; ndlp = ((struct lpfc_rport_data *)rport->dd_data)->pnode; if (!ndlp) @@ -164,11 +171,11 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) phba = vport->phba; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, - "rport devlosscb: sid:x%x did:x%x flg:x%x", + "rport devlosscb: sid:x%x did:x%x flg:x%lx", ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag); lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, - "3181 dev_loss_callbk x%06x, rport x%px flg x%x " + "3181 dev_loss_callbk x%06x, rport x%px flg x%lx " "load_flag x%lx refcnt %u state %d xpt x%x\n", ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag, vport->load_flag, kref_read(&ndlp->kref), @@ -177,38 +184,44 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) /* Don't schedule a worker thread event if the vport is going down. */ if (test_bit(FC_UNLOADING, &vport->load_flag) || !test_bit(HBA_SETUP, &phba->hba_flag)) { + spin_lock_irqsave(&ndlp->lock, iflags); ndlp->rport = NULL; + if (ndlp->fc4_xpt_flags & NVME_XPT_REGD) + nvme_reg = true; + /* The scsi_transport is done with the rport so lpfc cannot - * call to unregister. Remove the scsi transport reference - * and clean up the SCSI transport node details. + * call to unregister. */ - if (ndlp->fc4_xpt_flags & (NLP_XPT_REGD | SCSI_XPT_REGD)) { + if (ndlp->fc4_xpt_flags & SCSI_XPT_REGD) { ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD; - /* NVME transport-registered rports need the - * NLP_XPT_REGD flag to complete an unregister. + /* If NLP_XPT_REGD was cleared in lpfc_nlp_unreg_node, + * unregister calls were made to the scsi and nvme + * transports and refcnt was already decremented. Clear + * the NLP_XPT_REGD flag only if the NVME Rport is + * confirmed unregistered. */ - if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD)) + if (!nvme_reg && ndlp->fc4_xpt_flags & NLP_XPT_REGD) { ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD; + spin_unlock_irqrestore(&ndlp->lock, iflags); + lpfc_nlp_put(ndlp); /* may free ndlp */ + } else { + spin_unlock_irqrestore(&ndlp->lock, iflags); + } + } else { spin_unlock_irqrestore(&ndlp->lock, iflags); - lpfc_nlp_put(ndlp); - spin_lock_irqsave(&ndlp->lock, iflags); } /* Only 1 thread can drop the initial node reference. If * another thread has set NLP_DROPPED, this thread is done. */ - if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD) && - !(ndlp->nlp_flag & NLP_DROPPED)) { - ndlp->nlp_flag |= NLP_DROPPED; - spin_unlock_irqrestore(&ndlp->lock, iflags); - lpfc_nlp_put(ndlp); + if (nvme_reg || test_bit(NLP_DROPPED, &ndlp->nlp_flag)) return; - } - spin_unlock_irqrestore(&ndlp->lock, iflags); + set_bit(NLP_DROPPED, &ndlp->nlp_flag); + lpfc_nlp_put(ndlp); return; } @@ -235,14 +248,14 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) return; } - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag |= NLP_IN_DEV_LOSS; + set_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag); + spin_lock_irqsave(&ndlp->lock, iflags); /* If there is a PLOGI in progress, and we are in a * NLP_NPR_2B_DISC state, don't turn off the flag. */ if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE) - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); /* * The backend does not expect any more calls associated with this @@ -271,15 +284,13 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) } else { lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, "3188 worker thread is stopped %s x%06x, " - " rport x%px flg x%x load_flag x%lx refcnt " + " rport x%px flg x%lx load_flag x%lx refcnt " "%d\n", __func__, ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag, vport->load_flag, kref_read(&ndlp->kref)); if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD)) { - spin_lock_irqsave(&ndlp->lock, iflags); /* Node is in dev loss. No further transaction. */ - ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS; - spin_unlock_irqrestore(&ndlp->lock, iflags); + clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag); lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); } @@ -412,7 +423,7 @@ lpfc_check_nlp_post_devloss(struct lpfc_vport *vport, lpfc_nlp_get(ndlp); lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY | LOG_NODE, "8438 Devloss timeout reversed on DID x%x " - "refcnt %d ndlp %p flag x%x " + "refcnt %d ndlp %p flag x%lx " "port_state = x%x\n", ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp, ndlp->nlp_flag, vport->port_state); @@ -455,7 +466,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) ndlp->nlp_DID, ndlp->nlp_type, ndlp->nlp_sid); lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, - "3182 %s x%06x, nflag x%x xflags x%x refcnt %d\n", + "3182 %s x%06x, nflag x%lx xflags x%x refcnt %d\n", __func__, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->fc4_xpt_flags, kref_read(&ndlp->kref)); @@ -469,9 +480,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) *(name+4), *(name+5), *(name+6), *(name+7), ndlp->nlp_DID); - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS; - spin_unlock_irqrestore(&ndlp->lock, iflags); + clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag); return fcf_inuse; } @@ -499,7 +508,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) } break; case Fabric_Cntl_DID: - if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND) + if (test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag)) recovering = true; break; case FDMI_DID: @@ -527,15 +536,13 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) * the following lpfc_nlp_put is necessary after fabric node is * recovered. */ - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS; - spin_unlock_irqrestore(&ndlp->lock, iflags); + clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag); if (recovering) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY | LOG_NODE, "8436 Devloss timeout marked on " "DID x%x refcnt %d ndlp %p " - "flag x%x port_state = x%x\n", + "flag x%lx port_state = x%x\n", ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp, ndlp->nlp_flag, vport->port_state); @@ -552,7 +559,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) LOG_DISCOVERY | LOG_NODE, "8437 Devloss timeout ignored on " "DID x%x refcnt %d ndlp %p " - "flag x%x port_state = x%x\n", + "flag x%lx port_state = x%x\n", ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp, ndlp->nlp_flag, vport->port_state); @@ -572,7 +579,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0203 Devloss timeout on " "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x " - "NPort x%06x Data: x%x x%x x%x refcnt %d\n", + "NPort x%06x Data: x%lx x%x x%x refcnt %d\n", *name, *(name+1), *(name+2), *(name+3), *(name+4), *(name+5), *(name+6), *(name+7), ndlp->nlp_DID, ndlp->nlp_flag, @@ -582,15 +589,13 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) lpfc_printf_vlog(vport, KERN_INFO, LOG_TRACE_EVENT, "0204 Devloss timeout on " "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x " - "NPort x%06x Data: x%x x%x x%x\n", + "NPort x%06x Data: x%lx x%x x%x\n", *name, *(name+1), *(name+2), *(name+3), *(name+4), *(name+5), *(name+6), *(name+7), ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); } - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS; - spin_unlock_irqrestore(&ndlp->lock, iflags); + clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag); /* If we are devloss, but we are in the process of rediscovering the * ndlp, don't issue a NLP_EVT_DEVICE_RM event. @@ -1355,7 +1360,7 @@ lpfc_linkup_cleanup_nodes(struct lpfc_vport *vport) if (ndlp->nlp_DID != Fabric_DID) lpfc_unreg_rpi(vport, ndlp); lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + } else if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) { /* Fail outstanding IO now since device is * marked for PLOGI. */ @@ -3864,14 +3869,13 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) pmb->ctx_ndlp = NULL; lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI | LOG_NODE | LOG_DISCOVERY, - "0002 rpi:%x DID:%x flg:%x %d x%px\n", + "0002 rpi:%x DID:%x flg:%lx %d x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref), ndlp); - if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND) - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); - if (ndlp->nlp_flag & NLP_IGNR_REG_CMPL || + if (test_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag) || ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE) { /* We rcvd a rscn after issuing this * mbox reg login, we may have cycled @@ -3881,16 +3885,14 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) * there is another reg login in * process. */ - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag); /* * We cannot leave the RPI registered because * if we go thru discovery again for this ndlp * a subsequent REG_RPI will fail. */ - ndlp->nlp_flag |= NLP_RPI_REGISTERED; + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); lpfc_unreg_rpi(vport, ndlp); } @@ -4203,7 +4205,7 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; - ndlp->nlp_flag |= NLP_RPI_REGISTERED; + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); @@ -4334,9 +4336,7 @@ lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) * reference. */ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_nlp_put(ndlp); } @@ -4357,11 +4357,11 @@ lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; - ndlp->nlp_flag |= NLP_RPI_REGISTERED; + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, - "0003 rpi:%x DID:%x flg:%x %d x%px\n", + "0003 rpi:%x DID:%x flg:%lx %d x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref), ndlp); @@ -4453,8 +4453,8 @@ lpfc_mbx_cmpl_fc_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) __func__, ndlp->nlp_DID, ndlp->nlp_rpi, ndlp->nlp_state); - ndlp->nlp_flag |= NLP_RPI_REGISTERED; - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); @@ -4488,7 +4488,7 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, - "rport add: did:x%x flg:x%x type x%x", + "rport add: did:x%x flg:x%lx type x%x", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); /* Don't add the remote port if unloading. */ @@ -4556,7 +4556,7 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp) return; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, - "rport delete: did:x%x flg:x%x type x%x", + "rport delete: did:x%x flg:x%lx type x%x", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, @@ -4672,7 +4672,7 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE | LOG_DISCOVERY, "0999 %s Not regd: ndlp x%px rport x%px DID " - "x%x FLG x%x XPT x%x\n", + "x%x FLG x%lx XPT x%x\n", __func__, ndlp, ndlp->rport, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->fc4_xpt_flags); return; @@ -4688,7 +4688,7 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) } else if (!ndlp->rport) { lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE | LOG_DISCOVERY, - "1999 %s NDLP in devloss x%px DID x%x FLG x%x" + "1999 %s NDLP in devloss x%px DID x%x FLG x%lx" " XPT x%x refcnt %u\n", __func__, ndlp, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->fc4_xpt_flags, @@ -4733,7 +4733,7 @@ lpfc_handle_adisc_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_type |= NLP_FC_NODE; fallthrough; case NLP_STE_MAPPED_NODE: - ndlp->nlp_flag &= ~NLP_NODEV_REMOVE; + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); lpfc_nlp_reg_node(vport, ndlp); break; @@ -4744,7 +4744,7 @@ lpfc_handle_adisc_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, * backend, attempt it now */ case NLP_STE_NPR_NODE: - ndlp->nlp_flag &= ~NLP_RCV_PLOGI; + clear_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag); fallthrough; default: lpfc_nlp_unreg_node(vport, ndlp); @@ -4765,13 +4765,13 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } if (new_state == NLP_STE_UNMAPPED_NODE) { - ndlp->nlp_flag &= ~NLP_NODEV_REMOVE; + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); ndlp->nlp_type |= NLP_FC_NODE; } if (new_state == NLP_STE_MAPPED_NODE) - ndlp->nlp_flag &= ~NLP_NODEV_REMOVE; + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); if (new_state == NLP_STE_NPR_NODE) - ndlp->nlp_flag &= ~NLP_RCV_PLOGI; + clear_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag); /* Reg/Unreg for FCP and NVME Transport interface */ if ((old_state == NLP_STE_MAPPED_NODE || @@ -4779,7 +4779,7 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* For nodes marked for ADISC, Handle unreg in ADISC cmpl * if linkup. In linkdown do unreg_node */ - if (!(ndlp->nlp_flag & NLP_NPR_ADISC) || + if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag) || !lpfc_is_link_up(vport->phba)) lpfc_nlp_unreg_node(vport, ndlp); } @@ -4799,9 +4799,7 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, (!ndlp->rport || ndlp->rport->scsi_target_id == -1 || ndlp->rport->scsi_target_id >= LPFC_MAX_TARGET)) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_TGT_NO_SCSIID, &ndlp->nlp_flag); lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); } } @@ -4833,7 +4831,7 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int state) { int old_state = ndlp->nlp_state; - int node_dropped = ndlp->nlp_flag & NLP_DROPPED; + bool node_dropped = test_bit(NLP_DROPPED, &ndlp->nlp_flag); char name1[16], name2[16]; unsigned long iflags; @@ -4849,7 +4847,7 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (node_dropped && old_state == NLP_STE_UNUSED_NODE && state != NLP_STE_UNUSED_NODE) { - ndlp->nlp_flag &= ~NLP_DROPPED; + clear_bit(NLP_DROPPED, &ndlp->nlp_flag); lpfc_nlp_get(ndlp); } @@ -4857,7 +4855,7 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, state != NLP_STE_NPR_NODE) lpfc_cancel_retry_delay_tmo(vport, ndlp); if (old_state == NLP_STE_UNMAPPED_NODE) { - ndlp->nlp_flag &= ~NLP_TGT_NO_SCSIID; + clear_bit(NLP_TGT_NO_SCSIID, &ndlp->nlp_flag); ndlp->nlp_type &= ~NLP_FC_NODE; } @@ -4954,14 +4952,8 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * reference from lpfc_nlp_init. If set, don't drop it again and * introduce an imbalance. */ - spin_lock_irq(&ndlp->lock); - if (!(ndlp->nlp_flag & NLP_DROPPED)) { - ndlp->nlp_flag |= NLP_DROPPED; - spin_unlock_irq(&ndlp->lock); + if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag)) lpfc_nlp_put(ndlp); - return; - } - spin_unlock_irq(&ndlp->lock); } /* @@ -5076,9 +5068,9 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba, } else if (pring->ringno == LPFC_FCP_RING) { /* Skip match check if waiting to relogin to FCP target */ if ((ndlp->nlp_type & NLP_FCP_TARGET) && - (ndlp->nlp_flag & NLP_DELAY_TMO)) { + test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) return 0; - } + if (ulp_context == ndlp->nlp_rpi) return 1; } @@ -5148,7 +5140,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) * Everything that matches on txcmplq will be returned * by firmware with a no rpi error. */ - if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { + if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) { if (phba->sli_rev != LPFC_SLI_REV4) lpfc_sli3_dequeue_nport_iocbs(phba, ndlp, &completions); else @@ -5182,29 +5174,19 @@ lpfc_nlp_logo_unreg(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) lpfc_issue_els_logo(vport, ndlp, 0); /* Check to see if there are any deferred events to process */ - if ((ndlp->nlp_flag & NLP_UNREG_INP) && - (ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING)) { + if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag) && + ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "1434 UNREG cmpl deferred logo x%x " "on NPort x%x Data: x%x x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_defer_did, ndlp); - ndlp->nlp_flag &= ~NLP_UNREG_INP; + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING; lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); } else { - /* NLP_RELEASE_RPI is only set for SLI4 ports. */ - if (ndlp->nlp_flag & NLP_RELEASE_RPI) { - lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_RELEASE_RPI; - ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; - spin_unlock_irq(&ndlp->lock); - } - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_UNREG_INP; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); } /* The node has an outstanding reference for the unreg. Now @@ -5224,8 +5206,6 @@ static void lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, LPFC_MBOXQ_t *mbox) { - unsigned long iflags; - /* Driver always gets a reference on the mailbox job * in support of async jobs. */ @@ -5233,9 +5213,8 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport, if (!mbox->ctx_ndlp) return; - if (ndlp->nlp_flag & NLP_ISSUE_LOGO) { + if (test_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag)) { mbox->mbox_cmpl = lpfc_nlp_logo_unreg; - } else if (phba->sli_rev == LPFC_SLI_REV4 && !test_bit(FC_UNLOADING, &vport->load_flag) && (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >= @@ -5243,13 +5222,6 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport, (kref_read(&ndlp->kref) > 0)) { mbox->mbox_cmpl = lpfc_sli4_unreg_rpi_cmpl_clr; } else { - if (test_bit(FC_UNLOADING, &vport->load_flag)) { - if (phba->sli_rev == LPFC_SLI_REV4) { - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag |= NLP_RELEASE_RPI; - spin_unlock_irqrestore(&ndlp->lock, iflags); - } - } mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; } } @@ -5271,13 +5243,13 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) int rc, acc_plogi = 1; uint16_t rpi; - if (ndlp->nlp_flag & NLP_RPI_REGISTERED || - ndlp->nlp_flag & NLP_REG_LOGIN_SEND) { - if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND) + if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag) || + test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag)) { + if (test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag)) lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, "3366 RPI x%x needs to be " - "unregistered nlp_flag x%x " + "unregistered nlp_flag x%lx " "did x%x\n", ndlp->nlp_rpi, ndlp->nlp_flag, ndlp->nlp_DID); @@ -5285,11 +5257,11 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) /* If there is already an UNREG in progress for this ndlp, * no need to queue up another one. */ - if (ndlp->nlp_flag & NLP_UNREG_INP) { + if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag)) { lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, "1436 unreg_rpi SKIP UNREG x%x on " - "NPort x%x deferred x%x flg x%x " + "NPort x%x deferred x%x flg x%lx " "Data: x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_defer_did, @@ -5312,27 +5284,24 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) return 1; } + /* Accept PLOGIs after unreg_rpi_cmpl. */ if (mbox->mbox_cmpl == lpfc_sli4_unreg_rpi_cmpl_clr) - /* - * accept PLOGIs after unreg_rpi_cmpl - */ acc_plogi = 0; - if (((ndlp->nlp_DID & Fabric_DID_MASK) != - Fabric_DID_MASK) && - (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag))) - ndlp->nlp_flag |= NLP_UNREG_INP; + + if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) + set_bit(NLP_UNREG_INP, &ndlp->nlp_flag); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, "1433 unreg_rpi UNREG x%x on " - "NPort x%x deferred flg x%x " + "NPort x%x deferred flg x%lx " "Data:x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, ndlp); rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { - ndlp->nlp_flag &= ~NLP_UNREG_INP; + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); mempool_free(mbox, phba->mbox_mem_pool); acc_plogi = 1; lpfc_nlp_put(ndlp); @@ -5342,7 +5311,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) LOG_NODE | LOG_DISCOVERY, "1444 Failed to allocate mempool " "unreg_rpi UNREG x%x, " - "DID x%x, flag x%x, " + "DID x%x, flag x%lx, " "ndlp x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, ndlp); @@ -5352,7 +5321,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * not unloading. */ if (!test_bit(FC_UNLOADING, &vport->load_flag)) { - ndlp->nlp_flag &= ~NLP_UNREG_INP; + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); lpfc_issue_els_logo(vport, ndlp, 0); ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, @@ -5365,13 +5334,13 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) out: if (phba->sli_rev != LPFC_SLI_REV4) ndlp->nlp_rpi = 0; - ndlp->nlp_flag &= ~NLP_RPI_REGISTERED; - ndlp->nlp_flag &= ~NLP_NPR_ADISC; + clear_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); if (acc_plogi) - ndlp->nlp_flag &= ~NLP_LOGO_ACC; + clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); return 1; } - ndlp->nlp_flag &= ~NLP_LOGO_ACC; + clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); return 0; } @@ -5399,7 +5368,7 @@ lpfc_unreg_hba_rpis(struct lpfc_hba *phba) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { spin_lock_irqsave(&vports[i]->fc_nodes_list_lock, iflags); list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) { - if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { + if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) { /* The mempool_alloc might sleep */ spin_unlock_irqrestore(&vports[i]->fc_nodes_list_lock, iflags); @@ -5487,7 +5456,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) /* Cleanup node for NPort */ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, "0900 Cleanup node for NPort x%x " - "Data: x%x x%x x%x\n", + "Data: x%lx x%x x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); lpfc_dequeue_node(vport, ndlp); @@ -5532,9 +5501,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_els_abort(phba, ndlp); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = 0; del_timer_sync(&ndlp->nlp_delayfunc); @@ -5543,10 +5510,6 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) list_del_init(&ndlp->dev_loss_evt.evt_listp); list_del_init(&ndlp->recovery_evt.evt_listp); lpfc_cleanup_vports_rrqs(vport, ndlp); - - if (phba->sli_rev == LPFC_SLI_REV4) - ndlp->nlp_flag |= NLP_RELEASE_RPI; - return 0; } @@ -5620,7 +5583,7 @@ __lpfc_findnode_did(struct lpfc_vport *vport, uint32_t did) ); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE_VERBOSE, "0929 FIND node DID " - "Data: x%px x%x x%x x%x x%x x%px\n", + "Data: x%px x%x x%lx x%x x%x x%px\n", ndlp, ndlp->nlp_DID, ndlp->nlp_flag, data1, ndlp->nlp_rpi, ndlp->active_rrqs_xri_bitmap); @@ -5667,7 +5630,7 @@ lpfc_findnode_mapped(struct lpfc_vport *vport) iflags); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE_VERBOSE, "2025 FIND node DID MAPPED " - "Data: x%px x%x x%x x%x x%px\n", + "Data: x%px x%x x%lx x%x x%px\n", ndlp, ndlp->nlp_DID, ndlp->nlp_flag, data1, ndlp->active_rrqs_xri_bitmap); @@ -5701,13 +5664,11 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "6453 Setup New Node 2B_DISC x%x " - "Data:x%x x%x x%lx\n", + "Data:x%lx x%x x%lx\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, vport->fc_flag); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); return ndlp; } @@ -5726,7 +5687,7 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "6455 Setup RSCN Node 2B_DISC x%x " - "Data:x%x x%x x%lx\n", + "Data:x%lx x%x x%lx\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, vport->fc_flag); @@ -5744,13 +5705,11 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) NLP_EVT_DEVICE_RECOVERY); } - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); } else { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "6456 Skip Setup RSCN Node x%x " - "Data:x%x x%x x%lx\n", + "Data:x%lx x%x x%lx\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, vport->fc_flag); ndlp = NULL; @@ -5758,7 +5717,7 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) } else { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "6457 Setup Active Node 2B_DISC x%x " - "Data:x%x x%x x%lx\n", + "Data:x%lx x%x x%lx\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, vport->fc_flag); @@ -5769,7 +5728,7 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) if (ndlp->nlp_state == NLP_STE_ADISC_ISSUE || ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || (!vport->phba->nvmet_support && - ndlp->nlp_flag & NLP_RCV_PLOGI)) + test_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag))) return NULL; if (vport->phba->nvmet_support) @@ -5779,10 +5738,7 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) * allows for rediscovery */ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); } return ndlp; } @@ -6153,7 +6109,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) /* Clean up the ndlp on Fabric connections */ lpfc_drop_node(vport, ndlp); - } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + } else if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) { /* Fail outstanding IO now since device * is marked for PLOGI. */ @@ -6366,11 +6322,11 @@ lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; - ndlp->nlp_flag |= NLP_RPI_REGISTERED; + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, - "0004 rpi:%x DID:%x flg:%x %d x%px\n", + "0004 rpi:%x DID:%x flg:%lx %d x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref), ndlp); @@ -6420,7 +6376,7 @@ __lpfc_find_node(struct lpfc_vport *vport, node_filter filter, void *param) if (filter(ndlp, param)) { lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE_VERBOSE, "3185 FIND node filter %ps DID " - "ndlp x%px did x%x flg x%x st x%x " + "ndlp x%px did x%x flg x%lx st x%x " "xri x%x type x%x rpi x%x\n", filter, ndlp, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, @@ -6555,9 +6511,10 @@ lpfc_nlp_init(struct lpfc_vport *vport, uint32_t did) INIT_LIST_HEAD(&ndlp->nlp_listp); if (vport->phba->sli_rev == LPFC_SLI_REV4) { ndlp->nlp_rpi = rpi; - lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, - "0007 Init New ndlp x%px, rpi:x%x DID:%x " - "flg:x%x refcnt:%d\n", + lpfc_printf_vlog(vport, KERN_INFO, + LOG_ELS | LOG_NODE | LOG_DISCOVERY, + "0007 Init New ndlp x%px, rpi:x%x DID:x%x " + "flg:x%lx refcnt:%d\n", ndlp, ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); @@ -6589,7 +6546,7 @@ lpfc_nlp_release(struct kref *kref) struct lpfc_vport *vport = ndlp->vport; lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, - "node release: did:x%x flg:x%x type:x%x", + "node release: did:x%x flg:x%lx type:x%x", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, @@ -6601,19 +6558,12 @@ lpfc_nlp_release(struct kref *kref) lpfc_cancel_retry_delay_tmo(vport, ndlp); lpfc_cleanup_node(vport, ndlp); - /* Not all ELS transactions have registered the RPI with the port. - * In these cases the rpi usage is temporary and the node is - * released when the WQE is completed. Catch this case to free the - * RPI to the pool. Because this node is in the release path, a lock - * is unnecessary. All references are gone and the node has been - * dequeued. + /* All nodes are initialized with an RPI that needs to be released + * now. All references are gone and the node has been dequeued. */ - if (ndlp->nlp_flag & NLP_RELEASE_RPI) { - if (ndlp->nlp_rpi != LPFC_RPI_ALLOC_ERROR && - !(ndlp->nlp_flag & (NLP_RPI_REGISTERED | NLP_UNREG_INP))) { - lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi); - ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; - } + if (vport->phba->sli_rev == LPFC_SLI_REV4) { + lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; } /* The node is not freed back to memory, it is released to a pool so @@ -6642,7 +6592,7 @@ lpfc_nlp_get(struct lpfc_nodelist *ndlp) if (ndlp) { lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, - "node get: did:x%x flg:x%x refcnt:x%x", + "node get: did:x%x flg:x%lx refcnt:x%x", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); @@ -6674,7 +6624,7 @@ lpfc_nlp_put(struct lpfc_nodelist *ndlp) { if (ndlp) { lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, - "node put: did:x%x flg:x%x refcnt:x%x", + "node put: did:x%x flg:x%lx refcnt:x%x", ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref)); } else { @@ -6727,11 +6677,12 @@ lpfc_fcf_inuse(struct lpfc_hba *phba) spin_unlock_irqrestore(&vports[i]->fc_nodes_list_lock, iflags); goto out; - } else if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { + } else if (test_bit(NLP_RPI_REGISTERED, + &ndlp->nlp_flag)) { ret = 1; lpfc_printf_log(phba, KERN_INFO, LOG_NODE | LOG_DISCOVERY, - "2624 RPI %x DID %x flag %x " + "2624 RPI %x DID %x flag %lx " "still logged in\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 0dd451009b079..7f57397d91a90 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1943,6 +1943,7 @@ lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action, lpfc_offline_prep(phba, mbx_action); lpfc_sli_flush_io_rings(phba); + lpfc_nvmels_flush_cmd(phba); lpfc_offline(phba); /* release interrupt for possible resource change */ lpfc_sli4_disable_intr(phba); @@ -3092,7 +3093,8 @@ lpfc_cleanup(struct lpfc_vport *vport) lpfc_printf_vlog(ndlp->vport, KERN_ERR, LOG_DISCOVERY, "0282 did:x%x ndlp:x%px " - "refcnt:%d xflags x%x nflag x%x\n", + "refcnt:%d xflags x%x " + "nflag x%lx\n", ndlp->nlp_DID, (void *)ndlp, kref_read(&ndlp->kref), ndlp->fc4_xpt_flags, @@ -3379,7 +3381,7 @@ lpfc_block_mgmt_io(struct lpfc_hba *phba, int mbx_action) } /** - * lpfc_sli4_node_prep - Assign RPIs for active nodes. + * lpfc_sli4_node_rpi_restore - Recover assigned RPIs for active nodes. * @phba: pointer to lpfc hba data structure. * * Allocate RPIs for all active remote nodes. This is needed whenever @@ -3387,7 +3389,7 @@ lpfc_block_mgmt_io(struct lpfc_hba *phba, int mbx_action) * is to fixup the temporary rpi assignments. **/ void -lpfc_sli4_node_prep(struct lpfc_hba *phba) +lpfc_sli4_node_rpi_restore(struct lpfc_hba *phba) { struct lpfc_nodelist *ndlp, *next_ndlp; struct lpfc_vport **vports; @@ -3397,10 +3399,10 @@ lpfc_sli4_node_prep(struct lpfc_hba *phba) return; vports = lpfc_create_vport_work_array(phba); - if (vports == NULL) + if (!vports) return; - for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { + for (i = 0; i <= phba->max_vports && vports[i]; i++) { if (test_bit(FC_UNLOADING, &vports[i]->load_flag)) continue; @@ -3409,14 +3411,20 @@ lpfc_sli4_node_prep(struct lpfc_hba *phba) nlp_listp) { rpi = lpfc_sli4_alloc_rpi(phba); if (rpi == LPFC_RPI_ALLOC_ERROR) { - /* TODO print log? */ + lpfc_printf_vlog(ndlp->vport, KERN_INFO, + LOG_NODE | LOG_DISCOVERY, + "0099 RPI alloc error for " + "ndlp x%px DID:x%06x " + "flg:x%lx\n", + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag); continue; } ndlp->nlp_rpi = rpi; lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY, "0009 Assign RPI x%x to ndlp x%px " - "DID:x%06x flg:x%x\n", + "DID:x%06x flg:x%lx\n", ndlp->nlp_rpi, ndlp, ndlp->nlp_DID, ndlp->nlp_flag); } @@ -3820,35 +3828,12 @@ lpfc_offline_prep(struct lpfc_hba *phba, int mbx_action) &vports[i]->fc_nodes, nlp_listp) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); - + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); if (offline || hba_pci_err) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_UNREG_INP | - NLP_RPI_REGISTERED); - spin_unlock_irq(&ndlp->lock); - if (phba->sli_rev == LPFC_SLI_REV4) - lpfc_sli_rpi_release(vports[i], - ndlp); - } else { - lpfc_unreg_rpi(vports[i], ndlp); - } - /* - * Whenever an SLI4 port goes offline, free the - * RPI. Get a new RPI when the adapter port - * comes back online. - */ - if (phba->sli_rev == LPFC_SLI_REV4) { - lpfc_printf_vlog(vports[i], KERN_INFO, - LOG_NODE | LOG_DISCOVERY, - "0011 Free RPI x%x on " - "ndlp: x%px did x%x\n", - ndlp->nlp_rpi, ndlp, - ndlp->nlp_DID); - lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi); - ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; + clear_bit(NLP_UNREG_INP, + &ndlp->nlp_flag); + clear_bit(NLP_RPI_REGISTERED, + &ndlp->nlp_flag); } if (ndlp->nlp_type & NLP_FABRIC) { @@ -6925,9 +6910,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_FDISC; vport->port_state = LPFC_FDISC; } else { @@ -13518,6 +13501,8 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) /* Disable FW logging to host memory */ lpfc_ras_stop_fwlog(phba); + lpfc_sli4_queue_unset(phba); + /* Reset SLI4 HBA FCoE function */ lpfc_pci_function_reset(phba); diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 4574716c8764f..4d88cfe71caed 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -65,7 +65,7 @@ lpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_name *nn, struct lpfc_name *pn) { /* First, we MUST have a RPI registered */ - if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED)) + if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) return 0; /* Compare the ADISC rsp WWNN / WWPN matches our internal node @@ -239,7 +239,7 @@ lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) /* Abort outstanding I/O on NPort */ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_DISCOVERY, "2819 Abort outstanding I/O on NPort x%x " - "Data: x%x x%x x%x\n", + "Data: x%lx x%x x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); /* Clean up all fabric IOs first.*/ @@ -340,7 +340,7 @@ lpfc_defer_plogi_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *login_mbox) /* Now process the REG_RPI cmpl */ lpfc_mbx_cmpl_reg_login(phba, login_mbox); - ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN; + clear_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag); kfree(save_iocb); } @@ -404,7 +404,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* PLOGI chkparm OK */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, - "0114 PLOGI chkparm OK Data: x%x x%x x%x " + "0114 PLOGI chkparm OK Data: x%x x%x x%lx " "x%x x%x x%lx\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi, vport->port_state, @@ -429,7 +429,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* if already logged in, do implicit logout */ switch (ndlp->nlp_state) { case NLP_STE_NPR_NODE: - if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) + if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) break; fallthrough; case NLP_STE_REG_LOGIN_ISSUE: @@ -449,7 +449,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_type &= ~(NLP_NVME_TARGET | NLP_NVME_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; ndlp->nlp_nvme_info &= ~NLP_NVME_NSLER; - ndlp->nlp_flag &= ~NLP_FIRSTBURST; + clear_bit(NLP_FIRSTBURST, &ndlp->nlp_flag); lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL); @@ -480,7 +480,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_type &= ~(NLP_NVME_TARGET | NLP_NVME_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; ndlp->nlp_nvme_info &= ~NLP_NVME_NSLER; - ndlp->nlp_flag &= ~NLP_FIRSTBURST; + clear_bit(NLP_FIRSTBURST, &ndlp->nlp_flag); login_mbox = NULL; link_mbox = NULL; @@ -552,13 +552,13 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_can_disctmo(vport); } - ndlp->nlp_flag &= ~NLP_SUPPRESS_RSP; + clear_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag); if ((phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) && sp->cmn.valid_vendor_ver_level) { vid = be32_to_cpu(sp->un.vv.vid); flag = be32_to_cpu(sp->un.vv.flags); if ((vid == LPFC_VV_EMLX_ID) && (flag & LPFC_VV_SUPPRESS_RSP)) - ndlp->nlp_flag |= NLP_SUPPRESS_RSP; + set_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag); } login_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -627,10 +627,9 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, * this ELS request. The only way to do this is * to register, then unregister the RPI. */ - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= (NLP_RM_DFLT_RPI | NLP_ACC_REGLOGIN | - NLP_RCV_PLOGI); - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_RM_DFLT_RPI, &ndlp->nlp_flag); + set_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag); + set_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag); } stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD; @@ -665,9 +664,8 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, login_mbox->ctx_u.save_iocb = save_iocb; /* For PLOGI ACC */ - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= (NLP_ACC_REGLOGIN | NLP_RCV_PLOGI); - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag); + set_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag); /* Start the ball rolling by issuing REG_LOGIN here */ rc = lpfc_sli_issue_mbox(phba, login_mbox, MBX_NOWAIT); @@ -797,7 +795,7 @@ lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, */ if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET)) { if ((ndlp->nlp_state != NLP_STE_MAPPED_NODE) && - !(ndlp->nlp_flag & NLP_NPR_ADISC)) + !test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE); } @@ -814,9 +812,7 @@ lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* 1 sec timeout */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); @@ -835,9 +831,7 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary * PLOGIs during LOGO storms from a device. */ - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); if (els_cmd == ELS_CMD_PRLO) lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL); else @@ -890,9 +884,7 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_FDISC; vport->port_state = LPFC_FDISC; } else { @@ -915,14 +907,12 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_state <= NLP_STE_PRLI_ISSUE)) { mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000 * 1)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_ELS | LOG_DISCOVERY, "3204 Start nlpdelay on DID x%06x " - "nflag x%x lastels x%x ref cnt %u", + "nflag x%lx lastels x%x ref cnt %u", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_last_elscmd, kref_read(&ndlp->kref)); @@ -935,9 +925,7 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); /* The driver has to wait until the ACC completes before it continues * processing the LOGO. The action will resume in * lpfc_cmpl_els_logo_acc routine. Since part of processing includes an @@ -978,7 +966,7 @@ lpfc_rcv_prli_support_check(struct lpfc_vport *vport, out: lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY, "6115 Rcv PRLI (%x) check failed: ndlp rpi %d " - "state x%x flags x%x port_type: x%x " + "state x%x flags x%lx port_type: x%x " "npr->initfcn: x%x npr->tgtfcn: x%x\n", cmd, ndlp->nlp_rpi, ndlp->nlp_state, ndlp->nlp_flag, vport->port_type, @@ -1020,7 +1008,7 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (npr->prliType == PRLI_NVME_TYPE) ndlp->nlp_type |= NLP_NVME_TARGET; if (npr->writeXferRdyDis) - ndlp->nlp_flag |= NLP_FIRSTBURST; + set_bit(NLP_FIRSTBURST, &ndlp->nlp_flag); } if (npr->Retry && ndlp->nlp_type & (NLP_FCP_INITIATOR | NLP_FCP_TARGET)) @@ -1057,7 +1045,7 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, roles |= FC_RPORT_ROLE_FCP_TARGET; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, - "rport rolechg: role:x%x did:x%x flg:x%x", + "rport rolechg: role:x%x did:x%x flg:x%lx", roles, ndlp->nlp_DID, ndlp->nlp_flag); if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME) @@ -1068,10 +1056,8 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, static uint32_t lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { - if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED)) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) { + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); return 0; } @@ -1081,16 +1067,12 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) (test_bit(FC_RSCN_MODE, &vport->fc_flag) || ((ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) && (ndlp->nlp_type & NLP_FCP_TARGET)))) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); return 1; } } - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); lpfc_unreg_rpi(vport, ndlp); return 0; } @@ -1115,10 +1097,10 @@ lpfc_release_rpi(struct lpfc_hba *phba, struct lpfc_vport *vport, /* If there is already an UNREG in progress for this ndlp, * no need to queue up another one. */ - if (ndlp->nlp_flag & NLP_UNREG_INP) { + if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag)) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "1435 release_rpi SKIP UNREG x%x on " - "NPort x%x deferred x%x flg x%x " + "NPort x%x deferred x%x flg x%lx " "Data: x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_defer_did, @@ -1143,11 +1125,11 @@ lpfc_release_rpi(struct lpfc_hba *phba, struct lpfc_vport *vport, if (((ndlp->nlp_DID & Fabric_DID_MASK) != Fabric_DID_MASK) && (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag))) - ndlp->nlp_flag |= NLP_UNREG_INP; + set_bit(NLP_UNREG_INP, &ndlp->nlp_flag); lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "1437 release_rpi UNREG x%x " - "on NPort x%x flg x%x\n", + "on NPort x%x flg x%lx\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); @@ -1175,7 +1157,7 @@ lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0271 Illegal State Transition: node x%x " - "event x%x, state x%x Data: x%x x%x\n", + "event x%x, state x%x Data: x%x x%lx\n", ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, ndlp->nlp_flag); return ndlp->nlp_state; @@ -1190,13 +1172,12 @@ lpfc_cmpl_plogi_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, * working on the same NPortID, do nothing for this thread * to stop it. */ - if (!(ndlp->nlp_flag & NLP_RCV_PLOGI)) { + if (!test_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag)) lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0272 Illegal State Transition: node x%x " - "event x%x, state x%x Data: x%x x%x\n", + "event x%x, state x%x Data: x%x x%lx\n", ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, ndlp->nlp_flag); - } return ndlp->nlp_state; } @@ -1230,9 +1211,7 @@ lpfc_rcv_logo_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); return ndlp->nlp_state; @@ -1290,11 +1269,9 @@ lpfc_rcv_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, NULL); } else { if (lpfc_rcv_plogi(vport, ndlp, cmdiocb) && - (ndlp->nlp_flag & NLP_NPR_2B_DISC) && - (vport->num_disc_nodes)) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) && + vport->num_disc_nodes) { + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); /* Check if there are more PLOGIs to be sent */ lpfc_more_plogi(vport); if (vport->num_disc_nodes == 0) { @@ -1356,9 +1333,7 @@ lpfc_rcv_els_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Put ndlp in npr state set plogi timer for 1 sec */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000 * 1)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; ndlp->nlp_prev_state = NLP_STE_PLOGI_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); @@ -1389,7 +1364,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, ulp_status = get_job_ulpstatus(phba, rspiocb); - if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + if (test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag)) { /* Recovery from PLOGI collision logic */ return ndlp->nlp_state; } @@ -1418,7 +1393,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, goto out; /* PLOGI chkparm OK */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, - "0121 PLOGI chkparm OK Data: x%x x%x x%x x%x\n", + "0121 PLOGI chkparm OK Data: x%x x%x x%lx x%x\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi); if (vport->cfg_fcp_class == 2 && (sp->cls2.classValid)) @@ -1446,14 +1421,14 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, ed_tov = (phba->fc_edtov + 999999) / 1000000; } - ndlp->nlp_flag &= ~NLP_SUPPRESS_RSP; + clear_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag); if ((phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) && sp->cmn.valid_vendor_ver_level) { vid = be32_to_cpu(sp->un.vv.vid); flag = be32_to_cpu(sp->un.vv.flags); if ((vid == LPFC_VV_EMLX_ID) && (flag & LPFC_VV_SUPPRESS_RSP)) - ndlp->nlp_flag |= NLP_SUPPRESS_RSP; + set_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag); } /* @@ -1476,7 +1451,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, LOG_TRACE_EVENT, "0133 PLOGI: no memory " "for config_link " - "Data: x%x x%x x%x x%x\n", + "Data: x%x x%x x%lx x%x\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi); goto out; @@ -1500,7 +1475,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, if (!mbox) { lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0018 PLOGI: no memory for reg_login " - "Data: x%x x%x x%x x%x\n", + "Data: x%x x%x x%lx x%x\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi); goto out; @@ -1520,7 +1495,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, mbox->mbox_cmpl = lpfc_mbx_cmpl_fdmi_reg_login; break; default: - ndlp->nlp_flag |= NLP_REG_LOGIN_SEND; + set_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; } @@ -1535,8 +1510,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, NLP_STE_REG_LOGIN_ISSUE); return ndlp->nlp_state; } - if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND) - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); /* decrement node reference count to the failed mbox * command */ @@ -1544,7 +1518,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED); lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0134 PLOGI: cannot issue reg_login " - "Data: x%x x%x x%x x%x\n", + "Data: x%x x%x x%lx x%x\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi); } else { @@ -1552,7 +1526,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0135 PLOGI: cannot format reg_login " - "Data: x%x x%x x%x x%x\n", + "Data: x%x x%x x%lx x%x\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi); } @@ -1605,18 +1579,15 @@ static uint32_t lpfc_device_rm_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NODEV_REMOVE; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { + set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); return ndlp->nlp_state; - } else { - /* software abort outstanding PLOGI */ - lpfc_els_abort(vport->phba, ndlp); - - lpfc_drop_node(vport, ndlp); - return NLP_STE_FREED_NODE; } + /* software abort outstanding PLOGI */ + lpfc_els_abort(vport->phba, ndlp); + + lpfc_drop_node(vport, ndlp); + return NLP_STE_FREED_NODE; } static uint32_t @@ -1636,9 +1607,8 @@ lpfc_device_recov_plogi_issue(struct lpfc_vport *vport, ndlp->nlp_prev_state = NLP_STE_PLOGI_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); return ndlp->nlp_state; } @@ -1656,10 +1626,7 @@ lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, cmdiocb = (struct lpfc_iocbq *) arg; if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) { - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(&ndlp->lock); + if (test_and_clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { if (vport->num_disc_nodes) lpfc_more_adisc(vport); } @@ -1748,9 +1715,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport, /* 1 sec timeout */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE; @@ -1789,18 +1754,15 @@ static uint32_t lpfc_device_rm_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NODEV_REMOVE; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { + set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); return ndlp->nlp_state; - } else { - /* software abort outstanding ADISC */ - lpfc_els_abort(vport->phba, ndlp); - - lpfc_drop_node(vport, ndlp); - return NLP_STE_FREED_NODE; } + /* software abort outstanding ADISC */ + lpfc_els_abort(vport->phba, ndlp); + + lpfc_drop_node(vport, ndlp); + return NLP_STE_FREED_NODE; } static uint32_t @@ -1820,9 +1782,8 @@ lpfc_device_recov_adisc_issue(struct lpfc_vport *vport, ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_disc_set_adisc(vport, ndlp); return ndlp->nlp_state; } @@ -1856,7 +1817,7 @@ lpfc_rcv_prli_reglogin_issue(struct lpfc_vport *vport, * transition to UNMAPPED provided the RPI has completed * registration. */ - if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { + if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) { lpfc_rcv_prli(vport, ndlp, cmdiocb); lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp); } else { @@ -1895,7 +1856,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport, if ((mb = phba->sli.mbox_active)) { if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) && (ndlp == mb->ctx_ndlp)) { - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); lpfc_nlp_put(ndlp); mb->ctx_ndlp = NULL; mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; @@ -1906,7 +1867,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport, list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) { if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) && (ndlp == mb->ctx_ndlp)) { - ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND; + clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag); lpfc_nlp_put(ndlp); list_del(&mb->list); phba->sli.mboxq_cnt--; @@ -1976,9 +1937,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, /* Put ndlp in npr state set plogi timer for 1 sec */ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000 * 1)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; lpfc_issue_els_logo(vport, ndlp, 0); @@ -1989,7 +1948,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, if (phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; - ndlp->nlp_flag |= NLP_RPI_REGISTERED; + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); /* Only if we are not a fabric nport do we issue PRLI */ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, @@ -2061,15 +2020,12 @@ lpfc_device_rm_reglogin_issue(struct lpfc_vport *vport, void *arg, uint32_t evt) { - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NODEV_REMOVE; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { + set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); return ndlp->nlp_state; - } else { - lpfc_drop_node(vport, ndlp); - return NLP_STE_FREED_NODE; } + lpfc_drop_node(vport, ndlp); + return NLP_STE_FREED_NODE; } static uint32_t @@ -2084,17 +2040,16 @@ lpfc_device_recov_reglogin_issue(struct lpfc_vport *vport, ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); /* If we are a target we won't immediately transition into PRLI, * so if REG_LOGIN already completed we don't need to ignore it. */ - if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED) || + if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag) || !vport->phba->nvmet_support) - ndlp->nlp_flag |= NLP_IGNR_REG_CMPL; + set_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_disc_set_adisc(vport, ndlp); return ndlp->nlp_state; } @@ -2228,7 +2183,8 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (npr->targetFunc) { ndlp->nlp_type |= NLP_FCP_TARGET; if (npr->writeXferRdyDis) - ndlp->nlp_flag |= NLP_FIRSTBURST; + set_bit(NLP_FIRSTBURST, + &ndlp->nlp_flag); } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; @@ -2272,7 +2228,7 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Both sides support FB. The target's first * burst size is a 512 byte encoded value. */ - ndlp->nlp_flag |= NLP_FIRSTBURST; + set_bit(NLP_FIRSTBURST, &ndlp->nlp_flag); ndlp->nvme_fb_size = bf_get_be32(prli_fb_sz, nvpr); @@ -2287,7 +2243,7 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, "6029 NVME PRLI Cmpl w1 x%08x " - "w4 x%08x w5 x%08x flag x%x, " + "w4 x%08x w5 x%08x flag x%lx, " "fcp_info x%x nlp_type x%x\n", be32_to_cpu(nvpr->word1), be32_to_cpu(nvpr->word4), @@ -2299,9 +2255,7 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, (vport->port_type == LPFC_NPIV_PORT) && vport->cfg_restrict_login) { out: - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_TARGET_REMOVE; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_TARGET_REMOVE, &ndlp->nlp_flag); lpfc_issue_els_logo(vport, ndlp, 0); ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; @@ -2353,18 +2307,15 @@ static uint32_t lpfc_device_rm_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NODEV_REMOVE; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { + set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); return ndlp->nlp_state; - } else { - /* software abort outstanding PLOGI */ - lpfc_els_abort(vport->phba, ndlp); - - lpfc_drop_node(vport, ndlp); - return NLP_STE_FREED_NODE; } + /* software abort outstanding PLOGI */ + lpfc_els_abort(vport->phba, ndlp); + + lpfc_drop_node(vport, ndlp); + return NLP_STE_FREED_NODE; } @@ -2401,9 +2352,8 @@ lpfc_device_recov_prli_issue(struct lpfc_vport *vport, ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_disc_set_adisc(vport, ndlp); return ndlp->nlp_state; } @@ -2442,9 +2392,7 @@ lpfc_rcv_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); return ndlp->nlp_state; } @@ -2483,9 +2431,8 @@ lpfc_cmpl_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { ndlp->nlp_prev_state = NLP_STE_LOGO_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); lpfc_disc_set_adisc(vport, ndlp); return ndlp->nlp_state; } @@ -2591,8 +2538,9 @@ lpfc_device_recov_unmap_node(struct lpfc_vport *vport, { ndlp->nlp_prev_state = NLP_STE_UNMAPPED_NODE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); spin_unlock_irq(&ndlp->lock); lpfc_disc_set_adisc(vport, ndlp); @@ -2653,9 +2601,7 @@ lpfc_rcv_prlo_mapped_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_sli_abort_iocb(vport, ndlp->nlp_sid, 0, LPFC_CTX_TGT); /* Send PRLO_ACC */ - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL); /* Save ELS_CMD_PRLO as the last elscmd and then set to NPR. @@ -2665,7 +2611,7 @@ lpfc_rcv_prlo_mapped_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_ELS | LOG_DISCOVERY, - "3422 DID x%06x nflag x%x lastels x%x ref cnt %u\n", + "3422 DID x%06x nflag x%lx lastels x%x ref cnt %u\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_last_elscmd, kref_read(&ndlp->kref)); @@ -2685,8 +2631,9 @@ lpfc_device_recov_mapped_node(struct lpfc_vport *vport, ndlp->nlp_prev_state = NLP_STE_MAPPED_NODE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); spin_unlock_irq(&ndlp->lock); return ndlp->nlp_state; @@ -2699,16 +2646,16 @@ lpfc_rcv_plogi_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; /* Ignore PLOGI if we have an outstanding LOGO */ - if (ndlp->nlp_flag & (NLP_LOGO_SND | NLP_LOGO_ACC)) + if (test_bit(NLP_LOGO_SND, &ndlp->nlp_flag) || + test_bit(NLP_LOGO_ACC, &ndlp->nlp_flag)) return ndlp->nlp_state; if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) { lpfc_cancel_retry_delay_tmo(vport, ndlp); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC); - spin_unlock_irq(&ndlp->lock); - } else if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC)) { + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); + } else if (!test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { /* send PLOGI immediately, move to PLOGI issue state */ - if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) { ndlp->nlp_prev_state = NLP_STE_NPR_NODE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); @@ -2729,14 +2676,14 @@ lpfc_rcv_prli_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL); - if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) { /* * ADISC nodes will be handled in regular discovery path after * receiving response from NS. * * For other nodes, Send PLOGI to trigger an implicit LOGO. */ - if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) { ndlp->nlp_prev_state = NLP_STE_NPR_NODE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); @@ -2767,15 +2714,15 @@ lpfc_rcv_padisc_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, * or discovery in progress for this node. Starting discovery * here will affect the counting of discovery threads. */ - if (!(ndlp->nlp_flag & NLP_DELAY_TMO) && - !(ndlp->nlp_flag & NLP_NPR_2B_DISC)) { + if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag) && + !test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { /* * ADISC nodes will be handled in regular discovery path after * receiving response from NS. * * For other nodes, Send PLOGI to trigger an implicit LOGO. */ - if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) { ndlp->nlp_prev_state = NLP_STE_NPR_NODE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); @@ -2790,24 +2737,18 @@ lpfc_rcv_prlo_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_LOGO_ACC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); - if ((ndlp->nlp_flag & NLP_DELAY_TMO) == 0) { + if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) { mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000 * 1)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag); + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; } else { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_NPR_ADISC; - spin_unlock_irq(&ndlp->lock); + clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag); } return ndlp->nlp_state; } @@ -2844,7 +2785,7 @@ lpfc_cmpl_prli_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ulp_status = get_job_ulpstatus(phba, rspiocb); - if (ulp_status && (ndlp->nlp_flag & NLP_NODEV_REMOVE)) { + if (ulp_status && test_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag)) { lpfc_drop_node(vport, ndlp); return NLP_STE_FREED_NODE; } @@ -2877,7 +2818,7 @@ lpfc_cmpl_adisc_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ulp_status = get_job_ulpstatus(phba, rspiocb); - if (ulp_status && (ndlp->nlp_flag & NLP_NODEV_REMOVE)) { + if (ulp_status && test_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag)) { lpfc_drop_node(vport, ndlp); return NLP_STE_FREED_NODE; } @@ -2896,12 +2837,11 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_vport *vport, /* SLI4 ports have preallocated logical rpis. */ if (vport->phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; - ndlp->nlp_flag |= NLP_RPI_REGISTERED; - if (ndlp->nlp_flag & NLP_LOGO_ACC) { + set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag); + if (test_bit(NLP_LOGO_ACC, &ndlp->nlp_flag)) lpfc_unreg_rpi(vport, ndlp); - } } else { - if (ndlp->nlp_flag & NLP_NODEV_REMOVE) { + if (test_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag)) { lpfc_drop_node(vport, ndlp); return NLP_STE_FREED_NODE; } @@ -2913,10 +2853,8 @@ static uint32_t lpfc_device_rm_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_NODEV_REMOVE; - spin_unlock_irq(&ndlp->lock); + if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) { + set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); return ndlp->nlp_state; } lpfc_drop_node(vport, ndlp); @@ -2932,8 +2870,9 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, return ndlp->nlp_state; lpfc_cancel_retry_delay_tmo(vport, ndlp); + clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag); + clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); spin_unlock_irq(&ndlp->lock); return ndlp->nlp_state; @@ -3146,7 +3085,7 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* DSM in event on NPort in state */ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0211 DSM in event x%x on NPort x%x in " - "state %d rpi x%x Data: x%x x%x\n", + "state %d rpi x%x Data: x%lx x%x\n", evt, ndlp->nlp_DID, cur_state, ndlp->nlp_rpi, ndlp->nlp_flag, data1); @@ -3163,12 +3102,12 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ((uint32_t)ndlp->nlp_type)); lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0212 DSM out state %d on NPort x%x " - "rpi x%x Data: x%x x%x\n", + "rpi x%x Data: x%lx x%x\n", rc, ndlp->nlp_DID, ndlp->nlp_rpi, ndlp->nlp_flag, data1); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM, - "DSM out: ste:%d did:x%x flg:x%x", + "DSM out: ste:%d did:x%x flg:x%lx", rc, ndlp->nlp_DID, ndlp->nlp_flag); /* Decrement the ndlp reference count held for this function */ lpfc_nlp_put(ndlp); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index fec23c7237304..49dd78ed8a9a1 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1232,7 +1232,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, /* Word 5 */ if ((phba->cfg_nvme_enable_fb) && - (pnode->nlp_flag & NLP_FIRSTBURST)) { + test_bit(NLP_FIRSTBURST, &pnode->nlp_flag)) { req_len = lpfc_ncmd->nvmeCmd->payload_length; if (req_len < pnode->nvme_fb_size) wqe->fcp_iwrite.initial_xfer_len = @@ -2231,6 +2231,7 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, struct lpfc_hba *phba = vport->phba; struct lpfc_sli4_hdw_queue *qp; int abts_scsi, abts_nvme; + u16 nvmels_cnt; /* Host transport has to clean up and confirm requiring an indefinite * wait. Print a message if a 10 second wait expires and renew the @@ -2243,6 +2244,7 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, pending = 0; abts_scsi = 0; abts_nvme = 0; + nvmels_cnt = 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { qp = &phba->sli4_hba.hdwq[i]; if (!vport->localport || !qp || !qp->io_wq) @@ -2255,6 +2257,11 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, abts_scsi += qp->abts_scsi_io_bufs; abts_nvme += qp->abts_nvme_io_bufs; } + if (phba->sli4_hba.nvmels_wq) { + pring = phba->sli4_hba.nvmels_wq->pring; + if (pring) + nvmels_cnt = pring->txcmplq_cnt; + } if (!vport->localport || test_bit(HBA_PCI_ERR, &vport->phba->bit_flags) || phba->link_state == LPFC_HBA_ERROR || @@ -2263,10 +2270,10 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "6176 Lport x%px Localport x%px wait " - "timed out. Pending %d [%d:%d]. " + "timed out. Pending %d [%d:%d:%d]. " "Renewing.\n", lport, vport->localport, pending, - abts_scsi, abts_nvme); + abts_scsi, abts_nvme, nvmels_cnt); continue; } break; @@ -2644,14 +2651,11 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * reference. Check if another thread has set * NLP_DROPPED. */ - spin_lock_irq(&ndlp->lock); - if (!(ndlp->nlp_flag & NLP_DROPPED)) { - ndlp->nlp_flag |= NLP_DROPPED; - spin_unlock_irq(&ndlp->lock); + if (!test_and_set_bit(NLP_DROPPED, + &ndlp->nlp_flag)) { lpfc_nlp_put(ndlp); return; } - spin_unlock_irq(&ndlp->lock); } } } @@ -2841,3 +2845,43 @@ lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, (pwqeIn->cmd_cmpl)(phba, pwqeIn, pwqeIn); #endif } + +/** + * lpfc_nvmels_flush_cmd - Clean up outstanding nvmels commands for a port + * @phba: Pointer to HBA context object. + * + **/ +void +lpfc_nvmels_flush_cmd(struct lpfc_hba *phba) +{ +#if (IS_ENABLED(CONFIG_NVME_FC)) + LIST_HEAD(cancel_list); + struct lpfc_sli_ring *pring = NULL; + struct lpfc_iocbq *piocb, *tmp_iocb; + unsigned long iflags; + + if (phba->sli4_hba.nvmels_wq) + pring = phba->sli4_hba.nvmels_wq->pring; + + if (unlikely(!pring)) + return; + + spin_lock_irqsave(&phba->hbalock, iflags); + spin_lock(&pring->ring_lock); + list_splice_init(&pring->txq, &cancel_list); + pring->txq_cnt = 0; + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + if (piocb->cmd_flag & LPFC_IO_NVME_LS) { + list_move_tail(&piocb->list, &cancel_list); + pring->txcmplq_cnt--; + piocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ; + } + } + spin_unlock(&pring->ring_lock); + spin_unlock_irqrestore(&phba->hbalock, iflags); + + if (!list_empty(&cancel_list)) + lpfc_sli_cancel_iocbs(phba, &cancel_list, IOSTAT_LOCAL_REJECT, + IOERR_SLI_DOWN); +#endif +} diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 55c3e2c2bf8f7..e6c9112a88627 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -2854,7 +2854,7 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba, /* In template ar=1 wqes=0 sup=0 irsp=0 irsplen=0 */ if (rsp->rsplen == LPFC_NVMET_SUCCESS_LEN) { - if (ndlp->nlp_flag & NLP_SUPPRESS_RSP) + if (test_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag)) bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 1); } else { diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 11c974bffa720..905026a4782cf 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -4629,7 +4629,7 @@ static int lpfc_scsi_prep_cmnd_buf_s3(struct lpfc_vport *vport, iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR; iocb_cmd->ulpPU = PARM_READ_CHECK; if (vport->cfg_first_burst_size && - (pnode->nlp_flag & NLP_FIRSTBURST)) { + test_bit(NLP_FIRSTBURST, &pnode->nlp_flag)) { u32 xrdy_len; fcpdl = scsi_bufflen(scsi_cmnd); @@ -5829,7 +5829,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct fc_rport *rport, lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "0702 Issue %s to TGT %d LUN %llu " - "rpi x%x nlp_flag x%x Data: x%x x%x\n", + "rpi x%x nlp_flag x%lx Data: x%x x%x\n", lpfc_taskmgmt_name(task_mgmt_cmd), tgt_id, lun_id, pnode->nlp_rpi, pnode->nlp_flag, iocbq->sli4_xritag, iocbq->cmd_flag); @@ -6094,8 +6094,8 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0722 Target Reset rport failure: rdata x%px\n", rdata); if (pnode) { + clear_bit(NLP_NPR_ADISC, &pnode->nlp_flag); spin_lock_irqsave(&pnode->lock, flags); - pnode->nlp_flag &= ~NLP_NPR_ADISC; pnode->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; spin_unlock_irqrestore(&pnode->lock, flags); } @@ -6124,7 +6124,7 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) !pnode->logo_waitq) { pnode->logo_waitq = &waitq; pnode->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; - pnode->nlp_flag |= NLP_ISSUE_LOGO; + set_bit(NLP_ISSUE_LOGO, &pnode->nlp_flag); pnode->save_flags |= NLP_WAIT_FOR_LOGO; spin_unlock_irqrestore(&pnode->lock, flags); lpfc_unreg_rpi(vport, pnode); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 2ec6e55771b45..874644b31a3eb 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1932,7 +1932,7 @@ lpfc_issue_cmf_sync_wqe(struct lpfc_hba *phba, u32 ms, u64 total) union lpfc_wqe128 *wqe; struct lpfc_iocbq *sync_buf; unsigned long iflags; - u32 ret_val; + u32 ret_val, cgn_sig_freq; u32 atot, wtot, max; u8 warn_sync_period = 0; @@ -1987,8 +1987,10 @@ lpfc_issue_cmf_sync_wqe(struct lpfc_hba *phba, u32 ms, u64 total) } else if (wtot) { if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ONLY || phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) { + cgn_sig_freq = phba->cgn_sig_freq ? phba->cgn_sig_freq : + lpfc_fabric_cgn_frequency; /* We hit an Signal warning condition */ - max = LPFC_SEC_TO_MSEC / lpfc_fabric_cgn_frequency * + max = LPFC_SEC_TO_MSEC / cgn_sig_freq * lpfc_acqe_cgn_frequency; bf_set(cmf_sync_wsigmax, &wqe->cmf_sync, max); bf_set(cmf_sync_wsigcnt, &wqe->cmf_sync, wtot); @@ -2842,27 +2844,6 @@ lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) return; } -static void -__lpfc_sli_rpi_release(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) -{ - unsigned long iflags; - - if (ndlp->nlp_flag & NLP_RELEASE_RPI) { - lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi); - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag &= ~NLP_RELEASE_RPI; - ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; - spin_unlock_irqrestore(&ndlp->lock, iflags); - } - ndlp->nlp_flag &= ~NLP_UNREG_INP; -} - -void -lpfc_sli_rpi_release(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) -{ - __lpfc_sli_rpi_release(vport, ndlp); -} - /** * lpfc_sli_def_mbox_cmpl - Default mailbox completion handler * @phba: Pointer to HBA context object. @@ -2932,18 +2913,16 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) vport, KERN_INFO, LOG_MBOX | LOG_DISCOVERY, "1438 UNREG cmpl deferred mbox x%x " - "on NPort x%x Data: x%x x%x x%px x%lx x%x\n", + "on NPort x%x Data: x%lx x%x x%px x%lx x%x\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_defer_did, ndlp, vport->load_flag, kref_read(&ndlp->kref)); - if ((ndlp->nlp_flag & NLP_UNREG_INP) && - (ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING)) { - ndlp->nlp_flag &= ~NLP_UNREG_INP; + if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag) && + ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING) { + clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag); ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING; lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); - } else { - __lpfc_sli_rpi_release(vport, ndlp); } /* The unreg_login mailbox is complete and had a @@ -2991,6 +2970,7 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) { struct lpfc_vport *vport = pmb->vport; struct lpfc_nodelist *ndlp; + bool unreg_inp; ndlp = pmb->ctx_ndlp; if (pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) { @@ -3003,20 +2983,26 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) vport, KERN_INFO, LOG_MBOX | LOG_SLI | LOG_NODE, "0010 UNREG_LOGIN vpi:x%x " - "rpi:%x DID:%x defer x%x flg x%x " + "rpi:%x DID:%x defer x%x flg x%lx " "x%px\n", vport->vpi, ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_defer_did, ndlp->nlp_flag, ndlp); - ndlp->nlp_flag &= ~NLP_LOGO_ACC; + + /* Cleanup the nlp_flag now that the UNREG RPI + * has completed. + */ + unreg_inp = test_and_clear_bit(NLP_UNREG_INP, + &ndlp->nlp_flag); + clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag); /* Check to see if there are any deferred * events to process */ - if ((ndlp->nlp_flag & NLP_UNREG_INP) && - (ndlp->nlp_defer_did != - NLP_EVT_NOTHING_PENDING)) { + if (unreg_inp && + ndlp->nlp_defer_did != + NLP_EVT_NOTHING_PENDING) { lpfc_printf_vlog( vport, KERN_INFO, LOG_MBOX | LOG_SLI | LOG_NODE, @@ -3025,14 +3011,12 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) "NPort x%x Data: x%x x%px\n", ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_defer_did, ndlp); - ndlp->nlp_flag &= ~NLP_UNREG_INP; ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING; lpfc_issue_els_plogi( vport, ndlp->nlp_DID, 0); - } else { - __lpfc_sli_rpi_release(vport, ndlp); } + lpfc_nlp_put(ndlp); } } @@ -5291,6 +5275,8 @@ lpfc_sli_brdrestart_s4(struct lpfc_hba *phba) "0296 Restart HBA Data: x%x x%x\n", phba->pport->port_state, psli->sli_flag); + lpfc_sli4_queue_unset(phba); + rc = lpfc_sli4_brdreset(phba); if (rc) { phba->link_state = LPFC_HBA_ERROR; @@ -8748,6 +8734,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) lpfc_sli_config_mbox_opcode_get( phba, mboxq), rc, dd); + /* * Allocate all resources (xri,rpi,vpi,vfi) now. Subsequent * calls depends on these resources to complete port setup. @@ -8760,6 +8747,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) goto out_free_mbox; } + lpfc_sli4_node_rpi_restore(phba); + lpfc_set_host_data(phba, mboxq); rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); @@ -8947,7 +8936,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) rc = -ENODEV; goto out_free_iocblist; } - lpfc_sli4_node_prep(phba); if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) { if ((phba->nvmet_support == 0) || (phba->cfg_nvmet_mrq == 1)) { @@ -14352,9 +14340,7 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe) * an unsolicited PLOGI from the same NPortId from * starting another mailbox transaction. */ - spin_lock_irqsave(&ndlp->lock, iflags); - ndlp->nlp_flag |= NLP_UNREG_INP; - spin_unlock_irqrestore(&ndlp->lock, iflags); + set_bit(NLP_UNREG_INP, &ndlp->nlp_flag); lpfc_unreg_login(phba, vport->vpi, pmbox->un.varWords[0], pmb); pmb->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi; @@ -17625,6 +17611,9 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq) if (!eq) return -ENODEV; + if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) + goto list_remove; + mbox = mempool_alloc(eq->phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; @@ -17651,10 +17640,12 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq) shdr_status, shdr_add_status, rc); status = -ENXIO; } + mempool_free(mbox, eq->phba->mbox_mem_pool); +list_remove: /* Remove eq from any list */ list_del_init(&eq->list); - mempool_free(mbox, eq->phba->mbox_mem_pool); + return status; } @@ -17682,6 +17673,10 @@ lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq) /* sanity check on queue memory */ if (!cq) return -ENODEV; + + if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) + goto list_remove; + mbox = mempool_alloc(cq->phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; @@ -17707,9 +17702,11 @@ lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq) shdr_status, shdr_add_status, rc); status = -ENXIO; } + mempool_free(mbox, cq->phba->mbox_mem_pool); + +list_remove: /* Remove cq from any list */ list_del_init(&cq->list); - mempool_free(mbox, cq->phba->mbox_mem_pool); return status; } @@ -17737,6 +17734,10 @@ lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq) /* sanity check on queue memory */ if (!mq) return -ENODEV; + + if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) + goto list_remove; + mbox = mempool_alloc(mq->phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; @@ -17762,9 +17763,11 @@ lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq) shdr_status, shdr_add_status, rc); status = -ENXIO; } + mempool_free(mbox, mq->phba->mbox_mem_pool); + +list_remove: /* Remove mq from any list */ list_del_init(&mq->list); - mempool_free(mbox, mq->phba->mbox_mem_pool); return status; } @@ -17792,6 +17795,10 @@ lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq) /* sanity check on queue memory */ if (!wq) return -ENODEV; + + if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) + goto list_remove; + mbox = mempool_alloc(wq->phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; @@ -17816,11 +17823,13 @@ lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq) shdr_status, shdr_add_status, rc); status = -ENXIO; } + mempool_free(mbox, wq->phba->mbox_mem_pool); + +list_remove: /* Remove wq from any list */ list_del_init(&wq->list); kfree(wq->pring); wq->pring = NULL; - mempool_free(mbox, wq->phba->mbox_mem_pool); return status; } @@ -17850,6 +17859,10 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq, /* sanity check on queue memory */ if (!hrq || !drq) return -ENODEV; + + if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) + goto list_remove; + mbox = mempool_alloc(hrq->phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; @@ -17890,9 +17903,11 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq, shdr_status, shdr_add_status, rc); status = -ENXIO; } + mempool_free(mbox, hrq->phba->mbox_mem_pool); + +list_remove: list_del_init(&hrq->list); list_del_init(&drq->list); - mempool_free(mbox, hrq->phba->mbox_mem_pool); return status; } @@ -19074,9 +19089,9 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_vport *vport, * to free ndlp when transmit completes */ if (ndlp->nlp_state == NLP_STE_UNUSED_NODE && - !(ndlp->nlp_flag & NLP_DROPPED) && + !test_bit(NLP_DROPPED, &ndlp->nlp_flag) && !(ndlp->fc4_xpt_flags & (NVME_XPT_REGD | SCSI_XPT_REGD))) { - ndlp->nlp_flag |= NLP_DROPPED; + set_bit(NLP_DROPPED, &ndlp->nlp_flag); lpfc_nlp_put(ndlp); } } @@ -21094,11 +21109,7 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) /* Unregister the RPI when mailbox complete */ mb->mbox_flag |= LPFC_MBX_IMED_UNREG; restart_loop = 1; - spin_unlock_irq(&phba->hbalock); - spin_lock(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL; - spin_unlock(&ndlp->lock); - spin_lock_irq(&phba->hbalock); + clear_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag); break; } } @@ -21113,9 +21124,7 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) ndlp = mb->ctx_ndlp; mb->ctx_ndlp = NULL; if (ndlp) { - spin_lock(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL; - spin_unlock(&ndlp->lock); + clear_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag); lpfc_nlp_put(ndlp); } } @@ -21124,9 +21133,7 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) /* Release the ndlp with the cleaned-up active mailbox command */ if (act_mbx_ndlp) { - spin_lock(&act_mbx_ndlp->lock); - act_mbx_ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL; - spin_unlock(&act_mbx_ndlp->lock); + clear_bit(NLP_IGNR_REG_CMPL, &act_mbx_ndlp->nlp_flag); lpfc_nlp_put(act_mbx_ndlp); } } diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index e70f163fab900..61fe1220f8ad3 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "14.4.0.5" +#define LPFC_DRIVER_VERSION "14.4.0.6" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 7a4d4d8e2ad55..9e0e357633779 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -496,7 +496,7 @@ lpfc_send_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) !ndlp->logo_waitq) { ndlp->logo_waitq = &waitq; ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; - ndlp->nlp_flag |= NLP_ISSUE_LOGO; + set_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag); ndlp->save_flags |= NLP_WAIT_FOR_LOGO; } spin_unlock_irq(&ndlp->lock); @@ -515,8 +515,8 @@ lpfc_send_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) } /* Error - clean up node flags. */ + clear_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag); spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag &= ~NLP_ISSUE_LOGO; ndlp->save_flags &= ~NLP_WAIT_FOR_LOGO; spin_unlock_irq(&ndlp->lock); @@ -708,7 +708,7 @@ lpfc_vport_delete(struct fc_vport *fc_vport) lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT | LOG_ELS, "1829 DA_ID issue status %d. " - "SFlag x%x NState x%x, NFlag x%x " + "SFlag x%x NState x%x, NFlag x%lx " "Rpi x%x\n", rc, ndlp->save_flags, ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_rpi); diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c index 187ae0a65d404..ff0253d47a0e8 100644 --- a/drivers/scsi/mac_esp.c +++ b/drivers/scsi/mac_esp.c @@ -432,7 +432,7 @@ static void esp_mac_remove(struct platform_device *dev) static struct platform_driver esp_mac_driver = { .probe = esp_mac_probe, - .remove_new = esp_mac_remove, + .remove = esp_mac_remove, .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c index f225bb20aa220..a86bd839d08e9 100644 --- a/drivers/scsi/mac_scsi.c +++ b/drivers/scsi/mac_scsi.c @@ -547,7 +547,7 @@ static void __exit mac_scsi_remove(struct platform_device *pdev) * triggering a section mismatch warning. */ static struct platform_driver mac_scsi_driver __refdata = { - .remove_new = __exit_p(mac_scsi_remove), + .remove = __exit_p(mac_scsi_remove), .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/scsi/mvme16x_scsi.c b/drivers/scsi/mvme16x_scsi.c index e08a38e2a442b..9b19d5205e508 100644 --- a/drivers/scsi/mvme16x_scsi.c +++ b/drivers/scsi/mvme16x_scsi.c @@ -127,7 +127,7 @@ static struct platform_driver mvme16x_scsi_driver = { .name = "mvme16x-scsi", }, .probe = mvme16x_probe, - .remove_new = mvme16x_device_remove, + .remove = mvme16x_device_remove, }; static int __init mvme16x_scsi_init(void) diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h index 501b574239e8b..7871e29a820a0 100644 --- a/drivers/scsi/pm8001/pm8001_defs.h +++ b/drivers/scsi/pm8001/pm8001_defs.h @@ -92,8 +92,11 @@ enum port_type { #define PM8001_MAX_MSIX_VEC 64 /* max msi-x int for spcv/ve */ #define PM8001_RESERVE_SLOT 8 -#define CONFIG_SCSI_PM8001_MAX_DMA_SG 528 -#define PM8001_MAX_DMA_SG CONFIG_SCSI_PM8001_MAX_DMA_SG +#define PM8001_SECTOR_SIZE 512 +#define PM8001_PAGE_SIZE_4K 4096 +#define PM8001_MAX_IO_SIZE (4 * 1024 * 1024) +#define PM8001_MAX_DMA_SG (PM8001_MAX_IO_SIZE / PM8001_PAGE_SIZE_4K) +#define PM8001_MAX_SECTORS (PM8001_MAX_IO_SIZE / PM8001_SECTOR_SIZE) enum memory_region_num { AAP1 = 0x0, /* application acceleration processor */ diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 33e1eba62ca12..f8c81e53e93f7 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -68,6 +68,10 @@ static bool pm8001_read_wwn = true; module_param_named(read_wwn, pm8001_read_wwn, bool, 0444); MODULE_PARM_DESC(zoned, "Get WWN from the controller. Default: true"); +uint pcs_event_log_severity = 0x03; +module_param(pcs_event_log_severity, int, 0644); +MODULE_PARM_DESC(pcs_event_log_severity, "PCS event log severity level"); + static struct scsi_transport_template *pm8001_stt; static int pm8001_init_ccb_tag(struct pm8001_hba_info *); @@ -117,6 +121,7 @@ static const struct scsi_host_template pm8001_sht = { .scan_start = pm8001_scan_start, .can_queue = 1, .sg_tablesize = PM8001_MAX_DMA_SG, + .max_sectors = PM8001_MAX_SECTORS, .shost_groups = pm8001_host_groups, .sdev_groups = pm8001_sdev_groups, .track_queue_depth = 1, @@ -447,9 +452,6 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha, } for (i = 0; i < PM8001_MAX_DEVICES; i++) { pm8001_ha->devices[i].dev_type = SAS_PHY_UNUSED; - pm8001_ha->devices[i].id = i; - pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES; - atomic_set(&pm8001_ha->devices[i].running_req, 0); } pm8001_ha->flags = PM8001F_INIT_TIME; return 0; diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index ee2da8e49d4cf..d80cffd25a6ed 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -572,6 +572,13 @@ void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, pm8001_ccb_free(pm8001_ha, ccb); } +static void pm8001_init_dev(struct pm8001_device *pm8001_dev, int id) +{ + pm8001_dev->id = id; + pm8001_dev->device_id = PM8001_MAX_DEVICES; + atomic_set(&pm8001_dev->running_req, 0); +} + /** * pm8001_alloc_dev - find a empty pm8001_device * @pm8001_ha: our hba card information @@ -580,9 +587,11 @@ static struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) { u32 dev; for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) { - if (pm8001_ha->devices[dev].dev_type == SAS_PHY_UNUSED) { - pm8001_ha->devices[dev].id = dev; - return &pm8001_ha->devices[dev]; + struct pm8001_device *pm8001_dev = &pm8001_ha->devices[dev]; + + if (pm8001_dev->dev_type == SAS_PHY_UNUSED) { + pm8001_init_dev(pm8001_dev, dev); + return pm8001_dev; } } if (dev == PM8001_MAX_DEVICES) { @@ -613,9 +622,7 @@ struct pm8001_device *pm8001_find_dev(struct pm8001_hba_info *pm8001_ha, void pm8001_free_dev(struct pm8001_device *pm8001_dev) { - u32 id = pm8001_dev->id; memset(pm8001_dev, 0, sizeof(*pm8001_dev)); - pm8001_dev->id = id; pm8001_dev->dev_type = SAS_PHY_UNUSED; pm8001_dev->device_id = PM8001_MAX_DEVICES; pm8001_dev->sas_device = NULL; diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index ced6721380a85..42c7b3f7afbf1 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -96,6 +96,8 @@ extern struct list_head hba_list; extern const struct pm8001_dispatch pm8001_8001_dispatch; extern const struct pm8001_dispatch pm8001_80xx_dispatch; +extern uint pcs_event_log_severity; + struct pm8001_hba_info; struct pm8001_ccb_info; struct pm8001_device; diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index a9869cd8c4c07..e65951dd2024e 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -763,7 +763,8 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) pm8001_ha->memoryMap.region[IOP].phys_addr_lo; pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_size = PM8001_EVENT_LOG_SIZE; - pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity = 0x01; + pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity = + pcs_event_log_severity; pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt = 0x01; /* Enable higher IQs and OQs, 32 to 63, bit 16 */ diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index cf13148ba281c..d2f47dc31dbf9 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -2738,6 +2738,7 @@ static int qedf_alloc_and_init_sb(struct qedf_ctx *qedf, sb_id, QED_SB_TYPE_STORAGE); if (ret) { + dma_free_coherent(&qedf->pdev->dev, sizeof(*sb_virt), sb_virt, sb_phys); QEDF_ERR(&qedf->dbg_ctx, "Status block initialization failed (0x%x) for id = %d.\n", ret, sb_id); @@ -4018,11 +4019,6 @@ void qedf_stag_change_work(struct work_struct *work) struct qedf_ctx *qedf = container_of(work, struct qedf_ctx, stag_work.work); - if (!qedf) { - QEDF_ERR(&qedf->dbg_ctx, "qedf is NULL"); - return; - } - if (test_bit(QEDF_IN_RECOVERY, &qedf->flags)) { QEDF_ERR(&qedf->dbg_ctx, "Already is in recovery, hence not calling software context reset.\n"); diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index c5aec26019d6a..628d59dda20cc 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -369,6 +369,7 @@ static int qedi_alloc_and_init_sb(struct qedi_ctx *qedi, ret = qedi_ops->common->sb_init(qedi->cdev, sb_info, sb_virt, sb_phys, sb_id, QED_SB_TYPE_STORAGE); if (ret) { + dma_free_coherent(&qedi->pdev->dev, sizeof(*sb_virt), sb_virt, sb_phys); QEDI_ERR(&qedi->dbg_ctx, "Status block initialization failed for id = %d.\n", sb_id); diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 6177f4798f3ac..74866b9f2b145 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1463,7 +1463,7 @@ static struct platform_driver qpti_sbus_driver = { .of_match_table = qpti_match, }, .probe = qpti_sbus_probe, - .remove_new = qpti_sbus_remove, + .remove = qpti_sbus_remove, }; module_platform_driver(qpti_sbus_driver); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 32f94db6d6bf5..f3a1ecb42128a 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1274,7 +1274,7 @@ static umode_t scsi_sdev_attr_is_visible(struct kobject *kobj, } static umode_t scsi_sdev_bin_attr_is_visible(struct kobject *kobj, - struct bin_attribute *attr, int i) + const struct bin_attribute *attr, int i) { struct device *dev = kobj_to_dev(kobj); struct scsi_device *sdev = to_scsi_device(dev); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index f86be197fedd0..84334ab39c810 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -307,10 +307,6 @@ sg_open(struct inode *inode, struct file *filp) if (retval) goto sg_put; - retval = scsi_autopm_get_device(device); - if (retval) - goto sdp_put; - /* scsi_block_when_processing_errors() may block so bypass * check if O_NONBLOCK. Permits SCSI commands to be issued * during error recovery. Tread carefully. */ @@ -318,7 +314,7 @@ sg_open(struct inode *inode, struct file *filp) scsi_block_when_processing_errors(device))) { retval = -ENXIO; /* we are in error recovery for this device */ - goto error_out; + goto sdp_put; } mutex_lock(&sdp->open_rel_lock); @@ -371,8 +367,6 @@ sg_open(struct inode *inode, struct file *filp) } error_mutex_locked: mutex_unlock(&sdp->open_rel_lock); -error_out: - scsi_autopm_put_device(device); sdp_put: kref_put(&sdp->d_ref, sg_device_destroy); scsi_device_put(device); @@ -392,7 +386,6 @@ sg_release(struct inode *inode, struct file *filp) SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); mutex_lock(&sdp->open_rel_lock); - scsi_autopm_put_device(sdp->device); kref_put(&sfp->f_ref, sg_remove_sfp); sdp->open_cnt--; diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index b0bef83db7b60..6594661db5f40 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -306,7 +306,7 @@ static void sgiwd93_remove(struct platform_device *pdev) static struct platform_driver sgiwd93_driver = { .probe = sgiwd93_probe, - .remove_new = sgiwd93_remove, + .remove = sgiwd93_remove, .driver = { .name = "sgiwd93", } diff --git a/drivers/scsi/sni_53c710.c b/drivers/scsi/sni_53c710.c index 9df1c90a24c11..d1d2556c8fc4b 100644 --- a/drivers/scsi/sni_53c710.c +++ b/drivers/scsi/sni_53c710.c @@ -119,7 +119,7 @@ static void snirm710_driver_remove(struct platform_device *dev) static struct platform_driver snirm710_driver = { .probe = snirm710_probe, - .remove_new = snirm710_driver_remove, + .remove = snirm710_driver_remove, .driver = { .name = "snirm_53c710", }, diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index beb88f25dbb99..e8ef27d7ef618 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -991,7 +991,10 @@ static int test_ready(struct scsi_tape *STp, int do_wait) scode = cmdstatp->sense_hdr.sense_key; if (scode == UNIT_ATTENTION) { /* New media? */ - new_session = 1; + if (cmdstatp->sense_hdr.asc == 0x28) { /* New media */ + new_session = 1; + DEBC_printk(STp, "New tape session."); + } if (attentions < MAX_ATTENTIONS) { attentions++; continue; @@ -3506,6 +3509,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) int i, cmd_nr, cmd_type, bt; int retval = 0; unsigned int blk; + bool cmd_mtiocget; struct scsi_tape *STp = file->private_data; struct st_modedef *STm; struct st_partstat *STps; @@ -3619,6 +3623,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) */ if (mtc.mt_op != MTREW && mtc.mt_op != MTOFFL && + mtc.mt_op != MTLOAD && mtc.mt_op != MTRETEN && mtc.mt_op != MTERASE && mtc.mt_op != MTSEEK && @@ -3732,17 +3737,28 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) goto out; } + cmd_mtiocget = cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET); + if ((i = flush_buffer(STp, 0)) < 0) { - retval = i; - goto out; - } - if (STp->can_partitions && - (i = switch_partition(STp)) < 0) { - retval = i; - goto out; + if (cmd_mtiocget && STp->pos_unknown) { + /* flush fails -> modify status accordingly */ + reset_state(STp); + STp->pos_unknown = 1; + } else { /* return error */ + retval = i; + goto out; + } + } else { /* flush_buffer succeeds */ + if (STp->can_partitions) { + i = switch_partition(STp); + if (i < 0) { + retval = i; + goto out; + } + } } - if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) { + if (cmd_mtiocget) { struct mtget mt_status; if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) { @@ -3756,7 +3772,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK); mt_status.mt_blkno = STps->drv_block; mt_status.mt_fileno = STps->drv_file; - if (STp->block_size != 0) { + if (STp->block_size != 0 && mt_status.mt_blkno >= 0) { if (STps->rw == ST_WRITING) mt_status.mt_blkno += (STp->buffer)->buffer_bytes / STp->block_size; diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c index fffc0fa525940..ca9cd691cc324 100644 --- a/drivers/scsi/sun3_scsi.c +++ b/drivers/scsi/sun3_scsi.c @@ -656,8 +656,14 @@ static void __exit sun3_scsi_remove(struct platform_device *pdev) iounmap(ioaddr); } -static struct platform_driver sun3_scsi_driver = { - .remove_new = __exit_p(sun3_scsi_remove), +/* + * sun3_scsi_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver sun3_scsi_driver __refdata = { + .remove = __exit_p(sun3_scsi_remove), .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/scsi/sun3x_esp.c b/drivers/scsi/sun3x_esp.c index e20f314cf3e7d..365406885b8ef 100644 --- a/drivers/scsi/sun3x_esp.c +++ b/drivers/scsi/sun3x_esp.c @@ -265,7 +265,7 @@ static void esp_sun3x_remove(struct platform_device *dev) static struct platform_driver esp_sun3x_driver = { .probe = esp_sun3x_probe, - .remove_new = esp_sun3x_remove, + .remove = esp_sun3x_remove, .driver = { .name = "sun3x_esp", }, diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index 5ce6c9d19d1e6..aa430501f0c71 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -603,7 +603,7 @@ static struct platform_driver esp_sbus_driver = { .of_match_table = esp_match, }, .probe = esp_sbus_probe, - .remove_new = esp_sbus_remove, + .remove = esp_sbus_remove, }; module_platform_driver(esp_sbus_driver); diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 74350b5871dc8..ea571eeb30787 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -209,7 +209,6 @@ int __init register_intc_controller(struct intc_desc *desc) goto err0; INIT_LIST_HEAD(&d->list); - list_add_tail(&d->list, &intc_list); raw_spin_lock_init(&d->lock); INIT_RADIX_TREE(&d->tree, GFP_ATOMIC); @@ -369,6 +368,7 @@ int __init register_intc_controller(struct intc_desc *desc) d->skip_suspend = desc->skip_syscore_suspend; + list_add_tail(&d->list, &intc_list); nr_intc_controllers++; return 0; diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c index 53d1d707ca1a7..d11b60efda33f 100644 --- a/drivers/soundwire/amd_init.c +++ b/drivers/soundwire/amd_init.c @@ -121,6 +121,7 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res) sdw_pdata[index].instance = index; sdw_pdata[index].acp_sdw_lock = res->acp_lock; + sdw_pdata[index].acp_rev = res->acp_rev; pdevinfo[index].name = "amd_sdw_manager"; pdevinfo[index].id = index; pdevinfo[index].parent = res->parent; diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 0d01849c35861..5a4bfaef65fb2 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -433,12 +433,18 @@ static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_pa u32 frame_fmt_reg, dpn_frame_fmt; dev_dbg(amd_manager->dev, "p_params->num:0x%x\n", p_params->num); - switch (amd_manager->instance) { - case ACP_SDW0: - frame_fmt_reg = sdw0_manager_dp_reg[p_params->num].frame_fmt_reg; - break; - case ACP_SDW1: - frame_fmt_reg = sdw1_manager_dp_reg[p_params->num].frame_fmt_reg; + switch (amd_manager->acp_rev) { + case ACP63_PCI_REV_ID: + switch (amd_manager->instance) { + case ACP_SDW0: + frame_fmt_reg = acp63_sdw0_dp_reg[p_params->num].frame_fmt_reg; + break; + case ACP_SDW1: + frame_fmt_reg = acp63_sdw1_dp_reg[p_params->num].frame_fmt_reg; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -465,20 +471,28 @@ static int amd_sdw_transport_params(struct sdw_bus *bus, u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg; u32 offset_reg, lane_ctrl_ch_en_reg; - switch (amd_manager->instance) { - case ACP_SDW0: - frame_fmt_reg = sdw0_manager_dp_reg[params->port_num].frame_fmt_reg; - sample_int_reg = sdw0_manager_dp_reg[params->port_num].sample_int_reg; - hctrl_dp0_reg = sdw0_manager_dp_reg[params->port_num].hctrl_dp0_reg; - offset_reg = sdw0_manager_dp_reg[params->port_num].offset_reg; - lane_ctrl_ch_en_reg = sdw0_manager_dp_reg[params->port_num].lane_ctrl_ch_en_reg; - break; - case ACP_SDW1: - frame_fmt_reg = sdw1_manager_dp_reg[params->port_num].frame_fmt_reg; - sample_int_reg = sdw1_manager_dp_reg[params->port_num].sample_int_reg; - hctrl_dp0_reg = sdw1_manager_dp_reg[params->port_num].hctrl_dp0_reg; - offset_reg = sdw1_manager_dp_reg[params->port_num].offset_reg; - lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[params->port_num].lane_ctrl_ch_en_reg; + switch (amd_manager->acp_rev) { + case ACP63_PCI_REV_ID: + switch (amd_manager->instance) { + case ACP_SDW0: + frame_fmt_reg = acp63_sdw0_dp_reg[params->port_num].frame_fmt_reg; + sample_int_reg = acp63_sdw0_dp_reg[params->port_num].sample_int_reg; + hctrl_dp0_reg = acp63_sdw0_dp_reg[params->port_num].hctrl_dp0_reg; + offset_reg = acp63_sdw0_dp_reg[params->port_num].offset_reg; + lane_ctrl_ch_en_reg = + acp63_sdw0_dp_reg[params->port_num].lane_ctrl_ch_en_reg; + break; + case ACP_SDW1: + frame_fmt_reg = acp63_sdw1_dp_reg[params->port_num].frame_fmt_reg; + sample_int_reg = acp63_sdw1_dp_reg[params->port_num].sample_int_reg; + hctrl_dp0_reg = acp63_sdw1_dp_reg[params->port_num].hctrl_dp0_reg; + offset_reg = acp63_sdw1_dp_reg[params->port_num].offset_reg; + lane_ctrl_ch_en_reg = + acp63_sdw1_dp_reg[params->port_num].lane_ctrl_ch_en_reg; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -520,12 +534,20 @@ static int amd_sdw_port_enable(struct sdw_bus *bus, u32 dpn_ch_enable; u32 lane_ctrl_ch_en_reg; - switch (amd_manager->instance) { - case ACP_SDW0: - lane_ctrl_ch_en_reg = sdw0_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg; - break; - case ACP_SDW1: - lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg; + switch (amd_manager->acp_rev) { + case ACP63_PCI_REV_ID: + switch (amd_manager->instance) { + case ACP_SDW0: + lane_ctrl_ch_en_reg = + acp63_sdw0_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg; + break; + case ACP_SDW1: + lane_ctrl_ch_en_reg = + acp63_sdw1_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -910,6 +932,7 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) amd_manager->mmio = amd_manager->acp_mmio + (amd_manager->instance * SDW_MANAGER_REG_OFFSET); amd_manager->acp_sdw_lock = pdata->acp_sdw_lock; + amd_manager->acp_rev = pdata->acp_rev; amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS); amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS); amd_manager->dev = dev; @@ -926,15 +949,21 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) * information. */ amd_manager->bus.controller_id = 0; - - switch (amd_manager->instance) { - case ACP_SDW0: - amd_manager->num_dout_ports = AMD_SDW0_MAX_TX_PORTS; - amd_manager->num_din_ports = AMD_SDW0_MAX_RX_PORTS; - break; - case ACP_SDW1: - amd_manager->num_dout_ports = AMD_SDW1_MAX_TX_PORTS; - amd_manager->num_din_ports = AMD_SDW1_MAX_RX_PORTS; + dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev); + switch (amd_manager->acp_rev) { + case ACP63_PCI_REV_ID: + switch (amd_manager->instance) { + case ACP_SDW0: + amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS; + amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS; + break; + case ACP_SDW1: + amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS; + amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h index 707065468e05a..cc2170e4521ed 100644 --- a/drivers/soundwire/amd_manager.h +++ b/drivers/soundwire/amd_manager.h @@ -155,12 +155,12 @@ #define AMD_SDW_IRQ_MASK_8TO11 0x000c7777 #define AMD_SDW_IRQ_ERROR_MASK 0xff #define AMD_SDW_MAX_FREQ_NUM 1 -#define AMD_SDW0_MAX_TX_PORTS 3 -#define AMD_SDW0_MAX_RX_PORTS 3 -#define AMD_SDW1_MAX_TX_PORTS 1 -#define AMD_SDW1_MAX_RX_PORTS 1 -#define AMD_SDW0_MAX_DAI 6 -#define AMD_SDW1_MAX_DAI 2 +#define AMD_ACP63_SDW0_MAX_TX_PORTS 3 +#define AMD_ACP63_SDW0_MAX_RX_PORTS 3 +#define AMD_ACP63_SDW1_MAX_TX_PORTS 1 +#define AMD_ACP63_SDW1_MAX_RX_PORTS 1 +#define AMD_ACP63_SDW0_MAX_DAI 6 +#define AMD_ACP63_SDW1_MAX_DAI 2 #define AMD_SDW_SLAVE_0_ATTACHED 5 #define AMD_SDW_SSP_COUNTER_VAL 3 @@ -222,7 +222,7 @@ struct sdw_manager_dp_reg { * in SoundWire DMA driver. */ -static struct sdw_manager_dp_reg sdw0_manager_dp_reg[AMD_SDW0_MAX_DAI] = { +static struct sdw_manager_dp_reg acp63_sdw0_dp_reg[AMD_ACP63_SDW0_MAX_DAI] = { {ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0, ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0}, {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL, @@ -237,7 +237,7 @@ static struct sdw_manager_dp_reg sdw0_manager_dp_reg[AMD_SDW0_MAX_DAI] = { ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0}, }; -static struct sdw_manager_dp_reg sdw1_manager_dp_reg[AMD_SDW1_MAX_DAI] = { +static struct sdw_manager_dp_reg acp63_sdw1_dp_reg[AMD_ACP63_SDW1_MAX_DAI] = { {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL, ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0}, {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL, diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 263ca32f0c5c3..d1dc62c34f1cf 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -112,7 +112,7 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, /* Set higher order bits */ *bus->assigned = ~GENMASK(SDW_BROADCAST_DEV_NUM, SDW_ENUM_DEV_NUM); - /* Set enumuration device number and broadcast device number */ + /* Set enumeration device number and broadcast device number */ set_bit(SDW_ENUM_DEV_NUM, bus->assigned); set_bit(SDW_BROADCAST_DEV_NUM, bus->assigned); diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 05652e983539b..f367670ea991b 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1377,6 +1377,31 @@ static void cdns_init_clock_ctrl(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, ssp_interval); } +/** + * sdw_cdns_soft_reset() - Cadence soft-reset + * @cdns: Cadence instance + */ +int sdw_cdns_soft_reset(struct sdw_cdns *cdns) +{ + int ret; + + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_SOFT_RST, + CDNS_MCP_CONTROL_SOFT_RST); + + ret = cdns_config_update(cdns); + if (ret < 0) { + dev_err(cdns->dev, "%s: config update failed\n", __func__); + return ret; + } + + ret = cdns_set_wait(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_SOFT_RST, 0); + if (ret < 0) + dev_err(cdns->dev, "%s: Soft Reset timed out\n", __func__); + + return ret; +} +EXPORT_SYMBOL(sdw_cdns_soft_reset); + /** * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance @@ -1400,6 +1425,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, CDNS_IP_MCP_CONTROL_CMD_ACCEPT, CDNS_IP_MCP_CONTROL_CMD_ACCEPT); + /* disable wakeup */ + cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, + CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP, + 0); + /* Configure mcp config */ val = cdns_readl(cdns, CDNS_MCP_CONFIG); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index e1d7969ba48ae..c34fb050fe4f0 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -168,6 +168,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns); irqreturn_t sdw_cdns_irq(int irq, void *dev_id); irqreturn_t sdw_cdns_thread(int irq, void *dev_id); +int sdw_cdns_soft_reset(struct sdw_cdns *cdns); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index ae689d5d1ab9e..599954d927529 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -41,6 +41,10 @@ static int md_flags; module_param_named(sdw_md_flags, md_flags, int, 0444); MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); +static int mclk_divider; +module_param_named(sdw_mclk_divider, mclk_divider, int, 0444); +MODULE_PARM_DESC(sdw_mclk_divider, "SoundWire Intel mclk divider"); + struct wake_capable_part { const u16 mfg_id; const u16 part_id; @@ -142,8 +146,12 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) "intel-sdw-ip-clock", &prop->mclk_freq); - /* the values reported by BIOS are the 2x clock, not the bus clock */ - prop->mclk_freq /= 2; + if (mclk_divider) + /* use kernel parameter for BIOS or board work-arounds */ + prop->mclk_freq /= mclk_divider; + else + /* the values reported by BIOS are the 2x clock, not the bus clock */ + prop->mclk_freq /= 2; fwnode_property_read_u32(link, "intel-quirk-mask", diff --git a/drivers/soundwire/intel_bus_common.c b/drivers/soundwire/intel_bus_common.c index d3ff6c65b64c3..ad1f8ebdbfc9b 100644 --- a/drivers/soundwire/intel_bus_common.c +++ b/drivers/soundwire/intel_bus_common.c @@ -16,6 +16,12 @@ int intel_start_bus(struct sdw_intel *sdw) struct sdw_bus *bus = &cdns->bus; int ret; + ret = sdw_cdns_soft_reset(cdns); + if (ret < 0) { + dev_err(dev, "%s: unable to soft-reset Cadence IP: %d\n", __func__, ret); + return ret; + } + /* * follow recommended programming flows to avoid timeouts when * gsync is enabled diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index e5d9df26d4dc9..9d59f486edbe0 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -23,6 +23,26 @@ #include #include "bus.h" +static bool mipi_fwnode_property_read_bool(const struct fwnode_handle *fwnode, + const char *propname) +{ + int ret; + u8 val; + + if (!fwnode_property_present(fwnode, propname)) + return false; + ret = fwnode_property_read_u8_array(fwnode, propname, &val, 1); + if (ret < 0) + return false; + return !!val; +} + +static bool mipi_device_property_read_bool(const struct device *dev, + const char *propname) +{ + return mipi_fwnode_property_read_bool(dev_fwnode(dev), propname); +} + /** * sdw_master_read_prop() - Read Master properties * @bus: SDW bus instance @@ -31,8 +51,11 @@ int sdw_master_read_prop(struct sdw_bus *bus) { struct sdw_master_prop *prop = &bus->prop; struct fwnode_handle *link; + const char *scales_prop; char name[32]; - int nval, i; + int nval; + int ret; + int i; device_property_read_u32(bus->dev, "mipi-sdw-sw-interface-revision", @@ -48,11 +71,11 @@ int sdw_master_read_prop(struct sdw_bus *bus) return -EIO; } - if (fwnode_property_read_bool(link, + if (mipi_fwnode_property_read_bool(link, "mipi-sdw-clock-stop-mode0-supported")) prop->clk_stop_modes |= BIT(SDW_CLK_STOP_MODE0); - if (fwnode_property_read_bool(link, + if (mipi_fwnode_property_read_bool(link, "mipi-sdw-clock-stop-mode1-supported")) prop->clk_stop_modes |= BIT(SDW_CLK_STOP_MODE1); @@ -71,9 +94,11 @@ int sdw_master_read_prop(struct sdw_bus *bus) return -ENOMEM; } - fwnode_property_read_u32_array(link, + ret = fwnode_property_read_u32_array(link, "mipi-sdw-clock-frequencies-supported", prop->clk_freq, prop->num_clk_freq); + if (ret < 0) + return ret; } /* @@ -88,7 +113,12 @@ int sdw_master_read_prop(struct sdw_bus *bus) } } - nval = fwnode_property_count_u32(link, "mipi-sdw-supported-clock-gears"); + scales_prop = "mipi-sdw-supported-clock-scales"; + nval = fwnode_property_count_u32(link, scales_prop); + if (nval == 0) { + scales_prop = "mipi-sdw-supported-clock-gears"; + nval = fwnode_property_count_u32(link, scales_prop); + } if (nval > 0) { prop->num_clk_gears = nval; prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears, @@ -99,10 +129,12 @@ int sdw_master_read_prop(struct sdw_bus *bus) return -ENOMEM; } - fwnode_property_read_u32_array(link, - "mipi-sdw-supported-clock-gears", + ret = fwnode_property_read_u32_array(link, + scales_prop, prop->clk_gears, prop->num_clk_gears); + if (ret < 0) + return ret; } fwnode_property_read_u32(link, "mipi-sdw-default-frame-rate", @@ -114,7 +146,7 @@ int sdw_master_read_prop(struct sdw_bus *bus) fwnode_property_read_u32(link, "mipi-sdw-default-frame-col-size", &prop->default_col); - prop->dynamic_frame = fwnode_property_read_bool(link, + prop->dynamic_frame = mipi_fwnode_property_read_bool(link, "mipi-sdw-dynamic-frame-shape"); fwnode_property_read_u32(link, "mipi-sdw-command-error-threshold", @@ -131,6 +163,7 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave, struct sdw_dp0_prop *dp0) { int nval; + int ret; fwnode_property_read_u32(port, "mipi-sdw-port-max-wordlength", &dp0->max_word); @@ -148,20 +181,38 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave, if (!dp0->words) return -ENOMEM; - fwnode_property_read_u32_array(port, + ret = fwnode_property_read_u32_array(port, "mipi-sdw-port-wordlength-configs", dp0->words, dp0->num_words); + if (ret < 0) + return ret; } - dp0->BRA_flow_controlled = fwnode_property_read_bool(port, + dp0->BRA_flow_controlled = mipi_fwnode_property_read_bool(port, "mipi-sdw-bra-flow-controlled"); - dp0->simple_ch_prep_sm = fwnode_property_read_bool(port, + dp0->simple_ch_prep_sm = mipi_fwnode_property_read_bool(port, "mipi-sdw-simplified-channel-prepare-sm"); - dp0->imp_def_interrupts = fwnode_property_read_bool(port, + dp0->imp_def_interrupts = mipi_fwnode_property_read_bool(port, "mipi-sdw-imp-def-dp0-interrupts-supported"); + nval = fwnode_property_count_u32(port, "mipi-sdw-lane-list"); + if (nval > 0) { + dp0->num_lanes = nval; + dp0->lane_list = devm_kcalloc(&slave->dev, + dp0->num_lanes, sizeof(*dp0->lane_list), + GFP_KERNEL); + if (!dp0->lane_list) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(port, + "mipi-sdw-lane-list", + dp0->lane_list, dp0->num_lanes); + if (ret < 0) + return ret; + } + return 0; } @@ -171,9 +222,10 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, { struct fwnode_handle *node; u32 bit, i = 0; - int nval; unsigned long addr; char name[40]; + int nval; + int ret; addr = ports; /* valid ports are 1 to 14 so apply mask */ @@ -208,9 +260,11 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, return -ENOMEM; } - fwnode_property_read_u32_array(node, + ret = fwnode_property_read_u32_array(node, "mipi-sdw-port-wordlength-configs", dpn[i].words, dpn[i].num_words); + if (ret < 0) + return ret; } fwnode_property_read_u32(node, "mipi-sdw-data-port-type", @@ -220,7 +274,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, "mipi-sdw-max-grouping-supported", &dpn[i].max_grouping); - dpn[i].simple_ch_prep_sm = fwnode_property_read_bool(node, + dpn[i].simple_ch_prep_sm = mipi_fwnode_property_read_bool(node, "mipi-sdw-simplified-channelprepare-sm"); fwnode_property_read_u32(node, @@ -249,9 +303,11 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, return -ENOMEM; } - fwnode_property_read_u32_array(node, + ret = fwnode_property_read_u32_array(node, "mipi-sdw-channel-number-list", dpn[i].channels, dpn[i].num_channels); + if (ret < 0) + return ret; } nval = fwnode_property_count_u32(node, "mipi-sdw-channel-combination-list"); @@ -266,10 +322,12 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, return -ENOMEM; } - fwnode_property_read_u32_array(node, + ret = fwnode_property_read_u32_array(node, "mipi-sdw-channel-combination-list", dpn[i].ch_combinations, dpn[i].num_ch_combinations); + if (ret < 0) + return ret; } fwnode_property_read_u32(node, @@ -278,13 +336,27 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, fwnode_property_read_u32(node, "mipi-sdw-max-async-buffer", &dpn[i].max_async_buffer); - dpn[i].block_pack_mode = fwnode_property_read_bool(node, + dpn[i].block_pack_mode = mipi_fwnode_property_read_bool(node, "mipi-sdw-block-packing-mode"); fwnode_property_read_u32(node, "mipi-sdw-port-encoding-type", &dpn[i].port_encoding); - /* TODO: Read audio mode */ + nval = fwnode_property_count_u32(node, "mipi-sdw-lane-list"); + if (nval > 0) { + dpn[i].num_lanes = nval; + dpn[i].lane_list = devm_kcalloc(&slave->dev, + dpn[i].num_lanes, sizeof(*dpn[i].lane_list), + GFP_KERNEL); + if (!dpn[i].lane_list) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(node, + "mipi-sdw-lane-list", + dpn[i].lane_list, dpn[i].num_lanes); + if (ret < 0) + return ret; + } fwnode_handle_put(node); @@ -304,42 +376,46 @@ int sdw_slave_read_prop(struct sdw_slave *slave) struct device *dev = &slave->dev; struct fwnode_handle *port; int nval; + int ret; device_property_read_u32(dev, "mipi-sdw-sw-interface-revision", &prop->mipi_revision); - prop->wake_capable = device_property_read_bool(dev, + prop->wake_capable = mipi_device_property_read_bool(dev, "mipi-sdw-wake-up-unavailable"); prop->wake_capable = !prop->wake_capable; - prop->test_mode_capable = device_property_read_bool(dev, + prop->test_mode_capable = mipi_device_property_read_bool(dev, "mipi-sdw-test-mode-supported"); prop->clk_stop_mode1 = false; - if (device_property_read_bool(dev, + if (mipi_device_property_read_bool(dev, "mipi-sdw-clock-stop-mode1-supported")) prop->clk_stop_mode1 = true; - prop->simple_clk_stop_capable = device_property_read_bool(dev, + prop->simple_clk_stop_capable = mipi_device_property_read_bool(dev, "mipi-sdw-simplified-clockstopprepare-sm-supported"); device_property_read_u32(dev, "mipi-sdw-clockstopprepare-timeout", &prop->clk_stop_timeout); - device_property_read_u32(dev, "mipi-sdw-slave-channelprepare-timeout", - &prop->ch_prep_timeout); + ret = device_property_read_u32(dev, "mipi-sdw-peripheral-channelprepare-timeout", + &prop->ch_prep_timeout); + if (ret < 0) + device_property_read_u32(dev, "mipi-sdw-slave-channelprepare-timeout", + &prop->ch_prep_timeout); device_property_read_u32(dev, "mipi-sdw-clockstopprepare-hard-reset-behavior", &prop->reset_behave); - prop->high_PHY_capable = device_property_read_bool(dev, + prop->high_PHY_capable = mipi_device_property_read_bool(dev, "mipi-sdw-highPHY-capable"); - prop->paging_support = device_property_read_bool(dev, + prop->paging_support = mipi_device_property_read_bool(dev, "mipi-sdw-paging-support"); - prop->bank_delay_support = device_property_read_bool(dev, + prop->bank_delay_support = mipi_device_property_read_bool(dev, "mipi-sdw-bank-delay-support"); device_property_read_u32(dev, @@ -354,7 +430,17 @@ int sdw_slave_read_prop(struct sdw_slave *slave) device_property_read_u32(dev, "mipi-sdw-sink-port-list", &prop->sink_ports); - /* Read dp0 properties */ + device_property_read_u32(dev, "mipi-sdw-sdca-interrupt-register-list", + &prop->sdca_interrupt_register_list); + + prop->commit_register_supported = mipi_device_property_read_bool(dev, + "mipi-sdw-commit-register-supported"); + + /* + * Read dp0 properties - we don't rely on the 'mipi-sdw-dp-0-supported' + * property since the 'mipi-sdw-dp0-subproperties' property is logically + * equivalent. + */ port = device_get_named_child_node(dev, "mipi-sdw-dp-0-subproperties"); if (!port) { dev_dbg(dev, "DP0 node not found!!\n"); diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index aed57002fd0e6..2b403b14066c1 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1173,7 +1173,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, else sconfig.direction = SDW_DATA_DIR_RX; - /* hw parameters wil be ignored as we only support PDM */ + /* hw parameters will be ignored as we only support PDM */ sconfig.ch_count = 1; sconfig.frame_rate = params_rate(params); sconfig.type = stream->type; diff --git a/drivers/soundwire/sysfs_slave.c b/drivers/soundwire/sysfs_slave.c index f4259710dd0f7..c5c22d1708ec5 100644 --- a/drivers/soundwire/sysfs_slave.c +++ b/drivers/soundwire/sysfs_slave.c @@ -215,7 +215,7 @@ const struct attribute_group *sdw_attr_groups[] = { /* * the status is shown in capital letters for UNATTACHED and RESERVED - * on purpose, to highligh users to the fact that these status values + * on purpose, to highlight users to the fact that these status values * are not expected. */ static const char *const slave_status[] = { diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 91108ddfaef21..316bce577081f 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -183,7 +183,7 @@ static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz) case QSPI_MR: return "MR"; case QSPI_RD: - return "MR"; + return "RD"; case QSPI_TD: return "TD"; case QSPI_SR: diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0b6b0151b3a3f..eeb7d082c2472 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1685,7 +1685,7 @@ static unsigned int spi_imx_transfer_estimate_time_us(struct spi_transfer *trans words = DIV_ROUND_UP(transfer->len * BITS_PER_BYTE, transfer->bits_per_word); word_delay_us = DIV_ROUND_CLOSEST(spi_delay_to_ns(&transfer->word_delay, transfer), NSEC_PER_USEC); - result += words * word_delay_us; + result += (u64)words * word_delay_us; } return min(result, U32_MAX); diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 316e3db884926..69d0f21755684 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -503,7 +503,7 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op rockchip_sfc_adjust_op_work((struct spi_mem_op *)op); rockchip_sfc_xfer_setup(sfc, mem, op, len); if (len) { - if (likely(sfc->use_dma) && len >= SFC_DMA_TRANS_THRETHOLD) { + if (likely(sfc->use_dma) && len >= SFC_DMA_TRANS_THRETHOLD && !(len & 0x3)) { init_completion(&sfc->cp); rockchip_sfc_irq_unmask(sfc, SFC_IMR_DMA); ret = rockchip_sfc_xfer_data_dma(sfc, op, len); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 460a49d9a0de0..ff1add2ecb91f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -424,6 +424,16 @@ static int spi_probe(struct device *dev) spi->irq = 0; } + if (has_acpi_companion(dev) && spi->irq < 0) { + struct acpi_device *adev = to_acpi_device_node(dev->fwnode); + + spi->irq = acpi_dev_gpio_irq_get(adev, 0); + if (spi->irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (spi->irq < 0) + spi->irq = 0; + } + ret = dev_pm_domain_attach(dev, true); if (ret) return ret; @@ -2866,9 +2876,6 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias, sizeof(spi->modalias)); - if (spi->irq < 0) - spi->irq = acpi_dev_gpio_irq_get(adev, 0); - acpi_device_set_enumerated(adev); adev->power.flags.ignore_parent = true; diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 9ba9495fcc4ba..ea843159b745d 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1763,14 +1763,13 @@ static int spmi_pmic_arb_register_buses(struct spmi_pmic_arb *pmic_arb, { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - struct device_node *child; int ret; /* legacy mode doesn't provide child node for the bus */ if (of_device_is_compatible(node, "qcom,spmi-pmic-arb")) return spmi_pmic_arb_bus_init(pdev, node, pmic_arb); - for_each_available_child_of_node(node, child) { + for_each_available_child_of_node_scoped(node, child) { if (of_node_name_eq(child, "spmi")) { ret = spmi_pmic_arb_bus_init(pdev, child, pmic_arb); if (ret) diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index fcdd559a8acfd..075e775d3868b 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,20 +24,10 @@ menuconfig STAGING if STAGING -source "drivers/staging/olpc_dcon/Kconfig" - source "drivers/staging/rtl8723bs/Kconfig" -source "drivers/staging/rtl8712/Kconfig" - -source "drivers/staging/rts5208/Kconfig" - source "drivers/staging/octeon/Kconfig" -source "drivers/staging/vt6655/Kconfig" - -source "drivers/staging/vt6656/Kconfig" - source "drivers/staging/iio/Kconfig" source "drivers/staging/sm750fb/Kconfig" @@ -46,8 +36,6 @@ source "drivers/staging/nvec/Kconfig" source "drivers/staging/media/Kconfig" -source "drivers/staging/gdm724x/Kconfig" - source "drivers/staging/fbtft/Kconfig" source "drivers/staging/most/Kconfig" @@ -58,8 +46,8 @@ source "drivers/staging/vc04_services/Kconfig" source "drivers/staging/axis-fifo/Kconfig" -source "drivers/staging/fieldbus/Kconfig" - source "drivers/staging/vme_user/Kconfig" +source "drivers/staging/gpib/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 4a84c4848664a..e681e403509ce 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -2,21 +2,15 @@ # Makefile for staging directory obj-y += media/ -obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ obj-$(CONFIG_RTL8723BS) += rtl8723bs/ -obj-$(CONFIG_R8712U) += rtl8712/ -obj-$(CONFIG_RTS5208) += rts5208/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ -obj-$(CONFIG_VT6655) += vt6655/ -obj-$(CONFIG_VT6656) += vt6656/ obj-$(CONFIG_VME_BUS) += vme_user/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_FB_SM750) += sm750fb/ obj-$(CONFIG_MFD_NVEC) += nvec/ -obj-$(CONFIG_LTE_GDM724X) += gdm724x/ obj-$(CONFIG_FB_TFT) += fbtft/ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ -obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ +obj-$(CONFIG_GPIB) += gpib/ diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c index 1bbb9a6db5979..7540c20090c78 100644 --- a/drivers/staging/axis-fifo/axis-fifo.c +++ b/drivers/staging/axis-fifo/axis-fifo.c @@ -919,7 +919,7 @@ static struct platform_driver axis_fifo_driver = { .of_match_table = axis_fifo_of_match, }, .probe = axis_fifo_probe, - .remove_new = axis_fifo_remove, + .remove = axis_fifo_remove, }; static int __init axis_fifo_init(void) diff --git a/drivers/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h index 3e00a26a29d5c..317be17b95c16 100644 --- a/drivers/staging/fbtft/fbtft.h +++ b/drivers/staging/fbtft/fbtft.h @@ -330,7 +330,7 @@ static struct platform_driver fbtft_driver_platform_driver = { \ .of_match_table = dt_ids, \ }, \ .probe = fbtft_driver_probe_pdev, \ - .remove_new = fbtft_driver_remove_pdev, \ + .remove = fbtft_driver_remove_pdev, \ }; \ \ static int __init fbtft_driver_module_init(void) \ diff --git a/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev b/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev deleted file mode 100644 index 45f631ea32a60..0000000000000 --- a/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev +++ /dev/null @@ -1,31 +0,0 @@ -What: /dev/fieldbus_devX -Date: December 2018 -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - The cdev interface to drivers for Fieldbus Device Memory - (aka. Process Memory). - - The following file operations are supported: - - open(2) - Create an I/O context associated with the file descriptor. - - read(2) - Read from Process Memory's "read area". - Clears POLLERR | POLLPRI from the file descriptor. - - write(2) - Write to Process Memory's "write area". - - poll(2), select(2), epoll_wait(2) etc. - When a "Process Memory Read Area Changed" event occurs, - POLLERR | POLLPRI will be set on the file descriptor. - Note that POLLIN | POLLOUT events are always set, because the - process memory area is always readable and writable. - - close(2) - Free up the I/O context that was associated - with the file descriptor. - -Users: TBD diff --git a/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev b/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev deleted file mode 100644 index 439f14d33c3bf..0000000000000 --- a/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev +++ /dev/null @@ -1,62 +0,0 @@ -What: /sys/class/fieldbus_dev/fieldbus_devX/card_name -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - Human-readable name of the Fieldbus Device. - -What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_type -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - The type of fieldbus implemented by this device. - Possible values: - 'unknown' - 'profinet' - -What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_id -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - The unique fieldbus id associated with this device. - The exact format of this id is fieldbus type dependent, e.g. - a mac address for profinet. - -What: /sys/class/fieldbus_dev/fieldbus_devX/read_area_size -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - The size, in bytes, of the Process Memory read area. - Note: this area is accessible by reading from the associated - character device (/dev/fieldbus_devX). - -What: /sys/class/fieldbus_dev/fieldbus_devX/write_area_size -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - The size, in bytes, of the Process Memory write area. - Note: this area is accessible by writing to the associated - character device (/dev/fieldbus_devX) - -What: /sys/class/fieldbus_dev/fieldbus_devX/online -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - Whether the fieldbus is online or offline. - Possible values: - '1' meaning 'online' - '0' meaning 'offline' - Note: an uevent is generated when this property changes. - -What: /sys/class/fieldbus_dev/fieldbus_devX/enabled -KernelVersion: 5.1 (staging) -Contact: Sven Van Asbroeck -Description: - Whether the device is enabled (power on) or - disabled (power off). - Possible values: - '1' meaning enabled - '0' meaning disabled - Normally a r/o property, but optionally r/w: - Writing '1' enables the device (power on) with default - settings. - Writing '0' disables the card (power off). diff --git a/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt b/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt deleted file mode 100644 index f34a95611645b..0000000000000 --- a/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt +++ /dev/null @@ -1,71 +0,0 @@ -* Arcx Anybus-S controller - -This chip communicates with the SoC over a parallel bus. It is -expected that its Device Tree node is specified as the child of a node -corresponding to the parallel bus used for communication. - -Required properties: --------------------- - - - compatible : The following chip-specific string: - "arcx,anybus-controller" - - - reg : three areas: - index 0: bus memory area where the cpld registers are located. - index 1: bus memory area of the first host's dual-port ram. - index 2: bus memory area of the second host's dual-port ram. - - - reset-gpios : the GPIO pin connected to the reset line of the controller. - - - interrupts : two interrupts: - index 0: interrupt connected to the first host - index 1: interrupt connected to the second host - Generic interrupt client node bindings are described in - interrupt-controller/interrupts.txt - -Optional: use of subnodes -------------------------- - -The card connected to a host may need additional properties. These can be -specified in subnodes to the controller node. - -The subnodes are identified by the standard 'reg' property. Which information -exactly can be specified depends on the bindings for the function driver -for the subnode. - -Required controller node properties when using subnodes: -- #address-cells: should be one. -- #size-cells: should be zero. - -Required subnode properties: -- reg: Must contain the host index of the card this subnode describes: - <0> for the first host on the controller - <1> for the second host on the controller - Note that only a single card can be plugged into a host, so the host - index uniquely describes the card location. - -Example of usage: ------------------ - -This example places the bridge on top of the i.MX WEIM parallel bus, see: -Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml - -&weim { - controller@0,0 { - compatible = "arcx,anybus-controller"; - reg = <0 0 0x100>, <0 0x400000 0x800>, <1 0x400000 0x800>; - reset-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>; - interrupt-parent = <&gpio1>; - interrupts = <1 IRQ_TYPE_LEVEL_LOW>, <5 IRQ_TYPE_LEVEL_LOW>; - /* fsl,weim-cs-timing is a i.MX WEIM bus specific property */ - fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100 - 0x00000000 0xa0000240 0x00000000>; - /* optional subnode for a card plugged into the first host */ - #address-cells = <1>; - #size-cells = <0>; - card@0 { - reg = <0>; - /* card specific properties go here */ - }; - }; -}; diff --git a/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt b/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt deleted file mode 100644 index 89fb8e14676f9..0000000000000 --- a/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt +++ /dev/null @@ -1,66 +0,0 @@ - Fieldbus-Device Subsystem - ============================================ - -Part 0 - What is a Fieldbus Device ? ------------------------------------- - -Fieldbus is the name of a family of industrial computer network protocols used -for real-time distributed control, standardized as IEC 61158. - -A complex automated industrial system -- such as manufacturing assembly line -- -usually needs a distributed control system -- an organized hierarchy of -controller systems -- to function. In this hierarchy, there is usually a -Human Machine Interface (HMI) at the top, where an operator can monitor or -operate the system. This is typically linked to a middle layer of programmable -logic controllers (PLC) via a non-time-critical communications system -(e.g. Ethernet). At the bottom of the control chain is the fieldbus that links -the PLCs to the components that actually do the work, such as sensors, -actuators, electric motors, console lights, switches, valves and contactors. - -(Source: Wikipedia) - -A "Fieldbus Device" is such an actuator, motor, console light, switch, ... -controlled via the Fieldbus by a PLC aka "Fieldbus Controller". - -Communication between PLC and device typically happens via process data memory, -separated into input and output areas. The Fieldbus then cyclically transfers -the PLC's output area to the device's input area, and vice versa. - -Part I - Why do we need this subsystem? ---------------------------------------- - -Fieldbus device (client) adapters are commercially available. They allow data -exchange with a PLC aka "Fieldbus Controller" via process data memory. - -They are typically used when a Linux device wants to expose itself as an -actuator, motor, console light, switch, etc. over the fieldbus. - -The purpose of this subsystem is: -a) present a general, standardized, extensible API/ABI to userspace; and -b) present a convenient interface to drivers. - -Part II - How can drivers use the subsystem? --------------------------------------------- - -Any driver that wants to register as a Fieldbus Device should allocate and -populate a 'struct fieldbus_dev' (from include/linux/fieldbus_dev.h). -Registration then happens by calling fieldbus_dev_register(). - -Part III - How can userspace use the subsystem? ------------------------------------------------ - -Fieldbus protocols and adapters are diverse and varied. However, they share -a limited few common behaviours and properties. This allows us to define -a simple interface consisting of a character device and a set of sysfs files: - -See: -drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev -drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev - -Note that this simple interface does not provide a way to modify adapter -configuration settings. It is therefore useful only for adapters that get their -configuration settings some other way, e.g. non-volatile memory on the adapter, -through the network, ... - -At a later phase, this simple interface can easily co-exist with a future -(netlink-based ?) configuration settings interface. diff --git a/drivers/staging/fieldbus/Kconfig b/drivers/staging/fieldbus/Kconfig deleted file mode 100644 index b0b865acccfbc..0000000000000 --- a/drivers/staging/fieldbus/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -menuconfig FIELDBUS_DEV - tristate "Fieldbus Device Support" - help - Support for Fieldbus Device Adapters. - - Fieldbus device (client) adapters allow data exchange with a PLC aka. - "Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.) - - They are typically used when a Linux device wants to expose itself - as an actuator, motor, console light, switch, etc. over the fieldbus. - - This framework is designed to provide a generic interface to Fieldbus - Devices from both the Linux Kernel and the userspace. - - If unsure, say no. - -source "drivers/staging/fieldbus/anybuss/Kconfig" - diff --git a/drivers/staging/fieldbus/Makefile b/drivers/staging/fieldbus/Makefile deleted file mode 100644 index bdf645d41344d..0000000000000 --- a/drivers/staging/fieldbus/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for fieldbus_dev drivers. -# - -obj-$(CONFIG_FIELDBUS_DEV) += fieldbus_dev.o anybuss/ -fieldbus_dev-y := dev_core.o diff --git a/drivers/staging/fieldbus/TODO b/drivers/staging/fieldbus/TODO deleted file mode 100644 index 6d6626af4ec7d..0000000000000 --- a/drivers/staging/fieldbus/TODO +++ /dev/null @@ -1,5 +0,0 @@ -TODO: --Get more people/drivers to use the Fieldbus userspace ABI. It requires - verification/sign-off by multiple users. - -Contact: Sven Van Asbroeck diff --git a/drivers/staging/fieldbus/anybuss/Kconfig b/drivers/staging/fieldbus/anybuss/Kconfig deleted file mode 100644 index 635a0a7b7dd29..0000000000000 --- a/drivers/staging/fieldbus/anybuss/Kconfig +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config HMS_ANYBUSS_BUS - tristate "HMS Anybus-S Bus Support" - select REGMAP - depends on OF && FIELDBUS_DEV - help - Driver for the HMS Industrial Networks Anybus-S bus. - You can attach a single Anybus-S compatible card to it, which - typically provides fieldbus and industrial ethernet - functionality. - -if HMS_ANYBUSS_BUS - -config ARCX_ANYBUS_CONTROLLER - tristate "Arcx Anybus-S Controller" - depends on OF && GPIOLIB && HAS_IOMEM && REGULATOR - select REGMAP_MMIO - help - Select this to get support for the Arcx Anybus controller. - It connects to the SoC via a parallel memory bus, and - embeds up to two Anybus-S buses (slots). - There is also a CAN power readout, unrelated to the Anybus, - modelled as a regulator. - -config HMS_PROFINET - tristate "HMS Profinet IRT Controller (Anybus-S)" - depends on FIELDBUS_DEV && HMS_ANYBUSS_BUS - help - If you say yes here you get support for the HMS Industrial - Networks Profinet IRT Controller. - - It will be registered with the kernel as a fieldbus_dev, - so userspace can interact with it via the fieldbus_dev userspace - interface(s). - - This driver can also be built as a module. If so, the module - will be called hms-profinet. - - If unsure, say N. - -endif diff --git a/drivers/staging/fieldbus/anybuss/Makefile b/drivers/staging/fieldbus/anybuss/Makefile deleted file mode 100644 index 3ad3dcc6be56b..0000000000000 --- a/drivers/staging/fieldbus/anybuss/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for anybuss drivers. -# - -obj-$(CONFIG_HMS_ANYBUSS_BUS) += anybuss_core.o -anybuss_core-y += host.o - -obj-$(CONFIG_ARCX_ANYBUS_CONTROLLER) += arcx-anybus.o -obj-$(CONFIG_HMS_PROFINET) += hms-profinet.o diff --git a/drivers/staging/fieldbus/anybuss/anybuss-client.h b/drivers/staging/fieldbus/anybuss/anybuss-client.h deleted file mode 100644 index c21c4bebfb845..0000000000000 --- a/drivers/staging/fieldbus/anybuss/anybuss-client.h +++ /dev/null @@ -1,95 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Anybus-S client adapter definitions - * - * Copyright 2018 Arcx Inc - */ - -#ifndef __LINUX_ANYBUSS_CLIENT_H__ -#define __LINUX_ANYBUSS_CLIENT_H__ - -#include -#include -#include - -/* move to when taking this out of staging */ -#include "../fieldbus_dev.h" - -struct anybuss_host; - -struct anybuss_client { - struct device dev; - struct anybuss_host *host; - __be16 anybus_id; - /* - * these can be optionally set by the client to receive event - * notifications from the host. - */ - void (*on_area_updated)(struct anybuss_client *client); - void (*on_online_changed)(struct anybuss_client *client, bool online); -}; - -struct anybuss_client_driver { - struct device_driver driver; - int (*probe)(struct anybuss_client *adev); - void (*remove)(struct anybuss_client *adev); - u16 anybus_id; -}; - -int anybuss_client_driver_register(struct anybuss_client_driver *drv); -void anybuss_client_driver_unregister(struct anybuss_client_driver *drv); - -static inline struct anybuss_client *to_anybuss_client(struct device *dev) -{ - return container_of(dev, struct anybuss_client, dev); -} - -#define to_anybuss_client_driver(__drv) container_of_const(__drv, struct anybuss_client_driver, driver) - -static inline void * -anybuss_get_drvdata(const struct anybuss_client *client) -{ - return dev_get_drvdata(&client->dev); -} - -static inline void -anybuss_set_drvdata(struct anybuss_client *client, void *data) -{ - dev_set_drvdata(&client->dev, data); -} - -int anybuss_set_power(struct anybuss_client *client, bool power_on); - -struct anybuss_memcfg { - u16 input_io; - u16 input_dpram; - u16 input_total; - - u16 output_io; - u16 output_dpram; - u16 output_total; - - enum fieldbus_dev_offl_mode offl_mode; -}; - -int anybuss_start_init(struct anybuss_client *client, - const struct anybuss_memcfg *cfg); -int anybuss_finish_init(struct anybuss_client *client); -int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr, - void *buf, size_t count); -int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num, - const void *buf, size_t count); -int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num, - const void *buf, size_t count); -int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num, - void *buf, size_t count); - -/* these help clients make a struct file_operations */ -int anybuss_write_input(struct anybuss_client *client, - const char __user *buf, size_t size, - loff_t *offset); -int anybuss_read_output(struct anybuss_client *client, - char __user *buf, size_t size, - loff_t *offset); - -#endif /* __LINUX_ANYBUSS_CLIENT_H__ */ diff --git a/drivers/staging/fieldbus/anybuss/anybuss-controller.h b/drivers/staging/fieldbus/anybuss/anybuss-controller.h deleted file mode 100644 index 02fa0749043bd..0000000000000 --- a/drivers/staging/fieldbus/anybuss/anybuss-controller.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Anybus-S controller definitions - * - * Copyright 2018 Arcx Inc - */ - -#ifndef __LINUX_ANYBUSS_CONTROLLER_H__ -#define __LINUX_ANYBUSS_CONTROLLER_H__ - -#include -#include - -/* - * To instantiate an Anybus-S host, a controller should provide the following: - * - a reset function which resets the attached card; - * - a regmap which provides access to the attached card's dpram; - * - the irq of the attached card - */ -/** - * struct anybuss_ops - Controller resources to instantiate an Anybus-S host - * - * @reset: asserts/deasserts the anybus card's reset line. - * @regmap: provides access to the card's dual-port RAM area. - * @irq: number of the interrupt connected to the card's interrupt line. - * @host_idx: for multi-host controllers, the host index: - * 0 for the first host on the controller, 1 for the second, etc. - */ -struct anybuss_ops { - void (*reset)(struct device *dev, bool assert); - struct regmap *regmap; - int irq; - int host_idx; -}; - -struct anybuss_host; - -struct anybuss_host * __must_check -anybuss_host_common_probe(struct device *dev, - const struct anybuss_ops *ops); -void anybuss_host_common_remove(struct anybuss_host *host); - -struct anybuss_host * __must_check -devm_anybuss_host_common_probe(struct device *dev, - const struct anybuss_ops *ops); - -#endif /* __LINUX_ANYBUSS_CONTROLLER_H__ */ diff --git a/drivers/staging/fieldbus/anybuss/arcx-anybus.c b/drivers/staging/fieldbus/anybuss/arcx-anybus.c deleted file mode 100644 index fcd3e3722ae01..0000000000000 --- a/drivers/staging/fieldbus/anybuss/arcx-anybus.c +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Arcx Anybus-S Controller driver - * - * Copyright (C) 2018 Arcx Inc - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* move to when taking this out of staging */ -#include "anybuss-controller.h" - -#define CPLD_STATUS1 0x80 -#define CPLD_CONTROL 0x80 -#define CPLD_CONTROL_CRST 0x40 -#define CPLD_CONTROL_RST1 0x04 -#define CPLD_CONTROL_RST2 0x80 -#define CPLD_STATUS1_AB 0x02 -#define CPLD_STATUS1_CAN_POWER 0x01 -#define CPLD_DESIGN_LO 0x81 -#define CPLD_DESIGN_HI 0x82 -#define CPLD_CAP 0x83 -#define CPLD_CAP_COMPAT 0x01 -#define CPLD_CAP_SEP_RESETS 0x02 - -struct controller_priv { - struct device *class_dev; - bool common_reset; - struct gpio_desc *reset_gpiod; - void __iomem *cpld_base; - struct mutex ctrl_lock; /* protects CONTROL register */ - u8 control_reg; - char version[3]; - u16 design_no; -}; - -static void do_reset(struct controller_priv *cd, u8 rst_bit, bool reset) -{ - mutex_lock(&cd->ctrl_lock); - /* - * CPLD_CONTROL is write-only, so cache its value in - * cd->control_reg - */ - if (reset) - cd->control_reg &= ~rst_bit; - else - cd->control_reg |= rst_bit; - writeb(cd->control_reg, cd->cpld_base + CPLD_CONTROL); - /* - * h/w work-around: - * the hardware is 'too fast', so a reset followed by an immediate - * not-reset will _not_ change the anybus reset line in any way, - * losing the reset. to prevent this from happening, introduce - * a minimum reset duration. - * Verified minimum safe duration required using a scope - * on 14-June-2018: 100 us. - */ - if (reset) - usleep_range(100, 200); - mutex_unlock(&cd->ctrl_lock); -} - -static int anybuss_reset(struct controller_priv *cd, - unsigned long id, bool reset) -{ - if (id >= 2) - return -EINVAL; - if (cd->common_reset) - do_reset(cd, CPLD_CONTROL_CRST, reset); - else - do_reset(cd, id ? CPLD_CONTROL_RST2 : CPLD_CONTROL_RST1, reset); - return 0; -} - -static void export_reset_0(struct device *dev, bool assert) -{ - struct controller_priv *cd = dev_get_drvdata(dev); - - anybuss_reset(cd, 0, assert); -} - -static void export_reset_1(struct device *dev, bool assert) -{ - struct controller_priv *cd = dev_get_drvdata(dev); - - anybuss_reset(cd, 1, assert); -} - -/* - * parallel bus limitation: - * - * the anybus is 8-bit wide. we can't assume that the hardware will translate - * word accesses on the parallel bus to multiple byte-accesses on the anybus. - * - * the imx WEIM bus does not provide this type of translation. - * - * to be safe, we will limit parallel bus accesses to a single byte - * at a time for now. - */ - -static const struct regmap_config arcx_regmap_cfg = { - .reg_bits = 16, - .val_bits = 8, - .max_register = 0x7ff, - .use_single_read = true, - .use_single_write = true, - /* - * single-byte parallel bus accesses are atomic, so don't - * require any synchronization. - */ - .disable_locking = true, -}; - -static struct regmap *create_parallel_regmap(struct platform_device *pdev, - int idx) -{ - void __iomem *base; - struct device *dev = &pdev->dev; - - base = devm_platform_ioremap_resource(pdev, idx + 1); - if (IS_ERR(base)) - return ERR_CAST(base); - return devm_regmap_init_mmio(dev, base, &arcx_regmap_cfg); -} - -static struct anybuss_host * -create_anybus_host(struct platform_device *pdev, int idx) -{ - struct anybuss_ops ops = {}; - - switch (idx) { - case 0: - ops.reset = export_reset_0; - break; - case 1: - ops.reset = export_reset_1; - break; - default: - return ERR_PTR(-EINVAL); - } - ops.host_idx = idx; - ops.regmap = create_parallel_regmap(pdev, idx); - if (IS_ERR(ops.regmap)) - return ERR_CAST(ops.regmap); - ops.irq = platform_get_irq(pdev, idx); - if (ops.irq < 0) - return ERR_PTR(ops.irq); - return devm_anybuss_host_common_probe(&pdev->dev, &ops); -} - -static ssize_t version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct controller_priv *cd = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", cd->version); -} -static DEVICE_ATTR_RO(version); - -static ssize_t design_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct controller_priv *cd = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", cd->design_no); -} -static DEVICE_ATTR_RO(design_number); - -static struct attribute *controller_attributes[] = { - &dev_attr_version.attr, - &dev_attr_design_number.attr, - NULL, -}; - -static const struct attribute_group controller_attribute_group = { - .attrs = controller_attributes, -}; - -static const struct attribute_group *controller_attribute_groups[] = { - &controller_attribute_group, - NULL, -}; - -static void controller_device_release(struct device *dev) -{ - kfree(dev); -} - -static int can_power_is_enabled(struct regulator_dev *rdev) -{ - struct controller_priv *cd = rdev_get_drvdata(rdev); - - return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER); -} - -static const struct regulator_ops can_power_ops = { - .is_enabled = can_power_is_enabled, -}; - -static const struct regulator_desc can_power_desc = { - .name = "regulator-can-power", - .id = -1, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .ops = &can_power_ops, -}; - -static const struct class controller_class = { - .name = "arcx_anybus_controller", -}; - -static DEFINE_IDA(controller_index_ida); - -static int controller_probe(struct platform_device *pdev) -{ - struct controller_priv *cd; - struct device *dev = &pdev->dev; - struct regulator_config config = { }; - struct regulator_dev *regulator; - int err, id; - struct anybuss_host *host; - u8 status1, cap; - - cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL); - if (!cd) - return -ENOMEM; - dev_set_drvdata(dev, cd); - mutex_init(&cd->ctrl_lock); - cd->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(cd->reset_gpiod)) - return PTR_ERR(cd->reset_gpiod); - - /* CPLD control memory, sits at index 0 */ - cd->cpld_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(cd->cpld_base)) { - dev_err(dev, - "failed to map cpld base address\n"); - err = PTR_ERR(cd->cpld_base); - goto out_reset; - } - - /* identify cpld */ - status1 = readb(cd->cpld_base + CPLD_STATUS1); - cd->design_no = (readb(cd->cpld_base + CPLD_DESIGN_HI) << 8) | - readb(cd->cpld_base + CPLD_DESIGN_LO); - snprintf(cd->version, sizeof(cd->version), "%c%d", - 'A' + ((status1 >> 5) & 0x7), - (status1 >> 2) & 0x7); - dev_info(dev, "design number %d, revision %s\n", - cd->design_no, - cd->version); - cap = readb(cd->cpld_base + CPLD_CAP); - if (!(cap & CPLD_CAP_COMPAT)) { - dev_err(dev, "unsupported controller [cap=0x%02X]", cap); - err = -ENODEV; - goto out_reset; - } - - if (status1 & CPLD_STATUS1_AB) { - dev_info(dev, "has anybus-S slot(s)"); - cd->common_reset = !(cap & CPLD_CAP_SEP_RESETS); - dev_info(dev, "supports %s", cd->common_reset ? - "a common reset" : "separate resets"); - for (id = 0; id < 2; id++) { - host = create_anybus_host(pdev, id); - if (!IS_ERR(host)) - continue; - err = PTR_ERR(host); - /* -ENODEV is fine, it just means no card detected */ - if (err != -ENODEV) - goto out_reset; - } - } - - id = ida_alloc(&controller_index_ida, GFP_KERNEL); - if (id < 0) { - err = id; - goto out_reset; - } - /* export can power readout as a regulator */ - config.dev = dev; - config.driver_data = cd; - regulator = devm_regulator_register(dev, &can_power_desc, &config); - if (IS_ERR(regulator)) { - err = PTR_ERR(regulator); - goto out_ida; - } - /* make controller info visible to userspace */ - cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL); - if (!cd->class_dev) { - err = -ENOMEM; - goto out_ida; - } - cd->class_dev->class = &controller_class; - cd->class_dev->groups = controller_attribute_groups; - cd->class_dev->parent = dev; - cd->class_dev->id = id; - cd->class_dev->release = controller_device_release; - dev_set_name(cd->class_dev, "%d", cd->class_dev->id); - dev_set_drvdata(cd->class_dev, cd); - err = device_register(cd->class_dev); - if (err) - goto out_dev; - return 0; -out_dev: - put_device(cd->class_dev); -out_ida: - ida_free(&controller_index_ida, id); -out_reset: - gpiod_set_value_cansleep(cd->reset_gpiod, 1); - return err; -} - -static void controller_remove(struct platform_device *pdev) -{ - struct controller_priv *cd = platform_get_drvdata(pdev); - int id = cd->class_dev->id; - - device_unregister(cd->class_dev); - ida_free(&controller_index_ida, id); - gpiod_set_value_cansleep(cd->reset_gpiod, 1); -} - -static const struct of_device_id controller_of_match[] = { - { .compatible = "arcx,anybus-controller" }, - { } -}; - -MODULE_DEVICE_TABLE(of, controller_of_match); - -static struct platform_driver controller_driver = { - .probe = controller_probe, - .remove_new = controller_remove, - .driver = { - .name = "arcx-anybus-controller", - .of_match_table = controller_of_match, - }, -}; - -static int __init controller_init(void) -{ - int err; - - err = class_register(&controller_class); - if (err) - return err; - err = platform_driver_register(&controller_driver); - if (err) - class_unregister(&controller_class); - - return err; -} - -static void __exit controller_exit(void) -{ - platform_driver_unregister(&controller_driver); - class_unregister(&controller_class); - ida_destroy(&controller_index_ida); -} - -module_init(controller_init); -module_exit(controller_exit); - -MODULE_DESCRIPTION("Arcx Anybus-S Controller driver"); -MODULE_AUTHOR("Sven Van Asbroeck "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/fieldbus/anybuss/hms-profinet.c b/drivers/staging/fieldbus/anybuss/hms-profinet.c deleted file mode 100644 index e691736a53f17..0000000000000 --- a/drivers/staging/fieldbus/anybuss/hms-profinet.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * HMS Profinet Client Driver - * - * Copyright (C) 2018 Arcx Inc - */ - -#include -#include -#include -#include - -/* move to when taking this out of staging */ -#include "../fieldbus_dev.h" - -/* move to when taking this out of staging */ -#include "anybuss-client.h" - -#define PROFI_DPRAM_SIZE 512 - -/* - * --------------------------------------------------------------- - * Anybus Profinet mailbox messages - definitions - * --------------------------------------------------------------- - * note that we're depending on the layout of these structures being - * exactly as advertised. - */ - -struct msg_mac_addr { - u8 addr[6]; -}; - -struct profi_priv { - struct fieldbus_dev fbdev; - struct anybuss_client *client; - struct mutex enable_lock; /* serializes card enable */ - bool power_on; -}; - -static ssize_t -profi_read_area(struct fieldbus_dev *fbdev, char __user *buf, size_t size, - loff_t *offset) -{ - struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); - - return anybuss_read_output(priv->client, buf, size, offset); -} - -static ssize_t -profi_write_area(struct fieldbus_dev *fbdev, const char __user *buf, - size_t size, loff_t *offset) -{ - struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); - - return anybuss_write_input(priv->client, buf, size, offset); -} - -static int profi_id_get(struct fieldbus_dev *fbdev, char *buf, - size_t max_size) -{ - struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); - struct msg_mac_addr response; - int ret; - - ret = anybuss_recv_msg(priv->client, 0x0010, &response, - sizeof(response)); - if (ret < 0) - return ret; - return snprintf(buf, max_size, "%pM\n", response.addr); -} - -static bool profi_enable_get(struct fieldbus_dev *fbdev) -{ - struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); - bool power_on; - - mutex_lock(&priv->enable_lock); - power_on = priv->power_on; - mutex_unlock(&priv->enable_lock); - - return power_on; -} - -static int __profi_enable(struct profi_priv *priv) -{ - int ret; - struct anybuss_client *client = priv->client; - /* Initialization Sequence, Generic Anybus Mode */ - const struct anybuss_memcfg mem_cfg = { - .input_io = 220, - .input_dpram = PROFI_DPRAM_SIZE, - .input_total = PROFI_DPRAM_SIZE, - .output_io = 220, - .output_dpram = PROFI_DPRAM_SIZE, - .output_total = PROFI_DPRAM_SIZE, - .offl_mode = FIELDBUS_DEV_OFFL_MODE_CLEAR, - }; - - /* - * switch anybus off then on, this ensures we can do a complete - * configuration cycle in case anybus was already on. - */ - anybuss_set_power(client, false); - ret = anybuss_set_power(client, true); - if (ret) - goto err; - ret = anybuss_start_init(client, &mem_cfg); - if (ret) - goto err; - ret = anybuss_finish_init(client); - if (ret) - goto err; - priv->power_on = true; - return 0; - -err: - anybuss_set_power(client, false); - priv->power_on = false; - return ret; -} - -static int __profi_disable(struct profi_priv *priv) -{ - struct anybuss_client *client = priv->client; - - anybuss_set_power(client, false); - priv->power_on = false; - return 0; -} - -static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable) -{ - int ret; - struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); - - mutex_lock(&priv->enable_lock); - if (enable) - ret = __profi_enable(priv); - else - ret = __profi_disable(priv); - mutex_unlock(&priv->enable_lock); - - return ret; -} - -static void profi_on_area_updated(struct anybuss_client *client) -{ - struct profi_priv *priv = anybuss_get_drvdata(client); - - fieldbus_dev_area_updated(&priv->fbdev); -} - -static void profi_on_online_changed(struct anybuss_client *client, bool online) -{ - struct profi_priv *priv = anybuss_get_drvdata(client); - - fieldbus_dev_online_changed(&priv->fbdev, online); -} - -static int profinet_probe(struct anybuss_client *client) -{ - struct profi_priv *priv; - struct device *dev = &client->dev; - int err; - - client->on_area_updated = profi_on_area_updated; - client->on_online_changed = profi_on_online_changed; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - mutex_init(&priv->enable_lock); - priv->client = client; - priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE; - priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE; - priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)"; - priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET; - priv->fbdev.read_area = profi_read_area; - priv->fbdev.write_area = profi_write_area; - priv->fbdev.fieldbus_id_get = profi_id_get; - priv->fbdev.enable_get = profi_enable_get; - priv->fbdev.simple_enable_set = profi_simple_enable; - priv->fbdev.parent = dev; - err = fieldbus_dev_register(&priv->fbdev); - if (err < 0) - return err; - dev_info(dev, "card detected, registered as %s", - dev_name(priv->fbdev.dev)); - anybuss_set_drvdata(client, priv); - - return 0; -} - -static void profinet_remove(struct anybuss_client *client) -{ - struct profi_priv *priv = anybuss_get_drvdata(client); - - fieldbus_dev_unregister(&priv->fbdev); -} - -static struct anybuss_client_driver profinet_driver = { - .probe = profinet_probe, - .remove = profinet_remove, - .driver = { - .name = "hms-profinet", - .owner = THIS_MODULE, - }, - .anybus_id = 0x0089, -}; - -static int __init profinet_init(void) -{ - return anybuss_client_driver_register(&profinet_driver); -} -module_init(profinet_init); - -static void __exit profinet_exit(void) -{ - return anybuss_client_driver_unregister(&profinet_driver); -} -module_exit(profinet_exit); - -MODULE_AUTHOR("Sven Van Asbroeck "); -MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/fieldbus/anybuss/host.c b/drivers/staging/fieldbus/anybuss/host.c deleted file mode 100644 index 4f2b2fce92eec..0000000000000 --- a/drivers/staging/fieldbus/anybuss/host.c +++ /dev/null @@ -1,1452 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * HMS Anybus-S Host Driver - * - * Copyright (C) 2018 Arcx Inc - */ - -/* - * Architecture Overview - * ===================== - * This driver (running on the CPU/SoC) and the Anybus-S card communicate - * by reading and writing data to/from the Anybus-S Dual-Port RAM (dpram). - * This is memory connected to both the SoC and Anybus-S card, which both sides - * can access freely and concurrently. - * - * Synchronization happens by means of two registers located in the dpram: - * IND_AB: written exclusively by the Anybus card; and - * IND_AP: written exclusively by this driver. - * - * Communication happens using one of the following mechanisms: - * 1. reserve, read/write, release dpram memory areas: - * using an IND_AB/IND_AP protocol, the driver is able to reserve certain - * memory areas. no dpram memory can be read or written except if reserved. - * (with a few limited exceptions) - * 2. send and receive data structures via a shared mailbox: - * using an IND_AB/IND_AP protocol, the driver and Anybus card are able to - * exchange commands and responses using a shared mailbox. - * 3. receive software interrupts: - * using an IND_AB/IND_AP protocol, the Anybus card is able to notify the - * driver of certain events such as: bus online/offline, data available. - * note that software interrupt event bits are located in a memory area - * which must be reserved before it can be accessed. - * - * The manual[1] is silent on whether these mechanisms can happen concurrently, - * or how they should be synchronized. However, section 13 (Driver Example) - * provides the following suggestion for developing a driver: - * a) an interrupt handler which updates global variables; - * b) a continuously-running task handling area requests (1 above) - * c) a continuously-running task handling mailbox requests (2 above) - * The example conspicuously leaves out software interrupts (3 above), which - * is the thorniest issue to get right (see below). - * - * The naive, straightforward way to implement this would be: - * - create an isr which updates shared variables; - * - create a work_struct which handles software interrupts on a queue; - * - create a function which does reserve/update/unlock in a loop; - * - create a function which does mailbox send/receive in a loop; - * - call the above functions from the driver's read/write/ioctl; - * - synchronize using mutexes/spinlocks: - * + only one area request at a time - * + only one mailbox request at a time - * + protect AB_IND, AB_IND against data hazards (e.g. read-after-write) - * - * Unfortunately, the presence of the software interrupt causes subtle yet - * considerable synchronization issues; especially problematic is the - * requirement to reserve/release the area which contains the status bits. - * - * The driver architecture presented here sidesteps these synchronization issues - * by accessing the dpram from a single kernel thread only. User-space throws - * "tasks" (i.e. 1, 2 above) into a task queue, waits for their completion, - * and the kernel thread runs them to completion. - * - * Each task has a task_function, which is called/run by the queue thread. - * That function communicates with the Anybus card, and returns either - * 0 (OK), a negative error code (error), or -EINPROGRESS (waiting). - * On OK or error, the queue thread completes and dequeues the task, - * which also releases the user space thread which may still be waiting for it. - * On -EINPROGRESS (waiting), the queue thread will leave the task on the queue, - * and revisit (call again) whenever an interrupt event comes in. - * - * Each task has a state machine, which is run by calling its task_function. - * It ensures that the task will go through its various stages over time, - * returning -EINPROGRESS if it wants to wait for an event to happen. - * - * Note that according to the manual's driver example, the following operations - * may run independent of each other: - * - area reserve/read/write/release (point 1 above) - * - mailbox operations (point 2 above) - * - switching power on/off - * - * To allow them to run independently, each operation class gets its own queue. - * - * Userspace processes A, B, C, D post tasks to the appropriate queue, - * and wait for task completion: - * - * process A B C D - * | | | | - * v v v v - * |<----- ======================================== - * | | | | - * | v v v-------<-------+ - * | +--------------------------------------+ | - * | | power q | mbox q | area q | | - * | |------------|------------|------------| | - * | | task | task | task | | - * | | task | task | task | | - * | | task wait | task wait | task wait | | - * | +--------------------------------------+ | - * | ^ ^ ^ | - * | | | | ^ - * | +--------------------------------------+ | - * | | queue thread | | - * | |--------------------------------------| | - * | | single-threaded: | | - * | | loop: | | - * v | for each queue: | | - * | | run task state machine | | - * | | if task waiting: | | - * | | leave on queue | | - * | | if task done: | | - * | | complete task, remove from q | | - * | | if software irq event bits set: | | - * | | notify userspace | | - * | | post clear event bits task------>|>-------+ - * | | wait for IND_AB changed event OR | - * | | task added event OR | - * | | timeout | - * | | end loop | - * | +--------------------------------------+ - * | + wake up + - * | +--------------------------------------+ - * | ^ ^ - * | | | - * +-------->------- | - * | - * +--------------------------------------+ - * | interrupt service routine | - * |--------------------------------------| - * | wake up queue thread on IND_AB change| - * +--------------------------------------+ - * - * Note that the Anybus interrupt is dual-purpose: - * - after a reset, triggered when the card becomes ready; - * - during normal operation, triggered when AB_IND changes. - * This is why the interrupt service routine doesn't just wake up the - * queue thread, but also completes the card_boot completion. - * - * [1] https://www.anybus.com/docs/librariesprovider7/default-document-library/ - * manuals-design-guides/hms-hmsi-27-275.pdf - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* move to when taking this out of staging */ -#include "anybuss-client.h" -#include "anybuss-controller.h" - -#define DPRAM_SIZE 0x800 -#define MAX_MBOX_MSG_SZ 0x0FF -#define TIMEOUT (HZ * 2) -#define MAX_DATA_AREA_SZ 0x200 -#define MAX_FBCTRL_AREA_SZ 0x1BE - -#define REG_BOOTLOADER_V 0x7C0 -#define REG_API_V 0x7C2 -#define REG_FIELDBUS_V 0x7C4 -#define REG_SERIAL_NO 0x7C6 -#define REG_FIELDBUS_TYPE 0x7CC -#define REG_MODULE_SW_V 0x7CE -#define REG_IND_AB 0x7FF -#define REG_IND_AP 0x7FE -#define REG_EVENT_CAUSE 0x7ED -#define MBOX_IN_AREA 0x400 -#define MBOX_OUT_AREA 0x520 -#define DATA_IN_AREA 0x000 -#define DATA_OUT_AREA 0x200 -#define FBCTRL_AREA 0x640 - -#define EVENT_CAUSE_DC 0x01 -#define EVENT_CAUSE_FBOF 0x02 -#define EVENT_CAUSE_FBON 0x04 - -#define IND_AB_UPDATED 0x08 -#define IND_AX_MIN 0x80 -#define IND_AX_MOUT 0x40 -#define IND_AX_IN 0x04 -#define IND_AX_OUT 0x02 -#define IND_AX_FBCTRL 0x01 -#define IND_AP_LOCK 0x08 -#define IND_AP_ACTION 0x10 -#define IND_AX_EVNT 0x20 -#define IND_AP_ABITS (IND_AX_IN | IND_AX_OUT | \ - IND_AX_FBCTRL | \ - IND_AP_ACTION | IND_AP_LOCK) - -#define INFO_TYPE_FB 0x0002 -#define INFO_TYPE_APP 0x0001 -#define INFO_COMMAND 0x4000 - -#define OP_MODE_FBFC 0x0002 -#define OP_MODE_FBS 0x0004 -#define OP_MODE_CD 0x0200 - -#define CMD_START_INIT 0x0001 -#define CMD_ANYBUS_INIT 0x0002 -#define CMD_END_INIT 0x0003 - -/* - * --------------------------------------------------------------- - * Anybus mailbox messages - definitions - * --------------------------------------------------------------- - * note that we're depending on the layout of these structures being - * exactly as advertised. - */ - -struct anybus_mbox_hdr { - __be16 id; - __be16 info; - __be16 cmd_num; - __be16 data_size; - __be16 frame_count; - __be16 frame_num; - __be16 offset_high; - __be16 offset_low; - __be16 extended[8]; -}; - -struct msg_anybus_init { - __be16 input_io_len; - __be16 input_dpram_len; - __be16 input_total_len; - __be16 output_io_len; - __be16 output_dpram_len; - __be16 output_total_len; - __be16 op_mode; - __be16 notif_config; - __be16 wd_val; -}; - -/* ------------- ref counted tasks ------------- */ - -struct ab_task; -typedef int (*ab_task_fn_t)(struct anybuss_host *cd, - struct ab_task *t); -typedef void (*ab_done_fn_t)(struct anybuss_host *cd); - -struct area_priv { - bool is_write; - u16 flags; - u16 addr; - size_t count; - u8 buf[MAX_DATA_AREA_SZ]; -}; - -struct mbox_priv { - struct anybus_mbox_hdr hdr; - size_t msg_out_sz; - size_t msg_in_sz; - u8 msg[MAX_MBOX_MSG_SZ]; -}; - -struct ab_task { - struct kmem_cache *cache; - struct kref refcount; - ab_task_fn_t task_fn; - ab_done_fn_t done_fn; - int result; - struct completion done; - unsigned long start_jiffies; - union { - struct area_priv area_pd; - struct mbox_priv mbox_pd; - }; -}; - -static struct ab_task *ab_task_create_get(struct kmem_cache *cache, - ab_task_fn_t task_fn) -{ - struct ab_task *t; - - t = kmem_cache_alloc(cache, GFP_KERNEL); - if (!t) - return NULL; - t->cache = cache; - kref_init(&t->refcount); - t->task_fn = task_fn; - t->done_fn = NULL; - t->result = 0; - init_completion(&t->done); - return t; -} - -static void __ab_task_destroy(struct kref *refcount) -{ - struct ab_task *t = container_of(refcount, struct ab_task, refcount); - struct kmem_cache *cache = t->cache; - - kmem_cache_free(cache, t); -} - -static void ab_task_put(struct ab_task *t) -{ - kref_put(&t->refcount, __ab_task_destroy); -} - -static struct ab_task *__ab_task_get(struct ab_task *t) -{ - kref_get(&t->refcount); - return t; -} - -static void __ab_task_finish(struct ab_task *t, struct anybuss_host *cd) -{ - if (t->done_fn) - t->done_fn(cd); - complete(&t->done); -} - -static void -ab_task_dequeue_finish_put(struct kfifo *q, struct anybuss_host *cd) -{ - int ret; - struct ab_task *t; - - ret = kfifo_out(q, &t, sizeof(t)); - WARN_ON(!ret); - __ab_task_finish(t, cd); - ab_task_put(t); -} - -static int -ab_task_enqueue(struct ab_task *t, struct kfifo *q, spinlock_t *slock, - wait_queue_head_t *wq) -{ - int ret; - - t->start_jiffies = jiffies; - __ab_task_get(t); - ret = kfifo_in_spinlocked(q, &t, sizeof(t), slock); - if (!ret) { - ab_task_put(t); - return -ENOMEM; - } - wake_up(wq); - return 0; -} - -static int -ab_task_enqueue_wait(struct ab_task *t, struct kfifo *q, spinlock_t *slock, - wait_queue_head_t *wq) -{ - int ret; - - ret = ab_task_enqueue(t, q, slock, wq); - if (ret) - return ret; - ret = wait_for_completion_interruptible(&t->done); - if (ret) - return ret; - return t->result; -} - -/* ------------------------ anybus hardware ------------------------ */ - -struct anybuss_host { - struct device *dev; - struct anybuss_client *client; - void (*reset)(struct device *dev, bool assert); - struct regmap *regmap; - int irq; - int host_idx; - struct task_struct *qthread; - wait_queue_head_t wq; - struct completion card_boot; - atomic_t ind_ab; - spinlock_t qlock; /* protects IN side of powerq, mboxq, areaq */ - struct kmem_cache *qcache; - struct kfifo qs[3]; - struct kfifo *powerq; - struct kfifo *mboxq; - struct kfifo *areaq; - bool power_on; - bool softint_pending; -}; - -static void reset_assert(struct anybuss_host *cd) -{ - cd->reset(cd->dev, true); -} - -static void reset_deassert(struct anybuss_host *cd) -{ - cd->reset(cd->dev, false); -} - -static int test_dpram(struct regmap *regmap) -{ - int i; - unsigned int val; - - for (i = 0; i < DPRAM_SIZE; i++) - regmap_write(regmap, i, (u8)i); - for (i = 0; i < DPRAM_SIZE; i++) { - regmap_read(regmap, i, &val); - if ((u8)val != (u8)i) - return -EIO; - } - return 0; -} - -static int read_ind_ab(struct regmap *regmap) -{ - unsigned long timeout = jiffies + HZ / 2; - unsigned int a, b, i = 0; - - while (time_before_eq(jiffies, timeout)) { - regmap_read(regmap, REG_IND_AB, &a); - regmap_read(regmap, REG_IND_AB, &b); - if (likely(a == b)) - return (int)a; - if (i < 10) { - cpu_relax(); - i++; - } else { - usleep_range(500, 1000); - } - } - WARN(1, "IND_AB register not stable"); - return -ETIMEDOUT; -} - -static int write_ind_ap(struct regmap *regmap, unsigned int ind_ap) -{ - unsigned long timeout = jiffies + HZ / 2; - unsigned int v, i = 0; - - while (time_before_eq(jiffies, timeout)) { - regmap_write(regmap, REG_IND_AP, ind_ap); - regmap_read(regmap, REG_IND_AP, &v); - if (likely(ind_ap == v)) - return 0; - if (i < 10) { - cpu_relax(); - i++; - } else { - usleep_range(500, 1000); - } - } - WARN(1, "IND_AP register not stable"); - return -ETIMEDOUT; -} - -static irqreturn_t irq_handler(int irq, void *data) -{ - struct anybuss_host *cd = data; - int ind_ab; - - /* - * irq handler needs exclusive access to the IND_AB register, - * because the act of reading the register acks the interrupt. - * - * store the register value in cd->ind_ab (an atomic_t), so that the - * queue thread is able to read it without causing an interrupt ack - * side-effect (and without spuriously acking an interrupt). - */ - ind_ab = read_ind_ab(cd->regmap); - if (ind_ab < 0) - return IRQ_NONE; - atomic_set(&cd->ind_ab, ind_ab); - complete(&cd->card_boot); - wake_up(&cd->wq); - return IRQ_HANDLED; -} - -/* ------------------------ power on/off tasks --------------------- */ - -static int task_fn_power_off(struct anybuss_host *cd, - struct ab_task *t) -{ - struct anybuss_client *client = cd->client; - - if (!cd->power_on) - return 0; - disable_irq(cd->irq); - reset_assert(cd); - atomic_set(&cd->ind_ab, IND_AB_UPDATED); - if (client->on_online_changed) - client->on_online_changed(client, false); - cd->power_on = false; - return 0; -} - -static int task_fn_power_on_2(struct anybuss_host *cd, - struct ab_task *t) -{ - if (completion_done(&cd->card_boot)) { - cd->power_on = true; - return 0; - } - if (time_after(jiffies, t->start_jiffies + TIMEOUT)) { - disable_irq(cd->irq); - reset_assert(cd); - dev_err(cd->dev, "power on timed out"); - return -ETIMEDOUT; - } - return -EINPROGRESS; -} - -static int task_fn_power_on(struct anybuss_host *cd, - struct ab_task *t) -{ - unsigned int dummy; - - if (cd->power_on) - return 0; - /* - * anybus docs: prevent false 'init done' interrupt by - * doing a dummy read of IND_AB register while in reset. - */ - regmap_read(cd->regmap, REG_IND_AB, &dummy); - reinit_completion(&cd->card_boot); - enable_irq(cd->irq); - reset_deassert(cd); - t->task_fn = task_fn_power_on_2; - return -EINPROGRESS; -} - -int anybuss_set_power(struct anybuss_client *client, bool power_on) -{ - struct anybuss_host *cd = client->host; - struct ab_task *t; - int err; - - t = ab_task_create_get(cd->qcache, power_on ? - task_fn_power_on : task_fn_power_off); - if (!t) - return -ENOMEM; - err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq); - ab_task_put(t); - return err; -} -EXPORT_SYMBOL_GPL(anybuss_set_power); - -/* ---------------------------- area tasks ------------------------ */ - -static int task_fn_area_3(struct anybuss_host *cd, struct ab_task *t) -{ - struct area_priv *pd = &t->area_pd; - - if (!cd->power_on) - return -EIO; - if (atomic_read(&cd->ind_ab) & pd->flags) { - /* area not released yet */ - if (time_after(jiffies, t->start_jiffies + TIMEOUT)) - return -ETIMEDOUT; - return -EINPROGRESS; - } - return 0; -} - -static int task_fn_area_2(struct anybuss_host *cd, struct ab_task *t) -{ - struct area_priv *pd = &t->area_pd; - unsigned int ind_ap; - int ret; - - if (!cd->power_on) - return -EIO; - regmap_read(cd->regmap, REG_IND_AP, &ind_ap); - if (!(atomic_read(&cd->ind_ab) & pd->flags)) { - /* we don't own the area yet */ - if (time_after(jiffies, t->start_jiffies + TIMEOUT)) { - dev_warn(cd->dev, "timeout waiting for area"); - dump_stack(); - return -ETIMEDOUT; - } - return -EINPROGRESS; - } - /* we own the area, do what we're here to do */ - if (pd->is_write) - regmap_bulk_write(cd->regmap, pd->addr, pd->buf, - pd->count); - else - regmap_bulk_read(cd->regmap, pd->addr, pd->buf, - pd->count); - /* ask to release the area, must use unlocked release */ - ind_ap &= ~IND_AP_ABITS; - ind_ap |= pd->flags; - ret = write_ind_ap(cd->regmap, ind_ap); - if (ret) - return ret; - t->task_fn = task_fn_area_3; - return -EINPROGRESS; -} - -static int task_fn_area(struct anybuss_host *cd, struct ab_task *t) -{ - struct area_priv *pd = &t->area_pd; - unsigned int ind_ap; - int ret; - - if (!cd->power_on) - return -EIO; - regmap_read(cd->regmap, REG_IND_AP, &ind_ap); - /* ask to take the area */ - ind_ap &= ~IND_AP_ABITS; - ind_ap |= pd->flags | IND_AP_ACTION | IND_AP_LOCK; - ret = write_ind_ap(cd->regmap, ind_ap); - if (ret) - return ret; - t->task_fn = task_fn_area_2; - return -EINPROGRESS; -} - -static struct ab_task * -create_area_reader(struct kmem_cache *qcache, u16 flags, u16 addr, - size_t count) -{ - struct ab_task *t; - struct area_priv *ap; - - t = ab_task_create_get(qcache, task_fn_area); - if (!t) - return NULL; - ap = &t->area_pd; - ap->flags = flags; - ap->addr = addr; - ap->is_write = false; - ap->count = count; - return t; -} - -static struct ab_task * -create_area_writer(struct kmem_cache *qcache, u16 flags, u16 addr, - const void *buf, size_t count) -{ - struct ab_task *t; - struct area_priv *ap; - - t = ab_task_create_get(qcache, task_fn_area); - if (!t) - return NULL; - ap = &t->area_pd; - ap->flags = flags; - ap->addr = addr; - ap->is_write = true; - ap->count = count; - memcpy(ap->buf, buf, count); - return t; -} - -static struct ab_task * -create_area_user_writer(struct kmem_cache *qcache, u16 flags, u16 addr, - const void __user *buf, size_t count) -{ - struct ab_task *t; - struct area_priv *ap; - - t = ab_task_create_get(qcache, task_fn_area); - if (!t) - return ERR_PTR(-ENOMEM); - ap = &t->area_pd; - ap->flags = flags; - ap->addr = addr; - ap->is_write = true; - ap->count = count; - if (copy_from_user(ap->buf, buf, count)) { - ab_task_put(t); - return ERR_PTR(-EFAULT); - } - return t; -} - -static bool area_range_ok(u16 addr, size_t count, u16 area_start, - size_t area_sz) -{ - u16 area_end_ex = area_start + area_sz; - u16 addr_end_ex; - - if (addr < area_start) - return false; - if (addr >= area_end_ex) - return false; - addr_end_ex = addr + count; - if (addr_end_ex > area_end_ex) - return false; - return true; -} - -/* -------------------------- mailbox tasks ----------------------- */ - -static int task_fn_mbox_2(struct anybuss_host *cd, struct ab_task *t) -{ - struct mbox_priv *pd = &t->mbox_pd; - unsigned int ind_ap; - - if (!cd->power_on) - return -EIO; - regmap_read(cd->regmap, REG_IND_AP, &ind_ap); - if (((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MOUT) == 0) { - /* output message not here */ - if (time_after(jiffies, t->start_jiffies + TIMEOUT)) - return -ETIMEDOUT; - return -EINPROGRESS; - } - /* grab the returned header and msg */ - regmap_bulk_read(cd->regmap, MBOX_OUT_AREA, &pd->hdr, - sizeof(pd->hdr)); - regmap_bulk_read(cd->regmap, MBOX_OUT_AREA + sizeof(pd->hdr), - pd->msg, pd->msg_in_sz); - /* tell anybus we've consumed the message */ - ind_ap ^= IND_AX_MOUT; - return write_ind_ap(cd->regmap, ind_ap); -} - -static int task_fn_mbox(struct anybuss_host *cd, struct ab_task *t) -{ - struct mbox_priv *pd = &t->mbox_pd; - unsigned int ind_ap; - int ret; - - if (!cd->power_on) - return -EIO; - regmap_read(cd->regmap, REG_IND_AP, &ind_ap); - if ((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MIN) { - /* mbox input area busy */ - if (time_after(jiffies, t->start_jiffies + TIMEOUT)) - return -ETIMEDOUT; - return -EINPROGRESS; - } - /* write the header and msg to input area */ - regmap_bulk_write(cd->regmap, MBOX_IN_AREA, &pd->hdr, - sizeof(pd->hdr)); - regmap_bulk_write(cd->regmap, MBOX_IN_AREA + sizeof(pd->hdr), - pd->msg, pd->msg_out_sz); - /* tell anybus we gave it a message */ - ind_ap ^= IND_AX_MIN; - ret = write_ind_ap(cd->regmap, ind_ap); - if (ret) - return ret; - t->start_jiffies = jiffies; - t->task_fn = task_fn_mbox_2; - return -EINPROGRESS; -} - -static void log_invalid_other(struct device *dev, - struct anybus_mbox_hdr *hdr) -{ - size_t ext_offs = ARRAY_SIZE(hdr->extended) - 1; - u16 code = be16_to_cpu(hdr->extended[ext_offs]); - - dev_err(dev, " Invalid other: [0x%02X]", code); -} - -static const char * const EMSGS[] = { - "Invalid Message ID", - "Invalid Message Type", - "Invalid Command", - "Invalid Data Size", - "Message Header Malformed (offset 008h)", - "Message Header Malformed (offset 00Ah)", - "Message Header Malformed (offset 00Ch - 00Dh)", - "Invalid Address", - "Invalid Response", - "Flash Config Error", -}; - -static int mbox_cmd_err(struct device *dev, struct mbox_priv *mpriv) -{ - int i; - u8 ecode; - struct anybus_mbox_hdr *hdr = &mpriv->hdr; - u16 info = be16_to_cpu(hdr->info); - u8 *phdr = (u8 *)hdr; - u8 *pmsg = mpriv->msg; - - if (!(info & 0x8000)) - return 0; - ecode = (info >> 8) & 0x0F; - dev_err(dev, "mailbox command failed:"); - if (ecode == 0x0F) - log_invalid_other(dev, hdr); - else if (ecode < ARRAY_SIZE(EMSGS)) - dev_err(dev, " Error code: %s (0x%02X)", - EMSGS[ecode], ecode); - else - dev_err(dev, " Error code: 0x%02X\n", ecode); - dev_err(dev, "Failed command:"); - dev_err(dev, "Message Header:"); - for (i = 0; i < sizeof(mpriv->hdr); i += 2) - dev_err(dev, "%02X%02X", phdr[i], phdr[i + 1]); - dev_err(dev, "Message Data:"); - for (i = 0; i < mpriv->msg_in_sz; i += 2) - dev_err(dev, "%02X%02X", pmsg[i], pmsg[i + 1]); - dev_err(dev, "Stack dump:"); - dump_stack(); - return -EIO; -} - -static int _anybus_mbox_cmd(struct anybuss_host *cd, - u16 cmd_num, bool is_fb_cmd, - const void *msg_out, size_t msg_out_sz, - void *msg_in, size_t msg_in_sz, - const void *ext, size_t ext_sz) -{ - struct ab_task *t; - struct mbox_priv *pd; - struct anybus_mbox_hdr *h; - size_t msg_sz = max(msg_in_sz, msg_out_sz); - u16 info; - int err; - - if (msg_sz > MAX_MBOX_MSG_SZ) - return -EINVAL; - if (ext && ext_sz > sizeof(h->extended)) - return -EINVAL; - t = ab_task_create_get(cd->qcache, task_fn_mbox); - if (!t) - return -ENOMEM; - pd = &t->mbox_pd; - h = &pd->hdr; - info = is_fb_cmd ? INFO_TYPE_FB : INFO_TYPE_APP; - /* - * prevent uninitialized memory in the header from being sent - * across the anybus - */ - memset(h, 0, sizeof(*h)); - h->info = cpu_to_be16(info | INFO_COMMAND); - h->cmd_num = cpu_to_be16(cmd_num); - h->data_size = cpu_to_be16(msg_out_sz); - h->frame_count = cpu_to_be16(1); - h->frame_num = cpu_to_be16(1); - h->offset_high = cpu_to_be16(0); - h->offset_low = cpu_to_be16(0); - if (ext) - memcpy(h->extended, ext, ext_sz); - memcpy(pd->msg, msg_out, msg_out_sz); - pd->msg_out_sz = msg_out_sz; - pd->msg_in_sz = msg_in_sz; - err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq); - if (err) - goto out; - /* - * mailbox mechanism worked ok, but maybe the mbox response - * contains an error ? - */ - err = mbox_cmd_err(cd->dev, pd); - if (err) - goto out; - memcpy(msg_in, pd->msg, msg_in_sz); -out: - ab_task_put(t); - return err; -} - -/* ------------------------ anybus queues ------------------------ */ - -static void process_q(struct anybuss_host *cd, struct kfifo *q) -{ - struct ab_task *t; - int ret; - - ret = kfifo_out_peek(q, &t, sizeof(t)); - if (!ret) - return; - t->result = t->task_fn(cd, t); - if (t->result != -EINPROGRESS) - ab_task_dequeue_finish_put(q, cd); -} - -static bool qs_have_work(struct kfifo *qs, size_t num) -{ - size_t i; - struct ab_task *t; - int ret; - - for (i = 0; i < num; i++, qs++) { - ret = kfifo_out_peek(qs, &t, sizeof(t)); - if (ret && (t->result != -EINPROGRESS)) - return true; - } - return false; -} - -static void process_qs(struct anybuss_host *cd) -{ - size_t i; - struct kfifo *qs = cd->qs; - size_t nqs = ARRAY_SIZE(cd->qs); - - for (i = 0; i < nqs; i++, qs++) - process_q(cd, qs); -} - -static void softint_ack(struct anybuss_host *cd) -{ - unsigned int ind_ap; - - cd->softint_pending = false; - if (!cd->power_on) - return; - regmap_read(cd->regmap, REG_IND_AP, &ind_ap); - ind_ap &= ~IND_AX_EVNT; - ind_ap |= atomic_read(&cd->ind_ab) & IND_AX_EVNT; - write_ind_ap(cd->regmap, ind_ap); -} - -static void process_softint(struct anybuss_host *cd) -{ - struct anybuss_client *client = cd->client; - static const u8 zero; - int ret; - unsigned int ind_ap, ev; - struct ab_task *t; - - if (!cd->power_on) - return; - if (cd->softint_pending) - return; - regmap_read(cd->regmap, REG_IND_AP, &ind_ap); - if (!((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_EVNT)) - return; - /* process software interrupt */ - regmap_read(cd->regmap, REG_EVENT_CAUSE, &ev); - if (ev & EVENT_CAUSE_FBON) { - if (client->on_online_changed) - client->on_online_changed(client, true); - dev_dbg(cd->dev, "Fieldbus ON"); - } - if (ev & EVENT_CAUSE_FBOF) { - if (client->on_online_changed) - client->on_online_changed(client, false); - dev_dbg(cd->dev, "Fieldbus OFF"); - } - if (ev & EVENT_CAUSE_DC) { - if (client->on_area_updated) - client->on_area_updated(client); - dev_dbg(cd->dev, "Fieldbus data changed"); - } - /* - * reset the event cause bits. - * this must be done while owning the fbctrl area, so we'll - * enqueue a task to do that. - */ - t = create_area_writer(cd->qcache, IND_AX_FBCTRL, - REG_EVENT_CAUSE, &zero, sizeof(zero)); - if (!t) { - ret = -ENOMEM; - goto out; - } - t->done_fn = softint_ack; - ret = ab_task_enqueue(t, cd->powerq, &cd->qlock, &cd->wq); - ab_task_put(t); - cd->softint_pending = true; -out: - WARN_ON(ret); - if (ret) - softint_ack(cd); -} - -static int qthread_fn(void *data) -{ - struct anybuss_host *cd = data; - struct kfifo *qs = cd->qs; - size_t nqs = ARRAY_SIZE(cd->qs); - unsigned int ind_ab; - - /* - * this kernel thread has exclusive access to the anybus's memory. - * only exception: the IND_AB register, which is accessed exclusively - * by the interrupt service routine (ISR). This thread must not touch - * the IND_AB register, but it does require access to its value. - * - * the interrupt service routine stores the register's value in - * cd->ind_ab (an atomic_t), where we may safely access it, with the - * understanding that it can be modified by the ISR at any time. - */ - - while (!kthread_should_stop()) { - /* - * make a local copy of IND_AB, so we can go around the loop - * again in case it changed while processing queues and softint. - */ - ind_ab = atomic_read(&cd->ind_ab); - process_qs(cd); - process_softint(cd); - wait_event_timeout(cd->wq, - (atomic_read(&cd->ind_ab) != ind_ab) || - qs_have_work(qs, nqs) || - kthread_should_stop(), - HZ); - /* - * time out so even 'stuck' tasks will run eventually, - * and can time out. - */ - } - - return 0; -} - -/* ------------------------ anybus exports ------------------------ */ - -int anybuss_start_init(struct anybuss_client *client, - const struct anybuss_memcfg *cfg) -{ - int ret; - u16 op_mode; - struct anybuss_host *cd = client->host; - struct msg_anybus_init msg = { - .input_io_len = cpu_to_be16(cfg->input_io), - .input_dpram_len = cpu_to_be16(cfg->input_dpram), - .input_total_len = cpu_to_be16(cfg->input_total), - .output_io_len = cpu_to_be16(cfg->output_io), - .output_dpram_len = cpu_to_be16(cfg->output_dpram), - .output_total_len = cpu_to_be16(cfg->output_total), - .notif_config = cpu_to_be16(0x000F), - .wd_val = cpu_to_be16(0), - }; - - switch (cfg->offl_mode) { - case FIELDBUS_DEV_OFFL_MODE_CLEAR: - op_mode = 0; - break; - case FIELDBUS_DEV_OFFL_MODE_FREEZE: - op_mode = OP_MODE_FBFC; - break; - case FIELDBUS_DEV_OFFL_MODE_SET: - op_mode = OP_MODE_FBS; - break; - default: - return -EINVAL; - } - msg.op_mode = cpu_to_be16(op_mode | OP_MODE_CD); - ret = _anybus_mbox_cmd(cd, CMD_START_INIT, false, NULL, 0, - NULL, 0, NULL, 0); - if (ret) - return ret; - return _anybus_mbox_cmd(cd, CMD_ANYBUS_INIT, false, - &msg, sizeof(msg), NULL, 0, NULL, 0); -} -EXPORT_SYMBOL_GPL(anybuss_start_init); - -int anybuss_finish_init(struct anybuss_client *client) -{ - struct anybuss_host *cd = client->host; - - return _anybus_mbox_cmd(cd, CMD_END_INIT, false, NULL, 0, - NULL, 0, NULL, 0); -} -EXPORT_SYMBOL_GPL(anybuss_finish_init); - -int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr, - void *buf, size_t count) -{ - struct anybuss_host *cd = client->host; - struct ab_task *t; - int ret; - - if (count == 0) - return 0; - if (!area_range_ok(addr, count, FBCTRL_AREA, - MAX_FBCTRL_AREA_SZ)) - return -EFAULT; - t = create_area_reader(cd->qcache, IND_AX_FBCTRL, addr, count); - if (!t) - return -ENOMEM; - ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq); - if (ret) - goto out; - memcpy(buf, t->area_pd.buf, count); -out: - ab_task_put(t); - return ret; -} -EXPORT_SYMBOL_GPL(anybuss_read_fbctrl); - -int anybuss_write_input(struct anybuss_client *client, - const char __user *buf, size_t size, - loff_t *offset) -{ - ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size); - struct anybuss_host *cd = client->host; - struct ab_task *t; - int ret; - - if (len <= 0) - return 0; - t = create_area_user_writer(cd->qcache, IND_AX_IN, - DATA_IN_AREA + *offset, buf, len); - if (IS_ERR(t)) - return PTR_ERR(t); - ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq); - ab_task_put(t); - if (ret) - return ret; - /* success */ - *offset += len; - return len; -} -EXPORT_SYMBOL_GPL(anybuss_write_input); - -int anybuss_read_output(struct anybuss_client *client, - char __user *buf, size_t size, - loff_t *offset) -{ - ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size); - struct anybuss_host *cd = client->host; - struct ab_task *t; - int ret; - - if (len <= 0) - return 0; - t = create_area_reader(cd->qcache, IND_AX_OUT, - DATA_OUT_AREA + *offset, len); - if (!t) - return -ENOMEM; - ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq); - if (ret) - goto out; - if (copy_to_user(buf, t->area_pd.buf, len)) - ret = -EFAULT; -out: - ab_task_put(t); - if (ret) - return ret; - /* success */ - *offset += len; - return len; -} -EXPORT_SYMBOL_GPL(anybuss_read_output); - -int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num, - const void *buf, size_t count) -{ - struct anybuss_host *cd = client->host; - - return _anybus_mbox_cmd(cd, cmd_num, true, buf, count, NULL, 0, - NULL, 0); -} -EXPORT_SYMBOL_GPL(anybuss_send_msg); - -int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num, - const void *buf, size_t count) -{ - struct anybuss_host *cd = client->host; - - return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, NULL, 0, - buf, count); -} -EXPORT_SYMBOL_GPL(anybuss_send_ext); - -int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num, - void *buf, size_t count) -{ - struct anybuss_host *cd = client->host; - - return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, buf, count, - NULL, 0); -} -EXPORT_SYMBOL_GPL(anybuss_recv_msg); - -/* ------------------------ bus functions ------------------------ */ - -static int anybus_bus_match(struct device *dev, - const struct device_driver *drv) -{ - const struct anybuss_client_driver *adrv = - to_anybuss_client_driver(drv); - struct anybuss_client *adev = - to_anybuss_client(dev); - - return adrv->anybus_id == be16_to_cpu(adev->anybus_id); -} - -static int anybus_bus_probe(struct device *dev) -{ - struct anybuss_client_driver *adrv = - to_anybuss_client_driver(dev->driver); - struct anybuss_client *adev = - to_anybuss_client(dev); - - return adrv->probe(adev); -} - -static void anybus_bus_remove(struct device *dev) -{ - struct anybuss_client_driver *adrv = - to_anybuss_client_driver(dev->driver); - - if (adrv->remove) - adrv->remove(to_anybuss_client(dev)); -} - -static const struct bus_type anybus_bus = { - .name = "anybuss", - .match = anybus_bus_match, - .probe = anybus_bus_probe, - .remove = anybus_bus_remove, -}; - -int anybuss_client_driver_register(struct anybuss_client_driver *drv) -{ - if (!drv->probe) - return -ENODEV; - - drv->driver.bus = &anybus_bus; - return driver_register(&drv->driver); -} -EXPORT_SYMBOL_GPL(anybuss_client_driver_register); - -void anybuss_client_driver_unregister(struct anybuss_client_driver *drv) -{ - return driver_unregister(&drv->driver); -} -EXPORT_SYMBOL_GPL(anybuss_client_driver_unregister); - -static void client_device_release(struct device *dev) -{ - kfree(to_anybuss_client(dev)); -} - -static int taskq_alloc(struct device *dev, struct kfifo *q) -{ - void *buf; - size_t size = 64 * sizeof(struct ab_task *); - - buf = devm_kzalloc(dev, size, GFP_KERNEL); - if (!buf) - return -EIO; - return kfifo_init(q, buf, size); -} - -static int anybus_of_get_host_idx(struct device_node *np) -{ - const __be32 *host_idx; - - host_idx = of_get_address(np, 0, NULL, NULL); - if (!host_idx) - return -ENOENT; - return __be32_to_cpu(*host_idx); -} - -static struct device_node * -anybus_of_find_child_device(struct device *dev, int host_idx) -{ - struct device_node *node; - - if (!dev || !dev->of_node) - return NULL; - for_each_child_of_node(dev->of_node, node) { - if (anybus_of_get_host_idx(node) == host_idx) - return node; - } - return NULL; -} - -struct anybuss_host * __must_check -anybuss_host_common_probe(struct device *dev, - const struct anybuss_ops *ops) -{ - int ret, i; - u8 val[4]; - __be16 fieldbus_type; - struct anybuss_host *cd; - - cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL); - if (!cd) - return ERR_PTR(-ENOMEM); - cd->dev = dev; - cd->host_idx = ops->host_idx; - init_completion(&cd->card_boot); - init_waitqueue_head(&cd->wq); - for (i = 0; i < ARRAY_SIZE(cd->qs); i++) { - ret = taskq_alloc(dev, &cd->qs[i]); - if (ret) - return ERR_PTR(ret); - } - if (WARN_ON(ARRAY_SIZE(cd->qs) < 3)) - return ERR_PTR(-EINVAL); - cd->powerq = &cd->qs[0]; - cd->mboxq = &cd->qs[1]; - cd->areaq = &cd->qs[2]; - cd->reset = ops->reset; - if (!cd->reset) - return ERR_PTR(-EINVAL); - cd->regmap = ops->regmap; - if (!cd->regmap) - return ERR_PTR(-EINVAL); - spin_lock_init(&cd->qlock); - cd->qcache = kmem_cache_create(dev_name(dev), - sizeof(struct ab_task), 0, 0, NULL); - if (!cd->qcache) - return ERR_PTR(-ENOMEM); - cd->irq = ops->irq; - if (cd->irq <= 0) { - ret = -EINVAL; - goto err_qcache; - } - /* - * use a dpram test to check if a card is present, this is only - * possible while in reset. - */ - reset_assert(cd); - if (test_dpram(cd->regmap)) { - dev_err(dev, "no Anybus-S card in slot"); - ret = -ENODEV; - goto err_qcache; - } - ret = devm_request_threaded_irq(dev, cd->irq, NULL, irq_handler, - IRQF_ONESHOT, dev_name(dev), cd); - if (ret) { - dev_err(dev, "could not request irq"); - goto err_qcache; - } - /* - * startup sequence: - * a) perform dummy IND_AB read to prevent false 'init done' irq - * (already done by test_dpram() above) - * b) release reset - * c) wait for first interrupt - * d) interrupt came in: ready to go ! - */ - reset_deassert(cd); - if (!wait_for_completion_timeout(&cd->card_boot, TIMEOUT)) { - ret = -ETIMEDOUT; - goto err_reset; - } - /* - * according to the anybus docs, we're allowed to read these - * without handshaking / reserving the area - */ - dev_info(dev, "Anybus-S card detected"); - regmap_bulk_read(cd->regmap, REG_BOOTLOADER_V, val, 2); - dev_info(dev, "Bootloader version: %02X%02X", - val[0], val[1]); - regmap_bulk_read(cd->regmap, REG_API_V, val, 2); - dev_info(dev, "API version: %02X%02X", val[0], val[1]); - regmap_bulk_read(cd->regmap, REG_FIELDBUS_V, val, 2); - dev_info(dev, "Fieldbus version: %02X%02X", val[0], val[1]); - regmap_bulk_read(cd->regmap, REG_SERIAL_NO, val, 4); - dev_info(dev, "Serial number: %02X%02X%02X%02X", - val[0], val[1], val[2], val[3]); - add_device_randomness(&val, 4); - regmap_bulk_read(cd->regmap, REG_FIELDBUS_TYPE, &fieldbus_type, - sizeof(fieldbus_type)); - dev_info(dev, "Fieldbus type: %04X", be16_to_cpu(fieldbus_type)); - regmap_bulk_read(cd->regmap, REG_MODULE_SW_V, val, 2); - dev_info(dev, "Module SW version: %02X%02X", - val[0], val[1]); - /* put card back reset until a client driver releases it */ - disable_irq(cd->irq); - reset_assert(cd); - atomic_set(&cd->ind_ab, IND_AB_UPDATED); - /* fire up the queue thread */ - cd->qthread = kthread_run(qthread_fn, cd, dev_name(dev)); - if (IS_ERR(cd->qthread)) { - dev_err(dev, "could not create kthread"); - ret = PTR_ERR(cd->qthread); - goto err_reset; - } - /* - * now advertise that we've detected a client device (card). - * the bus infrastructure will match it to a client driver. - */ - cd->client = kzalloc(sizeof(*cd->client), GFP_KERNEL); - if (!cd->client) { - ret = -ENOMEM; - goto err_kthread; - } - cd->client->anybus_id = fieldbus_type; - cd->client->host = cd; - cd->client->dev.bus = &anybus_bus; - cd->client->dev.parent = dev; - cd->client->dev.release = client_device_release; - cd->client->dev.of_node = - anybus_of_find_child_device(dev, cd->host_idx); - dev_set_name(&cd->client->dev, "anybuss.card%d", cd->host_idx); - ret = device_register(&cd->client->dev); - if (ret) - goto err_device; - return cd; -err_device: - put_device(&cd->client->dev); -err_kthread: - kthread_stop(cd->qthread); -err_reset: - reset_assert(cd); -err_qcache: - kmem_cache_destroy(cd->qcache); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(anybuss_host_common_probe); - -void anybuss_host_common_remove(struct anybuss_host *host) -{ - struct anybuss_host *cd = host; - - device_unregister(&cd->client->dev); - kthread_stop(cd->qthread); - reset_assert(cd); - kmem_cache_destroy(cd->qcache); -} -EXPORT_SYMBOL_GPL(anybuss_host_common_remove); - -static void host_release(void *res) -{ - anybuss_host_common_remove(res); -} - -struct anybuss_host * __must_check -devm_anybuss_host_common_probe(struct device *dev, - const struct anybuss_ops *ops) -{ - struct anybuss_host *host; - int ret; - - host = anybuss_host_common_probe(dev, ops); - if (IS_ERR(host)) - return host; - - ret = devm_add_action_or_reset(dev, host_release, host); - if (ret) - return ERR_PTR(ret); - - return host; -} -EXPORT_SYMBOL_GPL(devm_anybuss_host_common_probe); - -static int __init anybus_init(void) -{ - int ret; - - ret = bus_register(&anybus_bus); - if (ret) - pr_err("could not register Anybus-S bus: %d\n", ret); - return ret; -} -module_init(anybus_init); - -static void __exit anybus_exit(void) -{ - bus_unregister(&anybus_bus); -} -module_exit(anybus_exit); - -MODULE_DESCRIPTION("HMS Anybus-S Host Driver"); -MODULE_AUTHOR("Sven Van Asbroeck "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/fieldbus/dev_core.c b/drivers/staging/fieldbus/dev_core.c deleted file mode 100644 index 0053ebd91442d..0000000000000 --- a/drivers/staging/fieldbus/dev_core.c +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Fieldbus Device Driver Core - * - */ - -#include -#include -#include -#include -#include -#include -#include - -/* move to when taking this out of staging */ -#include "fieldbus_dev.h" - -/* Maximum number of fieldbus devices */ -#define MAX_FIELDBUSES 32 - -/* the dev_t structure to store the dynamically allocated fieldbus devices */ -static dev_t fieldbus_devt; -static DEFINE_IDA(fieldbus_ida); -static DEFINE_MUTEX(fieldbus_mtx); - -static ssize_t online_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%d\n", !!fb->online); -} -static DEVICE_ATTR_RO(online); - -static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - if (!fb->enable_get) - return -EINVAL; - return sysfs_emit(buf, "%d\n", !!fb->enable_get(fb)); -} - -static ssize_t enabled_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t n) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - bool value; - int ret; - - if (!fb->simple_enable_set) - return -ENOTSUPP; - ret = kstrtobool(buf, &value); - if (ret) - return ret; - ret = fb->simple_enable_set(fb, value); - if (ret < 0) - return ret; - return n; -} -static DEVICE_ATTR_RW(enabled); - -static ssize_t card_name_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - /* card_name was provided by child driver. */ - return sysfs_emit(buf, "%s\n", fb->card_name); -} -static DEVICE_ATTR_RO(card_name); - -static ssize_t read_area_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%zu\n", fb->read_area_sz); -} -static DEVICE_ATTR_RO(read_area_size); - -static ssize_t write_area_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - return sysfs_emit(buf, "%zu\n", fb->write_area_sz); -} -static DEVICE_ATTR_RO(write_area_size); - -static ssize_t fieldbus_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - return fb->fieldbus_id_get(fb, buf, PAGE_SIZE); -} -static DEVICE_ATTR_RO(fieldbus_id); - -static ssize_t fieldbus_type_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - const char *t; - - switch (fb->fieldbus_type) { - case FIELDBUS_DEV_TYPE_PROFINET: - t = "profinet"; - break; - default: - t = "unknown"; - break; - } - - return sysfs_emit(buf, "%s\n", t); -} -static DEVICE_ATTR_RO(fieldbus_type); - -static struct attribute *fieldbus_attrs[] = { - &dev_attr_enabled.attr, - &dev_attr_card_name.attr, - &dev_attr_fieldbus_id.attr, - &dev_attr_read_area_size.attr, - &dev_attr_write_area_size.attr, - &dev_attr_online.attr, - &dev_attr_fieldbus_type.attr, - NULL, -}; - -static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr, - int n) -{ - struct device *dev = kobj_to_dev(kobj); - struct fieldbus_dev *fb = dev_get_drvdata(dev); - umode_t mode = attr->mode; - - if (attr == &dev_attr_enabled.attr) { - mode = 0; - if (fb->enable_get) - mode |= 0444; - if (fb->simple_enable_set) - mode |= 0200; - } - - return mode; -} - -static const struct attribute_group fieldbus_group = { - .attrs = fieldbus_attrs, - .is_visible = fieldbus_is_visible, -}; -__ATTRIBUTE_GROUPS(fieldbus); - -static const struct class fieldbus_class = { - .name = "fieldbus_dev", - .dev_groups = fieldbus_groups, -}; - -struct fb_open_file { - struct fieldbus_dev *fbdev; - int dc_event; -}; - -static int fieldbus_open(struct inode *inode, struct file *filp) -{ - struct fb_open_file *of; - struct fieldbus_dev *fbdev = container_of(inode->i_cdev, - struct fieldbus_dev, - cdev); - - of = kzalloc(sizeof(*of), GFP_KERNEL); - if (!of) - return -ENOMEM; - of->fbdev = fbdev; - filp->private_data = of; - return 0; -} - -static int fieldbus_release(struct inode *node, struct file *filp) -{ - struct fb_open_file *of = filp->private_data; - - kfree(of); - return 0; -} - -static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size, - loff_t *offset) -{ - struct fb_open_file *of = filp->private_data; - struct fieldbus_dev *fbdev = of->fbdev; - - of->dc_event = fbdev->dc_event; - return fbdev->read_area(fbdev, buf, size, offset); -} - -static ssize_t fieldbus_write(struct file *filp, const char __user *buf, - size_t size, loff_t *offset) -{ - struct fb_open_file *of = filp->private_data; - struct fieldbus_dev *fbdev = of->fbdev; - - return fbdev->write_area(fbdev, buf, size, offset); -} - -static __poll_t fieldbus_poll(struct file *filp, poll_table *wait) -{ - struct fb_open_file *of = filp->private_data; - struct fieldbus_dev *fbdev = of->fbdev; - __poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM; - - poll_wait(filp, &fbdev->dc_wq, wait); - /* data changed ? */ - if (fbdev->dc_event != of->dc_event) - mask |= EPOLLPRI | EPOLLERR; - return mask; -} - -static const struct file_operations fieldbus_fops = { - .open = fieldbus_open, - .release = fieldbus_release, - .read = fieldbus_read, - .write = fieldbus_write, - .poll = fieldbus_poll, - .llseek = generic_file_llseek, - .owner = THIS_MODULE, -}; - -void fieldbus_dev_area_updated(struct fieldbus_dev *fb) -{ - fb->dc_event++; - wake_up_all(&fb->dc_wq); -} -EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated); - -void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online) -{ - fb->online = online; - kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE); -} -EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed); - -static void __fieldbus_dev_unregister(struct fieldbus_dev *fb) -{ - if (!fb) - return; - device_destroy(&fieldbus_class, fb->cdev.dev); - cdev_del(&fb->cdev); - ida_free(&fieldbus_ida, fb->id); -} - -void fieldbus_dev_unregister(struct fieldbus_dev *fb) -{ - mutex_lock(&fieldbus_mtx); - __fieldbus_dev_unregister(fb); - mutex_unlock(&fieldbus_mtx); -} -EXPORT_SYMBOL_GPL(fieldbus_dev_unregister); - -static int __fieldbus_dev_register(struct fieldbus_dev *fb) -{ - dev_t devno; - int err; - - if (!fb) - return -EINVAL; - if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get) - return -EINVAL; - fb->id = ida_alloc_max(&fieldbus_ida, MAX_FIELDBUSES - 1, GFP_KERNEL); - if (fb->id < 0) - return fb->id; - devno = MKDEV(MAJOR(fieldbus_devt), fb->id); - init_waitqueue_head(&fb->dc_wq); - cdev_init(&fb->cdev, &fieldbus_fops); - err = cdev_add(&fb->cdev, devno, 1); - if (err) { - pr_err("fieldbus_dev%d unable to add device %d:%d\n", - fb->id, MAJOR(fieldbus_devt), fb->id); - goto err_cdev; - } - fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb, - "fieldbus_dev%d", fb->id); - if (IS_ERR(fb->dev)) { - err = PTR_ERR(fb->dev); - goto err_dev_create; - } - return 0; - -err_dev_create: - cdev_del(&fb->cdev); -err_cdev: - ida_free(&fieldbus_ida, fb->id); - return err; -} - -int fieldbus_dev_register(struct fieldbus_dev *fb) -{ - int err; - - mutex_lock(&fieldbus_mtx); - err = __fieldbus_dev_register(fb); - mutex_unlock(&fieldbus_mtx); - - return err; -} -EXPORT_SYMBOL_GPL(fieldbus_dev_register); - -static int __init fieldbus_init(void) -{ - int err; - - err = class_register(&fieldbus_class); - if (err < 0) { - pr_err("fieldbus_dev: could not register class\n"); - return err; - } - err = alloc_chrdev_region(&fieldbus_devt, 0, - MAX_FIELDBUSES, "fieldbus_dev"); - if (err < 0) { - pr_err("fieldbus_dev: unable to allocate char dev region\n"); - goto err_alloc; - } - return 0; - -err_alloc: - class_unregister(&fieldbus_class); - return err; -} - -static void __exit fieldbus_exit(void) -{ - unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES); - class_unregister(&fieldbus_class); - ida_destroy(&fieldbus_ida); -} - -subsys_initcall(fieldbus_init); -module_exit(fieldbus_exit); - -MODULE_AUTHOR("Sven Van Asbroeck "); -MODULE_AUTHOR("Jonathan Stiles "); -MODULE_DESCRIPTION("Fieldbus Device Driver Core"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/fieldbus/fieldbus_dev.h b/drivers/staging/fieldbus/fieldbus_dev.h deleted file mode 100644 index 301dca3b8d71a..0000000000000 --- a/drivers/staging/fieldbus/fieldbus_dev.h +++ /dev/null @@ -1,114 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Fieldbus Device Driver Core - * - */ - -#ifndef __FIELDBUS_DEV_H -#define __FIELDBUS_DEV_H - -#include -#include - -enum fieldbus_dev_type { - FIELDBUS_DEV_TYPE_UNKNOWN = 0, - FIELDBUS_DEV_TYPE_PROFINET, -}; - -enum fieldbus_dev_offl_mode { - FIELDBUS_DEV_OFFL_MODE_CLEAR = 0, - FIELDBUS_DEV_OFFL_MODE_FREEZE, - FIELDBUS_DEV_OFFL_MODE_SET -}; - -/** - * struct fieldbus_dev - Fieldbus device - * @read_area: [DRIVER] function to read the process data area of the - * device. same parameters/return values as - * the read function in struct file_operations - * @write_area: [DRIVER] function to write to the process data area of - * the device. same parameters/return values as - * the write function in struct file_operations - * @write_area_sz [DRIVER] size of the writable process data area - * @read_area_sz [DRIVER] size of the readable process data area - * @card_name [DRIVER] name of the card, e.g. "ACME Inc. profinet" - * @fieldbus_type [DRIVER] fieldbus type of this device, e.g. - * FIELDBUS_DEV_TYPE_PROFINET - * @enable_get [DRIVER] function which returns true if the card - * is enabled, false otherwise - * @fieldbus_id_get [DRIVER] function to retrieve the unique fieldbus id - * by which this device can be identified; - * return value follows the snprintf convention - * @simple_enable_set [DRIVER] (optional) function to enable the device - * according to its default settings - * @parent [DRIVER] (optional) the device's parent device - */ -struct fieldbus_dev { - ssize_t (*read_area)(struct fieldbus_dev *fbdev, char __user *buf, - size_t size, loff_t *offset); - ssize_t (*write_area)(struct fieldbus_dev *fbdev, - const char __user *buf, size_t size, - loff_t *offset); - size_t write_area_sz, read_area_sz; - const char *card_name; - enum fieldbus_dev_type fieldbus_type; - bool (*enable_get)(struct fieldbus_dev *fbdev); - int (*fieldbus_id_get)(struct fieldbus_dev *fbdev, char *buf, - size_t max_size); - int (*simple_enable_set)(struct fieldbus_dev *fbdev, bool enable); - struct device *parent; - - /* private data */ - int id; - struct cdev cdev; - struct device *dev; - int dc_event; - wait_queue_head_t dc_wq; - bool online; -}; - -#if IS_ENABLED(CONFIG_FIELDBUS_DEV) - -/** - * fieldbus_dev_unregister() - * - unregister a previously registered fieldbus device - * @fb: Device structure previously registered - **/ -void fieldbus_dev_unregister(struct fieldbus_dev *fb); - -/** - * fieldbus_dev_register() - * - register a device with the fieldbus device subsystem - * @fb: Device structure filled by the device driver - **/ -int __must_check fieldbus_dev_register(struct fieldbus_dev *fb); - -/** - * fieldbus_dev_area_updated() - * - notify the subsystem that an external fieldbus controller updated - * the process data area - * @fb: Device structure - **/ -void fieldbus_dev_area_updated(struct fieldbus_dev *fb); - -/** - * fieldbus_dev_online_changed() - * - notify the subsystem that the fieldbus online status changed - * @fb: Device structure - **/ -void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online); - -#else /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */ - -static inline void fieldbus_dev_unregister(struct fieldbus_dev *fb) {} -static inline int __must_check fieldbus_dev_register(struct fieldbus_dev *fb) -{ - return -ENOTSUPP; -} - -static inline void fieldbus_dev_area_updated(struct fieldbus_dev *fb) {} -static inline void fieldbus_dev_online_changed(struct fieldbus_dev *fb, - bool online) {} - -#endif /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */ -#endif /* __FIELDBUS_DEV_H */ diff --git a/drivers/staging/gdm724x/Kconfig b/drivers/staging/gdm724x/Kconfig deleted file mode 100644 index 1f403ecd9608b..0000000000000 --- a/drivers/staging/gdm724x/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# GCT GDM724x LTE driver configuration -# - -config LTE_GDM724X - tristate "GCT GDM724x LTE support" - depends on NET && USB && TTY && m - help - This driver supports GCT GDM724x LTE chip based USB modem devices. - It exposes 4 network devices to be used per PDN and 2 tty devices to be - used for AT commands and DM monitoring applications. - The modules will be called gdmulte.ko and gdmtty.ko - - GCT-ATCx can be used for AT Commands - GCT-DMx can be used for LTE protocol monitoring diff --git a/drivers/staging/gdm724x/Makefile b/drivers/staging/gdm724x/Makefile deleted file mode 100644 index e61b95788c9fc..0000000000000 --- a/drivers/staging/gdm724x/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_LTE_GDM724X) := gdmulte.o -gdmulte-y += gdm_lte.o netlink_k.o -gdmulte-y += gdm_usb.o gdm_endian.o - -obj-$(CONFIG_LTE_GDM724X) += gdmtty.o -gdmtty-y := gdm_tty.o gdm_mux.o - diff --git a/drivers/staging/gdm724x/TODO b/drivers/staging/gdm724x/TODO deleted file mode 100644 index b2b571ecb063f..0000000000000 --- a/drivers/staging/gdm724x/TODO +++ /dev/null @@ -1,16 +0,0 @@ -TODO: -- Clean up coding style to meet kernel standard. (80 line limit, netdev_err) -- Remove test for host endian -- Remove confusing macros (endian, hci_send, sdu_send, rcv_with_cb) -- Fixes for every instances of function returning -1 -- Check for skb->len in gdm_lte_emulate_arp() -- Use ALIGN() macro for dummy_cnt in up_to_host() -- Error handling in init_usb() -- Explain reason for multiples of 512 bytes in alloc_tx_struct() -- Review use of atomic allocation for tx structs -- No error checking for alloc_tx_struct in do_tx() -- fix up static tty port allocation to be dynamic - -Patches to: - Jonathan Kim - Dean ahn diff --git a/drivers/staging/gdm724x/gdm_endian.c b/drivers/staging/gdm724x/gdm_endian.c deleted file mode 100644 index ae39e59daf707..0000000000000 --- a/drivers/staging/gdm724x/gdm_endian.c +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#include -#include "gdm_endian.h" - -__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x) -{ - if (dev_ed == ENDIANNESS_LITTLE) - return (__force __dev16)cpu_to_le16(x); - else - return (__force __dev16)cpu_to_be16(x); -} - -u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x) -{ - if (dev_ed == ENDIANNESS_LITTLE) - return le16_to_cpu((__force __le16)x); - else - return be16_to_cpu((__force __be16)x); -} - -__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x) -{ - if (dev_ed == ENDIANNESS_LITTLE) - return (__force __dev32)cpu_to_le32(x); - else - return (__force __dev32)cpu_to_be32(x); -} - -u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x) -{ - if (dev_ed == ENDIANNESS_LITTLE) - return le32_to_cpu((__force __le32)x); - else - return be32_to_cpu((__force __be32)x); -} diff --git a/drivers/staging/gdm724x/gdm_endian.h b/drivers/staging/gdm724x/gdm_endian.h deleted file mode 100644 index f373dc3a19bf3..0000000000000 --- a/drivers/staging/gdm724x/gdm_endian.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef __GDM_ENDIAN_H__ -#define __GDM_ENDIAN_H__ - -#include - -/* - * For data in "device-endian" byte order (device endianness is model - * dependent). Analogous to __leXX or __beXX. - */ -typedef __u32 __bitwise __dev32; -typedef __u16 __bitwise __dev16; - -enum { - ENDIANNESS_MIN = 0, - ENDIANNESS_UNKNOWN, - ENDIANNESS_LITTLE, - ENDIANNESS_BIG, - ENDIANNESS_MIDDLE, - ENDIANNESS_MAX -}; - -__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x); -u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x); -__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x); -u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x); - -#endif /*__GDM_ENDIAN_H__*/ diff --git a/drivers/staging/gdm724x/gdm_lte.c b/drivers/staging/gdm724x/gdm_lte.c deleted file mode 100644 index eb754b231429b..0000000000000 --- a/drivers/staging/gdm724x/gdm_lte.c +++ /dev/null @@ -1,937 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gdm_lte.h" -#include "netlink_k.h" -#include "hci.h" -#include "hci_packet.h" -#include "gdm_endian.h" - -/* - * Netlink protocol number - */ -#define NETLINK_LTE 30 - -/* - * Default MTU Size - */ -#define DEFAULT_MTU_SIZE 1500 - -#define IP_VERSION_4 4 -#define IP_VERSION_6 6 - -static struct { - int ref_cnt; - struct sock *sock; -} lte_event; - -static const struct device_type wwan_type = { - .name = "wwan", -}; - -static int gdm_lte_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int gdm_lte_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int gdm_lte_set_config(struct net_device *dev, struct ifmap *map) -{ - if (dev->flags & IFF_UP) - return -EBUSY; - return 0; -} - -static void tx_complete(void *arg) -{ - struct nic *nic = arg; - - if (netif_queue_stopped(nic->netdev)) - netif_wake_queue(nic->netdev); -} - -static int gdm_lte_rx(struct sk_buff *skb, struct nic *nic, int nic_type) -{ - int ret, len; - - len = skb->len + ETH_HLEN; - ret = netif_rx(skb); - if (ret == NET_RX_DROP) { - nic->stats.rx_dropped++; - } else { - nic->stats.rx_packets++; - nic->stats.rx_bytes += len; - } - - return 0; -} - -static int gdm_lte_emulate_arp(struct sk_buff *skb_in, u32 nic_type) -{ - struct nic *nic = netdev_priv(skb_in->dev); - struct sk_buff *skb_out; - struct ethhdr eth; - struct vlan_ethhdr vlan_eth; - struct arphdr *arp_in; - struct arphdr *arp_out; - struct arpdata { - u8 ar_sha[ETH_ALEN]; - u8 ar_sip[4]; - u8 ar_tha[ETH_ALEN]; - u8 ar_tip[4]; - }; - struct arpdata *arp_data_in; - struct arpdata *arp_data_out; - u8 arp_temp[60]; - void *mac_header_data; - u32 mac_header_len; - - /* Check for skb->len, discard if empty */ - if (skb_in->len == 0) - return -ENODATA; - - /* Format the mac header so that it can be put to skb */ - if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) { - memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr)); - mac_header_data = &vlan_eth; - mac_header_len = VLAN_ETH_HLEN; - } else { - memcpy(ð, skb_in->data, sizeof(struct ethhdr)); - mac_header_data = ð - mac_header_len = ETH_HLEN; - } - - /* Get the pointer of the original request */ - arp_in = (struct arphdr *)(skb_in->data + mac_header_len); - arp_data_in = (struct arpdata *)(skb_in->data + mac_header_len + - sizeof(struct arphdr)); - - /* Get the pointer of the outgoing response */ - arp_out = (struct arphdr *)arp_temp; - arp_data_out = (struct arpdata *)(arp_temp + sizeof(struct arphdr)); - - /* Copy the arp header */ - memcpy(arp_out, arp_in, sizeof(struct arphdr)); - arp_out->ar_op = htons(ARPOP_REPLY); - - /* Copy the arp payload: based on 2 bytes of mac and fill the IP */ - arp_data_out->ar_sha[0] = arp_data_in->ar_sha[0]; - arp_data_out->ar_sha[1] = arp_data_in->ar_sha[1]; - memcpy(&arp_data_out->ar_sha[2], &arp_data_in->ar_tip[0], 4); - memcpy(&arp_data_out->ar_sip[0], &arp_data_in->ar_tip[0], 4); - memcpy(&arp_data_out->ar_tha[0], &arp_data_in->ar_sha[0], 6); - memcpy(&arp_data_out->ar_tip[0], &arp_data_in->ar_sip[0], 4); - - /* Fill the destination mac with source mac of the received packet */ - memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN); - /* Fill the source mac with nic's source mac */ - memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN); - - /* Alloc skb and reserve align */ - skb_out = dev_alloc_skb(skb_in->len); - if (!skb_out) - return -ENOMEM; - skb_reserve(skb_out, NET_IP_ALIGN); - - skb_put_data(skb_out, mac_header_data, mac_header_len); - skb_put_data(skb_out, arp_out, sizeof(struct arphdr)); - skb_put_data(skb_out, arp_data_out, sizeof(struct arpdata)); - - skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto; - skb_out->dev = skb_in->dev; - skb_reset_mac_header(skb_out); - skb_pull(skb_out, ETH_HLEN); - - gdm_lte_rx(skb_out, nic, nic_type); - - return 0; -} - -static __sum16 icmp6_checksum(struct ipv6hdr *ipv6, u16 *ptr, int len) -{ - unsigned short *w; - __wsum sum = 0; - int i; - u16 pa; - - union { - struct { - u8 ph_src[16]; - u8 ph_dst[16]; - u32 ph_len; - u8 ph_zero[3]; - u8 ph_nxt; - } ph __packed; - u16 pa[20]; - } pseudo_header; - - memset(&pseudo_header, 0, sizeof(pseudo_header)); - memcpy(&pseudo_header.ph.ph_src, &ipv6->saddr.in6_u.u6_addr8, 16); - memcpy(&pseudo_header.ph.ph_dst, &ipv6->daddr.in6_u.u6_addr8, 16); - pseudo_header.ph.ph_len = be16_to_cpu(ipv6->payload_len); - pseudo_header.ph.ph_nxt = ipv6->nexthdr; - - for (i = 0; i < ARRAY_SIZE(pseudo_header.pa); i++) { - pa = pseudo_header.pa[i]; - sum = csum_add(sum, csum_unfold((__force __sum16)pa)); - } - - w = ptr; - while (len > 1) { - sum = csum_add(sum, csum_unfold((__force __sum16)*w++)); - len -= 2; - } - - return csum_fold(sum); -} - -static int gdm_lte_emulate_ndp(struct sk_buff *skb_in, u32 nic_type) -{ - struct nic *nic = netdev_priv(skb_in->dev); - struct sk_buff *skb_out; - struct ethhdr eth; - struct vlan_ethhdr vlan_eth; - struct neighbour_advertisement { - u8 target_address[16]; - u8 type; - u8 length; - u8 link_layer_address[6]; - }; - struct neighbour_advertisement na; - struct neighbour_solicitation { - u8 target_address[16]; - }; - struct neighbour_solicitation *ns; - struct ipv6hdr *ipv6_in; - struct ipv6hdr ipv6_out; - struct icmp6hdr *icmp6_in; - struct icmp6hdr icmp6_out; - - void *mac_header_data; - u32 mac_header_len; - - /* Format the mac header so that it can be put to skb */ - if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) { - memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr)); - if (ntohs(vlan_eth.h_vlan_encapsulated_proto) != ETH_P_IPV6) - return -EPROTONOSUPPORT; - mac_header_data = &vlan_eth; - mac_header_len = VLAN_ETH_HLEN; - } else { - memcpy(ð, skb_in->data, sizeof(struct ethhdr)); - if (ntohs(eth.h_proto) != ETH_P_IPV6) - return -EPROTONOSUPPORT; - mac_header_data = ð - mac_header_len = ETH_HLEN; - } - - /* Check if this is IPv6 ICMP packet */ - ipv6_in = (struct ipv6hdr *)(skb_in->data + mac_header_len); - if (ipv6_in->version != 6 || ipv6_in->nexthdr != IPPROTO_ICMPV6) - return -EPROTONOSUPPORT; - - /* Check if this is NDP packet */ - icmp6_in = (struct icmp6hdr *)(skb_in->data + mac_header_len + - sizeof(struct ipv6hdr)); - if (icmp6_in->icmp6_type == NDISC_ROUTER_SOLICITATION) { /* Check RS */ - return -EPROTONOSUPPORT; - } else if (icmp6_in->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) { - /* Check NS */ - u8 icmp_na[sizeof(struct icmp6hdr) + - sizeof(struct neighbour_advertisement)]; - u8 zero_addr8[16] = {0,}; - - if (memcmp(ipv6_in->saddr.in6_u.u6_addr8, zero_addr8, 16) == 0) - /* Duplicate Address Detection: Source IP is all zero */ - return 0; - - icmp6_out.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; - icmp6_out.icmp6_code = 0; - icmp6_out.icmp6_cksum = 0; - /* R=0, S=1, O=1 */ - icmp6_out.icmp6_dataun.un_data32[0] = htonl(0x60000000); - - ns = (struct neighbour_solicitation *) - (skb_in->data + mac_header_len + - sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr)); - memcpy(&na.target_address, ns->target_address, 16); - na.type = 0x02; - na.length = 1; - na.link_layer_address[0] = 0x00; - na.link_layer_address[1] = 0x0a; - na.link_layer_address[2] = 0x3b; - na.link_layer_address[3] = 0xaf; - na.link_layer_address[4] = 0x63; - na.link_layer_address[5] = 0xc7; - - memcpy(&ipv6_out, ipv6_in, sizeof(struct ipv6hdr)); - memcpy(ipv6_out.saddr.in6_u.u6_addr8, &na.target_address, 16); - memcpy(ipv6_out.daddr.in6_u.u6_addr8, - ipv6_in->saddr.in6_u.u6_addr8, 16); - ipv6_out.payload_len = htons(sizeof(struct icmp6hdr) + - sizeof(struct neighbour_advertisement)); - - memcpy(icmp_na, &icmp6_out, sizeof(struct icmp6hdr)); - memcpy(icmp_na + sizeof(struct icmp6hdr), &na, - sizeof(struct neighbour_advertisement)); - - icmp6_out.icmp6_cksum = icmp6_checksum(&ipv6_out, - (u16 *)icmp_na, - sizeof(icmp_na)); - } else { - return -EINVAL; - } - - /* Fill the destination mac with source mac of the received packet */ - memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN); - /* Fill the source mac with nic's source mac */ - memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN); - - /* Alloc skb and reserve align */ - skb_out = dev_alloc_skb(skb_in->len); - if (!skb_out) - return -ENOMEM; - skb_reserve(skb_out, NET_IP_ALIGN); - - skb_put_data(skb_out, mac_header_data, mac_header_len); - skb_put_data(skb_out, &ipv6_out, sizeof(struct ipv6hdr)); - skb_put_data(skb_out, &icmp6_out, sizeof(struct icmp6hdr)); - skb_put_data(skb_out, &na, sizeof(struct neighbour_advertisement)); - - skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto; - skb_out->dev = skb_in->dev; - skb_reset_mac_header(skb_out); - skb_pull(skb_out, ETH_HLEN); - - gdm_lte_rx(skb_out, nic, nic_type); - - return 0; -} - -static s32 gdm_lte_tx_nic_type(struct net_device *dev, struct sk_buff *skb) -{ - struct nic *nic = netdev_priv(dev); - struct ethhdr *eth; - struct vlan_ethhdr *vlan_eth; - struct iphdr *ip; - struct ipv6hdr *ipv6; - int mac_proto; - void *network_data; - u32 nic_type; - - /* NIC TYPE is based on the nic_id of this net_device */ - nic_type = 0x00000010 | nic->nic_id; - - /* Get ethernet protocol */ - eth = (struct ethhdr *)skb->data; - if (ntohs(eth->h_proto) == ETH_P_8021Q) { - vlan_eth = skb_vlan_eth_hdr(skb); - mac_proto = ntohs(vlan_eth->h_vlan_encapsulated_proto); - network_data = skb->data + VLAN_ETH_HLEN; - nic_type |= NIC_TYPE_F_VLAN; - } else { - mac_proto = ntohs(eth->h_proto); - network_data = skb->data + ETH_HLEN; - } - - /* Process packet for nic type */ - switch (mac_proto) { - case ETH_P_ARP: - nic_type |= NIC_TYPE_ARP; - break; - case ETH_P_IP: - nic_type |= NIC_TYPE_F_IPV4; - ip = network_data; - - /* Check DHCPv4 */ - if (ip->protocol == IPPROTO_UDP) { - struct udphdr *udp = - network_data + sizeof(struct iphdr); - if (ntohs(udp->dest) == 67 || ntohs(udp->dest) == 68) - nic_type |= NIC_TYPE_F_DHCP; - } - break; - case ETH_P_IPV6: - nic_type |= NIC_TYPE_F_IPV6; - ipv6 = network_data; - - if (ipv6->nexthdr == IPPROTO_ICMPV6) /* Check NDP request */ { - struct icmp6hdr *icmp6 = - network_data + sizeof(struct ipv6hdr); - if (icmp6->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) - nic_type |= NIC_TYPE_ICMPV6; - } else if (ipv6->nexthdr == IPPROTO_UDP) /* Check DHCPv6 */ { - struct udphdr *udp = - network_data + sizeof(struct ipv6hdr); - if (ntohs(udp->dest) == 546 || ntohs(udp->dest) == 547) - nic_type |= NIC_TYPE_F_DHCP; - } - break; - default: - break; - } - - return nic_type; -} - -static netdev_tx_t gdm_lte_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct nic *nic = netdev_priv(dev); - u32 nic_type; - void *data_buf; - int data_len; - int idx; - int ret = 0; - - nic_type = gdm_lte_tx_nic_type(dev, skb); - if (nic_type == 0) { - netdev_err(dev, "tx - invalid nic_type\n"); - return -EMEDIUMTYPE; - } - - if (nic_type & NIC_TYPE_ARP) { - if (gdm_lte_emulate_arp(skb, nic_type) == 0) { - dev_kfree_skb(skb); - return 0; - } - } - - if (nic_type & NIC_TYPE_ICMPV6) { - if (gdm_lte_emulate_ndp(skb, nic_type) == 0) { - dev_kfree_skb(skb); - return 0; - } - } - - /* - * Need byte shift (that is, remove VLAN tag) if there is one - * For the case of ARP, this breaks the offset as vlan_ethhdr+4 - * is treated as ethhdr However, it shouldn't be a problem as - * the response starts from arp_hdr and ethhdr is created by this - * driver based on the NIC mac - */ - if (nic_type & NIC_TYPE_F_VLAN) { - struct vlan_ethhdr *vlan_eth = skb_vlan_eth_hdr(skb); - - nic->vlan_id = ntohs(vlan_eth->h_vlan_TCI) & VLAN_VID_MASK; - data_buf = skb->data + (VLAN_ETH_HLEN - ETH_HLEN); - data_len = skb->len - (VLAN_ETH_HLEN - ETH_HLEN); - } else { - nic->vlan_id = 0; - data_buf = skb->data; - data_len = skb->len; - } - - /* If it is a ICMPV6 packet, clear all the other bits : - * for backward compatibility with the firmware - */ - if (nic_type & NIC_TYPE_ICMPV6) - nic_type = NIC_TYPE_ICMPV6; - - /* If it is not a dhcp packet, clear all the flag bits : - * original NIC, otherwise the special flag (IPVX | DHCP) - */ - if (!(nic_type & NIC_TYPE_F_DHCP)) - nic_type &= NIC_TYPE_MASK; - - ret = sscanf(dev->name, "lte%d", &idx); - if (ret != 1) { - dev_kfree_skb(skb); - return -EINVAL; - } - - ret = nic->phy_dev->send_sdu_func(nic->phy_dev->priv_dev, - data_buf, data_len, - nic->pdn_table.dft_eps_id, 0, - tx_complete, nic, idx, - nic_type); - - if (ret == TX_NO_BUFFER || ret == TX_NO_SPC) { - netif_stop_queue(dev); - if (ret == TX_NO_BUFFER) - ret = 0; - else - ret = -ENOSPC; - } else if (ret == TX_NO_DEV) { - ret = -ENODEV; - } - - /* Updates tx stats */ - if (ret) { - nic->stats.tx_dropped++; - } else { - nic->stats.tx_packets++; - nic->stats.tx_bytes += data_len; - } - dev_kfree_skb(skb); - - return 0; -} - -static struct net_device_stats *gdm_lte_stats(struct net_device *dev) -{ - struct nic *nic = netdev_priv(dev); - - return &nic->stats; -} - -static int gdm_lte_event_send(struct net_device *dev, char *buf, int len) -{ - struct phy_dev *phy_dev = ((struct nic *)netdev_priv(dev))->phy_dev; - struct hci_packet *hci = (struct hci_packet *)buf; - int length; - int idx; - int ret; - - ret = sscanf(dev->name, "lte%d", &idx); - if (ret != 1) - return -EINVAL; - - length = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev), - hci->len) + HCI_HEADER_SIZE; - return netlink_send(lte_event.sock, idx, 0, buf, length, dev); -} - -static void gdm_lte_event_rcv(struct net_device *dev, u16 type, - void *msg, int len) -{ - struct nic *nic = netdev_priv(dev); - - nic->phy_dev->send_hci_func(nic->phy_dev->priv_dev, msg, len, NULL, - NULL); -} - -int gdm_lte_event_init(void) -{ - if (lte_event.ref_cnt == 0) - lte_event.sock = netlink_init(NETLINK_LTE, gdm_lte_event_rcv); - - if (lte_event.sock) { - lte_event.ref_cnt++; - return 0; - } - - pr_err("event init failed\n"); - return -ENODATA; -} - -void gdm_lte_event_exit(void) -{ - if (lte_event.sock && --lte_event.ref_cnt == 0) { - sock_release(lte_event.sock->sk_socket); - lte_event.sock = NULL; - } -} - -static int find_dev_index(u32 nic_type) -{ - u8 index; - - index = (u8)(nic_type & 0x0000000f); - if (index >= MAX_NIC_TYPE) - return -EINVAL; - - return index; -} - -static void gdm_lte_netif_rx(struct net_device *dev, char *buf, - int len, int flagged_nic_type) -{ - u32 nic_type; - struct nic *nic; - struct sk_buff *skb; - struct ethhdr eth; - struct vlan_ethhdr vlan_eth; - void *mac_header_data; - u32 mac_header_len; - char ip_version = 0; - - nic_type = flagged_nic_type & NIC_TYPE_MASK; - nic = netdev_priv(dev); - - if (flagged_nic_type & NIC_TYPE_F_DHCP) { - /* Change the destination mac address - * with the one requested the IP - */ - if (flagged_nic_type & NIC_TYPE_F_IPV4) { - struct dhcp_packet { - u8 op; /* BOOTREQUEST or BOOTREPLY */ - u8 htype; /* hardware address type. - * 1 = 10mb ethernet - */ - u8 hlen; /* hardware address length */ - u8 hops; /* used by relay agents only */ - u32 xid; /* unique id */ - u16 secs; /* elapsed since client began - * acquisition/renewal - */ - u16 flags; /* only one flag so far: */ - #define BROADCAST_FLAG 0x8000 - /* "I need broadcast replies" */ - u32 ciaddr; /* client IP (if client is in - * BOUND, RENEW or REBINDING state) - */ - u32 yiaddr; /* 'your' (client) IP address */ - /* IP address of next server to use in - * bootstrap, returned in DHCPOFFER, - * DHCPACK by server - */ - u32 siaddr_nip; - u32 gateway_nip; /* relay agent IP address */ - u8 chaddr[16]; /* link-layer client hardware - * address (MAC) - */ - u8 sname[64]; /* server host name (ASCIZ) */ - u8 file[128]; /* boot file name (ASCIZ) */ - u32 cookie; /* fixed first four option - * bytes (99,130,83,99 dec) - */ - } __packed; - int offset = sizeof(struct iphdr) + - sizeof(struct udphdr) + - offsetof(struct dhcp_packet, chaddr); - if (offset + ETH_ALEN > len) - return; - ether_addr_copy(nic->dest_mac_addr, buf + offset); - } - } - - if (nic->vlan_id > 0) { - mac_header_data = (void *)&vlan_eth; - mac_header_len = VLAN_ETH_HLEN; - } else { - mac_header_data = (void *)ð - mac_header_len = ETH_HLEN; - } - - /* Format the data so that it can be put to skb */ - ether_addr_copy(mac_header_data, nic->dest_mac_addr); - memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN); - - vlan_eth.h_vlan_TCI = htons(nic->vlan_id); - vlan_eth.h_vlan_proto = htons(ETH_P_8021Q); - - if (nic_type == NIC_TYPE_ARP) { - /* Should be response: Only happens because - * there was a request from the host - */ - eth.h_proto = htons(ETH_P_ARP); - vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_ARP); - } else { - ip_version = buf[0] >> 4; - if (ip_version == IP_VERSION_4) { - eth.h_proto = htons(ETH_P_IP); - vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IP); - } else if (ip_version == IP_VERSION_6) { - eth.h_proto = htons(ETH_P_IPV6); - vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IPV6); - } else { - netdev_err(dev, "Unknown IP version %d\n", ip_version); - return; - } - } - - /* Alloc skb and reserve align */ - skb = dev_alloc_skb(len + mac_header_len + NET_IP_ALIGN); - if (!skb) - return; - skb_reserve(skb, NET_IP_ALIGN); - - skb_put_data(skb, mac_header_data, mac_header_len); - skb_put_data(skb, buf, len); - - skb->protocol = ((struct ethhdr *)mac_header_data)->h_proto; - skb->dev = dev; - skb_reset_mac_header(skb); - skb_pull(skb, ETH_HLEN); - - gdm_lte_rx(skb, nic, nic_type); -} - -static void gdm_lte_multi_sdu_pkt(struct phy_dev *phy_dev, char *buf, int len) -{ - struct net_device *dev; - struct multi_sdu *multi_sdu = (struct multi_sdu *)buf; - struct sdu *sdu = NULL; - u8 endian = phy_dev->get_endian(phy_dev->priv_dev); - u8 *data = (u8 *)multi_sdu->data; - int copied; - u16 i = 0; - u16 num_packet; - u16 hci_len; - u16 cmd_evt; - u32 nic_type; - int index; - - num_packet = gdm_dev16_to_cpu(endian, multi_sdu->num_packet); - - for (i = 0; i < num_packet; i++) { - copied = data - multi_sdu->data; - if (len < copied + sizeof(*sdu)) { - pr_err("rx prevent buffer overflow"); - return; - } - - sdu = (struct sdu *)data; - - cmd_evt = gdm_dev16_to_cpu(endian, sdu->cmd_evt); - hci_len = gdm_dev16_to_cpu(endian, sdu->len); - nic_type = gdm_dev32_to_cpu(endian, sdu->nic_type); - - if (cmd_evt != LTE_RX_SDU) { - pr_err("rx sdu wrong hci %04x\n", cmd_evt); - return; - } - if (hci_len < 12 || - len < copied + sizeof(*sdu) + (hci_len - 12)) { - pr_err("rx sdu invalid len %d\n", hci_len); - return; - } - - index = find_dev_index(nic_type); - if (index < 0) { - pr_err("rx sdu invalid nic_type :%x\n", nic_type); - return; - } - dev = phy_dev->dev[index]; - gdm_lte_netif_rx(dev, (char *)sdu->data, - (int)(hci_len - 12), nic_type); - - data += ((hci_len + 3) & 0xfffc) + HCI_HEADER_SIZE; - } -} - -static void gdm_lte_pdn_table(struct net_device *dev, char *buf, int len) -{ - struct nic *nic = netdev_priv(dev); - struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf; - u8 ed = nic->phy_dev->get_endian(nic->phy_dev->priv_dev); - - if (!pdn_table->activate) { - memset(&nic->pdn_table, 0x00, sizeof(struct pdn_table)); - netdev_info(dev, "pdn deactivated\n"); - - return; - } - - nic->pdn_table.activate = pdn_table->activate; - nic->pdn_table.dft_eps_id = gdm_dev32_to_cpu(ed, pdn_table->dft_eps_id); - nic->pdn_table.nic_type = gdm_dev32_to_cpu(ed, pdn_table->nic_type); - - netdev_info(dev, "pdn activated, nic_type=0x%x\n", - nic->pdn_table.nic_type); -} - -static int gdm_lte_receive_pkt(struct phy_dev *phy_dev, char *buf, int len) -{ - struct hci_packet *hci = (struct hci_packet *)buf; - struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf; - struct sdu *sdu; - struct net_device *dev; - u8 endian = phy_dev->get_endian(phy_dev->priv_dev); - int ret = 0; - u16 cmd_evt; - u32 nic_type; - int index; - - if (!len) - return ret; - - cmd_evt = gdm_dev16_to_cpu(endian, hci->cmd_evt); - - dev = phy_dev->dev[0]; - if (!dev) - return 0; - - switch (cmd_evt) { - case LTE_RX_SDU: - sdu = (struct sdu *)hci->data; - nic_type = gdm_dev32_to_cpu(endian, sdu->nic_type); - index = find_dev_index(nic_type); - if (index < 0) - return index; - dev = phy_dev->dev[index]; - gdm_lte_netif_rx(dev, hci->data, len, nic_type); - break; - case LTE_RX_MULTI_SDU: - gdm_lte_multi_sdu_pkt(phy_dev, buf, len); - break; - case LTE_LINK_ON_OFF_INDICATION: - netdev_info(dev, "link %s\n", - ((struct hci_connect_ind *)buf)->connect - ? "on" : "off"); - break; - case LTE_PDN_TABLE_IND: - pdn_table = (struct hci_pdn_table_ind *)buf; - nic_type = gdm_dev32_to_cpu(endian, pdn_table->nic_type); - index = find_dev_index(nic_type); - if (index < 0) - return index; - dev = phy_dev->dev[index]; - gdm_lte_pdn_table(dev, buf, len); - fallthrough; - default: - ret = gdm_lte_event_send(dev, buf, len); - break; - } - - return ret; -} - -static int rx_complete(void *arg, void *data, int len, int context) -{ - struct phy_dev *phy_dev = arg; - - return gdm_lte_receive_pkt(phy_dev, data, len); -} - -void start_rx_proc(struct phy_dev *phy_dev) -{ - int i; - - for (i = 0; i < MAX_RX_SUBMIT_COUNT; i++) - phy_dev->rcv_func(phy_dev->priv_dev, - rx_complete, phy_dev, USB_COMPLETE); -} - -static const struct net_device_ops gdm_netdev_ops = { - .ndo_open = gdm_lte_open, - .ndo_stop = gdm_lte_close, - .ndo_set_config = gdm_lte_set_config, - .ndo_start_xmit = gdm_lte_tx, - .ndo_get_stats = gdm_lte_stats, -}; - -static u8 gdm_lte_macaddr[ETH_ALEN] = {0x00, 0x0a, 0x3b, 0x00, 0x00, 0x00}; - -static void form_mac_address(u8 *dev_addr, u8 *nic_src, u8 *nic_dest, - u8 *mac_address, u8 index) -{ - /* Form the dev_addr */ - if (!mac_address) - ether_addr_copy(dev_addr, gdm_lte_macaddr); - else - ether_addr_copy(dev_addr, mac_address); - - /* The last byte of the mac address - * should be less than or equal to 0xFC - */ - dev_addr[ETH_ALEN - 1] += index; - - /* Create random nic src and copy the first - * 3 bytes to be the same as dev_addr - */ - eth_random_addr(nic_src); - memcpy(nic_src, dev_addr, 3); - - /* Copy the nic_dest from dev_addr*/ - ether_addr_copy(nic_dest, dev_addr); -} - -static void validate_mac_address(u8 *mac_address) -{ - /* if zero address or multicast bit set, restore the default value */ - if (is_zero_ether_addr(mac_address) || (mac_address[0] & 0x01)) { - pr_err("MAC invalid, restoring default\n"); - memcpy(mac_address, gdm_lte_macaddr, 6); - } -} - -int register_lte_device(struct phy_dev *phy_dev, - struct device *dev, u8 *mac_address) -{ - struct nic *nic; - struct net_device *net; - char pdn_dev_name[16]; - u8 addr[ETH_ALEN]; - int ret = 0; - u8 index; - - validate_mac_address(mac_address); - - for (index = 0; index < MAX_NIC_TYPE; index++) { - /* Create device name lteXpdnX */ - sprintf(pdn_dev_name, "lte%%dpdn%d", index); - - /* Allocate netdev */ - net = alloc_netdev(sizeof(struct nic), pdn_dev_name, - NET_NAME_UNKNOWN, ether_setup); - if (!net) { - ret = -ENOMEM; - goto err; - } - net->netdev_ops = &gdm_netdev_ops; - net->flags &= ~IFF_MULTICAST; - net->mtu = DEFAULT_MTU_SIZE; - - nic = netdev_priv(net); - memset(nic, 0, sizeof(struct nic)); - nic->netdev = net; - nic->phy_dev = phy_dev; - nic->nic_id = index; - - form_mac_address(addr, - nic->src_mac_addr, - nic->dest_mac_addr, - mac_address, - index); - eth_hw_addr_set(net, addr); - - SET_NETDEV_DEV(net, dev); - SET_NETDEV_DEVTYPE(net, &wwan_type); - - ret = register_netdev(net); - if (ret) - goto err; - - netif_carrier_on(net); - - phy_dev->dev[index] = net; - } - - return 0; - -err: - unregister_lte_device(phy_dev); - - return ret; -} - -void unregister_lte_device(struct phy_dev *phy_dev) -{ - struct net_device *net; - int index; - - for (index = 0; index < MAX_NIC_TYPE; index++) { - net = phy_dev->dev[index]; - if (!net) - continue; - - unregister_netdev(net); - free_netdev(net); - } -} diff --git a/drivers/staging/gdm724x/gdm_lte.h b/drivers/staging/gdm724x/gdm_lte.h deleted file mode 100644 index f2143a6e0e990..0000000000000 --- a/drivers/staging/gdm724x/gdm_lte.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _GDM_LTE_H_ -#define _GDM_LTE_H_ - -#include -#include - -#include "gdm_endian.h" - -#define MAX_NIC_TYPE 4 -#define MAX_RX_SUBMIT_COUNT 3 -#define DRIVER_VERSION "3.7.17.0" - -enum TX_ERROR_CODE { - TX_NO_ERROR = 0, - TX_NO_DEV, - TX_NO_SPC, - TX_NO_BUFFER, -}; - -enum CALLBACK_CONTEXT { - KERNEL_THREAD = 0, - USB_COMPLETE, -}; - -struct pdn_table { - u8 activate; - u32 dft_eps_id; - u32 nic_type; -} __packed; - -struct nic; - -struct phy_dev { - void *priv_dev; - struct net_device *dev[MAX_NIC_TYPE]; - int (*send_hci_func)(void *priv_dev, void *data, int len, - void (*cb)(void *cb_data), void *cb_data); - int (*send_sdu_func)(void *priv_dev, void *data, int len, - unsigned int dft_eps_id, unsigned int eps_id, - void (*cb)(void *cb_data), void *cb_data, - int dev_idx, int nic_type); - int (*rcv_func)(void *priv_dev, - int (*cb)(void *cb_data, void *data, int len, - int context), - void *cb_data, int context); - u8 (*get_endian)(void *priv_dev); -}; - -struct nic { - struct net_device *netdev; - struct phy_dev *phy_dev; - struct net_device_stats stats; - struct pdn_table pdn_table; - u8 dest_mac_addr[ETH_ALEN]; - u8 src_mac_addr[ETH_ALEN]; - u32 nic_id; - u16 vlan_id; -}; - -int gdm_lte_event_init(void); -void gdm_lte_event_exit(void); - -void start_rx_proc(struct phy_dev *phy_dev); -int register_lte_device(struct phy_dev *phy_dev, struct device *dev, - u8 *mac_address); -void unregister_lte_device(struct phy_dev *phy_dev); - -#endif /* _GDM_LTE_H_ */ diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c deleted file mode 100644 index 9b12619671a13..0000000000000 --- a/drivers/staging/gdm724x/gdm_mux.c +++ /dev/null @@ -1,668 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gdm_mux.h" - -static u16 packet_type_for_tty_index[TTY_MAX_COUNT] = {0xF011, 0xF010}; - -#define USB_DEVICE_CDC_DATA(vid, pid) \ - .match_flags = \ - USB_DEVICE_ID_MATCH_DEVICE |\ - USB_DEVICE_ID_MATCH_INT_CLASS |\ - USB_DEVICE_ID_MATCH_INT_SUBCLASS,\ - .idVendor = vid,\ - .idProduct = pid,\ - .bInterfaceClass = USB_CLASS_COMM,\ - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM - -static const struct usb_device_id id_table[] = { - { USB_DEVICE_CDC_DATA(0x1076, 0x8000) }, /* GCT GDM7240 */ - { USB_DEVICE_CDC_DATA(0x1076, 0x8f00) }, /* GCT GDM7243 */ - { USB_DEVICE_CDC_DATA(0x1076, 0x9000) }, /* GCT GDM7243 */ - { USB_DEVICE_CDC_DATA(0x1d74, 0x2300) }, /* LGIT Phoenix */ - {} -}; - -MODULE_DEVICE_TABLE(usb, id_table); - -static int packet_type_to_tty_index(u16 packet_type) -{ - int i; - - for (i = 0; i < TTY_MAX_COUNT; i++) { - if (packet_type_for_tty_index[i] == packet_type) - return i; - } - - return -1; -} - -static struct mux_tx *alloc_mux_tx(int len) -{ - struct mux_tx *t; - - t = kzalloc(sizeof(*t), GFP_ATOMIC); - if (!t) - return NULL; - - t->urb = usb_alloc_urb(0, GFP_ATOMIC); - t->buf = kmalloc(MUX_TX_MAX_SIZE, GFP_ATOMIC); - if (!t->urb || !t->buf) { - usb_free_urb(t->urb); - kfree(t->buf); - kfree(t); - return NULL; - } - - return t; -} - -static void free_mux_tx(struct mux_tx *t) -{ - if (t) { - usb_free_urb(t->urb); - kfree(t->buf); - kfree(t); - } -} - -static struct mux_rx *alloc_mux_rx(void) -{ - struct mux_rx *r; - - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) - return NULL; - - r->urb = usb_alloc_urb(0, GFP_KERNEL); - r->buf = kmalloc(MUX_RX_MAX_SIZE, GFP_KERNEL); - if (!r->urb || !r->buf) { - usb_free_urb(r->urb); - kfree(r->buf); - kfree(r); - return NULL; - } - - return r; -} - -static void free_mux_rx(struct mux_rx *r) -{ - if (r) { - usb_free_urb(r->urb); - kfree(r->buf); - kfree(r); - } -} - -static struct mux_rx *get_rx_struct(struct rx_cxt *rx) -{ - struct mux_rx *r; - unsigned long flags; - - spin_lock_irqsave(&rx->free_list_lock, flags); - - if (list_empty(&rx->rx_free_list)) { - spin_unlock_irqrestore(&rx->free_list_lock, flags); - return NULL; - } - - r = list_entry(rx->rx_free_list.prev, struct mux_rx, free_list); - list_del(&r->free_list); - - spin_unlock_irqrestore(&rx->free_list_lock, flags); - - return r; -} - -static void put_rx_struct(struct rx_cxt *rx, struct mux_rx *r) -{ - unsigned long flags; - - spin_lock_irqsave(&rx->free_list_lock, flags); - list_add_tail(&r->free_list, &rx->rx_free_list); - spin_unlock_irqrestore(&rx->free_list_lock, flags); -} - -static int up_to_host(struct mux_rx *r) -{ - struct mux_dev *mux_dev = r->mux_dev; - struct mux_pkt_header *mux_header; - unsigned int start_flag; - unsigned int payload_size; - unsigned short packet_type; - int total_len; - u32 packet_size_sum = r->offset; - int index; - int ret = TO_HOST_INVALID_PACKET; - int len = r->len; - - while (1) { - mux_header = (struct mux_pkt_header *)(r->buf + - packet_size_sum); - start_flag = __le32_to_cpu(mux_header->start_flag); - payload_size = __le32_to_cpu(mux_header->payload_size); - packet_type = __le16_to_cpu(mux_header->packet_type); - - if (start_flag != START_FLAG) { - pr_err("invalid START_FLAG %x\n", start_flag); - break; - } - - total_len = ALIGN(MUX_HEADER_SIZE + payload_size, 4); - - if (len - packet_size_sum < total_len) { - pr_err("invalid payload : %d %d %04x\n", - payload_size, len, packet_type); - break; - } - - index = packet_type_to_tty_index(packet_type); - if (index < 0) { - pr_err("invalid index %d\n", index); - break; - } - - ret = r->callback(mux_header->data, - payload_size, - index, - mux_dev->tty_dev, - RECV_PACKET_PROCESS_CONTINUE - ); - if (ret == TO_HOST_BUFFER_REQUEST_FAIL) { - r->offset += packet_size_sum; - break; - } - - packet_size_sum += total_len; - if (len - packet_size_sum <= MUX_HEADER_SIZE + 2) { - ret = r->callback(NULL, - 0, - index, - mux_dev->tty_dev, - RECV_PACKET_PROCESS_COMPLETE - ); - break; - } - } - - return ret; -} - -static void do_rx(struct work_struct *work) -{ - struct mux_dev *mux_dev = - container_of(work, struct mux_dev, work_rx.work); - struct mux_rx *r; - struct rx_cxt *rx = &mux_dev->rx; - unsigned long flags; - int ret = 0; - - while (1) { - spin_lock_irqsave(&rx->to_host_lock, flags); - if (list_empty(&rx->to_host_list)) { - spin_unlock_irqrestore(&rx->to_host_lock, flags); - break; - } - r = list_entry(rx->to_host_list.next, struct mux_rx, - to_host_list); - list_del(&r->to_host_list); - spin_unlock_irqrestore(&rx->to_host_lock, flags); - - ret = up_to_host(r); - if (ret == TO_HOST_BUFFER_REQUEST_FAIL) - pr_err("failed to send mux data to host\n"); - else - put_rx_struct(rx, r); - } -} - -static void remove_rx_submit_list(struct mux_rx *r, struct rx_cxt *rx) -{ - unsigned long flags; - struct mux_rx *r_remove, *r_remove_next; - - spin_lock_irqsave(&rx->submit_list_lock, flags); - list_for_each_entry_safe(r_remove, r_remove_next, &rx->rx_submit_list, - rx_submit_list) { - if (r == r_remove) - list_del(&r->rx_submit_list); - } - spin_unlock_irqrestore(&rx->submit_list_lock, flags); -} - -static void gdm_mux_rcv_complete(struct urb *urb) -{ - struct mux_rx *r = urb->context; - struct mux_dev *mux_dev = r->mux_dev; - struct rx_cxt *rx = &mux_dev->rx; - unsigned long flags; - - remove_rx_submit_list(r, rx); - - if (urb->status) { - if (mux_dev->usb_state == PM_NORMAL) - dev_err(&urb->dev->dev, "%s: urb status error %d\n", - __func__, urb->status); - put_rx_struct(rx, r); - } else { - r->len = r->urb->actual_length; - spin_lock_irqsave(&rx->to_host_lock, flags); - list_add_tail(&r->to_host_list, &rx->to_host_list); - schedule_work(&mux_dev->work_rx.work); - spin_unlock_irqrestore(&rx->to_host_lock, flags); - } -} - -static int gdm_mux_recv(void *priv_dev, - int (*cb)(void *data, int len, int tty_index, - struct tty_dev *tty_dev, int complete)) -{ - struct mux_dev *mux_dev = priv_dev; - struct usb_device *usbdev = mux_dev->usbdev; - struct mux_rx *r; - struct rx_cxt *rx = &mux_dev->rx; - unsigned long flags; - int ret; - - if (!usbdev) { - pr_err("device is disconnected\n"); - return -ENODEV; - } - - r = get_rx_struct(rx); - if (!r) { - pr_err("get_rx_struct fail\n"); - return -ENOMEM; - } - - r->offset = 0; - r->mux_dev = (void *)mux_dev; - r->callback = cb; - mux_dev->rx_cb = cb; - - usb_fill_bulk_urb(r->urb, - usbdev, - usb_rcvbulkpipe(usbdev, 0x86), - r->buf, - MUX_RX_MAX_SIZE, - gdm_mux_rcv_complete, - r); - - spin_lock_irqsave(&rx->submit_list_lock, flags); - list_add_tail(&r->rx_submit_list, &rx->rx_submit_list); - spin_unlock_irqrestore(&rx->submit_list_lock, flags); - - ret = usb_submit_urb(r->urb, GFP_KERNEL); - - if (ret) { - spin_lock_irqsave(&rx->submit_list_lock, flags); - list_del(&r->rx_submit_list); - spin_unlock_irqrestore(&rx->submit_list_lock, flags); - - put_rx_struct(rx, r); - - pr_err("usb_submit_urb ret=%d\n", ret); - } - - usb_mark_last_busy(usbdev); - - return ret; -} - -static void gdm_mux_send_complete(struct urb *urb) -{ - struct mux_tx *t = urb->context; - - if (urb->status == -ECONNRESET) { - dev_info(&urb->dev->dev, "CONNRESET\n"); - free_mux_tx(t); - return; - } - - if (t->callback) - t->callback(t->cb_data); - - free_mux_tx(t); -} - -static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index, - void (*cb)(void *data), void *cb_data) -{ - struct mux_dev *mux_dev = priv_dev; - struct usb_device *usbdev = mux_dev->usbdev; - struct mux_pkt_header *mux_header; - struct mux_tx *t = NULL; - static u32 seq_num = 1; - int total_len; - int ret; - unsigned long flags; - - if (mux_dev->usb_state == PM_SUSPEND) { - ret = usb_autopm_get_interface(mux_dev->intf); - if (!ret) - usb_autopm_put_interface(mux_dev->intf); - } - - spin_lock_irqsave(&mux_dev->write_lock, flags); - - total_len = ALIGN(MUX_HEADER_SIZE + len, 4); - - t = alloc_mux_tx(total_len); - if (!t) { - pr_err("alloc_mux_tx fail\n"); - spin_unlock_irqrestore(&mux_dev->write_lock, flags); - return -ENOMEM; - } - - mux_header = (struct mux_pkt_header *)t->buf; - mux_header->start_flag = __cpu_to_le32(START_FLAG); - mux_header->seq_num = __cpu_to_le32(seq_num++); - mux_header->payload_size = __cpu_to_le32((u32)len); - mux_header->packet_type = __cpu_to_le16(packet_type_for_tty_index[tty_index]); - - memcpy(t->buf + MUX_HEADER_SIZE, data, len); - memset(t->buf + MUX_HEADER_SIZE + len, 0, - total_len - MUX_HEADER_SIZE - len); - - t->len = total_len; - t->callback = cb; - t->cb_data = cb_data; - - usb_fill_bulk_urb(t->urb, - usbdev, - usb_sndbulkpipe(usbdev, 5), - t->buf, - total_len, - gdm_mux_send_complete, - t); - - ret = usb_submit_urb(t->urb, GFP_ATOMIC); - - spin_unlock_irqrestore(&mux_dev->write_lock, flags); - - if (ret) - pr_err("usb_submit_urb Error: %d\n", ret); - - usb_mark_last_busy(usbdev); - - return ret; -} - -static int gdm_mux_send_control(void *priv_dev, int request, int value, - void *buf, int len) -{ - struct mux_dev *mux_dev = priv_dev; - struct usb_device *usbdev = mux_dev->usbdev; - int ret; - - ret = usb_control_msg(usbdev, - usb_sndctrlpipe(usbdev, 0), - request, - USB_RT_ACM, - value, - 2, - buf, - len, - 5000 - ); - - if (ret < 0) - pr_err("usb_control_msg error: %d\n", ret); - - return min(ret, 0); -} - -static void release_usb(struct mux_dev *mux_dev) -{ - struct rx_cxt *rx = &mux_dev->rx; - struct mux_rx *r, *r_next; - unsigned long flags; - - cancel_delayed_work(&mux_dev->work_rx); - - spin_lock_irqsave(&rx->submit_list_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->rx_submit_list, - rx_submit_list) { - spin_unlock_irqrestore(&rx->submit_list_lock, flags); - usb_kill_urb(r->urb); - spin_lock_irqsave(&rx->submit_list_lock, flags); - } - spin_unlock_irqrestore(&rx->submit_list_lock, flags); - - spin_lock_irqsave(&rx->free_list_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->rx_free_list, free_list) { - list_del(&r->free_list); - free_mux_rx(r); - } - spin_unlock_irqrestore(&rx->free_list_lock, flags); - - spin_lock_irqsave(&rx->to_host_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) { - if (r->mux_dev == (void *)mux_dev) { - list_del(&r->to_host_list); - free_mux_rx(r); - } - } - spin_unlock_irqrestore(&rx->to_host_lock, flags); -} - -static int init_usb(struct mux_dev *mux_dev) -{ - struct mux_rx *r; - struct rx_cxt *rx = &mux_dev->rx; - int ret = 0; - int i; - - spin_lock_init(&mux_dev->write_lock); - INIT_LIST_HEAD(&rx->to_host_list); - INIT_LIST_HEAD(&rx->rx_submit_list); - INIT_LIST_HEAD(&rx->rx_free_list); - spin_lock_init(&rx->to_host_lock); - spin_lock_init(&rx->submit_list_lock); - spin_lock_init(&rx->free_list_lock); - - for (i = 0; i < MAX_ISSUE_NUM * 2; i++) { - r = alloc_mux_rx(); - if (!r) { - ret = -ENOMEM; - break; - } - - list_add(&r->free_list, &rx->rx_free_list); - } - - INIT_DELAYED_WORK(&mux_dev->work_rx, do_rx); - - return ret; -} - -static int gdm_mux_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct mux_dev *mux_dev; - struct tty_dev *tty_dev; - u16 idVendor, idProduct; - int bInterfaceNumber; - int ret; - int i; - struct usb_device *usbdev = interface_to_usbdev(intf); - - bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber; - - idVendor = __le16_to_cpu(usbdev->descriptor.idVendor); - idProduct = __le16_to_cpu(usbdev->descriptor.idProduct); - - pr_info("mux vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct); - - if (bInterfaceNumber != 2) - return -ENODEV; - - mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL); - if (!mux_dev) - return -ENOMEM; - - tty_dev = kzalloc(sizeof(*tty_dev), GFP_KERNEL); - if (!tty_dev) { - ret = -ENOMEM; - goto err_free_mux; - } - - mux_dev->usbdev = usbdev; - mux_dev->control_intf = intf; - - ret = init_usb(mux_dev); - if (ret) - goto err_free_usb; - - tty_dev->priv_dev = (void *)mux_dev; - tty_dev->send_func = gdm_mux_send; - tty_dev->recv_func = gdm_mux_recv; - tty_dev->send_control = gdm_mux_send_control; - - ret = register_lte_tty_device(tty_dev, &intf->dev); - if (ret) - goto err_unregister_tty; - - for (i = 0; i < TTY_MAX_COUNT; i++) - mux_dev->tty_dev = tty_dev; - - mux_dev->intf = intf; - mux_dev->usb_state = PM_NORMAL; - - usb_get_dev(usbdev); - usb_set_intfdata(intf, tty_dev); - - return 0; - -err_unregister_tty: - unregister_lte_tty_device(tty_dev); -err_free_usb: - release_usb(mux_dev); - kfree(tty_dev); -err_free_mux: - kfree(mux_dev); - - return ret; -} - -static void gdm_mux_disconnect(struct usb_interface *intf) -{ - struct tty_dev *tty_dev; - struct mux_dev *mux_dev; - struct usb_device *usbdev = interface_to_usbdev(intf); - - tty_dev = usb_get_intfdata(intf); - - mux_dev = tty_dev->priv_dev; - - release_usb(mux_dev); - unregister_lte_tty_device(tty_dev); - - kfree(mux_dev); - kfree(tty_dev); - - usb_put_dev(usbdev); -} - -static int gdm_mux_suspend(struct usb_interface *intf, pm_message_t pm_msg) -{ - struct tty_dev *tty_dev; - struct mux_dev *mux_dev; - struct rx_cxt *rx; - struct mux_rx *r, *r_next; - unsigned long flags; - - tty_dev = usb_get_intfdata(intf); - mux_dev = tty_dev->priv_dev; - rx = &mux_dev->rx; - - cancel_work_sync(&mux_dev->work_rx.work); - - if (mux_dev->usb_state != PM_NORMAL) { - dev_err(intf->usb_dev, "usb suspend - invalid state\n"); - return -1; - } - - mux_dev->usb_state = PM_SUSPEND; - - spin_lock_irqsave(&rx->submit_list_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->rx_submit_list, - rx_submit_list) { - spin_unlock_irqrestore(&rx->submit_list_lock, flags); - usb_kill_urb(r->urb); - spin_lock_irqsave(&rx->submit_list_lock, flags); - } - spin_unlock_irqrestore(&rx->submit_list_lock, flags); - - return 0; -} - -static int gdm_mux_resume(struct usb_interface *intf) -{ - struct tty_dev *tty_dev; - struct mux_dev *mux_dev; - u8 i; - - tty_dev = usb_get_intfdata(intf); - mux_dev = tty_dev->priv_dev; - - if (mux_dev->usb_state != PM_SUSPEND) { - dev_err(intf->usb_dev, "usb resume - invalid state\n"); - return -1; - } - - mux_dev->usb_state = PM_NORMAL; - - for (i = 0; i < MAX_ISSUE_NUM; i++) - gdm_mux_recv(mux_dev, mux_dev->rx_cb); - - return 0; -} - -static struct usb_driver gdm_mux_driver = { - .name = "gdm_mux", - .probe = gdm_mux_probe, - .disconnect = gdm_mux_disconnect, - .id_table = id_table, - .supports_autosuspend = 1, - .suspend = gdm_mux_suspend, - .resume = gdm_mux_resume, - .reset_resume = gdm_mux_resume, -}; - -static int __init gdm_usb_mux_init(void) -{ - int ret; - - ret = register_lte_tty_driver(); - if (ret) - return ret; - - return usb_register(&gdm_mux_driver); -} - -static void __exit gdm_usb_mux_exit(void) -{ - usb_deregister(&gdm_mux_driver); - unregister_lte_tty_driver(); -} - -module_init(gdm_usb_mux_init); -module_exit(gdm_usb_mux_exit); - -MODULE_DESCRIPTION("GCT LTE TTY Device Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/gdm724x/gdm_mux.h b/drivers/staging/gdm724x/gdm_mux.h deleted file mode 100644 index 87b8d921fdc82..0000000000000 --- a/drivers/staging/gdm724x/gdm_mux.h +++ /dev/null @@ -1,85 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _GDM_MUX_H_ -#define _GDM_MUX_H_ - -#include -#include -#include - -#include "gdm_tty.h" - -#define PM_NORMAL 0 -#define PM_SUSPEND 1 - -#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) - -#define START_FLAG 0xA512485A -#define MUX_HEADER_SIZE 14 -#define MUX_TX_MAX_SIZE (1024 * 10) -#define MUX_RX_MAX_SIZE (1024 * 30) -#define AT_PKT_TYPE 0xF011 -#define DM_PKT_TYPE 0xF010 - -#define RETRY_TIMER 30 /* msec */ - -struct mux_pkt_header { - __le32 start_flag; - __le32 seq_num; - __le32 payload_size; - __le16 packet_type; - unsigned char data[]; -}; - -struct mux_tx { - struct urb *urb; - u8 *buf; - int len; - void (*callback)(void *cb_data); - void *cb_data; -}; - -struct mux_rx { - struct list_head free_list; - struct list_head rx_submit_list; - struct list_head to_host_list; - struct urb *urb; - u8 *buf; - void *mux_dev; - u32 offset; - u32 len; - int (*callback)(void *data, - int len, - int tty_index, - struct tty_dev *tty_dev, - int complete); -}; - -struct rx_cxt { - struct list_head to_host_list; - struct list_head rx_submit_list; - struct list_head rx_free_list; - spinlock_t to_host_lock; - spinlock_t submit_list_lock; - spinlock_t free_list_lock; -}; - -struct mux_dev { - struct usb_device *usbdev; - struct usb_interface *control_intf; - struct usb_interface *data_intf; - struct rx_cxt rx; - struct delayed_work work_rx; - struct usb_interface *intf; - int usb_state; - int (*rx_cb)(void *data, - int len, - int tty_index, - struct tty_dev *tty_dev, - int complete); - spinlock_t write_lock; - struct tty_dev *tty_dev; -}; - -#endif /* _GDM_MUX_H_ */ diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c deleted file mode 100644 index 15c246d3b1a3e..0000000000000 --- a/drivers/staging/gdm724x/gdm_tty.c +++ /dev/null @@ -1,316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gdm_tty.h" - -#define GDM_TTY_MAJOR 0 -#define GDM_TTY_MINOR 32 - -#define WRITE_SIZE 2048 - -#define MUX_TX_MAX_SIZE 2048 - -static inline bool gdm_tty_ready(struct gdm *gdm) -{ - return gdm && gdm->tty_dev && gdm->port.count; -} - -static struct tty_driver *gdm_driver[TTY_MAX_COUNT]; -static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR]; -static DEFINE_MUTEX(gdm_table_lock); - -static const char *DRIVER_STRING[TTY_MAX_COUNT] = {"GCTATC", "GCTDM"}; -static char *DEVICE_STRING[TTY_MAX_COUNT] = {"GCT-ATC", "GCT-DM"}; - -static void gdm_port_destruct(struct tty_port *port) -{ - struct gdm *gdm = container_of(port, struct gdm, port); - - mutex_lock(&gdm_table_lock); - gdm_table[gdm->index][gdm->minor] = NULL; - mutex_unlock(&gdm_table_lock); - - kfree(gdm); -} - -static const struct tty_port_operations gdm_port_ops = { - .destruct = gdm_port_destruct, -}; - -static int gdm_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct gdm *gdm = NULL; - int ret; - - ret = match_string(DRIVER_STRING, TTY_MAX_COUNT, - tty->driver->driver_name); - if (ret < 0) - return -ENODEV; - - mutex_lock(&gdm_table_lock); - gdm = gdm_table[ret][tty->index]; - if (!gdm) { - mutex_unlock(&gdm_table_lock); - return -ENODEV; - } - - tty_port_get(&gdm->port); - - ret = tty_standard_install(driver, tty); - if (ret) { - tty_port_put(&gdm->port); - mutex_unlock(&gdm_table_lock); - return ret; - } - - tty->driver_data = gdm; - mutex_unlock(&gdm_table_lock); - - return 0; -} - -static int gdm_tty_open(struct tty_struct *tty, struct file *filp) -{ - struct gdm *gdm = tty->driver_data; - - return tty_port_open(&gdm->port, tty, filp); -} - -static void gdm_tty_cleanup(struct tty_struct *tty) -{ - struct gdm *gdm = tty->driver_data; - - tty_port_put(&gdm->port); -} - -static void gdm_tty_hangup(struct tty_struct *tty) -{ - struct gdm *gdm = tty->driver_data; - - tty_port_hangup(&gdm->port); -} - -static void gdm_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct gdm *gdm = tty->driver_data; - - tty_port_close(&gdm->port, tty, filp); -} - -static int gdm_tty_recv_complete(void *data, - int len, - int index, - struct tty_dev *tty_dev, - int complete) -{ - struct gdm *gdm = tty_dev->gdm[index]; - - if (!gdm_tty_ready(gdm)) { - if (complete == RECV_PACKET_PROCESS_COMPLETE) - gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev, - gdm_tty_recv_complete); - return TO_HOST_PORT_CLOSE; - } - - if (data && len) { - if (tty_buffer_request_room(&gdm->port, len) == len) { - tty_insert_flip_string(&gdm->port, data, len); - tty_flip_buffer_push(&gdm->port); - } else { - return TO_HOST_BUFFER_REQUEST_FAIL; - } - } - - if (complete == RECV_PACKET_PROCESS_COMPLETE) - gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev, - gdm_tty_recv_complete); - - return 0; -} - -static void gdm_tty_send_complete(void *arg) -{ - struct gdm *gdm = arg; - - if (!gdm_tty_ready(gdm)) - return; - - tty_port_tty_wakeup(&gdm->port); -} - -static ssize_t gdm_tty_write(struct tty_struct *tty, const u8 *buf, size_t len) -{ - struct gdm *gdm = tty->driver_data; - size_t remain = len; - size_t sent_len = 0; - - if (!gdm_tty_ready(gdm)) - return -ENODEV; - - while (remain) { - size_t sending_len = min_t(size_t, MUX_TX_MAX_SIZE, remain); - - gdm->tty_dev->send_func(gdm->tty_dev->priv_dev, - (void *)(buf + sent_len), - sending_len, - gdm->index, - gdm_tty_send_complete, - gdm); - sent_len += sending_len; - remain -= sending_len; - } - - return len; -} - -static unsigned int gdm_tty_write_room(struct tty_struct *tty) -{ - struct gdm *gdm = tty->driver_data; - - if (!gdm_tty_ready(gdm)) - return 0; - - return WRITE_SIZE; -} - -int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device) -{ - struct gdm *gdm; - int i; - int j; - - for (i = 0; i < TTY_MAX_COUNT; i++) { - gdm = kmalloc(sizeof(*gdm), GFP_KERNEL); - if (!gdm) - return -ENOMEM; - - mutex_lock(&gdm_table_lock); - for (j = 0; j < GDM_TTY_MINOR; j++) { - if (!gdm_table[i][j]) - break; - } - - if (j == GDM_TTY_MINOR) { - kfree(gdm); - mutex_unlock(&gdm_table_lock); - return -EINVAL; - } - - gdm_table[i][j] = gdm; - mutex_unlock(&gdm_table_lock); - - tty_dev->gdm[i] = gdm; - tty_port_init(&gdm->port); - - gdm->port.ops = &gdm_port_ops; - gdm->index = i; - gdm->minor = j; - gdm->tty_dev = tty_dev; - - tty_port_register_device(&gdm->port, gdm_driver[i], - gdm->minor, device); - } - - for (i = 0; i < MAX_ISSUE_NUM; i++) - gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev, - gdm_tty_recv_complete); - - return 0; -} - -void unregister_lte_tty_device(struct tty_dev *tty_dev) -{ - struct gdm *gdm; - struct tty_struct *tty; - int i; - - for (i = 0; i < TTY_MAX_COUNT; i++) { - gdm = tty_dev->gdm[i]; - if (!gdm) - continue; - - mutex_lock(&gdm_table_lock); - gdm_table[gdm->index][gdm->minor] = NULL; - mutex_unlock(&gdm_table_lock); - - tty = tty_port_tty_get(&gdm->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - - tty_unregister_device(gdm_driver[i], gdm->minor); - tty_port_put(&gdm->port); - } -} - -static const struct tty_operations gdm_tty_ops = { - .install = gdm_tty_install, - .open = gdm_tty_open, - .close = gdm_tty_close, - .cleanup = gdm_tty_cleanup, - .hangup = gdm_tty_hangup, - .write = gdm_tty_write, - .write_room = gdm_tty_write_room, -}; - -int register_lte_tty_driver(void) -{ - struct tty_driver *tty_driver; - int i; - int ret; - - for (i = 0; i < TTY_MAX_COUNT; i++) { - tty_driver = tty_alloc_driver(GDM_TTY_MINOR, - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(tty_driver)) - return PTR_ERR(tty_driver); - - tty_driver->owner = THIS_MODULE; - tty_driver->driver_name = DRIVER_STRING[i]; - tty_driver->name = DEVICE_STRING[i]; - tty_driver->major = GDM_TTY_MAJOR; - tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - tty_driver->subtype = SERIAL_TYPE_NORMAL; - tty_driver->init_termios = tty_std_termios; - tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL; - tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN; - tty_set_operations(tty_driver, &gdm_tty_ops); - - ret = tty_register_driver(tty_driver); - if (ret) { - tty_driver_kref_put(tty_driver); - return ret; - } - - gdm_driver[i] = tty_driver; - } - - return ret; -} - -void unregister_lte_tty_driver(void) -{ - struct tty_driver *tty_driver; - int i; - - for (i = 0; i < TTY_MAX_COUNT; i++) { - tty_driver = gdm_driver[i]; - if (tty_driver) { - tty_unregister_driver(tty_driver); - tty_driver_kref_put(tty_driver); - } - } -} - diff --git a/drivers/staging/gdm724x/gdm_tty.h b/drivers/staging/gdm724x/gdm_tty.h deleted file mode 100644 index afec97ced4769..0000000000000 --- a/drivers/staging/gdm724x/gdm_tty.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _GDM_TTY_H_ -#define _GDM_TTY_H_ - -#include -#include - -#define TTY_MAX_COUNT 2 - -#define MAX_ISSUE_NUM 3 - -enum TO_HOST_RESULT { - TO_HOST_BUFFER_REQUEST_FAIL = 1, - TO_HOST_PORT_CLOSE = 2, - TO_HOST_INVALID_PACKET = 3, -}; - -enum RECV_PACKET_PROCESS { - RECV_PACKET_PROCESS_COMPLETE = 0, - RECV_PACKET_PROCESS_CONTINUE = 1, -}; - -struct gdm { - struct tty_dev *tty_dev; - struct tty_port port; - unsigned int index; - unsigned int minor; -}; - -struct tty_dev { - void *priv_dev; - int (*send_func)(void *priv_dev, - void *data, - int len, - int tty_index, - void (*cb)(void *cb_data), - void *cb_data); - int (*recv_func)(void *priv_dev, - int (*cb)(void *data, - int len, - int tty_index, - struct tty_dev *tty_dev, - int complete)); - int (*send_control)(void *priv_dev, - int request, - int value, - void *data, - int len); - struct gdm *gdm[2]; -}; - -int register_lte_tty_driver(void); -void unregister_lte_tty_driver(void); -int register_lte_tty_device(struct tty_dev *tty_dev, struct device *dev); -void unregister_lte_tty_device(struct tty_dev *tty_dev); - -#endif /* _GDM_USB_H_ */ - diff --git a/drivers/staging/gdm724x/gdm_usb.c b/drivers/staging/gdm724x/gdm_usb.c deleted file mode 100644 index 54bdb64f52e88..0000000000000 --- a/drivers/staging/gdm724x/gdm_usb.c +++ /dev/null @@ -1,1012 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gdm_usb.h" -#include "gdm_lte.h" -#include "hci.h" -#include "hci_packet.h" -#include "gdm_endian.h" - -#define USB_DEVICE_CDC_DATA(vid, pid) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS | \ - USB_DEVICE_ID_MATCH_INT_SUBCLASS,\ - .idVendor = vid,\ - .idProduct = pid,\ - .bInterfaceClass = USB_CLASS_COMM,\ - .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET - -#define USB_DEVICE_MASS_DATA(vid, pid) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_INFO,\ - .idVendor = vid,\ - .idProduct = pid,\ - .bInterfaceSubClass = USB_SC_SCSI, \ - .bInterfaceClass = USB_CLASS_MASS_STORAGE,\ - .bInterfaceProtocol = USB_PR_BULK - -static const struct usb_device_id id_table[] = { - { USB_DEVICE_CDC_DATA(VID_GCT, PID_GDM7240) }, /* GCT GDM7240 */ - { USB_DEVICE_CDC_DATA(VID_GCT, PID_GDM7243) }, /* GCT GDM7243 */ - { } -}; - -MODULE_DEVICE_TABLE(usb, id_table); - -static void do_tx(struct work_struct *work); -static void do_rx(struct work_struct *work); - -static int gdm_usb_recv(void *priv_dev, - int (*cb)(void *cb_data, - void *data, int len, int context), - void *cb_data, - int context); - -static int request_mac_address(struct lte_udev *udev) -{ - struct hci_packet *hci; - struct usb_device *usbdev = udev->usbdev; - int actual; - int ret = -1; - - hci = kmalloc(struct_size(hci, data, 1), GFP_KERNEL); - if (!hci) - return -ENOMEM; - - hci->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_GET_INFORMATION); - hci->len = gdm_cpu_to_dev16(udev->gdm_ed, 1); - hci->data[0] = MAC_ADDRESS; - - ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), hci, 5, - &actual, 1000); - - udev->request_mac_addr = 1; - kfree(hci); - - return ret; -} - -static struct usb_tx *alloc_tx_struct(int len) -{ - struct usb_tx *t = NULL; - int ret = 0; - - t = kzalloc(sizeof(*t), GFP_ATOMIC); - if (!t) { - ret = -ENOMEM; - goto out; - } - - t->urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!(len % 512)) - len++; - - t->buf = kmalloc(len, GFP_ATOMIC); - if (!t->urb || !t->buf) { - ret = -ENOMEM; - goto out; - } - -out: - if (ret < 0) { - if (t) { - usb_free_urb(t->urb); - kfree(t->buf); - kfree(t); - } - return NULL; - } - - return t; -} - -static struct usb_tx_sdu *alloc_tx_sdu_struct(void) -{ - struct usb_tx_sdu *t_sdu; - - t_sdu = kzalloc(sizeof(*t_sdu), GFP_KERNEL); - if (!t_sdu) - return NULL; - - t_sdu->buf = kmalloc(SDU_BUF_SIZE, GFP_KERNEL); - if (!t_sdu->buf) { - kfree(t_sdu); - return NULL; - } - - return t_sdu; -} - -static void free_tx_struct(struct usb_tx *t) -{ - if (t) { - usb_free_urb(t->urb); - kfree(t->buf); - kfree(t); - } -} - -static void free_tx_sdu_struct(struct usb_tx_sdu *t_sdu) -{ - if (t_sdu) { - kfree(t_sdu->buf); - kfree(t_sdu); - } -} - -static struct usb_tx_sdu *get_tx_sdu_struct(struct tx_cxt *tx, int *no_spc) -{ - struct usb_tx_sdu *t_sdu; - - if (list_empty(&tx->free_list)) - return NULL; - - t_sdu = list_entry(tx->free_list.next, struct usb_tx_sdu, list); - list_del(&t_sdu->list); - - tx->avail_count--; - - *no_spc = list_empty(&tx->free_list) ? 1 : 0; - - return t_sdu; -} - -static void put_tx_struct(struct tx_cxt *tx, struct usb_tx_sdu *t_sdu) -{ - list_add_tail(&t_sdu->list, &tx->free_list); - tx->avail_count++; -} - -static struct usb_rx *alloc_rx_struct(void) -{ - struct usb_rx *r = NULL; - int ret = 0; - - r = kmalloc(sizeof(*r), GFP_KERNEL); - if (!r) { - ret = -ENOMEM; - goto out; - } - - r->urb = usb_alloc_urb(0, GFP_KERNEL); - r->buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL); - if (!r->urb || !r->buf) { - ret = -ENOMEM; - goto out; - } -out: - - if (ret < 0) { - if (r) { - usb_free_urb(r->urb); - kfree(r->buf); - kfree(r); - } - return NULL; - } - - return r; -} - -static void free_rx_struct(struct usb_rx *r) -{ - if (r) { - usb_free_urb(r->urb); - kfree(r->buf); - kfree(r); - } -} - -static struct usb_rx *get_rx_struct(struct rx_cxt *rx, int *no_spc) -{ - struct usb_rx *r; - unsigned long flags; - - spin_lock_irqsave(&rx->rx_lock, flags); - - if (list_empty(&rx->free_list)) { - spin_unlock_irqrestore(&rx->rx_lock, flags); - return NULL; - } - - r = list_entry(rx->free_list.next, struct usb_rx, free_list); - list_del(&r->free_list); - - rx->avail_count--; - - *no_spc = list_empty(&rx->free_list) ? 1 : 0; - - spin_unlock_irqrestore(&rx->rx_lock, flags); - - return r; -} - -static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r) -{ - unsigned long flags; - - spin_lock_irqsave(&rx->rx_lock, flags); - - list_add_tail(&r->free_list, &rx->free_list); - rx->avail_count++; - - spin_unlock_irqrestore(&rx->rx_lock, flags); -} - -static void release_usb(struct lte_udev *udev) -{ - struct rx_cxt *rx = &udev->rx; - struct tx_cxt *tx = &udev->tx; - struct usb_tx *t, *t_next; - struct usb_rx *r, *r_next; - struct usb_tx_sdu *t_sdu, *t_sdu_next; - unsigned long flags; - - spin_lock_irqsave(&tx->lock, flags); - list_for_each_entry_safe(t_sdu, t_sdu_next, &tx->sdu_list, list) { - list_del(&t_sdu->list); - free_tx_sdu_struct(t_sdu); - } - - list_for_each_entry_safe(t, t_next, &tx->hci_list, list) { - list_del(&t->list); - free_tx_struct(t); - } - - list_for_each_entry_safe(t_sdu, t_sdu_next, &tx->free_list, list) { - list_del(&t_sdu->list); - free_tx_sdu_struct(t_sdu); - } - spin_unlock_irqrestore(&tx->lock, flags); - - spin_lock_irqsave(&rx->submit_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->rx_submit_list, - rx_submit_list) { - spin_unlock_irqrestore(&rx->submit_lock, flags); - usb_kill_urb(r->urb); - spin_lock_irqsave(&rx->submit_lock, flags); - } - spin_unlock_irqrestore(&rx->submit_lock, flags); - - spin_lock_irqsave(&rx->rx_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->free_list, free_list) { - list_del(&r->free_list); - free_rx_struct(r); - } - spin_unlock_irqrestore(&rx->rx_lock, flags); - - spin_lock_irqsave(&rx->to_host_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) { - if (r->index == (void *)udev) { - list_del(&r->to_host_list); - free_rx_struct(r); - } - } - spin_unlock_irqrestore(&rx->to_host_lock, flags); -} - -static int init_usb(struct lte_udev *udev) -{ - int ret = 0; - int i; - struct tx_cxt *tx = &udev->tx; - struct rx_cxt *rx = &udev->rx; - struct usb_tx_sdu *t_sdu = NULL; - struct usb_rx *r = NULL; - - udev->send_complete = 1; - udev->tx_stop = 0; - udev->request_mac_addr = 0; - udev->usb_state = PM_NORMAL; - - INIT_LIST_HEAD(&tx->sdu_list); - INIT_LIST_HEAD(&tx->hci_list); - INIT_LIST_HEAD(&tx->free_list); - INIT_LIST_HEAD(&rx->rx_submit_list); - INIT_LIST_HEAD(&rx->free_list); - INIT_LIST_HEAD(&rx->to_host_list); - spin_lock_init(&tx->lock); - spin_lock_init(&rx->rx_lock); - spin_lock_init(&rx->submit_lock); - spin_lock_init(&rx->to_host_lock); - - tx->avail_count = 0; - rx->avail_count = 0; - - udev->rx_cb = NULL; - - for (i = 0; i < MAX_NUM_SDU_BUF; i++) { - t_sdu = alloc_tx_sdu_struct(); - if (!t_sdu) { - ret = -ENOMEM; - goto fail; - } - - list_add(&t_sdu->list, &tx->free_list); - tx->avail_count++; - } - - for (i = 0; i < MAX_RX_SUBMIT_COUNT * 2; i++) { - r = alloc_rx_struct(); - if (!r) { - ret = -ENOMEM; - goto fail; - } - - list_add(&r->free_list, &rx->free_list); - rx->avail_count++; - } - INIT_DELAYED_WORK(&udev->work_tx, do_tx); - INIT_DELAYED_WORK(&udev->work_rx, do_rx); - return 0; -fail: - release_usb(udev); - return ret; -} - -static int set_mac_address(u8 *data, void *arg) -{ - struct phy_dev *phy_dev = arg; - struct lte_udev *udev = phy_dev->priv_dev; - struct tlv *tlv = (struct tlv *)data; - u8 mac_address[ETH_ALEN] = {0, }; - - if (tlv->type == MAC_ADDRESS && udev->request_mac_addr) { - memcpy(mac_address, tlv->data, tlv->len); - - if (register_lte_device(phy_dev, - &udev->intf->dev, mac_address) < 0) - pr_err("register lte device failed\n"); - - udev->request_mac_addr = 0; - - return 1; - } - - return 0; -} - -static void do_rx(struct work_struct *work) -{ - struct lte_udev *udev = - container_of(work, struct lte_udev, work_rx.work); - struct rx_cxt *rx = &udev->rx; - struct usb_rx *r; - struct hci_packet *hci; - struct phy_dev *phy_dev; - u16 cmd_evt; - int ret; - unsigned long flags; - - while (1) { - spin_lock_irqsave(&rx->to_host_lock, flags); - if (list_empty(&rx->to_host_list)) { - spin_unlock_irqrestore(&rx->to_host_lock, flags); - break; - } - r = list_entry(rx->to_host_list.next, - struct usb_rx, to_host_list); - list_del(&r->to_host_list); - spin_unlock_irqrestore(&rx->to_host_lock, flags); - - phy_dev = r->cb_data; - udev = phy_dev->priv_dev; - hci = (struct hci_packet *)r->buf; - cmd_evt = gdm_dev16_to_cpu(udev->gdm_ed, hci->cmd_evt); - - switch (cmd_evt) { - case LTE_GET_INFORMATION_RESULT: - if (set_mac_address(hci->data, r->cb_data) == 0) { - r->callback(r->cb_data, - r->buf, - r->urb->actual_length, - KERNEL_THREAD); - } - break; - - default: - if (r->callback) { - ret = r->callback(r->cb_data, - r->buf, - r->urb->actual_length, - KERNEL_THREAD); - - if (ret == -EAGAIN) - pr_err("failed to send received data\n"); - } - break; - } - - put_rx_struct(rx, r); - - gdm_usb_recv(udev, - r->callback, - r->cb_data, - USB_COMPLETE); - } -} - -static void remove_rx_submit_list(struct usb_rx *r, struct rx_cxt *rx) -{ - unsigned long flags; - struct usb_rx *r_remove, *r_remove_next; - - spin_lock_irqsave(&rx->submit_lock, flags); - list_for_each_entry_safe(r_remove, r_remove_next, - &rx->rx_submit_list, rx_submit_list) { - if (r == r_remove) { - list_del(&r->rx_submit_list); - break; - } - } - spin_unlock_irqrestore(&rx->submit_lock, flags); -} - -static void gdm_usb_rcv_complete(struct urb *urb) -{ - struct usb_rx *r = urb->context; - struct rx_cxt *rx = r->rx; - unsigned long flags; - struct lte_udev *udev = container_of(r->rx, struct lte_udev, rx); - struct usb_device *usbdev = udev->usbdev; - - remove_rx_submit_list(r, rx); - - if (!urb->status && r->callback) { - spin_lock_irqsave(&rx->to_host_lock, flags); - list_add_tail(&r->to_host_list, &rx->to_host_list); - schedule_work(&udev->work_rx.work); - spin_unlock_irqrestore(&rx->to_host_lock, flags); - } else { - if (urb->status && udev->usb_state == PM_NORMAL) - dev_err(&urb->dev->dev, "%s: urb status error %d\n", - __func__, urb->status); - - put_rx_struct(rx, r); - } - - usb_mark_last_busy(usbdev); -} - -static int gdm_usb_recv(void *priv_dev, - int (*cb)(void *cb_data, - void *data, int len, int context), - void *cb_data, - int context) -{ - struct lte_udev *udev = priv_dev; - struct usb_device *usbdev = udev->usbdev; - struct rx_cxt *rx = &udev->rx; - struct usb_rx *r; - int no_spc; - int ret; - unsigned long flags; - - if (!udev->usbdev) { - pr_err("invalid device\n"); - return -ENODEV; - } - - r = get_rx_struct(rx, &no_spc); - if (!r) { - pr_err("Out of Memory\n"); - return -ENOMEM; - } - - udev->rx_cb = cb; - r->callback = cb; - r->cb_data = cb_data; - r->index = (void *)udev; - r->rx = rx; - - usb_fill_bulk_urb(r->urb, - usbdev, - usb_rcvbulkpipe(usbdev, 0x83), - r->buf, - RX_BUF_SIZE, - gdm_usb_rcv_complete, - r); - - spin_lock_irqsave(&rx->submit_lock, flags); - list_add_tail(&r->rx_submit_list, &rx->rx_submit_list); - spin_unlock_irqrestore(&rx->submit_lock, flags); - - if (context == KERNEL_THREAD) - ret = usb_submit_urb(r->urb, GFP_KERNEL); - else - ret = usb_submit_urb(r->urb, GFP_ATOMIC); - - if (ret) { - spin_lock_irqsave(&rx->submit_lock, flags); - list_del(&r->rx_submit_list); - spin_unlock_irqrestore(&rx->submit_lock, flags); - - pr_err("usb_submit_urb failed (%p)\n", r); - put_rx_struct(rx, r); - } - - return ret; -} - -static void gdm_usb_send_complete(struct urb *urb) -{ - struct usb_tx *t = urb->context; - struct tx_cxt *tx = t->tx; - struct lte_udev *udev = container_of(tx, struct lte_udev, tx); - unsigned long flags; - - if (urb->status == -ECONNRESET) { - dev_info(&urb->dev->dev, "CONNRESET\n"); - return; - } - - if (t->callback) - t->callback(t->cb_data); - - free_tx_struct(t); - - spin_lock_irqsave(&tx->lock, flags); - udev->send_complete = 1; - schedule_work(&udev->work_tx.work); - spin_unlock_irqrestore(&tx->lock, flags); -} - -static int send_tx_packet(struct usb_device *usbdev, struct usb_tx *t, u32 len) -{ - int ret = 0; - - if (!(len % 512)) - len++; - - usb_fill_bulk_urb(t->urb, - usbdev, - usb_sndbulkpipe(usbdev, 2), - t->buf, - len, - gdm_usb_send_complete, - t); - - ret = usb_submit_urb(t->urb, GFP_ATOMIC); - - if (ret) - dev_err(&usbdev->dev, "usb_submit_urb failed: %d\n", - ret); - - usb_mark_last_busy(usbdev); - - return ret; -} - -static u32 packet_aggregation(struct lte_udev *udev, u8 *send_buf) -{ - struct tx_cxt *tx = &udev->tx; - struct usb_tx_sdu *t_sdu = NULL; - struct multi_sdu *multi_sdu = (struct multi_sdu *)send_buf; - u16 send_len = 0; - u16 num_packet = 0; - unsigned long flags; - - multi_sdu->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_TX_MULTI_SDU); - - while (num_packet < MAX_PACKET_IN_MULTI_SDU) { - spin_lock_irqsave(&tx->lock, flags); - if (list_empty(&tx->sdu_list)) { - spin_unlock_irqrestore(&tx->lock, flags); - break; - } - - t_sdu = list_entry(tx->sdu_list.next, struct usb_tx_sdu, list); - if (send_len + t_sdu->len > MAX_SDU_SIZE) { - spin_unlock_irqrestore(&tx->lock, flags); - break; - } - - list_del(&t_sdu->list); - spin_unlock_irqrestore(&tx->lock, flags); - - memcpy(multi_sdu->data + send_len, t_sdu->buf, t_sdu->len); - - send_len += (t_sdu->len + 3) & 0xfffc; - num_packet++; - - if (tx->avail_count > 10) - t_sdu->callback(t_sdu->cb_data); - - spin_lock_irqsave(&tx->lock, flags); - put_tx_struct(tx, t_sdu); - spin_unlock_irqrestore(&tx->lock, flags); - } - - multi_sdu->len = gdm_cpu_to_dev16(udev->gdm_ed, send_len); - multi_sdu->num_packet = gdm_cpu_to_dev16(udev->gdm_ed, num_packet); - - return send_len + offsetof(struct multi_sdu, data); -} - -static void do_tx(struct work_struct *work) -{ - struct lte_udev *udev = - container_of(work, struct lte_udev, work_tx.work); - struct usb_device *usbdev = udev->usbdev; - struct tx_cxt *tx = &udev->tx; - struct usb_tx *t = NULL; - int is_send = 0; - u32 len = 0; - unsigned long flags; - - if (!usb_autopm_get_interface(udev->intf)) - usb_autopm_put_interface(udev->intf); - - if (udev->usb_state == PM_SUSPEND) - return; - - spin_lock_irqsave(&tx->lock, flags); - if (!udev->send_complete) { - spin_unlock_irqrestore(&tx->lock, flags); - return; - } - udev->send_complete = 0; - - if (!list_empty(&tx->hci_list)) { - t = list_entry(tx->hci_list.next, struct usb_tx, list); - list_del(&t->list); - len = t->len; - t->is_sdu = 0; - is_send = 1; - } else if (!list_empty(&tx->sdu_list)) { - if (udev->tx_stop) { - udev->send_complete = 1; - spin_unlock_irqrestore(&tx->lock, flags); - return; - } - - t = alloc_tx_struct(TX_BUF_SIZE); - if (!t) { - spin_unlock_irqrestore(&tx->lock, flags); - return; - } - t->callback = NULL; - t->tx = tx; - t->is_sdu = 1; - is_send = 1; - } - - if (!is_send) { - udev->send_complete = 1; - spin_unlock_irqrestore(&tx->lock, flags); - return; - } - spin_unlock_irqrestore(&tx->lock, flags); - - if (t->is_sdu) - len = packet_aggregation(udev, t->buf); - - if (send_tx_packet(usbdev, t, len)) { - pr_err("send_tx_packet failed\n"); - t->callback = NULL; - gdm_usb_send_complete(t->urb); - } -} - -#define SDU_PARAM_LEN 12 -static int gdm_usb_sdu_send(void *priv_dev, void *data, int len, - unsigned int dft_eps_ID, unsigned int eps_ID, - void (*cb)(void *data), void *cb_data, - int dev_idx, int nic_type) -{ - struct lte_udev *udev = priv_dev; - struct tx_cxt *tx = &udev->tx; - struct usb_tx_sdu *t_sdu; - struct sdu *sdu = NULL; - unsigned long flags; - int no_spc = 0; - u16 send_len; - - if (!udev->usbdev) { - pr_err("sdu send - invalid device\n"); - return TX_NO_DEV; - } - - spin_lock_irqsave(&tx->lock, flags); - t_sdu = get_tx_sdu_struct(tx, &no_spc); - spin_unlock_irqrestore(&tx->lock, flags); - - if (!t_sdu) { - pr_err("sdu send - free list empty\n"); - return TX_NO_SPC; - } - - sdu = (struct sdu *)t_sdu->buf; - sdu->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_TX_SDU); - if (nic_type == NIC_TYPE_ARP) { - send_len = len + SDU_PARAM_LEN; - memcpy(sdu->data, data, len); - } else { - send_len = len - ETH_HLEN; - send_len += SDU_PARAM_LEN; - memcpy(sdu->data, data + ETH_HLEN, len - ETH_HLEN); - } - - sdu->len = gdm_cpu_to_dev16(udev->gdm_ed, send_len); - sdu->dft_eps_ID = gdm_cpu_to_dev32(udev->gdm_ed, dft_eps_ID); - sdu->bearer_ID = gdm_cpu_to_dev32(udev->gdm_ed, eps_ID); - sdu->nic_type = gdm_cpu_to_dev32(udev->gdm_ed, nic_type); - - t_sdu->len = send_len + HCI_HEADER_SIZE; - t_sdu->callback = cb; - t_sdu->cb_data = cb_data; - - spin_lock_irqsave(&tx->lock, flags); - list_add_tail(&t_sdu->list, &tx->sdu_list); - schedule_work(&udev->work_tx.work); - spin_unlock_irqrestore(&tx->lock, flags); - - if (no_spc) - return TX_NO_BUFFER; - - return 0; -} - -static int gdm_usb_hci_send(void *priv_dev, void *data, int len, - void (*cb)(void *data), void *cb_data) -{ - struct lte_udev *udev = priv_dev; - struct tx_cxt *tx = &udev->tx; - struct usb_tx *t; - unsigned long flags; - - if (!udev->usbdev) { - pr_err("hci send - invalid device\n"); - return -ENODEV; - } - - t = alloc_tx_struct(len); - if (!t) { - pr_err("hci_send - out of memory\n"); - return -ENOMEM; - } - - memcpy(t->buf, data, len); - t->callback = cb; - t->cb_data = cb_data; - t->len = len; - t->tx = tx; - t->is_sdu = 0; - - spin_lock_irqsave(&tx->lock, flags); - list_add_tail(&t->list, &tx->hci_list); - schedule_work(&udev->work_tx.work); - spin_unlock_irqrestore(&tx->lock, flags); - - return 0; -} - -static u8 gdm_usb_get_endian(void *priv_dev) -{ - struct lte_udev *udev = priv_dev; - - return udev->gdm_ed; -} - -static int gdm_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - int ret = 0; - struct phy_dev *phy_dev = NULL; - struct lte_udev *udev = NULL; - u16 idVendor, idProduct; - int bInterfaceNumber; - struct usb_device *usbdev = interface_to_usbdev(intf); - - bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber; - idVendor = __le16_to_cpu(usbdev->descriptor.idVendor); - idProduct = __le16_to_cpu(usbdev->descriptor.idProduct); - - pr_info("net vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct); - - if (bInterfaceNumber > NETWORK_INTERFACE) { - pr_info("not a network device\n"); - return -ENODEV; - } - - phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL); - if (!phy_dev) - return -ENOMEM; - - udev = kzalloc(sizeof(*udev), GFP_KERNEL); - if (!udev) { - ret = -ENOMEM; - goto err_udev; - } - - phy_dev->priv_dev = (void *)udev; - phy_dev->send_hci_func = gdm_usb_hci_send; - phy_dev->send_sdu_func = gdm_usb_sdu_send; - phy_dev->rcv_func = gdm_usb_recv; - phy_dev->get_endian = gdm_usb_get_endian; - - udev->usbdev = usbdev; - ret = init_usb(udev); - if (ret < 0) { - dev_err(intf->usb_dev, "init_usb func failed\n"); - goto err_init_usb; - } - udev->intf = intf; - - intf->needs_remote_wakeup = 1; - usb_enable_autosuspend(usbdev); - pm_runtime_set_autosuspend_delay(&usbdev->dev, AUTO_SUSPEND_TIMER); - - /* List up hosts with big endians, otherwise, - * defaults to little endian - */ - if (idProduct == PID_GDM7243) - udev->gdm_ed = ENDIANNESS_BIG; - else - udev->gdm_ed = ENDIANNESS_LITTLE; - - ret = request_mac_address(udev); - if (ret < 0) { - dev_err(intf->usb_dev, "request Mac address failed\n"); - goto err_mac_address; - } - - start_rx_proc(phy_dev); - usb_get_dev(usbdev); - usb_set_intfdata(intf, phy_dev); - - return 0; - -err_mac_address: - release_usb(udev); -err_init_usb: - kfree(udev); -err_udev: - kfree(phy_dev); - - return ret; -} - -static void gdm_usb_disconnect(struct usb_interface *intf) -{ - struct phy_dev *phy_dev; - struct lte_udev *udev; - struct usb_device *usbdev; - - usbdev = interface_to_usbdev(intf); - phy_dev = usb_get_intfdata(intf); - - udev = phy_dev->priv_dev; - unregister_lte_device(phy_dev); - - release_usb(udev); - - kfree(udev); - udev = NULL; - - kfree(phy_dev); - phy_dev = NULL; - - usb_put_dev(usbdev); -} - -static int gdm_usb_suspend(struct usb_interface *intf, pm_message_t pm_msg) -{ - struct phy_dev *phy_dev; - struct lte_udev *udev; - struct rx_cxt *rx; - struct usb_rx *r; - struct usb_rx *r_next; - unsigned long flags; - - phy_dev = usb_get_intfdata(intf); - udev = phy_dev->priv_dev; - rx = &udev->rx; - if (udev->usb_state != PM_NORMAL) { - dev_err(intf->usb_dev, "usb suspend - invalid state\n"); - return -1; - } - - udev->usb_state = PM_SUSPEND; - - spin_lock_irqsave(&rx->submit_lock, flags); - list_for_each_entry_safe(r, r_next, &rx->rx_submit_list, - rx_submit_list) { - spin_unlock_irqrestore(&rx->submit_lock, flags); - usb_kill_urb(r->urb); - spin_lock_irqsave(&rx->submit_lock, flags); - } - spin_unlock_irqrestore(&rx->submit_lock, flags); - - cancel_work_sync(&udev->work_tx.work); - cancel_work_sync(&udev->work_rx.work); - - return 0; -} - -static int gdm_usb_resume(struct usb_interface *intf) -{ - struct phy_dev *phy_dev; - struct lte_udev *udev; - struct tx_cxt *tx; - struct rx_cxt *rx; - unsigned long flags; - int issue_count; - int i; - - phy_dev = usb_get_intfdata(intf); - udev = phy_dev->priv_dev; - rx = &udev->rx; - - if (udev->usb_state != PM_SUSPEND) { - dev_err(intf->usb_dev, "usb resume - invalid state\n"); - return -1; - } - udev->usb_state = PM_NORMAL; - - spin_lock_irqsave(&rx->rx_lock, flags); - issue_count = rx->avail_count - MAX_RX_SUBMIT_COUNT; - spin_unlock_irqrestore(&rx->rx_lock, flags); - - if (issue_count >= 0) { - for (i = 0; i < issue_count; i++) - gdm_usb_recv(phy_dev->priv_dev, - udev->rx_cb, - phy_dev, - USB_COMPLETE); - } - - tx = &udev->tx; - spin_lock_irqsave(&tx->lock, flags); - schedule_work(&udev->work_tx.work); - spin_unlock_irqrestore(&tx->lock, flags); - - return 0; -} - -static struct usb_driver gdm_usb_lte_driver = { - .name = "gdm_lte", - .probe = gdm_usb_probe, - .disconnect = gdm_usb_disconnect, - .id_table = id_table, - .supports_autosuspend = 1, - .suspend = gdm_usb_suspend, - .resume = gdm_usb_resume, - .reset_resume = gdm_usb_resume, -}; - -static int __init gdm_usb_lte_init(void) -{ - if (gdm_lte_event_init() < 0) { - pr_err("error creating event\n"); - return -1; - } - - return usb_register(&gdm_usb_lte_driver); -} - -static void __exit gdm_usb_lte_exit(void) -{ - gdm_lte_event_exit(); - - usb_deregister(&gdm_usb_lte_driver); -} - -module_init(gdm_usb_lte_init); -module_exit(gdm_usb_lte_exit); - -MODULE_VERSION(DRIVER_VERSION); -MODULE_DESCRIPTION("GCT LTE USB Device Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/gdm724x/gdm_usb.h b/drivers/staging/gdm724x/gdm_usb.h deleted file mode 100644 index db689b091c4fd..0000000000000 --- a/drivers/staging/gdm724x/gdm_usb.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _GDM_USB_H_ -#define _GDM_USB_H_ - -#include -#include -#include -#include - -#include "gdm_endian.h" -#include "hci_packet.h" - -#define PM_NORMAL 0 -#define PM_SUSPEND 1 -#define AUTO_SUSPEND_TIMER 5000 /* ms */ - -#define RX_BUF_SIZE (1024 * 32) -#define TX_BUF_SIZE (1024 * 32) -#define SDU_BUF_SIZE 2048 -#define MAX_SDU_SIZE (1024 * 30) -#define MAX_PACKET_IN_MULTI_SDU 256 - -#define VID_GCT 0x1076 -#define PID_GDM7240 0x8000 -#define PID_GDM7243 0x9000 - -#define NETWORK_INTERFACE 1 -#define USB_SC_SCSI 0x06 -#define USB_PR_BULK 0x50 - -#define MAX_NUM_SDU_BUF 64 - -struct usb_tx { - struct list_head list; - struct urb *urb; - u8 *buf; - u32 len; - void (*callback)(void *cb_data); - void *cb_data; - struct tx_cxt *tx; - u8 is_sdu; -}; - -struct usb_tx_sdu { - struct list_head list; - u8 *buf; - u32 len; - void (*callback)(void *cb_data); - void *cb_data; -}; - -struct usb_rx { - struct list_head to_host_list; - struct list_head free_list; - struct list_head rx_submit_list; - struct rx_cxt *rx; - struct urb *urb; - u8 *buf; - int (*callback)(void *cb_data, void *data, int len, int context); - void *cb_data; - void *index; -}; - -struct tx_cxt { - struct list_head sdu_list; - struct list_head hci_list; - struct list_head free_list; - u32 avail_count; - spinlock_t lock; -}; - -struct rx_cxt { - struct list_head to_host_list; - struct list_head rx_submit_list; - struct list_head free_list; - u32 avail_count; - spinlock_t to_host_lock; - spinlock_t rx_lock; - spinlock_t submit_lock; -}; - -struct lte_udev { - struct usb_device *usbdev; - struct tx_cxt tx; - struct rx_cxt rx; - struct delayed_work work_tx; - struct delayed_work work_rx; - u8 gdm_ed; - u8 send_complete; - u8 tx_stop; - struct usb_interface *intf; - int (*rx_cb)(void *cb_data, void *data, int len, int context); - int usb_state; - u8 request_mac_addr; -}; - -#endif /* _GDM_USB_H_ */ diff --git a/drivers/staging/gdm724x/hci.h b/drivers/staging/gdm724x/hci.h deleted file mode 100644 index b30945daf3a53..0000000000000 --- a/drivers/staging/gdm724x/hci.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _HCI_H_ -#define _HCI_H_ - -#define LTE_GET_INFORMATION 0x3002 -#define LTE_GET_INFORMATION_RESULT 0xB003 - #define MAC_ADDRESS 0xA2 - -#define LTE_LINK_ON_OFF_INDICATION 0xB133 -#define LTE_PDN_TABLE_IND 0xB143 - -#define LTE_TX_SDU 0x3200 -#define LTE_RX_SDU 0xB201 -#define LTE_TX_MULTI_SDU 0x3202 -#define LTE_RX_MULTI_SDU 0xB203 - -#define LTE_DL_SDU_FLOW_CONTROL 0x3305 -#define LTE_UL_SDU_FLOW_CONTROL 0xB306 - -#define LTE_AT_CMD_TO_DEVICE 0x3307 -#define LTE_AT_CMD_FROM_DEVICE 0xB308 - -#define LTE_SDIO_DM_SEND_PKT 0x3312 -#define LTE_SDIO_DM_RECV_PKT 0xB313 - -#define LTE_NV_RESTORE_REQUEST 0xB30C -#define LTE_NV_RESTORE_RESPONSE 0x330D -#define LTE_NV_SAVE_REQUEST 0xB30E - #define NV_TYPE_LTE_INFO 0x00 - #define NV_TYPE_BOARD_CONFIG 0x01 - #define NV_TYPE_RF_CAL 0x02 - #define NV_TYPE_TEMP 0x03 - #define NV_TYPE_NET_INFO 0x04 - #define NV_TYPE_SAFETY_INFO 0x05 - #define NV_TYPE_CDMA_CAL 0x06 - #define NV_TYPE_VENDOR 0x07 - #define NV_TYPE_ALL 0xff -#define LTE_NV_SAVE_RESPONSE 0x330F - -#define LTE_AT_CMD_TO_DEVICE_EXT 0x3323 -#define LTE_AT_CMD_FROM_DEVICE_EXT 0xB324 - -#endif /* _HCI_H_ */ diff --git a/drivers/staging/gdm724x/hci_packet.h b/drivers/staging/gdm724x/hci_packet.h deleted file mode 100644 index 3bb01e94f3b57..0000000000000 --- a/drivers/staging/gdm724x/hci_packet.h +++ /dev/null @@ -1,82 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _HCI_PACKET_H_ -#define _HCI_PACKET_H_ - -#define HCI_HEADER_SIZE 4 - -/* - * The NIC type definition: - * For backward compatibility, lower 16 bits used as they were. - * Lower 16 bit: NIC_TYPE values - * Uppoer 16 bit: NIC_TYPE Flags - */ -#define NIC_TYPE_NIC0 0x00000010 -#define NIC_TYPE_NIC1 0x00000011 -#define NIC_TYPE_NIC2 0x00000012 -#define NIC_TYPE_NIC3 0x00000013 -#define NIC_TYPE_ARP 0x00000100 -#define NIC_TYPE_ICMPV6 0x00000200 -#define NIC_TYPE_MASK 0x0000FFFF -#define NIC_TYPE_F_IPV4 0x00010000 -#define NIC_TYPE_F_IPV6 0x00020000 -#define NIC_TYPE_F_DHCP 0x00040000 -#define NIC_TYPE_F_NDP 0x00080000 -#define NIC_TYPE_F_VLAN 0x00100000 - -struct hci_packet { - __dev16 cmd_evt; - __dev16 len; - u8 data[]; -} __packed; - -struct tlv { - u8 type; - u8 len; - u8 *data[]; -} __packed; - -struct sdu_header { - __dev16 cmd_evt; - __dev16 len; - __dev32 dft_eps_id; - __dev32 bearer_ID; - __dev32 nic_type; -} __packed; - -struct sdu { - __dev16 cmd_evt; - __dev16 len; - __dev32 dft_eps_ID; - __dev32 bearer_ID; - __dev32 nic_type; - u8 data[]; -} __packed; - -struct multi_sdu { - __dev16 cmd_evt; - __dev16 len; - __dev16 num_packet; - __dev16 reserved; - u8 data[]; -} __packed; - -struct hci_pdn_table_ind { - __dev16 cmd_evt; - __dev16 len; - u8 activate; - __dev32 dft_eps_id; - __dev32 nic_type; - u8 pdn_type; - u8 ipv4_addr[4]; - u8 ipv6_intf_id[8]; -} __packed; - -struct hci_connect_ind { - __dev16 cmd_evt; - __dev16 len; - __dev32 connect; -} __packed; - -#endif /* _HCI_PACKET_H_ */ diff --git a/drivers/staging/gdm724x/netlink_k.c b/drivers/staging/gdm724x/netlink_k.c deleted file mode 100644 index 8f39cc5617aad..0000000000000 --- a/drivers/staging/gdm724x/netlink_k.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "netlink_k.h" - -static DEFINE_MUTEX(netlink_mutex); - -#define ND_MAX_GROUP 30 -#define ND_IFINDEX_LEN sizeof(int) -#define ND_NLMSG_SPACE(len) (NLMSG_SPACE(len) + ND_IFINDEX_LEN) -#define ND_NLMSG_DATA(nlh) ((void *)((char *)NLMSG_DATA(nlh) + \ - ND_IFINDEX_LEN)) -#define ND_NLMSG_S_LEN(len) ((len) + ND_IFINDEX_LEN) -#define ND_NLMSG_R_LEN(nlh) ((nlh)->nlmsg_len - ND_IFINDEX_LEN) -#define ND_NLMSG_IFIDX(nlh) NLMSG_DATA(nlh) -#define ND_MAX_MSG_LEN (1024 * 32) - -static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len); - -static void netlink_rcv_cb(struct sk_buff *skb) -{ - struct nlmsghdr *nlh; - struct net_device *dev; - u32 mlen; - void *msg; - int ifindex; - - if (!rcv_cb) { - pr_err("nl cb - unregistered\n"); - return; - } - - if (skb->len < NLMSG_HDRLEN) { - pr_err("nl cb - invalid skb length\n"); - return; - } - - nlh = (struct nlmsghdr *)skb->data; - - if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) { - pr_err("nl cb - invalid length (%d,%d)\n", - skb->len, nlh->nlmsg_len); - return; - } - - memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN); - msg = ND_NLMSG_DATA(nlh); - mlen = ND_NLMSG_R_LEN(nlh); - - dev = dev_get_by_index(&init_net, ifindex); - if (dev) { - rcv_cb(dev, nlh->nlmsg_type, msg, mlen); - dev_put(dev); - } else { - pr_err("nl cb - dev (%d) not found\n", ifindex); - } -} - -static void netlink_rcv(struct sk_buff *skb) -{ - mutex_lock(&netlink_mutex); - netlink_rcv_cb(skb); - mutex_unlock(&netlink_mutex); -} - -struct sock *netlink_init(int unit, - void (*cb)(struct net_device *dev, u16 type, - void *msg, int len)) -{ - struct sock *sock; - struct netlink_kernel_cfg cfg = { - .input = netlink_rcv, - }; - - sock = netlink_kernel_create(&init_net, unit, &cfg); - - if (sock) - rcv_cb = cb; - - return sock; -} - -int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len, - struct net_device *dev) -{ - static u32 seq; - struct sk_buff *skb = NULL; - struct nlmsghdr *nlh; - int ret = 0; - - if (group > ND_MAX_GROUP) - return -EINVAL; - - if (!netlink_has_listeners(sock, group + 1)) - return -ESRCH; - - skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - seq++; - - nlh = nlmsg_put(skb, 0, seq, type, len, 0); - memcpy(NLMSG_DATA(nlh), msg, len); - NETLINK_CB(skb).portid = 0; - NETLINK_CB(skb).dst_group = 0; - - ret = netlink_broadcast(sock, skb, 0, group + 1, GFP_ATOMIC); - if (!ret) - return len; - - if (ret != -ESRCH) - netdev_err(dev, "nl broadcast g=%d, t=%d, l=%d, r=%d\n", - group, type, len, ret); - else if (netlink_has_listeners(sock, group + 1)) - return -EAGAIN; - - return ret; -} diff --git a/drivers/staging/gdm724x/netlink_k.h b/drivers/staging/gdm724x/netlink_k.h deleted file mode 100644 index d42eea9bea3ec..0000000000000 --- a/drivers/staging/gdm724x/netlink_k.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ - -#ifndef _NETLINK_K_H -#define _NETLINK_K_H - -#include -#include - -struct sock *netlink_init(int unit, - void (*cb)(struct net_device *dev, - u16 type, void *msg, int len)); -int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len, - struct net_device *dev); - -#endif /* _NETLINK_K_H_ */ diff --git a/drivers/staging/gpib/Kconfig b/drivers/staging/gpib/Kconfig new file mode 100644 index 0000000000000..95308d15a5551 --- /dev/null +++ b/drivers/staging/gpib/Kconfig @@ -0,0 +1,251 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig GPIB + tristate "Linux GPIB drivers" + help + Enable support for GPIB cards and dongles for Linux. GPIB + is the General Purpose Interface Bus which conforms to the + IEEE488 standard. + + This set of drivers can be used with the corresponding user + space library that can be found on Sourceforge under linux-gpib. + Select the drivers for your hardware from the list. + +if GPIB + +config GPIB_COMMON + tristate "GPIB core" + help + + Core common driver for all GPIB drivers. It provides the + interface for the userland library + + To compile this driver as a module, choose M here: the module will be + called gpib_common + +config GPIB_AGILENT_82350B + tristate "Agilent 8235xx PCI(e) adapters" + depends on PCI + select GPIB_COMMON + select GPIB_TMS9914 + help + Enable support for HP/Agilent/Keysight boards + 82350A + 82350B + 82351A + + To compile this driver as a module, choose M here: the module will be + called agilent_82350b. + +config GPIB_AGILENT_82357A + tristate "Agilent 82357a/b USB dongles" + select GPIB_COMMON + depends on USB + help + Enable support for Agilent/Keysight 82357x USB dongles. + + To compile this driver as a module, choose M here: the module will be + called agilent_82357a. + +config GPIB_CEC_PCI + tristate "CEC PCI board" + depends on PCI + depends on HAS_IOPORT + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for Capital Equipment Corporation PCI-488 + and Keithly KPCI-488 boards. + + To compile this driver as a module, choose M here: the module will be + called cec_gpib. + +config GPIB_NI_PCI_ISA + tristate "NI PCI/ISA compatible boards" + depends on ISA_BUS || PCI || PCMCIA + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for National Instruments boards based + on TNT4882 chips: + AT-GPIB (with NAT4882 chip) + AT-GPIB (with NEC7210 chip) + AT-GPIB/TNT + PCI-GPIB + PCIe-GPIB + PCI-GPIB+ + PCM-GPIB + PXI-GPIB + PCMCIA-GPIB + and Capital Equipment Corporation CEC-488 board. + + To compile this driver as a module, choose M here: the module will be + called tnt4882. + +config GPIB_CB7210 + tristate "Measurement Computing compatible boards" + depends on HAS_IOPORT + depends on ISA_BUS || PCI || PCMCIA + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for Measurement Computing (Computer Boards): + CPCI_GPIB, ISA-GPIB, ISA-GPIB/LC, PCI-GPIB/1M, PCI-GPIB/300K and + PCMCIA-GPIB + Quancom PCIGPIB-1 with MC cb7210 chip + + To compile this driver as a module, choose M here: the module will be + +config GPIB_NI_USB + tristate "NI USB dongles" + select GPIB_COMMON + depends on USB + help + Enable support for National Instruments + GPIB-USB-B + GPIB-USB-HS + GPIB-USB-HS+ + Keithly + KUSB-488 + KUSB-488A + Measurement Computing (Computer Boards) + USB-488 + + To compile this driver as a module, choose M here: the module will be + called ni_usb. + +config GPIB_FLUKE + tristate "Fluke" + depends on OF + select GPIB_COMMON + select GPIB_NEC7210 + help + GPIB driver for Fluke based cda devices. + + To compile this driver as a module, choose M here: the module will be + called fluke_gpib + +config GPIB_FMH + tristate "FMH FPGA based devices" + select GPIB_COMMON + select GPIB_NEC7210 + depends on BROKEN + depends on OF && PCI + help + GPIB driver for fmhess FPGA based devices + + To compile this driver as a module, choose M here: the module will be + called fmh_gpib + +config GPIB_GPIO + tristate "RPi GPIO bitbang" + depends on ARCH_BCM2835 || COMPILE_TEST + select GPIB_COMMON + help + GPIB bitbang driver Raspberry Pi GPIO adapters + + To compile this driver as a module, choose M here: the module will be + called gpib_bitbang + +config GPIB_HP82335 + tristate "HP82335/HP27209" + depends on ISA_BUS + select GPIB_COMMON + select GPIB_TMS9914 + help + GPIB driver for HP82335 and HP27209 boards + + To compile this driver as a module, choose M here: the module will be + called hp82335 + + +config GPIB_HP82341 + tristate "HP82341x" + select GPIB_COMMON + select GPIB_TMS9914 + depends on BROKEN + depends on ISA_BUS || EISA + help + GPIB driver for HP82341 A/B/C/D boards + + To compile this driver as a module, choose M here: the module will be + called hp82341 + +config GPIB_INES + tristate "INES" + depends on PCI || ISA_BUS || PCMCIA + depends on HAS_IOPORT + select GPIB_COMMON + select GPIB_NEC7210 + help + GPIB driver for Ines compatible boards + Ines + GPIB-HS-NT + GPIB for Compact PCI + GPIB for PCI + GPIB for PCMCIA + GPIB PC/104 + Hameg + HO80-2 + Quancom + PCIGPIB-1 based on Ines iGPIB 72010 chip + + To compile this driver as a module, choose M here: the module will be + called ines_gpib + called cb7210. + +config GPIB_PCMCIA + bool "PCMCIA/Cardbus support for NI MC and Ines boards" + depends on PCCARD && (GPIB_NI_PCI_ISA || GPIB_CB7210 || GPIB_INES) + help + Enable PCMCIA/CArdbus support for National Instruments, + measurement computing boards and Ines boards. + +config GPIB_LPVO + tristate "LPVO DIY USB GPIB" + select GPIB_COMMON + depends on USB + help + Enable support for LPVO Self-made usb-gpib adapter + + To compile this driver as a module, choose M here: the module will be + called lpvo_usb_gpib + +config GPIB_PC2 + tristate "PC2 PC2a" + depends on ISA_BUS + depends on HAS_IOPORT + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for pc2 and pc2a compatible adapters + Capital Equipment Corporation PC-488 + CONTEC GP-IB(PC) + Hameg HO80 + Iotech GP488B + Keithly MBC-488 + Measurement Computing ISA-GPIB-PCA2 + National Instruments PCII, PCIIa and PCII/IIa + + To compile this driver as a module, choose M here: the module will be + called pc2_gpib + + +config GPIB_TMS9914 + tristate + select GPIB_COMMON + help + Enable support for TMS 9914 chip. + + To compile this driver as a module, choose M here: the module will be + called tms9914 + +config GPIB_NEC7210 + tristate + select GPIB_COMMON + help + Enable support for NEC 7210 compatible chips. + + To compile this driver as a module, choose M here: the module will be + called nec7210 + +endif # GPIB diff --git a/drivers/staging/gpib/Makefile b/drivers/staging/gpib/Makefile new file mode 100644 index 0000000000000..d0e88f5c08444 --- /dev/null +++ b/drivers/staging/gpib/Makefile @@ -0,0 +1,20 @@ + +subdir-ccflags-y += -I$(src)/include -I$(src)/uapi + +obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b/ +obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a/ +obj-$(CONFIG_GPIB_CB7210) += cb7210/ +obj-$(CONFIG_GPIB_CEC_PCI) += cec/ +obj-$(CONFIG_GPIB_COMMON) += common/ +obj-$(CONFIG_GPIB_FLUKE) += eastwood/ +obj-$(CONFIG_GPIB_FMH) += fmh_gpib/ +obj-$(CONFIG_GPIB_GPIO) += gpio/ +obj-$(CONFIG_GPIB_HP82335) += hp_82335/ +obj-$(CONFIG_GPIB_HP82341) += hp_82341/ +obj-$(CONFIG_GPIB_INES) += ines/ +obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib/ +obj-$(CONFIG_GPIB_NEC7210) += nec7210/ +obj-$(CONFIG_GPIB_NI_USB) += ni_usb/ +obj-$(CONFIG_GPIB_PC2) += pc2/ +obj-$(CONFIG_GPIB_TMS9914) += tms9914/ +obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882/ diff --git a/drivers/staging/gpib/TODO b/drivers/staging/gpib/TODO new file mode 100644 index 0000000000000..bf2c397425487 --- /dev/null +++ b/drivers/staging/gpib/TODO @@ -0,0 +1,21 @@ +TODO: +- checkpatch.pl fixes +- fix device drivers that are broken ("depends on BROKEN" in Kconfig) +- tidy-up comments: + - there are some "//comments" and "// comments" scattered around + - sometimes they are misaligned + - sometimes "// comments" are interleaved with "/* comments */" + - multiline comments should start with initial almost-blank line: + /* + * Good + * multiline + * comment + */ + /* Bad + * multiline + * comment + */ +- resolve XXX notes where possible +- fix FIXME notes +- clean-up commented-out code +- fix typos diff --git a/drivers/staging/gpib/agilent_82350b/Makefile b/drivers/staging/gpib/agilent_82350b/Makefile new file mode 100644 index 0000000000000..d9236c92e04be --- /dev/null +++ b/drivers/staging/gpib/agilent_82350b/Makefile @@ -0,0 +1,2 @@ + +obj-m += agilent_82350b.o diff --git a/drivers/staging/gpib/agilent_82350b/agilent_82350b.c b/drivers/staging/gpib/agilent_82350b/agilent_82350b.c new file mode 100644 index 0000000000000..53006d0cc79c5 --- /dev/null +++ b/drivers/staging/gpib/agilent_82350b/agilent_82350b.c @@ -0,0 +1,932 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2002, 2004 by Frank Mori Hess * + ***************************************************************************/ + +#include "agilent_82350b.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for Agilent 82350b"); + +int agilent_82350b_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; + int retval = 0; + unsigned short event_status; + int i, num_fifo_bytes; + //hardware doesn't support checking for end-of-string character when using fifo + if (tms_priv->eos_flags & REOS) { + //pr_info("ag-rd: using tms9914 read for REOS %x EOS %x\n",tms_priv->eos_flags, + // tms_priv->eos); + return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); + } + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + read_and_clear_event_status(board); + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + //disable fifo for the moment + writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + // handle corner case of board not in holdoff and one byte might slip in early + if (tms_priv->holdoff_active == 0 && length > 1) { + size_t num_bytes; + + retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + dev_err(board->gpib_dev, "%s: tms9914_read failed retval=%i\n", + driver_name, retval); + if (retval < 0 || *end) + return retval; + ++buffer; + --length; + } + tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); + tms9914_release_holdoff(tms_priv); + i = 0; + num_fifo_bytes = length - 1; + write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BIIE, IMR0); // disable BI interrupts + while (i < num_fifo_bytes && *end == 0) { + int block_size; + int j; + int count; + + if (num_fifo_bytes - i < agilent_82350b_fifo_size) + block_size = num_fifo_bytes - i; + else + block_size = agilent_82350b_fifo_size; + set_transfer_counter(a_priv, block_size); + writeb(ENABLE_TI_TO_SRAM | DIRECTION_GPIB_TO_HOST, + a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + if (agilent_82350b_fifo_is_halted(a_priv)) + writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG); + + clear_bit(READ_READY_BN, &tms_priv->state); + + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + (TERM_COUNT_STATUS_BIT | + BUFFER_END_STATUS_BIT)) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + if (retval) { + dev_dbg(board->gpib_dev, "%s: read wait interrupted\n", driver_name); + retval = -ERESTARTSYS; + break; + } + count = block_size - read_transfer_counter(a_priv); + for (j = 0; j < count && i < num_fifo_bytes; ++j) + buffer[i++] = readb(a_priv->sram_base + j); + if (event_status & BUFFER_END_STATUS_BIT) { + clear_bit(RECEIVED_END_BN, &tms_priv->state); + + tms_priv->holdoff_active = 1; + *end = 1; + } + if (test_bit(TIMO_NUM, &board->status)) { + dev_err(board->gpib_dev, "%s: read timed out\n", driver_name); + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + dev_err(board->gpib_dev, "%s: device clear interrupted read\n", + driver_name); + retval = -EINTR; + break; + } + } + write_byte(tms_priv, tms_priv->imr0_bits, IMR0); // re-enable BI interrupts + *bytes_read += i; + buffer += i; + length -= i; + writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + if (retval < 0) + return retval; + // read last bytes if we havn't received an END yet + if (*end == 0) { + size_t num_bytes; + // try to make sure we holdoff after last byte read + retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static int translate_wait_return_value(gpib_board_t *board, int retval) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; + + if (retval) { + dev_err(board->gpib_dev, "%s: write wait interrupted\n", driver_name); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) { + dev_err(board->gpib_dev, "%s: write timed out\n", driver_name); + return -ETIMEDOUT; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + dev_err(board->gpib_dev, "%s: device clear interrupted write\n", driver_name); + return -EINTR; + } + return 0; +} + +int agilent_82350b_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; + int i, j; + unsigned short event_status; + int retval = 0; + int fifotransferlength = length; + int block_size = 0; + size_t num_bytes; + + *bytes_written = 0; + if (send_eoi) + --fifotransferlength; + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + + event_status = read_and_clear_event_status(board); + + //pr_info("ag_ac_wr: event status 0x%x tms state 0x%lx\n", event_status, tms_priv->state); + +#ifdef EXPERIMENTAL + pr_info("ag_ac_wr: wait for previous BO to complete if any\n"); + retval = wait_event_interruptible(board->wait, + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(WRITE_READY_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + retval = translate_wait_return_value(board, retval); + + if (retval) + return retval; +#endif + + //pr_info("ag_ac_wr: sending first byte\n"); + retval = agilent_82350b_write(board, buffer, 1, 0, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + + //pr_info("ag_ac_wr: %ld bytes eoi %d tms state 0x%lx\n",length, send_eoi, tms_priv->state); + + write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BOIE, IMR0); + for (i = 1; i < fifotransferlength;) { + clear_bit(WRITE_READY_BN, &tms_priv->state); + + if (fifotransferlength - i < agilent_82350b_fifo_size) + block_size = fifotransferlength - i; + else + block_size = agilent_82350b_fifo_size; + set_transfer_counter(a_priv, block_size); + for (j = 0; j < block_size; ++j, ++i) { + // load data into board's sram + writeb(buffer[i], a_priv->sram_base + j); + } + writeb(ENABLE_TI_TO_SRAM, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + + //pr_info("ag_ac_wr: send block: %d bytes tms 0x%lx\n", block_size, + // tms_priv->state); + + if (agilent_82350b_fifo_is_halted(a_priv)) { + writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG); + // pr_info("ag_ac_wr: needed restart\n"); + } + + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + TERM_COUNT_STATUS_BIT) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + num_bytes = block_size - read_transfer_counter(a_priv); + //pr_info("ag_ac_wr: sent %ld bytes tms 0x%lx\n", num_bytes, tms_priv->state); + + *bytes_written += num_bytes; + retval = translate_wait_return_value(board, retval); + if (retval) + break; + } + write_byte(tms_priv, tms_priv->imr0_bits, IMR0); + if (retval) + return retval; + + if (send_eoi) { + //pr_info("ag_ac_wr: sending last byte with eoi byte no: %d\n", + // fifotransferlength+1); + + retval = agilent_82350b_write(board, buffer + fifotransferlength, 1, send_eoi, + &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +unsigned short read_and_clear_event_status(gpib_board_t *board) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + unsigned long flags; + unsigned short status; + + spin_lock_irqsave(&board->spinlock, flags); + status = a_priv->event_status_bits; + a_priv->event_status_bits = 0; + spin_unlock_irqrestore(&board->spinlock, flags); + return status; +} + +irqreturn_t agilent_82350b_interrupt(int irq, void *arg) + +{ + int tms9914_status1 = 0, tms9914_status2 = 0; + int event_status; + gpib_board_t *board = arg; + struct agilent_82350b_priv *a_priv = board->private_data; + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + + spin_lock_irqsave(&board->spinlock, flags); + event_status = readb(a_priv->gpib_base + EVENT_STATUS_REG); + if (event_status & IRQ_STATUS_BIT) + retval = IRQ_HANDLED; + + if (event_status & TMS9914_IRQ_STATUS_BIT) { + tms9914_status1 = read_byte(&a_priv->tms9914_priv, ISR0); + tms9914_status2 = read_byte(&a_priv->tms9914_priv, ISR1); + tms9914_interrupt_have_status(board, &a_priv->tms9914_priv, tms9914_status1, + tms9914_status2); + } +//pr_info("event_status=0x%x s1 %x s2 %x\n", event_status,tms9914_status1,tms9914_status2); +//write-clear status bits + if (event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT)) { + writeb(event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT), + a_priv->gpib_base + EVENT_STATUS_REG); + a_priv->event_status_bits |= event_status; + wake_up_interruptible(&board->wait); + } + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +void agilent_82350b_detach(gpib_board_t *board); + +const char *driver_name = "agilent_82350b"; + +int read_transfer_counter(struct agilent_82350b_priv *a_priv) + +{ + int lo, mid, value; + + lo = readb(a_priv->gpib_base + XFER_COUNT_LO_REG); + mid = readb(a_priv->gpib_base + XFER_COUNT_MID_REG); + value = (lo & 0xff) | ((mid << 8) & 0x7f00); + value = ~(value - 1) & 0x7fff; + return value; +} + +void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count) + +{ + int complement = -count; + + writeb(complement & 0xff, a_priv->gpib_base + XFER_COUNT_LO_REG); + writeb((complement >> 8) & 0xff, a_priv->gpib_base + XFER_COUNT_MID_REG); + //I don't think the hi count reg is even used, but oh well + writeb((complement >> 16) & 0xf, a_priv->gpib_base + XFER_COUNT_HI_REG); +} + +// wrappers for interface functions +int agilent_82350b_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); +} + +int agilent_82350b_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); +} + +int agilent_82350b_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); +} + +int agilent_82350b_take_control(gpib_board_t *board, int synchronous) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_take_control_workaround(board, &priv->tms9914_priv, synchronous); +} + +int agilent_82350b_go_to_standby(gpib_board_t *board) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_go_to_standby(board, &priv->tms9914_priv); +} + +void agilent_82350b_request_system_control(gpib_board_t *board, int request_control) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + + if (request_control) { + a_priv->card_mode_bits |= CM_SYSTEM_CONTROLLER_BIT; + if (a_priv->model != MODEL_82350A) + writeb(IC_SYSTEM_CONTROLLER_BIT, a_priv->gpib_base + INTERNAL_CONFIG_REG); + } else { + a_priv->card_mode_bits &= ~CM_SYSTEM_CONTROLLER_BIT; + if (a_priv->model != MODEL_82350A) + writeb(0, a_priv->gpib_base + INTERNAL_CONFIG_REG); + } + writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG); + tms9914_request_system_control(board, &a_priv->tms9914_priv, request_control); +} + +void agilent_82350b_interface_clear(gpib_board_t *board, int assert) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_interface_clear(board, &priv->tms9914_priv, assert); +} + +void agilent_82350b_remote_enable(gpib_board_t *board, int enable) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_remote_enable(board, &priv->tms9914_priv, enable); +} + +int agilent_82350b_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); +} + +void agilent_82350b_disable_eos(gpib_board_t *board) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_disable_eos(board, &priv->tms9914_priv); +} + +unsigned int agilent_82350b_update_status(gpib_board_t *board, unsigned int clear_mask) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); +} + +int agilent_82350b_primary_address(gpib_board_t *board, unsigned int address) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_primary_address(board, &priv->tms9914_priv, address); +} + +int agilent_82350b_secondary_address(gpib_board_t *board, unsigned int address, int enable) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); +} + +int agilent_82350b_parallel_poll(gpib_board_t *board, uint8_t *result) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_parallel_poll(board, &priv->tms9914_priv, result); +} + +void agilent_82350b_parallel_poll_configure(gpib_board_t *board, uint8_t config) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); +} + +void agilent_82350b_parallel_poll_response(gpib_board_t *board, int ist) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); +} + +void agilent_82350b_serial_poll_response(gpib_board_t *board, uint8_t status) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_serial_poll_response(board, &priv->tms9914_priv, status); +} + +uint8_t agilent_82350b_serial_poll_status(gpib_board_t *board) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_serial_poll_status(board, &priv->tms9914_priv); +} + +int agilent_82350b_line_status(const gpib_board_t *board) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_line_status(board, &priv->tms9914_priv); +} + +unsigned int agilent_82350b_t1_delay(gpib_board_t *board, unsigned int nanosec) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + static const int nanosec_per_clock = 30; + unsigned int value; + + tms9914_t1_delay(board, &a_priv->tms9914_priv, nanosec); + + value = (nanosec + nanosec_per_clock - 1) / nanosec_per_clock; + if (value > 0xff) + value = 0xff; + writeb(value, a_priv->gpib_base + T1_DELAY_REG); + return value * nanosec_per_clock; +} + +void agilent_82350b_return_to_local(gpib_board_t *board) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_return_to_local(board, &priv->tms9914_priv); +} + +int agilent_82350b_allocate_private(gpib_board_t *board) + +{ + board->private_data = kzalloc(sizeof(struct agilent_82350b_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + return 0; +} + +void agilent_82350b_free_private(gpib_board_t *board) + +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int init_82350a_hardware(gpib_board_t *board, const gpib_board_config_t *config) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + static const unsigned int firmware_length = 5302; + unsigned int borg_status; + static const unsigned int timeout = 1000; + int i, j; + const char *firmware_data = config->init_data; + const unsigned int plx_cntrl_static_bits = PLX9050_WAITO_NOT_USER0_SELECT_BIT | + PLX9050_USER0_OUTPUT_BIT | + PLX9050_LLOCK_NOT_USER1_SELECT_BIT | + PLX9050_USER1_OUTPUT_BIT | + PLX9050_USER2_OUTPUT_BIT | + PLX9050_USER3_OUTPUT_BIT | + PLX9050_PCI_READ_MODE_BIT | + PLX9050_PCI_WRITE_MODE_BIT | + PLX9050_PCI_RETRY_DELAY_BITS(64) | + PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT; + +// load borg data + borg_status = readb(a_priv->borg_base); + if ((borg_status & BORG_DONE_BIT)) + return 0; + // need to programme borg + if (!config->init_data || config->init_data_length != firmware_length) { + dev_err(board->gpib_dev, "%s: the 82350A board requires firmware after powering on.\n", + driver_name); + return -EIO; + } + dev_info(board->gpib_dev, "%s: Loading firmware...\n", driver_name); + + // tickle the borg + writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT, + a_priv->plx_base + PLX9050_CNTRL_REG); + usleep_range(1000, 2000); + writel(plx_cntrl_static_bits, a_priv->plx_base + PLX9050_CNTRL_REG); + usleep_range(1000, 2000); + writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT, + a_priv->plx_base + PLX9050_CNTRL_REG); + usleep_range(1000, 2000); + + for (i = 0; i < config->init_data_length; ++i) { + for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_READY_BIT) == 0; ++j) { + if (need_resched()) + schedule(); + usleep_range(10, 20); + } + if (j == timeout) { + dev_err(board->gpib_dev, "%s: timed out loading firmware.\n", driver_name); + return -ETIMEDOUT; + } + writeb(firmware_data[i], a_priv->gpib_base + CONFIG_DATA_REG); + } + for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_DONE_BIT) == 0; ++j) { + if (need_resched()) + schedule(); + usleep_range(10, 20); + } + if (j == timeout) { + dev_err(board->gpib_dev, "%s: timed out waiting for firmware load to complete.\n", + driver_name); + return -ETIMEDOUT; + } + dev_info(board->gpib_dev, "%s: ...done.\n", driver_name); + return 0; +} + +static int test_sram(gpib_board_t *board) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + unsigned int i; + const unsigned int sram_length = pci_resource_len(a_priv->pci_device, SRAM_82350A_REGION); + // test SRAM + const unsigned int byte_mask = 0xff; + + for (i = 0; i < sram_length; ++i) { + writeb(i & byte_mask, a_priv->sram_base + i); + if (need_resched()) + schedule(); + } + for (i = 0; i < sram_length; ++i) { + unsigned int read_value = readb(a_priv->sram_base + i); + + if ((i & byte_mask) != read_value) { + dev_err(board->gpib_dev, "%s: SRAM test failed at %d wanted %d got %d\n", + driver_name, i, (i & byte_mask), read_value); + return -EIO; + } + if (need_resched()) + schedule(); + } + dev_info(board->gpib_dev, "%s: SRAM test passed 0x%x bytes checked\n", + driver_name, sram_length); + return 0; +} + +static int agilent_82350b_generic_attach(gpib_board_t *board, const gpib_board_config_t *config, + int use_fifos) + +{ + struct agilent_82350b_priv *a_priv; + struct tms9914_priv *tms_priv; + int retval; + + board->status = 0; + + if (agilent_82350b_allocate_private(board)) + return -ENOMEM; + a_priv = board->private_data; + a_priv->using_fifos = use_fifos; + tms_priv = &a_priv->tms9914_priv; + tms_priv->read_byte = tms9914_iomem_read_byte; + tms_priv->write_byte = tms9914_iomem_write_byte; + tms_priv->offset = 1; + + // find board + a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT, + PCI_DEVICE_ID_82350B, NULL); + if (a_priv->pci_device) { + a_priv->model = MODEL_82350B; + dev_info(board->gpib_dev, "%s: Agilent 82350B board found\n", driver_name); + + } else { + a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT, + PCI_DEVICE_ID_82351A, NULL); + if (a_priv->pci_device) { + a_priv->model = MODEL_82351A; + dev_info(board->gpib_dev, "%s: Agilent 82351B board found\n", driver_name); + + } else { + a_priv->pci_device = gpib_pci_get_subsys(config, PCI_VENDOR_ID_PLX, + PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_HP, + PCI_SUBDEVICE_ID_82350A, + a_priv->pci_device); + if (a_priv->pci_device) { + a_priv->model = MODEL_82350A; + dev_info(board->gpib_dev, "%s: HP/Agilent 82350A board found\n", + driver_name); + } else { + dev_err(board->gpib_dev, "%s: no 82350/82351 board found\n", + driver_name); + return -ENODEV; + } + } + } + if (pci_enable_device(a_priv->pci_device)) { + dev_err(board->gpib_dev, "%s: error enabling pci device\n", driver_name); + return -EIO; + } + if (pci_request_regions(a_priv->pci_device, driver_name)) + return -EIO; + switch (a_priv->model) { + case MODEL_82350A: + a_priv->plx_base = ioremap(pci_resource_start(a_priv->pci_device, PLX_MEM_REGION), + pci_resource_len(a_priv->pci_device, PLX_MEM_REGION)); + dev_dbg(board->gpib_dev, "%s: plx base address remapped to 0x%p\n", + driver_name, a_priv->plx_base); + a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, + GPIB_82350A_REGION), + pci_resource_len(a_priv->pci_device, + GPIB_82350A_REGION)); + dev_dbg(board->gpib_dev, "%s: gpib base address remapped to 0x%p\n", + driver_name, a_priv->gpib_base); + tms_priv->iobase = a_priv->gpib_base + TMS9914_BASE_REG; + a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, + SRAM_82350A_REGION), + pci_resource_len(a_priv->pci_device, + SRAM_82350A_REGION)); + dev_dbg(board->gpib_dev, "%s: sram base address remapped to 0x%p\n", + driver_name, a_priv->sram_base); + a_priv->borg_base = ioremap(pci_resource_start(a_priv->pci_device, + BORG_82350A_REGION), + pci_resource_len(a_priv->pci_device, + BORG_82350A_REGION)); + dev_dbg(board->gpib_dev, "%s: borg base address remapped to 0x%p\n", + driver_name, a_priv->borg_base); + + retval = init_82350a_hardware(board, config); + if (retval < 0) + return retval; + break; + case MODEL_82350B: + case MODEL_82351A: + a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, GPIB_REGION), + pci_resource_len(a_priv->pci_device, GPIB_REGION)); + dev_dbg(board->gpib_dev, "%s: gpib base address remapped to 0x%p\n", + driver_name, a_priv->gpib_base); + tms_priv->iobase = a_priv->gpib_base + TMS9914_BASE_REG; + a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, SRAM_REGION), + pci_resource_len(a_priv->pci_device, SRAM_REGION)); + dev_dbg(board->gpib_dev, "%s: sram base address remapped to 0x%p\n", + driver_name, a_priv->sram_base); + a_priv->misc_base = ioremap(pci_resource_start(a_priv->pci_device, MISC_REGION), + pci_resource_len(a_priv->pci_device, MISC_REGION)); + dev_dbg(board->gpib_dev, "%s: misc base address remapped to 0x%p\n", + driver_name, a_priv->misc_base); + break; + default: + pr_err("%s: invalid board\n", driver_name); + return -1; + } + + retval = test_sram(board); + if (retval < 0) + return retval; + + if (request_irq(a_priv->pci_device->irq, agilent_82350b_interrupt, + IRQF_SHARED, driver_name, board)) { + pr_err("%s: can't request IRQ %d\n", driver_name, a_priv->pci_device->irq); + return -EIO; + } + a_priv->irq = a_priv->pci_device->irq; + dev_dbg(board->gpib_dev, "%s: IRQ %d\n", driver_name, a_priv->irq); + + writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + a_priv->card_mode_bits = ENABLE_PCI_IRQ_BIT; + writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG); + + if (a_priv->model == MODEL_82350A) { + // enable PCI interrupts for 82350a + writel(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR2_POLARITY_BIT | + PLX9050_PCI_INTR_EN_BIT, + a_priv->plx_base + PLX9050_INTCSR_REG); + } + + if (use_fifos) { + writeb(ENABLE_BUFFER_END_EVENTS_BIT | ENABLE_TERM_COUNT_EVENTS_BIT, + a_priv->gpib_base + EVENT_ENABLE_REG); + writeb(ENABLE_TERM_COUNT_INTERRUPT_BIT | ENABLE_BUFFER_END_INTERRUPT_BIT | + ENABLE_TMS9914_INTERRUPTS_BIT, a_priv->gpib_base + INTERRUPT_ENABLE_REG); + //write-clear event status bits + writeb(BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT, + a_priv->gpib_base + EVENT_STATUS_REG); + } else { + writeb(0, a_priv->gpib_base + EVENT_ENABLE_REG); + writeb(ENABLE_TMS9914_INTERRUPTS_BIT, + a_priv->gpib_base + INTERRUPT_ENABLE_REG); + } + board->t1_nano_sec = agilent_82350b_t1_delay(board, 2000); + tms9914_board_reset(tms_priv); + + tms9914_online(board, tms_priv); + + return 0; +} + +int agilent_82350b_unaccel_attach(gpib_board_t *board, const gpib_board_config_t *config) + +{ + return agilent_82350b_generic_attach(board, config, 0); +} + +int agilent_82350b_accel_attach(gpib_board_t *board, const gpib_board_config_t *config) + +{ + return agilent_82350b_generic_attach(board, config, 1); +} + +void agilent_82350b_detach(gpib_board_t *board) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv; + + if (a_priv) { + if (a_priv->plx_base) // disable interrupts + writel(0, a_priv->plx_base + PLX9050_INTCSR_REG); + + tms_priv = &a_priv->tms9914_priv; + if (a_priv->irq) + free_irq(a_priv->irq, board); + if (a_priv->gpib_base) { + tms9914_board_reset(tms_priv); + if (a_priv->misc_base) + iounmap((void *)a_priv->misc_base); + if (a_priv->borg_base) + iounmap((void *)a_priv->borg_base); + if (a_priv->sram_base) + iounmap((void *)a_priv->sram_base); + if (a_priv->gpib_base) + iounmap((void *)a_priv->gpib_base); + if (a_priv->plx_base) + iounmap((void *)a_priv->plx_base); + pci_release_regions(a_priv->pci_device); + } + if (a_priv->pci_device) + pci_dev_put(a_priv->pci_device); + } + agilent_82350b_free_private(board); +} + +gpib_interface_t agilent_82350b_unaccel_interface = { +name: "agilent_82350b_unaccel", +attach : agilent_82350b_unaccel_attach, +detach : agilent_82350b_detach, +read : agilent_82350b_read, +write : agilent_82350b_write, +command : agilent_82350b_command, +request_system_control : agilent_82350b_request_system_control, +take_control : agilent_82350b_take_control, +go_to_standby : agilent_82350b_go_to_standby, +interface_clear : agilent_82350b_interface_clear, +remote_enable : agilent_82350b_remote_enable, +enable_eos : agilent_82350b_enable_eos, +disable_eos : agilent_82350b_disable_eos, +parallel_poll : agilent_82350b_parallel_poll, +parallel_poll_configure : agilent_82350b_parallel_poll_configure, +parallel_poll_response : agilent_82350b_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : agilent_82350b_line_status, +update_status : agilent_82350b_update_status, +primary_address : agilent_82350b_primary_address, +secondary_address : agilent_82350b_secondary_address, +serial_poll_response : agilent_82350b_serial_poll_response, +t1_delay : agilent_82350b_t1_delay, +return_to_local : agilent_82350b_return_to_local, +}; + +gpib_interface_t agilent_82350b_interface = { +name: "agilent_82350b", +attach : agilent_82350b_accel_attach, +detach : agilent_82350b_detach, +read : agilent_82350b_accel_read, +write : agilent_82350b_accel_write, +command : agilent_82350b_command, +request_system_control : agilent_82350b_request_system_control, +take_control : agilent_82350b_take_control, +go_to_standby : agilent_82350b_go_to_standby, +interface_clear : agilent_82350b_interface_clear, +remote_enable : agilent_82350b_remote_enable, +enable_eos : agilent_82350b_enable_eos, +disable_eos : agilent_82350b_disable_eos, +parallel_poll : agilent_82350b_parallel_poll, +parallel_poll_configure : agilent_82350b_parallel_poll_configure, +parallel_poll_response : agilent_82350b_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : agilent_82350b_line_status, +update_status : agilent_82350b_update_status, +primary_address : agilent_82350b_primary_address, +secondary_address : agilent_82350b_secondary_address, +serial_poll_response : agilent_82350b_serial_poll_response, +t1_delay : agilent_82350b_t1_delay, +return_to_local : agilent_82350b_return_to_local, +}; + +static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) + +{ + return 0; +} + +static const struct pci_device_id agilent_82350b_pci_table[] = { + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, + PCI_SUBDEVICE_ID_82350A, 0, 0, 0 }, + { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table); + +static struct pci_driver agilent_82350b_pci_driver = { + .name = "agilent_82350b", + .id_table = agilent_82350b_pci_table, + .probe = &agilent_82350b_pci_probe +}; + +static int __init agilent_82350b_init_module(void) + +{ + int result; + + result = pci_register_driver(&agilent_82350b_pci_driver); + if (result) { + pr_err("agilent_82350b: pci_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&agilent_82350b_unaccel_interface, THIS_MODULE); + gpib_register_driver(&agilent_82350b_interface, THIS_MODULE); + return 0; +} + +static void __exit agilent_82350b_exit_module(void) + +{ + gpib_unregister_driver(&agilent_82350b_interface); + gpib_unregister_driver(&agilent_82350b_unaccel_interface); + + pci_unregister_driver(&agilent_82350b_pci_driver); +} + +module_init(agilent_82350b_init_module); +module_exit(agilent_82350b_exit_module); diff --git a/drivers/staging/gpib/agilent_82350b/agilent_82350b.h b/drivers/staging/gpib/agilent_82350b/agilent_82350b.h new file mode 100644 index 0000000000000..30683d67d1705 --- /dev/null +++ b/drivers/staging/gpib/agilent_82350b/agilent_82350b.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002, 2004 by Frank Mori Hess * + ***************************************************************************/ + +#include "gpibP.h" +#include "plx9050.h" +#include "tms9914.h" + +enum pci_vendor_ids { + PCI_VENDOR_ID_AGILENT = 0x15bc, +}; + +enum pci_device_ids { + PCI_DEVICE_ID_82350B = 0x0b01, + PCI_DEVICE_ID_82351A = 0x1218 +}; + +enum pci_subdevice_ids { + PCI_SUBDEVICE_ID_82350A = 0x10b0, +}; + +enum pci_regions_82350a { + PLX_MEM_REGION = 0, + PLX_IO_REGION = 1, + GPIB_82350A_REGION = 2, + SRAM_82350A_REGION = 3, + BORG_82350A_REGION = 4 +}; + +enum pci_regions_82350b { + GPIB_REGION = 0, + SRAM_REGION = 1, + MISC_REGION = 2, +}; + +enum board_model { + MODEL_82350A, + MODEL_82350B, + MODEL_82351A +}; + +// struct which defines private_data for board +struct agilent_82350b_priv { + struct tms9914_priv tms9914_priv; + struct pci_dev *pci_device; + void *plx_base; //82350a only + void *gpib_base; + void *sram_base; + void *misc_base; + void *borg_base; + int irq; + unsigned short card_mode_bits; + unsigned short event_status_bits; + enum board_model model; + bool using_fifos; +}; + +// driver name +extern const char *driver_name; + +// interfaces +extern gpib_interface_t agilent_82350b_interface; +// init functions + +int agilent_82350b_unaccel_attach(gpib_board_t *board, const gpib_board_config_t *config); +int agilent_82350b_accel_attach(gpib_board_t *board, const gpib_board_config_t *config); + +// interface functions +int agilent_82350b_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read); +int agilent_82350b_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written); +int agilent_82350b_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read); +int agilent_82350b_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written); +int agilent_82350b_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written); +int agilent_82350b_take_control(gpib_board_t *board, int synchronous); +int agilent_82350b_go_to_standby(gpib_board_t *board); +void agilent_82350b_request_system_control(gpib_board_t *board, int request_control); +void agilent_82350b_interface_clear(gpib_board_t *board, int assert); +void agilent_82350b_remote_enable(gpib_board_t *board, int enable); +int agilent_82350b_enable_eos(gpib_board_t *board, uint8_t eos_byte, int + compare_8_bits); +void agilent_82350b_disable_eos(gpib_board_t *board); +unsigned int agilent_82350b_update_status(gpib_board_t *board, unsigned int clear_mask); +int agilent_82350b_primary_address(gpib_board_t *board, unsigned int address); +int agilent_82350b_secondary_address(gpib_board_t *board, unsigned int address, int + enable); +int agilent_82350b_parallel_poll(gpib_board_t *board, uint8_t *result); +void agilent_82350b_parallel_poll_configure(gpib_board_t *board, uint8_t config); +void agilent_82350b_parallel_poll_response(gpib_board_t *board, int ist); +void agilent_82350b_serial_poll_response(gpib_board_t *board, uint8_t status); +void agilent_82350b_return_to_local(gpib_board_t *board); +uint8_t agilent_82350b_serial_poll_status(gpib_board_t *board); +int agilent_82350b_line_status(const gpib_board_t *board); +unsigned int agilent_82350b_t1_delay(gpib_board_t *board, unsigned int nanosec); + +// interrupt service routines +irqreturn_t agilent_82350b_interrupt(int irq, void *arg); + +// utility functions +int agilent_82350b_allocate_private(gpib_board_t *board); +void agilent_82350b_free_private(gpib_board_t *board); +unsigned short read_and_clear_event_status(gpib_board_t *board); +int read_transfer_counter(struct agilent_82350b_priv *a_priv); +void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count); + +//registers +enum agilent_82350b_gpib_registers + +{ + CARD_MODE_REG = 0x1, + CONFIG_DATA_REG = 0x2, // 82350A specific + INTERRUPT_ENABLE_REG = 0x3, + EVENT_STATUS_REG = 0x4, + EVENT_ENABLE_REG = 0x5, + STREAM_STATUS_REG = 0x7, + DEBUG_RAM0_REG = 0x8, + DEBUG_RAM1_REG = 0x9, + DEBUG_RAM2_REG = 0xa, + DEBUG_RAM3_REG = 0xb, + XFER_COUNT_LO_REG = 0xc, + XFER_COUNT_MID_REG = 0xd, + XFER_COUNT_HI_REG = 0xe, + TMS9914_BASE_REG = 0x10, + INTERNAL_CONFIG_REG = 0x18, + IMR0_READ_REG = 0x19, //read + T1_DELAY_REG = 0x19, // write + IMR1_READ_REG = 0x1a, + ADR_READ_REG = 0x1b, + SPMR_READ_REG = 0x1c, + PPR_READ_REG = 0x1d, + CDOR_READ_REG = 0x1e, + SRAM_ACCESS_CONTROL_REG = 0x1f, +}; + +enum card_mode_bits + +{ + ACTIVE_CONTROLLER_BIT = 0x2, // read-only + CM_SYSTEM_CONTROLLER_BIT = 0x8, + ENABLE_BUS_MONITOR_BIT = 0x10, + ENABLE_PCI_IRQ_BIT = 0x20, +}; + +enum interrupt_enable_bits + +{ + ENABLE_TMS9914_INTERRUPTS_BIT = 0x1, + ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10, + ENABLE_TERM_COUNT_INTERRUPT_BIT = 0x20, +}; + +enum event_enable_bits + +{ + ENABLE_BUFFER_END_EVENTS_BIT = 0x10, + ENABLE_TERM_COUNT_EVENTS_BIT = 0x20, +}; + +enum event_status_bits + +{ + TMS9914_IRQ_STATUS_BIT = 0x1, + IRQ_STATUS_BIT = 0x2, + BUFFER_END_STATUS_BIT = 0x10, // write-clear + TERM_COUNT_STATUS_BIT = 0x20, // write-clear +}; + +enum stream_status_bits + +{ + HALTED_STATUS_BIT = 0x1, //read + RESTART_STREAM_BIT = 0x1, //write +}; + +enum internal_config_bits + +{ + IC_SYSTEM_CONTROLLER_BIT = 0x80, +}; + +enum sram_access_control_bits + +{ + DIRECTION_GPIB_TO_HOST = 0x20, // transfer direction + ENABLE_TI_TO_SRAM = 0x40, // enable fifo + ENABLE_FAST_TALKER = 0x80 // added for 82350A (not used) +}; + +enum borg_bits + +{ + BORG_READY_BIT = 0x40, + BORG_DONE_BIT = 0x80 +}; + +static const int agilent_82350b_fifo_size = 0x8000; + +static inline int agilent_82350b_fifo_is_halted(struct agilent_82350b_priv *a_priv) + +{ + return readb(a_priv->gpib_base + STREAM_STATUS_REG) & HALTED_STATUS_BIT; +} + diff --git a/drivers/staging/gpib/agilent_82357a/Makefile b/drivers/staging/gpib/agilent_82357a/Makefile new file mode 100644 index 0000000000000..4a1d940fce2b8 --- /dev/null +++ b/drivers/staging/gpib/agilent_82357a/Makefile @@ -0,0 +1,4 @@ + +obj-m += agilent_82357a.o + + diff --git a/drivers/staging/gpib/agilent_82357a/agilent_82357a.c b/drivers/staging/gpib/agilent_82357a/agilent_82357a.c new file mode 100644 index 0000000000000..bf05fb4a736b3 --- /dev/null +++ b/drivers/staging/gpib/agilent_82357a/agilent_82357a.c @@ -0,0 +1,1712 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * driver for Agilent 82357A/B usb to gpib adapters * + * copyright : (C) 2004 by Frank Mori Hess * + ***************************************************************************/ + +#define _GNU_SOURCE + +#include +#include +#include +#include "agilent_82357a.h" +#include "gpibP.h" +#include "tms9914.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for Agilent 82357A/B usb adapters"); + +#define MAX_NUM_82357A_INTERFACES 128 +static struct usb_interface *agilent_82357a_driver_interfaces[MAX_NUM_82357A_INTERFACES]; +DEFINE_MUTEX(agilent_82357a_hotplug_lock); // protect board insertion and removal + +static unsigned int agilent_82357a_update_status(gpib_board_t *board, unsigned int clear_mask); + +static int agilent_82357a_take_control_internal(gpib_board_t *board, int synchronous); + +static void agilent_82357a_bulk_complete(struct urb *urb) +{ + struct agilent_82357a_urb_ctx *context = urb->context; + + up(&context->complete); +} + +static void agilent_82357a_timeout_handler(struct timer_list *t) +{ + struct agilent_82357a_priv *a_priv = from_timer(a_priv, t, bulk_timer); + struct agilent_82357a_urb_ctx *context = &a_priv->context; + + context->timed_out = 1; + up(&context->complete); +} + +static int agilent_82357a_send_bulk_msg(struct agilent_82357a_priv *a_priv, void *data, + int data_length, int *actual_data_length, + int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int out_pipe; + struct agilent_82357a_urb_ctx *context = &a_priv->context; + + *actual_data_length = 0; + retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENODEV; + } + if (a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -EAGAIN; + } + a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + out_pipe = usb_sndbulkpipe(usb_dev, a_priv->bulk_out_endpoint); + sema_init(&context->complete, 0); + context->timed_out = 0; + usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, out_pipe, data, data_length, + &agilent_82357a_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "%s: failed to submit bulk out urb, retval=%i\n", + __func__, retval); + mutex_unlock(&a_priv->bulk_alloc_lock); + goto cleanup; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + if (down_interruptible(&context->complete)) { + dev_err(&usb_dev->dev, "%s: interrupted\n", __func__); + retval = -ERESTARTSYS; + goto cleanup; + } + if (context->timed_out) { + retval = -ETIMEDOUT; + } else { + retval = a_priv->bulk_urb->status; + *actual_data_length = a_priv->bulk_urb->actual_length; + } +cleanup: + if (timeout_msecs) { + if (timer_pending(&a_priv->bulk_timer)) + del_timer_sync(&a_priv->bulk_timer); + } + mutex_lock(&a_priv->bulk_alloc_lock); + if (a_priv->bulk_urb) { + usb_kill_urb(a_priv->bulk_urb); + usb_free_urb(a_priv->bulk_urb); + a_priv->bulk_urb = NULL; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + return retval; +} + +static int agilent_82357a_receive_bulk_msg(struct agilent_82357a_priv *a_priv, void *data, + int data_length, int *actual_data_length, + int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + struct agilent_82357a_urb_ctx *context = &a_priv->context; + + *actual_data_length = 0; + retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENODEV; + } + if (a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -EAGAIN; + } + a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + in_pipe = usb_rcvbulkpipe(usb_dev, AGILENT_82357_BULK_IN_ENDPOINT); + sema_init(&context->complete, 0); + context->timed_out = 0; + usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, in_pipe, data, data_length, + &agilent_82357a_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "%s: failed to submit bulk out urb, retval=%i\n", + __func__, retval); + mutex_unlock(&a_priv->bulk_alloc_lock); + goto cleanup; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + if (down_interruptible(&context->complete)) { + dev_err(&usb_dev->dev, "%s: interrupted\n", __func__); + retval = -ERESTARTSYS; + goto cleanup; + } + if (context->timed_out) { + retval = -ETIMEDOUT; + goto cleanup; + } + retval = a_priv->bulk_urb->status; + *actual_data_length = a_priv->bulk_urb->actual_length; +cleanup: + if (timeout_msecs) + del_timer_sync(&a_priv->bulk_timer); + + mutex_lock(&a_priv->bulk_alloc_lock); + if (a_priv->bulk_urb) { + usb_kill_urb(a_priv->bulk_urb); + usb_free_urb(a_priv->bulk_urb); + a_priv->bulk_urb = NULL; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + return retval; +} + +static int agilent_82357a_receive_control_msg(struct agilent_82357a_priv *a_priv, __u8 request, + __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + + retval = mutex_lock_interruptible(&a_priv->control_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + mutex_unlock(&a_priv->control_alloc_lock); + return -ENODEV; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + in_pipe = usb_rcvctrlpipe(usb_dev, AGILENT_82357_CONTROL_ENDPOINT); + retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data, + size, timeout_msecs); + mutex_unlock(&a_priv->control_alloc_lock); + return retval; +} + +static void agilent_82357a_dump_raw_block(const u8 *raw_data, int length) +{ + pr_info("hex block dump\n"); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true); +} + +static int agilent_82357a_write_registers(struct agilent_82357a_priv *a_priv, + const struct agilent_82357a_register_pairlet *writes, + int num_writes) +{ + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval; + u8 *out_data, *in_data; + int out_data_length, in_data_length; + int bytes_written, bytes_read; + int i = 0; + int j; + static const int bytes_per_write = 2; + static const int header_length = 2; + static const int max_writes = 31; + + if (num_writes > max_writes) { + dev_err(&usb_dev->dev, "%s: bug! num_writes=%i too large\n", __func__, num_writes); + return -EIO; + } + out_data_length = num_writes * bytes_per_write + header_length; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = DATA_PIPE_CMD_WR_REGS; + out_data[i++] = num_writes; + for (j = 0; j < num_writes; j++) { + out_data[i++] = writes[j].address; + out_data[i++] = writes[j].value; + } + if (i > out_data_length) + dev_err(&usb_dev->dev, "%s: bug! buffer overrun\n", __func__); + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + return retval; + } + in_data_length = 0x20; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, + &bytes_read, 1000); + mutex_unlock(&a_priv->bulk_transfer_lock); + + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + agilent_82357a_dump_raw_block(in_data, bytes_read); + kfree(in_data); + return -EIO; + } + if (in_data[0] != (0xff & ~DATA_PIPE_CMD_WR_REGS)) { + dev_err(&usb_dev->dev, "%s: error, bulk command=0x%x != ~DATA_PIPE_CMD_WR_REGS\n", + __func__, in_data[0]); + return -EIO; + } + if (in_data[1]) { + dev_err(&usb_dev->dev, "%s: nonzero error code 0x%x in DATA_PIPE_CMD_WR_REGS response\n", + __func__, in_data[1]); + return -EIO; + } + kfree(in_data); + return 0; +} + +static int agilent_82357a_read_registers(struct agilent_82357a_priv *a_priv, + struct agilent_82357a_register_pairlet *reads, + int num_reads, int blocking) +{ + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval; + u8 *out_data, *in_data; + int out_data_length, in_data_length; + int bytes_written, bytes_read; + int i = 0; + int j; + static const int header_length = 2; + static const int max_reads = 62; + + if (num_reads > max_reads) + dev_err(&usb_dev->dev, "%s: bug! num_reads=%i too large\n", __func__, num_reads); + + out_data_length = num_reads + header_length; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = DATA_PIPE_CMD_RD_REGS; + out_data[i++] = num_reads; + for (j = 0; j < num_reads; j++) + out_data[i++] = reads[j].address; + if (i > out_data_length) + dev_err(&usb_dev->dev, "%s: bug! buffer overrun\n", __func__); + if (blocking) { + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + } else { + retval = mutex_trylock(&a_priv->bulk_transfer_lock); + if (retval == 0) { + kfree(out_data); + return -EAGAIN; + } + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + return retval; + } + in_data_length = 0x20; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, + &bytes_read, 10000); + mutex_unlock(&a_priv->bulk_transfer_lock); + + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + agilent_82357a_dump_raw_block(in_data, bytes_read); + kfree(in_data); + return -EIO; + } + i = 0; + if (in_data[i++] != (0xff & ~DATA_PIPE_CMD_RD_REGS)) { + dev_err(&usb_dev->dev, "%s: error, bulk command=0x%x != ~DATA_PIPE_CMD_RD_REGS\n", + __func__, in_data[0]); + return -EIO; + } + if (in_data[i++]) { + dev_err(&usb_dev->dev, "%s: nonzero error code 0x%x in DATA_PIPE_CMD_RD_REGS response\n", + __func__, in_data[1]); + return -EIO; + } + for (j = 0; j < num_reads; j++) + reads[j].value = in_data[i++]; + kfree(in_data); + return 0; +} + +static int agilent_82357a_abort(struct agilent_82357a_priv *a_priv, int flush) +{ + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval = 0; + int receive_control_retval; + u16 wIndex = 0; + u8 *status_data; + static const unsigned int status_data_len = 2; + + status_data = kmalloc(status_data_len, GFP_KERNEL); + if (!status_data) + return -ENOMEM; + + if (flush) + wIndex |= XA_FLUSH; + receive_control_retval = agilent_82357a_receive_control_msg(a_priv, + agilent_82357a_control_request, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, XFER_ABORT, + wIndex, status_data, + status_data_len, 100); + if (receive_control_retval < 0) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_receive_control_msg() returned %i\n", + __func__, receive_control_retval); + retval = -EIO; + goto cleanup; + } + if (status_data[0] != (~XFER_ABORT & 0xff)) { + dev_err(&usb_dev->dev, "%s: error, major code=0x%x != ~XFER_ABORT\n", + __func__, status_data[0]); + retval = -EIO; + goto cleanup; + } + switch (status_data[1]) { + case UGP_SUCCESS: + retval = 0; + break; + case UGP_ERR_FLUSHING: + if (flush) { + retval = 0; + break; + } + fallthrough; + case UGP_ERR_FLUSHING_ALREADY: + default: + dev_err(&usb_dev->dev, "%s: abort returned error code=0x%x\n", + __func__, status_data[1]); + retval = -EIO; + break; + } + +cleanup: + kfree(status_data); + return retval; +} + +// interface functions +int agilent_82357a_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written); + +static int agilent_82357a_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *nbytes) +{ + int retval; + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + u8 *out_data, *in_data; + int out_data_length, in_data_length; + int bytes_written, bytes_read; + int i = 0; + u8 trailing_flags; + unsigned long start_jiffies = jiffies; + int msec_timeout; + + *nbytes = 0; + *end = 0; + out_data_length = 0x9; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = DATA_PIPE_CMD_READ; + out_data[i++] = 0; //primary address when ARF_NO_ADDR is not set + out_data[i++] = 0; //secondary address when ARF_NO_ADDR is not set + out_data[i] = ARF_NO_ADDRESS | ARF_END_ON_EOI; + if (a_priv->eos_mode & REOS) + out_data[i] |= ARF_END_ON_EOS_CHAR; + ++i; + out_data[i++] = length & 0xff; + out_data[i++] = (length >> 8) & 0xff; + out_data[i++] = (length >> 16) & 0xff; + out_data[i++] = (length >> 24) & 0xff; + out_data[i++] = a_priv->eos_char; + msec_timeout = (board->usec_timeout + 999) / 1000; + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, msec_timeout); + kfree(out_data); + if (retval || bytes_written != i) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + if (retval < 0) + return retval; + return -EIO; + } + in_data_length = length + 1; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + if (board->usec_timeout != 0) + msec_timeout -= jiffies_to_msecs(jiffies - start_jiffies) - 1; + if (msec_timeout >= 0) { + retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, + &bytes_read, msec_timeout); + } else { + retval = -ETIMEDOUT; + bytes_read = 0; + } + if (retval == -ETIMEDOUT) { + int extra_bytes_read; + int extra_bytes_retval; + + agilent_82357a_abort(a_priv, 1); + extra_bytes_retval = agilent_82357a_receive_bulk_msg(a_priv, in_data + bytes_read, + in_data_length - bytes_read, + &extra_bytes_read, 100); + bytes_read += extra_bytes_read; + if (extra_bytes_retval) { + dev_err(&usb_dev->dev, "%s: extra_bytes_retval=%i, bytes_read=%i\n", + __func__, extra_bytes_retval, bytes_read); + agilent_82357a_abort(a_priv, 0); + } + } else if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + agilent_82357a_abort(a_priv, 0); + } + mutex_unlock(&a_priv->bulk_transfer_lock); + if (bytes_read > length + 1) { + bytes_read = length + 1; + pr_warn("%s: bytes_read > length? truncating", __func__); + } + + if (bytes_read >= 1) { + memcpy(buffer, in_data, bytes_read - 1); + trailing_flags = in_data[bytes_read - 1]; + *nbytes = bytes_read - 1; + if (trailing_flags & (ATRF_EOI | ATRF_EOS)) + *end = 1; + } + kfree(in_data); + + /* Fix for a bug in 9914A that does not return the contents of ADSR + * when the board is in listener active state and ATN is not asserted. + * Set ATN here to obtain a valid board level ibsta + */ + agilent_82357a_take_control_internal(board, 0); + + //FIXME check trailing flags for error + return retval; +} + +static ssize_t agilent_82357a_generic_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_commands, int send_eoi, size_t *bytes_written) +{ + int retval; + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + u8 *out_data = NULL; + u8 *status_data = NULL; + int out_data_length; + int raw_bytes_written; + int i = 0, j; + int msec_timeout; + unsigned short bsr, adsr; + struct agilent_82357a_register_pairlet read_reg; + + *bytes_written = 0; + out_data_length = length + 0x8; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = DATA_PIPE_CMD_WRITE; + out_data[i++] = 0; // primary address when AWF_NO_ADDRESS is not set + out_data[i++] = 0; // secondary address when AWF_NO_ADDRESS is not set + out_data[i] = AWF_NO_ADDRESS | AWF_NO_FAST_TALKER_FIRST_BYTE; + if (send_commands) + out_data[i] |= AWF_ATN | AWF_NO_FAST_TALKER; + if (send_eoi) + out_data[i] |= AWF_SEND_EOI; + ++i; + out_data[i++] = length & 0xff; + out_data[i++] = (length >> 8) & 0xff; + out_data[i++] = (length >> 16) & 0xff; + out_data[i++] = (length >> 24) & 0xff; + for (j = 0; j < length; j++) + out_data[i++] = buffer[j]; + + clear_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags); + + msec_timeout = (board->usec_timeout + 999) / 1000; + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &raw_bytes_written, + msec_timeout); + kfree(out_data); + if (retval || raw_bytes_written != i) { + agilent_82357a_abort(a_priv, 0); + dev_err(&usb_dev->dev, "%s: agilent_82357a_send_bulk_msg returned %i, raw_bytes_written=%i, i=%i\n", + __func__, retval, raw_bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + if (retval < 0) + return retval; + return -EIO; + } + + retval = wait_event_interruptible(board->wait, + test_bit(AIF_WRITE_COMPLETE_BN, + &a_priv->interrupt_flags) || + test_bit(TIMO_NUM, &board->status)); + if (retval) { + dev_err(&usb_dev->dev, "%s: wait write complete interrupted\n", __func__); + agilent_82357a_abort(a_priv, 0); + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ERESTARTSYS; + } + + if (test_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags) == 0) { + dev_dbg(&usb_dev->dev, "write timed out ibs %i, tmo %i\n", + test_bit(TIMO_NUM, &board->status), msec_timeout); + + agilent_82357a_abort(a_priv, 0); + + mutex_unlock(&a_priv->bulk_transfer_lock); + + read_reg.address = BSR; + retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return -ETIMEDOUT; + } + + bsr = read_reg.value; + dev_dbg(&usb_dev->dev, "write aborted bsr 0x%x\n", bsr); + + if (send_commands) {/* check for no listeners */ + if ((bsr & BSR_ATN_BIT) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) { + dev_dbg(&usb_dev->dev, "No listener on command\n"); + clear_bit(TIMO_NUM, &board->status); + return -ENOTCONN; // no listener on bus + } + } else { + read_reg.address = ADSR; + retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return -ETIMEDOUT; + } + adsr = read_reg.value; + if ((adsr & HR_TA) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) { + dev_dbg(&usb_dev->dev, "No listener on write\n"); + clear_bit(TIMO_NUM, &board->status); + return -ECOMM; + } + } + + return -ETIMEDOUT; + } + + status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL); + if (!status_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + + retval = agilent_82357a_receive_control_msg(a_priv, agilent_82357a_control_request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + XFER_STATUS, 0, status_data, STATUS_DATA_LEN, + 100); + mutex_unlock(&a_priv->bulk_transfer_lock); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_receive_control_msg() returned %i\n", + __func__, retval); + kfree(status_data); + return -EIO; + } + *bytes_written = (u32)status_data[2]; + *bytes_written |= (u32)status_data[3] << 8; + *bytes_written |= (u32)status_data[4] << 16; + *bytes_written |= (u32)status_data[5] << 24; + + kfree(status_data); + return 0; +} + +static int agilent_82357a_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + return agilent_82357a_generic_write(board, buffer, length, 0, send_eoi, bytes_written); +} + +int agilent_82357a_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written) +{ + return agilent_82357a_generic_write(board, buffer, length, 1, 0, bytes_written); +} + +int agilent_82357a_take_control_internal(gpib_board_t *board, int synchronous) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + write.address = AUXCR; + if (synchronous) + write.value = AUX_TCS; + else + write.value = AUX_TCA; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + + return retval; +} + +static int agilent_82357a_take_control(gpib_board_t *board, int synchronous) +{ + const int timeout = 10; + int i; + +/* It looks like the 9914 does not handle tcs properly. + * See comment above tms9914_take_control_workaround() in + * drivers/gpib/tms9914/tms9914_aux.c + */ + if (synchronous) + return -ETIMEDOUT; + + agilent_82357a_take_control_internal(board, synchronous); + // busy wait until ATN is asserted + for (i = 0; i < timeout; ++i) { + agilent_82357a_update_status(board, 0); + if (test_bit(ATN_NUM, &board->status)) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + return 0; +} + +static int agilent_82357a_go_to_standby(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + write.address = AUXCR; + write.value = AUX_GTS; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return 0; +} + +//FIXME should change prototype to return int +static void agilent_82357a_request_system_control(gpib_board_t *board, int request_control) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet writes[2]; + int retval; + int i = 0; + + /* 82357B needs bit to be set in 9914 AUXCR register */ + writes[i].address = AUXCR; + if (request_control) { + writes[i].value = AUX_RQC; + a_priv->hw_control_bits |= SYSTEM_CONTROLLER; + } else { + writes[i].value = AUX_RLC; + a_priv->is_cic = 0; + a_priv->hw_control_bits &= ~SYSTEM_CONTROLLER; + } + ++i; + writes[i].address = HW_CONTROL; + writes[i].value = a_priv->hw_control_bits; + ++i; + retval = agilent_82357a_write_registers(a_priv, writes, i); + if (retval) + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return;// retval; +} + +static void agilent_82357a_interface_clear(gpib_board_t *board, int assert) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + write.address = AUXCR; + write.value = AUX_SIC; + if (assert) { + write.value |= AUX_CS; + a_priv->is_cic = 1; + } + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); +} + +static void agilent_82357a_remote_enable(gpib_board_t *board, int enable) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + write.address = AUXCR; + write.value = AUX_SRE; + if (enable) + write.value |= AUX_CS; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + a_priv->ren_state = enable; + return;// 0; +} + +static int agilent_82357a_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + + if (compare_8_bits == 0) { + pr_warn("%s: hardware only supports 8-bit EOS compare", __func__); + return -EOPNOTSUPP; + } + a_priv->eos_char = eos_byte; + a_priv->eos_mode = REOS | BIN; + return 0; +} + +static void agilent_82357a_disable_eos(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + + a_priv->eos_mode &= ~REOS; +} + +static unsigned int agilent_82357a_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet address_status, bus_status; + int retval; + + board->status &= ~clear_mask; + if (a_priv->is_cic) + set_bit(CIC_NUM, &board->status); + else + clear_bit(CIC_NUM, &board->status); + address_status.address = ADSR; + retval = agilent_82357a_read_registers(a_priv, &address_status, 1, 0); + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return board->status; + } + // check for remote/local + if (address_status.value & HR_REM) + set_bit(REM_NUM, &board->status); + else + clear_bit(REM_NUM, &board->status); + // check for lockout + if (address_status.value & HR_LLO) + set_bit(LOK_NUM, &board->status); + else + clear_bit(LOK_NUM, &board->status); + // check for ATN + if (address_status.value & HR_ATN) + set_bit(ATN_NUM, &board->status); + else + clear_bit(ATN_NUM, &board->status); + // check for talker/listener addressed + if (address_status.value & HR_TA) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + if (address_status.value & HR_LA) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + + bus_status.address = BSR; + retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0); + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return board->status; + } + if (bus_status.value & BSR_SRQ_BIT) + set_bit(SRQI_NUM, &board->status); + else + clear_bit(SRQI_NUM, &board->status); + + return board->status; +} + +static int agilent_82357a_primary_address(gpib_board_t *board, unsigned int address) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + // put primary address in address0 + write.address = ADR; + write.value = address & ADDRESS_MASK; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return retval; + } + return retval; +} + +static int agilent_82357a_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + if (enable) + pr_warn("%s: warning: assigning a secondary address not supported\n", __func__); + return -EOPNOTSUPP; +} + +static int agilent_82357a_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet writes[2]; + struct agilent_82357a_register_pairlet read; + int retval; + + // execute parallel poll + writes[0].address = AUXCR; + writes[0].value = AUX_CS | AUX_RPP; + writes[1].address = HW_CONTROL; + writes[1].value = a_priv->hw_control_bits & ~NOT_PARALLEL_POLL; + retval = agilent_82357a_write_registers(a_priv, writes, 2); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return retval; + } + udelay(2); //silly, since usb write will take way longer + read.address = CPTR; + retval = agilent_82357a_read_registers(a_priv, &read, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return retval; + } + *result = read.value; + // clear parallel poll state + writes[0].address = HW_CONTROL; + writes[0].value = a_priv->hw_control_bits | NOT_PARALLEL_POLL; + writes[1].address = AUXCR; + writes[1].value = AUX_RPP; + retval = agilent_82357a_write_registers(a_priv, writes, 2); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return retval; + } + return 0; +} + +static void agilent_82357a_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + //board can only be system controller + return;// 0; +} + +static void agilent_82357a_parallel_poll_response(gpib_board_t *board, int ist) +{ + //board can only be system controller + return;// 0; +} + +static void agilent_82357a_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + //board can only be system controller + return;// 0; +} + +static uint8_t agilent_82357a_serial_poll_status(gpib_board_t *board) +{ + //board can only be system controller + return 0; +} + +static void agilent_82357a_return_to_local(gpib_board_t *board) +{ + //board can only be system controller + return;// 0; +} + +static int agilent_82357a_line_status(const gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet bus_status; + int retval; + int status = ValidALL; + + bus_status.address = BSR; + retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0); + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return retval; + } + if (bus_status.value & BSR_REN_BIT) + status |= BusREN; + if (bus_status.value & BSR_IFC_BIT) + status |= BusIFC; + if (bus_status.value & BSR_SRQ_BIT) + status |= BusSRQ; + if (bus_status.value & BSR_EOI_BIT) + status |= BusEOI; + if (bus_status.value & BSR_NRFD_BIT) + status |= BusNRFD; + if (bus_status.value & BSR_NDAC_BIT) + status |= BusNDAC; + if (bus_status.value & BSR_DAV_BIT) + status |= BusDAV; + if (bus_status.value & BSR_ATN_BIT) + status |= BusATN; + return status; +} + +static unsigned short nanosec_to_fast_talker_bits(unsigned int *nanosec) +{ + static const int nanosec_per_bit = 21; + static const int max_value = 0x72; + static const int min_value = 0x11; + unsigned short bits; + + bits = (*nanosec + nanosec_per_bit / 2) / nanosec_per_bit; + if (bits < min_value) + bits = min_value; + if (bits > max_value) + bits = max_value; + *nanosec = bits * nanosec_per_bit; + return bits; +} + +static unsigned int agilent_82357a_t1_delay(gpib_board_t *board, unsigned int nanosec) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + write.address = FAST_TALKER_T1; + write.value = nanosec_to_fast_talker_bits(&nanosec); + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return nanosec; +} + +static void agilent_82357a_interrupt_complete(struct urb *urb) +{ + gpib_board_t *board = urb->context; + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval; + u8 *transfer_buffer = urb->transfer_buffer; + unsigned long interrupt_flags; + + switch (urb->status) { + /* success */ + case 0: + break; + /* unlinked, don't resubmit */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* other error, resubmit */ + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "%s: failed to resubmit interrupt urb\n", __func__); + return; + } + + interrupt_flags = transfer_buffer[0]; + if (test_bit(AIF_READ_COMPLETE_BN, &interrupt_flags)) + set_bit(AIF_READ_COMPLETE_BN, &a_priv->interrupt_flags); + if (test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags)) + set_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags); + if (test_bit(AIF_SRQ_BN, &interrupt_flags)) + set_bit(SRQI_NUM, &board->status); + + wake_up_interruptible(&board->wait); + + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "%s: failed to resubmit interrupt urb\n", __func__); +} + +static int agilent_82357a_setup_urbs(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + int int_pipe; + int retval; + + retval = mutex_lock_interruptible(&a_priv->interrupt_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + retval = -ENODEV; + goto setup_exit; + } + + a_priv->interrupt_buffer = kmalloc(INTERRUPT_BUF_LEN, GFP_KERNEL); + if (!a_priv->interrupt_buffer) { + retval = -ENOMEM; + goto setup_exit; + } + a_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!a_priv->interrupt_urb) { + retval = -ENOMEM; + goto setup_exit; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + int_pipe = usb_rcvintpipe(usb_dev, a_priv->interrupt_in_endpoint); + usb_fill_int_urb(a_priv->interrupt_urb, usb_dev, int_pipe, a_priv->interrupt_buffer, + INTERRUPT_BUF_LEN, &agilent_82357a_interrupt_complete, board, 1); + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL); + if (retval) { + usb_free_urb(a_priv->interrupt_urb); + a_priv->interrupt_urb = NULL; + dev_err(&usb_dev->dev, "%s: failed to submit first interrupt urb, retval=%i\n", + __func__, retval); + goto setup_exit; + } + mutex_unlock(&a_priv->interrupt_alloc_lock); + return 0; + +setup_exit: + kfree(a_priv->interrupt_buffer); + mutex_unlock(&a_priv->interrupt_alloc_lock); + return retval; +} + +#ifdef RESET_USB_CONFIG +static int agilent_82357a_reset_usb_configuration(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct usb_device *usb_dev; + int retval; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + retval = usb_reset_configuration(usb_dev); + if (retval) + dev_err(&usb_dev->dev, "%s: usb_reset_configuration() returned %i\n", + __func__, retval); + return retval; +} +#endif + +static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv) +{ + if (a_priv && a_priv->bus_interface) { + if (a_priv->interrupt_urb) + usb_kill_urb(a_priv->interrupt_urb); + if (a_priv->bulk_urb) + usb_kill_urb(a_priv->bulk_urb); + } +}; + +static int agilent_82357a_allocate_private(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv; + + board->private_data = kmalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + a_priv = board->private_data; + memset(a_priv, 0, sizeof(struct agilent_82357a_priv)); + mutex_init(&a_priv->bulk_transfer_lock); + mutex_init(&a_priv->bulk_alloc_lock); + mutex_init(&a_priv->control_alloc_lock); + mutex_init(&a_priv->interrupt_alloc_lock); + return 0; +} + +static void agilent_82357a_free_private(struct agilent_82357a_priv *a_priv) +{ + usb_free_urb(a_priv->interrupt_urb); + kfree(a_priv->interrupt_buffer); + kfree(a_priv); +} + +static int agilent_82357a_init(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet hw_control; + struct agilent_82357a_register_pairlet writes[0x20]; + int retval; + int i; + unsigned int nanosec; + + i = 0; + writes[i].address = LED_CONTROL; + writes[i].value = FAIL_LED_ON; + ++i; + writes[i].address = RESET_TO_POWERUP; + writes[i].value = RESET_SPACEBALL; + ++i; + retval = agilent_82357a_write_registers(a_priv, writes, i); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return -EIO; + } + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(usec_to_jiffies(2000))) + return -ERESTARTSYS; + i = 0; + writes[i].address = AUXCR; + writes[i].value = AUX_NBAF; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_HLDE; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_TON; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_LON; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_RSV2; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_INVAL; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_RPP; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_STDL; + ++i; + writes[i].address = AUXCR; + writes[i].value = AUX_VSTDL; + ++i; + writes[i].address = FAST_TALKER_T1; + nanosec = board->t1_nano_sec; + writes[i].value = nanosec_to_fast_talker_bits(&nanosec); + board->t1_nano_sec = nanosec; + ++i; + writes[i].address = ADR; + writes[i].value = board->pad & ADDRESS_MASK; + ++i; + writes[i].address = PPR; + writes[i].value = 0; + ++i; + writes[i].address = SPMR; + writes[i].value = 0; + ++i; + writes[i].address = PROTOCOL_CONTROL; + writes[i].value = WRITE_COMPLETE_INTERRUPT_EN; + ++i; + writes[i].address = IMR0; + writes[i].value = HR_BOIE | HR_BIIE; + ++i; + writes[i].address = IMR1; + writes[i].value = HR_SRQIE; + ++i; + // turn off reset state + writes[i].address = AUXCR; + writes[i].value = AUX_CHIP_RESET; + ++i; + writes[i].address = LED_CONTROL; + writes[i].value = FIRMWARE_LED_CONTROL; + ++i; + if (i > ARRAY_SIZE(writes)) { + dev_err(&usb_dev->dev, "%s: bug! writes[] overflow\n", __func__); + return -EFAULT; + } + retval = agilent_82357a_write_registers(a_priv, writes, i); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return -EIO; + } + hw_control.address = HW_CONTROL; + retval = agilent_82357a_read_registers(a_priv, &hw_control, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_read_registers() returned error\n", + __func__); + return -EIO; + } + a_priv->hw_control_bits = (hw_control.value & ~0x7) | NOT_TI_RESET | NOT_PARALLEL_POLL; + + return 0; +} + +static inline int agilent_82357a_device_match(struct usb_interface *interface, + const gpib_board_config_t *config) +{ + struct usb_device * const usbdev = interface_to_usbdev(interface); + + if (gpib_match_device_path(&interface->dev, config->device_path) == 0) + return 0; + if (config->serial_number && + strcmp(usbdev->serial, config->serial_number) != 0) + return 0; + + return 1; +} + +static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + int retval; + int i; + unsigned int product_id; + struct agilent_82357a_priv *a_priv; + struct usb_device *usb_dev; + + if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) + return -ERESTARTSYS; + + retval = agilent_82357a_allocate_private(board); + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + a_priv = board->private_data; + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] && + !usb_get_intfdata(agilent_82357a_driver_interfaces[i]) && + agilent_82357a_device_match(agilent_82357a_driver_interfaces[i], config)) { + a_priv->bus_interface = agilent_82357a_driver_interfaces[i]; + usb_set_intfdata(agilent_82357a_driver_interfaces[i], board); + usb_dev = interface_to_usbdev(a_priv->bus_interface); + dev_info(&usb_dev->dev, + "bus %d dev num %d attached to gpib minor %d, agilent usb interface %i\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) { + mutex_unlock(&agilent_82357a_hotplug_lock); + pr_err("No Agilent 82357 gpib adapters found, have you loaded its firmware?\n"); + return -ENODEV; + } + product_id = le16_to_cpu(interface_to_usbdev(a_priv->bus_interface)->descriptor.idProduct); + switch (product_id) { + case USB_DEVICE_ID_AGILENT_82357A: + a_priv->bulk_out_endpoint = AGILENT_82357A_BULK_OUT_ENDPOINT; + a_priv->interrupt_in_endpoint = AGILENT_82357A_INTERRUPT_IN_ENDPOINT; + break; + case USB_DEVICE_ID_AGILENT_82357B: + a_priv->bulk_out_endpoint = AGILENT_82357B_BULK_OUT_ENDPOINT; + a_priv->interrupt_in_endpoint = AGILENT_82357B_INTERRUPT_IN_ENDPOINT; + break; + default: + dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n"); + return -EIO; + } +#ifdef RESET_USB_CONFIG + retval = agilent_82357a_reset_usb_configuration(board); + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } +#endif + retval = agilent_82357a_setup_urbs(board); + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + + timer_setup(&a_priv->bulk_timer, agilent_82357a_timeout_handler, 0); + + board->t1_nano_sec = 800; + + retval = agilent_82357a_init(board); + + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + + dev_info(&usb_dev->dev, "%s: attached\n", __func__); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; +} + +static int agilent_82357a_go_idle(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet writes[0x20]; + int retval; + int i; + + i = 0; + // turn on tms9914 reset state + writes[i].address = AUXCR; + writes[i].value = AUX_CS | AUX_CHIP_RESET; + ++i; + a_priv->hw_control_bits &= ~NOT_TI_RESET; + writes[i].address = HW_CONTROL; + writes[i].value = a_priv->hw_control_bits; + ++i; + writes[i].address = PROTOCOL_CONTROL; + writes[i].value = 0; + ++i; + writes[i].address = IMR0; + writes[i].value = 0; + ++i; + writes[i].address = IMR1; + writes[i].value = 0; + ++i; + writes[i].address = LED_CONTROL; + writes[i].value = 0; + ++i; + if (i > ARRAY_SIZE(writes)) { + dev_err(&usb_dev->dev, "%s: bug! writes[] overflow\n", __func__); + return -EFAULT; + } + retval = agilent_82357a_write_registers(a_priv, writes, i); + if (retval) { + dev_err(&usb_dev->dev, "%s: agilent_82357a_write_registers() returned error\n", + __func__); + return -EIO; + } + return 0; +} + +static void agilent_82357a_detach(gpib_board_t *board) +{ + struct agilent_82357a_priv *a_priv; + struct usb_device *usb_dev; + + mutex_lock(&agilent_82357a_hotplug_lock); + + a_priv = board->private_data; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + if (a_priv) { + if (a_priv->bus_interface) { + agilent_82357a_go_idle(board); + usb_set_intfdata(a_priv->bus_interface, NULL); + } + mutex_lock(&a_priv->control_alloc_lock); + mutex_lock(&a_priv->bulk_alloc_lock); + mutex_lock(&a_priv->interrupt_alloc_lock); + agilent_82357a_cleanup_urbs(a_priv); + agilent_82357a_free_private(a_priv); + } + dev_info(&usb_dev->dev, "%s: detached\n", __func__); + mutex_unlock(&agilent_82357a_hotplug_lock); +} + +gpib_interface_t agilent_82357a_gpib_interface = { +name: "agilent_82357a", +attach : agilent_82357a_attach, +detach : agilent_82357a_detach, +read : agilent_82357a_read, +write : agilent_82357a_write, +command : agilent_82357a_command, +take_control : agilent_82357a_take_control, +go_to_standby : agilent_82357a_go_to_standby, +request_system_control : agilent_82357a_request_system_control, +interface_clear : agilent_82357a_interface_clear, +remote_enable : agilent_82357a_remote_enable, +enable_eos : agilent_82357a_enable_eos, +disable_eos : agilent_82357a_disable_eos, +parallel_poll : agilent_82357a_parallel_poll, +parallel_poll_configure : agilent_82357a_parallel_poll_configure, +parallel_poll_response : agilent_82357a_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : agilent_82357a_line_status, +update_status : agilent_82357a_update_status, +primary_address : agilent_82357a_primary_address, +secondary_address : agilent_82357a_secondary_address, +serial_poll_response : agilent_82357a_serial_poll_response, +serial_poll_status : agilent_82357a_serial_poll_status, +t1_delay : agilent_82357a_t1_delay, +return_to_local : agilent_82357a_return_to_local, +no_7_bit_eos : 1, +skip_check_for_command_acceptors : 1 +}; + +// Table with the USB-devices: just now only testing IDs +static struct usb_device_id agilent_82357a_driver_device_table[] = { + {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357A)}, + {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357B)}, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, agilent_82357a_driver_device_table); + +static int agilent_82357a_driver_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int i; + char *path; + static const int path_length = 1024; + struct usb_device *usb_dev; + + if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) + return -ERESTARTSYS; + usb_dev = usb_get_dev(interface_to_usbdev(interface)); + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (!agilent_82357a_driver_interfaces[i]) { + agilent_82357a_driver_interfaces[i] = interface; + usb_set_intfdata(interface, NULL); + dev_dbg(&usb_dev->dev, "set bus interface %i to address 0x%p\n", + i, interface); + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) { + usb_put_dev(usb_dev); + mutex_unlock(&agilent_82357a_hotplug_lock); + dev_err(&usb_dev->dev, "%s: out of space in agilent_82357a_driver_interfaces[]\n", + __func__); + return -1; + } + path = kmalloc(path_length, GFP_KERNEL); + if (!path) { + usb_put_dev(usb_dev); + mutex_unlock(&agilent_82357a_hotplug_lock); + return -ENOMEM; + } + usb_make_path(usb_dev, path, path_length); + dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path); + kfree(path); + mutex_unlock(&agilent_82357a_hotplug_lock); + return 0; +} + +static void agilent_82357a_driver_disconnect(struct usb_interface *interface) +{ + int i; + struct usb_device *usb_dev = interface_to_usbdev(interface); + + mutex_lock(&agilent_82357a_hotplug_lock); + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] == interface) { + gpib_board_t *board = usb_get_intfdata(interface); + + if (board) { + struct agilent_82357a_priv *a_priv = board->private_data; + + if (a_priv) { + mutex_lock(&a_priv->control_alloc_lock); + mutex_lock(&a_priv->bulk_alloc_lock); + mutex_lock(&a_priv->interrupt_alloc_lock); + agilent_82357a_cleanup_urbs(a_priv); + a_priv->bus_interface = NULL; + mutex_unlock(&a_priv->interrupt_alloc_lock); + mutex_unlock(&a_priv->bulk_alloc_lock); + mutex_unlock(&a_priv->control_alloc_lock); + } + } + dev_dbg(&usb_dev->dev, "nulled agilent_82357a_driver_interfaces[%i]\n", i); + agilent_82357a_driver_interfaces[i] = NULL; + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) + dev_err(&usb_dev->dev, "unable to find interface in agilent_82357a_driver_interfaces[]? bug?\n"); + usb_put_dev(usb_dev); + + mutex_unlock(&agilent_82357a_hotplug_lock); +} + +static int agilent_82357a_driver_suspend(struct usb_interface *interface, pm_message_t message) +{ + int i, retval; + struct usb_device *usb_dev = interface_to_usbdev(interface); + + mutex_lock(&agilent_82357a_hotplug_lock); + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] == interface) { + gpib_board_t *board = usb_get_intfdata(interface); + + if (board) { + struct agilent_82357a_priv *a_priv = board->private_data; + + if (a_priv) { + agilent_82357a_abort(a_priv, 0); + agilent_82357a_abort(a_priv, 0); + retval = agilent_82357a_go_idle(board); + if (retval) { + dev_err(&usb_dev->dev, "%s: failed to go idle, retval=%i\n", + __func__, retval); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + mutex_lock(&a_priv->interrupt_alloc_lock); + agilent_82357a_cleanup_urbs(a_priv); + mutex_unlock(&a_priv->interrupt_alloc_lock); + dev_info(&usb_dev->dev, + "bus %d dev num %d gpib minor %d, agilent usb interface %i suspended\n", + usb_dev->bus->busnum, usb_dev->devnum, + board->minor, i); + } + } + break; + } + } + + mutex_unlock(&agilent_82357a_hotplug_lock); + + return 0; +} + +static int agilent_82357a_driver_resume(struct usb_interface *interface) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + gpib_board_t *board; + int i, retval; + + mutex_lock(&agilent_82357a_hotplug_lock); + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] == interface) { + board = usb_get_intfdata(interface); + if (board) + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) + goto resume_exit; + + struct agilent_82357a_priv *a_priv = board->private_data; + + if (a_priv) { + if (a_priv->interrupt_urb) { + mutex_lock(&a_priv->interrupt_alloc_lock); + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "%s: failed to resubmit interrupt urb, retval=%i\n", + __func__, retval); + mutex_unlock(&a_priv->interrupt_alloc_lock); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + mutex_unlock(&a_priv->interrupt_alloc_lock); + } + retval = agilent_82357a_init(board); + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + // set/unset system controller + agilent_82357a_request_system_control(board, board->master); + // toggle ifc if master + if (board->master) { + agilent_82357a_interface_clear(board, 1); + usleep_range(200, 250); + agilent_82357a_interface_clear(board, 0); + } + // assert/unassert REN + agilent_82357a_remote_enable(board, a_priv->ren_state); + + dev_info(&usb_dev->dev, + "bus %d dev num %d gpib minor %d, agilent usb interface %i resumed\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + } + +resume_exit: + mutex_unlock(&agilent_82357a_hotplug_lock); + + return 0; +} + +static struct usb_driver agilent_82357a_bus_driver = { + .name = "agilent_82357a_gpib", + .probe = agilent_82357a_driver_probe, + .disconnect = agilent_82357a_driver_disconnect, + .suspend = agilent_82357a_driver_suspend, + .resume = agilent_82357a_driver_resume, + .id_table = agilent_82357a_driver_device_table, +}; + +static int __init agilent_82357a_init_module(void) +{ + int i; + + pr_info("agilent_82357a_gpib driver loading"); + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) + agilent_82357a_driver_interfaces[i] = NULL; + usb_register(&agilent_82357a_bus_driver); + gpib_register_driver(&agilent_82357a_gpib_interface, THIS_MODULE); + + return 0; +} + +static void __exit agilent_82357a_exit_module(void) +{ + pr_info("agilent_82357a_gpib driver unloading"); + gpib_unregister_driver(&agilent_82357a_gpib_interface); + usb_deregister(&agilent_82357a_bus_driver); +} + +module_init(agilent_82357a_init_module); +module_exit(agilent_82357a_exit_module); diff --git a/drivers/staging/gpib/agilent_82357a/agilent_82357a.h b/drivers/staging/gpib/agilent_82357a/agilent_82357a.h new file mode 100644 index 0000000000000..cdbc3ec5d8bd0 --- /dev/null +++ b/drivers/staging/gpib/agilent_82357a/agilent_82357a.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2004 by Frank Mori Hess * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "gpibP.h" +#include "tms9914.h" + +enum usb_vendor_ids { + USB_VENDOR_ID_AGILENT = 0x0957 +}; + +enum usb_device_ids { + USB_DEVICE_ID_AGILENT_82357A = 0x0107, + USB_DEVICE_ID_AGILENT_82357A_PREINIT = 0x0007, // device id before firmware is loaded + USB_DEVICE_ID_AGILENT_82357B = 0x0718, // device id before firmware is loaded + USB_DEVICE_ID_AGILENT_82357B_PREINIT = 0x0518, // device id before firmware is loaded +}; + +enum endpoint_addresses { + AGILENT_82357_CONTROL_ENDPOINT = 0x0, + AGILENT_82357_BULK_IN_ENDPOINT = 0x2, + AGILENT_82357A_BULK_OUT_ENDPOINT = 0x4, + AGILENT_82357A_INTERRUPT_IN_ENDPOINT = 0x6, + AGILENT_82357B_BULK_OUT_ENDPOINT = 0x6, + AGILENT_82357B_INTERRUPT_IN_ENDPOINT = 0x8, +}; + +enum bulk_commands { + DATA_PIPE_CMD_WRITE = 0x1, + DATA_PIPE_CMD_READ = 0x3, + DATA_PIPE_CMD_WR_REGS = 0x4, + DATA_PIPE_CMD_RD_REGS = 0x5 +}; + +enum agilent_82357a_read_flags { + ARF_END_ON_EOI = 0x1, + ARF_NO_ADDRESS = 0x2, + ARF_END_ON_EOS_CHAR = 0x4, + ARF_SPOLL = 0x8 +}; + +enum agilent_82357a_trailing_read_flags { + ATRF_EOI = 0x1, + ATRF_ATN = 0x2, + ATRF_IFC = 0x4, + ATRF_EOS = 0x8, + ATRF_ABORT = 0x10, + ATRF_COUNT = 0x20, + ATRF_DEAD_BUS = 0x40, + ATRF_UNADDRESSED = 0x80 +}; + +enum agilent_82357a_write_flags { + AWF_SEND_EOI = 0x1, + AWF_NO_FAST_TALKER_FIRST_BYTE = 0x2, + AWF_NO_FAST_TALKER = 0x4, + AWF_NO_ADDRESS = 0x8, + AWF_ATN = 0x10, + AWF_SEPARATE_HEADER = 0x80 +}; + +enum agilent_82357a_interrupt_flag_bit_numbers { + AIF_SRQ_BN = 0, + AIF_WRITE_COMPLETE_BN = 1, + AIF_READ_COMPLETE_BN = 2, +}; + +enum agilent_82357_error_codes { + UGP_SUCCESS = 0, + UGP_ERR_INVALID_CMD = 1, + UGP_ERR_INVALID_PARAM = 2, + UGP_ERR_INVALID_REG = 3, + UGP_ERR_GPIB_READ = 4, + UGP_ERR_GPIB_WRITE = 5, + UGP_ERR_FLUSHING = 6, + UGP_ERR_FLUSHING_ALREADY = 7, + UGP_ERR_UNSUPPORTED = 8, + UGP_ERR_OTHER = 9 +}; + +enum agilent_82357_control_values { + XFER_ABORT = 0xa0, + XFER_STATUS = 0xb0, +}; + +enum xfer_status_bits { + XS_COMPLETED = 0x1, + XS_READ = 0x2, +}; + +enum xfer_status_completion_bits { + XSC_EOI = 0x1, + XSC_ATN = 0x2, + XSC_IFC = 0x4, + XSC_EOS = 0x8, + XSC_ABORT = 0x10, + XSC_COUNT = 0x20, + XSC_DEAD_BUS = 0x40, + XSC_BUS_NOT_ADDRESSED = 0x80 +}; + +enum xfer_abort_type { + XA_FLUSH = 0x1 +}; + +#define STATUS_DATA_LEN 8 +#define INTERRUPT_BUF_LEN 8 + +struct agilent_82357a_urb_ctx { + struct semaphore complete; + unsigned timed_out : 1; +}; + +// struct which defines local data for each 82357 device +struct agilent_82357a_priv { + struct usb_interface *bus_interface; + unsigned short eos_char; + unsigned short eos_mode; + unsigned short hw_control_bits; + unsigned long interrupt_flags; + struct urb *bulk_urb; + struct urb *interrupt_urb; + u8 *interrupt_buffer; + struct mutex bulk_transfer_lock; // bulk transfer lock + struct mutex bulk_alloc_lock; // bulk transfer allocation lock + struct mutex interrupt_alloc_lock; // interrupt allocation lock + struct mutex control_alloc_lock; // control message allocation lock + struct timer_list bulk_timer; + struct agilent_82357a_urb_ctx context; + unsigned int bulk_out_endpoint; + unsigned int interrupt_in_endpoint; + unsigned is_cic : 1; + unsigned ren_state : 1; +}; + +struct agilent_82357a_register_pairlet { + short address; + unsigned short value; +}; + +enum firmware_registers { + HW_CONTROL = 0xa, + LED_CONTROL = 0xb, + RESET_TO_POWERUP = 0xc, + PROTOCOL_CONTROL = 0xd, + FAST_TALKER_T1 = 0xe +}; + +enum hardware_control_bits { + NOT_TI_RESET = 0x1, + SYSTEM_CONTROLLER = 0x2, + NOT_PARALLEL_POLL = 0x4, + OSCILLATOR_5V_ON = 0x8, + OUTPUT_5V_ON = 0x20, + CPLD_3V_ON = 0x80, +}; + +enum led_control_bits { + FIRMWARE_LED_CONTROL = 0x1, + FAIL_LED_ON = 0x20, + READY_LED_ON = 0x40, + ACCESS_LED_ON = 0x80 +}; + +enum reset_to_powerup_bits { + RESET_SPACEBALL = 0x1, // wait 2 millisec after sending +}; + +enum protocol_control_bits { + WRITE_COMPLETE_INTERRUPT_EN = 0x1, +}; + +static const int agilent_82357a_control_request = 0x4; + diff --git a/drivers/staging/gpib/cb7210/Makefile b/drivers/staging/gpib/cb7210/Makefile new file mode 100644 index 0000000000000..22e0214fc17dc --- /dev/null +++ b/drivers/staging/gpib/cb7210/Makefile @@ -0,0 +1,4 @@ +ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA +obj-m += cb7210.o + + diff --git a/drivers/staging/gpib/cb7210/cb7210.c b/drivers/staging/gpib/cb7210/cb7210.c new file mode 100644 index 0000000000000..63df7f3eb3f3e --- /dev/null +++ b/drivers/staging/gpib/cb7210/cb7210.c @@ -0,0 +1,1556 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * Measurement Computing boards using cb7210.2 and cbi488.2 chips + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "cb7210.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpib_pci_ids.h" +#include "quancom_pci.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver Measurement Computing boards using cb7210.2 and cbi488.2"); + +static inline int have_fifo_word(const struct cb7210_priv *cb_priv) +{ + if (((cb7210_read_byte(cb_priv, HS_STATUS)) & + (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) == + (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) + return 1; + else + return 0; +} + +static inline void input_fifo_enable(gpib_board_t *board, int enable) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + if (enable) { + cb_priv->in_fifo_half_full = 0; + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + + cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits, + HS_MODE); + + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); + + cb_priv->hs_mode_bits |= HS_RX_ENABLE; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + } else { + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, nec7210_iobase(cb_priv) + + HS_MODE); + + clear_bit(READ_READY_BN, &nec_priv->state); + } + + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fifo_read(gpib_board_t *board, struct cb7210_priv *cb_priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + int hs_status; + u16 word; + unsigned long flags; + + *bytes_read = 0; + if (cb_priv->fifo_iobase == 0) { + pr_err("cb7210: fifo iobase is zero!\n"); + return -EIO; + } + *end = 0; + if (length <= cb7210_fifo_size) { + pr_err("cb7210: bug! %s with length < fifo size\n", __func__); + return -EINVAL; + } + + input_fifo_enable(board, 1); + + while (*bytes_read + cb7210_fifo_size < length) { + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + if (wait_event_interruptible(board->wait, + (cb_priv->in_fifo_half_full && + have_fifo_word(cb_priv)) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_warn("cb7210: fifo half full wait interrupted\n"); + retval = -ERESTARTSYS; + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + + while (have_fifo_word(cb_priv)) { + word = inw(cb_priv->fifo_iobase + DIR); + buffer[(*bytes_read)++] = word & 0xff; + buffer[(*bytes_read)++] = (word >> 8) & 0xff; + } + + cb_priv->in_fifo_half_full = 0; + + hs_status = cb7210_read_byte(cb_priv, HS_STATUS); + + spin_unlock_irqrestore(&board->spinlock, flags); + + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) { + *end = 1; + break; + } + if (hs_status & HS_FIFO_FULL) + break; + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + retval = -EINTR; + break; + } + } + hs_status = cb7210_read_byte(cb_priv, HS_STATUS); + if (hs_status & HS_RX_LSB_NOT_EMPTY) { + word = inw(cb_priv->fifo_iobase + DIR); + buffer[(*bytes_read)++] = word & 0xff; + } + + input_fifo_enable(board, 0); + + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &nec_priv->state) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_warn("cb7210: fifo half full wait interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_bit(READ_READY_BN, &nec_priv->state)) { + nec7210_set_handshake_mode(board, nec_priv, HR_HLDA); + buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end); + } + + return retval; +} + +int cb7210_accel_read(gpib_board_t *board, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval; + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + size_t num_bytes; + + *bytes_read = 0; + // deal with limitations of fifo + if (length < cb7210_fifo_size + 3 || (nec_priv->auxa_bits & HR_REOS)) + return cb7210_read(board, buffer, length, end, bytes_read); + *end = 0; + + nec7210_release_rfd_holdoff(board, nec_priv); + + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_warn("cb7210: read ready wait interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end); + if (*end) + return 0; + + nec7210_release_rfd_holdoff(board, nec_priv); + + retval = fifo_read(board, cb_priv, &buffer[*bytes_read], length - *bytes_read - 1, + end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + if (*end) + return 0; + + retval = cb7210_read(board, &buffer[*bytes_read], 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + + return 0; +} + +static int output_fifo_empty(const struct cb7210_priv *cb_priv) +{ + if ((cb7210_read_byte(cb_priv, HS_STATUS) & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) + == 0) + return 1; + else + return 0; +} + +static inline void output_fifo_enable(gpib_board_t *board, int enable) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + if (enable) { + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + + cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits, + HS_MODE); + + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb_priv->hs_mode_bits |= HS_TX_ENABLE; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); + + clear_bit(WRITE_READY_BN, &nec_priv->state); + + } else { + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE); + } + + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fifo_write(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + size_t count = 0; + ssize_t retval = 0; + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned int num_bytes, i; + unsigned long flags; + + *bytes_written = 0; + if (cb_priv->fifo_iobase == 0) { + pr_err("cb7210: fifo iobase is zero!\n"); + return -EINVAL; + } + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + clear_bit(BUS_ERROR_BN, &nec_priv->state); + + output_fifo_enable(board, 1); + + while (count < length) { + // wait until byte is ready to be sent + if (wait_event_interruptible(board->wait, + cb_priv->out_fifo_half_empty || + output_fifo_empty(cb_priv) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_warn("cb7210: fifo wait interrupted\n"); + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(BUS_ERROR_BN, &nec_priv->state)) + break; + + if (output_fifo_empty(cb_priv)) + num_bytes = cb7210_fifo_size - cb7210_fifo_width; + else + num_bytes = cb7210_fifo_size / 2; + if (num_bytes + count > length) + num_bytes = length - count; + if (num_bytes % cb7210_fifo_width) { + pr_err("cb7210: bug! %s with odd number of bytes\n", __func__); + retval = -EINVAL; + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + for (i = 0; i < num_bytes / cb7210_fifo_width; i++) { + u16 word; + + word = buffer[count++] & 0xff; + word |= (buffer[count++] << 8) & 0xff00; + outw(word, cb_priv->fifo_iobase + CDOR); + } + cb_priv->out_fifo_half_empty = 0; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE); + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + spin_unlock_irqrestore(&board->spinlock, flags); + } + // wait last byte has been sent + if (wait_event_interruptible(board->wait, + output_fifo_empty(cb_priv) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_err("cb7210: wait for last byte interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + + output_fifo_enable(board, 0); + + *bytes_written = count; + return retval; +} + +int cb7210_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long fast_chunk_size, leftover; + int retval; + size_t num_bytes; + + *bytes_written = 0; + if (length > cb7210_fifo_width) + fast_chunk_size = length - 1; + else + fast_chunk_size = 0; + fast_chunk_size -= fast_chunk_size % cb7210_fifo_width; + leftover = length - fast_chunk_size; + + retval = fifo_write(board, buffer, fast_chunk_size, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + + retval = nec7210_write(board, nec_priv, buffer + fast_chunk_size, leftover, + send_eoi, &num_bytes); + *bytes_written += num_bytes; + return retval; +} + +int cb7210_line_status(const gpib_board_t *board) +{ + int status = ValidALL; + int bsr_bits; + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + + bsr_bits = cb7210_paged_read_byte(cb_priv, BUS_STATUS, BUS_STATUS_PAGE); + + if ((bsr_bits & BSR_REN_BIT) == 0) + status |= BusREN; + if ((bsr_bits & BSR_IFC_BIT) == 0) + status |= BusIFC; + if ((bsr_bits & BSR_SRQ_BIT) == 0) + status |= BusSRQ; + if ((bsr_bits & BSR_EOI_BIT) == 0) + status |= BusEOI; + if ((bsr_bits & BSR_NRFD_BIT) == 0) + status |= BusNRFD; + if ((bsr_bits & BSR_NDAC_BIT) == 0) + status |= BusNDAC; + if ((bsr_bits & BSR_DAV_BIT) == 0) + status |= BusDAV; + if ((bsr_bits & BSR_ATN_BIT) == 0) + status |= BusATN; + + return status; +} + +unsigned int cb7210_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 350) { + write_byte(nec_priv, AUX_HI_SPEED, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + } + return retval; +} + +irqreturn_t cb7210_locked_internal_interrupt(gpib_board_t *board); + +/* + * GPIB interrupt service routines + */ + +irqreturn_t cb_pci_interrupt(int irq, void *arg) +{ + int bits; + gpib_board_t *board = arg; + struct cb7210_priv *priv = board->private_data; + + // first task check if this is really our interrupt in a shared irq environment + switch (priv->pci_chip) { + case PCI_CHIP_AMCC_S5933: + if ((inl(priv->amcc_iobase + INTCSR_REG) & + (INBOX_INTR_CS_BIT | INTR_ASSERTED_BIT)) == 0) + return IRQ_NONE; + + // read incoming mailbox to clear mailbox full flag + inl(priv->amcc_iobase + INCOMING_MAILBOX_REG(3)); + // clear amccs5933 interrupt + bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | + INBOX_SELECT_BITS(3) | INBOX_INTR_CS_BIT; + outl(bits, priv->amcc_iobase + INTCSR_REG); + break; + case PCI_CHIP_QUANCOM: + if ((inb(nec7210_iobase(priv) + QUANCOM_IRQ_CONTROL_STATUS_REG) & + QUANCOM_IRQ_ASSERTED_BIT)) + outb(QUANCOM_IRQ_ENABLE_BIT, nec7210_iobase(priv) + + QUANCOM_IRQ_CONTROL_STATUS_REG); + break; + default: + break; + } + return cb7210_locked_internal_interrupt(arg); +} + +irqreturn_t cb7210_internal_interrupt(gpib_board_t *board) +{ + int hs_status, status1, status2; + struct cb7210_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int clear_bits; + + if ((priv->hs_mode_bits & HS_ENABLE_MASK)) { + status1 = 0; + hs_status = cb7210_read_byte(priv, HS_STATUS); + } else { + hs_status = 0; + status1 = read_byte(nec_priv, ISR1); + } + status2 = read_byte(nec_priv, ISR2); + nec7210_interrupt_have_status(board, nec_priv, status1, status2); + + dev_dbg(board->gpib_dev, "cb7210: status 0x%x, mode 0x%x\n", hs_status, priv->hs_mode_bits); + + clear_bits = 0; + + if (hs_status & HS_HALF_FULL) { + if (priv->hs_mode_bits & HS_TX_ENABLE) + priv->out_fifo_half_empty = 1; + else if (priv->hs_mode_bits & HS_RX_ENABLE) + priv->in_fifo_half_full = 1; + clear_bits |= HS_CLR_HF_INT; + } + + if (hs_status & HS_SRQ_INT) { + set_bit(SRQI_NUM, &board->status); + clear_bits |= HS_CLR_SRQ_INT; + } + + if ((hs_status & HS_EOI_INT)) { + clear_bits |= HS_CLR_EOI_EMPTY_INT; + set_bit(RECEIVED_END_BN, &nec_priv->state); + if ((nec_priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE) + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + + if ((priv->hs_mode_bits & HS_TX_ENABLE) && + (hs_status & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) == 0) + clear_bits |= HS_CLR_EOI_EMPTY_INT; + + if (clear_bits) { + cb7210_write_byte(priv, priv->hs_mode_bits | clear_bits, HS_MODE); + cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE); + wake_up_interruptible(&board->wait); + } + + return IRQ_HANDLED; +} + +irqreturn_t cb7210_locked_internal_interrupt(gpib_board_t *board) +{ + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = cb7210_internal_interrupt(board); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +irqreturn_t cb7210_interrupt(int irq, void *arg) +{ + return cb7210_internal_interrupt(arg); +} + +static int cb_pci_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int cb_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void cb_pci_detach(gpib_board_t *board); +static void cb_isa_detach(gpib_board_t *board); + +// wrappers for interface functions +int cb7210_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +int cb7210_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +int cb7210_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +int cb7210_take_control(gpib_board_t *board, int synchronous) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +int cb7210_go_to_standby(gpib_board_t *board) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +void cb7210_request_system_control(gpib_board_t *board, int request_control) +{ + struct cb7210_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + if (request_control) + priv->hs_mode_bits |= HS_SYS_CONTROL; + else + priv->hs_mode_bits &= ~HS_SYS_CONTROL; + + cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE); + nec7210_request_system_control(board, nec_priv, request_control); +} + +void cb7210_interface_clear(gpib_board_t *board, int assert) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +void cb7210_remote_enable(gpib_board_t *board, int enable) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +int cb7210_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +void cb7210_disable_eos(gpib_board_t *board) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +unsigned int cb7210_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +int cb7210_primary_address(gpib_board_t *board, unsigned int address) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +int cb7210_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +int cb7210_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +void cb7210_parallel_poll_configure(gpib_board_t *board, uint8_t configuration) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); +} + +void cb7210_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +void cb7210_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +uint8_t cb7210_serial_poll_status(gpib_board_t *board) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +void cb7210_return_to_local(gpib_board_t *board) +{ + struct cb7210_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + write_byte(nec_priv, AUX_RTL2, AUXMR); + udelay(1); + write_byte(nec_priv, AUX_RTL, AUXMR); +} + +gpib_interface_t cb_pci_unaccel_interface = { +name: "cbi_pci_unaccel", +attach : cb_pci_attach, +detach : cb_pci_detach, +read : cb7210_read, +write : cb7210_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_pci_accel_interface = { +name: "cbi_pci_accel", +attach : cb_pci_attach, +detach : cb_pci_detach, +read : cb7210_accel_read, +write : cb7210_accel_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_pci_interface = { +name: "cbi_pci", +attach : cb_pci_attach, +detach : cb_pci_detach, +read : cb7210_accel_read, +write : cb7210_accel_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_isa_unaccel_interface = { +name: "cbi_isa_unaccel", +attach : cb_isa_attach, +detach : cb_isa_detach, +read : cb7210_read, +write : cb7210_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_isa_interface = { +name: "cbi_isa", +attach : cb_isa_attach, +detach : cb_isa_detach, +read : cb7210_accel_read, +write : cb7210_accel_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_isa_accel_interface = { +name: "cbi_isa_accel", +attach : cb_isa_attach, +detach : cb_isa_detach, +read : cb7210_accel_read, +write : cb7210_accel_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +static int cb7210_allocate_private(gpib_board_t *board) +{ + struct cb7210_priv *priv; + + board->private_data = kmalloc(sizeof(struct cb7210_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct cb7210_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +void cb7210_generic_detach(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +// generic part of attach functions shared by all cb7210 boards +int cb7210_generic_attach(gpib_board_t *board) +{ + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + + if (cb7210_allocate_private(board)) + return -ENOMEM; + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + nec_priv->read_byte = nec7210_locking_ioport_read_byte; + nec_priv->write_byte = nec7210_locking_ioport_write_byte; + nec_priv->offset = cb7210_reg_offset; + nec_priv->type = CB7210; + return 0; +} + +int cb7210_init(struct cb7210_priv *cb_priv, gpib_board_t *board) +{ + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + + cb7210_write_byte(cb_priv, HS_RESET7210, HS_INT_LEVEL); + cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); + + nec7210_board_reset(nec_priv, board); + cb7210_write_byte(cb_priv, HS_TX_ENABLE | HS_RX_ENABLE | HS_CLR_SRQ_INT | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE); + + cb_priv->hs_mode_bits = HS_HF_INT_EN; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + /* set clock register for maximum (20 MHz) driving frequency + * ICR should be set to clock in megahertz (1-15) and to zero + * for clocks faster than 15 MHz (max 20MHz) + */ + write_byte(nec_priv, ICR | 0, AUXMR); + + if (cb_priv->pci_chip == PCI_CHIP_QUANCOM) { + /* change interrupt polarity */ + nec_priv->auxb_bits |= HR_INV; + write_byte(nec_priv, nec_priv->auxb_bits, AUXMR); + } + nec7210_board_online(nec_priv, board); + + /* poll so we can detect assertion of ATN */ + if (gpib_request_pseudo_irq(board, cb_pci_interrupt)) { + pr_err("pc2_gpib: failed to allocate pseudo_irq\n"); + return -1; + } + return 0; +} + +int cb_pci_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int bits; + int retval; + + retval = cb7210_generic_attach(board); + if (retval) + return retval; + + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + + cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS, + PCI_DEVICE_ID_CBOARDS_PCI_GPIB, NULL); + if (cb_priv->pci_device) + cb_priv->pci_chip = PCI_CHIP_AMCC_S5933; + if (!cb_priv->pci_device) { + cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS, + PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, NULL); + if (cb_priv->pci_device) + cb_priv->pci_chip = PCI_CHIP_AMCC_S5933; + } + if (!cb_priv->pci_device) { + cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_QUANCOM, + PCI_DEVICE_ID_QUANCOM_GPIB, NULL); + if (cb_priv->pci_device) { + cb_priv->pci_chip = PCI_CHIP_QUANCOM; + nec_priv->offset = 4; + } + } + if (!cb_priv->pci_device) { + pr_warn("cb7210: no supported boards found.\n"); + return -1; + } + + if (pci_enable_device(cb_priv->pci_device)) { + pr_err("cb7210: error enabling pci device\n"); + return -1; + } + + if (pci_request_regions(cb_priv->pci_device, "cb7210")) + return -1; + switch (cb_priv->pci_chip) { + case PCI_CHIP_AMCC_S5933: + cb_priv->amcc_iobase = pci_resource_start(cb_priv->pci_device, 0); + nec_priv->iobase = (void *)(pci_resource_start(cb_priv->pci_device, 1)); + cb_priv->fifo_iobase = pci_resource_start(cb_priv->pci_device, 2); + break; + case PCI_CHIP_QUANCOM: + nec_priv->iobase = (void *)(pci_resource_start(cb_priv->pci_device, 0)); + cb_priv->fifo_iobase = (unsigned long)nec_priv->iobase; + break; + default: + pr_err("cb7210: bug! unhandled pci_chip=%i\n", cb_priv->pci_chip); + return -EIO; + } + isr_flags |= IRQF_SHARED; + if (request_irq(cb_priv->pci_device->irq, cb_pci_interrupt, isr_flags, "cb7210", board)) { + pr_err("cb7210: can't request IRQ %d\n", cb_priv->pci_device->irq); + return -1; + } + cb_priv->irq = cb_priv->pci_device->irq; + + switch (cb_priv->pci_chip) { + case PCI_CHIP_AMCC_S5933: + // make sure mailbox flags are clear + inl(cb_priv->amcc_iobase + INCOMING_MAILBOX_REG(3)); + // enable interrupts on amccs5933 chip + bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | INBOX_SELECT_BITS(3) | + INBOX_INTR_CS_BIT; + outl(bits, cb_priv->amcc_iobase + INTCSR_REG); + break; + default: + break; + } + return cb7210_init(cb_priv, board); +} + +void cb_pci_detach(gpib_board_t *board) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cb_priv) { + gpib_free_pseudo_irq(board); + nec_priv = &cb_priv->nec7210_priv; + if (cb_priv->irq) { + // disable amcc interrupts + outl(0, cb_priv->amcc_iobase + INTCSR_REG); + free_irq(cb_priv->irq, board); + } + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + pci_release_regions(cb_priv->pci_device); + } + if (cb_priv->pci_device) + pci_dev_put(cb_priv->pci_device); + } + cb7210_generic_detach(board); +} + +int cb_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + int isr_flags = 0; + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + unsigned int bits; + int retval; + + retval = cb7210_generic_attach(board); + if (retval) + return retval; + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + if (request_region((unsigned long)config->ibbase, cb7210_iosize, "cb7210") == 0) { + pr_err("gpib: ioports starting at 0x%p are already in use\n", config->ibbase); + return -EIO; + } + nec_priv->iobase = config->ibbase; + cb_priv->fifo_iobase = nec7210_iobase(cb_priv); + + bits = irq_bits(config->ibirq); + if (bits == 0) + pr_err("board incapable of using irq %i, try 2-5, 7, 10, or 11\n", config->ibirq); + + // install interrupt handler + if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, "cb7210", board)) { + pr_err("gpib: can't request IRQ %d\n", config->ibirq); + return -EBUSY; + } + cb_priv->irq = config->ibirq; + + return cb7210_init(cb_priv, board); +} + +void cb_isa_detach(gpib_board_t *board) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cb_priv) { + gpib_free_pseudo_irq(board); + nec_priv = &cb_priv->nec7210_priv; + if (cb_priv->irq) + free_irq(cb_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec7210_iobase(cb_priv), cb7210_iosize); + } + } + cb7210_generic_detach(board); +} + +static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id cb7210_pci_table[] = { + {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, cb7210_pci_table); + +static struct pci_driver cb7210_pci_driver = { + .name = "cb7210", + .id_table = cb7210_pci_table, + .probe = &cb7210_pci_probe +}; + +/*************************************************************************** + * Support for computer boards pcmcia-gpib card + * + * Based on gpib PCMCIA client driver written by Claus Schroeter + * (clausi@chemie.fu-berlin.de), which was adapted from the + * pcmcia skeleton example (presumably David Hinds) + ***************************************************************************/ + +#ifdef GPIB_PCMCIA + +#include +#include +#include +#include + +#include +#include + +/* + * All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + * you do not define PCMCIA_DEBUG at all, all the debug code will be + * left out. If you compile with PCMCIA_DEBUG=0, the debug code will + * be present but disabled -- but it can then be enabled for specific + * modules at load time with a 'pc_debug=#' option to insmod. + */ + +#define PCMCIA_DEBUG 1 + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +#define DEBUG(n, args...) do {if (pc_debug > (n)) pr_debug(args); } while (0) +#else +#define DEBUG(args...) +#endif + +/* + * The event() function is this driver's Card Services event handler. + * It will be called by Card Services when an appropriate card status + * event is received. The config() and release() entry points are + * used to configure or release a socket, in response to card insertion + * and ejection events. They are invoked from the gpib event + * handler. + */ + +static int cb_gpib_config(struct pcmcia_device *link); +static void cb_gpib_release(struct pcmcia_device *link); +static int cb_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config); +static void cb_pcmcia_detach(gpib_board_t *board); + +/* + * A linked list of "instances" of the gpib device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + */ + +static struct pcmcia_device *curr_dev; + +/* + * A dev_link_t structure has fields for most things that are needed + * to keep track of a socket, but there will usually be some device + * specific information that also needs to be kept track of. The + * 'priv' pointer in a dev_link_t structure can be used to point to + * a device-specific private data structure, like this. + * + * A driver needs to provide a dev_node_t structure for each device + * on a card. In some cases, there is only one device per card (for + * example, ethernet cards, modems). In other cases, there may be + * many actual or logical devices (SCSI adapters, memory cards with + * multiple partitions). The dev_node_t structures need to be kept + * in a linked list starting at the 'dev' field of a dev_link_t + * structure. We allocate them in the card's private data structure, + * because they generally can't be allocated dynamically. + */ + +struct local_info { + struct pcmcia_device *p_dev; + gpib_board_t *dev; +}; + +/* + * gpib_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ + +static int cb_gpib_probe(struct pcmcia_device *link) +{ + struct local_info *info; + +// int ret, i; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /* Allocate space for private device-specific data */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + /* The io structure describes IO port mapping */ + link->resource[0]->end = 16; + link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + link->resource[1]->end = 16; + link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; + link->io_lines = 10; + + /* General socket configuration */ + link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + link->config_index = 1; + link->config_regs = PRESENT_OPTION; + + /* Register with Card Services */ + curr_dev = link; + return cb_gpib_config(link); +} /* gpib_attach */ + +/* + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ + +static void cb_gpib_remove(struct pcmcia_device *link) +{ + struct local_info *info = link->priv; + //struct gpib_board_t *dev = info->dev; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + if (info->dev) + cb_pcmcia_detach(info->dev); + cb_gpib_release(link); + + //free_netdev(dev); + kfree(info); +} + +static int cb_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) +{ + return pcmcia_request_io(link); +} + +/* + * gpib_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * ethernet device available to the system. + */ + +static int cb_gpib_config(struct pcmcia_device *link) +{ + struct pcmcia_device *handle; + struct local_info *dev; + int retval; + + handle = link; + dev = link->priv; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + retval = pcmcia_loop_config(link, &cb_gpib_config_iteration, NULL); + if (retval) { + dev_warn(&link->dev, "no configuration found\n"); + cb_gpib_release(link); + return -ENODEV; + } + + DEBUG(0, "gpib_cs: manufacturer: 0x%x card: 0x%x\n", link->manf_id, link->card_id); + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + retval = pcmcia_enable_device(link); + if (retval) { + dev_warn(&link->dev, "pcmcia_enable_device failed\n"); + cb_gpib_release(link); + return -ENODEV; + } + + pr_info("gpib device loaded\n"); + return 0; +} /* gpib_config */ + +/* + * After a card is removed, gpib_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ + +static void cb_gpib_release(struct pcmcia_device *link) +{ + DEBUG(0, "%s(0x%p)\n", __func__, link); + pcmcia_disable_device(link); +} + +static int cb_gpib_suspend(struct pcmcia_device *link) +{ + //struct local_info *info = link->priv; + //struct gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + if (link->open) + pr_warn("Device still open ???\n"); + //netif_device_detach(dev); + + return 0; +} + +static int cb_gpib_resume(struct pcmcia_device *link) +{ + //struct local_info *info = link->priv; + //struct gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * printk("Gpib resumed ???\n"); + * //netif_device_attach(dev); + * + */ + return cb_gpib_config(link); +} + +/*====================================================================*/ + +static struct pcmcia_device_id cb_pcmcia_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0005), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, cb_pcmcia_ids); + +static struct pcmcia_driver cb_gpib_cs_driver = { + .owner = THIS_MODULE, + .drv = { .name = "cb_gpib_cs", }, + .id_table = cb_pcmcia_ids, + .probe = cb_gpib_probe, + .remove = cb_gpib_remove, + .suspend = cb_gpib_suspend, + .resume = cb_gpib_resume, +}; + +int cb_pcmcia_init_module(void) +{ + pcmcia_register_driver(&cb_gpib_cs_driver); + return 0; +} + +void cb_pcmcia_cleanup_module(void) +{ + DEBUG(0, "cb_gpib_cs: unloading\n"); + pcmcia_unregister_driver(&cb_gpib_cs_driver); +} + +gpib_interface_t cb_pcmcia_unaccel_interface = { +name: "cbi_pcmcia_unaccel", +attach : cb_pcmcia_attach, +detach : cb_pcmcia_detach, +read : cb7210_read, +write : cb7210_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_pcmcia_interface = { +name: "cbi_pcmcia", +attach : cb_pcmcia_attach, +detach : cb_pcmcia_detach, +read : cb7210_accel_read, +write : cb7210_accel_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +gpib_interface_t cb_pcmcia_accel_interface = { +name: "cbi_pcmcia_accel", +attach : cb_pcmcia_attach, +detach : cb_pcmcia_detach, +read : cb7210_accel_read, +write : cb7210_accel_write, +command : cb7210_command, +take_control : cb7210_take_control, +go_to_standby : cb7210_go_to_standby, +request_system_control : cb7210_request_system_control, +interface_clear : cb7210_interface_clear, +remote_enable : cb7210_remote_enable, +enable_eos : cb7210_enable_eos, +disable_eos : cb7210_disable_eos, +parallel_poll : cb7210_parallel_poll, +parallel_poll_configure : cb7210_parallel_poll_configure, +parallel_poll_response : cb7210_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : cb7210_line_status, +update_status : cb7210_update_status, +primary_address : cb7210_primary_address, +secondary_address : cb7210_secondary_address, +serial_poll_response : cb7210_serial_poll_response, +serial_poll_status : cb7210_serial_poll_status, +t1_delay : cb7210_t1_delay, +return_to_local : cb7210_return_to_local, +}; + +int cb_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + int retval; + + if (!curr_dev) { + pr_err("no cb pcmcia cards found\n"); + return -1; + } + + retval = cb7210_generic_attach(board); + if (retval) + return retval; + + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + + if (request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), + "cb7210") == 0) { + pr_err("gpib: ioports starting at 0x%lx are already in use\n", + (unsigned long)curr_dev->resource[0]->start); + return -EIO; + } + nec_priv->iobase = (void *)(unsigned long)curr_dev->resource[0]->start; + cb_priv->fifo_iobase = curr_dev->resource[0]->start; + + if (request_irq(curr_dev->irq, cb7210_interrupt, IRQF_SHARED, + "cb7210", board)) { + pr_err("cb7210: failed to request IRQ %d\n", curr_dev->irq); + return -1; + } + cb_priv->irq = curr_dev->irq; + + return cb7210_init(cb_priv, board); +} + +void cb_pcmcia_detach(gpib_board_t *board) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cb_priv) { + nec_priv = &cb_priv->nec7210_priv; + gpib_free_pseudo_irq(board); + if (cb_priv->irq) + free_irq(cb_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec7210_iobase(cb_priv), cb7210_iosize); + } + } + cb7210_generic_detach(board); +} + +#endif /* GPIB_PCMCIA */ + +static int __init cb7210_init_module(void) +{ + int err = 0; + int result; + + result = pci_register_driver(&cb7210_pci_driver); + if (result) { + pr_err("cb7210: pci_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&cb_pci_interface, THIS_MODULE); + gpib_register_driver(&cb_isa_interface, THIS_MODULE); + gpib_register_driver(&cb_pci_accel_interface, THIS_MODULE); + gpib_register_driver(&cb_pci_unaccel_interface, THIS_MODULE); + gpib_register_driver(&cb_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&cb_isa_unaccel_interface, THIS_MODULE); + +#ifdef GPIB__PCMCIA + gpib_register_driver(&cb_pcmcia_interface, THIS_MODULE); + gpib_register_driver(&cb_pcmcia_accel_interface, THIS_MODULE); + gpib_register_driver(&cb_pcmcia_unaccel_interface, THIS_MODULE); + err += cb_pcmcia_init_module(); +#endif + if (err) + return -1; + + return 0; +} + +static void __exit cb7210_exit_module(void) +{ + gpib_unregister_driver(&cb_pci_interface); + gpib_unregister_driver(&cb_isa_interface); + gpib_unregister_driver(&cb_pci_accel_interface); + gpib_unregister_driver(&cb_pci_unaccel_interface); + gpib_unregister_driver(&cb_isa_accel_interface); + gpib_unregister_driver(&cb_isa_unaccel_interface); +#ifdef GPIB_PCMCIA + gpib_unregister_driver(&cb_pcmcia_interface); + gpib_unregister_driver(&cb_pcmcia_accel_interface); + gpib_unregister_driver(&cb_pcmcia_unaccel_interface); + cb_pcmcia_cleanup_module(); +#endif + + pci_unregister_driver(&cb7210_pci_driver); +} + +module_init(cb7210_init_module); +module_exit(cb7210_exit_module); diff --git a/drivers/staging/gpib/cb7210/cb7210.h b/drivers/staging/gpib/cb7210/cb7210.h new file mode 100644 index 0000000000000..4ad976de2b684 --- /dev/null +++ b/drivers/staging/gpib/cb7210/cb7210.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "nec7210.h" +#include "gpibP.h" +#include "amccs5933.h" + +#include +#include + +enum { + PCI_DEVICE_ID_CBOARDS_PCI_GPIB = 0x6, + PCI_DEVICE_ID_CBOARDS_CPCI_GPIB = 0xe, +}; + +enum pci_chip { + PCI_CHIP_NONE = 0, + PCI_CHIP_AMCC_S5933, + PCI_CHIP_QUANCOM +}; + +// struct which defines private_data for cb7210 boards +struct cb7210_priv { + struct nec7210_priv nec7210_priv; + struct pci_dev *pci_device; + // base address of amccs5933 pci chip + unsigned long amcc_iobase; + unsigned long fifo_iobase; + unsigned int irq; + enum pci_chip pci_chip; + u8 hs_mode_bits; + unsigned out_fifo_half_empty : 1; + unsigned in_fifo_half_full : 1; +}; + +// interfaces +extern gpib_interface_t cb_pcmcia_interface; +extern gpib_interface_t cb_pcmcia_accel_interface; +extern gpib_interface_t cb_pcmcia_unaccel_interface; + +// interrupt service routines +irqreturn_t cb_pci_interrupt(int irq, void *arg); +irqreturn_t cb7210_interrupt(int irq, void *arg); +irqreturn_t cb7210_internal_interrupt(gpib_board_t *board); + +// interface functions +int cb7210_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +int cb7210_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +int cb7210_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +int cb7210_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +int cb7210_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written); +int cb7210_take_control(gpib_board_t *board, int synchronous); +int cb7210_go_to_standby(gpib_board_t *board); +void cb7210_request_system_control(gpib_board_t *board, int request_control); +void cb7210_interface_clear(gpib_board_t *board, int assert); +void cb7210_remote_enable(gpib_board_t *board, int enable); +int cb7210_enable_eos(gpib_board_t *board, uint8_t eos_byte, + int compare_8_bits); +void cb7210_disable_eos(gpib_board_t *board); +unsigned int cb7210_update_status(gpib_board_t *board, unsigned int clear_mask); +int cb7210_primary_address(gpib_board_t *board, unsigned int address); +int cb7210_secondary_address(gpib_board_t *board, unsigned int address, + int enable); +int cb7210_parallel_poll(gpib_board_t *board, uint8_t *result); +void cb7210_serial_poll_response(gpib_board_t *board, uint8_t status); +uint8_t cb7210_serial_poll_status(gpib_board_t *board); +void cb7210_parallel_poll_configure(gpib_board_t *board, uint8_t configuration); +void cb7210_parallel_poll_response(gpib_board_t *board, int ist); +int cb7210_line_status(const gpib_board_t *board); +unsigned int cb7210_t1_delay(gpib_board_t *board, unsigned int nano_sec); +void cb7210_return_to_local(gpib_board_t *board); + +// utility functions +void cb7210_generic_detach(gpib_board_t *board); +int cb7210_generic_attach(gpib_board_t *board); +int cb7210_init(struct cb7210_priv *priv, gpib_board_t *board); + +// pcmcia init/cleanup +int cb_pcmcia_init_module(void); +void cb_pcmcia_cleanup_module(void); + +// pci-gpib register offset +static const int cb7210_reg_offset = 1; + +// uses 10 ioports +static const int cb7210_iosize = 10; + +// fifo size in bytes +static const int cb7210_fifo_size = 2048; +static const int cb7210_fifo_width = 2; + +// cb7210 specific registers and bits +enum cb7210_regs { + BUS_STATUS = 0x7, +}; + +enum cb7210_page_in { + BUS_STATUS_PAGE = 1, +}; + +enum hs_regs { + //write registers + HS_MODE = 0x8, /* HS_MODE register */ + HS_INT_LEVEL = 0x9, /* HS_INT_LEVEL register */ + //read registers + HS_STATUS = 0x8, /* HS_STATUS register */ +}; + +static inline unsigned long nec7210_iobase(const struct cb7210_priv *cb_priv) +{ + return (unsigned long)(cb_priv->nec7210_priv.iobase); +} + +static inline int cb7210_page_in_bits(unsigned int page) +{ + return 0x50 | (page & 0xf); +} + +static inline uint8_t cb7210_paged_read_byte(struct cb7210_priv *cb_priv, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset); + udelay(1); + retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); + return retval; +} + +// don't use for register_num < 8, since it doesn't lock +static inline uint8_t cb7210_read_byte(const struct cb7210_priv *cb_priv, + enum hs_regs register_num) +{ + const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + u8 retval; + + retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset); + return retval; +} + +static inline void cb7210_paged_write_byte(struct cb7210_priv *cb_priv, uint8_t data, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset); + udelay(1); + outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); +} + +// don't use for register_num < 8, since it doesn't lock +static inline void cb7210_write_byte(const struct cb7210_priv *cb_priv, uint8_t data, + enum hs_regs register_num) +{ + const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + + outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset); +} + +enum bus_status_bits { + BSR_ATN_BIT = 0x1, + BSR_EOI_BIT = 0x2, + BSR_SRQ_BIT = 0x4, + BSR_IFC_BIT = 0x8, + BSR_REN_BIT = 0x10, + BSR_DAV_BIT = 0x20, + BSR_NRFD_BIT = 0x40, + BSR_NDAC_BIT = 0x80, +}; + +/* CBI 488.2 HS control */ + +/* when both bit 0 and 1 are set, it + * 1 clears the transmit state machine to an initial condition + * 2 clears any residual interrupts left latched on cbi488.2 + * 3 resets all control bits in HS_MODE to zero + * 4 enables TX empty interrupts + * when both bit 0 and 1 are zero, then the high speed mode is disabled + */ +enum hs_mode_bits { + HS_ENABLE_MASK = 0x3, + HS_TX_ENABLE = (1 << 0), + HS_RX_ENABLE = (1 << 1), + HS_HF_INT_EN = (1 << 3), + HS_CLR_SRQ_INT = (1 << 4), + HS_CLR_EOI_EMPTY_INT = (1 << 5), + HS_CLR_HF_INT = (1 << 6), + HS_SYS_CONTROL = (1 << 7), +}; + +/* CBI 488.2 status */ +enum hs_status_bits { + HS_FIFO_FULL = (1 << 0), + HS_HALF_FULL = (1 << 1), + HS_SRQ_INT = (1 << 2), + HS_EOI_INT = (1 << 3), + HS_TX_MSB_NOT_EMPTY = (1 << 4), + HS_RX_MSB_NOT_EMPTY = (1 << 5), + HS_TX_LSB_NOT_EMPTY = (1 << 6), + HS_RX_LSB_NOT_EMPTY = (1 << 7), +}; + +/* CBI488.2 hs_int_level register */ +enum hs_int_level_bits { + HS_RESET7210 = (1 << 7), +}; + +static inline unsigned int irq_bits(unsigned int irq) +{ + switch (irq) { + case 2: + case 3: + case 4: + case 5: + return irq - 1; + case 7: + return 0x5; + case 10: + return 0x6; + case 11: + return 0x7; + default: + return 0; + } +} + +enum cb7210_aux_cmds { +/* AUX_RTL2 is an undocumented aux command which causes cb7210 to assert + * (and keep asserted) local rtl message. This is used in conjunction + * with the (stupid) cb7210 implementation + * of the normal nec7210 AUX_RTL aux command, which + * causes the rtl message to toggle between on and off. + */ + AUX_RTL2 = 0xd, + AUX_LO_SPEED = 0x40, + AUX_HI_SPEED = 0x41, +}; diff --git a/drivers/staging/gpib/cec/Makefile b/drivers/staging/gpib/cec/Makefile new file mode 100644 index 0000000000000..f4638628ff294 --- /dev/null +++ b/drivers/staging/gpib/cec/Makefile @@ -0,0 +1,3 @@ + +obj-m += cec_gpib.o + diff --git a/drivers/staging/gpib/cec/cec.h b/drivers/staging/gpib/cec/cec.h new file mode 100644 index 0000000000000..352cf83d8328f --- /dev/null +++ b/drivers/staging/gpib/cec/cec.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "nec7210.h" +#include "gpibP.h" +#include "plx9050.h" + +struct cec_priv { + struct nec7210_priv nec7210_priv; + struct pci_dev *pci_device; + // base address for plx9052 pci chip + unsigned long plx_iobase; + unsigned int irq; +}; + +// interfaces +extern gpib_interface_t cec_pci_interface; +extern gpib_interface_t cec_pcmcia_interface; + +// interface functions +int cec_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read); +int cec_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written); +int cec_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written); +int cec_take_control(gpib_board_t *board, int synchronous); +int cec_go_to_standby(gpib_board_t *board); +void cec_request_system_control(gpib_board_t *board, int request_control); +void cec_interface_clear(gpib_board_t *board, int assert); +void cec_remote_enable(gpib_board_t *board, int enable); +int cec_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits); +void cec_disable_eos(gpib_board_t *board); +unsigned int cec_update_status(gpib_board_t *board, unsigned int clear_mask); +int cec_primary_address(gpib_board_t *board, unsigned int address); +int cec_secondary_address(gpib_board_t *board, unsigned int address, int enable); +int cec_parallel_poll(gpib_board_t *board, uint8_t *result); +void cec_parallel_poll_configure(gpib_board_t *board, uint8_t configuration); +void cec_parallel_poll_response(gpib_board_t *board, int ist); +void cec_serial_poll_response(gpib_board_t *board, uint8_t status); +void cec_return_to_local(gpib_board_t *board); + +// interrupt service routines +irqreturn_t cec_interrupt(int irq, void *arg); + +// utility functions +void cec_free_private(gpib_board_t *board); +int cec_generic_attach(gpib_board_t *board); +void cec_init(struct cec_priv *priv, const gpib_board_t *board); + +// offset between consecutive nec7210 registers +static const int cec_reg_offset = 1; diff --git a/drivers/staging/gpib/cec/cec_gpib.c b/drivers/staging/gpib/cec/cec_gpib.c new file mode 100644 index 0000000000000..3dc933deb4018 --- /dev/null +++ b/drivers/staging/gpib/cec/cec_gpib.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "cec.h" +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for CEC PCI and PCMCIA boards"); + +/* + * GPIB interrupt service routines + */ + +irqreturn_t cec_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct cec_priv *priv = board->private_data; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = nec7210_interrupt(board, &priv->nec7210_priv); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +#define CEC_VENDOR_ID 0x12fc +#define CEC_DEV_ID 0x5cec +#define CEC_SUBID 0x9050 + +static int cec_pci_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void cec_pci_detach(gpib_board_t *board); + +// wrappers for interface functions +int cec_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +int cec_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +int cec_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +int cec_take_control(gpib_board_t *board, int synchronous) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +int cec_go_to_standby(gpib_board_t *board) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +void cec_request_system_control(gpib_board_t *board, int request_control) +{ + struct cec_priv *priv = board->private_data; + + nec7210_request_system_control(board, &priv->nec7210_priv, request_control); +} + +void cec_interface_clear(gpib_board_t *board, int assert) +{ + struct cec_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +void cec_remote_enable(gpib_board_t *board, int enable) +{ + struct cec_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +int cec_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +void cec_disable_eos(gpib_board_t *board) +{ + struct cec_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +unsigned int cec_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +int cec_primary_address(gpib_board_t *board, unsigned int address) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +int cec_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +int cec_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +void cec_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + struct cec_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); +} + +void cec_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct cec_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +void cec_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct cec_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static uint8_t cec_serial_poll_status(gpib_board_t *board) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static unsigned int cec_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec); +} + +void cec_return_to_local(gpib_board_t *board) +{ + struct cec_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +gpib_interface_t cec_pci_interface = { +name: "cec_pci", +attach : cec_pci_attach, +detach : cec_pci_detach, +read : cec_read, +write : cec_write, +command : cec_command, +take_control : cec_take_control, +go_to_standby : cec_go_to_standby, +request_system_control : cec_request_system_control, +interface_clear : cec_interface_clear, +remote_enable : cec_remote_enable, +enable_eos : cec_enable_eos, +disable_eos : cec_disable_eos, +parallel_poll : cec_parallel_poll, +parallel_poll_configure : cec_parallel_poll_configure, +parallel_poll_response : cec_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, //XXX +update_status : cec_update_status, +primary_address : cec_primary_address, +secondary_address : cec_secondary_address, +serial_poll_response : cec_serial_poll_response, +serial_poll_status : cec_serial_poll_status, +t1_delay : cec_t1_delay, +return_to_local : cec_return_to_local, +}; + +static int cec_allocate_private(gpib_board_t *board) +{ + struct cec_priv *priv; + + board->private_data = kmalloc(sizeof(struct cec_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct cec_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +void cec_free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +int cec_generic_attach(gpib_board_t *board) +{ + struct cec_priv *cec_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + + if (cec_allocate_private(board)) + return -ENOMEM; + cec_priv = board->private_data; + nec_priv = &cec_priv->nec7210_priv; + nec_priv->read_byte = nec7210_ioport_read_byte; + nec_priv->write_byte = nec7210_ioport_write_byte; + nec_priv->offset = cec_reg_offset; + nec_priv->type = NEC7210; // guess + return 0; +} + +void cec_init(struct cec_priv *cec_priv, const gpib_board_t *board) +{ + struct nec7210_priv *nec_priv = &cec_priv->nec7210_priv; + + nec7210_board_reset(nec_priv, board); + + /* set internal counter register for 8 MHz input clock */ + write_byte(nec_priv, ICR | 8, AUXMR); + + nec7210_board_online(nec_priv, board); +} + +int cec_pci_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct cec_priv *cec_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + + retval = cec_generic_attach(board); + if (retval) + return retval; + + cec_priv = board->private_data; + nec_priv = &cec_priv->nec7210_priv; + + // find board + cec_priv->pci_device = NULL; + while ((cec_priv->pci_device = + gpib_pci_get_device(config, CEC_VENDOR_ID, + CEC_DEV_ID, cec_priv->pci_device))) { + // check for board with plx9050 controller + if (cec_priv->pci_device->subsystem_device == CEC_SUBID) + break; + } + if (!cec_priv->pci_device) { + pr_err("gpib: no cec PCI board found\n"); + return -1; + } + + if (pci_enable_device(cec_priv->pci_device)) { + pr_err("error enabling pci device\n"); + return -1; + } + + if (pci_request_regions(cec_priv->pci_device, "cec-gpib")) + return -1; + + cec_priv->plx_iobase = pci_resource_start(cec_priv->pci_device, 1); + pr_info(" plx9050 base address 0x%lx\n", cec_priv->plx_iobase); + nec_priv->iobase = (void *)(pci_resource_start(cec_priv->pci_device, 3)); + pr_info(" nec7210 base address 0x%p\n", nec_priv->iobase); + + isr_flags |= IRQF_SHARED; + if (request_irq(cec_priv->pci_device->irq, cec_interrupt, isr_flags, "pci-gpib", board)) { + pr_err("gpib: can't request IRQ %d\n", cec_priv->pci_device->irq); + return -1; + } + cec_priv->irq = cec_priv->pci_device->irq; + if (gpib_request_pseudo_irq(board, cec_interrupt)) { + pr_err("cec: failed to allocate pseudo irq\n"); + return -1; + } + cec_init(cec_priv, board); + + // enable interrupts on plx chip + outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT, + cec_priv->plx_iobase + PLX9050_INTCSR_REG); + + return 0; +} + +void cec_pci_detach(gpib_board_t *board) +{ + struct cec_priv *cec_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cec_priv) { + nec_priv = &cec_priv->nec7210_priv; + gpib_free_pseudo_irq(board); + if (cec_priv->irq) { + // disable plx9050 interrupts + outl(0, cec_priv->plx_iobase + PLX9050_INTCSR_REG); + free_irq(cec_priv->irq, board); + } + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + pci_release_regions(cec_priv->pci_device); + } + if (cec_priv->pci_device) + pci_dev_put(cec_priv->pci_device); + } + cec_free_private(board); +} + +static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id cec_pci_table[] = { + {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 }, + {0} +}; +MODULE_DEVICE_TABLE(pci, cec_pci_table); + +static struct pci_driver cec_pci_driver = { + .name = "cec_gpib", + .id_table = cec_pci_table, + .probe = &cec_pci_probe +}; + +static int __init cec_init_module(void) +{ + int result; + + result = pci_register_driver(&cec_pci_driver); + if (result) { + pr_err("cec_gpib: pci_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&cec_pci_interface, THIS_MODULE); + + return 0; +} + +static void cec_exit_module(void) +{ + gpib_unregister_driver(&cec_pci_interface); + + pci_unregister_driver(&cec_pci_driver); +} + +module_init(cec_init_module); +module_exit(cec_exit_module); diff --git a/drivers/staging/gpib/common/Makefile b/drivers/staging/gpib/common/Makefile new file mode 100644 index 0000000000000..0c4c77bea75b3 --- /dev/null +++ b/drivers/staging/gpib/common/Makefile @@ -0,0 +1,6 @@ + +obj-m += gpib_common.o + +gpib_common-objs := gpib_os.o iblib.o + + diff --git a/drivers/staging/gpib/common/gpib_os.c b/drivers/staging/gpib/common/gpib_os.c new file mode 100644 index 0000000000000..405237d8cb479 --- /dev/null +++ b/drivers/staging/gpib/common/gpib_os.c @@ -0,0 +1,2328 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2004 by Frank Mori Hess + *************************************************************************** + */ + +#include "ibsys.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB base support"); +MODULE_ALIAS_CHARDEV_MAJOR(GPIB_CODE); + +static int board_type_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, unsigned long arg); +static int read_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, + unsigned long arg); +static int write_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, + unsigned long arg); +static int command_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, + unsigned long arg); +static int open_dev_ioctl(struct file *filep, gpib_board_t *board, unsigned long arg); +static int close_dev_ioctl(struct file *filep, gpib_board_t *board, unsigned long arg); +static int serial_poll_ioctl(gpib_board_t *board, unsigned long arg); +static int wait_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, unsigned long arg); +static int parallel_poll_ioctl(gpib_board_t *board, unsigned long arg); +static int online_ioctl(gpib_board_t *board, unsigned long arg); +static int remote_enable_ioctl(gpib_board_t *board, unsigned long arg); +static int take_control_ioctl(gpib_board_t *board, unsigned long arg); +static int line_status_ioctl(gpib_board_t *board, unsigned long arg); +static int pad_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg); +static int sad_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg); +static int eos_ioctl(gpib_board_t *board, unsigned long arg); +static int request_service_ioctl(gpib_board_t *board, unsigned long arg); +static int request_service2_ioctl(gpib_board_t *board, unsigned long arg); +static int iobase_ioctl(gpib_board_config_t *config, unsigned long arg); +static int irq_ioctl(gpib_board_config_t *config, unsigned long arg); +static int dma_ioctl(gpib_board_config_t *config, unsigned long arg); +static int autospoll_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg); +static int mutex_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg); +static int timeout_ioctl(gpib_board_t *board, unsigned long arg); +static int status_bytes_ioctl(gpib_board_t *board, unsigned long arg); +static int board_info_ioctl(const gpib_board_t *board, unsigned long arg); +static int ppc_ioctl(gpib_board_t *board, unsigned long arg); +static int set_local_ppoll_mode_ioctl(gpib_board_t *board, unsigned long arg); +static int get_local_ppoll_mode_ioctl(gpib_board_t *board, unsigned long arg); +static int query_board_rsv_ioctl(gpib_board_t *board, unsigned long arg); +static int interface_clear_ioctl(gpib_board_t *board, unsigned long arg); +static int select_pci_ioctl(gpib_board_config_t *config, unsigned long arg); +static int select_device_path_ioctl(gpib_board_config_t *config, unsigned long arg); +static int event_ioctl(gpib_board_t *board, unsigned long arg); +static int request_system_control_ioctl(gpib_board_t *board, unsigned long arg); +static int t1_delay_ioctl(gpib_board_t *board, unsigned long arg); + +static int cleanup_open_devices(gpib_file_private_t *file_priv, gpib_board_t *board); + +static int pop_gpib_event_nolock(gpib_board_t *board, gpib_event_queue_t *queue, short *event_type); + +/* + * Timer functions + */ + +/* Watchdog timeout routine */ + +static void watchdog_timeout(struct timer_list *t) +{ + gpib_board_t *board = from_timer(board, t, timer); + + set_bit(TIMO_NUM, &board->status); + wake_up_interruptible(&board->wait); +} + +/* install timer interrupt handler */ +void os_start_timer(gpib_board_t *board, unsigned int usec_timeout) +/* Starts the timeout task */ +{ + if (timer_pending(&board->timer)) { + pr_err("gpib: bug! timer already running?\n"); + return; + } + clear_bit(TIMO_NUM, &board->status); + + if (usec_timeout > 0) { + board->timer.function = watchdog_timeout; + /* set number of ticks */ + mod_timer(&board->timer, jiffies + usec_to_jiffies(usec_timeout)); + } +} + +void os_remove_timer(gpib_board_t *board) +/* Removes the timeout task */ +{ + if (timer_pending(&board->timer)) + del_timer_sync(&board->timer); +} + +int io_timed_out(gpib_board_t *board) +{ + if (test_bit(TIMO_NUM, &board->status)) + return 1; + return 0; +} + +void writeb_wrapper(unsigned int value, void *address) +{ + writeb(value, address); +}; +EXPORT_SYMBOL(writeb_wrapper); + +void writew_wrapper(unsigned int value, void *address) +{ + writew(value, address); +}; +EXPORT_SYMBOL(writew_wrapper); + +unsigned int readb_wrapper(void *address) +{ + return readb(address); +}; +EXPORT_SYMBOL(readb_wrapper); + +unsigned int readw_wrapper(void *address) +{ + return readw(address); +}; +EXPORT_SYMBOL(readw_wrapper); + +#ifdef CONFIG_HAS_IOPORT +void outb_wrapper(unsigned int value, void *address) +{ + outb(value, (unsigned long)(address)); +}; +EXPORT_SYMBOL(outb_wrapper); + +void outw_wrapper(unsigned int value, void *address) +{ + outw(value, (unsigned long)(address)); +}; +EXPORT_SYMBOL(outw_wrapper); + +unsigned int inb_wrapper(void *address) +{ + return inb((unsigned long)(address)); +}; +EXPORT_SYMBOL(inb_wrapper); + +unsigned int inw_wrapper(void *address) +{ + return inw((unsigned long)(address)); +}; +EXPORT_SYMBOL(inw_wrapper); +#endif + +/* this is a function instead of a constant because of Suse + * defining HZ to be a function call to get_hz() + */ +static inline int pseudo_irq_period(void) +{ + return (HZ + 99) / 100; +} + +static void pseudo_irq_handler(struct timer_list *t) +{ + struct gpib_pseudo_irq *pseudo_irq = from_timer(pseudo_irq, t, timer); + + if (pseudo_irq->handler) + pseudo_irq->handler(0, pseudo_irq->board); + else + pr_err("gpib: bug! pseudo_irq.handler is NULL\n"); + + if (atomic_read(&pseudo_irq->active)) + mod_timer(&pseudo_irq->timer, jiffies + pseudo_irq_period()); +} + +int gpib_request_pseudo_irq(gpib_board_t *board, irqreturn_t (*handler)(int, void *)) +{ + if (timer_pending(&board->pseudo_irq.timer) || board->pseudo_irq.handler) { + pr_err("gpib: only one pseudo interrupt per board allowed\n"); + return -1; + } + + board->pseudo_irq.handler = handler; + board->pseudo_irq.timer.function = pseudo_irq_handler; + board->pseudo_irq.board = board; + + atomic_set(&board->pseudo_irq.active, 1); + + mod_timer(&board->pseudo_irq.timer, jiffies + pseudo_irq_period()); + + return 0; +} +EXPORT_SYMBOL(gpib_request_pseudo_irq); + +void gpib_free_pseudo_irq(gpib_board_t *board) +{ + atomic_set(&board->pseudo_irq.active, 0); + + del_timer_sync(&board->pseudo_irq.timer); + board->pseudo_irq.handler = NULL; +} +EXPORT_SYMBOL(gpib_free_pseudo_irq); + +static const unsigned int serial_timeout = 1000000; + +unsigned int num_status_bytes(const gpib_status_queue_t *dev) +{ + if (!dev) + return 0; + return dev->num_status_bytes; +} + +// push status byte onto back of status byte fifo +int push_status_byte(gpib_board_t *board, gpib_status_queue_t *device, u8 poll_byte) +{ + struct list_head *head = &device->status_bytes; + status_byte_t *status; + static const unsigned int max_num_status_bytes = 1024; + int retval; + + if (num_status_bytes(device) >= max_num_status_bytes) { + u8 lost_byte; + + device->dropped_byte = 1; + retval = pop_status_byte(board, device, &lost_byte); + if (retval < 0) + return retval; + } + + status = kmalloc(sizeof(status_byte_t), GFP_KERNEL); + if (!status) + return -ENOMEM; + + INIT_LIST_HEAD(&status->list); + status->poll_byte = poll_byte; + + list_add_tail(&status->list, head); + + device->num_status_bytes++; + + dev_dbg(board->gpib_dev, "pushed status byte 0x%x, %i in queue\n", + (int)poll_byte, num_status_bytes(device)); + + return 0; +} + +// pop status byte from front of status byte fifo +int pop_status_byte(gpib_board_t *board, gpib_status_queue_t *device, u8 *poll_byte) +{ + struct list_head *head = &device->status_bytes; + struct list_head *front = head->next; + status_byte_t *status; + + if (num_status_bytes(device) == 0) + return -EIO; + + if (front == head) + return -EIO; + + if (device->dropped_byte) { + device->dropped_byte = 0; + return -EPIPE; + } + + status = list_entry(front, status_byte_t, list); + *poll_byte = status->poll_byte; + + list_del(front); + kfree(status); + + device->num_status_bytes--; + + dev_dbg(board->gpib_dev, "popped status byte 0x%x, %i in queue\n", + (int)*poll_byte, num_status_bytes(device)); + + return 0; +} + +gpib_status_queue_t *get_gpib_status_queue(gpib_board_t *board, unsigned int pad, int sad) +{ + gpib_status_queue_t *device; + struct list_head *list_ptr; + const struct list_head *head = &board->device_list; + + for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { + device = list_entry(list_ptr, gpib_status_queue_t, list); + if (gpib_address_equal(device->pad, device->sad, pad, sad)) + return device; + } + + return NULL; +} + +int get_serial_poll_byte(gpib_board_t *board, unsigned int pad, int sad, unsigned int usec_timeout, + uint8_t *poll_byte) +{ + gpib_status_queue_t *device; + + dev_dbg(board->gpib_dev, "%s:()\n", __func__); + + device = get_gpib_status_queue(board, pad, sad); + if (num_status_bytes(device)) + return pop_status_byte(board, device, poll_byte); + else + return dvrsp(board, pad, sad, usec_timeout, poll_byte); +} + +int autopoll_all_devices(gpib_board_t *board) +{ + int retval; + + dev_dbg(board->gpib_dev, "entering %s()\n", __func__); + if (mutex_lock_interruptible(&board->user_mutex)) + return -ERESTARTSYS; + if (mutex_lock_interruptible(&board->big_gpib_mutex)) { + mutex_unlock(&board->user_mutex); + return -ERESTARTSYS; + } + + dev_dbg(board->gpib_dev, "autopoll has board lock\n"); + + retval = serial_poll_all(board, serial_timeout); + if (retval < 0) { + mutex_unlock(&board->big_gpib_mutex); + mutex_unlock(&board->user_mutex); + return retval; + } + + dev_dbg(board->gpib_dev, "%s complete\n", __func__); + /* need to wake wait queue in case someone is + * waiting on RQS + */ + wake_up_interruptible(&board->wait); + mutex_unlock(&board->big_gpib_mutex); + mutex_unlock(&board->user_mutex); + + return retval; +} + +static int setup_serial_poll(gpib_board_t *board, unsigned int usec_timeout) +{ + u8 cmd_string[8]; + int i; + size_t bytes_written; + int ret; + + dev_dbg(board->gpib_dev, "entering %s()\n", __func__); + + os_start_timer(board, usec_timeout); + ret = ibcac(board, 1, 1); + if (ret < 0) { + os_remove_timer(board); + return ret; + } + + i = 0; + cmd_string[i++] = UNL; + cmd_string[i++] = MLA(board->pad); /* controller's listen address */ + if (board->sad >= 0) + cmd_string[i++] = MSA(board->sad); + cmd_string[i++] = SPE; //serial poll enable + + ret = board->interface->command(board, cmd_string, i, &bytes_written); + if (ret < 0 || bytes_written < i) { + pr_err("gpib: failed to setup serial poll\n"); + os_remove_timer(board); + return -EIO; + } + os_remove_timer(board); + + return 0; +} + +static int read_serial_poll_byte(gpib_board_t *board, unsigned int pad, + int sad, unsigned int usec_timeout, uint8_t *result) +{ + u8 cmd_string[8]; + int end_flag; + int ret; + int i; + size_t nbytes; + + dev_dbg(board->gpib_dev, "entering %s(), pad=%i sad=%i\n", __func__, pad, sad); + + os_start_timer(board, usec_timeout); + ret = ibcac(board, 1, 1); + if (ret < 0) { + os_remove_timer(board); + return ret; + } + + i = 0; + // send talk address + cmd_string[i++] = MTA(pad); + if (sad >= 0) + cmd_string[i++] = MSA(sad); + + ret = board->interface->command(board, cmd_string, i, &nbytes); + if (ret < 0 || nbytes < i) { + pr_err("gpib: failed to setup serial poll\n"); + os_remove_timer(board); + return -EIO; + } + + ibgts(board); + + // read poll result + ret = board->interface->read(board, result, 1, &end_flag, &nbytes); + if (ret < 0 || nbytes < 1) { + pr_err("gpib: serial poll failed\n"); + os_remove_timer(board); + return -EIO; + } + os_remove_timer(board); + + return 0; +} + +static int cleanup_serial_poll(gpib_board_t *board, unsigned int usec_timeout) +{ + u8 cmd_string[8]; + int ret; + size_t bytes_written; + + dev_dbg(board->gpib_dev, "entering %s()\n", __func__); + + os_start_timer(board, usec_timeout); + ret = ibcac(board, 1, 1); + if (ret < 0) { + os_remove_timer(board); + return ret; + } + + cmd_string[0] = SPD; /* disable serial poll bytes */ + cmd_string[1] = UNT; + ret = board->interface->command(board, cmd_string, 2, &bytes_written); + if (ret < 0 || bytes_written < 2) { + pr_err("gpib: failed to disable serial poll\n"); + os_remove_timer(board); + return -EIO; + } + os_remove_timer(board); + + return 0; +} + +static int serial_poll_single(gpib_board_t *board, unsigned int pad, int sad, + unsigned int usec_timeout, uint8_t *result) +{ + int retval, cleanup_retval; + + retval = setup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + retval = read_serial_poll_byte(board, pad, sad, usec_timeout, result); + cleanup_retval = cleanup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + if (cleanup_retval < 0) + return retval; + + return 0; +} + +int serial_poll_all(gpib_board_t *board, unsigned int usec_timeout) +{ + int retval = 0; + struct list_head *cur; + const struct list_head *head = NULL; + gpib_status_queue_t *device; + u8 result; + unsigned int num_bytes = 0; + + dev_dbg(board->gpib_dev, "entering %s()\n", __func__); + + head = &board->device_list; + if (head->next == head) + return 0; + + retval = setup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + + for (cur = head->next; cur != head; cur = cur->next) { + device = list_entry(cur, gpib_status_queue_t, list); + retval = read_serial_poll_byte(board, + device->pad, device->sad, usec_timeout, &result); + if (retval < 0) + continue; + if (result & request_service_bit) { + retval = push_status_byte(board, device, result); + if (retval < 0) + continue; + num_bytes++; + } + } + + retval = cleanup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + + return num_bytes; +} + +/* + * DVRSP + * This function performs a serial poll of the device with primary + * address pad and secondary address sad. If the device has no + * secondary address, pass a negative number in for this argument. At the + * end of a successful serial poll the response is returned in result. + * SPD and UNT are sent at the completion of the poll. + */ + +int dvrsp(gpib_board_t *board, unsigned int pad, int sad, + unsigned int usec_timeout, uint8_t *result) +{ + int status = ibstatus(board); + int retval; + + if ((status & CIC) == 0) { + pr_err("gpib: not CIC during serial poll\n"); + return -1; + } + + if (pad > MAX_GPIB_PRIMARY_ADDRESS || sad > MAX_GPIB_SECONDARY_ADDRESS) { + pr_err("gpib: bad address for serial poll"); + return -1; + } + + retval = serial_poll_single(board, pad, sad, usec_timeout, result); + if (io_timed_out(board)) + retval = -ETIMEDOUT; + + return retval; +} + +static gpib_descriptor_t *handle_to_descriptor(const gpib_file_private_t *file_priv, + int handle) +{ + if (handle < 0 || handle >= GPIB_MAX_NUM_DESCRIPTORS) { + pr_err("gpib: invalid handle %i\n", handle); + return NULL; + } + + return file_priv->descriptors[handle]; +} + +static int init_gpib_file_private(gpib_file_private_t *priv) +{ + memset(priv, 0, sizeof(*priv)); + atomic_set(&priv->holding_mutex, 0); + priv->descriptors[0] = kmalloc(sizeof(gpib_descriptor_t), GFP_KERNEL); + if (!priv->descriptors[0]) { + pr_err("gpib: failed to allocate default board descriptor\n"); + return -ENOMEM; + } + init_gpib_descriptor(priv->descriptors[0]); + priv->descriptors[0]->is_board = 1; + mutex_init(&priv->descriptors_mutex); + return 0; +} + +int ibopen(struct inode *inode, struct file *filep) +{ + unsigned int minor = iminor(inode); + gpib_board_t *board; + gpib_file_private_t *priv; + + if (minor >= GPIB_MAX_NUM_BOARDS) { + pr_err("gpib: invalid minor number of device file\n"); + return -ENXIO; + } + + board = &board_array[minor]; + + filep->private_data = kmalloc(sizeof(gpib_file_private_t), GFP_KERNEL); + if (!filep->private_data) + return -ENOMEM; + + priv = filep->private_data; + init_gpib_file_private((gpib_file_private_t *)filep->private_data); + + dev_dbg(board->gpib_dev, "pid %i, gpib: opening minor %d\n", current->pid, minor); + + if (board->use_count == 0) { + int retval; + + retval = request_module("gpib%i", minor); + if (retval) { + dev_dbg(board->gpib_dev, "pid %i, gpib: request module returned %i\n", + current->pid, retval); + } + } + if (board->interface) { + if (!try_module_get(board->provider_module)) { + pr_err("gpib: try_module_get() failed\n"); + return -EIO; + } + board->use_count++; + priv->got_module = 1; + } + return 0; +} + +int ibclose(struct inode *inode, struct file *filep) +{ + unsigned int minor = iminor(inode); + gpib_board_t *board; + gpib_file_private_t *priv = filep->private_data; + gpib_descriptor_t *desc; + + if (minor >= GPIB_MAX_NUM_BOARDS) { + pr_err("gpib: invalid minor number of device file\n"); + return -ENODEV; + } + + board = &board_array[minor]; + + dev_dbg(board->gpib_dev, "pid %i, closing minor %d\n", current->pid, minor); + + if (priv) { + desc = handle_to_descriptor(priv, 0); + if (desc) { + if (desc->autopoll_enabled) { + dev_dbg(board->gpib_dev, "pid %i, decrementing autospollers\n", + current->pid); + if (board->autospollers > 0) + board->autospollers--; + else + pr_err("gpib: Attempt to decrement zero autospollers\n"); + } + } else { + pr_err("gpib: Unexpected null gpib_descriptor\n"); + } + + cleanup_open_devices(priv, board); + + if (atomic_read(&priv->holding_mutex)) + mutex_unlock(&board->user_mutex); + + if (priv->got_module && board->use_count) { + module_put(board->provider_module); + --board->use_count; + } + + kfree(filep->private_data); + filep->private_data = NULL; + } + + return 0; +} + +long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + unsigned int minor = iminor(filep->f_path.dentry->d_inode); + gpib_board_t *board; + gpib_file_private_t *file_priv = filep->private_data; + long retval = -ENOTTY; + + if (minor >= GPIB_MAX_NUM_BOARDS) { + pr_err("gpib: invalid minor number of device file\n"); + return -ENODEV; + } + board = &board_array[minor]; + + if (mutex_lock_interruptible(&board->big_gpib_mutex)) + return -ERESTARTSYS; + + dev_dbg(board->gpib_dev, "pid %i, ioctl %d, interface=%s, use=%d, onl=%d\n", + current->pid, cmd & 0xff, + board->interface ? board->interface->name : "", + board->use_count, + board->online); + + switch (cmd) { + case CFCBOARDTYPE: + retval = board_type_ioctl(file_priv, board, arg); + goto done; + case IBONL: + retval = online_ioctl(board, arg); + goto done; + default: + break; + } + if (!board->interface) { + pr_err("gpib: no gpib board configured on /dev/gpib%i\n", minor); + retval = -ENODEV; + goto done; + } + if (file_priv->got_module == 0) { + if (!try_module_get(board->provider_module)) { + pr_err("gpib: try_module_get() failed\n"); + retval = -EIO; + goto done; + } + file_priv->got_module = 1; + board->use_count++; + } + switch (cmd) { + case CFCBASE: + retval = iobase_ioctl(&board->config, arg); + goto done; + case CFCIRQ: + retval = irq_ioctl(&board->config, arg); + goto done; + case CFCDMA: + retval = dma_ioctl(&board->config, arg); + goto done; + case IBAUTOSPOLL: + retval = autospoll_ioctl(board, file_priv, arg); + goto done; + case IBBOARD_INFO: + retval = board_info_ioctl(board, arg); + goto done; + case IBMUTEX: + /* Need to unlock board->big_gpib_mutex before potentially locking board->user_mutex + * to maintain consistent locking order + */ + mutex_unlock(&board->big_gpib_mutex); + return mutex_ioctl(board, file_priv, arg); + case IBPAD: + retval = pad_ioctl(board, file_priv, arg); + goto done; + case IBSAD: + retval = sad_ioctl(board, file_priv, arg); + goto done; + case IBSELECT_PCI: + retval = select_pci_ioctl(&board->config, arg); + goto done; + case IBSELECT_DEVICE_PATH: + retval = select_device_path_ioctl(&board->config, arg); + goto done; + default: + break; + } + + if (!board->online) { + pr_err("gpib: ioctl %i invalid for offline board\n", + cmd & 0xff); + retval = -EINVAL; + goto done; + } + + switch (cmd) { + case IBEVENT: + retval = event_ioctl(board, arg); + goto done; + case IBCLOSEDEV: + retval = close_dev_ioctl(filep, board, arg); + goto done; + case IBOPENDEV: + retval = open_dev_ioctl(filep, board, arg); + goto done; + case IBSPOLL_BYTES: + retval = status_bytes_ioctl(board, arg); + goto done; + case IBWAIT: + retval = wait_ioctl(file_priv, board, arg); + if (retval == -ERESTARTSYS) + return retval; + goto done; + case IBLINES: + retval = line_status_ioctl(board, arg); + goto done; + case IBLOC: + board->interface->return_to_local(board); + retval = 0; + goto done; + default: + break; + } + + spin_lock(&board->locking_pid_spinlock); + if (current->pid != board->locking_pid) { + spin_unlock(&board->locking_pid_spinlock); + pr_err("gpib: need to hold board lock to perform ioctl %i\n", + cmd & 0xff); + retval = -EPERM; + goto done; + } + spin_unlock(&board->locking_pid_spinlock); + + switch (cmd) { + case IB_T1_DELAY: + retval = t1_delay_ioctl(board, arg); + goto done; + case IBCAC: + retval = take_control_ioctl(board, arg); + goto done; + case IBCMD: + /* IO ioctls can take a long time, we need to unlock board->big_gpib_mutex + * before we call them. + */ + mutex_unlock(&board->big_gpib_mutex); + return command_ioctl(file_priv, board, arg); + case IBEOS: + retval = eos_ioctl(board, arg); + goto done; + case IBGTS: + retval = ibgts(board); + goto done; + case IBPPC: + retval = ppc_ioctl(board, arg); + goto done; + case IBPP2_SET: + retval = set_local_ppoll_mode_ioctl(board, arg); + goto done; + case IBPP2_GET: + retval = get_local_ppoll_mode_ioctl(board, arg); + goto done; + case IBQUERY_BOARD_RSV: + retval = query_board_rsv_ioctl(board, arg); + goto done; + case IBRD: + /* IO ioctls can take a long time, we need to unlock board->big_gpib_mutex + * before we call them. + */ + mutex_unlock(&board->big_gpib_mutex); + return read_ioctl(file_priv, board, arg); + case IBRPP: + retval = parallel_poll_ioctl(board, arg); + goto done; + case IBRSC: + retval = request_system_control_ioctl(board, arg); + goto done; + case IBRSP: + retval = serial_poll_ioctl(board, arg); + goto done; + case IBRSV: + retval = request_service_ioctl(board, arg); + goto done; + case IBRSV2: + retval = request_service2_ioctl(board, arg); + goto done; + case IBSIC: + retval = interface_clear_ioctl(board, arg); + goto done; + case IBSRE: + retval = remote_enable_ioctl(board, arg); + goto done; + case IBTMO: + retval = timeout_ioctl(board, arg); + goto done; + case IBWRT: + /* IO ioctls can take a long time, we need to unlock board->big_gpib_mutex + * before we call them. + */ + mutex_unlock(&board->big_gpib_mutex); + return write_ioctl(file_priv, board, arg); + default: + retval = -ENOTTY; + goto done; + } + +done: + mutex_unlock(&board->big_gpib_mutex); + dev_dbg(board->gpib_dev, "ioctl done status = 0x%lx\n", board->status); + return retval; +} + +static int board_type_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, unsigned long arg) +{ + struct list_head *list_ptr; + board_type_ioctl_t cmd; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (board->online) { + pr_err("gpib: can't change board type while board is online.\n"); + return -EBUSY; + } + + retval = copy_from_user(&cmd, (void *)arg, sizeof(board_type_ioctl_t)); + if (retval) + return retval; + + for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers; + list_ptr = list_ptr->next) { + gpib_interface_list_t *entry; + + entry = list_entry(list_ptr, gpib_interface_list_t, list); + if (strcmp(entry->interface->name, cmd.name) == 0) { + int i; + int had_module = file_priv->got_module; + + if (board->use_count) { + for (i = 0; i < board->use_count; ++i) + module_put(board->provider_module); + board->interface = NULL; + file_priv->got_module = 0; + } + board->interface = entry->interface; + board->provider_module = entry->module; + for (i = 0; i < board->use_count; ++i) { + if (!try_module_get(entry->module)) { + board->use_count = i; + return -EIO; + } + } + if (had_module == 0) { + if (!try_module_get(entry->module)) + return -EIO; + ++board->use_count; + } + file_priv->got_module = 1; + return 0; + } + } + + return -EINVAL; +} + +static int read_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, + unsigned long arg) +{ + read_write_ioctl_t read_cmd; + u8 *userbuf; + unsigned long remain; + int end_flag = 0; + int retval; + ssize_t read_ret = 0; + gpib_descriptor_t *desc; + size_t nbytes; + + retval = copy_from_user(&read_cmd, (void *)arg, sizeof(read_cmd)); + if (retval) + return -EFAULT; + + if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) + return -EINVAL; + + desc = handle_to_descriptor(file_priv, read_cmd.handle); + if (!desc) + return -EINVAL; + + if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) + return -EFAULT; + + userbuf = (u8 *)(unsigned long)read_cmd.buffer_ptr; + userbuf += read_cmd.completed_transfer_count; + + remain = read_cmd.requested_transfer_count - read_cmd.completed_transfer_count; + + /* Check write access to buffer */ + if (!access_ok(userbuf, remain)) + return -EFAULT; + + atomic_set(&desc->io_in_progress, 1); + + /* Read buffer loads till we fill the user supplied buffer */ + while (remain > 0 && end_flag == 0) { + nbytes = 0; + read_ret = ibrd(board, board->buffer, (board->buffer_length < remain) ? + board->buffer_length : remain, &end_flag, &nbytes); + if (nbytes == 0) + break; + retval = copy_to_user(userbuf, board->buffer, nbytes); + if (retval) { + retval = -EFAULT; + break; + } + remain -= nbytes; + userbuf += nbytes; + if (read_ret < 0) + break; + } + read_cmd.completed_transfer_count = read_cmd.requested_transfer_count - remain; + read_cmd.end = end_flag; + /* suppress errors (for example due to timeout or interruption by device clear) + * if all bytes got sent. This prevents races that can occur in the various drivers + * if a device receives a device clear immediately after a transfer completes and + * the driver code wasn't careful enough to handle that case. + */ + if (remain == 0 || end_flag) + read_ret = 0; + if (retval == 0) + retval = copy_to_user((void *)arg, &read_cmd, sizeof(read_cmd)); + + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + if (retval) + return -EFAULT; + + return read_ret; +} + +static int command_ioctl(gpib_file_private_t *file_priv, + gpib_board_t *board, unsigned long arg) +{ + read_write_ioctl_t cmd; + u8 *userbuf; + unsigned long remain; + int retval; + int fault = 0; + gpib_descriptor_t *desc; + size_t bytes_written; + int no_clear_io_in_prog; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (cmd.completed_transfer_count > cmd.requested_transfer_count) + return -EINVAL; + + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) + return -EINVAL; + + userbuf = (u8 *)(unsigned long)cmd.buffer_ptr; + userbuf += cmd.completed_transfer_count; + + no_clear_io_in_prog = cmd.end; + cmd.end = 0; + + remain = cmd.requested_transfer_count - cmd.completed_transfer_count; + + /* Check read access to buffer */ + if (!access_ok(userbuf, remain)) + return -EFAULT; + + /* Write buffer loads till we empty the user supplied buffer. + * Call drivers at least once, even if remain is zero, in + * order to allow them to insure previous commands were + * completely finished, in the case of a restarted ioctl. + */ + + atomic_set(&desc->io_in_progress, 1); + + do { + fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? + board->buffer_length : remain); + if (fault) { + retval = -EFAULT; + bytes_written = 0; + } else { + retval = ibcmd(board, board->buffer, (board->buffer_length < remain) ? + board->buffer_length : remain, &bytes_written); + } + remain -= bytes_written; + userbuf += bytes_written; + if (retval < 0) { + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + break; + } + } while (remain > 0); + + cmd.completed_transfer_count = cmd.requested_transfer_count - remain; + + if (fault == 0) + fault = copy_to_user((void *)arg, &cmd, sizeof(cmd)); + + /* + * no_clear_io_in_prog (cmd.end) is true when io_in_progress should + * not be set to zero because the cmd in progress is the address setup + * operation for an async read or write. This causes CMPL not to be set + * in general_ibstatus until the async read or write completes. + */ + if (!no_clear_io_in_prog || fault) + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + if (fault) + return -EFAULT; + + return retval; +} + +static int write_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, + unsigned long arg) +{ + read_write_ioctl_t write_cmd; + u8 *userbuf; + unsigned long remain; + int retval = 0; + int fault; + gpib_descriptor_t *desc; + + fault = copy_from_user(&write_cmd, (void *)arg, sizeof(write_cmd)); + if (fault) + return -EFAULT; + + if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) + return -EINVAL; + + desc = handle_to_descriptor(file_priv, write_cmd.handle); + if (!desc) + return -EINVAL; + + userbuf = (u8 *)(unsigned long)write_cmd.buffer_ptr; + userbuf += write_cmd.completed_transfer_count; + + remain = write_cmd.requested_transfer_count - write_cmd.completed_transfer_count; + + /* Check read access to buffer */ + if (!access_ok(userbuf, remain)) + return -EFAULT; + + atomic_set(&desc->io_in_progress, 1); + + /* Write buffer loads till we empty the user supplied buffer */ + while (remain > 0) { + int send_eoi; + size_t bytes_written = 0; + + send_eoi = remain <= board->buffer_length && write_cmd.end; + fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? + board->buffer_length : remain); + if (fault) { + retval = -EFAULT; + break; + } + retval = ibwrt(board, board->buffer, (board->buffer_length < remain) ? + board->buffer_length : remain, send_eoi, &bytes_written); + remain -= bytes_written; + userbuf += bytes_written; + if (retval < 0) + break; + } + write_cmd.completed_transfer_count = write_cmd.requested_transfer_count - remain; + /* suppress errors (for example due to timeout or interruption by device clear) + * if all bytes got sent. This prevents races that can occur in the various drivers + * if a device receives a device clear immediately after a transfer completes and + * the driver code wasn't careful enough to handle that case. + */ + if (remain == 0) + retval = 0; + if (fault == 0) + fault = copy_to_user((void *)arg, &write_cmd, sizeof(write_cmd)); + + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + if (fault) + return -EFAULT; + + return retval; +} + +static int status_bytes_ioctl(gpib_board_t *board, unsigned long arg) +{ + gpib_status_queue_t *device; + spoll_bytes_ioctl_t cmd; + int retval; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + device = get_gpib_status_queue(board, cmd.pad, cmd.sad); + if (!device) + cmd.num_bytes = 0; + else + cmd.num_bytes = num_status_bytes(device); + + retval = copy_to_user((void *)arg, &cmd, sizeof(cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int increment_open_device_count(gpib_board_t *board, struct list_head *head, + unsigned int pad, int sad) +{ + struct list_head *list_ptr; + gpib_status_queue_t *device; + + /* first see if address has already been opened, then increment + * open count + */ + for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { + device = list_entry(list_ptr, gpib_status_queue_t, list); + if (gpib_address_equal(device->pad, device->sad, pad, sad)) { + dev_dbg(board->gpib_dev, "pid %i, incrementing open count for pad %i, sad %i\n", + current->pid, device->pad, device->sad); + device->reference_count++; + return 0; + } + } + + /* otherwise we need to allocate a new gpib_status_queue_t */ + device = kmalloc(sizeof(gpib_status_queue_t), GFP_ATOMIC); + if (!device) + return -ENOMEM; + init_gpib_status_queue(device); + device->pad = pad; + device->sad = sad; + device->reference_count = 1; + + list_add(&device->list, head); + + dev_dbg(board->gpib_dev, "pid %i, opened pad %i, sad %i\n", + current->pid, device->pad, device->sad); + + return 0; +} + +static int subtract_open_device_count(gpib_board_t *board, struct list_head *head, + unsigned int pad, int sad, unsigned int count) +{ + gpib_status_queue_t *device; + struct list_head *list_ptr; + + for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { + device = list_entry(list_ptr, gpib_status_queue_t, list); + if (gpib_address_equal(device->pad, device->sad, pad, sad)) { + dev_dbg(board->gpib_dev, "pid %i, decrementing open count for pad %i, sad %i\n", + current->pid, device->pad, device->sad); + if (count > device->reference_count) { + pr_err("gpib: bug! in %s()\n", __func__); + return -EINVAL; + } + device->reference_count -= count; + if (device->reference_count == 0) { + dev_dbg(board->gpib_dev, "pid %i, closing pad %i, sad %i\n", + current->pid, device->pad, device->sad); + list_del(list_ptr); + kfree(device); + } + return 0; + } + } + pr_err("gpib: bug! tried to close address that was never opened!\n"); + return -EINVAL; +} + +static inline int decrement_open_device_count(gpib_board_t *board, struct list_head *head, + unsigned int pad, int sad) +{ + return subtract_open_device_count(board, head, pad, sad, 1); +} + +static int cleanup_open_devices(gpib_file_private_t *file_priv, gpib_board_t *board) +{ + int retval = 0; + int i; + + for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) { + gpib_descriptor_t *desc; + + desc = file_priv->descriptors[i]; + if (!desc) + continue; + + if (desc->is_board == 0) { + retval = decrement_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + } + kfree(desc); + file_priv->descriptors[i] = NULL; + } + + return 0; +} + +static int open_dev_ioctl(struct file *filep, gpib_board_t *board, unsigned long arg) +{ + open_dev_ioctl_t open_dev_cmd; + int retval; + gpib_file_private_t *file_priv = filep->private_data; + int i; + + retval = copy_from_user(&open_dev_cmd, (void *)arg, sizeof(open_dev_cmd)); + if (retval) + return -EFAULT; + + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) + if (!file_priv->descriptors[i]) + break; + if (i == GPIB_MAX_NUM_DESCRIPTORS) { + mutex_unlock(&file_priv->descriptors_mutex); + return -ERANGE; + } + file_priv->descriptors[i] = kmalloc(sizeof(gpib_descriptor_t), GFP_KERNEL); + if (!file_priv->descriptors[i]) { + mutex_unlock(&file_priv->descriptors_mutex); + return -ENOMEM; + } + init_gpib_descriptor(file_priv->descriptors[i]); + + file_priv->descriptors[i]->pad = open_dev_cmd.pad; + file_priv->descriptors[i]->sad = open_dev_cmd.sad; + file_priv->descriptors[i]->is_board = open_dev_cmd.is_board; + mutex_unlock(&file_priv->descriptors_mutex); + + retval = increment_open_device_count(board, &board->device_list, open_dev_cmd.pad, + open_dev_cmd.sad); + if (retval < 0) + return retval; + + /* clear stuck srq state, since we may be able to find service request on + * the new device + */ + atomic_set(&board->stuck_srq, 0); + + open_dev_cmd.handle = i; + retval = copy_to_user((void *)arg, &open_dev_cmd, sizeof(open_dev_cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int close_dev_ioctl(struct file *filep, gpib_board_t *board, unsigned long arg) +{ + close_dev_ioctl_t cmd; + gpib_file_private_t *file_priv = filep->private_data; + int retval; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) + return -EINVAL; + if (!file_priv->descriptors[cmd.handle]) + return -EINVAL; + + retval = decrement_open_device_count(board, &board->device_list, + file_priv->descriptors[cmd.handle]->pad, + file_priv->descriptors[cmd.handle]->sad); + if (retval < 0) + return retval; + + kfree(file_priv->descriptors[cmd.handle]); + file_priv->descriptors[cmd.handle] = NULL; + + return 0; +} + +static int serial_poll_ioctl(gpib_board_t *board, unsigned long arg) +{ + serial_poll_ioctl_t serial_cmd; + int retval; + + dev_dbg(board->gpib_dev, "pid %i, entering %s()\n", current->pid, __func__); + + retval = copy_from_user(&serial_cmd, (void *)arg, sizeof(serial_cmd)); + if (retval) + return -EFAULT; + + retval = get_serial_poll_byte(board, serial_cmd.pad, serial_cmd.sad, board->usec_timeout, + &serial_cmd.status_byte); + if (retval < 0) + return retval; + + retval = copy_to_user((void *)arg, &serial_cmd, sizeof(serial_cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int wait_ioctl(gpib_file_private_t *file_priv, gpib_board_t *board, + unsigned long arg) +{ + wait_ioctl_t wait_cmd; + int retval; + gpib_descriptor_t *desc; + + retval = copy_from_user(&wait_cmd, (void *)arg, sizeof(wait_cmd)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, wait_cmd.handle); + if (!desc) + return -EINVAL; + + retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, + wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); + if (retval < 0) + return retval; + + retval = copy_to_user((void *)arg, &wait_cmd, sizeof(wait_cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int parallel_poll_ioctl(gpib_board_t *board, unsigned long arg) +{ + u8 poll_byte; + int retval; + + retval = ibrpp(board, &poll_byte); + if (retval < 0) + return retval; + + retval = copy_to_user((void *)arg, &poll_byte, sizeof(poll_byte)); + if (retval) + return -EFAULT; + + return 0; +} + +static int online_ioctl(gpib_board_t *board, unsigned long arg) +{ + online_ioctl_t online_cmd; + int retval; + void *init_data = NULL; + + board->config.init_data = NULL; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&online_cmd, (void *)arg, sizeof(online_cmd)); + if (retval) + return -EFAULT; + if (online_cmd.init_data_length > 0) { + board->config.init_data = vmalloc(online_cmd.init_data_length); + if (!board->config.init_data) + return -ENOMEM; + if (WARN_ON_ONCE(sizeof(init_data) > sizeof(online_cmd.init_data_ptr))) + return -EFAULT; + init_data = (void *)(unsigned long)(online_cmd.init_data_ptr); + retval = copy_from_user(board->config.init_data, init_data, + online_cmd.init_data_length); + if (retval) { + vfree(board->config.init_data); + return -EFAULT; + } + board->config.init_data_length = online_cmd.init_data_length; + } else { + board->config.init_data = NULL; + board->config.init_data_length = 0; + } + if (online_cmd.online) + retval = ibonline(board); + else + retval = iboffline(board); + if (board->config.init_data) { + vfree(board->config.init_data); + board->config.init_data = NULL; + board->config.init_data_length = 0; + } + return retval; +} + +static int remote_enable_ioctl(gpib_board_t *board, unsigned long arg) +{ + int enable; + int retval; + + retval = copy_from_user(&enable, (void *)arg, sizeof(enable)); + if (retval) + return -EFAULT; + + return ibsre(board, enable); +} + +static int take_control_ioctl(gpib_board_t *board, unsigned long arg) +{ + int synchronous; + int retval; + + retval = copy_from_user(&synchronous, (void *)arg, sizeof(synchronous)); + if (retval) + return -EFAULT; + + return ibcac(board, synchronous, 1); +} + +static int line_status_ioctl(gpib_board_t *board, unsigned long arg) +{ + short lines; + int retval; + + retval = iblines(board, &lines); + if (retval < 0) + return retval; + + retval = copy_to_user((void *)arg, &lines, sizeof(lines)); + if (retval) + return -EFAULT; + + return 0; +} + +static int pad_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg) +{ + pad_ioctl_t cmd; + int retval; + gpib_descriptor_t *desc; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) + return -EINVAL; + + if (desc->is_board) { + retval = ibpad(board, cmd.pad); + if (retval < 0) + return retval; + } else { + retval = decrement_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + + desc->pad = cmd.pad; + + retval = increment_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + } + + return 0; +} + +static int sad_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg) +{ + sad_ioctl_t cmd; + int retval; + gpib_descriptor_t *desc; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) + return -EINVAL; + + if (desc->is_board) { + retval = ibsad(board, cmd.sad); + if (retval < 0) + return retval; + } else { + retval = decrement_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + + desc->sad = cmd.sad; + + retval = increment_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + } + return 0; +} + +static int eos_ioctl(gpib_board_t *board, unsigned long arg) +{ + eos_ioctl_t eos_cmd; + int retval; + + retval = copy_from_user(&eos_cmd, (void *)arg, sizeof(eos_cmd)); + if (retval) + return -EFAULT; + + return ibeos(board, eos_cmd.eos, eos_cmd.eos_flags); +} + +static int request_service_ioctl(gpib_board_t *board, unsigned long arg) +{ + u8 status_byte; + int retval; + + retval = copy_from_user(&status_byte, (void *)arg, sizeof(status_byte)); + if (retval) + return -EFAULT; + + return ibrsv2(board, status_byte, status_byte & request_service_bit); +} + +static int request_service2_ioctl(gpib_board_t *board, unsigned long arg) +{ + request_service2_t request_service2_cmd; + int retval; + + retval = copy_from_user(&request_service2_cmd, (void *)arg, sizeof(request_service2_t)); + if (retval) + return -EFAULT; + + return ibrsv2(board, request_service2_cmd.status_byte, + request_service2_cmd.new_reason_for_service); +} + +static int iobase_ioctl(gpib_board_config_t *config, unsigned long arg) +{ + u64 base_addr; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&base_addr, (void *)arg, sizeof(base_addr)); + if (retval) + return -EFAULT; + + if (WARN_ON_ONCE(sizeof(void *) > sizeof(base_addr))) + return -EFAULT; + config->ibbase = (void *)(unsigned long)(base_addr); + + return 0; +} + +static int irq_ioctl(gpib_board_config_t *config, unsigned long arg) +{ + unsigned int irq; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&irq, (void *)arg, sizeof(irq)); + if (retval) + return -EFAULT; + + config->ibirq = irq; + + return 0; +} + +static int dma_ioctl(gpib_board_config_t *config, unsigned long arg) +{ + unsigned int dma_channel; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&dma_channel, (void *)arg, sizeof(dma_channel)); + if (retval) + return -EFAULT; + + config->ibdma = dma_channel; + + return 0; +} + +static int autospoll_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg) +{ + autospoll_ioctl_t enable; + int retval; + gpib_descriptor_t *desc; + + retval = copy_from_user(&enable, (void *)arg, sizeof(enable)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, 0); /* board handle is 0 */ + + if (enable) { + if (!desc->autopoll_enabled) { + board->autospollers++; + desc->autopoll_enabled = 1; + } + retval = 0; + } else { + if (desc->autopoll_enabled) { + desc->autopoll_enabled = 0; + if (board->autospollers > 0) { + board->autospollers--; + retval = 0; + } else { + pr_err("gpib: tried to set number of autospollers negative\n"); + retval = -EINVAL; + } + } else { + pr_err("gpib: autopoll disable requested before enable\n"); + retval = -EINVAL; + } + } + return retval; +} + +static int mutex_ioctl(gpib_board_t *board, gpib_file_private_t *file_priv, + unsigned long arg) +{ + int retval, lock_mutex; + + retval = copy_from_user(&lock_mutex, (void *)arg, sizeof(lock_mutex)); + if (retval) + return -EFAULT; + + if (lock_mutex) { + retval = mutex_lock_interruptible(&board->user_mutex); + if (retval) { + pr_warn("gpib: ioctl interrupted while waiting on lock\n"); + return -ERESTARTSYS; + } + + spin_lock(&board->locking_pid_spinlock); + board->locking_pid = current->pid; + spin_unlock(&board->locking_pid_spinlock); + + atomic_set(&file_priv->holding_mutex, 1); + + dev_dbg(board->gpib_dev, "pid %i, locked board %d mutex\n", + current->pid, board->minor); + } else { + spin_lock(&board->locking_pid_spinlock); + if (current->pid != board->locking_pid) { + pr_err("gpib: bug! pid %i tried to release mutex held by pid %i\n", + current->pid, board->locking_pid); + spin_unlock(&board->locking_pid_spinlock); + return -EPERM; + } + board->locking_pid = 0; + spin_unlock(&board->locking_pid_spinlock); + + atomic_set(&file_priv->holding_mutex, 0); + + mutex_unlock(&board->user_mutex); + dev_dbg(board->gpib_dev, "pid %i, unlocked board %i mutex\n", + current->pid, board->minor); + } + return 0; +} + +static int timeout_ioctl(gpib_board_t *board, unsigned long arg) +{ + unsigned int timeout; + int retval; + + retval = copy_from_user(&timeout, (void *)arg, sizeof(timeout)); + if (retval) + return -EFAULT; + + board->usec_timeout = timeout; + dev_dbg(board->gpib_dev, "pid %i, timeout set to %i usec\n", current->pid, timeout); + + return 0; +} + +static int ppc_ioctl(gpib_board_t *board, unsigned long arg) +{ + ppoll_config_ioctl_t cmd; + int retval; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (cmd.set_ist) { + board->ist = 1; + board->interface->parallel_poll_response(board, board->ist); + } else if (cmd.clear_ist) { + board->ist = 0; + board->interface->parallel_poll_response(board, board->ist); + } + + if (cmd.config) { + retval = ibppc(board, cmd.config); + if (retval < 0) + return retval; + } + + return 0; +} + +static int set_local_ppoll_mode_ioctl(gpib_board_t *board, unsigned long arg) +{ + local_ppoll_mode_ioctl_t cmd; + int retval; + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (!board->interface->local_parallel_poll_mode) { + pr_warn("gpib: local/remote parallel poll mode not supported by driver."); + return -EIO; + } + board->local_ppoll_mode = cmd != 0; + board->interface->local_parallel_poll_mode(board, board->local_ppoll_mode); + + return 0; +} + +static int get_local_ppoll_mode_ioctl(gpib_board_t *board, unsigned long arg) +{ + local_ppoll_mode_ioctl_t cmd; + int retval; + + cmd = board->local_ppoll_mode; + retval = copy_to_user((void *)arg, &cmd, sizeof(cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int query_board_rsv_ioctl(gpib_board_t *board, unsigned long arg) +{ + int status; + int retval; + + status = board->interface->serial_poll_status(board); + + retval = copy_to_user((void *)arg, &status, sizeof(status)); + if (retval) + return -EFAULT; + + return 0; +} + +static int board_info_ioctl(const gpib_board_t *board, unsigned long arg) +{ + board_info_ioctl_t info; + int retval; + + info.pad = board->pad; + info.sad = board->sad; + info.parallel_poll_configuration = board->parallel_poll_configuration; + info.is_system_controller = board->master; + if (board->autospollers) + info.autopolling = 1; + else + info.autopolling = 0; + info.t1_delay = board->t1_nano_sec; + info.ist = board->ist; + info.no_7_bit_eos = board->interface->no_7_bit_eos; + retval = copy_to_user((void *)arg, &info, sizeof(info)); + if (retval) + return -EFAULT; + + return 0; +} + +static int interface_clear_ioctl(gpib_board_t *board, unsigned long arg) +{ + unsigned int usec_duration; + int retval; + + retval = copy_from_user(&usec_duration, (void *)arg, sizeof(usec_duration)); + if (retval) + return -EFAULT; + + return ibsic(board, usec_duration); +} + +static int select_pci_ioctl(gpib_board_config_t *config, unsigned long arg) +{ + select_pci_ioctl_t selection; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&selection, (void *)arg, sizeof(selection)); + if (retval) + return -EFAULT; + + config->pci_bus = selection.pci_bus; + config->pci_slot = selection.pci_slot; + + return 0; +} + +static int select_device_path_ioctl(gpib_board_config_t *config, unsigned long arg) +{ + select_device_path_ioctl_t *selection; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + selection = vmalloc(sizeof(select_device_path_ioctl_t)); + if (!selection) + return -ENOMEM; + + retval = copy_from_user(selection, (void *)arg, sizeof(select_device_path_ioctl_t)); + if (retval) { + vfree(selection); + return -EFAULT; + } + + selection->device_path[sizeof(selection->device_path) - 1] = '\0'; + kfree(config->device_path); + config->device_path = NULL; + if (strlen(selection->device_path) > 0) + config->device_path = kstrdup(selection->device_path, GFP_KERNEL); + + vfree(selection); + return 0; +} + +unsigned int num_gpib_events(const gpib_event_queue_t *queue) +{ + return queue->num_events; +} + +static int push_gpib_event_nolock(gpib_board_t *board, short event_type) +{ + gpib_event_queue_t *queue = &board->event_queue; + struct list_head *head = &queue->event_head; + gpib_event_t *event; + static const unsigned int max_num_events = 1024; + int retval; + + if (num_gpib_events(queue) >= max_num_events) { + short lost_event; + + queue->dropped_event = 1; + retval = pop_gpib_event_nolock(board, queue, &lost_event); + if (retval < 0) + return retval; + } + + event = kmalloc(sizeof(gpib_event_t), GFP_ATOMIC); + if (!event) { + queue->dropped_event = 1; + pr_err("gpib: failed to allocate memory for event\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&event->list); + event->event_type = event_type; + + list_add_tail(&event->list, head); + + queue->num_events++; + + dev_dbg(board->gpib_dev, "pushed event %i, %i in queue\n", + (int)event_type, num_gpib_events(queue)); + + return 0; +} + +// push event onto back of event queue +int push_gpib_event(gpib_board_t *board, short event_type) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&board->event_queue.lock, flags); + retval = push_gpib_event_nolock(board, event_type); + spin_unlock_irqrestore(&board->event_queue.lock, flags); + + if (event_type == EventDevTrg) + board->status |= DTAS; + if (event_type == EventDevClr) + board->status |= DCAS; + + return retval; +} +EXPORT_SYMBOL(push_gpib_event); + +static int pop_gpib_event_nolock(gpib_board_t *board, gpib_event_queue_t *queue, short *event_type) +{ + struct list_head *head = &queue->event_head; + struct list_head *front = head->next; + gpib_event_t *event; + + if (num_gpib_events(queue) == 0) { + *event_type = EventNone; + return 0; + } + + if (front == head) + return -EIO; + + if (queue->dropped_event) { + queue->dropped_event = 0; + return -EPIPE; + } + + event = list_entry(front, gpib_event_t, list); + *event_type = event->event_type; + + list_del(front); + kfree(event); + + queue->num_events--; + + dev_dbg(board->gpib_dev, "popped event %i, %i in queue\n", + (int)*event_type, num_gpib_events(queue)); + + return 0; +} + +// pop event from front of event queue +int pop_gpib_event(gpib_board_t *board, gpib_event_queue_t *queue, short *event_type) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&queue->lock, flags); + retval = pop_gpib_event_nolock(board, queue, event_type); + spin_unlock_irqrestore(&queue->lock, flags); + return retval; +} + +static int event_ioctl(gpib_board_t *board, unsigned long arg) +{ + event_ioctl_t user_event; + int retval; + short event; + + retval = pop_gpib_event(board, &board->event_queue, &event); + if (retval < 0) + return retval; + + user_event = event; + + retval = copy_to_user((void *)arg, &user_event, sizeof(user_event)); + if (retval) + return -EFAULT; + + return 0; +} + +static int request_system_control_ioctl(gpib_board_t *board, unsigned long arg) +{ + rsc_ioctl_t request_control; + int retval; + + retval = copy_from_user(&request_control, (void *)arg, sizeof(request_control)); + if (retval) + return -EFAULT; + + ibrsc(board, request_control); + + return 0; +} + +static int t1_delay_ioctl(gpib_board_t *board, unsigned long arg) +{ + t1_delay_ioctl_t cmd; + unsigned int delay; + int retval; + + if (!board->interface->t1_delay) { + pr_warn("gpib: t1 delay not implemented in driver!\n"); + return -EIO; + } + + retval = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + delay = cmd; + + board->t1_nano_sec = board->interface->t1_delay(board, delay); + + return 0; +} + +const struct file_operations ib_fops = { +owner: THIS_MODULE, +llseek : NULL, +unlocked_ioctl : &ibioctl, +compat_ioctl : &ibioctl, +open : &ibopen, +release : &ibclose, +}; + +gpib_board_t board_array[GPIB_MAX_NUM_BOARDS]; + +LIST_HEAD(registered_drivers); + +void init_gpib_descriptor(gpib_descriptor_t *desc) +{ + desc->pad = 0; + desc->sad = -1; + desc->is_board = 0; + desc->autopoll_enabled = 0; + atomic_set(&desc->io_in_progress, 0); +} + +void gpib_register_driver(gpib_interface_t *interface, struct module *provider_module) +{ + struct gpib_interface_list_struct *entry; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + entry->interface = interface; + entry->module = provider_module; + list_add(&entry->list, ®istered_drivers); + pr_info("gpib: registered %s interface\n", interface->name); +} +EXPORT_SYMBOL(gpib_register_driver); + +void gpib_unregister_driver(gpib_interface_t *interface) +{ + int i; + struct list_head *list_ptr; + + for (i = 0; i < GPIB_MAX_NUM_BOARDS; i++) { + gpib_board_t *board = &board_array[i]; + + if (board->interface == interface) { + if (board->use_count > 0) + pr_warn("gpib: Warning: deregistered interface %s in use\n", + interface->name); + iboffline(board); + board->interface = NULL; + } + } + for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers;) { + gpib_interface_list_t *entry; + + entry = list_entry(list_ptr, gpib_interface_list_t, list); + list_ptr = list_ptr->next; + if (entry->interface == interface) { + list_del(&entry->list); + kfree(entry); + } + } + pr_info("gpib: unregistered %s interface\n", interface->name); +} +EXPORT_SYMBOL(gpib_unregister_driver); + +static void init_gpib_board_config(gpib_board_config_t *config) +{ + memset(config, 0, sizeof(gpib_board_config_t)); + config->pci_bus = -1; + config->pci_slot = -1; +} + +void init_gpib_board(gpib_board_t *board) +{ + board->interface = NULL; + board->provider_module = NULL; + board->buffer = NULL; + board->buffer_length = 0; + board->status = 0; + init_waitqueue_head(&board->wait); + mutex_init(&board->user_mutex); + mutex_init(&board->big_gpib_mutex); + board->locking_pid = 0; + spin_lock_init(&board->locking_pid_spinlock); + spin_lock_init(&board->spinlock); + timer_setup(&board->timer, NULL, 0); + board->dev = NULL; + board->gpib_dev = NULL; + init_gpib_board_config(&board->config); + board->private_data = NULL; + board->use_count = 0; + INIT_LIST_HEAD(&board->device_list); + board->pad = 0; + board->sad = -1; + board->usec_timeout = 3000000; + board->parallel_poll_configuration = 0; + board->online = 0; + board->autospollers = 0; + board->autospoll_task = NULL; + init_event_queue(&board->event_queue); + board->minor = -1; + init_gpib_pseudo_irq(&board->pseudo_irq); + board->master = 1; + atomic_set(&board->stuck_srq, 0); + board->local_ppoll_mode = 0; +} + +int gpib_allocate_board(gpib_board_t *board) +{ + if (!board->buffer) { + board->buffer_length = 0x4000; + board->buffer = vmalloc(board->buffer_length); + if (!board->buffer) { + board->buffer_length = 0; + return -ENOMEM; + } + } + return 0; +} + +void gpib_deallocate_board(gpib_board_t *board) +{ + short dummy; + + if (board->buffer) { + vfree(board->buffer); + board->buffer = NULL; + board->buffer_length = 0; + } + while (num_gpib_events(&board->event_queue)) + pop_gpib_event(board, &board->event_queue, &dummy); +} + +static void init_board_array(gpib_board_t *board_array, unsigned int length) +{ + int i; + + for (i = 0; i < length; i++) { + init_gpib_board(&board_array[i]); + board_array[i].minor = i; + } +} + +void init_gpib_status_queue(gpib_status_queue_t *device) +{ + INIT_LIST_HEAD(&device->list); + INIT_LIST_HEAD(&device->status_bytes); + device->num_status_bytes = 0; + device->reference_count = 0; + device->dropped_byte = 0; +} + +static struct class *gpib_class; + +static int __init gpib_common_init_module(void) +{ + int i; + + pr_info("Linux-GPIB core driver\n"); + init_board_array(board_array, GPIB_MAX_NUM_BOARDS); + if (register_chrdev(GPIB_CODE, "gpib", &ib_fops)) { + pr_err("gpib: can't get major %d\n", GPIB_CODE); + return -EIO; + } + gpib_class = class_create("gpib_common"); + if (IS_ERR(gpib_class)) { + pr_err("gpib: failed to create gpib class\n"); + unregister_chrdev(GPIB_CODE, "gpib"); + return PTR_ERR(gpib_class); + } + for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) + board_array[i].gpib_dev = device_create(gpib_class, 0, + MKDEV(GPIB_CODE, i), NULL, "gpib%i", i); + + return 0; +} + +static void __exit gpib_common_exit_module(void) +{ + int i; + + for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) + device_destroy(gpib_class, MKDEV(GPIB_CODE, i)); + + class_destroy(gpib_class); + unregister_chrdev(GPIB_CODE, "gpib"); +} + +int gpib_match_device_path(struct device *dev, const char *device_path_in) +{ + if (device_path_in) { + char *device_path; + + device_path = kobject_get_path(&dev->kobj, GFP_KERNEL); + if (!device_path) { + dev_err(dev, "kobject_get_path returned NULL."); + return 0; + } + if (strcmp(device_path_in, device_path) != 0) { + kfree(device_path); + return 0; + } + kfree(device_path); + } + return 1; +} +EXPORT_SYMBOL(gpib_match_device_path); + +struct pci_dev *gpib_pci_get_device(const gpib_board_config_t *config, unsigned int vendor_id, + unsigned int device_id, struct pci_dev *from) +{ + struct pci_dev *pci_device = from; + + while ((pci_device = pci_get_device(vendor_id, device_id, pci_device))) { + if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != + PCI_SLOT(pci_device->devfn)) + continue; + if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) + continue; + return pci_device; + } + return NULL; +} +EXPORT_SYMBOL(gpib_pci_get_device); + +struct pci_dev *gpib_pci_get_subsys(const gpib_board_config_t *config, unsigned int vendor_id, + unsigned int device_id, unsigned int ss_vendor, + unsigned int ss_device, + struct pci_dev *from) +{ + struct pci_dev *pci_device = from; + + while ((pci_device = pci_get_subsys(vendor_id, device_id, + ss_vendor, ss_device, pci_device))) { + if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != + PCI_SLOT(pci_device->devfn)) + continue; + if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) + continue; + return pci_device; + } + return NULL; +} +EXPORT_SYMBOL(gpib_pci_get_subsys); + +module_init(gpib_common_init_module); +module_exit(gpib_common_exit_module); + diff --git a/drivers/staging/gpib/common/iblib.c b/drivers/staging/gpib/common/iblib.c new file mode 100644 index 0000000000000..db1911cc1b263 --- /dev/null +++ b/drivers/staging/gpib/common/iblib.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "ibsys.h" +#include +#include +#include + +/* + * IBCAC + * Return to the controller active state from the + * controller standby state, i.e., turn ATN on. Note + * that in order to enter the controller active state + * from the controller idle state, ibsic must be called. + * If sync is non-zero, attempt to take control synchronously. + * If fallback_to_async is non-zero, try to take control asynchronously + * if synchronous attempt fails. + */ +int ibcac(gpib_board_t *board, int sync, int fallback_to_async) +{ + int status = ibstatus(board); + int retval; + + if ((status & CIC) == 0) { + pr_err("gpib: not CIC during %s()\n", __func__); + return -1; + } + + if (status & ATN) + return 0; + + if (sync && (status & LACS) == 0) + /* tcs (take control synchronously) can only possibly work when + * controller is listener. Error code also needs to be -ETIMEDOUT + * or it will giveout without doing fallback. + */ + retval = -ETIMEDOUT; + else + retval = board->interface->take_control(board, sync); + + if (retval < 0 && fallback_to_async) { + if (sync && retval == -ETIMEDOUT) + retval = board->interface->take_control(board, 0); + } + board->interface->update_status(board, 0); + + return retval; +} + +/* After ATN is asserted, it should cause any connected devices + * to start listening for command bytes and leave acceptor idle state. + * So if ATN is asserted and neither NDAC or NRFD are asserted, + * then there are no devices and ibcmd should error out immediately. + * Some gpib hardware sees itself asserting NDAC/NRFD when it + * is controller in charge, in which case this check will + * do nothing useful (but shouldn't cause any harm either). + * Drivers that don't need this check (ni_usb for example) may + * set the skip_check_for_command_acceptors flag in their + * gpib_interface_struct to avoid useless overhead. + */ +static int check_for_command_acceptors(gpib_board_t *board) +{ + int lines; + + if (board->interface->skip_check_for_command_acceptors) + return 0; + if (!board->interface->line_status) + return 0; + + udelay(2); // allow time for devices to respond to ATN if it was just asserted + + lines = board->interface->line_status(board); + if (lines < 0) + return lines; + + if (lines & ValidATN) { + if ((lines & BusATN) == 0) { + pr_err("gpib: ATN not asserted in %s()?", __func__); + return 0; + } + } + + if ((lines & ValidNRFD) && (lines & ValidNDAC)) { + if ((lines & BusNRFD) == 0 && (lines & BusNDAC) == 0) + return -ENOTCONN; + } + + return 0; +} + +/* + * IBCMD + * Write cnt command bytes from buf to the GPIB. The + * command operation terminates only on I/O complete. + * + * NOTE: + * 1. Prior to beginning the command, the interface is + * placed in the controller active state. + * 2. Before calling ibcmd for the first time, ibsic + * must be called to initialize the GPIB and enable + * the interface to leave the controller idle state. + */ +int ibcmd(gpib_board_t *board, uint8_t *buf, size_t length, size_t *bytes_written) +{ + ssize_t ret = 0; + int status; + + *bytes_written = 0; + + status = ibstatus(board); + + if ((status & CIC) == 0) { + pr_err("gpib: cannot send command when not controller-in-charge\n"); + return -EIO; + } + + os_start_timer(board, board->usec_timeout); + + ret = ibcac(board, 1, 1); + if (ret == 0) { + ret = check_for_command_acceptors(board); + if (ret == 0) + ret = board->interface->command(board, buf, length, bytes_written); + } + + os_remove_timer(board); + + if (io_timed_out(board)) + ret = -ETIMEDOUT; + + return ret; +} + +/* + * IBGTS + * Go to the controller standby state from the controller + * active state, i.e., turn ATN off. + */ + +int ibgts(gpib_board_t *board) +{ + int status = ibstatus(board); + int retval; + + if ((status & CIC) == 0) { + pr_err("gpib: not CIC during %s()\n", __func__); + return -1; + } + + retval = board->interface->go_to_standby(board); /* go to standby */ + if (retval < 0) + pr_err("gpib: error while going to standby\n"); + + board->interface->update_status(board, 0); + + return retval; +} + +static int autospoll_wait_should_wake_up(gpib_board_t *board) +{ + int retval; + + mutex_lock(&board->big_gpib_mutex); + + retval = board->master && board->autospollers > 0 && + !atomic_read(&board->stuck_srq) && + test_and_clear_bit(SRQI_NUM, &board->status); + + mutex_unlock(&board->big_gpib_mutex); + return retval; +} + +static int autospoll_thread(void *board_void) +{ + gpib_board_t *board = board_void; + int retval = 0; + + dev_dbg(board->gpib_dev, "entering autospoll thread\n"); + + while (1) { + wait_event_interruptible(board->wait, + kthread_should_stop() || + autospoll_wait_should_wake_up(board)); + dev_dbg(board->gpib_dev, "autospoll wait satisfied\n"); + if (kthread_should_stop()) + break; + + mutex_lock(&board->big_gpib_mutex); + /* make sure we are still good after we have lock */ + if (board->autospollers <= 0 || board->master == 0) { + mutex_unlock(&board->big_gpib_mutex); + continue; + } + mutex_unlock(&board->big_gpib_mutex); + + if (try_module_get(board->provider_module)) { + retval = autopoll_all_devices(board); + module_put(board->provider_module); + } else { + pr_err("gpib%i: %s: try_module_get() failed!\n", board->minor, __func__); + } + if (retval <= 0) { + pr_err("gpib%i: %s: stuck SRQ\n", board->minor, __func__); + + atomic_set(&board->stuck_srq, 1); // XXX could be better + set_bit(SRQI_NUM, &board->status); + } + } + pr_info("gpib%i: exiting autospoll thread\n", board->minor); + return retval; +} + +int ibonline(gpib_board_t *board) +{ + int retval; + + if (board->online) + return -EBUSY; + if (!board->interface) + return -ENODEV; + retval = gpib_allocate_board(board); + if (retval < 0) + return retval; + + board->dev = NULL; + board->local_ppoll_mode = 0; + retval = board->interface->attach(board, &board->config); + if (retval < 0) { + board->interface->detach(board); + pr_err("gpib: interface attach failed\n"); + return retval; + } + /* nios2nommu on 2.6.11 uclinux kernel has weird problems + * with autospoll thread causing huge slowdowns + */ +#ifndef CONFIG_NIOS2 + board->autospoll_task = kthread_run(&autospoll_thread, board, + "gpib%d_autospoll_kthread", board->minor); + retval = IS_ERR(board->autospoll_task); + if (retval) { + pr_err("gpib: failed to create autospoll thread\n"); + board->interface->detach(board); + return retval; + } +#endif + board->online = 1; + dev_dbg(board->gpib_dev, "gpib: board online\n"); + + return 0; +} + +/* XXX need to make sure board is generally not in use (grab board lock?) */ +int iboffline(gpib_board_t *board) +{ + int retval; + + if (board->online == 0) + return 0; + if (!board->interface) + return -ENODEV; + + if (board->autospoll_task && !IS_ERR(board->autospoll_task)) { + retval = kthread_stop(board->autospoll_task); + if (retval) + pr_err("gpib: kthread_stop returned %i\n", retval); + board->autospoll_task = NULL; + } + + board->interface->detach(board); + gpib_deallocate_board(board); + board->online = 0; + dev_dbg(board->gpib_dev, "gpib: board offline\n"); + + return 0; +} + +/* + * IBLINES + * Poll the GPIB control lines and return their status in buf. + * + * LSB (bits 0-7) - VALID lines mask (lines that can be monitored). + * Next LSB (bits 8-15) - STATUS lines mask (lines that are currently set). + * + */ +int iblines(const gpib_board_t *board, short *lines) +{ + int retval; + + *lines = 0; + if (!board->interface->line_status) + return 0; + retval = board->interface->line_status(board); + if (retval < 0) + return retval; + *lines = retval; + return 0; +} + +/* + * IBRD + * Read up to 'length' bytes of data from the GPIB into buf. End + * on detection of END (EOI and or EOS) and set 'end_flag'. + * + * NOTE: + * 1. The interface is placed in the controller standby + * state prior to beginning the read. + * 2. Prior to calling ibrd, the intended devices as well + * as the interface board itself must be addressed by + * calling ibcmd. + */ + +int ibrd(gpib_board_t *board, uint8_t *buf, size_t length, int *end_flag, size_t *nbytes) +{ + ssize_t ret = 0; + int retval; + size_t bytes_read; + + *nbytes = 0; + *end_flag = 0; + if (length == 0) { + pr_warn("gpib: %s() called with zero length?\n", __func__); + return 0; + } + + if (board->master) { + retval = ibgts(board); + if (retval < 0) + return retval; + } + /* XXX resetting timer here could cause timeouts take longer than they should, + * since read_ioctl calls this + * function in a loop, there is probably a similar problem with writes/commands + */ + os_start_timer(board, board->usec_timeout); + + do { + ret = board->interface->read(board, buf, length - *nbytes, end_flag, &bytes_read); + if (ret < 0) { + pr_err("gpib read error\n"); + goto ibrd_out; + } + buf += bytes_read; + *nbytes += bytes_read; + if (need_resched()) + schedule(); + } while (ret == 0 && *nbytes > 0 && *nbytes < length && *end_flag == 0); +ibrd_out: + os_remove_timer(board); + + return ret; +} + +/* + * IBRPP + * Conduct a parallel poll and return the byte in buf. + * + * NOTE: + * 1. Prior to conducting the poll the interface is placed + * in the controller active state. + */ +int ibrpp(gpib_board_t *board, uint8_t *result) +{ + int retval = 0; + + os_start_timer(board, board->usec_timeout); + retval = ibcac(board, 1, 1); + if (retval) + return -1; + + if (board->interface->parallel_poll(board, result)) { + pr_err("gpib: parallel poll failed\n"); + retval = -1; + } + os_remove_timer(board); + return retval; +} + +int ibppc(gpib_board_t *board, uint8_t configuration) +{ + configuration &= 0x1f; + board->interface->parallel_poll_configure(board, configuration); + board->parallel_poll_configuration = configuration; + + return 0; +} + +int ibrsv2(gpib_board_t *board, uint8_t status_byte, int new_reason_for_service) +{ + int board_status = ibstatus(board); + const unsigned int MSS = status_byte & request_service_bit; + + if ((board_status & CIC)) { + pr_err("gpib: interface requested service while CIC\n"); + return -EINVAL; + } + + if (MSS == 0 && new_reason_for_service) + return -EINVAL; + + if (board->interface->serial_poll_response2) { + board->interface->serial_poll_response2(board, status_byte, new_reason_for_service); + // fall back on simpler serial_poll_response if the behavior would be the same + } else if (board->interface->serial_poll_response && + (MSS == 0 || (MSS && new_reason_for_service))) { + board->interface->serial_poll_response(board, status_byte); + } else { + return -EOPNOTSUPP; + } + + return 0; +} + +/* + * IBSIC + * Send IFC for at least 100 microseconds. + * + * NOTE: + * 1. Ibsic must be called prior to the first call to + * ibcmd in order to initialize the bus and enable the + * interface to leave the controller idle state. + */ +int ibsic(gpib_board_t *board, unsigned int usec_duration) +{ + if (board->master == 0) { + pr_err("gpib: tried to assert IFC when not system controller\n"); + return -1; + } + + if (usec_duration < 100) + usec_duration = 100; + if (usec_duration > 1000) { + usec_duration = 1000; + pr_warn("gpib: warning, shortening long udelay\n"); + } + + dev_dbg(board->gpib_dev, "sending interface clear\n"); + board->interface->interface_clear(board, 1); + udelay(usec_duration); + board->interface->interface_clear(board, 0); + + return 0; +} + +void ibrsc(gpib_board_t *board, int request_control) +{ + board->master = request_control != 0; + if (!board->interface->request_system_control) { + pr_err("gpib: bug! driver does not implement request_system_control()\n"); + return; + } + board->interface->request_system_control(board, request_control); +} + +/* + * IBSRE + * Send REN true if v is non-zero or false if v is zero. + */ +int ibsre(gpib_board_t *board, int enable) +{ + if (board->master == 0) { + pr_err("gpib: tried to set REN when not system controller\n"); + return -1; + } + + board->interface->remote_enable(board, enable); /* set or clear REN */ + if (!enable) + usleep_range(100, 150); + + return 0; +} + +/* + * IBPAD + * change the GPIB address of the interface board. The address + * must be 0 through 30. ibonl resets the address to PAD. + */ +int ibpad(gpib_board_t *board, unsigned int addr) +{ + if (addr > MAX_GPIB_PRIMARY_ADDRESS) { + pr_err("gpib: invalid primary address %u\n", addr); + return -1; + } + board->pad = addr; + if (board->online) + board->interface->primary_address(board, board->pad); + dev_dbg(board->gpib_dev, "set primary addr to %i\n", board->pad); + return 0; +} + +/* + * IBSAD + * change the secondary GPIB address of the interface board. + * The address must be 0 through 30, or negative disables. ibonl resets the + * address to SAD. + */ +int ibsad(gpib_board_t *board, int addr) +{ + if (addr > MAX_GPIB_SECONDARY_ADDRESS) { + pr_err("gpib: invalid secondary address %i\n", addr); + return -1; + } + board->sad = addr; + if (board->online) { + if (board->sad >= 0) + board->interface->secondary_address(board, board->sad, 1); + else + board->interface->secondary_address(board, 0, 0); + } + dev_dbg(board->gpib_dev, "set secondary addr to %i\n", board->sad); + + return 0; +} + +/* + * IBEOS + * Set the end-of-string modes for I/O operations to v. + * + */ +int ibeos(gpib_board_t *board, int eos, int eosflags) +{ + int retval; + + if (eosflags & ~EOS_MASK) { + pr_err("bad EOS modes\n"); + return -EINVAL; + } + if (eosflags & REOS) { + retval = board->interface->enable_eos(board, eos, eosflags & BIN); + } else { + board->interface->disable_eos(board); + retval = 0; + } + return retval; +} + +int ibstatus(gpib_board_t *board) +{ + return general_ibstatus(board, NULL, 0, 0, NULL); +} + +int general_ibstatus(gpib_board_t *board, const gpib_status_queue_t *device, + int clear_mask, int set_mask, gpib_descriptor_t *desc) +{ + int status = 0; + short line_status; + + if (board->private_data) { + status = board->interface->update_status(board, clear_mask); + /* XXX should probably stop having drivers use TIMO bit in + * board->status to avoid confusion + */ + status &= ~TIMO; + /* get real SRQI status if we can */ + if (iblines(board, &line_status) == 0) { + if ((line_status & ValidSRQ)) { + if ((line_status & BusSRQ)) + status |= SRQI; + else + status &= ~SRQI; + } + } + } + if (device) + if (num_status_bytes(device)) + status |= RQS; + + if (desc) { + if (set_mask & CMPL) + atomic_set(&desc->io_in_progress, 0); + else if (clear_mask & CMPL) + atomic_set(&desc->io_in_progress, 1); + + if (atomic_read(&desc->io_in_progress)) + status &= ~CMPL; + else + status |= CMPL; + } + if (num_gpib_events(&board->event_queue)) + status |= EVENT; + else + status &= ~EVENT; + + return status; +} + +struct wait_info { + gpib_board_t *board; + struct timer_list timer; + int timed_out; + unsigned long usec_timeout; +}; + +static void wait_timeout(struct timer_list *t) +{ + struct wait_info *winfo = from_timer(winfo, t, timer); + + winfo->timed_out = 1; + wake_up_interruptible(&winfo->board->wait); +} + +static void init_wait_info(struct wait_info *winfo) +{ + winfo->board = NULL; + winfo->timed_out = 0; + timer_setup_on_stack(&winfo->timer, wait_timeout, 0); +} + +static int wait_satisfied(struct wait_info *winfo, gpib_status_queue_t *status_queue, + int wait_mask, int *status, gpib_descriptor_t *desc) +{ + gpib_board_t *board = winfo->board; + int temp_status; + + if (mutex_lock_interruptible(&board->big_gpib_mutex)) + return -ERESTARTSYS; + + temp_status = general_ibstatus(board, status_queue, 0, 0, desc); + + mutex_unlock(&board->big_gpib_mutex); + + if (winfo->timed_out) + temp_status |= TIMO; + else + temp_status &= ~TIMO; + if (wait_mask & temp_status) { + *status = temp_status; + return 1; + } +//XXX does wait for END work? + return 0; +} + +/* install timer interrupt handler */ +static void start_wait_timer(struct wait_info *winfo) +/* Starts the timeout task */ +{ + winfo->timed_out = 0; + + if (winfo->usec_timeout > 0) + mod_timer(&winfo->timer, jiffies + usec_to_jiffies(winfo->usec_timeout)); +} + +static void remove_wait_timer(struct wait_info *winfo) +{ + del_timer_sync(&winfo->timer); + destroy_timer_on_stack(&winfo->timer); +} + +/* + * IBWAIT + * Check or wait for a GPIB event to occur. The mask argument + * is a bit vector corresponding to the status bit vector. It + * has a bit set for each condition which can terminate the wait + * If the mask is 0 then + * no condition is waited for. + */ +int ibwait(gpib_board_t *board, int wait_mask, int clear_mask, int set_mask, + int *status, unsigned long usec_timeout, gpib_descriptor_t *desc) +{ + int retval = 0; + gpib_status_queue_t *status_queue; + struct wait_info winfo; + + if (desc->is_board) + status_queue = NULL; + else + status_queue = get_gpib_status_queue(board, desc->pad, desc->sad); + + if (wait_mask == 0) { + *status = general_ibstatus(board, status_queue, clear_mask, set_mask, desc); + return 0; + } + + mutex_unlock(&board->big_gpib_mutex); + + init_wait_info(&winfo); + winfo.board = board; + winfo.usec_timeout = usec_timeout; + start_wait_timer(&winfo); + + if (wait_event_interruptible(board->wait, wait_satisfied(&winfo, status_queue, + wait_mask, status, desc))) { + dev_dbg(board->gpib_dev, "wait interrupted\n"); + retval = -ERESTARTSYS; + } + remove_wait_timer(&winfo); + + if (retval) + return retval; + if (mutex_lock_interruptible(&board->big_gpib_mutex)) + return -ERESTARTSYS; + + /* make sure we only clear status bits that we are reporting */ + if (*status & clear_mask || set_mask) + general_ibstatus(board, status_queue, *status & clear_mask, set_mask, 0); + + return 0; +} + +/* + * IBWRT + * Write cnt bytes of data from buf to the GPIB. The write + * operation terminates only on I/O complete. + * + * NOTE: + * 1. Prior to beginning the write, the interface is + * placed in the controller standby state. + * 2. Prior to calling ibwrt, the intended devices as + * well as the interface board itself must be + * addressed by calling ibcmd. + */ +int ibwrt(gpib_board_t *board, uint8_t *buf, size_t cnt, int send_eoi, size_t *bytes_written) +{ + int ret = 0; + int retval; + + if (cnt == 0) { + pr_warn("gpib: %s() called with zero length?\n", __func__); + return 0; + } + + if (board->master) { + retval = ibgts(board); + if (retval < 0) + return retval; + } + os_start_timer(board, board->usec_timeout); + ret = board->interface->write(board, buf, cnt, send_eoi, bytes_written); + + if (io_timed_out(board)) + ret = -ETIMEDOUT; + + os_remove_timer(board); + + return ret; +} + diff --git a/drivers/staging/gpib/common/ibsys.h b/drivers/staging/gpib/common/ibsys.h new file mode 100644 index 0000000000000..da20971e9c7ee --- /dev/null +++ b/drivers/staging/gpib/common/ibsys.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include "gpibP.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_GPIB_PRIMARY_ADDRESS 30 +#define MAX_GPIB_SECONDARY_ADDRESS 31 + +int gpib_allocate_board(gpib_board_t *board); +void gpib_deallocate_board(gpib_board_t *board); + +unsigned int num_status_bytes(const gpib_status_queue_t *dev); +int push_status_byte(gpib_board_t *board, gpib_status_queue_t *device, uint8_t poll_byte); +int pop_status_byte(gpib_board_t *board, gpib_status_queue_t *device, uint8_t *poll_byte); +gpib_status_queue_t *get_gpib_status_queue(gpib_board_t *board, unsigned int pad, int sad); +int get_serial_poll_byte(gpib_board_t *board, unsigned int pad, int sad, + unsigned int usec_timeout, uint8_t *poll_byte); +int autopoll_all_devices(gpib_board_t *board); diff --git a/drivers/staging/gpib/eastwood/Makefile b/drivers/staging/gpib/eastwood/Makefile new file mode 100644 index 0000000000000..c74056f959d03 --- /dev/null +++ b/drivers/staging/gpib/eastwood/Makefile @@ -0,0 +1,3 @@ + +obj-m += fluke_gpib.o + diff --git a/drivers/staging/gpib/eastwood/fluke_gpib.c b/drivers/staging/gpib/eastwood/fluke_gpib.c new file mode 100644 index 0000000000000..3f938ab0c84d9 --- /dev/null +++ b/drivers/staging/gpib/eastwood/fluke_gpib.c @@ -0,0 +1,1179 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * GPIB Driver for Fluke cda devices. Basically, its a driver for a (bugfixed) + * cb7210 connected to channel 0 of a pl330 dma controller. + * Author: Frank Mori Hess + * copyright: (C) 2006, 2010, 2015 Fluke Corporation + ***************************************************************************/ + +#include "fluke_gpib.h" + +#include "gpibP.h" +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB Driver for Fluke cda devices"); + +static int fluke_attach_holdoff_all(gpib_board_t *board, const gpib_board_config_t *config); +static int fluke_attach_holdoff_end(gpib_board_t *board, const gpib_board_config_t *config); +static void fluke_detach(gpib_board_t *board); +static int fluke_config_dma(gpib_board_t *board, int output); +static irqreturn_t fluke_gpib_internal_interrupt(gpib_board_t *board); + +static struct platform_device *fluke_gpib_pdev; + +static uint8_t fluke_locking_read_byte(struct nec7210_priv *nec_priv, unsigned int register_number) +{ + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + retval = fluke_read_byte_nolock(nec_priv, register_number); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); + return retval; +} + +static void fluke_locking_write_byte(struct nec7210_priv *nec_priv, uint8_t byte, + unsigned int register_number) +{ + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + fluke_write_byte_nolock(nec_priv, byte, register_number); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); +} + +// wrappers for interface functions +static int fluke_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int fluke_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int fluke_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int fluke_take_control(gpib_board_t *board, int synchronous) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int fluke_go_to_standby(gpib_board_t *board) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static void fluke_request_system_control(gpib_board_t *board, int request_control) +{ + struct fluke_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + nec7210_request_system_control(board, nec_priv, request_control); +} + +static void fluke_interface_clear(gpib_board_t *board, int assert) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void fluke_remote_enable(gpib_board_t *board, int enable) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int fluke_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void fluke_disable_eos(gpib_board_t *board) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int fluke_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int fluke_primary_address(gpib_board_t *board, unsigned int address) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int fluke_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int fluke_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void fluke_parallel_poll_configure(gpib_board_t *board, uint8_t configuration) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); +} + +static void fluke_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void fluke_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static uint8_t fluke_serial_poll_status(gpib_board_t *board) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void fluke_return_to_local(gpib_board_t *board) +{ + struct fluke_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + write_byte(nec_priv, AUX_RTL2, AUXMR); + udelay(1); + write_byte(nec_priv, AUX_RTL, AUXMR); +} + +static int fluke_line_status(const gpib_board_t *board) +{ + int status = ValidALL; + int bsr_bits; + struct fluke_priv *e_priv; + struct nec7210_priv *nec_priv; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + bsr_bits = fluke_paged_read_byte(e_priv, BUS_STATUS, BUS_STATUS_PAGE); + + if ((bsr_bits & BSR_REN_BIT) == 0) + status |= BusREN; + if ((bsr_bits & BSR_IFC_BIT) == 0) + status |= BusIFC; + if ((bsr_bits & BSR_SRQ_BIT) == 0) + status |= BusSRQ; + if ((bsr_bits & BSR_EOI_BIT) == 0) + status |= BusEOI; + if ((bsr_bits & BSR_NRFD_BIT) == 0) + status |= BusNRFD; + if ((bsr_bits & BSR_NDAC_BIT) == 0) + status |= BusNDAC; + if ((bsr_bits & BSR_DAV_BIT) == 0) + status |= BusDAV; + if ((bsr_bits & BSR_ATN_BIT) == 0) + status |= BusATN; + + return status; +} + +static unsigned int fluke_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 350) { + write_byte(nec_priv, AUX_HI_SPEED, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + } + return retval; +} + +static int lacs_or_read_ready(gpib_board_t *board) +{ + const struct fluke_priv *e_priv = board->private_data; + const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + int retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = test_bit(LACS_NUM, &board->status) || test_bit(READ_READY_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +/* Wait until it is possible for a read to do something useful. This + * is not essential, it only exists to prevent RFD holdoff from being released pointlessly. + */ +static int wait_for_read(gpib_board_t *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + lacs_or_read_ready(board) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +/* Check if the SH state machine is in SGNS. We check twice since there is a very small chance + * we could be blowing through SGNS from SIDS to SDYS if there is already a + * byte available in the handshake state machine. We are interested + * in the case where the handshake is stuck in SGNS due to no byte being + * available to the chip (and thus we can be confident a dma transfer will + * result in at least one byte making it into the chip). This matters + * because we want to be confident before sending a "send eoi" auxilary + * command that we will be able to also put the associated data byte + * in the chip before any potential timeout. + */ +static int source_handshake_is_sgns(struct fluke_priv *e_priv) +{ + int i; + + for (i = 0; i < 2; ++i) { + if ((fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) & + SOURCE_HANDSHAKE_MASK) != SOURCE_HANDSHAKE_SGNS_BITS) { + return 0; + } + } + return 1; +} + +static int source_handshake_is_sids_or_sgns(struct fluke_priv *e_priv) +{ + unsigned int source_handshake_bits; + + source_handshake_bits = fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) & + SOURCE_HANDSHAKE_MASK; + + return (source_handshake_bits == SOURCE_HANDSHAKE_SGNS_BITS) || + (source_handshake_bits == SOURCE_HANDSHAKE_SIDS_BITS); +} + +/* Wait until the gpib chip is ready to accept a data out byte. + * If the chip is SGNS it is probably waiting for a a byte to + * be written to it. + */ +static int wait_for_data_out_ready(gpib_board_t *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; +// printk("%s: enter\n", __FUNCTION__); + + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + source_handshake_is_sgns(e_priv)) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; +// printk("%s: exit, retval=%i\n", __FUNCTION__, retval); + return retval; +} + +static int wait_for_sids_or_sgns(gpib_board_t *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; +// printk("%s: enter\n", __FUNCTION__); + + if (wait_event_interruptible(board->wait, + source_handshake_is_sids_or_sgns(e_priv) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; +// printk("%s: exit, retval=%i\n", __FUNCTION__, retval); + return retval; +} + +static void fluke_dma_callback(void *arg) +{ + gpib_board_t *board = arg; + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); +// printk("%s: enter\n", __FUNCTION__); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE); + wake_up_interruptible(&board->wait); + + fluke_gpib_internal_interrupt(board); + clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); +// printk("%s: exit\n", __FUNCTION__); + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fluke_dma_write(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + int retval = 0; + dma_addr_t address; + struct dma_async_tx_descriptor *tx_desc; + + *bytes_written = 0; +// printk("%s: enter\n", __FUNCTION__); + if (WARN_ON_ONCE(length > e_priv->dma_buffer_size)) + return -EFAULT; + dmaengine_terminate_all(e_priv->dma_channel); + // write-clear counter + writel(0x0, e_priv->write_transfer_counter); + + memcpy(e_priv->dma_buffer, buffer, length); + address = dma_map_single(board->dev, e_priv->dma_buffer, + length, DMA_TO_DEVICE); + /* program dma controller */ + retval = fluke_config_dma(board, 1); + if (retval) + goto cleanup; + + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + pr_err("fluke_gpib: failed to allocate dma transmit descriptor\n"); + retval = -ENOMEM; + goto cleanup; + } + tx_desc->callback = fluke_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + + clear_bit(WRITE_READY_BN, &nec_priv->state); + set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + + // printk("%s: in spin lock\n", __FUNCTION__); + spin_unlock_irqrestore(&board->spinlock, flags); + +// printk("%s: waiting for write.\n", __FUNCTION__); + // suspend until message is sent + if (wait_event_interruptible(board->wait, + ((readl(e_priv->write_transfer_counter) & + write_transfer_counter_mask) == length) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted!\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + // disable board's dma + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fluke_dma_callback got called + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state)) + fluke_dma_callback(board); + + /* if everything went fine, try to wait until last byte is actually + * transmitted across gpib (but don't try _too_ hard) + */ + if (retval == 0) + retval = wait_for_sids_or_sgns(board); + + *bytes_written = readl(e_priv->write_transfer_counter) & write_transfer_counter_mask; + if (WARN_ON_ONCE(*bytes_written > length)) + return -EFAULT; + +cleanup: + dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE); +// printk("%s: exit, retval=%d\n", __FUNCTION__, retval); + return retval; +} + +static int fluke_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remainder = length; + size_t transfer_size; + ssize_t retval = 0; + size_t dma_remainder = remainder; + + if (!e_priv->dma_channel) { + pr_err("fluke_gpib: No dma channel available, cannot do accel write."); + return -ENXIO; + } + + *bytes_written = 0; + if (length < 1) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + + if (send_eoi) + --dma_remainder; +// printk("%s: entering while loop\n", __FUNCTION__); + + while (dma_remainder > 0) { + size_t num_bytes; + + retval = wait_for_data_out_ready(board); + if (retval < 0) + break; + + transfer_size = (e_priv->dma_buffer_size < dma_remainder) ? + e_priv->dma_buffer_size : dma_remainder; + retval = fluke_dma_write(board, buffer, transfer_size, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + break; + dma_remainder -= num_bytes; + remainder -= num_bytes; + buffer += num_bytes; + if (need_resched()) + schedule(); + } + if (retval < 0) + return retval; + //handle sending of last byte with eoi + if (send_eoi) { + size_t num_bytes; + // printk("%s: handling last byte\n", __FUNCTION__); + if (WARN_ON_ONCE(remainder != 1)) + return -EFAULT; + + /* wait until we are sure we will be able to write the data byte + * into the chip before we send AUX_SEOI. This prevents a timeout + * scenerio where we send AUX_SEOI but then timeout without getting + * any bytes into the gpib chip. This will result in the first byte + * of the next write having a spurious EOI set on the first byte. + */ + retval = wait_for_data_out_ready(board); + if (retval < 0) + return retval; + + write_byte(nec_priv, AUX_SEOI, AUXMR); + retval = fluke_dma_write(board, buffer, remainder, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + remainder -= num_bytes; + } +// printk("%s: bytes send=%i\n", __FUNCTION__, (int)(length - remainder)); + return 0; +} + +static int fluke_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie) +{ + struct dma_tx_state state; + int result; + + result = dmaengine_pause(chan); + if (result < 0) { + pr_err("fluke_gpib: dma pause failed?\n"); + return result; + } + dmaengine_tx_status(chan, cookie, &state); + // hardware doesn't support resume, so dont call this + // method unless the dma transfer is done. + return state.residue; +} + +static int fluke_dma_read(gpib_board_t *board, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned long flags; + int residue; + dma_addr_t bus_address; + struct dma_async_tx_descriptor *tx_desc; + dma_cookie_t dma_cookie; + int i; + static const int timeout = 10; + + // printk("%s: enter, bus_address=0x%x, length=%i\n", __FUNCTION__, + // (unsigned)bus_address, + // (int)length); + + *bytes_read = 0; + *end = 0; + if (length == 0) + return 0; + + bus_address = dma_map_single(board->dev, e_priv->dma_buffer, + length, DMA_FROM_DEVICE); + + /* program dma controller */ + retval = fluke_config_dma(board, 0); + if (retval) { + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + return retval; + } + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, + bus_address, length, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + pr_err("fluke_gpib: failed to allocate dma transmit descriptor\n"); + dma_unmap_single(NULL, bus_address, length, DMA_FROM_DEVICE); + return -EIO; + } + tx_desc->callback = fluke_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + // enable nec7210 dma + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + dma_cookie = dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + + set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + clear_bit(READ_READY_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); +// printk("waiting for data transfer.\n"); + // wait for data to transfer + if (wait_event_interruptible(board->wait, + test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0 || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_warn("fluke: dma read wait interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + + /* If we woke up because of end, wait until the dma transfer has pulled + * the data byte associated with the end before we cancel the dma transfer. + */ + if (test_bit(RECEIVED_END_BN, &nec_priv->state)) { + for (i = 0; i < timeout; ++i) { + if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0) + break; + if ((read_byte(nec_priv, ADR0) & DATA_IN_STATUS) == 0) + break; + usleep_range(10, 15); + } + if (i == timeout) + pr_warn("fluke_gpib: timeout waiting for dma to transfer end data byte.\n"); + } + + // stop the dma transfer + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + /* delay a little just to make sure any bytes in dma controller's fifo get + * written to memory before we disable it + */ + usleep_range(10, 15); + residue = fluke_get_dma_residue(e_priv->dma_channel, dma_cookie); + if (WARN_ON_ONCE(residue > length || residue < 0)) + return -EFAULT; + *bytes_read += length - residue; + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fluke_dma_callback got called + if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)) + fluke_dma_callback(board); + + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + memcpy(buffer, e_priv->dma_buffer, *bytes_read); + + /* If we got an end interrupt, figure out if it was + * associated with the last byte we dma'd or with a + * byte still sitting on the cb7210. + */ + spin_lock_irqsave(&board->spinlock, flags); + if (test_bit(READ_READY_BN, &nec_priv->state) == 0) { + // There is no byte sitting on the cb7210. If we + // saw an end interrupt, we need to deal with it now + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +static int fluke_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remain = length; + size_t transfer_size; + int retval = 0; + size_t dma_nbytes; + +/* printk("%s: enter, buffer=0x%p, length=%i\n", __FUNCTION__, + * buffer, (int)length); + * printk("\t dma_buffer=0x%p\n", e_priv->dma_buffer); + */ + *end = 0; + *bytes_read = 0; + + smp_mb__before_atomic(); + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + smp_mb__after_atomic(); + + retval = wait_for_read(board); + if (retval < 0) + return retval; + + nec7210_release_rfd_holdoff(board, nec_priv); + +// printk("%s: entering while loop\n", __FUNCTION__); + while (remain > 0) { + transfer_size = (e_priv->dma_buffer_size < remain) ? + e_priv->dma_buffer_size : remain; + retval = fluke_dma_read(board, buffer, transfer_size, end, &dma_nbytes); + remain -= dma_nbytes; + buffer += dma_nbytes; + *bytes_read += dma_nbytes; + if (*end) + break; + if (retval < 0) { +// printk("%s: early exit, retval=%i\n", __FUNCTION__, (int)retval); + return retval; + } + if (need_resched()) + schedule(); + } +// printk("%s: exit, retval=%i\n", __FUNCTION__, (int)retval); + return retval; +} + +gpib_interface_t fluke_unaccel_interface = { +name: "fluke_unaccel", +attach : fluke_attach_holdoff_all, +detach : fluke_detach, +read : fluke_read, +write : fluke_write, +command : fluke_command, +take_control : fluke_take_control, +go_to_standby : fluke_go_to_standby, +request_system_control : fluke_request_system_control, +interface_clear : fluke_interface_clear, +remote_enable : fluke_remote_enable, +enable_eos : fluke_enable_eos, +disable_eos : fluke_disable_eos, +parallel_poll : fluke_parallel_poll, +parallel_poll_configure : fluke_parallel_poll_configure, +parallel_poll_response : fluke_parallel_poll_response, +line_status : fluke_line_status, +update_status : fluke_update_status, +primary_address : fluke_primary_address, +secondary_address : fluke_secondary_address, +serial_poll_response : fluke_serial_poll_response, +serial_poll_status : fluke_serial_poll_status, +t1_delay : fluke_t1_delay, +return_to_local : fluke_return_to_local, +}; + +/* fluke_hybrid uses dma for writes but not for reads. Added + * to deal with occasional corruption of bytes seen when doing dma + * reads. From looking at the cb7210 vhdl, I believe the corruption + * is due to a hardware bug triggered by the cpu reading a cb7210 + * } + * register just as the dma controller is also doing a read. + */ + +gpib_interface_t fluke_hybrid_interface = { +name: "fluke_hybrid", +attach : fluke_attach_holdoff_all, +detach : fluke_detach, +read : fluke_read, +write : fluke_accel_write, +command : fluke_command, +take_control : fluke_take_control, +go_to_standby : fluke_go_to_standby, +request_system_control : fluke_request_system_control, +interface_clear : fluke_interface_clear, +remote_enable : fluke_remote_enable, +enable_eos : fluke_enable_eos, +disable_eos : fluke_disable_eos, +parallel_poll : fluke_parallel_poll, +parallel_poll_configure : fluke_parallel_poll_configure, +parallel_poll_response : fluke_parallel_poll_response, +line_status : fluke_line_status, +update_status : fluke_update_status, +primary_address : fluke_primary_address, +secondary_address : fluke_secondary_address, +serial_poll_response : fluke_serial_poll_response, +serial_poll_status : fluke_serial_poll_status, +t1_delay : fluke_t1_delay, +return_to_local : fluke_return_to_local, +}; + +gpib_interface_t fluke_interface = { +name: "fluke", +attach : fluke_attach_holdoff_end, +detach : fluke_detach, +read : fluke_accel_read, +write : fluke_accel_write, +command : fluke_command, +take_control : fluke_take_control, +go_to_standby : fluke_go_to_standby, +request_system_control : fluke_request_system_control, +interface_clear : fluke_interface_clear, +remote_enable : fluke_remote_enable, +enable_eos : fluke_enable_eos, +disable_eos : fluke_disable_eos, +parallel_poll : fluke_parallel_poll, +parallel_poll_configure : fluke_parallel_poll_configure, +parallel_poll_response : fluke_parallel_poll_response, +line_status : fluke_line_status, +update_status : fluke_update_status, +primary_address : fluke_primary_address, +secondary_address : fluke_secondary_address, +serial_poll_response : fluke_serial_poll_response, +serial_poll_status : fluke_serial_poll_status, +t1_delay : fluke_t1_delay, +return_to_local : fluke_return_to_local, +}; + +irqreturn_t fluke_gpib_internal_interrupt(gpib_board_t *board) +{ + int status0, status1, status2; + struct fluke_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int retval = IRQ_NONE; + + if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) + set_bit(READ_READY_BN, &nec_priv->state); + + status0 = fluke_paged_read_byte(priv, ISR0_IMR0, ISR0_IMR0_PAGE); + status1 = read_byte(nec_priv, ISR1); + status2 = read_byte(nec_priv, ISR2); + + if (status0 & FLUKE_IFCI_BIT) { + push_gpib_event(board, EventIFC); + retval = IRQ_HANDLED; + } + + if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED) + retval = IRQ_HANDLED; +/* + * if((status1 & nec_priv->reg_bits[IMR1]) || + * (status2 & (nec_priv->reg_bits[IMR2] & IMR2_ENABLE_INTR_MASK))) + * { + * printk("fluke: status1 0x%x, status2 0x%x\n", status1, status2); + * } + */ + + if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) { + if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state)) + set_bit(READ_READY_BN, &nec_priv->state); + else + clear_bit(READ_READY_BN, &nec_priv->state); + } + + if (retval == IRQ_HANDLED) + wake_up_interruptible(&board->wait); + + return retval; +} + +static irqreturn_t fluke_gpib_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = fluke_gpib_internal_interrupt(board); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static int fluke_allocate_private(gpib_board_t *board) +{ + struct fluke_priv *priv; + + board->private_data = kmalloc(sizeof(struct fluke_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + priv = board->private_data; + memset(priv, 0, sizeof(struct fluke_priv)); + init_nec7210_private(&priv->nec7210_priv); + priv->dma_buffer_size = 0x7ff; + priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL); + if (!priv->dma_buffer) + return -ENOMEM; + return 0; +} + +static void fluke_generic_detach(gpib_board_t *board) +{ + if (board->private_data) { + struct fluke_priv *e_priv = board->private_data; + + kfree(e_priv->dma_buffer); + kfree(board->private_data); + board->private_data = NULL; + } +} + +// generic part of attach functions shared by all cb7210 boards +static int fluke_generic_attach(gpib_board_t *board) +{ + struct fluke_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + + board->status = 0; + + retval = fluke_allocate_private(board); + if (retval < 0) + return retval; + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + nec_priv->read_byte = fluke_locking_read_byte; + nec_priv->write_byte = fluke_locking_write_byte; + nec_priv->offset = fluke_reg_offset; + nec_priv->type = CB7210; + return 0; +} + +static int fluke_config_dma(gpib_board_t *board, int output) +{ + struct fluke_priv *e_priv = board->private_data; + struct dma_slave_config config; + + config.src_maxburst = 1; + config.dst_maxburst = 1; + config.device_fc = true; + + if (output) { + config.direction = DMA_MEM_TO_DEV; + config.src_addr = 0; + config.dst_addr = e_priv->dma_port_res->start; + config.src_addr_width = 1; + config.dst_addr_width = 1; + } else { + config.direction = DMA_DEV_TO_MEM; + config.src_addr = e_priv->dma_port_res->start; + config.dst_addr = 0; + config.src_addr_width = 1; + config.dst_addr_width = 1; + } + return dmaengine_slave_config(e_priv->dma_channel, &config); +} + +static int fluke_init(struct fluke_priv *e_priv, gpib_board_t *board, int handshake_mode) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + + nec7210_board_reset(nec_priv, board); + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + /* set clock register for driving frequency + * ICR should be set to clock in megahertz (1-15) and to zero + * for clocks faster than 15 MHz (max 20MHz) + */ + write_byte(nec_priv, ICR | 10, AUXMR); + nec7210_set_handshake_mode(board, nec_priv, handshake_mode); + + nec7210_board_online(nec_priv, board); + + /* poll so we can detect ATN changes */ + if (gpib_request_pseudo_irq(board, fluke_gpib_interrupt)) { + pr_err("fluke_gpib: failed to allocate pseudo_irq\n"); + return -EINVAL; + } + + fluke_paged_write_byte(e_priv, FLUKE_IFCIE_BIT, ISR0_IMR0, ISR0_IMR0_PAGE); + return 0; +} + +/* This function is passed to dma_request_channel() in order to + * select the pl330 dma channel which has been hardwired to + * the gpib controller. + */ +static bool gpib_dma_channel_filter(struct dma_chan *chan, void *filter_param) +{ + // select the channel which is wired to the gpib chip + return chan->chan_id == 0; +} + +static int fluke_attach_impl(gpib_board_t *board, const gpib_board_config_t *config, + unsigned int handshake_mode) +{ + struct fluke_priv *e_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + int irq; + struct resource *res; + dma_cap_mask_t dma_cap; + + if (!fluke_gpib_pdev) { + pr_err("No gpib platform device was found, attach failed.\n"); + return -ENODEV; + } + + retval = fluke_generic_attach(board); + if (retval) + return retval; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + nec_priv->offset = fluke_reg_offset; + board->dev = &fluke_gpib_pdev->dev; + + res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for cb7210 gpib\n"); + return -ENODEV; + } + + if (request_mem_region(res->start, + resource_size(res), + fluke_gpib_pdev->name) == NULL) { + dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->gpib_iomem_res = res; + + nec_priv->iobase = ioremap(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + pr_info("gpib: iobase %lx remapped to %p, length=%d\n", + (unsigned long)e_priv->gpib_iomem_res->start, + nec_priv->iobase, (int)resource_size(e_priv->gpib_iomem_res)); + if (!nec_priv->iobase) { + dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n"); + return -ENOMEM; + } + + res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for gpib dma port\n"); + return -ENODEV; + } + if (request_mem_region(res->start, + resource_size(res), + fluke_gpib_pdev->name) == NULL) { + dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->dma_port_res = res; + + res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 2); + if (!res) { + dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for write transfer counter\n"); + return -ENODEV; + } + + if (request_mem_region(res->start, + resource_size(res), + fluke_gpib_pdev->name) == NULL) { + dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->write_transfer_counter_res = res; + + e_priv->write_transfer_counter = ioremap(e_priv->write_transfer_counter_res->start, + resource_size(e_priv->write_transfer_counter_res)); + pr_info("gpib: write transfer counter %lx remapped to %p, length=%d\n", + (unsigned long)e_priv->write_transfer_counter_res->start, + e_priv->write_transfer_counter, + (int)resource_size(e_priv->write_transfer_counter_res)); + if (!e_priv->write_transfer_counter) { + dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n"); + return -ENOMEM; + } + + irq = platform_get_irq(fluke_gpib_pdev, 0); + pr_info("gpib: irq %d\n", irq); + if (irq < 0) { + dev_err(&fluke_gpib_pdev->dev, "fluke_gpib: request for IRQ failed\n"); + return -EBUSY; + } + retval = request_irq(irq, fluke_gpib_interrupt, isr_flags, fluke_gpib_pdev->name, board); + if (retval) { + dev_err(&fluke_gpib_pdev->dev, + "cannot register interrupt handler err=%d\n", + retval); + return retval; + } + e_priv->irq = irq; + + dma_cap_zero(dma_cap); + dma_cap_set(DMA_SLAVE, dma_cap); + e_priv->dma_channel = dma_request_channel(dma_cap, gpib_dma_channel_filter, NULL); + if (!e_priv->dma_channel) { + pr_err("fluke_gpib: failed to allocate a dma channel.\n"); + // we don't error out here because unaccel interface will still + // work without dma + } + + return fluke_init(e_priv, board, handshake_mode); +} + +int fluke_attach_holdoff_all(gpib_board_t *board, const gpib_board_config_t *config) +{ + return fluke_attach_impl(board, config, HR_HLDA); +} + +int fluke_attach_holdoff_end(gpib_board_t *board, const gpib_board_config_t *config) +{ + return fluke_attach_impl(board, config, HR_HLDE); +} + +void fluke_detach(gpib_board_t *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (e_priv) { + if (e_priv->dma_channel) + dma_release_channel(e_priv->dma_channel); + gpib_free_pseudo_irq(board); + nec_priv = &e_priv->nec7210_priv; + + if (nec_priv->iobase) { + fluke_paged_write_byte(e_priv, 0, ISR0_IMR0, ISR0_IMR0_PAGE); + nec7210_board_reset(nec_priv, board); + } + if (e_priv->irq) + free_irq(e_priv->irq, board); + if (e_priv->write_transfer_counter_res) { + release_mem_region(e_priv->write_transfer_counter_res->start, + resource_size(e_priv->write_transfer_counter_res)); + } + if (e_priv->dma_port_res) { + release_mem_region(e_priv->dma_port_res->start, + resource_size(e_priv->dma_port_res)); + } + if (e_priv->gpib_iomem_res) + release_mem_region(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + } + fluke_generic_detach(board); +} + +static int fluke_gpib_probe(struct platform_device *pdev) +{ + fluke_gpib_pdev = pdev; + return 0; +} + +static const struct of_device_id fluke_gpib_of_match[] = { + { .compatible = "flk,fgpib-4.0"}, + { {0} } +}; +MODULE_DEVICE_TABLE(of, fluke_gpib_of_match); + +static struct platform_driver fluke_gpib_platform_driver = { + .driver = { + .name = "fluke_gpib", + .owner = THIS_MODULE, + .of_match_table = fluke_gpib_of_match, + }, + .probe = &fluke_gpib_probe +}; + +static int __init fluke_init_module(void) +{ + int result; + + result = platform_driver_register(&fluke_gpib_platform_driver); + if (result) { + pr_err("fluke_gpib: platform_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&fluke_unaccel_interface, THIS_MODULE); + gpib_register_driver(&fluke_hybrid_interface, THIS_MODULE); + gpib_register_driver(&fluke_interface, THIS_MODULE); + + pr_info("fluke_gpib\n"); + return 0; +} + +static void __exit fluke_exit_module(void) +{ + gpib_unregister_driver(&fluke_unaccel_interface); + gpib_unregister_driver(&fluke_hybrid_interface); + gpib_unregister_driver(&fluke_interface); + platform_driver_unregister(&fluke_gpib_platform_driver); +} + +module_init(fluke_init_module); +module_exit(fluke_exit_module); diff --git a/drivers/staging/gpib/eastwood/fluke_gpib.h b/drivers/staging/gpib/eastwood/fluke_gpib.h new file mode 100644 index 0000000000000..fcbd42f8f9af6 --- /dev/null +++ b/drivers/staging/gpib/eastwood/fluke_gpib.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Author: Frank Mori Hess + * copyright: (C) 2006, 2010, 2015 Fluke Corporation + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include "nec7210.h" + +struct fluke_priv { + struct nec7210_priv nec7210_priv; + struct resource *gpib_iomem_res; + struct resource *write_transfer_counter_res; + struct resource *dma_port_res; + int irq; + struct dma_chan *dma_channel; + u8 *dma_buffer; + int dma_buffer_size; + void *write_transfer_counter; +}; + +// cb7210 specific registers and bits +enum cb7210_regs { + STATE1_REG = 0x4, + ISR0_IMR0 = 0x6, + BUS_STATUS = 0x7 +}; + +enum cb7210_page_in { + ISR0_IMR0_PAGE = 1, + BUS_STATUS_PAGE = 1, + STATE1_PAGE = 1 +}; + +/* IMR0 -- Interrupt Mode Register 0 */ +enum imr0_bits { + FLUKE_IFCIE_BIT = 0x8, /* interface clear interrupt */ +}; + +/* ISR0 -- Interrupt Status Register 0 */ +enum isr0_bits { + FLUKE_IFCI_BIT = 0x8, /* interface clear interrupt */ +}; + +enum state1_bits { + SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */ + SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */ + SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */ + SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */ + SOURCE_HANDSHAKE_MASK = 0x7 +}; + +// we customized the cb7210 vhdl to give the "data in" status +// on the unused bit 7 of the address0 register. +enum cb7210_address0 { + DATA_IN_STATUS = 0x80 +}; + +static inline int cb7210_page_in_bits(unsigned int page) +{ + return 0x50 | (page & 0xf); +} + +// don't use without locking nec_priv->register_page_lock +static inline uint8_t fluke_read_byte_nolock(struct nec7210_priv *nec_priv, + int register_num) +{ + u8 retval; + + retval = readl(nec_priv->iobase + register_num * nec_priv->offset); + return retval; +} + +// don't use without locking nec_priv->register_page_lock +static inline void fluke_write_byte_nolock(struct nec7210_priv *nec_priv, uint8_t data, + int register_num) +{ + writel(data, nec_priv->iobase + register_num * nec_priv->offset); +} + +static inline uint8_t fluke_paged_read_byte(struct fluke_priv *e_priv, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR); + udelay(1); + /* chip auto clears the page after a read */ + retval = fluke_read_byte_nolock(nec_priv, register_num); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); + return retval; +} + +static inline void fluke_paged_write_byte(struct fluke_priv *e_priv, uint8_t data, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR); + udelay(1); + fluke_write_byte_nolock(nec_priv, data, register_num); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); +} + +enum bus_status_bits { + BSR_ATN_BIT = 0x1, + BSR_EOI_BIT = 0x2, + BSR_SRQ_BIT = 0x4, + BSR_IFC_BIT = 0x8, + BSR_REN_BIT = 0x10, + BSR_DAV_BIT = 0x20, + BSR_NRFD_BIT = 0x40, + BSR_NDAC_BIT = 0x80, +}; + +enum cb7210_aux_cmds { +/* AUX_RTL2 is an undocumented aux command which causes cb7210 to assert + * (and keep asserted) local rtl message. This is used in conjunction + * with the (stupid) cb7210 implementation + * of the normal nec7210 AUX_RTL aux command, which + * causes the rtl message to toggle between on and off. + */ + AUX_RTL2 = 0xd, + AUX_NBAF = 0xe, // new byte available false (also clears seoi) + AUX_LO_SPEED = 0x40, + AUX_HI_SPEED = 0x41, +}; + +enum { + fluke_reg_offset = 4, + fluke_num_regs = 8, + write_transfer_counter_mask = 0x7ff, +}; diff --git a/drivers/staging/gpib/fmh_gpib/Makefile b/drivers/staging/gpib/fmh_gpib/Makefile new file mode 100644 index 0000000000000..cc4d9e7cd5cdc --- /dev/null +++ b/drivers/staging/gpib/fmh_gpib/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_GPIB_FMH) += fmh_gpib.o diff --git a/drivers/staging/gpib/fmh_gpib/fmh_gpib.c b/drivers/staging/gpib/fmh_gpib/fmh_gpib.c new file mode 100644 index 0000000000000..62791db1c34a4 --- /dev/null +++ b/drivers/staging/gpib/fmh_gpib/fmh_gpib.c @@ -0,0 +1,1725 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * GPIB Driver for fmh_gpib_core, see + * https://github.com/fmhess/fmh_gpib_core + * + * More specifically, it is a driver for the hardware arrangement described by + * src/examples/fmh_gpib_top.vhd in the fmh_gpib_core repository. + * + * Author: Frank Mori Hess + * Copyright: (C) 2006, 2010, 2015 Fluke Corporation + * (C) 2017 Frank Mori Hess + ***************************************************************************/ + +#include "fmh_gpib.h" + +#include "gpibP.h" +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +static irqreturn_t fmh_gpib_interrupt(int irq, void *arg); +static int fmh_gpib_attach_holdoff_all(gpib_board_t *board, const gpib_board_config_t *config); +static int fmh_gpib_attach_holdoff_end(gpib_board_t *board, const gpib_board_config_t *config); +static void fmh_gpib_detach(gpib_board_t *board); +static int fmh_gpib_pci_attach_holdoff_all(gpib_board_t *board, const gpib_board_config_t *config); +static int fmh_gpib_pci_attach_holdoff_end(gpib_board_t *board, const gpib_board_config_t *config); +static void fmh_gpib_pci_detach(gpib_board_t *board); +static int fmh_gpib_config_dma(gpib_board_t *board, int output); +static irqreturn_t fmh_gpib_internal_interrupt(gpib_board_t *board); +static struct platform_driver fmh_gpib_platform_driver; +static struct pci_driver fmh_gpib_pci_driver; + +// wrappers for interface functions +static int fmh_gpib_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int fmh_gpib_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int fmh_gpib_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int fmh_gpib_take_control(gpib_board_t *board, int synchronous) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int fmh_gpib_go_to_standby(gpib_board_t *board) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static void fmh_gpib_request_system_control(gpib_board_t *board, int request_control) +{ + struct fmh_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + nec7210_request_system_control(board, nec_priv, request_control); +} + +static void fmh_gpib_interface_clear(gpib_board_t *board, int assert) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void fmh_gpib_remote_enable(gpib_board_t *board, int enable) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int fmh_gpib_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void fmh_gpib_disable_eos(gpib_board_t *board) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int fmh_gpib_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int fmh_gpib_primary_address(gpib_board_t *board, unsigned int address) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int fmh_gpib_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int fmh_gpib_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void fmh_gpib_parallel_poll_configure(gpib_board_t *board, uint8_t configuration) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); +} + +static void fmh_gpib_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void fmh_gpib_local_parallel_poll_mode(gpib_board_t *board, int local) +{ + struct fmh_priv *priv = board->private_data; + + if (local) { + write_byte(&priv->nec7210_priv, AUX_I_REG | LOCAL_PPOLL_MODE_BIT, AUXMR); + } else { + /* For fmh_gpib_core, remote parallel poll config mode is unaffected by the + * state of the disable bit of the parallel poll register (unlike the tnt4882). + * So, we don't need to worry about that. + */ + write_byte(&priv->nec7210_priv, AUX_I_REG | 0x0, AUXMR); + } +} + +static void fmh_gpib_serial_poll_response2(gpib_board_t *board, uint8_t status, + int new_reason_for_service) +{ + struct fmh_priv *priv = board->private_data; + unsigned long flags; + const int MSS = status & request_service_bit; + const int reqt = MSS && new_reason_for_service; + const int reqf = MSS == 0; + + spin_lock_irqsave(&board->spinlock, flags); + if (reqt) { + priv->nec7210_priv.srq_pending = 1; + clear_bit(SPOLL_NUM, &board->status); + } else if (reqf) { + priv->nec7210_priv.srq_pending = 0; + } + + if (reqt) { + /* It may seem like a race to issue reqt before updating + * the status byte, but it is not. The chip does not + * issue the reqt until the SPMR is written to at + * a later time. + */ + write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR); + } else if (reqf) { + write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR); + } + /* We need to always zero bit 6 of the status byte before writing it to + * the SPMR to insure we are using + * serial poll mode SP1, and not accidentally triggering mode SP3. + */ + write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR); + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static uint8_t fmh_gpib_serial_poll_status(gpib_board_t *board) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void fmh_gpib_return_to_local(gpib_board_t *board) +{ + struct fmh_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + write_byte(nec_priv, AUX_RTL2, AUXMR); + udelay(1); + write_byte(nec_priv, AUX_RTL, AUXMR); +} + +static int fmh_gpib_line_status(const gpib_board_t *board) +{ + int status = ValidALL; + int bsr_bits; + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + bsr_bits = read_byte(nec_priv, BUS_STATUS_REG); + + if ((bsr_bits & BSR_REN_BIT) == 0) + status |= BusREN; + if ((bsr_bits & BSR_IFC_BIT) == 0) + status |= BusIFC; + if ((bsr_bits & BSR_SRQ_BIT) == 0) + status |= BusSRQ; + if ((bsr_bits & BSR_EOI_BIT) == 0) + status |= BusEOI; + if ((bsr_bits & BSR_NRFD_BIT) == 0) + status |= BusNRFD; + if ((bsr_bits & BSR_NDAC_BIT) == 0) + status |= BusNDAC; + if ((bsr_bits & BSR_DAV_BIT) == 0) + status |= BusDAV; + if ((bsr_bits & BSR_ATN_BIT) == 0) + status |= BusATN; + + return status; +} + +static unsigned int fmh_gpib_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 350) { + write_byte(nec_priv, AUX_HI_SPEED, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + } + return retval; +} + +static int lacs_or_read_ready(gpib_board_t *board) +{ + const struct fmh_priv *e_priv = board->private_data; + const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + retval = test_bit(LACS_NUM, &board->status) || + test_bit(READ_READY_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +static int wait_for_read(gpib_board_t *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + lacs_or_read_ready(board) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +static int wait_for_rx_fifo_half_full_or_end(gpib_board_t *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & + RX_FIFO_HALF_FULL) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +/* Wait until the gpib chip is ready to accept a data out byte. + */ +static int wait_for_data_out_ready(gpib_board_t *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; +// printk("%s: enter\n", __FUNCTION__); + + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + (read_byte(nec_priv, EXT_STATUS_1_REG) & + DATA_OUT_STATUS_BIT)) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; +// printk("%s: exit, retval=%i\n", __FUNCTION__, retval); + return retval; +} + +static void fmh_gpib_dma_callback(void *arg) +{ + gpib_board_t *board = arg; + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); +// printk("%s: enter\n", __FUNCTION__); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE); + wake_up_interruptible(&board->wait); + + fmh_gpib_internal_interrupt(board); + + clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + + // printk("%s: exit\n", __FUNCTION__); + spin_unlock_irqrestore(&board->spinlock, flags); +} + +/* returns true when all the bytes of a write have been transferred to + * the chip and successfully transferred out over the gpib bus. + */ +static int fmh_gpib_all_bytes_are_sent(struct fmh_priv *e_priv) +{ + if (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & fifo_xfer_counter_mask) + return 0; + + if ((read_byte(&e_priv->nec7210_priv, EXT_STATUS_1_REG) & DATA_OUT_STATUS_BIT) == 0) + return 0; + + return 1; +} + +static int fmh_gpib_dma_write(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + int retval = 0; + dma_addr_t address; + struct dma_async_tx_descriptor *tx_desc; + + *bytes_written = 0; +// printk("%s: enter\n", __FUNCTION__); + if (WARN_ON_ONCE(length > e_priv->dma_buffer_size)) + return -EFAULT; + dmaengine_terminate_all(e_priv->dma_channel); + memcpy(e_priv->dma_buffer, buffer, length); + address = dma_map_single(board->dev, e_priv->dma_buffer, length, DMA_TO_DEVICE); + if (dma_mapping_error(board->dev, address)) + pr_err("dma mapping error in dma write!\n"); + /* program dma controller */ + retval = fmh_gpib_config_dma(board, 1); + if (retval) + goto cleanup; + + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + pr_err("fmh_gpib_gpib: failed to allocate dma transmit descriptor\n"); + retval = -ENOMEM; + goto cleanup; + } + tx_desc->callback = fmh_gpib_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, TX_FIFO_DMA_REQUEST_ENABLE | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + + dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + clear_bit(WRITE_READY_BN, &nec_priv->state); + set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); +// printk("%s: in spin lock\n", __FUNCTION__); + spin_unlock_irqrestore(&board->spinlock, flags); + +// printk("%s: waiting for write.\n", __FUNCTION__); + // suspend until message is sent + if (wait_event_interruptible(board->wait, + fmh_gpib_all_bytes_are_sent(e_priv) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted!\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + // disable board's dma + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fmh_gpib_dma_callback got called + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state)) + fmh_gpib_dma_callback(board); + + *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & + fifo_xfer_counter_mask); + if (WARN_ON_ONCE(*bytes_written > length)) + return -EFAULT; + /* printk("length=%i, *bytes_written=%i, residue=%i, retval=%i\n", + * length, *bytes_written, get_dma_residue(e_priv->dma_channel), retval); + */ +cleanup: + dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE); +// printk("%s: exit, retval=%d\n", __FUNCTION__, retval); + return retval; +} + +static int fmh_gpib_accel_write(gpib_board_t *board, uint8_t *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remainder = length; + size_t transfer_size; + ssize_t retval = 0; + size_t dma_remainder = remainder; + + if (!e_priv->dma_channel) { + pr_err("fmh_gpib_gpib: No dma channel available, cannot do accel write."); + return -ENXIO; + } + + *bytes_written = 0; + if (length < 1) + return 0; + + smp_mb__before_atomic(); + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + smp_mb__after_atomic(); + + if (send_eoi) + --dma_remainder; +// printk("%s: entering while loop\n", __FUNCTION__); + + while (dma_remainder > 0) { + size_t num_bytes; + + retval = wait_for_data_out_ready(board); + if (retval < 0) + break; + + transfer_size = (e_priv->dma_buffer_size < dma_remainder) ? + e_priv->dma_buffer_size : dma_remainder; + retval = fmh_gpib_dma_write(board, buffer, transfer_size, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + break; + dma_remainder -= num_bytes; + remainder -= num_bytes; + buffer += num_bytes; + if (need_resched()) + schedule(); + } + if (retval < 0) + return retval; + //handle sending of last byte with eoi + if (send_eoi) { + size_t num_bytes; + // printk("%s: handling last byte\n", __FUNCTION__); + if (WARN_ON_ONCE(remainder != 1)) + return -EFAULT; + + /* wait until we are sure we will be able to write the data byte + * into the chip before we send AUX_SEOI. This prevents a timeout + * scenario where we send AUX_SEOI but then timeout without getting + * any bytes into the gpib chip. This will result in the first byte + * of the next write having a spurious EOI set on the first byte. + */ + retval = wait_for_data_out_ready(board); + if (retval < 0) + return retval; + + write_byte(nec_priv, AUX_SEOI, AUXMR); + retval = fmh_gpib_dma_write(board, buffer, remainder, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + remainder -= num_bytes; + } +// printk("%s: bytes send=%i\n", __FUNCTION__, (int)(length - remainder)); + return 0; +} + +static int fmh_gpib_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie) +{ + struct dma_tx_state state; + int result; + + result = dmaengine_pause(chan); + if (result < 0) { + pr_err("fmh_gpib_gpib: dma pause failed?\n"); + return result; + } + dmaengine_tx_status(chan, cookie, &state); + // dma330 hardware doesn't support resume, so dont call this + // method unless the dma transfer is done. + return state.residue; +} + +static int wait_for_tx_fifo_half_empty(gpib_board_t *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; +// printk("%s: enter\n", __FUNCTION__); + + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & + TX_FIFO_HALF_EMPTY)) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; +// printk("%s: exit, retval=%i\n", __FUNCTION__, retval); + return retval; +} + +/* supports writing a chunk of data whose length must fit into the hardware'd xfer counter, + * called in a loop by fmh_gpib_fifo_write() + */ +static int fmh_gpib_fifo_write_countable(gpib_board_t *board, uint8_t *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned int remainder; + + *bytes_written = 0; +// printk("%s: enter\n", __FUNCTION__); + if (WARN_ON_ONCE(length > fifo_xfer_counter_mask)) + return -EFAULT; + + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + + remainder = length; + while (remainder > 0) { + int i; + + fifos_write(e_priv, TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG); + retval = wait_for_tx_fifo_half_empty(board); + if (retval < 0) + goto cleanup; + + for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && remainder > 0; ++i) { + unsigned int data_value = *buffer; + + if (send_eoi && remainder == 1) + data_value |= FIFO_DATA_EOI_FLAG; + fifos_write(e_priv, data_value, FIFO_DATA_REG); + ++buffer; + --remainder; + } + } + + // suspend until last byte is sent + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE); + if (wait_event_interruptible(board->wait, + fmh_gpib_all_bytes_are_sent(e_priv) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted!\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + +cleanup: + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + + *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & + fifo_xfer_counter_mask); + if (WARN_ON_ONCE(*bytes_written > length)) + return -EFAULT; + /* printk("length=%i, *bytes_written=%i, residue=%i, retval=%i\n", + * length, *bytes_written, get_dma_residue(e_priv->dma_channel), retval); + */ + +// printk("%s: exit, retval=%d\n", __FUNCTION__, retval); + return retval; +} + +static int fmh_gpib_fifo_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remainder = length; + size_t transfer_size; + ssize_t retval = 0; + + *bytes_written = 0; + if (length < 1) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + +// printk("%s: entering while loop\n", __FUNCTION__); + + while (remainder > 0) { + size_t num_bytes; + int last_pass; + + retval = wait_for_data_out_ready(board); + if (retval < 0) + break; + + if (fifo_xfer_counter_mask < remainder) { + // round transfer size to a multiple of half fifo size + transfer_size = (fifo_xfer_counter_mask / + fmh_gpib_half_fifo_size(e_priv)) * + fmh_gpib_half_fifo_size(e_priv); + last_pass = 0; + } else { + transfer_size = remainder; + last_pass = 1; + } + retval = fmh_gpib_fifo_write_countable(board, buffer, transfer_size, + last_pass && send_eoi, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + break; + remainder -= num_bytes; + buffer += num_bytes; + if (need_resched()) + schedule(); + } +// printk("%s: bytes send=%i\n", __FUNCTION__, (int)(length - remainder)); + return retval; +} + +static int fmh_gpib_dma_read(gpib_board_t *board, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned long flags; + int residue; + int wait_retval; + dma_addr_t bus_address; + struct dma_async_tx_descriptor *tx_desc; + dma_cookie_t dma_cookie; + + // printk("%s: enter, bus_address=0x%x, length=%i\n", __FUNCTION__, + //(unsigned)bus_address, +// (int)length); + + *bytes_read = 0; + *end = 0; + if (length == 0) + return 0; + + bus_address = dma_map_single(board->dev, e_priv->dma_buffer, + length, DMA_FROM_DEVICE); + if (dma_mapping_error(board->dev, bus_address)) + pr_err("dma mapping error in dma read!"); + + /* program dma controller */ + retval = fmh_gpib_config_dma(board, 0); + if (retval) { + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + return retval; + } + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, bus_address, + length, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + pr_err("fmh_gpib_gpib: failed to allocate dma transmit descriptor\n"); + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + return -EIO; + } + tx_desc->callback = fmh_gpib_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + // enable nec7210 dma + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, RX_FIFO_DMA_REQUEST_ENABLE | RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + dma_cookie = dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + + set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); +// printk("waiting for data transfer.\n"); + // wait for data to transfer + wait_retval = wait_event_interruptible(board->wait, + test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) + == 0 || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status)); + if (wait_retval) { + pr_warn("fmh_gpib: dma read wait interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + // stop the dma transfer + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + // give time for pl330 to transfer any in-flight data, since + // pl330 will throw it away when dmaengine_pause is called. + usleep_range(10, 15); + residue = fmh_gpib_get_dma_residue(e_priv->dma_channel, dma_cookie); + if (WARN_ON_ONCE(residue > length || residue < 0)) + return -EFAULT; + *bytes_read += length - residue; + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fmh_gpib_dma_callback got called + if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)) + fmh_gpib_dma_callback(board); + + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + memcpy(buffer, e_priv->dma_buffer, *bytes_read); + + /* Manually read any dregs out of fifo. */ + while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) { + if ((*bytes_read) >= length) { + dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d residue=%d\n", + (int)(*bytes_read), (int)length, (int)residue); + break; + } + buffer[(*bytes_read)++] = fifos_read(e_priv, FIFO_DATA_REG) & fifo_data_mask; + } + + /* If we got an end interrupt, figure out if it was + * associated with the last byte we dma'd or with a + * byte still sitting on the cb7210. + */ + spin_lock_irqsave(&board->spinlock, flags); + if (*bytes_read > 0 && test_bit(READ_READY_BN, &nec_priv->state) == 0) { + // If there is no byte sitting on the cb7210 and we + // saw an end, we need to deal with it now + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + } + spin_unlock_irqrestore(&board->spinlock, flags); +// printk("\tbytes_read=%i, residue=%i, end=%i, retval=%i, wait_retval=%i\n", +// *bytes_read, residue, *end, retval, wait_retval); + + return retval; +} + +static void fmh_gpib_release_rfd_holdoff(gpib_board_t *board, struct fmh_priv *e_priv) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned int ext_status_1; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG); + + /* if there is an end byte sitting on the chip, don't release + * holdoff. We want it left set after we read out the end + * byte. + */ + if ((ext_status_1 & (DATA_IN_STATUS_BIT | END_STATUS_BIT)) != + (DATA_IN_STATUS_BIT | END_STATUS_BIT)) { + if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT) + write_byte(nec_priv, AUX_FH, AUXMR); + + /* Check if an end byte raced in before we executed the AUX_FH command. + * If it did, we want to make sure the rfd holdoff is in effect. The end + * byte can arrive since + * AUX_RFD_HOLDOFF_ASAP doesn't immediately force the acceptor handshake + * to leave ACRS. + */ + if ((read_byte(nec_priv, EXT_STATUS_1_REG) & + (RFD_HOLDOFF_STATUS_BIT | DATA_IN_STATUS_BIT | END_STATUS_BIT)) == + (DATA_IN_STATUS_BIT | END_STATUS_BIT)) { + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } else { + clear_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + } + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fmh_gpib_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remain = length; + size_t transfer_size; + int retval = 0; + size_t dma_nbytes; + unsigned long flags; + + smp_mb__before_atomic(); + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + smp_mb__after_atomic(); + *end = 0; + *bytes_read = 0; + + retval = wait_for_read(board); + if (retval < 0) + return retval; + + fmh_gpib_release_rfd_holdoff(board, e_priv); + while (remain > 0) { + transfer_size = (e_priv->dma_buffer_size < remain) ? + e_priv->dma_buffer_size : remain; + retval = fmh_gpib_dma_read(board, buffer, transfer_size, end, &dma_nbytes); + remain -= dma_nbytes; + buffer += dma_nbytes; + *bytes_read += dma_nbytes; + if (*end) + break; + if (retval < 0) + break; + if (need_resched()) + schedule(); + } + + spin_lock_irqsave(&board->spinlock, flags); + if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state) == 0) { + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +/* Read a chunk of data whose length is within the limits of the hardware's + * xfer counter. Called in a loop from fmh_gpib_fifo_read(). + */ +static int fmh_gpib_fifo_read_countable(gpib_board_t *board, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + // printk("%s: enter, bus_address=0x%x, length=%i\n", __FUNCTION__, + // (unsigned)bus_address, +// (int)length); + + *bytes_read = 0; + *end = 0; + if (length == 0) + return 0; + + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + while (*bytes_read < length && *end == 0) { + int i; + + fifos_write(e_priv, RX_FIFO_HALF_FULL_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG); + retval = wait_for_rx_fifo_half_full_or_end(board); + if (retval < 0) + goto cleanup; + + for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && *end == 0; ++i) { + unsigned int data_value; + + data_value = fifos_read(e_priv, FIFO_DATA_REG); + buffer[(*bytes_read)++] = data_value & fifo_data_mask; + if (data_value & FIFO_DATA_EOI_FLAG) + *end = 1; + } + } + +cleanup: + // stop the transfer + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + + /* Manually read any dregs out of fifo. */ + while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) { + unsigned int data_value; + + if ((*bytes_read) >= length) { + dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d\n", + (int)(*bytes_read), (int)length); + break; + } + data_value = fifos_read(e_priv, FIFO_DATA_REG); + buffer[(*bytes_read)++] = data_value & fifo_data_mask; + if (data_value & FIFO_DATA_EOI_FLAG) + *end = 1; + } + +// printk("\tbytes_read=%i, end=%i, retval=%i, wait_retval=%i\n", +// *bytes_read, *end, retval, wait_retval); + + return retval; +} + +static int fmh_gpib_fifo_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remain = length; + size_t transfer_size; + int retval = 0; + size_t nbytes; + unsigned long flags; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + *end = 0; + *bytes_read = 0; + + /* Do a little prep with data in interrupt so that following wait_for_read() + * will wake up if a data byte is received. + */ + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, HR_DIIE); + fmh_gpib_interrupt(0, board); + + retval = wait_for_read(board); + if (retval < 0) + return retval; + + fmh_gpib_release_rfd_holdoff(board, e_priv); + while (remain > 0) { + if (fifo_xfer_counter_mask < remain) { + // round transfer size to a multiple of half fifo size + transfer_size = (fifo_xfer_counter_mask / + fmh_gpib_half_fifo_size(e_priv)) * + fmh_gpib_half_fifo_size(e_priv); + } else { + transfer_size = remain; + } + retval = fmh_gpib_fifo_read_countable(board, buffer, transfer_size, end, &nbytes); + remain -= nbytes; + buffer += nbytes; + *bytes_read += nbytes; + if (*end) + break; + if (retval < 0) + break; + if (need_resched()) + schedule(); + } + + if (*end == 0) { + spin_lock_irqsave(&board->spinlock, flags); + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + } + + return retval; +} + +gpib_interface_t fmh_gpib_unaccel_interface = { +name: "fmh_gpib_unaccel", +attach : fmh_gpib_attach_holdoff_all, +detach : fmh_gpib_detach, +read : fmh_gpib_read, +write : fmh_gpib_write, +command : fmh_gpib_command, +take_control : fmh_gpib_take_control, +go_to_standby : fmh_gpib_go_to_standby, +request_system_control : fmh_gpib_request_system_control, +interface_clear : fmh_gpib_interface_clear, +remote_enable : fmh_gpib_remote_enable, +enable_eos : fmh_gpib_enable_eos, +disable_eos : fmh_gpib_disable_eos, +parallel_poll : fmh_gpib_parallel_poll, +parallel_poll_configure : fmh_gpib_parallel_poll_configure, +parallel_poll_response : fmh_gpib_parallel_poll_response, +local_parallel_poll_mode : fmh_gpib_local_parallel_poll_mode, +line_status : fmh_gpib_line_status, +update_status : fmh_gpib_update_status, +primary_address : fmh_gpib_primary_address, +secondary_address : fmh_gpib_secondary_address, +serial_poll_response2 : fmh_gpib_serial_poll_response2, +serial_poll_status : fmh_gpib_serial_poll_status, +t1_delay : fmh_gpib_t1_delay, +return_to_local : fmh_gpib_return_to_local, +}; + +gpib_interface_t fmh_gpib_interface = { +name: "fmh_gpib", +attach : fmh_gpib_attach_holdoff_end, +detach : fmh_gpib_detach, +read : fmh_gpib_accel_read, +write : fmh_gpib_accel_write, +command : fmh_gpib_command, +take_control : fmh_gpib_take_control, +go_to_standby : fmh_gpib_go_to_standby, +request_system_control : fmh_gpib_request_system_control, +interface_clear : fmh_gpib_interface_clear, +remote_enable : fmh_gpib_remote_enable, +enable_eos : fmh_gpib_enable_eos, +disable_eos : fmh_gpib_disable_eos, +parallel_poll : fmh_gpib_parallel_poll, +parallel_poll_configure : fmh_gpib_parallel_poll_configure, +parallel_poll_response : fmh_gpib_parallel_poll_response, +local_parallel_poll_mode : fmh_gpib_local_parallel_poll_mode, +line_status : fmh_gpib_line_status, +update_status : fmh_gpib_update_status, +primary_address : fmh_gpib_primary_address, +secondary_address : fmh_gpib_secondary_address, +serial_poll_response2 : fmh_gpib_serial_poll_response2, +serial_poll_status : fmh_gpib_serial_poll_status, +t1_delay : fmh_gpib_t1_delay, +return_to_local : fmh_gpib_return_to_local, +}; + +gpib_interface_t fmh_gpib_pci_interface = { +name: "fmh_gpib_pci", +attach : fmh_gpib_pci_attach_holdoff_end, +detach : fmh_gpib_pci_detach, +read : fmh_gpib_fifo_read, +write : fmh_gpib_fifo_write, +command : fmh_gpib_command, +take_control : fmh_gpib_take_control, +go_to_standby : fmh_gpib_go_to_standby, +request_system_control : fmh_gpib_request_system_control, +interface_clear : fmh_gpib_interface_clear, +remote_enable : fmh_gpib_remote_enable, +enable_eos : fmh_gpib_enable_eos, +disable_eos : fmh_gpib_disable_eos, +parallel_poll : fmh_gpib_parallel_poll, +parallel_poll_configure : fmh_gpib_parallel_poll_configure, +parallel_poll_response : fmh_gpib_parallel_poll_response, +local_parallel_poll_mode : fmh_gpib_local_parallel_poll_mode, +line_status : fmh_gpib_line_status, +update_status : fmh_gpib_update_status, +primary_address : fmh_gpib_primary_address, +secondary_address : fmh_gpib_secondary_address, +serial_poll_response2 : fmh_gpib_serial_poll_response2, +serial_poll_status : fmh_gpib_serial_poll_status, +t1_delay : fmh_gpib_t1_delay, +return_to_local : fmh_gpib_return_to_local, +}; + +gpib_interface_t fmh_gpib_pci_unaccel_interface = { +name: "fmh_gpib_pci_unaccel", +attach : fmh_gpib_pci_attach_holdoff_all, +detach : fmh_gpib_pci_detach, +read : fmh_gpib_read, +write : fmh_gpib_write, +command : fmh_gpib_command, +take_control : fmh_gpib_take_control, +go_to_standby : fmh_gpib_go_to_standby, +request_system_control : fmh_gpib_request_system_control, +interface_clear : fmh_gpib_interface_clear, +remote_enable : fmh_gpib_remote_enable, +enable_eos : fmh_gpib_enable_eos, +disable_eos : fmh_gpib_disable_eos, +parallel_poll : fmh_gpib_parallel_poll, +parallel_poll_configure : fmh_gpib_parallel_poll_configure, +parallel_poll_response : fmh_gpib_parallel_poll_response, +local_parallel_poll_mode : fmh_gpib_local_parallel_poll_mode, +line_status : fmh_gpib_line_status, +update_status : fmh_gpib_update_status, +primary_address : fmh_gpib_primary_address, +secondary_address : fmh_gpib_secondary_address, +serial_poll_response2 : fmh_gpib_serial_poll_response2, +serial_poll_status : fmh_gpib_serial_poll_status, +t1_delay : fmh_gpib_t1_delay, +return_to_local : fmh_gpib_return_to_local, +}; + +irqreturn_t fmh_gpib_internal_interrupt(gpib_board_t *board) +{ + unsigned int status0, status1, status2, ext_status_1, fifo_status; + struct fmh_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int retval = IRQ_NONE; + + status0 = read_byte(nec_priv, ISR0_IMR0_REG); + status1 = read_byte(nec_priv, ISR1); + status2 = read_byte(nec_priv, ISR2); + fifo_status = fifos_read(priv, FIFO_CONTROL_STATUS_REG); + + if (status0 & IFC_INTERRUPT_BIT) { + push_gpib_event(board, EventIFC); + retval = IRQ_HANDLED; + } + + if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED) + retval = IRQ_HANDLED; + + ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG); + + if (ext_status_1 & DATA_IN_STATUS_BIT) + set_bit(READ_READY_BN, &nec_priv->state); + else + clear_bit(READ_READY_BN, &nec_priv->state); + + if (ext_status_1 & DATA_OUT_STATUS_BIT) + set_bit(WRITE_READY_BN, &nec_priv->state); + else + clear_bit(WRITE_READY_BN, &nec_priv->state); + + if (ext_status_1 & COMMAND_OUT_STATUS_BIT) + set_bit(COMMAND_READY_BN, &nec_priv->state); + else + clear_bit(COMMAND_READY_BN, &nec_priv->state); + + if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT) + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + else + clear_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + if (ext_status_1 & END_STATUS_BIT) { + /* only set RECEIVED_END while there is still a data + * byte sitting in the chip, to avoid spuriously + * setting it multiple times after it has been cleared + * during a read. + */ + if (ext_status_1 & DATA_IN_STATUS_BIT) + set_bit(RECEIVED_END_BN, &nec_priv->state); + } else { + clear_bit(RECEIVED_END_BN, &nec_priv->state); + } + + if ((fifo_status & TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED) && + (fifo_status & TX_FIFO_HALF_EMPTY)) { + /* We really only want to clear the + * TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE bit in the + * FIFO_CONTROL_STATUS_REG. Since we are not being + * careful, this also has a side effect of disabling + * DMA requests and the RX fifo interrupt. That is + * fine though, since they should never be in use at + * the same time as the TX fifo interrupt. + */ + fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG); + retval = IRQ_HANDLED; + } + + if ((fifo_status & RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED) && + (fifo_status & RX_FIFO_HALF_FULL)) { + /* We really only want to clear the + * RX_FIFO_HALF_FULL_INTERRUPT_ENABLE bit in the + * FIFO_CONTROL_STATUS_REG. Since we are not being + * careful, this also has a side effect of disabling + * DMA requests and the TX fifo interrupt. That is + * fine though, since they should never be in use at + * the same time as the RX fifo interrupt. + */ + fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG); + retval = IRQ_HANDLED; + } + + if (retval == IRQ_HANDLED) + wake_up_interruptible(&board->wait); + + return retval; +} + +irqreturn_t fmh_gpib_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = fmh_gpib_internal_interrupt(board); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static int fmh_gpib_allocate_private(gpib_board_t *board) +{ + struct fmh_priv *priv; + + board->private_data = kmalloc(sizeof(struct fmh_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + priv = board->private_data; + memset(priv, 0, sizeof(struct fmh_priv)); + init_nec7210_private(&priv->nec7210_priv); + priv->dma_buffer_size = 0x800; + priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL); + if (!priv->dma_buffer) + return -ENOMEM; + return 0; +} + +static void fmh_gpib_generic_detach(gpib_board_t *board) +{ + if (board->private_data) { + struct fmh_priv *e_priv = board->private_data; + + kfree(e_priv->dma_buffer); + kfree(board->private_data); + board->private_data = NULL; + } + if (board->dev) + dev_set_drvdata(board->dev, NULL); +} + +// generic part of attach functions +static int fmh_gpib_generic_attach(gpib_board_t *board) +{ + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + + board->status = 0; + + retval = fmh_gpib_allocate_private(board); + if (retval < 0) + return retval; + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + nec_priv->read_byte = gpib_cs_read_byte; + nec_priv->write_byte = gpib_cs_write_byte; + nec_priv->offset = 1; + nec_priv->type = CB7210; + return 0; +} + +static int fmh_gpib_config_dma(gpib_board_t *board, int output) +{ + struct fmh_priv *e_priv = board->private_data; + struct dma_slave_config config; + + config.device_fc = true; + + if (e_priv->dma_burst_length < 1) { + config.src_maxburst = 1; + config.dst_maxburst = 1; + } else { + config.src_maxburst = e_priv->dma_burst_length; + config.dst_maxburst = e_priv->dma_burst_length; + } + + config.src_addr_width = 1; + config.dst_addr_width = 1; + + if (output) { + config.direction = DMA_MEM_TO_DEV; + config.src_addr = 0; + config.dst_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset; + } else { + config.direction = DMA_DEV_TO_MEM; + config.src_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset; + config.dst_addr = 0; + } + return dmaengine_slave_config(e_priv->dma_channel, &config); +} + +static int fmh_gpib_init(struct fmh_priv *e_priv, gpib_board_t *board, int handshake_mode) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + unsigned int fifo_status_bits; + + fifos_write(e_priv, RX_FIFO_CLEAR | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + + nec7210_board_reset(nec_priv, board); + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + nec7210_set_handshake_mode(board, nec_priv, handshake_mode); + + /* Hueristically check if hardware supports fifo half full/empty interrupts */ + fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG); + e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) && + (fifo_status_bits & TX_FIFO_HALF_EMPTY); + + nec7210_board_online(nec_priv, board); + + write_byte(nec_priv, IFC_INTERRUPT_ENABLE_BIT | ATN_INTERRUPT_ENABLE_BIT, ISR0_IMR0_REG); + + spin_lock_irqsave(&board->spinlock, flags); + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + return 0; +} + +/* Match callback for driver_find_device */ +static int fmh_gpib_device_match(struct device *dev, const void *data) +{ + const gpib_board_config_t *config = data; + + if (dev_get_drvdata(dev)) + return 0; + + if (gpib_match_device_path(dev, config->device_path) == 0) + return 0; + + // driver doesn't support selection by serial number + if (config->serial_number) + return 0; + + dev_notice(dev, "matched: %s\n", of_node_full_name(dev_of_node((dev)))); + return 1; +} + +static int fmh_gpib_attach_impl(gpib_board_t *board, const gpib_board_config_t *config, + unsigned int handshake_mode, int acquire_dma) +{ + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + int irq; + struct resource *res; + struct platform_device *pdev; + + board->dev = driver_find_device(&fmh_gpib_platform_driver.driver, + NULL, (const void *)config, &fmh_gpib_device_match); + if (!board->dev) { + pr_err("No matching fmh_gpib_core device was found, attach failed."); + return -ENODEV; + } + // currently only used to mark the device as already attached + dev_set_drvdata(board->dev, board); + pdev = to_platform_device(board->dev); + + retval = fmh_gpib_generic_attach(board); + if (retval) + return retval; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpib_control_status"); + if (!res) { + dev_err(board->dev, "Unable to locate mmio resource for cb7210 gpib\n"); + return -ENODEV; + } + + if (request_mem_region(res->start, + resource_size(res), + pdev->name) == NULL) { + dev_err(board->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->gpib_iomem_res = res; + + nec_priv->iobase = ioremap(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + if (!nec_priv->iobase) { + dev_err(board->dev, "Could not map I/O memory for gpib\n"); + return -ENOMEM; + } + dev_info(board->dev, "iobase 0x%lx remapped to %p, length=%ld\n", + (unsigned long)e_priv->gpib_iomem_res->start, + nec_priv->iobase, (unsigned long)resource_size(e_priv->gpib_iomem_res)); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma_fifos"); + if (!res) { + dev_err(board->dev, "Unable to locate mmio resource for gpib dma port\n"); + return -ENODEV; + } + if (request_mem_region(res->start, + resource_size(res), + pdev->name) == NULL) { + dev_err(board->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->dma_port_res = res; + e_priv->fifo_base = ioremap(e_priv->dma_port_res->start, + resource_size(e_priv->dma_port_res)); + if (!e_priv->fifo_base) { + dev_err(board->dev, "Could not map I/O memory for fifos\n"); + return -ENOMEM; + } + dev_info(board->dev, "dma fifos 0x%lx remapped to %p, length=%ld\n", + (unsigned long)e_priv->dma_port_res->start, e_priv->fifo_base, + (unsigned long)resource_size(e_priv->dma_port_res)); + + irq = platform_get_irq(pdev, 0); + pr_info("gpib: irq %d\n", irq); + if (irq < 0) { + dev_err(board->dev, "fmh_gpib_gpib: request for IRQ failed\n"); + return -EBUSY; + } + retval = request_irq(irq, fmh_gpib_interrupt, IRQF_SHARED, pdev->name, board); + if (retval) { + dev_err(board->dev, + "cannot register interrupt handler err=%d\n", + retval); + return retval; + } + e_priv->irq = irq; + + if (acquire_dma) { + e_priv->dma_channel = dma_request_slave_channel(board->dev, "rxtx"); + if (!e_priv->dma_channel) { + dev_err(board->dev, "failed to acquire dma channel \"rxtx\".\n"); + return -EIO; + } + } + /* in the future we might want to know the half-fifo size + * (dma_burst_length) even when not using dma, so go ahead an + * initialize it unconditionally. + */ + e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) & + fifo_max_burst_length_mask; + + return fmh_gpib_init(e_priv, board, handshake_mode); +} + +int fmh_gpib_attach_holdoff_all(gpib_board_t *board, const gpib_board_config_t *config) +{ + return fmh_gpib_attach_impl(board, config, HR_HLDA, 0); +} + +int fmh_gpib_attach_holdoff_end(gpib_board_t *board, const gpib_board_config_t *config) +{ + return fmh_gpib_attach_impl(board, config, HR_HLDE, 1); +} + +void fmh_gpib_detach(gpib_board_t *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (e_priv) { + if (e_priv->dma_channel) + dma_release_channel(e_priv->dma_channel); + nec_priv = &e_priv->nec7210_priv; + + if (e_priv->irq) + free_irq(e_priv->irq, board); + if (e_priv->fifo_base) + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + if (nec_priv->iobase) { + write_byte(nec_priv, 0, ISR0_IMR0_REG); + nec7210_board_reset(nec_priv, board); + } + if (e_priv->fifo_base) + iounmap(e_priv->fifo_base); + if (nec_priv->iobase) + iounmap(nec_priv->iobase); + if (e_priv->dma_port_res) { + release_mem_region(e_priv->dma_port_res->start, + resource_size(e_priv->dma_port_res)); + } + if (e_priv->gpib_iomem_res) + release_mem_region(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + } + fmh_gpib_generic_detach(board); +} + +static int fmh_gpib_pci_attach_impl(gpib_board_t *board, const gpib_board_config_t *config, + unsigned int handshake_mode) +{ + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + struct pci_dev *pci_device; + + retval = fmh_gpib_generic_attach(board); + if (retval) + return retval; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + // find board + pci_device = gpib_pci_get_device(config, BOGUS_PCI_VENDOR_ID_FLUKE, + BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, NULL); + if (!pci_device) { + pr_err("No matching fmh_gpib_core pci device was found, attach failed."); + return -ENODEV; + } + board->dev = &pci_device->dev; + + // bladerunner prototype has offset of 4 between gpib control/status registers + nec_priv->offset = 4; + + if (pci_enable_device(pci_device)) { + dev_err(board->dev, "error enabling pci device\n"); + return -EIO; + } + if (pci_request_regions(pci_device, KBUILD_MODNAME)) { + dev_err(board->dev, "pci_request_regions failed\n"); + return -EIO; + } + e_priv->gpib_iomem_res = &pci_device->resource[gpib_control_status_pci_resource_index]; + e_priv->dma_port_res = &pci_device->resource[gpib_fifo_pci_resource_index]; + + nec_priv->iobase = ioremap(pci_resource_start(pci_device, + gpib_control_status_pci_resource_index), + pci_resource_len(pci_device, + gpib_control_status_pci_resource_index)); + dev_info(board->dev, "base address for gpib control/status registers remapped to 0x%p\n", + nec_priv->iobase); + + if (e_priv->dma_port_res->flags & IORESOURCE_MEM) { + e_priv->fifo_base = ioremap(pci_resource_start(pci_device, + gpib_fifo_pci_resource_index), + pci_resource_len(pci_device, + gpib_fifo_pci_resource_index)); + dev_info(board->dev, "base address for gpib fifo registers remapped to 0x%p\n", + e_priv->fifo_base); + } else { + e_priv->fifo_base = NULL; + dev_info(board->dev, "hardware has no gpib fifo registers.\n"); + } + + if (pci_device->irq) { + retval = request_irq(pci_device->irq, fmh_gpib_interrupt, IRQF_SHARED, + KBUILD_MODNAME, board); + if (retval) { + dev_err(board->dev, + "cannot register interrupt handler err=%d\n", + retval); + return retval; + } + } + e_priv->irq = pci_device->irq; + + e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) & + fifo_max_burst_length_mask; + + return fmh_gpib_init(e_priv, board, handshake_mode); +} + +int fmh_gpib_pci_attach_holdoff_all(gpib_board_t *board, const gpib_board_config_t *config) +{ + return fmh_gpib_pci_attach_impl(board, config, HR_HLDA); +} + +int fmh_gpib_pci_attach_holdoff_end(gpib_board_t *board, const gpib_board_config_t *config) +{ + int retval; + struct fmh_priv *e_priv; + + retval = fmh_gpib_pci_attach_impl(board, config, HR_HLDE); + e_priv = board->private_data; + if (retval == 0 && e_priv && e_priv->supports_fifo_interrupts == 0) { + pr_err("fmh_gpib: your fmh_gpib_core does not appear to support fifo interrupts. Try the fmh_gpib_pci_unaccel board type instead."); + return -EIO; + } + return retval; +} + +void fmh_gpib_pci_detach(gpib_board_t *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (e_priv) { + nec_priv = &e_priv->nec7210_priv; + + if (e_priv->irq) + free_irq(e_priv->irq, board); + if (e_priv->fifo_base) + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + if (nec_priv->iobase) { + write_byte(nec_priv, 0, ISR0_IMR0_REG); + nec7210_board_reset(nec_priv, board); + } + if (e_priv->fifo_base) + iounmap(e_priv->fifo_base); + if (nec_priv->iobase) + iounmap(nec_priv->iobase); + if (e_priv->dma_port_res || e_priv->gpib_iomem_res) + pci_release_regions(to_pci_dev(board->dev)); + if (board->dev) + pci_dev_put(to_pci_dev(board->dev)); + } + fmh_gpib_generic_detach(board); +} + +static int fmh_gpib_platform_probe(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id fmh_gpib_of_match[] = { + { .compatible = "fmhess,fmh_gpib_core"}, + { {0} } +}; +MODULE_DEVICE_TABLE(of, fmh_gpib_of_match); + +static struct platform_driver fmh_gpib_platform_driver = { + .driver = { + .name = "fmh_gpib", + .owner = THIS_MODULE, + .of_match_table = fmh_gpib_of_match, + }, + .probe = &fmh_gpib_platform_probe +}; + +static int fmh_gpib_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id fmh_gpib_pci_match[] = { + { BOGUS_PCI_VENDOR_ID_FLUKE, BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, fmh_gpib_pci_match); + +static struct pci_driver fmh_gpib_pci_driver = { + .name = "fmh_gpib", + .id_table = fmh_gpib_pci_match, + .probe = &fmh_gpib_pci_probe +}; + +static int __init fmh_gpib_init_module(void) +{ + int result; + + result = platform_driver_register(&fmh_gpib_platform_driver); + if (result) { + pr_err("fmh_gpib: platform_driver_register failed!\n"); + return result; + } + + result = pci_register_driver(&fmh_gpib_pci_driver); + if (result) { + pr_err("fmh_gpib: pci_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&fmh_gpib_unaccel_interface, THIS_MODULE); + gpib_register_driver(&fmh_gpib_interface, THIS_MODULE); + gpib_register_driver(&fmh_gpib_pci_unaccel_interface, THIS_MODULE); + gpib_register_driver(&fmh_gpib_pci_interface, THIS_MODULE); + + pr_info("fmh_gpib\n"); + return 0; +} + +static void __exit fmh_gpib_exit_module(void) +{ + gpib_unregister_driver(&fmh_gpib_pci_interface); + gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface); + gpib_unregister_driver(&fmh_gpib_unaccel_interface); + gpib_unregister_driver(&fmh_gpib_interface); + + pci_unregister_driver(&fmh_gpib_pci_driver); + platform_driver_unregister(&fmh_gpib_platform_driver); +} + +module_init(fmh_gpib_init_module); +module_exit(fmh_gpib_exit_module); diff --git a/drivers/staging/gpib/fmh_gpib/fmh_gpib.h b/drivers/staging/gpib/fmh_gpib/fmh_gpib.h new file mode 100644 index 0000000000000..43bfc89d2a6fe --- /dev/null +++ b/drivers/staging/gpib/fmh_gpib/fmh_gpib.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Author: Frank Mori Hess + * Copyright: (C) 2006, 2010, 2015 Fluke Corporation + * (C) 2017 Frank Mori Hess + ***************************************************************************/ + +#include +#include +#include +#include +#include "nec7210.h" + +static const int fifo_reg_offset = 2; + +static const int gpib_control_status_pci_resource_index; +static const int gpib_fifo_pci_resource_index = 1; + +/* We don't have a real pci vendor/device id, the following will need to be + * patched to match prototype hardware. + */ +#define BOGUS_PCI_VENDOR_ID_FLUKE 0xffff +#define BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER 0x0 + +struct fmh_priv { + struct nec7210_priv nec7210_priv; + struct resource *gpib_iomem_res; + struct resource *write_transfer_counter_res; + struct resource *dma_port_res; + int irq; + struct dma_chan *dma_channel; + u8 *dma_buffer; + int dma_buffer_size; + int dma_burst_length; + void *fifo_base; + unsigned supports_fifo_interrupts : 1; +}; + +static inline int fmh_gpib_half_fifo_size(struct fmh_priv *priv) +{ + return priv->dma_burst_length; +} + +// registers beyond the nec7210 register set +enum fmh_gpib_regs { + EXT_STATUS_1_REG = 0x9, + STATE1_REG = 0xc, + ISR0_IMR0_REG = 0xe, + BUS_STATUS_REG = 0xf +}; + +/* IMR0 -- Interrupt Mode Register 0 */ +enum imr0_bits { + ATN_INTERRUPT_ENABLE_BIT = 0x4, + IFC_INTERRUPT_ENABLE_BIT = 0x8 +}; + +/* ISR0 -- Interrupt Status Register 0 */ +enum isr0_bits { + ATN_INTERRUPT_BIT = 0x4, + IFC_INTERRUPT_BIT = 0x8 +}; + +enum state1_bits { + SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */ + SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */ + SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */ + SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */ + SOURCE_HANDSHAKE_MASK = 0x7 +}; + +enum fmh_gpib_auxmr_bits { + AUX_I_REG = 0xe0, +}; + +enum aux_reg_i_bits { + LOCAL_PPOLL_MODE_BIT = 0x4 +}; + +enum ext_status_1_bits { + DATA_IN_STATUS_BIT = 0x01, + DATA_OUT_STATUS_BIT = 0x02, + COMMAND_OUT_STATUS_BIT = 0x04, + RFD_HOLDOFF_STATUS_BIT = 0x08, + END_STATUS_BIT = 0x10 +}; + +/* dma fifo reg and bits */ +enum dma_fifo_regs { + FIFO_DATA_REG = 0x0, + FIFO_CONTROL_STATUS_REG = 0x1, + FIFO_XFER_COUNTER_REG = 0x2, + FIFO_MAX_BURST_LENGTH_REG = 0x3 +}; + +enum fifo_data_bits { + FIFO_DATA_EOI_FLAG = 0x100 +}; + +enum fifo_control_bits { + TX_FIFO_DMA_REQUEST_ENABLE = 0x0001, + TX_FIFO_CLEAR = 0x0002, + TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE = 0x0008, + RX_FIFO_DMA_REQUEST_ENABLE = 0x0100, + RX_FIFO_CLEAR = 0x0200, + RX_FIFO_HALF_FULL_INTERRUPT_ENABLE = 0x0800 +}; + +enum fifo_status_bits { + TX_FIFO_EMPTY = 0x0001, + TX_FIFO_FULL = 0x0002, + TX_FIFO_HALF_EMPTY = 0x0004, + TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED = 0x0008, + TX_FIFO_DMA_REQUEST_IS_ENABLED = 0x0010, + RX_FIFO_EMPTY = 0x0100, + RX_FIFO_FULL = 0x0200, + RX_FIFO_HALF_FULL = 0x0400, + RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED = 0x0800, + RX_FIFO_DMA_REQUEST_IS_ENABLED = 0x1000 +}; + +static const unsigned int fifo_data_mask = 0x00ff; +static const unsigned int fifo_xfer_counter_mask = 0x0fff; +static const unsigned int fifo_max_burst_length_mask = 0x00ff; + +static inline uint8_t gpib_cs_read_byte(struct nec7210_priv *nec_priv, + unsigned int register_num) +{ + return readb(nec_priv->iobase + register_num * nec_priv->offset); +} + +static inline void gpib_cs_write_byte(struct nec7210_priv *nec_priv, uint8_t data, + unsigned int register_num) +{ + writeb(data, nec_priv->iobase + register_num * nec_priv->offset); +} + +static inline uint16_t fifos_read(struct fmh_priv *fmh_priv, int register_num) +{ + if (!fmh_priv->fifo_base) + return 0; + return readw(fmh_priv->fifo_base + register_num * fifo_reg_offset); +} + +static inline void fifos_write(struct fmh_priv *fmh_priv, uint16_t data, int register_num) +{ + if (!fmh_priv->fifo_base) + return; + writew(data, fmh_priv->fifo_base + register_num * fifo_reg_offset); +} + +enum bus_status_bits { + BSR_ATN_BIT = 0x01, + BSR_EOI_BIT = 0x02, + BSR_SRQ_BIT = 0x04, + BSR_IFC_BIT = 0x08, + BSR_REN_BIT = 0x10, + BSR_DAV_BIT = 0x20, + BSR_NRFD_BIT = 0x40, + BSR_NDAC_BIT = 0x80, +}; + +enum fmh_gpib_aux_cmds { + /* AUX_RTL2 is an auxiliary command which causes the cb7210 to assert + * (and keep asserted) the local rtl message. This is used in conjunction + * with the normal nec7210 AUX_RTL command, which + * pulses the rtl message, having the effect of clearing rtl if it was left + * asserted by AUX_RTL2. + */ + AUX_RTL2 = 0x0d, + AUX_RFD_HOLDOFF_ASAP = 0x15, + AUX_REQT = 0x18, + AUX_REQF = 0x19, + AUX_LO_SPEED = 0x40, + AUX_HI_SPEED = 0x41 +}; diff --git a/drivers/staging/gpib/gpio/Makefile b/drivers/staging/gpib/gpio/Makefile new file mode 100644 index 0000000000000..a31ded6e59245 --- /dev/null +++ b/drivers/staging/gpib/gpio/Makefile @@ -0,0 +1,4 @@ + +obj-m += gpib_bitbang.o + + diff --git a/drivers/staging/gpib/gpio/gpib_bitbang.c b/drivers/staging/gpib/gpio/gpib_bitbang.c new file mode 100644 index 0000000000000..a2d562cbd65b4 --- /dev/null +++ b/drivers/staging/gpib/gpio/gpib_bitbang.c @@ -0,0 +1,1476 @@ +// SPDX-License-Identifier: GPL-2.0 + +/************************************************************************* + * This code has been developed at the Institute of Sensor and Actuator * + * Systems (Technical University of Vienna, Austria) to enable the GPIO * + * lines (e.g. of a raspberry pi) to function as a GPIO master device * + * * + * authors : Thomas Klima * + * Marcello Carla' * + * Dave Penkler * + * * + * copyright : (C) 2016 Thomas Klima * + * * + *************************************************************************/ + +/* + * limitations: + * works only on RPi + * cannot function as non-CIC system controller with SN7516x because + * SN75161B cannot simultaneously make ATN input with IFC and REN as + * outputs. + * not implemented: + * parallel poll + * return2local + * device support (non master operation) + */ + +#define NAME KBUILD_MODNAME + +#define ENABLE_IRQ(IRQ, TYPE) irq_set_irq_type(IRQ, TYPE) +#define DISABLE_IRQ(IRQ) irq_set_irq_type(IRQ, IRQ_TYPE_NONE) + +/* Debug print levels: + * 0 = load/unload info and errors that make the driver fail; + * 1 = + warnings for unforeseen events that may break the current + * operation and lead to a timeout, but do not affect the + * driver integrity (mainly unexpected interrupts); + * 2 = + trace of function calls; + * 3 = + trace of protocol codes; + * 4 = + trace of interrupt operation. + */ +#define dbg_printk(level, frm, ...) \ + do { if (debug >= (level)) \ + pr_info("%s:%s - " frm, NAME, __func__, ## __VA_ARGS__); } \ + while (0) + +#define LINVAL gpiod_get_value(DAV), \ + gpiod_get_value(NRFD), \ + gpiod_get_value(NDAC), \ + gpiod_get_value(SRQ) +#define LINFMT "DAV: %d NRFD:%d NDAC: %d SRQ: %d" + +#include "gpibP.h" +#include "gpib_state_machines.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sn7516x_used = 1, sn7516x; +module_param(sn7516x_used, int, 0660); + +#define PINMAP_0 "elektronomikon" +#define PINMAP_1 "gpib4pi-1.1" +#define PINMAP_2 "yoga" +static char *pin_map = PINMAP_0; +module_param(pin_map, charp, 0660); +MODULE_PARM_DESC(pin_map, " valid values: " PINMAP_0 " " PINMAP_1 " " PINMAP_2); + +/********************************************** + * Signal pairing and pin wiring between the * + * Raspberry-Pi connector and the GPIB bus * + * * + * signal pin wiring * + * GPIB Pi-gpio GPIB -> RPi * + ********************************************** + */ +enum lines_t { + D01_pin_nr = 20, /* 1 -> 38 */ + D02_pin_nr = 26, /* 2 -> 37 */ + D03_pin_nr = 16, /* 3 -> 36 */ + D04_pin_nr = 19, /* 4 -> 35 */ + D05_pin_nr = 13, /* 13 -> 33 */ + D06_pin_nr = 12, /* 14 -> 32 */ + D07_pin_nr = 6, /* 15 -> 31 */ + D08_pin_nr = 5, /* 16 -> 29 */ + EOI_pin_nr = 9, /* 5 -> 21 */ + DAV_pin_nr = 10, /* 6 -> 19 */ + NRFD_pin_nr = 24, /* 7 -> 18 */ + NDAC_pin_nr = 23, /* 8 -> 16 */ + IFC_pin_nr = 22, /* 9 -> 15 */ + SRQ_pin_nr = 11, /* 10 -> 23 */ + _ATN_pin_nr = 25, /* 11 -> 22 */ + REN_pin_nr = 27, /* 17 -> 13 */ +/* + * GROUND PINS + * 12,18,19,20,21,22,23,24 => 14,20,25,30,34,39 + */ + +/* + * These lines are used to control the external + * SN75160/161 driver chips when used. + * When not used there is reduced fan out; + * currently tested with up to 4 devices. + */ + +/* Pi GPIO RPI 75161B 75160B Description */ + PE_pin_nr = 7, /* 26 -> nc 11 Pullup Enable */ + DC_pin_nr = 8, /* 24 -> 12 nc Direction control */ + TE_pin_nr = 18, /* 12 -> 2 1 Talk Enable */ + ACT_LED_pin_nr = 4, /* 7 -> LED */ + +/* YOGA adapter uses different pinout to ease layout */ + YOGA_D03_pin_nr = 13, + YOGA_D04_pin_nr = 12, + YOGA_D05_pin_nr = 21, + YOGA_D06_pin_nr = 19, +}; + +/* + * GPIO descriptors and pins - WARNING: STRICTLY KEEP ITEMS ORDER + */ + +#define GPIB_PINS 16 +#define SN7516X_PINS 4 +#define NUM_PINS (GPIB_PINS + SN7516X_PINS) + +DEFINE_LED_TRIGGER(ledtrig_gpib); +#define ACT_LED_ON do { \ + if (ACT_LED) \ + gpiod_direction_output(ACT_LED, 1); \ + else \ + led_trigger_event(ledtrig_gpib, LED_FULL); } \ + while (0) +#define ACT_LED_OFF do { \ + if (ACT_LED) \ + gpiod_direction_output(ACT_LED, 0); \ + else \ + led_trigger_event(ledtrig_gpib, LED_OFF); } \ + while (0) + +struct gpio_desc *all_descriptors[GPIB_PINS + SN7516X_PINS]; + +#define D01 all_descriptors[0] +#define D02 all_descriptors[1] +#define D03 all_descriptors[2] +#define D04 all_descriptors[3] +#define D05 all_descriptors[4] +#define D06 all_descriptors[5] +#define D07 all_descriptors[6] +#define D08 all_descriptors[7] + +#define EOI all_descriptors[8] +#define NRFD all_descriptors[9] +#define IFC all_descriptors[10] +#define _ATN all_descriptors[11] +#define REN all_descriptors[12] +#define DAV all_descriptors[13] +#define NDAC all_descriptors[14] +#define SRQ all_descriptors[15] + +#define PE all_descriptors[16] +#define DC all_descriptors[17] +#define TE all_descriptors[18] +#define ACT_LED all_descriptors[19] + +/* YOGA dapter uses a global enable for the buffer chips, re-using the TE pin */ +#define YOGA_ENABLE TE + +int gpios_vector[] = { + D01_pin_nr, + D02_pin_nr, + D03_pin_nr, + D04_pin_nr, + D05_pin_nr, + D06_pin_nr, + D07_pin_nr, + D08_pin_nr, + + EOI_pin_nr, + NRFD_pin_nr, + IFC_pin_nr, + _ATN_pin_nr, + REN_pin_nr, + DAV_pin_nr, + NDAC_pin_nr, + SRQ_pin_nr, + + PE_pin_nr, + DC_pin_nr, + TE_pin_nr, + ACT_LED_pin_nr +}; + +/* Lookup table for general GPIOs */ + +static struct gpiod_lookup_table gpib_gpio_table_1 = { + // for bcm2835/6 + .dev_id = "", // device id of board device + .table = { + GPIO_LOOKUP_IDX("GPIO_GCLK", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_CE1_N", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_CE0_N", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_MISO", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_MOSI", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_SCLK", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table gpib_gpio_table_0 = { + .dev_id = "", // device id of board device + .table = { + // for bcm27xx based pis (b b+ 2b 3b 3b+ 4 5) + GPIO_LOOKUP_IDX("GPIO4", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO7", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO8", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO9", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO10", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO11", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table *lookup_tables[] = { + &gpib_gpio_table_0, + &gpib_gpio_table_1, + 0 +}; + +/* struct which defines private_data for gpio driver */ + +struct bb_priv { + int irq_NRFD; + int irq_NDAC; + int irq_DAV; + int irq_SRQ; + int dav_mode; /* dav interrupt mode 0/1 -> edge/levels */ + int nrfd_mode; /* nrfd interrupt mode 0/1 -> edge/levels */ + int ndac_mode; /* nrfd interrupt mode 0/1 -> edge/levels */ + int dav_tx; /* keep trace of DAV status while sending */ + int dav_rx; /* keep trace of DAV status while receiving */ + u8 eos; // eos character + short eos_flags; // eos mode + short eos_check; /* eos check required in current operation ... */ + short eos_check_8; /* ... with byte comparison */ + short eos_mask_7; /* ... with 7 bit masked character */ + short int end; + int request; + int count; + int direction; + int t1_delay; + u8 *rbuf; + u8 *wbuf; + int end_flag; + int r_busy; /* 0==idle 1==busy */ + int w_busy; + int write_done; + int cmd; /* 1 = cmd write in progress */ + size_t w_cnt; + size_t length; + u8 *w_buf; + spinlock_t rw_lock; // protect mods to rw_lock + int phase; + int ndac_idle; + int ndac_seq; + int nrfd_idle; + int nrfd_seq; + int dav_seq; + long all_irqs; + int dav_idle; + int atn_asserted; + + enum talker_function_state talker_state; + enum listener_function_state listener_state; +}; + +inline long usec_diff(struct timespec64 *a, struct timespec64 *b); +static void bb_buffer_print(unsigned char *buffer, size_t length, int cmd, int eoi); +static void set_data_lines(u8 byte); +static u8 get_data_lines(void); +static void set_data_lines_input(void); +static void set_data_lines_output(void); +static inline int check_for_eos(struct bb_priv *priv, uint8_t byte); +static void set_atn(struct bb_priv *priv, int atn_asserted); + +static inline void SET_DIR_WRITE(struct bb_priv *priv); +static inline void SET_DIR_READ(struct bb_priv *priv); + +#define DIR_READ 0 +#define DIR_WRITE 1 + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB helper functions for bitbanging I/O"); + +/**** global variables ****/ +#ifdef CONFIG_GPIB_DEBUG +static int debug = 1; +#else +static int debug; +#endif +module_param(debug, int, 0644); + +static char printable(char x) +{ + if (x < 32 || x > 126) + return ' '; + return x; +} + +/*************************************************************************** + * * + * READ * + * * + ***************************************************************************/ + +static int bb_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct bb_priv *priv = board->private_data; + unsigned long flags; + int retval = 0; + + ACT_LED_ON; + SET_DIR_READ(priv); + + dbg_printk(2, "board: %p lock %d length: %zu\n", + board, mutex_is_locked(&board->user_mutex), length); + + priv->end = 0; + priv->count = 0; + priv->rbuf = buffer; + if (length == 0) + goto read_end; + priv->request = length; + priv->eos_check = (priv->eos_flags & REOS) == 0; /* do eos check */ + priv->eos_check_8 = priv->eos_flags & BIN; /* over 8 bits */ + priv->eos_mask_7 = priv->eos & 0x7f; /* with this 7 bit eos */ + + dbg_printk(3, ".........." LINFMT "\n", LINVAL); + + spin_lock_irqsave(&priv->rw_lock, flags); + priv->dav_mode = 1; + priv->dav_rx = 1; + ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_LEVEL_LOW); + priv->end_flag = 0; + gpiod_set_value(NRFD, 1); // ready for data + priv->r_busy = 1; + priv->phase = 100; + spin_unlock_irqrestore(&priv->rw_lock, flags); + + /* wait for the interrupt routines finish their work */ + + retval = wait_event_interruptible(board->wait, + (priv->end_flag || board->status & TIMO)); + + dbg_printk(3, "awake from wait queue: %d\n", retval); + + if (retval == 0 && board->status & TIMO) { + retval = -ETIMEDOUT; + dbg_printk(1, "timeout\n"); + } else if (retval) { + retval = -ERESTARTSYS; + } + + DISABLE_IRQ(priv->irq_DAV); + spin_lock_irqsave(&priv->rw_lock, flags); + gpiod_set_value(NRFD, 0); // DIR_READ line state + priv->r_busy = 0; + spin_unlock_irqrestore(&priv->rw_lock, flags); + +read_end: + ACT_LED_OFF; + *bytes_read = priv->count; + *end = priv->end; + priv->r_busy = 0; + dbg_printk(2, "return: %d eoi|eos: %d count: %d\n\n", retval, priv->end, priv->count); + return retval; +} + +/*************************************************************************** + * * + * READ interrupt routine (DAV line) * + * * + ***************************************************************************/ + +static irqreturn_t bb_DAV_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct bb_priv *priv = board->private_data; + int val; + unsigned long flags; + + spin_lock_irqsave(&priv->rw_lock, flags); + + priv->all_irqs++; + + if (priv->dav_mode) { + ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_EDGE_BOTH); + priv->dav_mode = 0; + } + + if (priv->r_busy == 0) { + dbg_printk(1, "interrupt while idle after %d at %d\n", + priv->count, priv->phase); + priv->dav_idle++; + priv->phase = 200; + goto dav_exit; /* idle */ + } + + val = gpiod_get_value(DAV); + if (val == priv->dav_rx) { + dbg_printk(1, "out of order DAV interrupt %d/%d after %zu/%zu at %d cmd %d " + LINFMT ".\n", val, priv->dav_rx, priv->w_cnt, priv->length, + priv->phase, priv->cmd, LINVAL); + priv->dav_seq++; + } + priv->dav_rx = val; + + dbg_printk(3, "> irq: %d DAV: %d st: %4lx dir: %d busy: %d:%d\n", + irq, val, board->status, priv->direction, priv->r_busy, priv->w_busy); + + if (val == 0) { + gpiod_set_value(NRFD, 0); // not ready for data + priv->rbuf[priv->count++] = get_data_lines(); + priv->end = !gpiod_get_value(EOI); + gpiod_set_value(NDAC, 1); // data accepted + priv->end |= check_for_eos(priv, priv->rbuf[priv->count - 1]); + priv->end_flag = ((priv->count >= priv->request) || priv->end); + priv->phase = 210; + } else { + gpiod_set_value(NDAC, 0); // data not accepted + if (priv->end_flag) { + priv->r_busy = 0; + wake_up_interruptible(&board->wait); + priv->phase = 220; + } else { + gpiod_set_value(NRFD, 1); // ready for data + priv->phase = 230; + } + } + +dav_exit: + spin_unlock_irqrestore(&priv->rw_lock, flags); + dbg_printk(3, "< irq: %d count %d\n", irq, priv->count); + return IRQ_HANDLED; +} + +/*************************************************************************** + * * + * WRITE * + * * + ***************************************************************************/ + +static int bb_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + unsigned long flags; + int retval = 0; + + struct bb_priv *priv = board->private_data; + + ACT_LED_ON; + + priv->w_cnt = 0; + priv->w_buf = buffer; + dbg_printk(2, "board %p lock %d length: %zu\n", + board, mutex_is_locked(&board->user_mutex), length); + + if (debug > 1) + bb_buffer_print(buffer, length, priv->cmd, send_eoi); + priv->count = 0; + priv->phase = 300; + + if (length == 0) + goto write_end; + priv->end = send_eoi; + priv->length = length; + + SET_DIR_WRITE(priv); + + dbg_printk(2, "Enabling interrupts - NRFD: %d NDAC: %d\n", + gpiod_get_value(NRFD), gpiod_get_value(NDAC)); + + if (gpiod_get_value(NRFD) && gpiod_get_value(NDAC)) { /* check for listener */ + retval = -ENODEV; + goto write_end; + } + + spin_lock_irqsave(&priv->rw_lock, flags); + priv->w_busy = 1; /* make the interrupt routines active */ + priv->write_done = 0; + priv->nrfd_mode = 1; + priv->ndac_mode = 1; + priv->dav_tx = 1; + ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_LEVEL_HIGH); + ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_LEVEL_HIGH); + spin_unlock_irqrestore(&priv->rw_lock, flags); + + /* wait for the interrupt routines finish their work */ + + retval = wait_event_interruptible(board->wait, + priv->write_done || (board->status & TIMO)); + + dbg_printk(3, "awake from wait queue: %d\n", retval); + + if (retval == 0) { + if (board->status & TIMO) { + retval = -ETIMEDOUT; + dbg_printk(1, "timeout after %zu/%zu at %d " LINFMT " eoi: %d\n", + priv->w_cnt, length, priv->phase, LINVAL, send_eoi); + } else { + // dbg_printk(1,"written %zu\n", priv->w_cnt); + retval = priv->w_cnt; + } + } else { + retval = -ERESTARTSYS; + } + + DISABLE_IRQ(priv->irq_NRFD); + DISABLE_IRQ(priv->irq_NDAC); + + spin_lock_irqsave(&priv->rw_lock, flags); + priv->w_busy = 0; + gpiod_set_value(DAV, 1); // DIR_WRITE line state + gpiod_set_value(EOI, 1); // De-assert EOI (in case) + spin_unlock_irqrestore(&priv->rw_lock, flags); + +write_end: + *bytes_written = priv->w_cnt; + ACT_LED_OFF; + dbg_printk(2, "sent %zu bytes\r\n\r\n", *bytes_written); + priv->phase = 310; + return retval; +} + +/*************************************************************************** + * * + * WRITE interrupt routine (NRFD line) * + * * + ***************************************************************************/ + +static irqreturn_t bb_NRFD_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct bb_priv *priv = board->private_data; + unsigned long flags; + int nrfd; + + spin_lock_irqsave(&priv->rw_lock, flags); + + nrfd = gpiod_get_value(NRFD); + priv->all_irqs++; + + dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n", + irq, nrfd, gpiod_get_value(NDAC), board->status, priv->direction, + priv->w_busy, priv->r_busy); + + if (priv->nrfd_mode) { + ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_EDGE_RISING); + priv->nrfd_mode = 0; + } + + if (priv->w_busy == 0) { + dbg_printk(1, "interrupt while idle after %zu/%zu at %d\n", + priv->w_cnt, priv->length, priv->phase); + priv->nrfd_idle++; + goto nrfd_exit; /* idle */ + } + if (nrfd == 0) { + dbg_printk(1, "out of order interrupt after %zu/%zu at %d cmd %d " LINFMT ".\n", + priv->w_cnt, priv->length, priv->phase, priv->cmd, LINVAL); + priv->phase = 400; + priv->nrfd_seq++; + goto nrfd_exit; + } + if (!priv->dav_tx) { + dbg_printk(1, "DAV low after %zu/%zu cmd %d " LINFMT ". No action.\n", + priv->w_cnt, priv->length, priv->cmd, LINVAL); + priv->dav_seq++; + goto nrfd_exit; + } + + if (priv->atn_asserted && priv->w_cnt >= priv->length) { // test for end of transfer + priv->write_done = 1; + priv->w_busy = 0; + wake_up_interruptible(&board->wait); + goto nrfd_exit; + } + + dbg_printk(3, "sending %zu\n", priv->w_cnt); + + set_data_lines(priv->w_buf[priv->w_cnt++]); // put the data on the lines + + if (priv->w_cnt == priv->length && priv->end) { + dbg_printk(3, "Asserting EOI\n"); + gpiod_set_value(EOI, 0); // Assert EOI + } + + gpiod_set_value(DAV, 0); // Data available + priv->dav_tx = 0; + priv->phase = 410; + +nrfd_exit: + spin_unlock_irqrestore(&priv->rw_lock, flags); + + return IRQ_HANDLED; +} + +/*************************************************************************** + * * + * WRITE interrupt routine (NDAC line) * + * * + ***************************************************************************/ + +static irqreturn_t bb_NDAC_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct bb_priv *priv = board->private_data; + unsigned long flags; + int ndac; + + spin_lock_irqsave(&priv->rw_lock, flags); + + ndac = gpiod_get_value(NDAC); + priv->all_irqs++; + dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n", + irq, gpiod_get_value(NRFD), ndac, board->status, priv->direction, + priv->w_busy, priv->r_busy); + + if (priv->ndac_mode) { + ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_EDGE_RISING); + priv->ndac_mode = 0; + } + + if (priv->w_busy == 0) { + dbg_printk(1, "interrupt while idle.\n"); + priv->ndac_idle++; + goto ndac_exit; + } + if (ndac == 0) { + dbg_printk(1, "out of order interrupt at %zu:%d.\n", priv->w_cnt, priv->phase); + priv->phase = 500; + priv->ndac_seq++; + goto ndac_exit; + } + if (priv->dav_tx) { + dbg_printk(1, "DAV high after %zu/%zu cmd %d " LINFMT ". No action.\n", + priv->w_cnt, priv->length, priv->cmd, LINVAL); + priv->dav_seq++; + goto ndac_exit; + } + + dbg_printk(3, "accepted %zu\n", priv->w_cnt - 1); + + if (!priv->atn_asserted && priv->w_cnt >= priv->length) { // test for end of transfer + priv->write_done = 1; + priv->w_busy = 0; + wake_up_interruptible(&board->wait); + } else { + gpiod_set_value(DAV, 1); // Data not available + priv->dav_tx = 1; + priv->phase = 510; + } + +ndac_exit: + spin_unlock_irqrestore(&priv->rw_lock, flags); + return IRQ_HANDLED; +} + +/*************************************************************************** + * * + * interrupt routine for SRQ line * + * * + ***************************************************************************/ + +static irqreturn_t bb_SRQ_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + + int val = gpiod_get_value(SRQ); + + dbg_printk(3, "> %d st: %4lx\n", val, board->status); + + if (!val) + set_bit(SRQI_NUM, &board->status); /* set_bit() is atomic */ + + wake_up_interruptible(&board->wait); + + return IRQ_HANDLED; +} + +static int bb_command(gpib_board_t *board, uint8_t *buffer, + size_t length, size_t *bytes_written) +{ + size_t ret; + struct bb_priv *priv = board->private_data; + int i; + + dbg_printk(2, "%p %p\n", buffer, board->buffer); + + /* the _ATN line has already been asserted by bb_take_control() */ + + priv->cmd = 1; + + ret = bb_write(board, buffer, length, 0, bytes_written); // no eoi + + for (i = 0; i < length; i++) { + if (buffer[i] == UNT) { + priv->talker_state = talker_idle; + } else { + if (buffer[i] == UNL) { + priv->listener_state = listener_idle; + } else { + if (buffer[i] == (MTA(board->pad))) { + priv->talker_state = talker_addressed; + priv->listener_state = listener_idle; + } else if (buffer[i] == (MLA(board->pad))) { + priv->listener_state = listener_addressed; + priv->talker_state = talker_idle; + } + } + } + } + + /* the _ATN line will be released by bb_go_to_stby */ + + priv->cmd = 0; + + return ret; +} + +/*************************************************************************** + * * + * Buffer print with decode for debug/trace * + * * + ***************************************************************************/ + +static char *cmd_string[32] = { + "", // 0x00 + "GTL", // 0x01 + "", // 0x02 + "", // 0x03 + "SDC", // 0x04 + "PPC", // 0x05 + "", // 0x06 + "", // 0x07 + "GET", // 0x08 + "TCT", // 0x09 + "", // 0x0a + "", // 0x0b + "", // 0x0c + "", // 0x0d + "", // 0x0e + "", // 0x0f + "", // 0x10 + "LLO", // 0x11 + "", // 0x12 + "", // 0x13 + "DCL", // 0x14 + "PPU", // 0x15 + "", // 0x16 + "", // 0x17 + "SPE", // 0x18 + "SPD", // 0x19 + "", // 0x1a + "", // 0x1b + "", // 0x1c + "", // 0x1d + "", // 0x1e + "CFE" // 0x1f +}; + +static void bb_buffer_print(unsigned char *buffer, size_t length, int cmd, int eoi) +{ + int i; + + if (cmd) { + dbg_printk(2, "\n", length); + for (i = 0; i < length; i++) { + if (buffer[i] < 0x20) { + dbg_printk(3, "0x%x=%s\n", buffer[i], cmd_string[buffer[i]]); + } else if (buffer[i] == 0x3f) { + dbg_printk(3, "0x%x=%s\n", buffer[i], "UNL"); + } else if (buffer[i] == 0x5f) { + dbg_printk(3, "0x%x=%s\n", buffer[i], "UNT"); + } else if (buffer[i] < 0x60) { + dbg_printk(3, "0x%x=%s%d\n", buffer[i], + (buffer[i] & 0x40) ? "TLK" : "LSN", buffer[i] & 0x1F); + } else { + dbg_printk(3, "0x%x\n", buffer[i]); + } + } + } else { + dbg_printk(2, "\n", length, (eoi) ? "w.EOI" : " "); + for (i = 0; i < length; i++) + dbg_printk(2, "%3d 0x%x->%c\n", i, buffer[i], printable(buffer[i])); + } +} + +/*************************************************************************** + * * + * STATUS Management * + * * + ***************************************************************************/ +static void set_atn(struct bb_priv *priv, int atn_asserted) +{ + if (priv->listener_state != listener_idle && + priv->talker_state != talker_idle) { + dbg_printk(0, "listener/talker state machine conflict\n"); + } + if (atn_asserted) { + if (priv->listener_state == listener_active) + priv->listener_state = listener_addressed; + if (priv->talker_state == talker_active) + priv->talker_state = talker_addressed; + } else { + if (priv->listener_state == listener_addressed) { + priv->listener_state = listener_active; + SET_DIR_READ(priv); // make sure holdoff is active when we unassert ATN + } + if (priv->talker_state == talker_addressed) + priv->talker_state = talker_active; + } + gpiod_direction_output(_ATN, !atn_asserted); + priv->atn_asserted = atn_asserted; +} + +static int bb_take_control(gpib_board_t *board, int synchronous) +{ + dbg_printk(2, "%d\n", synchronous); + set_atn(board->private_data, 1); + set_bit(CIC_NUM, &board->status); + return 0; +} + +static int bb_go_to_standby(gpib_board_t *board) +{ + dbg_printk(2, "\n"); + set_atn(board->private_data, 0); + return 0; +} + +static void bb_request_system_control(gpib_board_t *board, int request_control) +{ + dbg_printk(2, "%d\n", request_control); + if (request_control) { + set_bit(CIC_NUM, &board->status); + // drive DAV & EOI false, enable NRFD & NDAC irqs + SET_DIR_WRITE(board->private_data); + } else { + clear_bit(CIC_NUM, &board->status); + } +} + +static void bb_interface_clear(gpib_board_t *board, int assert) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "%d\n", assert); + if (assert) { + gpiod_direction_output(IFC, 0); + priv->talker_state = talker_idle; + priv->listener_state = listener_idle; + } else { + gpiod_direction_output(IFC, 1); + } +} + +static void bb_remote_enable(gpib_board_t *board, int enable) +{ + dbg_printk(2, "%d\n", enable); + if (enable) { + set_bit(REM_NUM, &board->status); + gpiod_direction_output(REN, 0); + } else { + clear_bit(REM_NUM, &board->status); + gpiod_direction_output(REN, 1); + } +} + +static int bb_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "%s\n", "EOS_en"); + priv->eos = eos_byte; + priv->eos_flags = REOS; + if (compare_8_bits) + priv->eos_flags |= BIN; + + return 0; +} + +static void bb_disable_eos(gpib_board_t *board) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "\n"); + priv->eos_flags &= ~REOS; +} + +static unsigned int bb_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct bb_priv *priv = board->private_data; + + board->status &= ~clear_mask; + + if (gpiod_get_value(SRQ)) /* SRQ asserted low */ + clear_bit(SRQI_NUM, &board->status); + else + set_bit(SRQI_NUM, &board->status); + if (gpiod_get_value(_ATN)) /* ATN asserted low */ + clear_bit(ATN_NUM, &board->status); + else + set_bit(ATN_NUM, &board->status); + if (priv->talker_state == talker_active || + priv->talker_state == talker_addressed) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + + if (priv->listener_state == listener_active || + priv->listener_state == listener_addressed) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + + dbg_printk(2, "0x%lx mask 0x%x\n", board->status, clear_mask); + + return board->status; +} + +static int bb_primary_address(gpib_board_t *board, unsigned int address) +{ + dbg_printk(2, "%d\n", address); + board->pad = address; + return 0; +} + +static int bb_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + dbg_printk(2, "%d %d\n", address, enable); + if (enable) + board->sad = address; + return 0; +} + +static int bb_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + dbg_printk(1, "%s\n", "not implemented"); + return -EPERM; +} + +static void bb_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + dbg_printk(1, "%s\n", "not implemented"); +} + +static void bb_parallel_poll_response(gpib_board_t *board, int ist) +{ +} + +static void bb_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + dbg_printk(1, "%s\n", "not implemented"); +} + +static uint8_t bb_serial_poll_status(gpib_board_t *board) +{ + dbg_printk(1, "%s\n", "not implemented"); + return 0; // -ENOSYS; +} + +static unsigned int bb_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct bb_priv *priv = board->private_data; + + if (nano_sec <= 350) + priv->t1_delay = 350; + else if (nano_sec <= 1100) + priv->t1_delay = 1100; + else + priv->t1_delay = 2000; + + dbg_printk(2, "t1 delay set to %d nanosec\n", priv->t1_delay); + + return priv->t1_delay; +} + +static void bb_return_to_local(gpib_board_t *board) +{ + dbg_printk(1, "%s\n", "not implemented"); +} + +static int bb_line_status(const gpib_board_t *board) +{ + int line_status = ValidALL; + +// dbg_printk(1,"\n"); + + if (gpiod_get_value(REN) == 0) + line_status |= BusREN; + if (gpiod_get_value(IFC) == 0) + line_status |= BusIFC; + if (gpiod_get_value(NDAC) == 0) + line_status |= BusNDAC; + if (gpiod_get_value(NRFD) == 0) + line_status |= BusNRFD; + if (gpiod_get_value(DAV) == 0) + line_status |= BusDAV; + if (gpiod_get_value(EOI) == 0) + line_status |= BusEOI; + if (gpiod_get_value(_ATN) == 0) + line_status |= BusATN; + if (gpiod_get_value(SRQ) == 0) + line_status |= BusSRQ; + + dbg_printk(2, "status lines: %4x\n", line_status); + + return line_status; +} + +/*************************************************************************** + * * + * Module Management * + * * + ***************************************************************************/ + +static int allocate_private(gpib_board_t *board) +{ + board->private_data = kzalloc(sizeof(struct bb_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + return 0; +} + +static void free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int bb_get_irq(gpib_board_t *board, char *name, + struct gpio_desc *gpio, int *irq, + irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags) +{ + if (!gpio) + return -1; + gpiod_direction_input(gpio); + *irq = gpiod_to_irq(gpio); + dbg_printk(2, "IRQ %s: %d\n", name, *irq); + if (*irq < 0) { + dbg_printk(0, "gpib: can't get IRQ for %s\n", name); + return -1; + } + if (request_threaded_irq(*irq, handler, thread_fn, flags, name, board)) { + dbg_printk(0, "gpib: can't request IRQ for %s %d\n", name, *irq); + *irq = 0; + return -1; + } + DISABLE_IRQ(*irq); + return 0; +} + +static void bb_free_irq(gpib_board_t *board, int *irq, char *name) +{ + if (*irq) { + free_irq(*irq, board); + dbg_printk(2, "IRQ %d(%s) freed\n", *irq, name); + *irq = 0; + } +} + +static void release_gpios(void) +{ + int j; + + for (j = 0 ; j < NUM_PINS ; j++) { + if (all_descriptors[j]) { + gpiod_put(all_descriptors[j]); + all_descriptors[j] = 0; + } + } +} + +static int allocate_gpios(gpib_board_t *board) +{ + int j, retval = 0; + bool error = false; + int table_index = 0; + char name[256]; + struct gpio_desc *desc; + struct gpiod_lookup_table *lookup_table; + + if (!board->gpib_dev) { + pr_err("NULL gpib dev for board\n"); + return -ENOENT; + } + + lookup_table = lookup_tables[0]; + lookup_table->dev_id = dev_name(board->gpib_dev); + gpiod_add_lookup_table(lookup_table); + dbg_printk(1, "Allocating gpios using table index %d\n", table_index); + + for (j = 0 ; j < NUM_PINS ; j++) { + if (gpios_vector[j] < 0) + continue; + /* name not really used in gpiod_get_index() */ + sprintf(name, "GPIO%d", gpios_vector[j]); +try_again: + dbg_printk(1, "Allocating gpio %s pin no %d\n", name, gpios_vector[j]); + desc = gpiod_get_index(board->gpib_dev, name, gpios_vector[j], GPIOD_IN); + + if (IS_ERR(desc)) { + gpiod_remove_lookup_table(lookup_table); + table_index++; + lookup_table = lookup_tables[table_index]; + if (lookup_table) { + dbg_printk(1, "Allocation failed, now using table_index %d\n", + table_index); + lookup_table->dev_id = dev_name(board->gpib_dev); + gpiod_add_lookup_table(lookup_table); + goto try_again; + } + dbg_printk(0, "Unable to obtain gpio descriptor for pin %d error %ld\n", + gpios_vector[j], PTR_ERR(desc)); + error = true; + break; + } + all_descriptors[j] = desc; + } + + if (error) { /* undo what already done */ + release_gpios(); + retval = -1; + } + if (lookup_table) + gpiod_remove_lookup_table(lookup_table); + // Initialize LED trigger + led_trigger_register_simple("gpib", &ledtrig_gpib); + return retval; +} + +static void bb_detach(gpib_board_t *board) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "Enter with data %p\n", board->private_data); + if (!board->private_data) + return; + + led_trigger_unregister_simple(ledtrig_gpib); + + bb_free_irq(board, &priv->irq_DAV, NAME "_DAV"); + bb_free_irq(board, &priv->irq_NRFD, NAME "_NRFD"); + bb_free_irq(board, &priv->irq_NDAC, NAME "_NDAC"); + bb_free_irq(board, &priv->irq_SRQ, NAME "_SRQ"); + + if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */ + gpiod_set_value(YOGA_ENABLE, 0); + } + + release_gpios(); + + dbg_printk(2, "detached board: %d\n", board->minor); + dbg_printk(0, "NRFD: idle %d, seq %d, NDAC: idle %d, seq %d DAV: idle %d seq: %d all: %ld", + priv->nrfd_idle, priv->nrfd_seq, + priv->ndac_idle, priv->ndac_seq, + priv->dav_idle, priv->dav_seq, priv->all_irqs); + + free_private(board); +} + +static int bb_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct bb_priv *priv; + int retval = 0; + + dbg_printk(2, "%s\n", "Enter ..."); + + board->status = 0; + + if (allocate_private(board)) + return -ENOMEM; + priv = board->private_data; + priv->direction = -1; + priv->t1_delay = 2000; + priv->listener_state = listener_idle; + priv->talker_state = talker_idle; + + sn7516x = sn7516x_used; + if (strcmp(PINMAP_0, pin_map) == 0) { + if (!sn7516x) { + gpios_vector[&(PE) - &all_descriptors[0]] = -1; + gpios_vector[&(DC) - &all_descriptors[0]] = -1; + gpios_vector[&(TE) - &all_descriptors[0]] = -1; + } + } else if (strcmp(PINMAP_1, pin_map) == 0) { + if (!sn7516x) { + gpios_vector[&(PE) - &all_descriptors[0]] = -1; + gpios_vector[&(DC) - &all_descriptors[0]] = -1; + gpios_vector[&(TE) - &all_descriptors[0]] = -1; + } + gpios_vector[&(REN) - &all_descriptors[0]] = 0; /* 27 -> 0 REN on GPIB pin 0 */ + } else if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */ + sn7516x = 0; + gpios_vector[&(D03) - &all_descriptors[0]] = YOGA_D03_pin_nr; + gpios_vector[&(D04) - &all_descriptors[0]] = YOGA_D04_pin_nr; + gpios_vector[&(D05) - &all_descriptors[0]] = YOGA_D05_pin_nr; + gpios_vector[&(D06) - &all_descriptors[0]] = YOGA_D06_pin_nr; + gpios_vector[&(PE) - &all_descriptors[0]] = -1; + gpios_vector[&(DC) - &all_descriptors[0]] = -1; + gpios_vector[&(ACT_LED) - &all_descriptors[0]] = -1; + } else { + dbg_printk(0, "Unrecognized pin mapping.\n"); + goto bb_attach_fail; + } + dbg_printk(0, "Using pin map \"%s\" %s\n", pin_map, (sn7516x) ? + " with SN7516x driver support" : ""); + + if (allocate_gpios(board)) + goto bb_attach_fail; + +/* Configure SN7516X control lines. + * drive ATN, IFC and REN as outputs only when master + * i.e. system controller. In this mode can only be the CIC + * When not master then enable device mode ATN, IFC & REN as inputs + */ + if (sn7516x) { + gpiod_direction_output(DC, 0); + gpiod_direction_output(TE, 1); + gpiod_direction_output(PE, 1); + } + + if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA: enable level shifters */ + gpiod_direction_output(YOGA_ENABLE, 1); + } + + spin_lock_init(&priv->rw_lock); + + /* request DAV interrupt for read */ + if (bb_get_irq(board, NAME "_DAV", DAV, &priv->irq_DAV, bb_DAV_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + /* request NRFD interrupt for write */ + if (bb_get_irq(board, NAME "_NRFD", NRFD, &priv->irq_NRFD, bb_NRFD_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + /* request NDAC interrupt for write */ + if (bb_get_irq(board, NAME "_NDAC", NDAC, &priv->irq_NDAC, bb_NDAC_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + /* request SRQ interrupt for Service Request */ + if (bb_get_irq(board, NAME "_SRQ", SRQ, &priv->irq_SRQ, bb_SRQ_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + ENABLE_IRQ(priv->irq_SRQ, IRQ_TYPE_EDGE_FALLING); + + dbg_printk(0, "attached board %d\n", board->minor); + goto bb_attach_out; + +bb_attach_fail_r: + release_gpios(); +bb_attach_fail: + retval = -1; +bb_attach_out: + return retval; +} + +gpib_interface_t bb_interface = { +name: NAME, +attach : bb_attach, +detach : bb_detach, +read : bb_read, +write : bb_write, +command : bb_command, +take_control : bb_take_control, +go_to_standby : bb_go_to_standby, +request_system_control : bb_request_system_control, +interface_clear : bb_interface_clear, +remote_enable : bb_remote_enable, +enable_eos : bb_enable_eos, +disable_eos : bb_disable_eos, +parallel_poll : bb_parallel_poll, +parallel_poll_configure : bb_parallel_poll_configure, +parallel_poll_response : bb_parallel_poll_response, +line_status : bb_line_status, +update_status : bb_update_status, +primary_address : bb_primary_address, +secondary_address : bb_secondary_address, +serial_poll_response : bb_serial_poll_response, +serial_poll_status : bb_serial_poll_status, +t1_delay : bb_t1_delay, +return_to_local : bb_return_to_local, +}; + +static int __init bb_init_module(void) +{ + gpib_register_driver(&bb_interface, THIS_MODULE); + + dbg_printk(0, "module loaded with pin map \"%s\"%s\n", + pin_map, (sn7516x_used) ? " and SN7516x driver support" : ""); + return 0; +} + +static void __exit bb_exit_module(void) +{ + dbg_printk(0, "module unloaded!"); + + gpib_unregister_driver(&bb_interface); +} + +module_init(bb_init_module); +module_exit(bb_exit_module); + +/*************************************************************************** + * * + * UTILITY Functions * + * * + ***************************************************************************/ +inline long usec_diff(struct timespec64 *a, struct timespec64 *b) +{ + return ((a->tv_sec - b->tv_sec) * 1000000 + + (a->tv_nsec - b->tv_nsec) / 1000); +} + +static inline int check_for_eos(struct bb_priv *priv, uint8_t byte) +{ + if (priv->eos_check) + return 0; + + if (priv->eos_check_8) { + if (priv->eos == byte) + return 1; + } else { + if (priv->eos_mask_7 == (byte & 0x7f)) + return 1; + } + return 0; +} + +static void set_data_lines_output(void) +{ + gpiod_direction_output(D01, 1); + gpiod_direction_output(D02, 1); + gpiod_direction_output(D03, 1); + gpiod_direction_output(D04, 1); + gpiod_direction_output(D05, 1); + gpiod_direction_output(D06, 1); + gpiod_direction_output(D07, 1); + gpiod_direction_output(D08, 1); +} + +static void set_data_lines(u8 byte) +{ + gpiod_set_value(D01, !(byte & 0x01)); + gpiod_set_value(D02, !(byte & 0x02)); + gpiod_set_value(D03, !(byte & 0x04)); + gpiod_set_value(D04, !(byte & 0x08)); + gpiod_set_value(D05, !(byte & 0x10)); + gpiod_set_value(D06, !(byte & 0x20)); + gpiod_set_value(D07, !(byte & 0x40)); + gpiod_set_value(D08, !(byte & 0x80)); +} + +static u8 get_data_lines(void) +{ + u8 ret; + + ret = gpiod_get_value(D01); + ret |= gpiod_get_value(D02) << 1; + ret |= gpiod_get_value(D03) << 2; + ret |= gpiod_get_value(D04) << 3; + ret |= gpiod_get_value(D05) << 4; + ret |= gpiod_get_value(D06) << 5; + ret |= gpiod_get_value(D07) << 6; + ret |= gpiod_get_value(D08) << 7; + return ~ret; +} + +static void set_data_lines_input(void) +{ + gpiod_direction_input(D01); + gpiod_direction_input(D02); + gpiod_direction_input(D03); + gpiod_direction_input(D04); + gpiod_direction_input(D05); + gpiod_direction_input(D06); + gpiod_direction_input(D07); + gpiod_direction_input(D08); +} + +static inline void SET_DIR_WRITE(struct bb_priv *priv) +{ + if (priv->direction == DIR_WRITE) + return; + + gpiod_direction_input(NRFD); + gpiod_direction_input(NDAC); + set_data_lines_output(); + gpiod_direction_output(DAV, 1); + gpiod_direction_output(EOI, 1); + + if (sn7516x) { + gpiod_set_value(PE, 1); /* set data lines to transmit on sn75160b */ + gpiod_set_value(TE, 1); /* set NDAC and NRFD to receive and DAV to transmit */ + } + + priv->direction = DIR_WRITE; +} + +static inline void SET_DIR_READ(struct bb_priv *priv) +{ + if (priv->direction == DIR_READ) + return; + + gpiod_direction_input(DAV); + gpiod_direction_input(EOI); + + set_data_lines_input(); + + if (sn7516x) { + gpiod_set_value(PE, 0); /* set data lines to receive on sn75160b */ + gpiod_set_value(TE, 0); /* set NDAC and NRFD to transmit and DAV to receive */ + } + + gpiod_direction_output(NRFD, 0); // hold off the talker + gpiod_direction_output(NDAC, 0); // data not accepted + + priv->direction = DIR_READ; +} diff --git a/drivers/staging/gpib/hp_82335/Makefile b/drivers/staging/gpib/hp_82335/Makefile new file mode 100644 index 0000000000000..8b7a552e93556 --- /dev/null +++ b/drivers/staging/gpib/hp_82335/Makefile @@ -0,0 +1,4 @@ + +obj-m += hp82335.o + + diff --git a/drivers/staging/gpib/hp_82335/hp82335.c b/drivers/staging/gpib/hp_82335/hp82335.c new file mode 100644 index 0000000000000..40afe42aea478 --- /dev/null +++ b/drivers/staging/gpib/hp_82335/hp82335.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess * + ***************************************************************************/ + +/*should enable ATN interrupts (and update board->status on occurrence), + * implement recovery from bus errors (if necessary) + */ + +#include "hp82335.h" +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for HP 82335 interface cards"); + +static int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void hp82335_detach(gpib_board_t *board); + +// wrappers for interface functions +int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); +} + +int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); +} + +int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); +} + +int hp82335_take_control(gpib_board_t *board, int synchronous) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_take_control(board, &priv->tms9914_priv, synchronous); +} + +int hp82335_go_to_standby(gpib_board_t *board) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_go_to_standby(board, &priv->tms9914_priv); +} + +void hp82335_request_system_control(gpib_board_t *board, int request_control) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_request_system_control(board, &priv->tms9914_priv, request_control); +} + +void hp82335_interface_clear(gpib_board_t *board, int assert) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_interface_clear(board, &priv->tms9914_priv, assert); +} + +void hp82335_remote_enable(gpib_board_t *board, int enable) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_remote_enable(board, &priv->tms9914_priv, enable); +} + +int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); +} + +void hp82335_disable_eos(gpib_board_t *board) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_disable_eos(board, &priv->tms9914_priv); +} + +unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); +} + +int hp82335_primary_address(gpib_board_t *board, unsigned int address) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_primary_address(board, &priv->tms9914_priv, address); +} + +int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); +} + +int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_parallel_poll(board, &priv->tms9914_priv, result); +} + +void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); +} + +void hp82335_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); +} + +void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_serial_poll_response(board, &priv->tms9914_priv, status); +} + +static uint8_t hp82335_serial_poll_status(gpib_board_t *board) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_serial_poll_status(board, &priv->tms9914_priv); +} + +static int hp82335_line_status(const gpib_board_t *board) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_line_status(board, &priv->tms9914_priv); +} + +static unsigned int hp82335_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); +} + +void hp82335_return_to_local(gpib_board_t *board) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_return_to_local(board, &priv->tms9914_priv); +} + +gpib_interface_t hp82335_interface = { +name: "hp82335", +attach : hp82335_attach, +detach : hp82335_detach, +read : hp82335_read, +write : hp82335_write, +command : hp82335_command, +request_system_control : hp82335_request_system_control, +take_control : hp82335_take_control, +go_to_standby : hp82335_go_to_standby, +interface_clear : hp82335_interface_clear, +remote_enable : hp82335_remote_enable, +enable_eos : hp82335_enable_eos, +disable_eos : hp82335_disable_eos, +parallel_poll : hp82335_parallel_poll, +parallel_poll_configure : hp82335_parallel_poll_configure, +parallel_poll_response : hp82335_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : hp82335_line_status, +update_status : hp82335_update_status, +primary_address : hp82335_primary_address, +secondary_address : hp82335_secondary_address, +serial_poll_response : hp82335_serial_poll_response, +serial_poll_status : hp82335_serial_poll_status, +t1_delay : hp82335_t1_delay, +return_to_local : hp82335_return_to_local, +}; + +int hp82335_allocate_private(gpib_board_t *board) +{ + board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + return 0; +} + +void hp82335_free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static inline unsigned int tms9914_to_hp82335_offset(unsigned int register_num) +{ + return 0x1ff8 + register_num; +} + +static uint8_t hp82335_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return tms9914_iomem_read_byte(priv, tms9914_to_hp82335_offset(register_num)); +} + +static void hp82335_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num) +{ + tms9914_iomem_write_byte(priv, data, tms9914_to_hp82335_offset(register_num)); +} + +static void hp82335_clear_interrupt(struct hp82335_priv *hp_priv) +{ + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + + writeb(0, tms_priv->iobase + HPREG_INTR_CLEAR); +} + +int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct hp82335_priv *hp_priv; + struct tms9914_priv *tms_priv; + int retval; + const unsigned long upper_iomem_base = (unsigned long)config->ibbase + hp82335_rom_size; + + board->status = 0; + + if (hp82335_allocate_private(board)) + return -ENOMEM; + hp_priv = board->private_data; + tms_priv = &hp_priv->tms9914_priv; + tms_priv->read_byte = hp82335_read_byte; + tms_priv->write_byte = hp82335_write_byte; + tms_priv->offset = 1; + + switch ((unsigned long)(config->ibbase)) { + case 0xc4000: + case 0xc8000: + case 0xcc000: + case 0xd0000: + case 0xd4000: + case 0xd8000: + case 0xdc000: + case 0xe0000: + case 0xe4000: + case 0xe8000: + case 0xec000: + case 0xf0000: + case 0xf4000: + case 0xf8000: + case 0xfc000: + break; + default: + pr_err("hp82335: invalid base io address 0x%p\n", config->ibbase); + return -EINVAL; + } + if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) { + pr_err("hp82335: failed to allocate io memory region 0x%lx-0x%lx\n", + upper_iomem_base, upper_iomem_base + hp82335_upper_iomem_size - 1); + return -EBUSY; + } + hp_priv->raw_iobase = upper_iomem_base; + tms_priv->iobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size); + pr_info("hp82335: upper half of 82335 iomem region 0x%lx remapped to 0x%p\n", + hp_priv->raw_iobase, tms_priv->iobase); + + retval = request_irq(config->ibirq, hp82335_interrupt, 0, "hp82335", board); + if (retval) { + pr_err("hp82335: can't request IRQ %d\n", config->ibirq); + return retval; + } + hp_priv->irq = config->ibirq; + pr_info("hp82335: IRQ %d\n", config->ibirq); + + tms9914_board_reset(tms_priv); + + hp82335_clear_interrupt(hp_priv); + + writeb(INTR_ENABLE, tms_priv->iobase + HPREG_CCR); + + tms9914_online(board, tms_priv); + + return 0; +} + +void hp82335_detach(gpib_board_t *board) +{ + struct hp82335_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv; + + if (hp_priv) { + tms_priv = &hp_priv->tms9914_priv; + if (hp_priv->irq) + free_irq(hp_priv->irq, board); + if (tms_priv->iobase) { + writeb(0, tms_priv->iobase + HPREG_CCR); + tms9914_board_reset(tms_priv); + iounmap((void *)tms_priv->iobase); + } + if (hp_priv->raw_iobase) + release_mem_region(hp_priv->raw_iobase, hp82335_upper_iomem_size); + } + hp82335_free_private(board); +} + +static int __init hp82335_init_module(void) +{ + gpib_register_driver(&hp82335_interface, THIS_MODULE); + return 0; +} + +static void __exit hp82335_exit_module(void) +{ + gpib_unregister_driver(&hp82335_interface); +} + +module_init(hp82335_init_module); +module_exit(hp82335_exit_module); + +/* + * GPIB interrupt service routines + */ + +irqreturn_t hp82335_interrupt(int irq, void *arg) +{ + int status1, status2; + gpib_board_t *board = arg; + struct hp82335_priv *priv = board->private_data; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + status1 = read_byte(&priv->tms9914_priv, ISR0); + status2 = read_byte(&priv->tms9914_priv, ISR1); + hp82335_clear_interrupt(priv); + retval = tms9914_interrupt_have_status(board, &priv->tms9914_priv, status1, status2); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + diff --git a/drivers/staging/gpib/hp_82335/hp82335.h b/drivers/staging/gpib/hp_82335/hp82335.h new file mode 100644 index 0000000000000..5e5297af731ae --- /dev/null +++ b/drivers/staging/gpib/hp_82335/hp82335.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess * + ***************************************************************************/ + +#ifndef _HP82335_H +#define _HP82335_H + +#include "tms9914.h" +#include "gpibP.h" + +// struct which defines private_data for board +struct hp82335_priv { + struct tms9914_priv tms9914_priv; + unsigned int irq; + unsigned long raw_iobase; +}; + +// interfaces +extern gpib_interface_t hp82335_interface; + +// interface functions +int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read); +int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written); +int hp82335_take_control(gpib_board_t *board, int synchronous); +int hp82335_go_to_standby(gpib_board_t *board); +void hp82335_request_system_control(gpib_board_t *board, int request_control); +void hp82335_interface_clear(gpib_board_t *board, int assert); +void hp82335_remote_enable(gpib_board_t *board, int enable); +int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int + compare_8_bits); +void hp82335_disable_eos(gpib_board_t *board); +unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask); +int hp82335_primary_address(gpib_board_t *board, unsigned int address); +int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int + enable); +int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result); +void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config); +void hp82335_parallel_poll_response(gpib_board_t *board, int ist); +void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status); +void hp82335_return_to_local(gpib_board_t *board); + +// interrupt service routines +irqreturn_t hp82335_interrupt(int irq, void *arg); + +// utility functions +int hp82335_allocate_private(gpib_board_t *board); +void hp82335_free_private(gpib_board_t *board); + +// size of io memory region used +static const int hp82335_rom_size = 0x2000; +static const int hp82335_upper_iomem_size = 0x2000; + +// hp82335 register offsets +enum hp_read_regs { + HPREG_CSR = 0x17f8, + HPREG_STATUS = 0x1ffc, +}; + +enum hp_write_regs { + HPREG_INTR_CLEAR = 0x17f7, + HPREG_CCR = HPREG_CSR, +}; + +enum ccr_bits { + DMA_ENABLE = (1 << 0), /* DMA enable */ + DMA_CHAN_SELECT = (1 << 1), /* DMA channel select O=3,1=2 */ + INTR_ENABLE = (1 << 2), /* interrupt enable */ + SYS_DISABLE = (1 << 3), /* system controller disable */ +}; + +enum csr_bits { + SWITCH6 = (1 << 0), /* switch 6 position */ + SWITCH5 = (1 << 1), /* switch 5 position */ + SYS_CONTROLLER = (1 << 2), /* system controller bit */ + DMA_ENABLE_STATUS = (1 << 4), /* DMA enabled */ + DMA_CHAN_STATUS = (1 << 5), /* DMA channel 0=3,1=2 */ + INTR_ENABLE_STATUS = (1 << 6), /* Interrupt enable */ + INTR_PENDING = (1 << 7), /* Interrupt Pending */ +}; + +#endif // _HP82335_H diff --git a/drivers/staging/gpib/hp_82341/Makefile b/drivers/staging/gpib/hp_82341/Makefile new file mode 100644 index 0000000000000..1fe7db4f8ca47 --- /dev/null +++ b/drivers/staging/gpib/hp_82341/Makefile @@ -0,0 +1,2 @@ + +obj-m += hp_82341.o diff --git a/drivers/staging/gpib/hp_82341/hp_82341.c b/drivers/staging/gpib/hp_82341/hp_82341.c new file mode 100644 index 0000000000000..8ad1c885a9fb6 --- /dev/null +++ b/drivers/staging/gpib/hp_82341/hp_82341.c @@ -0,0 +1,895 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * Driver for hp 82341a/b/c/d boards. * + * Might be worth merging with Agilent 82350b driver. * + * copyright : (C) 2002, 2005 by Frank Mori Hess * + ***************************************************************************/ + +#include "hp_82341.h" +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +int hp_82341_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + int retval = 0; + unsigned short event_status; + int i; + int num_fifo_bytes; + //hardware doesn't support checking for end-of-string character when using fifo + if (tms_priv->eos_flags & REOS) + return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + read_and_clear_event_status(board); + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + //disable fifo for the moment + outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + // Handle corner case of board not in holdoff and one byte has slipped in already. + // Also, board sometimes has problems (spurious 1 byte reads) when read fifo is + // started up with board in + // TACS under certain data holdoff conditions. Doing a 1 byte tms9914-style + // read avoids these problems. + if (/*tms_priv->holdoff_active == 0 && */length > 1) { + size_t num_bytes; + + retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + pr_err("tms9914_read failed retval=%i\n", retval); + if (retval < 0 || *end) + return retval; + ++buffer; + --length; + } + tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); + tms9914_release_holdoff(tms_priv); + outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); + i = 0; + num_fifo_bytes = length - 1; + while (i < num_fifo_bytes && *end == 0) { + int block_size; + int j; + int count; + + if (num_fifo_bytes - i < hp_82341_fifo_size) + block_size = num_fifo_bytes - i; + else + block_size = hp_82341_fifo_size; + set_transfer_counter(hp_priv, block_size); + outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + + BUFFER_CONTROL_REG); + if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) + outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); + + clear_bit(READ_READY_BN, &tms_priv->state); + + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + (TERMINAL_COUNT_EVENT_BIT | + BUFFER_END_EVENT_BIT)) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + if (retval) { + pr_warn("%s: read wait interrupted\n", __func__); + retval = -ERESTARTSYS; + break; + } + // have to disable buffer before we can read from buffer port + outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + count = block_size - read_transfer_counter(hp_priv); + j = 0; + while (j < count && i < num_fifo_bytes) { + unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); + + buffer[i++] = data_word & 0xff; + ++j; + if (j < count && i < num_fifo_bytes) { + buffer[i++] = (data_word >> 8) & 0xff; + ++j; + } + } + if (event_status & BUFFER_END_EVENT_BIT) { + clear_bit(RECEIVED_END_BN, &tms_priv->state); + + *end = 1; + tms_priv->holdoff_active = 1; + } + if (test_bit(TIMO_NUM, &board->status)) { + pr_debug("%s: minor %i: read timed out\n", __FILE__, board->minor); + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + pr_warn("%s: device clear interrupted read\n", __FILE__); + retval = -EINTR; + break; + } + } + *bytes_read += i; + buffer += i; + length -= i; + if (retval < 0) + return retval; + // read last byte if we havn't received an END yet + if (*end == 0) { + size_t num_bytes; + // try to make sure we holdoff after last byte read + retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static int restart_write_fifo(gpib_board_t *board, struct hp_82341_priv *hp_priv) +{ + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + + if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0) + return 0; + while (1) { + int status; + + //restart doesn't work if data holdoff is in effect + status = tms9914_line_status(board, tms_priv); + if ((status & BusNRFD) == 0) { + outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); + return 0; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) + return -EINTR; + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (msleep_interruptible(1)) + return -EINTR; + } + return 0; +} + +int hp_82341_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + int i, j; + unsigned short event_status; + int retval = 0; + int fifo_xfer_len = length; + + *bytes_written = 0; + if (send_eoi) + --fifo_xfer_len; + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + read_and_clear_event_status(board); + outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); + for (i = 0; i < fifo_xfer_len;) { + int block_size; + + if (fifo_xfer_len - i < hp_82341_fifo_size) + block_size = fifo_xfer_len - i; + else + block_size = hp_82341_fifo_size; + set_transfer_counter(hp_priv, block_size); + // load data into board's fifo + for (j = 0; j < block_size;) { + unsigned short data_word = buffer[i++]; + ++j; + if (j < block_size) { + data_word |= buffer[i++] << 8; + ++j; + } + outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); + } + clear_bit(WRITE_READY_BN, &tms_priv->state); + outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + retval = restart_write_fifo(board, hp_priv); + if (retval < 0) { + pr_err("hp82341: failed to restart write stream\n"); + break; + } + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + TERMINAL_COUNT_EVENT_BIT) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + *bytes_written += block_size - read_transfer_counter(hp_priv); + if (retval) { + pr_warn("%s: write wait interrupted\n", __FILE__); + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + pr_debug("%s: minor %i: write timed out\n", __FILE__, board->minor); + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + pr_warn("%s: device clear interrupted write\n", __FILE__); + retval = -EINTR; + break; + } + } + if (retval) + return retval; + if (send_eoi) { + size_t num_bytes; + + retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static int hp_82341_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void hp_82341_detach(gpib_board_t *board); + +// wrappers for interface functions +int hp_82341_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); +} + +int hp_82341_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); +} + +int hp_82341_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); +} + +int hp_82341_take_control(gpib_board_t *board, int synchronous) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_take_control(board, &priv->tms9914_priv, synchronous); +} + +int hp_82341_go_to_standby(gpib_board_t *board) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_go_to_standby(board, &priv->tms9914_priv); +} + +void hp_82341_request_system_control(gpib_board_t *board, int request_control) +{ + struct hp_82341_priv *priv = board->private_data; + + if (request_control) + priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT; + else + priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT; + outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG); + tms9914_request_system_control(board, &priv->tms9914_priv, request_control); +} + +void hp_82341_interface_clear(gpib_board_t *board, int assert) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_interface_clear(board, &priv->tms9914_priv, assert); +} + +void hp_82341_remote_enable(gpib_board_t *board, int enable) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_remote_enable(board, &priv->tms9914_priv, enable); +} + +int hp_82341_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); +} + +void hp_82341_disable_eos(gpib_board_t *board) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_disable_eos(board, &priv->tms9914_priv); +} + +unsigned int hp_82341_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); +} + +int hp_82341_primary_address(gpib_board_t *board, unsigned int address) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_primary_address(board, &priv->tms9914_priv, address); +} + +int hp_82341_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); +} + +int hp_82341_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_parallel_poll(board, &priv->tms9914_priv, result); +} + +void hp_82341_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); +} + +void hp_82341_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); +} + +void hp_82341_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_serial_poll_response(board, &priv->tms9914_priv, status); +} + +static uint8_t hp_82341_serial_poll_status(gpib_board_t *board) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_serial_poll_status(board, &priv->tms9914_priv); +} + +static int hp_82341_line_status(const gpib_board_t *board) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_line_status(board, &priv->tms9914_priv); +} + +static unsigned int hp_82341_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); +} + +void hp_82341_return_to_local(gpib_board_t *board) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_return_to_local(board, &priv->tms9914_priv); +} + +gpib_interface_t hp_82341_unaccel_interface = { +name: "hp_82341_unaccel", +attach : hp_82341_attach, +detach : hp_82341_detach, +read : hp_82341_read, +write : hp_82341_write, +command : hp_82341_command, +request_system_control : hp_82341_request_system_control, +take_control : hp_82341_take_control, +go_to_standby : hp_82341_go_to_standby, +interface_clear : hp_82341_interface_clear, +remote_enable : hp_82341_remote_enable, +enable_eos : hp_82341_enable_eos, +disable_eos : hp_82341_disable_eos, +parallel_poll : hp_82341_parallel_poll, +parallel_poll_configure : hp_82341_parallel_poll_configure, +parallel_poll_response : hp_82341_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : hp_82341_line_status, +update_status : hp_82341_update_status, +primary_address : hp_82341_primary_address, +secondary_address : hp_82341_secondary_address, +serial_poll_response : hp_82341_serial_poll_response, +serial_poll_status : hp_82341_serial_poll_status, +t1_delay : hp_82341_t1_delay, +return_to_local : hp_82341_return_to_local, +}; + +gpib_interface_t hp_82341_interface = { +name: "hp_82341", +attach : hp_82341_attach, +detach : hp_82341_detach, +read : hp_82341_accel_read, +write : hp_82341_accel_write, +command : hp_82341_command, +request_system_control : hp_82341_request_system_control, +take_control : hp_82341_take_control, +go_to_standby : hp_82341_go_to_standby, +interface_clear : hp_82341_interface_clear, +remote_enable : hp_82341_remote_enable, +enable_eos : hp_82341_enable_eos, +disable_eos : hp_82341_disable_eos, +parallel_poll : hp_82341_parallel_poll, +parallel_poll_configure : hp_82341_parallel_poll_configure, +parallel_poll_response : hp_82341_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : hp_82341_line_status, +update_status : hp_82341_update_status, +primary_address : hp_82341_primary_address, +secondary_address : hp_82341_secondary_address, +serial_poll_response : hp_82341_serial_poll_response, +t1_delay : hp_82341_t1_delay, +return_to_local : hp_82341_return_to_local, +}; + +int hp_82341_allocate_private(gpib_board_t *board) +{ + board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + return 0; +} + +void hp_82341_free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static uint8_t hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return inb((unsigned long)(priv->iobase) + register_num); +} + +static void hp_82341_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num) +{ + outb(data, (unsigned long)(priv->iobase) + register_num); +} + +static int hp_82341_find_isapnp_board(struct pnp_dev **dev) +{ + *dev = pnp_find_dev(NULL, ISAPNP_VENDOR('H', 'W', 'P'), + ISAPNP_FUNCTION(0x1411), NULL); + if (!*dev || !(*dev)->card) { + pr_err("hp_82341: failed to find isapnp board\n"); + return -ENODEV; + } + if (pnp_device_attach(*dev) < 0) { + pr_err("hp_82341: board already active, skipping\n"); + return -EBUSY; + } + if (pnp_activate_dev(*dev) < 0) { + pnp_device_detach(*dev); + pr_err("hp_82341: failed to activate() atgpib/tnt, aborting\n"); + return -EAGAIN; + } + if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { + pnp_device_detach(*dev); + pr_err("hp_82341: invalid port or irq for atgpib/tnt, aborting\n"); + return -ENOMEM; + } + return 0; +} + +static int xilinx_ready(struct hp_82341_priv *hp_priv) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & XILINX_READY_BIT) + return 1; + else + return 0; + break; + case HW_VERSION_82341D: + if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_READY_BIT) + return 1; + else + return 0; + default: + pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__); + break; + } + return 0; +} + +static int xilinx_done(struct hp_82341_priv *hp_priv) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & DONE_PGL_BIT) + return 1; + else + return 0; + case HW_VERSION_82341D: + if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_DONE_BIT) + return 1; + else + return 0; + default: + pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__); + break; + } + return 0; +} + +static int irq_valid(struct hp_82341_priv *hp_priv, int irq) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + switch (irq) { + case 3: + case 5: + case 7: + case 9: + case 10: + case 11: + case 12: + case 15: + return 1; + default: + pr_err("hp_82341: invalid irq=%i for 82341C, irq must be 3, 5, 7, 9, 10, 11, 12, or 15.\n", + irq); + return 0; + } + break; + case HW_VERSION_82341D: + return 1; + default: + pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__); + break; + } + return 0; +} + +static int hp_82341_load_firmware_array(struct hp_82341_priv *hp_priv, + const unsigned char *firmware_data, + unsigned int firmware_length) +{ + int i, j; + static const int timeout = 100; + + for (i = 0; i < firmware_length; ++i) { + for (j = 0; j < timeout; ++j) { + if (need_resched()) + schedule(); + if (xilinx_ready(hp_priv)) + break; + usleep_range(10, 15); + } + if (j == timeout) { + pr_err("hp_82341: timed out waiting for Xilinx ready.\n"); + return -ETIMEDOUT; + } + outb(firmware_data[i], hp_priv->iobase[0] + XILINX_DATA_REG); + } + for (j = 0; j < timeout; ++j) { + if (xilinx_done(hp_priv)) + break; + if (need_resched()) + schedule(); + usleep_range(10, 15); + } + if (j == timeout) { + pr_err("hp_82341: timed out waiting for Xilinx done.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv, const gpib_board_config_t *config) +{ + if (config->init_data_length == 0) { + if (xilinx_done(hp_priv)) + return 0; + pr_err("hp_82341: board needs be initialized with firmware upload.\n" + "\tUse the --init-data option of gpib_config.\n"); + return -EINVAL; + } + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (config->init_data_length != hp_82341c_firmware_length) { + pr_err("hp_82341: bad firmware length=%i for 82341c (expected %i).\n", + config->init_data_length, hp_82341c_firmware_length); + return -EINVAL; + } + break; + case HW_VERSION_82341D: + if (config->init_data_length != hp_82341d_firmware_length) { + pr_err("hp_82341: bad firmware length=%i for 82341d (expected %i).\n", + config->init_data_length, hp_82341d_firmware_length); + return -EINVAL; + } + break; + default: + pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__); + break; + } + return hp_82341_load_firmware_array(hp_priv, config->init_data, config->init_data_length); +} + +static void set_xilinx_not_prog(struct hp_82341_priv *hp_priv, int assert) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (assert) + hp_priv->config_control_bits |= DONE_PGL_BIT; + else + hp_priv->config_control_bits &= ~DONE_PGL_BIT; + outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); + break; + case HW_VERSION_82341D: + if (assert) + isapnp_write_byte(PIO_DATA_REG, HP_82341D_NOT_PROG_BIT); + else + isapnp_write_byte(PIO_DATA_REG, 0x0); + break; + default: + break; + } +} + +// clear xilinx firmware +static int clear_xilinx(struct hp_82341_priv *hp_priv) +{ + set_xilinx_not_prog(hp_priv, 1); + if (msleep_interruptible(1)) + return -EINTR; + set_xilinx_not_prog(hp_priv, 0); + if (msleep_interruptible(1)) + return -EINTR; + set_xilinx_not_prog(hp_priv, 1); + if (msleep_interruptible(1)) + return -EINTR; + return 0; +} + +int hp_82341_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct hp_82341_priv *hp_priv; + struct tms9914_priv *tms_priv; + unsigned long start_addr; + void *iobase; + int irq; + int i; + int retval; + + board->status = 0; + if (hp_82341_allocate_private(board)) + return -ENOMEM; + hp_priv = board->private_data; + tms_priv = &hp_priv->tms9914_priv; + tms_priv->read_byte = hp_82341_read_byte; + tms_priv->write_byte = hp_82341_write_byte; + tms_priv->offset = 1; + + if (config->ibbase == 0) { + struct pnp_dev *dev; + int retval = hp_82341_find_isapnp_board(&dev); + + if (retval < 0) + return retval; + hp_priv->pnp_dev = dev; + iobase = (void *)(pnp_port_start(dev, 0)); + irq = pnp_irq(dev, 0); + hp_priv->hw_version = HW_VERSION_82341D; + hp_priv->io_region_offset = 0x8; + } else { + iobase = config->ibbase; + irq = config->ibirq; + hp_priv->hw_version = HW_VERSION_82341C; + hp_priv->io_region_offset = 0x400; + } + pr_info("hp_82341: base io 0x%p\n", iobase); + for (i = 0; i < hp_82341_num_io_regions; ++i) { + start_addr = (unsigned long)(iobase) + i * hp_priv->io_region_offset; + if (!request_region(start_addr, hp_82341_region_iosize, "hp_82341")) { + pr_err("hp_82341: failed to allocate io ports 0x%lx-0x%lx\n", + start_addr, + start_addr + hp_82341_region_iosize - 1); + return -EIO; + } + hp_priv->iobase[i] = start_addr; + } + tms_priv->iobase = (void *)(hp_priv->iobase[2]); + if (hp_priv->hw_version == HW_VERSION_82341D) { + retval = isapnp_cfg_begin(hp_priv->pnp_dev->card->number, + hp_priv->pnp_dev->number); + if (retval < 0) { + pr_err("hp_82341: isapnp_cfg_begin returned error\n"); + return retval; + } + isapnp_write_byte(PIO_DIRECTION_REG, HP_82341D_XILINX_READY_BIT | + HP_82341D_XILINX_DONE_BIT); + } + retval = clear_xilinx(hp_priv); + if (retval < 0) + return retval; + retval = hp_82341_load_firmware(hp_priv, config); + if (hp_priv->hw_version == HW_VERSION_82341D) + isapnp_cfg_end(); + if (retval < 0) + return retval; + if (irq_valid(hp_priv, irq) == 0) + return -EINVAL; + if (request_irq(irq, hp_82341_interrupt, 0, "hp_82341", board)) { + pr_err("hp_82341: failed to allocate IRQ %d\n", irq); + return -EIO; + } + hp_priv->irq = irq; + pr_info("hp_82341: IRQ %d\n", irq); + hp_priv->config_control_bits &= ~IRQ_SELECT_MASK; + hp_priv->config_control_bits |= IRQ_SELECT_BITS(irq); + outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); + hp_priv->mode_control_bits |= ENABLE_IRQ_CONFIG_BIT; + outb(hp_priv->mode_control_bits, hp_priv->iobase[0] + MODE_CONTROL_STATUS_REG); + tms9914_board_reset(tms_priv); + outb(ENABLE_BUFFER_END_EVENT_BIT | ENABLE_TERMINAL_COUNT_EVENT_BIT | + ENABLE_TI_INTERRUPT_EVENT_BIT, hp_priv->iobase[0] + EVENT_ENABLE_REG); + outb(ENABLE_BUFFER_END_INTERRUPT_BIT | ENABLE_TERMINAL_COUNT_INTERRUPT_BIT | + ENABLE_TI_INTERRUPT_BIT, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); + //write clear event register + outb((TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | + BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), + hp_priv->iobase[0] + EVENT_STATUS_REG); + + tms9914_online(board, tms_priv); + pr_info("hp_82341: board id %x %x %x %x\n", inb(hp_priv->iobase[1] + ID0_REG), + inb(hp_priv->iobase[1] + ID1_REG), inb(hp_priv->iobase[2] + ID2_REG), + inb(hp_priv->iobase[2] + ID3_REG)); + return 0; +} + +void hp_82341_detach(gpib_board_t *board) +{ + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv; + int i; + + if (hp_priv) { + tms_priv = &hp_priv->tms9914_priv; + if (hp_priv->iobase[0]) { + outb(0, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); + if (tms_priv->iobase) + tms9914_board_reset(tms_priv); + if (hp_priv->irq) + free_irq(hp_priv->irq, board); + } + for (i = 0; i < hp_82341_num_io_regions; ++i) { + if (hp_priv->iobase[i]) + release_region(hp_priv->iobase[i], hp_82341_region_iosize); + } + if (hp_priv->pnp_dev) + pnp_device_detach(hp_priv->pnp_dev); + } + hp_82341_free_private(board); +} + +static const struct pnp_device_id hp_82341_pnp_table[] = { + {.id = "HWP1411"}, + {.id = ""} +}; +MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); + +static int __init hp_82341_init_module(void) +{ + gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE); + gpib_register_driver(&hp_82341_interface, THIS_MODULE); + return 0; +} + +static void __exit hp_82341_exit_module(void) +{ + gpib_unregister_driver(&hp_82341_interface); + gpib_unregister_driver(&hp_82341_unaccel_interface); +} + +module_init(hp_82341_init_module); +module_exit(hp_82341_exit_module); + +/* + * GPIB interrupt service routines + */ +unsigned short read_and_clear_event_status(gpib_board_t *board) +{ + struct hp_82341_priv *hp_priv = board->private_data; + unsigned long flags; + unsigned short status; + + spin_lock_irqsave(&board->spinlock, flags); + status = hp_priv->event_status_bits; + hp_priv->event_status_bits = 0; + spin_unlock_irqrestore(&board->spinlock, flags); + return status; +} + +irqreturn_t hp_82341_interrupt(int irq, void *arg) +{ + int status1, status2; + gpib_board_t *board = arg; + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + int event_status; + + spin_lock_irqsave(&board->spinlock, flags); + event_status = inb(hp_priv->iobase[0] + EVENT_STATUS_REG); +// printk("hp_82341: interrupt event_status=0x%x\n", event_status); + if (event_status & INTERRUPT_PENDING_EVENT_BIT) + retval = IRQ_HANDLED; + //write-clear status bits + if (event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | + BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT)) { + outb(event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | + BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), + hp_priv->iobase[0] + EVENT_STATUS_REG); + hp_priv->event_status_bits |= event_status; + } + if (event_status & TI_INTERRUPT_EVENT_BIT) { + status1 = read_byte(tms_priv, ISR0); + status2 = read_byte(tms_priv, ISR1); + tms9914_interrupt_have_status(board, tms_priv, status1, status2); +/* printk("hp_82341: interrupt status1=0x%x status2=0x%x\n", + * status1, status2); + */ + } + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +int read_transfer_counter(struct hp_82341_priv *hp_priv) +{ + int lo, mid, value; + + lo = inb(hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); + mid = inb(hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); + value = (lo & 0xff) | ((mid << 8) & 0x7f00); + value = ~(value - 1) & 0x7fff; + return value; +} + +void set_transfer_counter(struct hp_82341_priv *hp_priv, int count) +{ + int complement = -count; + + outb(complement & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); + outb((complement >> 8) & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); + //I don't think the hi count reg is even used, but oh well + outb((complement >> 16) & 0xf, hp_priv->iobase[1] + TRANSFER_COUNT_HIGH_REG); +} + diff --git a/drivers/staging/gpib/hp_82341/hp_82341.h b/drivers/staging/gpib/hp_82341/hp_82341.h new file mode 100644 index 0000000000000..7c391860b3994 --- /dev/null +++ b/drivers/staging/gpib/hp_82341/hp_82341.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002, 2005 by Frank Mori Hess * + ***************************************************************************/ + +#include "tms9914.h" +#include "gpibP.h" + +enum hp_82341_hardware_version { + HW_VERSION_UNKNOWN, + HW_VERSION_82341C, + HW_VERSION_82341D, +}; + +// struct which defines private_data for board +struct hp_82341_priv { + struct tms9914_priv tms9914_priv; + unsigned int irq; + unsigned short config_control_bits; + unsigned short mode_control_bits; + unsigned short event_status_bits; + struct pnp_dev *pnp_dev; + unsigned long iobase[4]; + unsigned long io_region_offset; + enum hp_82341_hardware_version hw_version; +}; + +// interfaces +extern gpib_interface_t hp_82341_interface; + +// interface functions +int hp_82341_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read); +int hp_82341_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written); +int hp_82341_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read); +int hp_82341_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written); +int hp_82341_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written); +int hp_82341_take_control(gpib_board_t *board, int synchronous); +int hp_82341_go_to_standby(gpib_board_t *board); +void hp_82341_request_system_control(gpib_board_t *board, int request_control); +void hp_82341_interface_clear(gpib_board_t *board, int assert); +void hp_82341_remote_enable(gpib_board_t *board, int enable); +int hp_82341_enable_eos(gpib_board_t *board, uint8_t eos_byte, int + compare_8_bits); +void hp_82341_disable_eos(gpib_board_t *board); +unsigned int hp_82341_update_status(gpib_board_t *board, unsigned int clear_mask); +int hp_82341_primary_address(gpib_board_t *board, unsigned int address); +int hp_82341_secondary_address(gpib_board_t *board, unsigned int address, int + enable); +int hp_82341_parallel_poll(gpib_board_t *board, uint8_t *result); +void hp_82341_parallel_poll_configure(gpib_board_t *board, uint8_t config); +void hp_82341_parallel_poll_response(gpib_board_t *board, int ist); +void hp_82341_serial_poll_response(gpib_board_t *board, uint8_t status); +void hp_82341_return_to_local(gpib_board_t *board); + +// interrupt service routines +irqreturn_t hp_82341_interrupt(int irq, void *arg); + +// utility functions +int hp_82341_allocate_private(gpib_board_t *board); +void hp_82341_free_private(gpib_board_t *board); + +static const int hp_82341_region_iosize = 0x8; +static const int hp_82341_num_io_regions = 4; +static const int hp_82341_fifo_size = 0xffe; +static const int hp_82341c_firmware_length = 5764; +static const int hp_82341d_firmware_length = 5302; + +// hp 82341 register offsets +enum hp_82341_region_0_registers { + CONFIG_CONTROL_STATUS_REG = 0x0, + MODE_CONTROL_STATUS_REG = 0x1, + MONITOR_REG = 0x2, // after initialization + XILINX_DATA_REG = 0x2, // before initialization, write only + INTERRUPT_ENABLE_REG = 0x3, + EVENT_STATUS_REG = 0x4, + EVENT_ENABLE_REG = 0x5, + STREAM_STATUS_REG = 0x7, +}; + +enum hp_82341_region_1_registers { + ID0_REG = 0x2, + ID1_REG = 0x3, + TRANSFER_COUNT_LOW_REG = 0x4, + TRANSFER_COUNT_MID_REG = 0x5, + TRANSFER_COUNT_HIGH_REG = 0x6, +}; + +enum hp_82341_region_3_registers { + BUFFER_PORT_LOW_REG = 0x0, + BUFFER_PORT_HIGH_REG = 0x1, + ID2_REG = 0x2, + ID3_REG = 0x3, + BUFFER_FLUSH_REG = 0x4, + BUFFER_CONTROL_REG = 0x7 +}; + +enum config_control_status_bits { + IRQ_SELECT_MASK = 0x7, + DMA_CONFIG_MASK = 0x18, + ENABLE_DMA_CONFIG_BIT = 0x20, + XILINX_READY_BIT = 0x40, //read only + DONE_PGL_BIT = 0x80 +}; + +static inline unsigned int IRQ_SELECT_BITS(int irq) +{ + switch (irq) { + case 3: + return 0x3; + case 5: + return 0x2; + case 7: + return 0x1; + case 9: + return 0x0; + case 10: + return 0x7; + case 11: + return 0x6; + case 12: + return 0x5; + case 15: + return 0x4; + default: + return 0x0; + } +}; + +enum mode_control_status_bits { + SLOT8_BIT = 0x1, // read only + ACTIVE_CONTROLLER_BIT = 0x2, // read only + ENABLE_DMA_BIT = 0x4, + SYSTEM_CONTROLLER_BIT = 0x8, + MONITOR_BIT = 0x10, + ENABLE_IRQ_CONFIG_BIT = 0x20, + ENABLE_TI_STREAM_BIT = 0x40 +}; + +enum monitor_bits { + MONITOR_INTERRUPT_PENDING_BIT = 0x1, // read only + MONITOR_CLEAR_HOLDOFF_BIT = 0x2, // write only + MONITOR_PPOLL_BIT = 0x4, // write clear + MONITOR_SRQ_BIT = 0x8, // write clear + MONITOR_IFC_BIT = 0x10, // write clear + MONITOR_REN_BIT = 0x20, // write clear + MONITOR_END_BIT = 0x40, // write clear + MONITOR_DAV_BIT = 0x80 // write clear +}; + +enum interrupt_enable_bits { + ENABLE_TI_INTERRUPT_BIT = 0x1, + ENABLE_POINTERS_EQUAL_INTERRUPT_BIT = 0x4, + ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10, + ENABLE_TERMINAL_COUNT_INTERRUPT_BIT = 0x20, + ENABLE_DMA_TERMINAL_COUNT_INTERRUPT_BIT = 0x80, +}; + +enum event_status_bits { + TI_INTERRUPT_EVENT_BIT = 0x1, //write clear + INTERRUPT_PENDING_EVENT_BIT = 0x2, // read only + POINTERS_EQUAL_EVENT_BIT = 0x4, //write clear + BUFFER_END_EVENT_BIT = 0x10, //write clear + TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear + DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear +}; + +enum event_enable_bits { + ENABLE_TI_INTERRUPT_EVENT_BIT = 0x1, //write clear + ENABLE_POINTERS_EQUAL_EVENT_BIT = 0x4, //write clear + ENABLE_BUFFER_END_EVENT_BIT = 0x10, //write clear + ENABLE_TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear + ENABLE_DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear +}; + +enum stream_status_bits { + HALTED_STATUS_BIT = 0x1, //read + RESTART_STREAM_BIT = 0x1 //write +}; + +enum buffer_control_bits { + DIRECTION_GPIB_TO_HOST_BIT = 0x20, // transfer direction (set for gpib to host) + ENABLE_TI_BUFFER_BIT = 0x40, //enable fifo + FAST_WR_EN_BIT = 0x80, // 350 ns t1 delay? +}; + +// registers accessible through isapnp chip on 82341d +enum hp_82341d_pnp_registers { + PIO_DATA_REG = 0x20, //read/write pio data lines + PIO_DIRECTION_REG = 0x21, // set pio data line directions (set for input) +}; + +enum hp_82341d_pnp_pio_bits { + HP_82341D_XILINX_READY_BIT = 0x1, + HP_82341D_XILINX_DONE_BIT = 0x2, + // use register layout compatible with C and older versions instead of 32 contiguous ioports + HP_82341D_LEGACY_MODE_BIT = 0x4, + HP_82341D_NOT_PROG_BIT = 0x8, // clear to reinitialize xilinx +}; + +unsigned short read_and_clear_event_status(gpib_board_t *board); +int read_transfer_counter(struct hp_82341_priv *hp_priv); +void set_transfer_counter(struct hp_82341_priv *hp_priv, int count); diff --git a/drivers/staging/gpib/include/amcc5920.h b/drivers/staging/gpib/include/amcc5920.h new file mode 100644 index 0000000000000..766b3799223f1 --- /dev/null +++ b/drivers/staging/gpib/include/amcc5920.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Header for amcc5920 pci chip + * + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +// plx pci chip registers and bits +enum amcc_registers { + AMCC_INTCS_REG = 0x38, + AMCC_PASS_THRU_REG = 0x60, +}; + +enum amcc_incsr_bits { + AMCC_ADDON_INTR_ENABLE_BIT = 0x2000, + AMCC_ADDON_INTR_ACTIVE_BIT = 0x400000, + AMCC_INTR_ACTIVE_BIT = 0x800000, +}; + +static const int bits_per_region = 8; + +static inline uint32_t amcc_wait_state_bits(unsigned int region, unsigned int num_wait_states) +{ + return (num_wait_states & 0x7) << (-region * bits_per_region); +}; + +enum amcc_prefetch_bits { + PREFETCH_DISABLED = 0x0, + PREFETCH_SMALL = 0x8, + PREFETCH_MEDIUM = 0x10, + PREFETCH_LARGE = 0x18, +}; + +static inline uint32_t amcc_prefetch_bits(unsigned int region, enum amcc_prefetch_bits prefetch) +{ + return prefetch << (--region * bits_per_region); +}; + +static inline uint32_t amcc_PTADR_mode_bit(unsigned int region) +{ + return 0x80 << (--region * bits_per_region); +}; + +static inline uint32_t amcc_disable_write_fifo_bit(unsigned int region) +{ + return 0x20 << (--region * bits_per_region); +}; + diff --git a/drivers/staging/gpib/include/amccs5933.h b/drivers/staging/gpib/include/amccs5933.h new file mode 100644 index 0000000000000..4de0f67974584 --- /dev/null +++ b/drivers/staging/gpib/include/amccs5933.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Registers and bits for amccs5933 pci chip + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +// register offsets +enum { + MBEF_REG = 0x34, // mailbux empty/full + INTCSR_REG = 0x38, // interrupt control and status + BMCSR_REG = 0x3c, // bus master control and status +}; + +// incoming mailbox 0-3 register offsets +extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox) +{ + return (0x10 + 4 * mailbox); +}; + +// bit definitions + +// INTCSR bits +enum { + OUTBOX_EMPTY_INTR_BIT = 0x10, // enable outbox empty interrupt + INBOX_FULL_INTR_BIT = 0x1000, // enable inbox full interrupt + INBOX_INTR_CS_BIT = 0x20000, // read, or write clear inbox full interrupt + INTR_ASSERTED_BIT = 0x800000, // read only, interrupt asserted +}; + +// select byte 0 to 3 of incoming mailbox +extern inline int INBOX_BYTE_BITS(unsigned int byte) +{ + return (byte & 0x3) << 8; +}; + +// select incoming mailbox 0 to 3 +extern inline int INBOX_SELECT_BITS(unsigned int mailbox) +{ + return (mailbox & 0x3) << 10; +}; + +// select byte 0 to 3 of outgoing mailbox +extern inline int OUTBOX_BYTE_BITS(unsigned int byte) +{ + return (byte & 0x3); +}; + +// select outgoing mailbox 0 to 3 +extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox) +{ + return (mailbox & 0x3) << 2; +}; + +//BMCSR bits +enum { + MBOX_FLAGS_RESET_BIT = 0x08000000, // resets mailbox empty/full flags +}; + diff --git a/drivers/staging/gpib/include/gpibP.h b/drivers/staging/gpib/include/gpibP.h new file mode 100644 index 0000000000000..5fc42b645ab70 --- /dev/null +++ b/drivers/staging/gpib/include/gpibP.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002,2003 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_P_H +#define _GPIB_P_H + +#include + +#include "gpib_types.h" +#include "gpib_proto.h" +#include "gpib_user.h" +#include "gpib_ioctl.h" + +#include +#include + +void gpib_register_driver(gpib_interface_t *interface, struct module *mod); +void gpib_unregister_driver(gpib_interface_t *interface); +struct pci_dev *gpib_pci_get_device(const gpib_board_config_t *config, unsigned int vendor_id, + unsigned int device_id, struct pci_dev *from); +struct pci_dev *gpib_pci_get_subsys(const gpib_board_config_t *config, unsigned int vendor_id, + unsigned int device_id, unsigned int ss_vendor, + unsigned int ss_device, struct pci_dev *from); +unsigned int num_gpib_events(const gpib_event_queue_t *queue); +int push_gpib_event(gpib_board_t *board, short event_type); +int pop_gpib_event(gpib_board_t *board, gpib_event_queue_t *queue, short *event_type); +int gpib_request_pseudo_irq(gpib_board_t *board, irqreturn_t (*handler)(int, void *)); +void gpib_free_pseudo_irq(gpib_board_t *board); +int gpib_match_device_path(struct device *dev, const char *device_path_in); + +extern gpib_board_t board_array[GPIB_MAX_NUM_BOARDS]; + +extern struct list_head registered_drivers; + +#include + +void writeb_wrapper(unsigned int value, void *address); +unsigned int readb_wrapper(void *address); +void outb_wrapper(unsigned int value, void *address); +unsigned int inb_wrapper(void *address); +void writew_wrapper(unsigned int value, void *address); +unsigned int readw_wrapper(void *address); +void outw_wrapper(unsigned int value, void *address); +unsigned int inw_wrapper(void *address); + +#endif // _GPIB_P_H + diff --git a/drivers/staging/gpib/include/gpib_pci_ids.h b/drivers/staging/gpib/include/gpib_pci_ids.h new file mode 100644 index 0000000000000..52dcab07a7d18 --- /dev/null +++ b/drivers/staging/gpib/include/gpib_pci_ids.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __GPIB_PCI_IDS_H +#define __GPIB_PCI_IDS_H + +#ifndef PCI_VENDOR_ID_AMCC +#define PCI_VENDOR_ID_AMCC 0x10e8 +#endif + +#ifndef PCI_VENDOR_ID_CBOARDS +#define PCI_VENDOR_ID_CBOARDS 0x1307 +#endif + +#ifndef PCI_VENDOR_ID_QUANCOM +#define PCI_VENDOR_ID_QUANCOM 0x8008 +#endif + +#ifndef PCI_DEVICE_ID_QUANCOM_GPIB +#define PCI_DEVICE_ID_QUANCOM_GPIB 0x3302 +#endif + +#endif // __GPIB_PCI_IDS_H + diff --git a/drivers/staging/gpib/include/gpib_proto.h b/drivers/staging/gpib/include/gpib_proto.h new file mode 100644 index 0000000000000..1499f954210b8 --- /dev/null +++ b/drivers/staging/gpib/include/gpib_proto.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIB_PROTO_INCLUDED +#define GPIB_PROTO_INCLUDED + +#include + +int ibopen(struct inode *inode, struct file *filep); +int ibclose(struct inode *inode, struct file *file); +long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg); +int osInit(void); +void osReset(void); +void os_start_timer(gpib_board_t *board, unsigned int usec_timeout); +void os_remove_timer(gpib_board_t *board); +void osSendEOI(void); +void osSendEOI(void); +void init_gpib_board(gpib_board_t *board); +static inline unsigned long usec_to_jiffies(unsigned int usec) +{ + unsigned long usec_per_jiffy = 1000000 / HZ; + + return 1 + (usec + usec_per_jiffy - 1) / usec_per_jiffy; +}; + +int serial_poll_all(gpib_board_t *board, unsigned int usec_timeout); +void init_gpib_descriptor(gpib_descriptor_t *desc); +int dvrsp(gpib_board_t *board, unsigned int pad, int sad, + unsigned int usec_timeout, uint8_t *result); +int ibAPWait(gpib_board_t *board, int pad); +int ibAPrsp(gpib_board_t *board, int padsad, char *spb); +void ibAPE(gpib_board_t *board, int pad, int v); +int ibcac(gpib_board_t *board, int sync, int fallback_to_async); +int ibcmd(gpib_board_t *board, uint8_t *buf, size_t length, size_t *bytes_written); +int ibgts(gpib_board_t *board); +int ibonline(gpib_board_t *board); +int iboffline(gpib_board_t *board); +int iblines(const gpib_board_t *board, short *lines); +int ibrd(gpib_board_t *board, uint8_t *buf, size_t length, int *end_flag, size_t *bytes_read); +int ibrpp(gpib_board_t *board, uint8_t *buf); +int ibrsv2(gpib_board_t *board, uint8_t status_byte, int new_reason_for_service); +void ibrsc(gpib_board_t *board, int request_control); +int ibsic(gpib_board_t *board, unsigned int usec_duration); +int ibsre(gpib_board_t *board, int enable); +int ibpad(gpib_board_t *board, unsigned int addr); +int ibsad(gpib_board_t *board, int addr); +int ibeos(gpib_board_t *board, int eos, int eosflags); +int ibwait(gpib_board_t *board, int wait_mask, int clear_mask, int set_mask, + int *status, unsigned long usec_timeout, gpib_descriptor_t *desc); +int ibwrt(gpib_board_t *board, uint8_t *buf, size_t cnt, int send_eoi, size_t *bytes_written); +int ibstatus(gpib_board_t *board); +int general_ibstatus(gpib_board_t *board, const gpib_status_queue_t *device, + int clear_mask, int set_mask, gpib_descriptor_t *desc); +int io_timed_out(gpib_board_t *board); +int ibppc(gpib_board_t *board, uint8_t configuration); + +#endif /* GPIB_PROTO_INCLUDED */ diff --git a/drivers/staging/gpib/include/gpib_state_machines.h b/drivers/staging/gpib/include/gpib_state_machines.h new file mode 100644 index 0000000000000..7488c00f191e8 --- /dev/null +++ b/drivers/staging/gpib/include/gpib_state_machines.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2006 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_STATE_MACHINES_H +#define _GPIB_STATE_MACHINES_H + +enum talker_function_state { + talker_idle, + talker_addressed, + talker_active, + serial_poll_active +}; + +enum listener_function_state { + listener_idle, + listener_addressed, + listener_active +}; + +#endif // _GPIB_STATE_MACHINES_H diff --git a/drivers/staging/gpib/include/gpib_types.h b/drivers/staging/gpib/include/gpib_types.h new file mode 100644 index 0000000000000..ee2643da6d710 --- /dev/null +++ b/drivers/staging/gpib/include/gpib_types.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_TYPES_H +#define _GPIB_TYPES_H + +#ifdef __KERNEL__ +/* gpib_interface_t defines the interface + * between the board-specific details dealt with in the drivers + * and generic interface provided by gpib-common. + * This really should be in a different header file. + */ +#include "gpib_user.h" +#include +#include +#include +#include +#include +#include +#include + +typedef struct gpib_interface_struct gpib_interface_t; +typedef struct gpib_board_struct gpib_board_t; + +/* config parameters that are only used by driver attach functions */ +typedef struct { + /* firmware blob */ + void *init_data; + int init_data_length; + /* IO base address to use for non-pnp cards (set by core, driver should make local copy) */ + void *ibbase; + /* IRQ to use for non-pnp cards (set by core, driver should make local copy) */ + unsigned int ibirq; + /* dma channel to use for non-pnp cards (set by core, driver should make local copy) */ + unsigned int ibdma; + /* pci bus of card, useful for distinguishing multiple identical pci cards + * (negative means don't care) + */ + int pci_bus; + /* pci slot of card, useful for distinguishing multiple identical pci cards + * (negative means don't care) + */ + int pci_slot; + /* sysfs device path of hardware to attach */ + char *device_path; + /* serial number of hardware to attach */ + char *serial_number; +} gpib_board_config_t; + +struct gpib_interface_struct { + /* name of board */ + char *name; + /* attach() initializes board and allocates resources */ + int (*attach)(gpib_board_t *board, const gpib_board_config_t *config); + /* detach() shuts down board and frees resources */ + void (*detach)(gpib_board_t *board); + /* read() should read at most 'length' bytes from the bus into + * 'buffer'. It should return when it fills the buffer or + * encounters an END (EOI and or EOS if appropriate). It should set 'end' + * to be nonzero if the read was terminated by an END, otherwise 'end' + * should be zero. + * Ultimately, this will be changed into or replaced by an asynchronous + * read. Zero return value for success, negative + * return indicates error. + * nbytes returns number of bytes read + */ + int (*read)(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read); + /* write() should write 'length' bytes from buffer to the bus. + * If the boolean value send_eoi is nonzero, then EOI should + * be sent along with the last byte. Returns number of bytes + * written or negative value on error. + */ + int (*write)(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written); + /* command() writes the command bytes in 'buffer' to the bus + * Returns zero on success or negative value on error. + */ + int (*command)(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written); + /* Take control (assert ATN). If 'asyncronous' is nonzero, take + * control asyncronously (assert ATN immediately without waiting + * for other processes to complete first). Should not return + * until board becomes controller in charge. Returns zero no success, + * nonzero on error. + */ + int (*take_control)(gpib_board_t *board, int asyncronous); + /* De-assert ATN. Returns zero on success, nonzer on error. + */ + int (*go_to_standby)(gpib_board_t *board); + /* request/release control of the IFC and REN lines (system controller) */ + void (*request_system_control)(gpib_board_t *board, int request_control); + /* Asserts or de-asserts 'interface clear' (IFC) depending on + * boolean value of 'assert' + */ + void (*interface_clear)(gpib_board_t *board, int assert); + /* Sends remote enable command if 'enable' is nonzero, disables remote mode + * if 'enable' is zero + */ + void (*remote_enable)(gpib_board_t *board, int enable); + /* enable END for reads, when byte 'eos' is received. If + * 'compare_8_bits' is nonzero, then all 8 bits are compared + * with the eos bytes. Otherwise only the 7 least significant + * bits are compared. + */ + int (*enable_eos)(gpib_board_t *board, uint8_t eos, int compare_8_bits); + /* disable END on eos byte (END on EOI only)*/ + void (*disable_eos)(gpib_board_t *board); + /* configure parallel poll */ + void (*parallel_poll_configure)(gpib_board_t *board, uint8_t configuration); + /* conduct parallel poll */ + int (*parallel_poll)(gpib_board_t *board, uint8_t *result); + /* set/clear ist (individual status bit) */ + void (*parallel_poll_response)(gpib_board_t *board, int ist); + /* select local parallel poll configuration mode PP2 versus remote PP1 */ + void (*local_parallel_poll_mode)(gpib_board_t *board, int local); + /* Returns current status of the bus lines. Should be set to + * NULL if your board does not have the ability to query the + * state of the bus lines. + */ + int (*line_status)(const gpib_board_t *board); + /* updates and returns the board's current status. + * The meaning of the bits are specified in gpib_user.h + * in the IBSTA section. The driver does not need to + * worry about setting the CMPL, END, TIMO, or ERR bits. + */ + unsigned int (*update_status)(gpib_board_t *board, unsigned int clear_mask); + /* Sets primary address 0-30 for gpib interface card. + */ + int (*primary_address)(gpib_board_t *board, unsigned int address); + /* Sets and enables, or disables secondary address 0-30 + * for gpib interface card. + */ + int (*secondary_address)(gpib_board_t *board, unsigned int address, + int enable); + /* Sets the byte the board should send in response to a serial poll. + * This function should also start or stop requests for service via + * IEEE 488.2 reqt/reqf, based on MSS (bit 6 of the status_byte). + * If the more flexible serial_poll_response2 is implemented by the + * driver, then this method should be left NULL since it will not + * be used. This method can generate spurious service requests + * which are allowed by IEEE 488.2, but not ideal. + * + * This method should implement the serial poll response method described + * by IEEE 488.2 section 11.3.3.4.3 "Allowed Coupled Control of + * STB, reqt, and reqf". + */ + void (*serial_poll_response)(gpib_board_t *board, uint8_t status_byte); + /* Sets the byte the board should send in response to a serial poll. + * This function should also request service via IEEE 488.2 reqt/reqf + * based on MSS (bit 6 of the status_byte) and new_reason_for_service. + * reqt should be set true if new_reason_for_service is true, + * and reqf should be set true if MSS is false. This function + * will never be called with MSS false and new_reason_for_service + * true simultaneously, so don't worry about that case. + * + * This method implements the serial poll response method described + * by IEEE 488.2 section 11.3.3.4.1 "Preferred Implementation". + * + * If this method is left NULL by the driver, then the user library + * function ibrsv2 will not work. + */ + void (*serial_poll_response2)(gpib_board_t *board, uint8_t status_byte, + int new_reason_for_service); + /* returns the byte the board will send in response to a serial poll. + */ + uint8_t (*serial_poll_status)(gpib_board_t *board); + /* adjust T1 delay */ + unsigned int (*t1_delay)(gpib_board_t *board, unsigned int nano_sec); + /* go to local mode */ + void (*return_to_local)(gpib_board_t *board); + /* board does not support 7 bit eos comparisons */ + unsigned no_7_bit_eos : 1; + /* skip check for listeners before trying to send command bytes */ + unsigned skip_check_for_command_acceptors : 1; +}; + +typedef struct { + struct list_head event_head; + spinlock_t lock; // for access to event list + unsigned int num_events; + unsigned dropped_event : 1; +} gpib_event_queue_t; + +static inline void init_event_queue(gpib_event_queue_t *queue) +{ + INIT_LIST_HEAD(&queue->event_head); + queue->num_events = 0; + queue->dropped_event = 0; + spin_lock_init(&queue->lock); +} + +/* struct for supporting polling operation when irq is not available */ +struct gpib_pseudo_irq { + struct timer_list timer; + irqreturn_t (*handler)(int irq, void *arg); + gpib_board_t *board; + atomic_t active; +}; + +static inline void init_gpib_pseudo_irq(struct gpib_pseudo_irq *pseudo_irq) +{ + pseudo_irq->handler = NULL; + timer_setup(&pseudo_irq->timer, NULL, 0); + atomic_set(&pseudo_irq->active, 0); +} + +/* list so we can make a linked list of drivers */ +typedef struct gpib_interface_list_struct { + struct list_head list; + gpib_interface_t *interface; + struct module *module; +} gpib_interface_list_t; + +/* One gpib_board_t is allocated for each physical board in the computer. + * It provides storage for variables local to each board, and interface + * functions for performing operations on the board + */ +struct gpib_board_struct { + /* functions used by this board */ + gpib_interface_t *interface; + /* Pointer to module whose use count we should increment when + * interface is in use + */ + struct module *provider_module; + /* buffer used to store read/write data for this board */ + u8 *buffer; + /* length of buffer */ + unsigned int buffer_length; + /* Used to hold the board's current status (see update_status() above) + */ + unsigned long status; + /* Driver should only sleep on this wait queue. It is special in that the + * core will wake this queue and set the TIMO bit in 'status' when the + * watchdog timer times out. + */ + wait_queue_head_t wait; + /* Lock that only allows one process to access this board at a time. + * Has to be first in any locking order, since it can be locked over + * multiple ioctls. + */ + struct mutex user_mutex; + /* Mutex which compensates for removal of "big kernel lock" from kernel. + * Should not be held for extended waits. + */ + struct mutex big_gpib_mutex; + /* pid of last process to lock the board mutex */ + pid_t locking_pid; + spinlock_t locking_pid_spinlock; // lock for setting locking pid + /* Spin lock for dealing with races with the interrupt handler */ + spinlock_t spinlock; + /* Watchdog timer to enable timeouts */ + struct timer_list timer; + /* device of attached driver if any */ + struct device *dev; + /* gpib_common device gpibN */ + struct device *gpib_dev; + /* 'private_data' can be used as seen fit by the driver to + * store additional variables for this board + */ + void *private_data; + /* Number of open file descriptors using this board */ + unsigned int use_count; + /* list of open devices connected to this board */ + struct list_head device_list; + /* primary address */ + unsigned int pad; + /* secondary address */ + int sad; + /* timeout for io operations, in microseconds */ + unsigned int usec_timeout; + /* board's parallel poll configuration byte */ + u8 parallel_poll_configuration; + /* t1 delay we are using */ + unsigned int t1_nano_sec; + /* Count that keeps track of whether board is up and running or not */ + unsigned int online; + /* number of processes trying to autopoll */ + int autospollers; + /* autospoll kernel thread */ + struct task_struct *autospoll_task; + /* queue for recording received trigger/clear/ifc events */ + gpib_event_queue_t event_queue; + /* minor number for this board's device file */ + int minor; + /* struct to deal with polling mode*/ + struct gpib_pseudo_irq pseudo_irq; + /* error dong autopoll */ + atomic_t stuck_srq; + gpib_board_config_t config; + /* Flag that indicates whether board is system controller of the bus */ + unsigned master : 1; + /* individual status bit */ + unsigned ist : 1; + /* one means local parallel poll mode ieee 488.1 PP2 (or no parallel poll PP0), + * zero means remote parallel poll configuration mode ieee 488.1 PP1 + */ + unsigned local_ppoll_mode : 1; +}; + +/* element of event queue */ +typedef struct { + struct list_head list; + short event_type; +} gpib_event_t; + +/* Each board has a list of gpib_status_queue_t to keep track of all open devices + * on the bus, so we know what address to poll when we get a service request + */ +typedef struct { + /* list_head so we can make a linked list of devices */ + struct list_head list; + unsigned int pad; /* primary gpib address */ + int sad; /* secondary gpib address (negative means disabled) */ + /* stores serial poll bytes for this device */ + struct list_head status_bytes; + unsigned int num_status_bytes; + /* number of times this address is opened */ + unsigned int reference_count; + /* flags loss of status byte error due to limit on size of queue */ + unsigned dropped_byte : 1; +} gpib_status_queue_t; + +typedef struct { + struct list_head list; + u8 poll_byte; +} status_byte_t; + +void init_gpib_status_queue(gpib_status_queue_t *device); + +/* Used to store device-descriptor-specific information */ +typedef struct { + unsigned int pad; /* primary gpib address */ + int sad; /* secondary gpib address (negative means disabled) */ + atomic_t io_in_progress; + unsigned is_board : 1; + unsigned autopoll_enabled : 1; +} gpib_descriptor_t; + +typedef struct { + atomic_t holding_mutex; + gpib_descriptor_t *descriptors[GPIB_MAX_NUM_DESCRIPTORS]; + /* locked while descriptors are being allocated/deallocated */ + struct mutex descriptors_mutex; + unsigned got_module : 1; +} gpib_file_private_t; + +#endif /* __KERNEL__ */ + +#endif /* _GPIB_TYPES_H */ diff --git a/drivers/staging/gpib/include/nec7210.h b/drivers/staging/gpib/include/nec7210.h new file mode 100644 index 0000000000000..c00aba4ce846d --- /dev/null +++ b/drivers/staging/gpib/include/nec7210.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _NEC7210_H +#define _NEC7210_H + +#include "gpib_state_machines.h" +#include +#include +#include +#include + +#include "gpib_types.h" +#include "nec7210_registers.h" + +/* struct used to provide variables local to a nec7210 chip */ +struct nec7210_priv { + void *iobase; + unsigned int offset; // offset between successive nec7210 io addresses + unsigned int dma_channel; + u8 *dma_buffer; + unsigned int dma_buffer_length; // length of dma buffer + dma_addr_t dma_buffer_addr; // bus address of board->buffer for use with dma + // software copy of bits written to registers + u8 reg_bits[8]; + u8 auxa_bits; // bits written to auxiliary register A + u8 auxb_bits; // bits written to auxiliary register B + // used to keep track of board's state, bit definitions given below + unsigned long state; + /* lock for chips that extend the nec7210 registers by paging in alternate regs */ + spinlock_t register_page_lock; + // wrappers for outb, inb, readb, or writeb + u8 (*read_byte)(struct nec7210_priv *priv, unsigned int register_number); + void (*write_byte)(struct nec7210_priv *priv, u8 byte, unsigned int register_number); + enum nec7210_chipset type; + enum talker_function_state talker_state; + enum listener_function_state listener_state; + void *private; + unsigned srq_pending : 1; +}; + +static inline void init_nec7210_private(struct nec7210_priv *priv) +{ + memset(priv, 0, sizeof(struct nec7210_priv)); + spin_lock_init(&priv->register_page_lock); +} + +// slightly shorter way to access read_byte and write_byte +static inline u8 read_byte(struct nec7210_priv *priv, unsigned int register_number) +{ + return priv->read_byte(priv, register_number); +} + +static inline void write_byte(struct nec7210_priv *priv, u8 byte, unsigned int register_number) +{ + priv->write_byte(priv, byte, register_number); +} + +// struct nec7210_priv.state bit numbers +enum { + PIO_IN_PROGRESS_BN, // pio transfer in progress + DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress + DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress + READ_READY_BN, // board has data byte available to read + WRITE_READY_BN, // board is ready to send a data byte + COMMAND_READY_BN, // board is ready to send a command byte + RECEIVED_END_BN, // received END + BUS_ERROR_BN, // output error has occurred + RFD_HOLDOFF_BN, // rfd holdoff in effect + DEV_CLEAR_BN, // device clear received + ADR_CHANGE_BN, // address state change occurred +}; + +// interface functions +int nec7210_read(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read); +int nec7210_write(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length, int send_eoi, size_t *bytes_written); +int nec7210_command(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length, size_t *bytes_written); +int nec7210_take_control(gpib_board_t *board, struct nec7210_priv *priv, int syncronous); +int nec7210_go_to_standby(gpib_board_t *board, struct nec7210_priv *priv); +void nec7210_request_system_control(gpib_board_t *board, + struct nec7210_priv *priv, int request_control); +void nec7210_interface_clear(gpib_board_t *board, struct nec7210_priv *priv, int assert); +void nec7210_remote_enable(gpib_board_t *board, struct nec7210_priv *priv, int enable); +int nec7210_enable_eos(gpib_board_t *board, struct nec7210_priv *priv, uint8_t eos_bytes, + int compare_8_bits); +void nec7210_disable_eos(gpib_board_t *board, struct nec7210_priv *priv); +unsigned int nec7210_update_status(gpib_board_t *board, struct nec7210_priv *priv, + unsigned int clear_mask); +unsigned int nec7210_update_status_nolock(gpib_board_t *board, struct nec7210_priv *priv); +int nec7210_primary_address(const gpib_board_t *board, + struct nec7210_priv *priv, unsigned int address); +int nec7210_secondary_address(const gpib_board_t *board, struct nec7210_priv *priv, + unsigned int address, int enable); +int nec7210_parallel_poll(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *result); +void nec7210_serial_poll_response(gpib_board_t *board, struct nec7210_priv *priv, uint8_t status); +void nec7210_parallel_poll_configure(gpib_board_t *board, + struct nec7210_priv *priv, unsigned int configuration); +void nec7210_parallel_poll_response(gpib_board_t *board, + struct nec7210_priv *priv, int ist); +uint8_t nec7210_serial_poll_status(gpib_board_t *board, + struct nec7210_priv *priv); +unsigned int nec7210_t1_delay(gpib_board_t *board, + struct nec7210_priv *priv, unsigned int nano_sec); +void nec7210_return_to_local(const gpib_board_t *board, struct nec7210_priv *priv); + +// utility functions +void nec7210_board_reset(struct nec7210_priv *priv, const gpib_board_t *board); +void nec7210_board_online(struct nec7210_priv *priv, const gpib_board_t *board); +unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg, + unsigned int mask, unsigned int bits); +void nec7210_set_handshake_mode(gpib_board_t *board, struct nec7210_priv *priv, int mode); +void nec7210_release_rfd_holdoff(gpib_board_t *board, struct nec7210_priv *priv); +uint8_t nec7210_read_data_in(gpib_board_t *board, struct nec7210_priv *priv, int *end); + +// wrappers for io functions +uint8_t nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_ioport_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num); +uint8_t nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_iomem_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num); +uint8_t nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, uint8_t data, + unsigned int register_num); +uint8_t nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, uint8_t data, + unsigned int register_num); + +// interrupt service routine +irqreturn_t nec7210_interrupt(gpib_board_t *board, struct nec7210_priv *priv); +irqreturn_t nec7210_interrupt_have_status(gpib_board_t *board, + struct nec7210_priv *priv, int status1, int status2); + +#endif //_NEC7210_H diff --git a/drivers/staging/gpib/include/nec7210_registers.h b/drivers/staging/gpib/include/nec7210_registers.h new file mode 100644 index 0000000000000..888803dd97f9e --- /dev/null +++ b/drivers/staging/gpib/include/nec7210_registers.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _NEC7210_REGISTERS_H +#define _NEC7210_REGISTERS_H + +enum nec7210_chipset { + NEC7210, // The original + TNT4882, // NI + NAT4882, // NI + CB7210, // measurement computing + IOT7210, // iotech + IGPIB7210, // Ines + TNT5004, // NI (minor differences to TNT4882) +}; + +/* nec7210 register numbers (might need to be multiplied by + * a board-dependent offset to get actually io address offset) + */ +// write registers +enum nec7210_write_regs { + CDOR, // command/data out + IMR1, // interrupt mask 1 + IMR2, // interrupt mask 2 + SPMR, // serial poll mode + ADMR, // address mode + AUXMR, // auxiliary mode + ADR, // address + EOSR, // end-of-string + + // nec7210 has 8 registers + nec7210_num_registers = 8, +}; + +// read registers +enum nec7210_read_regs { + DIR, // data in + ISR1, // interrupt status 1 + ISR2, // interrupt status 2 + SPSR, // serial poll status + ADSR, // address status + CPTR, // command pass though + ADR0, // address 1 + ADR1, // address 2 +}; + +//bit definitions common to nec-7210 compatible registers + +// ISR1: interrupt status register 1 +enum isr1_bits { + HR_DI = (1 << 0), + HR_DO = (1 << 1), + HR_ERR = (1 << 2), + HR_DEC = (1 << 3), + HR_END = (1 << 4), + HR_DET = (1 << 5), + HR_APT = (1 << 6), + HR_CPT = (1 << 7), +}; + +// IMR1: interrupt mask register 1 +enum imr1_bits { + HR_DIIE = (1 << 0), + HR_DOIE = (1 << 1), + HR_ERRIE = (1 << 2), + HR_DECIE = (1 << 3), + HR_ENDIE = (1 << 4), + HR_DETIE = (1 << 5), + HR_APTIE = (1 << 6), + HR_CPTIE = (1 << 7), +}; + +// ISR2, interrupt status register 2 +enum isr2_bits { + HR_ADSC = (1 << 0), + HR_REMC = (1 << 1), + HR_LOKC = (1 << 2), + HR_CO = (1 << 3), + HR_REM = (1 << 4), + HR_LOK = (1 << 5), + HR_SRQI = (1 << 6), + HR_INT = (1 << 7), +}; + +// IMR2, interrupt mask register 2 +enum imr2_bits { + // all the bits in this register that enable interrupts + IMR2_ENABLE_INTR_MASK = 0x4f, + HR_ACIE = (1 << 0), + HR_REMIE = (1 << 1), + HR_LOKIE = (1 << 2), + HR_COIE = (1 << 3), + HR_DMAI = (1 << 4), + HR_DMAO = (1 << 5), + HR_SRQIE = (1 << 6), +}; + +// SPSR, serial poll status register +enum spsr_bits { + HR_PEND = (1 << 6), +}; + +// SPMR, serial poll mode register +enum spmr_bits { + HR_RSV = (1 << 6), +}; + +// ADSR, address status register +enum adsr_bits { + HR_MJMN = (1 << 0), + HR_TA = (1 << 1), + HR_LA = (1 << 2), + HR_TPAS = (1 << 3), + HR_LPAS = (1 << 4), + HR_SPMS = (1 << 5), + HR_NATN = (1 << 6), + HR_CIC = (1 << 7), +}; + +// ADMR, address mode register +enum admr_bits { + HR_ADM0 = (1 << 0), + HR_ADM1 = (1 << 1), + HR_TRM0 = (1 << 4), + HR_TRM1 = (1 << 5), + HR_TRM_EOIOE_TRIG = 0, + HR_TRM_CIC_TRIG = HR_TRM0, + HR_TRM_CIC_EOIOE = HR_TRM1, + HR_TRM_CIC_PE = HR_TRM0 | HR_TRM1, + HR_LON = (1 << 6), + HR_TON = (1 << 7), +}; + +// ADR, bits used in address0, address1 and address0/1 registers +enum adr_bits { + ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits */ + HR_DL = (1 << 5), + HR_DT = (1 << 6), + HR_ARS = (1 << 7), +}; + +// ADR1, address1 register +enum adr1_bits { + HR_EOI = (1 << 7), +}; + +// AUXMR, auxiliary mode register +enum auxmr_bits { + ICR = 0x20, + PPR = 0x60, + AUXRA = 0x80, + AUXRB = 0xa0, + AUXRE = 0xc0, +}; + +// auxra, auxiliary register A +enum auxra_bits { + HR_HANDSHAKE_MASK = 0x3, + HR_HLDA = 0x1, + HR_HLDE = 0x2, + HR_LCM = 0x3, /* auxra listen continuous */ + HR_REOS = 0x4, + HR_XEOS = 0x8, + HR_BIN = 0x10, +}; + +// auxrb, auxiliary register B +enum auxrb_bits { + HR_CPTE = (1 << 0), + HR_SPEOI = (1 << 1), + HR_TRI = (1 << 2), + HR_INV = (1 << 3), + HR_ISS = (1 << 4), +}; + +enum auxre_bits { + HR_DAC_HLD_DCAS = 0x1, /* perform DAC holdoff on receiving clear */ + HR_DAC_HLD_DTAS = 0x2, /* perform DAC holdoff on receiving trigger */ +}; + +// parallel poll register +enum ppr_bits { + HR_PPS = (1 << 3), + HR_PPU = (1 << 4), +}; + +/* 7210 Auxiliary Commands */ +enum aux_cmds { + AUX_PON = 0x0, /* Immediate Execute pon */ + AUX_CPPF = 0x1, /* Clear Parallel Poll Flag */ + AUX_CR = 0x2, /* Chip Reset */ + AUX_FH = 0x3, /* Finish Handshake */ + AUX_TRIG = 0x4, /* Trigger */ + AUX_RTL = 0x5, /* Return to local */ + AUX_SEOI = 0x6, /* Send EOI */ + AUX_NVAL = 0x7, /* Non-Valid Secondary Command or Address */ + AUX_SPPF = 0x9, /* Set Parallel Poll Flag */ + AUX_VAL = 0xf, /* Valid Secondary Command or Address */ + AUX_GTS = 0x10, /* Go To Standby */ + AUX_TCA = 0x11, /* Take Control Asynchronously */ + AUX_TCS = 0x12, /* Take Control Synchronously */ + AUX_LTN = 0x13, /* Listen */ + AUX_DSC = 0x14, /* Disable System Control */ + AUX_CIFC = 0x16, /* Clear IFC */ + AUX_CREN = 0x17, /* Clear REN */ + AUX_TCSE = 0x1a, /* Take Control Synchronously on End */ + AUX_LTNC = 0x1b, /* Listen in Continuous Mode */ + AUX_LUN = 0x1c, /* Local Unlisten */ + AUX_EPP = 0x1d, /* Execute Parallel Poll */ + AUX_SIFC = 0x1e, /* Set IFC */ + AUX_SREN = 0x1f, /* Set REN */ +}; + +#endif //_NEC7210_REGISTERS_H diff --git a/drivers/staging/gpib/include/plx9050.h b/drivers/staging/gpib/include/plx9050.h new file mode 100644 index 0000000000000..66c56335f5c0b --- /dev/null +++ b/drivers/staging/gpib/include/plx9050.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Header for plx9050 pci chip + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _PLX9050_GPIB_H +#define _PLX9050_GPIB_H + +// plx pci chip registers and bits +enum { + PLX9050_INTCSR_REG = 0x4c, + PLX9050_CNTRL_REG = 0x50 +}; + +enum plx9050_intcsr_bits { + PLX9050_LINTR1_EN_BIT = 0x1, + PLX9050_LINTR1_POLARITY_BIT = 0x2, + PLX9050_LINTR1_STATUS_BIT = 0x4, + PLX9050_LINTR2_EN_BIT = 0x8, + PLX9050_LINTR2_POLARITY_BIT = 0x10, + PLX9050_LINTR2_STATUS_BIT = 0x20, + PLX9050_PCI_INTR_EN_BIT = 0x40, + PLX9050_SOFT_INTR_BIT = 0x80, + PLX9050_LINTR1_SELECT_ENABLE_BIT = 0x100, //9052 extension + PLX9050_LINTR2_SELECT_ENABLE_BIT = 0x200, //9052 extension + PLX9050_LINTR1_EDGE_CLEAR_BIT = 0x400, //9052 extension + PLX9050_LINTR2_EDGE_CLEAR_BIT = 0x800, //9052 extension +}; + +enum plx9050_cntrl_bits { + PLX9050_WAITO_NOT_USER0_SELECT_BIT = 0x1, + PLX9050_USER0_OUTPUT_BIT = 0x2, + PLX9050_USER0_DATA_BIT = 0x4, + PLX9050_LLOCK_NOT_USER1_SELECT_BIT = 0x8, + PLX9050_USER1_OUTPUT_BIT = 0x10, + PLX9050_USER1_DATA_BIT = 0x20, + PLX9050_CS2_NOT_USER2_SELECT_BIT = 0x40, + PLX9050_USER2_OUTPUT_BIT = 0x80, + PLX9050_USER2_DATA_BIT = 0x100, + PLX9050_CS3_NOT_USER3_SELECT_BIT = 0x200, + PLX9050_USER3_OUTPUT_BIT = 0x400, + PLX9050_USER3_DATA_BIT = 0x800, + PLX9050_PCIBAR_ENABLE_MASK = 0x3000, + PLX9050_PCIBAR_MEMORY_AND_IO_ENABLE_BITS = 0x0, + PLX9050_PCIBAR_MEMORY_NO_IO_ENABLE_BITS = 0x1000, + PLX9050_PCIBAR_IO_NO_MEMORY_ENABLE_BITS = 0x2000, + PLX9050_PCIBAR_MEMORY_AND_IO_TOO_ENABLE_BITS = 0x3000, + PLX9050_PCI_READ_MODE_BIT = 0x4000, + PLX9050_PCI_READ_WITH_WRITE_FLUSH_MODE_BIT = 0x8000, + PLX9050_PCI_READ_NO_FLUSH_MODE_BIT = 0x10000, + PLX9050_PCI_READ_NO_WRITE_MODE_BIT = 0x20000, + PLX9050_PCI_WRITE_MODE_BIT = 0x40000, + PLX9050_PCI_RETRY_DELAY_MASK = 0x780000, + PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT = 0x800000, + PLX9050_EEPROM_CLOCK_BIT = 0x1000000, + PLX9050_EEPROM_CHIP_SELECT_BIT = 0x2000000, + PLX9050_WRITE_TO_EEPROM_BIT = 0x4000000, + PLX9050_READ_EEPROM_DATA_BIT = 0x8000000, + PLX9050_EEPROM_VALID_BIT = 0x10000000, + PLX9050_RELOAD_CONFIG_REGISTERS_BIT = 0x20000000, + PLX9050_PCI_SOFTWARE_RESET_BIT = 0x40000000, + PLX9050_MASK_REVISION_BIT = 0x80000000 +}; + +static inline unsigned int PLX9050_PCI_RETRY_DELAY_BITS(unsigned int clocks) +{ + return ((clocks / 8) << 19) & PLX9050_PCI_RETRY_DELAY_MASK; +} + +#endif // _PLX9050_GPIB_H diff --git a/drivers/staging/gpib/include/quancom_pci.h b/drivers/staging/gpib/include/quancom_pci.h new file mode 100644 index 0000000000000..cdaf0d056be9e --- /dev/null +++ b/drivers/staging/gpib/include/quancom_pci.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Quancom pci stuff + * copyright (C) 2005 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _QUANCOM_PCI_H +#define _QUANCOM_PCI_H + +/* quancom registers */ +enum quancom_regs { + QUANCOM_IRQ_CONTROL_STATUS_REG = 0xfc, +}; + +enum quancom_irq_control_status_bits { + QUANCOM_IRQ_ASSERTED_BIT = 0x1, /* readable */ + /* (any write to the register clears the interrupt)*/ + QUANCOM_IRQ_ENABLE_BIT = 0x4, /* writeable */ +}; + +#endif // _QUANCOM_PCI_H diff --git a/drivers/staging/gpib/include/tms9914.h b/drivers/staging/gpib/include/tms9914.h new file mode 100644 index 0000000000000..456b488212d2b --- /dev/null +++ b/drivers/staging/gpib/include/tms9914.h @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _TMS9914_H +#define _TMS9914_H + +#include +#include +#include "gpib_state_machines.h" +#include "gpib_types.h" + +enum tms9914_holdoff_mode { + TMS9914_HOLDOFF_NONE, + TMS9914_HOLDOFF_EOI, + TMS9914_HOLDOFF_ALL, +}; + +/* struct used to provide variables local to a tms9914 chip */ +struct tms9914_priv { + void *iobase; + unsigned int offset; // offset between successive tms9914 io addresses + unsigned int dma_channel; + // software copy of bits written to interrupt mask registers + u8 imr0_bits, imr1_bits; + // bits written to address mode register + u8 admr_bits; + u8 auxa_bits; // bits written to auxiliary register A + // used to keep track of board's state, bit definitions given below + unsigned long state; + u8 eos; // eos character + short eos_flags; + u8 spoll_status; + enum tms9914_holdoff_mode holdoff_mode; + unsigned int ppoll_line; + enum talker_function_state talker_state; + enum listener_function_state listener_state; + unsigned ppoll_sense : 1; + unsigned ppoll_enable : 1; + unsigned ppoll_configure_state : 1; + unsigned primary_listen_addressed : 1; + unsigned primary_talk_addressed : 1; + unsigned holdoff_on_end : 1; + unsigned holdoff_on_all : 1; + unsigned holdoff_active : 1; + // wrappers for outb, inb, readb, or writeb + u8 (*read_byte)(struct tms9914_priv *priv, unsigned int register_number); + void (*write_byte)(struct tms9914_priv *priv, u8 byte, unsigned int + register_number); +}; + +// slightly shorter way to access read_byte and write_byte +static inline u8 read_byte(struct tms9914_priv *priv, unsigned int register_number) +{ + return priv->read_byte(priv, register_number); +} + +static inline void write_byte(struct tms9914_priv *priv, u8 byte, unsigned int register_number) +{ + priv->write_byte(priv, byte, register_number); +} + +// struct tms9914_priv.state bit numbers +enum { + PIO_IN_PROGRESS_BN, // pio transfer in progress + DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress + DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress + READ_READY_BN, // board has data byte available to read + WRITE_READY_BN, // board is ready to send a data byte + COMMAND_READY_BN, // board is ready to send a command byte + RECEIVED_END_BN, // received END + BUS_ERROR_BN, // bus error + DEV_CLEAR_BN, // device clear received +}; + +// interface functions +int tms9914_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read); +int tms9914_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, int send_eoi, size_t *bytes_written); +int tms9914_command(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, size_t *bytes_written); +int tms9914_take_control(gpib_board_t *board, struct tms9914_priv *priv, int syncronous); +/* alternate version of tms9914_take_control which works around buggy tcs + * implementation. + */ +int tms9914_take_control_workaround(gpib_board_t *board, struct tms9914_priv *priv, + int syncronous); +int tms9914_go_to_standby(gpib_board_t *board, struct tms9914_priv *priv); +void tms9914_request_system_control(gpib_board_t *board, struct tms9914_priv *priv, + int request_control); +void tms9914_interface_clear(gpib_board_t *board, struct tms9914_priv *priv, int assert); +void tms9914_remote_enable(gpib_board_t *board, struct tms9914_priv *priv, int enable); +int tms9914_enable_eos(gpib_board_t *board, struct tms9914_priv *priv, uint8_t eos_bytes, + int compare_8_bits); +void tms9914_disable_eos(gpib_board_t *board, struct tms9914_priv *priv); +unsigned int tms9914_update_status(gpib_board_t *board, struct tms9914_priv *priv, + unsigned int clear_mask); +int tms9914_primary_address(gpib_board_t *board, + struct tms9914_priv *priv, unsigned int address); +int tms9914_secondary_address(gpib_board_t *board, struct tms9914_priv *priv, + unsigned int address, int enable); +int tms9914_parallel_poll(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *result); +void tms9914_parallel_poll_configure(gpib_board_t *board, + struct tms9914_priv *priv, uint8_t config); +void tms9914_parallel_poll_response(gpib_board_t *board, + struct tms9914_priv *priv, int ist); +void tms9914_serial_poll_response(gpib_board_t *board, struct tms9914_priv *priv, uint8_t status); +uint8_t tms9914_serial_poll_status(gpib_board_t *board, struct tms9914_priv *priv); +int tms9914_line_status(const gpib_board_t *board, struct tms9914_priv *priv); +unsigned int tms9914_t1_delay(gpib_board_t *board, struct tms9914_priv *priv, + unsigned int nano_sec); +void tms9914_return_to_local(const gpib_board_t *board, struct tms9914_priv *priv); + +// utility functions +void tms9914_board_reset(struct tms9914_priv *priv); +void tms9914_online(gpib_board_t *board, struct tms9914_priv *priv); +void tms9914_release_holdoff(struct tms9914_priv *priv); +void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode); + +// wrappers for io functions +uint8_t tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num); +void tms9914_ioport_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num); +uint8_t tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num); +void tms9914_iomem_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num); + +// interrupt service routine +irqreturn_t tms9914_interrupt(gpib_board_t *board, struct tms9914_priv *priv); +irqreturn_t tms9914_interrupt_have_status(gpib_board_t *board, struct tms9914_priv *priv, + int status1, int status2); + +// tms9914 has 8 registers +enum { + ms9914_num_registers = 8, +}; + +/* tms9914 register numbers (might need to be multiplied by + * a board-dependent offset to get actually io address offset) + */ +// write registers +enum { + IMR0 = 0, /* interrupt mask 0 */ + IMR1 = 1, /* interrupt mask 1 */ + AUXCR = 3, /* auxiliary command */ + ADR = 4, // address register + SPMR = 5, // serial poll mode register + PPR = 6, /* parallel poll */ + CDOR = 7, /* data out register */ +}; + +// read registers +enum { + ISR0 = 0, /* interrupt status 0 */ + ISR1 = 1, /* interrupt status 1 */ + ADSR = 2, /* address status */ + BSR = 3, /* bus status */ + CPTR = 6, /* command pass thru */ + DIR = 7, /* data in register */ +}; + +//bit definitions common to tms9914 compatible registers + +/* ISR0 - Register bits */ +enum isr0_bits { + HR_MAC = (1 << 0), /* My Address Change */ + HR_RLC = (1 << 1), /* Remote/Local change */ + HR_SPAS = (1 << 2), /* Serial Poll active State */ + HR_END = (1 << 3), /* END (EOI or EOS) */ + HR_BO = (1 << 4), /* Byte Out */ + HR_BI = (1 << 5), /* Byte In */ +}; + +/* IMR0 - Register bits */ +enum imr0_bits { + HR_MACIE = (1 << 0), /* */ + HR_RLCIE = (1 << 1), /* */ + HR_SPASIE = (1 << 2), /* */ + HR_ENDIE = (1 << 3), /* */ + HR_BOIE = (1 << 4), /* */ + HR_BIIE = (1 << 5), /* */ +}; + +/* ISR1 - Register bits */ +enum isr1_bits { + HR_IFC = (1 << 0), /* IFC asserted */ + HR_SRQ = (1 << 1), /* SRQ asserted */ + HR_MA = (1 << 2), /* My Address */ + HR_DCAS = (1 << 3), /* Device Clear active State */ + HR_APT = (1 << 4), /* Address pass Through */ + HR_UNC = (1 << 5), /* Unrecognized Command */ + HR_ERR = (1 << 6), /* Data Transmission Error */ + HR_GET = (1 << 7), /* Group execute Trigger */ +}; + +/* IMR1 - Register bits */ +enum imr1_bits { + HR_IFCIE = (1 << 0), /* */ + HR_SRQIE = (1 << 1), /* */ + HR_MAIE = (1 << 2), /* */ + HR_DCASIE = (1 << 3), /* */ + HR_APTIE = (1 << 4), /* */ + HR_UNCIE = (1 << 5), /* */ + HR_ERRIE = (1 << 6), /* */ + HR_GETIE = (1 << 7), /* */ +}; + +/* ADSR - Register bits */ +enum adsr_bits { + HR_ULPA = (1 << 0), /* Store last address LSB */ + HR_TA = (1 << 1), /* Talker Adressed */ + HR_LA = (1 << 2), /* Listener adressed */ + HR_TPAS = (1 << 3), /* talker primary address state */ + HR_LPAS = (1 << 4), /* listener " */ + HR_ATN = (1 << 5), /* ATN active */ + HR_LLO = (1 << 6), /* LLO active */ + HR_REM = (1 << 7), /* REM active */ +}; + +/* ADR - Register bits */ +enum adr_bits { + ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits for ADR */ + HR_DAT = (1 << 5), /* disable talker */ + HR_DAL = (1 << 6), /* disable listener */ + HR_EDPA = (1 << 7), /* enable dual primary addressing */ +}; + +enum bus_status_bits { + BSR_REN_BIT = 0x1, + BSR_IFC_BIT = 0x2, + BSR_SRQ_BIT = 0x4, + BSR_EOI_BIT = 0x8, + BSR_NRFD_BIT = 0x10, + BSR_NDAC_BIT = 0x20, + BSR_DAV_BIT = 0x40, + BSR_ATN_BIT = 0x80, +}; + +/*---------------------------------------------------------*/ +/* TMS 9914 Auxiliary Commands */ +/*---------------------------------------------------------*/ + +enum aux_cmd_bits { + AUX_CS = 0x80, /* set bit instead of clearing it, used with commands marked 'd' below */ + AUX_CHIP_RESET = 0x0, /* d Chip reset */ + AUX_INVAL = 0x1, // release dac holdoff, invalid command byte + AUX_VAL = (AUX_INVAL | AUX_CS), // release dac holdoff, valid command byte + AUX_RHDF = 0x2, /* X Release RFD holdoff */ + AUX_HLDA = 0x3, /* d holdoff on all data */ + AUX_HLDE = 0x4, /* d holdoff on EOI only */ + AUX_NBAF = 0x5, /* X Set new byte available false */ + AUX_FGET = 0x6, /* d force GET */ + AUX_RTL = 0x7, /* d return to local */ + AUX_SEOI = 0x8, /* X send EOI with next byte */ + AUX_LON = 0x9, /* d Listen only */ + AUX_TON = 0xa, /* d Talk only */ + AUX_GTS = 0xb, /* X goto standby */ + AUX_TCA = 0xc, /* X take control asynchronously */ + AUX_TCS = 0xd, /* X take " synchronously */ + AUX_RPP = 0xe, /* d Request parallel poll */ + AUX_SIC = 0xf, /* d send interface clear */ + AUX_SRE = 0x10, /* d send remote enable */ + AUX_RQC = 0x11, /* X request control */ + AUX_RLC = 0x12, /* X release control */ + AUX_DAI = 0x13, /* d disable all interrupts */ + AUX_PTS = 0x14, /* X pass through next secondary */ + AUX_STDL = 0x15, /* d short T1 delay */ + AUX_SHDW = 0x16, /* d shadow handshake */ + AUX_VSTDL = 0x17, /* d very short T1 delay (smj9914 extension) */ + AUX_RSV2 = 0x18, /* d request service bit 2 (smj9914 extension) */ +}; + +#endif //_TMS9914_H diff --git a/drivers/staging/gpib/include/tnt4882_registers.h b/drivers/staging/gpib/include/tnt4882_registers.h new file mode 100644 index 0000000000000..1b1441cd03d50 --- /dev/null +++ b/drivers/staging/gpib/include/tnt4882_registers.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002, 2004 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _TNT4882_REGISTERS_H +#define _TNT4882_REGISTERS_H + +// tnt4882 register offsets +enum { + ACCWR = 0x5, + // offset of auxiliary command register in 9914 mode + AUXCR = 0x6, + INTRT = 0x7, + // register number for auxiliary command register when swap bit is set (9914 mode) + SWAPPED_AUXCR = 0xa, + HSSEL = 0xd, // handshake select register + CNT2 = 0x9, + CNT3 = 0xb, + CFG = 0x10, + SASR = 0x1b, + IMR0 = 0x1d, + IMR3 = 0x12, + CNT0 = 0x14, + CNT1 = 0x16, + KEYREG = 0x17, // key control register (7210 mode only) + CSR = KEYREG, + FIFOB = 0x18, + FIFOA = 0x19, + CCR = 0x1a, // carry cycle register + CMDR = 0x1c, // command register + TIMER = 0x1e, // timer register + + STS1 = 0x10, /* T488 Status Register 1 */ + STS2 = 0x1c, /* T488 Status Register 2 */ + ISR0 = IMR0, + ISR3 = 0x1a, /* T488 Interrupt Status Register 3 */ + BCR = 0x1f, /* bus control/status register */ + BSR = BCR, +}; + +enum { + tnt_pagein_offset = 0x11, +}; + +/*============================================================*/ + +/* TURBO-488 registers bit definitions */ + +enum bus_control_status_bits { + BCSR_REN_BIT = 0x1, + BCSR_IFC_BIT = 0x2, + BCSR_SRQ_BIT = 0x4, + BCSR_EOI_BIT = 0x8, + BCSR_NRFD_BIT = 0x10, + BCSR_NDAC_BIT = 0x20, + BCSR_DAV_BIT = 0x40, + BCSR_ATN_BIT = 0x80, +}; + +/* CFG -- Configuration Register (write only) */ +enum cfg_bits { + TNT_COMMAND = 0x80, /* bytes are command bytes instead of data bytes + * (tnt4882 one-chip and newer only?) + */ + TNT_TLCHE = (1 << 6), /* halt transfer on imr0, imr1, or imr2 interrupt */ + TNT_IN = (1 << 5), /* transfer is GPIB read */ + TNT_A_B = (1 << 4), /* order to use fifos 1=fifo A first(big endian), + * 0=fifo b first(little endian) + */ + TNT_CCEN = (1 << 3), /* enable carry cycle */ + TNT_TMOE = (1 << 2), /* enable CPU bus time limit */ + TNT_TIM_BYTN = (1 << 1), /* tmot reg is: 1=125ns clocks, 0=num bytes */ + TNT_B_16BIT = (1 << 0), /* 1=FIFO is 16-bit register, 0=8-bit */ +}; + +/* CMDR -- Command Register */ +enum cmdr_bits { + CLRSC = 0x2, /* clear the system controller bit */ + SETSC = 0x3, /* set the system controller bit */ + GO = 0x4, /* start fifos */ + STOP = 0x8, /* stop fifos */ + RESET_FIFO = 0x10, /* reset the FIFOs */ + SOFT_RESET = 0x22, /* issue a software reset */ + HARD_RESET = 0x40 /* 500x only? */ +}; + +/* HSSEL -- handshake select register (write only) */ +enum hssel_bits { + TNT_ONE_CHIP_BIT = 0x1, + NODMA = 0x10, + TNT_GO2SIDS_BIT = 0x20, +}; + +/* IMR0 -- Interrupt Mode Register 0 */ +enum imr0_bits { + TNT_SYNCIE_BIT = 0x1, /* handshake sync */ + TNT_TOIE_BIT = 0x2, /* timeout */ + TNT_ATNIE_BIT = 0x4, /* ATN interrupt */ + TNT_IFCIE_BIT = 0x8, /* interface clear interrupt */ + TNT_BTO_BIT = 0x10, /* byte timeout */ + TNT_NLEN_BIT = 0x20, /* treat new line as EOS char */ + TNT_STBOIE_BIT = 0x40, /* status byte out */ + TNT_IMR0_ALWAYS_BITS = 0x80, /* always set this bit on write */ +}; + +/* ISR0 -- Interrupt Status Register 0 */ +enum isr0_bits { + TNT_SYNC_BIT = 0x1, /* handshake sync */ + TNT_TO_BIT = 0x2, /* timeout */ + TNT_ATNI_BIT = 0x4, /* ATN interrupt */ + TNT_IFCI_BIT = 0x8, /* interface clear interrupt */ + TNT_EOS_BIT = 0x10, /* end of string */ + TNT_NL_BIT = 0x20, /* new line receive */ + TNT_STBO_BIT = 0x40, /* status byte out */ + TNT_NBA_BIT = 0x80, /* new byte available */ +}; + +/* ISR3 -- Interrupt Status Register 3 (read only) */ +enum isr3_bits { + HR_DONE = (1 << 0), /* transfer done */ + HR_TLCI = (1 << 1), /* isr0, isr1, or isr2 interrupt asserted */ + HR_NEF = (1 << 2), /* NOT empty fifo */ + HR_NFF = (1 << 3), /* NOT full fifo */ + HR_STOP = (1 << 4), /* fifo empty or STOP command issued */ + HR_SRQI_CIC = (1 << 5), /* SRQ asserted and we are CIC (500x only?)*/ + HR_INTR = (1 << 7), /* isr3 interrupt active */ +}; + +enum keyreg_bits { + MSTD = 0x20, // enable 350ns T1 delay +}; + +/* STS1 -- Status Register 1 (read only) */ +enum sts1_bits { + S_DONE = 0x80, /* DMA done */ + S_SC = 0x40, /* is system controller */ + S_IN = 0x20, /* DMA in (to memory) */ + S_DRQ = 0x10, /* DRQ line (for diagnostics) */ + S_STOP = 0x08, /* DMA stopped */ + S_NDAV = 0x04, /* inverse of DAV */ + S_HALT = 0x02, /* status of transfer machine */ + S_GSYNC = 0x01, /* indicates if GPIB is in sync w I/O */ +}; + +/* STS2 -- Status Register 2 */ +enum sts2_bits { + AFFN = (1 << 3), /* "A full FIFO NOT" (0=FIFO full) */ + AEFN = (1 << 2), /* "A empty FIFO NOT" (0=FIFO empty) */ + BFFN = (1 << 1), /* "B full FIFO NOT" (0=FIFO full) */ + BEFN = (1 << 0), /* "B empty FIFO NOT" (0=FIFO empty) */ +}; + +// Auxiliary commands +enum tnt4882_aux_cmds { + AUX_9914 = 0x15, // switch to 9914 mode + AUX_REQT = 0x18, + AUX_REQF = 0x19, + AUX_PAGEIN = 0x50, /* page in alternate registers */ + AUX_HLDI = 0x51, // rfd holdoff immediately + AUX_CLEAR_END = 0x55, + AUX_7210 = 0x99, // switch to 7210 mode +}; + +enum tnt4882_aux_regs { + AUXRG = 0x40, + AUXRI = 0xe0, +}; + +enum auxg_bits { + /* no talking when no listeners bit (prevents bus errors when data written at wrong time) */ + NTNL_BIT = 0x8, + RPP2_BIT = 0x4, /* set/clear local rpp message */ + CHES_BIT = 0x1, /*clear holdoff on end select bit*/ +}; + +enum auxi_bits { + SISB = 0x1, // static interrupt bits (don't clear isr1, isr2 on read) + PP2 = 0x4, // ignore remote parallel poll configuration + USTD = 0x8, // ultra short (1100 nanosec) T1 delay +}; + +enum sasr_bits { + ACRDY_BIT = 0x4, /* acceptor ready state */ + ADHS_BIT = 0x8, /* acceptor data holdoff state */ + ANHS2_BIT = 0x10, /* acceptor not ready holdoff immediately state */ + ANHS1_BIT = 0x20, /* acceptor not ready holdoff state */ + AEHS_BIT = 0x40, /* acceptor end holdoff state */ +}; + +#endif // _TNT4882_REGISTERS_H diff --git a/drivers/staging/gpib/ines/Makefile b/drivers/staging/gpib/ines/Makefile new file mode 100644 index 0000000000000..cdcaa59a4e39c --- /dev/null +++ b/drivers/staging/gpib/ines/Makefile @@ -0,0 +1,4 @@ +ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA +obj-m += ines_gpib.o + + diff --git a/drivers/staging/gpib/ines/ines.h b/drivers/staging/gpib/ines/ines.h new file mode 100644 index 0000000000000..7e8302619998f --- /dev/null +++ b/drivers/staging/gpib/ines/ines.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Header for ines GPIB boards + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _INES_GPIB_H +#define _INES_GPIB_H + +#include "nec7210.h" +#include "gpibP.h" +#include "plx9050.h" +#include "amcc5920.h" +#include "quancom_pci.h" +#include + +enum ines_pci_chip { + PCI_CHIP_NONE, + PCI_CHIP_PLX9050, + PCI_CHIP_AMCC5920, + PCI_CHIP_QUANCOM, + PCI_CHIP_QUICKLOGIC5030, +}; + +struct ines_priv { + struct nec7210_priv nec7210_priv; + struct pci_dev *pci_device; + // base address for plx9052 pci chip + unsigned long plx_iobase; + // base address for amcc5920 pci chip + unsigned long amcc_iobase; + unsigned int irq; + enum ines_pci_chip pci_chip_type; + u8 extend_mode_bits; +}; + +// interfaces +extern gpib_interface_t ines_pci_interface; +extern gpib_interface_t ines_pci_accel_interface; +extern gpib_interface_t ines_pcmcia_interface; +extern gpib_interface_t ines_pcmcia_accel_interface; +extern gpib_interface_t ines_pcmcia_unaccel_interface; + +// interface functions +int ines_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read); +int ines_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +int ines_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +int ines_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +int ines_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written); +int ines_take_control(gpib_board_t *board, int synchronous); +int ines_go_to_standby(gpib_board_t *board); +void ines_request_system_control(gpib_board_t *board, int request_control); +void ines_interface_clear(gpib_board_t *board, int assert); +void ines_remote_enable(gpib_board_t *board, int enable); +int ines_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits); +void ines_disable_eos(gpib_board_t *board); +unsigned int ines_update_status(gpib_board_t *board, unsigned int clear_mask); +int ines_primary_address(gpib_board_t *board, unsigned int address); +int ines_secondary_address(gpib_board_t *board, unsigned int address, int enable); +int ines_parallel_poll(gpib_board_t *board, uint8_t *result); +void ines_parallel_poll_configure(gpib_board_t *board, uint8_t config); +void ines_parallel_poll_response(gpib_board_t *board, int ist); +void ines_serial_poll_response(gpib_board_t *board, uint8_t status); +uint8_t ines_serial_poll_status(gpib_board_t *board); +int ines_line_status(const gpib_board_t *board); +unsigned int ines_t1_delay(gpib_board_t *board, unsigned int nano_sec); +void ines_return_to_local(gpib_board_t *board); + +// interrupt service routines +irqreturn_t ines_pci_interrupt(int irq, void *arg); +irqreturn_t ines_interrupt(gpib_board_t *board); + +// utility functions +void ines_free_private(gpib_board_t *board); +int ines_generic_attach(gpib_board_t *board); +void ines_online(struct ines_priv *priv, const gpib_board_t *board, int use_accel); +void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count); + +/* inb/outb wrappers */ +static inline unsigned int ines_inb(struct ines_priv *priv, unsigned int register_number) +{ + return inb((unsigned long)(priv->nec7210_priv.iobase) + + register_number * priv->nec7210_priv.offset); +} + +static inline void ines_outb(struct ines_priv *priv, unsigned int value, + unsigned int register_number) +{ + outb(value, (unsigned long)(priv->nec7210_priv.iobase) + + register_number * priv->nec7210_priv.offset); +} + +// pcmcia init/cleanup + +int ines_pcmcia_init_module(void); +void ines_pcmcia_cleanup_module(void); + +enum ines_regs { + // read + FIFO_STATUS = 0x8, + ISR3 = 0x9, + ISR4 = 0xa, + IN_FIFO_COUNT = 0x10, + OUT_FIFO_COUNT = 0x11, + EXTEND_STATUS = 0xf, + + // write + XDMA_CONTROL = 0x8, + IMR3 = ISR3, + IMR4 = ISR4, + IN_FIFO_WATERMARK = IN_FIFO_COUNT, + OUT_FIFO_WATERMARK = OUT_FIFO_COUNT, + EXTEND_MODE = 0xf, + + // read-write + XFER_COUNT_LOWER = 0xb, + XFER_COUNT_UPPER = 0xc, + BUS_CONTROL_MONITOR = 0x13, +}; + +enum isr3_imr3_bits { + HW_TIMEOUT_BIT = 0x1, + XFER_COUNT_BIT = 0x2, + CMD_RECEIVED_BIT = 0x4, + TCT_RECEIVED_BIT = 0x8, + IFC_ACTIVE_BIT = 0x10, + ATN_ACTIVE_BIT = 0x20, + FIFO_ERROR_BIT = 0x40, +}; + +enum isr4_imr4_bits { + IN_FIFO_WATERMARK_BIT = 0x1, + OUT_FIFO_WATERMARK_BIT = 0x2, + IN_FIFO_FULL_BIT = 0x4, + OUT_FIFO_EMPTY_BIT = 0x8, + IN_FIFO_READY_BIT = 0x10, + OUT_FIFO_READY_BIT = 0x20, + IN_FIFO_EXIT_WATERMARK_BIT = 0x40, + OUT_FIFO_EXIT_WATERMARK_BIT = 0x80, +}; + +enum extend_mode_bits { + TR3_TRIG_ENABLE_BIT = 0x1, // enable generation of trigger pulse T/R3 pin + // clear message available status bit when chip writes byte with EOI true + MAV_ENABLE_BIT = 0x2, + EOS1_ENABLE_BIT = 0x4, // enable eos register 1 + EOS2_ENABLE_BIT = 0x8, // enable eos register 2 + EOIDIS_BIT = 0x10, // disable EOI interrupt when doing rfd holdoff on end? + XFER_COUNTER_ENABLE_BIT = 0x20, + XFER_COUNTER_OUTPUT_BIT = 0x40, // use counter for output, clear for input + // when xfer counter hits 0, assert EOI on write or RFD holdoff on read + LAST_BYTE_HANDLING_BIT = 0x80, +}; + +enum extend_status_bits { + OUTPUT_MESSAGE_IN_PROGRESS_BIT = 0x1, + SCSEL_BIT = 0x2, // statue of SCSEL pin + LISTEN_DISABLED = 0x4, + IN_FIFO_EMPTY_BIT = 0x8, + OUT_FIFO_FULL_BIT = 0x10, +}; + +// ines adds fifo enable bits to address mode register +enum ines_admr_bits { + IN_FIFO_ENABLE_BIT = 0x8, + OUT_FIFO_ENABLE_BIT = 0x4, +}; + +enum xdma_control_bits { + DMA_OUTPUT_BIT = 0x1, // use dma for output, clear for input + ENABLE_SYNC_DMA_BIT = 0x2, + DMA_ACCESS_EVERY_CYCLE = 0x4,// dma accesses fifo every cycle, clear for every other cycle + DMA_16BIT = 0x8, // clear for 8 bit transfers +}; + +enum bus_control_monitor_bits { + BCM_DAV_BIT = 0x1, + BCM_NRFD_BIT = 0x2, + BCM_NDAC_BIT = 0x4, + BCM_IFC_BIT = 0x8, + BCM_ATN_BIT = 0x10, + BCM_SRQ_BIT = 0x20, + BCM_REN_BIT = 0x40, + BCM_EOI_BIT = 0x80, +}; + +enum ines_aux_reg_bits { + INES_AUXD = 0x40, +}; + +enum ines_aux_cmds { + INES_RFD_HLD_IMMEDIATE = 0x4, + INES_AUX_CLR_OUT_FIFO = 0x5, + INES_AUX_CLR_IN_FIFO = 0x6, + INES_AUX_XMODE = 0xa, +}; + +enum ines_auxd_bits { + INES_FOLLOWING_T1_MASK = 0x3, + INES_FOLLOWING_T1_500ns = 0x0, + INES_FOLLOWING_T1_350ns = 0x1, + INES_FOLLOWING_T1_250ns = 0x2, + INES_INITIAL_TI_MASK = 0xc, + INES_INITIAL_T1_2000ns = 0x0, + INES_INITIAL_T1_1100ns = 0x4, + INES_INITIAL_T1_700ns = 0x8, + INES_T6_2us = 0x0, + INES_T6_50us = 0x10, +}; + +#endif // _INES_GPIB_H diff --git a/drivers/staging/gpib/ines/ines_gpib.c b/drivers/staging/gpib/ines/ines_gpib.c new file mode 100644 index 0000000000000..9d8387c3bf019 --- /dev/null +++ b/drivers/staging/gpib/ines/ines_gpib.c @@ -0,0 +1,1464 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 1999 Axel Dziemba (axel.dziemba@ines.de) + * (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "ines.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpib_pci_ids.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for Ines iGPIB 72010"); + +int ines_line_status(const gpib_board_t *board) +{ + int status = ValidALL; + int bcm_bits; + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + bcm_bits = ines_inb(ines_priv, BUS_CONTROL_MONITOR); + + if (bcm_bits & BCM_REN_BIT) + status |= BusREN; + if (bcm_bits & BCM_IFC_BIT) + status |= BusIFC; + if (bcm_bits & BCM_SRQ_BIT) + status |= BusSRQ; + if (bcm_bits & BCM_EOI_BIT) + status |= BusEOI; + if (bcm_bits & BCM_NRFD_BIT) + status |= BusNRFD; + if (bcm_bits & BCM_NDAC_BIT) + status |= BusNDAC; + if (bcm_bits & BCM_DAV_BIT) + status |= BusDAV; + if (bcm_bits & BCM_ATN_BIT) + status |= BusATN; + + return status; +} + +void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count) +{ + if (count > 0xffff) { + pr_err("ines: bug! tried to set xfer counter > 0xffff\n"); + return; + } + ines_outb(priv, (count >> 8) & 0xff, XFER_COUNT_UPPER); + ines_outb(priv, count & 0xff, XFER_COUNT_LOWER); +} + +unsigned int ines_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 250) { + write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_250ns | + INES_INITIAL_T1_2000ns, AUXMR); + retval = 250; + } else if (nano_sec <= 350) { + write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_350ns | + INES_INITIAL_T1_2000ns, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_500ns | + INES_INITIAL_T1_2000ns, AUXMR); + retval = 500; + } + + return retval; +} + +static inline unsigned short num_in_fifo_bytes(struct ines_priv *ines_priv) +{ + return ines_inb(ines_priv, IN_FIFO_COUNT); +} + +static ssize_t pio_read(gpib_board_t *board, struct ines_priv *ines_priv, uint8_t *buffer, + size_t length, size_t *nbytes) +{ + ssize_t retval = 0; + unsigned int num_fifo_bytes, i; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + + *nbytes = 0; + while (*nbytes < length) { + if (wait_event_interruptible(board->wait, + num_in_fifo_bytes(ines_priv) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_warn("gpib: pio read wait interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + + num_fifo_bytes = num_in_fifo_bytes(ines_priv); + if (num_fifo_bytes + *nbytes > length) { + pr_warn("ines: counter allowed %li extra byte(s)\n", + (long)(num_fifo_bytes - (length - *nbytes))); + num_fifo_bytes = length - *nbytes; + } + for (i = 0; i < num_fifo_bytes; i++) + buffer[(*nbytes)++] = read_byte(nec_priv, DIR); + if (test_bit(RECEIVED_END_BN, &nec_priv->state) && + num_in_fifo_bytes(ines_priv) == 0) + break; + if (need_resched()) + schedule(); + } + /* make sure RECEIVED_END is in sync */ + ines_interrupt(board); + return retval; +} + +int ines_accel_read(gpib_board_t *board, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + int counter_setting; + + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + + //clear in fifo + nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, 0); + nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, IN_FIFO_ENABLE_BIT); + + ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT; + ines_priv->extend_mode_bits &= ~XFER_COUNTER_OUTPUT_BIT & ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + counter_setting = length - num_in_fifo_bytes(ines_priv); + if (counter_setting > 0) { + ines_set_xfer_counter(ines_priv, length); + ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + // holdoff on END + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + /* release rfd holdoff */ + write_byte(nec_priv, AUX_FH, AUXMR); + } + + retval = pio_read(board, ines_priv, buffer, length, bytes_read); + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + if (retval < 0) { + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + return retval; + } + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + + return retval; +} + +static const int out_fifo_size = 0xff; + +static inline unsigned short num_out_fifo_bytes(struct ines_priv *ines_priv) +{ + return ines_inb(ines_priv, OUT_FIFO_COUNT); +} + +static int ines_write_wait(gpib_board_t *board, struct ines_priv *ines_priv, + unsigned int fifo_threshold) +{ + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + + // wait until byte is ready to be sent + if (wait_event_interruptible(board->wait, + num_out_fifo_bytes(ines_priv) < fifo_threshold || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(BUS_ERROR_BN, &nec_priv->state)) + return -EIO; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + + return 0; +} + +int ines_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + size_t count = 0; + ssize_t retval = 0; + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + unsigned int num_bytes, i; + + *bytes_written = 0; + //clear out fifo + nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, 0); + nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, OUT_FIFO_ENABLE_BIT); + + ines_priv->extend_mode_bits |= XFER_COUNTER_OUTPUT_BIT; + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_priv->extend_mode_bits &= ~LAST_BYTE_HANDLING_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + ines_set_xfer_counter(ines_priv, length); + if (send_eoi) + ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT; + ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + while (count < length) { + retval = ines_write_wait(board, ines_priv, out_fifo_size); + if (retval < 0) + break; + + num_bytes = out_fifo_size - num_out_fifo_bytes(ines_priv); + if (num_bytes + count > length) + num_bytes = length - count; + for (i = 0; i < num_bytes; i++) + write_byte(nec_priv, buffer[count++], CDOR); + } + if (retval < 0) { + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + *bytes_written = length - num_out_fifo_bytes(ines_priv); + return retval; + } + // wait last byte has been sent + retval = ines_write_wait(board, ines_priv, 1); + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + *bytes_written = length - num_out_fifo_bytes(ines_priv); + + return retval; +} + +irqreturn_t ines_pci_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct ines_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + if (priv->pci_chip_type == PCI_CHIP_QUANCOM) { + if ((inb((unsigned long)nec_priv->iobase + + QUANCOM_IRQ_CONTROL_STATUS_REG) & + QUANCOM_IRQ_ASSERTED_BIT)) + outb(QUANCOM_IRQ_ENABLE_BIT, (unsigned long)(nec_priv->iobase) + + QUANCOM_IRQ_CONTROL_STATUS_REG); + } + + return ines_interrupt(board); +} + +irqreturn_t ines_interrupt(gpib_board_t *board) +{ + struct ines_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + unsigned int isr3_bits, isr4_bits; + unsigned long flags; + int wake = 0; + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_interrupt(board, nec_priv); + isr3_bits = ines_inb(priv, ISR3); + isr4_bits = ines_inb(priv, ISR4); + if (isr3_bits & IFC_ACTIVE_BIT) { + push_gpib_event(board, EventIFC); + wake++; + } + if (isr3_bits & FIFO_ERROR_BIT) + pr_err("ines gpib: fifo error\n"); + if (isr3_bits & XFER_COUNT_BIT) + wake++; + + if (isr4_bits & (IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | OUT_FIFO_WATERMARK_BIT | + OUT_FIFO_EMPTY_BIT)) + wake++; + + if (wake) + wake_up_interruptible(&board->wait); + spin_unlock_irqrestore(&board->spinlock, flags); + return IRQ_HANDLED; +} + +static int ines_pci_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ines_pci_accel_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ines_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void ines_pci_detach(gpib_board_t *board); +static void ines_isa_detach(gpib_board_t *board); + +enum ines_pci_vendor_ids { + PCI_VENDOR_ID_INES_QUICKLOGIC = 0x16da +}; + +enum ines_pci_device_ids { + PCI_DEVICE_ID_INES_GPIB_AMCC = 0x8507, + PCI_DEVICE_ID_INES_GPIB_QL5030 = 0x11, +}; + +enum ines_pci_subdevice_ids { + PCI_SUBDEVICE_ID_INES_GPIB = 0x1072 +}; + +static struct pci_device_id ines_pci_table[] = { + {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, + {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC, + PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, + {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, + PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0}, + {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)}, + {0} +}; +MODULE_DEVICE_TABLE(pci, ines_pci_table); + +struct ines_pci_id { + unsigned int vendor_id; + unsigned int device_id; + int subsystem_vendor_id; + int subsystem_device_id; + unsigned int gpib_region; + unsigned int io_offset; + enum ines_pci_chip pci_chip_type; +}; + +struct ines_pci_id pci_ids[] = { + {vendor_id: PCI_VENDOR_ID_PLX, + device_id : PCI_DEVICE_ID_PLX_9050, + subsystem_vendor_id : PCI_VENDOR_ID_PLX, + subsystem_device_id : PCI_SUBDEVICE_ID_INES_GPIB, + gpib_region : 2, + io_offset : 1, + pci_chip_type : PCI_CHIP_PLX9050, + }, + {vendor_id: PCI_VENDOR_ID_AMCC, + device_id : PCI_DEVICE_ID_INES_GPIB_AMCC, + subsystem_vendor_id : PCI_VENDOR_ID_AMCC, + subsystem_device_id : PCI_SUBDEVICE_ID_INES_GPIB, + gpib_region : 1, + io_offset : 1, + pci_chip_type : PCI_CHIP_AMCC5920, + }, + {vendor_id: PCI_VENDOR_ID_INES_QUICKLOGIC, + device_id : PCI_DEVICE_ID_INES_GPIB_QL5030, + subsystem_vendor_id : PCI_VENDOR_ID_INES_QUICKLOGIC, + subsystem_device_id : PCI_DEVICE_ID_INES_GPIB_QL5030, + gpib_region : 1, + io_offset : 1, + pci_chip_type : PCI_CHIP_QUICKLOGIC5030, + }, + {vendor_id: PCI_VENDOR_ID_QUANCOM, + device_id : PCI_DEVICE_ID_QUANCOM_GPIB, + subsystem_vendor_id : -1, + subsystem_device_id : -1, + gpib_region : 0, + io_offset : 4, + pci_chip_type : PCI_CHIP_QUANCOM, + }, +}; + +static const int num_pci_chips = ARRAY_SIZE(pci_ids); + +// wrappers for interface functions +int ines_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read) +{ + struct ines_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + ssize_t retval; + int dummy; + + retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); + if (retval < 0) { + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + nec7210_read_data_in(board, nec_priv, &dummy); + } + return retval; +} + +int ines_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +int ines_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +int ines_take_control(gpib_board_t *board, int synchronous) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +int ines_go_to_standby(gpib_board_t *board) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +void ines_request_system_control(gpib_board_t *board, int request_control) +{ + struct ines_priv *priv = board->private_data; + + nec7210_request_system_control(board, &priv->nec7210_priv, request_control); +} + +void ines_interface_clear(gpib_board_t *board, int assert) +{ + struct ines_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +void ines_remote_enable(gpib_board_t *board, int enable) +{ + struct ines_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +int ines_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +void ines_disable_eos(gpib_board_t *board) +{ + struct ines_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +unsigned int ines_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +int ines_primary_address(gpib_board_t *board, unsigned int address) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +int ines_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +int ines_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +void ines_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + struct ines_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); +} + +void ines_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct ines_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +void ines_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct ines_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +uint8_t ines_serial_poll_status(gpib_board_t *board) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +void ines_return_to_local(gpib_board_t *board) +{ + struct ines_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +gpib_interface_t ines_pci_unaccel_interface = { +name: "ines_pci_unaccel", +attach : ines_pci_attach, +detach : ines_pci_detach, +read : ines_read, +write : ines_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +gpib_interface_t ines_pci_interface = { +name: "ines_pci", +attach : ines_pci_accel_attach, +detach : ines_pci_detach, +read : ines_accel_read, +write : ines_accel_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +gpib_interface_t ines_pci_accel_interface = { +name: "ines_pci_accel", +attach : ines_pci_accel_attach, +detach : ines_pci_detach, +read : ines_accel_read, +write : ines_accel_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +gpib_interface_t ines_isa_interface = { +name: "ines_isa", +attach : ines_isa_attach, +detach : ines_isa_detach, +read : ines_accel_read, +write : ines_accel_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +static int ines_allocate_private(gpib_board_t *board) +{ + struct ines_priv *priv; + + board->private_data = kmalloc(sizeof(struct ines_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct ines_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +void ines_free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +int ines_generic_attach(gpib_board_t *board) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + + if (ines_allocate_private(board)) + return -ENOMEM; + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + nec_priv->read_byte = nec7210_ioport_read_byte; + nec_priv->write_byte = nec7210_ioport_write_byte; + nec_priv->offset = 1; + nec_priv->type = IGPIB7210; + ines_priv->pci_chip_type = PCI_CHIP_NONE; + + return 0; +} + +void ines_online(struct ines_priv *ines_priv, const gpib_board_t *board, int use_accel) +{ + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + + /* ines doesn't seem to use internal count register */ + write_byte(nec_priv, ICR | 0, AUXMR); + + write_byte(nec_priv, INES_AUX_XMODE, AUXMR); + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + write_byte(nec_priv, INES_AUXD | 0, AUXMR); + ines_outb(ines_priv, 0, XDMA_CONTROL); + ines_priv->extend_mode_bits = 0; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + if (use_accel) { + ines_outb(ines_priv, 0x80, OUT_FIFO_WATERMARK); + ines_outb(ines_priv, 0x80, IN_FIFO_WATERMARK); + ines_outb(ines_priv, IFC_ACTIVE_BIT | ATN_ACTIVE_BIT | + FIFO_ERROR_BIT | XFER_COUNT_BIT, IMR3); + ines_outb(ines_priv, IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | + OUT_FIFO_WATERMARK_BIT | OUT_FIFO_EMPTY_BIT, IMR4); + } else { + nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT | OUT_FIFO_ENABLE_BIT, 0); + ines_outb(ines_priv, IFC_ACTIVE_BIT | FIFO_ERROR_BIT, IMR3); + ines_outb(ines_priv, 0, IMR4); + } + + nec7210_board_online(nec_priv, board); + if (use_accel) + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, 0); +} + +static int ines_common_pci_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + struct ines_pci_id found_id; + unsigned int i; + struct pci_dev *pdev; + + memset(&found_id, 0, sizeof(found_id)); + + retval = ines_generic_attach(board); + if (retval) + return retval; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + // find board + ines_priv->pci_device = NULL; + for (i = 0; i < num_pci_chips && !ines_priv->pci_device; i++) { + pdev = NULL; + do { + if (pci_ids[i].subsystem_vendor_id >= 0 && + pci_ids[i].subsystem_device_id >= 0) + pdev = pci_get_subsys(pci_ids[i].vendor_id, pci_ids[i].device_id, + pci_ids[i].subsystem_vendor_id, + pci_ids[i].subsystem_device_id, pdev); + else + pdev = pci_get_device(pci_ids[i].vendor_id, pci_ids[i].device_id, + pdev); + if (!pdev) + break; + if (config->pci_bus >= 0 && config->pci_bus != pdev->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(pdev->devfn)) + continue; + found_id = pci_ids[i]; + ines_priv->pci_device = pdev; + break; + } while (1); + } + if (!ines_priv->pci_device) { + pr_err("gpib: could not find ines PCI board\n"); + return -1; + } + + if (pci_enable_device(ines_priv->pci_device)) { + pr_err("error enabling pci device\n"); + return -1; + } + + if (pci_request_regions(ines_priv->pci_device, "ines-gpib")) + return -1; + nec_priv->iobase = (void *)(pci_resource_start(ines_priv->pci_device, + found_id.gpib_region)); + + ines_priv->pci_chip_type = found_id.pci_chip_type; + nec_priv->offset = found_id.io_offset; + switch (ines_priv->pci_chip_type) { + case PCI_CHIP_PLX9050: + ines_priv->plx_iobase = pci_resource_start(ines_priv->pci_device, 1); + break; + case PCI_CHIP_AMCC5920: + ines_priv->amcc_iobase = pci_resource_start(ines_priv->pci_device, 0); + break; + case PCI_CHIP_QUANCOM: + break; + case PCI_CHIP_QUICKLOGIC5030: + break; + default: + pr_err("gpib: unspecified chip type? (bug)\n"); + nec_priv->iobase = 0; + pci_release_regions(ines_priv->pci_device); + return -1; + } + + nec7210_board_reset(nec_priv, board); +#ifdef QUANCOM_PCI + if (ines_priv->pci_chip_type == PCI_CHIP_QUANCOM) { + /* change interrupt polarity */ + nec_priv->auxb_bits |= HR_INV; + ines_outb(ines_priv, nec_priv->auxb_bits, AUXMR); + } +#endif + isr_flags |= IRQF_SHARED; + if (request_irq(ines_priv->pci_device->irq, ines_pci_interrupt, isr_flags, + "pci-gpib", board)) { + pr_err("gpib: can't request IRQ %d\n", ines_priv->pci_device->irq); + return -1; + } + ines_priv->irq = ines_priv->pci_device->irq; + + // enable interrupts on pci chip + switch (ines_priv->pci_chip_type) { + case PCI_CHIP_PLX9050: + outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT, + ines_priv->plx_iobase + PLX9050_INTCSR_REG); + break; + case PCI_CHIP_AMCC5920: + { + static const int region = 1; + static const int num_wait_states = 7; + u32 bits; + + bits = amcc_prefetch_bits(region, PREFETCH_DISABLED); + bits |= amcc_PTADR_mode_bit(region); + bits |= amcc_disable_write_fifo_bit(region); + bits |= amcc_wait_state_bits(region, num_wait_states); + outl(bits, ines_priv->amcc_iobase + AMCC_PASS_THRU_REG); + outl(AMCC_ADDON_INTR_ENABLE_BIT, ines_priv->amcc_iobase + AMCC_INTCS_REG); + } + break; + case PCI_CHIP_QUANCOM: + outb(QUANCOM_IRQ_ENABLE_BIT, (unsigned long)(nec_priv->iobase) + + QUANCOM_IRQ_CONTROL_STATUS_REG); + break; + case PCI_CHIP_QUICKLOGIC5030: + break; + default: + pr_err("gpib: unspecified chip type? (bug)\n"); + return -1; + } + + return 0; +} + +int ines_pci_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pci_attach(board, config); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 0); + + return 0; +} + +int ines_pci_accel_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pci_attach(board, config); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 1); + + return 0; +} + +static const int ines_isa_iosize = 0x20; + +int ines_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + + retval = ines_generic_attach(board); + if (retval) + return retval; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + if (!request_region((unsigned long)config->ibbase, ines_isa_iosize, "ines_gpib")) { + pr_err("ines_gpib: ioports at 0x%p already in use\n", config->ibbase); + return -1; + } + nec_priv->iobase = config->ibbase; + nec_priv->offset = 1; + nec7210_board_reset(nec_priv, board); + if (request_irq(config->ibirq, ines_pci_interrupt, isr_flags, "ines_gpib", board)) { + pr_err("ines_gpib: failed to allocate IRQ %d\n", config->ibirq); + return -1; + } + ines_priv->irq = config->ibirq; + ines_online(ines_priv, board, 1); + return 0; +} + +void ines_pci_detach(gpib_board_t *board) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (ines_priv) { + nec_priv = &ines_priv->nec7210_priv; + if (ines_priv->irq) { + // disable interrupts + switch (ines_priv->pci_chip_type) { + case PCI_CHIP_AMCC5920: + if (ines_priv->plx_iobase) + outl(0, ines_priv->plx_iobase + PLX9050_INTCSR_REG); + break; + case PCI_CHIP_QUANCOM: + if (nec_priv->iobase) + outb(0, (unsigned long)(nec_priv->iobase) + + QUANCOM_IRQ_CONTROL_STATUS_REG); + break; + default: + break; + } + free_irq(ines_priv->irq, board); + } + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + pci_release_regions(ines_priv->pci_device); + } + if (ines_priv->pci_device) + pci_dev_put(ines_priv->pci_device); + } + ines_free_private(board); +} + +void ines_isa_detach(gpib_board_t *board) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (ines_priv) { + nec_priv = &ines_priv->nec7210_priv; + if (ines_priv->irq) + free_irq(ines_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region((unsigned long)(nec_priv->iobase), ines_isa_iosize); + } + } + ines_free_private(board); +} + +static int ines_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static struct pci_driver ines_pci_driver = { + .name = "ines_gpib", + .id_table = ines_pci_table, + .probe = &ines_pci_probe +}; + +#ifdef GPIB_PCMCIA + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +#define DEBUG(n, args...) do {if (pc_debug > (n)) pr_debug(args)} while (0) +#else +#define DEBUG(args...) +#endif + +static const int ines_pcmcia_iosize = 0x20; + +/* The event() function is this driver's Card Services event handler. + * It will be called by Card Services when an appropriate card status + * event is received. The config() and release() entry points are + * used to configure or release a socket, in response to card insertion + * and ejection events. They are invoked from the gpib event + * handler. + */ + +static int ines_gpib_config(struct pcmcia_device *link); +static void ines_gpib_release(struct pcmcia_device *link); +static int ines_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ines_pcmcia_accel_attach(gpib_board_t *board, const gpib_board_config_t *config); +static void ines_pcmcia_detach(gpib_board_t *board); +static irqreturn_t ines_pcmcia_interrupt(int irq, void *arg); +static int ines_common_pcmcia_attach(gpib_board_t *board); +/* + * A linked list of "instances" of the gpib device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + */ + +static struct pcmcia_device *curr_dev; + +/* + * A dev_link_t structure has fields for most things that are needed + * to keep track of a socket, but there will usually be some device + * specific information that also needs to be kept track of. The + * 'priv' pointer in a dev_link_t structure can be used to point to + * a device-specific private data structure, like this. + * + * A driver needs to provide a dev_node_t structure for each device + * on a card. In some cases, there is only one device per card (for + * example, ethernet cards, modems). In other cases, there may be + * many actual or logical devices (SCSI adapters, memory cards with + * multiple partitions). The dev_node_t structures need to be kept + * in a linked list starting at the 'dev' field of a dev_link_t + * structure. We allocate them in the card's private data structure, + * because they generally can't be allocated dynamically. + */ + +struct local_info { + struct pcmcia_device *p_dev; + gpib_board_t *dev; + u_short manfid; + u_short cardid; +}; + +/* + * gpib_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ +static int ines_gpib_probe(struct pcmcia_device *link) +{ + struct local_info *info; + +// int ret, i; + + DEBUG(0, "%s(0x%p)\n", __func__ link); + + /* Allocate space for private device-specific data */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + /* The io structure describes IO port mapping */ + link->resource[0]->end = 32; + link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + link->io_lines = 5; + + /* General socket configuration */ + link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + /* Register with Card Services */ + curr_dev = link; + return ines_gpib_config(link); +} + +/* + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ +static void ines_gpib_remove(struct pcmcia_device *link) +{ + struct local_info *info = link->priv; + //struct gpib_board_t *dev = info->dev; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + if (info->dev) + ines_pcmcia_detach(info->dev); + ines_gpib_release(link); + + //free_netdev(dev); + kfree(info); +} + +static int ines_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) +{ + return pcmcia_request_io(link); +} + +/* + * gpib_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * device available to the system. + */ +static int ines_gpib_config(struct pcmcia_device *link) +{ + struct local_info *dev; + int retval; + void *virt; + + dev = link->priv; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + retval = pcmcia_loop_config(link, &ines_gpib_config_iteration, NULL); + if (retval) { + dev_warn(&link->dev, "no configuration found\n"); + ines_gpib_release(link); + return -ENODEV; + } + + pr_debug("ines_cs: manufacturer: 0x%x card: 0x%x\n", + link->manf_id, link->card_id); + + /* for the ines card we have to setup the configuration registers in + * attribute memory here + */ + link->resource[2]->flags |= WIN_MEMORY_TYPE_AM | WIN_DATA_WIDTH_8 | WIN_ENABLE; + link->resource[2]->end = 0x1000; + retval = pcmcia_request_window(link, link->resource[2], 250); + if (retval) { + dev_warn(&link->dev, "pcmcia_request_window failed\n"); + ines_gpib_release(link); + return -ENODEV; + } + retval = pcmcia_map_mem_page(link, link->resource[2], 0); + if (retval) { + dev_warn(&link->dev, "pcmcia_map_mem_page failed\n"); + ines_gpib_release(link); + return -ENODEV; + } + virt = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + writeb((link->resource[2]->start >> 2) & 0xff, virt + 0xf0); // IOWindow base + iounmap((void *)virt); + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + retval = pcmcia_enable_device(link); + if (retval) { + ines_gpib_release(link); + return -ENODEV; + } + pr_info("ines gpib device loaded\n"); + return 0; +} /* gpib_config */ + +/* + * After a card is removed, gpib_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ + +static void ines_gpib_release(struct pcmcia_device *link) +{ + DEBUG(0, "%s(0x%p)\n", __func__, link); + pcmcia_disable_device(link); +} /* gpib_release */ + +static int ines_gpib_suspend(struct pcmcia_device *link) +{ + //struct local_info *info = link->priv; + //struct gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + if (link->open) + pr_err("Device still open ???\n"); + //netif_device_detach(dev); + + return 0; +} + +static int ines_gpib_resume(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * printk("Gpib resumed ???\n"); + * //netif_device_attach(dev); + *} + */ + return ines_gpib_config(link); +} + +static struct pcmcia_device_id ines_pcmcia_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x01b4, 0x4730), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, ines_pcmcia_ids); + +static struct pcmcia_driver ines_gpib_cs_driver = { + .owner = THIS_MODULE, + .name = "ines_gpib_cs", + .id_table = ines_pcmcia_ids, + .probe = ines_gpib_probe, + .remove = ines_gpib_remove, + .suspend = ines_gpib_suspend, + .resume = ines_gpib_resume, +}; + +int ines_pcmcia_init_module(void) +{ + pcmcia_register_driver(&ines_gpib_cs_driver); + return 0; +} + +void ines_pcmcia_cleanup_module(void) +{ + DEBUG(0, "ines_cs: unloading\n"); + pcmcia_unregister_driver(&ines_gpib_cs_driver); +} + +gpib_interface_t ines_pcmcia_unaccel_interface = { +name: "ines_pcmcia_unaccel", +attach : ines_pcmcia_attach, +detach : ines_pcmcia_detach, +read : ines_read, +write : ines_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +gpib_interface_t ines_pcmcia_accel_interface = { +name: "ines_pcmcia_accel", +attach : ines_pcmcia_accel_attach, +detach : ines_pcmcia_detach, +read : ines_accel_read, +write : ines_accel_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +gpib_interface_t ines_pcmcia_interface = { +name: "ines_pcmcia", +attach : ines_pcmcia_accel_attach, +detach : ines_pcmcia_detach, +read : ines_accel_read, +write : ines_accel_write, +command : ines_command, +take_control : ines_take_control, +go_to_standby : ines_go_to_standby, +request_system_control : ines_request_system_control, +interface_clear : ines_interface_clear, +remote_enable : ines_remote_enable, +enable_eos : ines_enable_eos, +disable_eos : ines_disable_eos, +parallel_poll : ines_parallel_poll, +parallel_poll_configure : ines_parallel_poll_configure, +parallel_poll_response : ines_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ines_line_status, +update_status : ines_update_status, +primary_address : ines_primary_address, +secondary_address : ines_secondary_address, +serial_poll_response : ines_serial_poll_response, +serial_poll_status : ines_serial_poll_status, +t1_delay : ines_t1_delay, +return_to_local : ines_return_to_local, +}; + +irqreturn_t ines_pcmcia_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + + return ines_interrupt(board); +} + +int ines_common_pcmcia_attach(gpib_board_t *board) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int retval; + + if (!curr_dev) { + pr_err("no ines pcmcia cards found\n"); + return -1; + } + + retval = ines_generic_attach(board); + if (retval) + return retval; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + if (request_region(curr_dev->resource[0]->start, + resource_size(curr_dev->resource[0]), "ines_gpib") == 0) { + pr_err("ines_gpib: ioports at 0x%lx already in use\n", + (unsigned long)(curr_dev->resource[0]->start)); + return -1; + } + + nec_priv->iobase = (void *)(unsigned long)curr_dev->resource[0]->start; + + nec7210_board_reset(nec_priv, board); + + if (request_irq(curr_dev->irq, ines_pcmcia_interrupt, IRQF_SHARED, + "pcmcia-gpib", board)) { + pr_err("gpib: can't request IRQ %d\n", curr_dev->irq); + return -1; + } + ines_priv->irq = curr_dev->irq; + + return 0; +} + +int ines_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pcmcia_attach(board); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 0); + + return 0; +} + +int ines_pcmcia_accel_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pcmcia_attach(board); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 1); + + return 0; +} + +void ines_pcmcia_detach(gpib_board_t *board) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (ines_priv) { + nec_priv = &ines_priv->nec7210_priv; + if (ines_priv->irq) + free_irq(ines_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region((unsigned long)(nec_priv->iobase), ines_pcmcia_iosize); + } + } + ines_free_private(board); +} + +#endif /* GPIB_PCMCIA */ + +static int __init ines_init_module(void) +{ + int err = 0; + + err = pci_register_driver(&ines_pci_driver); + if (err) { + pr_err("ines_gpib: pci_driver_register failed!\n"); + return err; + } + + gpib_register_driver(&ines_pci_interface, THIS_MODULE); + gpib_register_driver(&ines_pci_unaccel_interface, THIS_MODULE); + gpib_register_driver(&ines_pci_accel_interface, THIS_MODULE); + gpib_register_driver(&ines_isa_interface, THIS_MODULE); +#ifdef GPIB_PCMCIA + gpib_register_driver(&ines_pcmcia_interface, THIS_MODULE); + gpib_register_driver(&ines_pcmcia_unaccel_interface, THIS_MODULE); + gpib_register_driver(&ines_pcmcia_accel_interface, THIS_MODULE); + err += ines_pcmcia_init_module(); +#endif + if (err) + return -1; + + return 0; +} + +static void __exit ines_exit_module(void) +{ + gpib_unregister_driver(&ines_pci_interface); + gpib_unregister_driver(&ines_pci_unaccel_interface); + gpib_unregister_driver(&ines_pci_accel_interface); + gpib_unregister_driver(&ines_isa_interface); +#ifdef GPIB__PCMCIA + gpib_unregister_driver(&ines_pcmcia_interface); + gpib_unregister_driver(&ines_pcmcia_unaccel_interface); + gpib_unregister_driver(&ines_pcmcia_accel_interface); + ines_pcmcia_cleanup_module(); +#endif + + pci_unregister_driver(&ines_pci_driver); +} + +module_init(ines_init_module); +module_exit(ines_exit_module); diff --git a/drivers/staging/gpib/lpvo_usb_gpib/Makefile b/drivers/staging/gpib/lpvo_usb_gpib/Makefile new file mode 100644 index 0000000000000..137511acce635 --- /dev/null +++ b/drivers/staging/gpib/lpvo_usb_gpib/Makefile @@ -0,0 +1,3 @@ + +obj-m += lpvo_usb_gpib.o + diff --git a/drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c new file mode 100644 index 0000000000000..796c3a5be5451 --- /dev/null +++ b/drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -0,0 +1,2136 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * This code has been developed at the Department of Physics (University * + * of Florence, Italy) to support in linux-gpib the open usb-gpib adapter * + * implemented at the University of Ljubljana (lpvo.fe.uni-lj.si/gpib) * + * * + * copyright : (C) 2011 Marcello Carla' * + ***************************************************************************/ + +/* base module includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpibP.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for LPVO usb devices"); + +#define NAME "lpvo_usb_gpib" + +/* + * Table of devices that work with this driver. + * + * Currently, only one device is known to be used in the + * lpvo_usb_gpib adapter (FTDI 0403:6001). + * If your adapter uses a different chip, insert a line + * in the following table with proper , . + * + * To have your chip automatically handled by the driver, + * update files "/usr/local/etc/modprobe.d/lpvo_usb_gpib.conf" + * and /usr/local/etc/udev/rules.d/99-lpvo_usb_gpib.rules. + * + */ + +static const struct usb_device_id skel_table[] = { + { USB_DEVICE(0x0403, 0x6001) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, skel_table); + +/* + * *** Diagnostics and Debug *** + * + * The module parameter "debug" controls the sending of debug messages to + * syslog. By default it is set to 0 or 1 according to GPIB_CONFIG_KERNEL_DEBUG. + * debug = 0: only register/deregister messages are generated + * 1: every action is logged + * 2: extended logging; each single exchanged byte is documented + * (about twice the log volume of [1]) + * To switch debug level: + * At module loading: modprobe lpvo_usb_gpib debug={0,1,2} + * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug + */ + +static int debug; +module_param(debug, int, 0644); + +#define DIA_LOG(level, format, ...) \ + do { if (debug >= (level)) \ + pr_alert("%s:%s - " format, NAME, __func__, ## __VA_ARGS__); } \ + while (0) + +/* standard and extended command sets of the usb-gpib adapter */ + +#define USB_GPIB_ON "\nIB\n" +#define USB_GPIB_OFF "\nIBO\n" +#define USB_GPIB_IBm0 "\nIBm0\n" /* do not assert REN with IFC */ +#define USB_GPIB_IBm1 "\nIBm1\n" /* assert REN with IFC */ +#define USB_GPIB_IBCL "\nIBZ\n" +#define USB_GPIB_STATUS "\nIBS\n" +#define USB_GPIB_READ "\nIB?\n" +#define USB_GPIB_READ_1 "\nIBB\n" +#define USB_GPIB_EOI "\nIBe0\n" +#define USB_GPIB_FTMO "\nIBf0\n" /* disable first byte timeout */ +#define USB_GPIB_TTMOZ "\nIBt0\n" /* disable byte timeout */ + +/* incomplete commands */ + +#define USB_GPIB_BTMO "\nIBt" /* set byte timeout */ +#define USB_GPIB_TTMO "\nIBT" /* set total timeout */ + +#define USB_GPIB_DEBUG_ON "\nIBDE\xAA\n" +#define USB_GPIB_SET_LISTEN "\nIBDT0\n" +#define USB_GPIB_SET_TALK "\nIBDT1\n" +#define USB_GPIB_SET_LINES "\nIBDC\n" +#define USB_GPIB_SET_DATA "\nIBDM\n" +#define USB_GPIB_READ_LINES "\nIBD?C\n" +#define USB_GPIB_READ_DATA "\nIBD?M\n" +#define USB_GPIB_READ_BUS "\nIBD??\n" + +/* command sequences */ + +#define USB_GPIB_UNTALK "\nIBC_\n" +#define USB_GPIB_UNLISTEN "\nIBC?\n" + +/* special characters used by the adapter */ + +#define DLE ('\020') +#define STX ('\02') +#define ETX ('\03') +#define ACK ('\06') +#define NODATA ('\03') +#define NODAV ('\011') + +#define IB_BUS_REN 0x01 +#define IB_BUS_IFC 0x02 +#define IB_BUS_NDAC 0x04 +#define IB_BUS_NRFD 0x08 +#define IB_BUS_DAV 0x10 +#define IB_BUS_EOI 0x20 +#define IB_BUS_ATN 0x40 +#define IB_BUS_SRQ 0x80 + +#define INBUF_SIZE 128 + +struct char_buf { /* used by one_char() routine */ + char *inbuf; + int last; + int nchar; +}; + +struct usb_gpib_priv { /* private data to the device */ + u8 eos; /* eos character */ + short eos_flags; /* eos mode */ + int timeout; /* current value for timeout */ + void *dev; /* the usb device private data structure */ +}; + +#define GPIB_DEV (((struct usb_gpib_priv *)board->private_data)->dev) + +#define SHOW_STATUS(board) { \ + DIA_LOG(2, "# - board %p\n", board); \ + DIA_LOG(2, "# - buffer_length %d\n", board->buffer_length); \ + DIA_LOG(2, "# - status %lx\n", board->status); \ + DIA_LOG(2, "# - use_count %d\n", board->use_count); \ + DIA_LOG(2, "# - pad %x\n", board->pad); \ + DIA_LOG(2, "# - sad %x\n", board->sad); \ + DIA_LOG(2, "# - timeout %d\n", board->usec_timeout); \ + DIA_LOG(2, "# - ppc %d\n", board->parallel_poll_configuration); \ + DIA_LOG(2, "# - t1delay %d\n", board->t1_nano_sec); \ + DIA_LOG(2, "# - online %d\n", board->online); \ + DIA_LOG(2, "# - autopoll %d\n", board->autospollers); \ + DIA_LOG(2, "# - autopoll task %p\n", board->autospoll_task); \ + DIA_LOG(2, "# - minor %d\n", board->minor); \ + DIA_LOG(2, "# - master %d\n", board->master); \ + DIA_LOG(2, "# - list %d\n", board->ist); \ + } +/* + * n = 0; + * list_for_each (l, &board->device_list) n++; + * TTY_LOG ("%s:%s - devices in list %d\n", a, b, n); + */ + +/* + * TTY_LOG - write a message to the current work terminal (if any) + */ + +#define TTY_LOG(format, ...) { \ + char buf[128]; \ + struct tty_struct *tty = get_current_tty(); \ + if (tty) { \ + snprintf(buf, 128, format, __VA_ARGS__); \ + tty->driver->ops->write(tty, buf, strlen(buf)); \ + tty->driver->ops->write(tty, "\r", 1); \ + } \ + } + +/* + * GLOBAL VARIABLES: required for + * pairing among gpib minor and usb minor. + * MAX_DEV is the max number of usb-gpib adapters; free + * to change as you like, but no more than 32 + */ + +#define MAX_DEV 8 +static struct usb_interface *lpvo_usb_interfaces[MAX_DEV]; /* registered interfaces */ +static int usb_minors[MAX_DEV]; /* usb minors */ +static int assigned_usb_minors; /* mask of filled slots */ +static struct mutex minors_lock; /* operations on usb_minors are to be protected */ + +/* + * usb-skeleton prototypes + */ + +struct usb_skel; +static ssize_t skel_do_write(struct usb_skel *, const char *, size_t); +static ssize_t skel_do_read(struct usb_skel *, char *, size_t); +static int skel_do_open(gpib_board_t *, int); +static int skel_do_release(gpib_board_t *); + +/* + * usec_diff : take difference in MICROsec between two 'timespec' + * (unix time in sec and NANOsec) + */ + +inline int usec_diff(struct timespec64 *a, struct timespec64 *b) +{ + return ((a->tv_sec - b->tv_sec) * 1000000 + + (a->tv_nsec - b->tv_nsec) / 1000); +} + +/* + * *** these routines are specific to the usb-gpib adapter *** + */ + +/** + * write_loop() - Send a byte sequence to the adapter + * + * @dev: the private device structure + * @msg: the byte sequence. + * @leng: the byte sequence length. + * + */ + +static int write_loop(void *dev, char *msg, int leng) +{ +// int nchar = 0, val; + +// do { + + return skel_do_write(dev, msg, leng); + +// if (val < 1) { +// printk (KERN_ALERT "%s:%s - write error: %d %d/%d\n", +// NAME, __func__, val, nchar, leng); +// return -EIO; +// } +// nchar +=val; +// } while (nchar < leng); +// return leng; +} + +static char printable(char x) +{ + if (x < 32 || x > 126) + return ' '; + return x; +} + +/** + * send_command() - Send a byte sequence and return a single byte reply. + * + * @board: the gpib_board_struct data area for this gpib interface + * @msg: the byte sequence. + * @leng the byte sequence length; can be given as zero and is + * computed automatically, but if 'msg' contains a zero byte, + * it has to be given explicitly. + */ + +static int send_command(gpib_board_t *board, char *msg, int leng) +{ + char buffer[64]; + int nchar, j; + int retval; + struct timespec64 before, after; + + ktime_get_real_ts64 (&before); + + if (!leng) + leng = strlen(msg); + retval = write_loop(GPIB_DEV, msg, leng); + if (retval < 0) + return retval; + + nchar = skel_do_read(GPIB_DEV, buffer, 64); + + if (nchar < 0) { + DIA_LOG(0, " return from read: %d\n", nchar); + return nchar; + } else if (nchar != 1) { + for (j = 0 ; j < leng ; j++) { + DIA_LOG(0, " Irregular reply to command: %d %x %c\n", + j, msg[j], printable(msg[j])); + } + for (j = 0 ; j < nchar ; j++) { + DIA_LOG(0, " Irregular command reply: %d %x %c\n", + j, buffer[j] & 0xff, printable(buffer[j])); + } + return -EIO; + } + ktime_get_real_ts64 (&after); + + DIA_LOG(1, "Sent %d - done %d us.\n", leng, usec_diff(&after, &before)); + + return buffer[0] & 0xff; +} + +/* + * + * set_control_line() - Set the value of a single gpib control line + * + * @board: the gpib_board_struct data area for this gpib interface + * @line: line mask + * @value: line new value (0/1) + * + */ + +static int set_control_line(gpib_board_t *board, int line, int value) +{ + char msg[] = USB_GPIB_SET_LINES; + int retval; + int leng = strlen(msg); + + DIA_LOG(1, "setting line %x to %x\n", line, value); + + retval = send_command(board, USB_GPIB_READ_LINES, 0); + + DIA_LOG(1, "old line values: %x\n", retval); + + if (retval == -EIO) + return retval; + + msg[leng - 2] = value ? (retval & ~line) : retval | line; + + retval = send_command(board, msg, 0); + + DIA_LOG(1, "operation result: %x\n", retval); + + return retval; +} + +/* + * one_char() - read one single byte from input buffer + * + * @board: the gpib_board_struct data area for this gpib interface + * @char_buf: the routine private data structure + */ + +static int one_char(gpib_board_t *board, struct char_buf *b) +{ + struct timespec64 before, after; + + if (b->nchar) { + DIA_LOG(2, "-> %x\n", b->inbuf[b->last - b->nchar]); + return b->inbuf[b->last - b->nchar--]; + } + ktime_get_real_ts64 (&before); + b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); + b->last = b->nchar; + ktime_get_real_ts64 (&after); + + DIA_LOG(2, "read %d bytes in %d usec\n", + b->nchar, usec_diff(&after, &before)); + + if (b->nchar > 0) { + DIA_LOG(2, "--> %x\n", b->inbuf[b->last - b->nchar]); + return b->inbuf[b->last - b->nchar--]; + } else if (b->nchar == 0) { + dev_alert(board->gpib_dev, "%s:%s - read returned EOF\n", NAME, __func__); + return -EIO; + } + dev_alert(board->gpib_dev, "%s:%s - read error %d\n", NAME, __func__, b->nchar); + TTY_LOG("\n *** %s *** Read Error - %s\n", NAME, + "Reset the adapter with 'gpib_config'\n"); + return -EIO; +} + +/** + * set_timeout() - set single byte / total timeouts on the adapter + * + * @board: the gpib_board_struct data area for this gpib interface + * + * For sake of speed, the operation is performed only if it + * modifies the current (saved) value. Minimum allowed timeout + * is 30 ms (T30ms -> 8); timeout disable (TNONE -> 0) currently + * not supported. + */ + +static void set_timeout(gpib_board_t *board) +{ + int n, val; + char command[sizeof(USB_GPIB_TTMO) + 6]; + struct usb_gpib_priv *data = board->private_data; + + if (data->timeout == board->usec_timeout) + return; + + n = (board->usec_timeout + 32767) / 32768; + if (n < 2) + n = 2; + + DIA_LOG(1, "Set timeout to %d us -> %d\n", board->usec_timeout, n); + + sprintf(command, "%s%d\n", USB_GPIB_BTMO, n > 255 ? 255 : n); + val = send_command(board, command, 0); + + if (val == ACK) { + if (n > 65535) + n = 65535; + sprintf(command, "%s%d\n", USB_GPIB_TTMO, n); + val = send_command(board, command, 0); + } + + if (val != ACK) { + dev_alert(board->gpib_dev, "%s:%s - error in timeout set: <%s>\n", + NAME, __func__, command); + } else { + data->timeout = board->usec_timeout; + } +} + +/* + * now the standard interface functions - attach and detach + */ + +/** + * usb_gpib_attach() - activate the usb-gpib converter board + * + * @board: the gpib_board_struct data area for this gpib interface + * @config: firmware data, if any (from gpib_config -I ) + * + * The channel name is ttyUSBn, with n=0 by default. Other values for n + * passed with gpib_config -b . + * + * In this routine I trust that when an error code is returned + * detach() will be called. Always. + */ + +static int usb_gpib_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + int retval, j; + int base = (long)config->ibbase; + char *device_path; + int match; + struct usb_device *udev; + + DIA_LOG(0, "Board %p -t %s -m %d -a %p -u %d -l %d -b %d\n", + board, board->interface->name, board->minor, config->device_path, + config->pci_bus, config->pci_slot, base); + + board->private_data = NULL; /* to be sure - we can detach before setting */ + + /* identify device to be attached */ + + mutex_lock(&minors_lock); + + if (config->device_path) { + /* if config->device_path given, try that first */ + dev_alert(board->gpib_dev, "%s:%s - Looking for device_path: %s\n", + NAME, __func__, config->device_path); + for (j = 0 ; j < MAX_DEV ; j++) { + if ((assigned_usb_minors & 1 << j) == 0) + continue; + udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL); + match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev, + config->device_path); + DIA_LOG(1, "dev. %d: minor %d path: %s --> %d\n", j, + lpvo_usb_interfaces[j]->minor, device_path, match); + kfree(device_path); + if (match) + break; + } + } else if (config->pci_bus != -1 && config->pci_slot != -1) { + /* second: look for bus and slot */ + for (j = 0 ; j < MAX_DEV ; j++) { + if ((assigned_usb_minors & 1 << j) == 0) + continue; + udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j, + udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot); + if (config->pci_bus == udev->bus->busnum && + config->pci_slot == udev->devnum) + break; + } + } else { /* last chance: usb_minor, given as ibbase */ + for (j = 0 ; j < MAX_DEV ; j++) { + if (usb_minors[j] == base && assigned_usb_minors & 1 << j) + break; + } + } + mutex_unlock(&minors_lock); + + if (j == MAX_DEV) { + dev_alert(board->gpib_dev, "%s:%s - Requested device is not registered.\n", + NAME, __func__); + return -EIO; + } + + board->private_data = kzalloc(sizeof(struct usb_gpib_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + + retval = skel_do_open(board, usb_minors[j]); + + DIA_LOG(1, "Skel open: %d\n", retval); + + if (retval) { + TTY_LOG("%s:%s - skel open failed.\n", NAME, __func__); + kfree(board->private_data); + board->private_data = NULL; + return -ENODEV; + } + + SHOW_STATUS(board); + + retval = send_command(board, USB_GPIB_ON, 0); + DIA_LOG(1, "USB_GPIB_ON returns %x\n", retval); + if (retval != ACK) + return -EIO; + + /* We must setup debug mode because we need the extended instruction + * set to cope with the Core (gpib_common) point of view + */ + + retval = send_command(board, USB_GPIB_DEBUG_ON, 0); + DIA_LOG(1, "USB_GPIB_DEBUG_ON returns %x\n", retval); + if (retval != ACK) + return -EIO; + + /* We must keep REN off after an IFC because so it is + * assumed by the Core + */ + + retval = send_command(board, USB_GPIB_IBm0, 0); + DIA_LOG(1, "USB_GPIB_IBm0 returns %x\n", retval); + if (retval != ACK) + return -EIO; + + retval = set_control_line(board, IB_BUS_REN, 0); + if (retval != ACK) + return -EIO; + + retval = send_command(board, USB_GPIB_FTMO, 0); + DIA_LOG(1, "USB_GPIB_FTMO returns %x\n", retval); + if (retval != ACK) + return -EIO; + + SHOW_STATUS(board); + TTY_LOG("Module '%s' has been sucesfully configured\n", NAME); + return 0; +} + +/** + * usb_gpib_detach() - deactivate the usb-gpib converter board + * + * @board: the gpib_board data area for this gpib interface + * + */ + +static void usb_gpib_detach(gpib_board_t *board) +{ + int retval; + + SHOW_STATUS(board); + + DIA_LOG(0, "detaching %p\n", board); + + if (board->private_data) { + if (GPIB_DEV) { + write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF)); + msleep(100); + DIA_LOG(1, "%s", "GPIB off\n"); + retval = skel_do_release(board); + DIA_LOG(1, "skel release -> %d\n", retval); + } + kfree(board->private_data); + board->private_data = NULL; + } + + DIA_LOG(0, "done %p\n", board); + TTY_LOG("Module '%s' has been detached\n", NAME); +} + +/* + * Other functions follow in alphabetical order + */ +/* command */ +static int usb_gpib_command(gpib_board_t *board, + u8 *buffer, + size_t length, + size_t *bytes_written) +{ + int i, retval; + char command[6] = "IBc\n"; + + DIA_LOG(1, "enter %p\n", board); + + set_timeout(board); + + *bytes_written = 0; + for (i = 0 ; i < length ; i++) { + command[3] = buffer[i]; + retval = send_command(board, command, 5); + DIA_LOG(2, "%d ==> %x %x\n", i, buffer[i], retval); + if (retval != 0x06) + return retval; + ++(*bytes_written); + } + return 0; +} + +/** + * disable_eos() - Disable END on eos byte (END on EOI only) + * + * @board: the gpib_board data area for this gpib interface + * + * With the lpvo adapter eos can only be handled via software. + * Cannot do nothing here, but remember for future use. + */ + +static void usb_gpib_disable_eos(gpib_board_t *board) +{ + ((struct usb_gpib_priv *)board->private_data)->eos_flags &= ~REOS; + DIA_LOG(1, "done: %x\n", + ((struct usb_gpib_priv *)board->private_data)->eos_flags); +} + +/** + * enable_eos() - Enable END for reads when eos byte is received. + * + * @board: the gpib_board data area for this gpib interface + * @eos_byte: the 'eos' byte + * @compare_8_bits: if zero ignore eigthth bit when comparing + * + */ + +static int usb_gpib_enable_eos(gpib_board_t *board, + u8 eos_byte, + int compare_8_bits) +{ + struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data; + + DIA_LOG(1, "enter with %x\n", eos_byte); + pd->eos = eos_byte; + pd->eos_flags = REOS; + if (compare_8_bits) + pd->eos_flags |= BIN; + return 0; +} + +/** + * go_to_standby() - De-assert ATN + * + * @board: the gpib_board data area for this gpib interface + */ + +static int usb_gpib_go_to_standby(gpib_board_t *board) +{ + int retval = set_control_line(board, IB_BUS_ATN, 0); + + DIA_LOG(1, "done with %x\n", retval); + + if (retval == ACK) + return 0; + return -EIO; +} + +/** + * interface_clear() - Assert or de-assert IFC + * + * @board: the gpib_board data area for this gpib interface + * assert: 1: assert IFC; 0: de-assert IFC + * + * Currently on the assert request we issue the lpvo IBZ + * command that cycles IFC low for 100 usec, then we ignore + * the de-assert request. + */ + +static void usb_gpib_interface_clear(gpib_board_t *board, int assert) +{ + int retval = 0; + + DIA_LOG(1, "enter with %d\n", assert); + + if (assert) { + retval = send_command(board, USB_GPIB_IBCL, 0); + + set_bit(CIC_NUM, &board->status); + } + + DIA_LOG(1, "done with %d %d\n", assert, retval); +} + +/** + * line_status() - Read the status of the bus lines. + * + * @board: the gpib_board data area for this gpib interface + * + * We can read all lines. + */ + +#define WQT wait_queue_entry_t +#define WQH head +#define WQE entry + +static int usb_gpib_line_status(const gpib_board_t *board) +{ + int buffer; + int line_status = ValidALL; /* all lines will be read */ + struct list_head *p, *q; + WQT *item; + unsigned long flags; + int sleep = 0; + + DIA_LOG(1, "%s\n", "request"); + + /* if we are on the wait queue (board->wait), do not hurry + * reading status line; instead, pause a little + */ + + spin_lock_irqsave((spinlock_t *)&board->wait.lock, flags); + q = (struct list_head *)&board->wait.WQH; + list_for_each(p, q) { + item = container_of(p, WQT, WQE); + if (item->private == current) { + sleep = 20; + break; + } + /* pid is: ((struct task_struct *) item->private)->pid); */ + } + spin_unlock_irqrestore((spinlock_t *)&board->wait.lock, flags); + if (sleep) { + DIA_LOG(1, "we are on the wait queue - sleep %d ms\n", sleep); + msleep(sleep); + } + + buffer = send_command((gpib_board_t *)board, USB_GPIB_STATUS, 0); + + if (buffer < 0) { + dev_alert(board->gpib_dev, "%s:%s - line status read failed with %d\n", + NAME, __func__, buffer); + return -1; + } + + if ((buffer & 0x01) == 0) + line_status |= BusREN; + if ((buffer & 0x02) == 0) + line_status |= BusIFC; + if ((buffer & 0x04) == 0) + line_status |= BusNDAC; + if ((buffer & 0x08) == 0) + line_status |= BusNRFD; + if ((buffer & 0x10) == 0) + line_status |= BusDAV; + if ((buffer & 0x20) == 0) + line_status |= BusEOI; + if ((buffer & 0x40) == 0) + line_status |= BusATN; + if ((buffer & 0x80) == 0) + line_status |= BusSRQ; + + DIA_LOG(1, "done with %x %x\n", buffer, line_status); + + return line_status; +} + +/* parallel_poll */ + +static int usb_gpib_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + /* request parallel poll asserting ATN | EOI; + * we suppose ATN already asserted + */ + + int retval; + + DIA_LOG(1, "enter %p\n", board); + + retval = set_control_line(board, IB_BUS_EOI, 1); + if (retval != ACK) { + dev_alert(board->gpib_dev, "%s:%s - assert EOI failed\n", NAME, __func__); + return -EIO; + } + + *result = send_command(board, USB_GPIB_READ_DATA, 0); + + DIA_LOG(1, "done with %x\n", *result); + + retval = set_control_line(board, IB_BUS_EOI, 0); + if (retval != 0x06) { + dev_alert(board->gpib_dev, "%s:%s - unassert EOI failed\n", NAME, __func__); + return -EIO; + } + + return 0; +} + +/* read */ + +static int usb_gpib_read(gpib_board_t *board, + u8 *buffer, + size_t length, + int *end, + size_t *bytes_read) +{ +#define MAX_READ_EXCESS 16384 + + struct char_buf b = {NULL, 0}; + + int retval; + char c, nc; + int ic; + struct timespec64 before, after; + int read_count = MAX_READ_EXCESS; + struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data; + + DIA_LOG(1, "enter %p -> %zu\n", board, length); + + *bytes_read = 0; /* by default, things go wrong */ + *end = 0; + + set_timeout(board); + + /* single byte read has a special handling */ + + if (length == 1) { + char inbuf[2] = {0, 0}; + + /* read a single character */ + + ktime_get_real_ts64 (&before); + + retval = write_loop(GPIB_DEV, USB_GPIB_READ_1, strlen(USB_GPIB_READ_1)); + if (retval < 0) + return retval; + + retval = skel_do_read(GPIB_DEV, inbuf, 1); + retval += skel_do_read(GPIB_DEV, inbuf + 1, 1); + + ktime_get_real_ts64 (&after); + + DIA_LOG(1, "single read: %x %x %x in %d\n", retval, + inbuf[0], inbuf[1], + usec_diff(&after, &before)); + + /* good char / last char? */ + + if (retval == 2 && inbuf[1] == ACK) { + buffer[0] = inbuf[0]; + *bytes_read = 1; + return 0; + } + if (retval < 2) + return -EIO; + else + return -ETIME; + return 0; + } + + /* allocate buffer for multibyte read */ + + b.inbuf = kmalloc(INBUF_SIZE, GFP_KERNEL); + if (!b.inbuf) + return -ENOMEM; + + /* send read command and check sequence */ + + retval = write_loop(GPIB_DEV, USB_GPIB_READ, strlen(USB_GPIB_READ)); + if (retval < 0) + goto read_return; + + if (one_char(board, &b) != DLE || one_char(board, &b) != STX) { + dev_alert(board->gpib_dev, "%s:%s - wrong sequence\n", + NAME, __func__); + retval = -EIO; + goto read_return; + } + + /* get data flow */ + + while (1) { + ic = one_char(board, &b); + if (ic == -EIO) { + retval = -EIO; + goto read_return; + } + c = ic; + + if (c == DLE) + nc = one_char(board, &b); + if (c != DLE || nc == DLE) { + /* data byte - store into buffer */ + + if (*bytes_read == length) + break; /* data overflow */ + if (c == DLE) + c = nc; + buffer[(*bytes_read)++] = c; + if (c == pd->eos) { + *end = 1; + break; + } + + } else { + /* we are in the closing sequence */ + + if (c == ETX) { + c = one_char(board, &b); + if (c == ACK) { + *end = 1; + retval = 0; + goto read_return; + } else { + dev_alert(board->gpib_dev, "%s:%s - %s %x\n", + NAME, __func__, + "Wrong end of message", c); + retval = -ETIME; + goto read_return; + } + } else { + dev_alert(board->gpib_dev, "%s:%s - %s\n", NAME, __func__, + "lone in stream"); + retval = -EIO; + goto read_return; + } + } + } + + /* we had a data overflow - flush excess data */ + + while (read_count--) { + if (one_char(board, &b) != DLE) + continue; + c = one_char(board, &b); + if (c == DLE) + continue; + if (c == ETX) { + c = one_char(board, &b); + if (c == ACK) { + if (MAX_READ_EXCESS - read_count > 1) + dev_alert(board->gpib_dev, "%s:%s - %s\n", NAME, __func__, + "small buffer - maybe some data lost"); + retval = 0; + goto read_return; + } + break; + } + } + + dev_alert(board->gpib_dev, "%s:%s - no input end - GPIB board in odd state\n", + NAME, __func__); + retval = -EIO; + +read_return: + kfree(b.inbuf); + + DIA_LOG(1, "done with byte/status: %d %x %d\n", + (int)*bytes_read, retval, *end); + + if (retval == 0 || retval == -ETIME) { + if (send_command(board, USB_GPIB_UNTALK, sizeof(USB_GPIB_UNTALK)) == 0x06) + return retval; + return -EIO; + } + + return retval; +} + +/* remote_enable */ + +static void usb_gpib_remote_enable(gpib_board_t *board, int enable) +{ + int retval; + + retval = set_control_line(board, IB_BUS_REN, enable ? 1 : 0); + if (retval != ACK) + dev_alert(board->gpib_dev, "%s:%s - could not set REN line: %x\n", + NAME, __func__, retval); + + DIA_LOG(1, "done with %x\n", retval); +} + +/* request_system_control */ + +static void usb_gpib_request_system_control(gpib_board_t *board, + int request_control) +{ + if (request_control) + set_bit(CIC_NUM, &board->status); + else + clear_bit(CIC_NUM, &board->status); + + DIA_LOG(1, "done with %d -> %lx\n", request_control, board->status); +} + +/* take_control */ +/* beware: the sync flag is ignored; what is its real meaning? */ + +static int usb_gpib_take_control(gpib_board_t *board, int sync) +{ + int retval; + + retval = set_control_line(board, IB_BUS_ATN, 1); + + DIA_LOG(1, "done with %d %x\n", sync, retval); + + if (retval == ACK) + return 0; + return -EIO; +} + +/* update_status */ + +static unsigned int usb_gpib_update_status(gpib_board_t *board, + unsigned int clear_mask) +{ + /* There is nothing we can do here, I guess */ + + board->status &= ~clear_mask; + + DIA_LOG(1, "done with %x %lx\n", clear_mask, board->status); + + return board->status; +} + +/* write */ +/* beware: DLE characters are not escaped - can only send ASCII data */ + +static int usb_gpib_write(gpib_board_t *board, + u8 *buffer, + size_t length, + int send_eoi, + size_t *bytes_written) +{ + int retval; + char *msg; + + DIA_LOG(1, "enter %p -> %zu\n", board, length); + + set_timeout(board); + + msg = kmalloc(length + 8, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + memcpy(msg, "\nIB\020\002", 5); + memcpy(msg + 5, buffer, length); + memcpy(msg + 5 + length, "\020\003\n", 3); + + retval = send_command(board, msg, length + 8); + kfree(msg); + + DIA_LOG(1, "<%.*s> -> %x\n", (int)length, buffer, retval); + + if (retval != ACK) + return -EPIPE; + + *bytes_written = length; + + if (send_command(board, USB_GPIB_UNLISTEN, sizeof(USB_GPIB_UNLISTEN)) + != 0x06) + return -EPIPE; + + return length; +} + +/* + * *** following functions not implemented yet *** + */ + +/* parallel_poll configure */ + +static void usb_gpib_parallel_poll_configure(gpib_board_t *board, + uint8_t configuration) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); +} + +/* parallel_poll_response */ + +static void usb_gpib_parallel_poll_response(gpib_board_t *board, int ist) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); +} + +/* primary_address */ + +static int usb_gpib_primary_address(gpib_board_t *board, unsigned int address) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); + return 0; +} + +/* return_to_local */ + +static void usb_gpib_return_to_local(gpib_board_t *board) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); +} + +/* secondary_address */ + +static int usb_gpib_secondary_address(gpib_board_t *board, + unsigned int address, + int enable) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); + return 0; +} + +/* serial_poll_response */ + +static void usb_gpib_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); +} + +/* serial_poll_status */ + +static uint8_t usb_gpib_serial_poll_status(gpib_board_t *board) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); + return 0; +} + +/* t1_delay */ + +static unsigned int usb_gpib_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + dev_alert(board->gpib_dev, "%s:%s - currently a NOP\n", NAME, __func__); + return 0; +} + +/* + * *** module dispatch table and init/exit functions *** + */ + +gpib_interface_t usb_gpib_interface = { +name: NAME, +attach : usb_gpib_attach, +detach : usb_gpib_detach, +read : usb_gpib_read, +write : usb_gpib_write, +command : usb_gpib_command, +take_control : usb_gpib_take_control, +go_to_standby : usb_gpib_go_to_standby, +request_system_control : usb_gpib_request_system_control, +interface_clear : usb_gpib_interface_clear, +remote_enable : usb_gpib_remote_enable, +enable_eos : usb_gpib_enable_eos, +disable_eos : usb_gpib_disable_eos, +parallel_poll : usb_gpib_parallel_poll, +parallel_poll_configure : usb_gpib_parallel_poll_configure, +parallel_poll_response : usb_gpib_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : usb_gpib_line_status, +update_status : usb_gpib_update_status, +primary_address : usb_gpib_primary_address, +secondary_address : usb_gpib_secondary_address, +serial_poll_response : usb_gpib_serial_poll_response, +serial_poll_status : usb_gpib_serial_poll_status, +t1_delay : usb_gpib_t1_delay, +return_to_local : usb_gpib_return_to_local, +skip_check_for_command_acceptors : 1 +}; + +/* + * usb_gpib_init_module(), usb_gpib_exit_module() + * + * This functions are called every time a new device is detected + * and registered or is removed and unregistered. + * We must take note of created and destroyed usb minors to be used + * when usb_gpib_attach() and usb_gpib_detach() will be called on + * request by gpib_config. + */ + +static int usb_gpib_init_module(struct usb_interface *interface) +{ + int j, mask, rv; + + rv = mutex_lock_interruptible(&minors_lock); + if (rv < 0) + return rv; + + if (!assigned_usb_minors) { + gpib_register_driver(&usb_gpib_interface, THIS_MODULE); + } else { + /* check if minor is already registered - maybe useless, but if + * it happens the code is inconsistent somewhere + */ + + for (j = 0 ; j < MAX_DEV ; j++) { + if (usb_minors[j] == interface->minor && assigned_usb_minors & 1 << j) { + pr_alert("%s:%s - CODE BUG: USB minor %d registered at %d.\n", + NAME, __func__, interface->minor, j); + rv = -1; + goto exit; + } + } + } + + /* find a free slot */ + + for (j = 0 ; j < MAX_DEV ; j++) { + mask = 1 << j; + if ((assigned_usb_minors & mask) == 0) { + usb_minors[j] = interface->minor; + lpvo_usb_interfaces[j] = interface; + assigned_usb_minors |= mask; + DIA_LOG(0, "usb minor %d registered at %d\n", interface->minor, j); + rv = 0; + goto exit; + } + } + pr_alert("%s:%s - No slot available for interface %p minor %d\n", + NAME, __func__, interface, interface->minor); + rv = -1; + +exit: + mutex_unlock(&minors_lock); + return rv; +} + +static void usb_gpib_exit_module(int minor) +{ + int j; + + mutex_lock(&minors_lock); + for (j = 0 ; j < MAX_DEV ; j++) { + if (usb_minors[j] == minor && assigned_usb_minors & 1 << j) { + assigned_usb_minors &= ~(1 << j); + usb_minors[j] = -1; + if (assigned_usb_minors == 0) + gpib_unregister_driver(&usb_gpib_interface); + goto exit; + } + } + pr_alert("%s:%s - CODE BUG: USB minor %d not found.\n", NAME, __func__, minor); + +exit: + mutex_unlock(&minors_lock); +} + +/* + * Default latency time (16 msec) is too long. + * We must use 1 msec (best); anyhow, no more than 5 msec. + * + * Defines and function taken and modified from the kernel tree + * (see ftdi_sio.h and ftdi_sio.c). + * + */ + +#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 +#define WDR_TIMEOUT 5000 /* default urb timeout */ +#define WDR_SHORT_TIMEOUT 1000 /* shorter urb timeout */ + +#define LATENCY_TIMER 1 /* use a small latency timer: 1 ... 5 msec */ +#define LATENCY_CHANNEL 0 /* channel selection in multichannel devices */ +static int write_latency_timer(struct usb_device *udev) +{ + int rv = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + FTDI_SIO_SET_LATENCY_TIMER_REQUEST, + FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, + LATENCY_TIMER, LATENCY_CHANNEL, + NULL, 0, WDR_TIMEOUT); + if (rv < 0) + pr_alert("Unable to write latency timer: %i\n", rv); + return rv; +} + +/***************************************************************************** + * * + * The following code is a modified version of the USB Skeleton driver * + * written by Greg Kroah-Hartman and available in the kernel tree. * + * * + * Functions skel_open() and skel_release() have been rewritten and named * + * skel_do_open() and skel_do_release() to process the attach and detach * + * requests coming from gpib_config. * + * * + * Functions skel_read() and skel_write() have been split into a * + * skel_do_read() and skel_do_write(), that cover the kernel stuff of read * + * and write operations, and the original skel_read() and skel_write(), * + * that handle communication with user space and call their _do_ companion. * + * * + * Only the _do_ versions are used by the lpvo_usb_gpib driver; other ones * + * can be (optionally) maintained in the compilation to have direct access * + * to a gpib controller for debug and diagnostics. * + * * + * To avoid collisions in names, devices in user space have been renamed * + * lpvo_raw1, lpvo_raw2 .... and the usb driver has been renamed with the * + * gpib module name. * + * * + *****************************************************************************/ + +/* + * USB Skeleton driver - 2.2 + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c + * but has been rewritten to be easier to read and use. + */ + +#include +#include +#include +#include + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_SKEL_MINOR_BASE 192 + +/* private defines */ + +#define MAX_TRANSFER (PAGE_SIZE - 512) +/* MAX_TRANSFER is chosen so that the VM is not stressed by + * allocations > PAGE_SIZE and the number of packets in a page + * is an integer 512 is the largest possible packet on EHCI + */ + +#define WRITES_IN_FLIGHT 1 /* we do not want more than one pending write */ +#define USER_DEVICE 1 /* compile for device(s) in user space */ + +/* Structure to hold all of our device specific stuff */ +struct usb_skel { + struct usb_device *udev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + struct semaphore limit_sem; /* limiting the number of writes in progress */ + struct usb_anchor submitted; /* in case need to retract our submissions */ + struct urb *bulk_in_urb; /* the urb to read data with */ + unsigned char *bulk_in_buffer; /* the buffer to receive data */ + size_t bulk_in_size; /* the size of the receive buffer */ + size_t bulk_in_filled; /* number of bytes in the buffer */ + size_t bulk_in_copied; /* already copied to user space */ + __u8 bulk_in_endpoint_addr; /* the address of the bulk in endpoint */ + __u8 bulk_out_endpoint_addr; /* the address of the bulk out endpoint */ + int errors; /* the last request tanked */ + bool ongoing_read; /* a read is going on */ + spinlock_t err_lock; /* lock for errors */ + struct kref kref; + struct mutex io_mutex; /* synchronize I/O with disconnect */ + wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ +}; + +#define to_skel_dev(d) container_of(d, struct usb_skel, kref) + +static struct usb_driver skel_driver; +static void skel_draw_down(struct usb_skel *dev); + +static void skel_delete(struct kref *kref) +{ + struct usb_skel *dev = to_skel_dev(kref); + + usb_free_urb(dev->bulk_in_urb); + usb_put_dev(dev->udev); + kfree(dev->bulk_in_buffer); + kfree(dev); +} + +/* + * skel_do_open() - to be called by usb_gpib_attach + */ + +static int skel_do_open(gpib_board_t *board, int subminor) +{ + struct usb_skel *dev; + struct usb_interface *interface; + int retval = 0; + + DIA_LOG(0, "Required minor: %d\n", subminor); + + interface = usb_find_interface(&skel_driver, subminor); + if (!interface) { + pr_err("%s - error, can't find device for minor %d\n", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit; + } + + retval = usb_autopm_get_interface(interface); + if (retval) + goto exit; + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + /* save our object in the file's private structure */ + GPIB_DEV = dev; + +exit: + return retval; +} + +/* + * skel_do_release() - to be called by usb_gpib_detach + */ + +static int skel_do_release(gpib_board_t *board) +{ + struct usb_skel *dev; + + dev = GPIB_DEV; + if (!dev) + return -ENODEV; + + /* allow the device to be autosuspended */ + mutex_lock(&dev->io_mutex); + if (dev->interface) + usb_autopm_put_interface(dev->interface); + mutex_unlock(&dev->io_mutex); + + /* decrement the count on our device */ + kref_put(&dev->kref, skel_delete); + return 0; +} + +/* + * read functions + */ + +static void skel_read_bulk_callback(struct urb *urb) +{ + struct usb_skel *dev; + unsigned long flags; + + dev = urb->context; + + spin_lock_irqsave(&dev->err_lock, flags); + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + dev_err(&dev->interface->dev, + "%s - nonzero read bulk status received: %d\n", + __func__, urb->status); + + dev->errors = urb->status; + } else { + dev->bulk_in_filled = urb->actual_length; + } + dev->ongoing_read = 0; + spin_unlock_irqrestore(&dev->err_lock, flags); + + wake_up_interruptible(&dev->bulk_in_wait); +} + +static int skel_do_read_io(struct usb_skel *dev, size_t count) +{ + int rv; + + /* prepare a read */ + usb_fill_bulk_urb(dev->bulk_in_urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpoint_addr), + dev->bulk_in_buffer, + min(dev->bulk_in_size, count), + skel_read_bulk_callback, + dev); + /* tell everybody to leave the URB alone */ + spin_lock_irq(&dev->err_lock); + dev->ongoing_read = 1; + spin_unlock_irq(&dev->err_lock); + + /* submit bulk in urb, which means no data to deliver */ + dev->bulk_in_filled = 0; + dev->bulk_in_copied = 0; + + /* do it */ + rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); + if (rv < 0) { + dev_err(&dev->interface->dev, + "%s - failed submitting read urb, error %d\n", + __func__, rv); + rv = (rv == -ENOMEM) ? rv : -EIO; + spin_lock_irq(&dev->err_lock); + dev->ongoing_read = 0; + spin_unlock_irq(&dev->err_lock); + } + + return rv; +} + +/* + * skel_do_read() - read operations from lpvo_usb_gpib + */ + +static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) +{ + int rv; + bool ongoing_io; + + /* if we cannot read at all, return EOF */ + + if (!dev->bulk_in_urb || !count) + return 0; + + DIA_LOG(1, "enter for %zu.\n", count); + +restart: /* added to comply with ftdi timeout technique */ + + /* no concurrent readers */ + + DIA_LOG(2, "restart with %zd %zd.\n", dev->bulk_in_filled, dev->bulk_in_copied); + + rv = mutex_lock_interruptible(&dev->io_mutex); + if (rv < 0) + return rv; + + if (!dev->interface) { /* disconnect() was called */ + rv = -ENODEV; + goto exit; + } + +retry: + /* if IO is under way, we must not touch things */ + spin_lock_irq(&dev->err_lock); + ongoing_io = dev->ongoing_read; + spin_unlock_irq(&dev->err_lock); + + DIA_LOG(2, "retry with %d.\n", ongoing_io); + + if (ongoing_io) { +// /* nonblocking IO shall not wait */ +// /* no file, no O_NONBLOCK; maybe provide when from user space */ +// if (file->f_flags & O_NONBLOCK) { +// rv = -EAGAIN; +// goto exit; +// } + + /* + * IO may take forever + * hence wait in an interruptible state + */ + rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read)); + if (rv < 0) + goto exit; + } + + /* errors must be reported */ + rv = dev->errors; + if (rv < 0) { + /* any error is reported once */ + dev->errors = 0; + /* to preserve notifications about reset */ + rv = (rv == -EPIPE) ? rv : -EIO; + /* report it */ + goto exit; + } + + /* + * if the buffer is filled we may satisfy the read + * else we need to start IO + */ + + if (dev->bulk_in_filled) { + /* we had read data */ + + size_t available = dev->bulk_in_filled - dev->bulk_in_copied; +// size_t chunk = min(available, count); /* compute chunk later */ + size_t chunk; + + DIA_LOG(2, "we have data: %zu %zu.\n", dev->bulk_in_filled, dev->bulk_in_copied); + + if (!available) { + /* + * all data has been used + * actual IO needs to be done + */ + /* it seems that requests for less than dev->bulk_in_size + * are not accepted + */ + rv = skel_do_read_io(dev, dev->bulk_in_size); + if (rv < 0) + goto exit; + else + goto retry; + } + + /* + * data is available - chunk tells us how much shall be copied + */ + + /* Condition dev->bulk_in_copied > 0 maybe will never happen. In case, + * signal the event and copy using the original procedure, i.e., copy + * first two bytes also + */ + + if (dev->bulk_in_copied) { + int j; + + for (j = 0 ; j < dev->bulk_in_filled ; j++) { + pr_alert("copy -> %x %zu %x\n", + j, dev->bulk_in_copied, dev->bulk_in_buffer[j]); + } + chunk = min(available, count); + memcpy(buffer, dev->bulk_in_buffer + dev->bulk_in_copied, chunk); + rv = chunk; + dev->bulk_in_copied += chunk; + + /* copy discarding first two bytes that contain ftdi chip status */ + + } else { + /* account for two bytes to be discarded */ + chunk = min(available, count + 2); + if (chunk < 2) { + pr_alert("BAD READ - chunk: %zu\n", chunk); + rv = -EIO; + goto exit; + } + + memcpy(buffer, dev->bulk_in_buffer + 2, chunk - 2); + rv = chunk; + dev->bulk_in_copied += chunk; + } + + /* + * if we are asked for more than we have, + * we start IO but don't wait + * + * No, no read ahead allowed; if the case, more data will be + * asked for by the lpvo_usb_gpib layer. + */ +// if (available < count) +// skel_do_read_io(dev, dev->bulk_in_size); + } else { + DIA_LOG(1, "no data - start read - copied: %zd.\n", dev->bulk_in_copied); + + /* no data in the buffer */ + rv = skel_do_read_io(dev, dev->bulk_in_size); + if (rv < 0) + goto exit; + else + goto retry; + } +exit: + mutex_unlock(&dev->io_mutex); + if (rv == 2) + goto restart; /* ftdi chip returns two status bytes after a latency anyhow */ + DIA_LOG(1, "exit with %d.\n", rv); + if (rv > 0) + return rv - 2; /* account for 2 discarded bytes in a valid buffer */ + return rv; +} + +/* + * write functions + */ + +static void skel_write_bulk_callback(struct urb *urb) +{ + struct usb_skel *dev; + unsigned long flags; + + dev = urb->context; + + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + dev_err(&dev->interface->dev, + "%s - nonzero write bulk status received: %d\n", + __func__, urb->status); + + spin_lock_irqsave(&dev->err_lock, flags); + dev->errors = urb->status; + spin_unlock_irqrestore(&dev->err_lock, flags); + } + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + up(&dev->limit_sem); +} + +/* + * skel_do_write() - write operations from lpvo_usb_gpib + */ + +static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count) +{ + int retval = 0; + struct urb *urb = NULL; + char *buf = NULL; + size_t writesize = min_t(size_t, count, (size_t)MAX_TRANSFER); + + /* verify that we actually have some data to write */ + if (count == 0) + goto exit; + + /* + * limit the number of URBs in flight to stop a user from using up all + * RAM + */ + /* Only one URB is used, because we can't have a pending write() and go on */ + +// if (!(file->f_flags & O_NONBLOCK)) { /* no NONBLOCK provided */ + if (down_interruptible(&dev->limit_sem)) { + retval = -ERESTARTSYS; + goto exit; + } +// } else { +// if (down_trylock(&dev->limit_sem)) { +// retval = -EAGAIN; +// goto exit; +// } +// } + + spin_lock_irq(&dev->err_lock); + retval = dev->errors; + if (retval < 0) { + /* any error is reported once */ + dev->errors = 0; + /* to preserve notifications about reset */ + retval = (retval == -EPIPE) ? retval : -EIO; + } + spin_unlock_irq(&dev->err_lock); + if (retval < 0) + goto error; + + /* create a urb, and a buffer for it, and copy the data to the urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } + + buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + goto error; + } + + memcpy(buf, buffer, count); + + /* this lock makes sure we don't submit URBs to gone devices */ + mutex_lock(&dev->io_mutex); + if (!dev->interface) { /* disconnect() was called */ + mutex_unlock(&dev->io_mutex); + retval = -ENODEV; + goto error; + } + + /* initialize the urb properly */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr), + buf, writesize, skel_write_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->submitted); + + /* send the data out the bulk port */ + retval = usb_submit_urb(urb, GFP_KERNEL); + mutex_unlock(&dev->io_mutex); + if (retval) { + dev_err(&dev->interface->dev, + "%s - failed submitting write urb, error %d\n", + __func__, retval); + goto error_unanchor; + } + + /* + * release our reference to this urb, the USB core will eventually free + * it entirely + */ + usb_free_urb(urb); + + return writesize; + +error_unanchor: + usb_unanchor_urb(urb); +error: + if (urb) { + usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma); + usb_free_urb(urb); + } + up(&dev->limit_sem); + +exit: + return retval; +} + +/* + * services for the user space devices + */ + +#if USER_DEVICE /* conditional compilation of user space device */ + +static int skel_flush(struct file *file, fl_owner_t id) +{ + struct usb_skel *dev; + int res; + + dev = file->private_data; + if (!dev) + return -ENODEV; + + /* wait for io to stop */ + mutex_lock(&dev->io_mutex); + skel_draw_down(dev); + + /* read out errors, leave subsequent opens a clean slate */ + spin_lock_irq(&dev->err_lock); + res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; + dev->errors = 0; + spin_unlock_irq(&dev->err_lock); + + mutex_unlock(&dev->io_mutex); + + return res; +} + +static int skel_open(struct inode *inode, struct file *file) +{ + struct usb_skel *dev; + struct usb_interface *interface; + int subminor; + int retval = 0; + + subminor = iminor(inode); + + interface = usb_find_interface(&skel_driver, subminor); + if (!interface) { + pr_err("%s - error, can't find device for minor %d\n", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit; + } + + retval = usb_autopm_get_interface(interface); + if (retval) + goto exit; + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + /* save our object in the file's private structure */ + file->private_data = dev; + +exit: + return retval; +} + +static int skel_release(struct inode *inode, struct file *file) +{ + struct usb_skel *dev; + + dev = file->private_data; + if (!dev) + return -ENODEV; + + /* allow the device to be autosuspended */ + mutex_lock(&dev->io_mutex); + if (dev->interface) + usb_autopm_put_interface(dev->interface); + mutex_unlock(&dev->io_mutex); + + /* decrement the count on our device */ + kref_put(&dev->kref, skel_delete); + return 0; +} + +/* + * user space access to read function + */ + +static ssize_t skel_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct usb_skel *dev; + char *buf; + ssize_t rv; + + dev = file->private_data; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rv = skel_do_read(dev, buf, count); + + pr_alert("%s - return with %zu\n", __func__, rv); + + if (rv > 0) { + if (copy_to_user(buffer, buf, rv)) { + kfree(buf); + return -EFAULT; + } + } + kfree(buf); + return rv; +} + +/* + * user space access to write function + */ + +static ssize_t skel_write(struct file *file, const char *user_buffer, + size_t count, loff_t *ppos) +{ + struct usb_skel *dev; + char *buf; + ssize_t rv; + + dev = file->private_data; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buffer, count)) { + kfree(buf); + return -EFAULT; + } + + rv = skel_do_write(dev, buf, count); + kfree(buf); + return rv; +} +#endif + +static const struct file_operations skel_fops = { + .owner = THIS_MODULE, +#if USER_DEVICE + .read = skel_read, + .write = skel_write, + .open = skel_open, + .release = skel_release, + .flush = skel_flush, + .llseek = noop_llseek, +#endif +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with the driver core + */ +#if USER_DEVICE +static struct usb_class_driver skel_class = { + .name = "lpvo_raw%d", + .fops = &skel_fops, + .minor_base = USB_SKEL_MINOR_BASE, +}; +#endif + +static int skel_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_skel *dev; + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + int retval; + char *device_path; + + mutex_init(&minors_lock); /* required for handling minor numbers table */ + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + kref_init(&dev->kref); + sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); + mutex_init(&dev->io_mutex); + spin_lock_init(&dev->err_lock); + init_usb_anchor(&dev->submitted); + init_waitqueue_head(&dev->bulk_in_wait); + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + retval = usb_find_common_endpoints(interface->cur_altsetting, + &bulk_in, &bulk_out, NULL, NULL); + if (retval) { + dev_err(&interface->dev, + "Could not find both bulk-in and bulk-out endpoints\n"); + goto error; + } + + dev->bulk_in_size = usb_endpoint_maxp(bulk_in); + dev->bulk_in_endpoint_addr = bulk_in->bEndpointAddress; + dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL); + if (!dev->bulk_in_buffer) { + retval = -ENOMEM; + goto error; + } + dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->bulk_in_urb) { + retval = -ENOMEM; + goto error; + } + + dev->bulk_out_endpoint_addr = bulk_out->bEndpointAddress; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* let the world know */ + + device_path = kobject_get_path(&dev->udev->dev.kobj, GFP_KERNEL); + pr_alert("%s:%s - New lpvo_usb_device -> bus: %d dev: %d path: %s\n", NAME, __func__, + dev->udev->bus->busnum, dev->udev->devnum, device_path); + kfree(device_path); + +#if USER_DEVICE + /* we can register the device now, as it is ready */ + retval = usb_register_dev(interface, &skel_class); + if (retval) { + /* something prevented us from registering this driver */ + dev_err(&interface->dev, + "Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto error; + } + + /* let the user know what node this device is now attached to */ + dev_info(&interface->dev, + "lpvo_usb_gpib device now attached to lpvo_raw%d", + interface->minor); +#endif + + write_latency_timer(dev->udev); /* adjust the latency timer */ + + usb_gpib_init_module(interface); /* last, init the lpvo for this minor */ + + return 0; + +error: + /* this frees allocated memory */ + kref_put(&dev->kref, skel_delete); + + return retval; +} + +static void skel_disconnect(struct usb_interface *interface) +{ + struct usb_skel *dev; + int minor = interface->minor; + + usb_gpib_exit_module(minor); /* first, disactivate the lpvo */ + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + +#if USER_DEVICE + /* give back our minor */ + usb_deregister_dev(interface, &skel_class); +#endif + + /* prevent more I/O from starting */ + mutex_lock(&dev->io_mutex); + dev->interface = NULL; + mutex_unlock(&dev->io_mutex); + + usb_kill_anchored_urbs(&dev->submitted); + + /* decrement our usage count */ + kref_put(&dev->kref, skel_delete); + + dev_info(&interface->dev, "USB lpvo_raw #%d now disconnected", minor); +} + +static void skel_draw_down(struct usb_skel *dev) +{ + int time; + + time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); + if (!time) + usb_kill_anchored_urbs(&dev->submitted); + usb_kill_urb(dev->bulk_in_urb); +} + +static int skel_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + if (!dev) + return 0; + skel_draw_down(dev); + return 0; +} + +static int skel_resume(struct usb_interface *intf) +{ + return 0; +} + +static int skel_pre_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + mutex_lock(&dev->io_mutex); + skel_draw_down(dev); + + return 0; +} + +static int skel_post_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + /* we are sure no URBs are active - no locking needed */ + dev->errors = -EPIPE; + mutex_unlock(&dev->io_mutex); + + return 0; +} + +static struct usb_driver skel_driver = { + .name = NAME, + .probe = skel_probe, + .disconnect = skel_disconnect, + .suspend = skel_suspend, + .resume = skel_resume, + .pre_reset = skel_pre_reset, + .post_reset = skel_post_reset, + .id_table = skel_table, + .supports_autosuspend = 1, +}; + +module_usb_driver(skel_driver); diff --git a/drivers/staging/gpib/nec7210/Makefile b/drivers/staging/gpib/nec7210/Makefile new file mode 100644 index 0000000000000..8d4d90f211092 --- /dev/null +++ b/drivers/staging/gpib/nec7210/Makefile @@ -0,0 +1,4 @@ + +obj-m += nec7210.o + + diff --git a/drivers/staging/gpib/nec7210/board.h b/drivers/staging/gpib/nec7210/board.h new file mode 100644 index 0000000000000..ac3fe38ade572 --- /dev/null +++ b/drivers/staging/gpib/nec7210/board.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_PCIIA_BOARD_H +#define _GPIB_PCIIA_BOARD_H + +#include "gpibP.h" +#include +#include +#include +#include + +#include "nec7210.h" + +#endif //_GPIB_PCIIA_BOARD_H + diff --git a/drivers/staging/gpib/nec7210/nec7210.c b/drivers/staging/gpib/nec7210/nec7210.c new file mode 100644 index 0000000000000..1d99510354974 --- /dev/null +++ b/drivers/staging/gpib/nec7210/nec7210.c @@ -0,0 +1,1134 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "board.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB library code for NEC uPD7210"); + +int nec7210_enable_eos(gpib_board_t *board, struct nec7210_priv *priv, uint8_t eos_byte, + int compare_8_bits) +{ + write_byte(priv, eos_byte, EOSR); + priv->auxa_bits |= HR_REOS; + if (compare_8_bits) + priv->auxa_bits |= HR_BIN; + else + priv->auxa_bits &= ~HR_BIN; + write_byte(priv, priv->auxa_bits, AUXMR); + return 0; +} +EXPORT_SYMBOL(nec7210_enable_eos); + +void nec7210_disable_eos(gpib_board_t *board, struct nec7210_priv *priv) +{ + priv->auxa_bits &= ~HR_REOS; + write_byte(priv, priv->auxa_bits, AUXMR); +} +EXPORT_SYMBOL(nec7210_disable_eos); + +int nec7210_parallel_poll(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *result) +{ + int ret; + + clear_bit(COMMAND_READY_BN, &priv->state); + + // execute parallel poll + write_byte(priv, AUX_EPP, AUXMR); + // wait for result FIXME: support timeouts + ret = wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state)); + if (ret) { + dev_dbg(board->gpib_dev, "gpib: parallel poll interrupted\n"); + return -ERESTARTSYS; + } + *result = read_byte(priv, CPTR); + + return 0; +} +EXPORT_SYMBOL(nec7210_parallel_poll); + +void nec7210_parallel_poll_configure(gpib_board_t *board, + struct nec7210_priv *priv, unsigned int configuration) +{ + write_byte(priv, PPR | configuration, AUXMR); +} +EXPORT_SYMBOL(nec7210_parallel_poll_configure); + +void nec7210_parallel_poll_response(gpib_board_t *board, struct nec7210_priv *priv, int ist) +{ + if (ist) + write_byte(priv, AUX_SPPF, AUXMR); + else + write_byte(priv, AUX_CPPF, AUXMR); +} +EXPORT_SYMBOL(nec7210_parallel_poll_response); +/* This is really only adequate for chips that do a 488.2 style reqt/reqf + * based on bit 6 of the SPMR (see chapter 11.3.3 of 488.2). For simpler chips that simply + * set rsv directly based on bit 6, we either need to do more hardware setup to expose + * the 488.2 capability (for example with NI chips), or we need to implement the + * 488.2 set srv state machine in the driver (if that is even viable). + */ +void nec7210_serial_poll_response(gpib_board_t *board, struct nec7210_priv *priv, uint8_t status) +{ + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + if (status & request_service_bit) { + priv->srq_pending = 1; + clear_bit(SPOLL_NUM, &board->status); + + } else { + priv->srq_pending = 0; + } + write_byte(priv, status, SPMR); + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(nec7210_serial_poll_response); + +uint8_t nec7210_serial_poll_status(gpib_board_t *board, struct nec7210_priv *priv) +{ + return read_byte(priv, SPSR); +} +EXPORT_SYMBOL(nec7210_serial_poll_status); + +int nec7210_primary_address(const gpib_board_t *board, struct nec7210_priv *priv, + unsigned int address) +{ + // put primary address in address0 + write_byte(priv, address & ADDRESS_MASK, ADR); + return 0; +} +EXPORT_SYMBOL(nec7210_primary_address); + +int nec7210_secondary_address(const gpib_board_t *board, struct nec7210_priv *priv, + unsigned int address, int enable) +{ + if (enable) { + // put secondary address in address1 + write_byte(priv, HR_ARS | (address & ADDRESS_MASK), ADR); + // go to address mode 2 + priv->reg_bits[ADMR] &= ~HR_ADM0; + priv->reg_bits[ADMR] |= HR_ADM1; + } else { + // disable address1 register + write_byte(priv, HR_ARS | HR_DT | HR_DL, ADR); + // go to address mode 1 + priv->reg_bits[ADMR] |= HR_ADM0; + priv->reg_bits[ADMR] &= ~HR_ADM1; + } + write_byte(priv, priv->reg_bits[ADMR], ADMR); + return 0; +} +EXPORT_SYMBOL(nec7210_secondary_address); + +static void update_talker_state(struct nec7210_priv *priv, unsigned int address_status_bits) +{ + if ((address_status_bits & HR_TA)) { + if ((address_status_bits & HR_NATN)) { + if (address_status_bits & HR_SPMS) + priv->talker_state = serial_poll_active; + else + priv->talker_state = talker_active; + } else { + priv->talker_state = talker_addressed; + } + } else { + priv->talker_state = talker_idle; + } +} + +static void update_listener_state(struct nec7210_priv *priv, unsigned int address_status_bits) +{ + if (address_status_bits & HR_LA) { + if ((address_status_bits & HR_NATN)) + priv->listener_state = listener_active; + else + priv->listener_state = listener_addressed; + } else { + priv->listener_state = listener_idle; + } +} + +unsigned int nec7210_update_status_nolock(gpib_board_t *board, struct nec7210_priv *priv) +{ + int address_status_bits; + u8 spoll_status; + + if (!priv) + return 0; + + address_status_bits = read_byte(priv, ADSR); + if (address_status_bits & HR_CIC) + set_bit(CIC_NUM, &board->status); + else + clear_bit(CIC_NUM, &board->status); + // check for talker/listener addressed + update_talker_state(priv, address_status_bits); + if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + update_listener_state(priv, address_status_bits); + if (priv->listener_state == listener_active || + priv->listener_state == listener_addressed) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + if (address_status_bits & HR_NATN) + clear_bit(ATN_NUM, &board->status); + else + set_bit(ATN_NUM, &board->status); + spoll_status = nec7210_serial_poll_status(board, priv); + if (priv->srq_pending && (spoll_status & request_service_bit) == 0) { + priv->srq_pending = 0; + set_bit(SPOLL_NUM, &board->status); + } +// dev_dbg(board->gpib_dev, "status 0x%x, state 0x%x\n", board->status, priv->state); + + /* we rely on the interrupt handler to set the + * rest of the status bits + */ + + return board->status; +} +EXPORT_SYMBOL(nec7210_update_status_nolock); + +unsigned int nec7210_update_status(gpib_board_t *board, struct nec7210_priv *priv, + unsigned int clear_mask) +{ + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&board->spinlock, flags); + board->status &= ~clear_mask; + retval = nec7210_update_status_nolock(board, priv); + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} +EXPORT_SYMBOL(nec7210_update_status); + +unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg, + unsigned int mask, unsigned int bits) +{ + priv->reg_bits[reg] &= ~mask; + priv->reg_bits[reg] |= mask & bits; + write_byte(priv, priv->reg_bits[reg], reg); + return priv->reg_bits[reg]; +} +EXPORT_SYMBOL(nec7210_set_reg_bits); + +void nec7210_set_handshake_mode(gpib_board_t *board, struct nec7210_priv *priv, int mode) +{ + unsigned long flags; + + mode &= HR_HANDSHAKE_MASK; + + spin_lock_irqsave(&board->spinlock, flags); + if ((priv->auxa_bits & HR_HANDSHAKE_MASK) != mode) { + priv->auxa_bits &= ~HR_HANDSHAKE_MASK; + priv->auxa_bits |= mode; + write_byte(priv, priv->auxa_bits, AUXMR); + } + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(nec7210_set_handshake_mode); + +uint8_t nec7210_read_data_in(gpib_board_t *board, struct nec7210_priv *priv, int *end) +{ + unsigned long flags; + u8 data; + + spin_lock_irqsave(&board->spinlock, flags); + data = read_byte(priv, DIR); + clear_bit(READ_READY_BN, &priv->state); + if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) + *end = 1; + else + *end = 0; + spin_unlock_irqrestore(&board->spinlock, flags); + + return data; +} +EXPORT_SYMBOL(nec7210_read_data_in); + +int nec7210_take_control(gpib_board_t *board, struct nec7210_priv *priv, int syncronous) +{ + int i; + const int timeout = 100; + int retval = 0; + unsigned int adsr_bits = 0; + + if (syncronous) + write_byte(priv, AUX_TCS, AUXMR); + else + write_byte(priv, AUX_TCA, AUXMR); + // busy wait until ATN is asserted + for (i = 0; i < timeout; i++) { + adsr_bits = read_byte(priv, ADSR); + if ((adsr_bits & HR_NATN) == 0) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + + clear_bit(WRITE_READY_BN, &priv->state); + + return retval; +} +EXPORT_SYMBOL(nec7210_take_control); + +int nec7210_go_to_standby(gpib_board_t *board, struct nec7210_priv *priv) +{ + int i; + const int timeout = 1000; + unsigned int adsr_bits = 0; + int retval = 0; + + write_byte(priv, AUX_GTS, AUXMR); + // busy wait until ATN is released + for (i = 0; i < timeout; i++) { + adsr_bits = read_byte(priv, ADSR); + if (adsr_bits & HR_NATN) + break; + udelay(1); + } + // if busy wait has failed, try sleeping + if (i == timeout) { + for (i = 0; i < HZ; i++) { + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(1)) + return -ERESTARTSYS; + adsr_bits = read_byte(priv, ADSR); + if (adsr_bits & HR_NATN) + break; + } + if (i == HZ) { + pr_err("nec7210: error waiting for NATN\n"); + return -ETIMEDOUT; + } + } + + clear_bit(COMMAND_READY_BN, &priv->state); + return retval; +} +EXPORT_SYMBOL(nec7210_go_to_standby); + +void nec7210_request_system_control(gpib_board_t *board, struct nec7210_priv *priv, + int request_control) +{ + if (request_control == 0) { + write_byte(priv, AUX_CREN, AUXMR); + write_byte(priv, AUX_CIFC, AUXMR); + write_byte(priv, AUX_DSC, AUXMR); + } +} +EXPORT_SYMBOL(nec7210_request_system_control); + +void nec7210_interface_clear(gpib_board_t *board, struct nec7210_priv *priv, int assert) +{ + if (assert) + write_byte(priv, AUX_SIFC, AUXMR); + else + write_byte(priv, AUX_CIFC, AUXMR); +} +EXPORT_SYMBOL(nec7210_interface_clear); + +void nec7210_remote_enable(gpib_board_t *board, struct nec7210_priv *priv, int enable) +{ + if (enable) + write_byte(priv, AUX_SREN, AUXMR); + else + write_byte(priv, AUX_CREN, AUXMR); +} +EXPORT_SYMBOL(nec7210_remote_enable); + +void nec7210_release_rfd_holdoff(gpib_board_t *board, struct nec7210_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + if (test_bit(RFD_HOLDOFF_BN, &priv->state) && + test_bit(READ_READY_BN, &priv->state) == 0) { + write_byte(priv, AUX_FH, AUXMR); + clear_bit(RFD_HOLDOFF_BN, &priv->state); + } + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(nec7210_release_rfd_holdoff); + +unsigned int nec7210_t1_delay(gpib_board_t *board, struct nec7210_priv *priv, + unsigned int nano_sec) +{ + unsigned int retval; + + if (nano_sec <= 500) { + priv->auxb_bits |= HR_TRI; + retval = 500; + } else { + priv->auxb_bits &= ~HR_TRI; + retval = 2000; + } + write_byte(priv, priv->auxb_bits, AUXMR); + + return retval; +} +EXPORT_SYMBOL(nec7210_t1_delay); + +void nec7210_return_to_local(const gpib_board_t *board, struct nec7210_priv *priv) +{ + write_byte(priv, AUX_RTL, AUXMR); +} +EXPORT_SYMBOL(nec7210_return_to_local); + +static inline short nec7210_atn_has_changed(gpib_board_t *board, struct nec7210_priv *priv) +{ + short address_status_bits = read_byte(priv, ADSR); + + if (address_status_bits & HR_NATN) { + if (test_bit(ATN_NUM, &board->status)) + return 1; + else + return 0; + } else { + if (test_bit(ATN_NUM, &board->status)) + return 0; + else + return 1; + } + return -1; +} + +int nec7210_command(gpib_board_t *board, struct nec7210_priv *priv, uint8_t + *buffer, size_t length, size_t *bytes_written) +{ + int retval = 0; + unsigned long flags; + + *bytes_written = 0; + + clear_bit(BUS_ERROR_BN, &priv->state); + + while (*bytes_written < length) { + if (wait_event_interruptible(board->wait, + test_bit(COMMAND_READY_BN, &priv->state) || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib command wait interrupted\n"); + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) + break; + if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) { + pr_err("nec7210: bus error on command byte\n"); + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(COMMAND_READY_BN, &priv->state); + write_byte(priv, buffer[*bytes_written], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + + ++(*bytes_written); + + if (need_resched()) + schedule(); + } + // wait for last byte to get sent + if (wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state) || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib command wait interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) { + dev_dbg(board->gpib_dev, "gpib command timed out\n"); + retval = -ETIMEDOUT; + } + if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) { + pr_err("nec7210: bus error on command byte\n"); + retval = -EIO; + } + + return retval; +} +EXPORT_SYMBOL(nec7210_command); + +static int pio_read(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + + *bytes_read = 0; + *end = 0; + + while (*bytes_read < length) { + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "nec7210: pio read wait interrupted\n"); + retval = -ERESTARTSYS; + break; + } + if (test_bit(READ_READY_BN, &priv->state)) { + if (*bytes_read == 0) { + /* We set the handshake mode here because we know + * no new bytes will arrive (it has already arrived + * and is awaiting being read out of the chip) while we are changing + * modes. This ensures we can reliably keep track + * of the holdoff state. + */ + nec7210_set_handshake_mode(board, priv, HR_HLDA); + } + buffer[(*bytes_read)++] = nec7210_read_data_in(board, priv, end); + if (*end) + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + dev_dbg(board->gpib_dev, "interrupted by timeout\n"); + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &priv->state)) { + dev_dbg(board->gpib_dev, "interrupted by device clear\n"); + retval = -EINTR; + break; + } + + if (*bytes_read < length) + nec7210_release_rfd_holdoff(board, priv); + + if (need_resched()) + schedule(); + } + return retval; +} + +#ifdef NEC_DMA +static ssize_t __dma_read(gpib_board_t *board, struct nec7210_priv *priv, size_t length) +{ + ssize_t retval = 0; + size_t count = 0; + unsigned long flags, dma_irq_flags; + + if (length == 0) + return 0; + + spin_lock_irqsave(&board->spinlock, flags); + + dma_irq_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + /* program dma controller */ + clear_dma_ff(priv->dma_channel); + set_dma_count(priv->dma_channel, length); + set_dma_addr(priv->dma_channel, priv->dma_buffer_addr); + set_dma_mode(priv->dma_channel, DMA_MODE_READ); + release_dma_lock(dma_irq_flags); + + enable_dma(priv->dma_channel); + + set_bit(DMA_READ_IN_PROGRESS_BN, &priv->state); + clear_bit(READ_READY_BN, &priv->state); + + // enable nec7210 dma + nec7210_set_reg_bits(priv, IMR2, HR_DMAI, HR_DMAI); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // wait for data to transfer + if (wait_event_interruptible(board->wait, + test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state) == 0 || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "nec7210: dma read wait interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &priv->state)) + retval = -EINTR; + + // disable nec7210 dma + nec7210_set_reg_bits(priv, IMR2, HR_DMAI, 0); + + // record how many bytes we transferred + flags = claim_dma_lock(); + clear_dma_ff(priv->dma_channel); + disable_dma(priv->dma_channel); + count += length - get_dma_residue(priv->dma_channel); + release_dma_lock(flags); + + return retval ? retval : count; +} + +static ssize_t dma_read(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length) +{ + size_t remain = length; + size_t transfer_size; + ssize_t retval = 0; + + while (remain > 0) { + transfer_size = (priv->dma_buffer_length < remain) ? + priv->dma_buffer_length : remain; + retval = __dma_read(board, priv, transfer_size); + if (retval < 0) + break; + memcpy(buffer, priv->dma_buffer, transfer_size); + remain -= retval; + buffer += retval; + if (test_bit(RECEIVED_END_BN, &priv->state)) + break; + } + + if (retval < 0) + return retval; + + return length - remain; +} +#endif + +int nec7210_read(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + + *end = 0; + *bytes_read = 0; + + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &priv->state); // XXX wrong + + nec7210_release_rfd_holdoff(board, priv); + + retval = pio_read(board, priv, buffer, length, end, bytes_read); + + return retval; +} +EXPORT_SYMBOL(nec7210_read); + +static int pio_write_wait(gpib_board_t *board, struct nec7210_priv *priv, + short wake_on_lacs, short wake_on_atn, short wake_on_bus_error) +{ + // wait until byte is ready to be sent + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + test_bit(WRITE_READY_BN, &priv->state)) || + test_bit(DEV_CLEAR_BN, &priv->state) || + (wake_on_bus_error && test_bit(BUS_ERROR_BN, &priv->state)) || + (wake_on_lacs && test_bit(LACS_NUM, &board->status)) || + (wake_on_atn && test_bit(ATN_NUM, &board->status)) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) { + dev_dbg(board->gpib_dev, "nec7210: write timed out\n"); + return -ETIMEDOUT; + } + if (test_bit(DEV_CLEAR_BN, &priv->state)) { + dev_dbg(board->gpib_dev, "nec7210: write interrupted by clear\n"); + return -EINTR; + } + if (wake_on_bus_error && test_and_clear_bit(BUS_ERROR_BN, &priv->state)) { + dev_dbg(board->gpib_dev, "nec7210: bus error on write\n"); + return -EIO; + } + return 0; +} + +static int pio_write(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length, size_t *bytes_written) +{ + size_t last_count = 0; + ssize_t retval = 0; + unsigned long flags; + const int max_bus_errors = (length > 1000) ? length : 1000; + int bus_error_count = 0; + *bytes_written = 0; + + clear_bit(BUS_ERROR_BN, &priv->state); + + while (*bytes_written < length) { + if (need_resched()) + schedule(); + + retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210); + if (retval == -EIO) { + /* resend last byte on bus error */ + *bytes_written = last_count; + dev_dbg(board->gpib_dev, "resending %c\n", buffer[*bytes_written]); + /* we can get unrecoverable bus errors, + * so give up after a while + */ + bus_error_count++; + if (bus_error_count > max_bus_errors) + return retval; + continue; + } else { + if (retval < 0) + return retval; + } + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(BUS_ERROR_BN, &priv->state); + clear_bit(WRITE_READY_BN, &priv->state); + last_count = *bytes_written; + write_byte(priv, buffer[(*bytes_written)++], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + } + retval = pio_write_wait(board, priv, 1, 1, priv->type == NEC7210); + return retval; +} + +#ifdef NEC_DMA +static ssize_t __dma_write(gpib_board_t *board, struct nec7210_priv *priv, dma_addr_t address, + size_t length) +{ + unsigned long flags, dma_irq_flags; + int residue = 0; + int retval = 0; + + spin_lock_irqsave(&board->spinlock, flags); + + /* program dma controller */ + dma_irq_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + clear_dma_ff(priv->dma_channel); + set_dma_count(priv->dma_channel, length); + set_dma_addr(priv->dma_channel, address); + set_dma_mode(priv->dma_channel, DMA_MODE_WRITE); + enable_dma(priv->dma_channel); + release_dma_lock(dma_irq_flags); + + // enable board's dma for output + nec7210_set_reg_bits(priv, IMR2, HR_DMAO, HR_DMAO); + + clear_bit(WRITE_READY_BN, &priv->state); + set_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // suspend until message is sent + if (wait_event_interruptible(board->wait, + test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0 || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted!\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) + retval = -EIO; + + // disable board's dma + nec7210_set_reg_bits(priv, IMR2, HR_DMAO, 0); + + dma_irq_flags = claim_dma_lock(); + clear_dma_ff(priv->dma_channel); + disable_dma(priv->dma_channel); + residue = get_dma_residue(priv->dma_channel); + release_dma_lock(dma_irq_flags); + + if (residue) + retval = -EPIPE; + + return retval ? retval : length; +} + +static ssize_t dma_write(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, + size_t length) +{ + size_t remain = length; + size_t transfer_size; + ssize_t retval = 0; + + while (remain > 0) { + transfer_size = (priv->dma_buffer_length < remain) ? + priv->dma_buffer_length : remain; + memcpy(priv->dma_buffer, buffer, transfer_size); + retval = __dma_write(board, priv, priv->dma_buffer_addr, transfer_size); + if (retval < 0) + break; + remain -= retval; + buffer += retval; + } + + if (retval < 0) + return retval; + + return length - remain; +} +#endif +int nec7210_write(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + int retval = 0; + + *bytes_written = 0; + + clear_bit(DEV_CLEAR_BN, &priv->state); //XXX + + if (send_eoi) + length-- ; /* save the last byte for sending EOI */ + + if (length > 0) { + // isa dma transfer + if (0 /*priv->dma_channel*/) { +/* + * dma writes are unreliable since they can't recover from bus errors + * (which happen when ATN is asserted in the middle of a write) + */ +#ifdef NEC_DMA + retval = dma_write(board, priv, buffer, length); + if (retval < 0) + return retval; + count += retval; +#endif + } else { // PIO transfer + size_t num_bytes; + + retval = pio_write(board, priv, buffer, length, &num_bytes); + + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + } + if (send_eoi) { + size_t num_bytes; + + /* We need to wait to make sure we will immediately be able to write the data byte + * into the chip before sending the associated AUX_SEOI command. This is really + * only needed for length==1 since otherwise the earlier calls to pio_write + * will have dont the wait already. + */ + retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210); + if (retval < 0) + return retval; + /*send EOI */ + write_byte(priv, AUX_SEOI, AUXMR); + + retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + + return retval; +} +EXPORT_SYMBOL(nec7210_write); + +/* + * interrupt service routine + */ +irqreturn_t nec7210_interrupt(gpib_board_t *board, struct nec7210_priv *priv) +{ + int status1, status2; + + // read interrupt status (also clears status) + status1 = read_byte(priv, ISR1); + status2 = read_byte(priv, ISR2); + + return nec7210_interrupt_have_status(board, priv, status1, status2); +} +EXPORT_SYMBOL(nec7210_interrupt); + +irqreturn_t nec7210_interrupt_have_status(gpib_board_t *board, + struct nec7210_priv *priv, int status1, int status2) +{ +#ifdef NEC_DMA + unsigned long dma_flags; +#endif + int retval = IRQ_NONE; + + // record service request in status + if (status2 & HR_SRQI) + set_bit(SRQI_NUM, &board->status); + + // change in lockout status + if (status2 & HR_LOKC) { + if (status2 & HR_LOK) + set_bit(LOK_NUM, &board->status); + else + clear_bit(LOK_NUM, &board->status); + } + + // change in remote status + if (status2 & HR_REMC) { + if (status2 & HR_REM) + set_bit(REM_NUM, &board->status); + else + clear_bit(REM_NUM, &board->status); + } + + // record reception of END + if (status1 & HR_END) { + set_bit(RECEIVED_END_BN, &priv->state); + if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE) + set_bit(RFD_HOLDOFF_BN, &priv->state); + } + + // get incoming data in PIO mode + if ((status1 & HR_DI)) { + set_bit(READ_READY_BN, &priv->state); + if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDA) + set_bit(RFD_HOLDOFF_BN, &priv->state); + } +#ifdef NEC_DMA + // check for dma read transfer complete + if (test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state)) { + dma_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + clear_dma_ff(priv->dma_channel); + if ((status1 & HR_END) || get_dma_residue(priv->dma_channel) == 0) + clear_bit(DMA_READ_IN_PROGRESS_BN, &priv->state); + else + enable_dma(priv->dma_channel); + release_dma_lock(dma_flags); + } +#endif + if ((status1 & HR_DO)) { + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0) + set_bit(WRITE_READY_BN, &priv->state); +#ifdef NEC_DMA + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state)) { // write data, isa dma mode + // check if dma transfer is complete + dma_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + clear_dma_ff(priv->dma_channel); + if (get_dma_residue(priv->dma_channel) == 0) { + clear_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state); + // XXX race? byte may still be in CDOR reg + } else { + clear_bit(WRITE_READY_BN, &priv->state); + enable_dma(priv->dma_channel); + } + release_dma_lock(dma_flags); + } +#endif + } + + // outgoing command can be sent + if (status2 & HR_CO) + set_bit(COMMAND_READY_BN, &priv->state); + + // command pass through received + if (status1 & HR_CPT) { + unsigned int command; + + command = read_byte(priv, CPTR) & gpib_command_mask; + write_byte(priv, AUX_NVAL, AUXMR); +// printk("gpib: command pass through 0x%x\n", command); + } + + if (status1 & HR_ERR) + set_bit(BUS_ERROR_BN, &priv->state); + + if (status1 & HR_DEC) { + unsigned short address_status_bits = read_byte(priv, ADSR); + + // ignore device clear events if we are controller in charge + if ((address_status_bits & HR_CIC) == 0) { + push_gpib_event(board, EventDevClr); + set_bit(DEV_CLEAR_BN, &priv->state); + } + } + + if (status1 & HR_DET) + push_gpib_event(board, EventDevTrg); + + // Addressing status has changed + if (status2 & HR_ADSC) + set_bit(ADR_CHANGE_BN, &priv->state); + + if ((status1 & priv->reg_bits[IMR1]) || + (status2 & (priv->reg_bits[IMR2] & IMR2_ENABLE_INTR_MASK)) || + nec7210_atn_has_changed(board, priv)) { + nec7210_update_status_nolock(board, priv); + dev_dbg(board->gpib_dev, "minor %i, stat %lx, isr1 0x%x, imr1 0x%x, isr2 0x%x, imr2 0x%x\n", + board->minor, board->status, status1, priv->reg_bits[IMR1], status2, + priv->reg_bits[IMR2]); + wake_up_interruptible(&board->wait); /* wake up sleeping process */ + retval = IRQ_HANDLED; + } + + return retval; +} +EXPORT_SYMBOL(nec7210_interrupt_have_status); + +void nec7210_board_reset(struct nec7210_priv *priv, const gpib_board_t *board) +{ + /* 7210 chip reset */ + write_byte(priv, AUX_CR, AUXMR); + + /* disable all interrupts */ + priv->reg_bits[IMR1] = 0; + write_byte(priv, priv->reg_bits[IMR1], IMR1); + priv->reg_bits[IMR2] = 0; + write_byte(priv, priv->reg_bits[IMR2], IMR2); + write_byte(priv, 0, SPMR); + + /* clear registers by reading */ + read_byte(priv, CPTR); + read_byte(priv, ISR1); + read_byte(priv, ISR2); + + /* parallel poll unconfigure */ + write_byte(priv, PPR | HR_PPU, AUXMR); + + priv->reg_bits[ADMR] = HR_TRM0 | HR_TRM1; + + priv->auxa_bits = AUXRA | HR_HLDA; + write_byte(priv, priv->auxa_bits, AUXMR); + + write_byte(priv, AUXRE | 0, AUXMR); + + /* set INT pin to active high, enable command pass through of unknown commands */ + priv->auxb_bits = AUXRB | HR_CPTE; + write_byte(priv, priv->auxb_bits, AUXMR); + write_byte(priv, AUXRE, AUXMR); +} +EXPORT_SYMBOL(nec7210_board_reset); + +void nec7210_board_online(struct nec7210_priv *priv, const gpib_board_t *board) +{ + /* set GPIB address */ + nec7210_primary_address(board, priv, board->pad); + nec7210_secondary_address(board, priv, board->sad, board->sad >= 0); + + // enable interrupts + priv->reg_bits[IMR1] = HR_ERRIE | HR_DECIE | HR_ENDIE | + HR_DETIE | HR_CPTIE | HR_DOIE | HR_DIIE; + priv->reg_bits[IMR2] = IMR2_ENABLE_INTR_MASK; + write_byte(priv, priv->reg_bits[IMR1], IMR1); + write_byte(priv, priv->reg_bits[IMR2], IMR2); + + write_byte(priv, AUX_PON, AUXMR); +} +EXPORT_SYMBOL(nec7210_board_online); + +#ifdef CONFIG_HAS_IOPORT +/* wrappers for io */ +uint8_t nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + return inb((unsigned long)(priv->iobase) + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_ioport_read_byte); + +void nec7210_ioport_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num) +{ + if (register_num == AUXMR) + /* locking makes absolutely sure noone accesses the + * AUXMR register faster than once per microsecond + */ + nec7210_locking_ioport_write_byte(priv, data, register_num); + else + outb(data, (unsigned long)(priv->iobase) + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_ioport_write_byte); + +/* locking variants of io wrappers, for chips that page-in registers */ +uint8_t nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + retval = inb((unsigned long)(priv->iobase) + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); + return retval; +} +EXPORT_SYMBOL(nec7210_locking_ioport_read_byte); + +void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, uint8_t data, + unsigned int register_num) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + if (register_num == AUXMR) + udelay(1); + outb(data, (unsigned long)(priv->iobase) + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); +} +EXPORT_SYMBOL(nec7210_locking_ioport_write_byte); +#endif + +uint8_t nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + return readb(priv->iobase + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_iomem_read_byte); + +void nec7210_iomem_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num) +{ + if (register_num == AUXMR) + /* locking makes absolutely sure noone accesses the + * AUXMR register faster than once per microsecond + */ + nec7210_locking_iomem_write_byte(priv, data, register_num); + else + writeb(data, priv->iobase + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_iomem_write_byte); + +uint8_t nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + retval = readb(priv->iobase + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); + return retval; +} +EXPORT_SYMBOL(nec7210_locking_iomem_read_byte); + +void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, uint8_t data, + unsigned int register_num) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + if (register_num == AUXMR) + udelay(1); + writeb(data, priv->iobase + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); +} +EXPORT_SYMBOL(nec7210_locking_iomem_write_byte); + +static int __init nec7210_init_module(void) +{ + return 0; +} + +static void __exit nec7210_exit_module(void) +{ +} + +module_init(nec7210_init_module); +module_exit(nec7210_exit_module); diff --git a/drivers/staging/gpib/ni_usb/Makefile b/drivers/staging/gpib/ni_usb/Makefile new file mode 100644 index 0000000000000..e22b3b21a62c6 --- /dev/null +++ b/drivers/staging/gpib/ni_usb/Makefile @@ -0,0 +1,4 @@ + +obj-m += ni_usb_gpib.o + + diff --git a/drivers/staging/gpib/ni_usb/ni_usb_gpib.c b/drivers/staging/gpib/ni_usb/ni_usb_gpib.c new file mode 100644 index 0000000000000..b7b6fb1be3790 --- /dev/null +++ b/drivers/staging/gpib/ni_usb/ni_usb_gpib.c @@ -0,0 +1,2640 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * driver for National Instruments usb to gpib adapters + * copyright : (C) 2004 by Frank Mori Hess + ***************************************************************************/ + +#include +#include +#include +#include "ni_usb_gpib.h" +#include "gpibP.h" +#include "nec7210.h" +#include "tnt4882_registers.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for National Instruments USB devices"); + +#define MAX_NUM_NI_USB_INTERFACES 128 +static struct usb_interface *ni_usb_driver_interfaces[MAX_NUM_NI_USB_INTERFACES]; + +static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status); +static int ni_usb_set_interrupt_monitor(gpib_board_t *board, unsigned int monitored_bits); +static void ni_usb_stop(struct ni_usb_priv *ni_priv); + +static DEFINE_MUTEX(ni_usb_hotplug_lock); + +//calculates a reasonable timeout in that can be passed to usb functions +static inline unsigned long ni_usb_timeout_msecs(unsigned int usec) +{ + if (usec == 0) + return 0; + return 2000 + usec / 500; +}; + +// returns timeout code byte for use in ni-usb-b instructions +static unsigned short ni_usb_timeout_code(unsigned int usec) +{ + if (usec == 0) + return 0xf0; + else if (usec <= 10) + return 0xf1; + else if (usec <= 30) + return 0xf2; + else if (usec <= 100) + return 0xf3; + else if (usec <= 300) + return 0xf4; + else if (usec <= 1000) + return 0xf5; + else if (usec <= 3000) + return 0xf6; + else if (usec <= 10000) + return 0xf7; + else if (usec <= 30000) + return 0xf8; + else if (usec <= 100000) + return 0xf9; + else if (usec <= 300000) + return 0xfa; + else if (usec <= 1000000) + return 0xfb; + else if (usec <= 3000000) + return 0xfc; + else if (usec <= 10000000) + return 0xfd; + else if (usec <= 30000000) + return 0xfe; + else if (usec <= 100000000) + return 0xff; + else if (usec <= 300000000) + return 0x01; + /* NI driver actually uses 0xff for timeout T1000s, which is a bug in their code. + * I've verified on a usb-b that a code of 0x2 is correct for a 1000 sec timeout + */ + else if (usec <= 1000000000) + return 0x02; + pr_err("%s: bug? usec is greater than 1e9\n", __func__); + return 0xf0; +} + +static void ni_usb_bulk_complete(struct urb *urb) +{ + struct ni_usb_urb_ctx *context = urb->context; + +// printk("debug: %s: status=0x%x, error_count=%i, actual_length=%i\n", __func__, +// urb->status, urb->error_count, urb->actual_length); + up(&context->complete); +} + +static void ni_usb_timeout_handler(struct timer_list *t) +{ + struct ni_usb_priv *ni_priv = from_timer(ni_priv, t, bulk_timer); + struct ni_usb_urb_ctx *context = &ni_priv->context; + + context->timed_out = 1; + up(&context->complete); +}; + +// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases +static int ni_usb_nonblocking_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, + int data_length, int *actual_data_length, + int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int out_pipe; + struct ni_usb_urb_ctx *context = &ni_priv->context; + + *actual_data_length = 0; + mutex_lock(&ni_priv->bulk_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENODEV; + } + if (ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -EAGAIN; + } + ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_pipe = usb_sndbulkpipe(usb_dev, ni_priv->bulk_out_endpoint); + sema_init(&context->complete, 0); + context->timed_out = 0; + usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, out_pipe, data, data_length, + &ni_usb_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL); + if (retval) { + del_timer_sync(&ni_priv->bulk_timer); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + dev_err(&usb_dev->dev, "%s: failed to submit bulk out urb, retval=%i\n", + __func__, retval); + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; + } + mutex_unlock(&ni_priv->bulk_transfer_lock); + down(&context->complete); // wait for ni_usb_bulk_complete + if (context->timed_out) { + usb_kill_urb(ni_priv->bulk_urb); + dev_err(&usb_dev->dev, "%s: killed urb due to timeout\n", __func__); + retval = -ETIMEDOUT; + } else { + retval = ni_priv->bulk_urb->status; + } + + del_timer_sync(&ni_priv->bulk_timer); + *actual_data_length = ni_priv->bulk_urb->actual_length; + mutex_lock(&ni_priv->bulk_transfer_lock); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; +} + +static int ni_usb_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, int data_length, + int *actual_data_length, int timeout_msecs) +{ + int retval; + int timeout_msecs_remaining = timeout_msecs; + + retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, actual_data_length, + timeout_msecs_remaining); + while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) { + usleep_range(1000, 1500); + retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, + actual_data_length, + timeout_msecs_remaining); + if (timeout_msecs != 0) + --timeout_msecs_remaining; + } + if (timeout_msecs != 0 && timeout_msecs_remaining <= 0) + return -ETIMEDOUT; + return retval; +} + +// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases +static int ni_usb_nonblocking_receive_bulk_msg(struct ni_usb_priv *ni_priv, + void *data, int data_length, + int *actual_data_length, int timeout_msecs, + int interruptible) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + struct ni_usb_urb_ctx *context = &ni_priv->context; + + *actual_data_length = 0; + mutex_lock(&ni_priv->bulk_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENODEV; + } + if (ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -EAGAIN; + } + ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + in_pipe = usb_rcvbulkpipe(usb_dev, ni_priv->bulk_in_endpoint); + sema_init(&context->complete, 0); + context->timed_out = 0; + usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, in_pipe, data, data_length, + &ni_usb_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + //printk("%s: submitting urb\n", __func__); + retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL); + if (retval) { + del_timer_sync(&ni_priv->bulk_timer); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + dev_err(&usb_dev->dev, "%s: failed to submit bulk out urb, retval=%i\n", + __func__, retval); + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; + } + mutex_unlock(&ni_priv->bulk_transfer_lock); + if (interruptible) { + if (down_interruptible(&context->complete)) { + /* If we got interrupted by a signal while + * waiting for the usb gpib to respond, we + * should send a stop command so it will + * finish up with whatever it was doing and + * send its response now. + */ + ni_usb_stop(ni_priv); + retval = -ERESTARTSYS; + /* now do an uninterruptible wait, it shouldn't take long + * for the board to respond now. + */ + down(&context->complete); + } + } else { + down(&context->complete); + } + if (context->timed_out) { + usb_kill_urb(ni_priv->bulk_urb); + dev_err(&usb_dev->dev, "%s: killed urb due to timeout\n", __func__); + retval = -ETIMEDOUT; + } else { + if (ni_priv->bulk_urb->status) + retval = ni_priv->bulk_urb->status; + } + del_timer_sync(&ni_priv->bulk_timer); + *actual_data_length = ni_priv->bulk_urb->actual_length; + mutex_lock(&ni_priv->bulk_transfer_lock); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; +} + +static int ni_usb_receive_bulk_msg(struct ni_usb_priv *ni_priv, void *data, + int data_length, int *actual_data_length, int timeout_msecs, + int interruptible) +{ + int retval; + int timeout_msecs_remaining = timeout_msecs; + + retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length, + actual_data_length, timeout_msecs_remaining, + interruptible); + while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) { + usleep_range(1000, 1500); + retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length, + actual_data_length, + timeout_msecs_remaining, + interruptible); + if (timeout_msecs != 0) + --timeout_msecs_remaining; + } + if (timeout_msecs && timeout_msecs_remaining <= 0) + return -ETIMEDOUT; + return retval; +} + +static int ni_usb_receive_control_msg(struct ni_usb_priv *ni_priv, __u8 request, + __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + + mutex_lock(&ni_priv->control_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->control_transfer_lock); + return -ENODEV; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + in_pipe = usb_rcvctrlpipe(usb_dev, 0); + retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data, + size, timeout_msecs); + mutex_unlock(&ni_priv->control_transfer_lock); + return retval; +} + +static void ni_usb_soft_update_status(gpib_board_t *board, unsigned int ni_usb_ibsta, + unsigned int clear_mask) +{ + static const unsigned int ni_usb_ibsta_mask = SRQI | ATN | CIC | REM | LACS | TACS | LOK; + + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + unsigned int need_monitoring_bits = ni_usb_ibsta_monitor_mask; + unsigned long flags; + + board->status &= ~clear_mask; + board->status &= ~ni_usb_ibsta_mask; + board->status |= ni_usb_ibsta & ni_usb_ibsta_mask; + //FIXME should generate events on DTAS and DCAS + + spin_lock_irqsave(&board->spinlock, flags); +/* remove set status bits from monitored set why ?***/ + ni_priv->monitored_ibsta_bits &= ~ni_usb_ibsta; + need_monitoring_bits &= ~ni_priv->monitored_ibsta_bits; /* mm - monitored set */ + spin_unlock_irqrestore(&board->spinlock, flags); + dev_dbg(&usb_dev->dev, "%s: need_monitoring_bits=0x%x\n", __func__, need_monitoring_bits); + + if (need_monitoring_bits & ~ni_usb_ibsta) + ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); + else if (need_monitoring_bits & ni_usb_ibsta) + wake_up_interruptible(&board->wait); + + dev_dbg(&usb_dev->dev, "%s: ni_usb_ibsta=0x%x\n", __func__, ni_usb_ibsta); +} + +static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status) +{ + u16 count; + + status->id = buffer[0]; + status->ibsta = (buffer[1] << 8) | buffer[2]; + status->error_code = buffer[3]; + count = buffer[4] | (buffer[5] << 8); + count = ~count; + count++; + status->count = count; + return 8; +}; + +static void ni_usb_dump_raw_block(const u8 *raw_data, int length) +{ + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true); +} + +static int ni_usb_parse_register_read_block(const u8 *raw_data, unsigned int *results, + int num_results) +{ + int i = 0; + int j; + int unexpected = 0; + static const int results_per_chunk = 3; + + for (j = 0; j < num_results;) { + int k; + + if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_START_ID) { + pr_err("%s: parse error: wrong start id\n", __func__); + unexpected = 1; + } + for (k = 0; k < results_per_chunk && j < num_results; ++k) + results[j++] = raw_data[i++]; + } + while (i % 4) + i++; + if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_END_ID) { + pr_err("%s: parse error: wrong end id\n", __func__); + unexpected = 1; + } + if (raw_data[i++] % results_per_chunk != num_results % results_per_chunk) { + pr_err("%s: parse error: wrong count=%i for NIUSB_REGISTER_READ_DATA_END\n", + __func__, (int)raw_data[i - 1]); + unexpected = 1; + } + while (i % 4) { + if (raw_data[i++] != 0) { + pr_err("%s: unexpected data: raw_data[%i]=0x%x, expected 0\n", + __func__, i - 1, (int)raw_data[i - 1]); + unexpected = 1; + } + } + if (unexpected) + ni_usb_dump_raw_block(raw_data, i); + return i; +} + +static int ni_usb_parse_termination_block(const u8 *buffer) +{ + int i = 0; + + if (buffer[i++] != NIUSB_TERM_ID || + buffer[i++] != 0x0 || + buffer[i++] != 0x0 || + buffer[i++] != 0x0) { + pr_err("%s: received unexpected termination block\n", __func__); + pr_err(" expected: 0x%x 0x%x 0x%x 0x%x\n", + NIUSB_TERM_ID, 0x0, 0x0, 0x0); + pr_err(" received: 0x%x 0x%x 0x%x 0x%x\n", + buffer[i - 4], buffer[i - 3], buffer[i - 2], buffer[i - 1]); + } + return i; +}; + +static int parse_board_ibrd_readback(const u8 *raw_data, struct ni_usb_status_block *status, + u8 *parsed_data, int parsed_data_length, + int *actual_bytes_read) +{ + static const int ibrd_data_block_length = 0xf; + static const int ibrd_extended_data_block_length = 0x1e; + int data_block_length = 0; + int i = 0; + int j = 0; + int k; + unsigned int adr1_bits; + int num_data_blocks = 0; + struct ni_usb_status_block register_write_status; + int unexpected = 0; + + while (raw_data[i] == NIUSB_IBRD_DATA_ID || raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) { + if (raw_data[i] == NIUSB_IBRD_DATA_ID) { + data_block_length = ibrd_data_block_length; + } else if (raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) { + data_block_length = ibrd_extended_data_block_length; + if (raw_data[++i] != 0) { + pr_err("%s: unexpected data: raw_data[%i]=0x%x, expected 0\n", + __func__, i, (int)raw_data[i]); + unexpected = 1; + } + } else { + pr_err("%s: logic bug!\n", __func__); + return -EINVAL; + } + ++i; + for (k = 0; k < data_block_length; k++) { + if (j < parsed_data_length) + parsed_data[j++] = raw_data[i++]; + else + ++i; + } + ++num_data_blocks; + } + i += ni_usb_parse_status_block(&raw_data[i], status); + if (status->id != NIUSB_IBRD_STATUS_ID) { + pr_err("%s: bug: status->id=%i, != ibrd_status_id\n", __func__, status->id); + return -EIO; + } + adr1_bits = raw_data[i++]; + if (num_data_blocks) { + *actual_bytes_read = (num_data_blocks - 1) * data_block_length + raw_data[i++]; + } else { + ++i; + *actual_bytes_read = 0; + } + if (*actual_bytes_read > j) + pr_err("%s: bug: discarded data. actual_bytes_read=%i, j=%i\n", + __func__, *actual_bytes_read, j); + for (k = 0; k < 2; k++) + if (raw_data[i++] != 0) { + pr_err("%s: unexpected data: raw_data[%i]=0x%x, expected 0\n", + __func__, i - 1, (int)raw_data[i - 1]); + unexpected = 1; + } + i += ni_usb_parse_status_block(&raw_data[i], ®ister_write_status); + if (register_write_status.id != NIUSB_REG_WRITE_ID) { + pr_err("%s: unexpected data: register write status id=0x%x, expected 0x%x\n", + __func__, register_write_status.id, NIUSB_REG_WRITE_ID); + unexpected = 1; + } + if (raw_data[i++] != 2) { + pr_err("%s: unexpected data: register write count=%i, expected 2\n", + __func__, (int)raw_data[i - 1]); + unexpected = 1; + } + for (k = 0; k < 3; k++) + if (raw_data[i++] != 0) { + pr_err("%s: unexpected data: raw_data[%i]=0x%x, expected 0\n", + __func__, i - 1, (int)raw_data[i - 1]); + unexpected = 1; + } + i += ni_usb_parse_termination_block(&raw_data[i]); + if (unexpected) + ni_usb_dump_raw_block(raw_data, i); + return i; +} + +static int ni_usb_parse_reg_write_status_block(const u8 *raw_data, + struct ni_usb_status_block *status, + int *writes_completed) +{ + int i = 0; + + i += ni_usb_parse_status_block(raw_data, status); + *writes_completed = raw_data[i++]; + while (i % 4) + i++; + return i; +} + +static int ni_usb_write_registers(struct ni_usb_priv *ni_priv, + const struct ni_usb_register *writes, int num_writes, + unsigned int *ibsta) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + u8 *out_data, *in_data; + int out_data_length; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + int j; + struct ni_usb_status_block status; + static const int bytes_per_write = 3; + int reg_writes_completed; + + out_data_length = num_writes * bytes_per_write + 0x10; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) { + dev_err(&usb_dev->dev, "%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + i += ni_usb_bulk_register_write_header(&out_data[i], num_writes); + for (j = 0; j < num_writes; j++) + i += ni_usb_bulk_register_write(&out_data[i], writes[j]); + while (i % 4) + out_data[i++] = 0x00; + i += ni_usb_bulk_termination(&out_data[i]); + if (i > out_data_length) + dev_err(&usb_dev->dev, "%s: bug! buffer overrun\n", __func__); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + if (retval || bytes_read != 16) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + ni_usb_dump_raw_block(in_data, bytes_read); + kfree(in_data); + return retval; + } + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + ni_usb_parse_reg_write_status_block(in_data, &status, ®_writes_completed); + //FIXME parse extra 09 status bits and termination + kfree(in_data); + if (status.id != NIUSB_REG_WRITE_ID) { + dev_err(&usb_dev->dev, "%s: parse error, id=0x%x != NIUSB_REG_WRITE_ID\n", + __func__, status.id); + return -EIO; + } + if (status.error_code) { + dev_err(&usb_dev->dev, "%s: nonzero error code 0x%x\n", + __func__, status.error_code); + return -EIO; + } + if (reg_writes_completed != num_writes) { + dev_err(&usb_dev->dev, "%s: reg_writes_completed=%i, num_writes=%i\n", + __func__, reg_writes_completed, num_writes); + return -EIO; + } + if (ibsta) + *ibsta = status.ibsta; + return 0; +} + +// interface functions +static int ni_usb_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read) +{ + int retval, parse_retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + static const int out_data_length = 0x20; + int in_data_length; + int usb_bytes_written = 0, usb_bytes_read = 0; + int i = 0; + int complement_count; + int actual_length; + struct ni_usb_status_block status; + static const int max_read_length = 0xffff; + struct ni_usb_register reg; + + *bytes_read = 0; + if (length > max_read_length) { + length = max_read_length; + dev_err(&usb_dev->dev, "%s: read length too long\n", __func__); + } + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = 0x0a; + out_data[i++] = ni_priv->eos_mode >> 8; + out_data[i++] = ni_priv->eos_char; + out_data[i++] = ni_usb_timeout_code(board->usec_timeout); + complement_count = length - 1; + complement_count = ~complement_count; + out_data[i++] = complement_count & 0xff; + out_data[i++] = (complement_count >> 8) & 0xff; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_register_write_header(&out_data[i], 2); + reg.device = NIUSB_SUBDEV_TNT4882; + reg.address = nec7210_to_tnt4882_offset(AUXMR); + reg.value = AUX_HLDI; + i += ni_usb_bulk_register_write(&out_data[i], reg); + reg.value = AUX_CLEAR_END; + i += ni_usb_bulk_register_write(&out_data[i], reg); + while (i % 4) // pad with zeros to 4-byte boundary + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, 1000); + kfree(out_data); + if (retval || usb_bytes_written != i) { + if (retval == 0) + retval = -EIO; + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n", + __func__, retval, usb_bytes_written, i); + mutex_unlock(&ni_priv->addressed_transfer_lock); + return retval; + } + + in_data_length = (length / 30 + 1) * 0x20 + 0x20; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read, + ni_usb_timeout_msecs(board->usec_timeout), 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if (retval == -ERESTARTSYS) { + } else if (retval) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, usb_bytes_read=%i\n", + __func__, retval, usb_bytes_read); + kfree(in_data); + return retval; + } + parse_retval = parse_board_ibrd_readback(in_data, &status, buffer, length, &actual_length); + if (parse_retval != usb_bytes_read) { + if (parse_retval >= 0) + parse_retval = -EIO; + dev_err(&usb_dev->dev, "%s: retval=%i usb_bytes_read=%i\n", + __func__, parse_retval, usb_bytes_read); + kfree(in_data); + return parse_retval; + } + if (actual_length != length - status.count) { + dev_err(&usb_dev->dev, "%s: actual_length=%i expected=%li\n", + __func__, actual_length, (long)(length - status.count)); + ni_usb_dump_raw_block(in_data, usb_bytes_read); + } + kfree(in_data); + switch (status.error_code) { + case NIUSB_NO_ERROR: + retval = 0; + break; + case NIUSB_ABORTED_ERROR: + /* this is expected if ni_usb_receive_bulk_msg got + * interrupted by a signal and returned -ERESTARTSYS + */ + break; + case NIUSB_ATN_STATE_ERROR: + retval = -EIO; + dev_err(&usb_dev->dev, "%s: read when ATN set\n", __func__); + break; + case NIUSB_ADDRESSING_ERROR: + retval = -EIO; + break; + case NIUSB_TIMEOUT_ERROR: + retval = -ETIMEDOUT; + break; + case NIUSB_EOSMODE_ERROR: + dev_err(&usb_dev->dev, "%s: driver bug, we should have been able to avoid NIUSB_EOSMODE_ERROR.\n", + __func__); + retval = -EINVAL; + break; + default: + dev_err(&usb_dev->dev, "%s: unknown error code=%i\n", __func__, status.error_code); + retval = -EIO; + break; + } + ni_usb_soft_update_status(board, status.ibsta, 0); + if (status.ibsta & END) + *end = 1; + else + *end = 0; + *bytes_read = actual_length; + return retval; +} + +static int ni_usb_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + int out_data_length; + static const int in_data_length = 0x10; + int usb_bytes_written = 0, usb_bytes_read = 0; + int i = 0, j; + int complement_count; + struct ni_usb_status_block status; + static const int max_write_length = 0xffff; + + *bytes_written = 0; + if (length > max_write_length) { + length = max_write_length; + send_eoi = 0; + dev_err(&usb_dev->dev, "%s: write length too long\n", __func__); + } + out_data_length = length + 0x10; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = 0x0d; + complement_count = length - 1; + complement_count = ~complement_count; + out_data[i++] = complement_count & 0xff; + out_data[i++] = (complement_count >> 8) & 0xff; + out_data[i++] = ni_usb_timeout_code(board->usec_timeout); + out_data[i++] = 0x0; + out_data[i++] = 0x0; + if (send_eoi) + out_data[i++] = 0x8; + else + out_data[i++] = 0x0; + out_data[i++] = 0x0; + for (j = 0; j < length; j++) + out_data[i++] = buffer[j]; + while (i % 4) // pad with zeros to 4-byte boundary + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, + ni_usb_timeout_msecs(board->usec_timeout)); + kfree(out_data); + if (retval || usb_bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n", + __func__, retval, usb_bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return -ENOMEM; + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read, + ni_usb_timeout_msecs(board->usec_timeout), 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if ((retval && retval != -ERESTARTSYS) || usb_bytes_read != 12) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, usb_bytes_read=%i\n", + __func__, retval, usb_bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + switch (status.error_code) { + case NIUSB_NO_ERROR: + retval = 0; + break; + case NIUSB_ABORTED_ERROR: + /* this is expected if ni_usb_receive_bulk_msg got + * interrupted by a signal and returned -ERESTARTSYS + */ + break; + case NIUSB_ADDRESSING_ERROR: + dev_err(&usb_dev->dev, "%s: Addressing error retval %d error code=%i\n", + __func__, retval, status.error_code); + retval = -ENXIO; + break; + case NIUSB_NO_LISTENER_ERROR: + retval = -ECOMM; + break; + case NIUSB_TIMEOUT_ERROR: + retval = -ETIMEDOUT; + break; + default: + dev_err(&usb_dev->dev, "%s: unknown error code=%i\n", + __func__, status.error_code); + retval = -EPIPE; + break; + } + ni_usb_soft_update_status(board, status.ibsta, 0); + *bytes_written = length - status.count; + return retval; +} + +static int ni_usb_command_chunk(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *command_bytes_written) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + int out_data_length; + static const int in_data_length = 0x10; + int bytes_written = 0, bytes_read = 0; + int i = 0, j; + unsigned int complement_count; + struct ni_usb_status_block status; + // usb-b gives error 4 if you try to send more than 16 command bytes at once + static const int max_command_length = 0x10; + + *command_bytes_written = 0; + if (length > max_command_length) + length = max_command_length; + out_data_length = length + 0x10; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = 0x0c; + complement_count = length - 1; + complement_count = ~complement_count; + out_data[i++] = complement_count; + out_data[i++] = 0x0; + out_data[i++] = ni_usb_timeout_code(board->usec_timeout); + for (j = 0; j < length; j++) + out_data[i++] = buffer[j]; + while (i % 4) // pad with zeros to 4-byte boundary + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, + ni_usb_timeout_msecs(board->usec_timeout)); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, + ni_usb_timeout_msecs(board->usec_timeout), 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + *command_bytes_written = length - status.count; + switch (status.error_code) { + case NIUSB_NO_ERROR: + break; + case NIUSB_ABORTED_ERROR: + /* this is expected if ni_usb_receive_bulk_msg got + * interrupted by a signal and returned -ERESTARTSYS + */ + break; + case NIUSB_NO_BUS_ERROR: + return -ENOTCONN; + case NIUSB_EOSMODE_ERROR: + dev_err(&usb_dev->dev, "%s: got eosmode error. Driver bug?\n", __func__); + return -EIO; + case NIUSB_TIMEOUT_ERROR: + return -ETIMEDOUT; + default: + dev_err(&usb_dev->dev, "%s: unknown error code=%i\n", __func__, status.error_code); + return -EIO; + } + ni_usb_soft_update_status(board, status.ibsta, 0); + return 0; +} + +static int ni_usb_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written) +{ + size_t count; + int retval; + + *bytes_written = 0; + while (*bytes_written < length) { + retval = ni_usb_command_chunk(board, buffer + *bytes_written, + length - *bytes_written, &count); + *bytes_written += count; + if (retval < 0) + return retval; + } + return 0; +} + +static int ni_usb_take_control(gpib_board_t *board, int synchronous) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x10; + int bytes_written = 0, bytes_read = 0; + int i = 0; + struct ni_usb_status_block status; + + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = NIUSB_IBCAC_ID; + if (synchronous) + out_data[i++] = 0x1; + else + out_data[i++] = 0x0; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) { + if (retval == 0) + retval = -EIO; + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + ni_usb_soft_update_status(board, status.ibsta, 0); + return retval; +} + +static int ni_usb_go_to_standby(gpib_board_t *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + struct ni_usb_status_block status; + + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = NIUSB_IBGTS_ID; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if (retval || bytes_read != 12) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + if (status.id != NIUSB_IBGTS_ID) + dev_err(&usb_dev->dev, "%s: bug: status.id 0x%x != INUSB_IBGTS_ID\n", + __func__, status.id); + ni_usb_soft_update_status(board, status.ibsta, 0); + return 0; +} + +static void ni_usb_request_system_control(gpib_board_t *board, int request_control) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[4]; + unsigned int ibsta; + + if (request_control) { + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + writes[i].value = SETSC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CIFC; + i++; + } else { + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CREN; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CIFC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_DSC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + writes[i].value = CLRSC; + i++; + } + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return; // retval; + } + if (!request_control) + ni_priv->ren_state = 0; + ni_usb_soft_update_status(board, ibsta, 0); + return; // 0; +} + +//FIXME maybe the interface should have a "pulse interface clear" function that can return an error? +static void ni_usb_interface_clear(gpib_board_t *board, int assert) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x10; + int bytes_written = 0, bytes_read = 0; + int i = 0; + struct ni_usb_status_block status; + + // FIXME: we are going to pulse when assert is true, and ignore otherwise + if (assert == 0) + return; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) { + dev_err(&usb_dev->dev, "%s: kmalloc failed\n", __func__); + return; + } + out_data[i++] = NIUSB_IBSIC_ID; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return; + } + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return; + + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + if (retval || bytes_read != 12) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + kfree(in_data); + return; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + ni_usb_soft_update_status(board, status.ibsta, 0); +} + +static void ni_usb_remote_enable(gpib_board_t *board, int enable) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + struct ni_usb_register reg; + unsigned int ibsta; + + reg.device = NIUSB_SUBDEV_TNT4882; + reg.address = nec7210_to_tnt4882_offset(AUXMR); + if (enable) + reg.value = AUX_SREN; + else + reg.value = AUX_CREN; + retval = ni_usb_write_registers(ni_priv, ®, 1, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return; //retval; + } + ni_priv->ren_state = enable; + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static int ni_usb_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct ni_usb_priv *ni_priv = board->private_data; + + ni_priv->eos_char = eos_byte; + ni_priv->eos_mode |= REOS; + if (compare_8_bits) + ni_priv->eos_mode |= BIN; + else + ni_priv->eos_mode &= ~BIN; + return 0; +} + +static void ni_usb_disable_eos(gpib_board_t *board) +{ + struct ni_usb_priv *ni_priv = board->private_data; + /* adapter gets unhappy if you don't zero all the bits + * for the eos mode and eos char (returns error 4 on reads). + */ + ni_priv->eos_mode = 0; + ni_priv->eos_char = 0; +} + +static unsigned int ni_usb_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + static const int buffer_length = 8; + u8 *buffer; + struct ni_usb_status_block status; + + //printk("%s: receive control pipe is %i\n", __func__, pipe); + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return board->status; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x200, 0x0, buffer, buffer_length, 1000); + if (retval != buffer_length) { + dev_err(&usb_dev->dev, "%s: usb_control_msg returned %i\n", __func__, retval); + kfree(buffer); + return board->status; + } + ni_usb_parse_status_block(buffer, &status); + kfree(buffer); + ni_usb_soft_update_status(board, status.ibsta, clear_mask); + return board->status; +} + +// tells ni-usb to immediately stop an ongoing i/o operation +static void ni_usb_stop(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + static const int buffer_length = 8; + u8 *buffer; + struct ni_usb_status_block status; + + //printk("%s: receive control pipe is %i\n", __func__, pipe); + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_STOP_REQUEST, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, buffer_length, 1000); + if (retval != buffer_length) { + dev_err(&usb_dev->dev, "%s: usb_control_msg returned %i\n", __func__, retval); + kfree(buffer); + return; + } + ni_usb_parse_status_block(buffer, &status); + kfree(buffer); +} + +static int ni_usb_primary_address(gpib_board_t *board, unsigned int address) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[2]; + unsigned int ibsta; + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADR); + writes[i].value = address; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x0; + writes[i].value = address; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +static int ni_usb_write_sad(struct ni_usb_register *writes, int address, int enable) +{ + unsigned int adr_bits, admr_bits; + int i = 0; + + adr_bits = HR_ARS; + admr_bits = HR_TRM0 | HR_TRM1; + if (enable) { + adr_bits |= address; + admr_bits |= HR_ADM1; + } else { + adr_bits |= HR_DT | HR_DL; + admr_bits |= HR_ADM0; + } + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADR); + writes[i].value = adr_bits; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADMR); + writes[i].value = admr_bits; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x1; + writes[i].value = enable ? MSA(address) : 0x0; + i++; + return i; +} + +static int ni_usb_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[3]; + unsigned int ibsta; + + i += ni_usb_write_sad(writes, address, enable); + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +static int ni_usb_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + int j = 0; + struct ni_usb_status_block status; + + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = NIUSB_IBRPP_ID; + out_data[i++] = 0xf0; //FIXME: this should be the parallel poll timeout code + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + /*FIXME: 1000 should use parallel poll timeout (not supported yet)*/ + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + + kfree(out_data); + if (retval || bytes_written != i) { + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return retval; + } + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return -ENOMEM; + + /*FIXME: should use parallel poll timeout (not supported yet)*/ + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, + &bytes_read, 1000, 1); + + if (retval && retval != -ERESTARTSYS) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + kfree(in_data); + return retval; + } + j += ni_usb_parse_status_block(in_data, &status); + *result = in_data[j++]; + kfree(in_data); + ni_usb_soft_update_status(board, status.ibsta, 0); + return retval; +} + +static void ni_usb_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = PPR | config; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static void ni_usb_parallel_poll_response(gpib_board_t *board, int ist) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + if (ist) + writes[i].value = AUX_SPPF; + else + writes[i].value = AUX_CPPF; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static void ni_usb_serial_poll_response(gpib_board_t *board, u8 status) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(SPMR); + writes[i].value = status; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static uint8_t ni_usb_serial_poll_status(gpib_board_t *board) +{ + return 0; +} + +static void ni_usb_return_to_local(gpib_board_t *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_RTL; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static int ni_usb_line_status(const gpib_board_t *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + u8 *out_data, *in_data; + static const int out_data_length = 0x20; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + unsigned int bsr_bits; + int line_status = ValidALL; + // NI windows driver reads 0xd(HSSEL), 0xc (ARD0), 0x1f (BSR) + + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + /* line status gets called during ibwait */ + retval = mutex_trylock(&ni_priv->addressed_transfer_lock); + + if (retval == 0) { + kfree(out_data); + return -EBUSY; + } + i += ni_usb_bulk_register_read_header(&out_data[i], 1); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_TNT4882, BSR); + while (i % 4) + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + __func__, retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, in_data, in_data_length, + &bytes_read, 1000, 0); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + kfree(in_data); + return retval; + } + + ni_usb_parse_register_read_block(in_data, &bsr_bits, 1); + kfree(in_data); + if (bsr_bits & BCSR_REN_BIT) + line_status |= BusREN; + if (bsr_bits & BCSR_IFC_BIT) + line_status |= BusIFC; + if (bsr_bits & BCSR_SRQ_BIT) + line_status |= BusSRQ; + if (bsr_bits & BCSR_EOI_BIT) + line_status |= BusEOI; + if (bsr_bits & BCSR_NRFD_BIT) + line_status |= BusNRFD; + if (bsr_bits & BCSR_NDAC_BIT) + line_status |= BusNDAC; + if (bsr_bits & BCSR_DAV_BIT) + line_status |= BusDAV; + if (bsr_bits & BCSR_ATN_BIT) + line_status |= BusATN; + return line_status; +} + +static int ni_usb_setup_t1_delay(struct ni_usb_register *reg, unsigned int nano_sec, + unsigned int *actual_ns) +{ + int i = 0; + + *actual_ns = 2000; + + reg[i].device = NIUSB_SUBDEV_TNT4882; + reg[i].address = nec7210_to_tnt4882_offset(AUXMR); + if (nano_sec <= 1100) { + reg[i].value = AUXRI | USTD | SISB; + *actual_ns = 1100; + } else { + reg[i].value = AUXRI | SISB; + } + i++; + reg[i].device = NIUSB_SUBDEV_TNT4882; + reg[i].address = nec7210_to_tnt4882_offset(AUXMR); + if (nano_sec <= 500) { + reg[i].value = AUXRB | HR_TRI; + *actual_ns = 500; + } else { + reg[i].value = AUXRB; + } + i++; + reg[i].device = NIUSB_SUBDEV_TNT4882; + reg[i].address = KEYREG; + if (nano_sec <= 350) { + reg[i].value = MSTD; + *actual_ns = 350; + } else { + reg[i].value = 0x0; + } + i++; + return i; +} + +static unsigned int ni_usb_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + struct ni_usb_register writes[3]; + unsigned int ibsta; + unsigned int actual_ns; + int i; + + i = ni_usb_setup_t1_delay(writes, nano_sec, &actual_ns); + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return -1; //FIXME should change return type to int for error reporting + } + board->t1_nano_sec = actual_ns; + ni_usb_soft_update_status(board, ibsta, 0); + return actual_ns; +} + +static int ni_usb_allocate_private(gpib_board_t *board) +{ + struct ni_usb_priv *ni_priv; + + board->private_data = kmalloc(sizeof(struct ni_usb_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + ni_priv = board->private_data; + memset(ni_priv, 0, sizeof(struct ni_usb_priv)); + mutex_init(&ni_priv->bulk_transfer_lock); + mutex_init(&ni_priv->control_transfer_lock); + mutex_init(&ni_priv->interrupt_transfer_lock); + mutex_init(&ni_priv->addressed_transfer_lock); + return 0; +} + +static void ni_usb_free_private(struct ni_usb_priv *ni_priv) +{ + usb_free_urb(ni_priv->interrupt_urb); + kfree(ni_priv); +} + +#define NUM_INIT_WRITES 26 +static int ni_usb_setup_init(gpib_board_t *board, struct ni_usb_register *writes) +{ + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + unsigned int mask, actual_ns; + int i = 0; + + writes[i].device = NIUSB_SUBDEV_UNKNOWN3; + writes[i].address = 0x10; + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + writes[i].value = SOFT_RESET; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + mask = AUXRA | HR_HLDA; + if (ni_priv->eos_mode & BIN) + mask |= HR_BIN; + writes[i].value = mask; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = AUXCR; + writes[i].value = mask; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = HSSEL; + writes[i].value = TNT_ONE_CHIP_BIT; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CR; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = IMR0; + writes[i].value = TNT_IMR0_ALWAYS_BITS; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(IMR1); + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(IMR2); + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = IMR3; + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_HLDI; + i++; + + i += ni_usb_setup_t1_delay(&writes[i], board->t1_nano_sec, &actual_ns); + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUXRG | NTNL_BIT; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + if (board->master) + mask = SETSC; // set system controller + else + mask = CLRSC; // clear system controller + writes[i].value = mask; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CIFC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADR); + writes[i].value = board->pad; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x0; + writes[i].value = board->pad; + i++; + + i += ni_usb_write_sad(&writes[i], board->sad, board->sad >= 0); + + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x2; // could this be a timeout ? + writes[i].value = 0xfd; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = 0xf; // undocumented address + writes[i].value = 0x11; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_PON; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CPPF; + i++; + if (i > NUM_INIT_WRITES) { + dev_err(&usb_dev->dev, "%s: bug!, buffer overrun, i=%i\n", __func__, i); + return 0; + } + return i; +} + +static int ni_usb_init(gpib_board_t *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + struct ni_usb_register *writes; + unsigned int ibsta; + int writes_len; + + writes = kmalloc_array(NUM_INIT_WRITES, sizeof(*writes), GFP_KERNEL); + if (!writes) + return -ENOMEM; + + writes_len = ni_usb_setup_init(board, writes); + if (writes_len) + retval = ni_usb_write_registers(ni_priv, writes, writes_len, &ibsta); + else + return -EFAULT; + kfree(writes); + if (retval) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +static void ni_usb_interrupt_complete(struct urb *urb) +{ + gpib_board_t *board = urb->context; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + struct ni_usb_status_block status; + unsigned long flags; + +// printk("debug: %s: status=0x%x, error_count=%i, actual_length=%i\n", __func__, +// urb->status, urb->error_count, urb->actual_length); + + switch (urb->status) { + /* success */ + case 0: + break; + /* unlinked, don't resubmit */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* other error, resubmit */ + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "%s: failed to resubmit interrupt urb\n", __func__); + return; + } + + ni_usb_parse_status_block(urb->transfer_buffer, &status); +// printk("debug: ibsta=0x%x\n", status.ibsta); + + spin_lock_irqsave(&board->spinlock, flags); + ni_priv->monitored_ibsta_bits &= ~status.ibsta; +// printk("debug: monitored_ibsta_bits=0x%x\n", ni_priv->monitored_ibsta_bits); + spin_unlock_irqrestore(&board->spinlock, flags); + + wake_up_interruptible(&board->wait); + + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "%s: failed to resubmit interrupt urb\n", __func__); +} + +static int ni_usb_set_interrupt_monitor(gpib_board_t *board, unsigned int monitored_bits) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + static const int buffer_length = 8; + u8 *buffer; + struct ni_usb_status_block status; + unsigned long flags; + //printk("%s: receive control pipe is %i\n", __func__, pipe); + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + spin_lock_irqsave(&board->spinlock, flags); + ni_priv->monitored_ibsta_bits = ni_usb_ibsta_monitor_mask & monitored_bits; +// dev_err(&usb_dev->dev, "debug: %s: monitored_ibsta_bits=0x%x\n", +// __func__, ni_priv->monitored_ibsta_bits); + spin_unlock_irqrestore(&board->spinlock, flags); + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x300, ni_usb_ibsta_monitor_mask & monitored_bits, + buffer, buffer_length, 1000); + if (retval != buffer_length) { + dev_err(&usb_dev->dev, "%s: usb_control_msg returned %i\n", __func__, retval); + kfree(buffer); + return -1; + } + ni_usb_parse_status_block(buffer, &status); + kfree(buffer); + return 0; +} + +static int ni_usb_setup_urbs(gpib_board_t *board) +{ + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int int_pipe; + int retval; + + if (ni_priv->interrupt_in_endpoint < 0) + return 0; + + mutex_lock(&ni_priv->interrupt_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->interrupt_transfer_lock); + return -ENODEV; + } + ni_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ni_priv->interrupt_urb) { + mutex_unlock(&ni_priv->interrupt_transfer_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int_pipe = usb_rcvintpipe(usb_dev, ni_priv->interrupt_in_endpoint); + usb_fill_int_urb(ni_priv->interrupt_urb, usb_dev, int_pipe, ni_priv->interrupt_buffer, + sizeof(ni_priv->interrupt_buffer), &ni_usb_interrupt_complete, board, 1); + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL); + mutex_unlock(&ni_priv->interrupt_transfer_lock); + if (retval) { + dev_err(&usb_dev->dev, "%s: failed to submit first interrupt urb, retval=%i\n", + __func__, retval); + return retval; + } + return 0; +} + +static void ni_usb_cleanup_urbs(struct ni_usb_priv *ni_priv) +{ + if (ni_priv && ni_priv->bus_interface) { + if (ni_priv->interrupt_urb) + usb_kill_urb(ni_priv->interrupt_urb); + if (ni_priv->bulk_urb) + usb_kill_urb(ni_priv->bulk_urb); + } +} + +static int ni_usb_b_read_serial_number(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + u8 *out_data; + u8 *in_data; + static const int out_data_length = 0x20; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + static const int num_reads = 4; + unsigned int results[4]; + int j; + unsigned int serial_number; + +// printk("%s: %s\n", __func__); + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return -ENOMEM; + + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) { + kfree(in_data); + return -ENOMEM; + } + i += ni_usb_bulk_register_read_header(&out_data[i], num_reads); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_1_REG); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_2_REG); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_3_REG); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_4_REG); + while (i % 4) + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + retval = ni_usb_send_bulk_msg(ni_priv, out_data, out_data_length, &bytes_written, 1000); + if (retval) { + dev_err(&usb_dev->dev, "%s: ni_usb_send_bulk_msg returned %i, bytes_written=%i, i=%li\n", + __func__, + retval, bytes_written, (long)out_data_length); + goto serial_out; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + if (retval) { + dev_err(&usb_dev->dev, "%s: ni_usb_receive_bulk_msg returned %i, bytes_read=%i\n", + __func__, retval, bytes_read); + ni_usb_dump_raw_block(in_data, bytes_read); + goto serial_out; + } + if (ARRAY_SIZE(results) < num_reads) { + dev_err(&usb_dev->dev, "Setup bug\n"); + retval = -EINVAL; + goto serial_out; + } + ni_usb_parse_register_read_block(in_data, results, num_reads); + serial_number = 0; + for (j = 0; j < num_reads; ++j) + serial_number |= (results[j] & 0xff) << (8 * j); + dev_info(&usb_dev->dev, "%s: board serial number is 0x%x\n", __func__, serial_number); + retval = 0; +serial_out: + kfree(in_data); + kfree(out_data); + return retval; +} + +static int ni_usb_hs_wait_for_ready(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + static const int buffer_size = 0x10; + static const int timeout = 50; + static const int msec_sleep_duration = 100; + int i; int retval; + int j; + int unexpected = 0; + unsigned int serial_number; + u8 *buffer; + + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_SERIAL_NUMBER_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, buffer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: usb_control_msg request 0x%x returned %i\n", + __func__, NI_USB_SERIAL_NUMBER_REQUEST, retval); + goto ready_out; + } + j = 0; + if (buffer[j] != NI_USB_SERIAL_NUMBER_REQUEST) { + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x%x\n", + __func__, j, (int)buffer[j], NI_USB_SERIAL_NUMBER_REQUEST); + unexpected = 1; + } + if (unexpected) + ni_usb_dump_raw_block(buffer, retval); + // NI-USB-HS+ pads the serial with 0x0 to make 16 bytes + if (retval != 5 && retval != 16) { + dev_err(&usb_dev->dev, "%s: received unexpected number of bytes = %i, expected 5 or 16\n", + __func__, retval); + ni_usb_dump_raw_block(buffer, retval); + } + serial_number = 0; + serial_number |= buffer[++j]; + serial_number |= (buffer[++j] << 8); + serial_number |= (buffer[++j] << 16); + serial_number |= (buffer[++j] << 24); + dev_info(&usb_dev->dev, "%s: board serial number is 0x%x\n", __func__, serial_number); + for (i = 0; i < timeout; ++i) { + int ready = 0; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_POLL_READY_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, buffer_size, 100); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: usb_control_msg request 0x%x returned %i\n", + __func__, NI_USB_POLL_READY_REQUEST, retval); + goto ready_out; + } + j = 0; + unexpected = 0; + if (buffer[j] != NI_USB_POLL_READY_REQUEST) { // [0] + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x%x\n", + __func__, j, (int)buffer[j], NI_USB_POLL_READY_REQUEST); + unexpected = 1; + } + ++j; + if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [1] HS+ sends 0x0 + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + if (buffer[++j] != 0x0) { // [2] + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x%x\n", + __func__, j, (int)buffer[j], 0x0); + unexpected = 1; + } + ++j; + // MC usb-488 (and sometimes NI-USB-HS?) sends 0x8 here; MC usb-488A sends 0x7 here + // NI-USB-HS+ sends 0x0 + if (buffer[j] != 0x1 && buffer[j] != 0x8 && buffer[j] != 0x7 && buffer[j] != 0x0) { + // [3] + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x0, 0x1, 0x7 or 0x8\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + ++j; + // NI-USB-HS+ sends 0 here + if (buffer[j] != 0x30 && buffer[j] != 0x0) { // [4] + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x30\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + ++j; + // MC usb-488 (and sometimes NI-USB-HS?) and NI-USB-HS+ sends 0x0 here + if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [5] + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + if (buffer[++j] != 0x0) { // [6] + ready = 1; + // NI-USB-HS+ sends 0xf here + if (buffer[j] != 0x2 && buffer[j] != 0xe && buffer[j] != 0xf && + buffer[j] != 0x16) { + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x2, 0xe, 0xf or 0x16\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + } + if (buffer[++j] != 0x0) { // [7] + ready = 1; + // MC usb-488 sends 0x5 here; MC usb-488A sends 0x6 here + if (buffer[j] != 0x3 && buffer[j] != 0x5 && buffer[j] != 0x6 && + buffer[j] != 0x8) { + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x3 or 0x5, 0x6 or 0x08\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + } + ++j; + if (buffer[j] != 0x0 && buffer[j] != 0x2) { // [8] MC usb-488 sends 0x2 here + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x2\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + ++j; + // MC usb-488A and NI-USB-HS sends 0x3 here; NI-USB-HS+ sends 0x30 here + if (buffer[j] != 0x0 && buffer[j] != 0x3 && buffer[j] != 0x30) { // [9] + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x0, 0x3 or 0x30\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + if (buffer[++j] != 0x0) { + ready = 1; + if (buffer[j] != 0x96 && buffer[j] != 0x7 && buffer[j] != 0x6e) { +// [10] MC usb-488 sends 0x7 here + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[%i]=0x%x, expected 0x96, 0x07 or 0x6e\n", + __func__, j, (int)buffer[j]); + unexpected = 1; + } + } + if (unexpected) + ni_usb_dump_raw_block(buffer, retval); + if (ready) + break; + retval = msleep_interruptible(msec_sleep_duration); + if (retval) { + dev_err(&usb_dev->dev, "ni_usb_gpib: msleep interrupted\n"); + retval = -ERESTARTSYS; + goto ready_out; + } + } + retval = 0; + +ready_out: + kfree(buffer); + dev_dbg(&usb_dev->dev, "%s: exit retval=%d\n", __func__, retval); + return retval; +} + +/* This does some extra init for HS+ models, as observed on Windows. One of the + * control requests causes the LED to stop blinking. + * I'm not sure what the other 2 requests do. None of these requests are actually required + * for the adapter to work, maybe they do some init for the analyzer interface + * (which we don't use). + */ +static int ni_usb_hs_plus_extra_init(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + u8 *buffer; + static const int buffer_size = 16; + int transfer_size; + + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + do { + transfer_size = 16; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0x48_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, transfer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: usb_control_msg request 0x%x returned %i\n", + __func__, NI_USB_HS_PLUS_0x48_REQUEST, retval); + break; + } + // expected response data: 48 f3 30 00 00 00 00 00 00 00 00 00 00 00 00 00 + if (buffer[0] != NI_USB_HS_PLUS_0x48_REQUEST) + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[0]=0x%x, expected 0x%x\n", + __func__, (int)buffer[0], NI_USB_HS_PLUS_0x48_REQUEST); + + transfer_size = 2; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_LED_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x1, 0x0, buffer, transfer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: usb_control_msg request 0x%x returned %i\n", + __func__, NI_USB_HS_PLUS_LED_REQUEST, retval); + break; + } + // expected response data: 4b 00 + if (buffer[0] != NI_USB_HS_PLUS_LED_REQUEST) + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[0]=0x%x, expected 0x%x\n", + __func__, (int)buffer[0], NI_USB_HS_PLUS_LED_REQUEST); + + transfer_size = 9; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0xf8_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + 0x0, 0x1, buffer, transfer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "%s: usb_control_msg request 0x%x returned %i\n", + __func__, NI_USB_HS_PLUS_0xf8_REQUEST, retval); + break; + } + // expected response data: f8 01 00 00 00 01 00 00 00 + if (buffer[0] != NI_USB_HS_PLUS_0xf8_REQUEST) + dev_err(&usb_dev->dev, "%s: unexpected data: buffer[0]=0x%x, expected 0x%x\n", + __func__, (int)buffer[0], NI_USB_HS_PLUS_0xf8_REQUEST); + + } while (0); + + // cleanup + kfree(buffer); + return retval; +} + +static inline int ni_usb_device_match(struct usb_interface *interface, + const gpib_board_config_t *config) +{ + if (gpib_match_device_path(&interface->dev, config->device_path) == 0) + return 0; + return 1; +} + +static int ni_usb_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + int retval; + int i; + struct ni_usb_priv *ni_priv; + int product_id; + struct usb_device *usb_dev; + + mutex_lock(&ni_usb_hotplug_lock); + retval = ni_usb_allocate_private(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + ni_priv = board->private_data; + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] && + !usb_get_intfdata(ni_usb_driver_interfaces[i]) && + ni_usb_device_match(ni_usb_driver_interfaces[i], config)) { + ni_priv->bus_interface = ni_usb_driver_interfaces[i]; + usb_set_intfdata(ni_usb_driver_interfaces[i], board); + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + dev_info(&usb_dev->dev, + "bus %d dev num %d attached to gpib minor %d, NI usb interface %i\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + mutex_unlock(&ni_usb_hotplug_lock); + pr_err("No supported NI usb gpib adapters found, have you loaded its firmware?\n"); + return -ENODEV; + } + if (usb_reset_configuration(interface_to_usbdev(ni_priv->bus_interface))) + dev_err(&usb_dev->dev, "ni_usb_gpib: usb_reset_configuration() failed.\n"); + + product_id = le16_to_cpu(usb_dev->descriptor.idProduct); + ni_priv->product_id = product_id; + + timer_setup(&ni_priv->bulk_timer, ni_usb_timeout_handler, 0); + + switch (product_id) { + case USB_DEVICE_ID_NI_USB_B: + ni_priv->bulk_out_endpoint = NIUSB_B_BULK_OUT_ENDPOINT; + ni_priv->bulk_in_endpoint = NIUSB_B_BULK_IN_ENDPOINT; + ni_priv->interrupt_in_endpoint = NIUSB_B_INTERRUPT_IN_ENDPOINT; + ni_usb_b_read_serial_number(ni_priv); + break; + case USB_DEVICE_ID_NI_USB_HS: + case USB_DEVICE_ID_MC_USB_488: + case USB_DEVICE_ID_KUSB_488A: + ni_priv->bulk_out_endpoint = NIUSB_HS_BULK_OUT_ENDPOINT; + ni_priv->bulk_in_endpoint = NIUSB_HS_BULK_IN_ENDPOINT; + ni_priv->interrupt_in_endpoint = NIUSB_HS_INTERRUPT_IN_ENDPOINT; + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + case USB_DEVICE_ID_NI_USB_HS_PLUS: + ni_priv->bulk_out_endpoint = NIUSB_HS_PLUS_BULK_OUT_ENDPOINT; + ni_priv->bulk_in_endpoint = NIUSB_HS_PLUS_BULK_IN_ENDPOINT; + ni_priv->interrupt_in_endpoint = NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT; + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_hs_plus_extra_init(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + default: + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id %x\n", + product_id); + return -EINVAL; + } + + retval = ni_usb_setup_urbs(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_set_interrupt_monitor(board, 0); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + + board->t1_nano_sec = 500; + + retval = ni_usb_init(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + + mutex_unlock(&ni_usb_hotplug_lock); + dev_info(&usb_dev->dev, "%s: attached\n", __func__); + return retval; +} + +static int ni_usb_shutdown_hardware(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + int i = 0; + struct ni_usb_register writes[2]; + static const int writes_length = ARRAY_SIZE(writes); + unsigned int ibsta; + +// printk("%s: %s\n", __func__); + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CR; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN3; + writes[i].address = 0x10; + writes[i].value = 0x0; + i++; + if (i > writes_length) { + dev_err(&usb_dev->dev, "%s: bug!, buffer overrun, i=%i\n", __func__, i); + return -EINVAL; + } + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval) { + dev_err(&usb_dev->dev, "%s: register write failed, retval=%i\n", __func__, retval); + return retval; + } + return 0; +} + +static void ni_usb_detach(gpib_board_t *board) +{ + struct ni_usb_priv *ni_priv; + + mutex_lock(&ni_usb_hotplug_lock); +// under windows, software unplug does chip_reset nec7210 aux command, +// then writes 0x0 to address 0x10 of device 3 + ni_priv = board->private_data; + if (ni_priv) { + if (ni_priv->bus_interface) { + ni_usb_set_interrupt_monitor(board, 0); + ni_usb_shutdown_hardware(ni_priv); + usb_set_intfdata(ni_priv->bus_interface, NULL); + } + mutex_lock(&ni_priv->bulk_transfer_lock); + mutex_lock(&ni_priv->control_transfer_lock); + mutex_lock(&ni_priv->interrupt_transfer_lock); + ni_usb_cleanup_urbs(ni_priv); + ni_usb_free_private(ni_priv); + } + mutex_unlock(&ni_usb_hotplug_lock); +} + +gpib_interface_t ni_usb_gpib_interface = { +name: "ni_usb_b", +attach : ni_usb_attach, +detach : ni_usb_detach, +read : ni_usb_read, +write : ni_usb_write, +command : ni_usb_command, +take_control : ni_usb_take_control, +go_to_standby : ni_usb_go_to_standby, +request_system_control : ni_usb_request_system_control, +interface_clear : ni_usb_interface_clear, +remote_enable : ni_usb_remote_enable, +enable_eos : ni_usb_enable_eos, +disable_eos : ni_usb_disable_eos, +parallel_poll : ni_usb_parallel_poll, +parallel_poll_configure : ni_usb_parallel_poll_configure, +parallel_poll_response : ni_usb_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : ni_usb_line_status, +update_status : ni_usb_update_status, +primary_address : ni_usb_primary_address, +secondary_address : ni_usb_secondary_address, +serial_poll_response : ni_usb_serial_poll_response, +serial_poll_status : ni_usb_serial_poll_status, +t1_delay : ni_usb_t1_delay, +return_to_local : ni_usb_return_to_local, +skip_check_for_command_acceptors : 1 +}; + +// Table with the USB-devices: just now only testing IDs +static struct usb_device_id ni_usb_driver_device_table[] = { + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_B)}, + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS)}, + // gpib-usb-hs+ has a second interface for the analyzer, which we ignore + {USB_DEVICE_INTERFACE_NUMBER(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS_PLUS, 0)}, + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_KUSB_488A)}, + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_MC_USB_488)}, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, ni_usb_driver_device_table); + +static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + int i; + char *path; + static const int path_length = 1024; + + mutex_lock(&ni_usb_hotplug_lock); + usb_get_dev(usb_dev); + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (!ni_usb_driver_interfaces[i]) { + ni_usb_driver_interfaces[i] = interface; + usb_set_intfdata(interface, NULL); + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + usb_put_dev(usb_dev); + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(&usb_dev->dev, "%s: ni_usb_driver_interfaces[] full\n", __func__); + return -1; + } + path = kmalloc(path_length, GFP_KERNEL); + if (!path) { + usb_put_dev(usb_dev); + mutex_unlock(&ni_usb_hotplug_lock); + return -ENOMEM; + } + usb_make_path(usb_dev, path, path_length); + dev_info(&usb_dev->dev, "ni_usb_gpib: probe succeeded for path: %s\n", path); + kfree(path); + mutex_unlock(&ni_usb_hotplug_lock); + return 0; +} + +static void ni_usb_driver_disconnect(struct usb_interface *interface) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + int i; + + mutex_lock(&ni_usb_hotplug_lock); + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] == interface) { + gpib_board_t *board = usb_get_intfdata(interface); + + if (board) { + struct ni_usb_priv *ni_priv = board->private_data; + + if (ni_priv) { + mutex_lock(&ni_priv->bulk_transfer_lock); + mutex_lock(&ni_priv->control_transfer_lock); + mutex_lock(&ni_priv->interrupt_transfer_lock); + ni_usb_cleanup_urbs(ni_priv); + ni_priv->bus_interface = NULL; + mutex_unlock(&ni_priv->interrupt_transfer_lock); + mutex_unlock(&ni_priv->control_transfer_lock); + mutex_unlock(&ni_priv->bulk_transfer_lock); + } + } + ni_usb_driver_interfaces[i] = NULL; + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) + dev_err(&usb_dev->dev, "%s: unable to find interface in ni_usb_driver_interfaces[]? bug?\n", + __func__); + usb_put_dev(usb_dev); + mutex_unlock(&ni_usb_hotplug_lock); +} + +static int ni_usb_driver_suspend(struct usb_interface *interface, pm_message_t message) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + gpib_board_t *board; + int i, retval; + + mutex_lock(&ni_usb_hotplug_lock); + + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] == interface) { + board = usb_get_intfdata(interface); + if (board) + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + mutex_unlock(&ni_usb_hotplug_lock); + return 0; + } + + struct ni_usb_priv *ni_priv = board->private_data; + + if (ni_priv) { + ni_usb_set_interrupt_monitor(board, 0); + retval = ni_usb_shutdown_hardware(ni_priv); + if (retval) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + if (ni_priv->interrupt_urb) { + mutex_lock(&ni_priv->interrupt_transfer_lock); + ni_usb_cleanup_urbs(ni_priv); + mutex_unlock(&ni_priv->interrupt_transfer_lock); + } + dev_info(&usb_dev->dev, + "bus %d dev num %d gpib minor %d, ni usb interface %i suspended\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + } + + mutex_unlock(&ni_usb_hotplug_lock); + return 0; +} + +static int ni_usb_driver_resume(struct usb_interface *interface) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + + gpib_board_t *board; + int i, retval; + + mutex_lock(&ni_usb_hotplug_lock); + + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] == interface) { + board = usb_get_intfdata(interface); + if (board) + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + mutex_unlock(&ni_usb_hotplug_lock); + return 0; + } + + struct ni_usb_priv *ni_priv = board->private_data; + + if (ni_priv) { + if (ni_priv->interrupt_urb) { + mutex_lock(&ni_priv->interrupt_transfer_lock); + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "%s: failed to resubmit interrupt urb, retval=%i\n", + __func__, retval); + mutex_unlock(&ni_priv->interrupt_transfer_lock); + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + mutex_unlock(&ni_priv->interrupt_transfer_lock); + } else { + dev_err(&usb_dev->dev, "%s: bug! int urb not set up\n", __func__); + mutex_unlock(&ni_usb_hotplug_lock); + return -EINVAL; + } + + switch (ni_priv->product_id) { + case USB_DEVICE_ID_NI_USB_B: + ni_usb_b_read_serial_number(ni_priv); + break; + case USB_DEVICE_ID_NI_USB_HS: + case USB_DEVICE_ID_MC_USB_488: + case USB_DEVICE_ID_KUSB_488A: + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + case USB_DEVICE_ID_NI_USB_HS_PLUS: + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_hs_plus_extra_init(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + default: + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id\n"); + return -EINVAL; + } + + retval = ni_usb_set_interrupt_monitor(board, 0); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + + retval = ni_usb_init(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + if (board->master) + ni_usb_interface_clear(board, 1); // this is a pulsed action + if (ni_priv->ren_state) + ni_usb_remote_enable(board, 1); + + dev_info(&usb_dev->dev, + "bus %d dev num %d gpib minor %d, ni usb interface %i resumed\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + } + + mutex_unlock(&ni_usb_hotplug_lock); + return 0; +} + +static struct usb_driver ni_usb_bus_driver = { + .name = "ni_usb_gpib", + .probe = ni_usb_driver_probe, + .disconnect = ni_usb_driver_disconnect, + .suspend = ni_usb_driver_suspend, + .resume = ni_usb_driver_resume, + .id_table = ni_usb_driver_device_table, +}; + +static int __init ni_usb_init_module(void) +{ + int i; + + pr_info("ni_usb_gpib driver loading\n"); + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) + ni_usb_driver_interfaces[i] = NULL; + usb_register(&ni_usb_bus_driver); + gpib_register_driver(&ni_usb_gpib_interface, THIS_MODULE); + + return 0; +} + +static void __exit ni_usb_exit_module(void) +{ + pr_info("ni_usb_gpib driver unloading\n"); + gpib_unregister_driver(&ni_usb_gpib_interface); + usb_deregister(&ni_usb_bus_driver); +} + +module_init(ni_usb_init_module); +module_exit(ni_usb_exit_module); diff --git a/drivers/staging/gpib/ni_usb/ni_usb_gpib.h b/drivers/staging/gpib/ni_usb/ni_usb_gpib.h new file mode 100644 index 0000000000000..9b21dfa0f3f6d --- /dev/null +++ b/drivers/staging/gpib/ni_usb/ni_usb_gpib.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2004 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _NI_USB_GPIB_H +#define _NI_USB_GPIB_H + +#include +#include +#include +#include +#include "gpibP.h" + +enum { + USB_VENDOR_ID_NI = 0x3923 +}; + +enum { + USB_DEVICE_ID_NI_USB_B = 0x702a, + USB_DEVICE_ID_NI_USB_B_PREINIT = 0x702b, // device id before firmware is loaded + USB_DEVICE_ID_NI_USB_HS = 0x709b, + USB_DEVICE_ID_NI_USB_HS_PLUS = 0x7618, + USB_DEVICE_ID_KUSB_488A = 0x725c, + USB_DEVICE_ID_MC_USB_488 = 0x725d +}; + +enum ni_usb_device { + NIUSB_SUBDEV_TNT4882 = 1, + NIUSB_SUBDEV_UNKNOWN2 = 2, + NIUSB_SUBDEV_UNKNOWN3 = 3, +}; + +enum endpoint_addresses { + NIUSB_B_BULK_OUT_ENDPOINT = 0x2, + NIUSB_B_BULK_IN_ENDPOINT = 0x2, + NIUSB_B_BULK_IN_ALT_ENDPOINT = 0x6, + NIUSB_B_INTERRUPT_IN_ENDPOINT = 0x4, +}; + +enum hs_enpoint_addresses { + NIUSB_HS_BULK_OUT_ENDPOINT = 0x2, + NIUSB_HS_BULK_OUT_ALT_ENDPOINT = 0x6, + NIUSB_HS_BULK_IN_ENDPOINT = 0x4, + NIUSB_HS_BULK_IN_ALT_ENDPOINT = 0x8, + NIUSB_HS_INTERRUPT_IN_ENDPOINT = 0x1, +}; + +enum hs_plus_endpoint_addresses { + NIUSB_HS_PLUS_BULK_OUT_ENDPOINT = 0x1, + NIUSB_HS_PLUS_BULK_OUT_ALT_ENDPOINT = 0x4, + NIUSB_HS_PLUS_BULK_IN_ENDPOINT = 0x2, + NIUSB_HS_PLUS_BULK_IN_ALT_ENDPOINT = 0x5, + NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT = 0x3, +}; + +struct ni_usb_urb_ctx { + struct semaphore complete; + unsigned timed_out : 1; +}; + +// struct which defines private_data for ni_usb devices +struct ni_usb_priv { + struct usb_interface *bus_interface; + int bulk_out_endpoint; + int bulk_in_endpoint; + int interrupt_in_endpoint; + u8 eos_char; + unsigned short eos_mode; + unsigned int monitored_ibsta_bits; + struct urb *bulk_urb; + struct urb *interrupt_urb; + u8 interrupt_buffer[0x11]; + struct mutex addressed_transfer_lock; // protect transfer lock + struct mutex bulk_transfer_lock; // protect bulk message sends + struct mutex control_transfer_lock; // protect control messages + struct mutex interrupt_transfer_lock; // protect interrupt messages + struct timer_list bulk_timer; + struct ni_usb_urb_ctx context; + int product_id; + unsigned short ren_state; +}; + +struct ni_usb_status_block { + short id; + unsigned short ibsta; + short error_code; + unsigned short count; +}; + +struct ni_usb_register { + enum ni_usb_device device; + short address; + unsigned short value; +}; + +enum ni_usb_bulk_ids { + NIUSB_IBCAC_ID = 0x1, + NIUSB_UNKNOWN3_ID = 0x3, // device level function id? + NIUSB_TERM_ID = 0x4, + NIUSB_IBGTS_ID = 0x6, + NIUSB_IBRPP_ID = 0x7, + NIUSB_REG_READ_ID = 0x8, + NIUSB_REG_WRITE_ID = 0x9, + NIUSB_IBSIC_ID = 0xf, + NIUSB_REGISTER_READ_DATA_START_ID = 0x34, + NIUSB_REGISTER_READ_DATA_END_ID = 0x35, + NIUSB_IBRD_DATA_ID = 0x36, + NIUSB_IBRD_EXTENDED_DATA_ID = 0x37, + NIUSB_IBRD_STATUS_ID = 0x38 +}; + +enum ni_usb_error_codes { + NIUSB_NO_ERROR = 0, + /* NIUSB_ABORTED_ERROR occurs when I/O is interrupted early by + * doing a NI_USB_STOP_REQUEST on the control endpoint. + */ + NIUSB_ABORTED_ERROR = 1, + // NIUSB_READ_ATN_ERROR occurs when you do a board read while + // ATN is set + NIUSB_ATN_STATE_ERROR = 2, + // NIUSB_ADDRESSING_ERROR occurs when you do a board + // read/write as CIC but are not in LACS/TACS + NIUSB_ADDRESSING_ERROR = 3, + /* NIUSB_EOSMODE_ERROR occurs on reads if any eos mode or char + * bits are set when REOS is not set. + * Have also seen error 4 if you try to send more than 16 + * command bytes at once on a usb-b. + */ + NIUSB_EOSMODE_ERROR = 4, + // NIUSB_NO_BUS_ERROR occurs when you try to write a command + // byte but there are no devices connected to the gpib bus + NIUSB_NO_BUS_ERROR = 5, + // NIUSB_NO_LISTENER_ERROR occurs when you do a board write as + // CIC with no listener + NIUSB_NO_LISTENER_ERROR = 8, + // get NIUSB_TIMEOUT_ERROR on board read/write timeout + NIUSB_TIMEOUT_ERROR = 10, +}; + +enum ni_usb_control_requests { + NI_USB_STOP_REQUEST = 0x20, + NI_USB_WAIT_REQUEST = 0x21, + NI_USB_POLL_READY_REQUEST = 0x40, + NI_USB_SERIAL_NUMBER_REQUEST = 0x41, + NI_USB_HS_PLUS_0x48_REQUEST = 0x48, + NI_USB_HS_PLUS_LED_REQUEST = 0x4b, + NI_USB_HS_PLUS_0xf8_REQUEST = 0xf8 +}; + +static const unsigned int ni_usb_ibsta_monitor_mask = + SRQI | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS; + +static inline int nec7210_to_tnt4882_offset(int offset) +{ + return 2 * offset; +}; + +static inline int ni_usb_bulk_termination(u8 *buffer) +{ + int i = 0; + + buffer[i++] = NIUSB_TERM_ID; + buffer[i++] = 0x0; + buffer[i++] = 0x0; + buffer[i++] = 0x0; + return i; +} + +enum ni_usb_unknown3_register { + SERIAL_NUMBER_4_REG = 0x8, + SERIAL_NUMBER_3_REG = 0x9, + SERIAL_NUMBER_2_REG = 0xa, + SERIAL_NUMBER_1_REG = 0xb, +}; + +static inline int ni_usb_bulk_register_write_header(u8 *buffer, int num_writes) +{ + int i = 0; + + buffer[i++] = NIUSB_REG_WRITE_ID; + buffer[i++] = num_writes; + buffer[i++] = 0x0; + return i; +} + +static inline int ni_usb_bulk_register_write(u8 *buffer, struct ni_usb_register reg) +{ + int i = 0; + + buffer[i++] = reg.device; + buffer[i++] = reg.address; + buffer[i++] = reg.value; + return i; +} + +static inline int ni_usb_bulk_register_read_header(u8 *buffer, int num_reads) +{ + int i = 0; + + buffer[i++] = NIUSB_REG_READ_ID; + buffer[i++] = num_reads; + return i; +} + +static inline int ni_usb_bulk_register_read(u8 *buffer, int device, int address) +{ + int i = 0; + + buffer[i++] = device; + buffer[i++] = address; + return i; +} + +#endif // _NI_USB_GPIB_H diff --git a/drivers/staging/gpib/pc2/Makefile b/drivers/staging/gpib/pc2/Makefile new file mode 100644 index 0000000000000..8148425e0f876 --- /dev/null +++ b/drivers/staging/gpib/pc2/Makefile @@ -0,0 +1,5 @@ + +obj-m += pc2_gpib.o + + + diff --git a/drivers/staging/gpib/pc2/pc2_gpib.c b/drivers/staging/gpib/pc2/pc2_gpib.c new file mode 100644 index 0000000000000..7b3b34f473416 --- /dev/null +++ b/drivers/staging/gpib/pc2/pc2_gpib.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nec7210.h" +#include "gpibP.h" + +// struct which defines private_data for pc2 driver +struct pc2_priv { + struct nec7210_priv nec7210_priv; + unsigned int irq; + // io address that clears interrupt for pc2a (0x2f0 + irq) + unsigned int clear_intr_addr; +}; + +// pc2 uses 8 consecutive io addresses +static const int pc2_iosize = 8; +static const int pc2a_iosize = 8; +static const int pc2_2a_iosize = 16; + +// offset between io addresses of successive nec7210 registers +static const int pc2a_reg_offset = 0x400; +static const int pc2_reg_offset = 1; + +//interrupt service routine +static irqreturn_t pc2_interrupt(int irq, void *arg); +static irqreturn_t pc2a_interrupt(int irq, void *arg); + +// pc2 specific registers and bits + +// interrupt clear register address +static const int pc2a_clear_intr_iobase = 0x2f0; +static inline unsigned int CLEAR_INTR_REG(unsigned int irq) +{ + return pc2a_clear_intr_iobase + irq; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for PC2/PC2a and compatible devices"); + +static int pc2_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int pc2a_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int pc2a_cb7210_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int pc2_2a_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void pc2_detach(gpib_board_t *board); +static void pc2a_detach(gpib_board_t *board); +static void pc2_2a_detach(gpib_board_t *board); + +/* + * GPIB interrupt service routines + */ + +irqreturn_t pc2_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct pc2_priv *priv = board->private_data; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = nec7210_interrupt(board, &priv->nec7210_priv); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +irqreturn_t pc2a_interrupt(int irq, void *arg) +{ + gpib_board_t *board = arg; + struct pc2_priv *priv = board->private_data; + int status1, status2; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + // read interrupt status (also clears status) + status1 = read_byte(&priv->nec7210_priv, ISR1); + status2 = read_byte(&priv->nec7210_priv, ISR2); + /* clear interrupt circuit */ + if (priv->irq) + outb(0xff, CLEAR_INTR_REG(priv->irq)); + retval = nec7210_interrupt_have_status(board, &priv->nec7210_priv, status1, status2); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +// wrappers for interface functions +static int pc2_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int pc2_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int pc2_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int pc2_take_control(gpib_board_t *board, int synchronous) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int pc2_go_to_standby(gpib_board_t *board) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static void pc2_request_system_control(gpib_board_t *board, int request_control) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_request_system_control(board, &priv->nec7210_priv, request_control); +} + +static void pc2_interface_clear(gpib_board_t *board, int assert) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void pc2_remote_enable(gpib_board_t *board, int enable) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int pc2_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void pc2_disable_eos(gpib_board_t *board) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int pc2_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int pc2_primary_address(gpib_board_t *board, unsigned int address) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int pc2_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int pc2_parallel_poll(gpib_board_t *board, uint8_t *result) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void pc2_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); +} + +static void pc2_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void pc2_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static uint8_t pc2_serial_poll_status(gpib_board_t *board) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static unsigned int pc2_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec); +} + +static void pc2_return_to_local(gpib_board_t *board) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +gpib_interface_t pc2_interface = { +name: "pcII", +attach : pc2_attach, +detach : pc2_detach, +read : pc2_read, +write : pc2_write, +command : pc2_command, +take_control : pc2_take_control, +go_to_standby : pc2_go_to_standby, +request_system_control : pc2_request_system_control, +interface_clear : pc2_interface_clear, +remote_enable : pc2_remote_enable, +enable_eos : pc2_enable_eos, +disable_eos : pc2_disable_eos, +parallel_poll : pc2_parallel_poll, +parallel_poll_configure : pc2_parallel_poll_configure, +parallel_poll_response : pc2_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, +update_status : pc2_update_status, +primary_address : pc2_primary_address, +secondary_address : pc2_secondary_address, +serial_poll_response : pc2_serial_poll_response, +serial_poll_status : pc2_serial_poll_status, +t1_delay : pc2_t1_delay, +return_to_local : pc2_return_to_local, +}; + +gpib_interface_t pc2a_interface = { +name: "pcIIa", +attach : pc2a_attach, +detach : pc2a_detach, +read : pc2_read, +write : pc2_write, +command : pc2_command, +take_control : pc2_take_control, +go_to_standby : pc2_go_to_standby, +request_system_control : pc2_request_system_control, +interface_clear : pc2_interface_clear, +remote_enable : pc2_remote_enable, +enable_eos : pc2_enable_eos, +disable_eos : pc2_disable_eos, +parallel_poll : pc2_parallel_poll, +parallel_poll_configure : pc2_parallel_poll_configure, +parallel_poll_response : pc2_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, +update_status : pc2_update_status, +primary_address : pc2_primary_address, +secondary_address : pc2_secondary_address, +serial_poll_response : pc2_serial_poll_response, +serial_poll_status : pc2_serial_poll_status, +t1_delay : pc2_t1_delay, +return_to_local : pc2_return_to_local, +}; + +gpib_interface_t pc2a_cb7210_interface = { +name: "pcIIa_cb7210", +attach : pc2a_cb7210_attach, +detach : pc2a_detach, +read : pc2_read, +write : pc2_write, +command : pc2_command, +take_control : pc2_take_control, +go_to_standby : pc2_go_to_standby, +request_system_control : pc2_request_system_control, +interface_clear : pc2_interface_clear, +remote_enable : pc2_remote_enable, +enable_eos : pc2_enable_eos, +disable_eos : pc2_disable_eos, +parallel_poll : pc2_parallel_poll, +parallel_poll_configure : pc2_parallel_poll_configure, +parallel_poll_response : pc2_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, //XXX +update_status : pc2_update_status, +primary_address : pc2_primary_address, +secondary_address : pc2_secondary_address, +serial_poll_response : pc2_serial_poll_response, +serial_poll_status : pc2_serial_poll_status, +t1_delay : pc2_t1_delay, +return_to_local : pc2_return_to_local, +}; + +gpib_interface_t pc2_2a_interface = { +name: "pcII_IIa", +attach : pc2_2a_attach, +detach : pc2_2a_detach, +read : pc2_read, +write : pc2_write, +command : pc2_command, +take_control : pc2_take_control, +go_to_standby : pc2_go_to_standby, +request_system_control : pc2_request_system_control, +interface_clear : pc2_interface_clear, +remote_enable : pc2_remote_enable, +enable_eos : pc2_enable_eos, +disable_eos : pc2_disable_eos, +parallel_poll : pc2_parallel_poll, +parallel_poll_configure : pc2_parallel_poll_configure, +parallel_poll_response : pc2_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, +update_status : pc2_update_status, +primary_address : pc2_primary_address, +secondary_address : pc2_secondary_address, +serial_poll_response : pc2_serial_poll_response, +serial_poll_status : pc2_serial_poll_status, +t1_delay : pc2_t1_delay, +return_to_local : pc2_return_to_local, +}; + +static int allocate_private(gpib_board_t *board) +{ + struct pc2_priv *priv; + + board->private_data = kmalloc(sizeof(struct pc2_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct pc2_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +static void free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int pc2_generic_attach(gpib_board_t *board, const gpib_board_config_t *config, + enum nec7210_chipset chipset) +{ + struct pc2_priv *pc2_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + if (allocate_private(board)) + return -ENOMEM; + pc2_priv = board->private_data; + nec_priv = &pc2_priv->nec7210_priv; + nec_priv->read_byte = nec7210_ioport_read_byte; + nec_priv->write_byte = nec7210_ioport_write_byte; + nec_priv->type = chipset; + +#ifndef PC2_DMA + /* board->dev hasn't been initialized, so forget about DMA until this driver + * is adapted to use isa_register_driver. + */ + if (config->ibdma) + pr_err("DMA disabled for pc2 gpib, driver needs to be adapted to use isa_register_driver to get a struct device*"); +#else + if (config->ibdma) { + nec_priv->dma_buffer_length = 0x1000; + nec_priv->dma_buffer = dma_alloc_coherent(board->dev, + nec_priv->dma_buffer_length, & + nec_priv->dma_buffer_addr, GFP_ATOMIC); + if (!nec_priv->dma_buffer) + return -ENOMEM; + + // request isa dma channel + if (request_dma(config->ibdma, "pc2")) { + pr_err("gpib: can't request DMA %d\n", config->ibdma); + return -1; + } + nec_priv->dma_channel = config->ibdma; + } +#endif + + return 0; +} + +int pc2_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + int isr_flags = 0; + struct pc2_priv *pc2_priv; + struct nec7210_priv *nec_priv; + int retval; + + retval = pc2_generic_attach(board, config, NEC7210); + if (retval) + return retval; + + pc2_priv = board->private_data; + nec_priv = &pc2_priv->nec7210_priv; + nec_priv->offset = pc2_reg_offset; + + if (request_region((unsigned long)config->ibbase, pc2_iosize, "pc2") == 0) { + pr_err("gpib: ioports are already in use\n"); + return -1; + } + nec_priv->iobase = config->ibbase; + + nec7210_board_reset(nec_priv, board); + + // install interrupt handler + if (config->ibirq) { + if (request_irq(config->ibirq, pc2_interrupt, isr_flags, "pc2", board)) { + pr_err("gpib: can't request IRQ %d\n", config->ibirq); + return -1; + } + } + pc2_priv->irq = config->ibirq; + /* poll so we can detect assertion of ATN */ + if (gpib_request_pseudo_irq(board, pc2_interrupt)) { + pr_err("pc2_gpib: failed to allocate pseudo_irq\n"); + return -1; + } + /* set internal counter register for 8 MHz input clock */ + write_byte(nec_priv, ICR | 8, AUXMR); + + nec7210_board_online(nec_priv, board); + + return 0; +} + +void pc2_detach(gpib_board_t *board) +{ + struct pc2_priv *pc2_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (pc2_priv) { + nec_priv = &pc2_priv->nec7210_priv; +#ifdef PC2_DMA + if (nec_priv->dma_channel) + free_dma(nec_priv->dma_channel); +#endif + gpib_free_pseudo_irq(board); + if (pc2_priv->irq) + free_irq(pc2_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region((unsigned long)(nec_priv->iobase), pc2_iosize); + } + if (nec_priv->dma_buffer) { + dma_free_coherent(board->dev, nec_priv->dma_buffer_length, + nec_priv->dma_buffer, nec_priv->dma_buffer_addr); + nec_priv->dma_buffer = NULL; + } + } + free_private(board); +} + +static int pc2a_common_attach(gpib_board_t *board, const gpib_board_config_t *config, + unsigned int num_registers, enum nec7210_chipset chipset) +{ + unsigned int i, j; + struct pc2_priv *pc2_priv; + struct nec7210_priv *nec_priv; + int retval; + + retval = pc2_generic_attach(board, config, chipset); + if (retval) + return retval; + + pc2_priv = board->private_data; + nec_priv = &pc2_priv->nec7210_priv; + nec_priv->offset = pc2a_reg_offset; + + switch ((unsigned long)(config->ibbase)) { + case 0x02e1: + case 0x22e1: + case 0x42e1: + case 0x62e1: + break; + default: + pr_err("PCIIa base range invalid, must be one of 0x[0246]2e1, but is 0x%p\n", + config->ibbase); + return -1; + } + + if (config->ibirq) { + if (config->ibirq < 2 || config->ibirq > 7) { + pr_err("pc2_gpib: illegal interrupt level %i\n", config->ibirq); + return -1; + } + } else { + pr_err("pc2_gpib: interrupt disabled, using polling mode (slow)\n"); + } +#ifdef CHECK_IOPORTS + unsigned int err = 0; + + for (i = 0; i < num_registers; i++) { + if (check_region((unsigned long)config->ibbase + i * pc2a_reg_offset, 1)) + err++; + } + if (config->ibirq && check_region(pc2a_clear_intr_iobase + config->ibirq, 1)) + err++; + if (err) { + pr_err("gpib: ioports are already in use"); + return -1; + } +#endif + for (i = 0; i < num_registers; i++) { + if (!request_region((unsigned long)config->ibbase + + i * pc2a_reg_offset, 1, "pc2a")) { + pr_err("gpib: ioports are already in use"); + for (j = 0; j < i; j++) + release_region((unsigned long)(config->ibbase) + + j * pc2a_reg_offset, 1); + return -1; + } + } + nec_priv->iobase = config->ibbase; + if (config->ibirq) { + if (!request_region(pc2a_clear_intr_iobase + config->ibirq, 1, "pc2a")) { + pr_err("gpib: ioports are already in use"); + return -1; + } + pc2_priv->clear_intr_addr = pc2a_clear_intr_iobase + config->ibirq; + if (request_irq(config->ibirq, pc2a_interrupt, 0, "pc2a", board)) { + pr_err("gpib: can't request IRQ %d\n", config->ibirq); + return -1; + } + } + pc2_priv->irq = config->ibirq; + /* poll so we can detect assertion of ATN */ + if (gpib_request_pseudo_irq(board, pc2_interrupt)) { + pr_err("pc2_gpib: failed to allocate pseudo_irq\n"); + return -1; + } + + // make sure interrupt is clear + if (pc2_priv->irq) + outb(0xff, CLEAR_INTR_REG(pc2_priv->irq)); + + nec7210_board_reset(nec_priv, board); + + /* set internal counter register for 8 MHz input clock */ + write_byte(nec_priv, ICR | 8, AUXMR); + + nec7210_board_online(nec_priv, board); + + return 0; +} + +int pc2a_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return pc2a_common_attach(board, config, pc2a_iosize, NEC7210); +} + +int pc2a_cb7210_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return pc2a_common_attach(board, config, pc2a_iosize, CB7210); +} + +int pc2_2a_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return pc2a_common_attach(board, config, pc2_2a_iosize, NAT4882); +} + +static void pc2a_common_detach(gpib_board_t *board, unsigned int num_registers) +{ + int i; + struct pc2_priv *pc2_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (pc2_priv) { + nec_priv = &pc2_priv->nec7210_priv; +#ifdef PC2_DMA + if (nec_priv->dma_channel) + free_dma(nec_priv->dma_channel); +#endif + gpib_free_pseudo_irq(board); + if (pc2_priv->irq) + free_irq(pc2_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + for (i = 0; i < num_registers; i++) + release_region((unsigned long)nec_priv->iobase + + i * pc2a_reg_offset, 1); + } + if (pc2_priv->clear_intr_addr) + release_region(pc2_priv->clear_intr_addr, 1); + if (nec_priv->dma_buffer) { + dma_free_coherent(board->dev, nec_priv->dma_buffer_length, + nec_priv->dma_buffer, + nec_priv->dma_buffer_addr); + nec_priv->dma_buffer = NULL; + } + } + free_private(board); +} + +void pc2a_detach(gpib_board_t *board) +{ + pc2a_common_detach(board, pc2a_iosize); +} + +void pc2_2a_detach(gpib_board_t *board) +{ + pc2a_common_detach(board, pc2_2a_iosize); +} + +static int __init pc2_init_module(void) +{ + gpib_register_driver(&pc2_interface, THIS_MODULE); + gpib_register_driver(&pc2a_interface, THIS_MODULE); + gpib_register_driver(&pc2a_cb7210_interface, THIS_MODULE); + gpib_register_driver(&pc2_2a_interface, THIS_MODULE); + + return 0; +} + +static void __exit pc2_exit_module(void) +{ + gpib_unregister_driver(&pc2_interface); + gpib_unregister_driver(&pc2a_interface); + gpib_unregister_driver(&pc2a_cb7210_interface); + gpib_unregister_driver(&pc2_2a_interface); +} + +module_init(pc2_init_module); +module_exit(pc2_exit_module); + diff --git a/drivers/staging/gpib/tms9914/Makefile b/drivers/staging/gpib/tms9914/Makefile new file mode 100644 index 0000000000000..81b7e3cf104c0 --- /dev/null +++ b/drivers/staging/gpib/tms9914/Makefile @@ -0,0 +1,6 @@ + +obj-m += tms9914.o + + + + diff --git a/drivers/staging/gpib/tms9914/tms9914.c b/drivers/staging/gpib/tms9914/tms9914.c new file mode 100644 index 0000000000000..152b243b845b5 --- /dev/null +++ b/drivers/staging/gpib/tms9914/tms9914.c @@ -0,0 +1,910 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpibP.h" +#include "tms9914.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB library for tms9914"); + +static unsigned int update_status_nolock(gpib_board_t *board, struct tms9914_priv *priv); + +int tms9914_take_control(gpib_board_t *board, struct tms9914_priv *priv, int synchronous) +{ + int i; + const int timeout = 100; + + if (synchronous) + write_byte(priv, AUX_TCS, AUXCR); + else + write_byte(priv, AUX_TCA, AUXCR); + // busy wait until ATN is asserted + for (i = 0; i < timeout; i++) { + if ((read_byte(priv, ADSR) & HR_ATN)) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + + clear_bit(WRITE_READY_BN, &priv->state); + + return 0; +} +EXPORT_SYMBOL_GPL(tms9914_take_control); + +/* The agilent 82350B has a buggy implementation of tcs which interferes with the + * operation of tca. It appears to be based on the controller state machine + * described in the TI 9900 TMS9914A data manual published in 1982. This + * manual describes tcs as putting the controller into a CWAS + * state where it waits indefinitely for ANRS and ignores tca. Since a + * functioning tca is far more important than tcs, we work around the + * problem by never issuing tcs. + * + * I don't know if this problem exists in the real tms9914a or just in the fpga + * of the 82350B. For now, only the agilent_82350b uses this workaround. + * The rest of the tms9914 based drivers still use tms9914_take_control + * directly (which does issue tcs). + */ +int tms9914_take_control_workaround(gpib_board_t *board, struct tms9914_priv *priv, int synchronous) +{ + if (synchronous) + return -ETIMEDOUT; + return tms9914_take_control(board, priv, synchronous); +} +EXPORT_SYMBOL_GPL(tms9914_take_control_workaround); + +int tms9914_go_to_standby(gpib_board_t *board, struct tms9914_priv *priv) +{ + int i; + const int timeout = 1000; + + write_byte(priv, AUX_GTS, AUXCR); + // busy wait until ATN is released + for (i = 0; i < timeout; i++) { + if ((read_byte(priv, ADSR) & HR_ATN) == 0) + break; + udelay(1); + } + if (i == timeout) { + pr_err("error waiting for NATN\n"); + return -ETIMEDOUT; + } + + clear_bit(COMMAND_READY_BN, &priv->state); + + return 0; +} +EXPORT_SYMBOL_GPL(tms9914_go_to_standby); + +void tms9914_interface_clear(gpib_board_t *board, struct tms9914_priv *priv, int assert) +{ + if (assert) { + write_byte(priv, AUX_SIC | AUX_CS, AUXCR); + + set_bit(CIC_NUM, &board->status); + } else { + write_byte(priv, AUX_SIC, AUXCR); + } +} +EXPORT_SYMBOL_GPL(tms9914_interface_clear); + +void tms9914_remote_enable(gpib_board_t *board, struct tms9914_priv *priv, int enable) +{ + if (enable) + write_byte(priv, AUX_SRE | AUX_CS, AUXCR); + else + write_byte(priv, AUX_SRE, AUXCR); +} +EXPORT_SYMBOL_GPL(tms9914_remote_enable); + +void tms9914_request_system_control(gpib_board_t *board, struct tms9914_priv *priv, + int request_control) +{ + if (request_control) { + write_byte(priv, AUX_RQC, AUXCR); + } else { + clear_bit(CIC_NUM, &board->status); + write_byte(priv, AUX_RLC, AUXCR); + } +} +EXPORT_SYMBOL_GPL(tms9914_request_system_control); + +unsigned int tms9914_t1_delay(gpib_board_t *board, struct tms9914_priv *priv, + unsigned int nano_sec) +{ + static const int clock_period = 200; // assuming 5Mhz input clock + int num_cycles; + + num_cycles = 12; + + if (nano_sec <= 8 * clock_period) { + write_byte(priv, AUX_STDL | AUX_CS, AUXCR); + num_cycles = 8; + } else { + write_byte(priv, AUX_STDL, AUXCR); + } + + if (nano_sec <= 4 * clock_period) { + write_byte(priv, AUX_VSTDL | AUX_CS, AUXCR); + num_cycles = 4; + } else { + write_byte(priv, AUX_VSTDL, AUXCR); + } + + return num_cycles * clock_period; +} +EXPORT_SYMBOL_GPL(tms9914_t1_delay); + +void tms9914_return_to_local(const gpib_board_t *board, struct tms9914_priv *priv) +{ + write_byte(priv, AUX_RTL, AUXCR); +} +EXPORT_SYMBOL_GPL(tms9914_return_to_local); + +void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode) +{ + switch (mode) { + case TMS9914_HOLDOFF_NONE: + write_byte(priv, AUX_HLDE, AUXCR); + write_byte(priv, AUX_HLDA, AUXCR); + break; + case TMS9914_HOLDOFF_EOI: + write_byte(priv, AUX_HLDE | AUX_CS, AUXCR); + write_byte(priv, AUX_HLDA, AUXCR); + break; + case TMS9914_HOLDOFF_ALL: + write_byte(priv, AUX_HLDE, AUXCR); + write_byte(priv, AUX_HLDA | AUX_CS, AUXCR); + break; + default: + pr_err("%s: bug! bad holdoff mode %i\n", __func__, mode); + break; + } + priv->holdoff_mode = mode; +} +EXPORT_SYMBOL_GPL(tms9914_set_holdoff_mode); + +void tms9914_release_holdoff(struct tms9914_priv *priv) +{ + if (priv->holdoff_active) { + write_byte(priv, AUX_RHDF, AUXCR); + priv->holdoff_active = 0; + } +} +EXPORT_SYMBOL_GPL(tms9914_release_holdoff); + +int tms9914_enable_eos(gpib_board_t *board, struct tms9914_priv *priv, uint8_t eos_byte, + int compare_8_bits) +{ + priv->eos = eos_byte; + priv->eos_flags = REOS; + if (compare_8_bits) + priv->eos_flags |= BIN; + return 0; +} +EXPORT_SYMBOL(tms9914_enable_eos); + +void tms9914_disable_eos(gpib_board_t *board, struct tms9914_priv *priv) +{ + priv->eos_flags &= ~REOS; +} +EXPORT_SYMBOL(tms9914_disable_eos); + +int tms9914_parallel_poll(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *result) +{ + // execute parallel poll + write_byte(priv, AUX_CS | AUX_RPP, AUXCR); + udelay(2); + *result = read_byte(priv, CPTR); + // clear parallel poll state + write_byte(priv, AUX_RPP, AUXCR); + return 0; +} +EXPORT_SYMBOL(tms9914_parallel_poll); + +static void set_ppoll_reg(struct tms9914_priv *priv, int enable, + unsigned int dio_line, int sense, int ist) +{ + u8 dio_byte; + + if (enable && ((sense && ist) || (!sense && !ist))) { + dio_byte = 1 << (dio_line - 1); + write_byte(priv, dio_byte, PPR); + } else { + write_byte(priv, 0, PPR); + } +} + +void tms9914_parallel_poll_configure(gpib_board_t *board, + struct tms9914_priv *priv, uint8_t config) +{ + priv->ppoll_enable = (config & PPC_DISABLE) == 0; + priv->ppoll_line = (config & PPC_DIO_MASK) + 1; + priv->ppoll_sense = (config & PPC_SENSE) != 0; + set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, board->ist); +} +EXPORT_SYMBOL(tms9914_parallel_poll_configure); + +void tms9914_parallel_poll_response(gpib_board_t *board, + struct tms9914_priv *priv, int ist) +{ + set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, ist); +} +EXPORT_SYMBOL(tms9914_parallel_poll_response); + +void tms9914_serial_poll_response(gpib_board_t *board, struct tms9914_priv *priv, uint8_t status) +{ + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + write_byte(priv, status, SPMR); + priv->spoll_status = status; + if (status & request_service_bit) + write_byte(priv, AUX_RSV2 | AUX_CS, AUXCR); + else + write_byte(priv, AUX_RSV2, AUXCR); + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(tms9914_serial_poll_response); + +uint8_t tms9914_serial_poll_status(gpib_board_t *board, struct tms9914_priv *priv) +{ + u8 status; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + status = priv->spoll_status; + spin_unlock_irqrestore(&board->spinlock, flags); + + return status; +} +EXPORT_SYMBOL(tms9914_serial_poll_status); + +int tms9914_primary_address(gpib_board_t *board, struct tms9914_priv *priv, unsigned int address) +{ + // put primary address in address0 + write_byte(priv, address & ADDRESS_MASK, ADR); + return 0; +} +EXPORT_SYMBOL(tms9914_primary_address); + +int tms9914_secondary_address(gpib_board_t *board, struct tms9914_priv *priv, + unsigned int address, int enable) +{ + if (enable) + priv->imr1_bits |= HR_APTIE; + else + priv->imr1_bits &= ~HR_APTIE; + + write_byte(priv, priv->imr1_bits, IMR1); + return 0; +} +EXPORT_SYMBOL(tms9914_secondary_address); + +unsigned int tms9914_update_status(gpib_board_t *board, struct tms9914_priv *priv, + unsigned int clear_mask) +{ + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = update_status_nolock(board, priv); + board->status &= ~clear_mask; + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} +EXPORT_SYMBOL(tms9914_update_status); + +static void update_talker_state(struct tms9914_priv *priv, unsigned int address_status_bits) +{ + if (address_status_bits & HR_TA) { + if (address_status_bits & HR_ATN) + priv->talker_state = talker_addressed; + else + /* this could also be serial_poll_active, but the tms9914 provides no + * way to distinguish, so we'll assume talker_active + */ + priv->talker_state = talker_active; + } else { + priv->talker_state = talker_idle; + } +} + +static void update_listener_state(struct tms9914_priv *priv, unsigned int address_status_bits) +{ + if (address_status_bits & HR_LA) { + if (address_status_bits & HR_ATN) + priv->listener_state = listener_addressed; + else + priv->listener_state = listener_active; + } else { + priv->listener_state = listener_idle; + } +} + +static unsigned int update_status_nolock(gpib_board_t *board, struct tms9914_priv *priv) +{ + int address_status; + int bsr_bits; + + address_status = read_byte(priv, ADSR); + + // check for remote/local + if (address_status & HR_REM) + set_bit(REM_NUM, &board->status); + else + clear_bit(REM_NUM, &board->status); + // check for lockout + if (address_status & HR_LLO) + set_bit(LOK_NUM, &board->status); + else + clear_bit(LOK_NUM, &board->status); + // check for ATN + if (address_status & HR_ATN) + set_bit(ATN_NUM, &board->status); + else + clear_bit(ATN_NUM, &board->status); + // check for talker/listener addressed + update_talker_state(priv, address_status); + if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + + update_listener_state(priv, address_status); + if (priv->listener_state == listener_active || priv->listener_state == listener_addressed) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + // Check for SRQI - not reset elsewhere except in autospoll + if (board->status & SRQI) { + bsr_bits = read_byte(priv, BSR); + if (!(bsr_bits & BSR_SRQ_BIT)) + clear_bit(SRQI_NUM, &board->status); + } + + dev_dbg(board->gpib_dev, "status 0x%lx, state 0x%lx\n", board->status, priv->state); + + return board->status; +} + +int tms9914_line_status(const gpib_board_t *board, struct tms9914_priv *priv) +{ + int bsr_bits; + int status = ValidALL; + + bsr_bits = read_byte(priv, BSR); + + if (bsr_bits & BSR_REN_BIT) + status |= BusREN; + if (bsr_bits & BSR_IFC_BIT) + status |= BusIFC; + if (bsr_bits & BSR_SRQ_BIT) + status |= BusSRQ; + if (bsr_bits & BSR_EOI_BIT) + status |= BusEOI; + if (bsr_bits & BSR_NRFD_BIT) + status |= BusNRFD; + if (bsr_bits & BSR_NDAC_BIT) + status |= BusNDAC; + if (bsr_bits & BSR_DAV_BIT) + status |= BusDAV; + if (bsr_bits & BSR_ATN_BIT) + status |= BusATN; + + return status; +} +EXPORT_SYMBOL(tms9914_line_status); + +static int check_for_eos(struct tms9914_priv *priv, uint8_t byte) +{ + static const u8 seven_bit_compare_mask = 0x7f; + + if ((priv->eos_flags & REOS) == 0) + return 0; + + if (priv->eos_flags & BIN) { + if (priv->eos == byte) + return 1; + } else { + if ((priv->eos & seven_bit_compare_mask) == (byte & seven_bit_compare_mask)) + return 1; + } + return 0; +} + +static int wait_for_read_byte(gpib_board_t *board, struct tms9914_priv *priv) +{ + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_debug("gpib: pio read wait interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + + if (test_bit(DEV_CLEAR_BN, &priv->state)) + return -EINTR; + return 0; +} + +static inline uint8_t tms9914_read_data_in(gpib_board_t *board, struct tms9914_priv *priv, int *end) +{ + unsigned long flags; + u8 data; + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(READ_READY_BN, &priv->state); + data = read_byte(priv, DIR); + if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) + *end = 1; + else + *end = 0; + switch (priv->holdoff_mode) { + case TMS9914_HOLDOFF_EOI: + if (*end) + priv->holdoff_active = 1; + break; + case TMS9914_HOLDOFF_ALL: + priv->holdoff_active = 1; + break; + case TMS9914_HOLDOFF_NONE: + break; + default: + pr_err("%s: bug! bad holdoff mode %i\n", __func__, priv->holdoff_mode); + break; + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return data; +} + +static int pio_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + + *bytes_read = 0; + *end = 0; + while (*bytes_read < length && *end == 0) { + tms9914_release_holdoff(priv); + retval = wait_for_read_byte(board, priv); + if (retval < 0) + return retval; + buffer[(*bytes_read)++] = tms9914_read_data_in(board, priv, end); + + if (check_for_eos(priv, buffer[*bytes_read - 1])) + *end = 1; + } + + return retval; +} + +int tms9914_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + size_t num_bytes; + + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &priv->state); + + // transfer data (except for last byte) + if (length > 1) { + if (priv->eos_flags & REOS) + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); + else + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_EOI); + // PIO transfer + retval = pio_read(board, priv, buffer, length - 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + buffer += num_bytes; + length -= num_bytes; + } + // read last bytes if we havn't received an END yet + if (*end == 0) { + // make sure we holdoff after last byte read + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); + retval = pio_read(board, priv, buffer, length, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} +EXPORT_SYMBOL(tms9914_read); + +static int pio_write_wait(gpib_board_t *board, struct tms9914_priv *priv) +{ + // wait until next byte is ready to be sent + if (wait_event_interruptible(board->wait, + test_bit(WRITE_READY_BN, &priv->state) || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted!\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(BUS_ERROR_BN, &priv->state)) + return -EIO; + if (test_bit(DEV_CLEAR_BN, &priv->state)) + return -EINTR; + + return 0; +} + +static int pio_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, size_t *bytes_written) +{ + ssize_t retval = 0; + unsigned long flags; + + *bytes_written = 0; + while (*bytes_written < length) { + retval = pio_write_wait(board, priv); + if (retval < 0) + break; + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(WRITE_READY_BN, &priv->state); + write_byte(priv, buffer[(*bytes_written)++], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + } + retval = pio_write_wait(board, priv); + if (retval < 0) + return retval; + + return length; +} + +int tms9914_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + ssize_t retval = 0; + + *bytes_written = 0; + if (length == 0) + return 0; + + clear_bit(BUS_ERROR_BN, &priv->state); + clear_bit(DEV_CLEAR_BN, &priv->state); + + if (send_eoi) + length-- ; /* save the last byte for sending EOI */ + + if (length > 0) { + size_t num_bytes; + // PIO transfer + retval = pio_write(board, priv, buffer, length, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + if (send_eoi) { + size_t num_bytes; + /*send EOI */ + write_byte(priv, AUX_SEOI, AUXCR); + + retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); + *bytes_written += num_bytes; + } + return retval; +} +EXPORT_SYMBOL(tms9914_write); + +static void check_my_address_state(gpib_board_t *board, struct tms9914_priv *priv, int cmd_byte) +{ + if (cmd_byte == MLA(board->pad)) { + priv->primary_listen_addressed = 1; + // become active listener + if (board->sad < 0) + write_byte(priv, AUX_LON | AUX_CS, AUXCR); + } else if (board->sad >= 0 && priv->primary_listen_addressed && + cmd_byte == MSA(board->sad)) { + // become active listener + write_byte(priv, AUX_LON | AUX_CS, AUXCR); + } else if (cmd_byte != MLA(board->pad) && (cmd_byte & 0xe0) == LAD) { + priv->primary_listen_addressed = 0; + } else if (cmd_byte == UNL) { + priv->primary_listen_addressed = 0; + write_byte(priv, AUX_LON, AUXCR); + } else if (cmd_byte == MTA(board->pad)) { + priv->primary_talk_addressed = 1; + if (board->sad < 0) + //make active talker + write_byte(priv, AUX_TON | AUX_CS, AUXCR); + } else if (board->sad >= 0 && priv->primary_talk_addressed && + cmd_byte == MSA(board->sad)) { + // become active talker + write_byte(priv, AUX_TON | AUX_CS, AUXCR); + } else if (cmd_byte != MTA(board->pad) && (cmd_byte & 0xe0) == TAD) { + // Other Talk Address + priv->primary_talk_addressed = 0; + write_byte(priv, AUX_TON, AUXCR); + } else if (cmd_byte == UNT) { + priv->primary_talk_addressed = 0; + write_byte(priv, AUX_TON, AUXCR); + } +} + +int tms9914_command(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, + size_t length, size_t *bytes_written) +{ + int retval = 0; + unsigned long flags; + + *bytes_written = 0; + while (*bytes_written < length) { + if (wait_event_interruptible(board->wait, + test_bit(COMMAND_READY_BN, + &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_debug("gpib command wait interrupted\n"); + break; + } + if (test_bit(TIMO_NUM, &board->status)) + break; + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(COMMAND_READY_BN, &priv->state); + write_byte(priv, buffer[*bytes_written], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + + check_my_address_state(board, priv, buffer[*bytes_written]); + + ++(*bytes_written); + } + // wait until last command byte is written + if (wait_event_interruptible(board->wait, + test_bit(COMMAND_READY_BN, + &priv->state) || test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + + return retval; +} +EXPORT_SYMBOL(tms9914_command); + +irqreturn_t tms9914_interrupt(gpib_board_t *board, struct tms9914_priv *priv) +{ + int status0, status1; + + // read interrupt status (also clears status) + status0 = read_byte(priv, ISR0); + status1 = read_byte(priv, ISR1); + return tms9914_interrupt_have_status(board, priv, status0, status1); +} +EXPORT_SYMBOL(tms9914_interrupt); + +irqreturn_t tms9914_interrupt_have_status(gpib_board_t *board, struct tms9914_priv *priv, + int status0, int status1) +{ + // record reception of END + if (status0 & HR_END) + set_bit(RECEIVED_END_BN, &priv->state); + // get incoming data in PIO mode + if ((status0 & HR_BI)) + set_bit(READ_READY_BN, &priv->state); + if ((status0 & HR_BO)) { + if (read_byte(priv, ADSR) & HR_ATN) + set_bit(COMMAND_READY_BN, &priv->state); + else + set_bit(WRITE_READY_BN, &priv->state); + } + + if (status0 & HR_SPAS) { + priv->spoll_status &= ~request_service_bit; + write_byte(priv, priv->spoll_status, SPMR); + //FIXME: set SPOLL status bit + } + // record service request in status + if (status1 & HR_SRQ) + set_bit(SRQI_NUM, &board->status); + // have been addressed (with secondary addressing disabled) + if (status1 & HR_MA) + // clear dac holdoff + write_byte(priv, AUX_VAL, AUXCR); + // unrecognized command received + if (status1 & HR_UNC) { + unsigned short command_byte = read_byte(priv, CPTR) & gpib_command_mask; + + switch (command_byte) { + case PPConfig: + priv->ppoll_configure_state = 1; + /* AUX_PTS generates another UNC interrupt on the next command byte + * if it is in the secondary address group (such as PPE and PPD). + */ + write_byte(priv, AUX_PTS, AUXCR); + write_byte(priv, AUX_VAL, AUXCR); + break; + case PPU: + tms9914_parallel_poll_configure(board, priv, command_byte); + write_byte(priv, AUX_VAL, AUXCR); + break; + default: + if (is_PPE(command_byte) || is_PPD(command_byte)) { + if (priv->ppoll_configure_state) { + tms9914_parallel_poll_configure(board, priv, command_byte); + write_byte(priv, AUX_VAL, AUXCR); + } else {// bad parallel poll configure byte + // clear dac holdoff + write_byte(priv, AUX_INVAL, AUXCR); + } + } else { + // printk("tms9914: unrecognized gpib command pass thru 0x%x\n", + // command_byte); + // clear dac holdoff + write_byte(priv, AUX_INVAL, AUXCR); + } + break; + } + + if (in_primary_command_group(command_byte) && command_byte != PPConfig) + priv->ppoll_configure_state = 0; + } + + if (status1 & HR_ERR) { + dev_dbg(board->gpib_dev, "gpib bus error\n"); + set_bit(BUS_ERROR_BN, &priv->state); + } + + if (status1 & HR_IFC) { + push_gpib_event(board, EventIFC); + clear_bit(CIC_NUM, &board->status); + } + + if (status1 & HR_GET) { + push_gpib_event(board, EventDevTrg); + // clear dac holdoff + write_byte(priv, AUX_VAL, AUXCR); + } + + if (status1 & HR_DCAS) { + push_gpib_event(board, EventDevClr); + // clear dac holdoff + write_byte(priv, AUX_VAL, AUXCR); + set_bit(DEV_CLEAR_BN, &priv->state); + } + + // check for being addressed with secondary addressing + if (status1 & HR_APT) { + if (board->sad < 0) + pr_err("tms9914: bug, APT interrupt without secondary addressing?\n"); + if ((read_byte(priv, CPTR) & gpib_command_mask) == MSA(board->sad)) + write_byte(priv, AUX_VAL, AUXCR); + else + write_byte(priv, AUX_INVAL, AUXCR); + } + + if ((status0 & priv->imr0_bits) || (status1 & priv->imr1_bits)) { +// dev_dbg(board->gpib_dev, "isr0 0x%x, imr0 0x%x, isr1 0x%x, imr1 0x%x\n", +// status0, priv->imr0_bits, status1, priv->imr1_bits); + update_status_nolock(board, priv); + wake_up_interruptible(&board->wait); + } + return IRQ_HANDLED; +} +EXPORT_SYMBOL(tms9914_interrupt_have_status); + +void tms9914_board_reset(struct tms9914_priv *priv) +{ + /* chip reset */ + write_byte(priv, AUX_CHIP_RESET | AUX_CS, AUXCR); + + /* disable all interrupts */ + priv->imr0_bits = 0; + write_byte(priv, priv->imr0_bits, IMR0); + priv->imr1_bits = 0; + write_byte(priv, priv->imr1_bits, IMR1); + write_byte(priv, AUX_DAI | AUX_CS, AUXCR); + + /* clear registers by reading */ + read_byte(priv, CPTR); + read_byte(priv, ISR0); + read_byte(priv, ISR1); + + write_byte(priv, 0, SPMR); + + /* parallel poll unconfigure */ + write_byte(priv, 0, PPR); + // request for data holdoff + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); +} +EXPORT_SYMBOL_GPL(tms9914_board_reset); + +void tms9914_online(gpib_board_t *board, struct tms9914_priv *priv) +{ + /* set GPIB address */ + tms9914_primary_address(board, priv, board->pad); + tms9914_secondary_address(board, priv, board->sad, board->sad >= 0); + + // enable tms9914 interrupts + priv->imr0_bits |= HR_MACIE | HR_RLCIE | HR_ENDIE | HR_BOIE | HR_BIIE | + HR_SPASIE; + priv->imr1_bits |= HR_MAIE | HR_SRQIE | HR_UNCIE | HR_ERRIE | HR_IFCIE | + HR_GETIE | HR_DCASIE; + write_byte(priv, priv->imr0_bits, IMR0); + write_byte(priv, priv->imr1_bits, IMR1); + write_byte(priv, AUX_DAI, AUXCR); + + // turn off reset state + write_byte(priv, AUX_CHIP_RESET, AUXCR); +} +EXPORT_SYMBOL_GPL(tms9914_online); + +#ifdef CONFIG_HAS_IOPORT +// wrapper for inb +uint8_t tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return inb((unsigned long)(priv->iobase) + register_num * priv->offset); +} +EXPORT_SYMBOL_GPL(tms9914_ioport_read_byte); + +// wrapper for outb +void tms9914_ioport_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num) +{ + outb(data, (unsigned long)(priv->iobase) + register_num * priv->offset); + if (register_num == AUXCR) + udelay(1); +} +EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte); +#endif + +// wrapper for readb +uint8_t tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return readb(priv->iobase + register_num * priv->offset); +} +EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte); + +// wrapper for writeb +void tms9914_iomem_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num) +{ + writeb(data, priv->iobase + register_num * priv->offset); + if (register_num == AUXCR) + udelay(1); +} +EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte); + +static int __init tms9914_init_module(void) +{ + return 0; +} + +static void __exit tms9914_exit_module(void) +{ +} + +module_init(tms9914_init_module); +module_exit(tms9914_exit_module); + diff --git a/drivers/staging/gpib/tnt4882/Makefile b/drivers/staging/gpib/tnt4882/Makefile new file mode 100644 index 0000000000000..f767c990db7a0 --- /dev/null +++ b/drivers/staging/gpib/tnt4882/Makefile @@ -0,0 +1,7 @@ +ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA +obj-m += tnt4882.o + +tnt4882-objs := tnt4882_gpib.o mite.o + + + diff --git a/drivers/staging/gpib/tnt4882/mite.c b/drivers/staging/gpib/tnt4882/mite.c new file mode 100644 index 0000000000000..0edf34d243e9e --- /dev/null +++ b/drivers/staging/gpib/tnt4882/mite.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2 + +/* + * Hardware driver for NI Mite PCI interface chip, + * adapted from COMEDI + * + * Copyright (C) 1997-8 David A. Schleef + * Copyright (C) 2002 Frank Mori Hess + * + * The PCI-MIO E series driver was originally written by + * Tomasz Motylewski <...>, and ported to comedi by ds. + * + * References for specifications: + * + * 321747b.pdf Register Level Programmer Manual (obsolete) + * 321747c.pdf Register Level Programmer Manual (new) + * DAQ-STC reference manual + * + * Other possibly relevant info: + * + * 320517c.pdf User manual (obsolete) + * 320517f.pdf User manual (new) + * 320889a.pdf delete + * 320906c.pdf maximum signal ratings + * 321066a.pdf about 16x + * 321791a.pdf discontinuation of at-mio-16e-10 rev. c + * 321808a.pdf about at-mio-16e-10 rev P + * 321837a.pdf discontinuation of at-mio-16de-10 rev d + * 321838a.pdf about at-mio-16de-10 rev N + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mite.h" + +#define PCI_MITE_SIZE 4096 +#define PCI_DAQ_SIZE 4096 + +struct mite_struct *mite_devices; + +#define TOP_OF_PAGE(x) ((x) | (~(PAGE_MASK))) + +void mite_init(void) +{ + struct pci_dev *pcidev; + struct mite_struct *mite; + + for (pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, NULL); + pcidev; + pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, pcidev)) { + mite = kzalloc(sizeof(*mite), GFP_KERNEL); + if (!mite) + return; + + mite->pcidev = pcidev; + pci_dev_get(mite->pcidev); + mite->next = mite_devices; + mite_devices = mite; + } +} + +int mite_setup(struct mite_struct *mite) +{ + u32 addr; + + if (pci_enable_device(mite->pcidev)) { + pr_err("mite: error enabling mite.\n"); + return -EIO; + } + pci_set_master(mite->pcidev); + if (pci_request_regions(mite->pcidev, "mite")) { + pr_err("mite: failed to request mite io regions.\n"); + return -EIO; + } + addr = pci_resource_start(mite->pcidev, 0); + mite->mite_phys_addr = addr; + mite->mite_io_addr = ioremap(addr, pci_resource_len(mite->pcidev, 0)); + if (!mite->mite_io_addr) { + pr_err("mite: failed to remap mite io memory address.\n"); + return -ENOMEM; + } + pr_info("mite: 0x%08lx mapped to %p\n", mite->mite_phys_addr, mite->mite_io_addr); + addr = pci_resource_start(mite->pcidev, 1); + mite->daq_phys_addr = addr; + mite->daq_io_addr = ioremap(mite->daq_phys_addr, pci_resource_len(mite->pcidev, 1)); + if (!mite->daq_io_addr) { + pr_err("mite: failed to remap daq io memory address.\n"); + return -ENOMEM; + } + pr_info("mite: daq: 0x%08lx mapped to %p\n", mite->daq_phys_addr, mite->daq_io_addr); + writel(mite->daq_phys_addr | WENAB, mite->mite_io_addr + MITE_IODWBSR); + mite->used = 1; + return 0; +} + +void mite_cleanup(void) +{ + struct mite_struct *mite, *next; + + for (mite = mite_devices; mite; mite = next) { + next = mite->next; + if (mite->pcidev) + pci_dev_put(mite->pcidev); + kfree(mite); + } +} + +void mite_unsetup(struct mite_struct *mite) +{ + if (!mite) + return; + if (mite->mite_io_addr) { + iounmap(mite->mite_io_addr); + mite->mite_io_addr = NULL; + } + if (mite->daq_io_addr) { + iounmap(mite->daq_io_addr); + mite->daq_io_addr = NULL; + } + if (mite->mite_phys_addr) { + pci_release_regions(mite->pcidev); + pci_disable_device(mite->pcidev); + mite->mite_phys_addr = 0; + } + mite->used = 0; +} + +void mite_list_devices(void) +{ + struct mite_struct *mite, *next; + + pr_info("Available NI PCI device IDs:"); + if (mite_devices) + for (mite = mite_devices; mite; mite = next) { + next = mite->next; + pr_info(" 0x%04x", mite_device_id(mite)); + if (mite->used) + pr_info("(used)"); + } + pr_info("\n"); +} + +int mite_bytes_transferred(struct mite_struct *mite, int chan) +{ + int dar, fcr; + + dar = readl(mite->mite_io_addr + MITE_DAR + CHAN_OFFSET(chan)); + fcr = readl(mite->mite_io_addr + MITE_FCR + CHAN_OFFSET(chan)) & 0x000000FF; + return dar - fcr; +} + +int mite_dma_tcr(struct mite_struct *mite) +{ + int tcr; + int lkar; + + lkar = readl(mite->mite_io_addr + CHAN_OFFSET(0) + MITE_LKAR); + tcr = readl(mite->mite_io_addr + CHAN_OFFSET(0) + MITE_TCR); + MDPRINTK("lkar=0x%08x tcr=%d\n", lkar, tcr); + + return tcr; +} + +void mite_dma_disarm(struct mite_struct *mite) +{ + int chor; + + /* disarm */ + chor = CHOR_ABORT; + writel(chor, mite->mite_io_addr + CHAN_OFFSET(0) + MITE_CHOR); +} + +void mite_dump_regs(struct mite_struct *mite) +{ + void *addr = 0; + unsigned long temp = 0; + + pr_info("mite address is =0x%p\n", mite->mite_io_addr); + + addr = mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(0); + pr_info("mite status[CHOR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CHOR_strings,temp); + addr = mite->mite_io_addr + MITE_CHCR + CHAN_OFFSET(0); + pr_info("mite status[CHCR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CHCR_strings,temp); + addr = mite->mite_io_addr + MITE_TCR + CHAN_OFFSET(0); + pr_info("mite status[TCR] at 0x%p =0x%08x\n", addr, readl(addr)); + addr = mite->mite_io_addr + MITE_MCR + CHAN_OFFSET(0); + pr_info("mite status[MCR] at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_MCR_strings,temp); + addr = mite->mite_io_addr + MITE_MAR + CHAN_OFFSET(0); + pr_info("mite status[MAR] at 0x%p =0x%08x\n", addr, readl(addr)); + addr = mite->mite_io_addr + MITE_DCR + CHAN_OFFSET(0); + pr_info("mite status[DCR] at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CR_strings,temp); + addr = mite->mite_io_addr + MITE_DAR + CHAN_OFFSET(0); + pr_info("mite status[DAR] at 0x%p =0x%08x\n", addr, readl(addr)); + addr = mite->mite_io_addr + MITE_LKCR + CHAN_OFFSET(0); + pr_info("mite status[LKCR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CR_strings,temp); + addr = mite->mite_io_addr + MITE_LKAR + CHAN_OFFSET(0); + pr_info("mite status[LKAR]at 0x%p =0x%08x\n", addr, readl(addr)); + + addr = mite->mite_io_addr + MITE_CHSR + CHAN_OFFSET(0); + pr_info("mite status[CHSR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CHSR_strings,temp); + addr = mite->mite_io_addr + MITE_FCR + CHAN_OFFSET(0); + pr_info("mite status[FCR] at 0x%p =0x%08x\n\n", addr, readl(addr)); +} + diff --git a/drivers/staging/gpib/tnt4882/mite.h b/drivers/staging/gpib/tnt4882/mite.h new file mode 100644 index 0000000000000..6454d069b8cc4 --- /dev/null +++ b/drivers/staging/gpib/tnt4882/mite.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2 */ + +/* + * Hardware driver for NI Mite PCI interface chip + * + * Copyright (C) 1999 David A. Schleef + */ + +#ifndef _MITE_H_ +#define _MITE_H_ + +#include + +#define PCI_VENDOR_ID_NATINST 0x1093 + +//#define DEBUG_MITE + +#ifdef DEBUG_MITE +#define MDPRINTK(format, args...) pr_debug(format, ## args) +#else +#define MDPRINTK(args...) +#endif + +#define MITE_RING_SIZE 3000 +struct mite_dma_chain { + u32 count; + u32 addr; + u32 next; +}; + +struct mite_struct { + struct mite_struct *next; + int used; + + struct pci_dev *pcidev; + unsigned long mite_phys_addr; + void *mite_io_addr; + unsigned long daq_phys_addr; + void *daq_io_addr; + + int DMA_CheckNearEnd; + + struct mite_dma_chain ring[MITE_RING_SIZE]; +}; + +extern struct mite_struct *mite_devices; + +extern inline unsigned int mite_irq(struct mite_struct *mite) +{ + return mite->pcidev->irq; +}; + +extern inline unsigned int mite_device_id(struct mite_struct *mite) +{ + return mite->pcidev->device; +}; + +void mite_init(void); +void mite_cleanup(void); +int mite_setup(struct mite_struct *mite); +void mite_unsetup(struct mite_struct *mite); +void mite_list_devices(void); + +int mite_dma_tcr(struct mite_struct *mite); + +void mite_dma_arm(struct mite_struct *mite); +void mite_dma_disarm(struct mite_struct *mite); + +void mite_dump_regs(struct mite_struct *mite); +void mite_setregs(struct mite_struct *mite, unsigned long ll_start, int chan, int dir); +int mite_bytes_transferred(struct mite_struct *mite, int chan); + +#define CHAN_OFFSET(x) (0x100 * (x)) + +/* DMA base for chan 0 is 0x500, chan 1 is 0x600 */ + +#define MITE_CHOR 0x500 +#define CHOR_DMARESET BIT(31) +#define CHOR_SET_SEND_TC BIT(11) +#define CHOR_CLR_SEND_TC BIT(10) +#define CHOR_SET_LPAUSE BIT(9) +#define CHOR_CLR_LPAUSE BIT(8) +#define CHOR_CLRDONE BIT(7) +#define CHOR_CLRRB BIT(6) +#define CHOR_CLRLC BIT(5) +#define CHOR_FRESET BIT(4) +#define CHOR_ABORT BIT(3) +#define CHOR_STOP BIT(2) +#define CHOR_CONT BIT(1) +#define CHOR_START BIT(0) +#define CHOR_PON (CHOR_CLR_SEND_TC | CHOR_CLR_LPAUSE) + +#define MITE_CHCR 0x504 +#define CHCR_SET_DMA_IE BIT(31) +#define CHCR_CLR_DMA_IE BIT(30) +#define CHCR_SET_LINKP_IE BIT(29) +#define CHCR_CLR_LINKP_IE BIT(28) +#define CHCR_SET_SAR_IE BIT(27) +#define CHCR_CLR_SAR_IE BIT(26) +#define CHCR_SET_DONE_IE BIT(25) +#define CHCR_CLR_DONE_IE BIT(24) +#define CHCR_SET_MRDY_IE BIT(23) +#define CHCR_CLR_MRDY_IE BIT(22) +#define CHCR_SET_DRDY_IE BIT(21) +#define CHCR_CLR_DRDY_IE BIT(20) +#define CHCR_SET_LC_IE BIT(19) +#define CHCR_CLR_LC_IE BIT(18) +#define CHCR_SET_CONT_RB_IE BIT(17) +#define CHCR_CLR_CONT_RB_IE BIT(16) +#define CHCR_FIFODIS BIT(15) +#define CHCR_FIFO_ON 0 +#define CHCR_BURSTEN BIT(14) +#define CHCR_NO_BURSTEN 0 +#define CHCR_NFTP(x) ((x) << 11) +#define CHCR_NFTP0 CHCR_NFTP(0) +#define CHCR_NFTP1 CHCR_NFTP(1) +#define CHCR_NFTP2 CHCR_NFTP(2) +#define CHCR_NFTP4 CHCR_NFTP(3) +#define CHCR_NFTP8 CHCR_NFTP(4) +#define CHCR_NFTP16 CHCR_NFTP(5) +#define CHCR_NETP(x) ((x) << 11) +#define CHCR_NETP0 CHCR_NETP(0) +#define CHCR_NETP1 CHCR_NETP(1) +#define CHCR_NETP2 CHCR_NETP(2) +#define CHCR_NETP4 CHCR_NETP(3) +#define CHCR_NETP8 CHCR_NETP(4) +#define CHCR_CHEND1 BIT(5) +#define CHCR_CHEND0 BIT(4) +#define CHCR_DIR BIT(3) +#define CHCR_DEV_TO_MEM CHCR_DIR +#define CHCR_MEM_TO_DEV 0 +#define CHCR_NORMAL ((0) << 0) +#define CHCR_CONTINUE ((1) << 0) +#define CHCR_RINGBUFF ((2) << 0) +#define CHCR_LINKSHORT ((4) << 0) +#define CHCR_LINKLONG ((5) << 0) +#define CHCRPON (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE | \ + CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE | \ + CHCR_CLR_LC_IE | CHCR_CLR_CONT_IE) + +#define MITE_TCR 0x508 + +/* CR bits */ +#define CR_RL(x) ((x) << 21) +#define CR_RL0 CR_RL(0) +#define CR_RL1 CR_RL(1) +#define CR_RL2 CR_RL(2) +#define CR_RL4 CR_RL(3) +#define CR_RL8 CR_RL(4) +#define CR_RL16 CR_RL(5) +#define CR_RL32 CR_RL(6) +#define CR_RL64 CR_RL(7) +#define CR_RD(x) ((x) << 19) +#define CR_RD0 CR_RD(0) +#define CR_RD32 CR_RD(1) +#define CR_RD512 CR_RD(2) +#define CR_RD8192 CR_RD(3) +#define CR_REQS(x) ((x) << 16) +#define CR_REQSDRQ0 CR_REQS(4) +#define CR_REQSDRQ1 CR_REQS(5) +#define CR_REQSDRQ2 CR_REQS(6) +#define CR_REQSDRQ3 CR_REQS(7) +#define CR_ASEQX(x) ((x) << 10) +#define CR_ASEQX0 CR_ASEQX(0) +#define CR_ASEQDONT CR_ASEQX0 +#define CR_ASEQXP1 CR_ASEQX(1) +#define CR_ASEQUP CR_ASEQXP1 +#define CR_ASEQXP2 CR_ASEQX(2) +#define CR_ASEQDOWN CR_ASEQXP2 +#define CR_ASEQXP4 CR_ASEQX(3) +#define CR_ASEQXP8 CR_ASEQX(4) +#define CR_ASEQXP16 CR_ASEQX(5) +#define CR_ASEQXP32 CR_ASEQX(6) +#define CR_ASEQXP64 CR_ASEQX(7) +#define CR_ASEQXM1 CR_ASEQX(9) +#define CR_ASEQXM2 CR_ASEQX(10) +#define CR_ASEQXM4 CR_ASEQX(11) +#define CR_ASEQXM8 CR_ASEQX(12) +#define CR_ASEQXM16 CR_ASEQX(13) +#define CR_ASEQXM32 CR_ASEQX(14) +#define CR_ASEQXM64 CR_ASEQX(15) +#define CR_PSIZEBYTE BIT(8) +#define CR_PSIZEHALF (2 << 8) +#define CR_PSIZEWORD (3 << 8) +#define CR_PORTCPU (0 << 6) +#define CR_PORTIO BIT(6) +#define CR_PORTVXI (2 << 6) +#define CR_PORTMXI (3 << 6) +#define CR_AMDEVICE BIT(0) + +#define CHSR_INT 0x80000000 +#define CHSR_DONE 0x02000000 +#define CHSR_LINKC 0x00080000 + +#define MITE_MCR 0x50c +#define MCRPON 0 + +#define MITE_MAR 0x510 + +#define MITE_DCR 0x514 +#define DCR_NORMAL BIT(29) +#define DCRPON 0 + +#define MITE_DAR 0x518 + +#define MITE_LKCR 0x51c + +#define MITE_LKAR 0x520 +#define MITE_LLKAR 0x524 +#define MITE_BAR 0x528 +#define MITE_BCR 0x52c +#define MITE_SAR 0x530 +#define MITE_WSCR 0x534 +#define MITE_WSER 0x538 +#define MITE_CHSR 0x53c +#define MITE_FCR 0x540 + +#define MITE_FIFO 0x80 +#define MITE_FIFOEND 0xff + +#define MITE_AMRAM 0x00 +#define MITE_AMDEVICE 0x01 +#define MITE_AMHOST_A32_SINGLE 0x09 +#define MITE_AMHOST_A24_SINGLE 0x39 +#define MITE_AMHOST_A16_SINGLE 0x29 +#define MITE_AMHOST_A32_BLOCK 0x0b +#define MITE_AMHOST_A32D64_BLOCK 0x08 +#define MITE_AMHOST_A24_BLOCK 0x3b + +enum mite_registers { + MITE_IODWBSR = 0xc0, //IO Device Window Base Size Register + MITE_CSIGR = 0x460, //chip signature + MITE_IODWBSR_1 = 0xc4, // IO Device Window Base Size Register 1 (used by 6602 boards) + MITE_IODWCR_1 = 0xf4 +}; + +enum MITE_IODWBSR_bits { + WENAB = 0x80, // window enable + WENAB_6602 = 0x8c // window enable for 6602 boards +}; + +#endif + diff --git a/drivers/staging/gpib/tnt4882/tnt4882_gpib.c b/drivers/staging/gpib/tnt4882/tnt4882_gpib.c new file mode 100644 index 0000000000000..e49a952fa0d83 --- /dev/null +++ b/drivers/staging/gpib/tnt4882/tnt4882_gpib.c @@ -0,0 +1,1874 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * National Instruments boards using tnt4882 or compatible chips (at-gpib, etc). + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nec7210.h" +#include "gpibP.h" +#include "mite.h" +#include "tnt4882_registers.h" + +static const int ISAPNP_VENDOR_ID_NI = ISAPNP_VENDOR('N', 'I', 'C'); +static const int ISAPNP_ID_NI_ATGPIB_TNT = 0xc601; +enum { + PCI_DEVICE_ID_NI_GPIB = 0xc801, + PCI_DEVICE_ID_NI_GPIB_PLUS = 0xc811, + PCI_DEVICE_ID_NI_GPIB_PLUS2 = 0x71ad, + PCI_DEVICE_ID_NI_PXIGPIB = 0xc821, + PCI_DEVICE_ID_NI_PMCGPIB = 0xc831, + PCI_DEVICE_ID_NI_PCIEGPIB = 0x70cf, + PCI_DEVICE_ID_NI_PCIE2GPIB = 0x710e, +// Measurement Computing PCI-488 same design as PCI-GPIB with TNT5004 + PCI_DEVICE_ID_MC_PCI488 = 0x7259, + PCI_DEVICE_ID_CEC_NI_GPIB = 0x7258 +}; + +// struct which defines private_data for tnt4882 devices +struct tnt4882_priv { + struct nec7210_priv nec7210_priv; + struct mite_struct *mite; + struct pnp_dev *pnp_dev; + unsigned int irq; + unsigned short imr0_bits; + unsigned short imr3_bits; + unsigned short auxg_bits; // bits written to auxiliary register G + void (*io_writeb)(unsigned int value, void *address); + void (*io_writew)(unsigned int value, void *address); + unsigned int (*io_readb)(void *address); + unsigned int (*io_readw)(void *address); +}; + +// interface functions +static int tnt4882_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +static int tnt4882_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +static int tnt4882_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +static int tnt4882_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +static int tnt4882_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written); +static int tnt4882_command_unaccel(gpib_board_t *board, uint8_t *buffer, + size_t length, size_t *bytes_written); +static int tnt4882_take_control(gpib_board_t *board, int synchronous); +static int tnt4882_go_to_standby(gpib_board_t *board); +static void tnt4882_request_system_control(gpib_board_t *board, int request_control); +static void tnt4882_interface_clear(gpib_board_t *board, int assert); +static void tnt4882_remote_enable(gpib_board_t *board, int enable); +static int tnt4882_enable_eos(gpib_board_t *board, uint8_t eos_byte, int + compare_8_bits); +static void tnt4882_disable_eos(gpib_board_t *board); +static unsigned int tnt4882_update_status(gpib_board_t *board, unsigned int clear_mask); +static int tnt4882_primary_address(gpib_board_t *board, unsigned int address); +static int tnt4882_secondary_address(gpib_board_t *board, unsigned int address, + int enable); +static int tnt4882_parallel_poll(gpib_board_t *board, uint8_t *result); +static void tnt4882_parallel_poll_configure(gpib_board_t *board, uint8_t config); +static void tnt4882_parallel_poll_response(gpib_board_t *board, int ist); +static void tnt4882_serial_poll_response(gpib_board_t *board, uint8_t status); +static uint8_t tnt4882_serial_poll_status(gpib_board_t *board); +static int tnt4882_line_status(const gpib_board_t *board); +static unsigned int tnt4882_t1_delay(gpib_board_t *board, unsigned int nano_sec); +static void tnt4882_return_to_local(gpib_board_t *board); + +// interrupt service routines +static irqreturn_t tnt4882_internal_interrupt(gpib_board_t *board); +static irqreturn_t tnt4882_interrupt(int irq, void *arg); + +// utility functions +static int tnt4882_allocate_private(gpib_board_t *board); +static void tnt4882_free_private(gpib_board_t *board); +static void tnt4882_init(struct tnt4882_priv *tnt_priv, const gpib_board_t *board); +static void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, gpib_board_t *board); + +// register offset for nec7210 compatible registers +static const int atgpib_reg_offset = 2; + +// number of ioports used +static const int atgpib_iosize = 32; + +/* paged io */ +static inline unsigned int tnt_paged_readb(struct tnt4882_priv *priv, unsigned long offset) +{ + priv->io_writeb(AUX_PAGEIN, priv->nec7210_priv.iobase + AUXMR * priv->nec7210_priv.offset); + udelay(1); + return priv->io_readb(priv->nec7210_priv.iobase + offset); +} + +static inline void tnt_paged_writeb(struct tnt4882_priv *priv, unsigned int value, + unsigned long offset) +{ + priv->io_writeb(AUX_PAGEIN, priv->nec7210_priv.iobase + AUXMR * priv->nec7210_priv.offset); + udelay(1); + priv->io_writeb(value, priv->nec7210_priv.iobase + offset); +} + +/* readb/writeb wrappers */ +static inline unsigned short tnt_readb(struct tnt4882_priv *priv, unsigned long offset) +{ + void *address = priv->nec7210_priv.iobase + offset; + unsigned long flags; + unsigned short retval; + spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock; + + spin_lock_irqsave(register_lock, flags); + switch (offset) { + case CSR: + case SASR: + case ISR0: + case BSR: + switch (priv->nec7210_priv.type) { + case TNT4882: + case TNT5004: + retval = priv->io_readb(address); + break; + case NAT4882: + retval = tnt_paged_readb(priv, offset - tnt_pagein_offset); + break; + case NEC7210: + retval = 0; + break; + default: + pr_err("tnt4882: bug! unsupported ni_chipset\n"); + retval = 0; + break; + } + break; + default: + retval = priv->io_readb(address); + break; + } + spin_unlock_irqrestore(register_lock, flags); + return retval; +} + +static inline void tnt_writeb(struct tnt4882_priv *priv, unsigned short value, unsigned long offset) +{ + void *address = priv->nec7210_priv.iobase + offset; + unsigned long flags; + spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock; + + spin_lock_irqsave(register_lock, flags); + switch (offset) { + case KEYREG: + case IMR0: + case BCR: + switch (priv->nec7210_priv.type) { + case TNT4882: + case TNT5004: + priv->io_writeb(value, address); + break; + case NAT4882: + tnt_paged_writeb(priv, value, offset - tnt_pagein_offset); + break; + case NEC7210: + break; + default: + pr_err("tnt4882: bug! unsupported ni_chipset\n"); + break; + } + break; + default: + priv->io_writeb(value, address); + break; + } + spin_unlock_irqrestore(register_lock, flags); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for National Instruments boards using tnt4882 or compatible chips"); + +int tnt4882_line_status(const gpib_board_t *board) +{ + int status = ValidALL; + int bcsr_bits; + struct tnt4882_priv *tnt_priv; + + tnt_priv = board->private_data; + + bcsr_bits = tnt_readb(tnt_priv, BSR); + + if (bcsr_bits & BCSR_REN_BIT) + status |= BusREN; + if (bcsr_bits & BCSR_IFC_BIT) + status |= BusIFC; + if (bcsr_bits & BCSR_SRQ_BIT) + status |= BusSRQ; + if (bcsr_bits & BCSR_EOI_BIT) + status |= BusEOI; + if (bcsr_bits & BCSR_NRFD_BIT) + status |= BusNRFD; + if (bcsr_bits & BCSR_NDAC_BIT) + status |= BusNDAC; + if (bcsr_bits & BCSR_DAV_BIT) + status |= BusDAV; + if (bcsr_bits & BCSR_ATN_BIT) + status |= BusATN; + + return status; +} + +unsigned int tnt4882_t1_delay(gpib_board_t *board, unsigned int nano_sec) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + if (nec_priv->type == NEC7210) + return retval; + + if (nano_sec <= 350) { + tnt_writeb(tnt_priv, MSTD, KEYREG); + retval = 350; + } else { + tnt_writeb(tnt_priv, 0, KEYREG); + } + if (nano_sec > 500 && nano_sec <= 1100) { + write_byte(nec_priv, AUXRI | USTD, AUXMR); + retval = 1100; + } else { + write_byte(nec_priv, AUXRI, AUXMR); + } + return retval; +} + +static int fifo_word_available(struct tnt4882_priv *tnt_priv) +{ + int status2; + int retval; + + status2 = tnt_readb(tnt_priv, STS2); + retval = (status2 & AEFN) && (status2 & BEFN); + + return retval; +} + +static int fifo_byte_available(struct tnt4882_priv *tnt_priv) +{ + int status2; + int retval; + + status2 = tnt_readb(tnt_priv, STS2); + retval = (status2 & AEFN) || (status2 & BEFN); + + return retval; +} + +static int fifo_xfer_done(struct tnt4882_priv *tnt_priv) +{ + int status1; + int retval; + + status1 = tnt_readb(tnt_priv, STS1); + retval = status1 & (S_DONE | S_HALT); + + return retval; +} + +static int drain_fifo_words(struct tnt4882_priv *tnt_priv, uint8_t *buffer, int num_bytes) +{ + int count = 0; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + while (fifo_word_available(tnt_priv) && count + 2 <= num_bytes) { + short word; + + word = tnt_priv->io_readw(nec_priv->iobase + FIFOB); + buffer[count++] = word & 0xff; + buffer[count++] = (word >> 8) & 0xff; + } + return count; +} + +static void tnt4882_release_holdoff(gpib_board_t *board, struct tnt4882_priv *tnt_priv) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned short sasr_bits; + + sasr_bits = tnt_readb(tnt_priv, SASR); + + /*tnt4882 not in one-chip mode won't always release holdoff unless we + * are in the right mode when release handshake command is given + */ + if (sasr_bits & AEHS_BIT) /* holding off due to holdoff on end mode*/ { + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + write_byte(nec_priv, AUX_FH, AUXMR); + } else if (sasr_bits & ANHS1_BIT) { /* held off due to holdoff on all data mode*/ + nec7210_set_handshake_mode(board, nec_priv, HR_HLDA); + write_byte(nec_priv, AUX_FH, AUXMR); + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + } else { /* held off due to holdoff immediately command */ + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + write_byte(nec_priv, AUX_FH, AUXMR); + } +} + +int tnt4882_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, + size_t *bytes_read) +{ + size_t count = 0; + ssize_t retval = 0; + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned int bits; + s32 hw_count; + unsigned long flags; + + *bytes_read = 0; + // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + clear_bit(ADR_CHANGE_BN, &nec_priv->state); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, HR_ENDIE); + if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + else + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + tnt_writeb(tnt_priv, nec_priv->auxa_bits | HR_HLDA, CCR); + bits = TNT_B_16BIT | TNT_IN | TNT_CCEN; + tnt_writeb(tnt_priv, bits, CFG); + tnt_writeb(tnt_priv, RESET_FIFO, CMDR); + udelay(1); + // load 2's complement of count into hardware counters + hw_count = -length; + tnt_writeb(tnt_priv, hw_count & 0xff, CNT0); + tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1); + tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2); + tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3); + + tnt4882_release_holdoff(board, tnt_priv); + + tnt_writeb(tnt_priv, GO, CMDR); + udelay(1); + + spin_lock_irqsave(&board->spinlock, flags); + tnt_priv->imr3_bits |= HR_DONE | HR_NEF; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + while (count + 2 <= length && + test_bit(RECEIVED_END_BN, &nec_priv->state) == 0 && + fifo_xfer_done(tnt_priv) == 0) { + // wait until a word is ready + if (wait_event_interruptible(board->wait, + fifo_word_available(tnt_priv) || + fifo_xfer_done(tnt_priv) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(ADR_CHANGE_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_err("tnt4882: read interrupted\n"); + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + //pr_info("tnt4882: minor %i read timed out\n", board->minor); + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + pr_err("tnt4882: device clear interrupted read\n"); + retval = -EINTR; + break; + } + if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) { + pr_err("tnt4882: address change interrupted read\n"); + retval = -EINTR; + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + count += drain_fifo_words(tnt_priv, &buffer[count], length - count); + tnt_priv->imr3_bits |= HR_NEF; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + if (need_resched()) + schedule(); + } + // wait for last byte + if (count < length) { + spin_lock_irqsave(&board->spinlock, flags); + tnt_priv->imr3_bits |= HR_DONE | HR_NEF; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + if (wait_event_interruptible(board->wait, + fifo_xfer_done(tnt_priv) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(ADR_CHANGE_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + pr_err("tnt4882: read interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + //pr_info("tnt4882: read timed out\n"); + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + pr_err("tnt4882: device clear interrupted read\n"); + retval = -EINTR; + } + if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) { + pr_err("tnt4882: address change interrupted read\n"); + retval = -EINTR; + } + count += drain_fifo_words(tnt_priv, &buffer[count], length - count); + if (fifo_byte_available(tnt_priv) && count < length) + buffer[count++] = tnt_readb(tnt_priv, FIFOB); + } + if (count < length) + tnt_writeb(tnt_priv, STOP, CMDR); + udelay(1); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + /* force handling of any pending interrupts (seems to be needed + * to keep interrupts from getting hosed, plus for syncing + * with RECEIVED_END below) + */ + tnt4882_internal_interrupt(board); + /* RECEIVED_END should be in sync now */ + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + if (retval < 0) { + // force immediate holdoff + write_byte(nec_priv, AUX_HLDI, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + *bytes_read = count; + + return retval; +} + +static int fifo_space_available(struct tnt4882_priv *tnt_priv) +{ + int status2; + int retval; + + status2 = tnt_readb(tnt_priv, STS2); + retval = (status2 & AFFN) && (status2 & BFFN); + + return retval; +} + +static unsigned int tnt_transfer_count(struct tnt4882_priv *tnt_priv) +{ + unsigned int count = 0; + + count |= tnt_readb(tnt_priv, CNT0) & 0xff; + count |= (tnt_readb(tnt_priv, CNT1) << 8) & 0xff00; + count |= (tnt_readb(tnt_priv, CNT2) << 16) & 0xff0000; + count |= (tnt_readb(tnt_priv, CNT3) << 24) & 0xff000000; + // return two's complement + return -count; +}; + +static int write_wait(gpib_board_t *board, struct tnt4882_priv *tnt_priv, + int wait_for_done, int send_commands) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + if (wait_event_interruptible(board->wait, + (!wait_for_done && fifo_space_available(tnt_priv)) || + fifo_xfer_done(tnt_priv) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "gpib write interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) { + pr_info("tnt4882: write timed out\n"); + return -ETIMEDOUT; + } + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) { + pr_err("tnt4882: write bus error\n"); + return (send_commands) ? -ENOTCONN : -ECOMM; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + pr_err("tnt4882: device clear interrupted write\n"); + return -EINTR; + } + return 0; +} + +static int generic_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, int send_commands, size_t *bytes_written) +{ + size_t count = 0; + ssize_t retval = 0; + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned int bits; + s32 hw_count; + unsigned long flags; + + *bytes_written = 0; + // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ERRIE, HR_ERRIE); + + if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + else + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + + tnt_writeb(tnt_priv, RESET_FIFO, CMDR); + udelay(1); + + bits = TNT_B_16BIT; + if (send_eoi) { + bits |= TNT_CCEN; + if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) + tnt_writeb(tnt_priv, AUX_SEOI, CCR); + } + if (send_commands) + bits |= TNT_COMMAND; + tnt_writeb(tnt_priv, bits, CFG); + + // load 2's complement of count into hardware counters + hw_count = -length; + tnt_writeb(tnt_priv, hw_count & 0xff, CNT0); + tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1); + tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2); + tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3); + + tnt_writeb(tnt_priv, GO, CMDR); + udelay(1); + + spin_lock_irqsave(&board->spinlock, flags); + tnt_priv->imr3_bits |= HR_DONE; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + while (count < length) { + // wait until byte is ready to be sent + retval = write_wait(board, tnt_priv, 0, send_commands); + if (retval < 0) + break; + if (fifo_xfer_done(tnt_priv)) + break; + spin_lock_irqsave(&board->spinlock, flags); + while (fifo_space_available(tnt_priv) && count < length) { + u16 word; + + word = buffer[count++] & 0xff; + if (count < length) + word |= (buffer[count++] << 8) & 0xff00; + tnt_priv->io_writew(word, nec_priv->iobase + FIFOB); + } +// avoid unnecessary HR_NFF interrupts +// tnt_priv->imr3_bits |= HR_NFF; +// tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + if (need_resched()) + schedule(); + } + // wait last byte has been sent + if (retval == 0) + retval = write_wait(board, tnt_priv, 1, send_commands); + + tnt_writeb(tnt_priv, STOP, CMDR); + udelay(1); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ERR, 0x0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0x0); + /* force handling of any interrupts that happened + * while they were masked (this appears to be needed) + */ + tnt4882_internal_interrupt(board); + *bytes_written = length - tnt_transfer_count(tnt_priv); + return retval; +} + +int tnt4882_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + return generic_write(board, buffer, length, send_eoi, 0, bytes_written); +} + +int tnt4882_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + return generic_write(board, buffer, length, 0, 1, bytes_written); +} + +irqreturn_t tnt4882_internal_interrupt(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + int isr0_bits, isr3_bits, imr3_bits; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_interrupt(board, &priv->nec7210_priv); + + isr0_bits = tnt_readb(priv, ISR0); + isr3_bits = tnt_readb(priv, ISR3); + imr3_bits = priv->imr3_bits; + + if (isr0_bits & TNT_IFCI_BIT) + push_gpib_event(board, EventIFC); + //XXX don't need this wakeup, one below should do? +// wake_up_interruptible(&board->wait); + + if (isr3_bits & HR_NFF) + priv->imr3_bits &= ~HR_NFF; + if (isr3_bits & HR_NEF) + priv->imr3_bits &= ~HR_NEF; + if (isr3_bits & HR_DONE) + priv->imr3_bits &= ~HR_DONE; + if (isr3_bits & (HR_INTR | HR_TLCI)) { + dev_dbg(board->gpib_dev, "tnt4882: minor %i isr0 0x%x imr0 0x%x isr3 0x%x imr3 0x%x\n", + board->minor, isr0_bits, priv->imr0_bits, isr3_bits, imr3_bits); + tnt_writeb(priv, priv->imr3_bits, IMR3); + wake_up_interruptible(&board->wait); + } + spin_unlock_irqrestore(&board->spinlock, flags); + return IRQ_HANDLED; +} + +irqreturn_t tnt4882_interrupt(int irq, void *arg) +{ + return tnt4882_internal_interrupt(arg); +} + +static int ni_tnt_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ni_nat4882_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ni_nec_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ni_pci_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void ni_isa_detach(gpib_board_t *board); +static void ni_pci_detach(gpib_board_t *board); + +#ifdef GPIB_PCMCIA +static int ni_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config); +static void ni_pcmcia_detach(gpib_board_t *board); +static int init_ni_gpib_cs(void); +static void __exit exit_ni_gpib_cs(void); +#endif + +// wrappers for interface functions +int tnt4882_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read) +{ + struct tnt4882_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int retval; + int dummy; + + retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); + + if (retval < 0) { // force immediate holdoff + write_byte(nec_priv, AUX_HLDI, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + nec7210_read_data_in(board, nec_priv, &dummy); + } + return retval; +} + +int tnt4882_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +int tnt4882_command_unaccel(gpib_board_t *board, uint8_t *buffer, + size_t length, size_t *bytes_written) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +int tnt4882_take_control(gpib_board_t *board, int synchronous) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +int tnt4882_go_to_standby(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +void tnt4882_request_system_control(gpib_board_t *board, int request_control) +{ + struct tnt4882_priv *priv = board->private_data; + + if (request_control) { + tnt_writeb(priv, SETSC, CMDR); + udelay(1); + } + nec7210_request_system_control(board, &priv->nec7210_priv, request_control); + if (!request_control) { + tnt_writeb(priv, CLRSC, CMDR); + udelay(1); + } +} + +void tnt4882_interface_clear(gpib_board_t *board, int assert) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +void tnt4882_remote_enable(gpib_board_t *board, int enable) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +int tnt4882_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +void tnt4882_disable_eos(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +unsigned int tnt4882_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + unsigned long flags; + u8 line_status; + unsigned int retval; + struct tnt4882_priv *priv = board->private_data; + + spin_lock_irqsave(&board->spinlock, flags); + board->status &= ~clear_mask; + retval = nec7210_update_status_nolock(board, &priv->nec7210_priv); + /* set / clear SRQ state since it is not cleared by interrupt */ + line_status = tnt_readb(priv, BSR); + if (line_status & BCSR_SRQ_BIT) + set_bit(SRQI_NUM, &board->status); + else + clear_bit(SRQI_NUM, &board->status); + spin_unlock_irqrestore(&board->spinlock, flags); + return board->status; +} + +int tnt4882_primary_address(gpib_board_t *board, unsigned int address) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +int tnt4882_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +int tnt4882_parallel_poll(gpib_board_t *board, uint8_t *result) + +{ + struct tnt4882_priv *tnt_priv = board->private_data; + + if (tnt_priv->nec7210_priv.type != NEC7210) { + tnt_priv->auxg_bits |= RPP2_BIT; + write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); + udelay(2); //FIXME use parallel poll timeout + *result = read_byte(&tnt_priv->nec7210_priv, CPTR); + tnt_priv->auxg_bits &= ~RPP2_BIT; + write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); + return 0; + } else { + return nec7210_parallel_poll(board, &tnt_priv->nec7210_priv, result); + } +} + +void tnt4882_parallel_poll_configure(gpib_board_t *board, uint8_t config) +{ + struct tnt4882_priv *priv = board->private_data; + + if (priv->nec7210_priv.type == TNT5004) { + /* configure locally */ + write_byte(&priv->nec7210_priv, AUXRI | 0x4, AUXMR); + if (config) + /* set response + clear sense */ + write_byte(&priv->nec7210_priv, PPR | config, AUXMR); + else + /* disable ppoll */ + write_byte(&priv->nec7210_priv, PPR | 0x10, AUXMR); + } else { + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); + } +} + +void tnt4882_parallel_poll_response(gpib_board_t *board, int ist) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +/* this is just used by the old nec7210 isa interfaces, the newer + * boards use tnt4882_serial_poll_response2 + */ +void tnt4882_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static void tnt4882_serial_poll_response2(gpib_board_t *board, uint8_t status, + int new_reason_for_service) +{ + struct tnt4882_priv *priv = board->private_data; + unsigned long flags; + const int MSS = status & request_service_bit; + const int reqt = MSS && new_reason_for_service; + const int reqf = MSS == 0; + + spin_lock_irqsave(&board->spinlock, flags); + if (reqt) { + priv->nec7210_priv.srq_pending = 1; + clear_bit(SPOLL_NUM, &board->status); + } else { + if (reqf) + priv->nec7210_priv.srq_pending = 0; + } + if (reqt) + /* It may seem like a race to issue reqt before updating + * the status byte, but it is not. The chip does not + * issue the reqt until the SPMR is written to at + * a later time. + */ + write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR); + else if (reqf) + write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR); + /* We need to always zero bit 6 of the status byte before writing it to + * the SPMR to insure we are using + * serial poll mode SP1, and not accidentally triggering mode SP3. + */ + write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR); + spin_unlock_irqrestore(&board->spinlock, flags); +} + +uint8_t tnt4882_serial_poll_status(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +void tnt4882_return_to_local(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +gpib_interface_t ni_pci_interface = { +name: "ni_pci", +attach : ni_pci_attach, +detach : ni_pci_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response2 : tnt4882_serial_poll_response2, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_pci_accel_interface = { +name: "ni_pci_accel", +attach : ni_pci_attach, +detach : ni_pci_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response2 : tnt4882_serial_poll_response2, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_isa_interface = { +name: "ni_isa", +attach : ni_tnt_isa_attach, +detach : ni_isa_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response2 : tnt4882_serial_poll_response2, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_nat4882_isa_interface = { +name: "ni_nat4882_isa", +attach : ni_nat4882_isa_attach, +detach : ni_isa_detach, +read : tnt4882_read, +write : tnt4882_write, +command : tnt4882_command_unaccel, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response2 : tnt4882_serial_poll_response2, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_nec_isa_interface = { +name: "ni_nec_isa", +attach : ni_nec_isa_attach, +detach : ni_isa_detach, +read : tnt4882_read, +write : tnt4882_write, +command : tnt4882_command_unaccel, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response : tnt4882_serial_poll_response, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_isa_accel_interface = { +name: "ni_isa_accel", +attach : ni_tnt_isa_attach, +detach : ni_isa_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response2 : tnt4882_serial_poll_response2, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_nat4882_isa_accel_interface = { +name: "ni_nat4882_isa_accel", +attach : ni_nat4882_isa_attach, +detach : ni_isa_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command_unaccel, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response2 : tnt4882_serial_poll_response2, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_nec_isa_accel_interface = { +name: "ni_nec_isa_accel", +attach : ni_nec_isa_attach, +detach : ni_isa_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command_unaccel, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : NULL, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response : tnt4882_serial_poll_response, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +#ifdef GPIB_PCMCIA +gpib_interface_t ni_pcmcia_interface = { +name: "ni_pcmcia", +attach : ni_pcmcia_attach, +detach : ni_pcmcia_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response : tnt4882_serial_poll_response, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; + +gpib_interface_t ni_pcmcia_accel_interface = { +name: "ni_pcmcia_accel", +attach : ni_pcmcia_attach, +detach : ni_pcmcia_detach, +read : tnt4882_accel_read, +write : tnt4882_accel_write, +command : tnt4882_command, +take_control : tnt4882_take_control, +go_to_standby : tnt4882_go_to_standby, +request_system_control : tnt4882_request_system_control, +interface_clear : tnt4882_interface_clear, +remote_enable : tnt4882_remote_enable, +enable_eos : tnt4882_enable_eos, +disable_eos : tnt4882_disable_eos, +parallel_poll : tnt4882_parallel_poll, +parallel_poll_configure : tnt4882_parallel_poll_configure, +parallel_poll_response : tnt4882_parallel_poll_response, +local_parallel_poll_mode : NULL, // XXX +line_status : tnt4882_line_status, +update_status : tnt4882_update_status, +primary_address : tnt4882_primary_address, +secondary_address : tnt4882_secondary_address, +serial_poll_response : tnt4882_serial_poll_response, +serial_poll_status : tnt4882_serial_poll_status, +t1_delay : tnt4882_t1_delay, +return_to_local : tnt4882_return_to_local, +}; +#endif + +void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, gpib_board_t *board) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + tnt_priv->imr0_bits = 0; + tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0); + tnt_priv->imr3_bits = 0; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + tnt_readb(tnt_priv, IMR0); + tnt_readb(tnt_priv, IMR3); + nec7210_board_reset(nec_priv, board); +} + +int tnt4882_allocate_private(gpib_board_t *board) +{ + struct tnt4882_priv *tnt_priv; + + board->private_data = kmalloc(sizeof(struct tnt4882_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + tnt_priv = board->private_data; + memset(tnt_priv, 0, sizeof(struct tnt4882_priv)); + init_nec7210_private(&tnt_priv->nec7210_priv); + return 0; +} + +void tnt4882_free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +void tnt4882_init(struct tnt4882_priv *tnt_priv, const gpib_board_t *board) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + /* Turbo488 software reset */ + tnt_writeb(tnt_priv, SOFT_RESET, CMDR); + udelay(1); + + // turn off one-chip mode + tnt_writeb(tnt_priv, NODMA, HSSEL); + tnt_writeb(tnt_priv, 0, ACCWR); + // make sure we are in 7210 mode + tnt_writeb(tnt_priv, AUX_7210, AUXCR); + udelay(1); + // registers might be swapped, so write it to the swapped address too + tnt_writeb(tnt_priv, AUX_7210, SWAPPED_AUXCR); + udelay(1); + // turn on one-chip mode + if (nec_priv->type == TNT4882 || nec_priv->type == TNT5004) + tnt_writeb(tnt_priv, NODMA | TNT_ONE_CHIP_BIT, HSSEL); + else + tnt_writeb(tnt_priv, NODMA, HSSEL); + + nec7210_board_reset(nec_priv, board); + // read-clear isr0 + tnt_readb(tnt_priv, ISR0); + + // enable passing of nat4882 interrupts + tnt_priv->imr3_bits = HR_TLCI; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + + // enable interrupt + tnt_writeb(tnt_priv, 0x1, INTRT); + + // force immediate holdoff + write_byte(&tnt_priv->nec7210_priv, AUX_HLDI, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + tnt_priv->auxg_bits = AUXRG | NTNL_BIT; + write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); + + nec7210_board_online(nec_priv, board); + // enable interface clear interrupt for event queue + tnt_priv->imr0_bits = TNT_IMR0_ALWAYS_BITS | TNT_ATNI_BIT | TNT_IFCIE_BIT; + tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0); +} + +int ni_pci_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = IRQF_SHARED; + int retval; + struct mite_struct *mite; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + tnt_priv->io_writeb = writeb_wrapper; + tnt_priv->io_readb = readb_wrapper; + tnt_priv->io_writew = writew_wrapper; + tnt_priv->io_readw = readw_wrapper; + nec_priv = &tnt_priv->nec7210_priv; + nec_priv->type = TNT4882; + nec_priv->read_byte = nec7210_locking_iomem_read_byte; + nec_priv->write_byte = nec7210_locking_iomem_write_byte; + nec_priv->offset = atgpib_reg_offset; + + if (!mite_devices) { + pr_err("no National Instruments PCI boards found\n"); + return -1; + } + + for (mite = mite_devices; mite; mite = mite->next) { + short found_board; + + if (mite->used) + continue; + if (config->pci_bus >= 0 && config->pci_bus != mite->pcidev->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(mite->pcidev->devfn)) + continue; + switch (mite_device_id(mite)) { + case PCI_DEVICE_ID_NI_GPIB: + case PCI_DEVICE_ID_NI_GPIB_PLUS: + case PCI_DEVICE_ID_NI_GPIB_PLUS2: + case PCI_DEVICE_ID_NI_PXIGPIB: + case PCI_DEVICE_ID_NI_PMCGPIB: + case PCI_DEVICE_ID_NI_PCIEGPIB: + case PCI_DEVICE_ID_NI_PCIE2GPIB: +// support for Measurement Computing PCI-488 + case PCI_DEVICE_ID_MC_PCI488: + case PCI_DEVICE_ID_CEC_NI_GPIB: + found_board = 1; + break; + default: + found_board = 0; + break; + } + if (found_board) + break; + } + if (!mite) { + pr_err("no NI PCI-GPIB boards found\n"); + return -1; + } + tnt_priv->mite = mite; + retval = mite_setup(tnt_priv->mite); + if (retval < 0) { + pr_err("tnt4882: error setting up mite.\n"); + return retval; + } + + nec_priv->iobase = tnt_priv->mite->daq_io_addr; + + // get irq + if (request_irq(mite_irq(tnt_priv->mite), tnt4882_interrupt, isr_flags, + "ni-pci-gpib", board)) { + pr_err("gpib: can't request IRQ %d\n", mite_irq(tnt_priv->mite)); + return -1; + } + tnt_priv->irq = mite_irq(tnt_priv->mite); + pr_info("tnt4882: irq %i\n", tnt_priv->irq); + + // TNT5004 detection + switch (tnt_readb(tnt_priv, CSR) & 0xf0) { + case 0x30: + nec_priv->type = TNT4882; + pr_info("tnt4882: TNT4882 chipset detected\n"); + break; + case 0x40: + nec_priv->type = TNT5004; + pr_info("tnt4882: TNT5004 chipset detected\n"); + break; + } + tnt4882_init(tnt_priv, board); + + return 0; +} + +void ni_pci_detach(gpib_board_t *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + + if (nec_priv->iobase) + tnt4882_board_reset(tnt_priv, board); + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (tnt_priv->mite) + mite_unsetup(tnt_priv->mite); + } + tnt4882_free_private(board); +} + +static int ni_isapnp_find(struct pnp_dev **dev) +{ + *dev = pnp_find_dev(NULL, ISAPNP_VENDOR_ID_NI, + ISAPNP_FUNCTION(ISAPNP_ID_NI_ATGPIB_TNT), NULL); + if (!*dev || !(*dev)->card) { + pr_err("tnt4882: failed to find isapnp board\n"); + return -ENODEV; + } + if (pnp_device_attach(*dev) < 0) { + pr_err("tnt4882: atgpib/tnt board already active, skipping\n"); + return -EBUSY; + } + if (pnp_activate_dev(*dev) < 0) { + pnp_device_detach(*dev); + pr_err("tnt4882: failed to activate() atgpib/tnt, aborting\n"); + return -EAGAIN; + } + if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { + pnp_device_detach(*dev); + pr_err("tnt4882: invalid port or irq for atgpib/tnt, aborting\n"); + return -ENOMEM; + } + return 0; +} + +static int ni_isa_attach_common(gpib_board_t *board, const gpib_board_config_t *config, + enum nec7210_chipset chipset) +{ + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + void *iobase; + int irq; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + tnt_priv->io_writeb = outb_wrapper; + tnt_priv->io_readb = inb_wrapper; + tnt_priv->io_writew = outw_wrapper; + tnt_priv->io_readw = inw_wrapper; + nec_priv = &tnt_priv->nec7210_priv; + nec_priv->type = chipset; + nec_priv->read_byte = nec7210_locking_ioport_read_byte; + nec_priv->write_byte = nec7210_locking_ioport_write_byte; + nec_priv->offset = atgpib_reg_offset; + + // look for plug-n-play board + if (config->ibbase == 0) { + struct pnp_dev *dev; + int retval; + + retval = ni_isapnp_find(&dev); + if (retval < 0) + return retval; + tnt_priv->pnp_dev = dev; + iobase = (void *)(pnp_port_start(dev, 0)); + irq = pnp_irq(dev, 0); + } else { + iobase = config->ibbase; + irq = config->ibirq; + } + // allocate ioports + if (!request_region((unsigned long)(iobase), atgpib_iosize, "atgpib")) { + pr_err("tnt4882: failed to allocate ioports\n"); + return -1; + } + nec_priv->iobase = iobase; + + // get irq + if (request_irq(irq, tnt4882_interrupt, isr_flags, "atgpib", board)) { + pr_err("gpib: can't request IRQ %d\n", irq); + return -1; + } + tnt_priv->irq = irq; + + tnt4882_init(tnt_priv, board); + + return 0; +} + +int ni_tnt_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return ni_isa_attach_common(board, config, TNT4882); +} + +int ni_nat4882_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return ni_isa_attach_common(board, config, NAT4882); +} + +int ni_nec_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return ni_isa_attach_common(board, config, NEC7210); +} + +void ni_isa_detach(gpib_board_t *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + if (nec_priv->iobase) + tnt4882_board_reset(tnt_priv, board); + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (nec_priv->iobase) + release_region((unsigned long)(nec_priv->iobase), atgpib_iosize); + if (tnt_priv->pnp_dev) + pnp_device_detach(tnt_priv->pnp_dev); + } + tnt4882_free_private(board); +} + +static int tnt4882_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id tnt4882_pci_table[] = { + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS2)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PXIGPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PMCGPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIEGPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIE2GPIB)}, + // support for Measurement Computing PCI-488 + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_MC_PCI488)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_CEC_NI_GPIB)}, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, tnt4882_pci_table); + +static struct pci_driver tnt4882_pci_driver = { + .name = "tnt4882", + .id_table = tnt4882_pci_table, + .probe = &tnt4882_pci_probe +}; + +static const struct pnp_device_id tnt4882_pnp_table[] = { + {.id = "NICC601"}, + {.id = ""} +}; +MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); + +static int __init tnt4882_init_module(void) +{ + int result; + + result = pci_register_driver(&tnt4882_pci_driver); + if (result) { + pr_err("tnt4882: pci_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&ni_isa_interface, THIS_MODULE); + gpib_register_driver(&ni_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&ni_nat4882_isa_interface, THIS_MODULE); + gpib_register_driver(&ni_nat4882_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&ni_nec_isa_interface, THIS_MODULE); + gpib_register_driver(&ni_nec_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&ni_pci_interface, THIS_MODULE); + gpib_register_driver(&ni_pci_accel_interface, THIS_MODULE); +#ifdef GPIB_PCMCIA + gpib_register_driver(&ni_pcmcia_interface, THIS_MODULE); + gpib_register_driver(&ni_pcmcia_accel_interface, THIS_MODULE); + if (init_ni_gpib_cs() < 0) + return -1; +#endif + + mite_init(); + mite_list_devices(); + + return 0; +} + +static void __exit tnt4882_exit_module(void) +{ + gpib_unregister_driver(&ni_isa_interface); + gpib_unregister_driver(&ni_isa_accel_interface); + gpib_unregister_driver(&ni_nat4882_isa_interface); + gpib_unregister_driver(&ni_nat4882_isa_accel_interface); + gpib_unregister_driver(&ni_nec_isa_interface); + gpib_unregister_driver(&ni_nec_isa_accel_interface); + gpib_unregister_driver(&ni_pci_interface); + gpib_unregister_driver(&ni_pci_accel_interface); +#ifdef GPIB_PCMCIA + gpib_unregister_driver(&ni_pcmcia_interface); + gpib_unregister_driver(&ni_pcmcia_accel_interface); + exit_ni_gpib_cs(); +#endif + + mite_cleanup(); + + pci_unregister_driver(&tnt4882_pci_driver); +} + +#ifdef GPIB_PCMCIA + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + * you do not define PCMCIA_DEBUG at all, all the debug code will be + * left out. If you compile with PCMCIA_DEBUG=0, the debug code will + * be present but disabled -- but it can then be enabled for specific + * modules at load time with a 'pc_debug=#' option to insmod. + */ +#define PCMCIA_DEBUG 1 +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +module_param(pc_debug, int, 0); +#define DEBUG(n, args...) \ + do {if (pc_debug > (n)) \ + pr_debug(args); } \ + while (0) +#else +#define DEBUG(args...) +#endif + +static int ni_gpib_config(struct pcmcia_device *link); +static void ni_gpib_release(struct pcmcia_device *link); +static int ni_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config); +static void ni_pcmcia_detach(gpib_board_t *board); + +/* + * A linked list of "instances" of the dummy device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + * + * I think this dev_list is obsolete but the pointer is needed to keep + * the module instance for the ni_pcmcia_attach function. + */ + +static struct pcmcia_device *curr_dev; + +struct local_info_t { + struct pcmcia_device *p_dev; + gpib_board_t *dev; + int stop; + struct bus_operations *bus; +}; + +/* + * ni_gpib_probe() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + */ + +static int ni_gpib_probe(struct pcmcia_device *link) +{ + struct local_info_t *info; + //struct gpib_board_t *dev; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /* Allocate space for private device-specific data */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + /* + * General socket configuration defaults can go here. In this + * client, we assume very little, and rely on the CIS for almost + * everything. In most clients, many details (i.e., number, sizes, + * and attributes of IO windows) are fixed by the nature of the + * device, and can be hard-wired here. + */ + link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + /* Register with Card Services */ + curr_dev = link; + return ni_gpib_config(link); +} + +/* + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ +static void ni_gpib_remove(struct pcmcia_device *link) +{ + struct local_info_t *info = link->priv; + //struct gpib_board_t *dev = info->dev; + + DEBUG(0, "%s(%p)\n", __func__, link); + + if (info->dev) + ni_pcmcia_detach(info->dev); + ni_gpib_release(link); + + //free_netdev(dev); + kfree(info); +} + +static int ni_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) +{ + int retval; + + retval = pcmcia_request_io(link); + if (retval != 0) + return retval; + + return 0; +} + +/* + * ni_gpib_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * device available to the system. + */ +static int ni_gpib_config(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //gpib_board_t *dev = info->dev; + int last_ret; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + last_ret = pcmcia_loop_config(link, &ni_gpib_config_iteration, NULL); + if (last_ret) { + dev_warn(&link->dev, "no configuration found\n"); + ni_gpib_release(link); + return last_ret; + } + + last_ret = pcmcia_enable_device(link); + if (last_ret) { + ni_gpib_release(link); + return last_ret; + } + return 0; +} /* ni_gpib_config */ + +/* + * After a card is removed, ni_gpib_release() will unregister the + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void ni_gpib_release(struct pcmcia_device *link) +{ + DEBUG(0, "%s(0x%p)\n", __func__, link); + pcmcia_disable_device(link); +} /* ni_gpib_release */ + +static int ni_gpib_suspend(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + if (link->open) + pr_err("Device still open ???\n"); + //netif_device_detach(dev); + + return 0; +} + +static int ni_gpib_resume(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * printk("Gpib resumed ???\n"); + * //netif_device_attach(dev); + *} + */ + return ni_gpib_config(link); +} + +static struct pcmcia_device_id ni_pcmcia_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4882), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0c71), // NI PCMCIA-GPIB+ + PCMCIA_DEVICE_NULL +}; + +MODULE_DEVICE_TABLE(pcmcia, ni_pcmcia_ids); + +static struct pcmcia_driver ni_gpib_cs_driver = { + .name = "ni_gpib_cs", + .owner = THIS_MODULE, + .drv = { .name = "ni_gpib_cs", }, + .id_table = ni_pcmcia_ids, + .probe = ni_gpib_probe, + .remove = ni_gpib_remove, + .suspend = ni_gpib_suspend, + .resume = ni_gpib_resume, +}; + +int __init init_ni_gpib_cs(void) +{ + return pcmcia_register_driver(&ni_gpib_cs_driver); +} + +void __exit exit_ni_gpib_cs(void) +{ + DEBUG(0, "ni_gpib_cs: unloading\n"); + pcmcia_unregister_driver(&ni_gpib_cs_driver); +} + +static const int pcmcia_gpib_iosize = 32; + +int ni_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct local_info_t *info; + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = IRQF_SHARED; + + DEBUG(0, "%s(0x%p)\n", __func__, board); + + if (!curr_dev) { + pr_err("gpib: no NI PCMCIA board found\n"); + return -1; + } + + info = curr_dev->priv; + info->dev = board; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + tnt_priv->io_writeb = outb_wrapper; + tnt_priv->io_readb = inb_wrapper; + tnt_priv->io_writew = outw_wrapper; + tnt_priv->io_readw = inw_wrapper; + nec_priv = &tnt_priv->nec7210_priv; + nec_priv->type = TNT4882; + nec_priv->read_byte = nec7210_locking_ioport_read_byte; + nec_priv->write_byte = nec7210_locking_ioport_write_byte; + nec_priv->offset = atgpib_reg_offset; + + DEBUG(0, "ioport1 window attributes: 0x%lx\n", curr_dev->resource[0]->flags); + if (request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), + "tnt4882") == 0) { + pr_err("gpib: ioports starting at 0x%lx are already in use\n", + (unsigned long)curr_dev->resource[0]->start); + return -EIO; + } + + nec_priv->iobase = (void *)(unsigned long)curr_dev->resource[0]->start; + + // get irq + if (request_irq(curr_dev->irq, tnt4882_interrupt, isr_flags, "tnt4882", board)) { + pr_err("gpib: can't request IRQ %d\n", curr_dev->irq); + return -1; + } + tnt_priv->irq = curr_dev->irq; + + tnt4882_init(tnt_priv, board); + + return 0; +} + +void ni_pcmcia_detach(gpib_board_t *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + DEBUG(0, "%s(0x%p)\n", __func__, board); + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (nec_priv->iobase) { + tnt4882_board_reset(tnt_priv, board); + release_region((unsigned long)nec_priv->iobase, pcmcia_gpib_iosize); + } + } + tnt4882_free_private(board); +} + +#endif // GPIB_PCMCIA + +module_init(tnt4882_init_module); +module_exit(tnt4882_exit_module); diff --git a/drivers/staging/gpib/uapi/gpib_ioctl.h b/drivers/staging/gpib/uapi/gpib_ioctl.h new file mode 100644 index 0000000000000..6202865278ea7 --- /dev/null +++ b/drivers/staging/gpib/uapi/gpib_ioctl.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_IOCTL_H +#define _GPIB_IOCTL_H + +#include +#include + +#define GPIB_CODE 160 + +typedef struct { + char name[100]; +} board_type_ioctl_t; + +/* argument for read/write/command ioctls */ +typedef struct { + uint64_t buffer_ptr; + unsigned int requested_transfer_count; + unsigned int completed_transfer_count; + int end; /* end flag return for reads, end io suppression request for cmd*/ + int handle; +} read_write_ioctl_t; + +typedef struct { + unsigned int handle; + unsigned int pad; + int sad; + unsigned is_board : 1; +} open_dev_ioctl_t; + +typedef struct { + unsigned int handle; +} close_dev_ioctl_t; + +typedef struct { + unsigned int pad; + int sad; + uint8_t status_byte; +} serial_poll_ioctl_t; + +typedef struct { + int eos; + int eos_flags; +} eos_ioctl_t; + +typedef struct { + int handle; + int wait_mask; + int clear_mask; + int set_mask; + int ibsta; + int pad; + int sad; + unsigned int usec_timeout; +} wait_ioctl_t; + +typedef struct { + uint64_t init_data_ptr; + int init_data_length; + int online; +} online_ioctl_t; + +typedef struct { + unsigned int num_bytes; + unsigned int pad; + int sad; +} spoll_bytes_ioctl_t; + +typedef struct { + unsigned int pad; + int sad; + int parallel_poll_configuration; + int autopolling; + int is_system_controller; + unsigned int t1_delay; + unsigned ist : 1; + unsigned no_7_bit_eos : 1; +} board_info_ioctl_t; + +typedef struct { + int pci_bus; + int pci_slot; +} select_pci_ioctl_t; + +typedef struct { + uint8_t config; + unsigned set_ist : 1; + unsigned clear_ist : 1; +} ppoll_config_ioctl_t; + +typedef struct { + unsigned int handle; + unsigned int pad; +} pad_ioctl_t; + +typedef struct { + unsigned int handle; + int sad; +} sad_ioctl_t; + +// select a piece of hardware to attach by its sysfs device path +typedef struct { + char device_path[0x1000]; +} select_device_path_ioctl_t; + +typedef short event_ioctl_t; +typedef int rsc_ioctl_t; +typedef unsigned int t1_delay_ioctl_t; +typedef short autospoll_ioctl_t; +typedef short local_ppoll_mode_ioctl_t; + +// update status byte and request service +typedef struct { + uint8_t status_byte; + int new_reason_for_service; +} request_service2_t; + +/* Standard functions. */ +enum gpib_ioctl { + IBRD = _IOWR(GPIB_CODE, 100, read_write_ioctl_t), + IBWRT = _IOWR(GPIB_CODE, 101, read_write_ioctl_t), + IBCMD = _IOWR(GPIB_CODE, 102, read_write_ioctl_t), + IBOPENDEV = _IOWR(GPIB_CODE, 3, open_dev_ioctl_t), + IBCLOSEDEV = _IOW(GPIB_CODE, 4, close_dev_ioctl_t), + IBWAIT = _IOWR(GPIB_CODE, 5, wait_ioctl_t), + IBRPP = _IOWR(GPIB_CODE, 6, uint8_t), + + IBSIC = _IOW(GPIB_CODE, 9, unsigned int), + IBSRE = _IOW(GPIB_CODE, 10, int), + IBGTS = _IO(GPIB_CODE, 11), + IBCAC = _IOW(GPIB_CODE, 12, int), + IBLINES = _IOR(GPIB_CODE, 14, short), + IBPAD = _IOW(GPIB_CODE, 15, pad_ioctl_t), + IBSAD = _IOW(GPIB_CODE, 16, sad_ioctl_t), + IBTMO = _IOW(GPIB_CODE, 17, unsigned int), + IBRSP = _IOWR(GPIB_CODE, 18, serial_poll_ioctl_t), + IBEOS = _IOW(GPIB_CODE, 19, eos_ioctl_t), + IBRSV = _IOW(GPIB_CODE, 20, uint8_t), + CFCBASE = _IOW(GPIB_CODE, 21, uint64_t), + CFCIRQ = _IOW(GPIB_CODE, 22, unsigned int), + CFCDMA = _IOW(GPIB_CODE, 23, unsigned int), + CFCBOARDTYPE = _IOW(GPIB_CODE, 24, board_type_ioctl_t), + + IBMUTEX = _IOW(GPIB_CODE, 26, int), + IBSPOLL_BYTES = _IOWR(GPIB_CODE, 27, spoll_bytes_ioctl_t), + IBPPC = _IOW(GPIB_CODE, 28, ppoll_config_ioctl_t), + IBBOARD_INFO = _IOR(GPIB_CODE, 29, board_info_ioctl_t), + + IBQUERY_BOARD_RSV = _IOR(GPIB_CODE, 31, int), + IBSELECT_PCI = _IOWR(GPIB_CODE, 32, select_pci_ioctl_t), + IBEVENT = _IOR(GPIB_CODE, 33, event_ioctl_t), + IBRSC = _IOW(GPIB_CODE, 34, rsc_ioctl_t), + IB_T1_DELAY = _IOW(GPIB_CODE, 35, t1_delay_ioctl_t), + IBLOC = _IO(GPIB_CODE, 36), + + IBAUTOSPOLL = _IOW(GPIB_CODE, 38, autospoll_ioctl_t), + IBONL = _IOW(GPIB_CODE, 39, online_ioctl_t), + IBPP2_SET = _IOW(GPIB_CODE, 40, local_ppoll_mode_ioctl_t), + IBPP2_GET = _IOR(GPIB_CODE, 41, local_ppoll_mode_ioctl_t), + IBSELECT_DEVICE_PATH = _IOW(GPIB_CODE, 43, select_device_path_ioctl_t), + // 44 was IBSELECT_SERIAL_NUMBER + IBRSV2 = _IOW(GPIB_CODE, 45, request_service2_t) +}; + +#endif /* _GPIB_IOCTL_H */ diff --git a/drivers/staging/gpib/uapi/gpib_user.h b/drivers/staging/gpib/uapi/gpib_user.h new file mode 100644 index 0000000000000..0896a55a758fe --- /dev/null +++ b/drivers/staging/gpib/uapi/gpib_user.h @@ -0,0 +1,363 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_USER_H +#define _GPIB_USER_H + +#define GPIB_MAX_NUM_BOARDS 16 +#define GPIB_MAX_NUM_DESCRIPTORS 0x1000 + +enum ibsta_bit_numbers { + DCAS_NUM = 0, + DTAS_NUM = 1, + LACS_NUM = 2, + TACS_NUM = 3, + ATN_NUM = 4, + CIC_NUM = 5, + REM_NUM = 6, + LOK_NUM = 7, + CMPL_NUM = 8, + EVENT_NUM = 9, + SPOLL_NUM = 10, + RQS_NUM = 11, + SRQI_NUM = 12, + END_NUM = 13, + TIMO_NUM = 14, + ERR_NUM = 15 +}; + +/* IBSTA status bits (returned by all functions) */ +enum ibsta_bits { + DCAS = (1 << DCAS_NUM), /* device clear state */ + DTAS = (1 << DTAS_NUM), /* device trigger state */ + LACS = (1 << LACS_NUM), /* GPIB interface is addressed as Listener */ + TACS = (1 << TACS_NUM), /* GPIB interface is addressed as Talker */ + ATN = (1 << ATN_NUM), /* Attention is asserted */ + CIC = (1 << CIC_NUM), /* GPIB interface is Controller-in-Charge */ + REM = (1 << REM_NUM), /* remote state */ + LOK = (1 << LOK_NUM), /* lockout state */ + CMPL = (1 << CMPL_NUM), /* I/O is complete */ + EVENT = (1 << EVENT_NUM), /* DCAS, DTAS, or IFC has occurred */ + SPOLL = (1 << SPOLL_NUM), /* board serial polled by busmaster */ + RQS = (1 << RQS_NUM), /* Device requesting service */ + SRQI = (1 << SRQI_NUM), /* SRQ is asserted */ + END = (1 << END_NUM), /* EOI or EOS encountered */ + TIMO = (1 << TIMO_NUM), /* Time limit on I/O or wait function exceeded */ + ERR = (1 << ERR_NUM), /* Function call terminated on error */ + + device_status_mask = ERR | TIMO | END | CMPL | RQS, + board_status_mask = ERR | TIMO | END | CMPL | SPOLL | + EVENT | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS | SRQI, +}; + +/* IBERR error codes */ +enum iberr_code { + EDVR = 0, /* system error */ + ECIC = 1, /* not CIC */ + ENOL = 2, /* no listeners */ + EADR = 3, /* CIC and not addressed before I/O */ + EARG = 4, /* bad argument to function call */ + ESAC = 5, /* not SAC */ + EABO = 6, /* I/O operation was aborted */ + ENEB = 7, /* non-existent board (GPIB interface offline) */ + EDMA = 8, /* DMA hardware error detected */ + EOIP = 10, /* new I/O attempted with old I/O in progress */ + ECAP = 11, /* no capability for intended opeation */ + EFSO = 12, /* file system operation error */ + EBUS = 14, /* bus error */ + ESTB = 15, /* lost serial poll bytes */ + ESRQ = 16, /* SRQ stuck on */ + ETAB = 20 /* Table Overflow */ +}; + +/* Timeout values and meanings */ +enum gpib_timeout { + TNONE = 0, /* Infinite timeout (disabled) */ + T10us = 1, /* Timeout of 10 usec (ideal) */ + T30us = 2, /* Timeout of 30 usec (ideal) */ + T100us = 3, /* Timeout of 100 usec (ideal) */ + T300us = 4, /* Timeout of 300 usec (ideal) */ + T1ms = 5, /* Timeout of 1 msec (ideal) */ + T3ms = 6, /* Timeout of 3 msec (ideal) */ + T10ms = 7, /* Timeout of 10 msec (ideal) */ + T30ms = 8, /* Timeout of 30 msec (ideal) */ + T100ms = 9, /* Timeout of 100 msec (ideal) */ + T300ms = 10, /* Timeout of 300 msec (ideal) */ + T1s = 11, /* Timeout of 1 sec (ideal) */ + T3s = 12, /* Timeout of 3 sec (ideal) */ + T10s = 13, /* Timeout of 10 sec (ideal) */ + T30s = 14, /* Timeout of 30 sec (ideal) */ + T100s = 15, /* Timeout of 100 sec (ideal) */ + T300s = 16, /* Timeout of 300 sec (ideal) */ + T1000s = 17 /* Timeout of 1000 sec (maximum) */ +}; + +/* End-of-string (EOS) modes for use with ibeos */ + +enum eos_flags { + EOS_MASK = 0x1c00, + REOS = 0x0400, /* Terminate reads on EOS */ + XEOS = 0x800, /* assert EOI when EOS char is sent */ + BIN = 0x1000 /* Do 8-bit compare on EOS */ +}; + +/* GPIB Bus Control Lines bit vector */ +enum bus_control_line { + ValidDAV = 0x01, + ValidNDAC = 0x02, + ValidNRFD = 0x04, + ValidIFC = 0x08, + ValidREN = 0x10, + ValidSRQ = 0x20, + ValidATN = 0x40, + ValidEOI = 0x80, + ValidALL = 0xff, + BusDAV = 0x0100, /* DAV line status bit */ + BusNDAC = 0x0200, /* NDAC line status bit */ + BusNRFD = 0x0400, /* NRFD line status bit */ + BusIFC = 0x0800, /* IFC line status bit */ + BusREN = 0x1000, /* REN line status bit */ + BusSRQ = 0x2000, /* SRQ line status bit */ + BusATN = 0x4000, /* ATN line status bit */ + BusEOI = 0x8000 /* EOI line status bit */ +}; + +enum old_bus_control_line { + BUS_DAV = 0x0100, /* DAV line status bit */ + BUS_NDAC = 0x0200, /* NDAC line status bit */ + BUS_NRFD = 0x0400, /* NRFD line status bit */ + BUS_IFC = 0x0800, /* IFC line status bit */ + BUS_REN = 0x1000, /* REN line status bit */ + BUS_SRQ = 0x2000, /* SRQ line status bit */ + BUS_ATN = 0x4000, /* ATN line status bit */ + BUS_EOI = 0x8000 /* EOI line status bit */ +}; + +/* Possible GPIB command messages */ + +enum cmd_byte { + GTL = 0x1, /* go to local */ + SDC = 0x4, /* selected device clear */ + PPConfig = 0x5, +#ifndef PPC + PPC = PPConfig, /* parallel poll configure */ +#endif + GET = 0x8, /* group execute trigger */ + TCT = 0x9, /* take control */ + LLO = 0x11, /* local lockout */ + DCL = 0x14, /* device clear */ + PPU = 0x15, /* parallel poll unconfigure */ + SPE = 0x18, /* serial poll enable */ + SPD = 0x19, /* serial poll disable */ + CFE = 0x1f, /* configure enable */ + LAD = 0x20, /* value to be 'ored' in to obtain listen address */ + UNL = 0x3F, /* unlisten */ + TAD = 0x40, /* value to be 'ored' in to obtain talk address */ + UNT = 0x5F, /* untalk */ + SAD = 0x60, /* my secondary address (base) */ + PPE = 0x60, /* parallel poll enable (base) */ + PPD = 0x70 /* parallel poll disable */ +}; + +enum ppe_bits { + PPC_DISABLE = 0x10, + PPC_SENSE = 0x8, /* parallel poll sense bit */ + PPC_DIO_MASK = 0x7 +}; + +/* confine address to range 0 to 30. */ +static inline unsigned int gpib_address_restrict(unsigned int addr) +{ + addr &= 0x1f; + if (addr == 0x1f) + addr = 0; + return addr; +} + +static inline uint8_t MLA(unsigned int addr) +{ + return gpib_address_restrict(addr) | LAD; +} + +static inline uint8_t MTA(unsigned int addr) +{ + return gpib_address_restrict(addr) | TAD; +} + +static inline uint8_t MSA(unsigned int addr) +{ + return gpib_address_restrict(addr) | SAD; +} + +static inline uint8_t PPE_byte(unsigned int dio_line, int sense) +{ + uint8_t cmd; + + cmd = PPE; + if (sense) + cmd |= PPC_SENSE; + cmd |= (dio_line - 1) & 0x7; + return cmd; +} + +static inline uint8_t CFGn(unsigned int meters) +{ + return 0x6 | (meters & 0xf); +} + +/* mask of bits that actually matter in a command byte */ +enum { + gpib_command_mask = 0x7f, +}; + +static inline int is_PPE(uint8_t command) +{ + return (command & 0x70) == 0x60; +} + +static inline int is_PPD(uint8_t command) +{ + return (command & 0x70) == 0x70; +} + +static inline int in_addressed_command_group(uint8_t command) +{ + return (command & 0x70) == 0x0; +} + +static inline int in_universal_command_group(uint8_t command) +{ + return (command & 0x70) == 0x10; +} + +static inline int in_listen_address_group(uint8_t command) +{ + return (command & 0x60) == 0x20; +} + +static inline int in_talk_address_group(uint8_t command) +{ + return (command & 0x60) == 0x40; +} + +static inline int in_primary_command_group(uint8_t command) +{ + return in_addressed_command_group(command) || + in_universal_command_group(command) || + in_listen_address_group(command) || + in_talk_address_group(command); +} + +static inline int gpib_address_equal(unsigned int pad1, int sad1, unsigned int pad2, int sad2) +{ + if (pad1 == pad2) { + if (sad1 == sad2) + return 1; + if (sad1 < 0 && sad2 < 0) + return 1; + } + + return 0; +} + +enum ibask_option { + IbaPAD = 0x1, + IbaSAD = 0x2, + IbaTMO = 0x3, + IbaEOT = 0x4, + IbaPPC = 0x5, /* board only */ + IbaREADDR = 0x6, /* device only */ + IbaAUTOPOLL = 0x7, /* board only */ + IbaCICPROT = 0x8, /* board only */ + IbaIRQ = 0x9, /* board only */ + IbaSC = 0xa, /* board only */ + IbaSRE = 0xb, /* board only */ + IbaEOSrd = 0xc, + IbaEOSwrt = 0xd, + IbaEOScmp = 0xe, + IbaEOSchar = 0xf, + IbaPP2 = 0x10, /* board only */ + IbaTIMING = 0x11, /* board only */ + IbaDMA = 0x12, /* board only */ + IbaReadAdjust = 0x13, + IbaWriteAdjust = 0x14, + IbaEventQueue = 0x15, /* board only */ + IbaSPollBit = 0x16, /* board only */ + IbaSpollBit = 0x16, /* board only */ + IbaSendLLO = 0x17, /* board only */ + IbaSPollTime = 0x18, /* device only */ + IbaPPollTime = 0x19, /* board only */ + IbaEndBitIsNormal = 0x1a, + IbaUnAddr = 0x1b, /* device only */ + IbaHSCableLength = 0x1f, /* board only */ + IbaIst = 0x20, /* board only */ + IbaRsv = 0x21, /* board only */ + IbaBNA = 0x200, /* device only */ + /* linux-gpib extensions */ + Iba7BitEOS = 0x1000 /* board only. Returns 1 if board supports 7 bit eos compares*/ +}; + +enum ibconfig_option { + IbcPAD = 0x1, + IbcSAD = 0x2, + IbcTMO = 0x3, + IbcEOT = 0x4, + IbcPPC = 0x5, /* board only */ + IbcREADDR = 0x6, /* device only */ + IbcAUTOPOLL = 0x7, /* board only */ + IbcCICPROT = 0x8, /* board only */ + IbcIRQ = 0x9, /* board only */ + IbcSC = 0xa, /* board only */ + IbcSRE = 0xb, /* board only */ + IbcEOSrd = 0xc, + IbcEOSwrt = 0xd, + IbcEOScmp = 0xe, + IbcEOSchar = 0xf, + IbcPP2 = 0x10, /* board only */ + IbcTIMING = 0x11, /* board only */ + IbcDMA = 0x12, /* board only */ + IbcReadAdjust = 0x13, + IbcWriteAdjust = 0x14, + IbcEventQueue = 0x15, /* board only */ + IbcSPollBit = 0x16, /* board only */ + IbcSpollBit = 0x16, /* board only */ + IbcSendLLO = 0x17, /* board only */ + IbcSPollTime = 0x18, /* device only */ + IbcPPollTime = 0x19, /* board only */ + IbcEndBitIsNormal = 0x1a, + IbcUnAddr = 0x1b, /* device only */ + IbcHSCableLength = 0x1f, /* board only */ + IbcIst = 0x20, /* board only */ + IbcRsv = 0x21, /* board only */ + IbcBNA = 0x200 /* device only */ +}; + +enum t1_delays { + T1_DELAY_2000ns = 1, + T1_DELAY_500ns = 2, + T1_DELAY_350ns = 3 +}; + +enum { + request_service_bit = 0x40, +}; + +enum gpib_events { + EventNone = 0, + EventDevTrg = 1, + EventDevClr = 2, + EventIFC = 3 +}; + +enum gpib_stb { + IbStbRQS = 0x40, /* IEEE 488.1 & 2 */ + IbStbESB = 0x20, /* IEEE 488.2 only */ + IbStbMAV = 0x10 /* IEEE 488.2 only */ +}; + +#endif /* _GPIB_USER_H */ + +/* Check for errors */ diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index aa6f266b62a14..90ab32638d3f5 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -470,7 +470,7 @@ MODULE_DEVICE_TABLE(of, arche_apb_ctrl_of_match); static struct platform_driver arche_apb_ctrl_device_driver = { .probe = arche_apb_ctrl_probe, - .remove_new = arche_apb_ctrl_remove, + .remove = arche_apb_ctrl_remove, .shutdown = arche_apb_ctrl_shutdown, .driver = { .name = "arche-apb-ctrl", diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index b33977ccd5271..d48464390f58e 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -623,7 +623,7 @@ MODULE_DEVICE_TABLE(of, arche_platform_of_match); static struct platform_driver arche_platform_device_driver = { .probe = arche_platform_probe, - .remove_new = arche_platform_remove, + .remove = arche_platform_remove, .shutdown = arche_platform_shutdown, .driver = { .name = "arche-platform-ctrl", diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 9b26e148d40fb..16bcf7fc81586 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -42,11 +42,6 @@ struct gb_gpio_controller { struct mutex irq_lock; }; -static inline struct gb_gpio_controller *gpio_chip_to_gb_gpio_controller(struct gpio_chip *chip) -{ - return container_of(chip, struct gb_gpio_controller, chip); -} - static struct gpio_chip *irq_data_to_gpio_chip(struct irq_data *d) { return d->domain->host_data; @@ -278,7 +273,7 @@ static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, static void gb_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; line->masked = true; @@ -288,7 +283,7 @@ static void gb_gpio_irq_mask(struct irq_data *d) static void gb_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; line->masked = false; @@ -298,7 +293,7 @@ static void gb_gpio_irq_unmask(struct irq_data *d) static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; struct device *dev = &ggc->gbphy_dev->dev; u8 irq_type; @@ -336,7 +331,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) static void gb_gpio_irq_bus_lock(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); mutex_lock(&ggc->irq_lock); } @@ -344,7 +339,7 @@ static void gb_gpio_irq_bus_lock(struct irq_data *d) static void gb_gpio_irq_bus_sync_unlock(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; if (line->irq_type_pending) { @@ -407,21 +402,21 @@ static int gb_gpio_request_handler(struct gb_operation *op) static int gb_gpio_request(struct gpio_chip *chip, unsigned int offset) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); return gb_gpio_activate_operation(ggc, (u8)offset); } static void gb_gpio_free(struct gpio_chip *chip, unsigned int offset) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); gb_gpio_deactivate_operation(ggc, (u8)offset); } static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); u8 which; int ret; @@ -435,7 +430,7 @@ static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); return gb_gpio_direction_in_operation(ggc, (u8)offset); } @@ -443,14 +438,14 @@ static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); return gb_gpio_direction_out_operation(ggc, (u8)offset, !!value); } static int gb_gpio_get(struct gpio_chip *chip, unsigned int offset) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); u8 which; int ret; @@ -464,7 +459,7 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned int offset) static void gb_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); gb_gpio_set_value_operation(ggc, (u8)offset, !!value); } @@ -472,7 +467,7 @@ static void gb_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) static int gb_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) { - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpiochip_get_data(chip); u32 debounce; if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) @@ -579,7 +574,7 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_line_free; - ret = gpiochip_add_data(gpio, NULL); + ret = gpiochip_add_data(gpio, ggc); if (ret) { dev_err(&gbphy_dev->dev, "failed to add gpio chip: %d\n", ret); goto exit_line_free; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index cdf4ebb93b104..8eab94cb06faf 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -596,11 +596,13 @@ static int get_serial_info(struct tty_struct *tty, struct gb_tty *gb_tty = tty->driver_data; ss->line = gb_tty->minor; + mutex_lock(&gb_tty->port.mutex); ss->close_delay = jiffies_to_msecs(gb_tty->port.close_delay) / 10; ss->closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : jiffies_to_msecs(gb_tty->port.closing_wait) / 10; + mutex_unlock(&gb_tty->port.mutex); return 0; } diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO deleted file mode 100644 index 0fa6a5500bdb1..0000000000000 --- a/drivers/staging/iio/TODO +++ /dev/null @@ -1,5 +0,0 @@ -2020-02-25 - - -Contact: Jonathan Cameron . -Mailing list: linux-iio@vger.kernel.org diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 4ae1a7039418b..d5544fc2fe989 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -628,9 +628,9 @@ static void ad5933_work(struct work_struct *work) int scan_count = bitmap_weight(indio_dev->active_scan_mask, iio_get_masklength(indio_dev)); ret = ad5933_i2c_read(st->client, - test_bit(1, indio_dev->active_scan_mask) ? - AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA, - scan_count * 2, (u8 *)buf); + test_bit(1, indio_dev->active_scan_mask) ? + AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA, + scan_count * 2, (u8 *)buf); if (ret) return; diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c index 712f916f0935f..71e6e278a4b3c 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c +++ b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c @@ -629,7 +629,7 @@ MODULE_DEVICE_TABLE(of, atmel_isc_of_match); static struct platform_driver atmel_isc_driver = { .probe = atmel_isc_probe, - .remove_new = atmel_isc_remove, + .remove = atmel_isc_remove, .driver = { .name = "atmel-sama5d2-isc", .pm = &atmel_isc_dev_pm_ops, diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c index 9485167d5b7d7..1f74c2dd044cf 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c +++ b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c @@ -592,7 +592,7 @@ MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); static struct platform_driver microchip_xisc_driver = { .probe = microchip_xisc_probe, - .remove_new = microchip_xisc_remove, + .remove = microchip_xisc_remove, .driver = { .name = "microchip-sama7g5-xisc", .pm = µchip_xisc_dev_pm_ops, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 785aac8819221..3edbc57be2caa 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -2076,7 +2076,7 @@ MODULE_DEVICE_TABLE(platform, imx_csi_ids); static struct platform_driver imx_csi_driver = { .probe = imx_csi_probe, - .remove_new = imx_csi_remove, + .remove = imx_csi_remove, .id_table = imx_csi_ids, .driver = { .name = "imx-ipuv3-csi", diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index be54dca11465d..a08389b99d14a 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -129,7 +129,7 @@ MODULE_DEVICE_TABLE(of, imx_media_dt_ids); static struct platform_driver imx_media_pdrv = { .probe = imx_media_probe, - .remove_new = imx_media_remove, + .remove = imx_media_remove, .driver = { .name = "imx-media", .of_match_table = imx_media_dt_ids, diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 0d8b420616235..dd8c7b3233bcc 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -836,7 +836,7 @@ static struct platform_driver csi2_driver = { .of_match_table = csi2_dt_ids, }, .probe = csi2_probe, - .remove_new = csi2_remove, + .remove = csi2_remove, }; module_platform_driver(csi2_driver); diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 52185090129ba..6d34a482492e8 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -1117,7 +1117,7 @@ static void vdec_remove(struct platform_device *pdev) static struct platform_driver meson_vdec_driver = { .probe = vdec_probe, - .remove_new = vdec_remove, + .remove = vdec_remove, .driver = { .name = "meson-vdec", .of_match_table = vdec_dt_match, diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index f468af64bbef1..f9bef5173bf25 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -1101,7 +1101,7 @@ static const struct dev_pm_ops rkvdec_pm_ops = { static struct platform_driver rkvdec_driver = { .probe = rkvdec_probe, - .remove_new = rkvdec_remove, + .remove = rkvdec_remove, .driver = { .name = "rkvdec", .of_match_table = of_rkvdec_match, diff --git a/drivers/staging/media/starfive/camss/stf-camss.c b/drivers/staging/media/starfive/camss/stf-camss.c index b6d34145bc191..259aaad010d2f 100644 --- a/drivers/staging/media/starfive/camss/stf-camss.c +++ b/drivers/staging/media/starfive/camss/stf-camss.c @@ -422,7 +422,7 @@ static const struct dev_pm_ops stfcamss_pm_ops = { static struct platform_driver stfcamss_driver = { .probe = stfcamss_probe, - .remove_new = stfcamss_remove, + .remove = stfcamss_remove, .driver = { .name = "starfive-camss", .pm = &stfcamss_pm_ops, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index f52df68360452..52a9588462ce5 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -705,7 +705,7 @@ static const struct dev_pm_ops cedrus_dev_pm_ops = { static struct platform_driver cedrus_driver = { .probe = cedrus_probe, - .remove_new = cedrus_remove, + .remove = cedrus_remove, .driver = { .name = CEDRUS_NAME, .of_match_table = cedrus_dt_match, diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c index 58f8ae92320d6..6877f2beee8c8 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c @@ -536,7 +536,7 @@ MODULE_DEVICE_TABLE(of, sun6i_isp_of_match); static struct platform_driver sun6i_isp_platform_driver = { .probe = sun6i_isp_probe, - .remove_new = sun6i_isp_remove, + .remove = sun6i_isp_remove, .driver = { .name = SUN6I_ISP_NAME, .of_match_table = sun6i_isp_of_match, diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index 255cccd0c5fda..604185c00a1a3 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -858,5 +858,5 @@ struct platform_driver tegra_csi_driver = { .pm = &tegra_csi_pm_ops, }, .probe = tegra_csi_probe, - .remove_new = tegra_csi_remove, + .remove = tegra_csi_remove, }; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index ad481b35e618a..c9276ff76157f 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1977,5 +1977,5 @@ struct platform_driver tegra_vi_driver = { .pm = &tegra_vi_pm_ops, }, .probe = tegra_vi_probe, - .remove_new = tegra_vi_remove, + .remove = tegra_vi_remove, }; diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c index 8504b9ea9cea2..5ec717f3afd50 100644 --- a/drivers/staging/media/tegra-video/vip.c +++ b/drivers/staging/media/tegra-video/vip.c @@ -281,5 +281,5 @@ struct platform_driver tegra_vip_driver = { .of_match_table = tegra_vip_of_id_table, }, .probe = tegra_vip_probe, - .remove_new = tegra_vip_remove, + .remove = tegra_vip_remove, }; diff --git a/drivers/staging/most/TODO b/drivers/staging/most/TODO index 4fa11a9d2cf74..a6448a05ed461 100644 --- a/drivers/staging/most/TODO +++ b/drivers/staging/most/TODO @@ -1,8 +1 @@ * Get through code review with Greg Kroah-Hartman - -Contact: -To: -Christian Gromm -Cc: -Michael Fabry -Christian Gromm diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index ed6a9cc885412..dad2abe6c0c90 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -1090,7 +1090,7 @@ MODULE_DEVICE_TABLE(of, dim2_of_match); static struct platform_driver dim2_driver = { .probe = dim2_probe, - .remove_new = dim2_remove, + .remove = dim2_remove, .driver = { .name = "hdm_dim2", .of_match_table = dim2_of_match, diff --git a/drivers/staging/most/i2c/i2c.c b/drivers/staging/most/i2c/i2c.c index ce869280a056b..184b2dd11fc34 100644 --- a/drivers/staging/most/i2c/i2c.c +++ b/drivers/staging/most/i2c/i2c.c @@ -352,8 +352,8 @@ static void i2c_remove(struct i2c_client *client) } static const struct i2c_device_id i2c_id[] = { - { "most_i2c", 0 }, - { }, /* Terminating entry */ + { "most_i2c" }, + { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(i2c, i2c_id); diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 977f8fc29e631..263774e6a78ca 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -952,7 +952,7 @@ MODULE_DEVICE_TABLE(of, nvidia_nvec_of_match); static struct platform_driver nvec_device_driver = { .probe = tegra_nvec_probe, - .remove_new = tegra_nvec_remove, + .remove = tegra_nvec_remove, .driver = { .name = "nvec", .pm = &nvec_pm_ops, diff --git a/drivers/staging/nvec/nvec_kbd.c b/drivers/staging/nvec/nvec_kbd.c index d0259c80f8105..d2b91318f1516 100644 --- a/drivers/staging/nvec/nvec_kbd.c +++ b/drivers/staging/nvec/nvec_kbd.c @@ -175,7 +175,7 @@ static void nvec_kbd_remove(struct platform_device *pdev) static struct platform_driver nvec_kbd_driver = { .probe = nvec_kbd_probe, - .remove_new = nvec_kbd_remove, + .remove = nvec_kbd_remove, .driver = { .name = "nvec-kbd", }, diff --git a/drivers/staging/nvec/nvec_power.c b/drivers/staging/nvec/nvec_power.c index 9943b1fff1905..e0e67a3eb7222 100644 --- a/drivers/staging/nvec/nvec_power.c +++ b/drivers/staging/nvec/nvec_power.c @@ -433,7 +433,7 @@ static void nvec_power_remove(struct platform_device *pdev) static struct platform_driver nvec_power_driver = { .probe = nvec_power_probe, - .remove_new = nvec_power_remove, + .remove = nvec_power_remove, .driver = { .name = "nvec-power", } diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c index f34016c4a26b6..575233fa1677e 100644 --- a/drivers/staging/nvec/nvec_ps2.c +++ b/drivers/staging/nvec/nvec_ps2.c @@ -175,7 +175,7 @@ static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend, static struct platform_driver nvec_mouse_driver = { .probe = nvec_mouse_probe, - .remove_new = nvec_mouse_remove, + .remove = nvec_mouse_remove, .driver = { .name = "nvec-mouse", .pm = &nvec_mouse_pm_ops, diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index bbf33b88bb7c2..261f8dbdc3827 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -40,8 +40,8 @@ #define GET_SKBUFF_QOS(skb) 0 #endif -static void cvm_oct_tx_do_cleanup(unsigned long arg); -static DECLARE_TASKLET_OLD(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup); +static void cvm_oct_tx_do_cleanup(struct tasklet_struct *clean); +static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup); /* Maximum number of SKBs to try to free per xmit packet. */ #define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2) @@ -670,7 +670,7 @@ void cvm_oct_tx_shutdown_dev(struct net_device *dev) } } -static void cvm_oct_tx_do_cleanup(unsigned long arg) +static void cvm_oct_tx_do_cleanup(struct tasklet_struct *clean) { int port; diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index a5e99cc78a454..eadb74fc14c8d 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -977,7 +977,7 @@ MODULE_DEVICE_TABLE(of, cvm_oct_match); static struct platform_driver cvm_oct_driver = { .probe = cvm_oct_probe, - .remove_new = cvm_oct_remove, + .remove = cvm_oct_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = cvm_oct_match, diff --git a/drivers/staging/olpc_dcon/Kconfig b/drivers/staging/olpc_dcon/Kconfig deleted file mode 100644 index d0ba34cc32f7a..0000000000000 --- a/drivers/staging/olpc_dcon/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config FB_OLPC_DCON - tristate "One Laptop Per Child Display CONtroller support" - depends on OLPC && FB && BROKEN - depends on I2C - depends on GPIO_CS5535 && ACPI - select BACKLIGHT_CLASS_DEVICE - help - In order to support very low power operation, the XO laptop uses a - secondary Display CONtroller, or DCON. This secondary controller - is present in the video pipeline between the primary display - controller (integrate into the processor or chipset) and the LCD - panel. It allows the main processor/display controller to be - completely powered off while still retaining an image on the display. - This controller is only available on OLPC platforms. Unless you have - one of these platforms, you will want to say 'N'. - diff --git a/drivers/staging/olpc_dcon/Makefile b/drivers/staging/olpc_dcon/Makefile deleted file mode 100644 index 734b2ce26066f..0000000000000 --- a/drivers/staging/olpc_dcon/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -olpc-dcon-objs += olpc_dcon.o olpc_dcon_xo_1.o olpc_dcon_xo_1_5.o -obj-$(CONFIG_FB_OLPC_DCON) += olpc-dcon.o - - diff --git a/drivers/staging/olpc_dcon/TODO b/drivers/staging/olpc_dcon/TODO deleted file mode 100644 index 7c263358b44a9..0000000000000 --- a/drivers/staging/olpc_dcon/TODO +++ /dev/null @@ -1,15 +0,0 @@ -TODO: - - complete rewrite: - 1. The underlying fbdev drivers need to be converted into drm kernel - modesetting drivers. - 2. The dcon low-power display mode can then be integrated using the - drm damage tracking and self-refresh helpers. - This bolted-on self-refresh support that digs around in fbdev - internals, but isn't properly integrated, is not the correct solution. - - see if vx855 gpio API can be made similar enough to cs5535 so we can - share more code - -Please send patches to Greg Kroah-Hartman and -copy: - Daniel Drake - Jens Frederich diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c deleted file mode 100644 index 4cb904a5f8f40..0000000000000 --- a/drivers/staging/olpc_dcon/olpc_dcon.c +++ /dev/null @@ -1,807 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Mainly by David Woodhouse, somewhat modified by Jordan Crouse - * - * Copyright © 2006-2007 Red Hat, Inc. - * Copyright © 2006-2007 Advanced Micro Devices, Inc. - * Copyright © 2009 VIA Technology, Inc. - * Copyright (c) 2010-2011 Andres Salomon - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "olpc_dcon.h" - -/* Module definitions */ - -static ushort resumeline = 898; -module_param(resumeline, ushort, 0444); - -static struct dcon_platform_data *pdata; - -/* I2C structures */ - -/* Platform devices */ -static struct platform_device *dcon_device; - -static unsigned short normal_i2c[] = { 0x0d, I2C_CLIENT_END }; - -static s32 dcon_write(struct dcon_priv *dcon, u8 reg, u16 val) -{ - return i2c_smbus_write_word_data(dcon->client, reg, val); -} - -static s32 dcon_read(struct dcon_priv *dcon, u8 reg) -{ - return i2c_smbus_read_word_data(dcon->client, reg); -} - -/* ===== API functions - these are called by a variety of users ==== */ - -static int dcon_hw_init(struct dcon_priv *dcon, int is_init) -{ - u16 ver; - int rc = 0; - - ver = dcon_read(dcon, DCON_REG_ID); - if ((ver >> 8) != 0xDC) { - pr_err("DCON ID not 0xDCxx: 0x%04x instead.\n", ver); - rc = -ENXIO; - goto err; - } - - if (is_init) { - pr_info("Discovered DCON version %x\n", ver & 0xFF); - rc = pdata->init(dcon); - if (rc != 0) { - pr_err("Unable to init.\n"); - goto err; - } - } - - if (ver < 0xdc02) { - dev_err(&dcon->client->dev, - "DCON v1 is unsupported, giving up..\n"); - rc = -ENODEV; - goto err; - } - - /* SDRAM setup/hold time */ - dcon_write(dcon, 0x3a, 0xc040); - dcon_write(dcon, DCON_REG_MEM_OPT_A, 0x0000); /* clear option bits */ - dcon_write(dcon, DCON_REG_MEM_OPT_A, - MEM_DLL_CLOCK_DELAY | MEM_POWER_DOWN); - dcon_write(dcon, DCON_REG_MEM_OPT_B, MEM_SOFT_RESET); - - /* Colour swizzle, AA, no passthrough, backlight */ - if (is_init) { - dcon->disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE | - MODE_CSWIZZLE | MODE_COL_AA; - } - dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode); - - /* Set the scanline to interrupt on during resume */ - dcon_write(dcon, DCON_REG_SCAN_INT, resumeline); - -err: - return rc; -} - -/* - * The smbus doesn't always come back due to what is believed to be - * hardware (power rail) bugs. For older models where this is known to - * occur, our solution is to attempt to wait for the bus to stabilize; - * if it doesn't happen, cut power to the dcon, repower it, and wait - * for the bus to stabilize. Rinse, repeat until we have a working - * smbus. For newer models, we simply BUG(); we want to know if this - * still happens despite the power fixes that have been made! - */ -static int dcon_bus_stabilize(struct dcon_priv *dcon, int is_powered_down) -{ - unsigned long timeout; - u8 pm; - int x; - -power_up: - if (is_powered_down) { - pm = 1; - x = olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0); - if (x) { - pr_warn("unable to force dcon to power up: %d!\n", x); - return x; - } - usleep_range(10000, 11000); /* we'll be conservative */ - } - - pdata->bus_stabilize_wiggle(); - - for (x = -1, timeout = 50; timeout && x < 0; timeout--) { - usleep_range(1000, 1100); - x = dcon_read(dcon, DCON_REG_ID); - } - if (x < 0) { - pr_err("unable to stabilize dcon's smbus, reasserting power and praying.\n"); - BUG_ON(olpc_board_at_least(olpc_board(0xc2))); - pm = 0; - olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0); - msleep(100); - is_powered_down = 1; - goto power_up; /* argh, stupid hardware.. */ - } - - if (is_powered_down) - return dcon_hw_init(dcon, 0); - return 0; -} - -static void dcon_set_backlight(struct dcon_priv *dcon, u8 level) -{ - dcon->bl_val = level; - dcon_write(dcon, DCON_REG_BRIGHT, dcon->bl_val); - - /* Purposely turn off the backlight when we go to level 0 */ - if (dcon->bl_val == 0) { - dcon->disp_mode &= ~MODE_BL_ENABLE; - dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode); - } else if (!(dcon->disp_mode & MODE_BL_ENABLE)) { - dcon->disp_mode |= MODE_BL_ENABLE; - dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode); - } -} - -/* Set the output type to either color or mono */ -static int dcon_set_mono_mode(struct dcon_priv *dcon, bool enable_mono) -{ - if (dcon->mono == enable_mono) - return 0; - - dcon->mono = enable_mono; - - if (enable_mono) { - dcon->disp_mode &= ~(MODE_CSWIZZLE | MODE_COL_AA); - dcon->disp_mode |= MODE_MONO_LUMA; - } else { - dcon->disp_mode &= ~(MODE_MONO_LUMA); - dcon->disp_mode |= MODE_CSWIZZLE | MODE_COL_AA; - } - - dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode); - return 0; -} - -/* For now, this will be really stupid - we need to address how - * DCONLOAD works in a sleep and account for it accordingly - */ - -static void dcon_sleep(struct dcon_priv *dcon, bool sleep) -{ - int x; - - /* Turn off the backlight and put the DCON to sleep */ - - if (dcon->asleep == sleep) - return; - - if (!olpc_board_at_least(olpc_board(0xc2))) - return; - - if (sleep) { - u8 pm = 0; - - x = olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0); - if (x) - pr_warn("unable to force dcon to power down: %d!\n", x); - else - dcon->asleep = sleep; - } else { - /* Only re-enable the backlight if the backlight value is set */ - if (dcon->bl_val != 0) - dcon->disp_mode |= MODE_BL_ENABLE; - x = dcon_bus_stabilize(dcon, 1); - if (x) - pr_warn("unable to reinit dcon hardware: %d!\n", x); - else - dcon->asleep = sleep; - - /* Restore backlight */ - dcon_set_backlight(dcon, dcon->bl_val); - } - - /* We should turn off some stuff in the framebuffer - but what? */ -} - -/* the DCON seems to get confused if we change DCONLOAD too - * frequently -- i.e., approximately faster than frame time. - * normally we don't change it this fast, so in general we won't - * delay here. - */ -static void dcon_load_holdoff(struct dcon_priv *dcon) -{ - ktime_t delta_t, now; - - while (1) { - now = ktime_get(); - delta_t = ktime_sub(now, dcon->load_time); - if (ktime_to_ns(delta_t) > NSEC_PER_MSEC * 20) - break; - mdelay(4); - } -} - -static bool dcon_blank_fb(struct dcon_priv *dcon, bool blank) -{ - int err; - - console_lock(); - lock_fb_info(dcon->fbinfo); - - dcon->ignore_fb_events = true; - err = fb_blank(dcon->fbinfo, - blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); - dcon->ignore_fb_events = false; - unlock_fb_info(dcon->fbinfo); - console_unlock(); - - if (err) { - dev_err(&dcon->client->dev, "couldn't %sblank framebuffer\n", - blank ? "" : "un"); - return false; - } - return true; -} - -/* Set the source of the display (CPU or DCON) */ -static void dcon_source_switch(struct work_struct *work) -{ - struct dcon_priv *dcon = container_of(work, struct dcon_priv, - switch_source); - int source = dcon->pending_src; - - if (dcon->curr_src == source) - return; - - dcon_load_holdoff(dcon); - - dcon->switched = false; - - switch (source) { - case DCON_SOURCE_CPU: - pr_info("%s to CPU\n", __func__); - /* Enable the scanline interrupt bit */ - if (dcon_write(dcon, DCON_REG_MODE, - dcon->disp_mode | MODE_SCAN_INT)) - pr_err("couldn't enable scanline interrupt!\n"); - else - /* Wait up to one second for the scanline interrupt */ - wait_event_timeout(dcon->waitq, dcon->switched, HZ); - - if (!dcon->switched) - pr_err("Timeout entering CPU mode; expect a screen glitch.\n"); - - /* Turn off the scanline interrupt */ - if (dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode)) - pr_err("couldn't disable scanline interrupt!\n"); - - /* - * Ideally we'd like to disable interrupts here so that the - * fb unblanking and DCON turn on happen at a known time value; - * however, we can't do that right now with fb_blank - * messing with semaphores. - * - * For now, we just hope.. - */ - if (!dcon_blank_fb(dcon, false)) { - pr_err("Failed to enter CPU mode\n"); - dcon->pending_src = DCON_SOURCE_DCON; - return; - } - - /* And turn off the DCON */ - pdata->set_dconload(1); - dcon->load_time = ktime_get(); - - pr_info("The CPU has control\n"); - break; - case DCON_SOURCE_DCON: - { - ktime_t delta_t; - - pr_info("%s to DCON\n", __func__); - - /* Clear DCONLOAD - this implies that the DCON is in control */ - pdata->set_dconload(0); - dcon->load_time = ktime_get(); - - wait_event_timeout(dcon->waitq, dcon->switched, HZ / 2); - - if (!dcon->switched) { - pr_err("Timeout entering DCON mode; expect a screen glitch.\n"); - } else { - /* sometimes the DCON doesn't follow its own rules, - * and doesn't wait for two vsync pulses before - * ack'ing the frame load with an IRQ. the result - * is that the display shows the *previously* - * loaded frame. we can detect this by looking at - * the time between asserting DCONLOAD and the IRQ -- - * if it's less than 20msec, then the DCON couldn't - * have seen two VSYNC pulses. in that case we - * deassert and reassert, and hope for the best. - * see http://dev.laptop.org/ticket/9664 - */ - delta_t = ktime_sub(dcon->irq_time, dcon->load_time); - if (dcon->switched && ktime_to_ns(delta_t) - < NSEC_PER_MSEC * 20) { - pr_err("missed loading, retrying\n"); - pdata->set_dconload(1); - mdelay(41); - pdata->set_dconload(0); - dcon->load_time = ktime_get(); - mdelay(41); - } - } - - dcon_blank_fb(dcon, true); - pr_info("The DCON has control\n"); - break; - } - default: - BUG(); - } - - dcon->curr_src = source; -} - -static void dcon_set_source(struct dcon_priv *dcon, int arg) -{ - if (dcon->pending_src == arg) - return; - - dcon->pending_src = arg; - - if (dcon->curr_src != arg) - schedule_work(&dcon->switch_source); -} - -static void dcon_set_source_sync(struct dcon_priv *dcon, int arg) -{ - dcon_set_source(dcon, arg); - flush_work(&dcon->switch_source); -} - -static ssize_t dcon_mode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct dcon_priv *dcon = dev_get_drvdata(dev); - - return sprintf(buf, "%4.4X\n", dcon->disp_mode); -} - -static ssize_t dcon_sleep_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct dcon_priv *dcon = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", dcon->asleep); -} - -static ssize_t dcon_freeze_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct dcon_priv *dcon = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", dcon->curr_src == DCON_SOURCE_DCON ? 1 : 0); -} - -static ssize_t dcon_mono_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct dcon_priv *dcon = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", dcon->mono); -} - -static ssize_t dcon_resumeline_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", resumeline); -} - -static ssize_t dcon_mono_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned long enable_mono; - int rc; - - rc = kstrtoul(buf, 10, &enable_mono); - if (rc) - return rc; - - dcon_set_mono_mode(dev_get_drvdata(dev), enable_mono ? true : false); - - return count; -} - -static ssize_t dcon_freeze_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct dcon_priv *dcon = dev_get_drvdata(dev); - unsigned long output; - int ret; - - ret = kstrtoul(buf, 10, &output); - if (ret) - return ret; - - switch (output) { - case 0: - dcon_set_source(dcon, DCON_SOURCE_CPU); - break; - case 1: - dcon_set_source_sync(dcon, DCON_SOURCE_DCON); - break; - case 2: /* normally unused */ - dcon_set_source(dcon, DCON_SOURCE_DCON); - break; - default: - return -EINVAL; - } - - return count; -} - -static ssize_t dcon_resumeline_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned short rl; - int rc; - - rc = kstrtou16(buf, 10, &rl); - if (rc) - return rc; - - resumeline = rl; - dcon_write(dev_get_drvdata(dev), DCON_REG_SCAN_INT, resumeline); - - return count; -} - -static ssize_t dcon_sleep_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned long output; - int ret; - - ret = kstrtoul(buf, 10, &output); - if (ret) - return ret; - - dcon_sleep(dev_get_drvdata(dev), output ? true : false); - return count; -} - -static struct device_attribute dcon_device_files[] = { - __ATTR(mode, 0444, dcon_mode_show, NULL), - __ATTR(sleep, 0644, dcon_sleep_show, dcon_sleep_store), - __ATTR(freeze, 0644, dcon_freeze_show, dcon_freeze_store), - __ATTR(monochrome, 0644, dcon_mono_show, dcon_mono_store), - __ATTR(resumeline, 0644, dcon_resumeline_show, dcon_resumeline_store), -}; - -static int dcon_bl_update(struct backlight_device *dev) -{ - struct dcon_priv *dcon = bl_get_data(dev); - u8 level = backlight_get_brightness(dev) & 0x0F; - - if (level != dcon->bl_val) - dcon_set_backlight(dcon, level); - - /* power down the DCON when the screen is blanked */ - if (!dcon->ignore_fb_events) - dcon_sleep(dcon, !!(dev->props.state & BL_CORE_FBBLANK)); - - return 0; -} - -static int dcon_bl_get(struct backlight_device *dev) -{ - struct dcon_priv *dcon = bl_get_data(dev); - - return dcon->bl_val; -} - -static const struct backlight_ops dcon_bl_ops = { - .update_status = dcon_bl_update, - .get_brightness = dcon_bl_get, -}; - -static struct backlight_properties dcon_bl_props = { - .max_brightness = 15, - .type = BACKLIGHT_RAW, - .power = BACKLIGHT_POWER_ON, -}; - -static int dcon_reboot_notify(struct notifier_block *nb, - unsigned long foo, void *bar) -{ - struct dcon_priv *dcon = container_of(nb, struct dcon_priv, reboot_nb); - - if (!dcon || !dcon->client) - return NOTIFY_DONE; - - /* Turn off the DCON. Entirely. */ - dcon_write(dcon, DCON_REG_MODE, 0x39); - dcon_write(dcon, DCON_REG_MODE, 0x32); - return NOTIFY_DONE; -} - -static int unfreeze_on_panic(struct notifier_block *nb, - unsigned long e, void *p) -{ - pdata->set_dconload(1); - return NOTIFY_DONE; -} - -static struct notifier_block dcon_panic_nb = { - .notifier_call = unfreeze_on_panic, -}; - -static int dcon_detect(struct i2c_client *client, struct i2c_board_info *info) -{ - strscpy(info->type, "olpc_dcon", I2C_NAME_SIZE); - - return 0; -} - -static int dcon_probe(struct i2c_client *client) -{ - struct dcon_priv *dcon; - int rc, i, j; - - if (!pdata) - return -ENXIO; - - dcon = kzalloc(sizeof(*dcon), GFP_KERNEL); - if (!dcon) - return -ENOMEM; - - dcon->client = client; - init_waitqueue_head(&dcon->waitq); - INIT_WORK(&dcon->switch_source, dcon_source_switch); - dcon->reboot_nb.notifier_call = dcon_reboot_notify; - dcon->reboot_nb.priority = -1; - - i2c_set_clientdata(client, dcon); - - if (num_registered_fb < 1) { - dev_err(&client->dev, "DCON driver requires a registered fb\n"); - rc = -EIO; - goto einit; - } - dcon->fbinfo = registered_fb[0]; - - rc = dcon_hw_init(dcon, 1); - if (rc) - goto einit; - - /* Add the DCON device */ - - dcon_device = platform_device_alloc("dcon", -1); - - if (!dcon_device) { - pr_err("Unable to create the DCON device\n"); - rc = -ENOMEM; - goto eirq; - } - rc = platform_device_add(dcon_device); - platform_set_drvdata(dcon_device, dcon); - - if (rc) { - pr_err("Unable to add the DCON device\n"); - goto edev; - } - - for (i = 0; i < ARRAY_SIZE(dcon_device_files); i++) { - rc = device_create_file(&dcon_device->dev, - &dcon_device_files[i]); - if (rc) { - dev_err(&dcon_device->dev, "Cannot create sysfs file\n"); - goto ecreate; - } - } - - dcon->bl_val = dcon_read(dcon, DCON_REG_BRIGHT) & 0x0F; - - /* Add the backlight device for the DCON */ - dcon_bl_props.brightness = dcon->bl_val; - dcon->bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev, - dcon, &dcon_bl_ops, - &dcon_bl_props); - if (IS_ERR(dcon->bl_dev)) { - dev_err(&client->dev, "cannot register backlight dev (%ld)\n", - PTR_ERR(dcon->bl_dev)); - dcon->bl_dev = NULL; - } - - register_reboot_notifier(&dcon->reboot_nb); - atomic_notifier_chain_register(&panic_notifier_list, &dcon_panic_nb); - - return 0; - - ecreate: - for (j = 0; j < i; j++) - device_remove_file(&dcon_device->dev, &dcon_device_files[j]); - platform_device_del(dcon_device); - edev: - platform_device_put(dcon_device); - dcon_device = NULL; - eirq: - free_irq(DCON_IRQ, dcon); - einit: - kfree(dcon); - return rc; -} - -static void dcon_remove(struct i2c_client *client) -{ - struct dcon_priv *dcon = i2c_get_clientdata(client); - - unregister_reboot_notifier(&dcon->reboot_nb); - atomic_notifier_chain_unregister(&panic_notifier_list, &dcon_panic_nb); - - free_irq(DCON_IRQ, dcon); - - backlight_device_unregister(dcon->bl_dev); - - if (dcon_device) - platform_device_unregister(dcon_device); - cancel_work_sync(&dcon->switch_source); - - kfree(dcon); -} - -#ifdef CONFIG_PM -static int dcon_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct dcon_priv *dcon = i2c_get_clientdata(client); - - if (!dcon->asleep) { - /* Set up the DCON to have the source */ - dcon_set_source_sync(dcon, DCON_SOURCE_DCON); - } - - return 0; -} - -static int dcon_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct dcon_priv *dcon = i2c_get_clientdata(client); - - if (!dcon->asleep) { - dcon_bus_stabilize(dcon, 0); - dcon_set_source(dcon, DCON_SOURCE_CPU); - } - - return 0; -} - -#else - -#define dcon_suspend NULL -#define dcon_resume NULL - -#endif /* CONFIG_PM */ - -irqreturn_t dcon_interrupt(int irq, void *id) -{ - struct dcon_priv *dcon = id; - u8 status; - - if (pdata->read_status(&status)) - return IRQ_NONE; - - switch (status & 3) { - case 3: - pr_debug("DCONLOAD_MISSED interrupt\n"); - break; - - case 2: /* switch to DCON mode */ - case 1: /* switch to CPU mode */ - dcon->switched = true; - dcon->irq_time = ktime_get(); - wake_up(&dcon->waitq); - break; - - case 0: - /* workaround resume case: the DCON (on 1.5) doesn't - * ever assert status 0x01 when switching to CPU mode - * during resume. this is because DCONLOAD is de-asserted - * _immediately_ upon exiting S3, so the actual release - * of the DCON happened long before this point. - * see http://dev.laptop.org/ticket/9869 - */ - if (dcon->curr_src != dcon->pending_src && !dcon->switched) { - dcon->switched = true; - dcon->irq_time = ktime_get(); - wake_up(&dcon->waitq); - pr_debug("switching w/ status 0/0\n"); - } else { - pr_debug("scanline interrupt w/CPU\n"); - } - } - - return IRQ_HANDLED; -} - -static const struct dev_pm_ops dcon_pm_ops = { - .suspend = dcon_suspend, - .resume = dcon_resume, -}; - -static const struct i2c_device_id dcon_idtable[] = { - { "olpc_dcon", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, dcon_idtable); - -static struct i2c_driver dcon_driver = { - .driver = { - .name = "olpc_dcon", - .pm = &dcon_pm_ops, - }, - .class = I2C_CLASS_HWMON, - .id_table = dcon_idtable, - .probe = dcon_probe, - .remove = dcon_remove, - .detect = dcon_detect, - .address_list = normal_i2c, -}; - -static int __init olpc_dcon_init(void) -{ - /* XO-1.5 */ - if (olpc_board_at_least(olpc_board(0xd0))) - pdata = &dcon_pdata_xo_1_5; - else - pdata = &dcon_pdata_xo_1; - - return i2c_add_driver(&dcon_driver); -} - -static void __exit olpc_dcon_exit(void) -{ - i2c_del_driver(&dcon_driver); -} - -module_init(olpc_dcon_init); -module_exit(olpc_dcon_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/olpc_dcon/olpc_dcon.h b/drivers/staging/olpc_dcon/olpc_dcon.h deleted file mode 100644 index 41bd1360b56e4..0000000000000 --- a/drivers/staging/olpc_dcon/olpc_dcon.h +++ /dev/null @@ -1,112 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef OLPC_DCON_H_ -#define OLPC_DCON_H_ - -#include -#include - -/* DCON registers */ - -#define DCON_REG_ID 0 -#define DCON_REG_MODE 1 - -#define MODE_PASSTHRU BIT(0) -#define MODE_SLEEP BIT(1) -#define MODE_SLEEP_AUTO BIT(2) -#define MODE_BL_ENABLE BIT(3) -#define MODE_BLANK BIT(4) -#define MODE_CSWIZZLE BIT(5) -#define MODE_COL_AA BIT(6) -#define MODE_MONO_LUMA BIT(7) -#define MODE_SCAN_INT BIT(8) -#define MODE_CLOCKDIV BIT(9) -#define MODE_DEBUG BIT(14) -#define MODE_SELFTEST BIT(15) - -#define DCON_REG_HRES 0x2 -#define DCON_REG_HTOTAL 0x3 -#define DCON_REG_HSYNC_WIDTH 0x4 -#define DCON_REG_VRES 0x5 -#define DCON_REG_VTOTAL 0x6 -#define DCON_REG_VSYNC_WIDTH 0x7 -#define DCON_REG_TIMEOUT 0x8 -#define DCON_REG_SCAN_INT 0x9 -#define DCON_REG_BRIGHT 0xa -#define DCON_REG_MEM_OPT_A 0x41 -#define DCON_REG_MEM_OPT_B 0x42 - -/* Load Delay Locked Loop (DLL) settings for clock delay */ -#define MEM_DLL_CLOCK_DELAY BIT(0) -/* Memory controller power down function */ -#define MEM_POWER_DOWN BIT(8) -/* Memory controller software reset */ -#define MEM_SOFT_RESET BIT(0) - -/* Status values */ - -#define DCONSTAT_SCANINT 0 -#define DCONSTAT_SCANINT_DCON 1 -#define DCONSTAT_DISPLAYLOAD 2 -#define DCONSTAT_MISSED 3 - -/* Source values */ - -#define DCON_SOURCE_DCON 0 -#define DCON_SOURCE_CPU 1 - -/* Interrupt */ -#define DCON_IRQ 6 - -struct dcon_priv { - struct i2c_client *client; - struct fb_info *fbinfo; - struct backlight_device *bl_dev; - - wait_queue_head_t waitq; - struct work_struct switch_source; - struct notifier_block reboot_nb; - - /* Shadow register for the DCON_REG_MODE register */ - u8 disp_mode; - - /* The current backlight value - this saves us some smbus traffic */ - u8 bl_val; - - /* Current source, initialized at probe time */ - int curr_src; - - /* Desired source */ - int pending_src; - - /* Variables used during switches */ - bool switched; - ktime_t irq_time; - ktime_t load_time; - - /* Current output type; true == mono, false == color */ - bool mono; - bool asleep; - /* This get set while controlling fb blank state from the driver */ - bool ignore_fb_events; -}; - -struct dcon_platform_data { - int (*init)(struct dcon_priv *dcon); - void (*bus_stabilize_wiggle)(void); - void (*set_dconload)(int load); - int (*read_status)(u8 *status); -}; - -struct dcon_gpio { - const char *name; - unsigned long flags; -}; - -#include - -irqreturn_t dcon_interrupt(int irq, void *id); - -extern struct dcon_platform_data dcon_pdata_xo_1; -extern struct dcon_platform_data dcon_pdata_xo_1_5; - -#endif diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c deleted file mode 100644 index 02c059897784d..0000000000000 --- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Mainly by David Woodhouse, somewhat modified by Jordan Crouse - * - * Copyright © 2006-2007 Red Hat, Inc. - * Copyright © 2006-2007 Advanced Micro Devices, Inc. - * Copyright © 2009 VIA Technology, Inc. - * Copyright (c) 2010 Andres Salomon - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -#include "olpc_dcon.h" - -enum dcon_gpios { - OLPC_DCON_STAT0, - OLPC_DCON_STAT1, - OLPC_DCON_IRQ, - OLPC_DCON_LOAD, - OLPC_DCON_BLANK, -}; - -static const struct dcon_gpio gpios_asis[] = { - [OLPC_DCON_STAT0] = { .name = "dcon_stat0", .flags = GPIOD_ASIS }, - [OLPC_DCON_STAT1] = { .name = "dcon_stat1", .flags = GPIOD_ASIS }, - [OLPC_DCON_IRQ] = { .name = "dcon_irq", .flags = GPIOD_ASIS }, - [OLPC_DCON_LOAD] = { .name = "dcon_load", .flags = GPIOD_ASIS }, - [OLPC_DCON_BLANK] = { .name = "dcon_blank", .flags = GPIOD_ASIS }, -}; - -static struct gpio_desc *gpios[5]; - -static int dcon_init_xo_1(struct dcon_priv *dcon) -{ - unsigned char lob; - int ret, i; - const struct dcon_gpio *pin = &gpios_asis[0]; - - for (i = 0; i < ARRAY_SIZE(gpios_asis); i++) { - gpios[i] = devm_gpiod_get(&dcon->client->dev, pin[i].name, - pin[i].flags); - if (IS_ERR(gpios[i])) { - ret = PTR_ERR(gpios[i]); - pr_err("failed to request %s GPIO: %d\n", pin[i].name, - ret); - return ret; - } - } - - /* Turn off the event enable for GPIO7 just to be safe */ - cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE); - - /* - * Determine the current state by reading the GPIO bit; earlier - * stages of the boot process have established the state. - * - * Note that we read GPIO_OUTPUT_VAL rather than GPIO_READ_BACK here; - * this is because OFW will disable input for the pin and set a value.. - * READ_BACK will only contain a valid value if input is enabled and - * then a value is set. So, future readings of the pin can use - * READ_BACK, but the first one cannot. Awesome, huh? - */ - dcon->curr_src = cs5535_gpio_isset(OLPC_GPIO_DCON_LOAD, GPIO_OUTPUT_VAL) - ? DCON_SOURCE_CPU - : DCON_SOURCE_DCON; - dcon->pending_src = dcon->curr_src; - - /* Set the directions for the GPIO pins */ - gpiod_direction_input(gpios[OLPC_DCON_STAT0]); - gpiod_direction_input(gpios[OLPC_DCON_STAT1]); - gpiod_direction_input(gpios[OLPC_DCON_IRQ]); - gpiod_direction_input(gpios[OLPC_DCON_BLANK]); - gpiod_direction_output(gpios[OLPC_DCON_LOAD], - dcon->curr_src == DCON_SOURCE_CPU); - - /* Set up the interrupt mappings */ - - /* Set the IRQ to pair 2 */ - cs5535_gpio_setup_event(OLPC_GPIO_DCON_IRQ, 2, 0); - - /* Enable group 2 to trigger the DCON interrupt */ - cs5535_gpio_set_irq(2, DCON_IRQ); - - /* Select edge level for interrupt (in PIC) */ - lob = inb(0x4d0); - lob &= ~(1 << DCON_IRQ); - outb(lob, 0x4d0); - - /* Register the interrupt handler */ - if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", dcon)) { - pr_err("failed to request DCON's irq\n"); - return -EIO; - } - - /* Clear INV_EN for GPIO7 (DCONIRQ) */ - cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_INVERT); - - /* Enable filter for GPIO12 (DCONBLANK) */ - cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_FILTER); - - /* Disable filter for GPIO7 */ - cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_FILTER); - - /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ - cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_EVENT_COUNT); - cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_EVENT_COUNT); - - /* Add GPIO12 to the Filter Event Pair #7 */ - cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_FE7_SEL); - - /* Turn off negative Edge Enable for GPIO12 */ - cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_EN); - - /* Enable negative Edge Enable for GPIO7 */ - cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_EN); - - /* Zero the filter amount for Filter Event Pair #7 */ - cs5535_gpio_set(0, GPIO_FLTR7_AMOUNT); - - /* Clear the negative edge status for GPIO7 and GPIO12 */ - cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS); - cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_STS); - - /* FIXME: Clear the positive status as well, just to be sure */ - cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_POSITIVE_EDGE_STS); - cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_POSITIVE_EDGE_STS); - - /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */ - cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE); - cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_EVENTS_ENABLE); - - return 0; -} - -static void dcon_wiggle_xo_1(void) -{ - int x; - - /* - * According to HiMax, when powering the DCON up we should hold - * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON - * state machine to reset to a (sane) initial state. Mitch Bradley - * did some testing and discovered that holding for 16 SMB_CLK cycles - * worked a lot more reliably, so that's what we do here. - * - * According to the cs5536 spec, to set GPIO14 to SMB_CLK we must - * simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and - * GPIO15. - */ - cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); - cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL); - cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_ENABLE); - cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE); - cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1); - cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1); - cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX2); - cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2); - cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1); - cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1); - - for (x = 0; x < 16; x++) { - udelay(5); - cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); - udelay(5); - cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL); - } - udelay(5); - cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1); - cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1); - cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1); - cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1); -} - -static void dcon_set_dconload_1(int val) -{ - gpiod_set_value(gpios[OLPC_DCON_LOAD], val); -} - -static int dcon_read_status_xo_1(u8 *status) -{ - *status = gpiod_get_value(gpios[OLPC_DCON_STAT0]); - *status |= gpiod_get_value(gpios[OLPC_DCON_STAT1]) << 1; - - /* Clear the negative edge status for GPIO7 */ - cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS); - - return 0; -} - -struct dcon_platform_data dcon_pdata_xo_1 = { - .init = dcon_init_xo_1, - .bus_stabilize_wiggle = dcon_wiggle_xo_1, - .set_dconload = dcon_set_dconload_1, - .read_status = dcon_read_status_xo_1, -}; diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c deleted file mode 100644 index 52cdcd2a89d6f..0000000000000 --- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2009,2010 One Laptop per Child - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -/* TODO: this eventually belongs in linux/vx855.h */ -#define NR_VX855_GPI 14 -#define NR_VX855_GPO 13 -#define NR_VX855_GPIO 15 - -#define VX855_GPI(n) (n) -#define VX855_GPO(n) (NR_VX855_GPI + (n)) -#define VX855_GPIO(n) (NR_VX855_GPI + NR_VX855_GPO + (n)) - -#include "olpc_dcon.h" - -/* Hardware setup on the XO 1.5: - * DCONLOAD connects to VX855_GPIO1 (not SMBCK2) - * DCONBLANK connects to VX855_GPIO8 (not SSPICLK) unused in driver - * DCONSTAT0 connects to VX855_GPI10 (not SSPISDI) - * DCONSTAT1 connects to VX855_GPI11 (not nSSPISS) - * DCONIRQ connects to VX855_GPIO12 - * DCONSMBDATA connects to VX855 graphics CRTSPD - * DCONSMBCLK connects to VX855 graphics CRTSPCLK - */ - -#define VX855_GENL_PURPOSE_OUTPUT 0x44c /* PMIO_Rx4c-4f */ -#define VX855_GPI_STATUS_CHG 0x450 /* PMIO_Rx50 */ -#define VX855_GPI_SCI_SMI 0x452 /* PMIO_Rx52 */ -#define BIT_GPIO12 0x40 - -#define PREFIX "OLPC DCON:" - -enum dcon_gpios { - OLPC_DCON_STAT0, - OLPC_DCON_STAT1, - OLPC_DCON_LOAD, -}; - -struct gpiod_lookup_table gpios_table = { - .dev_id = NULL, - .table = { - GPIO_LOOKUP("VX855 South Bridge", VX855_GPIO(1), "dcon_load", - GPIO_ACTIVE_LOW), - GPIO_LOOKUP("VX855 South Bridge", VX855_GPI(10), "dcon_stat0", - GPIO_ACTIVE_LOW), - GPIO_LOOKUP("VX855 South Bridge", VX855_GPI(11), "dcon_stat1", - GPIO_ACTIVE_LOW), - { }, - }, -}; - -static const struct dcon_gpio gpios_asis[] = { - [OLPC_DCON_STAT0] = { .name = "dcon_stat0", .flags = GPIOD_ASIS }, - [OLPC_DCON_STAT1] = { .name = "dcon_stat1", .flags = GPIOD_ASIS }, - [OLPC_DCON_LOAD] = { .name = "dcon_load", .flags = GPIOD_ASIS }, -}; - -static struct gpio_desc *gpios[3]; - -static void dcon_clear_irq(void) -{ - /* irq status will appear in PMIO_Rx50[6] (RW1C) on gpio12 */ - outb(BIT_GPIO12, VX855_GPI_STATUS_CHG); -} - -static int dcon_was_irq(void) -{ - u8 tmp; - - /* irq status will appear in PMIO_Rx50[6] on gpio12 */ - tmp = inb(VX855_GPI_STATUS_CHG); - - return !!(tmp & BIT_GPIO12); -} - -static int dcon_init_xo_1_5(struct dcon_priv *dcon) -{ - unsigned int irq; - const struct dcon_gpio *pin = &gpios_asis[0]; - int i; - int ret; - - /* Add GPIO look up table */ - gpios_table.dev_id = dev_name(&dcon->client->dev); - gpiod_add_lookup_table(&gpios_table); - - /* Get GPIO descriptor */ - for (i = 0; i < ARRAY_SIZE(gpios_asis); i++) { - gpios[i] = devm_gpiod_get(&dcon->client->dev, pin[i].name, - pin[i].flags); - if (IS_ERR(gpios[i])) { - ret = PTR_ERR(gpios[i]); - pr_err("failed to request %s GPIO: %d\n", pin[i].name, - ret); - return ret; - } - } - - dcon_clear_irq(); - - /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */ - outb(inb(VX855_GPI_SCI_SMI) | BIT_GPIO12, VX855_GPI_SCI_SMI); - - /* Determine the current state of DCONLOAD, likely set by firmware */ - /* GPIO1 */ - dcon->curr_src = (inl(VX855_GENL_PURPOSE_OUTPUT) & 0x1000) ? - DCON_SOURCE_CPU : DCON_SOURCE_DCON; - dcon->pending_src = dcon->curr_src; - - /* we're sharing the IRQ with ACPI */ - irq = acpi_gbl_FADT.sci_interrupt; - if (request_irq(irq, &dcon_interrupt, IRQF_SHARED, "DCON", dcon)) { - pr_err("DCON (IRQ%d) allocation failed\n", irq); - return 1; - } - - return 0; -} - -static void set_i2c_line(int sda, int scl) -{ - unsigned char tmp; - unsigned int port = 0x26; - - /* FIXME: This directly accesses the CRT GPIO controller !!! */ - outb(port, 0x3c4); - tmp = inb(0x3c5); - - if (scl) - tmp |= 0x20; - else - tmp &= ~0x20; - - if (sda) - tmp |= 0x10; - else - tmp &= ~0x10; - - tmp |= 0x01; - - outb(port, 0x3c4); - outb(tmp, 0x3c5); -} - -static void dcon_wiggle_xo_1_5(void) -{ - int x; - - /* - * According to HiMax, when powering the DCON up we should hold - * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON - * state machine to reset to a (sane) initial state. Mitch Bradley - * did some testing and discovered that holding for 16 SMB_CLK cycles - * worked a lot more reliably, so that's what we do here. - */ - set_i2c_line(1, 1); - - for (x = 0; x < 16; x++) { - udelay(5); - set_i2c_line(1, 0); - udelay(5); - set_i2c_line(1, 1); - } - udelay(5); - - /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */ - outb(inb(VX855_GPI_SCI_SMI) | BIT_GPIO12, VX855_GPI_SCI_SMI); -} - -static void dcon_set_dconload_xo_1_5(int val) -{ - gpiod_set_value(gpios[OLPC_DCON_LOAD], val); -} - -static int dcon_read_status_xo_1_5(u8 *status) -{ - if (!dcon_was_irq()) - return -1; - - /* i believe this is the same as "inb(0x44b) & 3" */ - *status = gpiod_get_value(gpios[OLPC_DCON_STAT0]); - *status |= gpiod_get_value(gpios[OLPC_DCON_STAT1]) << 1; - - dcon_clear_irq(); - - return 0; -} - -struct dcon_platform_data dcon_pdata_xo_1_5 = { - .init = dcon_init_xo_1_5, - .bus_stabilize_wiggle = dcon_wiggle_xo_1_5, - .set_dconload = dcon_set_dconload_xo_1_5, - .read_status = dcon_read_status_xo_1_5, -}; diff --git a/drivers/staging/rtl8712/Kconfig b/drivers/staging/rtl8712/Kconfig deleted file mode 100644 index 8de26425225b3..0000000000000 --- a/drivers/staging/rtl8712/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config R8712U - tristate "RealTek RTL8712U (RTL8192SU) Wireless LAN NIC driver" - depends on WLAN && USB && CFG80211 - select WIRELESS_EXT - select WEXT_PRIV - select FW_LOADER - help - This option adds the Realtek RTL8712 USB device such as the - D-Link DWA-130. - - If built as a module, it will be called r8712u. - -config R8712_TX_AGGR - bool "Realtek RTL8712U Transmit Aggregation code" - depends on R8712U && BROKEN - help - This option provides transmit aggregation for the Realtek - RTL8712 USB device. - - diff --git a/drivers/staging/rtl8712/Makefile b/drivers/staging/rtl8712/Makefile deleted file mode 100644 index 3ae216b6621b1..0000000000000 --- a/drivers/staging/rtl8712/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -r8712u-y := \ - rtl871x_cmd.o \ - rtl8712_cmd.o \ - rtl871x_security.o \ - rtl871x_eeprom.o \ - rtl8712_efuse.o \ - hal_init.o \ - usb_halinit.o \ - usb_ops.o \ - usb_ops_linux.o \ - rtl871x_io.o \ - rtl8712_io.o \ - rtl871x_ioctl_linux.o \ - rtl871x_ioctl_rtl.o \ - rtl871x_ioctl_set.o \ - rtl8712_led.o \ - rtl871x_mlme.o \ - ieee80211.o \ - rtl871x_mp_ioctl.o \ - rtl871x_mp.o \ - mlme_linux.o \ - recv_linux.o \ - xmit_linux.o \ - usb_intf.o \ - os_intfs.o \ - rtl871x_pwrctrl.o \ - rtl8712_recv.o \ - rtl871x_recv.o \ - rtl871x_sta_mgt.o \ - rtl871x_xmit.o \ - rtl8712_xmit.o - -obj-$(CONFIG_R8712U) := r8712u.o - diff --git a/drivers/staging/rtl8712/TODO b/drivers/staging/rtl8712/TODO deleted file mode 100644 index 5aed36efa7cbf..0000000000000 --- a/drivers/staging/rtl8712/TODO +++ /dev/null @@ -1,12 +0,0 @@ -TODO: -- merge Realtek's bugfixes and new features into the driver -- switch to use MAC80211 -- checkpatch.pl fixes - only a few remain - -A replacement for this driver with MAC80211 support is available -at https://github.com/chunkeey/rtl8192su - -Please send any patches to Greg Kroah-Hartman , -Larry Finger , -Florian Schilhabel and -Linux Driver Project Developer List . diff --git a/drivers/staging/rtl8712/basic_types.h b/drivers/staging/rtl8712/basic_types.h deleted file mode 100644 index aecded87dd4c8..0000000000000 --- a/drivers/staging/rtl8712/basic_types.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __BASIC_TYPES_H__ -#define __BASIC_TYPES_H__ - -#include - -#define sint signed int - -/* Should we extend this to be host_addr_t and target_addr_t for case: - * host : x86_64 - * target : mips64 - */ -#define addr_t unsigned long - -#endif /*__BASIC_TYPES_H__*/ - diff --git a/drivers/staging/rtl8712/drv_types.h b/drivers/staging/rtl8712/drv_types.h deleted file mode 100644 index 76ac798642bd2..0000000000000 --- a/drivers/staging/rtl8712/drv_types.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -/* --------------------------------------------------------------------- - * - * For type defines and data structure defines - * - * --------------------------------------------------------------------- - */ -#ifndef __DRV_TYPES_H__ -#define __DRV_TYPES_H__ - -struct _adapter; - -#include "osdep_service.h" -#include "wlan_bssdef.h" -#include "rtl8712_spec.h" -#include "rtl8712_hal.h" -#include -#include - -enum _NIC_VERSION { - RTL8711_NIC, - RTL8712_NIC, - RTL8713_NIC, - RTL8716_NIC -}; - -struct qos_priv { - /* bit mask option: u-apsd, s-apsd, ts, block ack... */ - unsigned int qos_option; -}; - -#include "rtl871x_ht.h" -#include "rtl871x_cmd.h" -#include "rtl871x_xmit.h" -#include "rtl871x_recv.h" -#include "rtl871x_security.h" -#include "rtl871x_pwrctrl.h" -#include "rtl871x_io.h" -#include "rtl871x_eeprom.h" -#include "sta_info.h" -#include "rtl871x_mlme.h" -#include "rtl871x_mp.h" -#include "rtl871x_debug.h" -#include "rtl871x_rf.h" -#include "rtl871x_event.h" -#include "rtl871x_led.h" - -#define SPEC_DEV_ID_DISABLE_HT BIT(1) - -struct specific_device_id { - u32 flags; - u16 idVendor; - u16 idProduct; - -}; - -struct registry_priv { - u8 chip_version; - u8 rfintfs; - u8 lbkmode; - u8 hci; - u8 network_mode; /*infra, ad-hoc, auto*/ - struct ndis_802_11_ssid ssid; - u8 channel;/* ad-hoc support requirement */ - u8 wireless_mode;/* A, B, G, auto */ - u8 vrtl_carrier_sense; /*Enable, Disable, Auto*/ - u8 vcs_type;/*RTS/CTS, CTS-to-self*/ - u16 rts_thresh; - u16 frag_thresh; - u8 preamble;/*long, short, auto*/ - u8 scan_mode;/*active, passive*/ - u8 adhoc_tx_pwr; - u8 soft_ap; - u8 smart_ps; - u8 power_mgnt; - u8 radio_enable; - u8 long_retry_lmt; - u8 short_retry_lmt; - u16 busy_thresh; - u8 ack_policy; - u8 mp_mode; - u8 software_encrypt; - u8 software_decrypt; - /* UAPSD */ - u8 wmm_enable; - u8 uapsd_enable; - u8 uapsd_max_sp; - u8 uapsd_acbk_en; - u8 uapsd_acbe_en; - u8 uapsd_acvi_en; - u8 uapsd_acvo_en; - - struct wlan_bssid_ex dev_network; - - u8 ht_enable; - u8 cbw40_enable; - u8 ampdu_enable;/*for tx*/ - u8 rf_config; - u8 low_power; - u8 wifi_test; -}; - -struct dvobj_priv { - struct _adapter *padapter; - u32 nr_endpoint; - u8 ishighspeed; - uint (*inirp_init)(struct _adapter *adapter); - uint (*inirp_deinit)(struct _adapter *adapter); - struct usb_device *pusbdev; -}; - -/** - * struct _adapter - the main adapter structure for this device. - * - * bup: True indicates that the interface is up. - */ -struct _adapter { - struct dvobj_priv dvobjpriv; - struct mlme_priv mlmepriv; - struct cmd_priv cmdpriv; - struct evt_priv evtpriv; - struct io_queue *pio_queue; - struct xmit_priv xmitpriv; - struct recv_priv recvpriv; - struct sta_priv stapriv; - struct security_priv securitypriv; - struct registry_priv registrypriv; - struct wlan_acl_pool acl_list; - struct pwrctrl_priv pwrctrlpriv; - struct eeprom_priv eeprompriv; - struct hal_priv halpriv; - struct led_priv ledpriv; - struct mp_priv mppriv; - bool driver_stopped; - bool surprise_removed; - bool suspended; - u8 eeprom_address_size; - u8 hw_init_completed; - struct task_struct *cmd_thread; - uint (*dvobj_init)(struct _adapter *adapter); - void (*dvobj_deinit)(struct _adapter *adapter); - struct net_device *pnetdev; - int bup; - struct net_device_stats stats; - struct iw_statistics iwstats; - int pid; /*process id from UI*/ - struct work_struct wk_filter_rx_ff0; - const struct firmware *fw; - struct usb_interface *pusb_intf; - struct mutex mutex_start; - struct completion rtl8712_fw_ready; - struct completion rx_filter_ready; -}; - -static inline u8 *myid(struct eeprom_priv *peepriv) -{ - return peepriv->mac_addr; -} - -u8 r8712_usb_hal_bus_init(struct _adapter *adapter); - -#endif /*__DRV_TYPES_H__*/ - diff --git a/drivers/staging/rtl8712/ethernet.h b/drivers/staging/rtl8712/ethernet.h deleted file mode 100644 index 4b9b8a97a0bc6..0000000000000 --- a/drivers/staging/rtl8712/ethernet.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __INC_ETHERNET_H -#define __INC_ETHERNET_H - -#define ETHERNET_HEADER_SIZE 14 /*!< Ethernet Header Length*/ -#define LLC_HEADER_SIZE 6 /*!< LLC Header Length*/ - -#endif /* #ifndef __INC_ETHERNET_H */ - diff --git a/drivers/staging/rtl8712/hal_init.c b/drivers/staging/rtl8712/hal_init.c deleted file mode 100644 index 1148075f0cd64..0000000000000 --- a/drivers/staging/rtl8712/hal_init.c +++ /dev/null @@ -1,401 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _HAL_INIT_C_ - -#include -#include -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "usb_osintf.h" - -#define FWBUFF_ALIGN_SZ 512 -#define MAX_DUMP_FWSZ (48 * 1024) - -static void rtl871x_load_fw_fail(struct _adapter *adapter) -{ - struct usb_device *udev = adapter->dvobjpriv.pusbdev; - struct device *dev = &udev->dev; - struct device *parent = dev->parent; - - complete(&adapter->rtl8712_fw_ready); - - dev_err(&udev->dev, "r8712u: Firmware request failed\n"); - - if (parent) - device_lock(parent); - - device_release_driver(dev); - - if (parent) - device_unlock(parent); -} - -static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context) -{ - struct _adapter *adapter = context; - - if (!firmware) { - rtl871x_load_fw_fail(adapter); - return; - } - adapter->fw = firmware; - /* firmware available - start netdev */ - register_netdev(adapter->pnetdev); - complete(&adapter->rtl8712_fw_ready); -} - -static const char firmware_file[] = "rtlwifi/rtl8712u.bin"; - -int rtl871x_load_fw(struct _adapter *padapter) -{ - struct device *dev = &padapter->dvobjpriv.pusbdev->dev; - int rc; - - init_completion(&padapter->rtl8712_fw_ready); - dev_info(dev, "r8712u: Loading firmware from \"%s\"\n", firmware_file); - rc = request_firmware_nowait(THIS_MODULE, 1, firmware_file, dev, - GFP_KERNEL, padapter, rtl871x_load_fw_cb); - if (rc) - dev_err(dev, "r8712u: Firmware request error %d\n", rc); - return rc; -} -MODULE_FIRMWARE("rtlwifi/rtl8712u.bin"); - -static u32 rtl871x_open_fw(struct _adapter *adapter, const u8 **mappedfw) -{ - if (adapter->fw->size > 200000) { - dev_err(&adapter->pnetdev->dev, "r8712u: Bad fw->size of %zu\n", - adapter->fw->size); - return 0; - } - *mappedfw = adapter->fw->data; - return adapter->fw->size; -} - -static void fill_fwpriv(struct _adapter *adapter, struct fw_priv *fwpriv) -{ - struct dvobj_priv *dvobj = &adapter->dvobjpriv; - struct registry_priv *regpriv = &adapter->registrypriv; - - memset(fwpriv, 0, sizeof(struct fw_priv)); - /* todo: check if needs endian conversion */ - fwpriv->hci_sel = RTL8712_HCI_TYPE_72USB; - fwpriv->usb_ep_num = (u8)dvobj->nr_endpoint; - fwpriv->bw_40MHz_en = regpriv->cbw40_enable; - switch (regpriv->rf_config) { - case RTL8712_RF_1T1R: - fwpriv->rf_config = RTL8712_RFC_1T1R; - break; - case RTL8712_RF_2T2R: - fwpriv->rf_config = RTL8712_RFC_2T2R; - break; - case RTL8712_RF_1T2R: - default: - fwpriv->rf_config = RTL8712_RFC_1T2R; - } - fwpriv->mp_mode = (regpriv->mp_mode == 1); - /* 0:off 1:on 2:auto */ - fwpriv->vcs_type = regpriv->vrtl_carrier_sense; - fwpriv->vcs_mode = regpriv->vcs_type; /* 1:RTS/CTS 2:CTS to self */ - /* default enable turbo_mode */ - fwpriv->turbo_mode = (regpriv->wifi_test != 1); - fwpriv->low_power_mode = regpriv->low_power; -} - -static void update_fwhdr(struct fw_hdr *pfwhdr, const u8 *pmappedfw) -{ - pfwhdr->signature = le16_to_cpu(*(__le16 *)pmappedfw); - pfwhdr->version = le16_to_cpu(*(__le16 *)(pmappedfw + 2)); - /* define the size of boot loader */ - pfwhdr->dmem_size = le32_to_cpu(*(__le32 *)(pmappedfw + 4)); - /* define the size of FW in IMEM */ - pfwhdr->img_IMEM_size = le32_to_cpu(*(__le32 *)(pmappedfw + 8)); - /* define the size of FW in SRAM */ - pfwhdr->img_SRAM_size = le32_to_cpu(*(__le32 *)(pmappedfw + 12)); - /* define the size of DMEM variable */ - pfwhdr->fw_priv_sz = le32_to_cpu(*(__le32 *)(pmappedfw + 16)); -} - -static u8 chk_fwhdr(struct fw_hdr *pfwhdr, u32 ulfilelength) -{ - u32 fwhdrsz, fw_sz; - - /* check signature */ - if ((pfwhdr->signature != 0x8712) && (pfwhdr->signature != 0x8192)) - return _FAIL; - /* check fw_priv_sze & sizeof(struct fw_priv) */ - if (pfwhdr->fw_priv_sz != sizeof(struct fw_priv)) - return _FAIL; - /* check fw_sz & image_fw_sz */ - fwhdrsz = offsetof(struct fw_hdr, fwpriv) + pfwhdr->fw_priv_sz; - fw_sz = fwhdrsz + pfwhdr->img_IMEM_size + pfwhdr->img_SRAM_size + - pfwhdr->dmem_size; - if (fw_sz != ulfilelength) - return _FAIL; - return _SUCCESS; -} - -static u8 rtl8712_dl_fw(struct _adapter *adapter) -{ - sint i; - u8 tmp8, tmp8_a; - u16 tmp16; - u32 maxlen = 0; /* for compare usage */ - uint dump_imem_sz, imem_sz, dump_emem_sz, emem_sz; /* max = 49152; */ - struct fw_hdr fwhdr; - u32 ulfilelength; /* FW file size */ - const u8 *mappedfw = NULL; - u8 *tmpchar = NULL, *payload, *ptr; - struct tx_desc *txdesc; - u32 txdscp_sz = sizeof(struct tx_desc); - u8 ret = _FAIL; - - ulfilelength = rtl871x_open_fw(adapter, &mappedfw); - if (mappedfw && (ulfilelength > 0)) { - update_fwhdr(&fwhdr, mappedfw); - if (chk_fwhdr(&fwhdr, ulfilelength) == _FAIL) - return ret; - fill_fwpriv(adapter, &fwhdr.fwpriv); - /* firmware check ok */ - maxlen = (fwhdr.img_IMEM_size > fwhdr.img_SRAM_size) ? - fwhdr.img_IMEM_size : fwhdr.img_SRAM_size; - maxlen += txdscp_sz; - tmpchar = kmalloc(maxlen + FWBUFF_ALIGN_SZ, GFP_KERNEL); - if (!tmpchar) - return ret; - - txdesc = (struct tx_desc *)(tmpchar + FWBUFF_ALIGN_SZ - - ((addr_t)(tmpchar) & (FWBUFF_ALIGN_SZ - 1))); - payload = (u8 *)(txdesc) + txdscp_sz; - ptr = (u8 *)mappedfw + offsetof(struct fw_hdr, fwpriv) + - fwhdr.fw_priv_sz; - /* Download FirmWare */ - /* 1. determine IMEM code size and Load IMEM Code Section */ - imem_sz = fwhdr.img_IMEM_size; - do { - memset(txdesc, 0, TXDESC_SIZE); - if (imem_sz > MAX_DUMP_FWSZ/*49152*/) { - dump_imem_sz = MAX_DUMP_FWSZ; - } else { - dump_imem_sz = imem_sz; - txdesc->txdw0 |= cpu_to_le32(BIT(28)); - } - txdesc->txdw0 |= cpu_to_le32(dump_imem_sz & - 0x0000ffff); - memcpy(payload, ptr, dump_imem_sz); - r8712_write_mem(adapter, RTL8712_DMA_VOQ, - dump_imem_sz + TXDESC_SIZE, - (u8 *)txdesc); - ptr += dump_imem_sz; - imem_sz -= dump_imem_sz; - } while (imem_sz > 0); - i = 10; - tmp16 = r8712_read16(adapter, TCR); - while (((tmp16 & _IMEM_CODE_DONE) == 0) && (i > 0)) { - usleep_range(10, 1000); - tmp16 = r8712_read16(adapter, TCR); - i--; - } - if (i == 0 || (tmp16 & _IMEM_CHK_RPT) == 0) - goto exit_fail; - - /* 2.Download EMEM code size and Load EMEM Code Section */ - emem_sz = fwhdr.img_SRAM_size; - do { - memset(txdesc, 0, TXDESC_SIZE); - if (emem_sz > MAX_DUMP_FWSZ) { /* max=48k */ - dump_emem_sz = MAX_DUMP_FWSZ; - } else { - dump_emem_sz = emem_sz; - txdesc->txdw0 |= cpu_to_le32(BIT(28)); - } - txdesc->txdw0 |= cpu_to_le32(dump_emem_sz & - 0x0000ffff); - memcpy(payload, ptr, dump_emem_sz); - r8712_write_mem(adapter, RTL8712_DMA_VOQ, - dump_emem_sz + TXDESC_SIZE, - (u8 *)txdesc); - ptr += dump_emem_sz; - emem_sz -= dump_emem_sz; - } while (emem_sz > 0); - i = 5; - tmp16 = r8712_read16(adapter, TCR); - while (((tmp16 & _EMEM_CODE_DONE) == 0) && (i > 0)) { - usleep_range(10, 1000); - tmp16 = r8712_read16(adapter, TCR); - i--; - } - if (i == 0 || (tmp16 & _EMEM_CHK_RPT) == 0) - goto exit_fail; - - /* 3.Enable CPU */ - tmp8 = r8712_read8(adapter, SYS_CLKR); - r8712_write8(adapter, SYS_CLKR, tmp8 | BIT(2)); - tmp8_a = r8712_read8(adapter, SYS_CLKR); - if (tmp8_a != (tmp8 | BIT(2))) - goto exit_fail; - - tmp8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, tmp8 | BIT(2)); - tmp8_a = r8712_read8(adapter, SYS_FUNC_EN + 1); - if (tmp8_a != (tmp8 | BIT(2))) - goto exit_fail; - - r8712_read32(adapter, TCR); - - /* 4.polling IMEM Ready */ - i = 100; - tmp16 = r8712_read16(adapter, TCR); - while (((tmp16 & _IMEM_RDY) == 0) && (i > 0)) { - msleep(20); - tmp16 = r8712_read16(adapter, TCR); - i--; - } - if (i == 0) { - r8712_write16(adapter, 0x10250348, 0xc000); - r8712_write16(adapter, 0x10250348, 0xc001); - r8712_write16(adapter, 0x10250348, 0x2000); - r8712_write16(adapter, 0x10250348, 0x2001); - r8712_write16(adapter, 0x10250348, 0x2002); - r8712_write16(adapter, 0x10250348, 0x2003); - goto exit_fail; - } - /* 5.Download DMEM code size and Load EMEM Code Section */ - memset(txdesc, 0, TXDESC_SIZE); - txdesc->txdw0 |= cpu_to_le32(fwhdr.fw_priv_sz & 0x0000ffff); - txdesc->txdw0 |= cpu_to_le32(BIT(28)); - memcpy(payload, &fwhdr.fwpriv, fwhdr.fw_priv_sz); - r8712_write_mem(adapter, RTL8712_DMA_VOQ, - fwhdr.fw_priv_sz + TXDESC_SIZE, (u8 *)txdesc); - - /* polling dmem code done */ - i = 100; - tmp16 = r8712_read16(adapter, TCR); - while (((tmp16 & _DMEM_CODE_DONE) == 0) && (i > 0)) { - msleep(20); - tmp16 = r8712_read16(adapter, TCR); - i--; - } - if (i == 0) - goto exit_fail; - - tmp8 = r8712_read8(adapter, 0x1025000A); - if (tmp8 & BIT(4)) /* When boot from EEPROM, - * & FW need more time to read EEPROM - */ - i = 60; - else /* boot from EFUSE */ - i = 30; - tmp16 = r8712_read16(adapter, TCR); - while (((tmp16 & _FWRDY) == 0) && (i > 0)) { - msleep(100); - tmp16 = r8712_read16(adapter, TCR); - i--; - } - if (i == 0) - goto exit_fail; - } else { - goto exit_fail; - } - ret = _SUCCESS; - -exit_fail: - kfree(tmpchar); - return ret; -} - -uint rtl8712_hal_init(struct _adapter *padapter) -{ - u32 val32; - int i; - - /* r8712 firmware download */ - if (rtl8712_dl_fw(padapter) != _SUCCESS) - return _FAIL; - - netdev_info(padapter->pnetdev, "1 RCR=0x%x\n", - r8712_read32(padapter, RCR)); - val32 = r8712_read32(padapter, RCR); - r8712_write32(padapter, RCR, (val32 | BIT(26))); /* Enable RX TCP - * Checksum offload - */ - netdev_info(padapter->pnetdev, "2 RCR=0x%x\n", - r8712_read32(padapter, RCR)); - val32 = r8712_read32(padapter, RCR); - r8712_write32(padapter, RCR, (val32 | BIT(25))); /* Append PHY status */ - val32 = r8712_read32(padapter, 0x10250040); - r8712_write32(padapter, 0x10250040, (val32 & 0x00FFFFFF)); - /* for usb rx aggregation */ - r8712_write8(padapter, 0x102500B5, r8712_read8(padapter, 0x102500B5) | - BIT(0)); /* page = 128bytes */ - r8712_write8(padapter, 0x102500BD, r8712_read8(padapter, 0x102500BD) | - BIT(7)); /* enable usb rx aggregation */ - r8712_write8(padapter, 0x102500D9, 1); /* TH=1 => means that invalidate - * usb rx aggregation - */ - r8712_write8(padapter, 0x1025FE5B, 0x04); /* 1.7ms/4 */ - /* Fix the RX FIFO issue(USB error) */ - r8712_write8(padapter, 0x1025fe5C, r8712_read8(padapter, 0x1025fe5C) - | BIT(7)); - for (i = 0; i < ETH_ALEN; i++) - padapter->eeprompriv.mac_addr[i] = r8712_read8(padapter, - MACID + i); - return _SUCCESS; -} - -uint rtl8712_hal_deinit(struct _adapter *padapter) -{ - r8712_write8(padapter, RF_CTRL, 0x00); - /* Turn off BB */ - msleep(20); - /* Turn off MAC */ - r8712_write8(padapter, SYS_CLKR + 1, 0x38); /* Switch Control Path */ - r8712_write8(padapter, SYS_FUNC_EN + 1, 0x70); - r8712_write8(padapter, PMC_FSM, 0x06); /* Enable Loader Data Keep */ - r8712_write8(padapter, SYS_ISO_CTRL, 0xF9); /* Isolation signals from - * CORE, PLL - */ - r8712_write8(padapter, SYS_ISO_CTRL + 1, 0xe8); /* Enable EFUSE 1.2V */ - r8712_write8(padapter, AFE_PLL_CTRL, 0x00); /* Disable AFE PLL. */ - r8712_write8(padapter, LDOA15_CTRL, 0x54); /* Disable A15V */ - r8712_write8(padapter, SYS_FUNC_EN + 1, 0x50); /* Disable E-Fuse 1.2V */ - r8712_write8(padapter, LDOV12D_CTRL, 0x24); /* Disable LDO12(for CE) */ - r8712_write8(padapter, AFE_MISC, 0x30); /* Disable AFE BG&MB */ - /* Option for Disable 1.6V LDO. */ - r8712_write8(padapter, SPS0_CTRL, 0x56); /* Disable 1.6V LDO */ - r8712_write8(padapter, SPS0_CTRL + 1, 0x43); /* Set SW PFM */ - return _SUCCESS; -} - -uint rtl871x_hal_init(struct _adapter *padapter) -{ - padapter->hw_init_completed = false; - if (!padapter->halpriv.hal_bus_init) - return _FAIL; - if (padapter->halpriv.hal_bus_init(padapter) != _SUCCESS) - return _FAIL; - if (rtl8712_hal_init(padapter) == _SUCCESS) { - padapter->hw_init_completed = true; - } else { - padapter->hw_init_completed = false; - return _FAIL; - } - return _SUCCESS; -} diff --git a/drivers/staging/rtl8712/ieee80211.c b/drivers/staging/rtl8712/ieee80211.c deleted file mode 100644 index 7d8f1a29d18a9..0000000000000 --- a/drivers/staging/rtl8712/ieee80211.c +++ /dev/null @@ -1,415 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * ieee80211.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _IEEE80211_C - -#include "drv_types.h" -#include "ieee80211.h" -#include "wifi.h" -#include "osdep_service.h" -#include "wlan_bssdef.h" - -static const u8 WPA_OUI_TYPE[] = {0x00, 0x50, 0xf2, 1}; -static const u8 WPA_CIPHER_SUITE_NONE[] = {0x00, 0x50, 0xf2, 0}; -static const u8 WPA_CIPHER_SUITE_WEP40[] = {0x00, 0x50, 0xf2, 1}; -static const u8 WPA_CIPHER_SUITE_TKIP[] = {0x00, 0x50, 0xf2, 2}; -static const u8 WPA_CIPHER_SUITE_CCMP[] = {0x00, 0x50, 0xf2, 4}; -static const u8 WPA_CIPHER_SUITE_WEP104[] = {0x00, 0x50, 0xf2, 5}; - -static const u8 RSN_CIPHER_SUITE_NONE[] = {0x00, 0x0f, 0xac, 0}; -static const u8 RSN_CIPHER_SUITE_WEP40[] = {0x00, 0x0f, 0xac, 1}; -static const u8 RSN_CIPHER_SUITE_TKIP[] = {0x00, 0x0f, 0xac, 2}; -static const u8 RSN_CIPHER_SUITE_CCMP[] = {0x00, 0x0f, 0xac, 4}; -static const u8 RSN_CIPHER_SUITE_WEP104[] = {0x00, 0x0f, 0xac, 5}; - -/*----------------------------------------------------------- - * for adhoc-master to generate ie and provide supported-rate to fw - *----------------------------------------------------------- - */ - -static u8 WIFI_CCKRATES[] = { - (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK), - (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK), - (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK), - (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK) -}; - -static u8 WIFI_OFDMRATES[] = { - (IEEE80211_OFDM_RATE_6MB), - (IEEE80211_OFDM_RATE_9MB), - (IEEE80211_OFDM_RATE_12MB), - (IEEE80211_OFDM_RATE_18MB), - (IEEE80211_OFDM_RATE_24MB), - (IEEE80211_OFDM_RATE_36MB), - (IEEE80211_OFDM_RATE_48MB), - (IEEE80211_OFDM_RATE_54MB) -}; - -uint r8712_is_cckrates_included(u8 *rate) -{ - u32 i = 0; - - while (rate[i] != 0) { - if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) || - (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22)) - return true; - i++; - } - return false; -} - -uint r8712_is_cckratesonly_included(u8 *rate) -{ - u32 i = 0; - - while (rate[i] != 0) { - if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) && - (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22)) - return false; - i++; - } - return true; -} - -/* r8712_set_ie will update frame length */ -u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen) -{ - *pbuf = (u8)index; - *(pbuf + 1) = (u8)len; - if (len > 0) - memcpy((void *)(pbuf + 2), (void *)source, len); - *frlen = *frlen + (len + 2); - return pbuf + len + 2; -} - -/* --------------------------------------------------------------------------- - * index: the information element id index, limit is the limit for search - * --------------------------------------------------------------------------- - */ -u8 *r8712_get_ie(u8 *pbuf, sint index, uint *len, sint limit) -{ - sint tmp, i; - u8 *p; - - if (limit < 1) - return NULL; - p = pbuf; - i = 0; - *len = 0; - while (1) { - if (*p == index) { - *len = *(p + 1); - return p; - } - tmp = *(p + 1); - p += (tmp + 2); - i += (tmp + 2); - if (i >= limit) - break; - } - return NULL; -} - -static void set_supported_rate(u8 *rates, uint mode) -{ - memset(rates, 0, NDIS_802_11_LENGTH_RATES_EX); - switch (mode) { - case WIRELESS_11B: - memcpy(rates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); - break; - case WIRELESS_11G: - case WIRELESS_11A: - memcpy(rates, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN); - break; - case WIRELESS_11BG: - memcpy(rates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); - memcpy(rates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, - IEEE80211_NUM_OFDM_RATESLEN); - break; - } -} - -static uint r8712_get_rateset_len(u8 *rateset) -{ - uint i = 0; - - while (1) { - if ((rateset[i]) == 0) - break; - if (i > 12) - break; - i++; - } - return i; -} - -int r8712_generate_ie(struct registry_priv *registrypriv) -{ - int rate_len; - uint sz = 0; - struct wlan_bssid_ex *dev_network = ®istrypriv->dev_network; - u8 *ie = dev_network->IEs; - u16 beacon_period = (u16)dev_network->Configuration.BeaconPeriod; - - /*timestamp will be inserted by hardware*/ - sz += 8; - ie += sz; - /*beacon interval : 2bytes*/ - *(__le16 *)ie = cpu_to_le16(beacon_period); - sz += 2; - ie += 2; - /*capability info*/ - *(u16 *)ie = 0; - *(__le16 *)ie |= cpu_to_le16(WLAN_CAPABILITY_IBSS); - if (registrypriv->preamble == PREAMBLE_SHORT) - *(__le16 *)ie |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); - if (dev_network->Privacy) - *(__le16 *)ie |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); - sz += 2; - ie += 2; - /*SSID*/ - ie = r8712_set_ie(ie, WLAN_EID_SSID, dev_network->Ssid.SsidLength, - dev_network->Ssid.Ssid, &sz); - /*supported rates*/ - set_supported_rate(dev_network->rates, registrypriv->wireless_mode); - rate_len = r8712_get_rateset_len(dev_network->rates); - if (rate_len > 8) { - ie = r8712_set_ie(ie, WLAN_EID_SUPP_RATES, 8, - dev_network->rates, &sz); - ie = r8712_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), - (dev_network->rates + 8), &sz); - } else { - ie = r8712_set_ie(ie, WLAN_EID_SUPP_RATES, - rate_len, dev_network->rates, &sz); - } - /*DS parameter set*/ - ie = r8712_set_ie(ie, WLAN_EID_DS_PARAMS, 1, - (u8 *)&dev_network->Configuration.DSConfig, &sz); - /*IBSS Parameter Set*/ - ie = r8712_set_ie(ie, WLAN_EID_IBSS_PARAMS, 2, - (u8 *)&dev_network->Configuration.ATIMWindow, &sz); - return sz; -} - -unsigned char *r8712_get_wpa_ie(unsigned char *ie, uint *wpa_ie_len, int limit) -{ - u32 len; - u16 val16; - unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; - u8 *buf = ie; - - while (1) { - buf = r8712_get_ie(buf, _WPA_IE_ID_, &len, limit); - if (buf) { - /*check if oui matches...*/ - if (memcmp((buf + 2), wpa_oui_type, - sizeof(wpa_oui_type))) - goto check_next_ie; - /*check version...*/ - memcpy((u8 *)&val16, (buf + 6), sizeof(val16)); - le16_to_cpus(&val16); - if (val16 != 0x0001) - goto check_next_ie; - *wpa_ie_len = *(buf + 1); - return buf; - } - *wpa_ie_len = 0; - return NULL; -check_next_ie: - limit = limit - (buf - ie) - 2 - len; - if (limit <= 0) - break; - buf += (2 + len); - } - *wpa_ie_len = 0; - return NULL; -} - -unsigned char *r8712_get_wpa2_ie(unsigned char *pie, uint *rsn_ie_len, - int limit) -{ - return r8712_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit); -} - -static int r8712_get_wpa_cipher_suite(u8 *s) -{ - if (!memcmp(s, (void *)WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN)) - return WPA_CIPHER_NONE; - if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN)) - return WPA_CIPHER_WEP40; - if (!memcmp(s, (void *)WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN)) - return WPA_CIPHER_TKIP; - if (!memcmp(s, (void *)WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN)) - return WPA_CIPHER_CCMP; - if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN)) - return WPA_CIPHER_WEP104; - return 0; -} - -static int r8712_get_wpa2_cipher_suite(u8 *s) -{ - if (!memcmp(s, (void *)RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN)) - return WPA_CIPHER_NONE; - if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN)) - return WPA_CIPHER_WEP40; - if (!memcmp(s, (void *)RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN)) - return WPA_CIPHER_TKIP; - if (!memcmp(s, (void *)RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN)) - return WPA_CIPHER_CCMP; - if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN)) - return WPA_CIPHER_WEP104; - return 0; -} - -int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, - int *pairwise_cipher) -{ - int i; - int left, count; - u8 *pos; - - if (wpa_ie_len <= 0) { - /* No WPA IE - fail silently */ - return -EINVAL; - } - if ((*wpa_ie != _WPA_IE_ID_) || - (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) || - (memcmp(wpa_ie + 2, (void *)WPA_OUI_TYPE, WPA_SELECTOR_LEN))) - return -EINVAL; - pos = wpa_ie; - pos += 8; - left = wpa_ie_len - 8; - /*group_cipher*/ - if (left >= WPA_SELECTOR_LEN) { - *group_cipher = r8712_get_wpa_cipher_suite(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) { - return -EINVAL; - } - /*pairwise_cipher*/ - if (left >= 2) { - count = le16_to_cpu(*(__le16 *)pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -EINVAL; - for (i = 0; i < count; i++) { - *pairwise_cipher |= r8712_get_wpa_cipher_suite(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) { - return -EINVAL; - } - return 0; -} - -int r8712_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, - int *pairwise_cipher) -{ - int i; - int left, count; - u8 *pos; - - if (rsn_ie_len <= 0) { - /* No RSN IE - fail silently */ - return -EINVAL; - } - if ((*rsn_ie != _WPA2_IE_ID_) || - (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2))) - return -EINVAL; - pos = rsn_ie; - pos += 4; - left = rsn_ie_len - 4; - /*group_cipher*/ - if (left >= RSN_SELECTOR_LEN) { - *group_cipher = r8712_get_wpa2_cipher_suite(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } else if (left > 0) { - return -EINVAL; - } - /*pairwise_cipher*/ - if (left >= 2) { - count = le16_to_cpu(*(__le16 *)pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) - return -EINVAL; - for (i = 0; i < count; i++) { - *pairwise_cipher |= r8712_get_wpa2_cipher_suite(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } - } else if (left == 1) { - return -EINVAL; - } - return 0; -} - -int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, - u8 *wpa_ie, u16 *wpa_len) -{ - u8 authmode; - u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; - uint cnt; - - /*Search required WPA or WPA2 IE and copy to sec_ie[ ]*/ - cnt = _TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_; - while (cnt < in_len) { - authmode = in_ie[cnt]; - if ((authmode == _WPA_IE_ID_) && - (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { - memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); - *wpa_len = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt + 1] + 2; /*get next */ - } else { - if (authmode == _WPA2_IE_ID_) { - memcpy(rsn_ie, &in_ie[cnt], - in_ie[cnt + 1] + 2); - *rsn_len = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt + 1] + 2; /*get next*/ - } else { - cnt += in_ie[cnt + 1] + 2; /*get next*/ - } - } - } - return *rsn_len + *wpa_len; -} - -int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) -{ - int match; - uint cnt; - u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; - - cnt = 12; - match = false; - while (cnt < in_len) { - eid = in_ie[cnt]; - if ((eid == _WPA_IE_ID_) && - (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { - memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); - *wps_ielen = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt + 1] + 2; - match = true; - break; - } - cnt += in_ie[cnt + 1] + 2; /* goto next */ - } - return match; -} diff --git a/drivers/staging/rtl8712/ieee80211.h b/drivers/staging/rtl8712/ieee80211.h deleted file mode 100644 index 65ceaca9b51ea..0000000000000 --- a/drivers/staging/rtl8712/ieee80211.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __IEEE80211_H -#define __IEEE80211_H - -#include - -#define IEEE_CMD_SET_WPA_PARAM 1 -#define IEEE_CMD_SET_WPA_IE 2 -#define IEEE_CMD_SET_ENCRYPTION 3 -#define IEEE_CMD_MLME 4 - -#define IEEE_PARAM_WPA_ENABLED 1 -#define IEEE_PARAM_TKIP_COUNTERMEASURES 2 -#define IEEE_PARAM_DROP_UNENCRYPTED 3 -#define IEEE_PARAM_PRIVACY_INVOKED 4 -#define IEEE_PARAM_AUTH_ALGS 5 -#define IEEE_PARAM_IEEE_802_1X 6 -#define IEEE_PARAM_WPAX_SELECT 7 - -#define AUTH_ALG_OPEN_SYSTEM 0x1 -#define AUTH_ALG_SHARED_KEY 0x2 -#define AUTH_ALG_LEAP 0x00000004 - -#define IEEE_MLME_STA_DEAUTH 1 -#define IEEE_MLME_STA_DISASSOC 2 - -#define IEEE_CRYPT_ERR_UNKNOWN_ALG 2 -#define IEEE_CRYPT_ERR_UNKNOWN_ADDR 3 -#define IEEE_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define IEEE_CRYPT_ERR_KEY_SET_FAILED 5 -#define IEEE_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define IEEE_CRYPT_ERR_CARD_CONF_FAILED 7 - -#define IEEE_CRYPT_ALG_NAME_LEN 16 - -#define WPA_CIPHER_NONE BIT(0) -#define WPA_CIPHER_WEP40 BIT(1) -#define WPA_CIPHER_WEP104 BIT(2) -#define WPA_CIPHER_TKIP BIT(3) -#define WPA_CIPHER_CCMP BIT(4) - -#define WPA_SELECTOR_LEN 4 -#define RSN_HEADER_LEN 4 - -#define RSN_SELECTOR_LEN 4 - -enum NETWORK_TYPE { - WIRELESS_INVALID = 0, - WIRELESS_11B = 1, - WIRELESS_11G = 2, - WIRELESS_11BG = (WIRELESS_11B | WIRELESS_11G), - WIRELESS_11A = 4, - WIRELESS_11N = 8, - WIRELESS_11GN = (WIRELESS_11G | WIRELESS_11N), - WIRELESS_11BGN = (WIRELESS_11B | WIRELESS_11G | WIRELESS_11N), -}; - -struct ieee_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u8 name; - u32 value; - } wpa_param; - struct { - u32 len; - u8 reserved[32]; - u8 data[]; - } wpa_ie; - struct { - int command; - int reason_code; - } mlme; - struct { - u8 alg[IEEE_CRYPT_ALG_NAME_LEN]; - u8 set_tx; - u32 err; - u8 idx; - u8 seq[8]; /* sequence counter (set: RX, get: TX) */ - u16 key_len; - u8 key[]; - } crypt; - } u; -}; - -#define MIN_FRAG_THRESHOLD 256U -#define MAX_FRAG_THRESHOLD 2346U - -/* QoS,QOS */ -#define NORMAL_ACK 0 - -/* IEEE 802.11 defines */ - -#define P80211_OUI_LEN 3 - -struct ieee80211_snap_hdr { - u8 dsap; /* always 0xAA */ - u8 ssap; /* always 0xAA */ - u8 ctrl; /* always 0x03 */ - u8 oui[P80211_OUI_LEN]; /* organizational universal id */ -} __packed; - -#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) - -#define IEEE80211_CCK_RATE_LEN 4 -#define IEEE80211_NUM_OFDM_RATESLEN 8 - -#define IEEE80211_CCK_RATE_1MB 0x02 -#define IEEE80211_CCK_RATE_2MB 0x04 -#define IEEE80211_CCK_RATE_5MB 0x0B -#define IEEE80211_CCK_RATE_11MB 0x16 -#define IEEE80211_OFDM_RATE_6MB 0x0C -#define IEEE80211_OFDM_RATE_9MB 0x12 -#define IEEE80211_OFDM_RATE_12MB 0x18 -#define IEEE80211_OFDM_RATE_18MB 0x24 -#define IEEE80211_OFDM_RATE_24MB 0x30 -#define IEEE80211_OFDM_RATE_36MB 0x48 -#define IEEE80211_OFDM_RATE_48MB 0x60 -#define IEEE80211_OFDM_RATE_54MB 0x6C -#define IEEE80211_BASIC_RATE_MASK 0x80 - -#define WEP_KEYS 4 - -/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs - * only use 8, and then use extended rates for the remaining supported - * rates. Other APs, however, stick all of their supported rates on the - * main rates information element... - */ -#define MAX_RATES_LENGTH ((u8)12) -#define MAX_WPA_IE_LEN 128 - -struct registry_priv; - -u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen); -u8 *r8712_get_ie(u8 *pbuf, sint index, uint *len, sint limit); -unsigned char *r8712_get_wpa_ie(unsigned char *pie, uint *rsn_ie_len, - int limit); -unsigned char *r8712_get_wpa2_ie(unsigned char *pie, uint *rsn_ie_len, - int limit); -int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, - int *pairwise_cipher); -int r8712_parse_wpa2_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, - int *pairwise_cipher); -int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, - u8 *wpa_ie, u16 *wpa_len); -int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen); -int r8712_generate_ie(struct registry_priv *pregistrypriv); -uint r8712_is_cckrates_included(u8 *rate); -uint r8712_is_cckratesonly_included(u8 *rate); - -#endif /* IEEE80211_H */ - diff --git a/drivers/staging/rtl8712/mlme_linux.c b/drivers/staging/rtl8712/mlme_linux.c deleted file mode 100644 index 436816d14cdf3..0000000000000 --- a/drivers/staging/rtl8712/mlme_linux.c +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * mlme_linux.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _MLME_OSDEP_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "mlme_osdep.h" - -static void sitesurvey_ctrl_handler(struct timer_list *t) -{ - struct _adapter *adapter = - from_timer(adapter, t, - mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer); - - _r8712_sitesurvey_ctrl_handler(adapter); - mod_timer(&adapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer, - jiffies + msecs_to_jiffies(3000)); -} - -static void join_timeout_handler (struct timer_list *t) -{ - struct _adapter *adapter = - from_timer(adapter, t, mlmepriv.assoc_timer); - - _r8712_join_timeout_handler(adapter); -} - -static void _scan_timeout_handler (struct timer_list *t) -{ - struct _adapter *adapter = - from_timer(adapter, t, mlmepriv.scan_to_timer); - - r8712_scan_timeout_handler(adapter); -} - -static void dhcp_timeout_handler (struct timer_list *t) -{ - struct _adapter *adapter = - from_timer(adapter, t, mlmepriv.dhcp_timer); - - _r8712_dhcp_timeout_handler(adapter); -} - -static void wdg_timeout_handler (struct timer_list *t) -{ - struct _adapter *adapter = - from_timer(adapter, t, mlmepriv.wdg_timer); - - r8712_wdg_wk_cmd(adapter); - - mod_timer(&adapter->mlmepriv.wdg_timer, - jiffies + msecs_to_jiffies(2000)); -} - -void r8712_init_mlme_timer(struct _adapter *adapter) -{ - struct mlme_priv *mlmepriv = &adapter->mlmepriv; - - timer_setup(&mlmepriv->assoc_timer, join_timeout_handler, 0); - timer_setup(&mlmepriv->sitesurveyctrl.sitesurvey_ctrl_timer, - sitesurvey_ctrl_handler, 0); - timer_setup(&mlmepriv->scan_to_timer, _scan_timeout_handler, 0); - timer_setup(&mlmepriv->dhcp_timer, dhcp_timeout_handler, 0); - timer_setup(&mlmepriv->wdg_timer, wdg_timeout_handler, 0); -} - -void r8712_os_indicate_connect(struct _adapter *adapter) -{ - r8712_indicate_wx_assoc_event(adapter); - netif_carrier_on(adapter->pnetdev); -} - -static struct RT_PMKID_LIST backup_PMKID_list[NUM_PMKID_CACHE]; -void r8712_os_indicate_disconnect(struct _adapter *adapter) -{ - u8 backup_PMKID_index = 0; - u8 backup_TKIP_countermeasure = 0x00; - - r8712_indicate_wx_disassoc_event(adapter); - netif_carrier_off(adapter->pnetdev); - if (adapter->securitypriv.AuthAlgrthm == 2) { /*/802.1x*/ - /* We have to backup the PMK information for WiFi PMK Caching - * test item. Backup the btkip_countermeasure information. - * When the countermeasure is trigger, the driver have to - * disconnect with AP for 60 seconds. - */ - - memcpy(&backup_PMKID_list[0], - &adapter->securitypriv.PMKIDList[0], - sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE); - backup_PMKID_index = adapter->securitypriv.PMKIDIndex; - backup_TKIP_countermeasure = - adapter->securitypriv.btkip_countermeasure; - memset((unsigned char *)&adapter->securitypriv, 0, - sizeof(struct security_priv)); - timer_setup(&adapter->securitypriv.tkip_timer, - r8712_use_tkipkey_handler, 0); - /* Restore the PMK information to securitypriv structure - * for the following connection. - */ - memcpy(&adapter->securitypriv.PMKIDList[0], - &backup_PMKID_list[0], - sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE); - adapter->securitypriv.PMKIDIndex = backup_PMKID_index; - adapter->securitypriv.btkip_countermeasure = - backup_TKIP_countermeasure; - } else { /*reset values in securitypriv*/ - struct security_priv *sec_priv = &adapter->securitypriv; - - sec_priv->AuthAlgrthm = 0; /*open system*/ - sec_priv->PrivacyAlgrthm = _NO_PRIVACY_; - sec_priv->PrivacyKeyIndex = 0; - sec_priv->XGrpPrivacy = _NO_PRIVACY_; - sec_priv->XGrpKeyid = 1; - sec_priv->ndisauthtype = Ndis802_11AuthModeOpen; - sec_priv->ndisencryptstatus = Ndis802_11WEPDisabled; - sec_priv->wps_phase = false; - } -} - -void r8712_report_sec_ie(struct _adapter *adapter, u8 authmode, u8 *sec_ie) -{ - uint len; - u8 *buff, *p, i; - union iwreq_data wrqu; - - buff = NULL; - if (authmode == _WPA_IE_ID_) { - buff = kzalloc(IW_CUSTOM_MAX, GFP_ATOMIC); - if (!buff) - return; - p = buff; - p += sprintf(p, "ASSOCINFO(ReqIEs="); - len = sec_ie[1] + 2; - len = (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX; - for (i = 0; i < len; i++) - p += sprintf(p, "%02x", sec_ie[i]); - p += sprintf(p, ")"); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = p - buff; - wrqu.data.length = (wrqu.data.length < IW_CUSTOM_MAX) ? - wrqu.data.length : IW_CUSTOM_MAX; - wireless_send_event(adapter->pnetdev, IWEVCUSTOM, &wrqu, buff); - kfree(buff); - } -} diff --git a/drivers/staging/rtl8712/mlme_osdep.h b/drivers/staging/rtl8712/mlme_osdep.h deleted file mode 100644 index a02c782588ddb..0000000000000 --- a/drivers/staging/rtl8712/mlme_osdep.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __MLME_OSDEP_H_ -#define __MLME_OSDEP_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -void r8712_init_mlme_timer(struct _adapter *padapter); -void r8712_os_indicate_disconnect(struct _adapter *adapter); -void r8712_os_indicate_connect(struct _adapter *adapter); -void r8712_report_sec_ie(struct _adapter *adapter, u8 authmode, u8 *sec_ie); -int r8712_recv_indicatepkts_in_order(struct _adapter *adapter, - struct recv_reorder_ctrl *precvreorder_ctrl, - int bforced); -void r8712_indicate_wx_assoc_event(struct _adapter *padapter); -void r8712_indicate_wx_disassoc_event(struct _adapter *padapter); - -#endif /*_MLME_OSDEP_H_*/ - diff --git a/drivers/staging/rtl8712/mp_custom_oid.h b/drivers/staging/rtl8712/mp_custom_oid.h deleted file mode 100644 index a9fac87fcabc5..0000000000000 --- a/drivers/staging/rtl8712/mp_custom_oid.h +++ /dev/null @@ -1,287 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __CUSTOM_OID_H -#define __CUSTOM_OID_H - -/* 0xFF818000 - 0xFF81802F RTL8180 Mass Production Kit - * 0xFF818500 - 0xFF81850F RTL8185 Setup Utility - * 0xFF818580 - 0xFF81858F RTL8185 Phy Status Utility - * - * by Owen for Production Kit - * For Production Kit with Agilent Equipments - * in order to make our custom oids hopefully somewhat unique - * we will use 0xFF (indicating implementation specific OID) - * 81(first byte of non zero Realtek unique identifier) - * 80 (second byte of non zero Realtek unique identifier) - * XX (the custom OID number - providing 255 possible custom oids) - */ -#define OID_RT_PRO_RESET_DUT 0xFF818000 -#define OID_RT_PRO_SET_DATA_RATE 0xFF818001 -#define OID_RT_PRO_START_TEST 0xFF818002 -#define OID_RT_PRO_STOP_TEST 0xFF818003 -#define OID_RT_PRO_SET_PREAMBLE 0xFF818004 -#define OID_RT_PRO_SET_SCRAMBLER 0xFF818005 -#define OID_RT_PRO_SET_FILTER_BB 0xFF818006 -#define OID_RT_PRO_SET_MANUAL_DIVERSITY_BB 0xFF818007 -#define OID_RT_PRO_SET_CHANNEL_DIRECT_CALL 0xFF818008 -#define OID_RT_PRO_SET_SLEEP_MODE_DIRECT_CALL 0xFF818009 -#define OID_RT_PRO_SET_WAKE_MODE_DIRECT_CALL 0xFF81800A - -#define OID_RT_PRO_SET_TX_ANTENNA_BB 0xFF81800D -#define OID_RT_PRO_SET_ANTENNA_BB 0xFF81800E -#define OID_RT_PRO_SET_CR_SCRAMBLER 0xFF81800F -#define OID_RT_PRO_SET_CR_NEW_FILTER 0xFF818010 -#define OID_RT_PRO_SET_TX_POWER_CONTROL 0xFF818011 -#define OID_RT_PRO_SET_CR_TX_CONFIG 0xFF818012 -#define OID_RT_PRO_GET_TX_POWER_CONTROL 0xFF818013 -#define OID_RT_PRO_GET_CR_SIGNAL_QUALITY 0xFF818014 -#define OID_RT_PRO_SET_CR_SETPOINT 0xFF818015 -#define OID_RT_PRO_SET_INTEGRATOR 0xFF818016 -#define OID_RT_PRO_SET_SIGNAL_QUALITY 0xFF818017 -#define OID_RT_PRO_GET_INTEGRATOR 0xFF818018 -#define OID_RT_PRO_GET_SIGNAL_QUALITY 0xFF818019 -#define OID_RT_PRO_QUERY_EEPROM_TYPE 0xFF81801A -#define OID_RT_PRO_WRITE_MAC_ADDRESS 0xFF81801B -#define OID_RT_PRO_READ_MAC_ADDRESS 0xFF81801C -#define OID_RT_PRO_WRITE_CIS_DATA 0xFF81801D -#define OID_RT_PRO_READ_CIS_DATA 0xFF81801E -#define OID_RT_PRO_WRITE_POWER_CONTROL 0xFF81801F -#define OID_RT_PRO_READ_POWER_CONTROL 0xFF818020 -#define OID_RT_PRO_WRITE_EEPROM 0xFF818021 -#define OID_RT_PRO_READ_EEPROM 0xFF818022 -#define OID_RT_PRO_RESET_TX_PACKET_SENT 0xFF818023 -#define OID_RT_PRO_QUERY_TX_PACKET_SENT 0xFF818024 -#define OID_RT_PRO_RESET_RX_PACKET_RECEIVED 0xFF818025 -#define OID_RT_PRO_QUERY_RX_PACKET_RECEIVED 0xFF818026 -#define OID_RT_PRO_QUERY_RX_PACKET_CRC32_ERROR 0xFF818027 -#define OID_RT_PRO_QUERY_CURRENT_ADDRESS 0xFF818028 -#define OID_RT_PRO_QUERY_PERMANENT_ADDRESS 0xFF818029 -#define OID_RT_PRO_SET_PHILIPS_RF_PARAMETERS 0xFF81802A -#define OID_RT_PRO_RECEIVE_PACKET 0xFF81802C -#define OID_RT_PRO_WRITE_EEPROM_BYTE 0xFF81802D -#define OID_RT_PRO_READ_EEPROM_BYTE 0xFF81802E -#define OID_RT_PRO_SET_MODULATION 0xFF81802F -#define OID_RT_DRIVER_OPTION 0xFF818080 -#define OID_RT_RF_OFF 0xFF818081 -#define OID_RT_AUTH_STATUS 0xFF818082 -#define OID_RT_PRO_SET_CONTINUOUS_TX 0xFF81800B -#define OID_RT_PRO_SET_SINGLE_CARRIER_TX 0xFF81800C -#define OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX 0xFF81802B -#define OID_RT_PRO_SET_SINGLE_TONE_TX 0xFF818043 -#define OID_RT_UTILITY_FALSE_ALARM_COUNTERS 0xFF818580 -#define OID_RT_UTILITY_SELECT_DEBUG_MODE 0xFF818581 -#define OID_RT_UTILITY_SELECT_SUBCARRIER_NUMBER 0xFF818582 -#define OID_RT_UTILITY_GET_RSSI_STATUS 0xFF818583 -#define OID_RT_UTILITY_GET_FRAME_DETECTION_STATUS 0xFF818584 -#define OID_RT_UTILITY_GET_AGC_AND_FREQUENCY_OFFSET_ESTIMATION_STATUS \ - 0xFF818585 -#define OID_RT_UTILITY_GET_CHANNEL_ESTIMATION_STATUS 0xFF818586 -#define OID_RT_WIRELESS_MODE 0xFF818500 -#define OID_RT_SUPPORTED_RATES 0xFF818501 -#define OID_RT_DESIRED_RATES 0xFF818502 -#define OID_RT_WIRELESS_MODE_STARTING_ADHOC 0xFF818503 -#define OID_RT_GET_CONNECT_STATE 0xFF030001 -#define OID_RT_RESCAN 0xFF030002 -#define OID_RT_SET_KEY_LENGTH 0xFF030003 -#define OID_RT_SET_DEFAULT_KEY_ID 0xFF030004 -#define OID_RT_SET_CHANNEL 0xFF010182 -#define OID_RT_SET_SNIFFER_MODE 0xFF010183 -#define OID_RT_GET_SIGNAL_QUALITY 0xFF010184 -#define OID_RT_GET_SMALL_PACKET_CRC 0xFF010185 -#define OID_RT_GET_MIDDLE_PACKET_CRC 0xFF010186 -#define OID_RT_GET_LARGE_PACKET_CRC 0xFF010187 -#define OID_RT_GET_TX_RETRY 0xFF010188 -#define OID_RT_GET_RX_RETRY 0xFF010189 -#define OID_RT_PRO_SET_FW_DIG_STATE 0xFF01018A -#define OID_RT_PRO_SET_FW_RA_STATE 0xFF01018B -#define OID_RT_GET_RX_TOTAL_PACKET 0xFF010190 -#define OID_RT_GET_TX_BEACON_OK 0xFF010191 -#define OID_RT_GET_TX_BEACON_ERR 0xFF010192 -#define OID_RT_GET_RX_ICV_ERR 0xFF010193 -#define OID_RT_SET_ENCRYPTION_ALGORITHM 0xFF010194 -#define OID_RT_SET_NO_AUTO_RESCAN 0xFF010195 -#define OID_RT_GET_PREAMBLE_MODE 0xFF010196 -#define OID_RT_GET_DRIVER_UP_DELTA_TIME 0xFF010197 -#define OID_RT_GET_AP_IP 0xFF010198 -#define OID_RT_GET_CHANNELPLAN 0xFF010199 -#define OID_RT_SET_PREAMBLE_MODE 0xFF01019A -#define OID_RT_SET_BCN_INTVL 0xFF01019B -#define OID_RT_GET_RF_VENDER 0xFF01019C -#define OID_RT_DEDICATE_PROBE 0xFF01019D -#define OID_RT_PRO_RX_FILTER_PATTERN 0xFF01019E -#define OID_RT_GET_DCST_CURRENT_THRESHOLD 0xFF01019F -#define OID_RT_GET_CCA_ERR 0xFF0101A0 -#define OID_RT_GET_CCA_UPGRADE_THRESHOLD 0xFF0101A1 -#define OID_RT_GET_CCA_FALLBACK_THRESHOLD 0xFF0101A2 -#define OID_RT_GET_CCA_UPGRADE_EVALUATE_TIMES 0xFF0101A3 -#define OID_RT_GET_CCA_FALLBACK_EVALUATE_TIMES 0xFF0101A4 -#define OID_RT_SET_RATE_ADAPTIVE 0xFF0101A5 -#define OID_RT_GET_DCST_EVALUATE_PERIOD 0xFF0101A5 -#define OID_RT_GET_DCST_TIME_UNIT_INDEX 0xFF0101A6 -#define OID_RT_GET_TOTAL_TX_BYTES 0xFF0101A7 -#define OID_RT_GET_TOTAL_RX_BYTES 0xFF0101A8 -#define OID_RT_CURRENT_TX_POWER_LEVEL 0xFF0101A9 -#define OID_RT_GET_ENC_KEY_MISMATCH_COUNT 0xFF0101AA -#define OID_RT_GET_ENC_KEY_MATCH_COUNT 0xFF0101AB -#define OID_RT_GET_CHANNEL 0xFF0101AC -#define OID_RT_SET_CHANNELPLAN 0xFF0101AD -#define OID_RT_GET_HARDWARE_RADIO_OFF 0xFF0101AE -#define OID_RT_CHANNELPLAN_BY_COUNTRY 0xFF0101AF -#define OID_RT_SCAN_AVAILABLE_BSSID 0xFF0101B0 -#define OID_RT_GET_HARDWARE_VERSION 0xFF0101B1 -#define OID_RT_GET_IS_ROAMING 0xFF0101B2 -#define OID_RT_GET_IS_PRIVACY 0xFF0101B3 -#define OID_RT_GET_KEY_MISMATCH 0xFF0101B4 -#define OID_RT_SET_RSSI_ROAM_TRAFFIC_TH 0xFF0101B5 -#define OID_RT_SET_RSSI_ROAM_SIGNAL_TH 0xFF0101B6 -#define OID_RT_RESET_LOG 0xFF0101B7 -#define OID_RT_GET_LOG 0xFF0101B8 -#define OID_RT_SET_INDICATE_HIDDEN_AP 0xFF0101B9 -#define OID_RT_GET_HEADER_FAIL 0xFF0101BA -#define OID_RT_SUPPORTED_WIRELESS_MODE 0xFF0101BB -#define OID_RT_GET_CHANNEL_LIST 0xFF0101BC -#define OID_RT_GET_SCAN_IN_PROGRESS 0xFF0101BD -#define OID_RT_GET_TX_INFO 0xFF0101BE -#define OID_RT_RF_READ_WRITE_OFFSET 0xFF0101BF -#define OID_RT_RF_READ_WRITE 0xFF0101C0 -#define OID_RT_FORCED_DATA_RATE 0xFF0101C1 -#define OID_RT_WIRELESS_MODE_FOR_SCAN_LIST 0xFF0101C2 -#define OID_RT_GET_BSS_WIRELESS_MODE 0xFF0101C3 -#define OID_RT_SCAN_WITH_MAGIC_PACKET 0xFF0101C4 -#define OID_RT_PRO_RX_FILTER 0xFF0111C0 -#define OID_CE_USB_WRITE_REGISTRY 0xFF0111C1 -#define OID_CE_USB_READ_REGISTRY 0xFF0111C2 -#define OID_RT_PRO_SET_INITIAL_GAIN 0xFF0111C3 -#define OID_RT_PRO_SET_BB_RF_STANDBY_MODE 0xFF0111C4 -#define OID_RT_PRO_SET_BB_RF_SHUTDOWN_MODE 0xFF0111C5 -#define OID_RT_PRO_SET_TX_CHARGE_PUMP 0xFF0111C6 -#define OID_RT_PRO_SET_RX_CHARGE_PUMP 0xFF0111C7 -#define OID_RT_PRO_RF_WRITE_REGISTRY 0xFF0111C8 -#define OID_RT_PRO_RF_READ_REGISTRY 0xFF0111C9 -#define OID_RT_PRO_QUERY_RF_TYPE 0xFF0111CA -#define OID_RT_AP_GET_ASSOCIATED_STATION_LIST 0xFF010300 -#define OID_RT_AP_GET_CURRENT_TIME_STAMP 0xFF010301 -#define OID_RT_AP_SWITCH_INTO_AP_MODE 0xFF010302 -#define OID_RT_AP_SET_DTIM_PERIOD 0xFF010303 -#define OID_RT_AP_SUPPORTED 0xFF010304 -#define OID_RT_AP_SET_PASSPHRASE 0xFF010305 -#define OID_RT_PRO8187_WI_POLL 0xFF818780 -#define OID_RT_PRO_WRITE_BB_REG 0xFF818781 -#define OID_RT_PRO_READ_BB_REG 0xFF818782 -#define OID_RT_PRO_WRITE_RF_REG 0xFF818783 -#define OID_RT_PRO_READ_RF_REG 0xFF818784 -#define OID_RT_MH_VENDER_ID 0xFFEDC100 -#define OID_RT_PRO8711_JOIN_BSS 0xFF871100 -#define OID_RT_PRO_READ_REGISTER 0xFF871101 -#define OID_RT_PRO_WRITE_REGISTER 0xFF871102 -#define OID_RT_PRO_BURST_READ_REGISTER 0xFF871103 -#define OID_RT_PRO_BURST_WRITE_REGISTER 0xFF871104 -#define OID_RT_PRO_WRITE_TXCMD 0xFF871105 -#define OID_RT_PRO_READ16_EEPROM 0xFF871106 -#define OID_RT_PRO_WRITE16_EEPROM 0xFF871107 -#define OID_RT_PRO_H2C_SET_COMMAND 0xFF871108 -#define OID_RT_PRO_H2C_QUERY_RESULT 0xFF871109 -#define OID_RT_PRO8711_WI_POLL 0xFF87110A -#define OID_RT_PRO8711_PKT_LOSS 0xFF87110B -#define OID_RT_RD_ATTRIB_MEM 0xFF87110C -#define OID_RT_WR_ATTRIB_MEM 0xFF87110D -/*Method 2 for H2C/C2H*/ -#define OID_RT_PRO_H2C_CMD_MODE 0xFF871110 -#define OID_RT_PRO_H2C_CMD_RSP_MODE 0xFF871111 -#define OID_RT_PRO_H2C_CMD_EVENT_MODE 0xFF871112 -#define OID_RT_PRO_WAIT_C2H_EVENT 0xFF871113 -#define OID_RT_PRO_RW_ACCESS_PROTOCOL_TEST 0xFF871114 -#define OID_RT_PRO_SCSI_ACCESS_TEST 0xFF871115 -#define OID_RT_PRO_SCSI_TCPIPOFFLOAD_OUT 0xFF871116 -#define OID_RT_PRO_SCSI_TCPIPOFFLOAD_IN 0xFF871117 -#define OID_RT_RRO_RX_PKT_VIA_IOCTRL 0xFF871118 -#define OID_RT_RRO_RX_PKTARRAY_VIA_IOCTRL 0xFF871119 -#define OID_RT_RPO_SET_PWRMGT_TEST 0xFF87111A -#define OID_RT_PRO_QRY_PWRMGT_TEST 0XFF87111B -#define OID_RT_RPO_ASYNC_RWIO_TEST 0xFF87111C -#define OID_RT_RPO_ASYNC_RWIO_POLL 0xFF87111D -#define OID_RT_PRO_SET_RF_INTFS 0xFF87111E -#define OID_RT_POLL_RX_STATUS 0xFF87111F -#define OID_RT_PRO_CFG_DEBUG_MESSAGE 0xFF871120 -#define OID_RT_PRO_SET_DATA_RATE_EX 0xFF871121 -#define OID_RT_PRO_SET_BASIC_RATE 0xFF871122 -#define OID_RT_PRO_READ_TSSI 0xFF871123 -#define OID_RT_PRO_SET_POWER_TRACKING 0xFF871124 -#define OID_RT_PRO_QRY_PWRSTATE 0xFF871150 -#define OID_RT_PRO_SET_PWRSTATE 0xFF871151 -/*Method 2 , using workitem */ -#define OID_RT_SET_READ_REG 0xFF871181 -#define OID_RT_SET_WRITE_REG 0xFF871182 -#define OID_RT_SET_BURST_READ_REG 0xFF871183 -#define OID_RT_SET_BURST_WRITE_REG 0xFF871184 -#define OID_RT_SET_WRITE_TXCMD 0xFF871185 -#define OID_RT_SET_READ16_EEPROM 0xFF871186 -#define OID_RT_SET_WRITE16_EEPROM 0xFF871187 -#define OID_RT_QRY_POLL_WKITEM 0xFF871188 - -/*For SDIO INTERFACE only*/ -#define OID_RT_PRO_SYNCPAGERW_SRAM 0xFF8711A0 -#define OID_RT_PRO_871X_DRV_EXT 0xFF8711A1 - -/*For USB INTERFACE only*/ -#define OID_RT_PRO_USB_VENDOR_REQ 0xFF8711B0 -#define OID_RT_PRO_SCSI_AUTO_TEST 0xFF8711B1 -#define OID_RT_PRO_USB_MAC_AC_FIFO_WRITE 0xFF8711B2 -#define OID_RT_PRO_USB_MAC_RX_FIFO_READ 0xFF8711B3 -#define OID_RT_PRO_USB_MAC_RX_FIFO_POLLING 0xFF8711B4 - -#define OID_RT_PRO_H2C_SET_RATE_TABLE 0xFF8711FB -#define OID_RT_PRO_H2C_GET_RATE_TABLE 0xFF8711FC -#define OID_RT_PRO_H2C_C2H_LBK_TEST 0xFF8711FE - -#define OID_RT_PRO_ENCRYPTION_CTRL 0xFF871200 -#define OID_RT_PRO_ADD_STA_INFO 0xFF871201 -#define OID_RT_PRO_DELE_STA_INFO 0xFF871202 -#define OID_RT_PRO_QUERY_DR_VARIABLE 0xFF871203 - -#define OID_RT_PRO_RX_PACKET_TYPE 0xFF871204 - -#define OID_RT_PRO_READ_EFUSE 0xFF871205 -#define OID_RT_PRO_WRITE_EFUSE 0xFF871206 -#define OID_RT_PRO_RW_EFUSE_PGPKT 0xFF871207 -#define OID_RT_GET_EFUSE_CURRENT_SIZE 0xFF871208 - -#define OID_RT_SET_BANDWIDTH 0xFF871209 -#define OID_RT_SET_CRYSTAL_CAP 0xFF87120A - -#define OID_RT_SET_RX_PACKET_TYPE 0xFF87120B - -#define OID_RT_GET_EFUSE_MAX_SIZE 0xFF87120C - -#define OID_RT_PRO_SET_TX_AGC_OFFSET 0xFF87120D - -#define OID_RT_PRO_SET_PKT_TEST_MODE 0xFF87120E - -#define OID_RT_PRO_FOR_EVM_TEST_SETTING 0xFF87120F - -#define OID_RT_PRO_GET_THERMAL_METER 0xFF871210 - -#define OID_RT_RESET_PHY_RX_PACKET_COUNT 0xFF871211 -#define OID_RT_GET_PHY_RX_PACKET_RECEIVED 0xFF871212 -#define OID_RT_GET_PHY_RX_PACKET_CRC32_ERROR 0xFF871213 - -#define OID_RT_SET_POWER_DOWN 0xFF871214 - -#define OID_RT_GET_POWER_MODE 0xFF871215 - -#define OID_RT_PRO_EFUSE 0xFF871216 -#define OID_RT_PRO_EFUSE_MAP 0xFF871217 - -#endif /*#ifndef __CUSTOM_OID_H */ - diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c deleted file mode 100644 index 1b11f8b04e13f..0000000000000 --- a/drivers/staging/rtl8712/os_intfs.c +++ /dev/null @@ -1,482 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * os_intfs.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _OS_INTFS_C_ - -#include -#include -#include -#include "osdep_service.h" -#include "drv_types.h" -#include "xmit_osdep.h" -#include "recv_osdep.h" -#include "rtl871x_ioctl.h" -#include "usb_osintf.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("rtl871x wireless lan driver"); -MODULE_AUTHOR("Larry Finger"); - -static char ifname[IFNAMSIZ] = "wlan%d"; - -/* module param defaults */ -static int chip_version = RTL8712_2ndCUT; -static int rfintfs = HWPI; -static int lbkmode = RTL8712_AIR_TRX; -static int hci = RTL8712_USB; -static int ampdu_enable = 1;/*for enable tx_ampdu*/ - -/* The video_mode variable is for video mode.*/ -/* It may be specify when inserting module with video_mode=1 parameter.*/ -static int video_mode = 1; /* enable video mode*/ - -/*Ndis802_11Infrastructure; infra, ad-hoc, auto*/ -static int network_mode = Ndis802_11IBSS; -static int channel = 1;/*ad-hoc support requirement*/ -static int wireless_mode = WIRELESS_11BG; -static int vrtl_carrier_sense = AUTO_VCS; -static int vcs_type = RTS_CTS; -static int frag_thresh = 2346; -static int preamble = PREAMBLE_LONG;/*long, short, auto*/ -static int scan_mode = 1;/*active, passive*/ -static int adhoc_tx_pwr = 1; -static int soft_ap; -static int smart_ps = 1; -static int power_mgnt = PS_MODE_ACTIVE; -static int radio_enable = 1; -static int long_retry_lmt = 7; -static int short_retry_lmt = 7; -static int busy_thresh = 40; -static int ack_policy = NORMAL_ACK; -static int mp_mode; -static int software_encrypt; -static int software_decrypt; - -static int wmm_enable;/* default is set to disable the wmm.*/ -static int uapsd_enable; -static int uapsd_max_sp = NO_LIMIT; -static int uapsd_acbk_en; -static int uapsd_acbe_en; -static int uapsd_acvi_en; -static int uapsd_acvo_en; - -static int ht_enable = 1; -static int cbw40_enable = 1; -static int rf_config = RTL8712_RF_1T2R; /* 1T2R*/ -static int low_power; -/* mac address to use instead of the one stored in Efuse */ -char *r8712_initmac; -static char *initmac; -/* if wifi_test = 1, driver will disable the turbo mode and pass it to - * firmware private. - */ -static int wifi_test; - -module_param_string(ifname, ifname, sizeof(ifname), 0644); -module_param(wifi_test, int, 0644); -module_param(initmac, charp, 0644); -module_param(video_mode, int, 0644); -module_param(chip_version, int, 0644); -module_param(rfintfs, int, 0644); -module_param(lbkmode, int, 0644); -module_param(hci, int, 0644); -module_param(network_mode, int, 0644); -module_param(channel, int, 0644); -module_param(mp_mode, int, 0644); -module_param(wmm_enable, int, 0644); -module_param(vrtl_carrier_sense, int, 0644); -module_param(vcs_type, int, 0644); -module_param(busy_thresh, int, 0644); -module_param(ht_enable, int, 0644); -module_param(cbw40_enable, int, 0644); -module_param(ampdu_enable, int, 0644); -module_param(rf_config, int, 0644); -module_param(power_mgnt, int, 0644); -module_param(low_power, int, 0644); - -MODULE_PARM_DESC(ifname, " Net interface name, wlan%d=default"); -MODULE_PARM_DESC(initmac, "MAC-Address, default: use FUSE"); - -static int netdev_open(struct net_device *pnetdev); -static int netdev_close(struct net_device *pnetdev); - -static void loadparam(struct _adapter *padapter, struct net_device *pnetdev) -{ - struct registry_priv *registry_par = &padapter->registrypriv; - - registry_par->chip_version = (u8)chip_version; - registry_par->rfintfs = (u8)rfintfs; - registry_par->lbkmode = (u8)lbkmode; - registry_par->hci = (u8)hci; - registry_par->network_mode = (u8)network_mode; - memcpy(registry_par->ssid.Ssid, "ANY", 3); - registry_par->ssid.SsidLength = 3; - registry_par->channel = (u8)channel; - registry_par->wireless_mode = (u8)wireless_mode; - registry_par->vrtl_carrier_sense = (u8)vrtl_carrier_sense; - registry_par->vcs_type = (u8)vcs_type; - registry_par->frag_thresh = (u16)frag_thresh; - registry_par->preamble = (u8)preamble; - registry_par->scan_mode = (u8)scan_mode; - registry_par->adhoc_tx_pwr = (u8)adhoc_tx_pwr; - registry_par->soft_ap = (u8)soft_ap; - registry_par->smart_ps = (u8)smart_ps; - registry_par->power_mgnt = (u8)power_mgnt; - registry_par->radio_enable = (u8)radio_enable; - registry_par->long_retry_lmt = (u8)long_retry_lmt; - registry_par->short_retry_lmt = (u8)short_retry_lmt; - registry_par->busy_thresh = (u16)busy_thresh; - registry_par->ack_policy = (u8)ack_policy; - registry_par->mp_mode = (u8)mp_mode; - registry_par->software_encrypt = (u8)software_encrypt; - registry_par->software_decrypt = (u8)software_decrypt; - /*UAPSD*/ - registry_par->wmm_enable = (u8)wmm_enable; - registry_par->uapsd_enable = (u8)uapsd_enable; - registry_par->uapsd_max_sp = (u8)uapsd_max_sp; - registry_par->uapsd_acbk_en = (u8)uapsd_acbk_en; - registry_par->uapsd_acbe_en = (u8)uapsd_acbe_en; - registry_par->uapsd_acvi_en = (u8)uapsd_acvi_en; - registry_par->uapsd_acvo_en = (u8)uapsd_acvo_en; - registry_par->ht_enable = (u8)ht_enable; - registry_par->cbw40_enable = (u8)cbw40_enable; - registry_par->ampdu_enable = (u8)ampdu_enable; - registry_par->rf_config = (u8)rf_config; - registry_par->low_power = (u8)low_power; - registry_par->wifi_test = (u8)wifi_test; - r8712_initmac = initmac; -} - -static int r871x_net_set_mac_address(struct net_device *pnetdev, void *p) -{ - struct _adapter *padapter = netdev_priv(pnetdev); - struct sockaddr *addr = p; - - if (!padapter->bup) - eth_hw_addr_set(pnetdev, addr->sa_data); - return 0; -} - -static struct net_device_stats *r871x_net_get_stats(struct net_device *pnetdev) -{ - struct _adapter *padapter = netdev_priv(pnetdev); - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct recv_priv *precvpriv = &padapter->recvpriv; - - padapter->stats.tx_packets = pxmitpriv->tx_pkts; - padapter->stats.rx_packets = precvpriv->rx_pkts; - padapter->stats.tx_dropped = pxmitpriv->tx_drop; - padapter->stats.rx_dropped = precvpriv->rx_drop; - padapter->stats.tx_bytes = pxmitpriv->tx_bytes; - padapter->stats.rx_bytes = precvpriv->rx_bytes; - return &padapter->stats; -} - -static const struct net_device_ops rtl8712_netdev_ops = { - .ndo_open = netdev_open, - .ndo_stop = netdev_close, - .ndo_start_xmit = r8712_xmit_entry, - .ndo_set_mac_address = r871x_net_set_mac_address, - .ndo_get_stats = r871x_net_get_stats, - .ndo_do_ioctl = r871x_ioctl, -}; - -struct net_device *r8712_init_netdev(void) -{ - struct _adapter *padapter; - struct net_device *pnetdev; - - pnetdev = alloc_etherdev(sizeof(struct _adapter)); - if (!pnetdev) - return NULL; - if (dev_alloc_name(pnetdev, ifname) < 0) { - strscpy(ifname, "wlan%d", sizeof(ifname)); - dev_alloc_name(pnetdev, ifname); - } - padapter = netdev_priv(pnetdev); - padapter->pnetdev = pnetdev; - pr_info("r8712u: register rtl8712_netdev_ops to netdev_ops\n"); - pnetdev->netdev_ops = &rtl8712_netdev_ops; - pnetdev->watchdog_timeo = HZ; /* 1 second timeout */ - pnetdev->wireless_handlers = (struct iw_handler_def *) - &r871x_handlers_def; - loadparam(padapter, pnetdev); - netif_carrier_off(pnetdev); - padapter->pid = 0; /* Initial the PID value used for HW PBC.*/ - return pnetdev; -} - -static u32 start_drv_threads(struct _adapter *padapter) -{ - padapter->cmd_thread = kthread_run(r8712_cmd_thread, padapter, "%s", - padapter->pnetdev->name); - if (IS_ERR(padapter->cmd_thread)) - return _FAIL; - return _SUCCESS; -} - -void r8712_stop_drv_threads(struct _adapter *padapter) -{ - struct completion *completion = - &padapter->cmdpriv.terminate_cmdthread_comp; - - /*Below is to terminate r8712_cmd_thread & event_thread...*/ - complete(&padapter->cmdpriv.cmd_queue_comp); - if (padapter->cmd_thread) - wait_for_completion_interruptible(completion); - padapter->cmdpriv.cmd_seq = 1; -} - -static void start_drv_timers(struct _adapter *padapter) -{ - mod_timer(&padapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer, - jiffies + msecs_to_jiffies(5000)); - mod_timer(&padapter->mlmepriv.wdg_timer, - jiffies + msecs_to_jiffies(2000)); -} - -void r8712_stop_drv_timers(struct _adapter *padapter) -{ - del_timer_sync(&padapter->mlmepriv.assoc_timer); - del_timer_sync(&padapter->securitypriv.tkip_timer); - del_timer_sync(&padapter->mlmepriv.scan_to_timer); - del_timer_sync(&padapter->mlmepriv.dhcp_timer); - del_timer_sync(&padapter->mlmepriv.wdg_timer); - del_timer_sync(&padapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer); -} - -static void init_default_value(struct _adapter *padapter) -{ - struct registry_priv *pregistrypriv = &padapter->registrypriv; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - - /*xmit_priv*/ - pxmitpriv->vcs_setting = pregistrypriv->vrtl_carrier_sense; - pxmitpriv->vcs = pregistrypriv->vcs_type; - pxmitpriv->vcs_type = pregistrypriv->vcs_type; - pxmitpriv->rts_thresh = pregistrypriv->rts_thresh; - pxmitpriv->frag_len = pregistrypriv->frag_thresh; - /* mlme_priv */ - /* Maybe someday we should rename this variable to "active_mode"(Jeff)*/ - pmlmepriv->passive_mode = 1; /* 1: active, 0: passive. */ - /*ht_priv*/ - { - int i; - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - - phtpriv->ampdu_enable = false;/*set to disabled*/ - for (i = 0; i < 16; i++) - phtpriv->baddbareq_issued[i] = false; - } - /*security_priv*/ - psecuritypriv->sw_encrypt = pregistrypriv->software_encrypt; - psecuritypriv->sw_decrypt = pregistrypriv->software_decrypt; - psecuritypriv->binstallGrpkey = _FAIL; - /*pwrctrl_priv*/ - /*registry_priv*/ - r8712_init_registrypriv_dev_network(padapter); - r8712_update_registrypriv_dev_network(padapter); - /*misc.*/ -} - -int r8712_init_drv_sw(struct _adapter *padapter) -{ - int ret; - - ret = r8712_init_cmd_priv(&padapter->cmdpriv); - if (ret) - return ret; - padapter->cmdpriv.padapter = padapter; - ret = r8712_init_evt_priv(&padapter->evtpriv); - if (ret) - goto free_cmd; - ret = r8712_init_mlme_priv(padapter); - if (ret) - goto free_evt; - ret = _r8712_init_xmit_priv(&padapter->xmitpriv, padapter); - if (ret) - goto free_mlme; - ret = _r8712_init_recv_priv(&padapter->recvpriv, padapter); - if (ret) - goto free_xmit; - memset((unsigned char *)&padapter->securitypriv, 0, - sizeof(struct security_priv)); - timer_setup(&padapter->securitypriv.tkip_timer, - r8712_use_tkipkey_handler, 0); - ret = _r8712_init_sta_priv(&padapter->stapriv); - if (ret) - goto free_recv; - padapter->stapriv.padapter = padapter; - r8712_init_bcmc_stainfo(padapter); - r8712_init_pwrctrl_priv(padapter); - mp871xinit(padapter); - init_default_value(padapter); - r8712_InitSwLeds(padapter); - mutex_init(&padapter->mutex_start); - - return 0; - -free_recv: - _r8712_free_recv_priv(&padapter->recvpriv); -free_xmit: - _free_xmit_priv(&padapter->xmitpriv); -free_mlme: - r8712_free_mlme_priv(&padapter->mlmepriv); -free_evt: - r8712_free_evt_priv(&padapter->evtpriv); -free_cmd: - r8712_free_cmd_priv(&padapter->cmdpriv); - return ret; -} - -void r8712_free_drv_sw(struct _adapter *padapter) -{ - r8712_free_cmd_priv(&padapter->cmdpriv); - r8712_free_evt_priv(&padapter->evtpriv); - r8712_DeInitSwLeds(padapter); - r8712_free_mlme_priv(&padapter->mlmepriv); - _free_xmit_priv(&padapter->xmitpriv); - _r8712_free_sta_priv(&padapter->stapriv); - _r8712_free_recv_priv(&padapter->recvpriv); - mp871xdeinit(padapter); -} - -static void enable_video_mode(struct _adapter *padapter, int cbw40_value) -{ - /* bit 8: - * 1 -> enable video mode to 96B AP - * 0 -> disable video mode to 96B AP - * bit 9: - * 1 -> enable 40MHz mode - * 0 -> disable 40MHz mode - * bit 10: - * 1 -> enable STBC - * 0 -> disable STBC - */ - u32 intcmd = 0xf4000500; /* enable bit8, bit10*/ - - if (cbw40_value) { - /* if the driver supports the 40M bandwidth, - * we can enable the bit 9. - */ - intcmd |= 0x200; - } - r8712_fw_cmd(padapter, intcmd); -} - -/* - * - * This function intends to handle the activation of an interface - * i.e. when it is brought Up/Active from a Down state. - * - */ -static int netdev_open(struct net_device *pnetdev) -{ - struct _adapter *padapter = netdev_priv(pnetdev); - - mutex_lock(&padapter->mutex_start); - if (!padapter->bup) { - padapter->driver_stopped = false; - padapter->surprise_removed = false; - padapter->bup = true; - if (rtl871x_hal_init(padapter) != _SUCCESS) - goto netdev_open_error; - if (!r8712_initmac) { - /* Use the mac address stored in the Efuse */ - eth_hw_addr_set(pnetdev, - padapter->eeprompriv.mac_addr); - } else { - /* We have to inform f/w to use user-supplied MAC - * address. - */ - msleep(200); - r8712_setMacAddr_cmd(padapter, - (const u8 *)pnetdev->dev_addr); - /* - * The "myid" function will get the wifi mac address - * from eeprompriv structure instead of netdev - * structure. So, we have to overwrite the mac_addr - * stored in the eeprompriv structure. In this case, - * the real mac address won't be used anymore. So that, - * the eeprompriv.mac_addr should store the mac which - * users specify. - */ - memcpy(padapter->eeprompriv.mac_addr, - pnetdev->dev_addr, ETH_ALEN); - } - if (start_drv_threads(padapter) != _SUCCESS) - goto netdev_open_error; - if (!padapter->dvobjpriv.inirp_init) - goto netdev_open_error; - else - padapter->dvobjpriv.inirp_init(padapter); - r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt, - padapter->registrypriv.smart_ps); - } - if (!netif_queue_stopped(pnetdev)) - netif_start_queue(pnetdev); - else - netif_wake_queue(pnetdev); - - if (video_mode) - enable_video_mode(padapter, cbw40_enable); - /* start driver mlme relation timer */ - start_drv_timers(padapter); - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK); - mutex_unlock(&padapter->mutex_start); - return 0; -netdev_open_error: - padapter->bup = false; - netif_carrier_off(pnetdev); - netif_stop_queue(pnetdev); - mutex_unlock(&padapter->mutex_start); - return -1; -} - -/* - * - * This function intends to handle the shutdown of an interface - * i.e. when it is brought Down from an Up/Active state. - * - */ -static int netdev_close(struct net_device *pnetdev) -{ - struct _adapter *padapter = netdev_priv(pnetdev); - - /* Close LED*/ - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_POWER_OFF); - msleep(200); - - /*s1.*/ - if (pnetdev) { - if (!netif_queue_stopped(pnetdev)) - netif_stop_queue(pnetdev); - } - /*s2.*/ - /*s2-1. issue disassoc_cmd to fw*/ - r8712_disassoc_cmd(padapter); - /*s2-2. indicate disconnect to os*/ - r8712_ind_disconnect(padapter); - /*s2-3.*/ - r8712_free_assoc_resources(padapter); - /*s2-4.*/ - r8712_free_network_queue(padapter); - return 0; -} - -#include "mlme_osdep.h" diff --git a/drivers/staging/rtl8712/osdep_intf.h b/drivers/staging/rtl8712/osdep_intf.h deleted file mode 100644 index 9e75116c987ec..0000000000000 --- a/drivers/staging/rtl8712/osdep_intf.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __OSDEP_INTF_H_ -#define __OSDEP_INTF_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -#define RND4(x) (((x >> 2) + ((x & 3) != 0)) << 2) - -struct intf_priv { - u8 *intf_dev; - /* when in USB, IO is through interrupt in/out endpoints */ - struct usb_device *udev; - struct urb *piorw_urb; - struct completion io_retevt_comp; -}; - -int r871x_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); - -#endif /*_OSDEP_INTF_H_*/ diff --git a/drivers/staging/rtl8712/osdep_service.h b/drivers/staging/rtl8712/osdep_service.h deleted file mode 100644 index 0d9bb42cbc589..0000000000000 --- a/drivers/staging/rtl8712/osdep_service.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __OSDEP_SERVICE_H_ -#define __OSDEP_SERVICE_H_ - -#define _SUCCESS 1 -#define _FAIL 0 - -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* Necessary because we use the proc fs */ - -#include "basic_types.h" - -struct __queue { - struct list_head queue; - spinlock_t lock; -}; - -#define _pkt struct sk_buff -#define _buffer unsigned char - -#define _init_queue(pqueue) \ - do { \ - INIT_LIST_HEAD(&((pqueue)->queue)); \ - spin_lock_init(&((pqueue)->lock)); \ - } while (0) - -static inline u32 end_of_queue_search(struct list_head *head, - struct list_head *plist) -{ - return (head == plist); -} - -static inline void flush_signals_thread(void) -{ - if (signal_pending(current)) - flush_signals(current); -} - -#endif - diff --git a/drivers/staging/rtl8712/recv_linux.c b/drivers/staging/rtl8712/recv_linux.c deleted file mode 100644 index 215fca4abb3a0..0000000000000 --- a/drivers/staging/rtl8712/recv_linux.c +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * recv_linux.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _RECV_OSDEP_C_ - -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "wifi.h" -#include "recv_osdep.h" -#include "osdep_intf.h" -#include "ethernet.h" -#include -#include "usb_ops.h" - -/*init os related resource in struct recv_priv*/ -/*alloc os related resource in union recv_frame*/ -void r8712_os_recv_resource_alloc(struct _adapter *padapter, - union recv_frame *precvframe) -{ - precvframe->u.hdr.pkt_newalloc = NULL; - precvframe->u.hdr.pkt = NULL; -} - -/*alloc os related resource in struct recv_buf*/ -int r8712_os_recvbuf_resource_alloc(struct _adapter *padapter, - struct recv_buf *precvbuf) -{ - int res = 0; - - precvbuf->irp_pending = false; - precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); - if (!precvbuf->purb) - res = -ENOMEM; - precvbuf->pskb = NULL; - precvbuf->pallocated_buf = NULL; - precvbuf->pbuf = NULL; - precvbuf->pdata = NULL; - precvbuf->phead = NULL; - precvbuf->ptail = NULL; - precvbuf->pend = NULL; - precvbuf->transfer_len = 0; - precvbuf->len = 0; - return res; -} - -/*free os related resource in struct recv_buf*/ -void r8712_os_recvbuf_resource_free(struct _adapter *padapter, - struct recv_buf *precvbuf) -{ - if (precvbuf->pskb) - dev_kfree_skb_any(precvbuf->pskb); - if (precvbuf->purb) { - usb_kill_urb(precvbuf->purb); - usb_free_urb(precvbuf->purb); - } -} - -void r8712_handle_tkip_mic_err(struct _adapter *adapter, u8 bgroup) -{ - union iwreq_data wrqu; - struct iw_michaelmicfailure ev; - struct mlme_priv *mlmepriv = &adapter->mlmepriv; - - memset(&ev, 0x00, sizeof(ev)); - if (bgroup) - ev.flags |= IW_MICFAILURE_GROUP; - else - ev.flags |= IW_MICFAILURE_PAIRWISE; - ev.src_addr.sa_family = ARPHRD_ETHER; - ether_addr_copy(ev.src_addr.sa_data, &mlmepriv->assoc_bssid[0]); - memset(&wrqu, 0x00, sizeof(wrqu)); - wrqu.data.length = sizeof(ev); - wireless_send_event(adapter->pnetdev, IWEVMICHAELMICFAILURE, &wrqu, - (char *)&ev); -} - -void r8712_recv_indicatepkt(struct _adapter *adapter, - union recv_frame *recvframe) -{ - struct recv_priv *recvpriv; - struct __queue *free_recv_queue; - _pkt *skb; - struct rx_pkt_attrib *attrib = &recvframe->u.hdr.attrib; - - recvpriv = &adapter->recvpriv; - free_recv_queue = &recvpriv->free_recv_queue; - skb = recvframe->u.hdr.pkt; - if (!skb) - goto _recv_indicatepkt_drop; - skb->data = recvframe->u.hdr.rx_data; - skb->len = recvframe->u.hdr.len; - skb_set_tail_pointer(skb, skb->len); - if ((attrib->tcpchk_valid == 1) && (attrib->tcp_chkrpt == 1)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - skb->dev = adapter->pnetdev; - skb->protocol = eth_type_trans(skb, adapter->pnetdev); - netif_rx(skb); - recvframe->u.hdr.pkt = NULL; /* pointers to NULL before - * r8712_free_recvframe() - */ - r8712_free_recvframe(recvframe, free_recv_queue); - return; -_recv_indicatepkt_drop: - /*enqueue back to free_recv_queue*/ - if (recvframe) - r8712_free_recvframe(recvframe, free_recv_queue); - recvpriv->rx_drop++; -} - -static void _r8712_reordering_ctrl_timeout_handler (struct timer_list *t) -{ - struct recv_reorder_ctrl *reorder_ctrl = - from_timer(reorder_ctrl, t, reordering_ctrl_timer); - - r8712_reordering_ctrl_timeout_handler(reorder_ctrl); -} - -void r8712_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl) -{ - timer_setup(&preorder_ctrl->reordering_ctrl_timer, - _r8712_reordering_ctrl_timeout_handler, 0); -} diff --git a/drivers/staging/rtl8712/recv_osdep.h b/drivers/staging/rtl8712/recv_osdep.h deleted file mode 100644 index fbe3f28685064..0000000000000 --- a/drivers/staging/rtl8712/recv_osdep.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RECV_OSDEP_H_ -#define __RECV_OSDEP_H_ - -#include "osdep_service.h" -#include "drv_types.h" -#include - -int _r8712_init_recv_priv(struct recv_priv *precvpriv, - struct _adapter *padapter); -void _r8712_free_recv_priv(struct recv_priv *precvpriv); -void r8712_recv_entry(union recv_frame *precv_frame); -void r8712_recv_indicatepkt(struct _adapter *adapter, - union recv_frame *precv_frame); -void r8712_handle_tkip_mic_err(struct _adapter *padapter, u8 bgroup); -int r8712_init_recv_priv(struct recv_priv *precvpriv, - struct _adapter *padapter); -void r8712_free_recv_priv(struct recv_priv *precvpriv); -void r8712_os_recv_resource_alloc(struct _adapter *padapter, - union recv_frame *precvframe); -int r8712_os_recvbuf_resource_alloc(struct _adapter *padapter, - struct recv_buf *precvbuf); -void r8712_os_recvbuf_resource_free(struct _adapter *padapter, - struct recv_buf *precvbuf); -void r8712_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl); - -#endif diff --git a/drivers/staging/rtl8712/rtl8712_bitdef.h b/drivers/staging/rtl8712/rtl8712_bitdef.h deleted file mode 100644 index a4a687dcc2e7b..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_bitdef.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ - -#ifndef __RTL8712_BITDEF_H__ -#define __RTL8712_BITDEF_H__ - -#include "rtl8712_cmdctrl_bitdef.h" -#include "rtl8712_syscfg_bitdef.h" -#include "rtl8712_macsetting_bitdef.h" -#include "rtl8712_timectrl_bitdef.h" -#include "rtl8712_fifoctrl_bitdef.h" -#include "rtl8712_ratectrl_bitdef.h" -#include "rtl8712_edcasetting_bitdef.h" -#include "rtl8712_wmac_bitdef.h" -#include "rtl8712_security_bitdef.h" -#include "rtl8712_powersave_bitdef.h" -#include "rtl8712_gp_bitdef.h" -#include "rtl8712_interrupt_bitdef.h" -#include "rtl8712_debugctrl_bitdef.h" - -#endif /* __RTL8712_BITDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_cmd.c b/drivers/staging/rtl8712/rtl8712_cmd.c deleted file mode 100644 index bb7db96ed8219..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_cmd.c +++ /dev/null @@ -1,409 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl8712_cmd.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _RTL8712_CMD_C_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "mlme_osdep.h" -#include "rtl871x_ioctl_set.h" - -static void check_hw_pbc(struct _adapter *padapter) -{ - u8 tmp1byte; - - r8712_write8(padapter, MAC_PINMUX_CTRL, (GPIOMUX_EN | GPIOSEL_GPIO)); - tmp1byte = r8712_read8(padapter, GPIO_IO_SEL); - tmp1byte &= ~(HAL_8192S_HW_GPIO_WPS_BIT); - r8712_write8(padapter, GPIO_IO_SEL, tmp1byte); - tmp1byte = r8712_read8(padapter, GPIO_CTRL); - if (tmp1byte == 0xff) - return; - if (tmp1byte & HAL_8192S_HW_GPIO_WPS_BIT) { - /* Here we only set bPbcPressed to true - * After trigger PBC, the variable will be set to false - */ - netdev_dbg(padapter->pnetdev, "CheckPbcGPIO - PBC is pressed !!!!\n"); - /* 0 is the default value and it means the application monitors - * the HW PBC doesn't provide its pid to driver. - */ - if (padapter->pid == 0) - return; - kill_pid(find_vpid(padapter->pid), SIGUSR1, 1); - } -} - -/* query rx phy status from fw. - * Adhoc mode: beacon. - * Infrastructure mode: beacon , data. - */ -static void query_fw_rx_phy_status(struct _adapter *padapter) -{ - u32 val32 = 0; - int pollingcnts = 50; - - if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) { - r8712_write32(padapter, IOCMD_CTRL_REG, 0xf4000001); - msleep(100); - /* Wait FW complete IO Cmd */ - while ((r8712_read32(padapter, IOCMD_CTRL_REG)) && - (pollingcnts > 0)) { - pollingcnts--; - msleep(20); - } - if (pollingcnts != 0) - val32 = r8712_read32(padapter, IOCMD_DATA_REG); - else /* time out */ - val32 = 0; - val32 >>= 4; - padapter->recvpriv.fw_rssi = - (u8)r8712_signal_scale_mapping(val32); - } -} - -/* check mlme, hw, phy, or dynamic algorithm status. */ -static void StatusWatchdogCallback(struct _adapter *padapter) -{ - check_hw_pbc(padapter); - query_fw_rx_phy_status(padapter); -} - -static void r871x_internal_cmd_hdl(struct _adapter *padapter, u8 *pbuf) -{ - struct drvint_cmd_parm *pdrvcmd; - - if (!pbuf) - return; - pdrvcmd = (struct drvint_cmd_parm *)pbuf; - switch (pdrvcmd->i_cid) { - case WDG_WK_CID: - StatusWatchdogCallback(padapter); - break; - default: - break; - } - kfree(pdrvcmd->pbuf); -} - -static u8 read_bbreg_hdl(struct _adapter *padapter, u8 *pbuf) -{ - struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; - - r8712_free_cmd_obj(pcmd); - return H2C_SUCCESS; -} - -static u8 write_bbreg_hdl(struct _adapter *padapter, u8 *pbuf) -{ - void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); - struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; - - pcmd_callback = cmd_callback[pcmd->cmdcode].callback; - if (!pcmd_callback) - r8712_free_cmd_obj(pcmd); - else - pcmd_callback(padapter, pcmd); - return H2C_SUCCESS; -} - -static u8 read_rfreg_hdl(struct _adapter *padapter, u8 *pbuf) -{ - u32 val; - void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); - struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; - - if (pcmd->rsp && pcmd->rspsz > 0) - memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz); - pcmd_callback = cmd_callback[pcmd->cmdcode].callback; - if (!pcmd_callback) - r8712_free_cmd_obj(pcmd); - else - pcmd_callback(padapter, pcmd); - return H2C_SUCCESS; -} - -static u8 write_rfreg_hdl(struct _adapter *padapter, u8 *pbuf) -{ - void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); - struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; - - pcmd_callback = cmd_callback[pcmd->cmdcode].callback; - if (!pcmd_callback) - r8712_free_cmd_obj(pcmd); - else - pcmd_callback(padapter, pcmd); - return H2C_SUCCESS; -} - -static u8 sys_suspend_hdl(struct _adapter *padapter, u8 *pbuf) -{ - struct cmd_obj *pcmd = (struct cmd_obj *)pbuf; - - r8712_free_cmd_obj(pcmd); - return H2C_SUCCESS; -} - -static struct cmd_obj *cmd_hdl_filter(struct _adapter *padapter, - struct cmd_obj *pcmd) -{ - struct cmd_obj *pcmd_r; - - if (!pcmd) - return pcmd; - pcmd_r = NULL; - - switch (pcmd->cmdcode) { - case GEN_CMD_CODE(_Read_BBREG): - read_bbreg_hdl(padapter, (u8 *)pcmd); - break; - case GEN_CMD_CODE(_Write_BBREG): - write_bbreg_hdl(padapter, (u8 *)pcmd); - break; - case GEN_CMD_CODE(_Read_RFREG): - read_rfreg_hdl(padapter, (u8 *)pcmd); - break; - case GEN_CMD_CODE(_Write_RFREG): - write_rfreg_hdl(padapter, (u8 *)pcmd); - break; - case GEN_CMD_CODE(_SetUsbSuspend): - sys_suspend_hdl(padapter, (u8 *)pcmd); - break; - case GEN_CMD_CODE(_JoinBss): - r8712_joinbss_reset(padapter); - /* Before set JoinBss_CMD to FW, driver must ensure FW is in - * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign - * new pwr_mode to Driver, instead of use workitem to change - * state. - */ - if (padapter->pwrctrlpriv.pwr_mode > PS_MODE_ACTIVE) { - padapter->pwrctrlpriv.pwr_mode = PS_MODE_ACTIVE; - mutex_lock(&padapter->pwrctrlpriv.mutex_lock); - r8712_set_rpwm(padapter, PS_STATE_S4); - mutex_unlock(&padapter->pwrctrlpriv.mutex_lock); - } - pcmd_r = pcmd; - break; - case _DRV_INT_CMD_: - r871x_internal_cmd_hdl(padapter, pcmd->parmbuf); - r8712_free_cmd_obj(pcmd); - pcmd_r = NULL; - break; - default: - pcmd_r = pcmd; - break; - } - return pcmd_r; /* if returning pcmd_r == NULL, pcmd must be free. */ -} - -u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd) -{ - int pollingcnts = 50; - - r8712_write32(pAdapter, IOCMD_CTRL_REG, cmd); - msleep(100); - while ((r8712_read32(pAdapter, IOCMD_CTRL_REG != 0)) && - (pollingcnts > 0)) { - pollingcnts--; - msleep(20); - } - if (pollingcnts == 0) - return false; - return true; -} - -void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag) -{ - if (flag == 0) /* set */ - r8712_write32(pAdapter, IOCMD_DATA_REG, *value); - else /* query */ - *value = r8712_read32(pAdapter, IOCMD_DATA_REG); -} - -int r8712_cmd_thread(void *context) -{ - struct cmd_obj *pcmd; - unsigned int cmdsz, wr_sz; - __le32 *pcmdbuf; - struct tx_desc *pdesc; - void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd); - struct _adapter *padapter = context; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct completion *cmd_queue_comp = - &pcmdpriv->cmd_queue_comp; - struct mutex *pwctrl_lock = &padapter->pwrctrlpriv.mutex_lock; - - allow_signal(SIGTERM); - while (1) { - if (wait_for_completion_interruptible(cmd_queue_comp)) - break; - if (padapter->driver_stopped || padapter->surprise_removed) - break; - if (r8712_register_cmd_alive(padapter)) - continue; -_next: - pcmd = r8712_dequeue_cmd(&pcmdpriv->cmd_queue); - if (!(pcmd)) { - r8712_unregister_cmd_alive(padapter); - continue; - } - pcmdbuf = (__le32 *)pcmdpriv->cmd_buf; - pdesc = (struct tx_desc *)pcmdbuf; - memset(pdesc, 0, TXDESC_SIZE); - pcmd = cmd_hdl_filter(padapter, pcmd); - if (pcmd) { /* if pcmd != NULL, cmd will be handled by f/w */ - struct dvobj_priv *pdvobj = &padapter->dvobjpriv; - u8 blnPending = 0; - u16 cmdcode = pcmd->cmdcode; - - pcmdpriv->cmd_issued_cnt++; - cmdsz = round_up(pcmd->cmdsz, 8); - wr_sz = TXDESC_SIZE + 8 + cmdsz; - pdesc->txdw0 |= cpu_to_le32((wr_sz - TXDESC_SIZE) & - 0x0000ffff); - if (pdvobj->ishighspeed) { - if ((wr_sz % 512) == 0) - blnPending = 1; - } else { - if ((wr_sz % 64) == 0) - blnPending = 1; - } - if (blnPending) { /* 32 bytes for TX Desc - 8 offset */ - pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + - OFFSET_SZ + 8) << OFFSET_SHT) & - 0x00ff0000); - } else { - pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + - OFFSET_SZ) << - OFFSET_SHT) & - 0x00ff0000); - } - pdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); - pdesc->txdw1 |= cpu_to_le32((0x13 << QSEL_SHT) & - 0x00001f00); - pcmdbuf += (TXDESC_SIZE >> 2); - *pcmdbuf = cpu_to_le32((cmdsz & 0x0000ffff) | - (pcmd->cmdcode << 16) | - (pcmdpriv->cmd_seq << 24)); - pcmdbuf += 2; /* 8 bytes alignment */ - memcpy((u8 *)pcmdbuf, pcmd->parmbuf, pcmd->cmdsz); - if (blnPending) - wr_sz += 8; /* Append 8 bytes */ - r8712_write_mem(padapter, RTL8712_DMA_H2CCMD, wr_sz, - (u8 *)pdesc); - pcmdpriv->cmd_seq++; - if (cmdcode == GEN_CMD_CODE(_CreateBss)) { - pcmd->res = H2C_SUCCESS; - pcmd_callback = cmd_callback[cmdcode].callback; - if (pcmd_callback) - pcmd_callback(padapter, pcmd); - continue; - } - if (cmdcode == GEN_CMD_CODE(_SetPwrMode)) { - if (padapter->pwrctrlpriv.bSleep) { - mutex_lock(pwctrl_lock); - r8712_set_rpwm(padapter, PS_STATE_S2); - mutex_unlock(pwctrl_lock); - } - } - r8712_free_cmd_obj(pcmd); - if (list_empty(&pcmdpriv->cmd_queue.queue)) { - r8712_unregister_cmd_alive(padapter); - continue; - } else { - goto _next; - } - } else { - goto _next; - } - flush_signals_thread(); - } - /* free all cmd_obj resources */ - do { - pcmd = r8712_dequeue_cmd(&pcmdpriv->cmd_queue); - if (!pcmd) - break; - r8712_free_cmd_obj(pcmd); - } while (1); - complete(&pcmdpriv->terminate_cmdthread_comp); - return 0; -} - -void r8712_event_handle(struct _adapter *padapter, __le32 *peventbuf) -{ - u8 evt_code, evt_seq; - u16 evt_sz; - void (*event_callback)(struct _adapter *dev, u8 *pbuf); - struct evt_priv *pevt_priv = &padapter->evtpriv; - - if (!peventbuf) - goto _abort_event_; - evt_sz = (u16)(le32_to_cpu(*peventbuf) & 0xffff); - evt_seq = (u8)((le32_to_cpu(*peventbuf) >> 24) & 0x7f); - evt_code = (u8)((le32_to_cpu(*peventbuf) >> 16) & 0xff); - /* checking event sequence... */ - if ((evt_seq & 0x7f) != pevt_priv->event_seq) { - pevt_priv->event_seq = ((evt_seq + 1) & 0x7f); - goto _abort_event_; - } - /* checking if event code is valid */ - if (evt_code >= MAX_C2HEVT) { - pevt_priv->event_seq = ((evt_seq + 1) & 0x7f); - goto _abort_event_; - } else if ((evt_code == GEN_EVT_CODE(_Survey)) && - (evt_sz > sizeof(struct wlan_bssid_ex))) { - pevt_priv->event_seq = ((evt_seq + 1) & 0x7f); - goto _abort_event_; - } - /* checking if event size match the event parm size */ - if ((wlanevents[evt_code].parmsize) && - (wlanevents[evt_code].parmsize != evt_sz)) { - pevt_priv->event_seq = ((evt_seq + 1) & 0x7f); - goto _abort_event_; - } else if ((evt_sz == 0) && (evt_code != GEN_EVT_CODE(_WPS_PBC))) { - pevt_priv->event_seq = ((evt_seq + 1) & 0x7f); - goto _abort_event_; - } - pevt_priv->event_seq++; /* update evt_seq */ - if (pevt_priv->event_seq > 127) - pevt_priv->event_seq = 0; - /* move to event content, 8 bytes alignment */ - peventbuf = peventbuf + 2; - event_callback = wlanevents[evt_code].event_callback; - if (event_callback) - event_callback(padapter, (u8 *)peventbuf); - pevt_priv->evt_done_cnt++; -_abort_event_: - return; -} diff --git a/drivers/staging/rtl8712/rtl8712_cmd.h b/drivers/staging/rtl8712/rtl8712_cmd.h deleted file mode 100644 index a34d0dd023f33..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_cmd.h +++ /dev/null @@ -1,231 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_CMD_H_ -#define __RTL8712_CMD_H_ - -#define CMD_HDR_SZ 8 - -u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd); -void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag); - -struct cmd_hdr { - u32 cmd_dw0; - u32 cmd_dw1; -}; - -enum rtl8712_h2c_cmd { - GEN_CMD_CODE(_Read_MACREG), /*0*/ - GEN_CMD_CODE(_Write_MACREG), - GEN_CMD_CODE(_Read_BBREG), - GEN_CMD_CODE(_Write_BBREG), - GEN_CMD_CODE(_Read_RFREG), - GEN_CMD_CODE(_Write_RFREG), /*5*/ - GEN_CMD_CODE(_Read_EEPROM), - GEN_CMD_CODE(_Write_EEPROM), - GEN_CMD_CODE(_Read_EFUSE), - GEN_CMD_CODE(_Write_EFUSE), - - GEN_CMD_CODE(_Read_CAM), /*10*/ - GEN_CMD_CODE(_Write_CAM), - GEN_CMD_CODE(_setBCNITV), - GEN_CMD_CODE(_setMBIDCFG), - GEN_CMD_CODE(_JoinBss), /*14*/ - GEN_CMD_CODE(_DisConnect), /*15*/ - GEN_CMD_CODE(_CreateBss), - GEN_CMD_CODE(_SetOpMode), - GEN_CMD_CODE(_SiteSurvey), /*18*/ - GEN_CMD_CODE(_SetAuth), - - GEN_CMD_CODE(_SetKey), /*20*/ - GEN_CMD_CODE(_SetStaKey), - GEN_CMD_CODE(_SetAssocSta), - GEN_CMD_CODE(_DelAssocSta), - GEN_CMD_CODE(_SetStaPwrState), - GEN_CMD_CODE(_SetBasicRate), /*25*/ - GEN_CMD_CODE(_GetBasicRate), - GEN_CMD_CODE(_SetDataRate), - GEN_CMD_CODE(_GetDataRate), - GEN_CMD_CODE(_SetPhyInfo), - - GEN_CMD_CODE(_GetPhyInfo), /*30*/ - GEN_CMD_CODE(_SetPhy), - GEN_CMD_CODE(_GetPhy), - GEN_CMD_CODE(_readRssi), - GEN_CMD_CODE(_readGain), - GEN_CMD_CODE(_SetAtim), /*35*/ - GEN_CMD_CODE(_SetPwrMode), - GEN_CMD_CODE(_JoinbssRpt), - GEN_CMD_CODE(_SetRaTable), - GEN_CMD_CODE(_GetRaTable), - - GEN_CMD_CODE(_GetCCXReport), /*40*/ - GEN_CMD_CODE(_GetDTMReport), - GEN_CMD_CODE(_GetTXRateStatistics), - GEN_CMD_CODE(_SetUsbSuspend), - GEN_CMD_CODE(_SetH2cLbk), - GEN_CMD_CODE(_AddBAReq), /*45*/ - - GEN_CMD_CODE(_SetChannel), /*46*/ -/* MP_OFFLOAD Start (47~54)*/ - GEN_CMD_CODE(_SetTxPower), - GEN_CMD_CODE(_SwitchAntenna), - GEN_CMD_CODE(_SetCrystalCap), - GEN_CMD_CODE(_SetSingleCarrierTx), /*50*/ - GEN_CMD_CODE(_SetSingleToneTx), - GEN_CMD_CODE(_SetCarrierSuppressionTx), - GEN_CMD_CODE(_SetContinuousTx), - GEN_CMD_CODE(_SwitchBandwidth), /*54*/ -/* MP_OFFLOAD End*/ - GEN_CMD_CODE(_TX_Beacon), /*55*/ - GEN_CMD_CODE(_SetPowerTracking), - GEN_CMD_CODE(_AMSDU_TO_AMPDU), /*57*/ - GEN_CMD_CODE(_SetMacAddress), /*58*/ - - GEN_CMD_CODE(_DisconnectCtrl), /*59*/ - GEN_CMD_CODE(_SetChannelPlan), /*60*/ - GEN_CMD_CODE(_DisconnectCtrlEx), /*61*/ - - /* To do, modify these h2c cmd, add or delete */ - GEN_CMD_CODE(_GetH2cLbk), - - /* WPS extra IE */ - GEN_CMD_CODE(_SetProbeReqExtraIE), - GEN_CMD_CODE(_SetAssocReqExtraIE), - GEN_CMD_CODE(_SetProbeRspExtraIE), - GEN_CMD_CODE(_SetAssocRspExtraIE), - - /* the following is driver will do */ - GEN_CMD_CODE(_GetCurDataRate), - - GEN_CMD_CODE(_GetTxRetrycnt), /* to record times that Tx retry to - * transmit packet after association - */ - GEN_CMD_CODE(_GetRxRetrycnt), /* to record total number of the - * received frame with ReTry bit set in - * the WLAN header - */ - - GEN_CMD_CODE(_GetBCNOKcnt), - GEN_CMD_CODE(_GetBCNERRcnt), - GEN_CMD_CODE(_GetCurTxPwrLevel), - - GEN_CMD_CODE(_SetDIG), - GEN_CMD_CODE(_SetRA), - GEN_CMD_CODE(_SetPT), - GEN_CMD_CODE(_ReadTSSI), - - MAX_H2CCMD -}; - -#define _GetBBReg_CMD_ _Read_BBREG_CMD_ -#define _SetBBReg_CMD_ _Write_BBREG_CMD_ -#define _GetRFReg_CMD_ _Read_RFREG_CMD_ -#define _SetRFReg_CMD_ _Write_RFREG_CMD_ -#define _DRV_INT_CMD_ (MAX_H2CCMD + 1) -#define _SetRFIntFs_CMD_ (MAX_H2CCMD + 2) - -#ifdef _RTL8712_CMD_C_ -static struct _cmd_callback cmd_callback[] = { - {GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/ - {GEN_CMD_CODE(_Write_MACREG), NULL}, - {GEN_CMD_CODE(_Read_BBREG), NULL}, - {GEN_CMD_CODE(_Write_BBREG), NULL}, - {GEN_CMD_CODE(_Read_RFREG), &r8712_getbbrfreg_cmdrsp_callback}, - {GEN_CMD_CODE(_Write_RFREG), NULL}, /*5*/ - {GEN_CMD_CODE(_Read_EEPROM), NULL}, - {GEN_CMD_CODE(_Write_EEPROM), NULL}, - {GEN_CMD_CODE(_Read_EFUSE), NULL}, - {GEN_CMD_CODE(_Write_EFUSE), NULL}, - - {GEN_CMD_CODE(_Read_CAM), NULL}, /*10*/ - {GEN_CMD_CODE(_Write_CAM), NULL}, - {GEN_CMD_CODE(_setBCNITV), NULL}, - {GEN_CMD_CODE(_setMBIDCFG), NULL}, - {GEN_CMD_CODE(_JoinBss), &r8712_joinbss_cmd_callback}, /*14*/ - {GEN_CMD_CODE(_DisConnect), &r8712_disassoc_cmd_callback}, /*15*/ - {GEN_CMD_CODE(_CreateBss), &r8712_createbss_cmd_callback}, - {GEN_CMD_CODE(_SetOpMode), NULL}, - {GEN_CMD_CODE(_SiteSurvey), &r8712_survey_cmd_callback}, /*18*/ - {GEN_CMD_CODE(_SetAuth), NULL}, - - {GEN_CMD_CODE(_SetKey), NULL}, /*20*/ - {GEN_CMD_CODE(_SetStaKey), &r8712_setstaKey_cmdrsp_callback}, - {GEN_CMD_CODE(_SetAssocSta), &r8712_setassocsta_cmdrsp_callback}, - {GEN_CMD_CODE(_DelAssocSta), NULL}, - {GEN_CMD_CODE(_SetStaPwrState), NULL}, - {GEN_CMD_CODE(_SetBasicRate), NULL}, /*25*/ - {GEN_CMD_CODE(_GetBasicRate), NULL}, - {GEN_CMD_CODE(_SetDataRate), NULL}, - {GEN_CMD_CODE(_GetDataRate), NULL}, - {GEN_CMD_CODE(_SetPhyInfo), NULL}, - - {GEN_CMD_CODE(_GetPhyInfo), NULL}, /*30*/ - {GEN_CMD_CODE(_SetPhy), NULL}, - {GEN_CMD_CODE(_GetPhy), NULL}, - {GEN_CMD_CODE(_readRssi), NULL}, - {GEN_CMD_CODE(_readGain), NULL}, - {GEN_CMD_CODE(_SetAtim), NULL}, /*35*/ - {GEN_CMD_CODE(_SetPwrMode), NULL}, - {GEN_CMD_CODE(_JoinbssRpt), NULL}, - {GEN_CMD_CODE(_SetRaTable), NULL}, - {GEN_CMD_CODE(_GetRaTable), NULL}, - - {GEN_CMD_CODE(_GetCCXReport), NULL}, /*40*/ - {GEN_CMD_CODE(_GetDTMReport), NULL}, - {GEN_CMD_CODE(_GetTXRateStatistics), NULL}, - {GEN_CMD_CODE(_SetUsbSuspend), NULL}, - {GEN_CMD_CODE(_SetH2cLbk), NULL}, - {GEN_CMD_CODE(_AddBAReq), NULL}, /*45*/ - - {GEN_CMD_CODE(_SetChannel), NULL}, /*46*/ -/* MP_OFFLOAD Start (47~54)*/ - {GEN_CMD_CODE(_SetTxPower), NULL}, - {GEN_CMD_CODE(_SwitchAntenna), NULL}, - {GEN_CMD_CODE(_SetCrystalCap), NULL}, - {GEN_CMD_CODE(_SetSingleCarrierTx), NULL}, /*50*/ - {GEN_CMD_CODE(_SetSingleToneTx), NULL}, - {GEN_CMD_CODE(_SetCarrierSuppressionTx), NULL}, - {GEN_CMD_CODE(_SetContinuousTx), NULL}, - {GEN_CMD_CODE(_SwitchBandwidth), NULL}, /*54*/ -/* MP_OFFLOAD End*/ - {GEN_CMD_CODE(_TX_Beacon), NULL}, /*55*/ - {GEN_CMD_CODE(_SetPowerTracking), NULL}, - {GEN_CMD_CODE(_AMSDU_TO_AMPDU), NULL}, /*57*/ - {GEN_CMD_CODE(_SetMacAddress), NULL}, /*58*/ - - {GEN_CMD_CODE(_DisconnectCtrl), NULL}, /*59*/ - {GEN_CMD_CODE(_SetChannelPlan), NULL}, /*60*/ - {GEN_CMD_CODE(_DisconnectCtrlEx), NULL}, /*61*/ - - /* To do, modify these h2c cmd, add or delete */ - {GEN_CMD_CODE(_GetH2cLbk), NULL}, - - {_SetProbeReqExtraIE_CMD_, NULL}, - {_SetAssocReqExtraIE_CMD_, NULL}, - {_SetProbeRspExtraIE_CMD_, NULL}, - {_SetAssocRspExtraIE_CMD_, NULL}, - {_GetCurDataRate_CMD_, NULL}, - {_GetTxRetrycnt_CMD_, NULL}, - {_GetRxRetrycnt_CMD_, NULL}, - {_GetBCNOKcnt_CMD_, NULL}, - {_GetBCNERRcnt_CMD_, NULL}, - {_GetCurTxPwrLevel_CMD_, NULL}, - {_SetDIG_CMD_, NULL}, - {_SetRA_CMD_, NULL}, - {_SetPT_CMD_, NULL}, - {GEN_CMD_CODE(_ReadTSSI), &r8712_readtssi_cmdrsp_callback} -}; -#endif - -#endif diff --git a/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h deleted file mode 100644 index 68bdec07f51e8..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h +++ /dev/null @@ -1,95 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_CMDCTRL_BITDEF_H__ -#define __RTL8712_CMDCTRL_BITDEF_H__ - -/* - * 2. Command Control Registers (Offset: 0x0040 - 0x004F) - */ -/*--------------------------------------------------------------------------*/ -/* 8192S (CMD) command register bits (Offset 0x40, 16 bits)*/ -/*--------------------------------------------------------------------------*/ -#define _APSDOFF_STATUS BIT(15) -#define _APSDOFF BIT(14) -#define _BBRSTn BIT(13) /*Enable OFDM/CCK*/ -#define _BB_GLB_RSTn BIT(12) /*Enable BB*/ -#define _SCHEDULE_EN BIT(10) /*Enable MAC scheduler*/ -#define _MACRXEN BIT(9) -#define _MACTXEN BIT(8) -#define _DDMA_EN BIT(7) /*FW off load function enable*/ -#define _FW2HW_EN BIT(6) /*MAC every module reset */ -#define _RXDMA_EN BIT(5) -#define _TXDMA_EN BIT(4) -#define _HCI_RXDMA_EN BIT(3) -#define _HCI_TXDMA_EN BIT(2) - -/*TXPAUSE*/ -#define _STOPHCCA BIT(6) -#define _STOPHIGH BIT(5) -#define _STOPMGT BIT(4) -#define _STOPVO BIT(3) -#define _STOPVI BIT(2) -#define _STOPBE BIT(1) -#define _STOPBK BIT(0) - -/*TCR*/ -#define _DISCW BIT(20) -#define _ICV BIT(19) -#define _CFEND_FMT BIT(17) -#define _CRC BIT(16) -#define _FWRDY BIT(7) -#define _BASECHG BIT(6) -#define _IMEM_RDY BIT(5) -#define _DMEM_CODE_DONE BIT(4) -#define _EMEM_CHK_RPT BIT(3) -#define _EMEM_CODE_DONE BIT(2) -#define _IMEM_CHK_RPT BIT(1) -#define _IMEM_CODE_DONE BIT(0) - -#define _TXDMA_INIT_VALUE (_IMEM_CHK_RPT | _EMEM_CHK_RPT) - -/*RCR*/ -#define _ENMBID BIT(27) -#define _APP_PHYST_RXFF BIT(25) -#define _APP_PHYST_STAFF BIT(24) -#define _CBSSID BIT(23) -#define _APWRMGT BIT(22) -#define _ADD3 BIT(21) -#define _AMF BIT(20) -#define _ACF BIT(19) -#define _ADF BIT(18) -#define _APP_MIC BIT(17) -#define _APP_ICV BIT(16) -#define _RXFTH_MSK 0x0000E000 -#define _RXFTH_SHT 13 -#define _AICV BIT(12) -#define _RXPKTLMT_MSK 0x00000FC0 -#define _RXPKTLMT_SHT 6 -#define _ACRC32 BIT(5) -#define _AB BIT(3) -#define _AM BIT(2) -#define _APM BIT(1) -#define _AAP BIT(0) - -/*MSR*/ -#define _NETTYPE_MSK 0x03 -#define _NETTYPE_SHT 0 - -/*BT*/ -#define _BTMODE_MSK 0x06 -#define _BTMODE_SHT 1 -#define _ENBT BIT(0) - -/*MBIDCTRL*/ -#define _ENMBID_MODE BIT(15) -#define _BCNNO_MSK 0x7000 -#define _BCNNO_SHT 12 -#define _BCNSPACE_MSK 0x0FFF -#define _BCNSPACE_SHT 0 - -#endif /* __RTL8712_CMDCTRL_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h deleted file mode 100644 index fc67771c89b78..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_CMDCTRL_REGDEF_H__ -#define __RTL8712_CMDCTRL_REGDEF_H__ - -#define CR (RTL8712_CMDCTRL_ + 0x0000) -#define TXPAUSE (RTL8712_CMDCTRL_ + 0x0002) -#define TCR (RTL8712_CMDCTRL_ + 0x0004) -#define RCR (RTL8712_CMDCTRL_ + 0x0008) -#define MSR (RTL8712_CMDCTRL_ + 0x000C) -#define SYSF_CFG (RTL8712_CMDCTRL_ + 0x000D) -#define MBIDCTRL (RTL8712_CMDCTRL_ + 0x000E) - -#endif /* __RTL8712_CMDCTRL_REGDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h deleted file mode 100644 index bb3863467f0d6..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_DEBUGCTRL_BITDEF_H__ -#define __RTL8712_DEBUGCTRL_BITDEF_H__ - -/*BIST*/ -#define _BIST_RST BIT(0) - -/*LMS*/ -#define _LMS_MSK 0x03 - -/*WDG_CTRL*/ -#define _OVSEL_MSK 0x0600 -#define _OVSEL_SHT 9 -#define _WDGCLR BIT(8) -#define _WDGEN_MSK 0x00FF -#define _WDGEN_SHT 0 - -/*INTM*/ -#define _TXTIMER_MSK 0xF000 -#define _TXTIMER_SHT 12 -#define _TXNUM_MSK 0x0F00 -#define _TXNUM_SHT 8 -#define _RXTIMER_MSK 0x00F0 -#define _RXTIMER_SHT 4 -#define _RXNUM_MSK 0x000F -#define _RXNUM_SHT 0 - -/*FDLOCKTURN0*/ -/*FDLOCKTURN1*/ -#define _TURN1 BIT(0) - -/*FDLOCKFLAG0*/ -/*FDLOCKFLAG1*/ -#define _LOCKFLAG1_MSK 0x03 - -#endif /* __RTL8712_DEBUGCTRL_BITDEF_H__ */ diff --git a/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h deleted file mode 100644 index 319220e9d53de..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_DEBUGCTRL_REGDEF_H__ -#define __RTL8712_DEBUGCTRL_REGDEF_H__ - -#define BIST (RTL8712_DEBUGCTRL_ + 0x00) -#define DBS (RTL8712_DEBUGCTRL_ + 0x04) -#define LMS (RTL8712_DEBUGCTRL_ + 0x05) -#define CPUINST (RTL8712_DEBUGCTRL_ + 0x08) -#define CPUCAUSE (RTL8712_DEBUGCTRL_ + 0x0C) -#define LBUS_ERR_ADDR (RTL8712_DEBUGCTRL_ + 0x10) -#define LBUS_ERR_CMD (RTL8712_DEBUGCTRL_ + 0x14) -#define LBUS_ERR_DATA_L (RTL8712_DEBUGCTRL_ + 0x18) -#define LBUS_ERR_DATA_H (RTL8712_DEBUGCTRL_ + 0x1C) -#define LBUS_EXCEPTION_ADDR (RTL8712_DEBUGCTRL_ + 0x20) -#define WDG_CTRL (RTL8712_DEBUGCTRL_ + 0x24) -#define INTMTU (RTL8712_DEBUGCTRL_ + 0x28) -#define INTM (RTL8712_DEBUGCTRL_ + 0x2A) -#define FDLOCKTURN0 (RTL8712_DEBUGCTRL_ + 0x2C) -#define FDLOCKTURN1 (RTL8712_DEBUGCTRL_ + 0x2D) -#define FDLOCKFLAG0 (RTL8712_DEBUGCTRL_ + 0x2E) -#define FDLOCKFLAG1 (RTL8712_DEBUGCTRL_ + 0x2F) -#define TRXPKTBUF_DBG_DATA (RTL8712_DEBUGCTRL_ + 0x30) -#define TRXPKTBUF_DBG_CTRL (RTL8712_DEBUGCTRL_ + 0x38) -#define DPLL_MON (RTL8712_DEBUGCTRL_ + 0x3A) - -#endif /* __RTL8712_DEBUGCTRL_REGDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h b/drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h deleted file mode 100644 index 9048d6a652969..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_EDCASETTING_BITDEF_H__ -#define __RTL8712_EDCASETTING_BITDEF_H__ - -/*EDCAPARAM*/ -#define _TXOPLIMIT_MSK 0xFFFF0000 -#define _TXOPLIMIT_SHT 16 -#define _ECWIN_MSK 0x0000FF00 -#define _ECWIN_SHT 8 -#define _AIFS_MSK 0x000000FF -#define _AIFS_SHT 0 - -/*BCNTCFG*/ -#define _BCNECW_MSK 0xFF00 -#define _BCNECW_SHT 8 -#define _BCNIFS_MSK 0x00FF -#define _BCNIFS_SHT 0 - -/*CWRR*/ -#define _CWRR_MSK 0x03FF - -/*ACMAVG*/ -#define _AVG_TIME_UP BIT(3) -#define _AVGPERIOD_MSK 0x03 - -/*ACMHWCTRL*/ -#define _VOQ_ACM_STATUS BIT(6) -#define _VIQ_ACM_STATUS BIT(5) -#define _BEQ_ACM_STATUS BIT(4) -#define _VOQ_ACM_EN BIT(3) -#define _VIQ_ACM_EN BIT(2) -#define _BEQ_ACM_EN BIT(1) -#define _ACMHWEN BIT(0) - -/*VO_ADMTIME*/ -#define _VO_ACM_RUT BIT(18) -#define _VO_ADMTIME_MSK 0x0003FFF - -/*VI_ADMTIME*/ -#define _VI_ACM_RUT BIT(18) -#define _VI_ADMTIME_MSK 0x0003FFF - -/*BE_ADMTIME*/ -#define _BE_ACM_RUT BIT(18) -#define _BE_ADMTIME_MSK 0x0003FFF - -/*Retry limit reg*/ -#define _SRL_MSK 0xFF00 -#define _SRL_SHT 8 -#define _LRL_MSK 0x00FF -#define _LRL_SHT 0 - -#endif /* __RTL8712_EDCASETTING_BITDEF_H__*/ diff --git a/drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h b/drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h deleted file mode 100644 index 02ec9f3bba665..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_EDCASETTING_REGDEF_H__ -#define __RTL8712_EDCASETTING_REGDEF_H__ - -#define EDCA_VO_PARAM (RTL8712_EDCASETTING_ + 0x00) -#define EDCA_VI_PARAM (RTL8712_EDCASETTING_ + 0x04) -#define EDCA_BE_PARAM (RTL8712_EDCASETTING_ + 0x08) -#define EDCA_BK_PARAM (RTL8712_EDCASETTING_ + 0x0C) -#define BCNTCFG (RTL8712_EDCASETTING_ + 0x10) -#define CWRR (RTL8712_EDCASETTING_ + 0x12) -#define ACMAVG (RTL8712_EDCASETTING_ + 0x16) -#define ACMHWCTRL (RTL8712_EDCASETTING_ + 0x17) -#define VO_ADMTIME (RTL8712_EDCASETTING_ + 0x18) -#define VI_ADMTIME (RTL8712_EDCASETTING_ + 0x1C) -#define BE_ADMTIME (RTL8712_EDCASETTING_ + 0x20) -#define RL (RTL8712_EDCASETTING_ + 0x24) - -#endif /* __RTL8712_EDCASETTING_REGDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_efuse.c b/drivers/staging/rtl8712/rtl8712_efuse.c deleted file mode 100644 index a39d6c06648f5..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_efuse.c +++ /dev/null @@ -1,563 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * rtl8712_efuse.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _RTL8712_EFUSE_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "rtl8712_efuse.h" - -/* reserve 3 bytes for HW stop read */ -static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/; - -static void efuse_reg_ctrl(struct _adapter *adapter, u8 bPowerOn) -{ - u8 tmpu8 = 0; - - if (bPowerOn) { - /* -----------------e-fuse pwr & clk reg ctrl --------------- - * Enable LDOE25 Macro Block - */ - tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3); - tmpu8 |= 0x80; - r8712_write8(adapter, EFUSE_TEST + 3, tmpu8); - msleep(20); /* for some platform , need some delay time */ - /* Change Efuse Clock for write action to 40MHZ */ - r8712_write8(adapter, EFUSE_CLK_CTRL, 0x03); - msleep(20); /* for some platform , need some delay time */ - } else { - /* -----------------e-fuse pwr & clk reg ctrl ----------------- - * Disable LDOE25 Macro Block - */ - tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3); - tmpu8 &= 0x7F; - r8712_write8(adapter, EFUSE_TEST + 3, tmpu8); - /* Change Efuse Clock for write action to 500K */ - r8712_write8(adapter, EFUSE_CLK_CTRL, 0x02); - } -} - -/* - * Before write E-Fuse, this function must be called. - */ -u8 r8712_efuse_reg_init(struct _adapter *adapter) -{ - return true; -} - -void r8712_efuse_reg_uninit(struct _adapter *adapter) -{ - efuse_reg_ctrl(adapter, false); -} - -static u8 efuse_one_byte_read(struct _adapter *adapter, u16 addr, u8 *data) -{ - u8 tmpidx = 0, bResult; - - /* -----------------e-fuse reg ctrl --------------------------------- */ - r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */ - r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) | - (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC)); - r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */ - /* wait for complete */ - while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && - (tmpidx < 100)) - tmpidx++; - if (tmpidx < 100) { - *data = r8712_read8(adapter, EFUSE_CTRL); - bResult = true; - } else { - *data = 0xff; - bResult = false; - } - return bResult; -} - -static u8 efuse_one_byte_write(struct _adapter *adapter, u16 addr, u8 data) -{ - u8 tmpidx = 0, bResult; - - /* -----------------e-fuse reg ctrl -------------------------------- */ - r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */ - r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) | - (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC)); - r8712_write8(adapter, EFUSE_CTRL, data); /* data */ - r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */ - /* wait for complete */ - while ((0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && - (tmpidx < 100)) - tmpidx++; - if (tmpidx < 100) - bResult = true; - else - bResult = false; - return bResult; -} - -static u8 efuse_one_byte_rw(struct _adapter *adapter, u8 bRead, u16 addr, - u8 *data) -{ - u8 tmpidx = 0, tmpv8 = 0, bResult; - - /* -----------------e-fuse reg ctrl --------------------------------- */ - r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */ - tmpv8 = ((u8)((addr >> 8) & 0x03)) | - (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC); - r8712_write8(adapter, EFUSE_CTRL + 2, tmpv8); - if (bRead) { - r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */ - while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && - (tmpidx < 100)) - tmpidx++; - if (tmpidx < 100) { - *data = r8712_read8(adapter, EFUSE_CTRL); - bResult = true; - } else { - *data = 0; - bResult = false; - } - } else { - r8712_write8(adapter, EFUSE_CTRL, *data); /* data */ - r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */ - while ((0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && - (tmpidx < 100)) - tmpidx++; - if (tmpidx < 100) - bResult = true; - else - bResult = false; - } - return bResult; -} - -static u8 efuse_is_empty(struct _adapter *adapter, u8 *empty) -{ - u8 value, ret = true; - - /* read one byte to check if E-Fuse is empty */ - if (efuse_one_byte_rw(adapter, true, 0, &value)) { - if (value == 0xFF) - *empty = true; - else - *empty = false; - } else { - ret = false; - } - return ret; -} - -void r8712_efuse_change_max_size(struct _adapter *adapter) -{ - u16 pre_pg_data_saddr = 0x1FB; - u16 i; - u16 pre_pg_data_size = 5; - u8 pre_pg_data[5]; - - for (i = 0; i < pre_pg_data_size; i++) - efuse_one_byte_read(adapter, pre_pg_data_saddr + i, - &pre_pg_data[i]); - if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) && - (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) && - (pre_pg_data[4] == 0x0C)) - efuse_available_max_size -= pre_pg_data_size; -} - -int r8712_efuse_get_max_size(struct _adapter *adapter) -{ - return efuse_available_max_size; -} - -static u8 calculate_word_cnts(const u8 word_en) -{ - u8 word_cnts = 0; - u8 word_idx; - - for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) - if (!(word_en & BIT(word_idx))) - word_cnts++; /* 0 : write enable */ - return word_cnts; -} - -static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata, - u8 *targetdata) -{ - u8 tmpindex = 0; - u8 word_idx, byte_idx; - - for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) { - if (!(word_en & BIT(word_idx))) { - byte_idx = word_idx * 2; - targetdata[byte_idx] = sourdata[tmpindex++]; - targetdata[byte_idx + 1] = sourdata[tmpindex++]; - } - } -} - -u16 r8712_efuse_get_current_size(struct _adapter *adapter) -{ - int bContinual = true; - u16 efuse_addr = 0; - u8 hworden = 0; - u8 efuse_data, word_cnts = 0; - - while (bContinual && efuse_one_byte_read(adapter, efuse_addr, &efuse_data) && - (efuse_addr < efuse_available_max_size)) { - if (efuse_data != 0xFF) { - hworden = efuse_data & 0x0F; - word_cnts = calculate_word_cnts(hworden); - /* read next header */ - efuse_addr = efuse_addr + (word_cnts * 2) + 1; - } else { - bContinual = false; - } - } - return efuse_addr; -} - -u8 r8712_efuse_pg_packet_read(struct _adapter *adapter, u8 offset, u8 *data) -{ - u8 hoffset = 0, hworden = 0, word_cnts = 0; - u16 efuse_addr = 0; - u8 efuse_data; - u8 tmpidx = 0; - u8 tmpdata[PGPKT_DATA_SIZE]; - u8 ret = true; - - if (!data) - return false; - if (offset > 0x0f) - return false; - memset(data, 0xFF, sizeof(u8) * PGPKT_DATA_SIZE); - while (efuse_addr < efuse_available_max_size) { - if (efuse_one_byte_read(adapter, efuse_addr, &efuse_data)) { - if (efuse_data == 0xFF) - break; - hoffset = (efuse_data >> 4) & 0x0F; - hworden = efuse_data & 0x0F; - word_cnts = calculate_word_cnts(hworden); - if (hoffset == offset) { - memset(tmpdata, 0xFF, PGPKT_DATA_SIZE); - for (tmpidx = 0; tmpidx < word_cnts * 2; - tmpidx++) { - if (efuse_one_byte_read(adapter, efuse_addr + 1 + tmpidx, - &efuse_data)) { - tmpdata[tmpidx] = efuse_data; - } else { - ret = false; - } - } - pgpacket_copy_data(hworden, tmpdata, data); - } - efuse_addr += 1 + (word_cnts * 2); - } else { - ret = false; - break; - } - } - return ret; -} - -static u8 fix_header(struct _adapter *adapter, u8 header, u16 header_addr) -{ - struct PGPKT_STRUCT pkt; - u8 offset, word_en, value; - u16 addr; - int i; - u8 ret = true; - - pkt.offset = GET_EFUSE_OFFSET(header); - pkt.word_en = GET_EFUSE_WORD_EN(header); - addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2; - if (addr > efuse_available_max_size) - return false; - /* retrieve original data */ - addr = 0; - while (addr < header_addr) { - if (!efuse_one_byte_read(adapter, addr++, &value)) { - ret = false; - break; - } - offset = GET_EFUSE_OFFSET(value); - word_en = GET_EFUSE_WORD_EN(value); - if (pkt.offset != offset) { - addr += calculate_word_cnts(word_en) * 2; - continue; - } - for (i = 0; i < PGPKG_MAX_WORDS; i++) { - if (!(BIT(i) & word_en)) - continue; - if (BIT(i) & pkt.word_en) { - if (efuse_one_byte_read(adapter, - addr, - &value)) - pkt.data[i * 2] = value; - else - return false; - if (efuse_one_byte_read(adapter, - addr + 1, - &value)) - pkt.data[i * 2 + 1] = value; - else - return false; - } - addr += 2; - } - } - if (addr != header_addr) - return false; - addr++; - /* fill original data */ - for (i = 0; i < PGPKG_MAX_WORDS; i++) { - if (BIT(i) & pkt.word_en) { - efuse_one_byte_write(adapter, addr, pkt.data[i * 2]); - efuse_one_byte_write(adapter, addr + 1, - pkt.data[i * 2 + 1]); - /* additional check */ - if (!efuse_one_byte_read(adapter, addr, &value)) { - ret = false; - } else if (pkt.data[i * 2] != value) { - ret = false; - if (value == 0xFF) /* write again */ - efuse_one_byte_write(adapter, addr, - pkt.data[i * 2]); - } - if (!efuse_one_byte_read(adapter, addr + 1, &value)) { - ret = false; - } else if (pkt.data[i * 2 + 1] != value) { - ret = false; - if (value == 0xFF) /* write again */ - efuse_one_byte_write(adapter, addr + 1, - pkt.data[i * 2 + - 1]); - } - } - addr += 2; - } - return ret; -} - -u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset, - const u8 word_en, const u8 *data) -{ - u8 pg_header = 0; - u16 efuse_addr = 0, curr_size = 0; - u8 efuse_data, target_word_cnts = 0; - int repeat_times; - int sub_repeat; - u8 bResult = true; - - /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ - efuse_data = r8712_read8(adapter, EFUSE_CLK_CTRL); - if (efuse_data != 0x03) - return false; - pg_header = MAKE_EFUSE_HEADER(offset, word_en); - target_word_cnts = calculate_word_cnts(word_en); - repeat_times = 0; - efuse_addr = 0; - while (efuse_addr < efuse_available_max_size) { - curr_size = r8712_efuse_get_current_size(adapter); - if ((curr_size + 1 + target_word_cnts * 2) > - efuse_available_max_size) - return false; /*target_word_cnts + pg header(1 byte)*/ - efuse_addr = curr_size; /* current size is also the last addr*/ - efuse_one_byte_write(adapter, efuse_addr, pg_header); /*hdr*/ - sub_repeat = 0; - /* check if what we read is what we write */ - while (!efuse_one_byte_read(adapter, efuse_addr, - &efuse_data)) { - if (++sub_repeat > _REPEAT_THRESHOLD_) { - bResult = false; /* continue to blind write */ - break; /* continue to blind write */ - } - } - if ((sub_repeat > _REPEAT_THRESHOLD_) || - (pg_header == efuse_data)) { - /* write header ok OR can't check header(creep) */ - u8 i; - - /* go to next address */ - efuse_addr++; - for (i = 0; i < target_word_cnts * 2; i++) { - efuse_one_byte_write(adapter, - efuse_addr + i, - *(data + i)); - if (!efuse_one_byte_read(adapter, - efuse_addr + i, - &efuse_data)) - bResult = false; - else if (*(data + i) != efuse_data) /* fail */ - bResult = false; - } - break; - } - /* write header fail */ - bResult = false; - if (efuse_data == 0xFF) - return bResult; /* nothing damaged. */ - /* call rescue procedure */ - if (!fix_header(adapter, efuse_data, efuse_addr)) - return false; /* rescue fail */ - - if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */ - break; - /* otherwise, take another risk... */ - } - return bResult; -} - -u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr, - u16 cnts, u8 *data) -{ - int i; - u8 res = true; - - if (start_addr > EFUSE_MAX_SIZE) - return false; - if (!bRead && ((start_addr + cnts) > - efuse_available_max_size)) - return false; - if (!bRead && !r8712_efuse_reg_init(adapter)) - return false; - /* -----------------e-fuse one byte read / write ---------------------*/ - for (i = 0; i < cnts; i++) { - if ((start_addr + i) > EFUSE_MAX_SIZE) { - res = false; - break; - } - res = efuse_one_byte_rw(adapter, bRead, start_addr + i, - data + i); - if (!bRead && !res) - break; - } - if (!bRead) - r8712_efuse_reg_uninit(adapter); - return res; -} - -u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data) -{ - u8 offset, ret = true; - u8 pktdata[PGPKT_DATA_SIZE]; - int i, idx; - - if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) - return false; - if (efuse_is_empty(adapter, &offset) && offset) { - for (i = 0; i < cnts; i++) - data[i] = 0xFF; - return ret; - } - offset = (addr >> 3) & 0xF; - ret = r8712_efuse_pg_packet_read(adapter, offset, pktdata); - i = addr & 0x7; /* pktdata index */ - idx = 0; /* data index */ - - do { - for (; i < PGPKT_DATA_SIZE; i++) { - data[idx++] = pktdata[i]; - if (idx == cnts) - return ret; - } - offset++; - if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) - ret = false; - i = 0; - } while (1); - return ret; -} - -u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts, - u8 *data) -{ - u8 offset, word_en, empty; - u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE]; - int i, j, idx; - - if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) - return false; - /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ - empty = r8712_read8(adapter, EFUSE_CLK_CTRL); - if (empty != 0x03) - return false; - if (efuse_is_empty(adapter, &empty)) { - if (empty) - memset(pktdata, 0xFF, PGPKT_DATA_SIZE); - } else { - return false; - } - offset = (addr >> 3) & 0xF; - if (!empty) - if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) - return false; - word_en = 0xF; - memset(newdata, 0xFF, PGPKT_DATA_SIZE); - i = addr & 0x7; /* pktdata index */ - j = 0; /* newdata index */ - idx = 0; /* data index */ - - if (i & 0x1) { - /* odd start */ - if (data[idx] != pktdata[i]) { - word_en &= ~BIT(i >> 1); - newdata[j++] = pktdata[i - 1]; - newdata[j++] = data[idx]; - } - i++; - idx++; - } - do { - for (; i < PGPKT_DATA_SIZE; i += 2) { - if ((cnts - idx) == 1) { - if (data[idx] != pktdata[i]) { - word_en &= ~BIT(i >> 1); - newdata[j++] = data[idx]; - newdata[j++] = pktdata[1 + 1]; - } - idx++; - break; - } - - if ((data[idx] != pktdata[i]) || (data[idx + 1] != - pktdata[i + 1])) { - word_en &= ~BIT(i >> 1); - newdata[j++] = data[idx]; - newdata[j++] = data[idx + 1]; - } - idx += 2; - - if (idx == cnts) - break; - } - - if (word_en != 0xF) - if (!r8712_efuse_pg_packet_write(adapter, offset, - word_en, newdata)) - return false; - if (idx == cnts) - break; - offset++; - if (!empty) - if (!r8712_efuse_pg_packet_read(adapter, offset, - pktdata)) - return false; - i = 0; - j = 0; - word_en = 0xF; - memset(newdata, 0xFF, PGPKT_DATA_SIZE); - } while (1); - - return true; -} diff --git a/drivers/staging/rtl8712/rtl8712_efuse.h b/drivers/staging/rtl8712/rtl8712_efuse.h deleted file mode 100644 index 7a49740212eb5..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_efuse.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __RTL8712_EFUSE_H__ -#define __RTL8712_EFUSE_H__ - -#include "osdep_service.h" - -#define _REPEAT_THRESHOLD_ 3 - -#define EFUSE_MAX_SIZE 512 -#define EFUSE_MAP_MAX_SIZE 128 - -#define PGPKG_MAX_WORDS 4 -#define PGPKT_DATA_SIZE 8 /* PGPKG_MAX_WORDS*2; BYTES sizeof(u8)*8*/ -#define MAX_PGPKT_SIZE 9 /* 1 + PGPKT_DATA_SIZE; header + 2 * 4 words (BYTES)*/ - -#define GET_EFUSE_OFFSET(header) ((header & 0xF0) >> 4) -#define GET_EFUSE_WORD_EN(header) (header & 0x0F) -#define MAKE_EFUSE_HEADER(offset, word_en) ((((offset) & 0x0F) << 4) | \ - ((word_en) & 0x0F)) -/*--------------------------------------------------------------------------*/ -struct PGPKT_STRUCT { - u8 offset; - u8 word_en; - u8 data[PGPKT_DATA_SIZE]; -}; - -/*--------------------------------------------------------------------------*/ -u8 r8712_efuse_reg_init(struct _adapter *padapter); -void r8712_efuse_reg_uninit(struct _adapter *padapter); -u16 r8712_efuse_get_current_size(struct _adapter *padapter); -int r8712_efuse_get_max_size(struct _adapter *padapter); -void r8712_efuse_change_max_size(struct _adapter *padapter); -u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, - u8 offset, u8 *data); -u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, - const u8 offset, const u8 word_en, - const u8 *data); -u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, - u16 start_addr, u16 cnts, u8 *data); -u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, - u16 cnts, u8 *data); -u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, - u16 cnts, u8 *data); -#endif diff --git a/drivers/staging/rtl8712/rtl8712_event.h b/drivers/staging/rtl8712/rtl8712_event.h deleted file mode 100644 index 0d3e5feadcc01..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_event.h +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL8712_EVENT_H_ -#define _RTL8712_EVENT_H_ - -void r8712_event_handle(struct _adapter *padapter, __le32 *peventbuf); -void r8712_got_addbareq_event_callback(struct _adapter *adapter, u8 *pbuf); - -enum rtl8712_c2h_event { - GEN_EVT_CODE(_Read_MACREG) = 0, /*0*/ - GEN_EVT_CODE(_Read_BBREG), - GEN_EVT_CODE(_Read_RFREG), - GEN_EVT_CODE(_Read_EEPROM), - GEN_EVT_CODE(_Read_EFUSE), - GEN_EVT_CODE(_Read_CAM), /*5*/ - GEN_EVT_CODE(_Get_BasicRate), - GEN_EVT_CODE(_Get_DataRate), - GEN_EVT_CODE(_Survey), /*8*/ - GEN_EVT_CODE(_SurveyDone), /*9*/ - - GEN_EVT_CODE(_JoinBss), /*10*/ - GEN_EVT_CODE(_AddSTA), - GEN_EVT_CODE(_DelSTA), - GEN_EVT_CODE(_AtimDone), - GEN_EVT_CODE(_TX_Report), - GEN_EVT_CODE(_CCX_Report), /*15*/ - GEN_EVT_CODE(_DTM_Report), - GEN_EVT_CODE(_TX_Rate_Statistics), - GEN_EVT_CODE(_C2HLBK), - GEN_EVT_CODE(_FWDBG), - GEN_EVT_CODE(_C2HFEEDBACK), /*20*/ - GEN_EVT_CODE(_ADDBA), - GEN_EVT_CODE(_C2HBCN), - GEN_EVT_CODE(_ReportPwrState), /*filen: only for PCIE, USB*/ - GEN_EVT_CODE(_WPS_PBC), /*24*/ - GEN_EVT_CODE(_ADDBAReq_Report), /*25*/ - MAX_C2HEVT -}; - -#ifdef _RTL8712_CMD_C_ - -static struct fwevent wlanevents[] = { - {0, NULL}, /*0*/ - {0, NULL}, - {0, NULL}, - {0, NULL}, - {0, NULL}, - {0, NULL}, - {0, NULL}, - {0, NULL}, - {0, &r8712_survey_event_callback}, /*8*/ - {sizeof(struct surveydone_event), - &r8712_surveydone_event_callback}, /*9*/ - - {0, &r8712_joinbss_event_callback}, /*10*/ - {sizeof(struct stassoc_event), &r8712_stassoc_event_callback}, - {sizeof(struct stadel_event), &r8712_stadel_event_callback}, - {0, &r8712_atimdone_event_callback}, - {0, NULL}, - {0, NULL}, /*15*/ - {0, NULL}, - {0, NULL}, - {0, NULL}, - {0, NULL}, /*fwdbg_event_callback},*/ - {0, NULL}, /*20*/ - {0, NULL}, - {0, NULL}, - {0, &r8712_cpwm_event_callback}, - {0, &r8712_wpspbc_event_callback}, - {0, &r8712_got_addbareq_event_callback}, -}; - -#endif/*_RTL8712_CMD_C_*/ - -#endif diff --git a/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h deleted file mode 100644 index f09645fa1886e..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_FIFOCTRL_BITDEF_H__ -#define __RTL8712_FIFOCTRL_BITDEF_H__ - -/*PBP*/ -#define _PSTX_MSK 0xF0 -#define _PSTX_SHT 4 -#define _PSRX_MSK 0x0F -#define _PSRX_SHT 0 - -/*TXFF_STATUS*/ -#define _TXSTATUS_OVF BIT(15) - -/*RXFF_STATUS*/ -#define _STATUSFF1_OVF BIT(7) -#define _STATUSFF1_EMPTY BIT(6) -#define _STATUSFF0_OVF BIT(5) -#define _STATUSFF0_EMPTY BIT(4) -#define _RXFF1_OVF BIT(3) -#define _RXFF1_EMPTY BIT(2) -#define _RXFF0_OVF BIT(1) -#define _RXFF0_EMPTY BIT(0) - -/*TXFF_EMPTY_TH*/ -#define _BKQ_EMPTY_TH_MSK 0x0F0000 -#define _BKQ_EMPTY_TH_SHT 16 -#define _BEQ_EMPTY_TH_MSK 0x00F000 -#define _BEQ_EMPTY_TH_SHT 12 -#define _VIQ_EMPTY_TH_MSK 0x000F00 -#define _VIQ_EMPTY_TH_SHT 8 -#define _VOQ_EMPTY_TH_MSK 0x0000F0 -#define _VOQ_EMPTY_TH_SHT 4 -#define _BMCQ_EMPTY_TH_MSK 0x00000F -#define _BMCQ_EMPTY_TH_SHT 0 - -/*SDIO_RX_BLKSZ*/ -#define _SDIO_RX_BLKSZ_MSK 0x07 - -/*RXDMA_CTRL*/ -#define _C2HFF_POLL BIT(4) -#define _RXPKT_POLL BIT(0) - -/*RXPKT_NUM*/ -#define _RXCMD_NUM_MSK 0xFF00 -#define _RXCMD_NUM_SHT 8 -#define _RXFF0_NUM_MSK 0x00FF -#define _RXFF0_NUM_SHT 0 - -/*FIFOPAGE2*/ -#define _PUB_AVAL_PG_MSK 0xFFFF0000 -#define _PUB_AVAL_PG_SHT 16 -#define _BCN_AVAL_PG_MSK 0x0000FFFF -#define _BCN_AVAL_PG_SHT 0 - -/*RX0PKTNUM*/ -#define _RXFF0_DEC_POLL BIT(15) -#define _RXFF0_PKT_DEC_NUM_MSK 0x3F00 -#define _RXFF0_PKT_DEC_NUM_SHT 8 -#define _RXFF0_PKTNUM_RPT_MSK 0x00FF -#define _RXFF0_PKTNUM_RPT_SHT 0 - -/*RX1PKTNUM*/ -#define _RXFF1_DEC_POLL BIT(15) -#define _RXFF1_PKT_DEC_NUM_MSK 0x3F00 -#define _RXFF1_PKT_DEC_NUM_SHT 8 -#define _RXFF1_PKTNUM_RPT_MSK 0x00FF -#define _RXFF1_PKTNUM_RPT_SHT 0 - -/*RXFLTMAP0*/ -#define _MGTFLT13EN BIT(13) -#define _MGTFLT12EN BIT(12) -#define _MGTFLT11EN BIT(11) -#define _MGTFLT10EN BIT(10) -#define _MGTFLT9EN BIT(9) -#define _MGTFLT8EN BIT(8) -#define _MGTFLT5EN BIT(5) -#define _MGTFLT4EN BIT(4) -#define _MGTFLT3EN BIT(3) -#define _MGTFLT2EN BIT(2) -#define _MGTFLT1EN BIT(1) -#define _MGTFLT0EN BIT(0) - -/*RXFLTMAP1*/ -#define _CTRLFLT15EN BIT(15) -#define _CTRLFLT14EN BIT(14) -#define _CTRLFLT13EN BIT(13) -#define _CTRLFLT12EN BIT(12) -#define _CTRLFLT11EN BIT(11) -#define _CTRLFLT10EN BIT(10) -#define _CTRLFLT9EN BIT(9) -#define _CTRLFLT8EN BIT(8) -#define _CTRLFLT7EN BIT(7) -#define _CTRLFLT6EN BIT(6) - -/*RXFLTMAP2*/ -#define _DATAFLT15EN BIT(15) -#define _DATAFLT14EN BIT(14) -#define _DATAFLT13EN BIT(13) -#define _DATAFLT12EN BIT(12) -#define _DATAFLT11EN BIT(11) -#define _DATAFLT10EN BIT(10) -#define _DATAFLT9EN BIT(9) -#define _DATAFLT8EN BIT(8) -#define _DATAFLT7EN BIT(7) -#define _DATAFLT6EN BIT(6) -#define _DATAFLT5EN BIT(5) -#define _DATAFLT4EN BIT(4) -#define _DATAFLT3EN BIT(3) -#define _DATAFLT2EN BIT(2) -#define _DATAFLT1EN BIT(1) -#define _DATAFLT0EN BIT(0) - -/*RXFLTMAP3*/ -#define _MESHAFLT1EN BIT(1) -#define _MESHAFLT0EN BIT(0) - -/*TXPKT_NUM_CTRL*/ -#define _TXPKTNUM_DEC BIT(8) -#define _TXPKTNUM_MSK 0x00FF -#define _TXPKTNUM_SHT 0 - -/*TXFF_PG_NUM*/ -#define _TXFF_PG_NUM_MSK 0x0FFF - -#endif /* __RTL8712_FIFOCTRL_BITDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h deleted file mode 100644 index 189fdeb16d7d3..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_FIFOCTRL_REGDEF_H__ -#define __RTL8712_FIFOCTRL_REGDEF_H__ - -#define RQPN (RTL8712_FIFOCTRL_ + 0x00) -#define RXFF_BNDY (RTL8712_FIFOCTRL_ + 0x0C) -#define RXRPT_BNDY (RTL8712_FIFOCTRL_ + 0x10) -#define TXPKTBUF_PGBNDY (RTL8712_FIFOCTRL_ + 0x14) -#define PBP (RTL8712_FIFOCTRL_ + 0x15) -#define RX_DRVINFO_SZ (RTL8712_FIFOCTRL_ + 0x16) -#define TXFF_STATUS (RTL8712_FIFOCTRL_ + 0x17) -#define RXFF_STATUS (RTL8712_FIFOCTRL_ + 0x18) -#define TXFF_EMPTY_TH (RTL8712_FIFOCTRL_ + 0x19) -#define SDIO_RX_BLKSZ (RTL8712_FIFOCTRL_ + 0x1C) -#define RXDMA_RXCTRL (RTL8712_FIFOCTRL_ + 0x1D) -#define RXPKT_NUM (RTL8712_FIFOCTRL_ + 0x1E) -#define RXPKT_NUM_C2H (RTL8712_FIFOCTRL_ + 0x1F) -#define C2HCMD_UDT_SIZE (RTL8712_FIFOCTRL_ + 0x20) -#define C2HCMD_UDT_ADDR (RTL8712_FIFOCTRL_ + 0x22) -#define FIFOPAGE2 (RTL8712_FIFOCTRL_ + 0x24) -#define FIFOPAGE1 (RTL8712_FIFOCTRL_ + 0x28) -#define FW_RSVD_PG_CTRL (RTL8712_FIFOCTRL_ + 0x30) -#define TXRPTFF_RDPTR (RTL8712_FIFOCTRL_ + 0x40) -#define TXRPTFF_WTPTR (RTL8712_FIFOCTRL_ + 0x44) -#define C2HFF_RDPTR (RTL8712_FIFOCTRL_ + 0x48) -#define C2HFF_WTPTR (RTL8712_FIFOCTRL_ + 0x4C) -#define RXFF0_RDPTR (RTL8712_FIFOCTRL_ + 0x50) -#define RXFF0_WTPTR (RTL8712_FIFOCTRL_ + 0x54) -#define RXFF1_RDPTR (RTL8712_FIFOCTRL_ + 0x58) -#define RXFF1_WTPTR (RTL8712_FIFOCTRL_ + 0x5C) -#define RXRPT0FF_RDPTR (RTL8712_FIFOCTRL_ + 0x60) -#define RXRPT0FF_WTPTR (RTL8712_FIFOCTRL_ + 0x64) -#define RXRPT1FF_RDPTR (RTL8712_FIFOCTRL_ + 0x68) -#define RXRPT1FF_WTPTR (RTL8712_FIFOCTRL_ + 0x6C) -#define RX0PKTNUM (RTL8712_FIFOCTRL_ + 0x72) -#define RX1PKTNUM (RTL8712_FIFOCTRL_ + 0x74) -#define RXFLTMAP0 (RTL8712_FIFOCTRL_ + 0x76) -#define RXFLTMAP1 (RTL8712_FIFOCTRL_ + 0x78) -#define RXFLTMAP2 (RTL8712_FIFOCTRL_ + 0x7A) -#define RXFLTMAP3 (RTL8712_FIFOCTRL_ + 0x7c) -#define TBDA (RTL8712_FIFOCTRL_ + 0x84) -#define THPDA (RTL8712_FIFOCTRL_ + 0x88) -#define TCDA (RTL8712_FIFOCTRL_ + 0x8C) -#define TMDA (RTL8712_FIFOCTRL_ + 0x90) -#define HDA (RTL8712_FIFOCTRL_ + 0x94) -#define TVODA (RTL8712_FIFOCTRL_ + 0x98) -#define TVIDA (RTL8712_FIFOCTRL_ + 0x9C) -#define TBEDA (RTL8712_FIFOCTRL_ + 0xA0) -#define TBKDA (RTL8712_FIFOCTRL_ + 0xA4) -#define RCDA (RTL8712_FIFOCTRL_ + 0xA8) -#define RDSA (RTL8712_FIFOCTRL_ + 0xAC) -#define TXPKT_NUM_CTRL (RTL8712_FIFOCTRL_ + 0xB0) -#define TXQ_PGADD (RTL8712_FIFOCTRL_ + 0xB3) -#define TXFF_PG_NUM (RTL8712_FIFOCTRL_ + 0xB4) - -#endif /* __RTL8712_FIFOCTRL_REGDEF_H__ */ diff --git a/drivers/staging/rtl8712/rtl8712_gp_bitdef.h b/drivers/staging/rtl8712/rtl8712_gp_bitdef.h deleted file mode 100644 index ee651fb3fde3f..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_gp_bitdef.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_GP_BITDEF_H__ -#define __RTL8712_GP_BITDEF_H__ - -/*GPIO_CTRL*/ -#define _GPIO_MOD_MSK 0xFF000000 -#define _GPIO_MOD_SHT 24 -#define _GPIO_IO_SEL_MSK 0x00FF0000 -#define _GPIO_IO_SEL_SHT 16 -#define _GPIO_OUT_MSK 0x0000FF00 -#define _GPIO_OUT_SHT 8 -#define _GPIO_IN_MSK 0x000000FF -#define _GPIO_IN_SHT 0 - -/*SYS_PINMUX_CFG*/ -#define _GPIOSEL_MSK 0x0003 -#define _GPIOSEL_SHT 0 - -/*LED_CFG*/ -#define _LED1SV BIT(7) -#define _LED1CM_MSK 0x0070 -#define _LED1CM_SHT 4 -#define _LED0SV BIT(3) -#define _LED0CM_MSK 0x0007 -#define _LED0CM_SHT 0 - -/*PHY_REG*/ -#define _HST_RDRDY_SHT 0 -#define _HST_RDRDY_MSK 0xFF -#define _HST_RDRDY BIT(_HST_RDRDY_SHT) -#define _CPU_WTBUSY_SHT 1 -#define _CPU_WTBUSY_MSK 0xFF -#define _CPU_WTBUSY BIT(_CPU_WTBUSY_SHT) - -/* 11. General Purpose Registers (Offset: 0x02E0 - 0x02FF)*/ - -/* 8192S GPIO Config Setting (offset 0x2F1, 1 byte)*/ - -/*----------------------------------------------------------------------------*/ - -#define GPIOMUX_EN BIT(3) /* When this bit is set to "1", - * GPIO PINs will switch to MAC - * GPIO Function - */ -#define GPIOSEL_GPIO 0 /* UART or JTAG or pure GPIO*/ -#define GPIOSEL_PHYDBG 1 /* PHYDBG*/ -#define GPIOSEL_BT 2 /* BT_coex*/ -#define GPIOSEL_WLANDBG 3 /* WLANDBG*/ -#define GPIOSEL_GPIO_MASK (~(BIT(0) | BIT(1))) -/* HW Radio OFF switch (GPIO BIT) */ -#define HAL_8192S_HW_GPIO_OFF_BIT BIT(3) -#define HAL_8192S_HW_GPIO_OFF_MASK 0xF7 -#define HAL_8192S_HW_GPIO_WPS_BIT BIT(4) - -#endif /*__RTL8712_GP_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_gp_regdef.h b/drivers/staging/rtl8712/rtl8712_gp_regdef.h deleted file mode 100644 index 892a7fb139235..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_gp_regdef.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_GP_REGDEF_H__ -#define __RTL8712_GP_REGDEF_H__ - -#define PSTIMER (RTL8712_GP_ + 0x00) -#define TIMER1 (RTL8712_GP_ + 0x04) -#define TIMER2 (RTL8712_GP_ + 0x08) -#define GPIO_CTRL (RTL8712_GP_ + 0x0C) -#define GPIO_IO_SEL (RTL8712_GP_ + 0x0E) -#define GPIO_INTCTRL (RTL8712_GP_ + 0x10) -#define MAC_PINMUX_CTRL (RTL8712_GP_ + 0x11) -#define LEDCFG (RTL8712_GP_ + 0x12) -#define PHY_REG_RPT (RTL8712_GP_ + 0x13) -#define PHY_REG_DATA (RTL8712_GP_ + 0x14) - -#endif /*__RTL8712_GP_REGDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_hal.h b/drivers/staging/rtl8712/rtl8712_hal.h deleted file mode 100644 index 66cc4645e2d1c..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_hal.h +++ /dev/null @@ -1,142 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_HAL_H__ -#define __RTL8712_HAL_H__ - -enum _HW_VERSION { - RTL8712_FPGA, - RTL8712_1stCUT, /*A Cut (RTL8712_ASIC)*/ - RTL8712_2ndCUT, /*B Cut*/ - RTL8712_3rdCUT, /*C Cut*/ -}; - -enum _LOOPBACK_TYPE { - RTL8712_AIR_TRX = 0, - RTL8712_MAC_LBK, - RTL8712_BB_LBK, - RTL8712_MAC_FW_LBK = 4, - RTL8712_BB_FW_LBK = 8, -}; - -enum RTL871X_HCI_TYPE { - RTL8712_SDIO, - RTL8712_USB, -}; - -enum RTL8712_RF_CONFIG { - RTL8712_RF_1T1R, - RTL8712_RF_1T2R, - RTL8712_RF_2T2R -}; - -enum _RTL8712_HCI_TYPE_ { - RTL8712_HCI_TYPE_PCIE = 0x01, - RTL8712_HCI_TYPE_AP_PCIE = 0x81, - RTL8712_HCI_TYPE_USB = 0x02, - RTL8712_HCI_TYPE_92USB = 0x02, - RTL8712_HCI_TYPE_AP_USB = 0x82, - RTL8712_HCI_TYPE_72USB = 0x12, - RTL8712_HCI_TYPE_SDIO = 0x04, - RTL8712_HCI_TYPE_72SDIO = 0x14 -}; - -struct fw_priv { /*8-bytes alignment required*/ - /*--- long word 0 ----*/ - unsigned char signature_0; /*0x12: CE product, 0x92: IT product*/ - unsigned char signature_1; /*0x87: CE product, 0x81: IT product*/ - unsigned char hci_sel; /*0x81: PCI-AP, 01:PCIe, 02: 92S-U, 0x82: USB-AP, - * 0x12: 72S-U, 03:SDIO - */ - unsigned char chip_version; /*the same value as register value*/ - unsigned char customer_ID_0; /*customer ID low byte*/ - unsigned char customer_ID_1; /*customer ID high byte*/ - unsigned char rf_config; /*0x11: 1T1R, 0x12: 1T2R, 0x92: 1T2R turbo, - * 0x22: 2T2R - */ - unsigned char usb_ep_num; /* 4: 4EP, 6: 6EP, 11: 11EP*/ - /*--- long word 1 ----*/ - unsigned char regulatory_class_0; /*regulatory class bit map 0*/ - unsigned char regulatory_class_1; /*regulatory class bit map 1*/ - unsigned char regulatory_class_2; /*regulatory class bit map 2*/ - unsigned char regulatory_class_3; /*regulatory class bit map 3*/ - unsigned char rfintfs; /* 0:SWSI, 1:HWSI, 2:HWPI*/ - unsigned char def_nettype; - unsigned char turbo_mode; - unsigned char low_power_mode;/* 0: normal mode, 1: low power mode*/ - /*--- long word 2 ----*/ - unsigned char lbk_mode; /*0x00: normal, 0x03: MACLBK, 0x01: PHYLBK*/ - unsigned char mp_mode; /* 1: for MP use, 0: for normal driver */ - unsigned char vcs_type; /* 0:off 1:on 2:auto */ - unsigned char vcs_mode; /* 1:RTS/CTS 2:CTS to self */ - unsigned char rsvd022; - unsigned char rsvd023; - unsigned char rsvd024; - unsigned char rsvd025; - /*--- long word 3 ----*/ - unsigned char qos_en; /*1: QoS enable*/ - unsigned char bw_40MHz_en; /*1: 40MHz BW enable*/ - unsigned char AMSDU2AMPDU_en; /*1: 4181 convert AMSDU to AMPDU, - * 0: disable - */ - unsigned char AMPDU_en; /*1: 11n AMPDU enable*/ - unsigned char rate_control_offload; /*1: FW offloads,0: driver handles*/ - unsigned char aggregation_offload; /*1: FW offloads,0: driver handles*/ - unsigned char rsvd030; - unsigned char rsvd031; - /*--- long word 4 ----*/ - unsigned char beacon_offload; /* 1. FW offloads, 0: driver handles*/ - unsigned char MLME_offload; /* 2. FW offloads, 0: driver handles*/ - unsigned char hwpc_offload; /* 3. FW offloads, 0: driver handles*/ - unsigned char tcp_checksum_offload; /*4. FW offloads,0: driver handles*/ - unsigned char tcp_offload; /* 5. FW offloads, 0: driver handles*/ - unsigned char ps_control_offload; /* 6. FW offloads, 0: driver handles*/ - unsigned char WWLAN_offload; /* 7. FW offloads, 0: driver handles*/ - unsigned char rsvd040; - /*--- long word 5 ----*/ - unsigned char tcp_tx_frame_len_L; /*tcp tx packet length low byte*/ - unsigned char tcp_tx_frame_len_H; /*tcp tx packet length high byte*/ - unsigned char tcp_rx_frame_len_L; /*tcp rx packet length low byte*/ - unsigned char tcp_rx_frame_len_H; /*tcp rx packet length high byte*/ - unsigned char rsvd050; - unsigned char rsvd051; - unsigned char rsvd052; - unsigned char rsvd053; -}; - -struct fw_hdr {/*8-byte alignment required*/ - unsigned short signature; - unsigned short version; /* 0x8000 ~ 0x8FFF for FPGA version, - * 0x0000 ~ 0x7FFF for ASIC version, - */ - unsigned int dmem_size; /*define the size of boot loader*/ - unsigned int img_IMEM_size; /*define the size of FW in IMEM*/ - unsigned int img_SRAM_size; /*define the size of FW in SRAM*/ - unsigned int fw_priv_sz; /*define the size of DMEM variable*/ - unsigned short efuse_addr; - unsigned short h2ccnd_resp_addr; - unsigned int SVNRevision; - unsigned int release_time; /*Mon:Day:Hr:Min*/ - struct fw_priv fwpriv; -}; - -struct hal_priv { - /*Endpoint handles*/ - struct net_device *pipehdls_r8712[10]; - u8 (*hal_bus_init)(struct _adapter *adapter); -}; - -uint rtl8712_hal_init(struct _adapter *padapter); -int rtl871x_load_fw(struct _adapter *padapter); - -#endif diff --git a/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h b/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h deleted file mode 100644 index e9732a1bcd7ef..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_INTERRUPT_BITDEF_H__ -#define __RTL8712_INTERRUPT_BITDEF_H__ - -/*HIMR*/ -/*HISR*/ -#define _CPUERR BIT(29) -#define _ATIMEND BIT(28) -#define _TXBCNOK BIT(27) -#define _TXBCNERR BIT(26) -#define _BCNDMAINT4 BIT(25) -#define _BCNDMAINT3 BIT(24) -#define _BCNDMAINT2 BIT(23) -#define _BCNDMAINT1 BIT(22) -#define _BCNDOK4 BIT(21) -#define _BCNDOK3 BIT(20) -#define _BCNDOK2 BIT(19) -#define _BCNDOK1 BIT(18) -#define _TIMEOUT2 BIT(17) -#define _TIMEOUT1 BIT(16) -#define _TXFOVW BIT(15) -#define _PSTIMEOUT BIT(14) -#define _BCNDMAINT0 BIT(13) -#define _FOVW BIT(12) -#define _RDU BIT(11) -#define _RXCMDOK BIT(10) -#define _BCNDOK0 BIT(9) -#define _HIGHDOK BIT(8) -#define _COMDOK BIT(7) -#define _MGTDOK BIT(6) -#define _HCCADOK BIT(5) -#define _BKDOK BIT(4) -#define _BEDOK BIT(3) -#define _VIDOK BIT(2) -#define _VODOK BIT(1) -#define _RXOK BIT(0) - -#endif /*__RTL8712_INTERRUPT_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_io.c b/drivers/staging/rtl8712/rtl8712_io.c deleted file mode 100644 index 384cbdb05e196..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_io.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl8712_io.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE . - * Larry Finger - * - ******************************************************************************/ - -#define _RTL8712_IO_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "rtl871x_io.h" -#include "osdep_intf.h" -#include "usb_ops.h" - -u8 r8712_read8(struct _adapter *adapter, u32 addr) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - return hdl->io_ops._read8(hdl, addr); -} - -u16 r8712_read16(struct _adapter *adapter, u32 addr) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - return hdl->io_ops._read16(hdl, addr); -} - -u32 r8712_read32(struct _adapter *adapter, u32 addr) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - return hdl->io_ops._read32(hdl, addr); -} - -void r8712_write8(struct _adapter *adapter, u32 addr, u8 val) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - hdl->io_ops._write8(hdl, addr, val); -} - -void r8712_write16(struct _adapter *adapter, u32 addr, u16 val) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - hdl->io_ops._write16(hdl, addr, val); -} - -void r8712_write32(struct _adapter *adapter, u32 addr, u32 val) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - hdl->io_ops._write32(hdl, addr, val); -} - -void r8712_read_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - if (adapter->driver_stopped || adapter->surprise_removed) - return; - - hdl->io_ops._read_mem(hdl, addr, cnt, pmem); -} - -void r8712_write_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - hdl->io_ops._write_mem(hdl, addr, cnt, pmem); -} - -void r8712_read_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - if (adapter->driver_stopped || adapter->surprise_removed) - return; - - hdl->io_ops._read_port(hdl, addr, cnt, pmem); -} - -void r8712_write_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem) -{ - struct intf_hdl *hdl = &adapter->pio_queue->intf; - - hdl->io_ops._write_port(hdl, addr, cnt, pmem); -} diff --git a/drivers/staging/rtl8712/rtl8712_led.c b/drivers/staging/rtl8712/rtl8712_led.c deleted file mode 100644 index 6d9be5dec4e72..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_led.c +++ /dev/null @@ -1,1830 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl8712_led.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#include "drv_types.h" - -/*=========================================================================== - * Constant. - *=========================================================================== - - * - * Default LED behavior. - */ -#define LED_BLINK_NORMAL_INTERVAL 100 -#define LED_BLINK_SLOWLY_INTERVAL 200 -#define LED_BLINK_LONG_INTERVAL 400 - -#define LED_BLINK_NO_LINK_INTERVAL_ALPHA 1000 -#define LED_BLINK_LINK_INTERVAL_ALPHA 500 -#define LED_BLINK_SCAN_INTERVAL_ALPHA 180 -#define LED_BLINK_FASTER_INTERVAL_ALPHA 50 -#define LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA 5000 - -/*=========================================================================== - * LED object. - *=========================================================================== - */ -enum _LED_STATE_871x { - LED_UNKNOWN = 0, - LED_STATE_ON = 1, - LED_STATE_OFF = 2, - LED_BLINK_NORMAL = 3, - LED_BLINK_SLOWLY = 4, - LED_POWER_ON_BLINK = 5, - LED_SCAN_BLINK = 6, /* LED is blinking during scanning period, - * the # of times to blink is depend on time - * for scanning. - */ - LED_NO_LINK_BLINK = 7, /* LED is blinking during no link state. */ - LED_BLINK_StartToBlink = 8,/* Customized for Sercomm Printer - * Server case - */ - LED_BLINK_WPS = 9, /* LED is blinkg during WPS communication */ - LED_TXRX_BLINK = 10, - LED_BLINK_WPS_STOP = 11, /*for ALPHA */ - LED_BLINK_WPS_STOP_OVERLAP = 12, /*for BELKIN */ -}; - -/*=========================================================================== - * Prototype of protected function. - *=========================================================================== - */ -static void BlinkTimerCallback(struct timer_list *t); - -static void BlinkWorkItemCallback(struct work_struct *work); -/*=========================================================================== - * LED_819xUsb routines. - *=========================================================================== - * - * - * - * Description: - * Initialize an LED_871x object. - */ -static void InitLed871x(struct _adapter *padapter, struct LED_871x *pLed, - enum LED_PIN_871x LedPin) -{ - pLed->padapter = padapter; - pLed->LedPin = LedPin; - pLed->CurrLedState = LED_STATE_OFF; - pLed->bLedOn = false; - pLed->bLedBlinkInProgress = false; - pLed->BlinkTimes = 0; - pLed->BlinkingLedState = LED_UNKNOWN; - timer_setup(&pLed->BlinkTimer, BlinkTimerCallback, 0); - INIT_WORK(&pLed->BlinkWorkItem, BlinkWorkItemCallback); -} - -/* - * Description: - * DeInitialize an LED_871x object. - */ -static void DeInitLed871x(struct LED_871x *pLed) -{ - del_timer_sync(&pLed->BlinkTimer); - /* We should reset bLedBlinkInProgress if we cancel - * the LedControlTimer, - */ - pLed->bLedBlinkInProgress = false; -} - -/* - * Description: - * Turn on LED according to LedPin specified. - */ -static void SwLedOn(struct _adapter *padapter, struct LED_871x *pLed) -{ - u8 LedCfg; - - if (padapter->surprise_removed || padapter->driver_stopped) - return; - LedCfg = r8712_read8(padapter, LEDCFG); - switch (pLed->LedPin) { - case LED_PIN_GPIO0: - break; - case LED_PIN_LED0: - /* SW control led0 on.*/ - r8712_write8(padapter, LEDCFG, LedCfg & 0xf0); - break; - case LED_PIN_LED1: - /* SW control led1 on.*/ - r8712_write8(padapter, LEDCFG, LedCfg & 0x0f); - break; - default: - break; - } - pLed->bLedOn = true; -} - -/* - * Description: - * Turn off LED according to LedPin specified. - */ -static void SwLedOff(struct _adapter *padapter, struct LED_871x *pLed) -{ - u8 LedCfg; - - if (padapter->surprise_removed || padapter->driver_stopped) - return; - LedCfg = r8712_read8(padapter, LEDCFG); - switch (pLed->LedPin) { - case LED_PIN_GPIO0: - break; - case LED_PIN_LED0: - LedCfg &= 0xf0; /* Set to software control.*/ - r8712_write8(padapter, LEDCFG, (LedCfg | BIT(3))); - break; - case LED_PIN_LED1: - LedCfg &= 0x0f; /* Set to software control.*/ - r8712_write8(padapter, LEDCFG, (LedCfg | BIT(7))); - break; - default: - break; - } - pLed->bLedOn = false; -} - -/*=========================================================================== - * Interface to manipulate LED objects. - *=========================================================================== - * - * Description: - * Initialize all LED_871x objects. - */ -void r8712_InitSwLeds(struct _adapter *padapter) -{ - struct led_priv *pledpriv = &padapter->ledpriv; - - pledpriv->LedControlHandler = LedControl871x; - InitLed871x(padapter, &pledpriv->SwLed0, LED_PIN_LED0); - InitLed871x(padapter, &pledpriv->SwLed1, LED_PIN_LED1); -} - -/* Description: - * DeInitialize all LED_819xUsb objects. - */ -void r8712_DeInitSwLeds(struct _adapter *padapter) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - - DeInitLed871x(&ledpriv->SwLed0); - DeInitLed871x(&ledpriv->SwLed1); -} - -/* Description: - * Implementation of LED blinking behavior. - * It toggle off LED and schedule corresponding timer if necessary. - */ -static void SwLedBlink(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - u8 bStopBlinking = false; - - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - SwLedOff(padapter, pLed); - /* Determine if we shall change LED state again. */ - pLed->BlinkTimes--; - switch (pLed->CurrLedState) { - case LED_BLINK_NORMAL: - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - break; - case LED_BLINK_StartToBlink: - if (check_fwstate(pmlmepriv, _FW_LINKED) && - (pmlmepriv->fw_state & WIFI_STATION_STATE)) - bStopBlinking = true; - if (check_fwstate(pmlmepriv, _FW_LINKED) && - ((pmlmepriv->fw_state & WIFI_ADHOC_STATE) || - (pmlmepriv->fw_state & WIFI_ADHOC_MASTER_STATE))) - bStopBlinking = true; - else if (pLed->BlinkTimes == 0) - bStopBlinking = true; - break; - case LED_BLINK_WPS: - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - break; - default: - bStopBlinking = true; - break; - } - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED) && - !pLed->bLedOn) - SwLedOn(padapter, pLed); - else if (check_fwstate(pmlmepriv, _FW_LINKED) && pLed->bLedOn) - SwLedOff(padapter, pLed); - pLed->BlinkTimes = 0; - pLed->bLedBlinkInProgress = false; - } else { - /* Assign LED state to toggle. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - - /* Schedule a timer to toggle LED state. */ - switch (pLed->CurrLedState) { - case LED_BLINK_NORMAL: - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - break; - case LED_BLINK_SLOWLY: - case LED_BLINK_StartToBlink: - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL)); - break; - case LED_BLINK_WPS: - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LONG_INTERVAL)); - break; - default: - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL)); - break; - } - } -} - -static void SwLedBlink1(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - struct led_priv *ledpriv = &padapter->ledpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct eeprom_priv *peeprompriv = &padapter->eeprompriv; - struct LED_871x *pLed1 = &ledpriv->SwLed1; - u8 bStopBlinking = false; - - if (peeprompriv->CustomerID == RT_CID_819x_CAMEO) - pLed = &ledpriv->SwLed1; - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - SwLedOff(padapter, pLed); - if (peeprompriv->CustomerID == RT_CID_DEFAULT) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - if (!pLed1->bSWLedCtrl) { - SwLedOn(padapter, pLed1); - pLed1->bSWLedCtrl = true; - } else if (!pLed1->bLedOn) { - SwLedOn(padapter, pLed1); - } - } else { - if (!pLed1->bSWLedCtrl) { - SwLedOff(padapter, pLed1); - pLed1->bSWLedCtrl = true; - } else if (pLed1->bLedOn) { - SwLedOff(padapter, pLed1); - } - } - } - switch (pLed->CurrLedState) { - case LED_BLINK_SLOWLY: - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - break; - case LED_BLINK_NORMAL: - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA)); - break; - case LED_SCAN_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->bLedLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_NORMAL; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA)); - } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - } - pLed->bLedScanBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_TXRX_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->bLedLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_NORMAL; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA)); - } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - } - pLed->BlinkTimes = 0; - pLed->bLedBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_BLINK_WPS: - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - break; - case LED_BLINK_WPS_STOP: /* WPS success */ - if (pLed->BlinkingLedState == LED_STATE_ON) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA)); - bStopBlinking = false; - } else { - bStopBlinking = true; - } - if (bStopBlinking) { - pLed->bLedLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_NORMAL; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA)); - } - pLed->bLedWPSBlinkInProgress = false; - break; - default: - break; - } -} - -static void SwLedBlink2(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - u8 bStopBlinking = false; - - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - SwLedOff(padapter, pLed); - switch (pLed->CurrLedState) { - case LED_SCAN_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - SwLedOn(padapter, pLed); - } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - SwLedOff(padapter, pLed); - } - pLed->bLedScanBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_TXRX_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - SwLedOn(padapter, pLed); - } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - SwLedOff(padapter, pLed); - } - pLed->bLedBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - default: - break; - } -} - -static void SwLedBlink3(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - u8 bStopBlinking = false; - - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - if (pLed->CurrLedState != LED_BLINK_WPS_STOP) - SwLedOff(padapter, pLed); - switch (pLed->CurrLedState) { - case LED_SCAN_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (!pLed->bLedOn) - SwLedOn(padapter, pLed); - } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedOn) - SwLedOff(padapter, pLed); - } - pLed->bLedScanBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_TXRX_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (!pLed->bLedOn) - SwLedOn(padapter, pLed); - } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedOn) - SwLedOff(padapter, pLed); - } - pLed->bLedBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_BLINK_WPS: - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - break; - case LED_BLINK_WPS_STOP: /*WPS success*/ - if (pLed->BlinkingLedState == LED_STATE_ON) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA)); - bStopBlinking = false; - } else { - bStopBlinking = true; - } - if (bStopBlinking) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - SwLedOn(padapter, pLed); - pLed->bLedWPSBlinkInProgress = false; - } - break; - default: - break; - } -} - -static void SwLedBlink4(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - struct led_priv *ledpriv = &padapter->ledpriv; - struct LED_871x *pLed1 = &ledpriv->SwLed1; - u8 bStopBlinking = false; - - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - SwLedOff(padapter, pLed); - if (!pLed1->bLedWPSBlinkInProgress && - pLed1->BlinkingLedState == LED_UNKNOWN) { - pLed1->BlinkingLedState = LED_STATE_OFF; - pLed1->CurrLedState = LED_STATE_OFF; - SwLedOff(padapter, pLed1); - } - switch (pLed->CurrLedState) { - case LED_BLINK_SLOWLY: - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - break; - case LED_BLINK_StartToBlink: - if (pLed->bLedOn) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL)); - } else { - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - } - break; - case LED_SCAN_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - pLed->bLedScanBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_TXRX_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - pLed->bLedBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_BLINK_WPS: - if (pLed->bLedOn) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL)); - } else { - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - } - break; - case LED_BLINK_WPS_STOP: /*WPS authentication fail*/ - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - break; - case LED_BLINK_WPS_STOP_OVERLAP: /*WPS session overlap */ - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) { - if (pLed->bLedOn) - pLed->BlinkTimes = 1; - else - bStopBlinking = true; - } - if (bStopBlinking) { - pLed->BlinkTimes = 10; - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA)); - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - } - break; - default: - break; - } -} - -static void SwLedBlink5(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - u8 bStopBlinking = false; - - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - SwLedOff(padapter, pLed); - switch (pLed->CurrLedState) { - case LED_SCAN_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (!pLed->bLedOn) - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - pLed->bLedScanBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_TXRX_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (!pLed->bLedOn) - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - pLed->bLedBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - default: - break; - } -} - -static void SwLedBlink6(struct LED_871x *pLed) -{ - struct _adapter *padapter = pLed->padapter; - u8 bStopBlinking = false; - - /* Change LED according to BlinkingLedState specified. */ - if (pLed->BlinkingLedState == LED_STATE_ON) - SwLedOn(padapter, pLed); - else - SwLedOff(padapter, pLed); - switch (pLed->CurrLedState) { - case LED_TXRX_BLINK: - pLed->BlinkTimes--; - if (pLed->BlinkTimes == 0) - bStopBlinking = true; - if (bStopBlinking) { - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (!pLed->bLedOn) - SwLedOn(padapter, pLed); - pLed->bLedBlinkInProgress = false; - } else { - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_BLINK_WPS: - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - break; - - default: - break; - } -} - -/* Description: - * Callback function of LED BlinkTimer, - * it just schedules to corresponding BlinkWorkItem. - */ -static void BlinkTimerCallback(struct timer_list *t) -{ - struct LED_871x *pLed = from_timer(pLed, t, BlinkTimer); - - /* This fixed the crash problem on Fedora 12 when trying to do the - * insmod;ifconfig up;rmmod commands. - */ - if (pLed->padapter->surprise_removed || pLed->padapter->driver_stopped) - return; - schedule_work(&pLed->BlinkWorkItem); -} - -/* Description: - * Callback function of LED BlinkWorkItem. - * We dispatch actual LED blink action according to LedStrategy. - */ -static void BlinkWorkItemCallback(struct work_struct *work) -{ - struct LED_871x *pLed = container_of(work, struct LED_871x, - BlinkWorkItem); - struct led_priv *ledpriv = &pLed->padapter->ledpriv; - - switch (ledpriv->LedStrategy) { - case SW_LED_MODE0: - SwLedBlink(pLed); - break; - case SW_LED_MODE1: - SwLedBlink1(pLed); - break; - case SW_LED_MODE2: - SwLedBlink2(pLed); - break; - case SW_LED_MODE3: - SwLedBlink3(pLed); - break; - case SW_LED_MODE4: - SwLedBlink4(pLed); - break; - case SW_LED_MODE5: - SwLedBlink5(pLed); - break; - case SW_LED_MODE6: - SwLedBlink6(pLed); - break; - default: - SwLedBlink(pLed); - break; - } -} - -/*============================================================================ - * Default LED behavior. - *============================================================================ - * - * Description: - * Implement each led action for SW_LED_MODE0. - * This is default strategy. - */ - -static void SwLedControlMode1(struct _adapter *padapter, - enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct sitesurvey_ctrl *psitesurveyctrl = &pmlmepriv->sitesurveyctrl; - - if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO) - pLed = &ledpriv->SwLed1; - switch (LedAction) { - case LED_CTL_START_TO_LINK: - case LED_CTL_NO_LINK: - if (!pLed->bLedNoLinkBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - } - break; - case LED_CTL_LINK: - if (!pLed->bLedLinkBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_NORMAL; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA)); - } - break; - case LED_CTL_SITE_SURVEY: - if (psitesurveyctrl->traffic_busy && - check_fwstate(pmlmepriv, _FW_LINKED)) - ; /* dummy branch */ - else if (!pLed->bLedScanBlinkInProgress) { - if (IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedScanBlinkInProgress = true; - pLed->CurrLedState = LED_SCAN_BLINK; - pLed->BlinkTimes = 24; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_TX: - case LED_CTL_RX: - if (!pLed->bLedBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - pLed->bLedBlinkInProgress = true; - pLed->CurrLedState = LED_TXRX_BLINK; - pLed->BlinkTimes = 2; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - - case LED_CTL_START_WPS: /*wait until xinpin finish */ - case LED_CTL_START_WPS_BOTTON: - if (!pLed->bLedWPSBlinkInProgress) { - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - pLed->bLedWPSBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_WPS; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_STOP_WPS: - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - if (pLed->bLedWPSBlinkInProgress) - del_timer(&pLed->BlinkTimer); - else - pLed->bLedWPSBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_WPS_STOP; - if (pLed->bLedOn) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA)); - } else { - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - break; - case LED_CTL_STOP_WPS_FAIL: - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - break; - case LED_CTL_POWER_OFF: - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - default: - break; - } -} - -static void SwLedControlMode2(struct _adapter *padapter, - enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - - switch (LedAction) { - case LED_CTL_SITE_SURVEY: - if (pmlmepriv->sitesurveyctrl.traffic_busy) - ; /* dummy branch */ - else if (!pLed->bLedScanBlinkInProgress) { - if (IS_LED_WPS_BLINKING(pLed)) - return; - - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedScanBlinkInProgress = true; - pLed->CurrLedState = LED_SCAN_BLINK; - pLed->BlinkTimes = 24; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - - case LED_CTL_TX: - case LED_CTL_RX: - if (!pLed->bLedBlinkInProgress && - check_fwstate(pmlmepriv, _FW_LINKED)) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - pLed->bLedBlinkInProgress = true; - pLed->CurrLedState = LED_TXRX_BLINK; - pLed->BlinkTimes = 2; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - - case LED_CTL_LINK: - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - - case LED_CTL_START_WPS: /*wait until xinpin finish*/ - case LED_CTL_START_WPS_BOTTON: - if (!pLed->bLedWPSBlinkInProgress) { - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - pLed->bLedWPSBlinkInProgress = true; - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - break; - - case LED_CTL_STOP_WPS: - pLed->bLedWPSBlinkInProgress = false; - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - - case LED_CTL_STOP_WPS_FAIL: - pLed->bLedWPSBlinkInProgress = false; - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - - case LED_CTL_START_TO_LINK: - case LED_CTL_NO_LINK: - if (!IS_LED_BLINKING(pLed)) { - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - break; - case LED_CTL_POWER_OFF: - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - default: - break; - } -} - -static void SwLedControlMode3(struct _adapter *padapter, - enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - - switch (LedAction) { - case LED_CTL_SITE_SURVEY: - if (pmlmepriv->sitesurveyctrl.traffic_busy) - ; /* dummy branch */ - else if (!pLed->bLedScanBlinkInProgress) { - if (IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedScanBlinkInProgress = true; - pLed->CurrLedState = LED_SCAN_BLINK; - pLed->BlinkTimes = 24; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_TX: - case LED_CTL_RX: - if (!pLed->bLedBlinkInProgress && - check_fwstate(pmlmepriv, _FW_LINKED)) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - pLed->bLedBlinkInProgress = true; - pLed->CurrLedState = LED_TXRX_BLINK; - pLed->BlinkTimes = 2; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_CTL_LINK: - if (IS_LED_WPS_BLINKING(pLed)) - return; - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - case LED_CTL_START_WPS: /* wait until xinpin finish */ - case LED_CTL_START_WPS_BOTTON: - if (!pLed->bLedWPSBlinkInProgress) { - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - pLed->bLedWPSBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_WPS; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_STOP_WPS: - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } else { - pLed->bLedWPSBlinkInProgress = true; - } - pLed->CurrLedState = LED_BLINK_WPS_STOP; - if (pLed->bLedOn) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA)); - } else { - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - break; - case LED_CTL_STOP_WPS_FAIL: - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - case LED_CTL_START_TO_LINK: - case LED_CTL_NO_LINK: - if (!IS_LED_BLINKING(pLed)) { - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - break; - case LED_CTL_POWER_OFF: - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - default: - break; - } -} - -static void SwLedControlMode4(struct _adapter *padapter, - enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - struct LED_871x *pLed1 = &ledpriv->SwLed1; - - switch (LedAction) { - case LED_CTL_START_TO_LINK: - if (pLed1->bLedWPSBlinkInProgress) { - pLed1->bLedWPSBlinkInProgress = false; - del_timer(&pLed1->BlinkTimer); - pLed1->BlinkingLedState = LED_STATE_OFF; - pLed1->CurrLedState = LED_STATE_OFF; - if (pLed1->bLedOn) - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - if (!pLed->bLedStartToLinkBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - pLed->bLedStartToLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_StartToBlink; - if (pLed->bLedOn) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL)); - } else { - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - } - } - break; - case LED_CTL_LINK: - case LED_CTL_NO_LINK: - /*LED1 settings*/ - if (LedAction == LED_CTL_LINK) { - if (pLed1->bLedWPSBlinkInProgress) { - pLed1->bLedWPSBlinkInProgress = false; - del_timer(&pLed1->BlinkTimer); - pLed1->BlinkingLedState = LED_STATE_OFF; - pLed1->CurrLedState = LED_STATE_OFF; - if (pLed1->bLedOn) - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - } - if (!pLed->bLedNoLinkBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - } - break; - case LED_CTL_SITE_SURVEY: - if (pmlmepriv->sitesurveyctrl.traffic_busy && - check_fwstate(pmlmepriv, _FW_LINKED)) - ; - else if (!pLed->bLedScanBlinkInProgress) { - if (IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedScanBlinkInProgress = true; - pLed->CurrLedState = LED_SCAN_BLINK; - pLed->BlinkTimes = 24; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_TX: - case LED_CTL_RX: - if (!pLed->bLedBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK || - IS_LED_WPS_BLINKING(pLed)) - return; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - pLed->bLedBlinkInProgress = true; - pLed->CurrLedState = LED_TXRX_BLINK; - pLed->BlinkTimes = 2; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_CTL_START_WPS: /*wait until xinpin finish*/ - case LED_CTL_START_WPS_BOTTON: - if (pLed1->bLedWPSBlinkInProgress) { - pLed1->bLedWPSBlinkInProgress = false; - del_timer(&pLed1->BlinkTimer); - pLed1->BlinkingLedState = LED_STATE_OFF; - pLed1->CurrLedState = LED_STATE_OFF; - if (pLed1->bLedOn) - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - } - if (!pLed->bLedWPSBlinkInProgress) { - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - pLed->bLedWPSBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_WPS; - if (pLed->bLedOn) { - pLed->BlinkingLedState = LED_STATE_OFF; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL)); - } else { - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - } - } - break; - case LED_CTL_STOP_WPS: /*WPS connect success*/ - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - break; - case LED_CTL_STOP_WPS_FAIL: /*WPS authentication fail*/ - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - /*LED1 settings*/ - if (pLed1->bLedWPSBlinkInProgress) - del_timer(&pLed1->BlinkTimer); - else - pLed1->bLedWPSBlinkInProgress = true; - pLed1->CurrLedState = LED_BLINK_WPS_STOP; - if (pLed1->bLedOn) - pLed1->BlinkingLedState = LED_STATE_OFF; - else - pLed1->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - break; - case LED_CTL_STOP_WPS_FAIL_OVERLAP: /*WPS session overlap*/ - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - pLed->bLedNoLinkBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_SLOWLY; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA)); - /*LED1 settings*/ - if (pLed1->bLedWPSBlinkInProgress) - del_timer(&pLed1->BlinkTimer); - else - pLed1->bLedWPSBlinkInProgress = true; - pLed1->CurrLedState = LED_BLINK_WPS_STOP_OVERLAP; - pLed1->BlinkTimes = 10; - if (pLed1->bLedOn) - pLed1->BlinkingLedState = LED_STATE_OFF; - else - pLed1->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL)); - break; - case LED_CTL_POWER_OFF: - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedNoLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedNoLinkBlinkInProgress = false; - } - if (pLed->bLedLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedLinkBlinkInProgress = false; - } - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - if (pLed->bLedScanBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedScanBlinkInProgress = false; - } - if (pLed->bLedStartToLinkBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedStartToLinkBlinkInProgress = false; - } - if (pLed1->bLedWPSBlinkInProgress) { - del_timer(&pLed1->BlinkTimer); - pLed1->bLedWPSBlinkInProgress = false; - } - pLed1->BlinkingLedState = LED_UNKNOWN; - SwLedOff(padapter, pLed); - SwLedOff(padapter, pLed1); - break; - default: - break; - } -} - -static void SwLedControlMode5(struct _adapter *padapter, - enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - - if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO) - pLed = &ledpriv->SwLed1; - - switch (LedAction) { - case LED_CTL_POWER_ON: - case LED_CTL_NO_LINK: - case LED_CTL_LINK: /* solid blue */ - if (pLed->CurrLedState == LED_SCAN_BLINK) - return; - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - pLed->bLedBlinkInProgress = false; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - case LED_CTL_SITE_SURVEY: - if (pmlmepriv->sitesurveyctrl.traffic_busy && - check_fwstate(pmlmepriv, _FW_LINKED)) - ; /* dummy branch */ - else if (!pLed->bLedScanBlinkInProgress) { - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedScanBlinkInProgress = true; - pLed->CurrLedState = LED_SCAN_BLINK; - pLed->BlinkTimes = 24; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_TX: - case LED_CTL_RX: - if (!pLed->bLedBlinkInProgress) { - if (pLed->CurrLedState == LED_SCAN_BLINK) - return; - pLed->bLedBlinkInProgress = true; - pLed->CurrLedState = LED_TXRX_BLINK; - pLed->BlinkTimes = 2; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_CTL_POWER_OFF: - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - SwLedOff(padapter, pLed); - break; - default: - break; - } -} - -static void SwLedControlMode6(struct _adapter *padapter, - enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - - switch (LedAction) { - case LED_CTL_POWER_ON: - case LED_CTL_NO_LINK: - case LED_CTL_LINK: /*solid blue*/ - case LED_CTL_SITE_SURVEY: - if (IS_LED_WPS_BLINKING(pLed)) - return; - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - pLed->bLedBlinkInProgress = false; - mod_timer(&pLed->BlinkTimer, jiffies + msecs_to_jiffies(0)); - break; - case LED_CTL_TX: - case LED_CTL_RX: - if (!pLed->bLedBlinkInProgress && - check_fwstate(pmlmepriv, _FW_LINKED)) { - if (IS_LED_WPS_BLINKING(pLed)) - return; - pLed->bLedBlinkInProgress = true; - pLed->CurrLedState = LED_TXRX_BLINK; - pLed->BlinkTimes = 2; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA)); - } - break; - case LED_CTL_START_WPS: /*wait until xinpin finish*/ - case LED_CTL_START_WPS_BOTTON: - if (!pLed->bLedWPSBlinkInProgress) { - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - pLed->bLedWPSBlinkInProgress = true; - pLed->CurrLedState = LED_BLINK_WPS; - if (pLed->bLedOn) - pLed->BlinkingLedState = LED_STATE_OFF; - else - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, jiffies + - msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA)); - } - break; - case LED_CTL_STOP_WPS_FAIL: - case LED_CTL_STOP_WPS: - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - pLed->CurrLedState = LED_STATE_ON; - pLed->BlinkingLedState = LED_STATE_ON; - mod_timer(&pLed->BlinkTimer, - jiffies + msecs_to_jiffies(0)); - break; - case LED_CTL_POWER_OFF: - pLed->CurrLedState = LED_STATE_OFF; - pLed->BlinkingLedState = LED_STATE_OFF; - if (pLed->bLedBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedBlinkInProgress = false; - } - if (pLed->bLedWPSBlinkInProgress) { - del_timer(&pLed->BlinkTimer); - pLed->bLedWPSBlinkInProgress = false; - } - SwLedOff(padapter, pLed); - break; - default: - break; - } -} - -/* Description: - * Dispatch LED action according to pHalData->LedStrategy. - */ -void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction) -{ - struct led_priv *ledpriv = &padapter->ledpriv; - - if (!ledpriv->bRegUseLed) - return; - switch (ledpriv->LedStrategy) { - case SW_LED_MODE0: - break; - case SW_LED_MODE1: - SwLedControlMode1(padapter, LedAction); - break; - case SW_LED_MODE2: - SwLedControlMode2(padapter, LedAction); - break; - case SW_LED_MODE3: - SwLedControlMode3(padapter, LedAction); - break; - case SW_LED_MODE4: - SwLedControlMode4(padapter, LedAction); - break; - case SW_LED_MODE5: - SwLedControlMode5(padapter, LedAction); - break; - case SW_LED_MODE6: - SwLedControlMode6(padapter, LedAction); - break; - default: - break; - } -} - -void r8712_flush_led_works(struct _adapter *padapter) -{ - struct led_priv *pledpriv = &padapter->ledpriv; - - flush_work(&pledpriv->SwLed0.BlinkWorkItem); - flush_work(&pledpriv->SwLed1.BlinkWorkItem); -} diff --git a/drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h b/drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h deleted file mode 100644 index 46d758d3f3a4d..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_MACSETTING_BITDEF_H__ -#define __RTL8712_MACSETTING_BITDEF_H__ - -/*MACID*/ -/*BSSID*/ - -/*HWVID*/ -#define _HWVID_MSK 0x0F - -/*MAR*/ -/*MBIDCANCONTENT*/ - -/*MBIDCANCFG*/ -#define _POOLING BIT(31) -#define _WRITE_EN BIT(16) -#define _CAM_ADDR_MSK 0x001F -#define _CAM_ADDR_SHT 0 - -/*BUILDTIME*/ -#define _BUILDTIME_MSK 0x3FFFFFFF - -/*BUILDUSER*/ - -#endif /* __RTL8712_MACSETTING_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_macsetting_regdef.h b/drivers/staging/rtl8712/rtl8712_macsetting_regdef.h deleted file mode 100644 index 64740d99c2523..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_macsetting_regdef.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_MACSETTING_REGDEF_H__ -#define __RTL8712_MACSETTING_REGDEF_H__ - -#define MACID (RTL8712_MACIDSETTING_ + 0x0000) -#define BSSIDR (RTL8712_MACIDSETTING_ + 0x0008) -#define HWVID (RTL8712_MACIDSETTING_ + 0x000E) -#define MAR (RTL8712_MACIDSETTING_ + 0x0010) -#define MBIDCANCONTENT (RTL8712_MACIDSETTING_ + 0x0018) -#define MBIDCANCFG (RTL8712_MACIDSETTING_ + 0x0020) -#define BUILDTIME (RTL8712_MACIDSETTING_ + 0x0024) -#define BUILDUSER (RTL8712_MACIDSETTING_ + 0x0028) - -#endif /*__RTL8712_MACSETTING_REGDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_powersave_bitdef.h b/drivers/staging/rtl8712/rtl8712_powersave_bitdef.h deleted file mode 100644 index 53e0d6b440f34..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_powersave_bitdef.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_POWERSAVE_BITDEF_H__ -#define __RTL8712_POWERSAVE_BITDEF_H__ - -/*WOWCTRL*/ -#define _UWF BIT(3) -#define _MAGIC BIT(2) -#define _WOW_EN BIT(1) -#define _PMEN BIT(0) - -/*PSSTATUS*/ -#define _PSSTATUS_SEL_MSK 0x0F - -/*PSSWITCH*/ -#define _PSSWITCH_ACT BIT(7) -#define _PSSWITCH_SEL_MSK 0x0F -#define _PSSWITCH_SEL_SHT 0 - -/*LPNAV_CTRL*/ -#define _LPNAV_EN BIT(31) -#define _LPNAV_EARLY_MSK 0x7FFF0000 -#define _LPNAV_EARLY_SHT 16 -#define _LPNAV_TH_MSK 0x0000FFFF -#define _LPNAV_TH_SHT 0 - -/*RPWM*/ -/*CPWM*/ -#define _TOGGLING BIT(7) -#define _WWLAN BIT(3) -#define _RPS_ST BIT(2) -#define _WLAN_TRX BIT(1) -#define _SYS_CLK BIT(0) - -#endif /* __RTL8712_POWERSAVE_BITDEF_H__*/ diff --git a/drivers/staging/rtl8712/rtl8712_powersave_regdef.h b/drivers/staging/rtl8712/rtl8712_powersave_regdef.h deleted file mode 100644 index 1bcfde4b1c11f..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_powersave_regdef.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_POWERSAVE_REGDEF_H__ -#define __RTL8712_POWERSAVE_REGDEF_H__ - -#define WOWCTRL (RTL8712_POWERSAVE_ + 0x00) -#define PSSTATUS (RTL8712_POWERSAVE_ + 0x01) -#define PSSWITCH (RTL8712_POWERSAVE_ + 0x02) -#define MIMOPS_WAITPERIOD (RTL8712_POWERSAVE_ + 0x03) -#define LPNAV_CTRL (RTL8712_POWERSAVE_ + 0x04) -#define WFM0 (RTL8712_POWERSAVE_ + 0x10) -#define WFM1 (RTL8712_POWERSAVE_ + 0x20) -#define WFM2 (RTL8712_POWERSAVE_ + 0x30) -#define WFM3 (RTL8712_POWERSAVE_ + 0x40) -#define WFM4 (RTL8712_POWERSAVE_ + 0x50) -#define WFM5 (RTL8712_POWERSAVE_ + 0x60) -#define WFCRC (RTL8712_POWERSAVE_ + 0x70) -#define RPWM (RTL8712_POWERSAVE_ + 0x7C) -#define CPWM (RTL8712_POWERSAVE_ + 0x7D) - -#endif /* __RTL8712_POWERSAVE_REGDEF_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h deleted file mode 100644 index 1de51c48f9c1b..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_RATECTRL_BITDEF_H__ -#define __RTL8712_RATECTRL_BITDEF_H__ - -/*INIRTSMCS_SEL*/ -#define _INIRTSMCS_SEL_MSK 0x3F - -/* RRSR*/ -#define _RRSR_SHORT BIT(23) -#define _RRSR_RSC_MSK 0x600000 -#define _RRSR_RSC_SHT 21 -#define _RRSR_BITMAP_MSK 0x0FFFFF -#define _RRSR_BITMAP_SHT 0 - -/* AGGLEN_LMT_H*/ -#define _AGGLMT_MCS32_MSK 0xF0 -#define _AGGLMT_MCS32_SHT 4 -#define _AGGLMT_MCS15_SGI_MSK 0x0F -#define _AGGLMT_MCS15_SGI_SHT 0 - -/* DARFRC*/ -/* RARFRC*/ -/* MCS_TXAGC*/ -/* CCK_TXAGC*/ -#define _CCK_MSK 0xFF00 -#define _CCK_SHT 8 -#define _BARKER_MSK 0x00FF -#define _BARKER_SHT 0 - -#endif /* __RTL8712_RATECTRL_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h b/drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h deleted file mode 100644 index 9ed5653f3f7f1..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_RATECTRL_REGDEF_H__ -#define __RTL8712_RATECTRL_REGDEF_H__ - -#define INIMCS_SEL (RTL8712_RATECTRL_ + 0x00) -#define INIRTSMCS_SEL (RTL8712_RATECTRL_ + 0x20) -#define RRSR (RTL8712_RATECTRL_ + 0x21) -#define ARFR0 (RTL8712_RATECTRL_ + 0x24) -#define ARFR1 (RTL8712_RATECTRL_ + 0x28) -#define ARFR2 (RTL8712_RATECTRL_ + 0x2C) -#define ARFR3 (RTL8712_RATECTRL_ + 0x30) -#define ARFR4 (RTL8712_RATECTRL_ + 0x34) -#define ARFR5 (RTL8712_RATECTRL_ + 0x38) -#define ARFR6 (RTL8712_RATECTRL_ + 0x3C) -#define ARFR7 (RTL8712_RATECTRL_ + 0x40) -#define AGGLEN_LMT_H (RTL8712_RATECTRL_ + 0x47) -#define AGGLEN_LMT_L (RTL8712_RATECTRL_ + 0x48) -#define DARFRC (RTL8712_RATECTRL_ + 0x50) -#define RARFRC (RTL8712_RATECTRL_ + 0x58) -#define MCS_TXAGC0 (RTL8712_RATECTRL_ + 0x60) -#define MCS_TXAGC1 (RTL8712_RATECTRL_ + 0x61) -#define MCS_TXAGC2 (RTL8712_RATECTRL_ + 0x62) -#define MCS_TXAGC3 (RTL8712_RATECTRL_ + 0x63) -#define MCS_TXAGC4 (RTL8712_RATECTRL_ + 0x64) -#define MCS_TXAGC5 (RTL8712_RATECTRL_ + 0x65) -#define MCS_TXAGC6 (RTL8712_RATECTRL_ + 0x66) -#define MCS_TXAGC7 (RTL8712_RATECTRL_ + 0x67) -#define CCK_TXAGC (RTL8712_RATECTRL_ + 0x68) - -#endif /*__RTL8712_RATECTRL_REGDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_recv.c b/drivers/staging/rtl8712/rtl8712_recv.c deleted file mode 100644 index ab344d676bb94..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_recv.c +++ /dev/null @@ -1,1075 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl8712_recv.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL8712_RECV_C_ - -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "mlme_osdep.h" -#include "ethernet.h" -#include "usb_ops.h" -#include "wifi.h" - -static void recv_tasklet(struct tasklet_struct *t); - -int r8712_init_recv_priv(struct recv_priv *precvpriv, - struct _adapter *padapter) -{ - int i; - struct recv_buf *precvbuf; - addr_t tmpaddr = 0; - int alignment = 0; - struct sk_buff *pskb = NULL; - - /*init recv_buf*/ - _init_queue(&precvpriv->free_recv_buf_queue); - precvpriv->pallocated_recv_buf = - kzalloc(NR_RECVBUFF * sizeof(struct recv_buf) + 4, GFP_ATOMIC); - if (!precvpriv->pallocated_recv_buf) - return -ENOMEM; - precvpriv->precv_buf = precvpriv->pallocated_recv_buf + 4 - - ((addr_t)(precvpriv->pallocated_recv_buf) & 3); - precvbuf = (struct recv_buf *)precvpriv->precv_buf; - for (i = 0; i < NR_RECVBUFF; i++) { - INIT_LIST_HEAD(&precvbuf->list); - spin_lock_init(&precvbuf->recvbuf_lock); - if (r8712_os_recvbuf_resource_alloc(padapter, precvbuf)) - break; - precvbuf->ref_cnt = 0; - precvbuf->adapter = padapter; - list_add_tail(&precvbuf->list, - &precvpriv->free_recv_buf_queue.queue); - precvbuf++; - } - precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF; - tasklet_setup(&precvpriv->recv_tasklet, recv_tasklet); - skb_queue_head_init(&precvpriv->rx_skb_queue); - - skb_queue_head_init(&precvpriv->free_recv_skb_queue); - for (i = 0; i < NR_PREALLOC_RECV_SKB; i++) { - pskb = netdev_alloc_skb(padapter->pnetdev, MAX_RECVBUF_SZ + - RECVBUFF_ALIGN_SZ); - if (pskb) { - tmpaddr = (addr_t)pskb->data; - alignment = tmpaddr & (RECVBUFF_ALIGN_SZ - 1); - skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment)); - skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb); - } - pskb = NULL; - } - return 0; -} - -void r8712_free_recv_priv(struct recv_priv *precvpriv) -{ - int i; - struct recv_buf *precvbuf; - struct _adapter *padapter = precvpriv->adapter; - - precvbuf = (struct recv_buf *)precvpriv->precv_buf; - for (i = 0; i < NR_RECVBUFF; i++) { - r8712_os_recvbuf_resource_free(padapter, precvbuf); - precvbuf++; - } - kfree(precvpriv->pallocated_recv_buf); - skb_queue_purge(&precvpriv->rx_skb_queue); - if (skb_queue_len(&precvpriv->rx_skb_queue)) - netdev_warn(padapter->pnetdev, "r8712u: rx_skb_queue not empty\n"); - skb_queue_purge(&precvpriv->free_recv_skb_queue); - if (skb_queue_len(&precvpriv->free_recv_skb_queue)) - netdev_warn(padapter->pnetdev, "r8712u: free_recv_skb_queue not empty %d\n", - skb_queue_len(&precvpriv->free_recv_skb_queue)); -} - -void r8712_init_recvbuf(struct _adapter *padapter, struct recv_buf *precvbuf) -{ - precvbuf->transfer_len = 0; - precvbuf->len = 0; - precvbuf->ref_cnt = 0; - if (precvbuf->pbuf) { - precvbuf->pdata = precvbuf->pbuf; - precvbuf->phead = precvbuf->pbuf; - precvbuf->ptail = precvbuf->pbuf; - precvbuf->pend = precvbuf->pdata + MAX_RECVBUF_SZ; - } -} - -void r8712_free_recvframe(union recv_frame *precvframe, - struct __queue *pfree_recv_queue) -{ - unsigned long irqL; - struct _adapter *padapter = precvframe->u.hdr.adapter; - struct recv_priv *precvpriv = &padapter->recvpriv; - - if (precvframe->u.hdr.pkt) { - dev_kfree_skb_any(precvframe->u.hdr.pkt);/*free skb by driver*/ - precvframe->u.hdr.pkt = NULL; - } - spin_lock_irqsave(&pfree_recv_queue->lock, irqL); - list_del_init(&precvframe->u.hdr.list); - list_add_tail(&precvframe->u.hdr.list, &pfree_recv_queue->queue); - if (padapter) { - if (pfree_recv_queue == &precvpriv->free_recv_queue) - precvpriv->free_recvframe_cnt++; - } - spin_unlock_irqrestore(&pfree_recv_queue->lock, irqL); -} - -static void update_recvframe_attrib_from_recvstat(struct rx_pkt_attrib *pattrib, - struct recv_stat *prxstat) -{ - /*TODO: - * Offset 0 - */ - pattrib->bdecrypted = (le32_to_cpu(prxstat->rxdw0) & BIT(27)) == 0; - pattrib->crc_err = (le32_to_cpu(prxstat->rxdw0) & BIT(14)) != 0; - /*Offset 4*/ - /*Offset 8*/ - /*Offset 12*/ - if (le32_to_cpu(prxstat->rxdw3) & BIT(13)) { - pattrib->tcpchk_valid = 1; /* valid */ - if (le32_to_cpu(prxstat->rxdw3) & BIT(11)) - pattrib->tcp_chkrpt = 1; /* correct */ - else - pattrib->tcp_chkrpt = 0; /* incorrect */ - if (le32_to_cpu(prxstat->rxdw3) & BIT(12)) - pattrib->ip_chkrpt = 1; /* correct */ - else - pattrib->ip_chkrpt = 0; /* incorrect */ - } else { - pattrib->tcpchk_valid = 0; /* invalid */ - } - pattrib->mcs_rate = (u8)((le32_to_cpu(prxstat->rxdw3)) & 0x3f); - pattrib->htc = (u8)((le32_to_cpu(prxstat->rxdw3) >> 14) & 0x1); - /*Offset 16*/ - /*Offset 20*/ - /*phy_info*/ -} - -/*perform defrag*/ -static union recv_frame *recvframe_defrag(struct _adapter *adapter, - struct __queue *defrag_q) -{ - struct list_head *plist, *phead; - u8 wlanhdr_offset; - u8 curfragnum; - struct recv_frame_hdr *pfhdr, *pnfhdr; - union recv_frame *prframe, *pnextrframe; - struct __queue *pfree_recv_queue; - - pfree_recv_queue = &adapter->recvpriv.free_recv_queue; - phead = &defrag_q->queue; - plist = phead->next; - prframe = container_of(plist, union recv_frame, u.list); - list_del_init(&prframe->u.list); - pfhdr = &prframe->u.hdr; - curfragnum = 0; - if (curfragnum != pfhdr->attrib.frag_num) { - /*the first fragment number must be 0 - *free the whole queue - */ - r8712_free_recvframe(prframe, pfree_recv_queue); - r8712_free_recvframe_queue(defrag_q, pfree_recv_queue); - return NULL; - } - curfragnum++; - plist = &defrag_q->queue; - plist = plist->next; - while (!end_of_queue_search(phead, plist)) { - pnextrframe = container_of(plist, union recv_frame, u.list); - pnfhdr = &pnextrframe->u.hdr; - /*check the fragment sequence (2nd ~n fragment frame) */ - if (curfragnum != pnfhdr->attrib.frag_num) { - /* the fragment number must increase (after decache) - * release the defrag_q & prframe - */ - r8712_free_recvframe(prframe, pfree_recv_queue); - r8712_free_recvframe_queue(defrag_q, pfree_recv_queue); - return NULL; - } - curfragnum++; - /* copy the 2nd~n fragment frame's payload to the first fragment - * get the 2nd~last fragment frame's payload - */ - wlanhdr_offset = pnfhdr->attrib.hdrlen + pnfhdr->attrib.iv_len; - recvframe_pull(pnextrframe, wlanhdr_offset); - /* append to first fragment frame's tail (if privacy frame, - * pull the ICV) - */ - recvframe_pull_tail(prframe, pfhdr->attrib.icv_len); - memcpy(pfhdr->rx_tail, pnfhdr->rx_data, pnfhdr->len); - recvframe_put(prframe, pnfhdr->len); - pfhdr->attrib.icv_len = pnfhdr->attrib.icv_len; - plist = plist->next; - } - /* free the defrag_q queue and return the prframe */ - r8712_free_recvframe_queue(defrag_q, pfree_recv_queue); - return prframe; -} - -/* check if need to defrag, if needed queue the frame to defrag_q */ -union recv_frame *r8712_recvframe_chk_defrag(struct _adapter *padapter, - union recv_frame *precv_frame) -{ - u8 ismfrag; - u8 fragnum; - u8 *psta_addr; - struct recv_frame_hdr *pfhdr; - struct sta_info *psta; - struct sta_priv *pstapriv; - struct list_head *phead; - union recv_frame *prtnframe = NULL; - struct __queue *pfree_recv_queue, *pdefrag_q; - - pstapriv = &padapter->stapriv; - pfhdr = &precv_frame->u.hdr; - pfree_recv_queue = &padapter->recvpriv.free_recv_queue; - /* need to define struct of wlan header frame ctrl */ - ismfrag = pfhdr->attrib.mfrag; - fragnum = pfhdr->attrib.frag_num; - psta_addr = pfhdr->attrib.ta; - psta = r8712_get_stainfo(pstapriv, psta_addr); - if (!psta) - pdefrag_q = NULL; - else - pdefrag_q = &psta->sta_recvpriv.defrag_q; - - if ((ismfrag == 0) && (fragnum == 0)) - prtnframe = precv_frame;/*isn't a fragment frame*/ - if (ismfrag == 1) { - /* 0~(n-1) fragment frame - * enqueue to defraf_g - */ - if (pdefrag_q) { - if (fragnum == 0) { - /*the first fragment*/ - if (!list_empty(&pdefrag_q->queue)) { - /*free current defrag_q */ - r8712_free_recvframe_queue(pdefrag_q, pfree_recv_queue); - } - } - /* Then enqueue the 0~(n-1) fragment to the defrag_q */ - phead = &pdefrag_q->queue; - list_add_tail(&pfhdr->list, phead); - prtnframe = NULL; - } else { - /* can't find this ta's defrag_queue, so free this - * recv_frame - */ - r8712_free_recvframe(precv_frame, pfree_recv_queue); - prtnframe = NULL; - } - } - if ((ismfrag == 0) && (fragnum != 0)) { - /* the last fragment frame - * enqueue the last fragment - */ - if (pdefrag_q) { - phead = &pdefrag_q->queue; - list_add_tail(&pfhdr->list, phead); - /*call recvframe_defrag to defrag*/ - precv_frame = recvframe_defrag(padapter, pdefrag_q); - prtnframe = precv_frame; - } else { - /* can't find this ta's defrag_queue, so free this - * recv_frame - */ - r8712_free_recvframe(precv_frame, pfree_recv_queue); - prtnframe = NULL; - } - } - if (prtnframe && (prtnframe->u.hdr.attrib.privacy)) { - /* after defrag we must check tkip mic code */ - if (r8712_recvframe_chkmic(padapter, prtnframe) == _FAIL) { - r8712_free_recvframe(prtnframe, pfree_recv_queue); - prtnframe = NULL; - } - } - return prtnframe; -} - -static void amsdu_to_msdu(struct _adapter *padapter, union recv_frame *prframe) -{ - int a_len, padding_len; - u16 eth_type, nSubframe_Length; - u8 nr_subframes, i; - unsigned char *pdata; - struct rx_pkt_attrib *pattrib; - _pkt *sub_skb, *subframes[MAX_SUBFRAME_COUNT]; - struct recv_priv *precvpriv = &padapter->recvpriv; - struct __queue *pfree_recv_queue = &precvpriv->free_recv_queue; - - nr_subframes = 0; - pattrib = &prframe->u.hdr.attrib; - recvframe_pull(prframe, prframe->u.hdr.attrib.hdrlen); - if (prframe->u.hdr.attrib.iv_len > 0) - recvframe_pull(prframe, prframe->u.hdr.attrib.iv_len); - a_len = prframe->u.hdr.len; - pdata = prframe->u.hdr.rx_data; - while (a_len > ETH_HLEN) { - /* Offset 12 denote 2 mac address */ - nSubframe_Length = *((u16 *)(pdata + 12)); - /*==m==>change the length order*/ - nSubframe_Length = (nSubframe_Length >> 8) + - (nSubframe_Length << 8); - if (a_len < (ETHERNET_HEADER_SIZE + nSubframe_Length)) { - netdev_warn(padapter->pnetdev, "r8712u: nRemain_Length is %d and nSubframe_Length is: %d\n", - a_len, nSubframe_Length); - goto exit; - } - /* move the data point to data content */ - pdata += ETH_HLEN; - a_len -= ETH_HLEN; - /* Allocate new skb for releasing to upper layer */ - sub_skb = dev_alloc_skb(nSubframe_Length + 12); - if (!sub_skb) - break; - skb_reserve(sub_skb, 12); - skb_put_data(sub_skb, pdata, nSubframe_Length); - subframes[nr_subframes++] = sub_skb; - if (nr_subframes >= MAX_SUBFRAME_COUNT) { - netdev_warn(padapter->pnetdev, "r8712u: ParseSubframe(): Too many Subframes! Packets dropped!\n"); - break; - } - pdata += nSubframe_Length; - a_len -= nSubframe_Length; - if (a_len != 0) { - padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & 3); - if (padding_len == 4) - padding_len = 0; - if (a_len < padding_len) - goto exit; - pdata += padding_len; - a_len -= padding_len; - } - } - for (i = 0; i < nr_subframes; i++) { - sub_skb = subframes[i]; - /* convert hdr + possible LLC headers into Ethernet header */ - eth_type = (sub_skb->data[6] << 8) | sub_skb->data[7]; - if (sub_skb->len >= 8 && - ((!memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) && - eth_type != ETH_P_AARP && eth_type != ETH_P_IPX) || - !memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE))) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType - */ - skb_pull(sub_skb, SNAP_SIZE); - memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->src, - ETH_ALEN); - memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->dst, - ETH_ALEN); - } else { - __be16 len; - /* Leave Ethernet header part of hdr and full payload */ - len = htons(sub_skb->len); - memcpy(skb_push(sub_skb, 2), &len, 2); - memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->src, - ETH_ALEN); - memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->dst, - ETH_ALEN); - } - /* Indicate the packets to upper layer */ - if (sub_skb) { - sub_skb->protocol = - eth_type_trans(sub_skb, padapter->pnetdev); - sub_skb->dev = padapter->pnetdev; - if ((pattrib->tcpchk_valid == 1) && - (pattrib->tcp_chkrpt == 1)) { - sub_skb->ip_summed = CHECKSUM_UNNECESSARY; - } else { - sub_skb->ip_summed = CHECKSUM_NONE; - } - netif_rx(sub_skb); - } - } -exit: - prframe->u.hdr.len = 0; - r8712_free_recvframe(prframe, pfree_recv_queue); -} - -void r8712_rxcmd_event_hdl(struct _adapter *padapter, void *prxcmdbuf) -{ - __le32 voffset; - u8 *poffset; - u16 cmd_len, drvinfo_sz; - struct recv_stat *prxstat; - - poffset = prxcmdbuf; - voffset = *(__le32 *)poffset; - prxstat = prxcmdbuf; - drvinfo_sz = (le32_to_cpu(prxstat->rxdw0) & 0x000f0000) >> 16; - drvinfo_sz <<= 3; - poffset += RXDESC_SIZE + drvinfo_sz; - do { - voffset = *(__le32 *)poffset; - cmd_len = (u16)(le32_to_cpu(voffset) & 0xffff); - r8712_event_handle(padapter, (__le32 *)poffset); - poffset += (cmd_len + 8);/*8 bytes alignment*/ - } while (le32_to_cpu(voffset) & BIT(31)); -} - -static int check_indicate_seq(struct recv_reorder_ctrl *preorder_ctrl, - u16 seq_num) -{ - u8 wsize = preorder_ctrl->wsize_b; - u16 wend = (preorder_ctrl->indicate_seq + wsize - 1) % 4096; - - /* Rx Reorder initialize condition.*/ - if (preorder_ctrl->indicate_seq == 0xffff) - preorder_ctrl->indicate_seq = seq_num; - /* Drop out the packet which SeqNum is smaller than WinStart */ - if (SN_LESS(seq_num, preorder_ctrl->indicate_seq)) - return false; - /* - * Sliding window manipulation. Conditions includes: - * 1. Incoming SeqNum is equal to WinStart =>Window shift 1 - * 2. Incoming SeqNum is larger than the WinEnd => Window shift N - */ - if (SN_EQUAL(seq_num, preorder_ctrl->indicate_seq)) - preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + - 1) % 4096; - else if (SN_LESS(wend, seq_num)) { - if (seq_num >= (wsize - 1)) - preorder_ctrl->indicate_seq = seq_num + 1 - wsize; - else - preorder_ctrl->indicate_seq = 4095 - (wsize - - (seq_num + 1)) + 1; - } - return true; -} - -static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl, - union recv_frame *prframe) -{ - struct list_head *phead, *plist; - union recv_frame *pnextrframe; - struct rx_pkt_attrib *pnextattrib; - struct __queue *ppending_recvframe_queue = - &preorder_ctrl->pending_recvframe_queue; - struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib; - - phead = &ppending_recvframe_queue->queue; - plist = phead->next; - while (!end_of_queue_search(phead, plist)) { - pnextrframe = container_of(plist, union recv_frame, u.list); - pnextattrib = &pnextrframe->u.hdr.attrib; - - if (SN_EQUAL(pnextattrib->seq_num, pattrib->seq_num)) - return false; - - if (SN_LESS(pnextattrib->seq_num, pattrib->seq_num)) - plist = plist->next; - else - break; - } - list_del_init(&prframe->u.hdr.list); - list_add_tail(&prframe->u.hdr.list, plist); - return true; -} - -int r8712_recv_indicatepkts_in_order(struct _adapter *padapter, - struct recv_reorder_ctrl *preorder_ctrl, - int bforced) -{ - struct list_head *phead, *plist; - union recv_frame *prframe; - struct rx_pkt_attrib *pattrib; - int bPktInBuf = false; - struct __queue *ppending_recvframe_queue = - &preorder_ctrl->pending_recvframe_queue; - - phead = &ppending_recvframe_queue->queue; - plist = phead->next; - /* Handling some condition for forced indicate case.*/ - if (bforced) { - if (list_empty(phead)) - return true; - - prframe = container_of(plist, union recv_frame, u.list); - pattrib = &prframe->u.hdr.attrib; - preorder_ctrl->indicate_seq = pattrib->seq_num; - } - /* Prepare indication list and indication. - * Check if there is any packet need indicate. - */ - while (!list_empty(phead)) { - prframe = container_of(plist, union recv_frame, u.list); - pattrib = &prframe->u.hdr.attrib; - if (!SN_LESS(preorder_ctrl->indicate_seq, pattrib->seq_num)) { - plist = plist->next; - list_del_init(&prframe->u.hdr.list); - if (SN_EQUAL(preorder_ctrl->indicate_seq, - pattrib->seq_num)) - preorder_ctrl->indicate_seq = - (preorder_ctrl->indicate_seq + 1) % 4096; - /*indicate this recv_frame*/ - if (!pattrib->amsdu) { - if (!padapter->driver_stopped && - !padapter->surprise_removed) { - /* indicate this recv_frame */ - r8712_recv_indicatepkt(padapter, - prframe); - } - } else if (pattrib->amsdu == 1) { - amsdu_to_msdu(padapter, prframe); - } - /* Update local variables. */ - bPktInBuf = false; - } else { - bPktInBuf = true; - break; - } - } - return bPktInBuf; -} - -static int recv_indicatepkt_reorder(struct _adapter *padapter, - union recv_frame *prframe) -{ - unsigned long irql; - struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib; - struct recv_reorder_ctrl *preorder_ctrl = prframe->u.hdr.preorder_ctrl; - struct __queue *ppending_recvframe_queue = - &preorder_ctrl->pending_recvframe_queue; - - if (!pattrib->amsdu) { - /* s1. */ - r8712_wlanhdr_to_ethhdr(prframe); - if (pattrib->qos != 1) { - if (!padapter->driver_stopped && - !padapter->surprise_removed) { - r8712_recv_indicatepkt(padapter, prframe); - return 0; - } else { - return -EINVAL; - } - } - } - spin_lock_irqsave(&ppending_recvframe_queue->lock, irql); - /*s2. check if winstart_b(indicate_seq) needs to be updated*/ - if (!check_indicate_seq(preorder_ctrl, pattrib->seq_num)) - goto _err_exit; - /*s3. Insert all packet into Reorder Queue to maintain its ordering.*/ - if (!enqueue_reorder_recvframe(preorder_ctrl, prframe)) - goto _err_exit; - /*s4. - * Indication process. - * After Packet dropping and Sliding Window shifting as above, we can - * now just indicate the packets with the SeqNum smaller than latest - * WinStart and buffer other packets. - * - * For Rx Reorder condition: - * 1. All packets with SeqNum smaller than WinStart => Indicate - * 2. All packets with SeqNum larger than or equal to - * WinStart => Buffer it. - */ - if (r8712_recv_indicatepkts_in_order(padapter, preorder_ctrl, false)) { - mod_timer(&preorder_ctrl->reordering_ctrl_timer, - jiffies + msecs_to_jiffies(REORDER_WAIT_TIME)); - spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); - } else { - spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); - del_timer(&preorder_ctrl->reordering_ctrl_timer); - } - return 0; -_err_exit: - spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); - return -ENOMEM; -} - -void r8712_reordering_ctrl_timeout_handler(void *pcontext) -{ - unsigned long irql; - struct recv_reorder_ctrl *preorder_ctrl = pcontext; - struct _adapter *padapter = preorder_ctrl->padapter; - struct __queue *ppending_recvframe_queue = - &preorder_ctrl->pending_recvframe_queue; - - if (padapter->driver_stopped || padapter->surprise_removed) - return; - spin_lock_irqsave(&ppending_recvframe_queue->lock, irql); - r8712_recv_indicatepkts_in_order(padapter, preorder_ctrl, true); - spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); -} - -static int r8712_process_recv_indicatepkts(struct _adapter *padapter, - union recv_frame *prframe) -{ - int retval = _SUCCESS; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - - if (phtpriv->ht_option == 1) { /*B/G/N Mode*/ - if (recv_indicatepkt_reorder(padapter, prframe)) { - /* including perform A-MPDU Rx Ordering Buffer Control*/ - if (!padapter->driver_stopped && - !padapter->surprise_removed) - return _FAIL; - } - } else { /*B/G mode*/ - retval = r8712_wlanhdr_to_ethhdr(prframe); - if (retval) - return _FAIL; - if (!padapter->driver_stopped && !padapter->surprise_removed) { - /* indicate this recv_frame */ - r8712_recv_indicatepkt(padapter, prframe); - } else { - return _FAIL; - } - } - return retval; -} - -static u8 query_rx_pwr_percentage(s8 antpower) -{ - if ((antpower <= -100) || (antpower >= 20)) - return 0; - else if (antpower >= 0) - return 100; - else - return 100 + antpower; -} - -static u8 evm_db2percentage(s8 value) -{ - /* - * -33dB~0dB to 0%~99% - */ - s8 ret_val = clamp(-value, 0, 33) * 3; - - if (ret_val == 99) - ret_val = 100; - - return ret_val; -} - -s32 r8712_signal_scale_mapping(s32 cur_sig) -{ - s32 ret_sig; - - if (cur_sig >= 51 && cur_sig <= 100) - ret_sig = 100; - else if (cur_sig >= 41 && cur_sig <= 50) - ret_sig = 80 + ((cur_sig - 40) * 2); - else if (cur_sig >= 31 && cur_sig <= 40) - ret_sig = 66 + (cur_sig - 30); - else if (cur_sig >= 21 && cur_sig <= 30) - ret_sig = 54 + (cur_sig - 20); - else if (cur_sig >= 10 && cur_sig <= 20) - ret_sig = 42 + (((cur_sig - 10) * 2) / 3); - else if (cur_sig >= 5 && cur_sig <= 9) - ret_sig = 22 + (((cur_sig - 5) * 3) / 2); - else if (cur_sig >= 1 && cur_sig <= 4) - ret_sig = 6 + (((cur_sig - 1) * 3) / 2); - else - ret_sig = cur_sig; - return ret_sig; -} - -static s32 translate2dbm(struct _adapter *padapter, u8 signal_strength_idx) -{ - s32 signal_power; /* in dBm.*/ - /* Translate to dBm (x=0.5y-95).*/ - signal_power = (s32)((signal_strength_idx + 1) >> 1); - signal_power -= 95; - return signal_power; -} - -static void query_rx_phy_status(struct _adapter *padapter, - union recv_frame *prframe) -{ - u8 i, max_spatial_stream, evm; - struct recv_stat *prxstat = (struct recv_stat *)prframe->u.hdr.rx_head; - struct phy_stat *pphy_stat = (struct phy_stat *)(prxstat + 1); - u8 *pphy_head = (u8 *)(prxstat + 1); - s8 rx_pwr[4], rx_pwr_all; - u8 pwdb_all; - u32 rssi, total_rssi = 0; - u8 bcck_rate = 0, rf_rx_num = 0, cck_highpwr = 0; - struct phy_cck_rx_status *pcck_buf; - u8 sq; - - /* Record it for next packet processing*/ - bcck_rate = (prframe->u.hdr.attrib.mcs_rate <= 3 ? 1 : 0); - if (bcck_rate) { - u8 report; - - /* CCK Driver info Structure is not the same as OFDM packet.*/ - pcck_buf = (struct phy_cck_rx_status *)pphy_stat; - /* (1)Hardware does not provide RSSI for CCK - * (2)PWDB, Average PWDB calculated by hardware - * (for rate adaptive) - */ - if (!cck_highpwr) { - report = pcck_buf->cck_agc_rpt & 0xc0; - report >>= 6; - switch (report) { - /* Modify the RF RNA gain value to -40, -20, - * -2, 14 by Jenyu's suggestion - * Note: different RF with the different - * RNA gain. - */ - case 0x3: - rx_pwr_all = -40 - (pcck_buf->cck_agc_rpt & - 0x3e); - break; - case 0x2: - rx_pwr_all = -20 - (pcck_buf->cck_agc_rpt & - 0x3e); - break; - case 0x1: - rx_pwr_all = -2 - (pcck_buf->cck_agc_rpt & - 0x3e); - break; - case 0x0: - rx_pwr_all = 14 - (pcck_buf->cck_agc_rpt & - 0x3e); - break; - } - } else { - report = ((u8)(le32_to_cpu(pphy_stat->phydw1) >> 8)) & - 0x60; - report >>= 5; - switch (report) { - case 0x3: - rx_pwr_all = -40 - ((pcck_buf->cck_agc_rpt & - 0x1f) << 1); - break; - case 0x2: - rx_pwr_all = -20 - ((pcck_buf->cck_agc_rpt & - 0x1f) << 1); - break; - case 0x1: - rx_pwr_all = -2 - ((pcck_buf->cck_agc_rpt & - 0x1f) << 1); - break; - case 0x0: - rx_pwr_all = 14 - ((pcck_buf->cck_agc_rpt & - 0x1f) << 1); - break; - } - } - pwdb_all = query_rx_pwr_percentage(rx_pwr_all); - /* CCK gain is smaller than OFDM/MCS gain,*/ - /* so we add gain diff by experiences, the val is 6 */ - pwdb_all += 6; - if (pwdb_all > 100) - pwdb_all = 100; - /* modify the offset to make the same gain index with OFDM.*/ - if (pwdb_all > 34 && pwdb_all <= 42) - pwdb_all -= 2; - else if (pwdb_all > 26 && pwdb_all <= 34) - pwdb_all -= 6; - else if (pwdb_all > 14 && pwdb_all <= 26) - pwdb_all -= 8; - else if (pwdb_all > 4 && pwdb_all <= 14) - pwdb_all -= 4; - /* - * (3) Get Signal Quality (EVM) - */ - if (pwdb_all > 40) { - sq = 100; - } else { - sq = pcck_buf->sq_rpt; - if (pcck_buf->sq_rpt > 64) - sq = 0; - else if (pcck_buf->sq_rpt < 20) - sq = 100; - else - sq = ((64 - sq) * 100) / 44; - } - prframe->u.hdr.attrib.signal_qual = sq; - prframe->u.hdr.attrib.rx_mimo_signal_qual[0] = sq; - prframe->u.hdr.attrib.rx_mimo_signal_qual[1] = -1; - } else { - /* (1)Get RSSI for HT rate */ - for (i = 0; i < ((padapter->registrypriv.rf_config) & - 0x0f); i++) { - rf_rx_num++; - rx_pwr[i] = ((pphy_head[PHY_STAT_GAIN_TRSW_SHT + i] - & 0x3F) * 2) - 110; - /* Translate DBM to percentage. */ - rssi = query_rx_pwr_percentage(rx_pwr[i]); - total_rssi += rssi; - } - /* (2)PWDB, Average PWDB calculated by hardware (for - * rate adaptive) - */ - rx_pwr_all = (((pphy_head[PHY_STAT_PWDB_ALL_SHT]) >> 1) & 0x7f) - - 106; - pwdb_all = query_rx_pwr_percentage(rx_pwr_all); - - { - /* (3)EVM of HT rate */ - if (prframe->u.hdr.attrib.htc && - prframe->u.hdr.attrib.mcs_rate >= 20 && - prframe->u.hdr.attrib.mcs_rate <= 27) { - /* both spatial stream make sense */ - max_spatial_stream = 2; - } else { - /* only spatial stream 1 makes sense */ - max_spatial_stream = 1; - } - for (i = 0; i < max_spatial_stream; i++) { - evm = evm_db2percentage((pphy_head - [PHY_STAT_RXEVM_SHT + i]));/*dbm*/ - prframe->u.hdr.attrib.signal_qual = - (u8)(evm & 0xff); - prframe->u.hdr.attrib.rx_mimo_signal_qual[i] = - (u8)(evm & 0xff); - } - } - } - /* UI BSS List signal strength(in percentage), make it good looking, - * from 0~100. It is assigned to the BSS List in - * GetValueFromBeaconOrProbeRsp(). - */ - if (bcck_rate) { - prframe->u.hdr.attrib.signal_strength = - (u8)r8712_signal_scale_mapping(pwdb_all); - } else { - if (rf_rx_num != 0) - prframe->u.hdr.attrib.signal_strength = - (u8)(r8712_signal_scale_mapping(total_rssi /= - rf_rx_num)); - } -} - -static void process_link_qual(struct _adapter *padapter, - union recv_frame *prframe) -{ - u32 last_evm = 0, avg_val; - struct rx_pkt_attrib *pattrib; - struct smooth_rssi_data *sqd = &padapter->recvpriv.signal_qual_data; - - if (!prframe || !padapter) - return; - pattrib = &prframe->u.hdr.attrib; - if (pattrib->signal_qual != 0) { - /* - * 1. Record the general EVM to the sliding window. - */ - if (sqd->total_num++ >= PHY_LINKQUALITY_SLID_WIN_MAX) { - sqd->total_num = PHY_LINKQUALITY_SLID_WIN_MAX; - last_evm = sqd->elements[sqd->index]; - sqd->total_val -= last_evm; - } - sqd->total_val += pattrib->signal_qual; - sqd->elements[sqd->index++] = pattrib->signal_qual; - if (sqd->index >= PHY_LINKQUALITY_SLID_WIN_MAX) - sqd->index = 0; - - /* <1> Showed on UI for user, in percentage. */ - avg_val = sqd->total_val / sqd->total_num; - padapter->recvpriv.signal = (u8)avg_val; - } -} - -static void process_rssi(struct _adapter *padapter, union recv_frame *prframe) -{ - u32 last_rssi, tmp_val; - struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib; - struct smooth_rssi_data *ssd = &padapter->recvpriv.signal_strength_data; - - if (ssd->total_num++ >= PHY_RSSI_SLID_WIN_MAX) { - ssd->total_num = PHY_RSSI_SLID_WIN_MAX; - last_rssi = ssd->elements[ssd->index]; - ssd->total_val -= last_rssi; - } - ssd->total_val += pattrib->signal_strength; - ssd->elements[ssd->index++] = pattrib->signal_strength; - if (ssd->index >= PHY_RSSI_SLID_WIN_MAX) - ssd->index = 0; - tmp_val = ssd->total_val / ssd->total_num; - padapter->recvpriv.rssi = (s8)translate2dbm(padapter, (u8)tmp_val); -} - -static void process_phy_info(struct _adapter *padapter, - union recv_frame *prframe) -{ - query_rx_phy_status(padapter, prframe); - process_rssi(padapter, prframe); - process_link_qual(padapter, prframe); -} - -int recv_func(struct _adapter *padapter, void *pcontext) -{ - struct rx_pkt_attrib *pattrib; - union recv_frame *prframe, *orig_prframe; - int retval = _SUCCESS; - struct __queue *pfree_recv_queue = &padapter->recvpriv.free_recv_queue; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - prframe = pcontext; - orig_prframe = prframe; - pattrib = &prframe->u.hdr.attrib; - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - if (pattrib->crc_err == 1) - padapter->mppriv.rx_crcerrpktcount++; - else - padapter->mppriv.rx_pktcount++; - if (!check_fwstate(pmlmepriv, WIFI_MP_LPBK_STATE)) { - /* free this recv_frame */ - r8712_free_recvframe(orig_prframe, pfree_recv_queue); - goto _exit_recv_func; - } - } - /* check the frame crtl field and decache */ - retval = r8712_validate_recv_frame(padapter, prframe); - if (retval != _SUCCESS) { - /* free this recv_frame */ - r8712_free_recvframe(orig_prframe, pfree_recv_queue); - goto _exit_recv_func; - } - process_phy_info(padapter, prframe); - prframe = r8712_decryptor(padapter, prframe); - if (!prframe) { - retval = _FAIL; - goto _exit_recv_func; - } - prframe = r8712_recvframe_chk_defrag(padapter, prframe); - if (!prframe) - goto _exit_recv_func; - prframe = r8712_portctrl(padapter, prframe); - if (!prframe) { - retval = _FAIL; - goto _exit_recv_func; - } - retval = r8712_process_recv_indicatepkts(padapter, prframe); - if (retval != _SUCCESS) { - r8712_free_recvframe(orig_prframe, pfree_recv_queue); - goto _exit_recv_func; - } -_exit_recv_func: - return retval; -} - -static void recvbuf2recvframe(struct _adapter *padapter, struct sk_buff *pskb) -{ - u8 *pbuf, shift_sz = 0; - u8 frag, mf; - uint pkt_len; - u32 transfer_len; - struct recv_stat *prxstat; - u16 pkt_cnt, drvinfo_sz, pkt_offset, tmp_len, alloc_sz; - struct __queue *pfree_recv_queue; - _pkt *pkt_copy = NULL; - union recv_frame *precvframe = NULL; - struct recv_priv *precvpriv = &padapter->recvpriv; - - pfree_recv_queue = &precvpriv->free_recv_queue; - pbuf = pskb->data; - prxstat = (struct recv_stat *)pbuf; - pkt_cnt = (le32_to_cpu(prxstat->rxdw2) >> 16) & 0xff; - pkt_len = le32_to_cpu(prxstat->rxdw0) & 0x00003fff; - transfer_len = pskb->len; - /* Test throughput with Netgear 3700 (No security) with Chariot 3T3R - * pairs. The packet count will be a big number so that the containing - * packet will effect the Rx reordering. - */ - if (transfer_len < pkt_len) { - /* In this case, it means the MAX_RECVBUF_SZ is too small to - * get the data from 8712u. - */ - return; - } - do { - prxstat = (struct recv_stat *)pbuf; - pkt_len = le32_to_cpu(prxstat->rxdw0) & 0x00003fff; - /* more fragment bit */ - mf = (le32_to_cpu(prxstat->rxdw1) >> 27) & 0x1; - /* ragmentation number */ - frag = (le32_to_cpu(prxstat->rxdw2) >> 12) & 0xf; - /* uint 2^3 = 8 bytes */ - drvinfo_sz = (le32_to_cpu(prxstat->rxdw0) & 0x000f0000) >> 16; - drvinfo_sz <<= 3; - if (pkt_len <= 0) - return; - /* Qos data, wireless lan header length is 26 */ - if ((le32_to_cpu(prxstat->rxdw0) >> 23) & 0x01) - shift_sz = 2; - precvframe = r8712_alloc_recvframe(pfree_recv_queue); - if (!precvframe) - return; - INIT_LIST_HEAD(&precvframe->u.hdr.list); - precvframe->u.hdr.precvbuf = NULL; /*can't access the precvbuf*/ - precvframe->u.hdr.len = 0; - tmp_len = pkt_len + drvinfo_sz + RXDESC_SIZE; - pkt_offset = (u16)round_up(tmp_len, 128); - /* for first fragment packet, driver need allocate 1536 + - * drvinfo_sz + RXDESC_SIZE to defrag packet. - */ - if ((mf == 1) && (frag == 0)) - /*1658+6=1664, 1664 is 128 alignment.*/ - alloc_sz = max_t(u16, tmp_len, 1658); - else - alloc_sz = tmp_len; - /* 2 is for IP header 4 bytes alignment in QoS packet case. - * 4 is for skb->data 4 bytes alignment. - */ - alloc_sz += 6; - pkt_copy = netdev_alloc_skb(padapter->pnetdev, alloc_sz); - if (!pkt_copy) - return; - - precvframe->u.hdr.pkt = pkt_copy; - skb_reserve(pkt_copy, 4 - ((addr_t)(pkt_copy->data) % 4)); - skb_reserve(pkt_copy, shift_sz); - memcpy(pkt_copy->data, pbuf, tmp_len); - precvframe->u.hdr.rx_head = pkt_copy->data; - precvframe->u.hdr.rx_data = pkt_copy->data; - precvframe->u.hdr.rx_tail = pkt_copy->data; - precvframe->u.hdr.rx_end = pkt_copy->data + alloc_sz; - - recvframe_put(precvframe, tmp_len); - recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE); - /* because the endian issue, driver avoid reference to the - * rxstat after calling update_recvframe_attrib_from_recvstat(); - */ - update_recvframe_attrib_from_recvstat(&precvframe->u.hdr.attrib, - prxstat); - r8712_recv_entry(precvframe); - transfer_len -= pkt_offset; - pbuf += pkt_offset; - pkt_cnt--; - precvframe = NULL; - pkt_copy = NULL; - } while ((transfer_len > 0) && pkt_cnt > 0); -} - -static void recv_tasklet(struct tasklet_struct *t) -{ - struct sk_buff *pskb; - struct _adapter *padapter = from_tasklet(padapter, t, - recvpriv.recv_tasklet); - struct recv_priv *precvpriv = &padapter->recvpriv; - - while (NULL != (pskb = skb_dequeue(&precvpriv->rx_skb_queue))) { - recvbuf2recvframe(padapter, pskb); - skb_reset_tail_pointer(pskb); - pskb->len = 0; - if (!skb_cloned(pskb)) - skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb); - else - consume_skb(pskb); - } -} diff --git a/drivers/staging/rtl8712/rtl8712_recv.h b/drivers/staging/rtl8712/rtl8712_recv.h deleted file mode 100644 index a1360dcf91cec..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_recv.h +++ /dev/null @@ -1,145 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL8712_RECV_H_ -#define _RTL8712_RECV_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -/* Realtek's v2.6.6 reduced this to 4. However, under heavy network and CPU - * loads, even 8 receive buffers might not be enough; cutting it to 4 seemed - * unwise. - */ -#define NR_RECVBUFF (8) - -#define NR_PREALLOC_RECV_SKB (8) -#define RXDESC_SIZE 24 -#define RXDESC_OFFSET RXDESC_SIZE -#define RECV_BLK_SZ 512 -#define RECV_BLK_CNT 16 -#define RECV_BLK_TH RECV_BLK_CNT -#define MAX_RECVBUF_SZ 9100 -#define RECVBUFF_ALIGN_SZ 512 -#define RSVD_ROOM_SZ (0) -/*These definition is used for Rx packet reordering.*/ -#define SN_LESS(a, b) (((a-b) & 0x800) != 0) -#define SN_EQUAL(a, b) (a == b) -#define REORDER_WAIT_TIME 30 /* (ms)*/ - -struct recv_stat { - __le32 rxdw0; - __le32 rxdw1; - __le32 rxdw2; - __le32 rxdw3; - __le32 rxdw4; - __le32 rxdw5; -}; - -struct phy_cck_rx_status { - /* For CCK rate descriptor. This is a unsigned 8:1 variable. - * LSB bit present 0.5. And MSB 7 bts present a signed value. - * Range from -64~+63.5. - */ - u8 adc_pwdb_X[4]; - u8 sq_rpt; - u8 cck_agc_rpt; -}; - -struct phy_stat { - __le32 phydw0; - __le32 phydw1; - __le32 phydw2; - __le32 phydw3; - __le32 phydw4; - __le32 phydw5; - __le32 phydw6; - __le32 phydw7; -}; - -#define PHY_STAT_GAIN_TRSW_SHT 0 -#define PHY_STAT_PWDB_ALL_SHT 4 -#define PHY_STAT_CFOSHO_SHT 5 -#define PHY_STAT_CCK_AGC_RPT_SHT 5 -#define PHY_STAT_CFOTAIL_SHT 9 -#define PHY_STAT_RXEVM_SHT 13 -#define PHY_STAT_RXSNR_SHT 15 -#define PHY_STAT_PDSNR_SHT 19 -#define PHY_STAT_CSI_CURRENT_SHT 21 -#define PHY_STAT_CSI_TARGET_SHT 23 -#define PHY_STAT_SIGEVM_SHT 25 -#define PHY_STAT_MAX_EX_PWR_SHT 26 - -union recvstat { - struct recv_stat recv_stat; - unsigned int value[RXDESC_SIZE >> 2]; -}; - -struct recv_buf { - struct list_head list; - spinlock_t recvbuf_lock; - u32 ref_cnt; - struct _adapter *adapter; - struct urb *purb; - _pkt *pskb; - u8 irp_pending; - u32 transfer_len; - uint len; - u8 *phead; - u8 *pdata; - u8 *ptail; - u8 *pend; - u8 *pbuf; - u8 *pallocated_buf; -}; - -/* - * head -----> - * data -----> - * payload - * tail -----> - * end -----> - * len = (unsigned int )(tail - data); - */ -struct recv_frame_hdr { - struct list_head list; - _pkt *pkt; - _pkt *pkt_newalloc; - struct _adapter *adapter; - u8 fragcnt; - struct rx_pkt_attrib attrib; - uint len; - u8 *rx_head; - u8 *rx_data; - u8 *rx_tail; - u8 *rx_end; - void *precvbuf; - struct sta_info *psta; - /*for A-MPDU Rx reordering buffer control*/ - struct recv_reorder_ctrl *preorder_ctrl; -}; - -union recv_frame { - union { - struct list_head list; - struct recv_frame_hdr hdr; - } u; -}; - -void r8712_init_recvbuf(struct _adapter *padapter, struct recv_buf *precvbuf); -void r8712_rxcmd_event_hdl(struct _adapter *padapter, void *prxcmdbuf); -s32 r8712_signal_scale_mapping(s32 cur_sig); -void r8712_reordering_ctrl_timeout_handler(void *pcontext); - -#endif - diff --git a/drivers/staging/rtl8712/rtl8712_regdef.h b/drivers/staging/rtl8712/rtl8712_regdef.h deleted file mode 100644 index 28aec9aa539fd..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_regdef.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_REGDEF_H__ -#define __RTL8712_REGDEF_H__ - -#include "rtl8712_syscfg_regdef.h" -#include "rtl8712_cmdctrl_regdef.h" -#include "rtl8712_macsetting_regdef.h" -#include "rtl8712_timectrl_regdef.h" -#include "rtl8712_fifoctrl_regdef.h" -#include "rtl8712_ratectrl_regdef.h" -#include "rtl8712_edcasetting_regdef.h" -#include "rtl8712_wmac_regdef.h" -#include "rtl8712_powersave_regdef.h" -#include "rtl8712_gp_regdef.h" -#include "rtl8712_debugctrl_regdef.h" - -#define HIMR (RTL8712_INTERRUPT_ + 0x08) - -#endif /* __RTL8712_REGDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_security_bitdef.h b/drivers/staging/rtl8712/rtl8712_security_bitdef.h deleted file mode 100644 index 44275ef455a0a..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_security_bitdef.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_SECURITY_BITDEF_H__ -#define __RTL8712_SECURITY_BITDEF_H__ - -/*CAMCMD*/ -#define _SECCAM_POLLING BIT(31) -#define _SECCAM_CLR BIT(30) -#define _SECCAM_WE BIT(16) -#define _SECCAM_ADR_MSK 0x000000FF -#define _SECCAM_ADR_SHT 0 - -/*CAMDBG*/ -#define _SECCAM_INFO BIT(31) -#define _SEC_KEYFOUND BIT(30) -#define _SEC_CONFIG_MSK 0x3F000000 -#define _SEC_CONFIG_SHT 24 -#define _SEC_KEYCONTENT_MSK 0x00FFFFFF -#define _SEC_KEYCONTENT_SHT 0 - -/*SECCFG*/ -#define _NOSKMC BIT(5) -#define _SKBYA2 BIT(4) -#define _RXDEC BIT(3) -#define _TXENC BIT(2) -#define _RXUSEDK BIT(1) -#define _TXUSEDK BIT(0) - -#endif /*__RTL8712_SECURITY_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_spec.h b/drivers/staging/rtl8712/rtl8712_spec.h deleted file mode 100644 index 613a410e5714b..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_spec.h +++ /dev/null @@ -1,121 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_SPEC_H__ -#define __RTL8712_SPEC_H__ - -#define RTL8712_IOBASE_TXPKT 0x10200000 /*IOBASE_TXPKT*/ -#define RTL8712_IOBASE_RXPKT 0x10210000 /*IOBASE_RXPKT*/ -#define RTL8712_IOBASE_RXCMD 0x10220000 /*IOBASE_RXCMD*/ -#define RTL8712_IOBASE_TXSTATUS 0x10230000 /*IOBASE_TXSTATUS*/ -#define RTL8712_IOBASE_RXSTATUS 0x10240000 /*IOBASE_RXSTATUS*/ -#define RTL8712_IOBASE_IOREG 0x10250000 /*IOBASE_IOREG ADDR*/ -#define RTL8712_IOBASE_SCHEDULER 0x10260000 /*IOBASE_SCHEDULE*/ - -#define RTL8712_IOBASE_TRXDMA 0x10270000 /*IOBASE_TRXDMA*/ -#define RTL8712_IOBASE_TXLLT 0x10280000 /*IOBASE_TXLLT*/ -#define RTL8712_IOBASE_WMAC 0x10290000 /*IOBASE_WMAC*/ -#define RTL8712_IOBASE_FW2HW 0x102A0000 /*IOBASE_FW2HW*/ -#define RTL8712_IOBASE_ACCESS_PHYREG 0x102B0000 /*IOBASE_ACCESS_PHYREG*/ - -#define RTL8712_IOBASE_FF 0x10300000 /*IOBASE_FIFO 0x1031000~0x103AFFFF*/ - -/*IOREG Offset for 8712*/ -#define RTL8712_SYSCFG_ RTL8712_IOBASE_IOREG -#define RTL8712_CMDCTRL_ (RTL8712_IOBASE_IOREG + 0x40) -#define RTL8712_MACIDSETTING_ (RTL8712_IOBASE_IOREG + 0x50) -#define RTL8712_TIMECTRL_ (RTL8712_IOBASE_IOREG + 0x80) -#define RTL8712_FIFOCTRL_ (RTL8712_IOBASE_IOREG + 0xA0) -#define RTL8712_RATECTRL_ (RTL8712_IOBASE_IOREG + 0x160) -#define RTL8712_EDCASETTING_ (RTL8712_IOBASE_IOREG + 0x1D0) -#define RTL8712_WMAC_ (RTL8712_IOBASE_IOREG + 0x200) -#define RTL8712_SECURITY_ (RTL8712_IOBASE_IOREG + 0x240) -#define RTL8712_POWERSAVE_ (RTL8712_IOBASE_IOREG + 0x260) -#define RTL8712_GP_ (RTL8712_IOBASE_IOREG + 0x2E0) -#define RTL8712_INTERRUPT_ (RTL8712_IOBASE_IOREG + 0x300) -#define RTL8712_DEBUGCTRL_ (RTL8712_IOBASE_IOREG + 0x310) -#define RTL8712_OFFLOAD_ (RTL8712_IOBASE_IOREG + 0x2D0) - -/*FIFO for 8712*/ -#define RTL8712_DMA_BCNQ (RTL8712_IOBASE_FF + 0x10000) -#define RTL8712_DMA_MGTQ (RTL8712_IOBASE_FF + 0x20000) -#define RTL8712_DMA_BMCQ (RTL8712_IOBASE_FF + 0x30000) -#define RTL8712_DMA_VOQ (RTL8712_IOBASE_FF + 0x40000) -#define RTL8712_DMA_VIQ (RTL8712_IOBASE_FF + 0x50000) -#define RTL8712_DMA_BEQ (RTL8712_IOBASE_FF + 0x60000) -#define RTL8712_DMA_BKQ (RTL8712_IOBASE_FF + 0x70000) -#define RTL8712_DMA_RX0FF (RTL8712_IOBASE_FF + 0x80000) -#define RTL8712_DMA_H2CCMD (RTL8712_IOBASE_FF + 0x90000) -#define RTL8712_DMA_C2HCMD (RTL8712_IOBASE_FF + 0xA0000) - -/*------------------------------*/ - -/*BIT 16 15*/ -#define DID_SDIO_LOCAL 0 /* 0 0*/ -#define DID_WLAN_IOREG 1 /* 0 1*/ -#define DID_WLAN_FIFO 3 /* 1 1*/ -#define DID_UNDEFINE (-1) - -#define CMD_ADDR_MAPPING_SHIFT 2 /*SDIO CMD ADDR MAPPING, - *shift 2 bit for match - * offset[14:2] - */ - -/*Offset for SDIO LOCAL*/ -#define OFFSET_SDIO_LOCAL 0x0FFF - -/*Offset for WLAN IOREG*/ -#define OFFSET_WLAN_IOREG 0x0FFF - -/*Offset for WLAN FIFO*/ -#define OFFSET_TX_BCNQ 0x0300 -#define OFFSET_TX_HIQ 0x0310 -#define OFFSET_TX_CMDQ 0x0320 -#define OFFSET_TX_MGTQ 0x0330 -#define OFFSET_TX_HCCAQ 0x0340 -#define OFFSET_TX_VOQ 0x0350 -#define OFFSET_TX_VIQ 0x0360 -#define OFFSET_TX_BEQ 0x0370 -#define OFFSET_TX_BKQ 0x0380 -#define OFFSET_RX_RX0FFQ 0x0390 -#define OFFSET_RX_C2HFFQ 0x03A0 - -#define BK_QID_01 1 -#define BK_QID_02 2 -#define BE_QID_01 0 -#define BE_QID_02 3 -#define VI_QID_01 4 -#define VI_QID_02 5 -#define VO_QID_01 6 -#define VO_QID_02 7 -#define HCCA_QID_01 8 -#define HCCA_QID_02 9 -#define HCCA_QID_03 10 -#define HCCA_QID_04 11 -#define HCCA_QID_05 12 -#define HCCA_QID_06 13 -#define HCCA_QID_07 14 -#define HCCA_QID_08 15 -#define HI_QID 17 -#define CMD_QID 19 -#define MGT_QID 18 -#define BCN_QID 16 - -#include "rtl8712_regdef.h" - -#include "rtl8712_bitdef.h" - -#include "basic_types.h" - -#endif /* __RTL8712_SPEC_H__ */ - diff --git a/drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h b/drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h deleted file mode 100644 index d92df3fbd2b19..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h +++ /dev/null @@ -1,163 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_SYSCFG_BITDEF_H__ -#define __RTL8712_SYSCFG_BITDEF_H__ - -/*SYS_PWR_CTRL*/ -/*SRCTRL0*/ -/*SRCTRL1*/ -/*SYS_CLKR*/ - -/*SYS_IOS_CTRL*/ -#define iso_LDR2RP_SHT 8 /* EE Loader to Retention Path*/ -#define iso_LDR2RP BIT(iso_LDR2RP_SHT) /* 1:isolation, 0:attach*/ - -/*SYS_CTRL*/ -#define FEN_DIO_SDIO_SHT 0 -#define FEN_DIO_SDIO BIT(FEN_DIO_SDIO_SHT) -#define FEN_SDIO_SHT 1 -#define FEN_SDIO BIT(FEN_SDIO_SHT) -#define FEN_USBA_SHT 2 -#define FEN_USBA BIT(FEN_USBA_SHT) -#define FEN_UPLL_SHT 3 -#define FEN_UPLL BIT(FEN_UPLL_SHT) -#define FEN_USBD_SHT 4 -#define FEN_USBD BIT(FEN_USBD_SHT) -#define FEN_DIO_PCIE_SHT 5 -#define FEN_DIO_PCIE BIT(FEN_DIO_PCIE_SHT) -#define FEN_PCIEA_SHT 6 -#define FEN_PCIEA BIT(FEN_PCIEA_SHT) -#define FEN_PPLL_SHT 7 -#define FEN_PPLL BIT(FEN_PPLL_SHT) -#define FEN_PCIED_SHT 8 -#define FEN_PCIED BIT(FEN_PCIED_SHT) -#define FEN_CPUEN_SHT 10 -#define FEN_CPUEN BIT(FEN_CPUEN_SHT) -#define FEN_DCORE_SHT 11 -#define FEN_DCORE BIT(FEN_DCORE_SHT) -#define FEN_ELDR_SHT 12 -#define FEN_ELDR BIT(FEN_ELDR_SHT) -#define PWC_DV2LDR_SHT 13 -#define PWC_DV2LDR BIT(PWC_DV2LDR_SHT) /* Loader Power Enable*/ - -/*=== SYS_CLKR ===*/ -#define SYS_CLKSEL_SHT 0 -#define SYS_CLKSEL BIT(SYS_CLKSEL_SHT) /* System Clock 80MHz*/ -#define PS_CLKSEL_SHT 1 -#define PS_CLKSEL BIT(PS_CLKSEL_SHT) /*System power save - * clock select. - */ -#define CPU_CLKSEL_SHT 2 -#define CPU_CLKSEL BIT(CPU_CLKSEL_SHT) /* System Clock select, - * 1: AFE source, - * 0: System clock(L-Bus) - */ -#define INT32K_EN_SHT 3 -#define INT32K_EN BIT(INT32K_EN_SHT) -#define MACSLP_SHT 4 -#define MACSLP BIT(MACSLP_SHT) -#define MAC_CLK_EN_SHT 11 -#define MAC_CLK_EN BIT(MAC_CLK_EN_SHT) /* MAC Clock Enable.*/ -#define SYS_CLK_EN_SHT 12 -#define SYS_CLK_EN BIT(SYS_CLK_EN_SHT) -#define RING_CLK_EN_SHT 13 -#define RING_CLK_EN BIT(RING_CLK_EN_SHT) -#define SWHW_SEL_SHT 14 -#define SWHW_SEL BIT(SWHW_SEL_SHT) /* Load done, - * control path switch. - */ -#define FWHW_SEL_SHT 15 -#define FWHW_SEL BIT(FWHW_SEL_SHT) /* Sleep exit, - * control path switch. - */ - -/*9346CR*/ -#define _VPDIDX_MSK 0xFF00 -#define _VPDIDX_SHT 8 -#define _EEM_MSK 0x00C0 -#define _EEM_SHT 6 -#define _EEM0 BIT(6) -#define _EEM1 BIT(7) -#define _EEPROM_EN BIT(5) -#define _9356SEL BIT(4) -#define _EECS BIT(3) -#define _EESK BIT(2) -#define _EEDI BIT(1) -#define _EEDO BIT(0) - -/*AFE_MISC*/ -#define AFE_MISC_USB_MBEN_SHT 7 -#define AFE_MISC_USB_MBEN BIT(AFE_MISC_USB_MBEN_SHT) -#define AFE_MISC_USB_BGEN_SHT 6 -#define AFE_MISC_USB_BGEN BIT(AFE_MISC_USB_BGEN_SHT) -#define AFE_MISC_LD12_VDAJ_SHT 4 -#define AFE_MISC_LD12_VDAJ_MSK 0X0030 -#define AFE_MISC_LD12_VDAJ BIT(AFE_MISC_LD12_VDAJ_SHT) -#define AFE_MISC_I32_EN_SHT 3 -#define AFE_MISC_I32_EN BIT(AFE_MISC_I32_EN_SHT) -#define AFE_MISC_E32_EN_SHT 2 -#define AFE_MISC_E32_EN BIT(AFE_MISC_E32_EN_SHT) -#define AFE_MISC_MBEN_SHT 1 -#define AFE_MISC_MBEN BIT(AFE_MISC_MBEN_SHT)/* Enable AFE Macro - * Block's Mbias. - */ -#define AFE_MISC_BGEN_SHT 0 -#define AFE_MISC_BGEN BIT(AFE_MISC_BGEN_SHT)/* Enable AFE Macro - * Block's Bandgap. - */ - -/*--------------------------------------------------------------------------*/ -/* SPS1_CTRL bits (Offset 0x18-1E, 56bits)*/ -/*--------------------------------------------------------------------------*/ -#define SPS1_SWEN BIT(1) /* Enable vsps18 SW Macro Block.*/ -#define SPS1_LDEN BIT(0) /* Enable VSPS12 LDO Macro block.*/ - -/*----------------------------------------------------------------------------*/ -/* LDOA15_CTRL bits (Offset 0x20, 8bits)*/ -/*----------------------------------------------------------------------------*/ -#define LDA15_EN BIT(0) /* Enable LDOA15 Macro Block*/ - -/*----------------------------------------------------------------------------*/ -/* 8192S LDOV12D_CTRL bit (Offset 0x21, 8bits)*/ -/*----------------------------------------------------------------------------*/ -#define LDV12_EN BIT(0) /* Enable LDOVD12 Macro Block*/ -#define LDV12_SDBY BIT(1) /* LDOVD12 standby mode*/ - -/*CLK_PS_CTRL*/ -#define _CLK_GATE_EN BIT(0) - -/* EFUSE_CTRL*/ -#define EF_FLAG BIT(31) /* Access Flag, Write:1; - * Read:0 - */ -#define EF_PGPD 0x70000000 /* E-fuse Program time*/ -#define EF_RDT 0x0F000000 /* E-fuse read time: in the - * unit of cycle time - */ -#define EF_PDN_EN BIT(19) /* EFuse Power down enable*/ -#define ALD_EN BIT(18) /* Autoload Enable*/ -#define EF_ADDR 0x0003FF00 /* Access Address*/ -#define EF_DATA 0x000000FF /* Access Data*/ - -/* EFUSE_TEST*/ -#define LDOE25_EN BIT(31) /* Enable LDOE25 Macro Block*/ - -/* EFUSE_CLK_CTRL*/ -#define EFUSE_CLK_EN BIT(1) /* E-Fuse Clock Enable*/ -#define EFUSE_CLK_SEL BIT(0) /* E-Fuse Clock Select, - * 0:500K, 1:40M - */ - -#endif /*__RTL8712_SYSCFG_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_syscfg_regdef.h b/drivers/staging/rtl8712/rtl8712_syscfg_regdef.h deleted file mode 100644 index da5efcdedabe2..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_syscfg_regdef.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_SYSCFG_REGDEF_H__ -#define __RTL8712_SYSCFG_REGDEF_H__ - -#define SYS_ISO_CTRL (RTL8712_SYSCFG_ + 0x0000) -#define SYS_FUNC_EN (RTL8712_SYSCFG_ + 0x0002) -#define PMC_FSM (RTL8712_SYSCFG_ + 0x0004) -#define SYS_CLKR (RTL8712_SYSCFG_ + 0x0008) -#define EE_9346CR (RTL8712_SYSCFG_ + 0x000A) -#define EE_VPD (RTL8712_SYSCFG_ + 0x000C) -#define AFE_MISC (RTL8712_SYSCFG_ + 0x0010) -#define SPS0_CTRL (RTL8712_SYSCFG_ + 0x0011) -#define SPS1_CTRL (RTL8712_SYSCFG_ + 0x0018) -#define RF_CTRL (RTL8712_SYSCFG_ + 0x001F) -#define LDOA15_CTRL (RTL8712_SYSCFG_ + 0x0020) -#define LDOV12D_CTRL (RTL8712_SYSCFG_ + 0x0021) -#define LDOHCI12_CTRL (RTL8712_SYSCFG_ + 0x0022) -#define LDO_USB_CTRL (RTL8712_SYSCFG_ + 0x0023) -#define LPLDO_CTRL (RTL8712_SYSCFG_ + 0x0024) -#define AFE_XTAL_CTRL (RTL8712_SYSCFG_ + 0x0026) -#define AFE_PLL_CTRL (RTL8712_SYSCFG_ + 0x0028) -#define EFUSE_CTRL (RTL8712_SYSCFG_ + 0x0030) -#define EFUSE_TEST (RTL8712_SYSCFG_ + 0x0034) -#define PWR_DATA (RTL8712_SYSCFG_ + 0x0038) -#define DPS_TIMER (RTL8712_SYSCFG_ + 0x003C) -#define RCLK_MON (RTL8712_SYSCFG_ + 0x003E) -#define EFUSE_CLK_CTRL (RTL8712_SYSCFG_ + 0x02F8) - -#endif /*__RTL8712_SYSCFG_REGDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h deleted file mode 100644 index d7bc9dd5cecd2..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_TIMECTRL_BITDEF_H__ -#define __RTL8712_TIMECTRL_BITDEF_H__ - -/*TSFTR*/ -/*SLOT*/ -/*USTIME*/ - -/*TUBASE*/ -#define _TUBASE_MSK 0x07FF - -/*SIFS_CCK*/ -#define _SIFS_CCK_TRX_MSK 0xFF00 -#define _SIFS_CCK_TRX_SHT 0x8 -#define _SIFS_CCK_CTX_MSK 0x00FF -#define _SIFS_CCK_CTX_SHT 0 - -/*SIFS_OFDM*/ -#define _SIFS_OFDM_TRX_MSK 0xFF00 -#define _SIFS_OFDM_TRX_SHT 0x8 -#define _SIFS_OFDM_CTX_MSK 0x00FF -#define _SIFS_OFDM_CTX_SHT 0 - -/*PIFS*/ -/*ACKTO*/ -/*EIFS*/ -/*BCNITV*/ -/*ATIMWND*/ - -/*DRVERLYINT*/ -#define _ENSWBCN BIT(15) -#define _DRVERLY_TU_MSK 0x0FF0 -#define _DRVERLY_TU_SHT 4 -#define _DRVERLY_US_MSK 0x000F -#define _DRVERLY_US_SHT 0 - -/*BCNDMATIM*/ -#define _BCNDMATIM_MSK 0x03FF - -/*BCNERRTH*/ -/*MLT*/ - -#endif /* __RTL8712_TIMECTRL_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_timectrl_regdef.h b/drivers/staging/rtl8712/rtl8712_timectrl_regdef.h deleted file mode 100644 index b51603f1b8800..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_timectrl_regdef.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL8712_TIMECTRL_REGDEF_H__ -#define __RTL8712_TIMECTRL_REGDEF_H__ - -#define TSFTR (RTL8712_TIMECTRL_ + 0x00) -#define USTIME (RTL8712_TIMECTRL_ + 0x08) -#define SLOT (RTL8712_TIMECTRL_ + 0x09) -#define TUBASE (RTL8712_TIMECTRL_ + 0x0A) -#define SIFS_CCK (RTL8712_TIMECTRL_ + 0x0C) -#define SIFS_OFDM (RTL8712_TIMECTRL_ + 0x0E) -#define PIFS (RTL8712_TIMECTRL_ + 0x10) -#define ACKTO (RTL8712_TIMECTRL_ + 0x11) -#define EIFS (RTL8712_TIMECTRL_ + 0x12) -#define BCNITV (RTL8712_TIMECTRL_ + 0x14) -#define ATIMWND (RTL8712_TIMECTRL_ + 0x16) -#define DRVERLYINT (RTL8712_TIMECTRL_ + 0x18) -#define BCNDMATIM (RTL8712_TIMECTRL_ + 0x1A) -#define BCNERRTH (RTL8712_TIMECTRL_ + 0x1C) -#define MLT (RTL8712_TIMECTRL_ + 0x1D) - -#endif /* __RTL8712_TIMECTRL_REGDEF_H__ */ diff --git a/drivers/staging/rtl8712/rtl8712_wmac_bitdef.h b/drivers/staging/rtl8712/rtl8712_wmac_bitdef.h deleted file mode 100644 index ea164e4823476..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_wmac_bitdef.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_WMAC_BITDEF_H__ -#define __RTL8712_WMAC_BITDEF_H__ - -/*NAVCTRL*/ -#define _NAV_UPPER_EN BIT(18) -#define _NAV_MTO_EN BIT(17) -#define _NAV_UPPER BIT(16) -#define _NAV_MTO_MSK 0xFF00 -#define _NAV_MTO_SHT 8 -#define _RTSRST_MSK 0x00FF -#define _RTSRST_SHT 0 - -/*BWOPMODE*/ -#define _20MHZBW BIT(2) - -/*BACAMCMD*/ -#define _BACAM_POLL BIT(31) -#define _BACAM_RST BIT(17) -#define _BACAM_RW BIT(16) -#define _BACAM_ADDR_MSK 0x0000007F -#define _BACAM_ADDR_SHT 0 - -/*LBDLY*/ -#define _LBDLY_MSK 0x1F - -/*FWDLY*/ -#define _FWDLY_MSK 0x0F - -/*RXERR_RPT*/ -#define _RXERR_RPT_SEL_MSK 0xF0000000 -#define _RXERR_RPT_SEL_SHT 28 -#define _RPT_CNT_MSK 0x000FFFFF -#define _RPT_CNT_SHT 0 - -#endif /*__RTL8712_WMAC_BITDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_wmac_regdef.h b/drivers/staging/rtl8712/rtl8712_wmac_regdef.h deleted file mode 100644 index dfe3e9fbed431..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_wmac_regdef.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_WMAC_REGDEF_H__ -#define __RTL8712_WMAC_REGDEF_H__ - -#define NAVCTRL (RTL8712_WMAC_ + 0x00) -#define BWOPMODE (RTL8712_WMAC_ + 0x03) -#define BACAMCMD (RTL8712_WMAC_ + 0x04) -#define BACAMCONTENT (RTL8712_WMAC_ + 0x08) -#define LBDLY (RTL8712_WMAC_ + 0x10) -#define FWDLY (RTL8712_WMAC_ + 0x11) -#define HWPC_RX_CTRL (RTL8712_WMAC_ + 0x18) -#define MQ (RTL8712_WMAC_ + 0x20) -#define MA (RTL8712_WMAC_ + 0x22) -#define MS (RTL8712_WMAC_ + 0x24) -#define CLM_RESULT (RTL8712_WMAC_ + 0x27) -#define NHM_RPI_CNT (RTL8712_WMAC_ + 0x28) -#define RXERR_RPT (RTL8712_WMAC_ + 0x30) -#define NAV_PROT_LEN (RTL8712_WMAC_ + 0x34) -#define CFEND_TH (RTL8712_WMAC_ + 0x36) -#define AMPDU_MIN_SPACE (RTL8712_WMAC_ + 0x37) -#define TXOP_STALL_CTRL (RTL8712_WMAC_ + 0x38) - -#endif /*__RTL8712_WMAC_REGDEF_H__*/ - diff --git a/drivers/staging/rtl8712/rtl8712_xmit.c b/drivers/staging/rtl8712/rtl8712_xmit.c deleted file mode 100644 index 12f2fdb1b3cbc..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_xmit.c +++ /dev/null @@ -1,732 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl8712_xmit.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL8712_XMIT_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "wifi.h" -#include "osdep_intf.h" -#include "usb_ops.h" - -static void dump_xframe(struct _adapter *padapter, - struct xmit_frame *pxmitframe); -static void update_txdesc(struct xmit_frame *pxmitframe, uint *pmem, int sz); - -sint _r8712_init_hw_txqueue(struct hw_txqueue *phw_txqueue, u8 ac_tag) -{ - phw_txqueue->ac_tag = ac_tag; - switch (ac_tag) { - case BE_QUEUE_INX: - phw_txqueue->ff_hwaddr = RTL8712_DMA_BEQ; - break; - case BK_QUEUE_INX: - phw_txqueue->ff_hwaddr = RTL8712_DMA_BKQ; - break; - case VI_QUEUE_INX: - phw_txqueue->ff_hwaddr = RTL8712_DMA_VIQ; - break; - case VO_QUEUE_INX: - phw_txqueue->ff_hwaddr = RTL8712_DMA_VOQ; - break; - case BMC_QUEUE_INX: - phw_txqueue->ff_hwaddr = RTL8712_DMA_BEQ; - break; - } - return _SUCCESS; -} - -int r8712_txframes_sta_ac_pending(struct _adapter *padapter, - struct pkt_attrib *pattrib) -{ - struct sta_info *psta; - struct tx_servq *ptxservq; - int priority = pattrib->priority; - - psta = pattrib->psta; - switch (priority) { - case 1: - case 2: - ptxservq = &psta->sta_xmitpriv.bk_q; - break; - case 4: - case 5: - ptxservq = &psta->sta_xmitpriv.vi_q; - break; - case 6: - case 7: - ptxservq = &psta->sta_xmitpriv.vo_q; - break; - case 0: - case 3: - default: - ptxservq = &psta->sta_xmitpriv.be_q; - break; - } - return ptxservq->qcnt; -} - -static u32 get_ff_hwaddr(struct xmit_frame *pxmitframe) -{ - u32 addr = 0; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - struct _adapter *padapter = pxmitframe->padapter; - struct dvobj_priv *pdvobj = &padapter->dvobjpriv; - - if (pxmitframe->frame_tag == TXAGG_FRAMETAG) { - addr = RTL8712_DMA_H2CCMD; - } else if (pxmitframe->frame_tag == MGNT_FRAMETAG) { - addr = RTL8712_DMA_MGTQ; - } else if (pdvobj->nr_endpoint == 6) { - switch (pattrib->priority) { - case 0: - case 3: - addr = RTL8712_DMA_BEQ; - break; - case 1: - case 2: - addr = RTL8712_DMA_BKQ; - break; - case 4: - case 5: - addr = RTL8712_DMA_VIQ; - break; - case 6: - case 7: - addr = RTL8712_DMA_VOQ; - break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - addr = RTL8712_DMA_H2CCMD; - break; - default: - addr = RTL8712_DMA_BEQ; - break; - } - } else if (pdvobj->nr_endpoint == 4) { - switch (pattrib->qsel) { - case 0: - case 3: - case 1: - case 2: - addr = RTL8712_DMA_BEQ;/*RTL8712_EP_LO;*/ - break; - case 4: - case 5: - case 6: - case 7: - addr = RTL8712_DMA_VOQ;/*RTL8712_EP_HI;*/ - break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - addr = RTL8712_DMA_H2CCMD; - break; - default: - addr = RTL8712_DMA_BEQ;/*RTL8712_EP_LO;*/ - break; - } - } - return addr; -} - -static struct xmit_frame *dequeue_one_xmitframe(struct xmit_priv *pxmitpriv, - struct hw_xmit *phwxmit, struct tx_servq *ptxservq, - struct __queue *pframe_queue) -{ - struct list_head *xmitframe_plist, *xmitframe_phead; - struct xmit_frame *pxmitframe = NULL; - - xmitframe_phead = &pframe_queue->queue; - xmitframe_plist = xmitframe_phead->next; - if (!end_of_queue_search(xmitframe_phead, xmitframe_plist)) { - pxmitframe = container_of(xmitframe_plist, - struct xmit_frame, list); - list_del_init(&pxmitframe->list); - ptxservq->qcnt--; - phwxmit->txcmdcnt++; - } - return pxmitframe; -} - -static struct xmit_frame *dequeue_xframe_ex(struct xmit_priv *pxmitpriv, - struct hw_xmit *phwxmit_i, sint entry) -{ - unsigned long irqL0; - struct list_head *sta_plist, *sta_phead; - struct hw_xmit *phwxmit; - struct tx_servq *ptxservq = NULL; - struct __queue *pframe_queue = NULL; - struct xmit_frame *pxmitframe = NULL; - int i, inx[4]; - int j, acirp_cnt[4]; - - /*entry indx: 0->vo, 1->vi, 2->be, 3->bk.*/ - inx[0] = 0; acirp_cnt[0] = pxmitpriv->voq_cnt; - inx[1] = 1; acirp_cnt[1] = pxmitpriv->viq_cnt; - inx[2] = 2; acirp_cnt[2] = pxmitpriv->beq_cnt; - inx[3] = 3; acirp_cnt[3] = pxmitpriv->bkq_cnt; - for (i = 0; i < 4; i++) { - for (j = i + 1; j < 4; j++) { - if (acirp_cnt[j] < acirp_cnt[i]) { - swap(acirp_cnt[i], acirp_cnt[j]); - swap(inx[i], inx[j]); - } - } - } - spin_lock_irqsave(&pxmitpriv->lock, irqL0); - for (i = 0; i < entry; i++) { - phwxmit = phwxmit_i + inx[i]; - sta_phead = &phwxmit->sta_queue->queue; - sta_plist = sta_phead->next; - while (!end_of_queue_search(sta_phead, sta_plist)) { - ptxservq = container_of(sta_plist, struct tx_servq, tx_pending); - pframe_queue = &ptxservq->sta_pending; - pxmitframe = dequeue_one_xmitframe(pxmitpriv, phwxmit, ptxservq, - pframe_queue); - if (pxmitframe) { - phwxmit->accnt--; - goto exit_dequeue_xframe_ex; - } - sta_plist = sta_plist->next; - /*Remove sta node when there are no pending packets.*/ - if (list_empty(&pframe_queue->queue)) { - /* must be done after sta_plist->next - * and before break - */ - list_del_init(&ptxservq->tx_pending); - } - } - } -exit_dequeue_xframe_ex: - spin_unlock_irqrestore(&pxmitpriv->lock, irqL0); - return pxmitframe; -} - -void r8712_do_queue_select(struct _adapter *padapter, struct pkt_attrib *pattrib) -{ - unsigned int qsel = 0; - struct dvobj_priv *pdvobj = &padapter->dvobjpriv; - - if (pdvobj->nr_endpoint == 6) { - qsel = (unsigned int)pattrib->priority; - } else if (pdvobj->nr_endpoint == 4) { - qsel = (unsigned int)pattrib->priority; - if (qsel == 0 || qsel == 3) - qsel = 3; - else if (qsel == 1 || qsel == 2) - qsel = 1; - else if (qsel == 4 || qsel == 5) - qsel = 5; - else if (qsel == 6 || qsel == 7) - qsel = 7; - else - qsel = 3; - } - pattrib->qsel = qsel; -} - -#ifdef CONFIG_R8712_TX_AGGR -void r8712_construct_txaggr_cmd_desc(struct xmit_buf *pxmitbuf) -{ - struct tx_desc *ptx_desc = (struct tx_desc *)pxmitbuf->pbuf; - - /* Fill up TxCmd Descriptor according as USB FW Tx Aggregation info.*/ - /* dw0 */ - ptx_desc->txdw0 = cpu_to_le32(CMD_HDR_SZ & 0xffff); - ptx_desc->txdw0 |= - cpu_to_le32(((TXDESC_SIZE + OFFSET_SZ) << OFFSET_SHT) & - 0x00ff0000); - ptx_desc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); - - /* dw1 */ - ptx_desc->txdw1 |= cpu_to_le32((0x13 << QSEL_SHT) & 0x00001f00); -} - -void r8712_construct_txaggr_cmd_hdr(struct xmit_buf *pxmitbuf) -{ - struct xmit_frame *pxmitframe = (struct xmit_frame *) - pxmitbuf->priv_data; - struct _adapter *padapter = pxmitframe->padapter; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct cmd_hdr *pcmd_hdr = (struct cmd_hdr *) - (pxmitbuf->pbuf + TXDESC_SIZE); - - /* Fill up Cmd Header for USB FW Tx Aggregation.*/ - /* dw0 */ - pcmd_hdr->cmd_dw0 = cpu_to_le32((GEN_CMD_CODE(_AMSDU_TO_AMPDU) << 16) | - (pcmdpriv->cmd_seq << 24)); - pcmdpriv->cmd_seq++; -} - -void r8712_append_mpdu_unit(struct xmit_buf *pxmitbuf, - struct xmit_frame *pxmitframe) -{ - struct _adapter *padapter = pxmitframe->padapter; - struct tx_desc *ptx_desc = (struct tx_desc *)pxmitbuf->pbuf; - int last_txcmdsz = 0; - int padding_sz = 0; - - /* 802.3->802.11 converter */ - r8712_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe); - /* free skb struct */ - r8712_xmit_complete(padapter, pxmitframe); - if (pxmitframe->attrib.ether_type != 0x0806) { - if ((pxmitframe->attrib.ether_type != 0x888e) && - (pxmitframe->attrib.dhcp_pkt != 1)) { - r8712_issue_addbareq_cmd(padapter, pxmitframe->attrib.priority); - } - } - pxmitframe->last[0] = 1; - update_txdesc(pxmitframe, (uint *)(pxmitframe->buf_addr), pxmitframe->attrib.last_txcmdsz); - /*padding zero */ - last_txcmdsz = pxmitframe->attrib.last_txcmdsz; - padding_sz = (8 - (last_txcmdsz % 8)); - if ((last_txcmdsz % 8) != 0) { - int i; - - for (i = 0; i < padding_sz; i++) - *(pxmitframe->buf_addr + TXDESC_SIZE + last_txcmdsz + - i) = 0; - } - /* Add the new mpdu's length */ - ptx_desc->txdw0 = cpu_to_le32((ptx_desc->txdw0 & 0xffff0000) | - ((ptx_desc->txdw0 & 0x0000ffff) + - ((TXDESC_SIZE + last_txcmdsz + padding_sz) & - 0x0000ffff))); -} - -void r8712_xmitframe_aggr_1st(struct xmit_buf *pxmitbuf, - struct xmit_frame *pxmitframe) -{ - /* linux complete context doesn't need to protect */ - pxmitframe->pxmitbuf = pxmitbuf; - pxmitbuf->priv_data = pxmitframe; - pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0]; - /* buffer addr assoc */ - pxmitframe->buf_addr = pxmitbuf->pbuf + TXDESC_SIZE + CMD_HDR_SZ; - /*RTL8712_DMA_H2CCMD */ - r8712_construct_txaggr_cmd_desc(pxmitbuf); - r8712_construct_txaggr_cmd_hdr(pxmitbuf); - r8712_append_mpdu_unit(pxmitbuf, pxmitframe); - pxmitbuf->aggr_nr = 1; -} - -u16 r8712_xmitframe_aggr_next(struct xmit_buf *pxmitbuf, struct xmit_frame *pxmitframe) -{ - pxmitframe->pxmitbuf = pxmitbuf; - pxmitbuf->priv_data = pxmitframe; - pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0]; - /* buffer addr assoc */ - pxmitframe->buf_addr = pxmitbuf->pbuf + TXDESC_SIZE + - (((struct tx_desc *)pxmitbuf->pbuf)->txdw0 & 0x0000ffff); - r8712_append_mpdu_unit(pxmitbuf, pxmitframe); - r8712_free_xmitframe_ex(&pxmitframe->padapter->xmitpriv, - pxmitframe); - pxmitbuf->aggr_nr++; - - return TXDESC_SIZE + - (((struct tx_desc *)pxmitbuf->pbuf)->txdw0 & 0x0000ffff); -} - -void r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf, - struct xmit_frame *pxmitframe) -{ - struct _adapter *padapter = pxmitframe->padapter; - struct dvobj_priv *pdvobj = &padapter->dvobjpriv; - struct tx_desc *ptxdesc = pxmitbuf->pbuf; - struct cmd_hdr *pcmd_hdr = (struct cmd_hdr *) - (pxmitbuf->pbuf + TXDESC_SIZE); - u16 total_length = (u16)(ptxdesc->txdw0 & 0xffff); - - /* use 1st xmitframe as media */ - xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf); - pcmd_hdr->cmd_dw0 = cpu_to_le32(((total_length - CMD_HDR_SZ) & - 0x0000ffff) | (pcmd_hdr->cmd_dw0 & - 0xffff0000)); - - /* urb length in cmd_dw1 */ - pcmd_hdr->cmd_dw1 = cpu_to_le32((pxmitbuf->aggr_nr & 0xff) | - ((total_length + TXDESC_SIZE) << 16)); - pxmitframe->last[0] = 1; - pxmitframe->bpending[0] = false; - pxmitframe->mem_addr = pxmitbuf->pbuf; - - if ((pdvobj->ishighspeed && ((total_length + TXDESC_SIZE) % 0x200) == 0) || - ((!pdvobj->ishighspeed && ((total_length + TXDESC_SIZE) % - 0x40) == 0))) { - ptxdesc->txdw0 |= cpu_to_le32 - (((TXDESC_SIZE + OFFSET_SZ + 8) << OFFSET_SHT) & - 0x00ff0000); - /*32 bytes for TX Desc + 8 bytes pending*/ - } else { - ptxdesc->txdw0 |= cpu_to_le32 - (((TXDESC_SIZE + OFFSET_SZ) << OFFSET_SHT) & - 0x00ff0000); - /*default = 32 bytes for TX Desc*/ - } - r8712_write_port(pxmitframe->padapter, RTL8712_DMA_H2CCMD, total_length + TXDESC_SIZE, - (u8 *)pxmitframe); -} - -#endif - -static void update_txdesc(struct xmit_frame *pxmitframe, uint *pmem, int sz) -{ - uint qsel; - struct _adapter *padapter = pxmitframe->padapter; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct qos_priv *pqospriv = &pmlmepriv->qospriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - struct tx_desc *ptxdesc = (struct tx_desc *)pmem; - struct dvobj_priv *pdvobj = &padapter->dvobjpriv; -#ifdef CONFIG_R8712_TX_AGGR - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; -#endif - u8 blnSetTxDescOffset; - bool bmcst = is_multicast_ether_addr(pattrib->ra); - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - struct tx_desc txdesc_mp; - - memcpy(&txdesc_mp, ptxdesc, sizeof(struct tx_desc)); - memset(ptxdesc, 0, sizeof(struct tx_desc)); - /* offset 0 */ - ptxdesc->txdw0 |= cpu_to_le32(sz & 0x0000ffff); - if (pdvobj->ishighspeed) { - if (((sz + TXDESC_SIZE) % 512) == 0) - blnSetTxDescOffset = 1; - else - blnSetTxDescOffset = 0; - } else { - if (((sz + TXDESC_SIZE) % 64) == 0) - blnSetTxDescOffset = 1; - else - blnSetTxDescOffset = 0; - } - if (blnSetTxDescOffset) { - /* 32 bytes for TX Desc + 8 bytes pending */ - ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + OFFSET_SZ + 8) << - OFFSET_SHT) & 0x00ff0000); - } else { - /* default = 32 bytes for TX Desc */ - ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + OFFSET_SZ) << - OFFSET_SHT) & 0x00ff0000); - } - ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); - if (pxmitframe->frame_tag == DATA_FRAMETAG) { - /* offset 4 */ - ptxdesc->txdw1 |= cpu_to_le32((pattrib->mac_id) & 0x1f); - -#ifdef CONFIG_R8712_TX_AGGR - /* dirty workaround, need to check if it is aggr cmd. */ - if ((u8 *)pmem != (u8 *)pxmitframe->pxmitbuf->pbuf) { - ptxdesc->txdw0 |= cpu_to_le32 - ((0x3 << TYPE_SHT) & TYPE_MSK); - qsel = (uint)(pattrib->qsel & 0x0000001f); - if (qsel == 2) - qsel = 0; - ptxdesc->txdw1 |= cpu_to_le32 - ((qsel << QSEL_SHT) & 0x00001f00); - ptxdesc->txdw2 = cpu_to_le32 - ((qsel << RTS_RC_SHT) & 0x001f0000); - ptxdesc->txdw6 |= cpu_to_le32 - ((0x5 << RSVD6_SHT) & RSVD6_MSK); - } else { - ptxdesc->txdw0 |= cpu_to_le32 - ((0x3 << TYPE_SHT) & TYPE_MSK); - ptxdesc->txdw1 |= cpu_to_le32 - ((0x13 << QSEL_SHT) & 0x00001f00); - qsel = (uint)(pattrib->qsel & 0x0000001f); - if (qsel == 2) - qsel = 0; - ptxdesc->txdw2 = cpu_to_le32 - ((qsel << RTS_RC_SHT) & 0x0001f000); - ptxdesc->txdw7 |= cpu_to_le32 - (pcmdpriv->cmd_seq << 24); - pcmdpriv->cmd_seq++; - } - pattrib->qsel = 0x13; -#else - qsel = (uint)(pattrib->qsel & 0x0000001f); - ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00); -#endif - if (!pqospriv->qos_option) - ptxdesc->txdw1 |= cpu_to_le32(BIT(16));/*Non-QoS*/ - if ((pattrib->encrypt > 0) && !pattrib->bswenc) { - switch (pattrib->encrypt) { /*SEC_TYPE*/ - case _WEP40_: - case _WEP104_: - ptxdesc->txdw1 |= cpu_to_le32((0x01 << 22) & - 0x00c00000); - /*KEY_ID when WEP is used;*/ - ptxdesc->txdw1 |= - cpu_to_le32((psecuritypriv->PrivacyKeyIndex << 17) & - 0x00060000); - break; - case _TKIP_: - case _TKIP_WTMIC_: - ptxdesc->txdw1 |= cpu_to_le32((0x02 << 22) & - 0x00c00000); - break; - case _AES_: - ptxdesc->txdw1 |= cpu_to_le32((0x03 << 22) & - 0x00c00000); - break; - case _NO_PRIVACY_: - default: - break; - } - } - /*offset 8*/ - if (bmcst) - ptxdesc->txdw2 |= cpu_to_le32(BMC); - - /*offset 12*/ - /* f/w will increase the seqnum by itself, driver pass the - * correct priority to fw. - * fw will check the correct priority for increasing the - * seqnum per tid. about usb using 4-endpoint, qsel points out - * the correct mapping between AC&Endpoint, - * the purpose is that correct mapping lets the MAC release - * the AC Queue list correctly. - */ - ptxdesc->txdw3 = cpu_to_le32((pattrib->priority << SEQ_SHT) & - 0x0fff0000); - if ((pattrib->ether_type != 0x888e) && - (pattrib->ether_type != 0x0806) && - (pattrib->dhcp_pkt != 1)) { - /*Not EAP & ARP type data packet*/ - if (phtpriv->ht_option == 1) { /*B/G/N Mode*/ - if (!phtpriv->ampdu_enable) - ptxdesc->txdw2 |= cpu_to_le32(BK); - } - } else { - /* EAP data packet and ARP packet. - * Use the 1M data rate to send the EAP/ARP packet. - * This will maybe make the handshake smooth. - */ - /*driver uses data rate*/ - ptxdesc->txdw4 = cpu_to_le32(0x80000000); - ptxdesc->txdw5 = cpu_to_le32(0x001f8000);/*1M*/ - } - if (pattrib->pctrl == 1) { /* mp tx packets */ - struct tx_desc *ptxdesc_mp; - - ptxdesc_mp = &txdesc_mp; - /* offset 8 */ - ptxdesc->txdw2 = ptxdesc_mp->txdw2; - if (bmcst) - ptxdesc->txdw2 |= cpu_to_le32(BMC); - ptxdesc->txdw2 |= cpu_to_le32(BK); - /* offset 16 */ - ptxdesc->txdw4 = ptxdesc_mp->txdw4; - /* offset 20 */ - ptxdesc->txdw5 = ptxdesc_mp->txdw5; - pattrib->pctrl = 0;/* reset to zero; */ - } - } else if (pxmitframe->frame_tag == MGNT_FRAMETAG) { - /* offset 4 */ - /* CAM_ID(MAC_ID), default=5; */ - ptxdesc->txdw1 |= cpu_to_le32((0x05) & 0x1f); - qsel = (uint)(pattrib->qsel & 0x0000001f); - ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00); - ptxdesc->txdw1 |= cpu_to_le32(BIT(16));/* Non-QoS */ - /* offset 8 */ - if (bmcst) - ptxdesc->txdw2 |= cpu_to_le32(BMC); - /* offset 12 */ - /* f/w will increase the seqnum by itself, driver pass the - * correct priority to fw. - * fw will check the correct priority for increasing the seqnum - * per tid. about usb using 4-endpoint, qsel points out the - * correct mapping between AC&Endpoint, - * the purpose is that correct mapping let the MAC releases - * the AC Queue list correctly. - */ - ptxdesc->txdw3 = cpu_to_le32((pattrib->priority << SEQ_SHT) & - 0x0fff0000); - /* offset 16 */ - ptxdesc->txdw4 = cpu_to_le32(0x80002040);/*gtest*/ - /* offset 20 */ - ptxdesc->txdw5 = cpu_to_le32(0x001f8000);/* gtest 1M */ - } else if (pxmitframe->frame_tag == TXAGG_FRAMETAG) { - /* offset 4 */ - qsel = 0x13; - ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00); - } else { - /* offset 4 */ - qsel = (uint)(pattrib->priority & 0x0000001f); - ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00); - /*offset 8*/ - /*offset 12*/ - ptxdesc->txdw3 = cpu_to_le32((pattrib->seqnum << SEQ_SHT) & - 0x0fff0000); - /*offset 16*/ - ptxdesc->txdw4 = cpu_to_le32(0x80002040);/*gtest*/ - /*offset 20*/ - ptxdesc->txdw5 = cpu_to_le32(0x001f9600);/*gtest*/ - } -} - -int r8712_xmitframe_complete(struct _adapter *padapter, - struct xmit_priv *pxmitpriv, - struct xmit_buf *pxmitbuf) -{ - struct hw_xmit *phwxmits; - sint hwentry; - struct xmit_frame *pxmitframe = NULL; -#ifdef CONFIG_R8712_TX_AGGR - struct xmit_frame *p2ndxmitframe = NULL; -#else - int res = _SUCCESS; -#endif - - phwxmits = pxmitpriv->hwxmits; - hwentry = pxmitpriv->hwxmit_entry; - if (!pxmitbuf) { - pxmitbuf = r8712_alloc_xmitbuf(pxmitpriv); - if (!pxmitbuf) - return false; -#ifdef CONFIG_R8712_TX_AGGR - pxmitbuf->aggr_nr = 0; -#endif - } - /* 1st frame dequeued */ - pxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry); - /* need to remember the 1st frame */ - if (pxmitframe) { -#ifdef CONFIG_R8712_TX_AGGR - /* 1. dequeue 2nd frame - * 2. aggr if 2nd xframe is dequeued, else dump directly - */ - if (AGGR_NR_HIGH_BOUND > 1) - p2ndxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry); - if (pxmitframe->frame_tag != DATA_FRAMETAG) { - r8712_free_xmitbuf(pxmitpriv, pxmitbuf); - return false; - } - if (p2ndxmitframe) - if (p2ndxmitframe->frame_tag != DATA_FRAMETAG) { - r8712_free_xmitbuf(pxmitpriv, pxmitbuf); - return false; - } - r8712_xmitframe_aggr_1st(pxmitbuf, pxmitframe); - if (p2ndxmitframe) { - u16 total_length; - - total_length = r8712_xmitframe_aggr_next(pxmitbuf, p2ndxmitframe); - do { - p2ndxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry); - if (p2ndxmitframe) - total_length = - r8712_xmitframe_aggr_next(pxmitbuf, p2ndxmitframe); - else - break; - } while (total_length <= 0x1800 && - pxmitbuf->aggr_nr <= AGGR_NR_HIGH_BOUND); - } - if (pxmitbuf->aggr_nr > 0) - r8712_dump_aggr_xframe(pxmitbuf, pxmitframe); - -#else - - xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf); - if (pxmitframe->frame_tag == DATA_FRAMETAG) { - if (pxmitframe->attrib.priority <= 15) - res = r8712_xmitframe_coalesce(padapter, pxmitframe->pkt, - pxmitframe); - /* always return ndis_packet after - * r8712_xmitframe_coalesce - */ - r8712_xmit_complete(padapter, pxmitframe); - } - if (res == _SUCCESS) - dump_xframe(padapter, pxmitframe); - else - r8712_free_xmitframe_ex(pxmitpriv, pxmitframe); -#endif - - } else { /* pxmitframe == NULL && p2ndxmitframe == NULL */ - r8712_free_xmitbuf(pxmitpriv, pxmitbuf); - return false; - } - return true; -} - -static void dump_xframe(struct _adapter *padapter, - struct xmit_frame *pxmitframe) -{ - int t, sz, w_sz; - u8 *mem_addr; - u32 ff_hwaddr; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - - if (pxmitframe->attrib.ether_type != 0x0806) { - if (pxmitframe->attrib.ether_type != 0x888e) - r8712_issue_addbareq_cmd(padapter, pattrib->priority); - } - mem_addr = pxmitframe->buf_addr; - for (t = 0; t < pattrib->nr_frags; t++) { - if (t != (pattrib->nr_frags - 1)) { - sz = pxmitpriv->frag_len; - sz = sz - 4 - (psecuritypriv->sw_encrypt ? 0 : - pattrib->icv_len); - pxmitframe->last[t] = 0; - } else { - sz = pattrib->last_txcmdsz; - pxmitframe->last[t] = 1; - } - update_txdesc(pxmitframe, (uint *)mem_addr, sz); - w_sz = sz + TXDESC_SIZE; - pxmitframe->mem_addr = mem_addr; - pxmitframe->bpending[t] = false; - ff_hwaddr = get_ff_hwaddr(pxmitframe); -#ifdef CONFIG_R8712_TX_AGGR - r8712_write_port(padapter, RTL8712_DMA_H2CCMD, w_sz, - (unsigned char *)pxmitframe); -#else - r8712_write_port(padapter, ff_hwaddr, w_sz, - (unsigned char *)pxmitframe); -#endif - mem_addr += w_sz; - mem_addr = (u8 *)RND4(((addr_t)(mem_addr))); - } -} - -void r8712_xmit_direct(struct _adapter *padapter, struct xmit_frame *pxmitframe) -{ - int res; - - res = r8712_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe); - pxmitframe->pkt = NULL; - if (res == _SUCCESS) - dump_xframe(padapter, pxmitframe); -} - -int r8712_xmit_enqueue(struct _adapter *padapter, struct xmit_frame *pxmitframe) -{ - if (r8712_xmit_classifier(padapter, pxmitframe)) { - pxmitframe->pkt = NULL; - return _FAIL; - } - return _SUCCESS; -} diff --git a/drivers/staging/rtl8712/rtl8712_xmit.h b/drivers/staging/rtl8712/rtl8712_xmit.h deleted file mode 100644 index 5cd651a0de75a..0000000000000 --- a/drivers/staging/rtl8712/rtl8712_xmit.h +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL8712_XMIT_H_ -#define _RTL8712_XMIT_H_ - -#define HWXMIT_ENTRY 4 - -#define VO_QUEUE_INX 0 -#define VI_QUEUE_INX 1 -#define BE_QUEUE_INX 2 -#define BK_QUEUE_INX 3 -#define TS_QUEUE_INX 4 -#define MGT_QUEUE_INX 5 -#define BMC_QUEUE_INX 6 -#define BCN_QUEUE_INX 7 - -#define HW_QUEUE_ENTRY 8 - -#define TXDESC_SIZE 32 -#define TXDESC_OFFSET TXDESC_SIZE - -#define NR_AMSDU_XMITFRAME 8 -#define NR_TXAGG_XMITFRAME 8 - -#define MAX_AMSDU_XMITBUF_SZ 8704 -#define MAX_TXAGG_XMITBUF_SZ 16384 /*16k*/ - -#define tx_cmd tx_desc - -/* - *defined for TX DESC Operation - */ - -#define MAX_TID (15) - -/*OFFSET 0*/ -#define OFFSET_SZ (0) -#define OFFSET_SHT (16) -#define OWN BIT(31) -#define FSG BIT(27) -#define LSG BIT(26) -#define TYPE_SHT (24) -#define TYPE_MSK (0x03000000) - -/*OFFSET 4*/ -#define PKT_OFFSET_SZ (0) -#define QSEL_SHT (8) -#define HWPC BIT(31) - -/*OFFSET 8*/ -#define BMC BIT(7) -#define BK BIT(30) -#define AGG_EN BIT(29) -#define RTS_RC_SHT (16) - -/*OFFSET 12*/ -#define SEQ_SHT (16) - -/*OFFSET 16*/ -#define TXBW BIT(18) - -/*OFFSET 20*/ -#define DISFB BIT(15) -#define RSVD6_MSK (0x00E00000) -#define RSVD6_SHT (21) - -struct tx_desc { - /*DWORD 0*/ - __le32 txdw0; - __le32 txdw1; - __le32 txdw2; - __le32 txdw3; - __le32 txdw4; - __le32 txdw5; - __le32 txdw6; - __le32 txdw7; -}; - -union txdesc { - struct tx_desc txdesc; - unsigned int value[TXDESC_SIZE >> 2]; -}; - -int r8712_xmitframe_complete(struct _adapter *padapter, - struct xmit_priv *pxmitpriv, - struct xmit_buf *pxmitbuf); -void r8712_do_queue_select(struct _adapter *padapter, - struct pkt_attrib *pattrib); - -#ifdef CONFIG_R8712_TX_AGGR -void r8712_xmitframe_aggr_1st(struct xmit_buf *pxmitbuf, - struct xmit_frame *pxmitframe); -void r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf, - struct xmit_frame *pxmitframe); -#endif - -#endif diff --git a/drivers/staging/rtl8712/rtl871x_cmd.c b/drivers/staging/rtl8712/rtl871x_cmd.c deleted file mode 100644 index ffeb91dd28c43..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_cmd.c +++ /dev/null @@ -1,750 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_cmd.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_CMD_C_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "mlme_osdep.h" - -/* - * Caller and the r8712_cmd_thread can protect cmd_q by spin_lock. - * No irqsave is necessary. - */ - -int r8712_init_cmd_priv(struct cmd_priv *pcmdpriv) -{ - init_completion(&pcmdpriv->cmd_queue_comp); - init_completion(&pcmdpriv->terminate_cmdthread_comp); - - _init_queue(&(pcmdpriv->cmd_queue)); - - /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */ - pcmdpriv->cmd_seq = 1; - pcmdpriv->cmd_allocated_buf = kmalloc(MAX_CMDSZ + CMDBUFF_ALIGN_SZ, - GFP_ATOMIC); - if (!pcmdpriv->cmd_allocated_buf) - return -ENOMEM; - pcmdpriv->cmd_buf = pcmdpriv->cmd_allocated_buf + CMDBUFF_ALIGN_SZ - - ((addr_t)(pcmdpriv->cmd_allocated_buf) & - (CMDBUFF_ALIGN_SZ - 1)); - pcmdpriv->rsp_allocated_buf = kmalloc(MAX_RSPSZ + 4, GFP_ATOMIC); - if (!pcmdpriv->rsp_allocated_buf) { - kfree(pcmdpriv->cmd_allocated_buf); - pcmdpriv->cmd_allocated_buf = NULL; - return -ENOMEM; - } - pcmdpriv->rsp_buf = pcmdpriv->rsp_allocated_buf + 4 - - ((addr_t)(pcmdpriv->rsp_allocated_buf) & 3); - pcmdpriv->cmd_issued_cnt = 0; - pcmdpriv->cmd_done_cnt = 0; - pcmdpriv->rsp_cnt = 0; - return 0; -} - -int r8712_init_evt_priv(struct evt_priv *pevtpriv) -{ - /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */ - pevtpriv->event_seq = 0; - pevtpriv->evt_allocated_buf = kmalloc(MAX_EVTSZ + 4, GFP_ATOMIC); - - if (!pevtpriv->evt_allocated_buf) - return -ENOMEM; - pevtpriv->evt_buf = pevtpriv->evt_allocated_buf + 4 - - ((addr_t)(pevtpriv->evt_allocated_buf) & 3); - pevtpriv->evt_done_cnt = 0; - return 0; -} - -void r8712_free_evt_priv(struct evt_priv *pevtpriv) -{ - kfree(pevtpriv->evt_allocated_buf); -} - -void r8712_free_cmd_priv(struct cmd_priv *pcmdpriv) -{ - if (pcmdpriv) { - kfree(pcmdpriv->cmd_allocated_buf); - kfree(pcmdpriv->rsp_allocated_buf); - } -} - -/* - * Calling Context: - * - * r8712_enqueue_cmd can only be called between kernel thread, - * since only spin_lock is used. - * - * ISR/Call-Back functions can't call this sub-function. - * - */ - -void r8712_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *obj) -{ - struct __queue *queue; - unsigned long irqL; - - if (pcmdpriv->padapter->eeprompriv.bautoload_fail_flag) - return; - if (!obj) - return; - queue = &pcmdpriv->cmd_queue; - spin_lock_irqsave(&queue->lock, irqL); - list_add_tail(&obj->list, &queue->queue); - spin_unlock_irqrestore(&queue->lock, irqL); - complete(&pcmdpriv->cmd_queue_comp); -} - -struct cmd_obj *r8712_dequeue_cmd(struct __queue *queue) -{ - unsigned long irqL; - struct cmd_obj *obj; - - spin_lock_irqsave(&queue->lock, irqL); - obj = list_first_entry_or_null(&queue->queue, - struct cmd_obj, list); - if (obj) - list_del_init(&obj->list); - spin_unlock_irqrestore(&queue->lock, irqL); - return obj; -} - -void r8712_enqueue_cmd_ex(struct cmd_priv *pcmdpriv, struct cmd_obj *obj) -{ - unsigned long irqL; - struct __queue *queue; - - if (!obj) - return; - if (pcmdpriv->padapter->eeprompriv.bautoload_fail_flag) - return; - queue = &pcmdpriv->cmd_queue; - spin_lock_irqsave(&queue->lock, irqL); - list_add_tail(&obj->list, &queue->queue); - spin_unlock_irqrestore(&queue->lock, irqL); - complete(&pcmdpriv->cmd_queue_comp); -} - -void r8712_free_cmd_obj(struct cmd_obj *pcmd) -{ - if ((pcmd->cmdcode != _JoinBss_CMD_) && - (pcmd->cmdcode != _CreateBss_CMD_)) - kfree(pcmd->parmbuf); - if (pcmd->rsp) { - if (pcmd->rspsz != 0) - kfree(pcmd->rsp); - } - kfree(pcmd); -} - -u8 r8712_sitesurvey_cmd(struct _adapter *padapter, - struct ndis_802_11_ssid *pssid) - __must_hold(&padapter->mlmepriv.lock) -{ - struct cmd_obj *ph2c; - struct sitesurvey_parm *psurveyPara; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return _FAIL; - psurveyPara = kmalloc(sizeof(*psurveyPara), GFP_ATOMIC); - if (!psurveyPara) { - kfree(ph2c); - return _FAIL; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, - GEN_CMD_CODE(_SiteSurvey)); - psurveyPara->bsslimit = cpu_to_le32(48); - psurveyPara->passive_mode = cpu_to_le32(pmlmepriv->passive_mode); - psurveyPara->ss_ssidlen = 0; - memset(psurveyPara->ss_ssid, 0, IW_ESSID_MAX_SIZE + 1); - if (pssid && pssid->SsidLength) { - int len = min_t(int, pssid->SsidLength, IW_ESSID_MAX_SIZE); - - memcpy(psurveyPara->ss_ssid, pssid->Ssid, len); - psurveyPara->ss_ssidlen = cpu_to_le32(len); - } - set_fwstate(pmlmepriv, _FW_UNDER_SURVEY); - r8712_enqueue_cmd(pcmdpriv, ph2c); - mod_timer(&pmlmepriv->scan_to_timer, - jiffies + msecs_to_jiffies(SCANNING_TIMEOUT)); - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_SITE_SURVEY); - complete(&padapter->rx_filter_ready); - return _SUCCESS; -} - -int r8712_setdatarate_cmd(struct _adapter *padapter, u8 *rateset) -{ - struct cmd_obj *ph2c; - struct setdatarate_parm *pbsetdataratepara; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return -ENOMEM; - pbsetdataratepara = kmalloc(sizeof(*pbsetdataratepara), GFP_ATOMIC); - if (!pbsetdataratepara) { - kfree(ph2c); - return -ENOMEM; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, pbsetdataratepara, - GEN_CMD_CODE(_SetDataRate)); - pbsetdataratepara->mac_id = 5; - memcpy(pbsetdataratepara->datarates, rateset, NumRates); - r8712_enqueue_cmd(pcmdpriv, ph2c); - return 0; -} - -void r8712_set_chplan_cmd(struct _adapter *padapter, int chplan) -{ - struct cmd_obj *ph2c; - struct SetChannelPlan_param *psetchplanpara; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - psetchplanpara = kmalloc(sizeof(*psetchplanpara), GFP_ATOMIC); - if (!psetchplanpara) { - kfree(ph2c); - return; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetchplanpara, GEN_CMD_CODE(_SetChannelPlan)); - psetchplanpara->ChannelPlan = chplan; - r8712_enqueue_cmd(pcmdpriv, ph2c); -} - -int r8712_setrfreg_cmd(struct _adapter *padapter, u8 offset, u32 val) -{ - struct cmd_obj *ph2c; - struct writeRF_parm *pwriterfparm; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return -ENOMEM; - pwriterfparm = kmalloc(sizeof(*pwriterfparm), GFP_ATOMIC); - if (!pwriterfparm) { - kfree(ph2c); - return -ENOMEM; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, pwriterfparm, GEN_CMD_CODE(_SetRFReg)); - pwriterfparm->offset = offset; - pwriterfparm->value = val; - r8712_enqueue_cmd(pcmdpriv, ph2c); - return 0; -} - -int r8712_getrfreg_cmd(struct _adapter *padapter, u8 offset, u8 *pval) -{ - struct cmd_obj *ph2c; - struct readRF_parm *prdrfparm; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return -ENOMEM; - prdrfparm = kmalloc(sizeof(*prdrfparm), GFP_ATOMIC); - if (!prdrfparm) { - kfree(ph2c); - return -ENOMEM; - } - INIT_LIST_HEAD(&ph2c->list); - ph2c->cmdcode = GEN_CMD_CODE(_GetRFReg); - ph2c->parmbuf = (unsigned char *)prdrfparm; - ph2c->cmdsz = sizeof(struct readRF_parm); - ph2c->rsp = pval; - ph2c->rspsz = sizeof(struct readRF_rsp); - prdrfparm->offset = offset; - r8712_enqueue_cmd(pcmdpriv, ph2c); - return 0; -} - -void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd) -{ - kfree(pcmd->parmbuf); - kfree(pcmd); - padapter->mppriv.workparam.bcompleted = true; -} - -void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd) -{ - kfree(pcmd->parmbuf); - kfree(pcmd); - - padapter->mppriv.workparam.bcompleted = true; -} - -int r8712_createbss_cmd(struct _adapter *padapter) -{ - struct cmd_obj *pcmd; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct wlan_bssid_ex *pdev_network = - &padapter->registrypriv.dev_network; - - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_START_TO_LINK); - pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC); - if (!pcmd) - return -ENOMEM; - INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = _CreateBss_CMD_; - pcmd->parmbuf = (unsigned char *)pdev_network; - pcmd->cmdsz = r8712_get_wlan_bssid_ex_sz(pdev_network); - pcmd->rsp = NULL; - pcmd->rspsz = 0; - /* notes: translate IELength & Length after assign to cmdsz; */ - pdev_network->Length = pcmd->cmdsz; - pdev_network->IELength = pdev_network->IELength; - pdev_network->Ssid.SsidLength = pdev_network->Ssid.SsidLength; - r8712_enqueue_cmd(pcmdpriv, pcmd); - return 0; -} - -int r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork) -{ - struct wlan_bssid_ex *psecnetwork; - struct cmd_obj *pcmd; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct qos_priv *pqospriv = &pmlmepriv->qospriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct registry_priv *pregistrypriv = &padapter->registrypriv; - enum NDIS_802_11_NETWORK_INFRASTRUCTURE ndis_network_mode = - pnetwork->network.InfrastructureMode; - - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_START_TO_LINK); - pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC); - if (!pcmd) - return -ENOMEM; - - /* for hidden ap to set fw_state here */ - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) != - true) { - switch (ndis_network_mode) { - case Ndis802_11IBSS: - pmlmepriv->fw_state |= WIFI_ADHOC_STATE; - break; - case Ndis802_11Infrastructure: - pmlmepriv->fw_state |= WIFI_STATION_STATE; - break; - case Ndis802_11APMode: - case Ndis802_11AutoUnknown: - case Ndis802_11InfrastructureMax: - break; - } - } - psecnetwork = &psecuritypriv->sec_bss; - memcpy(psecnetwork, &pnetwork->network, sizeof(*psecnetwork)); - psecuritypriv->authenticator_ie[0] = (unsigned char) - psecnetwork->IELength; - if ((psecnetwork->IELength - 12) < (256 - 1)) - memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], - psecnetwork->IELength - 12); - else - memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], (256 - 1)); - psecnetwork->IELength = 0; - /* - * If the driver wants to use the bssid to create the connection. - * If not, we copy the connecting AP's MAC address to it so that - * the driver just has the bssid information for PMKIDList searching. - */ - if (!pmlmepriv->assoc_by_bssid) - ether_addr_copy(&pmlmepriv->assoc_bssid[0], - &pnetwork->network.MacAddress[0]); - psecnetwork->IELength = r8712_restruct_sec_ie(padapter, &pnetwork->network.IEs[0], - &psecnetwork->IEs[0], pnetwork->network.IELength); - pqospriv->qos_option = 0; - if (pregistrypriv->wmm_enable) { - u32 tmp_len; - - tmp_len = r8712_restruct_wmm_ie(padapter, &pnetwork->network.IEs[0], - &psecnetwork->IEs[0], pnetwork->network.IELength, - psecnetwork->IELength); - if (psecnetwork->IELength != tmp_len) { - psecnetwork->IELength = tmp_len; - pqospriv->qos_option = 1; /* WMM IE in beacon */ - } else { - pqospriv->qos_option = 0; /* no WMM IE in beacon */ - } - } - if (pregistrypriv->ht_enable) { - /* - * For WEP mode, we will use the bg mode to do the connection - * to avoid some IOT issues, especially for Realtek 8192u - * SoftAP. - */ - if ((padapter->securitypriv.PrivacyAlgrthm != _WEP40_) && - (padapter->securitypriv.PrivacyAlgrthm != _WEP104_)) { - /* restructure_ht_ie */ - r8712_restructure_ht_ie(padapter, - &pnetwork->network.IEs[0], - &psecnetwork->IEs[0], - pnetwork->network.IELength, - &psecnetwork->IELength); - } - } - psecuritypriv->supplicant_ie[0] = (u8)psecnetwork->IELength; - if (psecnetwork->IELength < 255) - memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0], - psecnetwork->IELength); - else - memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0], - 255); - /* get cmdsz before endian conversion */ - pcmd->cmdsz = r8712_get_wlan_bssid_ex_sz(psecnetwork); -#ifdef __BIG_ENDIAN - /* wlan_network endian conversion */ - psecnetwork->Length = cpu_to_le32(psecnetwork->Length); - psecnetwork->Ssid.SsidLength = cpu_to_le32(psecnetwork->Ssid.SsidLength); - psecnetwork->Privacy = cpu_to_le32(psecnetwork->Privacy); - psecnetwork->Rssi = cpu_to_le32(psecnetwork->Rssi); - psecnetwork->NetworkTypeInUse = cpu_to_le32(psecnetwork->NetworkTypeInUse); - psecnetwork->Configuration.ATIMWindow = cpu_to_le32(psecnetwork->Configuration.ATIMWindow); - psecnetwork->Configuration.BeaconPeriod = cpu_to_le32(psecnetwork->Configuration.BeaconPeriod); - psecnetwork->Configuration.DSConfig = cpu_to_le32(psecnetwork->Configuration.DSConfig); - psecnetwork->Configuration.FHConfig.DwellTime = cpu_to_le32(psecnetwork->Configuration.FHConfig.DwellTime); - psecnetwork->Configuration.FHConfig.HopPattern = cpu_to_le32(psecnetwork->Configuration.FHConfig.HopPattern); - psecnetwork->Configuration.FHConfig.HopSet = cpu_to_le32(psecnetwork->Configuration.FHConfig.HopSet); - psecnetwork->Configuration.FHConfig.Length = cpu_to_le32(psecnetwork->Configuration.FHConfig.Length); - psecnetwork->Configuration.Length = cpu_to_le32(psecnetwork->Configuration.Length); - psecnetwork->InfrastructureMode = cpu_to_le32(psecnetwork->InfrastructureMode); - psecnetwork->IELength = cpu_to_le32(psecnetwork->IELength); -#endif - INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = _JoinBss_CMD_; - pcmd->parmbuf = (unsigned char *)psecnetwork; - pcmd->rsp = NULL; - pcmd->rspsz = 0; - r8712_enqueue_cmd(pcmdpriv, pcmd); - return 0; -} - -void r8712_disassoc_cmd(struct _adapter *padapter) /* for sta_mode */ -{ - struct cmd_obj *pdisconnect_cmd; - struct disconnect_parm *pdisconnect; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - pdisconnect_cmd = kmalloc(sizeof(*pdisconnect_cmd), GFP_ATOMIC); - if (!pdisconnect_cmd) - return; - pdisconnect = kmalloc(sizeof(*pdisconnect), GFP_ATOMIC); - if (!pdisconnect) { - kfree(pdisconnect_cmd); - return; - } - init_h2fwcmd_w_parm_no_rsp(pdisconnect_cmd, pdisconnect, _DisConnect_CMD_); - r8712_enqueue_cmd(pcmdpriv, pdisconnect_cmd); -} - -void r8712_setopmode_cmd(struct _adapter *padapter, - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype) -{ - struct cmd_obj *ph2c; - struct setopmode_parm *psetop; - - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - psetop = kmalloc(sizeof(*psetop), GFP_ATOMIC); - if (!psetop) { - kfree(ph2c); - return; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_); - psetop->mode = (u8)networktype; - r8712_enqueue_cmd(pcmdpriv, ph2c); -} - -void r8712_setstakey_cmd(struct _adapter *padapter, u8 *psta, u8 unicast_key) -{ - struct cmd_obj *ph2c; - struct set_stakey_parm *psetstakey_para; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct set_stakey_rsp *psetstakey_rsp = NULL; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct sta_info *sta = (struct sta_info *)psta; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - psetstakey_para = kmalloc(sizeof(*psetstakey_para), GFP_ATOMIC); - if (!psetstakey_para) { - kfree(ph2c); - return; - } - psetstakey_rsp = kmalloc(sizeof(*psetstakey_rsp), GFP_ATOMIC); - if (!psetstakey_rsp) { - kfree(ph2c); - kfree(psetstakey_para); - return; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_); - ph2c->rsp = (u8 *)psetstakey_rsp; - ph2c->rspsz = sizeof(struct set_stakey_rsp); - ether_addr_copy(psetstakey_para->addr, sta->hwaddr); - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) - psetstakey_para->algorithm = (unsigned char) - psecuritypriv->PrivacyAlgrthm; - else - GET_ENCRY_ALGO(psecuritypriv, sta, psetstakey_para->algorithm, false); - if (unicast_key) - memcpy(&psetstakey_para->key, &sta->x_UncstKey, 16); - else - memcpy(&psetstakey_para->key, - &psecuritypriv->XGrpKey[psecuritypriv->XGrpKeyid - 1].skey, - 16); - r8712_enqueue_cmd(pcmdpriv, ph2c); -} - -void r8712_setMacAddr_cmd(struct _adapter *padapter, const u8 *mac_addr) -{ - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct cmd_obj *ph2c; - struct SetMacAddr_param *psetMacAddr_para; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - psetMacAddr_para = kmalloc(sizeof(*psetMacAddr_para), GFP_ATOMIC); - if (!psetMacAddr_para) { - kfree(ph2c); - return; - } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetMacAddr_para, _SetMacAddress_CMD_); - ether_addr_copy(psetMacAddr_para->MacAddr, mac_addr); - r8712_enqueue_cmd(pcmdpriv, ph2c); -} - -void r8712_addbareq_cmd(struct _adapter *padapter, u8 tid) -{ - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - struct cmd_obj *ph2c; - struct addBaReq_parm *paddbareq_parm; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - paddbareq_parm = kmalloc(sizeof(*paddbareq_parm), GFP_ATOMIC); - if (!paddbareq_parm) { - kfree(ph2c); - return; - } - paddbareq_parm->tid = tid; - init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, GEN_CMD_CODE(_AddBAReq)); - r8712_enqueue_cmd_ex(pcmdpriv, ph2c); -} - -void r8712_wdg_wk_cmd(struct _adapter *padapter) -{ - struct cmd_obj *ph2c; - struct drvint_cmd_parm *pdrvintcmd_param; - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - pdrvintcmd_param = kmalloc(sizeof(*pdrvintcmd_param), GFP_ATOMIC); - if (!pdrvintcmd_param) { - kfree(ph2c); - return; - } - pdrvintcmd_param->i_cid = WDG_WK_CID; - pdrvintcmd_param->sz = 0; - pdrvintcmd_param->pbuf = NULL; - init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvintcmd_param, _DRV_INT_CMD_); - r8712_enqueue_cmd_ex(pcmdpriv, ph2c); -} - -void r8712_survey_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd) -{ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (pcmd->res != H2C_SUCCESS) - clr_fwstate(pmlmepriv, _FW_UNDER_SURVEY); - r8712_free_cmd_obj(pcmd); -} - -void r8712_disassoc_cmd_callback(struct _adapter *padapter, - struct cmd_obj *pcmd) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (pcmd->res != H2C_SUCCESS) { - spin_lock_irqsave(&pmlmepriv->lock, irqL); - set_fwstate(pmlmepriv, _FW_LINKED); - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - return; - } - r8712_free_cmd_obj(pcmd); -} - -void r8712_joinbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd) -{ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (pcmd->res != H2C_SUCCESS) - mod_timer(&pmlmepriv->assoc_timer, jiffies + msecs_to_jiffies(1)); - r8712_free_cmd_obj(pcmd); -} - -void r8712_createbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd) -{ - unsigned long irqL; - struct sta_info *psta = NULL; - struct wlan_network *pwlan = NULL; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)pcmd->parmbuf; - struct wlan_network *tgt_network = &(pmlmepriv->cur_network); - - if (pcmd->res != H2C_SUCCESS) - mod_timer(&pmlmepriv->assoc_timer, jiffies + msecs_to_jiffies(1)); - del_timer(&pmlmepriv->assoc_timer); -#ifdef __BIG_ENDIAN - /* endian_convert */ - pnetwork->Length = le32_to_cpu(pnetwork->Length); - pnetwork->Ssid.SsidLength = le32_to_cpu(pnetwork->Ssid.SsidLength); - pnetwork->Privacy = le32_to_cpu(pnetwork->Privacy); - pnetwork->Rssi = le32_to_cpu(pnetwork->Rssi); - pnetwork->NetworkTypeInUse = le32_to_cpu(pnetwork->NetworkTypeInUse); - pnetwork->Configuration.ATIMWindow = le32_to_cpu(pnetwork->Configuration.ATIMWindow); - pnetwork->Configuration.DSConfig = le32_to_cpu(pnetwork->Configuration.DSConfig); - pnetwork->Configuration.FHConfig.DwellTime = le32_to_cpu(pnetwork->Configuration.FHConfig.DwellTime); - pnetwork->Configuration.FHConfig.HopPattern = le32_to_cpu(pnetwork->Configuration.FHConfig.HopPattern); - pnetwork->Configuration.FHConfig.HopSet = le32_to_cpu(pnetwork->Configuration.FHConfig.HopSet); - pnetwork->Configuration.FHConfig.Length = le32_to_cpu(pnetwork->Configuration.FHConfig.Length); - pnetwork->Configuration.Length = le32_to_cpu(pnetwork->Configuration.Length); - pnetwork->InfrastructureMode = le32_to_cpu(pnetwork->InfrastructureMode); - pnetwork->IELength = le32_to_cpu(pnetwork->IELength); -#endif - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if ((pmlmepriv->fw_state) & WIFI_AP_STATE) { - psta = r8712_get_stainfo(&padapter->stapriv, pnetwork->MacAddress); - if (!psta) { - psta = r8712_alloc_stainfo(&padapter->stapriv, pnetwork->MacAddress); - if (!psta) - goto createbss_cmd_fail; - } - r8712_indicate_connect(padapter); - } else { - pwlan = _r8712_alloc_network(pmlmepriv); - if (!pwlan) { - pwlan = r8712_get_oldest_wlan_network(&pmlmepriv->scanned_queue); - if (!pwlan) - goto createbss_cmd_fail; - pwlan->last_scanned = jiffies; - } else { - list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); - } - pnetwork->Length = r8712_get_wlan_bssid_ex_sz(pnetwork); - memcpy(&(pwlan->network), pnetwork, pnetwork->Length); - pwlan->fixed = true; - memcpy(&tgt_network->network, pnetwork, (r8712_get_wlan_bssid_ex_sz(pnetwork))); - if (pmlmepriv->fw_state & _FW_UNDER_LINKING) - pmlmepriv->fw_state ^= _FW_UNDER_LINKING; - /* - * we will set _FW_LINKED when there is one more sat to - * join us (stassoc_event_callback) - */ - } -createbss_cmd_fail: - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - r8712_free_cmd_obj(pcmd); -} - -void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd) -{ - struct sta_priv *pstapriv = &padapter->stapriv; - struct set_stakey_rsp *psetstakey_rsp = (struct set_stakey_rsp *) (pcmd->rsp); - struct sta_info *psta = r8712_get_stainfo(pstapriv, psetstakey_rsp->addr); - - if (!psta) - goto exit; - psta->aid = psta->mac_id = psetstakey_rsp->keyid; /*CAM_ID(CAM_ENTRY)*/ -exit: - r8712_free_cmd_obj(pcmd); -} - -void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd) -{ - unsigned long irqL; - struct sta_priv *pstapriv = &padapter->stapriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct set_assocsta_parm *passocsta_parm = (struct set_assocsta_parm *)(pcmd->parmbuf); - struct set_assocsta_rsp *passocsta_rsp = (struct set_assocsta_rsp *) (pcmd->rsp); - struct sta_info *psta = r8712_get_stainfo(pstapriv, passocsta_parm->addr); - - if (!psta) - return; - psta->aid = psta->mac_id = passocsta_rsp->cam_id; - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if ((check_fwstate(pmlmepriv, WIFI_MP_STATE)) && (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) - pmlmepriv->fw_state ^= _FW_UNDER_LINKING; - set_fwstate(pmlmepriv, _FW_LINKED); - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - r8712_free_cmd_obj(pcmd); -} - -void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, u32 tryPktCnt, - u32 tryPktInterval, u32 firstStageTO) -{ - struct cmd_obj *ph2c; - struct DisconnectCtrlEx_param *param; - struct cmd_priv *pcmdpriv = &adapter->cmdpriv; - - ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC); - if (!ph2c) - return; - param = kzalloc(sizeof(*param), GFP_ATOMIC); - if (!param) { - kfree(ph2c); - return; - } - - param->EnableDrvCtrl = (unsigned char)enableDrvCtrl; - param->TryPktCnt = (unsigned char)tryPktCnt; - param->TryPktInterval = (unsigned char)tryPktInterval; - param->FirstStageTO = (unsigned int)firstStageTO; - - init_h2fwcmd_w_parm_no_rsp(ph2c, param, GEN_CMD_CODE(_DisconnectCtrlEx)); - r8712_enqueue_cmd(pcmdpriv, ph2c); -} diff --git a/drivers/staging/rtl8712/rtl871x_cmd.h b/drivers/staging/rtl8712/rtl871x_cmd.h deleted file mode 100644 index 268844af57f00..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_cmd.h +++ /dev/null @@ -1,750 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_CMD_H_ -#define __RTL871X_CMD_H_ - -#include "wlan_bssdef.h" -#include "rtl871x_rf.h" -#define C2H_MEM_SZ (16*1024) - -#include "osdep_service.h" -#include "ieee80211.h" - -#define FREE_CMDOBJ_SZ 128 -#define MAX_CMDSZ 512 -#define MAX_RSPSZ 512 -#define MAX_EVTSZ 1024 -#define CMDBUFF_ALIGN_SZ 512 - -struct cmd_obj { - u16 cmdcode; - u8 res; - u8 *parmbuf; - u32 cmdsz; - u8 *rsp; - u32 rspsz; - struct list_head list; -}; - -struct cmd_priv { - struct completion cmd_queue_comp; - struct completion terminate_cmdthread_comp; - struct __queue cmd_queue; - u8 cmd_seq; - u8 *cmd_buf; /*shall be non-paged, and 4 bytes aligned*/ - u8 *cmd_allocated_buf; - u8 *rsp_buf; /*shall be non-paged, and 4 bytes aligned*/ - u8 *rsp_allocated_buf; - u32 cmd_issued_cnt; - u32 cmd_done_cnt; - u32 rsp_cnt; - struct _adapter *padapter; -}; - -struct evt_obj { - u16 evtcode; - u8 res; - u8 *parmbuf; - u32 evtsz; - struct list_head list; -}; - -struct evt_priv { - struct __queue evt_queue; - u8 event_seq; - u8 *evt_buf; /*shall be non-paged, and 4 bytes aligned*/ - u8 *evt_allocated_buf; - u32 evt_done_cnt; -}; - -#define init_h2fwcmd_w_parm_no_rsp(pcmd, pparm, code) \ -do {\ - INIT_LIST_HEAD(&pcmd->list);\ - pcmd->cmdcode = code;\ - pcmd->parmbuf = (u8 *)(pparm);\ - pcmd->cmdsz = sizeof(*pparm);\ - pcmd->rsp = NULL;\ - pcmd->rspsz = 0;\ -} while (0) - -void r8712_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *obj); -void r8712_enqueue_cmd_ex(struct cmd_priv *pcmdpriv, struct cmd_obj *obj); -struct cmd_obj *r8712_dequeue_cmd(struct __queue *queue); -void r8712_free_cmd_obj(struct cmd_obj *pcmd); -int r8712_cmd_thread(void *context); -int r8712_init_cmd_priv(struct cmd_priv *pcmdpriv); -void r8712_free_cmd_priv(struct cmd_priv *pcmdpriv); -int r8712_init_evt_priv(struct evt_priv *pevtpriv); -void r8712_free_evt_priv(struct evt_priv *pevtpriv); - -enum rtl871x_drvint_cid { - NONE_WK_CID, - WDG_WK_CID, - MAX_WK_CID -}; - -enum RFINTFS { - SWSI, - HWSI, - HWPI, -}; - -/* - * Caller Mode: Infra, Ad-HoC(C) - * Notes: To enter USB suspend mode - * Command Mode - */ -struct usb_suspend_parm { - u32 action; /* 1: sleep, 0:resume */ -}; - -/* - * Caller Mode: Infra, Ad-HoC(C) - * Notes: To disconnect the current associated BSS - * Command Mode - */ -struct disconnect_parm { - u32 rsvd; -}; - -/* - * Caller Mode: AP, Ad-HoC, Infra - * Notes: To set the NIC mode of RTL8711 - * Command Mode - * The definition of mode: - * - * #define IW_MODE_AUTO 0 // Let the driver decides which AP to join - * #define IW_MODE_ADHOC 1 // Single cell network (Ad-Hoc Clients) - * #define IW_MODE_INFRA 2 // Multi cell network, roaming, .. - * #define IW_MODE_MASTER 3 // Synchronisation master or AP - * #define IW_MODE_REPEAT 4 // Wireless Repeater (forwarder) - * #define IW_MODE_SECOND 5 // Secondary master/repeater (backup) - * #define IW_MODE_MONITOR 6 // Passive monitor (listen only) - */ -struct setopmode_parm { - u8 mode; - u8 rsvd[3]; -}; - -/* - * Caller Mode: AP, Ad-HoC, Infra - * Notes: To ask RTL8711 performing site-survey - * Command-Event Mode - */ -struct sitesurvey_parm { - __le32 passive_mode; /*active: 1, passive: 0 */ - __le32 bsslimit; /* 1 ~ 48 */ - __le32 ss_ssidlen; - u8 ss_ssid[IW_ESSID_MAX_SIZE + 1]; -}; - -/* - * Caller Mode: Any - * Notes: To set the auth type of RTL8711. open/shared/802.1x - * Command Mode - */ -struct setauth_parm { - u8 mode; /*0: legacy open, 1: legacy shared 2: 802.1x*/ - u8 _1x; /*0: PSK, 1: TLS*/ - u8 rsvd[2]; -}; - -/* - * Caller Mode: Infra - * a. algorithm: wep40, wep104, tkip & aes - * b. keytype: grp key/unicast key - * c. key contents - * - * when shared key ==> keyid is the camid - * when 802.1x ==> keyid [0:1] ==> grp key - * when 802.1x ==> keyid > 2 ==> unicast key - */ -struct setkey_parm { - u8 algorithm; /* encryption algorithm, could be none, wep40, - * TKIP, CCMP, wep104 - */ - u8 keyid; - u8 grpkey; /* 1: this is the grpkey for 802.1x. - * 0: this is the unicast key for 802.1x - */ - u8 key[16]; /* this could be 40 or 104 */ -}; - -/* - * When in AP or Ad-Hoc mode, this is used to - * allocate an sw/hw entry for a newly associated sta. - * Command - * when shared key ==> algorithm/keyid - */ -struct set_stakey_parm { - u8 addr[ETH_ALEN]; - u8 algorithm; - u8 key[16]; -}; - -struct set_stakey_rsp { - u8 addr[ETH_ALEN]; - u8 keyid; - u8 rsvd; -}; - -struct SetMacAddr_param { - u8 MacAddr[ETH_ALEN]; -}; - -/* - * Caller Ad-Hoc/AP - * - * Command -Rsp(AID == CAMID) mode - * - * This is to force fw to add an sta_data entry per driver's request. - * - * FW will write an cam entry associated with it. - * - */ -struct set_assocsta_parm { - u8 addr[ETH_ALEN]; -}; - -struct set_assocsta_rsp { - u8 cam_id; - u8 rsvd[3]; -}; - -/* - * Caller Ad-Hoc/AP - * - * Command mode - * - * This is to force fw to del an sta_data entry per driver's request - * - * FW will invalidate the cam entry associated with it. - * - */ -struct del_assocsta_parm { - u8 addr[ETH_ALEN]; -}; - -/* - * Caller Mode: AP/Ad-HoC(M) - * - * Notes: To notify fw that given staid has changed its power state - * - * Command Mode - * - */ -struct setstapwrstate_parm { - u8 staid; - u8 status; - u8 hwaddr[6]; -}; - -/* - * Caller Mode: Any - * - * Notes: To setup the basic rate of RTL8711 - * - * Command Mode - * - */ -struct setbasicrate_parm { - u8 basicrates[NumRates]; -}; - -/* - * Caller Mode: Any - * - * Notes: To read the current basic rate - * - * Command-Rsp Mode - * - */ -struct getbasicrate_parm { - u32 rsvd; -}; - -struct getbasicrate_rsp { - u8 basicrates[NumRates]; -}; - -/* - * Caller Mode: Any - * - * Notes: To setup the data rate of RTL8711 - * - * Command Mode - * - */ -struct setdatarate_parm { - u8 mac_id; - u8 datarates[NumRates]; -}; - -enum _RT_CHANNEL_DOMAIN { - RT_CHANNEL_DOMAIN_FCC = 0, - RT_CHANNEL_DOMAIN_IC = 1, - RT_CHANNEL_DOMAIN_ETSI = 2, - RT_CHANNEL_DOMAIN_SPAIN = 3, - RT_CHANNEL_DOMAIN_FRANCE = 4, - RT_CHANNEL_DOMAIN_MKK = 5, - RT_CHANNEL_DOMAIN_MKK1 = 6, - RT_CHANNEL_DOMAIN_ISRAEL = 7, - RT_CHANNEL_DOMAIN_TELEC = 8, - - /* Be compatible with old channel plan. No good! */ - RT_CHANNEL_DOMAIN_MIC = 9, - RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN = 10, - RT_CHANNEL_DOMAIN_WORLD_WIDE_13 = 11, - RT_CHANNEL_DOMAIN_TELEC_NETGEAR = 12, - - RT_CHANNEL_DOMAIN_NCC = 13, - RT_CHANNEL_DOMAIN_5G = 14, - RT_CHANNEL_DOMAIN_5G_40M = 15, - /*===== Add new channel plan above this line===============*/ - RT_CHANNEL_DOMAIN_MAX, -}; - -struct SetChannelPlan_param { - enum _RT_CHANNEL_DOMAIN ChannelPlan; -}; - -/* - * Caller Mode: Any - * - * Notes: To read the current data rate - * - * Command-Rsp Mode - * - */ -struct getdatarate_parm { - u32 rsvd; - -}; - -struct getdatarate_rsp { - u8 datarates[NumRates]; -}; - -/* - * Caller Mode: Any - * AP: AP can use the info for the contents of beacon frame - * Infra: STA can use the info when sitesurveying - * Ad-HoC(M): Like AP - * Ad-HoC(C): Like STA - * - * - * Notes: To set the phy capability of the NIC - * - * Command Mode - * - */ - -/* - * Caller Mode: Any - * - * Notes: To set the channel/modem/band - * This command will be used when channel/modem/band is changed. - * - * Command Mode - * - */ -/* - * Caller Mode: Any - * - * Notes: To get the current setting of channel/modem/band - * - * Command-Rsp Mode - * - */ -struct getphy_rsp { - u8 rfchannel; - u8 modem; -}; - -struct readBB_parm { - u8 offset; -}; - -struct readBB_rsp { - u8 value; -}; - -struct readTSSI_parm { - u8 offset; -}; - -struct readTSSI_rsp { - u8 value; -}; - -struct writeBB_parm { - u8 offset; - u8 value; -}; - -struct writePTM_parm { - u8 type; -}; - -struct readRF_parm { - u8 offset; -}; - -struct readRF_rsp { - u32 value; -}; - -struct writeRF_parm { - u32 offset; - u32 value; -}; - -struct setrfintfs_parm { - u8 rfintfs; -}; - -struct getrfintfs_parm { - u8 rfintfs; -}; - -/* - * Notes: This command is used for H2C/C2H loopback testing - * - * mac[0] == 0 - * ==> CMD mode, return H2C_SUCCESS. - * The following condition must be true under CMD mode - * mac[1] == mac[4], mac[2] == mac[3], mac[0]=mac[5]= 0; - * s0 == 0x1234, s1 == 0xabcd, w0 == 0x78563412, w1 == 0x5aa5def7; - * s2 == (b1 << 8 | b0); - * - * mac[0] == 1 - * ==> CMD_RSP mode, return H2C_SUCCESS_RSP - * - * The rsp layout shall be: - * rsp: parm: - * mac[0] = mac[5]; - * mac[1] = mac[4]; - * mac[2] = mac[3]; - * mac[3] = mac[2]; - * mac[4] = mac[1]; - * mac[5] = mac[0]; - * s0 = s1; - * s1 = swap16(s0); - * w0 = swap32(w1); - * b0 = b1 - * s2 = s0 + s1 - * b1 = b0 - * w1 = w0 - * - * mac[0] == 2 - * ==> CMD_EVENT mode, return H2C_SUCCESS - * The event layout shall be: - * event: parm: - * mac[0] = mac[5]; - * mac[1] = mac[4]; - * mac[2] = event's sequence number, starting from 1 to parm's marc[3] - * mac[3] = mac[2]; - * mac[4] = mac[1]; - * mac[5] = mac[0]; - * s0 = swap16(s0) - event.mac[2]; - * s1 = s1 + event.mac[2]; - * w0 = swap32(w0); - * b0 = b1 - * s2 = s0 + event.mac[2] - * b1 = b0 - * w1 = swap32(w1) - event.mac[2]; - * - * parm->mac[3] is the total event counts that host requested. - * - * - * event will be the same with the cmd's param. - * - */ - -/* CMD param Formart for DRV INTERNAL CMD HDL*/ -struct drvint_cmd_parm { - int i_cid; /*internal cmd id*/ - int sz; /* buf sz*/ - unsigned char *pbuf; -}; - -/*------------------- Below are used for RF/BB tuning ---------------------*/ - -struct setantenna_parm { - u8 tx_antset; - u8 rx_antset; - u8 tx_antenna; - u8 rx_antenna; -}; - -struct enrateadaptive_parm { - u32 en; -}; - -struct settxagctbl_parm { - u32 txagc[MAX_RATES_LENGTH]; -}; - -struct gettxagctbl_parm { - u32 rsvd; -}; - -struct gettxagctbl_rsp { - u32 txagc[MAX_RATES_LENGTH]; -}; - -struct setagcctrl_parm { - u32 agcctrl; /* 0: pure hw, 1: fw */ -}; - -struct setssup_parm { - u32 ss_ForceUp[MAX_RATES_LENGTH]; -}; - -struct getssup_parm { - u32 rsvd; -}; - -struct getssup_rsp { - u8 ss_ForceUp[MAX_RATES_LENGTH]; -}; - -struct setssdlevel_parm { - u8 ss_DLevel[MAX_RATES_LENGTH]; -}; - -struct getssdlevel_parm { - u32 rsvd; -}; - -struct getssdlevel_rsp { - u8 ss_DLevel[MAX_RATES_LENGTH]; -}; - -struct setssulevel_parm { - u8 ss_ULevel[MAX_RATES_LENGTH]; -}; - -struct getssulevel_parm { - u32 rsvd; -}; - -struct getssulevel_rsp { - u8 ss_ULevel[MAX_RATES_LENGTH]; -}; - -struct setcountjudge_parm { - u8 count_judge[MAX_RATES_LENGTH]; -}; - -struct getcountjudge_parm { - u32 rsvd; -}; - -struct getcountjudge_rsp { - u8 count_judge[MAX_RATES_LENGTH]; -}; - -struct setpwrmode_parm { - u8 mode; - u8 flag_low_traffic_en; - u8 flag_lpnav_en; - u8 flag_rf_low_snr_en; - u8 flag_dps_en; /* 1: dps, 0: 32k */ - u8 bcn_rx_en; - u8 bcn_pass_cnt; /* fw report one beacon information to - * driver when it receives bcn_pass_cnt - * beacons. - */ - u8 bcn_to; /* beacon TO (ms). ¡§=0¡¨ no limit.*/ - u16 bcn_itv; - u8 app_itv; /* only for VOIP mode. */ - u8 awake_bcn_itv; - u8 smart_ps; - u8 bcn_pass_time; /* unit: 100ms */ -}; - -struct setatim_parm { - u8 op; /*0: add, 1:del*/ - u8 txid; /* id of dest station.*/ -}; - -struct setratable_parm { - u8 ss_ForceUp[NumRates]; - u8 ss_ULevel[NumRates]; - u8 ss_DLevel[NumRates]; - u8 count_judge[NumRates]; -}; - -struct getratable_parm { - uint rsvd; -}; - -struct getratable_rsp { - u8 ss_ForceUp[NumRates]; - u8 ss_ULevel[NumRates]; - u8 ss_DLevel[NumRates]; - u8 count_judge[NumRates]; -}; - -/*to get TX,RX retry count*/ -struct gettxretrycnt_parm { - unsigned int rsvd; -}; - -struct gettxretrycnt_rsp { - unsigned long tx_retrycnt; -}; - -struct getrxretrycnt_parm { - unsigned int rsvd; -}; - -struct getrxretrycnt_rsp { - unsigned long rx_retrycnt; -}; - -/*to get BCNOK,BCNERR count*/ -struct getbcnokcnt_parm { - unsigned int rsvd; -}; - -struct getbcnokcnt_rsp { - unsigned long bcnokcnt; -}; - -struct getbcnerrcnt_parm { - unsigned int rsvd; -}; - -struct getbcnerrcnt_rsp { - unsigned long bcnerrcnt; -}; - -/* to get current TX power level*/ -struct getcurtxpwrlevel_parm { - unsigned int rsvd; -}; - -struct getcurtxpwrlevel_rsp { - unsigned short tx_power; -}; - -/*dynamic on/off DIG*/ -struct setdig_parm { - unsigned char dig_on; /* 1:on , 0:off */ -}; - -/*dynamic on/off RA*/ -struct setra_parm { - unsigned char ra_on; /* 1:on , 0:off */ -}; - -struct setprobereqextraie_parm { - unsigned char e_id; - unsigned char ie_len; - unsigned char ie[]; -}; - -struct setassocreqextraie_parm { - unsigned char e_id; - unsigned char ie_len; - unsigned char ie[]; -}; - -struct setproberspextraie_parm { - unsigned char e_id; - unsigned char ie_len; - unsigned char ie[]; -}; - -struct setassocrspextraie_parm { - unsigned char e_id; - unsigned char ie_len; - unsigned char ie[]; -}; - -struct addBaReq_parm { - unsigned int tid; -}; - -/*H2C Handler index: 46 */ -struct SetChannel_parm { - u32 curr_ch; -}; - -/*H2C Handler index: 61 */ -struct DisconnectCtrlEx_param { - /* MAXTIME = (2 * FirstStageTO) + (TryPktCnt * TryPktInterval) */ - unsigned char EnableDrvCtrl; - unsigned char TryPktCnt; - unsigned char TryPktInterval; /* Unit: ms */ - unsigned char rsvd; - unsigned int FirstStageTO; /* Unit: ms */ -}; - -#define GEN_CMD_CODE(cmd) cmd ## _CMD_ - -/* - * Result: - * 0x00: success - * 0x01: success, and check Response. - * 0x02: cmd ignored due to duplicated sequence number - * 0x03: cmd dropped due to invalid cmd code - * 0x04: reserved. - */ - -#define H2C_RSP_OFFSET 512 -#define H2C_SUCCESS 0x00 -#define H2C_SUCCESS_RSP 0x01 -#define H2C_DUPLICATED 0x02 -#define H2C_DROPPED 0x03 -#define H2C_PARAMETERS_ERROR 0x04 -#define H2C_REJECTED 0x05 -#define H2C_CMD_OVERFLOW 0x06 -#define H2C_RESERVED 0x07 - -void r8712_setMacAddr_cmd(struct _adapter *padapter, const u8 *mac_addr); -u8 r8712_sitesurvey_cmd(struct _adapter *padapter, struct ndis_802_11_ssid *pssid); -int r8712_createbss_cmd(struct _adapter *padapter); -void r8712_setstakey_cmd(struct _adapter *padapter, u8 *psta, u8 unicast_key); -int r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork); -void r8712_disassoc_cmd(struct _adapter *padapter); -void r8712_setopmode_cmd(struct _adapter *padapter, enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype); -int r8712_setdatarate_cmd(struct _adapter *padapter, u8 *rateset); -void r8712_set_chplan_cmd(struct _adapter *padapter, int chplan); -int r8712_getrfreg_cmd(struct _adapter *padapter, u8 offset, u8 *pval); -int r8712_setrfreg_cmd(struct _adapter *padapter, u8 offset, u32 val); -void r8712_addbareq_cmd(struct _adapter *padapter, u8 tid); -void r8712_wdg_wk_cmd(struct _adapter *padapter); -void r8712_survey_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_disassoc_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_joinbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_createbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); -void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, u32 tryPktCnt, - u32 tryPktInterval, u32 firstStageTO); - -struct _cmd_callback { - u32 cmd_code; - void (*callback)(struct _adapter *padapter, struct cmd_obj *cmd); -}; - -#include "rtl8712_cmd.h" - -#endif /* _CMD_H_ */ - diff --git a/drivers/staging/rtl8712/rtl871x_debug.h b/drivers/staging/rtl8712/rtl871x_debug.h deleted file mode 100644 index 69c631af2a2ac..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_debug.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_DEBUG_H__ -#define __RTL871X_DEBUG_H__ - -#include "osdep_service.h" -#include "drv_types.h" - -#define _drv_emerg_ 1 -#define _drv_alert_ 2 -#define _drv_crit_ 3 -#define _drv_err_ 4 -#define _drv_warning_ 5 -#define _drv_notice_ 6 -#define _drv_info_ 7 -#define _drv_dump_ 8 -#define _drv_debug_ 9 - -#define _module_rtl871x_xmit_c_ BIT(0) -#define _module_xmit_osdep_c_ BIT(1) -#define _module_rtl871x_recv_c_ BIT(2) -#define _module_recv_osdep_c_ BIT(3) -#define _module_rtl871x_mlme_c_ BIT(4) -#define _module_mlme_osdep_c_ BIT(5) -#define _module_rtl871x_sta_mgt_c_ BIT(6) -#define _module_rtl871x_cmd_c_ BIT(7) -#define _module_cmd_osdep_c_ BIT(8) -#define _module_rtl871x_io_c_ BIT(9) -#define _module_io_osdep_c_ BIT(10) -#define _module_os_intfs_c_ BIT(11) -#define _module_rtl871x_security_c_ BIT(12) -#define _module_rtl871x_eeprom_c_ BIT(13) -#define _module_hal_init_c_ BIT(14) -#define _module_hci_hal_init_c_ BIT(15) -#define _module_rtl871x_ioctl_c_ BIT(16) -#define _module_rtl871x_ioctl_set_c_ BIT(17) -#define _module_rtl871x_pwrctrl_c_ BIT(19) -#define _module_hci_intfs_c_ BIT(20) -#define _module_hci_ops_c_ BIT(21) -#define _module_osdep_service_c_ BIT(22) -#define _module_rtl871x_mp_ioctl_c_ BIT(23) -#define _module_hci_ops_os_c_ BIT(24) -#define _module_rtl871x_ioctl_os_c BIT(25) -#define _module_rtl8712_cmd_c_ BIT(26) -#define _module_rtl871x_mp_c_ BIT(27) -#define _module_rtl8712_xmit_c_ BIT(28) -#define _module_rtl8712_efuse_c_ BIT(29) -#define _module_rtl8712_recv_c_ BIT(30) -#define _module_rtl8712_led_c_ BIT(31) - -#undef _MODULE_DEFINE_ - -#if defined _RTL871X_XMIT_C_ - #define _MODULE_DEFINE_ _module_rtl871x_xmit_c_ -#elif defined _XMIT_OSDEP_C_ - #define _MODULE_DEFINE_ _module_xmit_osdep_c_ -#elif defined _RTL871X_RECV_C_ - #define _MODULE_DEFINE_ _module_rtl871x_recv_c_ -#elif defined _RECV_OSDEP_C_ - #define _MODULE_DEFINE_ _module_recv_osdep_c_ -#elif defined _RTL871X_MLME_C_ - #define _MODULE_DEFINE_ _module_rtl871x_mlme_c_ -#elif defined _MLME_OSDEP_C_ - #define _MODULE_DEFINE_ _module_mlme_osdep_c_ -#elif defined _RTL871X_STA_MGT_C_ - #define _MODULE_DEFINE_ _module_rtl871x_sta_mgt_c_ -#elif defined _RTL871X_CMD_C_ - #define _MODULE_DEFINE_ _module_rtl871x_cmd_c_ -#elif defined _CMD_OSDEP_C_ - #define _MODULE_DEFINE_ _module_cmd_osdep_c_ -#elif defined _RTL871X_IO_C_ - #define _MODULE_DEFINE_ _module_rtl871x_io_c_ -#elif defined _IO_OSDEP_C_ - #define _MODULE_DEFINE_ _module_io_osdep_c_ -#elif defined _OS_INTFS_C_ - #define _MODULE_DEFINE_ _module_os_intfs_c_ -#elif defined _RTL871X_SECURITY_C_ - #define _MODULE_DEFINE_ _module_rtl871x_security_c_ -#elif defined _RTL871X_EEPROM_C_ - #define _MODULE_DEFINE_ _module_rtl871x_eeprom_c_ -#elif defined _HAL_INIT_C_ - #define _MODULE_DEFINE_ _module_hal_init_c_ -#elif defined _HCI_HAL_INIT_C_ - #define _MODULE_DEFINE_ _module_hci_hal_init_c_ -#elif defined _RTL871X_IOCTL_C_ - #define _MODULE_DEFINE_ _module_rtl871x_ioctl_c_ -#elif defined _RTL871X_IOCTL_SET_C_ - #define _MODULE_DEFINE_ _module_rtl871x_ioctl_set_c_ -#elif defined _RTL871X_PWRCTRL_C_ - #define _MODULE_DEFINE_ _module_rtl871x_pwrctrl_c_ -#elif defined _HCI_INTF_C_ - #define _MODULE_DEFINE_ _module_hci_intfs_c_ -#elif defined _HCI_OPS_C_ - #define _MODULE_DEFINE_ _module_hci_ops_c_ -#elif defined _OSDEP_HCI_INTF_C_ - #define _MODULE_DEFINE_ _module_hci_intfs_c_ -#elif defined _OSDEP_SERVICE_C_ - #define _MODULE_DEFINE_ _module_osdep_service_c_ -#elif defined _RTL871X_MP_IOCTL_C_ - #define _MODULE_DEFINE_ _module_rtl871x_mp_ioctl_c_ -#elif defined _HCI_OPS_OS_C_ - #define _MODULE_DEFINE_ _module_hci_ops_os_c_ -#elif defined _RTL871X_IOCTL_LINUX_C_ - #define _MODULE_DEFINE_ _module_rtl871x_ioctl_os_c -#elif defined _RTL871X_MP_C_ - #define _MODULE_DEFINE_ _module_rtl871x_mp_c_ -#elif defined _RTL8712_CMD_C_ - #define _MODULE_DEFINE_ _module_rtl8712_cmd_c_ -#elif defined _RTL8712_XMIT_C_ - #define _MODULE_DEFINE_ _module_rtl8712_xmit_c_ -#elif defined _RTL8712_EFUSE_C_ - #define _MODULE_DEFINE_ _module_rtl8712_efuse_c_ -#elif defined _RTL8712_RECV_C_ - #define _MODULE_DEFINE_ _module_rtl8712_recv_c_ -#else - #undef _MODULE_DEFINE_ -#endif - -#endif /*__RTL871X_DEBUG_H__*/ diff --git a/drivers/staging/rtl8712/rtl871x_eeprom.c b/drivers/staging/rtl8712/rtl871x_eeprom.c deleted file mode 100644 index 221bf92e1b1c3..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_eeprom.c +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_eeprom.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_EEPROM_C_ - -#include "osdep_service.h" -#include "drv_types.h" - -static void up_clk(struct _adapter *padapter, u16 *x) -{ - *x = *x | _EESK; - r8712_write8(padapter, EE_9346CR, (u8)*x); - udelay(CLOCK_RATE); -} - -static void down_clk(struct _adapter *padapter, u16 *x) -{ - *x = *x & ~_EESK; - r8712_write8(padapter, EE_9346CR, (u8)*x); - udelay(CLOCK_RATE); -} - -static void shift_out_bits(struct _adapter *padapter, u16 data, u16 count) -{ - u16 x, mask; - - if (padapter->surprise_removed) - goto out; - mask = 0x01 << (count - 1); - x = r8712_read8(padapter, EE_9346CR); - x &= ~(_EEDO | _EEDI); - do { - x &= ~_EEDI; - if (data & mask) - x |= _EEDI; - if (padapter->surprise_removed) - goto out; - r8712_write8(padapter, EE_9346CR, (u8)x); - udelay(CLOCK_RATE); - up_clk(padapter, &x); - down_clk(padapter, &x); - mask >>= 1; - } while (mask); - if (padapter->surprise_removed) - goto out; - x &= ~_EEDI; - r8712_write8(padapter, EE_9346CR, (u8)x); -out:; -} - -static u16 shift_in_bits(struct _adapter *padapter) -{ - u16 x, d = 0, i; - - if (padapter->surprise_removed) - goto out; - x = r8712_read8(padapter, EE_9346CR); - x &= ~(_EEDO | _EEDI); - d = 0; - for (i = 0; i < 16; i++) { - d <<= 1; - up_clk(padapter, &x); - if (padapter->surprise_removed) - goto out; - x = r8712_read8(padapter, EE_9346CR); - x &= ~(_EEDI); - if (x & _EEDO) - d |= 1; - down_clk(padapter, &x); - } -out: - return d; -} - -static void standby(struct _adapter *padapter) -{ - u8 x; - - x = r8712_read8(padapter, EE_9346CR); - x &= ~(_EECS | _EESK); - r8712_write8(padapter, EE_9346CR, x); - udelay(CLOCK_RATE); - x |= _EECS; - r8712_write8(padapter, EE_9346CR, x); - udelay(CLOCK_RATE); -} - -static u16 wait_eeprom_cmd_done(struct _adapter *padapter) -{ - u8 x; - u16 i; - - standby(padapter); - for (i = 0; i < 200; i++) { - x = r8712_read8(padapter, EE_9346CR); - if (x & _EEDO) - return true; - udelay(CLOCK_RATE); - } - return false; -} - -static void eeprom_clean(struct _adapter *padapter) -{ - u16 x; - - if (padapter->surprise_removed) - return; - x = r8712_read8(padapter, EE_9346CR); - if (padapter->surprise_removed) - return; - x &= ~(_EECS | _EEDI); - r8712_write8(padapter, EE_9346CR, (u8)x); - if (padapter->surprise_removed) - return; - up_clk(padapter, &x); - if (padapter->surprise_removed) - return; - down_clk(padapter, &x); -} - -void r8712_eeprom_write16(struct _adapter *padapter, u16 reg, u16 data) -{ - u8 x; - u8 tmp8_ori, tmp8_new, tmp8_clk_ori, tmp8_clk_new; - - tmp8_ori = r8712_read8(padapter, 0x102502f1); - tmp8_new = tmp8_ori & 0xf7; - if (tmp8_ori != tmp8_new) - r8712_write8(padapter, 0x102502f1, tmp8_new); - tmp8_clk_ori = r8712_read8(padapter, 0x10250003); - tmp8_clk_new = tmp8_clk_ori | 0x20; - if (tmp8_clk_new != tmp8_clk_ori) - r8712_write8(padapter, 0x10250003, tmp8_clk_new); - x = r8712_read8(padapter, EE_9346CR); - x &= ~(_EEDI | _EEDO | _EESK | _EEM0); - x |= _EEM1 | _EECS; - r8712_write8(padapter, EE_9346CR, x); - shift_out_bits(padapter, EEPROM_EWEN_OPCODE, 5); - if (padapter->eeprom_address_size == 8) /*CF+ and SDIO*/ - shift_out_bits(padapter, 0, 6); - else /* USB */ - shift_out_bits(padapter, 0, 4); - standby(padapter); - /* Erase this particular word. Write the erase opcode and register - * number in that order. The opcode is 3bits in length; reg is 6 - * bits long. - */ - standby(padapter); - /* write the new word to the EEPROM - * send the write opcode the EEPORM - */ - shift_out_bits(padapter, EEPROM_WRITE_OPCODE, 3); - /* select which word in the EEPROM that we are writing to. */ - shift_out_bits(padapter, reg, padapter->eeprom_address_size); - /* write the data to the selected EEPROM word. */ - shift_out_bits(padapter, data, 16); - if (wait_eeprom_cmd_done(padapter)) { - standby(padapter); - shift_out_bits(padapter, EEPROM_EWDS_OPCODE, 5); - shift_out_bits(padapter, reg, 4); - eeprom_clean(padapter); - } - if (tmp8_clk_new != tmp8_clk_ori) - r8712_write8(padapter, 0x10250003, tmp8_clk_ori); - if (tmp8_new != tmp8_ori) - r8712_write8(padapter, 0x102502f1, tmp8_ori); -} - -u16 r8712_eeprom_read16(struct _adapter *padapter, u16 reg) /*ReadEEprom*/ -{ - u16 x; - u16 data = 0; - u8 tmp8_ori, tmp8_new, tmp8_clk_ori, tmp8_clk_new; - - tmp8_ori = r8712_read8(padapter, 0x102502f1); - tmp8_new = tmp8_ori & 0xf7; - if (tmp8_ori != tmp8_new) - r8712_write8(padapter, 0x102502f1, tmp8_new); - tmp8_clk_ori = r8712_read8(padapter, 0x10250003); - tmp8_clk_new = tmp8_clk_ori | 0x20; - if (tmp8_clk_new != tmp8_clk_ori) - r8712_write8(padapter, 0x10250003, tmp8_clk_new); - if (padapter->surprise_removed) - goto out; - /* select EEPROM, reset bits, set _EECS */ - x = r8712_read8(padapter, EE_9346CR); - if (padapter->surprise_removed) - goto out; - x &= ~(_EEDI | _EEDO | _EESK | _EEM0); - x |= _EEM1 | _EECS; - r8712_write8(padapter, EE_9346CR, (unsigned char)x); - /* write the read opcode and register number in that order - * The opcode is 3bits in length, reg is 6 bits long - */ - shift_out_bits(padapter, EEPROM_READ_OPCODE, 3); - shift_out_bits(padapter, reg, padapter->eeprom_address_size); - /* Now read the data (16 bits) in from the selected EEPROM word */ - data = shift_in_bits(padapter); - eeprom_clean(padapter); -out: - if (tmp8_clk_new != tmp8_clk_ori) - r8712_write8(padapter, 0x10250003, tmp8_clk_ori); - if (tmp8_new != tmp8_ori) - r8712_write8(padapter, 0x102502f1, tmp8_ori); - return data; -} diff --git a/drivers/staging/rtl8712/rtl871x_eeprom.h b/drivers/staging/rtl8712/rtl871x_eeprom.h deleted file mode 100644 index 7bdeb2aaa0259..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_eeprom.h +++ /dev/null @@ -1,88 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __RTL871X_EEPROM_H__ -#define __RTL871X_EEPROM_H__ - -#include "osdep_service.h" - -#define RTL8712_EEPROM_ID 0x8712 -#define EEPROM_MAX_SIZE 256 -#define CLOCK_RATE 50 /*100us*/ - -/*- EEPROM opcodes*/ -#define EEPROM_READ_OPCODE 06 -#define EEPROM_WRITE_OPCODE 05 -#define EEPROM_ERASE_OPCODE 07 -#define EEPROM_EWEN_OPCODE 19 /* Erase/write enable*/ -#define EEPROM_EWDS_OPCODE 16 /* Erase/write disable*/ - -#define EEPROM_CID_DEFAULT 0x0 -#define EEPROM_CID_ALPHA 0x1 -#define EEPROM_CID_Senao 0x3 -#define EEPROM_CID_NetCore 0x5 -#define EEPROM_CID_CAMEO 0X8 -#define EEPROM_CID_SITECOM 0x9 -#define EEPROM_CID_COREGA 0xB -#define EEPROM_CID_EDIMAX_BELKIN 0xC -#define EEPROM_CID_SERCOMM_BELKIN 0xE -#define EEPROM_CID_CAMEO1 0xF -#define EEPROM_CID_WNC_COREGA 0x12 -#define EEPROM_CID_CLEVO 0x13 -#define EEPROM_CID_WHQL 0xFE - -enum RT_CUSTOMER_ID { - RT_CID_DEFAULT = 0, - RT_CID_8187_ALPHA0 = 1, - RT_CID_8187_SERCOMM_PS = 2, - RT_CID_8187_HW_LED = 3, - RT_CID_8187_NETGEAR = 4, - RT_CID_WHQL = 5, - RT_CID_819x_CAMEO = 6, - RT_CID_819x_RUNTOP = 7, - RT_CID_819x_Senao = 8, - RT_CID_TOSHIBA = 9, - RT_CID_819x_Netcore = 10, - RT_CID_Nettronix = 11, - RT_CID_DLINK = 12, - RT_CID_PRONET = 13, - RT_CID_COREGA = 14, - RT_CID_819x_ALPHA = 15, - RT_CID_819x_Sitecom = 16, - RT_CID_CCX = 17, - RT_CID_819x_Lenovo = 18, - RT_CID_819x_QMI = 19, - RT_CID_819x_Edimax_Belkin = 20, - RT_CID_819x_Sercomm_Belkin = 21, - RT_CID_819x_CAMEO1 = 22, - RT_CID_819x_MSI = 23, - RT_CID_819x_Acer = 24, - RT_CID_819x_AzWave_ASUS = 25, - RT_CID_819x_AzWave = 26, - RT_CID_819x_WNC_COREGA = 27, - RT_CID_819x_CLEVO = 28, -}; - -struct eeprom_priv { - u8 bautoload_fail_flag; - u8 bempty; - u8 sys_config; - u8 mac_addr[6]; - u8 config0; - u16 channel_plan; - u8 country_string[3]; - u8 tx_power_b[15]; - u8 tx_power_g[15]; - u8 tx_power_a[201]; - u8 efuse_eeprom_data[EEPROM_MAX_SIZE]; - enum RT_CUSTOMER_ID CustomerID; -}; - -void r8712_eeprom_write16(struct _adapter *padapter, u16 reg, u16 data); -u16 r8712_eeprom_read16(struct _adapter *padapter, u16 reg); - -#endif /*__RTL871X_EEPROM_H__*/ - diff --git a/drivers/staging/rtl8712/rtl871x_event.h b/drivers/staging/rtl8712/rtl871x_event.h deleted file mode 100644 index 0cc780cf43418..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_event.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871x_EVENT_H_ -#define _RTL871x_EVENT_H_ - -#include "osdep_service.h" - -#include "wlan_bssdef.h" -#include -#include - -/* - * Used to report a bss has been scanned - */ -struct survey_event { - struct wlan_bssid_ex bss; -}; - -/* - * Used to report that the requested site survey has been done. - * bss_cnt indicates the number of bss that has been reported. - */ -struct surveydone_event { - unsigned int bss_cnt; - -}; - -/* - * Used to report the link result of joining the given bss - * join_res: - * -1: authentication fail - * -2: association fail - * > 0: TID - */ -struct joinbss_event { - struct wlan_network network; -}; - -/* - * Used to report a given STA has joinned the created BSS. - * It is used in AP/Ad-HoC(M) mode. - */ -struct stassoc_event { - unsigned char macaddr[6]; - unsigned char rsvd[2]; - __le32 cam_id; -}; - -struct stadel_event { - unsigned char macaddr[6]; - unsigned char rsvd[2]; -}; - -struct addba_event { - unsigned int tid; -}; - -#define GEN_EVT_CODE(event) event ## _EVT_ - -struct fwevent { - u32 parmsize; - void (*event_callback)(struct _adapter *dev, u8 *pbuf); -}; - -#define C2HEVENT_SZ 32 -struct event_node { - unsigned char *node; - unsigned char evt_code; - unsigned short evt_sz; - /*volatile*/ int *caller_ff_tail; - int caller_ff_sz; -}; - -struct c2hevent_queue { - /*volatile*/ int head; - /*volatile*/ int tail; - struct event_node nodes[C2HEVENT_SZ]; - unsigned char seq; -}; - -#define NETWORK_QUEUE_SZ 4 - -struct network_queue { - /*volatile*/ int head; - /*volatile*/ int tail; - struct wlan_bssid_ex networks[NETWORK_QUEUE_SZ]; -}; - -struct ADDBA_Req_Report_parm { - unsigned char MacAddress[ETH_ALEN]; - unsigned short StartSeqNum; - unsigned char tid; -}; - -#include "rtl8712_event.h" - -#endif /* _WLANEVENT_H_ */ - diff --git a/drivers/staging/rtl8712/rtl871x_ht.h b/drivers/staging/rtl8712/rtl871x_ht.h deleted file mode 100644 index ebd78665775dd..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ht.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871X_HT_H_ -#define _RTL871X_HT_H_ - -#include "osdep_service.h" -#include "wifi.h" - -struct ht_priv { - unsigned int ht_option; - unsigned int ampdu_enable;/*for enable Tx A-MPDU*/ - unsigned char baddbareq_issued[16]; - unsigned int tx_amsdu_enable;/*for enable Tx A-MSDU */ - unsigned int tx_amdsu_maxlen; /* 1: 8k, 0:4k ; default:8k, for tx */ - unsigned int rx_ampdu_maxlen; /* for rx reordering ctrl win_sz, - * updated when join_callback. - */ - struct ieee80211_ht_cap ht_cap; -}; - -#endif /*_RTL871X_HT_H_ */ - diff --git a/drivers/staging/rtl8712/rtl871x_io.c b/drivers/staging/rtl8712/rtl871x_io.c deleted file mode 100644 index 20e080e284dd4..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_io.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_io.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -/* - * - * The purpose of rtl871x_io.c - * - * a. provides the API - * b. provides the protocol engine - * c. provides the software interface between caller and the hardware interface - * - * For r8712u, both sync/async operations are provided. - * - * Only sync read/write_mem operations are provided. - * - */ - -#define _RTL871X_IO_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "rtl871x_io.h" -#include "osdep_intf.h" -#include "usb_ops.h" - -static uint _init_intf_hdl(struct _adapter *padapter, - struct intf_hdl *pintf_hdl) -{ - struct intf_priv *pintf_priv; - void (*set_intf_option)(u32 *poption) = NULL; - void (*set_intf_funs)(struct intf_hdl *pintf_hdl); - void (*set_intf_ops)(struct _io_ops *pops); - uint (*init_intf_priv)(struct intf_priv *pintfpriv); - - set_intf_option = &(r8712_usb_set_intf_option); - set_intf_funs = &(r8712_usb_set_intf_funs); - set_intf_ops = &r8712_usb_set_intf_ops; - init_intf_priv = &r8712_usb_init_intf_priv; - pintf_priv = kmalloc(sizeof(*pintf_priv), GFP_ATOMIC); - pintf_hdl->pintfpriv = pintf_priv; - if (!pintf_priv) - goto _init_intf_hdl_fail; - pintf_hdl->adapter = (u8 *)padapter; - set_intf_option(&pintf_hdl->intf_option); - set_intf_funs(pintf_hdl); - set_intf_ops(&pintf_hdl->io_ops); - pintf_priv->intf_dev = (u8 *)&padapter->dvobjpriv; - if (init_intf_priv(pintf_priv) == _FAIL) - goto _init_intf_hdl_fail; - return _SUCCESS; -_init_intf_hdl_fail: - kfree(pintf_priv); - return _FAIL; -} - -static void _unload_intf_hdl(struct intf_priv *pintfpriv) -{ - void (*unload_intf_priv)(struct intf_priv *pintfpriv); - - unload_intf_priv = &r8712_usb_unload_intf_priv; - unload_intf_priv(pintfpriv); - kfree(pintfpriv); -} - -static uint register_intf_hdl(u8 *dev, struct intf_hdl *pintfhdl) -{ - struct _adapter *adapter = (struct _adapter *)dev; - - pintfhdl->intf_option = 0; - pintfhdl->adapter = dev; - pintfhdl->intf_dev = (u8 *)&adapter->dvobjpriv; - if (!_init_intf_hdl(adapter, pintfhdl)) - goto register_intf_hdl_fail; - return _SUCCESS; -register_intf_hdl_fail: - return false; -} - -static void unregister_intf_hdl(struct intf_hdl *pintfhdl) -{ - _unload_intf_hdl(pintfhdl->pintfpriv); - memset((u8 *)pintfhdl, 0, sizeof(struct intf_hdl)); -} - -uint r8712_alloc_io_queue(struct _adapter *adapter) -{ - u32 i; - struct io_queue *pio_queue; - struct io_req *pio_req; - - pio_queue = kmalloc(sizeof(*pio_queue), GFP_ATOMIC); - if (!pio_queue) - goto alloc_io_queue_fail; - INIT_LIST_HEAD(&pio_queue->free_ioreqs); - INIT_LIST_HEAD(&pio_queue->processing); - INIT_LIST_HEAD(&pio_queue->pending); - spin_lock_init(&pio_queue->lock); - pio_queue->pallocated_free_ioreqs_buf = kzalloc(NUM_IOREQ * - (sizeof(struct io_req)) + 4, - GFP_ATOMIC); - if ((pio_queue->pallocated_free_ioreqs_buf) == NULL) - goto alloc_io_queue_fail; - pio_queue->free_ioreqs_buf = pio_queue->pallocated_free_ioreqs_buf + 4 - - ((addr_t)(pio_queue->pallocated_free_ioreqs_buf) - & 3); - pio_req = (struct io_req *)(pio_queue->free_ioreqs_buf); - for (i = 0; i < NUM_IOREQ; i++) { - INIT_LIST_HEAD(&pio_req->list); - list_add_tail(&pio_req->list, &pio_queue->free_ioreqs); - pio_req++; - } - if ((register_intf_hdl((u8 *)adapter, &pio_queue->intf)) == _FAIL) - goto alloc_io_queue_fail; - adapter->pio_queue = pio_queue; - return _SUCCESS; -alloc_io_queue_fail: - if (pio_queue) { - kfree(pio_queue->pallocated_free_ioreqs_buf); - kfree(pio_queue); - } - adapter->pio_queue = NULL; - return _FAIL; -} - -void r8712_free_io_queue(struct _adapter *adapter) -{ - struct io_queue *pio_queue = adapter->pio_queue; - - if (pio_queue) { - kfree(pio_queue->pallocated_free_ioreqs_buf); - adapter->pio_queue = NULL; - unregister_intf_hdl(&pio_queue->intf); - kfree(pio_queue); - } -} diff --git a/drivers/staging/rtl8712/rtl871x_io.h b/drivers/staging/rtl8712/rtl871x_io.h deleted file mode 100644 index f09d50a29b82c..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_io.h +++ /dev/null @@ -1,236 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871X_IO_H_ -#define _RTL871X_IO_H_ - -#include "osdep_service.h" -#include "osdep_intf.h" - -#define NUM_IOREQ 8 - -#define MAX_PROT_SZ (64-16) - -#define _IOREADY 0 -#define _IO_WAIT_COMPLETE 1 -#define _IO_WAIT_RSP 2 - -/* IO COMMAND TYPE */ -#define _IOSZ_MASK_ (0x7F) -#define _IO_WRITE_ BIT(7) -#define _IO_FIXED_ BIT(8) -#define _IO_BURST_ BIT(9) -#define _IO_BYTE_ BIT(10) -#define _IO_HW_ BIT(11) -#define _IO_WORD_ BIT(12) -#define _IO_SYNC_ BIT(13) -#define _IO_CMDMASK_ (0x1F80) - -/* - * For prompt mode accessing, caller shall free io_req - * Otherwise, io_handler will free io_req - */ -/* IO STATUS TYPE */ -#define _IO_ERR_ BIT(2) -#define _IO_SUCCESS_ BIT(1) -#define _IO_DONE_ BIT(0) -#define IO_RD32 (_IO_SYNC_ | _IO_WORD_) -#define IO_RD16 (_IO_SYNC_ | _IO_HW_) -#define IO_RD8 (_IO_SYNC_ | _IO_BYTE_) -#define IO_RD32_ASYNC (_IO_WORD_) -#define IO_RD16_ASYNC (_IO_HW_) -#define IO_RD8_ASYNC (_IO_BYTE_) -#define IO_WR32 (_IO_WRITE_ | _IO_SYNC_ | _IO_WORD_) -#define IO_WR16 (_IO_WRITE_ | _IO_SYNC_ | _IO_HW_) -#define IO_WR8 (_IO_WRITE_ | _IO_SYNC_ | _IO_BYTE_) -#define IO_WR32_ASYNC (_IO_WRITE_ | _IO_WORD_) -#define IO_WR16_ASYNC (_IO_WRITE_ | _IO_HW_) -#define IO_WR8_ASYNC (_IO_WRITE_ | _IO_BYTE_) -/* - * Only Sync. burst accessing is provided. - */ -#define IO_WR_BURST(x) (IO_WRITE_ | _IO_SYNC_ | _IO_BURST_ | \ - ((x) & _IOSZ_MASK_)) -#define IO_RD_BURST(x) (_IO_SYNC_ | _IO_BURST_ | ((x) & _IOSZ_MASK_)) -/*below is for the intf_option bit definition...*/ -#define _INTF_ASYNC_ BIT(0) /*support async io*/ -struct intf_priv; -struct intf_hdl; -struct io_queue; -struct _io_ops { - uint (*_sdbus_read_bytes_to_membuf)(struct intf_priv *pintfpriv, - u32 addr, u32 cnt, u8 *pbuf); - uint (*_sdbus_read_blocks_to_membuf)(struct intf_priv *pintfpriv, - u32 addr, u32 cnt, u8 *pbuf); - u8 (*_read8)(struct intf_hdl *pintfhdl, u32 addr); - u16 (*_read16)(struct intf_hdl *pintfhdl, u32 addr); - u32 (*_read32)(struct intf_hdl *pintfhdl, u32 addr); - uint (*_sdbus_write_blocks_from_membuf)(struct intf_priv *pintfpriv, - u32 addr, u32 cnt, u8 *pbuf, - u8 async); - uint (*_sdbus_write_bytes_from_membuf)(struct intf_priv *pintfpriv, - u32 addr, u32 cnt, u8 *pbuf); - u8 (*_cmd52r)(struct intf_priv *pintfpriv, u32 addr); - void (*_cmd52w)(struct intf_priv *pintfpriv, u32 addr, u8 val8); - u8 (*_cmdfunc152r)(struct intf_priv *pintfpriv, u32 addr); - void (*_cmdfunc152w)(struct intf_priv *pintfpriv, u32 addr, u8 val8); - void (*_write8)(struct intf_hdl *pintfhdl, u32 addr, u8 val); - void (*_write16)(struct intf_hdl *pintfhdl, u32 addr, u16 val); - void (*_write32)(struct intf_hdl *pintfhdl, u32 addr, u32 val); - void (*_read_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, - u8 *pmem); - void (*_write_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, - u8 *pmem); - void (*_sync_irp_protocol_rw)(struct io_queue *pio_q); - u32 (*_read_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, - u8 *pmem); - u32 (*_write_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, - u8 *pmem); -}; - -struct io_req { - struct list_head list; - u32 addr; - /*volatile*/ u32 val; - u32 command; - u32 status; - u8 *pbuf; - void (*_async_io_callback)(struct _adapter *padapter, - struct io_req *pio_req, u8 *cnxt); - u8 *cnxt; -}; - -struct intf_hdl { - u32 intf_option; - u8 *adapter; - u8 *intf_dev; - struct intf_priv *pintfpriv; - void (*intf_hdl_init)(u8 *priv); - void (*intf_hdl_unload)(u8 *priv); - void (*intf_hdl_open)(u8 *priv); - void (*intf_hdl_close)(u8 *priv); - struct _io_ops io_ops; -}; - -struct reg_protocol_rd { -#ifdef __LITTLE_ENDIAN - /* DW1 */ - u32 NumOfTrans:4; - u32 Reserved1:4; - u32 Reserved2:24; - /* DW2 */ - u32 ByteCount:7; - u32 WriteEnable:1; /*0:read, 1:write*/ - u32 FixOrContinuous:1; /*0:continuous, 1: Fix*/ - u32 BurstMode:1; - u32 Byte1Access:1; - u32 Byte2Access:1; - u32 Byte4Access:1; - u32 Reserved3:3; - u32 Reserved4:16; - /*DW3*/ - u32 BusAddress; - /*DW4*/ -#else -/*DW1*/ - u32 Reserved1:4; - u32 NumOfTrans:4; - u32 Reserved2:24; - /*DW2*/ - u32 WriteEnable:1; - u32 ByteCount:7; - u32 Reserved3:3; - u32 Byte4Access:1; - u32 Byte2Access:1; - u32 Byte1Access:1; - u32 BurstMode:1; - u32 FixOrContinuous:1; - u32 Reserved4:16; - /*DW3*/ - u32 BusAddress; - /*DW4*/ -#endif -}; - -struct reg_protocol_wt { -#ifdef __LITTLE_ENDIAN - /*DW1*/ - u32 NumOfTrans:4; - u32 Reserved1:4; - u32 Reserved2:24; - /*DW2*/ - u32 ByteCount:7; - u32 WriteEnable:1; /*0:read, 1:write*/ - u32 FixOrContinuous:1; /*0:continuous, 1: Fix*/ - u32 BurstMode:1; - u32 Byte1Access:1; - u32 Byte2Access:1; - u32 Byte4Access:1; - u32 Reserved3:3; - u32 Reserved4:16; - /*DW3*/ - u32 BusAddress; - /*DW4*/ - u32 Value; -#else - /*DW1*/ - u32 Reserved1:4; - u32 NumOfTrans:4; - u32 Reserved2:24; - /*DW2*/ - u32 WriteEnable:1; - u32 ByteCount:7; - u32 Reserved3:3; - u32 Byte4Access:1; - u32 Byte2Access:1; - u32 Byte1Access:1; - u32 BurstMode:1; - u32 FixOrContinuous:1; - u32 Reserved4:16; - /*DW3*/ - u32 BusAddress; - /*DW4*/ - u32 Value; -#endif -}; - -/* - * Below is the data structure used by _io_handler - */ - -struct io_queue { - spinlock_t lock; - struct list_head free_ioreqs; - /*The io_req list that will be served in the single protocol r/w.*/ - struct list_head pending; - struct list_head processing; - u8 *free_ioreqs_buf; /* 4-byte aligned */ - u8 *pallocated_free_ioreqs_buf; - struct intf_hdl intf; -}; - -u8 r8712_read8(struct _adapter *adapter, u32 addr); -u16 r8712_read16(struct _adapter *adapter, u32 addr); -u32 r8712_read32(struct _adapter *adapter, u32 addr); -void r8712_read_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem); -void r8712_read_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem); -void r8712_write8(struct _adapter *adapter, u32 addr, u8 val); -void r8712_write16(struct _adapter *adapter, u32 addr, u16 val); -void r8712_write32(struct _adapter *adapter, u32 addr, u32 val); -void r8712_write_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem); -void r8712_write_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem); -/*ioreq */ -uint r8712_alloc_io_queue(struct _adapter *adapter); -void r8712_free_io_queue(struct _adapter *adapter); - -#endif /*_RTL871X_IO_H_*/ diff --git a/drivers/staging/rtl8712/rtl871x_ioctl.h b/drivers/staging/rtl8712/rtl871x_ioctl.h deleted file mode 100644 index d6332a8c7f4f7..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ioctl.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __IOCTL_H -#define __IOCTL_H - -#include "osdep_service.h" -#include "drv_types.h" - -#ifndef OID_802_11_CAPABILITY - #define OID_802_11_CAPABILITY 0x0d010122 -#endif - -#ifndef OID_802_11_PMKID - #define OID_802_11_PMKID 0x0d010123 -#endif - -/* For DDK-defined OIDs*/ -#define OID_NDIS_SEG1 0x00010100 -#define OID_NDIS_SEG2 0x00010200 -#define OID_NDIS_SEG3 0x00020100 -#define OID_NDIS_SEG4 0x01010100 -#define OID_NDIS_SEG5 0x01020100 -#define OID_NDIS_SEG6 0x01020200 -#define OID_NDIS_SEG7 0xFD010100 -#define OID_NDIS_SEG8 0x0D010100 -#define OID_NDIS_SEG9 0x0D010200 -#define OID_NDIS_SEG10 0x0D020200 -#define SZ_OID_NDIS_SEG1 23 -#define SZ_OID_NDIS_SEG2 3 -#define SZ_OID_NDIS_SEG3 6 -#define SZ_OID_NDIS_SEG4 6 -#define SZ_OID_NDIS_SEG5 4 -#define SZ_OID_NDIS_SEG6 8 -#define SZ_OID_NDIS_SEG7 7 -#define SZ_OID_NDIS_SEG8 36 -#define SZ_OID_NDIS_SEG9 24 -#define SZ_OID_NDIS_SEG10 19 - -/* For Realtek-defined OIDs*/ -#define OID_MP_SEG1 0xFF871100 -#define OID_MP_SEG2 0xFF818000 -#define OID_MP_SEG3 0xFF818700 -#define OID_MP_SEG4 0xFF011100 - -enum oid_type { - QUERY_OID, - SET_OID -}; - -struct oid_funs_node { - unsigned int oid_start; /*the starting number for OID*/ - unsigned int oid_end; /*the ending number for OID*/ - struct oid_obj_priv *node_array; - unsigned int array_sz; /*the size of node_array*/ - int query_counter; /*count the number of query hits for this segment*/ - int set_counter; /*count the number of set hits for this segment*/ -}; - -struct oid_par_priv { - void *adapter_context; - uint oid; - void *information_buf; - unsigned long information_buf_len; - unsigned long *bytes_rw; - unsigned long *bytes_needed; - enum oid_type type_of_oid; - unsigned int dbg; -}; - -struct oid_obj_priv { - unsigned char dbg; /* 0: without OID debug message - * 1: with OID debug message - */ - uint (*oidfuns)(struct oid_par_priv *poid_par_priv); -}; - -uint oid_null_function(struct oid_par_priv *poid_par_priv); - -extern struct iw_handler_def r871x_handlers_def; - -uint drv_query_info(struct net_device *MiniportAdapterContext, - uint Oid, - void *InformationBuffer, - u32 InformationBufferLength, - u32 *BytesWritten, - u32 *BytesNeeded); - -uint drv_set_info(struct net_device *MiniportAdapterContext, - uint Oid, - void *InformationBuffer, - u32 InformationBufferLength, - u32 *BytesRead, - u32 *BytesNeeded); - -#endif diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c deleted file mode 100644 index 0653aa27b1fa2..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c +++ /dev/null @@ -1,2275 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_ioctl_linux.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_IOCTL_LINUX_C_ -#define _RTL871X_MP_IOCTL_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "wlan_bssdef.h" -#include "rtl871x_debug.h" -#include "wifi.h" -#include "rtl871x_mlme.h" -#include "rtl871x_ioctl.h" -#include "rtl871x_ioctl_set.h" -#include "rtl871x_mp_ioctl.h" -#include "mlme_osdep.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 0x1E) - -#define SCAN_ITEM_SIZE 768 -#define MAX_CUSTOM_LEN 64 -#define RATE_COUNT 4 - -static const u32 rtl8180_rates[] = {1000000, 2000000, 5500000, 11000000, - 6000000, 9000000, 12000000, 18000000, - 24000000, 36000000, 48000000, 54000000}; - -static const long ieee80211_wlan_frequencies[] = { - 2412, 2417, 2422, 2427, - 2432, 2437, 2442, 2447, - 2452, 2457, 2462, 2467, - 2472, 2484 -}; - -void r8712_indicate_wx_assoc_event(struct _adapter *padapter) -{ - union iwreq_data wrqu; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, ETH_ALEN); - wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); -} - -void r8712_indicate_wx_disassoc_event(struct _adapter *padapter) -{ - union iwreq_data wrqu; - - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); -} - -static inline void handle_pairwise_key(struct sta_info *psta, - struct ieee_param *param, - struct _adapter *padapter) -{ - /* pairwise key */ - memcpy(psta->x_UncstKey.skey, param->u.crypt.key, - (param->u.crypt. key_len > 16 ? 16 : param->u.crypt.key_len)); - if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ - memcpy(psta->tkiptxmickey. skey, - ¶m->u.crypt.key[16], 8); - memcpy(psta->tkiprxmickey. skey, - ¶m->u.crypt.key[24], 8); - padapter->securitypriv. busetkipkey = false; - mod_timer(&padapter->securitypriv.tkip_timer, - jiffies + msecs_to_jiffies(50)); - } - r8712_setstakey_cmd(padapter, (unsigned char *)psta, true); -} - -static inline void handle_group_key(struct ieee_param *param, - struct _adapter *padapter) -{ - union Keytype *gk = padapter->securitypriv.XGrpKey; - union Keytype *gtk = padapter->securitypriv.XGrptxmickey; - union Keytype *grk = padapter->securitypriv.XGrprxmickey; - - if (param->u.crypt.idx > 0 && - param->u.crypt.idx < 3) { - /* group key idx is 1 or 2 */ - memcpy(gk[param->u.crypt.idx - 1].skey, - param->u.crypt.key, - (param->u.crypt.key_len > 16 ? 16 : - param->u.crypt.key_len)); - memcpy(gtk[param->u.crypt.idx - 1].skey, - ¶m->u.crypt.key[16], 8); - memcpy(grk[param->u.crypt.idx - 1].skey, - ¶m->u.crypt.key[24], 8); - padapter->securitypriv.binstallGrpkey = true; - r8712_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx); - if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE) { - if (padapter->registrypriv.power_mgnt != padapter->pwrctrlpriv.pwr_mode) - mod_timer(&padapter->mlmepriv.dhcp_timer, - jiffies + msecs_to_jiffies(60000)); - } - } -} - -static noinline_for_stack char *translate_scan_wpa(struct iw_request_info *info, - struct wlan_network *pnetwork, - struct iw_event *iwe, - char *start, char *stop) -{ - /* parsing WPA/WPA2 IE */ - u8 buf[MAX_WPA_IE_LEN]; - u8 wpa_ie[255], rsn_ie[255]; - u16 wpa_len = 0, rsn_len = 0; - int n, i; - - r8712_get_sec_ie(pnetwork->network.IEs, - pnetwork->network.IELength, rsn_ie, &rsn_len, - wpa_ie, &wpa_len); - if (wpa_len > 0) { - memset(buf, 0, MAX_WPA_IE_LEN); - n = sprintf(buf, "wpa_ie="); - for (i = 0; i < wpa_len; i++) { - n += scnprintf(buf + n, MAX_WPA_IE_LEN - n, - "%02x", wpa_ie[i]); - if (n == MAX_WPA_IE_LEN - 1) - break; - } - memset(iwe, 0, sizeof(*iwe)); - iwe->cmd = IWEVCUSTOM; - iwe->u.data.length = (u16)strlen(buf); - start = iwe_stream_add_point(info, start, stop, iwe, buf); - memset(iwe, 0, sizeof(*iwe)); - iwe->cmd = IWEVGENIE; - iwe->u.data.length = (u16)wpa_len; - start = iwe_stream_add_point(info, start, stop, iwe, wpa_ie); - } - if (rsn_len > 0) { - memset(buf, 0, MAX_WPA_IE_LEN); - n = sprintf(buf, "rsn_ie="); - for (i = 0; i < rsn_len; i++) { - n += scnprintf(buf + n, MAX_WPA_IE_LEN - n, - "%02x", rsn_ie[i]); - if (n == MAX_WPA_IE_LEN - 1) - break; - } - memset(iwe, 0, sizeof(*iwe)); - iwe->cmd = IWEVCUSTOM; - iwe->u.data.length = strlen(buf); - start = iwe_stream_add_point(info, start, stop, iwe, buf); - memset(iwe, 0, sizeof(*iwe)); - iwe->cmd = IWEVGENIE; - iwe->u.data.length = rsn_len; - start = iwe_stream_add_point(info, start, stop, iwe, rsn_ie); - } - - return start; -} - -static noinline_for_stack char *translate_scan_wps(struct iw_request_info *info, - struct wlan_network *pnetwork, - struct iw_event *iwe, - char *start, char *stop) -{ - /* parsing WPS IE */ - u8 wps_ie[512]; - uint wps_ielen; - - if (r8712_get_wps_ie(pnetwork->network.IEs, pnetwork->network.IELength, wps_ie, &wps_ielen)) { - if (wps_ielen > 2) { - iwe->cmd = IWEVGENIE; - iwe->u.data.length = (u16)wps_ielen; - start = iwe_stream_add_point(info, start, stop, iwe, wps_ie); - } - } - - return start; -} - -static char *translate_scan(struct _adapter *padapter, - struct iw_request_info *info, - struct wlan_network *pnetwork, - char *start, char *stop) -{ - struct iw_event iwe; - char *current_val; - s8 *p; - u32 i = 0, ht_ielen = 0; - u16 cap, ht_cap = false; - u8 rssi; - - if ((pnetwork->network.Configuration.DSConfig < 1) || - (pnetwork->network.Configuration.DSConfig > 14)) { - if (pnetwork->network.Configuration.DSConfig < 1) - pnetwork->network.Configuration.DSConfig = 1; - else - pnetwork->network.Configuration.DSConfig = 14; - } - /* AP MAC address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - ether_addr_copy(iwe.u.ap_addr.sa_data, pnetwork->network.MacAddress); - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); - /* Add the ESSID */ - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - iwe.u.data.length = min_t(u32, pnetwork->network.Ssid.SsidLength, 32); - start = iwe_stream_add_point(info, start, stop, &iwe, - pnetwork->network.Ssid.Ssid); - /* parsing HT_CAP_IE */ - p = r8712_get_ie(&pnetwork->network.IEs[12], WLAN_EID_HT_CAPABILITY, - &ht_ielen, pnetwork->network.IELength - 12); - if (p && ht_ielen > 0) - ht_cap = true; - /* Add the protocol name */ - iwe.cmd = SIOCGIWNAME; - if (r8712_is_cckratesonly_included(pnetwork->network.rates)) { - if (ht_cap) - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bn"); - else - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b"); - } else if (r8712_is_cckrates_included(pnetwork->network.rates)) { - if (ht_cap) - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bgn"); - else - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg"); - } else { - if (ht_cap) - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11gn"); - else - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11g"); - } - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); - /* Add mode */ - iwe.cmd = SIOCGIWMODE; - memcpy((u8 *)&cap, r8712_get_capability_from_ie(pnetwork->network.IEs), 2); - le16_to_cpus(&cap); - if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_ESS)) { - if (cap & WLAN_CAPABILITY_ESS) - iwe.u.mode = (u32)IW_MODE_MASTER; - else - iwe.u.mode = (u32)IW_MODE_ADHOC; - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); - } - /* Add frequency/channel */ - iwe.cmd = SIOCGIWFREQ; - { - /* check legal index */ - u8 dsconfig = pnetwork->network.Configuration.DSConfig; - - if (dsconfig >= 1 && dsconfig <= sizeof(ieee80211_wlan_frequencies) / sizeof(long)) - iwe.u.freq.m = (s32)(ieee80211_wlan_frequencies[dsconfig - 1] * 100000); - else - iwe.u.freq.m = 0; - } - iwe.u.freq.e = (s16)1; - iwe.u.freq.i = (u8)pnetwork->network.Configuration.DSConfig; - start = iwe_stream_add_event(info, start, stop, &iwe, - IW_EV_FREQ_LEN); - /* Add encryption capability */ - iwe.cmd = SIOCGIWENCODE; - if (cap & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = (u16)(IW_ENCODE_ENABLED | IW_ENCODE_NOKEY); - else - iwe.u.data.flags = (u16)(IW_ENCODE_DISABLED); - iwe.u.data.length = (u16)0; - start = iwe_stream_add_point(info, start, stop, &iwe, pnetwork->network.Ssid.Ssid); - /*Add basic and extended rates */ - current_val = start + iwe_stream_lcp_len(info); - iwe.cmd = SIOCGIWRATE; - iwe.u.bitrate.fixed = 0; - iwe.u.bitrate.disabled = 0; - iwe.u.bitrate.value = 0; - i = 0; - while (pnetwork->network.rates[i] != 0) { - /* Bit rate given in 500 kb/s units */ - iwe.u.bitrate.value = (pnetwork->network.rates[i++] & 0x7F) * 500000; - current_val = iwe_stream_add_value(info, start, current_val, stop, &iwe, - IW_EV_PARAM_LEN); - } - /* Check if we added any event */ - if ((current_val - start) > iwe_stream_lcp_len(info)) - start = current_val; - - start = translate_scan_wpa(info, pnetwork, &iwe, start, stop); - - start = translate_scan_wps(info, pnetwork, &iwe, start, stop); - - /* Add quality statistics */ - iwe.cmd = IWEVQUAL; - rssi = r8712_signal_scale_mapping(pnetwork->network.Rssi); - /* we only update signal_level (signal strength) that is rssi. */ - iwe.u.qual.updated = (u8)(IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID); - iwe.u.qual.level = rssi; /* signal strength */ - iwe.u.qual.qual = 0; /* signal quality */ - iwe.u.qual.noise = 0; /* noise level */ - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); - /* how to translate rssi to ?% */ - return start; -} - -static int wpa_set_auth_algs(struct net_device *dev, u32 value) -{ - struct _adapter *padapter = netdev_priv(dev); - int ret = 0; - - if ((value & AUTH_ALG_SHARED_KEY) && (value & AUTH_ALG_OPEN_SYSTEM)) { - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeAutoSwitch; - padapter->securitypriv.AuthAlgrthm = 3; - } else if (value & AUTH_ALG_SHARED_KEY) { - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; - padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeShared; - padapter->securitypriv.AuthAlgrthm = 1; - } else if (value & AUTH_ALG_OPEN_SYSTEM) { - if (padapter->securitypriv.ndisauthtype < - Ndis802_11AuthModeWPAPSK) { - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeOpen; - padapter->securitypriv.AuthAlgrthm = 0; - } - } else { - ret = -EINVAL; - } - return ret; -} - -static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, - u32 param_len) -{ - int ret = 0; - u32 wep_key_idx, wep_key_len = 0; - struct NDIS_802_11_WEP *pwep = NULL; - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - - param->u.crypt.err = 0; - param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; - if (param_len != (u32)((u8 *) param->u.crypt.key - (u8 *)param) + - param->u.crypt.key_len) - return -EINVAL; - if (!is_broadcast_ether_addr(param->sta_addr)) - return -EINVAL; - - if (param->u.crypt.idx >= WEP_KEYS) { - /* for large key indices, set the default (0) */ - param->u.crypt.idx = 0; - } - if (strcmp(param->u.crypt.alg, "WEP") == 0) { - netdev_info(dev, "r8712u: %s: crypt.alg = WEP\n", __func__); - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; - padapter->securitypriv.PrivacyAlgrthm = _WEP40_; - padapter->securitypriv.XGrpPrivacy = _WEP40_; - wep_key_idx = param->u.crypt.idx; - wep_key_len = param->u.crypt.key_len; - if (wep_key_idx >= WEP_KEYS) - wep_key_idx = 0; - if (wep_key_len <= 0) - return -EINVAL; - - wep_key_len = wep_key_len <= 5 ? 5 : 13; - pwep = kzalloc(sizeof(*pwep), GFP_ATOMIC); - if (!pwep) - return -ENOMEM; - pwep->KeyLength = wep_key_len; - pwep->Length = wep_key_len + - offsetof(struct NDIS_802_11_WEP, KeyMaterial); - if (wep_key_len == 13) { - padapter->securitypriv.PrivacyAlgrthm = _WEP104_; - padapter->securitypriv.XGrpPrivacy = _WEP104_; - } - pwep->KeyIndex = wep_key_idx; - pwep->KeyIndex |= 0x80000000; - memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength); - if (param->u.crypt.set_tx) { - if (r8712_set_802_11_add_wep(padapter, pwep)) - ret = -EOPNOTSUPP; - } else { - /* don't update "psecuritypriv->PrivacyAlgrthm" and - * "psecuritypriv->PrivacyKeyIndex=keyid", but can - * r8712_set_key to fw/cam - */ - if (wep_key_idx >= WEP_KEYS) { - ret = -EOPNOTSUPP; - goto exit; - } - memcpy(&psecuritypriv->DefKey[wep_key_idx].skey[0], - pwep->KeyMaterial, - pwep->KeyLength); - psecuritypriv->DefKeylen[wep_key_idx] = - pwep->KeyLength; - r8712_set_key(padapter, psecuritypriv, wep_key_idx); - } - goto exit; - } - if (padapter->securitypriv.AuthAlgrthm == 2) { /* 802_1x */ - struct sta_info *psta, *pbcmc_sta; - struct sta_priv *pstapriv = &padapter->stapriv; - struct security_priv *spriv = &padapter->securitypriv; - - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | - WIFI_MP_STATE)) { /* sta mode */ - psta = r8712_get_stainfo(pstapriv, - get_bssid(pmlmepriv)); - if (psta) { - psta->ieee8021x_blocked = false; - if (spriv->ndisencryptstatus == - Ndis802_11Encryption2Enabled || - spriv->ndisencryptstatus == - Ndis802_11Encryption3Enabled) - psta->XPrivacy = spriv->PrivacyAlgrthm; - if (param->u.crypt.set_tx == 1) - handle_pairwise_key(psta, param, - padapter); - else /* group key */ - handle_group_key(param, padapter); - } - pbcmc_sta = r8712_get_bcmc_stainfo(padapter); - if (pbcmc_sta) { - pbcmc_sta->ieee8021x_blocked = false; - if (spriv->ndisencryptstatus == - Ndis802_11Encryption2Enabled || - spriv->ndisencryptstatus == - Ndis802_11Encryption3Enabled) - pbcmc_sta->XPrivacy = - spriv->PrivacyAlgrthm; - } - } - } -exit: - kfree(pwep); - return ret; -} - -static int r871x_set_wpa_ie(struct _adapter *padapter, char *pie, - unsigned short ielen) -{ - u8 *buf = NULL; - int group_cipher = 0, pairwise_cipher = 0; - int ret = 0; - - if (ielen > MAX_WPA_IE_LEN || !pie) - return -EINVAL; - if (ielen) { - buf = kmemdup(pie, ielen, GFP_ATOMIC); - if (!buf) - return -ENOMEM; - if (ielen < RSN_HEADER_LEN) { - ret = -EINVAL; - goto exit; - } - if (r8712_parse_wpa_ie(buf, ielen, &group_cipher, - &pairwise_cipher) == 0) { - padapter->securitypriv.AuthAlgrthm = 2; - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeWPAPSK; - } - if (r8712_parse_wpa2_ie(buf, ielen, &group_cipher, - &pairwise_cipher) == 0) { - padapter->securitypriv.AuthAlgrthm = 2; - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeWPA2PSK; - } - switch (group_cipher) { - case WPA_CIPHER_NONE: - padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; - break; - case WPA_CIPHER_WEP40: - padapter->securitypriv.XGrpPrivacy = _WEP40_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; - break; - case WPA_CIPHER_TKIP: - padapter->securitypriv.XGrpPrivacy = _TKIP_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; - break; - case WPA_CIPHER_CCMP: - padapter->securitypriv.XGrpPrivacy = _AES_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; - break; - case WPA_CIPHER_WEP104: - padapter->securitypriv.XGrpPrivacy = _WEP104_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; - break; - } - switch (pairwise_cipher) { - case WPA_CIPHER_NONE: - padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; - break; - case WPA_CIPHER_WEP40: - padapter->securitypriv.PrivacyAlgrthm = _WEP40_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; - break; - case WPA_CIPHER_TKIP: - padapter->securitypriv.PrivacyAlgrthm = _TKIP_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; - break; - case WPA_CIPHER_CCMP: - padapter->securitypriv.PrivacyAlgrthm = _AES_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; - break; - case WPA_CIPHER_WEP104: - padapter->securitypriv.PrivacyAlgrthm = _WEP104_; - padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; - break; - } - padapter->securitypriv.wps_phase = false; - {/* set wps_ie */ - u16 cnt = 0; - u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; - - while (cnt < ielen) { - eid = buf[cnt]; - - if ((eid == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(&buf[cnt + 2], wps_oui, 4))) { - netdev_info(padapter->pnetdev, "r8712u: SET WPS_IE\n"); - padapter->securitypriv.wps_ie_len = - ((buf[cnt + 1] + 2) < - (MAX_WPA_IE_LEN << 2)) ? - (buf[cnt + 1] + 2) : - (MAX_WPA_IE_LEN << 2); - memcpy(padapter->securitypriv.wps_ie, - &buf[cnt], - padapter->securitypriv.wps_ie_len); - padapter->securitypriv.wps_phase = - true; - netdev_info(padapter->pnetdev, "r8712u: SET WPS_IE, wps_phase==true\n"); - cnt += buf[cnt + 1] + 2; - break; - } - - cnt += buf[cnt + 1] + 2; - } - } - } -exit: - kfree(buf); - return ret; -} - -static int r8711_wx_get_name(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - u32 ht_ielen = 0; - char *p; - u8 ht_cap = false; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; - u8 *prates; - - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE) == true) { - /* parsing HT_CAP_IE */ - p = r8712_get_ie(&pcur_bss->IEs[12], WLAN_EID_HT_CAPABILITY, - &ht_ielen, pcur_bss->IELength - 12); - if (p && ht_ielen > 0) - ht_cap = true; - prates = pcur_bss->rates; - if (r8712_is_cckratesonly_included(prates)) { - if (ht_cap) - snprintf(wrqu->name, IFNAMSIZ, - "IEEE 802.11bn"); - else - snprintf(wrqu->name, IFNAMSIZ, - "IEEE 802.11b"); - } else if (r8712_is_cckrates_included(prates)) { - if (ht_cap) - snprintf(wrqu->name, IFNAMSIZ, - "IEEE 802.11bgn"); - else - snprintf(wrqu->name, IFNAMSIZ, - "IEEE 802.11bg"); - } else { - if (ht_cap) - snprintf(wrqu->name, IFNAMSIZ, - "IEEE 802.11gn"); - else - snprintf(wrqu->name, IFNAMSIZ, - "IEEE 802.11g"); - } - } else { - snprintf(wrqu->name, IFNAMSIZ, "unassociated"); - } - return 0; -} - -static const long frequency_list[] = { - 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, - 2467, 2472, 2484, 4915, 4920, 4925, 4935, 4940, 4945, 4960, 4980, - 5035, 5040, 5045, 5055, 5060, 5080, 5170, 5180, 5190, 5200, 5210, - 5220, 5230, 5240, 5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, - 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5745, 5765, 5785, 5805, - 5825 -}; - -static int r8711_wx_set_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct iw_freq *fwrq = &wrqu->freq; - int rc = 0; - -/* If setting by frequency, convert to a channel */ - if ((fwrq->e == 1) && (fwrq->m >= 241200000) && (fwrq->m <= 248700000)) { - int f = fwrq->m / 100000; - int c = 0; - - while ((c < 14) && (f != frequency_list[c])) - c++; - fwrq->e = 0; - fwrq->m = c + 1; - } - /* Setting by channel number */ - if ((fwrq->m > 14) || (fwrq->e > 0)) { - rc = -EOPNOTSUPP; - } else { - int channel = fwrq->m; - - if ((channel < 1) || (channel > 14)) { - rc = -EINVAL; - } else { - /* Yes ! We can set it !!! */ - padapter->registrypriv.channel = channel; - } - } - return rc; -} - -static int r8711_wx_get_freq(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; - - if (!check_fwstate(pmlmepriv, _FW_LINKED)) - return -ENOLINK; - - wrqu->freq.m = ieee80211_wlan_frequencies[ - pcur_bss->Configuration.DSConfig - 1] * 100000; - wrqu->freq.e = 1; - wrqu->freq.i = pcur_bss->Configuration.DSConfig; - - return 0; -} - -static int r8711_wx_set_mode(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *b) -{ - struct _adapter *padapter = netdev_priv(dev); - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networkType; - - switch (wrqu->mode) { - case IW_MODE_AUTO: - networkType = Ndis802_11AutoUnknown; - break; - case IW_MODE_ADHOC: - networkType = Ndis802_11IBSS; - break; - case IW_MODE_MASTER: - networkType = Ndis802_11APMode; - break; - case IW_MODE_INFRA: - networkType = Ndis802_11Infrastructure; - break; - default: - return -EINVAL; - } - if (Ndis802_11APMode == networkType) - r8712_setopmode_cmd(padapter, networkType); - else - r8712_setopmode_cmd(padapter, Ndis802_11AutoUnknown); - - r8712_set_802_11_infrastructure_mode(padapter, networkType); - return 0; -} - -static int r8711_wx_get_mode(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *b) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) - wrqu->mode = IW_MODE_INFRA; - else if (check_fwstate(pmlmepriv, - WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE)) - wrqu->mode = IW_MODE_ADHOC; - else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) - wrqu->mode = IW_MODE_MASTER; - else - wrqu->mode = IW_MODE_AUTO; - return 0; -} - -static int r871x_wx_set_pmkid(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct iw_pmksa *pPMK = (struct iw_pmksa *) extra; - struct RT_PMKID_LIST *pl = psecuritypriv->PMKIDList; - u8 strZeroMacAddress[ETH_ALEN] = {0x00}; - u8 strIssueBssid[ETH_ALEN] = {0x00}; - u8 j, blInserted = false; - int intReturn = false; - -/* - * There are the BSSID information in the bssid.sa_data array. - * If cmd is IW_PMKSA_FLUSH, it means the wpa_supplicant wants to clear - * all the PMKID information. If cmd is IW_PMKSA_ADD, it means the - * wpa_supplicant wants to add a PMKID/BSSID to driver. - * If cmd is IW_PMKSA_REMOVE, it means the wpa_supplicant wants to - * remove a PMKID/BSSID from driver. - */ - if (!pPMK) - return -EINVAL; - memcpy(strIssueBssid, pPMK->bssid.sa_data, ETH_ALEN); - switch (pPMK->cmd) { - case IW_PMKSA_ADD: - if (!memcmp(strIssueBssid, strZeroMacAddress, ETH_ALEN)) - return intReturn; - intReturn = true; - blInserted = false; - /* overwrite PMKID */ - for (j = 0; j < NUM_PMKID_CACHE; j++) { - if (!memcmp(pl[j].Bssid, strIssueBssid, ETH_ALEN)) { - /* BSSID is matched, the same AP => rewrite - * with new PMKID. - */ - netdev_info(dev, "r8712u: %s: BSSID exists in the PMKList.\n", - __func__); - memcpy(pl[j].PMKID, pPMK->pmkid, IW_PMKID_LEN); - pl[j].bUsed = true; - psecuritypriv->PMKIDIndex = j + 1; - blInserted = true; - break; - } - } - if (!blInserted) { - /* Find a new entry */ - netdev_info(dev, "r8712u: %s: Use the new entry index = %d for this PMKID.\n", - __func__, psecuritypriv->PMKIDIndex); - memcpy(pl[psecuritypriv->PMKIDIndex].Bssid, - strIssueBssid, ETH_ALEN); - memcpy(pl[psecuritypriv->PMKIDIndex].PMKID, - pPMK->pmkid, IW_PMKID_LEN); - pl[psecuritypriv->PMKIDIndex].bUsed = true; - psecuritypriv->PMKIDIndex++; - if (psecuritypriv->PMKIDIndex == NUM_PMKID_CACHE) - psecuritypriv->PMKIDIndex = 0; - } - break; - case IW_PMKSA_REMOVE: - intReturn = true; - for (j = 0; j < NUM_PMKID_CACHE; j++) { - if (!memcmp(pl[j].Bssid, strIssueBssid, ETH_ALEN)) { - /* BSSID is matched, the same AP => Remove - * this PMKID information and reset it. - */ - eth_zero_addr(pl[j].Bssid); - pl[j].bUsed = false; - break; - } - } - break; - case IW_PMKSA_FLUSH: - memset(psecuritypriv->PMKIDList, 0, - sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE); - psecuritypriv->PMKIDIndex = 0; - intReturn = true; - break; - default: - netdev_info(dev, "r8712u: %s: unknown Command\n", __func__); - intReturn = false; - break; - } - return intReturn; -} - -static int r8711_wx_get_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - wrqu->sens.value = 0; - wrqu->sens.fixed = 0; /* no auto select */ - wrqu->sens.disabled = 1; - return 0; -} - -static int r8711_wx_get_range(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct iw_range *range = (struct iw_range *)extra; - u16 val; - int i; - - wrqu->data.length = sizeof(*range); - memset(range, 0, sizeof(*range)); - /* Let's try to keep this struct in the same order as in - * linux/include/wireless.h - */ - - /* TODO: See what values we can set, and remove the ones we can't - * set, or fill them with some default data. - */ - /* ~5 Mb/s real (802.11b) */ - range->throughput = 5 * 1000 * 1000; - /* TODO: 8711 sensitivity ? */ - /* signal level threshold range */ - /* percent values between 0 and 100. */ - range->max_qual.qual = 100; - range->max_qual.level = 100; - range->max_qual.noise = 100; - range->max_qual.updated = 7; /* Updated all three */ - range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */ - /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ - range->avg_qual.level = 0x100 - 78; - range->avg_qual.noise = 0; - range->avg_qual.updated = 7; /* Updated all three */ - range->num_bitrates = RATE_COUNT; - for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) - range->bitrate[i] = rtl8180_rates[i]; - range->min_frag = MIN_FRAG_THRESHOLD; - range->max_frag = MAX_FRAG_THRESHOLD; - range->pm_capa = 0; - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 16; - range->num_channels = 14; - for (i = 0, val = 0; i < 14; i++) { - /* Include only legal frequencies for some countries */ - range->freq[val].i = i + 1; - range->freq[val].m = ieee80211_wlan_frequencies[i] * 100000; - range->freq[val].e = 1; - val++; - if (val == IW_MAX_FREQUENCIES) - break; - } - range->num_frequency = val; - range->enc_capa = IW_ENC_CAPA_WPA | - IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | - IW_ENC_CAPA_CIPHER_CCMP; - return 0; -} - -static int r8711_wx_get_rate(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); - -static int r871x_wx_set_priv(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *awrq, - char *extra) -{ - int ret = 0, len = 0; - char *ext; - struct _adapter *padapter = netdev_priv(dev); - struct iw_point *dwrq = (struct iw_point *)awrq; - - len = dwrq->length; - ext = strndup_user(dwrq->pointer, len); - if (IS_ERR(ext)) - return PTR_ERR(ext); - - if (!strcasecmp(ext, "RSSI")) { - /*Return received signal strength indicator in -db for */ - /* current AP */ - /* Rssi xx */ - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - struct wlan_network *pcur_network = &pmlmepriv->cur_network; - /*static u8 xxxx; */ - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - sprintf(ext, "%s rssi %d", - pcur_network->network.Ssid.Ssid, - /*(xxxx=xxxx+10) */ - ((padapter->recvpriv.fw_rssi) >> 1) - 95 - /*pcur_network->network.Rssi */ - ); - } else { - sprintf(ext, "OK"); - } - } else if (!strcasecmp(ext, "LINKSPEED")) { - /*Return link speed in MBPS */ - /*LinkSpeed xx */ - union iwreq_data wrqd; - int ret_inner; - int mbps; - - ret_inner = r8711_wx_get_rate(dev, info, &wrqd, extra); - if (ret_inner != 0) - mbps = 0; - else - mbps = wrqd.bitrate.value / 1000000; - sprintf(ext, "LINKSPEED %d", mbps); - } else if (!strcasecmp(ext, "MACADDR")) { - /*Return mac address of the station */ - /* Macaddr = xx:xx:xx:xx:xx:xx */ - sprintf(ext, "MACADDR = %pM", dev->dev_addr); - } else if (!strcasecmp(ext, "SCAN-ACTIVE")) { - /*Set scan type to active */ - /*OK if successful */ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - pmlmepriv->passive_mode = 1; - sprintf(ext, "OK"); - } else if (!strcasecmp(ext, "SCAN-PASSIVE")) { - /*Set scan type to passive */ - /*OK if successful */ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - pmlmepriv->passive_mode = 0; - sprintf(ext, "OK"); - } else if (!strncmp(ext, "DCE-E", 5)) { - /*Set scan type to passive */ - /*OK if successful */ - r8712_disconnectCtrlEx_cmd(padapter - , 1 /*u32 enableDrvCtrl */ - , 5 /*u32 tryPktCnt */ - , 100 /*u32 tryPktInterval */ - , 5000 /*u32 firstStageTO */ - ); - sprintf(ext, "OK"); - } else if (!strncmp(ext, "DCE-D", 5)) { - /*Set scan type to passive */ - /*OK if successfu */ - r8712_disconnectCtrlEx_cmd(padapter - , 0 /*u32 enableDrvCtrl */ - , 5 /*u32 tryPktCnt */ - , 100 /*u32 tryPktInterval */ - , 5000 /*u32 firstStageTO */ - ); - sprintf(ext, "OK"); - } else { - netdev_info(dev, "r8712u: %s: unknown Command %s.\n", __func__, ext); - goto FREE_EXT; - } - if (copy_to_user(dwrq->pointer, ext, min(dwrq->length, (__u16)(strlen(ext) + 1)))) - ret = -EFAULT; - -FREE_EXT: - kfree(ext); - return ret; -} - -/* set bssid flow - * s1. set_802_11_infrastructure_mode() - * s2. set_802_11_authentication_mode() - * s3. set_802_11_encryption_mode() - * s4. set_802_11_bssid() - * - * This function intends to handle the Set AP command, which specifies the - * MAC# of a preferred Access Point. - * Currently, the request comes via Wireless Extensions' SIOCSIWAP ioctl. - * - * For this operation to succeed, there is no need for the interface to be up. - * - */ -static int r8711_wx_set_wap(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *awrq, char *extra) -{ - int ret = -EINPROGRESS; - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct __queue *queue = &pmlmepriv->scanned_queue; - struct sockaddr *temp = (struct sockaddr *)awrq; - unsigned long irqL; - struct list_head *phead; - u8 *dst_bssid; - struct wlan_network *pnetwork = NULL; - enum NDIS_802_11_AUTHENTICATION_MODE authmode; - - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) - return -EBUSY; - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) - return ret; - if (temp->sa_family != ARPHRD_ETHER) - return -EINVAL; - authmode = padapter->securitypriv.ndisauthtype; - spin_lock_irqsave(&queue->lock, irqL); - phead = &queue->queue; - pmlmepriv->pscanned = phead->next; - while (1) { - if (end_of_queue_search(phead, pmlmepriv->pscanned)) - break; - pnetwork = container_of(pmlmepriv->pscanned, - struct wlan_network, list); - pmlmepriv->pscanned = pmlmepriv->pscanned->next; - dst_bssid = pnetwork->network.MacAddress; - if (!memcmp(dst_bssid, temp->sa_data, ETH_ALEN)) { - r8712_set_802_11_infrastructure_mode(padapter, - pnetwork->network.InfrastructureMode); - break; - } - } - spin_unlock_irqrestore(&queue->lock, irqL); - if (!ret) { - if (!r8712_set_802_11_authentication_mode(padapter, authmode)) { - ret = -ENOMEM; - } else { - if (!r8712_set_802_11_bssid(padapter, temp->sa_data)) - ret = -1; - } - } - return ret; -} - -static int r8711_wx_get_wap(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; - - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) - ether_addr_copy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress); - else - eth_zero_addr(wrqu->ap_addr.sa_data); - return 0; -} - -static int r871x_wx_set_mlme(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - int ret = 0; - struct _adapter *padapter = netdev_priv(dev); - struct iw_mlme *mlme = (struct iw_mlme *) extra; - - if (!mlme) - return -1; - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - if (!r8712_set_802_11_disassociate(padapter)) - ret = -1; - break; - case IW_MLME_DISASSOC: - if (!r8712_set_802_11_disassociate(padapter)) - ret = -1; - break; - default: - return -EOPNOTSUPP; - } - return ret; -} - -/* - * - * This function intends to handle the Set Scan command. - * Currently, the request comes via Wireless Extensions' SIOCSIWSCAN ioctl. - * - * For this operation to succeed, the interface is brought Up beforehand. - * - */ -static int r8711_wx_set_scan(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - u8 status = true; - - if (padapter->driver_stopped) { - netdev_info(dev, "In %s: driver_stopped=%d\n", - __func__, padapter->driver_stopped); - return -1; - } - if (!padapter->bup) - return -ENETDOWN; - if (!padapter->hw_init_completed) - return -1; - if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) || - (pmlmepriv->sitesurveyctrl.traffic_busy)) - return 0; - if (wrqu->data.length == sizeof(struct iw_scan_req)) { - struct iw_scan_req *req = (struct iw_scan_req *)extra; - - if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { - struct ndis_802_11_ssid ssid; - unsigned long irqL; - u32 len = min_t(u8, req->essid_len, IW_ESSID_MAX_SIZE); - - memset((unsigned char *)&ssid, 0, sizeof(struct ndis_802_11_ssid)); - memcpy(ssid.Ssid, req->essid, len); - ssid.SsidLength = len; - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | - _FW_UNDER_LINKING)) || - (pmlmepriv->sitesurveyctrl.traffic_busy)) { - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) - status = false; - } else { - status = r8712_sitesurvey_cmd(padapter, &ssid); - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - } - } else { - status = r8712_set_802_11_bssid_list_scan(padapter); - } - if (!status) - return -1; - return 0; -} - -static int r8711_wx_get_scan(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct __queue *queue = &pmlmepriv->scanned_queue; - struct wlan_network *pnetwork = NULL; - unsigned long irqL; - struct list_head *plist, *phead; - char *ev = extra; - char *stop = ev + wrqu->data.length; - u32 ret = 0, cnt = 0; - - if (padapter->driver_stopped) - return -EINVAL; - while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) { - msleep(30); - cnt++; - if (cnt > 100) - break; - } - spin_lock_irqsave(&queue->lock, irqL); - phead = &queue->queue; - plist = phead->next; - while (1) { - if (end_of_queue_search(phead, plist)) - break; - if ((stop - ev) < SCAN_ITEM_SIZE) { - ret = -E2BIG; - break; - } - pnetwork = container_of(plist, struct wlan_network, list); - ev = translate_scan(padapter, a, pnetwork, ev, stop); - plist = plist->next; - } - spin_unlock_irqrestore(&queue->lock, irqL); - wrqu->data.length = ev - extra; - wrqu->data.flags = 0; - return ret; -} - -/* set ssid flow - * s1. set_802_11_infrastructure_mode() - * s2. set_802_11_authenticaion_mode() - * s3. set_802_11_encryption_mode() - * s4. set_802_11_ssid() - * - * This function intends to handle the Set ESSID command. - * Currently, the request comes via the Wireless Extensions' SIOCSIWESSID ioctl. - * - * For this operation to succeed, there is no need for the interface to be Up. - * - */ -static int r8711_wx_set_essid(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct __queue *queue = &pmlmepriv->scanned_queue; - struct wlan_network *pnetwork = NULL; - enum NDIS_802_11_AUTHENTICATION_MODE authmode; - struct ndis_802_11_ssid ndis_ssid; - u8 *dst_ssid, *src_ssid; - struct list_head *phead; - u32 len; - - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) - return -EBUSY; - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) - return 0; - if (wrqu->essid.length > IW_ESSID_MAX_SIZE) - return -E2BIG; - authmode = padapter->securitypriv.ndisauthtype; - if (wrqu->essid.flags && wrqu->essid.length) { - len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? - wrqu->essid.length : IW_ESSID_MAX_SIZE; - memset(&ndis_ssid, 0, sizeof(struct ndis_802_11_ssid)); - ndis_ssid.SsidLength = len; - memcpy(ndis_ssid.Ssid, extra, len); - src_ssid = ndis_ssid.Ssid; - phead = &queue->queue; - pmlmepriv->pscanned = phead->next; - while (1) { - if (end_of_queue_search(phead, pmlmepriv->pscanned)) - break; - pnetwork = container_of(pmlmepriv->pscanned, - struct wlan_network, list); - pmlmepriv->pscanned = pmlmepriv->pscanned->next; - dst_ssid = pnetwork->network.Ssid.Ssid; - if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength)) - && (pnetwork->network.Ssid.SsidLength == - ndis_ssid.SsidLength)) { - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { - if (pnetwork->network. - InfrastructureMode - != - padapter->mlmepriv. - cur_network.network. - InfrastructureMode) - continue; - } - - r8712_set_802_11_infrastructure_mode( - padapter, - pnetwork->network.InfrastructureMode); - break; - } - } - r8712_set_802_11_authentication_mode(padapter, authmode); - r8712_set_802_11_ssid(padapter, &ndis_ssid); - } - return -EINPROGRESS; -} - -static int r8711_wx_get_essid(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; - u32 len, ret = 0; - - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE)) { - len = pcur_bss->Ssid.SsidLength; - wrqu->essid.length = len; - memcpy(extra, pcur_bss->Ssid.Ssid, len); - wrqu->essid.flags = 1; - } else { - ret = -ENOLINK; - } - return ret; -} - -static int r8711_wx_set_rate(struct net_device *dev, struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - u32 target_rate = wrqu->bitrate.value; - u32 fixed = wrqu->bitrate.fixed; - u32 ratevalue = 0; - u8 datarates[NumRates]; - u8 mpdatarate[NumRates] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff}; - int i; - - if (target_rate == -1) { - ratevalue = 11; - goto set_rate; - } - target_rate = target_rate / 100000; - switch (target_rate) { - case 10: - ratevalue = 0; - break; - case 20: - ratevalue = 1; - break; - case 55: - ratevalue = 2; - break; - case 60: - ratevalue = 3; - break; - case 90: - ratevalue = 4; - break; - case 110: - ratevalue = 5; - break; - case 120: - ratevalue = 6; - break; - case 180: - ratevalue = 7; - break; - case 240: - ratevalue = 8; - break; - case 360: - ratevalue = 9; - break; - case 480: - ratevalue = 10; - break; - case 540: - ratevalue = 11; - break; - default: - ratevalue = 11; - break; - } -set_rate: - for (i = 0; i < NumRates; i++) { - if (ratevalue == mpdatarate[i]) { - datarates[i] = mpdatarate[i]; - if (fixed == 0) - break; - } else { - datarates[i] = 0xff; - } - } - return r8712_setdatarate_cmd(padapter, datarates); -} - -static int r8711_wx_get_rate(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; - struct ieee80211_ht_cap *pht_capie; - unsigned char rf_type = padapter->registrypriv.rf_config; - int i; - u8 *p; - u16 rate, max_rate = 0, ht_cap = false; - u32 ht_ielen = 0; - u8 bw_40MHz = 0, short_GI = 0; - u16 mcs_rate = 0; - - i = 0; - if (!check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE)) - return -ENOLINK; - p = r8712_get_ie(&pcur_bss->IEs[12], WLAN_EID_HT_CAPABILITY, &ht_ielen, - pcur_bss->IELength - 12); - if (p && ht_ielen > 0) { - ht_cap = true; - pht_capie = (struct ieee80211_ht_cap *)(p + 2); - memcpy(&mcs_rate, &pht_capie->mcs, 2); - bw_40MHz = (le16_to_cpu(pht_capie->cap_info) & - IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; - short_GI = (le16_to_cpu(pht_capie->cap_info) & - (IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; - } - while ((pcur_bss->rates[i] != 0) && - (pcur_bss->rates[i] != 0xFF)) { - rate = pcur_bss->rates[i] & 0x7F; - if (rate > max_rate) - max_rate = rate; - wrqu->bitrate.fixed = 0; /* no auto select */ - wrqu->bitrate.value = rate * 500000; - i++; - } - if (ht_cap) { - if (mcs_rate & 0x8000 /* MCS15 */ - && - rf_type == RTL8712_RF_2T2R) - max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : - ((short_GI) ? 144 : 130); - else /* default MCS7 */ - max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : - ((short_GI) ? 72 : 65); - max_rate *= 2; /* Mbps/2 */ - } - wrqu->bitrate.value = max_rate * 500000; - return 0; -} - -static int r8711_wx_get_rts(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - - wrqu->rts.value = padapter->registrypriv.rts_thresh; - wrqu->rts.fixed = 0; /* no auto select */ - return 0; -} - -static int r8711_wx_set_frag(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - - if (wrqu->frag.disabled) { - padapter->xmitpriv.frag_len = MAX_FRAG_THRESHOLD; - } else { - if (wrqu->frag.value < MIN_FRAG_THRESHOLD || - wrqu->frag.value > MAX_FRAG_THRESHOLD) - return -EINVAL; - padapter->xmitpriv.frag_len = wrqu->frag.value & ~0x1; - } - return 0; -} - -static int r8711_wx_get_frag(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - - wrqu->frag.value = padapter->xmitpriv.frag_len; - wrqu->frag.fixed = 0; /* no auto select */ - return 0; -} - -static int r8711_wx_get_retry(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - wrqu->retry.value = 7; - wrqu->retry.fixed = 0; /* no auto select */ - wrqu->retry.disabled = 1; - return 0; -} - -static int r8711_wx_set_enc(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *keybuf) -{ - u32 key; - u32 keyindex_provided; - struct NDIS_802_11_WEP wep; - enum NDIS_802_11_AUTHENTICATION_MODE authmode; - struct iw_point *erq = &wrqu->encoding; - struct _adapter *padapter = netdev_priv(dev); - - key = erq->flags & IW_ENCODE_INDEX; - memset(&wep, 0, sizeof(struct NDIS_802_11_WEP)); - if (erq->flags & IW_ENCODE_DISABLED) { - netdev_info(dev, "r8712u: %s: EncryptionDisabled\n", __func__); - padapter->securitypriv.ndisencryptstatus = - Ndis802_11EncryptionDisabled; - padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; - padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; - padapter->securitypriv.AuthAlgrthm = 0; /* open system */ - authmode = Ndis802_11AuthModeOpen; - padapter->securitypriv.ndisauthtype = authmode; - return 0; - } - if (key) { - if (key > WEP_KEYS) - return -EINVAL; - key--; - keyindex_provided = 1; - } else { - keyindex_provided = 0; - key = padapter->securitypriv.PrivacyKeyIndex; - } - /* set authentication mode */ - if (erq->flags & IW_ENCODE_OPEN) { - netdev_info(dev, "r8712u: %s: IW_ENCODE_OPEN\n", __func__); - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; - padapter->securitypriv.AuthAlgrthm = 0; /* open system */ - padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; - padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; - authmode = Ndis802_11AuthModeOpen; - padapter->securitypriv.ndisauthtype = authmode; - } else if (erq->flags & IW_ENCODE_RESTRICTED) { - netdev_info(dev, - "r8712u: %s: IW_ENCODE_RESTRICTED\n", __func__); - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; - padapter->securitypriv.AuthAlgrthm = 1; /* shared system */ - padapter->securitypriv.PrivacyAlgrthm = _WEP40_; - padapter->securitypriv.XGrpPrivacy = _WEP40_; - authmode = Ndis802_11AuthModeShared; - padapter->securitypriv.ndisauthtype = authmode; - } else { - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; - padapter->securitypriv.AuthAlgrthm = 0; /* open system */ - padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; - padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; - authmode = Ndis802_11AuthModeOpen; - padapter->securitypriv.ndisauthtype = authmode; - } - wep.KeyIndex = key; - if (erq->length > 0) { - wep.KeyLength = erq->length <= 5 ? 5 : 13; - wep.Length = wep.KeyLength + - offsetof(struct NDIS_802_11_WEP, KeyMaterial); - } else { - wep.KeyLength = 0; - if (keyindex_provided == 1) { /* set key_id only, no given - * KeyMaterial(erq->length==0). - */ - padapter->securitypriv.PrivacyKeyIndex = key; - switch (padapter->securitypriv.DefKeylen[key]) { - case 5: - padapter->securitypriv.PrivacyAlgrthm = - _WEP40_; - break; - case 13: - padapter->securitypriv.PrivacyAlgrthm = - _WEP104_; - break; - default: - padapter->securitypriv.PrivacyAlgrthm = - _NO_PRIVACY_; - break; - } - return 0; - } - } - wep.KeyIndex |= 0x80000000; /* transmit key */ - memcpy(wep.KeyMaterial, keybuf, wep.KeyLength); - if (r8712_set_802_11_add_wep(padapter, &wep)) - return -EOPNOTSUPP; - return 0; -} - -static int r8711_wx_get_enc(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *keybuf) -{ - uint key; - struct _adapter *padapter = netdev_priv(dev); - struct iw_point *erq = &wrqu->encoding; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - union Keytype *dk = padapter->securitypriv.DefKey; - - if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - if (!check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - return 0; - } - } - key = erq->flags & IW_ENCODE_INDEX; - if (key) { - if (key > WEP_KEYS) - return -EINVAL; - key--; - } else { - key = padapter->securitypriv.PrivacyKeyIndex; - } - erq->flags = key + 1; - switch (padapter->securitypriv.ndisencryptstatus) { - case Ndis802_11EncryptionNotSupported: - case Ndis802_11EncryptionDisabled: - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - break; - case Ndis802_11Encryption1Enabled: - erq->length = padapter->securitypriv.DefKeylen[key]; - if (erq->length) { - memcpy(keybuf, dk[key].skey, - padapter->securitypriv.DefKeylen[key]); - erq->flags |= IW_ENCODE_ENABLED; - if (padapter->securitypriv.ndisauthtype == - Ndis802_11AuthModeOpen) - erq->flags |= IW_ENCODE_OPEN; - else if (padapter->securitypriv.ndisauthtype == - Ndis802_11AuthModeShared) - erq->flags |= IW_ENCODE_RESTRICTED; - } else { - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - } - break; - case Ndis802_11Encryption2Enabled: - case Ndis802_11Encryption3Enabled: - erq->length = 16; - erq->flags |= (IW_ENCODE_ENABLED | IW_ENCODE_OPEN | - IW_ENCODE_NOKEY); - break; - default: - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - break; - } - return 0; -} - -static int r8711_wx_get_power(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - wrqu->power.value = 0; - wrqu->power.fixed = 0; /* no auto select */ - wrqu->power.disabled = 1; - return 0; -} - -static int r871x_wx_set_gen_ie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - - return r871x_set_wpa_ie(padapter, extra, wrqu->data.length); -} - -static int r871x_wx_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct iw_param *param = (struct iw_param *)&wrqu->param; - int paramid; - int paramval; - int ret = 0; - - paramid = param->flags & IW_AUTH_INDEX; - paramval = param->value; - switch (paramid) { - case IW_AUTH_WPA_VERSION: - break; - case IW_AUTH_CIPHER_PAIRWISE: - break; - case IW_AUTH_CIPHER_GROUP: - break; - case IW_AUTH_KEY_MGMT: - /* - * ??? does not use these parameters - */ - break; - case IW_AUTH_TKIP_COUNTERMEASURES: - if (paramval) { - /* wpa_supplicant is enabling tkip countermeasure. */ - padapter->securitypriv.btkip_countermeasure = true; - } else { - /* wpa_supplicant is disabling tkip countermeasure. */ - padapter->securitypriv.btkip_countermeasure = false; - } - break; - case IW_AUTH_DROP_UNENCRYPTED: - /* HACK: - * - * wpa_supplicant calls set_wpa_enabled when the driver - * is loaded and unloaded, regardless of if WPA is being - * used. No other calls are made which can be used to - * determine if encryption will be used or not prior to - * association being expected. If encryption is not being - * used, drop_unencrypted is set to false, else true -- we - * can use this to determine if the CAP_PRIVACY_ON bit should - * be set. - */ - if (padapter->securitypriv.ndisencryptstatus == - Ndis802_11Encryption1Enabled) { - /* it means init value, or using wep, - * ndisencryptstatus = - * Ndis802_11Encryption1Enabled, - * then it needn't reset it; - */ - break; - } - - if (paramval) { - padapter->securitypriv.ndisencryptstatus = - Ndis802_11EncryptionDisabled; - padapter->securitypriv.PrivacyAlgrthm = - _NO_PRIVACY_; - padapter->securitypriv.XGrpPrivacy = - _NO_PRIVACY_; - padapter->securitypriv.AuthAlgrthm = 0; - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeOpen; - } - break; - case IW_AUTH_80211_AUTH_ALG: - ret = wpa_set_auth_algs(dev, (u32)paramval); - break; - case IW_AUTH_WPA_ENABLED: - break; - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - break; - case IW_AUTH_PRIVACY_INVOKED: - break; - default: - return -EOPNOTSUPP; - } - - return ret; -} - -static int r871x_wx_set_enc_ext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct iw_point *pencoding = &wrqu->encoding; - struct iw_encode_ext *pext = (struct iw_encode_ext *)extra; - struct ieee_param *param = NULL; - char *alg_name; - u32 param_len; - int ret = 0; - - switch (pext->alg) { - case IW_ENCODE_ALG_NONE: - alg_name = "none"; - break; - case IW_ENCODE_ALG_WEP: - alg_name = "WEP"; - break; - case IW_ENCODE_ALG_TKIP: - alg_name = "TKIP"; - break; - case IW_ENCODE_ALG_CCMP: - alg_name = "CCMP"; - break; - default: - return -EINVAL; - } - - param_len = sizeof(struct ieee_param) + pext->key_len; - param = kzalloc(param_len, GFP_ATOMIC); - if (!param) - return -ENOMEM; - param->cmd = IEEE_CMD_SET_ENCRYPTION; - eth_broadcast_addr(param->sta_addr); - strscpy((char *)param->u.crypt.alg, alg_name, IEEE_CRYPT_ALG_NAME_LEN); - if (pext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) - param->u.crypt.set_tx = 0; - if (pext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) - param->u.crypt.set_tx = 1; - param->u.crypt.idx = (pencoding->flags & 0x00FF) - 1; - if (pext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) - memcpy(param->u.crypt.seq, pext->rx_seq, 8); - if (pext->key_len) { - param->u.crypt.key_len = pext->key_len; - memcpy(param + 1, pext + 1, pext->key_len); - } - ret = wpa_set_encryption(dev, param, param_len); - kfree(param); - return ret; -} - -static int r871x_wx_get_nick(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - if (extra) { - wrqu->data.length = 8; - wrqu->data.flags = 1; - memcpy(extra, "rtl_wifi", 8); - } - return 0; -} - -static int r8711_wx_read32(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *keybuf) -{ - struct _adapter *padapter = netdev_priv(dev); - u32 addr; - u32 data32; - - get_user(addr, (u32 __user *)wrqu->data.pointer); - data32 = r8712_read32(padapter, addr); - put_user(data32, (u32 __user *)wrqu->data.pointer); - wrqu->data.length = (data32 & 0xffff0000) >> 16; - wrqu->data.flags = data32 & 0xffff; - get_user(addr, (u32 __user *)wrqu->data.pointer); - return 0; -} - -static int r8711_wx_write32(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *keybuf) -{ - struct _adapter *padapter = netdev_priv(dev); - u32 addr; - u32 data32; - - get_user(addr, (u32 __user *)wrqu->data.pointer); - data32 = ((u32)wrqu->data.length << 16) | (u32)wrqu->data.flags; - r8712_write32(padapter, addr, data32); - return 0; -} - -static int dummy(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *b) -{ - return -EINVAL; -} - -static int r8711_drvext_hdl(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - return 0; -} - -static int r871x_mp_ioctl_hdl(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct iw_point *p = &wrqu->data; - struct oid_par_priv oid_par; - struct mp_ioctl_handler *phandler; - struct mp_ioctl_param *poidparam; - unsigned long BytesRead, BytesWritten, BytesNeeded; - u8 *pparmbuf, bset; - u16 len; - uint status; - int ret = 0; - - if ((!p->length) || (!p->pointer)) - return -EINVAL; - - bset = (u8)(p->flags & 0xFFFF); - len = p->length; - pparmbuf = memdup_user(p->pointer, len); - if (IS_ERR(pparmbuf)) - return PTR_ERR(pparmbuf); - - poidparam = (struct mp_ioctl_param *)pparmbuf; - if (poidparam->subcode >= MAX_MP_IOCTL_SUBCODE) { - ret = -EINVAL; - goto _r871x_mp_ioctl_hdl_exit; - } - phandler = mp_ioctl_hdl + poidparam->subcode; - if ((phandler->paramsize != 0) && - (poidparam->len < phandler->paramsize)) { - ret = -EINVAL; - goto _r871x_mp_ioctl_hdl_exit; - } - if (phandler->oid == 0 && phandler->handler) { - status = phandler->handler(&oid_par); - } else if (phandler->handler) { - oid_par.adapter_context = padapter; - oid_par.oid = phandler->oid; - oid_par.information_buf = poidparam->data; - oid_par.information_buf_len = poidparam->len; - oid_par.dbg = 0; - BytesWritten = 0; - BytesNeeded = 0; - if (bset) { - oid_par.bytes_rw = &BytesRead; - oid_par.bytes_needed = &BytesNeeded; - oid_par.type_of_oid = SET_OID; - } else { - oid_par.bytes_rw = &BytesWritten; - oid_par.bytes_needed = &BytesNeeded; - oid_par.type_of_oid = QUERY_OID; - } - status = phandler->handler(&oid_par); - /* todo:check status, BytesNeeded, etc. */ - } else { - netdev_info(dev, "r8712u: %s: err!, subcode=%d, oid=%d, handler=%p\n", - __func__, poidparam->subcode, phandler->oid, - phandler->handler); - ret = -EFAULT; - goto _r871x_mp_ioctl_hdl_exit; - } - if (bset == 0x00) { /* query info */ - if (copy_to_user(p->pointer, pparmbuf, len)) - ret = -EFAULT; - } - if (status) { - ret = -EFAULT; - goto _r871x_mp_ioctl_hdl_exit; - } -_r871x_mp_ioctl_hdl_exit: - kfree(pparmbuf); - return ret; -} - -static int r871x_get_ap_info(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct __queue *queue = &pmlmepriv->scanned_queue; - struct iw_point *pdata = &wrqu->data; - struct wlan_network *pnetwork = NULL; - u32 cnt = 0, wpa_ielen; - unsigned long irqL; - struct list_head *plist, *phead; - unsigned char *pbuf; - u8 bssid[ETH_ALEN]; - char data[33]; - - if (padapter->driver_stopped || !pdata) - return -EINVAL; - while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | - _FW_UNDER_LINKING)) { - msleep(30); - cnt++; - if (cnt > 100) - break; - } - pdata->flags = 0; - if (pdata->length < 32) - return -EINVAL; - if (copy_from_user(data, pdata->pointer, 32)) - return -EINVAL; - data[32] = 0; - - spin_lock_irqsave(&pmlmepriv->scanned_queue.lock, irqL); - phead = &queue->queue; - plist = phead->next; - while (1) { - if (end_of_queue_search(phead, plist)) - break; - pnetwork = container_of(plist, struct wlan_network, list); - if (!mac_pton(data, bssid)) { - netdev_info(dev, "r8712u: Invalid BSSID '%s'.\n", - (u8 *)data); - spin_unlock_irqrestore(&pmlmepriv->scanned_queue.lock, - irqL); - return -EINVAL; - } - netdev_info(dev, "r8712u: BSSID:%pM\n", bssid); - if (ether_addr_equal(bssid, pnetwork->network.MacAddress)) { - /* BSSID match, then check if supporting wpa/wpa2 */ - pbuf = r8712_get_wpa_ie(&pnetwork->network.IEs[12], - &wpa_ielen, pnetwork->network.IELength - 12); - if (pbuf && (wpa_ielen > 0)) { - pdata->flags = 1; - break; - } - pbuf = r8712_get_wpa2_ie(&pnetwork->network.IEs[12], - &wpa_ielen, pnetwork->network.IELength - 12); - if (pbuf && (wpa_ielen > 0)) { - pdata->flags = 2; - break; - } - } - plist = plist->next; - } - spin_unlock_irqrestore(&pmlmepriv->scanned_queue.lock, irqL); - if (pdata->length >= 34) { - if (copy_to_user((u8 __user *)pdata->pointer + 32, - (u8 *)&pdata->flags, 1)) - return -EINVAL; - } - return 0; -} - -static int r871x_set_pid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct iw_point *pdata = &wrqu->data; - - if (padapter->driver_stopped || !pdata) - return -EINVAL; - if (copy_from_user(&padapter->pid, pdata->pointer, sizeof(int))) - return -EINVAL; - return 0; -} - -static int r871x_set_chplan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - int ret = 0; - struct _adapter *padapter = netdev_priv(dev); - struct iw_point *pdata = &wrqu->data; - int ch_plan = -1; - - if (padapter->driver_stopped || !pdata) { - ret = -EINVAL; - goto exit; - } - ch_plan = (int)*extra; - r8712_set_chplan_cmd(padapter, ch_plan); - -exit: - - return ret; -} - -static int r871x_wps_start(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct _adapter *padapter = netdev_priv(dev); - struct iw_point *pdata = &wrqu->data; - u32 u32wps_start = 0; - - if (padapter->driver_stopped || !pdata) - return -EINVAL; - if (copy_from_user((void *)&u32wps_start, pdata->pointer, 4)) - return -EFAULT; - if (u32wps_start == 0) - u32wps_start = *extra; - if (u32wps_start == 1) /* WPS Start */ - padapter->ledpriv.LedControlHandler(padapter, - LED_CTL_START_WPS); - else if (u32wps_start == 2) /* WPS Stop because of wps success */ - padapter->ledpriv.LedControlHandler(padapter, - LED_CTL_STOP_WPS); - else if (u32wps_start == 3) /* WPS Stop because of wps fail */ - padapter->ledpriv.LedControlHandler(padapter, - LED_CTL_STOP_WPS_FAIL); - return 0; -} - -static int wpa_set_param(struct net_device *dev, u8 name, u32 value) -{ - struct _adapter *padapter = netdev_priv(dev); - - switch (name) { - case IEEE_PARAM_WPA_ENABLED: - padapter->securitypriv.AuthAlgrthm = 2; /* 802.1x */ - switch ((value) & 0xff) { - case 1: /* WPA */ - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeWPAPSK; /* WPA_PSK */ - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption2Enabled; - break; - case 2: /* WPA2 */ - padapter->securitypriv.ndisauthtype = - Ndis802_11AuthModeWPA2PSK; /* WPA2_PSK */ - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption3Enabled; - break; - } - break; - case IEEE_PARAM_TKIP_COUNTERMEASURES: - break; - case IEEE_PARAM_DROP_UNENCRYPTED: - /* HACK: - * - * wpa_supplicant calls set_wpa_enabled when the driver - * is loaded and unloaded, regardless of if WPA is being - * used. No other calls are made which can be used to - * determine if encryption will be used or not prior to - * association being expected. If encryption is not being - * used, drop_unencrypted is set to false, else true -- we - * can use this to determine if the CAP_PRIVACY_ON bit should - * be set. - */ - break; - case IEEE_PARAM_PRIVACY_INVOKED: - break; - case IEEE_PARAM_AUTH_ALGS: - return wpa_set_auth_algs(dev, value); - case IEEE_PARAM_IEEE_802_1X: - break; - case IEEE_PARAM_WPAX_SELECT: - /* added for WPA2 mixed mode */ - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -static int wpa_mlme(struct net_device *dev, u32 command, u32 reason) -{ - struct _adapter *padapter = netdev_priv(dev); - - switch (command) { - case IEEE_MLME_STA_DEAUTH: - if (!r8712_set_802_11_disassociate(padapter)) - return -1; - break; - case IEEE_MLME_STA_DISASSOC: - if (!r8712_set_802_11_disassociate(padapter)) - return -1; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p) -{ - struct ieee_param *param; - int ret = 0; - struct _adapter *padapter = netdev_priv(dev); - - if (p->length < sizeof(struct ieee_param) || !p->pointer) - return -EINVAL; - param = memdup_user(p->pointer, p->length); - if (IS_ERR(param)) - return PTR_ERR(param); - switch (param->cmd) { - case IEEE_CMD_SET_WPA_PARAM: - ret = wpa_set_param(dev, param->u.wpa_param.name, - param->u.wpa_param.value); - break; - case IEEE_CMD_SET_WPA_IE: - ret = r871x_set_wpa_ie(padapter, (char *)param->u.wpa_ie.data, - (u16)param->u.wpa_ie.len); - break; - case IEEE_CMD_SET_ENCRYPTION: - ret = wpa_set_encryption(dev, param, p->length); - break; - case IEEE_CMD_MLME: - ret = wpa_mlme(dev, param->u.mlme.command, - param->u.mlme.reason_code); - break; - default: - ret = -EOPNOTSUPP; - break; - } - if (ret == 0 && copy_to_user(p->pointer, param, p->length)) - ret = -EFAULT; - kfree(param); - return ret; -} - -/* based on "driver_ipw" and for hostapd */ -int r871x_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct iwreq *wrq = (struct iwreq *)rq; - - switch (cmd) { - case RTL_IOCTL_WPA_SUPPLICANT: - return wpa_supplicant_ioctl(dev, &wrq->u.data); - default: - return -EOPNOTSUPP; - } - return 0; -} - -static iw_handler r8711_handlers[] = { - NULL, /* SIOCSIWCOMMIT */ - r8711_wx_get_name, /* SIOCGIWNAME */ - dummy, /* SIOCSIWNWID */ - dummy, /* SIOCGIWNWID */ - r8711_wx_set_freq, /* SIOCSIWFREQ */ - r8711_wx_get_freq, /* SIOCGIWFREQ */ - r8711_wx_set_mode, /* SIOCSIWMODE */ - r8711_wx_get_mode, /* SIOCGIWMODE */ - dummy, /* SIOCSIWSENS */ - r8711_wx_get_sens, /* SIOCGIWSENS */ - NULL, /* SIOCSIWRANGE */ - r8711_wx_get_range, /* SIOCGIWRANGE */ - r871x_wx_set_priv, /* SIOCSIWPRIV */ - NULL, /* SIOCGIWPRIV */ - NULL, /* SIOCSIWSTATS */ - NULL, /* SIOCGIWSTATS */ - dummy, /* SIOCSIWSPY */ - dummy, /* SIOCGIWSPY */ - NULL, /* SIOCGIWTHRSPY */ - NULL, /* SIOCWIWTHRSPY */ - r8711_wx_set_wap, /* SIOCSIWAP */ - r8711_wx_get_wap, /* SIOCGIWAP */ - r871x_wx_set_mlme, /* request MLME operation; - * uses struct iw_mlme - */ - dummy, /* SIOCGIWAPLIST -- deprecated */ - r8711_wx_set_scan, /* SIOCSIWSCAN */ - r8711_wx_get_scan, /* SIOCGIWSCAN */ - r8711_wx_set_essid, /* SIOCSIWESSID */ - r8711_wx_get_essid, /* SIOCGIWESSID */ - dummy, /* SIOCSIWNICKN */ - r871x_wx_get_nick, /* SIOCGIWNICKN */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - r8711_wx_set_rate, /* SIOCSIWRATE */ - r8711_wx_get_rate, /* SIOCGIWRATE */ - dummy, /* SIOCSIWRTS */ - r8711_wx_get_rts, /* SIOCGIWRTS */ - r8711_wx_set_frag, /* SIOCSIWFRAG */ - r8711_wx_get_frag, /* SIOCGIWFRAG */ - dummy, /* SIOCSIWTXPOW */ - dummy, /* SIOCGIWTXPOW */ - dummy, /* SIOCSIWRETRY */ - r8711_wx_get_retry, /* SIOCGIWRETRY */ - r8711_wx_set_enc, /* SIOCSIWENCODE */ - r8711_wx_get_enc, /* SIOCGIWENCODE */ - dummy, /* SIOCSIWPOWER */ - r8711_wx_get_power, /* SIOCGIWPOWER */ - NULL, /*---hole---*/ - NULL, /*---hole---*/ - r871x_wx_set_gen_ie, /* SIOCSIWGENIE */ - NULL, /* SIOCGIWGENIE */ - r871x_wx_set_auth, /* SIOCSIWAUTH */ - NULL, /* SIOCGIWAUTH */ - r871x_wx_set_enc_ext, /* SIOCSIWENCODEEXT */ - NULL, /* SIOCGIWENCODEEXT */ - r871x_wx_set_pmkid, /* SIOCSIWPMKSA */ - NULL, /*---hole---*/ -}; - -static const struct iw_priv_args r8711_private_args[] = { - { - SIOCIWFIRSTPRIV + 0x0, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "read32" - }, - { - SIOCIWFIRSTPRIV + 0x1, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "write32" - }, - { - SIOCIWFIRSTPRIV + 0x2, 0, 0, "driver_ext" - }, - { - SIOCIWFIRSTPRIV + 0x3, 0, 0, "mp_ioctl" - }, - { - SIOCIWFIRSTPRIV + 0x4, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "apinfo" - }, - { - SIOCIWFIRSTPRIV + 0x5, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setpid" - }, - { - SIOCIWFIRSTPRIV + 0x6, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wps_start" - }, - { - SIOCIWFIRSTPRIV + 0x7, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "chplan" - } -}; - -static iw_handler r8711_private_handler[] = { - r8711_wx_read32, - r8711_wx_write32, - r8711_drvext_hdl, - r871x_mp_ioctl_hdl, - r871x_get_ap_info, /*for MM DTV platform*/ - r871x_set_pid, - r871x_wps_start, - r871x_set_chplan -}; - -static struct iw_statistics *r871x_get_wireless_stats(struct net_device *dev) -{ - struct _adapter *padapter = netdev_priv(dev); - struct iw_statistics *piwstats = &padapter->iwstats; - int tmp_level = 0; - int tmp_qual = 0; - int tmp_noise = 0; - - if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) != true) { - piwstats->qual.qual = 0; - piwstats->qual.level = 0; - piwstats->qual.noise = 0; - } else { - /* show percentage, we need transfer dbm to original value. */ - tmp_level = padapter->recvpriv.fw_rssi; - tmp_qual = padapter->recvpriv.signal; - tmp_noise = padapter->recvpriv.noise; - piwstats->qual.level = tmp_level; - piwstats->qual.qual = tmp_qual; - piwstats->qual.noise = tmp_noise; - } - piwstats->qual.updated = IW_QUAL_ALL_UPDATED; - return &padapter->iwstats; -} - -struct iw_handler_def r871x_handlers_def = { - .standard = r8711_handlers, - .num_standard = ARRAY_SIZE(r8711_handlers), - .private = r8711_private_handler, - .private_args = (struct iw_priv_args *)r8711_private_args, - .num_private = ARRAY_SIZE(r8711_private_handler), - .num_private_args = sizeof(r8711_private_args) / - sizeof(struct iw_priv_args), - .get_wireless_stats = r871x_get_wireless_stats -}; diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_rtl.c b/drivers/staging/rtl8712/rtl871x_ioctl_rtl.c deleted file mode 100644 index 2b539335206aa..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ioctl_rtl.c +++ /dev/null @@ -1,519 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_ioctl_rtl.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_IOCTL_RTL_C_ - -#include -#include "osdep_service.h" -#include "drv_types.h" -#include "wlan_bssdef.h" -#include "wifi.h" -#include "rtl871x_ioctl.h" -#include "rtl871x_ioctl_set.h" -#include "rtl871x_ioctl_rtl.h" -#include "mp_custom_oid.h" -#include "rtl871x_mp.h" -#include "rtl871x_mp_ioctl.h" - -uint oid_rt_get_signal_quality_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_small_packet_crc_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - padapter->recvpriv.rx_smallpacket_crcerr; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_middle_packet_crc_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - padapter->recvpriv.rx_middlepacket_crcerr; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_large_packet_crc_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - padapter->recvpriv.rx_largepacket_crcerr; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_tx_retry_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_rx_retry_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_rx_total_packet_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - padapter->recvpriv.rx_pkts + - padapter->recvpriv.rx_drop; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_tx_beacon_ok_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_tx_beacon_err_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_rx_icv_err_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(uint *)poid_par_priv->information_buf = - padapter->recvpriv.rx_icv_err; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_set_encryption_algorithm_hdl(struct oid_par_priv - *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_preamble_mode_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - u32 preamblemode = 0; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - if (padapter->registrypriv.preamble == PREAMBLE_LONG) - preamblemode = 0; - else if (padapter->registrypriv.preamble == PREAMBLE_AUTO) - preamblemode = 1; - else if (padapter->registrypriv.preamble == PREAMBLE_SHORT) - preamblemode = 2; - *(u32 *)poid_par_priv->information_buf = preamblemode; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_ap_ip_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_channelplan_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - struct eeprom_priv *peeprompriv = &padapter->eeprompriv; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - *(u16 *)poid_par_priv->information_buf = peeprompriv->channel_plan; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_set_channelplan_hdl(struct oid_par_priv - *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - struct eeprom_priv *peeprompriv = &padapter->eeprompriv; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - peeprompriv->channel_plan = *(u16 *)poid_par_priv->information_buf; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_set_preamble_mode_hdl(struct oid_par_priv - *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - u32 preamblemode = 0; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - preamblemode = *(u32 *)poid_par_priv->information_buf; - if (preamblemode == 0) - padapter->registrypriv.preamble = PREAMBLE_LONG; - else if (preamblemode == 1) - padapter->registrypriv.preamble = PREAMBLE_AUTO; - else if (preamblemode == 2) - padapter->registrypriv.preamble = PREAMBLE_SHORT; - *(u32 *)poid_par_priv->information_buf = preamblemode; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_set_bcn_intvl_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_dedicate_probe_hdl(struct oid_par_priv - *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_total_tx_bytes_hdl(struct oid_par_priv - *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - padapter->xmitpriv.tx_bytes; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_total_rx_bytes_hdl(struct oid_par_priv - *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - padapter->recvpriv.rx_bytes; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_current_tx_power_level_hdl(struct oid_par_priv - *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_enc_key_mismatch_count_hdl(struct oid_par_priv - *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_enc_key_match_count_hdl(struct oid_par_priv - *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_channel_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct NDIS_802_11_CONFIGURATION *pnic_Config; - u32 channelnum; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (check_fwstate(pmlmepriv, _FW_LINKED) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) - pnic_Config = &pmlmepriv->cur_network.network.Configuration; - else - pnic_Config = &padapter->registrypriv.dev_network.Configuration; - channelnum = pnic_Config->DSConfig; - *(u32 *)poid_par_priv->information_buf = channelnum; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_hardware_radio_off_hdl(struct oid_par_priv - *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_key_mismatch_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_supported_wireless_mode_hdl(struct oid_par_priv - *poid_par_priv) -{ - u32 ulInfo = 0; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len >= sizeof(u32)) { - ulInfo |= 0x0100; /* WIRELESS_MODE_B */ - ulInfo |= 0x0200; /* WIRELESS_MODE_G */ - ulInfo |= 0x0400; /* WIRELESS_MODE_A */ - *(u32 *) poid_par_priv->information_buf = ulInfo; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_channel_list_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_scan_in_progress_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_forced_data_rate_hdl(struct oid_par_priv *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_wireless_mode_for_scan_list_hdl(struct oid_par_priv - *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_bss_wireless_mode_hdl(struct oid_par_priv - *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_scan_with_magic_packet_hdl(struct oid_par_priv - *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_ap_get_associated_station_list_hdl(struct oid_par_priv - *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_ap_switch_into_ap_mode_hdl(struct oid_par_priv* - poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_ap_supported_hdl(struct oid_par_priv *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_ap_set_passphrase_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_rf_write_registry_hdl(struct oid_par_priv* - poid_par_priv) -{ - uint status = RNDIS_STATUS_SUCCESS; - struct _adapter *Adapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != SET_OID) /* QUERY_OID */ - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len == - (sizeof(unsigned long) * 3)) { - if (r8712_setrfreg_cmd(Adapter, - *(unsigned char *)poid_par_priv->information_buf, - (unsigned long)(*((unsigned long *) - poid_par_priv->information_buf + 2)))) - status = RNDIS_STATUS_NOT_ACCEPTED; - } else { - status = RNDIS_STATUS_INVALID_LENGTH; - } - return status; -} - -uint oid_rt_pro_rf_read_registry_hdl(struct oid_par_priv *poid_par_priv) -{ - uint status = RNDIS_STATUS_SUCCESS; - struct _adapter *Adapter = poid_par_priv->adapter_context; - - if (poid_par_priv->type_of_oid != SET_OID) /* QUERY_OID */ - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len == (sizeof(unsigned long) * - 3)) { - if (Adapter->mppriv.act_in_progress) { - status = RNDIS_STATUS_NOT_ACCEPTED; - } else { - /* init workparam */ - Adapter->mppriv.act_in_progress = true; - Adapter->mppriv.workparam.bcompleted = false; - Adapter->mppriv.workparam.act_type = MPT_READ_RF; - Adapter->mppriv.workparam.io_offset = *(unsigned long *) - poid_par_priv->information_buf; - Adapter->mppriv.workparam.io_value = 0xcccccccc; - - /* RegOffsetValue - The offset of RF register to read. - * RegDataWidth - The data width of RF register to read. - * RegDataValue - The value to read. - * RegOffsetValue = *((unsigned long *)InformationBuffer); - * RegDataWidth = *((unsigned long *)InformationBuffer+1); - * RegDataValue = *((unsigned long *)InformationBuffer+2); - */ - if (r8712_getrfreg_cmd(Adapter, - *(unsigned char *)poid_par_priv->information_buf, - (unsigned char *)&Adapter->mppriv.workparam.io_value - )) - status = RNDIS_STATUS_NOT_ACCEPTED; - } - } else { - status = RNDIS_STATUS_INVALID_LENGTH; - } - return status; -} - -enum _CONNECT_STATE_ { - CHECKINGSTATUS, - ASSOCIATED, - ADHOCMODE, - NOTASSOCIATED -}; - -uint oid_rt_get_connect_state_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *padapter = poid_par_priv->adapter_context; - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - u32 ulInfo; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - /* nStatus==0 CheckingStatus - * nStatus==1 Associated - * nStatus==2 AdHocMode - * nStatus==3 NotAssociated - */ - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) - ulInfo = CHECKINGSTATUS; - else if (check_fwstate(pmlmepriv, _FW_LINKED)) - ulInfo = ASSOCIATED; - else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) - ulInfo = ADHOCMODE; - else - ulInfo = NOTASSOCIATED; - *(u32 *)poid_par_priv->information_buf = ulInfo; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_set_default_key_id_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_rtl.h b/drivers/staging/rtl8712/rtl871x_ioctl_rtl.h deleted file mode 100644 index 7c0b880ac6865..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ioctl_rtl.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871X_IOCTL_RTL_H -#define _RTL871X_IOCTL_RTL_H - -#include "osdep_service.h" -#include "drv_types.h" - -/*************** oid_rtl_seg_01_01 **************/ -uint oid_rt_get_signal_quality_hdl( - struct oid_par_priv *poid_par_priv);/*84*/ -uint oid_rt_get_small_packet_crc_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_middle_packet_crc_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_large_packet_crc_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_tx_retry_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_rx_retry_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_rx_total_packet_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_tx_beacon_ok_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_tx_beacon_err_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_rx_icv_err_hdl( - struct oid_par_priv *poid_par_priv);/*93*/ -uint oid_rt_set_encryption_algorithm_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_preamble_mode_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_ap_ip_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_channelplan_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_set_channelplan_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_set_preamble_mode_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_set_bcn_intvl_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_dedicate_probe_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_total_tx_bytes_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_total_rx_bytes_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_current_tx_power_level_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_enc_key_mismatch_count_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_enc_key_match_count_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_channel_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_hardware_radio_off_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_key_mismatch_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_supported_wireless_mode_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_channel_list_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_scan_in_progress_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_forced_data_rate_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_wireless_mode_for_scan_list_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_bss_wireless_mode_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_scan_with_magic_packet_hdl( - struct oid_par_priv *poid_par_priv); - -/************** oid_rtl_seg_01_03 section start **************/ -uint oid_rt_ap_get_associated_station_list_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_ap_switch_into_ap_mode_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_ap_supported_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_ap_set_passphrase_hdl( - struct oid_par_priv *poid_par_priv); -/* oid_rtl_seg_01_11 */ -uint oid_rt_pro_rf_write_registry_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_rf_read_registry_hdl( - struct oid_par_priv *poid_par_priv); -/*************** oid_rtl_seg_03_00 section start **************/ -uint oid_rt_get_connect_state_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_set_default_key_id_hdl( - struct oid_par_priv *poid_par_priv); - -#endif - diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_set.c b/drivers/staging/rtl8712/rtl871x_ioctl_set.c deleted file mode 100644 index 34c9a52b4c42a..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ioctl_set.c +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_ioctl_set.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_IOCTL_SET_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "rtl871x_ioctl_set.h" -#include "usb_osintf.h" -#include "usb_ops.h" - -static u8 validate_ssid(struct ndis_802_11_ssid *ssid) -{ - u8 i; - - if (ssid->SsidLength > 32) - return false; - for (i = 0; i < ssid->SsidLength; i++) { - /* wifi, printable ascii code must be supported */ - if (!((ssid->Ssid[i] >= 0x20) && (ssid->Ssid[i] <= 0x7e))) - return false; - } - return true; -} - -static u8 do_join(struct _adapter *padapter) -{ - struct list_head *plist, *phead; - u8 *pibss = NULL; - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - struct __queue *queue = &(pmlmepriv->scanned_queue); - int ret; - - phead = &queue->queue; - plist = phead->next; - pmlmepriv->cur_network.join_res = -2; - pmlmepriv->fw_state |= _FW_UNDER_LINKING; - pmlmepriv->pscanned = plist; - pmlmepriv->to_join = true; - - /* adhoc mode will start with an empty queue, but skip checking */ - if (!check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) && - list_empty(&queue->queue)) { - if (pmlmepriv->fw_state & _FW_UNDER_LINKING) - pmlmepriv->fw_state ^= _FW_UNDER_LINKING; - /* when set_ssid/set_bssid for do_join(), but scanning queue - * is empty we try to issue sitesurvey firstly - */ - if (!pmlmepriv->sitesurveyctrl.traffic_busy) - r8712_sitesurvey_cmd(padapter, &pmlmepriv->assoc_ssid); - return true; - } - - ret = r8712_select_and_join_from_scan(pmlmepriv); - if (!ret) { - mod_timer(&pmlmepriv->assoc_timer, - jiffies + msecs_to_jiffies(MAX_JOIN_TIMEOUT)); - } else { - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { - /* submit r8712_createbss_cmd to change to an - * ADHOC_MASTER pmlmepriv->lock has been - * acquired by caller... - */ - struct wlan_bssid_ex *pdev_network = - &padapter->registrypriv.dev_network; - pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE; - pibss = padapter->registrypriv.dev_network.MacAddress; - memcpy(&pdev_network->Ssid, - &pmlmepriv->assoc_ssid, - sizeof(struct ndis_802_11_ssid)); - r8712_update_registrypriv_dev_network(padapter); - r8712_generate_random_ibss(pibss); - if (r8712_createbss_cmd(padapter)) - return false; - pmlmepriv->to_join = false; - } else { - /* can't associate ; reset under-linking */ - if (pmlmepriv->fw_state & _FW_UNDER_LINKING) - pmlmepriv->fw_state ^= - _FW_UNDER_LINKING; - /* when set_ssid/set_bssid for do_join(), but - * there are no desired bss in scanning queue - * we try to issue sitesurvey first - */ - if (!pmlmepriv->sitesurveyctrl.traffic_busy) - r8712_sitesurvey_cmd(padapter, - &pmlmepriv->assoc_ssid); - } - } - return true; -} - -u8 r8712_set_802_11_bssid(struct _adapter *padapter, u8 *bssid) -{ - unsigned long irqL; - u8 status = true; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) { - status = false; - return status; - } - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | - _FW_UNDER_LINKING)) { - status = check_fwstate(pmlmepriv, _FW_UNDER_LINKING); - goto _Abort_Set_BSSID; - } - if (check_fwstate(pmlmepriv, - _FW_LINKED | WIFI_ADHOC_MASTER_STATE)) { - if (!memcmp(&pmlmepriv->cur_network.network.MacAddress, bssid, - ETH_ALEN)) { - if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE)) - /* driver is in - * WIFI_ADHOC_MASTER_STATE - */ - goto _Abort_Set_BSSID; - } else { - r8712_disassoc_cmd(padapter); - if (check_fwstate(pmlmepriv, _FW_LINKED)) - r8712_ind_disconnect(padapter); - r8712_free_assoc_resources(padapter); - if ((check_fwstate(pmlmepriv, - WIFI_ADHOC_MASTER_STATE))) { - _clr_fwstate_(pmlmepriv, - WIFI_ADHOC_MASTER_STATE); - set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); - } - } - } - memcpy(&pmlmepriv->assoc_bssid, bssid, ETH_ALEN); - pmlmepriv->assoc_by_bssid = true; - status = do_join(padapter); - goto done; -_Abort_Set_BSSID: -done: - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - return status; -} - -void r8712_set_802_11_ssid(struct _adapter *padapter, - struct ndis_802_11_ssid *ssid) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_network *pnetwork = &pmlmepriv->cur_network; - - if (!padapter->hw_init_completed) - return; - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) { - check_fwstate(pmlmepriv, _FW_UNDER_LINKING); - goto _Abort_Set_SSID; - } - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE)) { - if ((pmlmepriv->assoc_ssid.SsidLength == ssid->SsidLength) && - (!memcmp(&pmlmepriv->assoc_ssid.Ssid, ssid->Ssid, - ssid->SsidLength))) { - if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - if (!r8712_is_same_ibss(padapter, - pnetwork)) { - /* if in WIFI_ADHOC_MASTER_STATE or - * WIFI_ADHOC_STATE, create bss or - * rejoin again - */ - r8712_disassoc_cmd(padapter); - if (check_fwstate(pmlmepriv, - _FW_LINKED)) - r8712_ind_disconnect(padapter); - r8712_free_assoc_resources(padapter); - if (check_fwstate(pmlmepriv, - WIFI_ADHOC_MASTER_STATE)) { - _clr_fwstate_(pmlmepriv, - WIFI_ADHOC_MASTER_STATE); - set_fwstate(pmlmepriv, - WIFI_ADHOC_STATE); - } - } else { - /* driver is in - * WIFI_ADHOC_MASTER_STATE - */ - goto _Abort_Set_SSID; - } - } - } else { - r8712_disassoc_cmd(padapter); - if (check_fwstate(pmlmepriv, _FW_LINKED)) - r8712_ind_disconnect(padapter); - r8712_free_assoc_resources(padapter); - if (check_fwstate(pmlmepriv, - WIFI_ADHOC_MASTER_STATE)) { - _clr_fwstate_(pmlmepriv, - WIFI_ADHOC_MASTER_STATE); - set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); - } - } - } - if (padapter->securitypriv.btkip_countermeasure) - goto _Abort_Set_SSID; - if (!validate_ssid(ssid)) - goto _Abort_Set_SSID; - memcpy(&pmlmepriv->assoc_ssid, ssid, sizeof(struct ndis_802_11_ssid)); - pmlmepriv->assoc_by_bssid = false; - do_join(padapter); - goto done; -_Abort_Set_SSID: -done: - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -void r8712_set_802_11_infrastructure_mode(struct _adapter *padapter, - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_network *cur_network = &pmlmepriv->cur_network; - enum NDIS_802_11_NETWORK_INFRASTRUCTURE *pold_state = - &(cur_network->network.InfrastructureMode); - - if (*pold_state != networktype) { - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, _FW_LINKED) || - (*pold_state == Ndis802_11IBSS)) - r8712_disassoc_cmd(padapter); - if (check_fwstate(pmlmepriv, - _FW_LINKED | WIFI_ADHOC_MASTER_STATE)) - r8712_free_assoc_resources(padapter); - if (check_fwstate(pmlmepriv, _FW_LINKED) || - (*pold_state == Ndis802_11Infrastructure) || - (*pold_state == Ndis802_11IBSS)) { - /* will clr Linked_state before this function, - * we must have checked whether issue dis-assoc_cmd or - * not - */ - r8712_ind_disconnect(padapter); - } - *pold_state = networktype; - /* clear WIFI_STATION_STATE; WIFI_AP_STATE; WIFI_ADHOC_STATE; - * WIFI_ADHOC_MASTER_STATE - */ - _clr_fwstate_(pmlmepriv, WIFI_STATION_STATE | WIFI_AP_STATE | - WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE); - switch (networktype) { - case Ndis802_11IBSS: - set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); - break; - case Ndis802_11Infrastructure: - set_fwstate(pmlmepriv, WIFI_STATION_STATE); - break; - case Ndis802_11APMode: - set_fwstate(pmlmepriv, WIFI_AP_STATE); - break; - case Ndis802_11AutoUnknown: - case Ndis802_11InfrastructureMax: - break; - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - } -} - -u8 r8712_set_802_11_disassociate(struct _adapter *padapter) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - r8712_disassoc_cmd(padapter); - r8712_ind_disconnect(padapter); - r8712_free_assoc_resources(padapter); - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - return true; -} - -u8 r8712_set_802_11_bssid_list_scan(struct _adapter *padapter) -{ - struct mlme_priv *pmlmepriv = NULL; - unsigned long irqL; - u8 ret = true; - - if (!padapter) - return false; - pmlmepriv = &padapter->mlmepriv; - if (!padapter->hw_init_completed) - return false; - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING) || - pmlmepriv->sitesurveyctrl.traffic_busy) { - /* Scan or linking is in progress, do nothing. */ - ret = (u8)check_fwstate(pmlmepriv, _FW_UNDER_SURVEY); - } else { - r8712_free_network_queue(padapter); - ret = r8712_sitesurvey_cmd(padapter, NULL); - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - return ret; -} - -u8 r8712_set_802_11_authentication_mode(struct _adapter *padapter, - enum NDIS_802_11_AUTHENTICATION_MODE authmode) -{ - struct security_priv *psecuritypriv = &padapter->securitypriv; - u8 ret; - - psecuritypriv->ndisauthtype = authmode; - if (psecuritypriv->ndisauthtype > 3) - psecuritypriv->AuthAlgrthm = 2; /* 802.1x */ - if (r8712_set_auth(padapter, psecuritypriv)) - ret = false; - else - ret = true; - return ret; -} - -int r8712_set_802_11_add_wep(struct _adapter *padapter, - struct NDIS_802_11_WEP *wep) -{ - sint keyid; - struct security_priv *psecuritypriv = &padapter->securitypriv; - - keyid = wep->KeyIndex & 0x3fffffff; - if (keyid >= WEP_KEYS) - return -EINVAL; - switch (wep->KeyLength) { - case 5: - psecuritypriv->PrivacyAlgrthm = _WEP40_; - break; - case 13: - psecuritypriv->PrivacyAlgrthm = _WEP104_; - break; - default: - psecuritypriv->PrivacyAlgrthm = _NO_PRIVACY_; - break; - } - memcpy(psecuritypriv->DefKey[keyid].skey, &wep->KeyMaterial, - wep->KeyLength); - psecuritypriv->DefKeylen[keyid] = wep->KeyLength; - psecuritypriv->PrivacyKeyIndex = keyid; - return r8712_set_key(padapter, psecuritypriv, keyid); -} diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_set.h b/drivers/staging/rtl8712/rtl871x_ioctl_set.h deleted file mode 100644 index e2de820f61d98..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_ioctl_set.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __IOCTL_SET_H -#define __IOCTL_SET_H - -#include "drv_types.h" - -typedef u8 NDIS_802_11_PMKID_VALUE[16]; - -struct BSSIDInfo { - unsigned char BSSID[6]; - NDIS_802_11_PMKID_VALUE PMKID; -}; - -u8 r8712_set_802_11_authentication_mode(struct _adapter *pdapter, - enum NDIS_802_11_AUTHENTICATION_MODE authmode); - -u8 r8712_set_802_11_bssid(struct _adapter *padapter, u8 *bssid); - -int r8712_set_802_11_add_wep(struct _adapter *padapter, - struct NDIS_802_11_WEP *wep); - -u8 r8712_set_802_11_disassociate(struct _adapter *padapter); - -u8 r8712_set_802_11_bssid_list_scan(struct _adapter *padapter); - -void r8712_set_802_11_infrastructure_mode(struct _adapter *padapter, - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype); - -void r8712_set_802_11_ssid(struct _adapter *padapter, - struct ndis_802_11_ssid *ssid); - -#endif - diff --git a/drivers/staging/rtl8712/rtl871x_led.h b/drivers/staging/rtl8712/rtl871x_led.h deleted file mode 100644 index 2f0768132ad8f..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_led.h +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL8712_LED_H -#define __RTL8712_LED_H - -#include "osdep_service.h" -#include "drv_types.h" - -/*=========================================================================== - * LED customization. - *=========================================================================== - */ -enum LED_CTL_MODE { - LED_CTL_POWER_ON = 1, - LED_CTL_LINK = 2, - LED_CTL_NO_LINK = 3, - LED_CTL_TX = 4, - LED_CTL_RX = 5, - LED_CTL_SITE_SURVEY = 6, - LED_CTL_POWER_OFF = 7, - LED_CTL_START_TO_LINK = 8, - LED_CTL_START_WPS = 9, - LED_CTL_STOP_WPS = 10, - LED_CTL_START_WPS_BOTTON = 11, - LED_CTL_STOP_WPS_FAIL = 12, - LED_CTL_STOP_WPS_FAIL_OVERLAP = 13, -}; - -#define IS_LED_WPS_BLINKING(_LED_871x) \ - (((struct LED_871x *)_LED_871x)->CurrLedState == LED_BLINK_WPS \ - || ((struct LED_871x *)_LED_871x)->CurrLedState == LED_BLINK_WPS_STOP \ - || ((struct LED_871x *)_LED_871x)->bLedWPSBlinkInProgress) - -#define IS_LED_BLINKING(_LED_871x) \ - (((struct LED_871x *)_LED_871x)->bLedWPSBlinkInProgress \ - || ((struct LED_871x *)_LED_871x)->bLedScanBlinkInProgress) - -enum LED_PIN_871x { - LED_PIN_GPIO0, - LED_PIN_LED0, - LED_PIN_LED1 -}; - -/*=========================================================================== - * LED customization. - *=========================================================================== - */ -enum LED_STRATEGY_871x { - SW_LED_MODE0, /* SW control 1 LED via GPIO0. It is default option. */ - SW_LED_MODE1, /* 2 LEDs, through LED0 and LED1. For ALPHA. */ - SW_LED_MODE2, /* SW control 1 LED via GPIO0, - * custom for AzWave 8187 minicard. - */ - SW_LED_MODE3, /* SW control 1 LED via GPIO0, - * customized for Sercomm Printer Server case. - */ - SW_LED_MODE4, /*for Edimax / Belkin*/ - SW_LED_MODE5, /*for Sercomm / Belkin*/ - SW_LED_MODE6, /*for WNC / Corega*/ - HW_LED, /* HW control 2 LEDs, LED0 and LED1 (there are 4 different - * control modes, see MAC.CONFIG1 for details.) - */ -}; - -struct LED_871x { - struct _adapter *padapter; - enum LED_PIN_871x LedPin; /* Implementation for this SW led. */ - u32 CurrLedState; /* Current LED state. */ - u8 bLedOn; /* true if LED is ON */ - u8 bSWLedCtrl; - u8 bLedBlinkInProgress; /*true if blinking */ - u8 bLedNoLinkBlinkInProgress; - u8 bLedLinkBlinkInProgress; - u8 bLedStartToLinkBlinkInProgress; - u8 bLedScanBlinkInProgress; - u8 bLedWPSBlinkInProgress; - u32 BlinkTimes; /* No. times to toggle for blink.*/ - u32 BlinkingLedState; /* Next state for blinking, - * either LED_ON or OFF. - */ - - struct timer_list BlinkTimer; /* Timer object for led blinking.*/ - struct work_struct BlinkWorkItem; /* Workitem used by BlinkTimer */ -}; - -struct led_priv { - /* add for led control */ - struct LED_871x SwLed0; - struct LED_871x SwLed1; - enum LED_STRATEGY_871x LedStrategy; - u8 bRegUseLed; - void (*LedControlHandler)(struct _adapter *padapter, - enum LED_CTL_MODE LedAction); - /* add for led control */ -}; - -/*=========================================================================== - * Interface to manipulate LED objects. - *=========================================================================== - */ -void r8712_InitSwLeds(struct _adapter *padapter); -void r8712_DeInitSwLeds(struct _adapter *padapter); -void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction); -void r8712_flush_led_works(struct _adapter *padapter); - -#endif - diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c deleted file mode 100644 index 70c295e970685..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mlme.c +++ /dev/null @@ -1,1710 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_mlme.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_MLME_C_ - -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "xmit_osdep.h" -#include "mlme_osdep.h" -#include "sta_info.h" -#include "wifi.h" -#include "wlan_bssdef.h" - -static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len); - -int r8712_init_mlme_priv(struct _adapter *padapter) -{ - sint i; - u8 *pbuf; - struct wlan_network *pnetwork; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - memset((u8 *)pmlmepriv, 0, sizeof(struct mlme_priv)); - pmlmepriv->nic_hdl = (u8 *)padapter; - pmlmepriv->pscanned = NULL; - pmlmepriv->fw_state = 0; - pmlmepriv->cur_network.network.InfrastructureMode = - Ndis802_11AutoUnknown; - /* Maybe someday we should rename this variable to "active_mode"(Jeff)*/ - pmlmepriv->passive_mode = 1; /* 1: active, 0: passive. */ - spin_lock_init(&(pmlmepriv->lock)); - spin_lock_init(&(pmlmepriv->lock2)); - _init_queue(&(pmlmepriv->free_bss_pool)); - _init_queue(&(pmlmepriv->scanned_queue)); - set_scanned_network_val(pmlmepriv, 0); - memset(&pmlmepriv->assoc_ssid, 0, sizeof(struct ndis_802_11_ssid)); - pbuf = kmalloc_array(MAX_BSS_CNT, sizeof(struct wlan_network), - GFP_ATOMIC); - if (!pbuf) - return -ENOMEM; - pmlmepriv->free_bss_buf = pbuf; - pnetwork = (struct wlan_network *)pbuf; - for (i = 0; i < MAX_BSS_CNT; i++) { - INIT_LIST_HEAD(&(pnetwork->list)); - list_add_tail(&(pnetwork->list), - &(pmlmepriv->free_bss_pool.queue)); - pnetwork++; - } - pmlmepriv->sitesurveyctrl.last_rx_pkts = 0; - pmlmepriv->sitesurveyctrl.last_tx_pkts = 0; - pmlmepriv->sitesurveyctrl.traffic_busy = false; - /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */ - r8712_init_mlme_timer(padapter); - return 0; -} - -struct wlan_network *_r8712_alloc_network(struct mlme_priv *pmlmepriv) -{ - unsigned long irqL; - struct wlan_network *pnetwork; - struct __queue *free_queue = &pmlmepriv->free_bss_pool; - - spin_lock_irqsave(&free_queue->lock, irqL); - pnetwork = list_first_entry_or_null(&free_queue->queue, - struct wlan_network, list); - if (pnetwork) { - list_del_init(&pnetwork->list); - pnetwork->last_scanned = jiffies; - pmlmepriv->num_of_scanned++; - } - spin_unlock_irqrestore(&free_queue->lock, irqL); - return pnetwork; -} - -static void _free_network(struct mlme_priv *pmlmepriv, - struct wlan_network *pnetwork) -{ - u32 curr_time, delta_time; - unsigned long irqL; - struct __queue *free_queue = &(pmlmepriv->free_bss_pool); - - if (!pnetwork) - return; - if (pnetwork->fixed) - return; - curr_time = jiffies; - delta_time = (curr_time - (u32)pnetwork->last_scanned) / HZ; - if (delta_time < SCANQUEUE_LIFETIME) - return; - spin_lock_irqsave(&free_queue->lock, irqL); - list_del_init(&pnetwork->list); - list_add_tail(&pnetwork->list, &free_queue->queue); - pmlmepriv->num_of_scanned--; - spin_unlock_irqrestore(&free_queue->lock, irqL); -} - -static void free_network_nolock(struct mlme_priv *pmlmepriv, - struct wlan_network *pnetwork) -{ - struct __queue *free_queue = &pmlmepriv->free_bss_pool; - - if (!pnetwork) - return; - if (pnetwork->fixed) - return; - list_del_init(&pnetwork->list); - list_add_tail(&pnetwork->list, &free_queue->queue); - pmlmepriv->num_of_scanned--; -} - -/* return the wlan_network with the matching addr - * Shall be called under atomic context... - * to avoid possible racing condition... - */ -static struct wlan_network *r8712_find_network(struct __queue *scanned_queue, - u8 *addr) -{ - unsigned long irqL; - struct list_head *phead, *plist; - struct wlan_network *pnetwork = NULL; - - if (is_zero_ether_addr(addr)) - return NULL; - spin_lock_irqsave(&scanned_queue->lock, irqL); - phead = &scanned_queue->queue; - list_for_each(plist, phead) { - pnetwork = list_entry(plist, struct wlan_network, list); - if (!memcmp(addr, pnetwork->network.MacAddress, ETH_ALEN)) - break; - } - if (plist == phead) - pnetwork = NULL; - spin_unlock_irqrestore(&scanned_queue->lock, irqL); - return pnetwork; -} - -void r8712_free_network_queue(struct _adapter *padapter) -{ - unsigned long irqL; - struct list_head *phead, *plist; - struct wlan_network *pnetwork; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct __queue *scanned_queue = &pmlmepriv->scanned_queue; - - spin_lock_irqsave(&scanned_queue->lock, irqL); - phead = &scanned_queue->queue; - plist = phead->next; - while (!end_of_queue_search(phead, plist)) { - pnetwork = container_of(plist, struct wlan_network, list); - plist = plist->next; - _free_network(pmlmepriv, pnetwork); - } - spin_unlock_irqrestore(&scanned_queue->lock, irqL); -} - -sint r8712_if_up(struct _adapter *padapter) -{ - sint res; - - if (padapter->driver_stopped || padapter->surprise_removed || - !check_fwstate(&padapter->mlmepriv, _FW_LINKED)) { - res = false; - } else { - res = true; - } - return res; -} - -void r8712_generate_random_ibss(u8 *pibss) -{ - u32 curtime = jiffies; - - pibss[0] = 0x02; /*in ad-hoc mode bit1 must set to 1 */ - pibss[1] = 0x11; - pibss[2] = 0x87; - pibss[3] = (u8)(curtime & 0xff); - pibss[4] = (u8)((curtime >> 8) & 0xff); - pibss[5] = (u8)((curtime >> 16) & 0xff); -} - -uint r8712_get_wlan_bssid_ex_sz(struct wlan_bssid_ex *bss) -{ - return sizeof(*bss) + bss->IELength - MAX_IE_SZ; -} - -u8 *r8712_get_capability_from_ie(u8 *ie) -{ - return ie + 8 + 2; -} - -void r8712_free_mlme_priv(struct mlme_priv *pmlmepriv) -{ - kfree(pmlmepriv->free_bss_buf); -} - -static struct wlan_network *alloc_network(struct mlme_priv *pmlmepriv) -{ - return _r8712_alloc_network(pmlmepriv); -} - -int r8712_is_same_ibss(struct _adapter *adapter, struct wlan_network *pnetwork) -{ - int ret = true; - struct security_priv *psecuritypriv = &adapter->securitypriv; - - if ((psecuritypriv->PrivacyAlgrthm != _NO_PRIVACY_) && - (pnetwork->network.Privacy == cpu_to_le32(0))) - ret = false; - else if ((psecuritypriv->PrivacyAlgrthm == _NO_PRIVACY_) && - (pnetwork->network.Privacy == cpu_to_le32(1))) - ret = false; - else - ret = true; - return ret; - -} - -static int is_same_network(struct wlan_bssid_ex *src, - struct wlan_bssid_ex *dst) -{ - u16 s_cap, d_cap; - - memcpy((u8 *)&s_cap, r8712_get_capability_from_ie(src->IEs), 2); - memcpy((u8 *)&d_cap, r8712_get_capability_from_ie(dst->IEs), 2); - return (src->Ssid.SsidLength == dst->Ssid.SsidLength) && - (src->Configuration.DSConfig == - dst->Configuration.DSConfig) && - ((!memcmp(src->MacAddress, dst->MacAddress, - ETH_ALEN))) && - ((!memcmp(src->Ssid.Ssid, - dst->Ssid.Ssid, - src->Ssid.SsidLength))) && - ((s_cap & WLAN_CAPABILITY_IBSS) == - (d_cap & WLAN_CAPABILITY_IBSS)) && - ((s_cap & WLAN_CAPABILITY_ESS) == - (d_cap & WLAN_CAPABILITY_ESS)); - -} - -struct wlan_network *r8712_get_oldest_wlan_network( - struct __queue *scanned_queue) -{ - struct list_head *plist, *phead; - struct wlan_network *pwlan = NULL; - struct wlan_network *oldest = NULL; - - phead = &scanned_queue->queue; - plist = phead->next; - while (1) { - if (end_of_queue_search(phead, plist)) - break; - pwlan = container_of(plist, struct wlan_network, list); - if (!pwlan->fixed) { - if (!oldest || - time_after((unsigned long)oldest->last_scanned, - (unsigned long)pwlan->last_scanned)) - oldest = pwlan; - } - plist = plist->next; - } - return oldest; -} - -static void update_network(struct wlan_bssid_ex *dst, - struct wlan_bssid_ex *src, - struct _adapter *padapter) -{ - u32 last_evm = 0, tmpVal; - struct smooth_rssi_data *sqd = &padapter->recvpriv.signal_qual_data; - - if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) && - is_same_network(&(padapter->mlmepriv.cur_network.network), src)) { - if (padapter->recvpriv.signal_qual_data.total_num++ >= - PHY_LINKQUALITY_SLID_WIN_MAX) { - padapter->recvpriv.signal_qual_data.total_num = - PHY_LINKQUALITY_SLID_WIN_MAX; - last_evm = sqd->elements[sqd->index]; - padapter->recvpriv.signal_qual_data.total_val -= - last_evm; - } - padapter->recvpriv.signal_qual_data.total_val += src->Rssi; - - sqd->elements[sqd->index++] = src->Rssi; - if (padapter->recvpriv.signal_qual_data.index >= - PHY_LINKQUALITY_SLID_WIN_MAX) - padapter->recvpriv.signal_qual_data.index = 0; - /* <1> Showed on UI for user, in percentage. */ - tmpVal = padapter->recvpriv.signal_qual_data.total_val / - padapter->recvpriv.signal_qual_data.total_num; - padapter->recvpriv.signal = (u8)tmpVal; - - src->Rssi = padapter->recvpriv.signal; - } else { - src->Rssi = (src->Rssi + dst->Rssi) / 2; - } - memcpy((u8 *)dst, (u8 *)src, r8712_get_wlan_bssid_ex_sz(src)); -} - -static void update_current_network(struct _adapter *adapter, - struct wlan_bssid_ex *pnetwork) -{ - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - - if (is_same_network(&(pmlmepriv->cur_network.network), pnetwork)) { - update_network(&(pmlmepriv->cur_network.network), - pnetwork, adapter); - r8712_update_protection(adapter, - (pmlmepriv->cur_network.network.IEs) + - sizeof(struct NDIS_802_11_FIXED_IEs), - pmlmepriv->cur_network.network.IELength); - } -} - -/* Caller must hold pmlmepriv->lock first */ -static void update_scanned_network(struct _adapter *adapter, - struct wlan_bssid_ex *target) -{ - struct list_head *plist, *phead; - - u32 bssid_ex_sz; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - struct __queue *queue = &pmlmepriv->scanned_queue; - struct wlan_network *pnetwork = NULL; - struct wlan_network *oldest = NULL; - - phead = &queue->queue; - plist = phead->next; - - while (1) { - if (end_of_queue_search(phead, plist)) - break; - - pnetwork = container_of(plist, struct wlan_network, list); - if (is_same_network(&pnetwork->network, target)) - break; - if ((oldest == ((struct wlan_network *)0)) || - time_after((unsigned long)oldest->last_scanned, - (unsigned long)pnetwork->last_scanned)) - oldest = pnetwork; - - plist = plist->next; - } - - /* If we didn't find a match, then get a new network slot to initialize - * with this beacon's information - */ - if (end_of_queue_search(phead, plist)) { - if (list_empty(&pmlmepriv->free_bss_pool.queue)) { - /* If there are no more slots, expire the oldest */ - pnetwork = oldest; - target->Rssi = (pnetwork->network.Rssi + - target->Rssi) / 2; - memcpy(&pnetwork->network, target, - r8712_get_wlan_bssid_ex_sz(target)); - pnetwork->last_scanned = jiffies; - } else { - /* Otherwise just pull from the free list */ - /* update scan_time */ - pnetwork = alloc_network(pmlmepriv); - if (!pnetwork) - return; - bssid_ex_sz = r8712_get_wlan_bssid_ex_sz(target); - target->Length = bssid_ex_sz; - memcpy(&pnetwork->network, target, bssid_ex_sz); - list_add_tail(&pnetwork->list, &queue->queue); - } - } else { - /* we have an entry and we are going to update it. But - * this entry may be already expired. In this case we - * do the same as we found a new net and call the new_net - * handler - */ - update_network(&pnetwork->network, target, adapter); - pnetwork->last_scanned = jiffies; - } -} - -static void rtl8711_add_network(struct _adapter *adapter, - struct wlan_bssid_ex *pnetwork) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &(((struct _adapter *)adapter)->mlmepriv); - struct __queue *queue = &pmlmepriv->scanned_queue; - - spin_lock_irqsave(&queue->lock, irqL); - update_current_network(adapter, pnetwork); - update_scanned_network(adapter, pnetwork); - spin_unlock_irqrestore(&queue->lock, irqL); -} - -/*select the desired network based on the capability of the (i)bss. - * check items: (1) security - * (2) network_type - * (3) WMM - * (4) HT - * (5) others - */ -static int is_desired_network(struct _adapter *adapter, - struct wlan_network *pnetwork) -{ - u8 wps_ie[512]; - uint wps_ielen; - int bselected = true; - struct security_priv *psecuritypriv = &adapter->securitypriv; - - if (psecuritypriv->wps_phase) { - if (r8712_get_wps_ie(pnetwork->network.IEs, - pnetwork->network.IELength, wps_ie, - &wps_ielen)) - return true; - return false; - } - if ((psecuritypriv->PrivacyAlgrthm != _NO_PRIVACY_) && - (pnetwork->network.Privacy == 0)) - bselected = false; - if (check_fwstate(&adapter->mlmepriv, WIFI_ADHOC_STATE)) { - if (pnetwork->network.InfrastructureMode != - adapter->mlmepriv.cur_network.network.InfrastructureMode) - bselected = false; - } - return bselected; -} - -/* TODO: Perry : For Power Management */ -void r8712_atimdone_event_callback(struct _adapter *adapter, u8 *pbuf) -{ -} - -void r8712_survey_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - unsigned long flags; - u32 len; - struct wlan_bssid_ex *pnetwork; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - - pnetwork = (struct wlan_bssid_ex *)pbuf; -#ifdef __BIG_ENDIAN - /* endian_convert */ - pnetwork->Length = le32_to_cpu(pnetwork->Length); - pnetwork->Ssid.SsidLength = le32_to_cpu(pnetwork->Ssid.SsidLength); - pnetwork->Privacy = le32_to_cpu(pnetwork->Privacy); - pnetwork->Rssi = le32_to_cpu(pnetwork->Rssi); - pnetwork->NetworkTypeInUse = le32_to_cpu(pnetwork->NetworkTypeInUse); - pnetwork->Configuration.ATIMWindow = - le32_to_cpu(pnetwork->Configuration.ATIMWindow); - pnetwork->Configuration.BeaconPeriod = - le32_to_cpu(pnetwork->Configuration.BeaconPeriod); - pnetwork->Configuration.DSConfig = - le32_to_cpu(pnetwork->Configuration.DSConfig); - pnetwork->Configuration.FHConfig.DwellTime = - le32_to_cpu(pnetwork->Configuration.FHConfig.DwellTime); - pnetwork->Configuration.FHConfig.HopPattern = - le32_to_cpu(pnetwork->Configuration.FHConfig.HopPattern); - pnetwork->Configuration.FHConfig.HopSet = - le32_to_cpu(pnetwork->Configuration.FHConfig.HopSet); - pnetwork->Configuration.FHConfig.Length = - le32_to_cpu(pnetwork->Configuration.FHConfig.Length); - pnetwork->Configuration.Length = - le32_to_cpu(pnetwork->Configuration.Length); - pnetwork->InfrastructureMode = - le32_to_cpu(pnetwork->InfrastructureMode); - pnetwork->IELength = le32_to_cpu(pnetwork->IELength); -#endif - len = r8712_get_wlan_bssid_ex_sz(pnetwork); - if (len > sizeof(struct wlan_bssid_ex)) - return; - spin_lock_irqsave(&pmlmepriv->lock2, flags); - /* update IBSS_network 's timestamp */ - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { - if (!memcmp(&(pmlmepriv->cur_network.network.MacAddress), - pnetwork->MacAddress, ETH_ALEN)) { - struct wlan_network *ibss_wlan = NULL; - - memcpy(pmlmepriv->cur_network.network.IEs, - pnetwork->IEs, 8); - ibss_wlan = r8712_find_network( - &pmlmepriv->scanned_queue, - pnetwork->MacAddress); - if (ibss_wlan) { - memcpy(ibss_wlan->network.IEs, - pnetwork->IEs, 8); - goto exit; - } - } - } - /* lock pmlmepriv->lock when you accessing network_q */ - if (!check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { - if (pnetwork->Ssid.Ssid[0] != 0) { - rtl8711_add_network(adapter, pnetwork); - } else { - pnetwork->Ssid.SsidLength = 8; - memcpy(pnetwork->Ssid.Ssid, "", 8); - rtl8711_add_network(adapter, pnetwork); - } - } -exit: - spin_unlock_irqrestore(&pmlmepriv->lock2, flags); -} - -void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - - spin_lock_irqsave(&pmlmepriv->lock, irqL); - - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) { - del_timer(&pmlmepriv->scan_to_timer); - - _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); - } - - if (pmlmepriv->to_join) { - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { - if (!check_fwstate(pmlmepriv, _FW_LINKED)) { - set_fwstate(pmlmepriv, _FW_UNDER_LINKING); - - if (!r8712_select_and_join_from_scan(pmlmepriv)) { - mod_timer(&pmlmepriv->assoc_timer, jiffies + - msecs_to_jiffies(MAX_JOIN_TIMEOUT)); - } else { - struct wlan_bssid_ex *pdev_network = - &(adapter->registrypriv.dev_network); - u8 *pibss = - adapter->registrypriv.dev_network.MacAddress; - pmlmepriv->fw_state ^= _FW_UNDER_SURVEY; - memcpy(&pdev_network->Ssid, - &pmlmepriv->assoc_ssid, - sizeof(struct - ndis_802_11_ssid)); - r8712_update_registrypriv_dev_network - (adapter); - r8712_generate_random_ibss(pibss); - pmlmepriv->fw_state = - WIFI_ADHOC_MASTER_STATE; - pmlmepriv->to_join = false; - } - } - } else { - pmlmepriv->to_join = false; - set_fwstate(pmlmepriv, _FW_UNDER_LINKING); - if (!r8712_select_and_join_from_scan(pmlmepriv)) - mod_timer(&pmlmepriv->assoc_timer, jiffies + - msecs_to_jiffies(MAX_JOIN_TIMEOUT)); - else - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - } - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -/* - *r8712_free_assoc_resources: the caller has to lock pmlmepriv->lock - */ -void r8712_free_assoc_resources(struct _adapter *adapter) -{ - unsigned long irqL; - struct wlan_network *pwlan = NULL; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - struct sta_priv *pstapriv = &adapter->stapriv; - struct wlan_network *tgt_network = &pmlmepriv->cur_network; - - pwlan = r8712_find_network(&pmlmepriv->scanned_queue, - tgt_network->network.MacAddress); - - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_AP_STATE)) { - struct sta_info *psta; - - psta = r8712_get_stainfo(&adapter->stapriv, - tgt_network->network.MacAddress); - - spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); - r8712_free_stainfo(adapter, psta); - spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); - } - - if (check_fwstate(pmlmepriv, - WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) - r8712_free_all_stainfo(adapter); - if (pwlan) - pwlan->fixed = false; - - if (((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) && - (adapter->stapriv.asoc_sta_count == 1))) - free_network_nolock(pmlmepriv, pwlan); -} - -/* - * r8712_indicate_connect: the caller has to lock pmlmepriv->lock - */ -void r8712_indicate_connect(struct _adapter *padapter) -{ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - pmlmepriv->to_join = false; - set_fwstate(pmlmepriv, _FW_LINKED); - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_LINK); - r8712_os_indicate_connect(padapter); - if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE) - mod_timer(&pmlmepriv->dhcp_timer, - jiffies + msecs_to_jiffies(60000)); -} - -/* - * r8712_ind_disconnect: the caller has to lock pmlmepriv->lock - */ -void r8712_ind_disconnect(struct _adapter *padapter) -{ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - _clr_fwstate_(pmlmepriv, _FW_LINKED); - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK); - r8712_os_indicate_disconnect(padapter); - } - if (padapter->pwrctrlpriv.pwr_mode != - padapter->registrypriv.power_mgnt) { - del_timer(&pmlmepriv->dhcp_timer); - r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt, - padapter->registrypriv.smart_ps); - } -} - -/*Notes: - *pnetwork : returns from r8712_joinbss_event_callback - *ptarget_wlan: found from scanned_queue - *if join_res > 0, for (fw_state==WIFI_STATION_STATE), we check if - * "ptarget_sta" & "ptarget_wlan" exist. - *if join_res > 0, for (fw_state==WIFI_ADHOC_STATE), we only check - * if "ptarget_wlan" exist. - *if join_res > 0, update "cur_network->network" from - * "pnetwork->network" if (ptarget_wlan !=NULL). - */ -void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - unsigned long irqL = 0, irqL2; - struct sta_info *ptarget_sta = NULL, *pcur_sta = NULL; - struct sta_priv *pstapriv = &adapter->stapriv; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - struct wlan_network *cur_network = &pmlmepriv->cur_network; - struct wlan_network *pcur_wlan = NULL, *ptarget_wlan = NULL; - unsigned int the_same_macaddr = false; - struct wlan_network *pnetwork; - - if (sizeof(struct list_head) == 4 * sizeof(u32)) { - pnetwork = kmalloc(sizeof(struct wlan_network), GFP_ATOMIC); - if (!pnetwork) - return; - memcpy((u8 *)pnetwork + 16, (u8 *)pbuf + 8, - sizeof(struct wlan_network) - 16); - } else { - pnetwork = (struct wlan_network *)pbuf; - } - -#ifdef __BIG_ENDIAN - /* endian_convert */ - pnetwork->join_res = le32_to_cpu(pnetwork->join_res); - pnetwork->network_type = le32_to_cpu(pnetwork->network_type); - pnetwork->network.Length = le32_to_cpu(pnetwork->network.Length); - pnetwork->network.Ssid.SsidLength = - le32_to_cpu(pnetwork->network.Ssid.SsidLength); - pnetwork->network.Privacy = le32_to_cpu(pnetwork->network.Privacy); - pnetwork->network.Rssi = le32_to_cpu(pnetwork->network.Rssi); - pnetwork->network.NetworkTypeInUse = - le32_to_cpu(pnetwork->network.NetworkTypeInUse); - pnetwork->network.Configuration.ATIMWindow = - le32_to_cpu(pnetwork->network.Configuration.ATIMWindow); - pnetwork->network.Configuration.BeaconPeriod = - le32_to_cpu(pnetwork->network.Configuration.BeaconPeriod); - pnetwork->network.Configuration.DSConfig = - le32_to_cpu(pnetwork->network.Configuration.DSConfig); - pnetwork->network.Configuration.FHConfig.DwellTime = - le32_to_cpu(pnetwork->network.Configuration.FHConfig.DwellTime); - pnetwork->network.Configuration.FHConfig.HopPattern = - le32_to_cpu(pnetwork->network.Configuration.FHConfig.HopPattern); - pnetwork->network.Configuration.FHConfig.HopSet = - le32_to_cpu(pnetwork->network.Configuration.FHConfig.HopSet); - pnetwork->network.Configuration.FHConfig.Length = - le32_to_cpu(pnetwork->network.Configuration.FHConfig.Length); - pnetwork->network.Configuration.Length = - le32_to_cpu(pnetwork->network.Configuration.Length); - pnetwork->network.InfrastructureMode = - le32_to_cpu(pnetwork->network.InfrastructureMode); - pnetwork->network.IELength = le32_to_cpu(pnetwork->network.IELength); -#endif - - the_same_macaddr = !memcmp(pnetwork->network.MacAddress, - cur_network->network.MacAddress, ETH_ALEN); - pnetwork->network.Length = - r8712_get_wlan_bssid_ex_sz(&pnetwork->network); - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (pnetwork->network.Length > sizeof(struct wlan_bssid_ex)) - goto ignore_joinbss_callback; - if (pnetwork->join_res > 0) { - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { - /*s1. find ptarget_wlan*/ - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - if (the_same_macaddr) { - ptarget_wlan = - r8712_find_network(&pmlmepriv->scanned_queue, - cur_network->network.MacAddress); - } else { - pcur_wlan = - r8712_find_network(&pmlmepriv->scanned_queue, - cur_network->network.MacAddress); - if (pcur_wlan) - pcur_wlan->fixed = false; - - pcur_sta = r8712_get_stainfo(pstapriv, - cur_network->network.MacAddress); - spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL2); - r8712_free_stainfo(adapter, pcur_sta); - spin_unlock_irqrestore(&(pstapriv->sta_hash_lock), irqL2); - - ptarget_wlan = - r8712_find_network(&pmlmepriv->scanned_queue, - pnetwork->network.MacAddress); - if (ptarget_wlan) - ptarget_wlan->fixed = true; - } - } else { - ptarget_wlan = r8712_find_network(&pmlmepriv->scanned_queue, - pnetwork->network.MacAddress); - if (ptarget_wlan) - ptarget_wlan->fixed = true; - } - - if (!ptarget_wlan) { - if (check_fwstate(pmlmepriv, - _FW_UNDER_LINKING)) - pmlmepriv->fw_state ^= - _FW_UNDER_LINKING; - goto ignore_joinbss_callback; - } - - /*s2. find ptarget_sta & update ptarget_sta*/ - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - if (the_same_macaddr) { - ptarget_sta = - r8712_get_stainfo(pstapriv, - pnetwork->network.MacAddress); - if (!ptarget_sta) - ptarget_sta = - r8712_alloc_stainfo(pstapriv, - pnetwork->network.MacAddress); - } else { - ptarget_sta = - r8712_alloc_stainfo(pstapriv, - pnetwork->network.MacAddress); - } - if (ptarget_sta) /*update ptarget_sta*/ { - ptarget_sta->aid = pnetwork->join_res; - ptarget_sta->qos_option = 1; - ptarget_sta->mac_id = 5; - if (adapter->securitypriv.AuthAlgrthm == 2) { - adapter->securitypriv.binstallGrpkey = false; - adapter->securitypriv.busetkipkey = false; - adapter->securitypriv.bgrpkey_handshake = false; - ptarget_sta->ieee8021x_blocked = true; - ptarget_sta->XPrivacy = - adapter->securitypriv.PrivacyAlgrthm; - memset((u8 *)&ptarget_sta->x_UncstKey, - 0, - sizeof(union Keytype)); - memset((u8 *)&ptarget_sta->tkiprxmickey, - 0, - sizeof(union Keytype)); - memset((u8 *)&ptarget_sta->tkiptxmickey, - 0, - sizeof(union Keytype)); - memset((u8 *)&ptarget_sta->txpn, - 0, - sizeof(union pn48)); - memset((u8 *)&ptarget_sta->rxpn, - 0, - sizeof(union pn48)); - } - } else { - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) - pmlmepriv->fw_state ^= - _FW_UNDER_LINKING; - goto ignore_joinbss_callback; - } - } - - /*s3. update cur_network & indicate connect*/ - memcpy(&cur_network->network, &pnetwork->network, - pnetwork->network.Length); - cur_network->aid = pnetwork->join_res; - /*update fw_state will clr _FW_UNDER_LINKING*/ - switch (pnetwork->network.InfrastructureMode) { - case Ndis802_11Infrastructure: - pmlmepriv->fw_state = WIFI_STATION_STATE; - break; - case Ndis802_11IBSS: - pmlmepriv->fw_state = WIFI_ADHOC_STATE; - break; - default: - pmlmepriv->fw_state = WIFI_NULL_STATE; - break; - } - r8712_update_protection(adapter, - (cur_network->network.IEs) + - sizeof(struct NDIS_802_11_FIXED_IEs), - (cur_network->network.IELength)); - /*TODO: update HT_Capability*/ - update_ht_cap(adapter, cur_network->network.IEs, - cur_network->network.IELength); - /*indicate connect*/ - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) - r8712_indicate_connect(adapter); - del_timer(&pmlmepriv->assoc_timer); - } else { - goto ignore_joinbss_callback; - } - } else { - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { - mod_timer(&pmlmepriv->assoc_timer, - jiffies + msecs_to_jiffies(1)); - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - } - } -ignore_joinbss_callback: - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - if (sizeof(struct list_head) == 4 * sizeof(u32)) - kfree(pnetwork); -} - -void r8712_stassoc_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - unsigned long irqL; - struct sta_info *psta; - struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); - struct stassoc_event *pstassoc = (struct stassoc_event *)pbuf; - - /* to do: */ - if (!r8712_access_ctrl(&adapter->acl_list, pstassoc->macaddr)) - return; - psta = r8712_get_stainfo(&adapter->stapriv, pstassoc->macaddr); - if (psta) { - /*the sta have been in sta_info_queue => do nothing - *(between drv has received this event before and - * fw have not yet to set key to CAM_ENTRY) - */ - return; - } - - psta = r8712_alloc_stainfo(&adapter->stapriv, pstassoc->macaddr); - if (!psta) - return; - /* to do : init sta_info variable */ - psta->qos_option = 0; - psta->mac_id = le32_to_cpu(pstassoc->cam_id); - /* psta->aid = (uint)pstassoc->cam_id; */ - - if (adapter->securitypriv.AuthAlgrthm == 2) - psta->XPrivacy = adapter->securitypriv.PrivacyAlgrthm; - psta->ieee8021x_blocked = false; - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { - if (adapter->stapriv.asoc_sta_count == 2) { - /* a sta + bc/mc_stainfo (not Ibss_stainfo) */ - r8712_indicate_connect(adapter); - } - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -void r8712_stadel_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - unsigned long irqL, irqL2; - struct sta_info *psta; - struct wlan_network *pwlan = NULL; - struct wlan_bssid_ex *pdev_network = NULL; - u8 *pibss = NULL; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - struct stadel_event *pstadel = (struct stadel_event *)pbuf; - struct sta_priv *pstapriv = &adapter->stapriv; - struct wlan_network *tgt_network = &pmlmepriv->cur_network; - - spin_lock_irqsave(&pmlmepriv->lock, irqL2); - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - r8712_ind_disconnect(adapter); - r8712_free_assoc_resources(adapter); - } - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | - WIFI_ADHOC_STATE)) { - psta = r8712_get_stainfo(&adapter->stapriv, pstadel->macaddr); - spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); - r8712_free_stainfo(adapter, psta); - spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); - if (adapter->stapriv.asoc_sta_count == 1) { - /*a sta + bc/mc_stainfo (not Ibss_stainfo) */ - pwlan = r8712_find_network(&pmlmepriv->scanned_queue, - tgt_network->network.MacAddress); - if (pwlan) { - pwlan->fixed = false; - free_network_nolock(pmlmepriv, pwlan); - } - /*re-create ibss*/ - pdev_network = &(adapter->registrypriv.dev_network); - pibss = adapter->registrypriv.dev_network.MacAddress; - memcpy(pdev_network, &tgt_network->network, - r8712_get_wlan_bssid_ex_sz(&tgt_network->network)); - memcpy(&pdev_network->Ssid, - &pmlmepriv->assoc_ssid, - sizeof(struct ndis_802_11_ssid)); - r8712_update_registrypriv_dev_network(adapter); - r8712_generate_random_ibss(pibss); - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { - _clr_fwstate_(pmlmepriv, WIFI_ADHOC_STATE); - set_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE); - } - } - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL2); -} - -void r8712_cpwm_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - struct reportpwrstate_parm *preportpwrstate = - (struct reportpwrstate_parm *)pbuf; - - preportpwrstate->state |= (u8)(adapter->pwrctrlpriv.cpwm_tog + 0x80); - r8712_cpwm_int_hdl(adapter, preportpwrstate); -} - -/* When the Netgear 3500 AP is with WPA2PSK-AES mode, it will send - * the ADDBA req frame with start seq control = 0 to wifi client after - * the WPA handshake and the sequence number of following data packet - * will be 0. In this case, the Rx reorder sequence is not longer than 0 - * and the WiFi client will drop the data with seq number 0. - * So, the 8712 firmware has to inform driver with receiving the - * ADDBA-Req frame so that the driver can reset the - * sequence value of Rx reorder control. - */ -void r8712_got_addbareq_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - struct ADDBA_Req_Report_parm *pAddbareq_pram = - (struct ADDBA_Req_Report_parm *)pbuf; - struct sta_info *psta; - struct sta_priv *pstapriv = &adapter->stapriv; - struct recv_reorder_ctrl *precvreorder_ctrl = NULL; - - psta = r8712_get_stainfo(pstapriv, pAddbareq_pram->MacAddress); - if (psta) { - precvreorder_ctrl = - &psta->recvreorder_ctrl[pAddbareq_pram->tid]; - /* set the indicate_seq to 0xffff so that the rx reorder - * can store any following data packet. - */ - precvreorder_ctrl->indicate_seq = 0xffff; - } -} - -void r8712_wpspbc_event_callback(struct _adapter *adapter, u8 *pbuf) -{ - if (!adapter->securitypriv.wps_hw_pbc_pressed) - adapter->securitypriv.wps_hw_pbc_pressed = true; -} - -void _r8712_sitesurvey_ctrl_handler(struct _adapter *adapter) -{ - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - struct sitesurvey_ctrl *psitesurveyctrl = &pmlmepriv->sitesurveyctrl; - struct registry_priv *pregistrypriv = &adapter->registrypriv; - u64 current_tx_pkts; - uint current_rx_pkts; - - current_tx_pkts = (adapter->xmitpriv.tx_pkts) - - (psitesurveyctrl->last_tx_pkts); - current_rx_pkts = (adapter->recvpriv.rx_pkts) - - (psitesurveyctrl->last_rx_pkts); - psitesurveyctrl->last_tx_pkts = adapter->xmitpriv.tx_pkts; - psitesurveyctrl->last_rx_pkts = adapter->recvpriv.rx_pkts; - if ((current_tx_pkts > pregistrypriv->busy_thresh) || - (current_rx_pkts > pregistrypriv->busy_thresh)) - psitesurveyctrl->traffic_busy = true; - else - psitesurveyctrl->traffic_busy = false; -} - -void _r8712_join_timeout_handler(struct _adapter *adapter) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - - if (adapter->driver_stopped || adapter->surprise_removed) - return; - spin_lock_irqsave(&pmlmepriv->lock, irqL); - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - pmlmepriv->to_join = false; - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - r8712_os_indicate_disconnect(adapter); - _clr_fwstate_(pmlmepriv, _FW_LINKED); - } - if (adapter->pwrctrlpriv.pwr_mode != adapter->registrypriv.power_mgnt) { - r8712_set_ps_mode(adapter, adapter->registrypriv.power_mgnt, - adapter->registrypriv.smart_ps); - } - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -void r8712_scan_timeout_handler (struct _adapter *adapter) -{ - unsigned long irqL; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - - spin_lock_irqsave(&pmlmepriv->lock, irqL); - _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); - pmlmepriv->to_join = false; /* scan fail, so clear to_join flag */ - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -void _r8712_dhcp_timeout_handler (struct _adapter *adapter) -{ - if (adapter->driver_stopped || adapter->surprise_removed) - return; - if (adapter->pwrctrlpriv.pwr_mode != adapter->registrypriv.power_mgnt) - r8712_set_ps_mode(adapter, adapter->registrypriv.power_mgnt, - adapter->registrypriv.smart_ps); -} - -int r8712_select_and_join_from_scan(struct mlme_priv *pmlmepriv) -{ - struct list_head *phead; - unsigned char *dst_ssid, *src_ssid; - struct _adapter *adapter; - struct __queue *queue = NULL; - struct wlan_network *pnetwork = NULL; - struct wlan_network *pnetwork_max_rssi = NULL; - - adapter = (struct _adapter *)pmlmepriv->nic_hdl; - queue = &pmlmepriv->scanned_queue; - phead = &queue->queue; - pmlmepriv->pscanned = phead->next; - while (1) { - if (end_of_queue_search(phead, pmlmepriv->pscanned)) { - if (pmlmepriv->assoc_by_rssi && pnetwork_max_rssi) { - pnetwork = pnetwork_max_rssi; - goto ask_for_joinbss; - } - return -EINVAL; - } - pnetwork = container_of(pmlmepriv->pscanned, - struct wlan_network, list); - pmlmepriv->pscanned = pmlmepriv->pscanned->next; - if (pmlmepriv->assoc_by_bssid) { - dst_ssid = pnetwork->network.MacAddress; - src_ssid = pmlmepriv->assoc_bssid; - if (!memcmp(dst_ssid, src_ssid, ETH_ALEN)) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - if (is_same_network(&pmlmepriv->cur_network.network, - &pnetwork->network)) { - _clr_fwstate_(pmlmepriv, - _FW_UNDER_LINKING); - /*r8712_indicate_connect again*/ - r8712_indicate_connect(adapter); - return 2; - } - r8712_disassoc_cmd(adapter); - r8712_ind_disconnect(adapter); - r8712_free_assoc_resources(adapter); - } - goto ask_for_joinbss; - } - } else if (pmlmepriv->assoc_ssid.SsidLength == 0) { - goto ask_for_joinbss; - } - dst_ssid = pnetwork->network.Ssid.Ssid; - src_ssid = pmlmepriv->assoc_ssid.Ssid; - if ((pnetwork->network.Ssid.SsidLength == - pmlmepriv->assoc_ssid.SsidLength) && - (!memcmp(dst_ssid, src_ssid, - pmlmepriv->assoc_ssid.SsidLength))) { - if (pmlmepriv->assoc_by_rssi) { - /* if the ssid is the same, select the bss - * which has the max rssi - */ - if (pnetwork_max_rssi) { - if (pnetwork->network.Rssi > - pnetwork_max_rssi->network.Rssi) - pnetwork_max_rssi = pnetwork; - } else { - pnetwork_max_rssi = pnetwork; - } - } else if (is_desired_network(adapter, pnetwork)) { - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - r8712_disassoc_cmd(adapter); - r8712_free_assoc_resources(adapter); - } - goto ask_for_joinbss; - } - } - } - -ask_for_joinbss: - return r8712_joinbss_cmd(adapter, pnetwork); -} - -int r8712_set_auth(struct _adapter *adapter, - struct security_priv *psecuritypriv) -{ - struct cmd_priv *pcmdpriv = &adapter->cmdpriv; - struct cmd_obj *pcmd; - struct setauth_parm *psetauthparm; - - pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC); - if (!pcmd) - return -ENOMEM; - - psetauthparm = kzalloc(sizeof(*psetauthparm), GFP_ATOMIC); - if (!psetauthparm) { - kfree(pcmd); - return -ENOMEM; - } - psetauthparm->mode = (u8)psecuritypriv->AuthAlgrthm; - pcmd->cmdcode = _SetAuth_CMD_; - pcmd->parmbuf = (unsigned char *)psetauthparm; - pcmd->cmdsz = sizeof(struct setauth_parm); - pcmd->rsp = NULL; - pcmd->rspsz = 0; - INIT_LIST_HEAD(&pcmd->list); - r8712_enqueue_cmd(pcmdpriv, pcmd); - return 0; -} - -int r8712_set_key(struct _adapter *adapter, - struct security_priv *psecuritypriv, - sint keyid) -{ - struct cmd_priv *pcmdpriv = &adapter->cmdpriv; - struct cmd_obj *pcmd; - struct setkey_parm *psetkeyparm; - u8 keylen; - int ret; - - pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC); - if (!pcmd) - return -ENOMEM; - psetkeyparm = kzalloc(sizeof(*psetkeyparm), GFP_ATOMIC); - if (!psetkeyparm) { - ret = -ENOMEM; - goto err_free_cmd; - } - if (psecuritypriv->AuthAlgrthm == 2) { /* 802.1X */ - psetkeyparm->algorithm = - (u8)psecuritypriv->XGrpPrivacy; - } else { /* WEP */ - psetkeyparm->algorithm = - (u8)psecuritypriv->PrivacyAlgrthm; - } - psetkeyparm->keyid = (u8)keyid; - - switch (psetkeyparm->algorithm) { - case _WEP40_: - keylen = 5; - memcpy(psetkeyparm->key, - psecuritypriv->DefKey[keyid].skey, keylen); - break; - case _WEP104_: - keylen = 13; - memcpy(psetkeyparm->key, - psecuritypriv->DefKey[keyid].skey, keylen); - break; - case _TKIP_: - if (keyid < 1 || keyid > 2) { - ret = -EINVAL; - goto err_free_parm; - } - keylen = 16; - memcpy(psetkeyparm->key, - &psecuritypriv->XGrpKey[keyid - 1], keylen); - psetkeyparm->grpkey = 1; - break; - case _AES_: - if (keyid < 1 || keyid > 2) { - ret = -EINVAL; - goto err_free_parm; - } - keylen = 16; - memcpy(psetkeyparm->key, - &psecuritypriv->XGrpKey[keyid - 1], keylen); - psetkeyparm->grpkey = 1; - break; - default: - ret = -EINVAL; - goto err_free_parm; - } - pcmd->cmdcode = _SetKey_CMD_; - pcmd->parmbuf = (u8 *)psetkeyparm; - pcmd->cmdsz = (sizeof(struct setkey_parm)); - pcmd->rsp = NULL; - pcmd->rspsz = 0; - INIT_LIST_HEAD(&pcmd->list); - r8712_enqueue_cmd(pcmdpriv, pcmd); - return 0; - -err_free_parm: - kfree(psetkeyparm); -err_free_cmd: - kfree(pcmd); - return ret; -} - -/* adjust IEs for r8712_joinbss_cmd in WMM */ -int r8712_restruct_wmm_ie(struct _adapter *adapter, u8 *in_ie, u8 *out_ie, - uint in_len, uint initial_out_len) -{ - unsigned int ielength = 0; - unsigned int i, j; - - i = 12; /* after the fixed IE */ - while (i < in_len) { - ielength = initial_out_len; - if (in_ie[i] == 0xDD && in_ie[i + 2] == 0x00 && - in_ie[i + 3] == 0x50 && in_ie[i + 4] == 0xF2 && - in_ie[i + 5] == 0x02 && i + 5 < in_len) { - /*WMM element ID and OUI*/ - for (j = i; j < i + 9; j++) { - out_ie[ielength] = in_ie[j]; - ielength++; - } - out_ie[initial_out_len + 1] = 0x07; - out_ie[initial_out_len + 6] = 0x00; - out_ie[initial_out_len + 8] = 0x00; - break; - } - i += (in_ie[i + 1] + 2); /* to the next IE element */ - } - return ielength; -} - -/* - * Ported from 8185: IsInPreAuthKeyList(). - * - * Search by BSSID, - * Return Value: - * -1 :if there is no pre-auth key in the table - * >=0 :if there is pre-auth key, and return the entry id - */ -static int SecIsInPMKIDList(struct _adapter *Adapter, u8 *bssid) -{ - struct security_priv *p = &Adapter->securitypriv; - int i; - - for (i = 0; i < NUM_PMKID_CACHE; i++) - if (p->PMKIDList[i].bUsed && !memcmp(p->PMKIDList[i].Bssid, bssid, ETH_ALEN)) - return i; - return -1; -} - -sint r8712_restruct_sec_ie(struct _adapter *adapter, u8 *in_ie, - u8 *out_ie, uint in_len) -{ - u8 authmode = 0, match; - u8 sec_ie[IW_CUSTOM_MAX], uncst_oui[4], bkup_ie[255]; - u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; - uint ielength, cnt, remove_cnt; - int iEntry; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - struct security_priv *psecuritypriv = &adapter->securitypriv; - uint ndisauthmode = psecuritypriv->ndisauthtype; - uint ndissecuritytype = psecuritypriv->ndisencryptstatus; - - if ((ndisauthmode == Ndis802_11AuthModeWPA) || - (ndisauthmode == Ndis802_11AuthModeWPAPSK)) { - authmode = _WPA_IE_ID_; - uncst_oui[0] = 0x0; - uncst_oui[1] = 0x50; - uncst_oui[2] = 0xf2; - } - if ((ndisauthmode == Ndis802_11AuthModeWPA2) || - (ndisauthmode == Ndis802_11AuthModeWPA2PSK)) { - authmode = _WPA2_IE_ID_; - uncst_oui[0] = 0x0; - uncst_oui[1] = 0x0f; - uncst_oui[2] = 0xac; - } - switch (ndissecuritytype) { - case Ndis802_11Encryption1Enabled: - case Ndis802_11Encryption1KeyAbsent: - uncst_oui[3] = 0x1; - break; - case Ndis802_11Encryption2Enabled: - case Ndis802_11Encryption2KeyAbsent: - uncst_oui[3] = 0x2; - break; - case Ndis802_11Encryption3Enabled: - case Ndis802_11Encryption3KeyAbsent: - uncst_oui[3] = 0x4; - break; - default: - break; - } - /*Search required WPA or WPA2 IE and copy to sec_ie[] */ - cnt = 12; - match = false; - while (cnt < in_len) { - if (in_ie[cnt] == authmode) { - if ((authmode == _WPA_IE_ID_) && - (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { - memcpy(&sec_ie[0], &in_ie[cnt], - in_ie[cnt + 1] + 2); - match = true; - break; - } - if (authmode == _WPA2_IE_ID_) { - memcpy(&sec_ie[0], &in_ie[cnt], - in_ie[cnt + 1] + 2); - match = true; - break; - } - if (((authmode == _WPA_IE_ID_) && - (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) || - (authmode == _WPA2_IE_ID_)) - memcpy(&bkup_ie[0], &in_ie[cnt], - in_ie[cnt + 1] + 2); - } - cnt += in_ie[cnt + 1] + 2; /*get next*/ - } - /*restruct WPA IE or WPA2 IE in sec_ie[] */ - if (match) { - if (sec_ie[0] == _WPA_IE_ID_) { - /* parsing SSN IE to select required encryption - * algorithm, and set the bc/mc encryption algorithm - */ - while (true) { - /*check wpa_oui tag*/ - if (memcmp(&sec_ie[2], &wpa_oui[0], 4)) { - match = false; - break; - } - if ((sec_ie[6] != 0x01) || (sec_ie[7] != 0x0)) { - /*IE Ver error*/ - match = false; - break; - } - if (!memcmp(&sec_ie[8], &wpa_oui[0], 3)) { - /* get bc/mc encryption type (group - * key type) - */ - switch (sec_ie[11]) { - case 0x0: /*none*/ - psecuritypriv->XGrpPrivacy = - _NO_PRIVACY_; - break; - case 0x1: /*WEP_40*/ - psecuritypriv->XGrpPrivacy = - _WEP40_; - break; - case 0x2: /*TKIP*/ - psecuritypriv->XGrpPrivacy = - _TKIP_; - break; - case 0x3: /*AESCCMP*/ - case 0x4: - psecuritypriv->XGrpPrivacy = - _AES_; - break; - case 0x5: /*WEP_104*/ - psecuritypriv->XGrpPrivacy = - _WEP104_; - break; - } - } else { - match = false; - break; - } - if (sec_ie[12] == 0x01) { - /*check the unicast encryption type*/ - if (memcmp(&sec_ie[14], - &uncst_oui[0], 4)) { - match = false; - break; - - } /*else the uncst_oui is match*/ - } else { /*mixed mode, unicast_enc_type > 1*/ - /*select the uncst_oui and remove - * the other uncst_oui - */ - cnt = sec_ie[12]; - remove_cnt = (cnt - 1) * 4; - sec_ie[12] = 0x01; - memcpy(&sec_ie[14], &uncst_oui[0], 4); - /*remove the other unicast suit*/ - memcpy(&sec_ie[18], - &sec_ie[18 + remove_cnt], - sec_ie[1] - 18 + 2 - - remove_cnt); - sec_ie[1] = sec_ie[1] - remove_cnt; - } - break; - } - } - if (authmode == _WPA2_IE_ID_) { - /* parsing RSN IE to select required encryption - * algorithm, and set the bc/mc encryption algorithm - */ - while (true) { - if ((sec_ie[2] != 0x01) || (sec_ie[3] != 0x0)) { - /*IE Ver error*/ - match = false; - break; - } - if (!memcmp(&sec_ie[4], &uncst_oui[0], 3)) { - /*get bc/mc encryption type*/ - switch (sec_ie[7]) { - case 0x1: /*WEP_40*/ - psecuritypriv->XGrpPrivacy = - _WEP40_; - break; - case 0x2: /*TKIP*/ - psecuritypriv->XGrpPrivacy = - _TKIP_; - break; - case 0x4: /*AESWRAP*/ - psecuritypriv->XGrpPrivacy = - _AES_; - break; - case 0x5: /*WEP_104*/ - psecuritypriv->XGrpPrivacy = - _WEP104_; - break; - default: /*one*/ - psecuritypriv->XGrpPrivacy = - _NO_PRIVACY_; - break; - } - } else { - match = false; - break; - } - if (sec_ie[8] == 0x01) { - /*check the unicast encryption type*/ - if (memcmp(&sec_ie[10], - &uncst_oui[0], 4)) { - match = false; - break; - } /*else the uncst_oui is match*/ - } else { /*mixed mode, unicast_enc_type > 1*/ - /*select the uncst_oui and remove the - * other uncst_oui - */ - cnt = sec_ie[8]; - remove_cnt = (cnt - 1) * 4; - sec_ie[8] = 0x01; - memcpy(&sec_ie[10], &uncst_oui[0], 4); - /*remove the other unicast suit*/ - memcpy(&sec_ie[14], - &sec_ie[14 + remove_cnt], - (sec_ie[1] - 14 + 2 - - remove_cnt)); - sec_ie[1] = sec_ie[1] - remove_cnt; - } - break; - } - } - } - if ((authmode == _WPA_IE_ID_) || (authmode == _WPA2_IE_ID_)) { - /*copy fixed ie*/ - memcpy(out_ie, in_ie, 12); - ielength = 12; - /*copy RSN or SSN*/ - if (match) { - memcpy(&out_ie[ielength], &sec_ie[0], sec_ie[1] + 2); - ielength += sec_ie[1] + 2; - if (authmode == _WPA2_IE_ID_) { - /*the Pre-Authentication bit should be zero*/ - out_ie[ielength - 1] = 0; - out_ie[ielength - 2] = 0; - } - r8712_report_sec_ie(adapter, authmode, sec_ie); - } - } else { - /*copy fixed ie only*/ - memcpy(out_ie, in_ie, 12); - ielength = 12; - if (psecuritypriv->wps_phase) { - memcpy(out_ie + ielength, psecuritypriv->wps_ie, - psecuritypriv->wps_ie_len); - ielength += psecuritypriv->wps_ie_len; - } - } - iEntry = SecIsInPMKIDList(adapter, pmlmepriv->assoc_bssid); - if (iEntry < 0) - return ielength; - if (authmode == _WPA2_IE_ID_) { - out_ie[ielength] = 1; - ielength++; - out_ie[ielength] = 0; /*PMKID count = 0x0100*/ - ielength++; - memcpy(&out_ie[ielength], - &psecuritypriv->PMKIDList[iEntry].PMKID, 16); - ielength += 16; - out_ie[13] += 18;/*PMKID length = 2+16*/ - } - return ielength; -} - -void r8712_init_registrypriv_dev_network(struct _adapter *adapter) -{ - struct registry_priv *pregistrypriv = &adapter->registrypriv; - struct eeprom_priv *peepriv = &adapter->eeprompriv; - struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; - u8 *myhwaddr = myid(peepriv); - - memcpy(pdev_network->MacAddress, myhwaddr, ETH_ALEN); - memcpy(&pdev_network->Ssid, &pregistrypriv->ssid, - sizeof(struct ndis_802_11_ssid)); - pdev_network->Configuration.Length = - sizeof(struct NDIS_802_11_CONFIGURATION); - pdev_network->Configuration.BeaconPeriod = 100; - pdev_network->Configuration.FHConfig.Length = 0; - pdev_network->Configuration.FHConfig.HopPattern = 0; - pdev_network->Configuration.FHConfig.HopSet = 0; - pdev_network->Configuration.FHConfig.DwellTime = 0; -} - -void r8712_update_registrypriv_dev_network(struct _adapter *adapter) -{ - int sz = 0; - struct registry_priv *pregistrypriv = &adapter->registrypriv; - struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; - struct security_priv *psecuritypriv = &adapter->securitypriv; - struct wlan_network *cur_network = &adapter->mlmepriv.cur_network; - - pdev_network->Privacy = cpu_to_le32(psecuritypriv->PrivacyAlgrthm - > 0 ? 1 : 0); /* adhoc no 802.1x */ - pdev_network->Rssi = 0; - switch (pregistrypriv->wireless_mode) { - case WIRELESS_11B: - pdev_network->NetworkTypeInUse = Ndis802_11DS; - break; - case WIRELESS_11G: - case WIRELESS_11BG: - pdev_network->NetworkTypeInUse = Ndis802_11OFDM24; - break; - case WIRELESS_11A: - pdev_network->NetworkTypeInUse = Ndis802_11OFDM5; - break; - default: - /* TODO */ - break; - } - pdev_network->Configuration.DSConfig = pregistrypriv->channel; - if (cur_network->network.InfrastructureMode == Ndis802_11IBSS) - pdev_network->Configuration.ATIMWindow = 3; - pdev_network->InfrastructureMode = cur_network->network.InfrastructureMode; - /* 1. Supported rates - * 2. IE - */ - sz = r8712_generate_ie(pregistrypriv); - pdev_network->IELength = sz; - pdev_network->Length = r8712_get_wlan_bssid_ex_sz(pdev_network); -} - -/*the function is at passive_level*/ -void r8712_joinbss_reset(struct _adapter *padapter) -{ - int i; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - - /* todo: if you want to do something io/reg/hw setting before join_bss, - * please add code here - */ - phtpriv->ampdu_enable = false;/*reset to disabled*/ - for (i = 0; i < 16; i++) - phtpriv->baddbareq_issued[i] = false;/*reset it*/ - if (phtpriv->ht_option) { - /* validate usb rx aggregation */ - r8712_write8(padapter, 0x102500D9, 48);/*TH = 48 pages, 6k*/ - } else { - /* invalidate usb rx aggregation */ - /* TH=1 => means that invalidate usb rx aggregation */ - r8712_write8(padapter, 0x102500D9, 1); - } -} - -/*the function is >= passive_level*/ -unsigned int r8712_restructure_ht_ie(struct _adapter *padapter, u8 *in_ie, - u8 *out_ie, uint in_len, uint *pout_len) -{ - u32 ielen, out_len; - unsigned char *p; - struct ieee80211_ht_cap ht_capie; - unsigned char WMM_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00}; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct qos_priv *pqospriv = &pmlmepriv->qospriv; - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - - phtpriv->ht_option = 0; - p = r8712_get_ie(in_ie + 12, WLAN_EID_HT_CAPABILITY, &ielen, in_len - 12); - if (p && (ielen > 0)) { - if (pqospriv->qos_option == 0) { - out_len = *pout_len; - r8712_set_ie(out_ie + out_len, WLAN_EID_VENDOR_SPECIFIC, - _WMM_IE_Length_, WMM_IE, pout_len); - pqospriv->qos_option = 1; - } - out_len = *pout_len; - memset(&ht_capie, 0, sizeof(struct ieee80211_ht_cap)); - ht_capie.cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_TX_STBC | - IEEE80211_HT_CAP_MAX_AMSDU | - IEEE80211_HT_CAP_DSSSCCK40); - ht_capie.ampdu_params_info = (IEEE80211_HT_AMPDU_PARM_FACTOR & - 0x03) | (IEEE80211_HT_AMPDU_PARM_DENSITY & 0x00); - r8712_set_ie(out_ie + out_len, WLAN_EID_HT_CAPABILITY, - sizeof(struct ieee80211_ht_cap), - (unsigned char *)&ht_capie, pout_len); - phtpriv->ht_option = 1; - } - return phtpriv->ht_option; -} - -/* the function is > passive_level (in critical_section) */ -static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len) -{ - u8 *p, max_ampdu_sz; - int i; - uint len; - struct sta_info *bmc_sta, *psta; - struct ieee80211_ht_cap *pht_capie; - struct recv_reorder_ctrl *preorder_ctrl; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - struct registry_priv *pregistrypriv = &padapter->registrypriv; - struct wlan_network *pcur_network = &(pmlmepriv->cur_network); - - if (!phtpriv->ht_option) - return; - /* maybe needs check if ap supports rx ampdu. */ - if (!phtpriv->ampdu_enable && - (pregistrypriv->ampdu_enable == 1)) - phtpriv->ampdu_enable = true; - /*check Max Rx A-MPDU Size*/ - len = 0; - p = r8712_get_ie(pie + sizeof(struct NDIS_802_11_FIXED_IEs), - WLAN_EID_HT_CAPABILITY, - &len, ie_len - - sizeof(struct NDIS_802_11_FIXED_IEs)); - if (p && len > 0) { - pht_capie = (struct ieee80211_ht_cap *)(p + 2); - max_ampdu_sz = (pht_capie->ampdu_params_info & - IEEE80211_HT_AMPDU_PARM_FACTOR); - /* max_ampdu_sz (kbytes); */ - max_ampdu_sz = 1 << (max_ampdu_sz + 3); - phtpriv->rx_ampdu_maxlen = max_ampdu_sz; - } - /* for A-MPDU Rx reordering buffer control for bmc_sta & sta_info - * if A-MPDU Rx is enabled, resetting rx_ordering_ctrl - * wstart_b(indicate_seq) to default value=0xffff - * todo: check if AP can send A-MPDU packets - */ - bmc_sta = r8712_get_bcmc_stainfo(padapter); - if (bmc_sta) { - for (i = 0; i < 16; i++) { - preorder_ctrl = &bmc_sta->recvreorder_ctrl[i]; - preorder_ctrl->indicate_seq = 0xffff; - preorder_ctrl->wend_b = 0xffff; - } - } - psta = r8712_get_stainfo(&padapter->stapriv, - pcur_network->network.MacAddress); - if (psta) { - for (i = 0; i < 16; i++) { - preorder_ctrl = &psta->recvreorder_ctrl[i]; - preorder_ctrl->indicate_seq = 0xffff; - preorder_ctrl->wend_b = 0xffff; - } - } - len = 0; - p = r8712_get_ie(pie + sizeof(struct NDIS_802_11_FIXED_IEs), - WLAN_EID_HT_OPERATION, &len, - ie_len - sizeof(struct NDIS_802_11_FIXED_IEs)); -} - -void r8712_issue_addbareq_cmd(struct _adapter *padapter, int priority) -{ - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct ht_priv *phtpriv = &pmlmepriv->htpriv; - - if ((phtpriv->ht_option == 1) && (phtpriv->ampdu_enable)) { - if (!phtpriv->baddbareq_issued[priority]) { - r8712_addbareq_cmd(padapter, (u8)priority); - phtpriv->baddbareq_issued[priority] = true; - } - } -} diff --git a/drivers/staging/rtl8712/rtl871x_mlme.h b/drivers/staging/rtl8712/rtl871x_mlme.h deleted file mode 100644 index d7d25f240111f..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mlme.h +++ /dev/null @@ -1,205 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_MLME_H_ -#define __RTL871X_MLME_H_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "wlan_bssdef.h" - -#define MAX_BSS_CNT 64 -#define MAX_JOIN_TIMEOUT 6000 - -#define SCANNING_TIMEOUT 4500 - -#define SCANQUEUE_LIFETIME 20 /* unit:sec */ - -#define WIFI_NULL_STATE 0x00000000 -#define WIFI_ASOC_STATE 0x00000001 /* Under Linked state...*/ -#define WIFI_REASOC_STATE 0x00000002 -#define WIFI_SLEEP_STATE 0x00000004 -#define WIFI_STATION_STATE 0x00000008 -#define WIFI_AP_STATE 0x00000010 -#define WIFI_ADHOC_STATE 0x00000020 -#define WIFI_ADHOC_MASTER_STATE 0x00000040 -#define WIFI_UNDER_LINKING 0x00000080 -#define WIFI_SITE_MONITOR 0x00000800 /* to indicate the station - * is under site surveying - */ -#define WIFI_MP_STATE 0x00010000 -#define WIFI_MP_CTX_BACKGROUND 0x00020000 /* in cont. tx background*/ -#define WIFI_MP_CTX_ST 0x00040000 /* in cont. tx with - * single-tone - */ -#define WIFI_MP_CTX_BACKGROUND_PENDING 0x00080000 /* pending in cont, tx - * background due - * to out of skb - */ -#define WIFI_MP_CTX_CCK_HW 0x00100000 /* in continuous tx*/ -#define WIFI_MP_CTX_CCK_CS 0x00200000 /* in cont, tx with carrier - * suppression - */ -#define WIFI_MP_LPBK_STATE 0x00400000 - -#define _FW_UNDER_LINKING WIFI_UNDER_LINKING -#define _FW_LINKED WIFI_ASOC_STATE -#define _FW_UNDER_SURVEY WIFI_SITE_MONITOR - -/* - * there are several "locks" in mlme_priv, - * since mlme_priv is a shared resource between many threads, - * like ISR/Call-Back functions, the OID handlers, and even timer functions. - * Each _queue has its own locks, already. - * Other items are protected by mlme_priv.lock. - * To avoid possible dead lock, any thread trying to modify mlme_priv - * SHALL not lock up more than one lock at a time! - */ - -#define traffic_threshold 10 -#define traffic_scan_period 500 - -struct sitesurvey_ctrl { - u64 last_tx_pkts; - uint last_rx_pkts; - sint traffic_busy; - struct timer_list sitesurvey_ctrl_timer; -}; - -struct mlme_priv { - spinlock_t lock; - spinlock_t lock2; - sint fw_state; /*shall we protect this variable? */ - u8 to_join; /*flag*/ - u8 *nic_hdl; - struct list_head *pscanned; - struct __queue free_bss_pool; - struct __queue scanned_queue; - u8 *free_bss_buf; - unsigned long num_of_scanned; - u8 passive_mode; /*add for Android's SCAN-ACTIVE/SCAN-PASSIVE */ - struct ndis_802_11_ssid assoc_ssid; - u8 assoc_bssid[6]; - struct wlan_network cur_network; - struct sitesurvey_ctrl sitesurveyctrl; - struct timer_list assoc_timer; - uint assoc_by_bssid; - uint assoc_by_rssi; - struct timer_list scan_to_timer; /* driver handles scan_timeout.*/ - struct timer_list dhcp_timer; /* set dhcp to if driver in ps mode.*/ - struct qos_priv qospriv; - struct ht_priv htpriv; - struct timer_list wdg_timer; /*watchdog periodic timer*/ -}; - -static inline u8 *get_bssid(struct mlme_priv *pmlmepriv) -{ - return pmlmepriv->cur_network.network.MacAddress; -} - -static inline u8 check_fwstate(struct mlme_priv *pmlmepriv, sint state) -{ - if (pmlmepriv->fw_state & state) - return true; - return false; -} - -static inline sint get_fwstate(struct mlme_priv *pmlmepriv) -{ - return pmlmepriv->fw_state; -} - -/* - * No Limit on the calling context, - * therefore set it to be the critical section... - * - * ### NOTE:#### (!!!!) - * TAKE CARE BEFORE CALLING THIS FUNC, LOCK pmlmepriv->lock - */ -static inline void set_fwstate(struct mlme_priv *pmlmepriv, sint state) -{ - pmlmepriv->fw_state |= state; -} - -static inline void _clr_fwstate_(struct mlme_priv *pmlmepriv, sint state) -{ - pmlmepriv->fw_state &= ~state; -} - -/* - * No Limit on the calling context, - * therefore set it to be the critical section... - */ -static inline void clr_fwstate(struct mlme_priv *pmlmepriv, sint state) -{ - unsigned long irqL; - - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, state)) - pmlmepriv->fw_state ^= state; - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -static inline void set_scanned_network_val(struct mlme_priv *pmlmepriv, - sint val) -{ - unsigned long irqL; - - spin_lock_irqsave(&pmlmepriv->lock, irqL); - pmlmepriv->num_of_scanned = val; - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); -} - -void r8712_survey_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_stassoc_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_stadel_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_atimdone_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_cpwm_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_wpspbc_event_callback(struct _adapter *adapter, u8 *pbuf); -void r8712_free_network_queue(struct _adapter *adapter); -int r8712_init_mlme_priv(struct _adapter *adapter); -void r8712_free_mlme_priv(struct mlme_priv *pmlmepriv); -int r8712_select_and_join_from_scan(struct mlme_priv *pmlmepriv); -int r8712_set_key(struct _adapter *adapter, - struct security_priv *psecuritypriv, sint keyid); -int r8712_set_auth(struct _adapter *adapter, - struct security_priv *psecuritypriv); -uint r8712_get_wlan_bssid_ex_sz(struct wlan_bssid_ex *bss); -void r8712_generate_random_ibss(u8 *pibss); -u8 *r8712_get_capability_from_ie(u8 *ie); -struct wlan_network *r8712_get_oldest_wlan_network( - struct __queue *scanned_queue); -void r8712_free_assoc_resources(struct _adapter *adapter); -void r8712_ind_disconnect(struct _adapter *adapter); -void r8712_indicate_connect(struct _adapter *adapter); -int r8712_restruct_sec_ie(struct _adapter *adapter, u8 *in_ie, - u8 *out_ie, uint in_len); -int r8712_restruct_wmm_ie(struct _adapter *adapter, u8 *in_ie, - u8 *out_ie, uint in_len, uint initial_out_len); -void r8712_init_registrypriv_dev_network(struct _adapter *adapter); -void r8712_update_registrypriv_dev_network(struct _adapter *adapter); -void _r8712_sitesurvey_ctrl_handler(struct _adapter *adapter); -void _r8712_join_timeout_handler(struct _adapter *adapter); -void r8712_scan_timeout_handler(struct _adapter *adapter); -void _r8712_dhcp_timeout_handler(struct _adapter *adapter); -struct wlan_network *_r8712_alloc_network(struct mlme_priv *pmlmepriv); -sint r8712_if_up(struct _adapter *padapter); -void r8712_joinbss_reset(struct _adapter *padapter); -unsigned int r8712_restructure_ht_ie(struct _adapter *padapter, u8 *in_ie, - u8 *out_ie, uint in_len, uint *pout_len); -void r8712_issue_addbareq_cmd(struct _adapter *padapter, int priority); -int r8712_is_same_ibss(struct _adapter *adapter, struct wlan_network *pnetwork); - -#endif /*__RTL871X_MLME_H_*/ diff --git a/drivers/staging/rtl8712/rtl871x_mp.c b/drivers/staging/rtl8712/rtl871x_mp.c deleted file mode 100644 index 099c512c8519f..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mp.c +++ /dev/null @@ -1,724 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#define _RTL871X_MP_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "rtl871x_mp_phy_regdef.h" -#include "rtl8712_cmd.h" - -static void _init_mp_priv_(struct mp_priv *pmp_priv) -{ - pmp_priv->mode = _LOOPBOOK_MODE_; - pmp_priv->curr_ch = 1; - pmp_priv->curr_modem = MIXED_PHY; - pmp_priv->curr_rateidx = 0; - pmp_priv->curr_txpoweridx = 0x14; - pmp_priv->antenna_tx = ANTENNA_A; - pmp_priv->antenna_rx = ANTENNA_AB; - pmp_priv->check_mp_pkt = 0; - pmp_priv->tx_pktcount = 0; - pmp_priv->rx_pktcount = 0; - pmp_priv->rx_crcerrpktcount = 0; -} - -static int init_mp_priv(struct mp_priv *pmp_priv) -{ - int i; - struct mp_xmit_frame *pmp_xmitframe; - - _init_mp_priv_(pmp_priv); - _init_queue(&pmp_priv->free_mp_xmitqueue); - pmp_priv->pallocated_mp_xmitframe_buf = NULL; - pmp_priv->pallocated_mp_xmitframe_buf = kmalloc(NR_MP_XMITFRAME * - sizeof(struct mp_xmit_frame) + 4, - GFP_ATOMIC); - if (!pmp_priv->pallocated_mp_xmitframe_buf) - return -ENOMEM; - - pmp_priv->pmp_xmtframe_buf = pmp_priv->pallocated_mp_xmitframe_buf + - 4 - - ((addr_t)(pmp_priv->pallocated_mp_xmitframe_buf) & 3); - pmp_xmitframe = (struct mp_xmit_frame *)pmp_priv->pmp_xmtframe_buf; - for (i = 0; i < NR_MP_XMITFRAME; i++) { - INIT_LIST_HEAD(&(pmp_xmitframe->list)); - list_add_tail(&(pmp_xmitframe->list), - &(pmp_priv->free_mp_xmitqueue.queue)); - pmp_xmitframe->pkt = NULL; - pmp_xmitframe->frame_tag = MP_FRAMETAG; - pmp_xmitframe->padapter = pmp_priv->papdater; - pmp_xmitframe++; - } - pmp_priv->free_mp_xmitframe_cnt = NR_MP_XMITFRAME; - return 0; -} - -static int free_mp_priv(struct mp_priv *pmp_priv) -{ - kfree(pmp_priv->pallocated_mp_xmitframe_buf); - return 0; -} - -void mp871xinit(struct _adapter *padapter) -{ - struct mp_priv *pmppriv = &padapter->mppriv; - - pmppriv->papdater = padapter; - init_mp_priv(pmppriv); -} - -void mp871xdeinit(struct _adapter *padapter) -{ - struct mp_priv *pmppriv = &padapter->mppriv; - - free_mp_priv(pmppriv); -} - -/* - * Special for bb and rf reg read/write - */ -static u32 fw_iocmd_read(struct _adapter *pAdapter, struct IOCMD_STRUCT iocmd) -{ - u32 cmd32 = 0, val32 = 0; - u8 iocmd_class = iocmd.cmdclass; - u16 iocmd_value = iocmd.value; - u8 iocmd_idx = iocmd.index; - - cmd32 = (iocmd_class << 24) | (iocmd_value << 8) | iocmd_idx; - if (r8712_fw_cmd(pAdapter, cmd32)) - r8712_fw_cmd_data(pAdapter, &val32, 1); - else - val32 = 0; - return val32; -} - -static u8 fw_iocmd_write(struct _adapter *pAdapter, - struct IOCMD_STRUCT iocmd, u32 value) -{ - u32 cmd32 = 0; - u8 iocmd_class = iocmd.cmdclass; - u32 iocmd_value = iocmd.value; - u8 iocmd_idx = iocmd.index; - - r8712_fw_cmd_data(pAdapter, &value, 0); - msleep(100); - cmd32 = (iocmd_class << 24) | (iocmd_value << 8) | iocmd_idx; - return r8712_fw_cmd(pAdapter, cmd32); -} - -/* offset : 0X800~0XFFF */ -u32 r8712_bb_reg_read(struct _adapter *pAdapter, u16 offset) -{ - u8 shift = offset & 0x0003; /* 4 byte access */ - u16 bb_addr = offset & 0x0FFC; /* 4 byte access */ - u32 bb_val = 0; - struct IOCMD_STRUCT iocmd; - - iocmd.cmdclass = IOCMD_CLASS_BB_RF; - iocmd.value = bb_addr; - iocmd.index = IOCMD_BB_READ_IDX; - bb_val = fw_iocmd_read(pAdapter, iocmd); - if (shift != 0) { - u32 bb_val2 = 0; - - bb_val >>= (shift * 8); - iocmd.value += 4; - bb_val2 = fw_iocmd_read(pAdapter, iocmd); - bb_val2 <<= ((4 - shift) * 8); - bb_val |= bb_val2; - } - return bb_val; -} - -/* offset : 0X800~0XFFF */ -u8 r8712_bb_reg_write(struct _adapter *pAdapter, u16 offset, u32 value) -{ - u8 shift = offset & 0x0003; /* 4 byte access */ - u16 bb_addr = offset & 0x0FFC; /* 4 byte access */ - struct IOCMD_STRUCT iocmd; - - iocmd.cmdclass = IOCMD_CLASS_BB_RF; - iocmd.value = bb_addr; - iocmd.index = IOCMD_BB_WRITE_IDX; - if (shift != 0) { - u32 oldValue = 0; - u32 newValue = value; - - oldValue = r8712_bb_reg_read(pAdapter, iocmd.value); - oldValue &= (0xFFFFFFFF >> ((4 - shift) * 8)); - value = oldValue | (newValue << (shift * 8)); - if (!fw_iocmd_write(pAdapter, iocmd, value)) - return false; - iocmd.value += 4; - oldValue = r8712_bb_reg_read(pAdapter, iocmd.value); - oldValue &= (0xFFFFFFFF << (shift * 8)); - value = oldValue | (newValue >> ((4 - shift) * 8)); - } - return fw_iocmd_write(pAdapter, iocmd, value); -} - -/* offset : 0x00 ~ 0xFF */ -u32 r8712_rf_reg_read(struct _adapter *pAdapter, u8 path, u8 offset) -{ - u16 rf_addr = (path << 8) | offset; - struct IOCMD_STRUCT iocmd; - - iocmd.cmdclass = IOCMD_CLASS_BB_RF; - iocmd.value = rf_addr; - iocmd.index = IOCMD_RF_READ_IDX; - return fw_iocmd_read(pAdapter, iocmd); -} - -u8 r8712_rf_reg_write(struct _adapter *pAdapter, u8 path, u8 offset, u32 value) -{ - u16 rf_addr = (path << 8) | offset; - struct IOCMD_STRUCT iocmd; - - iocmd.cmdclass = IOCMD_CLASS_BB_RF; - iocmd.value = rf_addr; - iocmd.index = IOCMD_RF_WRIT_IDX; - return fw_iocmd_write(pAdapter, iocmd, value); -} - -static u32 bitshift(u32 bitmask) -{ - u32 i; - - for (i = 0; i <= 31; i++) - if (((bitmask >> i) & 0x1) == 1) - break; - return i; -} - -static u32 get_bb_reg(struct _adapter *pAdapter, u16 offset, u32 bitmask) -{ - u32 org_value, bit_shift; - - org_value = r8712_bb_reg_read(pAdapter, offset); - bit_shift = bitshift(bitmask); - return (org_value & bitmask) >> bit_shift; -} - -static u8 set_bb_reg(struct _adapter *pAdapter, - u16 offset, - u32 bitmask, - u32 value) -{ - u32 org_value, bit_shift, new_value; - - if (bitmask != bMaskDWord) { - org_value = r8712_bb_reg_read(pAdapter, offset); - bit_shift = bitshift(bitmask); - new_value = (org_value & (~bitmask)) | (value << bit_shift); - } else { - new_value = value; - } - return r8712_bb_reg_write(pAdapter, offset, new_value); -} - -static u32 get_rf_reg(struct _adapter *pAdapter, u8 path, u8 offset, - u32 bitmask) -{ - u32 org_value, bit_shift; - - org_value = r8712_rf_reg_read(pAdapter, path, offset); - bit_shift = bitshift(bitmask); - return (org_value & bitmask) >> bit_shift; -} - -static u8 set_rf_reg(struct _adapter *pAdapter, u8 path, u8 offset, u32 bitmask, - u32 value) -{ - u32 org_value, bit_shift, new_value; - - if (bitmask != bMaskDWord) { - org_value = r8712_rf_reg_read(pAdapter, path, offset); - bit_shift = bitshift(bitmask); - new_value = (org_value & (~bitmask)) | (value << bit_shift); - } else { - new_value = value; - } - return r8712_rf_reg_write(pAdapter, path, offset, new_value); -} - -/* - * SetChannel - * Description - * Use H2C command to change channel, - * not only modify rf register, but also other setting need to be done. - */ -void r8712_SetChannel(struct _adapter *pAdapter) -{ - struct cmd_priv *pcmdpriv = &pAdapter->cmdpriv; - struct cmd_obj *pcmd = NULL; - struct SetChannel_parm *pparm = NULL; - u16 code = GEN_CMD_CODE(_SetChannel); - - pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC); - if (!pcmd) - return; - pparm = kmalloc(sizeof(*pparm), GFP_ATOMIC); - if (!pparm) { - kfree(pcmd); - return; - } - pparm->curr_ch = pAdapter->mppriv.curr_ch; - init_h2fwcmd_w_parm_no_rsp(pcmd, pparm, code); - r8712_enqueue_cmd(pcmdpriv, pcmd); -} - -static void SetCCKTxPower(struct _adapter *pAdapter, u8 TxPower) -{ - u16 TxAGC = 0; - - TxAGC = TxPower; - set_bb_reg(pAdapter, rTxAGC_CCK_Mcs32, bTxAGCRateCCK, TxAGC); -} - -static void SetOFDMTxPower(struct _adapter *pAdapter, u8 TxPower) -{ - u32 TxAGC = 0; - - TxAGC |= ((TxPower << 24) | (TxPower << 16) | (TxPower << 8) | - TxPower); - set_bb_reg(pAdapter, rTxAGC_Rate18_06, bTxAGCRate18_06, TxAGC); - set_bb_reg(pAdapter, rTxAGC_Rate54_24, bTxAGCRate54_24, TxAGC); - set_bb_reg(pAdapter, rTxAGC_Mcs03_Mcs00, bTxAGCRateMCS3_MCS0, TxAGC); - set_bb_reg(pAdapter, rTxAGC_Mcs07_Mcs04, bTxAGCRateMCS7_MCS4, TxAGC); - set_bb_reg(pAdapter, rTxAGC_Mcs11_Mcs08, bTxAGCRateMCS11_MCS8, TxAGC); - set_bb_reg(pAdapter, rTxAGC_Mcs15_Mcs12, bTxAGCRateMCS15_MCS12, TxAGC); -} - -void r8712_SetTxPower(struct _adapter *pAdapter) -{ - u8 TxPower = pAdapter->mppriv.curr_txpoweridx; - - SetCCKTxPower(pAdapter, TxPower); - SetOFDMTxPower(pAdapter, TxPower); -} - -void r8712_SetTxAGCOffset(struct _adapter *pAdapter, u32 ulTxAGCOffset) -{ - u32 TxAGCOffset_B, TxAGCOffset_C, TxAGCOffset_D, tmpAGC; - - TxAGCOffset_B = ulTxAGCOffset & 0x000000ff; - TxAGCOffset_C = (ulTxAGCOffset & 0x0000ff00) >> 8; - TxAGCOffset_D = (ulTxAGCOffset & 0x00ff0000) >> 16; - tmpAGC = TxAGCOffset_D << 8 | TxAGCOffset_C << 4 | TxAGCOffset_B; - set_bb_reg(pAdapter, rFPGA0_TxGainStage, - (bXBTxAGC | bXCTxAGC | bXDTxAGC), tmpAGC); -} - -void r8712_SetDataRate(struct _adapter *pAdapter) -{ - u8 path = RF_PATH_A; - u8 offset = RF_SYN_G2; - u32 value; - - value = (pAdapter->mppriv.curr_rateidx < 4) ? 0x4440 : 0xF200; - r8712_rf_reg_write(pAdapter, path, offset, value); -} - -void r8712_SwitchBandwidth(struct _adapter *pAdapter) -{ - /* 3 1.Set MAC register : BWOPMODE bit2:1 20MhzBW */ - u8 regBwOpMode = 0; - u8 Bandwidth = pAdapter->mppriv.curr_bandwidth; - - regBwOpMode = r8712_read8(pAdapter, 0x10250203); - if (Bandwidth == HT_CHANNEL_WIDTH_20) - regBwOpMode |= BIT(2); - else - regBwOpMode &= ~(BIT(2)); - r8712_write8(pAdapter, 0x10250203, regBwOpMode); - /* 3 2.Set PHY related register */ - switch (Bandwidth) { - /* 20 MHz channel*/ - case HT_CHANNEL_WIDTH_20: - set_bb_reg(pAdapter, rFPGA0_RFMOD, bRFMOD, 0x0); - set_bb_reg(pAdapter, rFPGA1_RFMOD, bRFMOD, 0x0); - /* Use PHY_REG.txt default value. Do not need to change. - * Correct the tx power for CCK rate in 40M. - * It is set in Tx descriptor for 8192x series - */ - set_bb_reg(pAdapter, rFPGA0_AnalogParameter2, bMaskDWord, 0x58); - break; - /* 40 MHz channel*/ - case HT_CHANNEL_WIDTH_40: - set_bb_reg(pAdapter, rFPGA0_RFMOD, bRFMOD, 0x1); - set_bb_reg(pAdapter, rFPGA1_RFMOD, bRFMOD, 0x1); - /* Use PHY_REG.txt default value. Do not need to change. - * Correct the tx power for CCK rate in 40M. - * Set Control channel to upper or lower. These settings are - * required only for 40MHz - */ - set_bb_reg(pAdapter, rCCK0_System, bCCKSideBand, - (HAL_PRIME_CHNL_OFFSET_DONT_CARE >> 1)); - set_bb_reg(pAdapter, rOFDM1_LSTF, 0xC00, - HAL_PRIME_CHNL_OFFSET_DONT_CARE); - set_bb_reg(pAdapter, rFPGA0_AnalogParameter2, bMaskDWord, 0x18); - break; - default: - break; - } - - /* 3 3.Set RF related register */ - switch (Bandwidth) { - case HT_CHANNEL_WIDTH_20: - set_rf_reg(pAdapter, RF_PATH_A, RF_CHNLBW, - BIT(10) | BIT(11), 0x01); - break; - case HT_CHANNEL_WIDTH_40: - set_rf_reg(pAdapter, RF_PATH_A, RF_CHNLBW, - BIT(10) | BIT(11), 0x00); - break; - default: - break; - } -} - -/*------------------------------Define structure----------------------------*/ -struct R_ANTENNA_SELECT_OFDM { - u32 r_tx_antenna:4; - u32 r_ant_l:4; - u32 r_ant_non_ht:4; - u32 r_ant_ht1:4; - u32 r_ant_ht2:4; - u32 r_ant_ht_s1:4; - u32 r_ant_non_ht_s1:4; - u32 OFDM_TXSC:2; - u32 Reserved:2; -}; - -struct R_ANTENNA_SELECT_CCK { - u8 r_cckrx_enable_2:2; - u8 r_cckrx_enable:2; - u8 r_ccktx_enable:4; -}; - -void r8712_SwitchAntenna(struct _adapter *pAdapter) -{ - u32 ofdm_tx_en_val = 0, ofdm_tx_ant_sel_val = 0; - u8 ofdm_rx_ant_sel_val = 0; - u8 cck_ant_select_val = 0; - u32 cck_ant_sel_val = 0; - struct R_ANTENNA_SELECT_CCK *p_cck_txrx; - - p_cck_txrx = (struct R_ANTENNA_SELECT_CCK *)&cck_ant_select_val; - - switch (pAdapter->mppriv.antenna_tx) { - case ANTENNA_A: - /* From SD3 Willis suggestion !!! Set RF A=TX and B as standby*/ - set_bb_reg(pAdapter, rFPGA0_XA_HSSIParameter2, 0xe, 2); - set_bb_reg(pAdapter, rFPGA0_XB_HSSIParameter2, 0xe, 1); - ofdm_tx_en_val = 0x3; - ofdm_tx_ant_sel_val = 0x11111111;/* Power save */ - p_cck_txrx->r_ccktx_enable = 0x8; - break; - case ANTENNA_B: - set_bb_reg(pAdapter, rFPGA0_XA_HSSIParameter2, 0xe, 1); - set_bb_reg(pAdapter, rFPGA0_XB_HSSIParameter2, 0xe, 2); - ofdm_tx_en_val = 0x3; - ofdm_tx_ant_sel_val = 0x22222222;/* Power save */ - p_cck_txrx->r_ccktx_enable = 0x4; - break; - case ANTENNA_AB: /* For 8192S */ - set_bb_reg(pAdapter, rFPGA0_XA_HSSIParameter2, 0xe, 2); - set_bb_reg(pAdapter, rFPGA0_XB_HSSIParameter2, 0xe, 2); - ofdm_tx_en_val = 0x3; - ofdm_tx_ant_sel_val = 0x3321333; /* Disable Power save */ - p_cck_txrx->r_ccktx_enable = 0xC; - break; - default: - break; - } - /*OFDM Tx*/ - set_bb_reg(pAdapter, rFPGA1_TxInfo, 0xffffffff, ofdm_tx_ant_sel_val); - /*OFDM Tx*/ - set_bb_reg(pAdapter, rFPGA0_TxInfo, 0x0000000f, ofdm_tx_en_val); - switch (pAdapter->mppriv.antenna_rx) { - case ANTENNA_A: - ofdm_rx_ant_sel_val = 0x1; /* A */ - p_cck_txrx->r_cckrx_enable = 0x0; /* default: A */ - p_cck_txrx->r_cckrx_enable_2 = 0x0; /* option: A */ - break; - case ANTENNA_B: - ofdm_rx_ant_sel_val = 0x2; /* B */ - p_cck_txrx->r_cckrx_enable = 0x1; /* default: B */ - p_cck_txrx->r_cckrx_enable_2 = 0x1; /* option: B */ - break; - case ANTENNA_AB: - ofdm_rx_ant_sel_val = 0x3; /* AB */ - p_cck_txrx->r_cckrx_enable = 0x0; /* default:A */ - p_cck_txrx->r_cckrx_enable_2 = 0x1; /* option:B */ - break; - default: - break; - } - /*OFDM Rx*/ - set_bb_reg(pAdapter, rOFDM0_TRxPathEnable, 0x0000000f, - ofdm_rx_ant_sel_val); - /*OFDM Rx*/ - set_bb_reg(pAdapter, rOFDM1_TRxPathEnable, 0x0000000f, - ofdm_rx_ant_sel_val); - - cck_ant_sel_val = cck_ant_select_val; - /*CCK TxRx*/ - set_bb_reg(pAdapter, rCCK0_AFESetting, bMaskByte3, cck_ant_sel_val); -} - -static void TriggerRFThermalMeter(struct _adapter *pAdapter) -{ - /* 0x24: RF Reg[6:5] */ - set_rf_reg(pAdapter, RF_PATH_A, RF_T_METER, bRFRegOffsetMask, 0x60); -} - -static u32 ReadRFThermalMeter(struct _adapter *pAdapter) -{ - /* 0x24: RF Reg[4:0] */ - return get_rf_reg(pAdapter, RF_PATH_A, RF_T_METER, 0x1F); -} - -void r8712_GetThermalMeter(struct _adapter *pAdapter, u32 *value) -{ - TriggerRFThermalMeter(pAdapter); - msleep(1000); - *value = ReadRFThermalMeter(pAdapter); -} - -void r8712_SetSingleCarrierTx(struct _adapter *pAdapter, u8 bStart) -{ - if (bStart) { /* Start Single Carrier. */ - /* 1. if OFDM block on? */ - if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn)) - /*set OFDM block on*/ - set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bEnable); - /* 2. set CCK test mode off, set to CCK normal mode */ - set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, bDisable); - /* 3. turn on scramble setting */ - set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable); - /* 4. Turn On Single Carrier Tx and off the other test modes. */ - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, bEnable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable); - } else { /* Stop Single Carrier.*/ - /* Turn off all test modes.*/ - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, - bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable); - msleep(20); - /*BB Reset*/ - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0); - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1); - } -} - -void r8712_SetSingleToneTx(struct _adapter *pAdapter, u8 bStart) -{ - u8 rfPath; - - switch (pAdapter->mppriv.antenna_tx) { - case ANTENNA_B: - rfPath = RF_PATH_B; - break; - case ANTENNA_A: - default: - rfPath = RF_PATH_A; - break; - } - if (bStart) { /* Start Single Tone.*/ - set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, bDisable); - set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bDisable); - set_rf_reg(pAdapter, rfPath, RF_TX_G2, bRFRegOffsetMask, - 0xd4000); - msleep(100); - /* PAD all on.*/ - set_rf_reg(pAdapter, rfPath, RF_AC, bRFRegOffsetMask, 0x2001f); - msleep(100); - } else { /* Stop Single Tone.*/ - set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, bEnable); - set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bEnable); - set_rf_reg(pAdapter, rfPath, RF_TX_G2, bRFRegOffsetMask, - 0x54000); - msleep(100); - /* PAD all on.*/ - set_rf_reg(pAdapter, rfPath, RF_AC, bRFRegOffsetMask, 0x30000); - msleep(100); - } -} - -void r8712_SetCarrierSuppressionTx(struct _adapter *pAdapter, u8 bStart) -{ - if (bStart) { /* Start Carrier Suppression.*/ - if (pAdapter->mppriv.curr_rateidx <= MPT_RATE_11M) { - /* 1. if CCK block on? */ - if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn)) { - /*set CCK block on*/ - set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, - bEnable); - } - /* Turn Off All Test Mode */ - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, - bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, - bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, - bDisable); - /*transmit mode*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x2); - /*turn off scramble setting*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, - bDisable); - /*Set CCK Tx Test Rate*/ - /*Set FTxRate to 1Mbps*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKTxRate, 0x0); - } - } else { /* Stop Carrier Suppression. */ - if (pAdapter->mppriv.curr_rateidx <= MPT_RATE_11M) { - /*normal mode*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x0); - /*turn on scramble setting*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, - bEnable); - /*BB Reset*/ - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0); - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1); - } - } -} - -static void SetCCKContinuousTx(struct _adapter *pAdapter, u8 bStart) -{ - u32 cckrate; - - if (bStart) { - /* 1. if CCK block on? */ - if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn)) { - /*set CCK block on*/ - set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, bEnable); - } - /* Turn Off All Test Mode */ - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable); - /*Set CCK Tx Test Rate*/ - cckrate = pAdapter->mppriv.curr_rateidx; - set_bb_reg(pAdapter, rCCK0_System, bCCKTxRate, cckrate); - /*transmit mode*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x2); - /*turn on scramble setting*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable); - } else { - /*normal mode*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x0); - /*turn on scramble setting*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable); - /*BB Reset*/ - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0); - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1); - } -} /* mpt_StartCckContTx */ - -static void SetOFDMContinuousTx(struct _adapter *pAdapter, u8 bStart) -{ - if (bStart) { - /* 1. if OFDM block on? */ - if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn)) { - /*set OFDM block on*/ - set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bEnable); - } - /* 2. set CCK test mode off, set to CCK normal mode*/ - set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, bDisable); - /* 3. turn on scramble setting */ - set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable); - /* 4. Turn On Continue Tx and turn off the other test modes.*/ - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bEnable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable); - } else { - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, - bDisable); - set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable); - msleep(20); - /*BB Reset*/ - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0); - set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1); - } -} /* mpt_StartOfdmContTx */ - -void r8712_SetContinuousTx(struct _adapter *pAdapter, u8 bStart) -{ - /* ADC turn off [bit24-21] adc port0 ~ port1 */ - if (bStart) { - r8712_bb_reg_write(pAdapter, rRx_Wait_CCCA, - r8712_bb_reg_read(pAdapter, - rRx_Wait_CCCA) & 0xFE1FFFFF); - msleep(100); - } - if (pAdapter->mppriv.curr_rateidx <= MPT_RATE_11M) - SetCCKContinuousTx(pAdapter, bStart); - else if ((pAdapter->mppriv.curr_rateidx >= MPT_RATE_6M) && - (pAdapter->mppriv.curr_rateidx <= MPT_RATE_MCS15)) - SetOFDMContinuousTx(pAdapter, bStart); - /* ADC turn on [bit24-21] adc port0 ~ port1 */ - if (!bStart) - r8712_bb_reg_write(pAdapter, rRx_Wait_CCCA, - r8712_bb_reg_read(pAdapter, - rRx_Wait_CCCA) | 0x01E00000); -} - -void r8712_ResetPhyRxPktCount(struct _adapter *pAdapter) -{ - u32 i, phyrx_set = 0; - - for (i = OFDM_PPDU_BIT; i <= HT_MPDU_FAIL_BIT; i++) { - phyrx_set = 0; - phyrx_set |= (i << 28); /*select*/ - phyrx_set |= 0x08000000; /* set counter to zero*/ - r8712_write32(pAdapter, RXERR_RPT, phyrx_set); - } -} - -static u32 GetPhyRxPktCounts(struct _adapter *pAdapter, u32 selbit) -{ - /*selection*/ - u32 phyrx_set = 0; - u32 SelectBit; - - SelectBit = selbit << 28; - phyrx_set |= (SelectBit & 0xF0000000); - r8712_write32(pAdapter, RXERR_RPT, phyrx_set); - /*Read packet count*/ - return r8712_read32(pAdapter, RXERR_RPT) & RPTMaxCount; -} - -u32 r8712_GetPhyRxPktReceived(struct _adapter *pAdapter) -{ - u32 OFDM_cnt = GetPhyRxPktCounts(pAdapter, OFDM_MPDU_OK_BIT); - u32 CCK_cnt = GetPhyRxPktCounts(pAdapter, CCK_MPDU_OK_BIT); - u32 HT_cnt = GetPhyRxPktCounts(pAdapter, HT_MPDU_OK_BIT); - - return OFDM_cnt + CCK_cnt + HT_cnt; -} - -u32 r8712_GetPhyRxPktCRC32Error(struct _adapter *pAdapter) -{ - u32 OFDM_cnt = GetPhyRxPktCounts(pAdapter, OFDM_MPDU_FAIL_BIT); - u32 CCK_cnt = GetPhyRxPktCounts(pAdapter, CCK_MPDU_FAIL_BIT); - u32 HT_cnt = GetPhyRxPktCounts(pAdapter, HT_MPDU_FAIL_BIT); - - return OFDM_cnt + CCK_cnt + HT_cnt; -} diff --git a/drivers/staging/rtl8712/rtl871x_mp.h b/drivers/staging/rtl8712/rtl871x_mp.h deleted file mode 100644 index 0a60b1e6ccafc..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mp.h +++ /dev/null @@ -1,275 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_MP_H_ -#define __RTL871X_MP_H_ - -#define MPT_NOOP 0 -#define MPT_READ_MAC_1BYTE 1 -#define MPT_READ_MAC_2BYTE 2 -#define MPT_READ_MAC_4BYTE 3 -#define MPT_WRITE_MAC_1BYTE 4 -#define MPT_WRITE_MAC_2BYTE 5 -#define MPT_WRITE_MAC_4BYTE 6 -#define MPT_READ_BB_CCK 7 -#define MPT_WRITE_BB_CCK 8 -#define MPT_READ_BB_OFDM 9 -#define MPT_WRITE_BB_OFDM 10 -#define MPT_READ_RF 11 -#define MPT_WRITE_RF 12 -#define MPT_READ_EEPROM_1BYTE 13 -#define MPT_WRITE_EEPROM_1BYTE 14 -#define MPT_READ_EEPROM_2BYTE 15 -#define MPT_WRITE_EEPROM_2BYTE 16 -#define MPT_SET_CSTHRESHOLD 21 -#define MPT_SET_INITGAIN 22 -#define MPT_SWITCH_BAND 23 -#define MPT_SWITCH_CHANNEL 24 -#define MPT_SET_DATARATE 25 -#define MPT_SWITCH_ANTENNA 26 -#define MPT_SET_TX_POWER 27 -#define MPT_SET_CONT_TX 28 -#define MPT_SET_SINGLE_CARRIER 29 -#define MPT_SET_CARRIER_SUPPRESSION 30 -#define MPT_GET_RATE_TABLE 31 -#define MPT_READ_TSSI 32 -#define MPT_GET_THERMAL_METER 33 -#define MAX_MP_XMITBUF_SZ 2048 -#define NR_MP_XMITFRAME 8 - -struct mp_xmit_frame { - struct list_head list; - struct pkt_attrib attrib; - _pkt *pkt; - int frame_tag; - struct _adapter *padapter; - u8 *mem_addr; - u16 sz[8]; - struct urb *pxmit_urb[8]; - u8 bpending[8]; - u8 last[8]; -}; - -struct mp_wiparam { - u32 bcompleted; - u32 act_type; - u32 io_offset; - u32 io_value; -}; - -struct mp_priv { - struct _adapter *papdater; - /*OID cmd handler*/ - struct mp_wiparam workparam; - u8 act_in_progress; - /*Tx Section*/ - u8 TID; - u32 tx_pktcount; - /*Rx Section*/ - u32 rx_pktcount; - u32 rx_crcerrpktcount; - u32 rx_pktloss; - struct recv_stat rxstat; - /*RF/BB relative*/ - u32 curr_ch; - u32 curr_rateidx; - u8 curr_bandwidth; - u8 curr_modem; - u8 curr_txpoweridx; - u32 curr_crystalcap; - u16 antenna_tx; - u16 antenna_rx; - u8 curr_rfpath; - u8 check_mp_pkt; - uint ForcedDataRate; - struct wlan_network mp_network; - unsigned char network_macaddr[6]; - /*Testing Flag*/ - u32 mode;/*0 for normal type packet, - * 1 for loopback packet (16bytes TXCMD) - */ - sint prev_fw_state; - u8 *pallocated_mp_xmitframe_buf; - u8 *pmp_xmtframe_buf; - struct __queue free_mp_xmitqueue; - u32 free_mp_xmitframe_cnt; -}; - -struct IOCMD_STRUCT { - u8 cmdclass; - u16 value; - u8 index; -}; - -struct rf_reg_param { - u32 path; - u32 offset; - u32 value; -}; - -struct bb_reg_param { - u32 offset; - u32 value; -}; - -/* ======================================================================= */ - -#define LOWER true -#define RAISE false -#define IOCMD_CTRL_REG 0x10250370 -#define IOCMD_DATA_REG 0x10250374 -#define IOCMD_GET_THERMAL_METER 0xFD000028 -#define IOCMD_CLASS_BB_RF 0xF0 -#define IOCMD_BB_READ_IDX 0x00 -#define IOCMD_BB_WRITE_IDX 0x01 -#define IOCMD_RF_READ_IDX 0x02 -#define IOCMD_RF_WRIT_IDX 0x03 -#define BB_REG_BASE_ADDR 0x800 -#define RF_PATH_A 0 -#define RF_PATH_B 1 -#define RF_PATH_C 2 -#define RF_PATH_D 3 -#define MAX_RF_PATH_NUMS 2 -#define _2MAC_MODE_ 0 -#define _LOOPBOOK_MODE_ 1 - -/* MP set force data rate base on the definition. */ -enum { - /* CCK rate. */ - MPT_RATE_1M, /* 0 */ - MPT_RATE_2M, - MPT_RATE_55M, - MPT_RATE_11M, /* 3 */ - - /* OFDM rate. */ - MPT_RATE_6M, /* 4 */ - MPT_RATE_9M, - MPT_RATE_12M, - MPT_RATE_18M, - MPT_RATE_24M, - MPT_RATE_36M, - MPT_RATE_48M, - MPT_RATE_54M, /* 11 */ - - /* HT rate. */ - MPT_RATE_MCS0, /* 12 */ - MPT_RATE_MCS1, - MPT_RATE_MCS2, - MPT_RATE_MCS3, - MPT_RATE_MCS4, - MPT_RATE_MCS5, - MPT_RATE_MCS6, - MPT_RATE_MCS7, /* 19 */ - MPT_RATE_MCS8, - MPT_RATE_MCS9, - MPT_RATE_MCS10, - MPT_RATE_MCS11, - MPT_RATE_MCS12, - MPT_RATE_MCS13, - MPT_RATE_MCS14, - MPT_RATE_MCS15, /* 27 */ - MPT_RATE_LAST -}; - -/* Represent Channel Width in HT Capabilities */ -enum HT_CHANNEL_WIDTH { - HT_CHANNEL_WIDTH_20 = 0, - HT_CHANNEL_WIDTH_40 = 1, -}; - -#define MAX_TX_PWR_INDEX_N_MODE 64 /* 0x3F */ - -enum POWER_MODE { - POWER_LOW = 0, - POWER_NORMAL -}; - -#define RX_PKT_BROADCAST 1 -#define RX_PKT_DEST_ADDR 2 -#define RX_PKT_PHY_MATCH 3 - -#define RPTMaxCount 0x000FFFFF - -/* parameter 1 : BitMask - * bit 0 : OFDM PPDU - * bit 1 : OFDM False Alarm - * bit 2 : OFDM MPDU OK - * bit 3 : OFDM MPDU Fail - * bit 4 : CCK PPDU - * bit 5 : CCK False Alarm - * bit 6 : CCK MPDU ok - * bit 7 : CCK MPDU fail - * bit 8 : HT PPDU counter - * bit 9 : HT false alarm - * bit 10 : HT MPDU total - * bit 11 : HT MPDU OK - * bit 12 : HT MPDU fail - * bit 15 : RX full drop - */ -enum RXPHY_BITMASK { - OFDM_PPDU_BIT = 0, - OFDM_MPDU_OK_BIT, - OFDM_MPDU_FAIL_BIT, - CCK_PPDU_BIT, - CCK_MPDU_OK_BIT, - CCK_MPDU_FAIL_BIT, - HT_PPDU_BIT, - HT_MPDU_BIT, - HT_MPDU_OK_BIT, - HT_MPDU_FAIL_BIT, -}; - -enum ENCRY_CTRL_STATE { - HW_CONTROL, /*hw encryption& decryption*/ - SW_CONTROL, /*sw encryption& decryption*/ - HW_ENCRY_SW_DECRY, /*hw encryption & sw decryption*/ - SW_ENCRY_HW_DECRY /*sw encryption & hw decryption*/ -}; - -/* Bandwidth Offset */ -#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0 -#define HAL_PRIME_CHNL_OFFSET_LOWER 1 -#define HAL_PRIME_CHNL_OFFSET_UPPER 2 -/*=======================================================================*/ -void mp871xinit(struct _adapter *padapter); -void mp871xdeinit(struct _adapter *padapter); -u32 r8712_bb_reg_read(struct _adapter *Adapter, u16 offset); -u8 r8712_bb_reg_write(struct _adapter *Adapter, u16 offset, u32 value); -u32 r8712_rf_reg_read(struct _adapter *Adapter, u8 path, u8 offset); -u8 r8712_rf_reg_write(struct _adapter *Adapter, u8 path, - u8 offset, u32 value); -u32 r8712_get_bb_reg(struct _adapter *Adapter, u16 offset, u32 bitmask); -u8 r8712_set_bb_reg(struct _adapter *Adapter, u16 offset, - u32 bitmask, u32 value); -u32 r8712_get_rf_reg(struct _adapter *Adapter, u8 path, u8 offset, - u32 bitmask); -u8 r8712_set_rf_reg(struct _adapter *Adapter, u8 path, u8 offset, - u32 bitmask, u32 value); - -void r8712_SetChannel(struct _adapter *pAdapter); -void r8712_SetTxPower(struct _adapter *pAdapte); -void r8712_SetTxAGCOffset(struct _adapter *pAdapter, u32 ulTxAGCOffset); -void r8712_SetDataRate(struct _adapter *pAdapter); -void r8712_SwitchBandwidth(struct _adapter *pAdapter); -void r8712_SwitchAntenna(struct _adapter *pAdapter); -void r8712_GetThermalMeter(struct _adapter *pAdapter, u32 *value); -void r8712_SetContinuousTx(struct _adapter *pAdapter, u8 bStart); -void r8712_SetSingleCarrierTx(struct _adapter *pAdapter, u8 bStart); -void r8712_SetSingleToneTx(struct _adapter *pAdapter, u8 bStart); -void r8712_SetCarrierSuppressionTx(struct _adapter *pAdapter, u8 bStart); -void r8712_ResetPhyRxPktCount(struct _adapter *pAdapter); -u32 r8712_GetPhyRxPktReceived(struct _adapter *pAdapter); -u32 r8712_GetPhyRxPktCRC32Error(struct _adapter *pAdapter); - -#endif /*__RTL871X_MP_H_*/ - diff --git a/drivers/staging/rtl8712/rtl871x_mp_ioctl.c b/drivers/staging/rtl8712/rtl871x_mp_ioctl.c deleted file mode 100644 index 26fa09b45c908..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mp_ioctl.c +++ /dev/null @@ -1,883 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_mp_ioctl.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#include -#include "osdep_service.h" -#include "drv_types.h" -#include "mlme_osdep.h" -#include "rtl871x_mp.h" -#include "rtl871x_mp_ioctl.h" - -uint oid_null_function(struct oid_par_priv *poid_par_priv) -{ - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_wireless_mode_hdl(struct oid_par_priv *poid_par_priv) -{ - uint status = RNDIS_STATUS_SUCCESS; - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid == SET_OID) { - if (poid_par_priv->information_buf_len >= sizeof(u8)) - Adapter->registrypriv.wireless_mode = - *(u8 *)poid_par_priv->information_buf; - else - status = RNDIS_STATUS_INVALID_LENGTH; - } else if (poid_par_priv->type_of_oid == QUERY_OID) { - if (poid_par_priv->information_buf_len >= sizeof(u8)) { - *(u8 *)poid_par_priv->information_buf = - Adapter->registrypriv.wireless_mode; - *poid_par_priv->bytes_rw = - poid_par_priv->information_buf_len; - } else { - status = RNDIS_STATUS_INVALID_LENGTH; - } - } else { - status = RNDIS_STATUS_NOT_ACCEPTED; - } - return status; -} - -uint oid_rt_pro_write_bb_reg_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - struct bb_reg_param *pbbreg; - u16 offset; - u32 value; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(struct bb_reg_param)) - return RNDIS_STATUS_INVALID_LENGTH; - pbbreg = (struct bb_reg_param *)(poid_par_priv->information_buf); - offset = (u16)(pbbreg->offset) & 0xFFF; /*0ffset :0x800~0xfff*/ - if (offset < BB_REG_BASE_ADDR) - offset |= BB_REG_BASE_ADDR; - value = pbbreg->value; - r8712_bb_reg_write(Adapter, offset, value); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_read_bb_reg_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - struct bb_reg_param *pbbreg; - u16 offset; - u32 value; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(struct bb_reg_param)) - return RNDIS_STATUS_INVALID_LENGTH; - pbbreg = (struct bb_reg_param *)(poid_par_priv->information_buf); - offset = (u16)(pbbreg->offset) & 0xFFF; /*0ffset :0x800~0xfff*/ - if (offset < BB_REG_BASE_ADDR) - offset |= BB_REG_BASE_ADDR; - value = r8712_bb_reg_read(Adapter, offset); - pbbreg->value = value; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_write_rf_reg_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - struct rf_reg_param *pbbreg; - u8 path; - u8 offset; - u32 value; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(struct rf_reg_param)) - return RNDIS_STATUS_INVALID_LENGTH; - pbbreg = (struct rf_reg_param *)(poid_par_priv->information_buf); - path = (u8)pbbreg->path; - if (path > RF_PATH_B) - return RNDIS_STATUS_NOT_ACCEPTED; - offset = (u8)pbbreg->offset; - value = pbbreg->value; - r8712_rf_reg_write(Adapter, path, offset, value); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_read_rf_reg_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - struct rf_reg_param *pbbreg; - u8 path; - u8 offset; - u32 value; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(struct rf_reg_param)) - return RNDIS_STATUS_INVALID_LENGTH; - pbbreg = (struct rf_reg_param *)(poid_par_priv->information_buf); - path = (u8)pbbreg->path; - if (path > RF_PATH_B) /* 1T2R path_a /path_b */ - return RNDIS_STATUS_NOT_ACCEPTED; - offset = (u8)pbbreg->offset; - value = r8712_rf_reg_read(Adapter, path, offset); - pbbreg->value = value; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -/*This function initializes the DUT to the MP test mode*/ -static int mp_start_test(struct _adapter *padapter) -{ - struct mp_priv *pmppriv = &padapter->mppriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_network *tgt_network = &pmlmepriv->cur_network; - struct wlan_bssid_ex *bssid; - struct sta_info *psta; - unsigned long length; - unsigned long irqL; - int res = 0; - - bssid = kzalloc(sizeof(*bssid), GFP_KERNEL); - if (!bssid) - return -ENOMEM; - - /* 3 1. initialize a new struct wlan_bssid_ex */ - memcpy(bssid->MacAddress, pmppriv->network_macaddr, ETH_ALEN); - bssid->Ssid.SsidLength = 16; - memcpy(bssid->Ssid.Ssid, (unsigned char *)"mp_pseudo_adhoc", - bssid->Ssid.SsidLength); - bssid->InfrastructureMode = Ndis802_11IBSS; - bssid->NetworkTypeInUse = Ndis802_11DS; - bssid->IELength = 0; - length = r8712_get_wlan_bssid_ex_sz(bssid); - if (length % 4) { - /*round up to multiple of 4 bytes.*/ - bssid->Length = ((length >> 2) + 1) << 2; - } else { - bssid->Length = length; - } - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) - goto end_of_mp_start_test; - /*init mp_start_test status*/ - pmppriv->prev_fw_state = get_fwstate(pmlmepriv); - pmlmepriv->fw_state = WIFI_MP_STATE; - if (pmppriv->mode == _LOOPBOOK_MODE_) - set_fwstate(pmlmepriv, WIFI_MP_LPBK_STATE); /*append txdesc*/ - set_fwstate(pmlmepriv, _FW_UNDER_LINKING); - /* 3 2. create a new psta for mp driver */ - /* clear psta in the cur_network, if any */ - psta = r8712_get_stainfo(&padapter->stapriv, - tgt_network->network.MacAddress); - if (psta) - r8712_free_stainfo(padapter, psta); - psta = r8712_alloc_stainfo(&padapter->stapriv, bssid->MacAddress); - if (!psta) { - res = -ENOMEM; - goto end_of_mp_start_test; - } - /* 3 3. join pseudo AdHoc */ - tgt_network->join_res = 1; - tgt_network->aid = psta->aid = 1; - memcpy(&tgt_network->network, bssid, length); - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - r8712_os_indicate_connect(padapter); - /* Set to LINKED STATE for MP TRX Testing */ - set_fwstate(pmlmepriv, _FW_LINKED); -end_of_mp_start_test: - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - kfree(bssid); - return res; -} - -/*This function change the DUT from the MP test mode into normal mode */ -static int mp_stop_test(struct _adapter *padapter) -{ - struct mp_priv *pmppriv = &padapter->mppriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct wlan_network *tgt_network = &pmlmepriv->cur_network; - struct sta_info *psta; - unsigned long irqL; - - spin_lock_irqsave(&pmlmepriv->lock, irqL); - if (!check_fwstate(pmlmepriv, WIFI_MP_STATE)) - goto end_of_mp_stop_test; - /* 3 1. disconnect pseudo AdHoc */ - r8712_os_indicate_disconnect(padapter); - /* 3 2. clear psta used in mp test mode. */ - psta = r8712_get_stainfo(&padapter->stapriv, - tgt_network->network.MacAddress); - if (psta) - r8712_free_stainfo(padapter, psta); - /* 3 3. return to normal state (default:station mode) */ - pmlmepriv->fw_state = pmppriv->prev_fw_state; /* WIFI_STATION_STATE;*/ - /*flush the cur_network*/ - memset(tgt_network, 0, sizeof(struct wlan_network)); -end_of_mp_stop_test: - spin_unlock_irqrestore(&pmlmepriv->lock, irqL); - return _SUCCESS; -} - -uint oid_rt_pro_set_data_rate_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 ratevalue; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len != sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - ratevalue = *((u32 *)poid_par_priv->information_buf); - if (ratevalue >= MPT_RATE_LAST) - return RNDIS_STATUS_INVALID_DATA; - Adapter->mppriv.curr_rateidx = ratevalue; - r8712_SetDataRate(Adapter); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_start_test_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - uint status = RNDIS_STATUS_SUCCESS; - u32 mode; - u8 val8; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - mode = *((u32 *)poid_par_priv->information_buf); - Adapter->mppriv.mode = mode;/* 1 for loopback*/ - if (mp_start_test(Adapter)) - status = RNDIS_STATUS_NOT_ACCEPTED; - r8712_write8(Adapter, MSR, 1); /* Link in ad hoc network, 0x1025004C */ - r8712_write8(Adapter, RCR, 0); /* RCR : disable all pkt, 0x10250048 */ - /* RCR disable Check BSSID, 0x1025004a */ - r8712_write8(Adapter, RCR + 2, 0x57); - /* disable RX filter map , mgt frames will put in RX FIFO 0 */ - r8712_write16(Adapter, RXFLTMAP0, 0x0); - val8 = r8712_read8(Adapter, EE_9346CR); - if (!(val8 & _9356SEL)) { /*boot from EFUSE*/ - r8712_efuse_reg_init(Adapter); - r8712_efuse_change_max_size(Adapter); - r8712_efuse_reg_uninit(Adapter); - } - return status; -} - -uint oid_rt_pro_stop_test_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (mp_stop_test(Adapter) == _FAIL) - return RNDIS_STATUS_NOT_ACCEPTED; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_channel_direct_call_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 Channel; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len != sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - Channel = *((u32 *)poid_par_priv->information_buf); - if (Channel > 14) - return RNDIS_STATUS_NOT_ACCEPTED; - Adapter->mppriv.curr_ch = Channel; - r8712_SetChannel(Adapter); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_antenna_bb_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 antenna; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len != sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - antenna = *((u32 *)poid_par_priv->information_buf); - Adapter->mppriv.antenna_tx = (u16)((antenna & 0xFFFF0000) >> 16); - Adapter->mppriv.antenna_rx = (u16)(antenna & 0x0000FFFF); - r8712_SwitchAntenna(Adapter); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_tx_power_control_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 tx_pwr_idx; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len != sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - tx_pwr_idx = *((u32 *)poid_par_priv->information_buf); - if (tx_pwr_idx > MAX_TX_PWR_INDEX_N_MODE) - return RNDIS_STATUS_NOT_ACCEPTED; - Adapter->mppriv.curr_txpoweridx = (u8)tx_pwr_idx; - r8712_SetTxPower(Adapter); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_query_tx_packet_sent_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - - if (poid_par_priv->information_buf_len == sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - Adapter->mppriv.tx_pktcount; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_query_rx_packet_received_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - - if (poid_par_priv->information_buf_len == sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - Adapter->mppriv.rx_pktcount; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_query_rx_packet_crc32_error_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - - if (poid_par_priv->information_buf_len == sizeof(u32)) { - *(u32 *)poid_par_priv->information_buf = - Adapter->mppriv.rx_crcerrpktcount; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_reset_tx_packet_sent_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - Adapter->mppriv.tx_pktcount = 0; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_reset_rx_packet_received_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len == sizeof(u32)) { - Adapter->mppriv.rx_pktcount = 0; - Adapter->mppriv.rx_crcerrpktcount = 0; - } else { - return RNDIS_STATUS_INVALID_LENGTH; - } - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_reset_phy_rx_packet_count_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - r8712_ResetPhyRxPktCount(Adapter); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_phy_rx_packet_received_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len != sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - *(u32 *)poid_par_priv->information_buf = - r8712_GetPhyRxPktReceived(Adapter); - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_phy_rx_packet_crc32_error_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len != sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - *(u32 *)poid_par_priv->information_buf = - r8712_GetPhyRxPktCRC32Error(Adapter); - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - - Adapter->mppriv.curr_modem = *((u8 *)poid_par_priv->information_buf); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_continuous_tx_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 bStartTest; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - bStartTest = *((u32 *)poid_par_priv->information_buf); - r8712_SetContinuousTx(Adapter, (u8)bStartTest); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_single_carrier_tx_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 bStartTest; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - bStartTest = *((u32 *)poid_par_priv->information_buf); - r8712_SetSingleCarrierTx(Adapter, (u8)bStartTest); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_carrier_suppression_tx_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 bStartTest; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - bStartTest = *((u32 *)poid_par_priv->information_buf); - r8712_SetCarrierSuppressionTx(Adapter, (u8)bStartTest); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_set_single_tone_tx_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 bStartTest; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - bStartTest = *((u32 *)poid_par_priv->information_buf); - r8712_SetSingleToneTx(Adapter, (u8)bStartTest); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_read_register_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - uint status = RNDIS_STATUS_SUCCESS; - struct mp_rw_reg *RegRWStruct; - u16 offset; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - RegRWStruct = (struct mp_rw_reg *)poid_par_priv->information_buf; - if ((RegRWStruct->offset >= 0x10250800) && - (RegRWStruct->offset <= 0x10250FFF)) { - /*baseband register*/ - /*0ffset :0x800~0xfff*/ - offset = (u16)(RegRWStruct->offset) & 0xFFF; - RegRWStruct->value = r8712_bb_reg_read(Adapter, offset); - } else { - switch (RegRWStruct->width) { - case 1: - RegRWStruct->value = r8712_read8(Adapter, - RegRWStruct->offset); - break; - case 2: - RegRWStruct->value = r8712_read16(Adapter, - RegRWStruct->offset); - break; - case 4: - RegRWStruct->value = r8712_read32(Adapter, - RegRWStruct->offset); - break; - default: - status = RNDIS_STATUS_NOT_ACCEPTED; - break; - } - } - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return status; -} - -uint oid_rt_pro_write_register_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - uint status = RNDIS_STATUS_SUCCESS; - struct mp_rw_reg *RegRWStruct; - u16 offset; - u32 value; - u32 oldValue = 0; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - RegRWStruct = (struct mp_rw_reg *)poid_par_priv->information_buf; - if ((RegRWStruct->offset >= 0x10250800) && - (RegRWStruct->offset <= 0x10250FFF)) { - /*baseband register*/ - offset = (u16)(RegRWStruct->offset) & 0xFFF; - value = RegRWStruct->value; - switch (RegRWStruct->width) { - case 1: - oldValue = r8712_bb_reg_read(Adapter, offset); - oldValue &= 0xFFFFFF00; - value &= 0x000000FF; - value |= oldValue; - break; - case 2: - oldValue = r8712_bb_reg_read(Adapter, offset); - oldValue &= 0xFFFF0000; - value &= 0x0000FFFF; - value |= oldValue; - break; - } - r8712_bb_reg_write(Adapter, offset, value); - } else { - switch (RegRWStruct->width) { - case 1: - r8712_write8(Adapter, RegRWStruct->offset, - (unsigned char)RegRWStruct->value); - break; - case 2: - r8712_write16(Adapter, RegRWStruct->offset, - (unsigned short)RegRWStruct->value); - break; - case 4: - r8712_write32(Adapter, RegRWStruct->offset, - (unsigned int)RegRWStruct->value); - break; - default: - status = RNDIS_STATUS_NOT_ACCEPTED; - break; - } - } - return status; -} - -uint oid_rt_get_thermal_meter_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - - if (Adapter->mppriv.act_in_progress) - return RNDIS_STATUS_NOT_ACCEPTED; - - if (poid_par_priv->information_buf_len < sizeof(u8)) - return RNDIS_STATUS_INVALID_LENGTH; - /*init workparam*/ - Adapter->mppriv.act_in_progress = true; - Adapter->mppriv.workparam.bcompleted = false; - Adapter->mppriv.workparam.act_type = MPT_GET_THERMAL_METER; - Adapter->mppriv.workparam.io_offset = 0; - Adapter->mppriv.workparam.io_value = 0xFFFFFFFF; - r8712_GetThermalMeter(Adapter, &Adapter->mppriv.workparam.io_value); - Adapter->mppriv.workparam.bcompleted = true; - Adapter->mppriv.act_in_progress = false; - *(u32 *)poid_par_priv->information_buf = - Adapter->mppriv.workparam.io_value; - *poid_par_priv->bytes_rw = sizeof(u32); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_read_efuse_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - uint status = RNDIS_STATUS_SUCCESS; - - struct EFUSE_ACCESS_STRUCT *pefuse; - u8 *data; - u16 addr = 0, cnts = 0; - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < - sizeof(struct EFUSE_ACCESS_STRUCT)) - return RNDIS_STATUS_INVALID_LENGTH; - pefuse = (struct EFUSE_ACCESS_STRUCT *)poid_par_priv->information_buf; - addr = pefuse->start_addr; - cnts = pefuse->cnts; - data = pefuse->data; - memset(data, 0xFF, cnts); - if ((addr > 511) || (cnts < 1) || (cnts > 512) || (addr + cnts) > - EFUSE_MAX_SIZE) - return RNDIS_STATUS_NOT_ACCEPTED; - if (!r8712_efuse_access(Adapter, true, addr, cnts, data)) - status = RNDIS_STATUS_FAILURE; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return status; -} - -/*------------------------------------------------------------------------*/ -uint oid_rt_pro_write_efuse_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - uint status = RNDIS_STATUS_SUCCESS; - - struct EFUSE_ACCESS_STRUCT *pefuse; - u8 *data; - u16 addr = 0, cnts = 0; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - - pefuse = (struct EFUSE_ACCESS_STRUCT *)poid_par_priv->information_buf; - addr = pefuse->start_addr; - cnts = pefuse->cnts; - data = pefuse->data; - - if ((addr > 511) || (cnts < 1) || (cnts > 512) || - (addr + cnts) > r8712_efuse_get_max_size(Adapter)) - return RNDIS_STATUS_NOT_ACCEPTED; - if (!r8712_efuse_access(Adapter, false, addr, cnts, data)) - status = RNDIS_STATUS_FAILURE; - return status; -} - -/*----------------------------------------------------------------------*/ - -uint oid_rt_get_efuse_current_size_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(int)) - return RNDIS_STATUS_INVALID_LENGTH; - r8712_efuse_reg_init(Adapter); - *(int *)poid_par_priv->information_buf = - r8712_efuse_get_current_size(Adapter); - r8712_efuse_reg_uninit(Adapter); - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_get_efuse_max_size_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - *(int *)poid_par_priv->information_buf = - r8712_efuse_get_max_size(Adapter); - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_pro_efuse_hdl(struct oid_par_priv *poid_par_priv) -{ - uint status = RNDIS_STATUS_SUCCESS; - - if (poid_par_priv->type_of_oid == QUERY_OID) - status = oid_rt_pro_read_efuse_hdl(poid_par_priv); - else - status = oid_rt_pro_write_efuse_hdl(poid_par_priv); - return status; -} - -uint oid_rt_pro_efuse_map_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - uint status = RNDIS_STATUS_SUCCESS; - u8 *data; - - *poid_par_priv->bytes_rw = 0; - if (poid_par_priv->information_buf_len < EFUSE_MAP_MAX_SIZE) - return RNDIS_STATUS_INVALID_LENGTH; - data = (u8 *)poid_par_priv->information_buf; - if (poid_par_priv->type_of_oid == QUERY_OID) { - if (r8712_efuse_map_read(Adapter, 0, EFUSE_MAP_MAX_SIZE, data)) - *poid_par_priv->bytes_rw = EFUSE_MAP_MAX_SIZE; - else - status = RNDIS_STATUS_FAILURE; - } else { - /* SET_OID */ - if (r8712_efuse_reg_init(Adapter)) { - if (r8712_efuse_map_write(Adapter, 0, - EFUSE_MAP_MAX_SIZE, data)) - *poid_par_priv->bytes_rw = EFUSE_MAP_MAX_SIZE; - else - status = RNDIS_STATUS_FAILURE; - r8712_efuse_reg_uninit(Adapter); - } else { - status = RNDIS_STATUS_FAILURE; - } - } - return status; -} - -uint oid_rt_set_bandwidth_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u32 bandwidth; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - bandwidth = *((u32 *)poid_par_priv->information_buf);/*4*/ - if (bandwidth != HT_CHANNEL_WIDTH_20) - bandwidth = HT_CHANNEL_WIDTH_40; - Adapter->mppriv.curr_bandwidth = (u8)bandwidth; - r8712_SwitchBandwidth(Adapter); - return RNDIS_STATUS_SUCCESS; -} - -uint oid_rt_set_rx_packet_type_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - u8 rx_pkt_type; - u32 rcr_val32; - - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(u8)) - return RNDIS_STATUS_INVALID_LENGTH; - rx_pkt_type = *((u8 *)poid_par_priv->information_buf);/*4*/ - rcr_val32 = r8712_read32(Adapter, RCR);/*RCR = 0x10250048*/ - rcr_val32 &= ~(RCR_CBSSID | RCR_AB | RCR_AM | RCR_APM | RCR_AAP); - switch (rx_pkt_type) { - case RX_PKT_BROADCAST: - rcr_val32 |= (RCR_AB | RCR_AM | RCR_APM | RCR_AAP | RCR_ACRC32); - break; - case RX_PKT_DEST_ADDR: - rcr_val32 |= (RCR_AB | RCR_AM | RCR_APM | RCR_AAP | RCR_ACRC32); - break; - case RX_PKT_PHY_MATCH: - rcr_val32 |= (RCR_APM | RCR_ACRC32); - break; - default: - rcr_val32 &= ~(RCR_AAP | - RCR_APM | - RCR_AM | - RCR_AB | - RCR_ACRC32); - break; - } - if (rx_pkt_type == RX_PKT_DEST_ADDR) - Adapter->mppriv.check_mp_pkt = 1; - else - Adapter->mppriv.check_mp_pkt = 0; - r8712_write32(Adapter, RCR, rcr_val32); - return RNDIS_STATUS_SUCCESS; -} - -/*--------------------------------------------------------------------------*/ -/*Linux*/ -unsigned int mp_ioctl_xmit_packet_hdl(struct oid_par_priv *poid_par_priv) -{ - return _SUCCESS; -} - -/*-------------------------------------------------------------------------*/ -uint oid_rt_set_power_down_hdl(struct oid_par_priv *poid_par_priv) -{ - if (poid_par_priv->type_of_oid != SET_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - /*CALL the power_down function*/ - return RNDIS_STATUS_SUCCESS; -} - -/*-------------------------------------------------------------------------- */ -uint oid_rt_get_power_mode_hdl(struct oid_par_priv *poid_par_priv) -{ - struct _adapter *Adapter = (struct _adapter *) - (poid_par_priv->adapter_context); - - if (poid_par_priv->type_of_oid != QUERY_OID) - return RNDIS_STATUS_NOT_ACCEPTED; - if (poid_par_priv->information_buf_len < sizeof(u32)) - return RNDIS_STATUS_INVALID_LENGTH; - *(int *)poid_par_priv->information_buf = - Adapter->registrypriv.low_power ? POWER_LOW : POWER_NORMAL; - *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; - return RNDIS_STATUS_SUCCESS; -} diff --git a/drivers/staging/rtl8712/rtl871x_mp_ioctl.h b/drivers/staging/rtl8712/rtl871x_mp_ioctl.h deleted file mode 100644 index aa4d5ce471f2f..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mp_ioctl.h +++ /dev/null @@ -1,328 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871X_MP_IOCTL_H -#define _RTL871X_MP_IOCTL_H - -#include "osdep_service.h" -#include "drv_types.h" -#include "mp_custom_oid.h" -#include "rtl871x_ioctl.h" -#include "rtl871x_ioctl_rtl.h" -#include "rtl8712_efuse.h" - -#define TESTFWCMDNUMBER 1000000 -#define TEST_H2CINT_WAIT_TIME 500 -#define TEST_C2HINT_WAIT_TIME 500 -#define HCI_TEST_SYSCFG_HWMASK 1 -#define _BUSCLK_40M (4 << 2) - -struct CFG_DBG_MSG_STRUCT { - u32 DebugLevel; - u32 DebugComponent_H32; - u32 DebugComponent_L32; -}; - -struct mp_rw_reg { - uint offset; - uint width; - u32 value; -}; - -/* for OID_RT_PRO_READ16_EEPROM & OID_RT_PRO_WRITE16_EEPROM */ -struct eeprom_rw_param { - uint offset; - u16 value; -}; - -struct EFUSE_ACCESS_STRUCT { - u16 start_addr; - u16 cnts; - u8 data[]; -}; - -struct burst_rw_reg { - uint offset; - uint len; - u8 Data[256]; -}; - -struct usb_vendor_req { - u8 bRequest; - u16 wValue; - u16 wIndex; - u16 wLength; - u8 u8Dir;/*0:OUT, 1:IN */ - u8 u8InData; -}; - -struct DR_VARIABLE_STRUCT { - u8 offset; - u32 variable; -}; - -/* oid_rtl_seg_87_11_00 */ -uint oid_rt_pro_read_register_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_write_register_hdl(struct oid_par_priv *poid_par_priv); -/* oid_rtl_seg_81_80_00 */ -uint oid_rt_pro_set_data_rate_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_start_test_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_stop_test_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_channel_direct_call_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_antenna_bb_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_tx_power_control_hdl( - struct oid_par_priv *poid_par_priv); -/* oid_rtl_seg_81_80_20 */ -uint oid_rt_pro_query_tx_packet_sent_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_query_rx_packet_received_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_query_rx_packet_crc32_error_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_reset_tx_packet_sent_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_reset_rx_packet_received_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_continuous_tx_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_single_carrier_tx_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_carrier_suppression_tx_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_set_single_tone_tx_hdl( - struct oid_par_priv *poid_par_priv); -/* oid_rtl_seg_81_87 */ -uint oid_rt_pro_write_bb_reg_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_read_bb_reg_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_write_rf_reg_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_read_rf_reg_hdl(struct oid_par_priv *poid_par_priv); -/* oid_rtl_seg_81_85 */ -uint oid_rt_wireless_mode_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_read_efuse_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_write_efuse_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_get_efuse_current_size_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_efuse_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_pro_efuse_map_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_set_bandwidth_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_set_rx_packet_type_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_get_efuse_max_size_hdl(struct oid_par_priv *poid_par_priv); -uint oid_rt_get_thermal_meter_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_reset_phy_rx_packet_count_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_phy_rx_packet_received_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_phy_rx_packet_crc32_error_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_set_power_down_hdl( - struct oid_par_priv *poid_par_priv); -uint oid_rt_get_power_mode_hdl( - struct oid_par_priv *poid_par_priv); -#ifdef _RTL871X_MP_IOCTL_C_ /* CAUTION!!! */ -/* This ifdef _MUST_ be left in!! */ - -#else /* _RTL871X_MP_IOCTL_C_ */ -extern struct oid_obj_priv oid_rtl_seg_81_87[5]; -extern struct oid_obj_priv oid_rtl_seg_87_11_00[32]; -extern struct oid_obj_priv oid_rtl_seg_87_11_20[5]; -extern struct oid_obj_priv oid_rtl_seg_87_11_50[2]; -extern struct oid_obj_priv oid_rtl_seg_87_11_80[1]; -extern struct oid_obj_priv oid_rtl_seg_87_11_B0[1]; -extern struct oid_obj_priv oid_rtl_seg_87_11_F0[16]; -extern struct oid_obj_priv oid_rtl_seg_87_12_00[32]; - -#endif /* _RTL871X_MP_IOCTL_C_ */ - -enum MP_MODE { - MP_START_MODE, - MP_STOP_MODE, - MP_ERR_MODE -}; - -struct rwreg_param { - unsigned int offset; - unsigned int width; - unsigned int value; -}; - -struct bbreg_param { - unsigned int offset; - unsigned int phymask; - unsigned int value; -}; - -struct txpower_param { - unsigned int pwr_index; -}; - -struct datarate_param { - unsigned int rate_index; -}; - -struct rfintfs_parm { - unsigned int rfintfs; -}; - -struct mp_xmit_packet { - unsigned int len; -}; - -struct psmode_param { - unsigned int ps_mode; - unsigned int smart_ps; -}; - -struct mp_ioctl_handler { - unsigned int paramsize; - unsigned int (*handler)(struct oid_par_priv *poid_par_priv); - unsigned int oid; -}; - -struct mp_ioctl_param { - unsigned int subcode; - unsigned int len; - unsigned char data[]; -}; - -#define GEN_MP_IOCTL_SUBCODE(code) _MP_IOCTL_ ## code ## _CMD_ - -enum RTL871X_MP_IOCTL_SUBCODE { - GEN_MP_IOCTL_SUBCODE(MP_START), /*0*/ - GEN_MP_IOCTL_SUBCODE(MP_STOP), /*1*/ - GEN_MP_IOCTL_SUBCODE(READ_REG), /*2*/ - GEN_MP_IOCTL_SUBCODE(WRITE_REG), - GEN_MP_IOCTL_SUBCODE(SET_CHANNEL), /*4*/ - GEN_MP_IOCTL_SUBCODE(SET_TXPOWER), /*5*/ - GEN_MP_IOCTL_SUBCODE(SET_DATARATE), /*6*/ - GEN_MP_IOCTL_SUBCODE(READ_BB_REG), /*7*/ - GEN_MP_IOCTL_SUBCODE(WRITE_BB_REG), - GEN_MP_IOCTL_SUBCODE(READ_RF_REG), /*9*/ - GEN_MP_IOCTL_SUBCODE(WRITE_RF_REG), - GEN_MP_IOCTL_SUBCODE(SET_RF_INTFS), - GEN_MP_IOCTL_SUBCODE(IOCTL_XMIT_PACKET), /*12*/ - GEN_MP_IOCTL_SUBCODE(PS_STATE), /*13*/ - GEN_MP_IOCTL_SUBCODE(READ16_EEPROM), /*14*/ - GEN_MP_IOCTL_SUBCODE(WRITE16_EEPROM), /*15*/ - GEN_MP_IOCTL_SUBCODE(SET_PTM), /*16*/ - GEN_MP_IOCTL_SUBCODE(READ_TSSI), /*17*/ - GEN_MP_IOCTL_SUBCODE(CNTU_TX), /*18*/ - GEN_MP_IOCTL_SUBCODE(SET_BANDWIDTH), /*19*/ - GEN_MP_IOCTL_SUBCODE(SET_RX_PKT_TYPE), /*20*/ - GEN_MP_IOCTL_SUBCODE(RESET_PHY_RX_PKT_CNT), /*21*/ - GEN_MP_IOCTL_SUBCODE(GET_PHY_RX_PKT_RECV), /*22*/ - GEN_MP_IOCTL_SUBCODE(GET_PHY_RX_PKT_ERROR), /*23*/ - GEN_MP_IOCTL_SUBCODE(SET_POWER_DOWN), /*24*/ - GEN_MP_IOCTL_SUBCODE(GET_THERMAL_METER), /*25*/ - GEN_MP_IOCTL_SUBCODE(GET_POWER_MODE), /*26*/ - GEN_MP_IOCTL_SUBCODE(EFUSE), /*27*/ - GEN_MP_IOCTL_SUBCODE(EFUSE_MAP), /*28*/ - GEN_MP_IOCTL_SUBCODE(GET_EFUSE_MAX_SIZE), /*29*/ - GEN_MP_IOCTL_SUBCODE(GET_EFUSE_CURRENT_SIZE), /*30*/ - GEN_MP_IOCTL_SUBCODE(SC_TX), /*31*/ - GEN_MP_IOCTL_SUBCODE(CS_TX), /*32*/ - GEN_MP_IOCTL_SUBCODE(ST_TX), /*33*/ - GEN_MP_IOCTL_SUBCODE(SET_ANTENNA), /*34*/ - MAX_MP_IOCTL_SUBCODE, -}; - -unsigned int mp_ioctl_xmit_packet_hdl(struct oid_par_priv *poid_par_priv); - -#ifdef _RTL871X_MP_IOCTL_C_ /* CAUTION!!! */ -/* This ifdef _MUST_ be left in!! */ - -static struct mp_ioctl_handler mp_ioctl_hdl[] = { - {sizeof(u32), oid_rt_pro_start_test_hdl, - OID_RT_PRO_START_TEST},/*0*/ - {sizeof(u32), oid_rt_pro_stop_test_hdl, - OID_RT_PRO_STOP_TEST},/*1*/ - {sizeof(struct rwreg_param), - oid_rt_pro_read_register_hdl, - OID_RT_PRO_READ_REGISTER},/*2*/ - {sizeof(struct rwreg_param), - oid_rt_pro_write_register_hdl, - OID_RT_PRO_WRITE_REGISTER}, - {sizeof(u32), - oid_rt_pro_set_channel_direct_call_hdl, - OID_RT_PRO_SET_CHANNEL_DIRECT_CALL}, - {sizeof(struct txpower_param), - oid_rt_pro_set_tx_power_control_hdl, - OID_RT_PRO_SET_TX_POWER_CONTROL}, - {sizeof(u32), - oid_rt_pro_set_data_rate_hdl, - OID_RT_PRO_SET_DATA_RATE}, - {sizeof(struct bb_reg_param), - oid_rt_pro_read_bb_reg_hdl, - OID_RT_PRO_READ_BB_REG},/*7*/ - {sizeof(struct bb_reg_param), - oid_rt_pro_write_bb_reg_hdl, - OID_RT_PRO_WRITE_BB_REG}, - {sizeof(struct rwreg_param), - oid_rt_pro_read_rf_reg_hdl, - OID_RT_PRO_RF_READ_REGISTRY},/*9*/ - {sizeof(struct rwreg_param), - oid_rt_pro_write_rf_reg_hdl, - OID_RT_PRO_RF_WRITE_REGISTRY}, - {sizeof(struct rfintfs_parm), NULL, 0}, - {0, mp_ioctl_xmit_packet_hdl, 0},/*12*/ - {sizeof(struct psmode_param), NULL, 0},/*13*/ - {sizeof(struct eeprom_rw_param), NULL, 0},/*14*/ - {sizeof(struct eeprom_rw_param), NULL, 0},/*15*/ - {sizeof(unsigned char), NULL, 0},/*16*/ - {sizeof(u32), NULL, 0},/*17*/ - {sizeof(u32), oid_rt_pro_set_continuous_tx_hdl, - OID_RT_PRO_SET_CONTINUOUS_TX},/*18*/ - {sizeof(u32), oid_rt_set_bandwidth_hdl, - OID_RT_SET_BANDWIDTH},/*19*/ - {sizeof(u32), oid_rt_set_rx_packet_type_hdl, - OID_RT_SET_RX_PACKET_TYPE},/*20*/ - {0, oid_rt_reset_phy_rx_packet_count_hdl, - OID_RT_RESET_PHY_RX_PACKET_COUNT},/*21*/ - {sizeof(u32), oid_rt_get_phy_rx_packet_received_hdl, - OID_RT_GET_PHY_RX_PACKET_RECEIVED},/*22*/ - {sizeof(u32), oid_rt_get_phy_rx_packet_crc32_error_hdl, - OID_RT_GET_PHY_RX_PACKET_CRC32_ERROR},/*23*/ - {sizeof(unsigned char), oid_rt_set_power_down_hdl, - OID_RT_SET_POWER_DOWN},/*24*/ - {sizeof(u32), oid_rt_get_thermal_meter_hdl, - OID_RT_PRO_GET_THERMAL_METER},/*25*/ - {sizeof(u32), oid_rt_get_power_mode_hdl, - OID_RT_GET_POWER_MODE},/*26*/ - {sizeof(struct EFUSE_ACCESS_STRUCT), - oid_rt_pro_efuse_hdl, OID_RT_PRO_EFUSE},/*27*/ - {EFUSE_MAP_MAX_SIZE, oid_rt_pro_efuse_map_hdl, - OID_RT_PRO_EFUSE_MAP},/*28*/ - {sizeof(u32), oid_rt_get_efuse_max_size_hdl, - OID_RT_GET_EFUSE_MAX_SIZE},/*29*/ - {sizeof(u32), oid_rt_get_efuse_current_size_hdl, - OID_RT_GET_EFUSE_CURRENT_SIZE},/*30*/ - {sizeof(u32), oid_rt_pro_set_single_carrier_tx_hdl, - OID_RT_PRO_SET_SINGLE_CARRIER_TX},/*31*/ - {sizeof(u32), oid_rt_pro_set_carrier_suppression_tx_hdl, - OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX},/*32*/ - {sizeof(u32), oid_rt_pro_set_single_tone_tx_hdl, - OID_RT_PRO_SET_SINGLE_TONE_TX},/*33*/ - {sizeof(u32), oid_rt_pro_set_antenna_bb_hdl, - OID_RT_PRO_SET_ANTENNA_BB},/*34*/ -}; - -#else /* _RTL871X_MP_IOCTL_C_ */ -extern struct mp_ioctl_handler mp_ioctl_hdl[]; -#endif /* _RTL871X_MP_IOCTL_C_ */ - -#endif - diff --git a/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h b/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h deleted file mode 100644 index bb9f83d58225d..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h +++ /dev/null @@ -1,1034 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/***************************************************************************** - * Copyright(c) 2008, RealTEK Technology Inc. All Right Reserved. - * - * Module: __INC_HAL8192SPHYREG_H - * - * - * Note: 1. Define PMAC/BB register map - * 2. Define RF register map - * 3. PMAC/BB register bit mask. - * 4. RF reg bit mask. - * 5. Other BB/RF relative definition. - * - * - * Export: Constants, macro, functions(API), global variables(None). - * - * Abbrev: - * - * History: - * Data Who Remark - * 08/07/2007 MHC 1. Porting from 9x series PHYCFG.h. - * 2. Reorganize code architecture. - * 09/25/2008 MH 1. Add RL6052 register definition - * - *****************************************************************************/ -#ifndef __RTL871X_MP_PHY_REGDEF_H -#define __RTL871X_MP_PHY_REGDEF_H - -/*--------------------------Define Parameters-------------------------------*/ - -/*============================================================ - * 8192S Register offset definition - *============================================================ - * - * - * BB-PHY register PMAC 0x100 PHY 0x800 - 0xEFF - * 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF - * 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00 - * 3. RF register 0x00-2E - * 4. Bit Mask for BB/RF register - * 5. Other definition for BB/RF R/W - * - * 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF - * 1. Page1(0x100) - */ -#define rPMAC_Reset 0x100 -#define rPMAC_TxStart 0x104 -#define rPMAC_TxLegacySIG 0x108 -#define rPMAC_TxHTSIG1 0x10c -#define rPMAC_TxHTSIG2 0x110 -#define rPMAC_PHYDebug 0x114 -#define rPMAC_TxPacketNum 0x118 -#define rPMAC_TxIdle 0x11c -#define rPMAC_TxMACHeader0 0x120 -#define rPMAC_TxMACHeader1 0x124 -#define rPMAC_TxMACHeader2 0x128 -#define rPMAC_TxMACHeader3 0x12c -#define rPMAC_TxMACHeader4 0x130 -#define rPMAC_TxMACHeader5 0x134 -#define rPMAC_TxDataType 0x138 -#define rPMAC_TxRandomSeed 0x13c -#define rPMAC_CCKPLCPPreamble 0x140 -#define rPMAC_CCKPLCPHeader 0x144 -#define rPMAC_CCKCRC16 0x148 -#define rPMAC_OFDMRxCRC32OK 0x170 -#define rPMAC_OFDMRxCRC32Er 0x174 -#define rPMAC_OFDMRxParityEr 0x178 -#define rPMAC_OFDMRxCRC8Er 0x17c -#define rPMAC_CCKCRxRC16Er 0x180 -#define rPMAC_CCKCRxRC32Er 0x184 -#define rPMAC_CCKCRxRC32OK 0x188 -#define rPMAC_TxStatus 0x18c - -/* - * 2. Page2(0x200) - * - * The following two definition are only used for USB interface. - *#define RF_BB_CMD_ADDR 0x02c0 // RF/BB read/write command address. - *#define RF_BB_CMD_DATA 0x02c4 // RF/BB read/write command data. - * - * - * 3. Page8(0x800) - */ -#define rFPGA0_RFMOD 0x800 /*RF mode & CCK TxSC RF - * BW Setting?? - */ -#define rFPGA0_TxInfo 0x804 /* Status report?? */ -#define rFPGA0_PSDFunction 0x808 -#define rFPGA0_TxGainStage 0x80c /* Set TX PWR init gain? */ -#define rFPGA0_RFTiming1 0x810 /* Useless now */ -#define rFPGA0_RFTiming2 0x814 -#define rFPGA0_XA_HSSIParameter1 0x820 /* RF 3 wire register */ -#define rFPGA0_XA_HSSIParameter2 0x824 -#define rFPGA0_XB_HSSIParameter1 0x828 -#define rFPGA0_XB_HSSIParameter2 0x82c -#define rFPGA0_XC_HSSIParameter1 0x830 -#define rFPGA0_XC_HSSIParameter2 0x834 -#define rFPGA0_XD_HSSIParameter1 0x838 -#define rFPGA0_XD_HSSIParameter2 0x83c -#define rFPGA0_XA_LSSIParameter 0x840 -#define rFPGA0_XB_LSSIParameter 0x844 -#define rFPGA0_XC_LSSIParameter 0x848 -#define rFPGA0_XD_LSSIParameter 0x84c - -#define rFPGA0_RFWakeUpParameter 0x850 /* Useless now */ -#define rFPGA0_RFSleepUpParameter 0x854 - -#define rFPGA0_XAB_SwitchControl 0x858 /* RF Channel switch */ -#define rFPGA0_XCD_SwitchControl 0x85c - -#define rFPGA0_XA_RFInterfaceOE 0x860 /* RF Channel switch */ -#define rFPGA0_XB_RFInterfaceOE 0x864 -#define rFPGA0_XC_RFInterfaceOE 0x868 -#define rFPGA0_XD_RFInterfaceOE 0x86c -#define rFPGA0_XAB_RFInterfaceSW 0x870 /* RF Interface Software Ctrl */ -#define rFPGA0_XCD_RFInterfaceSW 0x874 - -#define rFPGA0_XAB_RFParameter 0x878 /* RF Parameter */ -#define rFPGA0_XCD_RFParameter 0x87c - -#define rFPGA0_AnalogParameter1 0x880 /* Crystal cap setting - * RF-R/W protection - * for parameter4?? - */ -#define rFPGA0_AnalogParameter2 0x884 -#define rFPGA0_AnalogParameter3 0x888 /* Useless now */ -#define rFPGA0_AnalogParameter4 0x88c - -#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Transceiver LSSI Readback */ -#define rFPGA0_XB_LSSIReadBack 0x8a4 -#define rFPGA0_XC_LSSIReadBack 0x8a8 -#define rFPGA0_XD_LSSIReadBack 0x8ac - -#define rFPGA0_PSDReport 0x8b4 /* Useless now */ -#define rFPGA0_XAB_RFInterfaceRB 0x8e0 /* Useless now */ -#define rFPGA0_XCD_RFInterfaceRB 0x8e4 /* Useless now */ - -/* - * 4. Page9(0x900) - */ -#define rFPGA1_RFMOD 0x900 /* RF mode & OFDM TxSC */ - -#define rFPGA1_TxBlock 0x904 /* Useless now */ -#define rFPGA1_DebugSelect 0x908 /* Useless now */ -#define rFPGA1_TxInfo 0x90c /* Useless now */ - -/* - * 5. PageA(0xA00) - * - * Set Control channel to upper or lower. - * These settings are required only for 40MHz - */ -#define rCCK0_System 0xa00 - -#define rCCK0_AFESetting 0xa04 /* Disable init gain now */ -#define rCCK0_CCA 0xa08 /* Disable init gain now */ - -#define rCCK0_RxAGC1 0xa0c -/* AGC default value, saturation level - * Antenna Diversity, RX AGC, LNA Threshold, RX LNA Threshold useless now. - * Not the same as 90 series - */ -#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */ - -#define rCCK0_RxHP 0xa14 - -#define rCCK0_DSPParameter1 0xa18 /* Timing recovery & Channel - * estimation threshold - */ -#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */ - -#define rCCK0_TxFilter1 0xa20 -#define rCCK0_TxFilter2 0xa24 -#define rCCK0_DebugPort 0xa28 /* debug port and Tx filter3 */ -#define rCCK0_FalseAlarmReport 0xa2c /* 0xa2d useless now 0xa30-a4f - * channel report - */ -#define rCCK0_TRSSIReport 0xa50 -#define rCCK0_RxReport 0xa54 /* 0xa57 */ -#define rCCK0_FACounterLower 0xa5c /* 0xa5b */ -#define rCCK0_FACounterUpper 0xa58 /* 0xa5c */ - -/* - * 6. PageC(0xC00) - */ -#define rOFDM0_LSTF 0xc00 -#define rOFDM0_TRxPathEnable 0xc04 -#define rOFDM0_TRMuxPar 0xc08 -#define rOFDM0_TRSWIsolation 0xc0c - -/*RxIQ DC offset, Rx digital filter, DC notch filter */ -#define rOFDM0_XARxAFE 0xc10 -#define rOFDM0_XARxIQImbalance 0xc14 /* RxIQ imbalance matrix */ -#define rOFDM0_XBRxAFE 0xc18 -#define rOFDM0_XBRxIQImbalance 0xc1c -#define rOFDM0_XCRxAFE 0xc20 -#define rOFDM0_XCRxIQImbalance 0xc24 -#define rOFDM0_XDRxAFE 0xc28 -#define rOFDM0_XDRxIQImbalance 0xc2c - -#define rOFDM0_RxDetector1 0xc30 /* PD,BW & SBD DM tune - * init gain - */ -#define rOFDM0_RxDetector2 0xc34 /* SBD & Fame Sync. */ -#define rOFDM0_RxDetector3 0xc38 /* Frame Sync. */ -#define rOFDM0_RxDetector4 0xc3c /* PD, SBD, Frame Sync & - * Short-GI - */ - -#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */ -#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */ -#define rOFDM0_CCADropThreshold 0xc48 /* CCA Drop threshold */ -#define rOFDM0_ECCAThreshold 0xc4c /* energy CCA */ - -#define rOFDM0_XAAGCCore1 0xc50 /* DIG */ -#define rOFDM0_XAAGCCore2 0xc54 -#define rOFDM0_XBAGCCore1 0xc58 -#define rOFDM0_XBAGCCore2 0xc5c -#define rOFDM0_XCAGCCore1 0xc60 -#define rOFDM0_XCAGCCore2 0xc64 -#define rOFDM0_XDAGCCore1 0xc68 -#define rOFDM0_XDAGCCore2 0xc6c -#define rOFDM0_AGCParameter1 0xc70 -#define rOFDM0_AGCParameter2 0xc74 -#define rOFDM0_AGCRSSITable 0xc78 -#define rOFDM0_HTSTFAGC 0xc7c - -#define rOFDM0_XATxIQImbalance 0xc80 /* TX PWR TRACK and DIG */ -#define rOFDM0_XATxAFE 0xc84 -#define rOFDM0_XBTxIQImbalance 0xc88 -#define rOFDM0_XBTxAFE 0xc8c -#define rOFDM0_XCTxIQImbalance 0xc90 -#define rOFDM0_XCTxAFE 0xc94 -#define rOFDM0_XDTxIQImbalance 0xc98 -#define rOFDM0_XDTxAFE 0xc9c - -#define rOFDM0_RxHPParameter 0xce0 -#define rOFDM0_TxPseudoNoiseWgt 0xce4 -#define rOFDM0_FrameSync 0xcf0 -#define rOFDM0_DFSReport 0xcf4 -#define rOFDM0_TxCoeff1 0xca4 -#define rOFDM0_TxCoeff2 0xca8 -#define rOFDM0_TxCoeff3 0xcac -#define rOFDM0_TxCoeff4 0xcb0 -#define rOFDM0_TxCoeff5 0xcb4 -#define rOFDM0_TxCoeff6 0xcb8 - -/* - * 7. PageD(0xD00) - */ -#define rOFDM1_LSTF 0xd00 -#define rOFDM1_TRxPathEnable 0xd04 - -#define rOFDM1_CFO 0xd08 /* No setting now */ -#define rOFDM1_CSI1 0xd10 -#define rOFDM1_SBD 0xd14 -#define rOFDM1_CSI2 0xd18 -#define rOFDM1_CFOTracking 0xd2c -#define rOFDM1_TRxMesaure1 0xd34 -#define rOFDM1_IntfDet 0xd3c -#define rOFDM1_PseudoNoiseStateAB 0xd50 -#define rOFDM1_PseudoNoiseStateCD 0xd54 -#define rOFDM1_RxPseudoNoiseWgt 0xd58 - -#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */ -#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */ -#define rOFDM_PHYCounter3 0xda8 /* MCS not support */ -#define rOFDM_ShortCFOAB 0xdac /* No setting now */ -#define rOFDM_ShortCFOCD 0xdb0 -#define rOFDM_LongCFOAB 0xdb4 -#define rOFDM_LongCFOCD 0xdb8 -#define rOFDM_TailCFOAB 0xdbc -#define rOFDM_TailCFOCD 0xdc0 -#define rOFDM_PWMeasure1 0xdc4 -#define rOFDM_PWMeasure2 0xdc8 -#define rOFDM_BWReport 0xdcc -#define rOFDM_AGCReport 0xdd0 -#define rOFDM_RxSNR 0xdd4 -#define rOFDM_RxEVMCSI 0xdd8 -#define rOFDM_SIGReport 0xddc - -/* - * 8. PageE(0xE00) - */ -#define rTxAGC_Rate18_06 0xe00 -#define rTxAGC_Rate54_24 0xe04 -#define rTxAGC_CCK_Mcs32 0xe08 -#define rTxAGC_Mcs03_Mcs00 0xe10 -#define rTxAGC_Mcs07_Mcs04 0xe14 -#define rTxAGC_Mcs11_Mcs08 0xe18 -#define rTxAGC_Mcs15_Mcs12 0xe1c - -/* Analog- control in RX_WAIT_CCA : REG: EE0 - * [Analog- Power & Control Register] - */ -#define rRx_Wait_CCCA 0xe70 -#define rAnapar_Ctrl_BB 0xee0 - -/* - * 7. RF Register 0x00-0x2E (RF 8256) - * RF-0222D 0x00-3F - * - * Zebra1 - */ -#define rZebra1_HSSIEnable 0x0 /* Useless now */ -#define rZebra1_TRxEnable1 0x1 -#define rZebra1_TRxEnable2 0x2 -#define rZebra1_AGC 0x4 -#define rZebra1_ChargePump 0x5 -#define rZebra1_Channel 0x7 /* RF channel switch */ -#define rZebra1_TxGain 0x8 /* Useless now */ -#define rZebra1_TxLPF 0x9 -#define rZebra1_RxLPF 0xb -#define rZebra1_RxHPFCorner 0xc - -/* Zebra4 */ -#define rGlobalCtrl 0 /* Useless now */ -#define rRTL8256_TxLPF 19 -#define rRTL8256_RxLPF 11 - -/* RTL8258 */ -#define rRTL8258_TxLPF 0x11 /* Useless now */ -#define rRTL8258_RxLPF 0x13 -#define rRTL8258_RSSILPF 0xa - -/* RL6052 Register definition */ -#define RF_AC 0x00 -#define RF_IQADJ_G1 0x01 -#define RF_IQADJ_G2 0x02 -#define RF_POW_TRSW 0x05 - -#define RF_GAIN_RX 0x06 -#define RF_GAIN_TX 0x07 - -#define RF_TXM_IDAC 0x08 -#define RF_BS_IQGEN 0x0F - -#define RF_MODE1 0x10 -#define RF_MODE2 0x11 - -#define RF_RX_AGC_HP 0x12 -#define RF_TX_AGC 0x13 -#define RF_BIAS 0x14 -#define RF_IPA 0x15 -#define RF_POW_ABILITY 0x17 -#define RF_MODE_AG 0x18 -#define rRfChannel 0x18 /* RF channel and BW switch */ -#define RF_CHNLBW 0x18 /* RF channel and BW switch */ -#define RF_TOP 0x19 -#define RF_RX_G1 0x1A -#define RF_RX_G2 0x1B -#define RF_RX_BB2 0x1C -#define RF_RX_BB1 0x1D - -#define RF_RCK1 0x1E -#define RF_RCK2 0x1F - -#define RF_TX_G1 0x20 -#define RF_TX_G2 0x21 -#define RF_TX_G3 0x22 - -#define RF_TX_BB1 0x23 -#define RF_T_METER 0x24 - -#define RF_SYN_G1 0x25 /* RF TX Power control */ -#define RF_SYN_G2 0x26 /* RF TX Power control */ -#define RF_SYN_G3 0x27 /* RF TX Power control */ -#define RF_SYN_G4 0x28 /* RF TX Power control */ -#define RF_SYN_G5 0x29 /* RF TX Power control */ -#define RF_SYN_G6 0x2A /* RF TX Power control */ -#define RF_SYN_G7 0x2B /* RF TX Power control */ -#define RF_SYN_G8 0x2C /* RF TX Power control */ - -#define RF_RCK_OS 0x30 /* RF TX PA control */ - -#define RF_TXPA_G1 0x31 /* RF TX PA control */ -#define RF_TXPA_G2 0x32 /* RF TX PA control */ -#define RF_TXPA_G3 0x33 /* RF TX PA control */ - -/* - * Bit Mask - * - * 1. Page1(0x100) - */ -#define bBBResetB 0x100 /* Useless now? */ -#define bGlobalResetB 0x200 -#define bOFDMTxStart 0x4 -#define bCCKTxStart 0x8 -#define bCRC32Debug 0x100 -#define bPMACLoopback 0x10 -#define bTxLSIG 0xffffff -#define bOFDMTxRate 0xf -#define bOFDMTxReserved 0x10 -#define bOFDMTxLength 0x1ffe0 -#define bOFDMTxParity 0x20000 -#define bTxHTSIG1 0xffffff -#define bTxHTMCSRate 0x7f -#define bTxHTBW 0x80 -#define bTxHTLength 0xffff00 -#define bTxHTSIG2 0xffffff -#define bTxHTSmoothing 0x1 -#define bTxHTSounding 0x2 -#define bTxHTReserved 0x4 -#define bTxHTAggreation 0x8 -#define bTxHTSTBC 0x30 -#define bTxHTAdvanceCoding 0x40 -#define bTxHTShortGI 0x80 -#define bTxHTNumberHT_LTF 0x300 -#define bTxHTCRC8 0x3fc00 -#define bCounterReset 0x10000 -#define bNumOfOFDMTx 0xffff -#define bNumOfCCKTx 0xffff0000 -#define bTxIdleInterval 0xffff -#define bOFDMService 0xffff0000 -#define bTxMACHeader 0xffffffff -#define bTxDataInit 0xff -#define bTxHTMode 0x100 -#define bTxDataType 0x30000 -#define bTxRandomSeed 0xffffffff -#define bCCKTxPreamble 0x1 -#define bCCKTxSFD 0xffff0000 -#define bCCKTxSIG 0xff -#define bCCKTxService 0xff00 -#define bCCKLengthExt 0x8000 -#define bCCKTxLength 0xffff0000 -#define bCCKTxCRC16 0xffff -#define bCCKTxStatus 0x1 -#define bOFDMTxStatus 0x2 -#define IS_BB_REG_OFFSET_92S(_Offset) ((_Offset >= 0x800) && \ - (_Offset <= 0xfff)) - -/* 2. Page8(0x800) */ -#define bRFMOD 0x1 /* Reg 0x800 rFPGA0_RFMOD */ -#define bJapanMode 0x2 -#define bCCKTxSC 0x30 -#define bCCKEn 0x1000000 -#define bOFDMEn 0x2000000 - -#define bOFDMRxADCPhase 0x10000 /* Useless now */ -#define bOFDMTxDACPhase 0x40000 -#define bXATxAGC 0x3f -#define bXBTxAGC 0xf00 /* Reg 80c rFPGA0_TxGainStage */ -#define bXCTxAGC 0xf000 -#define bXDTxAGC 0xf0000 - -#define bPAStart 0xf0000000 /* Useless now */ -#define bTRStart 0x00f00000 -#define bRFStart 0x0000f000 -#define bBBStart 0x000000f0 -#define bBBCCKStart 0x0000000f -#define bPAEnd 0xf /* Reg0x814 */ -#define bTREnd 0x0f000000 -#define bRFEnd 0x000f0000 -#define bCCAMask 0x000000f0 /* T2R */ -#define bR2RCCAMask 0x00000f00 -#define bHSSI_R2TDelay 0xf8000000 -#define bHSSI_T2RDelay 0xf80000 -#define bContTxHSSI 0x400 /* change gain at continue Tx */ -#define bIGFromCCK 0x200 -#define bAGCAddress 0x3f -#define bRxHPTx 0x7000 -#define bRxHPT2R 0x38000 -#define bRxHPCCKIni 0xc0000 -#define bAGCTxCode 0xc00000 -#define bAGCRxCode 0x300000 -#define b3WireDataLength 0x800 /* Reg 0x820~84f rFPGA0_XA_HSSIParm1 */ -#define b3WireAddressLength 0x400 -#define b3WireRFPowerDown 0x1 /* Useless now */ -#define b5GPAPEPolarity 0x40000000 -#define b2GPAPEPolarity 0x80000000 -#define bRFSW_TxDefaultAnt 0x3 -#define bRFSW_TxOptionAnt 0x30 -#define bRFSW_RxDefaultAnt 0x300 -#define bRFSW_RxOptionAnt 0x3000 -#define bRFSI_3WireData 0x1 -#define bRFSI_3WireClock 0x2 -#define bRFSI_3WireLoad 0x4 -#define bRFSI_3WireRW 0x8 -#define bRFSI_3Wire 0xf -#define bRFSI_RFENV 0x10 /* Reg 0x870 rFPGA0_XAB_RFInterfaceSW */ -#define bRFSI_TRSW 0x20 /* Useless now */ -#define bRFSI_TRSWB 0x40 -#define bRFSI_ANTSW 0x100 -#define bRFSI_ANTSWB 0x200 -#define bRFSI_PAPE 0x400 -#define bRFSI_PAPE5G 0x800 -#define bBandSelect 0x1 -#define bHTSIG2_GI 0x80 -#define bHTSIG2_Smoothing 0x01 -#define bHTSIG2_Sounding 0x02 -#define bHTSIG2_Aggreaton 0x08 -#define bHTSIG2_STBC 0x30 -#define bHTSIG2_AdvCoding 0x40 -#define bHTSIG2_NumOfHTLTF 0x300 -#define bHTSIG2_CRC8 0x3fc -#define bHTSIG1_MCS 0x7f -#define bHTSIG1_BandWidth 0x80 -#define bHTSIG1_HTLength 0xffff -#define bLSIG_Rate 0xf -#define bLSIG_Reserved 0x10 -#define bLSIG_Length 0x1fffe -#define bLSIG_Parity 0x20 -#define bCCKRxPhase 0x4 -#define bLSSIReadAddress 0x7f800000 /* T65 RF */ -#define bLSSIReadEdge 0x80000000 /* LSSI "Read" edge signal */ -#define bLSSIReadBackData 0xfffff /* T65 RF */ -#define bLSSIReadOKFlag 0x1000 /* Useless now */ -#define bCCKSampleRate 0x8 /*0: 44MHz, 1:88MHz*/ -#define bRegulator0Standby 0x1 -#define bRegulatorPLLStandby 0x2 -#define bRegulator1Standby 0x4 -#define bPLLPowerUp 0x8 -#define bDPLLPowerUp 0x10 -#define bDA10PowerUp 0x20 -#define bAD7PowerUp 0x200 -#define bDA6PowerUp 0x2000 -#define bXtalPowerUp 0x4000 -#define b40MDClkPowerUP 0x8000 -#define bDA6DebugMode 0x20000 -#define bDA6Swing 0x380000 - -/* Reg 0x880 rFPGA0_AnalogParameter1 20/40 CCK support switch 40/80 BB MHZ */ -#define bADClkPhase 0x4000000 - -#define b80MClkDelay 0x18000000 /* Useless */ -#define bAFEWatchDogEnable 0x20000000 - -/* Reg 0x884 rFPGA0_AnalogParameter2 Crystal cap */ -#define bXtalCap01 0xc0000000 -#define bXtalCap23 0x3 -#define bXtalCap92x 0x0f000000 -#define bXtalCap 0x0f000000 -#define bIntDifClkEnable 0x400 /* Useless */ -#define bExtSigClkEnable 0x800 -#define bBandgapMbiasPowerUp 0x10000 -#define bAD11SHGain 0xc0000 -#define bAD11InputRange 0x700000 -#define bAD11OPCurrent 0x3800000 -#define bIPathLoopback 0x4000000 -#define bQPathLoopback 0x8000000 -#define bAFELoopback 0x10000000 -#define bDA10Swing 0x7e0 -#define bDA10Reverse 0x800 -#define bDAClkSource 0x1000 -#define bAD7InputRange 0x6000 -#define bAD7Gain 0x38000 -#define bAD7OutputCMMode 0x40000 -#define bAD7InputCMMode 0x380000 -#define bAD7Current 0xc00000 -#define bRegulatorAdjust 0x7000000 -#define bAD11PowerUpAtTx 0x1 -#define bDA10PSAtTx 0x10 -#define bAD11PowerUpAtRx 0x100 -#define bDA10PSAtRx 0x1000 -#define bCCKRxAGCFormat 0x200 -#define bPSDFFTSamplepPoint 0xc000 -#define bPSDAverageNum 0x3000 -#define bIQPathControl 0xc00 -#define bPSDFreq 0x3ff -#define bPSDAntennaPath 0x30 -#define bPSDIQSwitch 0x40 -#define bPSDRxTrigger 0x400000 -#define bPSDTxTrigger 0x80000000 -#define bPSDSineToneScale 0x7f000000 -#define bPSDReport 0xffff - -/* 3. Page9(0x900) */ -#define bOFDMTxSC 0x30000000 /* Useless */ -#define bCCKTxOn 0x1 -#define bOFDMTxOn 0x2 -#define bDebugPage 0xfff /* reset debug page and HWord, LWord */ -#define bDebugItem 0xff /* reset debug page and LWord */ -#define bAntL 0x10 -#define bAntNonHT 0x100 -#define bAntHT1 0x1000 -#define bAntHT2 0x10000 -#define bAntHT1S1 0x100000 -#define bAntNonHTS1 0x1000000 - -/* 4. PageA(0xA00) */ -#define bCCKBBMode 0x3 /* Useless */ -#define bCCKTxPowerSaving 0x80 -#define bCCKRxPowerSaving 0x40 - -#define bCCKSideBand 0x10 /* Reg 0xa00 rCCK0_System 20/40 switch*/ -#define bCCKScramble 0x8 /* Useless */ -#define bCCKAntDiversity 0x8000 -#define bCCKCarrierRecovery 0x4000 -#define bCCKTxRate 0x3000 -#define bCCKDCCancel 0x0800 -#define bCCKISICancel 0x0400 -#define bCCKMatchFilter 0x0200 -#define bCCKEqualizer 0x0100 -#define bCCKPreambleDetect 0x800000 -#define bCCKFastFalseCCA 0x400000 -#define bCCKChEstStart 0x300000 -#define bCCKCCACount 0x080000 -#define bCCKcs_lim 0x070000 -#define bCCKBistMode 0x80000000 -#define bCCKCCAMask 0x40000000 -#define bCCKTxDACPhase 0x4 -#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */ -#define bCCKr_cp_mode0 0x0100 -#define bCCKTxDCOffset 0xf0 -#define bCCKRxDCOffset 0xf -#define bCCKCCAMode 0xc000 -#define bCCKFalseCS_lim 0x3f00 -#define bCCKCS_ratio 0xc00000 -#define bCCKCorgBit_sel 0x300000 -#define bCCKPD_lim 0x0f0000 -#define bCCKNewCCA 0x80000000 -#define bCCKRxHPofIG 0x8000 -#define bCCKRxIG 0x7f00 -#define bCCKLNAPolarity 0x800000 -#define bCCKRx1stGain 0x7f0000 -#define bCCKRFExtend 0x20000000 /* CCK Rx initial gain polarity */ -#define bCCKRxAGCSatLevel 0x1f000000 -#define bCCKRxAGCSatCount 0xe0 -#define bCCKRxRFSettle 0x1f /* AGCsamp_dly */ -#define bCCKFixedRxAGC 0x8000 -#define bCCKAntennaPolarity 0x2000 -#define bCCKTxFilterType 0x0c00 -#define bCCKRxAGCReportType 0x0300 -#define bCCKRxDAGCEn 0x80000000 -#define bCCKRxDAGCPeriod 0x20000000 -#define bCCKRxDAGCSatLevel 0x1f000000 -#define bCCKTimingRecovery 0x800000 -#define bCCKTxC0 0x3f0000 -#define bCCKTxC1 0x3f000000 -#define bCCKTxC2 0x3f -#define bCCKTxC3 0x3f00 -#define bCCKTxC4 0x3f0000 -#define bCCKTxC5 0x3f000000 -#define bCCKTxC6 0x3f -#define bCCKTxC7 0x3f00 -#define bCCKDebugPort 0xff0000 -#define bCCKDACDebug 0x0f000000 -#define bCCKFalseAlarmEnable 0x8000 -#define bCCKFalseAlarmRead 0x4000 -#define bCCKTRSSI 0x7f -#define bCCKRxAGCReport 0xfe -#define bCCKRxReport_AntSel 0x80000000 -#define bCCKRxReport_MFOff 0x40000000 -#define bCCKRxRxReport_SQLoss 0x20000000 -#define bCCKRxReport_Pktloss 0x10000000 -#define bCCKRxReport_Lockedbit 0x08000000 -#define bCCKRxReport_RateError 0x04000000 -#define bCCKRxReport_RxRate 0x03000000 -#define bCCKRxFACounterLower 0xff -#define bCCKRxFACounterUpper 0xff000000 -#define bCCKRxHPAGCStart 0xe000 -#define bCCKRxHPAGCFinal 0x1c00 -#define bCCKRxFalseAlarmEnable 0x8000 -#define bCCKFACounterFreeze 0x4000 -#define bCCKTxPathSel 0x10000000 -#define bCCKDefaultRxPath 0xc000000 -#define bCCKOptionRxPath 0x3000000 - -/* 5. PageC(0xC00) */ -#define bNumOfSTF 0x3 /* Useless */ -#define bShift_L 0xc0 -#define bGI_TH 0xc -#define bRxPathA 0x1 -#define bRxPathB 0x2 -#define bRxPathC 0x4 -#define bRxPathD 0x8 -#define bTxPathA 0x1 -#define bTxPathB 0x2 -#define bTxPathC 0x4 -#define bTxPathD 0x8 -#define bTRSSIFreq 0x200 -#define bADCBackoff 0x3000 -#define bDFIRBackoff 0xc000 -#define bTRSSILatchPhase 0x10000 -#define bRxIDCOffset 0xff -#define bRxQDCOffset 0xff00 -#define bRxDFIRMode 0x1800000 -#define bRxDCNFType 0xe000000 -#define bRXIQImb_A 0x3ff -#define bRXIQImb_B 0xfc00 -#define bRXIQImb_C 0x3f0000 -#define bRXIQImb_D 0xffc00000 -#define bDC_dc_Notch 0x60000 -#define bRxNBINotch 0x1f000000 -#define bPD_TH 0xf -#define bPD_TH_Opt2 0xc000 -#define bPWED_TH 0x700 -#define bIfMF_Win_L 0x800 -#define bPD_Option 0x1000 -#define bMF_Win_L 0xe000 -#define bBW_Search_L 0x30000 -#define bwin_enh_L 0xc0000 -#define bBW_TH 0x700000 -#define bED_TH2 0x3800000 -#define bBW_option 0x4000000 -#define bRatio_TH 0x18000000 -#define bWindow_L 0xe0000000 -#define bSBD_Option 0x1 -#define bFrame_TH 0x1c -#define bFS_Option 0x60 -#define bDC_Slope_check 0x80 -#define bFGuard_Counter_DC_L 0xe00 -#define bFrame_Weight_Short 0x7000 -#define bSub_Tune 0xe00000 -#define bFrame_DC_Length 0xe000000 -#define bSBD_start_offset 0x30000000 -#define bFrame_TH_2 0x7 -#define bFrame_GI2_TH 0x38 -#define bGI2_Sync_en 0x40 -#define bSarch_Short_Early 0x300 -#define bSarch_Short_Late 0xc00 -#define bSarch_GI2_Late 0x70000 -#define bCFOAntSum 0x1 -#define bCFOAcc 0x2 -#define bCFOStartOffset 0xc -#define bCFOLookBack 0x70 -#define bCFOSumWeight 0x80 -#define bDAGCEnable 0x10000 -#define bTXIQImb_A 0x3ff -#define bTXIQImb_B 0xfc00 -#define bTXIQImb_C 0x3f0000 -#define bTXIQImb_D 0xffc00000 -#define bTxIDCOffset 0xff -#define bTxQDCOffset 0xff00 -#define bTxDFIRMode 0x10000 -#define bTxPesudoNoiseOn 0x4000000 -#define bTxPesudoNoise_A 0xff -#define bTxPesudoNoise_B 0xff00 -#define bTxPesudoNoise_C 0xff0000 -#define bTxPesudoNoise_D 0xff000000 -#define bCCADropOption 0x20000 -#define bCCADropThres 0xfff00000 -#define bEDCCA_H 0xf -#define bEDCCA_L 0xf0 -#define bLambda_ED 0x300 -#define bRxInitialGain 0x7f -#define bRxAntDivEn 0x80 -#define bRxAGCAddressForLNA 0x7f00 -#define bRxHighPowerFlow 0x8000 -#define bRxAGCFreezeThres 0xc0000 -#define bRxFreezeStep_AGC1 0x300000 -#define bRxFreezeStep_AGC2 0xc00000 -#define bRxFreezeStep_AGC3 0x3000000 -#define bRxFreezeStep_AGC0 0xc000000 -#define bRxRssi_Cmp_En 0x10000000 -#define bRxQuickAGCEn 0x20000000 -#define bRxAGCFreezeThresMode 0x40000000 -#define bRxOverFlowCheckType 0x80000000 -#define bRxAGCShift 0x7f -#define bTRSW_Tri_Only 0x80 -#define bPowerThres 0x300 -#define bRxAGCEn 0x1 -#define bRxAGCTogetherEn 0x2 -#define bRxAGCMin 0x4 -#define bRxHP_Ini 0x7 -#define bRxHP_TRLNA 0x70 -#define bRxHP_RSSI 0x700 -#define bRxHP_BBP1 0x7000 -#define bRxHP_BBP2 0x70000 -#define bRxHP_BBP3 0x700000 -#define bRSSI_H 0x7f0000 /* the threshold for high power */ -#define bRSSI_Gen 0x7f000000 /* the threshold for ant divers */ -#define bRxSettle_TRSW 0x7 -#define bRxSettle_LNA 0x38 -#define bRxSettle_RSSI 0x1c0 -#define bRxSettle_BBP 0xe00 -#define bRxSettle_RxHP 0x7000 -#define bRxSettle_AntSW_RSSI 0x38000 -#define bRxSettle_AntSW 0xc0000 -#define bRxProcessTime_DAGC 0x300000 -#define bRxSettle_HSSI 0x400000 -#define bRxProcessTime_BBPPW 0x800000 -#define bRxAntennaPowerShift 0x3000000 -#define bRSSITableSelect 0xc000000 -#define bRxHP_Final 0x7000000 -#define bRxHTSettle_BBP 0x7 -#define bRxHTSettle_HSSI 0x8 -#define bRxHTSettle_RxHP 0x70 -#define bRxHTSettle_BBPPW 0x80 -#define bRxHTSettle_Idle 0x300 -#define bRxHTSettle_Reserved 0x1c00 -#define bRxHTRxHPEn 0x8000 -#define bRxHTAGCFreezeThres 0x30000 -#define bRxHTAGCTogetherEn 0x40000 -#define bRxHTAGCMin 0x80000 -#define bRxHTAGCEn 0x100000 -#define bRxHTDAGCEn 0x200000 -#define bRxHTRxHP_BBP 0x1c00000 -#define bRxHTRxHP_Final 0xe0000000 -#define bRxPWRatioTH 0x3 -#define bRxPWRatioEn 0x4 -#define bRxMFHold 0x3800 -#define bRxPD_Delay_TH1 0x38 -#define bRxPD_Delay_TH2 0x1c0 -#define bRxPD_DC_COUNT_MAX 0x600 -#define bRxPD_Delay_TH 0x8000 -#define bRxProcess_Delay 0xf0000 -#define bRxSearchrange_GI2_Early 0x700000 -#define bRxFrame_Guard_Counter_L 0x3800000 -#define bRxSGI_Guard_L 0xc000000 -#define bRxSGI_Search_L 0x30000000 -#define bRxSGI_TH 0xc0000000 -#define bDFSCnt0 0xff -#define bDFSCnt1 0xff00 -#define bDFSFlag 0xf0000 -#define bMFWeightSum 0x300000 -#define bMinIdxTH 0x7f000000 -#define bDAFormat 0x40000 -#define bTxChEmuEnable 0x01000000 -#define bTRSWIsolation_A 0x7f -#define bTRSWIsolation_B 0x7f00 -#define bTRSWIsolation_C 0x7f0000 -#define bTRSWIsolation_D 0x7f000000 -#define bExtLNAGain 0x7c00 - -/* 6. PageE(0xE00) */ -#define bSTBCEn 0x4 /* Useless */ -#define bAntennaMapping 0x10 -#define bNss 0x20 -#define bCFOAntSumD 0x200 -#define bPHYCounterReset 0x8000000 -#define bCFOReportGet 0x4000000 -#define bOFDMContinueTx 0x10000000 -#define bOFDMSingleCarrier 0x20000000 -#define bOFDMSingleTone 0x40000000 -#define bHTDetect 0x100 -#define bCFOEn 0x10000 -#define bCFOValue 0xfff00000 -#define bSigTone_Re 0x3f -#define bSigTone_Im 0x7f00 -#define bCounter_CCA 0xffff -#define bCounter_ParityFail 0xffff0000 -#define bCounter_RateIllegal 0xffff -#define bCounter_CRC8Fail 0xffff0000 -#define bCounter_MCSNoSupport 0xffff -#define bCounter_FastSync 0xffff -#define bShortCFO 0xfff -#define bShortCFOTLength 12 /* total */ -#define bShortCFOFLength 11 /* fraction */ -#define bLongCFO 0x7ff -#define bLongCFOTLength 11 -#define bLongCFOFLength 11 -#define bTailCFO 0x1fff -#define bTailCFOTLength 13 -#define bTailCFOFLength 12 -#define bmax_en_pwdB 0xffff -#define bCC_power_dB 0xffff0000 -#define bnoise_pwdB 0xffff -#define bPowerMeasTLength 10 -#define bPowerMeasFLength 3 -#define bRx_HT_BW 0x1 -#define bRxSC 0x6 -#define bRx_HT 0x8 -#define bNB_intf_det_on 0x1 -#define bIntf_win_len_cfg 0x30 -#define bNB_Intf_TH_cfg 0x1c0 -#define bRFGain 0x3f -#define bTableSel 0x40 -#define bTRSW 0x80 -#define bRxSNR_A 0xff -#define bRxSNR_B 0xff00 -#define bRxSNR_C 0xff0000 -#define bRxSNR_D 0xff000000 -#define bSNREVMTLength 8 -#define bSNREVMFLength 1 -#define bCSI1st 0xff -#define bCSI2nd 0xff00 -#define bRxEVM1st 0xff0000 -#define bRxEVM2nd 0xff000000 -#define bSIGEVM 0xff -#define bPWDB 0xff00 -#define bSGIEN 0x10000 - -#define bSFactorQAM1 0xf /* Useless */ -#define bSFactorQAM2 0xf0 -#define bSFactorQAM3 0xf00 -#define bSFactorQAM4 0xf000 -#define bSFactorQAM5 0xf0000 -#define bSFactorQAM6 0xf0000 -#define bSFactorQAM7 0xf00000 -#define bSFactorQAM8 0xf000000 -#define bSFactorQAM9 0xf0000000 -#define bCSIScheme 0x100000 - -#define bNoiseLvlTopSet 0x3 /* Useless */ -#define bChSmooth 0x4 -#define bChSmoothCfg1 0x38 -#define bChSmoothCfg2 0x1c0 -#define bChSmoothCfg3 0xe00 -#define bChSmoothCfg4 0x7000 -#define bMRCMode 0x800000 -#define bTHEVMCfg 0x7000000 - -#define bLoopFitType 0x1 /* Useless */ -#define bUpdCFO 0x40 -#define bUpdCFOOffData 0x80 -#define bAdvUpdCFO 0x100 -#define bAdvTimeCtrl 0x800 -#define bUpdClko 0x1000 -#define bFC 0x6000 -#define bTrackingMode 0x8000 -#define bPhCmpEnable 0x10000 -#define bUpdClkoLTF 0x20000 -#define bComChCFO 0x40000 -#define bCSIEstiMode 0x80000 -#define bAdvUpdEqz 0x100000 -#define bUChCfg 0x7000000 -#define bUpdEqz 0x8000000 - -#define bTxAGCRate18_06 0x7f7f7f7f /* Useless */ -#define bTxAGCRate54_24 0x7f7f7f7f -#define bTxAGCRateMCS32 0x7f -#define bTxAGCRateCCK 0x7f00 -#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f -#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f -#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f -#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f - -/* Rx Pseduo noise */ -#define bRxPesudoNoiseOn 0x20000000 /* Useless */ -#define bRxPesudoNoise_A 0xff -#define bRxPesudoNoise_B 0xff00 -#define bRxPesudoNoise_C 0xff0000 -#define bRxPesudoNoise_D 0xff000000 -#define bPesudoNoiseState_A 0xffff -#define bPesudoNoiseState_B 0xffff0000 -#define bPesudoNoiseState_C 0xffff -#define bPesudoNoiseState_D 0xffff0000 - -/* 7. RF Register - * Zebra1 - */ -#define bZebra1_HSSIEnable 0x8 /* Useless */ -#define bZebra1_TRxControl 0xc00 -#define bZebra1_TRxGainSetting 0x07f -#define bZebra1_RxCorner 0xc00 -#define bZebra1_TxChargePump 0x38 -#define bZebra1_RxChargePump 0x7 -#define bZebra1_ChannelNum 0xf80 -#define bZebra1_TxLPFBW 0x400 -#define bZebra1_RxLPFBW 0x600 - -/*Zebra4 */ -#define bRTL8256RegModeCtrl1 0x100 /* Useless */ -#define bRTL8256RegModeCtrl0 0x40 -#define bRTL8256_TxLPFBW 0x18 -#define bRTL8256_RxLPFBW 0x600 - -/* RTL8258 */ -#define bRTL8258_TxLPFBW 0xc /* Useless */ -#define bRTL8258_RxLPFBW 0xc00 -#define bRTL8258_RSSILPFBW 0xc0 - -/* - * Other Definition - */ - -/* byte endable for sb_write */ -#define bByte0 0x1 /* Useless */ -#define bByte1 0x2 -#define bByte2 0x4 -#define bByte3 0x8 -#define bWord0 0x3 -#define bWord1 0xc -#define bDWord 0xf - -/* for PutRegsetting & GetRegSetting BitMask */ -#define bMaskByte0 0xff /* Reg 0xc50 rOFDM0_XAAGCCore~0xC6f */ -#define bMaskByte1 0xff00 -#define bMaskByte2 0xff0000 -#define bMaskByte3 0xff000000 -#define bMaskHWord 0xffff0000 -#define bMaskLWord 0x0000ffff -#define bMaskDWord 0xffffffff - -/* for PutRFRegsetting & GetRFRegSetting BitMask */ -#define bRFRegOffsetMask 0xfffff -#define bEnable 0x1 /* Useless */ -#define bDisable 0x0 - -#define LeftAntenna 0x0 /* Useless */ -#define RightAntenna 0x1 - -#define tCheckTxStatus 500 /* 500ms Useless */ -#define tUpdateRxCounter 100 /* 100ms */ - -#define rateCCK 0 /* Useless */ -#define rateOFDM 1 -#define rateHT 2 - -/* define Register-End */ -#define bPMAC_End 0x1ff /* Useless */ -#define bFPGAPHY0_End 0x8ff -#define bFPGAPHY1_End 0x9ff -#define bCCKPHY0_End 0xaff -#define bOFDMPHY0_End 0xcff -#define bOFDMPHY1_End 0xdff - -#define bPMACControl 0x0 /* Useless */ -#define bWMACControl 0x1 -#define bWNICControl 0x2 - -#define ANTENNA_A 0x1 /* Useless */ -#define ANTENNA_B 0x2 -#define ANTENNA_AB 0x3 /* ANTENNA_A |ANTENNA_B */ - -#define ANTENNA_C 0x4 -#define ANTENNA_D 0x8 - -/* accept all physical address */ -#define RCR_AAP BIT(0) -#define RCR_APM BIT(1) /* accept physical match */ -#define RCR_AM BIT(2) /* accept multicast */ -#define RCR_AB BIT(3) /* accept broadcast */ -#define RCR_ACRC32 BIT(5) /* accept error packet */ -#define RCR_9356SEL BIT(6) -#define RCR_AICV BIT(12) /* Accept ICV error packet */ -#define RCR_RXFTH0 (BIT(13)|BIT(14)|BIT(15)) /* Rx FIFO threshold */ -#define RCR_ADF BIT(18) /* Accept Data(frame type) frame */ -#define RCR_ACF BIT(19) /* Accept control frame */ -#define RCR_AMF BIT(20) /* Accept management frame */ -#define RCR_ADD3 BIT(21) -#define RCR_APWRMGT BIT(22) /* Accept power management packet */ -#define RCR_CBSSID BIT(23) /* Accept BSSID match packet */ -#define RCR_ENMARP BIT(28) /* enable mac auto reset phy */ -#define RCR_EnCS1 BIT(29) /* enable carrier sense method 1 */ -#define RCR_EnCS2 BIT(30) /* enable carrier sense method 2 */ -/* Rx Early mode is performed for packet size greater than 1536 */ -#define RCR_OnlyErlPkt BIT(31) - -/*--------------------------Define Parameters-------------------------------*/ - -#endif /*__INC_HAL8192SPHYREG_H */ - diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.c b/drivers/staging/rtl8712/rtl871x_pwrctrl.c deleted file mode 100644 index cd6d9ff0bebca..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_pwrctrl.c +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_pwrctrl.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_PWRCTRL_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "osdep_intf.h" - -#define RTL8712_SDIO_LOCAL_BASE 0X10100000 -#define SDIO_HCPWM (RTL8712_SDIO_LOCAL_BASE + 0x0081) - -void r8712_set_rpwm(struct _adapter *padapter, u8 val8) -{ - u8 rpwm; - struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; - - if (pwrpriv->rpwm == val8) { - if (pwrpriv->rpwm_retry == 0) - return; - } - if (padapter->driver_stopped || padapter->surprise_removed) - return; - rpwm = val8 | pwrpriv->tog; - switch (val8) { - case PS_STATE_S1: - pwrpriv->cpwm = val8; - break; - case PS_STATE_S2:/* only for USB normal powersave mode use, - * temp mark some code. - */ - case PS_STATE_S3: - case PS_STATE_S4: - pwrpriv->cpwm = val8; - break; - default: - break; - } - pwrpriv->rpwm_retry = 0; - pwrpriv->rpwm = val8; - r8712_write8(padapter, 0x1025FE58, rpwm); - pwrpriv->tog += 0x80; -} - -void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode, uint smart_ps) -{ - struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; - - if (ps_mode > PM_Card_Disable) - return; - /* if driver is in active state, we dont need set smart_ps.*/ - if (ps_mode == PS_MODE_ACTIVE) - smart_ps = 0; - if ((pwrpriv->pwr_mode != ps_mode) || (pwrpriv->smart_ps != smart_ps)) { - if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) - pwrpriv->bSleep = true; - else - pwrpriv->bSleep = false; - pwrpriv->pwr_mode = ps_mode; - pwrpriv->smart_ps = smart_ps; - schedule_work(&pwrpriv->SetPSModeWorkItem); - } -} - -/* - * Caller:ISR handler... - * - * This will be called when CPWM interrupt is up. - * - * using to update cpwn of drv; and drv will make a decision to up or - * down pwr level - */ -void r8712_cpwm_int_hdl(struct _adapter *padapter, - struct reportpwrstate_parm *preportpwrstate) -{ - struct pwrctrl_priv *pwrpriv = &(padapter->pwrctrlpriv); - struct cmd_priv *pcmdpriv = &(padapter->cmdpriv); - - if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80)) - return; - del_timer(&padapter->pwrctrlpriv.rpwm_check_timer); - mutex_lock(&pwrpriv->mutex_lock); - pwrpriv->cpwm = (preportpwrstate->state) & 0xf; - if (pwrpriv->cpwm >= PS_STATE_S2) { - if (pwrpriv->alives & CMD_ALIVE) - complete(&(pcmdpriv->cmd_queue_comp)); - } - pwrpriv->cpwm_tog = (preportpwrstate->state) & 0x80; - mutex_unlock(&pwrpriv->mutex_lock); -} - -static inline void register_task_alive(struct pwrctrl_priv *pwrctrl, uint tag) -{ - pwrctrl->alives |= tag; -} - -static inline void unregister_task_alive(struct pwrctrl_priv *pwrctrl, uint tag) -{ - if (pwrctrl->alives & tag) - pwrctrl->alives ^= tag; -} - -static void _rpwm_check_handler (struct _adapter *padapter) -{ - struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; - - if (padapter->driver_stopped || padapter->surprise_removed) - return; - if (pwrpriv->cpwm != pwrpriv->rpwm) - schedule_work(&pwrpriv->rpwm_workitem); -} - -static void SetPSModeWorkItemCallback(struct work_struct *work) -{ - struct pwrctrl_priv *pwrpriv = container_of(work, - struct pwrctrl_priv, SetPSModeWorkItem); - struct _adapter *padapter = container_of(pwrpriv, - struct _adapter, pwrctrlpriv); - if (!pwrpriv->bSleep) { - mutex_lock(&pwrpriv->mutex_lock); - if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) - r8712_set_rpwm(padapter, PS_STATE_S4); - mutex_unlock(&pwrpriv->mutex_lock); - } -} - -static void rpwm_workitem_callback(struct work_struct *work) -{ - struct pwrctrl_priv *pwrpriv = container_of(work, - struct pwrctrl_priv, rpwm_workitem); - struct _adapter *padapter = container_of(pwrpriv, - struct _adapter, pwrctrlpriv); - if (pwrpriv->cpwm != pwrpriv->rpwm) { - mutex_lock(&pwrpriv->mutex_lock); - r8712_read8(padapter, SDIO_HCPWM); - pwrpriv->rpwm_retry = 1; - r8712_set_rpwm(padapter, pwrpriv->rpwm); - mutex_unlock(&pwrpriv->mutex_lock); - } -} - -static void rpwm_check_handler (struct timer_list *t) -{ - struct _adapter *adapter = - from_timer(adapter, t, pwrctrlpriv.rpwm_check_timer); - - _rpwm_check_handler(adapter); -} - -void r8712_init_pwrctrl_priv(struct _adapter *padapter) -{ - struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv; - - memset((unsigned char *)pwrctrlpriv, 0, sizeof(struct pwrctrl_priv)); - mutex_init(&pwrctrlpriv->mutex_lock); - pwrctrlpriv->cpwm = PS_STATE_S4; - pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE; - pwrctrlpriv->smart_ps = 0; - pwrctrlpriv->tog = 0x80; -/* clear RPWM to ensure driver and fw back to initial state. */ - r8712_write8(padapter, 0x1025FE58, 0); - INIT_WORK(&pwrctrlpriv->SetPSModeWorkItem, SetPSModeWorkItemCallback); - INIT_WORK(&pwrctrlpriv->rpwm_workitem, rpwm_workitem_callback); - timer_setup(&pwrctrlpriv->rpwm_check_timer, rpwm_check_handler, 0); -} - -/* - * Caller: r8712_cmd_thread - * Check if the fw_pwrstate is okay for issuing cmd. - * If not (cpwm should be is less than P2 state), then the sub-routine - * will raise the cpwm to be greater than or equal to P2. - * Calling Context: Passive - * Return Value: - * 0: r8712_cmd_thread can issue cmds to firmware afterwards. - * -EINVAL: r8712_cmd_thread can not do anything. - */ -int r8712_register_cmd_alive(struct _adapter *padapter) -{ - int res = 0; - struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv; - - mutex_lock(&pwrctrl->mutex_lock); - register_task_alive(pwrctrl, CMD_ALIVE); - if (pwrctrl->cpwm < PS_STATE_S2) { - r8712_set_rpwm(padapter, PS_STATE_S3); - res = -EINVAL; - } - mutex_unlock(&pwrctrl->mutex_lock); - return res; -} - -/* - * Caller: ISR - * If ISR's txdone, - * No more pkts for TX, - * Then driver shall call this fun. to power down firmware again. - */ -void r8712_unregister_cmd_alive(struct _adapter *padapter) -{ - struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv; - - mutex_lock(&pwrctrl->mutex_lock); - unregister_task_alive(pwrctrl, CMD_ALIVE); - if ((pwrctrl->cpwm > PS_STATE_S2) && - (pwrctrl->pwr_mode > PS_MODE_ACTIVE)) { - if ((pwrctrl->alives == 0) && - (check_fwstate(&padapter->mlmepriv, - _FW_UNDER_LINKING) != true)) { - r8712_set_rpwm(padapter, PS_STATE_S0); - } - } - mutex_unlock(&pwrctrl->mutex_lock); -} - -void r8712_flush_rwctrl_works(struct _adapter *padapter) -{ - struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv; - - flush_work(&pwrctrl->SetPSModeWorkItem); - flush_work(&pwrctrl->rpwm_workitem); -} diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.h b/drivers/staging/rtl8712/rtl871x_pwrctrl.h deleted file mode 100644 index b35b9c7920ebb..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_pwrctrl.h +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_PWRCTRL_H_ -#define __RTL871X_PWRCTRL_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -#define CMD_ALIVE BIT(2) - -enum Power_Mgnt { - PS_MODE_ACTIVE = 0, - PS_MODE_MIN, - PS_MODE_MAX, - PS_MODE_DTIM, - PS_MODE_VOIP, - PS_MODE_UAPSD_WMM, - PS_MODE_UAPSD, - PS_MODE_IBSS, - PS_MODE_WWLAN, - PM_Radio_Off, - PM_Card_Disable, - PS_MODE_NUM -}; - -/* - * BIT[2:0] = HW state - * BIT[3] = Protocol PS state, 0: register active state, - * 1: register sleep state - * BIT[4] = sub-state - */ - -#define PS_DPS BIT(0) -#define PS_LCLK (PS_DPS) -#define PS_RF_OFF BIT(1) -#define PS_ALL_ON BIT(2) -#define PS_ST_ACTIVE BIT(3) -#define PS_LP BIT(4) /* low performance */ - -#define PS_STATE_MASK (0x0F) -#define PS_STATE_HW_MASK (0x07) -#define PS_SEQ_MASK (0xc0) - -#define PS_STATE(x) (PS_STATE_MASK & (x)) -#define PS_STATE_HW(x) (PS_STATE_HW_MASK & (x)) -#define PS_SEQ(x) (PS_SEQ_MASK & (x)) - -#define PS_STATE_S0 (PS_DPS) -#define PS_STATE_S1 (PS_LCLK) -#define PS_STATE_S2 (PS_RF_OFF) -#define PS_STATE_S3 (PS_ALL_ON) -#define PS_STATE_S4 ((PS_ST_ACTIVE) | (PS_ALL_ON)) - -#define PS_IS_RF_ON(x) ((x) & (PS_ALL_ON)) -#define PS_IS_ACTIVE(x) ((x) & (PS_ST_ACTIVE)) -#define CLR_PS_STATE(x) ((x) = ((x) & (0xF0))) - -struct reportpwrstate_parm { - unsigned char mode; - unsigned char state; /* the CPWM value */ - unsigned short rsvd; -}; - -struct pwrctrl_priv { - struct mutex mutex_lock; - /*volatile*/ u8 rpwm; /* requested power state for fw */ - /* fw current power state. updated when 1. read from HCPWM or - * 2. driver lowers power level - */ - /*volatile*/ u8 cpwm; - /*volatile*/ u8 tog; /* toggling */ - /*volatile*/ u8 cpwm_tog; /* toggling */ - /*volatile*/ u8 tgt_rpwm; /* wanted power state */ - uint pwr_mode; - uint smart_ps; - uint alives; - uint ImrContent; /* used to store original imr. */ - uint bSleep; /* sleep -> active is different from active -> sleep. */ - - struct work_struct SetPSModeWorkItem; - struct work_struct rpwm_workitem; - struct timer_list rpwm_check_timer; - u8 rpwm_retry; - uint bSetPSModeWorkItemInProgress; - - spinlock_t pnp_pwr_mgnt_lock; - s32 pnp_current_pwr_state; - u8 pnp_bstop_trx; - u8 pnp_wwirp_pending; -}; - -void r8712_init_pwrctrl_priv(struct _adapter *adapter); -int r8712_register_cmd_alive(struct _adapter *padapter); -void r8712_unregister_cmd_alive(struct _adapter *padapter); -void r8712_cpwm_int_hdl(struct _adapter *padapter, - struct reportpwrstate_parm *preportpwrstate); -void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode, - uint smart_ps); -void r8712_set_rpwm(struct _adapter *padapter, u8 val8); -void r8712_flush_rwctrl_works(struct _adapter *padapter); - -#endif /* __RTL871X_PWRCTRL_H_ */ diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c deleted file mode 100644 index 8a3566214af72..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_recv.c +++ /dev/null @@ -1,671 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_recv.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_RECV_C_ - -#include -#include -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "mlme_osdep.h" -#include "ethernet.h" -#include "usb_ops.h" -#include "wifi.h" - -static const u8 SNAP_ETH_TYPE_IPX[2] = {0x81, 0x37}; - -/* Datagram Delivery Protocol */ -static const u8 SNAP_ETH_TYPE_APPLETALK_AARP[2] = {0x80, 0xf3}; - -void _r8712_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv) -{ - memset((u8 *)psta_recvpriv, 0, sizeof(struct sta_recv_priv)); - spin_lock_init(&psta_recvpriv->lock); - _init_queue(&psta_recvpriv->defrag_q); -} - -int _r8712_init_recv_priv(struct recv_priv *precvpriv, - struct _adapter *padapter) -{ - int ret; - sint i; - union recv_frame *precvframe; - - memset((unsigned char *)precvpriv, 0, sizeof(struct recv_priv)); - spin_lock_init(&precvpriv->lock); - _init_queue(&precvpriv->free_recv_queue); - _init_queue(&precvpriv->recv_pending_queue); - precvpriv->adapter = padapter; - precvpriv->free_recvframe_cnt = NR_RECVFRAME; - precvpriv->pallocated_frame_buf = kzalloc(NR_RECVFRAME * - sizeof(union recv_frame) + RXFRAME_ALIGN_SZ, - GFP_ATOMIC); - if (!precvpriv->pallocated_frame_buf) - return -ENOMEM; - precvpriv->precv_frame_buf = precvpriv->pallocated_frame_buf + - RXFRAME_ALIGN_SZ - - ((addr_t)(precvpriv->pallocated_frame_buf) & - (RXFRAME_ALIGN_SZ - 1)); - precvframe = (union recv_frame *)precvpriv->precv_frame_buf; - for (i = 0; i < NR_RECVFRAME; i++) { - INIT_LIST_HEAD(&(precvframe->u.list)); - list_add_tail(&(precvframe->u.list), - &(precvpriv->free_recv_queue.queue)); - r8712_os_recv_resource_alloc(padapter, precvframe); - precvframe->u.hdr.adapter = padapter; - precvframe++; - } - precvpriv->rx_pending_cnt = 1; - ret = r8712_init_recv_priv(precvpriv, padapter); - if (ret) - kfree(precvpriv->pallocated_frame_buf); - - return ret; -} - -void _r8712_free_recv_priv(struct recv_priv *precvpriv) -{ - kfree(precvpriv->pallocated_frame_buf); - r8712_free_recv_priv(precvpriv); -} - -union recv_frame *r8712_alloc_recvframe(struct __queue *pfree_recv_queue) -{ - unsigned long irqL; - union recv_frame *precvframe; - struct _adapter *padapter; - struct recv_priv *precvpriv; - - spin_lock_irqsave(&pfree_recv_queue->lock, irqL); - precvframe = list_first_entry_or_null(&pfree_recv_queue->queue, - union recv_frame, u.hdr.list); - if (precvframe) { - list_del_init(&precvframe->u.hdr.list); - padapter = precvframe->u.hdr.adapter; - if (padapter) { - precvpriv = &padapter->recvpriv; - if (pfree_recv_queue == &precvpriv->free_recv_queue) - precvpriv->free_recvframe_cnt--; - } - } - spin_unlock_irqrestore(&pfree_recv_queue->lock, irqL); - return precvframe; -} - -/* - * caller : defrag; recvframe_chk_defrag in recv_thread (passive) - * pframequeue: defrag_queue : will be accessed in recv_thread (passive) - * using spin_lock to protect - */ -void r8712_free_recvframe_queue(struct __queue *pframequeue, - struct __queue *pfree_recv_queue) -{ - union recv_frame *precvframe; - struct list_head *plist, *phead; - - spin_lock(&pframequeue->lock); - phead = &pframequeue->queue; - plist = phead->next; - while (!end_of_queue_search(phead, plist)) { - precvframe = container_of(plist, union recv_frame, u.list); - plist = plist->next; - r8712_free_recvframe(precvframe, pfree_recv_queue); - } - spin_unlock(&pframequeue->lock); -} - -sint r8712_recvframe_chkmic(struct _adapter *adapter, - union recv_frame *precvframe) -{ - sint i, res = _SUCCESS; - u32 datalen; - u8 miccode[8]; - u8 bmic_err = false; - u8 *pframe, *payload, *pframemic; - u8 *mickey, idx, *iv; - struct sta_info *stainfo; - struct rx_pkt_attrib *prxattrib = &precvframe->u.hdr.attrib; - struct security_priv *psecuritypriv = &adapter->securitypriv; - - stainfo = r8712_get_stainfo(&adapter->stapriv, &prxattrib->ta[0]); - if (prxattrib->encrypt == _TKIP_) { - /* calculate mic code */ - if (stainfo) { - if (is_multicast_ether_addr(prxattrib->ra)) { - iv = precvframe->u.hdr.rx_data + - prxattrib->hdrlen; - idx = iv[3]; - mickey = &psecuritypriv->XGrprxmickey[(((idx >> - 6) & 0x3)) - 1].skey[0]; - if (!psecuritypriv->binstallGrpkey) - return _FAIL; - } else { - mickey = &stainfo->tkiprxmickey.skey[0]; - } - /*icv_len included the mic code*/ - datalen = precvframe->u.hdr.len - prxattrib->hdrlen - - prxattrib->iv_len - prxattrib->icv_len - 8; - pframe = precvframe->u.hdr.rx_data; - payload = pframe + prxattrib->hdrlen + - prxattrib->iv_len; - seccalctkipmic(mickey, pframe, payload, datalen, - &miccode[0], - (unsigned char)prxattrib->priority); - pframemic = payload + datalen; - bmic_err = false; - for (i = 0; i < 8; i++) { - if (miccode[i] != *(pframemic + i)) - bmic_err = true; - } - if (bmic_err) { - if (prxattrib->bdecrypted) - r8712_handle_tkip_mic_err(adapter, - (u8)is_multicast_ether_addr(prxattrib->ra)); - res = _FAIL; - } else { - /* mic checked ok */ - if (!psecuritypriv->bcheck_grpkey && - is_multicast_ether_addr(prxattrib->ra)) - psecuritypriv->bcheck_grpkey = true; - } - recvframe_pull_tail(precvframe, 8); - } - } - return res; -} - -/* decrypt and set the ivlen,icvlen of the recv_frame */ -union recv_frame *r8712_decryptor(struct _adapter *padapter, - union recv_frame *precv_frame) -{ - struct rx_pkt_attrib *prxattrib = &precv_frame->u.hdr.attrib; - struct security_priv *psecuritypriv = &padapter->securitypriv; - union recv_frame *return_packet = precv_frame; - - if ((prxattrib->encrypt > 0) && ((prxattrib->bdecrypted == 0) || - psecuritypriv->sw_decrypt)) { - psecuritypriv->hw_decrypted = false; - switch (prxattrib->encrypt) { - case _WEP40_: - case _WEP104_: - r8712_wep_decrypt(padapter, (u8 *)precv_frame); - break; - case _TKIP_: - r8712_tkip_decrypt(padapter, (u8 *)precv_frame); - break; - case _AES_: - r8712_aes_decrypt(padapter, (u8 *)precv_frame); - break; - default: - break; - } - } else if (prxattrib->bdecrypted == 1) { - psecuritypriv->hw_decrypted = true; - } - return return_packet; -} - -/*###set the security information in the recv_frame */ -union recv_frame *r8712_portctrl(struct _adapter *adapter, - union recv_frame *precv_frame) -{ - u8 *psta_addr, *ptr; - uint auth_alg; - struct recv_frame_hdr *pfhdr; - struct sta_info *psta; - struct sta_priv *pstapriv; - union recv_frame *prtnframe; - u16 ether_type; - - pstapriv = &adapter->stapriv; - ptr = precv_frame->u.hdr.rx_data; - pfhdr = &precv_frame->u.hdr; - psta_addr = pfhdr->attrib.ta; - psta = r8712_get_stainfo(pstapriv, psta_addr); - auth_alg = adapter->securitypriv.AuthAlgrthm; - if (auth_alg == 2) { - /* get ether_type */ - ptr = ptr + pfhdr->attrib.hdrlen + LLC_HEADER_SIZE; - ether_type = get_unaligned_be16(ptr); - - if (psta && psta->ieee8021x_blocked) { - /* blocked - * only accept EAPOL frame - */ - if (ether_type == 0x888e) { - prtnframe = precv_frame; - } else { - /*free this frame*/ - r8712_free_recvframe(precv_frame, - &adapter->recvpriv.free_recv_queue); - prtnframe = NULL; - } - } else { - /* allowed - * check decryption status, and decrypt the - * frame if needed - */ - prtnframe = precv_frame; - /* check is the EAPOL frame or not (Rekey) */ - if (ether_type == 0x888e) { - /* check Rekey */ - prtnframe = precv_frame; - } - } - } else { - prtnframe = precv_frame; - } - return prtnframe; -} - -static sint recv_decache(union recv_frame *precv_frame, u8 bretry, - struct stainfo_rxcache *prxcache) -{ - sint tid = precv_frame->u.hdr.attrib.priority; - u16 seq_ctrl = ((precv_frame->u.hdr.attrib.seq_num & 0xffff) << 4) | - (precv_frame->u.hdr.attrib.frag_num & 0xf); - - if (tid > 15) - return _FAIL; - if (seq_ctrl == prxcache->tid_rxseq[tid]) - return _FAIL; - prxcache->tid_rxseq[tid] = seq_ctrl; - return _SUCCESS; -} - -static sint sta2sta_data_frame(struct _adapter *adapter, - union recv_frame *precv_frame, - struct sta_info **psta) -{ - u8 *ptr = precv_frame->u.hdr.rx_data; - sint ret = _SUCCESS; - struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; - struct sta_priv *pstapriv = &adapter->stapriv; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - u8 *mybssid = get_bssid(pmlmepriv); - u8 *myhwaddr = myid(&adapter->eeprompriv); - u8 *sta_addr = NULL; - bool bmcast = is_multicast_ether_addr(pattrib->dst); - - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { - /* filter packets that SA is myself or multicast or broadcast */ - if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) - return _FAIL; - if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast)) - return _FAIL; - if (is_zero_ether_addr(pattrib->bssid) || - is_zero_ether_addr(mybssid) || - (memcmp(pattrib->bssid, mybssid, ETH_ALEN))) - return _FAIL; - sta_addr = pattrib->src; - } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - /* For Station mode, sa and bssid should always be BSSID, - * and DA is my mac-address - */ - if (memcmp(pattrib->bssid, pattrib->src, ETH_ALEN)) - return _FAIL; - sta_addr = pattrib->bssid; - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { - if (bmcast) { - /* For AP mode, if DA == MCAST, then BSSID should - * be also MCAST - */ - if (!is_multicast_ether_addr(pattrib->bssid)) - return _FAIL; - } else { /* not mc-frame */ - /* For AP mode, if DA is non-MCAST, then it must be - * BSSID, and bssid == BSSID - */ - if (memcmp(pattrib->bssid, pattrib->dst, ETH_ALEN)) - return _FAIL; - sta_addr = pattrib->src; - } - } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); - memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); - memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - sta_addr = mybssid; - } else { - ret = _FAIL; - } - if (bmcast) - *psta = r8712_get_bcmc_stainfo(adapter); - else - *psta = r8712_get_stainfo(pstapriv, sta_addr); /* get ap_info */ - if (!*psta) { - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) - adapter->mppriv.rx_pktloss++; - return _FAIL; - } - return ret; -} - -static sint ap2sta_data_frame(struct _adapter *adapter, - union recv_frame *precv_frame, - struct sta_info **psta) -{ - u8 *ptr = precv_frame->u.hdr.rx_data; - struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; - struct sta_priv *pstapriv = &adapter->stapriv; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - u8 *mybssid = get_bssid(pmlmepriv); - u8 *myhwaddr = myid(&adapter->eeprompriv); - bool bmcast = is_multicast_ether_addr(pattrib->dst); - - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) && - check_fwstate(pmlmepriv, _FW_LINKED)) { - /* if NULL-frame, drop packet */ - if ((GetFrameSubType(ptr)) == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC)) - return _FAIL; - /* drop QoS-SubType Data, including QoS NULL, - * excluding QoS-Data - */ - if ((GetFrameSubType(ptr) & WIFI_QOS_DATA_TYPE) == - WIFI_QOS_DATA_TYPE) { - if (GetFrameSubType(ptr) & (BIT(4) | BIT(5) | BIT(6))) - return _FAIL; - } - - /* filter packets that SA is myself or multicast or broadcast */ - if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) - return _FAIL; - - /* da should be for me */ - if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast)) - return _FAIL; - /* check BSSID */ - if (is_zero_ether_addr(pattrib->bssid) || - is_zero_ether_addr(mybssid) || - (memcmp(pattrib->bssid, mybssid, ETH_ALEN))) - return _FAIL; - if (bmcast) - *psta = r8712_get_bcmc_stainfo(adapter); - else - *psta = r8712_get_stainfo(pstapriv, pattrib->bssid); - if (!*psta) - return _FAIL; - } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) && - check_fwstate(pmlmepriv, _FW_LINKED)) { - memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); - memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); - memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - memcpy(pattrib->bssid, mybssid, ETH_ALEN); - *psta = r8712_get_stainfo(pstapriv, pattrib->bssid); - if (!*psta) - return _FAIL; - } else { - return _FAIL; - } - return _SUCCESS; -} - -static sint sta2ap_data_frame(struct _adapter *adapter, - union recv_frame *precv_frame, - struct sta_info **psta) -{ - struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; - struct sta_priv *pstapriv = &adapter->stapriv; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - unsigned char *mybssid = get_bssid(pmlmepriv); - - if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { - /* For AP mode, if DA is non-MCAST, then it must be BSSID, - * and bssid == BSSID - * For AP mode, RA=BSSID, TX=STA(SRC_ADDR), A3=DST_ADDR - */ - if (memcmp(pattrib->bssid, mybssid, ETH_ALEN)) - return _FAIL; - *psta = r8712_get_stainfo(pstapriv, pattrib->src); - if (!*psta) - return _FAIL; - } - return _SUCCESS; -} - -static sint validate_recv_ctrl_frame(struct _adapter *adapter, - union recv_frame *precv_frame) -{ - return _FAIL; -} - -static sint validate_recv_mgnt_frame(struct _adapter *adapter, - union recv_frame *precv_frame) -{ - return _FAIL; -} - -static sint validate_recv_data_frame(struct _adapter *adapter, - union recv_frame *precv_frame) -{ - int res; - u8 bretry; - u8 *psa, *pda, *pbssid; - struct sta_info *psta = NULL; - u8 *ptr = precv_frame->u.hdr.rx_data; - struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; - struct security_priv *psecuritypriv = &adapter->securitypriv; - - bretry = GetRetry(ptr); - pda = ieee80211_get_DA((struct ieee80211_hdr *)ptr); - psa = ieee80211_get_SA((struct ieee80211_hdr *)ptr); - pbssid = get_hdr_bssid(ptr); - if (!pbssid) - return _FAIL; - memcpy(pattrib->dst, pda, ETH_ALEN); - memcpy(pattrib->src, psa, ETH_ALEN); - memcpy(pattrib->bssid, pbssid, ETH_ALEN); - switch (pattrib->to_fr_ds) { - case 0: - memcpy(pattrib->ra, pda, ETH_ALEN); - memcpy(pattrib->ta, psa, ETH_ALEN); - res = sta2sta_data_frame(adapter, precv_frame, &psta); - break; - case 1: - memcpy(pattrib->ra, pda, ETH_ALEN); - memcpy(pattrib->ta, pbssid, ETH_ALEN); - res = ap2sta_data_frame(adapter, precv_frame, &psta); - break; - case 2: - memcpy(pattrib->ra, pbssid, ETH_ALEN); - memcpy(pattrib->ta, psa, ETH_ALEN); - res = sta2ap_data_frame(adapter, precv_frame, &psta); - break; - case 3: - memcpy(pattrib->ra, GetAddr1Ptr(ptr), ETH_ALEN); - memcpy(pattrib->ta, GetAddr2Ptr(ptr), ETH_ALEN); - return _FAIL; - default: - return _FAIL; - } - if (res == _FAIL) - return _FAIL; - if (!psta) - return _FAIL; - precv_frame->u.hdr.psta = psta; - pattrib->amsdu = 0; - /* parsing QC field */ - if (pattrib->qos == 1) { - pattrib->priority = GetPriority((ptr + 24)); - pattrib->ack_policy = GetAckpolicy((ptr + 24)); - pattrib->amsdu = GetAMsdu((ptr + 24)); - pattrib->hdrlen = pattrib->to_fr_ds == 3 ? 32 : 26; - } else { - pattrib->priority = 0; - pattrib->hdrlen = (pattrib->to_fr_ds == 3) ? 30 : 24; - } - - if (pattrib->order)/*HT-CTRL 11n*/ - pattrib->hdrlen += 4; - precv_frame->u.hdr.preorder_ctrl = - &psta->recvreorder_ctrl[pattrib->priority]; - - /* decache, drop duplicate recv packets */ - if (recv_decache(precv_frame, bretry, &psta->sta_recvpriv.rxcache) == - _FAIL) - return _FAIL; - - if (pattrib->privacy) { - GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, - is_multicast_ether_addr(pattrib->ra)); - SET_ICE_IV_LEN(pattrib->iv_len, pattrib->icv_len, - pattrib->encrypt); - } else { - pattrib->encrypt = 0; - pattrib->iv_len = pattrib->icv_len = 0; - } - return _SUCCESS; -} - -sint r8712_validate_recv_frame(struct _adapter *adapter, - union recv_frame *precv_frame) -{ - /*shall check frame subtype, to / from ds, da, bssid */ - /*then call check if rx seq/frag. duplicated.*/ - - u8 type; - u8 subtype; - sint retval = _SUCCESS; - struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; - - u8 *ptr = precv_frame->u.hdr.rx_data; - u8 ver = (unsigned char)(*ptr) & 0x3; - - /*add version chk*/ - if (ver != 0) - return _FAIL; - type = GetFrameType(ptr); - subtype = GetFrameSubType(ptr); /*bit(7)~bit(2)*/ - pattrib->to_fr_ds = get_tofr_ds(ptr); - pattrib->frag_num = GetFragNum(ptr); - pattrib->seq_num = GetSequence(ptr); - pattrib->pw_save = GetPwrMgt(ptr); - pattrib->mfrag = GetMFrag(ptr); - pattrib->mdata = GetMData(ptr); - pattrib->privacy = GetPrivacy(ptr); - pattrib->order = GetOrder(ptr); - switch (type) { - case IEEE80211_FTYPE_MGMT: - retval = validate_recv_mgnt_frame(adapter, precv_frame); - break; - case IEEE80211_FTYPE_CTL: - retval = validate_recv_ctrl_frame(adapter, precv_frame); - break; - case IEEE80211_FTYPE_DATA: - pattrib->qos = (subtype & BIT(7)) ? 1 : 0; - retval = validate_recv_data_frame(adapter, precv_frame); - break; - default: - return _FAIL; - } - return retval; -} - -int r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe) -{ - /*remove the wlanhdr and add the eth_hdr*/ - sint rmv_len; - u16 len; - u8 bsnaphdr; - u8 *psnap_type; - struct ieee80211_snap_hdr *psnap; - struct _adapter *adapter = precvframe->u.hdr.adapter; - struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - - u8 *ptr = precvframe->u.hdr.rx_data; /*point to frame_ctrl field*/ - struct rx_pkt_attrib *pattrib = &precvframe->u.hdr.attrib; - - if (pattrib->encrypt) - recvframe_pull_tail(precvframe, pattrib->icv_len); - psnap = (struct ieee80211_snap_hdr *)(ptr + pattrib->hdrlen + - pattrib->iv_len); - psnap_type = ptr + pattrib->hdrlen + pattrib->iv_len + SNAP_SIZE; - /* convert hdr + possible LLC headers into Ethernet header */ - if ((!memcmp(psnap, (void *)rfc1042_header, SNAP_SIZE) && - (memcmp(psnap_type, (void *)SNAP_ETH_TYPE_IPX, 2)) && - (memcmp(psnap_type, (void *)SNAP_ETH_TYPE_APPLETALK_AARP, 2))) || - !memcmp(psnap, (void *)bridge_tunnel_header, SNAP_SIZE)) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType - */ - bsnaphdr = true; - } else { - /* Leave Ethernet header part of hdr and full payload */ - bsnaphdr = false; - } - rmv_len = pattrib->hdrlen + pattrib->iv_len + - (bsnaphdr ? SNAP_SIZE : 0); - len = precvframe->u.hdr.len - rmv_len; - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - ptr += rmv_len; - *ptr = 0x87; - *(ptr + 1) = 0x12; - /* append rx status for mp test packets */ - ptr = recvframe_pull(precvframe, (rmv_len - - sizeof(struct ethhdr) + 2) - 24); - if (!ptr) - return -ENOMEM; - memcpy(ptr, get_rxmem(precvframe), 24); - ptr += 24; - } else { - ptr = recvframe_pull(precvframe, (rmv_len - - sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0))); - if (!ptr) - return -ENOMEM; - } - - memcpy(ptr, pattrib->dst, ETH_ALEN); - memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN); - if (!bsnaphdr) { - __be16 be_tmp = htons(len); - - memcpy(ptr + 12, &be_tmp, 2); - } - return 0; -} - -void r8712_recv_entry(union recv_frame *precvframe) -{ - struct _adapter *padapter; - struct recv_priv *precvpriv; - - s32 ret = _SUCCESS; - - padapter = precvframe->u.hdr.adapter; - precvpriv = &(padapter->recvpriv); - - padapter->ledpriv.LedControlHandler(padapter, LED_CTL_RX); - - ret = recv_func(padapter, precvframe); - if (ret == _FAIL) - goto _recv_entry_drop; - precvpriv->rx_pkts++; - precvpriv->rx_bytes += (uint)(precvframe->u.hdr.rx_tail - - precvframe->u.hdr.rx_data); - return; -_recv_entry_drop: - precvpriv->rx_drop++; - padapter->mppriv.rx_pktloss = precvpriv->rx_drop; -} diff --git a/drivers/staging/rtl8712/rtl871x_recv.h b/drivers/staging/rtl8712/rtl871x_recv.h deleted file mode 100644 index 0760bccbf389c..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_recv.h +++ /dev/null @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _RTL871X_RECV_H_ -#define _RTL871X_RECV_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -#define NR_RECVFRAME 256 - -#define RXFRAME_ALIGN 8 -#define RXFRAME_ALIGN_SZ (1 << RXFRAME_ALIGN) - -#define MAX_SUBFRAME_COUNT 64 - -/* for Rx reordering buffer control */ -struct recv_reorder_ctrl { - struct _adapter *padapter; - u16 indicate_seq; /* =wstart_b, init_value=0xffff */ - u16 wend_b; - u8 wsize_b; - struct __queue pending_recvframe_queue; - struct timer_list reordering_ctrl_timer; -}; - -struct stainfo_rxcache { - u16 tid_rxseq[16]; -}; - -#define PHY_RSSI_SLID_WIN_MAX 100 -#define PHY_LINKQUALITY_SLID_WIN_MAX 20 - -struct smooth_rssi_data { - u32 elements[100]; /* array to store values */ - u32 index; /* index to current array to store */ - u32 total_num; /* num of valid elements */ - u32 total_val; /* sum of valid elements */ -}; - -struct rx_pkt_attrib { - u8 amsdu; - u8 order; - u8 qos; - u8 to_fr_ds; - u8 frag_num; - u16 seq_num; - u8 pw_save; - u8 mfrag; - u8 mdata; - u8 privacy; /* in frame_ctrl field */ - u8 bdecrypted; - int hdrlen; /* the WLAN Header Len */ - int encrypt; /* 0 no encrypt. != 0 encrypt algorithm */ - int iv_len; - int icv_len; - int priority; - int ack_policy; - u8 crc_err; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN]; - u8 ta[ETH_ALEN]; - u8 ra[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - u8 tcpchk_valid; /* 0: invalid, 1: valid */ - u8 ip_chkrpt; /* 0: incorrect, 1: correct */ - u8 tcp_chkrpt; /* 0: incorrect, 1: correct */ - u8 signal_qual; - s8 rx_mimo_signal_qual[2]; - u8 mcs_rate; - u8 htc; - u8 signal_strength; -}; - -/* - * accesser of recv_priv: recv_entry(dispatch / passive level); - * recv_thread(passive) ; returnpkt(dispatch) - * ; halt(passive) ; - * - * using enter_critical section to protect - */ -struct recv_priv { - spinlock_t lock; - struct __queue free_recv_queue; - struct __queue recv_pending_queue; - u8 *pallocated_frame_buf; - u8 *precv_frame_buf; - uint free_recvframe_cnt; - struct _adapter *adapter; - uint rx_bytes; - uint rx_pkts; - uint rx_drop; - uint rx_icv_err; - uint rx_largepacket_crcerr; - uint rx_smallpacket_crcerr; - uint rx_middlepacket_crcerr; - u8 rx_pending_cnt; - uint ff_hwaddr; - struct tasklet_struct recv_tasklet; - struct sk_buff_head free_recv_skb_queue; - struct sk_buff_head rx_skb_queue; - u8 *pallocated_recv_buf; - u8 *precv_buf; /* 4 alignment */ - struct __queue free_recv_buf_queue; - u32 free_recv_buf_queue_cnt; - /* For the phy information */ - s8 rssi; - u8 signal; - u8 noise; - u8 fw_rssi; - struct smooth_rssi_data signal_qual_data; - struct smooth_rssi_data signal_strength_data; -}; - -struct sta_recv_priv { - spinlock_t lock; - sint option; - struct __queue defrag_q; /* keeping the fragment frame until defrag */ - struct stainfo_rxcache rxcache; - uint sta_rx_bytes; - uint sta_rx_pkts; - uint sta_rx_fail; -}; - -#include "rtl8712_recv.h" - -/* get a free recv_frame from pfree_recv_queue */ -union recv_frame *r8712_alloc_recvframe(struct __queue *pfree_recv_queue); -void r8712_free_recvframe(union recv_frame *precvframe, - struct __queue *pfree_recv_queue); -void r8712_free_recvframe_queue(struct __queue *pframequeue, - struct __queue *pfree_recv_queue); -int r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe); -int recv_func(struct _adapter *padapter, void *pcontext); - -static inline u8 *get_rxmem(union recv_frame *precvframe) -{ - /* always return rx_head... */ - if (!precvframe) - return NULL; - return precvframe->u.hdr.rx_head; -} - -static inline u8 *recvframe_pull(union recv_frame *precvframe, sint sz) -{ - /* used for extract sz bytes from rx_data, update rx_data and return - * the updated rx_data to the caller - */ - if (!precvframe) - return NULL; - precvframe->u.hdr.rx_data += sz; - if (precvframe->u.hdr.rx_data > precvframe->u.hdr.rx_tail) { - precvframe->u.hdr.rx_data -= sz; - return NULL; - } - precvframe->u.hdr.len -= sz; - return precvframe->u.hdr.rx_data; -} - -static inline u8 *recvframe_put(union recv_frame *precvframe, sint sz) -{ - /* used for append sz bytes from ptr to rx_tail, update rx_tail and - * return the updated rx_tail to the caller - * after putting, rx_tail must be still larger than rx_end. - */ - if (!precvframe) - return NULL; - precvframe->u.hdr.rx_tail += sz; - if (precvframe->u.hdr.rx_tail > precvframe->u.hdr.rx_end) { - precvframe->u.hdr.rx_tail -= sz; - return NULL; - } - precvframe->u.hdr.len += sz; - return precvframe->u.hdr.rx_tail; -} - -static inline u8 *recvframe_pull_tail(union recv_frame *precvframe, sint sz) -{ - /* rmv data from rx_tail (by yitsen) - * used for extract sz bytes from rx_end, update rx_end and return the - * updated rx_end to the caller - * after pulling, rx_end must be still larger than rx_data. - */ - if (!precvframe) - return NULL; - precvframe->u.hdr.rx_tail -= sz; - if (precvframe->u.hdr.rx_tail < precvframe->u.hdr.rx_data) { - precvframe->u.hdr.rx_tail += sz; - return NULL; - } - precvframe->u.hdr.len -= sz; - return precvframe->u.hdr.rx_tail; -} - -struct sta_info; - -void _r8712_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv); -sint r8712_recvframe_chkmic(struct _adapter *adapter, - union recv_frame *precvframe); -union recv_frame *r8712_decryptor(struct _adapter *adapter, - union recv_frame *precv_frame); -union recv_frame *r8712_recvframe_chk_defrag(struct _adapter *adapter, - union recv_frame *precv_frame); -int r8712_validate_recv_frame(struct _adapter *adapter, - union recv_frame *precv_frame); -union recv_frame *r8712_portctrl(struct _adapter *adapter, - union recv_frame *precv_frame); - -#endif - diff --git a/drivers/staging/rtl8712/rtl871x_rf.h b/drivers/staging/rtl8712/rtl871x_rf.h deleted file mode 100644 index 7d98921a48fac..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_rf.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_RF_H_ -#define __RTL871X_RF_H_ - -#include "rtl871x_cmd.h" -#include "rtl871x_mp_phy_regdef.h" - -#define OFDM_PHY 1 -#define MIXED_PHY 2 -#define CCK_PHY 3 -#define NumRates (13) -#define RTL8711_RF_MAX_SENS 6 -#define RTL8711_RF_DEF_SENS 4 -#define NUM_CHANNELS 15 - -struct regulatory_class { - u32 starting_freq; /*MHz, */ - u8 channel_set[NUM_CHANNELS]; - u8 channel_cck_power[NUM_CHANNELS]; /*dbm*/ - u8 channel_ofdm_power[NUM_CHANNELS];/*dbm*/ - u8 txpower_limit; /*dbm*/ - u8 channel_spacing; /*MHz*/ - u8 modem; -}; - -enum _REG_PREAMBLE_MODE { - PREAMBLE_LONG = 1, - PREAMBLE_AUTO = 2, - PREAMBLE_SHORT = 3, -}; - -enum { - RTL8712_RFC_1T = 0x10, - RTL8712_RFC_2T = 0x20, - RTL8712_RFC_1R = 0x01, - RTL8712_RFC_2R = 0x02, - RTL8712_RFC_1T1R = 0x11, - RTL8712_RFC_1T2R = 0x12, - RTL8712_RFC_TURBO = 0x92, - RTL8712_RFC_2T2R = 0x22 -}; - -#endif /*__RTL871X_RF_H_*/ diff --git a/drivers/staging/rtl8712/rtl871x_security.c b/drivers/staging/rtl8712/rtl871x_security.c deleted file mode 100644 index e46a5dbc7b65f..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_security.c +++ /dev/null @@ -1,1386 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_security.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_SECURITY_C_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "osdep_intf.h" - -/* =====WEP related===== */ - -struct arc4context { - u32 x; - u32 y; - u8 state[256]; -}; - -static void arcfour_init(struct arc4context *parc4ctx, u8 *key, u32 key_len) -{ - u32 t, u; - u32 keyindex; - u32 stateindex; - u8 *state; - u32 counter; - - state = parc4ctx->state; - parc4ctx->x = 0; - parc4ctx->y = 0; - for (counter = 0; counter < 256; counter++) - state[counter] = (u8)counter; - keyindex = 0; - stateindex = 0; - for (counter = 0; counter < 256; counter++) { - t = state[counter]; - stateindex = (stateindex + key[keyindex] + t) & 0xff; - u = state[stateindex]; - state[stateindex] = (u8)t; - state[counter] = (u8)u; - if (++keyindex >= key_len) - keyindex = 0; - } -} - -static u32 arcfour_byte(struct arc4context *parc4ctx) -{ - u32 x; - u32 y; - u32 sx, sy; - u8 *state; - - state = parc4ctx->state; - x = (parc4ctx->x + 1) & 0xff; - sx = state[x]; - y = (sx + parc4ctx->y) & 0xff; - sy = state[y]; - parc4ctx->x = x; - parc4ctx->y = y; - state[y] = (u8)sx; - state[x] = (u8)sy; - return state[(sx + sy) & 0xff]; -} - -static void arcfour_encrypt(struct arc4context *parc4ctx, - u8 *dest, u8 *src, u32 len) -{ - u32 i; - - for (i = 0; i < len; i++) - dest[i] = src[i] ^ (unsigned char)arcfour_byte(parc4ctx); -} - -static sint bcrc32initialized; -static u32 crc32_table[256]; - -static u8 crc32_reverseBit(u8 data) -{ - return ((u8)(data << 7) & 0x80) | ((data << 5) & 0x40) | ((data << 3) - & 0x20) | ((data << 1) & 0x10) | ((data >> 1) & 0x08) | - ((data >> 3) & 0x04) | ((data >> 5) & 0x02) | ((data >> 7) & - 0x01); -} - -static void crc32_init(void) -{ - sint i, j; - u32 c; - u8 *p = (u8 *)&c, *p1; - u8 k; - - if (bcrc32initialized == 1) - return; - - for (i = 0; i < 256; ++i) { - k = crc32_reverseBit((u8)i); - for (c = ((u32)k) << 24, j = 8; j > 0; --j) - c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY_BE : (c << 1); - p1 = (u8 *)&crc32_table[i]; - p1[0] = crc32_reverseBit(p[3]); - p1[1] = crc32_reverseBit(p[2]); - p1[2] = crc32_reverseBit(p[1]); - p1[3] = crc32_reverseBit(p[0]); - } - bcrc32initialized = 1; -} - -static u32 getcrc32(u8 *buf, u32 len) -{ - u8 *p; - u32 crc; - - if (!bcrc32initialized) - crc32_init(); - crc = 0xffffffff; /* preload shift register, per CRC-32 spec */ - for (p = buf; len > 0; ++p, --len) - crc = crc32_table[(crc ^ *p) & 0xff] ^ (crc >> 8); - return ~crc; /* transmit complement, per CRC-32 spec */ -} - -/* - * Need to consider the fragment situation - */ -void r8712_wep_encrypt(struct _adapter *padapter, u8 *pxmitframe) -{ /* exclude ICV */ - unsigned char crc[4]; - struct arc4context mycontext; - u32 curfragnum, length, keylength, pki; - u8 *pframe, *payload, *iv; /*,*wepkey*/ - u8 wepkey[16]; - struct pkt_attrib *pattrib = &((struct xmit_frame *) - pxmitframe)->attrib; - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - - if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL) - return; - pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + TXDESC_OFFSET; - /*start to encrypt each fragment*/ - if ((pattrib->encrypt == _WEP40_) || (pattrib->encrypt == _WEP104_)) { - pki = psecuritypriv->PrivacyKeyIndex; - keylength = psecuritypriv->DefKeylen[pki]; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; - curfragnum++) { - iv = pframe + pattrib->hdrlen; - memcpy(&wepkey[0], iv, 3); - memcpy(&wepkey[3], &psecuritypriv->DefKey[ - psecuritypriv->PrivacyKeyIndex].skey[0], - keylength); - payload = pframe + pattrib->iv_len + pattrib->hdrlen; - if ((curfragnum + 1) == pattrib->nr_frags) { - length = pattrib->last_txcmdsz - - pattrib->hdrlen - - pattrib->iv_len - - pattrib->icv_len; - *((__le32 *)crc) = cpu_to_le32(getcrc32( - payload, length)); - arcfour_init(&mycontext, wepkey, 3 + keylength); - arcfour_encrypt(&mycontext, payload, payload, - length); - arcfour_encrypt(&mycontext, payload + length, - crc, 4); - } else { - length = pxmitpriv->frag_len - - pattrib->hdrlen - pattrib->iv_len - - pattrib->icv_len; - *((__le32 *)crc) = cpu_to_le32(getcrc32( - payload, length)); - arcfour_init(&mycontext, wepkey, 3 + keylength); - arcfour_encrypt(&mycontext, payload, payload, - length); - arcfour_encrypt(&mycontext, payload + length, - crc, 4); - pframe += pxmitpriv->frag_len; - pframe = (u8 *)RND4((addr_t)(pframe)); - } - } - } -} - -void r8712_wep_decrypt(struct _adapter *padapter, u8 *precvframe) -{ - /* exclude ICV */ - u8 crc[4]; - struct arc4context mycontext; - u32 length, keylength; - u8 *pframe, *payload, *iv, wepkey[16]; - u8 keyindex; - struct rx_pkt_attrib *prxattrib = &(((union recv_frame *) - precvframe)->u.hdr.attrib); - struct security_priv *psecuritypriv = &padapter->securitypriv; - - pframe = (unsigned char *)((union recv_frame *)precvframe)-> - u.hdr.rx_data; - /* start to decrypt recvframe */ - if ((prxattrib->encrypt == _WEP40_) || (prxattrib->encrypt == - _WEP104_)) { - iv = pframe + prxattrib->hdrlen; - keyindex = (iv[3] & 0x3); - keylength = psecuritypriv->DefKeylen[keyindex]; - memcpy(&wepkey[0], iv, 3); - memcpy(&wepkey[3], &psecuritypriv->DefKey[ - psecuritypriv->PrivacyKeyIndex].skey[0], - keylength); - length = ((union recv_frame *)precvframe)-> - u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len; - payload = pframe + prxattrib->iv_len + prxattrib->hdrlen; - /* decrypt payload include icv */ - arcfour_init(&mycontext, wepkey, 3 + keylength); - arcfour_encrypt(&mycontext, payload, payload, length); - /* calculate icv and compare the icv */ - *((__le32 *)crc) = cpu_to_le32(getcrc32(payload, length - 4)); - } -} - -/* 3 =====TKIP related===== */ - -static u32 secmicgetuint32(u8 *p) -/* Convert from Byte[] to Us4Byte32 in a portable way */ -{ - s32 i; - u32 res = 0; - - for (i = 0; i < 4; i++) - res |= ((u32)(*p++)) << (8 * i); - return res; -} - -static void secmicputuint32(u8 *p, u32 val) -/* Convert from Us4Byte32 to Byte[] in a portable way */ -{ - long i; - - for (i = 0; i < 4; i++) { - *p++ = (u8)(val & 0xff); - val >>= 8; - } -} - -static void secmicclear(struct mic_data *pmicdata) -{ -/* Reset the state to the empty message. */ - pmicdata->L = pmicdata->K0; - pmicdata->R = pmicdata->K1; - pmicdata->nBytesInM = 0; - pmicdata->M = 0; -} - -void r8712_secmicsetkey(struct mic_data *pmicdata, u8 *key) -{ - /* Set the key */ - pmicdata->K0 = secmicgetuint32(key); - pmicdata->K1 = secmicgetuint32(key + 4); - /* and reset the message */ - secmicclear(pmicdata); -} - -static void secmicappendbyte(struct mic_data *pmicdata, u8 b) -{ - /* Append the byte to our word-sized buffer */ - pmicdata->M |= ((u32)b) << (8 * pmicdata->nBytesInM); - pmicdata->nBytesInM++; - /* Process the word if it is full. */ - if (pmicdata->nBytesInM >= 4) { - pmicdata->L ^= pmicdata->M; - pmicdata->R ^= ROL32(pmicdata->L, 17); - pmicdata->L += pmicdata->R; - pmicdata->R ^= ((pmicdata->L & 0xff00ff00) >> 8) | - ((pmicdata->L & 0x00ff00ff) << 8); - pmicdata->L += pmicdata->R; - pmicdata->R ^= ROL32(pmicdata->L, 3); - pmicdata->L += pmicdata->R; - pmicdata->R ^= ROR32(pmicdata->L, 2); - pmicdata->L += pmicdata->R; - /* Clear the buffer */ - pmicdata->M = 0; - pmicdata->nBytesInM = 0; - } -} - -void r8712_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nbytes) -{ - /* This is simple */ - while (nbytes > 0) { - secmicappendbyte(pmicdata, *src++); - nbytes--; - } -} - -void r8712_secgetmic(struct mic_data *pmicdata, u8 *dst) -{ - /* Append the minimum padding */ - secmicappendbyte(pmicdata, 0x5a); - secmicappendbyte(pmicdata, 0); - secmicappendbyte(pmicdata, 0); - secmicappendbyte(pmicdata, 0); - secmicappendbyte(pmicdata, 0); - /* and then zeroes until the length is a multiple of 4 */ - while (pmicdata->nBytesInM != 0) - secmicappendbyte(pmicdata, 0); - /* The appendByte function has already computed the result. */ - secmicputuint32(dst, pmicdata->L); - secmicputuint32(dst + 4, pmicdata->R); - /* Reset to the empty message. */ - secmicclear(pmicdata); -} - -void seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len, u8 *mic_code, - u8 pri) -{ - - struct mic_data micdata; - u8 priority[4] = {0x0, 0x0, 0x0, 0x0}; - - r8712_secmicsetkey(&micdata, key); - priority[0] = pri; - /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */ - if (header[1] & 1) { /* ToDS==1 */ - r8712_secmicappend(&micdata, &header[16], 6); /* DA */ - if (header[1] & 2) /* From Ds==1 */ - r8712_secmicappend(&micdata, &header[24], 6); - else - r8712_secmicappend(&micdata, &header[10], 6); - } else { /* ToDS==0 */ - r8712_secmicappend(&micdata, &header[4], 6); /* DA */ - if (header[1] & 2) /* From Ds==1 */ - r8712_secmicappend(&micdata, &header[16], 6); - else - r8712_secmicappend(&micdata, &header[10], 6); - } - r8712_secmicappend(&micdata, &priority[0], 4); - r8712_secmicappend(&micdata, data, data_len); - r8712_secgetmic(&micdata, mic_code); -} - -/* macros for extraction/creation of unsigned char/unsigned short values */ -#define RotR1(v16) ((((v16) >> 1) & 0x7FFF) ^ (((v16) & 1) << 15)) -#define Lo8(v16) ((u8)((v16) & 0x00FF)) -#define Hi8(v16) ((u8)(((v16) >> 8) & 0x00FF)) -#define Lo16(v32) ((u16)((v32) & 0xFFFF)) -#define Hi16(v32) ((u16)(((v32) >> 16) & 0xFFFF)) -#define Mk16(hi, lo) ((lo) ^ (((u16)(hi)) << 8)) - -/* select the Nth 16-bit word of the temporal key unsigned char array TK[] */ -#define TK16(N) Mk16(tk[2 * (N) + 1], tk[2 * (N)]) - -/* S-box lookup: 16 bits --> 16 bits */ -#define _S_(v16) (Sbox1[0][Lo8(v16)] ^ Sbox1[1][Hi8(v16)]) - -/* fixed algorithm "parameters" */ -#define PHASE1_LOOP_CNT 8 /* this needs to be "big enough" */ -#define TA_SIZE 6 /* 48-bit transmitter address */ -#define TK_SIZE 16 /* 128-bit temporal key */ -#define P1K_SIZE 10 /* 80-bit Phase1 key */ -#define RC4_KEY_SIZE 16 /* 128-bit RC4KEY (104 bits unknown) */ - -/* 2-unsigned char by 2-unsigned char subset of the full AES S-box table */ -static const unsigned short Sbox1[2][256] = {/* Sbox for hash (can be in ROM) */ - { - 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, - 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, - 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, - 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, - 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, - 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, - 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, - 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, - 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, - 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, - 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, - 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, - 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, - 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, - 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, - 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, - 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, - 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, - 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, - 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, - 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, - 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, - 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, - 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, - 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, - 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, - 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, - 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, - 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, - 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, - 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, - 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, - }, - { /* second half is unsigned char-reversed version of first! */ - 0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491, - 0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC, - 0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB, - 0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B, - 0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83, - 0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A, - 0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F, - 0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA, - 0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B, - 0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713, - 0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6, - 0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85, - 0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411, - 0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B, - 0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1, - 0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF, - 0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E, - 0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6, - 0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B, - 0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD, - 0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8, - 0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2, - 0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049, - 0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810, - 0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197, - 0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F, - 0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C, - 0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927, - 0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733, - 0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5, - 0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0, - 0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C, - } -}; - -/* - ********************************************************************** - * Routine: Phase 1 -- generate P1K, given TA, TK, IV32 - * - * Inputs: - * tk[] = temporal key [128 bits] - * ta[] = transmitter's MAC address [ 48 bits] - * iv32 = upper 32 bits of IV [ 32 bits] - * Output: - * p1k[] = Phase 1 key [ 80 bits] - * - * Note: - * This function only needs to be called every 2**16 packets, - * although in theory it could be called every packet. - * - ********************************************************************** - */ -static void phase1(u16 *p1k, const u8 *tk, const u8 *ta, u32 iv32) -{ - sint i; - - /* Initialize the 80 bits of P1K[] from IV32 and TA[0..5] */ - p1k[0] = Lo16(iv32); - p1k[1] = Hi16(iv32); - p1k[2] = Mk16(ta[1], ta[0]); /* use TA[] as little-endian */ - p1k[3] = Mk16(ta[3], ta[2]); - p1k[4] = Mk16(ta[5], ta[4]); - /* Now compute an unbalanced Feistel cipher with 80-bit block */ - /* size on the 80-bit block P1K[], using the 128-bit key TK[] */ - for (i = 0; i < PHASE1_LOOP_CNT; i++) { /* Each add is mod 2**16 */ - p1k[0] += _S_(p1k[4] ^ TK16((i & 1) + 0)); - p1k[1] += _S_(p1k[0] ^ TK16((i & 1) + 2)); - p1k[2] += _S_(p1k[1] ^ TK16((i & 1) + 4)); - p1k[3] += _S_(p1k[2] ^ TK16((i & 1) + 6)); - p1k[4] += _S_(p1k[3] ^ TK16((i & 1) + 0)); - p1k[4] += (unsigned short)i; /* avoid "slide attacks" */ - } -} - -/* - ********************************************************************** - * Routine: Phase 2 -- generate RC4KEY, given TK, P1K, IV16 - * - * Inputs: - * tk[] = Temporal key [128 bits] - * p1k[] = Phase 1 output key [ 80 bits] - * iv16 = low 16 bits of IV counter [ 16 bits] - * Output: - * rc4key[] = the key used to encrypt the packet [128 bits] - * - * Note: - * The value {TA,IV32,IV16} for Phase1/Phase2 must be unique - * across all packets using the same key TK value. Then, for a - * given value of TK[], this TKIP48 construction guarantees that - * the final RC4KEY value is unique across all packets. - * - * Suggested implementation optimization: if PPK[] is "overlaid" - * appropriately on RC4KEY[], there is no need for the final - * for loop below that copies the PPK[] result into RC4KEY[]. - * - ********************************************************************** - */ -static void phase2(u8 *rc4key, const u8 *tk, const u16 *p1k, u16 iv16) -{ - sint i; - u16 PPK[6]; /* temporary key for mixing */ - - /* Note: all adds in the PPK[] equations below are mod 2**16 */ - for (i = 0; i < 5; i++) - PPK[i] = p1k[i]; /* first, copy P1K to PPK */ - PPK[5] = p1k[4] + iv16; /* next, add in IV16 */ - /* Bijective non-linear mixing of the 96 bits of PPK[0..5] */ - PPK[0] += _S_(PPK[5] ^ TK16(0)); /* Mix key in each "round" */ - PPK[1] += _S_(PPK[0] ^ TK16(1)); - PPK[2] += _S_(PPK[1] ^ TK16(2)); - PPK[3] += _S_(PPK[2] ^ TK16(3)); - PPK[4] += _S_(PPK[3] ^ TK16(4)); - PPK[5] += _S_(PPK[4] ^ TK16(5)); /* Total # S-box lookups == 6 */ - /* Final sweep: bijective, "linear". Rotates kill LSB correlations */ - PPK[0] += RotR1(PPK[5] ^ TK16(6)); - PPK[1] += RotR1(PPK[0] ^ TK16(7)); /* Use all of TK[] in Phase2 */ - PPK[2] += RotR1(PPK[1]); - PPK[3] += RotR1(PPK[2]); - PPK[4] += RotR1(PPK[3]); - PPK[5] += RotR1(PPK[4]); - /* Note: At this point, for a given key TK[0..15], the 96-bit output */ - /* value PPK[0..5] is guaranteed to be unique, as a function */ - /* of the 96-bit "input" value {TA,IV32,IV16}. That is, P1K */ - /* is now a keyed permutation of {TA,IV32,IV16}. */ - /* Set RC4KEY[0..3], which includes "cleartext" portion of RC4 key */ - rc4key[0] = Hi8(iv16); /* RC4KEY[0..2] is the WEP IV */ - rc4key[1] = (Hi8(iv16) | 0x20) & 0x7F; /* Help avoid weak (FMS) keys */ - rc4key[2] = Lo8(iv16); - rc4key[3] = Lo8((PPK[5] ^ TK16(0)) >> 1); - /* Copy 96 bits of PPK[0..5] to RC4KEY[4..15] (little-endian) */ - for (i = 0; i < 6; i++) { - rc4key[4 + 2 * i] = Lo8(PPK[i]); - rc4key[5 + 2 * i] = Hi8(PPK[i]); - } -} - -/*The hlen isn't include the IV*/ -u32 r8712_tkip_encrypt(struct _adapter *padapter, u8 *pxmitframe) -{ /* exclude ICV */ - u16 pnl; - u32 pnh; - u8 rc4key[16]; - u8 ttkey[16]; - u8 crc[4]; - struct arc4context mycontext; - u32 curfragnum, length; - - u8 *pframe, *payload, *iv, *prwskey; - union pn48 txpn; - struct sta_info *stainfo; - struct pkt_attrib *pattrib = &((struct xmit_frame *)pxmitframe)->attrib; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - u32 res = _SUCCESS; - - if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL) - return _FAIL; - - pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + TXDESC_OFFSET; - /* 4 start to encrypt each fragment */ - if (pattrib->encrypt == _TKIP_) { - if (pattrib->psta) - stainfo = pattrib->psta; - else - stainfo = r8712_get_stainfo(&padapter->stapriv, - &pattrib->ra[0]); - if (stainfo) { - prwskey = &stainfo->x_UncstKey.skey[0]; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; - curfragnum++) { - iv = pframe + pattrib->hdrlen; - payload = pframe + pattrib->iv_len + - pattrib->hdrlen; - GET_TKIP_PN(iv, txpn); - pnl = (u16)(txpn.val); - pnh = (u32)(txpn.val >> 16); - phase1((u16 *)&ttkey[0], prwskey, - &pattrib->ta[0], pnh); - phase2(&rc4key[0], prwskey, (u16 *)&ttkey[0], - pnl); - if ((curfragnum + 1) == pattrib->nr_frags) { - /* 4 the last fragment */ - length = pattrib->last_txcmdsz - - pattrib->hdrlen - - pattrib->iv_len - - pattrib->icv_len; - *((__le32 *)crc) = cpu_to_le32( - getcrc32(payload, length)); - arcfour_init(&mycontext, rc4key, 16); - arcfour_encrypt(&mycontext, payload, - payload, length); - arcfour_encrypt(&mycontext, payload + - length, crc, 4); - } else { - length = pxmitpriv->frag_len - - pattrib->hdrlen - - pattrib->iv_len - - pattrib->icv_len; - *((__le32 *)crc) = cpu_to_le32(getcrc32( - payload, length)); - arcfour_init(&mycontext, rc4key, 16); - arcfour_encrypt(&mycontext, payload, - payload, length); - arcfour_encrypt(&mycontext, - payload + length, crc, - 4); - pframe += pxmitpriv->frag_len; - pframe = (u8 *)RND4((addr_t)(pframe)); - } - } - } else { - res = _FAIL; - } - } - return res; -} - -/* The hlen doesn't include the IV */ -void r8712_tkip_decrypt(struct _adapter *padapter, u8 *precvframe) -{ /* exclude ICV */ - u16 pnl; - u32 pnh; - u8 rc4key[16]; - u8 ttkey[16]; - u8 crc[4]; - struct arc4context mycontext; - u32 length; - u8 *pframe, *payload, *iv, *prwskey, idx = 0; - union pn48 txpn; - struct sta_info *stainfo; - struct rx_pkt_attrib *prxattrib = &((union recv_frame *) - precvframe)->u.hdr.attrib; - struct security_priv *psecuritypriv = &padapter->securitypriv; - - pframe = (unsigned char *)((union recv_frame *) - precvframe)->u.hdr.rx_data; - /* 4 start to decrypt recvframe */ - if (prxattrib->encrypt == _TKIP_) { - stainfo = r8712_get_stainfo(&padapter->stapriv, - &prxattrib->ta[0]); - if (stainfo) { - iv = pframe + prxattrib->hdrlen; - payload = pframe + prxattrib->iv_len + - prxattrib->hdrlen; - length = ((union recv_frame *)precvframe)-> - u.hdr.len - prxattrib->hdrlen - - prxattrib->iv_len; - if (is_multicast_ether_addr(prxattrib->ra)) { - idx = iv[3]; - prwskey = &psecuritypriv->XGrpKey[ - ((idx >> 6) & 0x3) - 1].skey[0]; - if (!psecuritypriv->binstallGrpkey) - return; - } else { - prwskey = &stainfo->x_UncstKey.skey[0]; - } - GET_TKIP_PN(iv, txpn); - pnl = (u16)(txpn.val); - pnh = (u32)(txpn.val >> 16); - phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0], - pnh); - phase2(&rc4key[0], prwskey, (unsigned short *) - &ttkey[0], pnl); - /* 4 decrypt payload include icv */ - arcfour_init(&mycontext, rc4key, 16); - arcfour_encrypt(&mycontext, payload, payload, length); - *((__le32 *)crc) = cpu_to_le32(getcrc32(payload, - length - 4)); - } - } -} - -/* 3 =====AES related===== */ - -#define MAX_MSG_SIZE 2048 -/*****************************/ -/******** SBOX Table *********/ -/*****************************/ - -static const u8 sbox_table[256] = { - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, - 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, - 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, - 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, - 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, - 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, - 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, - 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, - 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, - 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, - 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, - 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, - 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, - 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, - 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, - 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, - 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 -}; - -/****************************************/ -/* aes128k128d() */ -/* Performs a 128 bit AES encrypt with */ -/* 128 bit data. */ -/****************************************/ -static void xor_128(u8 *a, u8 *b, u8 *out) -{ - sint i; - - for (i = 0; i < 16; i++) - out[i] = a[i] ^ b[i]; -} - -static void xor_32(u8 *a, u8 *b, u8 *out) -{ - sint i; - - for (i = 0; i < 4; i++) - out[i] = a[i] ^ b[i]; -} - -static u8 sbox(u8 a) -{ - return sbox_table[(sint)a]; -} - -static void next_key(u8 *key, sint round) -{ - u8 rcon; - u8 sbox_key[4]; - static const u8 rcon_table[12] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x1b, 0x36, 0x36, 0x36 - }; - - sbox_key[0] = sbox(key[13]); - sbox_key[1] = sbox(key[14]); - sbox_key[2] = sbox(key[15]); - sbox_key[3] = sbox(key[12]); - rcon = rcon_table[round]; - xor_32(&key[0], sbox_key, &key[0]); - key[0] = key[0] ^ rcon; - xor_32(&key[4], &key[0], &key[4]); - xor_32(&key[8], &key[4], &key[8]); - xor_32(&key[12], &key[8], &key[12]); -} - -static void byte_sub(u8 *in, u8 *out) -{ - sint i; - - for (i = 0; i < 16; i++) - out[i] = sbox(in[i]); -} - -static void shift_row(u8 *in, u8 *out) -{ - out[0] = in[0]; - out[1] = in[5]; - out[2] = in[10]; - out[3] = in[15]; - out[4] = in[4]; - out[5] = in[9]; - out[6] = in[14]; - out[7] = in[3]; - out[8] = in[8]; - out[9] = in[13]; - out[10] = in[2]; - out[11] = in[7]; - out[12] = in[12]; - out[13] = in[1]; - out[14] = in[6]; - out[15] = in[11]; -} - -static void mix_column(u8 *in, u8 *out) -{ - sint i; - u8 add1b[4]; - u8 add1bf7[4]; - u8 rotl[4]; - u8 swap_halves[4]; - u8 andf7[4]; - u8 rotr[4]; - u8 temp[4]; - u8 tempb[4]; - - for (i = 0; i < 4; i++) { - if ((in[i] & 0x80) == 0x80) - add1b[i] = 0x1b; - else - add1b[i] = 0x00; - } - swap_halves[0] = in[2]; /* Swap halves */ - swap_halves[1] = in[3]; - swap_halves[2] = in[0]; - swap_halves[3] = in[1]; - rotl[0] = in[3]; /* Rotate left 8 bits */ - rotl[1] = in[0]; - rotl[2] = in[1]; - rotl[3] = in[2]; - andf7[0] = in[0] & 0x7f; - andf7[1] = in[1] & 0x7f; - andf7[2] = in[2] & 0x7f; - andf7[3] = in[3] & 0x7f; - for (i = 3; i > 0; i--) { /* logical shift left 1 bit */ - andf7[i] = andf7[i] << 1; - if ((andf7[i - 1] & 0x80) == 0x80) - andf7[i] = (andf7[i] | 0x01); - } - andf7[0] = andf7[0] << 1; - andf7[0] = andf7[0] & 0xfe; - xor_32(add1b, andf7, add1bf7); - xor_32(in, add1bf7, rotr); - temp[0] = rotr[0]; /* Rotate right 8 bits */ - rotr[0] = rotr[1]; - rotr[1] = rotr[2]; - rotr[2] = rotr[3]; - rotr[3] = temp[0]; - xor_32(add1bf7, rotr, temp); - xor_32(swap_halves, rotl, tempb); - xor_32(temp, tempb, out); -} - -static void aes128k128d(u8 *key, u8 *data, u8 *ciphertext) -{ - sint round; - sint i; - u8 intermediatea[16]; - u8 intermediateb[16]; - u8 round_key[16]; - - for (i = 0; i < 16; i++) - round_key[i] = key[i]; - for (round = 0; round < 11; round++) { - if (round == 0) { - xor_128(round_key, data, ciphertext); - next_key(round_key, round); - } else if (round == 10) { - byte_sub(ciphertext, intermediatea); - shift_row(intermediatea, intermediateb); - xor_128(intermediateb, round_key, ciphertext); - } else { /* 1 - 9 */ - byte_sub(ciphertext, intermediatea); - shift_row(intermediatea, intermediateb); - mix_column(&intermediateb[0], &intermediatea[0]); - mix_column(&intermediateb[4], &intermediatea[4]); - mix_column(&intermediateb[8], &intermediatea[8]); - mix_column(&intermediateb[12], &intermediatea[12]); - xor_128(intermediatea, round_key, ciphertext); - next_key(round_key, round); - } - } -} - -/************************************************/ -/* construct_mic_iv() */ -/* Builds the MIC IV from header fields and PN */ -/************************************************/ -static void construct_mic_iv(u8 *mic_iv, sint qc_exists, sint a4_exists, - u8 *mpdu, uint payload_length, u8 *pn_vector) -{ - sint i; - - mic_iv[0] = 0x59; - if (qc_exists && a4_exists) - mic_iv[1] = mpdu[30] & 0x0f; /* QoS_TC */ - if (qc_exists && !a4_exists) - mic_iv[1] = mpdu[24] & 0x0f; /* mute bits 7-4 */ - if (!qc_exists) - mic_iv[1] = 0x00; - for (i = 2; i < 8; i++) - mic_iv[i] = mpdu[i + 8]; - for (i = 8; i < 14; i++) - mic_iv[i] = pn_vector[13 - i]; /* mic_iv[8:13] = PN[5:0] */ - mic_iv[14] = (unsigned char)(payload_length / 256); - mic_iv[15] = (unsigned char)(payload_length % 256); -} - -/************************************************/ -/* construct_mic_header1() */ -/* Builds the first MIC header block from */ -/* header fields. */ -/************************************************/ -static void construct_mic_header1(u8 *mic_header1, sint header_length, u8 *mpdu) -{ - mic_header1[0] = (u8)((header_length - 2) / 256); - mic_header1[1] = (u8)((header_length - 2) % 256); - mic_header1[2] = mpdu[0] & 0xcf; /* Mute CF poll & CF ack bits */ - /* Mute retry, more data and pwr mgt bits */ - mic_header1[3] = mpdu[1] & 0xc7; - mic_header1[4] = mpdu[4]; /* A1 */ - mic_header1[5] = mpdu[5]; - mic_header1[6] = mpdu[6]; - mic_header1[7] = mpdu[7]; - mic_header1[8] = mpdu[8]; - mic_header1[9] = mpdu[9]; - mic_header1[10] = mpdu[10]; /* A2 */ - mic_header1[11] = mpdu[11]; - mic_header1[12] = mpdu[12]; - mic_header1[13] = mpdu[13]; - mic_header1[14] = mpdu[14]; - mic_header1[15] = mpdu[15]; -} - -/************************************************/ -/* construct_mic_header2() */ -/* Builds the last MIC header block from */ -/* header fields. */ -/************************************************/ -static void construct_mic_header2(u8 *mic_header2, u8 *mpdu, sint a4_exists, - sint qc_exists) -{ - sint i; - - for (i = 0; i < 16; i++) - mic_header2[i] = 0x00; - mic_header2[0] = mpdu[16]; /* A3 */ - mic_header2[1] = mpdu[17]; - mic_header2[2] = mpdu[18]; - mic_header2[3] = mpdu[19]; - mic_header2[4] = mpdu[20]; - mic_header2[5] = mpdu[21]; - mic_header2[6] = 0x00; - mic_header2[7] = 0x00; /* mpdu[23]; */ - if (!qc_exists && a4_exists) - for (i = 0; i < 6; i++) - mic_header2[8 + i] = mpdu[24 + i]; /* A4 */ - if (qc_exists && !a4_exists) { - mic_header2[8] = mpdu[24] & 0x0f; /* mute bits 15 - 4 */ - mic_header2[9] = mpdu[25] & 0x00; - } - if (qc_exists && a4_exists) { - for (i = 0; i < 6; i++) - mic_header2[8 + i] = mpdu[24 + i]; /* A4 */ - mic_header2[14] = mpdu[30] & 0x0f; - mic_header2[15] = mpdu[31] & 0x00; - } -} - -/************************************************/ -/* construct_mic_header2() */ -/* Builds the last MIC header block from */ -/* header fields. */ -/************************************************/ -static void construct_ctr_preload(u8 *ctr_preload, - sint a4_exists, sint qc_exists, - u8 *mpdu, u8 *pn_vector, sint c) -{ - sint i; - - for (i = 0; i < 16; i++) - ctr_preload[i] = 0x00; - i = 0; - ctr_preload[0] = 0x01; /* flag */ - if (qc_exists && a4_exists) - ctr_preload[1] = mpdu[30] & 0x0f; - if (qc_exists && !a4_exists) - ctr_preload[1] = mpdu[24] & 0x0f; - for (i = 2; i < 8; i++) - ctr_preload[i] = mpdu[i + 8]; - for (i = 8; i < 14; i++) - ctr_preload[i] = pn_vector[13 - i]; - ctr_preload[14] = (unsigned char)(c / 256); /* Ctr */ - ctr_preload[15] = (unsigned char)(c % 256); -} - -/************************************/ -/* bitwise_xor() */ -/* A 128 bit, bitwise exclusive or */ -/************************************/ -static void bitwise_xor(u8 *ina, u8 *inb, u8 *out) -{ - sint i; - - for (i = 0; i < 16; i++) - out[i] = ina[i] ^ inb[i]; -} - -static void aes_cipher(u8 *key, uint hdrlen, - u8 *pframe, uint plen) -{ - uint qc_exists, a4_exists, i, j, payload_remainder; - uint num_blocks, payload_index; - - u8 pn_vector[6]; - u8 mic_iv[16]; - u8 mic_header1[16]; - u8 mic_header2[16]; - u8 ctr_preload[16]; - - /* Intermediate Buffers */ - u8 chain_buffer[16]; - u8 aes_out[16]; - u8 padded_buffer[16]; - u8 mic[8]; - u16 frtype = GetFrameType(pframe); - u16 frsubtype = GetFrameSubType(pframe); - - frsubtype >>= 4; - memset((void *)mic_iv, 0, 16); - memset((void *)mic_header1, 0, 16); - memset((void *)mic_header2, 0, 16); - memset((void *)ctr_preload, 0, 16); - memset((void *)chain_buffer, 0, 16); - memset((void *)aes_out, 0, 16); - memset((void *)padded_buffer, 0, 16); - - if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN)) - a4_exists = 0; - else - a4_exists = 1; - - if ((frtype == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA_CFACK)) || - (frtype == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA_CFPOLL)) || - (frtype == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA_CFACKPOLL))) { - qc_exists = 1; - if (hdrlen != WLAN_HDR_A3_QOS_LEN) - hdrlen += 2; - } else if ((frsubtype == 0x08) || - (frsubtype == 0x09) || - (frsubtype == 0x0a) || - (frsubtype == 0x0b)) { - if (hdrlen != WLAN_HDR_A3_QOS_LEN) - hdrlen += 2; - qc_exists = 1; - } else { - qc_exists = 0; - } - pn_vector[0] = pframe[hdrlen]; - pn_vector[1] = pframe[hdrlen + 1]; - pn_vector[2] = pframe[hdrlen + 4]; - pn_vector[3] = pframe[hdrlen + 5]; - pn_vector[4] = pframe[hdrlen + 6]; - pn_vector[5] = pframe[hdrlen + 7]; - construct_mic_iv(mic_iv, qc_exists, a4_exists, pframe, plen, pn_vector); - construct_mic_header1(mic_header1, hdrlen, pframe); - construct_mic_header2(mic_header2, pframe, a4_exists, qc_exists); - payload_remainder = plen % 16; - num_blocks = plen / 16; - /* Find start of payload */ - payload_index = hdrlen + 8; - /* Calculate MIC */ - aes128k128d(key, mic_iv, aes_out); - bitwise_xor(aes_out, mic_header1, chain_buffer); - aes128k128d(key, chain_buffer, aes_out); - bitwise_xor(aes_out, mic_header2, chain_buffer); - aes128k128d(key, chain_buffer, aes_out); - for (i = 0; i < num_blocks; i++) { - bitwise_xor(aes_out, &pframe[payload_index], chain_buffer); - payload_index += 16; - aes128k128d(key, chain_buffer, aes_out); - } - /* Add on the final payload block if it needs padding */ - if (payload_remainder > 0) { - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < payload_remainder; j++) - padded_buffer[j] = pframe[payload_index++]; - bitwise_xor(aes_out, padded_buffer, chain_buffer); - aes128k128d(key, chain_buffer, aes_out); - } - for (j = 0; j < 8; j++) - mic[j] = aes_out[j]; - /* Insert MIC into payload */ - for (j = 0; j < 8; j++) - pframe[payload_index + j] = mic[j]; - payload_index = hdrlen + 8; - for (i = 0; i < num_blocks; i++) { - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - pframe, pn_vector, i + 1); - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, &pframe[payload_index], chain_buffer); - for (j = 0; j < 16; j++) - pframe[payload_index++] = chain_buffer[j]; - } - if (payload_remainder > 0) { /* If short final block, then pad it,*/ - /* encrypt and copy unpadded part back */ - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - pframe, pn_vector, num_blocks + 1); - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < payload_remainder; j++) - padded_buffer[j] = pframe[payload_index + j]; - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, padded_buffer, chain_buffer); - for (j = 0; j < payload_remainder; j++) - pframe[payload_index++] = chain_buffer[j]; - } - /* Encrypt the MIC */ - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - pframe, pn_vector, 0); - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < 8; j++) - padded_buffer[j] = pframe[j + hdrlen + 8 + plen]; - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, padded_buffer, chain_buffer); - for (j = 0; j < 8; j++) - pframe[payload_index++] = chain_buffer[j]; -} - -u32 r8712_aes_encrypt(struct _adapter *padapter, u8 *pxmitframe) -{ /* exclude ICV */ - /* Intermediate Buffers */ - sint curfragnum, length; - u8 *pframe, *prwskey; - struct sta_info *stainfo; - struct pkt_attrib *pattrib = &((struct xmit_frame *) - pxmitframe)->attrib; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - u32 res = _SUCCESS; - - if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL) - return _FAIL; - pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + TXDESC_OFFSET; - /* 4 start to encrypt each fragment */ - if (pattrib->encrypt == _AES_) { - if (pattrib->psta) - stainfo = pattrib->psta; - else - stainfo = r8712_get_stainfo(&padapter->stapriv, - &pattrib->ra[0]); - if (stainfo) { - prwskey = &stainfo->x_UncstKey.skey[0]; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; - curfragnum++) { - if ((curfragnum + 1) == pattrib->nr_frags) { - length = pattrib->last_txcmdsz - - pattrib->hdrlen - - pattrib->iv_len - - pattrib->icv_len; - aes_cipher(prwskey, pattrib->hdrlen, - pframe, length); - } else { - length = pxmitpriv->frag_len - - pattrib->hdrlen - - pattrib->iv_len - - pattrib->icv_len; - aes_cipher(prwskey, pattrib->hdrlen, - pframe, length); - pframe += pxmitpriv->frag_len; - pframe = (u8 *)RND4((addr_t)(pframe)); - } - } - } else { - res = _FAIL; - } - } - return res; -} - -static void aes_decipher(u8 *key, uint hdrlen, - u8 *pframe, uint plen) -{ - static u8 message[MAX_MSG_SIZE]; - uint qc_exists, a4_exists, i, j, payload_remainder; - uint num_blocks, payload_index; - u8 pn_vector[6]; - u8 mic_iv[16]; - u8 mic_header1[16]; - u8 mic_header2[16]; - u8 ctr_preload[16]; - /* Intermediate Buffers */ - u8 chain_buffer[16]; - u8 aes_out[16]; - u8 padded_buffer[16]; - u8 mic[8]; - uint frtype = GetFrameType(pframe); - uint frsubtype = GetFrameSubType(pframe); - - frsubtype >>= 4; - memset((void *)mic_iv, 0, 16); - memset((void *)mic_header1, 0, 16); - memset((void *)mic_header2, 0, 16); - memset((void *)ctr_preload, 0, 16); - memset((void *)chain_buffer, 0, 16); - memset((void *)aes_out, 0, 16); - memset((void *)padded_buffer, 0, 16); - /* start to decrypt the payload */ - /*(plen including llc, payload and mic) */ - num_blocks = (plen - 8) / 16; - payload_remainder = (plen - 8) % 16; - pn_vector[0] = pframe[hdrlen]; - pn_vector[1] = pframe[hdrlen + 1]; - pn_vector[2] = pframe[hdrlen + 4]; - pn_vector[3] = pframe[hdrlen + 5]; - pn_vector[4] = pframe[hdrlen + 6]; - pn_vector[5] = pframe[hdrlen + 7]; - if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN)) - a4_exists = 0; - else - a4_exists = 1; - if ((frtype == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA_CFACK)) || - (frtype == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA_CFPOLL)) || - (frtype == (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA_CFACKPOLL))) { - qc_exists = 1; - if (hdrlen != WLAN_HDR_A3_QOS_LEN) - hdrlen += 2; - } else if ((frsubtype == 0x08) || - (frsubtype == 0x09) || - (frsubtype == 0x0a) || - (frsubtype == 0x0b)) { - if (hdrlen != WLAN_HDR_A3_QOS_LEN) - hdrlen += 2; - qc_exists = 1; - } else { - qc_exists = 0; - } - /* now, decrypt pframe with hdrlen offset and plen long */ - payload_index = hdrlen + 8; /* 8 is for extiv */ - for (i = 0; i < num_blocks; i++) { - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - pframe, pn_vector, i + 1); - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, &pframe[payload_index], chain_buffer); - for (j = 0; j < 16; j++) - pframe[payload_index++] = chain_buffer[j]; - } - if (payload_remainder > 0) { /* If short final block, pad it,*/ - /* encrypt it and copy the unpadded part back */ - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - pframe, pn_vector, num_blocks + 1); - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < payload_remainder; j++) - padded_buffer[j] = pframe[payload_index + j]; - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, padded_buffer, chain_buffer); - for (j = 0; j < payload_remainder; j++) - pframe[payload_index++] = chain_buffer[j]; - } - /* start to calculate the mic */ - memcpy((void *)message, pframe, (hdrlen + plen + 8)); - pn_vector[0] = pframe[hdrlen]; - pn_vector[1] = pframe[hdrlen + 1]; - pn_vector[2] = pframe[hdrlen + 4]; - pn_vector[3] = pframe[hdrlen + 5]; - pn_vector[4] = pframe[hdrlen + 6]; - pn_vector[5] = pframe[hdrlen + 7]; - construct_mic_iv(mic_iv, qc_exists, a4_exists, message, plen - 8, - pn_vector); - construct_mic_header1(mic_header1, hdrlen, message); - construct_mic_header2(mic_header2, message, a4_exists, qc_exists); - payload_remainder = (plen - 8) % 16; - num_blocks = (plen - 8) / 16; - /* Find start of payload */ - payload_index = hdrlen + 8; - /* Calculate MIC */ - aes128k128d(key, mic_iv, aes_out); - bitwise_xor(aes_out, mic_header1, chain_buffer); - aes128k128d(key, chain_buffer, aes_out); - bitwise_xor(aes_out, mic_header2, chain_buffer); - aes128k128d(key, chain_buffer, aes_out); - for (i = 0; i < num_blocks; i++) { - bitwise_xor(aes_out, &message[payload_index], chain_buffer); - payload_index += 16; - aes128k128d(key, chain_buffer, aes_out); - } - /* Add on the final payload block if it needs padding */ - if (payload_remainder > 0) { - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < payload_remainder; j++) - padded_buffer[j] = message[payload_index++]; - bitwise_xor(aes_out, padded_buffer, chain_buffer); - aes128k128d(key, chain_buffer, aes_out); - } - for (j = 0; j < 8; j++) - mic[j] = aes_out[j]; - /* Insert MIC into payload */ - for (j = 0; j < 8; j++) - message[payload_index + j] = mic[j]; - payload_index = hdrlen + 8; - for (i = 0; i < num_blocks; i++) { - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - message, pn_vector, i + 1); - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, &message[payload_index], chain_buffer); - for (j = 0; j < 16; j++) - message[payload_index++] = chain_buffer[j]; - } - if (payload_remainder > 0) { /* If short final block, pad it,*/ - /* encrypt and copy unpadded part back */ - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, - message, pn_vector, num_blocks + 1); - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < payload_remainder; j++) - padded_buffer[j] = message[payload_index + j]; - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, padded_buffer, chain_buffer); - for (j = 0; j < payload_remainder; j++) - message[payload_index++] = chain_buffer[j]; - } - /* Encrypt the MIC */ - construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, - pn_vector, 0); - for (j = 0; j < 16; j++) - padded_buffer[j] = 0x00; - for (j = 0; j < 8; j++) - padded_buffer[j] = message[j + hdrlen + plen]; - aes128k128d(key, ctr_preload, aes_out); - bitwise_xor(aes_out, padded_buffer, chain_buffer); - for (j = 0; j < 8; j++) - message[payload_index++] = chain_buffer[j]; - /* compare the mic */ -} - -void r8712_aes_decrypt(struct _adapter *padapter, u8 *precvframe) -{ /* exclude ICV */ - /* Intermediate Buffers */ - sint length; - u8 *pframe, *prwskey, *iv, idx; - struct sta_info *stainfo; - struct rx_pkt_attrib *prxattrib = &((union recv_frame *) - precvframe)->u.hdr.attrib; - struct security_priv *psecuritypriv = &padapter->securitypriv; - - pframe = (unsigned char *)((union recv_frame *)precvframe)-> - u.hdr.rx_data; - /* 4 start to encrypt each fragment */ - if (prxattrib->encrypt == _AES_) { - stainfo = r8712_get_stainfo(&padapter->stapriv, - &prxattrib->ta[0]); - if (stainfo) { - if (is_multicast_ether_addr(prxattrib->ra)) { - iv = pframe + prxattrib->hdrlen; - idx = iv[3]; - prwskey = &psecuritypriv->XGrpKey[ - ((idx >> 6) & 0x3) - 1].skey[0]; - if (!psecuritypriv->binstallGrpkey) - return; - - } else { - prwskey = &stainfo->x_UncstKey.skey[0]; - } - length = ((union recv_frame *)precvframe)-> - u.hdr.len - prxattrib->hdrlen - - prxattrib->iv_len; - aes_decipher(prwskey, prxattrib->hdrlen, pframe, - length); - } - } -} - -void r8712_use_tkipkey_handler(struct timer_list *t) -{ - struct _adapter *padapter = - from_timer(padapter, t, securitypriv.tkip_timer); - - padapter->securitypriv.busetkipkey = true; -} diff --git a/drivers/staging/rtl8712/rtl871x_security.h b/drivers/staging/rtl8712/rtl871x_security.h deleted file mode 100644 index 8461b7f05359a..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_security.h +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __RTL871X_SECURITY_H_ -#define __RTL871X_SECURITY_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -#define _NO_PRIVACY_ 0x0 -#define _WEP40_ 0x1 -#define _TKIP_ 0x2 -#define _TKIP_WTMIC_ 0x3 -#define _AES_ 0x4 -#define _WEP104_ 0x5 - -#define _WPA_IE_ID_ 0xdd -#define _WPA2_IE_ID_ 0x30 - -#ifndef Ndis802_11AuthModeWPA2 -#define Ndis802_11AuthModeWPA2 (Ndis802_11AuthModeWPANone + 1) -#endif - -#ifndef Ndis802_11AuthModeWPA2PSK -#define Ndis802_11AuthModeWPA2PSK (Ndis802_11AuthModeWPANone + 2) -#endif - -union pn48 { - u64 val; -#if defined(__BIG_ENDIAN) - struct { - u8 TSC7; - u8 TSC6; - u8 TSC5; - u8 TSC4; - u8 TSC3; - u8 TSC2; - u8 TSC1; - u8 TSC0; - } _byte_; -#else - struct { - u8 TSC0; - u8 TSC1; - u8 TSC2; - u8 TSC3; - u8 TSC4; - u8 TSC5; - u8 TSC6; - u8 TSC7; - } _byte_; -#endif -}; - -union Keytype { - u8 skey[16]; - u32 lkey[4]; -}; - -struct RT_PMKID_LIST { - u8 bUsed; - u8 Bssid[6]; - u8 PMKID[16]; - u8 SsidBuf[33]; - u8 *ssid_octet; - u16 ssid_length; -}; - -struct security_priv { - u32 AuthAlgrthm; /* 802.11 auth, could be open, shared, - * 8021x and authswitch - */ - u32 PrivacyAlgrthm; /* This specify the privacy for shared - * auth. algorithm. - */ - u32 PrivacyKeyIndex; /* this is only valid for legendary - * wep, 0~3 for key id. - */ - union Keytype DefKey[4]; /* this is only valid for def. key */ - u32 DefKeylen[4]; - u32 XGrpPrivacy; /* This specify the privacy algthm. - * used for Grp key - */ - u32 XGrpKeyid; /* key id used for Grp Key */ - union Keytype XGrpKey[2]; /* 802.1x Group Key, for - * inx0 and inx1 - */ - union Keytype XGrptxmickey[2]; - union Keytype XGrprxmickey[2]; - union pn48 Grptxpn; /* PN48 used for Grp Key xmit. */ - union pn48 Grprxpn; /* PN48 used for Grp Key recv. */ - u8 wps_hw_pbc_pressed;/*for hw pbc pressed*/ - u8 wps_phase;/*for wps*/ - u8 wps_ie[MAX_WPA_IE_LEN << 2]; - int wps_ie_len; - u8 binstallGrpkey; - u8 busetkipkey; - struct timer_list tkip_timer; - u8 bcheck_grpkey; - u8 bgrpkey_handshake; - s32 sw_encrypt; /* from registry_priv */ - s32 sw_decrypt; /* from registry_priv */ - s32 hw_decrypted; /* if the rx packets is hw_decrypted==false, - * it means the hw has not been ready. - */ - u32 ndisauthtype; /* keeps the auth_type & enc_status from upper - * layer ioctl(wpa_supplicant or wzc) - */ - u32 ndisencryptstatus; - struct wlan_bssid_ex sec_bss; /* for joinbss (h2c buffer) usage */ - struct NDIS_802_11_WEP ndiswep; - u8 assoc_info[600]; - u8 szofcapability[256]; /* for wpa2 usage */ - u8 oidassociation[512]; /* for wpa/wpa2 usage */ - u8 authenticator_ie[256]; /* store ap security information element */ - u8 supplicant_ie[256]; /* store sta security information element */ - /* for tkip countermeasure */ - u32 last_mic_err_time; - u8 btkip_countermeasure; - u8 btkip_wait_report; - u32 btkip_countermeasure_time; - /*------------------------------------------------------------------- - * For WPA2 Pre-Authentication. - *------------------------------------------------------------------ - **/ - struct RT_PMKID_LIST PMKIDList[NUM_PMKID_CACHE]; - u8 PMKIDIndex; -}; - -#define GET_ENCRY_ALGO(psecuritypriv, psta, encry_algo, bmcst) \ -do { \ - switch (psecuritypriv->AuthAlgrthm) { \ - case 0: \ - case 1: \ - case 3: \ - encry_algo = (u8)psecuritypriv->PrivacyAlgrthm; \ - break; \ - case 2: \ - if (bmcst) \ - encry_algo = (u8)psecuritypriv->XGrpPrivacy; \ - else \ - encry_algo = (u8)psta->XPrivacy; \ - break; \ - } \ -} while (0) -#define SET_ICE_IV_LEN(iv_len, icv_len, encrypt)\ -do {\ - switch (encrypt) { \ - case _WEP40_: \ - case _WEP104_: \ - iv_len = 4; \ - icv_len = 4; \ - break; \ - case _TKIP_: \ - iv_len = 8; \ - icv_len = 4; \ - break; \ - case _AES_: \ - iv_len = 8; \ - icv_len = 8; \ - break; \ - default: \ - iv_len = 0; \ - icv_len = 0; \ - break; \ - } \ -} while (0) -#define GET_TKIP_PN(iv, txpn) \ -do {\ - txpn._byte_.TSC0 = iv[2];\ - txpn._byte_.TSC1 = iv[0];\ - txpn._byte_.TSC2 = iv[4];\ - txpn._byte_.TSC3 = iv[5];\ - txpn._byte_.TSC4 = iv[6];\ - txpn._byte_.TSC5 = iv[7];\ -} while (0) - -#define ROL32(A, n) (((A) << (n)) | (((A) >> (32 - (n))) & ((1UL << (n)) - 1))) -#define ROR32(A, n) ROL32((A), 32 - (n)) - -struct mic_data { - u32 K0, K1; /* Key */ - u32 L, R; /* Current state */ - u32 M; /* Message accumulator (single word) */ - u32 nBytesInM; /* # bytes in M */ -}; - -void seccalctkipmic( - u8 *key, - u8 *header, - u8 *data, - u32 data_len, - u8 *Miccode, - u8 priority); - -void r8712_secmicsetkey(struct mic_data *pmicdata, u8 *key); -void r8712_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nBytes); -void r8712_secgetmic(struct mic_data *pmicdata, u8 *dst); -u32 r8712_aes_encrypt(struct _adapter *padapter, u8 *pxmitframe); -u32 r8712_tkip_encrypt(struct _adapter *padapter, u8 *pxmitframe); -void r8712_wep_encrypt(struct _adapter *padapter, u8 *pxmitframe); -void r8712_aes_decrypt(struct _adapter *padapter, u8 *precvframe); -void r8712_tkip_decrypt(struct _adapter *padapter, u8 *precvframe); -void r8712_wep_decrypt(struct _adapter *padapter, u8 *precvframe); -void r8712_use_tkipkey_handler(struct timer_list *t); - -#endif /*__RTL871X_SECURITY_H_ */ - diff --git a/drivers/staging/rtl8712/rtl871x_sta_mgt.c b/drivers/staging/rtl8712/rtl871x_sta_mgt.c deleted file mode 100644 index 2c806a0105bf6..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_sta_mgt.c +++ /dev/null @@ -1,263 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_sta_mgt.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_STA_MGT_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "xmit_osdep.h" -#include "sta_info.h" - -static void _init_stainfo(struct sta_info *psta) -{ - memset((u8 *)psta, 0, sizeof(struct sta_info)); - spin_lock_init(&psta->lock); - INIT_LIST_HEAD(&psta->list); - INIT_LIST_HEAD(&psta->hash_list); - _r8712_init_sta_xmit_priv(&psta->sta_xmitpriv); - _r8712_init_sta_recv_priv(&psta->sta_recvpriv); - INIT_LIST_HEAD(&psta->asoc_list); - INIT_LIST_HEAD(&psta->auth_list); -} - -int _r8712_init_sta_priv(struct sta_priv *pstapriv) -{ - struct sta_info *psta; - s32 i; - - pstapriv->pallocated_stainfo_buf = kmalloc(sizeof(struct sta_info) * - NUM_STA + 4, GFP_ATOMIC); - if (!pstapriv->pallocated_stainfo_buf) - return -ENOMEM; - pstapriv->pstainfo_buf = pstapriv->pallocated_stainfo_buf + 4 - - ((addr_t)(pstapriv->pallocated_stainfo_buf) & 3); - _init_queue(&pstapriv->free_sta_queue); - spin_lock_init(&pstapriv->sta_hash_lock); - pstapriv->asoc_sta_count = 0; - _init_queue(&pstapriv->sleep_q); - _init_queue(&pstapriv->wakeup_q); - psta = (struct sta_info *)(pstapriv->pstainfo_buf); - for (i = 0; i < NUM_STA; i++) { - _init_stainfo(psta); - INIT_LIST_HEAD(&(pstapriv->sta_hash[i])); - list_add_tail(&psta->list, &pstapriv->free_sta_queue.queue); - psta++; - } - INIT_LIST_HEAD(&pstapriv->asoc_list); - INIT_LIST_HEAD(&pstapriv->auth_list); - return 0; -} - -/* this function is used to free the memory of lock || sema for all stainfos */ -static void mfree_all_stainfo(struct sta_priv *pstapriv) -{ - unsigned long irqL; - struct list_head *plist, *phead; - - spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); - phead = &pstapriv->free_sta_queue.queue; - plist = phead->next; - while (!end_of_queue_search(phead, plist)) - plist = plist->next; - - spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); -} - -void _r8712_free_sta_priv(struct sta_priv *pstapriv) -{ - if (pstapriv) { - /* be done before free sta_hash_lock */ - mfree_all_stainfo(pstapriv); - kfree(pstapriv->pallocated_stainfo_buf); - } -} - -struct sta_info *r8712_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) -{ - s32 index; - struct list_head *phash_list; - struct sta_info *psta; - struct __queue *pfree_sta_queue; - struct recv_reorder_ctrl *preorder_ctrl; - int i = 0; - u16 wRxSeqInitialValue = 0xffff; - unsigned long flags; - - pfree_sta_queue = &pstapriv->free_sta_queue; - spin_lock_irqsave(&pfree_sta_queue->lock, flags); - psta = list_first_entry_or_null(&pfree_sta_queue->queue, - struct sta_info, list); - if (psta) { - list_del_init(&psta->list); - _init_stainfo(psta); - memcpy(psta->hwaddr, hwaddr, ETH_ALEN); - index = wifi_mac_hash(hwaddr); - if (index >= NUM_STA) { - psta = NULL; - goto exit; - } - phash_list = &pstapriv->sta_hash[index]; - list_add_tail(&psta->hash_list, phash_list); - pstapriv->asoc_sta_count++; - -/* For the SMC router, the sequence number of first packet of WPS handshake - * will be 0. In this case, this packet will be dropped by recv_decache function - * if we use the 0x00 as the default value for tid_rxseq variable. So, we - * initialize the tid_rxseq variable as the 0xffff. - */ - for (i = 0; i < 16; i++) - memcpy(&psta->sta_recvpriv.rxcache.tid_rxseq[i], - &wRxSeqInitialValue, 2); - /* for A-MPDU Rx reordering buffer control */ - for (i = 0; i < 16; i++) { - preorder_ctrl = &psta->recvreorder_ctrl[i]; - preorder_ctrl->padapter = pstapriv->padapter; - preorder_ctrl->indicate_seq = 0xffff; - preorder_ctrl->wend_b = 0xffff; - preorder_ctrl->wsize_b = 64; - _init_queue(&preorder_ctrl->pending_recvframe_queue); - r8712_init_recv_timer(preorder_ctrl); - } - } -exit: - spin_unlock_irqrestore(&pfree_sta_queue->lock, flags); - return psta; -} - -/* using pstapriv->sta_hash_lock to protect */ -void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta) -{ - int i; - unsigned long irqL0; - struct __queue *pfree_sta_queue; - struct recv_reorder_ctrl *preorder_ctrl; - struct sta_xmit_priv *pstaxmitpriv; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct sta_priv *pstapriv = &padapter->stapriv; - - if (!psta) - return; - pfree_sta_queue = &pstapriv->free_sta_queue; - pstaxmitpriv = &psta->sta_xmitpriv; - spin_lock_irqsave(&(pxmitpriv->vo_pending.lock), irqL0); - r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vo_q.sta_pending); - list_del_init(&(pstaxmitpriv->vo_q.tx_pending)); - spin_unlock_irqrestore(&(pxmitpriv->vo_pending.lock), irqL0); - spin_lock_irqsave(&(pxmitpriv->vi_pending.lock), irqL0); - r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vi_q.sta_pending); - list_del_init(&(pstaxmitpriv->vi_q.tx_pending)); - spin_unlock_irqrestore(&(pxmitpriv->vi_pending.lock), irqL0); - spin_lock_irqsave(&(pxmitpriv->bk_pending.lock), irqL0); - r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->bk_q.sta_pending); - list_del_init(&(pstaxmitpriv->bk_q.tx_pending)); - spin_unlock_irqrestore(&(pxmitpriv->bk_pending.lock), irqL0); - spin_lock_irqsave(&(pxmitpriv->be_pending.lock), irqL0); - r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->be_q.sta_pending); - list_del_init(&(pstaxmitpriv->be_q.tx_pending)); - spin_unlock_irqrestore(&(pxmitpriv->be_pending.lock), irqL0); - list_del_init(&psta->hash_list); - pstapriv->asoc_sta_count--; - /* re-init sta_info; 20061114 */ - _r8712_init_sta_xmit_priv(&psta->sta_xmitpriv); - _r8712_init_sta_recv_priv(&psta->sta_recvpriv); - /* for A-MPDU Rx reordering buffer control, - * cancel reordering_ctrl_timer - */ - for (i = 0; i < 16; i++) { - preorder_ctrl = &psta->recvreorder_ctrl[i]; - del_timer(&preorder_ctrl->reordering_ctrl_timer); - } - spin_lock(&(pfree_sta_queue->lock)); - /* insert into free_sta_queue; 20061114 */ - list_add_tail(&psta->list, &pfree_sta_queue->queue); - spin_unlock(&(pfree_sta_queue->lock)); -} - -/* free all stainfo which in sta_hash[all] */ -void r8712_free_all_stainfo(struct _adapter *padapter) -{ - unsigned long irqL; - struct list_head *plist, *phead; - s32 index; - struct sta_info *psta = NULL; - struct sta_priv *pstapriv = &padapter->stapriv; - struct sta_info *pbcmc_stainfo = r8712_get_bcmc_stainfo(padapter); - - if (pstapriv->asoc_sta_count == 1) - return; - spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); - for (index = 0; index < NUM_STA; index++) { - phead = &(pstapriv->sta_hash[index]); - plist = phead->next; - while (!end_of_queue_search(phead, plist)) { - psta = container_of(plist, - struct sta_info, hash_list); - plist = plist->next; - if (pbcmc_stainfo != psta) - r8712_free_stainfo(padapter, psta); - } - } - spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); -} - -/* any station allocated can be searched by hash list */ -struct sta_info *r8712_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) -{ - unsigned long irqL; - struct list_head *plist, *phead; - struct sta_info *psta = NULL; - u32 index; - - if (!hwaddr) - return NULL; - index = wifi_mac_hash(hwaddr); - spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); - phead = &(pstapriv->sta_hash[index]); - plist = phead->next; - while (!end_of_queue_search(phead, plist)) { - psta = container_of(plist, struct sta_info, hash_list); - if ((!memcmp(psta->hwaddr, hwaddr, ETH_ALEN))) { - /* if found the matched address */ - break; - } - psta = NULL; - plist = plist->next; - } - spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); - return psta; -} - -void r8712_init_bcmc_stainfo(struct _adapter *padapter) -{ - unsigned char bcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - struct sta_priv *pstapriv = &padapter->stapriv; - - r8712_alloc_stainfo(pstapriv, bcast_addr); -} - -struct sta_info *r8712_get_bcmc_stainfo(struct _adapter *padapter) -{ - struct sta_priv *pstapriv = &padapter->stapriv; - u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - - return r8712_get_stainfo(pstapriv, bc_addr); -} - -u8 r8712_access_ctrl(struct wlan_acl_pool *pacl_list, u8 *mac_addr) -{ - return true; -} diff --git a/drivers/staging/rtl8712/rtl871x_wlan_sme.h b/drivers/staging/rtl8712/rtl871x_wlan_sme.h deleted file mode 100644 index 97ea1451426c0..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_wlan_sme.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871X_WLAN_SME_H_ -#define _RTL871X_WLAN_SME_H_ - -#define MSR_APMODE 0x0C -#define MSR_STAMODE 0x08 -#define MSR_ADHOCMODE 0x04 -#define MSR_NOLINKMODE 0x00 -#define _1M_RATE_ 0 -#define _2M_RATE_ 1 -#define _5M_RATE_ 2 -#define _11M_RATE_ 3 -#define _6M_RATE_ 4 -#define _9M_RATE_ 5 -#define _12M_RATE_ 6 -#define _18M_RATE_ 7 -#define _24M_RATE_ 8 -#define _36M_RATE_ 9 -#define _48M_RATE_ 10 -#define _54M_RATE_ 11 - -#endif - diff --git a/drivers/staging/rtl8712/rtl871x_xmit.c b/drivers/staging/rtl8712/rtl871x_xmit.c deleted file mode 100644 index 408616e9afcff..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_xmit.c +++ /dev/null @@ -1,1056 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * rtl871x_xmit.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _RTL871X_XMIT_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "osdep_intf.h" -#include "usb_ops.h" - -#include -#include - -static const u8 P802_1H_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0xf8}; -static const u8 RFC1042_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0x00}; -static void init_hwxmits(struct hw_xmit *phwxmit, sint entry); -static void alloc_hwxmits(struct _adapter *padapter); -static void free_hwxmits(struct _adapter *padapter); - -static void _init_txservq(struct tx_servq *ptxservq) -{ - INIT_LIST_HEAD(&ptxservq->tx_pending); - _init_queue(&ptxservq->sta_pending); - ptxservq->qcnt = 0; -} - -void _r8712_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv) -{ - memset((unsigned char *)psta_xmitpriv, 0, - sizeof(struct sta_xmit_priv)); - spin_lock_init(&psta_xmitpriv->lock); - _init_txservq(&psta_xmitpriv->be_q); - _init_txservq(&psta_xmitpriv->bk_q); - _init_txservq(&psta_xmitpriv->vi_q); - _init_txservq(&psta_xmitpriv->vo_q); - INIT_LIST_HEAD(&psta_xmitpriv->legacy_dz); - INIT_LIST_HEAD(&psta_xmitpriv->apsd); -} - -int _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv, - struct _adapter *padapter) -{ - sint i; - struct xmit_buf *pxmitbuf; - struct xmit_frame *pxframe; - int j; - - memset((unsigned char *)pxmitpriv, 0, sizeof(struct xmit_priv)); - spin_lock_init(&pxmitpriv->lock); - /* - *Please insert all the queue initialization using _init_queue below - */ - pxmitpriv->adapter = padapter; - _init_queue(&pxmitpriv->be_pending); - _init_queue(&pxmitpriv->bk_pending); - _init_queue(&pxmitpriv->vi_pending); - _init_queue(&pxmitpriv->vo_pending); - _init_queue(&pxmitpriv->bm_pending); - _init_queue(&pxmitpriv->legacy_dz_queue); - _init_queue(&pxmitpriv->apsd_queue); - _init_queue(&pxmitpriv->free_xmit_queue); - /* - * Please allocate memory with sz = (struct xmit_frame) * NR_XMITFRAME, - * and initialize free_xmit_frame below. - * Please also apply free_txobj to link_up all the xmit_frames... - */ - pxmitpriv->pallocated_frame_buf = - kmalloc(NR_XMITFRAME * sizeof(struct xmit_frame) + 4, - GFP_ATOMIC); - if (!pxmitpriv->pallocated_frame_buf) { - pxmitpriv->pxmit_frame_buf = NULL; - return -ENOMEM; - } - pxmitpriv->pxmit_frame_buf = pxmitpriv->pallocated_frame_buf + 4 - - ((addr_t) (pxmitpriv->pallocated_frame_buf) & 3); - pxframe = (struct xmit_frame *) pxmitpriv->pxmit_frame_buf; - for (i = 0; i < NR_XMITFRAME; i++) { - INIT_LIST_HEAD(&(pxframe->list)); - pxframe->padapter = padapter; - pxframe->frame_tag = DATA_FRAMETAG; - pxframe->pkt = NULL; - pxframe->buf_addr = NULL; - pxframe->pxmitbuf = NULL; - list_add_tail(&(pxframe->list), - &(pxmitpriv->free_xmit_queue.queue)); - pxframe++; - } - pxmitpriv->free_xmitframe_cnt = NR_XMITFRAME; - /* - * init xmit hw_txqueue - */ - _r8712_init_hw_txqueue(&pxmitpriv->be_txqueue, BE_QUEUE_INX); - _r8712_init_hw_txqueue(&pxmitpriv->bk_txqueue, BK_QUEUE_INX); - _r8712_init_hw_txqueue(&pxmitpriv->vi_txqueue, VI_QUEUE_INX); - _r8712_init_hw_txqueue(&pxmitpriv->vo_txqueue, VO_QUEUE_INX); - _r8712_init_hw_txqueue(&pxmitpriv->bmc_txqueue, BMC_QUEUE_INX); - pxmitpriv->frag_len = MAX_FRAG_THRESHOLD; - pxmitpriv->txirp_cnt = 1; - /*per AC pending irp*/ - pxmitpriv->beq_cnt = 0; - pxmitpriv->bkq_cnt = 0; - pxmitpriv->viq_cnt = 0; - pxmitpriv->voq_cnt = 0; - /*init xmit_buf*/ - _init_queue(&pxmitpriv->free_xmitbuf_queue); - _init_queue(&pxmitpriv->pending_xmitbuf_queue); - pxmitpriv->pxmitbuf = kmalloc(NR_XMITBUFF * sizeof(struct xmit_buf), GFP_ATOMIC); - if (!pxmitpriv->pxmitbuf) - goto clean_up_frame_buf; - pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf; - for (i = 0; i < NR_XMITBUFF; i++) { - INIT_LIST_HEAD(&pxmitbuf->list); - pxmitbuf->pallocated_buf = - kmalloc(MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ, GFP_ATOMIC); - if (!pxmitbuf->pallocated_buf) { - j = 0; - goto clean_up_alloc_buf; - } - pxmitbuf->pbuf = pxmitbuf->pallocated_buf + XMITBUF_ALIGN_SZ - - ((addr_t) (pxmitbuf->pallocated_buf) & - (XMITBUF_ALIGN_SZ - 1)); - if (r8712_xmit_resource_alloc(padapter, pxmitbuf)) { - j = 1; - goto clean_up_alloc_buf; - } - list_add_tail(&pxmitbuf->list, - &(pxmitpriv->free_xmitbuf_queue.queue)); - pxmitbuf++; - } - pxmitpriv->free_xmitbuf_cnt = NR_XMITBUFF; - INIT_WORK(&padapter->wk_filter_rx_ff0, r8712_SetFilter); - alloc_hwxmits(padapter); - init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); - tasklet_setup(&pxmitpriv->xmit_tasklet, r8712_xmit_bh); - return 0; - -clean_up_alloc_buf: - if (j) { - /* failure happened in r8712_xmit_resource_alloc() - * delete extra pxmitbuf->pallocated_buf - */ - kfree(pxmitbuf->pallocated_buf); - } - for (j = 0; j < i; j++) { - int k; - - pxmitbuf--; /* reset pointer */ - kfree(pxmitbuf->pallocated_buf); - for (k = 0; k < 8; k++) /* delete xmit urb's */ - usb_free_urb(pxmitbuf->pxmit_urb[k]); - } - kfree(pxmitpriv->pxmitbuf); - pxmitpriv->pxmitbuf = NULL; -clean_up_frame_buf: - kfree(pxmitpriv->pallocated_frame_buf); - pxmitpriv->pallocated_frame_buf = NULL; - return -ENOMEM; -} - -void _free_xmit_priv(struct xmit_priv *pxmitpriv) -{ - int i; - struct _adapter *padapter = pxmitpriv->adapter; - struct xmit_frame *pxmitframe = (struct xmit_frame *) - pxmitpriv->pxmit_frame_buf; - struct xmit_buf *pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf; - - if (!pxmitpriv->pxmit_frame_buf) - return; - for (i = 0; i < NR_XMITFRAME; i++) { - r8712_xmit_complete(padapter, pxmitframe); - pxmitframe++; - } - for (i = 0; i < NR_XMITBUFF; i++) { - r8712_xmit_resource_free(padapter, pxmitbuf); - kfree(pxmitbuf->pallocated_buf); - pxmitbuf++; - } - kfree(pxmitpriv->pallocated_frame_buf); - kfree(pxmitpriv->pxmitbuf); - free_hwxmits(padapter); -} - -int r8712_update_attrib(struct _adapter *padapter, _pkt *pkt, - struct pkt_attrib *pattrib) -{ - struct pkt_file pktfile; - struct sta_info *psta = NULL; - struct ethhdr etherhdr; - - struct tx_cmd txdesc; - - bool bmcast; - struct sta_priv *pstapriv = &padapter->stapriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct qos_priv *pqospriv = &pmlmepriv->qospriv; - - _r8712_open_pktfile(pkt, &pktfile); - - _r8712_pktfile_read(&pktfile, (unsigned char *)ðerhdr, ETH_HLEN); - - pattrib->ether_type = ntohs(etherhdr.h_proto); - - /* - * If driver xmit ARP packet, driver can set ps mode to initial - * setting. It stands for getting DHCP or fix IP. - */ - if (pattrib->ether_type == 0x0806) { - if (padapter->pwrctrlpriv.pwr_mode != - padapter->registrypriv.power_mgnt) { - del_timer_sync(&pmlmepriv->dhcp_timer); - r8712_set_ps_mode(padapter, - padapter->registrypriv.power_mgnt, - padapter->registrypriv.smart_ps); - } - } - - memcpy(pattrib->dst, ðerhdr.h_dest, ETH_ALEN); - memcpy(pattrib->src, ðerhdr.h_source, ETH_ALEN); - pattrib->pctrl = 0; - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - memcpy(pattrib->ra, get_bssid(pmlmepriv), ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, get_bssid(pmlmepriv), ETH_ALEN); - } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - /*firstly, filter packet not belongs to mp*/ - if (pattrib->ether_type != 0x8712) - return -EINVAL; - /* for mp storing the txcmd per packet, - * according to the info of txcmd to update pattrib - */ - /*get MP_TXDESC_SIZE bytes txcmd per packet*/ - _r8712_pktfile_read(&pktfile, (u8 *)&txdesc, TXDESC_SIZE); - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - pattrib->pctrl = 1; - } - /* r8712_xmitframe_coalesce() overwrite this!*/ - pattrib->pktlen = pktfile.pkt_len; - if (pattrib->ether_type == ETH_P_IP) { - /* The following is for DHCP and ARP packet, we use cck1M to - * tx these packets and let LPS awake some time - * to prevent DHCP protocol fail - */ - u8 tmp[24]; - - _r8712_pktfile_read(&pktfile, &tmp[0], 24); - pattrib->dhcp_pkt = 0; - if (pktfile.pkt_len > 282) {/*MINIMUM_DHCP_PACKET_SIZE)*/ - if (pattrib->ether_type == ETH_P_IP) {/* IP header*/ - if (((tmp[21] == 68) && (tmp[23] == 67)) || - ((tmp[21] == 67) && (tmp[23] == 68))) { - /* 68 : UDP BOOTP client - * 67 : UDP BOOTP server - * Use low rate to send DHCP packet. - */ - pattrib->dhcp_pkt = 1; - } - } - } - } - bmcast = is_multicast_ether_addr(pattrib->ra); - /* get sta_info*/ - if (bmcast) { - psta = r8712_get_bcmc_stainfo(padapter); - pattrib->mac_id = 4; - } else { - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - psta = r8712_get_stainfo(pstapriv, - get_bssid(pmlmepriv)); - pattrib->mac_id = 5; - } else { - psta = r8712_get_stainfo(pstapriv, pattrib->ra); - if (!psta) /* drop the pkt */ - return -ENOMEM; - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) - pattrib->mac_id = 5; - else - pattrib->mac_id = psta->mac_id; - } - } - - if (psta) { - pattrib->psta = psta; - } else { - /* if we cannot get psta => drrp the pkt */ - return -ENOMEM; - } - - pattrib->ack_policy = 0; - /* get ether_hdr_len */ - pattrib->pkt_hdrlen = ETH_HLEN; - - if (pqospriv->qos_option) { - r8712_set_qos(&pktfile, pattrib); - } else { - pattrib->hdrlen = WLAN_HDR_A3_LEN; - pattrib->subtype = IEEE80211_FTYPE_DATA; - pattrib->priority = 0; - } - if (psta->ieee8021x_blocked) { - pattrib->encrypt = 0; - if ((pattrib->ether_type != 0x888e) && - !check_fwstate(pmlmepriv, WIFI_MP_STATE)) - return -EINVAL; - } else { - GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, bmcast); - } - switch (pattrib->encrypt) { - case _WEP40_: - case _WEP104_: - pattrib->iv_len = 4; - pattrib->icv_len = 4; - break; - case _TKIP_: - pattrib->iv_len = 8; - pattrib->icv_len = 4; - if (padapter->securitypriv.busetkipkey == _FAIL) - return -EINVAL; - break; - case _AES_: - pattrib->iv_len = 8; - pattrib->icv_len = 8; - break; - default: - pattrib->iv_len = 0; - pattrib->icv_len = 0; - break; - } - - if (pattrib->encrypt && - (padapter->securitypriv.sw_encrypt || - !psecuritypriv->hw_decrypted)) - pattrib->bswenc = true; - else - pattrib->bswenc = false; - /* if in MP_STATE, update pkt_attrib from mp_txcmd, and overwrite - * some settings above. - */ - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) - pattrib->priority = - (le32_to_cpu(txdesc.txdw1) >> QSEL_SHT) & 0x1f; - return 0; -} - -static int xmitframe_addmic(struct _adapter *padapter, - struct xmit_frame *pxmitframe) -{ - u32 curfragnum, length; - u8 *pframe, *payload, mic[8]; - struct mic_data micdata; - struct sta_info *stainfo; - struct qos_priv *pqospriv = &(padapter->mlmepriv.qospriv); - struct pkt_attrib *pattrib = &pxmitframe->attrib; - struct security_priv *psecpriv = &padapter->securitypriv; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - u8 priority[4] = {}; - bool bmcst = is_multicast_ether_addr(pattrib->ra); - - if (pattrib->psta) - stainfo = pattrib->psta; - else - stainfo = r8712_get_stainfo(&padapter->stapriv, - &pattrib->ra[0]); - if (pattrib->encrypt == _TKIP_) { - /*encode mic code*/ - if (stainfo) { - u8 null_key[16] = {}; - - pframe = pxmitframe->buf_addr + TXDESC_OFFSET; - if (bmcst) { - if (!memcmp(psecpriv->XGrptxmickey - [psecpriv->XGrpKeyid].skey, - null_key, 16)) - return -ENOMEM; - /*start to calculate the mic code*/ - r8712_secmicsetkey(&micdata, - psecpriv->XGrptxmickey - [psecpriv->XGrpKeyid].skey); - } else { - if (!memcmp(&stainfo->tkiptxmickey.skey[0], - null_key, 16)) - return -ENOMEM; - /* start to calculate the mic code */ - r8712_secmicsetkey(&micdata, - &stainfo->tkiptxmickey.skey[0]); - } - if (pframe[1] & 1) { /* ToDS==1 */ - r8712_secmicappend(&micdata, - &pframe[16], 6); /*DA*/ - if (pframe[1] & 2) /* From Ds==1 */ - r8712_secmicappend(&micdata, - &pframe[24], 6); - else - r8712_secmicappend(&micdata, - &pframe[10], 6); - } else { /* ToDS==0 */ - r8712_secmicappend(&micdata, - &pframe[4], 6); /* DA */ - if (pframe[1] & 2) /* From Ds==1 */ - r8712_secmicappend(&micdata, - &pframe[16], 6); - else - r8712_secmicappend(&micdata, - &pframe[10], 6); - } - if (pqospriv->qos_option == 1) - priority[0] = (u8)pxmitframe->attrib.priority; - r8712_secmicappend(&micdata, &priority[0], 4); - payload = pframe; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; - curfragnum++) { - payload = (u8 *)RND4((addr_t)(payload)); - payload += pattrib->hdrlen + pattrib->iv_len; - if ((curfragnum + 1) == pattrib->nr_frags) { - length = pattrib->last_txcmdsz - - pattrib->hdrlen - - pattrib->iv_len - - ((psecpriv->sw_encrypt) - ? pattrib->icv_len : 0); - r8712_secmicappend(&micdata, payload, - length); - payload = payload + length; - } else { - length = pxmitpriv->frag_len - - pattrib->hdrlen - pattrib->iv_len - - ((psecpriv->sw_encrypt) ? - pattrib->icv_len : 0); - r8712_secmicappend(&micdata, payload, - length); - payload = payload + length + - pattrib->icv_len; - } - } - r8712_secgetmic(&micdata, &(mic[0])); - /* add mic code and add the mic code length in - * last_txcmdsz - */ - memcpy(payload, &(mic[0]), 8); - pattrib->last_txcmdsz += 8; - payload = payload - pattrib->last_txcmdsz + 8; - } - } - return 0; -} - -static sint xmitframe_swencrypt(struct _adapter *padapter, - struct xmit_frame *pxmitframe) -{ - struct pkt_attrib *pattrib = &pxmitframe->attrib; - - if (pattrib->bswenc) { - switch (pattrib->encrypt) { - case _WEP40_: - case _WEP104_: - r8712_wep_encrypt(padapter, (u8 *)pxmitframe); - break; - case _TKIP_: - r8712_tkip_encrypt(padapter, (u8 *)pxmitframe); - break; - case _AES_: - r8712_aes_encrypt(padapter, (u8 *)pxmitframe); - break; - default: - break; - } - } - return _SUCCESS; -} - -static int make_wlanhdr(struct _adapter *padapter, u8 *hdr, - struct pkt_attrib *pattrib) -{ - u16 *qc; - - struct ieee80211_hdr *pwlanhdr = (struct ieee80211_hdr *)hdr; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct qos_priv *pqospriv = &pmlmepriv->qospriv; - __le16 *fctrl = &pwlanhdr->frame_control; - u8 *bssid; - - memset(hdr, 0, WLANHDR_OFFSET); - SetFrameSubType(fctrl, pattrib->subtype); - if (!(pattrib->subtype & IEEE80211_FTYPE_DATA)) - return 0; - - bssid = get_bssid(pmlmepriv); - - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - /* to_ds = 1, fr_ds = 0; */ - SetToDs(fctrl); - ether_addr_copy(pwlanhdr->addr1, bssid); - ether_addr_copy(pwlanhdr->addr2, pattrib->src); - ether_addr_copy(pwlanhdr->addr3, pattrib->dst); - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { - /* to_ds = 0, fr_ds = 1; */ - SetFrDs(fctrl); - ether_addr_copy(pwlanhdr->addr1, pattrib->dst); - ether_addr_copy(pwlanhdr->addr2, bssid); - ether_addr_copy(pwlanhdr->addr3, pattrib->src); - } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { - ether_addr_copy(pwlanhdr->addr1, pattrib->dst); - ether_addr_copy(pwlanhdr->addr2, pattrib->src); - ether_addr_copy(pwlanhdr->addr3, bssid); - } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - ether_addr_copy(pwlanhdr->addr1, pattrib->dst); - ether_addr_copy(pwlanhdr->addr2, pattrib->src); - ether_addr_copy(pwlanhdr->addr3, bssid); - } else { - return -EINVAL; - } - - if (pattrib->encrypt) - SetPrivacy(fctrl); - if (pqospriv->qos_option) { - qc = (unsigned short *)(hdr + pattrib->hdrlen - 2); - if (pattrib->priority) - SetPriority(qc, pattrib->priority); - SetAckpolicy(qc, pattrib->ack_policy); - } - /* TODO: fill HT Control Field */ - /* Update Seq Num will be handled by f/w */ - { - struct sta_info *psta; - bool bmcst = is_multicast_ether_addr(pattrib->ra); - - if (pattrib->psta) - psta = pattrib->psta; - else if (bmcst) - psta = r8712_get_bcmc_stainfo(padapter); - else - psta = r8712_get_stainfo(&padapter->stapriv, - pattrib->ra); - - if (psta) { - u16 *txtid = psta->sta_xmitpriv.txseq_tid; - - txtid[pattrib->priority]++; - txtid[pattrib->priority] &= 0xFFF; - pattrib->seqnum = txtid[pattrib->priority]; - SetSeqNum(hdr, pattrib->seqnum); - } - } - - return 0; -} - -static sint r8712_put_snap(u8 *data, u16 h_proto) -{ - struct ieee80211_snap_hdr *snap; - const u8 *oui; - - snap = (struct ieee80211_snap_hdr *)data; - snap->dsap = 0xaa; - snap->ssap = 0xaa; - snap->ctrl = 0x03; - if (h_proto == 0x8137 || h_proto == 0x80f3) - oui = P802_1H_OUI; - else - oui = RFC1042_OUI; - snap->oui[0] = oui[0]; - snap->oui[1] = oui[1]; - snap->oui[2] = oui[2]; - *(__be16 *)(data + SNAP_SIZE) = htons(h_proto); - return SNAP_SIZE + sizeof(u16); -} - -/* - * This sub-routine will perform all the following: - * 1. remove 802.3 header. - * 2. create wlan_header, based on the info in pxmitframe - * 3. append sta's iv/ext-iv - * 4. append LLC - * 5. move frag chunk from pframe to pxmitframe->mem - * 6. apply sw-encrypt, if necessary. - */ -sint r8712_xmitframe_coalesce(struct _adapter *padapter, _pkt *pkt, - struct xmit_frame *pxmitframe) -{ - struct pkt_file pktfile; - - sint frg_len, mpdu_len, llc_sz; - u32 mem_sz; - u8 frg_inx; - addr_t addr; - u8 *pframe, *mem_start, *ptxdesc; - struct sta_info *psta; - struct security_priv *psecpriv = &padapter->securitypriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - u8 *pbuf_start; - bool bmcst = is_multicast_ether_addr(pattrib->ra); - - if (!pattrib->psta) - return _FAIL; - psta = pattrib->psta; - if (!pxmitframe->buf_addr) - return _FAIL; - pbuf_start = pxmitframe->buf_addr; - ptxdesc = pbuf_start; - mem_start = pbuf_start + TXDESC_OFFSET; - if (make_wlanhdr(padapter, mem_start, pattrib)) - return _FAIL; - _r8712_open_pktfile(pkt, &pktfile); - _r8712_pktfile_read(&pktfile, NULL, (uint) pattrib->pkt_hdrlen); - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - /* truncate TXDESC_SIZE bytes txcmd if at mp mode for 871x */ - if (pattrib->ether_type == 0x8712) { - /* take care - update_txdesc overwrite this */ - _r8712_pktfile_read(&pktfile, ptxdesc, TXDESC_SIZE); - } - } - pattrib->pktlen = pktfile.pkt_len; - frg_inx = 0; - frg_len = pxmitpriv->frag_len - 4; - while (1) { - llc_sz = 0; - mpdu_len = frg_len; - pframe = mem_start; - SetMFrag(mem_start); - pframe += pattrib->hdrlen; - mpdu_len -= pattrib->hdrlen; - /* adding icv, if necessary...*/ - if (pattrib->iv_len) { - if (psta) { - switch (pattrib->encrypt) { - case _WEP40_: - case _WEP104_: - WEP_IV(pattrib->iv, psta->txpn, - (u8)psecpriv->PrivacyKeyIndex); - break; - case _TKIP_: - if (bmcst) - TKIP_IV(pattrib->iv, - psta->txpn, - (u8)psecpriv->XGrpKeyid); - else - TKIP_IV(pattrib->iv, psta->txpn, - 0); - break; - case _AES_: - if (bmcst) - AES_IV(pattrib->iv, psta->txpn, - (u8)psecpriv->XGrpKeyid); - else - AES_IV(pattrib->iv, psta->txpn, - 0); - break; - } - } - memcpy(pframe, pattrib->iv, pattrib->iv_len); - pframe += pattrib->iv_len; - mpdu_len -= pattrib->iv_len; - } - if (frg_inx == 0) { - llc_sz = r8712_put_snap(pframe, pattrib->ether_type); - pframe += llc_sz; - mpdu_len -= llc_sz; - } - if ((pattrib->icv_len > 0) && (pattrib->bswenc)) - mpdu_len -= pattrib->icv_len; - if (bmcst) - mem_sz = _r8712_pktfile_read(&pktfile, pframe, - pattrib->pktlen); - else - mem_sz = _r8712_pktfile_read(&pktfile, pframe, - mpdu_len); - pframe += mem_sz; - if ((pattrib->icv_len > 0) && (pattrib->bswenc)) { - memcpy(pframe, pattrib->icv, pattrib->icv_len); - pframe += pattrib->icv_len; - } - frg_inx++; - if (bmcst || r8712_endofpktfile(&pktfile)) { - pattrib->nr_frags = frg_inx; - pattrib->last_txcmdsz = pattrib->hdrlen + - pattrib->iv_len + - ((pattrib->nr_frags == 1) ? - llc_sz : 0) + - ((pattrib->bswenc) ? - pattrib->icv_len : 0) + mem_sz; - ClearMFrag(mem_start); - break; - } - addr = (addr_t)(pframe); - mem_start = (unsigned char *)RND4(addr) + TXDESC_OFFSET; - memcpy(mem_start, pbuf_start + TXDESC_OFFSET, pattrib->hdrlen); - } - - if (xmitframe_addmic(padapter, pxmitframe)) - return _FAIL; - xmitframe_swencrypt(padapter, pxmitframe); - return _SUCCESS; -} - -void r8712_update_protection(struct _adapter *padapter, u8 *ie, uint ie_len) -{ - uint protection; - u8 *perp; - uint erp_len; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct registry_priv *pregistrypriv = &padapter->registrypriv; - - switch (pxmitpriv->vcs_setting) { - case DISABLE_VCS: - pxmitpriv->vcs = NONE_VCS; - break; - case ENABLE_VCS: - break; - case AUTO_VCS: - default: - perp = r8712_get_ie(ie, WLAN_EID_ERP_INFO, &erp_len, ie_len); - if (!perp) { - pxmitpriv->vcs = NONE_VCS; - } else { - protection = (*(perp + 2)) & BIT(1); - if (protection) { - if (pregistrypriv->vcs_type == RTS_CTS) - pxmitpriv->vcs = RTS_CTS; - else - pxmitpriv->vcs = CTS_TO_SELF; - } else { - pxmitpriv->vcs = NONE_VCS; - } - } - break; - } -} - -struct xmit_buf *r8712_alloc_xmitbuf(struct xmit_priv *pxmitpriv) -{ - unsigned long irqL; - struct xmit_buf *pxmitbuf; - struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue; - - spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL); - pxmitbuf = list_first_entry_or_null(&pfree_xmitbuf_queue->queue, - struct xmit_buf, list); - if (pxmitbuf) { - list_del_init(&pxmitbuf->list); - pxmitpriv->free_xmitbuf_cnt--; - } - spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL); - return pxmitbuf; -} - -void r8712_free_xmitbuf(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf) -{ - unsigned long irqL; - struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue; - - if (!pxmitbuf) - return; - spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL); - list_del_init(&pxmitbuf->list); - list_add_tail(&(pxmitbuf->list), &pfree_xmitbuf_queue->queue); - pxmitpriv->free_xmitbuf_cnt++; - spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL); -} - -/* - * Calling context: - * 1. OS_TXENTRY - * 2. RXENTRY (rx_thread or RX_ISR/RX_CallBack) - * - * If we turn on USE_RXTHREAD, then, no need for critical section. - * Otherwise, we must use _enter/_exit critical to protect free_xmit_queue... - * - * Must be very very cautious... - * - */ -struct xmit_frame *r8712_alloc_xmitframe(struct xmit_priv *pxmitpriv) -{ - /* - * Please remember to use all the osdep_service api, - * and lock/unlock or _enter/_exit critical to protect - * pfree_xmit_queue - */ - unsigned long irqL; - struct xmit_frame *pxframe; - struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue; - - spin_lock_irqsave(&pfree_xmit_queue->lock, irqL); - pxframe = list_first_entry_or_null(&pfree_xmit_queue->queue, - struct xmit_frame, list); - if (pxframe) { - list_del_init(&pxframe->list); - pxmitpriv->free_xmitframe_cnt--; - pxframe->buf_addr = NULL; - pxframe->pxmitbuf = NULL; - pxframe->attrib.psta = NULL; - pxframe->pkt = NULL; - } - spin_unlock_irqrestore(&pfree_xmit_queue->lock, irqL); - return pxframe; -} - -void r8712_free_xmitframe(struct xmit_priv *pxmitpriv, - struct xmit_frame *pxmitframe) -{ - unsigned long irqL; - struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue; - struct _adapter *padapter = pxmitpriv->adapter; - - if (!pxmitframe) - return; - spin_lock_irqsave(&pfree_xmit_queue->lock, irqL); - list_del_init(&pxmitframe->list); - if (pxmitframe->pkt) - pxmitframe->pkt = NULL; - list_add_tail(&pxmitframe->list, &pfree_xmit_queue->queue); - pxmitpriv->free_xmitframe_cnt++; - spin_unlock_irqrestore(&pfree_xmit_queue->lock, irqL); - if (netif_queue_stopped(padapter->pnetdev)) - netif_wake_queue(padapter->pnetdev); -} - -void r8712_free_xmitframe_ex(struct xmit_priv *pxmitpriv, - struct xmit_frame *pxmitframe) -{ - if (!pxmitframe) - return; - if (pxmitframe->frame_tag == DATA_FRAMETAG) - r8712_free_xmitframe(pxmitpriv, pxmitframe); -} - -void r8712_free_xmitframe_queue(struct xmit_priv *pxmitpriv, - struct __queue *pframequeue) -{ - unsigned long irqL; - struct list_head *plist, *phead; - struct xmit_frame *pxmitframe; - - spin_lock_irqsave(&(pframequeue->lock), irqL); - phead = &pframequeue->queue; - plist = phead->next; - while (!end_of_queue_search(phead, plist)) { - pxmitframe = container_of(plist, struct xmit_frame, list); - plist = plist->next; - r8712_free_xmitframe(pxmitpriv, pxmitframe); - } - spin_unlock_irqrestore(&(pframequeue->lock), irqL); -} - -static inline struct tx_servq *get_sta_pending(struct _adapter *padapter, - struct __queue **ppstapending, - struct sta_info *psta, sint up) -{ - struct tx_servq *ptxservq; - struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits; - - switch (up) { - case 1: - case 2: - ptxservq = &(psta->sta_xmitpriv.bk_q); - *ppstapending = &padapter->xmitpriv.bk_pending; - (phwxmits + 3)->accnt++; - break; - case 4: - case 5: - ptxservq = &(psta->sta_xmitpriv.vi_q); - *ppstapending = &padapter->xmitpriv.vi_pending; - (phwxmits + 1)->accnt++; - break; - case 6: - case 7: - ptxservq = &(psta->sta_xmitpriv.vo_q); - *ppstapending = &padapter->xmitpriv.vo_pending; - (phwxmits + 0)->accnt++; - break; - case 0: - case 3: - default: - ptxservq = &(psta->sta_xmitpriv.be_q); - *ppstapending = &padapter->xmitpriv.be_pending; - (phwxmits + 2)->accnt++; - break; - } - return ptxservq; -} - -/* - * Will enqueue pxmitframe to the proper queue, and indicate it - * to xx_pending list..... - */ -int r8712_xmit_classifier(struct _adapter *padapter, - struct xmit_frame *pxmitframe) -{ - unsigned long irqL0; - struct __queue *pstapending; - struct sta_info *psta; - struct tx_servq *ptxservq; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - struct sta_priv *pstapriv = &padapter->stapriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - bool bmcst = is_multicast_ether_addr(pattrib->ra); - - if (pattrib->psta) { - psta = pattrib->psta; - } else { - if (bmcst) { - psta = r8712_get_bcmc_stainfo(padapter); - } else { - if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) - psta = r8712_get_stainfo(pstapriv, - get_bssid(pmlmepriv)); - else - psta = r8712_get_stainfo(pstapriv, pattrib->ra); - } - } - if (!psta) - return -EINVAL; - ptxservq = get_sta_pending(padapter, &pstapending, - psta, pattrib->priority); - spin_lock_irqsave(&pstapending->lock, irqL0); - if (list_empty(&ptxservq->tx_pending)) - list_add_tail(&ptxservq->tx_pending, &pstapending->queue); - list_add_tail(&pxmitframe->list, &ptxservq->sta_pending.queue); - ptxservq->qcnt++; - spin_unlock_irqrestore(&pstapending->lock, irqL0); - return 0; -} - -static void alloc_hwxmits(struct _adapter *padapter) -{ - struct hw_xmit *hwxmits; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - - pxmitpriv->hwxmit_entry = HWXMIT_ENTRY; - pxmitpriv->hwxmits = kmalloc_array(pxmitpriv->hwxmit_entry, - sizeof(struct hw_xmit), GFP_ATOMIC); - if (!pxmitpriv->hwxmits) - return; - hwxmits = pxmitpriv->hwxmits; - if (pxmitpriv->hwxmit_entry == 5) { - pxmitpriv->bmc_txqueue.head = 0; - hwxmits[0] .phwtxqueue = &pxmitpriv->bmc_txqueue; - hwxmits[0] .sta_queue = &pxmitpriv->bm_pending; - pxmitpriv->vo_txqueue.head = 0; - hwxmits[1] .phwtxqueue = &pxmitpriv->vo_txqueue; - hwxmits[1] .sta_queue = &pxmitpriv->vo_pending; - pxmitpriv->vi_txqueue.head = 0; - hwxmits[2] .phwtxqueue = &pxmitpriv->vi_txqueue; - hwxmits[2] .sta_queue = &pxmitpriv->vi_pending; - pxmitpriv->bk_txqueue.head = 0; - hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue; - hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; - pxmitpriv->be_txqueue.head = 0; - hwxmits[4] .phwtxqueue = &pxmitpriv->be_txqueue; - hwxmits[4] .sta_queue = &pxmitpriv->be_pending; - } else if (pxmitpriv->hwxmit_entry == 4) { - pxmitpriv->vo_txqueue.head = 0; - hwxmits[0] .phwtxqueue = &pxmitpriv->vo_txqueue; - hwxmits[0] .sta_queue = &pxmitpriv->vo_pending; - pxmitpriv->vi_txqueue.head = 0; - hwxmits[1] .phwtxqueue = &pxmitpriv->vi_txqueue; - hwxmits[1] .sta_queue = &pxmitpriv->vi_pending; - pxmitpriv->be_txqueue.head = 0; - hwxmits[2] .phwtxqueue = &pxmitpriv->be_txqueue; - hwxmits[2] .sta_queue = &pxmitpriv->be_pending; - pxmitpriv->bk_txqueue.head = 0; - hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue; - hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; - } -} - -static void free_hwxmits(struct _adapter *padapter) -{ - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - - kfree(pxmitpriv->hwxmits); -} - -static void init_hwxmits(struct hw_xmit *phwxmit, sint entry) -{ - sint i; - - for (i = 0; i < entry; i++, phwxmit++) { - spin_lock_init(&phwxmit->xmit_lock); - INIT_LIST_HEAD(&phwxmit->pending); - phwxmit->txcmdcnt = 0; - phwxmit->accnt = 0; - } -} - -void xmitframe_xmitbuf_attach(struct xmit_frame *pxmitframe, - struct xmit_buf *pxmitbuf) -{ - /* pxmitbuf attach to pxmitframe */ - pxmitframe->pxmitbuf = pxmitbuf; - /* urb and irp connection */ - pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0]; - /* buffer addr assoc */ - pxmitframe->buf_addr = pxmitbuf->pbuf; - /* pxmitframe attach to pxmitbuf */ - pxmitbuf->priv_data = pxmitframe; -} - -/* - * tx_action == 0 == no frames to transmit - * tx_action > 0 ==> we have frames to transmit - * tx_action < 0 ==> we have frames to transmit, but TXFF is not even enough - * to transmit 1 frame. - */ - -int r8712_pre_xmit(struct _adapter *padapter, struct xmit_frame *pxmitframe) -{ - unsigned long irqL; - int ret; - struct xmit_buf *pxmitbuf = NULL; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - - r8712_do_queue_select(padapter, pattrib); - spin_lock_irqsave(&pxmitpriv->lock, irqL); - if (r8712_txframes_sta_ac_pending(padapter, pattrib) > 0) { - ret = false; - r8712_xmit_enqueue(padapter, pxmitframe); - spin_unlock_irqrestore(&pxmitpriv->lock, irqL); - return ret; - } - pxmitbuf = r8712_alloc_xmitbuf(pxmitpriv); - if (!pxmitbuf) { /*enqueue packet*/ - ret = false; - r8712_xmit_enqueue(padapter, pxmitframe); - spin_unlock_irqrestore(&pxmitpriv->lock, irqL); - } else { /*dump packet directly*/ - spin_unlock_irqrestore(&pxmitpriv->lock, irqL); - ret = true; - xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf); - r8712_xmit_direct(padapter, pxmitframe); - } - return ret; -} diff --git a/drivers/staging/rtl8712/rtl871x_xmit.h b/drivers/staging/rtl8712/rtl871x_xmit.h deleted file mode 100644 index 784172c385e37..0000000000000 --- a/drivers/staging/rtl8712/rtl871x_xmit.h +++ /dev/null @@ -1,287 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _RTL871X_XMIT_H_ -#define _RTL871X_XMIT_H_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "xmit_osdep.h" - -#ifdef CONFIG_R8712_TX_AGGR -#define MAX_XMITBUF_SZ (16384) -#else -#define MAX_XMITBUF_SZ (2048) -#endif - -#define NR_XMITBUFF (4) - -#ifdef CONFIG_R8712_TX_AGGR -#define AGGR_NR_HIGH_BOUND (4) /*(8) */ -#define AGGR_NR_LOW_BOUND (2) -#endif - -#define XMITBUF_ALIGN_SZ 512 -#define TX_GUARD_BAND 5 -#define MAX_NUMBLKS (1) - -/* Fixed the Big Endian bug when using the software driver encryption.*/ -#define WEP_IV(pattrib_iv, txpn, keyidx)\ -do { \ - pattrib_iv[0] = txpn._byte_.TSC0;\ - pattrib_iv[1] = txpn._byte_.TSC1;\ - pattrib_iv[2] = txpn._byte_.TSC2;\ - pattrib_iv[3] = ((keyidx & 0x3) << 6);\ - txpn.val = (txpn.val == 0xffffff) ? 0 : (txpn.val + 1);\ -} while (0) - -/* Fixed the Big Endian bug when doing the Tx. - * The Linksys WRH54G will check this. - */ -#define TKIP_IV(pattrib_iv, txpn, keyidx)\ -do { \ - pattrib_iv[0] = txpn._byte_.TSC1;\ - pattrib_iv[1] = (txpn._byte_.TSC1 | 0x20) & 0x7f;\ - pattrib_iv[2] = txpn._byte_.TSC0;\ - pattrib_iv[3] = BIT(5) | ((keyidx & 0x3) << 6);\ - pattrib_iv[4] = txpn._byte_.TSC2;\ - pattrib_iv[5] = txpn._byte_.TSC3;\ - pattrib_iv[6] = txpn._byte_.TSC4;\ - pattrib_iv[7] = txpn._byte_.TSC5;\ - txpn.val = txpn.val == 0xffffffffffffULL ? 0 : \ - (txpn.val + 1);\ -} while (0) - -#define AES_IV(pattrib_iv, txpn, keyidx)\ -do { \ - pattrib_iv[0] = txpn._byte_.TSC0;\ - pattrib_iv[1] = txpn._byte_.TSC1;\ - pattrib_iv[2] = 0;\ - pattrib_iv[3] = BIT(5) | ((keyidx & 0x3) << 6);\ - pattrib_iv[4] = txpn._byte_.TSC2;\ - pattrib_iv[5] = txpn._byte_.TSC3;\ - pattrib_iv[6] = txpn._byte_.TSC4;\ - pattrib_iv[7] = txpn._byte_.TSC5;\ - txpn.val = txpn.val == 0xffffffffffffULL ? 0 : \ - (txpn.val + 1);\ -} while (0) - -struct hw_xmit { - spinlock_t xmit_lock; - struct list_head pending; - struct __queue *sta_queue; - struct hw_txqueue *phwtxqueue; - sint txcmdcnt; - int accnt; -}; - -struct pkt_attrib { - u8 type; - u8 subtype; - u8 bswenc; - u8 dhcp_pkt; - - u16 seqnum; - u16 ether_type; - u16 pktlen; /* the original 802.3 pkt raw_data len - * (not include ether_hdr data) - */ - u16 last_txcmdsz; - - u8 pkt_hdrlen; /*the original 802.3 pkt header len*/ - u8 hdrlen; /*the WLAN Header Len*/ - u8 nr_frags; - u8 ack_policy; - u8 mac_id; - u8 vcs_mode; /*virtual carrier sense method*/ - u8 pctrl;/*per packet txdesc control enable*/ - u8 qsel; - - u8 priority; - u8 encrypt; /* when 0 indicate no encrypt. when non-zero, - * indicate the encrypt algorithm - */ - u8 iv_len; - u8 icv_len; - unsigned char iv[8]; - unsigned char icv[8]; - u8 dst[ETH_ALEN] __aligned(2); /* for ether_addr_copy */ - u8 src[ETH_ALEN]; - u8 ta[ETH_ALEN]; - u8 ra[ETH_ALEN]; - struct sta_info *psta; -}; - -#define WLANHDR_OFFSET 64 -#define DATA_FRAMETAG 0x01 -#define L2_FRAMETAG 0x02 -#define MGNT_FRAMETAG 0x03 -#define AMSDU_FRAMETAG 0x04 -#define EII_FRAMETAG 0x05 -#define IEEE8023_FRAMETAG 0x06 -#define MP_FRAMETAG 0x07 -#define TXAGG_FRAMETAG 0x08 - -struct xmit_buf { - struct list_head list; - - u8 *pallocated_buf; - u8 *pbuf; - void *priv_data; - struct urb *pxmit_urb[8]; - u32 aggr_nr; -}; - -struct xmit_frame { - struct list_head list; - struct pkt_attrib attrib; - _pkt *pkt; - int frame_tag; - struct _adapter *padapter; - u8 *buf_addr; - struct xmit_buf *pxmitbuf; - u8 *mem_addr; - u16 sz[8]; - struct urb *pxmit_urb[8]; - u8 bpending[8]; - u8 last[8]; -}; - -struct tx_servq { - struct list_head tx_pending; - struct __queue sta_pending; - int qcnt; -}; - -struct sta_xmit_priv { - spinlock_t lock; - sint option; - sint apsd_setting; /* When bit mask is on, the associated edca - * queue supports APSD. - */ - struct tx_servq be_q; /* priority == 0,3 */ - struct tx_servq bk_q; /* priority == 1,2*/ - struct tx_servq vi_q; /*priority == 4,5*/ - struct tx_servq vo_q; /*priority == 6,7*/ - struct list_head legacy_dz; - struct list_head apsd; - u16 txseq_tid[16]; - uint sta_tx_bytes; - u64 sta_tx_pkts; - uint sta_tx_fail; -}; - -struct hw_txqueue { - sint head; - sint tail; - sint free_sz; /* in units of 64 bytes */ - sint free_cmdsz; - sint txsz[8]; - uint ff_hwaddr; - uint cmd_hwaddr; - sint ac_tag; -}; - -struct xmit_priv { - spinlock_t lock; - struct __queue be_pending; - struct __queue bk_pending; - struct __queue vi_pending; - struct __queue vo_pending; - struct __queue bm_pending; - struct __queue legacy_dz_queue; - struct __queue apsd_queue; - u8 *pallocated_frame_buf; - u8 *pxmit_frame_buf; - uint free_xmitframe_cnt; - uint mapping_addr; - uint pkt_sz; - struct __queue free_xmit_queue; - struct hw_txqueue be_txqueue; - struct hw_txqueue bk_txqueue; - struct hw_txqueue vi_txqueue; - struct hw_txqueue vo_txqueue; - struct hw_txqueue bmc_txqueue; - uint frag_len; - struct _adapter *adapter; - u8 vcs_setting; - u8 vcs; - u8 vcs_type; - u16 rts_thresh; - uint tx_bytes; - u64 tx_pkts; - uint tx_drop; - struct hw_xmit *hwxmits; - u8 hwxmit_entry; - u8 txirp_cnt; - struct tasklet_struct xmit_tasklet; - struct work_struct xmit_pipe4_reset_wi; - struct work_struct xmit_pipe6_reset_wi; - struct work_struct xmit_piped_reset_wi; - /*per AC pending irp*/ - int beq_cnt; - int bkq_cnt; - int viq_cnt; - int voq_cnt; - struct __queue free_amsdu_xmit_queue; - u8 *pallocated_amsdu_frame_buf; - u8 *pxmit_amsdu_frame_buf; - uint free_amsdu_xmitframe_cnt; - struct __queue free_txagg_xmit_queue; - u8 *pallocated_txagg_frame_buf; - u8 *pxmit_txagg_frame_buf; - uint free_txagg_xmitframe_cnt; - int cmdseq; - struct __queue free_xmitbuf_queue; - struct __queue pending_xmitbuf_queue; - u8 *pxmitbuf; - uint free_xmitbuf_cnt; -}; - -void r8712_free_xmitbuf(struct xmit_priv *pxmitpriv, - struct xmit_buf *pxmitbuf); -struct xmit_buf *r8712_alloc_xmitbuf(struct xmit_priv *pxmitpriv); -void r8712_update_protection(struct _adapter *padapter, u8 *ie, uint ie_len); -struct xmit_frame *r8712_alloc_xmitframe(struct xmit_priv *pxmitpriv); -void r8712_free_xmitframe(struct xmit_priv *pxmitpriv, - struct xmit_frame *pxmitframe); -void r8712_free_xmitframe_queue(struct xmit_priv *pxmitpriv, - struct __queue *pframequeue); -int r8712_xmit_classifier(struct _adapter *padapter, - struct xmit_frame *pxmitframe); -sint r8712_xmitframe_coalesce(struct _adapter *padapter, _pkt *pkt, - struct xmit_frame *pxmitframe); -sint _r8712_init_hw_txqueue(struct hw_txqueue *phw_txqueue, u8 ac_tag); -void _r8712_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv); -int r8712_update_attrib(struct _adapter *padapter, _pkt *pkt, - struct pkt_attrib *pattrib); -int r8712_txframes_sta_ac_pending(struct _adapter *padapter, - struct pkt_attrib *pattrib); -int _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv, - struct _adapter *padapter); -void _free_xmit_priv(struct xmit_priv *pxmitpriv); -void r8712_free_xmitframe_ex(struct xmit_priv *pxmitpriv, - struct xmit_frame *pxmitframe); -int r8712_pre_xmit(struct _adapter *padapter, struct xmit_frame *pxmitframe); -int r8712_xmit_enqueue(struct _adapter *padapter, - struct xmit_frame *pxmitframe); -void r8712_xmit_direct(struct _adapter *padapter, struct xmit_frame *pxmitframe); -void r8712_xmit_bh(struct tasklet_struct *t); - -void xmitframe_xmitbuf_attach(struct xmit_frame *pxmitframe, - struct xmit_buf *pxmitbuf); - -#include "rtl8712_xmit.h" - -#endif /*_RTL871X_XMIT_H_*/ - diff --git a/drivers/staging/rtl8712/sta_info.h b/drivers/staging/rtl8712/sta_info.h deleted file mode 100644 index 6286c622475e5..0000000000000 --- a/drivers/staging/rtl8712/sta_info.h +++ /dev/null @@ -1,132 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __STA_INFO_H_ -#define __STA_INFO_H_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "wifi.h" - -#define NUM_STA 32 -#define NUM_ACL 64 - -/* if mode ==0, then the sta is allowed once the addr is hit. - * if mode ==1, then the sta is rejected once the addr is non-hit. - */ -struct wlan_acl_node { - struct list_head list; - u8 addr[ETH_ALEN]; - u8 mode; -}; - -struct wlan_acl_pool { - struct wlan_acl_node aclnode[NUM_ACL]; -}; - -struct stainfo_stats { - uint rx_pkts; - uint rx_bytes; - u64 tx_pkts; - uint tx_bytes; -}; - -struct sta_info { - spinlock_t lock; - struct list_head list; /*free_sta_queue*/ - struct list_head hash_list; /*sta_hash*/ - struct sta_xmit_priv sta_xmitpriv; - struct sta_recv_priv sta_recvpriv; - uint state; - uint aid; - uint mac_id; - uint qos_option; - u8 hwaddr[ETH_ALEN]; - uint ieee8021x_blocked; /*0: allowed, 1:blocked */ - uint XPrivacy; /*aes, tkip...*/ - union Keytype tkiptxmickey; - union Keytype tkiprxmickey; - union Keytype x_UncstKey; - union pn48 txpn; /* PN48 used for Unicast xmit.*/ - union pn48 rxpn; /* PN48 used for Unicast recv.*/ - u8 bssrateset[16]; - uint bssratelen; - s32 rssi; - s32 signal_quality; - struct stainfo_stats sta_stats; - /*for A-MPDU Rx reordering buffer control */ - struct recv_reorder_ctrl recvreorder_ctrl[16]; - struct ht_priv htpriv; - /* Notes: - * STA_Mode: - * curr_network(mlme_priv/security_priv/qos/ht) - * + sta_info: (STA & AP) CAP/INFO - * scan_q: AP CAP/INFO - * AP_Mode: - * curr_network(mlme_priv/security_priv/qos/ht) : AP CAP/INFO - * sta_info: (AP & STA) CAP/INFO - */ - struct list_head asoc_list; - struct list_head auth_list; - unsigned int expire_to; - unsigned int auth_seq; - unsigned int authalg; - unsigned char chg_txt[128]; - unsigned int tx_ra_bitmap; -}; - -struct sta_priv { - u8 *pallocated_stainfo_buf; - u8 *pstainfo_buf; - struct __queue free_sta_queue; - spinlock_t sta_hash_lock; - struct list_head sta_hash[NUM_STA]; - int asoc_sta_count; - struct __queue sleep_q; - struct __queue wakeup_q; - struct _adapter *padapter; - struct list_head asoc_list; - struct list_head auth_list; - unsigned int auth_to; /* sec, time to expire in authenticating. */ - unsigned int assoc_to; /* sec, time to expire before associating. */ - unsigned int expire_to; /* sec , time to expire after associated. */ -}; - -static inline u32 wifi_mac_hash(u8 *mac) -{ - u32 x; - - x = mac[0]; - x = (x << 2) ^ mac[1]; - x = (x << 2) ^ mac[2]; - x = (x << 2) ^ mac[3]; - x = (x << 2) ^ mac[4]; - x = (x << 2) ^ mac[5]; - x ^= x >> 8; - x = x & (NUM_STA - 1); - return x; -} - -int _r8712_init_sta_priv(struct sta_priv *pstapriv); -void _r8712_free_sta_priv(struct sta_priv *pstapriv); -struct sta_info *r8712_alloc_stainfo(struct sta_priv *pstapriv, - u8 *hwaddr); -void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta); -void r8712_free_all_stainfo(struct _adapter *padapter); -struct sta_info *r8712_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr); -void r8712_init_bcmc_stainfo(struct _adapter *padapter); -struct sta_info *r8712_get_bcmc_stainfo(struct _adapter *padapter); -u8 r8712_access_ctrl(struct wlan_acl_pool *pacl_list, u8 *mac_addr); - -#endif /* _STA_INFO_H_ */ - diff --git a/drivers/staging/rtl8712/usb_halinit.c b/drivers/staging/rtl8712/usb_halinit.c deleted file mode 100644 index 313c569748e99..0000000000000 --- a/drivers/staging/rtl8712/usb_halinit.c +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * usb_halinit.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _HCI_HAL_INIT_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "usb_ops.h" -#include "usb_osintf.h" - -u8 r8712_usb_hal_bus_init(struct _adapter *adapter) -{ - u8 val8 = 0; - u8 ret = _SUCCESS; - int PollingCnt = 20; - struct registry_priv *registrypriv = &adapter->registrypriv; - - if (registrypriv->chip_version == RTL8712_FPGA) { - val8 = 0x01; - /* switch to 80M clock */ - r8712_write8(adapter, SYS_CLKR, val8); - val8 = r8712_read8(adapter, SPS1_CTRL); - val8 = val8 | 0x01; - /* enable VSPS12 LDO Macro block */ - r8712_write8(adapter, SPS1_CTRL, val8); - val8 = r8712_read8(adapter, AFE_MISC); - val8 = val8 | 0x01; - /* Enable AFE Macro Block's Bandgap */ - r8712_write8(adapter, AFE_MISC, val8); - val8 = r8712_read8(adapter, LDOA15_CTRL); - val8 = val8 | 0x01; - /* enable LDOA15 block */ - r8712_write8(adapter, LDOA15_CTRL, val8); - val8 = r8712_read8(adapter, SPS1_CTRL); - val8 = val8 | 0x02; - /* Enable VSPS12_SW Macro Block */ - r8712_write8(adapter, SPS1_CTRL, val8); - val8 = r8712_read8(adapter, AFE_MISC); - val8 = val8 | 0x02; - /* Enable AFE Macro Block's Mbias */ - r8712_write8(adapter, AFE_MISC, val8); - val8 = r8712_read8(adapter, SYS_ISO_CTRL + 1); - val8 = val8 | 0x08; - /* isolate PCIe Analog 1.2V to PCIe 3.3V and PCIE Digital */ - r8712_write8(adapter, SYS_ISO_CTRL + 1, val8); - val8 = r8712_read8(adapter, SYS_ISO_CTRL + 1); - val8 = val8 & 0xEF; - /* attach AFE PLL to MACTOP/BB/PCIe Digital */ - r8712_write8(adapter, SYS_ISO_CTRL + 1, val8); - val8 = r8712_read8(adapter, AFE_XTAL_CTRL + 1); - val8 = val8 & 0xFB; - /* enable AFE clock */ - r8712_write8(adapter, AFE_XTAL_CTRL + 1, val8); - val8 = r8712_read8(adapter, AFE_PLL_CTRL); - val8 = val8 | 0x01; - /* Enable AFE PLL Macro Block */ - r8712_write8(adapter, AFE_PLL_CTRL, val8); - val8 = 0xEE; - /* release isolation AFE PLL & MD */ - r8712_write8(adapter, SYS_ISO_CTRL, val8); - val8 = r8712_read8(adapter, SYS_CLKR + 1); - val8 = val8 | 0x08; - /* enable MAC clock */ - r8712_write8(adapter, SYS_CLKR + 1, val8); - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - val8 = val8 | 0x08; - /* enable Core digital and enable IOREG R/W */ - r8712_write8(adapter, SYS_FUNC_EN + 1, val8); - val8 = val8 | 0x80; - /* enable REG_EN */ - r8712_write8(adapter, SYS_FUNC_EN + 1, val8); - val8 = r8712_read8(adapter, SYS_CLKR + 1); - val8 = (val8 | 0x80) & 0xBF; - /* switch the control path */ - r8712_write8(adapter, SYS_CLKR + 1, val8); - val8 = 0xFC; - r8712_write8(adapter, CR, val8); - val8 = 0x37; - r8712_write8(adapter, CR + 1, val8); - /* reduce EndPoint & init it */ - r8712_write8(adapter, 0x102500ab, r8712_read8(adapter, - 0x102500ab) | BIT(6) | BIT(7)); - /* consideration of power consumption - init */ - r8712_write8(adapter, 0x10250008, r8712_read8(adapter, - 0x10250008) & 0xfffffffb); - } else if (registrypriv->chip_version == RTL8712_1stCUT) { - /* Initialization for power on sequence, */ - r8712_write8(adapter, SPS0_CTRL + 1, 0x53); - r8712_write8(adapter, SPS0_CTRL, 0x57); - /* Enable AFE Macro Block's Bandgap and Enable AFE Macro - * Block's Mbias - */ - val8 = r8712_read8(adapter, AFE_MISC); - r8712_write8(adapter, AFE_MISC, (val8 | AFE_MISC_BGEN | - AFE_MISC_MBEN)); - /* Enable LDOA15 block */ - val8 = r8712_read8(adapter, LDOA15_CTRL); - r8712_write8(adapter, LDOA15_CTRL, (val8 | LDA15_EN)); - val8 = r8712_read8(adapter, SPS1_CTRL); - r8712_write8(adapter, SPS1_CTRL, (val8 | SPS1_LDEN)); - msleep(20); - /* Enable Switch Regulator Block */ - val8 = r8712_read8(adapter, SPS1_CTRL); - r8712_write8(adapter, SPS1_CTRL, (val8 | SPS1_SWEN)); - r8712_write32(adapter, SPS1_CTRL, 0x00a7b267); - val8 = r8712_read8(adapter, SYS_ISO_CTRL + 1); - r8712_write8(adapter, SYS_ISO_CTRL + 1, (val8 | 0x08)); - /* Engineer Packet CP test Enable */ - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, (val8 | 0x20)); - val8 = r8712_read8(adapter, SYS_ISO_CTRL + 1); - r8712_write8(adapter, SYS_ISO_CTRL + 1, (val8 & 0x6F)); - /* Enable AFE clock */ - val8 = r8712_read8(adapter, AFE_XTAL_CTRL + 1); - r8712_write8(adapter, AFE_XTAL_CTRL + 1, (val8 & 0xfb)); - /* Enable AFE PLL Macro Block */ - val8 = r8712_read8(adapter, AFE_PLL_CTRL); - r8712_write8(adapter, AFE_PLL_CTRL, (val8 | 0x11)); - /* Attach AFE PLL to MACTOP/BB/PCIe Digital */ - val8 = r8712_read8(adapter, SYS_ISO_CTRL); - r8712_write8(adapter, SYS_ISO_CTRL, (val8 & 0xEE)); - /* Switch to 40M clock */ - val8 = r8712_read8(adapter, SYS_CLKR); - r8712_write8(adapter, SYS_CLKR, val8 & (~SYS_CLKSEL)); - /* SSC Disable */ - val8 = r8712_read8(adapter, SYS_CLKR); - /* Enable MAC clock */ - val8 = r8712_read8(adapter, SYS_CLKR + 1); - r8712_write8(adapter, SYS_CLKR + 1, (val8 | 0x18)); - /* Revised POS, */ - r8712_write8(adapter, PMC_FSM, 0x02); - /* Enable Core digital and enable IOREG R/W */ - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, (val8 | 0x08)); - /* Enable REG_EN */ - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, (val8 | 0x80)); - /* Switch the control path to FW */ - val8 = r8712_read8(adapter, SYS_CLKR + 1); - r8712_write8(adapter, SYS_CLKR + 1, (val8 | 0x80) & 0xBF); - r8712_write8(adapter, CR, 0xFC); - r8712_write8(adapter, CR + 1, 0x37); - /* Fix the RX FIFO issue(usb error), */ - val8 = r8712_read8(adapter, 0x1025FE5c); - r8712_write8(adapter, 0x1025FE5c, (val8 | BIT(7))); - val8 = r8712_read8(adapter, 0x102500ab); - r8712_write8(adapter, 0x102500ab, (val8 | BIT(6) | BIT(7))); - /* For power save, used this in the bit file after 970621 */ - val8 = r8712_read8(adapter, SYS_CLKR); - r8712_write8(adapter, SYS_CLKR, val8 & (~CPU_CLKSEL)); - } else if (registrypriv->chip_version == RTL8712_2ndCUT || - registrypriv->chip_version == RTL8712_3rdCUT) { - /* Initialization for power on sequence, - * E-Fuse leakage prevention sequence - */ - r8712_write8(adapter, 0x37, 0xb0); - msleep(20); - r8712_write8(adapter, 0x37, 0x30); - /* Set control path switch to HW control and reset Digital Core, - * CPU Core and MAC I/O to solve FW download fail when system - * from resume sate. - */ - val8 = r8712_read8(adapter, SYS_CLKR + 1); - if (val8 & 0x80) { - val8 &= 0x3f; - r8712_write8(adapter, SYS_CLKR + 1, val8); - } - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - val8 &= 0x73; - r8712_write8(adapter, SYS_FUNC_EN + 1, val8); - msleep(20); - /* Revised POS, */ - /* Enable AFE Macro Block's Bandgap and Enable AFE Macro - * Block's Mbias - */ - r8712_write8(adapter, SPS0_CTRL + 1, 0x53); - r8712_write8(adapter, SPS0_CTRL, 0x57); - val8 = r8712_read8(adapter, AFE_MISC); - /*Bandgap*/ - r8712_write8(adapter, AFE_MISC, (val8 | AFE_MISC_BGEN)); - r8712_write8(adapter, AFE_MISC, (val8 | AFE_MISC_BGEN | - AFE_MISC_MBEN | AFE_MISC_I32_EN)); - /* Enable PLL Power (LDOA15V) */ - val8 = r8712_read8(adapter, LDOA15_CTRL); - r8712_write8(adapter, LDOA15_CTRL, (val8 | LDA15_EN)); - /* Enable LDOV12D block */ - val8 = r8712_read8(adapter, LDOV12D_CTRL); - r8712_write8(adapter, LDOV12D_CTRL, (val8 | LDV12_EN)); - val8 = r8712_read8(adapter, SYS_ISO_CTRL + 1); - r8712_write8(adapter, SYS_ISO_CTRL + 1, (val8 | 0x08)); - /* Engineer Packet CP test Enable */ - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, (val8 | 0x20)); - /* Support 64k IMEM */ - val8 = r8712_read8(adapter, SYS_ISO_CTRL + 1); - r8712_write8(adapter, SYS_ISO_CTRL + 1, (val8 & 0x68)); - /* Enable AFE clock */ - val8 = r8712_read8(adapter, AFE_XTAL_CTRL + 1); - r8712_write8(adapter, AFE_XTAL_CTRL + 1, (val8 & 0xfb)); - /* Enable AFE PLL Macro Block */ - val8 = r8712_read8(adapter, AFE_PLL_CTRL); - r8712_write8(adapter, AFE_PLL_CTRL, (val8 | 0x11)); - /* Some sample will download fw failure. The clock will be - * stable with 500 us delay after reset the PLL - * TODO: When usleep is added to kernel, change next 3 - * udelay(500) to usleep(500) - */ - udelay(500); - r8712_write8(adapter, AFE_PLL_CTRL, (val8 | 0x51)); - udelay(500); - r8712_write8(adapter, AFE_PLL_CTRL, (val8 | 0x11)); - udelay(500); - /* Attach AFE PLL to MACTOP/BB/PCIe Digital */ - val8 = r8712_read8(adapter, SYS_ISO_CTRL); - r8712_write8(adapter, SYS_ISO_CTRL, (val8 & 0xEE)); - /* Switch to 40M clock */ - r8712_write8(adapter, SYS_CLKR, 0x00); - /* CPU Clock and 80M Clock SSC Disable to overcome FW download - * fail timing issue. - */ - val8 = r8712_read8(adapter, SYS_CLKR); - r8712_write8(adapter, SYS_CLKR, (val8 | 0xa0)); - /* Enable MAC clock */ - val8 = r8712_read8(adapter, SYS_CLKR + 1); - r8712_write8(adapter, SYS_CLKR + 1, (val8 | 0x18)); - /* Revised POS, */ - r8712_write8(adapter, PMC_FSM, 0x02); - /* Enable Core digital and enable IOREG R/W */ - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, (val8 | 0x08)); - /* Enable REG_EN */ - val8 = r8712_read8(adapter, SYS_FUNC_EN + 1); - r8712_write8(adapter, SYS_FUNC_EN + 1, (val8 | 0x80)); - /* Switch the control path to FW */ - val8 = r8712_read8(adapter, SYS_CLKR + 1); - r8712_write8(adapter, SYS_CLKR + 1, (val8 | 0x80) & 0xBF); - r8712_write8(adapter, CR, 0xFC); - r8712_write8(adapter, CR + 1, 0x37); - /* Fix the RX FIFO issue(usb error), 970410 */ - val8 = r8712_read8(adapter, 0x1025FE5c); - r8712_write8(adapter, 0x1025FE5c, (val8 | BIT(7))); - /* For power save, used this in the bit file after 970621 */ - val8 = r8712_read8(adapter, SYS_CLKR); - r8712_write8(adapter, SYS_CLKR, val8 & (~CPU_CLKSEL)); - /* Revised for 8051 ROM code wrong operation. */ - r8712_write8(adapter, 0x1025fe1c, 0x80); - /* To make sure that TxDMA can ready to download FW. - * We should reset TxDMA if IMEM RPT was not ready. - */ - do { - val8 = r8712_read8(adapter, TCR); - if ((val8 & _TXDMA_INIT_VALUE) == _TXDMA_INIT_VALUE) - break; - udelay(5); /* PlatformStallExecution(5); */ - } while (PollingCnt--); /* Delay 1ms */ - - if (PollingCnt <= 0) { - val8 = r8712_read8(adapter, CR); - r8712_write8(adapter, CR, val8 & (~_TXDMA_EN)); - udelay(2); /* PlatformStallExecution(2); */ - /* Reset TxDMA */ - r8712_write8(adapter, CR, val8 | _TXDMA_EN); - } - } else { - ret = _FAIL; - } - return ret; -} - -unsigned int r8712_usb_inirp_init(struct _adapter *adapter) -{ - u8 i; - struct recv_buf *recvbuf; - struct intf_hdl *intfhdl = &adapter->pio_queue->intf; - struct recv_priv *recvpriv = &(adapter->recvpriv); - - recvpriv->ff_hwaddr = RTL8712_DMA_RX0FF; /* mapping rx fifo address */ - /* issue Rx irp to receive data */ - recvbuf = (struct recv_buf *)recvpriv->precv_buf; - for (i = 0; i < NR_RECVBUFF; i++) { - if (r8712_usb_read_port(intfhdl, recvpriv->ff_hwaddr, 0, - (unsigned char *)recvbuf) == false) - return _FAIL; - recvbuf++; - recvpriv->free_recv_buf_queue_cnt--; - } - return _SUCCESS; -} - -unsigned int r8712_usb_inirp_deinit(struct _adapter *adapter) -{ - r8712_usb_read_port_cancel(adapter); - return _SUCCESS; -} diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c deleted file mode 100644 index df05213f922f4..0000000000000 --- a/drivers/staging/rtl8712/usb_intf.c +++ /dev/null @@ -1,638 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * usb_intf.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _HCI_INTF_C_ - -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "recv_osdep.h" -#include "xmit_osdep.h" -#include "rtl8712_efuse.h" -#include "usb_ops.h" -#include "usb_osintf.h" - -static struct usb_interface *pintf; - -static int r871xu_drv_init(struct usb_interface *pusb_intf, - const struct usb_device_id *pdid); - -static void r871xu_dev_remove(struct usb_interface *pusb_intf); - -static const struct usb_device_id rtl871x_usb_id_tbl[] = { -/* RTL8188SU */ - /* Realtek */ - {USB_DEVICE(0x0BDA, 0x8171)}, - {USB_DEVICE(0x0bda, 0x8173)}, - {USB_DEVICE(0x0bda, 0x8712)}, - {USB_DEVICE(0x0bda, 0x8713)}, - {USB_DEVICE(0x0bda, 0xC512)}, - /* Abocom */ - {USB_DEVICE(0x07B8, 0x8188)}, - /* ASUS */ - {USB_DEVICE(0x0B05, 0x1786)}, - {USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */ - /* Belkin */ - {USB_DEVICE(0x050D, 0x945A)}, - /* ISY IWL - Belkin clone */ - {USB_DEVICE(0x050D, 0x11F1)}, - /* Corega */ - {USB_DEVICE(0x07AA, 0x0047)}, - /* D-Link */ - {USB_DEVICE(0x2001, 0x3306)}, - {USB_DEVICE(0x07D1, 0x3306)}, /* 11n mode disable */ - /* Edimax */ - {USB_DEVICE(0x7392, 0x7611)}, - /* EnGenius */ - {USB_DEVICE(0x1740, 0x9603)}, - /* Hawking */ - {USB_DEVICE(0x0E66, 0x0016)}, - /* Hercules */ - {USB_DEVICE(0x06F8, 0xE034)}, - {USB_DEVICE(0x06F8, 0xE032)}, - /* Logitec */ - {USB_DEVICE(0x0789, 0x0167)}, - /* PCI */ - {USB_DEVICE(0x2019, 0xAB28)}, - {USB_DEVICE(0x2019, 0xED16)}, - /* Sitecom */ - {USB_DEVICE(0x0DF6, 0x0057)}, - {USB_DEVICE(0x0DF6, 0x0045)}, - {USB_DEVICE(0x0DF6, 0x0059)}, /* 11n mode disable */ - {USB_DEVICE(0x0DF6, 0x004B)}, - {USB_DEVICE(0x0DF6, 0x005B)}, - {USB_DEVICE(0x0DF6, 0x005D)}, - {USB_DEVICE(0x0DF6, 0x0063)}, - /* Sweex */ - {USB_DEVICE(0x177F, 0x0154)}, - /* Thinkware */ - {USB_DEVICE(0x0BDA, 0x5077)}, - /* Toshiba */ - {USB_DEVICE(0x1690, 0x0752)}, - /* - */ - {USB_DEVICE(0x20F4, 0x646B)}, - {USB_DEVICE(0x083A, 0xC512)}, - {USB_DEVICE(0x25D4, 0x4CA1)}, - {USB_DEVICE(0x25D4, 0x4CAB)}, - -/* RTL8191SU */ - /* Realtek */ - {USB_DEVICE(0x0BDA, 0x8172)}, - {USB_DEVICE(0x0BDA, 0x8192)}, - /* Amigo */ - {USB_DEVICE(0x0EB0, 0x9061)}, - /* ASUS/EKB */ - {USB_DEVICE(0x13D3, 0x3323)}, - {USB_DEVICE(0x13D3, 0x3311)}, /* 11n mode disable */ - {USB_DEVICE(0x13D3, 0x3342)}, - /* ASUS/EKBLenovo */ - {USB_DEVICE(0x13D3, 0x3333)}, - {USB_DEVICE(0x13D3, 0x3334)}, - {USB_DEVICE(0x13D3, 0x3335)}, /* 11n mode disable */ - {USB_DEVICE(0x13D3, 0x3336)}, /* 11n mode disable */ - /* ASUS/Media BOX */ - {USB_DEVICE(0x13D3, 0x3309)}, - /* Belkin */ - {USB_DEVICE(0x050D, 0x815F)}, - /* D-Link */ - {USB_DEVICE(0x07D1, 0x3302)}, - {USB_DEVICE(0x07D1, 0x3300)}, - {USB_DEVICE(0x07D1, 0x3303)}, - /* Edimax */ - {USB_DEVICE(0x7392, 0x7612)}, - /* EnGenius */ - {USB_DEVICE(0x1740, 0x9605)}, - /* Guillemot */ - {USB_DEVICE(0x06F8, 0xE031)}, - /* Hawking */ - {USB_DEVICE(0x0E66, 0x0015)}, - /* Mediao */ - {USB_DEVICE(0x13D3, 0x3306)}, - /* PCI */ - {USB_DEVICE(0x2019, 0xED18)}, - {USB_DEVICE(0x2019, 0x4901)}, - /* Sitecom */ - {USB_DEVICE(0x0DF6, 0x0058)}, - {USB_DEVICE(0x0DF6, 0x0049)}, - {USB_DEVICE(0x0DF6, 0x004C)}, - {USB_DEVICE(0x0DF6, 0x006C)}, - {USB_DEVICE(0x0DF6, 0x0064)}, - /* Skyworth */ - {USB_DEVICE(0x14b2, 0x3300)}, - {USB_DEVICE(0x14b2, 0x3301)}, - {USB_DEVICE(0x14B2, 0x3302)}, - /* - */ - {USB_DEVICE(0x04F2, 0xAFF2)}, - {USB_DEVICE(0x04F2, 0xAFF5)}, - {USB_DEVICE(0x04F2, 0xAFF6)}, - {USB_DEVICE(0x13D3, 0x3339)}, - {USB_DEVICE(0x13D3, 0x3340)}, /* 11n mode disable */ - {USB_DEVICE(0x13D3, 0x3341)}, /* 11n mode disable */ - {USB_DEVICE(0x13D3, 0x3310)}, - {USB_DEVICE(0x13D3, 0x3325)}, - -/* RTL8192SU */ - /* Realtek */ - {USB_DEVICE(0x0BDA, 0x8174)}, - /* Belkin */ - {USB_DEVICE(0x050D, 0x845A)}, - /* Corega */ - {USB_DEVICE(0x07AA, 0x0051)}, - /* Edimax */ - {USB_DEVICE(0x7392, 0x7622)}, - /* NEC */ - {USB_DEVICE(0x0409, 0x02B6)}, - {} -}; - -MODULE_DEVICE_TABLE(usb, rtl871x_usb_id_tbl); - -static struct specific_device_id specific_device_id_tbl[] = { - {.idVendor = 0x0b05, .idProduct = 0x1791, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x0df6, .idProduct = 0x0059, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x13d3, .idProduct = 0x3306, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x13D3, .idProduct = 0x3311, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x13d3, .idProduct = 0x3335, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x13d3, .idProduct = 0x3336, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x13d3, .idProduct = 0x3340, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {.idVendor = 0x13d3, .idProduct = 0x3341, - .flags = SPEC_DEV_ID_DISABLE_HT}, - {} -}; - -struct drv_priv { - struct usb_driver r871xu_drv; - int drv_registered; -}; - -#ifdef CONFIG_PM -static int r871x_suspend(struct usb_interface *pusb_intf, pm_message_t state) -{ - struct net_device *pnetdev = usb_get_intfdata(pusb_intf); - struct _adapter *padapter = netdev_priv(pnetdev); - - netdev_info(pnetdev, "Suspending...\n"); - padapter->suspended = true; - rtl871x_intf_stop(padapter); - if (pnetdev->netdev_ops->ndo_stop) - pnetdev->netdev_ops->ndo_stop(pnetdev); - mdelay(10); - netif_device_detach(pnetdev); - return 0; -} - -static void rtl871x_intf_resume(struct _adapter *padapter) -{ - if (padapter->dvobjpriv.inirp_init) - padapter->dvobjpriv.inirp_init(padapter); -} - -static int r871x_resume(struct usb_interface *pusb_intf) -{ - struct net_device *pnetdev = usb_get_intfdata(pusb_intf); - struct _adapter *padapter = netdev_priv(pnetdev); - - netdev_info(pnetdev, "Resuming...\n"); - netif_device_attach(pnetdev); - if (pnetdev->netdev_ops->ndo_open) - pnetdev->netdev_ops->ndo_open(pnetdev); - padapter->suspended = false; - rtl871x_intf_resume(padapter); - return 0; -} -#endif - -static struct drv_priv drvpriv = { - .r871xu_drv.name = "r8712u", - .r871xu_drv.id_table = rtl871x_usb_id_tbl, - .r871xu_drv.probe = r871xu_drv_init, - .r871xu_drv.disconnect = r871xu_dev_remove, -#ifdef CONFIG_PM - .r871xu_drv.suspend = r871x_suspend, - .r871xu_drv.resume = r871x_resume, -#endif -}; - -static uint r8712_usb_dvobj_init(struct _adapter *padapter) -{ - uint status = _SUCCESS; - struct usb_host_interface *phost_iface; - struct usb_interface_descriptor *piface_desc; - struct dvobj_priv *pdvobjpriv = &padapter->dvobjpriv; - struct usb_device *pusbd = pdvobjpriv->pusbdev; - - pdvobjpriv->padapter = padapter; - padapter->eeprom_address_size = 6; - phost_iface = pintf->cur_altsetting; - piface_desc = &phost_iface->desc; - pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints; - if (pusbd->speed == USB_SPEED_HIGH) { - pdvobjpriv->ishighspeed = true; - dev_info(&pusbd->dev, "r8712u: USB_SPEED_HIGH with %d endpoints\n", - pdvobjpriv->nr_endpoint); - } else { - pdvobjpriv->ishighspeed = false; - dev_info(&pusbd->dev, "r8712u: USB_SPEED_LOW with %d endpoints\n", - pdvobjpriv->nr_endpoint); - } - if ((r8712_alloc_io_queue(padapter)) == _FAIL) - status = _FAIL; - return status; -} - -static void r8712_usb_dvobj_deinit(struct _adapter *padapter) -{ - r8712_free_io_queue(padapter); -} - -void rtl871x_intf_stop(struct _adapter *padapter) -{ - /*disable_hw_interrupt*/ - if (!padapter->surprise_removed) { - /*device still exists, so driver can do i/o operation - * TODO: - */ - } - - /* cancel in irp */ - if (padapter->dvobjpriv.inirp_deinit) - padapter->dvobjpriv.inirp_deinit(padapter); - /* cancel out irp */ - r8712_usb_write_port_cancel(padapter); - /* TODO:cancel other irps */ -} - -void r871x_dev_unload(struct _adapter *padapter) -{ - if (padapter->bup) { - /*s1.*/ - padapter->driver_stopped = true; - - /*s3.*/ - rtl871x_intf_stop(padapter); - - /*s4.*/ - r8712_stop_drv_threads(padapter); - - /*s5.*/ - if (!padapter->surprise_removed) { - padapter->hw_init_completed = false; - rtl8712_hal_deinit(padapter); - } - - padapter->bup = false; - } -} - -static void disable_ht_for_spec_devid(const struct usb_device_id *pdid, - struct _adapter *padapter) -{ - u16 vid, pid; - u32 flags; - int i; - int num = ARRAY_SIZE(specific_device_id_tbl); - - for (i = 0; i < num; i++) { - vid = specific_device_id_tbl[i].idVendor; - pid = specific_device_id_tbl[i].idProduct; - flags = specific_device_id_tbl[i].flags; - - if ((pdid->idVendor == vid) && (pdid->idProduct == pid) && - (flags & SPEC_DEV_ID_DISABLE_HT)) { - padapter->registrypriv.ht_enable = 0; - padapter->registrypriv.cbw40_enable = 0; - padapter->registrypriv.ampdu_enable = 0; - } - } -} - -static const struct device_type wlan_type = { - .name = "wlan", -}; - -/* - * drv_init() - a device potentially for us - * - * notes: drv_init() is called when the bus driver has located a card for us - * to support. We accept the new device by returning 0. - */ -static int r871xu_drv_init(struct usb_interface *pusb_intf, - const struct usb_device_id *pdid) -{ - uint status; - struct _adapter *padapter = NULL; - struct dvobj_priv *pdvobjpriv; - struct net_device *pnetdev; - struct usb_device *udev; - - /* In this probe function, O.S. will provide the usb interface pointer - * to driver. We have to increase the reference count of the usb device - * structure by using the usb_get_dev function. - */ - udev = interface_to_usbdev(pusb_intf); - usb_get_dev(udev); - pintf = pusb_intf; - /* step 1. */ - pnetdev = r8712_init_netdev(); - if (!pnetdev) - goto put_dev; - padapter = netdev_priv(pnetdev); - disable_ht_for_spec_devid(pdid, padapter); - pdvobjpriv = &padapter->dvobjpriv; - pdvobjpriv->padapter = padapter; - padapter->dvobjpriv.pusbdev = udev; - padapter->pusb_intf = pusb_intf; - usb_set_intfdata(pusb_intf, pnetdev); - SET_NETDEV_DEV(pnetdev, &pusb_intf->dev); - pnetdev->dev.type = &wlan_type; - /* step 2. */ - padapter->dvobj_init = r8712_usb_dvobj_init; - padapter->dvobj_deinit = r8712_usb_dvobj_deinit; - padapter->halpriv.hal_bus_init = r8712_usb_hal_bus_init; - padapter->dvobjpriv.inirp_init = r8712_usb_inirp_init; - padapter->dvobjpriv.inirp_deinit = r8712_usb_inirp_deinit; - /* step 3. - * initialize the dvobj_priv - */ - - status = padapter->dvobj_init(padapter); - if (status != _SUCCESS) - goto free_netdev; - - /* step 4. */ - status = r8712_init_drv_sw(padapter); - if (status) - goto dvobj_deinit; - /* step 5. read efuse/eeprom data and get mac_addr */ - { - int i, offset; - u8 mac[6]; - u8 tmpU1b, AutoloadFail, eeprom_CustomerID; - u8 *pdata = padapter->eeprompriv.efuse_eeprom_data; - - tmpU1b = r8712_read8(padapter, EE_9346CR);/*CR9346*/ - - /* To check system boot selection.*/ - dev_info(&udev->dev, "r8712u: Boot from %s: Autoload %s\n", - (tmpU1b & _9356SEL) ? "EEPROM" : "EFUSE", - (tmpU1b & _EEPROM_EN) ? "OK" : "Failed"); - - /* To check autoload success or not.*/ - if (tmpU1b & _EEPROM_EN) { - AutoloadFail = true; - /* The following operations prevent Efuse leakage by - * turning on 2.5V. - */ - tmpU1b = r8712_read8(padapter, EFUSE_TEST + 3); - r8712_write8(padapter, EFUSE_TEST + 3, tmpU1b | 0x80); - msleep(20); - r8712_write8(padapter, EFUSE_TEST + 3, - (tmpU1b & (~BIT(7)))); - - /* Retrieve Chip version. - * Recognize IC version by Reg0x4 BIT15. - */ - tmpU1b = (u8)((r8712_read32(padapter, PMC_FSM) >> 15) & - 0x1F); - if (tmpU1b == 0x3) - padapter->registrypriv.chip_version = - RTL8712_3rdCUT; - else - padapter->registrypriv.chip_version = - (tmpU1b >> 1) + 1; - switch (padapter->registrypriv.chip_version) { - case RTL8712_1stCUT: - case RTL8712_2ndCUT: - case RTL8712_3rdCUT: - break; - default: - padapter->registrypriv.chip_version = - RTL8712_2ndCUT; - break; - } - - for (i = 0, offset = 0; i < 128; i += 8, offset++) - r8712_efuse_pg_packet_read(padapter, offset, - &pdata[i]); - - if (!r8712_initmac || !mac_pton(r8712_initmac, mac)) { - /* Use the mac address stored in the Efuse - * offset = 0x12 for usb in efuse - */ - ether_addr_copy(mac, &pdata[0x12]); - } - eeprom_CustomerID = pdata[0x52]; - switch (eeprom_CustomerID) { - case EEPROM_CID_ALPHA: - padapter->eeprompriv.CustomerID = - RT_CID_819x_ALPHA; - break; - case EEPROM_CID_CAMEO: - padapter->eeprompriv.CustomerID = - RT_CID_819x_CAMEO; - break; - case EEPROM_CID_SITECOM: - padapter->eeprompriv.CustomerID = - RT_CID_819x_Sitecom; - break; - case EEPROM_CID_COREGA: - padapter->eeprompriv.CustomerID = - RT_CID_COREGA; - break; - case EEPROM_CID_Senao: - padapter->eeprompriv.CustomerID = - RT_CID_819x_Senao; - break; - case EEPROM_CID_EDIMAX_BELKIN: - padapter->eeprompriv.CustomerID = - RT_CID_819x_Edimax_Belkin; - break; - case EEPROM_CID_SERCOMM_BELKIN: - padapter->eeprompriv.CustomerID = - RT_CID_819x_Sercomm_Belkin; - break; - case EEPROM_CID_WNC_COREGA: - padapter->eeprompriv.CustomerID = - RT_CID_819x_WNC_COREGA; - break; - case EEPROM_CID_WHQL: - break; - case EEPROM_CID_NetCore: - padapter->eeprompriv.CustomerID = - RT_CID_819x_Netcore; - break; - case EEPROM_CID_CAMEO1: - padapter->eeprompriv.CustomerID = - RT_CID_819x_CAMEO1; - break; - case EEPROM_CID_CLEVO: - padapter->eeprompriv.CustomerID = - RT_CID_819x_CLEVO; - break; - default: - padapter->eeprompriv.CustomerID = - RT_CID_DEFAULT; - break; - } - dev_info(&udev->dev, "r8712u: CustomerID = 0x%.4x\n", - padapter->eeprompriv.CustomerID); - /* Led mode */ - switch (padapter->eeprompriv.CustomerID) { - case RT_CID_DEFAULT: - case RT_CID_819x_ALPHA: - case RT_CID_819x_CAMEO: - padapter->ledpriv.LedStrategy = SW_LED_MODE1; - padapter->ledpriv.bRegUseLed = true; - break; - case RT_CID_819x_Sitecom: - padapter->ledpriv.LedStrategy = SW_LED_MODE2; - padapter->ledpriv.bRegUseLed = true; - break; - case RT_CID_COREGA: - case RT_CID_819x_Senao: - padapter->ledpriv.LedStrategy = SW_LED_MODE3; - padapter->ledpriv.bRegUseLed = true; - break; - case RT_CID_819x_Edimax_Belkin: - padapter->ledpriv.LedStrategy = SW_LED_MODE4; - padapter->ledpriv.bRegUseLed = true; - break; - case RT_CID_819x_Sercomm_Belkin: - padapter->ledpriv.LedStrategy = SW_LED_MODE5; - padapter->ledpriv.bRegUseLed = true; - break; - case RT_CID_819x_WNC_COREGA: - padapter->ledpriv.LedStrategy = SW_LED_MODE6; - padapter->ledpriv.bRegUseLed = true; - break; - default: - padapter->ledpriv.LedStrategy = SW_LED_MODE0; - padapter->ledpriv.bRegUseLed = false; - break; - } - } else { - AutoloadFail = false; - } - if ((!AutoloadFail) || - ((mac[0] == 0xff) && (mac[1] == 0xff) && - (mac[2] == 0xff) && (mac[3] == 0xff) && - (mac[4] == 0xff) && (mac[5] == 0xff)) || - ((mac[0] == 0x00) && (mac[1] == 0x00) && - (mac[2] == 0x00) && (mac[3] == 0x00) && - (mac[4] == 0x00) && (mac[5] == 0x00))) { - mac[0] = 0x00; - mac[1] = 0xe0; - mac[2] = 0x4c; - mac[3] = 0x87; - mac[4] = 0x00; - mac[5] = 0x00; - } - if (r8712_initmac) { - /* Make sure the user did not select a multicast - * address by setting bit 1 of first octet. - */ - mac[0] &= 0xFE; - dev_info(&udev->dev, - "r8712u: MAC Address from user = %pM\n", mac); - } else { - dev_info(&udev->dev, - "r8712u: MAC Address from efuse = %pM\n", mac); - } - eth_hw_addr_set(pnetdev, mac); - } - /* step 6. Load the firmware asynchronously */ - if (rtl871x_load_fw(padapter)) - goto deinit_drv_sw; - init_completion(&padapter->rx_filter_ready); - return 0; - -deinit_drv_sw: - r8712_free_drv_sw(padapter); -dvobj_deinit: - padapter->dvobj_deinit(padapter); -free_netdev: - free_netdev(pnetdev); -put_dev: - usb_put_dev(udev); - usb_set_intfdata(pusb_intf, NULL); - return -ENODEV; -} - -/* rmmod module & unplug(SurpriseRemoved) will call r871xu_dev_remove() - * => how to recognize both - */ -static void r871xu_dev_remove(struct usb_interface *pusb_intf) -{ - struct net_device *pnetdev = usb_get_intfdata(pusb_intf); - struct usb_device *udev = interface_to_usbdev(pusb_intf); - struct _adapter *padapter = netdev_priv(pnetdev); - - /* never exit with a firmware callback pending */ - wait_for_completion(&padapter->rtl8712_fw_ready); - if (pnetdev->reg_state != NETREG_UNINITIALIZED) - unregister_netdev(pnetdev); /* will call netdev_close() */ - usb_set_intfdata(pusb_intf, NULL); - release_firmware(padapter->fw); - if (drvpriv.drv_registered) - padapter->surprise_removed = true; - r8712_flush_rwctrl_works(padapter); - r8712_flush_led_works(padapter); - udelay(1); - /* Stop driver mlme relation timer */ - r8712_stop_drv_timers(padapter); - r871x_dev_unload(padapter); - if (padapter->dvobj_deinit) - padapter->dvobj_deinit(padapter); - r8712_free_drv_sw(padapter); - free_netdev(pnetdev); - - /* decrease the reference count of the usb device structure - * when disconnect - */ - usb_put_dev(udev); - - /* If we didn't unplug usb dongle and remove/insert module, driver - * fails on sitesurvey for the first time when device is up. - * Reset usb port for sitesurvey fail issue. - */ - if (udev->state != USB_STATE_NOTATTACHED) - usb_reset_device(udev); -} - -static int __init r8712u_drv_entry(void) -{ - drvpriv.drv_registered = true; - return usb_register(&drvpriv.r871xu_drv); -} - -static void __exit r8712u_drv_halt(void) -{ - drvpriv.drv_registered = false; - usb_deregister(&drvpriv.r871xu_drv); -} - -module_init(r8712u_drv_entry); -module_exit(r8712u_drv_halt); diff --git a/drivers/staging/rtl8712/usb_ops.c b/drivers/staging/rtl8712/usb_ops.c deleted file mode 100644 index af9966d03979c..0000000000000 --- a/drivers/staging/rtl8712/usb_ops.c +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * usb_ops.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _HCI_OPS_C_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "osdep_intf.h" -#include "usb_ops.h" -#include "recv_osdep.h" - -static u8 usb_read8(struct intf_hdl *intfhdl, u32 addr) -{ - u8 request; - u8 requesttype; - u16 wvalue; - u16 index; - u16 len; - int status; - __le32 data = 0; - struct intf_priv *intfpriv = intfhdl->pintfpriv; - - request = 0x05; - requesttype = 0x01; /* read_in */ - index = 0; - wvalue = (u16)(addr & 0x0000ffff); - len = 1; - status = r8712_usbctrl_vendorreq(intfpriv, request, wvalue, index, - &data, len, requesttype); - if (status < 0) - return 0; - return (u8)(le32_to_cpu(data) & 0x0ff); -} - -static u16 usb_read16(struct intf_hdl *intfhdl, u32 addr) -{ - u8 request; - u8 requesttype; - u16 wvalue; - u16 index; - u16 len; - int status; - __le32 data = 0; - struct intf_priv *intfpriv = intfhdl->pintfpriv; - - request = 0x05; - requesttype = 0x01; /* read_in */ - index = 0; - wvalue = (u16)(addr & 0x0000ffff); - len = 2; - status = r8712_usbctrl_vendorreq(intfpriv, request, wvalue, index, - &data, len, requesttype); - if (status < 0) - return 0; - return (u16)(le32_to_cpu(data) & 0xffff); -} - -static u32 usb_read32(struct intf_hdl *intfhdl, u32 addr) -{ - u8 request; - u8 requesttype; - u16 wvalue; - u16 index; - u16 len; - int status; - __le32 data = 0; - struct intf_priv *intfpriv = intfhdl->pintfpriv; - - request = 0x05; - requesttype = 0x01; /* read_in */ - index = 0; - wvalue = (u16)(addr & 0x0000ffff); - len = 4; - status = r8712_usbctrl_vendorreq(intfpriv, request, wvalue, index, - &data, len, requesttype); - if (status < 0) - return 0; - return le32_to_cpu(data); -} - -static void usb_write8(struct intf_hdl *intfhdl, u32 addr, u8 val) -{ - u8 request; - u8 requesttype; - u16 wvalue; - u16 index; - u16 len; - __le32 data; - struct intf_priv *intfpriv = intfhdl->pintfpriv; - - request = 0x05; - requesttype = 0x00; /* write_out */ - index = 0; - wvalue = (u16)(addr & 0x0000ffff); - len = 1; - data = cpu_to_le32((u32)val & 0x000000ff); - r8712_usbctrl_vendorreq(intfpriv, request, wvalue, index, &data, len, - requesttype); -} - -static void usb_write16(struct intf_hdl *intfhdl, u32 addr, u16 val) -{ - u8 request; - u8 requesttype; - u16 wvalue; - u16 index; - u16 len; - __le32 data; - struct intf_priv *intfpriv = intfhdl->pintfpriv; - - request = 0x05; - requesttype = 0x00; /* write_out */ - index = 0; - wvalue = (u16)(addr & 0x0000ffff); - len = 2; - data = cpu_to_le32((u32)val & 0x0000ffff); - r8712_usbctrl_vendorreq(intfpriv, request, wvalue, index, &data, len, - requesttype); -} - -static void usb_write32(struct intf_hdl *intfhdl, u32 addr, u32 val) -{ - u8 request; - u8 requesttype; - u16 wvalue; - u16 index; - u16 len; - __le32 data; - struct intf_priv *intfpriv = intfhdl->pintfpriv; - - request = 0x05; - requesttype = 0x00; /* write_out */ - index = 0; - wvalue = (u16)(addr & 0x0000ffff); - len = 4; - data = cpu_to_le32(val); - r8712_usbctrl_vendorreq(intfpriv, request, wvalue, index, &data, len, - requesttype); -} - -void r8712_usb_set_intf_option(u32 *option) -{ - *option = ((*option) | _INTF_ASYNC_); -} - -static void usb_intf_hdl_init(u8 *priv) -{ -} - -static void usb_intf_hdl_unload(u8 *priv) -{ -} - -static void usb_intf_hdl_open(u8 *priv) -{ -} - -static void usb_intf_hdl_close(u8 *priv) -{ -} - -void r8712_usb_set_intf_funs(struct intf_hdl *intfhdl) -{ - intfhdl->intf_hdl_init = usb_intf_hdl_init; - intfhdl->intf_hdl_unload = usb_intf_hdl_unload; - intfhdl->intf_hdl_open = usb_intf_hdl_open; - intfhdl->intf_hdl_close = usb_intf_hdl_close; -} - -void r8712_usb_set_intf_ops(struct _io_ops *ops) -{ - memset((u8 *)ops, 0, sizeof(struct _io_ops)); - ops->_read8 = usb_read8; - ops->_read16 = usb_read16; - ops->_read32 = usb_read32; - ops->_read_port = r8712_usb_read_port; - ops->_write8 = usb_write8; - ops->_write16 = usb_write16; - ops->_write32 = usb_write32; - ops->_write_mem = r8712_usb_write_mem; - ops->_write_port = r8712_usb_write_port; -} diff --git a/drivers/staging/rtl8712/usb_ops.h b/drivers/staging/rtl8712/usb_ops.h deleted file mode 100644 index 7a6b619b73fab..0000000000000 --- a/drivers/staging/rtl8712/usb_ops.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __USB_OPS_H_ -#define __USB_OPS_H_ - -#include "osdep_service.h" -#include "drv_types.h" -#include "osdep_intf.h" - -void r8712_usb_write_mem(struct intf_hdl *pintfhdl, u32 addr, - u32 cnt, u8 *wmem); -u32 r8712_usb_write_port(struct intf_hdl *pintfhdl, u32 addr, - u32 cnt, u8 *wmem); -u32 r8712_usb_read_port(struct intf_hdl *pintfhdl, u32 addr, - u32 cnt, u8 *rmem); -void r8712_usb_set_intf_option(u32 *poption); -void r8712_usb_set_intf_funs(struct intf_hdl *pintf_hdl); -uint r8712_usb_init_intf_priv(struct intf_priv *pintfpriv); -void r8712_usb_unload_intf_priv(struct intf_priv *pintfpriv); -void r8712_usb_set_intf_ops(struct _io_ops *pops); -void r8712_usb_read_port_cancel(struct _adapter *padapter); -void r8712_usb_write_port_cancel(struct _adapter *padapter); -int r8712_usbctrl_vendorreq(struct intf_priv *pintfpriv, u8 request, u16 value, - u16 index, void *pdata, u16 len, u8 requesttype); - -#endif - diff --git a/drivers/staging/rtl8712/usb_ops_linux.c b/drivers/staging/rtl8712/usb_ops_linux.c deleted file mode 100644 index 4a34824830e39..0000000000000 --- a/drivers/staging/rtl8712/usb_ops_linux.c +++ /dev/null @@ -1,508 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * usb_ops_linux.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _HCI_OPS_OS_C_ - -#include - -#include "osdep_service.h" -#include "drv_types.h" -#include "osdep_intf.h" -#include "usb_ops.h" - -#define RTL871X_VENQT_READ 0xc0 -#define RTL871X_VENQT_WRITE 0x40 - -uint r8712_usb_init_intf_priv(struct intf_priv *pintfpriv) -{ - pintfpriv->piorw_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!pintfpriv->piorw_urb) - return _FAIL; - init_completion(&pintfpriv->io_retevt_comp); - return _SUCCESS; -} - -void r8712_usb_unload_intf_priv(struct intf_priv *pintfpriv) -{ - if (pintfpriv->piorw_urb) { - usb_kill_urb(pintfpriv->piorw_urb); - usb_free_urb(pintfpriv->piorw_urb); - } -} - -static unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr) -{ - unsigned int pipe = 0; - struct usb_device *pusbd = pdvobj->pusbdev; - - if (pdvobj->nr_endpoint == 11) { - switch (addr) { - case RTL8712_DMA_BKQ: - pipe = usb_sndbulkpipe(pusbd, 0x07); - break; - case RTL8712_DMA_BEQ: - pipe = usb_sndbulkpipe(pusbd, 0x06); - break; - case RTL8712_DMA_VIQ: - pipe = usb_sndbulkpipe(pusbd, 0x05); - break; - case RTL8712_DMA_VOQ: - pipe = usb_sndbulkpipe(pusbd, 0x04); - break; - case RTL8712_DMA_BCNQ: - pipe = usb_sndbulkpipe(pusbd, 0x0a); - break; - case RTL8712_DMA_BMCQ: /* HI Queue */ - pipe = usb_sndbulkpipe(pusbd, 0x0b); - break; - case RTL8712_DMA_MGTQ: - pipe = usb_sndbulkpipe(pusbd, 0x0c); - break; - case RTL8712_DMA_RX0FF: - pipe = usb_rcvbulkpipe(pusbd, 0x03); /* in */ - break; - case RTL8712_DMA_C2HCMD: - pipe = usb_rcvbulkpipe(pusbd, 0x09); /* in */ - break; - case RTL8712_DMA_H2CCMD: - pipe = usb_sndbulkpipe(pusbd, 0x0d); - break; - } - } else if (pdvobj->nr_endpoint == 6) { - switch (addr) { - case RTL8712_DMA_BKQ: - pipe = usb_sndbulkpipe(pusbd, 0x07); - break; - case RTL8712_DMA_BEQ: - pipe = usb_sndbulkpipe(pusbd, 0x06); - break; - case RTL8712_DMA_VIQ: - pipe = usb_sndbulkpipe(pusbd, 0x05); - break; - case RTL8712_DMA_VOQ: - pipe = usb_sndbulkpipe(pusbd, 0x04); - break; - case RTL8712_DMA_RX0FF: - case RTL8712_DMA_C2HCMD: - pipe = usb_rcvbulkpipe(pusbd, 0x03); /* in */ - break; - case RTL8712_DMA_H2CCMD: - case RTL8712_DMA_BCNQ: - case RTL8712_DMA_BMCQ: - case RTL8712_DMA_MGTQ: - pipe = usb_sndbulkpipe(pusbd, 0x0d); - break; - } - } else if (pdvobj->nr_endpoint == 4) { - switch (addr) { - case RTL8712_DMA_BEQ: - pipe = usb_sndbulkpipe(pusbd, 0x06); - break; - case RTL8712_DMA_VOQ: - pipe = usb_sndbulkpipe(pusbd, 0x04); - break; - case RTL8712_DMA_RX0FF: - case RTL8712_DMA_C2HCMD: - pipe = usb_rcvbulkpipe(pusbd, 0x03); /* in */ - break; - case RTL8712_DMA_H2CCMD: - case RTL8712_DMA_BCNQ: - case RTL8712_DMA_BMCQ: - case RTL8712_DMA_MGTQ: - pipe = usb_sndbulkpipe(pusbd, 0x0d); - break; - } - } else { - pipe = 0; - } - return pipe; -} - -static void usb_write_mem_complete(struct urb *purb) -{ - struct io_queue *pio_q = (struct io_queue *)purb->context; - struct intf_hdl *pintf = &(pio_q->intf); - struct intf_priv *pintfpriv = pintf->pintfpriv; - struct _adapter *padapter = (struct _adapter *)pintf->adapter; - - if (purb->status != 0) { - if (purb->status == (-ESHUTDOWN)) - padapter->driver_stopped = true; - else - padapter->surprise_removed = true; - } - complete(&pintfpriv->io_retevt_comp); -} - -void r8712_usb_write_mem(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem) -{ - unsigned int pipe; - struct _adapter *padapter = (struct _adapter *)pintfhdl->adapter; - struct intf_priv *pintfpriv = pintfhdl->pintfpriv; - struct io_queue *pio_queue = padapter->pio_queue; - struct dvobj_priv *pdvobj = (struct dvobj_priv *)pintfpriv->intf_dev; - struct usb_device *pusbd = pdvobj->pusbdev; - struct urb *piorw_urb = pintfpriv->piorw_urb; - - if ((padapter->driver_stopped) || (padapter->surprise_removed) || - (padapter->pwrctrlpriv.pnp_bstop_trx)) - return; - /* translate DMA FIFO addr to pipehandle */ - pipe = ffaddr2pipehdl(pdvobj, addr); - if (pipe == 0) - return; - usb_fill_bulk_urb(piorw_urb, pusbd, pipe, - wmem, cnt, usb_write_mem_complete, - pio_queue); - usb_submit_urb(piorw_urb, GFP_ATOMIC); - wait_for_completion_interruptible(&pintfpriv->io_retevt_comp); -} - -static void r8712_usb_read_port_complete(struct urb *purb) -{ - uint isevt; - __le32 *pbuf; - struct recv_buf *precvbuf = (struct recv_buf *)purb->context; - struct _adapter *padapter = (struct _adapter *)precvbuf->adapter; - struct recv_priv *precvpriv = &padapter->recvpriv; - - if (padapter->surprise_removed || padapter->driver_stopped) - return; - if (purb->status == 0) { /* SUCCESS */ - if ((purb->actual_length > (MAX_RECVBUF_SZ)) || - (purb->actual_length < RXDESC_SIZE)) { - r8712_read_port(padapter, precvpriv->ff_hwaddr, 0, - (unsigned char *)precvbuf); - } else { - _pkt *pskb = precvbuf->pskb; - - precvbuf->transfer_len = purb->actual_length; - pbuf = (__le32 *)precvbuf->pbuf; - isevt = le32_to_cpu(*(pbuf + 1)) & 0x1ff; - if ((isevt & 0x1ff) == 0x1ff) { - r8712_rxcmd_event_hdl(padapter, pbuf); - skb_queue_tail(&precvpriv->rx_skb_queue, pskb); - r8712_read_port(padapter, precvpriv->ff_hwaddr, - 0, (unsigned char *)precvbuf); - } else { - skb_put(pskb, purb->actual_length); - skb_queue_tail(&precvpriv->rx_skb_queue, pskb); - tasklet_hi_schedule(&precvpriv->recv_tasklet); - r8712_read_port(padapter, precvpriv->ff_hwaddr, - 0, (unsigned char *)precvbuf); - } - } - } else { - switch (purb->status) { - case -EINVAL: - case -EPIPE: - case -ENODEV: - case -ESHUTDOWN: - padapter->driver_stopped = true; - break; - case -ENOENT: - if (!padapter->suspended) { - padapter->driver_stopped = true; - break; - } - fallthrough; - case -EPROTO: - r8712_read_port(padapter, precvpriv->ff_hwaddr, 0, - (unsigned char *)precvbuf); - break; - case -EINPROGRESS: - netdev_err(padapter->pnetdev, "ERROR: URB IS IN PROGRESS!\n"); - break; - default: - break; - } - } -} - -u32 r8712_usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem) -{ - unsigned int pipe; - int err; - u32 tmpaddr = 0; - int alignment = 0; - u32 ret = _SUCCESS; - struct urb *purb = NULL; - struct recv_buf *precvbuf = (struct recv_buf *)rmem; - struct intf_priv *pintfpriv = pintfhdl->pintfpriv; - struct dvobj_priv *pdvobj = (struct dvobj_priv *)pintfpriv->intf_dev; - struct _adapter *adapter = pdvobj->padapter; - struct recv_priv *precvpriv = &adapter->recvpriv; - struct usb_device *pusbd = pdvobj->pusbdev; - - if (adapter->driver_stopped || adapter->surprise_removed || - adapter->pwrctrlpriv.pnp_bstop_trx || !precvbuf) - return _FAIL; - r8712_init_recvbuf(adapter, precvbuf); - /* Try to use skb from the free queue */ - precvbuf->pskb = skb_dequeue(&precvpriv->free_recv_skb_queue); - - if (!precvbuf->pskb) { - precvbuf->pskb = netdev_alloc_skb(adapter->pnetdev, - MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ); - if (!precvbuf->pskb) - return _FAIL; - tmpaddr = (addr_t)precvbuf->pskb->data; - alignment = tmpaddr & (RECVBUFF_ALIGN_SZ - 1); - skb_reserve(precvbuf->pskb, - (RECVBUFF_ALIGN_SZ - alignment)); - precvbuf->phead = precvbuf->pskb->head; - precvbuf->pdata = precvbuf->pskb->data; - precvbuf->ptail = skb_tail_pointer(precvbuf->pskb); - precvbuf->pend = skb_end_pointer(precvbuf->pskb); - precvbuf->pbuf = precvbuf->pskb->data; - } else { /* skb is reused */ - precvbuf->phead = precvbuf->pskb->head; - precvbuf->pdata = precvbuf->pskb->data; - precvbuf->ptail = skb_tail_pointer(precvbuf->pskb); - precvbuf->pend = skb_end_pointer(precvbuf->pskb); - precvbuf->pbuf = precvbuf->pskb->data; - } - purb = precvbuf->purb; - /* translate DMA FIFO addr to pipehandle */ - pipe = ffaddr2pipehdl(pdvobj, addr); - usb_fill_bulk_urb(purb, pusbd, pipe, - precvbuf->pbuf, MAX_RECVBUF_SZ, - r8712_usb_read_port_complete, - precvbuf); - err = usb_submit_urb(purb, GFP_ATOMIC); - if ((err) && (err != (-EPERM))) - ret = _FAIL; - return ret; -} - -void r8712_usb_read_port_cancel(struct _adapter *padapter) -{ - int i; - struct recv_buf *precvbuf; - - precvbuf = (struct recv_buf *)padapter->recvpriv.precv_buf; - for (i = 0; i < NR_RECVBUFF; i++) { - if (precvbuf->purb) - usb_kill_urb(precvbuf->purb); - precvbuf++; - } -} - -void r8712_xmit_bh(struct tasklet_struct *t) -{ - int ret = false; - struct _adapter *padapter = from_tasklet(padapter, t, - xmitpriv.xmit_tasklet); - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - - if (padapter->driver_stopped || - padapter->surprise_removed) { - netdev_err(padapter->pnetdev, "xmit_bh => driver_stopped or surprise_removed\n"); - return; - } - ret = r8712_xmitframe_complete(padapter, pxmitpriv, NULL); - if (!ret) - return; - tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); -} - -static void usb_write_port_complete(struct urb *purb) -{ - int i; - struct xmit_frame *pxmitframe = (struct xmit_frame *)purb->context; - struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf; - struct _adapter *padapter = pxmitframe->padapter; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - - switch (pattrib->priority) { - case 1: - case 2: - pxmitpriv->bkq_cnt--; - break; - case 4: - case 5: - pxmitpriv->viq_cnt--; - break; - case 6: - case 7: - pxmitpriv->voq_cnt--; - break; - case 0: - case 3: - default: - pxmitpriv->beq_cnt--; - break; - } - pxmitpriv->txirp_cnt--; - for (i = 0; i < 8; i++) { - if (purb == pxmitframe->pxmit_urb[i]) { - pxmitframe->bpending[i] = false; - break; - } - } - if (padapter->surprise_removed) - return; - switch (purb->status) { - case 0: - break; - default: - netdev_warn(padapter->pnetdev, - "r8712u: pipe error: (%d)\n", purb->status); - break; - } - /* not to consider tx fragment */ - r8712_free_xmitframe_ex(pxmitpriv, pxmitframe); - r8712_free_xmitbuf(pxmitpriv, pxmitbuf); - tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); -} - -u32 r8712_usb_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem) -{ - unsigned long irqL; - int i, status; - unsigned int pipe; - u32 ret, bwritezero; - struct urb *purb = NULL; - struct _adapter *padapter = (struct _adapter *)pintfhdl->adapter; - struct dvobj_priv *pdvobj = &padapter->dvobjpriv; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - struct xmit_frame *pxmitframe = (struct xmit_frame *)wmem; - struct usb_device *pusbd = pdvobj->pusbdev; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - - if ((padapter->driver_stopped) || (padapter->surprise_removed) || - (padapter->pwrctrlpriv.pnp_bstop_trx)) - return _FAIL; - for (i = 0; i < 8; i++) { - if (!pxmitframe->bpending[i]) { - spin_lock_irqsave(&pxmitpriv->lock, irqL); - pxmitpriv->txirp_cnt++; - pxmitframe->bpending[i] = true; - switch (pattrib->priority) { - case 1: - case 2: - pxmitpriv->bkq_cnt++; - break; - case 4: - case 5: - pxmitpriv->viq_cnt++; - break; - case 6: - case 7: - pxmitpriv->voq_cnt++; - break; - case 0: - case 3: - default: - pxmitpriv->beq_cnt++; - break; - } - spin_unlock_irqrestore(&pxmitpriv->lock, irqL); - pxmitframe->sz[i] = (u16)cnt; - purb = pxmitframe->pxmit_urb[i]; - break; - } - } - bwritezero = false; - if (pdvobj->ishighspeed) { - if (cnt > 0 && cnt % 512 == 0) - bwritezero = true; - } else { - if (cnt > 0 && cnt % 64 == 0) - bwritezero = true; - } - /* translate DMA FIFO addr to pipehandle */ - pipe = ffaddr2pipehdl(pdvobj, addr); - if (pxmitpriv->free_xmitbuf_cnt % NR_XMITBUFF == 0) - purb->transfer_flags &= (~URB_NO_INTERRUPT); - else - purb->transfer_flags |= URB_NO_INTERRUPT; - if (bwritezero) - cnt += 8; - usb_fill_bulk_urb(purb, pusbd, pipe, - pxmitframe->mem_addr, - cnt, usb_write_port_complete, - pxmitframe); /* context is xmit_frame */ - status = usb_submit_urb(purb, GFP_ATOMIC); - if (!status) - ret = _SUCCESS; - else - ret = _FAIL; - return ret; -} - -void r8712_usb_write_port_cancel(struct _adapter *padapter) -{ - int i, j; - struct xmit_buf *pxmitbuf = (struct xmit_buf *) - padapter->xmitpriv.pxmitbuf; - - for (i = 0; i < NR_XMITBUFF; i++) { - for (j = 0; j < 8; j++) { - if (pxmitbuf->pxmit_urb[j]) - usb_kill_urb(pxmitbuf->pxmit_urb[j]); - } - pxmitbuf++; - } -} - -int r8712_usbctrl_vendorreq(struct intf_priv *pintfpriv, u8 request, u16 value, - u16 index, void *pdata, u16 len, u8 requesttype) -{ - unsigned int pipe; - int status; - u8 reqtype; - struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *) - pintfpriv->intf_dev; - struct usb_device *udev = pdvobjpriv->pusbdev; - /* For mstar platform, mstar suggests the address for USB IO - * should be 16 bytes alignment. Trying to fix it here. - */ - u8 *palloc_buf, *pIo_buf; - - palloc_buf = kmalloc((u32)len + 16, GFP_ATOMIC); - if (!palloc_buf) - return -ENOMEM; - pIo_buf = palloc_buf + 16 - ((addr_t)(palloc_buf) & 0x0f); - if (requesttype == 0x01) { - pipe = usb_rcvctrlpipe(udev, 0); /* read_in */ - reqtype = RTL871X_VENQT_READ; - } else { - pipe = usb_sndctrlpipe(udev, 0); /* write_out */ - reqtype = RTL871X_VENQT_WRITE; - memcpy(pIo_buf, pdata, len); - } - status = usb_control_msg(udev, pipe, request, reqtype, value, index, - pIo_buf, len, 500); - if (status < 0) - goto free; - if (status != len) { - status = -EREMOTEIO; - goto free; - } - /* Success this control transfer. */ - if (requesttype == 0x01) { - /* For Control read transfer, we have to copy the read - * data from pIo_buf to pdata. - */ - memcpy(pdata, pIo_buf, status); - } - -free: - kfree(palloc_buf); - return status; -} diff --git a/drivers/staging/rtl8712/usb_osintf.h b/drivers/staging/rtl8712/usb_osintf.h deleted file mode 100644 index 2e512b4a564c2..0000000000000 --- a/drivers/staging/rtl8712/usb_osintf.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __USB_OSINTF_H -#define __USB_OSINTF_H - -#include "osdep_service.h" -#include "drv_types.h" - -extern char *r8712_initmac; - -unsigned int r8712_usb_inirp_init(struct _adapter *padapter); -unsigned int r8712_usb_inirp_deinit(struct _adapter *padapter); -uint rtl871x_hal_init(struct _adapter *padapter); -uint rtl8712_hal_deinit(struct _adapter *padapter); - -void rtl871x_intf_stop(struct _adapter *padapter); -void r871x_dev_unload(struct _adapter *padapter); -void r8712_stop_drv_threads(struct _adapter *padapter); -void r8712_stop_drv_timers(struct _adapter *padapter); -int r8712_init_drv_sw(struct _adapter *padapter); -void r8712_free_drv_sw(struct _adapter *padapter); -struct net_device *r8712_init_netdev(void); - -#endif diff --git a/drivers/staging/rtl8712/wifi.h b/drivers/staging/rtl8712/wifi.h deleted file mode 100644 index 498e6dec7e67f..0000000000000 --- a/drivers/staging/rtl8712/wifi.h +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef _WIFI_H_ -#define _WIFI_H_ - -#include -#include - -#define WLAN_HDR_A3_LEN 24 -#define WLAN_HDR_A3_QOS_LEN 26 - -enum WIFI_FRAME_TYPE { - WIFI_QOS_DATA_TYPE = (BIT(7) | BIT(3)), /*!< QoS Data */ -}; - -#define SetToDs(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_TODS); \ -}) - -#define GetToDs(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0) - -#define ClearToDs(pbuf) ({ \ - *(__le16 *)(pbuf) &= (~cpu_to_le16(IEEE80211_FCTL_TODS)); \ -}) - -#define SetFrDs(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_FROMDS); \ -}) - -#define GetFrDs(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0) - -#define ClearFrDs(pbuf) ({ \ - *(__le16 *)(pbuf) &= (~cpu_to_le16(IEEE80211_FCTL_FROMDS)); \ -}) - -static inline unsigned char get_tofr_ds(unsigned char *pframe) -{ - return ((GetToDs(pframe) << 1) | GetFrDs(pframe)); -} - -#define SetMFrag(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); \ -}) - -#define GetMFrag(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0) - -#define ClearMFrag(pbuf) ({ \ - *(__le16 *)(pbuf) &= (~cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)); \ -}) - -#define SetRetry(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_RETRY); \ -}) - -#define GetRetry(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0) - -#define ClearRetry(pbuf) ({ \ - *(__le16 *)(pbuf) &= (~cpu_to_le16(IEEE80211_FCTL_RETRY)); \ -}) - -#define SetPwrMgt(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_PM); \ -}) - -#define GetPwrMgt(pbuf) (((*(__le16 *)(pbuf)) & \ - cpu_to_le16(IEEE80211_FCTL_PM)) != 0) - -#define ClearPwrMgt(pbuf) ({ \ - *(__le16 *)(pbuf) &= (~cpu_to_le16(IEEE80211_FCTL_PM)); \ -}) - -#define SetMData(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); \ -}) - -#define GetMData(pbuf) (((*(__le16 *)(pbuf)) & \ - cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0) - -#define ClearMData(pbuf) ({ \ - *(__le16 *)(pbuf) &= (~cpu_to_le16(IEEE80211_FCTL_MOREDATA)); \ -}) - -#define SetPrivacy(pbuf) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); \ -}) - -#define GetPrivacy(pbuf) (((*(__le16 *)(pbuf)) & \ - cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0) - -#define GetOrder(pbuf) (((*(__le16 *)(pbuf)) & \ - cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0) - -#define GetFrameType(pbuf) (le16_to_cpu(*(__le16 *)(pbuf)) & \ - (BIT(3) | BIT(2))) - -#define SetFrameType(pbuf, type) \ - do { \ - *(__le16 *)(pbuf) &= cpu_to_le16(~(BIT(3) | \ - BIT(2))); \ - *(__le16 *)(pbuf) |= cpu_to_le16(type); \ - } while (0) - -#define GetFrameSubType(pbuf) (le16_to_cpu(*(__le16 *)(pbuf)) & \ - (BIT(7) | BIT(6) | BIT(5) | BIT(4) | BIT(3) | \ - BIT(2))) - -#define SetFrameSubType(pbuf, type) \ - do { \ - *(__le16 *)(pbuf) &= cpu_to_le16(~(BIT(7) | BIT(6) | \ - BIT(5) | BIT(4) | BIT(3) | BIT(2))); \ - *(__le16 *)(pbuf) |= cpu_to_le16(type); \ - } while (0) - -#define GetSequence(pbuf) (le16_to_cpu(*(__le16 *)\ - ((addr_t)(pbuf) + 22)) >> 4) - -#define GetFragNum(pbuf) (le16_to_cpu(*(__le16 *)((addr_t)\ - (pbuf) + 22)) & 0x0f) - -#define SetSeqNum(pbuf, num) ({ \ - *(__le16 *)((addr_t)(pbuf) + 22) = \ - cpu_to_le16((le16_to_cpu(*(__le16 *)((addr_t)(pbuf) + 22)) & \ - 0x000f) | (0xfff0 & (num << 4))); \ -}) - -#define SetPriority(pbuf, tid) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16(tid & 0xf); \ -}) - -#define GetPriority(pbuf) ((le16_to_cpu(*(__le16 *)(pbuf))) & 0xf) - -#define SetAckpolicy(pbuf, ack) ({ \ - *(__le16 *)(pbuf) |= cpu_to_le16((ack & 3) << 5); \ -}) - -#define GetAckpolicy(pbuf) (((le16_to_cpu(*(__le16 *)pbuf)) >> 5) & 0x3) - -#define GetAMsdu(pbuf) (((le16_to_cpu(*(__le16 *)pbuf)) >> 7) & 0x1) - -#define GetAddr1Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 4)) - -#define GetAddr2Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 10)) - -#define GetAddr3Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 16)) - -#define GetAddr4Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 24)) - -static inline unsigned char *get_hdr_bssid(unsigned char *pframe) -{ - unsigned char *sa; - unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe); - - switch (to_fr_ds) { - case 0x00: /* ToDs=0, FromDs=0 */ - sa = GetAddr3Ptr(pframe); - break; - case 0x01: /* ToDs=0, FromDs=1 */ - sa = GetAddr2Ptr(pframe); - break; - case 0x02: /* ToDs=1, FromDs=0 */ - sa = GetAddr1Ptr(pframe); - break; - default: /* ToDs=1, FromDs=1 */ - sa = NULL; - break; - } - return sa; -} - -/* --------------------------------------------------------------------------- - * Below is the fixed elements... - * --------------------------------------------------------------------------- - */ -#define _BEACON_ITERVAL_ 2 -#define _CAPABILITY_ 2 -#define _TIMESTAMP_ 8 - -/*----------------------------------------------------------------------------- - * Below is the definition for WMM - *------------------------------------------------------------------------------ - */ -#define _WMM_IE_Length_ 7 /* for WMM STA */ - -#endif /* _WIFI_H_ */ - diff --git a/drivers/staging/rtl8712/wlan_bssdef.h b/drivers/staging/rtl8712/wlan_bssdef.h deleted file mode 100644 index ec3749813728d..0000000000000 --- a/drivers/staging/rtl8712/wlan_bssdef.h +++ /dev/null @@ -1,223 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __WLAN_BSSDEF_H__ -#define __WLAN_BSSDEF_H__ - -#define MAX_IE_SZ 768 - -#define NDIS_802_11_LENGTH_SSID 32 -#define NDIS_802_11_LENGTH_RATES 8 -#define NDIS_802_11_LENGTH_RATES_EX 16 - -struct ndis_802_11_ssid { - u32 SsidLength; - u8 Ssid[32]; -}; - -enum NDIS_802_11_NETWORK_TYPE { - Ndis802_11FH, - Ndis802_11DS, - Ndis802_11OFDM5, - Ndis802_11OFDM24, - Ndis802_11NetworkTypeMax /* not a real type, defined as an upper bound*/ -}; - -struct NDIS_802_11_CONFIGURATION_FH { - u32 Length; /* Length of structure */ - u32 HopPattern; /* As defined by 802.11, MSB set */ - u32 HopSet; /* to one if non-802.11 */ - u32 DwellTime; /* units are Kusec */ -}; - -/* - * FW will only save the channel number in DSConfig. - * ODI Handler will convert the channel number to freq. number. - */ -struct NDIS_802_11_CONFIGURATION { - u32 Length; /* Length of structure */ - u32 BeaconPeriod; /* units are Kusec */ - u32 ATIMWindow; /* units are Kusec */ - u32 DSConfig; /* Frequency, units are kHz */ - struct NDIS_802_11_CONFIGURATION_FH FHConfig; -}; - -enum NDIS_802_11_NETWORK_INFRASTRUCTURE { - Ndis802_11IBSS, - Ndis802_11Infrastructure, - Ndis802_11AutoUnknown, - Ndis802_11InfrastructureMax, /*Not a real value,defined as upper bound*/ - Ndis802_11APMode -}; - -struct NDIS_802_11_FIXED_IEs { - u8 Timestamp[8]; - u16 BeaconInterval; - u16 Capabilities; -}; - -struct wlan_bssid_ex { - u32 Length; - unsigned char MacAddress[6]; - u8 Reserved[2]; - struct ndis_802_11_ssid Ssid; - __le32 Privacy; - s32 Rssi; - enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; - struct NDIS_802_11_CONFIGURATION Configuration; - enum NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; - u8 rates[NDIS_802_11_LENGTH_RATES_EX]; - /* number of content bytes in EIs, which varies */ - u32 IELength; - /*(timestamp, beacon interval, and capability information) */ - u8 IEs[MAX_IE_SZ]; -}; - -enum NDIS_802_11_AUTHENTICATION_MODE { - Ndis802_11AuthModeOpen, - Ndis802_11AuthModeShared, - Ndis802_11AuthModeAutoSwitch, - Ndis802_11AuthModeWPA, - Ndis802_11AuthModeWPAPSK, - Ndis802_11AuthModeWPANone, - Ndis802_11AuthModeMax /* Not a real mode, defined as upper bound */ -}; - -enum { - Ndis802_11WEPEnabled, - Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, - Ndis802_11WEPDisabled, - Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, - Ndis802_11WEPKeyAbsent, - Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, - Ndis802_11WEPNotSupported, - Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, - Ndis802_11Encryption2Enabled, - Ndis802_11Encryption2KeyAbsent, - Ndis802_11Encryption3Enabled, - Ndis802_11Encryption3KeyAbsent -}; - -#define NDIS_802_11_AI_REQFI_CAPABILITIES 1 -#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2 -#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4 - -#define NDIS_802_11_AI_RESFI_CAPABILITIES 1 -#define NDIS_802_11_AI_RESFI_STATUSCODE 2 -#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4 - -struct NDIS_802_11_AI_REQFI { - u16 Capabilities; - u16 ListenInterval; - unsigned char CurrentAPAddress[6]; -}; - -struct NDIS_802_11_AI_RESFI { - u16 Capabilities; - u16 StatusCode; - u16 AssociationId; -}; - -struct NDIS_802_11_ASSOCIATION_INFORMATION { - u32 Length; - u16 AvailableRequestFixedIEs; - struct NDIS_802_11_AI_REQFI RequestFixedIEs; - u32 RequestIELength; - u32 OffsetRequestIEs; - u16 AvailableResponseFixedIEs; - struct NDIS_802_11_AI_RESFI ResponseFixedIEs; - u32 ResponseIELength; - u32 OffsetResponseIEs; -}; - -/* Key mapping keys require a BSSID*/ -struct NDIS_802_11_KEY { - u32 Length; /* Length of this structure */ - u32 KeyIndex; - u32 KeyLength; /* length of key in bytes */ - unsigned char BSSID[6]; - unsigned long long KeyRSC; - u8 KeyMaterial[32]; /* variable length */ -}; - -struct NDIS_802_11_REMOVE_KEY { - u32 Length; /* Length of this structure */ - u32 KeyIndex; - unsigned char BSSID[6]; -}; - -struct NDIS_802_11_WEP { - u32 Length; /* Length of this structure */ - u32 KeyIndex; /* 0 is the per-client key, - * 1-N are the global keys - */ - u32 KeyLength; /* length of key in bytes */ - u8 KeyMaterial[16]; /* variable length depending on above field */ -}; - -/* mask for authentication/integrity fields */ -#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f -#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 -#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 -#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 -#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E - -/* MIC check time, 60 seconds. */ -#define MIC_CHECK_TIME 60000000 - -#ifndef Ndis802_11APMode -#define Ndis802_11APMode (Ndis802_11InfrastructureMax + 1) -#endif - -struct wlan_network { - struct list_head list; - int network_type; /*refer to ieee80211.h for WIRELESS_11A/B/G */ - int fixed; /* set to fixed when not to be removed asi - * site-surveying - */ - unsigned int last_scanned; /*timestamp for the network */ - int aid; /*will only be valid when a BSS is joined. */ - int join_res; - struct wlan_bssid_ex network; /*must be the last item */ -}; - -enum VRTL_CARRIER_SENSE { - DISABLE_VCS, - ENABLE_VCS, - AUTO_VCS -}; - -enum VCS_TYPE { - NONE_VCS, - RTS_CTS, - CTS_TO_SELF -}; - -#define PWR_CAM 0 -#define PWR_MINPS 1 -#define PWR_MAXPS 2 -#define PWR_UAPSD 3 -#define PWR_VOIP 4 - -enum UAPSD_MAX_SP { - NO_LIMIT, - TWO_MSDU, - FOUR_MSDU, - SIX_MSDU -}; - -#define NUM_PRE_AUTH_KEY 16 -#define NUM_PMKID_CACHE NUM_PRE_AUTH_KEY - -#endif /* #ifndef WLAN_BSSDEF_H_ */ - diff --git a/drivers/staging/rtl8712/xmit_linux.c b/drivers/staging/rtl8712/xmit_linux.c deleted file mode 100644 index ceb6b590b310f..0000000000000 --- a/drivers/staging/rtl8712/xmit_linux.c +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * xmit_linux.c - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * Linux device driver for RTL8192SU - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ - -#define _XMIT_OSDEP_C_ - -#include -#include -#include -#include - -#include "osdep_service.h" -#include "drv_types.h" - -#include "wifi.h" -#include "mlme_osdep.h" -#include "xmit_osdep.h" -#include "osdep_intf.h" - -static uint remainder_len(struct pkt_file *pfile) -{ - return (uint)(pfile->buf_len - ((addr_t)(pfile->cur_addr) - - (addr_t)(pfile->buf_start))); -} - -void _r8712_open_pktfile(_pkt *pktptr, struct pkt_file *pfile) -{ - pfile->pkt = pktptr; - pfile->cur_addr = pfile->buf_start = pktptr->data; - pfile->pkt_len = pfile->buf_len = pktptr->len; - pfile->cur_buffer = pfile->buf_start; -} - -uint _r8712_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen) -{ - uint len; - - len = remainder_len(pfile); - len = (rlen > len) ? len : rlen; - if (rmem) - skb_copy_bits(pfile->pkt, pfile->buf_len - pfile->pkt_len, - rmem, len); - pfile->cur_addr += len; - pfile->pkt_len -= len; - return len; -} - -sint r8712_endofpktfile(struct pkt_file *pfile) -{ - return (pfile->pkt_len == 0); -} - -void r8712_set_qos(struct pkt_file *ppktfile, struct pkt_attrib *pattrib) -{ - struct ethhdr etherhdr; - struct iphdr ip_hdr; - u16 user_priority = 0; - - _r8712_open_pktfile(ppktfile->pkt, ppktfile); - _r8712_pktfile_read(ppktfile, (unsigned char *)ðerhdr, ETH_HLEN); - - /* get user_priority from IP hdr*/ - if (pattrib->ether_type == 0x0800) { - _r8712_pktfile_read(ppktfile, (u8 *)&ip_hdr, sizeof(ip_hdr)); - /*user_priority = (ntohs(ip_hdr.tos) >> 5) & 0x3 ;*/ - user_priority = ip_hdr.tos >> 5; - } else { - /* "When priority processing of data frames is supported, - * a STA's SME should send EAPOL-Key frames at the highest - * priority." - */ - - if (pattrib->ether_type == 0x888e) - user_priority = 7; - } - pattrib->priority = user_priority; - pattrib->hdrlen = WLAN_HDR_A3_QOS_LEN; - pattrib->subtype = WIFI_QOS_DATA_TYPE; -} - -void r8712_SetFilter(struct work_struct *work) -{ - struct _adapter *adapter = container_of(work, struct _adapter, - wk_filter_rx_ff0); - u8 oldvalue = 0x00, newvalue = 0x00; - - oldvalue = r8712_read8(adapter, 0x117); - newvalue = oldvalue & 0xfe; - r8712_write8(adapter, 0x117, newvalue); - - wait_for_completion(&adapter->rx_filter_ready); - r8712_write8(adapter, 0x117, oldvalue); -} - -int r8712_xmit_resource_alloc(struct _adapter *padapter, - struct xmit_buf *pxmitbuf) -{ - int i; - - for (i = 0; i < 8; i++) { - pxmitbuf->pxmit_urb[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!pxmitbuf->pxmit_urb[i]) { - int k; - - for (k = i - 1; k >= 0; k--) { - /* handle allocation errors part way through loop */ - usb_free_urb(pxmitbuf->pxmit_urb[k]); - } - netdev_err(padapter->pnetdev, "pxmitbuf->pxmit_urb[i] == NULL\n"); - return -ENOMEM; - } - kmemleak_not_leak(pxmitbuf->pxmit_urb[i]); - } - return 0; -} - -void r8712_xmit_resource_free(struct _adapter *padapter, - struct xmit_buf *pxmitbuf) -{ - int i; - - for (i = 0; i < 8; i++) { - if (pxmitbuf->pxmit_urb[i]) { - usb_kill_urb(pxmitbuf->pxmit_urb[i]); - usb_free_urb(pxmitbuf->pxmit_urb[i]); - } - } -} - -void r8712_xmit_complete(struct _adapter *padapter, struct xmit_frame *pxframe) -{ - if (pxframe->pkt) - dev_kfree_skb_any(pxframe->pkt); - pxframe->pkt = NULL; -} - -netdev_tx_t r8712_xmit_entry(_pkt *pkt, struct net_device *netdev) -{ - struct xmit_frame *xmitframe = NULL; - struct _adapter *adapter = netdev_priv(netdev); - struct xmit_priv *xmitpriv = &(adapter->xmitpriv); - - if (!r8712_if_up(adapter)) - goto _xmit_entry_drop; - - xmitframe = r8712_alloc_xmitframe(xmitpriv); - if (!xmitframe) - goto _xmit_entry_drop; - - if (r8712_update_attrib(adapter, pkt, &xmitframe->attrib)) - goto _xmit_entry_drop; - - adapter->ledpriv.LedControlHandler(adapter, LED_CTL_TX); - xmitframe->pkt = pkt; - if (r8712_pre_xmit(adapter, xmitframe)) { - /*dump xmitframe directly or drop xframe*/ - dev_kfree_skb_any(pkt); - xmitframe->pkt = NULL; - } - xmitpriv->tx_pkts++; - xmitpriv->tx_bytes += xmitframe->attrib.last_txcmdsz; - return NETDEV_TX_OK; -_xmit_entry_drop: - if (xmitframe) - r8712_free_xmitframe(xmitpriv, xmitframe); - xmitpriv->tx_drop++; - dev_kfree_skb_any(pkt); - return NETDEV_TX_OK; -} diff --git a/drivers/staging/rtl8712/xmit_osdep.h b/drivers/staging/rtl8712/xmit_osdep.h deleted file mode 100644 index 1ad42658c8831..0000000000000 --- a/drivers/staging/rtl8712/xmit_osdep.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. - * - * Modifications for inclusion into the Linux staging tree are - * Copyright(c) 2010 Larry Finger. All rights reserved. - * - * Contact information: - * WLAN FAE - * Larry Finger - * - ******************************************************************************/ -#ifndef __XMIT_OSDEP_H_ -#define __XMIT_OSDEP_H_ - -#include "osdep_service.h" -#include "drv_types.h" - -struct pkt_file { - _pkt *pkt; - u32 pkt_len; /*the remainder length of the open_file*/ - _buffer *cur_buffer; - u8 *buf_start; - u8 *cur_addr; - u32 buf_len; -}; - -#define NR_XMITFRAME 256 - -struct xmit_priv; -struct pkt_attrib; -struct sta_xmit_priv; -struct xmit_frame; -struct xmit_buf; - -netdev_tx_t r8712_xmit_entry(_pkt *pkt, struct net_device *pnetdev); -void r8712_SetFilter(struct work_struct *work); -int r8712_xmit_resource_alloc(struct _adapter *padapter, - struct xmit_buf *pxmitbuf); -void r8712_xmit_resource_free(struct _adapter *padapter, - struct xmit_buf *pxmitbuf); - -void r8712_set_qos(struct pkt_file *ppktfile, - struct pkt_attrib *pattrib); -void _r8712_open_pktfile(_pkt *pktptr, struct pkt_file *pfile); -uint _r8712_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen); -sint r8712_endofpktfile(struct pkt_file *pfile); -void r8712_xmit_complete(struct _adapter *padapter, - struct xmit_frame *pxframe); - -#endif diff --git a/drivers/staging/rtl8723bs/TODO b/drivers/staging/rtl8723bs/TODO index 4c413f9d3df00..050dcd0bffab5 100644 --- a/drivers/staging/rtl8723bs/TODO +++ b/drivers/staging/rtl8723bs/TODO @@ -6,6 +6,3 @@ TODO: of them will require refactoring - merge Realtek's bugfixes and new features into the driver - switch to use MAC80211 - -Please send any patches to Greg Kroah-Hartman , -Hans de Goede and Larry Finger . diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 84ce7307d8f3d..64ce33c6fba17 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -382,7 +382,7 @@ int rtw_cmd_thread(void *context) struct cmd_priv *pcmdpriv = &padapter->cmdpriv; struct drvextra_cmd_parm *extra_parm = NULL; - thread_enter("RTW_CMD_THREAD"); + allow_signal(SIGTERM); pcmdbuf = pcmdpriv->cmd_buf; @@ -1258,8 +1258,7 @@ static void dynamic_chk_wk_hdl(struct adapter *padapter) /* always call rtw_ps_processor() at last one. */ - if (is_primary_adapter(padapter)) - rtw_ps_processor(padapter); + rtw_ps_processor(padapter); } void lps_ctrl_wk_hdl(struct adapter *padapter, u8 lps_ctrl_type); @@ -1465,6 +1464,7 @@ u8 rtw_ps_cmd(struct adapter *padapter) struct drvextra_cmd_parm *pdrvextra_cmd_parm; struct cmd_priv *pcmdpriv = &padapter->cmdpriv; u8 res = _SUCCESS; + ppscmd = rtw_zmalloc(sizeof(struct cmd_obj)); if (!ppscmd) { res = _FAIL; diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 8b671f8a79659..7a74b011dedc2 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -77,33 +77,7 @@ struct adapter *padapter, u8 bWrite, u8 PwrState) { - padapter->HalFunc.EfusePowerSwitch(padapter, bWrite, PwrState); -} - -/*----------------------------------------------------------------------------- - * Function: Efuse_GetCurrentSize - * - * Overview: Get current efuse size!!! - * - * Input: NONE - * - * Output: NONE - * - * Return: NONE - * - * Revised History: - * When Who Remark - * 11/16/2008 MHC Create Version 0. - * - *---------------------------------------------------------------------------*/ -u16 -Efuse_GetCurrentSize( - struct adapter *padapter, - u8 efuseType, - bool bPseudoTest) -{ - return padapter->HalFunc.EfuseGetCurrentSize(padapter, efuseType, - bPseudoTest); + Hal_EfusePowerSwitch(padapter, bWrite, PwrState); } /* 11/16/2008 MH Add description. Get current efuse area enabled word!!. */ @@ -159,7 +133,7 @@ efuse_ReadEFuse( bool bPseudoTest ) { - Adapter->HalFunc.ReadEFuse(Adapter, efuseType, _offset, _size_byte, pbuf, bPseudoTest); + Hal_ReadEFuse(Adapter, efuseType, _offset, _size_byte, pbuf, bPseudoTest); } void @@ -171,7 +145,7 @@ EFUSE_GetEfuseDefinition( bool bPseudoTest ) { - padapter->HalFunc.EFUSEGetEfuseDefinition(padapter, efuseType, type, pOut, bPseudoTest); + Hal_GetEfuseDefinition(padapter, efuseType, type, pOut, bPseudoTest); } /*----------------------------------------------------------------------------- @@ -312,68 +286,6 @@ u8 efuse_OneByteWrite(struct adapter *padapter, u16 addr, u8 data, bool bPseudoT return bResult; } -int -Efuse_PgPacketRead(struct adapter *padapter, - u8 offset, - u8 *data, - bool bPseudoTest) -{ - return padapter->HalFunc.Efuse_PgPacketRead(padapter, offset, data, - bPseudoTest); -} - -int -Efuse_PgPacketWrite(struct adapter *padapter, - u8 offset, - u8 word_en, - u8 *data, - bool bPseudoTest) -{ - return padapter->HalFunc.Efuse_PgPacketWrite(padapter, offset, word_en, - data, bPseudoTest); -} - -/*----------------------------------------------------------------------------- - * Function: efuse_WordEnableDataRead - * - * Overview: Read allowed word in current efuse section data. - * - * Input: NONE - * - * Output: NONE - * - * Return: NONE - * - * Revised History: - * When Who Remark - * 11/16/2008 MHC Create Version 0. - * 11/21/2008 MHC Fix Write bug when we only enable late word. - * - *---------------------------------------------------------------------------*/ -void -efuse_WordEnableDataRead(u8 word_en, - u8 *sourdata, - u8 *targetdata) -{ - if (!(word_en & BIT(0))) { - targetdata[0] = sourdata[0]; - targetdata[1] = sourdata[1]; - } - if (!(word_en & BIT(1))) { - targetdata[2] = sourdata[2]; - targetdata[3] = sourdata[3]; - } - if (!(word_en & BIT(2))) { - targetdata[4] = sourdata[4]; - targetdata[5] = sourdata[5]; - } - if (!(word_en & BIT(3))) { - targetdata[6] = sourdata[6]; - targetdata[7] = sourdata[7]; - } -} - - u8 Efuse_WordEnableDataWrite(struct adapter *padapter, u16 efuse_addr, diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index cbdb134278d37..5ded183aa08c4 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -911,8 +911,7 @@ inline void rtw_indicate_scan_done(struct adapter *padapter, bool aborted) { rtw_os_indicate_scan_done(padapter, aborted); - if (is_primary_adapter(padapter) && - (!adapter_to_pwrctl(padapter)->bInSuspend) && + if ((!adapter_to_pwrctl(padapter)->bInSuspend) && (!check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE|WIFI_UNDER_LINKING))) { rtw_set_ips_deny(padapter, 0); @@ -1589,8 +1588,7 @@ void rtw_dynamic_check_timer_handler(struct adapter *adapter) } } else { - if (is_primary_adapter(adapter)) - rtw_dynamic_chk_wk_cmd(adapter); + rtw_dynamic_chk_wk_cmd(adapter); } /* auto site survey */ diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 4d4bec47d1874..317f3db193971 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -1870,10 +1870,10 @@ unsigned int OnAction_sa_query(struct adapter *padapter, union recv_frame *precv if (0) { int pp; - printk("pattrib->pktlen = %d =>", pattrib->pkt_len); + netdev_dbg(padapter->pnetdev, "pattrib->pktlen = %d =>", pattrib->pkt_len); for (pp = 0; pp < pattrib->pkt_len; pp++) - printk(" %02x ", pframe[pp]); - printk("\n"); + pr_cont(" %02x ", pframe[pp]); + pr_cont("\n"); } return _SUCCESS; @@ -4872,8 +4872,7 @@ void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res) /* set_link_timer(pmlmeext, DISCONNECT_TO); */ } - if (get_iface_type(padapter) == IFACE_PORT0) - rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_CONNECT, 0); + rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_CONNECT, 0); } /* currently only adhoc mode will go here */ @@ -5638,7 +5637,7 @@ u8 setkey_hdl(struct adapter *padapter, u8 *pbuf) rtw_hal_set_hwreg(padapter, HW_VAR_SEC_DK_CFG, (u8 *)true); /* allow multicast packets to driver */ - padapter->HalFunc.SetHwRegHandler(padapter, HW_VAR_ON_RCR_AM, null_addr); + SetHwReg8723BS(padapter, HW_VAR_ON_RCR_AM, null_addr); return H2C_SUCCESS; } diff --git a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c index dbfcbac3d8555..c60e179bb2e19 100644 --- a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c +++ b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c @@ -73,9 +73,6 @@ int ips_leave(struct adapter *padapter) struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter); int ret; - if (!is_primary_adapter(padapter)) - return _SUCCESS; - mutex_lock(&pwrpriv->lock); ret = _ips_leave(padapter); mutex_unlock(&pwrpriv->lock); @@ -455,10 +452,6 @@ void LPS_Enter(struct adapter *padapter, const char *msg) if (n_assoc_iface != 1) return; - /* Skip lps enter request for adapter not port0 */ - if (get_iface_type(padapter) != IFACE_PORT0) - return; - if (!PS_RDY_CHECK(dvobj->padapters)) return; diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index f37fec1efaf91..73c70b016f009 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -290,21 +290,11 @@ inline void rtw_set_oper_ch(struct adapter *adapter, u8 ch) dvobj->oper_channel = ch; } -inline u8 rtw_get_oper_bw(struct adapter *adapter) -{ - return adapter_to_dvobj(adapter)->oper_bwmode; -} - inline void rtw_set_oper_bw(struct adapter *adapter, u8 bw) { adapter_to_dvobj(adapter)->oper_bwmode = bw; } -inline u8 rtw_get_oper_choffset(struct adapter *adapter) -{ - return adapter_to_dvobj(adapter)->oper_ch_offset; -} - inline void rtw_set_oper_choffset(struct adapter *adapter, u8 offset) { adapter_to_dvobj(adapter)->oper_ch_offset = offset; @@ -445,34 +435,6 @@ void invalidate_cam_all(struct adapter *padapter) spin_unlock_bh(&cam_ctl->lock); } -static u32 _ReadCAM(struct adapter *padapter, u32 addr) -{ - u32 count = 0, cmd; - - cmd = CAM_POLLINIG | addr; - rtw_write32(padapter, RWCAM, cmd); - - do { - if (0 == (rtw_read32(padapter, REG_CAMCMD) & CAM_POLLINIG)) - break; - } while (count++ < 100); - - return rtw_read32(padapter, REG_CAMREAD); -} - -void read_cam(struct adapter *padapter, u8 entry, u8 *get_key) -{ - u32 j, addr, cmd; - - addr = entry << 3; - - for (j = 0; j < 6; j++) { - cmd = _ReadCAM(padapter, addr+j); - if (j > 1) /* get key from cam */ - memcpy(get_key+(j-2)*4, &cmd, 4); - } -} - void _write_cam(struct adapter *padapter, u8 entry, u16 ctrl, u8 *mac, u8 *key) { unsigned int i, val, addr; @@ -1613,9 +1575,9 @@ void update_wireless_mode(struct adapter *padapter) SIFS_Timer = 0x0a0a0808; /* 0x0808 -> for CCK, 0x0a0a -> for OFDM */ /* change this value if having IOT issues. */ - padapter->HalFunc.SetHwRegHandler(padapter, HW_VAR_RESP_SIFS, (u8 *)&SIFS_Timer); + SetHwReg8723BS(padapter, HW_VAR_RESP_SIFS, (u8 *)&SIFS_Timer); - padapter->HalFunc.SetHwRegHandler(padapter, HW_VAR_WIRELESS_MODE, (u8 *)&(pmlmeext->cur_wireless_mode)); + SetHwReg8723BS(padapter, HW_VAR_WIRELESS_MODE, (u8 *)&(pmlmeext->cur_wireless_mode)); if (pmlmeext->cur_wireless_mode & WIRELESS_11B) update_mgnt_tx_rate(padapter, IEEE80211_CCK_RATE_1MB); @@ -1822,29 +1784,3 @@ void rtw_release_macid(struct adapter *padapter, struct sta_info *psta) } spin_unlock_bh(&pdvobj->lock); } - -/* For 8188E RA */ -u8 rtw_search_max_mac_id(struct adapter *padapter) -{ - u8 max_mac_id = 0; - struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter); - int i; - - spin_lock_bh(&pdvobj->lock); - for (i = (NUM_STA-1); i >= 0 ; i--) { - if (pdvobj->macid[i] == true) - break; - } - max_mac_id = i; - spin_unlock_bh(&pdvobj->lock); - - return max_mac_id; -} - -struct adapter *dvobj_get_port0_adapter(struct dvobj_priv *dvobj) -{ - if (get_iface_type(dvobj->padapters[i]) != IFACE_PORT0) - return NULL; - - return dvobj->padapters; -} diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 3e88f14e3bf78..699cff7b0ac95 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -2489,7 +2489,7 @@ int rtw_xmit_thread(void *context) err = _SUCCESS; padapter = context; - thread_enter("RTW_XMIT_THREAD"); + allow_signal(SIGTERM); do { err = rtw_hal_xmit_thread_handler(padapter); diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 719dd116d8076..95fb38283c582 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -13,23 +13,20 @@ u8 rtw_hal_data_init(struct adapter *padapter) { - if (is_primary_adapter(padapter)) { /* if (padapter->isprimary) */ - padapter->hal_data_sz = sizeof(struct hal_com_data); - padapter->HalData = vzalloc(padapter->hal_data_sz); - if (!padapter->HalData) - return _FAIL; - } + padapter->hal_data_sz = sizeof(struct hal_com_data); + padapter->HalData = vzalloc(padapter->hal_data_sz); + if (!padapter->HalData) + return _FAIL; + return _SUCCESS; } void rtw_hal_data_deinit(struct adapter *padapter) { - if (is_primary_adapter(padapter)) { /* if (padapter->isprimary) */ - if (padapter->HalData) { - vfree(padapter->HalData); - padapter->HalData = NULL; - padapter->hal_data_sz = 0; - } + if (padapter->HalData) { + vfree(padapter->HalData); + padapter->HalData = NULL; + padapter->hal_data_sz = 0; } } @@ -796,19 +793,6 @@ u8 GetHalDefVar( return bResult; } -void GetHalODMVar( - struct adapter *Adapter, - enum hal_odm_variable eVariable, - void *pValue1, - void *pValue2 -) -{ - switch (eVariable) { - default: - break; - } -} - void SetHalODMVar( struct adapter *Adapter, enum hal_odm_variable eVariable, @@ -883,7 +867,6 @@ void rtw_hal_check_rxfifo_full(struct adapter *adapter) int save_cnt = false; /* switch counter to RX fifo */ - /* printk("8723b or 8192e , MAC_667 set 0xf0\n"); */ rtw_write8(adapter, REG_RXERR_RPT+3, rtw_read8(adapter, REG_RXERR_RPT+3)|0xf0); save_cnt = true; /* todo: other chips */ @@ -910,10 +893,9 @@ void rtw_dump_raw_rssi_info(struct adapter *padapter) for (rf_path = 0; rf_path < pHalData->NumTotalRFPath; rf_path++) { if (!isCCKrate) { - printk(", rx_ofdm_pwr:%d(dBm), rx_ofdm_snr:%d(dB)\n", - psample_pkt_rssi->ofdm_pwr[rf_path], psample_pkt_rssi->ofdm_snr[rf_path]); - } else { - printk("\n"); + netdev_dbg(padapter->pnetdev, ", rx_ofdm_pwr:%d(dBm), rx_ofdm_snr:%d(dB)\n", + psample_pkt_rssi->ofdm_pwr[rf_path], + psample_pkt_rssi->ofdm_snr[rf_path]); } } } diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 0a3900548fd2c..0db8f623b8059 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -9,52 +9,37 @@ void rtw_hal_chip_configure(struct adapter *padapter) { - if (padapter->HalFunc.intf_chip_configure) - padapter->HalFunc.intf_chip_configure(padapter); + rtl8723bs_interface_configure(padapter); } void rtw_hal_read_chip_info(struct adapter *padapter) { - if (padapter->HalFunc.read_adapter_info) - padapter->HalFunc.read_adapter_info(padapter); + ReadAdapterInfo8723BS(padapter); } void rtw_hal_read_chip_version(struct adapter *padapter) { - if (padapter->HalFunc.read_chip_version) - padapter->HalFunc.read_chip_version(padapter); + rtl8723b_read_chip_version(padapter); } void rtw_hal_def_value_init(struct adapter *padapter) { - if (is_primary_adapter(padapter)) - if (padapter->HalFunc.init_default_value) - padapter->HalFunc.init_default_value(padapter); + rtl8723bs_init_default_value(padapter); } void rtw_hal_free_data(struct adapter *padapter) { /* free HAL Data */ rtw_hal_data_deinit(padapter); - - if (is_primary_adapter(padapter)) - if (padapter->HalFunc.free_hal_data) - padapter->HalFunc.free_hal_data(padapter); } void rtw_hal_dm_init(struct adapter *padapter) { - if (is_primary_adapter(padapter)) - if (padapter->HalFunc.dm_init) - padapter->HalFunc.dm_init(padapter); + rtl8723b_init_dm_priv(padapter); } void rtw_hal_dm_deinit(struct adapter *padapter) { - /* cancel dm timer */ - if (is_primary_adapter(padapter)) - if (padapter->HalFunc.dm_deinit) - padapter->HalFunc.dm_deinit(padapter); } static void rtw_hal_init_opmode(struct adapter *padapter) @@ -82,7 +67,7 @@ uint rtw_hal_init(struct adapter *padapter) uint status; struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); - status = padapter->HalFunc.hal_init(padapter); + status = rtl8723bs_hal_init(padapter); if (status == _SUCCESS) { rtw_hal_init_opmode(padapter); @@ -111,7 +96,7 @@ uint rtw_hal_deinit(struct adapter *padapter) uint status = _SUCCESS; struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); - status = padapter->HalFunc.hal_deinit(padapter); + status = rtl8723bs_hal_deinit(padapter); if (status == _SUCCESS) { padapter = dvobj->padapters; @@ -123,34 +108,27 @@ uint rtw_hal_deinit(struct adapter *padapter) void rtw_hal_set_hwreg(struct adapter *padapter, u8 variable, u8 *val) { - if (padapter->HalFunc.SetHwRegHandler) - padapter->HalFunc.SetHwRegHandler(padapter, variable, val); + SetHwReg8723BS(padapter, variable, val); } void rtw_hal_get_hwreg(struct adapter *padapter, u8 variable, u8 *val) { - if (padapter->HalFunc.GetHwRegHandler) - padapter->HalFunc.GetHwRegHandler(padapter, variable, val); + GetHwReg8723BS(padapter, variable, val); } void rtw_hal_set_hwreg_with_buf(struct adapter *padapter, u8 variable, u8 *pbuf, int len) { - if (padapter->HalFunc.SetHwRegHandlerWithBuf) - padapter->HalFunc.SetHwRegHandlerWithBuf(padapter, variable, pbuf, len); + SetHwRegWithBuf8723B(padapter, variable, pbuf, len); } u8 rtw_hal_set_def_var(struct adapter *padapter, enum hal_def_variable eVariable, void *pValue) { - if (padapter->HalFunc.SetHalDefVarHandler) - return padapter->HalFunc.SetHalDefVarHandler(padapter, eVariable, pValue); - return _FAIL; + return SetHalDefVar8723BSDIO(padapter, eVariable, pValue); } u8 rtw_hal_get_def_var(struct adapter *padapter, enum hal_def_variable eVariable, void *pValue) { - if (padapter->HalFunc.GetHalDefVarHandler) - return padapter->HalFunc.GetHalDefVarHandler(padapter, eVariable, pValue); - return _FAIL; + return GetHalDefVar8723BSDIO(padapter, eVariable, pValue); } void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariable, void *pValue1, bool bSet) @@ -161,40 +139,27 @@ void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariab void rtw_hal_enable_interrupt(struct adapter *padapter) { - if (padapter->HalFunc.enable_interrupt) - padapter->HalFunc.enable_interrupt(padapter); + EnableInterrupt8723BSdio(padapter); } void rtw_hal_disable_interrupt(struct adapter *padapter) { - if (padapter->HalFunc.disable_interrupt) - padapter->HalFunc.disable_interrupt(padapter); + DisableInterrupt8723BSdio(padapter); } u8 rtw_hal_check_ips_status(struct adapter *padapter) { - u8 val = false; - - if (padapter->HalFunc.check_ips_status) - val = padapter->HalFunc.check_ips_status(padapter); - - return val; + return CheckIPSStatus(padapter); } s32 rtw_hal_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) { - if (padapter->HalFunc.hal_xmitframe_enqueue) - return padapter->HalFunc.hal_xmitframe_enqueue(padapter, pxmitframe); - - return false; + return rtl8723bs_hal_xmitframe_enqueue(padapter, pxmitframe); } s32 rtw_hal_xmit(struct adapter *padapter, struct xmit_frame *pxmitframe) { - if (padapter->HalFunc.hal_xmit) - return padapter->HalFunc.hal_xmit(padapter, pxmitframe); - - return false; + return rtl8723bs_hal_xmit(padapter, pxmitframe); } /* @@ -202,8 +167,6 @@ s32 rtw_hal_xmit(struct adapter *padapter, struct xmit_frame *pxmitframe) */ s32 rtw_hal_mgnt_xmit(struct adapter *padapter, struct xmit_frame *pmgntframe) { - s32 ret = _FAIL; - update_mgntframe_attrib_addr(padapter, pmgntframe); /* pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; */ /* pwlanhdr = (struct rtw_ieee80211_hdr *)pframe; */ @@ -220,36 +183,27 @@ s32 rtw_hal_mgnt_xmit(struct adapter *padapter, struct xmit_frame *pmgntframe) rtw_mgmt_xmitframe_coalesce(padapter, pmgntframe->pkt, pmgntframe); } - if (padapter->HalFunc.mgnt_xmit) - ret = padapter->HalFunc.mgnt_xmit(padapter, pmgntframe); - return ret; + return rtl8723bs_mgnt_xmit(padapter, pmgntframe); } s32 rtw_hal_init_xmit_priv(struct adapter *padapter) { - if (padapter->HalFunc.init_xmit_priv) - return padapter->HalFunc.init_xmit_priv(padapter); - return _FAIL; + return rtl8723bs_init_xmit_priv(padapter); } void rtw_hal_free_xmit_priv(struct adapter *padapter) { - if (padapter->HalFunc.free_xmit_priv) - padapter->HalFunc.free_xmit_priv(padapter); + rtl8723bs_free_xmit_priv(padapter); } s32 rtw_hal_init_recv_priv(struct adapter *padapter) { - if (padapter->HalFunc.init_recv_priv) - return padapter->HalFunc.init_recv_priv(padapter); - - return _FAIL; + return rtl8723bs_init_recv_priv(padapter); } void rtw_hal_free_recv_priv(struct adapter *padapter) { - if (padapter->HalFunc.free_recv_priv) - padapter->HalFunc.free_recv_priv(padapter); + rtl8723bs_free_recv_priv(padapter); } void rtw_hal_update_ra_mask(struct sta_info *psta, u8 rssi_level) @@ -267,91 +221,70 @@ void rtw_hal_update_ra_mask(struct sta_info *psta, u8 rssi_level) if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) add_RATid(padapter, psta, rssi_level); else { - if (padapter->HalFunc.UpdateRAMaskHandler) - padapter->HalFunc.UpdateRAMaskHandler(padapter, psta->mac_id, rssi_level); + UpdateHalRAMask8723B(padapter, psta->mac_id, rssi_level); } } void rtw_hal_add_ra_tid(struct adapter *padapter, u32 bitmap, u8 *arg, u8 rssi_level) { - if (padapter->HalFunc.Add_RateATid) - padapter->HalFunc.Add_RateATid(padapter, bitmap, arg, rssi_level); + rtl8723b_Add_RateATid(padapter, bitmap, arg, rssi_level); } /*Start specifical interface thread */ void rtw_hal_start_thread(struct adapter *padapter) { - if (padapter->HalFunc.run_thread) - padapter->HalFunc.run_thread(padapter); + rtl8723b_start_thread(padapter); } /*Start specifical interface thread */ void rtw_hal_stop_thread(struct adapter *padapter) { - if (padapter->HalFunc.cancel_thread) - padapter->HalFunc.cancel_thread(padapter); + rtl8723b_stop_thread(padapter); } u32 rtw_hal_read_bbreg(struct adapter *padapter, u32 RegAddr, u32 BitMask) { - u32 data = 0; - - if (padapter->HalFunc.read_bbreg) - data = padapter->HalFunc.read_bbreg(padapter, RegAddr, BitMask); - return data; + return PHY_QueryBBReg_8723B(padapter, RegAddr, BitMask); } void rtw_hal_write_bbreg(struct adapter *padapter, u32 RegAddr, u32 BitMask, u32 Data) { - if (padapter->HalFunc.write_bbreg) - padapter->HalFunc.write_bbreg(padapter, RegAddr, BitMask, Data); + PHY_SetBBReg_8723B(padapter, RegAddr, BitMask, Data); } u32 rtw_hal_read_rfreg(struct adapter *padapter, u32 eRFPath, u32 RegAddr, u32 BitMask) { - u32 data = 0; - - if (padapter->HalFunc.read_rfreg) - data = padapter->HalFunc.read_rfreg(padapter, eRFPath, RegAddr, BitMask); - return data; + return PHY_QueryRFReg_8723B(padapter, eRFPath, RegAddr, BitMask); } void rtw_hal_write_rfreg(struct adapter *padapter, u32 eRFPath, u32 RegAddr, u32 BitMask, u32 Data) { - if (padapter->HalFunc.write_rfreg) - padapter->HalFunc.write_rfreg(padapter, eRFPath, RegAddr, BitMask, Data); + PHY_SetRFReg_8723B(padapter, eRFPath, RegAddr, BitMask, Data); } void rtw_hal_set_chan(struct adapter *padapter, u8 channel) { - if (padapter->HalFunc.set_channel_handler) - padapter->HalFunc.set_channel_handler(padapter, channel); + PHY_SwChnl8723B(padapter, channel); } void rtw_hal_set_chnl_bw(struct adapter *padapter, u8 channel, enum channel_width Bandwidth, u8 Offset40, u8 Offset80) { - if (padapter->HalFunc.set_chnl_bw_handler) - padapter->HalFunc.set_chnl_bw_handler(padapter, channel, - Bandwidth, Offset40, - Offset80); + PHY_SetSwChnlBWMode8723B(padapter, channel, Bandwidth, Offset40, Offset80); } void rtw_hal_dm_watchdog(struct adapter *padapter) { - if (padapter->HalFunc.hal_dm_watchdog) - padapter->HalFunc.hal_dm_watchdog(padapter); + rtl8723b_HalDmWatchDog(padapter); } void rtw_hal_dm_watchdog_in_lps(struct adapter *padapter) { if (adapter_to_pwrctl(padapter)->fw_current_in_ps_mode) { - if (padapter->HalFunc.hal_dm_watchdog_in_lps) - padapter->HalFunc.hal_dm_watchdog_in_lps(padapter); /* this function caller is in interrupt context */ + rtl8723b_HalDmWatchDog_in_LPS(padapter); /* this function caller is in interrupt context */ } } void beacon_timing_control(struct adapter *padapter) { - if (padapter->HalFunc.SetBeaconRelatedRegistersHandler) - padapter->HalFunc.SetBeaconRelatedRegistersHandler(padapter); + rtl8723b_SetBeaconRelatedRegisters(padapter); } diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index ea3b4cd323603..8d6131f0ad47d 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -417,13 +417,11 @@ static void odm_RefreshRateAdaptiveMaskCE(struct dm_odm_t *pDM_Odm) u8 i; struct adapter *padapter = pDM_Odm->Adapter; - if (padapter->bDriverStopped) { + if (padapter->bDriverStopped) return; - } - if (!pDM_Odm->bUseRAMask) { + if (!pDM_Odm->bUseRAMask) return; - } for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) { PSTA_INFO_T pstat = pDM_Odm->pODM_StaInfo[i]; @@ -433,7 +431,6 @@ static void odm_RefreshRateAdaptiveMaskCE(struct dm_odm_t *pDM_Odm) continue; if (true == ODM_RAStateCheck(pDM_Odm, pstat->rssi_stat.UndecoratedSmoothedPWDB, false, &pstat->rssi_level)) { - /* printk("RSSI:%d, RSSI_LEVEL:%d\n", pstat->rssi_stat.UndecoratedSmoothedPWDB, pstat->rssi_level); */ rtw_hal_update_ra_mask(pstat, pstat->rssi_level); } @@ -461,9 +458,9 @@ static void odm_RefreshRateAdaptiveMaskCE(struct dm_odm_t *pDM_Odm) static void odm_RefreshRateAdaptiveMask(struct dm_odm_t *pDM_Odm) { - if (!(pDM_Odm->SupportAbility & ODM_BB_RA_MASK)) { + if (!(pDM_Odm->SupportAbility & ODM_BB_RA_MASK)) return; - } + odm_RefreshRateAdaptiveMaskCE(pDM_Odm); } @@ -512,7 +509,6 @@ bool ODM_RAStateCheck( RATRState = DM_RATR_STA_MIDDLE; else RATRState = DM_RATR_STA_LOW; - /* printk("==>%s, RATRState:0x%02x , RSSI:%d\n", __func__, RATRState, RSSI); */ if (*pRATRState != RATRState || bForceUpdate) { *pRATRState = RATRState; @@ -593,8 +589,6 @@ static void odm_RSSIMonitorCheckCE(struct dm_odm_t *pDM_Odm) } } - /* printk("%s ==> sta_cnt(%d)\n", __func__, sta_cnt); */ - for (i = 0; i < sta_cnt; i++) { if (PWDB_rssi[i] != (0)) { if (pHalData->fw_ractrl == true)/* Report every sta's RSSI to FW */ diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 37ebbbf408ecf..e15ec6452fd06 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -53,8 +53,6 @@ static int _BlockWrite(struct adapter *padapter, void *buffer, u32 buffSize) u8 *bufferPtr = buffer; u32 i = 0, offset = 0; -/* printk("====>%s %d\n", __func__, __LINE__); */ - /* 3 Phase #1 */ blockCount_p1 = buffSize / blockSize_p1; remainSize_p1 = buffSize % blockSize_p1; @@ -62,7 +60,8 @@ static int _BlockWrite(struct adapter *padapter, void *buffer, u32 buffSize) for (i = 0; i < blockCount_p1; i++) { ret = rtw_write32(padapter, (FW_8723B_START_ADDRESS + i * blockSize_p1), *((u32 *)(bufferPtr + i * blockSize_p1))); if (ret == _FAIL) { - printk("====>%s %d i:%d\n", __func__, __LINE__, i); + netdev_dbg(padapter->pnetdev, "write failed at %s %d, block:%d\n", + __func__, __LINE__, i); goto exit; } } @@ -85,7 +84,8 @@ static int _BlockWrite(struct adapter *padapter, void *buffer, u32 buffSize) ret = rtw_write8(padapter, (FW_8723B_START_ADDRESS + offset + i), *(bufferPtr + offset + i)); if (ret == _FAIL) { - printk("====>%s %d i:%d\n", __func__, __LINE__, i); + netdev_dbg(padapter->pnetdev, "write failed at %s %d, block:%d\n", + __func__, __LINE__, i); goto exit; } } @@ -127,7 +127,8 @@ static int _WriteFW(struct adapter *padapter, void *buffer, u32 size) ret = _PageWrite(padapter, page, bufferPtr+offset, MAX_DLFW_PAGE_SIZE); if (ret == _FAIL) { - printk("====>%s %d\n", __func__, __LINE__); + netdev_dbg(padapter->pnetdev, "page write failed at %s %d\n", + __func__, __LINE__); goto exit; } } @@ -138,7 +139,8 @@ static int _WriteFW(struct adapter *padapter, void *buffer, u32 size) ret = _PageWrite(padapter, page, bufferPtr+offset, remainSize); if (ret == _FAIL) { - printk("====>%s %d\n", __func__, __LINE__); + netdev_dbg(padapter->pnetdev, "remaining page write failed at %s %d\n", + __func__, __LINE__); goto exit; } } @@ -439,10 +441,6 @@ void rtl8723b_InitializeFirmwareVars(struct adapter *padapter) /* pHalData->H2CStopInsertQueue = false; */ } -static void rtl8723b_free_hal_data(struct adapter *padapter) -{ -} - /* */ /* Efuse related code */ /* */ @@ -492,7 +490,7 @@ static u8 hal_EfuseSwitchToBank( return bRet; } -static void Hal_GetEfuseDefinition( +void Hal_GetEfuseDefinition( struct adapter *padapter, u8 efuseType, u8 type, @@ -604,46 +602,7 @@ static void Hal_GetEfuseDefinition( #define EFUSE_ACCESS_ON_8723 0x69 /* For RTL8723 only. */ #define REG_EFUSE_ACCESS_8723 0x00CF /* Efuse access protection for RTL8723 */ -/* */ -static void Hal_BT_EfusePowerSwitch( - struct adapter *padapter, u8 bWrite, u8 PwrState -) -{ - u8 tempval; - if (PwrState) { - /* enable BT power cut */ - /* 0x6A[14] = 1 */ - tempval = rtw_read8(padapter, 0x6B); - tempval |= BIT(6); - rtw_write8(padapter, 0x6B, tempval); - - /* Attention!! Between 0x6A[14] and 0x6A[15] setting need 100us delay */ - /* So don't write 0x6A[14]= 1 and 0x6A[15]= 0 together! */ - msleep(1); - /* disable BT output isolation */ - /* 0x6A[15] = 0 */ - tempval = rtw_read8(padapter, 0x6B); - tempval &= ~BIT(7); - rtw_write8(padapter, 0x6B, tempval); - } else { - /* enable BT output isolation */ - /* 0x6A[15] = 1 */ - tempval = rtw_read8(padapter, 0x6B); - tempval |= BIT(7); - rtw_write8(padapter, 0x6B, tempval); - - /* Attention!! Between 0x6A[14] and 0x6A[15] setting need 100us delay */ - /* So don't write 0x6A[14]= 1 and 0x6A[15]= 0 together! */ - - /* disable BT power cut */ - /* 0x6A[14] = 1 */ - tempval = rtw_read8(padapter, 0x6B); - tempval &= ~BIT(6); - rtw_write8(padapter, 0x6B, tempval); - } - -} -static void Hal_EfusePowerSwitch( +void Hal_EfusePowerSwitch( struct adapter *padapter, u8 bWrite, u8 PwrState ) { @@ -928,7 +887,7 @@ static void hal_ReadEFuse_BT( kfree(efuseTbl); } -static void Hal_ReadEFuse( +void Hal_ReadEFuse( struct adapter *padapter, u8 efuseType, u16 _offset, @@ -1069,7 +1028,6 @@ static u16 hal_EfuseGetCurrentSize_BT(struct adapter *padapter, u8 bPseudoTest) /* only when bank is switched we have to reset the efuse_addr. */ if (bank != startBank) efuse_addr = 0; -#if 1 while (AVAILABLE_EFUSE_ADDR(efuse_addr)) { if (efuse_OneByteRead(padapter, efuse_addr, @@ -1098,33 +1056,6 @@ static u16 hal_EfuseGetCurrentSize_BT(struct adapter *padapter, u8 bPseudoTest) /* read next header */ efuse_addr += (word_cnts*2)+1; } -#else - while ( - bContinual && - efuse_OneByteRead(padapter, efuse_addr, &efuse_data, bPseudoTest) && - AVAILABLE_EFUSE_ADDR(efuse_addr) - ) { - if (efuse_data != 0xFF) { - if ((efuse_data&0x1F) == 0x0F) { /* extended header */ - efuse_addr++; - efuse_OneByteRead(padapter, efuse_addr, &efuse_data, bPseudoTest); - if ((efuse_data & 0x0F) == 0x0F) { - efuse_addr++; - continue; - } else { - hworden = efuse_data & 0x0F; - } - } else { - hworden = efuse_data & 0x0F; - } - word_cnts = Efuse_CalculateWordCnts(hworden); - /* read next header */ - efuse_addr = efuse_addr + (word_cnts*2)+1; - } else - bContinual = false; - } -#endif - /* Check if we need to check next bank efuse */ if (efuse_addr < retU2) @@ -1141,7 +1072,7 @@ static u16 hal_EfuseGetCurrentSize_BT(struct adapter *padapter, u8 bPseudoTest) return retU2; } -static u16 Hal_EfuseGetCurrentSize( +u16 Hal_EfuseGetCurrentSize( struct adapter *padapter, u8 efuseType, bool bPseudoTest ) { @@ -1220,403 +1151,6 @@ static u8 Hal_EfuseWordEnableDataWrite( return badworden; } -static s32 Hal_EfusePgPacketRead( - struct adapter *padapter, - u8 offset, - u8 *data, - bool bPseudoTest -) -{ - u8 efuse_data, word_cnts = 0; - u16 efuse_addr = 0; - u8 hoffset = 0, hworden = 0; - u8 i; - u8 max_section = 0; - s32 ret; - - - if (!data) - return false; - - EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAX_SECTION, &max_section, bPseudoTest); - if (offset > max_section) - return false; - - memset(data, 0xFF, PGPKT_DATA_SIZE); - ret = true; - - /* */ - /* Efuse has been pre-programmed dummy 5Bytes at the end of Efuse by CP. */ - /* Skip dummy parts to prevent unexpected data read from Efuse. */ - /* By pass right now. 2009.02.19. */ - /* */ - while (AVAILABLE_EFUSE_ADDR(efuse_addr)) { - if (efuse_OneByteRead(padapter, efuse_addr++, &efuse_data, bPseudoTest) == false) { - ret = false; - break; - } - - if (efuse_data == 0xFF) - break; - - if (EXT_HEADER(efuse_data)) { - hoffset = GET_HDR_OFFSET_2_0(efuse_data); - efuse_OneByteRead(padapter, efuse_addr++, &efuse_data, bPseudoTest); - if (ALL_WORDS_DISABLED(efuse_data)) - continue; - - hoffset |= ((efuse_data & 0xF0) >> 1); - hworden = efuse_data & 0x0F; - } else { - hoffset = (efuse_data>>4) & 0x0F; - hworden = efuse_data & 0x0F; - } - - if (hoffset == offset) { - for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { - /* Check word enable condition in the section */ - if (!(hworden & (0x01<= max_available) - return false; - - return true; -} - -static void hal_EfuseConstructPGPkt( - u8 offset, - u8 word_en, - u8 *pData, - struct pgpkt_struct *pTargetPkt -) -{ - memset(pTargetPkt->data, 0xFF, PGPKT_DATA_SIZE); - pTargetPkt->offset = offset; - pTargetPkt->word_en = word_en; - efuse_WordEnableDataRead(word_en, pData, pTargetPkt->data); - pTargetPkt->word_cnts = Efuse_CalculateWordCnts(pTargetPkt->word_en); -} - -static u8 hal_EfusePartialWriteCheck( - struct adapter *padapter, - u8 efuseType, - u16 *pAddr, - struct pgpkt_struct *pTargetPkt, - u8 bPseudoTest -) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(padapter); - struct efuse_hal *pEfuseHal = &pHalData->EfuseHal; - u8 bRet = false; - u16 startAddr = 0, efuse_max_available_len = 0, efuse_max = 0; - u8 efuse_data = 0; - - EFUSE_GetEfuseDefinition(padapter, efuseType, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, &efuse_max_available_len, bPseudoTest); - EFUSE_GetEfuseDefinition(padapter, efuseType, TYPE_EFUSE_CONTENT_LEN_BANK, &efuse_max, bPseudoTest); - - if (efuseType == EFUSE_WIFI) { - if (bPseudoTest) { -#ifdef HAL_EFUSE_MEMORY - startAddr = (u16)pEfuseHal->fakeEfuseUsedBytes; -#else - startAddr = (u16)fakeEfuseUsedBytes; -#endif - } else - rtw_hal_get_hwreg(padapter, HW_VAR_EFUSE_BYTES, (u8 *)&startAddr); - } else { - if (bPseudoTest) { -#ifdef HAL_EFUSE_MEMORY - startAddr = (u16)pEfuseHal->fakeBTEfuseUsedBytes; -#else - startAddr = (u16)fakeBTEfuseUsedBytes; -#endif - } else - rtw_hal_get_hwreg(padapter, HW_VAR_EFUSE_BT_BYTES, (u8 *)&startAddr); - } - startAddr %= efuse_max; - - while (1) { - if (startAddr >= efuse_max_available_len) { - bRet = false; - break; - } - - if (efuse_OneByteRead(padapter, startAddr, &efuse_data, bPseudoTest) && (efuse_data != 0xFF)) { -#if 1 - bRet = false; - break; -#else - if (EXT_HEADER(efuse_data)) { - cur_header = efuse_data; - startAddr++; - efuse_OneByteRead(padapter, startAddr, &efuse_data, bPseudoTest); - if (ALL_WORDS_DISABLED(efuse_data)) { - bRet = false; - break; - } else { - curPkt.offset = ((cur_header & 0xE0) >> 5) | ((efuse_data & 0xF0) >> 1); - curPkt.word_en = efuse_data & 0x0F; - } - } else { - cur_header = efuse_data; - curPkt.offset = (cur_header>>4) & 0x0F; - curPkt.word_en = cur_header & 0x0F; - } - - curPkt.word_cnts = Efuse_CalculateWordCnts(curPkt.word_en); - /* if same header is found but no data followed */ - /* write some part of data followed by the header. */ - if ( - (curPkt.offset == pTargetPkt->offset) && - (hal_EfuseCheckIfDatafollowed(padapter, curPkt.word_cnts, startAddr+1, bPseudoTest) == false) && - wordEnMatched(pTargetPkt, &curPkt, &matched_wden) == true - ) { - /* Here to write partial data */ - badworden = Efuse_WordEnableDataWrite(padapter, startAddr+1, matched_wden, pTargetPkt->data, bPseudoTest); - if (badworden != 0x0F) { - u32 PgWriteSuccess = 0; - /* if write fail on some words, write these bad words again */ - if (efuseType == EFUSE_WIFI) - PgWriteSuccess = Efuse_PgPacketWrite(padapter, pTargetPkt->offset, badworden, pTargetPkt->data, bPseudoTest); - else - PgWriteSuccess = Efuse_PgPacketWrite_BT(padapter, pTargetPkt->offset, badworden, pTargetPkt->data, bPseudoTest); - - if (!PgWriteSuccess) { - bRet = false; /* write fail, return */ - break; - } - } - /* partial write ok, update the target packet for later use */ - for (i = 0; i < 4; i++) { - if ((matched_wden & (0x1<word_en |= (0x1<word_cnts = Efuse_CalculateWordCnts(pTargetPkt->word_en); - } - /* read from next header */ - startAddr = startAddr + (curPkt.word_cnts*2) + 1; -#endif - } else { - /* not used header, 0xff */ - *pAddr = startAddr; - bRet = true; - break; - } - } - - return bRet; -} - -static u8 hal_EfusePgPacketWrite1ByteHeader( - struct adapter *padapter, - u8 efuseType, - u16 *pAddr, - struct pgpkt_struct *pTargetPkt, - u8 bPseudoTest -) -{ - u8 pg_header = 0, tmp_header = 0; - u16 efuse_addr = *pAddr; - u8 repeatcnt = 0; - - pg_header = ((pTargetPkt->offset << 4) & 0xf0) | pTargetPkt->word_en; - - do { - efuse_OneByteWrite(padapter, efuse_addr, pg_header, bPseudoTest); - efuse_OneByteRead(padapter, efuse_addr, &tmp_header, bPseudoTest); - if (tmp_header != 0xFF) - break; - if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_) - return false; - - } while (1); - - if (tmp_header != pg_header) - return false; - - *pAddr = efuse_addr; - - return true; -} - -static u8 hal_EfusePgPacketWrite2ByteHeader( - struct adapter *padapter, - u8 efuseType, - u16 *pAddr, - struct pgpkt_struct *pTargetPkt, - u8 bPseudoTest -) -{ - u16 efuse_addr, efuse_max_available_len = 0; - u8 pg_header = 0, tmp_header = 0; - u8 repeatcnt = 0; - - EFUSE_GetEfuseDefinition(padapter, efuseType, TYPE_AVAILABLE_EFUSE_BYTES_BANK, &efuse_max_available_len, bPseudoTest); - - efuse_addr = *pAddr; - if (efuse_addr >= efuse_max_available_len) - return false; - - pg_header = ((pTargetPkt->offset & 0x07) << 5) | 0x0F; - - do { - efuse_OneByteWrite(padapter, efuse_addr, pg_header, bPseudoTest); - efuse_OneByteRead(padapter, efuse_addr, &tmp_header, bPseudoTest); - if (tmp_header != 0xFF) - break; - if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_) - return false; - - } while (1); - - if (tmp_header != pg_header) - return false; - - /* to write ext_header */ - efuse_addr++; - pg_header = ((pTargetPkt->offset & 0x78) << 1) | pTargetPkt->word_en; - - do { - efuse_OneByteWrite(padapter, efuse_addr, pg_header, bPseudoTest); - efuse_OneByteRead(padapter, efuse_addr, &tmp_header, bPseudoTest); - if (tmp_header != 0xFF) - break; - if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_) - return false; - - } while (1); - - if (tmp_header != pg_header) /* offset PG fail */ - return false; - - *pAddr = efuse_addr; - - return true; -} - -static u8 hal_EfusePgPacketWriteHeader( - struct adapter *padapter, - u8 efuseType, - u16 *pAddr, - struct pgpkt_struct *pTargetPkt, - u8 bPseudoTest -) -{ - u8 bRet = false; - - if (pTargetPkt->offset >= EFUSE_MAX_SECTION_BASE) - bRet = hal_EfusePgPacketWrite2ByteHeader(padapter, efuseType, pAddr, pTargetPkt, bPseudoTest); - else - bRet = hal_EfusePgPacketWrite1ByteHeader(padapter, efuseType, pAddr, pTargetPkt, bPseudoTest); - - return bRet; -} - -static u8 hal_EfusePgPacketWriteData( - struct adapter *padapter, - u8 efuseType, - u16 *pAddr, - struct pgpkt_struct *pTargetPkt, - u8 bPseudoTest -) -{ - u16 efuse_addr; - u8 badworden; - - - efuse_addr = *pAddr; - badworden = Efuse_WordEnableDataWrite(padapter, efuse_addr+1, pTargetPkt->word_en, pTargetPkt->data, bPseudoTest); - if (badworden != 0x0F) - return false; - - return true; -} - -static s32 Hal_EfusePgPacketWrite( - struct adapter *padapter, - u8 offset, - u8 word_en, - u8 *pData, - bool bPseudoTest -) -{ - struct pgpkt_struct targetPkt; - u16 startAddr = 0; - u8 efuseType = EFUSE_WIFI; - - if (!hal_EfusePgCheckAvailableAddr(padapter, efuseType, bPseudoTest)) - return false; - - hal_EfuseConstructPGPkt(offset, word_en, pData, &targetPkt); - - if (!hal_EfusePartialWriteCheck(padapter, efuseType, &startAddr, &targetPkt, bPseudoTest)) - return false; - - if (!hal_EfusePgPacketWriteHeader(padapter, efuseType, &startAddr, &targetPkt, bPseudoTest)) - return false; - - if (!hal_EfusePgPacketWriteData(padapter, efuseType, &startAddr, &targetPkt, bPseudoTest)) - return false; - - return true; -} - -static bool Hal_EfusePgPacketWrite_BT( - struct adapter *padapter, - u8 offset, - u8 word_en, - u8 *pData, - bool bPseudoTest -) -{ - struct pgpkt_struct targetPkt; - u16 startAddr = 0; - u8 efuseType = EFUSE_BT; - - if (!hal_EfusePgCheckAvailableAddr(padapter, efuseType, bPseudoTest)) - return false; - - hal_EfuseConstructPGPkt(offset, word_en, pData, &targetPkt); - - if (!hal_EfusePartialWriteCheck(padapter, efuseType, &startAddr, &targetPkt, bPseudoTest)) - return false; - - if (!hal_EfusePgPacketWriteHeader(padapter, efuseType, &startAddr, &targetPkt, bPseudoTest)) - return false; - - if (!hal_EfusePgPacketWriteData(padapter, efuseType, &startAddr, &targetPkt, bPseudoTest)) - return false; - - return true; -} - static struct hal_version ReadChipVersion8723B(struct adapter *padapter) { u32 value32; @@ -1645,15 +1179,15 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) pHalData->MultiFunc |= ((value32 & BT_FUNC_EN) ? RT_MULTI_FUNC_BT : 0); pHalData->MultiFunc |= ((value32 & GPS_FUNC_EN) ? RT_MULTI_FUNC_GPS : 0); pHalData->PolarityCtl = ((value32 & WL_HWPDN_SL) ? RT_POLARITY_HIGH_ACT : RT_POLARITY_LOW_ACT); -#if 1 + dump_chip_info(ChipVersion); -#endif + pHalData->VersionID = ChipVersion; return ChipVersion; } -static void rtl8723b_read_chip_version(struct adapter *padapter) +void rtl8723b_read_chip_version(struct adapter *padapter) { ReadChipVersion8723B(padapter); } @@ -1747,7 +1281,7 @@ static void _BeaconFunctionEnable(struct adapter *padapter, u8 Enable, u8 Linked rtw_write8(padapter, REG_RD_CTRL+1, 0x6F); } -static void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) +void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) { u8 val8; u32 value32; @@ -1808,16 +1342,6 @@ static void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) rtw_write8(padapter, bcn_ctrl_reg, val8); } -static void rtl8723b_GetHalODMVar( - struct adapter *Adapter, - enum hal_odm_variable eVariable, - void *pValue1, - void *pValue2 -) -{ - GetHalODMVar(Adapter, eVariable, pValue1, pValue2); -} - static void rtl8723b_SetHalODMVar( struct adapter *Adapter, enum hal_odm_variable eVariable, @@ -1836,7 +1360,7 @@ static void hal_notch_filter_8723b(struct adapter *adapter, bool enable) rtw_write8(adapter, rOFDM0_RxDSP+1, rtw_read8(adapter, rOFDM0_RxDSP+1) & ~BIT1); } -static void UpdateHalRAMask8723B(struct adapter *padapter, u32 mac_id, u8 rssi_level) +void UpdateHalRAMask8723B(struct adapter *padapter, u32 mac_id, u8 rssi_level) { u32 mask, rate_bitmap; u8 shortGIrate = false; @@ -1876,49 +1400,9 @@ static void UpdateHalRAMask8723B(struct adapter *padapter, u32 mac_id, u8 rssi_l void rtl8723b_set_hal_ops(struct hal_ops *pHalFunc) { - pHalFunc->free_hal_data = &rtl8723b_free_hal_data; - - pHalFunc->dm_init = &rtl8723b_init_dm_priv; - - pHalFunc->read_chip_version = &rtl8723b_read_chip_version; - - pHalFunc->UpdateRAMaskHandler = &UpdateHalRAMask8723B; - - pHalFunc->set_bwmode_handler = &PHY_SetBWMode8723B; - pHalFunc->set_channel_handler = &PHY_SwChnl8723B; - pHalFunc->set_chnl_bw_handler = &PHY_SetSwChnlBWMode8723B; - - pHalFunc->set_tx_power_level_handler = &PHY_SetTxPowerLevel8723B; - pHalFunc->get_tx_power_level_handler = &PHY_GetTxPowerLevel8723B; - - pHalFunc->hal_dm_watchdog = &rtl8723b_HalDmWatchDog; - pHalFunc->hal_dm_watchdog_in_lps = &rtl8723b_HalDmWatchDog_in_LPS; - - - pHalFunc->SetBeaconRelatedRegistersHandler = &rtl8723b_SetBeaconRelatedRegisters; - - pHalFunc->Add_RateATid = &rtl8723b_Add_RateATid; - - pHalFunc->run_thread = &rtl8723b_start_thread; - pHalFunc->cancel_thread = &rtl8723b_stop_thread; - - pHalFunc->read_bbreg = &PHY_QueryBBReg_8723B; - pHalFunc->write_bbreg = &PHY_SetBBReg_8723B; - pHalFunc->read_rfreg = &PHY_QueryRFReg_8723B; - pHalFunc->write_rfreg = &PHY_SetRFReg_8723B; - /* Efuse related function */ - pHalFunc->BTEfusePowerSwitch = &Hal_BT_EfusePowerSwitch; - pHalFunc->EfusePowerSwitch = &Hal_EfusePowerSwitch; - pHalFunc->ReadEFuse = &Hal_ReadEFuse; - pHalFunc->EFUSEGetEfuseDefinition = &Hal_GetEfuseDefinition; - pHalFunc->EfuseGetCurrentSize = &Hal_EfuseGetCurrentSize; - pHalFunc->Efuse_PgPacketRead = &Hal_EfusePgPacketRead; - pHalFunc->Efuse_PgPacketWrite = &Hal_EfusePgPacketWrite; pHalFunc->Efuse_WordEnableDataWrite = &Hal_EfuseWordEnableDataWrite; - pHalFunc->Efuse_PgPacketWrite_BT = &Hal_EfusePgPacketWrite_BT; - pHalFunc->GetHalODMVarHandler = &rtl8723b_GetHalODMVar; pHalFunc->SetHalODMVarHandler = &rtl8723b_SetHalODMVar; pHalFunc->xmit_thread_handler = &hal_xmit_handler; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 4ff092b7c9c99..d8709d40cb33f 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -575,10 +575,6 @@ void PHY_SetTxPowerLevel8723B(struct adapter *Adapter, u8 Channel) PHY_SetTxPowerLevelByPath(Adapter, Channel, RFPath); } -void PHY_GetTxPowerLevel8723B(struct adapter *Adapter, s32 *powerlevel) -{ -} - static void phy_SetRegBW_8723B( struct adapter *Adapter, enum channel_width CurrentBW ) @@ -764,17 +760,6 @@ static void PHY_HandleSwChnlAndSetBW8723B( } } -void PHY_SetBWMode8723B( - struct adapter *Adapter, - enum channel_width Bandwidth, /* 20M or 40M */ - unsigned char Offset /* Upper, Lower, or Don't care */ -) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - - PHY_HandleSwChnlAndSetBW8723B(Adapter, false, true, pHalData->CurrentChannel, Bandwidth, Offset, Offset, pHalData->CurrentChannel); -} - /* Call after initialization */ void PHY_SwChnl8723B(struct adapter *Adapter, u8 channel) { diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index 78298e63edce6..5dc1c12fe03e5 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -411,14 +411,12 @@ int rtl8723bs_xmit_thread(void *context) s32 ret; struct adapter *padapter; struct xmit_priv *pxmitpriv; - u8 thread_name[20]; ret = _SUCCESS; padapter = context; pxmitpriv = &padapter->xmitpriv; - rtw_sprintf(thread_name, 20, "RTWHALXT-%s", ADPT_ARG(padapter)); - thread_enter(thread_name); + allow_signal(SIGTERM); do { ret = rtl8723bs_xmit_handler(padapter); diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index d3aae413fc0f9..af9a2b068796a 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -583,7 +583,7 @@ static bool HalDetectPwrDownMode(struct adapter *Adapter) return pHalData->pwrdown; } /* HalDetectPwrDownMode */ -static u32 rtl8723bs_hal_init(struct adapter *padapter) +u32 rtl8723bs_hal_init(struct adapter *padapter) { s32 ret; struct hal_com_data *pHalData; @@ -884,7 +884,7 @@ static void CardDisableRTL8723BSdio(struct adapter *padapter) HalPwrSeqCmdParsing(padapter, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, rtl8723B_card_disable_flow); } -static u32 rtl8723bs_hal_deinit(struct adapter *padapter) +u32 rtl8723bs_hal_deinit(struct adapter *padapter) { struct dvobj_priv *psdpriv = padapter->dvobj; struct debug_priv *pdbgpriv = &psdpriv->drv_dbg; @@ -937,17 +937,7 @@ static u32 rtl8723bs_hal_deinit(struct adapter *padapter) return _SUCCESS; } -static u32 rtl8723bs_inirp_init(struct adapter *padapter) -{ - return _SUCCESS; -} - -static u32 rtl8723bs_inirp_deinit(struct adapter *padapter) -{ - return _SUCCESS; -} - -static void rtl8723bs_init_default_value(struct adapter *padapter) +void rtl8723bs_init_default_value(struct adapter *padapter) { struct hal_com_data *pHalData; @@ -960,7 +950,7 @@ static void rtl8723bs_init_default_value(struct adapter *padapter) pHalData->SdioRxFIFOCnt = 0; } -static void rtl8723bs_interface_configure(struct adapter *padapter) +void rtl8723bs_interface_configure(struct adapter *padapter) { struct hal_com_data *pHalData = GET_HAL_DATA(padapter); struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); @@ -1142,7 +1132,7 @@ static s32 _ReadAdapterInfo8723BS(struct adapter *padapter) return _SUCCESS; } -static void ReadAdapterInfo8723BS(struct adapter *padapter) +void ReadAdapterInfo8723BS(struct adapter *padapter) { /* Read EEPROM size before call any EEPROM function */ padapter->EepromAddressSize = GetEEPROMSize8723B(padapter); @@ -1154,7 +1144,7 @@ static void ReadAdapterInfo8723BS(struct adapter *padapter) * If variable not handled here, * some variables will be processed in SetHwReg8723B() */ -static void SetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) +void SetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) { u8 val8; @@ -1195,7 +1185,7 @@ static void SetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) * If variable not handled here, * some variables will be processed in GetHwReg8723B() */ -static void GetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) +void GetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) { switch (variable) { case HW_VAR_CPWM: @@ -1214,7 +1204,7 @@ static void GetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) } } -static void SetHwRegWithBuf8723B(struct adapter *padapter, u8 variable, u8 *pbuf, int len) +void SetHwRegWithBuf8723B(struct adapter *padapter, u8 variable, u8 *pbuf, int len) { switch (variable) { case HW_VAR_C2H_HANDLE: @@ -1229,7 +1219,7 @@ static void SetHwRegWithBuf8723B(struct adapter *padapter, u8 variable, u8 *pbuf /* Description: */ /* Query setting of specified variable. */ /* */ -static u8 GetHalDefVar8723BSDIO( +u8 GetHalDefVar8723BSDIO( struct adapter *Adapter, enum hal_def_variable eVariable, void *pValue ) { @@ -1257,8 +1247,7 @@ static u8 GetHalDefVar8723BSDIO( /* Description: */ /* Change default setting of specified variable. */ /* */ -static u8 SetHalDefVar8723BSDIO(struct adapter *Adapter, - enum hal_def_variable eVariable, void *pValue) +u8 SetHalDefVar8723BSDIO(struct adapter *Adapter, enum hal_def_variable eVariable, void *pValue) { return SetHalDefVar8723B(Adapter, eVariable, pValue); } @@ -1269,32 +1258,4 @@ void rtl8723bs_set_hal_ops(struct adapter *padapter) rtl8723b_set_hal_ops(pHalFunc); - pHalFunc->hal_init = &rtl8723bs_hal_init; - pHalFunc->hal_deinit = &rtl8723bs_hal_deinit; - - pHalFunc->inirp_init = &rtl8723bs_inirp_init; - pHalFunc->inirp_deinit = &rtl8723bs_inirp_deinit; - - pHalFunc->init_xmit_priv = &rtl8723bs_init_xmit_priv; - pHalFunc->free_xmit_priv = &rtl8723bs_free_xmit_priv; - - pHalFunc->init_recv_priv = &rtl8723bs_init_recv_priv; - pHalFunc->free_recv_priv = &rtl8723bs_free_recv_priv; - - pHalFunc->init_default_value = &rtl8723bs_init_default_value; - pHalFunc->intf_chip_configure = &rtl8723bs_interface_configure; - pHalFunc->read_adapter_info = &ReadAdapterInfo8723BS; - - pHalFunc->enable_interrupt = &EnableInterrupt8723BSdio; - pHalFunc->disable_interrupt = &DisableInterrupt8723BSdio; - pHalFunc->check_ips_status = &CheckIPSStatus; - pHalFunc->SetHwRegHandler = &SetHwReg8723BS; - pHalFunc->GetHwRegHandler = &GetHwReg8723BS; - pHalFunc->SetHwRegHandlerWithBuf = &SetHwRegWithBuf8723B; - pHalFunc->GetHalDefVarHandler = &GetHalDefVar8723BSDIO; - pHalFunc->SetHalDefVarHandler = &SetHalDefVar8723BSDIO; - - pHalFunc->hal_xmit = &rtl8723bs_hal_xmit; - pHalFunc->mgnt_xmit = &rtl8723bs_mgnt_xmit; - pHalFunc->hal_xmitframe_enqueue = &rtl8723bs_hal_xmitframe_enqueue; } diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h index 0b35c97843cc7..7b0e824e05a9a 100644 --- a/drivers/staging/rtl8723bs/include/drv_types.h +++ b/drivers/staging/rtl8723bs/include/drv_types.h @@ -182,8 +182,6 @@ struct registry_priv { #include -#define is_primary_adapter(adapter) (1) -#define get_iface_type(adapter) (IFACE_PORT0) #define GET_PRIMARY_ADAPTER(padapter) (((struct adapter *)padapter)->dvobj->if1) #define GET_IFACE_NUMS(padapter) (((struct adapter *)padapter)->dvobj->iface_nums) #define GET_ADAPTER(padapter, iface_id) (((struct adapter *)padapter)->dvobj->padapters[iface_id]) @@ -320,14 +318,6 @@ static inline struct device *dvobj_to_dev(struct dvobj_priv *dvobj) return &dvobj->intf_data.func->dev; } -struct adapter *dvobj_get_port0_adapter(struct dvobj_priv *dvobj); - -enum { - IFACE_PORT0, /* mapping to port0 for C/D series chips */ - IFACE_PORT1, /* mapping to port1 for C/D series chip */ - MAX_IFACE_PORT, -}; - enum { DRIVER_NORMAL = 0, DRIVER_DISAPPEAR = 1, diff --git a/drivers/staging/rtl8723bs/include/hal_com.h b/drivers/staging/rtl8723bs/include/hal_com.h index 17d5cfb66a365..4db93484725f0 100644 --- a/drivers/staging/rtl8723bs/include/hal_com.h +++ b/drivers/staging/rtl8723bs/include/hal_com.h @@ -158,10 +158,6 @@ void rtw_dump_raw_rssi_info(struct adapter *padapter); void rtw_bb_rf_gain_offset(struct adapter *padapter); -void GetHalODMVar(struct adapter *Adapter, - enum hal_odm_variable eVariable, - void *pValue1, - void *pValue2); void SetHalODMVar( struct adapter *Adapter, enum hal_odm_variable eVariable, diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index efdd1f912b5d9..282e141616b0a 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -162,91 +162,9 @@ enum hal_intf_ps_func { typedef s32 (*c2h_id_filter)(u8 *c2h_evt); struct hal_ops { - u32 (*hal_power_on)(struct adapter *padapter); - void (*hal_power_off)(struct adapter *padapter); - u32 (*hal_init)(struct adapter *padapter); - u32 (*hal_deinit)(struct adapter *padapter); - - void (*free_hal_data)(struct adapter *padapter); - - u32 (*inirp_init)(struct adapter *padapter); - u32 (*inirp_deinit)(struct adapter *padapter); - void (*irp_reset)(struct adapter *padapter); - - s32 (*init_xmit_priv)(struct adapter *padapter); - void (*free_xmit_priv)(struct adapter *padapter); - - s32 (*init_recv_priv)(struct adapter *padapter); - void (*free_recv_priv)(struct adapter *padapter); - - void (*dm_init)(struct adapter *padapter); - void (*dm_deinit)(struct adapter *padapter); - void (*read_chip_version)(struct adapter *padapter); - - void (*init_default_value)(struct adapter *padapter); - - void (*intf_chip_configure)(struct adapter *padapter); - - void (*read_adapter_info)(struct adapter *padapter); - - void (*enable_interrupt)(struct adapter *padapter); - void (*disable_interrupt)(struct adapter *padapter); - u8 (*check_ips_status)(struct adapter *padapter); - s32 (*interrupt_handler)(struct adapter *padapter); - void (*clear_interrupt)(struct adapter *padapter); - void (*set_bwmode_handler)(struct adapter *padapter, enum channel_width Bandwidth, u8 Offset); - void (*set_channel_handler)(struct adapter *padapter, u8 channel); - void (*set_chnl_bw_handler)(struct adapter *padapter, u8 channel, enum channel_width Bandwidth, u8 Offset40, u8 Offset80); - - void (*set_tx_power_level_handler)(struct adapter *padapter, u8 channel); - void (*get_tx_power_level_handler)(struct adapter *padapter, s32 *powerlevel); - - void (*hal_dm_watchdog)(struct adapter *padapter); - void (*hal_dm_watchdog_in_lps)(struct adapter *padapter); - - - void (*SetHwRegHandler)(struct adapter *padapter, u8 variable, u8 *val); - void (*GetHwRegHandler)(struct adapter *padapter, u8 variable, u8 *val); - - void (*SetHwRegHandlerWithBuf)(struct adapter *padapter, u8 variable, u8 *pbuf, int len); - - u8 (*GetHalDefVarHandler)(struct adapter *padapter, enum hal_def_variable eVariable, void *pValue); - u8 (*SetHalDefVarHandler)(struct adapter *padapter, enum hal_def_variable eVariable, void *pValue); - - void (*GetHalODMVarHandler)(struct adapter *padapter, enum hal_odm_variable eVariable, void *pValue1, void *pValue2); void (*SetHalODMVarHandler)(struct adapter *padapter, enum hal_odm_variable eVariable, void *pValue1, bool bSet); - void (*UpdateRAMaskHandler)(struct adapter *padapter, u32 mac_id, u8 rssi_level); - void (*SetBeaconRelatedRegistersHandler)(struct adapter *padapter); - - void (*Add_RateATid)(struct adapter *padapter, u32 bitmap, u8 *arg, u8 rssi_level); - - void (*run_thread)(struct adapter *padapter); - void (*cancel_thread)(struct adapter *padapter); - - u8 (*interface_ps_func)(struct adapter *padapter, enum hal_intf_ps_func efunc_id, u8 *val); - - s32 (*hal_xmit)(struct adapter *padapter, struct xmit_frame *pxmitframe); - /* - * mgnt_xmit should be implemented to run in interrupt context - */ - s32 (*mgnt_xmit)(struct adapter *padapter, struct xmit_frame *pmgntframe); - s32 (*hal_xmitframe_enqueue)(struct adapter *padapter, struct xmit_frame *pxmitframe); - - u32 (*read_bbreg)(struct adapter *padapter, u32 RegAddr, u32 BitMask); - void (*write_bbreg)(struct adapter *padapter, u32 RegAddr, u32 BitMask, u32 Data); - u32 (*read_rfreg)(struct adapter *padapter, u8 eRFPath, u32 RegAddr, u32 BitMask); - void (*write_rfreg)(struct adapter *padapter, u8 eRFPath, u32 RegAddr, u32 BitMask, u32 Data); - - void (*EfusePowerSwitch)(struct adapter *padapter, u8 bWrite, u8 PwrState); - void (*BTEfusePowerSwitch)(struct adapter *padapter, u8 bWrite, u8 PwrState); - void (*ReadEFuse)(struct adapter *padapter, u8 efuseType, u16 _offset, u16 _size_byte, u8 *pbuf, bool bPseudoTest); - void (*EFUSEGetEfuseDefinition)(struct adapter *padapter, u8 efuseType, u8 type, void *pOut, bool bPseudoTest); - u16 (*EfuseGetCurrentSize)(struct adapter *padapter, u8 efuseType, bool bPseudoTest); - int (*Efuse_PgPacketRead)(struct adapter *padapter, u8 offset, u8 *data, bool bPseudoTest); - int (*Efuse_PgPacketWrite)(struct adapter *padapter, u8 offset, u8 word_en, u8 *data, bool bPseudoTest); u8 (*Efuse_WordEnableDataWrite)(struct adapter *padapter, u16 efuse_addr, u8 word_en, u8 *data, bool bPseudoTest); - bool (*Efuse_PgPacketWrite_BT)(struct adapter *padapter, u8 offset, u8 word_en, u8 *data, bool bPseudoTest); s32 (*xmit_thread_handler)(struct adapter *padapter); void (*hal_notch_filter)(struct adapter *adapter, bool enable); @@ -357,4 +275,17 @@ s32 rtw_hal_macid_wakeup(struct adapter *padapter, u32 macid); s32 rtw_hal_fill_h2c_cmd(struct adapter *, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer); +void SetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val); +void GetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val); +void SetHwRegWithBuf8723B(struct adapter *padapter, u8 variable, u8 *pbuf, int len); +u8 GetHalDefVar8723BSDIO(struct adapter *Adapter, enum hal_def_variable eVariable, void *pValue); +u8 SetHalDefVar8723BSDIO(struct adapter *Adapter, enum hal_def_variable eVariable, void *pValue); +void UpdateHalRAMask8723B(struct adapter *padapter, u32 mac_id, u8 rssi_level); +void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter); +void Hal_EfusePowerSwitch(struct adapter *padapter, u8 bWrite, u8 PwrState); +void Hal_ReadEFuse(struct adapter *padapter, u8 efuseType, u16 _offset, + u16 _size_byte, u8 *pbuf, bool bPseudoTest); +void Hal_GetEfuseDefinition(struct adapter *padapter, u8 efuseType, u8 type, + void *pOut, bool bPseudoTest); +u16 Hal_EfuseGetCurrentSize(struct adapter *padapter, u8 efuseType, bool bPseudoTest); #endif /* __HAL_INTF_H__ */ diff --git a/drivers/staging/rtl8723bs/include/hal_phy_cfg.h b/drivers/staging/rtl8723bs/include/hal_phy_cfg.h index ea494bcf830b8..07bf0a8d019aa 100644 --- a/drivers/staging/rtl8723bs/include/hal_phy_cfg.h +++ b/drivers/staging/rtl8723bs/include/hal_phy_cfg.h @@ -49,13 +49,8 @@ void PHY_SetTxPowerIndex(struct adapter *Adapter, u32 PowerIndex, u8 PHY_GetTxPowerIndex(struct adapter *padapter, u8 RFPath, u8 Rate, enum channel_width BandWidth, u8 Channel); -void PHY_GetTxPowerLevel8723B(struct adapter *Adapter, s32 *powerlevel); - void PHY_SetTxPowerLevel8723B(struct adapter *Adapter, u8 channel); -void PHY_SetBWMode8723B(struct adapter *Adapter, enum channel_width Bandwidth, - unsigned char Offset); - /* Call after initialization */ void PHY_SwChnl8723B(struct adapter *Adapter, u8 channel); diff --git a/drivers/staging/rtl8723bs/include/osdep_intf.h b/drivers/staging/rtl8723bs/include/osdep_intf.h index 111e0179712ac..215ece612f715 100644 --- a/drivers/staging/rtl8723bs/include/osdep_intf.h +++ b/drivers/staging/rtl8723bs/include/osdep_intf.h @@ -55,9 +55,6 @@ void rtw_unregister_netdevs(struct dvobj_priv *dvobj); u16 rtw_recv_select_queue(struct sk_buff *skb); -int rtw_ndev_notifier_register(void); -void rtw_ndev_notifier_unregister(void); - void rtw_ips_dev_unload(struct adapter *padapter); int rtw_ips_pwr_up(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index b21267d7ef720..8b1634f4091ec 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -73,11 +73,6 @@ int _rtw_netif_rx(struct net_device *ndev, struct sk_buff *skb); extern void _rtw_init_queue(struct __queue *pqueue); -static inline void thread_enter(char *name) -{ - allow_signal(SIGTERM); -} - static inline void flush_signals_thread(void) { if (signal_pending(current)) diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_recv.h b/drivers/staging/rtl8723bs/include/rtl8723b_recv.h index a108ce89bce43..783f64de0aec2 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_recv.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_recv.h @@ -87,4 +87,9 @@ void rtl8723bs_free_recv_priv(struct adapter *padapter); void rtl8723b_query_rx_phy_status(union recv_frame *prframe, struct phy_stat *pphy_stat); void rtl8723b_process_phy_info(struct adapter *padapter, void *prframe); +void rtl8723b_read_chip_version(struct adapter *padapter); +void rtl8723bs_init_default_value(struct adapter *padapter); +void rtl8723bs_interface_configure(struct adapter *padapter); +void ReadAdapterInfo8723BS(struct adapter *padapter); + #endif diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h b/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h index ad2542d0cabe2..ac4ca7e05b9bf 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h @@ -405,6 +405,8 @@ struct txdesc_8723b { void rtl8723b_update_txdesc(struct xmit_frame *pxmitframe, u8 *pmem); void rtl8723b_fill_fake_txdesc(struct adapter *padapter, u8 *pDesc, u32 BufferLen, u8 IsPsPoll, u8 IsBTQosNull, u8 bDataFrame); +u32 rtl8723bs_hal_init(struct adapter *padapter); +u32 rtl8723bs_hal_deinit(struct adapter *padapter); s32 rtl8723bs_init_xmit_priv(struct adapter *padapter); void rtl8723bs_free_xmit_priv(struct adapter *padapter); s32 rtl8723bs_hal_xmit(struct adapter *padapter, struct xmit_frame *pxmitframe); diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 0cb8c6f6d34db..d6ea8a4a856f1 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -90,16 +90,12 @@ extern u8 fakeBTEfuseInitMap[]; extern u8 fakeBTEfuseModifiedMap[]; /*------------------------Export global variable----------------------------*/ -u16 Efuse_GetCurrentSize(struct adapter *padapter, u8 efuseType, bool bPseudoTest); u8 Efuse_CalculateWordCnts(u8 word_en); void EFUSE_GetEfuseDefinition(struct adapter *padapter, u8 efuseType, u8 type, void *pOut, bool bPseudoTest); u8 efuse_OneByteRead(struct adapter *padapter, u16 addr, u8 *data, bool bPseudoTest); u8 efuse_OneByteWrite(struct adapter *padapter, u16 addr, u8 data, bool bPseudoTest); void Efuse_PowerSwitch(struct adapter *padapter, u8 bWrite, u8 PwrState); -int Efuse_PgPacketRead(struct adapter *padapter, u8 offset, u8 *data, bool bPseudoTest); -int Efuse_PgPacketWrite(struct adapter *padapter, u8 offset, u8 word_en, u8 *data, bool bPseudoTest); -void efuse_WordEnableDataRead(u8 word_en, u8 *sourdata, u8 *targetdata); u8 Efuse_WordEnableDataWrite(struct adapter *padapter, u16 efuse_addr, u8 word_en, u8 *data, bool bPseudoTest); u8 EFUSE_Read1Byte(struct adapter *padapter, u16 Address); diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index 8315399b64fdb..2080408743ef7 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -447,9 +447,7 @@ void Set_MSR(struct adapter *padapter, u8 type); u8 rtw_get_oper_ch(struct adapter *adapter); void rtw_set_oper_ch(struct adapter *adapter, u8 ch); -u8 rtw_get_oper_bw(struct adapter *adapter); void rtw_set_oper_bw(struct adapter *adapter, u8 bw); -u8 rtw_get_oper_choffset(struct adapter *adapter); void rtw_set_oper_choffset(struct adapter *adapter, u8 offset); u8 rtw_get_center_ch(u8 channel, u8 chnl_bw, u8 chnl_offset); unsigned long rtw_get_on_cur_ch_time(struct adapter *adapter); @@ -459,8 +457,6 @@ void r8723bs_select_channel(struct adapter *padapter, unsigned char channel); unsigned int decide_wait_for_beacon_timeout(unsigned int bcn_interval); -void read_cam(struct adapter *padapter, u8 entry, u8 *get_key); - /* modify HW only */ void _write_cam(struct adapter *padapter, u8 entry, u16 ctrl, u8 *mac, u8 *key); void _clear_cam_entry(struct adapter *padapter, u8 entry); @@ -528,7 +524,6 @@ void rtw_camid_free(struct adapter *adapter, u8 cam_id); extern void rtw_alloc_macid(struct adapter *padapter, struct sta_info *psta); extern void rtw_release_macid(struct adapter *padapter, struct sta_info *psta); -extern u8 rtw_search_max_mac_id(struct adapter *padapter); void report_join_res(struct adapter *padapter, int res); void report_survey_event(struct adapter *padapter, union recv_frame *precv_frame); diff --git a/drivers/staging/rtl8723bs/include/rtw_mp.h b/drivers/staging/rtl8723bs/include/rtw_mp.h index f94bb18479da5..5a1cbd2ed8515 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mp.h +++ b/drivers/staging/rtl8723bs/include/rtw_mp.h @@ -276,10 +276,6 @@ void _write_rfreg(struct adapter *padapter, u8 rfpath, u32 addr, u32 bitmask, u3 u32 read_macreg(struct adapter *padapter, u32 addr, u32 sz); void write_macreg(struct adapter *padapter, u32 addr, u32 val, u32 sz); -u32 read_bbreg(struct adapter *padapter, u32 addr, u32 bitmask); -void write_bbreg(struct adapter *padapter, u32 addr, u32 bitmask, u32 val); -u32 read_rfreg(struct adapter *padapter, u8 rfpath, u32 addr); -void write_rfreg(struct adapter *padapter, u8 rfpath, u32 addr, u32 val); void SetChannel(struct adapter *padapter); void SetBandwidth(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index b63a74e669bcf..c053ee9c1361c 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -581,7 +581,6 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ memcpy(txkey, &(param->u.crypt.key[16]), 8); memcpy(rxkey, &(param->u.crypt.key[24]), 8); @@ -626,7 +625,6 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { psta->dot118021XPrivacy = _TKIP_; - /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); @@ -657,7 +655,6 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ memcpy(txkey, &(param->u.crypt.key[16]), 8); memcpy(rxkey, &(param->u.crypt.key[24]), 8); @@ -785,7 +782,6 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ - /* DEBUG_ERR(("\nset key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len)); */ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); @@ -806,10 +802,6 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param } else if (strcmp(param->u.crypt.alg, "BIP") == 0) { /* save the IGTK key, length 16 bytes */ memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - /* - for (no = 0;no<16;no++) - printk(" %02x ", padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey[no]); - */ padapter->securitypriv.dot11wBIPKeyid = param->u.crypt.idx; padapter->securitypriv.binstallBIPkey = true; } @@ -817,9 +809,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param } pbcmc_sta = rtw_get_bcmc_stainfo(padapter); - if (!pbcmc_sta) { - /* DEBUG_ERR(("Set OID_802_11_ADD_KEY: bcmc stainfo is null\n")); */ - } else { + if (pbcmc_sta) { /* Jeff: don't disable ieee8021x_blocked while clearing key */ if (strcmp(param->u.crypt.alg, "none") != 0) pbcmc_sta->ieee8021x_blocked = false; diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c index a9e481e182ad6..793b051536f32 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c @@ -138,9 +138,7 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE) == true) { /* sta mode */ psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv)); - if (!psta) { - /* DEBUG_ERR(("Set wpa_set_encryption: Obtain Sta_info fail\n")); */ - } else { + if (psta) { /* Jeff: don't disable ieee8021x_blocked while clearing key */ if (strcmp(param->u.crypt.alg, "none") != 0) psta->ieee8021x_blocked = false; @@ -154,7 +152,6 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ - /* DEBUG_ERR(("\nset key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len)); */ memcpy(psta->dot11tkiptxmickey.skey, ¶m->u.crypt.key[16], 8); memcpy(psta->dot11tkiprxmickey.skey, ¶m->u.crypt.key[24], 8); @@ -177,13 +174,8 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1, true); } else if (strcmp(param->u.crypt.alg, "BIP") == 0) { - /* printk("BIP key_len =%d , index =%d @@@@@@@@@@@@@@@@@@\n", param->u.crypt.key_len, param->u.crypt.idx); */ /* save the IGTK key, length 16 bytes */ memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - /*printk("IGTK key below:\n"); - for (no = 0;no<16;no++) - printk(" %02x ", padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey[no]); - printk("\n");*/ padapter->securitypriv.dot11wBIPKeyid = param->u.crypt.idx; padapter->securitypriv.binstallBIPkey = true; } @@ -191,9 +183,7 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, } pbcmc_sta = rtw_get_bcmc_stainfo(padapter); - if (!pbcmc_sta) { - /* DEBUG_ERR(("Set OID_802_11_ADD_KEY: bcmc stainfo is null\n")); */ - } else { + if (pbcmc_sta) { /* Jeff: don't disable ieee8021x_blocked while clearing key */ if (strcmp(param->u.crypt.alg, "none") != 0) pbcmc_sta->ieee8021x_blocked = false; @@ -629,7 +619,6 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ memcpy(txkey, ¶m->u.crypt.key[16], 8); memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, ¶m->u.crypt.key[24], 8); @@ -674,7 +663,6 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { psta->dot118021XPrivacy = _TKIP_; - /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ memcpy(psta->dot11tkiptxmickey.skey, ¶m->u.crypt.key[16], 8); memcpy(psta->dot11tkiprxmickey.skey, ¶m->u.crypt.key[24], 8); @@ -703,7 +691,6 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ memcpy(txkey, ¶m->u.crypt.key[16], 8); memcpy(rxkey, ¶m->u.crypt.key[24], 8); diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index fc9b9c5efb50e..4e1917c054029 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -381,34 +381,6 @@ u16 rtw_recv_select_queue(struct sk_buff *skb) return rtw_1d_to_queue[priority]; } -static int rtw_ndev_notifier_call(struct notifier_block *nb, unsigned long state, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (dev->netdev_ops->ndo_do_ioctl != rtw_ioctl) - return NOTIFY_DONE; - - netdev_dbg(dev, FUNC_NDEV_FMT " state:%lu\n", FUNC_NDEV_ARG(dev), - state); - - return NOTIFY_DONE; -} - -static struct notifier_block rtw_ndev_notifier = { - .notifier_call = rtw_ndev_notifier_call, -}; - -int rtw_ndev_notifier_register(void) -{ - return register_netdevice_notifier(&rtw_ndev_notifier); -} - -void rtw_ndev_notifier_unregister(void) -{ - unregister_netdevice_notifier(&rtw_ndev_notifier); -} - - static int rtw_ndev_init(struct net_device *dev) { struct adapter *adapter = rtw_netdev_priv(dev); @@ -633,8 +605,7 @@ void rtw_reset_drv_sw(struct adapter *padapter) struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(padapter); /* hal_priv */ - if (is_primary_adapter(padapter)) - rtw_hal_def_value_init(padapter); + rtw_hal_def_value_init(padapter); RTW_ENABLE_FUNC(padapter, DF_RX_BIT); RTW_ENABLE_FUNC(padapter, DF_TX_BIT); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index d18fde4e5d6ce..5a7238e661ffb 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -72,7 +72,7 @@ static int sdio_alloc_irq(struct dvobj_priv *dvobj) err = sdio_claim_irq(func, &sd_sync_int_hdl); if (err) { dvobj->drv_dbg.dbg_sdio_alloc_irq_error_cnt++; - printk(KERN_CRIT "%s: sdio_claim_irq FAIL(%d)!\n", __func__, err); + netdev_crit(dvobj->if1->pnetdev, "%s: sdio_claim_irq FAIL(%d)!\n", __func__, err); } else { dvobj->drv_dbg.dbg_sdio_alloc_irq_cnt++; dvobj->irq_alloc = 1; @@ -382,7 +382,6 @@ static int rtw_drv_init( if (sdio_alloc_irq(dvobj) != _SUCCESS) goto free_if1; - rtw_ndev_notifier_register(); status = _SUCCESS; free_if1: @@ -484,22 +483,12 @@ static int rtw_sdio_resume(struct device *dev) static int __init rtw_drv_entry(void) { - int ret; - - ret = sdio_register_driver(&rtl8723bs_sdio_driver); - if (ret != 0) - rtw_ndev_notifier_unregister(); - - return ret; + return sdio_register_driver(&rtl8723bs_sdio_driver); } +module_init(rtw_drv_entry); static void __exit rtw_drv_halt(void) { sdio_unregister_driver(&rtl8723bs_sdio_driver); - - rtw_ndev_notifier_unregister(); } - - -module_init(rtw_drv_entry); module_exit(rtw_drv_halt); diff --git a/drivers/staging/rts5208/Kconfig b/drivers/staging/rts5208/Kconfig deleted file mode 100644 index b864023d3ccb5..0000000000000 --- a/drivers/staging/rts5208/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config RTS5208 - tristate "Realtek PCI-E Card Reader RTS5208/5288 support" - depends on PCI && SCSI - help - Say Y here to include driver code to support the Realtek - PCI-E card reader rts5208/rts5288. - - If this driver is compiled as a module, it will be named rts5208. diff --git a/drivers/staging/rts5208/Makefile b/drivers/staging/rts5208/Makefile deleted file mode 100644 index 3c9e9797d3d93..0000000000000 --- a/drivers/staging/rts5208/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_RTS5208) := rts5208.o - -rts5208-y := rtsx.o rtsx_chip.o rtsx_transport.o rtsx_scsi.o \ - rtsx_card.o general.o sd.o xd.o ms.o spi.o diff --git a/drivers/staging/rts5208/TODO b/drivers/staging/rts5208/TODO deleted file mode 100644 index 9cec0d8dd0b6c..0000000000000 --- a/drivers/staging/rts5208/TODO +++ /dev/null @@ -1,7 +0,0 @@ -TODO: -- use kernel coding style -- checkpatch.pl fixes -- We will use the stack in drivers/mmc to implement - rts5208/5288 in the future - -Micky Ching diff --git a/drivers/staging/rts5208/general.c b/drivers/staging/rts5208/general.c deleted file mode 100644 index 0f912b011064f..0000000000000 --- a/drivers/staging/rts5208/general.c +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include "general.h" - -int bit1cnt_long(u32 data) -{ - int i, cnt = 0; - - for (i = 0; i < 32; i++) { - if (data & 0x01) - cnt++; - data >>= 1; - } - return cnt; -} - diff --git a/drivers/staging/rts5208/general.h b/drivers/staging/rts5208/general.h deleted file mode 100644 index 53e2dbabf04b7..0000000000000 --- a/drivers/staging/rts5208/general.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __RTSX_GENERAL_H -#define __RTSX_GENERAL_H - -#include "rtsx.h" - -int bit1cnt_long(u32 data); - -#endif /* __RTSX_GENERAL_H */ diff --git a/drivers/staging/rts5208/ms.c b/drivers/staging/rts5208/ms.c deleted file mode 100644 index bfeb5873bf3ba..0000000000000 --- a/drivers/staging/rts5208/ms.c +++ /dev/null @@ -1,4311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include -#include - -#include "rtsx.h" -#include "ms.h" - -static inline void ms_set_err_code(struct rtsx_chip *chip, u8 err_code) -{ - struct ms_info *ms_card = &chip->ms_card; - - ms_card->err_code = err_code; -} - -static inline int ms_check_err_code(struct rtsx_chip *chip, u8 err_code) -{ - struct ms_info *ms_card = &chip->ms_card; - - return (ms_card->err_code == err_code); -} - -static int ms_parse_err_code(struct rtsx_chip *chip) -{ - return STATUS_FAIL; -} - -static int ms_transfer_tpc(struct rtsx_chip *chip, u8 trans_mode, - u8 tpc, u8 cnt, u8 cfg) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - u8 *ptr; - - dev_dbg(rtsx_dev(chip), "%s: tpc = 0x%x\n", __func__, tpc); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | trans_mode); - rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_add_cmd(chip, READ_REG_CMD, MS_TRANS_CFG, 0, 0); - - retval = rtsx_send_cmd(chip, MS_CARD, 5000); - if (retval < 0) { - rtsx_clear_ms_error(chip); - ms_set_err_code(chip, MS_TO_ERROR); - return ms_parse_err_code(chip); - } - - ptr = rtsx_get_cmd_data(chip) + 1; - - if (!(tpc & 0x08)) { /* Read Packet */ - if (*ptr & MS_CRC16_ERR) { - ms_set_err_code(chip, MS_CRC16_ERROR); - return ms_parse_err_code(chip); - } - } else { /* Write Packet */ - if (CHK_MSPRO(ms_card) && !(*ptr & 0x80)) { - if (*ptr & (MS_INT_ERR | MS_INT_CMDNK)) { - ms_set_err_code(chip, MS_CMD_NK); - return ms_parse_err_code(chip); - } - } - } - - if (*ptr & MS_RDY_TIMEOUT) { - rtsx_clear_ms_error(chip); - ms_set_err_code(chip, MS_TO_ERROR); - return ms_parse_err_code(chip); - } - - return STATUS_SUCCESS; -} - -static int ms_transfer_data(struct rtsx_chip *chip, u8 trans_mode, - u8 tpc, u16 sec_cnt, u8 cfg, bool mode_2k, - int use_sg, void *buf, int buf_len) -{ - int retval; - u8 val, err_code = 0; - enum dma_data_direction dir; - - if (!buf || !buf_len) - return STATUS_FAIL; - - if (trans_mode == MS_TM_AUTO_READ) { - dir = DMA_FROM_DEVICE; - err_code = MS_FLASH_READ_ERROR; - } else if (trans_mode == MS_TM_AUTO_WRITE) { - dir = DMA_TO_DEVICE; - err_code = MS_FLASH_WRITE_ERROR; - } else { - return STATUS_FAIL; - } - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_add_cmd(chip, WRITE_REG_CMD, - MS_SECTOR_CNT_H, 0xFF, (u8)(sec_cnt >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_SECTOR_CNT_L, 0xFF, (u8)sec_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - - if (mode_2k) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - MS_CFG, MS_2K_SECTOR_MODE, MS_2K_SECTOR_MODE); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_CFG, MS_2K_SECTOR_MODE, 0); - } - - trans_dma_enable(dir, chip, sec_cnt * 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, - MS_TRANSFER, 0xFF, MS_TRANSFER_START | trans_mode); - rtsx_add_cmd(chip, CHECK_REG_CMD, - MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data(chip, MS_CARD, buf, buf_len, - use_sg, dir, chip->mspro_timeout); - if (retval < 0) { - ms_set_err_code(chip, err_code); - if (retval == -ETIMEDOUT) - retval = STATUS_TIMEDOUT; - else - retval = STATUS_FAIL; - - return retval; - } - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &val); - if (retval) - return retval; - - if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT)) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_write_bytes(struct rtsx_chip *chip, - u8 tpc, u8 cnt, u8 cfg, u8 *data, int data_len) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - - if (!data || data_len < cnt) - return STATUS_ERROR; - - rtsx_init_cmd(chip); - - for (i = 0; i < cnt; i++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, data[i]); - } - if (cnt % 2) - rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2 + i, 0xFF, 0xFF); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, - MS_TRANSFER, 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); - rtsx_add_cmd(chip, CHECK_REG_CMD, - MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END); - - retval = rtsx_send_cmd(chip, MS_CARD, 5000); - if (retval < 0) { - u8 val = 0; - - rtsx_read_register(chip, MS_TRANS_CFG, &val); - dev_dbg(rtsx_dev(chip), "MS_TRANS_CFG: 0x%02x\n", val); - - rtsx_clear_ms_error(chip); - - if (!(tpc & 0x08)) { - if (val & MS_CRC16_ERR) { - ms_set_err_code(chip, MS_CRC16_ERROR); - return ms_parse_err_code(chip); - } - } else { - if (CHK_MSPRO(ms_card) && !(val & 0x80)) { - if (val & (MS_INT_ERR | MS_INT_CMDNK)) { - ms_set_err_code(chip, MS_CMD_NK); - return ms_parse_err_code(chip); - } - } - } - - if (val & MS_RDY_TIMEOUT) { - ms_set_err_code(chip, MS_TO_ERROR); - return ms_parse_err_code(chip); - } - - ms_set_err_code(chip, MS_TO_ERROR); - return ms_parse_err_code(chip); - } - - return STATUS_SUCCESS; -} - -static int ms_read_bytes(struct rtsx_chip *chip, - u8 tpc, u8 cnt, u8 cfg, u8 *data, int data_len) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 *ptr; - - if (!data) - return STATUS_ERROR; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF, - MS_TRANSFER_START | MS_TM_READ_BYTES); - rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - for (i = 0; i < data_len - 1; i++) - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0); - - if (data_len % 2) - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len, 0, 0); - else - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len - 1, - 0, 0); - - retval = rtsx_send_cmd(chip, MS_CARD, 5000); - if (retval < 0) { - u8 val = 0; - - rtsx_read_register(chip, MS_TRANS_CFG, &val); - rtsx_clear_ms_error(chip); - - if (!(tpc & 0x08)) { - if (val & MS_CRC16_ERR) { - ms_set_err_code(chip, MS_CRC16_ERROR); - return ms_parse_err_code(chip); - } - } else { - if (CHK_MSPRO(ms_card) && !(val & 0x80)) { - if (val & (MS_INT_ERR | MS_INT_CMDNK)) { - ms_set_err_code(chip, MS_CMD_NK); - return ms_parse_err_code(chip); - } - } - } - - if (val & MS_RDY_TIMEOUT) { - ms_set_err_code(chip, MS_TO_ERROR); - return ms_parse_err_code(chip); - } - - ms_set_err_code(chip, MS_TO_ERROR); - return ms_parse_err_code(chip); - } - - ptr = rtsx_get_cmd_data(chip) + 1; - - for (i = 0; i < data_len; i++) - data[i] = ptr[i]; - - if (tpc == PRO_READ_SHORT_DATA && data_len == 8) { - dev_dbg(rtsx_dev(chip), "Read format progress:\n"); - print_hex_dump_bytes(KBUILD_MODNAME ": ", DUMP_PREFIX_NONE, ptr, - cnt); - } - - return STATUS_SUCCESS; -} - -static int ms_set_rw_reg_addr(struct rtsx_chip *chip, u8 read_start, - u8 read_cnt, u8 write_start, u8 write_cnt) -{ - int retval, i; - u8 data[4]; - - data[0] = read_start; - data[1] = read_cnt; - data[2] = write_start; - data[3] = write_cnt; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, SET_RW_REG_ADRS, 4, - NO_WAIT_INT, data, 4); - if (retval == STATUS_SUCCESS) - return STATUS_SUCCESS; - rtsx_clear_ms_error(chip); - } - - return STATUS_FAIL; -} - -static int ms_send_cmd(struct rtsx_chip *chip, u8 cmd, u8 cfg) -{ - u8 data[2]; - - data[0] = cmd; - data[1] = 0; - - return ms_write_bytes(chip, PRO_SET_CMD, 1, cfg, data, 1); -} - -static int ms_set_init_para(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - - if (CHK_HG8BIT(ms_card)) { - if (chip->asic_code) - ms_card->ms_clock = chip->asic_ms_hg_clk; - else - ms_card->ms_clock = chip->fpga_ms_hg_clk; - - } else if (CHK_MSPRO(ms_card) || CHK_MS4BIT(ms_card)) { - if (chip->asic_code) - ms_card->ms_clock = chip->asic_ms_4bit_clk; - else - ms_card->ms_clock = chip->fpga_ms_4bit_clk; - - } else { - if (chip->asic_code) - ms_card->ms_clock = chip->asic_ms_1bit_clk; - else - ms_card->ms_clock = chip->fpga_ms_1bit_clk; - } - - retval = switch_clock(chip, ms_card->ms_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = select_card(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_switch_clock(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - - retval = select_card(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = switch_clock(chip, ms_card->ms_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_pull_ctl_disable(struct rtsx_chip *chip) -{ - int retval; - - if (CHECK_PID(chip, 0x5208)) { - retval = rtsx_write_register(chip, CARD_PULL_CTL1, 0xFF, - MS_D1_PD | MS_D2_PD | MS_CLK_PD | - MS_D6_PD); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF, - MS_D3_PD | MS_D0_PD | MS_BS_PD | - XD_D4_PD); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL3, 0xFF, - MS_D7_PD | XD_CE_PD | XD_CLE_PD | - XD_CD_PU); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL4, 0xFF, - XD_RDY_PD | SD_D3_PD | SD_D2_PD | - XD_ALE_PD); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | SD_WP_PD | SD_CD_PU | - SD_CMD_PD); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - if (retval) - return retval; - - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - retval = rtsx_write_register(chip, CARD_PULL_CTL1, - 0xFF, 0x55); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL2, - 0xFF, 0x55); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL3, - 0xFF, 0x4B); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_PULL_CTL4, - 0xFF, 0x69); - if (retval) - return retval; - } - } - - return STATUS_SUCCESS; -} - -static int ms_pull_ctl_enable(struct rtsx_chip *chip) -{ - int retval; - - rtsx_init_cmd(chip); - - if (CHECK_PID(chip, 0x5208)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, - MS_D1_PD | MS_D2_PD | MS_CLK_NP | MS_D6_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, - MS_D3_PD | MS_D0_PD | MS_BS_NP | XD_D4_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, - MS_D7_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, - XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - CARD_PULL_CTL1, 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, - CARD_PULL_CTL2, 0xFF, 0x45); - rtsx_add_cmd(chip, WRITE_REG_CMD, - CARD_PULL_CTL3, 0xFF, 0x4B); - rtsx_add_cmd(chip, WRITE_REG_CMD, - CARD_PULL_CTL4, 0xFF, 0x29); - } - } - - retval = rtsx_send_cmd(chip, MS_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_prepare_reset(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - u8 oc_mask = 0; - - ms_card->ms_type = 0; - ms_card->check_ms_flow = 0; - ms_card->switch_8bit_fail = 0; - ms_card->delay_write.delay_write_flag = 0; - - ms_card->pro_under_formatting = 0; - - retval = ms_power_off_card3v3(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!chip->ft2_fast_mode) - wait_timeout(250); - - retval = enable_card_clock(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (chip->asic_code) { - retval = ms_pull_ctl_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, - FPGA_MS_PULL_CTL_BIT | 0x20, 0); - if (retval) - return retval; - } - - if (!chip->ft2_fast_mode) { - retval = card_power_on(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - wait_timeout(150); - -#ifdef SUPPORT_OCP - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) - oc_mask = MS_OC_NOW | MS_OC_EVER; - else - oc_mask = SD_OC_NOW | SD_OC_EVER; - - if (chip->ocp_stat & oc_mask) { - dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n", - chip->ocp_stat); - return STATUS_FAIL; - } -#endif - } - - retval = rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, - MS_OUTPUT_EN); - if (retval) - return retval; - - if (chip->asic_code) { - retval = rtsx_write_register(chip, MS_CFG, 0xFF, - SAMPLE_TIME_RISING | - PUSH_TIME_DEFAULT | - NO_EXTEND_TOGGLE | - MS_BUS_WIDTH_1); - if (retval) - return retval; - - } else { - retval = rtsx_write_register(chip, MS_CFG, 0xFF, - SAMPLE_TIME_FALLING | - PUSH_TIME_DEFAULT | - NO_EXTEND_TOGGLE | - MS_BUS_WIDTH_1); - if (retval) - return retval; - } - retval = rtsx_write_register(chip, MS_TRANS_CFG, 0xFF, - NO_WAIT_INT | NO_AUTO_READ_INT_REG); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_STOP, MS_STOP | MS_CLR_ERR, - MS_STOP | MS_CLR_ERR); - if (retval) - return retval; - - retval = ms_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_identify_media_type(struct rtsx_chip *chip, int switch_8bit_bus) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 val; - - retval = ms_set_rw_reg_addr(chip, PRO_STATUS_REG, 6, SYSTEM_PARAM, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, READ_REG, - 6, NO_WAIT_INT); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - retval = rtsx_read_register(chip, PPBUF_BASE2 + 2, &val); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "Type register: 0x%x\n", val); - if (val != 0x01) { - if (val != 0x02) - ms_card->check_ms_flow = 1; - - return STATUS_FAIL; - } - - retval = rtsx_read_register(chip, PPBUF_BASE2 + 4, &val); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "Category register: 0x%x\n", val); - if (val != 0) { - ms_card->check_ms_flow = 1; - return STATUS_FAIL; - } - - retval = rtsx_read_register(chip, PPBUF_BASE2 + 5, &val); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "Class register: 0x%x\n", val); - if (val == 0) { - retval = rtsx_read_register(chip, PPBUF_BASE2, &val); - if (retval) - return retval; - - if (val & WRT_PRTCT) - chip->card_wp |= MS_CARD; - else - chip->card_wp &= ~MS_CARD; - - } else if ((val == 0x01) || (val == 0x02) || (val == 0x03)) { - chip->card_wp |= MS_CARD; - } else { - ms_card->check_ms_flow = 1; - return STATUS_FAIL; - } - - ms_card->ms_type |= TYPE_MSPRO; - - retval = rtsx_read_register(chip, PPBUF_BASE2 + 3, &val); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "IF Mode register: 0x%x\n", val); - if (val == 0) { - ms_card->ms_type &= 0x0F; - } else if (val == 7) { - if (switch_8bit_bus) - ms_card->ms_type |= MS_HG; - else - ms_card->ms_type &= 0x0F; - - } else { - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int ms_confirm_cpu_startup(struct rtsx_chip *chip) -{ - int retval, i, k; - u8 val; - - /* Confirm CPU StartUp */ - k = 0; - do { - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_read_bytes(chip, GET_INT, 1, - NO_WAIT_INT, &val, 1); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - if (k > 100) - return STATUS_FAIL; - - k++; - wait_timeout(100); - } while (!(val & INT_REG_CED)); - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - if (val & INT_REG_ERR) { - if (val & INT_REG_CMDNK) - chip->card_wp |= (MS_CARD); - else - return STATUS_FAIL; - } - /* -- end confirm CPU startup */ - - return STATUS_SUCCESS; -} - -static int ms_switch_parallel_bus(struct rtsx_chip *chip) -{ - int retval, i; - u8 data[2]; - - data[0] = PARALLEL_4BIT_IF; - data[1] = 0; - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, WRITE_REG, 1, NO_WAIT_INT, - data, 2); - if (retval == STATUS_SUCCESS) - break; - } - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_switch_8bit_bus(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 data[2]; - - data[0] = PARALLEL_8BIT_IF; - data[1] = 0; - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, WRITE_REG, 1, - NO_WAIT_INT, data, 2); - if (retval == STATUS_SUCCESS) - break; - } - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, MS_CFG, 0x98, - MS_BUS_WIDTH_8 | SAMPLE_TIME_FALLING); - if (retval) - return retval; - - ms_card->ms_type |= MS_8BIT; - retval = ms_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT, - 1, NO_WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int ms_pro_reset_flow(struct rtsx_chip *chip, int switch_8bit_bus) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - - for (i = 0; i < 3; i++) { - retval = ms_prepare_reset(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_identify_media_type(chip, switch_8bit_bus); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_confirm_cpu_startup(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_switch_parallel_bus(chip); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - continue; - } else { - break; - } - } - - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - /* Switch MS-PRO into Parallel mode */ - retval = rtsx_write_register(chip, MS_CFG, 0x18, MS_BUS_WIDTH_4); - if (retval) - return retval; - - retval = rtsx_write_register(chip, MS_CFG, PUSH_TIME_ODD, - PUSH_TIME_ODD); - if (retval) - return retval; - - retval = ms_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - /* If MSPro HG Card, We shall try to switch to 8-bit bus */ - if (CHK_MSHG(ms_card) && chip->support_ms_8bit && switch_8bit_bus) { - retval = ms_switch_8bit_bus(chip); - if (retval != STATUS_SUCCESS) { - ms_card->switch_8bit_fail = 1; - return STATUS_FAIL; - } - } - - return STATUS_SUCCESS; -} - -#ifdef XC_POWERCLASS -static int msxc_change_power(struct rtsx_chip *chip, u8 mode) -{ - int retval; - u8 buf[6]; - - ms_cleanup_work(chip); - - retval = ms_set_rw_reg_addr(chip, 0, 0, PRO_DATA_COUNT1, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf[0] = 0; - buf[1] = mode; - buf[2] = 0; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - - retval = ms_write_bytes(chip, PRO_WRITE_REG, 6, NO_WAIT_INT, buf, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, XC_CHG_POWER, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_read_register(chip, MS_TRANS_CFG, buf); - if (retval) - return retval; - - if (buf[0] & (MS_INT_CMDNK | MS_INT_ERR)) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} -#endif - -static int ms_read_attribute_info(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 val, *buf, class_code, device_type, sub_class, data[16]; - u16 total_blk = 0, blk_size = 0; -#ifdef SUPPORT_MSXC - u32 xc_total_blk = 0, xc_blk_size = 0; -#endif - u32 sys_info_addr = 0, sys_info_size; -#ifdef SUPPORT_PCGL_1P18 - u32 model_name_addr = 0, model_name_size; - int found_sys_info = 0, found_model_name = 0; -#endif - - retval = ms_set_rw_reg_addr(chip, PRO_INT_REG, 2, PRO_SYSTEM_PARAM, 7); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_MS8BIT(ms_card)) - data[0] = PARALLEL_8BIT_IF; - else - data[0] = PARALLEL_4BIT_IF; - - data[1] = 0; - - data[2] = 0x40; - data[3] = 0; - data[4] = 0; - data[5] = 0; - data[6] = 0; - data[7] = 0; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, PRO_WRITE_REG, 7, NO_WAIT_INT, - data, 8); - if (retval == STATUS_SUCCESS) - break; - } - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf = kmalloc(64 * 512, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_send_cmd(chip, PRO_READ_ATRB, WAIT_INT); - if (retval != STATUS_SUCCESS) - continue; - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &val); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - if (!(val & MS_INT_BREQ)) { - kfree(buf); - return STATUS_FAIL; - } - retval = ms_transfer_data(chip, MS_TM_AUTO_READ, - PRO_READ_LONG_DATA, 0x40, WAIT_INT, - 0, 0, buf, 64 * 512); - if (retval == STATUS_SUCCESS) - break; - - rtsx_clear_ms_error(chip); - } - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - i = 0; - do { - retval = rtsx_read_register(chip, MS_TRANS_CFG, &val); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - if ((val & MS_INT_CED) || !(val & MS_INT_BREQ)) - break; - - retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ, - PRO_READ_LONG_DATA, 0, WAIT_INT); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - i++; - } while (i < 1024); - - if (buf[0] != 0xa5 && buf[1] != 0xc3) { - /* Signature code is wrong */ - kfree(buf); - return STATUS_FAIL; - } - - if (buf[4] < 1 || buf[4] > 12) { - kfree(buf); - return STATUS_FAIL; - } - - for (i = 0; i < buf[4]; i++) { - int cur_addr_off = 16 + i * 12; - -#ifdef SUPPORT_MSXC - if (buf[cur_addr_off + 8] == 0x10 || - buf[cur_addr_off + 8] == 0x13) { -#else - if (buf[cur_addr_off + 8] == 0x10) { -#endif - sys_info_addr = ((u32)buf[cur_addr_off + 0] << 24) | - ((u32)buf[cur_addr_off + 1] << 16) | - ((u32)buf[cur_addr_off + 2] << 8) | - buf[cur_addr_off + 3]; - sys_info_size = ((u32)buf[cur_addr_off + 4] << 24) | - ((u32)buf[cur_addr_off + 5] << 16) | - ((u32)buf[cur_addr_off + 6] << 8) | - buf[cur_addr_off + 7]; - dev_dbg(rtsx_dev(chip), "sys_info_addr = 0x%x, sys_info_size = 0x%x\n", - sys_info_addr, sys_info_size); - if (sys_info_size != 96) { - kfree(buf); - return STATUS_FAIL; - } - if (sys_info_addr < 0x1A0) { - kfree(buf); - return STATUS_FAIL; - } - if ((sys_info_size + sys_info_addr) > 0x8000) { - kfree(buf); - return STATUS_FAIL; - } - -#ifdef SUPPORT_MSXC - if (buf[cur_addr_off + 8] == 0x13) - ms_card->ms_type |= MS_XC; -#endif -#ifdef SUPPORT_PCGL_1P18 - found_sys_info = 1; -#else - break; -#endif - } -#ifdef SUPPORT_PCGL_1P18 - if (buf[cur_addr_off + 8] == 0x15) { - model_name_addr = ((u32)buf[cur_addr_off + 0] << 24) | - ((u32)buf[cur_addr_off + 1] << 16) | - ((u32)buf[cur_addr_off + 2] << 8) | - buf[cur_addr_off + 3]; - model_name_size = ((u32)buf[cur_addr_off + 4] << 24) | - ((u32)buf[cur_addr_off + 5] << 16) | - ((u32)buf[cur_addr_off + 6] << 8) | - buf[cur_addr_off + 7]; - dev_dbg(rtsx_dev(chip), "model_name_addr = 0x%x, model_name_size = 0x%x\n", - model_name_addr, model_name_size); - if (model_name_size != 48) { - kfree(buf); - return STATUS_FAIL; - } - if (model_name_addr < 0x1A0) { - kfree(buf); - return STATUS_FAIL; - } - if ((model_name_size + model_name_addr) > 0x8000) { - kfree(buf); - return STATUS_FAIL; - } - - found_model_name = 1; - } - - if (found_sys_info && found_model_name) - break; -#endif - } - - if (i == buf[4]) { - kfree(buf); - return STATUS_FAIL; - } - - class_code = buf[sys_info_addr + 0]; - device_type = buf[sys_info_addr + 56]; - sub_class = buf[sys_info_addr + 46]; -#ifdef SUPPORT_MSXC - if (CHK_MSXC(ms_card)) { - xc_total_blk = ((u32)buf[sys_info_addr + 6] << 24) | - ((u32)buf[sys_info_addr + 7] << 16) | - ((u32)buf[sys_info_addr + 8] << 8) | - buf[sys_info_addr + 9]; - xc_blk_size = ((u32)buf[sys_info_addr + 32] << 24) | - ((u32)buf[sys_info_addr + 33] << 16) | - ((u32)buf[sys_info_addr + 34] << 8) | - buf[sys_info_addr + 35]; - dev_dbg(rtsx_dev(chip), "xc_total_blk = 0x%x, xc_blk_size = 0x%x\n", - xc_total_blk, xc_blk_size); - } else { - total_blk = ((u16)buf[sys_info_addr + 6] << 8) | - buf[sys_info_addr + 7]; - blk_size = ((u16)buf[sys_info_addr + 2] << 8) | - buf[sys_info_addr + 3]; - dev_dbg(rtsx_dev(chip), "total_blk = 0x%x, blk_size = 0x%x\n", - total_blk, blk_size); - } -#else - total_blk = ((u16)buf[sys_info_addr + 6] << 8) | buf[sys_info_addr + 7]; - blk_size = ((u16)buf[sys_info_addr + 2] << 8) | buf[sys_info_addr + 3]; - dev_dbg(rtsx_dev(chip), "total_blk = 0x%x, blk_size = 0x%x\n", - total_blk, blk_size); -#endif - - dev_dbg(rtsx_dev(chip), "class_code = 0x%x, device_type = 0x%x, sub_class = 0x%x\n", - class_code, device_type, sub_class); - - memcpy(ms_card->raw_sys_info, buf + sys_info_addr, 96); -#ifdef SUPPORT_PCGL_1P18 - memcpy(ms_card->raw_model_name, buf + model_name_addr, 48); -#endif - - kfree(buf); - -#ifdef SUPPORT_MSXC - if (CHK_MSXC(ms_card)) { - if (class_code != 0x03) - return STATUS_FAIL; - } else { - if (class_code != 0x02) - return STATUS_FAIL; - } -#else - if (class_code != 0x02) - return STATUS_FAIL; -#endif - - if (device_type != 0x00) { - if (device_type == 0x01 || device_type == 0x02 || - device_type == 0x03) { - chip->card_wp |= MS_CARD; - } else { - return STATUS_FAIL; - } - } - - if (sub_class & 0xC0) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "class_code: 0x%x, device_type: 0x%x, sub_class: 0x%x\n", - class_code, device_type, sub_class); - -#ifdef SUPPORT_MSXC - if (CHK_MSXC(ms_card)) { - chip->capacity[chip->card2lun[MS_CARD]] = - ms_card->capacity = xc_total_blk * xc_blk_size; - } else { - chip->capacity[chip->card2lun[MS_CARD]] = - ms_card->capacity = total_blk * blk_size; - } -#else - ms_card->capacity = total_blk * blk_size; - chip->capacity[chip->card2lun[MS_CARD]] = ms_card->capacity; -#endif - - return STATUS_SUCCESS; -} - -#ifdef SUPPORT_MAGIC_GATE -static int mg_set_tpc_para_sub(struct rtsx_chip *chip, - int type, u8 mg_entry_num); -#endif - -static int reset_ms_pro(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; -#ifdef XC_POWERCLASS - u8 change_power_class; - - if (chip->ms_power_class_en & 0x02) - change_power_class = 2; - else if (chip->ms_power_class_en & 0x01) - change_power_class = 1; - else - change_power_class = 0; -#endif - -#ifdef XC_POWERCLASS -retry: -#endif - retval = ms_pro_reset_flow(chip, 1); - if (retval != STATUS_SUCCESS) { - if (ms_card->switch_8bit_fail) { - retval = ms_pro_reset_flow(chip, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - return STATUS_FAIL; - } - } - - retval = ms_read_attribute_info(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - -#ifdef XC_POWERCLASS - if (CHK_HG8BIT(ms_card)) - change_power_class = 0; - - if (change_power_class && CHK_MSXC(ms_card)) { - u8 power_class_en = chip->ms_power_class_en; - - dev_dbg(rtsx_dev(chip), "power_class_en = 0x%x\n", - power_class_en); - dev_dbg(rtsx_dev(chip), "change_power_class = %d\n", - change_power_class); - - if (change_power_class) - power_class_en &= (1 << (change_power_class - 1)); - else - power_class_en = 0; - - if (power_class_en) { - u8 power_class_mode = - (ms_card->raw_sys_info[46] & 0x18) >> 3; - dev_dbg(rtsx_dev(chip), "power_class_mode = 0x%x", - power_class_mode); - if (change_power_class > power_class_mode) - change_power_class = power_class_mode; - if (change_power_class) { - retval = msxc_change_power(chip, - change_power_class); - if (retval != STATUS_SUCCESS) { - change_power_class--; - goto retry; - } - } - } - } -#endif - -#ifdef SUPPORT_MAGIC_GATE - retval = mg_set_tpc_para_sub(chip, 0, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; -#endif - - if (CHK_HG8BIT(ms_card)) - chip->card_bus_width[chip->card2lun[MS_CARD]] = 8; - else - chip->card_bus_width[chip->card2lun[MS_CARD]] = 4; - - return STATUS_SUCCESS; -} - -static int ms_read_status_reg(struct rtsx_chip *chip) -{ - int retval; - u8 val[2]; - - retval = ms_set_rw_reg_addr(chip, STATUS_REG0, 2, 0, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_read_bytes(chip, READ_REG, 2, NO_WAIT_INT, val, 2); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val[1] & (STS_UCDT | STS_UCEX | STS_UCFG)) { - ms_set_err_code(chip, MS_FLASH_READ_ERROR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int ms_read_extra_data(struct rtsx_chip *chip, - u16 block_addr, u8 page_num, u8 *buf, int buf_len) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 val, data[10]; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_MS4BIT(ms_card)) { - /* Parallel interface */ - data[0] = 0x88; - } else { - /* Serial interface */ - data[0] = 0x80; - } - data[1] = 0; - data[2] = (u8)(block_addr >> 8); - data[3] = (u8)block_addr; - data[4] = 0x40; - data[5] = page_num; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, - data, 6); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - retval = ms_read_status_reg(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, - MS_EXTRA_SIZE, SYSTEM_PARAM, - 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - } - - retval = ms_read_bytes(chip, READ_REG, MS_EXTRA_SIZE, NO_WAIT_INT, - data, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (buf && buf_len) { - if (buf_len > MS_EXTRA_SIZE) - buf_len = MS_EXTRA_SIZE; - memcpy(buf, data, buf_len); - } - - return STATUS_SUCCESS; -} - -static int ms_write_extra_data(struct rtsx_chip *chip, u16 block_addr, - u8 page_num, u8 *buf, int buf_len) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 val, data[16]; - - if (!buf || buf_len < MS_EXTRA_SIZE) - return STATUS_FAIL; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 6 + MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(block_addr >> 8); - data[3] = (u8)block_addr; - data[4] = 0x40; - data[5] = page_num; - - for (i = 6; i < MS_EXTRA_SIZE + 6; i++) - data[i] = buf[i - 6]; - - retval = ms_write_bytes(chip, WRITE_REG, (6 + MS_EXTRA_SIZE), - NO_WAIT_INT, data, 16); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - - return STATUS_SUCCESS; -} - -static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - u8 val, data[6]; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(block_addr >> 8); - data[3] = (u8)block_addr; - data[4] = 0x20; - data[5] = page_num; - - retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, data, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - if (!(val & INT_REG_BREQ)) { - ms_set_err_code(chip, MS_FLASH_READ_ERROR); - return STATUS_FAIL; - } - retval = ms_read_status_reg(chip); - if (retval != STATUS_SUCCESS) - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - - } else { - if (!(val & INT_REG_BREQ)) { - ms_set_err_code(chip, MS_BREQ_ERROR); - return STATUS_FAIL; - } - } - } - - retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ, READ_PAGE_DATA, - 0, NO_WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR)) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int ms_set_bad_block(struct rtsx_chip *chip, u16 phy_blk) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - u8 val, data[8], extra[MS_EXTRA_SIZE]; - - retval = ms_read_extra_data(chip, phy_blk, 0, extra, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 7); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(phy_blk >> 8); - data[3] = (u8)phy_blk; - data[4] = 0x80; - data[5] = 0; - data[6] = extra[0] & 0x7F; - data[7] = 0xFF; - - retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT, data, 7); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - - return STATUS_SUCCESS; -} - -static int ms_erase_block(struct rtsx_chip *chip, u16 phy_blk) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i = 0; - u8 val, data[6]; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(phy_blk >> 8); - data[3] = (u8)phy_blk; - data[4] = 0; - data[5] = 0; - - retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, data, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - -ERASE_RTY: - retval = ms_send_cmd(chip, BLOCK_ERASE, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - if (i < 3) { - i++; - goto ERASE_RTY; - } - - ms_set_err_code(chip, MS_CMD_NK); - ms_set_bad_block(chip, phy_blk); - return STATUS_FAIL; - } - - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - - return STATUS_SUCCESS; -} - -static void ms_set_page_status(u16 log_blk, u8 type, u8 *extra, int extra_len) -{ - if (!extra || extra_len < MS_EXTRA_SIZE) - return; - - memset(extra, 0xFF, MS_EXTRA_SIZE); - - if (type == set_PS_NG) { - /* set page status as 1:NG,and block status keep 1:OK */ - extra[0] = 0xB8; - } else { - /* set page status as 0:Data Error,and block status keep 1:OK */ - extra[0] = 0x98; - } - - extra[2] = (u8)(log_blk >> 8); - extra[3] = (u8)log_blk; -} - -static int ms_init_page(struct rtsx_chip *chip, u16 phy_blk, u16 log_blk, - u8 start_page, u8 end_page) -{ - int retval; - u8 extra[MS_EXTRA_SIZE], i; - - memset(extra, 0xff, MS_EXTRA_SIZE); - - extra[0] = 0xf8; /* Block, page OK, data erased */ - extra[1] = 0xff; - extra[2] = (u8)(log_blk >> 8); - extra[3] = (u8)log_blk; - - for (i = start_page; i < end_page; i++) { - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - - retval = ms_write_extra_data(chip, phy_blk, i, - extra, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk, - u16 log_blk, u8 start_page, u8 end_page) -{ - struct ms_info *ms_card = &chip->ms_card; - bool uncorrect_flag = false; - int retval, rty_cnt; - u8 extra[MS_EXTRA_SIZE], val, i, j, data[16]; - - dev_dbg(rtsx_dev(chip), "Copy page from 0x%x to 0x%x, logical block is 0x%x\n", - old_blk, new_blk, log_blk); - dev_dbg(rtsx_dev(chip), "start_page = %d, end_page = %d\n", - start_page, end_page); - - retval = ms_read_extra_data(chip, new_blk, 0, extra, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_read_status_reg(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_read_register(chip, PPBUF_BASE2, &val); - if (retval) - return retval; - - if (val & BUF_FULL) { - retval = ms_send_cmd(chip, CLEAR_BUF, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!(val & INT_REG_CED)) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - - for (i = start_page; i < end_page; i++) { - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - - retval = ms_read_extra_data(chip, old_blk, i, extra, - MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, - MS_EXTRA_SIZE, SYSTEM_PARAM, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(old_blk >> 8); - data[3] = (u8)old_blk; - data[4] = 0x20; - data[5] = i; - - retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, - data, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - retval = ms_read_status_reg(chip); - if (retval != STATUS_SUCCESS) { - uncorrect_flag = true; - dev_dbg(rtsx_dev(chip), "Uncorrectable error\n"); - } else { - uncorrect_flag = false; - } - - retval = ms_transfer_tpc(chip, - MS_TM_NORMAL_READ, - READ_PAGE_DATA, - 0, NO_WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (uncorrect_flag) { - ms_set_page_status(log_blk, set_PS_NG, - extra, - MS_EXTRA_SIZE); - if (i == 0) - extra[0] &= 0xEF; - - ms_write_extra_data(chip, old_blk, i, - extra, - MS_EXTRA_SIZE); - dev_dbg(rtsx_dev(chip), "page %d : extra[0] = 0x%x\n", - i, extra[0]); - MS_SET_BAD_BLOCK_FLG(ms_card); - - ms_set_page_status(log_blk, - set_PS_error, extra, - MS_EXTRA_SIZE); - ms_write_extra_data(chip, new_blk, i, - extra, - MS_EXTRA_SIZE); - continue; - } - - for (rty_cnt = 0; rty_cnt < MS_MAX_RETRY_COUNT; - rty_cnt++) { - retval = ms_transfer_tpc(chip, - MS_TM_NORMAL_WRITE, - WRITE_PAGE_DATA, - 0, NO_WAIT_INT); - if (retval == STATUS_SUCCESS) - break; - } - if (rty_cnt == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - } - - if (!(val & INT_REG_BREQ)) { - ms_set_err_code(chip, MS_BREQ_ERROR); - return STATUS_FAIL; - } - } - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, (6 + MS_EXTRA_SIZE)); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(new_blk >> 8); - data[3] = (u8)new_blk; - data[4] = 0x20; - data[5] = i; - - if ((extra[0] & 0x60) != 0x60) - data[6] = extra[0]; - else - data[6] = 0xF8; - - data[6 + 1] = 0xFF; - data[6 + 2] = (u8)(log_blk >> 8); - data[6 + 3] = (u8)log_blk; - - for (j = 4; j <= MS_EXTRA_SIZE; j++) - data[6 + j] = 0xFF; - - retval = ms_write_bytes(chip, WRITE_REG, (6 + MS_EXTRA_SIZE), - NO_WAIT_INT, data, 16); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - - if (i == 0) { - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, - MS_EXTRA_SIZE, SYSTEM_PARAM, - 7); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(old_blk >> 8); - data[3] = (u8)old_blk; - data[4] = 0x80; - data[5] = 0; - data[6] = 0xEF; - data[7] = 0xFF; - - retval = ms_write_bytes(chip, WRITE_REG, 7, - NO_WAIT_INT, data, 8); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_read_bytes(chip, GET_INT, 1, - NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - - if (val & INT_REG_CED) { - if (val & INT_REG_ERR) { - ms_set_err_code(chip, - MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - } - } - - return STATUS_SUCCESS; -} - -static int reset_ms(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - u16 i, reg_addr, block_size; - u8 val, extra[MS_EXTRA_SIZE], j, *ptr; -#ifndef SUPPORT_MAGIC_GATE - u16 eblock_cnt; -#endif - - retval = ms_prepare_reset(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_card->ms_type |= TYPE_MS; - - retval = ms_send_cmd(chip, MS_RESET, NO_WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_read_status_reg(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_read_register(chip, PPBUF_BASE2, &val); - if (retval) - return retval; - - if (val & WRT_PRTCT) - chip->card_wp |= MS_CARD; - else - chip->card_wp &= ~MS_CARD; - - i = 0; - -RE_SEARCH: - /* Search Boot Block */ - while (i < (MAX_DEFECTIVE_BLOCK + 2)) { - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - - retval = ms_read_extra_data(chip, i, 0, extra, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) { - i++; - continue; - } - - if (extra[0] & BLOCK_OK) { - if (!(extra[1] & NOT_BOOT_BLOCK)) { - ms_card->boot_block = i; - break; - } - } - i++; - } - - if (i == (MAX_DEFECTIVE_BLOCK + 2)) { - dev_dbg(rtsx_dev(chip), "No boot block found!"); - return STATUS_FAIL; - } - - for (j = 0; j < 3; j++) { - retval = ms_read_page(chip, ms_card->boot_block, j); - if (retval != STATUS_SUCCESS) { - if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR)) { - i = ms_card->boot_block + 1; - ms_set_err_code(chip, MS_NO_ERROR); - goto RE_SEARCH; - } - } - } - - retval = ms_read_page(chip, ms_card->boot_block, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - /* Read MS system information as sys_info */ - rtsx_init_cmd(chip); - - for (i = 0; i < 96; i++) - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 0x1A0 + i, 0, 0); - - retval = rtsx_send_cmd(chip, MS_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - ptr = rtsx_get_cmd_data(chip); - memcpy(ms_card->raw_sys_info, ptr, 96); - - /* Read useful block contents */ - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID0, 0, 0); - rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID1, 0, 0); - - for (reg_addr = DISABLED_BLOCK0; reg_addr <= DISABLED_BLOCK3; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0); - - for (reg_addr = BLOCK_SIZE_0; reg_addr <= PAGE_SIZE_1; reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0); - - rtsx_add_cmd(chip, READ_REG_CMD, MS_device_type, 0, 0); - rtsx_add_cmd(chip, READ_REG_CMD, MS_4bit_support, 0, 0); - - retval = rtsx_send_cmd(chip, MS_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - ptr = rtsx_get_cmd_data(chip); - - dev_dbg(rtsx_dev(chip), "Boot block data:\n"); - dev_dbg(rtsx_dev(chip), "%*ph\n", 16, ptr); - - /* Block ID error - * HEADER_ID0, HEADER_ID1 - */ - if (ptr[0] != 0x00 || ptr[1] != 0x01) { - i = ms_card->boot_block + 1; - goto RE_SEARCH; - } - - /* Page size error - * PAGE_SIZE_0, PAGE_SIZE_1 - */ - if (ptr[12] != 0x02 || ptr[13] != 0x00) { - i = ms_card->boot_block + 1; - goto RE_SEARCH; - } - - if (ptr[14] == 1 || ptr[14] == 3) - chip->card_wp |= MS_CARD; - - /* BLOCK_SIZE_0, BLOCK_SIZE_1 */ - block_size = ((u16)ptr[6] << 8) | ptr[7]; - if (block_size == 0x0010) { - /* Block size 16KB */ - ms_card->block_shift = 5; - ms_card->page_off = 0x1F; - } else if (block_size == 0x0008) { - /* Block size 8KB */ - ms_card->block_shift = 4; - ms_card->page_off = 0x0F; - } - - /* BLOCK_COUNT_0, BLOCK_COUNT_1 */ - ms_card->total_block = ((u16)ptr[8] << 8) | ptr[9]; - -#ifdef SUPPORT_MAGIC_GATE - j = ptr[10]; - - if (ms_card->block_shift == 4) { /* 4MB or 8MB */ - if (j < 2) { /* Effective block for 4MB: 0x1F0 */ - ms_card->capacity = 0x1EE0; - } else { /* Effective block for 8MB: 0x3E0 */ - ms_card->capacity = 0x3DE0; - } - } else { /* 16MB, 32MB, 64MB or 128MB */ - if (j < 5) { /* Effective block for 16MB: 0x3E0 */ - ms_card->capacity = 0x7BC0; - } else if (j < 0xA) { /* Effective block for 32MB: 0x7C0 */ - ms_card->capacity = 0xF7C0; - } else if (j < 0x11) { /* Effective block for 64MB: 0xF80 */ - ms_card->capacity = 0x1EF80; - } else { /* Effective block for 128MB: 0x1F00 */ - ms_card->capacity = 0x3DF00; - } - } -#else - /* EBLOCK_COUNT_0, EBLOCK_COUNT_1 */ - eblock_cnt = ((u16)ptr[10] << 8) | ptr[11]; - - ms_card->capacity = ((u32)eblock_cnt - 2) << ms_card->block_shift; -#endif - - chip->capacity[chip->card2lun[MS_CARD]] = ms_card->capacity; - - /* Switch I/F Mode */ - if (ptr[15]) { - retval = ms_set_rw_reg_addr(chip, 0, 0, SYSTEM_PARAM, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, PPBUF_BASE2, 0xFF, 0x88); - if (retval) - return retval; - - retval = rtsx_write_register(chip, PPBUF_BASE2 + 1, 0xFF, 0); - if (retval) - return retval; - - retval = ms_transfer_tpc(chip, MS_TM_WRITE_BYTES, WRITE_REG, 1, - NO_WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, MS_CFG, - 0x58 | MS_NO_CHECK_INT, - MS_BUS_WIDTH_4 | - PUSH_TIME_ODD | - MS_NO_CHECK_INT); - if (retval) - return retval; - - ms_card->ms_type |= MS_4BIT; - } - - if (CHK_MS4BIT(ms_card)) - chip->card_bus_width[chip->card2lun[MS_CARD]] = 4; - else - chip->card_bus_width[chip->card2lun[MS_CARD]] = 1; - - return STATUS_SUCCESS; -} - -static int ms_init_l2p_tbl(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int size, i, seg_no, retval; - u16 defect_block, reg_addr; - u8 val1, val2; - - ms_card->segment_cnt = ms_card->total_block >> 9; - dev_dbg(rtsx_dev(chip), "ms_card->segment_cnt = %d\n", - ms_card->segment_cnt); - - size = ms_card->segment_cnt * sizeof(struct zone_entry); - ms_card->segment = vzalloc(size); - if (!ms_card->segment) - return STATUS_FAIL; - - retval = ms_read_page(chip, ms_card->boot_block, 1); - if (retval != STATUS_SUCCESS) - goto INIT_FAIL; - - reg_addr = PPBUF_BASE2; - for (i = 0; i < (((ms_card->total_block >> 9) * 10) + 1); i++) { - int block_no; - - retval = rtsx_read_register(chip, reg_addr++, &val1); - if (retval != STATUS_SUCCESS) - goto INIT_FAIL; - - retval = rtsx_read_register(chip, reg_addr++, &val2); - if (retval != STATUS_SUCCESS) - goto INIT_FAIL; - - defect_block = ((u16)val1 << 8) | val2; - if (defect_block == 0xFFFF) - break; - - seg_no = defect_block / 512; - - block_no = ms_card->segment[seg_no].disable_count++; - ms_card->segment[seg_no].defect_list[block_no] = defect_block; - } - - for (i = 0; i < ms_card->segment_cnt; i++) { - ms_card->segment[i].build_flag = 0; - ms_card->segment[i].l2p_table = NULL; - ms_card->segment[i].free_table = NULL; - ms_card->segment[i].get_index = 0; - ms_card->segment[i].set_index = 0; - ms_card->segment[i].unused_blk_cnt = 0; - - dev_dbg(rtsx_dev(chip), "defective block count of segment %d is %d\n", - i, ms_card->segment[i].disable_count); - } - - return STATUS_SUCCESS; - -INIT_FAIL: - vfree(ms_card->segment); - ms_card->segment = NULL; - - return STATUS_FAIL; -} - -static u16 ms_get_l2p_tbl(struct rtsx_chip *chip, int seg_no, u16 log_off) -{ - struct ms_info *ms_card = &chip->ms_card; - struct zone_entry *segment; - - if (!ms_card->segment) - return 0xFFFF; - - segment = &ms_card->segment[seg_no]; - - if (segment->l2p_table) - return segment->l2p_table[log_off]; - - return 0xFFFF; -} - -static void ms_set_l2p_tbl(struct rtsx_chip *chip, - int seg_no, u16 log_off, u16 phy_blk) -{ - struct ms_info *ms_card = &chip->ms_card; - struct zone_entry *segment; - - if (!ms_card->segment) - return; - - segment = &ms_card->segment[seg_no]; - if (segment->l2p_table) - segment->l2p_table[log_off] = phy_blk; -} - -static void ms_set_unused_block(struct rtsx_chip *chip, u16 phy_blk) -{ - struct ms_info *ms_card = &chip->ms_card; - struct zone_entry *segment; - int seg_no; - - seg_no = (int)phy_blk >> 9; - segment = &ms_card->segment[seg_no]; - - segment->free_table[segment->set_index++] = phy_blk; - if (segment->set_index >= MS_FREE_TABLE_CNT) - segment->set_index = 0; - - segment->unused_blk_cnt++; -} - -static u16 ms_get_unused_block(struct rtsx_chip *chip, int seg_no) -{ - struct ms_info *ms_card = &chip->ms_card; - struct zone_entry *segment; - u16 phy_blk; - - segment = &ms_card->segment[seg_no]; - - if (segment->unused_blk_cnt <= 0) - return 0xFFFF; - - phy_blk = segment->free_table[segment->get_index]; - segment->free_table[segment->get_index++] = 0xFFFF; - if (segment->get_index >= MS_FREE_TABLE_CNT) - segment->get_index = 0; - - segment->unused_blk_cnt--; - - return phy_blk; -} - -static const unsigned short ms_start_idx[] = {0, 494, 990, 1486, 1982, 2478, - 2974, 3470, 3966, 4462, 4958, - 5454, 5950, 6446, 6942, 7438, - 7934}; - -static int ms_arbitrate_l2p(struct rtsx_chip *chip, u16 phy_blk, - u16 log_off, u8 us1, u8 us2) -{ - struct ms_info *ms_card = &chip->ms_card; - struct zone_entry *segment; - int seg_no; - u16 tmp_blk; - - seg_no = (int)phy_blk >> 9; - segment = &ms_card->segment[seg_no]; - tmp_blk = segment->l2p_table[log_off]; - - if (us1 != us2) { - if (us1 == 0) { - if (!(chip->card_wp & MS_CARD)) - ms_erase_block(chip, tmp_blk); - - ms_set_unused_block(chip, tmp_blk); - segment->l2p_table[log_off] = phy_blk; - } else { - if (!(chip->card_wp & MS_CARD)) - ms_erase_block(chip, phy_blk); - - ms_set_unused_block(chip, phy_blk); - } - } else { - if (phy_blk < tmp_blk) { - if (!(chip->card_wp & MS_CARD)) - ms_erase_block(chip, phy_blk); - - ms_set_unused_block(chip, phy_blk); - } else { - if (!(chip->card_wp & MS_CARD)) - ms_erase_block(chip, tmp_blk); - - ms_set_unused_block(chip, tmp_blk); - segment->l2p_table[log_off] = phy_blk; - } - } - - return STATUS_SUCCESS; -} - -static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no) -{ - struct ms_info *ms_card = &chip->ms_card; - struct zone_entry *segment; - bool defect_flag; - int retval, table_size, disable_cnt, i; - u16 start, end, phy_blk, log_blk, tmp_blk, idx; - u8 extra[MS_EXTRA_SIZE], us1, us2; - - dev_dbg(rtsx_dev(chip), "%s: %d\n", __func__, seg_no); - - if (!ms_card->segment) { - retval = ms_init_l2p_tbl(chip); - if (retval != STATUS_SUCCESS) - return retval; - } - - if (ms_card->segment[seg_no].build_flag) { - dev_dbg(rtsx_dev(chip), "l2p table of segment %d has been built\n", - seg_no); - return STATUS_SUCCESS; - } - - if (seg_no == 0) - table_size = 494; - else - table_size = 496; - - segment = &ms_card->segment[seg_no]; - - if (!segment->l2p_table) { - segment->l2p_table = vmalloc(array_size(table_size, 2)); - if (!segment->l2p_table) - goto BUILD_FAIL; - } - memset((u8 *)(segment->l2p_table), 0xff, array_size(table_size, 2)); - - if (!segment->free_table) { - segment->free_table = vmalloc(array_size(MS_FREE_TABLE_CNT, 2)); - if (!segment->free_table) - goto BUILD_FAIL; - } - memset((u8 *)(segment->free_table), 0xff, array_size(MS_FREE_TABLE_CNT, 2)); - - start = (u16)seg_no << 9; - end = (u16)(seg_no + 1) << 9; - - disable_cnt = segment->disable_count; - - segment->get_index = 0; - segment->set_index = 0; - segment->unused_blk_cnt = 0; - - for (phy_blk = start; phy_blk < end; phy_blk++) { - if (disable_cnt) { - defect_flag = false; - for (i = 0; i < segment->disable_count; i++) { - if (phy_blk == segment->defect_list[i]) { - defect_flag = true; - break; - } - } - if (defect_flag) { - disable_cnt--; - continue; - } - } - - retval = ms_read_extra_data(chip, phy_blk, 0, - extra, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) { - dev_dbg(rtsx_dev(chip), "read extra data fail\n"); - ms_set_bad_block(chip, phy_blk); - continue; - } - - if (seg_no == ms_card->segment_cnt - 1) { - if (!(extra[1] & NOT_TRANSLATION_TABLE)) { - if (!(chip->card_wp & MS_CARD)) { - retval = ms_erase_block(chip, phy_blk); - if (retval != STATUS_SUCCESS) - continue; - extra[2] = 0xff; - extra[3] = 0xff; - } - } - } - - if (!(extra[0] & BLOCK_OK)) - continue; - if (!(extra[1] & NOT_BOOT_BLOCK)) - continue; - if ((extra[0] & PAGE_OK) != PAGE_OK) - continue; - - log_blk = ((u16)extra[2] << 8) | extra[3]; - - if (log_blk == 0xFFFF) { - if (!(chip->card_wp & MS_CARD)) { - retval = ms_erase_block(chip, phy_blk); - if (retval != STATUS_SUCCESS) - continue; - } - ms_set_unused_block(chip, phy_blk); - continue; - } - - if (log_blk < ms_start_idx[seg_no] || - log_blk >= ms_start_idx[seg_no + 1]) { - if (!(chip->card_wp & MS_CARD)) { - retval = ms_erase_block(chip, phy_blk); - if (retval != STATUS_SUCCESS) - continue; - } - ms_set_unused_block(chip, phy_blk); - continue; - } - - idx = log_blk - ms_start_idx[seg_no]; - - if (segment->l2p_table[idx] == 0xFFFF) { - segment->l2p_table[idx] = phy_blk; - continue; - } - - us1 = extra[0] & 0x10; - tmp_blk = segment->l2p_table[idx]; - retval = ms_read_extra_data(chip, tmp_blk, 0, - extra, MS_EXTRA_SIZE); - if (retval != STATUS_SUCCESS) - continue; - us2 = extra[0] & 0x10; - - (void)ms_arbitrate_l2p(chip, phy_blk, - log_blk - ms_start_idx[seg_no], us1, us2); - } - - segment->build_flag = 1; - - dev_dbg(rtsx_dev(chip), "unused block count: %d\n", - segment->unused_blk_cnt); - - /* Logical Address Confirmation Process */ - if (seg_no == ms_card->segment_cnt - 1) { - if (segment->unused_blk_cnt < 2) - chip->card_wp |= MS_CARD; - } else { - if (segment->unused_blk_cnt < 1) - chip->card_wp |= MS_CARD; - } - - if (chip->card_wp & MS_CARD) - return STATUS_SUCCESS; - - for (log_blk = ms_start_idx[seg_no]; - log_blk < ms_start_idx[seg_no + 1]; log_blk++) { - idx = log_blk - ms_start_idx[seg_no]; - if (segment->l2p_table[idx] == 0xFFFF) { - phy_blk = ms_get_unused_block(chip, seg_no); - if (phy_blk == 0xFFFF) { - chip->card_wp |= MS_CARD; - return STATUS_SUCCESS; - } - retval = ms_init_page(chip, phy_blk, log_blk, 0, 1); - if (retval != STATUS_SUCCESS) - goto BUILD_FAIL; - - segment->l2p_table[idx] = phy_blk; - if (seg_no == ms_card->segment_cnt - 1) { - if (segment->unused_blk_cnt < 2) { - chip->card_wp |= MS_CARD; - return STATUS_SUCCESS; - } - } else { - if (segment->unused_blk_cnt < 1) { - chip->card_wp |= MS_CARD; - return STATUS_SUCCESS; - } - } - } - } - - /* Make boot block be the first normal block */ - if (seg_no == 0) { - for (log_blk = 0; log_blk < 494; log_blk++) { - tmp_blk = segment->l2p_table[log_blk]; - if (tmp_blk < ms_card->boot_block) { - dev_dbg(rtsx_dev(chip), "Boot block is not the first normal block.\n"); - - if (chip->card_wp & MS_CARD) - break; - - phy_blk = ms_get_unused_block(chip, 0); - retval = ms_copy_page(chip, tmp_blk, phy_blk, - log_blk, 0, - ms_card->page_off + 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - segment->l2p_table[log_blk] = phy_blk; - - retval = ms_set_bad_block(chip, tmp_blk); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - } - } - - return STATUS_SUCCESS; - -BUILD_FAIL: - segment->build_flag = 0; - vfree(segment->l2p_table); - segment->l2p_table = NULL; - vfree(segment->free_table); - segment->free_table = NULL; - - return STATUS_FAIL; -} - -int reset_ms_card(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int seg_no = ms_card->total_block / 512 - 1; - int retval; - - memset(ms_card, 0, sizeof(struct ms_info)); - - retval = enable_card_clock(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = select_card(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_card->ms_type = 0; - - retval = reset_ms_pro(chip); - if (retval != STATUS_SUCCESS) { - if (ms_card->check_ms_flow) { - retval = reset_ms(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - return STATUS_FAIL; - } - } - - retval = ms_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!CHK_MSPRO(ms_card)) { - /* Build table for the last segment, - * to check if L2P table block exists, erasing it - */ - retval = ms_build_l2p_tbl(chip, seg_no); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - dev_dbg(rtsx_dev(chip), "ms_card->ms_type = 0x%x\n", ms_card->ms_type); - - return STATUS_SUCCESS; -} - -static int mspro_set_rw_cmd(struct rtsx_chip *chip, - u32 start_sec, u16 sec_cnt, u8 cmd) -{ - int retval, i; - u8 data[8]; - - data[0] = cmd; - data[1] = (u8)(sec_cnt >> 8); - data[2] = (u8)sec_cnt; - data[3] = (u8)(start_sec >> 24); - data[4] = (u8)(start_sec >> 16); - data[5] = (u8)(start_sec >> 8); - data[6] = (u8)start_sec; - data[7] = 0; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, PRO_EX_SET_CMD, 7, - WAIT_INT, data, 8); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -void mspro_stop_seq_mode(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - - if (ms_card->seq_mode) { - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return; - - ms_card->seq_mode = 0; - ms_card->total_sec_cnt = 0; - ms_send_cmd(chip, PRO_STOP, WAIT_INT); - - rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH); - } -} - -static inline int ms_auto_tune_clock(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - - if (chip->asic_code) { - if (ms_card->ms_clock > 30) - ms_card->ms_clock -= 20; - } else { - if (ms_card->ms_clock == CLK_80) - ms_card->ms_clock = CLK_60; - else if (ms_card->ms_clock == CLK_60) - ms_card->ms_clock = CLK_40; - } - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int mspro_rw_multi_sector(struct scsi_cmnd *srb, - struct rtsx_chip *chip, u32 start_sector, - u16 sector_cnt) -{ - struct ms_info *ms_card = &chip->ms_card; - bool mode_2k = false; - int retval; - u16 count; - u8 val, trans_mode, rw_tpc, rw_cmd; - - ms_set_err_code(chip, MS_NO_ERROR); - - ms_card->cleanup_counter = 0; - - if (CHK_MSHG(ms_card)) { - if ((start_sector % 4) || (sector_cnt % 4)) { - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - rw_tpc = PRO_READ_LONG_DATA; - rw_cmd = PRO_READ_DATA; - } else { - rw_tpc = PRO_WRITE_LONG_DATA; - rw_cmd = PRO_WRITE_DATA; - } - } else { - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - rw_tpc = PRO_READ_QUAD_DATA; - rw_cmd = PRO_READ_2K_DATA; - } else { - rw_tpc = PRO_WRITE_QUAD_DATA; - rw_cmd = PRO_WRITE_2K_DATA; - } - mode_2k = true; - } - } else { - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - rw_tpc = PRO_READ_LONG_DATA; - rw_cmd = PRO_READ_DATA; - } else { - rw_tpc = PRO_WRITE_LONG_DATA; - rw_cmd = PRO_WRITE_DATA; - } - } - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (srb->sc_data_direction == DMA_FROM_DEVICE) - trans_mode = MS_TM_AUTO_READ; - else - trans_mode = MS_TM_AUTO_WRITE; - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &val); - if (retval) - return retval; - - if (ms_card->seq_mode) { - if (ms_card->pre_dir != srb->sc_data_direction || - ((ms_card->pre_sec_addr + ms_card->pre_sec_cnt) != - start_sector) || - (mode_2k && (ms_card->seq_mode & MODE_512_SEQ)) || - (!mode_2k && (ms_card->seq_mode & MODE_2K_SEQ)) || - !(val & MS_INT_BREQ) || - ((ms_card->total_sec_cnt + sector_cnt) > 0xFE00)) { - ms_card->seq_mode = 0; - ms_card->total_sec_cnt = 0; - if (val & MS_INT_BREQ) { - retval = ms_send_cmd(chip, PRO_STOP, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_write_register(chip, RBCTL, RB_FLUSH, - RB_FLUSH); - } - } - } - - if (!ms_card->seq_mode) { - ms_card->total_sec_cnt = 0; - if (sector_cnt >= SEQ_START_CRITERIA) { - if ((ms_card->capacity - start_sector) > 0xFE00) - count = 0xFE00; - else - count = (u16)(ms_card->capacity - start_sector); - - if (count > sector_cnt) { - if (mode_2k) - ms_card->seq_mode = MODE_2K_SEQ; - else - ms_card->seq_mode = MODE_512_SEQ; - } - } else { - count = sector_cnt; - } - retval = mspro_set_rw_cmd(chip, start_sector, count, rw_cmd); - if (retval != STATUS_SUCCESS) { - ms_card->seq_mode = 0; - return STATUS_FAIL; - } - } - - retval = ms_transfer_data(chip, trans_mode, rw_tpc, sector_cnt, - WAIT_INT, mode_2k, scsi_sg_count(srb), - scsi_sglist(srb), scsi_bufflen(srb)); - if (retval != STATUS_SUCCESS) { - ms_card->seq_mode = 0; - rtsx_read_register(chip, MS_TRANS_CFG, &val); - rtsx_clear_ms_error(chip); - - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - chip->rw_need_retry = 0; - dev_dbg(rtsx_dev(chip), "No card exist, exit %s\n", - __func__); - return STATUS_FAIL; - } - - if (val & MS_INT_BREQ) - ms_send_cmd(chip, PRO_STOP, WAIT_INT); - - if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) { - dev_dbg(rtsx_dev(chip), "MSPro CRC error, tune clock!\n"); - chip->rw_need_retry = 1; - ms_auto_tune_clock(chip); - } - - return retval; - } - - if (ms_card->seq_mode) { - ms_card->pre_sec_addr = start_sector; - ms_card->pre_sec_cnt = sector_cnt; - ms_card->pre_dir = srb->sc_data_direction; - ms_card->total_sec_cnt += sector_cnt; - } - - return STATUS_SUCCESS; -} - -static int mspro_read_format_progress(struct rtsx_chip *chip, - const int short_data_len) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u32 total_progress, cur_progress; - u8 cnt, tmp; - u8 data[8]; - - dev_dbg(rtsx_dev(chip), "%s, short_data_len = %d\n", __func__, - short_data_len); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp); - if (retval != STATUS_SUCCESS) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - if (!(tmp & MS_INT_BREQ)) { - if ((tmp & (MS_INT_CED | MS_INT_BREQ | MS_INT_CMDNK | - MS_INT_ERR)) == MS_INT_CED) { - ms_card->format_status = FORMAT_SUCCESS; - return STATUS_SUCCESS; - } - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - if (short_data_len >= 256) - cnt = 0; - else - cnt = (u8)short_data_len; - - retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT, - MS_NO_CHECK_INT); - if (retval != STATUS_SUCCESS) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, cnt, WAIT_INT, - data, 8); - if (retval != STATUS_SUCCESS) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - total_progress = (data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | data[3]; - cur_progress = (data[4] << 24) | (data[5] << 16) | - (data[6] << 8) | data[7]; - - dev_dbg(rtsx_dev(chip), "total_progress = %d, cur_progress = %d\n", - total_progress, cur_progress); - - if (total_progress == 0) { - ms_card->progress = 0; - } else { - u64 ulltmp = (u64)cur_progress * (u64)65535; - - do_div(ulltmp, total_progress); - ms_card->progress = (u16)ulltmp; - } - dev_dbg(rtsx_dev(chip), "progress = %d\n", ms_card->progress); - - for (i = 0; i < 5000; i++) { - retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp); - if (retval != STATUS_SUCCESS) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - if (tmp & (MS_INT_CED | MS_INT_CMDNK | - MS_INT_BREQ | MS_INT_ERR)) - break; - - wait_timeout(1); - } - - retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT, 0); - if (retval != STATUS_SUCCESS) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - if (i == 5000) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - if (tmp & (MS_INT_CMDNK | MS_INT_ERR)) { - ms_card->format_status = FORMAT_FAIL; - return STATUS_FAIL; - } - - if (tmp & MS_INT_CED) { - ms_card->format_status = FORMAT_SUCCESS; - ms_card->pro_under_formatting = 0; - } else if (tmp & MS_INT_BREQ) { - ms_card->format_status = FORMAT_IN_PROGRESS; - } else { - ms_card->format_status = FORMAT_FAIL; - ms_card->pro_under_formatting = 0; - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -void mspro_polling_format_status(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int i; - - if (ms_card->pro_under_formatting && - (rtsx_get_stat(chip) != RTSX_STAT_SS)) { - rtsx_set_stat(chip, RTSX_STAT_RUN); - - for (i = 0; i < 65535; i++) { - mspro_read_format_progress(chip, MS_SHORT_DATA_LEN); - if (ms_card->format_status != FORMAT_IN_PROGRESS) - break; - } - } -} - -int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip, - int short_data_len, bool quick_format) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 buf[8], tmp; - u16 para; - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_set_rw_reg_addr(chip, 0x00, 0x00, PRO_TPC_PARM, 0x01); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - memset(buf, 0, 2); - switch (short_data_len) { - case 32: - buf[0] = 0; - break; - case 64: - buf[0] = 1; - break; - case 128: - buf[0] = 2; - break; - case 256: - default: - buf[0] = 3; - break; - } - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, PRO_WRITE_REG, 1, - NO_WAIT_INT, buf, 2); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - if (quick_format) - para = 0x0000; - else - para = 0x0001; - - retval = mspro_set_rw_cmd(chip, 0, para, PRO_FORMAT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp); - if (retval) - return retval; - - if (tmp & (MS_INT_CMDNK | MS_INT_ERR)) - return STATUS_FAIL; - - if ((tmp & (MS_INT_BREQ | MS_INT_CED)) == MS_INT_BREQ) { - ms_card->pro_under_formatting = 1; - ms_card->progress = 0; - ms_card->format_status = FORMAT_IN_PROGRESS; - return STATUS_SUCCESS; - } - - if (tmp & MS_INT_CED) { - ms_card->pro_under_formatting = 0; - ms_card->progress = 0; - ms_card->format_status = FORMAT_SUCCESS; - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_NO_SENSE); - return STATUS_SUCCESS; - } - - return STATUS_FAIL; -} - -static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk, - u16 log_blk, u8 start_page, u8 end_page, - u8 *buf, unsigned int *index, - unsigned int *offset) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 extra[MS_EXTRA_SIZE], page_addr, val, trans_cfg, data[6]; - u8 *ptr; - - retval = ms_read_extra_data(chip, phy_blk, start_page, - extra, MS_EXTRA_SIZE); - if (retval == STATUS_SUCCESS) { - if ((extra[1] & 0x30) != 0x30) { - ms_set_err_code(chip, MS_FLASH_READ_ERROR); - return STATUS_FAIL; - } - } - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(phy_blk >> 8); - data[3] = (u8)phy_blk; - data[4] = 0; - data[5] = start_page; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, - data, 6); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ptr = buf; - - for (page_addr = start_page; page_addr < end_page; page_addr++) { - ms_set_err_code(chip, MS_NO_ERROR); - - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - if (val & INT_REG_ERR) { - if (val & INT_REG_BREQ) { - retval = ms_read_status_reg(chip); - if (retval != STATUS_SUCCESS) { - if (!(chip->card_wp & MS_CARD)) { - reset_ms(chip); - ms_set_page_status - (log_blk, set_PS_NG, - extra, - MS_EXTRA_SIZE); - ms_write_extra_data - (chip, phy_blk, - page_addr, extra, - MS_EXTRA_SIZE); - } - ms_set_err_code(chip, - MS_FLASH_READ_ERROR); - return STATUS_FAIL; - } - } else { - ms_set_err_code(chip, MS_FLASH_READ_ERROR); - return STATUS_FAIL; - } - } else { - if (!(val & INT_REG_BREQ)) { - ms_set_err_code(chip, MS_BREQ_ERROR); - return STATUS_FAIL; - } - } - - if (page_addr == (end_page - 1)) { - if (!(val & INT_REG_CED)) { - retval = ms_send_cmd(chip, BLOCK_END, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, - &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!(val & INT_REG_CED)) { - ms_set_err_code(chip, MS_FLASH_READ_ERROR); - return STATUS_FAIL; - } - - trans_cfg = NO_WAIT_INT; - } else { - trans_cfg = WAIT_INT; - } - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, READ_PAGE_DATA); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, - 0xFF, trans_cfg); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, RING_BUFFER); - - trans_dma_enable(DMA_FROM_DEVICE, chip, 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF, - MS_TRANSFER_START | MS_TM_NORMAL_READ); - rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data_partial(chip, MS_CARD, ptr, 512, - scsi_sg_count(chip->srb), - index, offset, - DMA_FROM_DEVICE, - chip->ms_timeout); - if (retval < 0) { - if (retval == -ETIMEDOUT) { - ms_set_err_code(chip, MS_TO_ERROR); - rtsx_clear_ms_error(chip); - return STATUS_TIMEDOUT; - } - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &val); - if (retval != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_TO_ERROR); - rtsx_clear_ms_error(chip); - return STATUS_TIMEDOUT; - } - if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) { - ms_set_err_code(chip, MS_CRC16_ERROR); - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - } - - if (scsi_sg_count(chip->srb) == 0) - ptr += 512; - } - - return STATUS_SUCCESS; -} - -static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk, - u16 new_blk, u16 log_blk, u8 start_page, - u8 end_page, u8 *buf, unsigned int *index, - unsigned int *offset) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, i; - u8 page_addr, val, data[16]; - u8 *ptr; - - if (!start_page) { - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, 7); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(old_blk >> 8); - data[3] = (u8)old_blk; - data[4] = 0x80; - data[5] = 0; - data[6] = 0xEF; - data[7] = 0xFF; - - retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT, - data, 8); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT, 1, - NO_WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = ms_set_rw_reg_addr(chip, OVERWRITE_FLAG, MS_EXTRA_SIZE, - SYSTEM_PARAM, (6 + MS_EXTRA_SIZE)); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ms_set_err_code(chip, MS_NO_ERROR); - - if (CHK_MS4BIT(ms_card)) - data[0] = 0x88; - else - data[0] = 0x80; - - data[1] = 0; - data[2] = (u8)(new_blk >> 8); - data[3] = (u8)new_blk; - if ((end_page - start_page) == 1) - data[4] = 0x20; - else - data[4] = 0; - - data[5] = start_page; - data[6] = 0xF8; - data[7] = 0xFF; - data[8] = (u8)(log_blk >> 8); - data[9] = (u8)log_blk; - - for (i = 0x0A; i < 0x10; i++) - data[i] = 0xFF; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, WRITE_REG, 6 + MS_EXTRA_SIZE, - NO_WAIT_INT, data, 16); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - ptr = buf; - for (page_addr = start_page; page_addr < end_page; page_addr++) { - ms_set_err_code(chip, MS_NO_ERROR); - - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - ms_set_err_code(chip, MS_NO_CARD); - return STATUS_FAIL; - } - - if (val & INT_REG_CMDNK) { - ms_set_err_code(chip, MS_CMD_NK); - return STATUS_FAIL; - } - if (val & INT_REG_ERR) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - if (!(val & INT_REG_BREQ)) { - ms_set_err_code(chip, MS_BREQ_ERROR); - return STATUS_FAIL; - } - - udelay(30); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, - 0xFF, WRITE_PAGE_DATA); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, - 0xFF, WAIT_INT); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, RING_BUFFER); - - trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF, - MS_TRANSFER_START | MS_TM_NORMAL_WRITE); - rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data_partial(chip, MS_CARD, ptr, 512, - scsi_sg_count(chip->srb), - index, offset, - DMA_TO_DEVICE, - chip->ms_timeout); - if (retval < 0) { - ms_set_err_code(chip, MS_TO_ERROR); - rtsx_clear_ms_error(chip); - - if (retval == -ETIMEDOUT) - return STATUS_TIMEDOUT; - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if ((end_page - start_page) == 1) { - if (!(val & INT_REG_CED)) { - ms_set_err_code(chip, MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } else { - if (page_addr == (end_page - 1)) { - if (!(val & INT_REG_CED)) { - retval = ms_send_cmd(chip, BLOCK_END, - WAIT_INT); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, GET_INT, 1, - NO_WAIT_INT, &val, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - if (page_addr == (end_page - 1) || - page_addr == ms_card->page_off) { - if (!(val & INT_REG_CED)) { - ms_set_err_code(chip, - MS_FLASH_WRITE_ERROR); - return STATUS_FAIL; - } - } - } - - if (scsi_sg_count(chip->srb) == 0) - ptr += 512; - } - - return STATUS_SUCCESS; -} - -static int ms_finish_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk, - u16 log_blk, u8 page_off) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval, seg_no; - - retval = ms_copy_page(chip, old_blk, new_blk, log_blk, - page_off, ms_card->page_off + 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - seg_no = old_blk >> 9; - - if (MS_TST_BAD_BLOCK_FLG(ms_card)) { - MS_CLR_BAD_BLOCK_FLG(ms_card); - ms_set_bad_block(chip, old_blk); - } else { - retval = ms_erase_block(chip, old_blk); - if (retval == STATUS_SUCCESS) - ms_set_unused_block(chip, old_blk); - } - - ms_set_l2p_tbl(chip, seg_no, log_blk - ms_start_idx[seg_no], new_blk); - - return STATUS_SUCCESS; -} - -static int ms_prepare_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk, - u16 log_blk, u8 start_page) -{ - int retval; - - if (start_page) { - retval = ms_copy_page(chip, old_blk, new_blk, log_blk, - 0, start_page); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -#ifdef MS_DELAY_WRITE -int ms_delay_write(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - struct ms_delay_write_tag *delay_write = &ms_card->delay_write; - int retval; - - if (delay_write->delay_write_flag) { - retval = ms_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - delay_write->delay_write_flag = 0; - retval = ms_finish_write(chip, - delay_write->old_phyblock, - delay_write->new_phyblock, - delay_write->logblock, - delay_write->pageoff); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} -#endif - -static inline void ms_rw_fail(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - if (srb->sc_data_direction == DMA_FROM_DEVICE) - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - else - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); -} - -static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 start_sector, u16 sector_cnt) -{ - struct ms_info *ms_card = &chip->ms_card; - unsigned int lun = SCSI_LUN(srb); - int retval, seg_no; - unsigned int index = 0, offset = 0; - u16 old_blk = 0, new_blk = 0, log_blk, total_sec_cnt = sector_cnt; - u8 start_page, end_page = 0, page_cnt; - u8 *ptr; -#ifdef MS_DELAY_WRITE - struct ms_delay_write_tag *delay_write = &ms_card->delay_write; -#endif - - ms_set_err_code(chip, MS_NO_ERROR); - - ms_card->cleanup_counter = 0; - - ptr = (u8 *)scsi_sglist(srb); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) { - ms_rw_fail(srb, chip); - return STATUS_FAIL; - } - - log_blk = (u16)(start_sector >> ms_card->block_shift); - start_page = (u8)(start_sector & ms_card->page_off); - - for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1; seg_no++) { - if (log_blk < ms_start_idx[seg_no + 1]) - break; - } - - if (ms_card->segment[seg_no].build_flag == 0) { - retval = ms_build_l2p_tbl(chip, seg_no); - if (retval != STATUS_SUCCESS) { - chip->card_fail |= MS_CARD; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { -#ifdef MS_DELAY_WRITE - if (delay_write->delay_write_flag && - delay_write->logblock == log_blk && - start_page > delay_write->pageoff) { - delay_write->delay_write_flag = 0; - retval = ms_copy_page(chip, - delay_write->old_phyblock, - delay_write->new_phyblock, - log_blk, - delay_write->pageoff, start_page); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } - old_blk = delay_write->old_phyblock; - new_blk = delay_write->new_phyblock; - } else if (delay_write->delay_write_flag && - (delay_write->logblock == log_blk) && - (start_page == delay_write->pageoff)) { - delay_write->delay_write_flag = 0; - old_blk = delay_write->old_phyblock; - new_blk = delay_write->new_phyblock; - } else { - retval = ms_delay_write(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } -#endif - old_blk = ms_get_l2p_tbl - (chip, seg_no, - log_blk - ms_start_idx[seg_no]); - new_blk = ms_get_unused_block(chip, seg_no); - if (old_blk == 0xFFFF || new_blk == 0xFFFF) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } - - retval = ms_prepare_write(chip, old_blk, new_blk, - log_blk, start_page); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, MS_CARD) != - STATUS_SUCCESS) { - set_sense_type - (chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } -#ifdef MS_DELAY_WRITE - } -#endif - } else { -#ifdef MS_DELAY_WRITE - retval = ms_delay_write(chip); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return STATUS_FAIL; - } -#endif - old_blk = ms_get_l2p_tbl(chip, seg_no, - log_blk - ms_start_idx[seg_no]); - if (old_blk == 0xFFFF) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return STATUS_FAIL; - } - } - - dev_dbg(rtsx_dev(chip), "seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n", - seg_no, old_blk, new_blk); - - while (total_sec_cnt) { - if ((start_page + total_sec_cnt) > (ms_card->page_off + 1)) - end_page = ms_card->page_off + 1; - else - end_page = start_page + (u8)total_sec_cnt; - - page_cnt = end_page - start_page; - - dev_dbg(rtsx_dev(chip), "start_page = %d, end_page = %d, page_cnt = %d\n", - start_page, end_page, page_cnt); - - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - retval = ms_read_multiple_pages(chip, - old_blk, log_blk, - start_page, end_page, - ptr, &index, &offset); - } else { - retval = ms_write_multiple_pages(chip, old_blk, new_blk, - log_blk, start_page, - end_page, ptr, &index, - &offset); - } - - if (retval != STATUS_SUCCESS) { - toggle_gpio(chip, 1); - if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - ms_rw_fail(srb, chip); - return STATUS_FAIL; - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { - if (end_page == (ms_card->page_off + 1)) { - retval = ms_erase_block(chip, old_blk); - if (retval == STATUS_SUCCESS) - ms_set_unused_block(chip, old_blk); - - ms_set_l2p_tbl(chip, seg_no, - log_blk - ms_start_idx[seg_no], - new_blk); - } - } - - total_sec_cnt -= page_cnt; - if (scsi_sg_count(srb) == 0) - ptr += page_cnt * 512; - - if (total_sec_cnt == 0) - break; - - log_blk++; - - for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1; - seg_no++) { - if (log_blk < ms_start_idx[seg_no + 1]) - break; - } - - if (ms_card->segment[seg_no].build_flag == 0) { - retval = ms_build_l2p_tbl(chip, seg_no); - if (retval != STATUS_SUCCESS) { - chip->card_fail |= MS_CARD; - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - } - - old_blk = ms_get_l2p_tbl(chip, seg_no, - log_blk - ms_start_idx[seg_no]); - if (old_blk == 0xFFFF) { - ms_rw_fail(srb, chip); - return STATUS_FAIL; - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { - new_blk = ms_get_unused_block(chip, seg_no); - if (new_blk == 0xFFFF) { - ms_rw_fail(srb, chip); - return STATUS_FAIL; - } - } - - dev_dbg(rtsx_dev(chip), "seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n", - seg_no, old_blk, new_blk); - - start_page = 0; - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { - if (end_page < (ms_card->page_off + 1)) { -#ifdef MS_DELAY_WRITE - delay_write->delay_write_flag = 1; - delay_write->old_phyblock = old_blk; - delay_write->new_phyblock = new_blk; - delay_write->logblock = log_blk; - delay_write->pageoff = end_page; -#else - retval = ms_finish_write(chip, old_blk, new_blk, - log_blk, end_page); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, MS_CARD) != - STATUS_SUCCESS) { - set_sense_type - (chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - - ms_rw_fail(srb, chip); - return STATUS_FAIL; - } -#endif - } - } - - scsi_set_resid(srb, 0); - - return STATUS_SUCCESS; -} - -int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 start_sector, u16 sector_cnt) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - - if (CHK_MSPRO(ms_card)) - retval = mspro_rw_multi_sector(srb, chip, start_sector, - sector_cnt); - else - retval = ms_rw_multi_sector(srb, chip, start_sector, - sector_cnt); - - return retval; -} - -void ms_free_l2p_tbl(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int i = 0; - - if (ms_card->segment) { - for (i = 0; i < ms_card->segment_cnt; i++) { - vfree(ms_card->segment[i].l2p_table); - ms_card->segment[i].l2p_table = NULL; - vfree(ms_card->segment[i].free_table); - ms_card->segment[i].free_table = NULL; - } - vfree(ms_card->segment); - ms_card->segment = NULL; - } -} - -#ifdef SUPPORT_MAGIC_GATE - -#ifdef READ_BYTES_WAIT_INT -static int ms_poll_int(struct rtsx_chip *chip) -{ - int retval; - u8 val; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANS_CFG, MS_INT_CED, MS_INT_CED); - - retval = rtsx_send_cmd(chip, MS_CARD, 5000); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - val = *rtsx_get_cmd_data(chip); - if (val & MS_INT_ERR) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} -#endif - -#ifdef MS_SAMPLE_INT_ERR -static int check_ms_err(struct rtsx_chip *chip) -{ - int retval; - u8 val; - - retval = rtsx_read_register(chip, MS_TRANSFER, &val); - if (retval != STATUS_SUCCESS) - return 1; - if (val & MS_TRANSFER_ERR) - return 1; - - retval = rtsx_read_register(chip, MS_TRANS_CFG, &val); - if (retval != STATUS_SUCCESS) - return 1; - - if (val & (MS_INT_ERR | MS_INT_CMDNK)) - return 1; - - return 0; -} -#else -static int check_ms_err(struct rtsx_chip *chip) -{ - int retval; - u8 val; - - retval = rtsx_read_register(chip, MS_TRANSFER, &val); - if (retval != STATUS_SUCCESS) - return 1; - if (val & MS_TRANSFER_ERR) - return 1; - - return 0; -} -#endif - -static int mg_send_ex_cmd(struct rtsx_chip *chip, u8 cmd, u8 entry_num) -{ - int retval, i; - u8 data[8]; - - data[0] = cmd; - data[1] = 0; - data[2] = 0; - data[3] = 0; - data[4] = 0; - data[5] = 0; - data[6] = entry_num; - data[7] = 0; - - for (i = 0; i < MS_MAX_RETRY_COUNT; i++) { - retval = ms_write_bytes(chip, PRO_EX_SET_CMD, 7, WAIT_INT, - data, 8); - if (retval == STATUS_SUCCESS) - break; - } - if (i == MS_MAX_RETRY_COUNT) - return STATUS_FAIL; - - if (check_ms_err(chip)) { - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int mg_set_tpc_para_sub(struct rtsx_chip *chip, int type, - u8 mg_entry_num) -{ - int retval; - u8 buf[6]; - - if (type == 0) - retval = ms_set_rw_reg_addr(chip, 0, 0, PRO_TPC_PARM, 1); - else - retval = ms_set_rw_reg_addr(chip, 0, 0, PRO_DATA_COUNT1, 6); - - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf[0] = 0; - buf[1] = 0; - if (type == 1) { - buf[2] = 0; - buf[3] = 0; - buf[4] = 0; - buf[5] = mg_entry_num; - } - retval = ms_write_bytes(chip, PRO_WRITE_REG, (type == 0) ? 1 : 6, - NO_WAIT_INT, buf, 6); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - int i; - unsigned int lun = SCSI_LUN(srb); - u8 buf1[32], buf2[12]; - - if (scsi_bufflen(srb) < 12) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return STATUS_FAIL; - } - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = mg_send_ex_cmd(chip, MG_SET_LID, 0); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB); - return STATUS_FAIL; - } - - memset(buf1, 0, 32); - rtsx_stor_get_xfer_buf(buf2, min_t(int, 12, scsi_bufflen(srb)), srb); - for (i = 0; i < 8; i++) - buf1[8 + i] = buf2[4 + i]; - - retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT, - buf1, 32); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB); - return STATUS_FAIL; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB); - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - int bufflen; - unsigned int lun = SCSI_LUN(srb); - u8 *buf = NULL; - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf = kmalloc(1540, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - buf[0] = 0x04; - buf[1] = 0x1A; - buf[2] = 0x00; - buf[3] = 0x00; - - retval = mg_send_ex_cmd(chip, MG_GET_LEKB, 0); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - goto free_buffer; - } - - retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA, - 3, WAIT_INT, 0, 0, buf + 4, 1536); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - rtsx_clear_ms_error(chip); - goto free_buffer; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - rtsx_clear_ms_error(chip); - retval = STATUS_FAIL; - goto free_buffer; - } - - bufflen = min_t(int, 1052, scsi_bufflen(srb)); - rtsx_stor_set_xfer_buf(buf, bufflen, srb); - -free_buffer: - kfree(buf); - return retval; -} - -int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - int bufflen; - int i; - unsigned int lun = SCSI_LUN(srb); - u8 buf[32]; - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = mg_send_ex_cmd(chip, MG_GET_ID, 0); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, 32, WAIT_INT, - buf, 32); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return STATUS_FAIL; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - - memcpy(ms_card->magic_gate_id, buf, 16); - -#ifdef READ_BYTES_WAIT_INT - retval = ms_poll_int(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return STATUS_FAIL; - } -#endif - - retval = mg_send_ex_cmd(chip, MG_SET_RD, 0); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return STATUS_FAIL; - } - - bufflen = min_t(int, 12, scsi_bufflen(srb)); - rtsx_stor_get_xfer_buf(buf, bufflen, srb); - - for (i = 0; i < 8; i++) - buf[i] = buf[4 + i]; - - for (i = 0; i < 24; i++) - buf[8 + i] = 0; - - retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, - 32, WAIT_INT, buf, 32); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return STATUS_FAIL; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - - ms_card->mg_auth = 0; - - return STATUS_SUCCESS; -} - -int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - int bufflen; - unsigned int lun = SCSI_LUN(srb); - u8 buf1[32], buf2[36]; - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = mg_send_ex_cmd(chip, MG_MAKE_RMS, 0); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - return STATUS_FAIL; - } - - retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, 32, WAIT_INT, - buf1, 32); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - return STATUS_FAIL; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - - buf2[0] = 0x00; - buf2[1] = 0x22; - buf2[2] = 0x00; - buf2[3] = 0x00; - - memcpy(buf2 + 4, ms_card->magic_gate_id, 16); - memcpy(buf2 + 20, buf1, 16); - - bufflen = min_t(int, 36, scsi_bufflen(srb)); - rtsx_stor_set_xfer_buf(buf2, bufflen, srb); - -#ifdef READ_BYTES_WAIT_INT - retval = ms_poll_int(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - return STATUS_FAIL; - } -#endif - - return STATUS_SUCCESS; -} - -int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - int i; - int bufflen; - unsigned int lun = SCSI_LUN(srb); - u8 buf[32]; - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = mg_send_ex_cmd(chip, MG_MAKE_KSE, 0); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - return STATUS_FAIL; - } - - bufflen = min_t(int, 12, scsi_bufflen(srb)); - rtsx_stor_get_xfer_buf(buf, bufflen, srb); - - for (i = 0; i < 8; i++) - buf[i] = buf[4 + i]; - - for (i = 0; i < 24; i++) - buf[8 + i] = 0; - - retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT, - buf, 32); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - return STATUS_FAIL; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN); - rtsx_clear_ms_error(chip); - return STATUS_FAIL; - } - - ms_card->mg_auth = 1; - - return STATUS_SUCCESS; -} - -int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - int bufflen; - unsigned int lun = SCSI_LUN(srb); - u8 *buf = NULL; - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf = kmalloc(1028, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - buf[0] = 0x04; - buf[1] = 0x02; - buf[2] = 0x00; - buf[3] = 0x00; - - retval = mg_send_ex_cmd(chip, MG_GET_IBD, ms_card->mg_entry_num); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - goto free_buffer; - } - - retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA, - 2, WAIT_INT, 0, 0, buf + 4, 1024); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - rtsx_clear_ms_error(chip); - goto free_buffer; - } - if (check_ms_err(chip)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - rtsx_clear_ms_error(chip); - retval = STATUS_FAIL; - goto free_buffer; - } - - bufflen = min_t(int, 1028, scsi_bufflen(srb)); - rtsx_stor_set_xfer_buf(buf, bufflen, srb); - -free_buffer: - kfree(buf); - return retval; -} - -int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - int bufflen; -#ifdef MG_SET_ICV_SLOW - int i; -#endif - unsigned int lun = SCSI_LUN(srb); - u8 *buf = NULL; - - ms_cleanup_work(chip); - - retval = ms_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf = kmalloc(1028, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - bufflen = min_t(int, 1028, scsi_bufflen(srb)); - rtsx_stor_get_xfer_buf(buf, bufflen, srb); - - retval = mg_send_ex_cmd(chip, MG_SET_IBD, ms_card->mg_entry_num); - if (retval != STATUS_SUCCESS) { - if (ms_card->mg_auth == 0) { - if ((buf[5] & 0xC0) != 0) - set_sense_type - (chip, lun, - SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB); - else - set_sense_type(chip, lun, - SENSE_TYPE_MG_WRITE_ERR); - } else { - set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR); - } - goto set_ICV_finish; - } - -#ifdef MG_SET_ICV_SLOW - for (i = 0; i < 2; i++) { - udelay(50); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, - 0xFF, PRO_WRITE_LONG_DATA); - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, WAIT_INT); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, RING_BUFFER); - - trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF, - MS_TRANSFER_START | MS_TM_NORMAL_WRITE); - rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data(chip, MS_CARD, buf + 4 + i * 512, - 512, 0, DMA_TO_DEVICE, 3000); - if (retval < 0 || check_ms_err(chip)) { - rtsx_clear_ms_error(chip); - if (ms_card->mg_auth == 0) { - if ((buf[5] & 0xC0) != 0) - set_sense_type - (chip, lun, - SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB); - else - set_sense_type(chip, lun, - SENSE_TYPE_MG_WRITE_ERR); - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MG_WRITE_ERR); - } - retval = STATUS_FAIL; - goto set_ICV_finish; - } - } -#else - retval = ms_transfer_data(chip, MS_TM_AUTO_WRITE, PRO_WRITE_LONG_DATA, - 2, WAIT_INT, 0, 0, buf + 4, 1024); - if (retval != STATUS_SUCCESS || check_ms_err(chip)) { - rtsx_clear_ms_error(chip); - if (ms_card->mg_auth == 0) { - if ((buf[5] & 0xC0) != 0) - set_sense_type - (chip, lun, - SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB); - else - set_sense_type(chip, lun, - SENSE_TYPE_MG_WRITE_ERR); - } else { - set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR); - } - goto set_ICV_finish; - } -#endif - -set_ICV_finish: - kfree(buf); - return retval; -} - -#endif /* SUPPORT_MAGIC_GATE */ - -void ms_cleanup_work(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - - if (CHK_MSPRO(ms_card)) { - if (ms_card->seq_mode) { - dev_dbg(rtsx_dev(chip), "MS Pro: stop transmission\n"); - mspro_stop_seq_mode(chip); - ms_card->cleanup_counter = 0; - } - if (CHK_MSHG(ms_card)) { - rtsx_write_register(chip, MS_CFG, - MS_2K_SECTOR_MODE, 0x00); - } - } -#ifdef MS_DELAY_WRITE - else if ((!CHK_MSPRO(ms_card)) && - ms_card->delay_write.delay_write_flag) { - dev_dbg(rtsx_dev(chip), "MS: delay write\n"); - ms_delay_write(chip); - ms_card->cleanup_counter = 0; - } -#endif -} - -int ms_power_off_card3v3(struct rtsx_chip *chip) -{ - int retval; - - retval = disable_card_clock(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (chip->asic_code) { - retval = ms_pull_ctl_disable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, - FPGA_MS_PULL_CTL_BIT | 0x20, - FPGA_MS_PULL_CTL_BIT); - if (retval) - return retval; - } - retval = rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0); - if (retval) - return retval; - - if (!chip->ft2_fast_mode) { - retval = card_power_off(chip, MS_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int release_ms_card(struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - int retval; - -#ifdef MS_DELAY_WRITE - ms_card->delay_write.delay_write_flag = 0; -#endif - ms_card->pro_under_formatting = 0; - - chip->card_ready &= ~MS_CARD; - chip->card_fail &= ~MS_CARD; - chip->card_wp &= ~MS_CARD; - - ms_free_l2p_tbl(chip); - - memset(ms_card->raw_sys_info, 0, 96); -#ifdef SUPPORT_PCGL_1P18 - memset(ms_card->raw_model_name, 0, 48); -#endif - - retval = ms_power_off_card3v3(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} diff --git a/drivers/staging/rts5208/ms.h b/drivers/staging/rts5208/ms.h deleted file mode 100644 index 33bda9ce36b67..0000000000000 --- a/drivers/staging/rts5208/ms.h +++ /dev/null @@ -1,214 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_MS_H -#define __REALTEK_RTSX_MS_H - -#define MS_DELAY_WRITE - -#define MS_MAX_RETRY_COUNT 3 - -#define MS_EXTRA_SIZE 0x9 - -#define WRT_PRTCT 0x01 - -/* Error Code */ -#define MS_NO_ERROR 0x00 -#define MS_CRC16_ERROR 0x80 -#define MS_TO_ERROR 0x40 -#define MS_NO_CARD 0x20 -#define MS_NO_MEMORY 0x10 -#define MS_CMD_NK 0x08 -#define MS_FLASH_READ_ERROR 0x04 -#define MS_FLASH_WRITE_ERROR 0x02 -#define MS_BREQ_ERROR 0x01 -#define MS_NOT_FOUND 0x03 - -/* Transfer Protocol Command */ -#define READ_PAGE_DATA 0x02 -#define READ_REG 0x04 -#define GET_INT 0x07 -#define WRITE_PAGE_DATA 0x0D -#define WRITE_REG 0x0B -#define SET_RW_REG_ADRS 0x08 -#define SET_CMD 0x0E - -#define PRO_READ_LONG_DATA 0x02 -#define PRO_READ_SHORT_DATA 0x03 -#define PRO_READ_REG 0x04 -#define PRO_READ_QUAD_DATA 0x05 -#define PRO_GET_INT 0x07 -#define PRO_WRITE_LONG_DATA 0x0D -#define PRO_WRITE_SHORT_DATA 0x0C -#define PRO_WRITE_QUAD_DATA 0x0A -#define PRO_WRITE_REG 0x0B -#define PRO_SET_RW_REG_ADRS 0x08 -#define PRO_SET_CMD 0x0E -#define PRO_EX_SET_CMD 0x09 - -#ifdef SUPPORT_MAGIC_GATE - -#define MG_GET_ID 0x40 -#define MG_SET_LID 0x41 -#define MG_GET_LEKB 0x42 -#define MG_SET_RD 0x43 -#define MG_MAKE_RMS 0x44 -#define MG_MAKE_KSE 0x45 -#define MG_SET_IBD 0x46 -#define MG_GET_IBD 0x47 - -#endif - -#ifdef XC_POWERCLASS -#define XC_CHG_POWER 0x16 -#endif - -#define BLOCK_READ 0xAA -#define BLOCK_WRITE 0x55 -#define BLOCK_END 0x33 -#define BLOCK_ERASE 0x99 -#define FLASH_STOP 0xCC - -#define SLEEP 0x5A -#define CLEAR_BUF 0xC3 -#define MS_RESET 0x3C - -#define PRO_READ_DATA 0x20 -#define PRO_WRITE_DATA 0x21 -#define PRO_READ_ATRB 0x24 -#define PRO_STOP 0x25 -#define PRO_ERASE 0x26 -#define PRO_READ_2K_DATA 0x27 -#define PRO_WRITE_2K_DATA 0x28 - -#define PRO_FORMAT 0x10 -#define PRO_SLEEP 0x11 - -#define INT_REG 0x01 -#define STATUS_REG0 0x02 -#define STATUS_REG1 0x03 - -#define SYSTEM_PARAM 0x10 -#define BLOCK_ADRS 0x11 -#define CMD_PARM 0x14 -#define PAGE_ADRS 0x15 - -#define OVERWRITE_FLAG 0x16 -#define MANAGEMEN_FLAG 0x17 -#define LOGICAL_ADRS 0x18 -#define RESERVE_AREA 0x1A - -#define PRO_INT_REG 0x01 -#define PRO_STATUS_REG 0x02 -#define PRO_TYPE_REG 0x04 -#define PRO_IF_mode_REG 0x05 -#define PRO_CATEGORY_REG 0x06 -#define PRO_CLASS_REG 0x07 - -#define PRO_SYSTEM_PARAM 0x10 -#define PRO_DATA_COUNT1 0x11 -#define PRO_DATA_COUNT0 0x12 -#define PRO_DATA_ADDR3 0x13 -#define PRO_DATA_ADDR2 0x14 -#define PRO_DATA_ADDR1 0x15 -#define PRO_DATA_ADDR0 0x16 - -#define PRO_TPC_PARM 0x17 -#define PRO_CMD_PARM 0x18 - -#define INT_REG_CED 0x80 -#define INT_REG_ERR 0x40 -#define INT_REG_BREQ 0x20 -#define INT_REG_CMDNK 0x01 - -#define BLOCK_BOOT 0xC0 -#define BLOCK_OK 0x80 -#define PAGE_OK 0x60 -#define DATA_COMPL 0x10 - -#define NOT_BOOT_BLOCK 0x4 -#define NOT_TRANSLATION_TABLE 0x8 - -#define HEADER_ID0 PPBUF_BASE2 -#define HEADER_ID1 (PPBUF_BASE2 + 1) -#define DISABLED_BLOCK0 (PPBUF_BASE2 + 0x170 + 4) -#define DISABLED_BLOCK1 (PPBUF_BASE2 + 0x170 + 5) -#define DISABLED_BLOCK2 (PPBUF_BASE2 + 0x170 + 6) -#define DISABLED_BLOCK3 (PPBUF_BASE2 + 0x170 + 7) -#define BLOCK_SIZE_0 (PPBUF_BASE2 + 0x1a0 + 2) -#define BLOCK_SIZE_1 (PPBUF_BASE2 + 0x1a0 + 3) -#define BLOCK_COUNT_0 (PPBUF_BASE2 + 0x1a0 + 4) -#define BLOCK_COUNT_1 (PPBUF_BASE2 + 0x1a0 + 5) -#define EBLOCK_COUNT_0 (PPBUF_BASE2 + 0x1a0 + 6) -#define EBLOCK_COUNT_1 (PPBUF_BASE2 + 0x1a0 + 7) -#define PAGE_SIZE_0 (PPBUF_BASE2 + 0x1a0 + 8) -#define PAGE_SIZE_1 (PPBUF_BASE2 + 0x1a0 + 9) - -#define MS_device_type (PPBUF_BASE2 + 0x1D8) - -#define MS_4bit_support (PPBUF_BASE2 + 0x1D3) - -#define set_PS_NG 1 -#define set_PS_error 0 - -#define PARALLEL_8BIT_IF 0x40 -#define PARALLEL_4BIT_IF 0x00 -#define SERIAL_IF 0x80 - -#define BUF_FULL 0x10 -#define BUF_EMPTY 0x20 - -#define MEDIA_BUSY 0x80 -#define FLASH_BUSY 0x40 -#define DATA_ERROR 0x20 -#define STS_UCDT 0x10 -#define EXTRA_ERROR 0x08 -#define STS_UCEX 0x04 -#define FLAG_ERROR 0x02 -#define STS_UCFG 0x01 - -#define MS_SHORT_DATA_LEN 32 - -#define FORMAT_SUCCESS 0 -#define FORMAT_FAIL 1 -#define FORMAT_IN_PROGRESS 2 - -#define MS_SET_BAD_BLOCK_FLG(ms_card) ((ms_card)->multi_flag |= 0x80) -#define MS_CLR_BAD_BLOCK_FLG(ms_card) ((ms_card)->multi_flag &= 0x7F) -#define MS_TST_BAD_BLOCK_FLG(ms_card) ((ms_card)->multi_flag & 0x80) - -void mspro_polling_format_status(struct rtsx_chip *chip); - -void mspro_stop_seq_mode(struct rtsx_chip *chip); -int reset_ms_card(struct rtsx_chip *chip); -int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 start_sector, u16 sector_cnt); -int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip, - int short_data_len, bool quick_format); -void ms_free_l2p_tbl(struct rtsx_chip *chip); -void ms_cleanup_work(struct rtsx_chip *chip); -int ms_power_off_card3v3(struct rtsx_chip *chip); -int release_ms_card(struct rtsx_chip *chip); -#ifdef MS_DELAY_WRITE -int ms_delay_write(struct rtsx_chip *chip); -#endif - -#ifdef SUPPORT_MAGIC_GATE -int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip); -#endif - -#endif /* __REALTEK_RTSX_MS_H */ diff --git a/drivers/staging/rts5208/rtsx.c b/drivers/staging/rts5208/rtsx.c deleted file mode 100644 index c4f54c311d054..0000000000000 --- a/drivers/staging/rts5208/rtsx.c +++ /dev/null @@ -1,987 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include -#include - -#include "rtsx.h" -#include "ms.h" -#include "sd.h" -#include "xd.h" - -MODULE_DESCRIPTION("Realtek PCI-Express card reader rts5208/rts5288 driver"); -MODULE_LICENSE("GPL"); - -static unsigned int delay_use = 1; -module_param(delay_use, uint, 0644); -MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); - -static int ss_en; -module_param(ss_en, int, 0644); -MODULE_PARM_DESC(ss_en, "enable selective suspend"); - -static int ss_interval = 50; -module_param(ss_interval, int, 0644); -MODULE_PARM_DESC(ss_interval, "Interval to enter ss state in seconds"); - -static int auto_delink_en; -module_param(auto_delink_en, int, 0644); -MODULE_PARM_DESC(auto_delink_en, "enable auto delink"); - -static unsigned char aspm_l0s_l1_en; -module_param(aspm_l0s_l1_en, byte, 0644); -MODULE_PARM_DESC(aspm_l0s_l1_en, "enable device aspm"); - -static int msi_en; -module_param(msi_en, int, 0644); -MODULE_PARM_DESC(msi_en, "enable msi"); - -static irqreturn_t rtsx_interrupt(int irq, void *dev_id); - -/*********************************************************************** - * Host functions - ***********************************************************************/ - -static const char *host_info(struct Scsi_Host *host) -{ - return "SCSI emulation for PCI-Express Mass Storage devices"; -} - -static int slave_alloc(struct scsi_device *sdev) -{ - /* - * Set the INQUIRY transfer length to 36. We don't use any of - * the extra data and many devices choke if asked for more or - * less than 36 bytes. - */ - sdev->inquiry_len = 36; - return 0; -} - -static int slave_configure(struct scsi_device *sdev) -{ - /* Set the SCSI level to at least 2. We'll leave it at 3 if that's - * what is originally reported. We need this to avoid confusing - * the SCSI layer with devices that report 0 or 1, but need 10-byte - * commands (ala ATAPI devices behind certain bridges, or devices - * which simply have broken INQUIRY data). - * - * NOTE: This means /dev/sg programs (ala cdrecord) will get the - * actual information. This seems to be the preference for - * programs like that. - * - * NOTE: This also means that /proc/scsi/scsi and sysfs may report - * the actual value or the modified one, depending on where the - * data comes from. - */ - if (sdev->scsi_level < SCSI_2) { - sdev->scsi_level = SCSI_2; - sdev->sdev_target->scsi_level = SCSI_2; - } - - return 0; -} - -/*********************************************************************** - * /proc/scsi/ functions - ***********************************************************************/ - -/* we use this macro to help us write into the buffer */ -#undef SPRINTF -#define SPRINTF(args...) \ - do { \ - if (pos < buffer + length) \ - pos += sprintf(pos, ## args); \ - } while (0) - -/* queue a command */ -/* This is always called with spin_lock_irq(host->host_lock) held */ -static int queuecommand_lck(struct scsi_cmnd *srb) -{ - void (*done)(struct scsi_cmnd *) = scsi_done; - struct rtsx_dev *dev = host_to_rtsx(srb->device->host); - struct rtsx_chip *chip = dev->chip; - - /* check for state-transition errors */ - if (chip->srb) { - dev_err(&dev->pci->dev, "Error: chip->srb = %p\n", - chip->srb); - return SCSI_MLQUEUE_HOST_BUSY; - } - - /* fail the command if we are disconnecting */ - if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) { - dev_info(&dev->pci->dev, "Fail command during disconnect\n"); - srb->result = DID_NO_CONNECT << 16; - done(srb); - return 0; - } - - /* enqueue the command and wake up the control thread */ - chip->srb = srb; - complete(&dev->cmnd_ready); - - return 0; -} - -static DEF_SCSI_QCMD(queuecommand) - -/*********************************************************************** - * Error handling functions - ***********************************************************************/ - -/* Command timeout and abort */ -static int command_abort(struct scsi_cmnd *srb) -{ - struct Scsi_Host *host = srb->device->host; - struct rtsx_dev *dev = host_to_rtsx(host); - struct rtsx_chip *chip = dev->chip; - - spin_lock_irq(host->host_lock); - - /* Is this command still active? */ - if (chip->srb != srb) { - spin_unlock_irq(host->host_lock); - dev_info(&dev->pci->dev, "-- nothing to abort\n"); - return FAILED; - } - - rtsx_set_stat(chip, RTSX_STAT_ABORT); - - spin_unlock_irq(host->host_lock); - - /* Wait for the aborted command to finish */ - wait_for_completion(&dev->notify); - - return SUCCESS; -} - -/* - * This invokes the transport reset mechanism to reset the state of the - * device - */ -static int device_reset(struct scsi_cmnd *srb) -{ - return SUCCESS; -} - -/* - * this defines our host template, with which we'll allocate hosts - */ - -static const struct scsi_host_template rtsx_host_template = { - /* basic userland interface stuff */ - .name = CR_DRIVER_NAME, - .proc_name = CR_DRIVER_NAME, - .info = host_info, - - /* command interface -- queued only */ - .queuecommand = queuecommand, - - /* error and abort handlers */ - .eh_abort_handler = command_abort, - .eh_device_reset_handler = device_reset, - - /* queue commands only, only one command per LUN */ - .can_queue = 1, - - /* unknown initiator id */ - .this_id = -1, - - .slave_alloc = slave_alloc, - .slave_configure = slave_configure, - - /* lots of sg segments can be handled */ - .sg_tablesize = SG_ALL, - - /* limit the total size of a transfer to 120 KB */ - .max_sectors = 240, - - /* - * Scatter-gather buffers (all but the last) must have a length - * divisible by the bulk maxpacket size. Otherwise a data packet - * would end up being short, causing a premature end to the data - * transfer. Since high-speed bulk pipes have a maxpacket size - * of 512, we'll use that as the scsi device queue's DMA alignment - * mask. Guaranteeing proper alignment of the first buffer will - * have the desired effect because, except at the beginning and - * the end, scatter-gather buffers follow page boundaries. - */ - .dma_alignment = 511, - - /* emulated HBA */ - .emulated = 1, - - /* we do our own delay after a device or bus reset */ - .skip_settle_delay = 1, - - /* module management */ - .module = THIS_MODULE -}; - -static int rtsx_acquire_irq(struct rtsx_dev *dev) -{ - struct rtsx_chip *chip = dev->chip; - - dev_info(&dev->pci->dev, "%s: chip->msi_en = %d, pci->irq = %d\n", - __func__, chip->msi_en, dev->pci->irq); - - if (request_irq(dev->pci->irq, rtsx_interrupt, - chip->msi_en ? 0 : IRQF_SHARED, - CR_DRIVER_NAME, dev)) { - dev_err(&dev->pci->dev, - "rtsx: unable to grab IRQ %d, disabling device\n", - dev->pci->irq); - return -1; - } - - dev->irq = dev->pci->irq; - pci_intx(dev->pci, !chip->msi_en); - - return 0; -} - -/* - * power management - */ -static int __maybe_unused rtsx_suspend(struct device *dev_d) -{ - struct pci_dev *pci = to_pci_dev(dev_d); - struct rtsx_dev *dev = pci_get_drvdata(pci); - struct rtsx_chip *chip; - - if (!dev) - return 0; - - /* lock the device pointers */ - mutex_lock(&dev->dev_mutex); - - chip = dev->chip; - - rtsx_do_before_power_down(chip, PM_S3); - - if (dev->irq >= 0) { - free_irq(dev->irq, (void *)dev); - dev->irq = -1; - } - - if (chip->msi_en) - pci_free_irq_vectors(pci); - - device_wakeup_enable(dev_d); - - /* unlock the device pointers */ - mutex_unlock(&dev->dev_mutex); - - return 0; -} - -static int __maybe_unused rtsx_resume(struct device *dev_d) -{ - struct pci_dev *pci = to_pci_dev(dev_d); - struct rtsx_dev *dev = pci_get_drvdata(pci); - struct rtsx_chip *chip; - - if (!dev) - return 0; - - chip = dev->chip; - - /* lock the device pointers */ - mutex_lock(&dev->dev_mutex); - - pci_set_master(pci); - - if (chip->msi_en) { - if (pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI) < 0) - chip->msi_en = 0; - } - - if (rtsx_acquire_irq(dev) < 0) { - /* unlock the device pointers */ - mutex_unlock(&dev->dev_mutex); - return -EIO; - } - - rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 0x00); - rtsx_init_chip(chip); - - /* unlock the device pointers */ - mutex_unlock(&dev->dev_mutex); - - return 0; -} - -static void rtsx_shutdown(struct pci_dev *pci) -{ - struct rtsx_dev *dev = pci_get_drvdata(pci); - struct rtsx_chip *chip; - - if (!dev) - return; - - chip = dev->chip; - - rtsx_do_before_power_down(chip, PM_S1); - - if (dev->irq >= 0) { - free_irq(dev->irq, (void *)dev); - dev->irq = -1; - } - - if (chip->msi_en) - pci_free_irq_vectors(pci); - - pci_disable_device(pci); -} - -static int rtsx_control_thread(void *__dev) -{ - struct rtsx_dev *dev = __dev; - struct rtsx_chip *chip = dev->chip; - struct Scsi_Host *host = rtsx_to_host(dev); - - for (;;) { - if (wait_for_completion_interruptible(&dev->cmnd_ready)) - break; - - /* lock the device pointers */ - mutex_lock(&dev->dev_mutex); - - /* if the device has disconnected, we are free to exit */ - if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) { - dev_info(&dev->pci->dev, "-- rtsx-control exiting\n"); - mutex_unlock(&dev->dev_mutex); - break; - } - - /* lock access to the state */ - spin_lock_irq(host->host_lock); - - /* has the command aborted ? */ - if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) { - chip->srb->result = DID_ABORT << 16; - goto skip_for_abort; - } - - spin_unlock_irq(host->host_lock); - - /* reject the command if the direction indicator - * is UNKNOWN - */ - if (chip->srb->sc_data_direction == DMA_BIDIRECTIONAL) { - dev_err(&dev->pci->dev, "UNKNOWN data direction\n"); - chip->srb->result = DID_ERROR << 16; - } else if (chip->srb->device->id) { - /* reject if target != 0 or if LUN is higher than - * the maximum known LUN - */ - dev_err(&dev->pci->dev, "Bad target number (%d:%d)\n", - chip->srb->device->id, - (u8)chip->srb->device->lun); - chip->srb->result = DID_BAD_TARGET << 16; - } else if (chip->srb->device->lun > chip->max_lun) { - dev_err(&dev->pci->dev, "Bad LUN (%d:%d)\n", - chip->srb->device->id, - (u8)chip->srb->device->lun); - chip->srb->result = DID_BAD_TARGET << 16; - } else { - /* we've got a command, let's do it! */ - scsi_show_command(chip); - rtsx_invoke_transport(chip->srb, chip); - } - - /* lock access to the state */ - spin_lock_irq(host->host_lock); - - /* did the command already complete because of a disconnect? */ - if (!chip->srb) - ; /* nothing to do */ - - /* indicate that the command is done */ - else if (chip->srb->result != DID_ABORT << 16) { - scsi_done(chip->srb); - } else { -skip_for_abort: - dev_err(&dev->pci->dev, "scsi command aborted\n"); - } - - if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) { - complete(&dev->notify); - - rtsx_set_stat(chip, RTSX_STAT_IDLE); - } - - /* finished working on this command */ - chip->srb = NULL; - spin_unlock_irq(host->host_lock); - - /* unlock the device pointers */ - mutex_unlock(&dev->dev_mutex); - } /* for (;;) */ - - /* notify the exit routine that we're actually exiting now - * - * complete()/wait_for_completion() is similar to up()/down(), - * except that complete() is safe in the case where the structure - * is getting deleted in a parallel mode of execution (i.e. just - * after the down() -- that's necessary for the thread-shutdown - * case. - * - * kthread_complete_and_exit() goes even further than this -- - * it is safe in the case that the thread of the caller is going away - * (not just the structure) -- this is necessary for the module-remove - * case. This is important in preemption kernels, which transfer the - * flow of execution immediately upon a complete(). - */ - kthread_complete_and_exit(&dev->control_exit, 0); -} - -static int rtsx_polling_thread(void *__dev) -{ - struct rtsx_dev *dev = __dev; - struct rtsx_chip *chip = dev->chip; - struct sd_info *sd_card = &chip->sd_card; - struct xd_info *xd_card = &chip->xd_card; - struct ms_info *ms_card = &chip->ms_card; - - sd_card->cleanup_counter = 0; - xd_card->cleanup_counter = 0; - ms_card->cleanup_counter = 0; - - /* Wait until SCSI scan finished */ - wait_timeout((delay_use + 5) * 1000); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(POLLING_INTERVAL)); - - /* lock the device pointers */ - mutex_lock(&dev->dev_mutex); - - /* if the device has disconnected, we are free to exit */ - if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) { - dev_info(&dev->pci->dev, "-- rtsx-polling exiting\n"); - mutex_unlock(&dev->dev_mutex); - break; - } - - mutex_unlock(&dev->dev_mutex); - - mspro_polling_format_status(chip); - - /* lock the device pointers */ - mutex_lock(&dev->dev_mutex); - - rtsx_polling_func(chip); - - /* unlock the device pointers */ - mutex_unlock(&dev->dev_mutex); - } - - kthread_complete_and_exit(&dev->polling_exit, 0); -} - -/* - * interrupt handler - */ -static irqreturn_t rtsx_interrupt(int irq, void *dev_id) -{ - struct rtsx_dev *dev = dev_id; - struct rtsx_chip *chip; - int retval; - u32 status; - - if (dev) - chip = dev->chip; - else - return IRQ_NONE; - - if (!chip) - return IRQ_NONE; - - spin_lock(&dev->reg_lock); - - retval = rtsx_pre_handle_interrupt(chip); - if (retval == STATUS_FAIL) { - spin_unlock(&dev->reg_lock); - if (chip->int_reg == 0xFFFFFFFF) - return IRQ_HANDLED; - return IRQ_NONE; - } - - status = chip->int_reg; - - if (dev->check_card_cd) { - if (!(dev->check_card_cd & status)) { - /* card not exist, return TRANS_RESULT_FAIL */ - dev->trans_result = TRANS_RESULT_FAIL; - if (dev->done) - complete(dev->done); - goto exit; - } - } - - if (status & (NEED_COMPLETE_INT | DELINK_INT)) { - if (status & (TRANS_FAIL_INT | DELINK_INT)) { - if (status & DELINK_INT) - RTSX_SET_DELINK(chip); - dev->trans_result = TRANS_RESULT_FAIL; - if (dev->done) - complete(dev->done); - } else if (status & TRANS_OK_INT) { - dev->trans_result = TRANS_RESULT_OK; - if (dev->done) - complete(dev->done); - } else if (status & DATA_DONE_INT) { - dev->trans_result = TRANS_NOT_READY; - if (dev->done && dev->trans_state == STATE_TRANS_SG) - complete(dev->done); - } - } - -exit: - spin_unlock(&dev->reg_lock); - return IRQ_HANDLED; -} - -/* Release all our dynamic resources */ -static void rtsx_release_resources(struct rtsx_dev *dev) -{ - dev_info(&dev->pci->dev, "-- %s\n", __func__); - - /* Tell the control thread to exit. The SCSI host must - * already have been removed so it won't try to queue - * any more commands. - */ - dev_info(&dev->pci->dev, "-- sending exit command to thread\n"); - complete(&dev->cmnd_ready); - if (dev->ctl_thread) - wait_for_completion(&dev->control_exit); - if (dev->polling_thread) - wait_for_completion(&dev->polling_exit); - - wait_timeout(200); - - if (dev->rtsx_resv_buf) { - dev->chip->host_cmds_ptr = NULL; - dev->chip->host_sg_tbl_ptr = NULL; - } - - if (dev->irq > 0) - free_irq(dev->irq, (void *)dev); - if (dev->chip->msi_en) - pci_free_irq_vectors(dev->pci); - if (dev->remap_addr) - iounmap(dev->remap_addr); - - rtsx_release_chip(dev->chip); - kfree(dev->chip); -} - -/* - * First stage of disconnect processing: stop all commands and remove - * the host - */ -static void quiesce_and_remove_host(struct rtsx_dev *dev) -{ - struct Scsi_Host *host = rtsx_to_host(dev); - struct rtsx_chip *chip = dev->chip; - - /* - * Prevent new transfers, stop the current command, and - * interrupt a SCSI-scan or device-reset delay - */ - mutex_lock(&dev->dev_mutex); - spin_lock_irq(host->host_lock); - rtsx_set_stat(chip, RTSX_STAT_DISCONNECT); - spin_unlock_irq(host->host_lock); - mutex_unlock(&dev->dev_mutex); - wake_up(&dev->delay_wait); - wait_for_completion(&dev->scanning_done); - - /* Wait some time to let other threads exist */ - wait_timeout(100); - - /* - * queuecommand won't accept any new commands and the control - * thread won't execute a previously-queued command. If there - * is such a command pending, complete it with an error. - */ - mutex_lock(&dev->dev_mutex); - if (chip->srb) { - chip->srb->result = DID_NO_CONNECT << 16; - spin_lock_irq(host->host_lock); - scsi_done(dev->chip->srb); - chip->srb = NULL; - spin_unlock_irq(host->host_lock); - } - mutex_unlock(&dev->dev_mutex); - - /* Now we own no commands so it's safe to remove the SCSI host */ - scsi_remove_host(host); -} - -/* Second stage of disconnect processing: deallocate all resources */ -static void release_everything(struct rtsx_dev *dev) -{ - rtsx_release_resources(dev); - - /* - * Drop our reference to the host; the SCSI core will free it - * when the refcount becomes 0. - */ - scsi_host_put(rtsx_to_host(dev)); -} - -/* Thread to carry out delayed SCSI-device scanning */ -static int rtsx_scan_thread(void *__dev) -{ - struct rtsx_dev *dev = __dev; - struct rtsx_chip *chip = dev->chip; - - /* Wait for the timeout to expire or for a disconnect */ - if (delay_use > 0) { - dev_info(&dev->pci->dev, - "%s: waiting for device to settle before scanning\n", - CR_DRIVER_NAME); - wait_event_interruptible_timeout - (dev->delay_wait, - rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT), - delay_use * HZ); - } - - /* If the device is still connected, perform the scanning */ - if (!rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) { - scsi_scan_host(rtsx_to_host(dev)); - dev_info(&dev->pci->dev, "%s: device scan complete\n", - CR_DRIVER_NAME); - - /* Should we unbind if no devices were detected? */ - } - - kthread_complete_and_exit(&dev->scanning_done, 0); -} - -static void rtsx_init_options(struct rtsx_chip *chip) -{ - chip->vendor_id = chip->rtsx->pci->vendor; - chip->product_id = chip->rtsx->pci->device; - chip->adma_mode = 1; - chip->lun_mc = 0; - chip->driver_first_load = 1; -#ifdef HW_AUTO_SWITCH_SD_BUS - chip->sdio_in_charge = 0; -#endif - - chip->mspro_formatter_enable = 1; - chip->ignore_sd = 0; - chip->use_hw_setting = 0; - chip->lun_mode = DEFAULT_SINGLE; - chip->auto_delink_en = auto_delink_en; - chip->ss_en = ss_en; - chip->ss_idle_period = ss_interval * 1000; - chip->remote_wakeup_en = 0; - chip->aspm_l0s_l1_en = aspm_l0s_l1_en; - chip->dynamic_aspm = 1; - chip->fpga_sd_sdr104_clk = CLK_200; - chip->fpga_sd_ddr50_clk = CLK_100; - chip->fpga_sd_sdr50_clk = CLK_100; - chip->fpga_sd_hs_clk = CLK_100; - chip->fpga_mmc_52m_clk = CLK_80; - chip->fpga_ms_hg_clk = CLK_80; - chip->fpga_ms_4bit_clk = CLK_80; - chip->fpga_ms_1bit_clk = CLK_40; - chip->asic_sd_sdr104_clk = 203; - chip->asic_sd_sdr50_clk = 98; - chip->asic_sd_ddr50_clk = 98; - chip->asic_sd_hs_clk = 98; - chip->asic_mmc_52m_clk = 98; - chip->asic_ms_hg_clk = 117; - chip->asic_ms_4bit_clk = 78; - chip->asic_ms_1bit_clk = 39; - chip->ssc_depth_sd_sdr104 = SSC_DEPTH_2M; - chip->ssc_depth_sd_sdr50 = SSC_DEPTH_2M; - chip->ssc_depth_sd_ddr50 = SSC_DEPTH_1M; - chip->ssc_depth_sd_hs = SSC_DEPTH_1M; - chip->ssc_depth_mmc_52m = SSC_DEPTH_1M; - chip->ssc_depth_ms_hg = SSC_DEPTH_1M; - chip->ssc_depth_ms_4bit = SSC_DEPTH_512K; - chip->ssc_depth_low_speed = SSC_DEPTH_512K; - chip->ssc_en = 1; - chip->sd_speed_prior = 0x01040203; - chip->sd_current_prior = 0x00010203; - chip->sd_ctl = SD_PUSH_POINT_AUTO | - SD_SAMPLE_POINT_AUTO | - SUPPORT_MMC_DDR_MODE; - chip->sd_ddr_tx_phase = 0; - chip->mmc_ddr_tx_phase = 1; - chip->sd_default_tx_phase = 15; - chip->sd_default_rx_phase = 15; - chip->pmos_pwr_on_interval = 200; - chip->sd_voltage_switch_delay = 1000; - chip->ms_power_class_en = 3; - - chip->sd_400mA_ocp_thd = 1; - chip->sd_800mA_ocp_thd = 5; - chip->ms_ocp_thd = 2; - - chip->card_drive_sel = 0x55; - chip->sd30_drive_sel_1v8 = 0x03; - chip->sd30_drive_sel_3v3 = 0x01; - - chip->do_delink_before_power_down = 1; - chip->auto_power_down = 1; - chip->polling_config = 0; - - chip->force_clkreq_0 = 1; - chip->ft2_fast_mode = 0; - - chip->sdio_retry_cnt = 1; - - chip->xd_timeout = 2000; - chip->sd_timeout = 10000; - chip->ms_timeout = 2000; - chip->mspro_timeout = 15000; - - chip->power_down_in_ss = 1; - - chip->sdr104_en = 1; - chip->sdr50_en = 1; - chip->ddr50_en = 1; - - chip->delink_stage1_step = 100; - chip->delink_stage2_step = 40; - chip->delink_stage3_step = 20; - - chip->auto_delink_in_L1 = 1; - chip->blink_led = 1; - chip->msi_en = msi_en; - chip->hp_watch_bios_hotplug = 0; - chip->max_payload = 0; - chip->phy_voltage = 0; - - chip->support_ms_8bit = 1; - chip->s3_pwr_off_delay = 1000; -} - -static int rtsx_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) -{ - struct Scsi_Host *host; - struct rtsx_dev *dev; - int err = 0; - struct task_struct *th; - - dev_dbg(&pci->dev, "Realtek PCI-E card reader detected\n"); - - err = pcim_enable_device(pci); - if (err < 0) { - dev_err(&pci->dev, "PCI enable device failed!\n"); - return err; - } - - err = pci_request_regions(pci, CR_DRIVER_NAME); - if (err < 0) { - dev_err(&pci->dev, "PCI request regions for %s failed!\n", - CR_DRIVER_NAME); - return err; - } - - /* - * Ask the SCSI layer to allocate a host structure, with extra - * space at the end for our private rtsx_dev structure. - */ - host = scsi_host_alloc(&rtsx_host_template, sizeof(*dev)); - if (!host) { - dev_err(&pci->dev, "Unable to allocate the scsi host\n"); - err = -ENOMEM; - goto scsi_host_alloc_fail; - } - - dev = host_to_rtsx(host); - memset(dev, 0, sizeof(struct rtsx_dev)); - - dev->chip = kzalloc(sizeof(*dev->chip), GFP_KERNEL); - if (!dev->chip) { - err = -ENOMEM; - goto chip_alloc_fail; - } - - spin_lock_init(&dev->reg_lock); - mutex_init(&dev->dev_mutex); - init_completion(&dev->cmnd_ready); - init_completion(&dev->control_exit); - init_completion(&dev->polling_exit); - init_completion(&dev->notify); - init_completion(&dev->scanning_done); - init_waitqueue_head(&dev->delay_wait); - - dev->pci = pci; - dev->irq = -1; - - dev_info(&pci->dev, "Resource length: 0x%x\n", - (unsigned int)pci_resource_len(pci, 0)); - dev->addr = pci_resource_start(pci, 0); - dev->remap_addr = ioremap(dev->addr, pci_resource_len(pci, 0)); - if (!dev->remap_addr) { - dev_err(&pci->dev, "ioremap error\n"); - err = -ENXIO; - goto ioremap_fail; - } - - /* - * Using "unsigned long" cast here to eliminate gcc warning in - * 64-bit system - */ - dev_info(&pci->dev, "Original address: 0x%lx, remapped address: 0x%lx\n", - (unsigned long)(dev->addr), (unsigned long)(dev->remap_addr)); - - dev->rtsx_resv_buf = dmam_alloc_coherent(&pci->dev, RTSX_RESV_BUF_LEN, - &dev->rtsx_resv_buf_addr, - GFP_KERNEL); - if (!dev->rtsx_resv_buf) { - dev_err(&pci->dev, "alloc dma buffer fail\n"); - err = -ENXIO; - goto dma_alloc_fail; - } - dev->chip->host_cmds_ptr = dev->rtsx_resv_buf; - dev->chip->host_cmds_addr = dev->rtsx_resv_buf_addr; - dev->chip->host_sg_tbl_ptr = dev->rtsx_resv_buf + HOST_CMDS_BUF_LEN; - dev->chip->host_sg_tbl_addr = dev->rtsx_resv_buf_addr + - HOST_CMDS_BUF_LEN; - - dev->chip->rtsx = dev; - - rtsx_init_options(dev->chip); - - dev_info(&pci->dev, "pci->irq = %d\n", pci->irq); - - if (dev->chip->msi_en) { - if (pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI) < 0) - dev->chip->msi_en = 0; - } - - if (rtsx_acquire_irq(dev) < 0) { - err = -EBUSY; - goto irq_acquire_fail; - } - - pci_set_master(pci); - synchronize_irq(dev->irq); - - rtsx_init_chip(dev->chip); - - /* - * set the supported max_lun and max_id for the scsi host - * NOTE: the minimal value of max_id is 1 - */ - host->max_id = 1; - host->max_lun = dev->chip->max_lun; - - /* Start up our control thread */ - th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME); - if (IS_ERR(th)) { - dev_err(&pci->dev, "Unable to start control thread\n"); - err = PTR_ERR(th); - goto control_thread_fail; - } - dev->ctl_thread = th; - - err = scsi_add_host(host, &pci->dev); - if (err) { - dev_err(&pci->dev, "Unable to add the scsi host\n"); - goto scsi_add_host_fail; - } - - /* Start up the thread for delayed SCSI-device scanning */ - th = kthread_run(rtsx_scan_thread, dev, "rtsx-scan"); - if (IS_ERR(th)) { - dev_err(&pci->dev, "Unable to start the device-scanning thread\n"); - complete(&dev->scanning_done); - err = PTR_ERR(th); - goto scan_thread_fail; - } - - /* Start up the thread for polling thread */ - th = kthread_run(rtsx_polling_thread, dev, "rtsx-polling"); - if (IS_ERR(th)) { - dev_err(&pci->dev, "Unable to start the device-polling thread\n"); - err = PTR_ERR(th); - goto scan_thread_fail; - } - dev->polling_thread = th; - - pci_set_drvdata(pci, dev); - - return 0; - - /* We come here if there are any problems */ -scan_thread_fail: - quiesce_and_remove_host(dev); -scsi_add_host_fail: - complete(&dev->cmnd_ready); - wait_for_completion(&dev->control_exit); -control_thread_fail: - free_irq(dev->irq, (void *)dev); - rtsx_release_chip(dev->chip); -irq_acquire_fail: - dev->chip->host_cmds_ptr = NULL; - dev->chip->host_sg_tbl_ptr = NULL; - if (dev->chip->msi_en) - pci_free_irq_vectors(dev->pci); -dma_alloc_fail: - iounmap(dev->remap_addr); -ioremap_fail: - kfree(dev->chip); -chip_alloc_fail: - dev_err(&pci->dev, "%s failed\n", __func__); - scsi_host_put(host); -scsi_host_alloc_fail: - pci_release_regions(pci); - return err; -} - -static void rtsx_remove(struct pci_dev *pci) -{ - struct rtsx_dev *dev = pci_get_drvdata(pci); - - quiesce_and_remove_host(dev); - release_everything(dev); - pci_release_regions(pci); -} - -/* PCI IDs */ -static const struct pci_device_id rtsx_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x5208), - PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x5288), - PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(pci, rtsx_ids); - -static SIMPLE_DEV_PM_OPS(rtsx_pm_ops, rtsx_suspend, rtsx_resume); - -/* pci_driver definition */ -static struct pci_driver rtsx_driver = { - .name = CR_DRIVER_NAME, - .id_table = rtsx_ids, - .probe = rtsx_probe, - .remove = rtsx_remove, - .driver.pm = &rtsx_pm_ops, - .shutdown = rtsx_shutdown, -}; - -module_pci_driver(rtsx_driver); diff --git a/drivers/staging/rts5208/rtsx.h b/drivers/staging/rts5208/rtsx.h deleted file mode 100644 index ec6f5b07390b2..0000000000000 --- a/drivers/staging/rts5208/rtsx.h +++ /dev/null @@ -1,164 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_H -#define __REALTEK_RTSX_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define CR_DRIVER_NAME "rts5208" - -/* - * macros for easy use - */ -#define wait_timeout_x(task_state, msecs) \ -do { \ - set_current_state((task_state)); \ - schedule_timeout((msecs) * HZ / 1000); \ -} while (0) -#define wait_timeout(msecs) wait_timeout_x(TASK_INTERRUPTIBLE, (msecs)) - -#define STATE_TRANS_NONE 0 -#define STATE_TRANS_CMD 1 -#define STATE_TRANS_BUF 2 -#define STATE_TRANS_SG 3 - -#define TRANS_NOT_READY 0 -#define TRANS_RESULT_OK 1 -#define TRANS_RESULT_FAIL 2 - -#define SCSI_LUN(srb) ((srb)->device->lun) - -struct rtsx_chip; - -struct rtsx_dev { - struct pci_dev *pci; - - /* pci resources */ - unsigned long addr; - void __iomem *remap_addr; - int irq; - - /* locks */ - spinlock_t reg_lock; - - struct task_struct *ctl_thread; /* the control thread */ - struct task_struct *polling_thread; /* the polling thread */ - - /* mutual exclusion and synchronization structures */ - struct completion cmnd_ready; /* to sleep thread on */ - struct completion control_exit; /* control thread exit */ - struct completion polling_exit; /* polling thread exit */ - struct completion notify; /* thread begin/end */ - struct completion scanning_done; /* wait for scan thread */ - - wait_queue_head_t delay_wait; /* wait during scan, reset */ - struct mutex dev_mutex; - - /* host reserved buffer */ - void *rtsx_resv_buf; - dma_addr_t rtsx_resv_buf_addr; - - char trans_result; - char trans_state; - - struct completion *done; - /* Whether interrupt handler should care card cd info */ - u32 check_card_cd; - - struct rtsx_chip *chip; -}; - -/* Convert between rtsx_dev and the corresponding Scsi_Host */ -static inline struct Scsi_Host *rtsx_to_host(struct rtsx_dev *dev) -{ - return container_of((void *)dev, struct Scsi_Host, hostdata); -} - -static inline struct rtsx_dev *host_to_rtsx(struct Scsi_Host *host) -{ - return (struct rtsx_dev *)host->hostdata; -} - -#define lock_state(chip) spin_lock_irq(&((chip)->rtsx->reg_lock)) -#define unlock_state(chip) spin_unlock_irq(&((chip)->rtsx->reg_lock)) - -/* struct scsi_cmnd transfer buffer access utilities */ -enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF}; - -#include "rtsx_chip.h" -#include "rtsx_transport.h" -#include "rtsx_scsi.h" -#include "rtsx_card.h" -#include "rtsx_sys.h" -#include "general.h" - -static inline void rtsx_writel(struct rtsx_chip *chip, u32 reg, u32 value) -{ - iowrite32(value, chip->rtsx->remap_addr + reg); -} - -static inline u32 rtsx_readl(struct rtsx_chip *chip, u32 reg) -{ - return ioread32(chip->rtsx->remap_addr + reg); -} - -static inline void rtsx_writew(struct rtsx_chip *chip, u32 reg, u16 value) -{ - iowrite16(value, chip->rtsx->remap_addr + reg); -} - -static inline u16 rtsx_readw(struct rtsx_chip *chip, u32 reg) -{ - return ioread16(chip->rtsx->remap_addr + reg); -} - -static inline void rtsx_writeb(struct rtsx_chip *chip, u32 reg, u8 value) -{ - iowrite8(value, chip->rtsx->remap_addr + reg); -} - -static inline u8 rtsx_readb(struct rtsx_chip *chip, u32 reg) -{ - return ioread8((chip)->rtsx->remap_addr + reg); -} - -static inline int rtsx_read_config_byte(struct rtsx_chip *chip, int where, u8 *val) -{ - return pci_read_config_byte(chip->rtsx->pci, where, val); -} - -static inline int rtsx_write_config_byte(struct rtsx_chip *chip, int where, u8 val) -{ - return pci_write_config_byte(chip->rtsx->pci, where, val); -} - -#endif /* __REALTEK_RTSX_H */ diff --git a/drivers/staging/rts5208/rtsx_card.c b/drivers/staging/rts5208/rtsx_card.c deleted file mode 100644 index 326b04756f62c..0000000000000 --- a/drivers/staging/rts5208/rtsx_card.c +++ /dev/null @@ -1,1151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include -#include -#include - -#include "rtsx.h" -#include "sd.h" -#include "xd.h" -#include "ms.h" - -void do_remaining_work(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; -#ifdef XD_DELAY_WRITE - struct xd_info *xd_card = &chip->xd_card; -#endif - struct ms_info *ms_card = &chip->ms_card; - - if (chip->card_ready & SD_CARD) { - if (sd_card->seq_mode) { - rtsx_set_stat(chip, RTSX_STAT_RUN); - sd_card->cleanup_counter++; - } else { - sd_card->cleanup_counter = 0; - } - } - -#ifdef XD_DELAY_WRITE - if (chip->card_ready & XD_CARD) { - if (xd_card->delay_write.delay_write_flag) { - rtsx_set_stat(chip, RTSX_STAT_RUN); - xd_card->cleanup_counter++; - } else { - xd_card->cleanup_counter = 0; - } - } -#endif - - if (chip->card_ready & MS_CARD) { - if (CHK_MSPRO(ms_card)) { - if (ms_card->seq_mode) { - rtsx_set_stat(chip, RTSX_STAT_RUN); - ms_card->cleanup_counter++; - } else { - ms_card->cleanup_counter = 0; - } - } else { -#ifdef MS_DELAY_WRITE - if (ms_card->delay_write.delay_write_flag) { - rtsx_set_stat(chip, RTSX_STAT_RUN); - ms_card->cleanup_counter++; - } else { - ms_card->cleanup_counter = 0; - } -#endif - } - } - - if (sd_card->cleanup_counter > POLLING_WAIT_CNT) - sd_cleanup_work(chip); - - if (xd_card->cleanup_counter > POLLING_WAIT_CNT) - xd_cleanup_work(chip); - - if (ms_card->cleanup_counter > POLLING_WAIT_CNT) - ms_cleanup_work(chip); -} - -void try_to_switch_sdio_ctrl(struct rtsx_chip *chip) -{ - u8 reg1 = 0, reg2 = 0; - - rtsx_read_register(chip, 0xFF34, ®1); - rtsx_read_register(chip, 0xFF38, ®2); - dev_dbg(rtsx_dev(chip), "reg 0xFF34: 0x%x, reg 0xFF38: 0x%x\n", - reg1, reg2); - if ((reg1 & 0xC0) && (reg2 & 0xC0)) { - chip->sd_int = 1; - rtsx_write_register(chip, SDIO_CTRL, 0xFF, - SDIO_BUS_CTRL | SDIO_CD_CTRL); - rtsx_write_register(chip, PWR_GATE_CTRL, - LDO3318_PWR_MASK, LDO_ON); - } -} - -#ifdef SUPPORT_SDIO_ASPM -void dynamic_configure_sdio_aspm(struct rtsx_chip *chip) -{ - u8 buf[12], reg; - int i; - - for (i = 0; i < 12; i++) - rtsx_read_register(chip, 0xFF08 + i, &buf[i]); - rtsx_read_register(chip, 0xFF25, ®); - if ((memcmp(buf, chip->sdio_raw_data, 12) != 0) || (reg & 0x03)) { - chip->sdio_counter = 0; - chip->sdio_idle = 0; - } else { - if (!chip->sdio_idle) { - chip->sdio_counter++; - if (chip->sdio_counter >= SDIO_IDLE_COUNT) { - chip->sdio_counter = 0; - chip->sdio_idle = 1; - } - } - } - memcpy(chip->sdio_raw_data, buf, 12); - - if (chip->sdio_idle) { - if (!chip->sdio_aspm) { - dev_dbg(rtsx_dev(chip), "SDIO enter ASPM!\n"); - rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC, - 0x30 | (chip->aspm_level[1] << 2)); - chip->sdio_aspm = 1; - } - } else { - if (chip->sdio_aspm) { - dev_dbg(rtsx_dev(chip), "SDIO exit ASPM!\n"); - rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC, 0x30); - chip->sdio_aspm = 0; - } - } -} -#endif - -void do_reset_sd_card(struct rtsx_chip *chip) -{ - int retval; - - dev_dbg(rtsx_dev(chip), "%s: %d, card2lun = 0x%x\n", __func__, - chip->sd_reset_counter, chip->card2lun[SD_CARD]); - - if (chip->card2lun[SD_CARD] >= MAX_ALLOWED_LUN_CNT) { - clear_bit(SD_NR, &chip->need_reset); - chip->sd_reset_counter = 0; - chip->sd_show_cnt = 0; - return; - } - - chip->rw_fail_cnt[chip->card2lun[SD_CARD]] = 0; - - rtsx_set_stat(chip, RTSX_STAT_RUN); - rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0); - - retval = reset_sd_card(chip); - if (chip->need_release & SD_CARD) - return; - if (retval == STATUS_SUCCESS) { - clear_bit(SD_NR, &chip->need_reset); - chip->sd_reset_counter = 0; - chip->sd_show_cnt = 0; - chip->card_ready |= SD_CARD; - chip->card_fail &= ~SD_CARD; - chip->rw_card[chip->card2lun[SD_CARD]] = sd_rw; - } else { - if (chip->sd_io || chip->sd_reset_counter >= MAX_RESET_CNT) { - clear_bit(SD_NR, &chip->need_reset); - chip->sd_reset_counter = 0; - chip->sd_show_cnt = 0; - } else { - chip->sd_reset_counter++; - } - chip->card_ready &= ~SD_CARD; - chip->card_fail |= SD_CARD; - chip->capacity[chip->card2lun[SD_CARD]] = 0; - chip->rw_card[chip->card2lun[SD_CARD]] = NULL; - - rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0); - if (!chip->ft2_fast_mode) - card_power_off(chip, SD_CARD); - if (chip->sd_io) { - chip->sd_int = 0; - try_to_switch_sdio_ctrl(chip); - } else { - disable_card_clock(chip, SD_CARD); - } - } -} - -void do_reset_xd_card(struct rtsx_chip *chip) -{ - int retval; - - dev_dbg(rtsx_dev(chip), "%s: %d, card2lun = 0x%x\n", __func__, - chip->xd_reset_counter, chip->card2lun[XD_CARD]); - - if (chip->card2lun[XD_CARD] >= MAX_ALLOWED_LUN_CNT) { - clear_bit(XD_NR, &chip->need_reset); - chip->xd_reset_counter = 0; - chip->xd_show_cnt = 0; - return; - } - - chip->rw_fail_cnt[chip->card2lun[XD_CARD]] = 0; - - rtsx_set_stat(chip, RTSX_STAT_RUN); - rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0); - - retval = reset_xd_card(chip); - if (chip->need_release & XD_CARD) - return; - if (retval == STATUS_SUCCESS) { - clear_bit(XD_NR, &chip->need_reset); - chip->xd_reset_counter = 0; - chip->card_ready |= XD_CARD; - chip->card_fail &= ~XD_CARD; - chip->rw_card[chip->card2lun[XD_CARD]] = xd_rw; - } else { - if (chip->xd_reset_counter >= MAX_RESET_CNT) { - clear_bit(XD_NR, &chip->need_reset); - chip->xd_reset_counter = 0; - chip->xd_show_cnt = 0; - } else { - chip->xd_reset_counter++; - } - chip->card_ready &= ~XD_CARD; - chip->card_fail |= XD_CARD; - chip->capacity[chip->card2lun[XD_CARD]] = 0; - chip->rw_card[chip->card2lun[XD_CARD]] = NULL; - - rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN, 0); - if (!chip->ft2_fast_mode) - card_power_off(chip, XD_CARD); - disable_card_clock(chip, XD_CARD); - } -} - -void do_reset_ms_card(struct rtsx_chip *chip) -{ - int retval; - - dev_dbg(rtsx_dev(chip), "%s: %d, card2lun = 0x%x\n", __func__, - chip->ms_reset_counter, chip->card2lun[MS_CARD]); - - if (chip->card2lun[MS_CARD] >= MAX_ALLOWED_LUN_CNT) { - clear_bit(MS_NR, &chip->need_reset); - chip->ms_reset_counter = 0; - chip->ms_show_cnt = 0; - return; - } - - chip->rw_fail_cnt[chip->card2lun[MS_CARD]] = 0; - - rtsx_set_stat(chip, RTSX_STAT_RUN); - rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0); - - retval = reset_ms_card(chip); - if (chip->need_release & MS_CARD) - return; - if (retval == STATUS_SUCCESS) { - clear_bit(MS_NR, &chip->need_reset); - chip->ms_reset_counter = 0; - chip->card_ready |= MS_CARD; - chip->card_fail &= ~MS_CARD; - chip->rw_card[chip->card2lun[MS_CARD]] = ms_rw; - } else { - if (chip->ms_reset_counter >= MAX_RESET_CNT) { - clear_bit(MS_NR, &chip->need_reset); - chip->ms_reset_counter = 0; - chip->ms_show_cnt = 0; - } else { - chip->ms_reset_counter++; - } - chip->card_ready &= ~MS_CARD; - chip->card_fail |= MS_CARD; - chip->capacity[chip->card2lun[MS_CARD]] = 0; - chip->rw_card[chip->card2lun[MS_CARD]] = NULL; - - rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0); - if (!chip->ft2_fast_mode) - card_power_off(chip, MS_CARD); - disable_card_clock(chip, MS_CARD); - } -} - -static void release_sdio(struct rtsx_chip *chip) -{ - if (chip->sd_io) { - rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR, - SD_STOP | SD_CLR_ERR); - - if (chip->chip_insert_with_sdio) { - chip->chip_insert_with_sdio = 0; - - if (CHECK_PID(chip, 0x5288)) - rtsx_write_register(chip, 0xFE5A, 0x08, 0x00); - else - rtsx_write_register(chip, 0xFE70, 0x80, 0x00); - } - - rtsx_write_register(chip, SDIO_CTRL, SDIO_CD_CTRL, 0); - chip->sd_io = 0; - } -} - -void rtsx_power_off_card(struct rtsx_chip *chip) -{ - if ((chip->card_ready & SD_CARD) || chip->sd_io) { - sd_cleanup_work(chip); - sd_power_off_card3v3(chip); - } - - if (chip->card_ready & XD_CARD) { - xd_cleanup_work(chip); - xd_power_off_card3v3(chip); - } - - if (chip->card_ready & MS_CARD) { - ms_cleanup_work(chip); - ms_power_off_card3v3(chip); - } -} - -void rtsx_release_cards(struct rtsx_chip *chip) -{ - chip->int_reg = rtsx_readl(chip, RTSX_BIPR); - - if ((chip->card_ready & SD_CARD) || chip->sd_io) { - if (chip->int_reg & SD_EXIST) - sd_cleanup_work(chip); - release_sd_card(chip); - } - - if (chip->card_ready & XD_CARD) { - if (chip->int_reg & XD_EXIST) - xd_cleanup_work(chip); - release_xd_card(chip); - } - - if (chip->card_ready & MS_CARD) { - if (chip->int_reg & MS_EXIST) - ms_cleanup_work(chip); - release_ms_card(chip); - } -} - -void rtsx_reset_cards(struct rtsx_chip *chip) -{ - if (!chip->need_reset) - return; - - rtsx_set_stat(chip, RTSX_STAT_RUN); - - rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL); - - rtsx_disable_aspm(chip); - - if ((chip->need_reset & SD_CARD) && chip->chip_insert_with_sdio) - clear_bit(SD_NR, &chip->need_reset); - - if (chip->need_reset & XD_CARD) { - chip->card_exist |= XD_CARD; - - if (chip->xd_show_cnt >= MAX_SHOW_CNT) - do_reset_xd_card(chip); - else - chip->xd_show_cnt++; - } - if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) { - if (chip->card_exist & XD_CARD) { - clear_bit(SD_NR, &chip->need_reset); - clear_bit(MS_NR, &chip->need_reset); - } - } - if (chip->need_reset & SD_CARD) { - chip->card_exist |= SD_CARD; - - if (chip->sd_show_cnt >= MAX_SHOW_CNT) { - rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH); - do_reset_sd_card(chip); - } else { - chip->sd_show_cnt++; - } - } - if (chip->need_reset & MS_CARD) { - chip->card_exist |= MS_CARD; - - if (chip->ms_show_cnt >= MAX_SHOW_CNT) - do_reset_ms_card(chip); - else - chip->ms_show_cnt++; - } -} - -void rtsx_reinit_cards(struct rtsx_chip *chip, int reset_chip) -{ - rtsx_set_stat(chip, RTSX_STAT_RUN); - - rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL); - - if (reset_chip) - rtsx_reset_chip(chip); - - chip->int_reg = rtsx_readl(chip, RTSX_BIPR); - - if ((chip->int_reg & SD_EXIST) && (chip->need_reinit & SD_CARD)) { - release_sdio(chip); - release_sd_card(chip); - - wait_timeout(100); - - chip->card_exist |= SD_CARD; - do_reset_sd_card(chip); - } - - if ((chip->int_reg & XD_EXIST) && (chip->need_reinit & XD_CARD)) { - release_xd_card(chip); - - wait_timeout(100); - - chip->card_exist |= XD_CARD; - do_reset_xd_card(chip); - } - - if ((chip->int_reg & MS_EXIST) && (chip->need_reinit & MS_CARD)) { - release_ms_card(chip); - - wait_timeout(100); - - chip->card_exist |= MS_CARD; - do_reset_ms_card(chip); - } - - chip->need_reinit = 0; -} - -#ifdef DISABLE_CARD_INT -void card_cd_debounce(struct rtsx_chip *chip, unsigned long *need_reset, - unsigned long *need_release) -{ - u8 release_map = 0, reset_map = 0; - - chip->int_reg = rtsx_readl(chip, RTSX_BIPR); - - if (chip->card_exist) { - if (chip->card_exist & XD_CARD) { - if (!(chip->int_reg & XD_EXIST)) - release_map |= XD_CARD; - } else if (chip->card_exist & SD_CARD) { - if (!(chip->int_reg & SD_EXIST)) - release_map |= SD_CARD; - } else if (chip->card_exist & MS_CARD) { - if (!(chip->int_reg & MS_EXIST)) - release_map |= MS_CARD; - } - } else { - if (chip->int_reg & XD_EXIST) - reset_map |= XD_CARD; - else if (chip->int_reg & SD_EXIST) - reset_map |= SD_CARD; - else if (chip->int_reg & MS_EXIST) - reset_map |= MS_CARD; - } - - if (reset_map) { - int xd_cnt = 0, sd_cnt = 0, ms_cnt = 0; - int i; - - for (i = 0; i < (DEBOUNCE_CNT); i++) { - chip->int_reg = rtsx_readl(chip, RTSX_BIPR); - - if (chip->int_reg & XD_EXIST) - xd_cnt++; - else - xd_cnt = 0; - - if (chip->int_reg & SD_EXIST) - sd_cnt++; - else - sd_cnt = 0; - - if (chip->int_reg & MS_EXIST) - ms_cnt++; - else - ms_cnt = 0; - - wait_timeout(30); - } - - reset_map = 0; - if (!(chip->card_exist & XD_CARD) && - (xd_cnt > (DEBOUNCE_CNT - 1))) - reset_map |= XD_CARD; - if (!(chip->card_exist & SD_CARD) && - (sd_cnt > (DEBOUNCE_CNT - 1))) - reset_map |= SD_CARD; - if (!(chip->card_exist & MS_CARD) && - (ms_cnt > (DEBOUNCE_CNT - 1))) - reset_map |= MS_CARD; - } - - if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) - rtsx_write_register(chip, HOST_SLEEP_STATE, 0xC0, 0x00); - - if (need_reset) - *need_reset = reset_map; - if (need_release) - *need_release = release_map; -} -#endif - -void rtsx_init_cards(struct rtsx_chip *chip) -{ - if (RTSX_TST_DELINK(chip) && (rtsx_get_stat(chip) != RTSX_STAT_SS)) { - dev_dbg(rtsx_dev(chip), "Reset chip in polling thread!\n"); - rtsx_reset_chip(chip); - RTSX_CLR_DELINK(chip); - } - -#ifdef DISABLE_CARD_INT - card_cd_debounce(chip, &chip->need_reset, &chip->need_release); -#endif - - if (chip->need_release) { - if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) { - if (chip->int_reg & XD_EXIST) { - clear_bit(SD_NR, &chip->need_release); - clear_bit(MS_NR, &chip->need_release); - } - } - - if (!(chip->card_exist & SD_CARD) && !chip->sd_io) - clear_bit(SD_NR, &chip->need_release); - if (!(chip->card_exist & XD_CARD)) - clear_bit(XD_NR, &chip->need_release); - if (!(chip->card_exist & MS_CARD)) - clear_bit(MS_NR, &chip->need_release); - - dev_dbg(rtsx_dev(chip), "chip->need_release = 0x%x\n", - (unsigned int)(chip->need_release)); - -#ifdef SUPPORT_OCP - if (chip->need_release) { - if (chip->ocp_stat & (CARD_OC_NOW | CARD_OC_EVER)) - rtsx_write_register(chip, OCPCLR, - CARD_OC_INT_CLR | - CARD_OC_CLR, - CARD_OC_INT_CLR | - CARD_OC_CLR); - chip->ocp_stat = 0; - } -#endif - if (chip->need_release) { - rtsx_set_stat(chip, RTSX_STAT_RUN); - rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL); - } - - if (chip->need_release & SD_CARD) { - clear_bit(SD_NR, &chip->need_release); - chip->card_exist &= ~SD_CARD; - chip->card_ejected &= ~SD_CARD; - chip->card_fail &= ~SD_CARD; - CLR_BIT(chip->lun_mc, chip->card2lun[SD_CARD]); - chip->rw_fail_cnt[chip->card2lun[SD_CARD]] = 0; - rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH); - - release_sdio(chip); - release_sd_card(chip); - } - - if (chip->need_release & XD_CARD) { - clear_bit(XD_NR, &chip->need_release); - chip->card_exist &= ~XD_CARD; - chip->card_ejected &= ~XD_CARD; - chip->card_fail &= ~XD_CARD; - CLR_BIT(chip->lun_mc, chip->card2lun[XD_CARD]); - chip->rw_fail_cnt[chip->card2lun[XD_CARD]] = 0; - - release_xd_card(chip); - - if (CHECK_PID(chip, 0x5288) && - CHECK_BARO_PKG(chip, QFN)) - rtsx_write_register(chip, HOST_SLEEP_STATE, - 0xC0, 0xC0); - } - - if (chip->need_release & MS_CARD) { - clear_bit(MS_NR, &chip->need_release); - chip->card_exist &= ~MS_CARD; - chip->card_ejected &= ~MS_CARD; - chip->card_fail &= ~MS_CARD; - CLR_BIT(chip->lun_mc, chip->card2lun[MS_CARD]); - chip->rw_fail_cnt[chip->card2lun[MS_CARD]] = 0; - - release_ms_card(chip); - } - - dev_dbg(rtsx_dev(chip), "chip->card_exist = 0x%x\n", - chip->card_exist); - - if (!chip->card_exist) - turn_off_led(chip, LED_GPIO); - } - - if (chip->need_reset) { - dev_dbg(rtsx_dev(chip), "chip->need_reset = 0x%x\n", - (unsigned int)(chip->need_reset)); - - rtsx_reset_cards(chip); - } - - if (chip->need_reinit) { - dev_dbg(rtsx_dev(chip), "chip->need_reinit = 0x%x\n", - (unsigned int)(chip->need_reinit)); - - rtsx_reinit_cards(chip, 0); - } -} - -int switch_ssc_clock(struct rtsx_chip *chip, int clk) -{ - int retval; - u8 n = (u8)(clk - 2), min_n, max_n; - u8 mcu_cnt, div, max_div, ssc_depth, ssc_depth_mask; - int sd_vpclk_phase_reset = 0; - - if (chip->cur_clk == clk) - return STATUS_SUCCESS; - - min_n = 60; - max_n = 120; - max_div = CLK_DIV_4; - - dev_dbg(rtsx_dev(chip), "Switch SSC clock to %dMHz (cur_clk = %d)\n", - clk, chip->cur_clk); - - if (clk <= 2 || n > max_n) - return STATUS_FAIL; - - mcu_cnt = (u8)(125 / clk + 3); - if (mcu_cnt > 7) - mcu_cnt = 7; - - div = CLK_DIV_1; - while ((n < min_n) && (div < max_div)) { - n = (n + 2) * 2 - 2; - div++; - } - dev_dbg(rtsx_dev(chip), "n = %d, div = %d\n", n, div); - - if (chip->ssc_en) { - ssc_depth = 0x01; - n -= 2; - } else { - ssc_depth = 0; - } - - ssc_depth_mask = 0x03; - - dev_dbg(rtsx_dev(chip), "ssc_depth = %d\n", ssc_depth); - - rtsx_init_cmd(chip); - rtsx_add_cmd(chip, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ); - rtsx_add_cmd(chip, WRITE_REG_CMD, CLK_DIV, 0xFF, (div << 4) | mcu_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL2, ssc_depth_mask, ssc_depth); - rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); - rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); - if (sd_vpclk_phase_reset) { - rtsx_add_cmd(chip, WRITE_REG_CMD, SD_VPCLK0_CTL, - PHASE_NOT_RESET, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, SD_VPCLK0_CTL, - PHASE_NOT_RESET, PHASE_NOT_RESET); - } - - retval = rtsx_send_cmd(chip, 0, WAIT_TIME); - if (retval < 0) - return STATUS_ERROR; - - udelay(10); - retval = rtsx_write_register(chip, CLK_CTL, CLK_LOW_FREQ, 0); - if (retval) - return retval; - - chip->cur_clk = clk; - - return STATUS_SUCCESS; -} - -int switch_normal_clock(struct rtsx_chip *chip, int clk) -{ - int retval; - u8 sel, div, mcu_cnt; - int sd_vpclk_phase_reset = 0; - - if (chip->cur_clk == clk) - return STATUS_SUCCESS; - - switch (clk) { - case CLK_20: - dev_dbg(rtsx_dev(chip), "Switch clock to 20MHz\n"); - sel = SSC_80; - div = CLK_DIV_4; - mcu_cnt = 7; - break; - - case CLK_30: - dev_dbg(rtsx_dev(chip), "Switch clock to 30MHz\n"); - sel = SSC_120; - div = CLK_DIV_4; - mcu_cnt = 7; - break; - - case CLK_40: - dev_dbg(rtsx_dev(chip), "Switch clock to 40MHz\n"); - sel = SSC_80; - div = CLK_DIV_2; - mcu_cnt = 7; - break; - - case CLK_50: - dev_dbg(rtsx_dev(chip), "Switch clock to 50MHz\n"); - sel = SSC_100; - div = CLK_DIV_2; - mcu_cnt = 6; - break; - - case CLK_60: - dev_dbg(rtsx_dev(chip), "Switch clock to 60MHz\n"); - sel = SSC_120; - div = CLK_DIV_2; - mcu_cnt = 6; - break; - - case CLK_80: - dev_dbg(rtsx_dev(chip), "Switch clock to 80MHz\n"); - sel = SSC_80; - div = CLK_DIV_1; - mcu_cnt = 5; - break; - - case CLK_100: - dev_dbg(rtsx_dev(chip), "Switch clock to 100MHz\n"); - sel = SSC_100; - div = CLK_DIV_1; - mcu_cnt = 5; - break; - - case CLK_120: - dev_dbg(rtsx_dev(chip), "Switch clock to 120MHz\n"); - sel = SSC_120; - div = CLK_DIV_1; - mcu_cnt = 5; - break; - - case CLK_150: - dev_dbg(rtsx_dev(chip), "Switch clock to 150MHz\n"); - sel = SSC_150; - div = CLK_DIV_1; - mcu_cnt = 4; - break; - - case CLK_200: - dev_dbg(rtsx_dev(chip), "Switch clock to 200MHz\n"); - sel = SSC_200; - div = CLK_DIV_1; - mcu_cnt = 4; - break; - - default: - dev_dbg(rtsx_dev(chip), "Try to switch to an illegal clock (%d)\n", - clk); - return STATUS_FAIL; - } - - retval = rtsx_write_register(chip, CLK_CTL, 0xFF, CLK_LOW_FREQ); - if (retval) - return retval; - if (sd_vpclk_phase_reset) { - retval = rtsx_write_register(chip, SD_VPCLK0_CTL, - PHASE_NOT_RESET, 0); - if (retval) - return retval; - retval = rtsx_write_register(chip, SD_VPCLK1_CTL, - PHASE_NOT_RESET, 0); - if (retval) - return retval; - } - retval = rtsx_write_register(chip, CLK_DIV, 0xFF, - (div << 4) | mcu_cnt); - if (retval) - return retval; - retval = rtsx_write_register(chip, CLK_SEL, 0xFF, sel); - if (retval) - return retval; - - if (sd_vpclk_phase_reset) { - udelay(200); - retval = rtsx_write_register(chip, SD_VPCLK0_CTL, - PHASE_NOT_RESET, PHASE_NOT_RESET); - if (retval) - return retval; - retval = rtsx_write_register(chip, SD_VPCLK1_CTL, - PHASE_NOT_RESET, PHASE_NOT_RESET); - if (retval) - return retval; - udelay(200); - } - retval = rtsx_write_register(chip, CLK_CTL, 0xFF, 0); - if (retval) - return retval; - - chip->cur_clk = clk; - - return STATUS_SUCCESS; -} - -void trans_dma_enable(enum dma_data_direction dir, struct rtsx_chip *chip, - u32 byte_cnt, u8 pack_size) -{ - if (pack_size > DMA_1024) - pack_size = DMA_512; - - rtsx_add_cmd(chip, WRITE_REG_CMD, IRQSTAT0, DMA_DONE_INT, DMA_DONE_INT); - - rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(byte_cnt >> 24)); - rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(byte_cnt >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(byte_cnt >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC0, 0xFF, (u8)byte_cnt); - - if (dir == DMA_FROM_DEVICE) { - rtsx_add_cmd(chip, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, - DMA_DIR_FROM_CARD | DMA_EN | pack_size); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, - DMA_DIR_TO_CARD | DMA_EN | pack_size); - } - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); -} - -int enable_card_clock(struct rtsx_chip *chip, u8 card) -{ - int retval; - u8 clk_en = 0; - - if (card & XD_CARD) - clk_en |= XD_CLK_EN; - if (card & SD_CARD) - clk_en |= SD_CLK_EN; - if (card & MS_CARD) - clk_en |= MS_CLK_EN; - - retval = rtsx_write_register(chip, CARD_CLK_EN, clk_en, clk_en); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int disable_card_clock(struct rtsx_chip *chip, u8 card) -{ - int retval; - u8 clk_en = 0; - - if (card & XD_CARD) - clk_en |= XD_CLK_EN; - if (card & SD_CARD) - clk_en |= SD_CLK_EN; - if (card & MS_CARD) - clk_en |= MS_CLK_EN; - - retval = rtsx_write_register(chip, CARD_CLK_EN, clk_en, 0); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int card_power_on(struct rtsx_chip *chip, u8 card) -{ - int retval; - u8 mask, val1, val2; - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && card == MS_CARD) { - mask = MS_POWER_MASK; - val1 = MS_PARTIAL_POWER_ON; - val2 = MS_POWER_ON; - } else { - mask = SD_POWER_MASK; - val1 = SD_PARTIAL_POWER_ON; - val2 = SD_POWER_ON; - } - - rtsx_init_cmd(chip); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val1); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - udelay(chip->pmos_pwr_on_interval); - - rtsx_init_cmd(chip); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val2); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int card_power_off(struct rtsx_chip *chip, u8 card) -{ - int retval; - u8 mask, val; - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && card == MS_CARD) { - mask = MS_POWER_MASK; - val = MS_POWER_OFF; - } else { - mask = SD_POWER_MASK; - val = SD_POWER_OFF; - } - - retval = rtsx_write_register(chip, CARD_PWR_CTL, mask, val); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 sec_addr, u16 sec_cnt) -{ - int retval; - unsigned int lun = SCSI_LUN(srb); - int i; - - if (!chip->rw_card[lun]) - return STATUS_FAIL; - - for (i = 0; i < 3; i++) { - chip->rw_need_retry = 0; - - retval = chip->rw_card[lun](srb, chip, sec_addr, sec_cnt); - if (retval != STATUS_SUCCESS) { - if (rtsx_check_chip_exist(chip) != STATUS_SUCCESS) { - rtsx_release_chip(chip); - return STATUS_FAIL; - } - if (detect_card_cd(chip, chip->cur_card) != - STATUS_SUCCESS) { - return STATUS_FAIL; - } - - if (!chip->rw_need_retry) { - dev_dbg(rtsx_dev(chip), "RW fail, but no need to retry\n"); - break; - } - } else { - chip->rw_need_retry = 0; - break; - } - - dev_dbg(rtsx_dev(chip), "Retry RW, (i = %d)\n", i); - } - - return retval; -} - -int card_share_mode(struct rtsx_chip *chip, int card) -{ - int retval; - u8 mask, value; - - if (CHECK_PID(chip, 0x5208)) { - mask = CARD_SHARE_MASK; - if (card == SD_CARD) - value = CARD_SHARE_48_SD; - else if (card == MS_CARD) - value = CARD_SHARE_48_MS; - else if (card == XD_CARD) - value = CARD_SHARE_48_XD; - else - return STATUS_FAIL; - - } else if (CHECK_PID(chip, 0x5288)) { - mask = 0x03; - if (card == SD_CARD) - value = CARD_SHARE_BAROSSA_SD; - else if (card == MS_CARD) - value = CARD_SHARE_BAROSSA_MS; - else if (card == XD_CARD) - value = CARD_SHARE_BAROSSA_XD; - else - return STATUS_FAIL; - - } else { - return STATUS_FAIL; - } - - retval = rtsx_write_register(chip, CARD_SHARE_MODE, mask, value); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int select_card(struct rtsx_chip *chip, int card) -{ - int retval; - - if (chip->cur_card != card) { - u8 mod; - - if (card == SD_CARD) - mod = SD_MOD_SEL; - else if (card == MS_CARD) - mod = MS_MOD_SEL; - else if (card == XD_CARD) - mod = XD_MOD_SEL; - else if (card == SPI_CARD) - mod = SPI_MOD_SEL; - else - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_SELECT, 0x07, mod); - if (retval) - return retval; - chip->cur_card = card; - - retval = card_share_mode(chip, card); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -void toggle_gpio(struct rtsx_chip *chip, u8 gpio) -{ - u8 temp_reg; - - rtsx_read_register(chip, CARD_GPIO, &temp_reg); - temp_reg ^= (0x01 << gpio); - rtsx_write_register(chip, CARD_GPIO, 0xFF, temp_reg); -} - -void turn_on_led(struct rtsx_chip *chip, u8 gpio) -{ - if (CHECK_PID(chip, 0x5288)) - rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), - (u8)(1 << gpio)); - else - rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0); -} - -void turn_off_led(struct rtsx_chip *chip, u8 gpio) -{ - if (CHECK_PID(chip, 0x5288)) - rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0); - else - rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), - (u8)(1 << gpio)); -} - -int detect_card_cd(struct rtsx_chip *chip, int card) -{ - u32 card_cd, status; - - if (card == SD_CARD) { - card_cd = SD_EXIST; - } else if (card == MS_CARD) { - card_cd = MS_EXIST; - } else if (card == XD_CARD) { - card_cd = XD_EXIST; - } else { - dev_dbg(rtsx_dev(chip), "Wrong card type: 0x%x\n", card); - return STATUS_FAIL; - } - - status = rtsx_readl(chip, RTSX_BIPR); - if (!(status & card_cd)) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int check_card_exist(struct rtsx_chip *chip, unsigned int lun) -{ - if (chip->card_exist & chip->lun2card[lun]) - return 1; - - return 0; -} - -int check_card_ready(struct rtsx_chip *chip, unsigned int lun) -{ - if (chip->card_ready & chip->lun2card[lun]) - return 1; - - return 0; -} - -int check_card_wp(struct rtsx_chip *chip, unsigned int lun) -{ - if (chip->card_wp & chip->lun2card[lun]) - return 1; - - return 0; -} - -u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun) -{ - if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD) - return (u8)XD_CARD; - else if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD) - return (u8)SD_CARD; - else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD) - return (u8)MS_CARD; - - return 0; -} - -void eject_card(struct rtsx_chip *chip, unsigned int lun) -{ - do_remaining_work(chip); - - if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD) { - release_sd_card(chip); - chip->card_ejected |= SD_CARD; - chip->card_ready &= ~SD_CARD; - chip->capacity[lun] = 0; - } else if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD) { - release_xd_card(chip); - chip->card_ejected |= XD_CARD; - chip->card_ready &= ~XD_CARD; - chip->capacity[lun] = 0; - } else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD) { - release_ms_card(chip); - chip->card_ejected |= MS_CARD; - chip->card_ready &= ~MS_CARD; - chip->capacity[lun] = 0; - } -} diff --git a/drivers/staging/rts5208/rtsx_card.h b/drivers/staging/rts5208/rtsx_card.h deleted file mode 100644 index 39727371cd7ad..0000000000000 --- a/drivers/staging/rts5208/rtsx_card.h +++ /dev/null @@ -1,1087 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_CARD_H -#define __REALTEK_RTSX_CARD_H - -#include "rtsx.h" -#include "rtsx_chip.h" -#include "rtsx_transport.h" -#include "sd.h" - -#define SSC_POWER_DOWN 0x01 -#define SD_OC_POWER_DOWN 0x02 -#define MS_OC_POWER_DOWN 0x04 -#define ALL_POWER_DOWN 0x07 -#define OC_POWER_DOWN 0x06 - -#define PMOS_STRG_MASK 0x10 -#define PMOS_STRG_800mA 0x10 -#define PMOS_STRG_400mA 0x00 - -#define POWER_OFF 0x03 -#define PARTIAL_POWER_ON 0x01 -#define POWER_ON 0x00 - -#define MS_POWER_OFF 0x0C -#define MS_PARTIAL_POWER_ON 0x04 -#define MS_POWER_ON 0x00 -#define MS_POWER_MASK 0x0C - -#define SD_POWER_OFF 0x03 -#define SD_PARTIAL_POWER_ON 0x01 -#define SD_POWER_ON 0x00 -#define SD_POWER_MASK 0x03 - -#define XD_OUTPUT_EN 0x02 -#define SD_OUTPUT_EN 0x04 -#define MS_OUTPUT_EN 0x08 -#define SPI_OUTPUT_EN 0x10 - -#define CLK_LOW_FREQ 0x01 - -#define CLK_DIV_1 0x01 -#define CLK_DIV_2 0x02 -#define CLK_DIV_4 0x03 -#define CLK_DIV_8 0x04 - -#define SSC_80 0 -#define SSC_100 1 -#define SSC_120 2 -#define SSC_150 3 -#define SSC_200 4 - -#define XD_CLK_EN 0x02 -#define SD_CLK_EN 0x04 -#define MS_CLK_EN 0x08 -#define SPI_CLK_EN 0x10 - -#define XD_MOD_SEL 1 -#define SD_MOD_SEL 2 -#define MS_MOD_SEL 3 -#define SPI_MOD_SEL 4 - -#define CHANGE_CLK 0x01 - -#define SD_CRC7_ERR 0x80 -#define SD_CRC16_ERR 0x40 -#define SD_CRC_WRITE_ERR 0x20 -#define SD_CRC_WRITE_ERR_MASK 0x1C -#define GET_CRC_TIME_OUT 0x02 -#define SD_TUNING_COMPARE_ERR 0x01 - -#define SD_RSP_80CLK_TIMEOUT 0x01 - -#define SD_CLK_TOGGLE_EN 0x80 -#define SD_CLK_FORCE_STOP 0x40 -#define SD_DAT3_STATUS 0x10 -#define SD_DAT2_STATUS 0x08 -#define SD_DAT1_STATUS 0x04 -#define SD_DAT0_STATUS 0x02 -#define SD_CMD_STATUS 0x01 - -#define SD_IO_USING_1V8 0x80 -#define SD_IO_USING_3V3 0x7F -#define TYPE_A_DRIVING 0x00 -#define TYPE_B_DRIVING 0x01 -#define TYPE_C_DRIVING 0x02 -#define TYPE_D_DRIVING 0x03 - -#define DDR_FIX_RX_DAT 0x00 -#define DDR_VAR_RX_DAT 0x80 -#define DDR_FIX_RX_DAT_EDGE 0x00 -#define DDR_FIX_RX_DAT_14_DELAY 0x40 -#define DDR_FIX_RX_CMD 0x00 -#define DDR_VAR_RX_CMD 0x20 -#define DDR_FIX_RX_CMD_POS_EDGE 0x00 -#define DDR_FIX_RX_CMD_14_DELAY 0x10 -#define SD20_RX_POS_EDGE 0x00 -#define SD20_RX_14_DELAY 0x08 -#define SD20_RX_SEL_MASK 0x08 - -#define DDR_FIX_TX_CMD_DAT 0x00 -#define DDR_VAR_TX_CMD_DAT 0x80 -#define DDR_FIX_TX_DAT_14_TSU 0x00 -#define DDR_FIX_TX_DAT_12_TSU 0x40 -#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 -#define DDR_FIX_TX_CMD_14_AHEAD 0x20 -#define SD20_TX_NEG_EDGE 0x00 -#define SD20_TX_14_AHEAD 0x10 -#define SD20_TX_SEL_MASK 0x10 -#define DDR_VAR_SDCLK_POL_SWAP 0x01 - -#define SD_TRANSFER_START 0x80 -#define SD_TRANSFER_END 0x40 -#define SD_STAT_IDLE 0x20 -#define SD_TRANSFER_ERR 0x10 -#define SD_TM_NORMAL_WRITE 0x00 -#define SD_TM_AUTO_WRITE_3 0x01 -#define SD_TM_AUTO_WRITE_4 0x02 -#define SD_TM_AUTO_READ_3 0x05 -#define SD_TM_AUTO_READ_4 0x06 -#define SD_TM_CMD_RSP 0x08 -#define SD_TM_AUTO_WRITE_1 0x09 -#define SD_TM_AUTO_WRITE_2 0x0A -#define SD_TM_NORMAL_READ 0x0C -#define SD_TM_AUTO_READ_1 0x0D -#define SD_TM_AUTO_READ_2 0x0E -#define SD_TM_AUTO_TUNING 0x0F - -#define PHASE_CHANGE 0x80 -#define PHASE_NOT_RESET 0x40 - -#define DCMPS_CHANGE 0x80 -#define DCMPS_CHANGE_DONE 0x40 -#define DCMPS_ERROR 0x20 -#define DCMPS_CURRENT_PHASE 0x1F - -#define SD_CLK_DIVIDE_0 0x00 -#define SD_CLK_DIVIDE_256 0xC0 -#define SD_CLK_DIVIDE_128 0x80 -#define SD_BUS_WIDTH_1 0x00 -#define SD_BUS_WIDTH_4 0x01 -#define SD_BUS_WIDTH_8 0x02 -#define SD_ASYNC_FIFO_NOT_RST 0x10 -#define SD_20_MODE 0x00 -#define SD_DDR_MODE 0x04 -#define SD_30_MODE 0x08 - -#define SD_CLK_DIVIDE_MASK 0xC0 - -#define SD_CMD_IDLE 0x80 - -#define SD_DATA_IDLE 0x80 - -#define DCM_RESET 0x08 -#define DCM_LOCKED 0x04 -#define DCM_208M 0x00 -#define DCM_TX 0x01 -#define DCM_RX 0x02 - -#define DRP_START 0x80 -#define DRP_DONE 0x40 - -#define DRP_WRITE 0x80 -#define DRP_READ 0x00 -#define DCM_WRITE_ADDRESS_50 0x50 -#define DCM_WRITE_ADDRESS_51 0x51 -#define DCM_READ_ADDRESS_00 0x00 -#define DCM_READ_ADDRESS_51 0x51 - -#define SD_CALCULATE_CRC7 0x00 -#define SD_NO_CALCULATE_CRC7 0x80 -#define SD_CHECK_CRC16 0x00 -#define SD_NO_CHECK_CRC16 0x40 -#define SD_NO_CHECK_WAIT_CRC_TO 0x20 -#define SD_WAIT_BUSY_END 0x08 -#define SD_NO_WAIT_BUSY_END 0x00 -#define SD_CHECK_CRC7 0x00 -#define SD_NO_CHECK_CRC7 0x04 -#define SD_RSP_LEN_0 0x00 -#define SD_RSP_LEN_6 0x01 -#define SD_RSP_LEN_17 0x02 -#define SD_RSP_TYPE_R0 0x04 -#define SD_RSP_TYPE_R1 0x01 -#define SD_RSP_TYPE_R1b 0x09 -#define SD_RSP_TYPE_R2 0x02 -#define SD_RSP_TYPE_R3 0x05 -#define SD_RSP_TYPE_R4 0x05 -#define SD_RSP_TYPE_R5 0x01 -#define SD_RSP_TYPE_R6 0x01 -#define SD_RSP_TYPE_R7 0x01 - -#define SD_RSP_80CLK_TIMEOUT_EN 0x01 - -#define SAMPLE_TIME_RISING 0x00 -#define SAMPLE_TIME_FALLING 0x80 -#define PUSH_TIME_DEFAULT 0x00 -#define PUSH_TIME_ODD 0x40 -#define NO_EXTEND_TOGGLE 0x00 -#define EXTEND_TOGGLE_CHK 0x20 -#define MS_BUS_WIDTH_1 0x00 -#define MS_BUS_WIDTH_4 0x10 -#define MS_BUS_WIDTH_8 0x18 -#define MS_2K_SECTOR_MODE 0x04 -#define MS_512_SECTOR_MODE 0x00 -#define MS_TOGGLE_TIMEOUT_EN 0x00 -#define MS_TOGGLE_TIMEOUT_DISEN 0x01 -#define MS_NO_CHECK_INT 0x02 - -#define WAIT_INT 0x80 -#define NO_WAIT_INT 0x00 -#define NO_AUTO_READ_INT_REG 0x00 -#define AUTO_READ_INT_REG 0x40 -#define MS_CRC16_ERR 0x20 -#define MS_RDY_TIMEOUT 0x10 -#define MS_INT_CMDNK 0x08 -#define MS_INT_BREQ 0x04 -#define MS_INT_ERR 0x02 -#define MS_INT_CED 0x01 - -#define MS_TRANSFER_START 0x80 -#define MS_TRANSFER_END 0x40 -#define MS_TRANSFER_ERR 0x20 -#define MS_BS_STATE 0x10 -#define MS_TM_READ_BYTES 0x00 -#define MS_TM_NORMAL_READ 0x01 -#define MS_TM_WRITE_BYTES 0x04 -#define MS_TM_NORMAL_WRITE 0x05 -#define MS_TM_AUTO_READ 0x08 -#define MS_TM_AUTO_WRITE 0x0C - -#define CARD_SHARE_MASK 0x0F -#define CARD_SHARE_MULTI_LUN 0x00 -#define CARD_SHARE_NORMAL 0x00 -#define CARD_SHARE_48_XD 0x02 -#define CARD_SHARE_48_SD 0x04 -#define CARD_SHARE_48_MS 0x08 -#define CARD_SHARE_BAROSSA_XD 0x00 -#define CARD_SHARE_BAROSSA_SD 0x01 -#define CARD_SHARE_BAROSSA_MS 0x02 - -#define MS_DRIVE_8 0x00 -#define MS_DRIVE_4 0x40 -#define MS_DRIVE_12 0x80 -#define SD_DRIVE_8 0x00 -#define SD_DRIVE_4 0x10 -#define SD_DRIVE_12 0x20 -#define XD_DRIVE_8 0x00 -#define XD_DRIVE_4 0x04 -#define XD_DRIVE_12 0x08 - -#define SPI_STOP 0x01 -#define XD_STOP 0x02 -#define SD_STOP 0x04 -#define MS_STOP 0x08 -#define SPI_CLR_ERR 0x10 -#define XD_CLR_ERR 0x20 -#define SD_CLR_ERR 0x40 -#define MS_CLR_ERR 0x80 - -#define CRC_FIX_CLK (0x00 << 0) -#define CRC_VAR_CLK0 (0x01 << 0) -#define CRC_VAR_CLK1 (0x02 << 0) -#define SD30_FIX_CLK (0x00 << 2) -#define SD30_VAR_CLK0 (0x01 << 2) -#define SD30_VAR_CLK1 (0x02 << 2) -#define SAMPLE_FIX_CLK (0x00 << 4) -#define SAMPLE_VAR_CLK0 (0x01 << 4) -#define SAMPLE_VAR_CLK1 (0x02 << 4) - -#define SDIO_VER_20 0x80 -#define SDIO_VER_10 0x00 -#define SDIO_VER_CHG 0x40 -#define SDIO_BUS_AUTO_SWITCH 0x10 - -#define PINGPONG_BUFFER 0x01 -#define RING_BUFFER 0x00 - -#define RB_FLUSH 0x80 - -#define DMA_DONE_INT_EN 0x80 -#define SUSPEND_INT_EN 0x40 -#define LINK_RDY_INT_EN 0x20 -#define LINK_DOWN_INT_EN 0x10 - -#define DMA_DONE_INT 0x80 -#define SUSPEND_INT 0x40 -#define LINK_RDY_INT 0x20 -#define LINK_DOWN_INT 0x10 - -#define MRD_ERR_INT_EN 0x40 -#define MWR_ERR_INT_EN 0x20 -#define SCSI_CMD_INT_EN 0x10 -#define TLP_RCV_INT_EN 0x08 -#define TLP_TRSMT_INT_EN 0x04 -#define MRD_COMPLETE_INT_EN 0x02 -#define MWR_COMPLETE_INT_EN 0x01 - -#define MRD_ERR_INT 0x40 -#define MWR_ERR_INT 0x20 -#define SCSI_CMD_INT 0x10 -#define TLP_RX_INT 0x08 -#define TLP_TX_INT 0x04 -#define MRD_COMPLETE_INT 0x02 -#define MWR_COMPLETE_INT 0x01 - -#define MSG_RX_INT_EN 0x08 -#define MRD_RX_INT_EN 0x04 -#define MWR_RX_INT_EN 0x02 -#define CPLD_RX_INT_EN 0x01 - -#define MSG_RX_INT 0x08 -#define MRD_RX_INT 0x04 -#define MWR_RX_INT 0x02 -#define CPLD_RX_INT 0x01 - -#define MSG_TX_INT_EN 0x08 -#define MRD_TX_INT_EN 0x04 -#define MWR_TX_INT_EN 0x02 -#define CPLD_TX_INT_EN 0x01 - -#define MSG_TX_INT 0x08 -#define MRD_TX_INT 0x04 -#define MWR_TX_INT 0x02 -#define CPLD_TX_INT 0x01 - -#define DMA_RST 0x80 -#define DMA_BUSY 0x04 -#define DMA_DIR_TO_CARD 0x00 -#define DMA_DIR_FROM_CARD 0x02 -#define DMA_EN 0x01 -#define DMA_128 (0 << 4) -#define DMA_256 (1 << 4) -#define DMA_512 (2 << 4) -#define DMA_1024 (3 << 4) -#define DMA_PACK_SIZE_MASK 0x30 - -#define XD_PWR_OFF_DELAY0 0x00 -#define XD_PWR_OFF_DELAY1 0x02 -#define XD_PWR_OFF_DELAY2 0x04 -#define XD_PWR_OFF_DELAY3 0x06 -#define XD_AUTO_PWR_OFF_EN 0xF7 -#define XD_NO_AUTO_PWR_OFF 0x08 - -#define XD_TIME_RWN_1 0x00 -#define XD_TIME_RWN_STEP 0x20 -#define XD_TIME_RW_1 0x00 -#define XD_TIME_RW_STEP 0x04 -#define XD_TIME_SETUP_1 0x00 -#define XD_TIME_SETUP_STEP 0x01 - -#define XD_ECC2_UNCORRECTABLE 0x80 -#define XD_ECC2_ERROR 0x40 -#define XD_ECC1_UNCORRECTABLE 0x20 -#define XD_ECC1_ERROR 0x10 -#define XD_RDY 0x04 -#define XD_CE_EN 0xFD -#define XD_CE_DISEN 0x02 -#define XD_WP_EN 0xFE -#define XD_WP_DISEN 0x01 - -#define XD_TRANSFER_START 0x80 -#define XD_TRANSFER_END 0x40 -#define XD_PPB_EMPTY 0x20 -#define XD_RESET 0x00 -#define XD_ERASE 0x01 -#define XD_READ_STATUS 0x02 -#define XD_READ_ID 0x03 -#define XD_READ_REDUNDANT 0x04 -#define XD_READ_PAGES 0x05 -#define XD_SET_CMD 0x06 -#define XD_NORMAL_READ 0x07 -#define XD_WRITE_PAGES 0x08 -#define XD_NORMAL_WRITE 0x09 -#define XD_WRITE_REDUNDANT 0x0A -#define XD_SET_ADDR 0x0B - -#define XD_PPB_TO_SIE 0x80 -#define XD_TO_PPB_ONLY 0x00 -#define XD_BA_TRANSFORM 0x40 -#define XD_BA_NO_TRANSFORM 0x00 -#define XD_NO_CALC_ECC 0x20 -#define XD_CALC_ECC 0x00 -#define XD_IGNORE_ECC 0x10 -#define XD_CHECK_ECC 0x00 -#define XD_DIRECT_TO_RB 0x08 -#define XD_ADDR_LENGTH_0 0x00 -#define XD_ADDR_LENGTH_1 0x01 -#define XD_ADDR_LENGTH_2 0x02 -#define XD_ADDR_LENGTH_3 0x03 -#define XD_ADDR_LENGTH_4 0x04 - -#define XD_GPG 0xFF -#define XD_BPG 0x00 - -#define XD_GBLK 0xFF -#define XD_LATER_BBLK 0xF0 - -#define XD_ECC2_ALL1 0x80 -#define XD_ECC1_ALL1 0x40 -#define XD_BA2_ALL0 0x20 -#define XD_BA1_ALL0 0x10 -#define XD_BA1_BA2_EQL 0x04 -#define XD_BA2_VALID 0x02 -#define XD_BA1_VALID 0x01 - -#define XD_PGSTS_ZEROBIT_OVER4 0x00 -#define XD_PGSTS_NOT_FF 0x02 -#define XD_AUTO_CHK_DATA_STATUS 0x01 - -#define RSTB_MODE_DETECT 0x80 -#define MODE_OUT_VLD 0x40 -#define MODE_OUT_0_NONE 0x00 -#define MODE_OUT_10_NONE 0x04 -#define MODE_OUT_10_47 0x05 -#define MODE_OUT_10_180 0x06 -#define MODE_OUT_10_680 0x07 -#define MODE_OUT_16_NONE 0x08 -#define MODE_OUT_16_47 0x09 -#define MODE_OUT_16_180 0x0A -#define MODE_OUT_16_680 0x0B -#define MODE_OUT_NONE_NONE 0x0C -#define MODE_OUT_NONE_47 0x0D -#define MODE_OUT_NONE_180 0x0E -#define MODE_OUT_NONE_680 0x0F - -#define CARD_OC_INT_EN 0x20 -#define CARD_DETECT_EN 0x08 - -#define MS_DETECT_EN 0x80 -#define MS_OCP_INT_EN 0x40 -#define MS_OCP_INT_CLR 0x20 -#define MS_OC_CLR 0x10 -#define SD_DETECT_EN 0x08 -#define SD_OCP_INT_EN 0x04 -#define SD_OCP_INT_CLR 0x02 -#define SD_OC_CLR 0x01 - -#define CARD_OCP_DETECT 0x80 -#define CARD_OC_NOW 0x08 -#define CARD_OC_EVER 0x04 - -#define MS_OCP_DETECT 0x80 -#define MS_OC_NOW 0x40 -#define MS_OC_EVER 0x20 -#define SD_OCP_DETECT 0x08 -#define SD_OC_NOW 0x04 -#define SD_OC_EVER 0x02 - -#define CARD_OC_INT_CLR 0x08 -#define CARD_OC_CLR 0x02 - -#define SD_OCP_GLITCH_MASK 0x07 -#define SD_OCP_GLITCH_6_4 0x00 -#define SD_OCP_GLITCH_64 0x01 -#define SD_OCP_GLITCH_640 0x02 -#define SD_OCP_GLITCH_1000 0x03 -#define SD_OCP_GLITCH_2000 0x04 -#define SD_OCP_GLITCH_4000 0x05 -#define SD_OCP_GLITCH_8000 0x06 -#define SD_OCP_GLITCH_10000 0x07 - -#define MS_OCP_GLITCH_MASK 0x70 -#define MS_OCP_GLITCH_6_4 (0x00 << 4) -#define MS_OCP_GLITCH_64 (0x01 << 4) -#define MS_OCP_GLITCH_640 (0x02 << 4) -#define MS_OCP_GLITCH_1000 (0x03 << 4) -#define MS_OCP_GLITCH_2000 (0x04 << 4) -#define MS_OCP_GLITCH_4000 (0x05 << 4) -#define MS_OCP_GLITCH_8000 (0x06 << 4) -#define MS_OCP_GLITCH_10000 (0x07 << 4) - -#define OCP_TIME_60 0x00 -#define OCP_TIME_100 (0x01 << 3) -#define OCP_TIME_200 (0x02 << 3) -#define OCP_TIME_400 (0x03 << 3) -#define OCP_TIME_600 (0x04 << 3) -#define OCP_TIME_800 (0x05 << 3) -#define OCP_TIME_1100 (0x06 << 3) -#define OCP_TIME_MASK 0x38 - -#define MS_OCP_TIME_60 0x00 -#define MS_OCP_TIME_100 (0x01 << 4) -#define MS_OCP_TIME_200 (0x02 << 4) -#define MS_OCP_TIME_400 (0x03 << 4) -#define MS_OCP_TIME_600 (0x04 << 4) -#define MS_OCP_TIME_800 (0x05 << 4) -#define MS_OCP_TIME_1100 (0x06 << 4) -#define MS_OCP_TIME_MASK 0x70 - -#define SD_OCP_TIME_60 0x00 -#define SD_OCP_TIME_100 0x01 -#define SD_OCP_TIME_200 0x02 -#define SD_OCP_TIME_400 0x03 -#define SD_OCP_TIME_600 0x04 -#define SD_OCP_TIME_800 0x05 -#define SD_OCP_TIME_1100 0x06 -#define SD_OCP_TIME_MASK 0x07 - -#define OCP_THD_315_417 0x00 -#define OCP_THD_283_783 (0x01 << 6) -#define OCP_THD_244_946 (0x02 << 6) -#define OCP_THD_191_1080 (0x03 << 6) -#define OCP_THD_MASK 0xC0 - -#define MS_OCP_THD_450 0x00 -#define MS_OCP_THD_550 (0x01 << 4) -#define MS_OCP_THD_650 (0x02 << 4) -#define MS_OCP_THD_750 (0x03 << 4) -#define MS_OCP_THD_850 (0x04 << 4) -#define MS_OCP_THD_950 (0x05 << 4) -#define MS_OCP_THD_1050 (0x06 << 4) -#define MS_OCP_THD_1150 (0x07 << 4) -#define MS_OCP_THD_MASK 0x70 - -#define SD_OCP_THD_450 0x00 -#define SD_OCP_THD_550 0x01 -#define SD_OCP_THD_650 0x02 -#define SD_OCP_THD_750 0x03 -#define SD_OCP_THD_850 0x04 -#define SD_OCP_THD_950 0x05 -#define SD_OCP_THD_1050 0x06 -#define SD_OCP_THD_1150 0x07 -#define SD_OCP_THD_MASK 0x07 - -#define FPGA_MS_PULL_CTL_EN 0xEF -#define FPGA_SD_PULL_CTL_EN 0xF7 -#define FPGA_XD_PULL_CTL_EN1 0xFE -#define FPGA_XD_PULL_CTL_EN2 0xFD -#define FPGA_XD_PULL_CTL_EN3 0xFB - -#define FPGA_MS_PULL_CTL_BIT 0x10 -#define FPGA_SD_PULL_CTL_BIT 0x08 - -#define BLINK_EN 0x08 -#define LED_GPIO0 (0 << 4) -#define LED_GPIO1 (1 << 4) -#define LED_GPIO2 (2 << 4) - -#define SDIO_BUS_CTRL 0x01 -#define SDIO_CD_CTRL 0x02 - -#define SSC_RSTB 0x80 -#define SSC_8X_EN 0x40 -#define SSC_FIX_FRAC 0x20 -#define SSC_SEL_1M 0x00 -#define SSC_SEL_2M 0x08 -#define SSC_SEL_4M 0x10 -#define SSC_SEL_8M 0x18 - -#define SSC_DEPTH_MASK 0x07 -#define SSC_DEPTH_DISALBE 0x00 -#define SSC_DEPTH_4M 0x01 -#define SSC_DEPTH_2M 0x02 -#define SSC_DEPTH_1M 0x03 -#define SSC_DEPTH_512K 0x04 -#define SSC_DEPTH_256K 0x05 -#define SSC_DEPTH_128K 0x06 -#define SSC_DEPTH_64K 0x07 - -#define XD_D3_NP 0x00 -#define XD_D3_PD (0x01 << 6) -#define XD_D3_PU (0x02 << 6) -#define XD_D2_NP 0x00 -#define XD_D2_PD (0x01 << 4) -#define XD_D2_PU (0x02 << 4) -#define XD_D1_NP 0x00 -#define XD_D1_PD (0x01 << 2) -#define XD_D1_PU (0x02 << 2) -#define XD_D0_NP 0x00 -#define XD_D0_PD 0x01 -#define XD_D0_PU 0x02 - -#define SD_D7_NP 0x00 -#define SD_D7_PD (0x01 << 4) -#define SD_DAT7_PU (0x02 << 4) -#define SD_CLK_NP 0x00 -#define SD_CLK_PD (0x01 << 2) -#define SD_CLK_PU (0x02 << 2) -#define SD_D5_NP 0x00 -#define SD_D5_PD 0x01 -#define SD_D5_PU 0x02 - -#define MS_D1_NP 0x00 -#define MS_D1_PD (0x01 << 6) -#define MS_D1_PU (0x02 << 6) -#define MS_D2_NP 0x00 -#define MS_D2_PD (0x01 << 4) -#define MS_D2_PU (0x02 << 4) -#define MS_CLK_NP 0x00 -#define MS_CLK_PD (0x01 << 2) -#define MS_CLK_PU (0x02 << 2) -#define MS_D6_NP 0x00 -#define MS_D6_PD 0x01 -#define MS_D6_PU 0x02 - -#define XD_D7_NP 0x00 -#define XD_D7_PD (0x01 << 6) -#define XD_D7_PU (0x02 << 6) -#define XD_D6_NP 0x00 -#define XD_D6_PD (0x01 << 4) -#define XD_D6_PU (0x02 << 4) -#define XD_D5_NP 0x00 -#define XD_D5_PD (0x01 << 2) -#define XD_D5_PU (0x02 << 2) -#define XD_D4_NP 0x00 -#define XD_D4_PD 0x01 -#define XD_D4_PU 0x02 - -#define SD_D6_NP 0x00 -#define SD_D6_PD (0x01 << 6) -#define SD_D6_PU (0x02 << 6) -#define SD_D0_NP 0x00 -#define SD_D0_PD (0x01 << 4) -#define SD_D0_PU (0x02 << 4) -#define SD_D1_NP 0x00 -#define SD_D1_PD 0x01 -#define SD_D1_PU 0x02 - -#define MS_D3_NP 0x00 -#define MS_D3_PD (0x01 << 6) -#define MS_D3_PU (0x02 << 6) -#define MS_D0_NP 0x00 -#define MS_D0_PD (0x01 << 4) -#define MS_D0_PU (0x02 << 4) -#define MS_BS_NP 0x00 -#define MS_BS_PD (0x01 << 2) -#define MS_BS_PU (0x02 << 2) - -#define XD_WP_NP 0x00 -#define XD_WP_PD (0x01 << 6) -#define XD_WP_PU (0x02 << 6) -#define XD_CE_NP 0x00 -#define XD_CE_PD (0x01 << 3) -#define XD_CE_PU (0x02 << 3) -#define XD_CLE_NP 0x00 -#define XD_CLE_PD (0x01 << 1) -#define XD_CLE_PU (0x02 << 1) -#define XD_CD_PD 0x00 -#define XD_CD_PU 0x01 - -#define SD_D4_NP 0x00 -#define SD_D4_PD (0x01 << 6) -#define SD_D4_PU (0x02 << 6) - -#define MS_D7_NP 0x00 -#define MS_D7_PD (0x01 << 6) -#define MS_D7_PU (0x02 << 6) - -#define XD_RDY_NP 0x00 -#define XD_RDY_PD (0x01 << 6) -#define XD_RDY_PU (0x02 << 6) -#define XD_WE_NP 0x00 -#define XD_WE_PD (0x01 << 4) -#define XD_WE_PU (0x02 << 4) -#define XD_RE_NP 0x00 -#define XD_RE_PD (0x01 << 2) -#define XD_RE_PU (0x02 << 2) -#define XD_ALE_NP 0x00 -#define XD_ALE_PD 0x01 -#define XD_ALE_PU 0x02 - -#define SD_D3_NP 0x00 -#define SD_D3_PD (0x01 << 4) -#define SD_D3_PU (0x02 << 4) -#define SD_D2_NP 0x00 -#define SD_D2_PD (0x01 << 2) -#define SD_D2_PU (0x02 << 2) - -#define MS_INS_PD 0x00 -#define MS_INS_PU (0x01 << 7) -#define SD_WP_NP 0x00 -#define SD_WP_PD (0x01 << 5) -#define SD_WP_PU (0x02 << 5) -#define SD_CD_PD 0x00 -#define SD_CD_PU (0x01 << 4) -#define SD_CMD_NP 0x00 -#define SD_CMD_PD (0x01 << 2) -#define SD_CMD_PU (0x02 << 2) - -#define MS_D5_NP 0x00 -#define MS_D5_PD (0x01 << 2) -#define MS_D5_PU (0x02 << 2) -#define MS_D4_NP 0x00 -#define MS_D4_PD 0x01 -#define MS_D4_PU 0x02 - -#define FORCE_PM_CLOCK 0x10 -#define EN_CLOCK_PM 0x01 - -#define HOST_ENTER_S3 0x02 -#define HOST_ENTER_S1 0x01 - -#define AUX_PWR_DETECTED 0x01 - -#define PHY_DEBUG_MODE 0x01 - -#define SPI_COMMAND_BIT_8 0xE0 -#define SPI_ADDRESS_BIT_24 0x17 -#define SPI_ADDRESS_BIT_32 0x1F - -#define SPI_TRANSFER0_START 0x80 -#define SPI_TRANSFER0_END 0x40 -#define SPI_C_MODE0 0x00 -#define SPI_CA_MODE0 0x01 -#define SPI_CDO_MODE0 0x02 -#define SPI_CDI_MODE0 0x03 -#define SPI_CADO_MODE0 0x04 -#define SPI_CADI_MODE0 0x05 -#define SPI_POLLING_MODE0 0x06 - -#define SPI_TRANSFER1_START 0x80 -#define SPI_TRANSFER1_END 0x40 -#define SPI_DO_MODE1 0x00 -#define SPI_DI_MODE1 0x01 - -#define CS_POLARITY_HIGH 0x40 -#define CS_POLARITY_LOW 0x00 -#define DTO_MSB_FIRST 0x00 -#define DTO_LSB_FIRST 0x20 -#define SPI_MASTER 0x00 -#define SPI_SLAVE 0x10 -#define SPI_MODE0 0x00 -#define SPI_MODE1 0x04 -#define SPI_MODE2 0x08 -#define SPI_MODE3 0x0C -#define SPI_MANUAL 0x00 -#define SPI_HALF_AUTO 0x01 -#define SPI_AUTO 0x02 -#define SPI_EEPROM_AUTO 0x03 - -#define EDO_TIMING_MASK 0x03 -#define SAMPLE_RISING 0x00 -#define SAMPLE_DELAY_HALF 0x01 -#define SAMPLE_DELAY_ONE 0x02 -#define SAPMLE_DELAY_ONE_HALF 0x03 -#define TCS_MASK 0x0C - -#define NOT_BYPASS_SD 0x02 -#define DISABLE_SDIO_FUNC 0x04 -#define SELECT_1LUN 0x08 - -#define PWR_GATE_EN 0x01 -#define LDO3318_PWR_MASK 0x06 -#define LDO_ON 0x00 -#define LDO_SUSPEND 0x04 -#define LDO_OFF 0x06 - -#define SD_CFG1 0xFDA0 -#define SD_CFG2 0xFDA1 -#define SD_CFG3 0xFDA2 -#define SD_STAT1 0xFDA3 -#define SD_STAT2 0xFDA4 -#define SD_BUS_STAT 0xFDA5 -#define SD_PAD_CTL 0xFDA6 -#define SD_SAMPLE_POINT_CTL 0xFDA7 -#define SD_PUSH_POINT_CTL 0xFDA8 -#define SD_CMD0 0xFDA9 -#define SD_CMD1 0xFDAA -#define SD_CMD2 0xFDAB -#define SD_CMD3 0xFDAC -#define SD_CMD4 0xFDAD -#define SD_CMD5 0xFDAE -#define SD_BYTE_CNT_L 0xFDAF -#define SD_BYTE_CNT_H 0xFDB0 -#define SD_BLOCK_CNT_L 0xFDB1 -#define SD_BLOCK_CNT_H 0xFDB2 -#define SD_TRANSFER 0xFDB3 -#define SD_CMD_STATE 0xFDB5 -#define SD_DATA_STATE 0xFDB6 - -#define DCM_DRP_CTL 0xFC23 -#define DCM_DRP_TRIG 0xFC24 -#define DCM_DRP_CFG 0xFC25 -#define DCM_DRP_WR_DATA_L 0xFC26 -#define DCM_DRP_WR_DATA_H 0xFC27 -#define DCM_DRP_RD_DATA_L 0xFC28 -#define DCM_DRP_RD_DATA_H 0xFC29 -#define SD_VPCLK0_CTL 0xFC2A -#define SD_VPCLK1_CTL 0xFC2B -#define SD_DCMPS0_CTL 0xFC2C -#define SD_DCMPS1_CTL 0xFC2D -#define SD_VPTX_CTL SD_VPCLK0_CTL -#define SD_VPRX_CTL SD_VPCLK1_CTL -#define SD_DCMPS_TX_CTL SD_DCMPS0_CTL -#define SD_DCMPS_RX_CTL SD_DCMPS1_CTL - -#define CARD_CLK_SOURCE 0xFC2E - -#define CARD_PWR_CTL 0xFD50 -#define CARD_CLK_SWITCH 0xFD51 -#define CARD_SHARE_MODE 0xFD52 -#define CARD_DRIVE_SEL 0xFD53 -#define CARD_STOP 0xFD54 -#define CARD_OE 0xFD55 -#define CARD_AUTO_BLINK 0xFD56 -#define CARD_GPIO_DIR 0xFD57 -#define CARD_GPIO 0xFD58 - -#define CARD_DATA_SOURCE 0xFD5B -#define CARD_SELECT 0xFD5C -#define SD30_DRIVE_SEL 0xFD5E - -#define CARD_CLK_EN 0xFD69 - -#define SDIO_CTRL 0xFD6B - -#define FPDCTL 0xFC00 -#define PDINFO 0xFC01 - -#define CLK_CTL 0xFC02 -#define CLK_DIV 0xFC03 -#define CLK_SEL 0xFC04 - -#define SSC_DIV_N_0 0xFC0F -#define SSC_DIV_N_1 0xFC10 - -#define RCCTL 0xFC14 - -#define FPGA_PULL_CTL 0xFC1D - -#define CARD_PULL_CTL1 0xFD60 -#define CARD_PULL_CTL2 0xFD61 -#define CARD_PULL_CTL3 0xFD62 -#define CARD_PULL_CTL4 0xFD63 -#define CARD_PULL_CTL5 0xFD64 -#define CARD_PULL_CTL6 0xFD65 - -#define IRQEN0 0xFE20 -#define IRQSTAT0 0xFE21 -#define IRQEN1 0xFE22 -#define IRQSTAT1 0xFE23 -#define TLPRIEN 0xFE24 -#define TLPRISTAT 0xFE25 -#define TLPTIEN 0xFE26 -#define TLPTISTAT 0xFE27 -#define DMATC0 0xFE28 -#define DMATC1 0xFE29 -#define DMATC2 0xFE2A -#define DMATC3 0xFE2B -#define DMACTL 0xFE2C -#define BCTL 0xFE2D -#define RBBC0 0xFE2E -#define RBBC1 0xFE2F -#define RBDAT 0xFE30 -#define RBCTL 0xFE34 -#define CFGADDR0 0xFE35 -#define CFGADDR1 0xFE36 -#define CFGDATA0 0xFE37 -#define CFGDATA1 0xFE38 -#define CFGDATA2 0xFE39 -#define CFGDATA3 0xFE3A -#define CFGRWCTL 0xFE3B -#define PHYRWCTL 0xFE3C -#define PHYDATA0 0xFE3D -#define PHYDATA1 0xFE3E -#define PHYADDR 0xFE3F -#define MSGRXDATA0 0xFE40 -#define MSGRXDATA1 0xFE41 -#define MSGRXDATA2 0xFE42 -#define MSGRXDATA3 0xFE43 -#define MSGTXDATA0 0xFE44 -#define MSGTXDATA1 0xFE45 -#define MSGTXDATA2 0xFE46 -#define MSGTXDATA3 0xFE47 -#define MSGTXCTL 0xFE48 -#define PETXCFG 0xFE49 - -#define CDRESUMECTL 0xFE52 -#define WAKE_SEL_CTL 0xFE54 -#define PME_FORCE_CTL 0xFE56 -#define ASPM_FORCE_CTL 0xFE57 -#define PM_CLK_FORCE_CTL 0xFE58 -#define PERST_GLITCH_WIDTH 0xFE5C -#define CHANGE_LINK_STATE 0xFE5B -#define RESET_LOAD_REG 0xFE5E -#define HOST_SLEEP_STATE 0xFE60 -#define MAIN_PWR_OFF_CTL 0xFE70 /* RTS5208 */ - -#define NFTS_TX_CTRL 0xFE72 - -#define PWR_GATE_CTRL 0xFE75 -#define PWD_SUSPEND_EN 0xFE76 - -#define EFUSE_CONTENT 0xFE5F - -#define XD_INIT 0xFD10 -#define XD_DTCTL 0xFD11 -#define XD_CTL 0xFD12 -#define XD_TRANSFER 0xFD13 -#define XD_CFG 0xFD14 -#define XD_ADDRESS0 0xFD15 -#define XD_ADDRESS1 0xFD16 -#define XD_ADDRESS2 0xFD17 -#define XD_ADDRESS3 0xFD18 -#define XD_ADDRESS4 0xFD19 -#define XD_DAT 0xFD1A -#define XD_PAGE_CNT 0xFD1B -#define XD_PAGE_STATUS 0xFD1C -#define XD_BLOCK_STATUS 0xFD1D -#define XD_BLOCK_ADDR1_L 0xFD1E -#define XD_BLOCK_ADDR1_H 0xFD1F -#define XD_BLOCK_ADDR2_L 0xFD20 -#define XD_BLOCK_ADDR2_H 0xFD21 -#define XD_BYTE_CNT_L 0xFD22 -#define XD_BYTE_CNT_H 0xFD23 -#define XD_PARITY 0xFD24 -#define XD_ECC_BIT1 0xFD25 -#define XD_ECC_BYTE1 0xFD26 -#define XD_ECC_BIT2 0xFD27 -#define XD_ECC_BYTE2 0xFD28 -#define XD_RESERVED0 0xFD29 -#define XD_RESERVED1 0xFD2A -#define XD_RESERVED2 0xFD2B -#define XD_RESERVED3 0xFD2C -#define XD_CHK_DATA_STATUS 0xFD2D -#define XD_CATCTL 0xFD2E - -#define MS_CFG 0xFD40 -#define MS_TPC 0xFD41 -#define MS_TRANS_CFG 0xFD42 -#define MS_TRANSFER 0xFD43 -#define MS_INT_REG 0xFD44 -#define MS_BYTE_CNT 0xFD45 -#define MS_SECTOR_CNT_L 0xFD46 -#define MS_SECTOR_CNT_H 0xFD47 -#define MS_DBUS_H 0xFD48 - -#define SSC_CTL1 0xFC11 -#define SSC_CTL2 0xFC12 - -#define OCPCTL 0xFC15 -#define OCPSTAT 0xFC16 -#define OCPCLR 0xFC17 /* 5208 */ -#define OCPPARA1 0xFC18 -#define OCPPARA2 0xFC19 - -#define EFUSE_OP 0xFC20 -#define EFUSE_CTRL 0xFC21 -#define EFUSE_DATA 0xFC22 - -#define SPI_COMMAND 0xFD80 -#define SPI_ADDR0 0xFD81 -#define SPI_ADDR1 0xFD82 -#define SPI_ADDR2 0xFD83 -#define SPI_ADDR3 0xFD84 -#define SPI_CA_NUMBER 0xFD85 -#define SPI_LENGTH0 0xFD86 -#define SPI_LENGTH1 0xFD87 -#define SPI_DATA 0xFD88 -#define SPI_DATA_NUMBER 0xFD89 -#define SPI_TRANSFER0 0xFD90 -#define SPI_TRANSFER1 0xFD91 -#define SPI_CONTROL 0xFD92 -#define SPI_SIG 0xFD93 -#define SPI_TCTL 0xFD94 -#define SPI_SLAVE_NUM 0xFD95 -#define SPI_CLK_DIVIDER0 0xFD96 -#define SPI_CLK_DIVIDER1 0xFD97 - -#define SRAM_BASE 0xE600 -#define RBUF_BASE 0xF400 -#define PPBUF_BASE1 0xF800 -#define PPBUF_BASE2 0xFA00 -#define IMAGE_FLAG_ADDR0 0xCE80 -#define IMAGE_FLAG_ADDR1 0xCE81 - -#define READ_OP 1 -#define WRITE_OP 2 - -#define LCTLR 0x80 - -#define POLLING_WAIT_CNT 1 -#define IDLE_MAX_COUNT 10 -#define SDIO_IDLE_COUNT 10 - -#define DEBOUNCE_CNT 5 - -void do_remaining_work(struct rtsx_chip *chip); -void try_to_switch_sdio_ctrl(struct rtsx_chip *chip); -void do_reset_sd_card(struct rtsx_chip *chip); -void do_reset_xd_card(struct rtsx_chip *chip); -void do_reset_ms_card(struct rtsx_chip *chip); -void rtsx_power_off_card(struct rtsx_chip *chip); -void rtsx_release_cards(struct rtsx_chip *chip); -void rtsx_reset_cards(struct rtsx_chip *chip); -void rtsx_reinit_cards(struct rtsx_chip *chip, int reset_chip); -void rtsx_init_cards(struct rtsx_chip *chip); -int switch_ssc_clock(struct rtsx_chip *chip, int clk); -int switch_normal_clock(struct rtsx_chip *chip, int clk); -int enable_card_clock(struct rtsx_chip *chip, u8 card); -int disable_card_clock(struct rtsx_chip *chip, u8 card); -int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 sec_addr, u16 sec_cnt); -void trans_dma_enable(enum dma_data_direction dir, - struct rtsx_chip *chip, u32 byte_cnt, u8 pack_size); -void toggle_gpio(struct rtsx_chip *chip, u8 gpio); -void turn_on_led(struct rtsx_chip *chip, u8 gpio); -void turn_off_led(struct rtsx_chip *chip, u8 gpio); - -int card_share_mode(struct rtsx_chip *chip, int card); -int select_card(struct rtsx_chip *chip, int card); -int detect_card_cd(struct rtsx_chip *chip, int card); -int check_card_exist(struct rtsx_chip *chip, unsigned int lun); -int check_card_ready(struct rtsx_chip *chip, unsigned int lun); -int check_card_wp(struct rtsx_chip *chip, unsigned int lun); -void eject_card(struct rtsx_chip *chip, unsigned int lun); -u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun); - -static inline u32 get_card_size(struct rtsx_chip *chip, unsigned int lun) -{ -#ifdef SUPPORT_SD_LOCK - struct sd_info *sd_card = &chip->sd_card; - - if ((get_lun_card(chip, lun) == SD_CARD) && - (sd_card->sd_lock_status & SD_LOCKED)) - return 0; - - return chip->capacity[lun]; -#else - return chip->capacity[lun]; -#endif -} - -static inline int switch_clock(struct rtsx_chip *chip, int clk) -{ - int retval = 0; - - if (chip->asic_code) - retval = switch_ssc_clock(chip, clk); - else - retval = switch_normal_clock(chip, clk); - - return retval; -} - -int card_power_on(struct rtsx_chip *chip, u8 card); -int card_power_off(struct rtsx_chip *chip, u8 card); - -static inline int card_power_off_all(struct rtsx_chip *chip) -{ - int retval; - - retval = rtsx_write_register(chip, CARD_PWR_CTL, 0x0F, 0x0F); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static inline void rtsx_clear_xd_error(struct rtsx_chip *chip) -{ - rtsx_write_register(chip, CARD_STOP, XD_STOP | XD_CLR_ERR, - XD_STOP | XD_CLR_ERR); -} - -static inline void rtsx_clear_sd_error(struct rtsx_chip *chip) -{ - rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR, - SD_STOP | SD_CLR_ERR); -} - -static inline void rtsx_clear_ms_error(struct rtsx_chip *chip) -{ - rtsx_write_register(chip, CARD_STOP, MS_STOP | MS_CLR_ERR, - MS_STOP | MS_CLR_ERR); -} - -static inline void rtsx_clear_spi_error(struct rtsx_chip *chip) -{ - rtsx_write_register(chip, CARD_STOP, SPI_STOP | SPI_CLR_ERR, - SPI_STOP | SPI_CLR_ERR); -} - -#ifdef SUPPORT_SDIO_ASPM -void dynamic_configure_sdio_aspm(struct rtsx_chip *chip); -#endif - -#endif /* __REALTEK_RTSX_CARD_H */ diff --git a/drivers/staging/rts5208/rtsx_chip.c b/drivers/staging/rts5208/rtsx_chip.c deleted file mode 100644 index 6375032918d4c..0000000000000 --- a/drivers/staging/rts5208/rtsx_chip.c +++ /dev/null @@ -1,2161 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include -#include -#include - -#include "rtsx.h" -#include "sd.h" -#include "xd.h" -#include "ms.h" - -static void rtsx_calibration(struct rtsx_chip *chip) -{ - rtsx_write_phy_register(chip, 0x1B, 0x135E); - wait_timeout(10); - rtsx_write_phy_register(chip, 0x00, 0x0280); - rtsx_write_phy_register(chip, 0x01, 0x7112); - rtsx_write_phy_register(chip, 0x01, 0x7110); - rtsx_write_phy_register(chip, 0x01, 0x7112); - rtsx_write_phy_register(chip, 0x01, 0x7113); - rtsx_write_phy_register(chip, 0x00, 0x0288); -} - -void rtsx_enable_card_int(struct rtsx_chip *chip) -{ - u32 reg = rtsx_readl(chip, RTSX_BIER); - int i; - - for (i = 0; i <= chip->max_lun; i++) { - if (chip->lun2card[i] & XD_CARD) - reg |= XD_INT_EN; - if (chip->lun2card[i] & SD_CARD) - reg |= SD_INT_EN; - if (chip->lun2card[i] & MS_CARD) - reg |= MS_INT_EN; - } - if (chip->hw_bypass_sd) - reg &= ~((u32)SD_INT_EN); - - rtsx_writel(chip, RTSX_BIER, reg); -} - -void rtsx_enable_bus_int(struct rtsx_chip *chip) -{ - u32 reg = 0; -#ifndef DISABLE_CARD_INT - int i; -#endif - - reg = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN; - -#ifndef DISABLE_CARD_INT - for (i = 0; i <= chip->max_lun; i++) { - dev_dbg(rtsx_dev(chip), "lun2card[%d] = 0x%02x\n", - i, chip->lun2card[i]); - - if (chip->lun2card[i] & XD_CARD) - reg |= XD_INT_EN; - if (chip->lun2card[i] & SD_CARD) - reg |= SD_INT_EN; - if (chip->lun2card[i] & MS_CARD) - reg |= MS_INT_EN; - } - if (chip->hw_bypass_sd) - reg &= ~((u32)SD_INT_EN); -#endif - - if (chip->ic_version >= IC_VER_C) - reg |= DELINK_INT_EN; -#ifdef SUPPORT_OCP - reg |= OC_INT_EN; -#endif - if (!chip->adma_mode) - reg |= DATA_DONE_INT_EN; - - /* Enable Bus Interrupt */ - rtsx_writel(chip, RTSX_BIER, reg); - - dev_dbg(rtsx_dev(chip), "RTSX_BIER: 0x%08x\n", reg); -} - -void rtsx_disable_bus_int(struct rtsx_chip *chip) -{ - rtsx_writel(chip, RTSX_BIER, 0); -} - -static int rtsx_pre_handle_sdio_old(struct rtsx_chip *chip) -{ - int retval; - - if (chip->ignore_sd && CHK_SDIO_EXIST(chip)) { - if (chip->asic_code) { - retval = rtsx_write_register(chip, CARD_PULL_CTL5, - 0xFF, - MS_INS_PU | SD_WP_PU | - SD_CD_PU | SD_CMD_PU); - if (retval) - return retval; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, - 0xFF, - FPGA_SD_PULL_CTL_EN); - if (retval) - return retval; - } - retval = rtsx_write_register(chip, CARD_SHARE_MODE, 0xFF, - CARD_SHARE_48_SD); - if (retval) - return retval; - - /* Enable SDIO internal clock */ - retval = rtsx_write_register(chip, 0xFF2C, 0x01, 0x01); - if (retval) - return retval; - - retval = rtsx_write_register(chip, SDIO_CTRL, 0xFF, - SDIO_BUS_CTRL | SDIO_CD_CTRL); - if (retval) - return retval; - - chip->sd_int = 1; - chip->sd_io = 1; - } else { - chip->need_reset |= SD_CARD; - } - - return STATUS_SUCCESS; -} - -#ifdef HW_AUTO_SWITCH_SD_BUS -static int rtsx_pre_handle_sdio_new(struct rtsx_chip *chip) -{ - u8 tmp; - bool sw_bypass_sd = false; - int retval; - - if (chip->driver_first_load) { - if (CHECK_PID(chip, 0x5288)) { - retval = rtsx_read_register(chip, 0xFE5A, &tmp); - if (retval) - return retval; - if (tmp & 0x08) - sw_bypass_sd = true; - } else if (CHECK_PID(chip, 0x5208)) { - retval = rtsx_read_register(chip, 0xFE70, &tmp); - if (retval) - return retval; - if (tmp & 0x80) - sw_bypass_sd = true; - } - } else { - if (chip->sdio_in_charge) - sw_bypass_sd = true; - } - dev_dbg(rtsx_dev(chip), "chip->sdio_in_charge = %d\n", - chip->sdio_in_charge); - dev_dbg(rtsx_dev(chip), "chip->driver_first_load = %d\n", - chip->driver_first_load); - dev_dbg(rtsx_dev(chip), "sw_bypass_sd = %d\n", - sw_bypass_sd); - - if (sw_bypass_sd) { - u8 cd_toggle_mask = 0; - - retval = rtsx_read_register(chip, TLPTISTAT, &tmp); - if (retval) - return retval; - cd_toggle_mask = 0x08; - - if (tmp & cd_toggle_mask) { - /* Disable sdio_bus_auto_switch */ - if (CHECK_PID(chip, 0x5288)) { - retval = rtsx_write_register(chip, 0xFE5A, - 0x08, 0x00); - if (retval) - return retval; - } else if (CHECK_PID(chip, 0x5208)) { - retval = rtsx_write_register(chip, 0xFE70, - 0x80, 0x00); - if (retval) - return retval; - } - - retval = rtsx_write_register(chip, TLPTISTAT, 0xFF, - tmp); - if (retval) - return retval; - - chip->need_reset |= SD_CARD; - } else { - dev_dbg(rtsx_dev(chip), "Chip inserted with SDIO!\n"); - - if (chip->asic_code) { - retval = sd_pull_ctl_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register - (chip, FPGA_PULL_CTL, - FPGA_SD_PULL_CTL_BIT | 0x20, - 0); - if (retval) - return retval; - } - retval = card_share_mode(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - /* Enable sdio_bus_auto_switch */ - if (CHECK_PID(chip, 0x5288)) { - retval = rtsx_write_register(chip, 0xFE5A, - 0x08, 0x08); - if (retval) - return retval; - } else if (CHECK_PID(chip, 0x5208)) { - retval = rtsx_write_register(chip, 0xFE70, - 0x80, 0x80); - if (retval) - return retval; - } - - chip->chip_insert_with_sdio = 1; - chip->sd_io = 1; - } - } else { - retval = rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08); - if (retval) - return retval; - - chip->need_reset |= SD_CARD; - } - - return STATUS_SUCCESS; -} -#endif - -static int rtsx_reset_aspm(struct rtsx_chip *chip) -{ - int ret; - - if (chip->dynamic_aspm) { - if (!CHK_SDIO_EXIST(chip) || !CHECK_PID(chip, 0x5288)) - return STATUS_SUCCESS; - - ret = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF, - chip->aspm_l0s_l1_en); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; - } - - if (CHECK_PID(chip, 0x5208)) { - ret = rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFF, 0x3F); - if (ret) - return ret; - } - ret = rtsx_write_config_byte(chip, LCTLR, chip->aspm_l0s_l1_en); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - - chip->aspm_level[0] = chip->aspm_l0s_l1_en; - if (CHK_SDIO_EXIST(chip)) { - chip->aspm_level[1] = chip->aspm_l0s_l1_en; - ret = rtsx_write_cfg_dw(chip, CHECK_PID(chip, 0x5288) ? 2 : 1, - 0xC0, 0xFF, chip->aspm_l0s_l1_en); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - } - - chip->aspm_enabled = 1; - - return STATUS_SUCCESS; -} - -static int rtsx_enable_pcie_intr(struct rtsx_chip *chip) -{ - int ret; - - if (!chip->asic_code || !CHECK_PID(chip, 0x5208)) { - rtsx_enable_bus_int(chip); - return STATUS_SUCCESS; - } - - if (chip->phy_debug_mode) { - ret = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0); - if (ret) - return ret; - rtsx_disable_bus_int(chip); - } else { - rtsx_enable_bus_int(chip); - } - - if (chip->ic_version >= IC_VER_D) { - u16 reg; - - ret = rtsx_read_phy_register(chip, 0x00, ®); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - - reg &= 0xFE7F; - reg |= 0x80; - ret = rtsx_write_phy_register(chip, 0x00, reg); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - - ret = rtsx_read_phy_register(chip, 0x1C, ®); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - - reg &= 0xFFF7; - ret = rtsx_write_phy_register(chip, 0x1C, reg); - if (ret != STATUS_SUCCESS) - return STATUS_FAIL; - } - - if (chip->driver_first_load && chip->ic_version < IC_VER_C) - rtsx_calibration(chip); - - return STATUS_SUCCESS; -} - -int rtsx_reset_chip(struct rtsx_chip *chip) -{ - int retval; - - rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr); - - rtsx_disable_aspm(chip); - - retval = rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 0x00); - if (retval) - return retval; - - /* Disable card clock */ - retval = rtsx_write_register(chip, CARD_CLK_EN, 0x1E, 0); - if (retval) - return retval; - -#ifdef SUPPORT_OCP - /* SSC power on, OCD power on */ - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) { - retval = rtsx_write_register(chip, FPDCTL, OC_POWER_DOWN, 0); - if (retval) - return retval; - } else { - retval = rtsx_write_register(chip, FPDCTL, OC_POWER_DOWN, - MS_OC_POWER_DOWN); - if (retval) - return retval; - } - - retval = rtsx_write_register(chip, OCPPARA1, OCP_TIME_MASK, - OCP_TIME_800); - if (retval) - return retval; - retval = rtsx_write_register(chip, OCPPARA2, OCP_THD_MASK, - OCP_THD_244_946); - if (retval) - return retval; - retval = rtsx_write_register(chip, OCPCTL, 0xFF, - CARD_OC_INT_EN | CARD_DETECT_EN); - if (retval) - return retval; -#else - /* OC power down */ - retval = rtsx_write_register(chip, FPDCTL, OC_POWER_DOWN, - OC_POWER_DOWN); - if (retval) - return retval; -#endif - - if (!CHECK_PID(chip, 0x5288)) { - retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0xFF, 0x03); - if (retval) - return retval; - } - - /* Turn off LED */ - retval = rtsx_write_register(chip, CARD_GPIO, 0xFF, 0x03); - if (retval) - return retval; - - /* Reset delink mode */ - retval = rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0); - if (retval) - return retval; - - /* Card driving select */ - retval = rtsx_write_register(chip, CARD_DRIVE_SEL, 0xFF, - chip->card_drive_sel); - if (retval) - return retval; - -#ifdef LED_AUTO_BLINK - retval = rtsx_write_register(chip, CARD_AUTO_BLINK, 0xFF, - LED_BLINK_SPEED | BLINK_EN | LED_GPIO0); - if (retval) - return retval; -#endif - - if (chip->asic_code) { - /* Enable SSC Clock */ - retval = rtsx_write_register(chip, SSC_CTL1, 0xFF, - SSC_8X_EN | SSC_SEL_4M); - if (retval) - return retval; - retval = rtsx_write_register(chip, SSC_CTL2, 0xFF, 0x12); - if (retval) - return retval; - } - - /* - * Disable cd_pwr_save (u_force_rst_core_en=0, u_cd_rst_core_en=0) - * 0xFE5B - * bit[1] u_cd_rst_core_en rst_value = 0 - * bit[2] u_force_rst_core_en rst_value = 0 - * bit[5] u_mac_phy_rst_n_dbg rst_value = 1 - * bit[4] u_non_sticky_rst_n_dbg rst_value = 0 - */ - retval = rtsx_write_register(chip, CHANGE_LINK_STATE, 0x16, 0x10); - if (retval) - return retval; - - /* Enable ASPM */ - if (chip->aspm_l0s_l1_en) { - retval = rtsx_reset_aspm(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - if (chip->asic_code && CHECK_PID(chip, 0x5208)) { - retval = rtsx_write_phy_register(chip, 0x07, 0x0129); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - retval = rtsx_write_config_byte(chip, LCTLR, - chip->aspm_l0s_l1_en); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = rtsx_write_config_byte(chip, 0x81, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_SDIO_EXIST(chip)) { - retval = rtsx_write_cfg_dw(chip, - CHECK_PID(chip, 0x5288) ? 2 : 1, - 0xC0, 0xFF00, 0x0100); - - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - if (CHECK_PID(chip, 0x5288) && !CHK_SDIO_EXIST(chip)) { - retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFFFF, 0x0103); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_cfg_dw(chip, 2, 0x84, 0xFF, 0x03); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT, - LINK_RDY_INT); - if (retval) - return retval; - - retval = rtsx_write_register(chip, PERST_GLITCH_WIDTH, 0xFF, 0x80); - if (retval) - return retval; - - retval = rtsx_enable_pcie_intr(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - chip->need_reset = 0; - - chip->int_reg = rtsx_readl(chip, RTSX_BIPR); - - if (chip->hw_bypass_sd) - goto nextcard; - dev_dbg(rtsx_dev(chip), "In %s, chip->int_reg = 0x%x\n", __func__, - chip->int_reg); - if (chip->int_reg & SD_EXIST) { -#ifdef HW_AUTO_SWITCH_SD_BUS - if (CHECK_PID(chip, 0x5208) && chip->ic_version < IC_VER_C) - retval = rtsx_pre_handle_sdio_old(chip); - else - retval = rtsx_pre_handle_sdio_new(chip); - - dev_dbg(rtsx_dev(chip), "chip->need_reset = 0x%x (%s)\n", - (unsigned int)(chip->need_reset), __func__); -#else /* HW_AUTO_SWITCH_SD_BUS */ - retval = rtsx_pre_handle_sdio_old(chip); -#endif /* HW_AUTO_SWITCH_SD_BUS */ - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - } else { - chip->sd_io = 0; - retval = rtsx_write_register(chip, SDIO_CTRL, - SDIO_BUS_CTRL | SDIO_CD_CTRL, 0); - if (retval) - return retval; - } - -nextcard: - if (chip->int_reg & XD_EXIST) - chip->need_reset |= XD_CARD; - if (chip->int_reg & MS_EXIST) - chip->need_reset |= MS_CARD; - if (chip->int_reg & CARD_EXIST) { - retval = rtsx_write_register(chip, SSC_CTL1, SSC_RSTB, - SSC_RSTB); - if (retval) - return retval; - } - - dev_dbg(rtsx_dev(chip), "In %s, chip->need_reset = 0x%x\n", __func__, - (unsigned int)(chip->need_reset)); - - retval = rtsx_write_register(chip, RCCTL, 0x01, 0x00); - if (retval) - return retval; - - if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) { - /* Turn off main power when entering S3/S4 state */ - retval = rtsx_write_register(chip, MAIN_PWR_OFF_CTL, 0x03, - 0x03); - if (retval) - return retval; - } - - if (chip->remote_wakeup_en && !chip->auto_delink_en) { - retval = rtsx_write_register(chip, WAKE_SEL_CTL, 0x07, 0x07); - if (retval) - return retval; - if (chip->aux_pwr_exist) { - retval = rtsx_write_register(chip, PME_FORCE_CTL, - 0xFF, 0x33); - if (retval) - return retval; - } - } else { - retval = rtsx_write_register(chip, WAKE_SEL_CTL, 0x07, 0x04); - if (retval) - return retval; - retval = rtsx_write_register(chip, PME_FORCE_CTL, 0xFF, 0x30); - if (retval) - return retval; - } - - if (CHECK_PID(chip, 0x5208) && chip->ic_version >= IC_VER_D) { - retval = rtsx_write_register(chip, PETXCFG, 0x1C, 0x14); - if (retval) - return retval; - } - - if (chip->asic_code && CHECK_PID(chip, 0x5208)) { - retval = rtsx_clr_phy_reg_bit(chip, 0x1C, 2); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - if (chip->ft2_fast_mode) { - retval = rtsx_write_register(chip, CARD_PWR_CTL, 0xFF, - MS_PARTIAL_POWER_ON | - SD_PARTIAL_POWER_ON); - if (retval) - return retval; - udelay(chip->pmos_pwr_on_interval); - retval = rtsx_write_register(chip, CARD_PWR_CTL, 0xFF, - MS_POWER_ON | SD_POWER_ON); - if (retval) - return retval; - - wait_timeout(200); - } - - /* Reset card */ - rtsx_reset_detected_cards(chip, 0); - - chip->driver_first_load = 0; - - return STATUS_SUCCESS; -} - -static inline int valid_sd_speed_prior(u32 sd_speed_prior) -{ - bool valid_para = true; - int i; - - for (i = 0; i < 4; i++) { - u8 tmp = (u8)(sd_speed_prior >> (i * 8)); - - if (tmp < 0x01 || tmp > 0x04) { - valid_para = false; - break; - } - } - - return valid_para; -} - -static inline int valid_sd_current_prior(u32 sd_current_prior) -{ - bool valid_para = true; - int i; - - for (i = 0; i < 4; i++) { - u8 tmp = (u8)(sd_current_prior >> (i * 8)); - - if (tmp > 0x03) { - valid_para = false; - break; - } - } - - return valid_para; -} - -static int rts5208_init(struct rtsx_chip *chip) -{ - int retval; - u16 reg = 0; - u8 val = 0; - - retval = rtsx_write_register(chip, CLK_SEL, 0x03, 0x03); - if (retval) - return retval; - retval = rtsx_read_register(chip, CLK_SEL, &val); - if (retval) - return retval; - chip->asic_code = val == 0 ? 1 : 0; - - if (chip->asic_code) { - retval = rtsx_read_phy_register(chip, 0x1C, ®); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "Value of phy register 0x1C is 0x%x\n", - reg); - chip->ic_version = (reg >> 4) & 0x07; - chip->phy_debug_mode = reg & PHY_DEBUG_MODE ? 1 : 0; - - } else { - retval = rtsx_read_register(chip, 0xFE80, &val); - if (retval) - return retval; - chip->ic_version = val; - chip->phy_debug_mode = 0; - } - - retval = rtsx_read_register(chip, PDINFO, &val); - if (retval) - return retval; - dev_dbg(rtsx_dev(chip), "PDINFO: 0x%x\n", val); - chip->aux_pwr_exist = val & AUX_PWR_DETECTED ? 1 : 0; - - retval = rtsx_read_register(chip, 0xFE50, &val); - if (retval) - return retval; - chip->hw_bypass_sd = val & 0x01 ? 1 : 0; - - rtsx_read_config_byte(chip, 0x0E, &val); - if (val & 0x80) - SET_SDIO_EXIST(chip); - else - CLR_SDIO_EXIST(chip); - - if (chip->use_hw_setting) { - retval = rtsx_read_register(chip, CHANGE_LINK_STATE, &val); - if (retval) - return retval; - chip->auto_delink_en = val & 0x80 ? 1 : 0; - } - - return STATUS_SUCCESS; -} - -static int rts5288_init(struct rtsx_chip *chip) -{ - int retval; - u8 val = 0, max_func; - u32 lval = 0; - - retval = rtsx_write_register(chip, CLK_SEL, 0x03, 0x03); - if (retval) - return retval; - retval = rtsx_read_register(chip, CLK_SEL, &val); - if (retval) - return retval; - chip->asic_code = val == 0 ? 1 : 0; - - chip->ic_version = 0; - chip->phy_debug_mode = 0; - - retval = rtsx_read_register(chip, PDINFO, &val); - if (retval) - return retval; - dev_dbg(rtsx_dev(chip), "PDINFO: 0x%x\n", val); - chip->aux_pwr_exist = val & AUX_PWR_DETECTED ? 1 : 0; - - retval = rtsx_read_register(chip, CARD_SHARE_MODE, &val); - if (retval) - return retval; - dev_dbg(rtsx_dev(chip), "CARD_SHARE_MODE: 0x%x\n", val); - chip->baro_pkg = val & 0x04 ? QFN : LQFP; - - retval = rtsx_read_register(chip, 0xFE5A, &val); - if (retval) - return retval; - chip->hw_bypass_sd = val & 0x10 ? 1 : 0; - - retval = rtsx_read_cfg_dw(chip, 0, 0x718, &lval); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - max_func = (u8)((lval >> 29) & 0x07); - dev_dbg(rtsx_dev(chip), "Max function number: %d\n", max_func); - if (max_func == 0x02) - SET_SDIO_EXIST(chip); - else - CLR_SDIO_EXIST(chip); - - if (chip->use_hw_setting) { - retval = rtsx_read_register(chip, CHANGE_LINK_STATE, &val); - if (retval) - return retval; - chip->auto_delink_en = val & 0x80 ? 1 : 0; - - if (CHECK_BARO_PKG(chip, LQFP)) - chip->lun_mode = SD_MS_1LUN; - else - chip->lun_mode = DEFAULT_SINGLE; - } - - return STATUS_SUCCESS; -} - -int rtsx_init_chip(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - struct xd_info *xd_card = &chip->xd_card; - struct ms_info *ms_card = &chip->ms_card; - int retval; - unsigned int i; - - dev_dbg(rtsx_dev(chip), "Vendor ID: 0x%04x, Product ID: 0x%04x\n", - chip->vendor_id, chip->product_id); - - chip->ic_version = 0; - - memset(xd_card, 0, sizeof(struct xd_info)); - memset(sd_card, 0, sizeof(struct sd_info)); - memset(ms_card, 0, sizeof(struct ms_info)); - - chip->xd_reset_counter = 0; - chip->sd_reset_counter = 0; - chip->ms_reset_counter = 0; - - chip->xd_show_cnt = MAX_SHOW_CNT; - chip->sd_show_cnt = MAX_SHOW_CNT; - chip->ms_show_cnt = MAX_SHOW_CNT; - - chip->sd_io = 0; - chip->auto_delink_cnt = 0; - chip->auto_delink_allowed = 1; - rtsx_set_stat(chip, RTSX_STAT_INIT); - - chip->aspm_enabled = 0; - chip->chip_insert_with_sdio = 0; - chip->sdio_aspm = 0; - chip->sdio_idle = 0; - chip->sdio_counter = 0; - chip->cur_card = 0; - chip->phy_debug_mode = 0; - chip->sdio_func_exist = 0; - memset(chip->sdio_raw_data, 0, 12); - - for (i = 0; i < MAX_ALLOWED_LUN_CNT; i++) { - set_sense_type(chip, i, SENSE_TYPE_NO_SENSE); - chip->rw_fail_cnt[i] = 0; - } - - if (!valid_sd_speed_prior(chip->sd_speed_prior)) - chip->sd_speed_prior = 0x01040203; - - dev_dbg(rtsx_dev(chip), "sd_speed_prior = 0x%08x\n", - chip->sd_speed_prior); - - if (!valid_sd_current_prior(chip->sd_current_prior)) - chip->sd_current_prior = 0x00010203; - - dev_dbg(rtsx_dev(chip), "sd_current_prior = 0x%08x\n", - chip->sd_current_prior); - - if (chip->sd_ddr_tx_phase > 31 || chip->sd_ddr_tx_phase < 0) - chip->sd_ddr_tx_phase = 0; - - if (chip->mmc_ddr_tx_phase > 31 || chip->mmc_ddr_tx_phase < 0) - chip->mmc_ddr_tx_phase = 0; - - retval = rtsx_write_register(chip, FPDCTL, SSC_POWER_DOWN, 0); - if (retval) - return retval; - wait_timeout(200); - retval = rtsx_write_register(chip, CLK_DIV, 0x07, 0x07); - if (retval) - return retval; - dev_dbg(rtsx_dev(chip), "chip->use_hw_setting = %d\n", - chip->use_hw_setting); - - if (CHECK_PID(chip, 0x5208)) { - retval = rts5208_init(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - } else if (CHECK_PID(chip, 0x5288)) { - retval = rts5288_init(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - if (chip->ss_en == 2) - chip->ss_en = 0; - - dev_dbg(rtsx_dev(chip), "chip->asic_code = %d\n", chip->asic_code); - dev_dbg(rtsx_dev(chip), "chip->ic_version = 0x%x\n", chip->ic_version); - dev_dbg(rtsx_dev(chip), "chip->phy_debug_mode = %d\n", - chip->phy_debug_mode); - dev_dbg(rtsx_dev(chip), "chip->aux_pwr_exist = %d\n", - chip->aux_pwr_exist); - dev_dbg(rtsx_dev(chip), "chip->sdio_func_exist = %d\n", - chip->sdio_func_exist); - dev_dbg(rtsx_dev(chip), "chip->hw_bypass_sd = %d\n", - chip->hw_bypass_sd); - dev_dbg(rtsx_dev(chip), "chip->aspm_l0s_l1_en = %d\n", - chip->aspm_l0s_l1_en); - dev_dbg(rtsx_dev(chip), "chip->lun_mode = %d\n", chip->lun_mode); - dev_dbg(rtsx_dev(chip), "chip->auto_delink_en = %d\n", - chip->auto_delink_en); - dev_dbg(rtsx_dev(chip), "chip->ss_en = %d\n", chip->ss_en); - dev_dbg(rtsx_dev(chip), "chip->baro_pkg = %d\n", chip->baro_pkg); - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) { - chip->card2lun[SD_CARD] = 0; - chip->card2lun[MS_CARD] = 1; - chip->card2lun[XD_CARD] = 0xFF; - chip->lun2card[0] = SD_CARD; - chip->lun2card[1] = MS_CARD; - chip->max_lun = 1; - SET_SDIO_IGNORED(chip); - } else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) { - chip->card2lun[SD_CARD] = 0; - chip->card2lun[MS_CARD] = 0; - chip->card2lun[XD_CARD] = 0xFF; - chip->lun2card[0] = SD_CARD | MS_CARD; - chip->max_lun = 0; - } else { - chip->card2lun[XD_CARD] = 0; - chip->card2lun[SD_CARD] = 0; - chip->card2lun[MS_CARD] = 0; - chip->lun2card[0] = XD_CARD | SD_CARD | MS_CARD; - chip->max_lun = 0; - } - - retval = rtsx_reset_chip(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -void rtsx_release_chip(struct rtsx_chip *chip) -{ - xd_free_l2p_tbl(chip); - ms_free_l2p_tbl(chip); - chip->card_exist = 0; - chip->card_ready = 0; -} - -#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK) -static inline void rtsx_blink_led(struct rtsx_chip *chip) -{ - if (chip->card_exist && chip->blink_led) { - if (chip->led_toggle_counter < LED_TOGGLE_INTERVAL) { - chip->led_toggle_counter++; - } else { - chip->led_toggle_counter = 0; - toggle_gpio(chip, LED_GPIO); - } - } -} -#endif - -static void rtsx_monitor_aspm_config(struct rtsx_chip *chip) -{ - bool reg_changed, maybe_support_aspm; - u32 tmp = 0; - u8 reg0 = 0, reg1 = 0; - - maybe_support_aspm = false; - reg_changed = false; - rtsx_read_config_byte(chip, LCTLR, ®0); - if (chip->aspm_level[0] != reg0) { - reg_changed = true; - chip->aspm_level[0] = reg0; - } - if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) { - rtsx_read_cfg_dw(chip, 1, 0xC0, &tmp); - reg1 = (u8)tmp; - if (chip->aspm_level[1] != reg1) { - reg_changed = true; - chip->aspm_level[1] = reg1; - } - - if ((reg0 & 0x03) && (reg1 & 0x03)) - maybe_support_aspm = true; - - } else { - if (reg0 & 0x03) - maybe_support_aspm = true; - } - - if (reg_changed) { - if (maybe_support_aspm) - chip->aspm_l0s_l1_en = 0x03; - - dev_dbg(rtsx_dev(chip), - "aspm_level[0] = 0x%02x, aspm_level[1] = 0x%02x\n", - chip->aspm_level[0], chip->aspm_level[1]); - - if (chip->aspm_l0s_l1_en) { - chip->aspm_enabled = 1; - } else { - chip->aspm_enabled = 0; - chip->sdio_aspm = 0; - } - rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFF, - 0x30 | chip->aspm_level[0] | - (chip->aspm_level[1] << 2)); - } -} - -static void rtsx_manage_ocp(struct rtsx_chip *chip) -{ -#ifdef SUPPORT_OCP - if (!chip->ocp_int) - return; - - rtsx_read_register(chip, OCPSTAT, &chip->ocp_stat); - - if (chip->card_exist & SD_CARD) - sd_power_off_card3v3(chip); - else if (chip->card_exist & MS_CARD) - ms_power_off_card3v3(chip); - else if (chip->card_exist & XD_CARD) - xd_power_off_card3v3(chip); - - chip->ocp_int = 0; -#endif -} - -static void rtsx_manage_sd_lock(struct rtsx_chip *chip) -{ -#ifdef SUPPORT_SD_LOCK - struct sd_info *sd_card = &chip->sd_card; - u8 val; - - if (!sd_card->sd_erase_status) - return; - - if (chip->card_exist & SD_CARD) { - rtsx_read_register(chip, 0xFD30, &val); - if (val & 0x02) { - sd_card->sd_erase_status = SD_NOT_ERASE; - sd_card->sd_lock_notify = 1; - chip->need_reinit |= SD_CARD; - } - } else { - sd_card->sd_erase_status = SD_NOT_ERASE; - } -#endif -} - -static bool rtsx_is_ss_allowed(struct rtsx_chip *chip) -{ - u32 val; - - if (!chip->ss_en || CHECK_PID(chip, 0x5288)) - return false; - - if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) { - rtsx_read_cfg_dw(chip, 1, 0x04, &val); - if (val & 0x07) - return false; - } - - return true; -} - -static void rtsx_manage_ss(struct rtsx_chip *chip) -{ - if (!rtsx_is_ss_allowed(chip) || chip->sd_io) - return; - - if (rtsx_get_stat(chip) != RTSX_STAT_IDLE) { - chip->ss_counter = 0; - return; - } - - if (chip->ss_counter < (chip->ss_idle_period / POLLING_INTERVAL)) - chip->ss_counter++; - else - rtsx_exclusive_enter_ss(chip); -} - -static void rtsx_manage_aspm(struct rtsx_chip *chip) -{ - u8 data; - - if (!CHECK_PID(chip, 0x5208)) - return; - - rtsx_monitor_aspm_config(chip); - -#ifdef SUPPORT_SDIO_ASPM - if (!CHK_SDIO_EXIST(chip) || CHK_SDIO_IGNORED(chip) || - !chip->aspm_l0s_l1_en || !chip->dynamic_aspm) - return; - - if (chip->sd_io) { - dynamic_configure_sdio_aspm(chip); - return; - } - - if (chip->sdio_aspm) - return; - - dev_dbg(rtsx_dev(chip), "SDIO enter ASPM!\n"); - data = 0x30 | (chip->aspm_level[1] << 2); - rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC, data); - chip->sdio_aspm = 1; -#endif -} - -static void rtsx_manage_idle(struct rtsx_chip *chip) -{ - if (chip->idle_counter < IDLE_MAX_COUNT) { - chip->idle_counter++; - return; - } - - if (rtsx_get_stat(chip) == RTSX_STAT_IDLE) - return; - - dev_dbg(rtsx_dev(chip), "Idle state!\n"); - rtsx_set_stat(chip, RTSX_STAT_IDLE); - -#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK) - chip->led_toggle_counter = 0; -#endif - rtsx_force_power_on(chip, SSC_PDCTL); - - turn_off_led(chip, LED_GPIO); - - if (chip->auto_power_down && !chip->card_ready && !chip->sd_io) - rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL); -} - -static void rtsx_manage_2lun_mode(struct rtsx_chip *chip) -{ -#ifdef SUPPORT_OCP - u8 sd_oc, ms_oc; - - sd_oc = chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER); - ms_oc = chip->ocp_stat & (MS_OC_NOW | MS_OC_EVER); - - if (sd_oc || ms_oc) - dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n", - chip->ocp_stat); - - if (sd_oc && (chip->card_exist & SD_CARD)) { - rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0); - card_power_off(chip, SD_CARD); - chip->card_fail |= SD_CARD; - } - - if (ms_oc && (chip->card_exist & MS_CARD)) { - rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0); - card_power_off(chip, MS_CARD); - chip->card_fail |= MS_CARD; - } -#endif -} - -static void rtsx_manage_1lun_mode(struct rtsx_chip *chip) -{ -#ifdef SUPPORT_OCP - if (!(chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER))) - return; - - dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n", - chip->ocp_stat); - - if (chip->card_exist & SD_CARD) { - rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0); - chip->card_fail |= SD_CARD; - } else if (chip->card_exist & MS_CARD) { - rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0); - chip->card_fail |= MS_CARD; - } else if (chip->card_exist & XD_CARD) { - rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN, 0); - chip->card_fail |= XD_CARD; - } - card_power_off(chip, SD_CARD); -#endif -} - -static void rtsx_delink_stage1(struct rtsx_chip *chip, int enter_L1, - int stage3_cnt) -{ - u8 val; - - rtsx_set_stat(chip, RTSX_STAT_DELINK); - - if (chip->asic_code && CHECK_PID(chip, 0x5208)) - rtsx_set_phy_reg_bit(chip, 0x1C, 2); - - if (chip->card_exist) - dev_dbg(rtsx_dev(chip), "False card inserted, do force delink\n"); - else - dev_dbg(rtsx_dev(chip), "No card inserted, do delink\n"); - - if (enter_L1) - rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 1); - - if (chip->card_exist) - val = 0x02; - else - val = 0x0A; - - rtsx_write_register(chip, CHANGE_LINK_STATE, val, val); - - if (enter_L1) - rtsx_enter_L1(chip); - - if (chip->card_exist) - chip->auto_delink_cnt = stage3_cnt + 1; -} - -static void rtsx_delink_stage(struct rtsx_chip *chip) -{ - int delink_stage1_cnt, delink_stage2_cnt, delink_stage3_cnt; - int enter_L1; - - if (!chip->auto_delink_en || !chip->auto_delink_allowed || - chip->card_ready || chip->card_ejected || chip->sd_io) { - chip->auto_delink_cnt = 0; - return; - } - - enter_L1 = chip->auto_delink_in_L1 && - (chip->aspm_l0s_l1_en || chip->ss_en); - - delink_stage1_cnt = chip->delink_stage1_step; - delink_stage2_cnt = delink_stage1_cnt + chip->delink_stage2_step; - delink_stage3_cnt = delink_stage2_cnt + chip->delink_stage3_step; - - if (chip->auto_delink_cnt > delink_stage3_cnt) - return; - - if (chip->auto_delink_cnt == delink_stage1_cnt) - rtsx_delink_stage1(chip, enter_L1, delink_stage3_cnt); - - if (chip->auto_delink_cnt == delink_stage2_cnt) { - dev_dbg(rtsx_dev(chip), "Try to do force delink\n"); - - if (enter_L1) - rtsx_exit_L1(chip); - - if (chip->asic_code && CHECK_PID(chip, 0x5208)) - rtsx_set_phy_reg_bit(chip, 0x1C, 2); - - rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0x0A); - } - - chip->auto_delink_cnt++; -} - -void rtsx_polling_func(struct rtsx_chip *chip) -{ - if (rtsx_chk_stat(chip, RTSX_STAT_SUSPEND)) - return; - - if (rtsx_chk_stat(chip, RTSX_STAT_DELINK)) - goto delink_stage; - - if (chip->polling_config) { - u8 val; - - rtsx_read_config_byte(chip, 0, &val); - } - - if (rtsx_chk_stat(chip, RTSX_STAT_SS)) - return; - - rtsx_manage_ocp(chip); - - rtsx_manage_sd_lock(chip); - - rtsx_init_cards(chip); - - rtsx_manage_ss(chip); - - rtsx_manage_aspm(chip); - - rtsx_manage_idle(chip); - - switch (rtsx_get_stat(chip)) { - case RTSX_STAT_RUN: -#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK) - rtsx_blink_led(chip); -#endif - do_remaining_work(chip); - break; - - case RTSX_STAT_IDLE: - if (chip->sd_io && !chip->sd_int) - try_to_switch_sdio_ctrl(chip); - - rtsx_enable_aspm(chip); - break; - - default: - break; - } - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) - rtsx_manage_2lun_mode(chip); - else - rtsx_manage_1lun_mode(chip); - -delink_stage: - rtsx_delink_stage(chip); -} - -/** - * rtsx_stop_cmd - stop command transfer and DMA transfer - * @chip: Realtek's card reader chip - * @card: flash card type - * - * Stop command transfer and DMA transfer. - * This function is called in error handler. - */ -void rtsx_stop_cmd(struct rtsx_chip *chip, int card) -{ - int i; - - for (i = 0; i <= 8; i++) { - int addr = RTSX_HCBAR + i * 4; - u32 reg; - - reg = rtsx_readl(chip, addr); - dev_dbg(rtsx_dev(chip), "BAR (0x%02x): 0x%08x\n", addr, reg); - } - rtsx_writel(chip, RTSX_HCBCTLR, STOP_CMD); - rtsx_writel(chip, RTSX_HDBCTLR, STOP_DMA); - - for (i = 0; i < 16; i++) { - u16 addr = 0xFE20 + (u16)i; - u8 val; - - rtsx_read_register(chip, addr, &val); - dev_dbg(rtsx_dev(chip), "0x%04X: 0x%02x\n", addr, val); - } - - rtsx_write_register(chip, DMACTL, 0x80, 0x80); - rtsx_write_register(chip, RBCTL, 0x80, 0x80); -} - -#define MAX_RW_REG_CNT 1024 - -int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data) -{ - int i; - u32 val = 3 << 30; - - val |= (u32)(addr & 0x3FFF) << 16; - val |= (u32)mask << 8; - val |= (u32)data; - - rtsx_writel(chip, RTSX_HAIMR, val); - - for (i = 0; i < MAX_RW_REG_CNT; i++) { - val = rtsx_readl(chip, RTSX_HAIMR); - if ((val & BIT(31)) == 0) { - if (data != (u8)val) - return STATUS_FAIL; - - return STATUS_SUCCESS; - } - } - - return STATUS_TIMEDOUT; -} - -int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data) -{ - u32 val = 2 << 30; - int i; - - if (data) - *data = 0; - - val |= (u32)(addr & 0x3FFF) << 16; - - rtsx_writel(chip, RTSX_HAIMR, val); - - for (i = 0; i < MAX_RW_REG_CNT; i++) { - val = rtsx_readl(chip, RTSX_HAIMR); - if ((val & BIT(31)) == 0) - break; - } - - if (i >= MAX_RW_REG_CNT) - return STATUS_TIMEDOUT; - - if (data) - *data = (u8)(val & 0xFF); - - return STATUS_SUCCESS; -} - -int rtsx_write_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 mask, - u32 val) -{ - int retval; - u8 mode = 0, tmp; - int i; - - for (i = 0; i < 4; i++) { - if (mask & 0xFF) { - retval = rtsx_write_register(chip, CFGDATA0 + i, - 0xFF, - (u8)(val & mask & 0xFF)); - if (retval) - return retval; - mode |= (1 << i); - } - mask >>= 8; - val >>= 8; - } - - if (mode) { - retval = rtsx_write_register(chip, CFGADDR0, 0xFF, (u8)addr); - if (retval) - return retval; - retval = rtsx_write_register(chip, CFGADDR1, 0xFF, - (u8)(addr >> 8)); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CFGRWCTL, 0xFF, - 0x80 | mode | - ((func_no & 0x03) << 4)); - if (retval) - return retval; - - for (i = 0; i < MAX_RW_REG_CNT; i++) { - retval = rtsx_read_register(chip, CFGRWCTL, &tmp); - if (retval) - return retval; - if ((tmp & 0x80) == 0) - break; - } - } - - return STATUS_SUCCESS; -} - -int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val) -{ - int retval; - int i; - u8 tmp; - u32 data = 0; - - retval = rtsx_write_register(chip, CFGADDR0, 0xFF, (u8)addr); - if (retval) - return retval; - retval = rtsx_write_register(chip, CFGADDR1, 0xFF, (u8)(addr >> 8)); - if (retval) - return retval; - retval = rtsx_write_register(chip, CFGRWCTL, 0xFF, - 0x80 | ((func_no & 0x03) << 4)); - if (retval) - return retval; - - for (i = 0; i < MAX_RW_REG_CNT; i++) { - retval = rtsx_read_register(chip, CFGRWCTL, &tmp); - if (retval) - return retval; - if ((tmp & 0x80) == 0) - break; - } - - for (i = 0; i < 4; i++) { - retval = rtsx_read_register(chip, CFGDATA0 + i, &tmp); - if (retval) - return retval; - data |= (u32)tmp << (i * 8); - } - - if (val) - *val = data; - - return STATUS_SUCCESS; -} - -int rtsx_write_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf, - int len) -{ - u32 *data, *mask; - u16 offset = addr % 4; - u16 aligned_addr = addr - offset; - int dw_len, i, j; - int retval; - size_t size; - - if (!buf) - return STATUS_NOMEM; - - if ((len + offset) % 4) - dw_len = (len + offset) / 4 + 1; - else - dw_len = (len + offset) / 4; - - dev_dbg(rtsx_dev(chip), "dw_len = %d\n", dw_len); - - size = array_size(dw_len, 4); - data = vzalloc(size); - if (!data) - return STATUS_NOMEM; - - mask = vzalloc(size); - if (!mask) { - vfree(data); - return STATUS_NOMEM; - } - - j = 0; - for (i = 0; i < len; i++) { - mask[j] |= 0xFF << (offset * 8); - data[j] |= buf[i] << (offset * 8); - if (++offset == 4) { - j++; - offset = 0; - } - } - - print_hex_dump_bytes(KBUILD_MODNAME ": ", DUMP_PREFIX_NONE, mask, size); - print_hex_dump_bytes(KBUILD_MODNAME ": ", DUMP_PREFIX_NONE, data, size); - - for (i = 0; i < dw_len; i++) { - retval = rtsx_write_cfg_dw(chip, func, aligned_addr + i * 4, - mask[i], data[i]); - if (retval != STATUS_SUCCESS) { - vfree(data); - vfree(mask); - return STATUS_FAIL; - } - } - - vfree(data); - vfree(mask); - - return STATUS_SUCCESS; -} - -int rtsx_read_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf, - int len) -{ - u32 *data; - u16 offset = addr % 4; - u16 aligned_addr = addr - offset; - int dw_len, i, j; - int retval; - - if ((len + offset) % 4) - dw_len = (len + offset) / 4 + 1; - else - dw_len = (len + offset) / 4; - - dev_dbg(rtsx_dev(chip), "dw_len = %d\n", dw_len); - - data = vmalloc(array_size(dw_len, 4)); - if (!data) - return STATUS_NOMEM; - - for (i = 0; i < dw_len; i++) { - retval = rtsx_read_cfg_dw(chip, func, aligned_addr + i * 4, - data + i); - if (retval != STATUS_SUCCESS) { - vfree(data); - return STATUS_FAIL; - } - } - - if (buf) { - j = 0; - - for (i = 0; i < len; i++) { - buf[i] = (u8)(data[j] >> (offset * 8)); - if (++offset == 4) { - j++; - offset = 0; - } - } - } - - vfree(data); - - return STATUS_SUCCESS; -} - -int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val) -{ - int retval; - bool finished = false; - int i; - u8 tmp; - - retval = rtsx_write_register(chip, PHYDATA0, 0xFF, (u8)val); - if (retval) - return retval; - retval = rtsx_write_register(chip, PHYDATA1, 0xFF, (u8)(val >> 8)); - if (retval) - return retval; - retval = rtsx_write_register(chip, PHYADDR, 0xFF, addr); - if (retval) - return retval; - retval = rtsx_write_register(chip, PHYRWCTL, 0xFF, 0x81); - if (retval) - return retval; - - for (i = 0; i < 100000; i++) { - retval = rtsx_read_register(chip, PHYRWCTL, &tmp); - if (retval) - return retval; - if (!(tmp & 0x80)) { - finished = true; - break; - } - } - - if (!finished) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val) -{ - int retval; - bool finished = false; - int i; - u16 data = 0; - u8 tmp; - - retval = rtsx_write_register(chip, PHYADDR, 0xFF, addr); - if (retval) - return retval; - retval = rtsx_write_register(chip, PHYRWCTL, 0xFF, 0x80); - if (retval) - return retval; - - for (i = 0; i < 100000; i++) { - retval = rtsx_read_register(chip, PHYRWCTL, &tmp); - if (retval) - return retval; - if (!(tmp & 0x80)) { - finished = true; - break; - } - } - - if (!finished) - return STATUS_FAIL; - - retval = rtsx_read_register(chip, PHYDATA0, &tmp); - if (retval) - return retval; - data = tmp; - retval = rtsx_read_register(chip, PHYDATA1, &tmp); - if (retval) - return retval; - data |= (u16)tmp << 8; - - if (val) - *val = data; - - return STATUS_SUCCESS; -} - -int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val) -{ - int retval; - int i; - u8 data = 0; - - retval = rtsx_write_register(chip, EFUSE_CTRL, 0xFF, 0x80 | addr); - if (retval) - return retval; - - for (i = 0; i < 100; i++) { - retval = rtsx_read_register(chip, EFUSE_CTRL, &data); - if (retval) - return retval; - if (!(data & 0x80)) - break; - udelay(1); - } - - if (data & 0x80) - return STATUS_TIMEDOUT; - - retval = rtsx_read_register(chip, EFUSE_DATA, &data); - if (retval) - return retval; - if (val) - *val = data; - - return STATUS_SUCCESS; -} - -int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val) -{ - int retval; - int i, j; - u8 data = 0, tmp = 0xFF; - - for (i = 0; i < 8; i++) { - if (val & (u8)(1 << i)) - continue; - - tmp &= (~(u8)(1 << i)); - dev_dbg(rtsx_dev(chip), "Write 0x%x to 0x%x\n", tmp, addr); - - retval = rtsx_write_register(chip, EFUSE_DATA, 0xFF, tmp); - if (retval) - return retval; - retval = rtsx_write_register(chip, EFUSE_CTRL, 0xFF, - 0xA0 | addr); - if (retval) - return retval; - - for (j = 0; j < 100; j++) { - retval = rtsx_read_register(chip, EFUSE_CTRL, &data); - if (retval) - return retval; - if (!(data & 0x80)) - break; - wait_timeout(3); - } - - if (data & 0x80) - return STATUS_TIMEDOUT; - - wait_timeout(5); - } - - return STATUS_SUCCESS; -} - -int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit) -{ - int retval; - u16 value; - - retval = rtsx_read_phy_register(chip, reg, &value); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (value & (1 << bit)) { - value &= ~(1 << bit); - retval = rtsx_write_phy_register(chip, reg, value); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit) -{ - int retval; - u16 value; - - retval = rtsx_read_phy_register(chip, reg, &value); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if ((value & (1 << bit)) == 0) { - value |= (1 << bit); - retval = rtsx_write_phy_register(chip, reg, value); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static void rtsx_handle_pm_dstate(struct rtsx_chip *chip, u8 dstate) -{ - u32 ultmp; - - dev_dbg(rtsx_dev(chip), "%04x set pm_dstate to %d\n", - chip->product_id, dstate); - - if (CHK_SDIO_EXIST(chip)) { - u8 func_no; - - if (CHECK_PID(chip, 0x5288)) - func_no = 2; - else - func_no = 1; - - rtsx_read_cfg_dw(chip, func_no, 0x84, &ultmp); - dev_dbg(rtsx_dev(chip), "pm_dstate of function %d: 0x%x\n", - (int)func_no, ultmp); - rtsx_write_cfg_dw(chip, func_no, 0x84, 0xFF, dstate); - } - - rtsx_write_config_byte(chip, 0x44, dstate); - rtsx_write_config_byte(chip, 0x45, 0); -} - -void rtsx_enter_L1(struct rtsx_chip *chip) -{ - rtsx_handle_pm_dstate(chip, 2); -} - -void rtsx_exit_L1(struct rtsx_chip *chip) -{ - rtsx_write_config_byte(chip, 0x44, 0); - rtsx_write_config_byte(chip, 0x45, 0); -} - -void rtsx_enter_ss(struct rtsx_chip *chip) -{ - dev_dbg(rtsx_dev(chip), "Enter Selective Suspend State!\n"); - - rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT, LINK_RDY_INT); - - if (chip->power_down_in_ss) { - rtsx_power_off_card(chip); - rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL); - } - - if (CHK_SDIO_EXIST(chip)) - rtsx_write_cfg_dw(chip, CHECK_PID(chip, 0x5288) ? 2 : 1, - 0xC0, 0xFF00, 0x0100); - - if (chip->auto_delink_en) { - rtsx_write_register(chip, HOST_SLEEP_STATE, 0x01, 0x01); - } else { - if (!chip->phy_debug_mode) { - u32 tmp; - - tmp = rtsx_readl(chip, RTSX_BIER); - tmp |= CARD_INT; - rtsx_writel(chip, RTSX_BIER, tmp); - } - - rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 0); - } - - rtsx_enter_L1(chip); - - RTSX_CLR_DELINK(chip); - rtsx_set_stat(chip, RTSX_STAT_SS); -} - -void rtsx_exit_ss(struct rtsx_chip *chip) -{ - dev_dbg(rtsx_dev(chip), "Exit Selective Suspend State!\n"); - - rtsx_exit_L1(chip); - - if (chip->power_down_in_ss) { - rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL); - udelay(1000); - } - - if (RTSX_TST_DELINK(chip)) { - chip->need_reinit = SD_CARD | MS_CARD | XD_CARD; - rtsx_reinit_cards(chip, 1); - RTSX_CLR_DELINK(chip); - } else if (chip->power_down_in_ss) { - chip->need_reinit = SD_CARD | MS_CARD | XD_CARD; - rtsx_reinit_cards(chip, 0); - } -} - -int rtsx_pre_handle_interrupt(struct rtsx_chip *chip) -{ - u32 status, int_enable; - bool exit_ss = false; -#ifdef SUPPORT_OCP - u32 ocp_int = 0; - - ocp_int = OC_INT; -#endif - - if (chip->ss_en) { - chip->ss_counter = 0; - if (rtsx_get_stat(chip) == RTSX_STAT_SS) { - exit_ss = true; - rtsx_exit_L1(chip); - rtsx_set_stat(chip, RTSX_STAT_RUN); - } - } - - int_enable = rtsx_readl(chip, RTSX_BIER); - chip->int_reg = rtsx_readl(chip, RTSX_BIPR); - - if (((chip->int_reg & int_enable) == 0) || - chip->int_reg == 0xFFFFFFFF) - return STATUS_FAIL; - - status = chip->int_reg &= (int_enable | 0x7FFFFF); - - if (status & CARD_INT) { - chip->auto_delink_cnt = 0; - - if (status & SD_INT) { - if (status & SD_EXIST) { - set_bit(SD_NR, &chip->need_reset); - } else { - set_bit(SD_NR, &chip->need_release); - chip->sd_reset_counter = 0; - chip->sd_show_cnt = 0; - clear_bit(SD_NR, &chip->need_reset); - } - } else { - /* - * If multi-luns, it's possible that - * when plugging/unplugging one card - * there is another card which still - * exists in the slot. In this case, - * all existed cards should be reset. - */ - if (exit_ss && (status & SD_EXIST)) - set_bit(SD_NR, &chip->need_reinit); - } - if (!CHECK_PID(chip, 0x5288) || CHECK_BARO_PKG(chip, QFN)) { - if (status & XD_INT) { - if (status & XD_EXIST) { - set_bit(XD_NR, &chip->need_reset); - } else { - set_bit(XD_NR, &chip->need_release); - chip->xd_reset_counter = 0; - chip->xd_show_cnt = 0; - clear_bit(XD_NR, &chip->need_reset); - } - } else { - if (exit_ss && (status & XD_EXIST)) - set_bit(XD_NR, &chip->need_reinit); - } - } - if (status & MS_INT) { - if (status & MS_EXIST) { - set_bit(MS_NR, &chip->need_reset); - } else { - set_bit(MS_NR, &chip->need_release); - chip->ms_reset_counter = 0; - chip->ms_show_cnt = 0; - clear_bit(MS_NR, &chip->need_reset); - } - } else { - if (exit_ss && (status & MS_EXIST)) - set_bit(MS_NR, &chip->need_reinit); - } - } - -#ifdef SUPPORT_OCP - chip->ocp_int = ocp_int & status; -#endif - - if (chip->sd_io && (chip->int_reg & DATA_DONE_INT)) - chip->int_reg &= ~(u32)DATA_DONE_INT; - - return STATUS_SUCCESS; -} - -void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat) -{ - int retval; - - dev_dbg(rtsx_dev(chip), "%s, pm_stat = %d\n", __func__, pm_stat); - - rtsx_set_stat(chip, RTSX_STAT_SUSPEND); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) - return; - - rtsx_release_cards(chip); - rtsx_disable_bus_int(chip); - turn_off_led(chip, LED_GPIO); - -#ifdef HW_AUTO_SWITCH_SD_BUS - if (chip->sd_io) { - chip->sdio_in_charge = 1; - if (CHECK_PID(chip, 0x5208)) { - rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08); - /* Enable sdio_bus_auto_switch */ - rtsx_write_register(chip, 0xFE70, 0x80, 0x80); - } else if (CHECK_PID(chip, 0x5288)) { - rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08); - /* Enable sdio_bus_auto_switch */ - rtsx_write_register(chip, 0xFE5A, 0x08, 0x08); - } - } -#endif - - if (CHECK_PID(chip, 0x5208) && chip->ic_version >= IC_VER_D) { - /* u_force_clkreq_0 */ - rtsx_write_register(chip, PETXCFG, 0x08, 0x08); - } - - if (pm_stat == PM_S1) { - dev_dbg(rtsx_dev(chip), "Host enter S1\n"); - rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, - HOST_ENTER_S1); - } else if (pm_stat == PM_S3) { - if (chip->s3_pwr_off_delay > 0) - wait_timeout(chip->s3_pwr_off_delay); - - dev_dbg(rtsx_dev(chip), "Host enter S3\n"); - rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, - HOST_ENTER_S3); - } - - if (chip->do_delink_before_power_down && chip->auto_delink_en) - rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 2); - - rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL); - - chip->cur_clk = 0; - chip->cur_card = 0; - chip->card_exist = 0; -} - -void rtsx_enable_aspm(struct rtsx_chip *chip) -{ - if (chip->aspm_l0s_l1_en && chip->dynamic_aspm && !chip->aspm_enabled) { - dev_dbg(rtsx_dev(chip), "Try to enable ASPM\n"); - chip->aspm_enabled = 1; - - if (chip->asic_code && CHECK_PID(chip, 0x5208)) - rtsx_write_phy_register(chip, 0x07, 0); - if (CHECK_PID(chip, 0x5208)) { - rtsx_write_register(chip, ASPM_FORCE_CTL, 0xF3, - 0x30 | chip->aspm_level[0]); - } else { - rtsx_write_config_byte(chip, LCTLR, - chip->aspm_l0s_l1_en); - } - - if (CHK_SDIO_EXIST(chip)) { - u16 val = chip->aspm_l0s_l1_en | 0x0100; - - rtsx_write_cfg_dw(chip, CHECK_PID(chip, 0x5288) ? 2 : 1, - 0xC0, 0xFFF, val); - } - } -} - -void rtsx_disable_aspm(struct rtsx_chip *chip) -{ - if (CHECK_PID(chip, 0x5208)) - rtsx_monitor_aspm_config(chip); - - if (chip->aspm_l0s_l1_en && chip->dynamic_aspm && chip->aspm_enabled) { - dev_dbg(rtsx_dev(chip), "Try to disable ASPM\n"); - chip->aspm_enabled = 0; - - if (chip->asic_code && CHECK_PID(chip, 0x5208)) - rtsx_write_phy_register(chip, 0x07, 0x0129); - if (CHECK_PID(chip, 0x5208)) - rtsx_write_register(chip, ASPM_FORCE_CTL, - 0xF3, 0x30); - else - rtsx_write_config_byte(chip, LCTLR, 0x00); - - wait_timeout(1); - } -} - -int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len) -{ - int retval; - int i, j; - u16 reg_addr; - u8 *ptr; - - if (!buf) - return STATUS_ERROR; - - ptr = buf; - reg_addr = PPBUF_BASE2; - for (i = 0; i < buf_len / 256; i++) { - rtsx_init_cmd(chip); - - for (j = 0; j < 256; j++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0); - - retval = rtsx_send_cmd(chip, 0, 250); - if (retval < 0) - return STATUS_FAIL; - - memcpy(ptr, rtsx_get_cmd_data(chip), 256); - ptr += 256; - } - - if (buf_len % 256) { - rtsx_init_cmd(chip); - - for (j = 0; j < buf_len % 256; j++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0); - - retval = rtsx_send_cmd(chip, 0, 250); - if (retval < 0) - return STATUS_FAIL; - } - - memcpy(ptr, rtsx_get_cmd_data(chip), buf_len % 256); - - return STATUS_SUCCESS; -} - -int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len) -{ - int retval; - int i, j; - u16 reg_addr; - u8 *ptr; - - if (!buf) - return STATUS_ERROR; - - ptr = buf; - reg_addr = PPBUF_BASE2; - for (i = 0; i < buf_len / 256; i++) { - rtsx_init_cmd(chip); - - for (j = 0; j < 256; j++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, reg_addr++, 0xFF, - *ptr); - ptr++; - } - - retval = rtsx_send_cmd(chip, 0, 250); - if (retval < 0) - return STATUS_FAIL; - } - - if (buf_len % 256) { - rtsx_init_cmd(chip); - - for (j = 0; j < buf_len % 256; j++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, reg_addr++, 0xFF, - *ptr); - ptr++; - } - - retval = rtsx_send_cmd(chip, 0, 250); - if (retval < 0) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int rtsx_check_chip_exist(struct rtsx_chip *chip) -{ - if (rtsx_readl(chip, 0) == 0xFFFFFFFF) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl) -{ - int retval; - u8 mask = 0; - - if (ctl & SSC_PDCTL) - mask |= SSC_POWER_DOWN; - -#ifdef SUPPORT_OCP - if (ctl & OC_PDCTL) { - mask |= SD_OC_POWER_DOWN; - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) - mask |= MS_OC_POWER_DOWN; - } -#endif - - if (mask) { - retval = rtsx_write_register(chip, FPDCTL, mask, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHECK_PID(chip, 0x5288)) - wait_timeout(200); - } - - return STATUS_SUCCESS; -} - -int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl) -{ - int retval; - u8 mask = 0, val = 0; - - if (ctl & SSC_PDCTL) - mask |= SSC_POWER_DOWN; - -#ifdef SUPPORT_OCP - if (ctl & OC_PDCTL) { - mask |= SD_OC_POWER_DOWN; - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) - mask |= MS_OC_POWER_DOWN; - } -#endif - - if (mask) { - val = mask; - retval = rtsx_write_register(chip, FPDCTL, mask, val); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} diff --git a/drivers/staging/rts5208/rtsx_chip.h b/drivers/staging/rts5208/rtsx_chip.h deleted file mode 100644 index bac65784d4a19..0000000000000 --- a/drivers/staging/rts5208/rtsx_chip.h +++ /dev/null @@ -1,987 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_CHIP_H -#define __REALTEK_RTSX_CHIP_H - -#include "rtsx.h" - -#define SUPPORT_CPRM -#define SUPPORT_OCP -#define SUPPORT_SDIO_ASPM -#define SUPPORT_MAGIC_GATE -#define SUPPORT_MSXC -#define SUPPORT_SD_LOCK -/* Hardware switch bus_ctl and cd_ctl automatically */ -#define HW_AUTO_SWITCH_SD_BUS -/* Enable hardware interrupt write clear */ -#define HW_INT_WRITE_CLR -/* #define LED_AUTO_BLINK */ -/* #define DISABLE_CARD_INT */ - -#ifdef SUPPORT_MAGIC_GATE - /* Using NORMAL_WRITE instead of AUTO_WRITE to set ICV */ - #define MG_SET_ICV_SLOW - /* HW may miss ERR/CMDNK signal when sampling INT status. */ - #define MS_SAMPLE_INT_ERR - /* - * HW DO NOT support Wait_INT function - * during READ_BYTES transfer mode - */ - #define READ_BYTES_WAIT_INT -#endif - -#ifdef SUPPORT_MSXC -#define XC_POWERCLASS -#define SUPPORT_PCGL_1P18 -#endif - -#ifndef LED_AUTO_BLINK -#define REGULAR_BLINK -#endif - -#define LED_BLINK_SPEED 5 -#define LED_TOGGLE_INTERVAL 6 -#define GPIO_TOGGLE_THRESHOLD 1024 -#define LED_GPIO 0 - -#define POLLING_INTERVAL 30 - -#define TRACE_ITEM_CNT 64 - -#ifndef STATUS_SUCCESS -#define STATUS_SUCCESS 0 -#endif -#ifndef STATUS_FAIL -#define STATUS_FAIL 1 -#endif -#ifndef STATUS_TIMEDOUT -#define STATUS_TIMEDOUT 2 -#endif -#ifndef STATUS_NOMEM -#define STATUS_NOMEM 3 -#endif -#ifndef STATUS_READ_FAIL -#define STATUS_READ_FAIL 4 -#endif -#ifndef STATUS_WRITE_FAIL -#define STATUS_WRITE_FAIL 5 -#endif -#ifndef STATUS_ERROR -#define STATUS_ERROR 10 -#endif - -#define PM_S1 1 -#define PM_S3 3 - -/* - * Transport return codes - */ - -#define TRANSPORT_GOOD 0 /* Transport good, command good */ -#define TRANSPORT_FAILED 1 /* Transport good, command failed */ -#define TRANSPORT_NO_SENSE 2 /* Command failed, no auto-sense */ -#define TRANSPORT_ERROR 3 /* Transport bad (i.e. device dead) */ - -/* - * Start-Stop-Unit - */ -#define STOP_MEDIUM 0x00 /* access disable */ -#define MAKE_MEDIUM_READY 0x01 /* access enable */ -#define UNLOAD_MEDIUM 0x02 /* unload */ -#define LOAD_MEDIUM 0x03 /* load */ - -/* - * STANDARD_INQUIRY - */ -#define QULIFIRE 0x00 -#define AENC_FNC 0x00 -#define TRML_IOP 0x00 -#define REL_ADR 0x00 -#define WBUS_32 0x00 -#define WBUS_16 0x00 -#define SYNC 0x00 -#define LINKED 0x00 -#define CMD_QUE 0x00 -#define SFT_RE 0x00 - -#define VEN_ID_LEN 8 /* Vendor ID Length */ -#define PRDCT_ID_LEN 16 /* Product ID Length */ -#define PRDCT_REV_LEN 4 /* Product LOT Length */ - -/* Dynamic flag definitions: used in set_bit() etc. */ -/* 0x00040000 transfer is active */ -#define RTSX_FLIDX_TRANS_ACTIVE 18 -/* 0x00100000 abort is in progress */ -#define RTSX_FLIDX_ABORTING 20 -/* 0x00200000 disconnect in progress */ -#define RTSX_FLIDX_DISCONNECTING 21 - -#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \ - (1UL << US_FLIDX_DISCONNECTING)) - -/* 0x00400000 device reset in progress */ -#define RTSX_FLIDX_RESETTING 22 -/* 0x00800000 SCSI midlayer timed out */ -#define RTSX_FLIDX_TIMED_OUT 23 -#define DRCT_ACCESS_DEV 0x00 /* Direct Access Device */ -#define RMB_DISC 0x80 /* The Device is Removable */ -#define ANSI_SCSI2 0x02 /* Based on ANSI-SCSI2 */ - -#define SCSI 0x00 /* Interface ID */ - -#define WRITE_PROTECTED_MEDIA 0x07 - -/*---- sense key ----*/ -#define ILI 0x20 /* ILI bit is on */ - -#define NO_SENSE 0x00 /* not exist sense key */ -#define RECOVER_ERR 0x01 /* Target/Logical unit is recoverd */ -#define NOT_READY 0x02 /* Logical unit is not ready */ -#define MEDIA_ERR 0x03 /* medium/data error */ -#define HARDWARE_ERR 0x04 /* hardware error */ -#define ILGAL_REQ 0x05 /* CDB/parameter/identify msg error */ -#define UNIT_ATTENTION 0x06 /* unit attention condition occur */ -#define DAT_PRTCT 0x07 /* read/write is desable */ -#define BLNC_CHK 0x08 /* find blank/DOF in read */ - /* write to unblank area */ -#define CPY_ABRT 0x0a /* Copy/Compare/Copy&Verify illegal */ -#define ABRT_CMD 0x0b /* Target make the command in error */ -#define EQUAL 0x0c /* Search Data end with Equal */ -#define VLM_OVRFLW 0x0d /* Some data are left in buffer */ -#define MISCMP 0x0e /* find inequality */ - -#define READ_ERR -1 -#define WRITE_ERR -2 - -#define FIRST_RESET 0x01 -#define USED_EXIST 0x02 - -/* - * SENSE_DATA - */ -/*---- valid ----*/ -#define SENSE_VALID 0x80 /* Sense data is valid as SCSI2 */ -#define SENSE_INVALID 0x00 /* Sense data is invalid as SCSI2 */ - -/*---- error code ----*/ -#define CUR_ERR 0x70 /* current error */ -#define DEF_ERR 0x71 /* specific command error */ - -/*---- sense key Information ----*/ -#define SNSKEYINFO_LEN 3 /* length of sense key information */ - -#define SKSV 0x80 -#define CDB_ILLEGAL 0x40 -#define DAT_ILLEGAL 0x00 -#define BPV 0x08 -#define BIT_ILLEGAL0 0 /* bit0 is illegal */ -#define BIT_ILLEGAL1 1 /* bit1 is illegal */ -#define BIT_ILLEGAL2 2 /* bit2 is illegal */ -#define BIT_ILLEGAL3 3 /* bit3 is illegal */ -#define BIT_ILLEGAL4 4 /* bit4 is illegal */ -#define BIT_ILLEGAL5 5 /* bit5 is illegal */ -#define BIT_ILLEGAL6 6 /* bit6 is illegal */ -#define BIT_ILLEGAL7 7 /* bit7 is illegal */ - -/*---- ASC ----*/ -#define ASC_NO_INFO 0x00 -#define ASC_MISCMP 0x1d -#define ASC_INVLD_CDB 0x24 -#define ASC_INVLD_PARA 0x26 -#define ASC_LU_NOT_READY 0x04 -#define ASC_WRITE_ERR 0x0c -#define ASC_READ_ERR 0x11 -#define ASC_LOAD_EJCT_ERR 0x53 -#define ASC_MEDIA_NOT_PRESENT 0x3A -#define ASC_MEDIA_CHANGED 0x28 -#define ASC_MEDIA_IN_PROCESS 0x04 -#define ASC_WRITE_PROTECT 0x27 -#define ASC_LUN_NOT_SUPPORTED 0x25 - -/*---- ASQC ----*/ -#define ASCQ_NO_INFO 0x00 -#define ASCQ_MEDIA_IN_PROCESS 0x01 -#define ASCQ_MISCMP 0x00 -#define ASCQ_INVLD_CDB 0x00 -#define ASCQ_INVLD_PARA 0x02 -#define ASCQ_LU_NOT_READY 0x02 -#define ASCQ_WRITE_ERR 0x02 -#define ASCQ_READ_ERR 0x00 -#define ASCQ_LOAD_EJCT_ERR 0x00 -#define ASCQ_WRITE_PROTECT 0x00 - -struct sense_data_t { - unsigned char err_code; /* error code */ - /* bit7 : valid */ - /* (1 : SCSI2) */ - /* (0 : Vendor * specific) */ - /* bit6-0 : error * code */ - /* (0x70 : current * error) */ - /* (0x71 : specific command error) */ - unsigned char seg_no; /* segment No. */ - unsigned char sense_key; /* byte5 : ILI */ - /* bit3-0 : sense key */ - unsigned char info[4]; /* information */ - unsigned char ad_sense_len; /* additional sense data length */ - unsigned char cmd_info[4]; /* command specific information */ - unsigned char asc; /* ASC */ - unsigned char ascq; /* ASCQ */ - unsigned char rfu; /* FRU */ - unsigned char sns_key_info[3];/* sense key specific information */ -}; - -/* PCI Operation Register Address */ -#define RTSX_HCBAR 0x00 -#define RTSX_HCBCTLR 0x04 -#define RTSX_HDBAR 0x08 -#define RTSX_HDBCTLR 0x0C -#define RTSX_HAIMR 0x10 -#define RTSX_BIPR 0x14 -#define RTSX_BIER 0x18 - -/* Host command buffer control register */ -#define STOP_CMD (0x01 << 28) - -/* Host data buffer control register */ -#define SDMA_MODE 0x00 -#define ADMA_MODE (0x02 << 26) -#define STOP_DMA (0x01 << 28) -#define TRIG_DMA (0x01 << 31) - -/* Bus interrupt pending register */ -#define CMD_DONE_INT BIT(31) -#define DATA_DONE_INT BIT(30) -#define TRANS_OK_INT BIT(29) -#define TRANS_FAIL_INT BIT(28) -#define XD_INT BIT(27) -#define MS_INT BIT(26) -#define SD_INT BIT(25) -#define GPIO0_INT BIT(24) -#define OC_INT BIT(23) -#define SD_WRITE_PROTECT BIT(19) -#define XD_EXIST BIT(18) -#define MS_EXIST BIT(17) -#define SD_EXIST BIT(16) -#define DELINK_INT GPIO0_INT -#define MS_OC_INT BIT(23) -#define SD_OC_INT BIT(22) - -#define CARD_INT (XD_INT | MS_INT | SD_INT) -#define NEED_COMPLETE_INT (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT) -#define RTSX_INT (CMD_DONE_INT | NEED_COMPLETE_INT | CARD_INT | \ - GPIO0_INT | OC_INT) - -#define CARD_EXIST (XD_EXIST | MS_EXIST | SD_EXIST) - -/* Bus interrupt enable register */ -#define CMD_DONE_INT_EN BIT(31) -#define DATA_DONE_INT_EN BIT(30) -#define TRANS_OK_INT_EN BIT(29) -#define TRANS_FAIL_INT_EN BIT(28) -#define XD_INT_EN BIT(27) -#define MS_INT_EN BIT(26) -#define SD_INT_EN BIT(25) -#define GPIO0_INT_EN BIT(24) -#define OC_INT_EN BIT(23) -#define DELINK_INT_EN GPIO0_INT_EN -#define MS_OC_INT_EN BIT(23) -#define SD_OC_INT_EN BIT(22) - -#define READ_REG_CMD 0 -#define WRITE_REG_CMD 1 -#define CHECK_REG_CMD 2 - -#define HOST_TO_DEVICE 0 -#define DEVICE_TO_HOST 1 - -#define RTSX_RESV_BUF_LEN 4096 -#define HOST_CMDS_BUF_LEN 1024 -#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN) - -#define SD_NR 2 -#define MS_NR 3 -#define XD_NR 4 -#define SPI_NR 7 -#define SD_CARD BIT(SD_NR) -#define MS_CARD BIT(MS_NR) -#define XD_CARD BIT(XD_NR) -#define SPI_CARD BIT(SPI_NR) - -#define MAX_ALLOWED_LUN_CNT 8 - -#define XD_FREE_TABLE_CNT 1200 -#define MS_FREE_TABLE_CNT 512 - -/* Bit Operation */ -#define SET_BIT(data, idx) ((data) |= 1 << (idx)) -#define CLR_BIT(data, idx) ((data) &= ~(1 << (idx))) -#define CHK_BIT(data, idx) ((data) & (1 << (idx))) - -/* SG descriptor */ -#define RTSX_SG_INT 0x04 -#define RTSX_SG_END 0x02 -#define RTSX_SG_VALID 0x01 - -#define RTSX_SG_NO_OP 0x00 -#define RTSX_SG_TRANS_DATA (0x02 << 4) -#define RTSX_SG_LINK_DESC (0x03 << 4) - -struct rtsx_chip; - -typedef int (*card_rw_func)(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 sec_addr, u16 sec_cnt); - -/* Supported Clock */ -enum card_clock {CLK_20 = 1, CLK_30, CLK_40, CLK_50, CLK_60, - CLK_80, CLK_100, CLK_120, CLK_150, CLK_200}; - -enum RTSX_STAT {RTSX_STAT_INIT, RTSX_STAT_IDLE, RTSX_STAT_RUN, RTSX_STAT_SS, - RTSX_STAT_DELINK, RTSX_STAT_SUSPEND, - RTSX_STAT_ABORT, RTSX_STAT_DISCONNECT}; -enum IC_VER {IC_VER_AB, IC_VER_C = 2, IC_VER_D = 3}; - -#define MAX_RESET_CNT 3 - -/* For MS Card */ -#define MAX_DEFECTIVE_BLOCK 10 - -struct zone_entry { - u16 *l2p_table; - u16 *free_table; - u16 defect_list[MAX_DEFECTIVE_BLOCK]; /* For MS card only */ - int set_index; - int get_index; - int unused_blk_cnt; - int disable_count; - /* To indicate whether the L2P table of this zone has been built. */ - int build_flag; -}; - -#define TYPE_SD 0x0000 -#define TYPE_MMC 0x0001 - -/* TYPE_SD */ -#define SD_HS 0x0100 -#define SD_SDR50 0x0200 -#define SD_DDR50 0x0400 -#define SD_SDR104 0x0800 -#define SD_HCXC 0x1000 - -/* TYPE_MMC */ -#define MMC_26M 0x0100 -#define MMC_52M 0x0200 -#define MMC_4BIT 0x0400 -#define MMC_8BIT 0x0800 -#define MMC_SECTOR_MODE 0x1000 -#define MMC_DDR52 0x2000 - -/* SD card */ -#define CHK_SD(sd_card) (((sd_card)->sd_type & 0xFF) == TYPE_SD) -#define CHK_SD_HS(sd_card) (CHK_SD(sd_card) && \ - ((sd_card)->sd_type & SD_HS)) -#define CHK_SD_SDR50(sd_card) (CHK_SD(sd_card) && \ - ((sd_card)->sd_type & SD_SDR50)) -#define CHK_SD_DDR50(sd_card) (CHK_SD(sd_card) && \ - ((sd_card)->sd_type & SD_DDR50)) -#define CHK_SD_SDR104(sd_card) (CHK_SD(sd_card) && \ - ((sd_card)->sd_type & SD_SDR104)) -#define CHK_SD_HCXC(sd_card) (CHK_SD(sd_card) && \ - ((sd_card)->sd_type & SD_HCXC)) -#define CHK_SD_HC(sd_card) (CHK_SD_HCXC(sd_card) && \ - ((sd_card)->capacity <= 0x4000000)) -#define CHK_SD_XC(sd_card) (CHK_SD_HCXC(sd_card) && \ - ((sd_card)->capacity > 0x4000000)) -#define CHK_SD30_SPEED(sd_card) (CHK_SD_SDR50(sd_card) || \ - CHK_SD_DDR50(sd_card) || \ - CHK_SD_SDR104(sd_card)) - -#define SET_SD(sd_card) ((sd_card)->sd_type = TYPE_SD) -#define SET_SD_HS(sd_card) ((sd_card)->sd_type |= SD_HS) -#define SET_SD_SDR50(sd_card) ((sd_card)->sd_type |= SD_SDR50) -#define SET_SD_DDR50(sd_card) ((sd_card)->sd_type |= SD_DDR50) -#define SET_SD_SDR104(sd_card) ((sd_card)->sd_type |= SD_SDR104) -#define SET_SD_HCXC(sd_card) ((sd_card)->sd_type |= SD_HCXC) - -#define CLR_SD_HS(sd_card) ((sd_card)->sd_type &= ~SD_HS) -#define CLR_SD_SDR50(sd_card) ((sd_card)->sd_type &= ~SD_SDR50) -#define CLR_SD_DDR50(sd_card) ((sd_card)->sd_type &= ~SD_DDR50) -#define CLR_SD_SDR104(sd_card) ((sd_card)->sd_type &= ~SD_SDR104) -#define CLR_SD_HCXC(sd_card) ((sd_card)->sd_type &= ~SD_HCXC) - -/* MMC card */ -#define CHK_MMC(sd_card) (((sd_card)->sd_type & 0xFF) == \ - TYPE_MMC) -#define CHK_MMC_26M(sd_card) (CHK_MMC(sd_card) && \ - ((sd_card)->sd_type & MMC_26M)) -#define CHK_MMC_52M(sd_card) (CHK_MMC(sd_card) && \ - ((sd_card)->sd_type & MMC_52M)) -#define CHK_MMC_4BIT(sd_card) (CHK_MMC(sd_card) && \ - ((sd_card)->sd_type & MMC_4BIT)) -#define CHK_MMC_8BIT(sd_card) (CHK_MMC(sd_card) && \ - ((sd_card)->sd_type & MMC_8BIT)) -#define CHK_MMC_SECTOR_MODE(sd_card) (CHK_MMC(sd_card) && \ - ((sd_card)->sd_type & MMC_SECTOR_MODE)) -#define CHK_MMC_DDR52(sd_card) (CHK_MMC(sd_card) && \ - ((sd_card)->sd_type & MMC_DDR52)) - -#define SET_MMC(sd_card) ((sd_card)->sd_type = TYPE_MMC) -#define SET_MMC_26M(sd_card) ((sd_card)->sd_type |= MMC_26M) -#define SET_MMC_52M(sd_card) ((sd_card)->sd_type |= MMC_52M) -#define SET_MMC_4BIT(sd_card) ((sd_card)->sd_type |= MMC_4BIT) -#define SET_MMC_8BIT(sd_card) ((sd_card)->sd_type |= MMC_8BIT) -#define SET_MMC_SECTOR_MODE(sd_card) ((sd_card)->sd_type |= MMC_SECTOR_MODE) -#define SET_MMC_DDR52(sd_card) ((sd_card)->sd_type |= MMC_DDR52) - -#define CLR_MMC_26M(sd_card) ((sd_card)->sd_type &= ~MMC_26M) -#define CLR_MMC_52M(sd_card) ((sd_card)->sd_type &= ~MMC_52M) -#define CLR_MMC_4BIT(sd_card) ((sd_card)->sd_type &= ~MMC_4BIT) -#define CLR_MMC_8BIT(sd_card) ((sd_card)->sd_type &= ~MMC_8BIT) -#define CLR_MMC_SECTOR_MODE(sd_card) ((sd_card)->sd_type &= ~MMC_SECTOR_MODE) -#define CLR_MMC_DDR52(sd_card) ((sd_card)->sd_type &= ~MMC_DDR52) - -#define CHK_MMC_HS(sd_card) (CHK_MMC_52M(sd_card) && \ - CHK_MMC_26M(sd_card)) -#define CLR_MMC_HS(sd_card) \ -do { \ - CLR_MMC_DDR52(sd_card); \ - CLR_MMC_52M(sd_card); \ - CLR_MMC_26M(sd_card); \ -} while (0) - -#define SD_SUPPORT_CLASS_TEN 0x01 -#define SD_SUPPORT_1V8 0x02 - -#define SD_SET_CLASS_TEN(sd_card) ((sd_card)->sd_setting |= \ - SD_SUPPORT_CLASS_TEN) -#define SD_CHK_CLASS_TEN(sd_card) ((sd_card)->sd_setting & \ - SD_SUPPORT_CLASS_TEN) -#define SD_CLR_CLASS_TEN(sd_card) ((sd_card)->sd_setting &= \ - ~SD_SUPPORT_CLASS_TEN) -#define SD_SET_1V8(sd_card) ((sd_card)->sd_setting |= \ - SD_SUPPORT_1V8) -#define SD_CHK_1V8(sd_card) ((sd_card)->sd_setting & \ - SD_SUPPORT_1V8) -#define SD_CLR_1V8(sd_card) ((sd_card)->sd_setting &= \ - ~SD_SUPPORT_1V8) - -struct sd_info { - u16 sd_type; - u8 err_code; - u8 sd_data_buf_ready; - u32 sd_addr; - u32 capacity; - - u8 raw_csd[16]; - u8 raw_scr[8]; - - /* Sequential RW */ - int seq_mode; - enum dma_data_direction pre_dir; - u32 pre_sec_addr; - u16 pre_sec_cnt; - - int cleanup_counter; - - int sd_clock; - - int mmc_dont_switch_bus; - -#ifdef SUPPORT_CPRM - int sd_pass_thru_en; - int pre_cmd_err; - u8 last_rsp_type; - u8 rsp[17]; -#endif - - u8 func_group1_mask; - u8 func_group2_mask; - u8 func_group3_mask; - u8 func_group4_mask; - - u8 sd_switch_fail; - u8 sd_read_phase; - -#ifdef SUPPORT_SD_LOCK - u8 sd_lock_status; - u8 sd_erase_status; - u8 sd_lock_notify; -#endif - int need_retune; -}; - -struct xd_delay_write_tag { - u32 old_phyblock; - u32 new_phyblock; - u32 logblock; - u8 pageoff; - u8 delay_write_flag; -}; - -struct xd_info { - u8 maker_code; - u8 device_code; - u8 block_shift; - u8 page_off; - u8 addr_cycle; - u16 cis_block; - u8 multi_flag; - u8 err_code; - u32 capacity; - - struct zone_entry *zone; - int zone_cnt; - - struct xd_delay_write_tag delay_write; - int cleanup_counter; - - int xd_clock; -}; - -#define MODE_512_SEQ 0x01 -#define MODE_2K_SEQ 0x02 - -#define TYPE_MS 0x0000 -#define TYPE_MSPRO 0x0001 - -#define MS_4BIT 0x0100 -#define MS_8BIT 0x0200 -#define MS_HG 0x0400 -#define MS_XC 0x0800 - -#define HG8BIT (MS_HG | MS_8BIT) - -#define CHK_MSPRO(ms_card) (((ms_card)->ms_type & 0xFF) == TYPE_MSPRO) -#define CHK_HG8BIT(ms_card) (CHK_MSPRO(ms_card) && \ - (((ms_card)->ms_type & HG8BIT) == HG8BIT)) -#define CHK_MSXC(ms_card) (CHK_MSPRO(ms_card) && \ - ((ms_card)->ms_type & MS_XC)) -#define CHK_MSHG(ms_card) (CHK_MSPRO(ms_card) && \ - ((ms_card)->ms_type & MS_HG)) - -#define CHK_MS8BIT(ms_card) (((ms_card)->ms_type & MS_8BIT)) -#define CHK_MS4BIT(ms_card) (((ms_card)->ms_type & MS_4BIT)) - -struct ms_delay_write_tag { - u16 old_phyblock; - u16 new_phyblock; - u16 logblock; - u8 pageoff; - u8 delay_write_flag; -}; - -struct ms_info { - u16 ms_type; - u8 block_shift; - u8 page_off; - u16 total_block; - u16 boot_block; - u32 capacity; - - u8 check_ms_flow; - u8 switch_8bit_fail; - u8 err_code; - - struct zone_entry *segment; - int segment_cnt; - - int pro_under_formatting; - int format_status; - u16 progress; - u8 raw_sys_info[96]; -#ifdef SUPPORT_PCGL_1P18 - u8 raw_model_name[48]; -#endif - - u8 multi_flag; - - /* Sequential RW */ - u8 seq_mode; - enum dma_data_direction pre_dir; - u32 pre_sec_addr; - u16 pre_sec_cnt; - u32 total_sec_cnt; - - struct ms_delay_write_tag delay_write; - - int cleanup_counter; - - int ms_clock; - -#ifdef SUPPORT_MAGIC_GATE - u8 magic_gate_id[16]; - u8 mg_entry_num; - int mg_auth; /* flag to indicate authentication process */ -#endif -}; - -struct spi_info { - u8 use_clk; - u8 write_en; - u16 clk_div; - u8 err_code; - - int spi_clock; -}; - -/************/ -/* LUN mode */ -/************/ -/* Single LUN, support xD/SD/MS */ -#define DEFAULT_SINGLE 0 -/* 2 LUN mode, support SD/MS */ -#define SD_MS_2LUN 1 -/* Single LUN, but only support SD/MS, for Barossa LQFP */ -#define SD_MS_1LUN 2 - -#define LAST_LUN_MODE 2 - -/* Barossa package */ -#define QFN 0 -#define LQFP 1 - -/******************/ -/* sd_ctl bit map */ -/******************/ -/* SD push point control, bit 0, 1 */ -#define SD_PUSH_POINT_CTL_MASK 0x03 -#define SD_PUSH_POINT_DELAY 0x01 -#define SD_PUSH_POINT_AUTO 0x02 -/* SD sample point control, bit 2, 3 */ -#define SD_SAMPLE_POINT_CTL_MASK 0x0C -#define SD_SAMPLE_POINT_DELAY 0x04 -#define SD_SAMPLE_POINT_AUTO 0x08 -/* SD DDR Tx phase set by user, bit 4 */ -#define SD_DDR_TX_PHASE_SET_BY_USER 0x10 -/* MMC DDR Tx phase set by user, bit 5 */ -#define MMC_DDR_TX_PHASE_SET_BY_USER 0x20 -/* Support MMC DDR mode, bit 6 */ -#define SUPPORT_MMC_DDR_MODE 0x40 -/* Reset MMC at first */ -#define RESET_MMC_FIRST 0x80 - -#define SEQ_START_CRITERIA 0x20 - -/* MS Power Class En */ -#define POWER_CLASS_2_EN 0x02 -#define POWER_CLASS_1_EN 0x01 - -#define MAX_SHOW_CNT 10 -#define MAX_RESET_CNT 3 - -#define SDIO_EXIST 0x01 -#define SDIO_IGNORED 0x02 - -#define CHK_SDIO_EXIST(chip) ((chip)->sdio_func_exist & SDIO_EXIST) -#define SET_SDIO_EXIST(chip) ((chip)->sdio_func_exist |= SDIO_EXIST) -#define CLR_SDIO_EXIST(chip) ((chip)->sdio_func_exist &= ~SDIO_EXIST) - -#define CHK_SDIO_IGNORED(chip) ((chip)->sdio_func_exist & SDIO_IGNORED) -#define SET_SDIO_IGNORED(chip) ((chip)->sdio_func_exist |= \ - SDIO_IGNORED) -#define CLR_SDIO_IGNORED(chip) ((chip)->sdio_func_exist &= \ - ~SDIO_IGNORED) - -struct rtsx_chip { - struct rtsx_dev *rtsx; - - u32 int_reg; /* Bus interrupt pending register */ - char max_lun; - void *context; - - void *host_cmds_ptr; /* host commands buffer pointer */ - dma_addr_t host_cmds_addr; - int ci; /* Command Index */ - - void *host_sg_tbl_ptr; /* SG descriptor table */ - dma_addr_t host_sg_tbl_addr; - int sgi; /* SG entry index */ - - struct scsi_cmnd *srb; /* current srb */ - struct sense_data_t sense_buffer[MAX_ALLOWED_LUN_CNT]; - - int cur_clk; /* current card clock */ - - /* Current accessed card */ - int cur_card; - - unsigned long need_release; /* need release bit map */ - unsigned long need_reset; /* need reset bit map */ - /* - * Flag to indicate that this card is just resumed from SS state, - * and need released before being resetted - */ - unsigned long need_reinit; - - int rw_need_retry; - -#ifdef SUPPORT_OCP - u32 ocp_int; - u8 ocp_stat; -#endif - - u8 card_exist; /* card exist bit map (physical exist) */ - u8 card_ready; /* card ready bit map (reset successfully) */ - u8 card_fail; /* card reset fail bit map */ - u8 card_ejected; /* card ejected bit map */ - u8 card_wp; /* card write protected bit map */ - - u8 lun_mc; /* - * flag to indicate whether to answer - * MediaChange - */ - -#ifndef LED_AUTO_BLINK - int led_toggle_counter; -#endif - - int sd_reset_counter; - int xd_reset_counter; - int ms_reset_counter; - - /* card bus width */ - u8 card_bus_width[MAX_ALLOWED_LUN_CNT]; - /* card capacity */ - u32 capacity[MAX_ALLOWED_LUN_CNT]; - /* read/write card function pointer */ - card_rw_func rw_card[MAX_ALLOWED_LUN_CNT]; - /* read/write capacity, used for GPIO Toggle */ - u32 rw_cap[MAX_ALLOWED_LUN_CNT]; - /* card to lun mapping table */ - u8 card2lun[32]; - /* lun to card mapping table */ - u8 lun2card[MAX_ALLOWED_LUN_CNT]; - - int rw_fail_cnt[MAX_ALLOWED_LUN_CNT]; - - int sd_show_cnt; - int xd_show_cnt; - int ms_show_cnt; - - /* card information */ - struct sd_info sd_card; - struct xd_info xd_card; - struct ms_info ms_card; - - struct spi_info spi; - - int auto_delink_cnt; - int auto_delink_allowed; - - int aspm_enabled; - - int sdio_aspm; - int sdio_idle; - int sdio_counter; - u8 sdio_raw_data[12]; - - u8 sd_io; - u8 sd_int; - - u8 rtsx_flag; - - int ss_counter; - int idle_counter; - enum RTSX_STAT rtsx_stat; - - u16 vendor_id; - u16 product_id; - u8 ic_version; - - int driver_first_load; - -#ifdef HW_AUTO_SWITCH_SD_BUS - int sdio_in_charge; -#endif - - u8 aspm_level[2]; - - int chip_insert_with_sdio; - - /* Options */ - - int adma_mode; - - int auto_delink_en; - int ss_en; - u8 lun_mode; - u8 aspm_l0s_l1_en; - - int power_down_in_ss; - - int sdr104_en; - int ddr50_en; - int sdr50_en; - - int baro_pkg; - - int asic_code; - int phy_debug_mode; - int hw_bypass_sd; - int sdio_func_exist; - int aux_pwr_exist; - u8 ms_power_class_en; - - int mspro_formatter_enable; - - int remote_wakeup_en; - - int ignore_sd; - int use_hw_setting; - - int ss_idle_period; - - int dynamic_aspm; - - int fpga_sd_sdr104_clk; - int fpga_sd_ddr50_clk; - int fpga_sd_sdr50_clk; - int fpga_sd_hs_clk; - int fpga_mmc_52m_clk; - int fpga_ms_hg_clk; - int fpga_ms_4bit_clk; - int fpga_ms_1bit_clk; - - int asic_sd_sdr104_clk; - int asic_sd_ddr50_clk; - int asic_sd_sdr50_clk; - int asic_sd_hs_clk; - int asic_mmc_52m_clk; - int asic_ms_hg_clk; - int asic_ms_4bit_clk; - int asic_ms_1bit_clk; - - u8 ssc_depth_sd_sdr104; - u8 ssc_depth_sd_ddr50; - u8 ssc_depth_sd_sdr50; - u8 ssc_depth_sd_hs; - u8 ssc_depth_mmc_52m; - u8 ssc_depth_ms_hg; - u8 ssc_depth_ms_4bit; - u8 ssc_depth_low_speed; - - u8 card_drive_sel; - u8 sd30_drive_sel_1v8; - u8 sd30_drive_sel_3v3; - - u8 sd_400mA_ocp_thd; - u8 sd_800mA_ocp_thd; - u8 ms_ocp_thd; - - int ssc_en; - int msi_en; - - int xd_timeout; - int sd_timeout; - int ms_timeout; - int mspro_timeout; - - int auto_power_down; - - int sd_ddr_tx_phase; - int mmc_ddr_tx_phase; - int sd_default_tx_phase; - int sd_default_rx_phase; - - int pmos_pwr_on_interval; - int sd_voltage_switch_delay; - int s3_pwr_off_delay; - - int force_clkreq_0; - int ft2_fast_mode; - - int do_delink_before_power_down; - int polling_config; - int sdio_retry_cnt; - - int delink_stage1_step; - int delink_stage2_step; - int delink_stage3_step; - - int auto_delink_in_L1; - int hp_watch_bios_hotplug; - int support_ms_8bit; - - u8 blink_led; - u8 phy_voltage; - u8 max_payload; - - u32 sd_speed_prior; - u32 sd_current_prior; - u32 sd_ctl; -}; - -static inline struct device *rtsx_dev(const struct rtsx_chip *chip) -{ - return &chip->rtsx->pci->dev; -} - -#define rtsx_set_stat(chip, stat) \ -do { \ - if ((stat) != RTSX_STAT_IDLE) { \ - (chip)->idle_counter = 0; \ - } \ - (chip)->rtsx_stat = (enum RTSX_STAT)(stat); \ -} while (0) -#define rtsx_get_stat(chip) ((chip)->rtsx_stat) -#define rtsx_chk_stat(chip, stat) ((chip)->rtsx_stat == (stat)) - -#define RTSX_SET_DELINK(chip) ((chip)->rtsx_flag |= 0x01) -#define RTSX_CLR_DELINK(chip) ((chip)->rtsx_flag &= 0xFE) -#define RTSX_TST_DELINK(chip) ((chip)->rtsx_flag & 0x01) - -#define CHECK_PID(chip, pid) ((chip)->product_id == (pid)) -#define CHECK_BARO_PKG(chip, pkg) ((chip)->baro_pkg == (pkg)) -#define CHECK_LUN_MODE(chip, mode) ((chip)->lun_mode == (mode)) - -/* Power down control */ -#define SSC_PDCTL 0x01 -#define OC_PDCTL 0x02 - -int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl); -int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl); - -void rtsx_enable_card_int(struct rtsx_chip *chip); -void rtsx_enable_bus_int(struct rtsx_chip *chip); -void rtsx_disable_bus_int(struct rtsx_chip *chip); -int rtsx_reset_chip(struct rtsx_chip *chip); -int rtsx_init_chip(struct rtsx_chip *chip); -void rtsx_release_chip(struct rtsx_chip *chip); -void rtsx_polling_func(struct rtsx_chip *chip); -void rtsx_stop_cmd(struct rtsx_chip *chip, int card); -int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data); -int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data); -int rtsx_write_cfg_dw(struct rtsx_chip *chip, - u8 func_no, u16 addr, u32 mask, u32 val); -int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val); -int rtsx_write_cfg_seq(struct rtsx_chip *chip, - u8 func, u16 addr, u8 *buf, int len); -int rtsx_read_cfg_seq(struct rtsx_chip *chip, - u8 func, u16 addr, u8 *buf, int len); -int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val); -int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val); -int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val); -int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val); -int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit); -int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit); -void rtsx_enter_ss(struct rtsx_chip *chip); -void rtsx_exit_ss(struct rtsx_chip *chip); -int rtsx_pre_handle_interrupt(struct rtsx_chip *chip); -void rtsx_enter_L1(struct rtsx_chip *chip); -void rtsx_exit_L1(struct rtsx_chip *chip); -void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat); -void rtsx_enable_aspm(struct rtsx_chip *chip); -void rtsx_disable_aspm(struct rtsx_chip *chip); -int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len); -int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len); -int rtsx_check_chip_exist(struct rtsx_chip *chip); - -#endif /* __REALTEK_RTSX_CHIP_H */ diff --git a/drivers/staging/rts5208/rtsx_scsi.c b/drivers/staging/rts5208/rtsx_scsi.c deleted file mode 100644 index c27cffb9ad8f1..0000000000000 --- a/drivers/staging/rts5208/rtsx_scsi.c +++ /dev/null @@ -1,3279 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include -#include - -#include "rtsx.h" -#include "sd.h" -#include "ms.h" -#include "spi.h" - -void scsi_show_command(struct rtsx_chip *chip) -{ - struct scsi_cmnd *srb = chip->srb; - char *what = NULL; - bool unknown_cmd = false; - int len; - - switch (srb->cmnd[0]) { - case TEST_UNIT_READY: - what = "TEST_UNIT_READY"; - break; - case REZERO_UNIT: - what = "REZERO_UNIT"; - break; - case REQUEST_SENSE: - what = "REQUEST_SENSE"; - break; - case FORMAT_UNIT: - what = "FORMAT_UNIT"; - break; - case READ_BLOCK_LIMITS: - what = "READ_BLOCK_LIMITS"; - break; - case REASSIGN_BLOCKS: - what = "REASSIGN_BLOCKS"; - break; - case READ_6: - what = "READ_6"; - break; - case WRITE_6: - what = "WRITE_6"; - break; - case SEEK_6: - what = "SEEK_6"; - break; - case READ_REVERSE: - what = "READ_REVERSE"; - break; - case WRITE_FILEMARKS: - what = "WRITE_FILEMARKS"; - break; - case SPACE: - what = "SPACE"; - break; - case INQUIRY: - what = "INQUIRY"; - break; - case RECOVER_BUFFERED_DATA: - what = "RECOVER_BUFFERED_DATA"; - break; - case MODE_SELECT: - what = "MODE_SELECT"; - break; - case RESERVE: - what = "RESERVE"; - break; - case RELEASE: - what = "RELEASE"; - break; - case COPY: - what = "COPY"; - break; - case ERASE: - what = "ERASE"; - break; - case MODE_SENSE: - what = "MODE_SENSE"; - break; - case START_STOP: - what = "START_STOP"; - break; - case RECEIVE_DIAGNOSTIC: - what = "RECEIVE_DIAGNOSTIC"; - break; - case SEND_DIAGNOSTIC: - what = "SEND_DIAGNOSTIC"; - break; - case ALLOW_MEDIUM_REMOVAL: - what = "ALLOW_MEDIUM_REMOVAL"; - break; - case SET_WINDOW: - what = "SET_WINDOW"; - break; - case READ_CAPACITY: - what = "READ_CAPACITY"; - break; - case READ_10: - what = "READ_10"; - break; - case WRITE_10: - what = "WRITE_10"; - break; - case SEEK_10: - what = "SEEK_10"; - break; - case WRITE_VERIFY: - what = "WRITE_VERIFY"; - break; - case VERIFY: - what = "VERIFY"; - break; - case SEARCH_HIGH: - what = "SEARCH_HIGH"; - break; - case SEARCH_EQUAL: - what = "SEARCH_EQUAL"; - break; - case SEARCH_LOW: - what = "SEARCH_LOW"; - break; - case SET_LIMITS: - what = "SET_LIMITS"; - break; - case READ_POSITION: - what = "READ_POSITION"; - break; - case SYNCHRONIZE_CACHE: - what = "SYNCHRONIZE_CACHE"; - break; - case LOCK_UNLOCK_CACHE: - what = "LOCK_UNLOCK_CACHE"; - break; - case READ_DEFECT_DATA: - what = "READ_DEFECT_DATA"; - break; - case MEDIUM_SCAN: - what = "MEDIUM_SCAN"; - break; - case COMPARE: - what = "COMPARE"; - break; - case COPY_VERIFY: - what = "COPY_VERIFY"; - break; - case WRITE_BUFFER: - what = "WRITE_BUFFER"; - break; - case READ_BUFFER: - what = "READ_BUFFER"; - break; - case UPDATE_BLOCK: - what = "UPDATE_BLOCK"; - break; - case READ_LONG: - what = "READ_LONG"; - break; - case WRITE_LONG: - what = "WRITE_LONG"; - break; - case CHANGE_DEFINITION: - what = "CHANGE_DEFINITION"; - break; - case WRITE_SAME: - what = "WRITE_SAME"; - break; - case GPCMD_READ_SUBCHANNEL: - what = "READ SUBCHANNEL"; - break; - case READ_TOC: - what = "READ_TOC"; - break; - case GPCMD_READ_HEADER: - what = "READ HEADER"; - break; - case GPCMD_PLAY_AUDIO_10: - what = "PLAY AUDIO (10)"; - break; - case GPCMD_PLAY_AUDIO_MSF: - what = "PLAY AUDIO MSF"; - break; - case GPCMD_GET_EVENT_STATUS_NOTIFICATION: - what = "GET EVENT/STATUS NOTIFICATION"; - break; - case GPCMD_PAUSE_RESUME: - what = "PAUSE/RESUME"; - break; - case LOG_SELECT: - what = "LOG_SELECT"; - break; - case LOG_SENSE: - what = "LOG_SENSE"; - break; - case GPCMD_STOP_PLAY_SCAN: - what = "STOP PLAY/SCAN"; - break; - case GPCMD_READ_DISC_INFO: - what = "READ DISC INFORMATION"; - break; - case GPCMD_READ_TRACK_RZONE_INFO: - what = "READ TRACK INFORMATION"; - break; - case GPCMD_RESERVE_RZONE_TRACK: - what = "RESERVE TRACK"; - break; - case GPCMD_SEND_OPC: - what = "SEND OPC"; - break; - case MODE_SELECT_10: - what = "MODE_SELECT_10"; - break; - case GPCMD_REPAIR_RZONE_TRACK: - what = "REPAIR TRACK"; - break; - case 0x59: - what = "READ MASTER CUE"; - break; - case MODE_SENSE_10: - what = "MODE_SENSE_10"; - break; - case GPCMD_CLOSE_TRACK: - what = "CLOSE TRACK/SESSION"; - break; - case 0x5C: - what = "READ BUFFER CAPACITY"; - break; - case 0x5D: - what = "SEND CUE SHEET"; - break; - case GPCMD_BLANK: - what = "BLANK"; - break; - case REPORT_LUNS: - what = "REPORT LUNS"; - break; - case MOVE_MEDIUM: - what = "MOVE_MEDIUM or PLAY AUDIO (12)"; - break; - case READ_12: - what = "READ_12"; - break; - case WRITE_12: - what = "WRITE_12"; - break; - case WRITE_VERIFY_12: - what = "WRITE_VERIFY_12"; - break; - case SEARCH_HIGH_12: - what = "SEARCH_HIGH_12"; - break; - case SEARCH_EQUAL_12: - what = "SEARCH_EQUAL_12"; - break; - case SEARCH_LOW_12: - what = "SEARCH_LOW_12"; - break; - case SEND_VOLUME_TAG: - what = "SEND_VOLUME_TAG"; - break; - case READ_ELEMENT_STATUS: - what = "READ_ELEMENT_STATUS"; - break; - case GPCMD_READ_CD_MSF: - what = "READ CD MSF"; - break; - case GPCMD_SCAN: - what = "SCAN"; - break; - case GPCMD_SET_SPEED: - what = "SET CD SPEED"; - break; - case GPCMD_MECHANISM_STATUS: - what = "MECHANISM STATUS"; - break; - case GPCMD_READ_CD: - what = "READ CD"; - break; - case 0xE1: - what = "WRITE CONTINUE"; - break; - case WRITE_LONG_2: - what = "WRITE_LONG_2"; - break; - case VENDOR_CMND: - what = "Realtek's vendor command"; - break; - default: - what = "(unknown command)"; - unknown_cmd = true; - break; - } - - if (srb->cmnd[0] != TEST_UNIT_READY) - dev_dbg(rtsx_dev(chip), "Command %s (%d bytes)\n", - what, srb->cmd_len); - - if (unknown_cmd) { - len = min_t(unsigned short, srb->cmd_len, 16); - dev_dbg(rtsx_dev(chip), "%*ph\n", len, srb->cmnd); - } -} - -void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type) -{ - switch (sense_type) { - case SENSE_TYPE_MEDIA_CHANGE: - set_sense_data(chip, lun, CUR_ERR, 0x06, 0, 0x28, 0, 0, 0); - break; - - case SENSE_TYPE_MEDIA_NOT_PRESENT: - set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x3A, 0, 0, 0); - break; - - case SENSE_TYPE_MEDIA_LBA_OVER_RANGE: - set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x21, 0, 0, 0); - break; - - case SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT: - set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x25, 0, 0, 0); - break; - - case SENSE_TYPE_MEDIA_WRITE_PROTECT: - set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x27, 0, 0, 0); - break; - - case SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR: - set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x11, 0, 0, 0); - break; - - case SENSE_TYPE_MEDIA_WRITE_ERR: - set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x02, 0, 0); - break; - - case SENSE_TYPE_MEDIA_INVALID_CMD_FIELD: - set_sense_data(chip, lun, CUR_ERR, ILGAL_REQ, 0, - ASC_INVLD_CDB, ASCQ_INVLD_CDB, CDB_ILLEGAL, 1); - break; - - case SENSE_TYPE_FORMAT_IN_PROGRESS: - set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, 0, 0); - break; - - case SENSE_TYPE_FORMAT_CMD_FAILED: - set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x31, 0x01, 0, 0); - break; - -#ifdef SUPPORT_MAGIC_GATE - case SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB: - set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x02, 0, 0); - break; - - case SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN: - set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x00, 0, 0); - break; - - case SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM: - set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x30, 0x00, 0, 0); - break; - - case SENSE_TYPE_MG_WRITE_ERR: - set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x00, 0, 0); - break; -#endif - -#ifdef SUPPORT_SD_LOCK - case SENSE_TYPE_MEDIA_READ_FORBIDDEN: - set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x11, 0x13, 0, 0); - break; -#endif - - case SENSE_TYPE_NO_SENSE: - default: - set_sense_data(chip, lun, CUR_ERR, 0, 0, 0, 0, 0, 0); - break; - } -} - -void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code, - u8 sense_key, u32 info, u8 asc, u8 ascq, u8 sns_key_info0, - u16 sns_key_info1) -{ - struct sense_data_t *sense = &chip->sense_buffer[lun]; - - sense->err_code = err_code; - sense->sense_key = sense_key; - sense->info[0] = (u8)(info >> 24); - sense->info[1] = (u8)(info >> 16); - sense->info[2] = (u8)(info >> 8); - sense->info[3] = (u8)info; - - sense->ad_sense_len = sizeof(struct sense_data_t) - 8; - sense->asc = asc; - sense->ascq = ascq; - if (sns_key_info0 != 0) { - sense->sns_key_info[0] = SKSV | sns_key_info0; - sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 4; - sense->sns_key_info[2] = sns_key_info1 & 0x0f; - } -} - -static int test_unit_ready(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - - if (!(CHK_BIT(chip->lun_mc, lun))) { - SET_BIT(chip->lun_mc, lun); - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - -#ifdef SUPPORT_SD_LOCK - if (get_lun_card(chip, SCSI_LUN(srb)) == SD_CARD) { - struct sd_info *sd_card = &chip->sd_card; - - if (sd_card->sd_lock_notify) { - sd_card->sd_lock_notify = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } else if (sd_card->sd_lock_status & SD_LOCKED) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_READ_FORBIDDEN); - return TRANSPORT_FAILED; - } - } -#endif - - return TRANSPORT_GOOD; -} - -static unsigned char formatter_inquiry_str[20] = { - 'M', 'E', 'M', 'O', 'R', 'Y', 'S', 'T', 'I', 'C', 'K', -#ifdef SUPPORT_MAGIC_GATE - '-', 'M', 'G', /* Byte[47:49] */ -#else - 0x20, 0x20, 0x20, /* Byte[47:49] */ -#endif - -#ifdef SUPPORT_MAGIC_GATE - 0x0B, /* Byte[50]: MG, MS, MSPro, MSXC */ -#else - 0x09, /* Byte[50]: MS, MSPro, MSXC */ -#endif - 0x00, /* Byte[51]: Category Specific Commands */ - 0x00, /* Byte[52]: Access Control and feature */ - 0x20, 0x20, 0x20, /* Byte[53:55] */ -}; - -static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - char *inquiry_default = (char *)"Generic-xD/SD/M.S. 1.00"; - char *inquiry_sdms = (char *)"Generic-SD/MemoryStick 1.00"; - char *inquiry_sd = (char *)"Generic-SD/MMC 1.00"; - char *inquiry_ms = (char *)"Generic-MemoryStick 1.00"; - char *inquiry_string; - unsigned char sendbytes; - unsigned char *buf; - u8 card = get_lun_card(chip, lun); - bool pro_formatter_flag = false; - unsigned char inquiry_buf[] = { - QULIFIRE | DRCT_ACCESS_DEV, - RMB_DISC | 0x0D, - 0x00, - 0x01, - 0x1f, - 0x02, - 0, - REL_ADR | WBUS_32 | WBUS_16 | SYNC | LINKED | CMD_QUE | SFT_RE, - }; - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) { - if (chip->lun2card[lun] == SD_CARD) - inquiry_string = inquiry_sd; - else - inquiry_string = inquiry_ms; - - } else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) { - inquiry_string = inquiry_sdms; - } else { - inquiry_string = inquiry_default; - } - - buf = vmalloc(scsi_bufflen(srb)); - if (!buf) - return TRANSPORT_ERROR; - -#ifdef SUPPORT_MAGIC_GATE - if (chip->mspro_formatter_enable && - (chip->lun2card[lun] & MS_CARD)) -#else - if (chip->mspro_formatter_enable) -#endif - if (!card || card == MS_CARD) - pro_formatter_flag = true; - - if (pro_formatter_flag) { - if (scsi_bufflen(srb) < 56) - sendbytes = (unsigned char)(scsi_bufflen(srb)); - else - sendbytes = 56; - - } else { - if (scsi_bufflen(srb) < 36) - sendbytes = (unsigned char)(scsi_bufflen(srb)); - else - sendbytes = 36; - } - - if (sendbytes > 8) { - memcpy(buf, inquiry_buf, 8); - memcpy(buf + 8, inquiry_string, min(sendbytes, 36) - 8); - if (pro_formatter_flag) { - /* Additional Length */ - buf[4] = 0x33; - } - } else { - memcpy(buf, inquiry_buf, sendbytes); - } - - if (pro_formatter_flag) { - if (sendbytes > 36) - memcpy(buf + 36, formatter_inquiry_str, sendbytes - 36); - } - - scsi_set_resid(srb, 0); - - rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb); - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int start_stop_unit(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - - scsi_set_resid(srb, scsi_bufflen(srb)); - - if (srb->cmnd[1] == 1) - return TRANSPORT_GOOD; - - switch (srb->cmnd[0x4]) { - case STOP_MEDIUM: - /* Media disabled */ - return TRANSPORT_GOOD; - - case UNLOAD_MEDIUM: - /* Media shall be unload */ - if (check_card_ready(chip, lun)) - eject_card(chip, lun); - return TRANSPORT_GOOD; - - case MAKE_MEDIUM_READY: - case LOAD_MEDIUM: - if (check_card_ready(chip, lun)) - return TRANSPORT_GOOD; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - - break; - } - - return TRANSPORT_ERROR; -} - -static int allow_medium_removal(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int prevent; - - prevent = srb->cmnd[4] & 0x1; - - scsi_set_resid(srb, 0); - - if (prevent) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return TRANSPORT_GOOD; -} - -static int request_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sense_data_t *sense; - unsigned int lun = SCSI_LUN(srb); - struct ms_info *ms_card = &chip->ms_card; - unsigned char *tmp, *buf; - - sense = &chip->sense_buffer[lun]; - - if ((get_lun_card(chip, lun) == MS_CARD) && - ms_card->pro_under_formatting) { - if (ms_card->format_status == FORMAT_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); - ms_card->pro_under_formatting = 0; - ms_card->progress = 0; - } else if (ms_card->format_status == FORMAT_IN_PROGRESS) { - /* Logical Unit Not Ready Format in Progress */ - set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, - 0, (u16)(ms_card->progress)); - } else { - /* Format Command Failed */ - set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED); - ms_card->pro_under_formatting = 0; - ms_card->progress = 0; - } - - rtsx_set_stat(chip, RTSX_STAT_RUN); - } - - buf = vmalloc(scsi_bufflen(srb)); - if (!buf) - return TRANSPORT_ERROR; - - tmp = (unsigned char *)sense; - memcpy(buf, tmp, scsi_bufflen(srb)); - - rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb); - vfree(buf); - - scsi_set_resid(srb, 0); - /* Reset Sense Data */ - set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); - return TRANSPORT_GOOD; -} - -static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd, - int lun, u8 *buf, int buf_len) -{ - struct ms_info *ms_card = &chip->ms_card; - int sys_info_offset; - int data_size = buf_len; - bool support_format = false; - int i = 0; - - if (cmd == MODE_SENSE) { - sys_info_offset = 8; - if (data_size > 0x68) - data_size = 0x68; - - buf[i++] = 0x67; /* Mode Data Length */ - } else { - sys_info_offset = 12; - if (data_size > 0x6C) - data_size = 0x6C; - - buf[i++] = 0x00; /* Mode Data Length (MSB) */ - buf[i++] = 0x6A; /* Mode Data Length (LSB) */ - } - - /* Medium Type Code */ - if (check_card_ready(chip, lun)) { - if (CHK_MSXC(ms_card)) { - support_format = true; - buf[i++] = 0x40; - } else if (CHK_MSPRO(ms_card)) { - support_format = true; - buf[i++] = 0x20; - } else { - buf[i++] = 0x10; - } - - /* WP */ - if (check_card_wp(chip, lun)) - buf[i++] = 0x80; - else - buf[i++] = 0x00; - - } else { - buf[i++] = 0x00; /* MediaType */ - buf[i++] = 0x00; /* WP */ - } - - buf[i++] = 0x00; /* Reserved */ - - if (cmd == MODE_SENSE_10) { - buf[i++] = 0x00; /* Reserved */ - buf[i++] = 0x00; /* Block descriptor length(MSB) */ - buf[i++] = 0x00; /* Block descriptor length(LSB) */ - - /* The Following Data is the content of "Page 0x20" */ - if (data_size >= 9) - buf[i++] = 0x20; /* Page Code */ - if (data_size >= 10) - buf[i++] = 0x62; /* Page Length */ - if (data_size >= 11) - buf[i++] = 0x00; /* No Access Control */ - if (data_size >= 12) { - if (support_format) - buf[i++] = 0xC0; /* SF, SGM */ - else - buf[i++] = 0x00; - } - } else { - /* The Following Data is the content of "Page 0x20" */ - if (data_size >= 5) - buf[i++] = 0x20; /* Page Code */ - if (data_size >= 6) - buf[i++] = 0x62; /* Page Length */ - if (data_size >= 7) - buf[i++] = 0x00; /* No Access Control */ - if (data_size >= 8) { - if (support_format) - buf[i++] = 0xC0; /* SF, SGM */ - else - buf[i++] = 0x00; - } - } - - if (data_size > sys_info_offset) { - /* 96 Bytes Attribute Data */ - int len = data_size - sys_info_offset; - - len = (len < 96) ? len : 96; - - memcpy(buf + sys_info_offset, ms_card->raw_sys_info, len); - } -} - -static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - unsigned int data_size; - int status; - bool pro_formatter_flag; - unsigned char page_code, *buf; - u8 card = get_lun_card(chip, lun); - -#ifndef SUPPORT_MAGIC_GATE - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - scsi_set_resid(srb, scsi_bufflen(srb)); - return TRANSPORT_FAILED; - } -#endif - - pro_formatter_flag = false; - data_size = 8; -#ifdef SUPPORT_MAGIC_GATE - if ((chip->lun2card[lun] & MS_CARD)) { - if (!card || card == MS_CARD) { - data_size = 108; - if (chip->mspro_formatter_enable) - pro_formatter_flag = true; - } - } -#else - if (card == MS_CARD) { - if (chip->mspro_formatter_enable) { - pro_formatter_flag = true; - data_size = 108; - } - } -#endif - - buf = kmalloc(data_size, GFP_KERNEL); - if (!buf) - return TRANSPORT_ERROR; - - page_code = srb->cmnd[2] & 0x3f; - - if (page_code == 0x3F || page_code == 0x1C || - page_code == 0x00 || - (pro_formatter_flag && page_code == 0x20)) { - if (srb->cmnd[0] == MODE_SENSE) { - if (page_code == 0x3F || page_code == 0x20) { - ms_mode_sense(chip, srb->cmnd[0], - lun, buf, data_size); - } else { - data_size = 4; - buf[0] = 0x03; - buf[1] = 0x00; - if (check_card_wp(chip, lun)) - buf[2] = 0x80; - else - buf[2] = 0x00; - - buf[3] = 0x00; - } - } else { - if (page_code == 0x3F || page_code == 0x20) { - ms_mode_sense(chip, srb->cmnd[0], - lun, buf, data_size); - } else { - data_size = 8; - buf[0] = 0x00; - buf[1] = 0x06; - buf[2] = 0x00; - if (check_card_wp(chip, lun)) - buf[3] = 0x80; - else - buf[3] = 0x00; - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x00; - buf[7] = 0x00; - } - } - status = TRANSPORT_GOOD; - } else { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - scsi_set_resid(srb, scsi_bufflen(srb)); - status = TRANSPORT_FAILED; - } - - if (status == TRANSPORT_GOOD) { - unsigned int len = min_t(unsigned int, scsi_bufflen(srb), - data_size); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - } - kfree(buf); - - return status; -} - -static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ -#ifdef SUPPORT_SD_LOCK - struct sd_info *sd_card = &chip->sd_card; -#endif - unsigned int lun = SCSI_LUN(srb); - int retval; - u32 start_sec; - u16 sec_cnt; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - if (!check_card_ready(chip, lun) || (get_card_size(chip, lun) == 0)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - - if (!(CHK_BIT(chip->lun_mc, lun))) { - SET_BIT(chip->lun_mc, lun); - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - -#ifdef SUPPORT_SD_LOCK - if (sd_card->sd_erase_status) { - /* Accessing to any card is forbidden - * until the erase procedure of SD is completed - */ - dev_dbg(rtsx_dev(chip), "SD card being erased!\n"); - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_READ_FORBIDDEN); - return TRANSPORT_FAILED; - } - - if (get_lun_card(chip, lun) == SD_CARD) { - if (sd_card->sd_lock_status & SD_LOCKED) { - dev_dbg(rtsx_dev(chip), "SD card locked!\n"); - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_READ_FORBIDDEN); - return TRANSPORT_FAILED; - } - } -#endif - - if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) { - start_sec = ((u32)srb->cmnd[2] << 24) | - ((u32)srb->cmnd[3] << 16) | - ((u32)srb->cmnd[4] << 8) | ((u32)srb->cmnd[5]); - sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; - } else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6)) { - start_sec = ((u32)(srb->cmnd[1] & 0x1F) << 16) | - ((u32)srb->cmnd[2] << 8) | ((u32)srb->cmnd[3]); - sec_cnt = srb->cmnd[4]; - if (sec_cnt == 0) - sec_cnt = 256; - } else if ((srb->cmnd[0] == VENDOR_CMND) && - (srb->cmnd[1] == SCSI_APP_CMD) && - ((srb->cmnd[2] == PP_READ10) || (srb->cmnd[2] == PP_WRITE10))) { - start_sec = ((u32)srb->cmnd[4] << 24) | - ((u32)srb->cmnd[5] << 16) | - ((u32)srb->cmnd[6] << 8) | ((u32)srb->cmnd[7]); - sec_cnt = ((u16)(srb->cmnd[9]) << 8) | srb->cmnd[10]; - } else { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - /* In some test, we will receive a start_sec like 0xFFFFFFFF. - * In this situation, start_sec + sec_cnt will overflow, so we - * need to judge start_sec at first - */ - if (start_sec > get_card_size(chip, lun) || - ((start_sec + sec_cnt) > get_card_size(chip, lun))) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LBA_OVER_RANGE); - return TRANSPORT_FAILED; - } - - if (sec_cnt == 0) { - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; - } - - if (chip->rw_fail_cnt[lun] == 3) { - dev_dbg(rtsx_dev(chip), "read/write fail three times in succession\n"); - if (srb->sc_data_direction == DMA_FROM_DEVICE) - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - else - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); - - return TRANSPORT_FAILED; - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { - if (check_card_wp(chip, lun)) { - dev_dbg(rtsx_dev(chip), "Write protected card!\n"); - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_PROTECT); - return TRANSPORT_FAILED; - } - } - - retval = card_rw(srb, chip, start_sec, sec_cnt); - if (retval != STATUS_SUCCESS) { - if (chip->need_release & chip->lun2card[lun]) { - chip->rw_fail_cnt[lun] = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - } else { - chip->rw_fail_cnt[lun]++; - if (srb->sc_data_direction == DMA_FROM_DEVICE) - set_sense_type - (chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - else - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - } - retval = TRANSPORT_FAILED; - goto exit; - } else { - chip->rw_fail_cnt[lun] = 0; - retval = TRANSPORT_GOOD; - } - - scsi_set_resid(srb, 0); - -exit: - return retval; -} - -static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned char *buf; - unsigned int lun = SCSI_LUN(srb); - unsigned int buf_len; - u8 card = get_lun_card(chip, lun); - u32 card_size; - int desc_cnt; - int i = 0; - - if (!check_card_ready(chip, lun)) { - if (!chip->mspro_formatter_enable) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - } - - buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12; - - buf = kmalloc(buf_len, GFP_KERNEL); - if (!buf) - return TRANSPORT_ERROR; - - buf[i++] = 0; - buf[i++] = 0; - buf[i++] = 0; - - /* Capacity List Length */ - if (buf_len > 12 && chip->mspro_formatter_enable && - (chip->lun2card[lun] & MS_CARD) && - (!card || card == MS_CARD)) { - buf[i++] = 0x10; - desc_cnt = 2; - } else { - buf[i++] = 0x08; - desc_cnt = 1; - } - - while (desc_cnt) { - if (check_card_ready(chip, lun)) { - card_size = get_card_size(chip, lun); - buf[i++] = (unsigned char)(card_size >> 24); - buf[i++] = (unsigned char)(card_size >> 16); - buf[i++] = (unsigned char)(card_size >> 8); - buf[i++] = (unsigned char)card_size; - - if (desc_cnt == 2) - buf[i++] = 2; - else - buf[i++] = 0; - } else { - buf[i++] = 0xFF; - buf[i++] = 0xFF; - buf[i++] = 0xFF; - buf[i++] = 0xFF; - - if (desc_cnt == 2) - buf[i++] = 3; - else - buf[i++] = 0; - } - - buf[i++] = 0x00; - buf[i++] = 0x02; - buf[i++] = 0x00; - - desc_cnt--; - } - - buf_len = min_t(unsigned int, scsi_bufflen(srb), buf_len); - rtsx_stor_set_xfer_buf(buf, buf_len, srb); - kfree(buf); - - scsi_set_resid(srb, scsi_bufflen(srb) - buf_len); - - return TRANSPORT_GOOD; -} - -static int read_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned char *buf; - unsigned int lun = SCSI_LUN(srb); - u32 card_size; - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - - if (!(CHK_BIT(chip->lun_mc, lun))) { - SET_BIT(chip->lun_mc, lun); - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - buf = kmalloc(8, GFP_KERNEL); - if (!buf) - return TRANSPORT_ERROR; - - card_size = get_card_size(chip, lun); - buf[0] = (unsigned char)((card_size - 1) >> 24); - buf[1] = (unsigned char)((card_size - 1) >> 16); - buf[2] = (unsigned char)((card_size - 1) >> 8); - buf[3] = (unsigned char)(card_size - 1); - - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x02; - buf[7] = 0x00; - - rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb); - kfree(buf); - - scsi_set_resid(srb, 0); - - return TRANSPORT_GOOD; -} - -static int read_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short len, i; - int retval; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len; i++) { - retval = spi_read_eeprom(chip, i, buf + i); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - } - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int write_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short len, i; - int retval; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - if (len == 511) { - retval = spi_erase_eeprom_chip(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } else { - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), - len); - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - for (i = 0; i < len; i++) { - retval = spi_write_eeprom(chip, i, buf[i]); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } - - vfree(buf); - } - - return TRANSPORT_GOOD; -} - -static int read_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr, len, i; - int retval; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3]; - len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - - if (addr < 0xFC00) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len; i++) { - retval = rtsx_read_register(chip, addr + i, buf + i); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - } - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int write_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr, len, i; - int retval; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3]; - len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - - if (addr < 0xFC00) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len; i++) { - retval = rtsx_write_register(chip, addr + i, 0xFF, buf[i]); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int get_sd_csd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - - if (get_lun_card(chip, lun) != SD_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - scsi_set_resid(srb, 0); - rtsx_stor_set_xfer_buf(sd_card->raw_csd, scsi_bufflen(srb), srb); - - return TRANSPORT_GOOD; -} - -static int toggle_gpio_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - u8 gpio = srb->cmnd[2]; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - if (gpio > 3) - gpio = 1; - toggle_gpio(chip, gpio); - - return TRANSPORT_GOOD; -} - -static int read_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - u8 addr, buf[4]; - u32 val; - unsigned int len; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = srb->cmnd[4]; - - val = rtsx_readl(chip, addr); - dev_dbg(rtsx_dev(chip), "Host register (0x%x): 0x%x\n", addr, val); - - buf[0] = (u8)(val >> 24); - buf[1] = (u8)(val >> 16); - buf[2] = (u8)(val >> 8); - buf[3] = (u8)val; - - len = min_t(unsigned int, scsi_bufflen(srb), 4); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - return TRANSPORT_GOOD; -} - -static int write_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - u8 addr, buf[4]; - u32 val; - unsigned int len; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = srb->cmnd[4]; - - len = min_t(unsigned int, scsi_bufflen(srb), 4); - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - val = ((u32)buf[0] << 24) | ((u32)buf[1] << 16) | ((u32)buf[2] - << 8) | buf[3]; - - rtsx_writel(chip, addr, val); - - return TRANSPORT_GOOD; -} - -static int set_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - - if (srb->cmnd[3] == 1) { - /* Variable Clock */ - struct xd_info *xd_card = &chip->xd_card; - struct sd_info *sd_card = &chip->sd_card; - struct ms_info *ms_card = &chip->ms_card; - - switch (srb->cmnd[4]) { - case XD_CARD: - xd_card->xd_clock = srb->cmnd[5]; - break; - - case SD_CARD: - sd_card->sd_clock = srb->cmnd[5]; - break; - - case MS_CARD: - ms_card->ms_clock = srb->cmnd[5]; - break; - - default: - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - } else if (srb->cmnd[3] == 2) { - if (srb->cmnd[4]) { - chip->blink_led = 1; - } else { - int retval; - - chip->blink_led = 0; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && - (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - turn_off_led(chip, LED_GPIO); - } - } else { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return TRANSPORT_GOOD; -} - -static int get_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - - if (srb->cmnd[3] == 1) { - struct xd_info *xd_card = &chip->xd_card; - struct sd_info *sd_card = &chip->sd_card; - struct ms_info *ms_card = &chip->ms_card; - u8 tmp; - - switch (srb->cmnd[4]) { - case XD_CARD: - tmp = (u8)(xd_card->xd_clock); - break; - - case SD_CARD: - tmp = (u8)(sd_card->sd_clock); - break; - - case MS_CARD: - tmp = (u8)(ms_card->ms_clock); - break; - - default: - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - rtsx_stor_set_xfer_buf(&tmp, 1, srb); - } else if (srb->cmnd[3] == 2) { - u8 tmp = chip->blink_led; - - rtsx_stor_set_xfer_buf(&tmp, 1, srb); - } else { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return TRANSPORT_GOOD; -} - -static int dma_access_ring_buffer(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - unsigned int lun = SCSI_LUN(srb); - u16 len; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - len = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5]; - len = min_t(u16, len, scsi_bufflen(srb)); - - if (srb->sc_data_direction == DMA_FROM_DEVICE) - dev_dbg(rtsx_dev(chip), "Read from device\n"); - else - dev_dbg(rtsx_dev(chip), "Write to device\n"); - - retval = rtsx_transfer_data(chip, 0, scsi_sglist(srb), len, - scsi_sg_count(srb), srb->sc_data_direction, - 1000); - if (retval < 0) { - if (srb->sc_data_direction == DMA_FROM_DEVICE) - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - else - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - - return TRANSPORT_FAILED; - } - scsi_set_resid(srb, 0); - - return TRANSPORT_GOOD; -} - -static int get_dev_status(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - struct ms_info *ms_card = &chip->ms_card; - int buf_len; - unsigned int lun = SCSI_LUN(srb); - u8 card = get_lun_card(chip, lun); - u8 status[32]; -#ifdef SUPPORT_OCP - u8 oc_now_mask = 0, oc_ever_mask = 0; -#endif - - memset(status, 0, 32); - - status[0] = (u8)(chip->product_id); - status[1] = chip->ic_version; - - if (chip->auto_delink_en) - status[2] = 0x10; - else - status[2] = 0x00; - - status[3] = 20; - status[4] = 10; - status[5] = 05; - status[6] = 21; - - if (chip->card_wp) - status[7] = 0x20; - else - status[7] = 0x00; - -#ifdef SUPPORT_OCP - status[8] = 0; - if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && - chip->lun2card[lun] == MS_CARD) { - oc_now_mask = MS_OC_NOW; - oc_ever_mask = MS_OC_EVER; - } else { - oc_now_mask = SD_OC_NOW; - oc_ever_mask = SD_OC_EVER; - } - - if (chip->ocp_stat & oc_now_mask) - status[8] |= 0x02; - - if (chip->ocp_stat & oc_ever_mask) - status[8] |= 0x01; -#endif - - if (card == SD_CARD) { - if (CHK_SD(sd_card)) { - if (CHK_SD_HCXC(sd_card)) { - if (sd_card->capacity > 0x4000000) - status[0x0E] = 0x02; - else - status[0x0E] = 0x01; - } else { - status[0x0E] = 0x00; - } - - if (CHK_SD_SDR104(sd_card)) - status[0x0F] = 0x03; - else if (CHK_SD_DDR50(sd_card)) - status[0x0F] = 0x04; - else if (CHK_SD_SDR50(sd_card)) - status[0x0F] = 0x02; - else if (CHK_SD_HS(sd_card)) - status[0x0F] = 0x01; - else - status[0x0F] = 0x00; - } else { - if (CHK_MMC_SECTOR_MODE(sd_card)) - status[0x0E] = 0x01; - else - status[0x0E] = 0x00; - - if (CHK_MMC_DDR52(sd_card)) - status[0x0F] = 0x03; - else if (CHK_MMC_52M(sd_card)) - status[0x0F] = 0x02; - else if (CHK_MMC_26M(sd_card)) - status[0x0F] = 0x01; - else - status[0x0F] = 0x00; - } - } else if (card == MS_CARD) { - if (CHK_MSPRO(ms_card)) { - if (CHK_MSXC(ms_card)) - status[0x0E] = 0x01; - else - status[0x0E] = 0x00; - - if (CHK_HG8BIT(ms_card)) - status[0x0F] = 0x01; - else - status[0x0F] = 0x00; - } - } - -#ifdef SUPPORT_SD_LOCK - if (card == SD_CARD) { - status[0x17] = 0x80; - if (sd_card->sd_erase_status) - status[0x17] |= 0x01; - if (sd_card->sd_lock_status & SD_LOCKED) { - status[0x17] |= 0x02; - status[0x07] |= 0x40; - } - if (sd_card->sd_lock_status & SD_PWD_EXIST) - status[0x17] |= 0x04; - } else { - status[0x17] = 0x00; - } - - dev_dbg(rtsx_dev(chip), "status[0x17] = 0x%x\n", status[0x17]); -#endif - - status[0x18] = 0x8A; - status[0x1A] = 0x28; -#ifdef SUPPORT_SD_LOCK - status[0x1F] = 0x01; -#endif - - buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(status)); - rtsx_stor_set_xfer_buf(status, buf_len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - buf_len); - - return TRANSPORT_GOOD; -} - -static int set_chip_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int phy_debug_mode; - int retval; - u16 reg; - - if (!CHECK_PID(chip, 0x5208)) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - phy_debug_mode = (int)(srb->cmnd[3]); - - if (phy_debug_mode) { - chip->phy_debug_mode = 1; - retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - rtsx_disable_bus_int(chip); - - retval = rtsx_read_phy_register(chip, 0x1C, ®); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - reg |= 0x0001; - retval = rtsx_write_phy_register(chip, 0x1C, reg); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - } else { - chip->phy_debug_mode = 0; - retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - rtsx_enable_bus_int(chip); - - retval = rtsx_read_phy_register(chip, 0x1C, ®); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - reg &= 0xFFFE; - retval = rtsx_write_phy_register(chip, 0x1C, reg); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - } - - return TRANSPORT_GOOD; -} - -static int rw_mem_cmd_buf(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval = STATUS_SUCCESS; - unsigned int lun = SCSI_LUN(srb); - u8 cmd_type, mask, value, idx; - u16 addr; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - switch (srb->cmnd[3]) { - case INIT_BATCHCMD: - rtsx_init_cmd(chip); - break; - - case ADD_BATCHCMD: - cmd_type = srb->cmnd[4]; - if (cmd_type > 2) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - addr = (srb->cmnd[5] << 8) | srb->cmnd[6]; - mask = srb->cmnd[7]; - value = srb->cmnd[8]; - rtsx_add_cmd(chip, cmd_type, addr, mask, value); - break; - - case SEND_BATCHCMD: - retval = rtsx_send_cmd(chip, 0, 1000); - break; - - case GET_BATCHRSP: - idx = srb->cmnd[4]; - value = *(rtsx_get_cmd_data(chip) + idx); - if (scsi_bufflen(srb) < 1) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - rtsx_stor_set_xfer_buf(&value, 1, srb); - scsi_set_resid(srb, 0); - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - return TRANSPORT_GOOD; -} - -static int suit_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - switch (srb->cmnd[3]) { - case INIT_BATCHCMD: - case ADD_BATCHCMD: - case SEND_BATCHCMD: - case GET_BATCHRSP: - return rw_mem_cmd_buf(srb, chip); - default: - return TRANSPORT_ERROR; - } -} - -static int read_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr, len, i; - int retval; - u8 *buf; - u16 val; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7]; - - if (len % 2) - len -= len % 2; - - if (len) { - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len / 2; i++) { - retval = rtsx_read_phy_register(chip, addr + i, &val); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type - (chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - buf[2 * i] = (u8)(val >> 8); - buf[2 * i + 1] = (u8)val; - } - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), - len); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - vfree(buf); - } - - return TRANSPORT_GOOD; -} - -static int write_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr, len, i; - int retval; - u8 *buf; - u16 val; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7]; - - if (len % 2) - len -= len % 2; - - if (len) { - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), - len); - - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len / 2; i++) { - val = ((u16)buf[2 * i] << 8) | buf[2 * i + 1]; - retval = rtsx_write_phy_register(chip, addr + i, val); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } - - vfree(buf); - } - - return TRANSPORT_GOOD; -} - -static int erase_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr; - int retval; - u8 mode; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - mode = srb->cmnd[3]; - addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - - if (mode == 0) { - retval = spi_erase_eeprom_chip(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } else if (mode == 1) { - retval = spi_erase_eeprom_byte(chip, addr); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } else { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return TRANSPORT_GOOD; -} - -static int read_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr, len, i; - int retval; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7]; - - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len; i++) { - retval = spi_read_eeprom(chip, addr + i, buf + i); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - } - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int write_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned short addr, len, i; - int retval; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5]; - len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7]; - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len; i++) { - retval = spi_write_eeprom(chip, addr + i, buf[i]); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - return TRANSPORT_FAILED; - } - } - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int read_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - u8 addr, len, i; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = srb->cmnd[4]; - len = srb->cmnd[5]; - - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - for (i = 0; i < len; i++) { - retval = rtsx_read_efuse(chip, addr + i, buf + i); - if (retval != STATUS_SUCCESS) { - vfree(buf); - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - } - - len = (u8)min_t(unsigned int, scsi_bufflen(srb), len); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int write_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval, result = TRANSPORT_GOOD; - u16 val; - u8 addr, len, i; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - addr = srb->cmnd[4]; - len = srb->cmnd[5]; - - len = (u8)min_t(unsigned int, scsi_bufflen(srb), len); - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - retval = rtsx_force_power_on(chip, SSC_PDCTL); - if (retval != STATUS_SUCCESS) { - vfree(buf); - return TRANSPORT_ERROR; - } - - if (chip->asic_code) { - retval = rtsx_read_phy_register(chip, 0x08, &val); - if (retval != STATUS_SUCCESS) { - vfree(buf); - return TRANSPORT_ERROR; - } - - retval = rtsx_write_register(chip, PWR_GATE_CTRL, - LDO3318_PWR_MASK, LDO_OFF); - if (retval != STATUS_SUCCESS) { - vfree(buf); - return TRANSPORT_ERROR; - } - - wait_timeout(600); - - retval = rtsx_write_phy_register(chip, 0x08, - 0x4C00 | chip->phy_voltage); - if (retval != STATUS_SUCCESS) { - vfree(buf); - return TRANSPORT_ERROR; - } - - retval = rtsx_write_register(chip, PWR_GATE_CTRL, - LDO3318_PWR_MASK, LDO_ON); - if (retval != STATUS_SUCCESS) { - vfree(buf); - return TRANSPORT_ERROR; - } - - wait_timeout(600); - } - - retval = card_power_on(chip, SPI_CARD); - if (retval != STATUS_SUCCESS) { - vfree(buf); - return TRANSPORT_ERROR; - } - - wait_timeout(50); - - for (i = 0; i < len; i++) { - retval = rtsx_write_efuse(chip, addr + i, buf[i]); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_WRITE_ERR); - result = TRANSPORT_FAILED; - goto exit; - } - } - -exit: - vfree(buf); - - retval = card_power_off(chip, SPI_CARD); - if (retval != STATUS_SUCCESS) - return TRANSPORT_ERROR; - - if (chip->asic_code) { - retval = rtsx_write_register(chip, PWR_GATE_CTRL, - LDO3318_PWR_MASK, LDO_OFF); - if (retval != STATUS_SUCCESS) - return TRANSPORT_ERROR; - - wait_timeout(600); - - retval = rtsx_write_phy_register(chip, 0x08, val); - if (retval != STATUS_SUCCESS) - return TRANSPORT_ERROR; - - retval = rtsx_write_register(chip, PWR_GATE_CTRL, - LDO3318_PWR_MASK, LDO_ON); - if (retval != STATUS_SUCCESS) - return TRANSPORT_ERROR; - } - - return result; -} - -static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - bool func_max; - u8 func; - u16 addr, len; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - func = srb->cmnd[3]; - addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5]; - len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7]; - - dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x, len = %d\n", - __func__, func, addr, len); - - if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) - func_max = true; - else - func_max = false; - - if (func > func_max) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - retval = rtsx_read_cfg_seq(chip, func, addr, buf, len); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - vfree(buf); - return TRANSPORT_FAILED; - } - - len = (u16)min_t(unsigned int, scsi_bufflen(srb), len); - rtsx_stor_set_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - bool func_max; - u8 func; - u16 addr, len; - u8 *buf; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - func = srb->cmnd[3]; - addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5]; - len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7]; - - dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x\n", - __func__, func, addr); - - if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) - func_max = true; - else - func_max = false; - - if (func > func_max) { - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); - buf = vmalloc(len); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - len); - - retval = rtsx_write_cfg_seq(chip, func, addr, buf, len); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); - vfree(buf); - return TRANSPORT_FAILED; - } - - vfree(buf); - - return TRANSPORT_GOOD; -} - -static int app_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int result; - - switch (srb->cmnd[2]) { - case PP_READ10: - case PP_WRITE10: - result = read_write(srb, chip); - break; - - case READ_HOST_REG: - result = read_host_reg(srb, chip); - break; - - case WRITE_HOST_REG: - result = write_host_reg(srb, chip); - break; - - case GET_VAR: - result = get_variable(srb, chip); - break; - - case SET_VAR: - result = set_variable(srb, chip); - break; - - case DMA_READ: - case DMA_WRITE: - result = dma_access_ring_buffer(srb, chip); - break; - - case READ_PHY: - result = read_phy_register(srb, chip); - break; - - case WRITE_PHY: - result = write_phy_register(srb, chip); - break; - - case ERASE_EEPROM2: - result = erase_eeprom2(srb, chip); - break; - - case READ_EEPROM2: - result = read_eeprom2(srb, chip); - break; - - case WRITE_EEPROM2: - result = write_eeprom2(srb, chip); - break; - - case READ_EFUSE: - result = read_efuse(srb, chip); - break; - - case WRITE_EFUSE: - result = write_efuse(srb, chip); - break; - - case READ_CFG: - result = read_cfg_byte(srb, chip); - break; - - case WRITE_CFG: - result = write_cfg_byte(srb, chip); - break; - - case SET_CHIP_MODE: - result = set_chip_mode(srb, chip); - break; - - case SUIT_CMD: - result = suit_cmd(srb, chip); - break; - - case GET_DEV_STATUS: - result = get_dev_status(srb, chip); - break; - - default: - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return result; -} - -static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - u8 rtsx_status[16]; - int buf_len; - unsigned int lun = SCSI_LUN(srb); - - rtsx_status[0] = (u8)(chip->vendor_id >> 8); - rtsx_status[1] = (u8)(chip->vendor_id); - - rtsx_status[2] = (u8)(chip->product_id >> 8); - rtsx_status[3] = (u8)(chip->product_id); - - rtsx_status[4] = (u8)lun; - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) { - if (chip->lun2card[lun] == SD_CARD) - rtsx_status[5] = 2; - else - rtsx_status[5] = 3; - } else { - if (chip->card_exist) { - if (chip->card_exist & XD_CARD) - rtsx_status[5] = 4; - else if (chip->card_exist & SD_CARD) - rtsx_status[5] = 2; - else if (chip->card_exist & MS_CARD) - rtsx_status[5] = 3; - else - rtsx_status[5] = 7; - } else { - rtsx_status[5] = 7; - } - } - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) - rtsx_status[6] = 2; - else - rtsx_status[6] = 1; - - rtsx_status[7] = (u8)(chip->product_id); - rtsx_status[8] = chip->ic_version; - - if (check_card_exist(chip, lun)) - rtsx_status[9] = 1; - else - rtsx_status[9] = 0; - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) - rtsx_status[10] = 0; - else - rtsx_status[10] = 1; - - if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) { - if (chip->lun2card[lun] == SD_CARD) - rtsx_status[11] = SD_CARD; - else - rtsx_status[11] = MS_CARD; - } else { - rtsx_status[11] = XD_CARD | SD_CARD | MS_CARD; - } - - if (check_card_ready(chip, lun)) - rtsx_status[12] = 1; - else - rtsx_status[12] = 0; - - if (get_lun_card(chip, lun) == XD_CARD) { - rtsx_status[13] = 0x40; - } else if (get_lun_card(chip, lun) == SD_CARD) { - struct sd_info *sd_card = &chip->sd_card; - - rtsx_status[13] = 0x20; - if (CHK_SD(sd_card)) { - if (CHK_SD_HCXC(sd_card)) - rtsx_status[13] |= 0x04; - if (CHK_SD_HS(sd_card)) - rtsx_status[13] |= 0x02; - } else { - rtsx_status[13] |= 0x08; - if (CHK_MMC_52M(sd_card)) - rtsx_status[13] |= 0x02; - if (CHK_MMC_SECTOR_MODE(sd_card)) - rtsx_status[13] |= 0x04; - } - } else if (get_lun_card(chip, lun) == MS_CARD) { - struct ms_info *ms_card = &chip->ms_card; - - if (CHK_MSPRO(ms_card)) { - rtsx_status[13] = 0x38; - if (CHK_HG8BIT(ms_card)) - rtsx_status[13] |= 0x04; -#ifdef SUPPORT_MSXC - if (CHK_MSXC(ms_card)) - rtsx_status[13] |= 0x01; -#endif - } else { - rtsx_status[13] = 0x30; - } - } else { - if (CHECK_LUN_MODE(chip, DEFAULT_SINGLE)) { -#ifdef SUPPORT_SDIO - if (chip->sd_io && chip->sd_int) - rtsx_status[13] = 0x60; - else - rtsx_status[13] = 0x70; -#else - rtsx_status[13] = 0x70; -#endif - } else { - if (chip->lun2card[lun] == SD_CARD) - rtsx_status[13] = 0x20; - else - rtsx_status[13] = 0x30; - } - } - - rtsx_status[14] = 0x78; - if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) - rtsx_status[15] = 0x83; - else - rtsx_status[15] = 0x82; - - buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(rtsx_status)); - rtsx_stor_set_xfer_buf(rtsx_status, buf_len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - buf_len); - - return TRANSPORT_GOOD; -} - -static int get_card_bus_width(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - u8 card, bus_width; - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - - card = get_lun_card(chip, lun); - if (card == SD_CARD || card == MS_CARD) { - bus_width = chip->card_bus_width[lun]; - } else { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return TRANSPORT_FAILED; - } - - scsi_set_resid(srb, 0); - rtsx_stor_set_xfer_buf(&bus_width, scsi_bufflen(srb), srb); - - return TRANSPORT_GOOD; -} - -static int spi_vendor_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int result; - unsigned int lun = SCSI_LUN(srb); - u8 gpio_dir; - - if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - rtsx_force_power_on(chip, SSC_PDCTL); - - rtsx_read_register(chip, CARD_GPIO_DIR, &gpio_dir); - rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir & 0x06); - - switch (srb->cmnd[2]) { - case SCSI_SPI_GETSTATUS: - result = spi_get_status(srb, chip); - break; - - case SCSI_SPI_SETPARAMETER: - result = spi_set_parameter(srb, chip); - break; - - case SCSI_SPI_READFALSHID: - result = spi_read_flash_id(srb, chip); - break; - - case SCSI_SPI_READFLASH: - result = spi_read_flash(srb, chip); - break; - - case SCSI_SPI_WRITEFLASH: - result = spi_write_flash(srb, chip); - break; - - case SCSI_SPI_WRITEFLASHSTATUS: - result = spi_write_flash_status(srb, chip); - break; - - case SCSI_SPI_ERASEFLASH: - result = spi_erase_flash(srb, chip); - break; - - default: - rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir); - - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir); - - if (result != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - return TRANSPORT_GOOD; -} - -static int vendor_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int result; - - switch (srb->cmnd[1]) { - case READ_STATUS: - result = read_status(srb, chip); - break; - - case READ_MEM: - result = read_mem(srb, chip); - break; - - case WRITE_MEM: - result = write_mem(srb, chip); - break; - - case READ_EEPROM: - result = read_eeprom(srb, chip); - break; - - case WRITE_EEPROM: - result = write_eeprom(srb, chip); - break; - - case TOGGLE_GPIO: - result = toggle_gpio_cmd(srb, chip); - break; - - case GET_SD_CSD: - result = get_sd_csd(srb, chip); - break; - - case GET_BUS_WIDTH: - result = get_card_bus_width(srb, chip); - break; - - case SCSI_APP_CMD: - result = app_cmd(srb, chip); - break; - - case SPI_VENDOR_COMMAND: - result = spi_vendor_cmd(srb, chip); - break; - - default: - set_sense_type(chip, SCSI_LUN(srb), - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return result; -} - -#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK) -void led_shine(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - u16 sec_cnt; - - if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) { - sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; - } else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6)) { - sec_cnt = srb->cmnd[4]; - if (sec_cnt == 0) - sec_cnt = 256; - } else { - return; - } - - if (chip->rw_cap[lun] >= GPIO_TOGGLE_THRESHOLD) { - toggle_gpio(chip, LED_GPIO); - chip->rw_cap[lun] = 0; - } else { - chip->rw_cap[lun] += sec_cnt; - } -} -#endif - -static int ms_format_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - unsigned int lun = SCSI_LUN(srb); - bool quick_format; - int retval; - - if (get_lun_card(chip, lun) != MS_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); - return TRANSPORT_FAILED; - } - - if (srb->cmnd[3] != 0x4D || srb->cmnd[4] != 0x47 || - srb->cmnd[5] != 0x66 || srb->cmnd[6] != 0x6D || - srb->cmnd[7] != 0x74) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - - if (!check_card_ready(chip, lun) || - (get_card_size(chip, lun) == 0)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - if (srb->cmnd[8] & 0x01) - quick_format = false; - else - quick_format = true; - - if (!(chip->card_ready & MS_CARD)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - - if (chip->card_wp & MS_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); - return TRANSPORT_FAILED; - } - - if (!CHK_MSPRO(ms_card)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); - return TRANSPORT_FAILED; - } - - retval = mspro_format(srb, chip, MS_SHORT_DATA_LEN, quick_format); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED); - return TRANSPORT_FAILED; - } - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; -} - -#ifdef SUPPORT_PCGL_1P18 -static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - unsigned int lun = SCSI_LUN(srb); - u8 dev_info_id, data_len; - u8 *buf; - unsigned int buf_len; - int i; - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - if (get_lun_card(chip, lun) != MS_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); - return TRANSPORT_FAILED; - } - - if (srb->cmnd[2] != 0xB0 || srb->cmnd[4] != 0x4D || - srb->cmnd[5] != 0x53 || srb->cmnd[6] != 0x49 || - srb->cmnd[7] != 0x44) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - dev_info_id = srb->cmnd[3]; - if ((CHK_MSXC(ms_card) && dev_info_id == 0x10) || - (!CHK_MSXC(ms_card) && dev_info_id == 0x13) || - !CHK_MSPRO(ms_card)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (dev_info_id == 0x15) { - buf_len = 0x3C; - data_len = 0x3A; - } else { - buf_len = 0x6C; - data_len = 0x6A; - } - - buf = kmalloc(buf_len, GFP_KERNEL); - if (!buf) - return TRANSPORT_ERROR; - - i = 0; - /* GET Memory Stick Media Information Response Header */ - buf[i++] = 0x00; /* Data length MSB */ - buf[i++] = data_len; /* Data length LSB */ - /* Device Information Type Code */ - if (CHK_MSXC(ms_card)) - buf[i++] = 0x03; - else - buf[i++] = 0x02; - - /* SGM bit */ - buf[i++] = 0x01; - /* Reserved */ - buf[i++] = 0x00; - buf[i++] = 0x00; - buf[i++] = 0x00; - /* Number of Device Information */ - buf[i++] = 0x01; - - /* Device Information Body */ - - /* Device Information ID Number */ - buf[i++] = dev_info_id; - /* Device Information Length */ - if (dev_info_id == 0x15) - data_len = 0x31; - else - data_len = 0x61; - - buf[i++] = 0x00; /* Data length MSB */ - buf[i++] = data_len; /* Data length LSB */ - /* Valid Bit */ - buf[i++] = 0x80; - if (dev_info_id == 0x10 || dev_info_id == 0x13) { - /* System Information */ - memcpy(buf + i, ms_card->raw_sys_info, 96); - } else { - /* Model Name */ - memcpy(buf + i, ms_card->raw_model_name, 48); - } - - rtsx_stor_set_xfer_buf(buf, buf_len, srb); - scsi_set_resid(srb, scsi_bufflen(srb) - buf_len); - - kfree(buf); - return STATUS_SUCCESS; -} -#endif - -static int ms_sp_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval = TRANSPORT_ERROR; - - if (srb->cmnd[2] == MS_FORMAT) - retval = ms_format_cmnd(srb, chip); -#ifdef SUPPORT_PCGL_1P18 - else if (srb->cmnd[2] == GET_MS_INFORMATION) - retval = get_ms_information(srb, chip); -#endif - - return retval; -} - -#ifdef SUPPORT_CPRM -static int sd_extension_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - unsigned int lun = SCSI_LUN(srb); - int result; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - sd_cleanup_work(chip); - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - if (get_lun_card(chip, lun) != SD_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); - return TRANSPORT_FAILED; - } - - switch (srb->cmnd[0]) { - case SD_PASS_THRU_MODE: - result = sd_pass_thru_mode(srb, chip); - break; - - case SD_EXECUTE_NO_DATA: - result = sd_execute_no_data(srb, chip); - break; - - case SD_EXECUTE_READ: - result = sd_execute_read_data(srb, chip); - break; - - case SD_EXECUTE_WRITE: - result = sd_execute_write_data(srb, chip); - break; - - case SD_GET_RSP: - result = sd_get_cmd_rsp(srb, chip); - break; - - case SD_HW_RST: - result = sd_hw_rst(srb, chip); - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - return result; -} -#endif - -#ifdef SUPPORT_MAGIC_GATE -static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - unsigned int lun = SCSI_LUN(srb); - int retval; - u8 key_format; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - ms_cleanup_work(chip); - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - if (get_lun_card(chip, lun) != MS_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); - return TRANSPORT_FAILED; - } - - if (srb->cmnd[7] != KC_MG_R_PRO) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (!CHK_MSPRO(ms_card)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return TRANSPORT_FAILED; - } - - key_format = srb->cmnd[10] & 0x3F; - dev_dbg(rtsx_dev(chip), "key_format = 0x%x\n", key_format); - - switch (key_format) { - case KF_GET_LOC_EKB: - if ((scsi_bufflen(srb) == 0x41C) && - srb->cmnd[8] == 0x04 && - srb->cmnd[9] == 0x1C) { - retval = mg_get_local_EKB(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - case KF_RSP_CHG: - if ((scsi_bufflen(srb) == 0x24) && - srb->cmnd[8] == 0x00 && - srb->cmnd[9] == 0x24) { - retval = mg_get_rsp_chg(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - case KF_GET_ICV: - ms_card->mg_entry_num = srb->cmnd[5]; - if ((scsi_bufflen(srb) == 0x404) && - srb->cmnd[8] == 0x04 && - srb->cmnd[9] == 0x04 && - srb->cmnd[2] == 0x00 && - srb->cmnd[3] == 0x00 && - srb->cmnd[4] == 0x00 && - srb->cmnd[5] < 32) { - retval = mg_get_ICV(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; -} - -static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct ms_info *ms_card = &chip->ms_card; - unsigned int lun = SCSI_LUN(srb); - int retval; - u8 key_format; - - rtsx_disable_aspm(chip); - - if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) { - rtsx_exit_ss(chip); - wait_timeout(100); - } - rtsx_set_stat(chip, RTSX_STAT_RUN); - - ms_cleanup_work(chip); - - if (!check_card_ready(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return TRANSPORT_FAILED; - } - if (check_card_wp(chip, lun)) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); - return TRANSPORT_FAILED; - } - if (get_lun_card(chip, lun) != MS_CARD) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); - return TRANSPORT_FAILED; - } - - if (srb->cmnd[7] != KC_MG_R_PRO) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (!CHK_MSPRO(ms_card)) { - set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); - return TRANSPORT_FAILED; - } - - key_format = srb->cmnd[10] & 0x3F; - dev_dbg(rtsx_dev(chip), "key_format = 0x%x\n", key_format); - - switch (key_format) { - case KF_SET_LEAF_ID: - if ((scsi_bufflen(srb) == 0x0C) && - srb->cmnd[8] == 0x00 && - srb->cmnd[9] == 0x0C) { - retval = mg_set_leaf_id(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - case KF_CHG_HOST: - if ((scsi_bufflen(srb) == 0x0C) && - srb->cmnd[8] == 0x00 && - srb->cmnd[9] == 0x0C) { - retval = mg_chg(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - case KF_RSP_HOST: - if ((scsi_bufflen(srb) == 0x0C) && - srb->cmnd[8] == 0x00 && - srb->cmnd[9] == 0x0C) { - retval = mg_rsp(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - case KF_SET_ICV: - ms_card->mg_entry_num = srb->cmnd[5]; - if ((scsi_bufflen(srb) == 0x404) && - srb->cmnd[8] == 0x04 && - srb->cmnd[9] == 0x04 && - srb->cmnd[2] == 0x00 && - srb->cmnd[3] == 0x00 && - srb->cmnd[4] == 0x00 && - srb->cmnd[5] < 32) { - retval = mg_set_ICV(srb, chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; -} -#endif - -int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ -#ifdef SUPPORT_SD_LOCK - struct sd_info *sd_card = &chip->sd_card; -#endif - struct ms_info *ms_card = &chip->ms_card; - unsigned int lun = SCSI_LUN(srb); - int result; - -#ifdef SUPPORT_SD_LOCK - if (sd_card->sd_erase_status) { - /* Block all SCSI command except for - * REQUEST_SENSE and rs_ppstatus - */ - if (!(srb->cmnd[0] == VENDOR_CMND && - srb->cmnd[1] == SCSI_APP_CMD && - srb->cmnd[2] == GET_DEV_STATUS) && - srb->cmnd[0] != REQUEST_SENSE) { - /* Logical Unit Not Ready Format in Progress */ - set_sense_data(chip, lun, CUR_ERR, - 0x02, 0, 0x04, 0x04, 0, 0); - return TRANSPORT_FAILED; - } - } -#endif - - if ((get_lun_card(chip, lun) == MS_CARD) && - ms_card->format_status == FORMAT_IN_PROGRESS) { - if (srb->cmnd[0] != REQUEST_SENSE && - srb->cmnd[0] != INQUIRY) { - /* Logical Unit Not Ready Format in Progress */ - set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, - 0, (u16)(ms_card->progress)); - return TRANSPORT_FAILED; - } - } - - switch (srb->cmnd[0]) { - case READ_10: - case WRITE_10: - case READ_6: - case WRITE_6: - result = read_write(srb, chip); -#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK) - led_shine(srb, chip); -#endif - break; - - case TEST_UNIT_READY: - result = test_unit_ready(srb, chip); - break; - - case INQUIRY: - result = inquiry(srb, chip); - break; - - case READ_CAPACITY: - result = read_capacity(srb, chip); - break; - - case START_STOP: - result = start_stop_unit(srb, chip); - break; - - case ALLOW_MEDIUM_REMOVAL: - result = allow_medium_removal(srb, chip); - break; - - case REQUEST_SENSE: - result = request_sense(srb, chip); - break; - - case MODE_SENSE: - case MODE_SENSE_10: - result = mode_sense(srb, chip); - break; - - case 0x23: - result = read_format_capacity(srb, chip); - break; - - case VENDOR_CMND: - result = vendor_cmnd(srb, chip); - break; - - case MS_SP_CMND: - result = ms_sp_cmnd(srb, chip); - break; - -#ifdef SUPPORT_CPRM - case SD_PASS_THRU_MODE: - case SD_EXECUTE_NO_DATA: - case SD_EXECUTE_READ: - case SD_EXECUTE_WRITE: - case SD_GET_RSP: - case SD_HW_RST: - result = sd_extension_cmnd(srb, chip); - break; -#endif - -#ifdef SUPPORT_MAGIC_GATE - case CMD_MSPRO_MG_RKEY: - result = mg_report_key(srb, chip); - break; - - case CMD_MSPRO_MG_SKEY: - result = mg_send_key(srb, chip); - break; -#endif - - case FORMAT_UNIT: - case MODE_SELECT: - case VERIFY: - result = TRANSPORT_GOOD; - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - result = TRANSPORT_FAILED; - } - - return result; -} diff --git a/drivers/staging/rts5208/rtsx_scsi.h b/drivers/staging/rts5208/rtsx_scsi.h deleted file mode 100644 index df6138c97aaad..0000000000000 --- a/drivers/staging/rts5208/rtsx_scsi.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_SCSI_H -#define __REALTEK_RTSX_SCSI_H - -#include "rtsx.h" -#include "rtsx_chip.h" - -#define MS_SP_CMND 0xFA -#define MS_FORMAT 0xA0 -#define GET_MS_INFORMATION 0xB0 - -#define VENDOR_CMND 0xF0 - -#define READ_STATUS 0x09 - -#define READ_EEPROM 0x04 -#define WRITE_EEPROM 0x05 -#define READ_MEM 0x0D -#define WRITE_MEM 0x0E -#define GET_BUS_WIDTH 0x13 -#define GET_SD_CSD 0x14 -#define TOGGLE_GPIO 0x15 -#define TRACE_MSG 0x18 - -#define SCSI_APP_CMD 0x10 - -#define PP_READ10 0x1A -#define PP_WRITE10 0x0A -#define READ_HOST_REG 0x1D -#define WRITE_HOST_REG 0x0D -#define SET_VAR 0x05 -#define GET_VAR 0x15 -#define DMA_READ 0x16 -#define DMA_WRITE 0x06 -#define GET_DEV_STATUS 0x10 -#define SET_CHIP_MODE 0x27 -#define SUIT_CMD 0xE0 -#define WRITE_PHY 0x07 -#define READ_PHY 0x17 -#define WRITE_EEPROM2 0x03 -#define READ_EEPROM2 0x13 -#define ERASE_EEPROM2 0x23 -#define WRITE_EFUSE 0x04 -#define READ_EFUSE 0x14 -#define WRITE_CFG 0x0E -#define READ_CFG 0x1E - -#define SPI_VENDOR_COMMAND 0x1C - -#define SCSI_SPI_GETSTATUS 0x00 -#define SCSI_SPI_SETPARAMETER 0x01 -#define SCSI_SPI_READFALSHID 0x02 -#define SCSI_SPI_READFLASH 0x03 -#define SCSI_SPI_WRITEFLASH 0x04 -#define SCSI_SPI_WRITEFLASHSTATUS 0x05 -#define SCSI_SPI_ERASEFLASH 0x06 - -#define INIT_BATCHCMD 0x41 -#define ADD_BATCHCMD 0x42 -#define SEND_BATCHCMD 0x43 -#define GET_BATCHRSP 0x44 - -#define CHIP_NORMALMODE 0x00 -#define CHIP_DEBUGMODE 0x01 - -/* SD Pass Through Command Extension */ -#define SD_PASS_THRU_MODE 0xD0 -#define SD_EXECUTE_NO_DATA 0xD1 -#define SD_EXECUTE_READ 0xD2 -#define SD_EXECUTE_WRITE 0xD3 -#define SD_GET_RSP 0xD4 -#define SD_HW_RST 0xD6 - -#ifdef SUPPORT_MAGIC_GATE -#define CMD_MSPRO_MG_RKEY 0xA4 /* Report Key Command */ -#define CMD_MSPRO_MG_SKEY 0xA3 /* Send Key Command */ - -/* CBWCB field: key class */ -#define KC_MG_R_PRO 0xBE /* MG-R PRO*/ - -/* CBWCB field: key format */ -#define KF_SET_LEAF_ID 0x31 /* Set Leaf ID */ -#define KF_GET_LOC_EKB 0x32 /* Get Local EKB */ -#define KF_CHG_HOST 0x33 /* Challenge (host) */ -#define KF_RSP_CHG 0x34 /* Response and Challenge (device) */ -#define KF_RSP_HOST 0x35 /* Response (host) */ -#define KF_GET_ICV 0x36 /* Get ICV */ -#define KF_SET_ICV 0x37 /* SSet ICV */ -#endif - -/* Sense type */ -#define SENSE_TYPE_NO_SENSE 0 -#define SENSE_TYPE_MEDIA_CHANGE 1 -#define SENSE_TYPE_MEDIA_NOT_PRESENT 2 -#define SENSE_TYPE_MEDIA_LBA_OVER_RANGE 3 -#define SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT 4 -#define SENSE_TYPE_MEDIA_WRITE_PROTECT 5 -#define SENSE_TYPE_MEDIA_INVALID_CMD_FIELD 6 -#define SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR 7 -#define SENSE_TYPE_MEDIA_WRITE_ERR 8 -#define SENSE_TYPE_FORMAT_IN_PROGRESS 9 -#define SENSE_TYPE_FORMAT_CMD_FAILED 10 -#ifdef SUPPORT_MAGIC_GATE -#define SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB 0x0b -#define SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN 0x0c -#define SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM 0x0d -#define SENSE_TYPE_MG_WRITE_ERR 0x0e -#endif -#ifdef SUPPORT_SD_LOCK -/* FOR Locked SD card*/ -#define SENSE_TYPE_MEDIA_READ_FORBIDDEN 0x10 -#endif - -void scsi_show_command(struct rtsx_chip *chip); -void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type); -void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code, - u8 sense_key, u32 info, u8 asc, u8 ascq, - u8 sns_key_info0, u16 sns_key_info1); -int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip); - -#endif /* __REALTEK_RTSX_SCSI_H */ diff --git a/drivers/staging/rts5208/rtsx_sys.h b/drivers/staging/rts5208/rtsx_sys.h deleted file mode 100644 index 77094809c8145..0000000000000 --- a/drivers/staging/rts5208/rtsx_sys.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __RTSX_SYS_H -#define __RTSX_SYS_H - -#include "rtsx.h" -#include "rtsx_chip.h" -#include "rtsx_card.h" - -static inline void rtsx_exclusive_enter_ss(struct rtsx_chip *chip) -{ - struct rtsx_dev *dev = chip->rtsx; - - spin_lock(&dev->reg_lock); - rtsx_enter_ss(chip); - spin_unlock(&dev->reg_lock); -} - -static inline void rtsx_reset_detected_cards(struct rtsx_chip *chip, int flag) -{ - rtsx_reset_cards(chip); -} - -#define RTSX_MSG_IN_INT(x) - -#endif /* __RTSX_SYS_H */ - diff --git a/drivers/staging/rts5208/rtsx_transport.c b/drivers/staging/rts5208/rtsx_transport.c deleted file mode 100644 index d5ad49de4c568..0000000000000 --- a/drivers/staging/rts5208/rtsx_transport.c +++ /dev/null @@ -1,768 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include - -#include "rtsx.h" - -/*********************************************************************** - * Scatter-gather transfer buffer access routines - ***********************************************************************/ - -/* - * Copy a buffer of length buflen to/from the srb's transfer buffer. - * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer - * points to a list of s-g entries and we ignore srb->request_bufflen. - * For non-scatter-gather transfers, srb->request_buffer points to the - * transfer buffer itself and srb->request_bufflen is the buffer's length.) - * Update the *index and *offset variables so that the next copy will - * pick up from where this one left off. - */ - -unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer, - unsigned int buflen, - struct scsi_cmnd *srb, - unsigned int *index, - unsigned int *offset, - enum xfer_buf_dir dir) -{ - unsigned int cnt; - - /* If not using scatter-gather, just transfer the data directly. */ - if (scsi_sg_count(srb) == 0) { - unsigned char *sgbuffer; - - if (*offset >= scsi_bufflen(srb)) - return 0; - cnt = min(buflen, scsi_bufflen(srb) - *offset); - - sgbuffer = (unsigned char *)scsi_sglist(srb) + *offset; - - if (dir == TO_XFER_BUF) - memcpy(sgbuffer, buffer, cnt); - else - memcpy(buffer, sgbuffer, cnt); - *offset += cnt; - - /* - * Using scatter-gather. We have to go through the list one entry - * at a time. Each s-g entry contains some number of pages which - * have to be copied one at a time. - */ - } else { - struct scatterlist *sg = - (struct scatterlist *)scsi_sglist(srb) - + *index; - - /* - * This loop handles a single s-g list entry, which may - * include multiple pages. Find the initial page structure - * and the starting offset within the page, and update - * the *offset and *index values for the next loop. - */ - cnt = 0; - while (cnt < buflen && *index < scsi_sg_count(srb)) { - struct page *page = sg_page(sg) + - ((sg->offset + *offset) >> PAGE_SHIFT); - unsigned int poff = (sg->offset + *offset) & - (PAGE_SIZE - 1); - unsigned int sglen = sg->length - *offset; - - if (sglen > buflen - cnt) { - /* Transfer ends within this s-g entry */ - sglen = buflen - cnt; - *offset += sglen; - } else { - /* Transfer continues to next s-g entry */ - *offset = 0; - ++*index; - ++sg; - } - - while (sglen > 0) { - unsigned int plen = min(sglen, (unsigned int) - PAGE_SIZE - poff); - - if (dir == TO_XFER_BUF) - memcpy_to_page(page, poff, buffer + cnt, plen); - else - memcpy_from_page(buffer + cnt, page, poff, plen); - - /* Start at the beginning of the next page */ - poff = 0; - ++page; - cnt += plen; - sglen -= plen; - } - } - } - - /* Return the amount actually transferred */ - return cnt; -} - -/* - * Store the contents of buffer into srb's transfer buffer and set the - * SCSI residue. - */ -void rtsx_stor_set_xfer_buf(unsigned char *buffer, - unsigned int buflen, struct scsi_cmnd *srb) -{ - unsigned int index = 0, offset = 0; - - rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset, - TO_XFER_BUF); - if (buflen < scsi_bufflen(srb)) - scsi_set_resid(srb, scsi_bufflen(srb) - buflen); -} - -void rtsx_stor_get_xfer_buf(unsigned char *buffer, - unsigned int buflen, struct scsi_cmnd *srb) -{ - unsigned int index = 0, offset = 0; - - rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset, - FROM_XFER_BUF); - if (buflen < scsi_bufflen(srb)) - scsi_set_resid(srb, scsi_bufflen(srb) - buflen); -} - -/*********************************************************************** - * Transport routines - ***********************************************************************/ - -/* - * Invoke the transport and basic error-handling/recovery methods - * - * This is used to send the message to the device and receive the response. - */ -void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int result; - - result = rtsx_scsi_handler(srb, chip); - - /* - * if the command gets aborted by the higher layers, we need to - * short-circuit all other processing. - */ - if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) { - dev_dbg(rtsx_dev(chip), "-- command was aborted\n"); - srb->result = DID_ABORT << 16; - goto handle_errors; - } - - /* if there is a transport error, reset and don't auto-sense */ - if (result == TRANSPORT_ERROR) { - dev_dbg(rtsx_dev(chip), "-- transport indicates error, resetting\n"); - srb->result = DID_ERROR << 16; - goto handle_errors; - } - - srb->result = SAM_STAT_GOOD; - - /* - * If we have a failure, we're going to do a REQUEST_SENSE - * automatically. Note that we differentiate between a command - * "failure" and an "error" in the transport mechanism. - */ - if (result == TRANSPORT_FAILED) { - /* set the result so the higher layers expect this data */ - srb->result = SAM_STAT_CHECK_CONDITION; - memcpy(srb->sense_buffer, - (unsigned char *)&chip->sense_buffer[SCSI_LUN(srb)], - sizeof(struct sense_data_t)); - } - - return; - -handle_errors: - return; -} - -void rtsx_add_cmd(struct rtsx_chip *chip, - u8 cmd_type, u16 reg_addr, u8 mask, u8 data) -{ - __le32 *cb = (__le32 *)(chip->host_cmds_ptr); - u32 val = 0; - - val |= (u32)(cmd_type & 0x03) << 30; - val |= (u32)(reg_addr & 0x3FFF) << 16; - val |= (u32)mask << 8; - val |= (u32)data; - - spin_lock_irq(&chip->rtsx->reg_lock); - if (chip->ci < (HOST_CMDS_BUF_LEN / 4)) - cb[(chip->ci)++] = cpu_to_le32(val); - - spin_unlock_irq(&chip->rtsx->reg_lock); -} - -void rtsx_send_cmd_no_wait(struct rtsx_chip *chip) -{ - u32 val = BIT(31); - - rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr); - - val |= (u32)(chip->ci * 4) & 0x00FFFFFF; - /* Hardware Auto Response */ - val |= 0x40000000; - rtsx_writel(chip, RTSX_HCBCTLR, val); -} - -int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout) -{ - struct rtsx_dev *rtsx = chip->rtsx; - struct completion trans_done; - u32 val = BIT(31); - long timeleft; - int err = 0; - - if (card == SD_CARD) - rtsx->check_card_cd = SD_EXIST; - else if (card == MS_CARD) - rtsx->check_card_cd = MS_EXIST; - else if (card == XD_CARD) - rtsx->check_card_cd = XD_EXIST; - else - rtsx->check_card_cd = 0; - - spin_lock_irq(&rtsx->reg_lock); - - /* set up data structures for the wakeup system */ - rtsx->done = &trans_done; - rtsx->trans_result = TRANS_NOT_READY; - init_completion(&trans_done); - rtsx->trans_state = STATE_TRANS_CMD; - - rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr); - - val |= (u32)(chip->ci * 4) & 0x00FFFFFF; - /* Hardware Auto Response */ - val |= 0x40000000; - rtsx_writel(chip, RTSX_HCBCTLR, val); - - spin_unlock_irq(&rtsx->reg_lock); - - /* Wait for TRANS_OK_INT */ - timeleft = wait_for_completion_interruptible_timeout(&trans_done, - msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", - chip->int_reg); - err = -ETIMEDOUT; - goto finish_send_cmd; - } - - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_RESULT_FAIL) - err = -EIO; - else if (rtsx->trans_result == TRANS_RESULT_OK) - err = 0; - - spin_unlock_irq(&rtsx->reg_lock); - -finish_send_cmd: - rtsx->done = NULL; - rtsx->trans_state = STATE_TRANS_NONE; - - if (err < 0) - rtsx_stop_cmd(chip, card); - - return err; -} - -static inline void rtsx_add_sg_tbl(struct rtsx_chip *chip, - u32 addr, u32 len, u8 option) -{ - __le64 *sgb = (__le64 *)(chip->host_sg_tbl_ptr); - u64 val = 0; - u32 temp_len = 0; - u8 temp_opt = 0; - - do { - if (len > 0x80000) { - temp_len = 0x80000; - temp_opt = option & (~RTSX_SG_END); - } else { - temp_len = len; - temp_opt = option; - } - val = ((u64)addr << 32) | ((u64)temp_len << 12) | temp_opt; - - if (chip->sgi < (HOST_SG_TBL_BUF_LEN / 8)) - sgb[(chip->sgi)++] = cpu_to_le64(val); - - len -= temp_len; - addr += temp_len; - } while (len); -} - -static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card, - struct scatterlist *sg, int num_sg, - unsigned int *index, - unsigned int *offset, int size, - enum dma_data_direction dma_dir, - int timeout) -{ - struct rtsx_dev *rtsx = chip->rtsx; - struct completion trans_done; - u8 dir; - int sg_cnt, i, resid; - int err = 0; - long timeleft; - struct scatterlist *sg_ptr; - u32 val = TRIG_DMA; - - if (!sg || num_sg <= 0 || !offset || !index) - return -EIO; - - if (dma_dir == DMA_TO_DEVICE) - dir = HOST_TO_DEVICE; - else if (dma_dir == DMA_FROM_DEVICE) - dir = DEVICE_TO_HOST; - else - return -ENXIO; - - if (card == SD_CARD) - rtsx->check_card_cd = SD_EXIST; - else if (card == MS_CARD) - rtsx->check_card_cd = MS_EXIST; - else if (card == XD_CARD) - rtsx->check_card_cd = XD_EXIST; - else - rtsx->check_card_cd = 0; - - spin_lock_irq(&rtsx->reg_lock); - - /* set up data structures for the wakeup system */ - rtsx->done = &trans_done; - - rtsx->trans_state = STATE_TRANS_SG; - rtsx->trans_result = TRANS_NOT_READY; - - spin_unlock_irq(&rtsx->reg_lock); - - sg_cnt = dma_map_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); - - resid = size; - sg_ptr = sg; - chip->sgi = 0; - /* - * Usually the next entry will be @sg@ + 1, but if this sg element - * is part of a chained scatterlist, it could jump to the start of - * a new scatterlist array. So here we use sg_next to move to - * the proper sg. - */ - for (i = 0; i < *index; i++) - sg_ptr = sg_next(sg_ptr); - for (i = *index; i < sg_cnt; i++) { - dma_addr_t addr; - unsigned int len; - u8 option; - - addr = sg_dma_address(sg_ptr); - len = sg_dma_len(sg_ptr); - - dev_dbg(rtsx_dev(chip), "DMA addr: 0x%x, Len: 0x%x\n", - (unsigned int)addr, len); - dev_dbg(rtsx_dev(chip), "*index = %d, *offset = %d\n", - *index, *offset); - - addr += *offset; - - if ((len - *offset) > resid) { - *offset += resid; - len = resid; - resid = 0; - } else { - resid -= (len - *offset); - len -= *offset; - *offset = 0; - *index = *index + 1; - } - option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA; - if ((i == sg_cnt - 1) || !resid) - option |= RTSX_SG_END; - - rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option); - - if (!resid) - break; - - sg_ptr = sg_next(sg_ptr); - } - - dev_dbg(rtsx_dev(chip), "SG table count = %d\n", chip->sgi); - - val |= (u32)(dir & 0x01) << 29; - val |= ADMA_MODE; - - spin_lock_irq(&rtsx->reg_lock); - - init_completion(&trans_done); - - rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr); - rtsx_writel(chip, RTSX_HDBCTLR, val); - - spin_unlock_irq(&rtsx->reg_lock); - - timeleft = wait_for_completion_interruptible_timeout(&trans_done, - msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", - __func__, __LINE__); - dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", - chip->int_reg); - err = -ETIMEDOUT; - goto out; - } - - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_RESULT_FAIL) { - err = -EIO; - spin_unlock_irq(&rtsx->reg_lock); - goto out; - } - spin_unlock_irq(&rtsx->reg_lock); - - /* Wait for TRANS_OK_INT */ - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_NOT_READY) { - init_completion(&trans_done); - spin_unlock_irq(&rtsx->reg_lock); - timeleft = wait_for_completion_interruptible_timeout(&trans_done, - msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", - __func__, __LINE__); - dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", - chip->int_reg); - err = -ETIMEDOUT; - goto out; - } - } else { - spin_unlock_irq(&rtsx->reg_lock); - } - - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_RESULT_FAIL) - err = -EIO; - else if (rtsx->trans_result == TRANS_RESULT_OK) - err = 0; - - spin_unlock_irq(&rtsx->reg_lock); - -out: - rtsx->done = NULL; - rtsx->trans_state = STATE_TRANS_NONE; - dma_unmap_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); - - if (err < 0) - rtsx_stop_cmd(chip, card); - - return err; -} - -static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card, - struct scatterlist *sg, int num_sg, - enum dma_data_direction dma_dir, - int timeout) -{ - struct rtsx_dev *rtsx = chip->rtsx; - struct completion trans_done; - u8 dir; - int buf_cnt, i; - int err = 0; - long timeleft; - struct scatterlist *sg_ptr; - - if (!sg || num_sg <= 0) - return -EIO; - - if (dma_dir == DMA_TO_DEVICE) - dir = HOST_TO_DEVICE; - else if (dma_dir == DMA_FROM_DEVICE) - dir = DEVICE_TO_HOST; - else - return -ENXIO; - - if (card == SD_CARD) - rtsx->check_card_cd = SD_EXIST; - else if (card == MS_CARD) - rtsx->check_card_cd = MS_EXIST; - else if (card == XD_CARD) - rtsx->check_card_cd = XD_EXIST; - else - rtsx->check_card_cd = 0; - - spin_lock_irq(&rtsx->reg_lock); - - /* set up data structures for the wakeup system */ - rtsx->done = &trans_done; - - rtsx->trans_state = STATE_TRANS_SG; - rtsx->trans_result = TRANS_NOT_READY; - - spin_unlock_irq(&rtsx->reg_lock); - - buf_cnt = dma_map_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); - - sg_ptr = sg; - - for (i = 0; i <= buf_cnt / (HOST_SG_TBL_BUF_LEN / 8); i++) { - u32 val = TRIG_DMA; - int sg_cnt, j; - - if (i == buf_cnt / (HOST_SG_TBL_BUF_LEN / 8)) - sg_cnt = buf_cnt % (HOST_SG_TBL_BUF_LEN / 8); - else - sg_cnt = HOST_SG_TBL_BUF_LEN / 8; - - chip->sgi = 0; - for (j = 0; j < sg_cnt; j++) { - dma_addr_t addr = sg_dma_address(sg_ptr); - unsigned int len = sg_dma_len(sg_ptr); - u8 option; - - dev_dbg(rtsx_dev(chip), "DMA addr: 0x%x, Len: 0x%x\n", - (unsigned int)addr, len); - - option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA; - if (j == (sg_cnt - 1)) - option |= RTSX_SG_END; - - rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option); - - sg_ptr = sg_next(sg_ptr); - } - - dev_dbg(rtsx_dev(chip), "SG table count = %d\n", chip->sgi); - - val |= (u32)(dir & 0x01) << 29; - val |= ADMA_MODE; - - spin_lock_irq(&rtsx->reg_lock); - - init_completion(&trans_done); - - rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr); - rtsx_writel(chip, RTSX_HDBCTLR, val); - - spin_unlock_irq(&rtsx->reg_lock); - - timeleft = wait_for_completion_interruptible_timeout(&trans_done, - msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", - __func__, __LINE__); - dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", - chip->int_reg); - err = -ETIMEDOUT; - goto out; - } - - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_RESULT_FAIL) { - err = -EIO; - spin_unlock_irq(&rtsx->reg_lock); - goto out; - } - spin_unlock_irq(&rtsx->reg_lock); - - sg_ptr += sg_cnt; - } - - /* Wait for TRANS_OK_INT */ - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_NOT_READY) { - init_completion(&trans_done); - spin_unlock_irq(&rtsx->reg_lock); - timeleft = wait_for_completion_interruptible_timeout(&trans_done, - msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", - __func__, __LINE__); - dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", - chip->int_reg); - err = -ETIMEDOUT; - goto out; - } - } else { - spin_unlock_irq(&rtsx->reg_lock); - } - - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_RESULT_FAIL) - err = -EIO; - else if (rtsx->trans_result == TRANS_RESULT_OK) - err = 0; - - spin_unlock_irq(&rtsx->reg_lock); - -out: - rtsx->done = NULL; - rtsx->trans_state = STATE_TRANS_NONE; - dma_unmap_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); - - if (err < 0) - rtsx_stop_cmd(chip, card); - - return err; -} - -static int rtsx_transfer_buf(struct rtsx_chip *chip, u8 card, void *buf, - size_t len, enum dma_data_direction dma_dir, - int timeout) -{ - struct rtsx_dev *rtsx = chip->rtsx; - struct completion trans_done; - dma_addr_t addr; - u8 dir; - int err = 0; - u32 val = BIT(31); - long timeleft; - - if (!buf || len <= 0) - return -EIO; - - if (dma_dir == DMA_TO_DEVICE) - dir = HOST_TO_DEVICE; - else if (dma_dir == DMA_FROM_DEVICE) - dir = DEVICE_TO_HOST; - else - return -ENXIO; - - addr = dma_map_single(&rtsx->pci->dev, buf, len, dma_dir); - if (dma_mapping_error(&rtsx->pci->dev, addr)) - return -ENOMEM; - - if (card == SD_CARD) - rtsx->check_card_cd = SD_EXIST; - else if (card == MS_CARD) - rtsx->check_card_cd = MS_EXIST; - else if (card == XD_CARD) - rtsx->check_card_cd = XD_EXIST; - else - rtsx->check_card_cd = 0; - - val |= (u32)(dir & 0x01) << 29; - val |= (u32)(len & 0x00FFFFFF); - - spin_lock_irq(&rtsx->reg_lock); - - /* set up data structures for the wakeup system */ - rtsx->done = &trans_done; - - init_completion(&trans_done); - - rtsx->trans_state = STATE_TRANS_BUF; - rtsx->trans_result = TRANS_NOT_READY; - - rtsx_writel(chip, RTSX_HDBAR, addr); - rtsx_writel(chip, RTSX_HDBCTLR, val); - - spin_unlock_irq(&rtsx->reg_lock); - - /* Wait for TRANS_OK_INT */ - timeleft = wait_for_completion_interruptible_timeout(&trans_done, - msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", - __func__, __LINE__); - dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", - chip->int_reg); - err = -ETIMEDOUT; - goto out; - } - - spin_lock_irq(&rtsx->reg_lock); - if (rtsx->trans_result == TRANS_RESULT_FAIL) - err = -EIO; - else if (rtsx->trans_result == TRANS_RESULT_OK) - err = 0; - - spin_unlock_irq(&rtsx->reg_lock); - -out: - rtsx->done = NULL; - rtsx->trans_state = STATE_TRANS_NONE; - dma_unmap_single(&rtsx->pci->dev, addr, len, dma_dir); - - if (err < 0) - rtsx_stop_cmd(chip, card); - - return err; -} - -int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card, - void *buf, size_t len, int use_sg, - unsigned int *index, unsigned int *offset, - enum dma_data_direction dma_dir, int timeout) -{ - int err = 0; - - /* don't transfer data during abort processing */ - if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) - return -EIO; - - if (use_sg) { - struct scatterlist *sg = buf; - - err = rtsx_transfer_sglist_adma_partial(chip, card, sg, use_sg, - index, offset, (int)len, - dma_dir, timeout); - } else { - err = rtsx_transfer_buf(chip, card, - buf, len, dma_dir, timeout); - } - if (err < 0) { - if (RTSX_TST_DELINK(chip)) { - RTSX_CLR_DELINK(chip); - chip->need_reinit = SD_CARD | MS_CARD | XD_CARD; - rtsx_reinit_cards(chip, 1); - } - } - - return err; -} - -int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len, - int use_sg, enum dma_data_direction dma_dir, int timeout) -{ - int err = 0; - - dev_dbg(rtsx_dev(chip), "use_sg = %d\n", use_sg); - - /* don't transfer data during abort processing */ - if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) - return -EIO; - - if (use_sg) { - err = rtsx_transfer_sglist_adma(chip, card, buf, - use_sg, dma_dir, timeout); - } else { - err = rtsx_transfer_buf(chip, card, buf, len, dma_dir, timeout); - } - - if (err < 0) { - if (RTSX_TST_DELINK(chip)) { - RTSX_CLR_DELINK(chip); - chip->need_reinit = SD_CARD | MS_CARD | XD_CARD; - rtsx_reinit_cards(chip, 1); - } - } - - return err; -} - diff --git a/drivers/staging/rts5208/rtsx_transport.h b/drivers/staging/rts5208/rtsx_transport.h deleted file mode 100644 index 097efed24b798..0000000000000 --- a/drivers/staging/rts5208/rtsx_transport.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_TRANSPORT_H -#define __REALTEK_RTSX_TRANSPORT_H - -#include "rtsx.h" -#include "rtsx_chip.h" - -#define WAIT_TIME 2000 - -unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer, - unsigned int buflen, - struct scsi_cmnd *srb, - unsigned int *index, - unsigned int *offset, - enum xfer_buf_dir dir); -void rtsx_stor_set_xfer_buf(unsigned char *buffer, unsigned int buflen, - struct scsi_cmnd *srb); -void rtsx_stor_get_xfer_buf(unsigned char *buffer, unsigned int buflen, - struct scsi_cmnd *srb); -void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip); - -#define rtsx_init_cmd(chip) ((chip)->ci = 0) - -void rtsx_add_cmd(struct rtsx_chip *chip, u8 cmd_type, u16 reg_addr, u8 mask, - u8 data); -void rtsx_send_cmd_no_wait(struct rtsx_chip *chip); -int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout); - -static inline u8 *rtsx_get_cmd_data(struct rtsx_chip *chip) -{ -#ifdef CMD_USING_SG - return (u8 *)(chip->host_sg_tbl_ptr); -#else - return (u8 *)(chip->host_cmds_ptr); -#endif -} - -int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len, - int use_sg, enum dma_data_direction dma_dir, - int timeout); - -int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card, void *buf, - size_t len, int use_sg, unsigned int *index, - unsigned int *offset, - enum dma_data_direction dma_dir, int timeout); - -#endif /* __REALTEK_RTSX_TRANSPORT_H */ diff --git a/drivers/staging/rts5208/sd.c b/drivers/staging/rts5208/sd.c deleted file mode 100644 index 74c4f476b3a4a..0000000000000 --- a/drivers/staging/rts5208/sd.c +++ /dev/null @@ -1,4717 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include - -#include "rtsx.h" -#include "sd.h" - -#define SD_MAX_RETRY_COUNT 3 - -static u16 REG_SD_CFG1; -static u16 REG_SD_CFG2; -static u16 REG_SD_CFG3; -static u16 REG_SD_STAT1; -static u16 REG_SD_STAT2; -static u16 REG_SD_BUS_STAT; -static u16 REG_SD_PAD_CTL; -static u16 REG_SD_SAMPLE_POINT_CTL; -static u16 REG_SD_PUSH_POINT_CTL; -static u16 REG_SD_CMD0; -static u16 REG_SD_CMD1; -static u16 REG_SD_CMD2; -static u16 REG_SD_CMD3; -static u16 REG_SD_CMD4; -static u16 REG_SD_CMD5; -static u16 REG_SD_BYTE_CNT_L; -static u16 REG_SD_BYTE_CNT_H; -static u16 REG_SD_BLOCK_CNT_L; -static u16 REG_SD_BLOCK_CNT_H; -static u16 REG_SD_TRANSFER; -static u16 REG_SD_VPCLK0_CTL; -static u16 REG_SD_VPCLK1_CTL; -static u16 REG_SD_DCMPS0_CTL; -static u16 REG_SD_DCMPS1_CTL; - -static inline void sd_set_err_code(struct rtsx_chip *chip, u8 err_code) -{ - struct sd_info *sd_card = &chip->sd_card; - - sd_card->err_code |= err_code; -} - -static inline void sd_clr_err_code(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - - sd_card->err_code = 0; -} - -static inline int sd_check_err_code(struct rtsx_chip *chip, u8 err_code) -{ - struct sd_info *sd_card = &chip->sd_card; - - return sd_card->err_code & err_code; -} - -static void sd_init_reg_addr(struct rtsx_chip *chip) -{ - REG_SD_CFG1 = 0xFD31; - REG_SD_CFG2 = 0xFD33; - REG_SD_CFG3 = 0xFD3E; - REG_SD_STAT1 = 0xFD30; - REG_SD_STAT2 = 0; - REG_SD_BUS_STAT = 0; - REG_SD_PAD_CTL = 0; - REG_SD_SAMPLE_POINT_CTL = 0; - REG_SD_PUSH_POINT_CTL = 0; - REG_SD_CMD0 = 0xFD34; - REG_SD_CMD1 = 0xFD35; - REG_SD_CMD2 = 0xFD36; - REG_SD_CMD3 = 0xFD37; - REG_SD_CMD4 = 0xFD38; - REG_SD_CMD5 = 0xFD5A; - REG_SD_BYTE_CNT_L = 0xFD39; - REG_SD_BYTE_CNT_H = 0xFD3A; - REG_SD_BLOCK_CNT_L = 0xFD3B; - REG_SD_BLOCK_CNT_H = 0xFD3C; - REG_SD_TRANSFER = 0xFD32; - REG_SD_VPCLK0_CTL = 0; - REG_SD_VPCLK1_CTL = 0; - REG_SD_DCMPS0_CTL = 0; - REG_SD_DCMPS1_CTL = 0; -} - -static int sd_check_data0_status(struct rtsx_chip *chip) -{ - int retval; - u8 stat; - - retval = rtsx_read_register(chip, REG_SD_STAT1, &stat); - if (retval) - return retval; - - if (!(stat & SD_DAT0_STATUS)) { - sd_set_err_code(chip, SD_BUSY); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx, - u32 arg, u8 rsp_type, u8 *rsp, int rsp_len) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int timeout = 100; - u16 reg_addr; - u8 *ptr; - int stat_idx = 0; - int rty_cnt = 0; - - sd_clr_err_code(chip); - - dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d, arg = 0x%08x\n", cmd_idx, arg); - - if (rsp_type == SD_RSP_TYPE_R1b) - timeout = 3000; - -RTY_SEND_CMD: - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | cmd_idx); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, (u8)(arg >> 24)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, (u8)(arg >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, (u8)(arg >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, (u8)arg); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, - 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END | SD_STAT_IDLE, SD_TRANSFER_END | - SD_STAT_IDLE); - - if (rsp_type == SD_RSP_TYPE_R2) { - for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0); - - stat_idx = 16; - } else if (rsp_type != SD_RSP_TYPE_R0) { - for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0); - - stat_idx = 5; - } - - rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_STAT1, 0, 0); - - retval = rtsx_send_cmd(chip, SD_CARD, timeout); - if (retval < 0) { - u8 val; - - rtsx_read_register(chip, REG_SD_STAT1, &val); - dev_dbg(rtsx_dev(chip), "SD_STAT1: 0x%x\n", val); - - rtsx_read_register(chip, REG_SD_CFG3, &val); - dev_dbg(rtsx_dev(chip), "SD_CFG3: 0x%x\n", val); - - if (retval == -ETIMEDOUT) { - if (rsp_type & SD_WAIT_BUSY_END) { - retval = sd_check_data0_status(chip); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - return retval; - } - } else { - sd_set_err_code(chip, SD_TO_ERR); - } - retval = STATUS_TIMEDOUT; - } else { - retval = STATUS_FAIL; - } - rtsx_clear_sd_error(chip); - - return retval; - } - - if (rsp_type == SD_RSP_TYPE_R0) - return STATUS_SUCCESS; - - ptr = rtsx_get_cmd_data(chip) + 1; - - if ((ptr[0] & 0xC0) != 0) { - sd_set_err_code(chip, SD_STS_ERR); - return STATUS_FAIL; - } - - if (!(rsp_type & SD_NO_CHECK_CRC7)) { - if (ptr[stat_idx] & SD_CRC7_ERR) { - if (cmd_idx == WRITE_MULTIPLE_BLOCK) { - sd_set_err_code(chip, SD_CRC_ERR); - return STATUS_FAIL; - } - if (rty_cnt < SD_MAX_RETRY_COUNT) { - wait_timeout(20); - rty_cnt++; - goto RTY_SEND_CMD; - } else { - sd_set_err_code(chip, SD_CRC_ERR); - return STATUS_FAIL; - } - } - } - - if (rsp_type == SD_RSP_TYPE_R1 || rsp_type == SD_RSP_TYPE_R1b) { - if (cmd_idx != SEND_RELATIVE_ADDR && - cmd_idx != SEND_IF_COND) { - if (cmd_idx != STOP_TRANSMISSION) { - if (ptr[1] & 0x80) - return STATUS_FAIL; - } -#ifdef SUPPORT_SD_LOCK - if (ptr[1] & 0x7D) { -#else - if (ptr[1] & 0x7F) { -#endif - dev_dbg(rtsx_dev(chip), "ptr[1]: 0x%02x\n", - ptr[1]); - return STATUS_FAIL; - } - if (ptr[2] & 0xFF) { - dev_dbg(rtsx_dev(chip), "ptr[2]: 0x%02x\n", - ptr[2]); - return STATUS_FAIL; - } - if (ptr[3] & 0x80) { - dev_dbg(rtsx_dev(chip), "ptr[3]: 0x%02x\n", - ptr[3]); - return STATUS_FAIL; - } - if (ptr[3] & 0x01) - sd_card->sd_data_buf_ready = 1; - else - sd_card->sd_data_buf_ready = 0; - } - } - - if (rsp && rsp_len) - memcpy(rsp, ptr, rsp_len); - - return STATUS_SUCCESS; -} - -static int sd_read_data(struct rtsx_chip *chip, - u8 trans_mode, u8 *cmd, int cmd_len, u16 byte_cnt, - u16 blk_cnt, u8 bus_width, u8 *buf, int buf_len, - int timeout) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i; - - sd_clr_err_code(chip); - - if (!buf) - buf_len = 0; - - if (buf_len > 512) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - if (cmd_len) { - dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", cmd[0] - 0x40); - for (i = 0; i < (min(cmd_len, 6)); i++) - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0 + i, - 0xFF, cmd[i]); - } - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, - (u8)byte_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, - (u8)(byte_cnt >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, - (u8)blk_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, - (u8)(blk_cnt >> 8)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | - SD_CHECK_CRC7 | SD_RSP_LEN_6); - if (trans_mode != SD_TM_AUTO_TUNING) - rtsx_add_cmd(chip, WRITE_REG_CMD, - CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - trans_mode | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, - SD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, SD_CARD, timeout); - if (retval < 0) { - if (retval == -ETIMEDOUT) { - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - } - - return STATUS_FAIL; - } - - if (buf && buf_len) { - retval = rtsx_read_ppbuf(chip, buf, buf_len); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_write_data(struct rtsx_chip *chip, u8 trans_mode, - u8 *cmd, int cmd_len, u16 byte_cnt, u16 blk_cnt, - u8 bus_width, u8 *buf, int buf_len, int timeout) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i; - - sd_clr_err_code(chip); - - if (!buf) - buf_len = 0; - - if (buf_len > 512) { - /* This function can't write data more than one page */ - return STATUS_FAIL; - } - - if (buf && buf_len) { - retval = rtsx_write_ppbuf(chip, buf, buf_len); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - rtsx_init_cmd(chip); - - if (cmd_len) { - dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", cmd[0] - 0x40); - for (i = 0; i < (min(cmd_len, 6)); i++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - REG_SD_CMD0 + i, 0xFF, cmd[i]); - } - } - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, - (u8)byte_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, - (u8)(byte_cnt >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, - (u8)blk_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, - (u8)(blk_cnt >> 8)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | - SD_CHECK_CRC7 | SD_RSP_LEN_6); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - trans_mode | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, - SD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, SD_CARD, timeout); - if (retval < 0) { - if (retval == -ETIMEDOUT) { - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - } - - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_check_csd(struct rtsx_chip *chip, char check_wp) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i; - u8 csd_ver, trans_speed; - u8 rsp[16]; - - for (i = 0; i < 6; i++) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - return STATUS_FAIL; - } - - retval = sd_send_cmd_get_rsp(chip, SEND_CSD, sd_card->sd_addr, - SD_RSP_TYPE_R2, rsp, 16); - if (retval == STATUS_SUCCESS) - break; - } - - if (i == 6) - return STATUS_FAIL; - - memcpy(sd_card->raw_csd, rsp + 1, 15); - - dev_dbg(rtsx_dev(chip), "CSD Response:\n"); - dev_dbg(rtsx_dev(chip), "%*ph\n", 16, sd_card->raw_csd); - - csd_ver = (rsp[1] & 0xc0) >> 6; - dev_dbg(rtsx_dev(chip), "csd_ver = %d\n", csd_ver); - - trans_speed = rsp[4]; - if ((trans_speed & 0x07) == 0x02) { - if ((trans_speed & 0xf8) >= 0x30) { - if (chip->asic_code) - sd_card->sd_clock = 47; - else - sd_card->sd_clock = CLK_50; - - } else if ((trans_speed & 0xf8) == 0x28) { - if (chip->asic_code) - sd_card->sd_clock = 39; - else - sd_card->sd_clock = CLK_40; - - } else if ((trans_speed & 0xf8) == 0x20) { - if (chip->asic_code) - sd_card->sd_clock = 29; - else - sd_card->sd_clock = CLK_30; - - } else if ((trans_speed & 0xf8) >= 0x10) { - if (chip->asic_code) - sd_card->sd_clock = 23; - else - sd_card->sd_clock = CLK_20; - - } else if ((trans_speed & 0x08) >= 0x08) { - if (chip->asic_code) - sd_card->sd_clock = 19; - else - sd_card->sd_clock = CLK_20; - } else { - return STATUS_FAIL; - } - } else { - return STATUS_FAIL; - } - - if (CHK_MMC_SECTOR_MODE(sd_card)) { - sd_card->capacity = 0; - } else { - if ((!CHK_SD_HCXC(sd_card)) || csd_ver == 0) { - u8 blk_size, c_size_mult; - u16 c_size; - - blk_size = rsp[6] & 0x0F; - c_size = ((u16)(rsp[7] & 0x03) << 10) - + ((u16)rsp[8] << 2) - + ((u16)(rsp[9] & 0xC0) >> 6); - c_size_mult = (u8)((rsp[10] & 0x03) << 1); - c_size_mult += (rsp[11] & 0x80) >> 7; - sd_card->capacity = (((u32)(c_size + 1)) * - (1 << (c_size_mult + 2))) - << (blk_size - 9); - } else { - u32 total_sector = 0; - - total_sector = (((u32)rsp[8] & 0x3f) << 16) | - ((u32)rsp[9] << 8) | (u32)rsp[10]; - sd_card->capacity = (total_sector + 1) << 10; - } - } - - if (check_wp) { - if (rsp[15] & 0x30) - chip->card_wp |= SD_CARD; - - dev_dbg(rtsx_dev(chip), "CSD WP Status: 0x%x\n", rsp[15]); - } - - return STATUS_SUCCESS; -} - -static int sd_set_sample_push_timing(struct rtsx_chip *chip) -{ - int retval; - struct sd_info *sd_card = &chip->sd_card; - u8 val = 0; - - if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY) - val |= 0x10; - - if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == SD_SAMPLE_POINT_AUTO) { - if (chip->asic_code) { - if (CHK_SD_HS(sd_card) || CHK_MMC_52M(sd_card)) { - if (val & 0x10) - val |= 0x04; - else - val |= 0x08; - } - } else { - if (val & 0x10) - val |= 0x04; - else - val |= 0x08; - } - } else if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == - SD_SAMPLE_POINT_DELAY) { - if (val & 0x10) - val |= 0x04; - else - val |= 0x08; - } - - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x1C, val); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static void sd_choose_proper_clock(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - - if (CHK_SD_SDR104(sd_card)) { - if (chip->asic_code) - sd_card->sd_clock = chip->asic_sd_sdr104_clk; - else - sd_card->sd_clock = chip->fpga_sd_sdr104_clk; - - } else if (CHK_SD_DDR50(sd_card)) { - if (chip->asic_code) - sd_card->sd_clock = chip->asic_sd_ddr50_clk; - else - sd_card->sd_clock = chip->fpga_sd_ddr50_clk; - - } else if (CHK_SD_SDR50(sd_card)) { - if (chip->asic_code) - sd_card->sd_clock = chip->asic_sd_sdr50_clk; - else - sd_card->sd_clock = chip->fpga_sd_sdr50_clk; - - } else if (CHK_SD_HS(sd_card)) { - if (chip->asic_code) - sd_card->sd_clock = chip->asic_sd_hs_clk; - else - sd_card->sd_clock = chip->fpga_sd_hs_clk; - - } else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card)) { - if (chip->asic_code) - sd_card->sd_clock = chip->asic_mmc_52m_clk; - else - sd_card->sd_clock = chip->fpga_mmc_52m_clk; - - } else if (CHK_MMC_26M(sd_card)) { - if (chip->asic_code) - sd_card->sd_clock = 48; - else - sd_card->sd_clock = CLK_50; - } -} - -static int sd_set_clock_divider(struct rtsx_chip *chip, u8 clk_div) -{ - int retval; - u8 mask = 0, val = 0; - - mask = 0x60; - if (clk_div == SD_CLK_DIVIDE_0) - val = 0x00; - else if (clk_div == SD_CLK_DIVIDE_128) - val = 0x40; - else if (clk_div == SD_CLK_DIVIDE_256) - val = 0x20; - - retval = rtsx_write_register(chip, REG_SD_CFG1, mask, val); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int sd_set_init_para(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - retval = sd_set_sample_push_timing(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - sd_choose_proper_clock(chip); - - retval = switch_clock(chip, sd_card->sd_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int sd_select_card(struct rtsx_chip *chip, int select) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd_idx, cmd_type; - u32 addr; - - if (select) { - cmd_idx = SELECT_CARD; - cmd_type = SD_RSP_TYPE_R1; - addr = sd_card->sd_addr; - } else { - cmd_idx = DESELECT_CARD; - cmd_type = SD_RSP_TYPE_R0; - addr = 0; - } - - retval = sd_send_cmd_get_rsp(chip, cmd_idx, addr, cmd_type, NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -#ifdef SUPPORT_SD_LOCK -static int sd_update_lock_status(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 rsp[5]; - - retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, rsp, 5); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (rsp[1] & 0x02) - sd_card->sd_lock_status |= SD_LOCKED; - else - sd_card->sd_lock_status &= ~SD_LOCKED; - - dev_dbg(rtsx_dev(chip), "sd_card->sd_lock_status = 0x%x\n", - sd_card->sd_lock_status); - - if (rsp[1] & 0x01) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} -#endif - -static int sd_wait_state_data_ready(struct rtsx_chip *chip, u8 state, - u8 data_ready, int polling_cnt) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval, i; - u8 rsp[5]; - - for (i = 0; i < polling_cnt; i++) { - retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, - sd_card->sd_addr, SD_RSP_TYPE_R1, - rsp, 5); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (((rsp[3] & 0x1E) == state) && - ((rsp[3] & 0x01) == data_ready)) - return STATUS_SUCCESS; - } - - return STATUS_FAIL; -} - -static int sd_change_bank_voltage(struct rtsx_chip *chip, u8 voltage) -{ - int retval; - - if (voltage == SD_IO_3V3) { - if (chip->asic_code) { - retval = rtsx_write_phy_register(chip, 0x08, - 0x4FC0 | - chip->phy_voltage); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, SD_PAD_CTL, - SD_IO_USING_1V8, 0); - if (retval) - return retval; - } - } else if (voltage == SD_IO_1V8) { - if (chip->asic_code) { - retval = rtsx_write_phy_register(chip, 0x08, - 0x4C40 | - chip->phy_voltage); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, SD_PAD_CTL, - SD_IO_USING_1V8, - SD_IO_USING_1V8); - if (retval) - return retval; - } - } else { - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_voltage_switch(struct rtsx_chip *chip) -{ - int retval; - u8 stat; - - retval = rtsx_write_register(chip, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, - SD_CLK_TOGGLE_EN); - if (retval) - return retval; - - retval = sd_send_cmd_get_rsp(chip, VOLTAGE_SWITCH, 0, SD_RSP_TYPE_R1, - NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - udelay(chip->sd_voltage_switch_delay); - - retval = rtsx_read_register(chip, SD_BUS_STAT, &stat); - if (retval) - return retval; - if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS)) { - return STATUS_FAIL; - } - - retval = rtsx_write_register(chip, SD_BUS_STAT, 0xFF, - SD_CLK_FORCE_STOP); - if (retval) - return retval; - retval = sd_change_bank_voltage(chip, SD_IO_1V8); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - wait_timeout(50); - - retval = rtsx_write_register(chip, SD_BUS_STAT, 0xFF, - SD_CLK_TOGGLE_EN); - if (retval) - return retval; - wait_timeout(10); - - retval = rtsx_read_register(chip, SD_BUS_STAT, &stat); - if (retval) - return retval; - if ((stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS)) != - (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS)) { - dev_dbg(rtsx_dev(chip), "SD_BUS_STAT: 0x%x\n", stat); - rtsx_write_register(chip, SD_BUS_STAT, SD_CLK_TOGGLE_EN | - SD_CLK_FORCE_STOP, 0); - rtsx_write_register(chip, CARD_CLK_EN, 0xFF, 0); - return STATUS_FAIL; - } - - retval = rtsx_write_register(chip, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int sd_reset_dcm(struct rtsx_chip *chip, u8 tune_dir) -{ - int retval; - - if (tune_dir == TUNE_RX) { - retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF, - DCM_RESET | DCM_RX); - if (retval) - return retval; - retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF, DCM_RX); - if (retval) - return retval; - } else { - retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF, - DCM_RESET | DCM_TX); - if (retval) - return retval; - retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF, DCM_TX); - if (retval) - return retval; - } - - return STATUS_SUCCESS; -} - -static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir) -{ - struct sd_info *sd_card = &chip->sd_card; - u16 SD_VP_CTL, SD_DCMPS_CTL; - u8 val; - int retval; - bool ddr_rx = false; - - dev_dbg(rtsx_dev(chip), "%s (sample_point = %d, tune_dir = %d)\n", - __func__, sample_point, tune_dir); - - if (tune_dir == TUNE_RX) { - SD_VP_CTL = SD_VPRX_CTL; - SD_DCMPS_CTL = SD_DCMPS_RX_CTL; - if (CHK_SD_DDR50(sd_card)) - ddr_rx = true; - } else { - SD_VP_CTL = SD_VPTX_CTL; - SD_DCMPS_CTL = SD_DCMPS_TX_CTL; - } - - if (chip->asic_code) { - retval = rtsx_write_register(chip, CLK_CTL, CHANGE_CLK, - CHANGE_CLK); - if (retval) - return retval; - retval = rtsx_write_register(chip, SD_VP_CTL, 0x1F, - sample_point); - if (retval) - return retval; - retval = rtsx_write_register(chip, SD_VPCLK0_CTL, - PHASE_NOT_RESET, 0); - if (retval) - return retval; - retval = rtsx_write_register(chip, SD_VPCLK0_CTL, - PHASE_NOT_RESET, PHASE_NOT_RESET); - if (retval) - return retval; - retval = rtsx_write_register(chip, CLK_CTL, CHANGE_CLK, 0); - if (retval) - return retval; - } else { - rtsx_read_register(chip, SD_VP_CTL, &val); - dev_dbg(rtsx_dev(chip), "SD_VP_CTL: 0x%x\n", val); - rtsx_read_register(chip, SD_DCMPS_CTL, &val); - dev_dbg(rtsx_dev(chip), "SD_DCMPS_CTL: 0x%x\n", val); - - if (ddr_rx) { - retval = rtsx_write_register(chip, SD_VP_CTL, - PHASE_CHANGE, - PHASE_CHANGE); - if (retval) - return retval; - udelay(50); - retval = rtsx_write_register(chip, SD_VP_CTL, 0xFF, - PHASE_CHANGE | - PHASE_NOT_RESET | - sample_point); - if (retval) - return retval; - } else { - retval = rtsx_write_register(chip, CLK_CTL, - CHANGE_CLK, CHANGE_CLK); - if (retval) - return retval; - udelay(50); - retval = rtsx_write_register(chip, SD_VP_CTL, 0xFF, - PHASE_NOT_RESET | - sample_point); - if (retval) - return retval; - } - udelay(100); - - rtsx_init_cmd(chip); - rtsx_add_cmd(chip, WRITE_REG_CMD, SD_DCMPS_CTL, DCMPS_CHANGE, - DCMPS_CHANGE); - rtsx_add_cmd(chip, CHECK_REG_CMD, SD_DCMPS_CTL, - DCMPS_CHANGE_DONE, DCMPS_CHANGE_DONE); - retval = rtsx_send_cmd(chip, SD_CARD, 100); - if (retval != STATUS_SUCCESS) - goto fail; - - val = *rtsx_get_cmd_data(chip); - if (val & DCMPS_ERROR) - goto fail; - - if ((val & DCMPS_CURRENT_PHASE) != sample_point) - goto fail; - - retval = rtsx_write_register(chip, SD_DCMPS_CTL, - DCMPS_CHANGE, 0); - if (retval) - return retval; - if (ddr_rx) { - retval = rtsx_write_register(chip, SD_VP_CTL, - PHASE_CHANGE, 0); - if (retval) - return retval; - } else { - retval = rtsx_write_register(chip, CLK_CTL, - CHANGE_CLK, 0); - if (retval) - return retval; - } - - udelay(50); - } - - retval = rtsx_write_register(chip, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0); - if (retval) - return retval; - - return STATUS_SUCCESS; - -fail: - rtsx_read_register(chip, SD_VP_CTL, &val); - dev_dbg(rtsx_dev(chip), "SD_VP_CTL: 0x%x\n", val); - rtsx_read_register(chip, SD_DCMPS_CTL, &val); - dev_dbg(rtsx_dev(chip), "SD_DCMPS_CTL: 0x%x\n", val); - - rtsx_write_register(chip, SD_DCMPS_CTL, DCMPS_CHANGE, 0); - rtsx_write_register(chip, SD_VP_CTL, PHASE_CHANGE, 0); - mdelay(10); - sd_reset_dcm(chip, tune_dir); - return STATUS_FAIL; -} - -static int sd_check_spec(struct rtsx_chip *chip, u8 bus_width) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd[5], buf[8]; - - retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - cmd[0] = 0x40 | SEND_SCR; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 8, 1, bus_width, - buf, 8, 250); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - return STATUS_FAIL; - } - - memcpy(sd_card->raw_scr, buf, 8); - - if ((buf[0] & 0x0F) == 0) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_query_switch_result(struct rtsx_chip *chip, u8 func_group, - u8 func_to_switch, u8 *buf, int buf_len) -{ - u8 support_mask = 0, query_switch = 0, switch_busy = 0; - int support_offset = 0, query_switch_offset = 0, check_busy_offset = 0; - - if (func_group == SD_FUNC_GROUP_1) { - support_offset = FUNCTION_GROUP1_SUPPORT_OFFSET; - query_switch_offset = FUNCTION_GROUP1_QUERY_SWITCH_OFFSET; - check_busy_offset = FUNCTION_GROUP1_CHECK_BUSY_OFFSET; - - switch (func_to_switch) { - case HS_SUPPORT: - support_mask = HS_SUPPORT_MASK; - query_switch = HS_QUERY_SWITCH_OK; - switch_busy = HS_SWITCH_BUSY; - break; - - case SDR50_SUPPORT: - support_mask = SDR50_SUPPORT_MASK; - query_switch = SDR50_QUERY_SWITCH_OK; - switch_busy = SDR50_SWITCH_BUSY; - break; - - case SDR104_SUPPORT: - support_mask = SDR104_SUPPORT_MASK; - query_switch = SDR104_QUERY_SWITCH_OK; - switch_busy = SDR104_SWITCH_BUSY; - break; - - case DDR50_SUPPORT: - support_mask = DDR50_SUPPORT_MASK; - query_switch = DDR50_QUERY_SWITCH_OK; - switch_busy = DDR50_SWITCH_BUSY; - break; - - default: - return STATUS_FAIL; - } - } else if (func_group == SD_FUNC_GROUP_3) { - support_offset = FUNCTION_GROUP3_SUPPORT_OFFSET; - query_switch_offset = FUNCTION_GROUP3_QUERY_SWITCH_OFFSET; - check_busy_offset = FUNCTION_GROUP3_CHECK_BUSY_OFFSET; - - switch (func_to_switch) { - case DRIVING_TYPE_A: - support_mask = DRIVING_TYPE_A_MASK; - query_switch = TYPE_A_QUERY_SWITCH_OK; - switch_busy = TYPE_A_SWITCH_BUSY; - break; - - case DRIVING_TYPE_C: - support_mask = DRIVING_TYPE_C_MASK; - query_switch = TYPE_C_QUERY_SWITCH_OK; - switch_busy = TYPE_C_SWITCH_BUSY; - break; - - case DRIVING_TYPE_D: - support_mask = DRIVING_TYPE_D_MASK; - query_switch = TYPE_D_QUERY_SWITCH_OK; - switch_busy = TYPE_D_SWITCH_BUSY; - break; - - default: - return STATUS_FAIL; - } - } else if (func_group == SD_FUNC_GROUP_4) { - support_offset = FUNCTION_GROUP4_SUPPORT_OFFSET; - query_switch_offset = FUNCTION_GROUP4_QUERY_SWITCH_OFFSET; - check_busy_offset = FUNCTION_GROUP4_CHECK_BUSY_OFFSET; - - switch (func_to_switch) { - case CURRENT_LIMIT_400: - support_mask = CURRENT_LIMIT_400_MASK; - query_switch = CURRENT_LIMIT_400_QUERY_SWITCH_OK; - switch_busy = CURRENT_LIMIT_400_SWITCH_BUSY; - break; - - case CURRENT_LIMIT_600: - support_mask = CURRENT_LIMIT_600_MASK; - query_switch = CURRENT_LIMIT_600_QUERY_SWITCH_OK; - switch_busy = CURRENT_LIMIT_600_SWITCH_BUSY; - break; - - case CURRENT_LIMIT_800: - support_mask = CURRENT_LIMIT_800_MASK; - query_switch = CURRENT_LIMIT_800_QUERY_SWITCH_OK; - switch_busy = CURRENT_LIMIT_800_SWITCH_BUSY; - break; - - default: - return STATUS_FAIL; - } - } else { - return STATUS_FAIL; - } - - if (func_group == SD_FUNC_GROUP_1) { - if (!(buf[support_offset] & support_mask) || - ((buf[query_switch_offset] & 0x0F) != query_switch)) { - return STATUS_FAIL; - } - } - - /* Check 'Busy Status' */ - if (buf[DATA_STRUCTURE_VER_OFFSET] == 0x01 && - ((buf[check_busy_offset] & switch_busy) == switch_busy)) { - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_check_switch_mode(struct rtsx_chip *chip, u8 mode, u8 func_group, - u8 func_to_switch, u8 bus_width) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd[5], buf[64]; - - dev_dbg(rtsx_dev(chip), "%s (mode = %d, func_group = %d, func_to_switch = %d)\n", - __func__, mode, func_group, func_to_switch); - - cmd[0] = 0x40 | SWITCH; - cmd[1] = mode; - - if (func_group == SD_FUNC_GROUP_1) { - cmd[2] = 0xFF; - cmd[3] = 0xFF; - cmd[4] = 0xF0 + func_to_switch; - } else if (func_group == SD_FUNC_GROUP_3) { - cmd[2] = 0xFF; - cmd[3] = 0xF0 + func_to_switch; - cmd[4] = 0xFF; - } else if (func_group == SD_FUNC_GROUP_4) { - cmd[2] = 0xFF; - cmd[3] = 0x0F + (func_to_switch << 4); - cmd[4] = 0xFF; - } else { - cmd[1] = SD_CHECK_MODE; - cmd[2] = 0xFF; - cmd[3] = 0xFF; - cmd[4] = 0xFF; - } - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1, bus_width, - buf, 64, 250); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - return STATUS_FAIL; - } - - dev_dbg(rtsx_dev(chip), "%*ph\n", 64, buf); - - if (func_group == NO_ARGUMENT) { - sd_card->func_group1_mask = buf[0x0D]; - sd_card->func_group2_mask = buf[0x0B]; - sd_card->func_group3_mask = buf[0x09]; - sd_card->func_group4_mask = buf[0x07]; - - dev_dbg(rtsx_dev(chip), "func_group1_mask = 0x%02x\n", - buf[0x0D]); - dev_dbg(rtsx_dev(chip), "func_group2_mask = 0x%02x\n", - buf[0x0B]); - dev_dbg(rtsx_dev(chip), "func_group3_mask = 0x%02x\n", - buf[0x09]); - dev_dbg(rtsx_dev(chip), "func_group4_mask = 0x%02x\n", - buf[0x07]); - } else { - /* Maximum current consumption, check whether current is - * acceptable; bit[511:496] = 0x0000 means some error happened. - */ - u16 cc = ((u16)buf[0] << 8) | buf[1]; - - dev_dbg(rtsx_dev(chip), "Maximum current consumption: %dmA\n", - cc); - if (cc == 0 || cc > 800) - return STATUS_FAIL; - - retval = sd_query_switch_result(chip, func_group, - func_to_switch, buf, 64); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (cc > 400 || func_to_switch > CURRENT_LIMIT_400) { - retval = rtsx_write_register(chip, OCPPARA2, - SD_OCP_THD_MASK, - chip->sd_800mA_ocp_thd); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PWR_CTL, - PMOS_STRG_MASK, - PMOS_STRG_800mA); - if (retval) - return retval; - } - } - - return STATUS_SUCCESS; -} - -static u8 downgrade_switch_mode(u8 func_group, u8 func_to_switch) -{ - if (func_group == SD_FUNC_GROUP_1) { - if (func_to_switch > HS_SUPPORT) - func_to_switch--; - - } else if (func_group == SD_FUNC_GROUP_4) { - if (func_to_switch > CURRENT_LIMIT_200) - func_to_switch--; - } - - return func_to_switch; -} - -static int sd_check_switch(struct rtsx_chip *chip, - u8 func_group, u8 func_to_switch, u8 bus_width) -{ - int retval; - int i; - bool switch_good = false; - - for (i = 0; i < 3; i++) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - return STATUS_FAIL; - } - - retval = sd_check_switch_mode(chip, SD_CHECK_MODE, func_group, - func_to_switch, bus_width); - if (retval == STATUS_SUCCESS) { - u8 stat; - - retval = sd_check_switch_mode(chip, SD_SWITCH_MODE, - func_group, - func_to_switch, - bus_width); - if (retval == STATUS_SUCCESS) { - switch_good = true; - break; - } - - retval = rtsx_read_register(chip, SD_STAT1, &stat); - if (retval) - return retval; - if (stat & SD_CRC16_ERR) { - dev_dbg(rtsx_dev(chip), "SD CRC16 error when switching mode\n"); - return STATUS_FAIL; - } - } - - func_to_switch = downgrade_switch_mode(func_group, - func_to_switch); - - wait_timeout(20); - } - - if (!switch_good) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i; - u8 func_to_switch = 0; - - /* Get supported functions */ - retval = sd_check_switch_mode(chip, SD_CHECK_MODE, NO_ARGUMENT, - NO_ARGUMENT, bus_width); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - sd_card->func_group1_mask &= ~(sd_card->sd_switch_fail); - - /* Function Group 1: Access Mode */ - for (i = 0; i < 4; i++) { - switch ((u8)(chip->sd_speed_prior >> (i * 8))) { - case SDR104_SUPPORT: - if ((sd_card->func_group1_mask & SDR104_SUPPORT_MASK) && - chip->sdr104_en) { - func_to_switch = SDR104_SUPPORT; - } - break; - - case DDR50_SUPPORT: - if ((sd_card->func_group1_mask & DDR50_SUPPORT_MASK) && - chip->ddr50_en) { - func_to_switch = DDR50_SUPPORT; - } - break; - - case SDR50_SUPPORT: - if ((sd_card->func_group1_mask & SDR50_SUPPORT_MASK) && - chip->sdr50_en) { - func_to_switch = SDR50_SUPPORT; - } - break; - - case HS_SUPPORT: - if (sd_card->func_group1_mask & HS_SUPPORT_MASK) - func_to_switch = HS_SUPPORT; - - break; - - default: - continue; - } - - if (func_to_switch) - break; - } - dev_dbg(rtsx_dev(chip), "SD_FUNC_GROUP_1: func_to_switch = 0x%02x", - func_to_switch); - -#ifdef SUPPORT_SD_LOCK - if ((sd_card->sd_lock_status & SD_SDR_RST) && - func_to_switch == DDR50_SUPPORT && - (sd_card->func_group1_mask & SDR50_SUPPORT_MASK)) { - func_to_switch = SDR50_SUPPORT; - dev_dbg(rtsx_dev(chip), "Using SDR50 instead of DDR50 for SD Lock\n"); - } -#endif - - if (func_to_switch) { - retval = sd_check_switch(chip, SD_FUNC_GROUP_1, func_to_switch, - bus_width); - if (retval != STATUS_SUCCESS) { - if (func_to_switch == SDR104_SUPPORT) { - sd_card->sd_switch_fail = SDR104_SUPPORT_MASK; - } else if (func_to_switch == DDR50_SUPPORT) { - sd_card->sd_switch_fail = SDR104_SUPPORT_MASK | - DDR50_SUPPORT_MASK; - } else if (func_to_switch == SDR50_SUPPORT) { - sd_card->sd_switch_fail = SDR104_SUPPORT_MASK | - DDR50_SUPPORT_MASK | SDR50_SUPPORT_MASK; - } - return STATUS_FAIL; - } - - if (func_to_switch == SDR104_SUPPORT) - SET_SD_SDR104(sd_card); - else if (func_to_switch == DDR50_SUPPORT) - SET_SD_DDR50(sd_card); - else if (func_to_switch == SDR50_SUPPORT) - SET_SD_SDR50(sd_card); - else - SET_SD_HS(sd_card); - } - - if (CHK_SD_DDR50(sd_card)) { - retval = rtsx_write_register(chip, SD_PUSH_POINT_CTL, 0x06, - 0x04); - if (retval) - return retval; - retval = sd_set_sample_push_timing(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - if (!func_to_switch || func_to_switch == HS_SUPPORT) { - /* Do not try to switch current limit if the card doesn't - * support UHS mode or we don't want it to support UHS mode - */ - return STATUS_SUCCESS; - } - - /* Function Group 4: Current Limit */ - func_to_switch = 0xFF; - - for (i = 0; i < 4; i++) { - switch ((u8)(chip->sd_current_prior >> (i * 8))) { - case CURRENT_LIMIT_800: - if (sd_card->func_group4_mask & CURRENT_LIMIT_800_MASK) - func_to_switch = CURRENT_LIMIT_800; - - break; - - case CURRENT_LIMIT_600: - if (sd_card->func_group4_mask & CURRENT_LIMIT_600_MASK) - func_to_switch = CURRENT_LIMIT_600; - - break; - - case CURRENT_LIMIT_400: - if (sd_card->func_group4_mask & CURRENT_LIMIT_400_MASK) - func_to_switch = CURRENT_LIMIT_400; - - break; - - case CURRENT_LIMIT_200: - if (sd_card->func_group4_mask & CURRENT_LIMIT_200_MASK) - func_to_switch = CURRENT_LIMIT_200; - - break; - - default: - continue; - } - - if (func_to_switch != 0xFF) - break; - } - - dev_dbg(rtsx_dev(chip), "SD_FUNC_GROUP_4: func_to_switch = 0x%02x", - func_to_switch); - - if (func_to_switch <= CURRENT_LIMIT_800) { - retval = sd_check_switch(chip, SD_FUNC_GROUP_4, func_to_switch, - bus_width); - if (retval != STATUS_SUCCESS) { - if (sd_check_err_code(chip, SD_NO_CARD)) - return STATUS_FAIL; - } - dev_dbg(rtsx_dev(chip), "Switch current limit finished! (%d)\n", - retval); - } - - if (CHK_SD_DDR50(sd_card)) { - retval = rtsx_write_register(chip, SD_PUSH_POINT_CTL, 0x06, 0); - if (retval) - return retval; - } - - return STATUS_SUCCESS; -} - -static int sd_wait_data_idle(struct rtsx_chip *chip) -{ - int retval = STATUS_TIMEDOUT; - int i; - u8 val = 0; - - for (i = 0; i < 100; i++) { - retval = rtsx_read_register(chip, SD_DATA_STATE, &val); - if (retval) - return retval; - if (val & SD_DATA_IDLE) { - retval = STATUS_SUCCESS; - break; - } - udelay(100); - } - dev_dbg(rtsx_dev(chip), "SD_DATA_STATE: 0x%02x\n", val); - - return retval; -} - -static int sd_sdr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point) -{ - int retval; - u8 cmd[5]; - - retval = sd_change_phase(chip, sample_point, TUNE_RX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - cmd[0] = 0x40 | SEND_TUNING_PATTERN; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - retval = sd_read_data(chip, SD_TM_AUTO_TUNING, cmd, 5, 0x40, 1, - SD_BUS_WIDTH_4, NULL, 0, 100); - if (retval != STATUS_SUCCESS) { - (void)sd_wait_data_idle(chip); - - rtsx_clear_sd_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_ddr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd[5]; - - retval = sd_change_phase(chip, sample_point, TUNE_RX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "sd ddr tuning rx\n"); - - retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - cmd[0] = 0x40 | SD_STATUS; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1, - SD_BUS_WIDTH_4, NULL, 0, 100); - if (retval != STATUS_SUCCESS) { - (void)sd_wait_data_idle(chip); - - rtsx_clear_sd_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int mmc_ddr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd[5], bus_width; - - if (CHK_MMC_8BIT(sd_card)) - bus_width = SD_BUS_WIDTH_8; - else if (CHK_MMC_4BIT(sd_card)) - bus_width = SD_BUS_WIDTH_4; - else - bus_width = SD_BUS_WIDTH_1; - - retval = sd_change_phase(chip, sample_point, TUNE_RX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "mmc ddr tuning rx\n"); - - cmd[0] = 0x40 | SEND_EXT_CSD; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 0x200, 1, - bus_width, NULL, 0, 100); - if (retval != STATUS_SUCCESS) { - (void)sd_wait_data_idle(chip); - - rtsx_clear_sd_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_sdr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - retval = sd_change_phase(chip, sample_point, TUNE_TX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, - SD_RSP_80CLK_TIMEOUT_EN); - if (retval) - return retval; - - retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) { - if (sd_check_err_code(chip, SD_RSP_TIMEOUT)) { - rtsx_write_register(chip, SD_CFG3, - SD_RSP_80CLK_TIMEOUT_EN, 0); - return STATUS_FAIL; - } - } - - retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, - 0); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int sd_ddr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd[5], bus_width; - - retval = sd_change_phase(chip, sample_point, TUNE_TX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (CHK_SD(sd_card)) { - bus_width = SD_BUS_WIDTH_4; - } else { - if (CHK_MMC_8BIT(sd_card)) - bus_width = SD_BUS_WIDTH_8; - else if (CHK_MMC_4BIT(sd_card)) - bus_width = SD_BUS_WIDTH_4; - else - bus_width = SD_BUS_WIDTH_1; - } - - retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, - SD_RSP_80CLK_TIMEOUT_EN); - if (retval) - return retval; - - cmd[0] = 0x40 | PROGRAM_CSD; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - retval = sd_write_data(chip, SD_TM_AUTO_WRITE_2, cmd, 5, 16, 1, - bus_width, sd_card->raw_csd, 16, 100); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0); - return STATUS_FAIL; - } - - retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, - 0); - if (retval) - return retval; - - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, SD_RSP_TYPE_R1, - NULL, 0); - - return STATUS_SUCCESS; -} - -static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map, - u8 tune_dir) -{ - struct sd_info *sd_card = &chip->sd_card; - struct timing_phase_path path[MAX_PHASE + 1]; - int i, j, cont_path_cnt; - bool new_block; - int max_len, final_path_idx; - u8 final_phase = 0xFF; - - if (phase_map == 0xFFFFFFFF) { - if (tune_dir == TUNE_RX) - final_phase = (u8)chip->sd_default_rx_phase; - else - final_phase = (u8)chip->sd_default_tx_phase; - - goto search_finish; - } - - cont_path_cnt = 0; - new_block = true; - j = 0; - for (i = 0; i < MAX_PHASE + 1; i++) { - if (phase_map & (1 << i)) { - if (new_block) { - new_block = false; - j = cont_path_cnt++; - path[j].start = i; - path[j].end = i; - } else { - path[j].end = i; - } - } else { - new_block = true; - if (cont_path_cnt) { - int idx = cont_path_cnt - 1; - - path[idx].len = path[idx].end - - path[idx].start + 1; - path[idx].mid = path[idx].start + - path[idx].len / 2; - } - } - } - - if (cont_path_cnt == 0) { - dev_dbg(rtsx_dev(chip), "No continuous phase path\n"); - goto search_finish; - } else { - int idx = cont_path_cnt - 1; - - path[idx].len = path[idx].end - path[idx].start + 1; - path[idx].mid = path[idx].start + path[idx].len / 2; - } - - if (path[0].start == 0 && - path[cont_path_cnt - 1].end == MAX_PHASE) { - path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1; - path[0].len += path[cont_path_cnt - 1].len; - path[0].mid = path[0].start + path[0].len / 2; - if (path[0].mid < 0) - path[0].mid += MAX_PHASE + 1; - - cont_path_cnt--; - } - - max_len = 0; - final_phase = 0; - final_path_idx = 0; - for (i = 0; i < cont_path_cnt; i++) { - if (path[i].len > max_len) { - max_len = path[i].len; - final_phase = (u8)path[i].mid; - final_path_idx = i; - } - - dev_dbg(rtsx_dev(chip), "path[%d].start = %d\n", - i, path[i].start); - dev_dbg(rtsx_dev(chip), "path[%d].end = %d\n", i, path[i].end); - dev_dbg(rtsx_dev(chip), "path[%d].len = %d\n", i, path[i].len); - dev_dbg(rtsx_dev(chip), "path[%d].mid = %d\n", i, path[i].mid); - dev_dbg(rtsx_dev(chip), "\n"); - } - - if (tune_dir == TUNE_TX) { - if (CHK_SD_SDR104(sd_card)) { - if (max_len > 15) { - int temp_mid = (max_len - 16) / 2; - int temp_final_phase = - path[final_path_idx].end - - (max_len - (6 + temp_mid)); - - if (temp_final_phase < 0) - final_phase = (u8)(temp_final_phase + - MAX_PHASE + 1); - else - final_phase = (u8)temp_final_phase; - } - } else if (CHK_SD_SDR50(sd_card)) { - if (max_len > 12) { - int temp_mid = (max_len - 13) / 2; - int temp_final_phase = - path[final_path_idx].end - - (max_len - (3 + temp_mid)); - - if (temp_final_phase < 0) - final_phase = (u8)(temp_final_phase + - MAX_PHASE + 1); - else - final_phase = (u8)temp_final_phase; - } - } - } - -search_finish: - dev_dbg(rtsx_dev(chip), "Final chosen phase: %d\n", final_phase); - return final_phase; -} - -static int sd_tuning_rx(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i, j; - u32 raw_phase_map[3], phase_map; - u8 final_phase; - int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point); - - if (CHK_SD(sd_card)) { - if (CHK_SD_DDR50(sd_card)) - tuning_cmd = sd_ddr_tuning_rx_cmd; - else - tuning_cmd = sd_sdr_tuning_rx_cmd; - - } else { - if (CHK_MMC_DDR52(sd_card)) - tuning_cmd = mmc_ddr_tuning_rx_cmd; - else - return STATUS_FAIL; - } - - for (i = 0; i < 3; i++) { - raw_phase_map[i] = 0; - for (j = MAX_PHASE; j >= 0; j--) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - return STATUS_FAIL; - } - - retval = tuning_cmd(chip, (u8)j); - if (retval == STATUS_SUCCESS) - raw_phase_map[i] |= 1 << j; - } - } - - phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2]; - for (i = 0; i < 3; i++) - dev_dbg(rtsx_dev(chip), "RX raw_phase_map[%d] = 0x%08x\n", - i, raw_phase_map[i]); - - dev_dbg(rtsx_dev(chip), "RX phase_map = 0x%08x\n", phase_map); - - final_phase = sd_search_final_phase(chip, phase_map, TUNE_RX); - if (final_phase == 0xFF) - return STATUS_FAIL; - - retval = sd_change_phase(chip, final_phase, TUNE_RX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_ddr_pre_tuning_tx(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i; - u32 phase_map; - u8 final_phase; - - retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, - SD_RSP_80CLK_TIMEOUT_EN); - if (retval) - return retval; - - phase_map = 0; - for (i = MAX_PHASE; i >= 0; i--) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - rtsx_write_register(chip, SD_CFG3, - SD_RSP_80CLK_TIMEOUT_EN, 0); - return STATUS_FAIL; - } - - retval = sd_change_phase(chip, (u8)i, TUNE_TX); - if (retval != STATUS_SUCCESS) - continue; - - retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, - sd_card->sd_addr, SD_RSP_TYPE_R1, - NULL, 0); - if (retval == STATUS_SUCCESS || - !sd_check_err_code(chip, SD_RSP_TIMEOUT)) - phase_map |= 1 << i; - } - - retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, - 0); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "DDR TX pre tune phase_map = 0x%08x\n", - phase_map); - - final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX); - if (final_phase == 0xFF) - return STATUS_FAIL; - - retval = sd_change_phase(chip, final_phase, TUNE_TX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "DDR TX pre tune phase: %d\n", - (int)final_phase); - - return STATUS_SUCCESS; -} - -static int sd_tuning_tx(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int i, j; - u32 raw_phase_map[3], phase_map; - u8 final_phase; - int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point); - - if (CHK_SD(sd_card)) { - if (CHK_SD_DDR50(sd_card)) - tuning_cmd = sd_ddr_tuning_tx_cmd; - else - tuning_cmd = sd_sdr_tuning_tx_cmd; - - } else { - if (CHK_MMC_DDR52(sd_card)) - tuning_cmd = sd_ddr_tuning_tx_cmd; - else - return STATUS_FAIL; - } - - for (i = 0; i < 3; i++) { - raw_phase_map[i] = 0; - for (j = MAX_PHASE; j >= 0; j--) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - rtsx_write_register(chip, SD_CFG3, - SD_RSP_80CLK_TIMEOUT_EN, 0); - return STATUS_FAIL; - } - - retval = tuning_cmd(chip, (u8)j); - if (retval == STATUS_SUCCESS) - raw_phase_map[i] |= 1 << j; - } - } - - phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2]; - for (i = 0; i < 3; i++) - dev_dbg(rtsx_dev(chip), "TX raw_phase_map[%d] = 0x%08x\n", - i, raw_phase_map[i]); - - dev_dbg(rtsx_dev(chip), "TX phase_map = 0x%08x\n", phase_map); - - final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX); - if (final_phase == 0xFF) - return STATUS_FAIL; - - retval = sd_change_phase(chip, final_phase, TUNE_TX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_sdr_tuning(struct rtsx_chip *chip) -{ - int retval; - - retval = sd_tuning_tx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sd_tuning_rx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_ddr_tuning(struct rtsx_chip *chip) -{ - int retval; - - if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) { - retval = sd_ddr_pre_tuning_tx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = sd_change_phase(chip, (u8)chip->sd_ddr_tx_phase, - TUNE_TX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = sd_tuning_rx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) { - retval = sd_tuning_tx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int mmc_ddr_tuning(struct rtsx_chip *chip) -{ - int retval; - - if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) { - retval = sd_ddr_pre_tuning_tx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = sd_change_phase(chip, (u8)chip->mmc_ddr_tx_phase, - TUNE_TX); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - retval = sd_tuning_rx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) { - retval = sd_tuning_tx(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int sd_switch_clock(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - int re_tuning = 0; - - retval = select_card(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = switch_clock(chip, sd_card->sd_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (re_tuning) { - if (CHK_SD(sd_card)) { - if (CHK_SD_DDR50(sd_card)) - retval = sd_ddr_tuning(chip); - else - retval = sd_sdr_tuning(chip); - } else { - if (CHK_MMC_DDR52(sd_card)) - retval = mmc_ddr_tuning(chip); - } - - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_prepare_reset(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - if (chip->asic_code) - sd_card->sd_clock = 29; - else - sd_card->sd_clock = CLK_30; - - sd_card->sd_type = 0; - sd_card->seq_mode = 0; - sd_card->sd_data_buf_ready = 0; - sd_card->capacity = 0; - -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status = 0; - sd_card->sd_erase_status = 0; -#endif - - chip->capacity[chip->card2lun[SD_CARD]] = 0; - chip->sd_io = 0; - - retval = sd_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return retval; - - retval = rtsx_write_register(chip, REG_SD_CFG1, 0xFF, 0x40); - if (retval) - return retval; - - retval = rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR, - SD_STOP | SD_CLR_ERR); - if (retval) - return retval; - - retval = select_card(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_pull_ctl_disable(struct rtsx_chip *chip) -{ - int retval; - - if (CHECK_PID(chip, 0x5208)) { - retval = rtsx_write_register(chip, CARD_PULL_CTL1, 0xFF, - XD_D3_PD | SD_D7_PD | SD_CLK_PD | - SD_D5_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF, - SD_D6_PD | SD_D0_PD | SD_D1_PD | - XD_D5_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL3, 0xFF, - SD_D4_PD | XD_CE_PD | XD_CLE_PD | - XD_CD_PU); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL4, 0xFF, - XD_RDY_PD | SD_D3_PD | SD_D2_PD | - XD_ALE_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | SD_WP_PD | SD_CD_PU | - SD_CMD_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - if (retval) - return retval; - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - retval = rtsx_write_register(chip, CARD_PULL_CTL1, - 0xFF, 0x55); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL2, - 0xFF, 0x55); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL3, - 0xFF, 0x4B); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL4, - 0xFF, 0x69); - if (retval) - return retval; - } - } - - return STATUS_SUCCESS; -} - -int sd_pull_ctl_enable(struct rtsx_chip *chip) -{ - int retval; - - rtsx_init_cmd(chip); - - if (CHECK_PID(chip, 0x5208)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, - XD_D3_PD | SD_DAT7_PU | SD_CLK_NP | SD_D5_PU); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, - SD_D6_PU | SD_D0_PU | SD_D1_PU | XD_D5_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, - SD_D4_PU | XD_CE_PD | XD_CLE_PD | XD_CD_PU); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, - XD_RDY_PD | SD_D3_PU | SD_D2_PU | XD_ALE_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | SD_WP_PU | SD_CD_PU | SD_CMD_PU); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, - 0xA8); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, - 0x5A); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, - 0x95); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, - 0xAA); - } - } - - retval = rtsx_send_cmd(chip, SD_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sd_init_power(struct rtsx_chip *chip) -{ - int retval; - - retval = sd_power_off_card3v3(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (!chip->ft2_fast_mode) - wait_timeout(250); - - retval = enable_card_clock(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (chip->asic_code) { - retval = sd_pull_ctl_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, - FPGA_SD_PULL_CTL_BIT | 0x20, 0); - if (retval) - return retval; - } - - if (!chip->ft2_fast_mode) { - retval = card_power_on(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - wait_timeout(260); - -#ifdef SUPPORT_OCP - if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) { - dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n", - chip->ocp_stat); - return STATUS_FAIL; - } -#endif - } - - retval = rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, - SD_OUTPUT_EN); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int sd_dummy_clock(struct rtsx_chip *chip) -{ - int retval; - - retval = rtsx_write_register(chip, REG_SD_CFG3, 0x01, 0x01); - if (retval) - return retval; - wait_timeout(5); - retval = rtsx_write_register(chip, REG_SD_CFG3, 0x01, 0); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int sd_read_lba0(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 cmd[5], bus_width; - - cmd[0] = 0x40 | READ_SINGLE_BLOCK; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - if (CHK_SD(sd_card)) { - bus_width = SD_BUS_WIDTH_4; - } else { - if (CHK_MMC_8BIT(sd_card)) - bus_width = SD_BUS_WIDTH_8; - else if (CHK_MMC_4BIT(sd_card)) - bus_width = SD_BUS_WIDTH_4; - else - bus_width = SD_BUS_WIDTH_1; - } - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 512, 1, - bus_width, NULL, 0, 100); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sd_check_wp_state(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u32 val; - u16 sd_card_type; - u8 cmd[5], buf[64]; - - retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - cmd[0] = 0x40 | SD_STATUS; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[4] = 0; - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1, - SD_BUS_WIDTH_4, buf, 64, 250); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - return STATUS_FAIL; - } - - dev_dbg(rtsx_dev(chip), "ACMD13:\n"); - dev_dbg(rtsx_dev(chip), "%*ph\n", 64, buf); - - sd_card_type = ((u16)buf[2] << 8) | buf[3]; - dev_dbg(rtsx_dev(chip), "sd_card_type = 0x%04x\n", sd_card_type); - if (sd_card_type == 0x0001 || sd_card_type == 0x0002) { - /* ROM card or OTP */ - chip->card_wp |= SD_CARD; - } - - /* Check SD Machanical Write-Protect Switch */ - val = rtsx_readl(chip, RTSX_BIPR); - if (val & SD_WRITE_PROTECT) - chip->card_wp |= SD_CARD; - - return STATUS_SUCCESS; -} - -static int reset_sd(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - bool hi_cap_flow = false; - int retval, i = 0, j = 0, k = 0; - bool sd_dont_switch = false; - bool support_1v8 = false; - bool try_sdio = true; - u8 rsp[16]; - u8 switch_bus_width; - u32 voltage = 0; - bool sd20_mode = false; - - SET_SD(sd_card); - -switch_fail: - - i = 0; - j = 0; - k = 0; - hi_cap_flow = false; - -#ifdef SUPPORT_SD_LOCK - if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) - goto SD_UNLOCK_ENTRY; -#endif - - retval = sd_prepare_reset(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - retval = sd_dummy_clock(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip) && try_sdio) { - int rty_cnt = 0; - - for (; rty_cnt < chip->sdio_retry_cnt; rty_cnt++) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - goto status_fail; - } - - retval = sd_send_cmd_get_rsp(chip, IO_SEND_OP_COND, 0, - SD_RSP_TYPE_R4, rsp, 5); - if (retval == STATUS_SUCCESS) { - int func_num = (rsp[1] >> 4) & 0x07; - - if (func_num) { - dev_dbg(rtsx_dev(chip), "SD_IO card (Function number: %d)!\n", - func_num); - chip->sd_io = 1; - goto status_fail; - } - - break; - } - - sd_init_power(chip); - - sd_dummy_clock(chip); - } - - dev_dbg(rtsx_dev(chip), "Normal card!\n"); - } - - /* Start Initialization Process of SD Card */ -RTY_SD_RST: - retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0, - NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - wait_timeout(20); - - retval = sd_send_cmd_get_rsp(chip, SEND_IF_COND, 0x000001AA, - SD_RSP_TYPE_R7, rsp, 5); - if (retval == STATUS_SUCCESS) { - if (rsp[4] == 0xAA && ((rsp[3] & 0x0f) == 0x01)) { - hi_cap_flow = true; - voltage = SUPPORT_VOLTAGE | 0x40000000; - } - } - - if (!hi_cap_flow) { - voltage = SUPPORT_VOLTAGE; - - retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, - SD_RSP_TYPE_R0, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - wait_timeout(20); - } - - do { - retval = sd_send_cmd_get_rsp(chip, APP_CMD, 0, SD_RSP_TYPE_R1, - NULL, 0); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - goto status_fail; - } - - j++; - if (j < 3) - goto RTY_SD_RST; - else - goto status_fail; - } - - retval = sd_send_cmd_get_rsp(chip, SD_APP_OP_COND, voltage, - SD_RSP_TYPE_R3, rsp, 5); - if (retval != STATUS_SUCCESS) { - k++; - if (k < 3) - goto RTY_SD_RST; - else - goto status_fail; - } - - i++; - wait_timeout(20); - } while (!(rsp[1] & 0x80) && (i < 255)); - - if (i == 255) - goto status_fail; - - if (hi_cap_flow) { - if (rsp[1] & 0x40) - SET_SD_HCXC(sd_card); - else - CLR_SD_HCXC(sd_card); - - support_1v8 = false; - } else { - CLR_SD_HCXC(sd_card); - support_1v8 = false; - } - dev_dbg(rtsx_dev(chip), "support_1v8 = %d\n", support_1v8); - - if (support_1v8) { - retval = sd_voltage_switch(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - } - - retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2, - NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - for (i = 0; i < 3; i++) { - retval = sd_send_cmd_get_rsp(chip, SEND_RELATIVE_ADDR, 0, - SD_RSP_TYPE_R6, rsp, 5); - if (retval != STATUS_SUCCESS) - goto status_fail; - - sd_card->sd_addr = (u32)rsp[1] << 24; - sd_card->sd_addr += (u32)rsp[2] << 16; - - if (sd_card->sd_addr) - break; - } - - retval = sd_check_csd(chip, 1); - if (retval != STATUS_SUCCESS) - goto status_fail; - - retval = sd_select_card(chip, 1); - if (retval != STATUS_SUCCESS) - goto status_fail; - -#ifdef SUPPORT_SD_LOCK -SD_UNLOCK_ENTRY: - retval = sd_update_lock_status(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - if (sd_card->sd_lock_status & SD_LOCKED) { - sd_card->sd_lock_status |= (SD_LOCK_1BIT_MODE | SD_PWD_EXIST); - return STATUS_SUCCESS; - } else if (!(sd_card->sd_lock_status & SD_UNLOCK_POW_ON)) { - sd_card->sd_lock_status &= ~SD_PWD_EXIST; - } -#endif - - retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - retval = sd_send_cmd_get_rsp(chip, SET_CLR_CARD_DETECT, 0, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - if (support_1v8) { - retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - switch_bus_width = SD_BUS_WIDTH_4; - } else { - switch_bus_width = SD_BUS_WIDTH_1; - } - - retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1, - NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - if (!(sd_card->raw_csd[4] & 0x40)) - sd_dont_switch = true; - - if (!sd_dont_switch) { - if (sd20_mode) { - /* Set sd_switch_fail here, because we needn't - * switch to UHS mode - */ - sd_card->sd_switch_fail = SDR104_SUPPORT_MASK | - DDR50_SUPPORT_MASK | SDR50_SUPPORT_MASK; - } - - /* Check the card whether follow SD1.1 spec or higher */ - retval = sd_check_spec(chip, switch_bus_width); - if (retval == STATUS_SUCCESS) { - retval = sd_switch_function(chip, switch_bus_width); - if (retval != STATUS_SUCCESS) { - sd_init_power(chip); - sd_dont_switch = true; - try_sdio = false; - - goto switch_fail; - } - } else { - if (support_1v8) { - sd_init_power(chip); - sd_dont_switch = true; - try_sdio = false; - - goto switch_fail; - } - } - } - - if (!support_1v8) { - retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - - retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2, - SD_RSP_TYPE_R1, NULL, 0); - if (retval != STATUS_SUCCESS) - goto status_fail; - } - -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE; -#endif - - if (!sd20_mode && CHK_SD30_SPEED(sd_card)) { - int read_lba0 = 1; - - retval = rtsx_write_register(chip, SD30_DRIVE_SEL, 0x07, - chip->sd30_drive_sel_1v8); - if (retval) - return retval; - - retval = sd_set_init_para(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - if (CHK_SD_DDR50(sd_card)) - retval = sd_ddr_tuning(chip); - else - retval = sd_sdr_tuning(chip); - - if (retval != STATUS_SUCCESS) { - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - try_sdio = false; - sd20_mode = true; - goto switch_fail; - } - - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - - if (CHK_SD_DDR50(sd_card)) { - retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000); - if (retval != STATUS_SUCCESS) - read_lba0 = 0; - } - - if (read_lba0) { - retval = sd_read_lba0(chip); - if (retval != STATUS_SUCCESS) { - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - try_sdio = false; - sd20_mode = true; - goto switch_fail; - } - } - } - - retval = sd_check_wp_state(chip); - if (retval != STATUS_SUCCESS) - goto status_fail; - - chip->card_bus_width[chip->card2lun[SD_CARD]] = 4; - -#ifdef SUPPORT_SD_LOCK - if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) { - retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_H, 0xFF, - 0x02); - if (retval) - return retval; - retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_L, 0xFF, - 0x00); - if (retval) - return retval; - } -#endif - - return STATUS_SUCCESS; - -status_fail: - return STATUS_FAIL; -} - -static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 buf[8] = {0}, bus_width, *ptr; - u16 byte_cnt; - int len; - - retval = sd_send_cmd_get_rsp(chip, BUSTEST_W, 0, SD_RSP_TYPE_R1, NULL, - 0); - if (retval != STATUS_SUCCESS) - return SWITCH_FAIL; - - if (width == MMC_8BIT_BUS) { - buf[0] = 0x55; - buf[1] = 0xAA; - len = 8; - byte_cnt = 8; - bus_width = SD_BUS_WIDTH_8; - } else { - buf[0] = 0x5A; - len = 4; - byte_cnt = 4; - bus_width = SD_BUS_WIDTH_4; - } - - retval = rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0x02); - if (retval != STATUS_SUCCESS) - return SWITCH_ERR; - - retval = sd_write_data(chip, SD_TM_AUTO_WRITE_3, NULL, 0, byte_cnt, 1, - bus_width, buf, len, 100); - if (retval != STATUS_SUCCESS) { - rtsx_clear_sd_error(chip); - rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0); - return SWITCH_ERR; - } - - retval = rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0); - if (retval != STATUS_SUCCESS) - return SWITCH_ERR; - - dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", BUSTEST_R); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | BUSTEST_R); - - if (width == MMC_8BIT_BUS) - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, - 0xFF, 0x08); - else - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, - 0xFF, 0x04); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, SD_CALCULATE_CRC7 | - SD_NO_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | - SD_CHECK_CRC7 | SD_RSP_LEN_6); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, - PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_NORMAL_READ | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, - SD_TRANSFER_END); - - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2, 0, 0); - if (width == MMC_8BIT_BUS) - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 1, 0, 0); - - retval = rtsx_send_cmd(chip, SD_CARD, 100); - if (retval < 0) { - rtsx_clear_sd_error(chip); - return SWITCH_ERR; - } - - ptr = rtsx_get_cmd_data(chip) + 1; - - if (width == MMC_8BIT_BUS) { - dev_dbg(rtsx_dev(chip), "BUSTEST_R [8bits]: 0x%02x 0x%02x\n", - ptr[0], ptr[1]); - if (ptr[0] == 0xAA && ptr[1] == 0x55) { - u8 rsp[5]; - u32 arg; - - if (CHK_MMC_DDR52(sd_card)) - arg = 0x03B70600; - else - arg = 0x03B70200; - - retval = sd_send_cmd_get_rsp(chip, SWITCH, arg, - SD_RSP_TYPE_R1b, rsp, 5); - if (retval == STATUS_SUCCESS && - !(rsp[4] & MMC_SWITCH_ERR)) - return SWITCH_SUCCESS; - } - } else { - dev_dbg(rtsx_dev(chip), "BUSTEST_R [4bits]: 0x%02x\n", ptr[0]); - if (ptr[0] == 0xA5) { - u8 rsp[5]; - u32 arg; - - if (CHK_MMC_DDR52(sd_card)) - arg = 0x03B70500; - else - arg = 0x03B70100; - - retval = sd_send_cmd_get_rsp(chip, SWITCH, arg, - SD_RSP_TYPE_R1b, rsp, 5); - if (retval == STATUS_SUCCESS && - !(rsp[4] & MMC_SWITCH_ERR)) - return SWITCH_SUCCESS; - } - } - - return SWITCH_FAIL; -} - -static int mmc_switch_timing_bus(struct rtsx_chip *chip, bool switch_ddr) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - u8 *ptr, card_type, card_type_mask = 0; - - CLR_MMC_HS(sd_card); - - dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", SEND_EXT_CSD); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, - 0x40 | SEND_EXT_CSD); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, 0); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, 2); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | - SD_CHECK_CRC7 | SD_RSP_LEN_6); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, - PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_NORMAL_READ | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, - SD_TRANSFER_END); - - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 196, 0xFF, 0); - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 212, 0xFF, 0); - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 213, 0xFF, 0); - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 214, 0xFF, 0); - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 215, 0xFF, 0); - - retval = rtsx_send_cmd(chip, SD_CARD, 1000); - if (retval < 0) { - if (retval == -ETIMEDOUT) { - rtsx_clear_sd_error(chip); - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - } - return STATUS_FAIL; - } - - ptr = rtsx_get_cmd_data(chip); - if (ptr[0] & SD_TRANSFER_ERR) { - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - return STATUS_FAIL; - } - - if (CHK_MMC_SECTOR_MODE(sd_card)) { - sd_card->capacity = ((u32)ptr[5] << 24) | ((u32)ptr[4] << 16) | - ((u32)ptr[3] << 8) | ((u32)ptr[2]); - } - - card_type_mask = 0x03; - card_type = ptr[1] & card_type_mask; - if (card_type) { - u8 rsp[5]; - - if (card_type & 0x04) { - if (switch_ddr) - SET_MMC_DDR52(sd_card); - else - SET_MMC_52M(sd_card); - } else if (card_type & 0x02) { - SET_MMC_52M(sd_card); - } else { - SET_MMC_26M(sd_card); - } - - retval = sd_send_cmd_get_rsp(chip, SWITCH, 0x03B90100, - SD_RSP_TYPE_R1b, rsp, 5); - if (retval != STATUS_SUCCESS || (rsp[4] & MMC_SWITCH_ERR)) - CLR_MMC_HS(sd_card); - } - - sd_choose_proper_clock(chip); - retval = switch_clock(chip, sd_card->sd_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - /* Test Bus Procedure */ - retval = mmc_test_switch_bus(chip, MMC_8BIT_BUS); - if (retval == SWITCH_SUCCESS) { - SET_MMC_8BIT(sd_card); - chip->card_bus_width[chip->card2lun[SD_CARD]] = 8; -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE; -#endif - } else if (retval == SWITCH_FAIL) { - retval = mmc_test_switch_bus(chip, MMC_4BIT_BUS); - if (retval == SWITCH_SUCCESS) { - SET_MMC_4BIT(sd_card); - chip->card_bus_width[chip->card2lun[SD_CARD]] = 4; -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE; -#endif - } else if (retval == SWITCH_FAIL) { - CLR_MMC_8BIT(sd_card); - CLR_MMC_4BIT(sd_card); - } else { - return STATUS_FAIL; - } - } else { - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int reset_mmc(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval, i = 0, j = 0, k = 0; - bool switch_ddr = true; - u8 rsp[16]; - u8 spec_ver = 0; - u32 temp; - -#ifdef SUPPORT_SD_LOCK - if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) - goto MMC_UNLOCK_ENTRY; -#endif - -switch_fail: - retval = sd_prepare_reset(chip); - if (retval != STATUS_SUCCESS) - return retval; - - SET_MMC(sd_card); - -RTY_MMC_RST: - retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0, - NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - do { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - return STATUS_FAIL; - } - - retval = sd_send_cmd_get_rsp(chip, SEND_OP_COND, - (SUPPORT_VOLTAGE | 0x40000000), - SD_RSP_TYPE_R3, rsp, 5); - if (retval != STATUS_SUCCESS) { - if (sd_check_err_code(chip, SD_BUSY) || - sd_check_err_code(chip, SD_TO_ERR)) { - k++; - if (k < 20) { - sd_clr_err_code(chip); - goto RTY_MMC_RST; - } else { - return STATUS_FAIL; - } - } else { - j++; - if (j < 100) { - sd_clr_err_code(chip); - goto RTY_MMC_RST; - } else { - return STATUS_FAIL; - } - } - } - - wait_timeout(20); - i++; - } while (!(rsp[1] & 0x80) && (i < 255)); - - if (i == 255) - return STATUS_FAIL; - - if ((rsp[1] & 0x60) == 0x40) - SET_MMC_SECTOR_MODE(sd_card); - else - CLR_MMC_SECTOR_MODE(sd_card); - - retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2, - NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - sd_card->sd_addr = 0x00100000; - retval = sd_send_cmd_get_rsp(chip, SET_RELATIVE_ADDR, sd_card->sd_addr, - SD_RSP_TYPE_R6, rsp, 5); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sd_check_csd(chip, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - spec_ver = (sd_card->raw_csd[0] & 0x3C) >> 2; - - retval = sd_select_card(chip, 1); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1, - NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - -#ifdef SUPPORT_SD_LOCK -MMC_UNLOCK_ENTRY: - retval = sd_update_lock_status(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; -#endif - - retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - chip->card_bus_width[chip->card2lun[SD_CARD]] = 1; - - if (!sd_card->mmc_dont_switch_bus) { - if (spec_ver == 4) { - /* MMC 4.x Cards */ - retval = mmc_switch_timing_bus(chip, switch_ddr); - if (retval != STATUS_SUCCESS) { - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - sd_card->mmc_dont_switch_bus = 1; - goto switch_fail; - } - } - - if (CHK_MMC_SECTOR_MODE(sd_card) && sd_card->capacity == 0) - return STATUS_FAIL; - - if (switch_ddr && CHK_MMC_DDR52(sd_card)) { - retval = sd_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = mmc_ddr_tuning(chip); - if (retval != STATUS_SUCCESS) { - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - switch_ddr = false; - goto switch_fail; - } - - retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000); - if (retval == STATUS_SUCCESS) { - retval = sd_read_lba0(chip); - if (retval != STATUS_SUCCESS) { - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - switch_ddr = false; - goto switch_fail; - } - } - } - } - -#ifdef SUPPORT_SD_LOCK - if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) { - retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_H, 0xFF, - 0x02); - if (retval) - return retval; - retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_L, 0xFF, - 0x00); - if (retval) - return retval; - } -#endif - - temp = rtsx_readl(chip, RTSX_BIPR); - if (temp & SD_WRITE_PROTECT) - chip->card_wp |= SD_CARD; - - return STATUS_SUCCESS; -} - -int reset_sd_card(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - sd_init_reg_addr(chip); - - memset(sd_card, 0, sizeof(struct sd_info)); - chip->capacity[chip->card2lun[SD_CARD]] = 0; - - retval = enable_card_clock(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (chip->ignore_sd && CHK_SDIO_EXIST(chip) && - !CHK_SDIO_IGNORED(chip)) { - if (chip->asic_code) { - retval = sd_pull_ctl_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, - FPGA_SD_PULL_CTL_BIT | - 0x20, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - retval = card_share_mode(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - chip->sd_io = 1; - return STATUS_FAIL; - } - - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (chip->sd_ctl & RESET_MMC_FIRST) { - retval = reset_mmc(chip); - if (retval != STATUS_SUCCESS) { - if (sd_check_err_code(chip, SD_NO_CARD)) - return STATUS_FAIL; - - retval = reset_sd(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - } else { - retval = reset_sd(chip); - if (retval != STATUS_SUCCESS) { - if (sd_check_err_code(chip, SD_NO_CARD)) - return STATUS_FAIL; - - if (chip->sd_io) - return STATUS_FAIL; - retval = reset_mmc(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - } - - retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_L, 0xFF, 0); - if (retval) - return retval; - retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_H, 0xFF, 2); - if (retval) - return retval; - - chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity; - - retval = sd_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "sd_card->sd_type = 0x%x\n", sd_card->sd_type); - - return STATUS_SUCCESS; -} - -static int reset_mmc_only(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - sd_card->sd_type = 0; - sd_card->seq_mode = 0; - sd_card->sd_data_buf_ready = 0; - sd_card->capacity = 0; - sd_card->sd_switch_fail = 0; - -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status = 0; - sd_card->sd_erase_status = 0; -#endif - - chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity = 0; - - retval = enable_card_clock(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sd_init_power(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = reset_mmc(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_L, 0xFF, 0); - if (retval) - return retval; - retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_H, 0xFF, 2); - if (retval) - return retval; - - chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity; - - retval = sd_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "In %s, sd_card->sd_type = 0x%x\n", - __func__, sd_card->sd_type); - - return STATUS_SUCCESS; -} - -#define WAIT_DATA_READY_RTY_CNT 255 - -static int wait_data_buf_ready(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int i, retval; - - for (i = 0; i < WAIT_DATA_READY_RTY_CNT; i++) { - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_NO_CARD); - return STATUS_FAIL; - } - - sd_card->sd_data_buf_ready = 0; - - retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, - sd_card->sd_addr, SD_RSP_TYPE_R1, - NULL, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (sd_card->sd_data_buf_ready) { - return sd_send_cmd_get_rsp(chip, SEND_STATUS, - sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0); - } - } - - sd_set_err_code(chip, SD_TO_ERR); - - return STATUS_FAIL; -} - -void sd_stop_seq_mode(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - if (sd_card->seq_mode) { - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return; - - retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0, - SD_RSP_TYPE_R1b, NULL, 0); - if (retval != STATUS_SUCCESS) - sd_set_err_code(chip, SD_STS_ERR); - - retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000); - if (retval != STATUS_SUCCESS) - sd_set_err_code(chip, SD_STS_ERR); - - sd_card->seq_mode = 0; - - rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH); - } -} - -static inline int sd_auto_tune_clock(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - if (chip->asic_code) { - if (sd_card->sd_clock > 30) - sd_card->sd_clock -= 20; - } else { - switch (sd_card->sd_clock) { - case CLK_200: - sd_card->sd_clock = CLK_150; - break; - - case CLK_150: - sd_card->sd_clock = CLK_120; - break; - - case CLK_120: - sd_card->sd_clock = CLK_100; - break; - - case CLK_100: - sd_card->sd_clock = CLK_80; - break; - - case CLK_80: - sd_card->sd_clock = CLK_60; - break; - - case CLK_60: - sd_card->sd_clock = CLK_50; - break; - - default: - break; - } - } - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, - u16 sector_cnt) -{ - struct sd_info *sd_card = &chip->sd_card; - u32 data_addr; - u8 cfg2; - int retval; - - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - dev_dbg(rtsx_dev(chip), "%s: Read %d %s from 0x%x\n", __func__, - sector_cnt, (sector_cnt > 1) ? "sectors" : "sector", - start_sector); - } else { - dev_dbg(rtsx_dev(chip), "%s: Write %d %s to 0x%x\n", __func__, - sector_cnt, (sector_cnt > 1) ? "sectors" : "sector", - start_sector); - } - - sd_card->cleanup_counter = 0; - - if (!(chip->card_ready & SD_CARD)) { - sd_card->seq_mode = 0; - - retval = reset_sd_card(chip); - if (retval == STATUS_SUCCESS) { - chip->card_ready |= SD_CARD; - chip->card_fail &= ~SD_CARD; - } else { - chip->card_ready &= ~SD_CARD; - chip->card_fail |= SD_CARD; - chip->capacity[chip->card2lun[SD_CARD]] = 0; - chip->rw_need_retry = 1; - return STATUS_FAIL; - } - } - - if (!CHK_SD_HCXC(sd_card) && !CHK_MMC_SECTOR_MODE(sd_card)) - data_addr = start_sector << 9; - else - data_addr = start_sector; - - sd_clr_err_code(chip); - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_IO_ERR); - goto RW_FAIL; - } - - if (sd_card->seq_mode && - (sd_card->pre_dir != srb->sc_data_direction || - ((sd_card->pre_sec_addr + sd_card->pre_sec_cnt) != - start_sector))) { - if (sd_card->pre_sec_cnt < 0x80 && - sd_card->pre_dir == DMA_FROM_DEVICE && - !CHK_SD30_SPEED(sd_card) && - !CHK_SD_HS(sd_card) && - !CHK_MMC_HS(sd_card)) { - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - } - - retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0, - SD_RSP_TYPE_R1b, NULL, 0); - if (retval != STATUS_SUCCESS) { - chip->rw_need_retry = 1; - sd_set_err_code(chip, SD_STS_ERR); - goto RW_FAIL; - } - - sd_card->seq_mode = 0; - - retval = rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH); - if (retval != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_IO_ERR); - goto RW_FAIL; - } - - if (sd_card->pre_sec_cnt < 0x80 && - !CHK_SD30_SPEED(sd_card) && - !CHK_SD_HS(sd_card) && - !CHK_MMC_HS(sd_card)) { - sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0); - } - } - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0x00); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, 0x02); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, - (u8)sector_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, - (u8)(sector_cnt >> 8)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - - if (CHK_MMC_8BIT(sd_card)) - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, - 0x03, SD_BUS_WIDTH_8); - else if (CHK_MMC_4BIT(sd_card) || CHK_SD(sd_card)) - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, - 0x03, SD_BUS_WIDTH_4); - else - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, - 0x03, SD_BUS_WIDTH_1); - - if (sd_card->seq_mode) { - cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | - SD_RSP_LEN_0; - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, cfg2); - - trans_dma_enable(srb->sc_data_direction, chip, sector_cnt * 512, - DMA_512); - - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_AUTO_READ_3 | SD_TRANSFER_START); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START); - } - - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - } else { - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", - READ_MULTIPLE_BLOCK); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, - 0x40 | READ_MULTIPLE_BLOCK); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, - (u8)(data_addr >> 24)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, - (u8)(data_addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, - (u8)(data_addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, - (u8)data_addr); - - cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | - SD_RSP_LEN_6; - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, - cfg2); - - trans_dma_enable(srb->sc_data_direction, chip, - sector_cnt * 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_AUTO_READ_2 | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - } else { - retval = rtsx_send_cmd(chip, SD_CARD, 50); - if (retval < 0) { - rtsx_clear_sd_error(chip); - - chip->rw_need_retry = 1; - sd_set_err_code(chip, SD_TO_ERR); - goto RW_FAIL; - } - - retval = wait_data_buf_ready(chip); - if (retval != STATUS_SUCCESS) { - chip->rw_need_retry = 1; - sd_set_err_code(chip, SD_TO_ERR); - goto RW_FAIL; - } - - retval = sd_send_cmd_get_rsp(chip, WRITE_MULTIPLE_BLOCK, - data_addr, SD_RSP_TYPE_R1, - NULL, 0); - if (retval != STATUS_SUCCESS) { - chip->rw_need_retry = 1; - goto RW_FAIL; - } - - rtsx_init_cmd(chip); - - cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | - SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, - cfg2); - - trans_dma_enable(srb->sc_data_direction, chip, - sector_cnt * 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - } - - sd_card->seq_mode = 1; - } - - retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb), - scsi_bufflen(srb), scsi_sg_count(srb), - srb->sc_data_direction, chip->sd_timeout); - if (retval < 0) { - u8 stat = 0; - int err; - - sd_card->seq_mode = 0; - - if (retval == -ETIMEDOUT) - err = STATUS_TIMEDOUT; - else - err = STATUS_FAIL; - - rtsx_read_register(chip, REG_SD_STAT1, &stat); - rtsx_clear_sd_error(chip); - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - chip->rw_need_retry = 0; - dev_dbg(rtsx_dev(chip), "No card exist, exit %s\n", - __func__); - return STATUS_FAIL; - } - - chip->rw_need_retry = 1; - - retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0, - SD_RSP_TYPE_R1b, NULL, 0); - if (retval != STATUS_SUCCESS) { - sd_set_err_code(chip, SD_STS_ERR); - goto RW_FAIL; - } - - if (stat & (SD_CRC7_ERR | SD_CRC16_ERR | SD_CRC_WRITE_ERR)) { - dev_dbg(rtsx_dev(chip), "SD CRC error, tune clock!\n"); - sd_set_err_code(chip, SD_CRC_ERR); - goto RW_FAIL; - } - - if (err == STATUS_TIMEDOUT) { - sd_set_err_code(chip, SD_TO_ERR); - goto RW_FAIL; - } - - return err; - } - - sd_card->pre_sec_addr = start_sector; - sd_card->pre_sec_cnt = sector_cnt; - sd_card->pre_dir = srb->sc_data_direction; - - return STATUS_SUCCESS; - -RW_FAIL: - sd_card->seq_mode = 0; - - if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) { - chip->rw_need_retry = 0; - dev_dbg(rtsx_dev(chip), "No card exist, exit %s\n", __func__); - return STATUS_FAIL; - } - - if (sd_check_err_code(chip, SD_CRC_ERR)) { - if (CHK_MMC_4BIT(sd_card) || CHK_MMC_8BIT(sd_card)) { - sd_card->mmc_dont_switch_bus = 1; - reset_mmc_only(chip); - sd_card->mmc_dont_switch_bus = 0; - } else { - sd_card->need_retune = 1; - sd_auto_tune_clock(chip); - } - } else if (sd_check_err_code(chip, SD_TO_ERR | SD_STS_ERR)) { - retval = reset_sd_card(chip); - if (retval != STATUS_SUCCESS) { - chip->card_ready &= ~SD_CARD; - chip->card_fail |= SD_CARD; - chip->capacity[chip->card2lun[SD_CARD]] = 0; - } - } - - return STATUS_FAIL; -} - -#ifdef SUPPORT_CPRM -int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx, u32 arg, - u8 rsp_type, u8 *rsp, int rsp_len, - bool special_check) -{ - int retval; - int timeout = 100; - u16 reg_addr; - u8 *ptr; - int stat_idx = 0; - int rty_cnt = 0; - - dev_dbg(rtsx_dev(chip), "EXT SD/MMC CMD %d\n", cmd_idx); - - if (rsp_type == SD_RSP_TYPE_R1b) - timeout = 3000; - -RTY_SEND_CMD: - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | cmd_idx); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, (u8)(arg >> 24)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, (u8)(arg >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, (u8)(arg >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, (u8)arg); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, - 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, - SD_TRANSFER_END); - - if (rsp_type == SD_RSP_TYPE_R2) { - for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0); - - stat_idx = 17; - } else if (rsp_type != SD_RSP_TYPE_R0) { - for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0); - - stat_idx = 6; - } - rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0, 0); - - rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_STAT1, 0, 0); - - retval = rtsx_send_cmd(chip, SD_CARD, timeout); - if (retval < 0) { - if (retval == -ETIMEDOUT) { - rtsx_clear_sd_error(chip); - - if (rsp_type & SD_WAIT_BUSY_END) { - retval = sd_check_data0_status(chip); - if (retval != STATUS_SUCCESS) - return retval; - } else { - sd_set_err_code(chip, SD_TO_ERR); - } - } - return STATUS_FAIL; - } - - if (rsp_type == SD_RSP_TYPE_R0) - return STATUS_SUCCESS; - - ptr = rtsx_get_cmd_data(chip) + 1; - - if ((ptr[0] & 0xC0) != 0) { - sd_set_err_code(chip, SD_STS_ERR); - return STATUS_FAIL; - } - - if (!(rsp_type & SD_NO_CHECK_CRC7)) { - if (ptr[stat_idx] & SD_CRC7_ERR) { - if (cmd_idx == WRITE_MULTIPLE_BLOCK) { - sd_set_err_code(chip, SD_CRC_ERR); - return STATUS_FAIL; - } - if (rty_cnt < SD_MAX_RETRY_COUNT) { - wait_timeout(20); - rty_cnt++; - goto RTY_SEND_CMD; - } else { - sd_set_err_code(chip, SD_CRC_ERR); - return STATUS_FAIL; - } - } - } - - if (cmd_idx == SELECT_CARD || cmd_idx == APP_CMD || - cmd_idx == SEND_STATUS || cmd_idx == STOP_TRANSMISSION) { - if (cmd_idx != STOP_TRANSMISSION && !special_check) { - if (ptr[1] & 0x80) - return STATUS_FAIL; - } -#ifdef SUPPORT_SD_LOCK - if (ptr[1] & 0x7D) { -#else - if (ptr[1] & 0x7F) { -#endif - return STATUS_FAIL; - } - if (ptr[2] & 0xF8) - return STATUS_FAIL; - - if (cmd_idx == SELECT_CARD) { - if (rsp_type == SD_RSP_TYPE_R2) { - if ((ptr[3] & 0x1E) != 0x04) - return STATUS_FAIL; - } - } - } - - if (rsp && rsp_len) - memcpy(rsp, ptr, rsp_len); - - return STATUS_SUCCESS; -} - -int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type) -{ - int retval, rsp_len; - u16 reg_addr; - - if (rsp_type == SD_RSP_TYPE_R0) - return STATUS_SUCCESS; - - rtsx_init_cmd(chip); - - if (rsp_type == SD_RSP_TYPE_R2) { - for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0); - - rsp_len = 17; - } else if (rsp_type != SD_RSP_TYPE_R0) { - for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; - reg_addr++) - rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0); - - rsp_len = 6; - } - rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0xFF, 0); - - retval = rtsx_send_cmd(chip, SD_CARD, 100); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (rsp) { - int min_len = (rsp_len < len) ? rsp_len : len; - - memcpy(rsp, rtsx_get_cmd_data(chip), min_len); - - dev_dbg(rtsx_dev(chip), "min_len = %d\n", min_len); - dev_dbg(rtsx_dev(chip), "Response in cmd buf: 0x%x 0x%x 0x%x 0x%x\n", - rsp[0], rsp[1], rsp[2], rsp[3]); - } - - return STATUS_SUCCESS; -} - -int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - int len; - u8 buf[18] = { - 0x00, - 0x00, - 0x00, - 0x0E, - 0x00, - 0x00, - 0x00, - 0x00, - 0x53, - 0x44, - 0x20, - 0x43, - 0x61, - 0x72, - 0x64, - 0x00, - 0x00, - 0x00, - }; - - sd_card->pre_cmd_err = 0; - - if (!(CHK_BIT(chip->lun_mc, lun))) { - SET_BIT(chip->lun_mc, lun); - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - if (srb->cmnd[2] != 0x53 || srb->cmnd[3] != 0x44 || - srb->cmnd[4] != 0x20 || srb->cmnd[5] != 0x43 || - srb->cmnd[6] != 0x61 || srb->cmnd[7] != 0x72 || - srb->cmnd[8] != 0x64) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - switch (srb->cmnd[1] & 0x0F) { - case 0: - sd_card->sd_pass_thru_en = 0; - break; - - case 1: - sd_card->sd_pass_thru_en = 1; - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - buf[5] = (CHK_SD(sd_card) == 1) ? 0x01 : 0x02; - if (chip->card_wp & SD_CARD) - buf[5] |= 0x80; - - buf[6] = (u8)(sd_card->sd_addr >> 16); - buf[7] = (u8)(sd_card->sd_addr >> 24); - - buf[15] = chip->max_lun; - - len = min_t(int, 18, scsi_bufflen(srb)); - rtsx_stor_set_xfer_buf(buf, len, srb); - - return TRANSPORT_GOOD; -} - -static inline int get_rsp_type(struct scsi_cmnd *srb, u8 *rsp_type, - int *rsp_len) -{ - if (!rsp_type || !rsp_len) - return STATUS_FAIL; - - switch (srb->cmnd[10]) { - case 0x03: - *rsp_type = SD_RSP_TYPE_R0; - *rsp_len = 0; - break; - - case 0x04: - *rsp_type = SD_RSP_TYPE_R1; - *rsp_len = 6; - break; - - case 0x05: - *rsp_type = SD_RSP_TYPE_R1b; - *rsp_len = 6; - break; - - case 0x06: - *rsp_type = SD_RSP_TYPE_R2; - *rsp_len = 17; - break; - - case 0x07: - *rsp_type = SD_RSP_TYPE_R3; - *rsp_len = 6; - break; - - default: - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - int retval, rsp_len; - u8 cmd_idx, rsp_type; - bool standby = false, acmd = false; - u32 arg; - - if (!sd_card->sd_pass_thru_en) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - if (sd_card->pre_cmd_err) { - sd_card->pre_cmd_err = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - cmd_idx = srb->cmnd[2] & 0x3F; - if (srb->cmnd[1] & 0x02) - standby = true; - - if (srb->cmnd[1] & 0x01) - acmd = true; - - arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) | - ((u32)srb->cmnd[5] << 8) | srb->cmnd[6]; - - retval = get_rsp_type(srb, &rsp_type, &rsp_len); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - sd_card->last_rsp_type = rsp_type; - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - -#ifdef SUPPORT_SD_LOCK - if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) { - if (CHK_MMC_8BIT(sd_card)) { - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, - SD_BUS_WIDTH_8); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) { - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, - SD_BUS_WIDTH_4); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - } - } -#else - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; -#endif - - if (standby) { - retval = sd_select_card(chip, 0); - if (retval != STATUS_SUCCESS) - goto sd_execute_cmd_failed; - } - - if (acmd) { - retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD, - sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_cmd_failed; - } - - retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type, - sd_card->rsp, rsp_len, false); - if (retval != STATUS_SUCCESS) - goto sd_execute_cmd_failed; - - if (standby) { - retval = sd_select_card(chip, 1); - if (retval != STATUS_SUCCESS) - goto sd_execute_cmd_failed; - } - -#ifdef SUPPORT_SD_LOCK - retval = sd_update_lock_status(chip); - if (retval != STATUS_SUCCESS) - goto sd_execute_cmd_failed; -#endif - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; - -sd_execute_cmd_failed: - sd_card->pre_cmd_err = 1; - set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); - release_sd_card(chip); - do_reset_sd_card(chip); - if (!(chip->card_ready & SD_CARD)) - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - - return TRANSPORT_FAILED; -} - -int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - int retval, rsp_len, i; - bool read_err = false, cmd13_checkbit = false; - u8 cmd_idx, rsp_type, bus_width; - bool standby = false, send_cmd12 = false, acmd = false; - u32 data_len; - - if (!sd_card->sd_pass_thru_en) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (sd_card->pre_cmd_err) { - sd_card->pre_cmd_err = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - cmd_idx = srb->cmnd[2] & 0x3F; - if (srb->cmnd[1] & 0x04) - send_cmd12 = true; - - if (srb->cmnd[1] & 0x02) - standby = true; - - if (srb->cmnd[1] & 0x01) - acmd = true; - - data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8] - << 8) | srb->cmnd[9]; - - retval = get_rsp_type(srb, &rsp_type, &rsp_len); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - sd_card->last_rsp_type = rsp_type; - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - -#ifdef SUPPORT_SD_LOCK - if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) { - if (CHK_MMC_8BIT(sd_card)) - bus_width = SD_BUS_WIDTH_8; - else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) - bus_width = SD_BUS_WIDTH_4; - else - bus_width = SD_BUS_WIDTH_1; - } else { - bus_width = SD_BUS_WIDTH_4; - } - dev_dbg(rtsx_dev(chip), "bus_width = %d\n", bus_width); -#else - bus_width = SD_BUS_WIDTH_4; -#endif - - if (data_len < 512) { - retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - } - - if (standby) { - retval = sd_select_card(chip, 0); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - } - - if (acmd) { - retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD, - sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - } - - if (data_len <= 512) { - int min_len; - u8 *buf; - u16 byte_cnt, blk_cnt; - u8 cmd[5]; - - byte_cnt = ((u16)(srb->cmnd[8] & 0x03) << 8) | srb->cmnd[9]; - blk_cnt = 1; - - cmd[0] = 0x40 | cmd_idx; - cmd[1] = srb->cmnd[3]; - cmd[2] = srb->cmnd[4]; - cmd[3] = srb->cmnd[5]; - cmd[4] = srb->cmnd[6]; - - buf = kmalloc(data_len, GFP_KERNEL); - if (!buf) - return TRANSPORT_ERROR; - - retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, byte_cnt, - blk_cnt, bus_width, buf, data_len, 2000); - if (retval != STATUS_SUCCESS) { - read_err = true; - kfree(buf); - rtsx_clear_sd_error(chip); - goto sd_execute_read_cmd_failed; - } - - min_len = min(data_len, scsi_bufflen(srb)); - rtsx_stor_set_xfer_buf(buf, min_len, srb); - - kfree(buf); - } else if (!(data_len & 0x1FF)) { - rtsx_init_cmd(chip); - - trans_dma_enable(DMA_FROM_DEVICE, chip, data_len, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, - 0x02); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, - 0x00); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, - 0xFF, (srb->cmnd[7] & 0xFE) >> 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, - 0xFF, (u8)((data_len & 0x0001FE00) >> 9)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, - 0x40 | cmd_idx); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, - srb->cmnd[3]); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, - srb->cmnd[4]); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, - srb->cmnd[5]); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, - srb->cmnd[6]); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, - 0xFF, SD_TM_AUTO_READ_2 | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb), - scsi_bufflen(srb), - scsi_sg_count(srb), - DMA_FROM_DEVICE, 10000); - if (retval < 0) { - read_err = true; - rtsx_clear_sd_error(chip); - goto sd_execute_read_cmd_failed; - } - - } else { - goto sd_execute_read_cmd_failed; - } - - retval = ext_sd_get_rsp(chip, rsp_len, sd_card->rsp, rsp_type); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - - if (standby) { - retval = sd_select_card(chip, 1); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - } - - if (send_cmd12) { - retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0, - SD_RSP_TYPE_R1b, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - } - - if (data_len < 512) { - retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - - retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - - retval = rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00); - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - } - - if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04)) - cmd13_checkbit = true; - - for (i = 0; i < 3; i++) { - retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS, - sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0, - cmd13_checkbit); - if (retval == STATUS_SUCCESS) - break; - } - if (retval != STATUS_SUCCESS) - goto sd_execute_read_cmd_failed; - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; - -sd_execute_read_cmd_failed: - sd_card->pre_cmd_err = 1; - set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); - if (read_err) - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - - release_sd_card(chip); - do_reset_sd_card(chip); - if (!(chip->card_ready & SD_CARD)) - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - - return TRANSPORT_FAILED; -} - -int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - int retval, rsp_len, i; - bool write_err = false, cmd13_checkbit = false; - u8 cmd_idx, rsp_type; - bool standby = false, send_cmd12 = false, acmd = false; - u32 data_len, arg; -#ifdef SUPPORT_SD_LOCK - int lock_cmd_fail = 0; - u8 sd_lock_state = 0; - u8 lock_cmd_type = 0; -#endif - - if (!sd_card->sd_pass_thru_en) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (sd_card->pre_cmd_err) { - sd_card->pre_cmd_err = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - cmd_idx = srb->cmnd[2] & 0x3F; - if (srb->cmnd[1] & 0x04) - send_cmd12 = true; - - if (srb->cmnd[1] & 0x02) - standby = true; - - if (srb->cmnd[1] & 0x01) - acmd = true; - - data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8] - << 8) | srb->cmnd[9]; - arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) | - ((u32)srb->cmnd[5] << 8) | srb->cmnd[6]; - -#ifdef SUPPORT_SD_LOCK - if (cmd_idx == LOCK_UNLOCK) { - sd_lock_state = sd_card->sd_lock_status; - sd_lock_state &= SD_LOCKED; - } -#endif - - retval = get_rsp_type(srb, &rsp_type, &rsp_len); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - sd_card->last_rsp_type = rsp_type; - - retval = sd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - -#ifdef SUPPORT_SD_LOCK - if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) { - if (CHK_MMC_8BIT(sd_card)) { - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, - SD_BUS_WIDTH_8); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - - } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) { - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, - SD_BUS_WIDTH_4); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; - } - } -#else - retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4); - if (retval != STATUS_SUCCESS) - return TRANSPORT_FAILED; -#endif - - if (data_len < 512) { - retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - } - - if (standby) { - retval = sd_select_card(chip, 0); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - } - - if (acmd) { - retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD, - sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - } - - retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type, - sd_card->rsp, rsp_len, false); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - - if (data_len <= 512) { - u16 i; - u8 *buf; - - buf = kmalloc(data_len, GFP_KERNEL); - if (!buf) - return TRANSPORT_ERROR; - - rtsx_stor_get_xfer_buf(buf, data_len, srb); - -#ifdef SUPPORT_SD_LOCK - if (cmd_idx == LOCK_UNLOCK) - lock_cmd_type = buf[0] & 0x0F; -#endif - - if (data_len > 256) { - rtsx_init_cmd(chip); - for (i = 0; i < 256; i++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, buf[i]); - } - retval = rtsx_send_cmd(chip, 0, 250); - if (retval != STATUS_SUCCESS) { - kfree(buf); - goto sd_execute_write_cmd_failed; - } - - rtsx_init_cmd(chip); - for (i = 256; i < data_len; i++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, buf[i]); - } - retval = rtsx_send_cmd(chip, 0, 250); - if (retval != STATUS_SUCCESS) { - kfree(buf); - goto sd_execute_write_cmd_failed; - } - } else { - rtsx_init_cmd(chip); - for (i = 0; i < data_len; i++) { - rtsx_add_cmd(chip, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, buf[i]); - } - retval = rtsx_send_cmd(chip, 0, 250); - if (retval != STATUS_SUCCESS) { - kfree(buf); - goto sd_execute_write_cmd_failed; - } - } - - kfree(buf); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, - srb->cmnd[8] & 0x03); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, - srb->cmnd[9]); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, - 0x00); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, - 0x01); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, - PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, SD_CARD, 250); - } else if (!(data_len & 0x1FF)) { - rtsx_init_cmd(chip); - - trans_dma_enable(DMA_TO_DEVICE, chip, data_len, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, - 0x02); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, - 0x00); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, - 0xFF, (srb->cmnd[7] & 0xFE) >> 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, - 0xFF, (u8)((data_len & 0x0001FE00) >> 9)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, - SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START); - rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb), - scsi_bufflen(srb), - scsi_sg_count(srb), - DMA_TO_DEVICE, 10000); - - } else { - goto sd_execute_write_cmd_failed; - } - - if (retval < 0) { - write_err = true; - rtsx_clear_sd_error(chip); - goto sd_execute_write_cmd_failed; - } - -#ifdef SUPPORT_SD_LOCK - if (cmd_idx == LOCK_UNLOCK) { - if (lock_cmd_type == SD_ERASE) { - sd_card->sd_erase_status = SD_UNDER_ERASING; - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; - } - - rtsx_init_cmd(chip); - rtsx_add_cmd(chip, CHECK_REG_CMD, 0xFD30, 0x02, 0x02); - - retval = rtsx_send_cmd(chip, SD_CARD, 250); - if (retval < 0) { - write_err = true; - rtsx_clear_sd_error(chip); - goto sd_execute_write_cmd_failed; - } - - retval = sd_update_lock_status(chip); - if (retval != STATUS_SUCCESS) { - dev_dbg(rtsx_dev(chip), "Lock command fail!\n"); - lock_cmd_fail = 1; - } - } -#endif /* SUPPORT_SD_LOCK */ - - if (standby) { - retval = sd_select_card(chip, 1); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - } - - if (send_cmd12) { - retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0, - SD_RSP_TYPE_R1b, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - } - - if (data_len < 512) { - retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, - SD_RSP_TYPE_R1, NULL, 0, - false); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - - retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - - retval = rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00); - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - } - - if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04)) - cmd13_checkbit = true; - - for (i = 0; i < 3; i++) { - retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS, - sd_card->sd_addr, - SD_RSP_TYPE_R1, NULL, 0, - cmd13_checkbit); - if (retval == STATUS_SUCCESS) - break; - } - if (retval != STATUS_SUCCESS) - goto sd_execute_write_cmd_failed; - -#ifdef SUPPORT_SD_LOCK - if (cmd_idx == LOCK_UNLOCK) { - if (!lock_cmd_fail) { - dev_dbg(rtsx_dev(chip), "lock_cmd_type = 0x%x\n", - lock_cmd_type); - if (lock_cmd_type & SD_CLR_PWD) - sd_card->sd_lock_status &= ~SD_PWD_EXIST; - - if (lock_cmd_type & SD_SET_PWD) - sd_card->sd_lock_status |= SD_PWD_EXIST; - } - - dev_dbg(rtsx_dev(chip), "sd_lock_state = 0x%x, sd_card->sd_lock_status = 0x%x\n", - sd_lock_state, sd_card->sd_lock_status); - if (sd_lock_state ^ (sd_card->sd_lock_status & SD_LOCKED)) { - sd_card->sd_lock_notify = 1; - if (sd_lock_state && - (sd_card->sd_lock_status & SD_LOCK_1BIT_MODE)) { - sd_card->sd_lock_status |= (SD_UNLOCK_POW_ON | SD_SDR_RST); - if (CHK_SD(sd_card)) { - retval = reset_sd(chip); - if (retval != STATUS_SUCCESS) { - sd_card->sd_lock_status &= - ~(SD_UNLOCK_POW_ON | SD_SDR_RST); - goto sd_execute_write_cmd_failed; - } - } - - sd_card->sd_lock_status &= ~(SD_UNLOCK_POW_ON | SD_SDR_RST); - } - } - } - - if (lock_cmd_fail) { - scsi_set_resid(srb, 0); - set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); - return TRANSPORT_FAILED; - } -#endif /* SUPPORT_SD_LOCK */ - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; - -sd_execute_write_cmd_failed: - sd_card->pre_cmd_err = 1; - set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); - if (write_err) - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); - - release_sd_card(chip); - do_reset_sd_card(chip); - if (!(chip->card_ready & SD_CARD)) - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - - return TRANSPORT_FAILED; -} - -int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - int count; - u16 data_len; - - if (!sd_card->sd_pass_thru_en) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (sd_card->pre_cmd_err) { - sd_card->pre_cmd_err = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - data_len = ((u16)srb->cmnd[7] << 8) | srb->cmnd[8]; - - if (sd_card->last_rsp_type == SD_RSP_TYPE_R0) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } else if (sd_card->last_rsp_type == SD_RSP_TYPE_R2) { - count = (data_len < 17) ? data_len : 17; - } else { - count = (data_len < 6) ? data_len : 6; - } - rtsx_stor_set_xfer_buf(sd_card->rsp, count, srb); - - dev_dbg(rtsx_dev(chip), "Response length: %d\n", data_len); - dev_dbg(rtsx_dev(chip), "Response: 0x%x 0x%x 0x%x 0x%x\n", - sd_card->rsp[0], sd_card->rsp[1], - sd_card->rsp[2], sd_card->rsp[3]); - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; -} - -int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - unsigned int lun = SCSI_LUN(srb); - int retval; - - if (!sd_card->sd_pass_thru_en) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - if (sd_card->pre_cmd_err) { - sd_card->pre_cmd_err = 0; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); - return TRANSPORT_FAILED; - } - - if (srb->cmnd[2] != 0x53 || srb->cmnd[3] != 0x44 || - srb->cmnd[4] != 0x20 || srb->cmnd[5] != 0x43 || - srb->cmnd[6] != 0x61 || srb->cmnd[7] != 0x72 || - srb->cmnd[8] != 0x64) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - switch (srb->cmnd[1] & 0x0F) { - case 0: -#ifdef SUPPORT_SD_LOCK - if (srb->cmnd[9] == 0x64) - sd_card->sd_lock_status |= SD_SDR_RST; -#endif - retval = reset_sd_card(chip); - if (retval != STATUS_SUCCESS) { -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status &= ~SD_SDR_RST; -#endif - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - sd_card->pre_cmd_err = 1; - return TRANSPORT_FAILED; - } -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status &= ~SD_SDR_RST; -#endif - break; - - case 1: - retval = reset_sd(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - sd_card->pre_cmd_err = 1; - return TRANSPORT_FAILED; - } - break; - - default: - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); - return TRANSPORT_FAILED; - } - - scsi_set_resid(srb, 0); - return TRANSPORT_GOOD; -} -#endif - -void sd_cleanup_work(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - - if (sd_card->seq_mode) { - dev_dbg(rtsx_dev(chip), "SD: stop transmission\n"); - sd_stop_seq_mode(chip); - sd_card->cleanup_counter = 0; - } -} - -int sd_power_off_card3v3(struct rtsx_chip *chip) -{ - int retval; - - retval = disable_card_clock(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0); - if (retval) - return retval; - - if (!chip->ft2_fast_mode) { - retval = card_power_off(chip, SD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - mdelay(50); - } - - if (chip->asic_code) { - retval = sd_pull_ctl_disable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, - FPGA_SD_PULL_CTL_BIT | 0x20, - FPGA_SD_PULL_CTL_BIT); - if (retval) - return retval; - } - - return STATUS_SUCCESS; -} - -int release_sd_card(struct rtsx_chip *chip) -{ - struct sd_info *sd_card = &chip->sd_card; - int retval; - - chip->card_ready &= ~SD_CARD; - chip->card_fail &= ~SD_CARD; - chip->card_wp &= ~SD_CARD; - - chip->sd_io = 0; - chip->sd_int = 0; - -#ifdef SUPPORT_SD_LOCK - sd_card->sd_lock_status = 0; - sd_card->sd_erase_status = 0; -#endif - - memset(sd_card->raw_csd, 0, 16); - memset(sd_card->raw_scr, 0, 8); - - retval = sd_power_off_card3v3(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} diff --git a/drivers/staging/rts5208/sd.h b/drivers/staging/rts5208/sd.h deleted file mode 100644 index f4ff62653b56c..0000000000000 --- a/drivers/staging/rts5208/sd.h +++ /dev/null @@ -1,289 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_SD_H -#define __REALTEK_RTSX_SD_H - -#include "rtsx_chip.h" - -#define SUPPORT_VOLTAGE 0x003C0000 - -/* Error Code */ -#define SD_NO_ERROR 0x0 -#define SD_CRC_ERR 0x80 -#define SD_TO_ERR 0x40 -#define SD_NO_CARD 0x20 -#define SD_BUSY 0x10 -#define SD_STS_ERR 0x08 -#define SD_RSP_TIMEOUT 0x04 -#define SD_IO_ERR 0x02 - -/* Return code for MMC switch bus */ -#define SWITCH_SUCCESS 0 -#define SWITCH_ERR 1 -#define SWITCH_FAIL 2 - -/* MMC/SD Command Index */ -/* Basic command (class 0) */ -#define GO_IDLE_STATE 0 -#define SEND_OP_COND 1 -#define ALL_SEND_CID 2 -#define SET_RELATIVE_ADDR 3 -#define SEND_RELATIVE_ADDR 3 -#define SET_DSR 4 -#define IO_SEND_OP_COND 5 -#define SWITCH 6 -#define SELECT_CARD 7 -#define DESELECT_CARD 7 -/* CMD8 is "SEND_EXT_CSD" for MMC4.x Spec - * while is "SEND_IF_COND" for SD 2.0 - */ -#define SEND_EXT_CSD 8 -#define SEND_IF_COND 8 - -#define SEND_CSD 9 -#define SEND_CID 10 -#define VOLTAGE_SWITCH 11 -#define READ_DAT_UTIL_STOP 11 -#define STOP_TRANSMISSION 12 -#define SEND_STATUS 13 -#define GO_INACTIVE_STATE 15 - -#define SET_BLOCKLEN 16 -#define READ_SINGLE_BLOCK 17 -#define READ_MULTIPLE_BLOCK 18 -#define SEND_TUNING_PATTERN 19 - -#define BUSTEST_R 14 -#define BUSTEST_W 19 - -#define WRITE_BLOCK 24 -#define WRITE_MULTIPLE_BLOCK 25 -#define PROGRAM_CSD 27 - -#define ERASE_WR_BLK_START 32 -#define ERASE_WR_BLK_END 33 -#define ERASE_CMD 38 - -#define LOCK_UNLOCK 42 -#define IO_RW_DIRECT 52 - -#define APP_CMD 55 -#define GEN_CMD 56 - -#define SET_BUS_WIDTH 6 -#define SD_STATUS 13 -#define SEND_NUM_WR_BLOCKS 22 -#define SET_WR_BLK_ERASE_COUNT 23 -#define SD_APP_OP_COND 41 -#define SET_CLR_CARD_DETECT 42 -#define SEND_SCR 51 - -#define SD_READ_COMPLETE 0x00 -#define SD_READ_TO 0x01 -#define SD_READ_ADVENCE 0x02 - -#define SD_CHECK_MODE 0x00 -#define SD_SWITCH_MODE 0x80 -#define SD_FUNC_GROUP_1 0x01 -#define SD_FUNC_GROUP_2 0x02 -#define SD_FUNC_GROUP_3 0x03 -#define SD_FUNC_GROUP_4 0x04 -#define SD_CHECK_SPEC_V1_1 0xFF - -#define NO_ARGUMENT 0x00 -#define CHECK_PATTERN 0x000000AA -#define VOLTAGE_SUPPLY_RANGE 0x00000100 -#define SUPPORT_HIGH_AND_EXTENDED_CAPACITY 0x40000000 -#define SUPPORT_MAX_POWER_PERMANCE 0x10000000 -#define SUPPORT_1V8 0x01000000 - -#define SWITCH_NO_ERR 0x00 -#define CARD_NOT_EXIST 0x01 -#define SPEC_NOT_SUPPORT 0x02 -#define CHECK_MODE_ERR 0x03 -#define CHECK_NOT_READY 0x04 -#define SWITCH_CRC_ERR 0x05 -#define SWITCH_MODE_ERR 0x06 -#define SWITCH_PASS 0x07 - -#ifdef SUPPORT_SD_LOCK -#define SD_ERASE 0x08 -#define SD_LOCK 0x04 -#define SD_UNLOCK 0x00 -#define SD_CLR_PWD 0x02 -#define SD_SET_PWD 0x01 - -#define SD_PWD_LEN 0x10 - -#define SD_LOCKED 0x80 -#define SD_LOCK_1BIT_MODE 0x40 -#define SD_PWD_EXIST 0x20 -#define SD_UNLOCK_POW_ON 0x01 -#define SD_SDR_RST 0x02 - -#define SD_NOT_ERASE 0x00 -#define SD_UNDER_ERASING 0x01 -#define SD_COMPLETE_ERASE 0x02 - -#define SD_RW_FORBIDDEN 0x0F - -#endif - -#define HS_SUPPORT 0x01 -#define SDR50_SUPPORT 0x02 -#define SDR104_SUPPORT 0x03 -#define DDR50_SUPPORT 0x04 - -#define HS_SUPPORT_MASK 0x02 -#define SDR50_SUPPORT_MASK 0x04 -#define SDR104_SUPPORT_MASK 0x08 -#define DDR50_SUPPORT_MASK 0x10 - -#define HS_QUERY_SWITCH_OK 0x01 -#define SDR50_QUERY_SWITCH_OK 0x02 -#define SDR104_QUERY_SWITCH_OK 0x03 -#define DDR50_QUERY_SWITCH_OK 0x04 - -#define HS_SWITCH_BUSY 0x02 -#define SDR50_SWITCH_BUSY 0x04 -#define SDR104_SWITCH_BUSY 0x08 -#define DDR50_SWITCH_BUSY 0x10 - -#define FUNCTION_GROUP1_SUPPORT_OFFSET 0x0D -#define FUNCTION_GROUP1_QUERY_SWITCH_OFFSET 0x10 -#define FUNCTION_GROUP1_CHECK_BUSY_OFFSET 0x1D - -#define DRIVING_TYPE_A 0x01 -#define DRIVING_TYPE_B 0x00 -#define DRIVING_TYPE_C 0x02 -#define DRIVING_TYPE_D 0x03 - -#define DRIVING_TYPE_A_MASK 0x02 -#define DRIVING_TYPE_B_MASK 0x01 -#define DRIVING_TYPE_C_MASK 0x04 -#define DRIVING_TYPE_D_MASK 0x08 - -#define TYPE_A_QUERY_SWITCH_OK 0x01 -#define TYPE_B_QUERY_SWITCH_OK 0x00 -#define TYPE_C_QUERY_SWITCH_OK 0x02 -#define TYPE_D_QUERY_SWITCH_OK 0x03 - -#define TYPE_A_SWITCH_BUSY 0x02 -#define TYPE_B_SWITCH_BUSY 0x01 -#define TYPE_C_SWITCH_BUSY 0x04 -#define TYPE_D_SWITCH_BUSY 0x08 - -#define FUNCTION_GROUP3_SUPPORT_OFFSET 0x09 -#define FUNCTION_GROUP3_QUERY_SWITCH_OFFSET 0x0F -#define FUNCTION_GROUP3_CHECK_BUSY_OFFSET 0x19 - -#define CURRENT_LIMIT_200 0x00 -#define CURRENT_LIMIT_400 0x01 -#define CURRENT_LIMIT_600 0x02 -#define CURRENT_LIMIT_800 0x03 - -#define CURRENT_LIMIT_200_MASK 0x01 -#define CURRENT_LIMIT_400_MASK 0x02 -#define CURRENT_LIMIT_600_MASK 0x04 -#define CURRENT_LIMIT_800_MASK 0x08 - -#define CURRENT_LIMIT_200_QUERY_SWITCH_OK 0x00 -#define CURRENT_LIMIT_400_QUERY_SWITCH_OK 0x01 -#define CURRENT_LIMIT_600_QUERY_SWITCH_OK 0x02 -#define CURRENT_LIMIT_800_QUERY_SWITCH_OK 0x03 - -#define CURRENT_LIMIT_200_SWITCH_BUSY 0x01 -#define CURRENT_LIMIT_400_SWITCH_BUSY 0x02 -#define CURRENT_LIMIT_600_SWITCH_BUSY 0x04 -#define CURRENT_LIMIT_800_SWITCH_BUSY 0x08 - -#define FUNCTION_GROUP4_SUPPORT_OFFSET 0x07 -#define FUNCTION_GROUP4_QUERY_SWITCH_OFFSET 0x0F -#define FUNCTION_GROUP4_CHECK_BUSY_OFFSET 0x17 - -#define DATA_STRUCTURE_VER_OFFSET 0x11 - -#define MAX_PHASE 31 - -#define MMC_8BIT_BUS 0x0010 -#define MMC_4BIT_BUS 0x0020 - -#define MMC_SWITCH_ERR 0x80 - -#define SD_IO_3V3 0 -#define SD_IO_1V8 1 - -#define TUNE_TX 0x00 -#define TUNE_RX 0x01 - -#define CHANGE_TX 0x00 -#define CHANGE_RX 0x01 - -#define DCM_HIGH_FREQUENCY_MODE 0x00 -#define DCM_LOW_FREQUENCY_MODE 0x01 - -#define DCM_HIGH_FREQUENCY_MODE_SET 0x0C -#define DCM_LOW_FREQUENCY_MODE_SET 0x00 - -#define MULTIPLY_BY_1 0x00 -#define MULTIPLY_BY_2 0x01 -#define MULTIPLY_BY_3 0x02 -#define MULTIPLY_BY_4 0x03 -#define MULTIPLY_BY_5 0x04 -#define MULTIPLY_BY_6 0x05 -#define MULTIPLY_BY_7 0x06 -#define MULTIPLY_BY_8 0x07 -#define MULTIPLY_BY_9 0x08 -#define MULTIPLY_BY_10 0x09 - -#define DIVIDE_BY_2 0x01 -#define DIVIDE_BY_3 0x02 -#define DIVIDE_BY_4 0x03 -#define DIVIDE_BY_5 0x04 -#define DIVIDE_BY_6 0x05 -#define DIVIDE_BY_7 0x06 -#define DIVIDE_BY_8 0x07 -#define DIVIDE_BY_9 0x08 -#define DIVIDE_BY_10 0x09 - -struct timing_phase_path { - int start; - int end; - int mid; - int len; -}; - -int sd_select_card(struct rtsx_chip *chip, int select); -int sd_pull_ctl_enable(struct rtsx_chip *chip); -int reset_sd_card(struct rtsx_chip *chip); -int sd_switch_clock(struct rtsx_chip *chip); -void sd_stop_seq_mode(struct rtsx_chip *chip); -int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 start_sector, u16 sector_cnt); -void sd_cleanup_work(struct rtsx_chip *chip); -int sd_power_off_card3v3(struct rtsx_chip *chip); -int release_sd_card(struct rtsx_chip *chip); -#ifdef SUPPORT_CPRM -int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx, - u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, - bool special_check); -int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type); - -int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip); -#endif - -#endif /* __REALTEK_RTSX_SD_H */ diff --git a/drivers/staging/rts5208/spi.c b/drivers/staging/rts5208/spi.c deleted file mode 100644 index e88fe1a998f81..0000000000000 --- a/drivers/staging/rts5208/spi.c +++ /dev/null @@ -1,906 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include - -#include "rtsx.h" -#include "spi.h" - -static inline void spi_set_err_code(struct rtsx_chip *chip, u8 err_code) -{ - struct spi_info *spi = &chip->spi; - - spi->err_code = err_code; -} - -static int spi_init(struct rtsx_chip *chip) -{ - int retval; - - retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF, - CS_POLARITY_LOW | DTO_MSB_FIRST - | SPI_MASTER | SPI_MODE0 | SPI_AUTO); - if (retval) - return retval; - retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK, - SAMPLE_DELAY_HALF); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int spi_set_init_para(struct rtsx_chip *chip) -{ - struct spi_info *spi = &chip->spi; - int retval; - - retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF, - (u8)(spi->clk_div >> 8)); - if (retval) - return retval; - retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF, - (u8)(spi->clk_div)); - if (retval) - return retval; - - retval = switch_clock(chip, spi->spi_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = select_card(chip, SPI_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN, - SPI_CLK_EN); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN, - SPI_OUTPUT_EN); - if (retval) - return retval; - - wait_timeout(10); - - retval = spi_init(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int sf_polling_status(struct rtsx_chip *chip, int msec) -{ - int retval; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, SPI_RDSR); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_POLLING_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, msec); - if (retval < 0) { - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_BUSY_ERR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sf_enable_write(struct rtsx_chip *chip, u8 ins) -{ - struct spi_info *spi = &chip->spi; - int retval; - - if (!spi->write_en) - return STATUS_SUCCESS; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_C_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) { - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int sf_disable_write(struct rtsx_chip *chip, u8 ins) -{ - struct spi_info *spi = &chip->spi; - int retval; - - if (!spi->write_en) - return STATUS_SUCCESS; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_C_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) { - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static void sf_program(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr, - u16 len) -{ - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, (u8)len); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, (u8)(len >> 8)); - if (addr_mode) { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, - (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, - (u8)(addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CADO_MODE0); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CDO_MODE0); - } - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); -} - -static int sf_erase(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr) -{ - int retval; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - if (addr_mode) { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, - (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, - (u8)(addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CA_MODE0); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_C_MODE0); - } - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) { - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int spi_init_eeprom(struct rtsx_chip *chip) -{ - int retval; - int clk; - - if (chip->asic_code) - clk = 30; - else - clk = CLK_30; - - retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF, 0x00); - if (retval) - return retval; - retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF, 0x27); - if (retval) - return retval; - - retval = switch_clock(chip, clk); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = select_card(chip, SPI_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN, - SPI_CLK_EN); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN, - SPI_OUTPUT_EN); - if (retval) - return retval; - - wait_timeout(10); - - retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF, - CS_POLARITY_HIGH | SPI_EEPROM_AUTO); - if (retval) - return retval; - retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK, - SAMPLE_DELAY_HALF); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -static int spi_eeprom_program_enable(struct rtsx_chip *chip) -{ - int retval; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x86); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x13); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CA_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -int spi_erase_eeprom_chip(struct rtsx_chip *chip) -{ - int retval; - - retval = spi_init_eeprom(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = spi_eeprom_program_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x12); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x84); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CA_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr) -{ - int retval; - - retval = spi_init_eeprom(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = spi_eeprom_program_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x07); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CA_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val) -{ - int retval; - u8 data; - - retval = spi_init_eeprom(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x06); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CADI_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) - return STATUS_FAIL; - - wait_timeout(5); - retval = rtsx_read_register(chip, SPI_DATA, &data); - if (retval) - return retval; - - if (val) - *val = data; - - retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val) -{ - int retval; - - retval = spi_init_eeprom(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = spi_eeprom_program_enable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x05); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, val); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x4E); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CA_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01); - if (retval) - return retval; - - return STATUS_SUCCESS; -} - -int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct spi_info *spi = &chip->spi; - - dev_dbg(rtsx_dev(chip), "%s: err_code = 0x%x\n", __func__, - spi->err_code); - rtsx_stor_set_xfer_buf(&spi->err_code, - min_t(int, scsi_bufflen(srb), 1), srb); - scsi_set_resid(srb, scsi_bufflen(srb) - 1); - - return STATUS_SUCCESS; -} - -int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - struct spi_info *spi = &chip->spi; - - spi_set_err_code(chip, SPI_NO_ERR); - - if (chip->asic_code) - spi->spi_clock = ((u16)(srb->cmnd[8]) << 8) | srb->cmnd[9]; - else - spi->spi_clock = srb->cmnd[3]; - - spi->clk_div = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5]; - spi->write_en = srb->cmnd[6]; - - dev_dbg(rtsx_dev(chip), "spi_clock = %d, clk_div = %d, write_en = %d\n", - spi->spi_clock, spi->clk_div, spi->write_en); - - return STATUS_SUCCESS; -} - -int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - u16 len; - u8 *buf; - - spi_set_err_code(chip, SPI_NO_ERR); - - len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; - if (len > 512) { - spi_set_err_code(chip, SPI_INVALID_COMMAND); - return STATUS_FAIL; - } - - retval = spi_set_init_para(chip); - if (retval != STATUS_SUCCESS) { - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, - PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, srb->cmnd[3]); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, srb->cmnd[4]); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, srb->cmnd[5]); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, srb->cmnd[6]); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, srb->cmnd[7]); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, srb->cmnd[8]); - - if (len == 0) { - if (srb->cmnd[9]) { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, - 0xFF, SPI_TRANSFER0_START | SPI_CA_MODE0); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, - 0xFF, SPI_TRANSFER0_START | SPI_C_MODE0); - } - } else { - if (srb->cmnd[9]) { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CADI_MODE0); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CDI_MODE0); - } - } - - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) { - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - if (len) { - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - retval = rtsx_read_ppbuf(chip, buf, len); - if (retval != STATUS_SUCCESS) { - spi_set_err_code(chip, SPI_READ_ERR); - kfree(buf); - return STATUS_FAIL; - } - - rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb); - scsi_set_resid(srb, 0); - - kfree(buf); - } - - return STATUS_SUCCESS; -} - -int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - unsigned int index = 0, offset = 0; - u8 ins, slow_read; - u32 addr; - u16 len; - u8 *buf; - - spi_set_err_code(chip, SPI_NO_ERR); - - ins = srb->cmnd[3]; - addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5]) - << 8) | srb->cmnd[6]; - len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; - slow_read = srb->cmnd[9]; - - retval = spi_set_init_para(chip); - if (retval != STATUS_SUCCESS) { - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - while (len) { - u16 pagelen = SF_PAGE_LEN - (u8)addr; - - if (pagelen > len) - pagelen = len; - - rtsx_init_cmd(chip); - - trans_dma_enable(DMA_FROM_DEVICE, chip, 256, DMA_256); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); - - if (slow_read) { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, - (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, - (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, - (u8)(addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, - (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, - (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR3, 0xFF, - (u8)(addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_32); - } - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, - (u8)(pagelen >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, - (u8)pagelen); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CADI_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, - SPI_TRANSFER0_END, SPI_TRANSFER0_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0, - DMA_FROM_DEVICE, 10000); - if (retval < 0) { - kfree(buf); - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index, &offset, - TO_XFER_BUF); - - addr += pagelen; - len -= pagelen; - } - - scsi_set_resid(srb, 0); - kfree(buf); - - return STATUS_SUCCESS; -} - -int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - u8 ins, program_mode; - u32 addr; - u16 len; - u8 *buf; - unsigned int index = 0, offset = 0; - - spi_set_err_code(chip, SPI_NO_ERR); - - ins = srb->cmnd[3]; - addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5]) - << 8) | srb->cmnd[6]; - len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8]; - program_mode = srb->cmnd[9]; - - retval = spi_set_init_para(chip); - if (retval != STATUS_SUCCESS) { - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - if (program_mode == BYTE_PROGRAM) { - buf = kmalloc(4, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - while (len) { - retval = sf_enable_write(chip, SPI_WREN); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset, - FROM_XFER_BUF); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, - buf[0]); - sf_program(chip, ins, 1, addr, 1); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) { - kfree(buf); - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - retval = sf_polling_status(chip, 100); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - addr++; - len--; - } - - kfree(buf); - - } else if (program_mode == AAI_PROGRAM) { - int first_byte = 1; - - retval = sf_enable_write(chip, SPI_WREN); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - buf = kmalloc(4, GFP_KERNEL); - if (!buf) - return STATUS_ERROR; - - while (len) { - rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset, - FROM_XFER_BUF); - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, - buf[0]); - if (first_byte) { - sf_program(chip, ins, 1, addr, 1); - first_byte = 0; - } else { - sf_program(chip, ins, 0, 0, 1); - } - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval < 0) { - kfree(buf); - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - retval = sf_polling_status(chip, 100); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - len--; - } - - kfree(buf); - - retval = sf_disable_write(chip, SPI_WRDI); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sf_polling_status(chip, 100); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else if (program_mode == PAGE_PROGRAM) { - buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL); - if (!buf) - return STATUS_NOMEM; - - while (len) { - u16 pagelen = SF_PAGE_LEN - (u8)addr; - - if (pagelen > len) - pagelen = len; - - retval = sf_enable_write(chip, SPI_WREN); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - rtsx_init_cmd(chip); - - trans_dma_enable(DMA_TO_DEVICE, chip, 256, DMA_256); - sf_program(chip, ins, 1, addr, pagelen); - - rtsx_send_cmd_no_wait(chip); - - rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index, - &offset, FROM_XFER_BUF); - - retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0, - DMA_TO_DEVICE, 100); - if (retval < 0) { - kfree(buf); - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - retval = sf_polling_status(chip, 100); - if (retval != STATUS_SUCCESS) { - kfree(buf); - return STATUS_FAIL; - } - - addr += pagelen; - len -= pagelen; - } - - kfree(buf); - } else { - spi_set_err_code(chip, SPI_INVALID_COMMAND); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - u8 ins, erase_mode; - u32 addr; - - spi_set_err_code(chip, SPI_NO_ERR); - - ins = srb->cmnd[3]; - addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5]) - << 8) | srb->cmnd[6]; - erase_mode = srb->cmnd[9]; - - retval = spi_set_init_para(chip); - if (retval != STATUS_SUCCESS) { - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - if (erase_mode == PAGE_ERASE) { - retval = sf_enable_write(chip, SPI_WREN); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sf_erase(chip, ins, 1, addr); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else if (erase_mode == CHIP_ERASE) { - retval = sf_enable_write(chip, SPI_WREN); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = sf_erase(chip, ins, 0, 0); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - spi_set_err_code(chip, SPI_INVALID_COMMAND); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip) -{ - int retval; - u8 ins, status, ewsr; - - ins = srb->cmnd[3]; - status = srb->cmnd[4]; - ewsr = srb->cmnd[5]; - - retval = spi_set_init_para(chip); - if (retval != STATUS_SUCCESS) { - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - retval = sf_enable_write(chip, ewsr); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, - PINGPONG_BUFFER); - - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, - SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, status); - rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF, - SPI_TRANSFER0_START | SPI_CDO_MODE0); - rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END, - SPI_TRANSFER0_END); - - retval = rtsx_send_cmd(chip, 0, 100); - if (retval != STATUS_SUCCESS) { - rtsx_clear_spi_error(chip); - spi_set_err_code(chip, SPI_HW_ERR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} diff --git a/drivers/staging/rts5208/spi.h b/drivers/staging/rts5208/spi.h deleted file mode 100644 index dcf93c80b2d50..0000000000000 --- a/drivers/staging/rts5208/spi.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_SPI_H -#define __REALTEK_RTSX_SPI_H - -/* SPI operation error */ -#define SPI_NO_ERR 0x00 -#define SPI_HW_ERR 0x01 -#define SPI_INVALID_COMMAND 0x02 -#define SPI_READ_ERR 0x03 -#define SPI_WRITE_ERR 0x04 -#define SPI_ERASE_ERR 0x05 -#define SPI_BUSY_ERR 0x06 - -/* Serial flash instruction */ -#define SPI_READ 0x03 -#define SPI_FAST_READ 0x0B -#define SPI_WREN 0x06 -#define SPI_WRDI 0x04 -#define SPI_RDSR 0x05 - -#define SF_PAGE_LEN 256 - -#define BYTE_PROGRAM 0 -#define AAI_PROGRAM 1 -#define PAGE_PROGRAM 2 - -#define PAGE_ERASE 0 -#define CHIP_ERASE 1 - -int spi_erase_eeprom_chip(struct rtsx_chip *chip); -int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr); -int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val); -int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val); -int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip); -int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip); - -#endif /* __REALTEK_RTSX_SPI_H */ diff --git a/drivers/staging/rts5208/xd.c b/drivers/staging/rts5208/xd.c deleted file mode 100644 index c0af378ada716..0000000000000 --- a/drivers/staging/rts5208/xd.c +++ /dev/null @@ -1,2145 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#include -#include -#include -#include - -#include "rtsx.h" -#include "rtsx_transport.h" -#include "rtsx_scsi.h" -#include "rtsx_card.h" -#include "xd.h" - -static int xd_build_l2p_tbl(struct rtsx_chip *chip, int zone_no); -static int xd_init_page(struct rtsx_chip *chip, u32 phy_blk, u16 logoff, - u8 start_page, u8 end_page); - -static inline void xd_set_err_code(struct rtsx_chip *chip, u8 err_code) -{ - struct xd_info *xd_card = &chip->xd_card; - - xd_card->err_code = err_code; -} - -static int xd_set_init_para(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval; - - if (chip->asic_code) - xd_card->xd_clock = 47; - else - xd_card->xd_clock = CLK_50; - - retval = switch_clock(chip, xd_card->xd_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int xd_switch_clock(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval; - - retval = select_card(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = switch_clock(chip, xd_card->xd_clock); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int xd_read_id(struct rtsx_chip *chip, u8 id_cmd, u8 *id_buf, u8 buf_len) -{ - int retval, i; - u8 *ptr; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DAT, 0xFF, id_cmd); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_READ_ID); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, XD_TRANSFER_END, - XD_TRANSFER_END); - - for (i = 0; i < 4; i++) - rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_ADDRESS1 + i), 0, 0); - - retval = rtsx_send_cmd(chip, XD_CARD, 20); - if (retval < 0) - return STATUS_FAIL; - - ptr = rtsx_get_cmd_data(chip) + 1; - if (id_buf && buf_len) { - if (buf_len > 4) - buf_len = 4; - memcpy(id_buf, ptr, buf_len); - } - - return STATUS_SUCCESS; -} - -static void xd_assign_phy_addr(struct rtsx_chip *chip, u32 addr, u8 mode) -{ - struct xd_info *xd_card = &chip->xd_card; - - switch (mode) { - case XD_RW_ADDR: - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS0, 0xFF, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS1, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS2, - 0xFF, (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS3, - 0xFF, (u8)(addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, 0xFF, - xd_card->addr_cycle | - XD_CALC_ECC | - XD_BA_NO_TRANSFORM); - break; - - case XD_ERASE_ADDR: - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS0, 0xFF, (u8)addr); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS1, - 0xFF, (u8)(addr >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS2, - 0xFF, (u8)(addr >> 16)); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, 0xFF, - (xd_card->addr_cycle - 1) | XD_CALC_ECC | - XD_BA_NO_TRANSFORM); - break; - - default: - break; - } -} - -static int xd_read_redundant(struct rtsx_chip *chip, u32 page_addr, - u8 *buf, int buf_len) -{ - int retval, i; - - rtsx_init_cmd(chip); - - xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, - 0xFF, XD_TRANSFER_START | XD_READ_REDUNDANT); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - for (i = 0; i < 6; i++) - rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_PAGE_STATUS + i), - 0, 0); - for (i = 0; i < 4; i++) - rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_RESERVED0 + i), - 0, 0); - rtsx_add_cmd(chip, READ_REG_CMD, XD_PARITY, 0, 0); - - retval = rtsx_send_cmd(chip, XD_CARD, 500); - if (retval < 0) - return STATUS_FAIL; - - if (buf && buf_len) { - u8 *ptr = rtsx_get_cmd_data(chip) + 1; - - if (buf_len > 11) - buf_len = 11; - memcpy(buf, ptr, buf_len); - } - - return STATUS_SUCCESS; -} - -static int xd_read_data_from_ppb(struct rtsx_chip *chip, int offset, - u8 *buf, int buf_len) -{ - int retval, i; - - if (!buf || buf_len < 0) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - for (i = 0; i < buf_len; i++) - rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + offset + i, - 0, 0); - - retval = rtsx_send_cmd(chip, 0, 250); - if (retval < 0) { - rtsx_clear_xd_error(chip); - return STATUS_FAIL; - } - - memcpy(buf, rtsx_get_cmd_data(chip), buf_len); - - return STATUS_SUCCESS; -} - -static int xd_read_cis(struct rtsx_chip *chip, u32 page_addr, u8 *buf, - int buf_len) -{ - int retval; - u8 reg; - - if (!buf || buf_len < 10) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS, - XD_AUTO_CHK_DATA_STATUS, XD_AUTO_CHK_DATA_STATUS); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_READ_PAGES); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, XD_TRANSFER_END, - XD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, XD_CARD, 250); - if (retval == -ETIMEDOUT) { - rtsx_clear_xd_error(chip); - return STATUS_FAIL; - } - - retval = rtsx_read_register(chip, XD_PAGE_STATUS, ®); - if (retval) - return retval; - if (reg != XD_GPG) { - rtsx_clear_xd_error(chip); - return STATUS_FAIL; - } - - retval = rtsx_read_register(chip, XD_CTL, ®); - if (retval) - return retval; - if (!(reg & XD_ECC1_ERROR) || !(reg & XD_ECC1_UNCORRECTABLE)) { - retval = xd_read_data_from_ppb(chip, 0, buf, buf_len); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - if (reg & XD_ECC1_ERROR) { - u8 ecc_bit, ecc_byte; - - retval = rtsx_read_register(chip, XD_ECC_BIT1, - &ecc_bit); - if (retval) - return retval; - retval = rtsx_read_register(chip, XD_ECC_BYTE1, - &ecc_byte); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "ECC_BIT1 = 0x%x, ECC_BYTE1 = 0x%x\n", - ecc_bit, ecc_byte); - if (ecc_byte < buf_len) { - dev_dbg(rtsx_dev(chip), "Before correct: 0x%x\n", - buf[ecc_byte]); - buf[ecc_byte] ^= (1 << ecc_bit); - dev_dbg(rtsx_dev(chip), "After correct: 0x%x\n", - buf[ecc_byte]); - } - } - } else if (!(reg & XD_ECC2_ERROR) || !(reg & XD_ECC2_UNCORRECTABLE)) { - rtsx_clear_xd_error(chip); - - retval = xd_read_data_from_ppb(chip, 256, buf, buf_len); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - if (reg & XD_ECC2_ERROR) { - u8 ecc_bit, ecc_byte; - - retval = rtsx_read_register(chip, XD_ECC_BIT2, - &ecc_bit); - if (retval) - return retval; - retval = rtsx_read_register(chip, XD_ECC_BYTE2, - &ecc_byte); - if (retval) - return retval; - - dev_dbg(rtsx_dev(chip), "ECC_BIT2 = 0x%x, ECC_BYTE2 = 0x%x\n", - ecc_bit, ecc_byte); - if (ecc_byte < buf_len) { - dev_dbg(rtsx_dev(chip), "Before correct: 0x%x\n", - buf[ecc_byte]); - buf[ecc_byte] ^= (1 << ecc_bit); - dev_dbg(rtsx_dev(chip), "After correct: 0x%x\n", - buf[ecc_byte]); - } - } - } else { - rtsx_clear_xd_error(chip); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static void xd_fill_pull_ctl_disable(struct rtsx_chip *chip) -{ - if (CHECK_PID(chip, 0x5208)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, - XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, - XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, - XD_WP_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, - XD_RDY_PD | XD_WE_PD | XD_RE_PD | XD_ALE_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, - 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, - 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, - 0xFF, 0x4B); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, - 0xFF, 0x69); - } - } -} - -static void xd_fill_pull_ctl_stage1_barossa(struct rtsx_chip *chip) -{ - if (CHECK_BARO_PKG(chip, QFN)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x4B); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); - } -} - -static void xd_fill_pull_ctl_enable(struct rtsx_chip *chip) -{ - if (CHECK_PID(chip, 0x5208)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, - XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, - XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, - XD_WP_PD | XD_CE_PU | XD_CLE_PD | XD_CD_PU); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, - XD_RDY_PU | XD_WE_PU | XD_RE_PU | XD_ALE_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, - 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, - 0xFF, 0x55); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, - 0xFF, 0x53); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, - 0xFF, 0xA9); - } - } -} - -static int xd_pull_ctl_disable(struct rtsx_chip *chip) -{ - int retval; - - if (CHECK_PID(chip, 0x5208)) { - retval = rtsx_write_register(chip, CARD_PULL_CTL1, 0xFF, - XD_D3_PD | - XD_D2_PD | - XD_D1_PD | - XD_D0_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF, - XD_D7_PD | - XD_D6_PD | - XD_D5_PD | - XD_D4_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL3, 0xFF, - XD_WP_PD | - XD_CE_PD | - XD_CLE_PD | - XD_CD_PU); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL4, 0xFF, - XD_RDY_PD | - XD_WE_PD | - XD_RE_PD | - XD_ALE_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL5, 0xFF, - MS_INS_PU | - SD_WP_PD | - SD_CD_PU | - SD_CMD_PD); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL6, 0xFF, - MS_D5_PD | MS_D4_PD); - if (retval) - return retval; - } else if (CHECK_PID(chip, 0x5288)) { - if (CHECK_BARO_PKG(chip, QFN)) { - retval = rtsx_write_register(chip, CARD_PULL_CTL1, - 0xFF, 0x55); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL2, - 0xFF, 0x55); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL3, - 0xFF, 0x4B); - if (retval) - return retval; - retval = rtsx_write_register(chip, CARD_PULL_CTL4, - 0xFF, 0x69); - if (retval) - return retval; - } - } - - return STATUS_SUCCESS; -} - -static int reset_xd(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval, i, j; - u8 *ptr, id_buf[4], redunt[11]; - - retval = select_card(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS, 0xFF, - XD_PGSTS_NOT_FF); - if (chip->asic_code) { - if (!CHECK_PID(chip, 0x5288)) - xd_fill_pull_ctl_disable(chip); - else - xd_fill_pull_ctl_stage1_barossa(chip); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF, - (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN3) | - 0x20); - } - - if (!chip->ft2_fast_mode) - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_INIT, - XD_NO_AUTO_PWR_OFF, 0); - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_OE, XD_OUTPUT_EN, 0); - - retval = rtsx_send_cmd(chip, XD_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - if (!chip->ft2_fast_mode) { - retval = card_power_off(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - wait_timeout(250); - - rtsx_init_cmd(chip); - - if (chip->asic_code) { - xd_fill_pull_ctl_enable(chip); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF, - (FPGA_XD_PULL_CTL_EN1 & - FPGA_XD_PULL_CTL_EN2) | - 0x20); - } - - retval = rtsx_send_cmd(chip, XD_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - retval = card_power_on(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - -#ifdef SUPPORT_OCP - wait_timeout(50); - if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) { - dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n", - chip->ocp_stat); - return STATUS_FAIL; - } -#endif - } - - rtsx_init_cmd(chip); - - if (chip->ft2_fast_mode) { - if (chip->asic_code) { - xd_fill_pull_ctl_enable(chip); - } else { - rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF, - (FPGA_XD_PULL_CTL_EN1 & - FPGA_XD_PULL_CTL_EN2) | - 0x20); - } - } - - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_OE, XD_OUTPUT_EN, XD_OUTPUT_EN); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CTL, XD_CE_DISEN, XD_CE_DISEN); - - retval = rtsx_send_cmd(chip, XD_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - if (!chip->ft2_fast_mode) - wait_timeout(200); - - retval = xd_set_init_para(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - /* Read ID to check if the timing setting is right */ - for (i = 0; i < 4; i++) { - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DTCTL, 0xFF, - XD_TIME_SETUP_STEP * 3 + - XD_TIME_RW_STEP * (2 + i) + XD_TIME_RWN_STEP * i); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CATCTL, 0xFF, - XD_TIME_SETUP_STEP * 3 + - XD_TIME_RW_STEP * (4 + i) + - XD_TIME_RWN_STEP * (3 + i)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_RESET); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0); - rtsx_add_cmd(chip, READ_REG_CMD, XD_CTL, 0, 0); - - retval = rtsx_send_cmd(chip, XD_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - ptr = rtsx_get_cmd_data(chip) + 1; - - dev_dbg(rtsx_dev(chip), "XD_DAT: 0x%x, XD_CTL: 0x%x\n", - ptr[0], ptr[1]); - - if (((ptr[0] & READY_FLAG) != READY_STATE) || - !(ptr[1] & XD_RDY)) - continue; - - retval = xd_read_id(chip, READ_ID, id_buf, 4); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - dev_dbg(rtsx_dev(chip), "READ_ID: 0x%x 0x%x 0x%x 0x%x\n", - id_buf[0], id_buf[1], id_buf[2], id_buf[3]); - - xd_card->device_code = id_buf[1]; - - /* Check if the xD card is supported */ - switch (xd_card->device_code) { - case XD_4M_X8_512_1: - case XD_4M_X8_512_2: - xd_card->block_shift = 4; - xd_card->page_off = 0x0F; - xd_card->addr_cycle = 3; - xd_card->zone_cnt = 1; - xd_card->capacity = 8000; - XD_SET_4MB(xd_card); - break; - case XD_8M_X8_512: - xd_card->block_shift = 4; - xd_card->page_off = 0x0F; - xd_card->addr_cycle = 3; - xd_card->zone_cnt = 1; - xd_card->capacity = 16000; - break; - case XD_16M_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 3; - xd_card->zone_cnt = 1; - xd_card->capacity = 32000; - break; - case XD_32M_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 3; - xd_card->zone_cnt = 2; - xd_card->capacity = 64000; - break; - case XD_64M_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 4; - xd_card->zone_cnt = 4; - xd_card->capacity = 128000; - break; - case XD_128M_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 4; - xd_card->zone_cnt = 8; - xd_card->capacity = 256000; - break; - case XD_256M_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 4; - xd_card->zone_cnt = 16; - xd_card->capacity = 512000; - break; - case XD_512M_X8: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 4; - xd_card->zone_cnt = 32; - xd_card->capacity = 1024000; - break; - case XD_1G_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 4; - xd_card->zone_cnt = 64; - xd_card->capacity = 2048000; - break; - case XD_2G_X8_512: - XD_PAGE_512(xd_card); - xd_card->addr_cycle = 4; - xd_card->zone_cnt = 128; - xd_card->capacity = 4096000; - break; - default: - continue; - } - - /* Confirm timing setting */ - for (j = 0; j < 10; j++) { - retval = xd_read_id(chip, READ_ID, id_buf, 4); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (id_buf[1] != xd_card->device_code) - break; - } - - if (j == 10) - break; - } - - if (i == 4) { - xd_card->block_shift = 0; - xd_card->page_off = 0; - xd_card->addr_cycle = 0; - xd_card->capacity = 0; - - return STATUS_FAIL; - } - - retval = xd_read_id(chip, READ_XD_ID, id_buf, 4); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - dev_dbg(rtsx_dev(chip), "READ_XD_ID: 0x%x 0x%x 0x%x 0x%x\n", - id_buf[0], id_buf[1], id_buf[2], id_buf[3]); - if (id_buf[2] != XD_ID_CODE) - return STATUS_FAIL; - - /* Search CIS block */ - for (i = 0; i < 24; i++) { - u32 page_addr; - - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) - return STATUS_FAIL; - - page_addr = (u32)i << xd_card->block_shift; - - for (j = 0; j < 3; j++) { - retval = xd_read_redundant(chip, page_addr, redunt, 11); - if (retval == STATUS_SUCCESS) - break; - } - if (j == 3) - continue; - - if (redunt[BLOCK_STATUS] != XD_GBLK) - continue; - - j = 0; - if (redunt[PAGE_STATUS] != XD_GPG) { - for (j = 1; j <= 8; j++) { - retval = xd_read_redundant(chip, page_addr + j, - redunt, 11); - if (retval == STATUS_SUCCESS) { - if (redunt[PAGE_STATUS] == XD_GPG) - break; - } - } - - if (j == 9) - break; - } - - /* Check CIS data */ - if (redunt[BLOCK_STATUS] == XD_GBLK && - (redunt[PARITY] & XD_BA1_ALL0)) { - u8 buf[10]; - - page_addr += j; - - retval = xd_read_cis(chip, page_addr, buf, 10); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (buf[0] == 0x01 && buf[1] == 0x03 && - buf[2] == 0xD9 && - buf[3] == 0x01 && buf[4] == 0xFF && - buf[5] == 0x18 && buf[6] == 0x02 && - buf[7] == 0xDF && buf[8] == 0x01 && - buf[9] == 0x20) { - xd_card->cis_block = (u16)i; - } - } - - break; - } - - dev_dbg(rtsx_dev(chip), "CIS block: 0x%x\n", xd_card->cis_block); - if (xd_card->cis_block == 0xFFFF) - return STATUS_FAIL; - - chip->capacity[chip->card2lun[XD_CARD]] = xd_card->capacity; - - return STATUS_SUCCESS; -} - -static int xd_check_data_blank(u8 *redunt) -{ - int i; - - for (i = 0; i < 6; i++) { - if (redunt[PAGE_STATUS + i] != 0xFF) - return 0; - } - - if ((redunt[PARITY] & (XD_ECC1_ALL1 | XD_ECC2_ALL1)) - != (XD_ECC1_ALL1 | XD_ECC2_ALL1)) - return 0; - - for (i = 0; i < 4; i++) { - if (redunt[RESERVED0 + i] != 0xFF) - return 0; - } - - return 1; -} - -static u16 xd_load_log_block_addr(u8 *redunt) -{ - u16 addr = 0xFFFF; - - if (redunt[PARITY] & XD_BA1_BA2_EQL) - addr = ((u16)redunt[BLOCK_ADDR1_H] << 8) | - redunt[BLOCK_ADDR1_L]; - else if (redunt[PARITY] & XD_BA1_VALID) - addr = ((u16)redunt[BLOCK_ADDR1_H] << 8) | - redunt[BLOCK_ADDR1_L]; - else if (redunt[PARITY] & XD_BA2_VALID) - addr = ((u16)redunt[BLOCK_ADDR2_H] << 8) | - redunt[BLOCK_ADDR2_L]; - - return addr; -} - -static int xd_init_l2p_tbl(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int size, i; - - dev_dbg(rtsx_dev(chip), "%s: zone_cnt = %d\n", __func__, - xd_card->zone_cnt); - - if (xd_card->zone_cnt < 1) - return STATUS_FAIL; - - size = xd_card->zone_cnt * sizeof(struct zone_entry); - dev_dbg(rtsx_dev(chip), "Buffer size for l2p table is %d\n", size); - - xd_card->zone = vmalloc(size); - if (!xd_card->zone) - return STATUS_ERROR; - - for (i = 0; i < xd_card->zone_cnt; i++) { - xd_card->zone[i].build_flag = 0; - xd_card->zone[i].l2p_table = NULL; - xd_card->zone[i].free_table = NULL; - xd_card->zone[i].get_index = 0; - xd_card->zone[i].set_index = 0; - xd_card->zone[i].unused_blk_cnt = 0; - } - - return STATUS_SUCCESS; -} - -static inline void free_zone(struct zone_entry *zone) -{ - if (!zone) - return; - - zone->build_flag = 0; - zone->set_index = 0; - zone->get_index = 0; - zone->unused_blk_cnt = 0; - vfree(zone->l2p_table); - zone->l2p_table = NULL; - vfree(zone->free_table); - zone->free_table = NULL; -} - -static void xd_set_unused_block(struct rtsx_chip *chip, u32 phy_blk) -{ - struct xd_info *xd_card = &chip->xd_card; - struct zone_entry *zone; - int zone_no; - - zone_no = (int)phy_blk >> 10; - if (zone_no >= xd_card->zone_cnt) { - dev_dbg(rtsx_dev(chip), "Set unused block to invalid zone (zone_no = %d, zone_cnt = %d)\n", - zone_no, xd_card->zone_cnt); - return; - } - zone = &xd_card->zone[zone_no]; - - if (!zone->free_table) { - if (xd_build_l2p_tbl(chip, zone_no) != STATUS_SUCCESS) - return; - } - - if (zone->set_index >= XD_FREE_TABLE_CNT || - zone->set_index < 0) { - free_zone(zone); - dev_dbg(rtsx_dev(chip), "Set unused block fail, invalid set_index\n"); - return; - } - - dev_dbg(rtsx_dev(chip), "Set unused block to index %d\n", - zone->set_index); - - zone->free_table[zone->set_index++] = (u16)(phy_blk & 0x3ff); - if (zone->set_index >= XD_FREE_TABLE_CNT) - zone->set_index = 0; - zone->unused_blk_cnt++; -} - -static u32 xd_get_unused_block(struct rtsx_chip *chip, int zone_no) -{ - struct xd_info *xd_card = &chip->xd_card; - struct zone_entry *zone; - u32 phy_blk; - - if (zone_no >= xd_card->zone_cnt) { - dev_dbg(rtsx_dev(chip), "Get unused block from invalid zone (zone_no = %d, zone_cnt = %d)\n", - zone_no, xd_card->zone_cnt); - return BLK_NOT_FOUND; - } - zone = &xd_card->zone[zone_no]; - - if (zone->unused_blk_cnt == 0 || - zone->set_index == zone->get_index) { - free_zone(zone); - dev_dbg(rtsx_dev(chip), "Get unused block fail, no unused block available\n"); - return BLK_NOT_FOUND; - } - if (zone->get_index >= XD_FREE_TABLE_CNT || zone->get_index < 0) { - free_zone(zone); - dev_dbg(rtsx_dev(chip), "Get unused block fail, invalid get_index\n"); - return BLK_NOT_FOUND; - } - - dev_dbg(rtsx_dev(chip), "Get unused block from index %d\n", - zone->get_index); - - phy_blk = zone->free_table[zone->get_index]; - zone->free_table[zone->get_index++] = 0xFFFF; - if (zone->get_index >= XD_FREE_TABLE_CNT) - zone->get_index = 0; - zone->unused_blk_cnt--; - - phy_blk += ((u32)(zone_no) << 10); - return phy_blk; -} - -static void xd_set_l2p_tbl(struct rtsx_chip *chip, - int zone_no, u16 log_off, u16 phy_off) -{ - struct xd_info *xd_card = &chip->xd_card; - struct zone_entry *zone; - - zone = &xd_card->zone[zone_no]; - zone->l2p_table[log_off] = phy_off; -} - -static u32 xd_get_l2p_tbl(struct rtsx_chip *chip, int zone_no, u16 log_off) -{ - struct xd_info *xd_card = &chip->xd_card; - struct zone_entry *zone; - int retval; - - zone = &xd_card->zone[zone_no]; - if (zone->l2p_table[log_off] == 0xFFFF) { - u32 phy_blk = 0; - int i; - -#ifdef XD_DELAY_WRITE - retval = xd_delay_write(chip); - if (retval != STATUS_SUCCESS) { - dev_dbg(rtsx_dev(chip), "In %s, delay write fail!\n", - __func__); - return BLK_NOT_FOUND; - } -#endif - - if (zone->unused_blk_cnt <= 0) { - dev_dbg(rtsx_dev(chip), "No unused block!\n"); - return BLK_NOT_FOUND; - } - - for (i = 0; i < zone->unused_blk_cnt; i++) { - phy_blk = xd_get_unused_block(chip, zone_no); - if (phy_blk == BLK_NOT_FOUND) { - dev_dbg(rtsx_dev(chip), "No unused block available!\n"); - return BLK_NOT_FOUND; - } - - retval = xd_init_page(chip, phy_blk, log_off, - 0, xd_card->page_off + 1); - if (retval == STATUS_SUCCESS) - break; - } - if (i >= zone->unused_blk_cnt) { - dev_dbg(rtsx_dev(chip), "No good unused block available!\n"); - return BLK_NOT_FOUND; - } - - xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(phy_blk & 0x3FF)); - return phy_blk; - } - - return (u32)zone->l2p_table[log_off] + ((u32)(zone_no) << 10); -} - -int reset_xd_card(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval; - - memset(xd_card, 0, sizeof(struct xd_info)); - - xd_card->block_shift = 0; - xd_card->page_off = 0; - xd_card->addr_cycle = 0; - xd_card->capacity = 0; - xd_card->zone_cnt = 0; - xd_card->cis_block = 0xFFFF; - xd_card->delay_write.delay_write_flag = 0; - - retval = enable_card_clock(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = reset_xd(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = xd_init_l2p_tbl(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int xd_mark_bad_block(struct rtsx_chip *chip, u32 phy_blk) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval; - u32 page_addr; - u8 reg = 0; - - dev_dbg(rtsx_dev(chip), "mark block 0x%x as bad block\n", phy_blk); - - if (phy_blk == BLK_NOT_FOUND) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, XD_GPG); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, XD_LATER_BBLK); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR2_H, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR2_L, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED0, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED1, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED2, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED3, 0xFF, 0xFF); - - page_addr = phy_blk << xd_card->block_shift; - - xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, - xd_card->page_off + 1); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_WRITE_REDUNDANT); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, XD_CARD, 500); - if (retval < 0) { - rtsx_clear_xd_error(chip); - rtsx_read_register(chip, XD_DAT, ®); - if (reg & PROGRAM_ERROR) - xd_set_err_code(chip, XD_PRG_ERROR); - else - xd_set_err_code(chip, XD_TO_ERROR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int xd_init_page(struct rtsx_chip *chip, u32 phy_blk, - u16 logoff, u8 start_page, u8 end_page) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval; - u32 page_addr; - u8 reg = 0; - - dev_dbg(rtsx_dev(chip), "Init block 0x%x\n", phy_blk); - - if (start_page > end_page) - return STATUS_FAIL; - if (phy_blk == BLK_NOT_FOUND) - return STATUS_FAIL; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, 0xFF); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H, - 0xFF, (u8)(logoff >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, (u8)logoff); - - page_addr = (phy_blk << xd_card->block_shift) + start_page; - - xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, - XD_BA_TRANSFORM, XD_BA_TRANSFORM); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, - 0xFF, (end_page - start_page)); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, - 0xFF, XD_TRANSFER_START | XD_WRITE_REDUNDANT); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, XD_CARD, 500); - if (retval < 0) { - rtsx_clear_xd_error(chip); - rtsx_read_register(chip, XD_DAT, ®); - if (reg & PROGRAM_ERROR) { - xd_mark_bad_block(chip, phy_blk); - xd_set_err_code(chip, XD_PRG_ERROR); - } else { - xd_set_err_code(chip, XD_TO_ERROR); - } - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int xd_copy_page(struct rtsx_chip *chip, u32 old_blk, u32 new_blk, - u8 start_page, u8 end_page) -{ - struct xd_info *xd_card = &chip->xd_card; - u32 old_page, new_page; - u8 i, reg = 0; - int retval; - - dev_dbg(rtsx_dev(chip), "Copy page from block 0x%x to block 0x%x\n", - old_blk, new_blk); - - if (start_page > end_page) - return STATUS_FAIL; - - if (old_blk == BLK_NOT_FOUND || new_blk == BLK_NOT_FOUND) - return STATUS_FAIL; - - old_page = (old_blk << xd_card->block_shift) + start_page; - new_page = (new_blk << xd_card->block_shift) + start_page; - - XD_CLR_BAD_NEWBLK(xd_card); - - retval = rtsx_write_register(chip, CARD_DATA_SOURCE, 0x01, - PINGPONG_BUFFER); - if (retval) - return retval; - - for (i = start_page; i < end_page; i++) { - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - rtsx_clear_xd_error(chip); - xd_set_err_code(chip, XD_NO_CARD); - return STATUS_FAIL; - } - - rtsx_init_cmd(chip); - - xd_assign_phy_addr(chip, old_page, XD_RW_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS, - XD_AUTO_CHK_DATA_STATUS, 0); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_READ_PAGES); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, XD_CARD, 500); - if (retval < 0) { - rtsx_clear_xd_error(chip); - reg = 0; - rtsx_read_register(chip, XD_CTL, ®); - if (reg & (XD_ECC1_ERROR | XD_ECC2_ERROR)) { - mdelay(100); - - if (detect_card_cd(chip, - XD_CARD) != STATUS_SUCCESS) { - xd_set_err_code(chip, XD_NO_CARD); - return STATUS_FAIL; - } - - if (((reg & XD_ECC1_ERROR) && - (reg & XD_ECC1_UNCORRECTABLE)) || - ((reg & XD_ECC2_ERROR) && - (reg & XD_ECC2_UNCORRECTABLE))) { - rtsx_write_register(chip, - XD_PAGE_STATUS, - 0xFF, - XD_BPG); - rtsx_write_register(chip, - XD_BLOCK_STATUS, - 0xFF, - XD_GBLK); - XD_SET_BAD_OLDBLK(xd_card); - dev_dbg(rtsx_dev(chip), "old block 0x%x ecc error\n", - old_blk); - } - } else { - xd_set_err_code(chip, XD_TO_ERROR); - return STATUS_FAIL; - } - } - - if (XD_CHK_BAD_OLDBLK(xd_card)) - rtsx_clear_xd_error(chip); - - rtsx_init_cmd(chip); - - xd_assign_phy_addr(chip, new_page, XD_RW_ADDR); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_WRITE_PAGES); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, XD_CARD, 300); - if (retval < 0) { - rtsx_clear_xd_error(chip); - reg = 0; - rtsx_read_register(chip, XD_DAT, ®); - if (reg & PROGRAM_ERROR) { - xd_mark_bad_block(chip, new_blk); - xd_set_err_code(chip, XD_PRG_ERROR); - XD_SET_BAD_NEWBLK(xd_card); - } else { - xd_set_err_code(chip, XD_TO_ERROR); - } - return STATUS_FAIL; - } - - old_page++; - new_page++; - } - - return STATUS_SUCCESS; -} - -static int xd_reset_cmd(struct rtsx_chip *chip) -{ - int retval; - u8 *ptr; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, - 0xFF, XD_TRANSFER_START | XD_RESET); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0); - rtsx_add_cmd(chip, READ_REG_CMD, XD_CTL, 0, 0); - - retval = rtsx_send_cmd(chip, XD_CARD, 100); - if (retval < 0) - return STATUS_FAIL; - - ptr = rtsx_get_cmd_data(chip) + 1; - if (((ptr[0] & READY_FLAG) == READY_STATE) && (ptr[1] & XD_RDY)) - return STATUS_SUCCESS; - - return STATUS_FAIL; -} - -static int xd_erase_block(struct rtsx_chip *chip, u32 phy_blk) -{ - struct xd_info *xd_card = &chip->xd_card; - u32 page_addr; - u8 reg = 0, *ptr; - int i, retval; - - if (phy_blk == BLK_NOT_FOUND) - return STATUS_FAIL; - - page_addr = phy_blk << xd_card->block_shift; - - for (i = 0; i < 3; i++) { - rtsx_init_cmd(chip); - - xd_assign_phy_addr(chip, page_addr, XD_ERASE_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_ERASE); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0); - - retval = rtsx_send_cmd(chip, XD_CARD, 250); - if (retval < 0) { - rtsx_clear_xd_error(chip); - rtsx_read_register(chip, XD_DAT, ®); - if (reg & PROGRAM_ERROR) { - xd_mark_bad_block(chip, phy_blk); - xd_set_err_code(chip, XD_PRG_ERROR); - return STATUS_FAIL; - } - xd_set_err_code(chip, XD_ERASE_FAIL); - retval = xd_reset_cmd(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - continue; - } - - ptr = rtsx_get_cmd_data(chip) + 1; - if (*ptr & PROGRAM_ERROR) { - xd_mark_bad_block(chip, phy_blk); - xd_set_err_code(chip, XD_PRG_ERROR); - return STATUS_FAIL; - } - - return STATUS_SUCCESS; - } - - xd_mark_bad_block(chip, phy_blk); - xd_set_err_code(chip, XD_ERASE_FAIL); - return STATUS_FAIL; -} - -static int xd_build_l2p_tbl(struct rtsx_chip *chip, int zone_no) -{ - struct xd_info *xd_card = &chip->xd_card; - struct zone_entry *zone; - int retval; - u32 start, end, i; - u16 max_logoff, cur_fst_page_logoff; - u16 cur_lst_page_logoff, ent_lst_page_logoff; - u8 redunt[11]; - - dev_dbg(rtsx_dev(chip), "%s: %d\n", __func__, zone_no); - - if (!xd_card->zone) { - retval = xd_init_l2p_tbl(chip); - if (retval != STATUS_SUCCESS) - return retval; - } - - if (xd_card->zone[zone_no].build_flag) { - dev_dbg(rtsx_dev(chip), "l2p table of zone %d has been built\n", - zone_no); - return STATUS_SUCCESS; - } - - zone = &xd_card->zone[zone_no]; - - if (!zone->l2p_table) { - zone->l2p_table = vmalloc(2000); - if (!zone->l2p_table) - goto build_fail; - } - memset((u8 *)(zone->l2p_table), 0xff, 2000); - - if (!zone->free_table) { - zone->free_table = vmalloc(XD_FREE_TABLE_CNT * 2); - if (!zone->free_table) - goto build_fail; - } - memset((u8 *)(zone->free_table), 0xff, XD_FREE_TABLE_CNT * 2); - - if (zone_no == 0) { - if (xd_card->cis_block == 0xFFFF) - start = 0; - else - start = xd_card->cis_block + 1; - if (XD_CHK_4MB(xd_card)) { - end = 0x200; - max_logoff = 499; - } else { - end = 0x400; - max_logoff = 999; - } - } else { - start = (u32)(zone_no) << 10; - end = (u32)(zone_no + 1) << 10; - max_logoff = 999; - } - - dev_dbg(rtsx_dev(chip), "start block 0x%x, end block 0x%x\n", - start, end); - - zone->set_index = 0; - zone->get_index = 0; - zone->unused_blk_cnt = 0; - - for (i = start; i < end; i++) { - u32 page_addr = i << xd_card->block_shift; - u32 phy_block; - - retval = xd_read_redundant(chip, page_addr, redunt, 11); - if (retval != STATUS_SUCCESS) - continue; - - if (redunt[BLOCK_STATUS] != 0xFF) { - dev_dbg(rtsx_dev(chip), "bad block\n"); - continue; - } - - if (xd_check_data_blank(redunt)) { - dev_dbg(rtsx_dev(chip), "blank block\n"); - xd_set_unused_block(chip, i); - continue; - } - - cur_fst_page_logoff = xd_load_log_block_addr(redunt); - if (cur_fst_page_logoff == 0xFFFF || - cur_fst_page_logoff > max_logoff) { - retval = xd_erase_block(chip, i); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, i); - continue; - } - - if (zone_no == 0 && cur_fst_page_logoff == 0 && - redunt[PAGE_STATUS] != XD_GPG) - XD_SET_MBR_FAIL(xd_card); - - if (zone->l2p_table[cur_fst_page_logoff] == 0xFFFF) { - zone->l2p_table[cur_fst_page_logoff] = (u16)(i & 0x3FF); - continue; - } - - phy_block = zone->l2p_table[cur_fst_page_logoff] + - ((u32)((zone_no) << 10)); - - page_addr = ((i + 1) << xd_card->block_shift) - 1; - - retval = xd_read_redundant(chip, page_addr, redunt, 11); - if (retval != STATUS_SUCCESS) - continue; - - cur_lst_page_logoff = xd_load_log_block_addr(redunt); - if (cur_lst_page_logoff == cur_fst_page_logoff) { - int m; - - page_addr = ((phy_block + 1) << - xd_card->block_shift) - 1; - - for (m = 0; m < 3; m++) { - retval = xd_read_redundant(chip, page_addr, - redunt, 11); - if (retval == STATUS_SUCCESS) - break; - } - - if (m == 3) { - zone->l2p_table[cur_fst_page_logoff] = - (u16)(i & 0x3FF); - retval = xd_erase_block(chip, phy_block); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, phy_block); - continue; - } - - ent_lst_page_logoff = xd_load_log_block_addr(redunt); - if (ent_lst_page_logoff != cur_fst_page_logoff) { - zone->l2p_table[cur_fst_page_logoff] = - (u16)(i & 0x3FF); - retval = xd_erase_block(chip, phy_block); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, phy_block); - continue; - } else { - retval = xd_erase_block(chip, i); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, i); - } - } else { - retval = xd_erase_block(chip, i); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, i); - } - } - - if (XD_CHK_4MB(xd_card)) - end = 500; - else - end = 1000; - - i = 0; - for (start = 0; start < end; start++) { - if (zone->l2p_table[start] == 0xFFFF) - i++; - } - - dev_dbg(rtsx_dev(chip), "Block count %d, invalid L2P entry %d\n", - end, i); - dev_dbg(rtsx_dev(chip), "Total unused block: %d\n", - zone->unused_blk_cnt); - - if ((zone->unused_blk_cnt - i) < 1) - chip->card_wp |= XD_CARD; - - zone->build_flag = 1; - - return STATUS_SUCCESS; - -build_fail: - vfree(zone->l2p_table); - zone->l2p_table = NULL; - vfree(zone->free_table); - zone->free_table = NULL; - - return STATUS_FAIL; -} - -static int xd_send_cmd(struct rtsx_chip *chip, u8 cmd) -{ - int retval; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DAT, 0xFF, cmd); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_SET_CMD); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - retval = rtsx_send_cmd(chip, XD_CARD, 200); - if (retval < 0) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} - -static int xd_read_multiple_pages(struct rtsx_chip *chip, u32 phy_blk, - u32 log_blk, u8 start_page, u8 end_page, - u8 *buf, unsigned int *index, - unsigned int *offset) -{ - struct xd_info *xd_card = &chip->xd_card; - u32 page_addr, new_blk; - u16 log_off; - u8 reg_val, page_cnt; - int zone_no, retval, i; - - if (start_page > end_page) - goto status_fail; - - page_cnt = end_page - start_page; - zone_no = (int)(log_blk / 1000); - log_off = (u16)(log_blk % 1000); - - if ((phy_blk & 0x3FF) == 0x3FF) { - for (i = 0; i < 256; i++) { - page_addr = ((u32)i) << xd_card->block_shift; - - retval = xd_read_redundant(chip, page_addr, NULL, 0); - if (retval == STATUS_SUCCESS) - break; - - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - xd_set_err_code(chip, XD_NO_CARD); - goto status_fail; - } - } - } - - page_addr = (phy_blk << xd_card->block_shift) + start_page; - - rtsx_init_cmd(chip); - - xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, XD_PPB_TO_SIE, XD_PPB_TO_SIE); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, page_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS, - XD_AUTO_CHK_DATA_STATUS, XD_AUTO_CHK_DATA_STATUS); - - trans_dma_enable(chip->srb->sc_data_direction, chip, - page_cnt * 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF, - XD_TRANSFER_START | XD_READ_PAGES); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END | XD_PPB_EMPTY, - XD_TRANSFER_END | XD_PPB_EMPTY); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data_partial(chip, XD_CARD, buf, page_cnt * 512, - scsi_sg_count(chip->srb), - index, offset, DMA_FROM_DEVICE, - chip->xd_timeout); - if (retval < 0) { - rtsx_clear_xd_error(chip); - - if (retval == -ETIMEDOUT) { - xd_set_err_code(chip, XD_TO_ERROR); - goto status_fail; - } else { - goto fail; - } - } - - return STATUS_SUCCESS; - -fail: - retval = rtsx_read_register(chip, XD_PAGE_STATUS, ®_val); - if (retval) - return retval; - - if (reg_val != XD_GPG) - xd_set_err_code(chip, XD_PRG_ERROR); - - retval = rtsx_read_register(chip, XD_CTL, ®_val); - if (retval) - return retval; - - if (((reg_val & (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE)) == - (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE)) || - ((reg_val & (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE)) == - (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))) { - wait_timeout(100); - - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - xd_set_err_code(chip, XD_NO_CARD); - goto status_fail; - } - - xd_set_err_code(chip, XD_ECC_ERROR); - - new_blk = xd_get_unused_block(chip, zone_no); - if (new_blk == NO_NEW_BLK) { - XD_CLR_BAD_OLDBLK(xd_card); - goto status_fail; - } - - retval = xd_copy_page(chip, phy_blk, new_blk, 0, - xd_card->page_off + 1); - if (retval != STATUS_SUCCESS) { - if (!XD_CHK_BAD_NEWBLK(xd_card)) { - retval = xd_erase_block(chip, new_blk); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, new_blk); - } else { - XD_CLR_BAD_NEWBLK(xd_card); - } - XD_CLR_BAD_OLDBLK(xd_card); - goto status_fail; - } - xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF)); - xd_erase_block(chip, phy_blk); - xd_mark_bad_block(chip, phy_blk); - XD_CLR_BAD_OLDBLK(xd_card); - } - -status_fail: - return STATUS_FAIL; -} - -static int xd_finish_write(struct rtsx_chip *chip, - u32 old_blk, u32 new_blk, u32 log_blk, u8 page_off) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval, zone_no; - u16 log_off; - - dev_dbg(rtsx_dev(chip), "old_blk = 0x%x, ", old_blk); - dev_dbg(rtsx_dev(chip), "new_blk = 0x%x, ", new_blk); - dev_dbg(rtsx_dev(chip), "log_blk = 0x%x\n", log_blk); - - if (page_off > xd_card->page_off) - return STATUS_FAIL; - - zone_no = (int)(log_blk / 1000); - log_off = (u16)(log_blk % 1000); - - if (old_blk == BLK_NOT_FOUND) { - retval = xd_init_page(chip, new_blk, log_off, - page_off, xd_card->page_off + 1); - if (retval != STATUS_SUCCESS) { - retval = xd_erase_block(chip, new_blk); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, new_blk); - return STATUS_FAIL; - } - } else { - retval = xd_copy_page(chip, old_blk, new_blk, - page_off, xd_card->page_off + 1); - if (retval != STATUS_SUCCESS) { - if (!XD_CHK_BAD_NEWBLK(xd_card)) { - retval = xd_erase_block(chip, new_blk); - if (retval == STATUS_SUCCESS) - xd_set_unused_block(chip, new_blk); - } - XD_CLR_BAD_NEWBLK(xd_card); - return STATUS_FAIL; - } - - retval = xd_erase_block(chip, old_blk); - if (retval == STATUS_SUCCESS) { - if (XD_CHK_BAD_OLDBLK(xd_card)) { - xd_mark_bad_block(chip, old_blk); - XD_CLR_BAD_OLDBLK(xd_card); - } else { - xd_set_unused_block(chip, old_blk); - } - } else { - xd_set_err_code(chip, XD_NO_ERROR); - XD_CLR_BAD_OLDBLK(xd_card); - } - } - - xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF)); - - return STATUS_SUCCESS; -} - -static int xd_prepare_write(struct rtsx_chip *chip, - u32 old_blk, u32 new_blk, u32 log_blk, u8 page_off) -{ - int retval; - - dev_dbg(rtsx_dev(chip), "%s, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x, page_off = %d\n", - __func__, old_blk, new_blk, log_blk, (int)page_off); - - if (page_off) { - retval = xd_copy_page(chip, old_blk, new_blk, 0, page_off); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} - -static int xd_write_multiple_pages(struct rtsx_chip *chip, u32 old_blk, - u32 new_blk, u32 log_blk, u8 start_page, - u8 end_page, u8 *buf, unsigned int *index, - unsigned int *offset) -{ - struct xd_info *xd_card = &chip->xd_card; - u32 page_addr; - int zone_no, retval; - u16 log_off; - u8 page_cnt, reg_val; - - dev_dbg(rtsx_dev(chip), "%s, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x\n", - __func__, old_blk, new_blk, log_blk); - - if (start_page > end_page) - goto status_fail; - - page_cnt = end_page - start_page; - zone_no = (int)(log_blk / 1000); - log_off = (u16)(log_blk % 1000); - - page_addr = (new_blk << xd_card->block_shift) + start_page; - - retval = xd_send_cmd(chip, READ1_1); - if (retval != STATUS_SUCCESS) - goto status_fail; - - rtsx_init_cmd(chip); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H, - 0xFF, (u8)(log_off >> 8)); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, (u8)log_off); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, XD_GBLK); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, XD_GPG); - - xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, XD_BA_TRANSFORM, - XD_BA_TRANSFORM); - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, page_cnt); - rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - - trans_dma_enable(chip->srb->sc_data_direction, chip, - page_cnt * 512, DMA_512); - - rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, - 0xFF, XD_TRANSFER_START | XD_WRITE_PAGES); - rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, - XD_TRANSFER_END, XD_TRANSFER_END); - - rtsx_send_cmd_no_wait(chip); - - retval = rtsx_transfer_data_partial(chip, XD_CARD, buf, page_cnt * 512, - scsi_sg_count(chip->srb), - index, offset, DMA_TO_DEVICE, chip->xd_timeout); - if (retval < 0) { - rtsx_clear_xd_error(chip); - - if (retval == -ETIMEDOUT) { - xd_set_err_code(chip, XD_TO_ERROR); - goto status_fail; - } else { - goto fail; - } - } - - if (end_page == (xd_card->page_off + 1)) { - xd_card->delay_write.delay_write_flag = 0; - - if (old_blk != BLK_NOT_FOUND) { - retval = xd_erase_block(chip, old_blk); - if (retval == STATUS_SUCCESS) { - if (XD_CHK_BAD_OLDBLK(xd_card)) { - xd_mark_bad_block(chip, old_blk); - XD_CLR_BAD_OLDBLK(xd_card); - } else { - xd_set_unused_block(chip, old_blk); - } - } else { - xd_set_err_code(chip, XD_NO_ERROR); - XD_CLR_BAD_OLDBLK(xd_card); - } - } - xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF)); - } - - return STATUS_SUCCESS; - -fail: - retval = rtsx_read_register(chip, XD_DAT, ®_val); - if (retval) - return retval; - if (reg_val & PROGRAM_ERROR) { - xd_set_err_code(chip, XD_PRG_ERROR); - xd_mark_bad_block(chip, new_blk); - } - -status_fail: - return STATUS_FAIL; -} - -#ifdef XD_DELAY_WRITE -int xd_delay_write(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - struct xd_delay_write_tag *delay_write = &xd_card->delay_write; - int retval; - - if (delay_write->delay_write_flag) { - retval = xd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - delay_write->delay_write_flag = 0; - retval = xd_finish_write(chip, - delay_write->old_phyblock, - delay_write->new_phyblock, - delay_write->logblock, - delay_write->pageoff); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } - - return STATUS_SUCCESS; -} -#endif - -int xd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 start_sector, u16 sector_cnt) -{ - struct xd_info *xd_card = &chip->xd_card; - unsigned int lun = SCSI_LUN(srb); -#ifdef XD_DELAY_WRITE - struct xd_delay_write_tag *delay_write = &xd_card->delay_write; -#endif - int retval, zone_no; - unsigned int index = 0, offset = 0; - u32 log_blk, old_blk = 0, new_blk = 0; - u16 log_off, total_sec_cnt = sector_cnt; - u8 start_page, end_page = 0, page_cnt; - u8 *ptr; - - xd_set_err_code(chip, XD_NO_ERROR); - - xd_card->cleanup_counter = 0; - - dev_dbg(rtsx_dev(chip), "%s: scsi_sg_count = %d\n", __func__, - scsi_sg_count(srb)); - - ptr = (u8 *)scsi_sglist(srb); - - retval = xd_switch_clock(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - chip->card_fail |= XD_CARD; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - - log_blk = start_sector >> xd_card->block_shift; - start_page = (u8)start_sector & xd_card->page_off; - zone_no = (int)(log_blk / 1000); - log_off = (u16)(log_blk % 1000); - - if (xd_card->zone[zone_no].build_flag == 0) { - retval = xd_build_l2p_tbl(chip, zone_no); - if (retval != STATUS_SUCCESS) { - chip->card_fail |= XD_CARD; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { -#ifdef XD_DELAY_WRITE - if (delay_write->delay_write_flag && - delay_write->logblock == log_blk && - start_page > delay_write->pageoff) { - delay_write->delay_write_flag = 0; - if (delay_write->old_phyblock != BLK_NOT_FOUND) { - retval = xd_copy_page(chip, - delay_write->old_phyblock, - delay_write->new_phyblock, - delay_write->pageoff, - start_page); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } - } - old_blk = delay_write->old_phyblock; - new_blk = delay_write->new_phyblock; - } else if (delay_write->delay_write_flag && - (delay_write->logblock == log_blk) && - (start_page == delay_write->pageoff)) { - delay_write->delay_write_flag = 0; - old_blk = delay_write->old_phyblock; - new_blk = delay_write->new_phyblock; - } else { - retval = xd_delay_write(chip); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } -#endif - old_blk = xd_get_l2p_tbl(chip, zone_no, log_off); - new_blk = xd_get_unused_block(chip, zone_no); - if (old_blk == BLK_NOT_FOUND || - new_blk == BLK_NOT_FOUND) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } - - retval = xd_prepare_write(chip, old_blk, new_blk, - log_blk, start_page); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, XD_CARD) != - STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } -#ifdef XD_DELAY_WRITE - } -#endif - } else { -#ifdef XD_DELAY_WRITE - retval = xd_delay_write(chip); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return STATUS_FAIL; - } -#endif - - old_blk = xd_get_l2p_tbl(chip, zone_no, log_off); - if (old_blk == BLK_NOT_FOUND) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return STATUS_FAIL; - } - } - - dev_dbg(rtsx_dev(chip), "old_blk = 0x%x\n", old_blk); - - while (total_sec_cnt) { - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - chip->card_fail |= XD_CARD; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - - if ((start_page + total_sec_cnt) > (xd_card->page_off + 1)) - end_page = xd_card->page_off + 1; - else - end_page = start_page + (u8)total_sec_cnt; - - page_cnt = end_page - start_page; - if (srb->sc_data_direction == DMA_FROM_DEVICE) { - retval = xd_read_multiple_pages(chip, old_blk, log_blk, - start_page, end_page, - ptr, &index, &offset); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - return STATUS_FAIL; - } - } else { - retval = xd_write_multiple_pages(chip, old_blk, - new_blk, log_blk, - start_page, end_page, - ptr, &index, &offset); - if (retval != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } - } - - total_sec_cnt -= page_cnt; - if (scsi_sg_count(srb) == 0) - ptr += page_cnt * 512; - - if (total_sec_cnt == 0) - break; - - log_blk++; - zone_no = (int)(log_blk / 1000); - log_off = (u16)(log_blk % 1000); - - if (xd_card->zone[zone_no].build_flag == 0) { - retval = xd_build_l2p_tbl(chip, zone_no); - if (retval != STATUS_SUCCESS) { - chip->card_fail |= XD_CARD; - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - } - - old_blk = xd_get_l2p_tbl(chip, zone_no, log_off); - if (old_blk == BLK_NOT_FOUND) { - if (srb->sc_data_direction == DMA_FROM_DEVICE) - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); - else - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - - return STATUS_FAIL; - } - - if (srb->sc_data_direction == DMA_TO_DEVICE) { - new_blk = xd_get_unused_block(chip, zone_no); - if (new_blk == BLK_NOT_FOUND) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } - } - - start_page = 0; - } - - if (srb->sc_data_direction == DMA_TO_DEVICE && - (end_page != (xd_card->page_off + 1))) { -#ifdef XD_DELAY_WRITE - delay_write->delay_write_flag = 1; - delay_write->old_phyblock = old_blk; - delay_write->new_phyblock = new_blk; - delay_write->logblock = log_blk; - delay_write->pageoff = end_page; -#else - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - chip->card_fail |= XD_CARD; - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - - retval = xd_finish_write(chip, old_blk, new_blk, - log_blk, end_page); - if (retval != STATUS_SUCCESS) { - if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) { - set_sense_type(chip, lun, - SENSE_TYPE_MEDIA_NOT_PRESENT); - return STATUS_FAIL; - } - set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); - return STATUS_FAIL; - } -#endif - } - - scsi_set_resid(srb, 0); - - return STATUS_SUCCESS; -} - -void xd_free_l2p_tbl(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int i = 0; - - if (xd_card->zone) { - for (i = 0; i < xd_card->zone_cnt; i++) { - vfree(xd_card->zone[i].l2p_table); - xd_card->zone[i].l2p_table = NULL; - vfree(xd_card->zone[i].free_table); - xd_card->zone[i].free_table = NULL; - } - vfree(xd_card->zone); - xd_card->zone = NULL; - } -} - -void xd_cleanup_work(struct rtsx_chip *chip) -{ -#ifdef XD_DELAY_WRITE - struct xd_info *xd_card = &chip->xd_card; - - if (xd_card->delay_write.delay_write_flag) { - dev_dbg(rtsx_dev(chip), "xD: delay write\n"); - xd_delay_write(chip); - xd_card->cleanup_counter = 0; - } -#endif -} - -int xd_power_off_card3v3(struct rtsx_chip *chip) -{ - int retval; - - retval = disable_card_clock(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - retval = rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN, 0); - if (retval) - return retval; - - if (!chip->ft2_fast_mode) { - retval = card_power_off(chip, XD_CARD); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - wait_timeout(50); - } - - if (chip->asic_code) { - retval = xd_pull_ctl_disable(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - } else { - retval = rtsx_write_register(chip, FPGA_PULL_CTL, 0xFF, 0xDF); - if (retval) - return retval; - } - - return STATUS_SUCCESS; -} - -int release_xd_card(struct rtsx_chip *chip) -{ - struct xd_info *xd_card = &chip->xd_card; - int retval; - - chip->card_ready &= ~XD_CARD; - chip->card_fail &= ~XD_CARD; - chip->card_wp &= ~XD_CARD; - - xd_card->delay_write.delay_write_flag = 0; - - xd_free_l2p_tbl(chip); - - retval = xd_power_off_card3v3(chip); - if (retval != STATUS_SUCCESS) - return STATUS_FAIL; - - return STATUS_SUCCESS; -} diff --git a/drivers/staging/rts5208/xd.h b/drivers/staging/rts5208/xd.h deleted file mode 100644 index 98c00f268e561..0000000000000 --- a/drivers/staging/rts5208/xd.h +++ /dev/null @@ -1,176 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG (wei_wang@realsil.com.cn) - * Micky Ching (micky_ching@realsil.com.cn) - */ - -#ifndef __REALTEK_RTSX_XD_H -#define __REALTEK_RTSX_XD_H - -#define XD_DELAY_WRITE - -/* Error Codes */ -#define XD_NO_ERROR 0x00 -#define XD_NO_MEMORY 0x80 -#define XD_PRG_ERROR 0x40 -#define XD_NO_CARD 0x20 -#define XD_READ_FAIL 0x10 -#define XD_ERASE_FAIL 0x08 -#define XD_WRITE_FAIL 0x04 -#define XD_ECC_ERROR 0x02 -#define XD_TO_ERROR 0x01 - -/* XD Commands */ -#define READ1_1 0x00 -#define READ1_2 0x01 -#define READ2 0x50 -#define READ_ID 0x90 -#define RESET 0xff -#define PAGE_PRG_1 0x80 -#define PAGE_PRG_2 0x10 -#define BLK_ERASE_1 0x60 -#define BLK_ERASE_2 0xD0 -#define READ_STS 0x70 -#define READ_XD_ID 0x9A -#define COPY_BACK_512 0x8A -#define COPY_BACK_2K 0x85 -#define READ1_1_2 0x30 -#define READ1_1_3 0x35 -#define CHG_DAT_OUT_1 0x05 -#define RDM_DAT_OUT_1 0x05 -#define CHG_DAT_OUT_2 0xE0 -#define RDM_DAT_OUT_2 0xE0 -#define CHG_DAT_OUT_2 0xE0 -#define CHG_DAT_IN_1 0x85 -#define CACHE_PRG 0x15 - -/* Redundant Area Related */ -#define XD_EXTRA_SIZE 0x10 -#define XD_2K_EXTRA_SIZE 0x40 - -#define NOT_WRITE_PROTECTED 0x80 -#define READY_STATE 0x40 -#define PROGRAM_ERROR 0x01 -#define PROGRAM_ERROR_N_1 0x02 -#define INTERNAL_READY 0x20 -#define READY_FLAG 0x5F - -#define XD_8M_X8_512 0xE6 -#define XD_16M_X8_512 0x73 -#define XD_32M_X8_512 0x75 -#define XD_64M_X8_512 0x76 -#define XD_128M_X8_512 0x79 -#define XD_256M_X8_512 0x71 -#define XD_128M_X8_2048 0xF1 -#define XD_256M_X8_2048 0xDA -#define XD_512M_X8 0xDC -#define XD_128M_X16_2048 0xC1 -#define XD_4M_X8_512_1 0xE3 -#define XD_4M_X8_512_2 0xE5 -#define XD_1G_X8_512 0xD3 -#define XD_2G_X8_512 0xD5 - -#define XD_ID_CODE 0xB5 - -#define VENDOR_BLOCK 0xEFFF -#define CIS_BLOCK 0xDFFF - -#define BLK_NOT_FOUND 0xFFFFFFFF - -#define NO_NEW_BLK 0xFFFFFFFF - -#define PAGE_CORRECTABLE 0x0 -#define PAGE_NOTCORRECTABLE 0x1 - -#define NO_OFFSET 0x0 -#define WITH_OFFSET 0x1 - -#define SECT_PER_PAGE 4 -#define XD_ADDR_MODE_2C XD_ADDR_MODE_2A - -#define ZONE0_BAD_BLOCK 23 -#define NOT_ZONE0_BAD_BLOCK 24 - -#define XD_RW_ADDR 0x01 -#define XD_ERASE_ADDR 0x02 - -#define XD_PAGE_512(xd_card) \ -do { \ - (xd_card)->block_shift = 5; \ - (xd_card)->page_off = 0x1F; \ -} while (0) - -#define XD_SET_BAD_NEWBLK(xd_card) ((xd_card)->multi_flag |= 0x01) -#define XD_CLR_BAD_NEWBLK(xd_card) ((xd_card)->multi_flag &= ~0x01) -#define XD_CHK_BAD_NEWBLK(xd_card) ((xd_card)->multi_flag & 0x01) - -#define XD_SET_BAD_OLDBLK(xd_card) ((xd_card)->multi_flag |= 0x02) -#define XD_CLR_BAD_OLDBLK(xd_card) ((xd_card)->multi_flag &= ~0x02) -#define XD_CHK_BAD_OLDBLK(xd_card) ((xd_card)->multi_flag & 0x02) - -#define XD_SET_MBR_FAIL(xd_card) ((xd_card)->multi_flag |= 0x04) -#define XD_CLR_MBR_FAIL(xd_card) ((xd_card)->multi_flag &= ~0x04) -#define XD_CHK_MBR_FAIL(xd_card) ((xd_card)->multi_flag & 0x04) - -#define XD_SET_ECC_FLD_ERR(xd_card) ((xd_card)->multi_flag |= 0x08) -#define XD_CLR_ECC_FLD_ERR(xd_card) ((xd_card)->multi_flag &= ~0x08) -#define XD_CHK_ECC_FLD_ERR(xd_card) ((xd_card)->multi_flag & 0x08) - -#define XD_SET_4MB(xd_card) ((xd_card)->multi_flag |= 0x10) -#define XD_CLR_4MB(xd_card) ((xd_card)->multi_flag &= ~0x10) -#define XD_CHK_4MB(xd_card) ((xd_card)->multi_flag & 0x10) - -#define XD_SET_ECC_ERR(xd_card) ((xd_card)->multi_flag |= 0x40) -#define XD_CLR_ECC_ERR(xd_card) ((xd_card)->multi_flag &= ~0x40) -#define XD_CHK_ECC_ERR(xd_card) ((xd_card)->multi_flag & 0x40) - -#define PAGE_STATUS 0 -#define BLOCK_STATUS 1 -#define BLOCK_ADDR1_L 2 -#define BLOCK_ADDR1_H 3 -#define BLOCK_ADDR2_L 4 -#define BLOCK_ADDR2_H 5 -#define RESERVED0 6 -#define RESERVED1 7 -#define RESERVED2 8 -#define RESERVED3 9 -#define PARITY 10 - -#define CIS0_0 0 -#define CIS0_1 1 -#define CIS0_2 2 -#define CIS0_3 3 -#define CIS0_4 4 -#define CIS0_5 5 -#define CIS0_6 6 -#define CIS0_7 7 -#define CIS0_8 8 -#define CIS0_9 9 -#define CIS1_0 256 -#define CIS1_1 (256 + 1) -#define CIS1_2 (256 + 2) -#define CIS1_3 (256 + 3) -#define CIS1_4 (256 + 4) -#define CIS1_5 (256 + 5) -#define CIS1_6 (256 + 6) -#define CIS1_7 (256 + 7) -#define CIS1_8 (256 + 8) -#define CIS1_9 (256 + 9) - -int reset_xd_card(struct rtsx_chip *chip); -#ifdef XD_DELAY_WRITE -int xd_delay_write(struct rtsx_chip *chip); -#endif -int xd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, - u32 start_sector, u16 sector_cnt); -void xd_free_l2p_tbl(struct rtsx_chip *chip); -void xd_cleanup_work(struct rtsx_chip *chip); -int xd_power_off_card3v3(struct rtsx_chip *chip); -int release_xd_card(struct rtsx_chip *chip); - -#endif /* __REALTEK_RTSX_XD_H */ diff --git a/drivers/staging/sm750fb/TODO b/drivers/staging/sm750fb/TODO index 481409eb3fb38..9dd57c5662570 100644 --- a/drivers/staging/sm750fb/TODO +++ b/drivers/staging/sm750fb/TODO @@ -12,8 +12,3 @@ TODO: Note: - This driver will be removed from staging after the drm driver is ready - The drm driver is getting ready at https://gitlab.com/sudipm/sm750/tree/sm750 - -Please send any patches to - Greg Kroah-Hartman - Sudip Mukherjee - Teddy Wang diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c index 133ed15f3dbcc..dc0d715ed9707 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c @@ -96,7 +96,8 @@ static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance, static int audio_vchi_callback(struct vchiq_instance *vchiq_instance, enum vchiq_reason reason, struct vchiq_header *header, - unsigned int handle, void *userdata) + unsigned int handle, + void *cb_data, void __user *cb_userdata) { struct bcm2835_audio_instance *instance = vchiq_get_service_userdata(vchiq_instance, handle); diff --git a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h index 6c40d8c1dde60..ee4469f4fc510 100644 --- a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h +++ b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h @@ -56,7 +56,7 @@ struct vchiq_service_base { enum vchiq_reason reason, struct vchiq_header *header, unsigned int handle, - void *bulk_userdata); + void *cb_data, void __user *cb_userdata); void *userdata; }; @@ -64,7 +64,8 @@ struct vchiq_completion_data_kernel { enum vchiq_reason reason; struct vchiq_header *header; void *service_userdata; - void *bulk_userdata; + void *cb_data; + void __user *cb_userdata; }; struct vchiq_service_params_kernel { @@ -73,7 +74,7 @@ struct vchiq_service_params_kernel { enum vchiq_reason reason, struct vchiq_header *header, unsigned int handle, - void *bulk_userdata); + void *cb_data, void __user *cb_userdata); void *userdata; short version; /* Increment for non-trivial changes */ short version_min; /* Update for incompatible changes */ diff --git a/drivers/staging/vc04_services/interface/TESTING b/drivers/staging/vc04_services/interface/TESTING index 273952dc9d859..c98f688b07e0f 100644 --- a/drivers/staging/vc04_services/interface/TESTING +++ b/drivers/staging/vc04_services/interface/TESTING @@ -52,10 +52,10 @@ Here are the most common kernel configurations: * Ping test - Command: vchiq_test -p 1 + Command: vchiq_test -p Expected output: - Ping test - service:echo, iters:1, version 3 + Ping test - service:echo, iters:1000, version 3 vchi ping (size 0) -> 57.000000us vchi ping (size 0, 0 async, 0 oneway) -> 122.000000us vchi bulk (size 0, 0 async, 0 oneway) -> 546.000000us diff --git a/drivers/staging/vc04_services/interface/TODO b/drivers/staging/vc04_services/interface/TODO index dfb1ee49633fe..f6f24600aa862 100644 --- a/drivers/staging/vc04_services/interface/TODO +++ b/drivers/staging/vc04_services/interface/TODO @@ -26,16 +26,3 @@ kthreads, userspace, limitations) could be very helpful for reviewers. The code follows the 80 characters limitation yet tends to go 3 or 4 levels of indentation deep making it very unpleasant to read. This is specially relevant in the character driver ioctl code and in the core thread functions. - -* Clean up Sparse warnings from __user annotations. See -vchiq_irq_queue_bulk_tx_rx(). Ensure that the address of "&waiter->bulk_waiter" -is never disclosed to userspace. - -* Fix behavior of message handling - -The polling behavior of vchiq_bulk_transmit(), vchiq_bulk_receive() and -vchiq_queue_kernel_message() looks broken. A possible signal should be -propagated back to user space to let the calling task handle it before -retrying. Hopefully these msleep(1) shouldn't be necessary anymore. - -https://lore.kernel.org/linux-staging/CAK8P3a3HGm1cPo4sW9fOY4E8AN8yAq3tevXxU5m8bmtmsU8WKw@mail.gmail.com/ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 6c488b1e26248..a4e83e5d619bc 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +35,6 @@ #include "vchiq_arm.h" #include "vchiq_bus.h" #include "vchiq_debugfs.h" -#include "vchiq_pagelist.h" #define DEVICE_NAME "vchiq" @@ -48,7 +46,6 @@ #define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1 #define BELL0 0x00 -#define BELL2 0x08 #define ARM_DS_ACTIVE BIT(2) @@ -109,21 +106,9 @@ struct vchiq_arm_state { int first_connect; }; -struct vchiq_pagelist_info { - struct pagelist *pagelist; - size_t pagelist_buffer_size; - dma_addr_t dma_addr; - enum dma_data_direction dma_dir; - unsigned int num_pages; - unsigned int pages_need_release; - struct page **pages; - struct scatterlist *scatterlist; - unsigned int scatterlist_mapped; -}; - static int -vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void *data, - unsigned int size, enum vchiq_bulk_dir dir); +vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_bulk *bulk_params); static irqreturn_t vchiq_doorbell_irq(int irq, void *dev_id) @@ -146,35 +131,6 @@ vchiq_doorbell_irq(int irq, void *dev_id) return ret; } -static void -cleanup_pagelistinfo(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo) -{ - if (pagelistinfo->scatterlist_mapped) { - dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, - pagelistinfo->num_pages, pagelistinfo->dma_dir); - } - - if (pagelistinfo->pages_need_release) - unpin_user_pages(pagelistinfo->pages, pagelistinfo->num_pages); - - dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, - pagelistinfo->pagelist, pagelistinfo->dma_addr); -} - -static inline bool -is_adjacent_block(u32 *addrs, dma_addr_t addr, unsigned int k) -{ - u32 tmp; - - if (!k) - return false; - - tmp = (addrs[k - 1] & PAGE_MASK) + - (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT); - - return tmp == (addr & PAGE_MASK); -} - /* * This function is called by the vchiq stack once it has been connected to * the videocore and clients can start to use the stack. @@ -225,270 +181,6 @@ void vchiq_add_connected_callback(struct vchiq_device *device, void (*callback)( } EXPORT_SYMBOL(vchiq_add_connected_callback); -/* There is a potential problem with partial cache lines (pages?) - * at the ends of the block when reading. If the CPU accessed anything in - * the same line (page?) then it may have pulled old data into the cache, - * obscuring the new data underneath. We can solve this by transferring the - * partial cache lines separately, and allowing the ARM to copy into the - * cached area. - */ - -static struct vchiq_pagelist_info * -create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, - size_t count, unsigned short type) -{ - struct vchiq_drv_mgmt *drv_mgmt; - struct pagelist *pagelist; - struct vchiq_pagelist_info *pagelistinfo; - struct page **pages; - u32 *addrs; - unsigned int num_pages, offset, i, k; - int actual_pages; - size_t pagelist_size; - struct scatterlist *scatterlist, *sg; - int dma_buffers; - dma_addr_t dma_addr; - - if (count >= INT_MAX - PAGE_SIZE) - return NULL; - - drv_mgmt = dev_get_drvdata(instance->state->dev); - - if (buf) - offset = (uintptr_t)buf & (PAGE_SIZE - 1); - else - offset = (uintptr_t)ubuf & (PAGE_SIZE - 1); - num_pages = DIV_ROUND_UP(count + offset, PAGE_SIZE); - - if ((size_t)num_pages > (SIZE_MAX - sizeof(struct pagelist) - - sizeof(struct vchiq_pagelist_info)) / - (sizeof(u32) + sizeof(pages[0]) + - sizeof(struct scatterlist))) - return NULL; - - pagelist_size = sizeof(struct pagelist) + - (num_pages * sizeof(u32)) + - (num_pages * sizeof(pages[0]) + - (num_pages * sizeof(struct scatterlist))) + - sizeof(struct vchiq_pagelist_info); - - /* Allocate enough storage to hold the page pointers and the page - * list - */ - pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, - GFP_KERNEL); - - dev_dbg(instance->state->dev, "arm: %pK\n", pagelist); - - if (!pagelist) - return NULL; - - addrs = pagelist->addrs; - pages = (struct page **)(addrs + num_pages); - scatterlist = (struct scatterlist *)(pages + num_pages); - pagelistinfo = (struct vchiq_pagelist_info *) - (scatterlist + num_pages); - - pagelist->length = count; - pagelist->type = type; - pagelist->offset = offset; - - /* Populate the fields of the pagelistinfo structure */ - pagelistinfo->pagelist = pagelist; - pagelistinfo->pagelist_buffer_size = pagelist_size; - pagelistinfo->dma_addr = dma_addr; - pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE; - pagelistinfo->num_pages = num_pages; - pagelistinfo->pages_need_release = 0; - pagelistinfo->pages = pages; - pagelistinfo->scatterlist = scatterlist; - pagelistinfo->scatterlist_mapped = 0; - - if (buf) { - unsigned long length = count; - unsigned int off = offset; - - for (actual_pages = 0; actual_pages < num_pages; - actual_pages++) { - struct page *pg = - vmalloc_to_page((buf + - (actual_pages * PAGE_SIZE))); - size_t bytes = PAGE_SIZE - off; - - if (!pg) { - cleanup_pagelistinfo(instance, pagelistinfo); - return NULL; - } - - if (bytes > length) - bytes = length; - pages[actual_pages] = pg; - length -= bytes; - off = 0; - } - /* do not try and release vmalloc pages */ - } else { - actual_pages = pin_user_pages_fast((unsigned long)ubuf & PAGE_MASK, num_pages, - type == PAGELIST_READ, pages); - - if (actual_pages != num_pages) { - dev_dbg(instance->state->dev, "arm: Only %d/%d pages locked\n", - actual_pages, num_pages); - - /* This is probably due to the process being killed */ - if (actual_pages > 0) - unpin_user_pages(pages, actual_pages); - cleanup_pagelistinfo(instance, pagelistinfo); - return NULL; - } - /* release user pages */ - pagelistinfo->pages_need_release = 1; - } - - /* - * Initialize the scatterlist so that the magic cookie - * is filled if debugging is enabled - */ - sg_init_table(scatterlist, num_pages); - /* Now set the pages for each scatterlist */ - for (i = 0; i < num_pages; i++) { - unsigned int len = PAGE_SIZE - offset; - - if (len > count) - len = count; - sg_set_page(scatterlist + i, pages[i], len, offset); - offset = 0; - count -= len; - } - - dma_buffers = dma_map_sg(instance->state->dev, - scatterlist, - num_pages, - pagelistinfo->dma_dir); - - if (dma_buffers == 0) { - cleanup_pagelistinfo(instance, pagelistinfo); - return NULL; - } - - pagelistinfo->scatterlist_mapped = 1; - - /* Combine adjacent blocks for performance */ - k = 0; - for_each_sg(scatterlist, sg, dma_buffers, i) { - unsigned int len = sg_dma_len(sg); - dma_addr_t addr = sg_dma_address(sg); - - /* Note: addrs is the address + page_count - 1 - * The firmware expects blocks after the first to be page- - * aligned and a multiple of the page size - */ - WARN_ON(len == 0); - WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); - WARN_ON(i && (addr & ~PAGE_MASK)); - if (is_adjacent_block(addrs, addr, k)) - addrs[k - 1] += ((len + PAGE_SIZE - 1) >> PAGE_SHIFT); - else - addrs[k++] = (addr & PAGE_MASK) | - (((len + PAGE_SIZE - 1) >> PAGE_SHIFT) - 1); - } - - /* Partial cache lines (fragments) require special measures */ - if ((type == PAGELIST_READ) && - ((pagelist->offset & (drv_mgmt->info->cache_line_size - 1)) || - ((pagelist->offset + pagelist->length) & - (drv_mgmt->info->cache_line_size - 1)))) { - char *fragments; - - if (down_interruptible(&drv_mgmt->free_fragments_sema)) { - cleanup_pagelistinfo(instance, pagelistinfo); - return NULL; - } - - WARN_ON(!drv_mgmt->free_fragments); - - down(&drv_mgmt->free_fragments_mutex); - fragments = drv_mgmt->free_fragments; - WARN_ON(!fragments); - drv_mgmt->free_fragments = *(char **)drv_mgmt->free_fragments; - up(&drv_mgmt->free_fragments_mutex); - pagelist->type = PAGELIST_READ_WITH_FRAGMENTS + - (fragments - drv_mgmt->fragments_base) / drv_mgmt->fragments_size; - } - - return pagelistinfo; -} - -static void -free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo, - int actual) -{ - struct vchiq_drv_mgmt *drv_mgmt; - struct pagelist *pagelist = pagelistinfo->pagelist; - struct page **pages = pagelistinfo->pages; - unsigned int num_pages = pagelistinfo->num_pages; - - dev_dbg(instance->state->dev, "arm: %pK, %d\n", pagelistinfo->pagelist, actual); - - drv_mgmt = dev_get_drvdata(instance->state->dev); - - /* - * NOTE: dma_unmap_sg must be called before the - * cpu can touch any of the data/pages. - */ - dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, - pagelistinfo->num_pages, pagelistinfo->dma_dir); - pagelistinfo->scatterlist_mapped = 0; - - /* Deal with any partial cache lines (fragments) */ - if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS && drv_mgmt->fragments_base) { - char *fragments = drv_mgmt->fragments_base + - (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * - drv_mgmt->fragments_size; - int head_bytes, tail_bytes; - - head_bytes = (drv_mgmt->info->cache_line_size - pagelist->offset) & - (drv_mgmt->info->cache_line_size - 1); - tail_bytes = (pagelist->offset + actual) & - (drv_mgmt->info->cache_line_size - 1); - - if ((actual >= 0) && (head_bytes != 0)) { - if (head_bytes > actual) - head_bytes = actual; - - memcpy_to_page(pages[0], - pagelist->offset, - fragments, - head_bytes); - } - if ((actual >= 0) && (head_bytes < actual) && - (tail_bytes != 0)) - memcpy_to_page(pages[num_pages - 1], - (pagelist->offset + actual) & - (PAGE_SIZE - 1) & ~(drv_mgmt->info->cache_line_size - 1), - fragments + drv_mgmt->info->cache_line_size, - tail_bytes); - - down(&drv_mgmt->free_fragments_mutex); - *(char **)fragments = drv_mgmt->free_fragments; - drv_mgmt->free_fragments = fragments; - up(&drv_mgmt->free_fragments_mutex); - up(&drv_mgmt->free_fragments_sema); - } - - /* Need to mark all the pages dirty. */ - if (pagelist->type != PAGELIST_WRITE && - pagelistinfo->pages_need_release) { - unsigned int i; - - for (i = 0; i < num_pages; i++) - set_page_dirty(pages[i]); - } - - cleanup_pagelistinfo(instance, pagelistinfo); -} - static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) { struct device *dev = &pdev->dev; @@ -616,58 +308,6 @@ static struct vchiq_arm_state *vchiq_platform_get_arm_state(struct vchiq_state * return (struct vchiq_arm_state *)state->platform_state; } -void -remote_event_signal(struct vchiq_state *state, struct remote_event *event) -{ - struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(state->dev); - - /* - * Ensure that all writes to shared data structures have completed - * before signalling the peer. - */ - wmb(); - - event->fired = 1; - - dsb(sy); /* data barrier operation */ - - if (event->armed) - writel(0, mgmt->regs + BELL2); /* trigger vc interrupt */ -} - -int -vchiq_prepare_bulk_data(struct vchiq_instance *instance, struct vchiq_bulk *bulk, void *offset, - void __user *uoffset, int size, int dir) -{ - struct vchiq_pagelist_info *pagelistinfo; - - pagelistinfo = create_pagelist(instance, offset, uoffset, size, - (dir == VCHIQ_BULK_RECEIVE) - ? PAGELIST_READ - : PAGELIST_WRITE); - - if (!pagelistinfo) - return -ENOMEM; - - bulk->data = pagelistinfo->dma_addr; - - /* - * Store the pagelistinfo address in remote_data, - * which isn't used by the slave. - */ - bulk->remote_data = pagelistinfo; - - return 0; -} - -void -vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk) -{ - if (bulk && bulk->remote_data && bulk->actual) - free_pagelist(instance, (struct vchiq_pagelist_info *)bulk->remote_data, - bulk->actual); -} - void vchiq_dump_platform_state(struct seq_file *f) { seq_puts(f, " Platform: 2835 (VC master)\n"); @@ -851,34 +491,31 @@ int vchiq_bulk_transmit(struct vchiq_instance *instance, unsigned int handle, const void *data, unsigned int size, void *userdata, enum vchiq_bulk_mode mode) { + struct vchiq_bulk bulk_params = {}; int ret; - while (1) { - switch (mode) { - case VCHIQ_BULK_MODE_NOCALLBACK: - case VCHIQ_BULK_MODE_CALLBACK: - ret = vchiq_bulk_xfer_callback_interruptible(instance, handle, - (void *)data, NULL, - size, mode, userdata, - VCHIQ_BULK_TRANSMIT); - break; - case VCHIQ_BULK_MODE_BLOCKING: - ret = vchiq_blocking_bulk_transfer(instance, handle, (void *)data, size, - VCHIQ_BULK_TRANSMIT); - break; - default: - return -EINVAL; - } - - /* - * vchiq_*_bulk_transfer() may return -EAGAIN, so we need - * to implement a retry mechanism since this function is - * supposed to block until queued - */ - if (ret != -EAGAIN) - break; - - msleep(1); + switch (mode) { + case VCHIQ_BULK_MODE_NOCALLBACK: + case VCHIQ_BULK_MODE_CALLBACK: + + bulk_params.offset = (void *)data; + bulk_params.mode = mode; + bulk_params.size = size; + bulk_params.cb_data = userdata; + bulk_params.dir = VCHIQ_BULK_TRANSMIT; + + ret = vchiq_bulk_xfer_callback(instance, handle, &bulk_params); + break; + case VCHIQ_BULK_MODE_BLOCKING: + bulk_params.offset = (void *)data; + bulk_params.mode = mode; + bulk_params.size = size; + bulk_params.dir = VCHIQ_BULK_TRANSMIT; + + ret = vchiq_blocking_bulk_transfer(instance, handle, &bulk_params); + break; + default: + return -EINVAL; } return ret; @@ -889,34 +526,31 @@ int vchiq_bulk_receive(struct vchiq_instance *instance, unsigned int handle, void *data, unsigned int size, void *userdata, enum vchiq_bulk_mode mode) { + struct vchiq_bulk bulk_params = {}; int ret; - while (1) { - switch (mode) { - case VCHIQ_BULK_MODE_NOCALLBACK: - case VCHIQ_BULK_MODE_CALLBACK: - ret = vchiq_bulk_xfer_callback_interruptible(instance, handle, - (void *)data, NULL, - size, mode, userdata, - VCHIQ_BULK_RECEIVE); - break; - case VCHIQ_BULK_MODE_BLOCKING: - ret = vchiq_blocking_bulk_transfer(instance, handle, (void *)data, size, - VCHIQ_BULK_RECEIVE); - break; - default: - return -EINVAL; - } - - /* - * vchiq_*_bulk_transfer() may return -EAGAIN, so we need - * to implement a retry mechanism since this function is - * supposed to block until queued - */ - if (ret != -EAGAIN) - break; - - msleep(1); + switch (mode) { + case VCHIQ_BULK_MODE_NOCALLBACK: + case VCHIQ_BULK_MODE_CALLBACK: + + bulk_params.offset = (void *)data; + bulk_params.mode = mode; + bulk_params.size = size; + bulk_params.cb_data = userdata; + bulk_params.dir = VCHIQ_BULK_RECEIVE; + + ret = vchiq_bulk_xfer_callback(instance, handle, &bulk_params); + break; + case VCHIQ_BULK_MODE_BLOCKING: + bulk_params.offset = (void *)data; + bulk_params.mode = mode; + bulk_params.size = size; + bulk_params.dir = VCHIQ_BULK_RECEIVE; + + ret = vchiq_blocking_bulk_transfer(instance, handle, &bulk_params); + break; + default: + return -EINVAL; } return ret; @@ -924,8 +558,8 @@ int vchiq_bulk_receive(struct vchiq_instance *instance, unsigned int handle, EXPORT_SYMBOL(vchiq_bulk_receive); static int -vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void *data, - unsigned int size, enum vchiq_bulk_dir dir) +vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_bulk *bulk_params) { struct vchiq_service *service; struct bulk_waiter_node *waiter = NULL, *iter; @@ -953,13 +587,14 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl if (bulk) { /* This thread has an outstanding bulk transfer. */ /* FIXME: why compare a dma address to a pointer? */ - if ((bulk->data != (dma_addr_t)(uintptr_t)data) || (bulk->size != size)) { + if ((bulk->dma_addr != (dma_addr_t)(uintptr_t)bulk_params->dma_addr) || + (bulk->size != bulk_params->size)) { /* * This is not a retry of the previous one. * Cancel the signal when the transfer completes. */ spin_lock(&service->state->bulk_waiter_spinlock); - bulk->userdata = NULL; + bulk->waiter = NULL; spin_unlock(&service->state->bulk_waiter_spinlock); } } @@ -969,15 +604,16 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl return -ENOMEM; } - ret = vchiq_bulk_xfer_blocking_interruptible(instance, handle, data, NULL, size, - &waiter->bulk_waiter, dir); + bulk_params->waiter = &waiter->bulk_waiter; + + ret = vchiq_bulk_xfer_blocking(instance, handle, bulk_params); if ((ret != -EAGAIN) || fatal_signal_pending(current) || !waiter->bulk_waiter.bulk) { struct vchiq_bulk *bulk = waiter->bulk_waiter.bulk; if (bulk) { /* Cancel the signal when the transfer completes. */ spin_lock(&service->state->bulk_waiter_spinlock); - bulk->userdata = NULL; + bulk->waiter = NULL; spin_unlock(&service->state->bulk_waiter_spinlock); } kfree(waiter); @@ -996,7 +632,7 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl static int add_completion(struct vchiq_instance *instance, enum vchiq_reason reason, struct vchiq_header *header, struct user_service *user_service, - void *bulk_userdata) + void *cb_data, void __user *cb_userdata) { struct vchiq_completion_data_kernel *completion; struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(instance->state->dev); @@ -1026,7 +662,8 @@ add_completion(struct vchiq_instance *instance, enum vchiq_reason reason, completion->reason = reason; /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */ completion->service_userdata = user_service->service; - completion->bulk_userdata = bulk_userdata; + completion->cb_data = cb_data; + completion->cb_userdata = cb_userdata; if (reason == VCHIQ_SERVICE_CLOSED) { /* @@ -1057,8 +694,8 @@ add_completion(struct vchiq_instance *instance, enum vchiq_reason reason, static int service_single_message(struct vchiq_instance *instance, - enum vchiq_reason reason, - struct vchiq_service *service, void *bulk_userdata) + enum vchiq_reason reason, struct vchiq_service *service, + void *cb_data, void __user *cb_userdata) { struct user_service *user_service; @@ -1076,7 +713,7 @@ service_single_message(struct vchiq_instance *instance, dev_dbg(instance->state->dev, "arm: Inserting extra MESSAGE_AVAILABLE\n"); ret = add_completion(instance, reason, NULL, user_service, - bulk_userdata); + cb_data, cb_userdata); if (ret) return ret; } @@ -1094,7 +731,8 @@ service_single_message(struct vchiq_instance *instance, int service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, - struct vchiq_header *header, unsigned int handle, void *bulk_userdata) + struct vchiq_header *header, unsigned int handle, + void *cb_data, void __user *cb_userdata) { /* * How do we ensure the callback goes to the right client? @@ -1133,9 +771,9 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, rcu_read_unlock(); dev_dbg(service->state->dev, - "arm: service %p(%d,%p), reason %d, header %p, instance %p, bulk_userdata %p\n", + "arm: service %p(%d,%p), reason %d, header %p, instance %p, cb_data %p, cb_userdata %p\n", user_service, service->localport, user_service->userdata, - reason, header, instance, bulk_userdata); + reason, header, instance, cb_data, cb_userdata); if (header && user_service->is_vchi) { spin_lock(&service->state->msg_queue_spinlock); @@ -1147,8 +785,8 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, DEBUG_TRACE(SERVICE_CALLBACK_LINE); DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); - ret = service_single_message(instance, reason, - service, bulk_userdata); + ret = service_single_message(instance, reason, service, + cb_data, cb_userdata); if (ret) { DEBUG_TRACE(SERVICE_CALLBACK_LINE); vchiq_service_put(service); @@ -1186,7 +824,7 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, return 0; return add_completion(instance, reason, header, user_service, - bulk_userdata); + cb_data, cb_userdata); } void vchiq_dump_platform_instances(struct vchiq_state *state, struct seq_file *f) @@ -1273,7 +911,8 @@ static int vchiq_keepalive_vchiq_callback(struct vchiq_instance *instance, enum vchiq_reason reason, struct vchiq_header *header, - unsigned int service_user, void *bulk_user) + unsigned int service_user, + void *cb_data, void __user *cb_userdata) { dev_err(instance->state->dev, "suspend: %s: callback reason %d\n", __func__, reason); @@ -1715,7 +1354,6 @@ MODULE_DEVICE_TABLE(of, vchiq_of_match); static int vchiq_probe(struct platform_device *pdev) { - struct device_node *fw_node; const struct vchiq_platform_info *info; struct vchiq_drv_mgmt *mgmt; int ret; @@ -1724,8 +1362,8 @@ static int vchiq_probe(struct platform_device *pdev) if (!info) return -EINVAL; - fw_node = of_find_compatible_node(NULL, NULL, - "raspberrypi,bcm2835-firmware"); + struct device_node *fw_node __free(device_node) = + of_find_compatible_node(NULL, NULL, "raspberrypi,bcm2835-firmware"); if (!fw_node) { dev_err(&pdev->dev, "Missing firmware node\n"); return -ENOENT; @@ -1736,7 +1374,6 @@ static int vchiq_probe(struct platform_device *pdev) return -ENOMEM; mgmt->fw = devm_rpi_firmware_get(&pdev->dev, fw_node); - of_node_put(fw_node); if (!mgmt->fw) return -EPROBE_DEFER; @@ -1744,8 +1381,10 @@ static int vchiq_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mgmt); ret = vchiq_platform_init(pdev, &mgmt->state); - if (ret) - goto failed_platform_init; + if (ret) { + dev_err(&pdev->dev, "arm: Could not initialize vchiq platform\n"); + return ret; + } vchiq_debugfs_init(&mgmt->state); @@ -1759,18 +1398,13 @@ static int vchiq_probe(struct platform_device *pdev) ret = vchiq_register_chrdev(&pdev->dev); if (ret) { dev_err(&pdev->dev, "arm: Failed to initialize vchiq cdev\n"); - goto error_exit; + return ret; } bcm2835_audio = vchiq_device_register(&pdev->dev, "bcm2835-audio"); bcm2835_camera = vchiq_device_register(&pdev->dev, "bcm2835-camera"); return 0; - -failed_platform_init: - dev_err(&pdev->dev, "arm: Could not initialize vchiq platform\n"); -error_exit: - return ret; } static void vchiq_remove(struct platform_device *pdev) @@ -1797,7 +1431,7 @@ static struct platform_driver vchiq_driver = { .of_match_table = vchiq_of_match, }, .probe = vchiq_probe, - .remove_new = vchiq_remove, + .remove = vchiq_remove, }; static int __init vchiq_driver_init(void) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index b402aac333d9b..e32b02f990244 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -155,7 +155,8 @@ static inline int vchiq_register_chrdev(struct device *parent) { return 0; } extern int service_callback(struct vchiq_instance *vchiq_instance, enum vchiq_reason reason, - struct vchiq_header *header, unsigned int handle, void *bulk_userdata); + struct vchiq_header *header, unsigned int handle, + void *cb_data, void __user *cb_userdata); extern void free_bulk_waiter(struct vchiq_instance *instance); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 1f94db6e0cd98..8d5795db4f39f 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include @@ -61,12 +63,16 @@ #define MAKE_REMOTE_USE (VCHIQ_MSG_REMOTE_USE << TYPE_SHIFT) #define MAKE_REMOTE_USE_ACTIVE (VCHIQ_MSG_REMOTE_USE_ACTIVE << TYPE_SHIFT) +#define PAGELIST_WRITE 0 +#define PAGELIST_READ 1 +#define PAGELIST_READ_WITH_FRAGMENTS 2 + +#define BELL2 0x08 + /* Ensure the fields are wide enough */ -static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0, 0, VCHIQ_PORT_MAX)) - == 0); +static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0, 0, VCHIQ_PORT_MAX)) == 0); static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0, VCHIQ_PORT_MAX, 0)) == 0); -static_assert((unsigned int)VCHIQ_PORT_MAX < - (unsigned int)VCHIQ_PORT_FREE); +static_assert((unsigned int)VCHIQ_PORT_MAX < (unsigned int)VCHIQ_PORT_FREE); #define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING, 0, 0) #define VCHIQ_MSGID_CLAIMED 0x40000000 @@ -449,15 +455,26 @@ mark_service_closing(struct vchiq_service *service) static inline int make_service_callback(struct vchiq_service *service, enum vchiq_reason reason, - struct vchiq_header *header, void *bulk_userdata) + struct vchiq_header *header, struct vchiq_bulk *bulk) { + void *cb_data = NULL; + void __user *cb_userdata = NULL; int status; - dev_dbg(service->state->dev, "core: %d: callback:%d (%s, %pK, %pK)\n", + /* + * If a bulk transfer is in progress, pass bulk->cb_*data to the + * callback function. + */ + if (bulk) { + cb_data = bulk->cb_data; + cb_userdata = bulk->cb_userdata; + } + + dev_dbg(service->state->dev, "core: %d: callback:%d (%s, %pK, %pK %pK)\n", service->state->id, service->localport, reason_names[reason], - header, bulk_userdata); + header, cb_data, cb_userdata); status = service->base.callback(service->instance, reason, header, service->handle, - bulk_userdata); + cb_data, cb_userdata); if (status && (status != -EAGAIN)) { dev_warn(service->state->dev, "core: %d: ignoring ERROR from callback to service %x\n", @@ -526,6 +543,25 @@ remote_event_wait(wait_queue_head_t *wq, struct remote_event *event) return ret; } +static void +remote_event_signal(struct vchiq_state *state, struct remote_event *event) +{ + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(state->dev); + + /* + * Ensure that all writes to shared data structures have completed + * before signalling the peer. + */ + wmb(); + + event->fired = 1; + + dsb(sy); /* data barrier operation */ + + if (event->armed) + writel(0, mgmt->regs + BELL2); /* trigger vc interrupt */ +} + /* * Acknowledge that the event has been signalled, and wake any waiters. Usually * called as a result of the doorbell being rung. @@ -911,6 +947,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, struct vchiq_service_quota *quota = NULL; struct vchiq_header *header; int type = VCHIQ_MSG_TYPE(msgid); + int svc_fourcc; size_t stride; @@ -922,7 +959,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, if (!(flags & QMFLAGS_NO_MUTEX_LOCK) && mutex_lock_killable(&state->slot_mutex)) - return -EAGAIN; + return -EINTR; if (type == VCHIQ_MSG_DATA) { int tx_end_index; @@ -962,8 +999,8 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, spin_unlock(&state->quota_spinlock); mutex_unlock(&state->slot_mutex); - if (wait_for_completion_interruptible(&state->data_quota_event)) - return -EAGAIN; + if (wait_for_completion_killable(&state->data_quota_event)) + return -EINTR; mutex_lock(&state->slot_mutex); spin_lock(&state->quota_spinlock); @@ -986,12 +1023,12 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, quota->message_use_count, quota->slot_use_count); VCHIQ_SERVICE_STATS_INC(service, quota_stalls); mutex_unlock(&state->slot_mutex); - if (wait_for_completion_interruptible("a->quota_event)) - return -EAGAIN; + if (wait_for_completion_killable("a->quota_event)) + return -EINTR; if (service->closing) return -EHOSTDOWN; if (mutex_lock_killable(&state->slot_mutex)) - return -EAGAIN; + return -EINTR; if (service->srvstate != VCHIQ_SRVSTATE_OPEN) { /* The service has been closed */ mutex_unlock(&state->slot_mutex); @@ -1103,17 +1140,13 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, header->msgid = msgid; header->size = size; - { - int svc_fourcc; - - svc_fourcc = service - ? service->base.fourcc - : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); + svc_fourcc = service ? service->base.fourcc + : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); - dev_dbg(state->dev, "core_msg: Sent Msg %s(%u) to %p4cc s:%u d:%d len:%zu\n", - msg_type_str(VCHIQ_MSG_TYPE(msgid)), VCHIQ_MSG_TYPE(msgid), - &svc_fourcc, VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid), size); - } + dev_dbg(state->dev, "core_msg: Sent Msg %s(%u) to %p4cc s:%u d:%d len:%zu\n", + msg_type_str(VCHIQ_MSG_TYPE(msgid)), + VCHIQ_MSG_TYPE(msgid), &svc_fourcc, + VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid), size); /* Make sure the new header is visible to the peer. */ wmb(); @@ -1175,9 +1208,8 @@ queue_message_sync(struct vchiq_state *state, struct vchiq_service *service, state->id, msg_type_str(VCHIQ_MSG_TYPE(msgid)), header, size, VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid)); - callback_result = - copy_message_data(copy_callback, context, - header->data, size); + callback_result = copy_message_data(copy_callback, context, + header->data, size); if (callback_result < 0) { mutex_unlock(&state->slot_mutex); @@ -1288,6 +1320,42 @@ get_bulk_reason(struct vchiq_bulk *bulk) return VCHIQ_BULK_RECEIVE_DONE; } +static int service_notify_bulk(struct vchiq_service *service, + struct vchiq_bulk *bulk) +{ + if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) { + if (bulk->dir == VCHIQ_BULK_TRANSMIT) { + VCHIQ_SERVICE_STATS_INC(service, bulk_tx_count); + VCHIQ_SERVICE_STATS_ADD(service, bulk_tx_bytes, + bulk->actual); + } else { + VCHIQ_SERVICE_STATS_INC(service, bulk_rx_count); + VCHIQ_SERVICE_STATS_ADD(service, bulk_rx_bytes, + bulk->actual); + } + } else { + VCHIQ_SERVICE_STATS_INC(service, bulk_aborted_count); + } + + if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { + struct bulk_waiter *waiter; + + spin_lock(&service->state->bulk_waiter_spinlock); + waiter = bulk->waiter; + if (waiter) { + waiter->actual = bulk->actual; + complete(&waiter->event); + } + spin_unlock(&service->state->bulk_waiter_spinlock); + } else if (bulk->mode == VCHIQ_BULK_MODE_CALLBACK) { + enum vchiq_reason reason = get_bulk_reason(bulk); + + return make_service_callback(service, reason, NULL, bulk); + } + + return 0; +} + /* Called by the slot handler - don't hold the bulk mutex */ static int notify_bulks(struct vchiq_service *service, struct vchiq_bulk_queue *queue, @@ -1311,38 +1379,10 @@ notify_bulks(struct vchiq_service *service, struct vchiq_bulk_queue *queue, * Only generate callbacks for non-dummy bulk * requests, and non-terminated services */ - if (bulk->data && service->instance) { - if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) { - if (bulk->dir == VCHIQ_BULK_TRANSMIT) { - VCHIQ_SERVICE_STATS_INC(service, bulk_tx_count); - VCHIQ_SERVICE_STATS_ADD(service, bulk_tx_bytes, - bulk->actual); - } else { - VCHIQ_SERVICE_STATS_INC(service, bulk_rx_count); - VCHIQ_SERVICE_STATS_ADD(service, bulk_rx_bytes, - bulk->actual); - } - } else { - VCHIQ_SERVICE_STATS_INC(service, bulk_aborted_count); - } - if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { - struct bulk_waiter *waiter; - - spin_lock(&service->state->bulk_waiter_spinlock); - waiter = bulk->userdata; - if (waiter) { - waiter->actual = bulk->actual; - complete(&waiter->event); - } - spin_unlock(&service->state->bulk_waiter_spinlock); - } else if (bulk->mode == VCHIQ_BULK_MODE_CALLBACK) { - enum vchiq_reason reason = - get_bulk_reason(bulk); - status = make_service_callback(service, reason, NULL, - bulk->userdata); - if (status == -EAGAIN) - break; - } + if (bulk->dma_addr && service->instance) { + status = service_notify_bulk(service, bulk); + if (status == -EAGAIN) + break; } queue->remove++; @@ -1415,6 +1455,331 @@ poll_services(struct vchiq_state *state) poll_services_of_group(state, group); } +static void +cleanup_pagelistinfo(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo) +{ + if (pagelistinfo->scatterlist_mapped) { + dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, + pagelistinfo->num_pages, pagelistinfo->dma_dir); + } + + if (pagelistinfo->pages_need_release) + unpin_user_pages(pagelistinfo->pages, pagelistinfo->num_pages); + + dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, + pagelistinfo->pagelist, pagelistinfo->dma_addr); +} + +static inline bool +is_adjacent_block(u32 *addrs, dma_addr_t addr, unsigned int k) +{ + u32 tmp; + + if (!k) + return false; + + tmp = (addrs[k - 1] & PAGE_MASK) + + (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT); + + return tmp == (addr & PAGE_MASK); +} + +/* There is a potential problem with partial cache lines (pages?) + * at the ends of the block when reading. If the CPU accessed anything in + * the same line (page?) then it may have pulled old data into the cache, + * obscuring the new data underneath. We can solve this by transferring the + * partial cache lines separately, and allowing the ARM to copy into the + * cached area. + */ +static struct vchiq_pagelist_info * +create_pagelist(struct vchiq_instance *instance, struct vchiq_bulk *bulk) +{ + struct vchiq_drv_mgmt *drv_mgmt; + struct pagelist *pagelist; + struct vchiq_pagelist_info *pagelistinfo; + struct page **pages; + u32 *addrs; + unsigned int num_pages, offset, i, k; + int actual_pages; + size_t pagelist_size; + struct scatterlist *scatterlist, *sg; + int dma_buffers; + unsigned int cache_line_size; + dma_addr_t dma_addr; + size_t count = bulk->size; + unsigned short type = (bulk->dir == VCHIQ_BULK_RECEIVE) + ? PAGELIST_READ : PAGELIST_WRITE; + + if (count >= INT_MAX - PAGE_SIZE) + return NULL; + + drv_mgmt = dev_get_drvdata(instance->state->dev); + + if (bulk->offset) + offset = (uintptr_t)bulk->offset & (PAGE_SIZE - 1); + else + offset = (uintptr_t)bulk->uoffset & (PAGE_SIZE - 1); + num_pages = DIV_ROUND_UP(count + offset, PAGE_SIZE); + + if ((size_t)num_pages > (SIZE_MAX - sizeof(struct pagelist) - + sizeof(struct vchiq_pagelist_info)) / + (sizeof(u32) + sizeof(pages[0]) + + sizeof(struct scatterlist))) + return NULL; + + pagelist_size = sizeof(struct pagelist) + + (num_pages * sizeof(u32)) + + (num_pages * sizeof(pages[0]) + + (num_pages * sizeof(struct scatterlist))) + + sizeof(struct vchiq_pagelist_info); + + /* Allocate enough storage to hold the page pointers and the page + * list + */ + pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, + GFP_KERNEL); + + dev_dbg(instance->state->dev, "arm: %pK\n", pagelist); + + if (!pagelist) + return NULL; + + addrs = pagelist->addrs; + pages = (struct page **)(addrs + num_pages); + scatterlist = (struct scatterlist *)(pages + num_pages); + pagelistinfo = (struct vchiq_pagelist_info *) + (scatterlist + num_pages); + + pagelist->length = count; + pagelist->type = type; + pagelist->offset = offset; + + /* Populate the fields of the pagelistinfo structure */ + pagelistinfo->pagelist = pagelist; + pagelistinfo->pagelist_buffer_size = pagelist_size; + pagelistinfo->dma_addr = dma_addr; + pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; + pagelistinfo->num_pages = num_pages; + pagelistinfo->pages_need_release = 0; + pagelistinfo->pages = pages; + pagelistinfo->scatterlist = scatterlist; + pagelistinfo->scatterlist_mapped = 0; + + if (bulk->offset) { + unsigned long length = count; + unsigned int off = offset; + + for (actual_pages = 0; actual_pages < num_pages; + actual_pages++) { + struct page *pg = + vmalloc_to_page(((unsigned int *)bulk->offset + + (actual_pages * PAGE_SIZE))); + size_t bytes = PAGE_SIZE - off; + + if (!pg) { + cleanup_pagelistinfo(instance, pagelistinfo); + return NULL; + } + + if (bytes > length) + bytes = length; + pages[actual_pages] = pg; + length -= bytes; + off = 0; + } + /* do not try and release vmalloc pages */ + } else { + actual_pages = + pin_user_pages_fast((unsigned long)bulk->uoffset & PAGE_MASK, num_pages, + type == PAGELIST_READ, pages); + + if (actual_pages != num_pages) { + dev_dbg(instance->state->dev, "arm: Only %d/%d pages locked\n", + actual_pages, num_pages); + + /* This is probably due to the process being killed */ + if (actual_pages > 0) + unpin_user_pages(pages, actual_pages); + cleanup_pagelistinfo(instance, pagelistinfo); + return NULL; + } + /* release user pages */ + pagelistinfo->pages_need_release = 1; + } + + /* + * Initialize the scatterlist so that the magic cookie + * is filled if debugging is enabled + */ + sg_init_table(scatterlist, num_pages); + /* Now set the pages for each scatterlist */ + for (i = 0; i < num_pages; i++) { + unsigned int len = PAGE_SIZE - offset; + + if (len > count) + len = count; + sg_set_page(scatterlist + i, pages[i], len, offset); + offset = 0; + count -= len; + } + + dma_buffers = dma_map_sg(instance->state->dev, + scatterlist, + num_pages, + pagelistinfo->dma_dir); + + if (dma_buffers == 0) { + cleanup_pagelistinfo(instance, pagelistinfo); + return NULL; + } + + pagelistinfo->scatterlist_mapped = 1; + + /* Combine adjacent blocks for performance */ + k = 0; + for_each_sg(scatterlist, sg, dma_buffers, i) { + unsigned int len = sg_dma_len(sg); + dma_addr_t addr = sg_dma_address(sg); + + /* Note: addrs is the address + page_count - 1 + * The firmware expects blocks after the first to be page- + * aligned and a multiple of the page size + */ + WARN_ON(len == 0); + WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); + WARN_ON(i && (addr & ~PAGE_MASK)); + if (is_adjacent_block(addrs, addr, k)) + addrs[k - 1] += ((len + PAGE_SIZE - 1) >> PAGE_SHIFT); + else + addrs[k++] = (addr & PAGE_MASK) | + (((len + PAGE_SIZE - 1) >> PAGE_SHIFT) - 1); + } + + /* Partial cache lines (fragments) require special measures */ + cache_line_size = drv_mgmt->info->cache_line_size; + if ((type == PAGELIST_READ) && + ((pagelist->offset & (cache_line_size - 1)) || + ((pagelist->offset + pagelist->length) & (cache_line_size - 1)))) { + char *fragments; + + if (down_interruptible(&drv_mgmt->free_fragments_sema)) { + cleanup_pagelistinfo(instance, pagelistinfo); + return NULL; + } + + WARN_ON(!drv_mgmt->free_fragments); + + down(&drv_mgmt->free_fragments_mutex); + fragments = drv_mgmt->free_fragments; + WARN_ON(!fragments); + drv_mgmt->free_fragments = *(char **)drv_mgmt->free_fragments; + up(&drv_mgmt->free_fragments_mutex); + pagelist->type = PAGELIST_READ_WITH_FRAGMENTS + + (fragments - drv_mgmt->fragments_base) / drv_mgmt->fragments_size; + } + + return pagelistinfo; +} + +static void +free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo, + int actual) +{ + struct vchiq_drv_mgmt *drv_mgmt; + struct pagelist *pagelist = pagelistinfo->pagelist; + struct page **pages = pagelistinfo->pages; + unsigned int num_pages = pagelistinfo->num_pages; + unsigned int cache_line_size; + + dev_dbg(instance->state->dev, "arm: %pK, %d\n", pagelistinfo->pagelist, actual); + + drv_mgmt = dev_get_drvdata(instance->state->dev); + + /* + * NOTE: dma_unmap_sg must be called before the + * cpu can touch any of the data/pages. + */ + dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, + pagelistinfo->num_pages, pagelistinfo->dma_dir); + pagelistinfo->scatterlist_mapped = 0; + + /* Deal with any partial cache lines (fragments) */ + cache_line_size = drv_mgmt->info->cache_line_size; + if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS && drv_mgmt->fragments_base) { + char *fragments = drv_mgmt->fragments_base + + (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * + drv_mgmt->fragments_size; + int head_bytes, tail_bytes; + + head_bytes = (cache_line_size - pagelist->offset) & + (cache_line_size - 1); + tail_bytes = (pagelist->offset + actual) & + (cache_line_size - 1); + + if ((actual >= 0) && (head_bytes != 0)) { + if (head_bytes > actual) + head_bytes = actual; + + memcpy_to_page(pages[0], pagelist->offset, + fragments, head_bytes); + } + if ((actual >= 0) && (head_bytes < actual) && + (tail_bytes != 0)) + memcpy_to_page(pages[num_pages - 1], + (pagelist->offset + actual) & + (PAGE_SIZE - 1) & ~(cache_line_size - 1), + fragments + cache_line_size, + tail_bytes); + + down(&drv_mgmt->free_fragments_mutex); + *(char **)fragments = drv_mgmt->free_fragments; + drv_mgmt->free_fragments = fragments; + up(&drv_mgmt->free_fragments_mutex); + up(&drv_mgmt->free_fragments_sema); + } + + /* Need to mark all the pages dirty. */ + if (pagelist->type != PAGELIST_WRITE && + pagelistinfo->pages_need_release) { + unsigned int i; + + for (i = 0; i < num_pages; i++) + set_page_dirty(pages[i]); + } + + cleanup_pagelistinfo(instance, pagelistinfo); +} + +static int +vchiq_prepare_bulk_data(struct vchiq_instance *instance, struct vchiq_bulk *bulk) +{ + struct vchiq_pagelist_info *pagelistinfo; + + pagelistinfo = create_pagelist(instance, bulk); + + if (!pagelistinfo) + return -ENOMEM; + + bulk->dma_addr = pagelistinfo->dma_addr; + + /* + * Store the pagelistinfo address in remote_data, + * which isn't used by the slave. + */ + bulk->remote_data = pagelistinfo; + + return 0; +} + +static void +vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk) +{ + if (bulk && bulk->remote_data && bulk->actual) + free_pagelist(instance, (struct vchiq_pagelist_info *)bulk->remote_data, + bulk->actual); +} + /* Called with the bulk_mutex held */ static void abort_outstanding_bulks(struct vchiq_service *service, @@ -1452,7 +1817,7 @@ abort_outstanding_bulks(struct vchiq_service *service, service->remoteport, bulk->size, bulk->remote_size); } else { /* fabricate a matching dummy bulk */ - bulk->data = 0; + bulk->dma_addr = 0; bulk->size = 0; bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : @@ -1468,8 +1833,10 @@ static int parse_open(struct vchiq_state *state, struct vchiq_header *header) { const struct vchiq_open_payload *payload; + struct vchiq_openack_payload ack_payload; struct vchiq_service *service = NULL; int msgid, size; + int openack_id; unsigned int localport, remoteport, fourcc; short version, version_min; @@ -1504,34 +1871,36 @@ parse_open(struct vchiq_state *state, struct vchiq_header *header) } service->peer_version = version; - if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { - struct vchiq_openack_payload ack_payload = { - service->version - }; - int openack_id = MAKE_OPENACK(service->localport, remoteport); + if (service->srvstate != VCHIQ_SRVSTATE_LISTENING) + goto done; - if (state->version_common < - VCHIQ_VERSION_SYNCHRONOUS_MODE) - service->sync = 0; + ack_payload.version = service->version; + openack_id = MAKE_OPENACK(service->localport, remoteport); - /* Acknowledge the OPEN */ - if (service->sync) { - if (queue_message_sync(state, NULL, openack_id, memcpy_copy_callback, - &ack_payload, sizeof(ack_payload)) == -EAGAIN) - goto bail_not_ready; + if (state->version_common < VCHIQ_VERSION_SYNCHRONOUS_MODE) + service->sync = 0; - /* The service is now open */ - set_service_state(service, VCHIQ_SRVSTATE_OPENSYNC); - } else { - if (queue_message(state, NULL, openack_id, memcpy_copy_callback, - &ack_payload, sizeof(ack_payload), 0) == -EAGAIN) - goto bail_not_ready; + /* Acknowledge the OPEN */ + if (service->sync) { + if (queue_message_sync(state, NULL, openack_id, + memcpy_copy_callback, + &ack_payload, + sizeof(ack_payload)) == -EAGAIN) + goto bail_not_ready; - /* The service is now open */ - set_service_state(service, VCHIQ_SRVSTATE_OPEN); - } + /* The service is now open */ + set_service_state(service, VCHIQ_SRVSTATE_OPENSYNC); + } else { + if (queue_message(state, NULL, openack_id, + memcpy_copy_callback, &ack_payload, + sizeof(ack_payload), 0) == -EINTR) + goto bail_not_ready; + + /* The service is now open */ + set_service_state(service, VCHIQ_SRVSTATE_OPEN); } +done: /* Success - the message has been dealt with */ vchiq_service_put(service); return 1; @@ -1539,7 +1908,7 @@ parse_open(struct vchiq_state *state, struct vchiq_header *header) fail_open: /* No available service, or an invalid request - send a CLOSE */ if (queue_message(state, NULL, MAKE_CLOSE(0, VCHIQ_MSG_SRCPORT(msgid)), - NULL, NULL, 0, 0) == -EAGAIN) + NULL, NULL, 0, 0) == -EINTR) goto bail_not_ready; return 1; @@ -1753,7 +2122,7 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) dev_dbg(state->dev, "core: %d: prs %s@%pK (%d->%d) %x@%pad\n", state->id, msg_type_str(type), header, remoteport, - localport, bulk->actual, &bulk->data); + localport, bulk->actual, &bulk->dma_addr); dev_dbg(state->dev, "core: %d: prs:%d %cx li=%x ri=%x p=%x\n", state->id, localport, @@ -1786,7 +2155,7 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) { /* Send a PAUSE in response */ if (queue_message(state, NULL, MAKE_PAUSE, NULL, NULL, 0, - QMFLAGS_NO_MUTEX_UNLOCK) == -EAGAIN) + QMFLAGS_NO_MUTEX_UNLOCK) == -EINTR) goto bail_not_ready; } /* At this point slot_mutex is held */ @@ -1903,7 +2272,7 @@ handle_poll(struct vchiq_state *state) case VCHIQ_CONNSTATE_PAUSING: if (queue_message(state, NULL, MAKE_PAUSE, NULL, NULL, 0, - QMFLAGS_NO_MUTEX_UNLOCK) != -EAGAIN) { + QMFLAGS_NO_MUTEX_UNLOCK) != -EINTR) { vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSE_SENT); } else { /* Retry later */ @@ -1913,7 +2282,7 @@ handle_poll(struct vchiq_state *state) case VCHIQ_CONNSTATE_RESUMING: if (queue_message(state, NULL, MAKE_RESUME, NULL, NULL, 0, - QMFLAGS_NO_MUTEX_LOCK) != -EAGAIN) { + QMFLAGS_NO_MUTEX_LOCK) != -EINTR) { vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); } else { /* @@ -2662,56 +3031,58 @@ close_service_complete(struct vchiq_service *service, int failstate) * returned to user context. */ static int -vchiq_bulk_xfer_queue_msg_interruptible(struct vchiq_service *service, - void *offset, void __user *uoffset, - int size, void *userdata, - enum vchiq_bulk_mode mode, - enum vchiq_bulk_dir dir) +vchiq_bulk_xfer_queue_msg_killable(struct vchiq_service *service, + struct vchiq_bulk *bulk_params) { struct vchiq_bulk_queue *queue; struct bulk_waiter *bulk_waiter = NULL; struct vchiq_bulk *bulk; struct vchiq_state *state = service->state; - const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; - const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? + const char dir_char = (bulk_params->dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; + const int dir_msgtype = (bulk_params->dir == VCHIQ_BULK_TRANSMIT) ? VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; int status = -EINVAL; int payload[2]; - if (mode == VCHIQ_BULK_MODE_BLOCKING) { - bulk_waiter = userdata; + if (bulk_params->mode == VCHIQ_BULK_MODE_BLOCKING) { + bulk_waiter = bulk_params->waiter; init_completion(&bulk_waiter->event); bulk_waiter->actual = 0; bulk_waiter->bulk = NULL; } - queue = (dir == VCHIQ_BULK_TRANSMIT) ? + queue = (bulk_params->dir == VCHIQ_BULK_TRANSMIT) ? &service->bulk_tx : &service->bulk_rx; if (mutex_lock_killable(&service->bulk_mutex)) - return -EAGAIN; + return -EINTR; if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) { VCHIQ_SERVICE_STATS_INC(service, bulk_stalls); do { mutex_unlock(&service->bulk_mutex); - if (wait_for_completion_interruptible(&service->bulk_remove_event)) - return -EAGAIN; + if (wait_for_completion_killable(&service->bulk_remove_event)) + return -EINTR; if (mutex_lock_killable(&service->bulk_mutex)) - return -EAGAIN; + return -EINTR; } while (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS); } bulk = &queue->bulks[BULK_INDEX(queue->local_insert)]; - bulk->mode = mode; - bulk->dir = dir; - bulk->userdata = userdata; - bulk->size = size; + /* Initiliaze the 'bulk' slot with bulk parameters passed in. */ + bulk->mode = bulk_params->mode; + bulk->dir = bulk_params->dir; + bulk->waiter = bulk_params->waiter; + bulk->cb_data = bulk_params->cb_data; + bulk->cb_userdata = bulk_params->cb_userdata; + bulk->size = bulk_params->size; + bulk->offset = bulk_params->offset; + bulk->uoffset = bulk_params->uoffset; bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; - if (vchiq_prepare_bulk_data(service->instance, bulk, offset, uoffset, size, dir)) + if (vchiq_prepare_bulk_data(service->instance, bulk)) goto unlock_error_exit; /* @@ -2722,21 +3093,21 @@ vchiq_bulk_xfer_queue_msg_interruptible(struct vchiq_service *service, dev_dbg(state->dev, "core: %d: bt (%d->%d) %cx %x@%pad %pK\n", state->id, service->localport, service->remoteport, - dir_char, size, &bulk->data, userdata); + dir_char, bulk->size, &bulk->dma_addr, bulk->cb_data); /* * The slot mutex must be held when the service is being closed, so * claim it here to ensure that isn't happening */ if (mutex_lock_killable(&state->slot_mutex)) { - status = -EAGAIN; + status = -EINTR; goto cancel_bulk_error_exit; } if (service->srvstate != VCHIQ_SRVSTATE_OPEN) goto unlock_both_error_exit; - payload[0] = lower_32_bits(bulk->data); + payload[0] = lower_32_bits(bulk->dma_addr); payload[1] = bulk->size; status = queue_message(state, NULL, @@ -2761,13 +3132,13 @@ vchiq_bulk_xfer_queue_msg_interruptible(struct vchiq_service *service, state->id, service->localport, dir_char, queue->local_insert, queue->remote_insert, queue->process); - if (bulk_waiter) { - bulk_waiter->bulk = bulk; - if (wait_for_completion_interruptible(&bulk_waiter->event)) - status = -EAGAIN; - else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) - status = -EINVAL; - } + if (bulk_waiter) { + bulk_waiter->bulk = bulk; + if (wait_for_completion_killable(&bulk_waiter->event)) + status = -EINTR; + else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) + status = -EINVAL; + } return status; @@ -2803,19 +3174,21 @@ vchiq_close_service_internal(struct vchiq_service *service, int close_recvd) if (close_recvd) { dev_err(state->dev, "core: (1) called in state %s\n", srvstate_names[service->srvstate]); - } else if (is_server) { - if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { - status = -EINVAL; - } else { - service->client_id = 0; - service->remoteport = VCHIQ_PORT_FREE; - if (service->srvstate == VCHIQ_SRVSTATE_CLOSEWAIT) - set_service_state(service, VCHIQ_SRVSTATE_LISTENING); - } - complete(&service->remove_event); - } else { + break; + } else if (!is_server) { vchiq_free_service_internal(service); + break; + } + + if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { + status = -EINVAL; + } else { + service->client_id = 0; + service->remoteport = VCHIQ_PORT_FREE; + if (service->srvstate == VCHIQ_SRVSTATE_CLOSEWAIT) + set_service_state(service, VCHIQ_SRVSTATE_LISTENING); } + complete(&service->remove_event); break; case VCHIQ_SRVSTATE_OPENING: if (close_recvd) { @@ -2945,6 +3318,7 @@ int vchiq_connect_internal(struct vchiq_state *state, struct vchiq_instance *instance) { struct vchiq_service *service; + int status = 0; int i; /* Find all services registered to this client and enable them. */ @@ -2956,9 +3330,10 @@ vchiq_connect_internal(struct vchiq_state *state, struct vchiq_instance *instanc } if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { - if (queue_message(state, NULL, MAKE_CONNECT, NULL, NULL, 0, - QMFLAGS_IS_BLOCKING) == -EAGAIN) - return -EAGAIN; + status = queue_message(state, NULL, MAKE_CONNECT, NULL, NULL, 0, + QMFLAGS_IS_BLOCKING); + if (status) + return status; vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); } @@ -2971,7 +3346,7 @@ vchiq_connect_internal(struct vchiq_state *state, struct vchiq_instance *instanc complete(&state->connect); } - return 0; + return status; } void @@ -3105,12 +3480,10 @@ vchiq_remove_service(struct vchiq_instance *instance, unsigned int handle) } int -vchiq_bulk_xfer_blocking_interruptible(struct vchiq_instance *instance, unsigned int handle, - void *offset, void __user *uoffset, int size, - void __user *userdata, enum vchiq_bulk_dir dir) +vchiq_bulk_xfer_blocking(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_bulk *bulk_params) { struct vchiq_service *service = find_service_by_handle(instance, handle); - enum vchiq_bulk_mode mode = VCHIQ_BULK_MODE_BLOCKING; int status = -EINVAL; if (!service) @@ -3119,15 +3492,13 @@ vchiq_bulk_xfer_blocking_interruptible(struct vchiq_instance *instance, unsigned if (service->srvstate != VCHIQ_SRVSTATE_OPEN) goto error_exit; - if (!offset && !uoffset) + if (!bulk_params->offset && !bulk_params->uoffset) goto error_exit; if (vchiq_check_service(service)) goto error_exit; - - status = vchiq_bulk_xfer_queue_msg_interruptible(service, offset, uoffset, size, - userdata, mode, dir); + status = vchiq_bulk_xfer_queue_msg_killable(service, bulk_params); error_exit: vchiq_service_put(service); @@ -3136,10 +3507,8 @@ vchiq_bulk_xfer_blocking_interruptible(struct vchiq_instance *instance, unsigned } int -vchiq_bulk_xfer_callback_interruptible(struct vchiq_instance *instance, unsigned int handle, - void *offset, void __user *uoffset, int size, - enum vchiq_bulk_mode mode, void *userdata, - enum vchiq_bulk_dir dir) +vchiq_bulk_xfer_callback(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_bulk *bulk_params) { struct vchiq_service *service = find_service_by_handle(instance, handle); int status = -EINVAL; @@ -3147,21 +3516,20 @@ vchiq_bulk_xfer_callback_interruptible(struct vchiq_instance *instance, unsigned if (!service) return -EINVAL; - if (mode != VCHIQ_BULK_MODE_CALLBACK && - mode != VCHIQ_BULK_MODE_NOCALLBACK) + if (bulk_params->mode != VCHIQ_BULK_MODE_CALLBACK && + bulk_params->mode != VCHIQ_BULK_MODE_NOCALLBACK) goto error_exit; if (service->srvstate != VCHIQ_SRVSTATE_OPEN) goto error_exit; - if (!offset && !uoffset) + if (!bulk_params->offset && !bulk_params->uoffset) goto error_exit; if (vchiq_check_service(service)) goto error_exit; - status = vchiq_bulk_xfer_queue_msg_interruptible(service, offset, uoffset, - size, userdata, mode, dir); + status = vchiq_bulk_xfer_queue_msg_killable(service, bulk_params); error_exit: vchiq_service_put(service); @@ -3175,8 +3543,8 @@ vchiq_bulk_xfer_callback_interruptible(struct vchiq_instance *instance, unsigned * and the call should be retried after being returned to user context. */ int -vchiq_bulk_xfer_waiting_interruptible(struct vchiq_instance *instance, - unsigned int handle, struct bulk_waiter *userdata) +vchiq_bulk_xfer_waiting(struct vchiq_instance *instance, + unsigned int handle, struct bulk_waiter *waiter) { struct vchiq_service *service = find_service_by_handle(instance, handle); struct bulk_waiter *bulk_waiter; @@ -3185,7 +3553,7 @@ vchiq_bulk_xfer_waiting_interruptible(struct vchiq_instance *instance, if (!service) return -EINVAL; - if (!userdata) + if (!waiter) goto error_exit; if (service->srvstate != VCHIQ_SRVSTATE_OPEN) @@ -3194,14 +3562,14 @@ vchiq_bulk_xfer_waiting_interruptible(struct vchiq_instance *instance, if (vchiq_check_service(service)) goto error_exit; - bulk_waiter = userdata; + bulk_waiter = waiter; vchiq_service_put(service); status = 0; - if (wait_for_completion_interruptible(&bulk_waiter->event)) - return -EAGAIN; + if (wait_for_completion_killable(&bulk_waiter->event)) + return -EINTR; else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) return -EINVAL; @@ -3267,24 +3635,8 @@ vchiq_queue_message(struct vchiq_instance *instance, unsigned int handle, int vchiq_queue_kernel_message(struct vchiq_instance *instance, unsigned int handle, void *data, unsigned int size) { - int status; - - while (1) { - status = vchiq_queue_message(instance, handle, memcpy_copy_callback, - data, size); - - /* - * vchiq_queue_message() may return -EAGAIN, so we need to - * implement a retry mechanism since this function is supposed - * to block until queued - */ - if (status != -EAGAIN) - break; - - msleep(1); - } - - return status; + return vchiq_queue_message(instance, handle, memcpy_copy_callback, + data, size); } EXPORT_SYMBOL(vchiq_queue_kernel_message); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 468463f318018..9b4e766990a49 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -6,11 +6,12 @@ #include #include -#include +#include #include #include #include #include +#include #include #include @@ -113,12 +114,16 @@ enum vchiq_bulk_dir { struct vchiq_bulk { short mode; short dir; - void *userdata; - dma_addr_t data; + void *cb_data; + void __user *cb_userdata; + struct bulk_waiter *waiter; + dma_addr_t dma_addr; int size; void *remote_data; int remote_size; int actual; + void *offset; + void __user *uoffset; }; struct vchiq_bulk_queue { @@ -409,6 +414,28 @@ struct vchiq_state { struct opaque_platform_state *platform_state; }; +struct pagelist { + u32 length; + u16 type; + u16 offset; + u32 addrs[1]; /* N.B. 12 LSBs hold the number + * of following pages at consecutive + * addresses. + */ +}; + +struct vchiq_pagelist_info { + struct pagelist *pagelist; + size_t pagelist_buffer_size; + dma_addr_t dma_addr; + enum dma_data_direction dma_dir; + unsigned int num_pages; + unsigned int pages_need_release; + struct page **pages; + struct scatterlist *scatterlist; + unsigned int scatterlist_mapped; +}; + static inline bool vchiq_remote_initialised(const struct vchiq_state *state) { return state->remote && state->remote->initialised; @@ -471,19 +498,16 @@ extern void remote_event_pollall(struct vchiq_state *state); extern int -vchiq_bulk_xfer_waiting_interruptible(struct vchiq_instance *instance, - unsigned int handle, struct bulk_waiter *userdata); +vchiq_bulk_xfer_waiting(struct vchiq_instance *instance, unsigned int handle, + struct bulk_waiter *userdata); extern int -vchiq_bulk_xfer_blocking_interruptible(struct vchiq_instance *instance, unsigned int handle, - void *offset, void __user *uoffset, int size, - void __user *userdata, enum vchiq_bulk_dir dir); +vchiq_bulk_xfer_blocking(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_bulk *bulk); extern int -vchiq_bulk_xfer_callback_interruptible(struct vchiq_instance *instance, unsigned int handle, - void *offset, void __user *uoffset, int size, - enum vchiq_bulk_mode mode, void *userdata, - enum vchiq_bulk_dir dir); +vchiq_bulk_xfer_callback(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_bulk *bulk); extern void vchiq_dump_state(struct seq_file *f, struct vchiq_state *state); @@ -529,13 +553,6 @@ vchiq_queue_message(struct vchiq_instance *instance, unsigned int handle, void *context, size_t size); -int vchiq_prepare_bulk_data(struct vchiq_instance *instance, struct vchiq_bulk *bulk, void *offset, - void __user *uoffset, int size, int dir); - -void vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk); - -void remote_event_signal(struct vchiq_state *state, struct remote_event *event); - void vchiq_dump_platform_state(struct seq_file *f); void vchiq_dump_platform_instances(struct vchiq_state *state, struct seq_file *f); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h index fabffd81b1ec9..b29e6693c9499 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h @@ -4,7 +4,8 @@ #ifndef VCHIQ_DEBUGFS_H #define VCHIQ_DEBUGFS_H -#include "vchiq_core.h" +struct vchiq_state; +struct vchiq_instance; struct vchiq_debugfs_node { struct dentry *dentry; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c index d41a4624cc92c..454f434165030 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c @@ -288,7 +288,7 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, { struct vchiq_service *service; struct bulk_waiter_node *waiter = NULL, *iter; - void *userdata; + struct vchiq_bulk bulk_params = {}; int status = 0; int ret; @@ -303,12 +303,14 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, goto out; } - userdata = &waiter->bulk_waiter; - - status = vchiq_bulk_xfer_blocking_interruptible(instance, args->handle, - NULL, args->data, args->size, - userdata, dir); + bulk_params.uoffset = args->data; + bulk_params.mode = args->mode; + bulk_params.size = args->size; + bulk_params.dir = dir; + bulk_params.waiter = &waiter->bulk_waiter; + status = vchiq_bulk_xfer_blocking(instance, args->handle, + &bulk_params); } else if (args->mode == VCHIQ_BULK_MODE_WAITING) { mutex_lock(&instance->bulk_waiter_list_mutex); list_for_each_entry(iter, &instance->bulk_waiter_list, @@ -328,16 +330,18 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, } dev_dbg(service->state->dev, "arm: found bulk_waiter %pK for pid %d\n", waiter, current->pid); - userdata = &waiter->bulk_waiter; - status = vchiq_bulk_xfer_waiting_interruptible(instance, args->handle, userdata); + status = vchiq_bulk_xfer_waiting(instance, args->handle, + &waiter->bulk_waiter); } else { - userdata = args->userdata; - - status = vchiq_bulk_xfer_callback_interruptible(instance, args->handle, NULL, - args->data, args->size, - args->mode, userdata, dir); - + bulk_params.uoffset = args->data; + bulk_params.mode = args->mode; + bulk_params.size = args->size; + bulk_params.dir = dir; + bulk_params.cb_userdata = args->userdata; + + status = vchiq_bulk_xfer_callback(instance, args->handle, + &bulk_params); } if (!waiter) { @@ -350,7 +354,7 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, if (waiter->bulk_waiter.bulk) { /* Cancel the signal when the transfer completes. */ spin_lock(&service->state->bulk_waiter_spinlock); - waiter->bulk_waiter.bulk->userdata = NULL; + waiter->bulk_waiter.bulk->waiter = NULL; spin_unlock(&service->state->bulk_waiter_spinlock); } kfree(waiter); @@ -410,7 +414,7 @@ struct vchiq_completion_data32 { enum vchiq_reason reason; compat_uptr_t header; compat_uptr_t service_userdata; - compat_uptr_t bulk_userdata; + compat_uptr_t cb_data; }; static int vchiq_put_completion(struct vchiq_completion_data __user *buf, @@ -424,7 +428,7 @@ static int vchiq_put_completion(struct vchiq_completion_data __user *buf, .reason = completion->reason, .header = ptr_to_compat(completion->header), .service_userdata = ptr_to_compat(completion->service_userdata), - .bulk_userdata = ptr_to_compat(completion->bulk_userdata), + .cb_data = ptr_to_compat(completion->cb_userdata), }; if (copy_to_user(&buf32[index], &tmp, sizeof(tmp))) return -EFAULT; @@ -545,11 +549,7 @@ static int vchiq_ioc_await_completion(struct vchiq_instance *instance, !instance->use_close_delivered) vchiq_service_put(service); - /* - * FIXME: address space mismatch, does bulk_userdata - * actually point to user or kernel memory? - */ - user_completion.bulk_userdata = completion->bulk_userdata; + user_completion.cb_userdata = completion->cb_userdata; if (vchiq_put_completion(args->buf, &user_completion, ret)) { if (ret == 0) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h index 17550831f86cb..afb71a83cfe70 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h @@ -47,7 +47,7 @@ struct vchiq_completion_data { enum vchiq_reason reason; struct vchiq_header __user *header; void __user *service_userdata; - void __user *bulk_userdata; + void __user *cb_userdata; }; struct vchiq_await_completion { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h deleted file mode 100644 index ebd12bfabb637..0000000000000 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright (c) 2010-2012 Broadcom. All rights reserved. */ - -#ifndef VCHIQ_PAGELIST_H -#define VCHIQ_PAGELIST_H - -#define PAGELIST_WRITE 0 -#define PAGELIST_READ 1 -#define PAGELIST_READ_WITH_FRAGMENTS 2 - -struct pagelist { - u32 length; - u16 type; - u16 offset; - u32 addrs[1]; /* N.B. 12 LSBs hold the number - * of following pages at consecutive - * addresses. - */ -}; - -#endif /* VCHIQ_PAGELIST_H */ diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 67489c334f7b2..3fe482bd27939 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -551,7 +551,8 @@ static void bulk_abort_cb(struct vchiq_mmal_instance *instance, /* incoming event service callback */ static int mmal_service_callback(struct vchiq_instance *vchiq_instance, enum vchiq_reason reason, struct vchiq_header *header, - unsigned int handle, void *bulk_ctx) + unsigned int handle, void *cb_data, + void __user *cb_userdata) { struct vchiq_mmal_instance *instance = vchiq_get_service_userdata(vchiq_instance, handle); u32 msg_len; @@ -626,11 +627,11 @@ static int mmal_service_callback(struct vchiq_instance *vchiq_instance, break; case VCHIQ_BULK_RECEIVE_DONE: - bulk_receive_cb(instance, bulk_ctx); + bulk_receive_cb(instance, cb_data); break; case VCHIQ_BULK_RECEIVE_ABORTED: - bulk_abort_cb(instance, bulk_ctx); + bulk_abort_cb(instance, cb_data); break; case VCHIQ_SERVICE_CLOSED: diff --git a/drivers/staging/vme_user/vme_bridge.h b/drivers/staging/vme_user/vme_bridge.h index 9bdc41bb66021..abf880d68b124 100644 --- a/drivers/staging/vme_user/vme_bridge.h +++ b/drivers/staging/vme_user/vme_bridge.h @@ -128,39 +128,49 @@ struct vme_bridge { struct mutex irq_mtx; /* Slave Functions */ - int (*slave_get)(struct vme_slave_resource *, int *, unsigned long long *, - unsigned long long *, dma_addr_t *, u32 *, u32 *); - int (*slave_set)(struct vme_slave_resource *, int, unsigned long long, - unsigned long long, dma_addr_t, u32, u32); + int (*slave_get)(struct vme_slave_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + dma_addr_t *buf_base, u32 *aspace, u32 *cycle); + int (*slave_set)(struct vme_slave_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, + dma_addr_t buf_base, u32 aspace, u32 cycle); /* Master Functions */ - int (*master_get)(struct vme_master_resource *, int *, unsigned long long *, - unsigned long long *, u32 *, u32 *, u32 *); - int (*master_set)(struct vme_master_resource *, int, unsigned long long, - unsigned long long, u32, u32, u32); - ssize_t (*master_read)(struct vme_master_resource *, void *, size_t, loff_t); - ssize_t (*master_write)(struct vme_master_resource *, void *, size_t, loff_t); - unsigned int (*master_rmw)(struct vme_master_resource *, unsigned int, - unsigned int, unsigned int, loff_t); + int (*master_get)(struct vme_master_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + u32 *aspace, u32 *cycle, u32 *dwidth); + int (*master_set)(struct vme_master_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, + u32 aspace, u32 cycle, u32 dwidth); + ssize_t (*master_read)(struct vme_master_resource *image, void *buf, + size_t count, loff_t offset); + ssize_t (*master_write)(struct vme_master_resource *image, void *buf, + size_t count, loff_t offset); + unsigned int (*master_rmw)(struct vme_master_resource *image, + unsigned int mask, unsigned int compare, + unsigned int swap, loff_t offset); /* DMA Functions */ - int (*dma_list_add)(struct vme_dma_list *, struct vme_dma_attr *, - struct vme_dma_attr *, size_t); - int (*dma_list_exec)(struct vme_dma_list *); - int (*dma_list_empty)(struct vme_dma_list *); + int (*dma_list_add)(struct vme_dma_list *list, struct vme_dma_attr *src, + struct vme_dma_attr *dest, size_t count); + int (*dma_list_exec)(struct vme_dma_list *list); + int (*dma_list_empty)(struct vme_dma_list *list); /* Interrupt Functions */ - void (*irq_set)(struct vme_bridge *, int, int, int); - int (*irq_generate)(struct vme_bridge *, int, int); + void (*irq_set)(struct vme_bridge *bridge, int level, int state, int sync); + int (*irq_generate)(struct vme_bridge *bridge, int level, int statid); /* Location monitor functions */ - int (*lm_set)(struct vme_lm_resource *, unsigned long long, u32, u32); - int (*lm_get)(struct vme_lm_resource *, unsigned long long *, u32 *, u32 *); - int (*lm_attach)(struct vme_lm_resource *, int, void (*callback)(void *), void *); - int (*lm_detach)(struct vme_lm_resource *, int); + int (*lm_set)(struct vme_lm_resource *lm, unsigned long long lm_base, + u32 aspace, u32 cycle); + int (*lm_get)(struct vme_lm_resource *lm, unsigned long long *lm_base, + u32 *aspace, u32 *cycle); + int (*lm_attach)(struct vme_lm_resource *lm, int monitor, + void (*callback)(void *), void *data); + int (*lm_detach)(struct vme_lm_resource *lm, int monitor); /* CR/CSR space functions */ - int (*slot_get)(struct vme_bridge *); + int (*slot_get)(struct vme_bridge *bridge); /* Bridge parent interface */ void *(*alloc_consistent)(struct device *dev, size_t size, dma_addr_t *dma); diff --git a/drivers/staging/vt6655/Kconfig b/drivers/staging/vt6655/Kconfig deleted file mode 100644 index 077f62ebe80cd..0000000000000 --- a/drivers/staging/vt6655/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VT6655 - tristate "VIA Technologies VT6655 support" - depends on PCI && HAS_IOPORT && MAC80211 && m - help - This is a vendor-written driver for VIA VT6655. diff --git a/drivers/staging/vt6655/Makefile b/drivers/staging/vt6655/Makefile deleted file mode 100644 index e70357ec0af8a..0000000000000 --- a/drivers/staging/vt6655/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -vt6655_stage-y += device_main.o \ - card.o \ - channel.o \ - mac.o \ - baseband.o \ - rxtx.o \ - dpc.o \ - power.o \ - srom.o \ - key.o \ - rf.o - -obj-$(CONFIG_VT6655) += vt6655_stage.o diff --git a/drivers/staging/vt6655/TODO b/drivers/staging/vt6655/TODO deleted file mode 100644 index 27654bd0ff5d7..0000000000000 --- a/drivers/staging/vt6655/TODO +++ /dev/null @@ -1,20 +0,0 @@ -TODO: -- remove __cplusplus ifdefs -- done -- prepare for merge with vt6656 driver: - - rename DEVICE_PRT() to DBG_PRT() -- done - - share 80211*.h includes - - split rf.c - - remove dead code - - abstract VT3253 chipset specific code -- add common vt665x infrastructure -- kill ttype.h -- switch to use MAC80211 -- verify unsigned long usage for x86-64 arch -- reduce .data footprint -- use kernel coding style -- checkpatch.pl fixes -- sparse fixes -- integrate with drivers/net/wireless - -Please send any patches to Greg Kroah-Hartman -and Philipp Hortmann . diff --git a/drivers/staging/vt6655/baseband.c b/drivers/staging/vt6655/baseband.c deleted file mode 100644 index f7824396c5ff7..0000000000000 --- a/drivers/staging/vt6655/baseband.c +++ /dev/null @@ -1,2257 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions to access baseband - * - * Author: Kyle Hsu - * - * Date: Aug.22, 2002 - * - * Functions: - * bb_get_frame_time - Calculate data frame transmitting time - * bb_read_embedded - Embedded read baseband register via MAC - * bb_write_embedded - Embedded write baseband register via MAC - * bb_vt3253_init - VIA VT3253 baseband chip init code - * - * Revision History: - * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. - * 08-07-2003 Bryan YC Fan: Add MAXIM2827/2825 and RFMD2959 support. - * 08-26-2003 Kyle Hsu : Modify BBuGetFrameTime() and - * BBvCalculateParameter(). - * cancel the setting of MAC_REG_SOFTPWRCTL on - * BBbVT3253Init(). - * Add the comments. - * 09-01-2003 Bryan YC Fan: RF & BB tables updated. - * Modified BBvLoopbackOn & BBvLoopbackOff(). - * - * - */ - -#include "mac.h" -#include "baseband.h" -#include "srom.h" -#include "rf.h" - -/*--------------------- Static Classes ----------------------------*/ - -/*--------------------- Static Variables --------------------------*/ - -/*--------------------- Static Functions --------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Static Definitions -------------------------*/ - -/*--------------------- Static Classes ----------------------------*/ - -/*--------------------- Static Variables --------------------------*/ - -#define CB_VT3253_INIT_FOR_RFMD 446 -static const unsigned char by_vt3253_init_tab_rfmd[CB_VT3253_INIT_FOR_RFMD][2] = { - {0x00, 0x30}, - {0x01, 0x00}, - {0x02, 0x00}, - {0x03, 0x00}, - {0x04, 0x00}, - {0x05, 0x00}, - {0x06, 0x00}, - {0x07, 0x00}, - {0x08, 0x70}, - {0x09, 0x45}, - {0x0a, 0x2a}, - {0x0b, 0x76}, - {0x0c, 0x00}, - {0x0d, 0x01}, - {0x0e, 0x80}, - {0x0f, 0x00}, - {0x10, 0x00}, - {0x11, 0x00}, - {0x12, 0x00}, - {0x13, 0x00}, - {0x14, 0x00}, - {0x15, 0x00}, - {0x16, 0x00}, - {0x17, 0x00}, - {0x18, 0x00}, - {0x19, 0x00}, - {0x1a, 0x00}, - {0x1b, 0x9d}, - {0x1c, 0x05}, - {0x1d, 0x00}, - {0x1e, 0x00}, - {0x1f, 0x00}, - {0x20, 0x00}, - {0x21, 0x00}, - {0x22, 0x00}, - {0x23, 0x00}, - {0x24, 0x00}, - {0x25, 0x4a}, - {0x26, 0x00}, - {0x27, 0x00}, - {0x28, 0x00}, - {0x29, 0x00}, - {0x2a, 0x00}, - {0x2b, 0x00}, - {0x2c, 0x00}, - {0x2d, 0xa8}, - {0x2e, 0x1a}, - {0x2f, 0x0c}, - {0x30, 0x26}, - {0x31, 0x5b}, - {0x32, 0x00}, - {0x33, 0x00}, - {0x34, 0x00}, - {0x35, 0x00}, - {0x36, 0xaa}, - {0x37, 0xaa}, - {0x38, 0xff}, - {0x39, 0xff}, - {0x3a, 0x00}, - {0x3b, 0x00}, - {0x3c, 0x00}, - {0x3d, 0x0d}, - {0x3e, 0x51}, - {0x3f, 0x04}, - {0x40, 0x00}, - {0x41, 0x08}, - {0x42, 0x00}, - {0x43, 0x08}, - {0x44, 0x06}, - {0x45, 0x14}, - {0x46, 0x05}, - {0x47, 0x08}, - {0x48, 0x00}, - {0x49, 0x00}, - {0x4a, 0x00}, - {0x4b, 0x00}, - {0x4c, 0x09}, - {0x4d, 0x80}, - {0x4e, 0x00}, - {0x4f, 0xc5}, - {0x50, 0x14}, - {0x51, 0x19}, - {0x52, 0x00}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x56, 0x00}, - {0x57, 0x00}, - {0x58, 0x00}, - {0x59, 0xb0}, - {0x5a, 0x00}, - {0x5b, 0x00}, - {0x5c, 0x00}, - {0x5d, 0x00}, - {0x5e, 0x00}, - {0x5f, 0x00}, - {0x60, 0x44}, - {0x61, 0x04}, - {0x62, 0x00}, - {0x63, 0x00}, - {0x64, 0x00}, - {0x65, 0x00}, - {0x66, 0x04}, - {0x67, 0xb7}, - {0x68, 0x00}, - {0x69, 0x00}, - {0x6a, 0x00}, - {0x6b, 0x00}, - {0x6c, 0x00}, - {0x6d, 0x03}, - {0x6e, 0x01}, - {0x6f, 0x00}, - {0x70, 0x00}, - {0x71, 0x00}, - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x00}, - {0x75, 0x00}, - {0x76, 0x00}, - {0x77, 0x00}, - {0x78, 0x00}, - {0x79, 0x00}, - {0x7a, 0x00}, - {0x7b, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x00}, - {0x7e, 0x00}, - {0x7f, 0x00}, - {0x80, 0x0b}, - {0x81, 0x00}, - {0x82, 0x3c}, - {0x83, 0x00}, - {0x84, 0x00}, - {0x85, 0x00}, - {0x86, 0x00}, - {0x87, 0x00}, - {0x88, 0x08}, - {0x89, 0x00}, - {0x8a, 0x08}, - {0x8b, 0xa6}, - {0x8c, 0x84}, - {0x8d, 0x47}, - {0x8e, 0xbb}, - {0x8f, 0x02}, - {0x90, 0x21}, - {0x91, 0x0c}, - {0x92, 0x04}, - {0x93, 0x22}, - {0x94, 0x00}, - {0x95, 0x00}, - {0x96, 0x00}, - {0x97, 0xeb}, - {0x98, 0x00}, - {0x99, 0x00}, - {0x9a, 0x00}, - {0x9b, 0x00}, - {0x9c, 0x00}, - {0x9d, 0x00}, - {0x9e, 0x00}, - {0x9f, 0x00}, - {0xa0, 0x00}, - {0xa1, 0x00}, - {0xa2, 0x00}, - {0xa3, 0x00}, - {0xa4, 0x00}, - {0xa5, 0x00}, - {0xa6, 0x10}, - {0xa7, 0x04}, - {0xa8, 0x10}, - {0xa9, 0x00}, - {0xaa, 0x8f}, - {0xab, 0x00}, - {0xac, 0x00}, - {0xad, 0x00}, - {0xae, 0x00}, - {0xaf, 0x80}, - {0xb0, 0x38}, - {0xb1, 0x00}, - {0xb2, 0x00}, - {0xb3, 0x00}, - {0xb4, 0xee}, - {0xb5, 0xff}, - {0xb6, 0x10}, - {0xb7, 0x00}, - {0xb8, 0x00}, - {0xb9, 0x00}, - {0xba, 0x00}, - {0xbb, 0x03}, - {0xbc, 0x00}, - {0xbd, 0x00}, - {0xbe, 0x00}, - {0xbf, 0x00}, - {0xc0, 0x10}, - {0xc1, 0x10}, - {0xc2, 0x18}, - {0xc3, 0x20}, - {0xc4, 0x10}, - {0xc5, 0x00}, - {0xc6, 0x22}, - {0xc7, 0x14}, - {0xc8, 0x0f}, - {0xc9, 0x08}, - {0xca, 0xa4}, - {0xcb, 0xa7}, - {0xcc, 0x3c}, - {0xcd, 0x10}, - {0xce, 0x20}, - {0xcf, 0x00}, - {0xd0, 0x00}, - {0xd1, 0x10}, - {0xd2, 0x00}, - {0xd3, 0x00}, - {0xd4, 0x10}, - {0xd5, 0x33}, - {0xd6, 0x70}, - {0xd7, 0x01}, - {0xd8, 0x00}, - {0xd9, 0x00}, - {0xda, 0x00}, - {0xdb, 0x00}, - {0xdc, 0x00}, - {0xdd, 0x00}, - {0xde, 0x00}, - {0xdf, 0x00}, - {0xe0, 0x00}, - {0xe1, 0x00}, - {0xe2, 0xcc}, - {0xe3, 0x04}, - {0xe4, 0x08}, - {0xe5, 0x10}, - {0xe6, 0x00}, - {0xe7, 0x0e}, - {0xe8, 0x88}, - {0xe9, 0xd4}, - {0xea, 0x05}, - {0xeb, 0xf0}, - {0xec, 0x79}, - {0xed, 0x0f}, - {0xee, 0x04}, - {0xef, 0x04}, - {0xf0, 0x00}, - {0xf1, 0x00}, - {0xf2, 0x00}, - {0xf3, 0x00}, - {0xf4, 0x00}, - {0xf5, 0x00}, - {0xf6, 0x00}, - {0xf7, 0x00}, - {0xf8, 0x00}, - {0xf9, 0x00}, - {0xF0, 0x00}, - {0xF1, 0xF8}, - {0xF0, 0x80}, - {0xF0, 0x00}, - {0xF1, 0xF4}, - {0xF0, 0x81}, - {0xF0, 0x01}, - {0xF1, 0xF0}, - {0xF0, 0x82}, - {0xF0, 0x02}, - {0xF1, 0xEC}, - {0xF0, 0x83}, - {0xF0, 0x03}, - {0xF1, 0xE8}, - {0xF0, 0x84}, - {0xF0, 0x04}, - {0xF1, 0xE4}, - {0xF0, 0x85}, - {0xF0, 0x05}, - {0xF1, 0xE0}, - {0xF0, 0x86}, - {0xF0, 0x06}, - {0xF1, 0xDC}, - {0xF0, 0x87}, - {0xF0, 0x07}, - {0xF1, 0xD8}, - {0xF0, 0x88}, - {0xF0, 0x08}, - {0xF1, 0xD4}, - {0xF0, 0x89}, - {0xF0, 0x09}, - {0xF1, 0xD0}, - {0xF0, 0x8A}, - {0xF0, 0x0A}, - {0xF1, 0xCC}, - {0xF0, 0x8B}, - {0xF0, 0x0B}, - {0xF1, 0xC8}, - {0xF0, 0x8C}, - {0xF0, 0x0C}, - {0xF1, 0xC4}, - {0xF0, 0x8D}, - {0xF0, 0x0D}, - {0xF1, 0xC0}, - {0xF0, 0x8E}, - {0xF0, 0x0E}, - {0xF1, 0xBC}, - {0xF0, 0x8F}, - {0xF0, 0x0F}, - {0xF1, 0xB8}, - {0xF0, 0x90}, - {0xF0, 0x10}, - {0xF1, 0xB4}, - {0xF0, 0x91}, - {0xF0, 0x11}, - {0xF1, 0xB0}, - {0xF0, 0x92}, - {0xF0, 0x12}, - {0xF1, 0xAC}, - {0xF0, 0x93}, - {0xF0, 0x13}, - {0xF1, 0xA8}, - {0xF0, 0x94}, - {0xF0, 0x14}, - {0xF1, 0xA4}, - {0xF0, 0x95}, - {0xF0, 0x15}, - {0xF1, 0xA0}, - {0xF0, 0x96}, - {0xF0, 0x16}, - {0xF1, 0x9C}, - {0xF0, 0x97}, - {0xF0, 0x17}, - {0xF1, 0x98}, - {0xF0, 0x98}, - {0xF0, 0x18}, - {0xF1, 0x94}, - {0xF0, 0x99}, - {0xF0, 0x19}, - {0xF1, 0x90}, - {0xF0, 0x9A}, - {0xF0, 0x1A}, - {0xF1, 0x8C}, - {0xF0, 0x9B}, - {0xF0, 0x1B}, - {0xF1, 0x88}, - {0xF0, 0x9C}, - {0xF0, 0x1C}, - {0xF1, 0x84}, - {0xF0, 0x9D}, - {0xF0, 0x1D}, - {0xF1, 0x80}, - {0xF0, 0x9E}, - {0xF0, 0x1E}, - {0xF1, 0x7C}, - {0xF0, 0x9F}, - {0xF0, 0x1F}, - {0xF1, 0x78}, - {0xF0, 0xA0}, - {0xF0, 0x20}, - {0xF1, 0x74}, - {0xF0, 0xA1}, - {0xF0, 0x21}, - {0xF1, 0x70}, - {0xF0, 0xA2}, - {0xF0, 0x22}, - {0xF1, 0x6C}, - {0xF0, 0xA3}, - {0xF0, 0x23}, - {0xF1, 0x68}, - {0xF0, 0xA4}, - {0xF0, 0x24}, - {0xF1, 0x64}, - {0xF0, 0xA5}, - {0xF0, 0x25}, - {0xF1, 0x60}, - {0xF0, 0xA6}, - {0xF0, 0x26}, - {0xF1, 0x5C}, - {0xF0, 0xA7}, - {0xF0, 0x27}, - {0xF1, 0x58}, - {0xF0, 0xA8}, - {0xF0, 0x28}, - {0xF1, 0x54}, - {0xF0, 0xA9}, - {0xF0, 0x29}, - {0xF1, 0x50}, - {0xF0, 0xAA}, - {0xF0, 0x2A}, - {0xF1, 0x4C}, - {0xF0, 0xAB}, - {0xF0, 0x2B}, - {0xF1, 0x48}, - {0xF0, 0xAC}, - {0xF0, 0x2C}, - {0xF1, 0x44}, - {0xF0, 0xAD}, - {0xF0, 0x2D}, - {0xF1, 0x40}, - {0xF0, 0xAE}, - {0xF0, 0x2E}, - {0xF1, 0x3C}, - {0xF0, 0xAF}, - {0xF0, 0x2F}, - {0xF1, 0x38}, - {0xF0, 0xB0}, - {0xF0, 0x30}, - {0xF1, 0x34}, - {0xF0, 0xB1}, - {0xF0, 0x31}, - {0xF1, 0x30}, - {0xF0, 0xB2}, - {0xF0, 0x32}, - {0xF1, 0x2C}, - {0xF0, 0xB3}, - {0xF0, 0x33}, - {0xF1, 0x28}, - {0xF0, 0xB4}, - {0xF0, 0x34}, - {0xF1, 0x24}, - {0xF0, 0xB5}, - {0xF0, 0x35}, - {0xF1, 0x20}, - {0xF0, 0xB6}, - {0xF0, 0x36}, - {0xF1, 0x1C}, - {0xF0, 0xB7}, - {0xF0, 0x37}, - {0xF1, 0x18}, - {0xF0, 0xB8}, - {0xF0, 0x38}, - {0xF1, 0x14}, - {0xF0, 0xB9}, - {0xF0, 0x39}, - {0xF1, 0x10}, - {0xF0, 0xBA}, - {0xF0, 0x3A}, - {0xF1, 0x0C}, - {0xF0, 0xBB}, - {0xF0, 0x3B}, - {0xF1, 0x08}, - {0xF0, 0x00}, - {0xF0, 0x3C}, - {0xF1, 0x04}, - {0xF0, 0xBD}, - {0xF0, 0x3D}, - {0xF1, 0x00}, - {0xF0, 0xBE}, - {0xF0, 0x3E}, - {0xF1, 0x00}, - {0xF0, 0xBF}, - {0xF0, 0x3F}, - {0xF1, 0x00}, - {0xF0, 0xC0}, - {0xF0, 0x00}, -}; - -#define CB_VT3253B0_INIT_FOR_RFMD 256 -static const unsigned char vt3253b0_rfmd[CB_VT3253B0_INIT_FOR_RFMD][2] = { - {0x00, 0x31}, - {0x01, 0x00}, - {0x02, 0x00}, - {0x03, 0x00}, - {0x04, 0x00}, - {0x05, 0x81}, - {0x06, 0x00}, - {0x07, 0x00}, - {0x08, 0x38}, - {0x09, 0x45}, - {0x0a, 0x2a}, - {0x0b, 0x76}, - {0x0c, 0x00}, - {0x0d, 0x00}, - {0x0e, 0x80}, - {0x0f, 0x00}, - {0x10, 0x00}, - {0x11, 0x00}, - {0x12, 0x00}, - {0x13, 0x00}, - {0x14, 0x00}, - {0x15, 0x00}, - {0x16, 0x00}, - {0x17, 0x00}, - {0x18, 0x00}, - {0x19, 0x00}, - {0x1a, 0x00}, - {0x1b, 0x8e}, - {0x1c, 0x06}, - {0x1d, 0x00}, - {0x1e, 0x00}, - {0x1f, 0x00}, - {0x20, 0x00}, - {0x21, 0x00}, - {0x22, 0x00}, - {0x23, 0x00}, - {0x24, 0x00}, - {0x25, 0x4a}, - {0x26, 0x00}, - {0x27, 0x00}, - {0x28, 0x00}, - {0x29, 0x00}, - {0x2a, 0x00}, - {0x2b, 0x00}, - {0x2c, 0x00}, - {0x2d, 0x34}, - {0x2e, 0x18}, - {0x2f, 0x0c}, - {0x30, 0x26}, - {0x31, 0x5b}, - {0x32, 0x00}, - {0x33, 0x00}, - {0x34, 0x00}, - {0x35, 0x00}, - {0x36, 0xaa}, - {0x37, 0xaa}, - {0x38, 0xff}, - {0x39, 0xff}, - {0x3a, 0xf8}, - {0x3b, 0x00}, - {0x3c, 0x00}, - {0x3d, 0x09}, - {0x3e, 0x0d}, - {0x3f, 0x04}, - {0x40, 0x00}, - {0x41, 0x08}, - {0x42, 0x00}, - {0x43, 0x08}, - {0x44, 0x08}, - {0x45, 0x14}, - {0x46, 0x05}, - {0x47, 0x08}, - {0x48, 0x00}, - {0x49, 0x00}, - {0x4a, 0x00}, - {0x4b, 0x00}, - {0x4c, 0x09}, - {0x4d, 0x80}, - {0x4e, 0x00}, - {0x4f, 0xc5}, - {0x50, 0x14}, - {0x51, 0x19}, - {0x52, 0x00}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x56, 0x00}, - {0x57, 0x00}, - {0x58, 0x00}, - {0x59, 0xb0}, - {0x5a, 0x00}, - {0x5b, 0x00}, - {0x5c, 0x00}, - {0x5d, 0x00}, - {0x5e, 0x00}, - {0x5f, 0x00}, - {0x60, 0x39}, - {0x61, 0x83}, - {0x62, 0x00}, - {0x63, 0x00}, - {0x64, 0x00}, - {0x65, 0x00}, - {0x66, 0xc0}, - {0x67, 0x49}, - {0x68, 0x00}, - {0x69, 0x00}, - {0x6a, 0x00}, - {0x6b, 0x00}, - {0x6c, 0x00}, - {0x6d, 0x03}, - {0x6e, 0x01}, - {0x6f, 0x00}, - {0x70, 0x00}, - {0x71, 0x00}, - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x00}, - {0x75, 0x00}, - {0x76, 0x00}, - {0x77, 0x00}, - {0x78, 0x00}, - {0x79, 0x00}, - {0x7a, 0x00}, - {0x7b, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x00}, - {0x7e, 0x00}, - {0x7f, 0x00}, - {0x80, 0x89}, - {0x81, 0x00}, - {0x82, 0x0e}, - {0x83, 0x00}, - {0x84, 0x00}, - {0x85, 0x00}, - {0x86, 0x00}, - {0x87, 0x00}, - {0x88, 0x08}, - {0x89, 0x00}, - {0x8a, 0x0e}, - {0x8b, 0xa7}, - {0x8c, 0x88}, - {0x8d, 0x47}, - {0x8e, 0xaa}, - {0x8f, 0x02}, - {0x90, 0x23}, - {0x91, 0x0c}, - {0x92, 0x06}, - {0x93, 0x08}, - {0x94, 0x00}, - {0x95, 0x00}, - {0x96, 0x00}, - {0x97, 0xeb}, - {0x98, 0x00}, - {0x99, 0x00}, - {0x9a, 0x00}, - {0x9b, 0x00}, - {0x9c, 0x00}, - {0x9d, 0x00}, - {0x9e, 0x00}, - {0x9f, 0x00}, - {0xa0, 0x00}, - {0xa1, 0x00}, - {0xa2, 0x00}, - {0xa3, 0xcd}, - {0xa4, 0x07}, - {0xa5, 0x33}, - {0xa6, 0x18}, - {0xa7, 0x00}, - {0xa8, 0x18}, - {0xa9, 0x00}, - {0xaa, 0x28}, - {0xab, 0x00}, - {0xac, 0x00}, - {0xad, 0x00}, - {0xae, 0x00}, - {0xaf, 0x18}, - {0xb0, 0x38}, - {0xb1, 0x30}, - {0xb2, 0x00}, - {0xb3, 0x00}, - {0xb4, 0x00}, - {0xb5, 0x00}, - {0xb6, 0x84}, - {0xb7, 0xfd}, - {0xb8, 0x00}, - {0xb9, 0x00}, - {0xba, 0x00}, - {0xbb, 0x03}, - {0xbc, 0x00}, - {0xbd, 0x00}, - {0xbe, 0x00}, - {0xbf, 0x00}, - {0xc0, 0x10}, - {0xc1, 0x20}, - {0xc2, 0x18}, - {0xc3, 0x20}, - {0xc4, 0x10}, - {0xc5, 0x2c}, - {0xc6, 0x1e}, - {0xc7, 0x10}, - {0xc8, 0x12}, - {0xc9, 0x01}, - {0xca, 0x6f}, - {0xcb, 0xa7}, - {0xcc, 0x3c}, - {0xcd, 0x10}, - {0xce, 0x00}, - {0xcf, 0x22}, - {0xd0, 0x00}, - {0xd1, 0x10}, - {0xd2, 0x00}, - {0xd3, 0x00}, - {0xd4, 0x10}, - {0xd5, 0x33}, - {0xd6, 0x80}, - {0xd7, 0x21}, - {0xd8, 0x00}, - {0xd9, 0x00}, - {0xda, 0x00}, - {0xdb, 0x00}, - {0xdc, 0x00}, - {0xdd, 0x00}, - {0xde, 0x00}, - {0xdf, 0x00}, - {0xe0, 0x00}, - {0xe1, 0xB3}, - {0xe2, 0x00}, - {0xe3, 0x00}, - {0xe4, 0x00}, - {0xe5, 0x10}, - {0xe6, 0x00}, - {0xe7, 0x18}, - {0xe8, 0x08}, - {0xe9, 0xd4}, - {0xea, 0x00}, - {0xeb, 0xff}, - {0xec, 0x79}, - {0xed, 0x10}, - {0xee, 0x30}, - {0xef, 0x02}, - {0xf0, 0x00}, - {0xf1, 0x09}, - {0xf2, 0x00}, - {0xf3, 0x00}, - {0xf4, 0x00}, - {0xf5, 0x00}, - {0xf6, 0x00}, - {0xf7, 0x00}, - {0xf8, 0x00}, - {0xf9, 0x00}, - {0xfa, 0x00}, - {0xfb, 0x00}, - {0xfc, 0x00}, - {0xfd, 0x00}, - {0xfe, 0x00}, - {0xff, 0x00}, -}; - -#define CB_VT3253B0_AGC_FOR_RFMD2959 195 -/* For RFMD2959 */ -static -unsigned char vt3253b0_agc4_rfmd2959[CB_VT3253B0_AGC_FOR_RFMD2959][2] = { - {0xF0, 0x00}, - {0xF1, 0x3E}, - {0xF0, 0x80}, - {0xF0, 0x00}, - {0xF1, 0x3E}, - {0xF0, 0x81}, - {0xF0, 0x01}, - {0xF1, 0x3E}, - {0xF0, 0x82}, - {0xF0, 0x02}, - {0xF1, 0x3E}, - {0xF0, 0x83}, - {0xF0, 0x03}, - {0xF1, 0x3B}, - {0xF0, 0x84}, - {0xF0, 0x04}, - {0xF1, 0x39}, - {0xF0, 0x85}, - {0xF0, 0x05}, - {0xF1, 0x38}, - {0xF0, 0x86}, - {0xF0, 0x06}, - {0xF1, 0x37}, - {0xF0, 0x87}, - {0xF0, 0x07}, - {0xF1, 0x36}, - {0xF0, 0x88}, - {0xF0, 0x08}, - {0xF1, 0x35}, - {0xF0, 0x89}, - {0xF0, 0x09}, - {0xF1, 0x35}, - {0xF0, 0x8A}, - {0xF0, 0x0A}, - {0xF1, 0x34}, - {0xF0, 0x8B}, - {0xF0, 0x0B}, - {0xF1, 0x34}, - {0xF0, 0x8C}, - {0xF0, 0x0C}, - {0xF1, 0x33}, - {0xF0, 0x8D}, - {0xF0, 0x0D}, - {0xF1, 0x32}, - {0xF0, 0x8E}, - {0xF0, 0x0E}, - {0xF1, 0x31}, - {0xF0, 0x8F}, - {0xF0, 0x0F}, - {0xF1, 0x30}, - {0xF0, 0x90}, - {0xF0, 0x10}, - {0xF1, 0x2F}, - {0xF0, 0x91}, - {0xF0, 0x11}, - {0xF1, 0x2F}, - {0xF0, 0x92}, - {0xF0, 0x12}, - {0xF1, 0x2E}, - {0xF0, 0x93}, - {0xF0, 0x13}, - {0xF1, 0x2D}, - {0xF0, 0x94}, - {0xF0, 0x14}, - {0xF1, 0x2C}, - {0xF0, 0x95}, - {0xF0, 0x15}, - {0xF1, 0x2B}, - {0xF0, 0x96}, - {0xF0, 0x16}, - {0xF1, 0x2B}, - {0xF0, 0x97}, - {0xF0, 0x17}, - {0xF1, 0x2A}, - {0xF0, 0x98}, - {0xF0, 0x18}, - {0xF1, 0x29}, - {0xF0, 0x99}, - {0xF0, 0x19}, - {0xF1, 0x28}, - {0xF0, 0x9A}, - {0xF0, 0x1A}, - {0xF1, 0x27}, - {0xF0, 0x9B}, - {0xF0, 0x1B}, - {0xF1, 0x26}, - {0xF0, 0x9C}, - {0xF0, 0x1C}, - {0xF1, 0x25}, - {0xF0, 0x9D}, - {0xF0, 0x1D}, - {0xF1, 0x24}, - {0xF0, 0x9E}, - {0xF0, 0x1E}, - {0xF1, 0x24}, - {0xF0, 0x9F}, - {0xF0, 0x1F}, - {0xF1, 0x23}, - {0xF0, 0xA0}, - {0xF0, 0x20}, - {0xF1, 0x22}, - {0xF0, 0xA1}, - {0xF0, 0x21}, - {0xF1, 0x21}, - {0xF0, 0xA2}, - {0xF0, 0x22}, - {0xF1, 0x20}, - {0xF0, 0xA3}, - {0xF0, 0x23}, - {0xF1, 0x20}, - {0xF0, 0xA4}, - {0xF0, 0x24}, - {0xF1, 0x1F}, - {0xF0, 0xA5}, - {0xF0, 0x25}, - {0xF1, 0x1E}, - {0xF0, 0xA6}, - {0xF0, 0x26}, - {0xF1, 0x1D}, - {0xF0, 0xA7}, - {0xF0, 0x27}, - {0xF1, 0x1C}, - {0xF0, 0xA8}, - {0xF0, 0x28}, - {0xF1, 0x1B}, - {0xF0, 0xA9}, - {0xF0, 0x29}, - {0xF1, 0x1B}, - {0xF0, 0xAA}, - {0xF0, 0x2A}, - {0xF1, 0x1A}, - {0xF0, 0xAB}, - {0xF0, 0x2B}, - {0xF1, 0x1A}, - {0xF0, 0xAC}, - {0xF0, 0x2C}, - {0xF1, 0x19}, - {0xF0, 0xAD}, - {0xF0, 0x2D}, - {0xF1, 0x18}, - {0xF0, 0xAE}, - {0xF0, 0x2E}, - {0xF1, 0x17}, - {0xF0, 0xAF}, - {0xF0, 0x2F}, - {0xF1, 0x16}, - {0xF0, 0xB0}, - {0xF0, 0x30}, - {0xF1, 0x15}, - {0xF0, 0xB1}, - {0xF0, 0x31}, - {0xF1, 0x15}, - {0xF0, 0xB2}, - {0xF0, 0x32}, - {0xF1, 0x15}, - {0xF0, 0xB3}, - {0xF0, 0x33}, - {0xF1, 0x14}, - {0xF0, 0xB4}, - {0xF0, 0x34}, - {0xF1, 0x13}, - {0xF0, 0xB5}, - {0xF0, 0x35}, - {0xF1, 0x12}, - {0xF0, 0xB6}, - {0xF0, 0x36}, - {0xF1, 0x11}, - {0xF0, 0xB7}, - {0xF0, 0x37}, - {0xF1, 0x10}, - {0xF0, 0xB8}, - {0xF0, 0x38}, - {0xF1, 0x0F}, - {0xF0, 0xB9}, - {0xF0, 0x39}, - {0xF1, 0x0E}, - {0xF0, 0xBA}, - {0xF0, 0x3A}, - {0xF1, 0x0D}, - {0xF0, 0xBB}, - {0xF0, 0x3B}, - {0xF1, 0x0C}, - {0xF0, 0xBC}, - {0xF0, 0x3C}, - {0xF1, 0x0B}, - {0xF0, 0xBD}, - {0xF0, 0x3D}, - {0xF1, 0x0B}, - {0xF0, 0xBE}, - {0xF0, 0x3E}, - {0xF1, 0x0A}, - {0xF0, 0xBF}, - {0xF0, 0x3F}, - {0xF1, 0x09}, - {0xF0, 0x00}, -}; - -#define CB_VT3253B0_INIT_FOR_AIROHA2230 256 -/* For AIROHA */ -static -unsigned char vt3253b0_airoha2230[CB_VT3253B0_INIT_FOR_AIROHA2230][2] = { - {0x00, 0x31}, - {0x01, 0x00}, - {0x02, 0x00}, - {0x03, 0x00}, - {0x04, 0x00}, - {0x05, 0x80}, - {0x06, 0x00}, - {0x07, 0x00}, - {0x08, 0x70}, - {0x09, 0x41}, - {0x0a, 0x2A}, - {0x0b, 0x76}, - {0x0c, 0x00}, - {0x0d, 0x00}, - {0x0e, 0x80}, - {0x0f, 0x00}, - {0x10, 0x00}, - {0x11, 0x00}, - {0x12, 0x00}, - {0x13, 0x00}, - {0x14, 0x00}, - {0x15, 0x00}, - {0x16, 0x00}, - {0x17, 0x00}, - {0x18, 0x00}, - {0x19, 0x00}, - {0x1a, 0x00}, - {0x1b, 0x8f}, - {0x1c, 0x09}, - {0x1d, 0x00}, - {0x1e, 0x00}, - {0x1f, 0x00}, - {0x20, 0x00}, - {0x21, 0x00}, - {0x22, 0x00}, - {0x23, 0x00}, - {0x24, 0x00}, - {0x25, 0x4a}, - {0x26, 0x00}, - {0x27, 0x00}, - {0x28, 0x00}, - {0x29, 0x00}, - {0x2a, 0x00}, - {0x2b, 0x00}, - {0x2c, 0x00}, - {0x2d, 0x4a}, - {0x2e, 0x00}, - {0x2f, 0x0a}, - {0x30, 0x26}, - {0x31, 0x5b}, - {0x32, 0x00}, - {0x33, 0x00}, - {0x34, 0x00}, - {0x35, 0x00}, - {0x36, 0xaa}, - {0x37, 0xaa}, - {0x38, 0xff}, - {0x39, 0xff}, - {0x3a, 0x79}, - {0x3b, 0x00}, - {0x3c, 0x00}, - {0x3d, 0x0b}, - {0x3e, 0x48}, - {0x3f, 0x04}, - {0x40, 0x00}, - {0x41, 0x08}, - {0x42, 0x00}, - {0x43, 0x08}, - {0x44, 0x08}, - {0x45, 0x14}, - {0x46, 0x05}, - {0x47, 0x09}, - {0x48, 0x00}, - {0x49, 0x00}, - {0x4a, 0x00}, - {0x4b, 0x00}, - {0x4c, 0x09}, - {0x4d, 0x73}, - {0x4e, 0x00}, - {0x4f, 0xc5}, - {0x50, 0x15}, - {0x51, 0x19}, - {0x52, 0x00}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x56, 0x00}, - {0x57, 0x00}, - {0x58, 0x00}, - {0x59, 0xb0}, - {0x5a, 0x00}, - {0x5b, 0x00}, - {0x5c, 0x00}, - {0x5d, 0x00}, - {0x5e, 0x00}, - {0x5f, 0x00}, - {0x60, 0xe4}, - {0x61, 0x80}, - {0x62, 0x00}, - {0x63, 0x00}, - {0x64, 0x00}, - {0x65, 0x00}, - {0x66, 0x98}, - {0x67, 0x0a}, - {0x68, 0x00}, - {0x69, 0x00}, - {0x6a, 0x00}, - {0x6b, 0x00}, - {0x6c, 0x00}, /* RobertYu:20050125, request by JJSue */ - {0x6d, 0x03}, - {0x6e, 0x01}, - {0x6f, 0x00}, - {0x70, 0x00}, - {0x71, 0x00}, - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x00}, - {0x75, 0x00}, - {0x76, 0x00}, - {0x77, 0x00}, - {0x78, 0x00}, - {0x79, 0x00}, - {0x7a, 0x00}, - {0x7b, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x00}, - {0x7e, 0x00}, - {0x7f, 0x00}, - {0x80, 0x8c}, - {0x81, 0x01}, - {0x82, 0x09}, - {0x83, 0x00}, - {0x84, 0x00}, - {0x85, 0x00}, - {0x86, 0x00}, - {0x87, 0x00}, - {0x88, 0x08}, - {0x89, 0x00}, - {0x8a, 0x0f}, - {0x8b, 0xb7}, - {0x8c, 0x88}, - {0x8d, 0x47}, - {0x8e, 0xaa}, - {0x8f, 0x02}, - {0x90, 0x22}, - {0x91, 0x00}, - {0x92, 0x00}, - {0x93, 0x00}, - {0x94, 0x00}, - {0x95, 0x00}, - {0x96, 0x00}, - {0x97, 0xeb}, - {0x98, 0x00}, - {0x99, 0x00}, - {0x9a, 0x00}, - {0x9b, 0x00}, - {0x9c, 0x00}, - {0x9d, 0x00}, - {0x9e, 0x00}, - {0x9f, 0x01}, - {0xa0, 0x00}, - {0xa1, 0x00}, - {0xa2, 0x00}, - {0xa3, 0x00}, - {0xa4, 0x00}, - {0xa5, 0x00}, - {0xa6, 0x10}, - {0xa7, 0x00}, - {0xa8, 0x18}, - {0xa9, 0x00}, - {0xaa, 0x00}, - {0xab, 0x00}, - {0xac, 0x00}, - {0xad, 0x00}, - {0xae, 0x00}, - {0xaf, 0x18}, - {0xb0, 0x38}, - {0xb1, 0x30}, - {0xb2, 0x00}, - {0xb3, 0x00}, - {0xb4, 0xff}, - {0xb5, 0x0f}, - {0xb6, 0xe4}, - {0xb7, 0xe2}, - {0xb8, 0x00}, - {0xb9, 0x00}, - {0xba, 0x00}, - {0xbb, 0x03}, - {0xbc, 0x01}, - {0xbd, 0x00}, - {0xbe, 0x00}, - {0xbf, 0x00}, - {0xc0, 0x18}, - {0xc1, 0x20}, - {0xc2, 0x07}, - {0xc3, 0x18}, - {0xc4, 0xff}, - {0xc5, 0x2c}, - {0xc6, 0x0c}, - {0xc7, 0x0a}, - {0xc8, 0x0e}, - {0xc9, 0x01}, - {0xca, 0x68}, - {0xcb, 0xa7}, - {0xcc, 0x3c}, - {0xcd, 0x10}, - {0xce, 0x00}, - {0xcf, 0x25}, - {0xd0, 0x40}, - {0xd1, 0x12}, - {0xd2, 0x00}, - {0xd3, 0x00}, - {0xd4, 0x10}, - {0xd5, 0x28}, - {0xd6, 0x80}, - {0xd7, 0x2A}, - {0xd8, 0x00}, - {0xd9, 0x00}, - {0xda, 0x00}, - {0xdb, 0x00}, - {0xdc, 0x00}, - {0xdd, 0x00}, - {0xde, 0x00}, - {0xdf, 0x00}, - {0xe0, 0x00}, - {0xe1, 0xB3}, - {0xe2, 0x00}, - {0xe3, 0x00}, - {0xe4, 0x00}, - {0xe5, 0x10}, - {0xe6, 0x00}, - {0xe7, 0x1C}, - {0xe8, 0x00}, - {0xe9, 0xf4}, - {0xea, 0x00}, - {0xeb, 0xff}, - {0xec, 0x79}, - {0xed, 0x20}, - {0xee, 0x30}, - {0xef, 0x01}, - {0xf0, 0x00}, - {0xf1, 0x3e}, - {0xf2, 0x00}, - {0xf3, 0x00}, - {0xf4, 0x00}, - {0xf5, 0x00}, - {0xf6, 0x00}, - {0xf7, 0x00}, - {0xf8, 0x00}, - {0xf9, 0x00}, - {0xfa, 0x00}, - {0xfb, 0x00}, - {0xfc, 0x00}, - {0xfd, 0x00}, - {0xfe, 0x00}, - {0xff, 0x00}, -}; - -#define CB_VT3253B0_INIT_FOR_UW2451 256 -/* For UW2451 */ -static unsigned char vt3253b0_uw2451[CB_VT3253B0_INIT_FOR_UW2451][2] = { - {0x00, 0x31}, - {0x01, 0x00}, - {0x02, 0x00}, - {0x03, 0x00}, - {0x04, 0x00}, - {0x05, 0x81}, - {0x06, 0x00}, - {0x07, 0x00}, - {0x08, 0x38}, - {0x09, 0x45}, - {0x0a, 0x28}, - {0x0b, 0x76}, - {0x0c, 0x00}, - {0x0d, 0x00}, - {0x0e, 0x80}, - {0x0f, 0x00}, - {0x10, 0x00}, - {0x11, 0x00}, - {0x12, 0x00}, - {0x13, 0x00}, - {0x14, 0x00}, - {0x15, 0x00}, - {0x16, 0x00}, - {0x17, 0x00}, - {0x18, 0x00}, - {0x19, 0x00}, - {0x1a, 0x00}, - {0x1b, 0x8f}, - {0x1c, 0x0f}, - {0x1d, 0x00}, - {0x1e, 0x00}, - {0x1f, 0x00}, - {0x20, 0x00}, - {0x21, 0x00}, - {0x22, 0x00}, - {0x23, 0x00}, - {0x24, 0x00}, - {0x25, 0x4a}, - {0x26, 0x00}, - {0x27, 0x00}, - {0x28, 0x00}, - {0x29, 0x00}, - {0x2a, 0x00}, - {0x2b, 0x00}, - {0x2c, 0x00}, - {0x2d, 0x18}, - {0x2e, 0x00}, - {0x2f, 0x0a}, - {0x30, 0x26}, - {0x31, 0x5b}, - {0x32, 0x00}, - {0x33, 0x00}, - {0x34, 0x00}, - {0x35, 0x00}, - {0x36, 0xaa}, - {0x37, 0xaa}, - {0x38, 0xff}, - {0x39, 0xff}, - {0x3a, 0x00}, - {0x3b, 0x00}, - {0x3c, 0x00}, - {0x3d, 0x03}, - {0x3e, 0x1d}, - {0x3f, 0x04}, - {0x40, 0x00}, - {0x41, 0x08}, - {0x42, 0x00}, - {0x43, 0x08}, - {0x44, 0x08}, - {0x45, 0x14}, - {0x46, 0x05}, - {0x47, 0x09}, - {0x48, 0x00}, - {0x49, 0x00}, - {0x4a, 0x00}, - {0x4b, 0x00}, - {0x4c, 0x09}, - {0x4d, 0x90}, - {0x4e, 0x00}, - {0x4f, 0xc5}, - {0x50, 0x15}, - {0x51, 0x19}, - {0x52, 0x00}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x56, 0x00}, - {0x57, 0x00}, - {0x58, 0x00}, - {0x59, 0xb0}, - {0x5a, 0x00}, - {0x5b, 0x00}, - {0x5c, 0x00}, - {0x5d, 0x00}, - {0x5e, 0x00}, - {0x5f, 0x00}, - {0x60, 0xb3}, - {0x61, 0x81}, - {0x62, 0x00}, - {0x63, 0x00}, - {0x64, 0x00}, - {0x65, 0x00}, - {0x66, 0x57}, - {0x67, 0x6c}, - {0x68, 0x00}, - {0x69, 0x00}, - {0x6a, 0x00}, - {0x6b, 0x00}, - {0x6c, 0x00}, /* RobertYu:20050125, request by JJSue */ - {0x6d, 0x03}, - {0x6e, 0x01}, - {0x6f, 0x00}, - {0x70, 0x00}, - {0x71, 0x00}, - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x00}, - {0x75, 0x00}, - {0x76, 0x00}, - {0x77, 0x00}, - {0x78, 0x00}, - {0x79, 0x00}, - {0x7a, 0x00}, - {0x7b, 0x00}, - {0x7c, 0x00}, - {0x7d, 0x00}, - {0x7e, 0x00}, - {0x7f, 0x00}, - {0x80, 0x8c}, - {0x81, 0x00}, - {0x82, 0x0e}, - {0x83, 0x00}, - {0x84, 0x00}, - {0x85, 0x00}, - {0x86, 0x00}, - {0x87, 0x00}, - {0x88, 0x08}, - {0x89, 0x00}, - {0x8a, 0x0e}, - {0x8b, 0xa7}, - {0x8c, 0x88}, - {0x8d, 0x47}, - {0x8e, 0xaa}, - {0x8f, 0x02}, - {0x90, 0x00}, - {0x91, 0x00}, - {0x92, 0x00}, - {0x93, 0x00}, - {0x94, 0x00}, - {0x95, 0x00}, - {0x96, 0x00}, - {0x97, 0xe3}, - {0x98, 0x00}, - {0x99, 0x00}, - {0x9a, 0x00}, - {0x9b, 0x00}, - {0x9c, 0x00}, - {0x9d, 0x00}, - {0x9e, 0x00}, - {0x9f, 0x00}, - {0xa0, 0x00}, - {0xa1, 0x00}, - {0xa2, 0x00}, - {0xa3, 0x00}, - {0xa4, 0x00}, - {0xa5, 0x00}, - {0xa6, 0x10}, - {0xa7, 0x00}, - {0xa8, 0x18}, - {0xa9, 0x00}, - {0xaa, 0x00}, - {0xab, 0x00}, - {0xac, 0x00}, - {0xad, 0x00}, - {0xae, 0x00}, - {0xaf, 0x18}, - {0xb0, 0x18}, - {0xb1, 0x30}, - {0xb2, 0x00}, - {0xb3, 0x00}, - {0xb4, 0x00}, - {0xb5, 0x00}, - {0xb6, 0x00}, - {0xb7, 0x00}, - {0xb8, 0x00}, - {0xb9, 0x00}, - {0xba, 0x00}, - {0xbb, 0x03}, - {0xbc, 0x01}, - {0xbd, 0x00}, - {0xbe, 0x00}, - {0xbf, 0x00}, - {0xc0, 0x10}, - {0xc1, 0x20}, - {0xc2, 0x00}, - {0xc3, 0x20}, - {0xc4, 0x00}, - {0xc5, 0x2c}, - {0xc6, 0x1c}, - {0xc7, 0x10}, - {0xc8, 0x10}, - {0xc9, 0x01}, - {0xca, 0x68}, - {0xcb, 0xa7}, - {0xcc, 0x3c}, - {0xcd, 0x09}, - {0xce, 0x00}, - {0xcf, 0x20}, - {0xd0, 0x40}, - {0xd1, 0x10}, - {0xd2, 0x00}, - {0xd3, 0x00}, - {0xd4, 0x20}, - {0xd5, 0x28}, - {0xd6, 0xa0}, - {0xd7, 0x2a}, - {0xd8, 0x00}, - {0xd9, 0x00}, - {0xda, 0x00}, - {0xdb, 0x00}, - {0xdc, 0x00}, - {0xdd, 0x00}, - {0xde, 0x00}, - {0xdf, 0x00}, - {0xe0, 0x00}, - {0xe1, 0xd3}, - {0xe2, 0xc0}, - {0xe3, 0x00}, - {0xe4, 0x00}, - {0xe5, 0x10}, - {0xe6, 0x00}, - {0xe7, 0x12}, - {0xe8, 0x12}, - {0xe9, 0x34}, - {0xea, 0x00}, - {0xeb, 0xff}, - {0xec, 0x79}, - {0xed, 0x20}, - {0xee, 0x30}, - {0xef, 0x01}, - {0xf0, 0x00}, - {0xf1, 0x3e}, - {0xf2, 0x00}, - {0xf3, 0x00}, - {0xf4, 0x00}, - {0xf5, 0x00}, - {0xf6, 0x00}, - {0xf7, 0x00}, - {0xf8, 0x00}, - {0xf9, 0x00}, - {0xfa, 0x00}, - {0xfb, 0x00}, - {0xfc, 0x00}, - {0xfd, 0x00}, - {0xfe, 0x00}, - {0xff, 0x00}, -}; - -#define CB_VT3253B0_AGC 193 -/* For AIROHA */ -static unsigned char vt3253b0_agc[CB_VT3253B0_AGC][2] = { - {0xF0, 0x00}, - {0xF1, 0x00}, - {0xF0, 0x80}, - {0xF0, 0x01}, - {0xF1, 0x00}, - {0xF0, 0x81}, - {0xF0, 0x02}, - {0xF1, 0x02}, - {0xF0, 0x82}, - {0xF0, 0x03}, - {0xF1, 0x04}, - {0xF0, 0x83}, - {0xF0, 0x03}, - {0xF1, 0x04}, - {0xF0, 0x84}, - {0xF0, 0x04}, - {0xF1, 0x06}, - {0xF0, 0x85}, - {0xF0, 0x05}, - {0xF1, 0x06}, - {0xF0, 0x86}, - {0xF0, 0x06}, - {0xF1, 0x06}, - {0xF0, 0x87}, - {0xF0, 0x07}, - {0xF1, 0x08}, - {0xF0, 0x88}, - {0xF0, 0x08}, - {0xF1, 0x08}, - {0xF0, 0x89}, - {0xF0, 0x09}, - {0xF1, 0x0A}, - {0xF0, 0x8A}, - {0xF0, 0x0A}, - {0xF1, 0x0A}, - {0xF0, 0x8B}, - {0xF0, 0x0B}, - {0xF1, 0x0C}, - {0xF0, 0x8C}, - {0xF0, 0x0C}, - {0xF1, 0x0C}, - {0xF0, 0x8D}, - {0xF0, 0x0D}, - {0xF1, 0x0E}, - {0xF0, 0x8E}, - {0xF0, 0x0E}, - {0xF1, 0x0E}, - {0xF0, 0x8F}, - {0xF0, 0x0F}, - {0xF1, 0x10}, - {0xF0, 0x90}, - {0xF0, 0x10}, - {0xF1, 0x10}, - {0xF0, 0x91}, - {0xF0, 0x11}, - {0xF1, 0x12}, - {0xF0, 0x92}, - {0xF0, 0x12}, - {0xF1, 0x12}, - {0xF0, 0x93}, - {0xF0, 0x13}, - {0xF1, 0x14}, - {0xF0, 0x94}, - {0xF0, 0x14}, - {0xF1, 0x14}, - {0xF0, 0x95}, - {0xF0, 0x15}, - {0xF1, 0x16}, - {0xF0, 0x96}, - {0xF0, 0x16}, - {0xF1, 0x16}, - {0xF0, 0x97}, - {0xF0, 0x17}, - {0xF1, 0x18}, - {0xF0, 0x98}, - {0xF0, 0x18}, - {0xF1, 0x18}, - {0xF0, 0x99}, - {0xF0, 0x19}, - {0xF1, 0x1A}, - {0xF0, 0x9A}, - {0xF0, 0x1A}, - {0xF1, 0x1A}, - {0xF0, 0x9B}, - {0xF0, 0x1B}, - {0xF1, 0x1C}, - {0xF0, 0x9C}, - {0xF0, 0x1C}, - {0xF1, 0x1C}, - {0xF0, 0x9D}, - {0xF0, 0x1D}, - {0xF1, 0x1E}, - {0xF0, 0x9E}, - {0xF0, 0x1E}, - {0xF1, 0x1E}, - {0xF0, 0x9F}, - {0xF0, 0x1F}, - {0xF1, 0x20}, - {0xF0, 0xA0}, - {0xF0, 0x20}, - {0xF1, 0x20}, - {0xF0, 0xA1}, - {0xF0, 0x21}, - {0xF1, 0x22}, - {0xF0, 0xA2}, - {0xF0, 0x22}, - {0xF1, 0x22}, - {0xF0, 0xA3}, - {0xF0, 0x23}, - {0xF1, 0x24}, - {0xF0, 0xA4}, - {0xF0, 0x24}, - {0xF1, 0x24}, - {0xF0, 0xA5}, - {0xF0, 0x25}, - {0xF1, 0x26}, - {0xF0, 0xA6}, - {0xF0, 0x26}, - {0xF1, 0x26}, - {0xF0, 0xA7}, - {0xF0, 0x27}, - {0xF1, 0x28}, - {0xF0, 0xA8}, - {0xF0, 0x28}, - {0xF1, 0x28}, - {0xF0, 0xA9}, - {0xF0, 0x29}, - {0xF1, 0x2A}, - {0xF0, 0xAA}, - {0xF0, 0x2A}, - {0xF1, 0x2A}, - {0xF0, 0xAB}, - {0xF0, 0x2B}, - {0xF1, 0x2C}, - {0xF0, 0xAC}, - {0xF0, 0x2C}, - {0xF1, 0x2C}, - {0xF0, 0xAD}, - {0xF0, 0x2D}, - {0xF1, 0x2E}, - {0xF0, 0xAE}, - {0xF0, 0x2E}, - {0xF1, 0x2E}, - {0xF0, 0xAF}, - {0xF0, 0x2F}, - {0xF1, 0x30}, - {0xF0, 0xB0}, - {0xF0, 0x30}, - {0xF1, 0x30}, - {0xF0, 0xB1}, - {0xF0, 0x31}, - {0xF1, 0x32}, - {0xF0, 0xB2}, - {0xF0, 0x32}, - {0xF1, 0x32}, - {0xF0, 0xB3}, - {0xF0, 0x33}, - {0xF1, 0x34}, - {0xF0, 0xB4}, - {0xF0, 0x34}, - {0xF1, 0x34}, - {0xF0, 0xB5}, - {0xF0, 0x35}, - {0xF1, 0x36}, - {0xF0, 0xB6}, - {0xF0, 0x36}, - {0xF1, 0x36}, - {0xF0, 0xB7}, - {0xF0, 0x37}, - {0xF1, 0x38}, - {0xF0, 0xB8}, - {0xF0, 0x38}, - {0xF1, 0x38}, - {0xF0, 0xB9}, - {0xF0, 0x39}, - {0xF1, 0x3A}, - {0xF0, 0xBA}, - {0xF0, 0x3A}, - {0xF1, 0x3A}, - {0xF0, 0xBB}, - {0xF0, 0x3B}, - {0xF1, 0x3C}, - {0xF0, 0xBC}, - {0xF0, 0x3C}, - {0xF1, 0x3C}, - {0xF0, 0xBD}, - {0xF0, 0x3D}, - {0xF1, 0x3E}, - {0xF0, 0xBE}, - {0xF0, 0x3E}, - {0xF1, 0x3E}, - {0xF0, 0xBF}, - {0xF0, 0x00}, -}; - -static const unsigned short awc_frame_time[MAX_RATE] = { - 10, 20, 55, 110, 24, 36, 48, 72, 96, 144, 192, 216 -}; - -/*--------------------- Export Variables --------------------------*/ -/* - * Description: Calculate data frame transmitting time - * - * Parameters: - * In: - * preamble_type - Preamble Type - * by_pkt_type - PK_TYPE_11A, PK_TYPE_11B, PK_TYPE_11GB, PK_TYPE_11GA - * cb_frame_length - Baseband Type - * tx_rate - Tx Rate - * Out: - * - * Return Value: FrameTime - * - */ -unsigned int bb_get_frame_time(unsigned char preamble_type, - unsigned char by_pkt_type, - unsigned int cb_frame_length, - unsigned short tx_rate) -{ - unsigned int frame_time; - unsigned int preamble; - unsigned int tmp; - unsigned int rate_idx = (unsigned int)tx_rate; - unsigned int rate = 0; - - if (rate_idx > RATE_54M) - return 0; - - rate = (unsigned int)awc_frame_time[rate_idx]; - - if (rate_idx <= 3) { /* CCK mode */ - if (preamble_type == PREAMBLE_SHORT) - preamble = 96; - else - preamble = 192; - frame_time = (cb_frame_length * 80) / rate; /* ????? */ - tmp = (frame_time * rate) / 80; - if (cb_frame_length != tmp) - frame_time++; - - return preamble + frame_time; - } - frame_time = (cb_frame_length * 8 + 22) / rate; /* ???????? */ - tmp = ((frame_time * rate) - 22) / 8; - if (cb_frame_length != tmp) - frame_time++; - - frame_time = frame_time * 4; /* ??????? */ - if (by_pkt_type != PK_TYPE_11A) - frame_time += 6; /* ?????? */ - - return 20 + frame_time; /* ?????? */ -} - -/* - * Description: Calculate Length, Service, and Signal fields of Phy for Tx - * - * Parameters: - * In: - * priv - Device Structure - * frame_length - Tx Frame Length - * tx_rate - Tx Rate - * Out: - * struct vnt_phy_field *phy - * - pointer to Phy Length field - * - pointer to Phy Service field - * - pointer to Phy Signal field - * - * Return Value: none - * - */ -void vnt_get_phy_field(struct vnt_private *priv, u32 frame_length, - u16 tx_rate, u8 pkt_type, struct vnt_phy_field *phy) -{ - u32 bit_count; - u32 count = 0; - u32 tmp; - int ext_bit; - u8 preamble_type = priv->preamble_type; - - bit_count = frame_length * 8; - ext_bit = false; - - switch (tx_rate) { - case RATE_1M: - count = bit_count; - - phy->signal = 0x00; - - break; - case RATE_2M: - count = bit_count / 2; - - if (preamble_type == PREAMBLE_SHORT) - phy->signal = 0x09; - else - phy->signal = 0x01; - - break; - case RATE_5M: - count = (bit_count * 10) / 55; - tmp = (count * 55) / 10; - - if (tmp != bit_count) - count++; - - if (preamble_type == PREAMBLE_SHORT) - phy->signal = 0x0a; - else - phy->signal = 0x02; - - break; - case RATE_11M: - count = bit_count / 11; - tmp = count * 11; - - if (tmp != bit_count) { - count++; - - if ((bit_count - tmp) <= 3) - ext_bit = true; - } - - if (preamble_type == PREAMBLE_SHORT) - phy->signal = 0x0b; - else - phy->signal = 0x03; - - break; - case RATE_6M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9b; - else - phy->signal = 0x8b; - - break; - case RATE_9M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9f; - else - phy->signal = 0x8f; - - break; - case RATE_12M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9a; - else - phy->signal = 0x8a; - - break; - case RATE_18M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9e; - else - phy->signal = 0x8e; - - break; - case RATE_24M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x99; - else - phy->signal = 0x89; - - break; - case RATE_36M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9d; - else - phy->signal = 0x8d; - - break; - case RATE_48M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x98; - else - phy->signal = 0x88; - - break; - case RATE_54M: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9c; - else - phy->signal = 0x8c; - break; - default: - if (pkt_type == PK_TYPE_11A) - phy->signal = 0x9c; - else - phy->signal = 0x8c; - break; - } - - if (pkt_type == PK_TYPE_11B) { - phy->service = 0x00; - if (ext_bit) - phy->service |= 0x80; - phy->len = cpu_to_le16((u16)count); - } else { - phy->service = 0x00; - phy->len = cpu_to_le16((u16)frame_length); - } -} - -/* - * Description: Read a byte from BASEBAND, by embedded programming - * - * Parameters: - * In: - * iobase - I/O base address - * by_bb_addr - address of register in Baseband - * Out: - * pby_data - data read - * - * Return Value: true if succeeded; false if failed. - * - */ -bool bb_read_embedded(struct vnt_private *priv, unsigned char by_bb_addr, - unsigned char *pby_data) -{ - void __iomem *iobase = priv->port_offset; - unsigned short ww; - unsigned char by_value; - - /* BB reg offset */ - iowrite8(by_bb_addr, iobase + MAC_REG_BBREGADR); - - /* turn on REGR */ - vt6655_mac_reg_bits_on(iobase, MAC_REG_BBREGCTL, BBREGCTL_REGR); - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - by_value = ioread8(iobase + MAC_REG_BBREGCTL); - if (by_value & BBREGCTL_DONE) - break; - } - - /* get BB data */ - *pby_data = ioread8(iobase + MAC_REG_BBREGDATA); - - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x30)\n"); - return false; - } - return true; -} - -/* - * Description: Write a Byte to BASEBAND, by embedded programming - * - * Parameters: - * In: - * iobase - I/O base address - * by_bb_addr - address of register in Baseband - * by_data - data to write - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ -bool bb_write_embedded(struct vnt_private *priv, unsigned char by_bb_addr, - unsigned char by_data) -{ - void __iomem *iobase = priv->port_offset; - unsigned short ww; - unsigned char by_value; - - /* BB reg offset */ - iowrite8(by_bb_addr, iobase + MAC_REG_BBREGADR); - /* set BB data */ - iowrite8(by_data, iobase + MAC_REG_BBREGDATA); - - /* turn on BBREGCTL_REGW */ - vt6655_mac_reg_bits_on(iobase, MAC_REG_BBREGCTL, BBREGCTL_REGW); - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - by_value = ioread8(iobase + MAC_REG_BBREGCTL); - if (by_value & BBREGCTL_DONE) - break; - } - - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x31)\n"); - return false; - } - return true; -} - -/* - * Description: VIA VT3253 Baseband chip init function - * - * Parameters: - * In: - * iobase - I/O base address - * byRevId - Revision ID - * rf_type - RF type - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ - -bool bb_vt3253_init(struct vnt_private *priv) -{ - bool result = true; - int ii; - void __iomem *iobase = priv->port_offset; - unsigned char rf_type = priv->rf_type; - unsigned char by_local_id = priv->local_id; - - if (rf_type == RF_RFMD2959) { - if (by_local_id <= REV_ID_VT3253_A1) { - for (ii = 0; ii < CB_VT3253_INIT_FOR_RFMD; ii++) - result &= bb_write_embedded(priv, - by_vt3253_init_tab_rfmd[ii][0], - by_vt3253_init_tab_rfmd[ii][1]); - - } else { - for (ii = 0; ii < CB_VT3253B0_INIT_FOR_RFMD; ii++) - result &= bb_write_embedded(priv, - vt3253b0_rfmd[ii][0], - vt3253b0_rfmd[ii][1]); - - for (ii = 0; ii < CB_VT3253B0_AGC_FOR_RFMD2959; ii++) - result &= bb_write_embedded(priv, - vt3253b0_agc4_rfmd2959[ii][0], - vt3253b0_agc4_rfmd2959[ii][1]); - - iowrite32(0x23, iobase + MAC_REG_ITRTMSET); - vt6655_mac_reg_bits_on(iobase, MAC_REG_PAPEDELAY, BIT(0)); - } - priv->bbvga[0] = 0x18; - priv->bbvga[1] = 0x0A; - priv->bbvga[2] = 0x0; - priv->bbvga[3] = 0x0; - priv->dbm_threshold[0] = -70; - priv->dbm_threshold[1] = -50; - priv->dbm_threshold[2] = 0; - priv->dbm_threshold[3] = 0; - } else if ((rf_type == RF_AIROHA) || (rf_type == RF_AL2230S)) { - for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++) - result &= bb_write_embedded(priv, - vt3253b0_airoha2230[ii][0], - vt3253b0_airoha2230[ii][1]); - - for (ii = 0; ii < CB_VT3253B0_AGC; ii++) - result &= bb_write_embedded(priv, - vt3253b0_agc[ii][0], vt3253b0_agc[ii][1]); - - priv->bbvga[0] = 0x1C; - priv->bbvga[1] = 0x10; - priv->bbvga[2] = 0x0; - priv->bbvga[3] = 0x0; - priv->dbm_threshold[0] = -70; - priv->dbm_threshold[1] = -48; - priv->dbm_threshold[2] = 0; - priv->dbm_threshold[3] = 0; - } else if (rf_type == RF_UW2451) { - for (ii = 0; ii < CB_VT3253B0_INIT_FOR_UW2451; ii++) - result &= bb_write_embedded(priv, - vt3253b0_uw2451[ii][0], - vt3253b0_uw2451[ii][1]); - - for (ii = 0; ii < CB_VT3253B0_AGC; ii++) - result &= bb_write_embedded(priv, - vt3253b0_agc[ii][0], - vt3253b0_agc[ii][1]); - - iowrite8(0x23, iobase + MAC_REG_ITRTMSET); - vt6655_mac_reg_bits_on(iobase, MAC_REG_PAPEDELAY, BIT(0)); - - priv->bbvga[0] = 0x14; - priv->bbvga[1] = 0x0A; - priv->bbvga[2] = 0x0; - priv->bbvga[3] = 0x0; - priv->dbm_threshold[0] = -60; - priv->dbm_threshold[1] = -50; - priv->dbm_threshold[2] = 0; - priv->dbm_threshold[3] = 0; - } else if (rf_type == RF_VT3226) { - for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++) - result &= bb_write_embedded(priv, - vt3253b0_airoha2230[ii][0], - vt3253b0_airoha2230[ii][1]); - - for (ii = 0; ii < CB_VT3253B0_AGC; ii++) - result &= bb_write_embedded(priv, - vt3253b0_agc[ii][0], vt3253b0_agc[ii][1]); - - priv->bbvga[0] = 0x1C; - priv->bbvga[1] = 0x10; - priv->bbvga[2] = 0x0; - priv->bbvga[3] = 0x0; - priv->dbm_threshold[0] = -70; - priv->dbm_threshold[1] = -48; - priv->dbm_threshold[2] = 0; - priv->dbm_threshold[3] = 0; - /* Fix VT3226 DFC system timing issue */ - vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_RFLEOPT); - /* {{ RobertYu: 20050104 */ - } else { - /* No VGA Table now */ - priv->update_bbvga = false; - priv->bbvga[0] = 0x1C; - } - - if (by_local_id > REV_ID_VT3253_A1) { - bb_write_embedded(priv, 0x04, 0x7F); - bb_write_embedded(priv, 0x0D, 0x01); - } - - return result; -} - -/* - * Description: Set ShortSlotTime mode - * - * Parameters: - * In: - * priv - Device Structure - * Out: - * none - * - * Return Value: none - * - */ -void -bb_set_short_slot_time(struct vnt_private *priv) -{ - unsigned char by_bb_rx_conf = 0; - unsigned char by_bb_vga = 0; - - bb_read_embedded(priv, 0x0A, &by_bb_rx_conf); /* CR10 */ - - if (priv->short_slot_time) - by_bb_rx_conf &= 0xDF; /* 1101 1111 */ - else - by_bb_rx_conf |= 0x20; /* 0010 0000 */ - - /* patch for 3253B0 Baseband with Cardbus module */ - bb_read_embedded(priv, 0xE7, &by_bb_vga); - if (by_bb_vga == priv->bbvga[0]) - by_bb_rx_conf |= 0x20; /* 0010 0000 */ - - bb_write_embedded(priv, 0x0A, by_bb_rx_conf); /* CR10 */ -} - -void bb_set_vga_gain_offset(struct vnt_private *priv, unsigned char by_data) -{ - unsigned char by_bb_rx_conf = 0; - - bb_write_embedded(priv, 0xE7, by_data); - - bb_read_embedded(priv, 0x0A, &by_bb_rx_conf); /* CR10 */ - /* patch for 3253B0 Baseband with Cardbus module */ - if (by_data == priv->bbvga[0]) - by_bb_rx_conf |= 0x20; /* 0010 0000 */ - else if (priv->short_slot_time) - by_bb_rx_conf &= 0xDF; /* 1101 1111 */ - else - by_bb_rx_conf |= 0x20; /* 0010 0000 */ - priv->bbvga_current = by_data; - bb_write_embedded(priv, 0x0A, by_bb_rx_conf); /* CR10 */ -} - -/* - * Description: Baseband SoftwareReset - * - * Parameters: - * In: - * iobase - I/O base address - * Out: - * none - * - * Return Value: none - * - */ -void -bb_software_reset(struct vnt_private *priv) -{ - bb_write_embedded(priv, 0x50, 0x40); - bb_write_embedded(priv, 0x50, 0); - bb_write_embedded(priv, 0x9C, 0x01); - bb_write_embedded(priv, 0x9C, 0); -} - -/* - * Description: Set Tx Antenna mode - * - * Parameters: - * In: - * priv - Device Structure - * by_antenna_mode - Antenna Mode - * Out: - * none - * - * Return Value: none - * - */ - -void -bb_set_tx_antenna_mode(struct vnt_private *priv, unsigned char by_antenna_mode) -{ - unsigned char by_bb_tx_conf; - - bb_read_embedded(priv, 0x09, &by_bb_tx_conf); /* CR09 */ - if (by_antenna_mode == ANT_DIVERSITY) { - /* bit 1 is diversity */ - by_bb_tx_conf |= 0x02; - } else if (by_antenna_mode == ANT_A) { - /* bit 2 is ANTSEL */ - by_bb_tx_conf &= 0xF9; /* 1111 1001 */ - } else if (by_antenna_mode == ANT_B) { - by_bb_tx_conf &= 0xFD; /* 1111 1101 */ - by_bb_tx_conf |= 0x04; - } - bb_write_embedded(priv, 0x09, by_bb_tx_conf); /* CR09 */ -} - -/* - * Description: Set Rx Antenna mode - * - * Parameters: - * In: - * priv - Device Structure - * by_antenna_mode - Antenna Mode - * Out: - * none - * - * Return Value: none - * - */ - -void -bb_set_rx_antenna_mode(struct vnt_private *priv, unsigned char by_antenna_mode) -{ - unsigned char by_bb_rx_conf; - - bb_read_embedded(priv, 0x0A, &by_bb_rx_conf); /* CR10 */ - if (by_antenna_mode == ANT_DIVERSITY) { - by_bb_rx_conf |= 0x01; - - } else if (by_antenna_mode == ANT_A) { - by_bb_rx_conf &= 0xFC; /* 1111 1100 */ - } else if (by_antenna_mode == ANT_B) { - by_bb_rx_conf &= 0xFE; /* 1111 1110 */ - by_bb_rx_conf |= 0x02; - } - bb_write_embedded(priv, 0x0A, by_bb_rx_conf); /* CR10 */ -} - -/* - * Description: bb_set_deep_sleep - * - * Parameters: - * In: - * priv - Device Structure - * Out: - * none - * - * Return Value: none - * - */ -void -bb_set_deep_sleep(struct vnt_private *priv, unsigned char by_local_id) -{ - bb_write_embedded(priv, 0x0C, 0x17); /* CR12 */ - bb_write_embedded(priv, 0x0D, 0xB9); /* CR13 */ -} - diff --git a/drivers/staging/vt6655/baseband.h b/drivers/staging/vt6655/baseband.h deleted file mode 100644 index e4a02c240a1c1..0000000000000 --- a/drivers/staging/vt6655/baseband.h +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions to access baseband - * - * Author: Jerry Chen - * - * Date: Jun. 5, 2002 - * - */ - -#ifndef __BASEBAND_H__ -#define __BASEBAND_H__ - -#include "device.h" - -/* - * Registers in the BASEBAND - */ -#define BB_MAX_CONTEXT_SIZE 256 - -/* - * Baseband RF pair definition in eeprom (Bits 6..0) - */ - -#define PREAMBLE_LONG 0 -#define PREAMBLE_SHORT 1 - -#define F5G 0 -#define F2_4G 1 - -#define TOP_RATE_54M 0x80000000 -#define TOP_RATE_48M 0x40000000 -#define TOP_RATE_36M 0x20000000 -#define TOP_RATE_24M 0x10000000 -#define TOP_RATE_18M 0x08000000 -#define TOP_RATE_12M 0x04000000 -#define TOP_RATE_11M 0x02000000 -#define TOP_RATE_9M 0x01000000 -#define TOP_RATE_6M 0x00800000 -#define TOP_RATE_55M 0x00400000 -#define TOP_RATE_2M 0x00200000 -#define TOP_RATE_1M 0x00100000 - -unsigned int bb_get_frame_time(unsigned char preamble_type, - unsigned char by_pkt_type, - unsigned int cb_frame_length, - unsigned short w_rate); - -void vnt_get_phy_field(struct vnt_private *priv, u32 frame_length, - u16 tx_rate, u8 pkt_type, struct vnt_phy_field *phy); - -bool bb_read_embedded(struct vnt_private *priv, unsigned char by_bb_addr, - unsigned char *pby_data); -bool bb_write_embedded(struct vnt_private *priv, unsigned char by_bb_addr, - unsigned char by_data); - -void bb_set_short_slot_time(struct vnt_private *priv); -void bb_set_vga_gain_offset(struct vnt_private *priv, unsigned char by_data); - -/* VT3253 Baseband */ -bool bb_vt3253_init(struct vnt_private *priv); -void bb_software_reset(struct vnt_private *priv); -void bb_set_tx_antenna_mode(struct vnt_private *priv, - unsigned char by_antenna_mode); -void bb_set_rx_antenna_mode(struct vnt_private *priv, - unsigned char by_antenna_mode); -void bb_set_deep_sleep(struct vnt_private *priv, unsigned char by_local_id); - -#endif /* __BASEBAND_H__ */ diff --git a/drivers/staging/vt6655/card.c b/drivers/staging/vt6655/card.c deleted file mode 100644 index 6a2e390e94939..0000000000000 --- a/drivers/staging/vt6655/card.c +++ /dev/null @@ -1,836 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Provide functions to setup NIC operation mode - * Functions: - * s_vSafeResetTx - Rest Tx - * card_set_rspinf - Set RSPINF - * CARDvUpdateBasicTopRate - Update BasicTopRate - * CARDbAddBasicRate - Add to BasicRateSet - * CARDbIsOFDMinBasicRate - Check if any OFDM rate is in BasicRateSet - * card_get_tsf_offset - Calculate TSFOffset - * vt6655_get_current_tsf - Read Current NIC TSF counter - * card_get_next_tbtt - Calculate Next Beacon TSF counter - * CARDvSetFirstNextTBTT - Set NIC Beacon time - * CARDvUpdateNextTBTT - Sync. NIC Beacon time - * card_radio_power_off - Turn Off NIC Radio Power - * - * Revision History: - * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. - * 08-26-2003 Kyle Hsu: Modify the definition type of iobase. - * 09-01-2003 Bryan YC Fan: Add vUpdateIFS(). - * - */ - -#include "card.h" -#include "baseband.h" -#include "mac.h" -#include "desc.h" -#include "rf.h" -#include "power.h" - -/*--------------------- Static Definitions -------------------------*/ - -#define C_SIFS_A 16 /* micro sec. */ -#define C_SIFS_BG 10 - -#define C_EIFS 80 /* micro sec. */ - -#define C_SLOT_SHORT 9 /* micro sec. */ -#define C_SLOT_LONG 20 - -#define C_CWMIN_A 15 /* slot time */ -#define C_CWMIN_B 31 - -#define C_CWMAX 1023 /* slot time */ - -#define WAIT_BEACON_TX_DOWN_TMO 3 /* Times */ - -/*--------------------- Static Variables --------------------------*/ - -static const unsigned short rx_bcn_tsf_off[MAX_RATE] = { - 17, 17, 17, 17, 34, 23, 17, 11, 8, 5, 4, 3}; - -/*--------------------- Static Functions --------------------------*/ - -static void vt6655_mac_set_bb_type(void __iomem *iobase, u32 mask) -{ - u32 reg_value; - - reg_value = ioread32(iobase + MAC_REG_ENCFG); - reg_value = reg_value & ~ENCFG_BBTYPE_MASK; - reg_value = reg_value | mask; - iowrite32(reg_value, iobase + MAC_REG_ENCFG); -} - -/*--------------------- Export Functions --------------------------*/ - -/* - * Description: Calculate TxRate and RsvTime fields for RSPINF in OFDM mode. - * - * Parameters: - * In: - * wRate - Tx Rate - * byPktType - Tx Packet type - * Out: - * tx_rate - pointer to RSPINF TxRate field - * rsv_time - pointer to RSPINF RsvTime field - * - * Return Value: none - */ -static void calculate_ofdmr_parameter(unsigned char rate, - u8 bb_type, - unsigned char *tx_rate, - unsigned char *rsv_time) -{ - switch (rate) { - case RATE_6M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x9B; - *rsv_time = 44; - } else { - *tx_rate = 0x8B; - *rsv_time = 50; - } - break; - - case RATE_9M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x9F; - *rsv_time = 36; - } else { - *tx_rate = 0x8F; - *rsv_time = 42; - } - break; - - case RATE_12M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x9A; - *rsv_time = 32; - } else { - *tx_rate = 0x8A; - *rsv_time = 38; - } - break; - - case RATE_18M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x9E; - *rsv_time = 28; - } else { - *tx_rate = 0x8E; - *rsv_time = 34; - } - break; - - case RATE_36M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x9D; - *rsv_time = 24; - } else { - *tx_rate = 0x8D; - *rsv_time = 30; - } - break; - - case RATE_48M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x98; - *rsv_time = 24; - } else { - *tx_rate = 0x88; - *rsv_time = 30; - } - break; - - case RATE_54M: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x9C; - *rsv_time = 24; - } else { - *tx_rate = 0x8C; - *rsv_time = 30; - } - break; - - case RATE_24M: - default: - if (bb_type == BB_TYPE_11A) { /* 5GHZ */ - *tx_rate = 0x99; - *rsv_time = 28; - } else { - *tx_rate = 0x89; - *rsv_time = 34; - } - break; - } -} - -/*--------------------- Export Functions --------------------------*/ - -/* - * Description: Update IFS - * - * Parameters: - * In: - * priv - The adapter to be set - * Out: - * none - * - * Return Value: None. - */ -bool card_set_phy_parameter(struct vnt_private *priv, u8 bb_type) -{ - unsigned char cw_max_min = 0; - unsigned char slot = 0; - unsigned char sifs = 0; - unsigned char difs = 0; - int i; - - /* Set SIFS, DIFS, EIFS, SlotTime, CwMin */ - if (bb_type == BB_TYPE_11A) { - vt6655_mac_set_bb_type(priv->port_offset, BB_TYPE_11A); - bb_write_embedded(priv, 0x88, 0x03); - slot = C_SLOT_SHORT; - sifs = C_SIFS_A; - difs = C_SIFS_A + 2 * C_SLOT_SHORT; - cw_max_min = 0xA4; - } else if (bb_type == BB_TYPE_11B) { - vt6655_mac_set_bb_type(priv->port_offset, BB_TYPE_11B); - bb_write_embedded(priv, 0x88, 0x02); - slot = C_SLOT_LONG; - sifs = C_SIFS_BG; - difs = C_SIFS_BG + 2 * C_SLOT_LONG; - cw_max_min = 0xA5; - } else { /* PK_TYPE_11GA & PK_TYPE_11GB */ - vt6655_mac_set_bb_type(priv->port_offset, BB_TYPE_11G); - bb_write_embedded(priv, 0x88, 0x08); - sifs = C_SIFS_BG; - - if (priv->short_slot_time) { - slot = C_SLOT_SHORT; - difs = C_SIFS_BG + 2 * C_SLOT_SHORT; - } else { - slot = C_SLOT_LONG; - difs = C_SIFS_BG + 2 * C_SLOT_LONG; - } - - cw_max_min = 0xa4; - - for (i = RATE_54M; i >= RATE_6M; i--) { - if (priv->basic_rates & ((u32)(0x1 << i))) { - cw_max_min |= 0x1; - break; - } - } - } - - if (priv->rf_type == RF_RFMD2959) { - /* - * bcs TX_PE will reserve 3 us hardware's processing - * time here is 2 us. - */ - sifs -= 3; - difs -= 3; - /* - * TX_PE will reserve 3 us for MAX2829 A mode only, it is for - * better TX throughput; MAC will need 2 us to process, so the - * SIFS, DIFS can be shorter by 2 us. - */ - } - - if (priv->sifs != sifs) { - priv->sifs = sifs; - iowrite8(priv->sifs, priv->port_offset + MAC_REG_SIFS); - } - if (priv->difs != difs) { - priv->difs = difs; - iowrite8(priv->difs, priv->port_offset + MAC_REG_DIFS); - } - if (priv->eifs != C_EIFS) { - priv->eifs = C_EIFS; - iowrite8(priv->eifs, priv->port_offset + MAC_REG_EIFS); - } - if (priv->slot != slot) { - priv->slot = slot; - iowrite8(priv->slot, priv->port_offset + MAC_REG_SLOT); - - bb_set_short_slot_time(priv); - } - if (priv->cw_max_min != cw_max_min) { - priv->cw_max_min = cw_max_min; - iowrite8(priv->cw_max_min, priv->port_offset + MAC_REG_CWMAXMIN0); - } - - priv->packet_type = card_get_pkt_type(priv); - - card_set_rspinf(priv, bb_type); - - return true; -} - -/* - * Description: Sync. TSF counter to BSS - * Get TSF offset and write to HW - * - * Parameters: - * In: - * priv - The adapter to be sync. - * rx_rate - data rate of receive beacon - * bss_timestamp - Rx BCN's TSF - * qwLocalTSF - Local TSF - * Out: - * none - * - * Return Value: none - */ -bool card_update_tsf(struct vnt_private *priv, unsigned char rx_rate, - u64 bss_timestamp) -{ - u64 local_tsf; - u64 tsf_offset = 0; - - local_tsf = vt6655_get_current_tsf(priv); - - if (bss_timestamp != local_tsf) { - tsf_offset = card_get_tsf_offset(rx_rate, bss_timestamp, - local_tsf); - /* adjust TSF, HW's TSF add TSF Offset reg */ - tsf_offset = le64_to_cpu(tsf_offset); - iowrite32((u32)tsf_offset, priv->port_offset + MAC_REG_TSFOFST); - iowrite32((u32)(tsf_offset >> 32), priv->port_offset + MAC_REG_TSFOFST + 4); - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TSFSYNCEN); - } - return true; -} - -/* - * Description: Set NIC TSF counter for first Beacon time - * Get NEXTTBTT from adjusted TSF and Beacon Interval - * - * Parameters: - * In: - * priv - The adapter to be set. - * beacon_interval - Beacon Interval - * Out: - * none - * - * Return Value: true if succeed; otherwise false - */ -bool card_set_beacon_period(struct vnt_private *priv, - unsigned short beacon_interval) -{ - u64 next_tbtt; - - next_tbtt = vt6655_get_current_tsf(priv); /* Get Local TSF counter */ - - next_tbtt = card_get_next_tbtt(next_tbtt, beacon_interval); - - /* set HW beacon interval */ - iowrite16(beacon_interval, priv->port_offset + MAC_REG_BI); - priv->beacon_interval = beacon_interval; - /* Set NextTBTT */ - next_tbtt = le64_to_cpu(next_tbtt); - iowrite32((u32)next_tbtt, priv->port_offset + MAC_REG_NEXTTBTT); - iowrite32((u32)(next_tbtt >> 32), priv->port_offset + MAC_REG_NEXTTBTT + 4); - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); - - return true; -} - -/* - * Description: Turn off Radio power - * - * Parameters: - * In: - * priv - The adapter to be turned off - * Out: - * none - * - */ -void card_radio_power_off(struct vnt_private *priv) -{ - if (priv->radio_off) - return; - - switch (priv->rf_type) { - case RF_RFMD2959: - vt6655_mac_word_reg_bits_off(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_TXPEINV); - vt6655_mac_word_reg_bits_on(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_SWPE1); - break; - - case RF_AIROHA: - case RF_AL2230S: - vt6655_mac_word_reg_bits_off(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_SWPE2); - vt6655_mac_word_reg_bits_off(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_SWPE3); - break; - } - - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_RXON); - - bb_set_deep_sleep(priv, priv->local_id); - - priv->radio_off = true; - pr_debug("chester power off\n"); - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_GPIOCTL0, LED_ACTSET); /* LED issue */ -} - -void card_safe_reset_tx(struct vnt_private *priv) -{ - unsigned int uu; - struct vnt_tx_desc *curr_td; - - /* initialize TD index */ - priv->tail_td[0] = &priv->ap_td0_rings[0]; - priv->apCurrTD[0] = &priv->ap_td0_rings[0]; - - priv->tail_td[1] = &priv->ap_td1_rings[0]; - priv->apCurrTD[1] = &priv->ap_td1_rings[0]; - - for (uu = 0; uu < TYPE_MAXTD; uu++) - priv->iTDUsed[uu] = 0; - - for (uu = 0; uu < priv->opts.tx_descs[0]; uu++) { - curr_td = &priv->ap_td0_rings[uu]; - curr_td->td0.owner = OWNED_BY_HOST; - /* init all Tx Packet pointer to NULL */ - } - for (uu = 0; uu < priv->opts.tx_descs[1]; uu++) { - curr_td = &priv->ap_td1_rings[uu]; - curr_td->td0.owner = OWNED_BY_HOST; - /* init all Tx Packet pointer to NULL */ - } - - /* set MAC TD pointer */ - vt6655_mac_set_curr_tx_desc_addr(TYPE_TXDMA0, priv, priv->td0_pool_dma); - - vt6655_mac_set_curr_tx_desc_addr(TYPE_AC0DMA, priv, priv->td1_pool_dma); - - /* set MAC Beacon TX pointer */ - iowrite32((u32)priv->tx_beacon_dma, priv->port_offset + MAC_REG_BCNDMAPTR); -} - -/* - * Description: - * Reset Rx - * - * Parameters: - * In: - * priv - Pointer to the adapter - * Out: - * none - * - * Return Value: none - */ -void CARDvSafeResetRx(struct vnt_private *priv) -{ - unsigned int uu; - struct vnt_rx_desc *pDesc; - - /* initialize RD index */ - priv->pCurrRD[0] = &priv->aRD0Ring[0]; - priv->pCurrRD[1] = &priv->aRD1Ring[0]; - - /* init state, all RD is chip's */ - for (uu = 0; uu < priv->opts.rx_descs0; uu++) { - pDesc = &priv->aRD0Ring[uu]; - pDesc->rd0.res_count = cpu_to_le16(priv->rx_buf_sz); - pDesc->rd0.owner = OWNED_BY_NIC; - pDesc->rd1.req_count = cpu_to_le16(priv->rx_buf_sz); - } - - /* init state, all RD is chip's */ - for (uu = 0; uu < priv->opts.rx_descs1; uu++) { - pDesc = &priv->aRD1Ring[uu]; - pDesc->rd0.res_count = cpu_to_le16(priv->rx_buf_sz); - pDesc->rd0.owner = OWNED_BY_NIC; - pDesc->rd1.req_count = cpu_to_le16(priv->rx_buf_sz); - } - - /* set perPkt mode */ - iowrite32(RX_PERPKT, priv->port_offset + MAC_REG_RXDMACTL0); - iowrite32(RX_PERPKT, priv->port_offset + MAC_REG_RXDMACTL1); - /* set MAC RD pointer */ - vt6655_mac_set_curr_rx_0_desc_addr(priv, priv->rd0_pool_dma); - - vt6655_mac_set_curr_rx_1_desc_addr(priv, priv->rd1_pool_dma); -} - -/* - * Description: Get response Control frame rate in CCK mode - * - * Parameters: - * In: - * priv - The adapter to be set - * wRateIdx - Receiving data rate - * Out: - * none - * - * Return Value: response Control frame rate - */ -static unsigned short CARDwGetCCKControlRate(struct vnt_private *priv, - unsigned short wRateIdx) -{ - unsigned int ui = (unsigned int)wRateIdx; - - while (ui > RATE_1M) { - if (priv->basic_rates & ((u32)0x1 << ui)) - return (unsigned short)ui; - - ui--; - } - return (unsigned short)RATE_1M; -} - -/* - * Description: Get response Control frame rate in OFDM mode - * - * Parameters: - * In: - * priv - The adapter to be set - * wRateIdx - Receiving data rate - * Out: - * none - * - * Return Value: response Control frame rate - */ -static unsigned short CARDwGetOFDMControlRate(struct vnt_private *priv, - unsigned short wRateIdx) -{ - unsigned int ui = (unsigned int)wRateIdx; - - pr_debug("BASIC RATE: %X\n", priv->basic_rates); - - if (!CARDbIsOFDMinBasicRate((void *)priv)) { - pr_debug("%s:(NO OFDM) %d\n", __func__, wRateIdx); - if (wRateIdx > RATE_24M) - wRateIdx = RATE_24M; - return wRateIdx; - } - while (ui > RATE_11M) { - if (priv->basic_rates & ((u32)0x1 << ui)) { - pr_debug("%s : %d\n", __func__, ui); - return (unsigned short)ui; - } - ui--; - } - pr_debug("%s: 6M\n", __func__); - return (unsigned short)RATE_24M; -} - -/* - * Description: Set RSPINF - * - * Parameters: - * In: - * priv - The adapter to be set - * Out: - * none - * - * Return Value: None. - */ -void card_set_rspinf(struct vnt_private *priv, u8 bb_type) -{ - union vnt_phy_field_swap phy; - unsigned char byTxRate, byRsvTime; /* For OFDM */ - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - /* Set to Page1 */ - VT6655_MAC_SELECT_PAGE1(priv->port_offset); - - /* RSPINF_b_1 */ - vnt_get_phy_field(priv, 14, - CARDwGetCCKControlRate(priv, RATE_1M), - PK_TYPE_11B, &phy.field_read); - - /* swap over to get correct write order */ - swap(phy.swap[0], phy.swap[1]); - - iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_1); - - /* RSPINF_b_2 */ - vnt_get_phy_field(priv, 14, - CARDwGetCCKControlRate(priv, RATE_2M), - PK_TYPE_11B, &phy.field_read); - - swap(phy.swap[0], phy.swap[1]); - - iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_2); - - /* RSPINF_b_5 */ - vnt_get_phy_field(priv, 14, - CARDwGetCCKControlRate(priv, RATE_5M), - PK_TYPE_11B, &phy.field_read); - - swap(phy.swap[0], phy.swap[1]); - - iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_5); - - /* RSPINF_b_11 */ - vnt_get_phy_field(priv, 14, - CARDwGetCCKControlRate(priv, RATE_11M), - PK_TYPE_11B, &phy.field_read); - - swap(phy.swap[0], phy.swap[1]); - - iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_11); - - /* RSPINF_a_6 */ - calculate_ofdmr_parameter(RATE_6M, - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_6); - /* RSPINF_a_9 */ - calculate_ofdmr_parameter(RATE_9M, - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_9); - /* RSPINF_a_12 */ - calculate_ofdmr_parameter(RATE_12M, - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_12); - /* RSPINF_a_18 */ - calculate_ofdmr_parameter(RATE_18M, - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_18); - /* RSPINF_a_24 */ - calculate_ofdmr_parameter(RATE_24M, - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_24); - /* RSPINF_a_36 */ - calculate_ofdmr_parameter(CARDwGetOFDMControlRate((void *)priv, - RATE_36M), - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_36); - /* RSPINF_a_48 */ - calculate_ofdmr_parameter(CARDwGetOFDMControlRate((void *)priv, - RATE_48M), - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_48); - /* RSPINF_a_54 */ - calculate_ofdmr_parameter(CARDwGetOFDMControlRate((void *)priv, - RATE_54M), - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_54); - /* RSPINF_a_72 */ - calculate_ofdmr_parameter(CARDwGetOFDMControlRate((void *)priv, - RATE_54M), - bb_type, - &byTxRate, - &byRsvTime); - iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_72); - /* Set to Page0 */ - VT6655_MAC_SELECT_PAGE0(priv->port_offset); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -void CARDvUpdateBasicTopRate(struct vnt_private *priv) -{ - unsigned char byTopOFDM = RATE_24M, byTopCCK = RATE_1M; - unsigned char ii; - - /* Determines the highest basic rate. */ - for (ii = RATE_54M; ii >= RATE_6M; ii--) { - if ((priv->basic_rates) & ((u32)(1 << ii))) { - byTopOFDM = ii; - break; - } - } - priv->byTopOFDMBasicRate = byTopOFDM; - - for (ii = RATE_11M;; ii--) { - if ((priv->basic_rates) & ((u32)(1 << ii))) { - byTopCCK = ii; - break; - } - if (ii == RATE_1M) - break; - } - priv->byTopCCKBasicRate = byTopCCK; -} - -bool CARDbIsOFDMinBasicRate(struct vnt_private *priv) -{ - int ii; - - for (ii = RATE_54M; ii >= RATE_6M; ii--) { - if ((priv->basic_rates) & ((u32)BIT(ii))) - return true; - } - return false; -} - -unsigned char card_get_pkt_type(struct vnt_private *priv) -{ - if (priv->byBBType == BB_TYPE_11A || priv->byBBType == BB_TYPE_11B) - return (unsigned char)priv->byBBType; - else if (CARDbIsOFDMinBasicRate((void *)priv)) - return PK_TYPE_11GA; - else - return PK_TYPE_11GB; -} - -/* - * Description: Calculate TSF offset of two TSF input - * Get TSF Offset from RxBCN's TSF and local TSF - * - * Parameters: - * In: - * priv - The adapter to be sync. - * qwTSF1 - Rx BCN's TSF - * qwTSF2 - Local TSF - * Out: - * none - * - * Return Value: TSF Offset value - */ -u64 card_get_tsf_offset(unsigned char rx_rate, u64 qwTSF1, u64 qwTSF2) -{ - unsigned short wRxBcnTSFOffst; - - wRxBcnTSFOffst = rx_bcn_tsf_off[rx_rate % MAX_RATE]; - - qwTSF2 += (u64)wRxBcnTSFOffst; - - return qwTSF1 - qwTSF2; -} - -/* - * Description: Read NIC TSF counter - * Get local TSF counter - * - * Parameters: - * In: - * priv - The adapter to be read - * Out: - * none - * - * Return Value: Current TSF counter - */ -u64 vt6655_get_current_tsf(struct vnt_private *priv) -{ - void __iomem *iobase = priv->port_offset; - unsigned short ww; - unsigned char data; - u32 low, high; - - vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TSFCNTRRD); - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - data = ioread8(iobase + MAC_REG_TFTCTL); - if (!(data & TFTCTL_TSFCNTRRD)) - break; - } - if (ww == W_MAX_TIMEOUT) - return 0; - low = ioread32(iobase + MAC_REG_TSFCNTR); - high = ioread32(iobase + MAC_REG_TSFCNTR + 4); - return le64_to_cpu(low + ((u64)high << 32)); -} - -/* - * Description: Read NIC TSF counter - * Get NEXTTBTT from adjusted TSF and Beacon Interval - * - * Parameters: - * In: - * qwTSF - Current TSF counter - * wbeaconInterval - Beacon Interval - * Out: - * qwCurrTSF - Current TSF counter - * - * Return Value: TSF value of next Beacon - */ -u64 card_get_next_tbtt(u64 qwTSF, unsigned short beacon_interval) -{ - u32 beacon_int; - - beacon_int = beacon_interval * 1024; - if (beacon_int) { - do_div(qwTSF, beacon_int); - qwTSF += 1; - qwTSF *= beacon_int; - } - - return qwTSF; -} - -/* - * Description: Set NIC TSF counter for first Beacon time - * Get NEXTTBTT from adjusted TSF and Beacon Interval - * - * Parameters: - * In: - * iobase - IO Base - * beacon_interval - Beacon Interval - * Out: - * none - * - * Return Value: none - */ -void CARDvSetFirstNextTBTT(struct vnt_private *priv, - unsigned short beacon_interval) -{ - void __iomem *iobase = priv->port_offset; - u64 next_tbtt; - - next_tbtt = vt6655_get_current_tsf(priv); /* Get Local TSF counter */ - - next_tbtt = card_get_next_tbtt(next_tbtt, beacon_interval); - /* Set NextTBTT */ - next_tbtt = le64_to_cpu(next_tbtt); - iowrite32((u32)next_tbtt, iobase + MAC_REG_NEXTTBTT); - iowrite32((u32)(next_tbtt >> 32), iobase + MAC_REG_NEXTTBTT + 4); - vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); -} - -/* - * Description: Sync NIC TSF counter for Beacon time - * Get NEXTTBTT and write to HW - * - * Parameters: - * In: - * priv - The adapter to be set - * qwTSF - Current TSF counter - * beacon_interval - Beacon Interval - * Out: - * none - * - * Return Value: none - */ -void CARDvUpdateNextTBTT(struct vnt_private *priv, u64 qwTSF, - unsigned short beacon_interval) -{ - void __iomem *iobase = priv->port_offset; - - qwTSF = card_get_next_tbtt(qwTSF, beacon_interval); - /* Set NextTBTT */ - qwTSF = le64_to_cpu(qwTSF); - iowrite32((u32)qwTSF, iobase + MAC_REG_NEXTTBTT); - iowrite32((u32)(qwTSF >> 32), iobase + MAC_REG_NEXTTBTT + 4); - vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); - pr_debug("Card:Update Next TBTT[%8llx]\n", qwTSF); -} diff --git a/drivers/staging/vt6655/card.h b/drivers/staging/vt6655/card.h deleted file mode 100644 index f6b462ebca51c..0000000000000 --- a/drivers/staging/vt6655/card.h +++ /dev/null @@ -1,62 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Provide functions to setup NIC operation mode - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - */ - -#ifndef __CARD_H__ -#define __CARD_H__ - -#include -#include - -/* - * Loopback mode - * - * LOBYTE is MAC LB mode, HIBYTE is MII LB mode - */ -#define CARD_LB_NONE MAKEWORD(MAC_LB_NONE, 0) -/* PHY must ISO, avoid MAC loopback packet go out */ -#define CARD_LB_MAC MAKEWORD(MAC_LB_INTERNAL, 0) -#define CARD_LB_PHY MAKEWORD(MAC_LB_EXT, 0) - -#define DEFAULT_MSDU_LIFETIME 512 /* ms */ -#define DEFAULT_MSDU_LIFETIME_RES_64us 8000 /* 64us */ - -#define DEFAULT_MGN_LIFETIME 8 /* ms */ -#define DEFAULT_MGN_LIFETIME_RES_64us 125 /* 64us */ - -#define CB_MAX_CHANNEL_24G 14 -#define CB_MAX_CHANNEL_5G 42 -#define CB_MAX_CHANNEL (CB_MAX_CHANNEL_24G + CB_MAX_CHANNEL_5G) - -struct vnt_private; - -void card_set_rspinf(struct vnt_private *priv, u8 bb_type); -void CARDvUpdateBasicTopRate(struct vnt_private *priv); -bool CARDbIsOFDMinBasicRate(struct vnt_private *priv); -void CARDvSetFirstNextTBTT(struct vnt_private *priv, - unsigned short beacon_interval); -void CARDvUpdateNextTBTT(struct vnt_private *priv, u64 qwTSF, - unsigned short beacon_interval); -u64 vt6655_get_current_tsf(struct vnt_private *priv); -u64 card_get_next_tbtt(u64 qwTSF, unsigned short beacon_interval); -u64 card_get_tsf_offset(unsigned char rx_rate, u64 qwTSF1, u64 qwTSF2); -unsigned char card_get_pkt_type(struct vnt_private *priv); -void card_safe_reset_tx(struct vnt_private *priv); -void CARDvSafeResetRx(struct vnt_private *priv); -void card_radio_power_off(struct vnt_private *priv); -bool card_set_phy_parameter(struct vnt_private *priv, u8 bb_type); -bool card_update_tsf(struct vnt_private *priv, unsigned char rx_rate, - u64 bss_timestamp); -bool card_set_beacon_period(struct vnt_private *priv, - unsigned short beacon_interval); - -#endif /* __CARD_H__ */ diff --git a/drivers/staging/vt6655/channel.c b/drivers/staging/vt6655/channel.c deleted file mode 100644 index 771c1364b0f08..0000000000000 --- a/drivers/staging/vt6655/channel.c +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - */ - -#include "baseband.h" -#include "channel.h" -#include "device.h" -#include "rf.h" - -static struct ieee80211_rate vnt_rates_bg[] = { - { .bitrate = 10, .hw_value = RATE_1M }, - { .bitrate = 20, .hw_value = RATE_2M }, - { .bitrate = 55, .hw_value = RATE_5M }, - { .bitrate = 110, .hw_value = RATE_11M }, - { .bitrate = 60, .hw_value = RATE_6M }, - { .bitrate = 90, .hw_value = RATE_9M }, - { .bitrate = 120, .hw_value = RATE_12M }, - { .bitrate = 180, .hw_value = RATE_18M }, - { .bitrate = 240, .hw_value = RATE_24M }, - { .bitrate = 360, .hw_value = RATE_36M }, - { .bitrate = 480, .hw_value = RATE_48M }, - { .bitrate = 540, .hw_value = RATE_54M }, -}; - -static struct ieee80211_channel vnt_channels_2ghz[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 } -}; - -static struct ieee80211_supported_band vnt_supported_2ghz_band = { - .channels = vnt_channels_2ghz, - .n_channels = ARRAY_SIZE(vnt_channels_2ghz), - .bitrates = vnt_rates_bg, - .n_bitrates = ARRAY_SIZE(vnt_rates_bg), -}; - -static void vnt_init_band(struct vnt_private *priv, - struct ieee80211_supported_band *supported_band, - enum nl80211_band band) -{ - int i; - - for (i = 0; i < supported_band->n_channels; i++) { - supported_band->channels[i].max_power = 0x3f; - supported_band->channels[i].flags = - IEEE80211_CHAN_NO_HT40; - } - - priv->hw->wiphy->bands[band] = supported_band; -} - -void vnt_init_bands(struct vnt_private *priv) -{ - vnt_init_band(priv, &vnt_supported_2ghz_band, NL80211_BAND_2GHZ); -} - -/** - * set_channel() - Set NIC media channel - * - * @priv: The adapter to be set - * @ch: Channel to be set - * - * Return Value: true if succeeded; false if failed. - * - */ -bool set_channel(struct vnt_private *priv, struct ieee80211_channel *ch) -{ - bool ret = true; - - if (priv->current_ch == ch->hw_value) - return ret; - - /* Set VGA to max sensitivity */ - if (priv->update_bbvga && - priv->bbvga_current != priv->bbvga[0]) { - priv->bbvga_current = priv->bbvga[0]; - - bb_set_vga_gain_offset(priv, priv->bbvga_current); - } - - /* clear NAV */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_MACCR, MACCR_CLRNAV); - - /* TX_PE will reserve 3 us for MAX2829 A mode only, - * it is for better TX throughput - */ - - priv->current_ch = ch->hw_value; - ret &= RFbSelectChannel(priv, priv->rf_type, - ch->hw_value); - - /* Init Synthesizer Table */ - if (priv->bEnablePSMode) - rf_write_wake_prog_syn(priv, priv->rf_type, ch->hw_value); - - bb_software_reset(priv); - - if (priv->local_id > REV_ID_VT3253_B1) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - /* set HW default power register */ - VT6655_MAC_SELECT_PAGE1(priv->port_offset); - RFbSetPower(priv, RATE_1M, priv->current_ch); - iowrite8(priv->cur_pwr, priv->port_offset + MAC_REG_PWRCCK); - RFbSetPower(priv, RATE_6M, priv->current_ch); - iowrite8(priv->cur_pwr, priv->port_offset + MAC_REG_PWROFDM); - VT6655_MAC_SELECT_PAGE0(priv->port_offset); - - spin_unlock_irqrestore(&priv->lock, flags); - } - - if (priv->byBBType == BB_TYPE_11B) - RFbSetPower(priv, RATE_1M, priv->current_ch); - else - RFbSetPower(priv, RATE_6M, priv->current_ch); - - return ret; -} diff --git a/drivers/staging/vt6655/channel.h b/drivers/staging/vt6655/channel.h deleted file mode 100644 index 78b2d82317e52..0000000000000 --- a/drivers/staging/vt6655/channel.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - */ - -#ifndef _CHANNEL_H_ -#define _CHANNEL_H_ - -#include "card.h" - -void vnt_init_bands(struct vnt_private *priv); - -bool set_channel(struct vnt_private *priv, struct ieee80211_channel *ch); - -#endif /* _CHANNEL_H_ */ diff --git a/drivers/staging/vt6655/desc.h b/drivers/staging/vt6655/desc.h deleted file mode 100644 index 17a40c53b8ffd..0000000000000 --- a/drivers/staging/vt6655/desc.h +++ /dev/null @@ -1,249 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose:The header file of descriptor - * - * Revision History: - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - */ - -#ifndef __DESC_H__ -#define __DESC_H__ - -#include -#include -#include "linux/ieee80211.h" - -#define B_OWNED_BY_CHIP 1 -#define B_OWNED_BY_HOST 0 - -/* Bits in the RSR register */ -#define RSR_ADDRBROAD 0x80 -#define RSR_ADDRMULTI 0x40 -#define RSR_ADDRUNI 0x00 -#define RSR_IVLDTYP 0x20 -#define RSR_IVLDLEN 0x10 /* invalid len (> 2312 byte) */ -#define RSR_BSSIDOK 0x08 -#define RSR_CRCOK 0x04 -#define RSR_BCNSSIDOK 0x02 -#define RSR_ADDROK 0x01 - -/* Bits in the new RSR register */ -#define NEWRSR_DECRYPTOK 0x10 -#define NEWRSR_CFPIND 0x08 -#define NEWRSR_HWUTSF 0x04 -#define NEWRSR_BCNHITAID 0x02 -#define NEWRSR_BCNHITAID0 0x01 - -/* Bits in the TSR0 register */ -#define TSR0_PWRSTS1_2 0xC0 -#define TSR0_PWRSTS7 0x20 -#define TSR0_NCR 0x1F - -/* Bits in the TSR1 register */ -#define TSR1_TERR 0x80 -#define TSR1_PWRSTS4_6 0x70 -#define TSR1_RETRYTMO 0x08 -#define TSR1_TMO 0x04 -#define TSR1_PWRSTS3 0x02 -#define ACK_DATA 0x01 - -/* Bits in the TCR register */ -#define EDMSDU 0x04 /* end of sdu */ -#define TCR_EDP 0x02 /* end of packet */ -#define TCR_STP 0x01 /* start of packet */ - -/* max transmit or receive buffer size */ -#define CB_MAX_BUF_SIZE 2900U - /* NOTE: must be multiple of 4 */ -#define CB_MAX_TX_BUF_SIZE CB_MAX_BUF_SIZE -#define CB_MAX_RX_BUF_SIZE_NORMAL CB_MAX_BUF_SIZE - -#define CB_BEACON_BUF_SIZE 512U - -#define CB_MAX_RX_DESC 128 -#define CB_MIN_RX_DESC 16 -#define CB_MAX_TX_DESC 64 -#define CB_MIN_TX_DESC 16 - -#define CB_MAX_RECEIVED_PACKETS 16 - /* - * limit our receive routine to indicating - * this many at a time for 2 reasons: - * 1. driver flow control to protocol layer - * 2. limit the time used in ISR routine - */ - -#define CB_EXTRA_RD_NUM 32 -#define CB_RD_NUM 32 -#define CB_TD_NUM 32 - -/* - * max number of physical segments in a single NDIS packet. Above this - * threshold, the packet is copied into a single physically contiguous buffer - */ -#define CB_MAX_SEGMENT 4 - -#define CB_MIN_MAP_REG_NUM 4 -#define CB_MAX_MAP_REG_NUM CB_MAX_TX_DESC - -#define CB_PROTOCOL_RESERVED_SECTION 16 - -/* - * if retrys excess 15 times , tx will abort, and if tx fifo underflow, - * tx will fail, we should try to resend it - */ -#define CB_MAX_TX_ABORT_RETRY 3 - -/* WMAC definition FIFO Control */ -#define FIFOCTL_AUTO_FB_1 0x1000 -#define FIFOCTL_AUTO_FB_0 0x0800 -#define FIFOCTL_GRPACK 0x0400 -#define FIFOCTL_11GA 0x0300 -#define FIFOCTL_11GB 0x0200 -#define FIFOCTL_11B 0x0100 -#define FIFOCTL_11A 0x0000 -#define FIFOCTL_RTS 0x0080 -#define FIFOCTL_ISDMA0 0x0040 -#define FIFOCTL_GENINT 0x0020 -#define FIFOCTL_TMOEN 0x0010 -#define FIFOCTL_LRETRY 0x0008 -#define FIFOCTL_CRCDIS 0x0004 -#define FIFOCTL_NEEDACK 0x0002 -#define FIFOCTL_LHEAD 0x0001 - -/* WMAC definition Frag Control */ -#define FRAGCTL_AES 0x0300 -#define FRAGCTL_TKIP 0x0200 -#define FRAGCTL_LEGACY 0x0100 -#define FRAGCTL_NONENCRYPT 0x0000 -#define FRAGCTL_ENDFRAG 0x0003 -#define FRAGCTL_MIDFRAG 0x0002 -#define FRAGCTL_STAFRAG 0x0001 -#define FRAGCTL_NONFRAG 0x0000 - -#define TYPE_TXDMA0 0 -#define TYPE_AC0DMA 1 -#define TYPE_ATIMDMA 2 -#define TYPE_SYNCDMA 3 -#define TYPE_MAXTD 2 - -#define TYPE_BEACONDMA 4 - -#define TYPE_RXDMA0 0 -#define TYPE_RXDMA1 1 -#define TYPE_MAXRD 2 - -/* TD_INFO flags control bit */ -#define TD_FLAGS_NETIF_SKB 0x01 /* check if need release skb */ -/* check if called from private skb (hostap) */ -#define TD_FLAGS_PRIV_SKB 0x02 -#define TD_FLAGS_PS_RETRY 0x04 /* check if PS STA frame re-transmit */ - -/* - * ref_sk_buff is used for mapping the skb structure between pre-built - * driver-obj & running kernel. Since different kernel version (2.4x) may - * change skb structure, i.e. pre-built driver-obj may link to older skb that - * leads error. - */ - -struct vnt_rd_info { - struct sk_buff *skb; - dma_addr_t skb_dma; -}; - -struct vnt_rdes0 { - volatile __le16 res_count; -#ifdef __BIG_ENDIAN - union { - volatile u16 f15_reserved; - struct { - volatile u8 f8_reserved1; - volatile u8 owner:1; - volatile u8 f7_reserved:7; - } __packed; - } __packed; -#else - u16 f15_reserved:15; - u16 owner:1; -#endif -} __packed; - -struct vnt_rdes1 { - __le16 req_count; - u16 reserved; -} __packed; - -/* Rx descriptor*/ -struct vnt_rx_desc { - volatile struct vnt_rdes0 rd0; - volatile struct vnt_rdes1 rd1; - volatile __le32 buff_addr; - volatile __le32 next_desc; - struct vnt_rx_desc *next __aligned(8); - struct vnt_rd_info *rd_info __aligned(8); -} __packed; - -struct vnt_tdes0 { - volatile u8 tsr0; - volatile u8 tsr1; -#ifdef __BIG_ENDIAN - union { - volatile u16 f15_txtime; - struct { - volatile u8 f8_reserved; - volatile u8 owner:1; - volatile u8 f7_reserved:7; - } __packed; - } __packed; -#else - volatile u16 f15_txtime:15; - volatile u16 owner:1; -#endif -} __packed; - -struct vnt_tdes1 { - volatile __le16 req_count; - volatile u8 tcr; - volatile u8 reserved; -} __packed; - -struct vnt_td_info { - void *mic_hdr; - struct sk_buff *skb; - unsigned char *buf; - dma_addr_t buf_dma; - u16 req_count; - u8 flags; -}; - -/* transmit descriptor */ -struct vnt_tx_desc { - volatile struct vnt_tdes0 td0; - volatile struct vnt_tdes1 td1; - volatile __le32 buff_addr; - volatile __le32 next_desc; - struct vnt_tx_desc *next __aligned(8); - struct vnt_td_info *td_info __aligned(8); -} __packed; - -/* Length, Service, and Signal fields of Phy for Tx */ -struct vnt_phy_field { - u8 signal; - u8 service; - __le16 len; -} __packed; - -union vnt_phy_field_swap { - struct vnt_phy_field field_read; - u16 swap[2]; - u32 field_write; -}; - -#endif /* __DESC_H__ */ diff --git a/drivers/staging/vt6655/device.h b/drivers/staging/vt6655/device.h deleted file mode 100644 index 5eaab6b172d37..0000000000000 --- a/drivers/staging/vt6655/device.h +++ /dev/null @@ -1,292 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: MAC Data structure - * - * Author: Tevin Chen - * - * Date: Mar 17, 1997 - * - */ - -#ifndef __DEVICE_H__ -#define __DEVICE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* device specific */ - -#include "device_cfg.h" -#include "card.h" -#include "srom.h" -#include "desc.h" -#include "key.h" -#include "mac.h" - -/*--------------------- Export Definitions -------------------------*/ - -#define RATE_1M 0 -#define RATE_2M 1 -#define RATE_5M 2 -#define RATE_11M 3 -#define RATE_6M 4 -#define RATE_9M 5 -#define RATE_12M 6 -#define RATE_18M 7 -#define RATE_24M 8 -#define RATE_36M 9 -#define RATE_48M 10 -#define RATE_54M 11 -#define MAX_RATE 12 - -#define AUTO_FB_NONE 0 -#define AUTO_FB_0 1 -#define AUTO_FB_1 2 - -#define FB_RATE0 0 -#define FB_RATE1 1 - -/* Antenna Mode */ -#define ANT_A 0 -#define ANT_B 1 -#define ANT_DIVERSITY 2 -#define ANT_RXD_TXA 3 -#define ANT_RXD_TXB 4 -#define ANT_UNKNOWN 0xFF - -#define BB_VGA_LEVEL 4 -#define BB_VGA_CHANGE_THRESHOLD 16 - -#define MAKE_BEACON_RESERVED 10 /* (us) */ - -/* BUILD OBJ mode */ - -#define AVAIL_TD(p, q) ((p)->opts.tx_descs[(q)] - ((p)->iTDUsed[(q)])) - -/* 0:11A 1:11B 2:11G */ -#define BB_TYPE_11A 0 -#define BB_TYPE_11B 1 -#define BB_TYPE_11G 2 - -/* 0:11a, 1:11b, 2:11gb (only CCK in BasicRate), 3:11ga (OFDM in BasicRate) */ -#define PK_TYPE_11A 0 -#define PK_TYPE_11B 1 -#define PK_TYPE_11GB 2 -#define PK_TYPE_11GA 3 - -#define OWNED_BY_HOST 0 -#define OWNED_BY_NIC 1 - -struct vnt_options { - int rx_descs0; /* Number of RX descriptors0 */ - int rx_descs1; /* Number of RX descriptors1 */ - int tx_descs[2]; /* Number of TX descriptors 0, 1 */ - int int_works; /* interrupt limits */ - int short_retry; - int long_retry; - int bbp_type; - u32 flags; -}; - -struct vnt_private { - struct pci_dev *pcid; - /* mac80211 */ - struct ieee80211_hw *hw; - struct ieee80211_vif *vif; - unsigned long key_entry_inuse; - u32 basic_rates; - u16 current_aid; - int mc_list_count; - u8 mac_hw; - -/* dma addr, rx/tx pool */ - dma_addr_t pool_dma; - dma_addr_t rd0_pool_dma; - dma_addr_t rd1_pool_dma; - - dma_addr_t td0_pool_dma; - dma_addr_t td1_pool_dma; - - dma_addr_t tx_bufs_dma0; - dma_addr_t tx_bufs_dma1; - dma_addr_t tx_beacon_dma; - - unsigned char *tx0_bufs; - unsigned char *tx1_bufs; - unsigned char *tx_beacon_bufs; - - void __iomem *port_offset; - u32 memaddr; - u32 ioaddr; - - spinlock_t lock; - - volatile int iTDUsed[TYPE_MAXTD]; - - struct vnt_tx_desc *apCurrTD[TYPE_MAXTD]; - struct vnt_tx_desc *tail_td[TYPE_MAXTD]; - - struct vnt_tx_desc *ap_td0_rings; - struct vnt_tx_desc *ap_td1_rings; - - struct vnt_rx_desc *aRD0Ring; - struct vnt_rx_desc *aRD1Ring; - struct vnt_rx_desc *pCurrRD[TYPE_MAXRD]; - - struct vnt_options opts; - - u32 flags; - - u32 rx_buf_sz; - u8 rx_rate; - - u32 rx_bytes; - - /* Version control */ - unsigned char local_id; - unsigned char rf_type; - - unsigned char max_pwr_level; - unsigned char byZoneType; - bool bZoneRegExist; - unsigned char byOriginalZonetype; - - unsigned char abyCurrentNetAddr[ETH_ALEN]; __aligned(2) - bool bLinkPass; /* link status: OK or fail */ - - unsigned int current_rssi; - unsigned char byCurrSQ; - - unsigned long dwTxAntennaSel; - unsigned long dwRxAntennaSel; - unsigned char byAntennaCount; - unsigned char byRxAntennaMode; - unsigned char byTxAntennaMode; - bool bTxRxAntInv; - - unsigned char *pbyTmpBuff; - unsigned int uSIFS; /* Current SIFS */ - unsigned int uDIFS; /* Current DIFS */ - unsigned int uEIFS; /* Current EIFS */ - unsigned int uSlot; /* Current SlotTime */ - unsigned int uCwMin; /* Current CwMin */ - unsigned int uCwMax; /* CwMax is fixed on 1023. */ - /* PHY parameter */ - unsigned char sifs; - unsigned char difs; - unsigned char eifs; - unsigned char slot; - unsigned char cw_max_min; - - u8 byBBType; /* 0:11A, 1:11B, 2:11G */ - u8 packet_type; /* - * 0:11a,1:11b,2:11gb (only CCK - * in BasicRate), 3:11ga (OFDM in - * Basic Rate) - */ - unsigned short wBasicRate; - unsigned char byACKRate; - unsigned char byTopOFDMBasicRate; - unsigned char byTopCCKBasicRate; - - unsigned char byMinChannel; - unsigned char byMaxChannel; - - unsigned char preamble_type; - unsigned char byShortPreamble; - - unsigned short wCurrentRate; - unsigned char byShortRetryLimit; - unsigned char byLongRetryLimit; - enum nl80211_iftype op_mode; - bool bBSSIDFilter; - unsigned short wMaxTransmitMSDULifetime; - - bool bEncryptionEnable; - bool bLongHeader; - bool short_slot_time; - bool bProtectMode; - bool bNonERPPresent; - bool bBarkerPreambleMd; - - bool bRadioControlOff; - bool radio_off; - bool bEnablePSMode; - unsigned short wListenInterval; - bool bPWBitOn; - - /* GPIO Radio Control */ - unsigned char byRadioCtl; - unsigned char byGPIO; - bool hw_radio_off; - bool bPrvActive4RadioOFF; - bool bGPIOBlockRead; - - /* Beacon related */ - unsigned short wSeqCounter; - unsigned short wBCNBufLen; - bool bBeaconBufReady; - bool bBeaconSent; - bool bIsBeaconBufReadySet; - unsigned int cbBeaconBufReadySetCnt; - bool bFixRate; - u16 current_ch; - - bool bAES; - - unsigned char byAutoFBCtrl; - - /* For Update BaseBand VGA Gain Offset */ - bool update_bbvga; - unsigned int uBBVGADiffCount; - unsigned char bbvga_new; - unsigned char bbvga_current; - unsigned char bbvga[BB_VGA_LEVEL]; - long dbm_threshold[BB_VGA_LEVEL]; - - unsigned char bb_pre_edrssi; - unsigned char byBBPreEDIndex; - - unsigned long dwDiagRefCount; - - /* For FOE Tuning */ - unsigned char byFOETuning; - - /* For RF Power table */ - unsigned char byCCKPwr; - unsigned char byOFDMPwrG; - unsigned char cur_pwr; - char byCurPwrdBm; - unsigned char abyCCKPwrTbl[CB_MAX_CHANNEL_24G + 1]; - unsigned char abyOFDMPwrTbl[CB_MAX_CHANNEL + 1]; - char abyCCKDefaultPwr[CB_MAX_CHANNEL_24G + 1]; - char abyOFDMDefaultPwr[CB_MAX_CHANNEL + 1]; - char abyRegPwr[CB_MAX_CHANNEL + 1]; - char abyLocalPwr[CB_MAX_CHANNEL + 1]; - - /* BaseBand Loopback Use */ - unsigned char byBBCR4d; - unsigned char byBBCRc9; - unsigned char byBBCR88; - unsigned char byBBCR09; - - unsigned char abyEEPROM[EEP_MAX_CONTEXT_SIZE]; /* unsigned long alignment */ - - unsigned short beacon_interval; - u16 wake_up_count; - - struct work_struct interrupt_work; - - struct ieee80211_low_level_stats low_stats; -}; - -#endif diff --git a/drivers/staging/vt6655/device_cfg.h b/drivers/staging/vt6655/device_cfg.h deleted file mode 100644 index 2d647a3619bad..0000000000000 --- a/drivers/staging/vt6655/device_cfg.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Driver configuration header - * Author: Lyndon Chen - * - * Date: Dec 17, 2002 - * - */ - -#ifndef __DEVICE_CONFIG_H -#define __DEVICE_CONFIG_H - -#include - -#define VID_TABLE_SIZE 64 -#define MCAST_TABLE_SIZE 64 -#define MCAM_SIZE 32 -#define VCAM_SIZE 32 -#define TX_QUEUE_NO 8 - -#define DEVICE_NAME "vt6655" -#define DEVICE_FULL_DRV_NAM "VIA Networking Solomon-A/B/G Wireless LAN Adapter Driver" - -#ifndef MAJOR_VERSION -#define MAJOR_VERSION 1 -#endif - -#ifndef MINOR_VERSION -#define MINOR_VERSION 17 -#endif - -#ifndef DEVICE_VERSION -#define DEVICE_VERSION "1.19.12" -#endif - -#include -#include - -#define PKT_BUF_SZ 2390 - -#endif diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c deleted file mode 100644 index bf3ecf7202066..0000000000000 --- a/drivers/staging/vt6655/device_main.c +++ /dev/null @@ -1,1868 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: driver entry for initial, open, close, tx and rx. - * - * Author: Lyndon Chen - * - * Date: Jan 8, 2003 - * - * Functions: - * - * vt6655_probe - module initial (insmod) driver entry - * vt6655_remove - module remove entry - * device_free_info - device structure resource free function - * device_print_info - print out resource - * device_rx_srv - rx service function - * device_alloc_rx_buf - rx buffer pre-allocated function - * device_free_rx_buf - free rx buffer function - * device_free_tx_buf - free tx buffer function - * device_init_rd0_ring - initial rd dma0 ring - * device_init_rd1_ring - initial rd dma1 ring - * device_init_td0_ring - initial tx dma0 ring buffer - * device_init_td1_ring - initial tx dma1 ring buffer - * device_init_registers - initial MAC & BBP & RF internal registers. - * device_init_rings - initial tx/rx ring buffer - * device_free_rings - free all allocated ring buffer - * device_tx_srv - tx interrupt service function - * - * Revision History: - */ - -#include -#include "device.h" -#include "card.h" -#include "channel.h" -#include "baseband.h" -#include "mac.h" -#include "power.h" -#include "rxtx.h" -#include "dpc.h" -#include "rf.h" -#include -#include -#include - -/*--------------------- Static Definitions -------------------------*/ -/* - * Define module options - */ -MODULE_AUTHOR("VIA Networking Technologies, Inc., "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("VIA Networking Solomon-A/B/G Wireless LAN Adapter Driver"); - -#define DEVICE_PARAM(N, D) - -#define RX_DESC_MIN0 16 -#define RX_DESC_MAX0 128 -#define RX_DESC_DEF0 32 -DEVICE_PARAM(RxDescriptors0, "Number of receive descriptors0"); - -#define RX_DESC_MIN1 16 -#define RX_DESC_MAX1 128 -#define RX_DESC_DEF1 32 -DEVICE_PARAM(RxDescriptors1, "Number of receive descriptors1"); - -#define TX_DESC_MIN0 16 -#define TX_DESC_MAX0 128 -#define TX_DESC_DEF0 32 -DEVICE_PARAM(TxDescriptors0, "Number of transmit descriptors0"); - -#define TX_DESC_MIN1 16 -#define TX_DESC_MAX1 128 -#define TX_DESC_DEF1 64 -DEVICE_PARAM(TxDescriptors1, "Number of transmit descriptors1"); - -#define INT_WORKS_DEF 20 -#define INT_WORKS_MIN 10 -#define INT_WORKS_MAX 64 - -DEVICE_PARAM(int_works, "Number of packets per interrupt services"); - -#define RTS_THRESH_DEF 2347 - -#define FRAG_THRESH_DEF 2346 - -#define SHORT_RETRY_MIN 0 -#define SHORT_RETRY_MAX 31 -#define SHORT_RETRY_DEF 8 - -DEVICE_PARAM(ShortRetryLimit, "Short frame retry limits"); - -#define LONG_RETRY_MIN 0 -#define LONG_RETRY_MAX 15 -#define LONG_RETRY_DEF 4 - -DEVICE_PARAM(LongRetryLimit, "long frame retry limits"); - -/* BasebandType[] baseband type selected - * 0: indicate 802.11a type - * 1: indicate 802.11b type - * 2: indicate 802.11g type - */ -#define BBP_TYPE_MIN 0 -#define BBP_TYPE_MAX 2 -#define BBP_TYPE_DEF 2 - -DEVICE_PARAM(BasebandType, "baseband type"); - -/* - * Static vars definitions - */ -static const struct pci_device_id vt6655_pci_id_table[] = { - { PCI_VDEVICE(VIA, 0x3253) }, - { 0, } -}; - -/*--------------------- Static Functions --------------------------*/ - -static int vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent); -static void device_free_info(struct vnt_private *priv); -static void device_print_info(struct vnt_private *priv); - -static void vt6655_mac_write_bssid_addr(void __iomem *iobase, const u8 *mac_addr); -static void vt6655_mac_read_ether_addr(void __iomem *iobase, u8 *mac_addr); - -static int device_init_rd0_ring(struct vnt_private *priv); -static int device_init_rd1_ring(struct vnt_private *priv); -static int device_init_td0_ring(struct vnt_private *priv); -static int device_init_td1_ring(struct vnt_private *priv); - -static int device_rx_srv(struct vnt_private *priv, unsigned int idx); -static int device_tx_srv(struct vnt_private *priv, unsigned int idx); -static bool device_alloc_rx_buf(struct vnt_private *, struct vnt_rx_desc *); -static void device_free_rx_buf(struct vnt_private *priv, - struct vnt_rx_desc *rd); -static void device_init_registers(struct vnt_private *priv); -static void device_free_tx_buf(struct vnt_private *, struct vnt_tx_desc *); -static void device_free_td0_ring(struct vnt_private *priv); -static void device_free_td1_ring(struct vnt_private *priv); -static void device_free_rd0_ring(struct vnt_private *priv); -static void device_free_rd1_ring(struct vnt_private *priv); -static void device_free_rings(struct vnt_private *priv); - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -static void vt6655_remove(struct pci_dev *pcid) -{ - struct vnt_private *priv = pci_get_drvdata(pcid); - - if (!priv) - return; - device_free_info(priv); -} - -static void device_get_options(struct vnt_private *priv) -{ - struct vnt_options *opts = &priv->opts; - - opts->rx_descs0 = RX_DESC_DEF0; - opts->rx_descs1 = RX_DESC_DEF1; - opts->tx_descs[0] = TX_DESC_DEF0; - opts->tx_descs[1] = TX_DESC_DEF1; - opts->int_works = INT_WORKS_DEF; - - opts->short_retry = SHORT_RETRY_DEF; - opts->long_retry = LONG_RETRY_DEF; - opts->bbp_type = BBP_TYPE_DEF; -} - -static void -device_set_options(struct vnt_private *priv) -{ - priv->byShortRetryLimit = priv->opts.short_retry; - priv->byLongRetryLimit = priv->opts.long_retry; - priv->byBBType = priv->opts.bbp_type; - priv->packet_type = priv->byBBType; - priv->byAutoFBCtrl = AUTO_FB_0; - priv->update_bbvga = true; - priv->preamble_type = 0; - - pr_debug(" byShortRetryLimit= %d\n", (int)priv->byShortRetryLimit); - pr_debug(" byLongRetryLimit= %d\n", (int)priv->byLongRetryLimit); - pr_debug(" preamble_type= %d\n", (int)priv->preamble_type); - pr_debug(" byShortPreamble= %d\n", (int)priv->byShortPreamble); - pr_debug(" byBBType= %d\n", (int)priv->byBBType); -} - -static void vt6655_mac_write_bssid_addr(void __iomem *iobase, const u8 *mac_addr) -{ - iowrite8(1, iobase + MAC_REG_PAGE1SEL); - for (int i = 0; i < 6; i++) - iowrite8(mac_addr[i], iobase + MAC_REG_BSSID0 + i); - iowrite8(0, iobase + MAC_REG_PAGE1SEL); -} - -static void vt6655_mac_read_ether_addr(void __iomem *iobase, u8 *mac_addr) -{ - iowrite8(1, iobase + MAC_REG_PAGE1SEL); - for (int i = 0; i < 6; i++) - mac_addr[i] = ioread8(iobase + MAC_REG_PAR0 + i); - iowrite8(0, iobase + MAC_REG_PAGE1SEL); -} - -static void vt6655_mac_dma_ctl(void __iomem *iobase, u8 reg_index) -{ - u32 reg_value; - - reg_value = ioread32(iobase + reg_index); - if (reg_value & DMACTL_RUN) - iowrite32(DMACTL_WAKE, iobase + reg_index); - else - iowrite32(DMACTL_RUN, iobase + reg_index); -} - -static void vt6655_mac_set_bits(void __iomem *iobase, u32 mask) -{ - u32 reg_value; - - reg_value = ioread32(iobase + MAC_REG_ENCFG); - reg_value = reg_value | mask; - iowrite32(reg_value, iobase + MAC_REG_ENCFG); -} - -static void vt6655_mac_clear_bits(void __iomem *iobase, u32 mask) -{ - u32 reg_value; - - reg_value = ioread32(iobase + MAC_REG_ENCFG); - reg_value = reg_value & ~mask; - iowrite32(reg_value, iobase + MAC_REG_ENCFG); -} - -static void vt6655_mac_en_protect_md(void __iomem *iobase) -{ - vt6655_mac_set_bits(iobase, ENCFG_PROTECTMD); -} - -static void vt6655_mac_dis_protect_md(void __iomem *iobase) -{ - vt6655_mac_clear_bits(iobase, ENCFG_PROTECTMD); -} - -static void vt6655_mac_en_barker_preamble_md(void __iomem *iobase) -{ - vt6655_mac_set_bits(iobase, ENCFG_BARKERPREAM); -} - -static void vt6655_mac_dis_barker_preamble_md(void __iomem *iobase) -{ - vt6655_mac_clear_bits(iobase, ENCFG_BARKERPREAM); -} - -/* - * Initialisation of MAC & BBP registers - */ - -static void device_init_registers(struct vnt_private *priv) -{ - unsigned long flags; - unsigned int ii; - unsigned char byValue; - unsigned char byCCKPwrdBm = 0; - unsigned char byOFDMPwrdBm = 0; - - MACbShutdown(priv); - bb_software_reset(priv); - - /* Do MACbSoftwareReset in MACvInitialize */ - MACbSoftwareReset(priv); - - priv->bAES = false; - - /* Only used in 11g type, sync with ERP IE */ - priv->bProtectMode = false; - - priv->bNonERPPresent = false; - priv->bBarkerPreambleMd = false; - priv->wCurrentRate = RATE_1M; - priv->byTopOFDMBasicRate = RATE_24M; - priv->byTopCCKBasicRate = RATE_1M; - - /* init MAC */ - MACvInitialize(priv); - - /* Get Local ID */ - priv->local_id = ioread8(priv->port_offset + MAC_REG_LOCALID); - - spin_lock_irqsave(&priv->lock, flags); - - SROMvReadAllContents(priv->port_offset, priv->abyEEPROM); - - spin_unlock_irqrestore(&priv->lock, flags); - - /* Get Channel range */ - priv->byMinChannel = 1; - priv->byMaxChannel = CB_MAX_CHANNEL; - - /* Get Antena */ - byValue = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_ANTENNA); - if (byValue & EEP_ANTINV) - priv->bTxRxAntInv = true; - else - priv->bTxRxAntInv = false; - - byValue &= (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN); - /* if not set default is All */ - if (byValue == 0) - byValue = (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN); - - if (byValue == (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN)) { - priv->byAntennaCount = 2; - priv->byTxAntennaMode = ANT_B; - priv->dwTxAntennaSel = 1; - priv->dwRxAntennaSel = 1; - - if (priv->bTxRxAntInv) - priv->byRxAntennaMode = ANT_A; - else - priv->byRxAntennaMode = ANT_B; - } else { - priv->byAntennaCount = 1; - priv->dwTxAntennaSel = 0; - priv->dwRxAntennaSel = 0; - - if (byValue & EEP_ANTENNA_AUX) { - priv->byTxAntennaMode = ANT_A; - - if (priv->bTxRxAntInv) - priv->byRxAntennaMode = ANT_B; - else - priv->byRxAntennaMode = ANT_A; - } else { - priv->byTxAntennaMode = ANT_B; - - if (priv->bTxRxAntInv) - priv->byRxAntennaMode = ANT_A; - else - priv->byRxAntennaMode = ANT_B; - } - } - - /* Set initial antenna mode */ - bb_set_tx_antenna_mode(priv, priv->byTxAntennaMode); - bb_set_rx_antenna_mode(priv, priv->byRxAntennaMode); - - /* zonetype initial */ - priv->byOriginalZonetype = priv->abyEEPROM[EEP_OFS_ZONETYPE]; - - if (!priv->bZoneRegExist) - priv->byZoneType = priv->abyEEPROM[EEP_OFS_ZONETYPE]; - - pr_debug("priv->byZoneType = %x\n", priv->byZoneType); - - /* Init RF module */ - RFbInit(priv); - - /* Get Desire Power Value */ - priv->cur_pwr = 0xFF; - priv->byCCKPwr = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_PWR_CCK); - priv->byOFDMPwrG = SROMbyReadEmbedded(priv->port_offset, - EEP_OFS_PWR_OFDMG); - - /* Load power Table */ - for (ii = 0; ii < CB_MAX_CHANNEL_24G; ii++) { - priv->abyCCKPwrTbl[ii + 1] = - SROMbyReadEmbedded(priv->port_offset, - (unsigned char)(ii + EEP_OFS_CCK_PWR_TBL)); - if (priv->abyCCKPwrTbl[ii + 1] == 0) - priv->abyCCKPwrTbl[ii + 1] = priv->byCCKPwr; - - priv->abyOFDMPwrTbl[ii + 1] = - SROMbyReadEmbedded(priv->port_offset, - (unsigned char)(ii + EEP_OFS_OFDM_PWR_TBL)); - if (priv->abyOFDMPwrTbl[ii + 1] == 0) - priv->abyOFDMPwrTbl[ii + 1] = priv->byOFDMPwrG; - - priv->abyCCKDefaultPwr[ii + 1] = byCCKPwrdBm; - priv->abyOFDMDefaultPwr[ii + 1] = byOFDMPwrdBm; - } - - /* recover 12,13 ,14channel for EUROPE by 11 channel */ - for (ii = 11; ii < 14; ii++) { - priv->abyCCKPwrTbl[ii] = priv->abyCCKPwrTbl[10]; - priv->abyOFDMPwrTbl[ii] = priv->abyOFDMPwrTbl[10]; - } - - /* Load OFDM A Power Table */ - for (ii = 0; ii < CB_MAX_CHANNEL_5G; ii++) { - priv->abyOFDMPwrTbl[ii + CB_MAX_CHANNEL_24G + 1] = - SROMbyReadEmbedded(priv->port_offset, - (unsigned char)(ii + EEP_OFS_OFDMA_PWR_TBL)); - - priv->abyOFDMDefaultPwr[ii + CB_MAX_CHANNEL_24G + 1] = - SROMbyReadEmbedded(priv->port_offset, - (unsigned char)(ii + EEP_OFS_OFDMA_PWR_dBm)); - } - - if (priv->local_id > REV_ID_VT3253_B1) { - VT6655_MAC_SELECT_PAGE1(priv->port_offset); - - iowrite8(MSRCTL1_TXPWR | MSRCTL1_CSAPAREN, priv->port_offset + MAC_REG_MSRCTL + 1); - - VT6655_MAC_SELECT_PAGE0(priv->port_offset); - } - - /* use relative tx timeout and 802.11i D4 */ - vt6655_mac_word_reg_bits_on(priv->port_offset, MAC_REG_CFG, - (CFG_TKIPOPT | CFG_NOTXTIMEOUT)); - - /* set performance parameter by registry */ - vt6655_mac_set_short_retry_limit(priv, priv->byShortRetryLimit); - MACvSetLongRetryLimit(priv, priv->byLongRetryLimit); - - /* reset TSF counter */ - iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); - /* enable TSF counter */ - iowrite8(TFTCTL_TSFCNTREN, priv->port_offset + MAC_REG_TFTCTL); - - /* initialize BBP registers */ - bb_vt3253_init(priv); - - if (priv->update_bbvga) { - priv->bbvga_current = priv->bbvga[0]; - priv->bbvga_new = priv->bbvga_current; - bb_set_vga_gain_offset(priv, priv->bbvga[0]); - } - - bb_set_rx_antenna_mode(priv, priv->byRxAntennaMode); - bb_set_tx_antenna_mode(priv, priv->byTxAntennaMode); - - /* Set BB and packet type at the same time. */ - /* Set Short Slot Time, xIFS, and RSPINF. */ - priv->wCurrentRate = RATE_54M; - - priv->radio_off = false; - - priv->byRadioCtl = SROMbyReadEmbedded(priv->port_offset, - EEP_OFS_RADIOCTL); - priv->hw_radio_off = false; - - if (priv->byRadioCtl & EEP_RADIOCTL_ENABLE) { - /* Get GPIO */ - priv->byGPIO = ioread8(priv->port_offset + MAC_REG_GPIOCTL1); - - if (((priv->byGPIO & GPIO0_DATA) && - !(priv->byRadioCtl & EEP_RADIOCTL_INV)) || - (!(priv->byGPIO & GPIO0_DATA) && - (priv->byRadioCtl & EEP_RADIOCTL_INV))) - priv->hw_radio_off = true; - } - - if (priv->hw_radio_off || priv->bRadioControlOff) - card_radio_power_off(priv); - - /* get Permanent network address */ - SROMvReadEtherAddress(priv->port_offset, priv->abyCurrentNetAddr); - pr_debug("Network address = %pM\n", priv->abyCurrentNetAddr); - - /* reset Tx pointer */ - CARDvSafeResetRx(priv); - /* reset Rx pointer */ - card_safe_reset_tx(priv); - - if (priv->local_id <= REV_ID_VT3253_A1) - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_RCR, RCR_WPAERR); - - /* Turn On Rx DMA */ - vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL0); - vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL1); - - /* start the adapter */ - iowrite8(HOSTCR_MACEN | HOSTCR_RXON | HOSTCR_TXON, priv->port_offset + MAC_REG_HOSTCR); -} - -static void device_print_info(struct vnt_private *priv) -{ - dev_info(&priv->pcid->dev, "MAC=%pM IO=0x%lx Mem=0x%lx IRQ=%d\n", - priv->abyCurrentNetAddr, (unsigned long)priv->ioaddr, - (unsigned long)priv->port_offset, priv->pcid->irq); -} - -static void device_free_info(struct vnt_private *priv) -{ - if (!priv) - return; - - if (priv->mac_hw) - ieee80211_unregister_hw(priv->hw); - - if (priv->port_offset) - iounmap(priv->port_offset); - - if (priv->pcid) - pci_release_regions(priv->pcid); - - if (priv->hw) - ieee80211_free_hw(priv->hw); -} - -static bool device_init_rings(struct vnt_private *priv) -{ - void *vir_pool; - - /*allocate all RD/TD rings a single pool*/ - vir_pool = dma_alloc_coherent(&priv->pcid->dev, - priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + - priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + - priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc) + - priv->opts.tx_descs[1] * sizeof(struct vnt_tx_desc), - &priv->pool_dma, GFP_ATOMIC); - if (!vir_pool) { - dev_err(&priv->pcid->dev, "allocate desc dma memory failed\n"); - return false; - } - - priv->aRD0Ring = vir_pool; - priv->aRD1Ring = vir_pool + - priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc); - - priv->rd0_pool_dma = priv->pool_dma; - priv->rd1_pool_dma = priv->rd0_pool_dma + - priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc); - - priv->tx0_bufs = dma_alloc_coherent(&priv->pcid->dev, - priv->opts.tx_descs[0] * PKT_BUF_SZ + - priv->opts.tx_descs[1] * PKT_BUF_SZ + - CB_BEACON_BUF_SIZE + - CB_MAX_BUF_SIZE, - &priv->tx_bufs_dma0, GFP_ATOMIC); - if (!priv->tx0_bufs) { - dev_err(&priv->pcid->dev, "allocate buf dma memory failed\n"); - - dma_free_coherent(&priv->pcid->dev, - priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + - priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + - priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc) + - priv->opts.tx_descs[1] * sizeof(struct vnt_tx_desc), - vir_pool, priv->pool_dma); - return false; - } - - priv->td0_pool_dma = priv->rd1_pool_dma + - priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc); - - priv->td1_pool_dma = priv->td0_pool_dma + - priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc); - - /* vir_pool: pvoid type */ - priv->ap_td0_rings = vir_pool - + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) - + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc); - - priv->ap_td1_rings = vir_pool - + priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) - + priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) - + priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc); - - priv->tx1_bufs = priv->tx0_bufs + - priv->opts.tx_descs[0] * PKT_BUF_SZ; - - priv->tx_beacon_bufs = priv->tx1_bufs + - priv->opts.tx_descs[1] * PKT_BUF_SZ; - - priv->pbyTmpBuff = priv->tx_beacon_bufs + - CB_BEACON_BUF_SIZE; - - priv->tx_bufs_dma1 = priv->tx_bufs_dma0 + - priv->opts.tx_descs[0] * PKT_BUF_SZ; - - priv->tx_beacon_dma = priv->tx_bufs_dma1 + - priv->opts.tx_descs[1] * PKT_BUF_SZ; - - return true; -} - -static void device_free_rings(struct vnt_private *priv) -{ - dma_free_coherent(&priv->pcid->dev, - priv->opts.rx_descs0 * sizeof(struct vnt_rx_desc) + - priv->opts.rx_descs1 * sizeof(struct vnt_rx_desc) + - priv->opts.tx_descs[0] * sizeof(struct vnt_tx_desc) + - priv->opts.tx_descs[1] * sizeof(struct vnt_tx_desc), - priv->aRD0Ring, priv->pool_dma); - - dma_free_coherent(&priv->pcid->dev, - priv->opts.tx_descs[0] * PKT_BUF_SZ + - priv->opts.tx_descs[1] * PKT_BUF_SZ + - CB_BEACON_BUF_SIZE + - CB_MAX_BUF_SIZE, - priv->tx0_bufs, priv->tx_bufs_dma0); -} - -static int device_init_rd0_ring(struct vnt_private *priv) -{ - int i; - dma_addr_t curr = priv->rd0_pool_dma; - struct vnt_rx_desc *desc; - int ret; - - /* Init the RD0 ring entries */ - for (i = 0; i < priv->opts.rx_descs0; - i ++, curr += sizeof(struct vnt_rx_desc)) { - desc = &priv->aRD0Ring[i]; - desc->rd_info = kzalloc(sizeof(*desc->rd_info), GFP_KERNEL); - if (!desc->rd_info) { - ret = -ENOMEM; - goto err_free_desc; - } - - if (!device_alloc_rx_buf(priv, desc)) { - dev_err(&priv->pcid->dev, "can not alloc rx bufs\n"); - ret = -ENOMEM; - goto err_free_rd; - } - - desc->next = &priv->aRD0Ring[(i + 1) % priv->opts.rx_descs0]; - desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_rx_desc)); - } - - if (i > 0) - priv->aRD0Ring[i - 1].next_desc = cpu_to_le32(priv->rd0_pool_dma); - priv->pCurrRD[0] = &priv->aRD0Ring[0]; - - return 0; - -err_free_rd: - kfree(desc->rd_info); - -err_free_desc: - while (i--) { - desc = &priv->aRD0Ring[i]; - device_free_rx_buf(priv, desc); - kfree(desc->rd_info); - } - - return ret; -} - -static int device_init_rd1_ring(struct vnt_private *priv) -{ - int i; - dma_addr_t curr = priv->rd1_pool_dma; - struct vnt_rx_desc *desc; - int ret; - - /* Init the RD1 ring entries */ - for (i = 0; i < priv->opts.rx_descs1; - i ++, curr += sizeof(struct vnt_rx_desc)) { - desc = &priv->aRD1Ring[i]; - desc->rd_info = kzalloc(sizeof(*desc->rd_info), GFP_KERNEL); - if (!desc->rd_info) { - ret = -ENOMEM; - goto err_free_desc; - } - - if (!device_alloc_rx_buf(priv, desc)) { - dev_err(&priv->pcid->dev, "can not alloc rx bufs\n"); - ret = -ENOMEM; - goto err_free_rd; - } - - desc->next = &priv->aRD1Ring[(i + 1) % priv->opts.rx_descs1]; - desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_rx_desc)); - } - - if (i > 0) - priv->aRD1Ring[i - 1].next_desc = cpu_to_le32(priv->rd1_pool_dma); - priv->pCurrRD[1] = &priv->aRD1Ring[0]; - - return 0; - -err_free_rd: - kfree(desc->rd_info); - -err_free_desc: - while (i--) { - desc = &priv->aRD1Ring[i]; - device_free_rx_buf(priv, desc); - kfree(desc->rd_info); - } - - return ret; -} - -static void device_free_rd0_ring(struct vnt_private *priv) -{ - int i; - - for (i = 0; i < priv->opts.rx_descs0; i++) { - struct vnt_rx_desc *desc = &priv->aRD0Ring[i]; - - device_free_rx_buf(priv, desc); - kfree(desc->rd_info); - } -} - -static void device_free_rd1_ring(struct vnt_private *priv) -{ - int i; - - for (i = 0; i < priv->opts.rx_descs1; i++) { - struct vnt_rx_desc *desc = &priv->aRD1Ring[i]; - - device_free_rx_buf(priv, desc); - kfree(desc->rd_info); - } -} - -static int device_init_td0_ring(struct vnt_private *priv) -{ - int i; - dma_addr_t curr; - struct vnt_tx_desc *desc; - int ret; - - curr = priv->td0_pool_dma; - for (i = 0; i < priv->opts.tx_descs[0]; - i++, curr += sizeof(struct vnt_tx_desc)) { - desc = &priv->ap_td0_rings[i]; - desc->td_info = kzalloc(sizeof(*desc->td_info), GFP_KERNEL); - if (!desc->td_info) { - ret = -ENOMEM; - goto err_free_desc; - } - - desc->td_info->buf = priv->tx0_bufs + i * PKT_BUF_SZ; - desc->td_info->buf_dma = priv->tx_bufs_dma0 + i * PKT_BUF_SZ; - - desc->next = &(priv->ap_td0_rings[(i + 1) % priv->opts.tx_descs[0]]); - desc->next_desc = cpu_to_le32(curr + - sizeof(struct vnt_tx_desc)); - } - - if (i > 0) - priv->ap_td0_rings[i - 1].next_desc = cpu_to_le32(priv->td0_pool_dma); - priv->tail_td[0] = priv->apCurrTD[0] = &priv->ap_td0_rings[0]; - - return 0; - -err_free_desc: - while (i--) { - desc = &priv->ap_td0_rings[i]; - kfree(desc->td_info); - } - - return ret; -} - -static int device_init_td1_ring(struct vnt_private *priv) -{ - int i; - dma_addr_t curr; - struct vnt_tx_desc *desc; - int ret; - - /* Init the TD ring entries */ - curr = priv->td1_pool_dma; - for (i = 0; i < priv->opts.tx_descs[1]; - i++, curr += sizeof(struct vnt_tx_desc)) { - desc = &priv->ap_td1_rings[i]; - desc->td_info = kzalloc(sizeof(*desc->td_info), GFP_KERNEL); - if (!desc->td_info) { - ret = -ENOMEM; - goto err_free_desc; - } - - desc->td_info->buf = priv->tx1_bufs + i * PKT_BUF_SZ; - desc->td_info->buf_dma = priv->tx_bufs_dma1 + i * PKT_BUF_SZ; - - desc->next = &(priv->ap_td1_rings[(i + 1) % priv->opts.tx_descs[1]]); - desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_tx_desc)); - } - - if (i > 0) - priv->ap_td1_rings[i - 1].next_desc = cpu_to_le32(priv->td1_pool_dma); - priv->tail_td[1] = priv->apCurrTD[1] = &priv->ap_td1_rings[0]; - - return 0; - -err_free_desc: - while (i--) { - desc = &priv->ap_td1_rings[i]; - kfree(desc->td_info); - } - - return ret; -} - -static void device_free_td0_ring(struct vnt_private *priv) -{ - int i; - - for (i = 0; i < priv->opts.tx_descs[0]; i++) { - struct vnt_tx_desc *desc = &priv->ap_td0_rings[i]; - struct vnt_td_info *td_info = desc->td_info; - - dev_kfree_skb(td_info->skb); - kfree(desc->td_info); - } -} - -static void device_free_td1_ring(struct vnt_private *priv) -{ - int i; - - for (i = 0; i < priv->opts.tx_descs[1]; i++) { - struct vnt_tx_desc *desc = &priv->ap_td1_rings[i]; - struct vnt_td_info *td_info = desc->td_info; - - dev_kfree_skb(td_info->skb); - kfree(desc->td_info); - } -} - -/*-----------------------------------------------------------------*/ - -static int device_rx_srv(struct vnt_private *priv, unsigned int idx) -{ - struct vnt_rx_desc *rd; - int works = 0; - - for (rd = priv->pCurrRD[idx]; - rd->rd0.owner == OWNED_BY_HOST; - rd = rd->next) { - if (works++ > 15) - break; - - if (!rd->rd_info->skb) - break; - - if (vnt_receive_frame(priv, rd)) { - if (!device_alloc_rx_buf(priv, rd)) { - dev_err(&priv->pcid->dev, - "can not allocate rx buf\n"); - break; - } - } - rd->rd0.owner = OWNED_BY_NIC; - } - - priv->pCurrRD[idx] = rd; - - return works; -} - -static bool device_alloc_rx_buf(struct vnt_private *priv, - struct vnt_rx_desc *rd) -{ - struct vnt_rd_info *rd_info = rd->rd_info; - - rd_info->skb = dev_alloc_skb((int)priv->rx_buf_sz); - if (!rd_info->skb) - return false; - - rd_info->skb_dma = - dma_map_single(&priv->pcid->dev, - skb_put(rd_info->skb, skb_tailroom(rd_info->skb)), - priv->rx_buf_sz, DMA_FROM_DEVICE); - if (dma_mapping_error(&priv->pcid->dev, rd_info->skb_dma)) { - dev_kfree_skb(rd_info->skb); - rd_info->skb = NULL; - return false; - } - - *((unsigned int *)&rd->rd0) = 0; /* FIX cast */ - - rd->rd0.res_count = cpu_to_le16(priv->rx_buf_sz); - rd->rd0.owner = OWNED_BY_NIC; - rd->rd1.req_count = cpu_to_le16(priv->rx_buf_sz); - rd->buff_addr = cpu_to_le32(rd_info->skb_dma); - - return true; -} - -static void device_free_rx_buf(struct vnt_private *priv, - struct vnt_rx_desc *rd) -{ - struct vnt_rd_info *rd_info = rd->rd_info; - - dma_unmap_single(&priv->pcid->dev, rd_info->skb_dma, - priv->rx_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb(rd_info->skb); -} - -static const u8 fallback_rate0[5][5] = { - {RATE_18M, RATE_18M, RATE_12M, RATE_12M, RATE_12M}, - {RATE_24M, RATE_24M, RATE_18M, RATE_12M, RATE_12M}, - {RATE_36M, RATE_36M, RATE_24M, RATE_18M, RATE_18M}, - {RATE_48M, RATE_48M, RATE_36M, RATE_24M, RATE_24M}, - {RATE_54M, RATE_54M, RATE_48M, RATE_36M, RATE_36M} -}; - -static const u8 fallback_rate1[5][5] = { - {RATE_18M, RATE_18M, RATE_12M, RATE_6M, RATE_6M}, - {RATE_24M, RATE_24M, RATE_18M, RATE_6M, RATE_6M}, - {RATE_36M, RATE_36M, RATE_24M, RATE_12M, RATE_12M}, - {RATE_48M, RATE_48M, RATE_24M, RATE_12M, RATE_12M}, - {RATE_54M, RATE_54M, RATE_36M, RATE_18M, RATE_18M} -}; - -static int vnt_int_report_rate(struct vnt_private *priv, - struct vnt_td_info *context, u8 tsr0, u8 tsr1) -{ - struct vnt_tx_fifo_head *fifo_head; - struct ieee80211_tx_info *info; - struct ieee80211_rate *rate; - u16 fb_option; - u8 tx_retry = (tsr0 & TSR0_NCR); - s8 idx; - - if (!context) - return -ENOMEM; - - if (!context->skb) - return -EINVAL; - - fifo_head = (struct vnt_tx_fifo_head *)context->buf; - fb_option = (le16_to_cpu(fifo_head->fifo_ctl) & - (FIFOCTL_AUTO_FB_0 | FIFOCTL_AUTO_FB_1)); - - info = IEEE80211_SKB_CB(context->skb); - idx = info->control.rates[0].idx; - - if (fb_option && !(tsr1 & TSR1_TERR)) { - u8 tx_rate; - u8 retry = tx_retry; - - rate = ieee80211_get_tx_rate(priv->hw, info); - tx_rate = rate->hw_value - RATE_18M; - - if (retry > 4) - retry = 4; - - if (fb_option & FIFOCTL_AUTO_FB_0) - tx_rate = fallback_rate0[tx_rate][retry]; - else if (fb_option & FIFOCTL_AUTO_FB_1) - tx_rate = fallback_rate1[tx_rate][retry]; - - if (info->band == NL80211_BAND_5GHZ) - idx = tx_rate - RATE_6M; - else - idx = tx_rate; - } - - ieee80211_tx_info_clear_status(info); - - info->status.rates[0].count = tx_retry; - - if (!(tsr1 & TSR1_TERR)) { - info->status.rates[0].idx = idx; - - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; - else - info->flags |= IEEE80211_TX_STAT_ACK; - } - - return 0; -} - -static int device_tx_srv(struct vnt_private *priv, unsigned int idx) -{ - struct vnt_tx_desc *desc; - int works = 0; - unsigned char byTsr0; - unsigned char byTsr1; - - for (desc = priv->tail_td[idx]; priv->iTDUsed[idx] > 0; desc = desc->next) { - if (desc->td0.owner == OWNED_BY_NIC) - break; - if (works++ > 15) - break; - - byTsr0 = desc->td0.tsr0; - byTsr1 = desc->td0.tsr1; - - /* Only the status of first TD in the chain is correct */ - if (desc->td1.tcr & TCR_STP) { - if ((desc->td_info->flags & TD_FLAGS_NETIF_SKB) != 0) { - if (!(byTsr1 & TSR1_TERR)) { - if (byTsr0 != 0) { - pr_debug(" Tx[%d] OK but has error. tsr1[%02X] tsr0[%02X]\n", - (int)idx, byTsr1, - byTsr0); - } - } else { - pr_debug(" Tx[%d] dropped & tsr1[%02X] tsr0[%02X]\n", - (int)idx, byTsr1, byTsr0); - } - } - - if (byTsr1 & TSR1_TERR) { - if ((desc->td_info->flags & TD_FLAGS_PRIV_SKB) != 0) { - pr_debug(" Tx[%d] fail has error. tsr1[%02X] tsr0[%02X]\n", - (int)idx, byTsr1, byTsr0); - } - } - - vnt_int_report_rate(priv, desc->td_info, byTsr0, byTsr1); - - device_free_tx_buf(priv, desc); - priv->iTDUsed[idx]--; - } - } - - priv->tail_td[idx] = desc; - - return works; -} - -static void device_error(struct vnt_private *priv, unsigned short status) -{ - if (status & ISR_FETALERR) { - dev_err(&priv->pcid->dev, "Hardware fatal error\n"); - - MACbShutdown(priv); - return; - } -} - -static void device_free_tx_buf(struct vnt_private *priv, - struct vnt_tx_desc *desc) -{ - struct vnt_td_info *td_info = desc->td_info; - struct sk_buff *skb = td_info->skb; - - if (skb) - ieee80211_tx_status_irqsafe(priv->hw, skb); - - td_info->skb = NULL; - td_info->flags = 0; -} - -static void vnt_check_bb_vga(struct vnt_private *priv) -{ - long dbm; - int i; - - if (!priv->update_bbvga) - return; - - if (priv->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) - return; - - if (!(priv->vif->cfg.assoc && priv->current_rssi)) - return; - - RFvRSSITodBm(priv, (u8)priv->current_rssi, &dbm); - - for (i = 0; i < BB_VGA_LEVEL; i++) { - if (dbm < priv->dbm_threshold[i]) { - priv->bbvga_new = priv->bbvga[i]; - break; - } - } - - if (priv->bbvga_new == priv->bbvga_current) { - priv->uBBVGADiffCount = 1; - return; - } - - priv->uBBVGADiffCount++; - - if (priv->uBBVGADiffCount == 1) { - /* first VGA diff gain */ - bb_set_vga_gain_offset(priv, priv->bbvga_new); - - dev_dbg(&priv->pcid->dev, - "First RSSI[%d] NewGain[%d] OldGain[%d] Count[%d]\n", - (int)dbm, priv->bbvga_new, - priv->bbvga_current, - (int)priv->uBBVGADiffCount); - } - - if (priv->uBBVGADiffCount >= BB_VGA_CHANGE_THRESHOLD) { - dev_dbg(&priv->pcid->dev, - "RSSI[%d] NewGain[%d] OldGain[%d] Count[%d]\n", - (int)dbm, priv->bbvga_new, - priv->bbvga_current, - (int)priv->uBBVGADiffCount); - - bb_set_vga_gain_offset(priv, priv->bbvga_new); - } -} - -static void vnt_interrupt_process(struct vnt_private *priv) -{ - struct ieee80211_low_level_stats *low_stats = &priv->low_stats; - int max_count = 0; - u32 mib_counter; - u32 isr; - unsigned long flags; - - isr = ioread32(priv->port_offset + MAC_REG_ISR); - - if (isr == 0) - return; - - if (isr == 0xffffffff) { - pr_debug("isr = 0xffff\n"); - return; - } - - spin_lock_irqsave(&priv->lock, flags); - - /* Read low level stats */ - mib_counter = ioread32(priv->port_offset + MAC_REG_MIBCNTR); - - low_stats->dot11RTSSuccessCount += mib_counter & 0xff; - low_stats->dot11RTSFailureCount += (mib_counter >> 8) & 0xff; - low_stats->dot11ACKFailureCount += (mib_counter >> 16) & 0xff; - low_stats->dot11FCSErrorCount += (mib_counter >> 24) & 0xff; - - /* - * TBD.... - * Must do this after doing rx/tx, cause ISR bit is slow - * than RD/TD write back - * update ISR counter - */ - while (isr && priv->vif) { - iowrite32(isr, priv->port_offset + MAC_REG_ISR); - - if (isr & ISR_FETALERR) { - pr_debug(" ISR_FETALERR\n"); - iowrite8(0, priv->port_offset + MAC_REG_SOFTPWRCTL); - iowrite16(SOFTPWRCTL_SWPECTI, priv->port_offset + MAC_REG_SOFTPWRCTL); - device_error(priv, isr); - } - - if (isr & ISR_TBTT) { - if (priv->op_mode != NL80211_IFTYPE_ADHOC) - vnt_check_bb_vga(priv); - - priv->bBeaconSent = false; - if (priv->bEnablePSMode) - PSbIsNextTBTTWakeUp((void *)priv); - - if ((priv->op_mode == NL80211_IFTYPE_AP || - priv->op_mode == NL80211_IFTYPE_ADHOC) && - priv->vif->bss_conf.enable_beacon) - MACvOneShotTimer1MicroSec(priv, - (priv->vif->bss_conf.beacon_int - - MAKE_BEACON_RESERVED) << 10); - - /* TODO: adhoc PS mode */ - } - - if (isr & ISR_BNTX) { - if (priv->op_mode == NL80211_IFTYPE_ADHOC) { - priv->bIsBeaconBufReadySet = false; - priv->cbBeaconBufReadySetCnt = 0; - } - - priv->bBeaconSent = true; - } - - if (isr & ISR_RXDMA0) - max_count += device_rx_srv(priv, TYPE_RXDMA0); - - if (isr & ISR_RXDMA1) - max_count += device_rx_srv(priv, TYPE_RXDMA1); - - if (isr & ISR_TXDMA0) - max_count += device_tx_srv(priv, TYPE_TXDMA0); - - if (isr & ISR_AC0DMA) - max_count += device_tx_srv(priv, TYPE_AC0DMA); - - if (isr & ISR_SOFTTIMER1) { - if (priv->vif->bss_conf.enable_beacon) - vnt_beacon_make(priv, priv->vif); - } - - /* If both buffers available wake the queue */ - if (AVAIL_TD(priv, TYPE_TXDMA0) && - AVAIL_TD(priv, TYPE_AC0DMA) && - ieee80211_queue_stopped(priv->hw, 0)) - ieee80211_wake_queues(priv->hw); - - isr = ioread32(priv->port_offset + MAC_REG_ISR); - - vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL0); - vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_RXDMACTL1); - - if (max_count > priv->opts.int_works) - break; - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void vnt_interrupt_work(struct work_struct *work) -{ - struct vnt_private *priv = - container_of(work, struct vnt_private, interrupt_work); - - if (priv->vif) - vnt_interrupt_process(priv); - - iowrite32(IMR_MASK_VALUE, priv->port_offset + MAC_REG_IMR); -} - -static irqreturn_t vnt_interrupt(int irq, void *arg) -{ - struct vnt_private *priv = arg; - - schedule_work(&priv->interrupt_work); - - iowrite32(0, priv->port_offset + MAC_REG_IMR); - - return IRQ_HANDLED; -} - -static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct vnt_tx_desc *head_td; - u32 dma_idx; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - if (ieee80211_is_data(hdr->frame_control)) - dma_idx = TYPE_AC0DMA; - else - dma_idx = TYPE_TXDMA0; - - if (AVAIL_TD(priv, dma_idx) < 1) { - spin_unlock_irqrestore(&priv->lock, flags); - ieee80211_stop_queues(priv->hw); - return -ENOMEM; - } - - head_td = priv->apCurrTD[dma_idx]; - - head_td->td1.tcr = 0; - - head_td->td_info->skb = skb; - - if (dma_idx == TYPE_AC0DMA) - head_td->td_info->flags = TD_FLAGS_NETIF_SKB; - - priv->apCurrTD[dma_idx] = head_td->next; - - spin_unlock_irqrestore(&priv->lock, flags); - - vnt_generate_fifo_header(priv, dma_idx, head_td, skb); - - spin_lock_irqsave(&priv->lock, flags); - - priv->bPWBitOn = false; - - /* Set TSR1 & ReqCount in TxDescHead */ - head_td->td1.tcr |= (TCR_STP | TCR_EDP | EDMSDU); - head_td->td1.req_count = cpu_to_le16(head_td->td_info->req_count); - - head_td->buff_addr = cpu_to_le32(head_td->td_info->buf_dma); - - /* Poll Transmit the adapter */ - wmb(); - head_td->td0.owner = OWNED_BY_NIC; - wmb(); /* second memory barrier */ - - if (head_td->td_info->flags & TD_FLAGS_NETIF_SKB) - vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_AC0DMACTL); - else - vt6655_mac_dma_ctl(priv->port_offset, MAC_REG_TXDMACTL0); - - priv->iTDUsed[dma_idx]++; - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static void vnt_tx_80211(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct vnt_private *priv = hw->priv; - - if (vnt_tx_packet(priv, skb)) - ieee80211_free_txskb(hw, skb); -} - -static int vnt_start(struct ieee80211_hw *hw) -{ - struct vnt_private *priv = hw->priv; - int ret; - - priv->rx_buf_sz = PKT_BUF_SZ; - if (!device_init_rings(priv)) - return -ENOMEM; - - ret = request_irq(priv->pcid->irq, vnt_interrupt, - IRQF_SHARED, "vt6655", priv); - if (ret) { - dev_dbg(&priv->pcid->dev, "failed to start irq\n"); - goto err_free_rings; - } - - dev_dbg(&priv->pcid->dev, "call device init rd0 ring\n"); - ret = device_init_rd0_ring(priv); - if (ret) - goto err_free_irq; - ret = device_init_rd1_ring(priv); - if (ret) - goto err_free_rd0_ring; - ret = device_init_td0_ring(priv); - if (ret) - goto err_free_rd1_ring; - ret = device_init_td1_ring(priv); - if (ret) - goto err_free_td0_ring; - - device_init_registers(priv); - - dev_dbg(&priv->pcid->dev, "enable MAC interrupt\n"); - iowrite32(IMR_MASK_VALUE, priv->port_offset + MAC_REG_IMR); - - ieee80211_wake_queues(hw); - - return 0; - -err_free_td0_ring: - device_free_td0_ring(priv); -err_free_rd1_ring: - device_free_rd1_ring(priv); -err_free_rd0_ring: - device_free_rd0_ring(priv); -err_free_irq: - free_irq(priv->pcid->irq, priv); -err_free_rings: - device_free_rings(priv); - return ret; -} - -static void vnt_stop(struct ieee80211_hw *hw, bool suspend) -{ - struct vnt_private *priv = hw->priv; - - ieee80211_stop_queues(hw); - - cancel_work_sync(&priv->interrupt_work); - - MACbShutdown(priv); - MACbSoftwareReset(priv); - card_radio_power_off(priv); - - device_free_td0_ring(priv); - device_free_td1_ring(priv); - device_free_rd0_ring(priv); - device_free_rd1_ring(priv); - device_free_rings(priv); - - free_irq(priv->pcid->irq, priv); -} - -static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - priv->vif = vif; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_ADHOC: - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); - - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); - - break; - case NL80211_IFTYPE_AP: - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); - - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); - - break; - default: - return -EOPNOTSUPP; - } - - priv->op_mode = vif->type; - - return 0; -} - -static void vnt_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_ADHOC: - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); - vt6655_mac_reg_bits_off(priv->port_offset, - MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); - break; - case NL80211_IFTYPE_AP: - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); - vt6655_mac_reg_bits_off(priv->port_offset, - MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); - break; - default: - break; - } - - priv->op_mode = NL80211_IFTYPE_UNSPECIFIED; -} - -static int vnt_config(struct ieee80211_hw *hw, u32 changed) -{ - struct vnt_private *priv = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - u8 bb_type; - - if (changed & IEEE80211_CONF_CHANGE_PS) { - if (conf->flags & IEEE80211_CONF_PS) - PSvEnablePowerSaving(priv, conf->listen_interval); - else - PSvDisablePowerSaving(priv); - } - - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || - (conf->flags & IEEE80211_CONF_OFFCHANNEL)) { - set_channel(priv, conf->chandef.chan); - - if (conf->chandef.chan->band == NL80211_BAND_5GHZ) - bb_type = BB_TYPE_11A; - else - bb_type = BB_TYPE_11G; - - if (priv->byBBType != bb_type) { - priv->byBBType = bb_type; - - card_set_phy_parameter(priv, priv->byBBType); - } - } - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - if (priv->byBBType == BB_TYPE_11B) - priv->wCurrentRate = RATE_1M; - else - priv->wCurrentRate = RATE_54M; - - RFbSetPower(priv, priv->wCurrentRate, - conf->chandef.chan->hw_value); - } - - return 0; -} - -static void vnt_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, u64 changed) -{ - struct vnt_private *priv = hw->priv; - - priv->current_aid = vif->cfg.aid; - - if (changed & BSS_CHANGED_BSSID && conf->bssid) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - vt6655_mac_write_bssid_addr(priv->port_offset, conf->bssid); - - spin_unlock_irqrestore(&priv->lock, flags); - } - - if (changed & BSS_CHANGED_BASIC_RATES) { - priv->basic_rates = conf->basic_rates; - - CARDvUpdateBasicTopRate(priv); - - dev_dbg(&priv->pcid->dev, - "basic rates %x\n", conf->basic_rates); - } - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - if (conf->use_short_preamble) { - vt6655_mac_en_barker_preamble_md(priv->port_offset); - priv->preamble_type = true; - } else { - vt6655_mac_dis_barker_preamble_md(priv->port_offset); - priv->preamble_type = false; - } - } - - if (changed & BSS_CHANGED_ERP_CTS_PROT) { - if (conf->use_cts_prot) - vt6655_mac_en_protect_md(priv->port_offset); - else - vt6655_mac_dis_protect_md(priv->port_offset); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - if (conf->use_short_slot) - priv->short_slot_time = true; - else - priv->short_slot_time = false; - - card_set_phy_parameter(priv, priv->byBBType); - bb_set_vga_gain_offset(priv, priv->bbvga[0]); - } - - if (changed & BSS_CHANGED_TXPOWER) - RFbSetPower(priv, priv->wCurrentRate, - conf->chanreq.oper.chan->hw_value); - - if (changed & BSS_CHANGED_BEACON_ENABLED) { - dev_dbg(&priv->pcid->dev, - "Beacon enable %d\n", conf->enable_beacon); - - if (conf->enable_beacon) { - vnt_beacon_enable(priv, vif, conf); - - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); - } else { - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, - TCR_AUTOBCNTX); - } - } - - if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INFO) && - priv->op_mode != NL80211_IFTYPE_AP) { - if (vif->cfg.assoc && conf->beacon_rate) { - card_update_tsf(priv, conf->beacon_rate->hw_value, - conf->sync_tsf); - - card_set_beacon_period(priv, conf->beacon_int); - - CARDvSetFirstNextTBTT(priv, conf->beacon_int); - } else { - iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); - iowrite8(TFTCTL_TSFCNTREN, priv->port_offset + MAC_REG_TFTCTL); - } - } -} - -static u64 vnt_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct vnt_private *priv = hw->priv; - struct netdev_hw_addr *ha; - u64 mc_filter = 0; - u32 bit_nr = 0; - - netdev_hw_addr_list_for_each(ha, mc_list) { - bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; - - mc_filter |= 1ULL << (bit_nr & 0x3f); - } - - priv->mc_list_count = mc_list->count; - - return mc_filter; -} - -static void vnt_configure(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct vnt_private *priv = hw->priv; - u8 rx_mode = 0; - - *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; - - rx_mode = ioread8(priv->port_offset + MAC_REG_RCR); - - dev_dbg(&priv->pcid->dev, "rx mode in = %x\n", rx_mode); - - if (changed_flags & FIF_ALLMULTI) { - if (*total_flags & FIF_ALLMULTI) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->mc_list_count > 2) { - VT6655_MAC_SELECT_PAGE1(priv->port_offset); - - iowrite32(0xffffffff, priv->port_offset + MAC_REG_MAR0); - iowrite32(0xffffffff, priv->port_offset + MAC_REG_MAR0 + 4); - - VT6655_MAC_SELECT_PAGE0(priv->port_offset); - } else { - VT6655_MAC_SELECT_PAGE1(priv->port_offset); - - multicast = le64_to_cpu(multicast); - iowrite32((u32)multicast, priv->port_offset + MAC_REG_MAR0); - iowrite32((u32)(multicast >> 32), - priv->port_offset + MAC_REG_MAR0 + 4); - - VT6655_MAC_SELECT_PAGE0(priv->port_offset); - } - - spin_unlock_irqrestore(&priv->lock, flags); - - rx_mode |= RCR_MULTICAST | RCR_BROADCAST; - } else { - rx_mode &= ~(RCR_MULTICAST | RCR_BROADCAST); - } - } - - if (changed_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) { - rx_mode |= RCR_MULTICAST | RCR_BROADCAST; - - if (*total_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) - rx_mode &= ~RCR_BSSID; - else - rx_mode |= RCR_BSSID; - } - - iowrite8(rx_mode, priv->port_offset + MAC_REG_RCR); - - dev_dbg(&priv->pcid->dev, "rx mode out= %x\n", rx_mode); -} - -static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct vnt_private *priv = hw->priv; - - switch (cmd) { - case SET_KEY: - if (vnt_set_keys(hw, sta, vif, key)) - return -EOPNOTSUPP; - break; - case DISABLE_KEY: - if (test_bit(key->hw_key_idx, &priv->key_entry_inuse)) - clear_bit(key->hw_key_idx, &priv->key_entry_inuse); - break; - default: - break; - } - - return 0; -} - -static int vnt_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct vnt_private *priv = hw->priv; - - memcpy(stats, &priv->low_stats, sizeof(*stats)); - - return 0; -} - -static u64 vnt_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - u64 tsf; - - tsf = vt6655_get_current_tsf(priv); - - return tsf; -} - -static void vnt_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u64 tsf) -{ - struct vnt_private *priv = hw->priv; - - CARDvUpdateNextTBTT(priv, tsf, vif->bss_conf.beacon_int); -} - -static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - /* reset TSF counter */ - iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); -} - -static const struct ieee80211_ops vnt_mac_ops = { - .add_chanctx = ieee80211_emulate_add_chanctx, - .remove_chanctx = ieee80211_emulate_remove_chanctx, - .change_chanctx = ieee80211_emulate_change_chanctx, - .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, - .tx = vnt_tx_80211, - .wake_tx_queue = ieee80211_handle_wake_tx_queue, - .start = vnt_start, - .stop = vnt_stop, - .add_interface = vnt_add_interface, - .remove_interface = vnt_remove_interface, - .config = vnt_config, - .bss_info_changed = vnt_bss_info_changed, - .prepare_multicast = vnt_prepare_multicast, - .configure_filter = vnt_configure, - .set_key = vnt_set_key, - .get_stats = vnt_get_stats, - .get_tsf = vnt_get_tsf, - .set_tsf = vnt_set_tsf, - .reset_tsf = vnt_reset_tsf, -}; - -static int vnt_init(struct vnt_private *priv) -{ - SET_IEEE80211_PERM_ADDR(priv->hw, priv->abyCurrentNetAddr); - - vnt_init_bands(priv); - - if (ieee80211_register_hw(priv->hw)) - return -ENODEV; - - priv->mac_hw = true; - - card_radio_power_off(priv); - - return 0; -} - -static int -vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent) -{ - struct vnt_private *priv; - struct ieee80211_hw *hw; - struct wiphy *wiphy; - int rc; - - dev_notice(&pcid->dev, - "%s Ver. %s\n", DEVICE_FULL_DRV_NAM, DEVICE_VERSION); - - dev_notice(&pcid->dev, - "Copyright (c) 2003 VIA Networking Technologies, Inc.\n"); - - hw = ieee80211_alloc_hw(sizeof(*priv), &vnt_mac_ops); - if (!hw) { - dev_err(&pcid->dev, "could not register ieee80211_hw\n"); - return -ENOMEM; - } - - priv = hw->priv; - priv->pcid = pcid; - - spin_lock_init(&priv->lock); - - priv->hw = hw; - - SET_IEEE80211_DEV(priv->hw, &pcid->dev); - - if (pci_enable_device(pcid)) { - device_free_info(priv); - return -ENODEV; - } - - dev_dbg(&pcid->dev, - "Before get pci_info memaddr is %x\n", priv->memaddr); - - pci_set_master(pcid); - - priv->memaddr = pci_resource_start(pcid, 0); - priv->ioaddr = pci_resource_start(pcid, 1); - priv->port_offset = ioremap(priv->memaddr & PCI_BASE_ADDRESS_MEM_MASK, - 256); - if (!priv->port_offset) { - dev_err(&pcid->dev, ": Failed to IO remapping ..\n"); - device_free_info(priv); - return -ENODEV; - } - - rc = pci_request_regions(pcid, DEVICE_NAME); - if (rc) { - dev_err(&pcid->dev, ": Failed to find PCI device\n"); - device_free_info(priv); - return -ENODEV; - } - - if (dma_set_mask(&pcid->dev, DMA_BIT_MASK(32))) { - dev_err(&pcid->dev, ": Failed to set dma 32 bit mask\n"); - device_free_info(priv); - return -ENODEV; - } - - INIT_WORK(&priv->interrupt_work, vnt_interrupt_work); - - /* do reset */ - if (!MACbSoftwareReset(priv)) { - dev_err(&pcid->dev, ": Failed to access MAC hardware..\n"); - device_free_info(priv); - return -ENODEV; - } - /* initial to reload eeprom */ - MACvInitialize(priv); - vt6655_mac_read_ether_addr(priv->port_offset, priv->abyCurrentNetAddr); - - /* Get RFType */ - priv->rf_type = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_RFTYPE); - priv->rf_type &= RF_MASK; - - dev_dbg(&pcid->dev, "RF Type = %x\n", priv->rf_type); - - device_get_options(priv); - device_set_options(priv); - - wiphy = priv->hw->wiphy; - - wiphy->frag_threshold = FRAG_THRESH_DEF; - wiphy->rts_threshold = RTS_THRESH_DEF; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); - - ieee80211_hw_set(priv->hw, TIMING_BEACON_ONLY); - ieee80211_hw_set(priv->hw, SIGNAL_DBM); - ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); - ieee80211_hw_set(priv->hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(priv->hw, SUPPORTS_PS); - - priv->hw->max_signal = 100; - - if (vnt_init(priv)) { - device_free_info(priv); - return -ENODEV; - } - - device_print_info(priv); - pci_set_drvdata(pcid, priv); - - return 0; -} - -/*------------------------------------------------------------------*/ - -static int __maybe_unused vt6655_suspend(struct device *dev_d) -{ - struct vnt_private *priv = dev_get_drvdata(dev_d); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - MACbShutdown(priv); - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int __maybe_unused vt6655_resume(struct device *dev_d) -{ - device_wakeup_disable(dev_d); - - return 0; -} - -MODULE_DEVICE_TABLE(pci, vt6655_pci_id_table); - -static SIMPLE_DEV_PM_OPS(vt6655_pm_ops, vt6655_suspend, vt6655_resume); - -static struct pci_driver device_driver = { - .name = DEVICE_NAME, - .id_table = vt6655_pci_id_table, - .probe = vt6655_probe, - .remove = vt6655_remove, - .driver.pm = &vt6655_pm_ops, -}; - -module_pci_driver(device_driver); diff --git a/drivers/staging/vt6655/dpc.c b/drivers/staging/vt6655/dpc.c deleted file mode 100644 index 7ada188e20489..0000000000000 --- a/drivers/staging/vt6655/dpc.c +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: handle dpc rx functions - * - * Author: Lyndon Chen - * - * Date: May 20, 2003 - * - * Functions: - * - * Revision History: - * - */ - -#include "device.h" -#include "baseband.h" -#include "rf.h" -#include "dpc.h" - -static bool vnt_rx_data(struct vnt_private *priv, struct sk_buff *skb, - u16 bytes_received) -{ - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_supported_band *sband; - struct ieee80211_rx_status rx_status = { 0 }; - struct ieee80211_hdr *hdr; - __le16 fc; - u8 *rsr, *new_rsr, *rssi; - __le64 *tsf_time; - u16 frame_size; - int ii, r; - u8 *rx_rate; - u8 *skb_data; - u8 rate_idx = 0; - u8 rate[MAX_RATE] = {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}; - long rx_dbm; - - /* [31:16]RcvByteCount ( not include 4-byte Status ) */ - frame_size = le16_to_cpu(*((__le16 *)(skb->data + 2))); - if (frame_size > 2346 || frame_size < 14) { - dev_dbg(&priv->pcid->dev, "------- WRONG Length 1\n"); - return false; - } - - skb_data = (u8 *)skb->data; - - rx_rate = skb_data + 1; - - sband = hw->wiphy->bands[hw->conf.chandef.chan->band]; - - for (r = RATE_1M; r < MAX_RATE; r++) { - if (*rx_rate == rate[r]) - break; - } - - priv->rx_rate = r; - - for (ii = 0; ii < sband->n_bitrates; ii++) { - if (sband->bitrates[ii].hw_value == r) { - rate_idx = ii; - break; - } - } - - if (ii == sband->n_bitrates) { - dev_dbg(&priv->pcid->dev, "Wrong RxRate %x\n", *rx_rate); - return false; - } - - tsf_time = (__le64 *)(skb_data + bytes_received - 12); - new_rsr = skb_data + bytes_received - 3; - rssi = skb_data + bytes_received - 2; - rsr = skb_data + bytes_received - 1; - if (*rsr & (RSR_IVLDTYP | RSR_IVLDLEN)) - return false; - - RFvRSSITodBm(priv, *rssi, &rx_dbm); - - priv->bb_pre_edrssi = (u8)rx_dbm + 1; - priv->current_rssi = *rssi; - - skb_pull(skb, 4); - skb_trim(skb, frame_size); - - rx_status.mactime = le64_to_cpu(*tsf_time); - rx_status.band = hw->conf.chandef.chan->band; - rx_status.signal = rx_dbm; - rx_status.flag = 0; - rx_status.freq = hw->conf.chandef.chan->center_freq; - - if (!(*rsr & RSR_CRCOK)) - rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - - hdr = (struct ieee80211_hdr *)(skb->data); - fc = hdr->frame_control; - - rx_status.rate_idx = rate_idx; - - if (ieee80211_has_protected(fc)) { - if (priv->local_id > REV_ID_VT3253_A1) - rx_status.flag |= RX_FLAG_DECRYPTED; - - /* Drop packet */ - if (!(*new_rsr & NEWRSR_DECRYPTOK)) - return false; - } - - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - - ieee80211_rx_irqsafe(priv->hw, skb); - - return true; -} - -bool vnt_receive_frame(struct vnt_private *priv, struct vnt_rx_desc *curr_rd) -{ - struct vnt_rd_info *rd_info = curr_rd->rd_info; - struct sk_buff *skb; - u16 frame_size; - - skb = rd_info->skb; - - dma_unmap_single(&priv->pcid->dev, rd_info->skb_dma, - priv->rx_buf_sz, DMA_FROM_DEVICE); - - frame_size = le16_to_cpu(curr_rd->rd1.req_count) - - le16_to_cpu(curr_rd->rd0.res_count); - - if ((frame_size > 2364) || (frame_size < 33)) { - /* Frame Size error drop this packet.*/ - dev_dbg(&priv->pcid->dev, "Wrong frame size %d\n", frame_size); - dev_kfree_skb_irq(skb); - return true; - } - - if (vnt_rx_data(priv, skb, frame_size)) - return true; - - dev_kfree_skb_irq(skb); - - return true; -} diff --git a/drivers/staging/vt6655/dpc.h b/drivers/staging/vt6655/dpc.h deleted file mode 100644 index 40364c0ab7f64..0000000000000 --- a/drivers/staging/vt6655/dpc.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: - * - * Author: Jerry Chen - * - * Date: Jun. 27, 2002 - * - */ - -#ifndef __DPC_H__ -#define __DPC_H__ - -#include "device.h" - -bool vnt_receive_frame(struct vnt_private *priv, struct vnt_rx_desc *curr_rd); - -#endif /* __RXTX_H__ */ diff --git a/drivers/staging/vt6655/key.c b/drivers/staging/vt6655/key.c deleted file mode 100644 index 1469015eb5b4a..0000000000000 --- a/drivers/staging/vt6655/key.c +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions for 802.11i Key management - * - * Author: Jerry Chen - * - * Date: May 29, 2003 - * - */ - -#include "key.h" -#include "mac.h" - -static int vnt_set_keymode(struct ieee80211_hw *hw, u8 *mac_addr, - struct ieee80211_key_conf *key, u32 key_type, - u32 mode, bool onfly_latch) -{ - struct vnt_private *priv = hw->priv; - u8 broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - u16 key_mode = 0; - u32 entry = 0; - u8 *bssid; - u8 key_inx = key->keyidx; - u8 i; - - if (mac_addr) - bssid = mac_addr; - else - bssid = &broadcast[0]; - - if (key_type != VNT_KEY_DEFAULTKEY) { - for (i = 0; i < (MAX_KEY_TABLE - 1); i++) { - if (!test_bit(i, &priv->key_entry_inuse)) { - set_bit(i, &priv->key_entry_inuse); - - key->hw_key_idx = i; - entry = key->hw_key_idx; - break; - } - } - } - - switch (key_type) { - case VNT_KEY_DEFAULTKEY: - /* default key last entry */ - entry = MAX_KEY_TABLE - 1; - key->hw_key_idx = entry; - fallthrough; - case VNT_KEY_ALLGROUP: - key_mode |= VNT_KEY_ALLGROUP; - if (onfly_latch) - key_mode |= VNT_KEY_ONFLY_ALL; - fallthrough; - case VNT_KEY_GROUP_ADDRESS: - key_mode |= mode; - fallthrough; - case VNT_KEY_GROUP: - key_mode |= (mode << 4); - key_mode |= VNT_KEY_GROUP; - break; - case VNT_KEY_PAIRWISE: - key_mode |= mode; - key_inx = 4; - break; - default: - return -EINVAL; - } - - if (onfly_latch) - key_mode |= VNT_KEY_ONFLY; - - if (mode == KEY_CTL_WEP) { - if (key->keylen == WLAN_KEY_LEN_WEP40) - key->key[15] &= 0x7f; - if (key->keylen == WLAN_KEY_LEN_WEP104) - key->key[15] |= 0x80; - } - - MACvSetKeyEntry(priv, key_mode, entry, key_inx, - bssid, (u32 *)key->key, priv->local_id); - - return 0; -} - -int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - struct ieee80211_vif *vif, struct ieee80211_key_conf *key) -{ - struct ieee80211_bss_conf *conf = &vif->bss_conf; - struct vnt_private *priv = hw->priv; - u8 *mac_addr = NULL; - u8 key_dec_mode = 0; - int ret = 0; - u32 u; - - if (sta) - mac_addr = &sta->addr[0]; - - switch (key->cipher) { - case 0: - for (u = 0 ; u < MAX_KEY_TABLE; u++) - MACvDisableKeyEntry(priv, u); - return ret; - - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - for (u = 0; u < MAX_KEY_TABLE; u++) - MACvDisableKeyEntry(priv, u); - - vnt_set_keymode(hw, mac_addr, - key, VNT_KEY_DEFAULTKEY, KEY_CTL_WEP, true); - - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - - return ret; - case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - - key_dec_mode = KEY_CTL_TKIP; - - break; - case WLAN_CIPHER_SUITE_CCMP: - key_dec_mode = KEY_CTL_CCMP; - - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - } - - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { - vnt_set_keymode(hw, mac_addr, - key, VNT_KEY_PAIRWISE, key_dec_mode, true); - } else { - vnt_set_keymode(hw, mac_addr, - key, VNT_KEY_DEFAULTKEY, key_dec_mode, true); - - vnt_set_keymode(hw, (u8 *)conf->bssid, - key, VNT_KEY_GROUP_ADDRESS, key_dec_mode, true); - } - - return 0; -} diff --git a/drivers/staging/vt6655/key.h b/drivers/staging/vt6655/key.h deleted file mode 100644 index d88da9dfb5c3b..0000000000000 --- a/drivers/staging/vt6655/key.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions for 802.11i Key management - * - * Author: Jerry Chen - * - * Date: May 29, 2003 - * - */ - -#ifndef __KEY_H__ -#define __KEY_H__ - -#include - -/*--------------------- Export Definitions -------------------------*/ -#define MAX_GROUP_KEY 4 -#define MAX_KEY_TABLE 11 -#define MAX_KEY_LEN 32 -#define AES_KEY_LEN 16 - -#define AUTHENTICATOR_KEY 0x10000000 -#define USE_KEYRSC 0x20000000 -#define PAIRWISE_KEY 0x40000000 -#define TRANSMIT_KEY 0x80000000 - -#define GROUP_KEY 0x00000000 - -#define KEY_CTL_WEP 0x00 -#define KEY_CTL_NONE 0x01 -#define KEY_CTL_TKIP 0x02 -#define KEY_CTL_CCMP 0x03 -#define KEY_CTL_INVALID 0xFF - -#define VNT_KEY_DEFAULTKEY 0x1 -#define VNT_KEY_GROUP_ADDRESS 0x2 -#define VNT_KEY_ALLGROUP 0x4 -#define VNT_KEY_GROUP 0x40 -#define VNT_KEY_PAIRWISE 0x00 -#define VNT_KEY_ONFLY 0x8000 -#define VNT_KEY_ONFLY_ALL 0x4000 - -struct vnt_private; - -int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - struct ieee80211_vif *vif, struct ieee80211_key_conf *key); - -#endif /* __KEY_H__ */ diff --git a/drivers/staging/vt6655/mac.c b/drivers/staging/vt6655/mac.c deleted file mode 100644 index b4ebc7d319619..0000000000000 --- a/drivers/staging/vt6655/mac.c +++ /dev/null @@ -1,851 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: MAC routines - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - * Functions: - * vt6655_mac_is_reg_bits_off - Test if All test Bits Off - * vt6655_mac_set_short_retry_limit - Set 802.11 Short Retry limit - * MACvSetLongRetryLimit - Set 802.11 Long Retry limit - * vt6655_mac_set_loopback_mode - Set MAC Loopback Mode - * vt6655_mac_save_context - Save Context of MAC Registers - * vt6655_mac_restore_context - Restore Context of MAC Registers - * MACbSoftwareReset - Software Reset MAC - * vt6655_mac_safe_rx_off - Turn Off MAC Rx - * vt6655_mac_safe_tx_off - Turn Off MAC Tx - * vt6655_mac_safe_stop - Stop MAC function - * MACbShutdown - Shut down MAC - * MACvInitialize - Initialize MAC - * MACvSetCurrRxDescAddr - Set Rx Descriptors Address - * MACvSetCurrTx0DescAddr - Set Tx0 Descriptors Address - * MACvSetCurrTx1DescAddr - Set Tx1 Descriptors Address - * MACvTimer0MicroSDelay - Micro Second Delay Loop by MAC - * - * Revision History: - * 08-22-2003 Kyle Hsu : Porting MAC functions from sim53 - * 09-03-2003 Bryan YC Fan : Add MACvClearBusSusInd()& - * MACvEnableBusSusEn() - * 09-18-2003 Jerry Chen : Add MACvSetKeyEntry & MACvDisableKeyEntry - * - */ - -#include "mac.h" - -void vt6655_mac_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask) -{ - unsigned char reg_value; - - reg_value = ioread8(iobase + reg_offset); - iowrite8(reg_value | bit_mask, iobase + reg_offset); -} - -void vt6655_mac_word_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask) -{ - unsigned short reg_value; - - reg_value = ioread16(iobase + reg_offset); - iowrite16(reg_value | (bit_mask), iobase + reg_offset); -} - -void vt6655_mac_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask) -{ - unsigned char reg_value; - - reg_value = ioread8(iobase + reg_offset); - iowrite8(reg_value & ~(bit_mask), iobase + reg_offset); -} - -void vt6655_mac_word_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask) -{ - unsigned short reg_value; - - reg_value = ioread16(iobase + reg_offset); - iowrite16(reg_value & ~(bit_mask), iobase + reg_offset); -} - -static void vt6655_mac_clear_stck_ds(void __iomem *iobase) -{ - u8 reg_value; - - reg_value = ioread8(iobase + MAC_REG_STICKHW); - reg_value = reg_value & 0xFC; - iowrite8(reg_value, iobase + MAC_REG_STICKHW); -} - -/* - * Description: - * Test if all test bits off - * - * Parameters: - * In: - * io_base - Base Address for MAC - * reg_offset - Offset of MAC Register - * mask - Test bits - * Out: - * none - * - * Return Value: true if all test bits Off; otherwise false - * - */ -static bool vt6655_mac_is_reg_bits_off(struct vnt_private *priv, - unsigned char reg_offset, - unsigned char mask) -{ - void __iomem *io_base = priv->port_offset; - - return !(ioread8(io_base + reg_offset) & mask); -} - -/* - * Description: - * Set 802.11 Short Retry Limit - * - * Parameters: - * In: - * io_base - Base Address for MAC - * retry_limit - Retry Limit - * Out: - * none - * - * Return Value: none - * - */ -void vt6655_mac_set_short_retry_limit(struct vnt_private *priv, unsigned char retry_limit) -{ - void __iomem *io_base = priv->port_offset; - /* set SRT */ - iowrite8(retry_limit, io_base + MAC_REG_SRT); -} - -/* - * Description: - * Set 802.11 Long Retry Limit - * - * Parameters: - * In: - * io_base - Base Address for MAC - * byRetryLimit- Retry Limit - * Out: - * none - * - * Return Value: none - * - */ -void MACvSetLongRetryLimit(struct vnt_private *priv, - unsigned char byRetryLimit) -{ - void __iomem *io_base = priv->port_offset; - /* set LRT */ - iowrite8(byRetryLimit, io_base + MAC_REG_LRT); -} - -/* - * Description: - * Set MAC Loopback mode - * - * Parameters: - * In: - * io_base - Base Address for MAC - * loopback_mode - Loopback Mode - * Out: - * none - * - * Return Value: none - * - */ -static void vt6655_mac_set_loopback_mode(struct vnt_private *priv, u8 loopback_mode) -{ - void __iomem *io_base = priv->port_offset; - - loopback_mode <<= 6; - /* set TCR */ - iowrite8((ioread8(io_base + MAC_REG_TEST) & 0x3f) | loopback_mode, io_base + MAC_REG_TEST); -} - -/* - * Description: - * Save MAC registers to context buffer - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * cxt_buf - Context buffer - * - * Return Value: none - * - */ -static void vt6655_mac_save_context(struct vnt_private *priv, u8 *cxt_buf) -{ - void __iomem *io_base = priv->port_offset; - - /* read page0 register */ - memcpy_fromio(cxt_buf, io_base, MAC_MAX_CONTEXT_SIZE_PAGE0); - - VT6655_MAC_SELECT_PAGE1(io_base); - - /* read page1 register */ - memcpy_fromio(cxt_buf + MAC_MAX_CONTEXT_SIZE_PAGE0, io_base, - MAC_MAX_CONTEXT_SIZE_PAGE1); - - VT6655_MAC_SELECT_PAGE0(io_base); -} - -/* - * Description: - * Restore MAC registers from context buffer - * - * Parameters: - * In: - * io_base - Base Address for MAC - * cxt_buf - Context buffer - * Out: - * none - * - * Return Value: none - * - */ -static void vt6655_mac_restore_context(struct vnt_private *priv, u8 *cxt_buf) -{ - void __iomem *io_base = priv->port_offset; - - VT6655_MAC_SELECT_PAGE1(io_base); - /* restore page1 */ - memcpy_toio(io_base, cxt_buf + MAC_MAX_CONTEXT_SIZE_PAGE0, - MAC_MAX_CONTEXT_SIZE_PAGE1); - - VT6655_MAC_SELECT_PAGE0(io_base); - - /* restore RCR,TCR,IMR... */ - memcpy_toio(io_base + MAC_REG_RCR, cxt_buf + MAC_REG_RCR, - MAC_REG_ISR - MAC_REG_RCR); - - /* restore MAC Config. */ - memcpy_toio(io_base + MAC_REG_LRT, cxt_buf + MAC_REG_LRT, - MAC_REG_PAGE1SEL - MAC_REG_LRT); - - iowrite8(*(cxt_buf + MAC_REG_CFG), io_base + MAC_REG_CFG); - - /* restore PS Config. */ - memcpy_toio(io_base + MAC_REG_PSCFG, cxt_buf + MAC_REG_PSCFG, - MAC_REG_BBREGCTL - MAC_REG_PSCFG); - - /* restore CURR_RX_DESC_ADDR, CURR_TX_DESC_ADDR */ - iowrite32(*(u32 *)(cxt_buf + MAC_REG_TXDMAPTR0), - io_base + MAC_REG_TXDMAPTR0); - iowrite32(*(u32 *)(cxt_buf + MAC_REG_AC0DMAPTR), - io_base + MAC_REG_AC0DMAPTR); - iowrite32(*(u32 *)(cxt_buf + MAC_REG_BCNDMAPTR), - io_base + MAC_REG_BCNDMAPTR); - iowrite32(*(u32 *)(cxt_buf + MAC_REG_RXDMAPTR0), - io_base + MAC_REG_RXDMAPTR0); - iowrite32(*(u32 *)(cxt_buf + MAC_REG_RXDMAPTR1), - io_base + MAC_REG_RXDMAPTR1); -} - -/* - * Description: - * Software Reset MAC - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: true if Reset Success; otherwise false - * - */ -bool MACbSoftwareReset(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - - /* turn on HOSTCR_SOFTRST, just write 0x01 to reset */ - iowrite8(0x01, io_base + MAC_REG_HOSTCR); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_HOSTCR) & HOSTCR_SOFTRST)) - break; - } - if (ww == W_MAX_TIMEOUT) - return false; - return true; -} - -/* - * Description: - * save some important register's value, then do reset, then restore - * register's value - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -static void vt6655_mac_save_soft_reset(struct vnt_private *priv) -{ - u8 tmp_reg_data[MAC_MAX_CONTEXT_SIZE_PAGE0 + MAC_MAX_CONTEXT_SIZE_PAGE1]; - - /* PATCH.... - * save some important register's value, then do - * reset, then restore register's value - */ - /* save MAC context */ - vt6655_mac_save_context(priv, tmp_reg_data); - /* do reset */ - MACbSoftwareReset(priv); - /* restore MAC context, except CR0 */ - vt6655_mac_restore_context(priv, tmp_reg_data); -} - -/* - * Description: - * Turn Off MAC Rx - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -static bool vt6655_mac_safe_rx_off(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - - /* turn off wow temp for turn off Rx safely */ - - /* Clear RX DMA0,1 */ - iowrite32(DMACTL_CLRRUN, io_base + MAC_REG_RXDMACTL0); - iowrite32(DMACTL_CLRRUN, io_base + MAC_REG_RXDMACTL1); - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread32(io_base + MAC_REG_RXDMACTL0) & DMACTL_RUN)) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x10)\n"); - return false; - } - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread32(io_base + MAC_REG_RXDMACTL1) & DMACTL_RUN)) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x11)\n"); - return false; - } - - /* try to safe shutdown RX */ - vt6655_mac_reg_bits_off(io_base, MAC_REG_HOSTCR, HOSTCR_RXON); - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_HOSTCR) & HOSTCR_RXONST)) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x12)\n"); - return false; - } - return true; -} - -/* - * Description: - * Turn Off MAC Tx - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -static bool vt6655_mac_safe_tx_off(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - - /* Clear TX DMA */ - /* Tx0 */ - iowrite32(DMACTL_CLRRUN, io_base + MAC_REG_TXDMACTL0); - /* AC0 */ - iowrite32(DMACTL_CLRRUN, io_base + MAC_REG_AC0DMACTL); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread32(io_base + MAC_REG_TXDMACTL0) & DMACTL_RUN)) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x20)\n"); - return false; - } - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread32(io_base + MAC_REG_AC0DMACTL) & DMACTL_RUN)) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x21)\n"); - return false; - } - - /* try to safe shutdown TX */ - vt6655_mac_reg_bits_off(io_base, MAC_REG_HOSTCR, HOSTCR_TXON); - - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_HOSTCR) & HOSTCR_TXONST)) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x24)\n"); - return false; - } - return true; -} - -/* - * Description: - * Stop MAC function - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -static bool vt6655_mac_safe_stop(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - - vt6655_mac_reg_bits_off(io_base, MAC_REG_TCR, TCR_AUTOBCNTX); - - if (!vt6655_mac_safe_rx_off(priv)) { - pr_debug(" vt6655_mac_safe_rx_off == false)\n"); - vt6655_mac_save_soft_reset(priv); - return false; - } - if (!vt6655_mac_safe_tx_off(priv)) { - pr_debug(" vt6655_mac_safe_tx_off == false)\n"); - vt6655_mac_save_soft_reset(priv); - return false; - } - - vt6655_mac_reg_bits_off(io_base, MAC_REG_HOSTCR, HOSTCR_MACEN); - - return true; -} - -/* - * Description: - * Shut Down MAC - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -bool MACbShutdown(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - /* disable MAC IMR */ - iowrite32(0, io_base + MAC_REG_IMR); - vt6655_mac_set_loopback_mode(priv, MAC_LB_INTERNAL); - /* stop the adapter */ - if (!vt6655_mac_safe_stop(priv)) { - vt6655_mac_set_loopback_mode(priv, MAC_LB_NONE); - return false; - } - vt6655_mac_set_loopback_mode(priv, MAC_LB_NONE); - return true; -} - -/* - * Description: - * Initialize MAC - * - * Parameters: - * In: - * io_base - Base Address for MAC - * Out: - * none - * - * Return Value: none - * - */ -void MACvInitialize(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - /* clear sticky bits */ - vt6655_mac_clear_stck_ds(io_base); - /* disable force PME-enable */ - iowrite8(PME_OVR, io_base + MAC_REG_PMC1); - /* only 3253 A */ - - /* do reset */ - MACbSoftwareReset(priv); - - /* reset TSF counter */ - iowrite8(TFTCTL_TSFCNTRST, io_base + MAC_REG_TFTCTL); - /* enable TSF counter */ - iowrite8(TFTCTL_TSFCNTREN, io_base + MAC_REG_TFTCTL); -} - -/* - * Description: - * Set the chip with current rx descriptor address - * - * Parameters: - * In: - * io_base - Base Address for MAC - * curr_desc_addr - Descriptor Address - * Out: - * none - * - * Return Value: none - * - */ -void vt6655_mac_set_curr_rx_0_desc_addr(struct vnt_private *priv, u32 curr_desc_addr) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - unsigned char org_dma_ctl; - - org_dma_ctl = ioread8(io_base + MAC_REG_RXDMACTL0); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_RXDMACTL0 + 2); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_RXDMACTL0) & DMACTL_RUN)) - break; - } - - iowrite32(curr_desc_addr, io_base + MAC_REG_RXDMAPTR0); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_RXDMACTL0); -} - -/* - * Description: - * Set the chip with current rx descriptor address - * - * Parameters: - * In: - * io_base - Base Address for MAC - * curr_desc_addr - Descriptor Address - * Out: - * none - * - * Return Value: none - * - */ -void vt6655_mac_set_curr_rx_1_desc_addr(struct vnt_private *priv, u32 curr_desc_addr) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - unsigned char org_dma_ctl; - - org_dma_ctl = ioread8(io_base + MAC_REG_RXDMACTL1); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_RXDMACTL1 + 2); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_RXDMACTL1) & DMACTL_RUN)) - break; - } - - iowrite32(curr_desc_addr, io_base + MAC_REG_RXDMAPTR1); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_RXDMACTL1); -} - -/* - * Description: - * Set the chip with current tx0 descriptor address - * - * Parameters: - * In: - * io_base - Base Address for MAC - * curr_desc_addr - Descriptor Address - * Out: - * none - * - * Return Value: none - * - */ -static void vt6655_mac_set_curr_tx_0_desc_addr_ex(struct vnt_private *priv, u32 curr_desc_addr) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - unsigned char org_dma_ctl; - - org_dma_ctl = ioread8(io_base + MAC_REG_TXDMACTL0); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_TXDMACTL0 + 2); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_TXDMACTL0) & DMACTL_RUN)) - break; - } - - iowrite32(curr_desc_addr, io_base + MAC_REG_TXDMAPTR0); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_TXDMACTL0); -} - -/* - * Description: - * Set the chip with current AC0 descriptor address - * - * Parameters: - * In: - * io_base - Base Address for MAC - * curr_desc_addr - Descriptor Address - * Out: - * none - * - * Return Value: none - * - */ -/* TxDMA1 = AC0DMA */ -static void vt6655_mac_set_curr_ac_0_desc_addr_ex(struct vnt_private *priv, u32 curr_desc_addr) -{ - void __iomem *io_base = priv->port_offset; - unsigned short ww; - unsigned char org_dma_ctl; - - org_dma_ctl = ioread8(io_base + MAC_REG_AC0DMACTL); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_AC0DMACTL + 2); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(ioread8(io_base + MAC_REG_AC0DMACTL) & DMACTL_RUN)) - break; - } - if (ww == W_MAX_TIMEOUT) - pr_debug(" DBG_PORT80(0x26)\n"); - iowrite32(curr_desc_addr, io_base + MAC_REG_AC0DMAPTR); - if (org_dma_ctl & DMACTL_RUN) - iowrite8(DMACTL_RUN, io_base + MAC_REG_AC0DMACTL); -} - -void vt6655_mac_set_curr_tx_desc_addr(int tx_type, struct vnt_private *priv, u32 curr_desc_addr) -{ - if (tx_type == TYPE_AC0DMA) - vt6655_mac_set_curr_ac_0_desc_addr_ex(priv, curr_desc_addr); - else if (tx_type == TYPE_TXDMA0) - vt6655_mac_set_curr_tx_0_desc_addr_ex(priv, curr_desc_addr); -} - -/* - * Description: - * Micro Second Delay via MAC - * - * Parameters: - * In: - * io_base - Base Address for MAC - * uDelay - Delay time (timer resolution is 4 us) - * Out: - * none - * - * Return Value: none - * - */ -void MACvTimer0MicroSDelay(struct vnt_private *priv, unsigned int uDelay) -{ - void __iomem *io_base = priv->port_offset; - unsigned char byValue; - unsigned int uu, ii; - - iowrite8(0, io_base + MAC_REG_TMCTL0); - iowrite32(uDelay, io_base + MAC_REG_TMDATA0); - iowrite8((TMCTL_TMD | TMCTL_TE), io_base + MAC_REG_TMCTL0); - for (ii = 0; ii < 66; ii++) { /* assume max PCI clock is 66Mhz */ - for (uu = 0; uu < uDelay; uu++) { - byValue = ioread8(io_base + MAC_REG_TMCTL0); - if ((byValue == 0) || - (byValue & TMCTL_TSUSP)) { - iowrite8(0, io_base + MAC_REG_TMCTL0); - return; - } - } - } - iowrite8(0, io_base + MAC_REG_TMCTL0); -} - -/* - * Description: - * Micro Second One shot timer via MAC - * - * Parameters: - * In: - * io_base - Base Address for MAC - * uDelay - Delay time - * Out: - * none - * - * Return Value: none - * - */ -void MACvOneShotTimer1MicroSec(struct vnt_private *priv, - unsigned int uDelayTime) -{ - void __iomem *io_base = priv->port_offset; - - iowrite8(0, io_base + MAC_REG_TMCTL1); - iowrite32(uDelayTime, io_base + MAC_REG_TMDATA1); - iowrite8((TMCTL_TMD | TMCTL_TE), io_base + MAC_REG_TMCTL1); -} - -void MACvSetMISCFifo(struct vnt_private *priv, unsigned short offset, - u32 data) -{ - void __iomem *io_base = priv->port_offset; - - if (offset > 273) - return; - iowrite16(offset, io_base + MAC_REG_MISCFFNDEX); - iowrite32(data, io_base + MAC_REG_MISCFFDATA); - iowrite16(MISCFFCTL_WRITE, io_base + MAC_REG_MISCFFCTL); -} - -bool MACbPSWakeup(struct vnt_private *priv) -{ - void __iomem *io_base = priv->port_offset; - unsigned int ww; - /* Read PSCTL */ - if (vt6655_mac_is_reg_bits_off(priv, MAC_REG_PSCTL, PSCTL_PS)) - return true; - - /* Disable PS */ - vt6655_mac_reg_bits_off(io_base, MAC_REG_PSCTL, PSCTL_PSEN); - - /* Check if SyncFlushOK */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (ioread8(io_base + MAC_REG_PSCTL) & PSCTL_WAKEDONE) - break; - } - if (ww == W_MAX_TIMEOUT) { - pr_debug(" DBG_PORT80(0x33)\n"); - return false; - } - return true; -} - -/* - * Description: - * Set the Key by MISCFIFO - * - * Parameters: - * In: - * io_base - Base Address for MAC - * - * Out: - * none - * - * Return Value: none - * - */ - -void MACvSetKeyEntry(struct vnt_private *priv, unsigned short wKeyCtl, - unsigned int uEntryIdx, unsigned int uKeyIdx, - unsigned char *pbyAddr, u32 *pdwKey, - unsigned char local_id) -{ - void __iomem *io_base = priv->port_offset; - unsigned short offset; - u32 data; - int ii; - - if (local_id <= 1) - return; - - offset = MISCFIFO_KEYETRY0; - offset += (uEntryIdx * MISCFIFO_KEYENTRYSIZE); - - data = 0; - data |= wKeyCtl; - data <<= 16; - data |= MAKEWORD(*(pbyAddr + 4), *(pbyAddr + 5)); - pr_debug("1. offset: %d, Data: %X, KeyCtl:%X\n", - offset, data, wKeyCtl); - - iowrite16(offset, io_base + MAC_REG_MISCFFNDEX); - iowrite32(data, io_base + MAC_REG_MISCFFDATA); - iowrite16(MISCFFCTL_WRITE, io_base + MAC_REG_MISCFFCTL); - offset++; - - data = 0; - data |= *(pbyAddr + 3); - data <<= 8; - data |= *(pbyAddr + 2); - data <<= 8; - data |= *(pbyAddr + 1); - data <<= 8; - data |= *pbyAddr; - pr_debug("2. offset: %d, Data: %X\n", offset, data); - - iowrite16(offset, io_base + MAC_REG_MISCFFNDEX); - iowrite32(data, io_base + MAC_REG_MISCFFDATA); - iowrite16(MISCFFCTL_WRITE, io_base + MAC_REG_MISCFFCTL); - offset++; - - offset += (uKeyIdx * 4); - for (ii = 0; ii < 4; ii++) { - /* always push 128 bits */ - pr_debug("3.(%d) offset: %d, Data: %X\n", - ii, offset + ii, *pdwKey); - iowrite16(offset + ii, io_base + MAC_REG_MISCFFNDEX); - iowrite32(*pdwKey++, io_base + MAC_REG_MISCFFDATA); - iowrite16(MISCFFCTL_WRITE, io_base + MAC_REG_MISCFFCTL); - } -} - -/* - * Description: - * Disable the Key Entry by MISCFIFO - * - * Parameters: - * In: - * io_base - Base Address for MAC - * - * Out: - * none - * - * Return Value: none - * - */ -void MACvDisableKeyEntry(struct vnt_private *priv, unsigned int uEntryIdx) -{ - void __iomem *io_base = priv->port_offset; - unsigned short offset; - - offset = MISCFIFO_KEYETRY0; - offset += (uEntryIdx * MISCFIFO_KEYENTRYSIZE); - - iowrite16(offset, io_base + MAC_REG_MISCFFNDEX); - iowrite32(0, io_base + MAC_REG_MISCFFDATA); - iowrite16(MISCFFCTL_WRITE, io_base + MAC_REG_MISCFFCTL); -} diff --git a/drivers/staging/vt6655/mac.h b/drivers/staging/vt6655/mac.h deleted file mode 100644 index a33af28522276..0000000000000 --- a/drivers/staging/vt6655/mac.h +++ /dev/null @@ -1,580 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: MAC routines - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - * Revision History: - * 07-01-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. - * 08-25-2003 Kyle Hsu: Porting MAC functions from sim53. - * 09-03-2003 Bryan YC Fan: Add vt6655_mac_dis_protect_md & vt6655_mac_en_protect_md - */ - -#ifndef __MAC_H__ -#define __MAC_H__ - -#include "device.h" - -/*--------------------- Export Definitions -------------------------*/ -/* Registers in the MAC */ -#define MAC_MAX_CONTEXT_SIZE_PAGE0 256 -#define MAC_MAX_CONTEXT_SIZE_PAGE1 128 - -/* Registers not related to 802.11b */ -#define MAC_REG_BCFG0 0x00 -#define MAC_REG_BCFG1 0x01 -#define MAC_REG_FCR0 0x02 -#define MAC_REG_FCR1 0x03 -#define MAC_REG_BISTCMD 0x04 -#define MAC_REG_BISTSR0 0x05 -#define MAC_REG_BISTSR1 0x06 -#define MAC_REG_BISTSR2 0x07 -#define MAC_REG_I2MCSR 0x08 -#define MAC_REG_I2MTGID 0x09 -#define MAC_REG_I2MTGAD 0x0A -#define MAC_REG_I2MCFG 0x0B -#define MAC_REG_I2MDIPT 0x0C -#define MAC_REG_I2MDOPT 0x0E -#define MAC_REG_PMC0 0x10 -#define MAC_REG_PMC1 0x11 -#define MAC_REG_STICKHW 0x12 -#define MAC_REG_LOCALID 0x14 -#define MAC_REG_TESTCFG 0x15 -#define MAC_REG_JUMPER0 0x16 -#define MAC_REG_JUMPER1 0x17 -#define MAC_REG_TMCTL0 0x18 -#define MAC_REG_TMCTL1 0x19 -#define MAC_REG_TMDATA0 0x1C - -/* MAC Parameter related */ -#define MAC_REG_LRT 0x20 -#define MAC_REG_SRT 0x21 -#define MAC_REG_SIFS 0x22 -#define MAC_REG_DIFS 0x23 -#define MAC_REG_EIFS 0x24 -#define MAC_REG_SLOT 0x25 -#define MAC_REG_BI 0x26 -#define MAC_REG_CWMAXMIN0 0x28 -#define MAC_REG_LINKOFFTOTM 0x2A -#define MAC_REG_SWTMOT 0x2B -#define MAC_REG_MIBCNTR 0x2C -#define MAC_REG_RTSOKCNT 0x2C -#define MAC_REG_RTSFAILCNT 0x2D -#define MAC_REG_ACKFAILCNT 0x2E -#define MAC_REG_FCSERRCNT 0x2F - -/* TSF Related */ -#define MAC_REG_TSFCNTR 0x30 -#define MAC_REG_NEXTTBTT 0x38 -#define MAC_REG_TSFOFST 0x40 -#define MAC_REG_TFTCTL 0x48 - -/* WMAC Control/Status Related */ -#define MAC_REG_ENCFG 0x4C -#define MAC_REG_PAGE1SEL 0x4F -#define MAC_REG_CFG 0x50 -#define MAC_REG_TEST 0x52 -#define MAC_REG_HOSTCR 0x54 -#define MAC_REG_MACCR 0x55 -#define MAC_REG_RCR 0x56 -#define MAC_REG_TCR 0x57 -#define MAC_REG_IMR 0x58 -#define MAC_REG_ISR 0x5C - -/* Power Saving Related */ -#define MAC_REG_PSCFG 0x60 -#define MAC_REG_PSCTL 0x61 -#define MAC_REG_PSPWRSIG 0x62 -#define MAC_REG_BBCR13 0x63 -#define MAC_REG_AIDATIM 0x64 -#define MAC_REG_PWBT 0x66 -#define MAC_REG_WAKEOKTMR 0x68 -#define MAC_REG_CALTMR 0x69 -#define MAC_REG_SYNSPACCNT 0x6A -#define MAC_REG_WAKSYNOPT 0x6B - -/* Baseband/IF Control Group */ -#define MAC_REG_BBREGCTL 0x6C -#define MAC_REG_CHANNEL 0x6D -#define MAC_REG_BBREGADR 0x6E -#define MAC_REG_BBREGDATA 0x6F -#define MAC_REG_IFREGCTL 0x70 -#define MAC_REG_IFDATA 0x71 -#define MAC_REG_ITRTMSET 0x74 -#define MAC_REG_PAPEDELAY 0x77 -#define MAC_REG_SOFTPWRCTL 0x78 -#define MAC_REG_GPIOCTL0 0x7A -#define MAC_REG_GPIOCTL1 0x7B - -/* MAC DMA Related Group */ -#define MAC_REG_TXDMACTL0 0x7C -#define MAC_REG_TXDMAPTR0 0x80 -#define MAC_REG_AC0DMACTL 0x84 -#define MAC_REG_AC0DMAPTR 0x88 -#define MAC_REG_BCNDMACTL 0x8C -#define MAC_REG_BCNDMAPTR 0x90 -#define MAC_REG_RXDMACTL0 0x94 -#define MAC_REG_RXDMAPTR0 0x98 -#define MAC_REG_RXDMACTL1 0x9C -#define MAC_REG_RXDMAPTR1 0xA0 -#define MAC_REG_SYNCDMACTL 0xA4 -#define MAC_REG_SYNCDMAPTR 0xA8 -#define MAC_REG_ATIMDMACTL 0xAC -#define MAC_REG_ATIMDMAPTR 0xB0 - -/* MiscFF PIO related */ -#define MAC_REG_MISCFFNDEX 0xB4 -#define MAC_REG_MISCFFCTL 0xB6 -#define MAC_REG_MISCFFDATA 0xB8 - -/* Extend SW Timer */ -#define MAC_REG_TMDATA1 0xBC - -/* WOW Related Group */ -#define MAC_REG_WAKEUPEN0 0xC0 -#define MAC_REG_WAKEUPEN1 0xC1 -#define MAC_REG_WAKEUPSR0 0xC2 -#define MAC_REG_WAKEUPSR1 0xC3 -#define MAC_REG_WAKE128_0 0xC4 -#define MAC_REG_WAKE128_1 0xD4 -#define MAC_REG_WAKE128_2 0xE4 -#define MAC_REG_WAKE128_3 0xF4 - -/************** Page 1 ******************/ -#define MAC_REG_CRC_128_0 0x04 -#define MAC_REG_CRC_128_1 0x06 -#define MAC_REG_CRC_128_2 0x08 -#define MAC_REG_CRC_128_3 0x0A - -/* MAC Configuration Group */ -#define MAC_REG_PAR0 0x0C -#define MAC_REG_PAR4 0x10 -#define MAC_REG_BSSID0 0x14 -#define MAC_REG_BSSID4 0x18 -#define MAC_REG_MAR0 0x1C -#define MAC_REG_MAR4 0x20 - -/* MAC RSPPKT INFO Group */ -#define MAC_REG_RSPINF_B_1 0x24 -#define MAC_REG_RSPINF_B_2 0x28 -#define MAC_REG_RSPINF_B_5 0x2C -#define MAC_REG_RSPINF_B_11 0x30 -#define MAC_REG_RSPINF_A_6 0x34 -#define MAC_REG_RSPINF_A_9 0x36 -#define MAC_REG_RSPINF_A_12 0x38 -#define MAC_REG_RSPINF_A_18 0x3A -#define MAC_REG_RSPINF_A_24 0x3C -#define MAC_REG_RSPINF_A_36 0x3E -#define MAC_REG_RSPINF_A_48 0x40 -#define MAC_REG_RSPINF_A_54 0x42 -#define MAC_REG_RSPINF_A_72 0x44 - -/* 802.11h relative */ -#define MAC_REG_QUIETINIT 0x60 -#define MAC_REG_QUIETGAP 0x62 -#define MAC_REG_QUIETDUR 0x64 -#define MAC_REG_MSRCTL 0x66 -#define MAC_REG_MSRBBSTS 0x67 -#define MAC_REG_MSRSTART 0x68 -#define MAC_REG_MSRDURATION 0x70 -#define MAC_REG_CCAFRACTION 0x72 -#define MAC_REG_PWRCCK 0x73 -#define MAC_REG_PWROFDM 0x7C - -/* Bits in the BCFG0 register */ -#define BCFG0_PERROFF 0x40 -#define BCFG0_MRDMDIS 0x20 -#define BCFG0_MRDLDIS 0x10 -#define BCFG0_MWMEN 0x08 -#define BCFG0_VSERREN 0x02 -#define BCFG0_LATMEN 0x01 - -/* Bits in the BCFG1 register */ -#define BCFG1_CFUNOPT 0x80 -#define BCFG1_CREQOPT 0x40 -#define BCFG1_DMA8 0x10 -#define BCFG1_ARBITOPT 0x08 -#define BCFG1_PCIMEN 0x04 -#define BCFG1_MIOEN 0x02 -#define BCFG1_CISDLYEN 0x01 - -/* Bits in RAMBIST registers */ -#define BISTCMD_TSTPAT5 0x00 -#define BISTCMD_TSTPATA 0x80 -#define BISTCMD_TSTERR 0x20 -#define BISTCMD_TSTPATF 0x18 -#define BISTCMD_TSTPAT0 0x10 -#define BISTCMD_TSTMODE 0x04 -#define BISTCMD_TSTITTX 0x03 -#define BISTCMD_TSTATRX 0x02 -#define BISTCMD_TSTATTX 0x01 -#define BISTCMD_TSTRX 0x00 -#define BISTSR0_BISTGO 0x01 -#define BISTSR1_TSTSR 0x01 -#define BISTSR2_CMDPRTEN 0x02 -#define BISTSR2_RAMTSTEN 0x01 - -/* Bits in the I2MCFG EEPROM register */ -#define I2MCFG_BOUNDCTL 0x80 -#define I2MCFG_WAITCTL 0x20 -#define I2MCFG_SCLOECTL 0x10 -#define I2MCFG_WBUSYCTL 0x08 -#define I2MCFG_NORETRY 0x04 -#define I2MCFG_I2MLDSEQ 0x02 -#define I2MCFG_I2CMFAST 0x01 - -/* Bits in the I2MCSR EEPROM register */ -#define I2MCSR_EEMW 0x80 -#define I2MCSR_EEMR 0x40 -#define I2MCSR_AUTOLD 0x08 -#define I2MCSR_NACK 0x02 -#define I2MCSR_DONE 0x01 - -/* Bits in the PMC1 register */ -#define SPS_RST 0x80 -#define PCISTIKY 0x40 -#define PME_OVR 0x02 - -/* Bits in the STICKYHW register */ -#define STICKHW_DS1_SHADOW 0x02 -#define STICKHW_DS0_SHADOW 0x01 - -/* Bits in the TMCTL register */ -#define TMCTL_TSUSP 0x04 -#define TMCTL_TMD 0x02 -#define TMCTL_TE 0x01 - -/* Bits in the TFTCTL register */ -#define TFTCTL_HWUTSF 0x80 -#define TFTCTL_TBTTSYNC 0x40 -#define TFTCTL_HWUTSFEN 0x20 -#define TFTCTL_TSFCNTRRD 0x10 -#define TFTCTL_TBTTSYNCEN 0x08 -#define TFTCTL_TSFSYNCEN 0x04 -#define TFTCTL_TSFCNTRST 0x02 -#define TFTCTL_TSFCNTREN 0x01 - -/* Bits in the EnhanceCFG register */ -#define ENCFG_BARKERPREAM 0x00020000 -#define ENCFG_NXTBTTCFPSTR 0x00010000 -#define ENCFG_BCNSUSCLR 0x00000200 -#define ENCFG_BCNSUSIND 0x00000100 -#define ENCFG_CFP_PROTECTEN 0x00000040 -#define ENCFG_PROTECTMD 0x00000020 -#define ENCFG_HWPARCFP 0x00000010 -#define ENCFG_CFNULRSP 0x00000004 -#define ENCFG_BBTYPE_MASK 0x00000003 -#define ENCFG_BBTYPE_G 0x00000002 -#define ENCFG_BBTYPE_B 0x00000001 -#define ENCFG_BBTYPE_A 0x00000000 - -/* Bits in the Page1Sel register */ -#define PAGE1_SEL 0x01 - -/* Bits in the CFG register */ -#define CFG_TKIPOPT 0x80 -#define CFG_RXDMAOPT 0x40 -#define CFG_TMOT_SW 0x20 -#define CFG_TMOT_HWLONG 0x10 -#define CFG_TMOT_HW 0x00 -#define CFG_CFPENDOPT 0x08 -#define CFG_BCNSUSEN 0x04 -#define CFG_NOTXTIMEOUT 0x02 -#define CFG_NOBUFOPT 0x01 - -/* Bits in the TEST register */ -#define TEST_LBEXT 0x80 -#define TEST_LBINT 0x40 -#define TEST_LBNONE 0x00 -#define TEST_SOFTINT 0x20 -#define TEST_CONTTX 0x10 -#define TEST_TXPE 0x08 -#define TEST_NAVDIS 0x04 -#define TEST_NOCTS 0x02 -#define TEST_NOACK 0x01 - -/* Bits in the HOSTCR register */ -#define HOSTCR_TXONST 0x80 -#define HOSTCR_RXONST 0x40 -#define HOSTCR_ADHOC 0x20 /* Network Type 1 = Ad-hoc */ -#define HOSTCR_AP 0x10 /* Port Type 1 = AP */ -#define HOSTCR_TXON 0x08 /* 0000 1000 */ -#define HOSTCR_RXON 0x04 /* 0000 0100 */ -#define HOSTCR_MACEN 0x02 /* 0000 0010 */ -#define HOSTCR_SOFTRST 0x01 /* 0000 0001 */ - -/* Bits in the MACCR register */ -#define MACCR_SYNCFLUSHOK 0x04 -#define MACCR_SYNCFLUSH 0x02 -#define MACCR_CLRNAV 0x01 - -/* Bits in the MAC_REG_GPIOCTL0 register */ -#define LED_ACTSET 0x01 -#define LED_RFOFF 0x02 -#define LED_NOCONNECT 0x04 - -/* Bits in the RCR register */ -#define RCR_SSID 0x80 -#define RCR_RXALLTYPE 0x40 -#define RCR_UNICAST 0x20 -#define RCR_BROADCAST 0x10 -#define RCR_MULTICAST 0x08 -#define RCR_WPAERR 0x04 -#define RCR_ERRCRC 0x02 -#define RCR_BSSID 0x01 - -/* Bits in the TCR register */ -#define TCR_SYNCDCFOPT 0x02 -#define TCR_AUTOBCNTX 0x01 /* Beacon automatically transmit enable */ - -/* Bits in the IMR register */ -#define IMR_MEASURESTART 0x80000000 -#define IMR_QUIETSTART 0x20000000 -#define IMR_RADARDETECT 0x10000000 -#define IMR_MEASUREEND 0x08000000 -#define IMR_SOFTTIMER1 0x00200000 -#define IMR_RXDMA1 0x00001000 /* 0000 0000 0001 0000 0000 0000 */ -#define IMR_RXNOBUF 0x00000800 -#define IMR_MIBNEARFULL 0x00000400 -#define IMR_SOFTINT 0x00000200 -#define IMR_FETALERR 0x00000100 -#define IMR_WATCHDOG 0x00000080 -#define IMR_SOFTTIMER 0x00000040 -#define IMR_GPIO 0x00000020 -#define IMR_TBTT 0x00000010 -#define IMR_RXDMA0 0x00000008 -#define IMR_BNTX 0x00000004 -#define IMR_AC0DMA 0x00000002 -#define IMR_TXDMA0 0x00000001 - -/* Bits in the ISR register */ -#define ISR_MEASURESTART 0x80000000 -#define ISR_QUIETSTART 0x20000000 -#define ISR_RADARDETECT 0x10000000 -#define ISR_MEASUREEND 0x08000000 -#define ISR_SOFTTIMER1 0x00200000 -#define ISR_RXDMA1 0x00001000 /* 0000 0000 0001 0000 0000 0000 */ -#define ISR_RXNOBUF 0x00000800 /* 0000 0000 0000 1000 0000 0000 */ -#define ISR_MIBNEARFULL 0x00000400 /* 0000 0000 0000 0100 0000 0000 */ -#define ISR_SOFTINT 0x00000200 -#define ISR_FETALERR 0x00000100 -#define ISR_WATCHDOG 0x00000080 -#define ISR_SOFTTIMER 0x00000040 -#define ISR_GPIO 0x00000020 -#define ISR_TBTT 0x00000010 -#define ISR_RXDMA0 0x00000008 -#define ISR_BNTX 0x00000004 -#define ISR_AC0DMA 0x00000002 -#define ISR_TXDMA0 0x00000001 - -/* Bits in the PSCFG register */ -#define PSCFG_PHILIPMD 0x40 -#define PSCFG_WAKECALEN 0x20 -#define PSCFG_WAKETMREN 0x10 -#define PSCFG_BBPSPROG 0x08 -#define PSCFG_WAKESYN 0x04 -#define PSCFG_SLEEPSYN 0x02 -#define PSCFG_AUTOSLEEP 0x01 - -/* Bits in the PSCTL register */ -#define PSCTL_WAKEDONE 0x20 -#define PSCTL_PS 0x10 -#define PSCTL_GO2DOZE 0x08 -#define PSCTL_LNBCN 0x04 -#define PSCTL_ALBCN 0x02 -#define PSCTL_PSEN 0x01 - -/* Bits in the PSPWSIG register */ -#define PSSIG_WPE3 0x80 -#define PSSIG_WPE2 0x40 -#define PSSIG_WPE1 0x20 -#define PSSIG_WRADIOPE 0x10 -#define PSSIG_SPE3 0x08 -#define PSSIG_SPE2 0x04 -#define PSSIG_SPE1 0x02 -#define PSSIG_SRADIOPE 0x01 - -/* Bits in the BBREGCTL register */ -#define BBREGCTL_DONE 0x04 -#define BBREGCTL_REGR 0x02 -#define BBREGCTL_REGW 0x01 - -/* Bits in the IFREGCTL register */ -#define IFREGCTL_DONE 0x04 -#define IFREGCTL_IFRF 0x02 -#define IFREGCTL_REGW 0x01 - -/* Bits in the SOFTPWRCTL register */ -#define SOFTPWRCTL_RFLEOPT 0x0800 -#define SOFTPWRCTL_TXPEINV 0x0200 -#define SOFTPWRCTL_SWPECTI 0x0100 -#define SOFTPWRCTL_SWPAPE 0x0020 -#define SOFTPWRCTL_SWCALEN 0x0010 -#define SOFTPWRCTL_SWRADIO_PE 0x0008 -#define SOFTPWRCTL_SWPE2 0x0004 -#define SOFTPWRCTL_SWPE1 0x0002 -#define SOFTPWRCTL_SWPE3 0x0001 - -/* Bits in the GPIOCTL1 register */ -#define GPIO1_DATA1 0x20 -#define GPIO1_MD1 0x10 -#define GPIO1_DATA0 0x02 -#define GPIO1_MD0 0x01 - -/* Bits in the DMACTL register */ -#define DMACTL_CLRRUN 0x00080000 -#define DMACTL_RUN 0x00000008 -#define DMACTL_WAKE 0x00000004 -#define DMACTL_DEAD 0x00000002 -#define DMACTL_ACTIVE 0x00000001 - -/* Bits in the RXDMACTL0 register */ -#define RX_PERPKT 0x00000100 -#define RX_PERPKTCLR 0x01000000 - -/* Bits in the BCNDMACTL register */ -#define BEACON_READY 0x01 - -/* Bits in the MISCFFCTL register */ -#define MISCFFCTL_WRITE 0x0001 - -/* Bits in WAKEUPEN0 */ -#define WAKEUPEN0_DIRPKT 0x10 -#define WAKEUPEN0_LINKOFF 0x08 -#define WAKEUPEN0_ATIMEN 0x04 -#define WAKEUPEN0_TIMEN 0x02 -#define WAKEUPEN0_MAGICEN 0x01 - -/* Bits in WAKEUPEN1 */ -#define WAKEUPEN1_128_3 0x08 -#define WAKEUPEN1_128_2 0x04 -#define WAKEUPEN1_128_1 0x02 -#define WAKEUPEN1_128_0 0x01 - -/* Bits in WAKEUPSR0 */ -#define WAKEUPSR0_DIRPKT 0x10 -#define WAKEUPSR0_LINKOFF 0x08 -#define WAKEUPSR0_ATIMEN 0x04 -#define WAKEUPSR0_TIMEN 0x02 -#define WAKEUPSR0_MAGICEN 0x01 - -/* Bits in WAKEUPSR1 */ -#define WAKEUPSR1_128_3 0x08 -#define WAKEUPSR1_128_2 0x04 -#define WAKEUPSR1_128_1 0x02 -#define WAKEUPSR1_128_0 0x01 - -/* Bits in the MAC_REG_GPIOCTL register */ -#define GPIO0_MD 0x01 -#define GPIO0_DATA 0x02 -#define GPIO0_INTMD 0x04 -#define GPIO1_MD 0x10 -#define GPIO1_DATA 0x20 - -/* Bits in the MSRCTL register */ -#define MSRCTL_FINISH 0x80 -#define MSRCTL_READY 0x40 -#define MSRCTL_RADARDETECT 0x20 -#define MSRCTL_EN 0x10 -#define MSRCTL_QUIETTXCHK 0x08 -#define MSRCTL_QUIETRPT 0x04 -#define MSRCTL_QUIETINT 0x02 -#define MSRCTL_QUIETEN 0x01 - -/* Bits in the MSRCTL1 register */ -#define MSRCTL1_TXPWR 0x08 -#define MSRCTL1_CSAPAREN 0x04 -#define MSRCTL1_TXPAUSE 0x01 - -/* Loopback mode */ -#define MAC_LB_EXT 0x02 -#define MAC_LB_INTERNAL 0x01 -#define MAC_LB_NONE 0x00 - -#define DEFAULT_BI 0x200 - -/* MiscFIFO Offset */ -#define MISCFIFO_KEYETRY0 32 -#define MISCFIFO_KEYENTRYSIZE 22 -#define MISCFIFO_SYNINFO_IDX 10 -#define MISCFIFO_SYNDATA_IDX 11 -#define MISCFIFO_SYNDATASIZE 21 - -/* enabled mask value of irq */ -#define IMR_MASK_VALUE (IMR_SOFTTIMER1 | \ - IMR_RXDMA1 | \ - IMR_RXNOBUF | \ - IMR_MIBNEARFULL | \ - IMR_SOFTINT | \ - IMR_FETALERR | \ - IMR_WATCHDOG | \ - IMR_SOFTTIMER | \ - IMR_GPIO | \ - IMR_TBTT | \ - IMR_RXDMA0 | \ - IMR_BNTX | \ - IMR_AC0DMA | \ - IMR_TXDMA0) - -/* max time out delay time */ -#define W_MAX_TIMEOUT 0xFFF0U - -/* wait time within loop */ -#define CB_DELAY_LOOP_WAIT 10 /* 10ms */ - -/* revision id */ -#define REV_ID_VT3253_A0 0x00 -#define REV_ID_VT3253_A1 0x01 -#define REV_ID_VT3253_B0 0x08 -#define REV_ID_VT3253_B1 0x09 - -/*--------------------- Export Types ------------------------------*/ - -/*--------------------- Export Macros ------------------------------*/ - -#define VT6655_MAC_SELECT_PAGE0(iobase) iowrite8(0, (iobase) + MAC_REG_PAGE1SEL) - -#define VT6655_MAC_SELECT_PAGE1(iobase) iowrite8(1, (iobase) + MAC_REG_PAGE1SEL) - -#define MAKEWORD(lb, hb) \ - ((unsigned short)(((unsigned char)(lb)) | (((unsigned short)((unsigned char)(hb))) << 8))) - -void vt6655_mac_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask); -void vt6655_mac_word_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask); -void vt6655_mac_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask); -void vt6655_mac_word_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask); - -void vt6655_mac_set_short_retry_limit(struct vnt_private *priv, unsigned char retry_limit); - -void MACvSetLongRetryLimit(struct vnt_private *priv, unsigned char byRetryLimit); - -bool MACbSoftwareReset(struct vnt_private *priv); -bool MACbShutdown(struct vnt_private *priv); -void MACvInitialize(struct vnt_private *priv); -void vt6655_mac_set_curr_rx_0_desc_addr(struct vnt_private *priv, u32 curr_desc_addr); -void vt6655_mac_set_curr_rx_1_desc_addr(struct vnt_private *priv, u32 curr_desc_addr); -void vt6655_mac_set_curr_tx_desc_addr(int tx_type, struct vnt_private *priv, u32 curr_desc_addr); -void MACvSetCurrSyncDescAddrEx(struct vnt_private *priv, - u32 curr_desc_addr); -void MACvSetCurrATIMDescAddrEx(struct vnt_private *priv, - u32 curr_desc_addr); -void MACvTimer0MicroSDelay(struct vnt_private *priv, unsigned int uDelay); -void MACvOneShotTimer1MicroSec(struct vnt_private *priv, unsigned int uDelayTime); - -void MACvSetMISCFifo(struct vnt_private *priv, unsigned short wOffset, - u32 dwData); - -bool MACbPSWakeup(struct vnt_private *priv); - -void MACvSetKeyEntry(struct vnt_private *priv, unsigned short wKeyCtl, - unsigned int uEntryIdx, unsigned int uKeyIdx, - unsigned char *pbyAddr, u32 *pdwKey, - unsigned char local_id); -void MACvDisableKeyEntry(struct vnt_private *priv, unsigned int uEntryIdx); - -#endif /* __MAC_H__ */ diff --git a/drivers/staging/vt6655/power.c b/drivers/staging/vt6655/power.c deleted file mode 100644 index 8527ad3eff486..0000000000000 --- a/drivers/staging/vt6655/power.c +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handles 802.11 power management functions - * - * Author: Lyndon Chen - * - * Date: July 17, 2002 - * - * Functions: - * PSvEnablePowerSaving - Enable Power Saving Mode - * PSvDiasblePowerSaving - Disable Power Saving Mode - * PSbConsiderPowerDown - Decide if we can Power Down - * PSvSendPSPOLL - Send PS-POLL packet - * PSbSendNullPacket - Send Null packet - * PSbIsNextTBTTWakeUp - Decide if we need to wake up at next Beacon - * - * Revision History: - * - */ - -#include "mac.h" -#include "device.h" -#include "power.h" -#include "card.h" - -/*--------------------- Static Definitions -------------------------*/ - -/*--------------------- Static Classes ----------------------------*/ - -/*--------------------- Static Functions --------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -/* - * - * Routine Description: - * Enable hw power saving functions - * - * Return Value: - * None. - * - */ - -void PSvEnablePowerSaving(struct vnt_private *priv, - unsigned short wListenInterval) -{ - u16 wAID = priv->current_aid | BIT(14) | BIT(15); - - /* set period of power up before TBTT */ - iowrite16(C_PWBT, priv->port_offset + MAC_REG_PWBT); - if (priv->op_mode != NL80211_IFTYPE_ADHOC) { - /* set AID */ - iowrite16(wAID, priv->port_offset + MAC_REG_AIDATIM); - } - - /* Set AutoSleep */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); - - /* Set HWUTSF */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); - - if (wListenInterval >= 2) { - /* clear always listen beacon */ - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); - /* first time set listen next beacon */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_LNBCN); - } else { - /* always listen beacon */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); - } - - /* enable power saving hw function */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_PSEN); - priv->bEnablePSMode = true; - - priv->bPWBitOn = true; - pr_debug("PS:Power Saving Mode Enable...\n"); -} - -/* - * - * Routine Description: - * Disable hw power saving functions - * - * Return Value: - * None. - * - */ - -void PSvDisablePowerSaving(struct vnt_private *priv) -{ - /* disable power saving hw function */ - MACbPSWakeup(priv); - - /* clear AutoSleep */ - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); - - /* clear HWUTSF */ - vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); - - /* set always listen beacon */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); - - priv->bEnablePSMode = false; - - priv->bPWBitOn = false; -} - -/* - * - * Routine Description: - * Check if Next TBTT must wake up - * - * Return Value: - * None. - * - */ - -bool PSbIsNextTBTTWakeUp(struct vnt_private *priv) -{ - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_conf *conf = &hw->conf; - bool wake_up = false; - - if (conf->listen_interval > 1) { - if (!priv->wake_up_count) - priv->wake_up_count = conf->listen_interval; - - --priv->wake_up_count; - - if (priv->wake_up_count == 1) { - /* Turn on wake up to listen next beacon */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_LNBCN); - wake_up = true; - } - } - - return wake_up; -} diff --git a/drivers/staging/vt6655/power.h b/drivers/staging/vt6655/power.h deleted file mode 100644 index 060516f81f5bb..0000000000000 --- a/drivers/staging/vt6655/power.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handles 802.11 power management functions - * - * Author: Lyndon Chen - * - * Date: July 17, 2002 - * - */ - -#ifndef __POWER_H__ -#define __POWER_H__ - -#include "device.h" - -#define C_PWBT 1000 /* micro sec. power up before TBTT */ -#define PS_FAST_INTERVAL 1 /* Fast power saving listen interval */ -#define PS_MAX_INTERVAL 4 /* MAX power saving listen interval */ - -void PSvDisablePowerSaving(struct vnt_private *priv); - -void PSvEnablePowerSaving(struct vnt_private *priv, unsigned short wListenInterval); - -bool PSbIsNextTBTTWakeUp(struct vnt_private *priv); - -#endif /* __POWER_H__ */ diff --git a/drivers/staging/vt6655/rf.c b/drivers/staging/vt6655/rf.c deleted file mode 100644 index d319ec21c97b8..0000000000000 --- a/drivers/staging/vt6655/rf.c +++ /dev/null @@ -1,535 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: rf function code - * - * Author: Jerry Chen - * - * Date: Feb. 19, 2004 - * - * Functions: - * IFRFbWriteEmbedded - Embedded write RF register via MAC - * - * Revision History: - * RobertYu 2005 - * chester 2008 - * - */ - -#include "mac.h" -#include "srom.h" -#include "rf.h" -#include "baseband.h" - -#define BY_AL2230_REG_LEN 23 /* 24bit */ -#define CB_AL2230_INIT_SEQ 15 -#define SWITCH_CHANNEL_DELAY_AL2230 200 /* us */ -#define AL2230_PWR_IDX_LEN 64 - -#define BY_AL7230_REG_LEN 23 /* 24bit */ -#define CB_AL7230_INIT_SEQ 16 -#define SWITCH_CHANNEL_DELAY_AL7230 200 /* us */ -#define AL7230_PWR_IDX_LEN 64 - -static const unsigned long al2230_init_table[CB_AL2230_INIT_SEQ] = { - 0x03F79000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x01A00200 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x00FFF300 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0005A400 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0F4DC500 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0805B600 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0146C700 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x00068800 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0403B900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x00DBBA00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x00099B00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0BDFFC00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x00000D00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x00580F00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW -}; - -static const unsigned long al2230_channel_table0[CB_MAX_CHANNEL] = { - 0x03F79000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 1, Tf = 2412MHz */ - 0x03F79000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 2, Tf = 2417MHz */ - 0x03E79000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 3, Tf = 2422MHz */ - 0x03E79000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 4, Tf = 2427MHz */ - 0x03F7A000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 5, Tf = 2432MHz */ - 0x03F7A000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 6, Tf = 2437MHz */ - 0x03E7A000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 7, Tf = 2442MHz */ - 0x03E7A000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 8, Tf = 2447MHz */ - 0x03F7B000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 9, Tf = 2452MHz */ - 0x03F7B000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 10, Tf = 2457MHz */ - 0x03E7B000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 11, Tf = 2462MHz */ - 0x03E7B000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 12, Tf = 2467MHz */ - 0x03F7C000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 13, Tf = 2472MHz */ - 0x03E7C000 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW /* channel = 14, Tf = 2412M */ -}; - -static const unsigned long al2230_channel_table1[CB_MAX_CHANNEL] = { - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 1, Tf = 2412MHz */ - 0x0B333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 2, Tf = 2417MHz */ - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 3, Tf = 2422MHz */ - 0x0B333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 4, Tf = 2427MHz */ - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 5, Tf = 2432MHz */ - 0x0B333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 6, Tf = 2437MHz */ - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 7, Tf = 2442MHz */ - 0x0B333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 8, Tf = 2447MHz */ - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 9, Tf = 2452MHz */ - 0x0B333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 10, Tf = 2457MHz */ - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 11, Tf = 2462MHz */ - 0x0B333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 12, Tf = 2467MHz */ - 0x03333100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, /* channel = 13, Tf = 2472MHz */ - 0x06666100 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW /* channel = 14, Tf = 2412M */ -}; - -static unsigned long al2230_power_table[AL2230_PWR_IDX_LEN] = { - 0x04040900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04041900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04042900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04043900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04044900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04045900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04046900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04047900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04048900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04049900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0404A900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0404B900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0404C900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0404D900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0404E900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0404F900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04050900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04051900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04052900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04053900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04054900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04055900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04056900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04057900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04058900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04059900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0405A900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0405B900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0405C900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0405D900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0405E900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0405F900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04060900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04061900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04062900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04063900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04064900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04065900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04066900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04067900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04068900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04069900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0406A900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0406B900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0406C900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0406D900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0406E900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0406F900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04070900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04071900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04072900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04073900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04074900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04075900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04076900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04077900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04078900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x04079900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0407A900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0407B900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0407C900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0407D900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0407E900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW, - 0x0407F900 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW -}; - -/* - * Description: Write to IF/RF, by embedded programming - * - * Parameters: - * In: - * iobase - I/O base address - * dwData - data to write - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ -bool IFRFbWriteEmbedded(struct vnt_private *priv, unsigned long dwData) -{ - void __iomem *iobase = priv->port_offset; - unsigned short ww; - unsigned long dwValue; - - iowrite32((u32)dwData, iobase + MAC_REG_IFREGCTL); - - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - dwValue = ioread32(iobase + MAC_REG_IFREGCTL); - if (dwValue & IFREGCTL_DONE) - break; - } - - if (ww == W_MAX_TIMEOUT) - return false; - - return true; -} - -/* - * Description: AIROHA IFRF chip init function - * - * Parameters: - * In: - * iobase - I/O base address - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ -static bool RFbAL2230Init(struct vnt_private *priv) -{ - void __iomem *iobase = priv->port_offset; - int ii; - bool ret; - - ret = true; - - /* 3-wire control for normal mode */ - iowrite8(0, iobase + MAC_REG_SOFTPWRCTL); - - vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, - (SOFTPWRCTL_SWPECTI | SOFTPWRCTL_TXPEINV)); - /* PLL Off */ - vt6655_mac_word_reg_bits_off(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3); - - /* patch abnormal AL2230 frequency output */ - IFRFbWriteEmbedded(priv, (0x07168700 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW)); - - for (ii = 0; ii < CB_AL2230_INIT_SEQ; ii++) - ret &= IFRFbWriteEmbedded(priv, al2230_init_table[ii]); - MACvTimer0MicroSDelay(priv, 30); /* delay 30 us */ - - /* PLL On */ - vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3); - - MACvTimer0MicroSDelay(priv, 150);/* 150us */ - ret &= IFRFbWriteEmbedded(priv, (0x00d80f00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW)); - MACvTimer0MicroSDelay(priv, 30);/* 30us */ - ret &= IFRFbWriteEmbedded(priv, (0x00780f00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW)); - MACvTimer0MicroSDelay(priv, 30);/* 30us */ - ret &= IFRFbWriteEmbedded(priv, - al2230_init_table[CB_AL2230_INIT_SEQ - 1]); - - vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 | - SOFTPWRCTL_SWPE2 | - SOFTPWRCTL_SWPECTI | - SOFTPWRCTL_TXPEINV)); - - /* 3-wire control for power saving mode */ - iowrite8(PSSIG_WPE3 | PSSIG_WPE2, iobase + MAC_REG_PSPWRSIG); - - return ret; -} - -static bool RFbAL2230SelectChannel(struct vnt_private *priv, unsigned char byChannel) -{ - void __iomem *iobase = priv->port_offset; - bool ret; - - ret = true; - - ret &= IFRFbWriteEmbedded(priv, al2230_channel_table0[byChannel - 1]); - ret &= IFRFbWriteEmbedded(priv, al2230_channel_table1[byChannel - 1]); - - /* Set Channel[7] = 0 to tell H/W channel is changing now. */ - iowrite8(byChannel & 0x7F, iobase + MAC_REG_CHANNEL); - MACvTimer0MicroSDelay(priv, SWITCH_CHANNEL_DELAY_AL2230); - /* Set Channel[7] = 1 to tell H/W channel change is done. */ - iowrite8(byChannel | 0x80, iobase + MAC_REG_CHANNEL); - - return ret; -} - -/* - * Description: RF init function - * - * Parameters: - * In: - * byBBType - * rf_type - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ -bool RFbInit(struct vnt_private *priv) -{ - bool ret = true; - - switch (priv->rf_type) { - case RF_AIROHA: - case RF_AL2230S: - priv->max_pwr_level = AL2230_PWR_IDX_LEN; - ret = RFbAL2230Init(priv); - break; - case RF_NOTHING: - ret = true; - break; - default: - ret = false; - break; - } - return ret; -} - -/* - * Description: Select channel - * - * Parameters: - * In: - * rf_type - * byChannel - Channel number - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ -bool RFbSelectChannel(struct vnt_private *priv, unsigned char rf_type, - u16 byChannel) -{ - bool ret = true; - - switch (rf_type) { - case RF_AIROHA: - case RF_AL2230S: - ret = RFbAL2230SelectChannel(priv, byChannel); - break; - /*{{ RobertYu: 20050104 */ - case RF_NOTHING: - ret = true; - break; - default: - ret = false; - break; - } - return ret; -} - -/* - * Description: Write WakeProgSyn - * - * Parameters: - * In: - * priv - Device Structure - * rf_type - RF type - * channel - Channel number - * - * Return Value: true if succeeded; false if failed. - * - */ -bool rf_write_wake_prog_syn(struct vnt_private *priv, unsigned char rf_type, - u16 channel) -{ - void __iomem *iobase = priv->port_offset; - int i; - unsigned char init_count = 0; - unsigned char sleep_count = 0; - unsigned short idx = MISCFIFO_SYNDATA_IDX; - - iowrite16(0, iobase + MAC_REG_MISCFFNDEX); - switch (rf_type) { - case RF_AIROHA: - case RF_AL2230S: - - if (channel > CB_MAX_CHANNEL_24G) - return false; - - /* Init Reg + Channel Reg (2) */ - init_count = CB_AL2230_INIT_SEQ + 2; - sleep_count = 0; - - for (i = 0; i < CB_AL2230_INIT_SEQ; i++) - MACvSetMISCFifo(priv, idx++, al2230_init_table[i]); - - MACvSetMISCFifo(priv, idx++, al2230_channel_table0[channel - 1]); - MACvSetMISCFifo(priv, idx++, al2230_channel_table1[channel - 1]); - break; - - /* Need to check, PLLON need to be low for channel setting */ - - case RF_NOTHING: - return true; - - default: - return false; - } - - MACvSetMISCFifo(priv, MISCFIFO_SYNINFO_IDX, (unsigned long)MAKEWORD(sleep_count, init_count)); - - return true; -} - -/* - * Description: Set Tx power - * - * Parameters: - * In: - * iobase - I/O base address - * dwRFPowerTable - RF Tx Power Setting - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ -bool RFbSetPower(struct vnt_private *priv, unsigned int rate, u16 uCH) -{ - bool ret; - unsigned char byPwr = 0; - unsigned char byDec = 0; - - if (priv->dwDiagRefCount != 0) - return true; - - if ((uCH < 1) || (uCH > CB_MAX_CHANNEL)) - return false; - - switch (rate) { - case RATE_1M: - case RATE_2M: - case RATE_5M: - case RATE_11M: - if (uCH > CB_MAX_CHANNEL_24G) - return false; - - byPwr = priv->abyCCKPwrTbl[uCH]; - break; - case RATE_6M: - case RATE_9M: - case RATE_12M: - case RATE_18M: - byPwr = priv->abyOFDMPwrTbl[uCH]; - byDec = byPwr + 10; - - if (byDec >= priv->max_pwr_level) - byDec = priv->max_pwr_level - 1; - - byPwr = byDec; - break; - case RATE_24M: - case RATE_36M: - case RATE_48M: - case RATE_54M: - byPwr = priv->abyOFDMPwrTbl[uCH]; - break; - } - - if (priv->cur_pwr == byPwr) - return true; - - ret = RFbRawSetPower(priv, byPwr, rate); - if (ret) - priv->cur_pwr = byPwr; - - return ret; -} - -/* - * Description: Set Tx power - * - * Parameters: - * In: - * iobase - I/O base address - * dwRFPowerTable - RF Tx Power Setting - * Out: - * none - * - * Return Value: true if succeeded; false if failed. - * - */ - -bool RFbRawSetPower(struct vnt_private *priv, unsigned char byPwr, - unsigned int rate) -{ - bool ret = true; - - if (byPwr >= priv->max_pwr_level) - return false; - - switch (priv->rf_type) { - case RF_AIROHA: - ret &= IFRFbWriteEmbedded(priv, al2230_power_table[byPwr]); - if (rate <= RATE_11M) - ret &= IFRFbWriteEmbedded(priv, 0x0001B400 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW); - else - ret &= IFRFbWriteEmbedded(priv, 0x0005A400 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW); - - break; - - case RF_AL2230S: - ret &= IFRFbWriteEmbedded(priv, al2230_power_table[byPwr]); - if (rate <= RATE_11M) { - ret &= IFRFbWriteEmbedded(priv, 0x040C1400 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW); - ret &= IFRFbWriteEmbedded(priv, 0x00299B00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW); - } else { - ret &= IFRFbWriteEmbedded(priv, 0x0005A400 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW); - ret &= IFRFbWriteEmbedded(priv, 0x00099B00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW); - } - - break; - - default: - break; - } - return ret; -} - -/* - * - * Routine Description: - * Translate RSSI to dBm - * - * Parameters: - * In: - * priv - The adapter to be translated - * byCurrRSSI - RSSI to be translated - * Out: - * pdwdbm - Translated dbm number - * - * Return Value: none - * - */ -void -RFvRSSITodBm(struct vnt_private *priv, unsigned char byCurrRSSI, long *pldBm) -{ - unsigned char byIdx = (((byCurrRSSI & 0xC0) >> 6) & 0x03); - long b = (byCurrRSSI & 0x3F); - long a = 0; - unsigned char abyAIROHARF[4] = {0, 18, 0, 40}; - - switch (priv->rf_type) { - case RF_AIROHA: - case RF_AL2230S: - a = abyAIROHARF[byIdx]; - break; - default: - break; - } - - *pldBm = -1 * (a + b * 2); -} - diff --git a/drivers/staging/vt6655/rf.h b/drivers/staging/vt6655/rf.h deleted file mode 100644 index 8eef100c7ef29..0000000000000 --- a/drivers/staging/vt6655/rf.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: - * - * Author: Jerry Chen - * - * Date: Feb. 19, 2004 - * - */ - -#ifndef __RF_H__ -#define __RF_H__ - -#include "device.h" - -/*--------------------- Export Definitions -------------------------*/ -/* - * Baseband RF pair definition in eeprom (Bits 6..0) - */ -#define RF_RFMD2959 0x01 -#define RF_MAXIMAG 0x02 -#define RF_AIROHA 0x03 - -#define RF_UW2451 0x05 -#define RF_MAXIMG 0x06 -#define RF_MAXIM2829 0x07 /* RobertYu: 20041118 */ -#define RF_UW2452 0x08 /* RobertYu: 20041210 */ -#define RF_AIROHA7230 0x0a /* RobertYu: 20050104 */ -#define RF_UW2453 0x0b - -#define RF_VT3226 0x09 -#define RF_AL2230S 0x0e - -#define RF_NOTHING 0x7E -#define RF_EMU 0x80 -#define RF_MASK 0x7F - -#define ZONE_FCC 0 -#define ZONE_MKK1 1 -#define ZONE_ETSI 2 -#define ZONE_IC 3 -#define ZONE_SPAIN 4 -#define ZONE_FRANCE 5 -#define ZONE_MKK 6 -#define ZONE_ISRAEL 7 - -/* [20050104] CB_MAXIM2829_CHANNEL_5G_HIGH, CB_UW2452_CHANNEL_5G_HIGH: 40==>41 */ -#define CB_MAXIM2829_CHANNEL_5G_HIGH 41 /* Index41: channel = 100, Tf = 5500MHz, set the (A3:A0=0101) D6=1 */ -#define CB_UW2452_CHANNEL_5G_HIGH 41 /* [20041210] Index41: channel = 100, Tf = 5500MHz, change VCO2->VCO3 */ - -/*--------------------- Export Classes ----------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -bool IFRFbWriteEmbedded(struct vnt_private *priv, unsigned long dwData); -bool RFbSelectChannel(struct vnt_private *priv, unsigned char rf_type, u16 byChannel); -bool RFbInit(struct vnt_private *priv); -bool rf_write_wake_prog_syn(struct vnt_private *priv, unsigned char rf_type, u16 channel); -bool RFbSetPower(struct vnt_private *priv, unsigned int rate, u16 uCH); -bool RFbRawSetPower(struct vnt_private *priv, unsigned char byPwr, - unsigned int rate); - -void RFvRSSITodBm(struct vnt_private *priv, unsigned char byCurrRSSI, - long *pldBm); - -#endif /* __RF_H__ */ diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c deleted file mode 100644 index 3705cb1e87b63..0000000000000 --- a/drivers/staging/vt6655/rxtx.c +++ /dev/null @@ -1,1462 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: handle WMAC/802.3/802.11 rx & tx functions - * - * Author: Lyndon Chen - * - * Date: May 20, 2003 - * - * Functions: - * s_vGenerateTxParameter - Generate tx dma required parameter. - * vGenerateMACHeader - Translate 802.3 to 802.11 header - * cbGetFragCount - Calculate fragment number count - * csBeacon_xmit - beacon tx function - * csMgmt_xmit - management tx function - * s_cbFillTxBufHead - fulfill tx dma buffer header - * s_uGetDataDuration - get tx data required duration - * s_uFillDataHead- fulfill tx data duration header - * s_uGetRTSCTSDuration- get rtx/cts required duration - * get_rtscts_time- get rts/cts reserved time - * s_uGetTxRsvTime- get frame reserved time - * s_vFillCTSHead- fulfill CTS ctl header - * s_vFillFragParameter- Set fragment ctl parameter. - * s_vFillRTSHead- fulfill RTS ctl header - * s_vFillTxKey- fulfill tx encrypt key - * s_vSWencryption- Software encrypt header - * vDMA0_tx_80211- tx 802.11 frame via dma0 - * vGenerateFIFOHeader- Generate tx FIFO ctl header - * - * Revision History: - * - */ - -#include "device.h" -#include "rxtx.h" -#include "card.h" -#include "mac.h" -#include "baseband.h" -#include "rf.h" - -/*--------------------- Static Definitions -------------------------*/ - -/*--------------------- Static Classes ----------------------------*/ - -/*--------------------- Static Variables --------------------------*/ - -/*--------------------- Static Functions --------------------------*/ - -/*--------------------- Static Definitions -------------------------*/ -/* if packet size < 256 -> in-direct send - * vpacket size >= 256 -> direct send - */ -#define CRITICAL_PACKET_LEN 256 - -static const unsigned short time_stamp_off[2][MAX_RATE] = { - {384, 288, 226, 209, 54, 43, 37, 31, 28, 25, 24, 23}, /* Long Preamble */ - {384, 192, 130, 113, 54, 43, 37, 31, 28, 25, 24, 23}, /* Short Preamble */ -}; - -static const unsigned short fb_opt0[2][5] = { - {RATE_12M, RATE_18M, RATE_24M, RATE_36M, RATE_48M}, /* fallback_rate0 */ - {RATE_12M, RATE_12M, RATE_18M, RATE_24M, RATE_36M}, /* fallback_rate1 */ -}; - -static const unsigned short fb_opt1[2][5] = { - {RATE_12M, RATE_18M, RATE_24M, RATE_24M, RATE_36M}, /* fallback_rate0 */ - {RATE_6M, RATE_6M, RATE_12M, RATE_12M, RATE_18M}, /* fallback_rate1 */ -}; - -#define RTSDUR_BB 0 -#define RTSDUR_BA 1 -#define RTSDUR_AA 2 -#define CTSDUR_BA 3 -#define RTSDUR_BA_F0 4 -#define RTSDUR_AA_F0 5 -#define RTSDUR_BA_F1 6 -#define RTSDUR_AA_F1 7 -#define CTSDUR_BA_F0 8 -#define CTSDUR_BA_F1 9 -#define DATADUR_B 10 -#define DATADUR_A 11 -#define DATADUR_A_F0 12 -#define DATADUR_A_F1 13 - -/*--------------------- Static Functions --------------------------*/ -static -void -s_vFillRTSHead( - struct vnt_private *pDevice, - unsigned char byPktType, - void *pvRTS, - unsigned int cbFrameLength, - bool bNeedAck, - bool bDisCRC, - struct ieee80211_hdr *hdr, - unsigned short wCurrentRate, - unsigned char byFBOption -); - -static -void -s_vGenerateTxParameter( - struct vnt_private *pDevice, - unsigned char byPktType, - struct vnt_tx_fifo_head *, - void *pvRrvTime, - void *pvRTS, - void *pvCTS, - unsigned int cbFrameSize, - bool bNeedACK, - unsigned int uDMAIdx, - void *psEthHeader, - unsigned short wCurrentRate -); - -static unsigned int -s_cbFillTxBufHead(struct vnt_private *pDevice, unsigned char byPktType, - unsigned char *pbyTxBufferAddr, - unsigned int uDMAIdx, struct vnt_tx_desc *pHeadTD, - unsigned int uNodeIndex); - -static -__le16 -s_uFillDataHead( - struct vnt_private *pDevice, - unsigned char byPktType, - void *pTxDataHead, - unsigned int cbFrameLength, - unsigned int uDMAIdx, - bool bNeedAck, - unsigned int uFragIdx, - unsigned int cbLastFragmentSize, - unsigned int uMACfragNum, - unsigned char byFBOption, - unsigned short wCurrentRate, - bool is_pspoll -); - -/*--------------------- Export Variables --------------------------*/ - -static __le16 vnt_time_stamp_off(struct vnt_private *priv, u16 rate) -{ - return cpu_to_le16(time_stamp_off[priv->preamble_type % 2] - [rate % MAX_RATE]); -} - -/* byPktType : PK_TYPE_11A 0 - * PK_TYPE_11B 1 - * PK_TYPE_11GB 2 - * PK_TYPE_11GA 3 - */ -static -unsigned int -s_uGetTxRsvTime( - struct vnt_private *pDevice, - unsigned char byPktType, - unsigned int cbFrameLength, - unsigned short wRate, - bool bNeedAck -) -{ - unsigned int uDataTime, uAckTime; - - uDataTime = bb_get_frame_time(pDevice->preamble_type, byPktType, cbFrameLength, wRate); - - if (!bNeedAck) - return uDataTime; - - /* - * CCK mode - 11b - * OFDM mode - 11g 2.4G & 11a 5G - */ - uAckTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, - byPktType == PK_TYPE_11B ? - pDevice->byTopCCKBasicRate : - pDevice->byTopOFDMBasicRate); - - return uDataTime + pDevice->uSIFS + uAckTime; -} - -static __le16 vnt_rxtx_rsvtime_le16(struct vnt_private *priv, u8 pkt_type, - u32 frame_length, u16 rate, bool need_ack) -{ - return cpu_to_le16((u16)s_uGetTxRsvTime(priv, pkt_type, - frame_length, rate, need_ack)); -} - -/* byFreqType: 0=>5GHZ 1=>2.4GHZ */ -static __le16 get_rtscts_time(struct vnt_private *priv, - unsigned char rts_rsvtype, - unsigned char pkt_type, - unsigned int frame_length, - unsigned short current_rate) -{ - unsigned int rrv_time = 0; - unsigned int rts_time = 0; - unsigned int cts_time = 0; - unsigned int ack_time = 0; - unsigned int data_time = 0; - - data_time = bb_get_frame_time(priv->preamble_type, pkt_type, frame_length, current_rate); - if (rts_rsvtype == 0) { /* RTSTxRrvTime_bb */ - rts_time = bb_get_frame_time(priv->preamble_type, pkt_type, 20, priv->byTopCCKBasicRate); - ack_time = bb_get_frame_time(priv->preamble_type, pkt_type, 14, priv->byTopCCKBasicRate); - cts_time = ack_time; - } else if (rts_rsvtype == 1) { /* RTSTxRrvTime_ba, only in 2.4GHZ */ - rts_time = bb_get_frame_time(priv->preamble_type, pkt_type, 20, priv->byTopCCKBasicRate); - cts_time = bb_get_frame_time(priv->preamble_type, pkt_type, 14, priv->byTopCCKBasicRate); - ack_time = bb_get_frame_time(priv->preamble_type, pkt_type, 14, priv->byTopOFDMBasicRate); - } else if (rts_rsvtype == 2) { /* RTSTxRrvTime_aa */ - rts_time = bb_get_frame_time(priv->preamble_type, pkt_type, 20, priv->byTopOFDMBasicRate); - ack_time = bb_get_frame_time(priv->preamble_type, pkt_type, 14, priv->byTopOFDMBasicRate); - cts_time = ack_time; - } else if (rts_rsvtype == 3) { /* CTSTxRrvTime_ba, only in 2.4GHZ */ - cts_time = bb_get_frame_time(priv->preamble_type, pkt_type, 14, priv->byTopCCKBasicRate); - ack_time = bb_get_frame_time(priv->preamble_type, pkt_type, 14, priv->byTopOFDMBasicRate); - rrv_time = cts_time + ack_time + data_time + 2 * priv->uSIFS; - return cpu_to_le16((u16)rrv_time); - } - - /* RTSRrvTime */ - rrv_time = rts_time + cts_time + ack_time + data_time + 3 * priv->uSIFS; - return cpu_to_le16((u16)rrv_time); -} - -/* byFreqType 0: 5GHz, 1:2.4Ghz */ -static -unsigned int -s_uGetDataDuration( - struct vnt_private *pDevice, - unsigned char byDurType, - unsigned int cbFrameLength, - unsigned char byPktType, - unsigned short wRate, - bool bNeedAck, - unsigned int uFragIdx, - unsigned int cbLastFragmentSize, - unsigned int uMACfragNum, - unsigned char byFBOption -) -{ - bool bLastFrag = false; - unsigned int uAckTime = 0, uNextPktTime = 0, len; - - if (uFragIdx == (uMACfragNum - 1)) - bLastFrag = true; - - if (uFragIdx == (uMACfragNum - 2)) - len = cbLastFragmentSize; - else - len = cbFrameLength; - - switch (byDurType) { - case DATADUR_B: /* DATADUR_B */ - if (bNeedAck) { - uAckTime = bb_get_frame_time(pDevice->preamble_type, - byPktType, 14, - pDevice->byTopCCKBasicRate); - } - /* Non Frag or Last Frag */ - if ((uMACfragNum == 1) || bLastFrag) { - if (!bNeedAck) - return 0; - } else { - /* First Frag or Mid Frag */ - uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, - len, wRate, bNeedAck); - } - - return pDevice->uSIFS + uAckTime + uNextPktTime; - - case DATADUR_A: /* DATADUR_A */ - if (bNeedAck) { - uAckTime = bb_get_frame_time(pDevice->preamble_type, - byPktType, 14, - pDevice->byTopOFDMBasicRate); - } - /* Non Frag or Last Frag */ - if ((uMACfragNum == 1) || bLastFrag) { - if (!bNeedAck) - return 0; - } else { - /* First Frag or Mid Frag */ - uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, - len, wRate, bNeedAck); - } - - return pDevice->uSIFS + uAckTime + uNextPktTime; - - case DATADUR_A_F0: /* DATADUR_A_F0 */ - case DATADUR_A_F1: /* DATADUR_A_F1 */ - if (bNeedAck) { - uAckTime = bb_get_frame_time(pDevice->preamble_type, - byPktType, 14, - pDevice->byTopOFDMBasicRate); - } - /* Non Frag or Last Frag */ - if ((uMACfragNum == 1) || bLastFrag) { - if (!bNeedAck) - return 0; - } else { - /* First Frag or Mid Frag */ - if (wRate < RATE_18M) - wRate = RATE_18M; - else if (wRate > RATE_54M) - wRate = RATE_54M; - - wRate -= RATE_18M; - - if (byFBOption == AUTO_FB_0) - wRate = fb_opt0[FB_RATE0][wRate]; - else - wRate = fb_opt1[FB_RATE0][wRate]; - - uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, - len, wRate, bNeedAck); - } - - return pDevice->uSIFS + uAckTime + uNextPktTime; - - default: - break; - } - - return 0; -} - -/* byFreqType: 0=>5GHZ 1=>2.4GHZ */ -static -__le16 -s_uGetRTSCTSDuration( - struct vnt_private *pDevice, - unsigned char byDurType, - unsigned int cbFrameLength, - unsigned char byPktType, - unsigned short wRate, - bool bNeedAck, - unsigned char byFBOption -) -{ - unsigned int uCTSTime = 0, uDurTime = 0; - - switch (byDurType) { - case RTSDUR_BB: /* RTSDuration_bb */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopCCKBasicRate); - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck); - break; - - case RTSDUR_BA: /* RTSDuration_ba */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopCCKBasicRate); - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck); - break; - - case RTSDUR_AA: /* RTSDuration_aa */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopOFDMBasicRate); - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck); - break; - - case CTSDUR_BA: /* CTSDuration_ba */ - uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck); - break; - - case RTSDUR_BA_F0: /* RTSDuration_ba_f0 */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopCCKBasicRate); - if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt0[FB_RATE0][wRate - RATE_18M], bNeedAck); - else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt1[FB_RATE0][wRate - RATE_18M], bNeedAck); - - break; - - case RTSDUR_AA_F0: /* RTSDuration_aa_f0 */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopOFDMBasicRate); - if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt0[FB_RATE0][wRate - RATE_18M], bNeedAck); - else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt1[FB_RATE0][wRate - RATE_18M], bNeedAck); - - break; - - case RTSDUR_BA_F1: /* RTSDuration_ba_f1 */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopCCKBasicRate); - if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt0[FB_RATE1][wRate - RATE_18M], bNeedAck); - else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt1[FB_RATE1][wRate - RATE_18M], bNeedAck); - - break; - - case RTSDUR_AA_F1: /* RTSDuration_aa_f1 */ - uCTSTime = bb_get_frame_time(pDevice->preamble_type, byPktType, 14, pDevice->byTopOFDMBasicRate); - if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt0[FB_RATE1][wRate - RATE_18M], bNeedAck); - else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt1[FB_RATE1][wRate - RATE_18M], bNeedAck); - - break; - - case CTSDUR_BA_F0: /* CTSDuration_ba_f0 */ - if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt0[FB_RATE0][wRate - RATE_18M], bNeedAck); - else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt1[FB_RATE0][wRate - RATE_18M], bNeedAck); - - break; - - case CTSDUR_BA_F1: /* CTSDuration_ba_f1 */ - if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt0[FB_RATE1][wRate - RATE_18M], bNeedAck); - else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M)) - uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, fb_opt1[FB_RATE1][wRate - RATE_18M], bNeedAck); - - break; - - default: - break; - } - - return cpu_to_le16((u16)uDurTime); -} - -static -__le16 -s_uFillDataHead( - struct vnt_private *pDevice, - unsigned char byPktType, - void *pTxDataHead, - unsigned int cbFrameLength, - unsigned int uDMAIdx, - bool bNeedAck, - unsigned int uFragIdx, - unsigned int cbLastFragmentSize, - unsigned int uMACfragNum, - unsigned char byFBOption, - unsigned short wCurrentRate, - bool is_pspoll -) -{ - struct vnt_tx_datahead_ab *buf = pTxDataHead; - - if (!pTxDataHead) - return 0; - - if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) { - /* Auto Fallback */ - struct vnt_tx_datahead_g_fb *buf = pTxDataHead; - - if (byFBOption == AUTO_FB_NONE) { - struct vnt_tx_datahead_g *buf = pTxDataHead; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate, - byPktType, &buf->a); - - vnt_get_phy_field(pDevice, cbFrameLength, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->b); - - if (is_pspoll) { - __le16 dur = cpu_to_le16(pDevice->current_aid | BIT(14) | BIT(15)); - - buf->duration_a = dur; - buf->duration_b = dur; - } else { - /* Get Duration and TimeStamp */ - buf->duration_a = - cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, - byPktType, wCurrentRate, bNeedAck, uFragIdx, - cbLastFragmentSize, uMACfragNum, - byFBOption)); - buf->duration_b = - cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_B, cbFrameLength, - PK_TYPE_11B, pDevice->byTopCCKBasicRate, - bNeedAck, uFragIdx, cbLastFragmentSize, - uMACfragNum, byFBOption)); - } - - buf->time_stamp_off_a = vnt_time_stamp_off(pDevice, wCurrentRate); - buf->time_stamp_off_b = vnt_time_stamp_off(pDevice, pDevice->byTopCCKBasicRate); - - return buf->duration_a; - } - - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate, - byPktType, &buf->a); - - vnt_get_phy_field(pDevice, cbFrameLength, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->b); - /* Get Duration and TimeStamp */ - buf->duration_a = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - buf->duration_b = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_B, cbFrameLength, PK_TYPE_11B, - pDevice->byTopCCKBasicRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - buf->duration_a_f0 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F0, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - buf->duration_a_f1 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F1, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - - buf->time_stamp_off_a = vnt_time_stamp_off(pDevice, wCurrentRate); - buf->time_stamp_off_b = vnt_time_stamp_off(pDevice, pDevice->byTopCCKBasicRate); - - return buf->duration_a; - /* if (byFBOption == AUTO_FB_NONE) */ - } else if (byPktType == PK_TYPE_11A) { - struct vnt_tx_datahead_ab *buf = pTxDataHead; - - if (byFBOption != AUTO_FB_NONE) { - /* Auto Fallback */ - struct vnt_tx_datahead_a_fb *buf = pTxDataHead; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate, - byPktType, &buf->a); - - /* Get Duration and TimeStampOff */ - buf->duration = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - buf->duration_f0 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F0, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - buf->duration_f1 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F1, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption)); - buf->time_stamp_off = vnt_time_stamp_off(pDevice, wCurrentRate); - return buf->duration; - } - - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate, - byPktType, &buf->ab); - - if (is_pspoll) { - __le16 dur = cpu_to_le16(pDevice->current_aid | BIT(14) | BIT(15)); - - buf->duration = dur; - } else { - /* Get Duration and TimeStampOff */ - buf->duration = - cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, - cbLastFragmentSize, uMACfragNum, - byFBOption)); - } - - buf->time_stamp_off = vnt_time_stamp_off(pDevice, wCurrentRate); - return buf->duration; - } - - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate, - byPktType, &buf->ab); - - if (is_pspoll) { - __le16 dur = cpu_to_le16(pDevice->current_aid | BIT(14) | BIT(15)); - - buf->duration = dur; - } else { - /* Get Duration and TimeStampOff */ - buf->duration = - cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_B, cbFrameLength, byPktType, - wCurrentRate, bNeedAck, uFragIdx, - cbLastFragmentSize, uMACfragNum, - byFBOption)); - } - - buf->time_stamp_off = vnt_time_stamp_off(pDevice, wCurrentRate); - return buf->duration; -} - -static -void -s_vFillRTSHead( - struct vnt_private *pDevice, - unsigned char byPktType, - void *pvRTS, - unsigned int cbFrameLength, - bool bNeedAck, - bool bDisCRC, - struct ieee80211_hdr *hdr, - unsigned short wCurrentRate, - unsigned char byFBOption -) -{ - unsigned int uRTSFrameLen = 20; - - if (!pvRTS) - return; - - if (bDisCRC) { - /* When CRCDIS bit is on, H/W forgot to generate FCS for - * RTS frame, in this case we need to decrease its length by 4. - */ - uRTSFrameLen -= 4; - } - - /* Note: So far RTSHead doesn't appear in ATIM & Beacom DMA, - * so we don't need to take them into account. - * Otherwise, we need to modify codes for them. - */ - if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) { - if (byFBOption == AUTO_FB_NONE) { - struct vnt_rts_g *buf = pvRTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->b); - - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopOFDMBasicRate, - byPktType, &buf->a); - /* Get Duration */ - buf->duration_bb = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BB, - cbFrameLength, PK_TYPE_11B, - pDevice->byTopCCKBasicRate, - bNeedAck, byFBOption); - buf->duration_aa = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->duration_ba = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - - buf->data.duration = buf->duration_aa; - /* Get RTS Frame body */ - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_RTS); - - ether_addr_copy(buf->data.ra, hdr->addr1); - ether_addr_copy(buf->data.ta, hdr->addr2); - } else { - struct vnt_rts_g_fb *buf = pvRTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->b); - - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopOFDMBasicRate, - byPktType, &buf->a); - /* Get Duration */ - buf->duration_bb = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BB, - cbFrameLength, PK_TYPE_11B, - pDevice->byTopCCKBasicRate, - bNeedAck, byFBOption); - buf->duration_aa = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->duration_ba = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->rts_duration_ba_f0 = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BA_F0, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->rts_duration_aa_f0 = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F0, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->rts_duration_ba_f1 = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BA_F1, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->rts_duration_aa_f1 = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F1, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->data.duration = buf->duration_aa; - /* Get RTS Frame body */ - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_RTS); - - ether_addr_copy(buf->data.ra, hdr->addr1); - ether_addr_copy(buf->data.ta, hdr->addr2); - } /* if (byFBOption == AUTO_FB_NONE) */ - } else if (byPktType == PK_TYPE_11A) { - if (byFBOption == AUTO_FB_NONE) { - struct vnt_rts_ab *buf = pvRTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopOFDMBasicRate, - byPktType, &buf->ab); - /* Get Duration */ - buf->duration = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->data.duration = buf->duration; - /* Get RTS Frame body */ - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_RTS); - - ether_addr_copy(buf->data.ra, hdr->addr1); - ether_addr_copy(buf->data.ta, hdr->addr2); - } else { - struct vnt_rts_a_fb *buf = pvRTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopOFDMBasicRate, - byPktType, &buf->a); - /* Get Duration */ - buf->duration = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->rts_duration_f0 = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F0, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->rts_duration_f1 = - s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F1, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - buf->data.duration = buf->duration; - /* Get RTS Frame body */ - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_RTS); - - ether_addr_copy(buf->data.ra, hdr->addr1); - ether_addr_copy(buf->data.ta, hdr->addr2); - } - } else if (byPktType == PK_TYPE_11B) { - struct vnt_rts_ab *buf = pvRTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uRTSFrameLen, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->ab); - /* Get Duration */ - buf->duration = - s_uGetRTSCTSDuration(pDevice, RTSDUR_BB, cbFrameLength, - byPktType, wCurrentRate, bNeedAck, - byFBOption); - - buf->data.duration = buf->duration; - /* Get RTS Frame body */ - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); - - ether_addr_copy(buf->data.ra, hdr->addr1); - ether_addr_copy(buf->data.ta, hdr->addr2); - } -} - -static -void -s_vFillCTSHead( - struct vnt_private *pDevice, - unsigned int uDMAIdx, - unsigned char byPktType, - void *pvCTS, - unsigned int cbFrameLength, - bool bNeedAck, - bool bDisCRC, - unsigned short wCurrentRate, - unsigned char byFBOption -) -{ - unsigned int uCTSFrameLen = 14; - - if (!pvCTS) - return; - - if (bDisCRC) { - /* When CRCDIS bit is on, H/W forgot to generate FCS for - * CTS frame, in this case we need to decrease its length by 4. - */ - uCTSFrameLen -= 4; - } - - if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) { - if (byFBOption != AUTO_FB_NONE && uDMAIdx != TYPE_ATIMDMA && uDMAIdx != TYPE_BEACONDMA) { - /* Auto Fall back */ - struct vnt_cts_fb *buf = pvCTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uCTSFrameLen, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->b); - - buf->duration_ba = - s_uGetRTSCTSDuration(pDevice, CTSDUR_BA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - - /* Get CTSDuration_ba_f0 */ - buf->cts_duration_ba_f0 = - s_uGetRTSCTSDuration(pDevice, CTSDUR_BA_F0, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - - /* Get CTSDuration_ba_f1 */ - buf->cts_duration_ba_f1 = - s_uGetRTSCTSDuration(pDevice, CTSDUR_BA_F1, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - - /* Get CTS Frame body */ - buf->data.duration = buf->duration_ba; - - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_CTS); - - buf->reserved2 = 0x0; - - ether_addr_copy(buf->data.ra, - pDevice->abyCurrentNetAddr); - } else { /* if (byFBOption != AUTO_FB_NONE && uDMAIdx != TYPE_ATIMDMA && uDMAIdx != TYPE_BEACONDMA) */ - struct vnt_cts *buf = pvCTS; - /* Get SignalField, ServiceField & Length */ - vnt_get_phy_field(pDevice, uCTSFrameLen, - pDevice->byTopCCKBasicRate, - PK_TYPE_11B, &buf->b); - - /* Get CTSDuration_ba */ - buf->duration_ba = - s_uGetRTSCTSDuration(pDevice, CTSDUR_BA, - cbFrameLength, byPktType, - wCurrentRate, bNeedAck, - byFBOption); - - /* Get CTS Frame body */ - buf->data.duration = buf->duration_ba; - - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_CTS); - - buf->reserved2 = 0x0; - ether_addr_copy(buf->data.ra, - pDevice->abyCurrentNetAddr); - } - } -} - -/* - * - * Description: - * Generate FIFO control for MAC & Baseband controller - * - * Parameters: - * In: - * pDevice - Pointer to adapter - * pTxDataHead - Transmit Data Buffer - * pTxBufHead - pTxBufHead - * pvRrvTime - pvRrvTime - * pvRTS - RTS Buffer - * pCTS - CTS Buffer - * cbFrameSize - Transmit Data Length (Hdr+Payload+FCS) - * bNeedACK - If need ACK - * uDescIdx - Desc Index - * Out: - * none - * - * Return Value: none - * - - - * unsigned int cbFrameSize, Hdr+Payload+FCS - */ -static -void -s_vGenerateTxParameter( - struct vnt_private *pDevice, - unsigned char byPktType, - struct vnt_tx_fifo_head *tx_buffer_head, - void *pvRrvTime, - void *pvRTS, - void *pvCTS, - unsigned int cbFrameSize, - bool bNeedACK, - unsigned int uDMAIdx, - void *psEthHeader, - unsigned short wCurrentRate -) -{ - u16 fifo_ctl = le16_to_cpu(tx_buffer_head->fifo_ctl); - bool bDisCRC = false; - unsigned char byFBOption = AUTO_FB_NONE; - - tx_buffer_head->current_rate = cpu_to_le16(wCurrentRate); - - if (fifo_ctl & FIFOCTL_CRCDIS) - bDisCRC = true; - - if (fifo_ctl & FIFOCTL_AUTO_FB_0) - byFBOption = AUTO_FB_0; - else if (fifo_ctl & FIFOCTL_AUTO_FB_1) - byFBOption = AUTO_FB_1; - - if (!pvRrvTime) - return; - - if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) { - if (pvRTS) { /* RTS_need */ - /* Fill RsvTime */ - struct vnt_rrv_time_rts *buf = pvRrvTime; - - buf->rts_rrv_time_aa = get_rtscts_time(pDevice, 2, byPktType, cbFrameSize, wCurrentRate); - buf->rts_rrv_time_ba = get_rtscts_time(pDevice, 1, byPktType, cbFrameSize, wCurrentRate); - buf->rts_rrv_time_bb = get_rtscts_time(pDevice, 0, byPktType, cbFrameSize, wCurrentRate); - buf->rrv_time_a = vnt_rxtx_rsvtime_le16(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK); - buf->rrv_time_b = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, pDevice->byTopCCKBasicRate, bNeedACK); - - s_vFillRTSHead(pDevice, byPktType, pvRTS, cbFrameSize, bNeedACK, bDisCRC, psEthHeader, wCurrentRate, byFBOption); - } else {/* RTS_needless, PCF mode */ - struct vnt_rrv_time_cts *buf = pvRrvTime; - - buf->rrv_time_a = vnt_rxtx_rsvtime_le16(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK); - buf->rrv_time_b = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, pDevice->byTopCCKBasicRate, bNeedACK); - buf->cts_rrv_time_ba = get_rtscts_time(pDevice, 3, byPktType, cbFrameSize, wCurrentRate); - - /* Fill CTS */ - s_vFillCTSHead(pDevice, uDMAIdx, byPktType, pvCTS, cbFrameSize, bNeedACK, bDisCRC, wCurrentRate, byFBOption); - } - } else if (byPktType == PK_TYPE_11A) { - if (pvRTS) {/* RTS_need, non PCF mode */ - struct vnt_rrv_time_ab *buf = pvRrvTime; - - buf->rts_rrv_time = get_rtscts_time(pDevice, 2, byPktType, cbFrameSize, wCurrentRate); - buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK); - - /* Fill RTS */ - s_vFillRTSHead(pDevice, byPktType, pvRTS, cbFrameSize, bNeedACK, bDisCRC, psEthHeader, wCurrentRate, byFBOption); - } else if (!pvRTS) {/* RTS_needless, non PCF mode */ - struct vnt_rrv_time_ab *buf = pvRrvTime; - - buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11A, cbFrameSize, wCurrentRate, bNeedACK); - } - } else if (byPktType == PK_TYPE_11B) { - if (pvRTS) {/* RTS_need, non PCF mode */ - struct vnt_rrv_time_ab *buf = pvRrvTime; - - buf->rts_rrv_time = get_rtscts_time(pDevice, 0, byPktType, cbFrameSize, wCurrentRate); - buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, wCurrentRate, bNeedACK); - - /* Fill RTS */ - s_vFillRTSHead(pDevice, byPktType, pvRTS, cbFrameSize, bNeedACK, bDisCRC, psEthHeader, wCurrentRate, byFBOption); - } else { /* RTS_needless, non PCF mode */ - struct vnt_rrv_time_ab *buf = pvRrvTime; - - buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, wCurrentRate, bNeedACK); - } - } -} - -static unsigned int -s_cbFillTxBufHead(struct vnt_private *pDevice, unsigned char byPktType, - unsigned char *pbyTxBufferAddr, - unsigned int uDMAIdx, struct vnt_tx_desc *pHeadTD, - unsigned int is_pspoll) -{ - struct vnt_td_info *td_info = pHeadTD->td_info; - struct sk_buff *skb = td_info->skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct vnt_tx_fifo_head *tx_buffer_head = - (struct vnt_tx_fifo_head *)td_info->buf; - u16 fifo_ctl = le16_to_cpu(tx_buffer_head->fifo_ctl); - unsigned int cbFrameSize; - __le16 uDuration; - unsigned char *pbyBuffer; - unsigned int uLength = 0; - unsigned int cbMICHDR = 0; - unsigned int uMACfragNum = 1; - unsigned int uPadding = 0; - unsigned int cbReqCount = 0; - bool bNeedACK = (bool)(fifo_ctl & FIFOCTL_NEEDACK); - bool bRTS = (bool)(fifo_ctl & FIFOCTL_RTS); - struct vnt_tx_desc *ptdCurr; - unsigned int cbHeaderLength = 0; - void *pvRrvTime = NULL; - struct vnt_mic_hdr *pMICHDR = NULL; - void *pvRTS = NULL; - void *pvCTS = NULL; - void *pvTxDataHd = NULL; - unsigned short wTxBufSize; /* FFinfo size */ - unsigned char byFBOption = AUTO_FB_NONE; - - cbFrameSize = skb->len + 4; - - if (info->control.hw_key) { - switch (info->control.hw_key->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - cbMICHDR = sizeof(struct vnt_mic_hdr); - break; - default: - break; - } - - cbFrameSize += info->control.hw_key->icv_len; - - if (pDevice->local_id > REV_ID_VT3253_A1) { - /* MAC Header should be padding 0 to DW alignment. */ - uPadding = 4 - (ieee80211_get_hdrlen_from_skb(skb) % 4); - uPadding %= 4; - } - } - - /* - * Use for AUTO FALL BACK - */ - if (fifo_ctl & FIFOCTL_AUTO_FB_0) - byFBOption = AUTO_FB_0; - else if (fifo_ctl & FIFOCTL_AUTO_FB_1) - byFBOption = AUTO_FB_1; - - /* Set RrvTime/RTS/CTS Buffer */ - wTxBufSize = sizeof(struct vnt_tx_fifo_head); - if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {/* 802.11g packet */ - - if (byFBOption == AUTO_FB_NONE) { - if (bRTS) {/* RTS_need */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts)); - pvRTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) + cbMICHDR); - pvCTS = NULL; - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) + - cbMICHDR + sizeof(struct vnt_rts_g)); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_rts) + - cbMICHDR + sizeof(struct vnt_rts_g) + - sizeof(struct vnt_tx_datahead_g); - } else { /* RTS_needless */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts)); - pvRTS = NULL; - pvCTS = (void *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts) + cbMICHDR); - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + - sizeof(struct vnt_rrv_time_cts) + cbMICHDR + sizeof(struct vnt_cts)); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_cts) + - cbMICHDR + sizeof(struct vnt_cts) + sizeof(struct vnt_tx_datahead_g); - } - } else { - /* Auto Fall Back */ - if (bRTS) {/* RTS_need */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts)); - pvRTS = (void *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) + cbMICHDR); - pvCTS = NULL; - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) + - cbMICHDR + sizeof(struct vnt_rts_g_fb)); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_rts) + - cbMICHDR + sizeof(struct vnt_rts_g_fb) + sizeof(struct vnt_tx_datahead_g_fb); - } else { /* RTS_needless */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts)); - pvRTS = NULL; - pvCTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts) + cbMICHDR); - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts) + - cbMICHDR + sizeof(struct vnt_cts_fb)); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_cts) + - cbMICHDR + sizeof(struct vnt_cts_fb) + sizeof(struct vnt_tx_datahead_g_fb); - } - } /* Auto Fall Back */ - } else {/* 802.11a/b packet */ - - if (byFBOption == AUTO_FB_NONE) { - if (bRTS) { - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab)); - pvRTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR); - pvCTS = NULL; - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + - sizeof(struct vnt_rrv_time_ab) + cbMICHDR + sizeof(struct vnt_rts_ab)); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) + - cbMICHDR + sizeof(struct vnt_rts_ab) + sizeof(struct vnt_tx_datahead_ab); - } else { /* RTS_needless, need MICHDR */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab)); - pvRTS = NULL; - pvCTS = NULL; - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) + - cbMICHDR + sizeof(struct vnt_tx_datahead_ab); - } - } else { - /* Auto Fall Back */ - if (bRTS) { /* RTS_need */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab)); - pvRTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR); - pvCTS = NULL; - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + - sizeof(struct vnt_rrv_time_ab) + cbMICHDR + sizeof(struct vnt_rts_a_fb)); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) + - cbMICHDR + sizeof(struct vnt_rts_a_fb) + sizeof(struct vnt_tx_datahead_a_fb); - } else { /* RTS_needless */ - pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize); - pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab)); - pvRTS = NULL; - pvCTS = NULL; - pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR); - cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) + - cbMICHDR + sizeof(struct vnt_tx_datahead_a_fb); - } - } /* Auto Fall Back */ - } - - td_info->mic_hdr = pMICHDR; - - memset((void *)(pbyTxBufferAddr + wTxBufSize), 0, (cbHeaderLength - wTxBufSize)); - - /* Fill FIFO,RrvTime,RTS,and CTS */ - s_vGenerateTxParameter(pDevice, byPktType, tx_buffer_head, pvRrvTime, pvRTS, pvCTS, - cbFrameSize, bNeedACK, uDMAIdx, hdr, pDevice->wCurrentRate); - /* Fill DataHead */ - uDuration = s_uFillDataHead(pDevice, byPktType, pvTxDataHd, cbFrameSize, uDMAIdx, bNeedACK, - 0, 0, uMACfragNum, byFBOption, pDevice->wCurrentRate, is_pspoll); - - hdr->duration_id = uDuration; - - cbReqCount = cbHeaderLength + uPadding + skb->len; - pbyBuffer = (unsigned char *)pHeadTD->td_info->buf; - uLength = cbHeaderLength + uPadding; - - /* Copy the Packet into a tx Buffer */ - memcpy((pbyBuffer + uLength), skb->data, skb->len); - - ptdCurr = pHeadTD; - - ptdCurr->td_info->req_count = (u16)cbReqCount; - - return cbHeaderLength; -} - -static void vnt_fill_txkey(struct ieee80211_hdr *hdr, u8 *key_buffer, - struct ieee80211_key_conf *tx_key, - struct sk_buff *skb, u16 payload_len, - struct vnt_mic_hdr *mic_hdr) -{ - u64 pn64; - u8 *iv = ((u8 *)hdr + ieee80211_get_hdrlen_from_skb(skb)); - - /* strip header and icv len from payload */ - payload_len -= ieee80211_get_hdrlen_from_skb(skb); - payload_len -= tx_key->icv_len; - - switch (tx_key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - memcpy(key_buffer, iv, 3); - memcpy(key_buffer + 3, tx_key->key, tx_key->keylen); - - if (tx_key->keylen == WLAN_KEY_LEN_WEP40) { - memcpy(key_buffer + 8, iv, 3); - memcpy(key_buffer + 11, - tx_key->key, WLAN_KEY_LEN_WEP40); - } - - break; - case WLAN_CIPHER_SUITE_TKIP: - ieee80211_get_tkip_p2k(tx_key, skb, key_buffer); - - break; - case WLAN_CIPHER_SUITE_CCMP: - - if (!mic_hdr) - return; - - mic_hdr->id = 0x59; - mic_hdr->payload_len = cpu_to_be16(payload_len); - ether_addr_copy(mic_hdr->mic_addr2, hdr->addr2); - - pn64 = atomic64_read(&tx_key->tx_pn); - mic_hdr->ccmp_pn[5] = pn64; - mic_hdr->ccmp_pn[4] = pn64 >> 8; - mic_hdr->ccmp_pn[3] = pn64 >> 16; - mic_hdr->ccmp_pn[2] = pn64 >> 24; - mic_hdr->ccmp_pn[1] = pn64 >> 32; - mic_hdr->ccmp_pn[0] = pn64 >> 40; - - if (ieee80211_has_a4(hdr->frame_control)) - mic_hdr->hlen = cpu_to_be16(28); - else - mic_hdr->hlen = cpu_to_be16(22); - - ether_addr_copy(mic_hdr->addr1, hdr->addr1); - ether_addr_copy(mic_hdr->addr2, hdr->addr2); - ether_addr_copy(mic_hdr->addr3, hdr->addr3); - - mic_hdr->frame_control = cpu_to_le16( - le16_to_cpu(hdr->frame_control) & 0xc78f); - mic_hdr->seq_ctrl = cpu_to_le16( - le16_to_cpu(hdr->seq_ctrl) & 0xf); - - if (ieee80211_has_a4(hdr->frame_control)) - ether_addr_copy(mic_hdr->addr4, hdr->addr4); - - memcpy(key_buffer, tx_key->key, WLAN_KEY_LEN_CCMP); - - break; - default: - break; - } -} - -int vnt_generate_fifo_header(struct vnt_private *priv, u32 dma_idx, - struct vnt_tx_desc *head_td, struct sk_buff *skb) -{ - struct vnt_td_info *td_info = head_td->td_info; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *tx_rate = &info->control.rates[0]; - struct ieee80211_rate *rate; - struct ieee80211_key_conf *tx_key; - struct ieee80211_hdr *hdr; - struct vnt_tx_fifo_head *tx_buffer_head = - (struct vnt_tx_fifo_head *)td_info->buf; - u16 tx_body_size = skb->len, current_rate; - u8 pkt_type; - bool is_pspoll = false; - - memset(tx_buffer_head, 0, sizeof(*tx_buffer_head)); - - hdr = (struct ieee80211_hdr *)(skb->data); - - rate = ieee80211_get_tx_rate(priv->hw, info); - - current_rate = rate->hw_value; - if (priv->wCurrentRate != current_rate && - !(priv->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { - priv->wCurrentRate = current_rate; - - RFbSetPower(priv, priv->wCurrentRate, - priv->hw->conf.chandef.chan->hw_value); - } - - if (current_rate > RATE_11M) { - if (info->band == NL80211_BAND_5GHZ) { - pkt_type = PK_TYPE_11A; - } else { - if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - pkt_type = PK_TYPE_11GB; - else - pkt_type = PK_TYPE_11GA; - } - } else { - pkt_type = PK_TYPE_11B; - } - - /*Set fifo controls */ - if (pkt_type == PK_TYPE_11A) - tx_buffer_head->fifo_ctl = 0; - else if (pkt_type == PK_TYPE_11B) - tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11B); - else if (pkt_type == PK_TYPE_11GB) - tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GB); - else if (pkt_type == PK_TYPE_11GA) - tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GA); - - /* generate interrupt */ - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_GENINT); - - if (!ieee80211_is_data(hdr->frame_control)) { - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_TMOEN); - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_ISDMA0); - tx_buffer_head->time_stamp = - cpu_to_le16(DEFAULT_MGN_LIFETIME_RES_64us); - } else { - tx_buffer_head->time_stamp = - cpu_to_le16(DEFAULT_MSDU_LIFETIME_RES_64us); - } - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_NEEDACK); - - if (ieee80211_has_retry(hdr->frame_control)) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LRETRY); - - if (tx_rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - priv->preamble_type = PREAMBLE_SHORT; - else - priv->preamble_type = PREAMBLE_LONG; - - if (tx_rate->flags & IEEE80211_TX_RC_USE_RTS_CTS) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_RTS); - - if (ieee80211_has_a4(hdr->frame_control)) { - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LHEAD); - priv->bLongHeader = true; - } - - if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) - is_pspoll = true; - - tx_buffer_head->frag_ctl = - cpu_to_le16(ieee80211_get_hdrlen_from_skb(skb) << 10); - - if (info->control.hw_key) { - switch (info->control.hw_key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_LEGACY); - break; - case WLAN_CIPHER_SUITE_TKIP: - tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_TKIP); - break; - case WLAN_CIPHER_SUITE_CCMP: - tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_AES); - break; - default: - break; - } - } - - tx_buffer_head->current_rate = cpu_to_le16(current_rate); - - /* legacy rates TODO use ieee80211_tx_rate */ - if (current_rate >= RATE_18M && ieee80211_is_data(hdr->frame_control)) { - if (priv->byAutoFBCtrl == AUTO_FB_0) - tx_buffer_head->fifo_ctl |= - cpu_to_le16(FIFOCTL_AUTO_FB_0); - else if (priv->byAutoFBCtrl == AUTO_FB_1) - tx_buffer_head->fifo_ctl |= - cpu_to_le16(FIFOCTL_AUTO_FB_1); - } - - tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_NONFRAG); - - s_cbFillTxBufHead(priv, pkt_type, (u8 *)tx_buffer_head, - dma_idx, head_td, is_pspoll); - - if (info->control.hw_key) { - tx_key = info->control.hw_key; - if (tx_key->keylen > 0) - vnt_fill_txkey(hdr, tx_buffer_head->tx_key, - tx_key, skb, tx_body_size, - td_info->mic_hdr); - } - - return 0; -} - -static int vnt_beacon_xmit(struct vnt_private *priv, - struct sk_buff *skb) -{ - struct vnt_tx_short_buf_head *short_head = - (struct vnt_tx_short_buf_head *)priv->tx_beacon_bufs; - struct ieee80211_mgmt *mgmt_hdr = (struct ieee80211_mgmt *) - (priv->tx_beacon_bufs + sizeof(*short_head)); - struct ieee80211_tx_info *info; - u32 frame_size = skb->len + 4; - u16 current_rate; - - memset(priv->tx_beacon_bufs, 0, sizeof(*short_head)); - - if (priv->byBBType == BB_TYPE_11A) { - current_rate = RATE_6M; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_size, current_rate, - PK_TYPE_11A, &short_head->ab); - - /* Get Duration and TimeStampOff */ - short_head->duration = - cpu_to_le16((u16)s_uGetDataDuration(priv, DATADUR_B, - frame_size, PK_TYPE_11A, current_rate, - false, 0, 0, 1, AUTO_FB_NONE)); - - short_head->time_stamp_off = - vnt_time_stamp_off(priv, current_rate); - } else { - current_rate = RATE_1M; - short_head->fifo_ctl |= cpu_to_le16(FIFOCTL_11B); - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_size, current_rate, - PK_TYPE_11B, &short_head->ab); - - /* Get Duration and TimeStampOff */ - short_head->duration = - cpu_to_le16((u16)s_uGetDataDuration(priv, DATADUR_B, - frame_size, PK_TYPE_11B, current_rate, - false, 0, 0, 1, AUTO_FB_NONE)); - - short_head->time_stamp_off = - vnt_time_stamp_off(priv, current_rate); - } - - short_head->fifo_ctl |= cpu_to_le16(FIFOCTL_GENINT); - - /* Copy Beacon */ - memcpy(mgmt_hdr, skb->data, skb->len); - - /* time stamp always 0 */ - mgmt_hdr->u.beacon.timestamp = 0; - - info = IEEE80211_SKB_CB(skb); - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mgmt_hdr; - - hdr->duration_id = 0; - hdr->seq_ctrl = cpu_to_le16(priv->wSeqCounter << 4); - } - - priv->wSeqCounter++; - if (priv->wSeqCounter > 0x0fff) - priv->wSeqCounter = 0; - - priv->wBCNBufLen = sizeof(*short_head) + skb->len; - - iowrite32((u32)priv->tx_beacon_dma, priv->port_offset + MAC_REG_BCNDMAPTR); - - iowrite16(priv->wBCNBufLen, priv->port_offset + MAC_REG_BCNDMACTL + 2); - /* Set auto Transmit on */ - vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); - /* Poll Transmit the adapter */ - iowrite8(BEACON_READY, priv->port_offset + MAC_REG_BCNDMACTL); - - return 0; -} - -int vnt_beacon_make(struct vnt_private *priv, struct ieee80211_vif *vif) -{ - struct sk_buff *beacon; - - beacon = ieee80211_beacon_get(priv->hw, vif, 0); - if (!beacon) - return -ENOMEM; - - if (vnt_beacon_xmit(priv, beacon)) { - ieee80211_free_txskb(priv->hw, beacon); - return -ENODEV; - } - - return 0; -} - -int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf) -{ - iowrite8(TFTCTL_TSFCNTRST, priv->port_offset + MAC_REG_TFTCTL); - - iowrite8(TFTCTL_TSFCNTREN, priv->port_offset + MAC_REG_TFTCTL); - - CARDvSetFirstNextTBTT(priv, conf->beacon_int); - - card_set_beacon_period(priv, conf->beacon_int); - - return vnt_beacon_make(priv, vif); -} diff --git a/drivers/staging/vt6655/rxtx.h b/drivers/staging/vt6655/rxtx.h deleted file mode 100644 index be1e5180d57b8..0000000000000 --- a/drivers/staging/vt6655/rxtx.h +++ /dev/null @@ -1,184 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: - * - * Author: Jerry Chen - * - * Date: Jun. 27, 2002 - * - */ - -#ifndef __RXTX_H__ -#define __RXTX_H__ - -#include "device.h" - -#define DEFAULT_MSDU_LIFETIME_RES_64us 8000 /* 64us */ -#define DEFAULT_MGN_LIFETIME_RES_64us 125 /* 64us */ - -/*--------------------- Export Definitions -------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -/* MIC HDR data header */ -struct vnt_mic_hdr { - u8 id; - u8 tx_priority; - u8 mic_addr2[ETH_ALEN]; - u8 ccmp_pn[IEEE80211_CCMP_PN_LEN]; - __be16 payload_len; - __be16 hlen; - __le16 frame_control; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctrl; - u8 addr4[ETH_ALEN]; - u16 packing; /* packing to 48 bytes */ -} __packed; - -/* RsvTime buffer header */ -struct vnt_rrv_time_rts { - __le16 rts_rrv_time_ba; - __le16 rts_rrv_time_aa; - __le16 rts_rrv_time_bb; - u16 reserved; - __le16 rrv_time_b; - __le16 rrv_time_a; -} __packed; - -struct vnt_rrv_time_cts { - __le16 cts_rrv_time_ba; - u16 reserved; - __le16 rrv_time_b; - __le16 rrv_time_a; -} __packed; - -struct vnt_rrv_time_ab { - __le16 rts_rrv_time; - __le16 rrv_time; -} __packed; - -/* TX data header */ -struct vnt_tx_datahead_g { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_b; - __le16 duration_a; - __le16 time_stamp_off_b; - __le16 time_stamp_off_a; -} __packed; - -struct vnt_tx_datahead_g_fb { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_b; - __le16 duration_a; - __le16 duration_a_f0; - __le16 duration_a_f1; - __le16 time_stamp_off_b; - __le16 time_stamp_off_a; -} __packed; - -struct vnt_tx_datahead_ab { - struct vnt_phy_field ab; - __le16 duration; - __le16 time_stamp_off; -} __packed; - -struct vnt_tx_datahead_a_fb { - struct vnt_phy_field a; - __le16 duration; - __le16 time_stamp_off; - __le16 duration_f0; - __le16 duration_f1; -} __packed; - -/* RTS buffer header */ -struct vnt_rts_g { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_ba; - __le16 duration_aa; - __le16 duration_bb; - u16 reserved; - struct ieee80211_rts data; -} __packed __aligned(2); - -struct vnt_rts_g_fb { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_ba; - __le16 duration_aa; - __le16 duration_bb; - u16 wReserved; - __le16 rts_duration_ba_f0; - __le16 rts_duration_aa_f0; - __le16 rts_duration_ba_f1; - __le16 rts_duration_aa_f1; - struct ieee80211_rts data; -} __packed __aligned(2); - -struct vnt_rts_ab { - struct vnt_phy_field ab; - __le16 duration; - u16 reserved; - struct ieee80211_rts data; -} __packed __aligned(2); - -struct vnt_rts_a_fb { - struct vnt_phy_field a; - __le16 duration; - u16 reserved; - __le16 rts_duration_f0; - __le16 rts_duration_f1; - struct ieee80211_rts data; -} __packed __aligned(2); - -/* CTS buffer header */ -struct vnt_cts { - struct vnt_phy_field b; - __le16 duration_ba; - u16 reserved; - struct ieee80211_cts data; - u16 reserved2; -} __packed __aligned(2); - -struct vnt_cts_fb { - struct vnt_phy_field b; - __le16 duration_ba; - u16 reserved; - __le16 cts_duration_ba_f0; - __le16 cts_duration_ba_f1; - struct ieee80211_cts data; - u16 reserved2; -} __packed __aligned(2); - -struct vnt_tx_fifo_head { - u8 tx_key[WLAN_KEY_LEN_CCMP]; - __le16 fifo_ctl; - __le16 time_stamp; - __le16 frag_ctl; - __le16 current_rate; -} __packed; - -struct vnt_tx_short_buf_head { - __le16 fifo_ctl; - u16 time_stamp; - struct vnt_phy_field ab; - __le16 duration; - __le16 time_stamp_off; -} __packed; - -int vnt_generate_fifo_header(struct vnt_private *priv, u32 dma_idx, - struct vnt_tx_desc *head_td, struct sk_buff *skb); -int vnt_beacon_make(struct vnt_private *priv, struct ieee80211_vif *vif); -int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf); - -#endif /* __RXTX_H__ */ diff --git a/drivers/staging/vt6655/srom.c b/drivers/staging/vt6655/srom.c deleted file mode 100644 index e80556509c58d..0000000000000 --- a/drivers/staging/vt6655/srom.c +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose:Implement functions to access eeprom - * - * Author: Jerry Chen - * - * Date: Jan 29, 2003 - * - * Functions: - * SROMbyReadEmbedded - Embedded read eeprom via MAC - * SROMbWriteEmbedded - Embedded write eeprom via MAC - * SROMvRegBitsOn - Set Bits On in eeprom - * SROMvRegBitsOff - Clear Bits Off in eeprom - * SROMbIsRegBitsOn - Test if Bits On in eeprom - * SROMbIsRegBitsOff - Test if Bits Off in eeprom - * SROMvReadAllContents - Read all contents in eeprom - * SROMvWriteAllContents - Write all contents in eeprom - * SROMvReadEtherAddress - Read Ethernet Address in eeprom - * SROMvWriteEtherAddress - Write Ethernet Address in eeprom - * SROMvReadSubSysVenId - Read Sub_VID and Sub_SysId in eeprom - * SROMbAutoLoad - Auto Load eeprom to MAC register - * - * Revision History: - * - */ - -#include "device.h" -#include "mac.h" -#include "srom.h" - -/*--------------------- Static Definitions -------------------------*/ - -/*--------------------- Static Classes ----------------------------*/ - -/*--------------------- Static Variables --------------------------*/ - -/*--------------------- Static Functions --------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -/* - * Description: Read a byte from EEPROM, by MAC I2C - * - * Parameters: - * In: - * iobase - I/O base address - * contnt_offset - address of EEPROM - * Out: - * none - * - * Return Value: data read - * - */ -unsigned char SROMbyReadEmbedded(void __iomem *iobase, - unsigned char contnt_offset) -{ - unsigned short wDelay, wNoACK; - unsigned char byWait; - unsigned char byData; - unsigned char byOrg; - - byOrg = ioread8(iobase + MAC_REG_I2MCFG); - /* turn off hardware retry for getting NACK */ - iowrite8(byOrg & (~I2MCFG_NORETRY), iobase + MAC_REG_I2MCFG); - for (wNoACK = 0; wNoACK < W_MAX_I2CRETRY; wNoACK++) { - iowrite8(EEP_I2C_DEV_ID, iobase + MAC_REG_I2MTGID); - iowrite8(contnt_offset, iobase + MAC_REG_I2MTGAD); - - /* issue read command */ - iowrite8(I2MCSR_EEMR, iobase + MAC_REG_I2MCSR); - /* wait DONE be set */ - for (wDelay = 0; wDelay < W_MAX_TIMEOUT; wDelay++) { - byWait = ioread8(iobase + MAC_REG_I2MCSR); - if (byWait & (I2MCSR_DONE | I2MCSR_NACK)) - break; - udelay(CB_DELAY_LOOP_WAIT); - } - if ((wDelay < W_MAX_TIMEOUT) && - (!(byWait & I2MCSR_NACK))) { - break; - } - } - byData = ioread8(iobase + MAC_REG_I2MDIPT); - iowrite8(byOrg, iobase + MAC_REG_I2MCFG); - return byData; -} - -/* - * Description: Read all contents of eeprom to buffer - * - * Parameters: - * In: - * iobase - I/O base address - * Out: - * pbyEepromRegs - EEPROM content Buffer - * - * Return Value: none - * - */ -void SROMvReadAllContents(void __iomem *iobase, unsigned char *pbyEepromRegs) -{ - int ii; - - /* ii = Rom Address */ - for (ii = 0; ii < EEP_MAX_CONTEXT_SIZE; ii++) { - *pbyEepromRegs = SROMbyReadEmbedded(iobase, - (unsigned char)ii); - pbyEepromRegs++; - } -} - -/* - * Description: Read Ethernet Address from eeprom to buffer - * - * Parameters: - * In: - * iobase - I/O base address - * Out: - * pbyEtherAddress - Ethernet Address buffer - * - * Return Value: none - * - */ -void SROMvReadEtherAddress(void __iomem *iobase, - unsigned char *pbyEtherAddress) -{ - unsigned char ii; - - /* ii = Rom Address */ - for (ii = 0; ii < ETH_ALEN; ii++) { - *pbyEtherAddress = SROMbyReadEmbedded(iobase, ii); - pbyEtherAddress++; - } -} diff --git a/drivers/staging/vt6655/srom.h b/drivers/staging/vt6655/srom.h deleted file mode 100644 index b03073ffa18a2..0000000000000 --- a/drivers/staging/vt6655/srom.h +++ /dev/null @@ -1,85 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions to access eeprom - * - * Author: Jerry Chen - * - * Date: Jan 29, 2003 - */ - -#ifndef __SROM_H__ -#define __SROM_H__ - -/*--------------------- Export Definitions -------------------------*/ - -#define EEP_MAX_CONTEXT_SIZE 256 - -#define CB_EEPROM_READBYTE_WAIT 900 /* us */ - -#define W_MAX_I2CRETRY 0x0fff - -/* Contents in the EEPROM */ -#define EEP_OFS_PAR 0x00 /* physical address */ -#define EEP_OFS_ANTENNA 0x16 -#define EEP_OFS_RADIOCTL 0x17 -#define EEP_OFS_RFTYPE 0x1B /* for select RF */ -#define EEP_OFS_MINCHANNEL 0x1C /* Min Channel # */ -#define EEP_OFS_MAXCHANNEL 0x1D /* Max Channel # */ -#define EEP_OFS_SIGNATURE 0x1E -#define EEP_OFS_ZONETYPE 0x1F -#define EEP_OFS_RFTABLE 0x20 /* RF POWER TABLE */ -#define EEP_OFS_PWR_CCK 0x20 -#define EEP_OFS_SETPT_CCK 0x21 -#define EEP_OFS_PWR_OFDMG 0x23 -#define EEP_OFS_SETPT_OFDMG 0x24 -#define EEP_OFS_PWR_FORMULA_OST 0x26 -#define EEP_OFS_MAJOR_VER 0x2E -#define EEP_OFS_MINOR_VER 0x2F -#define EEP_OFS_CCK_PWR_TBL 0x30 -#define EEP_OFS_CCK_PWR_dBm 0x3F -#define EEP_OFS_OFDM_PWR_TBL 0x40 -#define EEP_OFS_OFDM_PWR_dBm 0x4F -/*{{ RobertYu: 20041124 */ -#define EEP_OFS_SETPT_OFDMA 0x4E -#define EEP_OFS_OFDMA_PWR_TBL 0x50 -/*}}*/ -#define EEP_OFS_OFDMA_PWR_dBm 0xD2 - -/*----------need to remove --------------------*/ -#define EEP_OFS_BBTAB_LEN 0x70 /* BB Table Length */ -#define EEP_OFS_BBTAB_ADR 0x71 /* BB Table Offset */ -#define EEP_OFS_CHECKSUM 0xFF /* reserved area for baseband 28h~78h */ - -#define EEP_I2C_DEV_ID 0x50 /* EEPROM device address on I2C bus */ - -/* Bits in EEP_OFS_ANTENNA */ -#define EEP_ANTENNA_MAIN 0x01 -#define EEP_ANTENNA_AUX 0x02 -#define EEP_ANTINV 0x04 - -/* Bits in EEP_OFS_RADIOCTL */ -#define EEP_RADIOCTL_ENABLE 0x80 -#define EEP_RADIOCTL_INV 0x01 - -/*--------------------- Export Types ------------------------------*/ - -/*--------------------- Export Macros ------------------------------*/ - -/*--------------------- Export Classes ----------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -unsigned char SROMbyReadEmbedded(void __iomem *iobase, - unsigned char byContntOffset); - -void SROMvReadAllContents(void __iomem *iobase, unsigned char *pbyEepromRegs); - -void SROMvReadEtherAddress(void __iomem *iobase, - unsigned char *pbyEtherAddress); - -#endif /* __EEPROM_H__*/ diff --git a/drivers/staging/vt6655/test b/drivers/staging/vt6655/test deleted file mode 100644 index ba6dec774478e..0000000000000 --- a/drivers/staging/vt6655/test +++ /dev/null @@ -1,9 +0,0 @@ -KSP := /lib/modules/$(shell uname -r)/build \ - /usr/src/linux-$(shell uname -r) \ - /usr/src/linux-$(shell uname -r | sed 's/-.*//') \ -# /usr/src/kernel-headers-$(shell uname -r) \ -# /usr/src/kernel-source-$(shell uname -r) \ -# /usr/src/linux-$(shell uname -r | sed 's/\([0-9]*\.[0-9]*\)\..*/\1/') \ -# /usr/src/linux /home/plice -test_dir = $(shell [ -e $(dir)/include/linux ] && echo $(dir)) -KSP := $(foreach dir, $(KSP), $(test_dir)) diff --git a/drivers/staging/vt6656/Kconfig b/drivers/staging/vt6656/Kconfig deleted file mode 100644 index f52a3f1d9a2ee..0000000000000 --- a/drivers/staging/vt6656/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VT6656 - tristate "VIA Technologies VT6656 support" - depends on MAC80211 && USB && WLAN && m - select FW_LOADER - help - This is a vendor-written driver for VIA VT6656. diff --git a/drivers/staging/vt6656/Makefile b/drivers/staging/vt6656/Makefile deleted file mode 100644 index f696a9d7a143d..0000000000000 --- a/drivers/staging/vt6656/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -vt6656_stage-y += main_usb.o \ - card.o \ - mac.o \ - baseband.o \ - wcmd.o\ - rxtx.o \ - power.o \ - key.o \ - rf.o \ - usbpipe.o \ - channel.o - -obj-$(CONFIG_VT6656) += vt6656_stage.o diff --git a/drivers/staging/vt6656/TODO b/drivers/staging/vt6656/TODO deleted file mode 100644 index cca78d2d3cfeb..0000000000000 --- a/drivers/staging/vt6656/TODO +++ /dev/null @@ -1,17 +0,0 @@ -TODO: -- remove __cplusplus ifdefs -- done -- remove kernel version compatibility wrappers -- remove support for older wireless extensions -- prepare for merge with vt6655 driver: - - remove PRINT_K() macro - - split rf.c - - abstract VT3184 chipset specific code -- add common vt665x infrastructure -- kill ttype.h -- done -- switch to use MAC80211 -- use kernel coding style -- sparse fixes -- integrate with drivers/net/wireless - -Please send any patches to Greg Kroah-Hartman -and Forest Bond . diff --git a/drivers/staging/vt6656/baseband.c b/drivers/staging/vt6656/baseband.c deleted file mode 100644 index ad7b963f0d981..0000000000000 --- a/drivers/staging/vt6656/baseband.c +++ /dev/null @@ -1,455 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions to access baseband - * - * Author: Jerry Chen - * - * Date: Jun. 5, 2002 - * - * Functions: - * vnt_get_frame_time - Calculate data frame transmitting time - * vnt_get_phy_field - Calculate PhyLength, PhyService and Phy - * Signal parameter for baseband Tx - * vnt_vt3184_init - VIA VT3184 baseband chip init code - * - * Revision History: - * - * - */ - -#include -#include -#include -#include "device.h" -#include "mac.h" -#include "baseband.h" -#include "rf.h" -#include "usbpipe.h" - -static const u8 vnt_vt3184_agc[] = { - 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x06, 0x06, - 0x08, 0x08, 0x0a, 0x0a, 0x0c, 0x0c, 0x0e, 0x0e, /* 0x0f */ - 0x10, 0x10, 0x12, 0x12, 0x14, 0x14, 0x16, 0x16, - 0x18, 0x18, 0x1a, 0x1a, 0x1c, 0x1c, 0x1e, 0x1e, /* 0x1f */ - 0x20, 0x20, 0x22, 0x22, 0x24, 0x24, 0x26, 0x26, - 0x28, 0x28, 0x2a, 0x2a, 0x2c, 0x2c, 0x2e, 0x2e, /* 0x2f */ - 0x30, 0x30, 0x32, 0x32, 0x34, 0x34, 0x36, 0x36, - 0x38, 0x38, 0x3a, 0x3a, 0x3c, 0x3c, 0x3e, 0x3e /* 0x3f */ -}; - -static u8 vnt_vt3184_al2230[] = { - 0x31, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x70, 0x45, 0x2a, 0x76, 0x00, 0x00, 0x80, 0x00, /* 0x0f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x8e, 0x0a, 0x00, 0x00, 0x00, /* 0x1f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x0c, /* 0x2f */ - 0x26, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, - 0xff, 0xff, 0x79, 0x00, 0x00, 0x0b, 0x48, 0x04, /* 0x3f */ - 0x00, 0x08, 0x00, 0x08, 0x08, 0x14, 0x05, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x73, 0x00, 0xc5, /* 0x4f */ - 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f */ - 0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* 0x6f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */ - 0x8c, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x1f, 0xb7, 0x88, 0x47, 0xaa, 0x00, /* 0x8f */ - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0x9f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x18, /* 0xaf */ - 0x38, 0x30, 0x00, 0x00, 0xff, 0x0f, 0xe4, 0xe2, - 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, /* 0xbf */ - 0x18, 0x20, 0x07, 0x18, 0xff, 0xff, 0x0e, 0x0a, - 0x0e, 0x00, 0x82, 0xa7, 0x3c, 0x10, 0x30, 0x05, /* 0xcf */ - 0x40, 0x12, 0x00, 0x00, 0x10, 0x28, 0x80, 0x2a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xdf */ - 0x00, 0xf3, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, - 0x00, 0xf4, 0x00, 0xff, 0x79, 0x20, 0x30, 0x05, /* 0xef */ - 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xff */ -}; - -/* {{RobertYu:20060515, new BB setting for VT3226D0 */ -static const u8 vnt_vt3184_vt3226d0[] = { - 0x31, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x70, 0x45, 0x2a, 0x76, 0x00, 0x00, 0x80, 0x00, /* 0x0f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x8e, 0x0a, 0x00, 0x00, 0x00, /* 0x1f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x0c, /* 0x2f */ - 0x26, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, - 0xff, 0xff, 0x79, 0x00, 0x00, 0x0b, 0x48, 0x04, /* 0x3f */ - 0x00, 0x08, 0x00, 0x08, 0x08, 0x14, 0x05, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x73, 0x00, 0xc5, /* 0x4f */ - 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f */ - 0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* 0x6f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */ - 0x8c, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x1f, 0xb7, 0x88, 0x47, 0xaa, 0x00, /* 0x8f */ - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0x9f */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, /* 0xaf */ - 0x38, 0x30, 0x00, 0x00, 0xff, 0x0f, 0xe4, 0xe2, - 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, /* 0xbf */ - 0x18, 0x20, 0x07, 0x18, 0xff, 0xff, 0x10, 0x0a, - 0x0e, 0x00, 0x84, 0xa7, 0x3c, 0x10, 0x24, 0x05, /* 0xcf */ - 0x40, 0x12, 0x00, 0x00, 0x10, 0x28, 0x80, 0x2a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xdf */ - 0x00, 0xf3, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, - 0x00, 0xf4, 0x00, 0xff, 0x79, 0x20, 0x30, 0x08, /* 0xef */ - 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xff */ -}; - -struct vnt_threshold { - u8 bb_pre_ed_rssi; - u8 cr_201; - u8 cr_206; -}; - -static const struct vnt_threshold al2230_vnt_threshold[] = { - {0, 0x00, 0x30}, /* Max sensitivity */ - {68, 0x00, 0x36}, - {67, 0x00, 0x43}, - {66, 0x00, 0x51}, - {65, 0x00, 0x62}, - {64, 0x00, 0x79}, - {63, 0x00, 0x93}, - {62, 0x00, 0xb9}, - {61, 0x00, 0xe3}, - {60, 0x01, 0x18}, - {59, 0x01, 0x54}, - {58, 0x01, 0xa0}, - {57, 0x02, 0x20}, - {56, 0x02, 0xa0}, - {55, 0x03, 0x00}, - {53, 0x06, 0x00}, - {51, 0x09, 0x00}, - {49, 0x0e, 0x00}, - {47, 0x15, 0x00}, - {46, 0x1a, 0x00}, - {45, 0xff, 0x00} -}; - -static const struct vnt_threshold vt3226_vnt_threshold[] = { - {0, 0x00, 0x24}, /* Max sensitivity */ - {68, 0x00, 0x2d}, - {67, 0x00, 0x36}, - {66, 0x00, 0x43}, - {65, 0x00, 0x52}, - {64, 0x00, 0x68}, - {63, 0x00, 0x80}, - {62, 0x00, 0x9c}, - {61, 0x00, 0xc0}, - {60, 0x00, 0xea}, - {59, 0x01, 0x30}, - {58, 0x01, 0x70}, - {57, 0x01, 0xb0}, - {56, 0x02, 0x30}, - {55, 0x02, 0xc0}, - {53, 0x04, 0x00}, - {51, 0x07, 0x00}, - {49, 0x0a, 0x00}, - {47, 0x11, 0x00}, - {45, 0x18, 0x00}, - {43, 0x26, 0x00}, - {42, 0x36, 0x00}, - {41, 0xff, 0x00} -}; - -/* - * Description: Set Antenna mode - * - * Parameters: - * In: - * priv - Device Structure - * antenna_mode - Antenna Mode - * Out: - * none - * - * Return Value: none - * - */ -int vnt_set_antenna_mode(struct vnt_private *priv, u8 antenna_mode) -{ - switch (antenna_mode) { - case ANT_TXA: - case ANT_TXB: - break; - case ANT_RXA: - priv->bb_rx_conf &= 0xFC; - break; - case ANT_RXB: - priv->bb_rx_conf &= 0xFE; - priv->bb_rx_conf |= 0x02; - break; - } - - return vnt_control_out(priv, MESSAGE_TYPE_SET_ANTMD, - (u16)antenna_mode, 0, 0, NULL); -} - -/* - * Description: Set Antenna mode - * - * Parameters: - * In: - * pDevice - Device Structure - * byAntennaMode - Antenna Mode - * Out: - * none - * - * Return Value: none - * - */ - -int vnt_vt3184_init(struct vnt_private *priv) -{ - int ret; - u16 length; - u8 *addr = NULL; - const u8 *c_addr; - u8 data; - - ret = vnt_control_in(priv, MESSAGE_TYPE_READ, 0, MESSAGE_REQUEST_EEPROM, - EEP_MAX_CONTEXT_SIZE, priv->eeprom); - if (ret) - goto end; - - priv->rf_type = priv->eeprom[EEP_OFS_RFTYPE]; - - dev_dbg(&priv->usb->dev, "RF Type %d\n", priv->rf_type); - - if ((priv->rf_type == RF_AL2230) || - (priv->rf_type == RF_AL2230S)) { - priv->bb_rx_conf = vnt_vt3184_al2230[10]; - length = sizeof(vnt_vt3184_al2230); - addr = vnt_vt3184_al2230; - - priv->bb_vga[0] = 0x1c; - priv->bb_vga[1] = 0x10; - priv->bb_vga[2] = 0x0; - priv->bb_vga[3] = 0x0; - - } else if ((priv->rf_type == RF_VT3226) || - (priv->rf_type == RF_VT3226D0)) { - priv->bb_rx_conf = vnt_vt3184_vt3226d0[10]; - length = sizeof(vnt_vt3184_vt3226d0); - c_addr = vnt_vt3184_vt3226d0; - - priv->bb_vga[0] = 0x20; - priv->bb_vga[1] = 0x10; - priv->bb_vga[2] = 0x0; - priv->bb_vga[3] = 0x0; - - /* Fix VT3226 DFC system timing issue */ - ret = vnt_mac_reg_bits_on(priv, MAC_REG_SOFTPWRCTL2, - SOFTPWRCTL_RFLEOPT); - if (ret) - goto end; - } else { - goto end; - } - - if (addr) - c_addr = addr; - - ret = vnt_control_out_blocks(priv, VNT_REG_BLOCK_SIZE, - MESSAGE_REQUEST_BBREG, length, c_addr); - if (ret) - goto end; - - ret = vnt_control_out(priv, MESSAGE_TYPE_WRITE, 0, - MESSAGE_REQUEST_BBAGC, - sizeof(vnt_vt3184_agc), vnt_vt3184_agc); - if (ret) - goto end; - - if ((priv->rf_type == RF_VT3226) || - (priv->rf_type == RF_VT3226D0)) { - data = (priv->rf_type == RF_VT3226D0) ? 0x11 : 0x23; - - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG, - MAC_REG_ITRTMSET, data); - if (ret) - goto end; - - ret = vnt_mac_reg_bits_on(priv, MAC_REG_PAPEDELAY, BIT(0)); - if (ret) - goto end; - } - - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x04, 0x7f); - if (ret) - goto end; - - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0d, 0x01); - if (ret) - goto end; - - ret = vnt_rf_table_download(priv); - if (ret) - goto end; - - /* Fix for TX USB resets from vendors driver */ - ret = vnt_control_in(priv, MESSAGE_TYPE_READ, USB_REG4, - MESSAGE_REQUEST_MEM, sizeof(data), &data); - if (ret) - goto end; - - data |= 0x2; - - ret = vnt_control_out(priv, MESSAGE_TYPE_WRITE, USB_REG4, - MESSAGE_REQUEST_MEM, sizeof(data), &data); - -end: - return ret; -} - -/* - * Description: Set ShortSlotTime mode - * - * Parameters: - * In: - * priv - Device Structure - * Out: - * none - * - * Return Value: none - * - */ -int vnt_set_short_slot_time(struct vnt_private *priv) -{ - int ret = 0; - u8 bb_vga = 0; - - if (priv->short_slot_time) - priv->bb_rx_conf &= 0xdf; - else - priv->bb_rx_conf |= 0x20; - - ret = vnt_control_in_u8(priv, MESSAGE_REQUEST_BBREG, 0xe7, &bb_vga); - if (ret) - return ret; - - if (bb_vga == priv->bb_vga[0]) - priv->bb_rx_conf |= 0x20; - - return vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0a, - priv->bb_rx_conf); -} - -int vnt_set_vga_gain_offset(struct vnt_private *priv, u8 data) -{ - int ret; - - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0xE7, data); - if (ret) - return ret; - - /* patch for 3253B0 Baseband with Cardbus module */ - if (priv->short_slot_time) - priv->bb_rx_conf &= 0xdf; /* 1101 1111 */ - else - priv->bb_rx_conf |= 0x20; /* 0010 0000 */ - - return vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0a, - priv->bb_rx_conf); -} - -/* - * Description: vnt_set_deep_sleep - * - * Parameters: - * In: - * priv - Device Structure - * Out: - * none - * - * Return Value: none - * - */ -int vnt_set_deep_sleep(struct vnt_private *priv) -{ - int ret = 0; - - /* CR12 */ - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0c, 0x17); - if (ret) - return ret; - - /* CR13 */ - return vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0d, 0xB9); -} - -int vnt_exit_deep_sleep(struct vnt_private *priv) -{ - int ret = 0; - - /* CR12 */ - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0c, 0x00); - if (ret) - return ret; - - /* CR13 */ - return vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0d, 0x01); -} - -int vnt_update_pre_ed_threshold(struct vnt_private *priv, int scanning) -{ - const struct vnt_threshold *threshold = NULL; - u8 length; - u8 cr_201, cr_206; - u8 ed_inx; - int ret; - - switch (priv->rf_type) { - case RF_AL2230: - case RF_AL2230S: - threshold = al2230_vnt_threshold; - length = ARRAY_SIZE(al2230_vnt_threshold); - break; - - case RF_VT3226: - case RF_VT3226D0: - threshold = vt3226_vnt_threshold; - length = ARRAY_SIZE(vt3226_vnt_threshold); - break; - } - - if (!threshold) - return -EINVAL; - - for (ed_inx = scanning ? 0 : length - 1; ed_inx > 0; ed_inx--) { - if (priv->bb_pre_ed_rssi <= threshold[ed_inx].bb_pre_ed_rssi) - break; - } - - cr_201 = threshold[ed_inx].cr_201; - cr_206 = threshold[ed_inx].cr_206; - - if (ed_inx == priv->bb_pre_ed_index && !scanning) - return 0; - - priv->bb_pre_ed_index = ed_inx; - - dev_dbg(&priv->usb->dev, "%s bb_pre_ed_rssi %d\n", - __func__, priv->bb_pre_ed_rssi); - - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0xc9, cr_201); - if (ret) - return ret; - - return vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0xce, cr_206); -} - diff --git a/drivers/staging/vt6656/baseband.h b/drivers/staging/vt6656/baseband.h deleted file mode 100644 index dce50a311f243..0000000000000 --- a/drivers/staging/vt6656/baseband.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions to access baseband - * - * Author: Jerry Chen - * - * Date: Jun. 5, 2002 - * - * Revision History: - * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. - * 08-26-2003 Kyle Hsu : Add defines of packet type and TX rate. - */ - -#ifndef __BASEBAND_H__ -#define __BASEBAND_H__ - -#include "device.h" - -#define PREAMBLE_LONG 0 -#define PREAMBLE_SHORT 1 - -/* - * Registers in the BASEBAND - */ -#define BB_MAX_CONTEXT_SIZE 256 - -#define C_SIFS_A 16 /* usec */ -#define C_SIFS_BG 10 - -#define C_EIFS 80 /* usec */ - -#define C_SLOT_SHORT 9 /* usec */ -#define C_SLOT_LONG 20 - -#define C_CWMIN_A 15 /* slot time */ -#define C_CWMIN_B 31 - -#define C_CWMAX 1023 /* slot time */ - -/* 0:11A 1:11B 2:11G */ -#define BB_TYPE_11A 0 -#define BB_TYPE_11B 1 -#define BB_TYPE_11G 2 - -/* 0:11a, 1:11b, 2:11gb (only CCK in BasicRate), 3:11ga (OFDM in BasicRate) */ -#define PK_TYPE_11A 0 -#define PK_TYPE_11B 1 -#define PK_TYPE_11GB 2 -#define PK_TYPE_11GA 3 - -#define TOP_RATE_54M 0x80000000 -#define TOP_RATE_48M 0x40000000 -#define TOP_RATE_36M 0x20000000 -#define TOP_RATE_24M 0x10000000 -#define TOP_RATE_18M 0x08000000 -#define TOP_RATE_12M 0x04000000 -#define TOP_RATE_11M 0x02000000 -#define TOP_RATE_9M 0x01000000 -#define TOP_RATE_6M 0x00800000 -#define TOP_RATE_55M 0x00400000 -#define TOP_RATE_2M 0x00200000 -#define TOP_RATE_1M 0x00100000 - -int vnt_set_short_slot_time(struct vnt_private *priv); -int vnt_set_vga_gain_offset(struct vnt_private *priv, u8 data); -int vnt_set_antenna_mode(struct vnt_private *priv, u8 antenna_mode); -int vnt_vt3184_init(struct vnt_private *priv); -int vnt_set_deep_sleep(struct vnt_private *priv); -int vnt_exit_deep_sleep(struct vnt_private *priv); -int vnt_update_pre_ed_threshold(struct vnt_private *priv, int scanning); - -#endif /* __BASEBAND_H__ */ diff --git a/drivers/staging/vt6656/card.c b/drivers/staging/vt6656/card.c deleted file mode 100644 index b9dc0d13c00c8..0000000000000 --- a/drivers/staging/vt6656/card.c +++ /dev/null @@ -1,456 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Provide functions to setup NIC operation mode - * Functions: - * vnt_set_rspinf - Set RSPINF - * vnt_update_ifs - Update slotTime,SIFS,DIFS, and EIFS - * vnt_update_top_rates - Update BasicTopRate - * vnt_add_basic_rate - Add to BasicRateSet - * vnt_ofdm_min_rate - Check if any OFDM rate is in BasicRateSet - * vnt_get_tsf_offset - Calculate TSFOffset - * vnt_get_next_tbtt - Calculate Next Beacon TSF counter - * vnt_reset_next_tbtt - Set NIC Beacon time - * vnt_update_next_tbtt - Sync. NIC Beacon time - * vnt_radio_power_off - Turn Off NIC Radio Power - * vnt_radio_power_on - Turn On NIC Radio Power - * - * Revision History: - * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. - * 08-26-2003 Kyle Hsu: Modify the definition type of dwIoBase. - * 09-01-2003 Bryan YC Fan: Add vnt_update_ifs(). - * - */ - -#include -#include -#include "device.h" -#include "card.h" -#include "baseband.h" -#include "mac.h" -#include "desc.h" -#include "rf.h" -#include "power.h" -#include "key.h" -#include "usbpipe.h" - -/* const u16 cw_rxbcntsf_off[MAX_RATE] = - * {17, 34, 96, 192, 34, 23, 17, 11, 8, 5, 4, 3}; - */ - -static const u16 cw_rxbcntsf_off[MAX_RATE] = { - 192, 96, 34, 17, 34, 23, 17, 11, 8, 5, 4, 3 -}; - -int vnt_set_channel(struct vnt_private *priv, u32 connection_channel) -{ - int ret; - - if (connection_channel > CB_MAX_CHANNEL || !connection_channel) - return -EINVAL; - - /* clear NAV */ - vnt_mac_reg_bits_on(priv, MAC_REG_MACCR, MACCR_CLRNAV); - - /* Set Channel[7] = 0 to tell H/W channel is changing now. */ - vnt_mac_reg_bits_off(priv, MAC_REG_CHANNEL, - (BIT(7) | BIT(5) | BIT(4))); - - ret = vnt_control_out(priv, MESSAGE_TYPE_SELECT_CHANNEL, - connection_channel, 0, 0, NULL); - if (ret) - return ret; - - return vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG, MAC_REG_CHANNEL, - (u8)(connection_channel | 0x80)); -} - -static const u8 vnt_rspinf_b_short_table[] = { - 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x09, 0x00, - 0x15, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0b, 0x80 -}; - -static const u8 vnt_rspinf_b_long_table[] = { - 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x15, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x80 -}; - -static const u8 vnt_rspinf_a_table[] = { - 0x9b, 0x18, 0x9f, 0x10, 0x9a, 0x0a, 0x9e, 0x08, 0x99, - 0x08, 0x9d, 0x04, 0x98, 0x04, 0x9c, 0x04, 0x9c, 0x04 -}; - -static const u8 vnt_rspinf_gb_table[] = { - 0x8b, 0x1e, 0x8f, 0x16, 0x8a, 0x12, 0x8e, 0x0e, 0x89, - 0x0e, 0x8d, 0x0a, 0x88, 0x0a, 0x8c, 0x0a, 0x8c, 0x0a -}; - -int vnt_set_rspinf(struct vnt_private *priv, u8 bb_type) -{ - const u8 *data; - u16 len; - int ret; - - if (priv->preamble_type) { - data = vnt_rspinf_b_short_table; - len = ARRAY_SIZE(vnt_rspinf_b_short_table); - } else { - data = vnt_rspinf_b_long_table; - len = ARRAY_SIZE(vnt_rspinf_b_long_table); - } - - /* RSPINF_b_1 to RSPINF_b_11 */ - ret = vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_RSPINF_B_1, - MESSAGE_REQUEST_MACREG, len, data); - if (ret) - return ret; - - if (bb_type == BB_TYPE_11A) { - data = vnt_rspinf_a_table; - len = ARRAY_SIZE(vnt_rspinf_a_table); - } else { - data = vnt_rspinf_gb_table; - len = ARRAY_SIZE(vnt_rspinf_gb_table); - } - - /* RSPINF_a_6 to RSPINF_a_72 */ - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_RSPINF_A_6, - MESSAGE_REQUEST_MACREG, len, data); -} - -int vnt_update_ifs(struct vnt_private *priv) -{ - u8 max_min = 0; - u8 data[4]; - int ret; - - if (priv->packet_type == PK_TYPE_11A) { - priv->slot = C_SLOT_SHORT; - priv->sifs = C_SIFS_A; - priv->difs = C_SIFS_A + 2 * C_SLOT_SHORT; - max_min = 4; - } else { - priv->sifs = C_SIFS_BG; - - if (priv->short_slot_time) { - priv->slot = C_SLOT_SHORT; - max_min = 4; - } else { - priv->slot = C_SLOT_LONG; - max_min = 5; - } - - priv->difs = C_SIFS_BG + 2 * priv->slot; - } - - priv->eifs = C_EIFS; - - data[0] = (u8)priv->sifs; - data[1] = (u8)priv->difs; - data[2] = (u8)priv->eifs; - data[3] = (u8)priv->slot; - - ret = vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_SIFS, - MESSAGE_REQUEST_MACREG, 4, &data[0]); - if (ret) - return ret; - - max_min |= 0xa0; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_CWMAXMIN0, - MESSAGE_REQUEST_MACREG, 1, &max_min); -} - -void vnt_update_top_rates(struct vnt_private *priv) -{ - int pos; - - pos = fls(priv->basic_rates & GENMASK(RATE_54M, RATE_6M)); - priv->top_ofdm_basic_rate = pos ? (pos - 1) : RATE_24M; - - pos = fls(priv->basic_rates & GENMASK(RATE_11M, RATE_1M)); - priv->top_cck_basic_rate = pos ? (pos - 1) : RATE_1M; -} - -bool vnt_ofdm_min_rate(struct vnt_private *priv) -{ - return priv->basic_rates & GENMASK(RATE_54M, RATE_6M) ? true : false; -} - -u8 vnt_get_pkt_type(struct vnt_private *priv) -{ - if (priv->bb_type == BB_TYPE_11A || priv->bb_type == BB_TYPE_11B) - return (u8)priv->bb_type; - else if (vnt_ofdm_min_rate(priv)) - return PK_TYPE_11GA; - return PK_TYPE_11GB; -} - -/* - * Description: Calculate TSF offset of two TSF input - * Get TSF Offset from RxBCN's TSF and local TSF - * - * Parameters: - * In: - * rx_rate - rx rate. - * tsf1 - Rx BCN's TSF - * tsf2 - Local TSF - * Out: - * none - * - * Return Value: TSF Offset value - * - */ -u64 vnt_get_tsf_offset(u8 rx_rate, u64 tsf1, u64 tsf2) -{ - return tsf1 - tsf2 - (u64)cw_rxbcntsf_off[rx_rate % MAX_RATE]; -} - -int vnt_adjust_tsf(struct vnt_private *priv, u8 rx_rate, - u64 time_stamp, u64 local_tsf) -{ - u64 tsf_offset = 0; - u8 data[8]; - - tsf_offset = vnt_get_tsf_offset(rx_rate, time_stamp, local_tsf); - - data[0] = (u8)tsf_offset; - data[1] = (u8)(tsf_offset >> 8); - data[2] = (u8)(tsf_offset >> 16); - data[3] = (u8)(tsf_offset >> 24); - data[4] = (u8)(tsf_offset >> 32); - data[5] = (u8)(tsf_offset >> 40); - data[6] = (u8)(tsf_offset >> 48); - data[7] = (u8)(tsf_offset >> 56); - - return vnt_control_out(priv, MESSAGE_TYPE_SET_TSFTBTT, - MESSAGE_REQUEST_TSF, 0, 8, data); -} - -/* - * Description: Clear NIC TSF counter - * Clear local TSF counter - * - * Parameters: - * In: - * priv - The adapter to be read - * - * Return Value: true if success; otherwise false - * - */ -bool vnt_clear_current_tsf(struct vnt_private *priv) -{ - vnt_mac_reg_bits_on(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTRST); - - priv->current_tsf = 0; - - return true; -} - -/* - * Description: Read NIC TSF counter - * Get NEXTTBTT from adjusted TSF and Beacon Interval - * - * Parameters: - * In: - * tsf - Current TSF counter - * beacon_interval - Beacon Interval - * Out: - * tsf - Current TSF counter - * - * Return Value: TSF value of next Beacon - * - */ -u64 vnt_get_next_tbtt(u64 tsf, u16 beacon_interval) -{ - u32 beacon_int; - - beacon_int = beacon_interval * 1024; - - /* Next TBTT = - * ((local_current_TSF / beacon_interval) + 1) * beacon_interval - */ - if (beacon_int) { - do_div(tsf, beacon_int); - tsf += 1; - tsf *= beacon_int; - } - - return tsf; -} - -int vnt_reset_next_tbtt(struct vnt_private *priv, u16 beacon_interval) -{ - u64 next_tbtt = 0; - u8 data[8]; - - vnt_clear_current_tsf(priv); - - next_tbtt = vnt_get_next_tbtt(next_tbtt, beacon_interval); - - data[0] = (u8)next_tbtt; - data[1] = (u8)(next_tbtt >> 8); - data[2] = (u8)(next_tbtt >> 16); - data[3] = (u8)(next_tbtt >> 24); - data[4] = (u8)(next_tbtt >> 32); - data[5] = (u8)(next_tbtt >> 40); - data[6] = (u8)(next_tbtt >> 48); - data[7] = (u8)(next_tbtt >> 56); - - return vnt_control_out(priv, MESSAGE_TYPE_SET_TSFTBTT, - MESSAGE_REQUEST_TBTT, 0, 8, data); -} - -int vnt_update_next_tbtt(struct vnt_private *priv, u64 tsf, - u16 beacon_interval) -{ - u8 data[8]; - int ret; - - tsf = vnt_get_next_tbtt(tsf, beacon_interval); - - data[0] = (u8)tsf; - data[1] = (u8)(tsf >> 8); - data[2] = (u8)(tsf >> 16); - data[3] = (u8)(tsf >> 24); - data[4] = (u8)(tsf >> 32); - data[5] = (u8)(tsf >> 40); - data[6] = (u8)(tsf >> 48); - data[7] = (u8)(tsf >> 56); - - ret = vnt_control_out(priv, MESSAGE_TYPE_SET_TSFTBTT, - MESSAGE_REQUEST_TBTT, 0, 8, data); - if (ret) - return ret; - - dev_dbg(&priv->usb->dev, "%s TBTT: %8llx\n", __func__, tsf); - return 0; -} - -/* - * Description: Turn off Radio power - * - * Parameters: - * In: - * priv - The adapter to be turned off - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -int vnt_radio_power_off(struct vnt_private *priv) -{ - int ret = 0; - - switch (priv->rf_type) { - case RF_AL2230: - case RF_AL2230S: - case RF_VT3226: - case RF_VT3226D0: - ret = vnt_mac_reg_bits_off(priv, MAC_REG_SOFTPWRCTL, - (SOFTPWRCTL_SWPE2 | - SOFTPWRCTL_SWPE3)); - break; - } - - if (ret) - goto end; - - ret = vnt_mac_reg_bits_off(priv, MAC_REG_HOSTCR, HOSTCR_RXON); - if (ret) - goto end; - - ret = vnt_set_deep_sleep(priv); - if (ret) - goto end; - - ret = vnt_mac_reg_bits_on(priv, MAC_REG_GPIOCTL1, GPIO3_INTMD); - -end: - return ret; -} - -/* - * Description: Turn on Radio power - * - * Parameters: - * In: - * priv - The adapter to be turned on - * Out: - * none - * - * Return Value: true if success; otherwise false - * - */ -int vnt_radio_power_on(struct vnt_private *priv) -{ - int ret = 0; - - ret = vnt_exit_deep_sleep(priv); - if (ret) - return ret; - - ret = vnt_mac_reg_bits_on(priv, MAC_REG_HOSTCR, HOSTCR_RXON); - if (ret) - return ret; - - switch (priv->rf_type) { - case RF_AL2230: - case RF_AL2230S: - case RF_VT3226: - case RF_VT3226D0: - ret = vnt_mac_reg_bits_on(priv, MAC_REG_SOFTPWRCTL, - (SOFTPWRCTL_SWPE2 | - SOFTPWRCTL_SWPE3)); - if (ret) - return ret; - } - - return vnt_mac_reg_bits_off(priv, MAC_REG_GPIOCTL1, GPIO3_INTMD); -} - -int vnt_set_bss_mode(struct vnt_private *priv) -{ - int ret; - unsigned char type = priv->bb_type; - unsigned char data = 0; - unsigned char bb_vga_2_3 = 0x00; - - ret = vnt_mac_set_bb_type(priv, type); - if (ret) - return ret; - - priv->packet_type = vnt_get_pkt_type(priv); - - if (priv->bb_type == BB_TYPE_11A) { - data = 0x03; - bb_vga_2_3 = 0x10; - } else if (priv->bb_type == BB_TYPE_11B) { - data = 0x02; - } else if (priv->bb_type == BB_TYPE_11G) { - data = 0x08; - } - - if (data) { - ret = vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, - 0x88, data); - if (ret) - return ret; - } - - ret = vnt_update_ifs(priv); - if (ret) - return ret; - - ret = vnt_set_rspinf(priv, priv->bb_type); - if (ret) - return ret; - - priv->bb_vga[2] = bb_vga_2_3; - priv->bb_vga[3] = bb_vga_2_3; - - return vnt_set_vga_gain_offset(priv, priv->bb_vga[0]); -} diff --git a/drivers/staging/vt6656/card.h b/drivers/staging/vt6656/card.h deleted file mode 100644 index eb01f7cc871f4..0000000000000 --- a/drivers/staging/vt6656/card.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Provide functions to setup NIC operation mode - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - */ - -#ifndef __CARD_H__ -#define __CARD_H__ -#include "device.h" - -/* init card type */ - -#define CB_MAX_CHANNEL_24G 14 -#define CB_MAX_CHANNEL_5G 42 /* add channel9(5045MHz), 41==>42 */ -#define CB_MAX_CHANNEL (CB_MAX_CHANNEL_24G + CB_MAX_CHANNEL_5G) - -struct vnt_private; - -int vnt_set_channel(struct vnt_private *priv, u32 connection_channel); -int vnt_set_rspinf(struct vnt_private *priv, u8 bb_type); -int vnt_update_ifs(struct vnt_private *priv); -void vnt_update_top_rates(struct vnt_private *priv); -bool vnt_ofdm_min_rate(struct vnt_private *priv); -int vnt_adjust_tsf(struct vnt_private *priv, u8 rx_rate, - u64 time_stamp, u64 local_tsf); -bool vnt_clear_current_tsf(struct vnt_private *priv); -int vnt_reset_next_tbtt(struct vnt_private *priv, u16 beacon_interval); -int vnt_update_next_tbtt(struct vnt_private *priv, u64 tsf, - u16 beacon_interval); -u64 vnt_get_next_tbtt(u64 tsf, u16 beacon_interval); -u64 vnt_get_tsf_offset(u8 rx_rate, u64 tsf1, u64 tsf2); -int vnt_radio_power_off(struct vnt_private *priv); -int vnt_radio_power_on(struct vnt_private *priv); -u8 vnt_get_pkt_type(struct vnt_private *priv); -int vnt_set_bss_mode(struct vnt_private *priv); - -#endif /* __CARD_H__ */ diff --git a/drivers/staging/vt6656/channel.c b/drivers/staging/vt6656/channel.c deleted file mode 100644 index 413e2fc4a50dc..0000000000000 --- a/drivers/staging/vt6656/channel.c +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Channel number mapping - * - * Author: Lucas Lin - * - * Date: Dec 24, 2004 - * - * - * - * Revision History: - * 01-18-2005 RobertYu: remove the for loop searching in - * ChannelValid, change ChannelRuleTab - * to lookup-type, reorder table items. - * - * - */ - -#include "device.h" -#include "channel.h" -#include "rf.h" - -static struct ieee80211_rate vnt_rates_bg[] = { - { .bitrate = 10, .hw_value = RATE_1M }, - { .bitrate = 20, .hw_value = RATE_2M }, - { .bitrate = 55, .hw_value = RATE_5M }, - { .bitrate = 110, .hw_value = RATE_11M }, - { .bitrate = 60, .hw_value = RATE_6M }, - { .bitrate = 90, .hw_value = RATE_9M }, - { .bitrate = 120, .hw_value = RATE_12M }, - { .bitrate = 180, .hw_value = RATE_18M }, - { .bitrate = 240, .hw_value = RATE_24M }, - { .bitrate = 360, .hw_value = RATE_36M }, - { .bitrate = 480, .hw_value = RATE_48M }, - { .bitrate = 540, .hw_value = RATE_54M }, -}; - -static struct ieee80211_channel vnt_channels_2ghz[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 } -}; - -static struct ieee80211_supported_band vnt_supported_2ghz_band = { - .channels = vnt_channels_2ghz, - .n_channels = ARRAY_SIZE(vnt_channels_2ghz), - .bitrates = vnt_rates_bg, - .n_bitrates = ARRAY_SIZE(vnt_rates_bg), -}; - -void vnt_init_bands(struct vnt_private *priv) -{ - struct ieee80211_channel *ch; - int i; - - ch = vnt_channels_2ghz; - for (i = 0; i < ARRAY_SIZE(vnt_channels_2ghz); i++) { - ch[i].max_power = VNT_RF_MAX_POWER; - ch[i].flags = IEEE80211_CHAN_NO_HT40; - } - priv->hw->wiphy->bands[NL80211_BAND_2GHZ] = - &vnt_supported_2ghz_band; -} diff --git a/drivers/staging/vt6656/channel.h b/drivers/staging/vt6656/channel.h deleted file mode 100644 index 723660e40310a..0000000000000 --- a/drivers/staging/vt6656/channel.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Country Regulation Rules header file - * - * Author: Lucas Lin - * - * Date: Dec 23, 2004 - * - */ - -#ifndef _CHANNEL_H_ -#define _CHANNEL_H_ - -#include "device.h" - -void vnt_init_bands(struct vnt_private *priv); - -#endif /* _CHANNEL_H_ */ diff --git a/drivers/staging/vt6656/desc.h b/drivers/staging/vt6656/desc.h deleted file mode 100644 index c13561e528db3..0000000000000 --- a/drivers/staging/vt6656/desc.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose:The header file of descriptor - * - * Revision History: - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - */ - -#ifndef __DESC_H__ -#define __DESC_H__ - -#include -#include -#include - -/* max transmit or receive buffer size */ -#define CB_MAX_BUF_SIZE 2900U /* NOTE: must be multiple of 4 */ - -#define MAX_TOTAL_SIZE_WITH_ALL_HEADERS CB_MAX_BUF_SIZE - -#define MAX_INTERRUPT_SIZE 32 - -#define CB_MAX_RX_DESC 128 /* max # of descriptors */ -#define CB_MIN_RX_DESC 16 /* min # of RX descriptors */ -#define CB_MAX_TX_DESC 128 /* max # of descriptors */ -#define CB_MIN_TX_DESC 16 /* min # of TX descriptors */ - -/* - * bits in the RSR register - */ -#define RSR_ADDRBROAD BIT(7) -#define RSR_ADDRMULTI BIT(6) -#define RSR_ADDRUNI 0x00 -#define RSR_IVLDTYP BIT(5) /* invalid packet type */ -#define RSR_IVLDLEN BIT(4) /* invalid len (> 2312 byte) */ -#define RSR_BSSIDOK BIT(3) -#define RSR_CRCOK BIT(2) -#define RSR_BCNSSIDOK BIT(1) -#define RSR_ADDROK BIT(0) - -/* - * bits in the new RSR register - */ -#define NEWRSR_DECRYPTOK BIT(4) -#define NEWRSR_CFPIND BIT(3) -#define NEWRSR_HWUTSF BIT(2) -#define NEWRSR_BCNHITAID BIT(1) -#define NEWRSR_BCNHITAID0 BIT(0) - -/* - * bits in the TSR register - */ -#define TSR_RETRYTMO BIT(3) -#define TSR_TMO BIT(2) -#define TSR_ACKDATA BIT(1) -#define TSR_VALID BIT(0) - -#define FIFOCTL_AUTO_FB_1 0x1000 -#define FIFOCTL_AUTO_FB_0 0x0800 -#define FIFOCTL_GRPACK 0x0400 -#define FIFOCTL_11GA 0x0300 -#define FIFOCTL_11GB 0x0200 -#define FIFOCTL_11B 0x0100 -#define FIFOCTL_11A 0x0000 -#define FIFOCTL_RTS 0x0080 -#define FIFOCTL_ISDMA0 0x0040 -#define FIFOCTL_GENINT 0x0020 -#define FIFOCTL_TMOEN 0x0010 -#define FIFOCTL_LRETRY 0x0008 -#define FIFOCTL_CRCDIS 0x0004 -#define FIFOCTL_NEEDACK 0x0002 -#define FIFOCTL_LHEAD 0x0001 - -/* WMAC definition Frag Control */ -#define FRAGCTL_AES 0x0300 -#define FRAGCTL_TKIP 0x0200 -#define FRAGCTL_LEGACY 0x0100 -#define FRAGCTL_NONENCRYPT 0x0000 -#define FRAGCTL_ENDFRAG 0x0003 -#define FRAGCTL_MIDFRAG 0x0002 -#define FRAGCTL_STAFRAG 0x0001 -#define FRAGCTL_NONFRAG 0x0000 - -#endif /* __DESC_H__ */ diff --git a/drivers/staging/vt6656/device.h b/drivers/staging/vt6656/device.h deleted file mode 100644 index ca974d61d3f44..0000000000000 --- a/drivers/staging/vt6656/device.h +++ /dev/null @@ -1,386 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: MAC Data structure - * - * Author: Tevin Chen - * - * Date: Mar 17, 1997 - * - */ - -#ifndef __DEVICE_H__ -#define __DEVICE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef SIOCETHTOOL -#define DEVICE_ETHTOOL_IOCTL_SUPPORT -#include -#else -#undef DEVICE_ETHTOOL_IOCTL_SUPPORT -#endif - -#define RATE_1M 0 -#define RATE_2M 1 -#define RATE_5M 2 -#define RATE_11M 3 -#define RATE_6M 4 -#define RATE_9M 5 -#define RATE_12M 6 -#define RATE_18M 7 -#define RATE_24M 8 -#define RATE_36M 9 -#define RATE_48M 10 -#define RATE_54M 11 -#define RATE_AUTO 12 - -#define MAX_RATE 12 -#define VNT_B_RATES (BIT(RATE_1M) | BIT(RATE_2M) |\ - BIT(RATE_5M) | BIT(RATE_11M)) - -/* - * device specific - */ - -#include "wcmd.h" -#include "desc.h" -#include "key.h" -#include "card.h" - -#define VNT_USB_VENDOR_ID 0x160a -#define VNT_USB_PRODUCT_ID 0x3184 - -#define DEVICE_NAME "vt6656" -#define DEVICE_FULL_DRV_NAM "VIA Networking Wireless LAN USB Driver" - -#define DEVICE_VERSION "mac80211" - -#define FIRMWARE_VERSION 0x133 /* version 1.51 */ -#define FIRMWARE_NAME "vntwusb.fw" -#define FIRMWARE_CHUNK_SIZE 0x400 - -#define MAX_UINTS 8 -#define OPTION_DEFAULT { [0 ... MAX_UINTS - 1] = -1} - -#define DUPLICATE_RX_CACHE_LENGTH 5 - -#define AUTO_FB_NONE 0 -#define AUTO_FB_0 1 -#define AUTO_FB_1 2 - -#define FB_RATE0 0 -#define FB_RATE1 1 - -/* Antenna Mode */ -#define ANT_A 0 -#define ANT_B 1 -#define ANT_DIVERSITY 2 -#define ANT_RXD_TXA 3 -#define ANT_RXD_TXB 4 -#define ANT_UNKNOWN 0xFF -#define ANT_TXA 0 -#define ANT_TXB 1 -#define ANT_RXA 2 -#define ANT_RXB 3 - -#define BB_VGA_LEVEL 4 -#define BB_VGA_CHANGE_THRESHOLD 3 - -#define EEP_MAX_CONTEXT_SIZE 256 - -/* Contents in the EEPROM */ -#define EEP_OFS_PAR 0x0 -#define EEP_OFS_ANTENNA 0x17 -#define EEP_OFS_RADIOCTL 0x18 -#define EEP_OFS_RFTYPE 0x1b -#define EEP_OFS_MINCHANNEL 0x1c -#define EEP_OFS_MAXCHANNEL 0x1d -#define EEP_OFS_SIGNATURE 0x1e -#define EEP_OFS_ZONETYPE 0x1f -#define EEP_OFS_RFTABLE 0x20 -#define EEP_OFS_PWR_CCK 0x20 -#define EEP_OFS_SETPT_CCK 0x21 -#define EEP_OFS_PWR_OFDMG 0x23 - -#define EEP_OFS_CALIB_TX_IQ 0x24 -#define EEP_OFS_CALIB_TX_DC 0x25 -#define EEP_OFS_CALIB_RX_IQ 0x26 - -#define EEP_OFS_MAJOR_VER 0x2e -#define EEP_OFS_MINOR_VER 0x2f - -#define EEP_OFS_CCK_PWR_TBL 0x30 -#define EEP_OFS_OFDM_PWR_TBL 0x40 -#define EEP_OFS_OFDMA_PWR_TBL 0x50 - -/* Bits in EEP_OFS_ANTENNA */ -#define EEP_ANTENNA_MAIN BIT(0) -#define EEP_ANTENNA_AUX BIT(1) -#define EEP_ANTINV BIT(2) - -/* Bits in EEP_OFS_RADIOCTL */ -#define EEP_RADIOCTL_ENABLE BIT(7) - -/* control commands */ -#define MESSAGE_TYPE_READ 0x1 -#define MESSAGE_TYPE_WRITE 0x0 -#define MESSAGE_TYPE_LOCK_OR 0x2 -#define MESSAGE_TYPE_LOCK_AND 0x3 -#define MESSAGE_TYPE_WRITE_MASK 0x4 -#define MESSAGE_TYPE_CARDINIT 0x5 -#define MESSAGE_TYPE_INIT_RSP 0x6 -#define MESSAGE_TYPE_MACSHUTDOWN 0x7 -#define MESSAGE_TYPE_SETKEY 0x8 -#define MESSAGE_TYPE_CLRKEYENTRY 0x9 -#define MESSAGE_TYPE_WRITE_MISCFF 0xa -#define MESSAGE_TYPE_SET_ANTMD 0xb -#define MESSAGE_TYPE_SELECT_CHANNEL 0xc -#define MESSAGE_TYPE_SET_TSFTBTT 0xd -#define MESSAGE_TYPE_SET_SSTIFS 0xe -#define MESSAGE_TYPE_CHANGE_BBTYPE 0xf -#define MESSAGE_TYPE_DISABLE_PS 0x10 -#define MESSAGE_TYPE_WRITE_IFRF 0x11 - -/* command read/write(index) */ -#define MESSAGE_REQUEST_MEM 0x1 -#define MESSAGE_REQUEST_BBREG 0x2 -#define MESSAGE_REQUEST_MACREG 0x3 -#define MESSAGE_REQUEST_EEPROM 0x4 -#define MESSAGE_REQUEST_TSF 0x5 -#define MESSAGE_REQUEST_TBTT 0x6 -#define MESSAGE_REQUEST_BBAGC 0x7 -#define MESSAGE_REQUEST_VERSION 0x8 -#define MESSAGE_REQUEST_RF_INIT 0x9 -#define MESSAGE_REQUEST_RF_INIT2 0xa -#define MESSAGE_REQUEST_RF_CH0 0xb -#define MESSAGE_REQUEST_RF_CH1 0xc -#define MESSAGE_REQUEST_RF_CH2 0xd - -/* USB registers */ -#define USB_REG4 0x604 - -#define DEVICE_INIT_COLD 0x0 /* cold init */ -#define DEVICE_INIT_RESET 0x1 /* reset init or Dx to D0 power remain */ -#define DEVICE_INIT_DXPL 0x2 /* Dx to D0 power lost init */ - -/* Device init */ -struct vnt_cmd_card_init { - u8 init_class; - u8 exist_sw_net_addr; - u8 sw_net_addr[6]; - u8 short_retry_limit; - u8 long_retry_limit; -}; - -struct vnt_rsp_card_init { - u8 status; - u8 net_addr[6]; - u8 rf_type; - u8 min_channel; - u8 max_channel; -}; - -/* USB */ - -/* - * Enum of context types for SendPacket - */ -enum { - CONTEXT_DATA_PACKET = 0, - CONTEXT_BEACON_PACKET -}; - -struct vnt_rx_header { - u32 wbk_status; - u8 rx_sts; - u8 rx_rate; - u16 pay_load_len; -} __packed; - -struct vnt_rx_tail { - __le64 tsf_time; - u8 sq; - u8 new_rsr; - u8 rssi; - u8 rsr; - u8 sq_3; -} __packed; - -/* RCB (Receive Control Block) */ -struct vnt_rcb { - void *priv; - struct urb *urb; - struct sk_buff *skb; -}; - -/* used to track bulk out irps */ -struct vnt_usb_send_context { - void *priv; - struct sk_buff *skb; - void *tx_buffer; - u32 frame_len; - u16 tx_hdr_size; - u16 tx_rate; - u8 type; - u8 pkt_no; - u8 pkt_type; - bool in_use; -}; - -/* - * Structure to keep track of USB interrupt packets - */ -struct vnt_interrupt_buffer { - u8 *data_buf; -}; - -/* flags for options */ -#define DEVICE_FLAGS_UNPLUG 0 -#define DEVICE_FLAGS_DISCONNECTED 1 - -struct vnt_private { - /* mac80211 */ - struct ieee80211_hw *hw; - struct ieee80211_vif *vif; - u8 mac_hw; - /* netdev */ - struct usb_device *usb; - struct usb_interface *intf; - - u64 tsf_time; - - u32 rx_buf_sz; - int mc_list_count; - - spinlock_t lock; /* prepare tx USB URB */ - struct mutex usb_lock; /* USB control messages */ - - unsigned long flags; - - /* USB */ - struct urb *interrupt_urb; - u32 int_interval; - - /* Variables to track resources for the BULK In Pipe */ - struct vnt_rcb *rcb[CB_MAX_RX_DESC]; - u32 num_rcb; - - /* Variables to track resources for the BULK Out Pipe */ - struct vnt_usb_send_context *tx_context[CB_MAX_TX_DESC]; - struct usb_anchor tx_submitted; - u32 num_tx_context; - - /* Variables to track resources for the Interrupt In Pipe */ - struct vnt_interrupt_buffer int_buf; - - /* Version control */ - u16 firmware_version; - u8 local_id; - u8 rf_type; - u8 bb_rx_conf; - - struct vnt_cmd_card_init init_command; - struct vnt_rsp_card_init init_response; - u8 current_net_addr[ETH_ALEN] __aligned(2); - u8 permanent_net_addr[ETH_ALEN] __aligned(2); - - u8 exist_sw_net_addr; - - u64 current_tsf; - - /* 802.11 MAC specific */ - u32 current_rssi; - - /* Antenna Diversity */ - int tx_rx_ant_inv; - u32 rx_antenna_sel; - u8 rx_antenna_mode; - u8 tx_antenna_mode; - u8 radio_ctl; - - /* IFS & Cw */ - u32 sifs; /* Current SIFS */ - u32 difs; /* Current DIFS */ - u32 eifs; /* Current EIFS */ - u32 slot; /* Current SlotTime */ - - /* Rate */ - u8 bb_type; /* 0: 11A, 1:11B, 2:11G */ - u8 packet_type; /* 0:11a 1:11b 2:11gb 3:11ga */ - u32 basic_rates; - u8 top_ofdm_basic_rate; - u8 top_cck_basic_rate; - - u8 eeprom[EEP_MAX_CONTEXT_SIZE]; /*u32 alignment */ - - u8 preamble_type; - - /* For RF Power table */ - u8 cck_pwr; - u8 ofdm_pwr_g; - u8 ofdm_pwr_a; - u8 power; - u8 cck_pwr_tbl[14]; - u8 ofdm_pwr_tbl[14]; - u8 ofdm_a_pwr_tbl[42]; - - u16 tx_rate_fb0; - u16 tx_rate_fb1; - - enum nl80211_iftype op_mode; - - int short_slot_time; - - /* Power save */ - u16 current_aid; - - /* Beacon related */ - u16 seq_counter; - - enum vnt_cmd_state command_state; - - enum vnt_cmd command; - - /* 802.11 counter */ - - enum vnt_cmd cmd_queue[CMD_Q_SIZE]; - u32 cmd_dequeue_idx; - u32 cmd_enqueue_idx; - u32 free_cmd_queue; - int cmd_running; - - unsigned long key_entry_inuse; - - u8 auto_fb_ctrl; - - /* For Update BaseBand VGA Gain Offset */ - u8 bb_vga[BB_VGA_LEVEL]; - - u8 bb_pre_ed_rssi; - u8 bb_pre_ed_index; - - /* command timer */ - struct delayed_work run_command_work; - - struct ieee80211_low_level_stats low_stats; -}; - -int vnt_init(struct vnt_private *priv); - -#endif diff --git a/drivers/staging/vt6656/key.c b/drivers/staging/vt6656/key.c deleted file mode 100644 index bdc5f30c4f9d2..0000000000000 --- a/drivers/staging/vt6656/key.c +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions for 802.11i Key management - * - * Author: Jerry Chen - * - * Date: May 29, 2003 - * - * Functions: - * - * Revision History: - * - */ - -#include "mac.h" -#include "key.h" -#include "usbpipe.h" - -int vnt_key_init_table(struct vnt_private *priv) -{ - u8 i; - u8 data[MAX_KEY_TABLE]; - - for (i = 0; i < MAX_KEY_TABLE; i++) - data[i] = i; - - return vnt_control_out(priv, MESSAGE_TYPE_CLRKEYENTRY, - 0, 0, ARRAY_SIZE(data), data); -} - -static int vnt_set_keymode(struct ieee80211_hw *hw, u8 *mac_addr, - struct ieee80211_key_conf *key, u32 key_type, - u32 mode) -{ - struct vnt_private *priv = hw->priv; - u8 broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - u16 key_mode = 0; - u32 entry = 0; - u8 *bssid; - u8 key_inx = key->keyidx; - u8 i; - - if (mac_addr) - bssid = mac_addr; - else - bssid = &broadcast[0]; - - if (key_type != VNT_KEY_DEFAULTKEY) { - for (i = 0; i < (MAX_KEY_TABLE - 1); i++) { - if (!test_bit(i, &priv->key_entry_inuse)) { - set_bit(i, &priv->key_entry_inuse); - - key->hw_key_idx = i; - entry = key->hw_key_idx; - break; - } - } - } - - switch (key_type) { - case VNT_KEY_DEFAULTKEY: - /* default key last entry */ - entry = MAX_KEY_TABLE - 1; - key->hw_key_idx = entry; - fallthrough; - case VNT_KEY_GROUP_ADDRESS: - key_mode = mode | (mode << 4); - break; - case VNT_KEY_GROUP: - key_mode = mode << 4; - break; - case VNT_KEY_PAIRWISE: - key_mode |= mode; - key_inx = 4; - break; - default: - return -EINVAL; - } - - key_mode |= key_type; - - if (mode == KEY_CTL_WEP) { - if (key->keylen == WLAN_KEY_LEN_WEP40) - key->key[15] &= 0x7f; - if (key->keylen == WLAN_KEY_LEN_WEP104) - key->key[15] |= 0x80; - } - - return vnt_mac_set_keyentry(priv, key_mode, entry, - key_inx, bssid, key->key); -} - -int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - struct ieee80211_vif *vif, struct ieee80211_key_conf *key) -{ - struct vnt_private *priv = hw->priv; - u8 *mac_addr = NULL; - u8 key_dec_mode = 0; - - if (sta) - mac_addr = &sta->addr[0]; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - vnt_set_keymode(hw, mac_addr, key, VNT_KEY_DEFAULTKEY, - KEY_CTL_WEP); - - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - - return vnt_set_keymode(hw, mac_addr, key, VNT_KEY_DEFAULTKEY, - KEY_CTL_WEP); - - case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - - key_dec_mode = KEY_CTL_TKIP; - - break; - case WLAN_CIPHER_SUITE_CCMP: - if (priv->local_id <= MAC_REVISION_A1) - return -EOPNOTSUPP; - - key_dec_mode = KEY_CTL_CCMP; - - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - break; - default: - return -EOPNOTSUPP; - } - - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - return vnt_set_keymode(hw, mac_addr, key, VNT_KEY_PAIRWISE, - key_dec_mode); - - return vnt_set_keymode(hw, mac_addr, key, - VNT_KEY_GROUP_ADDRESS, key_dec_mode); -} diff --git a/drivers/staging/vt6656/key.h b/drivers/staging/vt6656/key.h deleted file mode 100644 index 6f1d5b4f6da78..0000000000000 --- a/drivers/staging/vt6656/key.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Implement functions for 802.11i Key management - * - * Author: Jerry Chen - * - * Date: May 29, 2003 - * - */ - -#ifndef __KEY_H__ -#define __KEY_H__ - -#include "device.h" - -#define MAX_KEY_TABLE 11 - -#define KEY_CTL_WEP 0x00 -#define KEY_CTL_NONE 0x01 -#define KEY_CTL_TKIP 0x02 -#define KEY_CTL_CCMP 0x03 - -#define VNT_KEY_ONFLY_ALL 0x4000 -#define VNT_KEY_ONFLY 0x8000 -#define VNT_KEY_ALLGROUP 0x04 -#define VNT_KEY_GROUP 0x40 -#define VNT_KEY_PAIRWISE VNT_KEY_ONFLY -#define VNT_KEY_GROUP_ADDRESS (VNT_KEY_ALLGROUP | VNT_KEY_GROUP) -#define VNT_KEY_DEFAULTKEY (VNT_KEY_GROUP_ADDRESS | VNT_KEY_ONFLY |\ - VNT_KEY_ONFLY_ALL) - -int vnt_key_init_table(struct vnt_private *priv); - -int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - struct ieee80211_vif *vif, struct ieee80211_key_conf *key); - -#endif /* __KEY_H__ */ diff --git a/drivers/staging/vt6656/mac.c b/drivers/staging/vt6656/mac.c deleted file mode 100644 index 49430c0a99b8d..0000000000000 --- a/drivers/staging/vt6656/mac.c +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: MAC routines - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - * Functions: - * - * Revision History: - */ - -#include - -#include "desc.h" -#include "mac.h" -#include "usbpipe.h" - -int vnt_mac_set_filter(struct vnt_private *priv, u64 mc_filter) -{ - __le64 le_mc = cpu_to_le64(mc_filter); - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_MAR0, - MESSAGE_REQUEST_MACREG, sizeof(le_mc), - (u8 *)&le_mc); -} - -int vnt_mac_shutdown(struct vnt_private *priv) -{ - return vnt_control_out(priv, MESSAGE_TYPE_MACSHUTDOWN, 0, 0, 0, NULL); -} - -int vnt_mac_set_bb_type(struct vnt_private *priv, u8 type) -{ - u8 data[2]; - - data[0] = type; - data[1] = EN_CFG_BB_TYPE_MASK; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_ENCFG0, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), - data); -} - -int vnt_mac_disable_keyentry(struct vnt_private *priv, u8 entry_idx) -{ - return vnt_control_out(priv, MESSAGE_TYPE_CLRKEYENTRY, 0, 0, - sizeof(entry_idx), &entry_idx); -} - -int vnt_mac_set_keyentry(struct vnt_private *priv, u16 key_ctl, u32 entry_idx, - u32 key_idx, u8 *addr, u8 *key) -{ - struct vnt_mac_set_key set_key; - u16 offset; - - offset = MISCFIFO_KEYETRY0; - offset += entry_idx * MISCFIFO_KEYENTRYSIZE; - - set_key.u.write.key_ctl = cpu_to_le16(key_ctl); - ether_addr_copy(set_key.u.write.addr, addr); - - /* swap over swap[0] and swap[1] to get correct write order */ - swap(set_key.u.swap[0], set_key.u.swap[1]); - - memcpy(set_key.key, key, WLAN_KEY_LEN_CCMP); - - dev_dbg(&priv->usb->dev, "offset %d key ctl %d set key %24ph\n", - offset, key_ctl, (u8 *)&set_key); - - return vnt_control_out(priv, MESSAGE_TYPE_SETKEY, offset, - (u16)key_idx, sizeof(struct vnt_mac_set_key), - (u8 *)&set_key); -} - -int vnt_mac_reg_bits_off(struct vnt_private *priv, u8 reg_ofs, u8 bits) -{ - u8 data[2]; - - data[0] = 0; - data[1] = bits; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, reg_ofs, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_reg_bits_on(struct vnt_private *priv, u8 reg_ofs, u8 bits) -{ - u8 data[2]; - - data[0] = bits; - data[1] = bits; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, reg_ofs, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_write_word(struct vnt_private *priv, u8 reg_ofs, u16 word) -{ - u8 data[2]; - - data[0] = (u8)(word & 0xff); - data[1] = (u8)(word >> 8); - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, reg_ofs, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_set_bssid_addr(struct vnt_private *priv, u8 *addr) -{ - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_BSSID0, - MESSAGE_REQUEST_MACREG, ETH_ALEN, addr); -} - -int vnt_mac_enable_protect_mode(struct vnt_private *priv) -{ - u8 data[2]; - - data[0] = EN_CFG_PROTECT_MD; - data[1] = EN_CFG_PROTECT_MD; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_ENCFG0, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_disable_protect_mode(struct vnt_private *priv) -{ - u8 data[2]; - - data[0] = 0; - data[1] = EN_CFG_PROTECT_MD; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_ENCFG0, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_enable_barker_preamble_mode(struct vnt_private *priv) -{ - u8 data[2]; - - data[0] = EN_CFG_BARKER_PREAM; - data[1] = EN_CFG_BARKER_PREAM; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_ENCFG2, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_disable_barker_preamble_mode(struct vnt_private *priv) -{ - u8 data[2]; - - data[0] = 0; - data[1] = EN_CFG_BARKER_PREAM; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_ENCFG2, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_set_beacon_interval(struct vnt_private *priv, u16 interval) -{ - u8 data[2]; - - data[0] = (u8)(interval & 0xff); - data[1] = (u8)(interval >> 8); - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_BI, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} - -int vnt_mac_set_led(struct vnt_private *priv, u8 state, u8 led) -{ - u8 data[2]; - - data[0] = led; - data[1] = state; - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_PAPEDELAY, - MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data); -} diff --git a/drivers/staging/vt6656/mac.h b/drivers/staging/vt6656/mac.h deleted file mode 100644 index 0ac845bd3c5ad..0000000000000 --- a/drivers/staging/vt6656/mac.h +++ /dev/null @@ -1,373 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: MAC routines - * - * Author: Tevin Chen - * - * Date: May 21, 1996 - * - * Revision History: - * 07-01-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. - * 08-25-2003 Kyle Hsu: Porting MAC functions from sim53. - * 09-03-2003 Bryan YC Fan: Add MACvDisableProtectMD & MACvEnableProtectMD - */ - -#ifndef __MAC_H__ -#define __MAC_H__ - -#include -#include "device.h" - -#define REV_ID_VT3253_A0 0x00 -#define REV_ID_VT3253_A1 0x01 -#define REV_ID_VT3253_B0 0x08 -#define REV_ID_VT3253_B1 0x09 - -/* Registers in the MAC */ -#define MAC_REG_BISTCMD 0x04 -#define MAC_REG_BISTSR0 0x05 -#define MAC_REG_BISTSR1 0x06 -#define MAC_REG_BISTSR2 0x07 -#define MAC_REG_I2MCSR 0x08 -#define MAC_REG_I2MTGID 0x09 -#define MAC_REG_I2MTGAD 0x0a -#define MAC_REG_I2MCFG 0x0b -#define MAC_REG_I2MDIPT 0x0c -#define MAC_REG_I2MDOPT 0x0e -#define MAC_REG_USBSUS 0x0f - -#define MAC_REG_LOCALID 0x14 -#define MAC_REG_TESTCFG 0x15 -#define MAC_REG_JUMPER0 0x16 -#define MAC_REG_JUMPER1 0x17 -#define MAC_REG_TMCTL 0x18 -#define MAC_REG_TMDATA0 0x1c -#define MAC_REG_TMDATA1 0x1d -#define MAC_REG_TMDATA2 0x1e -#define MAC_REG_TMDATA3 0x1f - -/* MAC Parameter related */ -#define MAC_REG_LRT 0x20 -#define MAC_REG_SRT 0x21 -#define MAC_REG_SIFS 0x22 -#define MAC_REG_DIFS 0x23 -#define MAC_REG_EIFS 0x24 -#define MAC_REG_SLOT 0x25 -#define MAC_REG_BI 0x26 -#define MAC_REG_CWMAXMIN0 0x28 -#define MAC_REG_LINKOFFTOTM 0x2a -#define MAC_REG_SWTMOT 0x2b -#define MAC_REG_RTSOKCNT 0x2c -#define MAC_REG_RTSFAILCNT 0x2d -#define MAC_REG_ACKFAILCNT 0x2e -#define MAC_REG_FCSERRCNT 0x2f - -/* TSF Related */ -#define MAC_REG_TSFCNTR 0x30 -#define MAC_REG_NEXTTBTT 0x38 -#define MAC_REG_TSFOFST 0x40 -#define MAC_REG_TFTCTL 0x48 - -/* WMAC Control/Status Related */ -#define MAC_REG_ENCFG0 0x4c -#define MAC_REG_ENCFG1 0x4d -#define MAC_REG_ENCFG2 0x4e - -#define MAC_REG_CFG 0x50 -#define MAC_REG_TEST 0x52 -#define MAC_REG_HOSTCR 0x54 -#define MAC_REG_MACCR 0x55 -#define MAC_REG_RCR 0x56 -#define MAC_REG_TCR 0x57 -#define MAC_REG_IMR 0x58 -#define MAC_REG_ISR 0x5c -#define MAC_REG_ISR1 0x5d - -/* Power Saving Related */ -#define MAC_REG_PSCFG 0x60 -#define MAC_REG_PSCTL 0x61 -#define MAC_REG_PSPWRSIG 0x62 -#define MAC_REG_BBCR13 0x63 -#define MAC_REG_AIDATIM 0x64 -#define MAC_REG_PWBT 0x66 -#define MAC_REG_WAKEOKTMR 0x68 -#define MAC_REG_CALTMR 0x69 -#define MAC_REG_SYNSPACCNT 0x6a -#define MAC_REG_WAKSYNOPT 0x6b - -/* Baseband/IF Control Group */ -#define MAC_REG_BBREGCTL 0x6c -#define MAC_REG_CHANNEL 0x6d -#define MAC_REG_BBREGADR 0x6e -#define MAC_REG_BBREGDATA 0x6f -#define MAC_REG_IFREGCTL 0x70 -#define MAC_REG_IFDATA 0x71 -#define MAC_REG_ITRTMSET 0x74 -#define MAC_REG_PAPEDELAY 0x77 -#define MAC_REG_SOFTPWRCTL 0x78 -#define MAC_REG_SOFTPWRCTL2 0x79 -#define MAC_REG_GPIOCTL0 0x7a -#define MAC_REG_GPIOCTL1 0x7b - -/* MiscFF PIO related */ -#define MAC_REG_MISCFFNDEX 0xbc -#define MAC_REG_MISCFFCTL 0xbe -#define MAC_REG_MISCFFDATA 0xc0 - -/* MAC Configuration Group */ -#define MAC_REG_PAR0 0xc4 -#define MAC_REG_PAR4 0xc8 -#define MAC_REG_BSSID0 0xcc -#define MAC_REG_BSSID4 0xd0 -#define MAC_REG_MAR0 0xd4 -#define MAC_REG_MAR4 0xd8 - -/* MAC RSPPKT INFO Group */ -#define MAC_REG_RSPINF_B_1 0xdC -#define MAC_REG_RSPINF_B_2 0xe0 -#define MAC_REG_RSPINF_B_5 0xe4 -#define MAC_REG_RSPINF_B_11 0xe8 -#define MAC_REG_RSPINF_A_6 0xec -#define MAC_REG_RSPINF_A_9 0xee -#define MAC_REG_RSPINF_A_12 0xf0 -#define MAC_REG_RSPINF_A_18 0xf2 -#define MAC_REG_RSPINF_A_24 0xf4 -#define MAC_REG_RSPINF_A_36 0xf6 -#define MAC_REG_RSPINF_A_48 0xf8 -#define MAC_REG_RSPINF_A_54 0xfa -#define MAC_REG_RSPINF_A_72 0xfc - -/* Bits in the I2MCFG EEPROM register */ -#define I2MCFG_BOUNDCTL BIT(7) -#define I2MCFG_WAITCTL BIT(5) -#define I2MCFG_SCLOECTL BIT(4) -#define I2MCFG_WBUSYCTL BIT(3) -#define I2MCFG_NORETRY BIT(2) -#define I2MCFG_I2MLDSEQ BIT(1) -#define I2MCFG_I2CMFAST BIT(0) - -/* Bits in the I2MCSR EEPROM register */ -#define I2MCSR_EEMW BIT(7) -#define I2MCSR_EEMR BIT(6) -#define I2MCSR_AUTOLD BIT(3) -#define I2MCSR_NACK BIT(1) -#define I2MCSR_DONE BIT(0) - -/* Bits in the TMCTL register */ -#define TMCTL_TSUSP BIT(2) -#define TMCTL_TMD BIT(1) -#define TMCTL_TE BIT(0) - -/* Bits in the TFTCTL register */ -#define TFTCTL_HWUTSF BIT(7) -#define TFTCTL_TBTTSYNC BIT(6) -#define TFTCTL_HWUTSFEN BIT(5) -#define TFTCTL_TSFCNTRRD BIT(4) -#define TFTCTL_TBTTSYNCEN BIT(3) -#define TFTCTL_TSFSYNCEN BIT(2) -#define TFTCTL_TSFCNTRST BIT(1) -#define TFTCTL_TSFCNTREN BIT(0) - -/* Bits in the EnhanceCFG_0 register */ -#define EN_CFG_BB_TYPE_A 0x00 -#define EN_CFG_BB_TYPE_B BIT(0) -#define EN_CFG_BB_TYPE_G BIT(1) -#define EN_CFG_BB_TYPE_MASK (EN_CFG_BB_TYPE_B | EN_CFG_BB_TYPE_G) -#define EN_CFG_PROTECT_MD BIT(5) - -/* Bits in the EnhanceCFG_1 register */ -#define EN_CFG_BCN_SUS_IND BIT(0) -#define EN_CFG_BCN_SUS_CLR BIT(1) - -/* Bits in the EnhanceCFG_2 register */ -#define EN_CFG_NXTBTTCFPSTR BIT(0) -#define EN_CFG_BARKER_PREAM BIT(1) -#define EN_CFG_PKT_BURST_MD BIT(2) - -/* Bits in the CFG register */ -#define CFG_TKIPOPT BIT(7) -#define CFG_RXDMAOPT BIT(6) -#define CFG_TMOT_SW BIT(5) -#define CFG_TMOT_HWLONG BIT(4) -#define CFG_TMOT_HW 0x00 -#define CFG_CFPENDOPT BIT(3) -#define CFG_BCNSUSEN BIT(2) -#define CFG_NOTXTIMEOUT BIT(1) -#define CFG_NOBUFOPT BIT(0) - -/* Bits in the TEST register */ -#define TEST_LBEXT BIT(7) -#define TEST_LBINT BIT(6) -#define TEST_LBNONE 0x00 -#define TEST_SOFTINT BIT(5) -#define TEST_CONTTX BIT(4) -#define TEST_TXPE BIT(3) -#define TEST_NAVDIS BIT(2) -#define TEST_NOCTS BIT(1) -#define TEST_NOACK BIT(0) - -/* Bits in the HOSTCR register */ -#define HOSTCR_TXONST BIT(7) -#define HOSTCR_RXONST BIT(6) -#define HOSTCR_ADHOC BIT(5) -#define HOSTCR_AP BIT(4) -#define HOSTCR_TXON BIT(3) -#define HOSTCR_RXON BIT(2) -#define HOSTCR_MACEN BIT(1) -#define HOSTCR_SOFTRST BIT(0) - -/* Bits in the MACCR register */ -#define MACCR_SYNCFLUSHOK BIT(2) -#define MACCR_SYNCFLUSH BIT(1) -#define MACCR_CLRNAV BIT(0) - -/* Bits in the RCR register */ -#define RCR_SSID BIT(7) -#define RCR_RXALLTYPE BIT(6) -#define RCR_UNICAST BIT(5) -#define RCR_BROADCAST BIT(4) -#define RCR_MULTICAST BIT(3) -#define RCR_WPAERR BIT(2) -#define RCR_ERRCRC BIT(1) -#define RCR_BSSID BIT(0) - -/* Bits in the TCR register */ -#define TCR_SYNCDCFOPT BIT(1) -#define TCR_AUTOBCNTX BIT(0) - -/* ISR1 */ -#define ISR_GPIO3 BIT(6) -#define ISR_RXNOBUF BIT(3) -#define ISR_MIBNEARFULL BIT(2) -#define ISR_SOFTINT BIT(1) -#define ISR_FETALERR BIT(0) - -#define LEDSTS_STS 0x06 -#define LEDSTS_TMLEN 0x78 -#define LEDSTS_OFF 0x00 -#define LEDSTS_ON 0x02 -#define LEDSTS_SLOW 0x04 -#define LEDSTS_INTER 0x06 - -/* ISR0 */ -#define ISR_WATCHDOG BIT(7) -#define ISR_SOFTTIMER BIT(6) -#define ISR_GPIO0 BIT(5) -#define ISR_TBTT BIT(4) -#define ISR_RXDMA0 BIT(3) -#define ISR_BNTX BIT(2) -#define ISR_ACTX BIT(0) - -/* Bits in the PSCFG register */ -#define PSCFG_PHILIPMD BIT(6) -#define PSCFG_WAKECALEN BIT(5) -#define PSCFG_WAKETMREN BIT(4) -#define PSCFG_BBPSPROG BIT(3) -#define PSCFG_WAKESYN BIT(2) -#define PSCFG_SLEEPSYN BIT(1) -#define PSCFG_AUTOSLEEP BIT(0) - -/* Bits in the PSCTL register */ -#define PSCTL_WAKEDONE BIT(5) -#define PSCTL_PS BIT(4) -#define PSCTL_GO2DOZE BIT(3) -#define PSCTL_LNBCN BIT(2) -#define PSCTL_ALBCN BIT(1) -#define PSCTL_PSEN BIT(0) - -/* Bits in the PSPWSIG register */ -#define PSSIG_WPE3 BIT(7) -#define PSSIG_WPE2 BIT(6) -#define PSSIG_WPE1 BIT(5) -#define PSSIG_WRADIOPE BIT(4) -#define PSSIG_SPE3 BIT(3) -#define PSSIG_SPE2 BIT(2) -#define PSSIG_SPE1 BIT(1) -#define PSSIG_SRADIOPE BIT(0) - -/* Bits in the BBREGCTL register */ -#define BBREGCTL_DONE BIT(2) -#define BBREGCTL_REGR BIT(1) -#define BBREGCTL_REGW BIT(0) - -/* Bits in the IFREGCTL register */ -#define IFREGCTL_DONE BIT(2) -#define IFREGCTL_IFRF BIT(1) -#define IFREGCTL_REGW BIT(0) - -/* Bits in the SOFTPWRCTL register */ -#define SOFTPWRCTL_RFLEOPT BIT(3) -#define SOFTPWRCTL_TXPEINV BIT(1) -#define SOFTPWRCTL_SWPECTI BIT(0) -#define SOFTPWRCTL_SWPAPE BIT(5) -#define SOFTPWRCTL_SWCALEN BIT(4) -#define SOFTPWRCTL_SWRADIO_PE BIT(3) -#define SOFTPWRCTL_SWPE2 BIT(2) -#define SOFTPWRCTL_SWPE1 BIT(1) -#define SOFTPWRCTL_SWPE3 BIT(0) - -/* Bits in the GPIOCTL1 register */ -#define GPIO3_MD BIT(5) -#define GPIO3_DATA BIT(6) -#define GPIO3_INTMD BIT(7) - -/* Bits in the MISCFFCTL register */ -#define MISCFFCTL_WRITE BIT(0) - -/* Loopback mode */ -#define MAC_LB_EXT BIT(1) -#define MAC_LB_INTERNAL BIT(0) -#define MAC_LB_NONE 0x00 - -/* Ethernet address filter type */ -#define PKT_TYPE_NONE 0x00 /* turn off receiver */ -#define PKT_TYPE_ALL_MULTICAST BIT(7) -#define PKT_TYPE_PROMISCUOUS BIT(6) -#define PKT_TYPE_DIRECTED BIT(5) /* obselete */ -#define PKT_TYPE_BROADCAST BIT(4) -#define PKT_TYPE_MULTICAST BIT(3) -#define PKT_TYPE_ERROR_WPA BIT(2) -#define PKT_TYPE_ERROR_CRC BIT(1) -#define PKT_TYPE_BSSID BIT(0) - -#define DEFAULT_BI 0x200 - -/* MiscFIFO Offset */ -#define MISCFIFO_KEYETRY0 32 -#define MISCFIFO_KEYENTRYSIZE 22 - -#define MAC_REVISION_A0 0x00 -#define MAC_REVISION_A1 0x01 - -struct vnt_mac_set_key { - union { - struct { - u8 addr[ETH_ALEN]; - __le16 key_ctl; - } write __packed; - u32 swap[2]; - } u; - u8 key[WLAN_KEY_LEN_CCMP]; -} __packed; - -int vnt_mac_set_filter(struct vnt_private *priv, u64 mc_filter); -int vnt_mac_shutdown(struct vnt_private *priv); -int vnt_mac_set_bb_type(struct vnt_private *priv, u8 type); -int vnt_mac_disable_keyentry(struct vnt_private *priv, u8 entry_idx); -int vnt_mac_set_keyentry(struct vnt_private *priv, u16 key_ctl, u32 entry_idx, - u32 key_idx, u8 *addr, u8 *key); -int vnt_mac_reg_bits_off(struct vnt_private *priv, u8 reg_ofs, u8 bits); -int vnt_mac_reg_bits_on(struct vnt_private *priv, u8 reg_ofs, u8 bits); -int vnt_mac_write_word(struct vnt_private *priv, u8 reg_ofs, u16 word); -int vnt_mac_set_bssid_addr(struct vnt_private *priv, u8 *addr); -int vnt_mac_enable_protect_mode(struct vnt_private *priv); -int vnt_mac_disable_protect_mode(struct vnt_private *priv); -int vnt_mac_enable_barker_preamble_mode(struct vnt_private *priv); -int vnt_mac_disable_barker_preamble_mode(struct vnt_private *priv); -int vnt_mac_set_beacon_interval(struct vnt_private *priv, u16 interval); -int vnt_mac_set_led(struct vnt_private *privpriv, u8 state, u8 led); - -#endif /* __MAC_H__ */ diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c deleted file mode 100644 index 4f09e733e7a81..0000000000000 --- a/drivers/staging/vt6656/main_usb.c +++ /dev/null @@ -1,1121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: driver entry for initial, open, close, tx and rx. - * - * Author: Lyndon Chen - * - * Date: Dec 8, 2005 - * - * Functions: - * - * vt6656_probe - module initial (insmod) driver entry - * vnt_free_tx_bufs - free tx buffer function - * vnt_init_registers- initial MAC & BBP & RF internal registers. - * - * Revision History: - */ -#undef __NO_VERSION__ - -#include -#include -#include -#include -#include "device.h" -#include "card.h" -#include "baseband.h" -#include "mac.h" -#include "power.h" -#include "wcmd.h" -#include "rxtx.h" -#include "rf.h" -#include "usbpipe.h" -#include "channel.h" - -/* - * define module options - */ - -/* version information */ -#define DRIVER_AUTHOR \ - "VIA Networking Technologies, Inc., " -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(DEVICE_FULL_DRV_NAM); - -#define RX_DESC_DEF0 64 -static int vnt_rx_buffers = RX_DESC_DEF0; -module_param_named(rx_buffers, vnt_rx_buffers, int, 0644); -MODULE_PARM_DESC(rx_buffers, "Number of receive usb rx buffers"); - -#define TX_DESC_DEF0 64 -static int vnt_tx_buffers = TX_DESC_DEF0; -module_param_named(tx_buffers, vnt_tx_buffers, int, 0644); -MODULE_PARM_DESC(tx_buffers, "Number of receive usb tx buffers"); - -#define RTS_THRESH_DEF 2347 -#define FRAG_THRESH_DEF 2346 - -/* BasebandType[] baseband type selected - * 0: indicate 802.11a type - * 1: indicate 802.11b type - * 2: indicate 802.11g type - */ - -#define BBP_TYPE_DEF 2 - -/* - * Static vars definitions - */ - -static const struct usb_device_id vt6656_table[] = { - {USB_DEVICE(VNT_USB_VENDOR_ID, VNT_USB_PRODUCT_ID)}, - {} -}; - -static void vnt_set_options(struct vnt_private *priv) -{ - /* Set number of TX buffers */ - if (vnt_tx_buffers < CB_MIN_TX_DESC || vnt_tx_buffers > CB_MAX_TX_DESC) - priv->num_tx_context = TX_DESC_DEF0; - else - priv->num_tx_context = vnt_tx_buffers; - - /* Set number of RX buffers */ - if (vnt_rx_buffers < CB_MIN_RX_DESC || vnt_rx_buffers > CB_MAX_RX_DESC) - priv->num_rcb = RX_DESC_DEF0; - else - priv->num_rcb = vnt_rx_buffers; - - priv->op_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->bb_type = BBP_TYPE_DEF; - priv->packet_type = priv->bb_type; - priv->preamble_type = PREAMBLE_LONG; - priv->exist_sw_net_addr = false; -} - -static int vnt_download_firmware(struct vnt_private *priv) -{ - struct device *dev = &priv->usb->dev; - const struct firmware *fw; - u16 length; - int ii; - int ret = 0; - - dev_dbg(dev, "---->Download firmware\n"); - - ret = request_firmware(&fw, FIRMWARE_NAME, dev); - if (ret) { - dev_err(dev, "firmware file %s request failed (%d)\n", - FIRMWARE_NAME, ret); - goto end; - } - - for (ii = 0; ii < fw->size; ii += FIRMWARE_CHUNK_SIZE) { - length = min_t(int, fw->size - ii, FIRMWARE_CHUNK_SIZE); - - ret = vnt_control_out(priv, 0, 0x1200 + ii, 0x0000, length, - fw->data + ii); - if (ret) - goto free_fw; - - dev_dbg(dev, "Download firmware...%d %zu\n", ii, fw->size); - } - -free_fw: - release_firmware(fw); -end: - return ret; -} - -static int vnt_firmware_branch_to_sram(struct vnt_private *priv) -{ - dev_dbg(&priv->usb->dev, "---->Branch to Sram\n"); - - return vnt_control_out(priv, 1, 0x1200, 0x0000, 0, NULL); -} - -static int vnt_check_firmware_version(struct vnt_private *priv) -{ - int ret = 0; - - ret = vnt_control_in(priv, MESSAGE_TYPE_READ, 0, - MESSAGE_REQUEST_VERSION, 2, - (u8 *)&priv->firmware_version); - if (ret) { - dev_dbg(&priv->usb->dev, - "Could not get firmware version: %d.\n", ret); - goto end; - } - - dev_dbg(&priv->usb->dev, "Firmware Version [%04x]\n", - priv->firmware_version); - - if (priv->firmware_version == 0xFFFF) { - dev_dbg(&priv->usb->dev, "In Loader.\n"); - ret = -EINVAL; - goto end; - } - - if (priv->firmware_version < FIRMWARE_VERSION) { - /* branch to loader for download new firmware */ - ret = vnt_firmware_branch_to_sram(priv); - if (ret) { - dev_dbg(&priv->usb->dev, - "Could not branch to SRAM: %d.\n", ret); - } else { - ret = -EINVAL; - } - } - -end: - return ret; -} - -/* - * initialization of MAC & BBP registers - */ -static int vnt_init_registers(struct vnt_private *priv) -{ - int ret; - struct vnt_cmd_card_init *init_cmd = &priv->init_command; - struct vnt_rsp_card_init *init_rsp = &priv->init_response; - u8 antenna; - int ii; - u8 tmp; - u8 calib_tx_iq = 0, calib_tx_dc = 0, calib_rx_iq = 0; - - dev_dbg(&priv->usb->dev, "---->INIbInitAdapter. [%d][%d]\n", - DEVICE_INIT_COLD, priv->packet_type); - - ret = vnt_check_firmware_version(priv); - if (ret) { - ret = vnt_download_firmware(priv); - if (ret) { - dev_dbg(&priv->usb->dev, - "Could not download firmware: %d.\n", ret); - goto end; - } - - ret = vnt_firmware_branch_to_sram(priv); - if (ret) { - dev_dbg(&priv->usb->dev, - "Could not branch to SRAM: %d.\n", ret); - goto end; - } - } - - ret = vnt_vt3184_init(priv); - if (ret) { - dev_dbg(&priv->usb->dev, "vnt_vt3184_init fail\n"); - goto end; - } - - init_cmd->init_class = DEVICE_INIT_COLD; - init_cmd->exist_sw_net_addr = priv->exist_sw_net_addr; - for (ii = 0; ii < ARRAY_SIZE(init_cmd->sw_net_addr); ii++) - init_cmd->sw_net_addr[ii] = priv->current_net_addr[ii]; - init_cmd->short_retry_limit = priv->hw->wiphy->retry_short; - init_cmd->long_retry_limit = priv->hw->wiphy->retry_long; - - /* issue card_init command to device */ - ret = vnt_control_out(priv, MESSAGE_TYPE_CARDINIT, 0, 0, - sizeof(struct vnt_cmd_card_init), - (u8 *)init_cmd); - if (ret) { - dev_dbg(&priv->usb->dev, "Issue Card init fail\n"); - goto end; - } - - ret = vnt_control_in(priv, MESSAGE_TYPE_INIT_RSP, 0, 0, - sizeof(struct vnt_rsp_card_init), - (u8 *)init_rsp); - if (ret) { - dev_dbg(&priv->usb->dev, "Cardinit request in status fail!\n"); - goto end; - } - - /* local ID for AES functions */ - ret = vnt_control_in(priv, MESSAGE_TYPE_READ, MAC_REG_LOCALID, - MESSAGE_REQUEST_MACREG, 1, &priv->local_id); - if (ret) - goto end; - - /* do MACbSoftwareReset in MACvInitialize */ - - priv->top_ofdm_basic_rate = RATE_24M; - priv->top_cck_basic_rate = RATE_1M; - - /* target to IF pin while programming to RF chip */ - priv->power = 0xFF; - - priv->cck_pwr = priv->eeprom[EEP_OFS_PWR_CCK]; - priv->ofdm_pwr_g = priv->eeprom[EEP_OFS_PWR_OFDMG]; - /* load power table */ - for (ii = 0; ii < ARRAY_SIZE(priv->cck_pwr_tbl); ii++) { - priv->cck_pwr_tbl[ii] = - priv->eeprom[ii + EEP_OFS_CCK_PWR_TBL]; - if (priv->cck_pwr_tbl[ii] == 0) - priv->cck_pwr_tbl[ii] = priv->cck_pwr; - - priv->ofdm_pwr_tbl[ii] = - priv->eeprom[ii + EEP_OFS_OFDM_PWR_TBL]; - if (priv->ofdm_pwr_tbl[ii] == 0) - priv->ofdm_pwr_tbl[ii] = priv->ofdm_pwr_g; - } - - /* - * original zonetype is USA, but custom zonetype is Europe, - * then need to recover 12, 13, 14 channels with 11 channel - */ - for (ii = 11; ii < ARRAY_SIZE(priv->cck_pwr_tbl); ii++) { - priv->cck_pwr_tbl[ii] = priv->cck_pwr_tbl[10]; - priv->ofdm_pwr_tbl[ii] = priv->ofdm_pwr_tbl[10]; - } - - priv->ofdm_pwr_a = 0x34; /* same as RFbMA2829SelectChannel */ - - /* load OFDM A power table */ - for (ii = 0; ii < CB_MAX_CHANNEL_5G; ii++) { - priv->ofdm_a_pwr_tbl[ii] = - priv->eeprom[ii + EEP_OFS_OFDMA_PWR_TBL]; - - if (priv->ofdm_a_pwr_tbl[ii] == 0) - priv->ofdm_a_pwr_tbl[ii] = priv->ofdm_pwr_a; - } - - antenna = priv->eeprom[EEP_OFS_ANTENNA]; - - if (antenna & EEP_ANTINV) - priv->tx_rx_ant_inv = true; - else - priv->tx_rx_ant_inv = false; - - antenna &= (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN); - - if (antenna == 0) /* if not set default is both */ - antenna = (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN); - - if (antenna == (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN)) { - priv->tx_antenna_mode = ANT_B; - priv->rx_antenna_sel = 1; - - if (priv->tx_rx_ant_inv) - priv->rx_antenna_mode = ANT_A; - else - priv->rx_antenna_mode = ANT_B; - } else { - priv->rx_antenna_sel = 0; - - if (antenna & EEP_ANTENNA_AUX) { - priv->tx_antenna_mode = ANT_A; - - if (priv->tx_rx_ant_inv) - priv->rx_antenna_mode = ANT_B; - else - priv->rx_antenna_mode = ANT_A; - } else { - priv->tx_antenna_mode = ANT_B; - - if (priv->tx_rx_ant_inv) - priv->rx_antenna_mode = ANT_A; - else - priv->rx_antenna_mode = ANT_B; - } - } - - /* Set initial antenna mode */ - ret = vnt_set_antenna_mode(priv, priv->rx_antenna_mode); - if (ret) - goto end; - - /* default Auto Mode */ - priv->bb_type = BB_TYPE_11G; - - /* get RFType */ - priv->rf_type = init_rsp->rf_type; - - /* load vt3266 calibration parameters in EEPROM */ - if (priv->rf_type == RF_VT3226D0) { - if ((priv->eeprom[EEP_OFS_MAJOR_VER] == 0x1) && - (priv->eeprom[EEP_OFS_MINOR_VER] >= 0x4)) { - calib_tx_iq = priv->eeprom[EEP_OFS_CALIB_TX_IQ]; - calib_tx_dc = priv->eeprom[EEP_OFS_CALIB_TX_DC]; - calib_rx_iq = priv->eeprom[EEP_OFS_CALIB_RX_IQ]; - if (calib_tx_iq || calib_tx_dc || calib_rx_iq) { - /* CR255, enable TX/RX IQ and - * DC compensation mode - */ - ret = vnt_control_out_u8(priv, - MESSAGE_REQUEST_BBREG, - 0xff, 0x03); - if (ret) - goto end; - - /* CR251, TX I/Q Imbalance Calibration */ - ret = vnt_control_out_u8(priv, - MESSAGE_REQUEST_BBREG, - 0xfb, calib_tx_iq); - if (ret) - goto end; - - /* CR252, TX DC-Offset Calibration */ - ret = vnt_control_out_u8(priv, - MESSAGE_REQUEST_BBREG, - 0xfC, calib_tx_dc); - if (ret) - goto end; - - /* CR253, RX I/Q Imbalance Calibration */ - ret = vnt_control_out_u8(priv, - MESSAGE_REQUEST_BBREG, - 0xfd, calib_rx_iq); - if (ret) - goto end; - } else { - /* CR255, turn off - * BB Calibration compensation - */ - ret = vnt_control_out_u8(priv, - MESSAGE_REQUEST_BBREG, - 0xff, 0x0); - if (ret) - goto end; - } - } - } - - /* get permanent network address */ - memcpy(priv->permanent_net_addr, init_rsp->net_addr, 6); - ether_addr_copy(priv->current_net_addr, priv->permanent_net_addr); - - /* if exist SW network address, use it */ - dev_dbg(&priv->usb->dev, "Network address = %pM\n", - priv->current_net_addr); - - priv->radio_ctl = priv->eeprom[EEP_OFS_RADIOCTL]; - - if ((priv->radio_ctl & EEP_RADIOCTL_ENABLE) != 0) { - ret = vnt_control_in(priv, MESSAGE_TYPE_READ, - MAC_REG_GPIOCTL1, MESSAGE_REQUEST_MACREG, - 1, &tmp); - if (ret) - goto end; - - if ((tmp & GPIO3_DATA) == 0) { - ret = vnt_mac_reg_bits_on(priv, MAC_REG_GPIOCTL1, - GPIO3_INTMD); - } else { - ret = vnt_mac_reg_bits_off(priv, MAC_REG_GPIOCTL1, - GPIO3_INTMD); - } - - if (ret) - goto end; - } - - ret = vnt_mac_set_led(priv, LEDSTS_TMLEN, 0x38); - if (ret) - goto end; - - ret = vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_SLOW); - if (ret) - goto end; - - ret = vnt_mac_reg_bits_on(priv, MAC_REG_GPIOCTL0, BIT(0)); - if (ret) - goto end; - - ret = vnt_radio_power_on(priv); - if (ret) - goto end; - - dev_dbg(&priv->usb->dev, "<----INIbInitAdapter Exit\n"); - -end: - return ret; -} - -static void vnt_free_tx_bufs(struct vnt_private *priv) -{ - struct vnt_usb_send_context *tx_context; - int ii; - - usb_kill_anchored_urbs(&priv->tx_submitted); - - for (ii = 0; ii < priv->num_tx_context; ii++) { - tx_context = priv->tx_context[ii]; - if (!tx_context) - continue; - - kfree(tx_context); - } -} - -static void vnt_free_rx_bufs(struct vnt_private *priv) -{ - struct vnt_rcb *rcb; - int ii; - - for (ii = 0; ii < priv->num_rcb; ii++) { - rcb = priv->rcb[ii]; - if (!rcb) - continue; - - /* deallocate URBs */ - if (rcb->urb) { - usb_kill_urb(rcb->urb); - usb_free_urb(rcb->urb); - } - - /* deallocate skb */ - if (rcb->skb) - dev_kfree_skb(rcb->skb); - - kfree(rcb); - } -} - -static void vnt_free_int_bufs(struct vnt_private *priv) -{ - kfree(priv->int_buf.data_buf); -} - -static int vnt_alloc_bufs(struct vnt_private *priv) -{ - int ret; - struct vnt_usb_send_context *tx_context; - struct vnt_rcb *rcb; - int ii; - - init_usb_anchor(&priv->tx_submitted); - - for (ii = 0; ii < priv->num_tx_context; ii++) { - tx_context = kmalloc(sizeof(*tx_context), GFP_KERNEL); - if (!tx_context) { - ret = -ENOMEM; - goto free_tx; - } - - priv->tx_context[ii] = tx_context; - tx_context->priv = priv; - tx_context->pkt_no = ii; - tx_context->in_use = false; - } - - for (ii = 0; ii < priv->num_rcb; ii++) { - priv->rcb[ii] = kzalloc(sizeof(*priv->rcb[ii]), GFP_KERNEL); - if (!priv->rcb[ii]) { - ret = -ENOMEM; - goto free_rx_tx; - } - - rcb = priv->rcb[ii]; - - rcb->priv = priv; - - /* allocate URBs */ - rcb->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!rcb->urb) { - ret = -ENOMEM; - goto free_rx_tx; - } - - rcb->skb = dev_alloc_skb(priv->rx_buf_sz); - if (!rcb->skb) { - ret = -ENOMEM; - goto free_rx_tx; - } - /* submit rx urb */ - ret = vnt_submit_rx_urb(priv, rcb); - if (ret) - goto free_rx_tx; - } - - priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!priv->interrupt_urb) { - ret = -ENOMEM; - goto free_rx_tx; - } - - priv->int_buf.data_buf = kmalloc(MAX_INTERRUPT_SIZE, GFP_KERNEL); - if (!priv->int_buf.data_buf) { - ret = -ENOMEM; - goto free_rx_tx_urb; - } - - return 0; - -free_rx_tx_urb: - usb_free_urb(priv->interrupt_urb); -free_rx_tx: - vnt_free_rx_bufs(priv); -free_tx: - vnt_free_tx_bufs(priv); - return ret; -} - -static void vnt_tx_80211(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct vnt_private *priv = hw->priv; - - if (vnt_tx_packet(priv, skb)) - ieee80211_free_txskb(hw, skb); -} - -static int vnt_start(struct ieee80211_hw *hw) -{ - int ret; - struct vnt_private *priv = hw->priv; - - priv->rx_buf_sz = MAX_TOTAL_SIZE_WITH_ALL_HEADERS; - - ret = vnt_alloc_bufs(priv); - if (ret) { - dev_dbg(&priv->usb->dev, "vnt_alloc_bufs fail...\n"); - goto err; - } - - clear_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags); - - ret = vnt_init_registers(priv); - if (ret) { - dev_dbg(&priv->usb->dev, " init register fail\n"); - goto free_all; - } - - ret = vnt_key_init_table(priv); - if (ret) - goto free_all; - - priv->int_interval = 1; /* bInterval is set to 1 */ - - ret = vnt_start_interrupt_urb(priv); - if (ret) - goto free_all; - - ieee80211_wake_queues(hw); - - return 0; - -free_all: - vnt_free_rx_bufs(priv); - vnt_free_tx_bufs(priv); - vnt_free_int_bufs(priv); - - usb_kill_urb(priv->interrupt_urb); - usb_free_urb(priv->interrupt_urb); -err: - return ret; -} - -static void vnt_stop(struct ieee80211_hw *hw, bool suspend) -{ - struct vnt_private *priv = hw->priv; - int i; - - if (!priv) - return; - - for (i = 0; i < MAX_KEY_TABLE; i++) - vnt_mac_disable_keyentry(priv, i); - - /* clear all keys */ - priv->key_entry_inuse = 0; - - if (!test_bit(DEVICE_FLAGS_UNPLUG, &priv->flags)) - vnt_mac_shutdown(priv); - - ieee80211_stop_queues(hw); - - set_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags); - - cancel_delayed_work_sync(&priv->run_command_work); - - priv->cmd_running = false; - - vnt_free_tx_bufs(priv); - vnt_free_rx_bufs(priv); - vnt_free_int_bufs(priv); - - usb_kill_urb(priv->interrupt_urb); - usb_free_urb(priv->interrupt_urb); -} - -static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - priv->vif = vif; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_ADHOC: - vnt_mac_reg_bits_off(priv, MAC_REG_RCR, RCR_UNICAST); - - vnt_mac_reg_bits_on(priv, MAC_REG_HOSTCR, HOSTCR_ADHOC); - - break; - case NL80211_IFTYPE_AP: - vnt_mac_reg_bits_off(priv, MAC_REG_RCR, RCR_UNICAST); - - vnt_mac_reg_bits_on(priv, MAC_REG_HOSTCR, HOSTCR_AP); - - break; - default: - return -EOPNOTSUPP; - } - - priv->op_mode = vif->type; - - /* LED blink on TX */ - vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_INTER); - - return 0; -} - -static void vnt_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_ADHOC: - vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX); - vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - vnt_mac_reg_bits_off(priv, MAC_REG_HOSTCR, HOSTCR_ADHOC); - break; - case NL80211_IFTYPE_AP: - vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX); - vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - vnt_mac_reg_bits_off(priv, MAC_REG_HOSTCR, HOSTCR_AP); - break; - default: - break; - } - - vnt_radio_power_off(priv); - - priv->op_mode = NL80211_IFTYPE_UNSPECIFIED; - - /* LED slow blink */ - vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_SLOW); -} - -static int vnt_config(struct ieee80211_hw *hw, u32 changed) -{ - struct vnt_private *priv = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - - if (changed & IEEE80211_CONF_CHANGE_PS) { - if (conf->flags & IEEE80211_CONF_PS) - vnt_enable_power_saving(priv, conf->listen_interval); - else - vnt_disable_power_saving(priv); - } - - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || - (conf->flags & IEEE80211_CONF_OFFCHANNEL)) { - vnt_set_channel(priv, conf->chandef.chan->hw_value); - - if (conf->chandef.chan->band == NL80211_BAND_5GHZ) - priv->bb_type = BB_TYPE_11A; - else - priv->bb_type = BB_TYPE_11G; - } - - if (changed & IEEE80211_CONF_CHANGE_POWER) - vnt_rf_setpower(priv, conf->chandef.chan); - - if (conf->flags & (IEEE80211_CONF_OFFCHANNEL | IEEE80211_CONF_IDLE)) - /* Set max sensitivity*/ - vnt_update_pre_ed_threshold(priv, true); - else - vnt_update_pre_ed_threshold(priv, false); - - return 0; -} - -static void vnt_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, u64 changed) -{ - struct vnt_private *priv = hw->priv; - - priv->current_aid = vif->cfg.aid; - - if (changed & BSS_CHANGED_BSSID && conf->bssid) - vnt_mac_set_bssid_addr(priv, (u8 *)conf->bssid); - - if (changed & BSS_CHANGED_BASIC_RATES) { - priv->basic_rates = conf->basic_rates; - - vnt_update_top_rates(priv); - - dev_dbg(&priv->usb->dev, "basic rates %x\n", conf->basic_rates); - } - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - if (conf->use_short_preamble) { - vnt_mac_enable_barker_preamble_mode(priv); - priv->preamble_type = PREAMBLE_SHORT; - } else { - vnt_mac_disable_barker_preamble_mode(priv); - priv->preamble_type = PREAMBLE_LONG; - } - } - - if (changed & BSS_CHANGED_ERP_CTS_PROT) { - if (conf->use_cts_prot) - vnt_mac_enable_protect_mode(priv); - else - vnt_mac_disable_protect_mode(priv); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - if (conf->use_short_slot) - priv->short_slot_time = true; - else - priv->short_slot_time = false; - - vnt_set_short_slot_time(priv); - vnt_set_vga_gain_offset(priv, priv->bb_vga[0]); - } - - if (changed & (BSS_CHANGED_BASIC_RATES | BSS_CHANGED_ERP_PREAMBLE | - BSS_CHANGED_ERP_SLOT)) - vnt_set_bss_mode(priv); - - if (changed & (BSS_CHANGED_TXPOWER | BSS_CHANGED_BANDWIDTH)) - vnt_rf_setpower(priv, conf->chanreq.oper.chan); - - if (changed & BSS_CHANGED_BEACON_ENABLED) { - dev_dbg(&priv->usb->dev, - "Beacon enable %d\n", conf->enable_beacon); - - if (conf->enable_beacon) { - vnt_beacon_enable(priv, vif, conf); - - vnt_mac_reg_bits_on(priv, MAC_REG_TCR, TCR_AUTOBCNTX); - } else { - vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX); - } - } - - if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INFO) && - priv->op_mode != NL80211_IFTYPE_AP) { - if (vif->cfg.assoc && conf->beacon_rate) { - u16 ps_beacon_int = conf->beacon_int; - - if (conf->dtim_period) - ps_beacon_int *= conf->dtim_period; - else if (hw->conf.listen_interval) - ps_beacon_int *= hw->conf.listen_interval; - - vnt_mac_reg_bits_on(priv, MAC_REG_TFTCTL, - TFTCTL_TSFCNTREN); - - vnt_mac_set_beacon_interval(priv, ps_beacon_int); - - vnt_reset_next_tbtt(priv, conf->beacon_int); - - vnt_adjust_tsf(priv, conf->beacon_rate->hw_value, - conf->sync_tsf, priv->current_tsf); - - vnt_update_next_tbtt(priv, - conf->sync_tsf, ps_beacon_int); - } else { - vnt_clear_current_tsf(priv); - - vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, - TFTCTL_TSFCNTREN); - } - } -} - -static u64 vnt_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct vnt_private *priv = hw->priv; - struct netdev_hw_addr *ha; - u64 mc_filter = 0; - u32 bit_nr; - - netdev_hw_addr_list_for_each(ha, mc_list) { - bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; - mc_filter |= BIT_ULL(bit_nr); - } - - priv->mc_list_count = mc_list->count; - - return mc_filter; -} - -static void vnt_configure(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct vnt_private *priv = hw->priv; - u8 rx_mode = 0; - - *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; - - vnt_control_in(priv, MESSAGE_TYPE_READ, MAC_REG_RCR, - MESSAGE_REQUEST_MACREG, sizeof(u8), &rx_mode); - - dev_dbg(&priv->usb->dev, "rx mode in = %x\n", rx_mode); - - if (changed_flags & FIF_ALLMULTI) { - if (*total_flags & FIF_ALLMULTI) { - if (priv->mc_list_count > 2) - vnt_mac_set_filter(priv, ~0); - else - vnt_mac_set_filter(priv, multicast); - - rx_mode |= RCR_MULTICAST | RCR_BROADCAST; - } else { - rx_mode &= ~(RCR_MULTICAST | RCR_BROADCAST); - } - } - - if (changed_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) { - if (*total_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) - rx_mode &= ~RCR_BSSID; - else - rx_mode |= RCR_BSSID; - } - - vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG, MAC_REG_RCR, rx_mode); - - dev_dbg(&priv->usb->dev, "rx mode out= %x\n", rx_mode); -} - -static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct vnt_private *priv = hw->priv; - - switch (cmd) { - case SET_KEY: - return vnt_set_keys(hw, sta, vif, key); - case DISABLE_KEY: - if (test_bit(key->hw_key_idx, &priv->key_entry_inuse)) { - clear_bit(key->hw_key_idx, &priv->key_entry_inuse); - - vnt_mac_disable_keyentry(priv, key->hw_key_idx); - } - break; - - default: - break; - } - - return 0; -} - -static int vnt_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct vnt_private *priv = hw->priv; - - memcpy(stats, &priv->low_stats, sizeof(*stats)); - - return 0; -} - -static u64 vnt_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - return priv->current_tsf; -} - -static void vnt_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u64 tsf) -{ - struct vnt_private *priv = hw->priv; - - vnt_update_next_tbtt(priv, tsf, vif->bss_conf.beacon_int); -} - -static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct vnt_private *priv = hw->priv; - - vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - - vnt_clear_current_tsf(priv); -} - -static const struct ieee80211_ops vnt_mac_ops = { - .add_chanctx = ieee80211_emulate_add_chanctx, - .remove_chanctx = ieee80211_emulate_remove_chanctx, - .change_chanctx = ieee80211_emulate_change_chanctx, - .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, - .tx = vnt_tx_80211, - .wake_tx_queue = ieee80211_handle_wake_tx_queue, - .start = vnt_start, - .stop = vnt_stop, - .add_interface = vnt_add_interface, - .remove_interface = vnt_remove_interface, - .config = vnt_config, - .bss_info_changed = vnt_bss_info_changed, - .prepare_multicast = vnt_prepare_multicast, - .configure_filter = vnt_configure, - .set_key = vnt_set_key, - .get_stats = vnt_get_stats, - .get_tsf = vnt_get_tsf, - .set_tsf = vnt_set_tsf, - .reset_tsf = vnt_reset_tsf, -}; - -int vnt_init(struct vnt_private *priv) -{ - if (vnt_init_registers(priv)) - return -EAGAIN; - - SET_IEEE80211_PERM_ADDR(priv->hw, priv->permanent_net_addr); - - vnt_init_bands(priv); - - if (ieee80211_register_hw(priv->hw)) - return -ENODEV; - - priv->mac_hw = true; - - vnt_radio_power_off(priv); - - return 0; -} - -static int -vt6656_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *udev; - struct vnt_private *priv; - struct ieee80211_hw *hw; - struct wiphy *wiphy; - int rc; - - udev = usb_get_dev(interface_to_usbdev(intf)); - - dev_notice(&udev->dev, "%s Ver. %s\n", - DEVICE_FULL_DRV_NAM, DEVICE_VERSION); - dev_notice(&udev->dev, - "Copyright (c) 2004 VIA Networking Technologies, Inc.\n"); - - hw = ieee80211_alloc_hw(sizeof(struct vnt_private), &vnt_mac_ops); - if (!hw) { - dev_err(&udev->dev, "could not register ieee80211_hw\n"); - rc = -ENOMEM; - goto err_nomem; - } - - priv = hw->priv; - priv->hw = hw; - priv->usb = udev; - priv->intf = intf; - - vnt_set_options(priv); - - spin_lock_init(&priv->lock); - mutex_init(&priv->usb_lock); - - INIT_DELAYED_WORK(&priv->run_command_work, vnt_run_command); - - usb_set_intfdata(intf, priv); - - wiphy = priv->hw->wiphy; - - wiphy->frag_threshold = FRAG_THRESH_DEF; - wiphy->rts_threshold = RTS_THRESH_DEF; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); - - ieee80211_hw_set(priv->hw, TIMING_BEACON_ONLY); - ieee80211_hw_set(priv->hw, SIGNAL_DBM); - ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); - ieee80211_hw_set(priv->hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(priv->hw, SUPPORTS_PS); - ieee80211_hw_set(priv->hw, PS_NULLFUNC_STACK); - - priv->hw->extra_tx_headroom = - sizeof(struct vnt_tx_buffer) + sizeof(struct vnt_tx_usb_header); - priv->hw->max_signal = 100; - - SET_IEEE80211_DEV(priv->hw, &intf->dev); - - rc = usb_reset_device(priv->usb); - if (rc) - dev_warn(&priv->usb->dev, - "%s reset fail status=%d\n", __func__, rc); - - clear_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags); - vnt_reset_command_timer(priv); - - vnt_schedule_command(priv, WLAN_CMD_INIT_MAC80211); - - return 0; - -err_nomem: - usb_put_dev(udev); - - return rc; -} - -static void vt6656_disconnect(struct usb_interface *intf) -{ - struct vnt_private *priv = usb_get_intfdata(intf); - - if (!priv) - return; - - if (priv->mac_hw) - ieee80211_unregister_hw(priv->hw); - - usb_set_intfdata(intf, NULL); - usb_put_dev(interface_to_usbdev(intf)); - - set_bit(DEVICE_FLAGS_UNPLUG, &priv->flags); - - ieee80211_free_hw(priv->hw); -} - -#ifdef CONFIG_PM - -static int vt6656_suspend(struct usb_interface *intf, pm_message_t message) -{ - return 0; -} - -static int vt6656_resume(struct usb_interface *intf) -{ - return 0; -} - -#endif /* CONFIG_PM */ - -MODULE_DEVICE_TABLE(usb, vt6656_table); - -static struct usb_driver vt6656_driver = { - .name = DEVICE_NAME, - .probe = vt6656_probe, - .disconnect = vt6656_disconnect, - .id_table = vt6656_table, -#ifdef CONFIG_PM - .suspend = vt6656_suspend, - .resume = vt6656_resume, -#endif /* CONFIG_PM */ -}; - -module_usb_driver(vt6656_driver); - -MODULE_FIRMWARE(FIRMWARE_NAME); diff --git a/drivers/staging/vt6656/power.c b/drivers/staging/vt6656/power.c deleted file mode 100644 index e5411f6284c76..0000000000000 --- a/drivers/staging/vt6656/power.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handles 802.11 power management functions - * - * Author: Lyndon Chen - * - * Date: July 17, 2002 - * - * Functions: - * vnt_enable_power_saving - Enable Power Saving Mode - * PSvDiasblePowerSaving - Disable Power Saving Mode - * vnt_next_tbtt_wakeup - Decide if we need to wake up at next Beacon - * - * Revision History: - * - */ - -#include "mac.h" -#include "device.h" -#include "power.h" -#include "wcmd.h" -#include "rxtx.h" -#include "card.h" -#include "usbpipe.h" - -/* - * - * Routine Description: - * Enable hw power saving functions - * - * Return Value: - * None. - * - */ - -void vnt_enable_power_saving(struct vnt_private *priv, u16 listen_interval) -{ - u16 aid = priv->current_aid | BIT(14) | BIT(15); - - /* set period of power up before TBTT */ - vnt_mac_write_word(priv, MAC_REG_PWBT, C_PWBT); - - if (priv->op_mode != NL80211_IFTYPE_ADHOC) - /* set AID */ - vnt_mac_write_word(priv, MAC_REG_AIDATIM, aid); - - /* Warren:06-18-2004,the sequence must follow - * PSEN->AUTOSLEEP->GO2DOZE - */ - /* enable power saving hw function */ - vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_PSEN); - - /* Set AutoSleep */ - vnt_mac_reg_bits_on(priv, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); - - /* Warren:MUST turn on this once before turn on AUTOSLEEP ,or the - * AUTOSLEEP doesn't work - */ - vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_GO2DOZE); - - /* always listen beacon */ - vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_ALBCN); - - dev_dbg(&priv->usb->dev, "PS:Power Saving Mode Enable...\n"); -} - -int vnt_disable_power_saving(struct vnt_private *priv) -{ - int ret; - - /* disable power saving hw function */ - ret = vnt_control_out(priv, MESSAGE_TYPE_DISABLE_PS, 0, - 0, 0, NULL); - if (ret) - return ret; - - /* clear AutoSleep */ - vnt_mac_reg_bits_off(priv, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); - - /* set always listen beacon */ - vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_ALBCN); - - return 0; -} - -/* - * - * Routine Description: - * Check if Next TBTT must wake up - * - * Return Value: - * None. - * - */ - -int vnt_next_tbtt_wakeup(struct vnt_private *priv) -{ - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_conf *conf = &hw->conf; - int wake_up = false; - - if (conf->listen_interval > 1) { - /* Turn on wake up to listen next beacon */ - vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_LNBCN); - wake_up = true; - } - - return wake_up; -} diff --git a/drivers/staging/vt6656/power.h b/drivers/staging/vt6656/power.h deleted file mode 100644 index 9f9c700729334..0000000000000 --- a/drivers/staging/vt6656/power.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handles 802.11 power management functions - * - * Author: Lyndon Chen - * - * Date: July 17, 2002 - * - */ - -#ifndef __POWER_H__ -#define __POWER_H__ - -#define C_PWBT 1000 /* micro sec. power up before TBTT */ - -int vnt_disable_power_saving(struct vnt_private *priv); -void vnt_enable_power_saving(struct vnt_private *priv, u16 listen_interval); -int vnt_next_tbtt_wakeup(struct vnt_private *priv); - -#endif /* __POWER_H__ */ diff --git a/drivers/staging/vt6656/rf.c b/drivers/staging/vt6656/rf.c deleted file mode 100644 index 464602c747270..0000000000000 --- a/drivers/staging/vt6656/rf.c +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: rf function code - * - * Author: Jerry Chen - * - * Date: Feb. 19, 2004 - * - * Functions: - * vnt_rf_write_embedded - Embedded write RF register via MAC - * - * Revision History: - * RF_VT3226: RobertYu:20051111, VT3226C0 and before - * RF_VT3226D0: RobertYu:20051228 - * RF_VT3342A0: RobertYu:20060609 - * - */ - -#include -#include "mac.h" -#include "rf.h" -#include "baseband.h" -#include "usbpipe.h" - -#define CB_AL2230_INIT_SEQ 15 -#define CB_AL7230_INIT_SEQ 16 -#define CB_VT3226_INIT_SEQ 11 -#define CB_VT3342_INIT_SEQ 13 - -static u8 al2230_init_table[CB_AL2230_INIT_SEQ][3] = { - {0x03, 0xf7, 0x90}, - {0x03, 0x33, 0x31}, - {0x01, 0xb8, 0x02}, - {0x00, 0xff, 0xf3}, - {0x00, 0x05, 0xa4}, - {0x0f, 0x4d, 0xc5}, - {0x08, 0x05, 0xb6}, - {0x01, 0x47, 0xc7}, - {0x00, 0x06, 0x88}, - {0x04, 0x03, 0xb9}, - {0x00, 0xdb, 0xba}, - {0x00, 0x09, 0x9b}, - {0x0b, 0xdf, 0xfc}, - {0x00, 0x00, 0x0d}, - {0x00, 0x58, 0x0f} -}; - -static u8 al2230_channel_table0[CB_MAX_CHANNEL_24G][3] = { - {0x03, 0xf7, 0x90}, - {0x03, 0xf7, 0x90}, - {0x03, 0xe7, 0x90}, - {0x03, 0xe7, 0x90}, - {0x03, 0xf7, 0xa0}, - {0x03, 0xf7, 0xa0}, - {0x03, 0xe7, 0xa0}, - {0x03, 0xe7, 0xa0}, - {0x03, 0xf7, 0xb0}, - {0x03, 0xf7, 0xb0}, - {0x03, 0xe7, 0xb0}, - {0x03, 0xe7, 0xb0}, - {0x03, 0xf7, 0xc0}, - {0x03, 0xe7, 0xc0} -}; - -static u8 al2230_channel_table1[CB_MAX_CHANNEL_24G][3] = { - {0x03, 0x33, 0x31}, - {0x0b, 0x33, 0x31}, - {0x03, 0x33, 0x31}, - {0x0b, 0x33, 0x31}, - {0x03, 0x33, 0x31}, - {0x0b, 0x33, 0x31}, - {0x03, 0x33, 0x31}, - {0x0b, 0x33, 0x31}, - {0x03, 0x33, 0x31}, - {0x0b, 0x33, 0x31}, - {0x03, 0x33, 0x31}, - {0x0b, 0x33, 0x31}, - {0x03, 0x33, 0x31}, - {0x06, 0x66, 0x61} -}; - -static u8 vt3226_init_table[CB_VT3226_INIT_SEQ][3] = { - {0x03, 0xff, 0x80}, - {0x02, 0x82, 0xa1}, - {0x03, 0xc6, 0xa2}, - {0x01, 0x97, 0x93}, - {0x03, 0x66, 0x64}, - {0x00, 0x61, 0xa5}, - {0x01, 0x7b, 0xd6}, - {0x00, 0x80, 0x17}, - {0x03, 0xf8, 0x08}, - {0x00, 0x02, 0x39}, - {0x02, 0x00, 0x2a} -}; - -static u8 vt3226d0_init_table[CB_VT3226_INIT_SEQ][3] = { - {0x03, 0xff, 0x80}, - {0x03, 0x02, 0x21}, - {0x03, 0xc6, 0xa2}, - {0x01, 0x97, 0x93}, - {0x03, 0x66, 0x64}, - {0x00, 0x71, 0xa5}, - {0x01, 0x15, 0xc6}, - {0x01, 0x2e, 0x07}, - {0x00, 0x58, 0x08}, - {0x00, 0x02, 0x79}, - {0x02, 0x01, 0xaa} -}; - -static u8 vt3226_channel_table0[CB_MAX_CHANNEL_24G][3] = { - {0x01, 0x97, 0x83}, - {0x01, 0x97, 0x83}, - {0x01, 0x97, 0x93}, - {0x01, 0x97, 0x93}, - {0x01, 0x97, 0x93}, - {0x01, 0x97, 0x93}, - {0x01, 0x97, 0xa3}, - {0x01, 0x97, 0xa3}, - {0x01, 0x97, 0xa3}, - {0x01, 0x97, 0xa3}, - {0x01, 0x97, 0xb3}, - {0x01, 0x97, 0xb3}, - {0x01, 0x97, 0xb3}, - {0x03, 0x37, 0xc3} -}; - -static u8 vt3226_channel_table1[CB_MAX_CHANNEL_24G][3] = { - {0x02, 0x66, 0x64}, - {0x03, 0x66, 0x64}, - {0x00, 0x66, 0x64}, - {0x01, 0x66, 0x64}, - {0x02, 0x66, 0x64}, - {0x03, 0x66, 0x64}, - {0x00, 0x66, 0x64}, - {0x01, 0x66, 0x64}, - {0x02, 0x66, 0x64}, - {0x03, 0x66, 0x64}, - {0x00, 0x66, 0x64}, - {0x01, 0x66, 0x64}, - {0x02, 0x66, 0x64}, - {0x00, 0xcc, 0xc4} -}; - -static const u32 vt3226d0_lo_current_table[CB_MAX_CHANNEL_24G] = { - 0x0135c600, - 0x0135c600, - 0x0235c600, - 0x0235c600, - 0x0235c600, - 0x0335c600, - 0x0335c600, - 0x0335c600, - 0x0335c600, - 0x0335c600, - 0x0335c600, - 0x0335c600, - 0x0335c600, - 0x0135c600 -}; - -enum { - VNT_TABLE_INIT = 0, - VNT_TABLE_INIT_2 = 0, - VNT_TABLE_0 = 1, - VNT_TABLE_1 = 2, - VNT_TABLE_2 = 1 -}; - -struct vnt_table_info { - u8 *addr; - int length; -}; - -static const struct vnt_table_info vnt_table_seq[][3] = { - { /* RF_AL2230, RF_AL2230S init table, channel table 0 and 1 */ - {&al2230_init_table[0][0], CB_AL2230_INIT_SEQ * 3}, - {&al2230_channel_table0[0][0], CB_MAX_CHANNEL_24G * 3}, - {&al2230_channel_table1[0][0], CB_MAX_CHANNEL_24G * 3} - }, { /* RF_VT3226 init table, channel table 0 and 1 */ - {&vt3226_init_table[0][0], CB_VT3226_INIT_SEQ * 3}, - {&vt3226_channel_table0[0][0], CB_MAX_CHANNEL_24G * 3}, - {&vt3226_channel_table1[0][0], CB_MAX_CHANNEL_24G * 3} - }, { /* RF_VT3226D0 init table, channel table 0 and 1 */ - {&vt3226d0_init_table[0][0], CB_VT3226_INIT_SEQ * 3}, - {&vt3226_channel_table0[0][0], CB_MAX_CHANNEL_24G * 3}, - {&vt3226_channel_table1[0][0], CB_MAX_CHANNEL_24G * 3} - } -}; - -/* - * Description: Write to IF/RF, by embedded programming - */ -int vnt_rf_write_embedded(struct vnt_private *priv, u32 data) -{ - u8 reg_data[4]; - - data |= (VNT_RF_REG_LEN << 3) | IFREGCTL_REGW; - - reg_data[0] = (u8)data; - reg_data[1] = (u8)(data >> 8); - reg_data[2] = (u8)(data >> 16); - reg_data[3] = (u8)(data >> 24); - - return vnt_control_out(priv, MESSAGE_TYPE_WRITE_IFRF, 0, 0, - ARRAY_SIZE(reg_data), reg_data); -} - -static u8 vnt_rf_addpower(struct vnt_private *priv) -{ - int base; - s32 rssi = -priv->current_rssi; - - if (!rssi) - return 7; - - if (priv->rf_type == RF_VT3226D0) - base = -60; - else - base = -70; - - if (rssi < base) - return ((rssi - base + 1) / -5) * 2 + 5; - - return 0; -} - -/* Set Tx power by power level and rate */ -static int vnt_rf_set_txpower(struct vnt_private *priv, u8 power, - struct ieee80211_channel *ch) -{ - u32 power_setting = 0; - int ret = 0; - - power += vnt_rf_addpower(priv); - if (power > VNT_RF_MAX_POWER) - power = VNT_RF_MAX_POWER; - - if (priv->power == power) - return 0; - - priv->power = power; - - switch (priv->rf_type) { - case RF_AL2230: - power_setting = 0x0404090 | (power << 12); - - ret = vnt_rf_write_embedded(priv, power_setting); - if (ret) - return ret; - - if (ch->flags & IEEE80211_CHAN_NO_OFDM) - ret = vnt_rf_write_embedded(priv, 0x0001b400); - else - ret = vnt_rf_write_embedded(priv, 0x0005a400); - - break; - case RF_AL2230S: - power_setting = 0x0404090 | (power << 12); - - ret = vnt_rf_write_embedded(priv, power_setting); - if (ret) - return ret; - - if (ch->flags & IEEE80211_CHAN_NO_OFDM) { - ret = vnt_rf_write_embedded(priv, 0x040c1400); - if (ret) - return ret; - - ret = vnt_rf_write_embedded(priv, 0x00299b00); - } else { - ret = vnt_rf_write_embedded(priv, 0x0005a400); - if (ret) - return ret; - - ret = vnt_rf_write_embedded(priv, 0x00099b00); - } - - break; - - case RF_VT3226: - power_setting = ((0x3f - power) << 20) | (0x17 << 8); - - ret = vnt_rf_write_embedded(priv, power_setting); - break; - case RF_VT3226D0: - if (ch->flags & IEEE80211_CHAN_NO_OFDM) { - u16 hw_value = ch->hw_value; - - power_setting = ((0x3f - power) << 20) | (0xe07 << 8); - - ret = vnt_rf_write_embedded(priv, power_setting); - if (ret) - return ret; - - ret = vnt_rf_write_embedded(priv, 0x03c6a200); - if (ret) - return ret; - - dev_dbg(&priv->usb->dev, - "%s 11b channel [%d]\n", __func__, hw_value); - - hw_value--; - - if (hw_value < ARRAY_SIZE(vt3226d0_lo_current_table)) { - ret = vnt_rf_write_embedded(priv, - vt3226d0_lo_current_table[hw_value]); - if (ret) - return ret; - } - - ret = vnt_rf_write_embedded(priv, 0x015C0800); - } else { - dev_dbg(&priv->usb->dev, - "@@@@ %s> 11G mode\n", __func__); - - power_setting = ((0x3f - power) << 20) | (0x7 << 8); - - ret = vnt_rf_write_embedded(priv, power_setting); - if (ret) - return ret; - - ret = vnt_rf_write_embedded(priv, 0x00C6A200); - if (ret) - return ret; - - ret = vnt_rf_write_embedded(priv, 0x016BC600); - if (ret) - return ret; - - ret = vnt_rf_write_embedded(priv, 0x00900800); - } - - break; - - default: - break; - } - return ret; -} - -/* Set Tx power by channel number type */ -int vnt_rf_setpower(struct vnt_private *priv, - struct ieee80211_channel *ch) -{ - u16 channel; - u8 power = priv->cck_pwr; - - if (!ch) - return -EINVAL; - - /* set channel number to array number */ - channel = ch->hw_value - 1; - - if (ch->flags & IEEE80211_CHAN_NO_OFDM) { - if (channel < ARRAY_SIZE(priv->cck_pwr_tbl)) - power = priv->cck_pwr_tbl[channel]; - } else if (ch->band == NL80211_BAND_5GHZ) { - /* remove 14 channels to array size */ - channel -= 14; - - if (channel < ARRAY_SIZE(priv->ofdm_a_pwr_tbl)) - power = priv->ofdm_a_pwr_tbl[channel]; - } else { - if (channel < ARRAY_SIZE(priv->ofdm_pwr_tbl)) - power = priv->ofdm_pwr_tbl[channel]; - } - - return vnt_rf_set_txpower(priv, power, ch); -} - -/* Convert rssi to dbm */ -void vnt_rf_rssi_to_dbm(struct vnt_private *priv, u8 rssi, long *dbm) -{ - u8 idx = ((rssi & 0xc0) >> 6) & 0x03; - long b = rssi & 0x3f; - long a = 0; - u8 airoharf[4] = {0, 18, 0, 40}; - - switch (priv->rf_type) { - case RF_AL2230: - case RF_AL2230S: - case RF_VT3226: - case RF_VT3226D0: - a = airoharf[idx]; - break; - default: - break; - } - - *dbm = -1 * (a + b * 2); -} - -int vnt_rf_table_download(struct vnt_private *priv) -{ - int ret; - int idx = -1; - const struct vnt_table_info *table_seq; - - switch (priv->rf_type) { - case RF_AL2230: - case RF_AL2230S: - idx = 0; - break; - case RF_VT3226: - idx = 1; - break; - case RF_VT3226D0: - idx = 2; - break; - } - - if (idx < 0) - return 0; - - table_seq = &vnt_table_seq[idx][0]; - - /* Init Table */ - ret = vnt_control_out(priv, MESSAGE_TYPE_WRITE, 0, - MESSAGE_REQUEST_RF_INIT, - table_seq[VNT_TABLE_INIT].length, - table_seq[VNT_TABLE_INIT].addr); - if (ret) - return ret; - - /* Channel Table 0 */ - ret = vnt_control_out_blocks(priv, VNT_REG_BLOCK_SIZE, - MESSAGE_REQUEST_RF_CH0, - table_seq[VNT_TABLE_0].length, - table_seq[VNT_TABLE_0].addr); - if (ret) - return ret; - - /* Channel Table 1 */ - ret = vnt_control_out_blocks(priv, VNT_REG_BLOCK_SIZE, - MESSAGE_REQUEST_RF_CH1, - table_seq[VNT_TABLE_1].length, - table_seq[VNT_TABLE_1].addr); - - return ret; -} diff --git a/drivers/staging/vt6656/rf.h b/drivers/staging/vt6656/rf.h deleted file mode 100644 index b47e149875d1d..0000000000000 --- a/drivers/staging/vt6656/rf.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: - * - * Author: Jerry Chen - * - * Date: Feb. 19, 2004 - * - */ - -#ifndef __RF_H__ -#define __RF_H__ - -#include "device.h" - -/* Baseband RF pair definition in eeprom (Bits 6..0) */ -#define RF_RFMD2959 0x01 -#define RF_MAXIMAG 0x02 -#define RF_AL2230 0x03 -#define RF_GCT5103 0x04 -#define RF_UW2451 0x05 -#define RF_MAXIMG 0x06 -#define RF_MAXIM2829 0x07 -#define RF_UW2452 0x08 -#define RF_VT3226 0x09 -#define RF_AIROHA7230 0x0a -#define RF_UW2453 0x0b -#define RF_VT3226D0 0x0c /* RobertYu:20051114 */ -#define RF_VT3342A0 0x0d /* RobertYu:20060609 */ -#define RF_AL2230S 0x0e - -#define RF_EMU 0x80 -#define RF_MASK 0x7F - -#define VNT_RF_MAX_POWER 0x3f -#define VNT_RF_REG_LEN 0x17 /* 24 bit length */ - -int vnt_rf_write_embedded(struct vnt_private *priv, u32 data); -int vnt_rf_setpower(struct vnt_private *priv, struct ieee80211_channel *ch); -void vnt_rf_rssi_to_dbm(struct vnt_private *priv, u8 rssi, long *dbm); -int vnt_rf_table_download(struct vnt_private *priv); - -#endif /* __RF_H__ */ diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c deleted file mode 100644 index cd99091c6c28f..0000000000000 --- a/drivers/staging/vt6656/rxtx.c +++ /dev/null @@ -1,730 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: handle WMAC/802.3/802.11 rx & tx functions - * - * Author: Lyndon Chen - * - * Date: May 20, 2003 - * - * Functions: - * vnt_generate_tx_parameter - Generate tx dma required parameter. - * vnt_get_rsvtime- get frame reserved time - * vnt_fill_cts_head- fulfill CTS ctl header - * - * Revision History: - * - */ - -#include -#include "device.h" -#include "rxtx.h" -#include "card.h" -#include "mac.h" -#include "rf.h" -#include "usbpipe.h" - -static const u16 vnt_time_stampoff[2][MAX_RATE] = { - /* Long Preamble */ - {384, 288, 226, 209, 54, 43, 37, 31, 28, 25, 24, 23}, - - /* Short Preamble */ - {384, 192, 130, 113, 54, 43, 37, 31, 28, 25, 24, 23}, -}; - -#define DATADUR_B 10 -#define DATADUR_A 11 - -static const u8 vnt_phy_signal[] = { - 0x00, /* RATE_1M */ - 0x01, /* RATE_2M */ - 0x02, /* RATE_5M */ - 0x03, /* RATE_11M */ - 0x8b, /* RATE_6M */ - 0x8f, /* RATE_9M */ - 0x8a, /* RATE_12M */ - 0x8e, /* RATE_18M */ - 0x89, /* RATE_24M */ - 0x8d, /* RATE_36M */ - 0x88, /* RATE_48M */ - 0x8c /* RATE_54M */ -}; - -static struct vnt_usb_send_context - *vnt_get_free_context(struct vnt_private *priv) -{ - struct vnt_usb_send_context *context = NULL; - int ii; - - for (ii = 0; ii < priv->num_tx_context; ii++) { - if (!priv->tx_context[ii]) - return NULL; - - context = priv->tx_context[ii]; - if (!context->in_use) { - context->in_use = true; - return context; - } - } - - if (ii == priv->num_tx_context) { - dev_dbg(&priv->usb->dev, "%s No Free Tx Context\n", __func__); - - ieee80211_stop_queues(priv->hw); - } - - return NULL; -} - -/* Get Length, Service, and Signal fields of Phy for Tx */ -static void vnt_get_phy_field(struct vnt_private *priv, u32 frame_length, - u16 tx_rate, u8 pkt_type, - struct vnt_phy_field *phy) -{ - u32 bit_count; - u32 count = 0; - u32 tmp; - int ext_bit; - int i; - u8 mask = 0; - u8 preamble_type = priv->preamble_type; - - bit_count = frame_length * 8; - ext_bit = false; - - switch (tx_rate) { - case RATE_1M: - count = bit_count; - break; - case RATE_2M: - count = bit_count / 2; - break; - case RATE_5M: - count = DIV_ROUND_UP(bit_count * 10, 55); - break; - case RATE_11M: - count = bit_count / 11; - tmp = count * 11; - - if (tmp != bit_count) { - count++; - - if ((bit_count - tmp) <= 3) - ext_bit = true; - } - - break; - } - - if (tx_rate > RATE_11M) { - if (pkt_type == PK_TYPE_11A) - mask = BIT(4); - } else if (tx_rate > RATE_1M) { - if (preamble_type == PREAMBLE_SHORT) - mask = BIT(3); - } - - i = tx_rate > RATE_54M ? RATE_54M : tx_rate; - phy->signal = vnt_phy_signal[i] | mask; - phy->service = 0x00; - - if (pkt_type == PK_TYPE_11B) { - if (ext_bit) - phy->service |= 0x80; - phy->len = cpu_to_le16((u16)count); - } else { - phy->len = cpu_to_le16((u16)frame_length); - } -} - -static __le16 vnt_time_stamp_off(struct vnt_private *priv, u16 rate) -{ - return cpu_to_le16(vnt_time_stampoff[priv->preamble_type % 2] - [rate % MAX_RATE]); -} - -static __le16 vnt_rxtx_rsvtime_le16(struct vnt_usb_send_context *context) -{ - struct vnt_private *priv = context->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(context->skb); - struct ieee80211_rate *rate = ieee80211_get_tx_rate(priv->hw, info); - - return ieee80211_generic_frame_duration(priv->hw, - info->control.vif, info->band, - context->frame_len, - rate); -} - -static __le16 vnt_get_rts_duration(struct vnt_usb_send_context *context) -{ - struct vnt_private *priv = context->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(context->skb); - - return ieee80211_rts_duration(priv->hw, priv->vif, - context->frame_len, info); -} - -static __le16 vnt_get_cts_duration(struct vnt_usb_send_context *context) -{ - struct vnt_private *priv = context->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(context->skb); - - return ieee80211_ctstoself_duration(priv->hw, priv->vif, - context->frame_len, info); -} - -static void vnt_rxtx_datahead_g(struct vnt_usb_send_context *tx_context, - struct vnt_tx_datahead_g *buf) -{ - struct vnt_private *priv = tx_context->priv; - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)tx_context->skb->data; - u32 frame_len = tx_context->frame_len; - u16 rate = tx_context->tx_rate; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_len, rate, tx_context->pkt_type, &buf->a); - vnt_get_phy_field(priv, frame_len, priv->top_cck_basic_rate, - PK_TYPE_11B, &buf->b); - - /* Get Duration and TimeStamp */ - buf->duration_a = hdr->duration_id; - buf->duration_b = hdr->duration_id; - buf->time_stamp_off_a = vnt_time_stamp_off(priv, rate); - buf->time_stamp_off_b = vnt_time_stamp_off(priv, - priv->top_cck_basic_rate); -} - -static void vnt_rxtx_datahead_ab(struct vnt_usb_send_context *tx_context, - struct vnt_tx_datahead_ab *buf) -{ - struct vnt_private *priv = tx_context->priv; - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)tx_context->skb->data; - u32 frame_len = tx_context->frame_len; - u16 rate = tx_context->tx_rate; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_len, rate, - tx_context->pkt_type, &buf->ab); - - /* Get Duration and TimeStampOff */ - buf->duration = hdr->duration_id; - buf->time_stamp_off = vnt_time_stamp_off(priv, rate); -} - -static void vnt_fill_ieee80211_rts(struct vnt_usb_send_context *tx_context, - struct ieee80211_rts *rts, __le16 duration) -{ - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)tx_context->skb->data; - - rts->duration = duration; - rts->frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); - - ether_addr_copy(rts->ra, hdr->addr1); - ether_addr_copy(rts->ta, hdr->addr2); -} - -static void vnt_rxtx_rts_g_head(struct vnt_usb_send_context *tx_context, - struct vnt_rts_g *buf) -{ - struct vnt_private *priv = tx_context->priv; - u16 rts_frame_len = 20; - - vnt_get_phy_field(priv, rts_frame_len, priv->top_cck_basic_rate, - PK_TYPE_11B, &buf->b); - vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate, - tx_context->pkt_type, &buf->a); - - buf->duration_bb = vnt_get_rts_duration(tx_context); - buf->duration_aa = buf->duration_bb; - buf->duration_ba = buf->duration_bb; - - vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration_aa); - - vnt_rxtx_datahead_g(tx_context, &buf->data_head); -} - -static void vnt_rxtx_rts_ab_head(struct vnt_usb_send_context *tx_context, - struct vnt_rts_ab *buf) -{ - struct vnt_private *priv = tx_context->priv; - u16 rts_frame_len = 20; - - vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate, - tx_context->pkt_type, &buf->ab); - - buf->duration = vnt_get_rts_duration(tx_context); - - vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration); - - vnt_rxtx_datahead_ab(tx_context, &buf->data_head); -} - -static void vnt_fill_cts_head(struct vnt_usb_send_context *tx_context, - union vnt_tx_data_head *head) -{ - struct vnt_private *priv = tx_context->priv; - struct vnt_cts *buf = &head->cts_g; - u32 cts_frame_len = 14; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, cts_frame_len, priv->top_cck_basic_rate, - PK_TYPE_11B, &buf->b); - /* Get CTSDuration_ba */ - buf->duration_ba = vnt_get_cts_duration(tx_context); - /*Get CTS Frame body*/ - buf->data.duration = buf->duration_ba; - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); - - ether_addr_copy(buf->data.ra, priv->current_net_addr); - - vnt_rxtx_datahead_g(tx_context, &buf->data_head); -} - -/* returns true if mic_hdr is needed */ -static bool vnt_fill_txkey(struct vnt_tx_buffer *tx_buffer, struct sk_buff *skb) -{ - struct vnt_tx_fifo_head *fifo = &tx_buffer->fifo_head; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_key_conf *tx_key = info->control.hw_key; - struct vnt_mic_hdr *mic_hdr; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u64 pn64; - u16 payload_len = skb->len; - u8 *iv = ((u8 *)hdr + ieee80211_get_hdrlen_from_skb(skb)); - - /* strip header and icv len from payload */ - payload_len -= ieee80211_get_hdrlen_from_skb(skb); - payload_len -= tx_key->icv_len; - - switch (tx_key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - memcpy(fifo->tx_key, iv, 3); - memcpy(fifo->tx_key + 3, tx_key->key, tx_key->keylen); - - if (tx_key->keylen == WLAN_KEY_LEN_WEP40) { - memcpy(fifo->tx_key + 8, iv, 3); - memcpy(fifo->tx_key + 11, - tx_key->key, WLAN_KEY_LEN_WEP40); - } - - fifo->frag_ctl |= cpu_to_le16(FRAGCTL_LEGACY); - break; - case WLAN_CIPHER_SUITE_TKIP: - ieee80211_get_tkip_p2k(tx_key, skb, fifo->tx_key); - - fifo->frag_ctl |= cpu_to_le16(FRAGCTL_TKIP); - break; - case WLAN_CIPHER_SUITE_CCMP: - if (info->control.use_cts_prot) { - if (info->control.use_rts) - mic_hdr = &tx_buffer->tx_head.tx_rts.tx.mic.hdr; - else - mic_hdr = &tx_buffer->tx_head.tx_cts.tx.mic.hdr; - } else { - mic_hdr = &tx_buffer->tx_head.tx_ab.tx.mic.hdr; - } - - mic_hdr->id = 0x59; - mic_hdr->payload_len = cpu_to_be16(payload_len); - ether_addr_copy(mic_hdr->mic_addr2, hdr->addr2); - - pn64 = atomic64_read(&tx_key->tx_pn); - mic_hdr->ccmp_pn[5] = pn64; - mic_hdr->ccmp_pn[4] = pn64 >> 8; - mic_hdr->ccmp_pn[3] = pn64 >> 16; - mic_hdr->ccmp_pn[2] = pn64 >> 24; - mic_hdr->ccmp_pn[1] = pn64 >> 32; - mic_hdr->ccmp_pn[0] = pn64 >> 40; - - if (ieee80211_has_a4(hdr->frame_control)) - mic_hdr->hlen = cpu_to_be16(28); - else - mic_hdr->hlen = cpu_to_be16(22); - - ether_addr_copy(mic_hdr->addr1, hdr->addr1); - ether_addr_copy(mic_hdr->addr2, hdr->addr2); - ether_addr_copy(mic_hdr->addr3, hdr->addr3); - - mic_hdr->frame_control = cpu_to_le16(le16_to_cpu(hdr->frame_control) & 0xc78f); - mic_hdr->seq_ctrl = cpu_to_le16(le16_to_cpu(hdr->seq_ctrl) & 0xf); - - if (ieee80211_has_a4(hdr->frame_control)) - ether_addr_copy(mic_hdr->addr4, hdr->addr4); - - memcpy(fifo->tx_key, tx_key->key, WLAN_KEY_LEN_CCMP); - - fifo->frag_ctl |= cpu_to_le16(FRAGCTL_AES); - return true; - default: - break; - } - - return false; -} - -static void vnt_rxtx_rts(struct vnt_usb_send_context *tx_context) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_context->skb); - struct vnt_tx_buffer *tx_buffer = tx_context->tx_buffer; - union vnt_tx_head *tx_head = &tx_buffer->tx_head; - struct vnt_rrv_time_rts *buf = &tx_head->tx_rts.rts; - union vnt_tx_data_head *head = &tx_head->tx_rts.tx.head; - - buf->rts_rrv_time_aa = vnt_get_rts_duration(tx_context); - buf->rts_rrv_time_ba = buf->rts_rrv_time_aa; - buf->rts_rrv_time_bb = buf->rts_rrv_time_aa; - - buf->rrv_time_a = vnt_rxtx_rsvtime_le16(tx_context); - buf->rrv_time_b = buf->rrv_time_a; - - if (info->control.hw_key) { - if (vnt_fill_txkey(tx_buffer, tx_context->skb)) - head = &tx_head->tx_rts.tx.mic.head; - } - - vnt_rxtx_rts_g_head(tx_context, &head->rts_g); -} - -static void vnt_rxtx_cts(struct vnt_usb_send_context *tx_context) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_context->skb); - struct vnt_tx_buffer *tx_buffer = tx_context->tx_buffer; - union vnt_tx_head *tx_head = &tx_buffer->tx_head; - struct vnt_rrv_time_cts *buf = &tx_head->tx_cts.cts; - union vnt_tx_data_head *head = &tx_head->tx_cts.tx.head; - - buf->rrv_time_a = vnt_rxtx_rsvtime_le16(tx_context); - buf->rrv_time_b = buf->rrv_time_a; - - buf->cts_rrv_time_ba = vnt_get_cts_duration(tx_context); - - if (info->control.hw_key) { - if (vnt_fill_txkey(tx_buffer, tx_context->skb)) - head = &tx_head->tx_cts.tx.mic.head; - } - - vnt_fill_cts_head(tx_context, head); -} - -static void vnt_rxtx_ab(struct vnt_usb_send_context *tx_context) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_context->skb); - struct vnt_tx_buffer *tx_buffer = tx_context->tx_buffer; - union vnt_tx_head *tx_head = &tx_buffer->tx_head; - struct vnt_rrv_time_ab *buf = &tx_head->tx_ab.ab; - union vnt_tx_data_head *head = &tx_head->tx_ab.tx.head; - - buf->rrv_time = vnt_rxtx_rsvtime_le16(tx_context); - - if (info->control.hw_key) { - if (vnt_fill_txkey(tx_buffer, tx_context->skb)) - head = &tx_head->tx_ab.tx.mic.head; - } - - if (info->control.use_rts) { - buf->rts_rrv_time = vnt_get_rts_duration(tx_context); - - vnt_rxtx_rts_ab_head(tx_context, &head->rts_ab); - - return; - } - - vnt_rxtx_datahead_ab(tx_context, &head->data_head_ab); -} - -static void vnt_generate_tx_parameter(struct vnt_usb_send_context *tx_context) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_context->skb); - - if (info->control.use_cts_prot) { - if (info->control.use_rts) { - vnt_rxtx_rts(tx_context); - - return; - } - - vnt_rxtx_cts(tx_context); - - return; - } - - vnt_rxtx_ab(tx_context); -} - -static u16 vnt_get_hdr_size(struct ieee80211_tx_info *info) -{ - u16 size = sizeof(struct vnt_tx_datahead_ab); - - if (info->control.use_cts_prot) { - if (info->control.use_rts) - size = sizeof(struct vnt_rts_g); - else - size = sizeof(struct vnt_cts); - } else if (info->control.use_rts) { - size = sizeof(struct vnt_rts_ab); - } - - if (info->control.hw_key) { - if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP) - size += sizeof(struct vnt_mic_hdr); - } - - /* Get rrv_time header */ - if (info->control.use_cts_prot) { - if (info->control.use_rts) - size += sizeof(struct vnt_rrv_time_rts); - else - size += sizeof(struct vnt_rrv_time_cts); - } else { - size += sizeof(struct vnt_rrv_time_ab); - } - - size += sizeof(struct vnt_tx_fifo_head); - - return size; -} - -int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *tx_rate = &info->control.rates[0]; - struct ieee80211_rate *rate; - struct ieee80211_hdr *hdr; - struct vnt_tx_buffer *tx_buffer; - struct vnt_tx_fifo_head *tx_buffer_head; - struct vnt_usb_send_context *tx_context; - unsigned long flags; - u8 pkt_type; - - hdr = (struct ieee80211_hdr *)(skb->data); - - rate = ieee80211_get_tx_rate(priv->hw, info); - - if (rate->hw_value > RATE_11M) { - if (info->band == NL80211_BAND_5GHZ) { - pkt_type = PK_TYPE_11A; - } else { - if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - if (priv->basic_rates & VNT_B_RATES) - pkt_type = PK_TYPE_11GB; - else - pkt_type = PK_TYPE_11GA; - } else { - pkt_type = PK_TYPE_11A; - } - } - } else { - pkt_type = PK_TYPE_11B; - } - - spin_lock_irqsave(&priv->lock, flags); - - tx_context = vnt_get_free_context(priv); - if (!tx_context) { - dev_dbg(&priv->usb->dev, "%s No free context\n", __func__); - spin_unlock_irqrestore(&priv->lock, flags); - return -ENOMEM; - } - - tx_context->pkt_type = pkt_type; - tx_context->frame_len = skb->len + 4; - tx_context->tx_rate = rate->hw_value; - - spin_unlock_irqrestore(&priv->lock, flags); - - tx_context->skb = skb_clone(skb, GFP_ATOMIC); - if (!tx_context->skb) { - tx_context->in_use = false; - return -ENOMEM; - } - - tx_buffer = skb_push(skb, vnt_get_hdr_size(info)); - tx_context->tx_buffer = tx_buffer; - tx_buffer_head = &tx_buffer->fifo_head; - - tx_context->type = CONTEXT_DATA_PACKET; - - /*Set fifo controls */ - if (pkt_type == PK_TYPE_11A) - tx_buffer_head->fifo_ctl = 0; - else if (pkt_type == PK_TYPE_11B) - tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11B); - else if (pkt_type == PK_TYPE_11GB) - tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GB); - else if (pkt_type == PK_TYPE_11GA) - tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GA); - - if (!ieee80211_is_data(hdr->frame_control)) { - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_GENINT | - FIFOCTL_ISDMA0); - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_TMOEN); - - tx_buffer_head->time_stamp = - cpu_to_le16(DEFAULT_MGN_LIFETIME_RES_64us); - } else { - tx_buffer_head->time_stamp = - cpu_to_le16(DEFAULT_MSDU_LIFETIME_RES_64us); - } - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_NEEDACK); - - if (ieee80211_has_retry(hdr->frame_control)) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LRETRY); - - if (info->control.use_rts) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_RTS); - - if (ieee80211_has_a4(hdr->frame_control)) - tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LHEAD); - - tx_buffer_head->frag_ctl = - cpu_to_le16(ieee80211_hdrlen(hdr->frame_control) << 10); - - if (info->control.hw_key) - tx_context->frame_len += info->control.hw_key->icv_len; - - tx_buffer_head->current_rate = cpu_to_le16(rate->hw_value); - - vnt_generate_tx_parameter(tx_context); - - tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_NONFRAG); - - priv->seq_counter = (le16_to_cpu(hdr->seq_ctrl) & - IEEE80211_SCTL_SEQ) >> 4; - - spin_lock_irqsave(&priv->lock, flags); - - if (vnt_tx_context(priv, tx_context, skb)) { - dev_kfree_skb(tx_context->skb); - spin_unlock_irqrestore(&priv->lock, flags); - return -EIO; - } - - dev_kfree_skb(skb); - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int vnt_beacon_xmit(struct vnt_private *priv, struct sk_buff *skb) -{ - struct vnt_tx_short_buf_head *short_head; - struct ieee80211_tx_info *info; - struct vnt_usb_send_context *context; - struct ieee80211_mgmt *mgmt_hdr; - unsigned long flags; - u32 frame_size = skb->len + 4; - u16 current_rate; - - spin_lock_irqsave(&priv->lock, flags); - - context = vnt_get_free_context(priv); - if (!context) { - dev_dbg(&priv->usb->dev, "%s No free context!\n", __func__); - spin_unlock_irqrestore(&priv->lock, flags); - return -ENOMEM; - } - - context->skb = skb; - - spin_unlock_irqrestore(&priv->lock, flags); - - mgmt_hdr = (struct ieee80211_mgmt *)skb->data; - short_head = skb_push(skb, sizeof(*short_head)); - - if (priv->bb_type == BB_TYPE_11A) { - current_rate = RATE_6M; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_size, current_rate, - PK_TYPE_11A, &short_head->ab); - - /* Get TimeStampOff */ - short_head->time_stamp_off = - vnt_time_stamp_off(priv, current_rate); - } else { - current_rate = RATE_1M; - short_head->fifo_ctl |= cpu_to_le16(FIFOCTL_11B); - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_size, current_rate, - PK_TYPE_11B, &short_head->ab); - - /* Get TimeStampOff */ - short_head->time_stamp_off = - vnt_time_stamp_off(priv, current_rate); - } - - /* Get Duration */ - short_head->duration = mgmt_hdr->duration; - - /* time stamp always 0 */ - mgmt_hdr->u.beacon.timestamp = 0; - - info = IEEE80211_SKB_CB(skb); - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mgmt_hdr; - - hdr->duration_id = 0; - hdr->seq_ctrl = cpu_to_le16(priv->seq_counter << 4); - } - - priv->seq_counter++; - if (priv->seq_counter > 0x0fff) - priv->seq_counter = 0; - - context->type = CONTEXT_BEACON_PACKET; - - spin_lock_irqsave(&priv->lock, flags); - - if (vnt_tx_context(priv, context, skb)) - ieee80211_free_txskb(priv->hw, context->skb); - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -int vnt_beacon_make(struct vnt_private *priv, struct ieee80211_vif *vif) -{ - struct sk_buff *beacon; - - beacon = ieee80211_beacon_get(priv->hw, vif, 0); - if (!beacon) - return -ENOMEM; - - if (vnt_beacon_xmit(priv, beacon)) { - ieee80211_free_txskb(priv->hw, beacon); - return -ENODEV; - } - - return 0; -} - -int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf) -{ - vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX); - - vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - - vnt_mac_set_beacon_interval(priv, conf->beacon_int); - - vnt_clear_current_tsf(priv); - - vnt_mac_reg_bits_on(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - - vnt_reset_next_tbtt(priv, conf->beacon_int); - - return vnt_beacon_make(priv, vif); -} diff --git a/drivers/staging/vt6656/rxtx.h b/drivers/staging/vt6656/rxtx.h deleted file mode 100644 index b9df0854b4b01..0000000000000 --- a/drivers/staging/vt6656/rxtx.h +++ /dev/null @@ -1,178 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: - * - * Author: Jerry Chen - * - * Date: Jun. 27, 2002 - * - */ - -#ifndef __RXTX_H__ -#define __RXTX_H__ - -#include "device.h" -#include "wcmd.h" -#include "baseband.h" - -#define DEFAULT_MGN_LIFETIME_RES_64us 125 /* 64us */ -#define DEFAULT_MSDU_LIFETIME_RES_64us 8000 - -/* Length, Service, and Signal fields of Phy for Tx */ -struct vnt_phy_field { - u8 signal; - u8 service; - __le16 len; -} __packed; - -/* MIC HDR data header */ -struct vnt_mic_hdr { - u8 id; - u8 tx_priority; - u8 mic_addr2[6]; - u8 ccmp_pn[IEEE80211_CCMP_PN_LEN]; - __be16 payload_len; - __be16 hlen; - __le16 frame_control; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; - __le16 seq_ctrl; - u8 addr4[6]; - u16 packing; /* packing to 48 bytes */ -} __packed; - -/* RsvTime buffer header */ -struct vnt_rrv_time_rts { - __le16 rts_rrv_time_ba; - __le16 rts_rrv_time_aa; - __le16 rts_rrv_time_bb; - u16 wReserved; - __le16 rrv_time_b; - __le16 rrv_time_a; -} __packed; - -struct vnt_rrv_time_cts { - __le16 cts_rrv_time_ba; - u16 wReserved; - __le16 rrv_time_b; - __le16 rrv_time_a; -} __packed; - -struct vnt_rrv_time_ab { - __le16 rts_rrv_time; - __le16 rrv_time; -} __packed; - -/* TX data header */ -struct vnt_tx_datahead_g { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_b; - __le16 duration_a; - __le16 time_stamp_off_b; - __le16 time_stamp_off_a; -} __packed; - -struct vnt_tx_datahead_ab { - struct vnt_phy_field ab; - __le16 duration; - __le16 time_stamp_off; -} __packed; - -/* RTS buffer header */ -struct vnt_rts_g { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_ba; - __le16 duration_aa; - __le16 duration_bb; - u16 wReserved; - struct ieee80211_rts data; - struct vnt_tx_datahead_g data_head; -} __packed __aligned(2); - -struct vnt_rts_ab { - struct vnt_phy_field ab; - __le16 duration; - u16 wReserved; - struct ieee80211_rts data; - struct vnt_tx_datahead_ab data_head; -} __packed __aligned(2); - -/* CTS buffer header */ -struct vnt_cts { - struct vnt_phy_field b; - __le16 duration_ba; - u16 wReserved; - struct ieee80211_cts data; - u16 reserved2; - struct vnt_tx_datahead_g data_head; -} __packed __aligned(2); - -union vnt_tx_data_head { - /* rts g */ - struct vnt_rts_g rts_g; - /* rts a/b */ - struct vnt_rts_ab rts_ab; - /* cts g */ - struct vnt_cts cts_g; - /* no rts/cts */ - struct vnt_tx_datahead_ab data_head_ab; -}; - -struct vnt_tx_mic_hdr { - struct vnt_mic_hdr hdr; - union vnt_tx_data_head head; -} __packed; - -union vnt_tx { - struct vnt_tx_mic_hdr mic; - union vnt_tx_data_head head; -}; - -union vnt_tx_head { - struct { - struct vnt_rrv_time_rts rts; - union vnt_tx tx; - } __packed tx_rts; - struct { - struct vnt_rrv_time_cts cts; - union vnt_tx tx; - } __packed tx_cts; - struct { - struct vnt_rrv_time_ab ab; - union vnt_tx tx; - } __packed tx_ab; -}; - -struct vnt_tx_fifo_head { - u8 tx_key[WLAN_KEY_LEN_CCMP]; - __le16 fifo_ctl; - __le16 time_stamp; - __le16 frag_ctl; - __le16 current_rate; -} __packed; - -struct vnt_tx_buffer { - struct vnt_tx_fifo_head fifo_head; - union vnt_tx_head tx_head; -} __packed; - -struct vnt_tx_short_buf_head { - __le16 fifo_ctl; - u16 time_stamp; - struct vnt_phy_field ab; - __le16 duration; - __le16 time_stamp_off; -} __packed; - -int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb); -int vnt_beacon_make(struct vnt_private *priv, struct ieee80211_vif *vif); -int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf); - -#endif /* __RXTX_H__ */ diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c deleted file mode 100644 index d505b4b69ba49..0000000000000 --- a/drivers/staging/vt6656/usbpipe.c +++ /dev/null @@ -1,506 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handle USB control endpoint - * - * Author: Warren Hsu - * - * Date: Mar. 29, 2005 - * - * Functions: - * vnt_control_out - Write variable length bytes to MEM/BB/MAC/EEPROM - * vnt_control_in - Read variable length bytes from MEM/BB/MAC/EEPROM - * vnt_control_out_u8 - Write one byte to MEM/BB/MAC/EEPROM - * vnt_control_in_u8 - Read one byte from MEM/BB/MAC/EEPROM - * - * Revision History: - * 04-05-2004 Jerry Chen: Initial release - * 11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte, - * ControlvMaskByte - * - */ - -#include "rxtx.h" -#include "desc.h" -#include "device.h" -#include "usbpipe.h" -#include "mac.h" -#include "rf.h" - -#define USB_CTL_WAIT 500 /* ms */ - -int vnt_control_out(struct vnt_private *priv, u8 request, u16 value, - u16 index, u16 length, const u8 *buffer) -{ - int ret = 0; - u8 *usb_buffer; - - if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) { - ret = -EINVAL; - goto end; - } - - mutex_lock(&priv->usb_lock); - - usb_buffer = kmemdup(buffer, length, GFP_KERNEL); - if (!usb_buffer) { - ret = -ENOMEM; - goto end_unlock; - } - - ret = usb_control_msg(priv->usb, - usb_sndctrlpipe(priv->usb, 0), - request, 0x40, value, - index, usb_buffer, length, USB_CTL_WAIT); - - kfree(usb_buffer); - - if (ret == (int)length) - ret = 0; - else - ret = -EIO; - -end_unlock: - mutex_unlock(&priv->usb_lock); -end: - return ret; -} - -int vnt_control_out_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 data) -{ - return vnt_control_out(priv, MESSAGE_TYPE_WRITE, - reg_off, reg, sizeof(u8), &data); -} - -int vnt_control_out_blocks(struct vnt_private *priv, - u16 block, u8 reg, u16 length, const u8 *data) -{ - int ret = 0, i; - - for (i = 0; i < length; i += block) { - u16 len = min_t(int, length - i, block); - - ret = vnt_control_out(priv, MESSAGE_TYPE_WRITE, - i, reg, len, data + i); - if (ret) - goto end; - } -end: - return ret; -} - -int vnt_control_in(struct vnt_private *priv, u8 request, u16 value, - u16 index, u16 length, u8 *buffer) -{ - int ret = 0; - u8 *usb_buffer; - - if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) { - ret = -EINVAL; - goto end; - } - - mutex_lock(&priv->usb_lock); - - usb_buffer = kmalloc(length, GFP_KERNEL); - if (!usb_buffer) { - ret = -ENOMEM; - goto end_unlock; - } - - ret = usb_control_msg(priv->usb, - usb_rcvctrlpipe(priv->usb, 0), - request, 0xc0, value, - index, usb_buffer, length, USB_CTL_WAIT); - - if (ret == length) - memcpy(buffer, usb_buffer, length); - - kfree(usb_buffer); - - if (ret == (int)length) - ret = 0; - else - ret = -EIO; - -end_unlock: - mutex_unlock(&priv->usb_lock); -end: - return ret; -} - -int vnt_control_in_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 *data) -{ - return vnt_control_in(priv, MESSAGE_TYPE_READ, - reg_off, reg, sizeof(u8), data); -} - -static int vnt_int_report_rate(struct vnt_private *priv, u8 pkt_no, u8 tsr) -{ - struct vnt_usb_send_context *context; - struct ieee80211_tx_info *info; - u8 tx_retry = (tsr & 0xf0) >> 4; - s8 idx; - - if (pkt_no >= priv->num_tx_context) - return -EINVAL; - - context = priv->tx_context[pkt_no]; - - if (!context->skb) - return -EINVAL; - - info = IEEE80211_SKB_CB(context->skb); - idx = info->control.rates[0].idx; - - ieee80211_tx_info_clear_status(info); - - info->status.rates[0].count = tx_retry; - - if (!(tsr & TSR_TMO)) { - info->status.rates[0].idx = idx; - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - info->flags |= IEEE80211_TX_STAT_ACK; - } - - ieee80211_tx_status_irqsafe(priv->hw, context->skb); - - context->in_use = false; - - return 0; -} - -static void vnt_int_process_data(struct vnt_private *priv) -{ - struct vnt_interrupt_data *int_data; - struct ieee80211_low_level_stats *low_stats = &priv->low_stats; - - dev_dbg(&priv->usb->dev, "---->s_nsInterruptProcessData\n"); - - int_data = (struct vnt_interrupt_data *)priv->int_buf.data_buf; - - if (int_data->tsr0 & TSR_VALID) - vnt_int_report_rate(priv, int_data->pkt0, int_data->tsr0); - - if (int_data->tsr1 & TSR_VALID) - vnt_int_report_rate(priv, int_data->pkt1, int_data->tsr1); - - if (int_data->tsr2 & TSR_VALID) - vnt_int_report_rate(priv, int_data->pkt2, int_data->tsr2); - - if (int_data->tsr3 & TSR_VALID) - vnt_int_report_rate(priv, int_data->pkt3, int_data->tsr3); - - if (!int_data->isr0) - return; - - if (int_data->isr0 & ISR_BNTX && priv->op_mode == NL80211_IFTYPE_AP) - vnt_schedule_command(priv, WLAN_CMD_BECON_SEND); - - priv->current_tsf = le64_to_cpu(int_data->tsf); - - low_stats->dot11RTSSuccessCount += int_data->rts_success; - low_stats->dot11RTSFailureCount += int_data->rts_fail; - low_stats->dot11ACKFailureCount += int_data->ack_fail; - low_stats->dot11FCSErrorCount += int_data->fcs_err; -} - -static void vnt_start_interrupt_urb_complete(struct urb *urb) -{ - struct vnt_private *priv = urb->context; - int status = urb->status; - - switch (status) { - case 0: - case -ETIMEDOUT: - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - return; - default: - break; - } - - if (status) - dev_dbg(&priv->usb->dev, "%s status = %d\n", __func__, status); - else - vnt_int_process_data(priv); - - if (!test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) - status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC); - - if (status) - dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status); -} - -int vnt_start_interrupt_urb(struct vnt_private *priv) -{ - int ret = 0; - - dev_dbg(&priv->usb->dev, "---->Interrupt Polling Thread\n"); - - usb_fill_int_urb(priv->interrupt_urb, - priv->usb, - usb_rcvintpipe(priv->usb, 1), - priv->int_buf.data_buf, - MAX_INTERRUPT_SIZE, - vnt_start_interrupt_urb_complete, - priv, - priv->int_interval); - - ret = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC); - if (ret) - dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", ret); - - return ret; -} - -static int vnt_rx_data(struct vnt_private *priv, struct vnt_rcb *ptr_rcb, - unsigned long bytes_received) -{ - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_supported_band *sband; - struct sk_buff *skb; - struct ieee80211_rx_status *rx_status; - struct vnt_rx_header *head; - struct vnt_rx_tail *tail; - u32 frame_size; - int ii; - u16 rx_bitrate, pay_load_with_padding; - u8 rate_idx = 0; - long rx_dbm; - - skb = ptr_rcb->skb; - rx_status = IEEE80211_SKB_RXCB(skb); - - /* [31:16]RcvByteCount ( not include 4-byte Status ) */ - head = (struct vnt_rx_header *)skb->data; - frame_size = head->wbk_status >> 16; - frame_size += 4; - - if (bytes_received != frame_size) { - dev_dbg(&priv->usb->dev, "------- WRONG Length 1\n"); - return false; - } - - if ((bytes_received > 2372) || (bytes_received <= 40)) { - /* Frame Size error drop this packet.*/ - dev_dbg(&priv->usb->dev, "------ WRONG Length 2\n"); - return false; - } - - /* real Frame Size = USBframe_size -4WbkStatus - 4RxStatus */ - /* -8TSF - 4RSR - 4SQ3 - ?Padding */ - - /* if SQ3 the range is 24~27, if no SQ3 the range is 20~23 */ - - /*Fix hardware bug => PLCP_Length error */ - if (((bytes_received - head->pay_load_len) > 27) || - ((bytes_received - head->pay_load_len) < 24) || - (bytes_received < head->pay_load_len)) { - dev_dbg(&priv->usb->dev, "Wrong PLCP Length %x\n", - head->pay_load_len); - return false; - } - - sband = hw->wiphy->bands[hw->conf.chandef.chan->band]; - rx_bitrate = head->rx_rate * 5; /* rx_rate * 5 */ - - for (ii = 0; ii < sband->n_bitrates; ii++) { - if (sband->bitrates[ii].bitrate == rx_bitrate) { - rate_idx = ii; - break; - } - } - - if (ii == sband->n_bitrates) { - dev_dbg(&priv->usb->dev, "Wrong Rx Bit Rate %d\n", rx_bitrate); - return false; - } - - pay_load_with_padding = ((head->pay_load_len / 4) + - ((head->pay_load_len % 4) ? 1 : 0)) * 4; - - tail = (struct vnt_rx_tail *)(skb->data + - sizeof(*head) + pay_load_with_padding); - priv->tsf_time = le64_to_cpu(tail->tsf_time); - - if (tail->rsr & (RSR_IVLDTYP | RSR_IVLDLEN)) - return false; - - vnt_rf_rssi_to_dbm(priv, tail->rssi, &rx_dbm); - - priv->bb_pre_ed_rssi = (u8)-rx_dbm + 1; - priv->current_rssi = priv->bb_pre_ed_rssi; - - skb_pull(skb, sizeof(*head)); - skb_trim(skb, head->pay_load_len); - - rx_status->mactime = priv->tsf_time; - rx_status->band = hw->conf.chandef.chan->band; - rx_status->signal = rx_dbm; - rx_status->flag = 0; - rx_status->freq = hw->conf.chandef.chan->center_freq; - - if (!(tail->rsr & RSR_CRCOK)) - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - - rx_status->rate_idx = rate_idx; - - if (tail->new_rsr & NEWRSR_DECRYPTOK) - rx_status->flag |= RX_FLAG_DECRYPTED; - - ieee80211_rx_irqsafe(priv->hw, skb); - - return true; -} - -static void vnt_submit_rx_urb_complete(struct urb *urb) -{ - struct vnt_rcb *rcb = urb->context; - struct vnt_private *priv = rcb->priv; - - switch (urb->status) { - case 0: - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - return; - case -ETIMEDOUT: - default: - dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status); - break; - } - - if (urb->actual_length) { - if (vnt_rx_data(priv, rcb, urb->actual_length)) { - rcb->skb = dev_alloc_skb(priv->rx_buf_sz); - if (!rcb->skb) - return; - } else { - skb_push(rcb->skb, skb_headroom(rcb->skb)); - skb_trim(rcb->skb, 0); - } - - urb->transfer_buffer = skb_put(rcb->skb, - skb_tailroom(rcb->skb)); - } - - if (usb_submit_urb(urb, GFP_ATOMIC)) - dev_dbg(&priv->usb->dev, "Failed to re submit rx skb\n"); -} - -int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb) -{ - int ret = 0; - struct urb *urb = rcb->urb; - - if (!rcb->skb) { - dev_dbg(&priv->usb->dev, "rcb->skb is null\n"); - ret = -EINVAL; - goto end; - } - - usb_fill_bulk_urb(urb, - priv->usb, - usb_rcvbulkpipe(priv->usb, 2), - skb_put(rcb->skb, skb_tailroom(rcb->skb)), - MAX_TOTAL_SIZE_WITH_ALL_HEADERS, - vnt_submit_rx_urb_complete, - rcb); - - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - dev_dbg(&priv->usb->dev, "Submit Rx URB failed %d\n", ret); -end: - return ret; -} - -static void vnt_tx_context_complete(struct urb *urb) -{ - struct vnt_usb_send_context *context = urb->context; - struct vnt_private *priv = context->priv; - - switch (urb->status) { - case 0: - dev_dbg(&priv->usb->dev, - "Write %d bytes\n", urb->actual_length); - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - context->in_use = false; - return; - case -ETIMEDOUT: - default: - dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status); - break; - } - - if (context->type == CONTEXT_DATA_PACKET) - ieee80211_wake_queues(priv->hw); - - if (urb->status || context->type == CONTEXT_BEACON_PACKET) { - if (context->skb) - ieee80211_free_txskb(priv->hw, context->skb); - - context->in_use = false; - } -} - -int vnt_tx_context(struct vnt_private *priv, - struct vnt_usb_send_context *context, - struct sk_buff *skb) -{ - struct vnt_tx_usb_header *usb; - struct urb *urb; - int status; - u16 count = skb->len; - - usb = skb_push(skb, sizeof(*usb)); - usb->tx_byte_count = cpu_to_le16(count); - usb->pkt_no = context->pkt_no; - usb->type = context->type; - - if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) { - context->in_use = false; - return -ENODEV; - } - - if (skb->len > MAX_TOTAL_SIZE_WITH_ALL_HEADERS) { - context->in_use = false; - return -E2BIG; - } - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - context->in_use = false; - return -ENOMEM; - } - - usb_fill_bulk_urb(urb, - priv->usb, - usb_sndbulkpipe(priv->usb, 3), - skb->data, - skb->len, - vnt_tx_context_complete, - context); - - usb_anchor_urb(urb, &priv->tx_submitted); - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_dbg(&priv->usb->dev, "Submit Tx URB failed %d\n", status); - usb_unanchor_urb(urb); - context->in_use = false; - } - - usb_free_urb(urb); - - return status; -} diff --git a/drivers/staging/vt6656/usbpipe.h b/drivers/staging/vt6656/usbpipe.h deleted file mode 100644 index 922312e299bf6..0000000000000 --- a/drivers/staging/vt6656/usbpipe.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: - * - * Author: Warren Hsu - * - * Date: Mar. 30, 2005 - * - */ - -#ifndef __USBPIPE_H__ -#define __USBPIPE_H__ - -#include "device.h" - -struct vnt_interrupt_data { - u8 tsr0; - u8 pkt0; - u16 time0; - u8 tsr1; - u8 pkt1; - u16 time1; - u8 tsr2; - u8 pkt2; - u16 time2; - u8 tsr3; - u8 pkt3; - u16 time3; - __le64 tsf; - u8 isr0; - u8 isr1; - u8 rts_success; - u8 rts_fail; - u8 ack_fail; - u8 fcs_err; - u8 sw[2]; -} __packed; - -struct vnt_tx_usb_header { - u8 type; - u8 pkt_no; - __le16 tx_byte_count; -} __packed; - -#define VNT_REG_BLOCK_SIZE 64 - -int vnt_control_out(struct vnt_private *priv, u8 request, u16 value, - u16 index, u16 length, const u8 *buffer); -int vnt_control_in(struct vnt_private *priv, u8 request, u16 value, - u16 index, u16 length, u8 *buffer); - -int vnt_control_out_u8(struct vnt_private *priv, u8 reg, u8 ref_off, u8 data); -int vnt_control_in_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 *data); - -int vnt_control_out_blocks(struct vnt_private *priv, - u16 block, u8 reg, u16 len, const u8 *data); - -int vnt_start_interrupt_urb(struct vnt_private *priv); -int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb); -int vnt_tx_context(struct vnt_private *priv, - struct vnt_usb_send_context *context, - struct sk_buff *skb); - -#endif /* __USBPIPE_H__ */ diff --git a/drivers/staging/vt6656/wcmd.c b/drivers/staging/vt6656/wcmd.c deleted file mode 100644 index 14b8aa5871190..0000000000000 --- a/drivers/staging/vt6656/wcmd.c +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handles the management command interface functions - * - * Author: Lyndon Chen - * - * Date: May 8, 2003 - * - * Functions: - * vnt_cmd_complete - Command Complete function - * vnt_schedule_command - Push Command and wait Command Scheduler to do - * vnt_cmd_timer_wait- Call back timer - * - * Revision History: - * - */ - -#include "device.h" -#include "mac.h" -#include "wcmd.h" -#include "power.h" -#include "usbpipe.h" -#include "rxtx.h" -#include "rf.h" - -static void vnt_cmd_timer_wait(struct vnt_private *priv, unsigned long msecs) -{ - schedule_delayed_work(&priv->run_command_work, msecs_to_jiffies(msecs)); -} - -static u32 add_one_with_wrap_around(u32 var, u8 modulo) -{ - if (var >= (modulo - 1)) - var = 0; - else - var++; - return var; -} - -static int vnt_cmd_complete(struct vnt_private *priv) -{ - priv->command_state = WLAN_CMD_IDLE; - if (priv->free_cmd_queue == CMD_Q_SIZE) { - /* Command Queue Empty */ - priv->cmd_running = false; - return true; - } - - priv->command = priv->cmd_queue[priv->cmd_dequeue_idx]; - - priv->cmd_dequeue_idx = add_one_with_wrap_around(priv->cmd_dequeue_idx, CMD_Q_SIZE); - priv->free_cmd_queue++; - priv->cmd_running = true; - - switch (priv->command) { - case WLAN_CMD_INIT_MAC80211: - priv->command_state = WLAN_CMD_INIT_MAC80211_START; - break; - - case WLAN_CMD_TBTT_WAKEUP: - priv->command_state = WLAN_CMD_TBTT_WAKEUP_START; - break; - - case WLAN_CMD_BECON_SEND: - priv->command_state = WLAN_CMD_BECON_SEND_START; - break; - - case WLAN_CMD_SETPOWER: - priv->command_state = WLAN_CMD_SETPOWER_START; - break; - - case WLAN_CMD_CHANGE_ANTENNA: - priv->command_state = WLAN_CMD_CHANGE_ANTENNA_START; - break; - - default: - break; - } - - vnt_cmd_timer_wait(priv, 0); - - return true; -} - -void vnt_run_command(struct work_struct *work) -{ - struct vnt_private *priv = - container_of(work, struct vnt_private, run_command_work.work); - - if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) - return; - - if (!priv->cmd_running) - return; - - switch (priv->command_state) { - case WLAN_CMD_INIT_MAC80211_START: - if (priv->mac_hw) - break; - - dev_info(&priv->usb->dev, "Starting mac80211\n"); - - if (vnt_init(priv)) { - /* If fail all ends TODO retry */ - dev_err(&priv->usb->dev, "failed to start\n"); - usb_set_intfdata(priv->intf, NULL); - ieee80211_free_hw(priv->hw); - return; - } - - break; - - case WLAN_CMD_TBTT_WAKEUP_START: - vnt_next_tbtt_wakeup(priv); - break; - - case WLAN_CMD_BECON_SEND_START: - if (!priv->vif) - break; - - vnt_beacon_make(priv, priv->vif); - - vnt_mac_reg_bits_on(priv, MAC_REG_TCR, TCR_AUTOBCNTX); - - break; - - case WLAN_CMD_SETPOWER_START: - - vnt_rf_setpower(priv, priv->hw->conf.chandef.chan); - - break; - - case WLAN_CMD_CHANGE_ANTENNA_START: - dev_dbg(&priv->usb->dev, "Change from Antenna%d to", - priv->rx_antenna_sel); - - if (priv->rx_antenna_sel == 0) { - priv->rx_antenna_sel = 1; - if (priv->tx_rx_ant_inv) - vnt_set_antenna_mode(priv, ANT_RXA); - else - vnt_set_antenna_mode(priv, ANT_RXB); - } else { - priv->rx_antenna_sel = 0; - if (priv->tx_rx_ant_inv) - vnt_set_antenna_mode(priv, ANT_RXB); - else - vnt_set_antenna_mode(priv, ANT_RXA); - } - break; - - default: - break; - } - - vnt_cmd_complete(priv); -} - -int vnt_schedule_command(struct vnt_private *priv, enum vnt_cmd command) -{ - if (priv->free_cmd_queue == 0) - return false; - - priv->cmd_queue[priv->cmd_enqueue_idx] = command; - - priv->cmd_enqueue_idx = add_one_with_wrap_around(priv->cmd_enqueue_idx, CMD_Q_SIZE); - priv->free_cmd_queue--; - - if (!priv->cmd_running) - vnt_cmd_complete(priv); - - return true; -} - -void vnt_reset_command_timer(struct vnt_private *priv) -{ - priv->free_cmd_queue = CMD_Q_SIZE; - priv->cmd_dequeue_idx = 0; - priv->cmd_enqueue_idx = 0; - priv->command_state = WLAN_CMD_IDLE; - priv->cmd_running = false; -} diff --git a/drivers/staging/vt6656/wcmd.h b/drivers/staging/vt6656/wcmd.h deleted file mode 100644 index a62924671b17e..0000000000000 --- a/drivers/staging/vt6656/wcmd.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Handles the management command interface functions - * - * Author: Lyndon Chen - * - * Date: May 8, 2002 - * - */ - -#ifndef __WCMD_H__ -#define __WCMD_H__ - -#include "device.h" - -/* Command code */ -enum vnt_cmd { - WLAN_CMD_INIT_MAC80211, - WLAN_CMD_SETPOWER, - WLAN_CMD_TBTT_WAKEUP, - WLAN_CMD_BECON_SEND, - WLAN_CMD_CHANGE_ANTENNA -}; - -#define CMD_Q_SIZE 32 - -/* Command state */ -enum vnt_cmd_state { - WLAN_CMD_INIT_MAC80211_START, - WLAN_CMD_SETPOWER_START, - WLAN_CMD_TBTT_WAKEUP_START, - WLAN_CMD_BECON_SEND_START, - WLAN_CMD_CHANGE_ANTENNA_START, - WLAN_CMD_IDLE -}; - -struct vnt_private; - -void vnt_reset_command_timer(struct vnt_private *priv); - -int vnt_schedule_command(struct vnt_private *priv, enum vnt_cmd); - -void vnt_run_command(struct work_struct *work); - -#endif /* __WCMD_H__ */ diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 440e07b1d5cdb..287ac5b0495f9 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -369,7 +369,7 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) bdev_file = bdev_file_open_by_path(dev->udev_path, BLK_OPEN_WRITE | BLK_OPEN_READ, pdv, NULL); if (IS_ERR(bdev_file)) { - pr_err("pSCSI: bdev_open_by_path() failed\n"); + pr_err("pSCSI: bdev_file_open_by_path() failed\n"); scsi_device_put(sd); return PTR_ERR(bdev_file); } diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 61e7ae524b1f8..d3f9686e26e71 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -220,6 +220,15 @@ config DEVFREQ_THERMAL If you want this support, you should say Y here. +config PCIE_THERMAL + bool "PCIe cooling support" + depends on PCIEPORTBUS + help + This implements PCIe cooling mechanism through bandwidth reduction + for PCIe devices. + + If you want this support, you should say Y here. + config THERMAL_EMULATION bool "Thermal emulation mode support" help diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 1e1559bb971e5..9abf43a74f2bb 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -32,6 +32,8 @@ thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o # devfreq cooling thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o +thermal_sys-$(CONFIG_PCIE_THERMAL) += pcie_cooling.o + obj-$(CONFIG_K3_THERMAL) += k3_bandgap.o k3_j72xx_bandgap.o # platform thermal drivers obj-y += broadcom/ diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index ac6fa6b8f99f0..3b644de3292e2 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -588,10 +588,15 @@ static void allow_maximum_power(struct thermal_zone_device *tz) static int check_power_actors(struct thermal_zone_device *tz, struct power_allocator_params *params) { - const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); + const struct thermal_trip_desc *td; struct thermal_instance *instance; int ret = 0; + if (!params->trip_max) + return 0; + + td = trip_to_trip_desc(params->trip_max); + list_for_each_entry(instance, &td->thermal_instances, trip_node) { if (!cdev_is_power_actor(instance->cdev)) { dev_warn(&tz->device, "power_allocator: %s is not a power actor\n", diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index b2fc02c3a767a..8660ef2175be8 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -75,11 +75,6 @@ struct odvp_attr { static BIN_ATTR_SIMPLE_RO(data_vault); -static struct bin_attribute *data_attributes[] = { - &bin_attr_data_vault, - NULL, -}; - static ssize_t imok_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -108,10 +103,6 @@ static const struct attribute_group imok_attribute_group = { .attrs = imok_attr, }; -static const struct attribute_group data_attribute_group = { - .bin_attrs = data_attributes, -}; - static ssize_t available_uuids_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -137,7 +128,7 @@ static ssize_t current_uuid_show(struct device *dev, struct int3400_thermal_priv *priv = dev_get_drvdata(dev); int i, length = 0; - if (priv->current_uuid_index > 0) + if (priv->current_uuid_index >= 0) return sprintf(buf, "%s\n", int3400_thermal_uuids[priv->current_uuid_index]); @@ -624,8 +615,7 @@ static int int3400_thermal_probe(struct platform_device *pdev) } if (!ZERO_OR_NULL_PTR(priv->data_vault)) { - result = sysfs_create_group(&pdev->dev.kobj, - &data_attribute_group); + result = device_create_bin_file(&pdev->dev, &bin_attr_data_vault); if (result) goto free_uuid; } @@ -648,7 +638,7 @@ static int int3400_thermal_probe(struct platform_device *pdev) free_sysfs: cleanup_odvp(priv); if (!ZERO_OR_NULL_PTR(priv->data_vault)) { - sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); + device_remove_bin_file(&pdev->dev, &bin_attr_data_vault); kfree(priv->data_vault); } free_uuid: @@ -683,7 +673,7 @@ static void int3400_thermal_remove(struct platform_device *pdev) acpi_thermal_rel_misc_device_remove(priv->adev->handle); if (!ZERO_OR_NULL_PTR(priv->data_vault)) - sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); + device_remove_bin_file(&pdev->dev, &bin_attr_data_vault); sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); thermal_zone_device_unregister(priv->thermal); diff --git a/drivers/thermal/pcie_cooling.c b/drivers/thermal/pcie_cooling.c new file mode 100644 index 0000000000000..a876d64f15827 --- /dev/null +++ b/drivers/thermal/pcie_cooling.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCIe cooling device + * + * Copyright (C) 2023-2024 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COOLING_DEV_TYPE_PREFIX "PCIe_Port_Link_Speed_" + +static int pcie_cooling_get_max_level(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct pci_dev *port = cdev->devdata; + + /* cooling state 0 is same as the maximum PCIe speed */ + *state = port->subordinate->max_bus_speed - PCIE_SPEED_2_5GT; + + return 0; +} + +static int pcie_cooling_get_cur_level(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct pci_dev *port = cdev->devdata; + + /* cooling state 0 is same as the maximum PCIe speed */ + *state = cdev->max_state - (port->subordinate->cur_bus_speed - PCIE_SPEED_2_5GT); + + return 0; +} + +static int pcie_cooling_set_cur_level(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct pci_dev *port = cdev->devdata; + enum pci_bus_speed speed; + + /* cooling state 0 is same as the maximum PCIe speed */ + speed = (cdev->max_state - state) + PCIE_SPEED_2_5GT; + + return pcie_set_target_speed(port, speed, true); +} + +static struct thermal_cooling_device_ops pcie_cooling_ops = { + .get_max_state = pcie_cooling_get_max_level, + .get_cur_state = pcie_cooling_get_cur_level, + .set_cur_state = pcie_cooling_set_cur_level, +}; + +struct thermal_cooling_device *pcie_cooling_device_register(struct pci_dev *port) +{ + char *name __free(kfree) = + kasprintf(GFP_KERNEL, COOLING_DEV_TYPE_PREFIX "%s", pci_name(port)); + if (!name) + return ERR_PTR(-ENOMEM); + + return thermal_cooling_device_register(name, port, &pcie_cooling_ops); +} + +void pcie_cooling_device_unregister(struct thermal_cooling_device *cdev) +{ + thermal_cooling_device_unregister(cdev); +} + +/* For bus_speed <-> state arithmetic */ +static_assert(PCIE_SPEED_2_5GT + 1 == PCIE_SPEED_5_0GT); +static_assert(PCIE_SPEED_5_0GT + 1 == PCIE_SPEED_8_0GT); +static_assert(PCIE_SPEED_8_0GT + 1 == PCIE_SPEED_16_0GT); +static_assert(PCIE_SPEED_16_0GT + 1 == PCIE_SPEED_32_0GT); +static_assert(PCIE_SPEED_32_0GT + 1 == PCIE_SPEED_64_0GT); + +MODULE_AUTHOR("Ilpo Järvinen "); +MODULE_DESCRIPTION("PCIe cooling driver"); diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index 5e94a45eba3ee..d7f2e6ca92c2c 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -938,7 +938,6 @@ static const struct adc_tm5_data adc_tm5_gen2_data_pmic = { static int adc_tm5_get_dt_data(struct adc_tm5_chip *adc_tm, struct device_node *node) { struct adc_tm5_channel *channels; - struct device_node *child; u32 value; int ret; struct device *dev = adc_tm->dev; @@ -982,12 +981,10 @@ static int adc_tm5_get_dt_data(struct adc_tm5_chip *adc_tm, struct device_node * adc_tm->avg_samples = VADC_DEF_AVG_SAMPLES; } - for_each_available_child_of_node(node, child) { + for_each_available_child_of_node_scoped(node, child) { ret = adc_tm5_get_dt_channel_data(adc_tm, channels, child); - if (ret) { - of_node_put(child); + if (ret) return ret; - } channels++; } diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index 3203d8bd13a8f..22674790629a7 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -348,19 +349,18 @@ static void sun8i_ths_reset_control_assert(void *data) static struct regmap *sun8i_ths_get_sram_regmap(struct device_node *node) { - struct device_node *sram_node; struct platform_device *sram_pdev; struct regmap *regmap = NULL; - sram_node = of_parse_phandle(node, "allwinner,sram", 0); + struct device_node *sram_node __free(device_node) = + of_parse_phandle(node, "allwinner,sram", 0); if (!sram_node) return ERR_PTR(-ENODEV); sram_pdev = of_find_device_by_node(sram_node); if (!sram_pdev) { /* platform device might not be probed yet */ - regmap = ERR_PTR(-EPROBE_DEFER); - goto out_put_node; + return ERR_PTR(-EPROBE_DEFER); } /* If no regmap is found then the other device driver is at fault */ @@ -369,8 +369,7 @@ static struct regmap *sun8i_ths_get_sram_regmap(struct device_node *node) regmap = ERR_PTR(-EINVAL); platform_device_put(sram_pdev); -out_put_node: - of_node_put(sram_node); + return regmap; } diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 6f1501e3fcc4c..2c5ddf0db40c6 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1651,7 +1651,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tegra_soctherm *ts = dev_get_drvdata(dev); - struct device_node *np_stc, *np_stcc; + struct device_node *np_stc; const char *name; int i; @@ -1668,7 +1668,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) return; } - for_each_child_of_node(np_stc, np_stcc) { + for_each_child_of_node_scoped(np_stc, np_stcc) { struct soctherm_throt_cfg *stc; struct thermal_cooling_device *tcd; int err; @@ -1683,7 +1683,6 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) if (stc->init) { dev_err(dev, "throttle-cfg: %s: redefined!\n", name); - of_node_put(np_stcc); break; } diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 07e09897165f3..fab11b98ca495 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -95,13 +95,11 @@ static int thermal_of_populate_trip(struct device_node *np, static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *ntrips) { - struct thermal_trip *tt; - struct device_node *trips; int ret, count; *ntrips = 0; - trips = of_get_child_by_name(np, "trips"); + struct device_node *trips __free(device_node) = of_get_child_by_name(np, "trips"); if (!trips) return NULL; @@ -109,39 +107,27 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n if (!count) return NULL; - tt = kzalloc(sizeof(*tt) * count, GFP_KERNEL); - if (!tt) { - ret = -ENOMEM; - goto out_of_node_put; - } - - *ntrips = count; + struct thermal_trip *tt __free(kfree) = kzalloc(sizeof(*tt) * count, GFP_KERNEL); + if (!tt) + return ERR_PTR(-ENOMEM); count = 0; for_each_child_of_node_scoped(trips, trip) { ret = thermal_of_populate_trip(trip, &tt[count++]); if (ret) - goto out_kfree; + return ERR_PTR(ret); } - of_node_put(trips); - - return tt; - -out_kfree: - kfree(tt); -out_of_node_put: - of_node_put(trips); + *ntrips = count; - return ERR_PTR(ret); + return no_free_ptr(tt); } static struct device_node *of_thermal_zone_find(struct device_node *sensor, int id) { - struct device_node *np, *tz; struct of_phandle_args sensor_specs; - np = of_find_node_by_name(NULL, "thermal-zones"); + struct device_node *np __free(device_node) = of_find_node_by_name(NULL, "thermal-zones"); if (!np) { pr_debug("No thermal zones description\n"); return ERR_PTR(-ENODEV); @@ -159,8 +145,7 @@ static struct device_node *of_thermal_zone_find(struct device_node *sensor, int "#thermal-sensor-cells"); if (count <= 0) { pr_err("%pOFn: missing thermal sensor\n", child); - tz = ERR_PTR(-EINVAL); - goto out; + return ERR_PTR(-EINVAL); } for (i = 0; i < count; i++) { @@ -172,22 +157,18 @@ static struct device_node *of_thermal_zone_find(struct device_node *sensor, int i, &sensor_specs); if (ret < 0) { pr_err("%pOFn: Failed to read thermal-sensors cells: %d\n", child, ret); - tz = ERR_PTR(ret); - goto out; + return ERR_PTR(ret); } if ((sensor == sensor_specs.np) && id == (sensor_specs.args_count ? sensor_specs.args[0] : 0)) { pr_debug("sensor %pOFn id=%d belongs to %pOFn\n", sensor, id, child); - tz = no_free_ptr(child); - goto out; + return no_free_ptr(child); } } } - tz = ERR_PTR(-ENODEV); -out: - of_node_put(np); - return tz; + + return ERR_PTR(-ENODEV); } static int thermal_of_monitor_init(struct device_node *np, int *delay, int *pdelay) @@ -297,7 +278,7 @@ static bool thermal_of_should_bind(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, struct cooling_spec *c) { - struct device_node *tz_np, *cm_np, *child; + struct device_node *tz_np, *cm_np; bool result = false; tz_np = thermal_of_zone_get_by_name(tz); @@ -311,7 +292,7 @@ static bool thermal_of_should_bind(struct thermal_zone_device *tz, goto out; /* Look up the trip and the cdev in the cooling maps. */ - for_each_child_of_node(cm_np, child) { + for_each_child_of_node_scoped(cm_np, child) { struct device_node *tr_np; int count, i; @@ -330,7 +311,6 @@ static bool thermal_of_should_bind(struct thermal_zone_device *tz, break; } - of_node_put(child); break; } diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 350310bd0feea..a1d0d8a33f208 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -7,6 +7,7 @@ * Mika Westerberg */ +#include #include #include #include @@ -43,6 +44,24 @@ #define MAX_DWELL_TIME 500 /* ms */ #define DWELL_SAMPLE_INTERVAL 10 +enum usb4_margin_cap_voltage_indp { + USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN, + USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL, + USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH, + USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN, + USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH, + USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN, +}; + +enum usb4_margin_cap_time_indp { + USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN, + USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR, + USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH, + USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN, + USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH, + USB4_MARGIN_CAP_TIME_INDP_UNKNOWN, +}; + /* Sideband registers and their sizes as defined in the USB4 spec */ struct sb_reg { unsigned int reg; @@ -395,6 +414,8 @@ static ssize_t retimer_sb_regs_write(struct file *file, * @target: Sideband target * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @dev: Pointer to the device that is the target (USB4 port or retimer) + * @gen: Link generation + * @asym_rx: %true% if @port supports asymmetric link with 3 Rx * @caps: Port lane margining capabilities * @results: Last lane margining results * @lanes: %0, %1 or %7 (all) @@ -416,15 +437,19 @@ static ssize_t retimer_sb_regs_write(struct file *file, * @time: %true if time margining is used instead of voltage * @right_high: %false if left/low margin test is performed, %true if * right/high + * @upper_eye: %false if the lower PAM3 eye is used, %true if the upper + * eye is used */ struct tb_margining { struct tb_port *port; enum usb4_sb_target target; u8 index; struct device *dev; - u32 caps[2]; - u32 results[2]; - unsigned int lanes; + unsigned int gen; + bool asym_rx; + u32 caps[3]; + u32 results[3]; + enum usb4_margining_lane lanes; unsigned int min_ber_level; unsigned int max_ber_level; unsigned int ber_level; @@ -441,6 +466,7 @@ struct tb_margining { bool software; bool time; bool right_high; + bool upper_eye; }; static int margining_modify_error_counter(struct tb_margining *margining, @@ -463,35 +489,75 @@ static int margining_modify_error_counter(struct tb_margining *margining, static bool supports_software(const struct tb_margining *margining) { - return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW; + if (margining->gen < 4) + return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW; + return margining->caps[2] & USB4_MARGIN_CAP_2_MODES_SW; } static bool supports_hardware(const struct tb_margining *margining) { - return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW; + if (margining->gen < 4) + return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW; + return margining->caps[2] & USB4_MARGIN_CAP_2_MODES_HW; } -static bool both_lanes(const struct tb_margining *margining) +static bool all_lanes(const struct tb_margining *margining) { - return margining->caps[0] & USB4_MARGIN_CAP_0_2_LANES; + return margining->caps[0] & USB4_MARGIN_CAP_0_ALL_LANES; } -static unsigned int +static enum usb4_margin_cap_voltage_indp independent_voltage_margins(const struct tb_margining *margining) { - return FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK, margining->caps[0]); + if (margining->gen < 4) { + switch (FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK, margining->caps[0])) { + case USB4_MARGIN_CAP_0_VOLTAGE_MIN: + return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN; + case USB4_MARGIN_CAP_0_VOLTAGE_HL: + return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL; + case USB4_MARGIN_CAP_1_TIME_BOTH: + return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH; + } + } else { + switch (FIELD_GET(USB4_MARGIN_CAP_2_VOLTAGE_INDP_MASK, margining->caps[2])) { + case USB4_MARGIN_CAP_2_VOLTAGE_MIN: + return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN; + case USB4_MARGIN_CAP_2_VOLTAGE_BOTH: + return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH; + } + } + return USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN; } static bool supports_time(const struct tb_margining *margining) { - return margining->caps[0] & USB4_MARGIN_CAP_0_TIME; + if (margining->gen < 4) + return margining->caps[0] & USB4_MARGIN_CAP_0_TIME; + return margining->caps[2] & USB4_MARGIN_CAP_2_TIME; } /* Only applicable if supports_time() returns true */ -static unsigned int +static enum usb4_margin_cap_time_indp independent_time_margins(const struct tb_margining *margining) { - return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]); + if (margining->gen < 4) { + switch (FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1])) { + case USB4_MARGIN_CAP_1_TIME_MIN: + return USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN; + case USB4_MARGIN_CAP_1_TIME_LR: + return USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR; + case USB4_MARGIN_CAP_1_TIME_BOTH: + return USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH; + } + } else { + switch (FIELD_GET(USB4_MARGIN_CAP_2_TIME_INDP_MASK, margining->caps[2])) { + case USB4_MARGIN_CAP_2_TIME_MIN: + return USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN; + case USB4_MARGIN_CAP_2_TIME_BOTH: + return USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH; + } + } + return USB4_MARGIN_CAP_TIME_INDP_UNKNOWN; } static bool @@ -570,16 +636,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used) { struct tb_margining *margining = s->private; struct tb *tb = margining->port->sw->tb; - u32 cap0, cap1; + int ret = 0; if (mutex_lock_interruptible(&tb->lock)) return -ERESTARTSYS; /* Dump the raw caps first */ - cap0 = margining->caps[0]; - seq_printf(s, "0x%08x\n", cap0); - cap1 = margining->caps[1]; - seq_printf(s, "0x%08x\n", cap1); + for (int i = 0; i < ARRAY_SIZE(margining->caps); i++) + seq_printf(s, "0x%08x\n", margining->caps[i]); seq_printf(s, "# software margining: %s\n", supports_software(margining) ? "yes" : "no"); @@ -593,8 +657,8 @@ static int margining_caps_show(struct seq_file *s, void *not_used) seq_puts(s, "# hardware margining: no\n"); } - seq_printf(s, "# both lanes simultaneously: %s\n", - both_lanes(margining) ? "yes" : "no"); + seq_printf(s, "# all lanes simultaneously: %s\n", + str_yes_no(all_lanes(margining))); seq_printf(s, "# voltage margin steps: %u\n", margining->voltage_steps); seq_printf(s, "# maximum voltage offset: %u mV\n", @@ -609,32 +673,54 @@ static int margining_caps_show(struct seq_file *s, void *not_used) } switch (independent_voltage_margins(margining)) { - case USB4_MARGIN_CAP_0_VOLTAGE_MIN: + case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN: seq_puts(s, "# returns minimum between high and low voltage margins\n"); break; - case USB4_MARGIN_CAP_0_VOLTAGE_HL: + case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL: seq_puts(s, "# returns high or low voltage margin\n"); break; - case USB4_MARGIN_CAP_0_VOLTAGE_BOTH: + case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH: seq_puts(s, "# returns both high and low margins\n"); break; + case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN: + seq_puts(s, "# returns minimum between high and low voltage margins in both lower and upper eye\n"); + break; + case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH: + seq_puts(s, "# returns both high and low margins of both upper and lower eye\n"); + break; + case USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN: + tb_port_warn(margining->port, + "failed to parse independent voltage margining capabilities\n"); + ret = -EIO; + goto out; } if (supports_time(margining)) { seq_puts(s, "# time margining: yes\n"); seq_printf(s, "# time margining is destructive: %s\n", - cap1 & USB4_MARGIN_CAP_1_TIME_DESTR ? "yes" : "no"); + str_yes_no(margining->caps[1] & USB4_MARGIN_CAP_1_TIME_DESTR)); switch (independent_time_margins(margining)) { - case USB4_MARGIN_CAP_1_TIME_MIN: + case USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN: seq_puts(s, "# returns minimum between left and right time margins\n"); break; - case USB4_MARGIN_CAP_1_TIME_LR: + case USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR: seq_puts(s, "# returns left or right margin\n"); break; - case USB4_MARGIN_CAP_1_TIME_BOTH: + case USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH: seq_puts(s, "# returns both left and right margins\n"); break; + case USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN: + seq_puts(s, "# returns minimum between left and right time margins in both lower and upper eye\n"); + break; + case USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH: + seq_puts(s, "# returns both left and right margins of both upper and lower eye\n"); + break; + case USB4_MARGIN_CAP_TIME_INDP_UNKNOWN: + tb_port_warn(margining->port, + "failed to parse independent time margining capabilities\n"); + ret = -EIO; + goto out; } seq_printf(s, "# time margin steps: %u\n", @@ -645,19 +731,43 @@ static int margining_caps_show(struct seq_file *s, void *not_used) seq_puts(s, "# time margining: no\n"); } +out: mutex_unlock(&tb->lock); - return 0; + return ret; } DEBUGFS_ATTR_RO(margining_caps); +static const struct { + enum usb4_margining_lane lane; + const char *name; +} lane_names[] = { + { + .lane = USB4_MARGINING_LANE_RX0, + .name = "0", + }, + { + .lane = USB4_MARGINING_LANE_RX1, + .name = "1", + }, + { + .lane = USB4_MARGINING_LANE_RX2, + .name = "2", + }, + { + .lane = USB4_MARGINING_LANE_ALL, + .name = "all", + }, +}; + static ssize_t margining_lanes_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct seq_file *s = file->private_data; struct tb_margining *margining = s->private; - struct tb *tb = margining->port->sw->tb; - int ret = 0; + struct tb_port *port = margining->port; + struct tb *tb = port->sw->tb; + int lane = -1; char *buf; buf = validate_and_copy_from_user(user_buf, &count); @@ -666,57 +776,60 @@ margining_lanes_write(struct file *file, const char __user *user_buf, buf[count - 1] = '\0'; - if (mutex_lock_interruptible(&tb->lock)) { - ret = -ERESTARTSYS; - goto out_free; + for (int i = 0; i < ARRAY_SIZE(lane_names); i++) { + if (!strcmp(buf, lane_names[i].name)) { + lane = lane_names[i].lane; + break; + } } - if (!strcmp(buf, "0")) { - margining->lanes = 0; - } else if (!strcmp(buf, "1")) { - margining->lanes = 1; - } else if (!strcmp(buf, "all")) { - /* Needs to be supported */ - if (both_lanes(margining)) - margining->lanes = 7; - else - ret = -EINVAL; - } else { - ret = -EINVAL; - } + free_page((unsigned long)buf); - mutex_unlock(&tb->lock); + if (lane == -1) + return -EINVAL; -out_free: - free_page((unsigned long)buf); - return ret < 0 ? ret : count; + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (lane == USB4_MARGINING_LANE_ALL && !all_lanes(margining)) + return -EINVAL; + /* + * Enabling on RX2 requires that it is supported by the + * USB4 port. + */ + if (lane == USB4_MARGINING_LANE_RX2 && !margining->asym_rx) + return -EINVAL; + + margining->lanes = lane; + } + + return count; } static int margining_lanes_show(struct seq_file *s, void *not_used) { struct tb_margining *margining = s->private; - struct tb *tb = margining->port->sw->tb; - unsigned int lanes; - - if (mutex_lock_interruptible(&tb->lock)) - return -ERESTARTSYS; + struct tb_port *port = margining->port; + struct tb *tb = port->sw->tb; - lanes = margining->lanes; - if (both_lanes(margining)) { - if (!lanes) - seq_puts(s, "[0] 1 all\n"); - else if (lanes == 1) - seq_puts(s, "0 [1] all\n"); - else - seq_puts(s, "0 1 [all]\n"); - } else { - if (!lanes) - seq_puts(s, "[0] 1\n"); - else - seq_puts(s, "0 [1]\n"); + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + for (int i = 0; i < ARRAY_SIZE(lane_names); i++) { + if (lane_names[i].lane == USB4_MARGINING_LANE_ALL && + !all_lanes(margining)) + continue; + if (lane_names[i].lane == USB4_MARGINING_LANE_RX2 && + !margining->asym_rx) + continue; + + if (i != 0) + seq_putc(s, ' '); + + if (lane_names[i].lane == margining->lanes) + seq_printf(s, "[%s]", lane_names[i].name); + else + seq_printf(s, "%s", lane_names[i].name); + } + seq_puts(s, "\n"); } - mutex_unlock(&tb->lock); return 0; } DEBUGFS_ATTR_RW(margining_lanes); @@ -1004,13 +1117,16 @@ static int margining_run_sw(struct tb_margining *margining, if (ret) break; - if (margining->lanes == USB4_MARGIN_SW_LANE_0) + if (margining->lanes == USB4_MARGINING_LANE_RX0) errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, margining->results[1]); - else if (margining->lanes == USB4_MARGIN_SW_LANE_1) + else if (margining->lanes == USB4_MARGINING_LANE_RX1) errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, margining->results[1]); - else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES) + else if (margining->lanes == USB4_MARGINING_LANE_RX2) + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK, + margining->results[1]); + else if (margining->lanes == USB4_MARGINING_LANE_ALL) errors = margining->results[1]; /* Any errors stop the test */ @@ -1030,6 +1146,31 @@ static int margining_run_sw(struct tb_margining *margining, return ret; } +static int validate_margining(struct tb_margining *margining) +{ + /* + * For running on RX2 the link must be asymmetric with 3 + * receivers. Because this is can change dynamically, check it + * here before we start the margining and report back error if + * expectations are not met. + */ + if (margining->lanes == USB4_MARGINING_LANE_RX2) { + int ret; + + ret = tb_port_get_link_width(margining->port); + if (ret < 0) + return ret; + if (ret != TB_LINK_WIDTH_ASYM_RX) { + tb_port_warn(margining->port, "link is %s expected %s", + tb_width_name(ret), + tb_width_name(TB_LINK_WIDTH_ASYM_RX)); + return -EINVAL; + } + } + + return 0; +} + static int margining_run_write(void *data, u64 val) { struct tb_margining *margining = data; @@ -1050,6 +1191,10 @@ static int margining_run_write(void *data, u64 val) goto out_rpm_put; } + ret = validate_margining(margining); + if (ret) + goto out_unlock; + if (tb_is_upstream_port(port)) down_sw = sw; else if (port->remote) @@ -1080,6 +1225,7 @@ static int margining_run_write(void *data, u64 val) .time = margining->time, .voltage_time_offset = margining->voltage_time_offset, .right_high = margining->right_high, + .upper_eye = margining->upper_eye, .optional_voltage_offset_range = margining->optional_voltage_offset_range, }; @@ -1095,6 +1241,7 @@ static int margining_run_write(void *data, u64 val) .lanes = margining->lanes, .time = margining->time, .right_high = margining->right_high, + .upper_eye = margining->upper_eye, .optional_voltage_offset_range = margining->optional_voltage_offset_range, }; @@ -1104,7 +1251,7 @@ static int margining_run_write(void *data, u64 val) margining->lanes); ret = usb4_port_hw_margin(port, margining->target, margining->index, ¶ms, - margining->results); + margining->results, ARRAY_SIZE(margining->results)); } if (down_sw) @@ -1132,13 +1279,12 @@ static ssize_t margining_results_write(struct file *file, return -ERESTARTSYS; /* Just clear the results */ - margining->results[0] = 0; - margining->results[1] = 0; + memset(margining->results, 0, sizeof(margining->results)); if (margining->software) { /* Clear the error counters */ margining_modify_error_counter(margining, - USB4_MARGIN_SW_ALL_LANES, + USB4_MARGINING_LANE_ALL, USB4_MARGIN_SW_ERROR_COUNTER_CLEAR); } @@ -1151,10 +1297,10 @@ static void voltage_margin_show(struct seq_file *s, { unsigned int tmp, voltage; - tmp = FIELD_GET(USB4_MARGIN_HW_RES_1_MARGIN_MASK, val); + tmp = FIELD_GET(USB4_MARGIN_HW_RES_MARGIN_MASK, val); voltage = tmp * margining->max_voltage_offset / margining->voltage_steps; seq_printf(s, "%u mV (%u)", voltage, tmp); - if (val & USB4_MARGIN_HW_RES_1_EXCEEDS) + if (val & USB4_MARGIN_HW_RES_EXCEEDS) seq_puts(s, " exceeds maximum"); seq_puts(s, "\n"); if (margining->optional_voltage_offset_range) @@ -1166,14 +1312,55 @@ static void time_margin_show(struct seq_file *s, { unsigned int tmp, interval; - tmp = FIELD_GET(USB4_MARGIN_HW_RES_1_MARGIN_MASK, val); + tmp = FIELD_GET(USB4_MARGIN_HW_RES_MARGIN_MASK, val); interval = tmp * margining->max_time_offset / margining->time_steps; seq_printf(s, "%u mUI (%u)", interval, tmp); - if (val & USB4_MARGIN_HW_RES_1_EXCEEDS) + if (val & USB4_MARGIN_HW_RES_EXCEEDS) seq_puts(s, " exceeds maximum"); seq_puts(s, "\n"); } +static u8 margining_hw_result_val(const u32 *results, + enum usb4_margining_lane lane, + bool right_high) +{ + u32 val; + + if (lane == USB4_MARGINING_LANE_RX0) + val = results[1]; + else if (lane == USB4_MARGINING_LANE_RX1) + val = results[1] >> USB4_MARGIN_HW_RES_LANE_SHIFT; + else if (lane == USB4_MARGINING_LANE_RX2) + val = results[2]; + else + val = 0; + + return right_high ? val : val >> USB4_MARGIN_HW_RES_LL_SHIFT; +} + +static void margining_hw_result_format(struct seq_file *s, + const struct tb_margining *margining, + enum usb4_margining_lane lane) +{ + u8 val; + + if (margining->time) { + val = margining_hw_result_val(margining->results, lane, true); + seq_printf(s, "# lane %u right time margin: ", lane); + time_margin_show(s, margining, val); + val = margining_hw_result_val(margining->results, lane, false); + seq_printf(s, "# lane %u left time margin: ", lane); + time_margin_show(s, margining, val); + } else { + val = margining_hw_result_val(margining->results, lane, true); + seq_printf(s, "# lane %u high voltage margin: ", lane); + voltage_margin_show(s, margining, val); + val = margining_hw_result_val(margining->results, lane, false); + seq_printf(s, "# lane %u low voltage margin: ", lane); + voltage_margin_show(s, margining, val); + } +} + static int margining_results_show(struct seq_file *s, void *not_used) { struct tb_margining *margining = s->private; @@ -1186,69 +1373,46 @@ static int margining_results_show(struct seq_file *s, void *not_used) seq_printf(s, "0x%08x\n", margining->results[0]); /* Only the hardware margining has two result dwords */ if (!margining->software) { - unsigned int val; - - seq_printf(s, "0x%08x\n", margining->results[1]); - - if (margining->time) { - if (!margining->lanes || margining->lanes == 7) { - val = margining->results[1]; - seq_puts(s, "# lane 0 right time margin: "); - time_margin_show(s, margining, val); - val = margining->results[1] >> - USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT; - seq_puts(s, "# lane 0 left time margin: "); - time_margin_show(s, margining, val); - } - if (margining->lanes == 1 || margining->lanes == 7) { - val = margining->results[1] >> - USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT; - seq_puts(s, "# lane 1 right time margin: "); - time_margin_show(s, margining, val); - val = margining->results[1] >> - USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT; - seq_puts(s, "# lane 1 left time margin: "); - time_margin_show(s, margining, val); - } + for (int i = 1; i < ARRAY_SIZE(margining->results); i++) + seq_printf(s, "0x%08x\n", margining->results[i]); + + if (margining->lanes == USB4_MARGINING_LANE_ALL) { + margining_hw_result_format(s, margining, + USB4_MARGINING_LANE_RX0); + margining_hw_result_format(s, margining, + USB4_MARGINING_LANE_RX1); + if (margining->asym_rx) + margining_hw_result_format(s, margining, + USB4_MARGINING_LANE_RX2); } else { - if (!margining->lanes || margining->lanes == 7) { - val = margining->results[1]; - seq_puts(s, "# lane 0 high voltage margin: "); - voltage_margin_show(s, margining, val); - val = margining->results[1] >> - USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT; - seq_puts(s, "# lane 0 low voltage margin: "); - voltage_margin_show(s, margining, val); - } - if (margining->lanes == 1 || margining->lanes == 7) { - val = margining->results[1] >> - USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT; - seq_puts(s, "# lane 1 high voltage margin: "); - voltage_margin_show(s, margining, val); - val = margining->results[1] >> - USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT; - seq_puts(s, "# lane 1 low voltage margin: "); - voltage_margin_show(s, margining, val); - } + margining_hw_result_format(s, margining, + margining->lanes); } } else { u32 lane_errors, result; seq_printf(s, "0x%08x\n", margining->results[1]); - result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]); - if (result == USB4_MARGIN_SW_LANE_0 || - result == USB4_MARGIN_SW_ALL_LANES) { + result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]); + if (result == USB4_MARGINING_LANE_RX0 || + result == USB4_MARGINING_LANE_ALL) { lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, margining->results[1]); seq_printf(s, "# lane 0 errors: %u\n", lane_errors); } - if (result == USB4_MARGIN_SW_LANE_1 || - result == USB4_MARGIN_SW_ALL_LANES) { + if (result == USB4_MARGINING_LANE_RX1 || + result == USB4_MARGINING_LANE_ALL) { lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, margining->results[1]); seq_printf(s, "# lane 1 errors: %u\n", lane_errors); } + if (margining->asym_rx && + (result == USB4_MARGINING_LANE_RX2 || + result == USB4_MARGINING_LANE_ALL)) { + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK, + margining->results[1]); + seq_printf(s, "# lane 2 errors: %u\n", lane_errors); + } } mutex_unlock(&tb->lock); @@ -1382,6 +1546,55 @@ static int margining_margin_show(struct seq_file *s, void *not_used) } DEBUGFS_ATTR_RW(margining_margin); +static ssize_t margining_eye_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_port *port = s->private; + struct usb4_port *usb4 = port->usb4; + struct tb *tb = port->sw->tb; + int ret = 0; + char *buf; + + buf = validate_and_copy_from_user(user_buf, &count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + buf[count - 1] = '\0'; + + scoped_cond_guard(mutex_intr, ret = -ERESTARTSYS, &tb->lock) { + if (!strcmp(buf, "lower")) + usb4->margining->upper_eye = false; + else if (!strcmp(buf, "upper")) + usb4->margining->upper_eye = true; + else + ret = -EINVAL; + } + + free_page((unsigned long)buf); + return ret ? ret : count; +} + +static int margining_eye_show(struct seq_file *s, void *not_used) +{ + struct tb_port *port = s->private; + struct usb4_port *usb4 = port->usb4; + struct tb *tb = port->sw->tb; + + scoped_guard(mutex_intr, &tb->lock) { + if (usb4->margining->upper_eye) + seq_puts(s, "lower [upper]\n"); + else + seq_puts(s, "[lower] upper\n"); + + return 0; + } + + return -ERESTARTSYS; +} +DEBUGFS_ATTR_RW(margining_eye); + static struct tb_margining *margining_alloc(struct tb_port *port, struct device *dev, enum usb4_sb_target target, @@ -1392,6 +1605,12 @@ static struct tb_margining *margining_alloc(struct tb_port *port, unsigned int val; int ret; + ret = tb_port_get_link_generation(port); + if (ret < 0) { + tb_port_warn(port, "failed to read link generation\n"); + return NULL; + } + margining = kzalloc(sizeof(*margining), GFP_KERNEL); if (!margining) return NULL; @@ -1400,8 +1619,11 @@ static struct tb_margining *margining_alloc(struct tb_port *port, margining->target = target; margining->index = index; margining->dev = dev; + margining->gen = ret; + margining->asym_rx = tb_port_width_supported(port, TB_LINK_WIDTH_ASYM_RX); - ret = usb4_port_margining_caps(port, target, index, margining->caps); + ret = usb4_port_margining_caps(port, target, index, margining->caps, + ARRAY_SIZE(margining->caps)); if (ret) { kfree(margining); return NULL; @@ -1411,10 +1633,17 @@ static struct tb_margining *margining_alloc(struct tb_port *port, if (supports_software(margining)) margining->software = true; - val = FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK, margining->caps[0]); - margining->voltage_steps = val; - val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]); - margining->max_voltage_offset = 74 + val * 2; + if (margining->gen < 4) { + val = FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK, margining->caps[0]); + margining->voltage_steps = val; + val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]); + margining->max_voltage_offset = 74 + val * 2; + } else { + val = FIELD_GET(USB4_MARGIN_CAP_2_VOLTAGE_STEPS_MASK, margining->caps[2]); + margining->voltage_steps = val; + val = FIELD_GET(USB4_MARGIN_CAP_2_MAX_VOLTAGE_OFFSET_MASK, margining->caps[2]); + margining->max_voltage_offset = 74 + val * 2; + } if (supports_optional_voltage_offset_range(margining)) { val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK, @@ -1456,11 +1685,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port, debugfs_create_file("results", 0600, dir, margining, &margining_results_fops); debugfs_create_file("test", 0600, dir, margining, &margining_test_fops); - if (independent_voltage_margins(margining) == USB4_MARGIN_CAP_0_VOLTAGE_HL || + if (independent_voltage_margins(margining) == USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL || (supports_time(margining) && - independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR)) - debugfs_create_file("margin", 0600, dir, margining, - &margining_margin_fops); + independent_time_margins(margining) == USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR)) + debugfs_create_file("margin", 0600, dir, margining, &margining_margin_fops); margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; margining->dwell_time = MIN_DWELL_TIME; @@ -1477,6 +1705,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port, debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining, &margining_dwell_time_fops); } + + if (margining->gen >= 4) + debugfs_create_file("eye", 0600, dir, port, &margining_eye_fops); + return margining; } diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 7af2642b97cb8..1257dd3ce7e6a 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1340,18 +1340,18 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (res) return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n"); - res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt"); - if (res) - return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n"); - nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL); if (!nhi) return -ENOMEM; nhi->pdev = pdev; nhi->ops = (const struct tb_nhi_ops *)id->driver_data; - /* cannot fail - table is allocated in pcim_iomap_regions */ - nhi->iobase = pcim_iomap_table(pdev)[0]; + + nhi->iobase = pcim_iomap_region(pdev, 0, "thunderbolt"); + res = PTR_ERR_OR_ZERO(nhi->iobase); + if (res) + return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n"); + nhi->hop_count = ioread32(nhi->iobase + REG_CAPS) & 0x3ff; dev_dbg(dev, "total paths: %d\n", nhi->hop_count); diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index dbcad25ead50f..5391502a4b873 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -49,7 +49,7 @@ enum usb4_sb_opcode { /* USB4_SB_OPCODE_READ_LANE_MARGINING_CAP */ #define USB4_MARGIN_CAP_0_MODES_HW BIT(0) #define USB4_MARGIN_CAP_0_MODES_SW BIT(1) -#define USB4_MARGIN_CAP_0_2_LANES BIT(2) +#define USB4_MARGIN_CAP_0_ALL_LANES BIT(2) #define USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK GENMASK(4, 3) #define USB4_MARGIN_CAP_0_VOLTAGE_MIN 0x0 #define USB4_MARGIN_CAP_0_VOLTAGE_HL 0x1 @@ -69,34 +69,44 @@ enum usb4_sb_opcode { #define USB4_MARGIN_CAP_1_TIME_OFFSET_MASK GENMASK(20, 16) #define USB4_MARGIN_CAP_1_MIN_BER_MASK GENMASK(25, 21) #define USB4_MARGIN_CAP_1_MAX_BER_MASK GENMASK(30, 26) +#define USB4_MARGIN_CAP_2_MODES_HW BIT(0) +#define USB4_MARGIN_CAP_2_MODES_SW BIT(1) +#define USB4_MARGIN_CAP_2_TIME BIT(2) +#define USB4_MARGIN_CAP_2_MAX_VOLTAGE_OFFSET_MASK GENMASK(8, 3) +#define USB4_MARGIN_CAP_2_VOLTAGE_STEPS_MASK GENMASK(15, 9) +#define USB4_MARGIN_CAP_2_VOLTAGE_INDP_MASK GENMASK(17, 16) +#define USB4_MARGIN_CAP_2_VOLTAGE_MIN 0x0 +#define USB4_MARGIN_CAP_2_VOLTAGE_BOTH 0x1 +#define USB4_MARGIN_CAP_2_TIME_INDP_MASK GENMASK(19, 18) +#define USB4_MARGIN_CAP_2_TIME_MIN 0x0 +#define USB4_MARGIN_CAP_2_TIME_BOTH 0x1 /* USB4_SB_OPCODE_RUN_HW_LANE_MARGINING */ #define USB4_MARGIN_HW_TIME BIT(3) -#define USB4_MARGIN_HW_RH BIT(4) +#define USB4_MARGIN_HW_RHU BIT(4) #define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5) #define USB4_MARGIN_HW_BER_SHIFT 5 #define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10) /* Applicable to all margin values */ -#define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0) -#define USB4_MARGIN_HW_RES_1_EXCEEDS BIT(7) -/* Different lane margin shifts */ -#define USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT 8 -#define USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT 16 -#define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24 +#define USB4_MARGIN_HW_RES_MARGIN_MASK GENMASK(6, 0) +#define USB4_MARGIN_HW_RES_EXCEEDS BIT(7) + +/* Shifts for parsing the lane results */ +#define USB4_MARGIN_HW_RES_LANE_SHIFT 16 +#define USB4_MARGIN_HW_RES_LL_SHIFT 8 /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */ #define USB4_MARGIN_SW_LANES_MASK GENMASK(2, 0) -#define USB4_MARGIN_SW_LANE_0 0x0 -#define USB4_MARGIN_SW_LANE_1 0x1 -#define USB4_MARGIN_SW_ALL_LANES 0x7 #define USB4_MARGIN_SW_TIME BIT(3) #define USB4_MARGIN_SW_RH BIT(4) #define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5) #define USB4_MARGIN_SW_VT_MASK GENMASK(12, 6) #define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13) +#define USB4_MARGIN_SW_UPPER_EYE BIT(15) #define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0) #define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4) +#define USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK GENMASK(11, 8) #endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 6737188f25815..ddbf0cd783776 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1367,11 +1367,18 @@ enum usb4_margin_sw_error_counter { USB4_MARGIN_SW_ERROR_COUNTER_STOP, }; +enum usb4_margining_lane { + USB4_MARGINING_LANE_RX0 = 0, + USB4_MARGINING_LANE_RX1 = 1, + USB4_MARGINING_LANE_RX2 = 2, + USB4_MARGINING_LANE_ALL = 7, +}; + /** * struct usb4_port_margining_params - USB4 margining parameters * @error_counter: Error counter operation for software margining * @ber_level: Current BER level contour value - * @lanes: %0, %1 or %7 (all) + * @lanes: Lanes to enable for the margining operation * @voltage_time_offset: Offset for voltage / time for software margining * @optional_voltage_offset_range: Enable optional extended voltage range * @right_high: %false if left/low margin test is performed, %true if right/high @@ -1380,18 +1387,19 @@ enum usb4_margin_sw_error_counter { struct usb4_port_margining_params { enum usb4_margin_sw_error_counter error_counter; u32 ber_level; - u32 lanes; + enum usb4_margining_lane lanes; u32 voltage_time_offset; bool optional_voltage_offset_range; bool right_high; + bool upper_eye; bool time; }; int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, - u8 index, u32 *caps); + u8 index, u32 *caps, size_t ncaps); int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, u8 index, const struct usb4_port_margining_params *params, - u32 *results); + u32 *results, size_t nresults); int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, u8 index, const struct usb4_port_margining_params *params, u32 *results); diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 402fdf8b1cdec..e51d01671d8e7 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1631,11 +1631,12 @@ int usb4_port_asym_start(struct tb_port *port) * @target: Sideband target * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @caps: Array with at least two elements to hold the results + * @ncaps: Number of elements in the caps array * * Reads the USB4 port lane margining capabilities into @caps. */ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, - u8 index, u32 *caps) + u8 index, u32 *caps, size_t ncaps) { int ret; @@ -1645,7 +1646,7 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, return ret; return usb4_port_sb_read(port, target, index, USB4_SB_DATA, caps, - sizeof(*caps) * 2); + sizeof(*caps) * ncaps); } /** @@ -1654,14 +1655,15 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, * @target: Sideband target * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @params: Parameters for USB4 hardware margining - * @results: Array with at least two elements to hold the results + * @results: Array to hold the results + * @nresults: Number of elements in the results array * * Runs hardware lane margining on USB4 port and returns the result in * @results. */ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, u8 index, const struct usb4_port_margining_params *params, - u32 *results) + u32 *results, size_t nresults) { u32 val; int ret; @@ -1672,8 +1674,8 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, val = params->lanes; if (params->time) val |= USB4_MARGIN_HW_TIME; - if (params->right_high) - val |= USB4_MARGIN_HW_RH; + if (params->right_high || params->upper_eye) + val |= USB4_MARGIN_HW_RHU; if (params->ber_level) val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level); if (params->optional_voltage_offset_range) @@ -1690,7 +1692,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, return ret; return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results, - sizeof(*results) * 2); + sizeof(*results) * nresults); } /** @@ -1722,6 +1724,8 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, val |= USB4_MARGIN_SW_OPT_VOLTAGE; if (params->right_high) val |= USB4_MARGIN_SW_RH; + if (params->upper_eye) + val |= USB4_MARGIN_SW_UPPER_EYE; val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter); val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset); diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 25c201cfb91e1..e5da9ce26006d 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -569,7 +569,7 @@ static struct platform_driver aspeed_vuart_driver = { .of_match_table = aspeed_vuart_table, }, .probe = aspeed_vuart_probe, - .remove_new = aspeed_vuart_remove, + .remove = aspeed_vuart_remove, }; module_platform_driver(aspeed_vuart_driver); diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index d7a0f271263a9..fdb53b54e99e6 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -267,7 +267,7 @@ static struct platform_driver bcm2835aux_serial_driver = { .pm = pm_ptr(&bcm2835aux_dev_pm_ops), }, .probe = bcm2835aux_serial_probe, - .remove_new = bcm2835aux_serial_remove, + .remove = bcm2835aux_serial_remove, }; module_platform_driver(bcm2835aux_serial_driver); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 2569ca69223f3..d0b18358859e1 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -812,7 +812,7 @@ static int brcmuart_handle_irq(struct uart_port *p) /* * if Receive Data Interrupt is enabled and * we're uing hardware flow control, deassert - * RTS and wait for any chars in the pipline to + * RTS and wait for any chars in the pipeline to * arrive and then check for DR again. */ if ((ier & UART_IER_RDI) && (up->mcr & UART_MCR_AFE)) { @@ -1204,7 +1204,7 @@ static struct platform_driver brcmuart_platform_driver = { .of_match_table = brcmuart_dt_ids, }, .probe = brcmuart_probe, - .remove_new = brcmuart_remove, + .remove = brcmuart_remove, }; static int __init brcmuart_init(void) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index ab9e7f2042602..6afcf27db3b88 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -750,7 +750,7 @@ static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { .quirks = DW_UART_QUIRK_CPR_VALUE | DW_UART_QUIRK_IS_DMA_FC, }; -static const struct dw8250_platform_data dw8250_starfive_jh7100_data = { +static const struct dw8250_platform_data dw8250_skip_set_rate_data = { .usr_reg = DW_UART_USR, .quirks = DW_UART_QUIRK_SKIP_SET_RATE, }; @@ -760,7 +760,8 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data }, { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, - { .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data }, + { .compatible = "sophgo,sg2044-uart", .data = &dw8250_skip_set_rate_data }, + { .compatible = "starfive,jh7100-uart", .data = &dw8250_skip_set_rate_data }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); @@ -796,7 +797,7 @@ static struct platform_driver dw8250_platform_driver = { .acpi_match_table = dw8250_acpi_match, }, .probe = dw8250_probe, - .remove_new = dw8250_remove, + .remove = dw8250_remove, }; module_platform_driver(dw8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index a754755100ffe..35094f884492c 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -219,7 +219,7 @@ static struct platform_driver serial8250_em_platform_driver = { .of_match_table = serial8250_em_dt_ids, }, .probe = serial8250_em_probe, - .remove_new = serial8250_em_remove, + .remove = serial8250_em_remove, }; module_platform_driver(serial8250_em_platform_driver); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index b7a75db15249a..fc52034462670 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -135,8 +136,6 @@ #define UART_EXAR_REGB_EECS BIT(5) #define UART_EXAR_REGB_EEDI BIT(6) #define UART_EXAR_REGB_EEDO BIT(7) -#define UART_EXAR_REGB_EE_ADDR_SIZE 6 -#define UART_EXAR_REGB_EE_DATA_SIZE 16 #define UART_EXAR_XR17C15X_PORT_OFFSET 0x200 #define UART_EXAR_XR17V25X_PORT_OFFSET 0x200 @@ -179,18 +178,19 @@ /* CTI EEPROM offsets */ #define CTI_EE_OFF_XR17C15X_OSC_FREQ 0x04 /* 2 words */ -#define CTI_EE_OFF_XR17V25X_OSC_FREQ 0x08 /* 2 words */ #define CTI_EE_OFF_XR17C15X_PART_NUM 0x0A /* 4 words */ -#define CTI_EE_OFF_XR17V25X_PART_NUM 0x0E /* 4 words */ #define CTI_EE_OFF_XR17C15X_SERIAL_NUM 0x0E /* 1 word */ + +#define CTI_EE_OFF_XR17V25X_OSC_FREQ 0x08 /* 2 words */ +#define CTI_EE_OFF_XR17V25X_PART_NUM 0x0E /* 4 words */ #define CTI_EE_OFF_XR17V25X_SERIAL_NUM 0x12 /* 1 word */ + #define CTI_EE_OFF_XR17V35X_SERIAL_NUM 0x11 /* 2 word */ #define CTI_EE_OFF_XR17V35X_BRD_FLAGS 0x13 /* 1 word */ #define CTI_EE_OFF_XR17V35X_PORT_FLAGS 0x14 /* 1 word */ #define CTI_EE_MASK_PORT_FLAGS_TYPE GENMASK(7, 0) -#define CTI_EE_MASK_OSC_FREQ_LOWER GENMASK(15, 0) -#define CTI_EE_MASK_OSC_FREQ_UPPER GENMASK(31, 16) +#define CTI_EE_MASK_OSC_FREQ GENMASK(31, 0) #define CTI_FPGA_RS485_IO_REG 0x2008 #define CTI_FPGA_CFG_INT_EN_REG 0x48 @@ -252,6 +252,7 @@ struct exar8250 { unsigned int nr; unsigned int osc_freq; struct exar8250_board *board; + struct eeprom_93cx6 eeprom; void __iomem *virt; int line[]; }; @@ -267,92 +268,37 @@ static inline u8 exar_read_reg(struct exar8250 *priv, unsigned int reg) return readb(priv->virt + reg); } -static inline void exar_ee_select(struct exar8250 *priv) -{ - // Set chip select pin high to enable EEPROM reads/writes - exar_write_reg(priv, UART_EXAR_REGB, UART_EXAR_REGB_EECS); - // Min ~500ns delay needed between CS assert and EEPROM access - udelay(1); -} - -static inline void exar_ee_deselect(struct exar8250 *priv) -{ - exar_write_reg(priv, UART_EXAR_REGB, 0x00); -} - -static inline void exar_ee_write_bit(struct exar8250 *priv, u8 bit) +static void exar_eeprom_93cx6_reg_read(struct eeprom_93cx6 *eeprom) { - u8 value = UART_EXAR_REGB_EECS; + struct exar8250 *priv = eeprom->data; + u8 regb = exar_read_reg(priv, UART_EXAR_REGB); - if (bit) - value |= UART_EXAR_REGB_EEDI; - - // Clock out the bit on the EEPROM interface - exar_write_reg(priv, UART_EXAR_REGB, value); - // 2us delay = ~500khz clock speed - udelay(2); - - value |= UART_EXAR_REGB_EECK; - - exar_write_reg(priv, UART_EXAR_REGB, value); - udelay(2); + /* EECK and EECS always read 0 from REGB so only set EEDO */ + eeprom->reg_data_out = regb & UART_EXAR_REGB_EEDO; } -static inline u8 exar_ee_read_bit(struct exar8250 *priv) +static void exar_eeprom_93cx6_reg_write(struct eeprom_93cx6 *eeprom) { - u8 regb; - u8 value = UART_EXAR_REGB_EECS; - - // Clock in the bit on the EEPROM interface - exar_write_reg(priv, UART_EXAR_REGB, value); - // 2us delay = ~500khz clock speed - udelay(2); + struct exar8250 *priv = eeprom->data; + u8 regb = 0; - value |= UART_EXAR_REGB_EECK; + if (eeprom->reg_data_in) + regb |= UART_EXAR_REGB_EEDI; + if (eeprom->reg_data_clock) + regb |= UART_EXAR_REGB_EECK; + if (eeprom->reg_chip_select) + regb |= UART_EXAR_REGB_EECS; - exar_write_reg(priv, UART_EXAR_REGB, value); - udelay(2); - - regb = exar_read_reg(priv, UART_EXAR_REGB); - - return (regb & UART_EXAR_REGB_EEDO ? 1 : 0); + exar_write_reg(priv, UART_EXAR_REGB, regb); } -/** - * exar_ee_read() - Read a word from the EEPROM - * @priv: Device's private structure - * @ee_addr: Offset of EEPROM to read word from - * - * Read a single 16bit word from an Exar UART's EEPROM. - * The type of the EEPROM is AT93C46D. - * - * Return: EEPROM word - */ -static u16 exar_ee_read(struct exar8250 *priv, u8 ee_addr) +static void exar_eeprom_init(struct exar8250 *priv) { - int i; - u16 data = 0; - - exar_ee_select(priv); - - // Send read command (opcode 110) - exar_ee_write_bit(priv, 1); - exar_ee_write_bit(priv, 1); - exar_ee_write_bit(priv, 0); - - // Send address to read from - for (i = UART_EXAR_REGB_EE_ADDR_SIZE - 1; i >= 0; i--) - exar_ee_write_bit(priv, ee_addr & BIT(i)); - - // Read data 1 bit at a time starting with a dummy bit - for (i = UART_EXAR_REGB_EE_DATA_SIZE; i >= 0; i--) { - if (exar_ee_read_bit(priv)) - data |= BIT(i); - } - - exar_ee_deselect(priv); - - return data; + priv->eeprom.data = priv; + priv->eeprom.register_read = exar_eeprom_93cx6_reg_read; + priv->eeprom.register_write = exar_eeprom_93cx6_reg_write; + priv->eeprom.width = PCI_EEPROM_WIDTH_93C46; + priv->eeprom.quirks |= PCI_EEPROM_QUIRK_EXTRA_READ_CYCLE; } /** @@ -360,7 +306,7 @@ static u16 exar_ee_read(struct exar8250 *priv, u8 ee_addr) * @priv: Device's private structure * @mpio_num: MPIO number/offset to configure * - * Configure a single MPIO as an output and disable tristate. It is reccomended + * Configure a single MPIO as an output and disable tristate. It is recommended * to set the level with exar_mpio_set_high()/exar_mpio_set_low() prior to * calling this function to ensure default MPIO pin state. * @@ -516,7 +462,7 @@ static int xr17v35x_startup(struct uart_port *port) serial_port_out(port, UART_XR_EFR, UART_EFR_ECB); /* - * Make sure all interrups are masked until initialization is + * Make sure all interrupts are masked until initialization is * complete and the FIFOs are cleared * * Synchronize UART_IER access against the console. @@ -696,20 +642,16 @@ static int cti_plx_int_enable(struct exar8250 *priv) */ static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) { - u16 lower_word; - u16 upper_word; + __le16 ee_words[2]; + u32 osc_freq; - lower_word = exar_ee_read(priv, eeprom_offset); - // Check if EEPROM word was blank - if (lower_word == 0xFFFF) - return -EIO; + eeprom_93cx6_multiread(&priv->eeprom, eeprom_offset, ee_words, ARRAY_SIZE(ee_words)); - upper_word = exar_ee_read(priv, (eeprom_offset + 1)); - if (upper_word == 0xFFFF) + osc_freq = le16_to_cpu(ee_words[0]) | (le16_to_cpu(ee_words[1]) << 16); + if (osc_freq == CTI_EE_MASK_OSC_FREQ) return -EIO; - return FIELD_PREP(CTI_EE_MASK_OSC_FREQ_LOWER, lower_word) | - FIELD_PREP(CTI_EE_MASK_OSC_FREQ_UPPER, upper_word); + return osc_freq; } /** @@ -833,7 +775,7 @@ static enum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv, u8 offset; offset = CTI_EE_OFF_XR17V35X_PORT_FLAGS + port_num; - port_flags = exar_ee_read(priv, offset); + eeprom_93cx6_read(&priv->eeprom, offset, &port_flags); port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags); if (CTI_PORT_TYPE_VALID(port_type)) @@ -1551,6 +1493,8 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) if (rc) return rc; + exar_eeprom_init(priv); + for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index e2aa2a1a02ddf..b4461a89b8d0c 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -21,6 +21,7 @@ #define CHIP_ID_F81866 0x1010 #define CHIP_ID_F81966 0x0215 #define CHIP_ID_F81216AD 0x1602 +#define CHIP_ID_F81216E 0x1617 #define CHIP_ID_F81216H 0x0501 #define CHIP_ID_F81216 0x0802 #define VENDOR_ID1 0x23 @@ -125,7 +126,7 @@ static int fintek_8250_enter_key(u16 base_port, u8 key) if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; - /* Force to deactive all SuperIO in this base_port */ + /* Force to deactivate all SuperIO in this base_port */ outb(EXIT_KEY, base_port + ADDR_PORT); outb(key, base_port + ADDR_PORT); @@ -158,6 +159,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) case CHIP_ID_F81866: case CHIP_ID_F81966: case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: break; @@ -181,6 +183,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, return 0; case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: *min = F81216_LDN_LOW; @@ -250,6 +253,7 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) break; case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, @@ -263,7 +267,8 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) { switch (pdata->pid) { - case CHIP_ID_F81216H: /* 128Bytes FIFO */ + case CHIP_ID_F81216E: /* 128Bytes FIFO */ + case CHIP_ID_F81216H: case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, @@ -297,6 +302,7 @@ static void fintek_8250_set_termios(struct uart_port *port, goto exit; switch (pdata->pid) { + case CHIP_ID_F81216E: case CHIP_ID_F81216H: reg = RS485; break; @@ -346,6 +352,7 @@ static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) struct fintek_8250 *pdata = uart->port.private_data; switch (pdata->pid) { + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81966: case CHIP_ID_F81866: @@ -438,6 +445,11 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) uart->port.rs485_supported = fintek_8250_rs485_supported; break; + case CHIP_ID_F81216E: /* F81216E does not support RS485 delays */ + uart->port.rs485_config = fintek_8250_rs485_config; + uart->port.rs485_supported = fintek_8250_rs485_supported; + break; + default: /* No RS485 Auto direction functional */ break; } diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index b4ed442082a85..1b7bd55619c66 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -179,7 +179,7 @@ static struct platform_driver fsl8250_platform_driver = { .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), }, .probe = fsl8250_acpi_probe, - .remove_new = fsl8250_acpi_remove, + .remove = fsl8250_acpi_remove, }; module_platform_driver(fsl8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index a2783e38a2e31..a73dd37736404 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -361,7 +361,7 @@ static struct platform_driver ingenic_uart_platform_driver = { .of_match_table = of_match, }, .probe = ingenic_uart_probe, - .remove_new = ingenic_uart_remove, + .remove = ingenic_uart_remove, }; module_platform_driver(ingenic_uart_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ioc3.c b/drivers/tty/serial/8250/8250_ioc3.c index 50c77c3dacf2c..499e80aa4cf96 100644 --- a/drivers/tty/serial/8250/8250_ioc3.c +++ b/drivers/tty/serial/8250/8250_ioc3.c @@ -84,7 +84,7 @@ static void serial8250_ioc3_remove(struct platform_device *pdev) static struct platform_driver serial8250_ioc3_driver = { .probe = serial8250_ioc3_probe, - .remove_new = serial8250_ioc3_remove, + .remove = serial8250_ioc3_remove, .driver = { .name = "ioc3-serial8250", } diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c index 47e1a056a60c3..d52445948da0b 100644 --- a/drivers/tty/serial/8250/8250_lpc18xx.c +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -195,7 +195,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); static struct platform_driver lpc18xx_serial_driver = { .probe = lpc18xx_serial_probe, - .remove_new = lpc18xx_serial_remove, + .remove = lpc18xx_serial_remove, .driver = { .name = "lpc18xx-uart", .of_match_table = lpc18xx_serial_match, diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index b9cca210e171c..b44de2ed7413f 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -346,8 +346,8 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, /* * Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS) * - * We need to recalcualte the quot register, as the claculation depends - * on the vaule in the highspeed register. + * We need to recalculate the quot register, as the calculation depends + * on the value in the highspeed register. * * Some baudrates are not supported by the chip, so we use the next * lower rate supported and update termios c_flag. @@ -654,7 +654,7 @@ static struct platform_driver mtk8250_platform_driver = { .of_match_table = mtk8250_of_match, }, .probe = mtk8250_probe, - .remove_new = mtk8250_remove, + .remove = mtk8250_remove, }; module_platform_driver(mtk8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index e14f47ef11725..64aed7efc5697 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -352,7 +352,7 @@ static struct platform_driver of_platform_serial_driver = { .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, - .remove_new = of_platform_serial_remove, + .remove = of_platform_serial_remove, }; module_platform_driver(of_platform_serial_driver); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 88b58f44e4e97..9eb9aa7668118 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -776,12 +776,12 @@ static void omap_8250_shutdown(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = port->private_data; + pm_runtime_get_sync(port->dev); + flush_work(&priv->qos_work); if (up->dma) omap_8250_rx_dma_flush(up); - pm_runtime_get_sync(port->dev); - serial_out(up, UART_OMAP_WER, 0); if (priv->habit & UART_HAS_EFR2) serial_out(up, UART_OMAP_EFR2, 0x0); @@ -1304,7 +1304,7 @@ static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, /* * This is mostly serial8250_handle_irq(). We have a slightly different DMA - * hoook for RX/TX and need different logic for them in the ISR. Therefore we + * hook for RX/TX and need different logic for them in the ISR. Therefore we * use the default routine in the non-DMA case and this one for with DMA. */ static int omap_8250_dma_handle_irq(struct uart_port *port) @@ -1338,7 +1338,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) serial8250_tx_chars(up); } else { /* - * try again due to an earlier failer which + * try again due to an earlier failure which * might have been resolved by now. */ if (omap_8250_tx_dma(up)) @@ -1867,7 +1867,7 @@ static struct platform_driver omap8250_platform_driver = { .of_match_table = omap8250_dt_ids, }, .probe = omap8250_probe, - .remove_new = omap8250_remove, + .remove = omap8250_remove, }; module_platform_driver(omap8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c index be7ff07cbdd00..8bdc1879d952b 100644 --- a/drivers/tty/serial/8250/8250_platform.c +++ b/drivers/tty/serial/8250/8250_platform.c @@ -285,7 +285,7 @@ MODULE_DEVICE_TABLE(acpi, acpi_platform_serial_table); static struct platform_driver serial8250_isa_driver = { .probe = serial8250_probe, - .remove_new = serial8250_remove, + .remove = serial8250_remove, .suspend = serial8250_suspend, .resume = serial8250_resume, .driver = { diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 96dd6126296c6..6dd0190b4843b 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -154,7 +154,7 @@ static void serial_pxa_remove(struct platform_device *pdev) static struct platform_driver serial_pxa_driver = { .probe = serial_pxa_probe, - .remove_new = serial_pxa_remove, + .remove = serial_pxa_remove, .driver = { .name = "pxa2xx-uart", diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c index 60a80d00d2519..2f3b0075763f2 100644 --- a/drivers/tty/serial/8250/8250_tegra.c +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -182,7 +182,7 @@ static struct platform_driver tegra_uart_driver = { .acpi_match_table = ACPI_PTR(tegra_uart_acpi_match), }, .probe = tegra_uart_probe, - .remove_new = tegra_uart_remove, + .remove = tegra_uart_remove, }; module_platform_driver(tegra_uart_driver); diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 670d2ca0f7572..4874a9632db39 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -282,7 +282,7 @@ MODULE_DEVICE_TABLE(of, uniphier_uart_match); static struct platform_driver uniphier_uart_platform_driver = { .probe = uniphier_uart_probe, - .remove_new = uniphier_uart_remove, + .remove = uniphier_uart_remove, .driver = { .name = "uniphier-uart", .of_match_table = uniphier_uart_match, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 1eb20350fcf43..55d26d16df9b9 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -150,6 +150,7 @@ config SERIAL_8250_EXAR tristate "8250/16550 Exar/Commtech PCI/PCIe device support" depends on SERIAL_8250 && PCI select SERIAL_8250_PCILIB + select EEPROM_93CX6 default SERIAL_8250 help This builds support for XR17C1xx, XR17V3xx and some Commtech diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index effcba71ea775..b9c3c3bed0c17 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -175,8 +175,8 @@ static int altera_jtaguart_startup(struct uart_port *port) ret = request_irq(port->irq, altera_jtaguart_interrupt, 0, DRV_NAME, port); if (ret) { - pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " - "interrupt vector=%d\n", port->line, port->irq); + dev_err(port->dev, "unable to attach Altera JTAG UART %d interrupt vector=%d\n", + port->line, port->irq); return ret; } @@ -449,7 +449,7 @@ MODULE_DEVICE_TABLE(of, altera_jtaguart_match); static struct platform_driver altera_jtaguart_platform_driver = { .probe = altera_jtaguart_probe, - .remove_new = altera_jtaguart_remove, + .remove = altera_jtaguart_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(altera_jtaguart_match), diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 897f0995b2fe7..c94655453c335 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -307,8 +307,8 @@ static int altera_uart_startup(struct uart_port *port) ret = request_irq(port->irq, altera_uart_interrupt, 0, dev_name(port->dev), port); if (ret) { - pr_err(DRV_NAME ": unable to attach Altera UART %d " - "interrupt vector=%d\n", port->line, port->irq); + dev_err(port->dev, "unable to attach Altera UART %d interrupt vector=%d\n", + port->line, port->irq); return ret; } } @@ -617,7 +617,7 @@ MODULE_DEVICE_TABLE(of, altera_uart_match); static struct platform_driver altera_uart_platform_driver = { .probe = altera_uart_probe, - .remove_new = altera_uart_remove, + .remove = altera_uart_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(altera_uart_match), diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 1c60850030b14..69b7a3e1e418e 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1819,6 +1819,13 @@ static void pl011_unthrottle_rx(struct uart_port *port) pl011_write(uap->im, uap, REG_IMSC); +#ifdef CONFIG_DMA_ENGINE + if (uap->using_rx_dma) { + uap->dmacr |= UART011_RXDMAE; + pl011_write(uap->dmacr, uap, REG_DMACR); + } +#endif + uart_port_unlock_irqrestore(&uap->port, flags); } @@ -2937,7 +2944,7 @@ MODULE_DEVICE_TABLE(acpi, sbsa_uart_acpi_match); static struct platform_driver arm_sbsa_uart_platform_driver = { .probe = sbsa_uart_probe, - .remove_new = sbsa_uart_remove, + .remove = sbsa_uart_remove, .driver = { .name = "sbsa-uart", .pm = &pl011_dev_pm_ops, diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 47889a5571198..8bb33556b3120 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -832,7 +832,7 @@ MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids); static struct platform_driver ar933x_uart_platform_driver = { .probe = ar933x_uart_probe, - .remove_new = ar933x_uart_remove, + .remove = ar933x_uart_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(ar933x_uart_of_ids), diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 09b246c9e389e..0cf05ac189938 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1166,7 +1166,7 @@ static void atmel_rx_from_dma(struct uart_port *port) port->icount.rx += count; } - /* USART retreives ownership of RX DMA buffer */ + /* USART retrieves ownership of RX DMA buffer */ dma_sync_single_for_device(port->dev, atmel_port->rx_phys, ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); @@ -2419,17 +2419,11 @@ static void atmel_release_port(struct uart_port *port) static int atmel_request_port(struct uart_port *port) { struct platform_device *mpdev = to_platform_device(port->dev->parent); - int size = resource_size(mpdev->resource); - - if (!request_mem_region(port->mapbase, size, "atmel_serial")) - return -EBUSY; if (port->flags & UPF_IOREMAP) { - port->membase = ioremap(port->mapbase, size); - if (port->membase == NULL) { - release_mem_region(port->mapbase, size); - return -ENOMEM; - } + port->membase = devm_platform_ioremap_resource(mpdev, 0); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); } return 0; @@ -3017,7 +3011,7 @@ static SIMPLE_DEV_PM_OPS(atmel_serial_pm_ops, atmel_serial_suspend, static struct platform_driver atmel_serial_driver = { .probe = atmel_serial_probe, - .remove_new = atmel_serial_remove, + .remove = atmel_serial_remove, .driver = { .name = "atmel_usart_serial", .of_match_table = of_match_ptr(atmel_serial_dt_ids), diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index b88cc28c94e33..51df9d2d8bfc5 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -884,7 +884,7 @@ MODULE_DEVICE_TABLE(of, bcm63xx_of_match); */ static struct platform_driver bcm_uart_platform_driver = { .probe = bcm_uart_probe, - .remove_new = bcm_uart_remove, + .remove = bcm_uart_remove, .driver = { .name = "bcm63xx_uart", .of_match_table = bcm63xx_of_match, diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 30425a3d19fbe..83186bf500027 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -529,7 +529,7 @@ static struct platform_driver clps711x_uart_platform = { .of_match_table = of_match_ptr(clps711x_uart_dt_ids), }, .probe = uart_clps711x_probe, - .remove_new = uart_clps711x_remove, + .remove = uart_clps711x_remove, }; static int __init uart_clps711x_init(void) diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index 6eb8625de435e..b778a20ec9b13 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -1573,7 +1573,7 @@ static struct platform_driver cpm_uart_driver = { .of_match_table = cpm_uart_match, }, .probe = cpm_uart_probe, - .remove_new = cpm_uart_remove, + .remove = cpm_uart_remove, }; static int __init cpm_uart_init(void) diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index 2ccd13cc0a899..d2482df5cb9b4 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -522,7 +522,7 @@ static struct platform_driver digicolor_uart_platform = { .of_match_table = of_match_ptr(digicolor_uart_dt_ids), }, .probe = digicolor_uart_probe, - .remove_new = digicolor_uart_remove, + .remove = digicolor_uart_remove, }; static int __init digicolor_uart_init(void) diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c index 85eb0392e3793..bb7cc65427f0b 100644 --- a/drivers/tty/serial/esp32_acm.c +++ b/drivers/tty/serial/esp32_acm.c @@ -423,7 +423,7 @@ static void esp32s3_acm_remove(struct platform_device *pdev) static struct platform_driver esp32s3_acm_driver = { .probe = esp32s3_acm_probe, - .remove_new = esp32s3_acm_remove, + .remove = esp32s3_acm_remove, .driver = { .name = DRIVER_NAME, .of_match_table = esp32s3_acm_dt_ids, diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c index 8c86cf9cb7630..667c2198a03a5 100644 --- a/drivers/tty/serial/esp32_uart.c +++ b/drivers/tty/serial/esp32_uart.c @@ -743,7 +743,7 @@ static void esp32_uart_remove(struct platform_device *pdev) static struct platform_driver esp32_uart_driver = { .probe = esp32_uart_probe, - .remove_new = esp32_uart_remove, + .remove = esp32_uart_remove, .driver = { .name = DRIVER_NAME, .of_match_table = esp32_uart_dt_ids, diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index e972df4b188d3..e70a56de1fced 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -882,7 +882,7 @@ static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume); static struct platform_driver linflex_driver = { .probe = linflex_probe, - .remove_new = linflex_remove, + .remove = linflex_remove, .driver = { .name = DRIVER_NAME, .of_match_table = linflex_dt_ids, diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 77efa7ee6eda2..57b0632a3db6f 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -184,6 +184,7 @@ #define UARTCTRL_SBK 0x00010000 #define UARTCTRL_MA1IE 0x00008000 #define UARTCTRL_MA2IE 0x00004000 +#define UARTCTRL_M7 0x00000800 #define UARTCTRL_IDLECFG GENMASK(10, 8) #define UARTCTRL_LOOPS 0x00000080 #define UARTCTRL_DOZEEN 0x00000040 @@ -2222,8 +2223,9 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, modem = lpuart32_read(&sport->port, UARTMODIR); sport->is_cs7 = false; /* - * only support CS8 and CS7, and for CS7 must enable PE. + * only support CS8 and CS7 * supported mode: + * - (7,n,1) (imx only) * - (7,e/o,1) * - (8,n,1) * - (8,m/s,1) @@ -2238,7 +2240,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CSIZE) == CS8 || (termios->c_cflag & CSIZE) == CS7) - ctrl = old_ctrl & ~UARTCTRL_M; + ctrl = old_ctrl & ~(UARTCTRL_M | UARTCTRL_M7); if (termios->c_cflag & CMSPAR) { if ((termios->c_cflag & CSIZE) != CS8) { @@ -2265,9 +2267,18 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, else bd &= ~UARTBAUD_SBNS; - /* parity must be enabled when CS7 to match 8-bits format */ - if ((termios->c_cflag & CSIZE) == CS7) - termios->c_cflag |= PARENB; + /* + * imx support 7-bits format, no limitation on parity when CS7 + * for layerscape, parity must be enabled when CS7 to match 8-bits format + */ + if ((termios->c_cflag & CSIZE) == CS7 && !(termios->c_cflag & PARENB)) { + if (is_imx7ulp_lpuart(sport) || + is_imx8ulp_lpuart(sport) || + is_imx8qxp_lpuart(sport)) + ctrl |= UARTCTRL_M7; + else + termios->c_cflag |= PARENB; + } if ((termios->c_cflag & PARENB)) { if (termios->c_cflag & CMSPAR) { @@ -3206,7 +3217,7 @@ static const struct dev_pm_ops lpuart_pm_ops = { static struct platform_driver lpuart_driver = { .probe = lpuart_probe, - .remove_new = lpuart_remove, + .remove = lpuart_remove, .driver = { .name = "fsl-lpuart", .of_match_table = lpuart_dt_ids, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 90974d338f3c0..17f70e4bee435 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -230,6 +230,8 @@ struct imx_port { unsigned int saved_reg[10]; bool context_saved; + bool last_putchar_was_newline; + enum imx_tx_state tx_state; struct hrtimer trigger_start_tx; struct hrtimer trigger_stop_tx; @@ -370,6 +372,7 @@ static void imx_uart_soft_reset(struct imx_port *sport) sport->idle_counter = 0; } +/* called with port.lock taken and irqs off */ static void imx_uart_disable_loopback_rs485(struct imx_port *sport) { unsigned int uts; @@ -470,6 +473,7 @@ static void imx_uart_stop_tx(struct uart_port *port) } } +/* called with port.lock taken and irqs off */ static void imx_uart_stop_rx_with_loopback_ctrl(struct uart_port *port, bool loopback) { struct imx_port *sport = to_imx_port(port); @@ -818,6 +822,8 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id) * issuing soft reset to the UART (just stop/start of RX does not help). Note * that what we do here is sending isolated start bit about 2.4 times shorter * than it is to be on UART configured baud rate. + * + * Called with port.lock taken and irqs off. */ static void imx_uart_check_flood(struct imx_port *sport, u32 usr2) { @@ -853,6 +859,7 @@ static void imx_uart_check_flood(struct imx_port *sport, u32 usr2) } } +/* called with port.lock taken and irqs off */ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; @@ -931,6 +938,7 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport); /* * We have a modem side uart, so the meanings of RTS and CTS are inverted. */ +/* called with port.lock taken and irqs off */ static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport) { unsigned int tmp = TIOCM_DSR; @@ -953,6 +961,8 @@ static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport) /* * Handle any change of modem status signal since we were last called. + * + * Called with port.lock taken and irqs off. */ static void imx_uart_mctrl_check(struct imx_port *sport) { @@ -1292,6 +1302,7 @@ static int imx_uart_start_rx_dma(struct imx_port *sport) return 0; } +/* called with port.lock taken and irqs off */ static void imx_uart_clear_rx_errors(struct imx_port *sport) { struct tty_port *port = &sport->port.state->port; @@ -1422,6 +1433,7 @@ static int imx_uart_dma_init(struct imx_port *sport) return ret; } +/* called with port.lock taken and irqs off */ static void imx_uart_enable_dma(struct imx_port *sport) { u32 ucr1; @@ -2069,26 +2081,34 @@ static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch) barrier(); imx_uart_writel(sport, ch, URTX0); + + sport->last_putchar_was_newline = (ch == '\n'); } -/* - * Interrupts are disabled on entering - */ -static void -imx_uart_console_write(struct console *co, const char *s, unsigned int count) +static void imx_uart_console_device_lock(struct console *co, unsigned long *flags) +{ + struct uart_port *up = &imx_uart_ports[co->index]->port; + + return __uart_port_lock_irqsave(up, flags); +} + +static void imx_uart_console_device_unlock(struct console *co, unsigned long flags) +{ + struct uart_port *up = &imx_uart_ports[co->index]->port; + + return __uart_port_unlock_irqrestore(up, flags); +} + +static void imx_uart_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) { struct imx_port *sport = imx_uart_ports[co->index]; + struct uart_port *port = &sport->port; struct imx_port_ucrs old_ucr; - unsigned long flags; unsigned int ucr1, usr2; - int locked = 1; - if (sport->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock_irqsave(&sport->port, &flags); - else - uart_port_lock_irqsave(&sport->port, &flags); + if (!nbcon_enter_unsafe(wctxt)) + return; /* * First, save UCR1/2/3 and then disable interrupts @@ -2102,10 +2122,12 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN); imx_uart_writel(sport, ucr1, UCR1); - imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2); - uart_console_write(&sport->port, s, count, imx_uart_console_putchar); + if (!sport->last_putchar_was_newline) + uart_console_write(port, "\n", 1, imx_uart_console_putchar); + uart_console_write(port, wctxt->outbuf, wctxt->len, + imx_uart_console_putchar); /* * Finally, wait for transmitter to become empty @@ -2115,8 +2137,73 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) 0, USEC_PER_SEC, false, sport, USR2); imx_uart_ucrs_restore(sport, &old_ucr); - if (locked) - uart_port_unlock_irqrestore(&sport->port, flags); + nbcon_exit_unsafe(wctxt); +} + +static void imx_uart_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct imx_port *sport = imx_uart_ports[co->index]; + struct uart_port *port = &sport->port; + struct imx_port_ucrs old_ucr; + unsigned int ucr1, usr2; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + /* + * First, save UCR1/2/3 and then disable interrupts + */ + imx_uart_ucrs_save(sport, &old_ucr); + ucr1 = old_ucr.ucr1; + + if (imx_uart_is_imx1(sport)) + ucr1 |= IMX1_UCR1_UARTCLKEN; + ucr1 |= UCR1_UARTEN; + ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN); + + imx_uart_writel(sport, ucr1, UCR1); + imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2); + + if (nbcon_exit_unsafe(wctxt)) { + int len = READ_ONCE(wctxt->len); + int i; + + /* + * Write out the message. Toggle unsafe for each byte in order + * to give another (higher priority) context the opportunity + * for a friendly takeover. If such a takeover occurs, this + * context must reacquire ownership in order to perform final + * actions (such as re-enabling the interrupts). + * + * IMPORTANT: wctxt->outbuf and wctxt->len are no longer valid + * after a reacquire so writing the message must be + * aborted. + */ + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + + uart_console_write(port, wctxt->outbuf + i, 1, + imx_uart_console_putchar); + + if (!nbcon_exit_unsafe(wctxt)) + break; + } + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + /* + * Finally, wait for transmitter to become empty + * and restore UCR1/2/3 + */ + read_poll_timeout(imx_uart_readl, usr2, usr2 & USR2_TXDC, + 0, USEC_PER_SEC, false, sport, USR2); + imx_uart_ucrs_restore(sport, &old_ucr); + + nbcon_exit_unsafe(wctxt); } /* @@ -2208,6 +2295,8 @@ imx_uart_console_setup(struct console *co, char *options) if (retval) goto error_console; + sport->last_putchar_was_newline = true; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else @@ -2244,11 +2333,14 @@ imx_uart_console_exit(struct console *co) static struct uart_driver imx_uart_uart_driver; static struct console imx_uart_console = { .name = DEV_NAME, - .write = imx_uart_console_write, + .write_atomic = imx_uart_console_write_atomic, + .write_thread = imx_uart_console_write_thread, + .device_lock = imx_uart_console_device_lock, + .device_unlock = imx_uart_console_device_unlock, + .flags = CON_PRINTBUFFER | CON_NBCON, .device = uart_console_device, .setup = imx_uart_console_setup, .exit = imx_uart_console_exit, - .flags = CON_PRINTBUFFER, .index = -1, .data = &imx_uart_uart_driver, }; @@ -2595,10 +2687,13 @@ static void imx_uart_save_context(struct imx_port *sport) uart_port_unlock_irqrestore(&sport->port, flags); } +/* called with irq off */ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) { u32 ucr3; + uart_port_lock(&sport->port); + ucr3 = imx_uart_readl(sport, UCR3); if (on) { imx_uart_writel(sport, USR1_AWAKE, USR1); @@ -2618,6 +2713,8 @@ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) } imx_uart_writel(sport, ucr1, UCR1); } + + uart_port_unlock(&sport->port); } static int imx_uart_suspend_noirq(struct device *dev) @@ -2717,7 +2814,7 @@ static const struct dev_pm_ops imx_uart_pm_ops = { static struct platform_driver imx_uart_platform_driver = { .probe = imx_uart_probe, - .remove_new = imx_uart_remove, + .remove = imx_uart_remove, .driver = { .name = "imx-uart", diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index a0731773ce75c..58a3ab030d670 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -915,7 +915,7 @@ MODULE_DEVICE_TABLE(of, ltq_asc_match); static struct platform_driver lqasc_driver = { .probe = lqasc_probe, - .remove_new = lqasc_remove, + .remove = lqasc_remove, .driver = { .name = DRVNAME, .of_match_table = ltq_asc_match, diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 3ce369f763498..6c13cf1ab646f 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -353,7 +353,7 @@ MODULE_DEVICE_TABLE(of, liteuart_of_match); static struct platform_driver liteuart_platform_driver = { .probe = liteuart_probe, - .remove_new = liteuart_remove, + .remove = liteuart_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = liteuart_of_match, diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 3e4ac46de1bc3..42c5f9bc18b7c 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -695,7 +695,7 @@ MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); static struct platform_driver serial_hs_lpc32xx_driver = { .probe = serial_hs_lpc32xx_probe, - .remove_new = serial_hs_lpc32xx_remove, + .remove = serial_hs_lpc32xx_remove, .suspend = serial_hs_lpc32xx_suspend, .resume = serial_hs_lpc32xx_resume, .driver = { diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 3b4206e815fe9..8dcad52eedfd1 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -794,7 +794,7 @@ static int ma35d1serial_resume(struct platform_device *dev) static struct platform_driver ma35d1serial_driver = { .probe = ma35d1serial_probe, - .remove_new = ma35d1serial_remove, + .remove = ma35d1serial_remove, .suspend = ma35d1serial_suspend, .resume = ma35d1serial_resume, .driver = { diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 58858dd352c59..93e7dda4d39ac 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -616,7 +616,7 @@ static void mcf_remove(struct platform_device *pdev) static struct platform_driver mcf_platform_driver = { .probe = mcf_probe, - .remove_new = mcf_remove, + .remove = mcf_remove, .driver = { .name = "mcfuart", }, diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 8eb586ac3b0da..a6cb2a535f9dc 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -842,7 +842,7 @@ MODULE_DEVICE_TABLE(of, meson_uart_dt_match); static struct platform_driver meson_uart_platform_driver = { .probe = meson_uart_probe, - .remove_new = meson_uart_remove, + .remove = meson_uart_remove, .driver = { .name = "meson_uart", .of_match_table = meson_uart_dt_match, diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index fb082ee73d5b2..059bea18dbab5 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -570,7 +570,7 @@ MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids); static struct platform_driver mlb_usio_driver = { .probe = mlb_usio_probe, - .remove_new = mlb_usio_remove, + .remove = mlb_usio_remove, .driver = { .name = USIO_NAME, .of_match_table = mlb_usio_dt_ids, diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 95dae5e27b286..f55aa353aed9e 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1843,7 +1843,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); static struct platform_driver mpc52xx_uart_of_driver = { .probe = mpc52xx_uart_of_probe, - .remove_new = mpc52xx_uart_of_remove, + .remove = mpc52xx_uart_of_remove, #ifdef CONFIG_PM .suspend = mpc52xx_uart_of_suspend, .resume = mpc52xx_uart_of_resume, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 0a9c5219df88b..1b137e0684442 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1894,7 +1894,7 @@ static const struct dev_pm_ops msm_serial_dev_pm_ops = { }; static struct platform_driver msm_platform_driver = { - .remove_new = msm_serial_remove, + .remove = msm_serial_remove, .probe = msm_serial_probe, .driver = { .name = "msm_serial", diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index a1c76565c3991..cc65c9fb6446c 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1704,7 +1704,7 @@ static void mxs_auart_remove(struct platform_device *pdev) static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, - .remove_new = mxs_auart_remove, + .remove = mxs_auart_remove, .driver = { .name = "mxs-auart", .of_match_table = mxs_auart_dt_ids, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d7e172eeaab15..0b85f47ff19e0 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1802,7 +1802,7 @@ MODULE_DEVICE_TABLE(of, omap_serial_of_match); static struct platform_driver serial_omap_driver = { .probe = serial_omap_probe, - .remove_new = serial_omap_remove, + .remove = serial_omap_remove, .driver = { .name = OMAP_SERIAL_DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index ecec483d4d59d..0542882cfbbe4 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -730,7 +730,7 @@ static void owl_uart_remove(struct platform_device *pdev) static struct platform_driver owl_uart_platform_driver = { .probe = owl_uart_probe, - .remove_new = owl_uart_remove, + .remove = owl_uart_remove, .driver = { .name = "owl-uart", .of_match_table = owl_uart_dt_matches, diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index 261c8115a700c..14d50bd7f1bd3 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -956,7 +956,7 @@ MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids); static struct platform_driver pic32_uart_platform_driver = { .probe = pic32_uart_probe, - .remove_new = pic32_uart_remove, + .remove = pic32_uart_remove, .driver = { .name = PIC32_DEV_NAME, .of_match_table = of_match_ptr(pic32_serial_dt_ids), diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 8969b11cc0a9a..e3a9193286954 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1776,7 +1776,7 @@ static struct macio_driver pmz_driver = { static struct platform_driver pmz_driver = { .probe = pmz_attach, - .remove_new = pmz_detach, + .remove = pmz_detach, .driver = { .name = "scc", }, diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 5dfe4e599ad68..a80ce7aaf309d 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1839,7 +1839,7 @@ static const struct of_device_id qcom_geni_serial_match_table[] = { MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); static struct platform_driver qcom_geni_serial_platform_driver = { - .remove_new = qcom_geni_serial_remove, + .remove = qcom_geni_serial_remove, .probe = qcom_geni_serial_probe, .driver = { .name = "qcom_geni_serial", diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 663e35e424bdb..87fa30d686878 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -777,7 +777,7 @@ static void rda_uart_remove(struct platform_device *pdev) static struct platform_driver rda_uart_platform_driver = { .probe = rda_uart_probe, - .remove_new = rda_uart_remove, + .remove = rda_uart_remove, .driver = { .name = "rda-uart", .of_match_table = rda_uart_dt_matches, diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 8bab2aedc4991..6d99a02dd439d 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -698,7 +698,6 @@ static int rp2_probe(struct pci_dev *pdev, const struct firmware *fw; struct rp2_card *card; struct rp2_uart_port *ports; - void __iomem * const *bars; int rc; card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); @@ -711,13 +710,16 @@ static int rp2_probe(struct pci_dev *pdev, if (rc) return rc; - rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME); + rc = pcim_request_all_regions(pdev, DRV_NAME); if (rc) return rc; - bars = pcim_iomap_table(pdev); - card->bar0 = bars[0]; - card->bar1 = bars[1]; + card->bar0 = pcim_iomap(pdev, 0, 0); + if (!card->bar0) + return -ENOMEM; + card->bar1 = pcim_iomap(pdev, 1, 0); + if (!card->bar1) + return -ENOMEM; card->pdev = pdev; rp2_decode_cap(id, &card->n_ports, &card->smpte); diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 79c794fa65451..3c34027687d21 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -880,7 +880,7 @@ static void sa1100_serial_remove(struct platform_device *pdev) static struct platform_driver sa11x0_serial_driver = { .probe = sa1100_serial_probe, - .remove_new = sa1100_serial_remove, + .remove = sa1100_serial_remove, .suspend = sa1100_serial_suspend, .resume = sa1100_serial_resume, .driver = { diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 0d184ee2f9cec..210fff7164c13 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -2498,6 +2498,12 @@ static const struct s3c24xx_serial_drv_data exynos850_serial_drv_data = { .fifosize = { 256, 64, 64, 64 }, }; +static const struct s3c24xx_serial_drv_data exynos8895_serial_drv_data = { + EXYNOS_COMMON_SERIAL_DRV_DATA, + /* samsung,uart-fifosize must be specified in the device tree. */ + .fifosize = { 0 }, +}; + static const struct s3c24xx_serial_drv_data gs101_serial_drv_data = { .info = { .name = "Google GS101 UART", @@ -2528,12 +2534,14 @@ static const struct s3c24xx_serial_drv_data gs101_serial_drv_data = { #define EXYNOS4210_SERIAL_DRV_DATA (&exynos4210_serial_drv_data) #define EXYNOS5433_SERIAL_DRV_DATA (&exynos5433_serial_drv_data) #define EXYNOS850_SERIAL_DRV_DATA (&exynos850_serial_drv_data) +#define EXYNOS8895_SERIAL_DRV_DATA (&exynos8895_serial_drv_data) #define GS101_SERIAL_DRV_DATA (&gs101_serial_drv_data) #else #define EXYNOS4210_SERIAL_DRV_DATA NULL #define EXYNOS5433_SERIAL_DRV_DATA NULL #define EXYNOS850_SERIAL_DRV_DATA NULL +#define EXYNOS8895_SERIAL_DRV_DATA NULL #define GS101_SERIAL_DRV_DATA NULL #endif @@ -2623,6 +2631,9 @@ static const struct platform_device_id s3c24xx_serial_driver_ids[] = { }, { .name = "gs101-uart", .driver_data = (kernel_ulong_t)GS101_SERIAL_DRV_DATA, + }, { + .name = "exynos8895-uart", + .driver_data = (kernel_ulong_t)EXYNOS8895_SERIAL_DRV_DATA, }, { }, }; @@ -2646,6 +2657,8 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { .data = ARTPEC8_SERIAL_DRV_DATA }, { .compatible = "google,gs101-uart", .data = GS101_SERIAL_DRV_DATA }, + { .compatible = "samsung,exynos8895-uart", + .data = EXYNOS8895_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); @@ -2653,7 +2666,7 @@ MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); static struct platform_driver samsung_serial_driver = { .probe = s3c24xx_serial_probe, - .remove_new = s3c24xx_serial_remove, + .remove = s3c24xx_serial_remove, .id_table = s3c24xx_serial_driver_ids, .driver = { .name = "samsung-uart", diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index ad88a33a504f5..9d0c971e49f59 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1473,7 +1473,7 @@ static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s, } static const struct serial_rs485 sc16is7xx_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, .delay_rts_before_send = 1, .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ }; diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 6d1d142fd2161..4c851dae6624f 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -1052,7 +1052,7 @@ static struct platform_driver sccnxp_uart_driver = { .name = SCCNXP_NAME, }, .probe = sccnxp_probe, - .remove_new = sccnxp_remove, + .remove = sccnxp_remove, .id_table = sccnxp_id_table, }; module_platform_driver(sccnxp_uart_driver); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 1183ca54ab921..8004fc00fb9cd 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1648,7 +1648,7 @@ static const struct dev_pm_ops tegra_uart_pm_ops = { static struct platform_driver tegra_uart_platform_driver = { .probe = tegra_uart_probe, - .remove_new = tegra_uart_remove, + .remove = tegra_uart_remove, .driver = { .name = "serial-tegra", .of_match_table = tegra_uart_of_match, diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index abba397229581..436a559234dfe 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1097,7 +1097,7 @@ static int serial_txx9_resume(struct platform_device *dev) static struct platform_driver serial_txx9_plat_driver = { .probe = serial_txx9_probe, - .remove_new = serial_txx9_remove, + .remove = serial_txx9_remove, #ifdef CONFIG_PM .suspend = serial_txx9_suspend, .resume = serial_txx9_resume, diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index b80e9a528e17f..df523c7444230 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3505,7 +3505,7 @@ static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume); static struct platform_driver sci_driver = { .probe = sci_probe, - .remove_new = sci_remove, + .remove = sci_remove, .driver = { .name = "sh-sci", .pm = &sci_dev_pm_ops, diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index cbfce65c9d221..5904a2d4cefa7 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -1040,7 +1040,7 @@ MODULE_DEVICE_TABLE(of, sifive_serial_of_match); static struct platform_driver sifive_serial_platform_driver = { .probe = sifive_serial_probe, - .remove_new = sifive_serial_remove, + .remove = sifive_serial_remove, .driver = { .name = SIFIVE_SERIAL_NAME, .pm = pm_sleep_ptr(&sifive_uart_pm_ops), diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 3fc54cc02a1fc..8c9366321f8e7 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -53,10 +53,12 @@ #define SPRD_IEN_TX_EMPTY BIT(1) #define SPRD_IEN_BREAK_DETECT BIT(7) #define SPRD_IEN_TIMEOUT BIT(13) +#define SPRD_IEN_DATA_TIMEOUT BIT(17) /* interrupt clear register */ #define SPRD_ICLR 0x0014 #define SPRD_ICLR_TIMEOUT BIT(13) +#define SPRD_ICLR_DATA_TIMEOUT BIT(17) /* line control register */ #define SPRD_LCR 0x0018 @@ -102,6 +104,7 @@ #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) #define SPRD_IMSR_BREAK_DETECT BIT(7) #define SPRD_IMSR_TIMEOUT BIT(13) +#define SPRD_IMSR_DATA_TIMEOUT BIT(17) #define SPRD_DEFAULT_SOURCE_CLK 26000000 #define SPRD_RX_DMA_STEP 1 @@ -118,6 +121,12 @@ struct sprd_uart_dma { bool enable; }; +struct sprd_uart_data { + unsigned int timeout_ien; + unsigned int timeout_iclr; + unsigned int timeout_imsr; +}; + struct sprd_uart_port { struct uart_port port; char name[16]; @@ -126,6 +135,7 @@ struct sprd_uart_port { struct sprd_uart_dma rx_dma; dma_addr_t pos; unsigned char *rx_buf_tail; + const struct sprd_uart_data *pdata; }; static struct sprd_uart_port *sprd_port[UART_NR_MAX]; @@ -134,6 +144,18 @@ static int sprd_ports_num; static int sprd_start_dma_rx(struct uart_port *port); static int sprd_tx_dma_config(struct uart_port *port); +static const struct sprd_uart_data sc9836_data = { + .timeout_ien = SPRD_IEN_TIMEOUT, + .timeout_iclr = SPRD_ICLR_TIMEOUT, + .timeout_imsr = SPRD_IMSR_TIMEOUT, +}; + +static const struct sprd_uart_data sc9632_data = { + .timeout_ien = SPRD_IEN_DATA_TIMEOUT, + .timeout_iclr = SPRD_ICLR_DATA_TIMEOUT, + .timeout_imsr = SPRD_IMSR_DATA_TIMEOUT, +}; + static inline unsigned int serial_in(struct uart_port *port, unsigned int offset) { @@ -637,6 +659,8 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; unsigned int ims; + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); uart_port_lock(port); @@ -647,14 +671,14 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) return IRQ_NONE; } - if (ims & SPRD_IMSR_TIMEOUT) - serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT); + if (ims & sp->pdata->timeout_imsr) + serial_out(port, SPRD_ICLR, sp->pdata->timeout_iclr); if (ims & SPRD_IMSR_BREAK_DETECT) serial_out(port, SPRD_ICLR, SPRD_IMSR_BREAK_DETECT); if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT | - SPRD_IMSR_TIMEOUT)) + sp->pdata->timeout_imsr)) sprd_rx(port); if (ims & SPRD_IMSR_TX_FIFO_EMPTY) @@ -729,7 +753,7 @@ static int sprd_startup(struct uart_port *port) /* enable interrupt */ uart_port_lock_irqsave(port, &flags); ien = serial_in(port, SPRD_IEN); - ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + ien |= SPRD_IEN_BREAK_DETECT | sp->pdata->timeout_ien; if (!sp->rx_dma.enable) ien |= SPRD_IEN_RX_FULL; serial_out(port, SPRD_IEN, ien); @@ -1184,6 +1208,12 @@ static int sprd_probe(struct platform_device *pdev) up->mapbase = res->start; + sport->pdata = of_device_get_match_data(&pdev->dev); + if (!sport->pdata) { + dev_err(&pdev->dev, "get match data failed!\n"); + return -EINVAL; + } + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -1248,14 +1278,15 @@ static int sprd_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume); static const struct of_device_id serial_ids[] = { - {.compatible = "sprd,sc9836-uart",}, + {.compatible = "sprd,sc9836-uart", .data = &sc9836_data}, + {.compatible = "sprd,sc9632-uart", .data = &sc9632_data}, {} }; MODULE_DEVICE_TABLE(of, serial_ids); static struct platform_driver sprd_platform_driver = { .probe = sprd_probe, - .remove_new = sprd_remove, + .remove = sprd_remove, .driver = { .name = "sprd_serial", .of_match_table = serial_ids, diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 8aea59f8ca13d..6ed9a327702bb 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -934,7 +934,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(asc_serial_pm_ops, asc_serial_suspend, static struct platform_driver asc_serial_driver = { .probe = asc_serial_probe, - .remove_new = asc_serial_remove, + .remove = asc_serial_remove, .driver = { .name = DRIVER_NAME, .pm = pm_sleep_ptr(&asc_serial_pm_ops), diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e1e7bc04c5792..7dc2545460751 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -2188,7 +2188,7 @@ static const struct dev_pm_ops stm32_serial_pm_ops = { static struct platform_driver stm32_serial_driver = { .probe = stm32_usart_serial_probe, - .remove_new = stm32_usart_serial_remove, + .remove = stm32_usart_serial_remove, .driver = { .name = DRIVER_NAME, .pm = &stm32_serial_pm_ops, diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 7f60679fdde19..2b3ec65d595d8 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -633,7 +633,7 @@ static struct platform_driver hv_driver = { .of_match_table = hv_match, }, .probe = hv_probe, - .remove_new = hv_remove, + .remove = hv_remove, }; static int __init sunhv_init(void) diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c index abf7c449308d9..38deee571b0d0 100644 --- a/drivers/tty/serial/sunplus-uart.c +++ b/drivers/tty/serial/sunplus-uart.c @@ -697,7 +697,7 @@ MODULE_DEVICE_TABLE(of, sp_uart_of_match); static struct platform_driver sunplus_uart_platform_driver = { .probe = sunplus_uart_probe, - .remove_new = sunplus_uart_remove, + .remove = sunplus_uart_remove, .driver = { .name = "sunplus_uart", .of_match_table = sp_uart_of_match, diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 1acbe2fba746f..df906ccf2e8a4 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -1100,7 +1100,7 @@ static struct platform_driver sab_driver = { .of_match_table = sab_match, }, .probe = sab_probe, - .remove_new = sab_remove, + .remove = sab_remove, }; static int __init sunsab_init(void) diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 0f463da5e7ce6..7f0fef07e141d 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1549,7 +1549,7 @@ static struct platform_driver su_driver = { .of_match_table = su_match, }, .probe = su_probe, - .remove_new = su_remove, + .remove = su_remove, }; static int __init sunsu_init(void) diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 71758ad4241c1..0551c24c06f5f 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1538,7 +1538,7 @@ static struct platform_driver zs_driver = { .of_match_table = zs_match, }, .probe = zs_probe, - .remove_new = zs_remove, + .remove = zs_remove, }; static int __init sunzilog_init(void) diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c index 21ca5fcadf499..7033dbfe8ba1e 100644 --- a/drivers/tty/serial/tegra-tcu.c +++ b/drivers/tty/serial/tegra-tcu.c @@ -293,7 +293,7 @@ static struct platform_driver tegra_tcu_driver = { .of_match_table = tegra_tcu_match, }, .probe = tegra_tcu_probe, - .remove_new = tegra_tcu_remove, + .remove = tegra_tcu_remove, }; module_platform_driver(tegra_tcu_driver); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 43fa0938b5e33..6fa93c3872a72 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -485,7 +485,7 @@ static struct platform_driver timbuart_platform_driver = { .name = "timb-uart", }, .probe = timbuart_probe, - .remove_new = timbuart_remove, + .remove = timbuart_remove, }; module_platform_driver(timbuart_platform_driver); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 68357ac8ffe3c..a41e7fc373b7c 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -915,7 +915,7 @@ MODULE_ALIAS("platform:uartlite"); static struct platform_driver ulite_platform_driver = { .probe = ulite_probe, - .remove_new = ulite_remove, + .remove = ulite_remove, .driver = { .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 4eed909468ff6..0613f8c11ab11 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1484,7 +1484,7 @@ static struct platform_driver ucc_uart_of_driver = { .of_match_table = ucc_uart_match, }, .probe = ucc_uart_probe, - .remove_new = ucc_uart_remove, + .remove = ucc_uart_remove, }; static int __init ucc_uart_init(void) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 777392914819d..beb151be4d328 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1903,7 +1903,7 @@ static void cdns_uart_remove(struct platform_device *pdev) static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, - .remove_new = cdns_uart_remove, + .remove = cdns_uart_remove, .driver = { .name = CDNS_UART_NAME, .of_match_table = cdns_uart_of_match, diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 9771072da177c..dcb1769c3625c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3631,7 +3631,7 @@ static struct ctl_table tty_table[] = { .data = &tty_ldisc_autoload, .maxlen = sizeof(tty_ldisc_autoload), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index dba935c712d64..240ce135bbfbc 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -417,13 +417,6 @@ void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba) } EXPORT_SYMBOL_GPL(ufshcd_mcq_make_queues_operational); -void ufshcd_mcq_enable_esi(struct ufs_hba *hba) -{ - ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_MEM_CFG) | 0x2, - REG_UFS_MEM_CFG); -} -EXPORT_SYMBOL_GPL(ufshcd_mcq_enable_esi); - void ufshcd_mcq_enable(struct ufs_hba *hba) { ufshcd_rmwl(hba, MCQ_MODE_SELECT, MCQ_MODE_SELECT, REG_UFS_MEM_CFG); @@ -437,6 +430,13 @@ void ufshcd_mcq_disable(struct ufs_hba *hba) hba->mcq_enabled = false; } +void ufshcd_mcq_enable_esi(struct ufs_hba *hba) +{ + ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_MEM_CFG) | 0x2, + REG_UFS_MEM_CFG); +} +EXPORT_SYMBOL_GPL(ufshcd_mcq_enable_esi); + void ufshcd_mcq_config_esi(struct ufs_hba *hba, struct msi_msg *msg) { ufshcd_writel(hba, msg->address_lo, REG_UFS_ESILBA); @@ -539,7 +539,7 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag) struct scsi_cmnd *cmd = lrbp->cmd; struct ufs_hw_queue *hwq; void __iomem *reg, *opr_sqd_base; - u32 nexus, id, val, rtc; + u32 nexus, id, val; int err; if (hba->quirks & UFSHCD_QUIRK_MCQ_BROKEN_RTC) @@ -573,14 +573,18 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag) writel(readl(opr_sqd_base + REG_SQRTC) | SQ_ICU, opr_sqd_base + REG_SQRTC); - /* Poll SQRTSy.CUS = 1. Return result from SQRTSy.RTC */ + /* Wait until SQRTSy.CUS = 1. Report SQRTSy.RTC. */ reg = opr_sqd_base + REG_SQRTS; err = read_poll_timeout(readl, val, val & SQ_CUS, 20, MCQ_POLL_US, false, reg); - rtc = FIELD_GET(SQ_ICU_ERR_CODE_MASK, readl(reg)); - if (err || rtc) - dev_err(hba->dev, "%s: failed. hwq=%d, tag=%d err=%d RTC=%d\n", - __func__, id, task_tag, err, rtc); + if (err) + dev_err(hba->dev, "%s: failed. hwq=%d, tag=%d err=%d\n", + __func__, id, task_tag, err); + else + dev_info(hba->dev, + "%s, hwq %d: cleanup return code (RTC) %ld\n", + __func__, id, + FIELD_GET(SQ_ICU_ERR_CODE_MASK, readl(reg))); if (ufshcd_mcq_sq_start(hba, hwq)) err = -ETIMEDOUT; diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index abbe7135a9778..6a26853330763 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -298,6 +298,7 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba); static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd); static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag); static void ufshcd_hba_exit(struct ufs_hba *hba); +static int ufshcd_device_init(struct ufs_hba *hba, bool init_dev_params); static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params); static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba); @@ -349,18 +350,6 @@ static void ufshcd_configure_wb(struct ufs_hba *hba) ufshcd_wb_toggle_buf_flush(hba, true); } -static void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) -{ - if (atomic_dec_and_test(&hba->scsi_block_reqs_cnt)) - scsi_unblock_requests(hba->host); -} - -static void ufshcd_scsi_block_requests(struct ufs_hba *hba) -{ - if (atomic_inc_return(&hba->scsi_block_reqs_cnt) == 1) - scsi_block_requests(hba->host); -} - static void ufshcd_add_cmd_upiu_trace(struct ufs_hba *hba, unsigned int tag, enum ufs_trace_str_t str_t) { @@ -739,25 +728,15 @@ EXPORT_SYMBOL_GPL(ufshcd_delay_us); * Return: -ETIMEDOUT on error, zero on success. */ static int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, - u32 val, unsigned long interval_us, - unsigned long timeout_ms) + u32 val, unsigned long interval_us, + unsigned long timeout_ms) { - int err = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); - - /* ignore bits that we don't intend to wait on */ - val = val & mask; + u32 v; - while ((ufshcd_readl(hba, reg) & mask) != val) { - usleep_range(interval_us, interval_us + 50); - if (time_after(jiffies, timeout)) { - if ((ufshcd_readl(hba, reg) & mask) != val) - err = -ETIMEDOUT; - break; - } - } + val &= mask; /* ignore bits that we don't intend to wait on */ - return err; + return read_poll_timeout(ufshcd_readl, v, (v & mask) == val, + interval_us, timeout_ms * 1000, false, hba, reg); } /** @@ -1255,11 +1234,13 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba, static u32 ufshcd_pending_cmds(struct ufs_hba *hba) { const struct scsi_device *sdev; + unsigned long flags; u32 pending = 0; - lockdep_assert_held(hba->host->host_lock); + spin_lock_irqsave(hba->host->host_lock, flags); __shost_for_each_device(sdev, hba->host) pending += sbitmap_weight(&sdev->budget_map); + spin_unlock_irqrestore(hba->host->host_lock, flags); return pending; } @@ -1273,7 +1254,6 @@ static u32 ufshcd_pending_cmds(struct ufs_hba *hba) static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us) { - unsigned long flags; int ret = 0; u32 tm_doorbell; u32 tr_pending; @@ -1281,7 +1261,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, ktime_t start; ufshcd_hold(hba); - spin_lock_irqsave(hba->host->host_lock, flags); /* * Wait for all the outstanding tasks/transfer requests. * Verify by checking the doorbell registers are clear. @@ -1302,7 +1281,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, break; } - spin_unlock_irqrestore(hba->host->host_lock, flags); io_schedule_timeout(msecs_to_jiffies(20)); if (ktime_to_us(ktime_sub(ktime_get(), start)) > wait_timeout_us) { @@ -1314,7 +1292,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, */ do_last_check = true; } - spin_lock_irqsave(hba->host->host_lock, flags); } while (tm_doorbell || tr_pending); if (timeout) { @@ -1324,7 +1301,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, ret = -EBUSY; } out: - spin_unlock_irqrestore(hba->host->host_lock, flags); ufshcd_release(hba); return ret; } @@ -2411,8 +2387,6 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba) int err; hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); - if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) - hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT; /* nutrs and nutmrs are 0 based values */ hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS_SDB) + 1; @@ -2551,13 +2525,11 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) * __ufshcd_send_uic_cmd - Send UIC commands and retrieve the result * @hba: per adapter instance * @uic_cmd: UIC command - * @completion: initialize the completion only if this is set to true * * Return: 0 only if success. */ static int -__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, - bool completion) +__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { lockdep_assert_held(&hba->uic_cmd_mutex); @@ -2567,8 +2539,7 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, return -EIO; } - if (completion) - init_completion(&uic_cmd->done); + init_completion(&uic_cmd->done); uic_cmd->cmd_active = 1; ufshcd_dispatch_uic_cmd(hba, uic_cmd); @@ -2594,7 +2565,7 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) mutex_lock(&hba->uic_cmd_mutex); ufshcd_add_delay_before_dme_cmd(hba); - ret = __ufshcd_send_uic_cmd(hba, uic_cmd, true); + ret = __ufshcd_send_uic_cmd(hba, uic_cmd); if (!ret) ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); @@ -2775,7 +2746,6 @@ void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u8 upiu_flags) ucd_req_ptr->sc.exp_data_transfer_len = cpu_to_be32(cmd->sdb.length); cdb_len = min_t(unsigned short, cmd->cmd_len, UFS_CDB_SIZE); - memset(ucd_req_ptr->sc.cdb, 0, UFS_CDB_SIZE); memcpy(ucd_req_ptr->sc.cdb, cmd->cmnd, cdb_len); memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); @@ -2878,6 +2848,26 @@ static void ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags); } +static void __ufshcd_setup_cmd(struct ufshcd_lrb *lrbp, struct scsi_cmnd *cmd, u8 lun, int tag) +{ + memset(lrbp->ucd_req_ptr, 0, sizeof(*lrbp->ucd_req_ptr)); + + lrbp->cmd = cmd; + lrbp->task_tag = tag; + lrbp->lun = lun; + ufshcd_prepare_lrbp_crypto(cmd ? scsi_cmd_to_rq(cmd) : NULL, lrbp); +} + +static void ufshcd_setup_scsi_cmd(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, + struct scsi_cmnd *cmd, u8 lun, int tag) +{ + __ufshcd_setup_cmd(lrbp, cmd, lun, tag); + lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba); + lrbp->req_abort_skip = false; + + ufshcd_comp_scsi_upiu(hba, lrbp); +} + /** * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID * @upiu_wlun_id: UPIU W-LUN id @@ -3010,16 +3000,8 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) ufshcd_hold(hba); lrbp = &hba->lrb[tag]; - lrbp->cmd = cmd; - lrbp->task_tag = tag; - lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun); - lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba); - ufshcd_prepare_lrbp_crypto(scsi_cmd_to_rq(cmd), lrbp); - - lrbp->req_abort_skip = false; - - ufshcd_comp_scsi_upiu(hba, lrbp); + ufshcd_setup_scsi_cmd(hba, lrbp, cmd, ufshcd_scsi_to_upiu_lun(cmd->device->lun), tag); err = ufshcd_map_sg(hba, lrbp); if (err) { @@ -3047,11 +3029,8 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) static void ufshcd_setup_dev_cmd(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, enum dev_cmd_type cmd_type, u8 lun, int tag) { - lrbp->cmd = NULL; - lrbp->task_tag = tag; - lrbp->lun = lun; + __ufshcd_setup_cmd(lrbp, NULL, lun, tag); lrbp->intr_cmd = true; /* No interrupt aggregation */ - ufshcd_prepare_lrbp_crypto(NULL, lrbp); hba->dev_cmd.type = cmd_type; } @@ -3083,7 +3062,6 @@ bool ufshcd_cmd_inflight(struct scsi_cmnd *cmd) static int ufshcd_clear_cmd(struct ufs_hba *hba, u32 task_tag) { u32 mask; - unsigned long flags; int err; if (hba->mcq_enabled) { @@ -3103,9 +3081,7 @@ static int ufshcd_clear_cmd(struct ufs_hba *hba, u32 task_tag) mask = 1U << task_tag; /* clear outstanding transaction before retry */ - spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_utrl_clear(hba, mask); - spin_unlock_irqrestore(hba->host->host_lock, flags); /* * wait for h/w to clear corresponding bit in door-bell. @@ -4288,7 +4264,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) reenable_intr = true; } spin_unlock_irqrestore(hba->host->host_lock, flags); - ret = __ufshcd_send_uic_cmd(hba, cmd, false); + ret = __ufshcd_send_uic_cmd(hba, cmd); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", @@ -4539,6 +4515,14 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) return -EINVAL; } + if (pwr_info->lane_rx != pwr_info->lane_tx) { + dev_err(hba->dev, "%s: asymmetric connected lanes. rx=%d, tx=%d\n", + __func__, + pwr_info->lane_rx, + pwr_info->lane_tx); + return -EINVAL; + } + /* * First, get the maximum gears of HS speed. * If a zero value, it means there is no HSGEAR capability. @@ -4822,51 +4806,44 @@ EXPORT_SYMBOL_GPL(ufshcd_hba_stop); */ static int ufshcd_hba_execute_hce(struct ufs_hba *hba) { - int retry_outer = 3; - int retry_inner; + int retry; -start: - if (ufshcd_is_hba_active(hba)) - /* change controller state to "reset state" */ - ufshcd_hba_stop(hba); + for (retry = 3; retry > 0; retry--) { + if (ufshcd_is_hba_active(hba)) + /* change controller state to "reset state" */ + ufshcd_hba_stop(hba); - /* UniPro link is disabled at this point */ - ufshcd_set_link_off(hba); + /* UniPro link is disabled at this point */ + ufshcd_set_link_off(hba); - ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE); + ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE); - /* start controller initialization sequence */ - ufshcd_hba_start(hba); + /* start controller initialization sequence */ + ufshcd_hba_start(hba); - /* - * To initialize a UFS host controller HCE bit must be set to 1. - * During initialization the HCE bit value changes from 1->0->1. - * When the host controller completes initialization sequence - * it sets the value of HCE bit to 1. The same HCE bit is read back - * to check if the controller has completed initialization sequence. - * So without this delay the value HCE = 1, set in the previous - * instruction might be read back. - * This delay can be changed based on the controller. - */ - ufshcd_delay_us(hba->vps->hba_enable_delay_us, 100); + /* + * To initialize a UFS host controller HCE bit must be set to 1. + * During initialization the HCE bit value changes from 1->0->1. + * When the host controller completes initialization sequence + * it sets the value of HCE bit to 1. The same HCE bit is read back + * to check if the controller has completed initialization sequence. + * So without this delay the value HCE = 1, set in the previous + * instruction might be read back. + * This delay can be changed based on the controller. + */ + ufshcd_delay_us(hba->vps->hba_enable_delay_us, 100); - /* wait for the host controller to complete initialization */ - retry_inner = 50; - while (!ufshcd_is_hba_active(hba)) { - if (retry_inner) { - retry_inner--; - } else { - dev_err(hba->dev, - "Controller enable failed\n"); - if (retry_outer) { - retry_outer--; - goto start; - } - return -EIO; - } - usleep_range(1000, 1100); + /* wait for the host controller to complete initialization */ + if (!ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE, CONTROLLER_ENABLE, + CONTROLLER_ENABLE, 1000, 50)) + break; + + dev_err(hba->dev, "Enabling the controller failed\n"); } + if (!retry) + return -EIO; + /* enable UIC related interrupts */ ufshcd_enable_intr(hba, UFSHCD_UIC_MASK); @@ -5258,6 +5235,9 @@ static int ufshcd_device_configure(struct scsi_device *sdev, */ sdev->silence_suspend = 1; + if (hba->vops && hba->vops->config_scsi_dev) + hba->vops->config_scsi_dev(sdev); + ufshcd_crypto_register(hba, q); return 0; @@ -5478,32 +5458,37 @@ static bool ufshcd_is_auto_hibern8_error(struct ufs_hba *hba, static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) { irqreturn_t retval = IRQ_NONE; + struct uic_command *cmd; spin_lock(hba->host->host_lock); + cmd = hba->active_uic_cmd; + if (WARN_ON_ONCE(!cmd)) + goto unlock; + if (ufshcd_is_auto_hibern8_error(hba, intr_status)) hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status); - if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) { - hba->active_uic_cmd->argument2 |= - ufshcd_get_uic_cmd_result(hba); - hba->active_uic_cmd->argument3 = - ufshcd_get_dme_attr_val(hba); + if (intr_status & UIC_COMMAND_COMPL) { + cmd->argument2 |= ufshcd_get_uic_cmd_result(hba); + cmd->argument3 = ufshcd_get_dme_attr_val(hba); if (!hba->uic_async_done) - hba->active_uic_cmd->cmd_active = 0; - complete(&hba->active_uic_cmd->done); + cmd->cmd_active = 0; + complete(&cmd->done); retval = IRQ_HANDLED; } - if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) { - hba->active_uic_cmd->cmd_active = 0; + if (intr_status & UFSHCD_UIC_PWR_MASK && hba->uic_async_done) { + cmd->cmd_active = 0; complete(hba->uic_async_done); retval = IRQ_HANDLED; } if (retval == IRQ_HANDLED) - ufshcd_add_uic_command_trace(hba, hba->active_uic_cmd, - UFS_CMD_COMP); + ufshcd_add_uic_command_trace(hba, cmd, UFS_CMD_COMP); + +unlock: spin_unlock(hba->host->host_lock); + return retval; } @@ -6196,12 +6181,11 @@ static void ufshcd_exception_event_handler(struct work_struct *work) u32 status = 0; hba = container_of(work, struct ufs_hba, eeh_work); - ufshcd_scsi_block_requests(hba); err = ufshcd_get_ee_status(hba, &status); if (err) { dev_err(hba->dev, "%s: failed to get exception status %d\n", __func__, err); - goto out; + return; } trace_ufshcd_exception_event(dev_name(hba->dev), status); @@ -6213,8 +6197,6 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ufshcd_temp_exception_event_handler(hba, status); ufs_debugfs_exception_event(hba, status); -out: - ufshcd_scsi_unblock_requests(hba); } /* Complete requests that have door-bell cleared */ @@ -6380,15 +6362,14 @@ static void ufshcd_err_handling_prepare(struct ufs_hba *hba) ufshcd_suspend_clkscaling(hba); ufshcd_clk_scaling_allow(hba, false); } - ufshcd_scsi_block_requests(hba); /* Wait for ongoing ufshcd_queuecommand() calls to finish. */ - blk_mq_wait_quiesce_done(&hba->host->tag_set); + blk_mq_quiesce_tagset(&hba->host->tag_set); cancel_work_sync(&hba->eeh_work); } static void ufshcd_err_handling_unprepare(struct ufs_hba *hba) { - ufshcd_scsi_unblock_requests(hba); + blk_mq_unquiesce_tagset(&hba->host->tag_set); ufshcd_release(hba); if (ufshcd_is_clkscaling_supported(hba)) ufshcd_clk_scaling_suspend(hba, false); @@ -7002,14 +6983,11 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) { int err = 0; u32 mask = 1 << tag; - unsigned long flags; if (!test_bit(tag, &hba->outstanding_tasks)) goto out; - spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_utmrl_clear(hba, tag); - spin_unlock_irqrestore(hba->host->host_lock, flags); /* poll for max. 1 sec to clear door bell register by h/w */ err = ufshcd_wait_for_register(hba, @@ -7052,12 +7030,13 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba, memcpy(hba->utmrdl_base_addr + task_tag, treq, sizeof(*treq)); ufshcd_vops_setup_task_mgmt(hba, task_tag, tm_function); - /* send command to the controller */ __set_bit(task_tag, &hba->outstanding_tasks); - ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL); spin_unlock_irqrestore(host->host_lock, flags); + /* send command to the controller */ + ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL); + ufshcd_add_tm_upiu_trace(hba, task_tag, UFS_TM_SEND); /* wait until the task management command is completed */ @@ -7473,10 +7452,9 @@ static void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap) int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) { struct ufshcd_lrb *lrbp = &hba->lrb[tag]; - int err = 0; + int err; int poll_cnt; u8 resp = 0xF; - u32 reg; for (poll_cnt = 100; poll_cnt; poll_cnt--) { err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, @@ -7491,46 +7469,27 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) * cmd not pending in the device, check if it is * in transition. */ - dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n", + dev_info( + hba->dev, + "%s: cmd with tag %d not pending in the device.\n", __func__, tag); - if (hba->mcq_enabled) { - /* MCQ mode */ - if (ufshcd_cmd_inflight(lrbp->cmd)) { - /* sleep for max. 200us same delay as in SDB mode */ - usleep_range(100, 200); - continue; - } - /* command completed already */ - dev_err(hba->dev, "%s: cmd at tag=%d is cleared.\n", - __func__, tag); - goto out; - } - - /* Single Doorbell Mode */ - reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); - if (reg & (1 << tag)) { - /* sleep for max. 200us to stabilize */ - usleep_range(100, 200); - continue; + if (!ufshcd_cmd_inflight(lrbp->cmd)) { + dev_info(hba->dev, + "%s: cmd with tag=%d completed.\n", + __func__, tag); + return 0; } - /* command completed already */ - dev_err(hba->dev, "%s: cmd at tag %d successfully cleared from DB.\n", - __func__, tag); - goto out; + usleep_range(100, 200); } else { dev_err(hba->dev, "%s: no response from device. tag = %d, err %d\n", __func__, tag, err); - if (!err) - err = resp; /* service response error */ - goto out; + return err ? : resp; } } - if (!poll_cnt) { - err = -EBUSY; - goto out; - } + if (!poll_cnt) + return -EBUSY; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); @@ -7540,7 +7499,7 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) dev_err(hba->dev, "%s: issued. tag = %d, err %d\n", __func__, tag, err); } - goto out; + return err; } err = ufshcd_clear_cmd(hba, tag); @@ -7548,7 +7507,6 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n", __func__, tag, err); -out: return err; } @@ -7671,6 +7629,29 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) return err; } +/** + * ufshcd_process_probe_result - Process the ufshcd_probe_hba() result. + * @hba: UFS host controller instance. + * @probe_start: time when the ufshcd_probe_hba() call started. + * @ret: ufshcd_probe_hba() return value. + */ +static void ufshcd_process_probe_result(struct ufs_hba *hba, + ktime_t probe_start, int ret) +{ + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + if (ret) + hba->ufshcd_state = UFSHCD_STATE_ERROR; + else if (hba->ufshcd_state == UFSHCD_STATE_RESET) + hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; + spin_unlock_irqrestore(hba->host->host_lock, flags); + + trace_ufshcd_init(dev_name(hba->dev), ret, + ktime_to_us(ktime_sub(ktime_get(), probe_start)), + hba->curr_dev_pwr_mode, hba->uic_link_state); +} + /** * ufshcd_host_reset_and_restore - reset and restore host controller * @hba: per-adapter instance @@ -7700,8 +7681,14 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) err = ufshcd_hba_enable(hba); /* Establish the link again and restore the device */ - if (!err) - err = ufshcd_probe_hba(hba, false); + if (!err) { + ktime_t probe_start = ktime_get(); + + err = ufshcd_device_init(hba, /*init_dev_params=*/false); + if (!err) + err = ufshcd_probe_hba(hba, false); + ufshcd_process_probe_result(hba, probe_start, err); + } if (err) dev_err(hba->dev, "%s: Host init failed %d\n", __func__, err); @@ -8727,10 +8714,43 @@ static void ufshcd_config_mcq(struct ufs_hba *hba) hba->nutrs); } +static int ufshcd_post_device_init(struct ufs_hba *hba) +{ + int ret; + + ufshcd_tune_unipro_params(hba); + + /* UFS device is also active now */ + ufshcd_set_ufs_dev_active(hba); + ufshcd_force_reset_auto_bkops(hba); + + ufshcd_set_timestamp_attr(hba); + + if (!hba->max_pwr_info.is_valid) + return 0; + + /* + * Set the right value to bRefClkFreq before attempting to + * switch to HS gears. + */ + if (hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL) + ufshcd_set_dev_ref_clk(hba); + /* Gear up to HS gear. */ + ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info); + if (ret) { + dev_err(hba->dev, "%s: Failed setting power mode, err = %d\n", + __func__, ret); + return ret; + } + + return 0; +} + static int ufshcd_device_init(struct ufs_hba *hba, bool init_dev_params) { int ret; - struct Scsi_Host *host = hba->host; + + WARN_ON_ONCE(!hba->scsi_host_added); hba->ufshcd_state = UFSHCD_STATE_RESET; @@ -8771,56 +8791,14 @@ static int ufshcd_device_init(struct ufs_hba *hba, bool init_dev_params) ret = ufshcd_device_params_init(hba); if (ret) return ret; - if (is_mcq_supported(hba) && !hba->scsi_host_added) { - ufshcd_mcq_enable(hba); - ret = ufshcd_alloc_mcq(hba); - if (!ret) { - ufshcd_config_mcq(hba); - } else { - /* Continue with SDB mode */ - ufshcd_mcq_disable(hba); - use_mcq_mode = false; - dev_err(hba->dev, "MCQ mode is disabled, err=%d\n", - ret); - } - ret = scsi_add_host(host, hba->dev); - if (ret) { - dev_err(hba->dev, "scsi_add_host failed\n"); - return ret; - } - hba->scsi_host_added = true; - } else if (is_mcq_supported(hba)) { - /* UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH is set */ + if (is_mcq_supported(hba) && + hba->quirks & UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH) { ufshcd_config_mcq(hba); ufshcd_mcq_enable(hba); } } - ufshcd_tune_unipro_params(hba); - - /* UFS device is also active now */ - ufshcd_set_ufs_dev_active(hba); - ufshcd_force_reset_auto_bkops(hba); - - ufshcd_set_timestamp_attr(hba); - - /* Gear up to HS gear if supported */ - if (hba->max_pwr_info.is_valid) { - /* - * Set the right value to bRefClkFreq before attempting to - * switch to HS gears. - */ - if (hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL) - ufshcd_set_dev_ref_clk(hba); - ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info); - if (ret) { - dev_err(hba->dev, "%s: Failed setting power mode, err = %d\n", - __func__, ret); - return ret; - } - } - - return 0; + return ufshcd_post_device_init(hba); } /** @@ -8834,14 +8812,8 @@ static int ufshcd_device_init(struct ufs_hba *hba, bool init_dev_params) */ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) { - ktime_t start = ktime_get(); - unsigned long flags; int ret; - ret = ufshcd_device_init(hba, init_dev_params); - if (ret) - goto out; - if (!hba->pm_op_in_progress && (hba->quirks & UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH)) { /* Reset the device and controller before doing reinit */ @@ -8854,13 +8826,13 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) dev_err(hba->dev, "Host controller enable failed\n"); ufshcd_print_evt_hist(hba); ufshcd_print_host_state(hba); - goto out; + return ret; } /* Reinit the device */ ret = ufshcd_device_init(hba, init_dev_params); if (ret) - goto out; + return ret; } ufshcd_print_pwr_info(hba); @@ -8880,18 +8852,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) ufshcd_write_ee_control(hba); ufshcd_configure_auto_hibern8(hba); -out: - spin_lock_irqsave(hba->host->host_lock, flags); - if (ret) - hba->ufshcd_state = UFSHCD_STATE_ERROR; - else if (hba->ufshcd_state == UFSHCD_STATE_RESET) - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - spin_unlock_irqrestore(hba->host->host_lock, flags); - - trace_ufshcd_init(dev_name(hba->dev), ret, - ktime_to_us(ktime_sub(ktime_get(), start)), - hba->curr_dev_pwr_mode, hba->uic_link_state); - return ret; + return 0; } /** @@ -8902,11 +8863,14 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) static void ufshcd_async_scan(void *data, async_cookie_t cookie) { struct ufs_hba *hba = (struct ufs_hba *)data; + ktime_t probe_start; int ret; down(&hba->host_sem); /* Initialize hba, detect and initialize UFS device */ + probe_start = ktime_get(); ret = ufshcd_probe_hba(hba, true); + ufshcd_process_probe_result(hba, probe_start, ret); up(&hba->host_sem); if (ret) goto out; @@ -10309,6 +10273,8 @@ EXPORT_SYMBOL_GPL(ufshcd_dealloc_host); */ static int ufshcd_set_dma_mask(struct ufs_hba *hba) { + if (hba->vops && hba->vops->set_dma_mask) + return hba->vops->set_dma_mask(hba); if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT) { if (!dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(64))) return 0; @@ -10372,6 +10338,74 @@ static const struct blk_mq_ops ufshcd_tmf_ops = { .queue_rq = ufshcd_queue_tmf, }; +static int ufshcd_add_scsi_host(struct ufs_hba *hba) +{ + int err; + + if (is_mcq_supported(hba)) { + ufshcd_mcq_enable(hba); + err = ufshcd_alloc_mcq(hba); + if (!err) { + ufshcd_config_mcq(hba); + } else { + /* Continue with SDB mode */ + ufshcd_mcq_disable(hba); + use_mcq_mode = false; + dev_err(hba->dev, "MCQ mode is disabled, err=%d\n", + err); + } + } + if (!is_mcq_supported(hba) && !hba->lsdb_sup) { + dev_err(hba->dev, + "%s: failed to initialize (legacy doorbell mode not supported)\n", + __func__); + return -EINVAL; + } + + err = scsi_add_host(hba->host, hba->dev); + if (err) { + dev_err(hba->dev, "scsi_add_host failed\n"); + return err; + } + hba->scsi_host_added = true; + + hba->tmf_tag_set = (struct blk_mq_tag_set) { + .nr_hw_queues = 1, + .queue_depth = hba->nutmrs, + .ops = &ufshcd_tmf_ops, + .flags = BLK_MQ_F_NO_SCHED, + }; + err = blk_mq_alloc_tag_set(&hba->tmf_tag_set); + if (err < 0) + goto remove_scsi_host; + hba->tmf_queue = blk_mq_alloc_queue(&hba->tmf_tag_set, NULL, NULL); + if (IS_ERR(hba->tmf_queue)) { + err = PTR_ERR(hba->tmf_queue); + goto free_tmf_tag_set; + } + hba->tmf_rqs = devm_kcalloc(hba->dev, hba->nutmrs, + sizeof(*hba->tmf_rqs), GFP_KERNEL); + if (!hba->tmf_rqs) { + err = -ENOMEM; + goto free_tmf_queue; + } + + return 0; + +free_tmf_queue: + blk_mq_destroy_queue(hba->tmf_queue); + blk_put_queue(hba->tmf_queue); + +free_tmf_tag_set: + blk_mq_free_tag_set(&hba->tmf_tag_set); + +remove_scsi_host: + if (hba->scsi_host_added) + scsi_remove_host(hba->host); + + return err; +} + /** * ufshcd_init - Driver initialization routine * @hba: per-adapter instance @@ -10503,42 +10537,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) hba->is_irq_enabled = true; } - if (!is_mcq_supported(hba)) { - if (!hba->lsdb_sup) { - dev_err(hba->dev, "%s: failed to initialize (legacy doorbell mode not supported)\n", - __func__); - err = -EINVAL; - goto out_disable; - } - err = scsi_add_host(host, hba->dev); - if (err) { - dev_err(hba->dev, "scsi_add_host failed\n"); - goto out_disable; - } - hba->scsi_host_added = true; - } - - hba->tmf_tag_set = (struct blk_mq_tag_set) { - .nr_hw_queues = 1, - .queue_depth = hba->nutmrs, - .ops = &ufshcd_tmf_ops, - .flags = BLK_MQ_F_NO_SCHED, - }; - err = blk_mq_alloc_tag_set(&hba->tmf_tag_set); - if (err < 0) - goto out_remove_scsi_host; - hba->tmf_queue = blk_mq_alloc_queue(&hba->tmf_tag_set, NULL, NULL); - if (IS_ERR(hba->tmf_queue)) { - err = PTR_ERR(hba->tmf_queue); - goto free_tmf_tag_set; - } - hba->tmf_rqs = devm_kcalloc(hba->dev, hba->nutmrs, - sizeof(*hba->tmf_rqs), GFP_KERNEL); - if (!hba->tmf_rqs) { - err = -ENOMEM; - goto free_tmf_queue; - } - /* Reset the attached device */ ufshcd_device_reset(hba); @@ -10550,7 +10548,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) dev_err(hba->dev, "Host controller enable failed\n"); ufshcd_print_evt_hist(hba); ufshcd_print_host_state(hba); - goto free_tmf_queue; + goto out_disable; } /* @@ -10576,7 +10574,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) /* Hold auto suspend until async scan completes */ pm_runtime_get_sync(dev); - atomic_set(&hba->scsi_block_reqs_cnt, 0); + /* * We are assuming that device wasn't put in sleep/power-down * state exclusively during the boot stage before kernel. @@ -10585,6 +10583,49 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) */ ufshcd_set_ufs_dev_active(hba); + /* Initialize hba, detect and initialize UFS device */ + ktime_t probe_start = ktime_get(); + + hba->ufshcd_state = UFSHCD_STATE_RESET; + + err = ufshcd_link_startup(hba); + if (err) + goto out_disable; + + if (hba->quirks & UFSHCD_QUIRK_SKIP_PH_CONFIGURATION) + goto initialized; + + /* Debug counters initialization */ + ufshcd_clear_dbg_ufs_stats(hba); + + /* UniPro link is active now */ + ufshcd_set_link_active(hba); + + /* Verify device initialization by sending NOP OUT UPIU */ + err = ufshcd_verify_dev_init(hba); + if (err) + goto out_disable; + + /* Initiate UFS initialization, and waiting until completion */ + err = ufshcd_complete_dev_init(hba); + if (err) + goto out_disable; + + err = ufshcd_device_params_init(hba); + if (err) + goto out_disable; + + err = ufshcd_post_device_init(hba); + +initialized: + ufshcd_process_probe_result(hba, probe_start, err); + if (err) + goto out_disable; + + err = ufshcd_add_scsi_host(hba); + if (err) + goto out_disable; + async_schedule(ufshcd_async_scan, hba); ufs_sysfs_add_nodes(hba->dev); @@ -10592,14 +10633,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_pm_qos_init(hba); return 0; -free_tmf_queue: - blk_mq_destroy_queue(hba->tmf_queue); - blk_put_queue(hba->tmf_queue); -free_tmf_tag_set: - blk_mq_free_tag_set(&hba->tmf_tag_set); -out_remove_scsi_host: - if (hba->scsi_host_added) - scsi_remove_host(hba->host); out_disable: hba->is_irq_enabled = false; ufshcd_hba_exit(hba); diff --git a/drivers/ufs/host/tc-dwc-g210-pci.c b/drivers/ufs/host/tc-dwc-g210-pci.c index 876781fd6861e..0167d8bef71a1 100644 --- a/drivers/ufs/host/tc-dwc-g210-pci.c +++ b/drivers/ufs/host/tc-dwc-g210-pci.c @@ -80,14 +80,12 @@ tc_dwc_g210_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); - err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD); - if (err < 0) { + mmio_base = pcim_iomap_region(pdev, 0, UFSHCD); + if (IS_ERR(mmio_base)) { dev_err(&pdev->dev, "request and iomap failed\n"); - return err; + return PTR_ERR(mmio_base); } - mmio_base = pcim_iomap_table(pdev)[0]; - err = ufshcd_alloc_host(&pdev->dev, &hba); if (err) { dev_err(&pdev->dev, "Allocation failed\n"); diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c index 5867e63385623..6548f7a8562fb 100644 --- a/drivers/ufs/host/ufs-exynos.c +++ b/drivers/ufs/host/ufs-exynos.c @@ -48,6 +48,8 @@ #define HCI_UNIPRO_APB_CLK_CTRL 0x68 #define UNIPRO_APB_CLK(v, x) (((v) & ~0xF) | ((x) & 0xF)) #define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C +#define WLU_EN BIT(31) +#define WLU_BURST_LEN(x) ((x) << 27 | ((x) & 0xF)) #define HCI_GPIO_OUT 0x70 #define HCI_ERR_EN_PA_LAYER 0x78 #define HCI_ERR_EN_DL_LAYER 0x7C @@ -74,6 +76,10 @@ #define CLK_CTRL_EN_MASK (REFCLK_CTRL_EN |\ UNIPRO_PCLK_CTRL_EN |\ UNIPRO_MCLK_CTRL_EN) + +#define HCI_IOP_ACG_DISABLE 0x100 +#define HCI_IOP_ACG_DISABLE_EN BIT(0) + /* Device fatal error */ #define DFES_ERR_EN BIT(31) #define DFES_DEF_L2_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\ @@ -198,15 +204,8 @@ static inline void exynos_ufs_ungate_clks(struct exynos_ufs *ufs) exynos_ufs_ctrl_clkstop(ufs, false); } -static int exynos7_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs) -{ - return 0; -} - -static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs) +static int exynos_ufs_shareability(struct exynos_ufs *ufs) { - struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr; - /* IO Coherency setting */ if (ufs->sysreg) { return regmap_update_bits(ufs->sysreg, @@ -214,11 +213,32 @@ static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs) UFS_SHARABLE, UFS_SHARABLE); } - attr->tx_dif_p_nsec = 3200000; - return 0; } +static int gs101_ufs_drv_init(struct exynos_ufs *ufs) +{ + struct ufs_hba *hba = ufs->hba; + u32 reg; + + /* Enable WriteBooster */ + hba->caps |= UFSHCD_CAP_WB_EN; + + /* Enable clock gating and hibern8 */ + hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; + + /* set ACG to be controlled by UFS_ACG_DISABLE */ + reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE); + hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN), HCI_IOP_ACG_DISABLE); + + return exynos_ufs_shareability(ufs); +} + +static int exynosauto_ufs_drv_init(struct exynos_ufs *ufs) +{ + return exynos_ufs_shareability(ufs); +} + static int exynosauto_ufs_post_hce_enable(struct exynos_ufs *ufs) { struct ufs_hba *hba = ufs->hba; @@ -546,6 +566,9 @@ static void exynos_ufs_specify_phy_time_attr(struct exynos_ufs *ufs) struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr; struct ufs_phy_time_cfg *t_cfg = &ufs->t_cfg; + if (ufs->opts & EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR) + return; + t_cfg->tx_linereset_p = exynos_ufs_calc_time_cntr(ufs, attr->tx_dif_p_nsec); t_cfg->tx_linereset_n = @@ -724,6 +747,9 @@ static void exynos_ufs_config_smu(struct exynos_ufs *ufs) { u32 reg, val; + if (ufs->opts & EXYNOS_UFS_OPT_UFSPR_SECURE) + return; + exynos_ufs_disable_auto_ctrl_hcc_save(ufs, &val); /* make encryption disabled by default */ @@ -771,6 +797,21 @@ static void exynos_ufs_config_sync_pattern_mask(struct exynos_ufs *ufs, exynos_ufs_disable_ov_tm(hba); } +#define UFS_HW_VER_MAJOR_MASK GENMASK(15, 8) + +static u32 exynos_ufs_get_hs_gear(struct ufs_hba *hba) +{ + u8 major; + + major = FIELD_GET(UFS_HW_VER_MAJOR_MASK, hba->ufs_version); + + if (major >= 3) + return UFS_HS_G4; + + /* Default is HS-G3 */ + return UFS_HS_G3; +} + static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) @@ -788,6 +829,10 @@ static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba, ufshcd_init_host_params(&host_params); + /* This driver only support symmetric gear setting e.g. hs_tx_gear == hs_rx_gear */ + host_params.hs_tx_gear = exynos_ufs_get_hs_gear(hba); + host_params.hs_rx_gear = exynos_ufs_get_hs_gear(hba); + ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); if (ret) { pr_err("%s: failed to determine capabilities\n", __func__); @@ -1429,7 +1474,7 @@ static int exynos_ufs_init(struct ufs_hba *hba) exynos_ufs_fmp_init(hba, ufs); if (ufs->drv_data->drv_init) { - ret = ufs->drv_data->drv_init(dev, ufs); + ret = ufs->drv_data->drv_init(ufs); if (ret) { dev_err(dev, "failed to init drv-data\n"); goto out; @@ -1440,8 +1485,8 @@ static int exynos_ufs_init(struct ufs_hba *hba) if (ret) goto out; exynos_ufs_specify_phy_time_attr(ufs); - if (!(ufs->opts & EXYNOS_UFS_OPT_UFSPR_SECURE)) - exynos_ufs_config_smu(ufs); + + exynos_ufs_config_smu(ufs); hba->host->dma_alignment = DATA_UNIT_SIZE - 1; return 0; @@ -1484,12 +1529,12 @@ static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba) hci_writel(ufs, 1 << 0, HCI_GPIO_OUT); } -static void exynos_ufs_pre_hibern8(struct ufs_hba *hba, u8 enter) +static void exynos_ufs_pre_hibern8(struct ufs_hba *hba, enum uic_cmd_dme cmd) { struct exynos_ufs *ufs = ufshcd_get_variant(hba); struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr; - if (!enter) { + if (cmd == UIC_CMD_DME_HIBER_EXIT) { if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL) exynos_ufs_disable_auto_ctrl_hcc(ufs); exynos_ufs_ungate_clks(ufs); @@ -1517,30 +1562,11 @@ static void exynos_ufs_pre_hibern8(struct ufs_hba *hba, u8 enter) } } -static void exynos_ufs_post_hibern8(struct ufs_hba *hba, u8 enter) +static void exynos_ufs_post_hibern8(struct ufs_hba *hba, enum uic_cmd_dme cmd) { struct exynos_ufs *ufs = ufshcd_get_variant(hba); - if (!enter) { - u32 cur_mode = 0; - u32 pwrmode; - - if (ufshcd_is_hs_mode(&ufs->dev_req_params)) - pwrmode = FAST_MODE; - else - pwrmode = SLOW_MODE; - - ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &cur_mode); - if (cur_mode != (pwrmode << 4 | pwrmode)) { - dev_warn(hba->dev, "%s: power mode change\n", __func__); - hba->pwr_info.pwr_rx = (cur_mode >> 4) & 0xf; - hba->pwr_info.pwr_tx = cur_mode & 0xf; - ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info); - } - - if (!(ufs->opts & EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB)) - exynos_ufs_establish_connt(ufs); - } else { + if (cmd == UIC_CMD_DME_HIBER_ENTER) { ufs->entry_hibern8_t = ktime_get(); exynos_ufs_gate_clks(ufs); if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL) @@ -1627,15 +1653,15 @@ static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba, } static void exynos_ufs_hibern8_notify(struct ufs_hba *hba, - enum uic_cmd_dme enter, + enum uic_cmd_dme cmd, enum ufs_notify_change_status notify) { switch ((u8)notify) { case PRE_CHANGE: - exynos_ufs_pre_hibern8(hba, enter); + exynos_ufs_pre_hibern8(hba, cmd); break; case POST_CHANGE: - exynos_ufs_post_hibern8(hba, enter); + exynos_ufs_post_hibern8(hba, cmd); break; } } @@ -1891,6 +1917,12 @@ static int gs101_ufs_post_link(struct exynos_ufs *ufs) { struct ufs_hba *hba = ufs->hba; + /* + * Enable Write Line Unique. This field has to be 0x3 + * to support Write Line Unique transaction on gs101. + */ + hci_writel(ufs, WLU_EN | WLU_BURST_LEN(3), HCI_AXIDMA_RWDATA_BURST_LEN); + exynos_ufs_enable_dbg_mode(hba); ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SAVECONFIGTIME), 0x3e8); exynos_ufs_disable_dbg_mode(hba); @@ -2036,7 +2068,6 @@ static const struct exynos_ufs_drv_data exynos_ufs_drvs = { EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX | EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB | EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER, - .drv_init = exynos7_ufs_drv_init, .pre_link = exynos7_ufs_pre_link, .post_link = exynos7_ufs_post_link, .pre_pwr_change = exynos7_ufs_pre_pwr_change, @@ -2045,26 +2076,6 @@ static const struct exynos_ufs_drv_data exynos_ufs_drvs = { static struct exynos_ufs_uic_attr gs101_uic_attr = { .tx_trailingclks = 0xff, - .tx_dif_p_nsec = 3000000, /* unit: ns */ - .tx_dif_n_nsec = 1000000, /* unit: ns */ - .tx_high_z_cnt_nsec = 20000, /* unit: ns */ - .tx_base_unit_nsec = 100000, /* unit: ns */ - .tx_gran_unit_nsec = 4000, /* unit: ns */ - .tx_sleep_cnt = 1000, /* unit: ns */ - .tx_min_activatetime = 0xa, - .rx_filler_enable = 0x2, - .rx_dif_p_nsec = 1000000, /* unit: ns */ - .rx_hibern8_wait_nsec = 4000000, /* unit: ns */ - .rx_base_unit_nsec = 100000, /* unit: ns */ - .rx_gran_unit_nsec = 4000, /* unit: ns */ - .rx_sleep_cnt = 1280, /* unit: ns */ - .rx_stall_cnt = 320, /* unit: ns */ - .rx_hs_g1_sync_len_cap = SYNC_LEN_COARSE(0xf), - .rx_hs_g2_sync_len_cap = SYNC_LEN_COARSE(0xf), - .rx_hs_g3_sync_len_cap = SYNC_LEN_COARSE(0xf), - .rx_hs_g1_prep_sync_len_cap = PREP_LEN(0xf), - .rx_hs_g2_prep_sync_len_cap = PREP_LEN(0xf), - .rx_hs_g3_prep_sync_len_cap = PREP_LEN(0xf), .pa_dbg_opt_suite1_val = 0x90913C1C, .pa_dbg_opt_suite1_off = PA_GS101_DBG_OPTION_SUITE1, .pa_dbg_opt_suite2_val = 0xE01C115F, @@ -2122,11 +2133,10 @@ static const struct exynos_ufs_drv_data gs101_ufs_drvs = { UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR | UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL | UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING, - .opts = EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL | - EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR | + .opts = EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR | EXYNOS_UFS_OPT_UFSPR_SECURE | EXYNOS_UFS_OPT_TIMER_TICK_SELECT, - .drv_init = exynosauto_ufs_drv_init, + .drv_init = gs101_ufs_drv_init, .pre_link = gs101_ufs_pre_link, .post_link = gs101_ufs_post_link, .pre_pwr_change = gs101_ufs_pre_pwr_change, diff --git a/drivers/ufs/host/ufs-exynos.h b/drivers/ufs/host/ufs-exynos.h index 1646c4a9bb088..9670dc138d1e4 100644 --- a/drivers/ufs/host/ufs-exynos.h +++ b/drivers/ufs/host/ufs-exynos.h @@ -182,7 +182,7 @@ struct exynos_ufs_drv_data { unsigned int quirks; unsigned int opts; /* SoC's specific operations */ - int (*drv_init)(struct device *dev, struct exynos_ufs *ufs); + int (*drv_init)(struct exynos_ufs *ufs); int (*pre_link)(struct exynos_ufs *ufs); int (*post_link)(struct exynos_ufs *ufs); int (*pre_pwr_change)(struct exynos_ufs *ufs, diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 9a5919434c4e0..06ab1e5e8b6fb 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1780,6 +1780,15 @@ static int ufs_mtk_config_esi(struct ufs_hba *hba) return ufs_mtk_config_mcq(hba, true); } +static void ufs_mtk_config_scsi_dev(struct scsi_device *sdev) +{ + struct ufs_hba *hba = shost_priv(sdev->host); + + dev_dbg(hba->dev, "lu %llu scsi device configured", sdev->lun); + if (sdev->lun == 2) + blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, sdev->request_queue); +} + /* * struct ufs_hba_mtk_vops - UFS MTK specific variant operations * @@ -1809,6 +1818,7 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = { .op_runtime_config = ufs_mtk_op_runtime_config, .mcq_config_resource = ufs_mtk_mcq_config_resource, .config_esi = ufs_mtk_config_esi, + .config_scsi_dev = ufs_mtk_config_scsi_dev, }; /** diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index ecdfff2456e31..3b592492e1520 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -828,12 +828,28 @@ static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba) if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME) err = ufs_qcom_quirk_host_pa_saveconfigtime(hba); - if (hba->dev_info.wmanufacturerid == UFS_VENDOR_WDC) - hba->dev_quirks |= UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE; - return err; } +/* UFS device-specific quirks */ +static struct ufs_dev_quirk ufs_qcom_dev_fixups[] = { + { .wmanufacturerid = UFS_VENDOR_SKHYNIX, + .model = UFS_ANY_MODEL, + .quirk = UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM }, + { .wmanufacturerid = UFS_VENDOR_TOSHIBA, + .model = UFS_ANY_MODEL, + .quirk = UFS_DEVICE_QUIRK_DELAY_AFTER_LPM }, + { .wmanufacturerid = UFS_VENDOR_WDC, + .model = UFS_ANY_MODEL, + .quirk = UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE }, + {} +}; + +static void ufs_qcom_fixup_dev_quirks(struct ufs_hba *hba) +{ + ufshcd_fixup_dev_quirks(hba, ufs_qcom_dev_fixups); +} + static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) { return ufshci_version(2, 0); @@ -858,7 +874,8 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) if (host->hw_ver.major > 0x3) hba->quirks |= UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH; - if (of_device_is_compatible(hba->dev->of_node, "qcom,sm8550-ufshc")) + if (of_device_is_compatible(hba->dev->of_node, "qcom,sm8550-ufshc") || + of_device_is_compatible(hba->dev->of_node, "qcom,sm8650-ufshc")) hba->quirks |= UFSHCD_QUIRK_BROKEN_LSDBS_CAP; } @@ -1801,6 +1818,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { .link_startup_notify = ufs_qcom_link_startup_notify, .pwr_change_notify = ufs_qcom_pwr_change_notify, .apply_dev_quirks = ufs_qcom_apply_dev_quirks, + .fixup_dev_quirks = ufs_qcom_fixup_dev_quirks, .suspend = ufs_qcom_suspend, .resume = ufs_qcom_resume, .dbg_register_dump = ufs_qcom_dump_dbg_regs, diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c index 8711e5cbc9680..3ff97112e1f6d 100644 --- a/drivers/ufs/host/ufs-renesas.c +++ b/drivers/ufs/host/ufs-renesas.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -364,14 +365,20 @@ static int ufs_renesas_init(struct ufs_hba *hba) return -ENOMEM; ufshcd_set_variant(hba, priv); - hba->quirks |= UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS | UFSHCD_QUIRK_HIBERN_FASTAUTO; + hba->quirks |= UFSHCD_QUIRK_HIBERN_FASTAUTO; return 0; } +static int ufs_renesas_set_dma_mask(struct ufs_hba *hba) +{ + return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32)); +} + static const struct ufs_hba_variant_ops ufs_renesas_vops = { .name = "renesas", .init = ufs_renesas_init, + .set_dma_mask = ufs_renesas_set_dma_mask, .setup_clocks = ufs_renesas_setup_clocks, .hce_enable_notify = ufs_renesas_hce_enable_notify, .dbg_register_dump = ufs_renesas_dbg_register_dump, diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 54e0cc0653a24..ea39c5d5b8cf1 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -588,14 +588,12 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); - err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD); - if (err < 0) { + mmio_base = pcim_iomap_region(pdev, 0, UFSHCD); + if (IS_ERR(mmio_base)) { dev_err(&pdev->dev, "request and iomap failed\n"); - return err; + return PTR_ERR(mmio_base); } - mmio_base = pcim_iomap_table(pdev)[0]; - err = ufshcd_alloc_host(&pdev->dev, &hba); if (err) { dev_err(&pdev->dev, "Allocation failed\n"); diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 13cc35ab5d29a..c70dd81bfc61f 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -210,8 +210,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) } if (uioinfo->irq) { - struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq); - /* * If a level interrupt, dont do lazy disable. Otherwise the * irq will fire again since clearing of the actual cause, on @@ -219,8 +217,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) * irqd_is_level_type() isn't used since isn't valid until * irq is configured. */ - if (irq_data && - irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) { + if (irq_get_trigger_type(uioinfo->irq) & IRQ_TYPE_LEVEL_MASK) { dev_dbg(&pdev->dev, "disable lazy unmask\n"); irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY); } diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 8704095994118..3976360d0096d 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -135,7 +135,7 @@ static void hv_uio_rescind(struct vmbus_channel *channel) * The ring buffer is allocated as contiguous memory by vmbus_open */ static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, struct vm_area_struct *vma) { struct vmbus_channel *channel diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 796f5be0a086a..2ec7d25e82649 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -173,8 +173,6 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) } if (uioinfo->irq) { - struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq); - /* * If a level interrupt, dont do lazy disable. Otherwise the * irq will fire again since clearing of the actual cause, on @@ -182,8 +180,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) * irqd_is_level_type() isn't used since isn't valid until * irq is configured. */ - if (irq_data && - irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) { + if (irq_get_trigger_type(uioinfo->irq) & IRQ_TYPE_LEVEL_MASK) { dev_dbg(&pdev->dev, "disable lazy unmask\n"); irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY); } diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index e8e43c38aa1b4..cd0f7b4bd82ab 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -808,7 +808,7 @@ static int check_dsp_e4(const u8 *dsp, int len) if (l > len) return 1; - /* zero is zero regardless endianes */ + /* zero is zero regardless endianness */ } while (blockidx->NotLastBlock); } @@ -1276,7 +1276,7 @@ static void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate) sc->stats.phy.dsrate == dsrate) return; - /* Original timming (1Mbit/s) from ADI (used in windows driver) */ + /* Original timing (1Mbit/s) from ADI (used in windows driver) */ timeout = (dsrate <= 1024*1024) ? 0 : 1; ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL); uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n", @@ -1972,7 +1972,7 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bDirection != E1_MODEMTOHOST) goto bad1; - /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to + /* FIXME : ADI930 reply wrong preamble (func = 2, sub = 2) to * the first MEMACCESS cmv. Ignore it... */ if (cmv->bFunction != dsc->function) { diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 2da6615fbb6f4..d1e622bb14062 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1158,7 +1158,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, if (i >= num_rcv_urbs) list_add_tail(&urb->urb_list, &channel->list); - vdbg(&intf->dev, "%s: alloced buffer 0x%p buf size %u urb 0x%p", + vdbg(&intf->dev, "%s: allocated buffer 0x%p buf size %u urb 0x%p", __func__, urb->transfer_buffer, urb->transfer_buffer_length, urb); } diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index bb9d5d7ffefc5..8f38e2c5369a1 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -201,7 +201,7 @@ static void c67x00_drv_remove(struct platform_device *pdev) static struct platform_driver c67x00_driver = { .probe = c67x00_drv_probe, - .remove_new = c67x00_drv_remove, + .remove = c67x00_drv_remove, .driver = { .name = "c67x00", }, diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 281de47e2a3b1..a2f041e1707c0 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -422,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match); static struct platform_driver cdns_imx_driver = { .probe = cdns_imx_probe, - .remove_new = cdns_imx_remove, + .remove = cdns_imx_remove, .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index 591d149de8f3d..3b3b3dc75f359 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -37,8 +37,6 @@ struct cdns3_wrap { #define PCI_DRIVER_NAME "cdns3-pci-usbss" #define PLAT_DRIVER_NAME "cdns-usb3" -#define PCI_DEVICE_ID_CDNS_USB3 0x0100 - static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) { struct pci_dev *func; @@ -189,7 +187,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id cdns3_pci_ids[] = { - { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USB3) }, + { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USBSS) }, { 0, } }; diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 3ef8e3c872a37..59ec505e198a4 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -327,7 +327,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match); static struct platform_driver cdns3_driver = { .probe = cdns3_plat_probe, - .remove_new = cdns3_plat_remove, + .remove = cdns3_plat_remove, .driver = { .name = "cdns-usb3", .of_match_table = of_match_ptr(of_cdns3_match), diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index c04d196abd878..2ff7f2b48cc2b 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); static struct platform_driver cdns_starfive_driver = { .probe = cdns_starfive_probe, - .remove_new = cdns_starfive_remove, + .remove = cdns_starfive_remove, .driver = { .name = "cdns3-starfive", .of_match_table = cdns_starfive_of_match, diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index cfabc12ee0e3c..040bb91e9c017 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -233,7 +233,7 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match); static struct platform_driver cdns_ti_driver = { .probe = cdns_ti_probe, - .remove_new = cdns_ti_remove, + .remove = cdns_ti_remove, .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 2d05368a6745a..a51144504ff33 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -28,12 +28,6 @@ #define PCI_DRIVER_NAME "cdns-pci-usbssp" #define PLAT_DRIVER_NAME "cdns-usbssp" -#define PCI_DEVICE_ID_CDNS_USB3 0x0100 -#define PCI_DEVICE_ID_CDNS_UDC 0x0200 - -#define PCI_CLASS_SERIAL_USB_CDNS_USB3 (PCI_CLASS_SERIAL_USB << 8 | 0x80) -#define PCI_CLASS_SERIAL_USB_CDNS_UDC PCI_CLASS_SERIAL_USB_DEVICE - static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { /* @@ -41,10 +35,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) * Platform has two function. The fist keeps resources for * Host/Device while the secon keeps resources for DRD/OTG. */ - if (pdev->device == PCI_DEVICE_ID_CDNS_UDC) - return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USB3, NULL); - if (pdev->device == PCI_DEVICE_ID_CDNS_USB3) - return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_UDC, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSS, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_USBSS) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSSP, NULL); return NULL; } @@ -221,12 +215,12 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = { }; static const struct pci_device_id cdnsp_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC), - .class = PCI_CLASS_SERIAL_USB_CDNS_UDC }, - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC), - .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 }, - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB3), - .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), + .class = PCI_CLASS_SERIAL_USB_DEVICE }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), + .class = PCI_CLASS_SERIAL_USB_CDNS }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSS), + .class = PCI_CLASS_SERIAL_USB_CDNS }, { 0, } }; diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 2a38e1eb65466..97437de52ef68 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -25,6 +25,7 @@ #define TD_PAGE_COUNT 5 #define CI_HDRC_PAGE_SIZE 4096ul /* page size for TD's */ #define ENDPT_MAX 32 +#define CI_MAX_REQ_SIZE (4 * CI_HDRC_PAGE_SIZE) #define CI_MAX_BUF_SIZE (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE) /****************************************************************************** @@ -260,6 +261,7 @@ struct ci_hdrc { bool b_sess_valid_event; bool imx28_write_fix; bool has_portsc_pec_bug; + bool has_short_pkt_limit; bool supports_runtime_pm; bool in_lpm; bool wakeup_int; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c64ab0e07ea03..f2801700be8ec 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -342,6 +342,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, + .flags = CI_HDRC_HAS_SHORT_PKT_LIMIT, .notify_event = ci_hdrc_imx_notify_event, }; int ret; @@ -675,7 +676,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { }; static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, - .remove_new = ci_hdrc_imx_remove, + .remove = ci_hdrc_imx_remove, .shutdown = ci_hdrc_imx_shutdown, .driver = { .name = "imx_usb", diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 1661639cd2ebb..3ab3daa78e340 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -292,7 +292,7 @@ MODULE_DEVICE_TABLE(of, msm_ci_dt_match); static struct platform_driver ci_hdrc_msm_driver = { .probe = ci_hdrc_msm_probe, - .remove_new = ci_hdrc_msm_remove, + .remove = ci_hdrc_msm_remove, .driver = { .name = "msm_hsusb", .of_match_table = msm_ci_dt_match, diff --git a/drivers/usb/chipidea/ci_hdrc_npcm.c b/drivers/usb/chipidea/ci_hdrc_npcm.c index 3e5e05dbda890..e52a2b05cbe2a 100644 --- a/drivers/usb/chipidea/ci_hdrc_npcm.c +++ b/drivers/usb/chipidea/ci_hdrc_npcm.c @@ -98,7 +98,7 @@ MODULE_DEVICE_TABLE(of, npcm_udc_dt_match); static struct platform_driver npcm_udc_driver = { .probe = npcm_udc_probe, - .remove_new = npcm_udc_remove, + .remove = npcm_udc_remove, .driver = { .name = "npcm_udc", .of_match_table = npcm_udc_dt_match, diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 9538d425f0a02..372788f0f9701 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -406,7 +406,7 @@ static struct platform_driver tegra_usb_driver = { .pm = pm_ptr(&tegra_usb_pm), }, .probe = tegra_usb_probe, - .remove_new = tegra_usb_remove, + .remove = tegra_usb_remove, }; module_platform_driver(tegra_usb_driver); diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 97379f653b062..8ffa1e95d8e86 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -116,7 +116,7 @@ static void ci_hdrc_usb2_remove(struct platform_device *pdev) static struct platform_driver ci_hdrc_usb2_driver = { .probe = ci_hdrc_usb2_probe, - .remove_new = ci_hdrc_usb2_remove, + .remove = ci_hdrc_usb2_remove, .driver = { .name = "chipidea-usb2", .of_match_table = ci_hdrc_usb2_of_match, diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 835bf2428dc6e..694b4a8e4e1d8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -765,7 +765,7 @@ static int ci_get_platdata(struct device *dev, ext_id = ERR_PTR(-ENODEV); ext_vbus = ERR_PTR(-ENODEV); - if (of_property_read_bool(dev->of_node, "extcon")) { + if (of_property_present(dev->of_node, "extcon")) { /* Each one of them is not mandatory */ ext_vbus = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) @@ -1076,6 +1076,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) CI_HDRC_SUPPORTS_RUNTIME_PM); ci->has_portsc_pec_bug = !!(ci->platdata->flags & CI_HDRC_HAS_PORTSC_PEC_MISSED); + ci->has_short_pkt_limit = !!(ci->platdata->flags & + CI_HDRC_HAS_SHORT_PKT_LIMIT); platform_set_drvdata(pdev, ci); ret = hw_device_init(ci, base); @@ -1495,7 +1497,7 @@ static const struct dev_pm_ops ci_pm_ops = { static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, - .remove_new = ci_hdrc_remove, + .remove = ci_hdrc_remove, .driver = { .name = "ci_hdrc", .pm = &ci_pm_ops, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 69ef3cd8d4f83..8a9b31fd5c89d 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -540,6 +541,126 @@ static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) return ret; } +/* + * Verify if the scatterlist is valid by iterating each sg entry. + * Return invalid sg entry index which is less than num_sgs. + */ +static int sglist_get_invalid_entry(struct device *dma_dev, u8 dir, + struct usb_request *req) +{ + int i; + struct scatterlist *s = req->sg; + + if (req->num_sgs == 1) + return 1; + + dir = dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + for (i = 0; i < req->num_sgs; i++, s = sg_next(s)) { + /* Only small sg (generally last sg) may be bounced. If + * that happens. we can't ensure the addr is page-aligned + * after dma map. + */ + if (dma_kmalloc_needs_bounce(dma_dev, s->length, dir)) + break; + + /* Make sure each sg start address (except first sg) is + * page-aligned and end address (except last sg) is also + * page-aligned. + */ + if (i == 0) { + if (!IS_ALIGNED(s->offset + s->length, + CI_HDRC_PAGE_SIZE)) + break; + } else { + if (s->offset) + break; + if (!sg_is_last(s) && !IS_ALIGNED(s->length, + CI_HDRC_PAGE_SIZE)) + break; + } + } + + return i; +} + +static int sglist_do_bounce(struct ci_hw_req *hwreq, int index, + bool copy, unsigned int *bounced) +{ + void *buf; + int i, ret, nents, num_sgs; + unsigned int rest, rounded; + struct scatterlist *sg, *src, *dst; + + nents = index + 1; + ret = sg_alloc_table(&hwreq->sgt, nents, GFP_KERNEL); + if (ret) + return ret; + + sg = src = hwreq->req.sg; + num_sgs = hwreq->req.num_sgs; + rest = hwreq->req.length; + dst = hwreq->sgt.sgl; + + for (i = 0; i < index; i++) { + memcpy(dst, src, sizeof(*src)); + rest -= src->length; + src = sg_next(src); + dst = sg_next(dst); + } + + /* create one bounce buffer */ + rounded = round_up(rest, CI_HDRC_PAGE_SIZE); + buf = kmalloc(rounded, GFP_KERNEL); + if (!buf) { + sg_free_table(&hwreq->sgt); + return -ENOMEM; + } + + sg_set_buf(dst, buf, rounded); + + hwreq->req.sg = hwreq->sgt.sgl; + hwreq->req.num_sgs = nents; + hwreq->sgt.sgl = sg; + hwreq->sgt.nents = num_sgs; + + if (copy) + sg_copy_to_buffer(src, num_sgs - index, buf, rest); + + *bounced = rest; + + return 0; +} + +static void sglist_do_debounce(struct ci_hw_req *hwreq, bool copy) +{ + void *buf; + int i, nents, num_sgs; + struct scatterlist *sg, *src, *dst; + + sg = hwreq->req.sg; + num_sgs = hwreq->req.num_sgs; + src = sg_last(sg, num_sgs); + buf = sg_virt(src); + + if (copy) { + dst = hwreq->sgt.sgl; + for (i = 0; i < num_sgs - 1; i++) + dst = sg_next(dst); + + nents = hwreq->sgt.nents - num_sgs + 1; + sg_copy_from_buffer(dst, nents, buf, sg_dma_len(src)); + } + + hwreq->req.sg = hwreq->sgt.sgl; + hwreq->req.num_sgs = hwreq->sgt.nents; + hwreq->sgt.sgl = sg; + hwreq->sgt.nents = num_sgs; + + kfree(buf); + sg_free_table(&hwreq->sgt); +} + /** * _hardware_enqueue: configures a request at hardware level * @hwep: endpoint @@ -552,6 +673,8 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) struct ci_hdrc *ci = hwep->ci; int ret = 0; struct td_node *firstnode, *lastnode; + unsigned int bounced_size; + struct scatterlist *sg; /* don't queue twice */ if (hwreq->req.status == -EALREADY) @@ -559,11 +682,29 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwreq->req.status = -EALREADY; + if (hwreq->req.num_sgs && hwreq->req.length && + ci->has_short_pkt_limit) { + ret = sglist_get_invalid_entry(ci->dev->parent, hwep->dir, + &hwreq->req); + if (ret < hwreq->req.num_sgs) { + ret = sglist_do_bounce(hwreq, ret, hwep->dir == TX, + &bounced_size); + if (ret) + return ret; + } + } + ret = usb_gadget_map_request_by_dev(ci->dev->parent, &hwreq->req, hwep->dir); if (ret) return ret; + if (hwreq->sgt.sgl) { + /* We've mapped a bigger buffer, now recover the actual size */ + sg = sg_last(hwreq->req.sg, hwreq->req.num_sgs); + sg_dma_len(sg) = min(sg_dma_len(sg), bounced_size); + } + if (hwreq->req.num_mapped_sgs) ret = prepare_td_for_sg(hwep, hwreq); else @@ -612,10 +753,17 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) do { hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW) && tmp_stat); hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; + + /* OP_ENDPTSTAT will be clear by HW when the endpoint met + * err. This dTD don't push to dQH if current dTD point is + * not the last one in previous request. + */ + if (hwep->qh.ptr->curr != cpu_to_le32(prevlastnode->dma)) + goto done; } /* QH configuration */ @@ -676,6 +824,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) unsigned remaining_length; unsigned actual = hwreq->req.length; struct ci_hdrc *ci = hwep->ci; + bool is_isoc = hwep->type == USB_ENDPOINT_XFER_ISOC; if (hwreq->req.status != -EALREADY) return -EINVAL; @@ -689,7 +838,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) int n = hw_ep_bit(hwep->num, hwep->dir); if (ci->rev == CI_REVISION_24 || - ci->rev == CI_REVISION_22) + ci->rev == CI_REVISION_22 || is_isoc) if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) reprime_dtd(ci, hwep, node); hwreq->req.status = -EALREADY; @@ -708,11 +857,15 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwreq->req.status = -EPROTO; break; } else if ((TD_STATUS_TR_ERR & hwreq->req.status)) { - hwreq->req.status = -EILSEQ; - break; + if (is_isoc) { + hwreq->req.status = 0; + } else { + hwreq->req.status = -EILSEQ; + break; + } } - if (remaining_length) { + if (remaining_length && !is_isoc) { if (hwep->dir == TX) { hwreq->req.status = -EPROTO; break; @@ -733,6 +886,10 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent, &hwreq->req, hwep->dir); + /* sglist bounced */ + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, hwep->dir == RX); + hwreq->req.actual += actual; if (hwreq->req.status) @@ -960,6 +1117,12 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req, return -EMSGSIZE; } + if (ci->has_short_pkt_limit && + hwreq->req.length > CI_MAX_REQ_SIZE) { + dev_err(hwep->ci->dev, "request length too big (max 16KB)\n"); + return -EMSGSIZE; + } + /* first nuke then test link, e.g. previous status has not sent */ if (!list_empty(&hwreq->queue)) { dev_err(hwep->ci->dev, "request already in queue\n"); @@ -1574,6 +1737,9 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) usb_gadget_unmap_request(&hwep->ci->gadget, req, hwep->dir); + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, false); + req->status = -ECONNRESET; if (hwreq->req.complete != NULL) { @@ -2063,7 +2229,7 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci) } } - if (USBi_UI & intr) + if ((USBi_UI | USBi_UEI) & intr) isr_tr_complete_handler(ci); if ((USBi_SLI & intr) && !(ci->suspended)) { diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 5193df1e18c75..c8a47389a46bb 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -69,11 +69,13 @@ struct td_node { * @req: request structure for gadget drivers * @queue: link to QH list * @tds: link to TD list + * @sgt: hold original sglist when bounce sglist */ struct ci_hw_req { struct usb_request req; struct list_head queue; struct list_head tds; + struct sg_table sgt; }; #ifdef CONFIG_USB_CHIPIDEA_UDC diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 173c78afd5022..1394881fde5f5 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1285,6 +1285,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx7ulp-usbmisc", .data = &imx7ulp_usbmisc_ops, }, + { + .compatible = "fsl,imx8ulp-usbmisc", + .data = &imx7ulp_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index b7bea1015d7c1..871cf199b6bf1 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -415,6 +415,9 @@ EXPORT_SYMBOL_GPL(usb_of_get_companion_dev); struct dentry *usb_debug_root; EXPORT_SYMBOL_GPL(usb_debug_root); +DEFINE_MUTEX(usb_dynids_lock); +EXPORT_SYMBOL_GPL(usb_dynids_lock); + static int __init usb_common_init(void) { usb_debug_root = debugfs_create_dir("usb", NULL); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 501e8bc9738eb..c84b4a7000846 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -340,7 +340,7 @@ MODULE_DEVICE_TABLE(of, usb_conn_dt_match); static struct platform_driver usb_conn_driver = { .probe = usb_conn_probe, - .remove_new = usb_conn_remove, + .remove = usb_conn_remove, .driver = { .name = "usb-conn-gpio", .pm = &usb_conn_pm_ops, diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 880d52c0949d4..25a00f974934f 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -924,7 +924,7 @@ int usb_get_configuration(struct usb_device *dev) result = -EINVAL; goto err; } - length = max((int) le16_to_cpu(desc->wTotalLength), + length = max_t(int, le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE); /* Now that we know the length, get the whole thing */ diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 3beb6a862e808..f6ce6e26e0d45 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -238,6 +238,9 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) dma_addr_t dma_handle = DMA_MAPPING_ERROR; int ret; + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory)); if (ret) goto error; @@ -1295,7 +1298,7 @@ static int do_proc_bulk(struct usb_dev_state *ps, return ret; len1 = bulk->len; - if (len1 < 0 || len1 >= (INT_MAX - sizeof(struct urb))) + if (len1 >= (INT_MAX - sizeof(struct urb))) return -EINVAL; if (bulk->ep & USB_DIR_IN) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0c3f12daac79e..f203fdbfb6f68 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -95,9 +95,9 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, } } - spin_lock(&dynids->lock); + mutex_lock(&usb_dynids_lock); list_add_tail(&dynid->node, &dynids->list); - spin_unlock(&dynids->lock); + mutex_unlock(&usb_dynids_lock); retval = driver_attach(driver); @@ -116,6 +116,7 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf) struct usb_dynid *dynid; size_t count = 0; + guard(mutex)(&usb_dynids_lock); list_for_each_entry(dynid, &dynids->list, node) if (dynid->id.bInterfaceClass != 0) count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n", @@ -160,7 +161,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, if (fields < 2) return -EINVAL; - spin_lock(&usb_driver->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) { struct usb_device_id *id = &dynid->id; @@ -171,7 +172,6 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, break; } } - spin_unlock(&usb_driver->dynids.lock); return count; } @@ -220,27 +220,24 @@ static void usb_free_dynids(struct usb_driver *usb_drv) { struct usb_dynid *dynid, *n; - spin_lock(&usb_drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) { list_del(&dynid->node); kfree(dynid); } - spin_unlock(&usb_drv->dynids.lock); } static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf, - struct usb_driver *drv) + const struct usb_driver *drv) { struct usb_dynid *dynid; - spin_lock(&drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (usb_match_one_id(intf, &dynid->id)) { - spin_unlock(&drv->dynids.lock); return &dynid->id; } } - spin_unlock(&drv->dynids.lock); return NULL; } @@ -853,7 +850,7 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev, EXPORT_SYMBOL_GPL(usb_device_match_id); bool usb_driver_applicable(struct usb_device *udev, - struct usb_device_driver *udrv) + const struct usb_device_driver *udrv) { if (udrv->id_table && udrv->match) return usb_device_match_id(udev, udrv->id_table) != NULL && @@ -873,7 +870,7 @@ static int usb_device_match(struct device *dev, const struct device_driver *drv) /* devices and interfaces are handled separately */ if (is_usb_device(dev)) { struct usb_device *udev; - struct usb_device_driver *udrv; + const struct usb_device_driver *udrv; /* interface drivers never match devices */ if (!is_usb_device_driver(drv)) @@ -893,7 +890,7 @@ static int usb_device_match(struct device *dev, const struct device_driver *drv) } else if (is_usb_interface(dev)) { struct usb_interface *intf; - struct usb_driver *usb_drv; + const struct usb_driver *usb_drv; const struct usb_device_id *id; /* device drivers never match interfaces */ @@ -1076,7 +1073,6 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, new_driver->driver.owner = owner; new_driver->driver.mod_name = mod_name; new_driver->driver.dev_groups = new_driver->dev_groups; - spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); retval = driver_register(&new_driver->driver); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 4b38b87a13438..e483994016087 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "usb.h" @@ -39,7 +40,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct ep_device *ep = to_ep_device(dev); \ - return sprintf(buf, format_string, ep->desc->field); \ + return sysfs_emit(buf, format_string, ep->desc->field); \ } \ static DEVICE_ATTR_RO(field) @@ -52,7 +53,7 @@ static ssize_t wMaxPacketSize_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ep_device *ep = to_ep_device(dev); - return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); + return sysfs_emit(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); } static DEVICE_ATTR_RO(wMaxPacketSize); @@ -76,7 +77,7 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, type = "Interrupt"; break; } - return sprintf(buf, "%s\n", type); + return sysfs_emit(buf, "%s\n", type); } static DEVICE_ATTR_RO(type); @@ -95,7 +96,7 @@ static ssize_t interval_show(struct device *dev, struct device_attribute *attr, interval /= 1000; } - return sprintf(buf, "%d%cs\n", interval, unit); + return sysfs_emit(buf, "%d%cs\n", interval, unit); } static DEVICE_ATTR_RO(interval); @@ -111,7 +112,7 @@ static ssize_t direction_show(struct device *dev, struct device_attribute *attr, direction = "in"; else direction = "out"; - return sprintf(buf, "%s\n", direction); + return sysfs_emit(buf, "%s\n", direction); } static DEVICE_ATTR_RO(direction); diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 85c999f71ad7c..5e3c515991f3f 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -87,7 +88,7 @@ static ssize_t usbport_trig_port_show(struct device *dev, struct usbport_trig_port, attr); - return sprintf(buf, "%d\n", port->observed) + 1; + return sysfs_emit(buf, "%d\n", port->observed) + 1; } static ssize_t usbport_trig_port_store(struct device *dev, diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index e7da2fca11a48..45d7af00f8d17 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -166,7 +167,7 @@ static ssize_t location_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "0x%08x\n", port_dev->location); + return sysfs_emit(buf, "0x%08x\n", port_dev->location); } static DEVICE_ATTR_RO(location); @@ -191,7 +192,7 @@ static ssize_t connect_type_show(struct device *dev, break; } - return sprintf(buf, "%s\n", result); + return sysfs_emit(buf, "%s\n", result); } static DEVICE_ATTR_RO(connect_type); @@ -210,7 +211,7 @@ static ssize_t over_current_count_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "%u\n", port_dev->over_current_count); + return sysfs_emit(buf, "%u\n", port_dev->over_current_count); } static DEVICE_ATTR_RO(over_current_count); @@ -219,7 +220,7 @@ static ssize_t quirks_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "%08x\n", port_dev->quirks); + return sysfs_emit(buf, "%08x\n", port_dev->quirks); } static ssize_t quirks_store(struct device *dev, struct device_attribute *attr, @@ -254,7 +255,7 @@ static ssize_t usb3_lpm_permit_show(struct device *dev, p = "0"; } - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static ssize_t usb3_lpm_permit_store(struct device *dev, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 61b6d978892c7..b4cba23831acd 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -925,7 +925,7 @@ static struct bin_attribute *dev_bin_attrs[] = { }; static umode_t dev_bin_attrs_are_visible(struct kobject *kobj, - struct bin_attribute *a, int n) + const struct bin_attribute *a, int n) { struct device *dev = kobj_to_dev(kobj); struct usb_device *udev = to_usb_device(dev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index b8324ea05b20f..a9b37aeb515be 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -75,7 +75,7 @@ extern int usb_match_device(struct usb_device *dev, extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev, const struct usb_device_id *id); extern bool usb_driver_applicable(struct usb_device *udev, - struct usb_device_driver *udrv); + const struct usb_device_driver *udrv); extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index c131719367eca..d7af55a8eced7 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -21,7 +21,7 @@ config USB_DWC2 if USB_DWC2 choice - bool "DWC2 Mode Selection" + prompt "DWC2 Mode Selection" default USB_DWC2_DUAL_ROLE if (USB && USB_GADGET) default USB_DWC2_HOST if (USB && !USB_GADGET) default USB_DWC2_PERIPHERAL if (!USB && USB_GADGET) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index c1b7209b94836..91c80a92d9b8b 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -756,7 +756,7 @@ static struct platform_driver dwc2_platform_driver = { .pm = &dwc2_dev_pm_ops, }, .probe = dwc2_driver_probe, - .remove_new = dwc2_driver_remove, + .remove = dwc2_driver_remove, .shutdown = dwc2_driver_shutdown, }; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 31078f3d41b88..310d182e10b50 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -23,7 +23,7 @@ config USB_DWC3_ULPI controller. choice - bool "DWC3 Mode Selection" + prompt "DWC3 Mode Selection" default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) default USB_DWC3_HOST if (USB && !USB_GADGET) default USB_DWC3_GADGET if (!USB && USB_GADGET) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 98114c2827c09..f219c82e9619d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1409,7 +1409,7 @@ static int dwc3_core_init(struct dwc3 *dwc) /* * When configured in HOST mode, after issuing U3/L2 exit controller - * fails to send proper CRC checksum in CRC5 feild. Because of this + * fails to send proper CRC checksum in CRC5 field. Because of this * behaviour Transaction Error is generated, resulting in reset and * re-enumeration of usb device attached. All the termsel, xcvrsel, * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1 @@ -1470,9 +1470,13 @@ static int dwc3_core_init(struct dwc3 *dwc) if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && (DWC3_IP_IS(DWC31)) && dwc->maximum_speed == USB_SPEED_SUPER) { - reg = dwc3_readl(dwc->regs, DWC3_LLUCTL); - reg |= DWC3_LLUCTL_FORCE_GEN1; - dwc3_writel(dwc->regs, DWC3_LLUCTL, reg); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i)); + reg |= DWC3_LLUCTL_FORCE_GEN1; + dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg); + } } return 0; @@ -1941,7 +1945,7 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) struct extcon_dev *edev = NULL; const char *name; - if (device_property_read_bool(dev, "extcon")) + if (device_property_present(dev, "extcon")) return extcon_get_edev_by_phandle(dev, 0); /* @@ -2651,7 +2655,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); static struct platform_driver dwc3_driver = { .probe = dwc3_probe, - .remove_new = dwc3_remove, + .remove = dwc3_remove, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index eab81dfdcc350..ee73789326bc6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -81,7 +81,7 @@ #define DWC3_GSNPSREV_MASK 0xffff #define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16) -/* DWC3 registers memory space boundries */ +/* DWC3 registers memory space boundaries */ #define DWC3_XHCI_REGS_START 0x0 #define DWC3_XHCI_REGS_END 0x7fff #define DWC3_GLOBALS_REGS_START 0xc100 @@ -179,7 +179,7 @@ #define DWC3_OEVTEN 0xcc0C #define DWC3_OSTS 0xcc10 -#define DWC3_LLUCTL 0xd024 +#define DWC3_LLUCTL(n) (0xd024 + ((n) * 0x80)) /* Bit fields */ @@ -915,6 +915,7 @@ struct dwc3_hwparams { #define DWC3_MODE(n) ((n) & 0x7) /* HWPARAMS1 */ +#define DWC3_SPRAM_TYPE(n) (((n) >> 23) & 1) #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) /* HWPARAMS3 */ @@ -925,6 +926,9 @@ struct dwc3_hwparams { #define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ (DWC3_NUM_IN_EPS_MASK)) >> 18) +/* HWPARAMS6 */ +#define DWC3_RAM0_DEPTH(n) (((n) & (0xffff0000)) >> 16) + /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) @@ -937,18 +941,14 @@ struct dwc3_hwparams { * @request: struct usb_request to be transferred * @list: a list_head used for request queueing * @dep: struct dwc3_ep owning this request - * @sg: pointer to first incomplete sg * @start_sg: pointer to the sg which should be queued next * @num_pending_sgs: counter to pending sgs - * @num_queued_sgs: counter to the number of sgs which already got queued * @remaining: amount of data remaining * @status: internal dwc3 request status tracking * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb * @trb_dma: DMA address of @trb * @num_trbs: number of TRBs used by this request - * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP - * or unaligned OUT) * @direction: IN or OUT direction flag * @mapped: true when request has been dma-mapped */ @@ -960,7 +960,6 @@ struct dwc3_request { struct scatterlist *start_sg; unsigned int num_pending_sgs; - unsigned int num_queued_sgs; unsigned int remaining; unsigned int status; @@ -978,7 +977,6 @@ struct dwc3_request { unsigned int num_trbs; - unsigned int needs_extra_trb:1; unsigned int direction:1; unsigned int mapped:1; }; diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index fad151e78fd66..5e3d1741701f7 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -377,7 +377,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match); static struct platform_driver dwc3_ti_driver = { .probe = dwc3_ti_probe, - .remove_new = dwc3_ti_remove, + .remove = dwc3_ti_remove, .driver = { .name = "dwc3-am62", .pm = DEV_PM_OPS, diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 9a6e988d165a6..f5d963fae9e06 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -243,7 +243,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dwc3_exynos_dev_pm_ops, static struct platform_driver dwc3_exynos_driver = { .probe = dwc3_exynos_probe, - .remove_new = dwc3_exynos_remove, + .remove = dwc3_exynos_remove, .driver = { .name = "exynos-dwc3", .of_match_table = exynos_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 64c0cd1995aa0..356812cbcd884 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -400,7 +400,7 @@ MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match); static struct platform_driver dwc3_imx8mp_driver = { .probe = dwc3_imx8mp_probe, - .remove_new = dwc3_imx8mp_remove, + .remove = dwc3_imx8mp_remove, .driver = { .name = "imx8mp-dwc3", .pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops), diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 8899348b62763..7ee1610162b94 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -208,7 +208,7 @@ MODULE_DEVICE_TABLE(of, kdwc3_of_match); static struct platform_driver kdwc3_driver = { .probe = kdwc3_probe, - .remove_new = kdwc3_remove, + .remove = kdwc3_remove, .driver = { .name = "keystone-dwc3", .of_match_table = kdwc3_of_match, diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 2c07c038b584d..7d80bf7b18b0d 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -968,7 +968,7 @@ MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); static struct platform_driver dwc3_meson_g12a_driver = { .probe = dwc3_meson_g12a_probe, - .remove_new = dwc3_meson_g12a_remove, + .remove = dwc3_meson_g12a_remove, .driver = { .name = "dwc3-meson-g12a", .of_match_table = dwc3_meson_g12a_match, diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 1a3b205367fd5..42bfc14ae0c46 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -520,7 +520,7 @@ MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match); static struct platform_driver dwc3_octeon_driver = { .probe = dwc3_octeon_probe, - .remove_new = dwc3_octeon_remove, + .remove = dwc3_octeon_remove, .driver = { .name = "dwc3-octeon", .of_match_table = dwc3_octeon_of_match, diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index be7be00ecb349..a4954a21be930 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -180,7 +180,7 @@ MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); static struct platform_driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, - .remove_new = dwc3_of_simple_remove, + .remove = dwc3_of_simple_remove, .shutdown = dwc3_of_simple_shutdown, .driver = { .name = "dwc3-of-simple", diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 2a11fc0ee84f1..b261c46124c61 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -416,7 +416,7 @@ static int dwc3_omap_extcon_register(struct dwc3_omap *omap) struct device_node *node = omap->dev->of_node; struct extcon_dev *edev; - if (of_property_read_bool(node, "extcon")) { + if (of_property_present(node, "extcon")) { edev = extcon_get_edev_by_phandle(omap->dev, 0); if (IS_ERR(edev)) { dev_vdbg(omap->dev, "couldn't get extcon device\n"); @@ -611,7 +611,7 @@ static const struct dev_pm_ops dwc3_omap_dev_pm_ops = { static struct platform_driver dwc3_omap_driver = { .probe = dwc3_omap_probe, - .remove_new = dwc3_omap_remove, + .remove = dwc3_omap_remove, .driver = { .name = "omap-dwc3", .of_match_table = of_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index c1d4b52f25b06..58683bb672e95 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -161,7 +161,7 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) struct extcon_dev *host_edev; int ret; - if (!of_property_read_bool(dev->of_node, "extcon")) + if (!of_property_present(dev->of_node, "extcon")) return 0; qcom->edev = extcon_get_edev_by_phandle(dev, 0); @@ -921,7 +921,7 @@ MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, - .remove_new = dwc3_qcom_remove, + .remove = dwc3_qcom_remove, .driver = { .name = "dwc3-qcom", .pm = &dwc3_qcom_dev_pm_ops, diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c index e9c8b032c72cb..56c53e0c02574 100644 --- a/drivers/usb/dwc3/dwc3-rtk.c +++ b/drivers/usb/dwc3/dwc3-rtk.c @@ -441,7 +441,7 @@ static const struct dev_pm_ops dwc3_rtk_dev_pm_ops = { static struct platform_driver dwc3_rtk_driver = { .probe = dwc3_rtk_probe, - .remove_new = dwc3_rtk_remove, + .remove = dwc3_rtk_remove, .driver = { .name = "rtk-dwc3", .of_match_table = rtk_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 2841021f3557f..e16c3237180e9 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -356,7 +356,7 @@ MODULE_DEVICE_TABLE(of, st_dwc3_match); static struct platform_driver st_dwc3_driver = { .probe = st_dwc3_probe, - .remove_new = st_dwc3_remove, + .remove = st_dwc3_remove, .driver = { .name = "usb-st-dwc3", .of_match_table = st_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index b5e5be424ce99..e3738e1610db2 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -420,7 +420,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { static struct platform_driver dwc3_xlnx_driver = { .probe = dwc3_xlnx_probe, - .remove_new = dwc3_xlnx_remove, + .remove = dwc3_xlnx_remove, .driver = { .name = "dwc3-xilinx", .of_match_table = dwc3_xlnx_of_match, diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c9533a99e47c8..666ac432f52d6 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -145,7 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * Unfortunately we have uncovered a limitation wrt the Data Phase. * * Section 9.4 says we can wait for the XferNotReady(DATA) event to - * come before issueing Start Transfer command, but if we do, we will + * come before issuing Start Transfer command, but if we do, we will * miss situations where the host starts another SETUP phase instead of * the DATA phase. Such cases happen at least on TD.7.6 of the Link * Layer Compliance Suite. @@ -232,7 +232,7 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) /* stall is always issued on EP0 */ dep = dwc->eps[0]; __dwc3_gadget_ep_set_halt(dep, 1, false); - dep->flags &= DWC3_EP_RESOURCE_ALLOCATED; + dep->flags &= DWC3_EP_RESOURCE_ALLOCATED | DWC3_EP_TRANSFER_STARTED; dep->flags |= DWC3_EP_ENABLED; dwc->delayed_status = false; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4959c26d3b71b..83dc7304d7010 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -197,7 +197,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, list_del(&req->list); req->remaining = 0; - req->needs_extra_trb = false; req->num_trbs = 0; if (req->request.status == -EINPROGRESS) @@ -687,6 +686,44 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) return fifo_size; } +/** + * dwc3_gadget_calc_ram_depth - calculates the ram depth for txfifo + * @dwc: pointer to the DWC3 context + */ +static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc) +{ + int ram_depth; + int fifo_0_start; + bool is_single_port_ram; + + /* Check supporting RAM type by HW */ + is_single_port_ram = DWC3_SPRAM_TYPE(dwc->hwparams.hwparams1); + + /* + * If a single port RAM is utilized, then allocate TxFIFOs from + * RAM0. otherwise, allocate them from RAM1. + */ + ram_depth = is_single_port_ram ? DWC3_RAM0_DEPTH(dwc->hwparams.hwparams6) : + DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + + /* + * In a single port RAM configuration, the available RAM is shared + * between the RX and TX FIFOs. This means that the txfifo can begin + * at a non-zero address. + */ + if (is_single_port_ram) { + u32 reg; + + /* Check if TXFIFOs start at non-zero addr */ + reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg); + + ram_depth -= (fifo_0_start >> 16); + } + + return ram_depth; +} + /** * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation * @dwc: pointer to the DWC3 context @@ -753,7 +790,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; int fifo_0_start; - int ram1_depth; + int ram_depth; int fifo_size; int min_depth; int num_in_ep; @@ -773,17 +810,32 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) if (dep->flags & DWC3_EP_TXFIFO_RESIZED) return 0; - ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + ram_depth = dwc3_gadget_calc_ram_depth(dwc); - if ((dep->endpoint.maxburst > 1 && - usb_endpoint_xfer_bulk(dep->endpoint.desc)) || - usb_endpoint_xfer_isoc(dep->endpoint.desc)) - num_fifos = 3; - - if (dep->endpoint.maxburst > 6 && - (usb_endpoint_xfer_bulk(dep->endpoint.desc) || - usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) - num_fifos = dwc->tx_fifo_resize_max_num; + switch (dwc->gadget->speed) { + case USB_SPEED_SUPER_PLUS: + case USB_SPEED_SUPER: + if (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + num_fifos = min_t(unsigned int, + dep->endpoint.maxburst, + dwc->tx_fifo_resize_max_num); + break; + case USB_SPEED_HIGH: + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + num_fifos = min_t(unsigned int, + usb_endpoint_maxp_mult(dep->endpoint.desc) + 1, + dwc->tx_fifo_resize_max_num); + break; + } + fallthrough; + case USB_SPEED_FULL: + if (usb_endpoint_xfer_bulk(dep->endpoint.desc)) + num_fifos = 2; + break; + default: + break; + } /* FIFO size for a single buffer */ fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1); @@ -794,7 +846,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) /* Reserve at least one FIFO for the number of IN EPs */ min_depth = num_in_ep * (fifo + 1); - remaining = ram1_depth - min_depth - dwc->last_fifo_depth; + remaining = ram_depth - min_depth - dwc->last_fifo_depth; remaining = max_t(int, 0, remaining); /* * We've already reserved 1 FIFO per EP, so check what we can fit in @@ -820,9 +872,9 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); /* Check fifo size allocation doesn't exceed available RAM size. */ - if (dwc->last_fifo_depth >= ram1_depth) { + if (dwc->last_fifo_depth >= ram_depth) { dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n", - dwc->last_fifo_depth, ram1_depth, + dwc->last_fifo_depth, ram_depth, dep->endpoint.name, fifo_size); if (DWC3_IP_IS(DWC3)) fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); @@ -1177,11 +1229,14 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) * pending to be processed by the driver. */ if (dep->trb_enqueue == dep->trb_dequeue) { + struct dwc3_request *req; + /* - * If there is any request remained in the started_list at - * this point, that means there is no TRB available. + * If there is any request remained in the started_list with + * active TRBs at this point, then there is no TRB available. */ - if (!list_empty(&dep->started_list)) + req = next_request(&dep->started_list); + if (req && req->num_trbs) return 0; return DWC3_TRB_NUM - 1; @@ -1384,6 +1439,7 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); unsigned int rem = req->request.length % maxp; unsigned int num_trbs = 1; + bool needs_extra_trb; if (dwc3_needs_extra_trb(dep, req)) num_trbs++; @@ -1391,15 +1447,15 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, if (dwc3_calc_trbs_left(dep) < num_trbs) return 0; - req->needs_extra_trb = num_trbs > 1; + needs_extra_trb = num_trbs > 1; /* Prepare a normal TRB */ if (req->direction || req->request.length) dwc3_prepare_one_trb(dep, req, entry_length, - req->needs_extra_trb, node, false, false); + needs_extra_trb, node, false, false); /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */ - if ((!req->direction && !req->request.length) || req->needs_extra_trb) + if ((!req->direction && !req->request.length) || needs_extra_trb) dwc3_prepare_one_trb(dep, req, req->direction ? 0 : maxp - rem, false, 1, true, false); @@ -1414,8 +1470,8 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, struct scatterlist *s; int i; unsigned int length = req->request.length; - unsigned int remaining = req->request.num_mapped_sgs - - req->num_queued_sgs; + unsigned int remaining = req->num_pending_sgs; + unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining; unsigned int num_trbs = req->num_trbs; bool needs_extra_trb = dwc3_needs_extra_trb(dep, req); @@ -1423,7 +1479,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, * If we resume preparing the request, then get the remaining length of * the request and resume where we left off. */ - for_each_sg(req->request.sg, s, req->num_queued_sgs, i) + for_each_sg(req->request.sg, s, num_queued_sgs, i) length -= sg_dma_len(s); for_each_sg(sg, s, remaining, i) { @@ -1488,7 +1544,6 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, if (!last_sg) req->start_sg = sg_next(s); - req->num_queued_sgs++; req->num_pending_sgs--; /* @@ -1569,9 +1624,7 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep) if (ret) return ret; - req->sg = req->request.sg; - req->start_sg = req->sg; - req->num_queued_sgs = 0; + req->start_sg = req->request.sg; req->num_pending_sgs = req->request.num_mapped_sgs; if (req->num_pending_sgs > 0) { @@ -3075,7 +3128,7 @@ static int dwc3_gadget_check_config(struct usb_gadget *g) struct dwc3 *dwc = gadget_to_dwc(g); struct usb_ep *ep; int fifo_size = 0; - int ram1_depth; + int ram_depth; int ep_num = 0; if (!dwc->do_fifo_resize) @@ -3098,8 +3151,8 @@ static int dwc3_gadget_check_config(struct usb_gadget *g) fifo_size += dwc->max_cfg_eps; /* Check if we can fit a single fifo per endpoint */ - ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); - if (fifo_size > ram1_depth) + ram_depth = dwc3_gadget_calc_ram_depth(dwc); + if (fifo_size > ram_depth) return -ENOMEM; return 0; @@ -3416,20 +3469,16 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep, int status) { struct dwc3_trb *trb; - struct scatterlist *sg = req->sg; - struct scatterlist *s; - unsigned int num_queued = req->num_queued_sgs; + unsigned int num_completed_trbs = req->num_trbs; unsigned int i; int ret = 0; - for_each_sg(sg, s, num_queued, i) { + for (i = 0; i < num_completed_trbs; i++) { trb = &dep->trb_pool[dep->trb_dequeue]; - req->sg = sg_next(s); - req->num_queued_sgs--; - ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req, - trb, event, status, true); + trb, event, status, + !!(trb->ctrl & DWC3_TRB_CTRL_CHN)); if (ret) break; } @@ -3437,19 +3486,9 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep, return ret; } -static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req, const struct dwc3_event_depevt *event, - int status) -{ - struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue]; - - return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb, - event, status, false); -} - static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req) { - return req->num_pending_sgs == 0 && req->num_queued_sgs == 0; + return req->num_pending_sgs == 0 && req->num_trbs == 0; } static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, @@ -3459,24 +3498,13 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, int request_status; int ret; - if (req->request.num_mapped_sgs) - ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event, - status); - else - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, - status); + ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event, status); req->request.actual = req->request.length - req->remaining; if (!dwc3_gadget_ep_request_completed(req)) goto out; - if (req->needs_extra_trb) { - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, - status); - req->needs_extra_trb = false; - } - /* * The event status only reflects the status of the TRB with IOC set. * For the requests that don't set interrupt on completion, the driver diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index e0533cee6870b..b48e108fc8fe7 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -35,7 +35,7 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) u32 reg; int i; - /* xhci regs is not mapped yet, do it temperary here */ + /* xhci regs are not mapped yet, do it temporarily here */ if (dwc->xhci_resources[0].start) { xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (!xhci_regs) { diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c index 0655afe7f9779..49f25a70b32eb 100644 --- a/drivers/usb/fotg210/fotg210-core.c +++ b/drivers/usb/fotg210/fotg210-core.c @@ -195,7 +195,7 @@ static struct platform_driver fotg210_driver = { .of_match_table = of_match_ptr(fotg210_of_match), }, .probe = fotg210_probe, - .remove_new = fotg210_remove, + .remove = fotg210_remove, }; static int __init fotg210_init(void) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f25dd2cb5d03b..bdda8c74602de 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1844,7 +1844,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->desc.bcdUSB = cpu_to_le16(0x0200); } - value = min(w_length, (u16) sizeof cdev->desc); + value = min_t(u16, w_length, sizeof(cdev->desc)); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: @@ -1863,19 +1863,19 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_BOS: if (gadget_is_superspeed(gadget) || gadget->lpm_capable || cdev->use_webusb) { value = bos_desc(cdev); - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); } break; case USB_DT_OTG: @@ -1930,7 +1930,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *(u8 *)req->buf = cdev->config->bConfigurationValue; else *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; /* function drivers must handle get/set altsetting */ @@ -1976,7 +1976,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value < 0) break; *((u8 *)req->buf) = value; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; case USB_REQ_GET_STATUS: if (gadget_is_otg(gadget) && gadget->hnp_polling_support && @@ -2111,8 +2111,20 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { + /* + * The Microsoft CompatID OS Descriptor Spec(w_index = 0x4) and + * Extended Prop OS Desc Spec(w_index = 0x5) state that the + * HighByte of wValue is the InterfaceNumber and the LowByte is + * the PageNumber. This high/low byte ordering is incorrectly + * documented in the Spec. USB analyzer output on the below + * request packets show the high/low byte inverted i.e LowByte + * is the InterfaceNumber and the HighByte is the PageNumber. + * Since we dont support >64KB CompatID/ExtendedProp descriptors, + * PageNumber is set to 0. Hence verify that the HighByte is 0 + * for below two cases. + */ case USB_RECIP_DEVICE: - if (w_index != 0x4 || (w_value & 0xff)) + if (w_index != 0x4 || (w_value >> 8)) break; buf[6] = w_index; /* Number of ext compat interfaces */ @@ -2128,9 +2140,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } break; case USB_RECIP_INTERFACE: - if (w_index != 0x5 || (w_value & 0xff)) + if (w_index != 0x5 || (w_value >> 8)) break; - interface = w_value >> 8; + interface = w_value & 0xFF; if (interface >= MAX_CONFIG_INTERFACES || !os_desc_cfg->interface[interface]) break; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b1f625245713a..95f144a54ed96 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -57,11 +57,11 @@ EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf); * usb_gadget_config_buf - builts a complete configuration descriptor * @config: Header for the descriptor, including characteristics such * as power requirements and number of interfaces. - * @desc: Null-terminated vector of pointers to the descriptors (interface, - * endpoint, etc) defining all functions in this device configuration. * @buf: Buffer for the resulting configuration descriptor. * @length: Length of buffer. If this is not big enough to hold the * entire configuration descriptor, an error code will be returned. + * @desc: Null-terminated vector of pointers to the descriptors (interface, + * endpoint, etc) defining all functions in this device configuration. * * This copies descriptors into the response buffer, building a descriptor * for that configuration. It returns the buffer length or a negative diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index c82a6a0fba93d..6499a88d346cd 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1184,7 +1184,7 @@ static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, struct gadget_info *gi = os_desc_item_to_gadget_info(item); int res, l; - l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); + l = min_t(int, len, OS_STRING_QW_SIGN_LEN >> 1); if (page[l - 1] == '\n') --l; diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 87917a7d4a9be..7ce1637276f09 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -41,6 +41,10 @@ obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o +ifneq ($(CONFIG_TRACING),) + CFLAGS_uvc_trace.o := -I$(src) + usb_f_uvc-y += uvc_trace.o +endif obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 2920f8000bbd8..2ccf7f4e4db15 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -456,7 +456,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, } /* FFS_SETUP_PENDING and not stall */ - len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength)); spin_unlock_irq(&ffs->ev.waitq.lock); @@ -590,7 +590,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ return __ffs_ep0_read_events(ffs, buf, - min(n, (size_t)ffs->ev.count)); + min_t(size_t, n, ffs->ev.count)); case FFS_SETUP_PENDING: if (ffs->ev.setup.bRequestType & USB_DIR_IN) { @@ -599,7 +599,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, goto done_mutex; } - len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength)); spin_unlock_irq(&ffs->ev.waitq.lock); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 08e0d1c511e8d..2eae8fc2e0db7 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -500,7 +500,7 @@ static int fsg_setup(struct usb_function *f, *(u8 *)req->buf = _fsg_common_get_max_lun(fsg->common); /* Respond with data/status */ - req->length = min((u16)1, w_length); + req->length = min_t(u16, 1, w_length); return ep0_queue(fsg->common); } @@ -655,7 +655,7 @@ static int do_read(struct fsg_common *common) * And don't try to read past the end of the file. */ amount = min(amount_left, FSG_BUFLEN); - amount = min((loff_t)amount, + amount = min_t(loff_t, amount, curlun->file_length - file_offset); /* Wait for the next buffer to become available */ @@ -1005,7 +1005,7 @@ static int do_verify(struct fsg_common *common) * And don't try to read past the end of the file. */ amount = min(amount_left, FSG_BUFLEN); - amount = min((loff_t)amount, + amount = min_t(loff_t, amount, curlun->file_length - file_offset); if (amount == 0) { curlun->sense_data = @@ -2167,7 +2167,7 @@ static int do_scsi_command(struct fsg_common *common) if (reply == -EINVAL) reply = 0; /* Error reply length */ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32)reply, common->data_size_from_cmnd); + reply = min_t(u32, reply, common->data_size_from_cmnd); bh->inreq->length = reply; bh->state = BUF_STATE_FULL; common->residue -= reply; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 1067847cc0799..837fcdfa3840f 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -819,9 +819,9 @@ static int f_midi_register_card(struct f_midi *midi) goto fail; } - strcpy(card->driver, f_midi_longname); - strcpy(card->longname, f_midi_longname); - strcpy(card->shortname, f_midi_shortname); + strscpy(card->driver, f_midi_longname); + strscpy(card->longname, f_midi_longname); + strscpy(card->shortname, f_midi_shortname); /* Set up rawmidi */ snd_component_add(card, "MIDI"); @@ -833,7 +833,7 @@ static int f_midi_register_card(struct f_midi *midi) } midi->rmidi = rmidi; midi->in_last_port = 0; - strcpy(rmidi->name, card->shortname); + strscpy(rmidi->name, card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 8285df9ed6fd7..ee3d9e3578f79 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1285,10 +1285,8 @@ static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, if (alt == 0) op_mode = MIDI_OP_MODE_MIDI1; - else if (alt == 1) - op_mode = MIDI_OP_MODE_MIDI2; else - op_mode = MIDI_OP_MODE_UNSET; + op_mode = MIDI_OP_MODE_MIDI2; if (midi2->operation_mode == op_mode) return 0; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 40187b7112e79..aa6ab666741a9 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -465,7 +465,7 @@ uvc_register_video(struct uvc_device *uvc) memcpy(mem, desc, (desc)->bLength); \ *(dst)++ = mem; \ mem += (desc)->bLength; \ - } while (0); + } while (0) #define UVC_COPY_DESCRIPTORS(mem, dst, src) \ do { \ @@ -991,6 +991,8 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_info(f, "%s()\n", __func__); + kthread_cancel_work_sync(&video->hw_submit); + if (video->async_wq) destroy_workqueue(video->async_wq); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index cb35687b11e7e..6f44dd7323150 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -71,6 +71,11 @@ extern unsigned int uvc_gadget_trace_param; #define UVCG_REQUEST_HEADER_LEN 12 +#define UVCG_REQ_MAX_INT_COUNT 16 +#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT) + +#define UVCG_STREAMING_MIN_BUFFERS 2 + /* ------------------------------------------------------------------------ * Structures */ @@ -91,16 +96,24 @@ struct uvc_video { struct work_struct pump; struct workqueue_struct *async_wq; + struct kthread_worker *kworker; + struct kthread_work hw_submit; + + atomic_t queued; + /* Frame parameters */ u8 bpp; u32 fcc; unsigned int width; unsigned int height; unsigned int imagesize; + unsigned int interval; struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; + unsigned int reqs_per_frame; + /* Requests */ bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 6fac696ea8463..f131943254a4c 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1566,11 +1566,13 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { /* ----------------------------------------------------------------------------- * streaming/uncompressed * streaming/mjpeg + * streaming/framebased */ static const char * const uvcg_format_names[] = { "uncompressed", "mjpeg", + "framebased", }; static struct uvcg_color_matching * @@ -1777,6 +1779,9 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + uvcg_format_set_indices(to_config_group(target)); format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); @@ -1816,6 +1821,9 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) if (format_ptr->fmt == target_fmt) { list_del(&format_ptr->entry); @@ -1826,6 +1834,7 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, --target_fmt->linked; +out: mutex_unlock(&opts->lock); mutex_unlock(su_mutex); } @@ -2022,6 +2031,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); +UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32); #undef UVCG_FRAME_ATTR @@ -2035,7 +2045,7 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, int result, i; char *pg = page; - mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2105,7 +2115,7 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); -static struct configfs_attribute *uvcg_frame_attrs[] = { +static struct configfs_attribute *uvcg_frame_attrs1[] = { &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, @@ -2118,12 +2128,31 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -static const struct config_item_type uvcg_frame_type = { +static struct configfs_attribute *uvcg_frame_attrs2[] = { + &uvcg_frame_attr_b_frame_index, + &uvcg_frame_attr_bm_capabilities, + &uvcg_frame_attr_w_width, + &uvcg_frame_attr_w_height, + &uvcg_frame_attr_dw_min_bit_rate, + &uvcg_frame_attr_dw_max_bit_rate, + &uvcg_frame_attr_dw_default_frame_interval, + &uvcg_frame_attr_dw_frame_interval, + &uvcg_frame_attr_dw_bytes_perline, + NULL, +}; + +static const struct config_item_type uvcg_frame_type1 = { .ct_item_ops = &uvcg_config_item_ops, - .ct_attrs = uvcg_frame_attrs, + .ct_attrs = uvcg_frame_attrs1, .ct_owner = THIS_MODULE, }; +static const struct config_item_type uvcg_frame_type2 = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_frame_attrs2, + .ct_owner = THIS_MODULE, +}; + static struct config_item *uvcg_frame_make(struct config_group *group, const char *name) { @@ -2145,6 +2174,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.dw_max_bit_rate = 55296000; h->frame.dw_max_video_frame_buffer_size = 460800; h->frame.dw_default_frame_interval = 666666; + h->frame.dw_bytes_perline = 0; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2157,6 +2187,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group, } else if (fmt->type == UVCG_MJPEG) { h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; h->fmt_type = UVCG_MJPEG; + } else if (fmt->type == UVCG_FRAMEBASED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED; + h->fmt_type = UVCG_FRAMEBASED; } else { mutex_unlock(&opts->lock); kfree(h); @@ -2175,7 +2208,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group, ++fmt->num_frames; mutex_unlock(&opts->lock); - config_item_init_type_name(&h->item, name, &uvcg_frame_type); + if (fmt->type == UVCG_FRAMEBASED) + config_item_init_type_name(&h->item, name, &uvcg_frame_type2); + else + config_item_init_type_name(&h->item, name, &uvcg_frame_type1); return &h->item; } @@ -2215,9 +2251,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) list_for_each_entry(ci, &fmt->cg_children, ci_entry) { struct uvcg_frame *frm; - if (ci->ci_type != &uvcg_frame_type) - continue; - frm = to_uvcg_frame(ci); frm->frame.b_frame_index = i++; } @@ -2677,6 +2710,251 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { .name = "mjpeg", }; +/* ----------------------------------------------------------------------------- + * streaming/framebased/ + */ + +static struct configfs_group_operations uvcg_framebased_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +UVC_ATTR_RO(uvcg_framebased_, cname, aname) + +#define UVCG_FRAMEBASED_ATTR(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_framebased_##cname##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +UVC_ATTR(uvcg_framebased_, cname, aname) + +UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); + +#undef UVCG_FRAMEBASED_ATTR +#undef UVCG_FRAMEBASED_ATTR_RO + +static ssize_t uvcg_framebased_guid_format_show(struct config_item *item, + char *page) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_framebased_guid_format_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_framebased_, guid_format, guidFormat); + +static inline ssize_t +uvcg_framebased_bma_controls_show(struct config_item *item, char *page) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_show(&u->fmt, page); +} + +static inline ssize_t +uvcg_framebased_bma_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_store(&u->fmt, page, len); +} + +UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls); + +static struct configfs_attribute *uvcg_framebased_attrs[] = { + &uvcg_framebased_attr_b_format_index, + &uvcg_framebased_attr_b_default_frame_index, + &uvcg_framebased_attr_b_bits_per_pixel, + &uvcg_framebased_attr_b_aspect_ratio_x, + &uvcg_framebased_attr_b_aspect_ratio_y, + &uvcg_framebased_attr_bm_interface_flags, + &uvcg_framebased_attr_bma_controls, + &uvcg_framebased_attr_guid_format, + NULL, +}; + +static const struct config_item_type uvcg_framebased_type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_group_ops, + .ct_attrs = uvcg_framebased_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_framebased_make(struct config_group *group, + const char *name) +{ + static char guid[] = { /*Declear frame based as H264 format*/ + 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_framebased *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); + + h->desc.bLength = UVC_DT_FORMAT_FRAMEBASED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 0; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + h->desc.bVariableSize = 1; + + INIT_LIST_HEAD(&h->fmt.frames); + h->fmt.type = UVCG_FRAMEBASED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_framebased_type); + + return &h->fmt.group; +} + +static struct configfs_group_operations uvcg_framebased_grp_ops = { + .make_group = uvcg_framebased_make, +}; + +static const struct uvcg_config_group_type uvcg_framebased_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "framebased", +}; + /* ----------------------------------------------------------------------------- * streaming/color_matching/default */ @@ -2912,6 +3190,7 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, if (ret) return ret; grp = &f->fmt->group; + j = 0; list_for_each_entry(item, &grp->cg_children, ci_entry) { frm = to_uvcg_frame(item); ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); @@ -2965,6 +3244,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, container_of(fmt, struct uvcg_mjpeg, fmt); *size += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, fmt); + + *size += sizeof(f->desc); } else { return -EINVAL; } @@ -2975,6 +3259,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, int sz = sizeof(frm->dw_frame_interval); *size += sizeof(frm->frame); + /* + * framebased has duplicate member with uncompressed and + * mjpeg, so minus it + */ + *size -= sizeof(u32); *size += frm->frame.b_frame_interval_type * sz; } break; @@ -2991,6 +3280,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm, + int sz) +{ + struct uvc_frame_framebased *desc = dest; + + desc->bLength = frm->frame.b_length; + desc->bDescriptorType = frm->frame.b_descriptor_type; + desc->bDescriptorSubType = frm->frame.b_descriptor_subtype; + desc->bFrameIndex = frm->frame.b_frame_index; + desc->bmCapabilities = frm->frame.bm_capabilities; + desc->wWidth = frm->frame.w_width; + desc->wHeight = frm->frame.w_height; + desc->dwMinBitRate = frm->frame.dw_min_bit_rate; + desc->dwMaxBitRate = frm->frame.dw_max_bit_rate; + desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval; + desc->bFrameIntervalType = frm->frame.b_frame_interval_type; + desc->dwBytesPerLine = frm->frame.dw_bytes_perline; + + return 0; +} + /* * Fill an array of streaming descriptors. * @@ -3045,6 +3355,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, + fmt); + + f->desc.bFormatIndex = n + 1; + f->desc.bNumFrameDescriptors = fmt->num_frames; + memcpy(*dest, &f->desc, sizeof(f->desc)); + *dest += sizeof(f->desc); } else { return -EINVAL; } @@ -3054,8 +3373,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_frame *frm = priv1; struct uvc_descriptor_header *h = *dest; - sz = sizeof(frm->frame); - memcpy(*dest, &frm->frame, sz); + sz = sizeof(frm->frame) - 4; + if (frm->fmt_type != UVCG_FRAMEBASED) + memcpy(*dest, &frm->frame, sz); + else + __uvcg_copy_framebased_desc(*dest, frm, sz); *dest += sz; sz = frm->frame.b_frame_interval_type * sizeof(*frm->dw_frame_interval); @@ -3066,7 +3388,10 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, frm->frame.b_frame_interval_type); else if (frm->fmt_type == UVCG_MJPEG) h->bLength = UVC_DT_FRAME_MJPEG_SIZE( - frm->frame.b_frame_interval_type); + frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_FRAMEBASED) + h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE( + frm->frame.b_frame_interval_type); } break; case UVCG_COLOR_MATCHING: { @@ -3285,6 +3610,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { &uvcg_streaming_header_grp_type, &uvcg_uncompressed_grp_type, &uvcg_mjpeg_grp_type, + &uvcg_framebased_grp_type, &uvcg_color_matching_grp_type, &uvcg_streaming_class_grp_type, NULL, diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index c6a690158138f..2f78cd4f396f2 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -49,6 +49,7 @@ container_of(group_ptr, struct uvcg_color_matching, group) enum uvcg_format_type { UVCG_UNCOMPRESSED = 0, UVCG_MJPEG, + UVCG_FRAMEBASED, }; struct uvcg_format { @@ -105,6 +106,7 @@ struct uvcg_frame { u32 dw_max_video_frame_buffer_size; u32 dw_default_frame_interval; u8 b_frame_interval_type; + u32 dw_bytes_perline; } __attribute__((packed)) frame; u32 *dw_frame_interval; }; @@ -142,6 +144,20 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); } +/* ----------------------------------------------------------------------------- + * streaming/framebased/ + */ + +struct uvcg_framebased { + struct uvcg_format fmt; + struct uvc_format_framebased desc; +}; + +static inline struct uvcg_framebased *to_uvcg_framebased(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_framebased, fmt); +} + /* ----------------------------------------------------------------------------- * control/extensions/ */ diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 0aa3d7e1f3cc3..5eaeae3e2441c 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -21,6 +21,7 @@ #include #include "uvc.h" +#include "uvc_video.h" /* ------------------------------------------------------------------------ * Video buffers queue management. @@ -44,33 +45,23 @@ static int uvc_queue_setup(struct vb2_queue *vq, { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_video *video = container_of(queue, struct uvc_video, queue); - unsigned int req_size; - unsigned int nreq; if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) *nbuffers = UVC_MAX_VIDEO_BUFFERS; + if (*nbuffers < UVCG_STREAMING_MIN_BUFFERS) + *nbuffers = UVCG_STREAMING_MIN_BUFFERS; *nplanes = 1; sizes[0] = video->imagesize; - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); - - /* We divide by two, to increase the chance to run - * into fewer requests for smaller framesizes. - */ - nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size); - nreq = clamp(nreq, 4U, 64U); - video->uvc_num_requests = nreq; - return 0; } static int uvc_buffer_prepare(struct vb2_buffer *vb) { struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); @@ -91,10 +82,15 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->mem = vb2_plane_vaddr(vb, 0); } buf->length = vb2_plane_size(vb, 0); - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { buf->bytesused = 0; - else + } else { buf->bytesused = vb2_get_plane_payload(vb, 0); + buf->req_payload_size = + DIV_ROUND_UP(buf->bytesused + + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), + video->reqs_per_frame); + } return 0; } diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 41f87b917f6bc..b54becc570a38 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -39,6 +39,8 @@ struct uvc_buffer { unsigned int offset; unsigned int length; unsigned int bytesused; + /* req_payload_size: only used with isoc */ + unsigned int req_payload_size; }; #define UVC_QUEUE_DISCONNECTED (1 << 0) diff --git a/drivers/usb/gadget/function/uvc_trace.c b/drivers/usb/gadget/function/uvc_trace.c new file mode 100644 index 0000000000000..d384f6d8221a5 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * trace.c - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik + */ + +#define CREATE_TRACE_POINTS +#include "uvc_trace.h" diff --git a/drivers/usb/gadget/function/uvc_trace.h b/drivers/usb/gadget/function/uvc_trace.h new file mode 100644 index 0000000000000..04c33cf43cc2d --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * trace.h - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM uvcg + +#if !defined(__UVCG_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __UVCG_TRACE_H + +#include +#include +#include +#include + +DECLARE_EVENT_CLASS(uvcg_video_req, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued), + TP_STRUCT__entry( + __field(struct usb_request *, req) + __field(u32, length) + __field(u32, queued) + ), + TP_fast_assign( + __entry->req = req; + __entry->length = req->length; + __entry->queued = queued; + ), + TP_printk("req %p length %u queued %u", + __entry->req, + __entry->length, + __entry->queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_complete, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_queue, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +#endif /* __UVCG_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE uvc_trace + +#include diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index de1736f834e6b..fc9a8d31a1e98 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -31,13 +31,22 @@ static const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) { char guid[16] = UVC_GUID_FORMAT_MJPEG; const struct uvc_format_desc *format; - struct uvcg_uncompressed *unc; if (uformat->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *unc; + unc = to_uvcg_uncompressed(&uformat->group.cg_item); if (!unc) return ERR_PTR(-EINVAL); + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); + } else if (uformat->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *unc; + + unc = to_uvcg_framebased(&uformat->group.cg_item); + if (!unc) + return ERR_PTR(-EINVAL); + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); } @@ -314,6 +323,56 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) return ret; } +static int uvc_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + /* Return the actual frame period. */ + timeperframe.numerator = video->interval; + timeperframe.denominator = 10000000; + v4l2_simplify_fraction(&timeperframe.numerator, + &timeperframe.denominator, 8, 333); + + uvcg_dbg(&uvc->func, "Getting frame interval of %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + parm->parm.output.timeperframe = timeperframe; + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + + return 0; +} + +static int uvc_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + timeperframe = parm->parm.output.timeperframe; + + video->interval = v4l2_fraction_to_interval(timeperframe.numerator, + timeperframe.denominator); + + uvcg_dbg(&uvc->func, "Setting frame interval to %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + return 0; +} + static int uvc_v4l2_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) @@ -496,6 +555,9 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) return ret; + if (uvc->state != UVC_STATE_STREAMING) + return 0; + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; @@ -587,6 +649,8 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_dqbuf = uvc_v4l2_dqbuf, .vidioc_streamon = uvc_v4l2_streamon, .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_s_parm = uvc_v4l2_s_parm, + .vidioc_g_parm = uvc_v4l2_g_parm, .vidioc_subscribe_event = uvc_v4l2_subscribe_event, .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, .vidioc_default = uvc_v4l2_ioctl_default, diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 57a851151225d..79e223713d8b9 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -19,6 +19,7 @@ #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" +#include "uvc_trace.h" /* -------------------------------------------------------------------------- * Video codecs @@ -78,7 +79,7 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, /* Copy video data to the USB buffer. */ mem = buf->mem + queue->buf_used; - nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); + nbytes = min_t(unsigned int, len, buf->bytesused - queue->buf_used); memcpy(data, mem, nbytes); queue->buf_used += nbytes; @@ -104,7 +105,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, } /* Process video data. */ - len = min((int)(video->max_payload_size - video->payload_size), len); + len = min_t(int, video->max_payload_size - video->payload_size, len); ret = uvc_video_encode_data(video, buf, mem, len); video->payload_size += ret; @@ -136,7 +137,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, unsigned int pending = buf->bytesused - video->queue.buf_used; struct uvc_request *ureq = req->context; struct scatterlist *sg, *iter; - unsigned int len = video->req_size; + unsigned int len = buf->req_payload_size; unsigned int sg_left, part = 0; unsigned int i; int header_len; @@ -146,15 +147,15 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, /* Init the header. */ header_len = uvc_video_encode_header(video, buf, ureq->header, - video->req_size); + buf->req_payload_size); sg_set_buf(sg, ureq->header, header_len); len -= header_len; if (pending <= len) len = pending; - req->length = (len == pending) ? - len + header_len : video->req_size; + req->length = (len == pending) ? len + header_len : + buf->req_payload_size; /* Init the pending sgs with payload */ sg = sg_next(sg); @@ -202,7 +203,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, { void *mem = req->buf; struct uvc_request *ureq = req->context; - int len = video->req_size; + int len = buf->req_payload_size; int ret; /* Add the header. */ @@ -214,7 +215,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, ret = uvc_video_encode_data(video, buf, mem, len); len -= ret; - req->length = video->req_size - len; + req->length = buf->req_payload_size - len; if (buf->bytesused == video->queue.buf_used || video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) { @@ -269,6 +270,10 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) } } + atomic_inc(&video->queued); + + trace_uvcg_video_queue(req, atomic_read(&video->queued)); + return ret; } @@ -304,7 +309,7 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, */ if (list_empty(&video->req_free) || ureq->last_buf || !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { + min(DIV_ROUND_UP(video->uvc_num_requests, 4), UVCG_REQ_MAX_INT_COUNT))) { video->req_int_count = 0; req->no_interrupt = 0; } else { @@ -322,50 +327,6 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, return 0; } -/* - * Must only be called from uvcg_video_enable - since after that we only want to - * queue requests to the endpoint from the uvc_video_complete complete handler. - * This function is needed in order to 'kick start' the flow of requests from - * gadget driver to the usb controller. - */ -static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) -{ - struct usb_request *req = NULL; - unsigned long flags = 0; - unsigned int count = 0; - int ret = 0; - - /* - * We only queue half of the free list since we still want to have - * some free usb_requests in the free list for the video_pump async_wq - * thread to encode uvc buffers into. Otherwise we could get into a - * situation where the free list does not have any usb requests to - * encode into - we always end up queueing 0 length requests to the - * end point. - */ - unsigned int half_list_size = video->uvc_num_requests / 2; - - spin_lock_irqsave(&video->req_lock, flags); - /* - * Take these requests off the free list and queue them all to the - * endpoint. Since we queue 0 length requests with the req_lock held, - * there isn't any 'data' race involved here with the complete handler. - */ - while (count < half_list_size) { - req = list_first_entry(&video->req_free, struct usb_request, - list); - list_del(&req->list); - req->length = 0; - ret = uvcg_video_ep_queue(video, req); - if (ret < 0) { - uvcg_queue_cancel(&video->queue, 0); - break; - } - count++; - } - spin_unlock_irqrestore(&video->req_lock, flags); -} - static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { @@ -373,12 +334,10 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *last_buf; - struct usb_request *to_queue = req; unsigned long flags; - bool is_bulk = video->max_payload_size; - int ret = 0; spin_lock_irqsave(&video->req_lock, flags); + atomic_dec(&video->queued); if (!video->is_enabled) { /* * When is_enabled is false, uvcg_video_disable() ensures @@ -438,51 +397,87 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) return; } + list_add_tail(&req->list, &video->req_free); /* - * Here we check whether any request is available in the ready - * list. If it is, queue it to the ep and add the current - * usb_request to the req_free list - for video_pump to fill in. - * Otherwise, just use the current usb_request to queue a 0 - * length request to the ep. Since we always add to the req_free - * list if we dequeue from the ready list, there will never - * be a situation where the req_free list is completely out of - * requests and cannot recover. + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. Since there are is a new free request wake up the pump. */ - to_queue->length = 0; - if (!list_empty(&video->req_ready)) { - to_queue = list_first_entry(&video->req_ready, - struct usb_request, list); - list_del(&to_queue->list); - list_add_tail(&req->list, &video->req_free); + queue_work(video->async_wq, &video->pump); + + trace_uvcg_video_complete(req, atomic_read(&video->queued)); + + spin_unlock_irqrestore(&video->req_lock, flags); + + kthread_queue_work(video->kworker, &video->hw_submit); +} + +static void uvcg_video_hw_submit(struct kthread_work *work) +{ + struct uvc_video *video = container_of(work, struct uvc_video, hw_submit); + bool is_bulk = video->max_payload_size; + unsigned long flags; + struct usb_request *req; + int ret = 0; + + while (true) { + if (!video->ep->enabled) + return; + spin_lock_irqsave(&video->req_lock, flags); /* - * Queue work to the wq as well since it is possible that a - * buffer may not have been completely encoded with the set of - * in-flight usb requests for whih the complete callbacks are - * firing. - * In that case, if we do not queue work to the worker thread, - * the buffer will never be marked as complete - and therefore - * not be returned to userpsace. As a result, - * dequeue -> queue -> dequeue flow of uvc buffers will not - * happen. + * Here we check whether any request is available in the ready + * list. If it is, queue it to the ep and add the current + * usb_request to the req_free list - for video_pump to fill in. + * Otherwise, just use the current usb_request to queue a 0 + * length request to the ep. Since we always add to the req_free + * list if we dequeue from the ready list, there will never + * be a situation where the req_free list is completely out of + * requests and cannot recover. */ - queue_work(video->async_wq, &video->pump); - } - /* - * Queue to the endpoint. The actual queueing to ep will - * only happen on one thread - the async_wq for bulk endpoints - * and this thread for isoc endpoints. - */ - ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); - if (ret < 0) { + if (!list_empty(&video->req_ready)) { + req = list_first_entry(&video->req_ready, + struct usb_request, list); + } else { + if (list_empty(&video->req_free) || + (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) { + spin_unlock_irqrestore(&video->req_lock, flags); + + return; + } + req = list_first_entry(&video->req_free, struct usb_request, + list); + req->length = 0; + } + list_del(&req->list); + /* - * Endpoint error, but the stream is still enabled. - * Put request back in req_free for it to be cleaned - * up later. + * Queue to the endpoint. The actual queueing to ep will + * only happen on one thread - the async_wq for bulk endpoints + * and this thread for isoc endpoints. */ - list_add_tail(&to_queue->list, &video->req_free); - } + ret = uvcg_video_usb_req_queue(video, req, !is_bulk); + if (ret < 0) { + /* + * Endpoint error, but the stream is still enabled. + * Put request back in req_free for it to be cleaned + * up later. + */ + list_add_tail(&req->list, &video->req_free); + /* + * There is a new free request - wake up the pump. + */ + queue_work(video->async_wq, &video->pump); - spin_unlock_irqrestore(&video->req_lock, flags); + } + + spin_unlock_irqrestore(&video->req_lock, flags); + } } static int @@ -496,23 +491,71 @@ uvc_video_free_requests(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; return 0; } +static void +uvc_video_prep_requests(struct uvc_video *video) +{ + struct uvc_device *uvc = container_of(video, struct uvc_device, video); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + unsigned int interval_duration = video->ep->desc->bInterval * 1250; + unsigned int max_req_size, req_size, header_size; + unsigned int nreq; + + max_req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult); + + if (!usb_endpoint_xfer_isoc(video->ep->desc)) { + video->req_size = max_req_size; + video->reqs_per_frame = video->uvc_num_requests = + DIV_ROUND_UP(video->imagesize, max_req_size); + + return; + } + + if (cdev->gadget->speed < USB_SPEED_HIGH) + interval_duration = video->ep->desc->bInterval * 10000; + + nreq = DIV_ROUND_UP(video->interval, interval_duration); + + header_size = nreq * UVCG_REQUEST_HEADER_LEN; + + req_size = DIV_ROUND_UP(video->imagesize + header_size, nreq); + + if (req_size > max_req_size) { + /* The prepared interval length and expected buffer size + * is not possible to stream with the currently configured + * isoc bandwidth. Fallback to the maximum. + */ + req_size = max_req_size; + } + video->req_size = req_size; + + /* We need to compensate the amount of requests to be + * allocated with the maximum amount of zero length requests. + * Since it is possible that hw_submit will initially + * enqueue some zero length requests and we then will not be + * able to fully encode one frame. + */ + video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT; + video->reqs_per_frame = nreq; +} + static int uvc_video_alloc_requests(struct uvc_video *video) { struct uvc_request *ureq; - unsigned int req_size; unsigned int i; int ret = -ENOMEM; - BUG_ON(video->req_size); - - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); + /* + * calculate in uvc_video_prep_requests + * - video->uvc_num_requests + * - video->req_size + */ + uvc_video_prep_requests(video); for (i = 0; i < video->uvc_num_requests; i++) { ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); @@ -523,7 +566,7 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->list, &video->ureqs); - ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + ureq->req_buffer = kmalloc(video->req_size, GFP_KERNEL); if (ureq->req_buffer == NULL) goto error; @@ -541,12 +584,10 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ sg_alloc_table(&ureq->sgt, - DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, + DIV_ROUND_UP(video->req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } - video->req_size = req_size; - return 0; error: @@ -699,7 +740,6 @@ uvcg_video_disable(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; spin_unlock_irqrestore(&video->req_lock, flags); /* @@ -752,7 +792,9 @@ int uvcg_video_enable(struct uvc_video *video) video->req_int_count = 0; - uvc_video_ep_queue_initial_requests(video); + atomic_set(&video->queued, 0); + + kthread_queue_work(video->kworker, &video->hw_submit); queue_work(video->async_wq, &video->pump); return ret; @@ -775,12 +817,24 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) if (!video->async_wq) return -EINVAL; + /* Allocate a kthread for asynchronous hw submit handler. */ + video->kworker = kthread_create_worker(0, "UVCG"); + if (IS_ERR(video->kworker)) { + uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n"); + return PTR_ERR(video->kworker); + } + + kthread_init_work(&video->hw_submit, uvcg_video_hw_submit); + + sched_set_fifo(video->kworker->task); + video->uvc = uvc; video->fcc = V4L2_PIX_FMT_YUYV; video->bpp = 16; video->width = 320; video->height = 240; video->imagesize = 320 * 240 * 2; + video->interval = 666666; /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index 4ca67b2f8f243..3684546e8801d 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -261,7 +261,7 @@ static struct usb_composite_driver hidg_driver = { }; static struct platform_driver hidg_plat_driver = { - .remove_new = hidg_plat_driver_remove, + .remove = hidg_plat_driver_remove, .driver = { .name = "hidg", }, diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index 112fd18d8c99d..20165e1582d9b 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -782,7 +782,7 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value) if (ret < 0) goto free; - length = min(io.length, (unsigned int)ret); + length = min_t(unsigned int, io.length, ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; else @@ -1168,7 +1168,7 @@ static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value) if (ret < 0) goto free; - length = min(io.length, (unsigned int)ret); + length = min_t(unsigned int, io.length, ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; else diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index f60a019bb1730..f2685f89b3e50 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -428,7 +428,7 @@ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids); static struct platform_driver ast_vhub_driver = { .probe = ast_vhub_probe, - .remove_new = ast_vhub_remove, + .remove = ast_vhub_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_vhub_dt_ids, diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index f4781e611aaa2..353bfb1ff0a12 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -156,7 +156,7 @@ #define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) #define AST_EP_DMA_DESC_PID_MDATA (3 << 14) #define EP_DESC1_IN_LEN(x) ((x) & 0x1fff) -#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for trasmit in 1 desc */ +#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for transmit in 1 desc */ struct ast_udc_request { struct usb_request req; @@ -1590,7 +1590,7 @@ MODULE_DEVICE_TABLE(of, ast_udc_of_dt_ids); static struct platform_driver ast_udc_driver = { .probe = ast_udc_probe, - .remove_new = ast_udc_remove, + .remove = ast_udc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_udc_of_dt_ids, diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index e3bf17a98b380..e3af4ec3794e2 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -2002,7 +2002,7 @@ static int at91udc_resume(struct platform_device *pdev) static struct platform_driver at91_udc_driver = { .probe = at91udc_probe, - .remove_new = at91udc_remove, + .remove = at91udc_remove, .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 4928eba193272..0c6f2ad81d37f 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2444,7 +2444,7 @@ static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); static struct platform_driver udc_driver = { .probe = usba_udc_probe, - .remove_new = usba_udc_remove, + .remove = usba_udc_remove, .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index da7011d906e01..502612a5650e7 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2367,7 +2367,7 @@ static void bcm63xx_udc_remove(struct platform_device *pdev) static struct platform_driver bcm63xx_udc_driver = { .probe = bcm63xx_udc_probe, - .remove_new = bcm63xx_udc_remove, + .remove = bcm63xx_udc_remove, .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 5149e2b7f0508..5c3d8b64c0e76 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -648,7 +648,7 @@ static struct platform_driver bdc_driver = { .of_match_table = bdc_of_match, }, .probe = bdc_probe, - .remove_new = bdc_remove, + .remove = bdc_remove, }; module_platform_driver(bdc_driver); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c index b1a8f772467c0..e589593b4cbf8 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c @@ -15,7 +15,6 @@ #include "cdns2-gadget.h" #define PCI_DRIVER_NAME "cdns-pci-usbhs" -#define PCI_DEVICE_ID_CDNS_USB2 0x0120 #define PCI_BAR_DEV 0 #define PCI_DEV_FN_DEVICE 0 @@ -113,7 +112,7 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = { }; static const struct pci_device_id cdns2_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB2), + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB), .class = PCI_CLASS_SERIAL_USB_DEVICE }, { 0, } }; diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 081ac7683c0b3..a7e8fa45776b5 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -80,7 +80,7 @@ module_param_named(num, mod_data.num, uint, S_IRUGO); MODULE_PARM_DESC(num, "number of emulated controllers"); /*-------------------------------------------------------------------------*/ -/* gadget side driver data structres */ +/* gadget side driver data structures */ struct dummy_ep { struct list_head queue; unsigned long last_io; /* jiffies timestamp */ @@ -1152,7 +1152,7 @@ static int dummy_udc_resume(struct platform_device *pdev) static struct platform_driver dummy_udc_driver = { .probe = dummy_udc_probe, - .remove_new = dummy_udc_remove, + .remove = dummy_udc_remove, .suspend = dummy_udc_suspend, .resume = dummy_udc_resume, .driver = { @@ -2769,7 +2769,7 @@ static int dummy_hcd_resume(struct platform_device *pdev) static struct platform_driver dummy_hcd_driver = { .probe = dummy_hcd_probe, - .remove_new = dummy_hcd_remove, + .remove = dummy_hcd_remove, .suspend = dummy_hcd_suspend, .resume = dummy_hcd_resume, .driver = { diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 4e88681a79b63..aacfde06387c0 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -511,7 +511,7 @@ static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num) out_8(&epparam->tbmr, rtfcr); tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE); - /* MRBLR must be divisble by 4 */ + /* MRBLR must be divisible by 4 */ tmp = (u16)(((tmp >> 2) << 2) + 4); out_be16(&epparam->mrblr, tmp); @@ -1413,7 +1413,7 @@ static int ep_txframe_handle(struct qe_ep *ep) return 0; } -/* confirm the already trainsmited bd */ +/* confirm the already transmitted bd */ static int qe_ep_txconf(struct qe_ep *ep) { struct qe_bd __iomem *bd; @@ -2196,7 +2196,7 @@ static int tx_irq(struct qe_udc *udc) } -/* setup packect's rx is handle in the function too */ +/* setup packet's rx is handle in the function too */ static void rx_irq(struct qe_udc *udc) { struct qe_ep *ep; @@ -2704,7 +2704,7 @@ static struct platform_driver udc_driver = { .of_match_table = qe_udc_match, }, .probe = qe_udc_probe, - .remove_new = qe_udc_remove, + .remove = qe_udc_remove, #ifdef CONFIG_PM .suspend = qe_udc_suspend, .resume = qe_udc_resume, diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 0cabd4eee6acb..8b7f7f9617745 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2685,7 +2685,7 @@ MODULE_DEVICE_TABLE(of, fsl_udc_dt_ids); static struct platform_driver udc_driver = { .probe = fsl_udc_probe, - .remove_new = fsl_udc_remove, + .remove = fsl_udc_remove, .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 873265634cccb..5e94a99b3e539 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1297,7 +1297,7 @@ static void init_controller(struct fusb300 *fusb300) reg |= val; iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR); - /*set u1 u2 timmer*/ + /*set u1 u2 timer*/ fusb300_set_u2_timeout(fusb300, 0xff); fusb300_set_u1_timeout(fusb300, 0xff); @@ -1507,7 +1507,7 @@ static int fusb300_probe(struct platform_device *pdev) static struct platform_driver fusb300_driver = { .probe = fusb300_probe, - .remove_new = fusb300_remove, + .remove = fusb300_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index fb901be5dac1b..bf5b3c964c187 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2249,7 +2249,7 @@ static struct platform_driver gr_driver = { .of_match_table = gr_match, }, .probe = gr_probe, - .remove_new = gr_remove, + .remove = gr_remove, }; module_platform_driver(gr_driver); diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 3bfd889ed56a4..89d6daf2bda7a 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3249,7 +3249,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); static struct platform_driver lpc32xx_udc_driver = { .probe = lpc32xx_udc_probe, - .remove_new = lpc32xx_udc_remove, + .remove = lpc32xx_udc_remove, .shutdown = lpc32xx_udc_shutdown, .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index bfaa5291e6c83..a938b2af0944b 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1688,7 +1688,7 @@ static int m66592_probe(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ static struct platform_driver m66592_driver = { .probe = m66592_probe, - .remove_new = m66592_remove, + .remove = m66592_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index e1dd5cf25d08a..062f43e146aaa 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -2047,7 +2047,7 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove_new = mv_u3d_remove, + .remove = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .name = "mv-u3d", diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 71012b2828910..ff103e6b3048f 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -2409,7 +2409,7 @@ static void mv_udc_shutdown(struct platform_device *pdev) static struct platform_driver udc_driver = { .probe = mv_udc_probe, - .remove_new = mv_udc_remove, + .remove = mv_udc_remove, .shutdown = mv_udc_shutdown, .driver = { .name = "mv-udc", diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 9230db57dab7a..7ecddbf5c90db 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2097,7 +2097,7 @@ static irqreturn_t net2272_irq(int irq, void *_dev) } /* check dma interrupts */ #endif - /* Platform/devcice interrupt handler */ + /* Platform/device interrupt handler */ #if !defined(PLX_PCI_RDK) net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); @@ -2685,7 +2685,7 @@ net2272_plat_remove(struct platform_device *pdev) static struct platform_driver net2272_plat_driver = { .probe = net2272_plat_probe, - .remove_new = net2272_plat_remove, + .remove = net2272_plat_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 61a45e4657d5d..698463bf697b2 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -576,13 +576,13 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) static void next_out_dma(struct omap_ep *ep, struct omap_req *req) { - unsigned packets = req->req.length - req->req.actual; + unsigned int packets = req->req.length - req->req.actual; int dma_trigger = 0; u16 w; /* set up this DMA transfer, enable the fifo, start */ packets /= ep->ep.maxpacket; - packets = min(packets, (unsigned)UDC_RXN_TC + 1); + packets = min_t(unsigned int, packets, UDC_RXN_TC + 1); req->dma_bytes = packets * ep->ep.maxpacket; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, ep->ep.maxpacket >> 1, packets, @@ -2980,7 +2980,7 @@ static int omap_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .probe = omap_udc_probe, - .remove_new = omap_udc_remove, + .remove = omap_udc_remove, .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 7c96fc9f680f1..1e9998024aaa3 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2474,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, .probe = pxa25x_udc_probe, - .remove_new = pxa25x_udc_remove, + .remove = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index ea79f85f8e0bb..f9a55d4f189f4 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2539,7 +2539,7 @@ static struct platform_driver udc_driver = { .of_match_table = of_match_ptr(udc_pxa_dt_ids), }, .probe = pxa_udc_probe, - .remove_new = pxa_udc_remove, + .remove = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index db4a10a979f9d..ff6f846b1d411 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1965,7 +1965,7 @@ static int r8a66597_probe(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 3b01734ce1b7e..fce5c41d9f298 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -3013,7 +3013,7 @@ static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, - .remove_new = renesas_usb3_remove, + .remove = renesas_usb3_remove, .driver = { .name = udc_name, .pm = &renesas_usb3_pm_ops, diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 657f265ac7cc5..14f4b2cf05a48 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -2482,7 +2482,7 @@ static int usbf_handle_ep0_setup(struct usbf_ep *ep0) ep0->delayed_status = 0; if ((crq.ctrlreq.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { - /* This is not a USB standard request -> delelate */ + /* This is not a USB standard request -> delegate */ goto delegate; } @@ -3381,7 +3381,7 @@ static struct platform_driver udc_driver = { .of_match_table = usbf_match, }, .probe = usbf_probe, - .remove_new = usbf_remove, + .remove = usbf_remove, }; module_platform_driver(udc_driver); diff --git a/drivers/usb/gadget/udc/rzv2m_usb3drd.c b/drivers/usb/gadget/udc/rzv2m_usb3drd.c index 36f4ff00d22fd..4692eae89f443 100644 --- a/drivers/usb/gadget/udc/rzv2m_usb3drd.c +++ b/drivers/usb/gadget/udc/rzv2m_usb3drd.c @@ -127,7 +127,7 @@ static struct platform_driver rzv2m_usb3drd_driver = { .of_match_table = rzv2m_usb3drd_of_match, }, .probe = rzv2m_usb3drd_probe, - .remove_new = rzv2m_usb3drd_remove, + .remove = rzv2m_usb3drd_remove, }; module_platform_driver(rzv2m_usb3drd_driver); diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index cd89532adec26..1f8a99d2a6436 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -2707,7 +2707,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) /* write fifo */ udc_txfifo_write(ep, &req->req); - /* lengh bytes transferred */ + /* length bytes transferred */ len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index ba5a066905077..db842a6de643d 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -309,7 +309,7 @@ MODULE_DEVICE_TABLE(of, of_udc_match); static struct platform_driver udc_plat_driver = { .probe = udc_plat_probe, - .remove_new = udc_plat_remove, + .remove = udc_plat_remove, .driver = { .name = "snps-udc-plat", .of_match_table = of_udc_match, diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 7aa46d426f31b..c7fdbc55fb0b9 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -4071,7 +4071,7 @@ static const struct dev_pm_ops tegra_xudc_pm_ops = { static struct platform_driver tegra_xudc_driver = { .probe = tegra_xudc_probe, - .remove_new = tegra_xudc_remove, + .remove = tegra_xudc_remove, .driver = { .name = "tegra-xudc", .pm = &tegra_xudc_pm_ops, diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index ebc45565c33e5..ae2aeb2718977 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2258,7 +2258,7 @@ static struct platform_driver xudc_driver = { .pm = &xudc_pm_ops, }, .probe = xudc_probe, - .remove_new = xudc_remove, + .remove = xudc_remove, }; module_platform_driver(xudc_driver); diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 75f6f99f8173e..37a2f1b61cbab 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -55,7 +55,7 @@ usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf) return -EINVAL; /* string descriptors have length, tag, then UTF16-LE text */ - len = min((size_t)USB_MAX_STRING_LEN, strlen(s->s)); + len = min_t(size_t, USB_MAX_STRING_LEN, strlen(s->s)); len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN, (wchar_t *) &buf[2], USB_MAX_STRING_LEN); if (len < 0) diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 7558cc4d90cc6..519386255886c 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 6a6e1c510b283..65747270fd88d 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -220,7 +220,7 @@ static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend, static struct platform_driver ehci_atmel_driver = { .probe = ehci_atmel_drv_probe, - .remove_new = ehci_atmel_drv_remove, + .remove = ehci_atmel_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "atmel-ehci", diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 68cad0620f1a9..888e8f6670d2a 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -250,7 +250,7 @@ MODULE_DEVICE_TABLE(of, brcm_ehci_of_match); static struct platform_driver ehci_brcm_driver = { .probe = ehci_brcm_probe, - .remove_new = ehci_brcm_remove, + .remove = ehci_brcm_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-brcm", diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index e3a961d3f5fc9..d2a5bedf736a2 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE(of, exynos_ehci_match); static struct platform_driver exynos_ehci_driver = { .probe = exynos_ehci_probe, - .remove_new = exynos_ehci_remove, + .remove = exynos_ehci_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "exynos-ehci", diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5b1ce394a4174..26f13278d4d69 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -706,7 +706,7 @@ static void fsl_ehci_drv_remove(struct platform_device *pdev) static struct platform_driver ehci_fsl_driver = { .probe = fsl_ehci_drv_probe, - .remove_new = fsl_ehci_drv_remove, + .remove = fsl_ehci_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 14150e4d33826..bd9762eaa1352 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -168,7 +168,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); static struct platform_driver ehci_grlib_driver = { .probe = ehci_hcd_grlib_probe, - .remove_new = ehci_hcd_grlib_remove, + .remove = ehci_hcd_grlib_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "grlib-ehci", diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index cbc0b86fcc365..6de79ac5e6a44 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -547,7 +547,7 @@ static int ehci_init(struct usb_hcd *hcd) * make problems: throughput reduction (!), data errors... */ if (park) { - park = min(park, (unsigned) 3); + park = min_t(unsigned int, park, 3); temp |= CMD_PARK; temp |= park << 8; } diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 2f1fc7eb8b727..cbabbe1071721 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -279,7 +279,7 @@ static const struct of_device_id ehci_mv_dt_ids[] = { static struct platform_driver ehci_mv_driver = { .probe = mv_ehci_probe, - .remove_new = mv_ehci_remove, + .remove = mv_ehci_remove, .shutdown = mv_ehci_shutdown, .driver = { .name = "mv-ehci", diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index 3d3317a1a0b3f..f1c7034c1e80d 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -122,7 +122,7 @@ MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); static struct platform_driver npcm7xx_ehci_hcd_driver = { .probe = npcm7xx_ehci_hcd_drv_probe, - .remove_new = npcm7xx_ehci_hcd_drv_remove, + .remove = npcm7xx_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "npcm7xx-ehci", diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index b24f371a46f3e..db4a1acb27dae 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -264,7 +264,7 @@ MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, - .remove_new = ehci_hcd_omap_remove, + .remove = ehci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ehci_hcd_omap_suspend, */ /*.resume = ehci_hcd_omap_resume, */ diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index ad145a54ca74e..34abff8669f8b 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -352,7 +352,7 @@ MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, - .remove_new = ehci_orion_drv_remove, + .remove = ehci_orion_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "orion-ehci", diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 98b073185e1c7..cdf41886e8caf 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -508,7 +508,7 @@ static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, static struct platform_driver ehci_platform_driver = { .id_table = ehci_platform_table, .probe = ehci_platform_probe, - .remove_new = ehci_platform_remove, + .remove = ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-platform", diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 7fd83e806ae46..8063b9d3aebd0 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); static struct platform_driver ehci_hcd_ppc_of_driver = { .probe = ehci_hcd_ppc_of_probe, - .remove_new = ehci_hcd_ppc_of_remove, + .remove = ehci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ehci", diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index d31d9506e41ab..5d0d972fb7b1b 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -169,7 +169,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev) static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, - .remove_new = ehci_hcd_sh_remove, + .remove = ehci_hcd_sh_remove, .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index d0e94e4c9fe27..e96710192d6b0 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -105,7 +105,9 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) /* registers start at offset 0x0 */ hcd_to_ehci(hcd)->caps = hcd->regs; - clk_prepare_enable(sehci->clk); + retval = clk_prepare_enable(sehci->clk); + if (retval) + goto err_put_hcd; retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err_stop_ehci; @@ -130,8 +132,7 @@ static void spear_ehci_hcd_drv_remove(struct platform_device *pdev) usb_remove_hcd(hcd); - if (sehci->clk) - clk_disable_unprepare(sehci->clk); + clk_disable_unprepare(sehci->clk); usb_put_hcd(hcd); } @@ -143,7 +144,7 @@ MODULE_DEVICE_TABLE(of, spear_ehci_id_table); static struct platform_driver spear_ehci_hcd_driver = { .probe = spear_ehci_hcd_drv_probe, - .remove_new = spear_ehci_hcd_drv_remove, + .remove = spear_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "spear-ehci", diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index 2dbb0d86daaaa..58867d816af75 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -320,7 +320,7 @@ MODULE_DEVICE_TABLE(of, st_ehci_ids); static struct platform_driver ehci_platform_driver = { .probe = st_ehci_platform_probe, - .remove_new = st_ehci_platform_remove, + .remove = st_ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ehci", diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index a2112c28f6314..1d16cfefabd71 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -220,7 +220,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); static struct platform_driver ehci_hcd_xilinx_of_driver = { .probe = ehci_hcd_xilinx_of_probe, - .remove_new = ehci_hcd_xilinx_of_remove, + .remove = ehci_hcd_xilinx_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xilinx-of-ehci", diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 9a1b5224f2396..22a0942f0bcee 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -791,7 +791,7 @@ static struct platform_driver of_fhci_driver = { .of_match_table = of_fhci_match, }, .probe = of_fhci_probe, - .remove_new = of_fhci_remove, + .remove = of_fhci_remove, }; module_platform_driver(of_fhci_driver); diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index a45ede80edfcd..c3acd410ce948 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -158,7 +158,7 @@ static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td) struct packet *pkt; u8 *data = NULL; - /* calcalate data address,len and toggle and then add the transaction */ + /* calculate data address,len and toggle and then add the transaction */ if (td->toggle == USB_TD_TOGGLE_CARRY) td->toggle = ed->toggle_carry; @@ -679,7 +679,7 @@ static void process_done_list(unsigned long data) DECLARE_TASKLET_OLD(fhci_tasklet, process_done_list); -/* transfer complted callback */ +/* transfer completed callback */ u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci) { if (!fhci->process_done_task->state) diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 6cdc3d805c32a..4e67b94719860 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -362,7 +362,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = { .of_match_table = fsl_usb2_mph_dr_of_match, }, .probe = fsl_usb2_mph_dr_of_probe, - .remove_new = fsl_usb2_mph_dr_of_remove, + .remove = fsl_usb2_mph_dr_of_remove, }; module_platform_driver(fsl_usb2_mph_dr_driver); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index a82d8926e922c..71c22c4bd1635 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1684,7 +1684,7 @@ MODULE_ALIAS("platform:isp116x-hcd"); static struct platform_driver isp116x_driver = { .probe = isp116x_probe, - .remove_new = isp116x_remove, + .remove = isp116x_remove, .suspend = isp116x_suspend, .resume = isp116x_resume, .driver = { diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 31059c8f94e63..2d3a082cb52f8 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2757,7 +2757,7 @@ static int isp1362_resume(struct platform_device *pdev) static struct platform_driver isp1362_driver = { .probe = isp1362_probe, - .remove_new = isp1362_remove, + .remove = isp1362_remove, .suspend = isp1362_suspend, .resume = isp1362_resume, diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c index 19d5777f5db25..361d33b0c4d29 100644 --- a/drivers/usb/host/octeon-hcd.c +++ b/drivers/usb/host/octeon-hcd.c @@ -3346,7 +3346,7 @@ static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_INDICATOR: dev_dbg(dev, " INDICATOR\n"); - /* Port inidicator not supported */ + /* Port indicator not supported */ break; case USB_PORT_FEAT_C_CONNECTION: dev_dbg(dev, " C_CONNECTION\n"); @@ -3711,8 +3711,8 @@ static struct platform_driver octeon_usb_driver = { .name = "octeon-hcd", .of_match_table = octeon_usb_match, }, - .probe = octeon_usb_probe, - .remove_new = octeon_usb_remove, + .probe = octeon_usb_probe, + .remove = octeon_usb_remove, }; static int __init octeon_usb_driver_init(void) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index f691cd98a5746..5df793dcb25da 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -685,7 +685,7 @@ static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend, static struct platform_driver ohci_hcd_at91_driver = { .probe = ohci_hcd_at91_drv_probe, - .remove_new = ohci_hcd_at91_drv_remove, + .remove = ohci_hcd_at91_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "at91_ohci", diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index d2b67da76762c..3c5ca2d7c92ed 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -531,7 +531,7 @@ static const struct ohci_driver_overrides da8xx_overrides __initconst = { */ static struct platform_driver ohci_hcd_da8xx_driver = { .probe = ohci_da8xx_probe, - .remove_new = ohci_da8xx_remove, + .remove = ohci_da8xx_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_da8xx_suspend, diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 1379e03644b2a..cc5cb09009880 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -262,7 +262,7 @@ MODULE_DEVICE_TABLE(of, exynos_ohci_match); static struct platform_driver exynos_ohci_driver = { .probe = exynos_ohci_probe, - .remove_new = exynos_ohci_remove, + .remove = exynos_ohci_remove, .shutdown = exynos_ohci_shutdown, .driver = { .name = "exynos-ohci", diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 5b775e1ea5270..24d5a1dc50560 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -254,7 +254,7 @@ static struct platform_driver ohci_hcd_nxp_driver = { .of_match_table = of_match_ptr(ohci_hcd_nxp_match), }, .probe = ohci_hcd_nxp_probe, - .remove_new = ohci_hcd_nxp_remove, + .remove = ohci_hcd_nxp_remove, }; static int __init ohci_nxp_init(void) diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 21a6f6c55e079..f6e56c4b99148 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -152,7 +152,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd) rh &= ~RH_A_NOCP; - /* gpio9 for overcurrent detction */ + /* gpio9 for overcurrent detection */ omap_cfg_reg(W8_1610_GPIO9); /* for paranoia's sake: disable USB.PUEN */ @@ -390,7 +390,7 @@ static int ohci_omap_resume(struct platform_device *dev) */ static struct platform_driver ohci_hcd_omap_driver = { .probe = ohci_hcd_omap_probe, - .remove_new = ohci_hcd_omap_remove, + .remove = ohci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_omap_suspend, diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 4a75507325ddc..f47ae12cde6a2 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -344,7 +344,7 @@ static const struct dev_pm_ops ohci_platform_pm_ops = { static struct platform_driver ohci_platform_driver = { .id_table = ohci_platform_table, .probe = ohci_platform_probe, - .remove_new = ohci_platform_remove, + .remove = ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ohci-platform", diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index a6be061f86532..acd0a0e398a43 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -219,7 +219,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); static struct platform_driver ohci_hcd_ppc_of_driver = { .probe = ohci_hcd_ppc_of_probe, - .remove_new = ohci_hcd_ppc_of_remove, + .remove = ohci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ohci", diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 3348c25ddb18d..45d026e85168b 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -569,7 +569,7 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_probe, - .remove_new = ohci_hcd_pxa27x_remove, + .remove = ohci_hcd_pxa27x_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "pxa27x-ohci", diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index c5c9b4cbcb9a3..66d970854357d 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -457,7 +457,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids); static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_probe, - .remove_new = ohci_hcd_s3c2410_remove, + .remove = ohci_hcd_s3c2410_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "s3c2410-ohci", diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 4b39e9d6f33a9..843a5378764e6 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -252,7 +252,7 @@ static int ohci_sm501_resume(struct platform_device *pdev) */ static struct platform_driver ohci_hcd_sm501_driver = { .probe = ohci_hcd_sm501_drv_probe, - .remove_new = ohci_hcd_sm501_drv_remove, + .remove = ohci_hcd_sm501_drv_remove, .shutdown = usb_hcd_platform_shutdown, .suspend = ohci_sm501_suspend, .resume = ohci_sm501_resume, diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 993f347c5c288..d7131e5a4477c 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -157,7 +157,7 @@ MODULE_DEVICE_TABLE(of, spear_ohci_id_table); /* Driver definition to register with the platform bus */ static struct platform_driver spear_ohci_hcd_driver = { .probe = spear_ohci_hcd_drv_probe, - .remove_new = spear_ohci_hcd_drv_remove, + .remove = spear_ohci_hcd_drv_remove, #ifdef CONFIG_PM .suspend = spear_ohci_hcd_drv_suspend, .resume = spear_ohci_hcd_drv_resume, diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 214342013f7ec..d1656fce54004 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -298,7 +298,7 @@ MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); static struct platform_driver ohci_platform_driver = { .probe = st_ohci_platform_probe, - .remove_new = st_ohci_platform_remove, + .remove = st_ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ohci", diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index ca3859463ba14..a6c20facf9450 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -885,7 +885,7 @@ static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) int a_blocks; /* blocks allocated */ int i, j; - /* Don't allocte bigger than supported */ + /* Don't allocate bigger than supported */ if (len > BUFFER_SIZE * BUFFER_NUM) { oxu_err(oxu, "buffer too big (%d)\n", len); return -ENOMEM; @@ -902,7 +902,7 @@ static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) /* Find a suitable available data buffer */ for (i = 0; i < BUFFER_NUM; - i += max(a_blocks, (int)oxu->db_used[i])) { + i += max_t(int, a_blocks, oxu->db_used[i])) { /* Check all the required blocks are available */ for (j = 0; j < a_blocks; j++) @@ -3040,7 +3040,7 @@ static int oxu_hcd_init(struct usb_hcd *hcd) * make problems: throughput reduction (!), data errors... */ if (park) { - park = min(park, (unsigned) 3); + park = min_t(unsigned int, park, 3); temp |= CMD_PARK; temp |= park << 8; } @@ -4289,7 +4289,7 @@ static int oxu_drv_resume(struct device *dev) static struct platform_driver oxu_driver = { .probe = oxu_drv_probe, - .remove_new = oxu_drv_remove, + .remove = oxu_drv_remove, .shutdown = oxu_drv_shutdown, .suspend = oxu_drv_suspend, .resume = oxu_drv_resume, diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 6576515a29cd3..a44992e2561b0 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -759,7 +759,7 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597, struct r8a66597_pipe_info *info = &pipe->info; unsigned short mbw = mbw_value(r8a66597); - /* pipe dma is only for external controlles */ + /* pipe dma is only for external controllers */ if (r8a66597->pdata->on_chip) return; @@ -1336,7 +1336,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) buf = (void *)urb->transfer_buffer + urb->actual_length; urb_len = urb->transfer_buffer_length - urb->actual_length; } - bufsize = min(urb_len, (int) td->maxpacket); + bufsize = min_t(int, urb_len, td->maxpacket); if (rcv_len <= bufsize) { size = rcv_len; } else { @@ -2510,7 +2510,7 @@ static int r8a66597_probe(struct platform_device *pdev) static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = hcd_name, .pm = R8A66597_DEV_PM_OPS, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 92f2d12384488..036f5fd6d159f 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1784,7 +1784,7 @@ sl811h_resume(struct platform_device *dev) /* this driver is exported so sl811_cs can depend on it */ struct platform_driver sl811h_driver = { .probe = sl811h_probe, - .remove_new = sl811h_remove, + .remove = sl811h_remove, .suspend = sl811h_suspend, .resume = sl811h_resume, diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index cfebb833668e4..8a1f6d1b5b560 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match); static struct platform_driver uhci_grlib_driver = { .probe = uhci_hcd_grlib_probe, - .remove_new = uhci_hcd_grlib_remove, + .remove = uhci_hcd_grlib_remove, .shutdown = uhci_hcd_grlib_shutdown, .driver = { .name = "grlib-uhci", diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 3dec5dd3a0d5c..a7c934404ebc7 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(of, platform_uhci_ids); static struct platform_driver uhci_platform_driver = { .probe = uhci_hcd_platform_probe, - .remove_new = uhci_hcd_platform_remove, + .remove = uhci_hcd_platform_remove, .shutdown = uhci_hcd_platform_shutdown, .driver = { .name = "platform-uhci", diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 241d7aa1fbc20..227e513867dd2 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -248,8 +248,9 @@ xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, trb->generic.field[2] = cpu_to_le32(field3); trb->generic.field[3] = cpu_to_le32(field4); - trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic); - + trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic, + xhci_trb_virt_to_dma(ring->enq_seg, + ring->enqueue)); ring->num_trbs_free--; next = ++(ring->enqueue); if (TRB_TYPE_LINK_LE32(next->link.control)) { @@ -471,7 +472,7 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags) trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK)); } INIT_LIST_HEAD(&ring->td_list); - xhci_initialize_ring_info(ring, 1); + xhci_initialize_ring_info(ring); return ring; dma_fail: kfree(seg); @@ -747,7 +748,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) return; } - trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic, req->trb_dma); switch (comp_code) { case COMP_SUCCESS: @@ -898,7 +899,9 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) */ rmb(); - trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic); + trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic, + xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue)); switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_PORT_STATUS): diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index f8ba15e7c225c..4f0c1b96e208f 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -214,14 +214,11 @@ static void xhci_ring_dump_segment(struct seq_file *s, static int xhci_ring_trb_show(struct seq_file *s, void *unused) { - int i; struct xhci_ring *ring = *(struct xhci_ring **)s->private; struct xhci_segment *seg = ring->first_seg; - for (i = 0; i < ring->num_segs; i++) { + xhci_for_each_ring_seg(ring->first_seg, seg) xhci_ring_dump_segment(s, seg); - seg = seg->next; - } return 0; } @@ -291,12 +288,13 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused) for (ep_index = 0; ep_index < 31; ep_index++) { ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params); - seq_printf(s, "%pad: %s\n", &dma, + seq_printf(s, "%pad: %s, virt_state:%#x\n", &dma, xhci_decode_ep_context(str, le32_to_cpu(ep_ctx->ep_info), le32_to_cpu(ep_ctx->ep_info2), le64_to_cpu(ep_ctx->deq), - le32_to_cpu(ep_ctx->tx_info))); + le32_to_cpu(ep_ctx->tx_info)), + dev->eps[ep_index].ep_state); } return 0; diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index f9a4a4b0eb574..8a7d46dae62c8 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -373,7 +373,7 @@ MODULE_DEVICE_TABLE(of, histb_xhci_of_match); static struct platform_driver histb_xhci_driver = { .probe = xhci_histb_probe, - .remove_new = xhci_histb_remove, + .remove = xhci_histb_remove, .driver = { .name = "xhci-histb", .pm = DEV_PM_OPS, diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 8d774f19271e6..9693464c05204 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -946,9 +946,9 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } /* did port event handler already start resume timing? */ if (!port->resume_timestamp) { - /* If not, maybe we are in a host initated resume? */ + /* If not, maybe we are in a host initiated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { - /* Host initated resume doesn't time the resume + /* Host initiated resume doesn't time the resume * signalling using resume_done[]. * It manually sets RESUME state, sleeps 20ms * and sets U0 state. This should probably be @@ -1924,7 +1924,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* resume already initiated */ break; default: - /* not in a resumeable state, ignore it */ + /* not in a resumable state, ignore it */ clear_bit(port_index, &bus_state->bus_suspended); break; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d2900197a49e7..15db90c54a45a 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -27,14 +27,12 @@ * "All components of all Command and Transfer TRBs shall be initialized to '0'" */ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, - unsigned int cycle_state, unsigned int max_packet, unsigned int num, gfp_t flags) { struct xhci_segment *seg; dma_addr_t dma; - int i; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; seg = kzalloc_node(sizeof(*seg), flags, dev_to_node(dev)); @@ -56,11 +54,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, return NULL; } } - /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ - if (cycle_state == 0) { - for (i = 0; i < TRBS_PER_SEGMENT; i++) - seg->trbs[i].link.control = cpu_to_le32(TRB_CYCLE); - } seg->num = num; seg->dma = dma; seg->next = NULL; @@ -78,82 +71,104 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) kfree(seg); } -static void xhci_free_segments_for_ring(struct xhci_hcd *xhci, - struct xhci_segment *first) +static void xhci_ring_segments_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { - struct xhci_segment *seg; + struct xhci_segment *seg, *next; + + ring->last_seg->next = NULL; + seg = ring->first_seg; - seg = first->next; - while (seg && seg != first) { - struct xhci_segment *next = seg->next; + while (seg) { + next = seg->next; xhci_segment_free(xhci, seg); seg = next; } - xhci_segment_free(xhci, first); } /* - * Make the prev segment point to the next segment. + * Only for transfer and command rings where driver is the producer, not for + * event rings. * - * Change the last TRB in the prev segment to be a Link TRB which points to the + * Change the last TRB in the segment to be a Link TRB which points to the * DMA address of the next segment. The caller needs to set any Link TRB * related flags, such as End TRB, Toggle Cycle, and no snoop. */ -static void xhci_link_segments(struct xhci_segment *prev, - struct xhci_segment *next, - enum xhci_ring_type type, bool chain_links) +static void xhci_set_link_trb(struct xhci_segment *seg, bool chain_links) { + union xhci_trb *trb; u32 val; - if (!prev || !next) + if (!seg || !seg->next) return; - prev->next = next; - if (type != TYPE_EVENT) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = - cpu_to_le64(next->dma); - /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ - val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); - val &= ~TRB_TYPE_BITMASK; - val |= TRB_TYPE(TRB_LINK); - if (chain_links) - val |= TRB_CHAIN; - prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); - } + trb = &seg->trbs[TRBS_PER_SEGMENT - 1]; + + /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ + val = le32_to_cpu(trb->link.control); + val &= ~TRB_TYPE_BITMASK; + val |= TRB_TYPE(TRB_LINK); + if (chain_links) + val |= TRB_CHAIN; + trb->link.control = cpu_to_le32(val); + trb->link.segment_ptr = cpu_to_le64(seg->next->dma); +} + +static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + struct xhci_segment *seg; + bool chain_links; + + if (ring->type == TYPE_EVENT) + return; + + chain_links = xhci_link_chain_quirk(xhci, ring->type); + xhci_for_each_ring_seg(ring->first_seg, seg) + xhci_set_link_trb(seg, chain_links); + + /* See section 4.9.2.1 and 6.4.4.1 */ + ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } /* - * Link the ring to the new segments. + * Link the src ring segments to the dst ring. * Set Toggle Cycle for the new ring if needed. */ -static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, - struct xhci_segment *first, struct xhci_segment *last, - unsigned int num_segs) +static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *src, struct xhci_ring *dst) { - struct xhci_segment *next, *seg; + struct xhci_segment *seg; bool chain_links; - if (!ring || !first || !last) + if (!src || !dst) return; - chain_links = xhci_link_chain_quirk(xhci, ring->type); + /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ + if (dst->cycle_state == 0) { + xhci_for_each_ring_seg(src->first_seg, seg) { + for (int i = 0; i < TRBS_PER_SEGMENT; i++) + seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE); + } + } - next = ring->enq_seg->next; - xhci_link_segments(ring->enq_seg, first, ring->type, chain_links); - xhci_link_segments(last, next, ring->type, chain_links); - ring->num_segs += num_segs; + src->last_seg->next = dst->enq_seg->next; + dst->enq_seg->next = src->first_seg; + if (dst->type != TYPE_EVENT) { + chain_links = xhci_link_chain_quirk(xhci, dst->type); + xhci_set_link_trb(dst->enq_seg, chain_links); + xhci_set_link_trb(src->last_seg, chain_links); + } + dst->num_segs += src->num_segs; - if (ring->enq_seg == ring->last_seg) { - if (ring->type != TYPE_EVENT) { - ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control + if (dst->enq_seg == dst->last_seg) { + if (dst->type != TYPE_EVENT) + dst->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control &= ~cpu_to_le32(LINK_TOGGLE); - last->trbs[TRBS_PER_SEGMENT-1].link.control - |= cpu_to_le32(LINK_TOGGLE); - } - ring->last_seg = last; + + dst->last_seg = src->last_seg; + } else if (dst->type != TYPE_EVENT) { + src->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control &= ~cpu_to_le32(LINK_TOGGLE); } - for (seg = ring->enq_seg; seg != ring->last_seg; seg = seg->next) + for (seg = dst->enq_seg; seg != dst->last_seg; seg = seg->next) seg->next->num = seg->num + 1; } @@ -224,7 +239,6 @@ static int xhci_update_stream_segment_mapping( struct radix_tree_root *trb_address_map, struct xhci_ring *ring, struct xhci_segment *first_seg, - struct xhci_segment *last_seg, gfp_t mem_flags) { struct xhci_segment *seg; @@ -234,28 +248,22 @@ static int xhci_update_stream_segment_mapping( if (WARN_ON_ONCE(trb_address_map == NULL)) return 0; - seg = first_seg; - do { + xhci_for_each_ring_seg(first_seg, seg) { ret = xhci_insert_segment_mapping(trb_address_map, ring, seg, mem_flags); if (ret) goto remove_streams; - if (seg == last_seg) - return 0; - seg = seg->next; - } while (seg != first_seg); + } return 0; remove_streams: failed_seg = seg; - seg = first_seg; - do { + xhci_for_each_ring_seg(first_seg, seg) { xhci_remove_segment_mapping(trb_address_map, seg); if (seg == failed_seg) return ret; - seg = seg->next; - } while (seg != first_seg); + } return ret; } @@ -267,17 +275,14 @@ static void xhci_remove_stream_mapping(struct xhci_ring *ring) if (WARN_ON_ONCE(ring->trb_address_map == NULL)) return; - seg = ring->first_seg; - do { + xhci_for_each_ring_seg(ring->first_seg, seg) xhci_remove_segment_mapping(ring->trb_address_map, seg); - seg = seg->next; - } while (seg != ring->first_seg); } static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) { return xhci_update_stream_segment_mapping(ring->trb_address_map, ring, - ring->first_seg, ring->last_seg, mem_flags); + ring->first_seg, mem_flags); } /* XXX: Do we need the hcd structure in all these functions? */ @@ -291,14 +296,13 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->first_seg) { if (ring->type == TYPE_STREAM) xhci_remove_stream_mapping(ring); - xhci_free_segments_for_ring(xhci, ring->first_seg); + xhci_ring_segments_free(xhci, ring); } kfree(ring); } -void xhci_initialize_ring_info(struct xhci_ring *ring, - unsigned int cycle_state) +void xhci_initialize_ring_info(struct xhci_ring *ring) { /* The ring is empty, so the enqueue pointer == dequeue pointer */ ring->enqueue = ring->first_seg->trbs; @@ -312,7 +316,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, * New rings are initialized with cycle state equal to 1; if we are * handling ring expansion, set the cycle state equal to the old ring. */ - ring->cycle_state = cycle_state; + ring->cycle_state = 1; /* * Each segment has a link TRB, and leave an extra TRB for SW @@ -323,46 +327,36 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ -static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, - struct xhci_segment **first, - struct xhci_segment **last, - unsigned int num_segs, - unsigned int cycle_state, - enum xhci_ring_type type, - unsigned int max_packet, - gfp_t flags) +static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, gfp_t flags) { struct xhci_segment *prev; unsigned int num = 0; - bool chain_links; - - chain_links = xhci_link_chain_quirk(xhci, type); - prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags); + prev = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags); if (!prev) return -ENOMEM; num++; - *first = prev; - while (num < num_segs) { + ring->first_seg = prev; + while (num < ring->num_segs) { struct xhci_segment *next; - next = xhci_segment_alloc(xhci, cycle_state, max_packet, num, - flags); + next = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags); if (!next) goto free_segments; - xhci_link_segments(prev, next, type, chain_links); + prev->next = next; prev = next; num++; } - xhci_link_segments(prev, *first, type, chain_links); - *last = prev; + ring->last_seg = prev; + ring->last_seg->next = ring->first_seg; return 0; free_segments: - xhci_free_segments_for_ring(xhci, *first); + ring->last_seg = prev; + xhci_ring_segments_free(xhci, ring); return -ENOMEM; } @@ -373,9 +367,8 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, * Set the end flag and the cycle toggle bit on the last segment. * See section 4.9.1 and figures 15 and 16. */ -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, unsigned int cycle_state, - enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { struct xhci_ring *ring; int ret; @@ -392,18 +385,12 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (num_segs == 0) return ring; - ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, &ring->last_seg, num_segs, - cycle_state, type, max_packet, flags); + ret = xhci_alloc_segments_for_ring(xhci, ring, flags); if (ret) goto fail; - /* Only event ring does not use link TRB */ - if (type != TYPE_EVENT) { - /* See section 4.9.2.1 and 6.4.4.1 */ - ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= - cpu_to_le32(LINK_TOGGLE); - } - xhci_initialize_ring_info(ring, cycle_state); + xhci_initialize_ring_segments(xhci, ring); + xhci_initialize_ring_info(ring); trace_xhci_ring_alloc(ring); return ring; @@ -427,23 +414,29 @@ void xhci_free_endpoint_ring(struct xhci_hcd *xhci, int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_new_segs, gfp_t flags) { - struct xhci_segment *first; - struct xhci_segment *last; - int ret; + struct xhci_ring new_ring; + int ret; + + if (num_new_segs == 0) + return 0; - ret = xhci_alloc_segments_for_ring(xhci, &first, &last, num_new_segs, ring->cycle_state, - ring->type, ring->bounce_buf_len, flags); + new_ring.num_segs = num_new_segs; + new_ring.bounce_buf_len = ring->bounce_buf_len; + new_ring.type = ring->type; + ret = xhci_alloc_segments_for_ring(xhci, &new_ring, flags); if (ret) return -ENOMEM; + xhci_initialize_ring_segments(xhci, &new_ring); + if (ring->type == TYPE_STREAM) { - ret = xhci_update_stream_segment_mapping(ring->trb_address_map, - ring, first, last, flags); + ret = xhci_update_stream_segment_mapping(ring->trb_address_map, ring, + new_ring.first_seg, flags); if (ret) goto free_segments; } - xhci_link_rings(xhci, ring, first, last, num_new_segs); + xhci_link_rings(xhci, ring, &new_ring); trace_xhci_ring_expansion(ring); xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ring expansion succeed, now has %d segments", @@ -452,7 +445,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; free_segments: - xhci_free_segments_for_ring(xhci, first); + xhci_ring_segments_free(xhci, &new_ring); return ret; } @@ -642,8 +635,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, max_packet, - mem_flags); + xhci_ring_alloc(xhci, 2, TYPE_STREAM, max_packet, mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -658,6 +650,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, addr); ret = xhci_update_stream_mapping(cur_ring, mem_flags); + + trace_xhci_alloc_stream_info_ctx(stream_info, cur_stream); if (ret) { xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; @@ -984,7 +978,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, 0, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 2, TYPE_CTRL, 0, flags); if (!dev->eps[0].ring) goto fail; @@ -1461,7 +1455,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); + xhci_ring_alloc(xhci, 2, ring_type, max_packet, mem_flags); if (!virt_dev->eps[ep_index].new_ring) return -ENOMEM; @@ -2271,7 +2265,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) if (!ir) return NULL; - ir->event_ring = xhci_ring_alloc(xhci, segs, 1, TYPE_EVENT, 0, flags); + ir->event_ring = xhci_ring_alloc(xhci, segs, TYPE_EVENT, 0, flags); if (!ir->event_ring) { xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); kfree(ir); @@ -2473,7 +2467,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, 0, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2518,11 +2512,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; - /* - * XXX: Might need to set the Interrupter Moderation Register to - * something other than the default (~1ms minimum between interrupts). - * See section 5.5.1.2. - */ for (i = 0; i < MAX_HC_SLOTS; i++) xhci->devs[i] = NULL; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 3252e3d2d79cd..9048313444409 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -853,7 +853,7 @@ MODULE_DEVICE_TABLE(of, mtk_xhci_of_match); static struct platform_driver mtk_xhci_driver = { .probe = xhci_mtk_probe, - .remove_new = xhci_mtk_remove, + .remove = xhci_mtk_remove, .driver = { .name = "xhci-mtk", .pm = DEV_PM_OPS, diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cb07cee9ed0c7..b21474e814828 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -28,18 +28,18 @@ #define SPARSE_CNTL_ENABLE 0xC12C /* Device for a quirk */ -#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 -#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 +#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 +#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1100 0x1100 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400 -#define PCI_VENDOR_ID_ETRON 0x1b6f -#define PCI_DEVICE_ID_EJ168 0x7023 -#define PCI_DEVICE_ID_EJ188 0x7052 +#define PCI_VENDOR_ID_ETRON 0x1b6f +#define PCI_DEVICE_ID_ETRON_EJ168 0x7023 +#define PCI_DEVICE_ID_ETRON_EJ188 0x7052 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1 #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f @@ -82,9 +82,6 @@ #define PCI_DEVICE_ID_ASMEDIA_3042_XHCI 0x3042 #define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242 -#define PCI_DEVICE_ID_CADENCE 0x17CD -#define PCI_DEVICE_ID_CADENCE_SSP 0x0200 - static const char hcd_name[] = "xhci_hcd"; static struct hc_driver __read_mostly xhci_pci_hc_driver; @@ -150,14 +147,11 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) hcd->irq = 0; /* - * calculate number of MSI-X vectors supported. - * - HCS_MAX_INTRS: the max number of interrupts the host can handle, - * with max number of interrupters based on the xhci HCSPARAMS1. - * - num_online_cpus: maximum MSI-X vectors per CPUs core. - * Add additional 1 vector to ensure always available interrupt. + * Calculate number of MSI/MSI-X vectors supported. + * - max_interrupters: the max number of interrupts requested, capped to xhci HCSPARAMS1. + * - num_online_cpus: one vector per CPUs core, with at least one overall. */ - xhci->nvecs = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); + xhci->nvecs = min(num_online_cpus() + 1, xhci->max_interrupters); /* TODO: Check with MSI Soc for sysdev */ xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs, @@ -395,14 +389,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ168) { - xhci->quirks |= XHCI_RESET_ON_RESUME; - xhci->quirks |= XHCI_BROKEN_STREAMS; - } - if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ188) { + (pdev->device == PCI_DEVICE_ID_ETRON_EJ168 || + pdev->device == PCI_DEVICE_ID_ETRON_EJ188)) { + xhci->quirks |= XHCI_ETRON_HOST; xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_BROKEN_STREAMS; + xhci->quirks |= XHCI_NO_SOFT_RETRY; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && @@ -482,8 +474,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; } - if (pdev->vendor == PCI_DEVICE_ID_CADENCE && - pdev->device == PCI_DEVICE_ID_CADENCE_SSP) + if (pdev->vendor == PCI_VENDOR_ID_CDNS && + pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) xhci->quirks |= XHCI_CDNS_SCTX_QUIRK; /* xHC spec requires PCI devices to support D3hot and D3cold */ @@ -646,6 +638,9 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) dma_set_max_seg_size(&dev->dev, UINT_MAX); + if (device_property_read_bool(&dev->dev, "ti,pwron-active-high")) + pci_clear_and_set_config_dword(dev, 0xE0, 0, 1 << 22); + return 0; put_usb3_hcd: diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index ecaa75718e592..e6c9006bd5685 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -573,7 +573,7 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); static struct platform_driver usb_generic_xhci_driver = { .probe = xhci_generic_plat_probe, - .remove_new = xhci_plat_remove, + .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-hcd", diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 8b357647728c2..1cc082a3b7939 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -274,7 +274,7 @@ static int xhci_renesas_probe(struct platform_device *pdev) static struct platform_driver usb_xhci_renesas_driver = { .probe = xhci_renesas_probe, - .remove_new = xhci_plat_remove, + .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-renesas-hcd", diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 928b93ad1ee86..4cf5363875c70 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -52,6 +52,7 @@ * endpoint rings; it generates events on the event ring for these. */ +#include #include #include #include @@ -145,10 +146,8 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. */ -static void next_trb(struct xhci_hcd *xhci, - struct xhci_ring *ring, - struct xhci_segment **seg, - union xhci_trb **trb) +static void next_trb(struct xhci_segment **seg, + union xhci_trb **trb) { if (trb_is_link(*trb) || last_trb_on_seg(*seg, *trb)) { *seg = (*seg)->next; @@ -169,13 +168,16 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->type == TYPE_EVENT) { if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { ring->dequeue++; - goto out; + return; } if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue)) ring->cycle_state ^= 1; ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; - goto out; + + trace_xhci_inc_deq(ring); + + return; } /* All other rings have link trbs */ @@ -190,14 +192,13 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; + trace_xhci_inc_deq(ring); + if (link_trb_count++ > ring->num_segs) { xhci_warn(xhci, "Ring is an endless link TRB loop\n"); break; } } -out: - trace_xhci_inc_deq(ring); - return; } @@ -266,13 +267,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, ring->enqueue = ring->enq_seg->trbs; next = ring->enqueue; + trace_xhci_inc_enq(ring); + if (link_trb_count++ > ring->num_segs) { xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__); break; } } - - trace_xhci_inc_enq(ring); } /* @@ -426,7 +427,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, } } -/* Must be called with xhci->lock held, releases and aquires lock back */ +/* Must be called with xhci->lock held, releases and acquires lock back */ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) { struct xhci_segment *new_seg = xhci->cmd_ring->deq_seg; @@ -446,9 +447,9 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) * avoiding corrupting the command ring pointer in case the command ring * is stopped by the time the upper dword is written. */ - next_trb(xhci, NULL, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); if (trb_is_link(new_deq)) - next_trb(xhci, NULL, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); crcr = xhci_trb_virt_to_dma(new_seg, new_deq); xhci_write_64(xhci, crcr | CMD_RING_ABORT, &xhci->op_regs->cmd_ring); @@ -660,8 +661,8 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, /* * We want to find the pointer, segment and cycle state of the new trb - * (the one after current TD's last_trb). We know the cycle state at - * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * (the one after current TD's end_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and end_trb are * found. */ do { @@ -671,14 +672,14 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, if (td_last_trb_found) break; } - if (new_deq == td->last_trb) + if (new_deq == td->end_trb) td_last_trb_found = true; if (cycle_found && trb_is_link(new_deq) && link_trb_toggles_cycle(new_deq)) new_cycle ^= 0x1; - next_trb(xhci, ep_ring, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); /* Search wrapped around, bail out */ if (new_deq == ep->ring->dequeue) { @@ -740,23 +741,22 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, * (The last TRB actually points to the ring enqueue pointer, which is not part * of this TD.) This is used to remove partially enqueued isoc TDs from a ring. */ -static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, - struct xhci_td *td, bool flip_cycle) +static void td_to_noop(struct xhci_td *td, bool flip_cycle) { struct xhci_segment *seg = td->start_seg; - union xhci_trb *trb = td->first_trb; + union xhci_trb *trb = td->start_trb; while (1) { trb_to_noop(trb, TRB_TR_NOOP); /* flip cycle if asked to */ - if (flip_cycle && trb != td->first_trb && trb != td->last_trb) + if (flip_cycle && trb != td->start_trb && trb != td->end_trb) trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE); - if (trb == td->last_trb) + if (trb == td->end_trb) break; - next_trb(xhci, ep_ring, &seg, &trb); + next_trb(&seg, &trb); } } @@ -799,7 +799,7 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, DMA_FROM_DEVICE); - /* for in tranfers we need to copy the data from bounce to sg */ + /* for in transfers we need to copy the data from bounce to sg */ if (urb->num_sgs) { len = sg_pcopy_from_buffer(urb->sg, urb->num_sgs, seg->bounce_buf, seg->bounce_len, seg->bounce_offs); @@ -814,8 +814,8 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, seg->bounce_offs = 0; } -static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_ring *ep_ring, int status) +static void xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, + struct xhci_ring *ep_ring, int status) { struct urb *urb = NULL; @@ -858,10 +858,18 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, status = 0; xhci_giveback_urb_in_irq(xhci, td, status); } - - return 0; } +/* Give back previous TD and move on to the next TD. */ +static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ring, + u32 status) +{ + ring->dequeue = td->end_trb; + ring->deq_seg = td->end_seg; + inc_deq(xhci, ring); + + xhci_td_cleanup(xhci, td, ring, status); +} /* Complete the cancelled URBs we unlinked from td_list. */ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) @@ -972,13 +980,20 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) unsigned int slot_id = ep->vdev->slot_id; int err; + /* + * This is not going to work if the hardware is changing its dequeue + * pointers as we look at them. Completion handler will call us later. + */ + if (ep->ep_state & SET_DEQ_PENDING) + return 0; + xhci = ep->xhci; list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb), + td->start_seg, td->start_trb), td->urb->stream_id, td->urb); list_del_init(&td->td_list); ring = xhci_urb_to_transfer_ring(xhci, td->urb); @@ -1020,16 +1035,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) "Found multiple active URBs %p and %p in stream %u?\n", td->urb, cached_td->urb, td->urb->stream_id); - td_to_noop(xhci, ring, cached_td, false); + td_to_noop(cached_td, false); cached_td->cancel_status = TD_CLEARED; } - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARING_CACHE; cached_td = td; break; } } else { - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARED; } } @@ -1054,13 +1069,26 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) continue; xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", td->urb); - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARED; } } return 0; } +/* + * Erase queued TDs from transfer ring(s) and give back those the xHC didn't + * stop on. If necessary, queue commands to move the xHC off cancelled TDs it + * stopped on. Those will be given back later when the commands complete. + * + * Call under xhci->lock on a stopped endpoint. + */ +void xhci_process_cancelled_tds(struct xhci_virt_ep *ep) +{ + xhci_invalidate_cancelled_tds(ep); + xhci_giveback_invalidated_tds(ep); +} + /* * Returns the TD the endpoint ring halted on. * Only call for non-running rings without streams. @@ -1151,16 +1179,35 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, return; case EP_STATE_STOPPED: /* - * NEC uPD720200 sometimes sets this state and fails with - * Context Error while continuing to process TRBs. - * Be conservative and trust EP_CTX_STATE on other chips. + * Per xHCI 4.6.9, Stop Endpoint command on a Stopped + * EP is a Context State Error, and EP stays Stopped. + * + * But maybe it failed on Halted, and somebody ran Reset + * Endpoint later. EP state is now Stopped and EP_HALTED + * still set because Reset EP handler will run after us. + */ + if (ep->ep_state & EP_HALTED) + break; + /* + * On some HCs EP state remains Stopped for some tens of + * us to a few ms or more after a doorbell ring, and any + * new Stop Endpoint fails without aborting the restart. + * This handler may run quickly enough to still see this + * Stopped state, but it will soon change to Running. + * + * Assume this bug on unexpected Stop Endpoint failures. + * Keep retrying until the EP starts and stops again, on + * chips where this is known to help. Wait for 100ms. */ if (!(xhci->quirks & XHCI_NEC_HOST)) break; + if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100))) + break; fallthrough; case EP_STATE_RUNNING: /* Race, HW handled stop ep cmd before ep was running */ - xhci_dbg(xhci, "Stop ep completion ctx error, ep is running\n"); + xhci_dbg(xhci, "Stop ep completion ctx error, ctx_state %d\n", + GET_EP_CTX_STATE(ep_ctx)); command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { @@ -1338,8 +1385,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; + struct xhci_stream_ctx *stream_ctx; struct xhci_td *td, *tmp_td; - bool deferred = false; ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); @@ -1360,6 +1407,11 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, trace_xhci_handle_cmd_set_deq(slot_ctx); trace_xhci_handle_cmd_set_deq_ep(ep_ctx); + if (ep->ep_state & EP_HAS_STREAMS) { + stream_ctx = &ep->stream_info->stream_ctx_array[stream_id]; + trace_xhci_handle_cmd_set_deq_stream(ep->stream_info, stream_id); + } + if (cmd_comp_code != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; @@ -1396,9 +1448,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, u64 deq; /* 4.6.10 deq ptr is written to the stream ctx for streams */ if (ep->ep_state & EP_HAS_STREAMS) { - struct xhci_stream_ctx *ctx = - &ep->stream_info->stream_ctx_array[stream_id]; - deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + deq = le64_to_cpu(stream_ctx->stream_ring) & SCTX_DEQ_MASK; /* * Cadence xHCI controllers store some endpoint state @@ -1410,8 +1460,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, * To fix this issue driver must clear Rsvd0 field. */ if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) { - ctx->reserved[0] = 0; - ctx->reserved[1] = 0; + stream_ctx->reserved[0] = 0; + stream_ctx->reserved[1] = 0; } } else { deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; @@ -1440,8 +1490,6 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", __func__, td->urb); xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); - } else if (td->cancel_status == TD_CLEARING_CACHE_DEFERRED) { - deferred = true; } else { xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", __func__, td->urb, td->cancel_status); @@ -1452,11 +1500,15 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, ep->queued_deq_seg = NULL; ep->queued_deq_ptr = NULL; - if (deferred) { - /* We have more streams to clear */ + /* Check for deferred or newly cancelled TDs */ + if (!list_empty(&ep->cancelled_td_list)) { xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n", __func__); xhci_invalidate_cancelled_tds(ep); + /* Try to restart the endpoint if all is done */ + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + /* Start giving back any TDs invalidated above */ + xhci_giveback_invalidated_tds(ep); } else { /* Restart any rings with pending URBs */ xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__); @@ -1716,7 +1768,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cmd_dma = le64_to_cpu(event->cmd_trb); cmd_trb = xhci->cmd_ring->dequeue; - trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic); + trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic, cmd_dma); cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); @@ -2074,7 +2126,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_ad dma_addr_t end_trb_dma; struct xhci_segment *cur_seg; - start_dma = xhci_trb_virt_to_dma(td->start_seg, td->first_trb); + start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); cur_seg = td->start_seg; do { @@ -2084,7 +2136,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_ad end_seg_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->last_trb); + end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb); if (debug) xhci_warn(xhci, @@ -2184,9 +2236,9 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) return 0; } -static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - u32 trb_comp_code) +static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + u32 trb_comp_code) { struct xhci_ep_ctx *ep_ctx; @@ -2201,7 +2253,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * stopped TDs. A stopped TD may be restarted, so don't update * the ring dequeue pointer or take this TD off any lists yet. */ - return 0; + return; case COMP_USB_TRANSACTION_ERROR: case COMP_BABBLE_DETECTED_ERROR: case COMP_SPLIT_TRANSACTION_ERROR: @@ -2226,8 +2278,8 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, !list_empty(&td->cancelled_td_list)) { xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n", (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb)); - return 0; + td->start_seg, td->start_trb)); + return; } /* endpoint not halted, don't reset it */ break; @@ -2235,7 +2287,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, /* Almost same procedure as for STALL_ERROR below */ xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return 0; + return; case COMP_STALL_ERROR: /* * xhci internal endpoint state will go to a "halt" state for @@ -2252,28 +2304,22 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return 0; /* xhci_handle_halted_endpoint marked td cancelled */ + return; /* xhci_handle_halted_endpoint marked td cancelled */ default: break; } - /* Update ring dequeue pointer */ - ep_ring->dequeue = td->last_trb; - ep_ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep_ring); - - return xhci_td_cleanup(xhci, td, ep_ring, td->status); + xhci_dequeue_td(xhci, td, ep_ring, td->status); } -/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */ -static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, - union xhci_trb *stop_trb) +/* sum trb lengths from the first trb up to stop_trb, _excluding_ stop_trb */ +static u32 sum_trb_lengths(struct xhci_td *td, union xhci_trb *stop_trb) { u32 sum; - union xhci_trb *trb = ring->dequeue; - struct xhci_segment *seg = ring->deq_seg; + union xhci_trb *trb = td->start_trb; + struct xhci_segment *seg = td->start_seg; - for (sum = 0; trb != stop_trb; next_trb(xhci, ring, &seg, &trb)) { + for (sum = 0; trb != stop_trb; next_trb(&seg, &trb)) { if (!trb_is_noop(trb) && !trb_is_link(trb)) sum += TRB_LEN(le32_to_cpu(trb->generic.field[2])); } @@ -2283,9 +2329,9 @@ static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, /* * Process control tds, update urb status and actual_length. */ -static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; @@ -2364,7 +2410,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb_length_set = true; td->urb->actual_length = requested - remaining; xhci_dbg(xhci, "Waiting for status stage event\n"); - return 0; + return; } /* at status stage */ @@ -2372,15 +2418,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb->actual_length = requested; finish_td: - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* * Process isochronous tds, update urb packet status and actual_length. */ -static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct urb_priv *urb_priv; int idx; @@ -2425,7 +2471,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, fallthrough; case COMP_ISOCH_BUFFER_OVERRUN: frame->status = -EOVERFLOW; - if (ep_trb != td->last_trb) + if (ep_trb != td->end_trb) td->error_mid_td = true; break; case COMP_INCOMPATIBLE_DEVICE_ERROR: @@ -2435,14 +2481,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_USB_TRANSACTION_ERROR: frame->status = -EPROTO; sum_trbs_for_length = true; - if (ep_trb != td->last_trb) + if (ep_trb != td->end_trb) td->error_mid_td = true; break; case COMP_STOPPED: sum_trbs_for_length = true; break; case COMP_STOPPED_SHORT_PACKET: - /* field normally containing residue now contains tranferred */ + /* field normally containing residue now contains transferred */ frame->status = short_framestatus; requested = remaining; break; @@ -2462,7 +2508,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, goto finish_td; if (sum_trbs_for_length) - frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) + + frame->actual_length = sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; else frame->actual_length = requested; @@ -2471,17 +2517,16 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, finish_td: /* Don't give back TD yet if we encountered an error mid TD */ - if (td->error_mid_td && ep_trb != td->last_trb) { + if (td->error_mid_td && ep_trb != td->end_trb) { xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n"); td->urb_length_set = true; - return 0; + return; } - - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } -static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_virt_ep *ep, int status) +static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, + struct xhci_virt_ep *ep, int status) { struct urb_priv *urb_priv; struct usb_iso_packet_descriptor *frame; @@ -2497,20 +2542,15 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, /* calc actual length */ frame->actual_length = 0; - /* Update ring dequeue pointer */ - ep->ring->dequeue = td->last_trb; - ep->ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep->ring); - - return xhci_td_cleanup(xhci, td, ep->ring, status); + xhci_dequeue_td(xhci, td, ep->ring, status); } /* * Process bulk and interrupt tds, update urb status and actual_length. */ -static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_slot_ctx *slot_ctx; u32 trb_comp_code; @@ -2526,7 +2566,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_SUCCESS: ep->err_count = 0; /* handle success with untransferred data as short packet */ - if (ep_trb != td->last_trb || remaining) { + if (ep_trb != td->end_trb || remaining) { xhci_warn(xhci, "WARN Successful completion on short TX\n"); xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", td->urb->ep->desc.bEndpointAddress, @@ -2542,7 +2582,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, goto finish_td; case COMP_STOPPED_LENGTH_INVALID: /* stopped on ep trb with invalid length, exclude it */ - td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb); + td->urb->actual_length = sum_trb_lengths(td, ep_trb); goto finish_td; case COMP_USB_TRANSACTION_ERROR: if (xhci->quirks & XHCI_NO_SOFT_RETRY || @@ -2553,17 +2593,17 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; xhci_handle_halted_endpoint(xhci, ep, td, EP_SOFT_RESET); - return 0; + return; default: /* do nothing */ break; } - if (ep_trb == td->last_trb) + if (ep_trb == td->end_trb) td->urb->actual_length = requested - remaining; else td->urb->actual_length = - sum_trb_lengths(xhci, ep_ring, ep_trb) + + sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; finish_td: if (remaining > requested) { @@ -2572,7 +2612,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb->actual_length = 0; } - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */ @@ -2792,10 +2832,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_trb_dma, false)) { xhci_dbg(xhci, "Missing TD completion event after mid TD error\n"); - ep_ring->dequeue = td->last_trb; - ep_ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep_ring); - xhci_td_cleanup(xhci, td, ep_ring, td->status); + xhci_dequeue_td(xhci, td, ep_ring, td->status); } if (list_empty(&ep_ring->td_list)) { @@ -2889,7 +2926,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep_ring->last_td_was_short = false; ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)]; - trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb); + trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma); /* * No-op TRB could trigger interrupts in a case where a URB was killed @@ -2939,7 +2976,9 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter { u32 trb_type; - trace_xhci_handle_event(ir->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic, + xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue)); /* * Barrier between reading the TRB_CYCLE (valid) flag before, and any @@ -3162,7 +3201,8 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, wmb(); trb->field[3] = cpu_to_le32(field4); - trace_xhci_queue_trb(ring, trb); + trace_xhci_queue_trb(ring, trb, + xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue)); inc_enq(xhci, ring, more_trbs_coming); } @@ -3302,7 +3342,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, /* Add this TD to the tail of the endpoint ring's TD list */ list_add_tail(&td->td_list, &ep_ring->td_list); td->start_seg = ep_ring->enq_seg; - td->first_trb = ep_ring->enqueue; + td->start_trb = ep_ring->enqueue; return 0; } @@ -3641,8 +3681,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field &= ~TRB_CHAIN; field |= TRB_IOC; more_trbs_coming = false; - td->last_trb = ring->enqueue; - td->last_trb_seg = ring->enq_seg; + td->end_trb = ring->enqueue; + td->end_seg = ring->enq_seg; if (xhci_urb_suitable_for_idt(urb)) { memcpy(&send_addr, urb->transfer_buffer, trb_buff_len); @@ -3690,8 +3730,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, 1, urb, 1, mem_flags); - urb_priv->td[1].last_trb = ring->enqueue; - urb_priv->td[1].last_trb_seg = ring->enq_seg; + urb_priv->td[1].end_trb = ring->enqueue; + urb_priv->td[1].end_seg = ring->enq_seg; field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC; queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field); } @@ -3727,6 +3767,20 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (!urb->setup_packet) return -EINVAL; + if ((xhci->quirks & XHCI_ETRON_HOST) && + urb->dev->speed >= USB_SPEED_SUPER) { + /* + * If next available TRB is the Link TRB in the ring segment then + * enqueue a No Op TRB, this can prevent the Setup and Data Stage + * TRB to be breaked by the Link TRB. + */ + if (trb_is_link(ep_ring->enqueue + 1)) { + field = TRB_TYPE(TRB_TR_NOOP) | ep_ring->cycle_state; + queue_trb(xhci, ep_ring, false, 0, 0, + TRB_INTR_TARGET(0), field); + } + } + /* 1 TRB for setup, 1 for status */ num_trbs = 2; /* @@ -3815,8 +3869,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* Save the DMA address of the last TRB in the TD */ - td->last_trb = ep_ring->enqueue; - td->last_trb_seg = ep_ring->enq_seg; + td->end_trb = ep_ring->enqueue; + td->end_seg = ep_ring->enq_seg; /* Queue status TRB - see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 */ /* If the device sent data, the status stage is an OUT transfer */ @@ -4101,8 +4155,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_CHAIN; } else { more_trbs_coming = false; - td->last_trb = ep_ring->enqueue; - td->last_trb_seg = ep_ring->enq_seg; + td->end_trb = ep_ring->enqueue; + td->end_seg = ep_ring->enq_seg; field |= TRB_IOC; if (trb_block_event_intr(xhci, num_tds, i, ir)) field |= TRB_BEI; @@ -4168,14 +4222,14 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Use the first TD as a temporary variable to turn the TDs we've queued * into No-ops with a software-owned cycle bit. That way the hardware * won't accidentally start executing bogus TDs when we partially - * overwrite them. td->first_trb and td->start_seg are already set. + * overwrite them. td->start_trb and td->start_seg are already set. */ - urb_priv->td[0].last_trb = ep_ring->enqueue; + urb_priv->td[0].end_trb = ep_ring->enqueue; /* Every TRB except the first & last will have its cycle bit flipped. */ - td_to_noop(xhci, ep_ring, &urb_priv->td[0], true); + td_to_noop(&urb_priv->td[0], true); /* Reset the ring enqueue back to the first TRB and its cycle bit. */ - ep_ring->enqueue = urb_priv->td[0].first_trb; + ep_ring->enqueue = urb_priv->td[0].start_trb; ep_ring->enq_seg = urb_priv->td[0].start_seg; ep_ring->cycle_state = start_cycle; usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 76f228e7443cb..06ae193ec8740 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2664,7 +2664,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); static struct platform_driver tegra_xusb_driver = { .probe = tegra_xusb_probe, - .remove_new = tegra_xusb_remove, + .remove = tegra_xusb_remove, .shutdown = tegra_xusb_shutdown, .driver = { .name = "tegra-xusb", diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 24405315ffe6d..bfb5c5c170127 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -108,9 +108,10 @@ DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, ); DECLARE_EVENT_CLASS(xhci_log_trb, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb), + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma), TP_STRUCT__entry( + __field(dma_addr_t, dma) __field(u32, type) __field(u32, field0) __field(u32, field1) @@ -118,51 +119,54 @@ DECLARE_EVENT_CLASS(xhci_log_trb, __field(u32, field3) ), TP_fast_assign( + __entry->dma = dma; __entry->type = ring->type; __entry->field0 = le32_to_cpu(trb->field[0]); __entry->field1 = le32_to_cpu(trb->field[1]); __entry->field2 = le32_to_cpu(trb->field[2]); __entry->field3 = le32_to_cpu(trb->field[3]); ), - TP_printk("%s: %s", xhci_ring_type_string(__entry->type), + TP_printk("%s: @%pad %s", + xhci_ring_type_string(__entry->type), &__entry->dma, xhci_decode_trb(__get_buf(XHCI_MSG_MAX), XHCI_MSG_MAX, __entry->field0, __entry->field1, __entry->field2, __entry->field3) ) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_event, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_command, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_transfer, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_queue_trb, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) + ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DECLARE_EVENT_CLASS(xhci_log_free_virt_dev, @@ -310,6 +314,37 @@ DEFINE_EVENT(xhci_log_urb, xhci_urb_dequeue, TP_ARGS(urb) ); +DECLARE_EVENT_CLASS(xhci_log_stream_ctx, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id), + TP_STRUCT__entry( + __field(unsigned int, stream_id) + __field(u64, stream_ring) + __field(dma_addr_t, ctx_array_dma) + + ), + TP_fast_assign( + __entry->stream_id = stream_id; + __entry->stream_ring = le64_to_cpu(info->stream_ctx_array[stream_id].stream_ring); + __entry->ctx_array_dma = info->ctx_array_dma + stream_id * 16; + + ), + TP_printk("stream %u ctx @%pad: SCT %llu deq %llx", __entry->stream_id, + &__entry->ctx_array_dma, CTX_TO_SCT(__entry->stream_ring), + __entry->stream_ring + ) +); + +DEFINE_EVENT(xhci_log_stream_ctx, xhci_alloc_stream_info_ctx, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id) +); + +DEFINE_EVENT(xhci_log_stream_ctx, xhci_handle_cmd_set_deq_stream, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id) +); + DECLARE_EVENT_CLASS(xhci_log_ep_ctx, TP_PROTO(struct xhci_ep_ctx *ctx), TP_ARGS(ctx), @@ -454,8 +489,6 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __field(void *, ring) __field(dma_addr_t, enq) __field(dma_addr_t, deq) - __field(dma_addr_t, enq_seg) - __field(dma_addr_t, deq_seg) __field(unsigned int, num_segs) __field(unsigned int, stream_id) __field(unsigned int, cycle_state) @@ -466,17 +499,15 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __entry->type = ring->type; __entry->num_segs = ring->num_segs; __entry->stream_id = ring->stream_id; - __entry->enq_seg = ring->enq_seg->dma; - __entry->deq_seg = ring->deq_seg->dma; __entry->cycle_state = ring->cycle_state; __entry->bounce_buf_len = ring->bounce_buf_len; __entry->enq = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); __entry->deq = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); ), - TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d bounce %d cycle %d", + TP_printk("%s %p: enq %pad deq %pad segs %d stream %d bounce %d cycle %d", xhci_ring_type_string(__entry->type), __entry->ring, - &__entry->enq, &__entry->enq_seg, - &__entry->deq, &__entry->deq_seg, + &__entry->enq, + &__entry->deq, __entry->num_segs, __entry->stream_id, __entry->bounce_buf_len, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 899c0effb5d3c..5ebde8cae4fc4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -8,6 +8,7 @@ * Some code borrowed from the Linux EHCI driver. */ +#include #include #include #include @@ -40,15 +41,15 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { - struct xhci_segment *seg = ring->first_seg; + struct xhci_segment *seg; if (!td || !td->start_seg) return false; - do { + + xhci_for_each_ring_seg(ring->first_seg, seg) { if (seg == td->start_seg) return true; - seg = seg->next; - } while (seg && seg != ring->first_seg); + } return false; } @@ -473,14 +474,7 @@ static int xhci_init(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init"); spin_lock_init(&xhci->lock); - if (xhci->hci_version == 0x95 && link_quirk) { - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "QUIRK: Not clearing Link TRB chain bits."); - xhci->quirks |= XHCI_LINK_TRB_QUIRK; - } else { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "xHCI doesn't need link TRB QUIRK"); - } + retval = xhci_mem_init(xhci, GFP_KERNEL); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init"); @@ -785,16 +779,10 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) struct xhci_segment *seg; ring = xhci->cmd_ring; - seg = ring->deq_seg; - do { - memset(seg->trbs, 0, - sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); - seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= - cpu_to_le32(~TRB_CYCLE); - seg = seg->next; - } while (seg != ring->deq_seg); - - xhci_initialize_ring_info(ring, 1); + xhci_for_each_ring_seg(ring->first_seg, seg) + memset(seg->trbs, 0, sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); + + xhci_initialize_ring_info(ring); /* * Reset the hardware dequeue pointer. * Yes, this will need to be re-written after resume, but we're paranoid @@ -1756,7 +1744,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) urb->ep->desc.bEndpointAddress, (unsigned long long) xhci_trb_virt_to_dma( urb_priv->td[i].start_seg, - urb_priv->td[i].first_trb)); + urb_priv->td[i].start_trb)); for (; i < urb_priv->num_tds; i++) { td = &urb_priv->td[i]; @@ -1768,15 +1756,27 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } } - /* Queue a stop endpoint command, but only if this is - * the first cancellation to be handled. - */ - if (!(ep->ep_state & EP_STOP_CMD_PENDING)) { + /* These completion handlers will sort out cancelled TDs for us */ + if (ep->ep_state & (EP_STOP_CMD_PENDING | EP_HALTED | SET_DEQ_PENDING)) { + xhci_dbg(xhci, "Not queuing Stop Endpoint on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); + goto done; + } + + /* In this case no commands are pending but the endpoint is stopped */ + if (ep->ep_state & EP_CLEARING_TT) { + /* and cancelled TDs can be given back right away */ + xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); + xhci_process_cancelled_tds(ep); + } else { + /* Otherwise, queue a new Stop Endpoint command */ command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { ret = -ENOMEM; goto done; } + ep->stop_time = jiffies; ep->ep_state |= EP_STOP_CMD_PENDING; xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id, ep_index, 0); @@ -2794,6 +2794,51 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, return -ENOMEM; } +/* + * Synchronous XHCI stop endpoint helper. Issues the stop endpoint command and + * waits for the command completion before returning. This does not call + * xhci_handle_cmd_stop_ep(), which has additional handling for 'context error' + * cases, along with transfer ring cleanup. + * + * xhci_stop_endpoint_sync() is intended to be utilized by clients that manage + * their own transfer ring, such as offload situations. + */ +int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, + gfp_t gfp_flags) +{ + struct xhci_command *command; + unsigned long flags; + int ret; + + command = xhci_alloc_command(xhci, true, gfp_flags); + if (!command) + return -ENOMEM; + + spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, + ep->ep_index, suspend); + if (ret < 0) { + spin_unlock_irqrestore(&xhci->lock, flags); + goto out; + } + + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(command->completion); + + /* No handling for COMP_CONTEXT_STATE_ERROR done at command completion*/ + if (command->status == COMP_COMMAND_ABORTED || + command->status == COMP_COMMAND_RING_STOPPED) { + xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); + ret = -ETIME; + } +out: + xhci_free_command(xhci, command); + + return ret; +} +EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync); /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. @@ -3692,6 +3737,8 @@ void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci, xhci->num_active_eps); } +static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); + /* * This submits a Reset Device Command, which will set the device state to 0, * set the device address to 0, and disable all the endpoints except the default @@ -3762,6 +3809,23 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, SLOT_STATE_DISABLED) return 0; + if (xhci->quirks & XHCI_ETRON_HOST) { + /* + * Obtaining a new device slot to inform the xHCI host that + * the USB device has been reset. + */ + ret = xhci_disable_slot(xhci, udev->slot_id); + xhci_free_virt_device(xhci, udev->slot_id); + if (!ret) { + ret = xhci_alloc_dev(hcd, udev); + if (ret == 1) + ret = 0; + else + ret = -EINVAL; + } + return ret; + } + trace_xhci_discover_or_reset_device(slot_ctx); xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id); @@ -5251,6 +5315,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x96) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; + if (xhci->hci_version == 0x95 && link_quirk) { + xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits"); + xhci->quirks |= XHCI_LINK_TRB_QUIRK; + } + /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f0fb696d56198..4914f0a10cff4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -554,6 +554,7 @@ struct xhci_stream_ctx { /* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ #define SCT_FOR_CTX(p) (((p) & 0x7) << 1) +#define CTX_TO_SCT(p) (((p) >> 1) & 0x7) /* Secondary stream array type, dequeue pointer is to a transfer ring */ #define SCT_SEC_TR 0 /* Primary stream array type, dequeue pointer is to a transfer ring */ @@ -690,6 +691,7 @@ struct xhci_virt_ep { /* Bandwidth checking storage */ struct xhci_bw_info bw_info; struct list_head bw_endpoint_list; + unsigned long stop_time; /* Isoch Frame ID checking storage */ int next_frame_id; /* Use new Isoch TRB layout needed for extended TBC support */ @@ -1023,9 +1025,6 @@ enum xhci_setup_dev { /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) -/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ -#define TRB_TBC(p) (((p) & 0x3) << 7) -#define TRB_TLBPC(p) (((p) & 0xf) << 16) /* Cycle bit - indicates TRB ownership by HC or HCD */ #define TRB_CYCLE (1<<0) @@ -1059,6 +1058,12 @@ enum xhci_setup_dev { /* Isochronous TRB specific fields */ #define TRB_SIA (1<<31) #define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20) +#define GET_FRAME_ID(p) (((p) >> 20) & 0x7ff) +/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define GET_TBC(p) (((p) >> 7) & 0x3) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) +#define GET_TLBPC(p) (((p) >> 16) & 0xf) /* TRB cache size for xHC with TRB cache */ #define TRB_CACHE_SIZE_HS 8 @@ -1259,6 +1264,9 @@ static inline const char *xhci_trb_type_string(u8 type) #define AVOID_BEI_INTERVAL_MIN 8 #define AVOID_BEI_INTERVAL_MAX 32 +#define xhci_for_each_ring_seg(head, seg) \ + for (seg = head; seg != NULL; seg = (seg->next != head ? seg->next : NULL)) + struct xhci_segment { union xhci_trb *trbs; /* private to HCD */ @@ -1287,9 +1295,9 @@ struct xhci_td { enum xhci_cancelled_td_status cancel_status; struct urb *urb; struct xhci_segment *start_seg; - union xhci_trb *first_trb; - union xhci_trb *last_trb; - struct xhci_segment *last_trb_seg; + union xhci_trb *start_trb; + struct xhci_segment *end_seg; + union xhci_trb *end_trb; struct xhci_segment *bounce_seg; /* actual_length of the URB has already been set */ bool urb_length_set; @@ -1624,6 +1632,7 @@ struct xhci_hcd { #define XHCI_ZHAOXIN_HOST BIT_ULL(46) #define XHCI_WRITE_64_HI_LO BIT_ULL(47) #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) +#define XHCI_ETRON_HOST BIT_ULL(49) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1788,14 +1797,12 @@ void xhci_slot_copy(struct xhci_hcd *xhci, int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, unsigned int cycle_state, +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, enum xhci_ring_type type, unsigned int max_packet, gfp_t flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_trbs, gfp_t flags); -void xhci_initialize_ring_info(struct xhci_ring *ring, - unsigned int cycle_state); +void xhci_initialize_ring_info(struct xhci_ring *ring); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1913,6 +1920,9 @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, void xhci_cleanup_command_queue(struct xhci_hcd *xhci); void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); unsigned int count_trbs(u64 addr, u64 len); +int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + int suspend, gfp_t gfp_flags); +void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, @@ -2070,7 +2080,6 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_NORMAL: - case TRB_ISOC: case TRB_EVENT_DATA: case TRB_TR_NOOP: snprintf(str, size, @@ -2087,7 +2096,25 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_ENT ? 'E' : 'e', field3 & TRB_CYCLE ? 'C' : 'c'); break; - + case TRB_ISOC: + snprintf(str, size, + "Buffer %08x%08x length %d TD size/TBC %d intr %d type '%s' TBC %u TLBPC %u frame_id %u flags %c:%c:%c:%c:%c:%c:%c:%c:%c", + field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + xhci_trb_type_string(type), + GET_TBC(field3), + GET_TLBPC(field3), + GET_FRAME_ID(field3), + field3 & TRB_SIA ? 'S' : 's', + field3 & TRB_BEI ? 'B' : 'b', + field3 & TRB_IDT ? 'I' : 'i', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); + break; case TRB_CMD_NOOP: case TRB_ENABLE_SLOT: snprintf(str, size, diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig index 2ed2b73291d18..e19178f3cdd39 100644 --- a/drivers/usb/isp1760/Kconfig +++ b/drivers/usb/isp1760/Kconfig @@ -26,7 +26,7 @@ config USB_ISP1761_UDC if USB_ISP1760 choice - bool "ISP1760 Mode Selection" + prompt "ISP1760 Mode Selection" default USB_ISP1760_DUAL_ROLE if (USB && USB_GADGET) default USB_ISP1760_HOST_ROLE if (USB && !USB_GADGET) default USB_ISP1760_GADGET_ROLE if (!USB && USB_GADGET) diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c index fe1e3985419a2..a64190addba6f 100644 --- a/drivers/usb/isp1760/isp1760-if.c +++ b/drivers/usb/isp1760/isp1760-if.c @@ -263,7 +263,7 @@ MODULE_DEVICE_TABLE(of, isp1760_of_match); static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, - .remove_new = isp1760_plat_remove, + .remove = isp1760_plat_remove, .driver = { .name = "isp1760", .of_match_table = of_match_ptr(isp1760_of_match), diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 6fb5140e29b9d..225863321dc47 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -27,6 +27,8 @@ static struct usb_class_driver chaoskey_class; static int chaoskey_rng_read(struct hwrng *rng, void *data, size_t max, bool wait); +static DEFINE_MUTEX(chaoskey_list_lock); + #define usb_dbg(usb_if, format, arg...) \ dev_dbg(&(usb_if)->dev, format, ## arg) @@ -233,6 +235,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) usb_deregister_dev(interface, &chaoskey_class); usb_set_intfdata(interface, NULL); + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); dev->present = false; @@ -244,6 +247,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) } else mutex_unlock(&dev->lock); + mutex_unlock(&chaoskey_list_lock); usb_dbg(interface, "disconnect done"); } @@ -251,6 +255,7 @@ static int chaoskey_open(struct inode *inode, struct file *file) { struct chaoskey *dev; struct usb_interface *interface; + int rv = 0; /* get the interface from minor number and driver information */ interface = usb_find_interface(&chaoskey_driver, iminor(inode)); @@ -266,18 +271,23 @@ static int chaoskey_open(struct inode *inode, struct file *file) } file->private_data = dev; + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); - ++dev->open; + if (dev->present) + ++dev->open; + else + rv = -ENODEV; mutex_unlock(&dev->lock); + mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "open success"); - return 0; + return rv; } static int chaoskey_release(struct inode *inode, struct file *file) { struct chaoskey *dev = file->private_data; struct usb_interface *interface; + int rv = 0; if (dev == NULL) return -ENODEV; @@ -286,14 +296,15 @@ static int chaoskey_release(struct inode *inode, struct file *file) usb_dbg(interface, "release"); + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); usb_dbg(interface, "open count at release is %d", dev->open); if (dev->open <= 0) { usb_dbg(interface, "invalid open count (%d)", dev->open); - mutex_unlock(&dev->lock); - return -ENODEV; + rv = -ENODEV; + goto bail; } --dev->open; @@ -302,13 +313,15 @@ static int chaoskey_release(struct inode *inode, struct file *file) if (dev->open == 0) { mutex_unlock(&dev->lock); chaoskey_free(dev); - } else - mutex_unlock(&dev->lock); - } else - mutex_unlock(&dev->lock); - + goto destruction; + } + } +bail: + mutex_unlock(&dev->lock); +destruction: + mutex_unlock(&chaoskey_list_lock); usb_dbg(interface, "release success"); - return 0; + return rv; } static void chaos_read_callback(struct urb *urb) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 6d28467ce3522..365c100693458 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -277,28 +277,45 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, struct iowarrior *dev; int read_idx; int offset; + int retval; dev = file->private_data; + if (file->f_flags & O_NONBLOCK) { + retval = mutex_trylock(&dev->mutex); + if (!retval) + return -EAGAIN; + } else { + retval = mutex_lock_interruptible(&dev->mutex); + if (retval) + return -ERESTARTSYS; + } + /* verify that the device wasn't unplugged */ - if (!dev || !dev->present) - return -ENODEV; + if (!dev->present) { + retval = -ENODEV; + goto exit; + } dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", dev->minor, count); /* read count must be packet size (+ time stamp) */ if ((count != dev->report_size) - && (count != (dev->report_size + 1))) - return -EINVAL; + && (count != (dev->report_size + 1))) { + retval = -EINVAL; + goto exit; + } /* repeat until no buffer overrun in callback handler occur */ do { atomic_set(&dev->overflow_flag, 0); if ((read_idx = read_index(dev)) == -1) { /* queue empty */ - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto exit; + } else { //next line will return when there is either new data, or the device is unplugged int r = wait_event_interruptible(dev->read_wait, @@ -309,28 +326,37 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, -1)); if (r) { //we were interrupted by a signal - return -ERESTART; + retval = -ERESTART; + goto exit; } if (!dev->present) { //The device was unplugged - return -ENODEV; + retval = -ENODEV; + goto exit; } if (read_idx == -1) { // Can this happen ??? - return 0; + retval = 0; + goto exit; } } } offset = read_idx * (dev->report_size + 1); if (copy_to_user(buffer, dev->read_queue + offset, count)) { - return -EFAULT; + retval = -EFAULT; + goto exit; } } while (atomic_read(&dev->overflow_flag)); read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; atomic_set(&dev->read_idx, read_idx); + mutex_unlock(&dev->mutex); return count; + +exit: + mutex_unlock(&dev->mutex); + return retval; } /* @@ -885,7 +911,6 @@ static int iowarrior_probe(struct usb_interface *interface, static void iowarrior_disconnect(struct usb_interface *interface) { struct iowarrior *dev = usb_get_intfdata(interface); - int minor = dev->minor; usb_deregister_dev(interface, &iowarrior_class); @@ -909,9 +934,6 @@ static void iowarrior_disconnect(struct usb_interface *interface) mutex_unlock(&dev->mutex); iowarrior_delete(dev); } - - dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", - minor - IOWARRIOR_MINOR_BASE); } /* usb specific object needed to register this driver with the usb subsystem */ diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 75dfdca04ff1c..36b11127280f3 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -473,7 +473,7 @@ static const struct dev_pm_ops __maybe_unused onboard_dev_pm_ops = { static struct platform_driver onboard_dev_driver = { .probe = onboard_dev_probe, - .remove_new = onboard_dev_remove, + .remove = onboard_dev_remove, .driver = { .name = "onboard-usb-dev", diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 19906301a4eb8..83079c414b4f2 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -239,7 +239,7 @@ MODULE_DEVICE_TABLE(of, eud_dt_match); static struct platform_driver eud_driver = { .probe = eud_probe, - .remove_new = eud_remove, + .remove = eud_remove, .driver = { .name = "qcom_eud", .dev_groups = eud_groups, diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index 01ceafc4ab78c..d9c21f7830557 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -332,14 +332,11 @@ static int ljca_send(struct ljca_adapter *adap, u8 type, u8 cmd, ret = usb_bulk_msg(adap->usb_dev, adap->tx_pipe, header, msg_len, &transferred, LJCA_WRITE_TIMEOUT_MS); - - usb_autopm_put_interface(adap->intf); - if (ret < 0) - goto out; + goto out_put; if (transferred != msg_len) { ret = -EIO; - goto out; + goto out_put; } if (ack) { @@ -347,11 +344,14 @@ static int ljca_send(struct ljca_adapter *adap, u8 type, u8 cmd, timeout); if (!ret) { ret = -ETIMEDOUT; - goto out; + goto out_put; } } ret = adap->actual_length; +out_put: + usb_autopm_put_interface(adap->intf); + out: spin_lock_irqsave(&adap->lock, flags); adap->ex_buf = NULL; @@ -811,6 +811,14 @@ static int ljca_probe(struct usb_interface *interface, if (ret) goto err_free; + /* + * This works around problems with ov2740 initialization on some + * Lenovo platforms. The autosuspend delay, has to be smaller than + * the delay after setting the reset_gpio line in ov2740_resume(). + * Otherwise the sensor randomly fails to initialize. + */ + pm_runtime_set_autosuspend_delay(&usb_dev->dev, 10); + usb_enable_autosuspend(usb_dev); return 0; diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 3b33e4878c608..322e59381b783 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -423,7 +423,7 @@ static struct platform_driver usb3503_platform_driver = { .pm = pm_ptr(&usb3503_platform_pm_ops), }, .probe = usb3503_platform_probe, - .remove_new = usb3503_platform_remove, + .remove = usb3503_platform_remove, }; static int __init usb3503_init(void) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index caf65f8294db2..8d379ae835bc4 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2021,7 +2021,8 @@ static struct urb *iso_alloc_urb( for (i = 0; i < packets; i++) { /* here, only the last packet will be short */ - urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp); + urb->iso_frame_desc[i].length = min_t(unsigned int, + bytes, maxp); bytes -= urb->iso_frame_desc[i].length; urb->iso_frame_desc[i].offset = maxp * i; diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 6aebc736a80c6..70dff0db5354f 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -441,7 +441,10 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer, if (count == 0) goto error; - mutex_lock(&dev->io_mutex); + retval = mutex_lock_interruptible(&dev->io_mutex); + if (retval < 0) + return -EINTR; + if (dev->disconnected) { /* already disconnected */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index afb71c18415dd..c93b43f5bc461 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -823,7 +823,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, ep = MON_OFF2HDR(rp, rp->b_out); if (rp->b_read < hdrbytes) { - step_len = min(nbytes, (size_t)(hdrbytes - rp->b_read)); + step_len = min_t(size_t, nbytes, hdrbytes - rp->b_read); ptr = ((char *)ep) + rp->b_read; if (step_len && copy_to_user(buf, ptr, step_len)) { mutex_unlock(&rp->fetch_lock); diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig index bf98fd36341da..d281da1cdbccf 100644 --- a/drivers/usb/mtu3/Kconfig +++ b/drivers/usb/mtu3/Kconfig @@ -21,7 +21,7 @@ config USB_MTU3 if USB_MTU3 choice - bool "MTU3 Mode Selection" + prompt "MTU3 Mode Selection" default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET) default USB_MTU3_HOST if (USB && !USB_GADGET) default USB_MTU3_GADGET if (!USB && USB_GADGET) diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 6858ed9fc3b2f..7b5a431acb565 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -307,7 +307,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled) goto out; - if (of_property_read_bool(node, "extcon")) { + if (of_property_present(node, "extcon")) { otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0); if (IS_ERR(otg_sx->edev)) { return dev_err_probe(dev, PTR_ERR(otg_sx->edev), @@ -621,7 +621,7 @@ MODULE_DEVICE_TABLE(of, mtu3_of_match); static struct platform_driver mtu3_driver = { .probe = mtu3_probe, - .remove_new = mtu3_remove, + .remove = mtu3_remove, .driver = { .name = MTU3_DRIVER_NAME, .pm = DEV_PM_OPS, diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 9a8cf3de06170..9e45d12b81d38 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -29,7 +29,7 @@ config USB_MUSB_HDRC if USB_MUSB_HDRC choice - bool "MUSB Mode Selection" + prompt "MUSB Mode Selection" default USB_MUSB_DUAL_ROLE if (USB && USB_GADGET) default USB_MUSB_HOST if (USB && !USB_GADGET) default USB_MUSB_GADGET if (!USB && USB_GADGET) diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 953094c1930c8..f772aa272bea6 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -633,7 +633,7 @@ MODULE_DEVICE_TABLE(of, da8xx_id_table); static struct platform_driver da8xx_driver = { .probe = da8xx_probe, - .remove_new = da8xx_remove, + .remove = da8xx_remove, .driver = { .name = "musb-da8xx", .pm = &da8xx_pm_ops, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index b38df9226278b..acdeb1117cd3f 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -325,7 +325,7 @@ MODULE_DEVICE_TABLE(of, jz4740_musb_of_match); static struct platform_driver jz4740_driver = { .probe = jz4740_probe, - .remove_new = jz4740_remove, + .remove = jz4740_remove, .driver = { .name = "musb-jz4740", .of_match_table = jz4740_musb_of_match, diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 63c86c046b986..aa988d74b58d0 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -523,7 +523,7 @@ MODULE_DEVICE_TABLE(of, mtk_musb_match); static struct platform_driver mtk_musb_driver = { .probe = mtk_musb_probe, - .remove_new = mtk_musb_remove, + .remove = mtk_musb_remove, .driver = { .name = "musb-mtk", .of_match_table = of_match_ptr(mtk_musb_match), diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c index 00e13214aa766..7edc8429b2749 100644 --- a/drivers/usb/musb/mpfs.c +++ b/drivers/usb/musb/mpfs.c @@ -369,7 +369,7 @@ MODULE_DEVICE_TABLE(of, mpfs_id_table); static struct platform_driver mpfs_musb_driver = { .probe = mpfs_probe, - .remove_new = mpfs_remove, + .remove = mpfs_remove, .driver = { .name = "mpfs-musb", .of_match_table = of_match_ptr(mpfs_id_table) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b24adb5b399fc..03b1154a6014a 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1387,7 +1387,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, /* expect hw_ep has already been zero-initialized */ - size = ffs(max(maxpacket, (u16) 8)) - 1; + size = ffs(max_t(u16, maxpacket, 8)) - 1; maxpacket = 1 << size; c_size = size - 3; @@ -2953,7 +2953,7 @@ static struct platform_driver musb_driver = { .dev_groups = musb_groups, }, .probe = musb_probe, - .remove_new = musb_remove, + .remove = musb_remove, }; module_platform_driver(musb_driver); diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9c7a8bbc0542c..2542239ec64ea 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -1032,7 +1032,7 @@ static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); static struct platform_driver dsps_usbss_driver = { .probe = dsps_probe, - .remove_new = dsps_remove, + .remove = dsps_remove, .driver = { .name = "musb-dsps", .pm = &dsps_pm_ops, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index bdf13911a1e59..c6076df0d50cc 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1161,12 +1161,19 @@ void musb_free_request(struct usb_ep *ep, struct usb_request *req) */ void musb_ep_restart(struct musb *musb, struct musb_request *req) { + u16 csr; + void __iomem *epio = req->ep->hw_ep->regs; + trace_musb_req_start(req); musb_ep_select(musb->mregs, req->epnum); - if (req->tx) + if (req->tx) { txstate(musb, req); - else - rxstate(musb, req); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + } } static int musb_ep_restart_resume_work(struct musb *musb, void *data) diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 6d7336727388b..f0786f8fbb25c 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -533,7 +533,7 @@ static void ep0_txstate(struct musb *musb) /* load the data */ fifo_src = (u8 *) request->buf + request->actual; - fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE, + fifo_count = min_t(unsigned, MUSB_EP0_FIFOSIZE, request->length - request->actual); musb_write_fifo(&musb->endpoints[0], fifo_count, fifo_src); request->actual += fifo_count; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index bc45077811679..732ba981e607e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -798,10 +798,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, } if (can_bulk_split(musb, qh->type)) - load_count = min((u32) hw_ep->max_packet_sz_tx, - len); + load_count = min_t(u32, hw_ep->max_packet_sz_tx, len); else - load_count = min((u32) packet_sz, len); + load_count = min_t(u32, packet_sz, len); if (dma_channel && musb_tx_dma_program(dma_controller, hw_ep, qh, urb, offset, len)) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b4a4c1df4e0d9..2970967a4fd28 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -608,7 +608,7 @@ MODULE_DEVICE_TABLE(of, omap2430_id_table); static struct platform_driver omap2430_driver = { .probe = omap2430_probe, - .remove_new = omap2430_remove, + .remove = omap2430_remove, .driver = { .name = "musb-omap2430", .pm = DEV_PM_OPS, diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index 05b6e7e52e027..eac1cde86be3f 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -857,7 +857,7 @@ MODULE_DEVICE_TABLE(of, sunxi_musb_match); static struct platform_driver sunxi_musb_driver = { .probe = sunxi_musb_probe, - .remove_new = sunxi_musb_remove, + .remove = sunxi_musb_remove, .driver = { .name = "musb-sunxi", .of_match_table = sunxi_musb_match, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 461587629bf26..90b760a95e4e3 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1290,7 +1290,7 @@ static void tusb_remove(struct platform_device *pdev) static struct platform_driver tusb_driver = { .probe = tusb_probe, - .remove_new = tusb_remove, + .remove = tusb_remove, .driver = { .name = "musb-tusb", }, diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index c8d9d2a1d2f03..8c2a43d992f5c 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -355,7 +355,7 @@ MODULE_DEVICE_TABLE(of, ux500_match); static struct platform_driver ux500_driver = { .probe = ux500_probe, - .remove_new = ux500_remove, + .remove = ux500_remove, .driver = { .name = "musb-ux500", .pm = &ux500_pm_ops, diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 408f47e390259..6a98aeeeae319 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -987,7 +987,7 @@ MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); static struct platform_driver ab8500_usb_driver = { .probe = ab8500_usb_probe, - .remove_new = ab8500_usb_remove, + .remove = ab8500_usb_remove, .id_table = ab8500_usb_devtype, .driver = { .name = "abx5x0-usb", diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 6db88e00f127c..ca9353dad71aa 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -133,7 +133,7 @@ MODULE_DEVICE_TABLE(of, am335x_phy_ids); static struct platform_driver am335x_phy_driver = { .probe = am335x_phy_probe, - .remove_new = am335x_phy_remove, + .remove = am335x_phy_remove, .driver = { .name = "am335x-phy-driver", .pm = &am335x_pm_ops, diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index c5c6b818998ee..42c42e1932326 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -1002,7 +1002,7 @@ static void fsl_otg_remove(struct platform_device *pdev) struct platform_driver fsl_otg_driver = { .probe = fsl_otg_probe, - .remove_new = fsl_otg_remove, + .remove = fsl_otg_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index e7d50e0a16123..6c3ececf91375 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -345,7 +345,7 @@ MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); static struct platform_driver usb_phy_generic_driver = { .probe = usb_phy_generic_probe, - .remove_new = usb_phy_generic_remove, + .remove = usb_phy_generic_remove, .driver = { .name = "usb_phy_generic", .of_match_table = nop_xceiv_dt_ids, diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 5428b2b67de1f..ce09e789afd88 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -385,7 +385,7 @@ static struct platform_driver gpio_vbus_driver = { .of_match_table = gpio_vbus_of_match, }, .probe = gpio_vbus_probe, - .remove_new = gpio_vbus_remove, + .remove = gpio_vbus_remove, }; module_platform_driver(gpio_vbus_driver); diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 993d7525a1021..f9b5c411aee4e 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -25,7 +25,7 @@ struct isp1301 { #define phy_to_isp(p) (container_of((p), struct isp1301, phy)) static const struct i2c_device_id isp1301_id[] = { - { "isp1301", 0 }, + { "isp1301" }, { } }; MODULE_DEVICE_TABLE(i2c, isp1301_id); diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index bd9a98ad1b309..51155c5513d3c 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -103,7 +103,7 @@ MODULE_DEVICE_TABLE(of, keystone_usbphy_ids); static struct platform_driver keystone_usbphy_driver = { .probe = keystone_usbphy_probe, - .remove_new = keystone_usbphy_remove, + .remove = keystone_usbphy_remove, .driver = { .name = "keystone-usbphy", .of_match_table = keystone_usbphy_ids, diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index df7c27474a75a..a7a102f2e163f 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -867,7 +867,7 @@ static int mv_otg_resume(struct platform_device *pdev) static struct platform_driver mv_otg_driver = { .probe = mv_otg_probe, - .remove_new = mv_otg_remove, + .remove = mv_otg_remove, .driver = { .name = driver_name, .dev_groups = mv_otg_groups, diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index cc4156c1b148e..7490f1798b461 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -952,7 +952,7 @@ static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, - .remove_new = mxs_phy_remove, + .remove = mxs_phy_remove, .driver = { .name = DRIVER_NAME, .of_match_table = mxs_phy_dt_ids, diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 5cac31c6029b3..ae7bf3ff89ee1 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -424,7 +424,7 @@ static void tahvo_usb_remove(struct platform_device *pdev) static struct platform_driver tahvo_usb_driver = { .probe = tahvo_usb_probe, - .remove_new = tahvo_usb_remove, + .remove = tahvo_usb_remove, .driver = { .name = "tahvo-usb", .dev_groups = tahvo_groups, diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 4ea47e6f835bf..bee222967f6b1 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1495,7 +1495,7 @@ static void tegra_usb_phy_remove(struct platform_device *pdev) static struct platform_driver tegra_usb_phy_driver = { .probe = tegra_usb_phy_probe, - .remove_new = tegra_usb_phy_remove, + .remove = tegra_usb_phy_remove, .driver = { .name = "tegra-phy", .of_match_table = tegra_usb_phy_id_table, diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index da09cff55abce..49d79c1257f3a 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -432,7 +432,7 @@ MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); static struct platform_driver twl6030_usb_driver = { .probe = twl6030_usb_probe, - .remove_new = twl6030_usb_remove, + .remove = twl6030_usb_remove, .driver = { .name = "twl6030_usb", .of_match_table = of_match_ptr(twl6030_usb_id_table), diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 06f789097989f..1ce134505cee8 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -365,7 +365,7 @@ static int usb_add_extcon(struct usb_phy *x) { int ret; - if (of_property_read_bool(x->dev->of_node, "extcon")) { + if (of_property_present(x->dev->of_node, "extcon")) { x->edev = extcon_get_edev_by_phandle(x->dev, 0); if (IS_ERR(x->edev)) return PTR_ERR(x->edev); diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index edc43f169d493..935fc496fe94b 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -632,7 +632,7 @@ static int usbhs_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - if (of_property_read_bool(dev_of_node(dev), "extcon")) { + if (of_property_present(dev_of_node(dev), "extcon")) { priv->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(priv->edev)) return PTR_ERR(priv->edev); @@ -835,7 +835,7 @@ static struct platform_driver renesas_usbhs_driver = { .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, - .remove_new = usbhs_remove, + .remove = usbhs_remove, }; module_platform_driver(renesas_usbhs_driver); diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index e5c6c413a075b..cd2e026dcb22f 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -217,7 +217,7 @@ static struct platform_driver intel_xhci_usb_driver = { }, .id_table = intel_xhci_usb_table, .probe = intel_xhci_usb_probe, - .remove_new = intel_xhci_usb_remove, + .remove = intel_xhci_usb_remove, }; module_platform_driver(intel_xhci_usb_driver); diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index d200e2c29a8ff..2fea1b1db4a26 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -136,12 +136,11 @@ static void free_dynids(struct usb_serial_driver *drv) { struct usb_dynid *dynid, *n; - spin_lock(&drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { list_del(&dynid->node); kfree(dynid); } - spin_unlock(&drv->dynids.lock); } const struct bus_type usb_serial_bus_type = { @@ -157,7 +156,6 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) int retval; driver->driver.bus = &usb_serial_bus_type; - spin_lock_init(&driver->dynids.lock); INIT_LIST_HEAD(&driver->dynids.list); retval = driver_register(&driver->driver); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c6f17d732b958..e07c5e3eb18c0 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1443,9 +1443,11 @@ static void get_serial_info(struct tty_struct *tty, struct serial_struct *ss) struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); + mutex_lock(&priv->cfg_lock); ss->flags = priv->flags; ss->baud_base = priv->baud_base; ss->custom_divisor = priv->custom_divisor; + mutex_unlock(&priv->cfg_lock); } static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 28c71d99e8572..1fffda7647f98 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1129,7 +1129,7 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, spin_lock_irqsave(&edge_port->ep_lock, flags); /* calculate number of bytes to put in fifo */ - copySize = min((unsigned int)count, + copySize = min_t(unsigned int, count, (edge_port->txCredits - fifo->count)); dev_dbg(&port->dev, "%s of %d byte(s) Fifo room %d -- will copy %d bytes\n", diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ad41363e3cea5..010688dd9e49c 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -31,6 +31,7 @@ #define PL2303_QUIRK_UART_STATE_IDX0 BIT(0) #define PL2303_QUIRK_LEGACY BIT(1) #define PL2303_QUIRK_ENDPOINT_HACK BIT(2) +#define PL2303_QUIRK_NO_BREAK_GETLINE BIT(3) static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID), @@ -467,6 +468,25 @@ static int pl2303_detect_type(struct usb_serial *serial) return -ENODEV; } +static bool pl2303_is_hxd_clone(struct usb_serial *serial) +{ + struct usb_device *udev = serial->dev; + unsigned char *buf; + int ret; + + buf = kmalloc(7, GFP_KERNEL); + if (!buf) + return false; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + + kfree(buf); + + return ret == -EPIPE; +} + static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; @@ -489,6 +509,9 @@ static int pl2303_startup(struct usb_serial *serial) spriv->quirks = (unsigned long)usb_get_serial_data(serial); spriv->quirks |= spriv->type->quirks; + if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) + spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE; + usb_set_serial_data(serial, spriv); if (type != TYPE_HXN) { @@ -725,9 +748,18 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty, static int pl2303_get_line_request(struct usb_serial_port *port, unsigned char buf[7]) { - struct usb_device *udev = port->serial->dev; + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + struct usb_device *udev = serial->dev; int ret; + if (spriv->quirks & PL2303_QUIRK_NO_BREAK_GETLINE) { + struct pl2303_private *priv = usb_get_serial_port_data(port); + + memcpy(buf, priv->line_settings, 7); + return 0; + } + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, 0, 0, buf, 7, 100); @@ -1064,9 +1096,13 @@ static int pl2303_carrier_raised(struct usb_serial_port *port) static int pl2303_set_break(struct usb_serial_port *port, bool enable) { struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); u16 state; int result; + if (spriv->quirks & PL2303_QUIRK_NO_BREAK_GETLINE) + return -ENOTTY; + if (enable) state = BREAK_ON; else diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 64a2e0bb5723e..741e68e461396 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -421,7 +421,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, unsigned long flags; unsigned char *buffer; struct urb *urb; - size_t writesize = min((size_t)count, (size_t)MAX_TRANSFER); + size_t writesize = min_t(size_t, count, MAX_TRANSFER); int retval = 0; /* verify that we actually have some data to write */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index df6a2ae0bf424..7266558d823ac 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -706,14 +706,12 @@ static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf, { struct usb_dynid *dynid; - spin_lock(&drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (usb_match_one_id(intf, &dynid->id)) { - spin_unlock(&drv->dynids.lock); return &dynid->id; } } - spin_unlock(&drv->dynids.lock); return NULL; } diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index a4bfbecbf16c3..fd46e81388d2b 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -737,7 +737,7 @@ static int sd_scsi_write(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[5] = (unsigned char)(bnByte); bcb->CDB[4] = (unsigned char)(bnByte>>8); @@ -1163,7 +1163,7 @@ static int ms_read_copyblock(struct us_data *us, u16 oldphy, u16 newphy, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200*len; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[1] = 0x08; bcb->CDB[4] = (unsigned char)(oldphy); @@ -1759,7 +1759,7 @@ static int ms_scsi_write(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[1] = 0x04; bcb->CDB[5] = (unsigned char)(bn); @@ -1931,7 +1931,7 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = sd_fw->size; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xEF; result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 0c423916d7bfa..54ffff86c6fad 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -212,7 +212,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; @@ -301,7 +301,7 @@ static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 03d1b9c69ea18..30ee76cfef05f 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -752,7 +752,7 @@ sddr09_read_data(struct us_data *us, // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. - len = min(sectors, (unsigned int) info->blocksize) * info->pagesize; + len = min_t(unsigned int, sectors, info->blocksize) * info->pagesize; buffer = kmalloc(len, GFP_NOIO); if (!buffer) return -ENOMEM; @@ -997,7 +997,7 @@ sddr09_write_data(struct us_data *us, * at a time between the bounce buffer and the actual transfer buffer. */ - len = min(sectors, (unsigned int) info->blocksize) * info->pagesize; + len = min_t(unsigned int, sectors, info->blocksize) * info->pagesize; buffer = kmalloc(len, GFP_NOIO); if (!buffer) { kfree(blockbuffer); diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index b8227478a7add..a37fc505c57fe 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -206,7 +206,7 @@ static int sddr55_read_data(struct us_data *us, // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. - len = min((unsigned int) sectors, (unsigned int) info->blocksize >> + len = min_t(unsigned int, sectors, info->blocksize >> info->smallpageshift) * PAGESIZE; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) @@ -224,7 +224,7 @@ static int sddr55_read_data(struct us_data *us, // Read as many sectors as possible in this block - pages = min((unsigned int) sectors << info->smallpageshift, + pages = min_t(unsigned int, sectors << info->smallpageshift, info->blocksize - page); len = pages << info->pageshift; @@ -333,7 +333,7 @@ static int sddr55_write_data(struct us_data *us, // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. - len = min((unsigned int) sectors, (unsigned int) info->blocksize >> + len = min_t(unsigned int, sectors, info->blocksize >> info->smallpageshift) * PAGESIZE; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) @@ -351,7 +351,7 @@ static int sddr55_write_data(struct us_data *us, // Write as many sectors as possible in this block - pages = min((unsigned int) sectors << info->smallpageshift, + pages = min_t(unsigned int, sectors << info->smallpageshift, info->blocksize - page); len = pages << info->pageshift; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 7449e379077a5..9d767f6bf7225 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1133,7 +1133,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(transfer_length); bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? - US_BULK_FLAG_IN : 0; + US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = srb->device->lun; if (us->fflags & US_FL_SCM_MULT_TARG) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 92cc1b1361208..2f03190a9873a 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -729,7 +729,7 @@ int dp_altmode_probe(struct typec_altmode *alt) /* FIXME: Port can only be DFP_U. */ - /* Make sure we have compatiple pin configurations */ + /* Make sure we have compatible pin configurations */ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 58f40156de562..4b3047e055a37 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -219,6 +219,13 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev, char *buf); static DEVICE_ATTR_RO(usb_power_delivery_revision); +static const char * const usb_modes[] = { + [USB_MODE_NONE] = "none", + [USB_MODE_USB2] = "usb2", + [USB_MODE_USB3] = "usb3", + [USB_MODE_USB4] = "usb4" +}; + /* ------------------------------------------------------------------------- */ /* Alternate Modes */ @@ -614,6 +621,75 @@ EXPORT_SYMBOL_GPL(typec_unregister_altmode); /* ------------------------------------------------------------------------- */ /* Type-C Partners */ +/** + * typec_partner_set_usb_mode - Assign active USB Mode for the partner + * @partner: USB Type-C partner + * @mode: USB Mode (USB2, USB3 or USB4) + * + * The port drivers can use this function to assign the active USB Mode to + * @partner. The USB Mode can change for example due to Data Reset. + */ +void typec_partner_set_usb_mode(struct typec_partner *partner, enum usb_mode mode) +{ + if (!partner || partner->usb_mode == mode) + return; + + partner->usb_capability |= BIT(mode - 1); + partner->usb_mode = mode; + sysfs_notify(&partner->dev.kobj, NULL, "usb_mode"); +} +EXPORT_SYMBOL_GPL(typec_partner_set_usb_mode); + +static ssize_t +usb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_partner *partner = to_typec_partner(dev); + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & partner->usb_capability)) + continue; + + if (i == partner->usb_mode) + len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]); + else + len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]); + } + + sysfs_emit_at(buf, len - 1, "\n"); + + return len; +} + +static ssize_t usb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_partner *partner = to_typec_partner(dev); + struct typec_port *port = to_typec_port(dev->parent); + int mode; + int ret; + + if (!port->ops || !port->ops->enter_usb_mode) + return -EOPNOTSUPP; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + if (mode == partner->usb_mode) + return size; + + ret = port->ops->enter_usb_mode(port, mode); + if (ret) + return ret; + + typec_partner_set_usb_mode(partner, mode); + + return size; +} +static DEVICE_ATTR_RW(usb_mode); + static ssize_t accessory_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -660,6 +736,7 @@ static struct attribute *typec_partner_attrs[] = { &dev_attr_supports_usb_power_delivery.attr, &dev_attr_number_of_alternate_modes.attr, &dev_attr_type.attr, + &dev_attr_usb_mode.attr, &dev_attr_usb_power_delivery_revision.attr, NULL }; @@ -667,6 +744,14 @@ static struct attribute *typec_partner_attrs[] = { static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); + struct typec_port *port = to_typec_port(partner->dev.parent); + + if (attr == &dev_attr_usb_mode.attr) { + if (!partner->usb_capability) + return 0; + if (!port->ops || !port->ops->enter_usb_mode) + return 0444; + } if (attr == &dev_attr_number_of_alternate_modes.attr) { if (partner->num_altmodes < 0) @@ -740,10 +825,33 @@ static void typec_partner_unlink_device(struct typec_partner *partner, struct de */ int typec_partner_set_identity(struct typec_partner *partner) { - if (!partner->identity) + u8 usb_capability = partner->usb_capability; + struct device *dev = &partner->dev; + struct usb_pd_identity *id; + + id = get_pd_identity(dev); + if (!id) return -EINVAL; - typec_report_identity(&partner->dev); + if (to_typec_port(dev->parent)->data_role == TYPEC_HOST) { + u32 devcap = PD_VDO_UFP_DEVCAP(id->vdo[0]); + + if (devcap & (DEV_USB2_CAPABLE | DEV_USB2_BILLBOARD)) + usb_capability |= USB_CAPABILITY_USB2; + if (devcap & DEV_USB3_CAPABLE) + usb_capability |= USB_CAPABILITY_USB3; + if (devcap & DEV_USB4_CAPABLE) + usb_capability |= USB_CAPABILITY_USB4; + } else { + usb_capability = PD_VDO_DFP_HOSTCAP(id->vdo[0]); + } + + if (partner->usb_capability != usb_capability) { + partner->usb_capability = usb_capability; + sysfs_notify(&dev->kobj, NULL, "usb_mode"); + } + + typec_report_identity(dev); return 0; } EXPORT_SYMBOL_GPL(typec_partner_set_identity); @@ -913,6 +1021,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; partner->num_altmodes = -1; + partner->usb_capability = desc->usb_capability; partner->pd_revision = desc->pd_revision; partner->svdm_version = port->cap->svdm_version; partner->attach = desc->attach; @@ -932,6 +1041,15 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->dev.type = &typec_partner_dev_type; dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev)); + if (port->usb2_dev) { + partner->usb_capability |= USB_CAPABILITY_USB2; + partner->usb_mode = USB_MODE_USB2; + } + if (port->usb3_dev) { + partner->usb_capability |= USB_CAPABILITY_USB2 | USB_CAPABILITY_USB3; + partner->usb_mode = USB_MODE_USB3; + } + ret = device_register(&partner->dev); if (ret) { dev_err(&port->dev, "failed to register partner (%d)\n", ret); @@ -1292,6 +1410,67 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable); /* ------------------------------------------------------------------------- */ /* USB Type-C ports */ +/** + * typec_port_set_usb_mode - Set the operational USB mode for the port + * @port: USB Type-C port + * @mode: USB Mode (USB2, USB3 or USB4) + * + * @mode will be used with the next Enter_USB message. Existing connections are + * not affected. + */ +void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode) +{ + port->usb_mode = mode; +} +EXPORT_SYMBOL_GPL(typec_port_set_usb_mode); + +static ssize_t +usb_capability_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & port->cap->usb_capability)) + continue; + + if (i == port->usb_mode) + len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]); + else + len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]); + } + + sysfs_emit_at(buf, len - 1, "\n"); + + return len; +} + +static ssize_t +usb_capability_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret = 0; + int mode; + + if (!port->ops || !port->ops->default_usb_mode_set) + return -EOPNOTSUPP; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + ret = port->ops->default_usb_mode_set(port, mode); + if (ret) + return ret; + + port->usb_mode = mode; + + return size; +} +static DEVICE_ATTR_RW(usb_capability); + /** * typec_port_set_usb_power_delivery - Assign USB PD for port. * @port: USB Type-C port. @@ -1760,6 +1939,7 @@ static struct attribute *typec_attrs[] = { &dev_attr_vconn_source.attr, &dev_attr_port_type.attr, &dev_attr_orientation.attr, + &dev_attr_usb_capability.attr, NULL, }; @@ -1793,6 +1973,11 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, if (port->cap->orientation_aware) return 0444; return 0; + } else if (attr == &dev_attr_usb_capability.attr) { + if (!port->cap->usb_capability) + return 0; + if (!port->ops || !port->ops->default_usb_mode_set) + return 0444; } return attr->mode; @@ -1864,13 +2049,18 @@ static void typec_partner_attach(struct typec_connector *con, struct device *dev struct typec_port *port = container_of(con, struct typec_port, con); struct typec_partner *partner = typec_get_partner(port); struct usb_device *udev = to_usb_device(dev); + enum usb_mode usb_mode; - if (udev->speed < USB_SPEED_SUPER) + if (udev->speed < USB_SPEED_SUPER) { + usb_mode = USB_MODE_USB2; port->usb2_dev = dev; - else + } else { + usb_mode = USB_MODE_USB3; port->usb3_dev = dev; + } if (partner) { + typec_partner_set_usb_mode(partner, usb_mode); typec_partner_link_device(partner, dev); put_device(&partner->dev); } @@ -2433,6 +2623,13 @@ struct typec_port *typec_register_port(struct device *parent, port->con.attach = typec_partner_attach; port->con.deattach = typec_partner_deattach; + if (cap->usb_capability & USB_CAPABILITY_USB4) + port->usb_mode = USB_MODE_USB4; + else if (cap->usb_capability & USB_CAPABILITY_USB3) + port->usb_mode = USB_MODE_USB3; + else if (cap->usb_capability & USB_CAPABILITY_USB2) + port->usb_mode = USB_MODE_USB2; + device_initialize(&port->dev); port->dev.class = &typec_class; port->dev.parent = parent; diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 7485cdb9dd201..b3076a24ad2ee 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -35,6 +35,8 @@ struct typec_partner { int num_altmodes; u16 pd_revision; /* 0300H = "3.0" */ enum usb_pd_svdm_ver svdm_version; + enum usb_mode usb_mode; + u8 usb_capability; struct usb_power_delivery *pd; @@ -55,6 +57,7 @@ struct typec_port { enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; + enum usb_mode usb_mode; struct mutex port_type_lock; enum typec_orientation orientation; diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index ce7db6ad30572..67381b4ef4f68 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -66,6 +66,15 @@ config TYPEC_MUX_PTN36502 Say Y or M if your system has a NXP PTN36502 Type-C redriver chip found on some devices with a Type-C port. +config TYPEC_MUX_TUSB1046 + tristate "TI TUSB1046 Type-C crosspoint switch driver" + depends on I2C + help + Driver for the Texas Instruments TUSB1046-DCI crosspoint switch. + Supports flipping USB-C SuperSpeed lanes to adapt to orientation + changes, as well as muxing DisplayPort and sideband signals to a + common Type-C connector. + config TYPEC_MUX_WCD939X_USBSS tristate "Qualcomm WCD939x USBSS Analog Audio Switch driver" depends on I2C diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index bb96f30267af0..60879446da936 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o obj-$(CONFIG_TYPEC_MUX_IT5205) += it5205.o obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o +obj-$(CONFIG_TYPEC_MUX_TUSB1046) += tusb1046.o obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c index 8902102c05a8e..1834f1a2dd9dd 100644 --- a/drivers/usb/typec/mux/gpio-sbu-mux.c +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -159,7 +159,7 @@ MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match); static struct platform_driver gpio_sbu_mux_driver = { .probe = gpio_sbu_mux_probe, - .remove_new = gpio_sbu_mux_remove, + .remove = gpio_sbu_mux_remove, .driver = { .name = "gpio_sbu_mux", .of_match_table = gpio_sbu_mux_match, diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 46b4c5c3a6beb..5dfe957543946 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -828,7 +828,7 @@ static struct platform_driver pmc_usb_driver = { .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids), }, .probe = pmc_usb_probe, - .remove_new = pmc_usb_remove, + .remove = pmc_usb_remove, }; static int __init pmc_usb_init(void) diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c new file mode 100644 index 0000000000000..b4f45c217b59f --- /dev/null +++ b/drivers/usb/typec/mux/tusb1046.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the TUSB1046-DCI USB Type-C crosspoint switch + * + * Copyright (C) 2024 Bootlin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TUSB1046_REG_GENERAL 0xa + +/* General register bits */ +#define TUSB1046_GENERAL_FLIPSEL BIT(2) +#define TUSB1046_GENERAL_CTLSEL GENMASK(1, 0) + +/* Mux modes */ +#define TUSB1046_CTLSEL_DISABLED 0x0 +#define TUSB1046_CTLSEL_USB3 0x1 +#define TUSB1046_CTLSEL_4LANE_DP 0x2 +#define TUSB1046_CTLSEL_USB3_AND_2LANE_DP 0x3 + +struct tusb1046_priv { + struct i2c_client *client; + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + /* Lock General register during accesses */ + struct mutex general_reg_lock; +}; + +static int tusb1046_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct tusb1046_priv *priv = typec_mux_get_drvdata(mux); + struct i2c_client *client = priv->client; + struct device *dev = &client->dev; + int mode, val, ret = 0; + + if (state->mode >= TYPEC_STATE_MODAL && + state->alt->svid != USB_TYPEC_DP_SID) + return -EINVAL; + + dev_dbg(dev, "mux mode requested: %lu\n", state->mode); + + mutex_lock(&priv->general_reg_lock); + + val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); + if (val < 0) { + dev_err(dev, "failed to read ctlsel status, err %d\n", val); + ret = val; + goto out_unlock; + } + + switch (state->mode) { + case TYPEC_STATE_USB: + mode = TUSB1046_CTLSEL_USB3; + break; + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + mode = TUSB1046_CTLSEL_4LANE_DP; + break; + case TYPEC_DP_STATE_D: + mode = TUSB1046_CTLSEL_USB3_AND_2LANE_DP; + break; + case TYPEC_STATE_SAFE: + default: + mode = TUSB1046_CTLSEL_DISABLED; + break; + } + + val &= ~TUSB1046_GENERAL_CTLSEL; + val |= mode; + + ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); + +out_unlock: + mutex_unlock(&priv->general_reg_lock); + return ret; +} + +static int tusb1046_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct tusb1046_priv *priv = typec_switch_get_drvdata(sw); + struct i2c_client *client = priv->client; + struct device *dev = &client->dev; + int val, ret = 0; + + dev_dbg(dev, "setting USB3.0 lane flip for orientation %d\n", orientation); + + mutex_lock(&priv->general_reg_lock); + + val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); + if (val < 0) { + dev_err(dev, "failed to read flipsel status, err %d\n", val); + ret = val; + goto out_unlock; + } + + if (orientation == TYPEC_ORIENTATION_REVERSE) + val |= TUSB1046_GENERAL_FLIPSEL; + else + val &= ~TUSB1046_GENERAL_FLIPSEL; + + ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); + +out_unlock: + mutex_unlock(&priv->general_reg_lock); + return ret; +} + +static int tusb1046_i2c_probe(struct i2c_client *client) +{ + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct device *dev = &client->dev; + struct tusb1046_priv *priv; + int ret = 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n"); + + priv->client = client; + + mutex_init(&priv->general_reg_lock); + + sw_desc.drvdata = priv; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = tusb1046_switch_set; + + priv->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(priv->sw)) { + ret = dev_err_probe(dev, PTR_ERR(priv->sw), "failed to register type-c switch\n"); + goto err_destroy_mutex; + } + + mux_desc.drvdata = priv; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = tusb1046_mux_set; + + priv->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(priv->mux)) { + ret = dev_err_probe(dev, PTR_ERR(priv->mux), "failed to register type-c mux\n"); + goto err_unregister_switch; + } + + i2c_set_clientdata(client, priv); + + return 0; + +err_unregister_switch: + typec_switch_unregister(priv->sw); +err_destroy_mutex: + mutex_destroy(&priv->general_reg_lock); + return ret; +} + +static void tusb1046_i2c_remove(struct i2c_client *client) +{ + struct tusb1046_priv *priv = i2c_get_clientdata(client); + + typec_switch_unregister(priv->sw); + typec_mux_unregister(priv->mux); + mutex_destroy(&priv->general_reg_lock); +} + +static const struct of_device_id tusb1046_match_table[] = { + {.compatible = "ti,tusb1046"}, + {}, +}; + +static struct i2c_driver tusb1046_driver = { + .driver = { + .name = "tusb1046", + .of_match_table = tusb1046_match_table, + }, + .probe = tusb1046_i2c_probe, + .remove = tusb1046_i2c_remove, +}; + +module_i2c_driver(tusb1046_driver); + +MODULE_DESCRIPTION("TUSB1046 USB Type-C switch driver"); +MODULE_AUTHOR("Romain Gantois "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index f3140fc04c12e..6d85b25b40bc0 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -633,9 +633,8 @@ MODULE_DEVICE_TABLE(of, stusb160x_of_match); static int stusb160x_probe(struct i2c_client *client) { + const struct regmap_config *regmap_config; struct stusb160x *chip; - const struct of_device_id *match; - struct regmap_config *regmap_config; struct fwnode_handle *fwnode; int ret; @@ -645,8 +644,8 @@ static int stusb160x_probe(struct i2c_client *client) i2c_set_clientdata(client, chip); - match = i2c_of_match_device(stusb160x_of_match, client); - regmap_config = (struct regmap_config *)match->data; + regmap_config = i2c_get_match_data(client); + chip->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index b80eb2d78d88b..3766790c1548c 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -163,7 +163,7 @@ static struct platform_driver qcom_pmic_typec_driver = { .of_match_table = qcom_pmic_typec_table, }, .probe = qcom_pmic_typec_probe, - .remove_new = qcom_pmic_typec_remove, + .remove = qcom_pmic_typec_remove, }; module_platform_driver(qcom_pmic_typec_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c index 02b7fd3022650..881ffacbfd773 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6360.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c @@ -221,7 +221,7 @@ static struct platform_driver mt6360_tcpc_driver = { .of_match_table = mt6360_tcpc_of_id, }, .probe = mt6360_tcpc_probe, - .remove_new = mt6360_tcpc_remove, + .remove = mt6360_tcpc_remove, }; module_platform_driver(mt6360_tcpc_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c index 9cda1005ef01a..1479f961772d0 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6370.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -196,7 +196,7 @@ static struct platform_driver mt6370_tcpc_driver = { .of_match_table = mt6370_tcpc_devid_table, }, .probe = mt6370_tcpc_probe, - .remove_new = mt6370_tcpc_remove, + .remove = mt6370_tcpc_remove, }; module_platform_driver(mt6370_tcpc_driver); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 7ae341a403424..6021eeb903fec 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -310,6 +310,18 @@ struct pd_data { unsigned int operating_snk_mw; }; +/* + * @sink_wait_cap_time: Deadline (in ms) for tTypeCSinkWaitCap timer + * @ps_src_wait_off_time: Deadline (in ms) for tPSSourceOff timer + * @cc_debounce_time: Deadline (in ms) for tCCDebounce timer + */ +struct pd_timings { + u32 sink_wait_cap_time; + u32 ps_src_off_time; + u32 cc_debounce_time; + u32 snk_bc12_cmpletion_time; +}; + struct tcpm_port { struct device *dev; @@ -552,6 +564,9 @@ struct tcpm_port { */ unsigned int message_id_prime; unsigned int rx_msgid_prime; + + /* Timer deadline values configured at runtime */ + struct pd_timings timings; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -4640,15 +4655,15 @@ static void run_state_machine(struct tcpm_port *port) case SRC_ATTACH_WAIT: if (tcpm_port_is_debug(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_audio(port)) tcpm_set_state(port, AUDIO_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_source(port) && port->vbus_vsafe0v) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SNK_TRY: @@ -4699,7 +4714,7 @@ static void run_state_machine(struct tcpm_port *port) } break; case SRC_TRYWAIT_DEBOUNCE: - tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, SRC_ATTACHED, port->timings.cc_debounce_time); break; case SRC_TRYWAIT_UNATTACHED: tcpm_set_state(port, SNK_UNATTACHED, 0); @@ -4902,7 +4917,7 @@ static void run_state_machine(struct tcpm_port *port) (port->cc1 != TYPEC_CC_OPEN && port->cc2 == TYPEC_CC_OPEN)) tcpm_set_state(port, SNK_DEBOUNCED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_disconnected(port)) tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); @@ -4942,7 +4957,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRYWAIT: tcpm_set_cc(port, TYPEC_CC_RD); - tcpm_set_state(port, SNK_TRYWAIT_VBUS, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, SNK_TRYWAIT_VBUS, port->timings.cc_debounce_time); break; case SNK_TRYWAIT_VBUS: /* @@ -4965,7 +4980,16 @@ static void run_state_machine(struct tcpm_port *port) if (ret < 0) tcpm_set_state(port, SNK_UNATTACHED, 0); else - tcpm_set_state(port, SNK_STARTUP, 0); + /* + * For Type C port controllers that use Battery Charging + * Detection (based on BCv1.2 spec) to detect USB + * charger type, add a delay of "snk_bc12_cmpletion_time" + * before transitioning to SNK_STARTUP to allow BC1.2 + * detection to complete before PD is eventually enabled + * in later states. + */ + tcpm_set_state(port, SNK_STARTUP, + port->timings.snk_bc12_cmpletion_time); break; case SNK_STARTUP: opmode = tcpm_get_pwr_opmode(port->polarity ? @@ -5015,7 +5039,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_DISCOVERY_DEBOUNCE: tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SNK_DISCOVERY_DEBOUNCE_DONE: if (!tcpm_port_is_disconnected(port) && @@ -5042,13 +5066,14 @@ static void run_state_machine(struct tcpm_port *port) if (port->vbus_never_low) { port->vbus_never_low = false; tcpm_set_state(port, SNK_SOFT_RESET, - PD_T_SINK_WAIT_CAP); + port->timings.sink_wait_cap_time); } else { if (!port->self_powered) upcoming_state = SNK_WAIT_CAPABILITIES_TIMEOUT; else upcoming_state = hard_reset_state(port); - tcpm_set_state(port, upcoming_state, PD_T_SINK_WAIT_CAP); + tcpm_set_state(port, SNK_WAIT_CAPABILITIES_TIMEOUT, + port->timings.sink_wait_cap_time); } break; case SNK_WAIT_CAPABILITIES_TIMEOUT: @@ -5070,7 +5095,8 @@ static void run_state_machine(struct tcpm_port *port) if (tcpm_pd_send_control(port, PD_CTRL_GET_SOURCE_CAP, TCPC_TX_SOP)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else - tcpm_set_state(port, hard_reset_state(port), PD_T_SINK_WAIT_CAP); + tcpm_set_state(port, hard_reset_state(port), + port->timings.sink_wait_cap_time); break; case SNK_NEGOTIATE_CAPABILITIES: port->pd_capable = true; @@ -5207,7 +5233,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ACC_UNATTACHED, 0); break; case AUDIO_ACC_DEBOUNCE: - tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, ACC_UNATTACHED, port->timings.cc_debounce_time); break; /* Hard_Reset states */ @@ -5274,7 +5300,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: - /* Do not discharge/disconnect during hard reseet */ + /* Do not discharge/disconnect during hard reset */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0); memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); @@ -5424,7 +5450,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ERROR_RECOVERY, 0); break; case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF: - tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF); + tcpm_set_state(port, ERROR_RECOVERY, port->timings.ps_src_off_time); break; case FR_SWAP_SNK_SRC_NEW_SINK_READY: if (port->vbus_source) @@ -5479,7 +5505,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_cc(port, TYPEC_CC_RD); /* allow CC debounce */ tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: /* @@ -5514,7 +5540,7 @@ static void run_state_machine(struct tcpm_port *port) port->pps_data.active, 0); tcpm_set_charge(port, false); tcpm_set_state(port, hard_reset_state(port), - PD_T_PS_SOURCE_OFF); + port->timings.ps_src_off_time); break; case PR_SWAP_SNK_SRC_SOURCE_ON: tcpm_enable_auto_vbus_discharge(port, true); @@ -5670,7 +5696,7 @@ static void run_state_machine(struct tcpm_port *port) case PORT_RESET_WAIT_OFF: tcpm_set_state(port, tcpm_default_state(port), - port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); + port->vbus_present ? port->timings.ps_src_off_time : 0); break; /* AMS intermediate state */ @@ -6070,7 +6096,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) break; case SNK_ATTACH_WAIT: case SNK_DEBOUNCED: - /* Do nothing, as TCPM is still waiting for vbus to reaach VSAFE5V to connect */ + /* Do nothing, as TCPM is still waiting for vbus to reach VSAFE5V to connect */ break; case SNK_NEGOTIATE_CAPABILITIES: @@ -6161,7 +6187,7 @@ static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) case SRC_ATTACH_WAIT: if (tcpm_port_is_source(port)) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SRC_STARTUP: case SRC_SEND_CAPABILITIES: @@ -7057,6 +7083,34 @@ static int tcpm_port_register_pd(struct tcpm_port *port) return ret; } +static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fwnode) +{ + int ret; + u32 val; + + ret = fwnode_property_read_u32(fwnode, "sink-wait-cap-time-ms", &val); + if (!ret) + port->timings.sink_wait_cap_time = val; + else + port->timings.sink_wait_cap_time = PD_T_SINK_WAIT_CAP; + + ret = fwnode_property_read_u32(fwnode, "ps-source-off-time-ms", &val); + if (!ret) + port->timings.ps_src_off_time = val; + else + port->timings.ps_src_off_time = PD_T_PS_SOURCE_OFF; + + ret = fwnode_property_read_u32(fwnode, "cc-debounce-time-ms", &val); + if (!ret) + port->timings.cc_debounce_time = val; + else + port->timings.cc_debounce_time = PD_T_CC_DEBOUNCE; + + ret = fwnode_property_read_u32(fwnode, "sink-bc12-completion-time-ms", &val); + if (!ret) + port->timings.snk_bc12_cmpletion_time = val; +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { struct fwnode_handle *capabilities, *child, *caps = NULL; @@ -7371,7 +7425,7 @@ static int tcpm_psy_get_input_power_limit(struct tcpm_port *port, src_mv = pdo_fixed_voltage(pdo); src_ma = pdo_max_current(pdo); tmp = src_mv * src_ma; - max_src_uw = tmp > max_src_uw ? tmp : max_src_uw; + max_src_uw = max(tmp, max_src_uw); } } @@ -7614,6 +7668,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) if (err < 0) goto out_destroy_wq; + tcpm_fw_get_timings(port, tcpc->fwnode); + port->try_role = port->typec_caps.prefer_role; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 60b2766a69bf8..759c982bb16a9 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -687,7 +687,7 @@ static struct platform_driver wcove_typec_driver = { .name = "bxt_wcove_usbc", }, .probe = wcove_typec_probe, - .remove_new = wcove_typec_remove, + .remove = wcove_typec_remove, }; module_platform_driver(wcove_typec_driver); diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index f67733cecfdf5..83ff23086d794 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -32,6 +32,7 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_UOR: case UCSI_SET_PDR: case UCSI_CONNECTOR_RESET: + case UCSI_SET_SINK_PATH: ret = ucsi_send_command(ucsi, val, NULL, 0); break; case UCSI_GET_CAPABILITY: diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 1c631c7855a96..62ac697304050 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -55,8 +55,8 @@ static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { val->intval = UCSI_PSY_OFFLINE; - if (con->status.flags & UCSI_CONSTAT_CONNECTED && - (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK) + if (UCSI_CONSTAT(con, CONNECTED) && + (UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK)) val->intval = UCSI_PSY_FIXED_ONLINE; return 0; } @@ -66,7 +66,7 @@ static int ucsi_psy_get_voltage_min(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: pdo = con->src_pdos[0]; val->intval = pdo_fixed_voltage(pdo) * 1000; @@ -89,7 +89,7 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: if (con->num_pdos > 0) { pdo = con->src_pdos[con->num_pdos - 1]; @@ -117,7 +117,7 @@ static int ucsi_psy_get_voltage_now(struct ucsi_connector *con, int index; u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: index = rdo_index(con->rdo); if (index > 0) { @@ -145,7 +145,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: if (con->num_pdos > 0) { pdo = con->src_pdos[con->num_pdos - 1]; @@ -173,9 +173,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, static int ucsi_psy_get_current_now(struct ucsi_connector *con, union power_supply_propval *val) { - u16 flags = con->status.flags; - - if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD) + if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) val->intval = rdo_op_current(con->rdo) * 1000; else val->intval = 0; @@ -185,11 +183,9 @@ static int ucsi_psy_get_current_now(struct ucsi_connector *con, static int ucsi_psy_get_usb_type(struct ucsi_connector *con, union power_supply_propval *val) { - u16 flags = con->status.flags; - val->intval = POWER_SUPPLY_USB_TYPE_C; - if (flags & UCSI_CONSTAT_CONNECTED && - UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD) + if (UCSI_CONSTAT(con, CONNECTED) && + UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) val->intval = POWER_SUPPLY_USB_TYPE_PD; return 0; @@ -197,18 +193,18 @@ static int ucsi_psy_get_usb_type(struct ucsi_connector *con, static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val) { - if (!(con->status.flags & UCSI_CONSTAT_CONNECTED)) { + if (!(UCSI_CONSTAT(con, CONNECTED))) { val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; return 0; } /* The Battery Charging Cabability Status field is only valid in sink role. */ - if ((con->status.flags & UCSI_CONSTAT_PWR_DIR) != TYPEC_SINK) { + if (UCSI_CONSTAT(con, PWR_DIR) != TYPEC_SINK) { val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; return 0; } - switch (UCSI_CONSTAT_BC_STATUS(con->status.pwr_status)) { + switch (UCSI_CONSTAT(con, BC_STATUS)) { case UCSI_CONSTAT_BC_NOMINAL_CHARGING: val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; break; diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index a0d3a934d3d92..41701dee70564 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -40,8 +40,8 @@ DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm, ); DECLARE_EVENT_CLASS(ucsi_log_connector_status, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status), + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con), TP_STRUCT__entry( __field(int, port) __field(u16, change) @@ -55,14 +55,14 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, ), TP_fast_assign( __entry->port = port - 1; - __entry->change = status->change; - __entry->opmode = UCSI_CONSTAT_PWR_OPMODE(status->flags); - __entry->connected = !!(status->flags & UCSI_CONSTAT_CONNECTED); - __entry->pwr_dir = !!(status->flags & UCSI_CONSTAT_PWR_DIR); - __entry->partner_flags = UCSI_CONSTAT_PARTNER_FLAGS(status->flags); - __entry->partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags); - __entry->request_data_obj = status->request_data_obj; - __entry->bc_status = UCSI_CONSTAT_BC_STATUS(status->pwr_status); + __entry->change = UCSI_CONSTAT(con, CHANGE); + __entry->opmode = UCSI_CONSTAT(con, PWR_OPMODE); + __entry->connected = UCSI_CONSTAT(con, CONNECTED); + __entry->pwr_dir = UCSI_CONSTAT(con, PWR_DIR); + __entry->partner_flags = UCSI_CONSTAT(con, PARTNER_FLAGS); + __entry->partner_type = UCSI_CONSTAT(con, PARTNER_TYPE); + __entry->request_data_obj = UCSI_CONSTAT(con, RDO); + __entry->bc_status = UCSI_CONSTAT(con, BC_STATUS); ), TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, " "sourcing=%d, partner_flags=%x, partner_type=%x, " @@ -73,13 +73,13 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, ); DEFINE_EVENT(ucsi_log_connector_status, ucsi_connector_change, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status) + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con) ); DEFINE_EVENT(ucsi_log_connector_status, ucsi_register_port, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status) + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con) ); DECLARE_EVENT_CLASS(ucsi_log_register_altmode, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index e0f3925e401b3..c435c0835744a 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -648,6 +648,17 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) } } +static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) +{ + u64 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + size_t size = min(UCSI_GET_CONNECTOR_STATUS_SIZE, UCSI_MAX_DATA_LENGTH(con->ucsi)); + int ret; + + ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + + return ret < 0 ? ret : 0; +} + static int ucsi_read_pdos(struct ucsi_connector *con, enum typec_role role, int is_partner, u32 *pdos, int offset, int num_pdos) @@ -658,8 +669,7 @@ static int ucsi_read_pdos(struct ucsi_connector *con, if (is_partner && ucsi->quirks & UCSI_NO_PARTNER_PDOS && - ((con->status.flags & UCSI_CONSTAT_PWR_DIR) || - !is_source(role))) + (UCSI_CONSTAT(con, PWR_DIR) || !is_source(role))) return 0; command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num); @@ -973,6 +983,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con) static int ucsi_check_connector_capability(struct ucsi_connector *con) { + u64 pd_revision; u64 command; int ret; @@ -986,17 +997,17 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return ret; } - typec_partner_set_pd_revision(con->partner, - UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags)); + pd_revision = UCSI_CONCAP(con, PARTNER_PD_REVISION_V2_1); + typec_partner_set_pd_revision(con->partner, UCSI_SPEC_REVISION_TO_BCD(pd_revision)); return ret; } static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: - con->rdo = con->status.request_data_obj; + con->rdo = UCSI_CONSTAT(con, RDO); typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0); ucsi_partner_task(con, ucsi_check_altmodes, 30, HZ); @@ -1020,7 +1031,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) static int ucsi_register_partner(struct ucsi_connector *con) { - u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags); + u8 pwr_opmode = UCSI_CONSTAT(con, PWR_OPMODE); struct typec_partner_desc desc; struct typec_partner *partner; @@ -1029,7 +1040,7 @@ static int ucsi_register_partner(struct ucsi_connector *con) memset(&desc, 0, sizeof(desc)); - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: desc.accessory = TYPEC_ACCESSORY_DEBUG; break; @@ -1047,6 +1058,11 @@ static int ucsi_register_partner(struct ucsi_connector *con) desc.identity = &con->partner_identity; desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; + if (con->ucsi->version >= UCSI_VERSION_2_1) { + u64 pd_revision = UCSI_CONCAP(con, PARTNER_PD_REVISION_V2_1); + desc.pd_revision = UCSI_SPEC_REVISION_TO_BCD(pd_revision); + } + partner = typec_register_partner(con->port, &desc); if (IS_ERR(partner)) { dev_err(con->ucsi->dev, @@ -1057,6 +1073,13 @@ static int ucsi_register_partner(struct ucsi_connector *con) con->partner = partner; + if (con->ucsi->version >= UCSI_VERSION_3_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) + typec_partner_set_usb_mode(partner, USB_MODE_USB4); + else if (con->ucsi->version >= UCSI_VERSION_2_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3)) + typec_partner_set_usb_mode(partner, USB_MODE_USB4); + return 0; } @@ -1081,7 +1104,7 @@ static void ucsi_partner_change(struct ucsi_connector *con) enum usb_role u_role = USB_ROLE_NONE; int ret; - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: u_role = USB_ROLE_HOST; @@ -1097,8 +1120,8 @@ static void ucsi_partner_change(struct ucsi_connector *con) break; } - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + if (UCSI_CONSTAT(con, CONNECTED)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: typec_set_mode(con->port, TYPEC_MODE_DEBUG); break; @@ -1106,14 +1129,13 @@ static void ucsi_partner_change(struct ucsi_connector *con) typec_set_mode(con->port, TYPEC_MODE_AUDIO); break; default: - if (UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) == - UCSI_CONSTAT_PARTNER_FLAG_USB) + if (UCSI_CONSTAT(con, PARTNER_FLAG_USB)) typec_set_mode(con->port, TYPEC_STATE_USB); } } /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1124,21 +1146,18 @@ static void ucsi_partner_change(struct ucsi_connector *con) static int ucsi_check_connection(struct ucsi_connector *con) { - u8 prev_flags = con->status.flags; - u64 command; + u8 prev_state = UCSI_CONSTAT(con, CONNECTED); int ret; - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &con->status, sizeof(con->status)); - if (ret < 0) { + ret = ucsi_get_connector_status(con, false); + if (ret) { dev_err(con->ucsi->dev, "GET_CONNECTOR_STATUS failed (%d)\n", ret); return ret; } - if (con->status.flags == prev_flags) - return 0; - - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + if (UCSI_CONSTAT(con, CONNECTED)) { + if (prev_state) + return 0; ucsi_register_partner(con); ucsi_pwr_opmode_change(con); ucsi_partner_change(con); @@ -1194,7 +1213,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; enum typec_role role; - u64 command; + u16 change; int ret; mutex_lock(&con->lock); @@ -1203,25 +1222,23 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - - ret = ucsi_send_command_common(ucsi, command, &con->status, - sizeof(con->status), true); - if (ret < 0) { + ret = ucsi_get_connector_status(con, true); + if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); clear_bit(EVENT_PENDING, &con->ucsi->flags); goto out_unlock; } - trace_ucsi_connector_change(con->num, &con->status); + trace_ucsi_connector_change(con->num, con); if (ucsi->ops->connector_status) ucsi->ops->connector_status(con); - role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + change = UCSI_CONSTAT(con, CHANGE); + role = UCSI_CONSTAT(con, PWR_DIR); - if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { typec_set_pwr_role(con->port, role); /* Complete pending power role swap */ @@ -1229,12 +1246,12 @@ static void ucsi_handle_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { + if (change & UCSI_CONSTAT_CONNECT_CHANGE) { typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); ucsi_partner_change(con); - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + if (UCSI_CONSTAT(con, CONNECTED)) { ucsi_register_partner(con); ucsi_partner_task(con, ucsi_check_connection, 1, HZ); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) @@ -1242,8 +1259,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) ucsi_partner_task(con, ucsi_check_cable, 1, HZ); - if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ); ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ); } @@ -1252,11 +1268,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) } } - if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || - con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE) + if (change & (UCSI_CONSTAT_POWER_OPMODE_CHANGE | UCSI_CONSTAT_POWER_LEVEL_CHANGE)) ucsi_pwr_opmode_change(con); - if (con->partner && con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) { + if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) { ucsi_partner_change(con); /* Complete pending data role swap */ @@ -1264,10 +1279,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) + if (change & UCSI_CONSTAT_CAM_CHANGE) ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ); - if (con->status.change & UCSI_CONSTAT_BC_CHANGE) + if (change & UCSI_CONSTAT_BC_CHANGE) ucsi_port_psy_changed(con); out_unlock: @@ -1427,7 +1442,7 @@ static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) goto out_unlock; } - partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags); + partner_type = UCSI_CONSTAT(con, PARTNER_TYPE); if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && role == TYPEC_DEVICE) || (partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && @@ -1471,7 +1486,7 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) goto out_unlock; } - cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + cur_role = UCSI_CONSTAT(con, PWR_DIR); if (cur_role == role) goto out_unlock; @@ -1494,8 +1509,7 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) mutex_lock(&con->lock); /* Something has gone wrong while swapping the role */ - if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) != - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT(con, PWR_OPMODE) != UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_reset_connector(con, true); ret = -EPROTO; } @@ -1563,19 +1577,18 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) if (ret < 0) goto out_unlock; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP) + if (UCSI_CONCAP(con, OPMODE_DRP)) cap->data = TYPEC_PORT_DRD; - else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DFP) + else if (UCSI_CONCAP(con, OPMODE_DFP)) cap->data = TYPEC_PORT_DFP; - else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP) + else if (UCSI_CONCAP(con, OPMODE_UFP)) cap->data = TYPEC_PORT_UFP; - if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) && - (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)) + if (UCSI_CONCAP(con, PROVIDER) && UCSI_CONCAP(con, CONSUMER)) cap->type = TYPEC_PORT_DRP; - else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) + else if (UCSI_CONCAP(con, PROVIDER)) cap->type = TYPEC_PORT_SRC; - else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER) + else if (UCSI_CONCAP(con, CONSUMER)) cap->type = TYPEC_PORT_SNK; cap->revision = ucsi->cap.typec_version; @@ -1583,11 +1596,18 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->svdm_version = SVDM_VER_2_0; cap->prefer_role = TYPEC_NO_PREFERRED_ROLE; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY) + if (UCSI_CONCAP(con, OPMODE_AUDIO_ACCESSORY)) *accessory++ = TYPEC_ACCESSORY_AUDIO; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY) + if (UCSI_CONCAP(con, OPMODE_DEBUG_ACCESSORY)) *accessory = TYPEC_ACCESSORY_DEBUG; + if (UCSI_CONCAP_USB2_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB2; + if (UCSI_CONCAP_USB3_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB3; + if (UCSI_CONCAP_USB4_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB4; + cap->driver_data = con; cap->ops = &ucsi_ops; @@ -1617,19 +1637,16 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Get the status */ - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status)); - if (ret < 0) { + ret = ucsi_get_connector_status(con, false); + if (ret) { dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); - ret = 0; goto out; } - ret = 0; /* ucsi_send_command() returns length on success */ if (ucsi->ops->connector_status) ucsi->ops->connector_status(con); - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: u_role = USB_ROLE_HOST; @@ -1646,9 +1663,8 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Check if there is already something connected */ - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { - typec_set_pwr_role(con->port, - !!(con->status.flags & UCSI_CONSTAT_PWR_DIR)); + if (UCSI_CONSTAT(con, CONNECTED)) { + typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); ucsi_register_partner(con); ucsi_pwr_opmode_change(con); ucsi_port_psy_changed(con); @@ -1659,7 +1675,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1669,16 +1685,14 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) ret = 0; } - if (con->partner && - UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_register_device_pdos(con); ucsi_get_src_pdos(con); ucsi_check_altmodes(con); ucsi_check_connector_capability(con); } - trace_ucsi_register_port(con->num, &con->status); + trace_ucsi_register_port(con->num, con); out: fwnode_handle_put(cap->fwnode); @@ -1761,7 +1775,8 @@ static int ucsi_init(struct ucsi *ucsi) /* Get PPM capabilities */ command = UCSI_GET_CAPABILITY; - ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap)); + ret = ucsi_send_command(ucsi, command, &ucsi->cap, + BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); if (ret < 0) goto err_reset; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1cf5aad4c23a9..5ff369c24a2fc 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -4,6 +4,7 @@ #define __DRIVER_USB_TYPEC_UCSI_H #include +#include #include #include #include @@ -95,26 +96,31 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); /* -------------------------------------------------------------------------- */ /* Commands */ -#define UCSI_PPM_RESET 0x01 -#define UCSI_CANCEL 0x02 -#define UCSI_CONNECTOR_RESET 0x03 -#define UCSI_ACK_CC_CI 0x04 -#define UCSI_SET_NOTIFICATION_ENABLE 0x05 -#define UCSI_GET_CAPABILITY 0x06 -#define UCSI_GET_CONNECTOR_CAPABILITY 0x07 -#define UCSI_SET_UOM 0x08 -#define UCSI_SET_UOR 0x09 -#define UCSI_SET_PDM 0x0a -#define UCSI_SET_PDR 0x0b -#define UCSI_GET_ALTERNATE_MODES 0x0c -#define UCSI_GET_CAM_SUPPORTED 0x0d -#define UCSI_GET_CURRENT_CAM 0x0e -#define UCSI_SET_NEW_CAM 0x0f -#define UCSI_GET_PDOS 0x10 -#define UCSI_GET_CABLE_PROPERTY 0x11 -#define UCSI_GET_CONNECTOR_STATUS 0x12 -#define UCSI_GET_ERROR_STATUS 0x13 -#define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_PPM_RESET 0x01 +#define UCSI_CANCEL 0x02 +#define UCSI_CONNECTOR_RESET 0x03 +#define UCSI_ACK_CC_CI 0x04 +#define UCSI_SET_NOTIFICATION_ENABLE 0x05 +#define UCSI_GET_CAPABILITY 0x06 +#define UCSI_GET_CAPABILITY_SIZE 128 +#define UCSI_GET_CONNECTOR_CAPABILITY 0x07 +#define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32 +#define UCSI_SET_UOM 0x08 +#define UCSI_SET_UOR 0x09 +#define UCSI_SET_PDM 0x0a +#define UCSI_SET_PDR 0x0b +#define UCSI_GET_ALTERNATE_MODES 0x0c +#define UCSI_GET_CAM_SUPPORTED 0x0d +#define UCSI_GET_CURRENT_CAM 0x0e +#define UCSI_SET_NEW_CAM 0x0f +#define UCSI_GET_PDOS 0x10 +#define UCSI_GET_CABLE_PROPERTY 0x11 +#define UCSI_GET_CABLE_PROPERTY_SIZE 64 +#define UCSI_GET_CONNECTOR_STATUS 0x12 +#define UCSI_GET_CONNECTOR_STATUS_SIZE 152 +#define UCSI_GET_ERROR_STATUS 0x13 +#define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_SET_SINK_PATH 0x1c #define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) #define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff) @@ -126,7 +132,6 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_CONNECTOR_RESET_HARD_VER_1_0 BIT(23) /* Deprecated in v1.1 */ #define UCSI_CONNECTOR_RESET_DATA_VER_2_0 BIT(23) /* Redefined in v2.0 */ - /* ACK_CC_CI bits */ #define UCSI_ACK_CONNECTOR_CHANGE BIT(16) #define UCSI_ACK_COMMAND_COMPLETE BIT(17) @@ -250,42 +255,6 @@ struct ucsi_capability { u16 typec_version; } __packed; -/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */ -struct ucsi_connector_capability { - u8 op_mode; -#define UCSI_CONCAP_OPMODE_DFP BIT(0) -#define UCSI_CONCAP_OPMODE_UFP BIT(1) -#define UCSI_CONCAP_OPMODE_DRP BIT(2) -#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY BIT(3) -#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY BIT(4) -#define UCSI_CONCAP_OPMODE_USB2 BIT(5) -#define UCSI_CONCAP_OPMODE_USB3 BIT(6) -#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7) - u32 flags; -#define UCSI_CONCAP_FLAG_PROVIDER BIT(0) -#define UCSI_CONCAP_FLAG_CONSUMER BIT(1) -#define UCSI_CONCAP_FLAG_SWAP_TO_DFP BIT(2) -#define UCSI_CONCAP_FLAG_SWAP_TO_UFP BIT(3) -#define UCSI_CONCAP_FLAG_SWAP_TO_SRC BIT(4) -#define UCSI_CONCAP_FLAG_SWAP_TO_SINK BIT(5) -#define UCSI_CONCAP_FLAG_EX_OP_MODE(_f_) \ - (((_f_) & GENMASK(13, 6)) >> 6) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN2 BIT(0) -#define UCSI_CONCAP_EX_OP_MODE_EPR_SRC BIT(1) -#define UCSI_CONCAP_EX_OP_MODE_EPR_SINK BIT(2) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN3 BIT(3) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN4 BIT(4) -#define UCSI_CONCAP_FLAG_MISC_CAPS(_f_) \ - (((_f_) & GENMASK(17, 14)) >> 14) -#define UCSI_CONCAP_MISC_CAP_FW_UPDATE BIT(0) -#define UCSI_CONCAP_MISC_CAP_SECURITY BIT(1) -#define UCSI_CONCAP_FLAG_REV_CURR_PROT_SUPPORT BIT(18) -#define UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV(_f_) \ - (((_f_) & GENMASK(20, 19)) >> 19) -#define UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(_f_) \ - UCSI_SPEC_REVISION_TO_BCD(UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV(_f_)) -} __packed; - struct ucsi_altmode { u16 svid; u32 mid; @@ -311,49 +280,143 @@ struct ucsi_cable_property { u8 latency; } __packed; -/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */ -struct ucsi_connector_status { - u16 change; -#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) -#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2) -#define UCSI_CONSTAT_PDOS_CHANGE BIT(5) -#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6) -#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7) -#define UCSI_CONSTAT_CAM_CHANGE BIT(8) -#define UCSI_CONSTAT_BC_CHANGE BIT(9) -#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) -#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) -#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) -#define UCSI_CONSTAT_ERROR BIT(15) - u16 flags; -#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0)) +/* Get Connector Capability Fields. */ +#define UCSI_CONCAP_OPMODE UCSI_DECLARE_BITFIELD(0, 0, 8) +#define UCSI_CONCAP_OPMODE_DFP UCSI_DECLARE_BITFIELD(0, 0, 1) +#define UCSI_CONCAP_OPMODE_UFP UCSI_DECLARE_BITFIELD(0, 1, 1) +#define UCSI_CONCAP_OPMODE_DRP UCSI_DECLARE_BITFIELD(0, 2, 1) +#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY UCSI_DECLARE_BITFIELD(0, 3, 1) +#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY UCSI_DECLARE_BITFIELD(0, 4, 1) +#define UCSI_CONCAP_OPMODE_USB2 UCSI_DECLARE_BITFIELD(0, 5, 1) +#define UCSI_CONCAP_OPMODE_USB3 UCSI_DECLARE_BITFIELD(0, 6, 1) +#define UCSI_CONCAP_OPMODE_ALT_MODE UCSI_DECLARE_BITFIELD(0, 7, 1) +#define UCSI_CONCAP_PROVIDER UCSI_DECLARE_BITFIELD(0, 8, 1) +#define UCSI_CONCAP_CONSUMER UCSI_DECLARE_BITFIELD(0, 9, 1) +#define UCSI_CONCAP_SWAP_TO_DFP_V1_1 UCSI_DECLARE_BITFIELD_V1_1(10, 1) +#define UCSI_CONCAP_SWAP_TO_UFP_V1_1 UCSI_DECLARE_BITFIELD_V1_1(11, 1) +#define UCSI_CONCAP_SWAP_TO_SRC_V1_1 UCSI_DECLARE_BITFIELD_V1_1(12, 1) +#define UCSI_CONCAP_SWAP_TO_SNK_V1_1 UCSI_DECLARE_BITFIELD_V1_1(13, 1) +#define UCSI_CONCAP_EXT_OPMODE_V2_0 UCSI_DECLARE_BITFIELD_V2_0(14, 8) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN2_V2_0 UCSI_DECLARE_BITFIELD_V2_0(14, 1) +#define UCSI_CONCAP_EXT_OPMODE_EPR_SRC_V2_0 UCSI_DECLARE_BITFIELD_V2_0(15, 1) +#define UCSI_CONCAP_EXT_OPMODE_EPR_SINK_V2_0 UCSI_DECLARE_BITFIELD_V2_0(16, 1) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN3_V2_0 UCSI_DECLARE_BITFIELD_V2_0(17, 1) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN4_V2_0 UCSI_DECLARE_BITFIELD_V2_0(18, 1) +#define UCSI_CONCAP_MISC_V2_0 UCSI_DECLARE_BITFIELD_V2_0(22, 4) +#define UCSI_CONCAP_MISC_FW_UPDATE_V2_0 UCSI_DECLARE_BITFIELD_V2_0(22, 1) +#define UCSI_CONCAP_MISC_SECURITY_V2_0 UCSI_DECLARE_BITFIELD_V2_0(23, 1) +#define UCSI_CONCAP_REV_CURR_PROT_SUPPORT_V2_0 UCSI_DECLARE_BITFIELD_V2_0(26, 1) +#define UCSI_CONCAP_PARTNER_PD_REVISION_V2_1 UCSI_DECLARE_BITFIELD_V2_1(27, 2) + +/* Helpers for USB capability checks. */ +#define UCSI_CONCAP_USB2_SUPPORT(_con_) UCSI_CONCAP((_con_), OPMODE_USB2) +#define UCSI_CONCAP_USB3_SUPPORT(_con_) UCSI_CONCAP((_con_), OPMODE_USB3) +#define UCSI_CONCAP_USB4_SUPPORT(_con_) \ + ((_con_)->ucsi->version >= UCSI_VERSION_2_0 && \ + (UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN2_V2_0) | \ + UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN3_V2_0) | \ + UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN4_V2_0))) + +/* Get Connector Status Fields. */ +#define UCSI_CONSTAT_CHANGE UCSI_DECLARE_BITFIELD(0, 0, 16) +#define UCSI_CONSTAT_PWR_OPMODE UCSI_DECLARE_BITFIELD(0, 16, 3) #define UCSI_CONSTAT_PWR_OPMODE_NONE 0 #define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1 #define UCSI_CONSTAT_PWR_OPMODE_BC 2 #define UCSI_CONSTAT_PWR_OPMODE_PD 3 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5 -#define UCSI_CONSTAT_CONNECTED BIT(3) -#define UCSI_CONSTAT_PWR_DIR BIT(4) -#define UCSI_CONSTAT_PARTNER_FLAGS(_f_) (((_f_) & GENMASK(12, 5)) >> 5) -#define UCSI_CONSTAT_PARTNER_FLAG_USB 1 -#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE 2 -#define UCSI_CONSTAT_PARTNER_TYPE(_f_) (((_f_) & GENMASK(15, 13)) >> 13) +#define UCSI_CONSTAT_CONNECTED UCSI_DECLARE_BITFIELD(0, 19, 1) +#define UCSI_CONSTAT_PWR_DIR UCSI_DECLARE_BITFIELD(0, 20, 1) +#define UCSI_CONSTAT_PARTNER_FLAGS UCSI_DECLARE_BITFIELD(0, 21, 8) +#define UCSI_CONSTAT_PARTNER_FLAG_USB UCSI_DECLARE_BITFIELD(0, 21, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE UCSI_DECLARE_BITFIELD(0, 22, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_USB4_GEN3 UCSI_DECLARE_BITFIELD(0, 23, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_USB4_GEN4 UCSI_DECLARE_BITFIELD(0, 24, 1) +#define UCSI_CONSTAT_PARTNER_TYPE UCSI_DECLARE_BITFIELD(0, 29, 3) #define UCSI_CONSTAT_PARTNER_TYPE_DFP 1 #define UCSI_CONSTAT_PARTNER_TYPE_UFP 2 -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ #define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5 #define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6 - u32 request_data_obj; - - u8 pwr_status; -#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(1, 0)) +#define UCSI_CONSTAT_RDO UCSI_DECLARE_BITFIELD(0, 32, 32) +#define UCSI_CONSTAT_BC_STATUS UCSI_DECLARE_BITFIELD(0, 64, 2) #define UCSI_CONSTAT_BC_NOT_CHARGING 0 #define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1 #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 -} __packed; +#define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) + +/* Connector Status Change Bits. */ +#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) +#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2) +#define UCSI_CONSTAT_PDOS_CHANGE BIT(5) +#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6) +#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7) +#define UCSI_CONSTAT_CAM_CHANGE BIT(8) +#define UCSI_CONSTAT_BC_CHANGE BIT(9) +#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) +#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) +#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) +#define UCSI_CONSTAT_ERROR BIT(15) + +#define UCSI_DECLARE_BITFIELD_V1_1(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_1_1, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V1_2(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_1_2, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V2_0(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_2_0, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V2_1(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_2_1, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V3_0(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_3_0, (_offset_), (_size_)) + +#define UCSI_DECLARE_BITFIELD(_ver_, _offset_, _size_) \ +(struct ucsi_bitfield) { \ + .version = _ver_, \ + .offset = _offset_, \ + .size = _size_, \ +} + +struct ucsi_bitfield { + const u16 version; + const u8 offset; + const u8 size; +}; + +/** + * ucsi_bitfield_read - Read a field from UCSI command response + * @_map_: UCSI command response + * @_field_: The field offset in the response data structure + * @_ver_: UCSI version where the field was introduced + * + * Reads the fields in the command responses by first checking that the field is + * valid with the UCSI interface version that is used in the system. + * @_ver_ is the minimum UCSI version for the @_field_. If the UCSI interface is + * older than @_ver_, a warning is generated. + * + * Caveats: + * - Removed fields are not checked - @_ver_ is just the minimum UCSI version. + * + * Returns the value of @_field_, or 0 when the UCSI interface is older than + * @_ver_. + */ +#define ucsi_bitfield_read(_map_, _field_, _ver_) \ +({ \ + struct ucsi_bitfield f = (_field_); \ + WARN((_ver_) < f.version, \ + "Access to unsupported field at offset 0x%x (need version %04x)", \ + f.offset, f.version) ? 0 : \ + bitmap_read((_map_), f.offset, f.size); \ +}) + +/* Helpers to access cached command responses. */ +#define UCSI_CONCAP(_con_, _field_) \ + ucsi_bitfield_read((_con_)->cap, UCSI_CONCAP_##_field_, (_con_)->ucsi->version) + +#define UCSI_CONSTAT(_con_, _field_) \ + ucsi_bitfield_read((_con_)->status, UCSI_CONSTAT_##_field_, (_con_)->ucsi->version) /* -------------------------------------------------------------------------- */ @@ -433,8 +496,10 @@ struct ucsi_connector { struct typec_capability typec_cap; - struct ucsi_connector_status status; - struct ucsi_connector_capability cap; + /* Cached command responses. */ + DECLARE_BITMAP(cap, UCSI_GET_CONNECTOR_CAPABILITY_SIZE); + DECLARE_BITMAP(status, UCSI_GET_CONNECTOR_STATUS_SIZE); + struct power_supply *psy; struct power_supply_desc psy_desc; u32 rdo; diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 7a5dff8d9cc6c..5c55155519634 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -61,9 +61,11 @@ static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; + if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) { + ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); + if (ret) + return ret; + } memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); @@ -73,11 +75,6 @@ static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); @@ -102,48 +99,11 @@ static const struct ucsi_operations ucsi_acpi_ops = { .async_control = ucsi_acpi_async_control }; -static int -ucsi_zenbook_read_cci(struct ucsi *ucsi, u32 *cci) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) { - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; - } - - memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); - - return 0; -} - -static int -ucsi_zenbook_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - - /* UCSI_MESSAGE_IN is never read for PPM_RESET, return stored data */ - memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); - - return 0; -} - -static const struct ucsi_operations ucsi_zenbook_ops = { - .read_version = ucsi_acpi_read_version, - .read_cci = ucsi_zenbook_read_cci, - .read_message_in = ucsi_zenbook_read_message_in, - .sync_control = ucsi_sync_control_common, - .async_control = ucsi_acpi_async_control -}; - static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - struct ucsi_connector_status *status; int ret; ret = ucsi_acpi_read_message_in(ucsi, val, val_len); @@ -152,11 +112,9 @@ static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_le if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && ua->check_bogus_event) { - status = (struct ucsi_connector_status *)val; - /* Clear the bogus change */ - if (status->change == bogus_change) - status->change = 0; + if (*(u16 *)val == bogus_change) + *(u16 *)val = 0; ua->check_bogus_event = false; } @@ -190,13 +148,6 @@ static const struct ucsi_operations ucsi_gram_ops = { }; static const struct dmi_system_id ucsi_acpi_quirks[] = { - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), - }, - .driver_data = (void *)&ucsi_zenbook_ops, - }, { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), @@ -320,7 +271,7 @@ static struct platform_driver ucsi_acpi_platform_driver = { .acpi_match_table = ACPI_PTR(ucsi_acpi_match), }, .probe = ucsi_acpi_probe, - .remove_new = ucsi_acpi_remove, + .remove = ucsi_acpi_remove, }; module_platform_driver(ucsi_acpi_platform_driver); diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index bccfc03b5986d..fcb8e61136cfd 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -644,6 +644,10 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command) uc->has_multiple_dp) { con_index = (uc->last_cmd_sent >> 16) & UCSI_CMD_CONNECTOR_MASK; + if (con_index == 0) { + ret = -EINVAL; + goto unlock; + } con = &uc->ucsi->connector[con_index - 1]; ucsi_ccg_update_set_new_cam_cmd(uc, con, &command); } @@ -651,6 +655,7 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command) ret = ucsi_sync_control_common(ucsi, command); pm_runtime_put_sync(uc->dev); +unlock: mutex_unlock(&uc->lock); return ret; diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 03c0fa8edc8db..90948cd6d2972 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -172,12 +172,12 @@ static int pmic_glink_ucsi_async_control(struct ucsi *__ucsi, u64 command) static void pmic_glink_ucsi_update_connector(struct ucsi_connector *con) { struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); - int i; - for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { - if (ucsi->port_orientation[i]) - con->typec_cap.orientation_aware = true; - } + if (con->num > PMIC_GLINK_MAX_PORTS || + !ucsi->port_orientation[con->num - 1]) + return; + + con->typec_cap.orientation_aware = true; } static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) @@ -185,7 +185,7 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); int orientation; - if (con->num >= PMIC_GLINK_MAX_PORTS || + if (con->num > PMIC_GLINK_MAX_PORTS || !ucsi->port_orientation[con->num - 1]) return; @@ -322,7 +322,6 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, struct pmic_glink_ucsi *ucsi; struct device *dev = &adev->dev; const struct of_device_id *match; - struct fwnode_handle *fwnode; int ret; ucsi = devm_kzalloc(dev, sizeof(*ucsi), GFP_KERNEL); @@ -354,14 +353,13 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, ucsi_set_drvdata(ucsi->ucsi, ucsi); - device_for_each_child_node(dev, fwnode) { + device_for_each_child_node_scoped(dev, fwnode) { struct gpio_desc *desc; u32 port; ret = fwnode_property_read_u32(fwnode, "reg", &port); if (ret < 0) { dev_err(dev, "missing reg property of %pOFn\n", fwnode); - fwnode_handle_put(fwnode); return ret; } @@ -376,11 +374,10 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, if (!desc) continue; - if (IS_ERR(desc)) { - fwnode_handle_put(fwnode); + if (IS_ERR(desc)) return dev_err_probe(dev, PTR_ERR(desc), "unable to acquire orientation gpio\n"); - } + ucsi->port_orientation[port] = desc; } diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 8dac1edc74d4e..b03e5021c25bd 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1487,7 +1487,7 @@ static int vhci_hcd_resume(struct platform_device *pdev) static struct platform_driver vhci_driver = { .probe = vhci_hcd_probe, - .remove_new = vhci_hcd_remove, + .remove = vhci_hcd_remove, .suspend = vhci_hcd_suspend, .resume = vhci_hcd_resume, .driver = { diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 8bee553e48945..993e721cb840d 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -19,7 +19,7 @@ MODULE_PARM_DESC(num, "number of emulated controllers"); static struct platform_driver vudc_driver = { .probe = vudc_probe, - .remove_new = vudc_remove, + .remove = vudc_remove, .driver = { .name = GADGET_NAME, .dev_groups = vudc_groups, diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 7d0c83b5b0715..8455f08f5d406 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -368,7 +368,6 @@ static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr unsigned long lgcd = 0; int log_entity_size; unsigned long size; - u64 start = 0; int err; struct page *pg; unsigned int nsg; @@ -379,10 +378,9 @@ static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr struct device *dma = mvdev->vdev.dma_dev; for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1); - map; map = vhost_iotlb_itree_next(map, start, mr->end - 1)) { + map; map = vhost_iotlb_itree_next(map, mr->start, mr->end - 1)) { size = maplen(map, mr); lgcd = gcd(lgcd, size); - start += size; } log_entity_size = ilog2(lgcd); diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c index 0d632ba5d2a3c..451c639299eb3 100644 --- a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c +++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c @@ -486,31 +486,11 @@ static int vf_qm_load_data(struct hisi_acc_vf_core_device *hisi_acc_vdev, return 0; } -static int vf_qm_state_save(struct hisi_acc_vf_core_device *hisi_acc_vdev, - struct hisi_acc_vf_migration_file *migf) +static int vf_qm_read_data(struct hisi_qm *vf_qm, struct acc_vf_data *vf_data) { - struct acc_vf_data *vf_data = &migf->vf_data; - struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm; struct device *dev = &vf_qm->pdev->dev; int ret; - if (unlikely(qm_wait_dev_not_ready(vf_qm))) { - /* Update state and return with match data */ - vf_data->vf_qm_state = QM_NOT_READY; - hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state; - migf->total_length = QM_MATCH_SIZE; - return 0; - } - - vf_data->vf_qm_state = QM_READY; - hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state; - - ret = vf_qm_cache_wb(vf_qm); - if (ret) { - dev_err(dev, "failed to writeback QM Cache!\n"); - return ret; - } - ret = qm_get_regs(vf_qm, vf_data); if (ret) return -EINVAL; @@ -536,6 +516,38 @@ static int vf_qm_state_save(struct hisi_acc_vf_core_device *hisi_acc_vdev, return -EINVAL; } + return 0; +} + +static int vf_qm_state_save(struct hisi_acc_vf_core_device *hisi_acc_vdev, + struct hisi_acc_vf_migration_file *migf) +{ + struct acc_vf_data *vf_data = &migf->vf_data; + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm; + struct device *dev = &vf_qm->pdev->dev; + int ret; + + if (unlikely(qm_wait_dev_not_ready(vf_qm))) { + /* Update state and return with match data */ + vf_data->vf_qm_state = QM_NOT_READY; + hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state; + migf->total_length = QM_MATCH_SIZE; + return 0; + } + + vf_data->vf_qm_state = QM_READY; + hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state; + + ret = vf_qm_cache_wb(vf_qm); + if (ret) { + dev_err(dev, "failed to writeback QM Cache!\n"); + return ret; + } + + ret = vf_qm_read_data(vf_qm, vf_data); + if (ret) + return -EINVAL; + migf->total_length = sizeof(struct acc_vf_data); return 0; } @@ -615,21 +627,43 @@ static void hisi_acc_vf_disable_fd(struct hisi_acc_vf_migration_file *migf) mutex_unlock(&migf->lock); } +static void +hisi_acc_debug_migf_copy(struct hisi_acc_vf_core_device *hisi_acc_vdev, + struct hisi_acc_vf_migration_file *src_migf) +{ + struct hisi_acc_vf_migration_file *dst_migf = hisi_acc_vdev->debug_migf; + + if (!dst_migf) + return; + + dst_migf->total_length = src_migf->total_length; + memcpy(&dst_migf->vf_data, &src_migf->vf_data, + sizeof(struct acc_vf_data)); +} + static void hisi_acc_vf_disable_fds(struct hisi_acc_vf_core_device *hisi_acc_vdev) { if (hisi_acc_vdev->resuming_migf) { + hisi_acc_debug_migf_copy(hisi_acc_vdev, hisi_acc_vdev->resuming_migf); hisi_acc_vf_disable_fd(hisi_acc_vdev->resuming_migf); fput(hisi_acc_vdev->resuming_migf->filp); hisi_acc_vdev->resuming_migf = NULL; } if (hisi_acc_vdev->saving_migf) { + hisi_acc_debug_migf_copy(hisi_acc_vdev, hisi_acc_vdev->saving_migf); hisi_acc_vf_disable_fd(hisi_acc_vdev->saving_migf); fput(hisi_acc_vdev->saving_migf->filp); hisi_acc_vdev->saving_migf = NULL; } } +static struct hisi_acc_vf_core_device *hisi_acc_get_vf_dev(struct vfio_device *vdev) +{ + return container_of(vdev, struct hisi_acc_vf_core_device, + core_device.vdev); +} + static void hisi_acc_vf_reset(struct hisi_acc_vf_core_device *hisi_acc_vdev) { hisi_acc_vdev->vf_qm_state = QM_NOT_READY; @@ -1031,8 +1065,7 @@ static struct file * hisi_acc_vfio_pci_set_device_state(struct vfio_device *vdev, enum vfio_device_mig_state new_state) { - struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(vdev, - struct hisi_acc_vf_core_device, core_device.vdev); + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(vdev); enum vfio_device_mig_state next_state; struct file *res = NULL; int ret; @@ -1073,8 +1106,7 @@ static int hisi_acc_vfio_pci_get_device_state(struct vfio_device *vdev, enum vfio_device_mig_state *curr_state) { - struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(vdev, - struct hisi_acc_vf_core_device, core_device.vdev); + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(vdev); mutex_lock(&hisi_acc_vdev->state_mutex); *curr_state = hisi_acc_vdev->mig_state; @@ -1276,10 +1308,132 @@ static long hisi_acc_vfio_pci_ioctl(struct vfio_device *core_vdev, unsigned int return vfio_pci_core_ioctl(core_vdev, cmd, arg); } +static int hisi_acc_vf_debug_check(struct seq_file *seq, struct vfio_device *vdev) +{ + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(vdev); + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm; + int ret; + + lockdep_assert_held(&hisi_acc_vdev->open_mutex); + /* + * When the device is not opened, the io_base is not mapped. + * The driver cannot perform device read and write operations. + */ + if (!hisi_acc_vdev->dev_opened) { + seq_puts(seq, "device not opened!\n"); + return -EINVAL; + } + + ret = qm_wait_dev_not_ready(vf_qm); + if (ret) { + seq_puts(seq, "VF device not ready!\n"); + return -EBUSY; + } + + return 0; +} + +static int hisi_acc_vf_debug_cmd(struct seq_file *seq, void *data) +{ + struct device *vf_dev = seq->private; + struct vfio_pci_core_device *core_device = dev_get_drvdata(vf_dev); + struct vfio_device *vdev = &core_device->vdev; + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(vdev); + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm; + u64 value; + int ret; + + mutex_lock(&hisi_acc_vdev->open_mutex); + ret = hisi_acc_vf_debug_check(seq, vdev); + if (ret) { + mutex_unlock(&hisi_acc_vdev->open_mutex); + return ret; + } + + value = readl(vf_qm->io_base + QM_MB_CMD_SEND_BASE); + if (value == QM_MB_CMD_NOT_READY) { + mutex_unlock(&hisi_acc_vdev->open_mutex); + seq_puts(seq, "mailbox cmd channel not ready!\n"); + return -EINVAL; + } + mutex_unlock(&hisi_acc_vdev->open_mutex); + seq_puts(seq, "mailbox cmd channel ready!\n"); + + return 0; +} + +static int hisi_acc_vf_dev_read(struct seq_file *seq, void *data) +{ + struct device *vf_dev = seq->private; + struct vfio_pci_core_device *core_device = dev_get_drvdata(vf_dev); + struct vfio_device *vdev = &core_device->vdev; + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(vdev); + size_t vf_data_sz = offsetofend(struct acc_vf_data, padding); + struct acc_vf_data *vf_data; + int ret; + + mutex_lock(&hisi_acc_vdev->open_mutex); + ret = hisi_acc_vf_debug_check(seq, vdev); + if (ret) { + mutex_unlock(&hisi_acc_vdev->open_mutex); + return ret; + } + + mutex_lock(&hisi_acc_vdev->state_mutex); + vf_data = kzalloc(sizeof(*vf_data), GFP_KERNEL); + if (!vf_data) { + ret = -ENOMEM; + goto mutex_release; + } + + vf_data->vf_qm_state = hisi_acc_vdev->vf_qm_state; + ret = vf_qm_read_data(&hisi_acc_vdev->vf_qm, vf_data); + if (ret) + goto migf_err; + + seq_hex_dump(seq, "Dev Data:", DUMP_PREFIX_OFFSET, 16, 1, + (const void *)vf_data, vf_data_sz, false); + + seq_printf(seq, + "guest driver load: %u\n" + "data size: %lu\n", + hisi_acc_vdev->vf_qm_state, + sizeof(struct acc_vf_data)); + +migf_err: + kfree(vf_data); +mutex_release: + mutex_unlock(&hisi_acc_vdev->state_mutex); + mutex_unlock(&hisi_acc_vdev->open_mutex); + + return ret; +} + +static int hisi_acc_vf_migf_read(struct seq_file *seq, void *data) +{ + struct device *vf_dev = seq->private; + struct vfio_pci_core_device *core_device = dev_get_drvdata(vf_dev); + struct vfio_device *vdev = &core_device->vdev; + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(vdev); + size_t vf_data_sz = offsetofend(struct acc_vf_data, padding); + struct hisi_acc_vf_migration_file *debug_migf = hisi_acc_vdev->debug_migf; + + /* Check whether the live migration operation has been performed */ + if (debug_migf->total_length < QM_MATCH_SIZE) { + seq_puts(seq, "device not migrated!\n"); + return -EAGAIN; + } + + seq_hex_dump(seq, "Mig Data:", DUMP_PREFIX_OFFSET, 16, 1, + (const void *)&debug_migf->vf_data, vf_data_sz, false); + seq_printf(seq, "migrate data length: %lu\n", debug_migf->total_length); + + return 0; +} + static int hisi_acc_vfio_pci_open_device(struct vfio_device *core_vdev) { - struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(core_vdev, - struct hisi_acc_vf_core_device, core_device.vdev); + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(core_vdev); struct vfio_pci_core_device *vdev = &hisi_acc_vdev->core_device; int ret; @@ -1288,12 +1442,16 @@ static int hisi_acc_vfio_pci_open_device(struct vfio_device *core_vdev) return ret; if (core_vdev->mig_ops) { + mutex_lock(&hisi_acc_vdev->open_mutex); ret = hisi_acc_vf_qm_init(hisi_acc_vdev); if (ret) { + mutex_unlock(&hisi_acc_vdev->open_mutex); vfio_pci_core_disable(vdev); return ret; } hisi_acc_vdev->mig_state = VFIO_DEVICE_STATE_RUNNING; + hisi_acc_vdev->dev_opened = true; + mutex_unlock(&hisi_acc_vdev->open_mutex); } vfio_pci_core_finish_enable(vdev); @@ -1302,11 +1460,13 @@ static int hisi_acc_vfio_pci_open_device(struct vfio_device *core_vdev) static void hisi_acc_vfio_pci_close_device(struct vfio_device *core_vdev) { - struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(core_vdev, - struct hisi_acc_vf_core_device, core_device.vdev); + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(core_vdev); struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm; + mutex_lock(&hisi_acc_vdev->open_mutex); + hisi_acc_vdev->dev_opened = false; iounmap(vf_qm->io_base); + mutex_unlock(&hisi_acc_vdev->open_mutex); vfio_pci_core_close_device(core_vdev); } @@ -1318,8 +1478,7 @@ static const struct vfio_migration_ops hisi_acc_vfio_pci_migrn_state_ops = { static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev) { - struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(core_vdev, - struct hisi_acc_vf_core_device, core_device.vdev); + struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_get_vf_dev(core_vdev); struct pci_dev *pdev = to_pci_dev(core_vdev->dev); struct hisi_qm *pf_qm = hisi_acc_get_pf_qm(pdev); @@ -1327,6 +1486,7 @@ static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev) hisi_acc_vdev->pf_qm = pf_qm; hisi_acc_vdev->vf_dev = pdev; mutex_init(&hisi_acc_vdev->state_mutex); + mutex_init(&hisi_acc_vdev->open_mutex); core_vdev->migration_flags = VFIO_MIGRATION_STOP_COPY | VFIO_MIGRATION_PRE_COPY; core_vdev->mig_ops = &hisi_acc_vfio_pci_migrn_state_ops; @@ -1372,6 +1532,47 @@ static const struct vfio_device_ops hisi_acc_vfio_pci_ops = { .detach_ioas = vfio_iommufd_physical_detach_ioas, }; +static void hisi_acc_vfio_debug_init(struct hisi_acc_vf_core_device *hisi_acc_vdev) +{ + struct vfio_device *vdev = &hisi_acc_vdev->core_device.vdev; + struct hisi_acc_vf_migration_file *migf; + struct dentry *vfio_dev_migration; + struct dentry *vfio_hisi_acc; + struct device *dev = vdev->dev; + + if (!debugfs_initialized() || + !IS_ENABLED(CONFIG_VFIO_DEBUGFS)) + return; + + if (vdev->ops != &hisi_acc_vfio_pci_migrn_ops) + return; + + vfio_dev_migration = debugfs_lookup("migration", vdev->debug_root); + if (!vfio_dev_migration) { + dev_err(dev, "failed to lookup migration debugfs file!\n"); + return; + } + + migf = kzalloc(sizeof(*migf), GFP_KERNEL); + if (!migf) + return; + hisi_acc_vdev->debug_migf = migf; + + vfio_hisi_acc = debugfs_create_dir("hisi_acc", vfio_dev_migration); + debugfs_create_devm_seqfile(dev, "dev_data", vfio_hisi_acc, + hisi_acc_vf_dev_read); + debugfs_create_devm_seqfile(dev, "migf_data", vfio_hisi_acc, + hisi_acc_vf_migf_read); + debugfs_create_devm_seqfile(dev, "cmd_state", vfio_hisi_acc, + hisi_acc_vf_debug_cmd); +} + +static void hisi_acc_vf_debugfs_exit(struct hisi_acc_vf_core_device *hisi_acc_vdev) +{ + kfree(hisi_acc_vdev->debug_migf); + hisi_acc_vdev->debug_migf = NULL; +} + static int hisi_acc_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct hisi_acc_vf_core_device *hisi_acc_vdev; @@ -1398,6 +1599,8 @@ static int hisi_acc_vfio_pci_probe(struct pci_dev *pdev, const struct pci_device ret = vfio_pci_core_register_device(&hisi_acc_vdev->core_device); if (ret) goto out_put_vdev; + + hisi_acc_vfio_debug_init(hisi_acc_vdev); return 0; out_put_vdev: @@ -1410,6 +1613,7 @@ static void hisi_acc_vfio_pci_remove(struct pci_dev *pdev) struct hisi_acc_vf_core_device *hisi_acc_vdev = hisi_acc_drvdata(pdev); vfio_pci_core_unregister_device(&hisi_acc_vdev->core_device); + hisi_acc_vf_debugfs_exit(hisi_acc_vdev); vfio_put_device(&hisi_acc_vdev->core_device.vdev); } diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h index 5bab46602fad2..245d7537b2bcd 100644 --- a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h +++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h @@ -32,6 +32,7 @@ #define QM_SQC_VFT_BASE_MASK_V2 GENMASK(15, 0) #define QM_SQC_VFT_NUM_SHIFT_V2 45 #define QM_SQC_VFT_NUM_MASK_V2 GENMASK(9, 0) +#define QM_MB_CMD_NOT_READY 0xffffffff /* RW regs */ #define QM_REGS_MAX_LEN 7 @@ -99,6 +100,13 @@ struct hisi_acc_vf_migration_file { struct hisi_acc_vf_core_device { struct vfio_pci_core_device core_device; u8 match_done; + /* + * io_base is only valid when dev_opened is true, + * which is protected by open_mutex. + */ + bool dev_opened; + /* Ensure the accuracy of dev_opened operation */ + struct mutex open_mutex; /* For migration state */ struct mutex state_mutex; @@ -107,9 +115,20 @@ struct hisi_acc_vf_core_device { struct pci_dev *vf_dev; struct hisi_qm *pf_qm; struct hisi_qm vf_qm; + /* + * vf_qm_state represents the QM_VF_STATE register value. + * It is set by Guest driver for the ACC VF dev indicating + * the driver has loaded and configured the dev correctly. + */ u32 vf_qm_state; int vf_id; struct hisi_acc_vf_migration_file *resuming_migf; struct hisi_acc_vf_migration_file *saving_migf; + + /* + * It holds migration data corresponding to the last migration + * and is used by the debugfs interface to report it. + */ + struct hisi_acc_vf_migration_file *debug_migf; }; #endif /* HISI_ACC_VFIO_PCI_H */ diff --git a/drivers/vfio/pci/mlx5/cmd.c b/drivers/vfio/pci/mlx5/cmd.c index 41a4b0cf42975..7527e277c8989 100644 --- a/drivers/vfio/pci/mlx5/cmd.c +++ b/drivers/vfio/pci/mlx5/cmd.c @@ -423,6 +423,7 @@ static int mlx5vf_add_migration_pages(struct mlx5_vhca_data_buffer *buf, unsigned long filled; unsigned int to_fill; int ret; + int i; to_fill = min_t(unsigned int, npages, PAGE_SIZE / sizeof(*page_list)); page_list = kvzalloc(to_fill * sizeof(*page_list), GFP_KERNEL_ACCOUNT); @@ -443,7 +444,7 @@ static int mlx5vf_add_migration_pages(struct mlx5_vhca_data_buffer *buf, GFP_KERNEL_ACCOUNT); if (ret) - goto err; + goto err_append; buf->allocated_length += filled * PAGE_SIZE; /* clean input for another bulk allocation */ memset(page_list, 0, filled * sizeof(*page_list)); @@ -454,6 +455,9 @@ static int mlx5vf_add_migration_pages(struct mlx5_vhca_data_buffer *buf, kvfree(page_list); return 0; +err_append: + for (i = filled - 1; i >= 0; i--) + __free_page(page_list[i]); err: kvfree(page_list); return ret; diff --git a/drivers/vfio/pci/mlx5/main.c b/drivers/vfio/pci/mlx5/main.c index 242c23eef452e..8833e60d42f56 100644 --- a/drivers/vfio/pci/mlx5/main.c +++ b/drivers/vfio/pci/mlx5/main.c @@ -640,14 +640,11 @@ mlx5vf_pci_save_device_data(struct mlx5vf_pci_core_device *mvdev, bool track) O_RDONLY); if (IS_ERR(migf->filp)) { ret = PTR_ERR(migf->filp); - goto end; + kfree(migf); + return ERR_PTR(ret); } migf->mvdev = mvdev; - ret = mlx5vf_cmd_alloc_pd(migf); - if (ret) - goto out_free; - stream_open(migf->filp->f_inode, migf->filp); mutex_init(&migf->lock); init_waitqueue_head(&migf->poll_wait); @@ -663,6 +660,11 @@ mlx5vf_pci_save_device_data(struct mlx5vf_pci_core_device *mvdev, bool track) INIT_LIST_HEAD(&migf->buf_list); INIT_LIST_HEAD(&migf->avail_list); spin_lock_init(&migf->list_lock); + + ret = mlx5vf_cmd_alloc_pd(migf); + if (ret) + goto out; + ret = mlx5vf_cmd_query_vhca_migration_state(mvdev, &length, &full_size, 0); if (ret) goto out_pd; @@ -692,10 +694,8 @@ mlx5vf_pci_save_device_data(struct mlx5vf_pci_core_device *mvdev, bool track) mlx5vf_free_data_buffer(buf); out_pd: mlx5fv_cmd_clean_migf_resources(migf); -out_free: +out: fput(migf->filp); -end: - kfree(migf); return ERR_PTR(ret); } @@ -1016,13 +1016,19 @@ mlx5vf_pci_resume_device_data(struct mlx5vf_pci_core_device *mvdev) O_WRONLY); if (IS_ERR(migf->filp)) { ret = PTR_ERR(migf->filp); - goto end; + kfree(migf); + return ERR_PTR(ret); } + stream_open(migf->filp->f_inode, migf->filp); + mutex_init(&migf->lock); + INIT_LIST_HEAD(&migf->buf_list); + INIT_LIST_HEAD(&migf->avail_list); + spin_lock_init(&migf->list_lock); migf->mvdev = mvdev; ret = mlx5vf_cmd_alloc_pd(migf); if (ret) - goto out_free; + goto out; buf = mlx5vf_alloc_data_buffer(migf, 0, DMA_TO_DEVICE); if (IS_ERR(buf)) { @@ -1041,20 +1047,13 @@ mlx5vf_pci_resume_device_data(struct mlx5vf_pci_core_device *mvdev) migf->buf_header[0] = buf; migf->load_state = MLX5_VF_LOAD_STATE_READ_HEADER; - stream_open(migf->filp->f_inode, migf->filp); - mutex_init(&migf->lock); - INIT_LIST_HEAD(&migf->buf_list); - INIT_LIST_HEAD(&migf->avail_list); - spin_lock_init(&migf->list_lock); return migf; out_buf: mlx5vf_free_data_buffer(migf->buf[0]); out_pd: mlx5vf_cmd_dealloc_pd(migf); -out_free: +out: fput(migf->filp); -end: - kfree(migf); return ERR_PTR(ret); } diff --git a/drivers/vfio/pci/nvgrace-gpu/main.c b/drivers/vfio/pci/nvgrace-gpu/main.c index a7fd018aa5483..a467085038f0c 100644 --- a/drivers/vfio/pci/nvgrace-gpu/main.c +++ b/drivers/vfio/pci/nvgrace-gpu/main.c @@ -866,6 +866,8 @@ static const struct pci_device_id nvgrace_gpu_vfio_pci_table[] = { { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_NVIDIA, 0x2342) }, /* GH200 480GB */ { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_NVIDIA, 0x2345) }, + /* GH200 SKU */ + { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_NVIDIA, 0x2348) }, {} }; diff --git a/drivers/vfio/pci/qat/main.c b/drivers/vfio/pci/qat/main.c index be3644ced17be..c78cb6de93906 100644 --- a/drivers/vfio/pci/qat/main.c +++ b/drivers/vfio/pci/qat/main.c @@ -304,7 +304,7 @@ static ssize_t qat_vf_resume_write(struct file *filp, const char __user *buf, offs = &filp->f_pos; if (*offs < 0 || - check_add_overflow((loff_t)len, *offs, &end)) + check_add_overflow(len, *offs, &end)) return -EOVERFLOW; if (end > mig_dev->state_size) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 97422aafaa7b5..ea2745c1ac5e6 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -313,6 +313,10 @@ static int vfio_virt_config_read(struct vfio_pci_core_device *vdev, int pos, return count; } +static struct perm_bits direct_ro_perms = { + .readfn = vfio_direct_config_read, +}; + /* Default capability regions to read-only, no-virtualization */ static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = { [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } @@ -1897,9 +1901,17 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_core_device *vdev, char __user cap_start = *ppos; } else { if (*ppos >= PCI_CFG_SPACE_SIZE) { - WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX); + /* + * We can get a cap_id that exceeds PCI_EXT_CAP_ID_MAX + * if we're hiding an unknown capability at the start + * of the extended capability list. Use default, ro + * access, which will virtualize the id and next values. + */ + if (cap_id > PCI_EXT_CAP_ID_MAX) + perm = &direct_ro_perms; + else + perm = &ecap_perms[cap_id]; - perm = &ecap_perms[cap_id]; cap_start = vfio_find_cap_start(vdev, *ppos); } else { WARN_ON(cap_id > PCI_CAP_ID_MAX); diff --git a/drivers/vfio/pci/virtio/Kconfig b/drivers/vfio/pci/virtio/Kconfig index bd80eca4a196c..2770f7eb702cc 100644 --- a/drivers/vfio/pci/virtio/Kconfig +++ b/drivers/vfio/pci/virtio/Kconfig @@ -1,15 +1,31 @@ # SPDX-License-Identifier: GPL-2.0-only config VIRTIO_VFIO_PCI - tristate "VFIO support for VIRTIO NET PCI devices" - depends on VIRTIO_PCI && VIRTIO_PCI_ADMIN_LEGACY - select VFIO_PCI_CORE - help - This provides support for exposing VIRTIO NET VF devices which support - legacy IO access, using the VFIO framework that can work with a legacy - virtio driver in the guest. - Based on PCIe spec, VFs do not support I/O Space. - As of that this driver emulates I/O BAR in software to let a VF be - seen as a transitional device by its users and let it work with - a legacy driver. - - If you don't know what to do here, say N. + tristate "VFIO support for VIRTIO NET PCI VF devices" + depends on VIRTIO_PCI + select VFIO_PCI_CORE + help + This provides migration support for VIRTIO NET PCI VF devices + using the VFIO framework. Migration support requires the + SR-IOV PF device to support specific VIRTIO extensions, + otherwise this driver provides no additional functionality + beyond vfio-pci. + + Migration support in this driver relies on dirty page tracking + provided by the IOMMU hardware and exposed through IOMMUFD, any + other use cases are dis-recommended. + + If you don't know what to do here, say N. + +config VIRTIO_VFIO_PCI_ADMIN_LEGACY + bool "Legacy I/O support for VIRTIO NET PCI VF devices" + depends on VIRTIO_VFIO_PCI && VIRTIO_PCI_ADMIN_LEGACY + default y + help + This extends the virtio-vfio-pci driver to support legacy I/O + access, allowing use of legacy virtio drivers with VIRTIO NET + PCI VF devices. Legacy I/O support requires the SR-IOV PF + device to support and enable specific VIRTIO extensions, + otherwise this driver provides no additional functionality + beyond vfio-pci. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/pci/virtio/Makefile b/drivers/vfio/pci/virtio/Makefile index 7171105baf330..d9b0bb40d6b34 100644 --- a/drivers/vfio/pci/virtio/Makefile +++ b/drivers/vfio/pci/virtio/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIRTIO_VFIO_PCI) += virtio-vfio-pci.o -virtio-vfio-pci-y := main.o +virtio-vfio-pci-y := main.o migrate.o +virtio-vfio-pci-$(CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY) += legacy_io.o diff --git a/drivers/vfio/pci/virtio/common.h b/drivers/vfio/pci/virtio/common.h new file mode 100644 index 0000000000000..c7d7e27af386e --- /dev/null +++ b/drivers/vfio/pci/virtio/common.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef VIRTIO_VFIO_COMMON_H +#define VIRTIO_VFIO_COMMON_H + +#include +#include +#include +#include + +enum virtiovf_migf_state { + VIRTIOVF_MIGF_STATE_ERROR = 1, + VIRTIOVF_MIGF_STATE_PRECOPY = 2, + VIRTIOVF_MIGF_STATE_COMPLETE = 3, +}; + +enum virtiovf_load_state { + VIRTIOVF_LOAD_STATE_READ_HEADER, + VIRTIOVF_LOAD_STATE_PREP_HEADER_DATA, + VIRTIOVF_LOAD_STATE_READ_HEADER_DATA, + VIRTIOVF_LOAD_STATE_PREP_CHUNK, + VIRTIOVF_LOAD_STATE_READ_CHUNK, + VIRTIOVF_LOAD_STATE_LOAD_CHUNK, +}; + +struct virtiovf_data_buffer { + struct sg_append_table table; + loff_t start_pos; + u64 length; + u64 allocated_length; + struct list_head buf_elm; + u8 include_header_object:1; + struct virtiovf_migration_file *migf; + /* Optimize virtiovf_get_migration_page() for sequential access */ + struct scatterlist *last_offset_sg; + unsigned int sg_last_entry; + unsigned long last_offset; +}; + +enum virtiovf_migf_header_flags { + VIRTIOVF_MIGF_HEADER_FLAGS_TAG_MANDATORY = 0, + VIRTIOVF_MIGF_HEADER_FLAGS_TAG_OPTIONAL = 1 << 0, +}; + +enum virtiovf_migf_header_tag { + VIRTIOVF_MIGF_HEADER_TAG_DEVICE_DATA = 0, +}; + +struct virtiovf_migration_header { + __le64 record_size; + /* For future use in case we may need to change the kernel protocol */ + __le32 flags; /* Use virtiovf_migf_header_flags */ + __le32 tag; /* Use virtiovf_migf_header_tag */ + __u8 data[]; /* Its size is given in the record_size */ +}; + +struct virtiovf_migration_file { + struct file *filp; + /* synchronize access to the file state */ + struct mutex lock; + loff_t max_pos; + u64 pre_copy_initial_bytes; + struct ratelimit_state pre_copy_rl_state; + u64 record_size; + u32 record_tag; + u8 has_obj_id:1; + u32 obj_id; + enum virtiovf_migf_state state; + enum virtiovf_load_state load_state; + /* synchronize access to the lists */ + spinlock_t list_lock; + struct list_head buf_list; + struct list_head avail_list; + struct virtiovf_data_buffer *buf; + struct virtiovf_data_buffer *buf_header; + struct virtiovf_pci_core_device *virtvdev; +}; + +struct virtiovf_pci_core_device { + struct vfio_pci_core_device core_device; +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY + u8 *bar0_virtual_buf; + /* synchronize access to the virtual buf */ + struct mutex bar_mutex; + void __iomem *notify_addr; + u64 notify_offset; + __le32 pci_base_addr_0; + __le16 pci_cmd; + u8 bar0_virtual_buf_size; + u8 notify_bar; +#endif + + /* LM related */ + u8 migrate_cap:1; + u8 deferred_reset:1; + /* protect migration state */ + struct mutex state_mutex; + enum vfio_device_mig_state mig_state; + /* protect the reset_done flow */ + spinlock_t reset_lock; + struct virtiovf_migration_file *resuming_migf; + struct virtiovf_migration_file *saving_migf; +}; + +void virtiovf_set_migratable(struct virtiovf_pci_core_device *virtvdev); +void virtiovf_open_migration(struct virtiovf_pci_core_device *virtvdev); +void virtiovf_close_migration(struct virtiovf_pci_core_device *virtvdev); +void virtiovf_migration_reset_done(struct pci_dev *pdev); + +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY +int virtiovf_open_legacy_io(struct virtiovf_pci_core_device *virtvdev); +long virtiovf_vfio_pci_core_ioctl(struct vfio_device *core_vdev, + unsigned int cmd, unsigned long arg); +int virtiovf_pci_ioctl_get_region_info(struct vfio_device *core_vdev, + unsigned int cmd, unsigned long arg); +ssize_t virtiovf_pci_core_write(struct vfio_device *core_vdev, + const char __user *buf, size_t count, + loff_t *ppos); +ssize_t virtiovf_pci_core_read(struct vfio_device *core_vdev, char __user *buf, + size_t count, loff_t *ppos); +bool virtiovf_support_legacy_io(struct pci_dev *pdev); +int virtiovf_init_legacy_io(struct virtiovf_pci_core_device *virtvdev); +void virtiovf_release_legacy_io(struct virtiovf_pci_core_device *virtvdev); +void virtiovf_legacy_io_reset_done(struct pci_dev *pdev); +#endif + +#endif /* VIRTIO_VFIO_COMMON_H */ diff --git a/drivers/vfio/pci/virtio/legacy_io.c b/drivers/vfio/pci/virtio/legacy_io.c new file mode 100644 index 0000000000000..20382ee15faca --- /dev/null +++ b/drivers/vfio/pci/virtio/legacy_io.c @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +static int +virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, + loff_t pos, char __user *buf, + size_t count, bool read) +{ + bool msix_enabled = + (virtvdev->core_device.irq_type == VFIO_PCI_MSIX_IRQ_INDEX); + struct pci_dev *pdev = virtvdev->core_device.pdev; + u8 *bar0_buf = virtvdev->bar0_virtual_buf; + bool common; + u8 offset; + int ret; + + common = pos < VIRTIO_PCI_CONFIG_OFF(msix_enabled); + /* offset within the relevant configuration area */ + offset = common ? pos : pos - VIRTIO_PCI_CONFIG_OFF(msix_enabled); + mutex_lock(&virtvdev->bar_mutex); + if (read) { + if (common) + ret = virtio_pci_admin_legacy_common_io_read(pdev, offset, + count, bar0_buf + pos); + else + ret = virtio_pci_admin_legacy_device_io_read(pdev, offset, + count, bar0_buf + pos); + if (ret) + goto out; + if (copy_to_user(buf, bar0_buf + pos, count)) + ret = -EFAULT; + } else { + if (copy_from_user(bar0_buf + pos, buf, count)) { + ret = -EFAULT; + goto out; + } + + if (common) + ret = virtio_pci_admin_legacy_common_io_write(pdev, offset, + count, bar0_buf + pos); + else + ret = virtio_pci_admin_legacy_device_io_write(pdev, offset, + count, bar0_buf + pos); + } +out: + mutex_unlock(&virtvdev->bar_mutex); + return ret; +} + +static int +virtiovf_pci_bar0_rw(struct virtiovf_pci_core_device *virtvdev, + loff_t pos, char __user *buf, + size_t count, bool read) +{ + struct vfio_pci_core_device *core_device = &virtvdev->core_device; + struct pci_dev *pdev = core_device->pdev; + u16 queue_notify; + int ret; + + if (!(le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO)) + return -EIO; + + if (pos + count > virtvdev->bar0_virtual_buf_size) + return -EINVAL; + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) { + pci_info_ratelimited(pdev, "runtime resume failed %d\n", ret); + return -EIO; + } + + switch (pos) { + case VIRTIO_PCI_QUEUE_NOTIFY: + if (count != sizeof(queue_notify)) { + ret = -EINVAL; + goto end; + } + if (read) { + ret = vfio_pci_core_ioread16(core_device, true, &queue_notify, + virtvdev->notify_addr); + if (ret) + goto end; + if (copy_to_user(buf, &queue_notify, + sizeof(queue_notify))) { + ret = -EFAULT; + goto end; + } + } else { + if (copy_from_user(&queue_notify, buf, count)) { + ret = -EFAULT; + goto end; + } + ret = vfio_pci_core_iowrite16(core_device, true, queue_notify, + virtvdev->notify_addr); + } + break; + default: + ret = virtiovf_issue_legacy_rw_cmd(virtvdev, pos, buf, count, + read); + } + +end: + pm_runtime_put(&pdev->dev); + return ret ? ret : count; +} + +static ssize_t virtiovf_pci_read_config(struct vfio_device *core_vdev, + char __user *buf, size_t count, + loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + size_t register_offset; + loff_t copy_offset; + size_t copy_count; + __le32 val32; + __le16 val16; + u8 val8; + int ret; + + ret = vfio_pci_core_read(core_vdev, buf, count, ppos); + if (ret < 0) + return ret; + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_DEVICE_ID, + sizeof(val16), ©_offset, + ©_count, ®ister_offset)) { + val16 = cpu_to_le16(VIRTIO_TRANS_ID_NET); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, copy_count)) + return -EFAULT; + } + + if ((le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO) && + vfio_pci_core_range_intersect_range(pos, count, PCI_COMMAND, + sizeof(val16), ©_offset, + ©_count, ®ister_offset)) { + if (copy_from_user((void *)&val16 + register_offset, buf + copy_offset, + copy_count)) + return -EFAULT; + val16 |= cpu_to_le16(PCI_COMMAND_IO); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, + copy_count)) + return -EFAULT; + } + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_REVISION_ID, + sizeof(val8), ©_offset, + ©_count, ®ister_offset)) { + /* Transional needs to have revision 0 */ + val8 = 0; + if (copy_to_user(buf + copy_offset, &val8, copy_count)) + return -EFAULT; + } + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, + sizeof(val32), ©_offset, + ©_count, ®ister_offset)) { + u32 bar_mask = ~(virtvdev->bar0_virtual_buf_size - 1); + u32 pci_base_addr_0 = le32_to_cpu(virtvdev->pci_base_addr_0); + + val32 = cpu_to_le32((pci_base_addr_0 & bar_mask) | PCI_BASE_ADDRESS_SPACE_IO); + if (copy_to_user(buf + copy_offset, (void *)&val32 + register_offset, copy_count)) + return -EFAULT; + } + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_SUBSYSTEM_ID, + sizeof(val16), ©_offset, + ©_count, ®ister_offset)) { + /* + * Transitional devices use the PCI subsystem device id as + * virtio device id, same as legacy driver always did. + */ + val16 = cpu_to_le16(VIRTIO_ID_NET); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, + copy_count)) + return -EFAULT; + } + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_SUBSYSTEM_VENDOR_ID, + sizeof(val16), ©_offset, + ©_count, ®ister_offset)) { + val16 = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, + copy_count)) + return -EFAULT; + } + + return count; +} + +ssize_t virtiovf_pci_core_read(struct vfio_device *core_vdev, char __user *buf, + size_t count, loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + + if (!count) + return 0; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return virtiovf_pci_read_config(core_vdev, buf, count, ppos); + + if (index == VFIO_PCI_BAR0_REGION_INDEX) + return virtiovf_pci_bar0_rw(virtvdev, pos, buf, count, true); + + return vfio_pci_core_read(core_vdev, buf, count, ppos); +} + +static ssize_t virtiovf_pci_write_config(struct vfio_device *core_vdev, + const char __user *buf, size_t count, + loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + size_t register_offset; + loff_t copy_offset; + size_t copy_count; + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_COMMAND, + sizeof(virtvdev->pci_cmd), + ©_offset, ©_count, + ®ister_offset)) { + if (copy_from_user((void *)&virtvdev->pci_cmd + register_offset, + buf + copy_offset, + copy_count)) + return -EFAULT; + } + + if (vfio_pci_core_range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, + sizeof(virtvdev->pci_base_addr_0), + ©_offset, ©_count, + ®ister_offset)) { + if (copy_from_user((void *)&virtvdev->pci_base_addr_0 + register_offset, + buf + copy_offset, + copy_count)) + return -EFAULT; + } + + return vfio_pci_core_write(core_vdev, buf, count, ppos); +} + +ssize_t virtiovf_pci_core_write(struct vfio_device *core_vdev, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + + if (!count) + return 0; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return virtiovf_pci_write_config(core_vdev, buf, count, ppos); + + if (index == VFIO_PCI_BAR0_REGION_INDEX) + return virtiovf_pci_bar0_rw(virtvdev, pos, (char __user *)buf, count, false); + + return vfio_pci_core_write(core_vdev, buf, count, ppos); +} + +int virtiovf_pci_ioctl_get_region_info(struct vfio_device *core_vdev, + unsigned int cmd, unsigned long arg) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + unsigned long minsz = offsetofend(struct vfio_region_info, offset); + void __user *uarg = (void __user *)arg; + struct vfio_region_info info = {}; + + if (copy_from_user(&info, uarg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + switch (info.index) { + case VFIO_PCI_BAR0_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = virtvdev->bar0_virtual_buf_size; + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + return copy_to_user(uarg, &info, minsz) ? -EFAULT : 0; + default: + return vfio_pci_core_ioctl(core_vdev, cmd, arg); + } +} + +long virtiovf_vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case VFIO_DEVICE_GET_REGION_INFO: + return virtiovf_pci_ioctl_get_region_info(core_vdev, cmd, arg); + default: + return vfio_pci_core_ioctl(core_vdev, cmd, arg); + } +} + +static int virtiovf_set_notify_addr(struct virtiovf_pci_core_device *virtvdev) +{ + struct vfio_pci_core_device *core_device = &virtvdev->core_device; + int ret; + + /* + * Setup the BAR where the 'notify' exists to be used by vfio as well + * This will let us mmap it only once and use it when needed. + */ + ret = vfio_pci_core_setup_barmap(core_device, + virtvdev->notify_bar); + if (ret) + return ret; + + virtvdev->notify_addr = core_device->barmap[virtvdev->notify_bar] + + virtvdev->notify_offset; + return 0; +} + +int virtiovf_open_legacy_io(struct virtiovf_pci_core_device *virtvdev) +{ + if (!virtvdev->bar0_virtual_buf) + return 0; + + /* + * Upon close_device() the vfio_pci_core_disable() is called + * and will close all the previous mmaps, so it seems that the + * valid life cycle for the 'notify' addr is per open/close. + */ + return virtiovf_set_notify_addr(virtvdev); +} + +static int virtiovf_get_device_config_size(unsigned short device) +{ + /* Network card */ + return offsetofend(struct virtio_net_config, status); +} + +static int virtiovf_read_notify_info(struct virtiovf_pci_core_device *virtvdev) +{ + u64 offset; + int ret; + u8 bar; + + ret = virtio_pci_admin_legacy_io_notify_info(virtvdev->core_device.pdev, + VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_OWNER_MEM, + &bar, &offset); + if (ret) + return ret; + + virtvdev->notify_bar = bar; + virtvdev->notify_offset = offset; + return 0; +} + +static bool virtiovf_bar0_exists(struct pci_dev *pdev) +{ + struct resource *res = pdev->resource; + + return res->flags; +} + +bool virtiovf_support_legacy_io(struct pci_dev *pdev) +{ + return virtio_pci_admin_has_legacy_io(pdev) && !virtiovf_bar0_exists(pdev); +} + +int virtiovf_init_legacy_io(struct virtiovf_pci_core_device *virtvdev) +{ + struct pci_dev *pdev = virtvdev->core_device.pdev; + int ret; + + ret = virtiovf_read_notify_info(virtvdev); + if (ret) + return ret; + + virtvdev->bar0_virtual_buf_size = VIRTIO_PCI_CONFIG_OFF(true) + + virtiovf_get_device_config_size(pdev->device); + BUILD_BUG_ON(!is_power_of_2(virtvdev->bar0_virtual_buf_size)); + virtvdev->bar0_virtual_buf = kzalloc(virtvdev->bar0_virtual_buf_size, + GFP_KERNEL); + if (!virtvdev->bar0_virtual_buf) + return -ENOMEM; + mutex_init(&virtvdev->bar_mutex); + return 0; +} + +void virtiovf_release_legacy_io(struct virtiovf_pci_core_device *virtvdev) +{ + kfree(virtvdev->bar0_virtual_buf); +} + +void virtiovf_legacy_io_reset_done(struct pci_dev *pdev) +{ + struct virtiovf_pci_core_device *virtvdev = dev_get_drvdata(&pdev->dev); + + virtvdev->pci_cmd = 0; +} diff --git a/drivers/vfio/pci/virtio/main.c b/drivers/vfio/pci/virtio/main.c index b5d3a8c5bbc9a..d534d48c41633 100644 --- a/drivers/vfio/pci/virtio/main.c +++ b/drivers/vfio/pci/virtio/main.c @@ -16,347 +16,12 @@ #include #include -struct virtiovf_pci_core_device { - struct vfio_pci_core_device core_device; - u8 *bar0_virtual_buf; - /* synchronize access to the virtual buf */ - struct mutex bar_mutex; - void __iomem *notify_addr; - u64 notify_offset; - __le32 pci_base_addr_0; - __le16 pci_cmd; - u8 bar0_virtual_buf_size; - u8 notify_bar; -}; - -static int -virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, - loff_t pos, char __user *buf, - size_t count, bool read) -{ - bool msix_enabled = - (virtvdev->core_device.irq_type == VFIO_PCI_MSIX_IRQ_INDEX); - struct pci_dev *pdev = virtvdev->core_device.pdev; - u8 *bar0_buf = virtvdev->bar0_virtual_buf; - bool common; - u8 offset; - int ret; - - common = pos < VIRTIO_PCI_CONFIG_OFF(msix_enabled); - /* offset within the relevant configuration area */ - offset = common ? pos : pos - VIRTIO_PCI_CONFIG_OFF(msix_enabled); - mutex_lock(&virtvdev->bar_mutex); - if (read) { - if (common) - ret = virtio_pci_admin_legacy_common_io_read(pdev, offset, - count, bar0_buf + pos); - else - ret = virtio_pci_admin_legacy_device_io_read(pdev, offset, - count, bar0_buf + pos); - if (ret) - goto out; - if (copy_to_user(buf, bar0_buf + pos, count)) - ret = -EFAULT; - } else { - if (copy_from_user(bar0_buf + pos, buf, count)) { - ret = -EFAULT; - goto out; - } - - if (common) - ret = virtio_pci_admin_legacy_common_io_write(pdev, offset, - count, bar0_buf + pos); - else - ret = virtio_pci_admin_legacy_device_io_write(pdev, offset, - count, bar0_buf + pos); - } -out: - mutex_unlock(&virtvdev->bar_mutex); - return ret; -} - -static int -virtiovf_pci_bar0_rw(struct virtiovf_pci_core_device *virtvdev, - loff_t pos, char __user *buf, - size_t count, bool read) -{ - struct vfio_pci_core_device *core_device = &virtvdev->core_device; - struct pci_dev *pdev = core_device->pdev; - u16 queue_notify; - int ret; - - if (!(le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO)) - return -EIO; - - if (pos + count > virtvdev->bar0_virtual_buf_size) - return -EINVAL; - - ret = pm_runtime_resume_and_get(&pdev->dev); - if (ret) { - pci_info_ratelimited(pdev, "runtime resume failed %d\n", ret); - return -EIO; - } - - switch (pos) { - case VIRTIO_PCI_QUEUE_NOTIFY: - if (count != sizeof(queue_notify)) { - ret = -EINVAL; - goto end; - } - if (read) { - ret = vfio_pci_core_ioread16(core_device, true, &queue_notify, - virtvdev->notify_addr); - if (ret) - goto end; - if (copy_to_user(buf, &queue_notify, - sizeof(queue_notify))) { - ret = -EFAULT; - goto end; - } - } else { - if (copy_from_user(&queue_notify, buf, count)) { - ret = -EFAULT; - goto end; - } - ret = vfio_pci_core_iowrite16(core_device, true, queue_notify, - virtvdev->notify_addr); - } - break; - default: - ret = virtiovf_issue_legacy_rw_cmd(virtvdev, pos, buf, count, - read); - } - -end: - pm_runtime_put(&pdev->dev); - return ret ? ret : count; -} - -static ssize_t virtiovf_pci_read_config(struct vfio_device *core_vdev, - char __user *buf, size_t count, - loff_t *ppos) -{ - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - size_t register_offset; - loff_t copy_offset; - size_t copy_count; - __le32 val32; - __le16 val16; - u8 val8; - int ret; - - ret = vfio_pci_core_read(core_vdev, buf, count, ppos); - if (ret < 0) - return ret; - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_DEVICE_ID, - sizeof(val16), ©_offset, - ©_count, ®ister_offset)) { - val16 = cpu_to_le16(VIRTIO_TRANS_ID_NET); - if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, copy_count)) - return -EFAULT; - } - - if ((le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO) && - vfio_pci_core_range_intersect_range(pos, count, PCI_COMMAND, - sizeof(val16), ©_offset, - ©_count, ®ister_offset)) { - if (copy_from_user((void *)&val16 + register_offset, buf + copy_offset, - copy_count)) - return -EFAULT; - val16 |= cpu_to_le16(PCI_COMMAND_IO); - if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, - copy_count)) - return -EFAULT; - } - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_REVISION_ID, - sizeof(val8), ©_offset, - ©_count, ®ister_offset)) { - /* Transional needs to have revision 0 */ - val8 = 0; - if (copy_to_user(buf + copy_offset, &val8, copy_count)) - return -EFAULT; - } - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, - sizeof(val32), ©_offset, - ©_count, ®ister_offset)) { - u32 bar_mask = ~(virtvdev->bar0_virtual_buf_size - 1); - u32 pci_base_addr_0 = le32_to_cpu(virtvdev->pci_base_addr_0); - - val32 = cpu_to_le32((pci_base_addr_0 & bar_mask) | PCI_BASE_ADDRESS_SPACE_IO); - if (copy_to_user(buf + copy_offset, (void *)&val32 + register_offset, copy_count)) - return -EFAULT; - } - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_SUBSYSTEM_ID, - sizeof(val16), ©_offset, - ©_count, ®ister_offset)) { - /* - * Transitional devices use the PCI subsystem device id as - * virtio device id, same as legacy driver always did. - */ - val16 = cpu_to_le16(VIRTIO_ID_NET); - if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, - copy_count)) - return -EFAULT; - } - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_SUBSYSTEM_VENDOR_ID, - sizeof(val16), ©_offset, - ©_count, ®ister_offset)) { - val16 = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET); - if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, - copy_count)) - return -EFAULT; - } - - return count; -} - -static ssize_t -virtiovf_pci_core_read(struct vfio_device *core_vdev, char __user *buf, - size_t count, loff_t *ppos) -{ - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); - unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - - if (!count) - return 0; - - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return virtiovf_pci_read_config(core_vdev, buf, count, ppos); - - if (index == VFIO_PCI_BAR0_REGION_INDEX) - return virtiovf_pci_bar0_rw(virtvdev, pos, buf, count, true); - - return vfio_pci_core_read(core_vdev, buf, count, ppos); -} - -static ssize_t virtiovf_pci_write_config(struct vfio_device *core_vdev, - const char __user *buf, size_t count, - loff_t *ppos) -{ - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - size_t register_offset; - loff_t copy_offset; - size_t copy_count; - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_COMMAND, - sizeof(virtvdev->pci_cmd), - ©_offset, ©_count, - ®ister_offset)) { - if (copy_from_user((void *)&virtvdev->pci_cmd + register_offset, - buf + copy_offset, - copy_count)) - return -EFAULT; - } - - if (vfio_pci_core_range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, - sizeof(virtvdev->pci_base_addr_0), - ©_offset, ©_count, - ®ister_offset)) { - if (copy_from_user((void *)&virtvdev->pci_base_addr_0 + register_offset, - buf + copy_offset, - copy_count)) - return -EFAULT; - } - - return vfio_pci_core_write(core_vdev, buf, count, ppos); -} - -static ssize_t -virtiovf_pci_core_write(struct vfio_device *core_vdev, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); - unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - - if (!count) - return 0; - - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return virtiovf_pci_write_config(core_vdev, buf, count, ppos); - - if (index == VFIO_PCI_BAR0_REGION_INDEX) - return virtiovf_pci_bar0_rw(virtvdev, pos, (char __user *)buf, count, false); - - return vfio_pci_core_write(core_vdev, buf, count, ppos); -} - -static int -virtiovf_pci_ioctl_get_region_info(struct vfio_device *core_vdev, - unsigned int cmd, unsigned long arg) -{ - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); - unsigned long minsz = offsetofend(struct vfio_region_info, offset); - void __user *uarg = (void __user *)arg; - struct vfio_region_info info = {}; - - if (copy_from_user(&info, uarg, minsz)) - return -EFAULT; - - if (info.argsz < minsz) - return -EINVAL; - - switch (info.index) { - case VFIO_PCI_BAR0_REGION_INDEX: - info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); - info.size = virtvdev->bar0_virtual_buf_size; - info.flags = VFIO_REGION_INFO_FLAG_READ | - VFIO_REGION_INFO_FLAG_WRITE; - return copy_to_user(uarg, &info, minsz) ? -EFAULT : 0; - default: - return vfio_pci_core_ioctl(core_vdev, cmd, arg); - } -} - -static long -virtiovf_vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, - unsigned long arg) -{ - switch (cmd) { - case VFIO_DEVICE_GET_REGION_INFO: - return virtiovf_pci_ioctl_get_region_info(core_vdev, cmd, arg); - default: - return vfio_pci_core_ioctl(core_vdev, cmd, arg); - } -} - -static int -virtiovf_set_notify_addr(struct virtiovf_pci_core_device *virtvdev) -{ - struct vfio_pci_core_device *core_device = &virtvdev->core_device; - int ret; - - /* - * Setup the BAR where the 'notify' exists to be used by vfio as well - * This will let us mmap it only once and use it when needed. - */ - ret = vfio_pci_core_setup_barmap(core_device, - virtvdev->notify_bar); - if (ret) - return ret; - - virtvdev->notify_addr = core_device->barmap[virtvdev->notify_bar] + - virtvdev->notify_offset; - return 0; -} +#include "common.h" static int virtiovf_pci_open_device(struct vfio_device *core_vdev) { - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + struct virtiovf_pci_core_device *virtvdev = container_of(core_vdev, + struct virtiovf_pci_core_device, core_device.vdev); struct vfio_pci_core_device *vdev = &virtvdev->core_device; int ret; @@ -364,88 +29,84 @@ static int virtiovf_pci_open_device(struct vfio_device *core_vdev) if (ret) return ret; - if (virtvdev->bar0_virtual_buf) { - /* - * Upon close_device() the vfio_pci_core_disable() is called - * and will close all the previous mmaps, so it seems that the - * valid life cycle for the 'notify' addr is per open/close. - */ - ret = virtiovf_set_notify_addr(virtvdev); - if (ret) { - vfio_pci_core_disable(vdev); - return ret; - } +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY + ret = virtiovf_open_legacy_io(virtvdev); + if (ret) { + vfio_pci_core_disable(vdev); + return ret; } +#endif + virtiovf_open_migration(virtvdev); vfio_pci_core_finish_enable(vdev); return 0; } -static int virtiovf_get_device_config_size(unsigned short device) +static void virtiovf_pci_close_device(struct vfio_device *core_vdev) { - /* Network card */ - return offsetofend(struct virtio_net_config, status); -} - -static int virtiovf_read_notify_info(struct virtiovf_pci_core_device *virtvdev) -{ - u64 offset; - int ret; - u8 bar; + struct virtiovf_pci_core_device *virtvdev = container_of(core_vdev, + struct virtiovf_pci_core_device, core_device.vdev); - ret = virtio_pci_admin_legacy_io_notify_info(virtvdev->core_device.pdev, - VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_OWNER_MEM, - &bar, &offset); - if (ret) - return ret; - - virtvdev->notify_bar = bar; - virtvdev->notify_offset = offset; - return 0; + virtiovf_close_migration(virtvdev); + vfio_pci_core_close_device(core_vdev); } +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY static int virtiovf_pci_init_device(struct vfio_device *core_vdev) { - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); - struct pci_dev *pdev; + struct virtiovf_pci_core_device *virtvdev = container_of(core_vdev, + struct virtiovf_pci_core_device, core_device.vdev); int ret; ret = vfio_pci_core_init_dev(core_vdev); if (ret) return ret; - pdev = virtvdev->core_device.pdev; - ret = virtiovf_read_notify_info(virtvdev); - if (ret) - return ret; - - virtvdev->bar0_virtual_buf_size = VIRTIO_PCI_CONFIG_OFF(true) + - virtiovf_get_device_config_size(pdev->device); - BUILD_BUG_ON(!is_power_of_2(virtvdev->bar0_virtual_buf_size)); - virtvdev->bar0_virtual_buf = kzalloc(virtvdev->bar0_virtual_buf_size, - GFP_KERNEL); - if (!virtvdev->bar0_virtual_buf) - return -ENOMEM; - mutex_init(&virtvdev->bar_mutex); - return 0; + /* + * The vfio_device_ops.init() callback is set to virtiovf_pci_init_device() + * only when legacy I/O is supported. Now, let's initialize it. + */ + return virtiovf_init_legacy_io(virtvdev); } +#endif static void virtiovf_pci_core_release_dev(struct vfio_device *core_vdev) { - struct virtiovf_pci_core_device *virtvdev = container_of( - core_vdev, struct virtiovf_pci_core_device, core_device.vdev); +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY + struct virtiovf_pci_core_device *virtvdev = container_of(core_vdev, + struct virtiovf_pci_core_device, core_device.vdev); - kfree(virtvdev->bar0_virtual_buf); + virtiovf_release_legacy_io(virtvdev); +#endif vfio_pci_core_release_dev(core_vdev); } -static const struct vfio_device_ops virtiovf_vfio_pci_tran_ops = { - .name = "virtio-vfio-pci-trans", +static const struct vfio_device_ops virtiovf_vfio_pci_lm_ops = { + .name = "virtio-vfio-pci-lm", + .init = vfio_pci_core_init_dev, + .release = virtiovf_pci_core_release_dev, + .open_device = virtiovf_pci_open_device, + .close_device = virtiovf_pci_close_device, + .ioctl = vfio_pci_core_ioctl, + .device_feature = vfio_pci_core_ioctl_feature, + .read = vfio_pci_core_read, + .write = vfio_pci_core_write, + .mmap = vfio_pci_core_mmap, + .request = vfio_pci_core_request, + .match = vfio_pci_core_match, + .bind_iommufd = vfio_iommufd_physical_bind, + .unbind_iommufd = vfio_iommufd_physical_unbind, + .attach_ioas = vfio_iommufd_physical_attach_ioas, + .detach_ioas = vfio_iommufd_physical_detach_ioas, +}; + +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY +static const struct vfio_device_ops virtiovf_vfio_pci_tran_lm_ops = { + .name = "virtio-vfio-pci-trans-lm", .init = virtiovf_pci_init_device, .release = virtiovf_pci_core_release_dev, .open_device = virtiovf_pci_open_device, - .close_device = vfio_pci_core_close_device, + .close_device = virtiovf_pci_close_device, .ioctl = virtiovf_vfio_pci_core_ioctl, .device_feature = vfio_pci_core_ioctl_feature, .read = virtiovf_pci_core_read, @@ -458,6 +119,7 @@ static const struct vfio_device_ops virtiovf_vfio_pci_tran_ops = { .attach_ioas = vfio_iommufd_physical_attach_ioas, .detach_ioas = vfio_iommufd_physical_detach_ioas, }; +#endif static const struct vfio_device_ops virtiovf_vfio_pci_ops = { .name = "virtio-vfio-pci", @@ -478,29 +140,34 @@ static const struct vfio_device_ops virtiovf_vfio_pci_ops = { .detach_ioas = vfio_iommufd_physical_detach_ioas, }; -static bool virtiovf_bar0_exists(struct pci_dev *pdev) -{ - struct resource *res = pdev->resource; - - return res->flags; -} - static int virtiovf_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { const struct vfio_device_ops *ops = &virtiovf_vfio_pci_ops; struct virtiovf_pci_core_device *virtvdev; + bool sup_legacy_io = false; + bool sup_lm = false; int ret; - if (pdev->is_virtfn && virtio_pci_admin_has_legacy_io(pdev) && - !virtiovf_bar0_exists(pdev)) - ops = &virtiovf_vfio_pci_tran_ops; + if (pdev->is_virtfn) { +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY + sup_legacy_io = virtiovf_support_legacy_io(pdev); + if (sup_legacy_io) + ops = &virtiovf_vfio_pci_tran_lm_ops; +#endif + sup_lm = virtio_pci_admin_has_dev_parts(pdev); + if (sup_lm && !sup_legacy_io) + ops = &virtiovf_vfio_pci_lm_ops; + } virtvdev = vfio_alloc_device(virtiovf_pci_core_device, core_device.vdev, &pdev->dev, ops); if (IS_ERR(virtvdev)) return PTR_ERR(virtvdev); + if (sup_lm) + virtiovf_set_migratable(virtvdev); + dev_set_drvdata(&pdev->dev, &virtvdev->core_device); ret = vfio_pci_core_register_device(&virtvdev->core_device); if (ret) @@ -529,9 +196,10 @@ MODULE_DEVICE_TABLE(pci, virtiovf_pci_table); static void virtiovf_pci_aer_reset_done(struct pci_dev *pdev) { - struct virtiovf_pci_core_device *virtvdev = dev_get_drvdata(&pdev->dev); - - virtvdev->pci_cmd = 0; +#ifdef CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY + virtiovf_legacy_io_reset_done(pdev); +#endif + virtiovf_migration_reset_done(pdev); } static const struct pci_error_handlers virtiovf_err_handlers = { diff --git a/drivers/vfio/pci/virtio/migrate.c b/drivers/vfio/pci/virtio/migrate.c new file mode 100644 index 0000000000000..ee54f4c178577 --- /dev/null +++ b/drivers/vfio/pci/virtio/migrate.c @@ -0,0 +1,1337 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +/* Device specification max parts size */ +#define MAX_LOAD_SIZE (BIT_ULL(BITS_PER_TYPE \ + (((struct virtio_admin_cmd_dev_parts_metadata_result *)0)->parts_size.size)) - 1) + +/* Initial target buffer size */ +#define VIRTIOVF_TARGET_INITIAL_BUF_SIZE SZ_1M + +static int +virtiovf_read_device_context_chunk(struct virtiovf_migration_file *migf, + u32 ctx_size); + +static struct page * +virtiovf_get_migration_page(struct virtiovf_data_buffer *buf, + unsigned long offset) +{ + unsigned long cur_offset = 0; + struct scatterlist *sg; + unsigned int i; + + /* All accesses are sequential */ + if (offset < buf->last_offset || !buf->last_offset_sg) { + buf->last_offset = 0; + buf->last_offset_sg = buf->table.sgt.sgl; + buf->sg_last_entry = 0; + } + + cur_offset = buf->last_offset; + + for_each_sg(buf->last_offset_sg, sg, + buf->table.sgt.orig_nents - buf->sg_last_entry, i) { + if (offset < sg->length + cur_offset) { + buf->last_offset_sg = sg; + buf->sg_last_entry += i; + buf->last_offset = cur_offset; + return nth_page(sg_page(sg), + (offset - cur_offset) / PAGE_SIZE); + } + cur_offset += sg->length; + } + return NULL; +} + +static int virtiovf_add_migration_pages(struct virtiovf_data_buffer *buf, + unsigned int npages) +{ + unsigned int to_alloc = npages; + struct page **page_list; + unsigned long filled; + unsigned int to_fill; + int ret; + int i; + + to_fill = min_t(unsigned int, npages, PAGE_SIZE / sizeof(*page_list)); + page_list = kvcalloc(to_fill, sizeof(*page_list), GFP_KERNEL_ACCOUNT); + if (!page_list) + return -ENOMEM; + + do { + filled = alloc_pages_bulk_array(GFP_KERNEL_ACCOUNT, to_fill, + page_list); + if (!filled) { + ret = -ENOMEM; + goto err; + } + to_alloc -= filled; + ret = sg_alloc_append_table_from_pages(&buf->table, page_list, + filled, 0, filled << PAGE_SHIFT, UINT_MAX, + SG_MAX_SINGLE_ALLOC, GFP_KERNEL_ACCOUNT); + + if (ret) + goto err_append; + buf->allocated_length += filled * PAGE_SIZE; + /* clean input for another bulk allocation */ + memset(page_list, 0, filled * sizeof(*page_list)); + to_fill = min_t(unsigned int, to_alloc, + PAGE_SIZE / sizeof(*page_list)); + } while (to_alloc > 0); + + kvfree(page_list); + return 0; + +err_append: + for (i = filled - 1; i >= 0; i--) + __free_page(page_list[i]); +err: + kvfree(page_list); + return ret; +} + +static void virtiovf_free_data_buffer(struct virtiovf_data_buffer *buf) +{ + struct sg_page_iter sg_iter; + + /* Undo alloc_pages_bulk_array() */ + for_each_sgtable_page(&buf->table.sgt, &sg_iter, 0) + __free_page(sg_page_iter_page(&sg_iter)); + sg_free_append_table(&buf->table); + kfree(buf); +} + +static struct virtiovf_data_buffer * +virtiovf_alloc_data_buffer(struct virtiovf_migration_file *migf, size_t length) +{ + struct virtiovf_data_buffer *buf; + int ret; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL_ACCOUNT); + if (!buf) + return ERR_PTR(-ENOMEM); + + ret = virtiovf_add_migration_pages(buf, + DIV_ROUND_UP_ULL(length, PAGE_SIZE)); + if (ret) + goto end; + + buf->migf = migf; + return buf; +end: + virtiovf_free_data_buffer(buf); + return ERR_PTR(ret); +} + +static void virtiovf_put_data_buffer(struct virtiovf_data_buffer *buf) +{ + spin_lock_irq(&buf->migf->list_lock); + list_add_tail(&buf->buf_elm, &buf->migf->avail_list); + spin_unlock_irq(&buf->migf->list_lock); +} + +static int +virtiovf_pci_alloc_obj_id(struct virtiovf_pci_core_device *virtvdev, u8 type, + u32 *obj_id) +{ + return virtio_pci_admin_obj_create(virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, type, obj_id); +} + +static void +virtiovf_pci_free_obj_id(struct virtiovf_pci_core_device *virtvdev, u32 obj_id) +{ + virtio_pci_admin_obj_destroy(virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, obj_id); +} + +static struct virtiovf_data_buffer * +virtiovf_get_data_buffer(struct virtiovf_migration_file *migf, size_t length) +{ + struct virtiovf_data_buffer *buf, *temp_buf; + struct list_head free_list; + + INIT_LIST_HEAD(&free_list); + + spin_lock_irq(&migf->list_lock); + list_for_each_entry_safe(buf, temp_buf, &migf->avail_list, buf_elm) { + list_del_init(&buf->buf_elm); + if (buf->allocated_length >= length) { + spin_unlock_irq(&migf->list_lock); + goto found; + } + /* + * Prevent holding redundant buffers. Put in a free + * list and call at the end not under the spin lock + * (&migf->list_lock) to minimize its scope usage. + */ + list_add(&buf->buf_elm, &free_list); + } + spin_unlock_irq(&migf->list_lock); + buf = virtiovf_alloc_data_buffer(migf, length); + +found: + while ((temp_buf = list_first_entry_or_null(&free_list, + struct virtiovf_data_buffer, buf_elm))) { + list_del(&temp_buf->buf_elm); + virtiovf_free_data_buffer(temp_buf); + } + + return buf; +} + +static void virtiovf_clean_migf_resources(struct virtiovf_migration_file *migf) +{ + struct virtiovf_data_buffer *entry; + + if (migf->buf) { + virtiovf_free_data_buffer(migf->buf); + migf->buf = NULL; + } + + if (migf->buf_header) { + virtiovf_free_data_buffer(migf->buf_header); + migf->buf_header = NULL; + } + + list_splice(&migf->avail_list, &migf->buf_list); + + while ((entry = list_first_entry_or_null(&migf->buf_list, + struct virtiovf_data_buffer, buf_elm))) { + list_del(&entry->buf_elm); + virtiovf_free_data_buffer(entry); + } + + if (migf->has_obj_id) + virtiovf_pci_free_obj_id(migf->virtvdev, migf->obj_id); +} + +static void virtiovf_disable_fd(struct virtiovf_migration_file *migf) +{ + mutex_lock(&migf->lock); + migf->state = VIRTIOVF_MIGF_STATE_ERROR; + migf->filp->f_pos = 0; + mutex_unlock(&migf->lock); +} + +static void virtiovf_disable_fds(struct virtiovf_pci_core_device *virtvdev) +{ + if (virtvdev->resuming_migf) { + virtiovf_disable_fd(virtvdev->resuming_migf); + virtiovf_clean_migf_resources(virtvdev->resuming_migf); + fput(virtvdev->resuming_migf->filp); + virtvdev->resuming_migf = NULL; + } + if (virtvdev->saving_migf) { + virtiovf_disable_fd(virtvdev->saving_migf); + virtiovf_clean_migf_resources(virtvdev->saving_migf); + fput(virtvdev->saving_migf->filp); + virtvdev->saving_migf = NULL; + } +} + +/* + * This function is called in all state_mutex unlock cases to + * handle a 'deferred_reset' if exists. + */ +static void virtiovf_state_mutex_unlock(struct virtiovf_pci_core_device *virtvdev) +{ +again: + spin_lock(&virtvdev->reset_lock); + if (virtvdev->deferred_reset) { + virtvdev->deferred_reset = false; + spin_unlock(&virtvdev->reset_lock); + virtvdev->mig_state = VFIO_DEVICE_STATE_RUNNING; + virtiovf_disable_fds(virtvdev); + goto again; + } + mutex_unlock(&virtvdev->state_mutex); + spin_unlock(&virtvdev->reset_lock); +} + +void virtiovf_migration_reset_done(struct pci_dev *pdev) +{ + struct virtiovf_pci_core_device *virtvdev = dev_get_drvdata(&pdev->dev); + + if (!virtvdev->migrate_cap) + return; + + /* + * As the higher VFIO layers are holding locks across reset and using + * those same locks with the mm_lock we need to prevent ABBA deadlock + * with the state_mutex and mm_lock. + * In case the state_mutex was taken already we defer the cleanup work + * to the unlock flow of the other running context. + */ + spin_lock(&virtvdev->reset_lock); + virtvdev->deferred_reset = true; + if (!mutex_trylock(&virtvdev->state_mutex)) { + spin_unlock(&virtvdev->reset_lock); + return; + } + spin_unlock(&virtvdev->reset_lock); + virtiovf_state_mutex_unlock(virtvdev); +} + +static int virtiovf_release_file(struct inode *inode, struct file *filp) +{ + struct virtiovf_migration_file *migf = filp->private_data; + + virtiovf_disable_fd(migf); + mutex_destroy(&migf->lock); + kfree(migf); + return 0; +} + +static struct virtiovf_data_buffer * +virtiovf_get_data_buff_from_pos(struct virtiovf_migration_file *migf, + loff_t pos, bool *end_of_data) +{ + struct virtiovf_data_buffer *buf; + bool found = false; + + *end_of_data = false; + spin_lock_irq(&migf->list_lock); + if (list_empty(&migf->buf_list)) { + *end_of_data = true; + goto end; + } + + buf = list_first_entry(&migf->buf_list, struct virtiovf_data_buffer, + buf_elm); + if (pos >= buf->start_pos && + pos < buf->start_pos + buf->length) { + found = true; + goto end; + } + + /* + * As we use a stream based FD we may expect having the data always + * on first chunk + */ + migf->state = VIRTIOVF_MIGF_STATE_ERROR; + +end: + spin_unlock_irq(&migf->list_lock); + return found ? buf : NULL; +} + +static ssize_t virtiovf_buf_read(struct virtiovf_data_buffer *vhca_buf, + char __user **buf, size_t *len, loff_t *pos) +{ + unsigned long offset; + ssize_t done = 0; + size_t copy_len; + + copy_len = min_t(size_t, + vhca_buf->start_pos + vhca_buf->length - *pos, *len); + while (copy_len) { + size_t page_offset; + struct page *page; + size_t page_len; + u8 *from_buff; + int ret; + + offset = *pos - vhca_buf->start_pos; + page_offset = offset % PAGE_SIZE; + offset -= page_offset; + page = virtiovf_get_migration_page(vhca_buf, offset); + if (!page) + return -EINVAL; + page_len = min_t(size_t, copy_len, PAGE_SIZE - page_offset); + from_buff = kmap_local_page(page); + ret = copy_to_user(*buf, from_buff + page_offset, page_len); + kunmap_local(from_buff); + if (ret) + return -EFAULT; + *pos += page_len; + *len -= page_len; + *buf += page_len; + done += page_len; + copy_len -= page_len; + } + + if (*pos >= vhca_buf->start_pos + vhca_buf->length) { + spin_lock_irq(&vhca_buf->migf->list_lock); + list_del_init(&vhca_buf->buf_elm); + list_add_tail(&vhca_buf->buf_elm, &vhca_buf->migf->avail_list); + spin_unlock_irq(&vhca_buf->migf->list_lock); + } + + return done; +} + +static ssize_t virtiovf_save_read(struct file *filp, char __user *buf, size_t len, + loff_t *pos) +{ + struct virtiovf_migration_file *migf = filp->private_data; + struct virtiovf_data_buffer *vhca_buf; + bool first_loop_call = true; + bool end_of_data; + ssize_t done = 0; + + if (pos) + return -ESPIPE; + pos = &filp->f_pos; + + mutex_lock(&migf->lock); + if (migf->state == VIRTIOVF_MIGF_STATE_ERROR) { + done = -ENODEV; + goto out_unlock; + } + + while (len) { + ssize_t count; + + vhca_buf = virtiovf_get_data_buff_from_pos(migf, *pos, &end_of_data); + if (first_loop_call) { + first_loop_call = false; + /* Temporary end of file as part of PRE_COPY */ + if (end_of_data && migf->state == VIRTIOVF_MIGF_STATE_PRECOPY) { + done = -ENOMSG; + goto out_unlock; + } + if (end_of_data && migf->state != VIRTIOVF_MIGF_STATE_COMPLETE) { + done = -EINVAL; + goto out_unlock; + } + } + + if (end_of_data) + goto out_unlock; + + if (!vhca_buf) { + done = -EINVAL; + goto out_unlock; + } + + count = virtiovf_buf_read(vhca_buf, &buf, &len, pos); + if (count < 0) { + done = count; + goto out_unlock; + } + done += count; + } + +out_unlock: + mutex_unlock(&migf->lock); + return done; +} + +static long virtiovf_precopy_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct virtiovf_migration_file *migf = filp->private_data; + struct virtiovf_pci_core_device *virtvdev = migf->virtvdev; + struct vfio_precopy_info info = {}; + loff_t *pos = &filp->f_pos; + bool end_of_data = false; + unsigned long minsz; + u32 ctx_size = 0; + int ret; + + if (cmd != VFIO_MIG_GET_PRECOPY_INFO) + return -ENOTTY; + + minsz = offsetofend(struct vfio_precopy_info, dirty_bytes); + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + mutex_lock(&virtvdev->state_mutex); + if (virtvdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY && + virtvdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY_P2P) { + ret = -EINVAL; + goto err_state_unlock; + } + + /* + * The virtio specification does not include a PRE_COPY concept. + * Since we can expect the data to remain the same for a certain period, + * we use a rate limiter mechanism before making a call to the device. + */ + if (__ratelimit(&migf->pre_copy_rl_state)) { + + ret = virtio_pci_admin_dev_parts_metadata_get(virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, migf->obj_id, + VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE, + &ctx_size); + if (ret) + goto err_state_unlock; + } + + mutex_lock(&migf->lock); + if (migf->state == VIRTIOVF_MIGF_STATE_ERROR) { + ret = -ENODEV; + goto err_migf_unlock; + } + + if (migf->pre_copy_initial_bytes > *pos) { + info.initial_bytes = migf->pre_copy_initial_bytes - *pos; + } else { + info.dirty_bytes = migf->max_pos - *pos; + if (!info.dirty_bytes) + end_of_data = true; + info.dirty_bytes += ctx_size; + } + + if (!end_of_data || !ctx_size) { + mutex_unlock(&migf->lock); + goto done; + } + + mutex_unlock(&migf->lock); + /* + * We finished transferring the current state and the device has a + * dirty state, read a new state. + */ + ret = virtiovf_read_device_context_chunk(migf, ctx_size); + if (ret) + /* + * The machine is running, and context size could be grow, so no reason to mark + * the device state as VIRTIOVF_MIGF_STATE_ERROR. + */ + goto err_state_unlock; + +done: + virtiovf_state_mutex_unlock(virtvdev); + if (copy_to_user((void __user *)arg, &info, minsz)) + return -EFAULT; + return 0; + +err_migf_unlock: + mutex_unlock(&migf->lock); +err_state_unlock: + virtiovf_state_mutex_unlock(virtvdev); + return ret; +} + +static const struct file_operations virtiovf_save_fops = { + .owner = THIS_MODULE, + .read = virtiovf_save_read, + .unlocked_ioctl = virtiovf_precopy_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .release = virtiovf_release_file, +}; + +static int +virtiovf_add_buf_header(struct virtiovf_data_buffer *header_buf, + u32 data_size) +{ + struct virtiovf_migration_file *migf = header_buf->migf; + struct virtiovf_migration_header header = {}; + struct page *page; + u8 *to_buff; + + header.record_size = cpu_to_le64(data_size); + header.flags = cpu_to_le32(VIRTIOVF_MIGF_HEADER_FLAGS_TAG_MANDATORY); + header.tag = cpu_to_le32(VIRTIOVF_MIGF_HEADER_TAG_DEVICE_DATA); + page = virtiovf_get_migration_page(header_buf, 0); + if (!page) + return -EINVAL; + to_buff = kmap_local_page(page); + memcpy(to_buff, &header, sizeof(header)); + kunmap_local(to_buff); + header_buf->length = sizeof(header); + header_buf->start_pos = header_buf->migf->max_pos; + migf->max_pos += header_buf->length; + spin_lock_irq(&migf->list_lock); + list_add_tail(&header_buf->buf_elm, &migf->buf_list); + spin_unlock_irq(&migf->list_lock); + return 0; +} + +static int +virtiovf_read_device_context_chunk(struct virtiovf_migration_file *migf, + u32 ctx_size) +{ + struct virtiovf_data_buffer *header_buf; + struct virtiovf_data_buffer *buf; + bool unmark_end = false; + struct scatterlist *sg; + unsigned int i; + u32 res_size; + int nent; + int ret; + + buf = virtiovf_get_data_buffer(migf, ctx_size); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + /* Find the total count of SG entries which satisfies the size */ + nent = sg_nents_for_len(buf->table.sgt.sgl, ctx_size); + if (nent <= 0) { + ret = -EINVAL; + goto out; + } + + /* + * Iterate to that SG entry and mark it as last (if it's not already) + * to let underlay layers iterate only till that entry. + */ + for_each_sg(buf->table.sgt.sgl, sg, nent - 1, i) + ; + + if (!sg_is_last(sg)) { + unmark_end = true; + sg_mark_end(sg); + } + + ret = virtio_pci_admin_dev_parts_get(migf->virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, + migf->obj_id, + VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_ALL, + buf->table.sgt.sgl, &res_size); + /* Restore the original SG mark end */ + if (unmark_end) + sg_unmark_end(sg); + if (ret) + goto out; + + buf->length = res_size; + header_buf = virtiovf_get_data_buffer(migf, + sizeof(struct virtiovf_migration_header)); + if (IS_ERR(header_buf)) { + ret = PTR_ERR(header_buf); + goto out; + } + + ret = virtiovf_add_buf_header(header_buf, res_size); + if (ret) + goto out_header; + + buf->start_pos = buf->migf->max_pos; + migf->max_pos += buf->length; + spin_lock(&migf->list_lock); + list_add_tail(&buf->buf_elm, &migf->buf_list); + spin_unlock_irq(&migf->list_lock); + return 0; + +out_header: + virtiovf_put_data_buffer(header_buf); +out: + virtiovf_put_data_buffer(buf); + return ret; +} + +static int +virtiovf_pci_save_device_final_data(struct virtiovf_pci_core_device *virtvdev) +{ + struct virtiovf_migration_file *migf = virtvdev->saving_migf; + u32 ctx_size; + int ret; + + if (migf->state == VIRTIOVF_MIGF_STATE_ERROR) + return -ENODEV; + + ret = virtio_pci_admin_dev_parts_metadata_get(virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, migf->obj_id, + VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE, + &ctx_size); + if (ret) + goto err; + + if (!ctx_size) { + ret = -EINVAL; + goto err; + } + + ret = virtiovf_read_device_context_chunk(migf, ctx_size); + if (ret) + goto err; + + migf->state = VIRTIOVF_MIGF_STATE_COMPLETE; + return 0; + +err: + migf->state = VIRTIOVF_MIGF_STATE_ERROR; + return ret; +} + +static struct virtiovf_migration_file * +virtiovf_pci_save_device_data(struct virtiovf_pci_core_device *virtvdev, + bool pre_copy) +{ + struct virtiovf_migration_file *migf; + u32 ctx_size; + u32 obj_id; + int ret; + + migf = kzalloc(sizeof(*migf), GFP_KERNEL_ACCOUNT); + if (!migf) + return ERR_PTR(-ENOMEM); + + migf->filp = anon_inode_getfile("virtiovf_mig", &virtiovf_save_fops, migf, + O_RDONLY); + if (IS_ERR(migf->filp)) { + ret = PTR_ERR(migf->filp); + kfree(migf); + return ERR_PTR(ret); + } + + stream_open(migf->filp->f_inode, migf->filp); + mutex_init(&migf->lock); + INIT_LIST_HEAD(&migf->buf_list); + INIT_LIST_HEAD(&migf->avail_list); + spin_lock_init(&migf->list_lock); + migf->virtvdev = virtvdev; + + lockdep_assert_held(&virtvdev->state_mutex); + ret = virtiovf_pci_alloc_obj_id(virtvdev, VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET, + &obj_id); + if (ret) + goto out; + + migf->obj_id = obj_id; + /* Mark as having a valid obj id which can be even 0 */ + migf->has_obj_id = true; + ret = virtio_pci_admin_dev_parts_metadata_get(virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, obj_id, + VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE, + &ctx_size); + if (ret) + goto out_clean; + + if (!ctx_size) { + ret = -EINVAL; + goto out_clean; + } + + ret = virtiovf_read_device_context_chunk(migf, ctx_size); + if (ret) + goto out_clean; + + if (pre_copy) { + migf->pre_copy_initial_bytes = migf->max_pos; + /* Arbitrarily set the pre-copy rate limit to 1-second intervals */ + ratelimit_state_init(&migf->pre_copy_rl_state, 1 * HZ, 1); + /* Prevent any rate messages upon its usage */ + ratelimit_set_flags(&migf->pre_copy_rl_state, + RATELIMIT_MSG_ON_RELEASE); + migf->state = VIRTIOVF_MIGF_STATE_PRECOPY; + } else { + migf->state = VIRTIOVF_MIGF_STATE_COMPLETE; + } + + return migf; + +out_clean: + virtiovf_clean_migf_resources(migf); +out: + fput(migf->filp); + return ERR_PTR(ret); +} + +/* + * Set the required object header at the beginning of the buffer. + * The actual device parts data will be written post of the header offset. + */ +static int virtiovf_set_obj_cmd_header(struct virtiovf_data_buffer *vhca_buf) +{ + struct virtio_admin_cmd_resource_obj_cmd_hdr obj_hdr = {}; + struct page *page; + u8 *to_buff; + + obj_hdr.type = cpu_to_le16(VIRTIO_RESOURCE_OBJ_DEV_PARTS); + obj_hdr.id = cpu_to_le32(vhca_buf->migf->obj_id); + page = virtiovf_get_migration_page(vhca_buf, 0); + if (!page) + return -EINVAL; + to_buff = kmap_local_page(page); + memcpy(to_buff, &obj_hdr, sizeof(obj_hdr)); + kunmap_local(to_buff); + + /* Mark the buffer as including the header object data */ + vhca_buf->include_header_object = 1; + return 0; +} + +static int +virtiovf_append_page_to_mig_buf(struct virtiovf_data_buffer *vhca_buf, + const char __user **buf, size_t *len, + loff_t *pos, ssize_t *done) +{ + unsigned long offset; + size_t page_offset; + struct page *page; + size_t page_len; + u8 *to_buff; + int ret; + + offset = *pos - vhca_buf->start_pos; + + if (vhca_buf->include_header_object) + /* The buffer holds the object header, update the offset accordingly */ + offset += sizeof(struct virtio_admin_cmd_resource_obj_cmd_hdr); + + page_offset = offset % PAGE_SIZE; + + page = virtiovf_get_migration_page(vhca_buf, offset - page_offset); + if (!page) + return -EINVAL; + + page_len = min_t(size_t, *len, PAGE_SIZE - page_offset); + to_buff = kmap_local_page(page); + ret = copy_from_user(to_buff + page_offset, *buf, page_len); + kunmap_local(to_buff); + if (ret) + return -EFAULT; + + *pos += page_len; + *done += page_len; + *buf += page_len; + *len -= page_len; + vhca_buf->length += page_len; + return 0; +} + +static ssize_t +virtiovf_resume_read_chunk(struct virtiovf_migration_file *migf, + struct virtiovf_data_buffer *vhca_buf, + size_t chunk_size, const char __user **buf, + size_t *len, loff_t *pos, ssize_t *done, + bool *has_work) +{ + size_t copy_len, to_copy; + int ret; + + to_copy = min_t(size_t, *len, chunk_size - vhca_buf->length); + copy_len = to_copy; + while (to_copy) { + ret = virtiovf_append_page_to_mig_buf(vhca_buf, buf, &to_copy, + pos, done); + if (ret) + return ret; + } + + *len -= copy_len; + if (vhca_buf->length == chunk_size) { + migf->load_state = VIRTIOVF_LOAD_STATE_LOAD_CHUNK; + migf->max_pos += chunk_size; + *has_work = true; + } + + return 0; +} + +static int +virtiovf_resume_read_header_data(struct virtiovf_migration_file *migf, + struct virtiovf_data_buffer *vhca_buf, + const char __user **buf, size_t *len, + loff_t *pos, ssize_t *done) +{ + size_t copy_len, to_copy; + size_t required_data; + int ret; + + required_data = migf->record_size - vhca_buf->length; + to_copy = min_t(size_t, *len, required_data); + copy_len = to_copy; + while (to_copy) { + ret = virtiovf_append_page_to_mig_buf(vhca_buf, buf, &to_copy, + pos, done); + if (ret) + return ret; + } + + *len -= copy_len; + if (vhca_buf->length == migf->record_size) { + switch (migf->record_tag) { + default: + /* Optional tag */ + break; + } + + migf->load_state = VIRTIOVF_LOAD_STATE_READ_HEADER; + migf->max_pos += migf->record_size; + vhca_buf->length = 0; + } + + return 0; +} + +static int +virtiovf_resume_read_header(struct virtiovf_migration_file *migf, + struct virtiovf_data_buffer *vhca_buf, + const char __user **buf, + size_t *len, loff_t *pos, + ssize_t *done, bool *has_work) +{ + struct page *page; + size_t copy_len; + u8 *to_buff; + int ret; + + copy_len = min_t(size_t, *len, + sizeof(struct virtiovf_migration_header) - vhca_buf->length); + page = virtiovf_get_migration_page(vhca_buf, 0); + if (!page) + return -EINVAL; + to_buff = kmap_local_page(page); + ret = copy_from_user(to_buff + vhca_buf->length, *buf, copy_len); + if (ret) { + ret = -EFAULT; + goto end; + } + + *buf += copy_len; + *pos += copy_len; + *done += copy_len; + *len -= copy_len; + vhca_buf->length += copy_len; + if (vhca_buf->length == sizeof(struct virtiovf_migration_header)) { + u64 record_size; + u32 flags; + + record_size = le64_to_cpup((__le64 *)to_buff); + if (record_size > MAX_LOAD_SIZE) { + ret = -ENOMEM; + goto end; + } + + migf->record_size = record_size; + flags = le32_to_cpup((__le32 *)(to_buff + + offsetof(struct virtiovf_migration_header, flags))); + migf->record_tag = le32_to_cpup((__le32 *)(to_buff + + offsetof(struct virtiovf_migration_header, tag))); + switch (migf->record_tag) { + case VIRTIOVF_MIGF_HEADER_TAG_DEVICE_DATA: + migf->load_state = VIRTIOVF_LOAD_STATE_PREP_CHUNK; + break; + default: + if (!(flags & VIRTIOVF_MIGF_HEADER_FLAGS_TAG_OPTIONAL)) { + ret = -EOPNOTSUPP; + goto end; + } + /* We may read and skip this optional record data */ + migf->load_state = VIRTIOVF_LOAD_STATE_PREP_HEADER_DATA; + } + + migf->max_pos += vhca_buf->length; + vhca_buf->length = 0; + *has_work = true; + } +end: + kunmap_local(to_buff); + return ret; +} + +static ssize_t virtiovf_resume_write(struct file *filp, const char __user *buf, + size_t len, loff_t *pos) +{ + struct virtiovf_migration_file *migf = filp->private_data; + struct virtiovf_data_buffer *vhca_buf = migf->buf; + struct virtiovf_data_buffer *vhca_buf_header = migf->buf_header; + unsigned int orig_length; + bool has_work = false; + ssize_t done = 0; + int ret = 0; + + if (pos) + return -ESPIPE; + + pos = &filp->f_pos; + if (*pos < vhca_buf->start_pos) + return -EINVAL; + + mutex_lock(&migf->virtvdev->state_mutex); + mutex_lock(&migf->lock); + if (migf->state == VIRTIOVF_MIGF_STATE_ERROR) { + done = -ENODEV; + goto out_unlock; + } + + while (len || has_work) { + has_work = false; + switch (migf->load_state) { + case VIRTIOVF_LOAD_STATE_READ_HEADER: + ret = virtiovf_resume_read_header(migf, vhca_buf_header, &buf, + &len, pos, &done, &has_work); + if (ret) + goto out_unlock; + break; + case VIRTIOVF_LOAD_STATE_PREP_HEADER_DATA: + if (vhca_buf_header->allocated_length < migf->record_size) { + virtiovf_free_data_buffer(vhca_buf_header); + + migf->buf_header = virtiovf_alloc_data_buffer(migf, + migf->record_size); + if (IS_ERR(migf->buf_header)) { + ret = PTR_ERR(migf->buf_header); + migf->buf_header = NULL; + goto out_unlock; + } + + vhca_buf_header = migf->buf_header; + } + + vhca_buf_header->start_pos = migf->max_pos; + migf->load_state = VIRTIOVF_LOAD_STATE_READ_HEADER_DATA; + break; + case VIRTIOVF_LOAD_STATE_READ_HEADER_DATA: + ret = virtiovf_resume_read_header_data(migf, vhca_buf_header, + &buf, &len, pos, &done); + if (ret) + goto out_unlock; + break; + case VIRTIOVF_LOAD_STATE_PREP_CHUNK: + { + u32 cmd_size = migf->record_size + + sizeof(struct virtio_admin_cmd_resource_obj_cmd_hdr); + + /* + * The DMA map/unmap is managed in virtio layer, we just need to extend + * the SG pages to hold the extra required chunk data. + */ + if (vhca_buf->allocated_length < cmd_size) { + ret = virtiovf_add_migration_pages(vhca_buf, + DIV_ROUND_UP_ULL(cmd_size - vhca_buf->allocated_length, + PAGE_SIZE)); + if (ret) + goto out_unlock; + } + + vhca_buf->start_pos = migf->max_pos; + migf->load_state = VIRTIOVF_LOAD_STATE_READ_CHUNK; + break; + } + case VIRTIOVF_LOAD_STATE_READ_CHUNK: + ret = virtiovf_resume_read_chunk(migf, vhca_buf, migf->record_size, + &buf, &len, pos, &done, &has_work); + if (ret) + goto out_unlock; + break; + case VIRTIOVF_LOAD_STATE_LOAD_CHUNK: + /* Mark the last SG entry and set its length */ + sg_mark_end(vhca_buf->last_offset_sg); + orig_length = vhca_buf->last_offset_sg->length; + /* Length should include the resource object command header */ + vhca_buf->last_offset_sg->length = vhca_buf->length + + sizeof(struct virtio_admin_cmd_resource_obj_cmd_hdr) - + vhca_buf->last_offset; + ret = virtio_pci_admin_dev_parts_set(migf->virtvdev->core_device.pdev, + vhca_buf->table.sgt.sgl); + /* Restore the original SG data */ + vhca_buf->last_offset_sg->length = orig_length; + sg_unmark_end(vhca_buf->last_offset_sg); + if (ret) + goto out_unlock; + migf->load_state = VIRTIOVF_LOAD_STATE_READ_HEADER; + /* be ready for reading the next chunk */ + vhca_buf->length = 0; + break; + default: + break; + } + } + +out_unlock: + if (ret) + migf->state = VIRTIOVF_MIGF_STATE_ERROR; + mutex_unlock(&migf->lock); + virtiovf_state_mutex_unlock(migf->virtvdev); + return ret ? ret : done; +} + +static const struct file_operations virtiovf_resume_fops = { + .owner = THIS_MODULE, + .write = virtiovf_resume_write, + .release = virtiovf_release_file, +}; + +static struct virtiovf_migration_file * +virtiovf_pci_resume_device_data(struct virtiovf_pci_core_device *virtvdev) +{ + struct virtiovf_migration_file *migf; + struct virtiovf_data_buffer *buf; + u32 obj_id; + int ret; + + migf = kzalloc(sizeof(*migf), GFP_KERNEL_ACCOUNT); + if (!migf) + return ERR_PTR(-ENOMEM); + + migf->filp = anon_inode_getfile("virtiovf_mig", &virtiovf_resume_fops, migf, + O_WRONLY); + if (IS_ERR(migf->filp)) { + ret = PTR_ERR(migf->filp); + kfree(migf); + return ERR_PTR(ret); + } + + stream_open(migf->filp->f_inode, migf->filp); + mutex_init(&migf->lock); + INIT_LIST_HEAD(&migf->buf_list); + INIT_LIST_HEAD(&migf->avail_list); + spin_lock_init(&migf->list_lock); + + buf = virtiovf_alloc_data_buffer(migf, VIRTIOVF_TARGET_INITIAL_BUF_SIZE); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); + goto out; + } + + migf->buf = buf; + + buf = virtiovf_alloc_data_buffer(migf, + sizeof(struct virtiovf_migration_header)); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); + goto out_clean; + } + + migf->buf_header = buf; + migf->load_state = VIRTIOVF_LOAD_STATE_READ_HEADER; + + migf->virtvdev = virtvdev; + ret = virtiovf_pci_alloc_obj_id(virtvdev, VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_SET, + &obj_id); + if (ret) + goto out_clean; + + migf->obj_id = obj_id; + /* Mark as having a valid obj id which can be even 0 */ + migf->has_obj_id = true; + ret = virtiovf_set_obj_cmd_header(migf->buf); + if (ret) + goto out_clean; + + return migf; + +out_clean: + virtiovf_clean_migf_resources(migf); +out: + fput(migf->filp); + return ERR_PTR(ret); +} + +static struct file * +virtiovf_pci_step_device_state_locked(struct virtiovf_pci_core_device *virtvdev, + u32 new) +{ + u32 cur = virtvdev->mig_state; + int ret; + + if (cur == VFIO_DEVICE_STATE_RUNNING_P2P && new == VFIO_DEVICE_STATE_STOP) { + /* NOP */ + return NULL; + } + + if (cur == VFIO_DEVICE_STATE_STOP && new == VFIO_DEVICE_STATE_RUNNING_P2P) { + /* NOP */ + return NULL; + } + + if ((cur == VFIO_DEVICE_STATE_RUNNING && new == VFIO_DEVICE_STATE_RUNNING_P2P) || + (cur == VFIO_DEVICE_STATE_PRE_COPY && new == VFIO_DEVICE_STATE_PRE_COPY_P2P)) { + ret = virtio_pci_admin_mode_set(virtvdev->core_device.pdev, + BIT(VIRTIO_ADMIN_CMD_DEV_MODE_F_STOPPED)); + if (ret) + return ERR_PTR(ret); + return NULL; + } + + if ((cur == VFIO_DEVICE_STATE_RUNNING_P2P && new == VFIO_DEVICE_STATE_RUNNING) || + (cur == VFIO_DEVICE_STATE_PRE_COPY_P2P && new == VFIO_DEVICE_STATE_PRE_COPY)) { + ret = virtio_pci_admin_mode_set(virtvdev->core_device.pdev, 0); + if (ret) + return ERR_PTR(ret); + return NULL; + } + + if (cur == VFIO_DEVICE_STATE_STOP && new == VFIO_DEVICE_STATE_STOP_COPY) { + struct virtiovf_migration_file *migf; + + migf = virtiovf_pci_save_device_data(virtvdev, false); + if (IS_ERR(migf)) + return ERR_CAST(migf); + get_file(migf->filp); + virtvdev->saving_migf = migf; + return migf->filp; + } + + if ((cur == VFIO_DEVICE_STATE_STOP_COPY && new == VFIO_DEVICE_STATE_STOP) || + (cur == VFIO_DEVICE_STATE_PRE_COPY && new == VFIO_DEVICE_STATE_RUNNING) || + (cur == VFIO_DEVICE_STATE_PRE_COPY_P2P && new == VFIO_DEVICE_STATE_RUNNING_P2P)) { + virtiovf_disable_fds(virtvdev); + return NULL; + } + + if (cur == VFIO_DEVICE_STATE_STOP && new == VFIO_DEVICE_STATE_RESUMING) { + struct virtiovf_migration_file *migf; + + migf = virtiovf_pci_resume_device_data(virtvdev); + if (IS_ERR(migf)) + return ERR_CAST(migf); + get_file(migf->filp); + virtvdev->resuming_migf = migf; + return migf->filp; + } + + if (cur == VFIO_DEVICE_STATE_RESUMING && new == VFIO_DEVICE_STATE_STOP) { + virtiovf_disable_fds(virtvdev); + return NULL; + } + + if ((cur == VFIO_DEVICE_STATE_RUNNING && new == VFIO_DEVICE_STATE_PRE_COPY) || + (cur == VFIO_DEVICE_STATE_RUNNING_P2P && + new == VFIO_DEVICE_STATE_PRE_COPY_P2P)) { + struct virtiovf_migration_file *migf; + + migf = virtiovf_pci_save_device_data(virtvdev, true); + if (IS_ERR(migf)) + return ERR_CAST(migf); + get_file(migf->filp); + virtvdev->saving_migf = migf; + return migf->filp; + } + + if (cur == VFIO_DEVICE_STATE_PRE_COPY_P2P && new == VFIO_DEVICE_STATE_STOP_COPY) { + ret = virtiovf_pci_save_device_final_data(virtvdev); + return ret ? ERR_PTR(ret) : NULL; + } + + /* + * vfio_mig_get_next_state() does not use arcs other than the above + */ + WARN_ON(true); + return ERR_PTR(-EINVAL); +} + +static struct file * +virtiovf_pci_set_device_state(struct vfio_device *vdev, + enum vfio_device_mig_state new_state) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + vdev, struct virtiovf_pci_core_device, core_device.vdev); + enum vfio_device_mig_state next_state; + struct file *res = NULL; + int ret; + + mutex_lock(&virtvdev->state_mutex); + while (new_state != virtvdev->mig_state) { + ret = vfio_mig_get_next_state(vdev, virtvdev->mig_state, + new_state, &next_state); + if (ret) { + res = ERR_PTR(ret); + break; + } + res = virtiovf_pci_step_device_state_locked(virtvdev, next_state); + if (IS_ERR(res)) + break; + virtvdev->mig_state = next_state; + if (WARN_ON(res && new_state != virtvdev->mig_state)) { + fput(res); + res = ERR_PTR(-EINVAL); + break; + } + } + virtiovf_state_mutex_unlock(virtvdev); + return res; +} + +static int virtiovf_pci_get_device_state(struct vfio_device *vdev, + enum vfio_device_mig_state *curr_state) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + vdev, struct virtiovf_pci_core_device, core_device.vdev); + + mutex_lock(&virtvdev->state_mutex); + *curr_state = virtvdev->mig_state; + virtiovf_state_mutex_unlock(virtvdev); + return 0; +} + +static int virtiovf_pci_get_data_size(struct vfio_device *vdev, + unsigned long *stop_copy_length) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + vdev, struct virtiovf_pci_core_device, core_device.vdev); + bool obj_id_exists; + u32 res_size; + u32 obj_id; + int ret; + + mutex_lock(&virtvdev->state_mutex); + obj_id_exists = virtvdev->saving_migf && virtvdev->saving_migf->has_obj_id; + if (!obj_id_exists) { + ret = virtiovf_pci_alloc_obj_id(virtvdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET, + &obj_id); + if (ret) + goto end; + } else { + obj_id = virtvdev->saving_migf->obj_id; + } + + ret = virtio_pci_admin_dev_parts_metadata_get(virtvdev->core_device.pdev, + VIRTIO_RESOURCE_OBJ_DEV_PARTS, obj_id, + VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE, + &res_size); + if (!ret) + *stop_copy_length = res_size; + + /* + * We can't leave this obj_id alive if didn't exist before, otherwise, it might + * stay alive, even without an active migration flow (e.g. migration was cancelled) + */ + if (!obj_id_exists) + virtiovf_pci_free_obj_id(virtvdev, obj_id); +end: + virtiovf_state_mutex_unlock(virtvdev); + return ret; +} + +static const struct vfio_migration_ops virtvdev_pci_mig_ops = { + .migration_set_state = virtiovf_pci_set_device_state, + .migration_get_state = virtiovf_pci_get_device_state, + .migration_get_data_size = virtiovf_pci_get_data_size, +}; + +void virtiovf_set_migratable(struct virtiovf_pci_core_device *virtvdev) +{ + virtvdev->migrate_cap = 1; + mutex_init(&virtvdev->state_mutex); + spin_lock_init(&virtvdev->reset_lock); + virtvdev->core_device.vdev.migration_flags = + VFIO_MIGRATION_STOP_COPY | + VFIO_MIGRATION_P2P | + VFIO_MIGRATION_PRE_COPY; + virtvdev->core_device.vdev.mig_ops = &virtvdev_pci_mig_ops; +} + +void virtiovf_open_migration(struct virtiovf_pci_core_device *virtvdev) +{ + if (!virtvdev->migrate_cap) + return; + + virtvdev->mig_state = VFIO_DEVICE_STATE_RUNNING; +} + +void virtiovf_close_migration(struct virtiovf_pci_core_device *virtvdev) +{ + if (!virtvdev->migrate_cap) + return; + + virtiovf_disable_fds(virtvdev); +} diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c index 5832485ab998c..c3329c8b4c169 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c @@ -1230,17 +1230,6 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu)); } -void dispc_enable_fifomerge(bool enable) -{ - if (!dss_has_feature(FEAT_FIFO_MERGE)) { - WARN_ON(enable); - return; - } - - DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); - REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); -} - void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, bool manual_update) @@ -3656,22 +3645,6 @@ void dispc_mgr_set_clock_div(enum omap_channel channel, dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); } -int dispc_mgr_get_clock_div(enum omap_channel channel, - struct dispc_clock_info *cinfo) -{ - unsigned long fck; - - fck = dispc_fclk_rate(); - - cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16); - cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0); - - cinfo->lck = fck / cinfo->lck_div; - cinfo->pck = cinfo->lck / cinfo->pck_div; - - return 0; -} - u32 dispc_read_irqstatus(void) { return dispc_read_reg(DISPC_IRQSTATUS); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.h b/drivers/video/fbdev/omap2/omapfb/dss/dss.h index 21cfcbf74a6d9..a33a43f26829f 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.h @@ -366,7 +366,6 @@ void dispc_disable_sidle(void); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); -void dispc_enable_fifomerge(bool enable); void dispc_enable_gamma_table(bool enable); typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck, @@ -388,8 +387,6 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, void dispc_mgr_set_clock_div(enum omap_channel channel, const struct dispc_clock_info *cinfo); -int dispc_mgr_get_clock_div(enum omap_channel channel, - struct dispc_clock_info *cinfo); void dispc_set_tv_pclk(unsigned long pclk); u32 dispc_read_irqstatus(void); diff --git a/drivers/video/fbdev/sh7760fb.c b/drivers/video/fbdev/sh7760fb.c index 3d2a27fefc874..130adef2e4686 100644 --- a/drivers/video/fbdev/sh7760fb.c +++ b/drivers/video/fbdev/sh7760fb.c @@ -409,12 +409,11 @@ static int sh7760fb_alloc_mem(struct fb_info *info) vram = PAGE_SIZE; fbmem = dma_alloc_coherent(info->device, vram, &par->fbdma, GFP_KERNEL); - if (!fbmem) return -ENOMEM; if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { - sh7760fb_free_mem(info); + dma_free_coherent(info->device, vram, fbmem, par->fbdma); dev_err(info->device, "kernel gave me memory at 0x%08lx, which is" "unusable for the LCDC\n", (unsigned long)par->fbdma); return -ENOMEM; diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 8beecf23ec85e..8cd01de27bafe 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -48,6 +48,9 @@ struct virtio_pci_admin_vq { /* Protects virtqueue access. */ spinlock_t lock; u64 supported_cmds; + u64 supported_caps; + u8 max_dev_parts_objects; + struct ida dev_parts_ida; /* Name of the admin queue: avq.$vq_index. */ char name[10]; u16 vq_index; @@ -167,15 +170,27 @@ struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev); BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ) | \ BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO)) +#define VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP \ + (BIT_ULL(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_DRIVER_CAP_SET) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_DEVICE_CAP_GET) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_DEV_PARTS_GET) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_DEV_PARTS_SET) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_DEV_MODE_SET)) + /* Unlike modern drivers which support hardware virtio devices, legacy drivers * assume software-based devices: e.g. they don't use proper memory barriers * on ARM, use big endian on PPC, etc. X86 drivers are mostly ok though, more * or less by chance. For now, only support legacy IO on X86. */ #ifdef CONFIG_VIRTIO_PCI_ADMIN_LEGACY -#define VIRTIO_ADMIN_CMD_BITMAP VIRTIO_LEGACY_ADMIN_CMD_BITMAP +#define VIRTIO_ADMIN_CMD_BITMAP (VIRTIO_LEGACY_ADMIN_CMD_BITMAP | \ + VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP) #else -#define VIRTIO_ADMIN_CMD_BITMAP 0 +#define VIRTIO_ADMIN_CMD_BITMAP VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP #endif bool vp_is_avq(struct virtio_device *vdev, unsigned int index); diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 4fbcbc7a9ae1c..5eaade7578606 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -15,6 +15,7 @@ */ #include +#include #define VIRTIO_PCI_NO_LEGACY #define VIRTIO_RING_NO_LEGACY #include "virtio_pci_common.h" @@ -54,8 +55,10 @@ void vp_modern_avq_done(struct virtqueue *vq) spin_lock_irqsave(&admin_vq->lock, flags); do { virtqueue_disable_cb(vq); - while ((cmd = virtqueue_get_buf(vq, &len))) + while ((cmd = virtqueue_get_buf(vq, &len))) { + cmd->result_sg_size = len; complete(&cmd->completion); + } } while (!virtqueue_enable_cb(vq)); spin_unlock_irqrestore(&admin_vq->lock, flags); } @@ -218,12 +221,117 @@ static void virtio_pci_admin_cmd_list_init(struct virtio_device *virtio_dev) kfree(data); } +static void +virtio_pci_admin_cmd_dev_parts_objects_enable(struct virtio_device *virtio_dev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev); + struct virtio_admin_cmd_cap_get_data *get_data; + struct virtio_admin_cmd_cap_set_data *set_data; + struct virtio_dev_parts_cap *result; + struct virtio_admin_cmd cmd = {}; + struct scatterlist result_sg; + struct scatterlist data_sg; + u8 resource_objects_limit; + u16 set_data_size; + int ret; + + get_data = kzalloc(sizeof(*get_data), GFP_KERNEL); + if (!get_data) + return; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + goto end; + + get_data->id = cpu_to_le16(VIRTIO_DEV_PARTS_CAP); + sg_init_one(&data_sg, get_data, sizeof(*get_data)); + sg_init_one(&result_sg, result, sizeof(*result)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEVICE_CAP_GET); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.data_sg = &data_sg; + cmd.result_sg = &result_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (ret) + goto err_get; + + set_data_size = sizeof(*set_data) + sizeof(*result); + set_data = kzalloc(set_data_size, GFP_KERNEL); + if (!set_data) + goto err_get; + + set_data->id = cpu_to_le16(VIRTIO_DEV_PARTS_CAP); + + /* Set the limit to the minimum value between the GET and SET values + * supported by the device. Since the obj_id for VIRTIO_DEV_PARTS_CAP + * is a globally unique value per PF, there is no possibility of + * overlap between GET and SET operations. + */ + resource_objects_limit = min(result->get_parts_resource_objects_limit, + result->set_parts_resource_objects_limit); + result->get_parts_resource_objects_limit = resource_objects_limit; + result->set_parts_resource_objects_limit = resource_objects_limit; + memcpy(set_data->cap_specific_data, result, sizeof(*result)); + sg_init_one(&data_sg, set_data, set_data_size); + cmd.data_sg = &data_sg; + cmd.result_sg = NULL; + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DRIVER_CAP_SET); + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (ret) + goto err_set; + + /* Allocate IDR to manage the dev caps objects */ + ida_init(&vp_dev->admin_vq.dev_parts_ida); + vp_dev->admin_vq.max_dev_parts_objects = resource_objects_limit; + +err_set: + kfree(set_data); +err_get: + kfree(result); +end: + kfree(get_data); +} + +static void virtio_pci_admin_cmd_cap_init(struct virtio_device *virtio_dev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev); + struct virtio_admin_cmd_query_cap_id_result *data; + struct virtio_admin_cmd cmd = {}; + struct scatterlist result_sg; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + sg_init_one(&result_sg, data, sizeof(*data)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.result_sg = &result_sg; + + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (ret) + goto end; + + /* Max number of caps fits into a single u64 */ + BUILD_BUG_ON(sizeof(data->supported_caps) > sizeof(u64)); + + vp_dev->admin_vq.supported_caps = le64_to_cpu(data->supported_caps[0]); + + if (!(vp_dev->admin_vq.supported_caps & (1 << VIRTIO_DEV_PARTS_CAP))) + goto end; + + virtio_pci_admin_cmd_dev_parts_objects_enable(virtio_dev); +end: + kfree(data); +} + static void vp_modern_avq_activate(struct virtio_device *vdev) { if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) return; virtio_pci_admin_cmd_list_init(vdev); + virtio_pci_admin_cmd_cap_init(vdev); } static void vp_modern_avq_cleanup(struct virtio_device *vdev) @@ -758,6 +866,353 @@ static bool vp_get_shm_region(struct virtio_device *vdev, return true; } +/* + * virtio_pci_admin_has_dev_parts - Checks whether the device parts + * functionality is supported + * @pdev: VF pci_dev + * + * Returns true on success. + */ +bool virtio_pci_admin_has_dev_parts(struct pci_dev *pdev) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_pci_device *vp_dev; + + if (!virtio_dev) + return false; + + if (!virtio_has_feature(virtio_dev, VIRTIO_F_ADMIN_VQ)) + return false; + + vp_dev = to_vp_device(virtio_dev); + + if (!((vp_dev->admin_vq.supported_cmds & VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP) == + VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP)) + return false; + + return vp_dev->admin_vq.max_dev_parts_objects; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_has_dev_parts); + +/* + * virtio_pci_admin_mode_set - Sets the mode of a member device + * @pdev: VF pci_dev + * @flags: device mode's flags + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_mode_set(struct pci_dev *pdev, u8 flags) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_dev_mode_set_data *data; + struct virtio_admin_cmd cmd = {}; + struct scatterlist data_sg; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->flags = flags; + sg_init_one(&data_sg, data, sizeof(*data)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_MODE_SET); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + + kfree(data); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_mode_set); + +/* + * virtio_pci_admin_obj_create - Creates an object for a given type and operation, + * following the max objects that can be created for that request. + * @pdev: VF pci_dev + * @obj_type: Object type + * @operation_type: Operation type + * @obj_id: Output unique object id + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_obj_create(struct pci_dev *pdev, u16 obj_type, u8 operation_type, + u32 *obj_id) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + u16 data_size = sizeof(struct virtio_admin_cmd_resource_obj_create_data); + struct virtio_admin_cmd_resource_obj_create_data *obj_create_data; + struct virtio_resource_obj_dev_parts obj_dev_parts = {}; + struct virtio_pci_admin_vq *avq; + struct virtio_admin_cmd cmd = {}; + struct scatterlist data_sg; + void *data; + int id = -1; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + if (obj_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS) + return -EOPNOTSUPP; + + if (operation_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET && + operation_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_SET) + return -EINVAL; + + avq = &to_vp_device(virtio_dev)->admin_vq; + if (!avq->max_dev_parts_objects) + return -EOPNOTSUPP; + + id = ida_alloc_range(&avq->dev_parts_ida, 0, + avq->max_dev_parts_objects - 1, GFP_KERNEL); + if (id < 0) + return id; + + *obj_id = id; + data_size += sizeof(obj_dev_parts); + data = kzalloc(data_size, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto end; + } + + obj_create_data = data; + obj_create_data->hdr.type = cpu_to_le16(obj_type); + obj_create_data->hdr.id = cpu_to_le32(*obj_id); + obj_dev_parts.type = operation_type; + memcpy(obj_create_data->resource_obj_specific_data, &obj_dev_parts, + sizeof(obj_dev_parts)); + sg_init_one(&data_sg, data, data_size); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + + kfree(data); +end: + if (ret) + ida_free(&avq->dev_parts_ida, id); + + return ret; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_obj_create); + +/* + * virtio_pci_admin_obj_destroy - Destroys an object of a given type and id + * @pdev: VF pci_dev + * @obj_type: Object type + * @id: Object id + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_obj_destroy(struct pci_dev *pdev, u16 obj_type, u32 id) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_resource_obj_cmd_hdr *data; + struct virtio_pci_device *vp_dev; + struct virtio_admin_cmd cmd = {}; + struct scatterlist data_sg; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + if (obj_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->type = cpu_to_le16(obj_type); + data->id = cpu_to_le32(id); + sg_init_one(&data_sg, data, sizeof(*data)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (!ret) { + vp_dev = to_vp_device(virtio_dev); + ida_free(&vp_dev->admin_vq.dev_parts_ida, id); + } + + kfree(data); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_obj_destroy); + +/* + * virtio_pci_admin_dev_parts_metadata_get - Gets the metadata of the device parts + * identified by the below attributes. + * @pdev: VF pci_dev + * @obj_type: Object type + * @id: Object id + * @metadata_type: Metadata type + * @out: Upon success holds the output for 'metadata type size' + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_dev_parts_metadata_get(struct pci_dev *pdev, u16 obj_type, + u32 id, u8 metadata_type, u32 *out) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_dev_parts_metadata_result *result; + struct virtio_admin_cmd_dev_parts_metadata_data *data; + struct scatterlist data_sg, result_sg; + struct virtio_admin_cmd cmd = {}; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + if (metadata_type != VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE) + return -EOPNOTSUPP; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) { + ret = -ENOMEM; + goto end; + } + + data->hdr.type = cpu_to_le16(obj_type); + data->hdr.id = cpu_to_le32(id); + data->type = metadata_type; + sg_init_one(&data_sg, data, sizeof(*data)); + sg_init_one(&result_sg, result, sizeof(*result)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + cmd.result_sg = &result_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (!ret) + *out = le32_to_cpu(result->parts_size.size); + + kfree(result); +end: + kfree(data); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_dev_parts_metadata_get); + +/* + * virtio_pci_admin_dev_parts_get - Gets the device parts identified by the below attributes. + * @pdev: VF pci_dev + * @obj_type: Object type + * @id: Object id + * @get_type: Get type + * @res_sg: Upon success holds the output result data + * @res_size: Upon success holds the output result size + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_dev_parts_get(struct pci_dev *pdev, u16 obj_type, u32 id, + u8 get_type, struct scatterlist *res_sg, + u32 *res_size) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_dev_parts_get_data *data; + struct scatterlist data_sg; + struct virtio_admin_cmd cmd = {}; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + if (get_type != VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_ALL) + return -EOPNOTSUPP; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->hdr.type = cpu_to_le16(obj_type); + data->hdr.id = cpu_to_le32(id); + data->type = get_type; + sg_init_one(&data_sg, data, sizeof(*data)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_PARTS_GET); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + cmd.result_sg = res_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (!ret) + *res_size = cmd.result_sg_size; + + kfree(data); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_dev_parts_get); + +/* + * virtio_pci_admin_dev_parts_set - Sets the device parts identified by the below attributes. + * @pdev: VF pci_dev + * @data_sg: The device parts data, its layout follows struct virtio_admin_cmd_dev_parts_set_data + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_dev_parts_set(struct pci_dev *pdev, struct scatterlist *data_sg) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd cmd = {}; + int vf_id; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_PARTS_SET); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = data_sg; + return vp_modern_admin_cmd_exec(virtio_dev, &cmd); +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_dev_parts_set); + static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .get = NULL, .set = NULL, diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 8167be01b4001..82a7d2cbc7045 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -223,15 +223,6 @@ struct vring_virtqueue { #endif }; -static struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring_virtqueue_split *vring_split, - struct virtio_device *vdev, - bool weak_barriers, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name, - struct device *dma_dev); static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num); static void vring_free(struct virtqueue *_vq); @@ -1135,6 +1126,64 @@ static int vring_alloc_queue_split(struct vring_virtqueue_split *vring_split, return 0; } +static struct virtqueue *__vring_new_virtqueue_split(unsigned int index, + struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + bool weak_barriers, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name, + struct device *dma_dev) +{ + struct vring_virtqueue *vq; + int err; + + vq = kmalloc(sizeof(*vq), GFP_KERNEL); + if (!vq) + return NULL; + + vq->packed_ring = false; + vq->vq.callback = callback; + vq->vq.vdev = vdev; + vq->vq.name = name; + vq->vq.index = index; + vq->vq.reset = false; + vq->we_own_ring = false; + vq->notify = notify; + vq->weak_barriers = weak_barriers; +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + vq->broken = true; +#else + vq->broken = false; +#endif + vq->dma_dev = dma_dev; + vq->use_dma_api = vring_use_dma_api(vdev); + + vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && + !context; + vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX); + + if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) + vq->weak_barriers = false; + + err = vring_alloc_state_extra_split(vring_split); + if (err) { + kfree(vq); + return NULL; + } + + virtqueue_vring_init_split(vring_split, vq); + + virtqueue_init(vq, vring_split->vring.num); + virtqueue_vring_attach_split(vq, vring_split); + + spin_lock(&vdev->vqs_list_lock); + list_add_tail(&vq->vq.list, &vdev->vqs); + spin_unlock(&vdev->vqs_list_lock); + return &vq->vq; +} + static struct virtqueue *vring_create_virtqueue_split( unsigned int index, unsigned int num, @@ -1157,7 +1206,7 @@ static struct virtqueue *vring_create_virtqueue_split( if (err) return NULL; - vq = __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers, + vq = __vring_new_virtqueue_split(index, &vring_split, vdev, weak_barriers, context, notify, callback, name, dma_dev); if (!vq) { vring_free_split(&vring_split, vdev, dma_dev); @@ -2055,36 +2104,29 @@ static void virtqueue_reinit_packed(struct vring_virtqueue *vq) virtqueue_vring_init_packed(&vq->packed, !!vq->vq.callback); } -static struct virtqueue *vring_create_virtqueue_packed( - unsigned int index, - unsigned int num, - unsigned int vring_align, - struct virtio_device *vdev, - bool weak_barriers, - bool may_reduce_num, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name, - struct device *dma_dev) +static struct virtqueue *__vring_new_virtqueue_packed(unsigned int index, + struct vring_virtqueue_packed *vring_packed, + struct virtio_device *vdev, + bool weak_barriers, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name, + struct device *dma_dev) { - struct vring_virtqueue_packed vring_packed = {}; struct vring_virtqueue *vq; int err; - if (vring_alloc_queue_packed(&vring_packed, vdev, num, dma_dev)) - goto err_ring; - vq = kmalloc(sizeof(*vq), GFP_KERNEL); if (!vq) - goto err_vq; + return NULL; vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; vq->vq.index = index; vq->vq.reset = false; - vq->we_own_ring = true; + vq->we_own_ring = false; vq->notify = notify; vq->weak_barriers = weak_barriers; #ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION @@ -2103,26 +2145,52 @@ static struct virtqueue *vring_create_virtqueue_packed( if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) vq->weak_barriers = false; - err = vring_alloc_state_extra_packed(&vring_packed); - if (err) - goto err_state_extra; + err = vring_alloc_state_extra_packed(vring_packed); + if (err) { + kfree(vq); + return NULL; + } - virtqueue_vring_init_packed(&vring_packed, !!callback); + virtqueue_vring_init_packed(vring_packed, !!callback); - virtqueue_init(vq, num); - virtqueue_vring_attach_packed(vq, &vring_packed); + virtqueue_init(vq, vring_packed->vring.num); + virtqueue_vring_attach_packed(vq, vring_packed); spin_lock(&vdev->vqs_list_lock); list_add_tail(&vq->vq.list, &vdev->vqs); spin_unlock(&vdev->vqs_list_lock); return &vq->vq; +} -err_state_extra: - kfree(vq); -err_vq: - vring_free_packed(&vring_packed, vdev, dma_dev); -err_ring: - return NULL; +static struct virtqueue *vring_create_virtqueue_packed( + unsigned int index, + unsigned int num, + unsigned int vring_align, + struct virtio_device *vdev, + bool weak_barriers, + bool may_reduce_num, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name, + struct device *dma_dev) +{ + struct vring_virtqueue_packed vring_packed = {}; + struct virtqueue *vq; + + if (vring_alloc_queue_packed(&vring_packed, vdev, num, dma_dev)) + return NULL; + + vq = __vring_new_virtqueue_packed(index, &vring_packed, vdev, weak_barriers, + context, notify, callback, name, dma_dev); + if (!vq) { + vring_free_packed(&vring_packed, vdev, dma_dev); + return NULL; + } + + to_vvq(vq)->we_own_ring = true; + + return vq; } static int virtqueue_resize_packed(struct virtqueue *_vq, u32 num) @@ -2650,68 +2718,6 @@ irqreturn_t vring_interrupt(int irq, void *_vq) } EXPORT_SYMBOL_GPL(vring_interrupt); -/* Only available for split ring */ -static struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring_virtqueue_split *vring_split, - struct virtio_device *vdev, - bool weak_barriers, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name, - struct device *dma_dev) -{ - struct vring_virtqueue *vq; - int err; - - if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) - return NULL; - - vq = kmalloc(sizeof(*vq), GFP_KERNEL); - if (!vq) - return NULL; - - vq->packed_ring = false; - vq->vq.callback = callback; - vq->vq.vdev = vdev; - vq->vq.name = name; - vq->vq.index = index; - vq->vq.reset = false; - vq->we_own_ring = false; - vq->notify = notify; - vq->weak_barriers = weak_barriers; -#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION - vq->broken = true; -#else - vq->broken = false; -#endif - vq->dma_dev = dma_dev; - vq->use_dma_api = vring_use_dma_api(vdev); - - vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && - !context; - vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX); - - if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) - vq->weak_barriers = false; - - err = vring_alloc_state_extra_split(vring_split); - if (err) { - kfree(vq); - return NULL; - } - - virtqueue_vring_init_split(vring_split, vq); - - virtqueue_init(vq, vring_split->vring.num); - virtqueue_vring_attach_split(vq, vring_split); - - spin_lock(&vdev->vqs_list_lock); - list_add_tail(&vq->vq.list, &vdev->vqs); - spin_unlock(&vdev->vqs_list_lock); - return &vq->vq; -} - struct virtqueue *vring_create_virtqueue( unsigned int index, unsigned int num, @@ -2846,7 +2852,6 @@ int virtqueue_reset(struct virtqueue *_vq, } EXPORT_SYMBOL_GPL(virtqueue_reset); -/* Only available for split ring */ struct virtqueue *vring_new_virtqueue(unsigned int index, unsigned int num, unsigned int vring_align, @@ -2860,11 +2865,19 @@ struct virtqueue *vring_new_virtqueue(unsigned int index, { struct vring_virtqueue_split vring_split = {}; - if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) - return NULL; + if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) { + struct vring_virtqueue_packed vring_packed = {}; + + vring_packed.vring.num = num; + vring_packed.vring.desc = pages; + return __vring_new_virtqueue_packed(index, &vring_packed, + vdev, weak_barriers, + context, notify, callback, + name, vdev->dev.parent); + } vring_init(&vring_split.vring, num, pages, vring_align); - return __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers, + return __vring_new_virtqueue_split(index, &vring_split, vdev, weak_barriers, context, notify, callback, name, vdev->dev.parent); } diff --git a/drivers/virtio/virtio_vdpa.c b/drivers/virtio/virtio_vdpa.c index 7364bd53e38dd..1f60c9d5cb181 100644 --- a/drivers/virtio/virtio_vdpa.c +++ b/drivers/virtio/virtio_vdpa.c @@ -364,14 +364,13 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev); struct vdpa_device *vdpa = vd_get_vdpa(vdev); const struct vdpa_config_ops *ops = vdpa->config; - struct irq_affinity default_affd = { 0 }; struct cpumask *masks; struct vdpa_callback cb; bool has_affinity = desc && ops->set_vq_affinity; int i, err, queue_idx = 0; if (has_affinity) { - masks = create_affinity_masks(nvqs, desc ? desc : &default_affd); + masks = create_affinity_masks(nvqs, desc); if (!masks) return -ENOMEM; } diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 281a1ed03a041..77e9c4387c1df 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -659,21 +659,6 @@ static void v9fs_destroy_inode_cache(void) kmem_cache_destroy(v9fs_inode_cache); } -static int v9fs_cache_register(void) -{ - int ret; - - ret = v9fs_init_inode_cache(); - if (ret < 0) - return ret; - return ret; -} - -static void v9fs_cache_unregister(void) -{ - v9fs_destroy_inode_cache(); -} - /** * init_v9fs - Initialize module * @@ -686,7 +671,7 @@ static int __init init_v9fs(void) pr_info("Installing v9fs 9p2000 file system support\n"); /* TODO: Setup list of registered trasnport modules */ - err = v9fs_cache_register(); + err = v9fs_init_inode_cache(); if (err < 0) { pr_err("Failed to register v9fs for caching\n"); return err; @@ -709,7 +694,7 @@ static int __init init_v9fs(void) v9fs_sysfs_cleanup(); out_cache: - v9fs_cache_unregister(); + v9fs_destroy_inode_cache(); return err; } @@ -722,7 +707,7 @@ static int __init init_v9fs(void) static void __exit exit_v9fs(void) { v9fs_sysfs_cleanup(); - v9fs_cache_unregister(); + v9fs_destroy_inode_cache(); unregister_filesystem(&v9fs_fs_type); } diff --git a/fs/backing-file.c b/fs/backing-file.c index 526ddb4d6f764..cbdad8b684742 100644 --- a/fs/backing-file.c +++ b/fs/backing-file.c @@ -327,6 +327,7 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma, struct backing_file_ctx *ctx) { const struct cred *old_cred; + struct file *user_file = vma->vm_file; int ret; if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING))) @@ -342,7 +343,7 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma, revert_creds_light(old_cred); if (ctx->accessed) - ctx->accessed(vma->vm_file); + ctx->accessed(user_file); return ret; } diff --git a/fs/bcachefs/Kconfig b/fs/bcachefs/Kconfig index 5bac803ea367c..ab6c95b895b33 100644 --- a/fs/bcachefs/Kconfig +++ b/fs/bcachefs/Kconfig @@ -24,6 +24,7 @@ config BCACHEFS_FS select XXHASH select SRCU select SYMBOLIC_ERRNAME + select MIN_HEAP help The bcachefs filesystem - a modern, copy on write filesystem, with support for multiple devices, compression, checksumming, etc. diff --git a/fs/bcachefs/clock.c b/fs/bcachefs/clock.c index 1d6b691e8da6e..1f8e035d7119f 100644 --- a/fs/bcachefs/clock.c +++ b/fs/bcachefs/clock.c @@ -14,21 +14,13 @@ static inline bool io_timer_cmp(const void *l, const void *r, void __always_unus return (*_l)->expire < (*_r)->expire; } -static inline void io_timer_swp(void *l, void *r, void __always_unused *args) -{ - struct io_timer **_l = (struct io_timer **)l; - struct io_timer **_r = (struct io_timer **)r; - - swap(*_l, *_r); -} +static const struct min_heap_callbacks callbacks = { + .less = io_timer_cmp, + .swp = NULL, +}; void bch2_io_timer_add(struct io_clock *clock, struct io_timer *timer) { - const struct min_heap_callbacks callbacks = { - .less = io_timer_cmp, - .swp = io_timer_swp, - }; - spin_lock(&clock->timer_lock); if (time_after_eq64((u64) atomic64_read(&clock->now), timer->expire)) { @@ -48,11 +40,6 @@ void bch2_io_timer_add(struct io_clock *clock, struct io_timer *timer) void bch2_io_timer_del(struct io_clock *clock, struct io_timer *timer) { - const struct min_heap_callbacks callbacks = { - .less = io_timer_cmp, - .swp = io_timer_swp, - }; - spin_lock(&clock->timer_lock); for (size_t i = 0; i < clock->timers.nr; i++) @@ -142,10 +129,6 @@ void bch2_kthread_io_clock_wait(struct io_clock *clock, static struct io_timer *get_expired_timer(struct io_clock *clock, u64 now) { struct io_timer *ret = NULL; - const struct min_heap_callbacks callbacks = { - .less = io_timer_cmp, - .swp = io_timer_swp, - }; if (clock->timers.nr && time_after_eq64(now, clock->timers.data[0]->expire)) { diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c index 749dcf368841d..6094afb0c6be7 100644 --- a/fs/bcachefs/ec.c +++ b/fs/bcachefs/ec.c @@ -1057,6 +1057,11 @@ static inline void ec_stripes_heap_swap(void *l, void *r, void *h) ec_stripes_heap_set_backpointer(_h, j); } +static const struct min_heap_callbacks callbacks = { + .less = ec_stripes_heap_cmp, + .swp = ec_stripes_heap_swap, +}; + static void heap_verify_backpointer(struct bch_fs *c, size_t idx) { ec_stripes_heap *h = &c->ec_stripes_heap; @@ -1069,11 +1074,6 @@ static void heap_verify_backpointer(struct bch_fs *c, size_t idx) void bch2_stripes_heap_del(struct bch_fs *c, struct stripe *m, size_t idx) { - const struct min_heap_callbacks callbacks = { - .less = ec_stripes_heap_cmp, - .swp = ec_stripes_heap_swap, - }; - mutex_lock(&c->ec_stripes_heap_lock); heap_verify_backpointer(c, idx); @@ -1084,11 +1084,6 @@ void bch2_stripes_heap_del(struct bch_fs *c, void bch2_stripes_heap_insert(struct bch_fs *c, struct stripe *m, size_t idx) { - const struct min_heap_callbacks callbacks = { - .less = ec_stripes_heap_cmp, - .swp = ec_stripes_heap_swap, - }; - mutex_lock(&c->ec_stripes_heap_lock); BUG_ON(min_heap_full(&c->ec_stripes_heap)); @@ -1107,10 +1102,6 @@ void bch2_stripes_heap_insert(struct bch_fs *c, void bch2_stripes_heap_update(struct bch_fs *c, struct stripe *m, size_t idx) { - const struct min_heap_callbacks callbacks = { - .less = ec_stripes_heap_cmp, - .swp = ec_stripes_heap_swap, - }; ec_stripes_heap *h = &c->ec_stripes_heap; bool do_deletes; size_t i; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3039a6b7aba4b..106f0e8af1779 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1257,6 +1257,7 @@ static int load_elf_binary(struct linux_binprm *bprm) } reloc_func_desc = interp_load_addr; + allow_write_access(interpreter); fput(interpreter); kfree(interp_elf_ex); @@ -1353,6 +1354,7 @@ static int load_elf_binary(struct linux_binprm *bprm) kfree(interp_elf_ex); kfree(interp_elf_phdata); out_free_file: + allow_write_access(interpreter); if (interpreter) fput(interpreter); out_free_ph: diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 31d253bd3961a..f1a7c4875c4aa 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -394,6 +394,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) goto error; } + allow_write_access(interpreter); fput(interpreter); interpreter = NULL; } @@ -465,8 +466,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) retval = 0; error: - if (interpreter) + if (interpreter) { + allow_write_access(interpreter); fput(interpreter); + } kfree(interpreter_name); kfree(exec_params.phdrs); kfree(exec_params.loadmap); diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 31660d8cc2c61..6a3a16f910516 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -247,10 +247,13 @@ static int load_misc_binary(struct linux_binprm *bprm) if (retval < 0) goto ret; - if (fmt->flags & MISC_FMT_OPEN_FILE) + if (fmt->flags & MISC_FMT_OPEN_FILE) { interp_file = file_clone_open(fmt->interp_file); - else + if (!IS_ERR(interp_file)) + deny_write_access(interp_file); + } else { interp_file = open_exec(fmt->interpreter); + } retval = PTR_ERR(interp_file); if (IS_ERR(interp_file)) goto ret; diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 4c82348fe1e60..85936f6d2bf70 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -2195,7 +2195,7 @@ int ceph_pool_perm_check(struct inode *inode, int need) if (ci->i_vino.snap != CEPH_NOSNAP) { /* * Pool permission check needs to write to the first object. - * But for snapshot, head of the first object may have alread + * But for snapshot, head of the first object may have already * been deleted. Skip check to avoid creating orphan object. */ return 0; diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index bed34fc11c919..a8d8b56cf9d21 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -978,20 +978,6 @@ int __ceph_caps_revoking_other(struct ceph_inode_info *ci, return 0; } -int ceph_caps_revoking(struct ceph_inode_info *ci, int mask) -{ - struct inode *inode = &ci->netfs.inode; - struct ceph_client *cl = ceph_inode_to_client(inode); - int ret; - - spin_lock(&ci->i_ceph_lock); - ret = __ceph_caps_revoking_other(ci, NULL, mask); - spin_unlock(&ci->i_ceph_lock); - doutc(cl, "%p %llx.%llx %s = %d\n", inode, ceph_vinop(inode), - ceph_cap_string(mask), ret); - return ret; -} - int __ceph_caps_used(struct ceph_inode_info *ci) { int used = 0; @@ -2813,7 +2799,7 @@ void ceph_take_cap_refs(struct ceph_inode_info *ci, int got, * requested from the MDS. * * Returns 0 if caps were not able to be acquired (yet), 1 if succeed, - * or a negative error code. There are 3 speical error codes: + * or a negative error code. There are 3 special error codes: * -EAGAIN: need to sleep but non-blocking is specified * -EFBIG: ask caller to call check_max_size() and try again. * -EUCLEAN: ask caller to call ceph_renew_caps() and try again. @@ -4085,23 +4071,22 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, struct ceph_cap *cap, *tcap, *new_cap = NULL; struct ceph_inode_info *ci = ceph_inode(inode); u64 t_cap_id; - unsigned mseq = le32_to_cpu(ex->migrate_seq); - unsigned t_seq, t_mseq; + u32 t_issue_seq, t_mseq; int target, issued; int mds = session->s_mds; if (ph) { t_cap_id = le64_to_cpu(ph->cap_id); - t_seq = le32_to_cpu(ph->seq); + t_issue_seq = le32_to_cpu(ph->issue_seq); t_mseq = le32_to_cpu(ph->mseq); target = le32_to_cpu(ph->mds); } else { - t_cap_id = t_seq = t_mseq = 0; + t_cap_id = t_issue_seq = t_mseq = 0; target = -1; } - doutc(cl, "%p %llx.%llx ci %p mds%d mseq %d target %d\n", - inode, ceph_vinop(inode), ci, mds, mseq, target); + doutc(cl, " cap %llx.%llx export to peer %d piseq %u pmseq %u\n", + ceph_vinop(inode), target, t_issue_seq, t_mseq); retry: down_read(&mdsc->snap_rwsem); spin_lock(&ci->i_ceph_lock); @@ -4134,12 +4119,12 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, if (tcap) { /* already have caps from the target */ if (tcap->cap_id == t_cap_id && - ceph_seq_cmp(tcap->seq, t_seq) < 0) { + ceph_seq_cmp(tcap->seq, t_issue_seq) < 0) { doutc(cl, " updating import cap %p mds%d\n", tcap, target); tcap->cap_id = t_cap_id; - tcap->seq = t_seq - 1; - tcap->issue_seq = t_seq - 1; + tcap->seq = t_issue_seq - 1; + tcap->issue_seq = t_issue_seq - 1; tcap->issued |= issued; tcap->implemented |= issued; if (cap == ci->i_auth_cap) { @@ -4154,7 +4139,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, int flag = (cap == ci->i_auth_cap) ? CEPH_CAP_FLAG_AUTH : 0; tcap = new_cap; ceph_add_cap(inode, tsession, t_cap_id, issued, 0, - t_seq - 1, t_mseq, (u64)-1, flag, &new_cap); + t_issue_seq - 1, t_mseq, (u64)-1, flag, &new_cap); if (!list_empty(&ci->i_cap_flush_list) && ci->i_auth_cap == tcap) { @@ -4228,18 +4213,22 @@ static void handle_cap_import(struct ceph_mds_client *mdsc, u64 realmino = le64_to_cpu(im->realm); u64 cap_id = le64_to_cpu(im->cap_id); u64 p_cap_id; + u32 piseq = 0; + u32 pmseq = 0; int peer; if (ph) { p_cap_id = le64_to_cpu(ph->cap_id); peer = le32_to_cpu(ph->mds); + piseq = le32_to_cpu(ph->issue_seq); + pmseq = le32_to_cpu(ph->mseq); } else { p_cap_id = 0; peer = -1; } - doutc(cl, "%p %llx.%llx ci %p mds%d mseq %d peer %d\n", - inode, ceph_vinop(inode), ci, mds, mseq, peer); + doutc(cl, " cap %llx.%llx import from peer %d piseq %u pmseq %u\n", + ceph_vinop(inode), peer, piseq, pmseq); retry: cap = __get_cap_for_mds(ci, mds); if (!cap) { @@ -4268,15 +4257,13 @@ static void handle_cap_import(struct ceph_mds_client *mdsc, doutc(cl, " remove export cap %p mds%d flags %d\n", ocap, peer, ph->flags); if ((ph->flags & CEPH_CAP_FLAG_AUTH) && - (ocap->seq != le32_to_cpu(ph->seq) || - ocap->mseq != le32_to_cpu(ph->mseq))) { + (ocap->seq != piseq || + ocap->mseq != pmseq)) { pr_err_ratelimited_client(cl, "mismatched seq/mseq: " "%p %llx.%llx mds%d seq %d mseq %d" " importer mds%d has peer seq %d mseq %d\n", inode, ceph_vinop(inode), peer, - ocap->seq, ocap->mseq, mds, - le32_to_cpu(ph->seq), - le32_to_cpu(ph->mseq)); + ocap->seq, ocap->mseq, mds, piseq, pmseq); } ceph_remove_cap(mdsc, ocap, (ph->flags & CEPH_CAP_FLAG_RELEASE)); } @@ -4350,7 +4337,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, struct ceph_snap_realm *realm = NULL; int op; int msg_version = le16_to_cpu(msg->hdr.version); - u32 seq, mseq; + u32 seq, mseq, issue_seq; struct ceph_vino vino; void *snaptrace; size_t snaptrace_len; @@ -4360,8 +4347,6 @@ void ceph_handle_caps(struct ceph_mds_session *session, bool close_sessions = false; bool do_cap_release = false; - doutc(cl, "from mds%d\n", session->s_mds); - if (!ceph_inc_mds_stopping_blocker(mdsc, session)) return; @@ -4375,6 +4360,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, vino.snap = CEPH_NOSNAP; seq = le32_to_cpu(h->seq); mseq = le32_to_cpu(h->migrate_seq); + issue_seq = le32_to_cpu(h->issue_seq); snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); @@ -4462,12 +4448,11 @@ void ceph_handle_caps(struct ceph_mds_session *session, /* lookup ino */ inode = ceph_find_inode(mdsc->fsc->sb, vino); - doutc(cl, " op %s ino %llx.%llx inode %p\n", ceph_cap_op_name(op), - vino.ino, vino.snap, inode); + doutc(cl, " caps mds%d op %s ino %llx.%llx inode %p seq %u iseq %u mseq %u\n", + session->s_mds, ceph_cap_op_name(op), vino.ino, vino.snap, inode, + seq, issue_seq, mseq); mutex_lock(&session->s_mutex); - doutc(cl, " mds%d seq %lld cap seq %u\n", session->s_mds, - session->s_seq, (unsigned)seq); if (!inode) { doutc(cl, " i don't have ino %llx\n", vino.ino); diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h index 47e0c319fc68c..d0768239a1c9c 100644 --- a/fs/ceph/crypto.h +++ b/fs/ceph/crypto.h @@ -27,7 +27,7 @@ struct ceph_fname { }; /* - * Header for the crypted file when truncating the size, this + * Header for the encrypted file when truncating the size, this * will be sent to MDS, and the MDS will update the encrypted * last block and then truncate the size. */ diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 24c08078f5aa3..fdf9dc15eafae 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -357,7 +357,7 @@ static int status_show(struct seq_file *s, void *p) seq_printf(s, "instance: %s.%lld %s/%u\n", ENTITY_NAME(inst->name), ceph_pr_addr(client_addr), le32_to_cpu(client_addr->nonce)); - seq_printf(s, "blocklisted: %s\n", fsc->blocklisted ? "true" : "false"); + seq_printf(s, "blocklisted: %s\n", str_true_false(fsc->blocklisted)); return 0; } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 952109292d691..0bf388e07a027 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -207,7 +207,7 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx, dentry = __dcache_find_get_entry(parent, idx + step, &cache_ctl); if (!dentry) { - /* use linar search */ + /* use linear search */ idx = 0; break; } @@ -659,7 +659,7 @@ static bool need_reset_readdir(struct ceph_dir_file_info *dfi, loff_t new_pos) return true; if (is_hash_order(new_pos)) { /* no need to reset last_name for a forward seek when - * dentries are sotred in hash order */ + * dentries are sorted in hash order */ } else if (dfi->frag != fpos_frag(new_pos)) { return true; } diff --git a/fs/ceph/export.c b/fs/ceph/export.c index 44451749c5446..150076ced9374 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -393,9 +393,9 @@ static struct dentry *ceph_get_parent(struct dentry *child) } dir = snapdir; } - /* If directory has already been deleted, futher get_parent + /* If directory has already been deleted, further get_parent * will fail. Do not mark snapdir dentry as disconnected, - * this prevent exportfs from doing futher get_parent. */ + * this prevents exportfs from doing further get_parent. */ if (unlinked) dn = d_obtain_root(dir); else @@ -452,7 +452,13 @@ static int __get_snap_name(struct dentry *parent, char *name, goto out; if (ceph_snap(inode) == CEPH_SNAPDIR) { if (ceph_snap(dir) == CEPH_NOSNAP) { - strcpy(name, fsc->mount_options->snapdir_name); + /* + * .get_name() from struct export_operations + * assumes that its 'name' parameter is pointing + * to a NAME_MAX+1 sized buffer + */ + strscpy(name, fsc->mount_options->snapdir_name, + NAME_MAX + 1); err = 0; } goto out; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 315ef02f9a3fa..7dd6c2275085b 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -160,7 +160,7 @@ struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, } /* - * get/constuct snapdir inode for a given directory + * get/construct snapdir inode for a given directory */ struct inode *ceph_get_snapdir(struct inode *parent) { diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index c4a5fd94bbbb3..219a2cc2bf3cc 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -827,7 +827,7 @@ static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info) * And the worst case is that for the none async openc request it will * successfully open the file if the CDentry hasn't been unlinked yet, * but later the previous delayed async unlink request will remove the - * CDenty. That means the just created file is possiblly deleted later + * CDentry. That means the just created file is possibly deleted later * by accident. * * We need to wait for the inflight async unlink requests to finish @@ -1747,14 +1747,6 @@ static void __open_export_target_sessions(struct ceph_mds_client *mdsc, } } -void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session) -{ - mutex_lock(&mdsc->mutex); - __open_export_target_sessions(mdsc, session); - mutex_unlock(&mdsc->mutex); -} - /* * session caps */ @@ -2362,7 +2354,7 @@ static void ceph_send_cap_releases(struct ceph_mds_client *mdsc, item->ino = cpu_to_le64(cap->cap_ino); item->cap_id = cpu_to_le64(cap->cap_id); item->migrate_seq = cpu_to_le32(cap->mseq); - item->seq = cpu_to_le32(cap->issue_seq); + item->issue_seq = cpu_to_le32(cap->issue_seq); msg->front.iov_len += sizeof(*item); ceph_put_cap(mdsc, cap); @@ -3269,7 +3261,7 @@ static int __prepare_send_request(struct ceph_mds_session *session, &session->s_features); /* - * Avoid inifinite retrying after overflow. The client will + * Avoid infinite retrying after overflow. The client will * increase the retry count and if the MDS is old version, * so we limit to retry at most 256 times. */ @@ -3522,7 +3514,7 @@ static void __do_request(struct ceph_mds_client *mdsc, /* * For async create we will choose the auth MDS of frag in parent - * directory to send the request and ususally this works fine, but + * directory to send the request and usually this works fine, but * if the migrated the dirtory to another MDS before it could handle * it the request will be forwarded. * @@ -4033,7 +4025,7 @@ static void handle_forward(struct ceph_mds_client *mdsc, __unregister_request(mdsc, req); } else if (fwd_seq <= req->r_num_fwd || (uint32_t)fwd_seq >= U32_MAX) { /* - * Avoid inifinite retrying after overflow. + * Avoid infinite retrying after overflow. * * The MDS will increase the fwd count and in client side * if the num_fwd is less than the one saved in request @@ -5609,9 +5601,9 @@ void send_flush_mdlog(struct ceph_mds_session *s) static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, struct ceph_mds_cap_auth *auth, + const struct cred *cred, char *tpath) { - const struct cred *cred = get_current_cred(); u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid); u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid); struct ceph_client *cl = mdsc->fsc->client; @@ -5734,11 +5726,12 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) for (i = 0; i < mdsc->s_cap_auths_num; i++) { struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i]; - err = ceph_mds_auth_match(mdsc, s, tpath); + err = ceph_mds_auth_match(mdsc, s, cred, tpath); if (err < 0) { + put_cred(cred); return err; } else if (err > 0) { - /* always follow the last auth caps' permision */ + /* always follow the last auth caps' permission */ root_squash_perms = true; rw_perms_s = NULL; if ((mask & MAY_WRITE) && s->writeable && @@ -5751,6 +5744,8 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) } } + put_cred(cred); + doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms, rw_perms_s); if (root_squash_perms && rw_perms_s == NULL) { diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 3dd54587944ac..38bb7e0d2d791 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -634,8 +634,6 @@ extern void ceph_mdsc_handle_fsmap(struct ceph_mds_client *mdsc, extern struct ceph_mds_session * ceph_mdsc_open_export_target_session(struct ceph_mds_client *mdsc, int target); -extern void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session); extern int ceph_trim_caps(struct ceph_mds_client *mdsc, struct ceph_mds_session *session, diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 73f321b52895e..de03cd6eb86ee 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -285,8 +285,10 @@ static int ceph_parse_new_source(const char *dev_name, const char *dev_name_end, size_t len; struct ceph_fsid fsid; struct ceph_parse_opts_ctx *pctx = fc->fs_private; + struct ceph_options *opts = pctx->copts; struct ceph_mount_options *fsopt = pctx->opts; - char *fsid_start, *fs_name_start; + const char *name_start = dev_name; + const char *fsid_start, *fs_name_start; if (*dev_name_end != '=') { dout("separator '=' missing in source"); @@ -296,8 +298,14 @@ static int ceph_parse_new_source(const char *dev_name, const char *dev_name_end, fsid_start = strchr(dev_name, '@'); if (!fsid_start) return invalfc(fc, "missing cluster fsid"); - ++fsid_start; /* start of cluster fsid */ + len = fsid_start - name_start; + kfree(opts->name); + opts->name = kstrndup(name_start, len, GFP_KERNEL); + if (!opts->name) + return -ENOMEM; + dout("using %s entity name", opts->name); + ++fsid_start; /* start of cluster fsid */ fs_name_start = strchr(fsid_start, '.'); if (!fs_name_start) return invalfc(fc, "missing file system name"); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 037eac35a9e02..af14ec3822462 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -60,7 +60,7 @@ /* max size of osd read request, limited by libceph */ #define CEPH_MAX_READ_SIZE CEPH_MSG_MAX_DATA_LEN -/* osd has a configurable limitaion of max write size. +/* osd has a configurable limitation of max write size. * CEPH_MSG_MAX_DATA_LEN should be small enough. */ #define CEPH_MAX_WRITE_SIZE CEPH_MSG_MAX_DATA_LEN #define CEPH_RASIZE_DEFAULT (8192*1024) /* max readahead */ @@ -796,7 +796,6 @@ extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask, extern int __ceph_caps_revoking_other(struct ceph_inode_info *ci, struct ceph_cap *ocap, int mask); -extern int ceph_caps_revoking(struct ceph_inode_info *ci, int mask); extern int __ceph_caps_used(struct ceph_inode_info *ci); static inline bool __ceph_is_file_opened(struct ceph_inode_info *ci) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index e066a556eccbf..1a9f122046666 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -899,7 +899,7 @@ static int __get_required_blob_size(struct ceph_inode_info *ci, int name_size, } /* - * If there are dirty xattrs, reencode xattrs into the prealloc_blob + * If there are dirty xattrs, re-encode xattrs into the prealloc_blob * and swap into place. It returns the old i_xattrs.blob (or NULL) so * that it can be freed by the caller as the i_ceph_lock is likely to be * held. diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 577c563023144..8dd1d7189c3b2 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -15,10 +15,10 @@ #include #include #include -#include #include #include -#include +#include +#include #include #include #include @@ -153,32 +153,30 @@ void ecryptfs_put_lower_file(struct inode *inode) } } -enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, - ecryptfs_opt_cipher, ecryptfs_opt_ecryptfs_cipher, - ecryptfs_opt_ecryptfs_key_bytes, - ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata, - ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig, - ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes, - ecryptfs_opt_unlink_sigs, ecryptfs_opt_mount_auth_tok_only, - ecryptfs_opt_check_dev_ruid, - ecryptfs_opt_err }; - -static const match_table_t tokens = { - {ecryptfs_opt_sig, "sig=%s"}, - {ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"}, - {ecryptfs_opt_cipher, "cipher=%s"}, - {ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"}, - {ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"}, - {ecryptfs_opt_passthrough, "ecryptfs_passthrough"}, - {ecryptfs_opt_xattr_metadata, "ecryptfs_xattr_metadata"}, - {ecryptfs_opt_encrypted_view, "ecryptfs_encrypted_view"}, - {ecryptfs_opt_fnek_sig, "ecryptfs_fnek_sig=%s"}, - {ecryptfs_opt_fn_cipher, "ecryptfs_fn_cipher=%s"}, - {ecryptfs_opt_fn_cipher_key_bytes, "ecryptfs_fn_key_bytes=%u"}, - {ecryptfs_opt_unlink_sigs, "ecryptfs_unlink_sigs"}, - {ecryptfs_opt_mount_auth_tok_only, "ecryptfs_mount_auth_tok_only"}, - {ecryptfs_opt_check_dev_ruid, "ecryptfs_check_dev_ruid"}, - {ecryptfs_opt_err, NULL} +enum { + Opt_sig, Opt_ecryptfs_sig, Opt_cipher, Opt_ecryptfs_cipher, + Opt_ecryptfs_key_bytes, Opt_passthrough, Opt_xattr_metadata, + Opt_encrypted_view, Opt_fnek_sig, Opt_fn_cipher, + Opt_fn_cipher_key_bytes, Opt_unlink_sigs, Opt_mount_auth_tok_only, + Opt_check_dev_ruid +}; + +static const struct fs_parameter_spec ecryptfs_fs_param_spec[] = { + fsparam_string ("sig", Opt_sig), + fsparam_string ("ecryptfs_sig", Opt_ecryptfs_sig), + fsparam_string ("cipher", Opt_cipher), + fsparam_string ("ecryptfs_cipher", Opt_ecryptfs_cipher), + fsparam_u32 ("ecryptfs_key_bytes", Opt_ecryptfs_key_bytes), + fsparam_flag ("ecryptfs_passthrough", Opt_passthrough), + fsparam_flag ("ecryptfs_xattr_metadata", Opt_xattr_metadata), + fsparam_flag ("ecryptfs_encrypted_view", Opt_encrypted_view), + fsparam_string ("ecryptfs_fnek_sig", Opt_fnek_sig), + fsparam_string ("ecryptfs_fn_cipher", Opt_fn_cipher), + fsparam_u32 ("ecryptfs_fn_key_bytes", Opt_fn_cipher_key_bytes), + fsparam_flag ("ecryptfs_unlink_sigs", Opt_unlink_sigs), + fsparam_flag ("ecryptfs_mount_auth_tok_only", Opt_mount_auth_tok_only), + fsparam_flag ("ecryptfs_check_dev_ruid", Opt_check_dev_ruid), + {} }; static int ecryptfs_init_global_auth_toks( @@ -219,19 +217,20 @@ static void ecryptfs_init_mount_crypt_stat( mount_crypt_stat->flags |= ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED; } +struct ecryptfs_fs_context { + /* Mount option status trackers */ + bool check_ruid; + bool sig_set; + bool cipher_name_set; + bool cipher_key_bytes_set; + bool fn_cipher_name_set; + bool fn_cipher_key_bytes_set; +}; + /** - * ecryptfs_parse_options - * @sbi: The ecryptfs super block - * @options: The options passed to the kernel - * @check_ruid: set to 1 if device uid should be checked against the ruid - * - * Parse mount options: - * debug=N - ecryptfs_verbosity level for debug output - * sig=XXX - description(signature) of the key to use - * - * Returns the dentry object of the lower-level (lower/interposed) - * directory; We want to mount our stackable file system on top of - * that lower directory. + * ecryptfs_parse_param + * @fc: The ecryptfs filesystem context + * @param: The mount parameter to parse * * The signature of the key to use must be the description of a key * already in the keyring. Mounting will fail if the key can not be @@ -239,143 +238,118 @@ static void ecryptfs_init_mount_crypt_stat( * * Returns zero on success; non-zero on error */ -static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options, - uid_t *check_ruid) +static int ecryptfs_parse_param( + struct fs_context *fc, + struct fs_parameter *param) { - char *p; - int rc = 0; - int sig_set = 0; - int cipher_name_set = 0; - int fn_cipher_name_set = 0; - int cipher_key_bytes; - int cipher_key_bytes_set = 0; - int fn_cipher_key_bytes; - int fn_cipher_key_bytes_set = 0; + int rc; + int opt; + struct fs_parse_result result; + struct ecryptfs_fs_context *ctx = fc->fs_private; + struct ecryptfs_sb_info *sbi = fc->s_fs_info; struct ecryptfs_mount_crypt_stat *mount_crypt_stat = &sbi->mount_crypt_stat; - substring_t args[MAX_OPT_ARGS]; - int token; - char *sig_src; - char *cipher_name_src; - char *fn_cipher_name_src; - char *fnek_src; - char *cipher_key_bytes_src; - char *fn_cipher_key_bytes_src; - u8 cipher_code; - *check_ruid = 0; + opt = fs_parse(fc, ecryptfs_fs_param_spec, param, &result); + if (opt < 0) + return opt; - if (!options) { - rc = -EINVAL; - goto out; - } - ecryptfs_init_mount_crypt_stat(mount_crypt_stat); - while ((p = strsep(&options, ",")) != NULL) { - if (!*p) - continue; - token = match_token(p, tokens, args); - switch (token) { - case ecryptfs_opt_sig: - case ecryptfs_opt_ecryptfs_sig: - sig_src = args[0].from; - rc = ecryptfs_add_global_auth_tok(mount_crypt_stat, - sig_src, 0); - if (rc) { - printk(KERN_ERR "Error attempting to register " - "global sig; rc = [%d]\n", rc); - goto out; - } - sig_set = 1; - break; - case ecryptfs_opt_cipher: - case ecryptfs_opt_ecryptfs_cipher: - cipher_name_src = args[0].from; - strscpy(mount_crypt_stat->global_default_cipher_name, - cipher_name_src); - cipher_name_set = 1; - break; - case ecryptfs_opt_ecryptfs_key_bytes: - cipher_key_bytes_src = args[0].from; - cipher_key_bytes = - (int)simple_strtol(cipher_key_bytes_src, - &cipher_key_bytes_src, 0); - mount_crypt_stat->global_default_cipher_key_size = - cipher_key_bytes; - cipher_key_bytes_set = 1; - break; - case ecryptfs_opt_passthrough: - mount_crypt_stat->flags |= - ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED; - break; - case ecryptfs_opt_xattr_metadata: - mount_crypt_stat->flags |= - ECRYPTFS_XATTR_METADATA_ENABLED; - break; - case ecryptfs_opt_encrypted_view: - mount_crypt_stat->flags |= - ECRYPTFS_XATTR_METADATA_ENABLED; - mount_crypt_stat->flags |= - ECRYPTFS_ENCRYPTED_VIEW_ENABLED; - break; - case ecryptfs_opt_fnek_sig: - fnek_src = args[0].from; - strscpy(mount_crypt_stat->global_default_fnek_sig, - fnek_src); - rc = ecryptfs_add_global_auth_tok( - mount_crypt_stat, - mount_crypt_stat->global_default_fnek_sig, - ECRYPTFS_AUTH_TOK_FNEK); - if (rc) { - printk(KERN_ERR "Error attempting to register " - "global fnek sig [%s]; rc = [%d]\n", - mount_crypt_stat->global_default_fnek_sig, - rc); - goto out; - } - mount_crypt_stat->flags |= - (ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES - | ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK); - break; - case ecryptfs_opt_fn_cipher: - fn_cipher_name_src = args[0].from; - strscpy(mount_crypt_stat->global_default_fn_cipher_name, - fn_cipher_name_src); - fn_cipher_name_set = 1; - break; - case ecryptfs_opt_fn_cipher_key_bytes: - fn_cipher_key_bytes_src = args[0].from; - fn_cipher_key_bytes = - (int)simple_strtol(fn_cipher_key_bytes_src, - &fn_cipher_key_bytes_src, 0); - mount_crypt_stat->global_default_fn_cipher_key_bytes = - fn_cipher_key_bytes; - fn_cipher_key_bytes_set = 1; - break; - case ecryptfs_opt_unlink_sigs: - mount_crypt_stat->flags |= ECRYPTFS_UNLINK_SIGS; - break; - case ecryptfs_opt_mount_auth_tok_only: - mount_crypt_stat->flags |= - ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY; - break; - case ecryptfs_opt_check_dev_ruid: - *check_ruid = 1; - break; - case ecryptfs_opt_err: - default: - printk(KERN_WARNING - "%s: eCryptfs: unrecognized option [%s]\n", - __func__, p); + switch (opt) { + case Opt_sig: + case Opt_ecryptfs_sig: + rc = ecryptfs_add_global_auth_tok(mount_crypt_stat, + param->string, 0); + if (rc) { + printk(KERN_ERR "Error attempting to register " + "global sig; rc = [%d]\n", rc); + return rc; + } + ctx->sig_set = 1; + break; + case Opt_cipher: + case Opt_ecryptfs_cipher: + strscpy(mount_crypt_stat->global_default_cipher_name, + param->string); + ctx->cipher_name_set = 1; + break; + case Opt_ecryptfs_key_bytes: + mount_crypt_stat->global_default_cipher_key_size = + result.uint_32; + ctx->cipher_key_bytes_set = 1; + break; + case Opt_passthrough: + mount_crypt_stat->flags |= + ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED; + break; + case Opt_xattr_metadata: + mount_crypt_stat->flags |= ECRYPTFS_XATTR_METADATA_ENABLED; + break; + case Opt_encrypted_view: + mount_crypt_stat->flags |= ECRYPTFS_XATTR_METADATA_ENABLED; + mount_crypt_stat->flags |= ECRYPTFS_ENCRYPTED_VIEW_ENABLED; + break; + case Opt_fnek_sig: + strscpy(mount_crypt_stat->global_default_fnek_sig, + param->string); + rc = ecryptfs_add_global_auth_tok( + mount_crypt_stat, + mount_crypt_stat->global_default_fnek_sig, + ECRYPTFS_AUTH_TOK_FNEK); + if (rc) { + printk(KERN_ERR "Error attempting to register " + "global fnek sig [%s]; rc = [%d]\n", + mount_crypt_stat->global_default_fnek_sig, rc); + return rc; } + mount_crypt_stat->flags |= + (ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES + | ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK); + break; + case Opt_fn_cipher: + strscpy(mount_crypt_stat->global_default_fn_cipher_name, + param->string); + ctx->fn_cipher_name_set = 1; + break; + case Opt_fn_cipher_key_bytes: + mount_crypt_stat->global_default_fn_cipher_key_bytes = + result.uint_32; + ctx->fn_cipher_key_bytes_set = 1; + break; + case Opt_unlink_sigs: + mount_crypt_stat->flags |= ECRYPTFS_UNLINK_SIGS; + break; + case Opt_mount_auth_tok_only: + mount_crypt_stat->flags |= ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY; + break; + case Opt_check_dev_ruid: + ctx->check_ruid = 1; + break; + default: + return -EINVAL; } - if (!sig_set) { + + return 0; +} + +static int ecryptfs_validate_options(struct fs_context *fc) +{ + int rc = 0; + u8 cipher_code; + struct ecryptfs_fs_context *ctx = fc->fs_private; + struct ecryptfs_sb_info *sbi = fc->s_fs_info; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat; + + + mount_crypt_stat = &sbi->mount_crypt_stat; + + if (!ctx->sig_set) { rc = -EINVAL; ecryptfs_printk(KERN_ERR, "You must supply at least one valid " "auth tok signature as a mount " "parameter; see the eCryptfs README\n"); goto out; } - if (!cipher_name_set) { + if (!ctx->cipher_name_set) { int cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER); BUG_ON(cipher_name_len > ECRYPTFS_MAX_CIPHER_NAME_SIZE); @@ -383,13 +357,13 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options, ECRYPTFS_DEFAULT_CIPHER); } if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) - && !fn_cipher_name_set) + && !ctx->fn_cipher_name_set) strcpy(mount_crypt_stat->global_default_fn_cipher_name, mount_crypt_stat->global_default_cipher_name); - if (!cipher_key_bytes_set) + if (!ctx->cipher_key_bytes_set) mount_crypt_stat->global_default_cipher_key_size = 0; if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) - && !fn_cipher_key_bytes_set) + && !ctx->fn_cipher_key_bytes_set) mount_crypt_stat->global_default_fn_cipher_key_bytes = mount_crypt_stat->global_default_cipher_key_size; @@ -453,45 +427,35 @@ struct kmem_cache *ecryptfs_sb_info_cache; static struct file_system_type ecryptfs_fs_type; /* - * ecryptfs_mount - * @fs_type: The filesystem type that the superblock should belong to - * @flags: The flags associated with the mount - * @dev_name: The path to mount over - * @raw_data: The options passed into the kernel + * ecryptfs_get_tree + * @fc: The filesystem context */ -static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) +static int ecryptfs_get_tree(struct fs_context *fc) { struct super_block *s; - struct ecryptfs_sb_info *sbi; + struct ecryptfs_fs_context *ctx = fc->fs_private; + struct ecryptfs_sb_info *sbi = fc->s_fs_info; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; struct ecryptfs_dentry_info *root_info; const char *err = "Getting sb failed"; struct inode *inode; struct path path; - uid_t check_ruid; int rc; - sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL); - if (!sbi) { - rc = -ENOMEM; - goto out; - } - - if (!dev_name) { + if (!fc->source) { rc = -EINVAL; err = "Device name cannot be null"; goto out; } - rc = ecryptfs_parse_options(sbi, raw_data, &check_ruid); + mount_crypt_stat = &sbi->mount_crypt_stat; + rc = ecryptfs_validate_options(fc); if (rc) { - err = "Error parsing options"; + err = "Error validating options"; goto out; } - mount_crypt_stat = &sbi->mount_crypt_stat; - s = sget(fs_type, NULL, set_anon_super, flags, NULL); + s = sget_fc(fc, NULL, set_anon_super_fc); if (IS_ERR(s)) { rc = PTR_ERR(s); goto out; @@ -510,7 +474,7 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags s->s_d_op = &ecryptfs_dops; err = "Reading sb failed"; - rc = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); + rc = kern_path(fc->source, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); if (rc) { ecryptfs_printk(KERN_WARNING, "kern_path() failed\n"); goto out1; @@ -529,7 +493,8 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags goto out_free; } - if (check_ruid && !uid_eq(d_inode(path.dentry)->i_uid, current_uid())) { + if (ctx->check_ruid && + !uid_eq(d_inode(path.dentry)->i_uid, current_uid())) { rc = -EPERM; printk(KERN_ERR "Mount of device (uid: %d) not owned by " "requested user (uid: %d)\n", @@ -544,7 +509,7 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags * Set the POSIX ACL flag based on whether they're enabled in the lower * mount. */ - s->s_flags = flags & ~SB_POSIXACL; + s->s_flags = fc->sb_flags & ~SB_POSIXACL; s->s_flags |= path.dentry->d_sb->s_flags & SB_POSIXACL; /** @@ -587,19 +552,19 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags root_info->lower_path = path; s->s_flags |= SB_ACTIVE; - return dget(s->s_root); + fc->root = dget(s->s_root); + return 0; out_free: path_put(&path); out1: deactivate_locked_super(s); out: - if (sbi) { + if (sbi) ecryptfs_destroy_mount_crypt_stat(&sbi->mount_crypt_stat); - kmem_cache_free(ecryptfs_sb_info_cache, sbi); - } + printk(KERN_ERR "%s; rc = [%d]\n", err, rc); - return ERR_PTR(rc); + return rc; } /** @@ -618,10 +583,54 @@ static void ecryptfs_kill_block_super(struct super_block *sb) kmem_cache_free(ecryptfs_sb_info_cache, sb_info); } +static void ecryptfs_free_fc(struct fs_context *fc) +{ + struct ecryptfs_fs_context *ctx = fc->fs_private; + struct ecryptfs_sb_info *sbi = fc->s_fs_info; + + kfree(ctx); + + if (sbi) { + ecryptfs_destroy_mount_crypt_stat(&sbi->mount_crypt_stat); + kmem_cache_free(ecryptfs_sb_info_cache, sbi); + } +} + +static const struct fs_context_operations ecryptfs_context_ops = { + .free = ecryptfs_free_fc, + .parse_param = ecryptfs_parse_param, + .get_tree = ecryptfs_get_tree, + .reconfigure = NULL, +}; + +static int ecryptfs_init_fs_context(struct fs_context *fc) +{ + struct ecryptfs_fs_context *ctx; + struct ecryptfs_sb_info *sbi = NULL; + + ctx = kzalloc(sizeof(struct ecryptfs_fs_context), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL); + if (!sbi) { + kfree(ctx); + ctx = NULL; + return -ENOMEM; + } + + ecryptfs_init_mount_crypt_stat(&sbi->mount_crypt_stat); + + fc->fs_private = ctx; + fc->s_fs_info = sbi; + fc->ops = &ecryptfs_context_ops; + return 0; +} + static struct file_system_type ecryptfs_fs_type = { .owner = THIS_MODULE, .name = "ecryptfs", - .mount = ecryptfs_mount, + .init_fs_context = ecryptfs_init_fs_context, + .parameters = ecryptfs_fs_param_spec, .kill_sb = ecryptfs_kill_block_super, .fs_flags = 0 }; diff --git a/fs/exec.c b/fs/exec.c index aaa605529a750..98cb7ba9983c7 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -883,7 +883,8 @@ EXPORT_SYMBOL(transfer_args_to_stack); */ static struct file *do_open_execat(int fd, struct filename *name, int flags) { - struct file *file; + int err; + struct file *file __free(fput) = NULL; struct open_flags open_exec_flags = { .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, .acc_mode = MAY_EXEC, @@ -908,12 +909,14 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) * an invariant that all non-regular files error out before we get here. */ if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)) || - path_noexec(&file->f_path)) { - fput(file); + path_noexec(&file->f_path)) return ERR_PTR(-EACCES); - } - return file; + err = deny_write_access(file); + if (err) + return ERR_PTR(err); + + return no_free_ptr(file); } /** @@ -923,7 +926,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) * * Returns ERR_PTR on failure or allocated struct file on success. * - * As this is a wrapper for the internal do_open_execat(). Also see + * As this is a wrapper for the internal do_open_execat(), callers + * must call allow_write_access() before fput() on release. Also see * do_close_execat(). */ struct file *open_exec(const char *name) @@ -1189,16 +1193,6 @@ static int unshare_sighand(struct task_struct *me) return 0; } -char *__get_task_comm(char *buf, size_t buf_size, struct task_struct *tsk) -{ - task_lock(tsk); - /* Always NUL terminated and zero-padded */ - strscpy_pad(buf, tsk->comm, buf_size); - task_unlock(tsk); - return buf; -} -EXPORT_SYMBOL_GPL(__get_task_comm); - /* * These functions flushes out all traces of the currently running executable * so that a new one can be started @@ -1475,8 +1469,10 @@ static int prepare_bprm_creds(struct linux_binprm *bprm) /* Matches do_open_execat() */ static void do_close_execat(struct file *file) { - if (file) - fput(file); + if (!file) + return; + allow_write_access(file); + fput(file); } static void free_bprm(struct linux_binprm *bprm) @@ -1801,6 +1797,7 @@ static int exec_binprm(struct linux_binprm *bprm) bprm->file = bprm->interpreter; bprm->interpreter = NULL; + allow_write_access(exec); if (unlikely(bprm->have_execfd)) { if (bprm->executable) { fput(exec); diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 7446bf09a04a8..fe0a9b8a0cd07 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -82,11 +82,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent if (ei->type != TYPE_DIR) return -EPERM; - if (ei->entry == -1) - exfat_chain_set(&dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - else - exfat_chain_set(&dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + exfat_chain_set(&dir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); dentries_per_clu = sbi->dentries_per_clu; max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES, @@ -135,21 +132,6 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent num_ext = ep->dentry.file.num_ext; dir_entry->attr = le16_to_cpu(ep->dentry.file.attr); - exfat_get_entry_time(sbi, &dir_entry->crtime, - ep->dentry.file.create_tz, - ep->dentry.file.create_time, - ep->dentry.file.create_date, - ep->dentry.file.create_time_cs); - exfat_get_entry_time(sbi, &dir_entry->mtime, - ep->dentry.file.modify_tz, - ep->dentry.file.modify_time, - ep->dentry.file.modify_date, - ep->dentry.file.modify_time_cs); - exfat_get_entry_time(sbi, &dir_entry->atime, - ep->dentry.file.access_tz, - ep->dentry.file.access_time, - ep->dentry.file.access_date, - 0); *uni_name.name = 0x0; err = exfat_get_uniname_from_ext_entry(sb, &clu, i, @@ -166,9 +148,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent ep = exfat_get_dentry(sb, &clu, i + 1, &bh); if (!ep) return -EIO; - dir_entry->size = - le64_to_cpu(ep->dentry.stream.valid_size); - dir_entry->entry = dentry; + dir_entry->entry = i; + dir_entry->dir = clu; brelse(bh); ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi); @@ -276,7 +257,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx) if (!nb->lfn[0]) goto end_of_dir; - i_pos = ((loff_t)ei->start_clu << 32) | (de.entry & 0xffffffff); + i_pos = ((loff_t)de.dir.dir << 32) | (de.entry & 0xffffffff); tmp = exfat_iget(sb, i_pos); if (tmp) { inum = tmp->i_ino; diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 3cdc1de362a94..78be6964a8a08 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -204,7 +204,9 @@ struct exfat_entry_set_cache { #define IS_DYNAMIC_ES(es) ((es)->__bh != (es)->bh) struct exfat_dir_entry { + /* the cluster where file dentry is located */ struct exfat_chain dir; + /* the index of file dentry in ->dir */ int entry; unsigned int type; unsigned int start_clu; @@ -290,7 +292,9 @@ struct exfat_sb_info { * EXFAT file system inode in-memory data */ struct exfat_inode_info { + /* the cluster where file dentry is located */ struct exfat_chain dir; + /* the index of file dentry in ->dir */ int entry; unsigned int type; unsigned short attr; @@ -508,6 +512,8 @@ struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es, int exfat_get_dentry_set(struct exfat_entry_set_cache *es, struct super_block *sb, struct exfat_chain *p_dir, int entry, unsigned int num_entries); +#define exfat_get_dentry_set_by_ei(es, sb, ei) \ + exfat_get_dentry_set(es, sb, &(ei)->dir, (ei)->entry, ES_ALL_ENTRIES) int exfat_get_empty_dentry_set(struct exfat_entry_set_cache *es, struct super_block *sb, struct exfat_chain *p_dir, int entry, unsigned int num_entries); diff --git a/fs/exfat/file.c b/fs/exfat/file.c index a25d7eb789f4c..fb38769c3e39d 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -584,6 +584,16 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) if (ret < 0) goto unlock; + if (iocb->ki_flags & IOCB_DIRECT) { + unsigned long align = pos | iov_iter_alignment(iter); + + if (!IS_ALIGNED(align, i_blocksize(inode)) && + !IS_ALIGNED(align, bdev_logical_block_size(inode->i_sb->s_bdev))) { + ret = -EINVAL; + goto unlock; + } + } + if (pos > valid_size) { ret = exfat_extend_valid_size(file, pos); if (ret < 0 && ret != -ENOSPC) { diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index d724de8f57bf9..96952d4acb500 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -43,7 +43,7 @@ int __exfat_write_inode(struct inode *inode, int sync) exfat_set_volume_dirty(sb); /* get the directory entry of given file or directory */ - if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES)) + if (exfat_get_dentry_set_by_ei(&es, sb, ei)) return -EIO; ep = exfat_get_dentry_cached(&es, ES_IDX_FILE); ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM); diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 2c4c442293529..97d2774760fe3 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -288,8 +288,22 @@ static int exfat_check_max_dentries(struct inode *inode) return 0; } -/* find empty directory entry. - * if there isn't any empty slot, expand cluster chain. +/* + * Find an empty directory entry set. + * + * If there isn't any empty slot, expand cluster chain. + * + * in: + * inode: inode of the parent directory + * num_entries: specifies how many dentries in the empty directory entry set + * + * out: + * p_dir: the cluster where the empty directory entry set is located + * es: The found empty directory entry set + * + * return: + * the directory entry index in p_dir is returned on succeeds + * -error code is returned on failure */ static int exfat_find_empty_entry(struct inode *inode, struct exfat_chain *p_dir, int num_entries, @@ -311,6 +325,9 @@ static int exfat_find_empty_entry(struct inode *inode, ei->hint_femp.eidx = EXFAT_HINT_NONE; } + exfat_chain_set(p_dir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, num_entries, es)) < 0) { if (dentry == -EIO) @@ -345,6 +362,7 @@ static int exfat_find_empty_entry(struct inode *inode, if (ei->start_clu == EXFAT_EOF_CLUSTER) { ei->start_clu = clu.dir; p_dir->dir = clu.dir; + hint_femp.eidx = 0; } /* append to the FAT chain */ @@ -377,7 +395,10 @@ static int exfat_find_empty_entry(struct inode *inode, inode->i_blocks += sbi->cluster_size >> 9; } - return dentry; + p_dir->dir = exfat_sector_to_cluster(sbi, es->bh[0]->b_blocknr); + p_dir->size -= dentry / sbi->dentries_per_clu; + + return dentry & (sbi->dentries_per_clu - 1); } /* @@ -385,14 +406,11 @@ static int exfat_find_empty_entry(struct inode *inode, * Zero if it was successful; otherwise nonzero. */ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path, - struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, - int lookup) + struct exfat_uni_name *p_uniname, int lookup) { int namelen; int lossy = NLS_NAME_NO_LOSSY; struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); int pathlen = strlen(path); /* @@ -431,24 +449,19 @@ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path, if ((lossy && !lookup) || !namelen) return (lossy & NLS_NAME_OVERLEN) ? -ENAMETOOLONG : -EINVAL; - exfat_chain_set(p_dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); - return 0; } static inline int exfat_resolve_path(struct inode *inode, - const unsigned char *path, struct exfat_chain *dir, - struct exfat_uni_name *uni) + const unsigned char *path, struct exfat_uni_name *uni) { - return __exfat_resolve_path(inode, path, dir, uni, 0); + return __exfat_resolve_path(inode, path, uni, 0); } static inline int exfat_resolve_path_for_lookup(struct inode *inode, - const unsigned char *path, struct exfat_chain *dir, - struct exfat_uni_name *uni) + const unsigned char *path, struct exfat_uni_name *uni) { - return __exfat_resolve_path(inode, path, dir, uni, 1); + return __exfat_resolve_path(inode, path, uni, 1); } static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info) @@ -457,8 +470,7 @@ static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info) } static int exfat_add_entry(struct inode *inode, const char *path, - struct exfat_chain *p_dir, unsigned int type, - struct exfat_dir_entry *info) + unsigned int type, struct exfat_dir_entry *info) { int ret, dentry, num_entries; struct super_block *sb = inode->i_sb; @@ -470,7 +482,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, int clu_size = 0; unsigned int start_clu = EXFAT_FREE_CLUSTER; - ret = exfat_resolve_path(inode, path, p_dir, &uniname); + ret = exfat_resolve_path(inode, path, &uniname); if (ret) goto out; @@ -481,7 +493,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, } /* exfat_find_empty_entry must be called before alloc_cluster() */ - dentry = exfat_find_empty_entry(inode, p_dir, num_entries, &es); + dentry = exfat_find_empty_entry(inode, &info->dir, num_entries, &es); if (dentry < 0) { ret = dentry; /* -EIO or -ENOSPC */ goto out; @@ -508,7 +520,6 @@ static int exfat_add_entry(struct inode *inode, const char *path, if (ret) goto out; - info->dir = *p_dir; info->entry = dentry; info->flags = ALLOC_NO_FAT_CHAIN; info->type = type; @@ -541,7 +552,6 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir, { struct super_block *sb = dir->i_sb; struct inode *inode; - struct exfat_chain cdir; struct exfat_dir_entry info; loff_t i_pos; int err; @@ -552,8 +562,7 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir, mutex_lock(&EXFAT_SB(sb)->s_lock); exfat_set_volume_dirty(sb); - err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE, - &info); + err = exfat_add_entry(dir, dentry->d_name.name, TYPE_FILE, &info); if (err) goto unlock; @@ -601,10 +610,13 @@ static int exfat_find(struct inode *dir, struct qstr *qname, return -ENOENT; /* check the validity of directory name in the given pathname */ - ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name); + ret = exfat_resolve_path_for_lookup(dir, qname->name, &uni_name); if (ret) return ret; + exfat_chain_set(&cdir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(dir), sbi), ei->flags); + /* check the validation of hint_stat and initialize it if required */ if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { ei->hint_stat.clu = cdir.dir; @@ -618,15 +630,16 @@ static int exfat_find(struct inode *dir, struct qstr *qname, if (dentry < 0) return dentry; /* -error value */ - info->dir = cdir; - info->entry = dentry; - info->num_subdirs = 0; - /* adjust cdir to the optimized value */ cdir.dir = hint_opt.clu; if (cdir.flags & ALLOC_NO_FAT_CHAIN) cdir.size -= dentry / sbi->dentries_per_clu; dentry = hint_opt.eidx; + + info->dir = cdir; + info->entry = dentry; + info->num_subdirs = 0; + if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES)) return -EIO; ep = exfat_get_dentry_cached(&es, ES_IDX_FILE); @@ -637,14 +650,26 @@ static int exfat_find(struct inode *dir, struct qstr *qname, info->size = le64_to_cpu(ep2->dentry.stream.valid_size); info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size); info->size = le64_to_cpu(ep2->dentry.stream.size); + + info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu); + if (!is_valid_cluster(sbi, info->start_clu) && info->size) { + exfat_warn(sb, "start_clu is invalid cluster(0x%x)", + info->start_clu); + info->size = 0; + info->valid_size = 0; + } + + if (info->valid_size > info->size) { + exfat_warn(sb, "valid_size(%lld) is greater than size(%lld)", + info->valid_size, info->size); + info->valid_size = info->size; + } + if (info->size == 0) { info->flags = ALLOC_NO_FAT_CHAIN; info->start_clu = EXFAT_EOF_CLUSTER; - } else { + } else info->flags = ep2->dentry.stream.flags; - info->start_clu = - le32_to_cpu(ep2->dentry.stream.start_clu); - } exfat_get_entry_time(sbi, &info->crtime, ep->dentry.file.create_tz, @@ -766,26 +791,23 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, /* remove an entry, BUT don't truncate */ static int exfat_unlink(struct inode *dir, struct dentry *dentry) { - struct exfat_chain cdir; struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; struct exfat_inode_info *ei = EXFAT_I(inode); struct exfat_entry_set_cache es; - int entry, err = 0; + int err = 0; if (unlikely(exfat_forced_shutdown(sb))) return -EIO; mutex_lock(&EXFAT_SB(sb)->s_lock); - exfat_chain_dup(&cdir, &ei->dir); - entry = ei->entry; if (ei->dir.dir == DIR_DELETED) { exfat_err(sb, "abnormal access to deleted dentry"); err = -ENOENT; goto unlock; } - err = exfat_get_dentry_set(&es, sb, &cdir, entry, ES_ALL_ENTRIES); + err = exfat_get_dentry_set_by_ei(&es, sb, ei); if (err) { err = -EIO; goto unlock; @@ -824,7 +846,6 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct super_block *sb = dir->i_sb; struct inode *inode; struct exfat_dir_entry info; - struct exfat_chain cdir; loff_t i_pos; int err; loff_t size = i_size_read(dir); @@ -834,8 +855,7 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir, mutex_lock(&EXFAT_SB(sb)->s_lock); exfat_set_volume_dirty(sb); - err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR, - &info); + err = exfat_add_entry(dir, dentry->d_name.name, TYPE_DIR, &info); if (err) goto unlock; @@ -915,21 +935,18 @@ static int exfat_check_dir_empty(struct super_block *sb, static int exfat_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - struct exfat_chain cdir, clu_to_free; + struct exfat_chain clu_to_free; struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); struct exfat_entry_set_cache es; - int entry, err; + int err; if (unlikely(exfat_forced_shutdown(sb))) return -EIO; mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); - exfat_chain_dup(&cdir, &ei->dir); - entry = ei->entry; - if (ei->dir.dir == DIR_DELETED) { exfat_err(sb, "abnormal access to deleted dentry"); err = -ENOENT; @@ -947,7 +964,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) goto unlock; } - err = exfat_get_dentry_set(&es, sb, &cdir, entry, ES_ALL_ENTRIES); + err = exfat_get_dentry_set_by_ei(&es, sb, ei); if (err) { err = -EIO; goto unlock; @@ -982,15 +999,14 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) return err; } -static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, - int oldentry, struct exfat_uni_name *p_uniname, - struct exfat_inode_info *ei) +static int exfat_rename_file(struct inode *parent_inode, + struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { int ret, num_new_entries; struct exfat_dentry *epold, *epnew; - struct super_block *sb = inode->i_sb; + struct super_block *sb = parent_inode->i_sb; struct exfat_entry_set_cache old_es, new_es; - int sync = IS_DIRSYNC(inode); + int sync = IS_DIRSYNC(parent_inode); if (unlikely(exfat_forced_shutdown(sb))) return -EIO; @@ -999,7 +1015,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, if (num_new_entries < 0) return num_new_entries; - ret = exfat_get_dentry_set(&old_es, sb, p_dir, oldentry, ES_ALL_ENTRIES); + ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei); if (ret) { ret = -EIO; return ret; @@ -1009,9 +1025,10 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, if (old_es.num_entries < num_new_entries) { int newentry; + struct exfat_chain dir; - newentry = exfat_find_empty_entry(inode, p_dir, num_new_entries, - &new_es); + newentry = exfat_find_empty_entry(parent_inode, &dir, + num_new_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_old_es; @@ -1034,8 +1051,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, if (ret) goto put_old_es; - exfat_remove_entries(inode, &old_es, ES_IDX_FILE); - ei->dir = *p_dir; + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE); + ei->dir = dir; ei->entry = newentry; } else { if (exfat_get_entry_type(epold) == TYPE_FILE) { @@ -1043,7 +1060,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, ei->attr |= EXFAT_ATTR_ARCHIVE; } - exfat_remove_entries(inode, &old_es, ES_IDX_FIRST_FILENAME + 1); + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FIRST_FILENAME + 1); exfat_init_ext_entry(&old_es, num_new_entries, p_uniname); } return exfat_put_dentry_set(&old_es, sync); @@ -1053,26 +1070,24 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, return ret; } -static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir, - int oldentry, struct exfat_chain *p_newdir, +static int exfat_move_file(struct inode *parent_inode, struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { int ret, newentry, num_new_entries; struct exfat_dentry *epmov, *epnew; - struct super_block *sb = inode->i_sb; struct exfat_entry_set_cache mov_es, new_es; + struct exfat_chain newdir; num_new_entries = exfat_calc_num_entries(p_uniname); if (num_new_entries < 0) return num_new_entries; - ret = exfat_get_dentry_set(&mov_es, sb, p_olddir, oldentry, - ES_ALL_ENTRIES); + ret = exfat_get_dentry_set_by_ei(&mov_es, parent_inode->i_sb, ei); if (ret) return -EIO; - newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries, - &new_es); + newentry = exfat_find_empty_entry(parent_inode, &newdir, + num_new_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_mov_es; @@ -1091,18 +1106,16 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir, *epnew = *epmov; exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); - exfat_remove_entries(inode, &mov_es, ES_IDX_FILE); - - exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size, - p_newdir->flags); + exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE); + ei->dir = newdir; ei->entry = newentry; - ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(inode)); + ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode)); if (ret) goto put_mov_es; - return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(inode)); + return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode)); put_mov_es: exfat_put_dentry_set(&mov_es, false); @@ -1116,19 +1129,12 @@ static int __exfat_rename(struct inode *old_parent_inode, struct dentry *new_dentry) { int ret; - int dentry; - struct exfat_chain olddir, newdir; - struct exfat_chain *p_dir = NULL; struct exfat_uni_name uni_name; - struct exfat_dentry *ep; struct super_block *sb = old_parent_inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); const unsigned char *new_path = new_dentry->d_name.name; struct inode *new_inode = new_dentry->d_inode; struct exfat_inode_info *new_ei = NULL; - unsigned int new_entry_type = TYPE_UNUSED; - int new_entry = 0; - struct buffer_head *new_bh = NULL; /* check the validity of pointer parameters */ if (new_path == NULL || strlen(new_path) == 0) @@ -1139,11 +1145,6 @@ static int __exfat_rename(struct inode *old_parent_inode, return -ENOENT; } - exfat_chain_set(&olddir, EXFAT_I(old_parent_inode)->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(old_parent_inode), sbi), - EXFAT_I(old_parent_inode)->flags); - dentry = ei->entry; - /* check whether new dir is existing directory and empty */ if (new_inode) { ret = -EIO; @@ -1154,17 +1155,8 @@ static int __exfat_rename(struct inode *old_parent_inode, goto out; } - p_dir = &(new_ei->dir); - new_entry = new_ei->entry; - ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh); - if (!ep) - goto out; - - new_entry_type = exfat_get_entry_type(ep); - brelse(new_bh); - /* if new_inode exists, update ei */ - if (new_entry_type == TYPE_DIR) { + if (S_ISDIR(new_inode->i_mode)) { struct exfat_chain new_clu; new_clu.dir = new_ei->start_clu; @@ -1180,26 +1172,22 @@ static int __exfat_rename(struct inode *old_parent_inode, } /* check the validity of directory name in the given new pathname */ - ret = exfat_resolve_path(new_parent_inode, new_path, &newdir, - &uni_name); + ret = exfat_resolve_path(new_parent_inode, new_path, &uni_name); if (ret) goto out; exfat_set_volume_dirty(sb); - if (olddir.dir == newdir.dir) - ret = exfat_rename_file(new_parent_inode, &olddir, dentry, - &uni_name, ei); + if (new_parent_inode == old_parent_inode) + ret = exfat_rename_file(new_parent_inode, &uni_name, ei); else - ret = exfat_move_file(new_parent_inode, &olddir, dentry, - &newdir, &uni_name, ei); + ret = exfat_move_file(new_parent_inode, &uni_name, ei); if (!ret && new_inode) { struct exfat_entry_set_cache es; /* delete entries of new_dir */ - ret = exfat_get_dentry_set(&es, sb, p_dir, new_entry, - ES_ALL_ENTRIES); + ret = exfat_get_dentry_set_by_ei(&es, sb, new_ei); if (ret) { ret = -EIO; goto del_out; @@ -1212,7 +1200,7 @@ static int __exfat_rename(struct inode *old_parent_inode, goto del_out; /* Free the clusters if new_inode is a dir(as if exfat_rmdir) */ - if (new_entry_type == TYPE_DIR && + if (S_ISDIR(new_inode->i_mode) && new_ei->start_clu != EXFAT_EOF_CLUSTER) { /* new_ei, new_clu_to_free */ struct exfat_chain new_clu_to_free; diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 4f2dd4ab44864..0c899cfba578a 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -382,14 +382,24 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid, int *max_len, struct inode *parent, int flags) { const struct export_operations *nop = inode->i_sb->s_export_op; + enum fid_type type; if (!exportfs_can_encode_fh(nop, flags)) return -EOPNOTSUPP; if (!nop && (flags & EXPORT_FH_FID)) - return exportfs_encode_ino64_fid(inode, fid, max_len); + type = exportfs_encode_ino64_fid(inode, fid, max_len); + else + type = nop->encode_fh(inode, fid->raw, max_len, parent); + + if (type > 0 && FILEID_USER_FLAGS(type)) { + pr_warn_once("%s: unexpected fh type value 0x%x from fstype %s.\n", + __func__, type, inode->i_sb->s_type->name); + return -EINVAL; + } + + return type; - return nop->encode_fh(inode, fid->raw, max_len, parent); } EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh); @@ -436,6 +446,9 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, char nbuf[NAME_MAX+1]; int err; + if (fileid_type < 0 || FILEID_USER_FLAGS(fileid_type)) + return ERR_PTR(-EINVAL); + /* * Try to get any dentry for the given file handle from the filesystem. */ diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 8bffdeccdbc3c..1fbc0607363bd 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -296,9 +296,8 @@ static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl, struct posix_acl *clone = NULL; if (acl) { - int size = sizeof(struct posix_acl) + acl->a_count * - sizeof(struct posix_acl_entry); - clone = kmemdup(acl, size, flags); + clone = kmemdup(acl, struct_size(acl, a_entries, acl->a_count), + flags); if (clone) refcount_set(&clone->a_refcount, 1); } diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 7f76460b721f2..efda9a0229816 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -32,7 +32,7 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io, f2fs_build_fault_attr(sbi, 0, 0); if (!end_io) f2fs_flush_merged_writes(sbi); - f2fs_handle_critical_error(sbi, reason, end_io); + f2fs_handle_critical_error(sbi, reason); } /* diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index e3ce763cce18f..a2478c2afb3a0 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1679,7 +1679,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag) /* reserved delalloc block should be mapped for fiemap. */ if (blkaddr == NEW_ADDR) map->m_flags |= F2FS_MAP_DELALLOC; - if (flag != F2FS_GET_BLOCK_DIO || !is_hole) + /* DIO READ and hole case, should not map the blocks. */ + if (!(flag == F2FS_GET_BLOCK_DIO && is_hole && !map->m_may_create)) map->m_flags |= F2FS_MAP_MAPPED; map->m_pblk = blkaddr; @@ -1821,16 +1822,6 @@ bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len) return true; } -static inline u64 bytes_to_blks(struct inode *inode, u64 bytes) -{ - return (bytes >> inode->i_blkbits); -} - -static inline u64 blks_to_bytes(struct inode *inode, u64 blks) -{ - return (blks << inode->i_blkbits); -} - static int f2fs_xattr_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo) { @@ -1856,7 +1847,7 @@ static int f2fs_xattr_fiemap(struct inode *inode, return err; } - phys = blks_to_bytes(inode, ni.blk_addr); + phys = F2FS_BLK_TO_BYTES(ni.blk_addr); offset = offsetof(struct f2fs_inode, i_addr) + sizeof(__le32) * (DEF_ADDRS_PER_INODE - get_inline_xattr_addrs(inode)); @@ -1888,7 +1879,7 @@ static int f2fs_xattr_fiemap(struct inode *inode, return err; } - phys = blks_to_bytes(inode, ni.blk_addr); + phys = F2FS_BLK_TO_BYTES(ni.blk_addr); len = inode->i_sb->s_blocksize; f2fs_put_page(page, 1); @@ -1904,30 +1895,11 @@ static int f2fs_xattr_fiemap(struct inode *inode, return (err < 0 ? err : 0); } -static loff_t max_inode_blocks(struct inode *inode) -{ - loff_t result = ADDRS_PER_INODE(inode); - loff_t leaf_count = ADDRS_PER_BLOCK(inode); - - /* two direct node blocks */ - result += (leaf_count * 2); - - /* two indirect node blocks */ - leaf_count *= NIDS_PER_BLOCK; - result += (leaf_count * 2); - - /* one double indirect node block */ - leaf_count *= NIDS_PER_BLOCK; - result += leaf_count; - - return result; -} - int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { struct f2fs_map_blocks map; - sector_t start_blk, last_blk; + sector_t start_blk, last_blk, blk_len, max_len; pgoff_t next_pgofs; u64 logical = 0, phys = 0, size = 0; u32 flags = 0; @@ -1969,16 +1941,15 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, goto out; } - if (bytes_to_blks(inode, len) == 0) - len = blks_to_bytes(inode, 1); - - start_blk = bytes_to_blks(inode, start); - last_blk = bytes_to_blks(inode, start + len - 1); + start_blk = F2FS_BYTES_TO_BLK(start); + last_blk = F2FS_BYTES_TO_BLK(start + len - 1); + blk_len = last_blk - start_blk + 1; + max_len = F2FS_BYTES_TO_BLK(maxbytes) - start_blk; next: memset(&map, 0, sizeof(map)); map.m_lblk = start_blk; - map.m_len = bytes_to_blks(inode, len); + map.m_len = blk_len; map.m_next_pgofs = &next_pgofs; map.m_seg_type = NO_CHECK_TYPE; @@ -1995,13 +1966,23 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, if (!compr_cluster && !(map.m_flags & F2FS_MAP_FLAGS)) { start_blk = next_pgofs; - if (blks_to_bytes(inode, start_blk) < blks_to_bytes(inode, - max_inode_blocks(inode))) + if (F2FS_BLK_TO_BYTES(start_blk) < maxbytes) goto prep_next; flags |= FIEMAP_EXTENT_LAST; } + /* + * current extent may cross boundary of inquiry, increase len to + * requery. + */ + if (!compr_cluster && (map.m_flags & F2FS_MAP_MAPPED) && + map.m_lblk + map.m_len - 1 == last_blk && + blk_len != max_len) { + blk_len = max_len; + goto next; + } + compr_appended = false; /* In a case of compressed cluster, append this to the last extent */ if (compr_cluster && ((map.m_flags & F2FS_MAP_DELALLOC) || @@ -2033,14 +2014,14 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, } else if (compr_appended) { unsigned int appended_blks = cluster_size - count_in_cluster + 1; - size += blks_to_bytes(inode, appended_blks); + size += F2FS_BLK_TO_BYTES(appended_blks); start_blk += appended_blks; compr_cluster = false; } else { - logical = blks_to_bytes(inode, start_blk); + logical = F2FS_BLK_TO_BYTES(start_blk); phys = __is_valid_data_blkaddr(map.m_pblk) ? - blks_to_bytes(inode, map.m_pblk) : 0; - size = blks_to_bytes(inode, map.m_len); + F2FS_BLK_TO_BYTES(map.m_pblk) : 0; + size = F2FS_BLK_TO_BYTES(map.m_len); flags = 0; if (compr_cluster) { @@ -2048,13 +2029,13 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, count_in_cluster += map.m_len; if (count_in_cluster == cluster_size) { compr_cluster = false; - size += blks_to_bytes(inode, 1); + size += F2FS_BLKSIZE; } } else if (map.m_flags & F2FS_MAP_DELALLOC) { flags = FIEMAP_EXTENT_UNWRITTEN; } - start_blk += bytes_to_blks(inode, size); + start_blk += F2FS_BYTES_TO_BLK(size); } prep_next: @@ -2092,7 +2073,7 @@ static int f2fs_read_single_page(struct inode *inode, struct folio *folio, struct readahead_control *rac) { struct bio *bio = *bio_ret; - const unsigned blocksize = blks_to_bytes(inode, 1); + const unsigned int blocksize = F2FS_BLKSIZE; sector_t block_in_file; sector_t last_block; sector_t last_block_in_file; @@ -2102,8 +2083,8 @@ static int f2fs_read_single_page(struct inode *inode, struct folio *folio, block_in_file = (sector_t)index; last_block = block_in_file + nr_pages; - last_block_in_file = bytes_to_blks(inode, - f2fs_readpage_limit(inode) + blocksize - 1); + last_block_in_file = F2FS_BYTES_TO_BLK(f2fs_readpage_limit(inode) + + blocksize - 1); if (last_block > last_block_in_file) last_block = last_block_in_file; @@ -2203,7 +2184,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, struct bio *bio = *bio_ret; unsigned int start_idx = cc->cluster_idx << cc->log_cluster_size; sector_t last_block_in_file; - const unsigned blocksize = blks_to_bytes(inode, 1); + const unsigned int blocksize = F2FS_BLKSIZE; struct decompress_io_ctx *dic = NULL; struct extent_info ei = {}; bool from_dnode = true; @@ -2212,8 +2193,8 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, f2fs_bug_on(sbi, f2fs_cluster_is_empty(cc)); - last_block_in_file = bytes_to_blks(inode, - f2fs_readpage_limit(inode) + blocksize - 1); + last_block_in_file = F2FS_BYTES_TO_BLK(f2fs_readpage_limit(inode) + + blocksize - 1); /* get rid of pages beyond EOF */ for (i = 0; i < cc->cluster_size; i++) { @@ -2388,10 +2369,10 @@ static int f2fs_mpage_readpages(struct inode *inode, .nr_cpages = 0, }; pgoff_t nc_cluster_idx = NULL_CLUSTER; + pgoff_t index; #endif unsigned nr_pages = rac ? readahead_count(rac) : 1; unsigned max_nr_pages = nr_pages; - pgoff_t index; int ret = 0; map.m_pblk = 0; @@ -2409,9 +2390,9 @@ static int f2fs_mpage_readpages(struct inode *inode, prefetchw(&folio->flags); } +#ifdef CONFIG_F2FS_FS_COMPRESSION index = folio_index(folio); -#ifdef CONFIG_F2FS_FS_COMPRESSION if (!f2fs_compressed_file(inode)) goto read_single_page; @@ -3444,6 +3425,11 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, if (!f2fs_lookup_read_extent_cache_block(inode, index, &dn.data_blkaddr)) { + if (IS_DEVICE_ALIASING(inode)) { + err = -ENODATA; + goto out; + } + if (locked) { err = f2fs_reserve_block(&dn, index); goto out; @@ -3974,7 +3960,7 @@ static int check_swap_activate(struct swap_info_struct *sis, * to be very smart. */ cur_lblock = 0; - last_lblock = bytes_to_blks(inode, i_size_read(inode)); + last_lblock = F2FS_BYTES_TO_BLK(i_size_read(inode)); while (cur_lblock < last_lblock && cur_lblock < sis->max) { struct f2fs_map_blocks map; @@ -4217,8 +4203,8 @@ static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, pgoff_t next_pgofs = 0; int err; - map.m_lblk = bytes_to_blks(inode, offset); - map.m_len = bytes_to_blks(inode, offset + length - 1) - map.m_lblk + 1; + map.m_lblk = F2FS_BYTES_TO_BLK(offset); + map.m_len = F2FS_BYTES_TO_BLK(offset + length - 1) - map.m_lblk + 1; map.m_next_pgofs = &next_pgofs; map.m_seg_type = f2fs_rw_hint_to_seg_type(F2FS_I_SB(inode), inode->i_write_hint); @@ -4229,7 +4215,7 @@ static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (err) return err; - iomap->offset = blks_to_bytes(inode, map.m_lblk); + iomap->offset = F2FS_BLK_TO_BYTES(map.m_lblk); /* * When inline encryption is enabled, sometimes I/O to an encrypted file @@ -4249,21 +4235,21 @@ static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (WARN_ON_ONCE(map.m_pblk == NEW_ADDR)) return -EINVAL; - iomap->length = blks_to_bytes(inode, map.m_len); + iomap->length = F2FS_BLK_TO_BYTES(map.m_len); iomap->type = IOMAP_MAPPED; iomap->flags |= IOMAP_F_MERGED; iomap->bdev = map.m_bdev; - iomap->addr = blks_to_bytes(inode, map.m_pblk); + iomap->addr = F2FS_BLK_TO_BYTES(map.m_pblk); } else { if (flags & IOMAP_WRITE) return -ENOTBLK; if (map.m_pblk == NULL_ADDR) { - iomap->length = blks_to_bytes(inode, next_pgofs) - - iomap->offset; + iomap->length = F2FS_BLK_TO_BYTES(next_pgofs) - + iomap->offset; iomap->type = IOMAP_HOLE; } else if (map.m_pblk == NEW_ADDR) { - iomap->length = blks_to_bytes(inode, map.m_len); + iomap->length = F2FS_BLK_TO_BYTES(map.m_len); iomap->type = IOMAP_UNWRITTEN; } else { f2fs_bug_on(F2FS_I_SB(inode), 1); diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 546b8ba912613..468828288a4a8 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -60,6 +60,70 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi) } #ifdef CONFIG_DEBUG_FS +static void update_multidevice_stats(struct f2fs_sb_info *sbi) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + struct f2fs_dev_stats *dev_stats = si->dev_stats; + int i, j; + + if (!f2fs_is_multi_device(sbi)) + return; + + memset(dev_stats, 0, sizeof(struct f2fs_dev_stats) * sbi->s_ndevs); + for (i = 0; i < sbi->s_ndevs; i++) { + unsigned int start_segno, end_segno; + block_t start_blk, end_blk; + + if (i == 0) { + start_blk = MAIN_BLKADDR(sbi); + end_blk = FDEV(i).end_blk + 1 - SEG0_BLKADDR(sbi); + } else { + start_blk = FDEV(i).start_blk; + end_blk = FDEV(i).end_blk + 1; + } + + start_segno = GET_SEGNO(sbi, start_blk); + end_segno = GET_SEGNO(sbi, end_blk); + + for (j = start_segno; j < end_segno; j++) { + unsigned int seg_blks, sec_blks; + + seg_blks = get_seg_entry(sbi, j)->valid_blocks; + + /* update segment stats */ + if (IS_CURSEG(sbi, j)) + dev_stats[i].devstats[0][DEVSTAT_INUSE]++; + else if (seg_blks == BLKS_PER_SEG(sbi)) + dev_stats[i].devstats[0][DEVSTAT_FULL]++; + else if (seg_blks != 0) + dev_stats[i].devstats[0][DEVSTAT_DIRTY]++; + else if (!test_bit(j, FREE_I(sbi)->free_segmap)) + dev_stats[i].devstats[0][DEVSTAT_FREE]++; + else + dev_stats[i].devstats[0][DEVSTAT_PREFREE]++; + + if (!__is_large_section(sbi) || + (j % SEGS_PER_SEC(sbi)) != 0) + continue; + + sec_blks = get_sec_entry(sbi, j)->valid_blocks; + + /* update section stats */ + if (IS_CURSEC(sbi, GET_SEC_FROM_SEG(sbi, j))) + dev_stats[i].devstats[1][DEVSTAT_INUSE]++; + else if (sec_blks == BLKS_PER_SEC(sbi)) + dev_stats[i].devstats[1][DEVSTAT_FULL]++; + else if (sec_blks != 0) + dev_stats[i].devstats[1][DEVSTAT_DIRTY]++; + else if (!test_bit(GET_SEC_FROM_SEG(sbi, j), + FREE_I(sbi)->free_secmap)) + dev_stats[i].devstats[1][DEVSTAT_FREE]++; + else + dev_stats[i].devstats[1][DEVSTAT_PREFREE]++; + } + } +} + static void update_general_status(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = F2FS_STAT(sbi); @@ -214,6 +278,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->valid_blks[type] += blks; } + update_multidevice_stats(sbi); + for (i = 0; i < MAX_CALL_TYPE; i++) si->cp_call_count[i] = atomic_read(&sbi->cp_call_count[i]); @@ -498,6 +564,36 @@ static int stat_show(struct seq_file *s, void *v) si->dirty_count); seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n", si->prefree_count, si->free_segs, si->free_secs); + if (f2fs_is_multi_device(sbi)) { + seq_puts(s, "Multidevice stats:\n"); + seq_printf(s, " [seg: %8s %8s %8s %8s %8s]", + "inuse", "dirty", "full", "free", "prefree"); + if (__is_large_section(sbi)) + seq_printf(s, " [sec: %8s %8s %8s %8s %8s]\n", + "inuse", "dirty", "full", "free", "prefree"); + else + seq_puts(s, "\n"); + + for (i = 0; i < sbi->s_ndevs; i++) { + seq_printf(s, " #%-2d %8u %8u %8u %8u %8u", i, + si->dev_stats[i].devstats[0][DEVSTAT_INUSE], + si->dev_stats[i].devstats[0][DEVSTAT_DIRTY], + si->dev_stats[i].devstats[0][DEVSTAT_FULL], + si->dev_stats[i].devstats[0][DEVSTAT_FREE], + si->dev_stats[i].devstats[0][DEVSTAT_PREFREE]); + if (!__is_large_section(sbi)) { + seq_puts(s, "\n"); + continue; + } + seq_printf(s, " %8u %8u %8u %8u %8u\n", + si->dev_stats[i].devstats[1][DEVSTAT_INUSE], + si->dev_stats[i].devstats[1][DEVSTAT_DIRTY], + si->dev_stats[i].devstats[1][DEVSTAT_FULL], + si->dev_stats[i].devstats[1][DEVSTAT_FREE], + si->dev_stats[i].devstats[1][DEVSTAT_PREFREE]); + } + seq_puts(s, "\n"); + } seq_printf(s, "CP calls: %d (BG: %d)\n", si->cp_call_count[TOTAL_CALL], si->cp_call_count[BACKGROUND]); @@ -598,9 +694,9 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n", si->ndirty_dent, si->ndirty_dirs, si->ndirty_all); - seq_printf(s, " - datas: %4d in files:%4d\n", + seq_printf(s, " - data: %4d in files:%4d\n", si->ndirty_data, si->ndirty_files); - seq_printf(s, " - quota datas: %4d in quota files:%4d\n", + seq_printf(s, " - quota data: %4d in quota files:%4d\n", si->ndirty_qdata, si->nquota_files); seq_printf(s, " - meta: %4d in %4d\n", si->ndirty_meta, si->meta_pages); @@ -665,6 +761,7 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_stat_info *si; + struct f2fs_dev_stats *dev_stats; unsigned long flags; int i; @@ -672,6 +769,15 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) if (!si) return -ENOMEM; + dev_stats = f2fs_kzalloc(sbi, sizeof(struct f2fs_dev_stats) * + sbi->s_ndevs, GFP_KERNEL); + if (!dev_stats) { + kfree(si); + return -ENOMEM; + } + + si->dev_stats = dev_stats; + si->all_area_segs = le32_to_cpu(raw_super->segment_count); si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit); si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat); @@ -724,6 +830,7 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi) list_del(&si->stat_list); raw_spin_unlock_irqrestore(&f2fs_stat_lock, flags); + kfree(si->dev_stats); kfree(si); } diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 62ac440d94168..347b3b6478347 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -24,6 +24,7 @@ bool sanity_check_extent_cache(struct inode *inode, struct page *ipage) struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_extent *i_ext = &F2FS_INODE(ipage)->i_ext; struct extent_info ei; + int devi; get_read_extent_info(&ei, i_ext); @@ -38,7 +39,36 @@ bool sanity_check_extent_cache(struct inode *inode, struct page *ipage) ei.blk, ei.fofs, ei.len); return false; } - return true; + + if (!IS_DEVICE_ALIASING(inode)) + return true; + + for (devi = 0; devi < sbi->s_ndevs; devi++) { + if (FDEV(devi).start_blk != ei.blk || + FDEV(devi).end_blk != ei.blk + ei.len - 1) + continue; + + if (devi == 0) { + f2fs_warn(sbi, + "%s: inode (ino=%lx) is an alias of meta device", + __func__, inode->i_ino); + return false; + } + + if (bdev_is_zoned(FDEV(devi).bdev)) { + f2fs_warn(sbi, + "%s: device alias inode (ino=%lx)'s extent info " + "[%u, %u, %u] maps to zoned block device", + __func__, inode->i_ino, ei.blk, ei.fofs, ei.len); + return false; + } + return true; + } + + f2fs_warn(sbi, "%s: device alias inode (ino=%lx)'s extent info " + "[%u, %u, %u] is inconsistent w/ any devices", + __func__, inode->i_ino, ei.blk, ei.fofs, ei.len); + return false; } static void __set_extent_info(struct extent_info *ei, @@ -76,6 +106,9 @@ static bool __init_may_extent_tree(struct inode *inode, enum extent_type type) static bool __may_extent_tree(struct inode *inode, enum extent_type type) { + if (IS_DEVICE_ALIASING(inode) && type == EX_READ) + return true; + /* * for recovered files during mount do not create extents * if shrinker is not registered. @@ -346,21 +379,22 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode, } static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, - struct extent_tree *et) + struct extent_tree *et, unsigned int nr_shrink) { struct rb_node *node, *next; struct extent_node *en; - unsigned int count = atomic_read(&et->node_cnt); + unsigned int count; node = rb_first_cached(&et->root); - while (node) { + + for (count = 0; node && count < nr_shrink; count++) { next = rb_next(node); en = rb_entry(node, struct extent_node, rb_node); __release_extent_node(sbi, et, en); node = next; } - return count - atomic_read(&et->node_cnt); + return count; } static void __drop_largest_extent(struct extent_tree *et, @@ -401,6 +435,11 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct page *ipage) if (atomic_read(&et->node_cnt) || !ei.len) goto skip; + if (IS_DEVICE_ALIASING(inode)) { + et->largest = ei; + goto skip; + } + en = __attach_extent_node(sbi, et, &ei, NULL, &et->root.rb_root.rb_node, true); if (en) { @@ -463,6 +502,11 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs, goto out; } + if (IS_DEVICE_ALIASING(inode)) { + ret = false; + goto out; + } + en = __lookup_extent_node(&et->root, et->cached_en, pgofs); if (!en) goto out; @@ -579,6 +623,30 @@ static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi, return en; } +static unsigned int __destroy_extent_node(struct inode *inode, + enum extent_type type) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et = F2FS_I(inode)->extent_tree[type]; + unsigned int nr_shrink = type == EX_READ ? + READ_EXTENT_CACHE_SHRINK_NUMBER : + AGE_EXTENT_CACHE_SHRINK_NUMBER; + unsigned int node_cnt = 0; + + if (!et || !atomic_read(&et->node_cnt)) + return 0; + + while (atomic_read(&et->node_cnt)) { + write_lock(&et->lock); + node_cnt += __free_extent_tree(sbi, et, nr_shrink); + write_unlock(&et->lock); + } + + f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); + + return node_cnt; +} + static void __update_extent_tree_range(struct inode *inode, struct extent_info *tei, enum extent_type type) { @@ -649,7 +717,9 @@ static void __update_extent_tree_range(struct inode *inode, } if (end < org_end && (type != EX_READ || - org_end - end >= F2FS_MIN_EXTENT_LEN)) { + (org_end - end >= F2FS_MIN_EXTENT_LEN && + atomic_read(&et->node_cnt) < + sbi->max_read_extent_count))) { if (parts) { __set_extent_info(&ei, end, org_end - end, @@ -717,9 +787,6 @@ static void __update_extent_tree_range(struct inode *inode, } } - if (is_inode_flag_set(inode, FI_NO_EXTENT)) - __free_extent_tree(sbi, et); - if (et->largest_updated) { et->largest_updated = false; updated = true; @@ -737,6 +804,9 @@ static void __update_extent_tree_range(struct inode *inode, out_read_extent_cache: write_unlock(&et->lock); + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + __destroy_extent_node(inode, EX_READ); + if (updated) f2fs_mark_inode_dirty_sync(inode, true); } @@ -899,10 +969,14 @@ static unsigned int __shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink list_for_each_entry_safe(et, next, &eti->zombie_list, list) { if (atomic_read(&et->node_cnt)) { write_lock(&et->lock); - node_cnt += __free_extent_tree(sbi, et); + node_cnt += __free_extent_tree(sbi, et, + nr_shrink - node_cnt - tree_cnt); write_unlock(&et->lock); } - f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); + + if (atomic_read(&et->node_cnt)) + goto unlock_out; + list_del_init(&et->list); radix_tree_delete(&eti->extent_tree_root, et->ino); kmem_cache_free(extent_tree_slab, et); @@ -1041,23 +1115,6 @@ unsigned int f2fs_shrink_age_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink return __shrink_extent_tree(sbi, nr_shrink, EX_BLOCK_AGE); } -static unsigned int __destroy_extent_node(struct inode *inode, - enum extent_type type) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct extent_tree *et = F2FS_I(inode)->extent_tree[type]; - unsigned int node_cnt = 0; - - if (!et || !atomic_read(&et->node_cnt)) - return 0; - - write_lock(&et->lock); - node_cnt = __free_extent_tree(sbi, et); - write_unlock(&et->lock); - - return node_cnt; -} - void f2fs_destroy_extent_node(struct inode *inode) { __destroy_extent_node(inode, EX_READ); @@ -1066,7 +1123,6 @@ void f2fs_destroy_extent_node(struct inode *inode) static void __drop_extent_tree(struct inode *inode, enum extent_type type) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct extent_tree *et = F2FS_I(inode)->extent_tree[type]; bool updated = false; @@ -1074,7 +1130,6 @@ static void __drop_extent_tree(struct inode *inode, enum extent_type type) return; write_lock(&et->lock); - __free_extent_tree(sbi, et); if (type == EX_READ) { set_inode_flag(inode, FI_NO_EXTENT); if (et->largest.len) { @@ -1083,6 +1138,9 @@ static void __drop_extent_tree(struct inode *inode, enum extent_type type) } } write_unlock(&et->lock); + + __destroy_extent_node(inode, type); + if (updated) f2fs_mark_inode_dirty_sync(inode, true); } @@ -1156,6 +1214,7 @@ void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi) sbi->hot_data_age_threshold = DEF_HOT_DATA_AGE_THRESHOLD; sbi->warm_data_age_threshold = DEF_WARM_DATA_AGE_THRESHOLD; sbi->last_age_weight = LAST_AGE_WEIGHT; + sbi->max_read_extent_count = DEF_MAX_READ_EXTENT_COUNT; } int __init f2fs_create_extent_cache(void) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 33f5449dc22d5..6f2cbf4c57402 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -213,6 +213,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_CASEFOLD 0x00001000 #define F2FS_FEATURE_COMPRESSION 0x00002000 #define F2FS_FEATURE_RO 0x00004000 +#define F2FS_FEATURE_DEVICE_ALIAS 0x00008000 #define __F2FS_HAS_FEATURE(raw_super, mask) \ ((raw_super->feature & cpu_to_le32(mask)) != 0) @@ -634,6 +635,9 @@ enum { #define DEF_HOT_DATA_AGE_THRESHOLD 262144 #define DEF_WARM_DATA_AGE_THRESHOLD 2621440 +/* default max read extent count per inode */ +#define DEF_MAX_READ_EXTENT_COUNT 10240 + /* extent cache type */ enum extent_type { EX_READ, @@ -1018,7 +1022,7 @@ static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode, #define NR_CURSEG_PERSIST_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) #define NR_CURSEG_TYPE (NR_CURSEG_INMEM_TYPE + NR_CURSEG_PERSIST_TYPE) -enum { +enum log_type { CURSEG_HOT_DATA = 0, /* directory entry blocks */ CURSEG_WARM_DATA, /* data blocks */ CURSEG_COLD_DATA, /* multimedia or GCed data blocks */ @@ -1063,7 +1067,6 @@ struct f2fs_sm_info { unsigned int segment_count; /* total # of segments */ unsigned int main_segments; /* # of segments in main area */ unsigned int reserved_segments; /* # of reserved segments */ - unsigned int additional_reserved_segments;/* reserved segs for IO align feature */ unsigned int ovp_segments; /* # of overprovision segments */ /* a threshold to reclaim prefree segments */ @@ -1619,6 +1622,7 @@ struct f2fs_sb_info { /* for extent tree cache */ struct extent_tree_info extent_tree[NR_EXTENT_CACHES]; atomic64_t allocated_data_blocks; /* for block age extent_cache */ + unsigned int max_read_extent_count; /* max read extent count per inode */ /* The threshold used for hot and warm data seperation*/ unsigned int hot_data_age_threshold; @@ -1758,6 +1762,7 @@ struct f2fs_sb_info { unsigned int dirty_device; /* for checkpoint data flush */ spinlock_t dev_lock; /* protect dirty_device */ bool aligned_blksize; /* all devices has the same logical blksize */ + unsigned int first_zoned_segno; /* first zoned segno */ /* For write statistics */ u64 sectors_written_start; @@ -3046,6 +3051,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) #define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ #define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ #define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */ +#define F2FS_DEVICE_ALIAS_FL 0x80000000 /* File for aliasing a device */ #define F2FS_QUOTA_DEFAULT_FL (F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL) @@ -3061,6 +3067,8 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) /* Flags that are appropriate for non-directories/regular files. */ #define F2FS_OTHER_FLMASK (F2FS_NODUMP_FL | F2FS_NOATIME_FL) +#define IS_DEVICE_ALIASING(inode) (F2FS_I(inode)->i_flags & F2FS_DEVICE_ALIAS_FL) + static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) { if (S_ISDIR(mode)) @@ -3632,8 +3640,7 @@ int f2fs_quota_sync(struct super_block *sb, int type); loff_t max_file_blocks(struct inode *inode); void f2fs_quota_off_umount(struct super_block *sb); void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag); -void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason, - bool irq_context); +void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason); void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error); void f2fs_handle_error_async(struct f2fs_sb_info *sbi, unsigned char error); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); @@ -3754,7 +3761,8 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, block_t old_addr, block_t new_addr, unsigned char version, bool recover_curseg, bool recover_newaddr); -int f2fs_get_segment_temp(int seg_type); +enum temp_type f2fs_get_segment_temp(struct f2fs_sb_info *sbi, + enum log_type seg_type); int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, block_t old_blkaddr, block_t *new_blkaddr, struct f2fs_summary *sum, int type, @@ -3771,8 +3779,7 @@ void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk); int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, unsigned int val, int alloc); void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); -int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi); -int f2fs_check_write_pointer(struct f2fs_sb_info *sbi); +int f2fs_check_and_fix_write_pointer(struct f2fs_sb_info *sbi); int f2fs_build_segment_manager(struct f2fs_sb_info *sbi); void f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi); int __init f2fs_create_segment_manager_caches(void); @@ -3783,6 +3790,8 @@ enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi, unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi); unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi, unsigned int segno); +unsigned long long f2fs_get_section_mtime(struct f2fs_sb_info *sbi, + unsigned int segno); #define DEF_FRAGMENT_SIZE 4 #define MIN_FRAGMENT_SIZE 1 @@ -3935,6 +3944,19 @@ void f2fs_destroy_recovery_cache(void); * debug.c */ #ifdef CONFIG_F2FS_STAT_FS +enum { + DEVSTAT_INUSE, + DEVSTAT_DIRTY, + DEVSTAT_FULL, + DEVSTAT_FREE, + DEVSTAT_PREFREE, + DEVSTAT_MAX, +}; + +struct f2fs_dev_stats { + unsigned int devstats[2][DEVSTAT_MAX]; /* 0: segs, 1: secs */ +}; + struct f2fs_stat_info { struct list_head stat_list; struct f2fs_sb_info *sbi; @@ -3998,6 +4020,7 @@ struct f2fs_stat_info { unsigned int block_count[2]; unsigned int inplace_count; unsigned long long base_mem, cache_mem, page_mem; + struct f2fs_dev_stats *dev_stats; }; static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) @@ -4510,6 +4533,7 @@ F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM); F2FS_FEATURE_FUNCS(casefold, CASEFOLD); F2FS_FEATURE_FUNCS(compression, COMPRESSION); F2FS_FEATURE_FUNCS(readonly, RO); +F2FS_FEATURE_FUNCS(device_alias, DEVICE_ALIAS); #ifdef CONFIG_BLK_DEV_ZONED static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 84447d5145aaf..aa9679b3d8e4f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -725,6 +725,11 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock) trace_f2fs_truncate_blocks_enter(inode, from); + if (IS_DEVICE_ALIASING(inode) && from) { + err = -EINVAL; + goto out_err; + } + free_from = (pgoff_t)F2FS_BLK_ALIGN(from); if (free_from >= max_file_blocks(inode)) @@ -739,6 +744,21 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock) goto out; } + if (IS_DEVICE_ALIASING(inode)) { + struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ]; + struct extent_info ei = et->largest; + unsigned int i; + + for (i = 0; i < ei.len; i++) + f2fs_invalidate_blocks(sbi, ei.blk + i); + + dec_valid_block_count(sbi, inode, ei.len); + f2fs_update_time(sbi, REQ_TIME); + + f2fs_put_page(ipage, 1); + goto out; + } + if (f2fs_has_inline_data(inode)) { f2fs_truncate_inline_inode(inode, ipage, from); f2fs_put_page(ipage, 1); @@ -774,7 +794,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock) /* lastly zero out the first data page */ if (!err) err = truncate_partial_data_page(inode, from, truncate_page); - +out_err: trace_f2fs_truncate_blocks_exit(inode, err); return err; } @@ -863,7 +883,11 @@ static bool f2fs_force_buffered_io(struct inode *inode, int rw) return true; if (f2fs_compressed_file(inode)) return true; - if (f2fs_has_inline_data(inode)) + /* + * only force direct read to use buffered IO, for direct write, + * it expects inline data conversion before committing IO. + */ + if (f2fs_has_inline_data(inode) && rw == READ) return true; /* disallow direct IO if any of devices has unaligned blksize */ @@ -992,7 +1016,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return -EPERM; if ((attr->ia_valid & ATTR_SIZE)) { - if (!f2fs_is_compress_backend_ready(inode)) + if (!f2fs_is_compress_backend_ready(inode) || + IS_DEVICE_ALIASING(inode)) return -EOPNOTSUPP; if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) && !IS_ALIGNED(attr->ia_size, @@ -1790,7 +1815,8 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, map.m_len = sec_blks; next_alloc: - if (has_not_enough_free_secs(sbi, 0, + if (has_not_enough_free_secs(sbi, 0, f2fs_sb_has_blkzoned(sbi) ? + ZONED_PIN_SEC_REQUIRED_COUNT : GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi)))) { f2fs_down_write(&sbi->gc_lock); stat_inc_gc_call_count(sbi, FOREGROUND); @@ -1860,7 +1886,7 @@ static long f2fs_fallocate(struct file *file, int mode, return -EIO; if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode))) return -ENOSPC; - if (!f2fs_is_compress_backend_ready(inode)) + if (!f2fs_is_compress_backend_ready(inode) || IS_DEVICE_ALIASING(inode)) return -EOPNOTSUPP; /* f2fs only support ->fallocate for regular file */ @@ -2343,9 +2369,12 @@ int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag, if (readonly) goto out; - /* grab sb->s_umount to avoid racing w/ remount() */ + /* + * grab sb->s_umount to avoid racing w/ remount() and other shutdown + * paths. + */ if (need_lock) - down_read(&sbi->sb->s_umount); + down_write(&sbi->sb->s_umount); f2fs_stop_gc_thread(sbi); f2fs_stop_discard_thread(sbi); @@ -2354,7 +2383,7 @@ int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag, clear_opt(sbi, DISCARD); if (need_lock) - up_read(&sbi->sb->s_umount); + up_write(&sbi->sb->s_umount); f2fs_update_time(sbi, REQ_TIME); out: @@ -2861,7 +2890,7 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!S_ISREG(inode->i_mode) || f2fs_is_atomic_file(inode)) + if (!S_ISREG(inode->i_mode)) return -EINVAL; if (f2fs_readonly(sbi->sb)) @@ -3291,6 +3320,9 @@ int f2fs_pin_file_control(struct inode *inode, bool inc) struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + if (IS_DEVICE_ALIASING(inode)) + return -EINVAL; + if (fi->i_gc_failures >= sbi->gc_pin_file_threshold) { f2fs_warn(sbi, "%s: Enable GC = ino %lx after %x GC trials", __func__, inode->i_ino, fi->i_gc_failures); @@ -3321,6 +3353,9 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg) if (f2fs_readonly(sbi->sb)) return -EROFS; + if (!pin && IS_DEVICE_ALIASING(inode)) + return -EOPNOTSUPP; + ret = mnt_want_write_file(filp); if (ret) return ret; @@ -3386,6 +3421,12 @@ static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg) return put_user(pin, (u32 __user *)arg); } +static int f2fs_ioc_get_dev_alias_file(struct file *filp, unsigned long arg) +{ + return put_user(IS_DEVICE_ALIASING(file_inode(filp)) ? 1 : 0, + (u32 __user *)arg); +} + int f2fs_precache_extents(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); @@ -3787,7 +3828,7 @@ static int reserve_compress_blocks(struct dnode_of_data *dn, pgoff_t count, to_reserved = cluster_size - compr_blocks - reserved; /* for the case all blocks in cluster were reserved */ - if (to_reserved == 1) { + if (reserved && to_reserved == 1) { dn->ofs_in_node += cluster_size; goto next; } @@ -4485,6 +4526,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_decompress_file(filp); case F2FS_IOC_COMPRESS_FILE: return f2fs_ioc_compress_file(filp); + case F2FS_IOC_GET_DEV_ALIAS_FILE: + return f2fs_ioc_get_dev_alias_file(filp, arg); default: return -ENOTTY; } @@ -4760,7 +4803,8 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter, else return 0; - map.m_may_create = true; + if (!IS_DEVICE_ALIASING(inode)) + map.m_may_create = true; if (dio) { map.m_seg_type = f2fs_rw_hint_to_seg_type(sbi, inode->i_write_hint); @@ -4816,8 +4860,8 @@ static void f2fs_dio_write_submit_io(const struct iomap_iter *iter, { struct inode *inode = iter->inode; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - int seg_type = f2fs_rw_hint_to_seg_type(sbi, inode->i_write_hint); - enum temp_type temp = f2fs_get_segment_temp(seg_type); + enum log_type type = f2fs_rw_hint_to_seg_type(sbi, inode->i_write_hint); + enum temp_type temp = f2fs_get_segment_temp(sbi, type); bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp); submit_bio(bio); @@ -5197,6 +5241,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_SET_COMPRESS_OPTION: case F2FS_IOC_DECOMPRESS_FILE: case F2FS_IOC_COMPRESS_FILE: + case F2FS_IOC_GET_DEV_ALIAS_FILE: break; default: return -ENOIOCTLCMD; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9322a7200e310..3e1b6d2ff3a73 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -257,6 +257,8 @@ static int select_gc_type(struct f2fs_sb_info *sbi, int gc_type) switch (sbi->gc_mode) { case GC_IDLE_CB: + case GC_URGENT_LOW: + case GC_URGENT_MID: gc_mode = GC_CB; break; case GC_IDLE_GREEDY: @@ -361,20 +363,15 @@ static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); - unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); - unsigned int start = GET_SEG_FROM_SEC(sbi, secno); unsigned long long mtime = 0; unsigned int vblocks; unsigned char age = 0; unsigned char u; - unsigned int i; unsigned int usable_segs_per_sec = f2fs_usable_segs_in_sec(sbi); - for (i = 0; i < usable_segs_per_sec; i++) - mtime += get_seg_entry(sbi, start + i)->mtime; + mtime = f2fs_get_section_mtime(sbi, segno); + f2fs_bug_on(sbi, mtime == INVALID_MTIME); vblocks = get_valid_blocks(sbi, segno, true); - - mtime = div_u64(mtime, usable_segs_per_sec); vblocks = div_u64(vblocks, usable_segs_per_sec); u = BLKS_TO_SEGS(sbi, vblocks * 100); @@ -519,10 +516,7 @@ static void add_victim_entry(struct f2fs_sb_info *sbi, struct victim_sel_policy *p, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); - unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); - unsigned int start = GET_SEG_FROM_SEC(sbi, secno); unsigned long long mtime = 0; - unsigned int i; if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { if (p->gc_mode == GC_AT && @@ -530,9 +524,8 @@ static void add_victim_entry(struct f2fs_sb_info *sbi, return; } - for (i = 0; i < SEGS_PER_SEC(sbi); i++) - mtime += get_seg_entry(sbi, start + i)->mtime; - mtime = div_u64(mtime, SEGS_PER_SEC(sbi)); + mtime = f2fs_get_section_mtime(sbi, segno); + f2fs_bug_on(sbi, mtime == INVALID_MTIME); /* Handle if the system time has changed by the user */ if (mtime < sit_i->min_mtime) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 2914b678bf8fb..5c1eaf55e1277 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -35,6 +35,7 @@ #define LIMIT_BOOST_ZONED_GC 25 /* percentage over total user space of boosted gc for zoned devices */ #define DEF_MIGRATION_WINDOW_GRANULARITY_ZONED 3 #define BOOST_GC_MULTIPLE 5 +#define ZONED_PIN_SEC_REQUIRED_COUNT 1 #define DEF_GC_FAILED_PINNED_FILES 2048 #define MAX_GC_FAILED_PINNED_FILES USHRT_MAX diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 1ed86df343a5d..282fd320bdb35 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -372,6 +372,19 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) return false; } + if (IS_DEVICE_ALIASING(inode)) { + if (!f2fs_sb_has_device_alias(sbi)) { + f2fs_warn(sbi, "%s: inode (ino=%lx) has device alias flag, but the feature is off", + __func__, inode->i_ino); + return false; + } + if (!f2fs_is_pinned_file(inode)) { + f2fs_warn(sbi, "%s: inode (ino=%lx) has device alias flag, but is not pinned", + __func__, inode->i_ino); + return false; + } + } + return true; } @@ -775,8 +788,10 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) !is_inode_flag_set(inode, FI_DIRTY_INODE)) return 0; - if (!f2fs_is_checkpoint_ready(sbi)) + if (!f2fs_is_checkpoint_ready(sbi)) { + f2fs_mark_inode_dirty_sync(inode, true); return -ENOSPC; + } /* * We need to balance fs here to prevent from producing dirty node pages @@ -823,7 +838,8 @@ void f2fs_evict_inode(struct inode *inode) f2fs_bug_on(sbi, get_dirty_pages(inode)); f2fs_remove_dirty_inode(inode); - f2fs_destroy_extent_tree(inode); + if (!IS_DEVICE_ALIASING(inode)) + f2fs_destroy_extent_tree(inode); if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; @@ -879,6 +895,9 @@ void f2fs_evict_inode(struct inode *inode) goto retry; } + if (IS_DEVICE_ALIASING(inode)) + f2fs_destroy_extent_tree(inode); + if (err) { f2fs_update_inode_page(inode); if (dquot_initialize_needed(inode)) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 59b13ff243fa8..0b900a7a48e59 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -905,6 +905,16 @@ static int truncate_node(struct dnode_of_data *dn) if (err) return err; + if (ni.blk_addr != NEW_ADDR && + !f2fs_is_valid_blkaddr(sbi, ni.blk_addr, DATA_GENERIC_ENHANCE)) { + f2fs_err_ratelimited(sbi, + "nat entry is corrupted, run fsck to fix it, ino:%u, " + "nid:%u, blkaddr:%u", ni.ino, ni.nid, ni.blk_addr); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_handle_error(sbi, ERROR_INCONSISTENT_NAT); + return -EFSCORRUPTED; + } + /* Deallocate node address */ f2fs_invalidate_blocks(sbi, ni.blk_addr); dec_valid_node_count(sbi, dn->inode, dn->nid == dn->inode->i_ino); @@ -1056,7 +1066,7 @@ static int truncate_partial_nodes(struct dnode_of_data *dn, int i; int idx = depth - 2; - nid[0] = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]); + nid[0] = get_nid(dn->inode_page, offset[0], true); if (!nid[0]) return 0; @@ -1167,7 +1177,7 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) skip_partial: while (cont) { - dn.nid = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]); + dn.nid = get_nid(page, offset[0], true); switch (offset[0]) { case NODE_DIR1_BLOCK: case NODE_DIR2_BLOCK: @@ -1199,13 +1209,10 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) } if (err < 0) goto fail; - if (offset[1] == 0 && - ri->i_nid[offset[0] - NODE_DIR1_BLOCK]) { + if (offset[1] == 0 && get_nid(page, offset[0], true)) { lock_page(page); BUG_ON(page->mapping != NODE_MAPPING(sbi)); - f2fs_wait_on_page_writeback(page, NODE, true, true); - ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; - set_page_dirty(page); + set_nid(page, offset[0], 0, true); unlock_page(page); } offset[1] = 0; @@ -1331,7 +1338,12 @@ struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs) err = -EFSCORRUPTED; dec_valid_node_count(sbi, dn->inode, !ofs); set_sbi_flag(sbi, SBI_NEED_FSCK); - f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR); + f2fs_warn_ratelimited(sbi, + "f2fs_new_node_page: inconsistent nat entry, " + "ino:%u, nid:%u, blkaddr:%u, ver:%u, flag:%u", + new_ni.ino, new_ni.nid, new_ni.blk_addr, + new_ni.version, new_ni.flag); + f2fs_handle_error(sbi, ERROR_INCONSISTENT_NAT); goto fail; } #endif diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index e4d81b8705d1e..f35be2c48e3c0 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -899,13 +899,8 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) * and the f2fs is not read only, check and fix zoned block devices' * write pointer consistency. */ - if (f2fs_sb_has_blkzoned(sbi) && !f2fs_readonly(sbi->sb)) { - int err2 = f2fs_fix_curseg_write_pointer(sbi); - - if (!err2) - err2 = f2fs_check_write_pointer(sbi); - if (err2) - err = err2; + if (!err) { + err = f2fs_check_and_fix_write_pointer(sbi); ret = err; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1766254279d24..eade36c5ef138 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1290,16 +1290,18 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi, wait_list, issued); return 0; } - - /* - * Issue discard for conventional zones only if the device - * supports discard. - */ - if (!bdev_max_discard_sectors(bdev)) - return -EOPNOTSUPP; } #endif + /* + * stop issuing discard for any of below cases: + * 1. device is conventional zone, but it doesn't support discard. + * 2. device is regulare device, after snapshot it doesn't support + * discard. + */ + if (!bdev_max_discard_sectors(bdev)) + return -EOPNOTSUPP; + trace_f2fs_issue_discard(bdev, dc->di.start, dc->di.len); lstart = dc->di.lstart; @@ -2711,7 +2713,7 @@ static int get_new_segment(struct f2fs_sb_info *sbi, if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_PRIOR_CONV || pinning) segno = 0; else - segno = max(first_zoned_segno(sbi), *newseg); + segno = max(sbi->first_zoned_segno, *newseg); hint = GET_SEC_FROM_SEG(sbi, segno); } #endif @@ -2723,7 +2725,7 @@ static int get_new_segment(struct f2fs_sb_info *sbi, if (secno >= MAIN_SECS(sbi) && f2fs_sb_has_blkzoned(sbi)) { /* Write only to sequential zones */ if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_ONLY_SEQ) { - hint = GET_SEC_FROM_SEG(sbi, first_zoned_segno(sbi)); + hint = GET_SEC_FROM_SEG(sbi, sbi->first_zoned_segno); secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint); } else secno = find_first_zero_bit(free_i->free_secmap, @@ -2926,7 +2928,8 @@ static int change_curseg(struct f2fs_sb_info *sbi, int type) struct f2fs_summary_block *sum_node; struct page *sum_page; - write_sum_page(sbi, curseg->sum_blk, GET_SUM_BLOCK(sbi, curseg->segno)); + if (curseg->inited) + write_sum_page(sbi, curseg->sum_blk, GET_SUM_BLOCK(sbi, curseg->segno)); __set_test_and_inuse(sbi, new_segno); @@ -3237,7 +3240,8 @@ int f2fs_allocate_pinning_section(struct f2fs_sb_info *sbi) if (f2fs_sb_has_blkzoned(sbi) && err == -EAGAIN && gc_required) { f2fs_down_write(&sbi->gc_lock); - err = f2fs_gc_range(sbi, 0, GET_SEGNO(sbi, FDEV(0).end_blk), true, 1); + err = f2fs_gc_range(sbi, 0, GET_SEGNO(sbi, FDEV(0).end_blk), + true, ZONED_PIN_SEC_REQUIRED_COUNT); f2fs_up_write(&sbi->gc_lock); gc_required = false; @@ -3581,18 +3585,35 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) } } -int f2fs_get_segment_temp(int seg_type) +enum temp_type f2fs_get_segment_temp(struct f2fs_sb_info *sbi, + enum log_type type) { - if (IS_HOT(seg_type)) - return HOT; - else if (IS_WARM(seg_type)) - return WARM; - return COLD; + struct curseg_info *curseg = CURSEG_I(sbi, type); + enum temp_type temp = COLD; + + switch (curseg->seg_type) { + case CURSEG_HOT_NODE: + case CURSEG_HOT_DATA: + temp = HOT; + break; + case CURSEG_WARM_NODE: + case CURSEG_WARM_DATA: + temp = WARM; + break; + case CURSEG_COLD_NODE: + case CURSEG_COLD_DATA: + temp = COLD; + break; + default: + f2fs_bug_on(sbi, 1); + } + + return temp; } static int __get_segment_type(struct f2fs_io_info *fio) { - int type = 0; + enum log_type type = CURSEG_HOT_DATA; switch (F2FS_OPTION(fio->sbi).active_logs) { case 2: @@ -3608,7 +3629,7 @@ static int __get_segment_type(struct f2fs_io_info *fio) f2fs_bug_on(fio->sbi, true); } - fio->temp = f2fs_get_segment_temp(type); + fio->temp = f2fs_get_segment_temp(fio->sbi, type); return type; } @@ -3793,10 +3814,35 @@ void f2fs_update_device_state(struct f2fs_sb_info *sbi, nid_t ino, } } +static int log_type_to_seg_type(enum log_type type) +{ + int seg_type = CURSEG_COLD_DATA; + + switch (type) { + case CURSEG_HOT_DATA: + case CURSEG_WARM_DATA: + case CURSEG_COLD_DATA: + case CURSEG_HOT_NODE: + case CURSEG_WARM_NODE: + case CURSEG_COLD_NODE: + seg_type = (int)type; + break; + case CURSEG_COLD_DATA_PINNED: + case CURSEG_ALL_DATA_ATGC: + seg_type = CURSEG_COLD_DATA; + break; + default: + break; + } + return seg_type; +} + static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { - int type = __get_segment_type(fio); - bool keep_order = (f2fs_lfs_mode(fio->sbi) && type == CURSEG_COLD_DATA); + enum log_type type = __get_segment_type(fio); + int seg_type = log_type_to_seg_type(type); + bool keep_order = (f2fs_lfs_mode(fio->sbi) && + seg_type == CURSEG_COLD_DATA); if (keep_order) f2fs_down_read(&fio->sbi->io_order_lock); @@ -3977,8 +4023,8 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, } } - f2fs_bug_on(sbi, !IS_DATASEG(type)); curseg = CURSEG_I(sbi, type); + f2fs_bug_on(sbi, !IS_DATASEG(curseg->seg_type)); mutex_lock(&curseg->curseg_mutex); down_write(&sit_i->sentry_lock); @@ -4778,12 +4824,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) sizeof(struct f2fs_journal), GFP_KERNEL); if (!array[i].journal) return -ENOMEM; - if (i < NR_PERSISTENT_LOG) - array[i].seg_type = CURSEG_HOT_DATA + i; - else if (i == CURSEG_COLD_DATA_PINNED) - array[i].seg_type = CURSEG_COLD_DATA; - else if (i == CURSEG_ALL_DATA_ATGC) - array[i].seg_type = CURSEG_COLD_DATA; + array[i].seg_type = log_type_to_seg_type(i); reset_curseg_fields(&array[i]); } return restore_curseg_summaries(sbi); @@ -5207,7 +5248,7 @@ static int report_one_zone_cb(struct blk_zone *zone, unsigned int idx, return 0; } -static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) +static int do_fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) { struct curseg_info *cs = CURSEG_I(sbi, type); struct f2fs_dev_info *zbd; @@ -5312,12 +5353,12 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) return 0; } -int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) +static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi) { int i, ret; for (i = 0; i < NR_PERSISTENT_LOG; i++) { - ret = fix_curseg_write_pointer(sbi, i); + ret = do_fix_curseg_write_pointer(sbi, i); if (ret) return ret; } @@ -5340,7 +5381,7 @@ static int check_zone_write_pointer_cb(struct blk_zone *zone, unsigned int idx, return check_zone_write_pointer(args->sbi, args->fdev, zone); } -int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) +static int check_write_pointer(struct f2fs_sb_info *sbi) { int i, ret; struct check_zone_write_pointer_args args; @@ -5360,6 +5401,20 @@ int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) return 0; } +int f2fs_check_and_fix_write_pointer(struct f2fs_sb_info *sbi) +{ + int ret; + + if (!f2fs_sb_has_blkzoned(sbi) || f2fs_readonly(sbi->sb)) + return 0; + + f2fs_notice(sbi, "Checking entire write pointers"); + ret = fix_curseg_write_pointer(sbi); + if (!ret) + ret = check_write_pointer(sbi); + return ret; +} + /* * Return the number of usable blocks in a segment. The number of blocks * returned is always equal to the number of blocks in a segment for @@ -5396,12 +5451,7 @@ static inline unsigned int f2fs_usable_zone_blks_in_seg( return BLKS_PER_SEG(sbi); } #else -int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) -{ - return 0; -} - -int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) +int f2fs_check_and_fix_write_pointer(struct f2fs_sb_info *sbi) { return 0; } @@ -5430,6 +5480,35 @@ unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi) return SEGS_PER_SEC(sbi); } +unsigned long long f2fs_get_section_mtime(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + unsigned int usable_segs_per_sec = f2fs_usable_segs_in_sec(sbi); + unsigned int secno = 0, start = 0; + unsigned int total_valid_blocks = 0; + unsigned long long mtime = 0; + unsigned int i = 0; + + secno = GET_SEC_FROM_SEG(sbi, segno); + start = GET_SEG_FROM_SEC(sbi, secno); + + if (!__is_large_section(sbi)) + return get_seg_entry(sbi, start + i)->mtime; + + for (i = 0; i < usable_segs_per_sec; i++) { + /* for large section, only check the mtime of valid segments */ + struct seg_entry *se = get_seg_entry(sbi, start+i); + + mtime += se->mtime * se->valid_blocks; + total_valid_blocks += se->valid_blocks; + } + + if (total_valid_blocks == 0) + return INVALID_MTIME; + + return div_u64(mtime, total_valid_blocks); +} + /* * Update min, max modified time for cost-benefit GC algorithm */ @@ -5443,13 +5522,9 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi) sit_i->min_mtime = ULLONG_MAX; for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi)) { - unsigned int i; unsigned long long mtime = 0; - for (i = 0; i < SEGS_PER_SEC(sbi); i++) - mtime += get_seg_entry(sbi, segno + i)->mtime; - - mtime = div_u64(mtime, SEGS_PER_SEC(sbi)); + mtime = f2fs_get_section_mtime(sbi, segno); if (sit_i->min_mtime > mtime) sit_i->min_mtime = mtime; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 71adb4a43bec5..943be4f1d6d2d 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -18,6 +18,8 @@ #define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */ #define F2FS_MIN_META_SEGMENTS 8 /* SB + 2 (CP + SIT + NAT) + SSA */ +#define INVALID_MTIME ULLONG_MAX /* no valid blocks in a segment/section */ + /* L: Logical segment # in volume, R: Relative segment # in main area */ #define GET_L2R_SEGNO(free_i, segno) ((segno) - (free_i)->start_segno) #define GET_R2L_SEGNO(free_i, segno) ((segno) + (free_i)->start_segno) @@ -32,10 +34,6 @@ static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, seg_type >= NR_PERSISTENT_LOG); } -#define IS_HOT(t) ((t) == CURSEG_HOT_NODE || (t) == CURSEG_HOT_DATA) -#define IS_WARM(t) ((t) == CURSEG_WARM_NODE || (t) == CURSEG_WARM_DATA) -#define IS_COLD(t) ((t) == CURSEG_COLD_NODE || (t) == CURSEG_COLD_DATA) - #define IS_CURSEG(sbi, seg) \ (((seg) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ ((seg) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ @@ -524,8 +522,7 @@ static inline unsigned int free_segments(struct f2fs_sb_info *sbi) static inline unsigned int reserved_segments(struct f2fs_sb_info *sbi) { - return SM_I(sbi)->reserved_segments + - SM_I(sbi)->additional_reserved_segments; + return SM_I(sbi)->reserved_segments; } static inline unsigned int free_sections(struct f2fs_sb_info *sbi) @@ -559,18 +556,21 @@ static inline int reserved_sections(struct f2fs_sb_info *sbi) } static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi, - unsigned int node_blocks, unsigned int dent_blocks) + unsigned int node_blocks, unsigned int data_blocks, + unsigned int dent_blocks) { - unsigned segno, left_blocks; + unsigned int segno, left_blocks, blocks; int i; - /* check current node sections in the worst case. */ - for (i = CURSEG_HOT_NODE; i <= CURSEG_COLD_NODE; i++) { + /* check current data/node sections in the worst case. */ + for (i = CURSEG_HOT_DATA; i < NR_PERSISTENT_LOG; i++) { segno = CURSEG_I(sbi, i)->segno; left_blocks = CAP_BLKS_PER_SEC(sbi) - get_ckpt_valid_blocks(sbi, segno, true); - if (node_blocks > left_blocks) + + blocks = i <= CURSEG_COLD_DATA ? data_blocks : node_blocks; + if (blocks > left_blocks) return false; } @@ -584,8 +584,9 @@ static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi, } /* - * calculate needed sections for dirty node/dentry - * and call has_curseg_enough_space + * calculate needed sections for dirty node/dentry and call + * has_curseg_enough_space, please note that, it needs to account + * dirty data as well in lfs mode when checkpoint is disabled. */ static inline void __get_secs_required(struct f2fs_sb_info *sbi, unsigned int *lower_p, unsigned int *upper_p, bool *curseg_p) @@ -594,19 +595,30 @@ static inline void __get_secs_required(struct f2fs_sb_info *sbi, get_pages(sbi, F2FS_DIRTY_DENTS) + get_pages(sbi, F2FS_DIRTY_IMETA); unsigned int total_dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS); + unsigned int total_data_blocks = 0; unsigned int node_secs = total_node_blocks / CAP_BLKS_PER_SEC(sbi); unsigned int dent_secs = total_dent_blocks / CAP_BLKS_PER_SEC(sbi); + unsigned int data_secs = 0; unsigned int node_blocks = total_node_blocks % CAP_BLKS_PER_SEC(sbi); unsigned int dent_blocks = total_dent_blocks % CAP_BLKS_PER_SEC(sbi); + unsigned int data_blocks = 0; + + if (f2fs_lfs_mode(sbi) && + unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { + total_data_blocks = get_pages(sbi, F2FS_DIRTY_DATA); + data_secs = total_data_blocks / CAP_BLKS_PER_SEC(sbi); + data_blocks = total_data_blocks % CAP_BLKS_PER_SEC(sbi); + } if (lower_p) - *lower_p = node_secs + dent_secs; + *lower_p = node_secs + dent_secs + data_secs; if (upper_p) *upper_p = node_secs + dent_secs + - (node_blocks ? 1 : 0) + (dent_blocks ? 1 : 0); + (node_blocks ? 1 : 0) + (dent_blocks ? 1 : 0) + + (data_blocks ? 1 : 0); if (curseg_p) *curseg_p = has_curseg_enough_space(sbi, - node_blocks, dent_blocks); + node_blocks, data_blocks, dent_blocks); } static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, @@ -637,12 +649,30 @@ static inline bool has_enough_free_secs(struct f2fs_sb_info *sbi, return !has_not_enough_free_secs(sbi, freed, needed); } +static inline bool has_enough_free_blks(struct f2fs_sb_info *sbi) +{ + unsigned int total_free_blocks = 0; + unsigned int avail_user_block_count; + + spin_lock(&sbi->stat_lock); + + avail_user_block_count = get_available_block_count(sbi, NULL, true); + total_free_blocks = avail_user_block_count - (unsigned int)valid_user_blocks(sbi); + + spin_unlock(&sbi->stat_lock); + + return total_free_blocks > 0; +} + static inline bool f2fs_is_checkpoint_ready(struct f2fs_sb_info *sbi) { if (likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED))) return true; if (likely(has_enough_free_secs(sbi, 0, 0))) return true; + if (!f2fs_lfs_mode(sbi) && + likely(has_enough_free_blks(sbi))) + return true; return false; } @@ -957,13 +987,3 @@ static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force) dcc->discard_wake = true; wake_up_interruptible_all(&dcc->discard_wait_queue); } - -static inline unsigned int first_zoned_segno(struct f2fs_sb_info *sbi) -{ - int devi; - - for (devi = 0; devi < sbi->s_ndevs; devi++) - if (bdev_is_zoned(FDEV(devi).bdev)) - return GET_SEGNO(sbi, FDEV(devi).start_blk); - return 0; -} diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 87ab5696bd482..fc7d463dee158 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -150,6 +150,8 @@ enum { Opt_mode, Opt_fault_injection, Opt_fault_type, + Opt_lazytime, + Opt_nolazytime, Opt_quota, Opt_noquota, Opt_usrquota, @@ -226,6 +228,8 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_fault_type, "fault_type=%u"}, + {Opt_lazytime, "lazytime"}, + {Opt_nolazytime, "nolazytime"}, {Opt_quota, "quota"}, {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, @@ -834,6 +838,10 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) set_opt(sbi, READ_EXTENT_CACHE); break; case Opt_noextent_cache: + if (F2FS_HAS_FEATURE(sbi, F2FS_FEATURE_DEVICE_ALIAS)) { + f2fs_err(sbi, "device aliasing requires extent cache"); + return -EINVAL; + } clear_opt(sbi, READ_EXTENT_CACHE); break; case Opt_noinline_data: @@ -918,6 +926,12 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) f2fs_info(sbi, "fault_type options not supported"); break; #endif + case Opt_lazytime: + sb->s_flags |= SB_LAZYTIME; + break; + case Opt_nolazytime: + sb->s_flags &= ~SB_LAZYTIME; + break; #ifdef CONFIG_QUOTA case Opt_quota: case Opt_usrquota: @@ -1158,7 +1172,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) break; } - strcpy(ext[ext_cnt], name); + ret = strscpy(ext[ext_cnt], name); + if (ret < 0) { + kfree(name); + return ret; + } F2FS_OPTION(sbi).compress_ext_cnt++; kfree(name); break; @@ -1187,7 +1205,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) break; } - strcpy(noext[noext_cnt], name); + ret = strscpy(noext[noext_cnt], name); + if (ret < 0) { + kfree(name); + return ret; + } F2FS_OPTION(sbi).nocompress_ext_cnt++; kfree(name); break; @@ -1738,6 +1760,18 @@ static int f2fs_freeze(struct super_block *sb) static int f2fs_unfreeze(struct super_block *sb) { + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + /* + * It will update discard_max_bytes of mounted lvm device to zero + * after creating snapshot on this lvm device, let's drop all + * remained discards. + * We don't need to disable real-time discard because discard_max_bytes + * will recover after removal of snapshot. + */ + if (test_opt(sbi, DISCARD) && !f2fs_hw_support_discard(sbi)) + f2fs_issue_discard_timeout(sbi); + clear_sbi_flag(F2FS_SB(sb), SBI_IS_FREEZING); return 0; } @@ -2474,6 +2508,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) } } + adjust_unusable_cap_perc(sbi); if (enable_checkpoint == !!test_opt(sbi, DISABLE_CHECKPOINT)) { if (test_opt(sbi, DISABLE_CHECKPOINT)) { err = f2fs_disable_checkpoint(sbi); @@ -2518,7 +2553,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) (test_opt(sbi, POSIX_ACL) ? SB_POSIXACL : 0); limit_reserve_root(sbi); - adjust_unusable_cap_perc(sbi); *flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME); return 0; restore_checkpoint: @@ -3322,7 +3356,7 @@ loff_t max_file_blocks(struct inode *inode) * fit within U32_MAX + 1 data units. */ - result = min(result, F2FS_BYTES_TO_BLK(((loff_t)U32_MAX + 1) * 4096)); + result = umin(result, F2FS_BYTES_TO_BLK(((loff_t)U32_MAX + 1) * 4096)); return result; } @@ -4155,8 +4189,7 @@ static bool system_going_down(void) || system_state == SYSTEM_RESTART; } -void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason, - bool irq_context) +void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason) { struct super_block *sb = sbi->sb; bool shutdown = reason == STOP_CP_REASON_SHUTDOWN; @@ -4168,10 +4201,12 @@ void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason, if (!f2fs_hw_is_readonly(sbi)) { save_stop_reason(sbi, reason); - if (irq_context && !shutdown) - schedule_work(&sbi->s_error_work); - else - f2fs_record_stop_reason(sbi); + /* + * always create an asynchronous task to record stop_reason + * in order to avoid potential deadlock when running into + * f2fs_record_stop_reason() synchronously. + */ + schedule_work(&sbi->s_error_work); } /* @@ -4217,6 +4252,16 @@ static void f2fs_record_error_work(struct work_struct *work) f2fs_record_stop_reason(sbi); } +static inline unsigned int get_first_zoned_segno(struct f2fs_sb_info *sbi) +{ + int devi; + + for (devi = 0; devi < sbi->s_ndevs; devi++) + if (bdev_is_zoned(FDEV(devi).bdev)) + return GET_SEGNO(sbi, FDEV(devi).start_blk); + return 0; +} + static int f2fs_scan_devices(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); @@ -4617,6 +4662,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) /* For write statistics */ sbi->sectors_written_start = f2fs_get_sectors_written(sbi); + /* get segno of first zoned block device */ + sbi->first_zoned_segno = get_first_zoned_segno(sbi); + /* Read accumulated write IO statistics if exists */ seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); if (__exist_node_summaries(sbi)) @@ -4738,26 +4786,23 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) reset_checkpoint: /* * If the f2fs is not readonly and fsync data recovery succeeds, - * check zoned block devices' write pointer consistency. + * write pointer consistency of cursegs and other zones are already + * checked and fixed during recovery. However, if recovery fails, + * write pointers are left untouched, and retry-mount should check + * them here. */ - if (f2fs_sb_has_blkzoned(sbi) && !f2fs_readonly(sb)) { - int err2; - - f2fs_notice(sbi, "Checking entire write pointers"); - err2 = f2fs_check_write_pointer(sbi); - if (err2) - err = err2; - } + if (skip_recovery) + err = f2fs_check_and_fix_write_pointer(sbi); if (err) goto free_meta; + /* f2fs_recover_fsync_data() cleared this already */ + clear_sbi_flag(sbi, SBI_POR_DOING); + err = f2fs_init_inmem_curseg(sbi); if (err) goto sync_free_meta; - /* f2fs_recover_fsync_data() cleared this already */ - clear_sbi_flag(sbi, SBI_POR_DOING); - if (test_opt(sbi, DISABLE_CHECKPOINT)) { err = f2fs_disable_checkpoint(sbi); if (err) @@ -4991,9 +5036,6 @@ static int __init init_f2fs_fs(void) err = f2fs_init_shrinker(); if (err) goto free_sysfs; - err = register_filesystem(&f2fs_fs_type); - if (err) - goto free_shrinker; f2fs_create_root_stats(); err = f2fs_init_post_read_processing(); if (err) @@ -5016,7 +5058,12 @@ static int __init init_f2fs_fs(void) err = f2fs_create_casefold_cache(); if (err) goto free_compress_cache; + err = register_filesystem(&f2fs_fs_type); + if (err) + goto free_casefold_cache; return 0; +free_casefold_cache: + f2fs_destroy_casefold_cache(); free_compress_cache: f2fs_destroy_compress_cache(); free_compress_mempool: @@ -5031,8 +5078,6 @@ static int __init init_f2fs_fs(void) f2fs_destroy_post_read_processing(); free_root_stats: f2fs_destroy_root_stats(); - unregister_filesystem(&f2fs_fs_type); -free_shrinker: f2fs_exit_shrinker(); free_sysfs: f2fs_exit_sysfs(); @@ -5056,6 +5101,7 @@ static int __init init_f2fs_fs(void) static void __exit exit_f2fs_fs(void) { + unregister_filesystem(&f2fs_fs_type); f2fs_destroy_casefold_cache(); f2fs_destroy_compress_cache(); f2fs_destroy_compress_mempool(); @@ -5064,7 +5110,6 @@ static void __exit exit_f2fs_fs(void) f2fs_destroy_iostat_processing(); f2fs_destroy_post_read_processing(); f2fs_destroy_root_stats(); - unregister_filesystem(&f2fs_fs_type); f2fs_exit_shrinker(); f2fs_exit_sysfs(); f2fs_destroy_garbage_collection_cache(); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index c56e8c8739352..6b99dc49f7761 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -501,9 +501,7 @@ static ssize_t __sbi_store(struct f2fs_attr *a, if (a->struct_type == RESERVED_BLOCKS) { spin_lock(&sbi->stat_lock); if (t > (unsigned long)(sbi->user_block_count - - F2FS_OPTION(sbi).root_reserved_blocks - - SEGS_TO_BLKS(sbi, - SM_I(sbi)->additional_reserved_segments))) { + F2FS_OPTION(sbi).root_reserved_blocks)) { spin_unlock(&sbi->stat_lock); return -EINVAL; } @@ -789,6 +787,13 @@ static ssize_t __sbi_store(struct f2fs_attr *a, return count; } + if (!strcmp(a->attr.name, "max_read_extent_count")) { + if (t > UINT_MAX) + return -EINVAL; + *ui = (unsigned int)t; + return count; + } + if (!strcmp(a->attr.name, "ipu_policy")) { if (t >= BIT(F2FS_IPU_MAX)) return -EINVAL; @@ -1054,6 +1059,8 @@ F2FS_SBI_GENERAL_RW_ATTR(revoked_atomic_block); F2FS_SBI_GENERAL_RW_ATTR(hot_data_age_threshold); F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold); F2FS_SBI_GENERAL_RW_ATTR(last_age_weight); +/* read extent cache */ +F2FS_SBI_GENERAL_RW_ATTR(max_read_extent_count); #ifdef CONFIG_BLK_DEV_ZONED F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec); F2FS_SBI_GENERAL_RW_ATTR(blkzone_alloc_policy); @@ -1244,6 +1251,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(hot_data_age_threshold), ATTR_LIST(warm_data_age_threshold), ATTR_LIST(last_age_weight), + ATTR_LIST(max_read_extent_count), NULL, }; ATTRIBUTE_GROUPS(f2fs); @@ -1313,6 +1321,7 @@ F2FS_SB_FEATURE_RO_ATTR(sb_checksum, SB_CHKSUM); F2FS_SB_FEATURE_RO_ATTR(casefold, CASEFOLD); F2FS_SB_FEATURE_RO_ATTR(compression, COMPRESSION); F2FS_SB_FEATURE_RO_ATTR(readonly, RO); +F2FS_SB_FEATURE_RO_ATTR(device_alias, DEVICE_ALIAS); static struct attribute *f2fs_sb_feat_attrs[] = { ATTR_LIST(sb_encryption), @@ -1329,6 +1338,7 @@ static struct attribute *f2fs_sb_feat_attrs[] = { ATTR_LIST(sb_casefold), ATTR_LIST(sb_compression), ATTR_LIST(sb_readonly), + ATTR_LIST(sb_device_alias), NULL, }; ATTRIBUTE_GROUPS(f2fs_sb_feat); diff --git a/fs/fcntl.c b/fs/fcntl.c index ac77dd912412e..49884fa3c81d2 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -374,6 +374,9 @@ static long fcntl_set_rw_hint(struct file *file, unsigned int cmd, u64 __user *argp = (u64 __user *)arg; u64 hint; + if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) + return -EPERM; + if (copy_from_user(&hint, argp, sizeof(hint))) return -EFAULT; if (!rw_hint_valid(hint)) diff --git a/fs/fhandle.c b/fs/fhandle.c index 5f801139358ec..ec9145047dfc9 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -31,6 +31,14 @@ static long do_sys_name_to_handle(const struct path *path, if (!exportfs_can_encode_fh(path->dentry->d_sb->s_export_op, fh_flags)) return -EOPNOTSUPP; + /* + * A request to encode a connectable handle for a disconnected dentry + * is unexpected since AT_EMPTY_PATH is not allowed. + */ + if (fh_flags & EXPORT_FH_CONNECTABLE && + WARN_ON(path->dentry->d_flags & DCACHE_DISCONNECTED)) + return -EINVAL; + if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) return -EFAULT; @@ -45,7 +53,7 @@ static long do_sys_name_to_handle(const struct path *path, /* convert handle size to multiple of sizeof(u32) */ handle_dwords = f_handle.handle_bytes >> 2; - /* we ask for a non connectable maybe decodeable file handle */ + /* Encode a possibly decodeable/connectable file handle */ retval = exportfs_encode_fh(path->dentry, (struct fid *)handle->f_handle, &handle_dwords, fh_flags); @@ -67,8 +75,23 @@ static long do_sys_name_to_handle(const struct path *path, * non variable part of the file_handle */ handle_bytes = 0; - } else + } else { + /* + * When asked to encode a connectable file handle, encode this + * property in the file handle itself, so that we later know + * how to decode it. + * For sanity, also encode in the file handle if the encoded + * object is a directory and verify this during decode, because + * decoding directory file handles is quite different than + * decoding connectable non-directory file handles. + */ + if (fh_flags & EXPORT_FH_CONNECTABLE) { + handle->handle_type |= FILEID_IS_CONNECTABLE; + if (d_is_dir(path->dentry)) + fh_flags |= FILEID_IS_DIR; + } retval = 0; + } /* copy the mount id */ if (unique_mntid) { if (put_user(real_mount(path->mnt)->mnt_id_unique, @@ -109,15 +132,30 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, { struct path path; int lookup_flags; - int fh_flags; + int fh_flags = 0; int err; if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID | - AT_HANDLE_MNT_ID_UNIQUE)) + AT_HANDLE_MNT_ID_UNIQUE | AT_HANDLE_CONNECTABLE)) + return -EINVAL; + + /* + * AT_HANDLE_FID means there is no intention to decode file handle + * AT_HANDLE_CONNECTABLE means there is an intention to decode a + * connected fd (with known path), so these flags are conflicting. + * AT_EMPTY_PATH could be used along with a dfd that refers to a + * disconnected non-directory, which cannot be used to encode a + * connectable file handle, because its parent is unknown. + */ + if (flag & AT_HANDLE_CONNECTABLE && + flag & (AT_HANDLE_FID | AT_EMPTY_PATH)) return -EINVAL; + else if (flag & AT_HANDLE_FID) + fh_flags |= EXPORT_FH_FID; + else if (flag & AT_HANDLE_CONNECTABLE) + fh_flags |= EXPORT_FH_CONNECTABLE; lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; - fh_flags = (flag & AT_HANDLE_FID) ? EXPORT_FH_FID : 0; if (flag & AT_EMPTY_PATH) lookup_flags |= LOOKUP_EMPTY; err = user_path_at(dfd, name, lookup_flags, &path); @@ -207,7 +245,13 @@ static int vfs_dentry_acceptable(void *context, struct dentry *dentry) if (!(ctx->flags & HANDLE_CHECK_SUBTREE) || d == root) retval = 1; - WARN_ON_ONCE(d != root && d != root->d_sb->s_root); + /* + * exportfs_decode_fh_raw() does not call acceptable() callback with + * a disconnected directory dentry, so we should have reached either + * mount fd directory or sb root. + */ + if (ctx->fh_flags & EXPORT_FH_DIR_ONLY) + WARN_ON_ONCE(d != root && d != root->d_sb->s_root); dput(d); return retval; } @@ -306,6 +350,12 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, retval = -EINVAL; goto out_path; } + if (f_handle.handle_type < 0 || + FILEID_USER_FLAGS(f_handle.handle_type) & ~FILEID_VALID_USER_FLAGS) { + retval = -EINVAL; + goto out_path; + } + handle = kmalloc(struct_size(handle, f_handle, f_handle.handle_bytes), GFP_KERNEL); if (!handle) { @@ -321,6 +371,19 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, goto out_handle; } + /* + * If handle was encoded with AT_HANDLE_CONNECTABLE, verify that we + * are decoding an fd with connected path, which is accessible from + * the mount fd path. + */ + if (f_handle.handle_type & FILEID_IS_CONNECTABLE) { + ctx.fh_flags |= EXPORT_FH_CONNECTABLE; + ctx.flags |= HANDLE_CHECK_SUBTREE; + } + if (f_handle.handle_type & FILEID_IS_DIR) + ctx.fh_flags |= EXPORT_FH_DIR_ONLY; + /* Filesystem code should not be exposed to user flags */ + handle->handle_type &= ~FILEID_USER_FLAGS_MASK; retval = do_handle_to_path(handle, path, &ctx); out_handle: diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index ce0ff7a9007b9..2c372180d631e 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -14,5 +14,6 @@ fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o fuse-y += iomode.o fuse-$(CONFIG_FUSE_DAX) += dax.o fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o +fuse-$(CONFIG_SYSCTL) += sysctl.o virtiofs-y := virtio_fs.o diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 0b2da7b7e2ad0..b39844d75a806 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -303,8 +303,8 @@ struct cuse_init_args { struct fuse_args_pages ap; struct cuse_init_in in; struct cuse_init_out out; - struct page *page; - struct fuse_page_desc desc; + struct folio *folio; + struct fuse_folio_desc desc; }; /** @@ -326,7 +326,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, struct fuse_args_pages *ap = &ia->ap; struct cuse_conn *cc = fc_to_cc(fc), *pos; struct cuse_init_out *arg = &ia->out; - struct page *page = ap->pages[0]; + struct folio *folio = ap->folios[0]; struct cuse_devinfo devinfo = { }; struct device *dev; struct cdev *cdev; @@ -343,7 +343,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, /* parse init reply */ cc->unrestricted_ioctl = arg->flags & CUSE_UNRESTRICTED_IOCTL; - rc = cuse_parse_devinfo(page_address(page), ap->args.out_args[1].size, + rc = cuse_parse_devinfo(folio_address(folio), ap->args.out_args[1].size, &devinfo); if (rc) goto err; @@ -411,7 +411,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, kobject_uevent(&dev->kobj, KOBJ_ADD); out: kfree(ia); - __free_page(page); + folio_put(folio); return; err_cdev: @@ -429,7 +429,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, static int cuse_send_init(struct cuse_conn *cc) { int rc; - struct page *page; + struct folio *folio; struct fuse_mount *fm = &cc->fm; struct cuse_init_args *ia; struct fuse_args_pages *ap; @@ -437,13 +437,14 @@ static int cuse_send_init(struct cuse_conn *cc) BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE); rc = -ENOMEM; - page = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (!page) + + folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, 0); + if (!folio) goto err; ia = kzalloc(sizeof(*ia), GFP_KERNEL); if (!ia) - goto err_free_page; + goto err_free_folio; ap = &ia->ap; ia->in.major = FUSE_KERNEL_VERSION; @@ -459,18 +460,18 @@ static int cuse_send_init(struct cuse_conn *cc) ap->args.out_args[1].size = CUSE_INIT_INFO_MAX; ap->args.out_argvar = true; ap->args.out_pages = true; - ap->num_pages = 1; - ap->pages = &ia->page; + ap->num_folios = 1; + ap->folios = &ia->folio; ap->descs = &ia->desc; - ia->page = page; + ia->folio = folio; ia->desc.length = ap->args.out_args[1].size; ap->args.end = cuse_process_init_reply; rc = fuse_simple_background(fm, &ap->args, GFP_KERNEL); if (rc) { kfree(ia); -err_free_page: - __free_page(page); +err_free_folio: + folio_put(folio); } err: return rc; diff --git a/fs/fuse/dax.c b/fs/fuse/dax.c index 12ef91d170bb3..9abbc2f2894f9 100644 --- a/fs/fuse/dax.c +++ b/fs/fuse/dax.c @@ -774,16 +774,6 @@ ssize_t fuse_dax_write_iter(struct kiocb *iocb, struct iov_iter *from) return ret; } -static int fuse_dax_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - - struct inode *inode = mapping->host; - struct fuse_conn *fc = get_fuse_conn(inode); - - return dax_writeback_mapping_range(mapping, fc->dax->dev, wbc); -} - static vm_fault_t __fuse_dax_fault(struct vm_fault *vmf, unsigned int order, bool write) { @@ -1323,7 +1313,6 @@ bool fuse_dax_inode_alloc(struct super_block *sb, struct fuse_inode *fi) } static const struct address_space_operations fuse_dax_file_aops = { - .writepages = fuse_dax_writepages, .direct_IO = noop_direct_IO, .dirty_folio = noop_dirty_folio, }; diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0723c6344b20e..27ccae63495d1 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1028,17 +1028,27 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, struct fuse_req *req = cs->req; struct fuse_args_pages *ap = container_of(req->args, typeof(*ap), args); - - for (i = 0; i < ap->num_pages && (nbytes || zeroing); i++) { + for (i = 0; i < ap->num_folios && (nbytes || zeroing); i++) { int err; unsigned int offset = ap->descs[i].offset; unsigned int count = min(nbytes, ap->descs[i].length); + struct page *orig, *pagep; + + orig = pagep = &ap->folios[i]->page; - err = fuse_copy_page(cs, &ap->pages[i], offset, count, zeroing); + err = fuse_copy_page(cs, &pagep, offset, count, zeroing); if (err) return err; nbytes -= count; + + /* + * fuse_copy_page may have moved a page from a pipe instead of + * copying into our given page, so update the folios if it was + * replaced. + */ + if (pagep != orig) + ap->folios[i] = page_folio(pagep); } return 0; } @@ -1654,24 +1664,25 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, num = outarg.size; while (num) { + struct folio *folio; struct page *page; unsigned int this_num; - err = -ENOMEM; - page = find_or_create_page(mapping, index, - mapping_gfp_mask(mapping)); - if (!page) + folio = filemap_grab_folio(mapping, index); + err = PTR_ERR(folio); + if (IS_ERR(folio)) goto out_iput; - this_num = min_t(unsigned, num, PAGE_SIZE - offset); + page = &folio->page; + this_num = min_t(unsigned, num, folio_size(folio) - offset); err = fuse_copy_page(cs, &page, offset, this_num, 0); - if (!PageUptodate(page) && !err && offset == 0 && - (this_num == PAGE_SIZE || file_size == end)) { - zero_user_segment(page, this_num, PAGE_SIZE); - SetPageUptodate(page); + if (!folio_test_uptodate(folio) && !err && offset == 0 && + (this_num == folio_size(folio) || file_size == end)) { + folio_zero_segment(folio, this_num, folio_size(folio)); + folio_mark_uptodate(folio); } - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); if (err) goto out_iput; @@ -1703,7 +1714,7 @@ static void fuse_retrieve_end(struct fuse_mount *fm, struct fuse_args *args, struct fuse_retrieve_args *ra = container_of(args, typeof(*ra), ap.args); - release_pages(ra->ap.pages, ra->ap.num_pages); + release_pages(ra->ap.folios, ra->ap.num_folios); kfree(ra); } @@ -1717,7 +1728,7 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, unsigned int num; unsigned int offset; size_t total_len = 0; - unsigned int num_pages; + unsigned int num_pages, cur_pages = 0; struct fuse_conn *fc = fm->fc; struct fuse_retrieve_args *ra; size_t args_size = sizeof(*ra); @@ -1736,15 +1747,15 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; num_pages = min(num_pages, fc->max_pages); - args_size += num_pages * (sizeof(ap->pages[0]) + sizeof(ap->descs[0])); + args_size += num_pages * (sizeof(ap->folios[0]) + sizeof(ap->descs[0])); ra = kzalloc(args_size, GFP_KERNEL); if (!ra) return -ENOMEM; ap = &ra->ap; - ap->pages = (void *) (ra + 1); - ap->descs = (void *) (ap->pages + num_pages); + ap->folios = (void *) (ra + 1); + ap->descs = (void *) (ap->folios + num_pages); args = &ap->args; args->nodeid = outarg->nodeid; @@ -1755,19 +1766,20 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, index = outarg->offset >> PAGE_SHIFT; - while (num && ap->num_pages < num_pages) { - struct page *page; + while (num && cur_pages < num_pages) { + struct folio *folio; unsigned int this_num; - page = find_get_page(mapping, index); - if (!page) + folio = filemap_get_folio(mapping, index); + if (IS_ERR(folio)) break; this_num = min_t(unsigned, num, PAGE_SIZE - offset); - ap->pages[ap->num_pages] = page; - ap->descs[ap->num_pages].offset = offset; - ap->descs[ap->num_pages].length = this_num; - ap->num_pages++; + ap->folios[ap->num_folios] = folio; + ap->descs[ap->num_folios].offset = offset; + ap->descs[ap->num_folios].length = this_num; + ap->num_folios++; + cur_pages++; offset = 0; num -= this_num; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 54104dd48af7c..494ac372ace07 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -366,7 +366,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name struct fuse_mount *fm = get_fuse_mount_super(sb); FUSE_ARGS(args); struct fuse_forget_link *forget; - u64 attr_version; + u64 attr_version, evict_ctr; int err; *inode = NULL; @@ -381,6 +381,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name goto out; attr_version = fuse_get_attr_version(fm->fc); + evict_ctr = fuse_get_evict_ctr(fm->fc); fuse_lookup_init(fm->fc, &args, nodeid, name, outarg); err = fuse_simple_request(fm, &args); @@ -398,7 +399,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, &outarg->attr, ATTR_TIMEOUT(outarg), - attr_version); + attr_version, evict_ctr); err = -ENOMEM; if (!*inode) { fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1); @@ -691,7 +692,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir, ff->nodeid = outentry.nodeid; ff->open_flags = outopenp->open_flags; inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, - &outentry.attr, ATTR_TIMEOUT(&outentry), 0); + &outentry.attr, ATTR_TIMEOUT(&outentry), 0, 0); if (!inode) { flags &= ~(O_CREAT | O_EXCL | O_TRUNC); fuse_sync_release(NULL, ff, flags); @@ -822,7 +823,7 @@ static int create_new_entry(struct mnt_idmap *idmap, struct fuse_mount *fm, goto out_put_forget_req; inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, ATTR_TIMEOUT(&outarg), 0); + &outarg.attr, ATTR_TIMEOUT(&outarg), 0, 0); if (!inode) { fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1); return -ENOMEM; @@ -1585,13 +1586,13 @@ static int fuse_permission(struct mnt_idmap *idmap, return err; } -static int fuse_readlink_page(struct inode *inode, struct page *page) +static int fuse_readlink_page(struct inode *inode, struct folio *folio) { struct fuse_mount *fm = get_fuse_mount(inode); - struct fuse_page_desc desc = { .length = PAGE_SIZE - 1 }; + struct fuse_folio_desc desc = { .length = PAGE_SIZE - 1 }; struct fuse_args_pages ap = { - .num_pages = 1, - .pages = &page, + .num_folios = 1, + .folios = &folio, .descs = &desc, }; char *link; @@ -1614,7 +1615,7 @@ static int fuse_readlink_page(struct inode *inode, struct page *page) if (WARN_ON(res >= PAGE_SIZE)) return -EIO; - link = page_address(page); + link = folio_address(folio); link[res] = '\0'; return 0; @@ -1624,7 +1625,7 @@ static const char *fuse_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *callback) { struct fuse_conn *fc = get_fuse_conn(inode); - struct page *page; + struct folio *folio; int err; err = -EIO; @@ -1638,20 +1639,20 @@ static const char *fuse_get_link(struct dentry *dentry, struct inode *inode, if (!dentry) goto out_err; - page = alloc_page(GFP_KERNEL); + folio = folio_alloc(GFP_KERNEL, 0); err = -ENOMEM; - if (!page) + if (!folio) goto out_err; - err = fuse_readlink_page(inode, page); + err = fuse_readlink_page(inode, folio); if (err) { - __free_page(page); + folio_put(folio); goto out_err; } - set_delayed_call(callback, page_put_link, page); + set_delayed_call(callback, page_put_link, &folio->page); - return page_address(page); + return folio_address(folio); out_err: return ERR_PTR(err); @@ -2028,7 +2029,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, fuse_change_attributes_common(inode, &outarg.attr, NULL, ATTR_TIMEOUT(&outarg), - fuse_get_cache_mask(inode)); + fuse_get_cache_mask(inode), 0); oldsize = inode->i_size; /* see the comment in fuse_change_attributes() */ if (!is_wb || is_truncate) @@ -2231,7 +2232,7 @@ void fuse_init_dir(struct inode *inode) static int fuse_symlink_read_folio(struct file *null, struct folio *folio) { - int err = fuse_readlink_page(folio->mapping->host, &folio->page); + int err = fuse_readlink_page(folio->mapping->host, folio); if (!err) folio_mark_uptodate(folio); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index dafdf766b1d53..88d0946b5bc98 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -436,7 +436,7 @@ static struct fuse_writepage_args *fuse_find_writeback(struct fuse_inode *fi, wpa = rb_entry(n, struct fuse_writepage_args, writepages_entry); WARN_ON(get_fuse_inode(wpa->inode) != fi); curr_index = wpa->ia.write.in.offset >> PAGE_SHIFT; - if (idx_from >= curr_index + wpa->ia.ap.num_pages) + if (idx_from >= curr_index + wpa->ia.ap.num_folios) n = n->rb_right; else if (idx_to < curr_index) n = n->rb_left; @@ -483,6 +483,21 @@ static void fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index) wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index)); } +static inline bool fuse_folio_is_writeback(struct inode *inode, + struct folio *folio) +{ + pgoff_t last = folio_next_index(folio) - 1; + return fuse_range_is_writeback(inode, folio_index(folio), last); +} + +static void fuse_wait_on_folio_writeback(struct inode *inode, + struct folio *folio) +{ + struct fuse_inode *fi = get_fuse_inode(inode); + + wait_event(fi->page_waitq, !fuse_folio_is_writeback(inode, folio)); +} + /* * Wait for all pending writepages on the inode to finish. * @@ -645,17 +660,20 @@ void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos, args->out_args[0].size = count; } -static void fuse_release_user_pages(struct fuse_args_pages *ap, +static void fuse_release_user_pages(struct fuse_args_pages *ap, ssize_t nres, bool should_dirty) { unsigned int i; - for (i = 0; i < ap->num_pages; i++) { + for (i = 0; i < ap->num_folios; i++) { if (should_dirty) - set_page_dirty_lock(ap->pages[i]); + folio_mark_dirty_lock(ap->folios[i]); if (ap->args.is_pinned) - unpin_user_page(ap->pages[i]); + unpin_folio(ap->folios[i]); } + + if (nres > 0 && ap->args.invalidate_vmap) + invalidate_kernel_vmap_range(ap->args.vmap_base, nres); } static void fuse_io_release(struct kref *kref) @@ -725,16 +743,16 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) } static struct fuse_io_args *fuse_io_alloc(struct fuse_io_priv *io, - unsigned int npages) + unsigned int nfolios) { struct fuse_io_args *ia; ia = kzalloc(sizeof(*ia), GFP_KERNEL); if (ia) { ia->io = io; - ia->ap.pages = fuse_pages_alloc(npages, GFP_KERNEL, - &ia->ap.descs); - if (!ia->ap.pages) { + ia->ap.folios = fuse_folios_alloc(nfolios, GFP_KERNEL, + &ia->ap.descs); + if (!ia->ap.folios) { kfree(ia); ia = NULL; } @@ -744,7 +762,7 @@ static struct fuse_io_args *fuse_io_alloc(struct fuse_io_priv *io, static void fuse_io_free(struct fuse_io_args *ia) { - kfree(ia->ap.pages); + kfree(ia->ap.folios); kfree(ia); } @@ -754,25 +772,29 @@ static void fuse_aio_complete_req(struct fuse_mount *fm, struct fuse_args *args, struct fuse_io_args *ia = container_of(args, typeof(*ia), ap.args); struct fuse_io_priv *io = ia->io; ssize_t pos = -1; - - fuse_release_user_pages(&ia->ap, io->should_dirty); + size_t nres; if (err) { /* Nothing */ } else if (io->write) { if (ia->write.out.size > ia->write.in.size) { err = -EIO; - } else if (ia->write.in.size != ia->write.out.size) { - pos = ia->write.in.offset - io->offset + - ia->write.out.size; + } else { + nres = ia->write.out.size; + if (ia->write.in.size != ia->write.out.size) + pos = ia->write.in.offset - io->offset + + ia->write.out.size; } } else { u32 outsize = args->out_args[0].size; + nres = outsize; if (ia->read.in.size != outsize) pos = ia->read.in.offset - io->offset + outsize; } + fuse_release_user_pages(&ia->ap, err ?: nres, io->should_dirty); + fuse_aio_complete(io, err, pos); fuse_io_free(ia); } @@ -843,33 +865,33 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read, * reached the client fs yet. So the hole is not present there. */ if (!fc->writeback_cache) { - loff_t pos = page_offset(ap->pages[0]) + num_read; + loff_t pos = folio_pos(ap->folios[0]) + num_read; fuse_read_update_size(inode, pos, attr_ver); } } -static int fuse_do_readpage(struct file *file, struct page *page) +static int fuse_do_readfolio(struct file *file, struct folio *folio) { - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; struct fuse_mount *fm = get_fuse_mount(inode); - loff_t pos = page_offset(page); - struct fuse_page_desc desc = { .length = PAGE_SIZE }; + loff_t pos = folio_pos(folio); + struct fuse_folio_desc desc = { .length = PAGE_SIZE }; struct fuse_io_args ia = { .ap.args.page_zeroing = true, .ap.args.out_pages = true, - .ap.num_pages = 1, - .ap.pages = &page, + .ap.num_folios = 1, + .ap.folios = &folio, .ap.descs = &desc, }; ssize_t res; u64 attr_ver; /* - * Page writeback can extend beyond the lifetime of the - * page-cache page, so make sure we read a properly synced - * page. + * With the temporary pages that are used to complete writeback, we can + * have writeback that extends beyond the lifetime of the folio. So + * make sure we read a properly synced folio. */ - fuse_wait_on_page_writeback(inode, page->index); + fuse_wait_on_folio_writeback(inode, folio); attr_ver = fuse_get_attr_version(fm->fc); @@ -887,25 +909,24 @@ static int fuse_do_readpage(struct file *file, struct page *page) if (res < desc.length) fuse_short_read(inode, attr_ver, res, &ia.ap); - SetPageUptodate(page); + folio_mark_uptodate(folio); return 0; } static int fuse_read_folio(struct file *file, struct folio *folio) { - struct page *page = &folio->page; - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; int err; err = -EIO; if (fuse_is_bad(inode)) goto out; - err = fuse_do_readpage(file, page); + err = fuse_do_readfolio(file, folio); fuse_invalidate_atime(inode); out: - unlock_page(page); + folio_unlock(folio); return err; } @@ -919,8 +940,8 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args, size_t num_read = args->out_args[0].size; struct address_space *mapping = NULL; - for (i = 0; mapping == NULL && i < ap->num_pages; i++) - mapping = ap->pages[i]->mapping; + for (i = 0; mapping == NULL && i < ap->num_folios; i++) + mapping = ap->folios[i]->mapping; if (mapping) { struct inode *inode = mapping->host; @@ -934,12 +955,8 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args, fuse_invalidate_atime(inode); } - for (i = 0; i < ap->num_pages; i++) { - struct folio *folio = page_folio(ap->pages[i]); - - folio_end_read(folio, !err); - folio_put(folio); - } + for (i = 0; i < ap->num_folios; i++) + folio_end_read(ap->folios[i], !err); if (ia->ff) fuse_file_put(ia->ff, false); @@ -951,8 +968,9 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file) struct fuse_file *ff = file->private_data; struct fuse_mount *fm = ff->fm; struct fuse_args_pages *ap = &ia->ap; - loff_t pos = page_offset(ap->pages[0]); - size_t count = ap->num_pages << PAGE_SHIFT; + loff_t pos = folio_pos(ap->folios[0]); + /* Currently, all folios in FUSE are one page */ + size_t count = ap->num_folios << PAGE_SHIFT; ssize_t res; int err; @@ -963,7 +981,7 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file) /* Don't overflow end offset */ if (pos + (count - 1) == LLONG_MAX) { count--; - ap->descs[ap->num_pages - 1].length--; + ap->descs[ap->num_folios - 1].length--; } WARN_ON((loff_t) (pos + count) < 0); @@ -985,18 +1003,36 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file) static void fuse_readahead(struct readahead_control *rac) { struct inode *inode = rac->mapping->host; + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = get_fuse_conn(inode); - unsigned int i, max_pages, nr_pages = 0; + unsigned int max_pages, nr_pages; + pgoff_t first = readahead_index(rac); + pgoff_t last = first + readahead_count(rac) - 1; if (fuse_is_bad(inode)) return; + wait_event(fi->page_waitq, !fuse_range_is_writeback(inode, first, last)); + max_pages = min_t(unsigned int, fc->max_pages, fc->max_read / PAGE_SIZE); - for (;;) { + /* + * This is only accurate the first time through, since readahead_folio() + * doesn't update readahead_count() from the previous folio until the + * next call. Grab nr_pages here so we know how many pages we're going + * to have to process. This means that we will exit here with + * readahead_count() == folio_nr_pages(last_folio), but we will have + * consumed all of the folios, and read_pages() will call + * readahead_folio() again which will clean up the rac. + */ + nr_pages = readahead_count(rac); + + while (nr_pages) { struct fuse_io_args *ia; struct fuse_args_pages *ap; + struct folio *folio; + unsigned cur_pages = min(max_pages, nr_pages); if (fc->num_background >= fc->congestion_threshold && rac->ra->async_size >= readahead_count(rac)) @@ -1006,23 +1042,19 @@ static void fuse_readahead(struct readahead_control *rac) */ break; - nr_pages = readahead_count(rac) - nr_pages; - if (nr_pages > max_pages) - nr_pages = max_pages; - if (nr_pages == 0) - break; - ia = fuse_io_alloc(NULL, nr_pages); + ia = fuse_io_alloc(NULL, cur_pages); if (!ia) return; ap = &ia->ap; - nr_pages = __readahead_batch(rac, ap->pages, nr_pages); - for (i = 0; i < nr_pages; i++) { - fuse_wait_on_page_writeback(inode, - readahead_index(rac) + i); - ap->descs[i].length = PAGE_SIZE; + + while (ap->num_folios < cur_pages) { + folio = readahead_folio(rac); + ap->folios[ap->num_folios] = folio; + ap->descs[ap->num_folios].length = folio_size(folio); + ap->num_folios++; } - ap->num_pages = nr_pages; fuse_send_readpages(ia, rac->file); + nr_pages -= cur_pages; } } @@ -1139,8 +1171,8 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia, bool short_write; int err; - for (i = 0; i < ap->num_pages; i++) - fuse_wait_on_page_writeback(inode, ap->pages[i]->index); + for (i = 0; i < ap->num_folios; i++) + fuse_wait_on_folio_writeback(inode, ap->folios[i]); fuse_write_args_fill(ia, ff, pos, count); ia->write.in.flags = fuse_write_flags(iocb); @@ -1154,24 +1186,24 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia, short_write = ia->write.out.size < count; offset = ap->descs[0].offset; count = ia->write.out.size; - for (i = 0; i < ap->num_pages; i++) { - struct page *page = ap->pages[i]; + for (i = 0; i < ap->num_folios; i++) { + struct folio *folio = ap->folios[i]; if (err) { - ClearPageUptodate(page); + folio_clear_uptodate(folio); } else { - if (count >= PAGE_SIZE - offset) - count -= PAGE_SIZE - offset; + if (count >= folio_size(folio) - offset) + count -= folio_size(folio) - offset; else { if (short_write) - ClearPageUptodate(page); + folio_clear_uptodate(folio); count = 0; } offset = 0; } - if (ia->write.page_locked && (i == ap->num_pages - 1)) - unlock_page(page); - put_page(page); + if (ia->write.folio_locked && (i == ap->num_folios - 1)) + folio_unlock(folio); + folio_put(folio); } return err; @@ -1185,6 +1217,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia, struct fuse_args_pages *ap = &ia->ap; struct fuse_conn *fc = get_fuse_conn(mapping->host); unsigned offset = pos & (PAGE_SIZE - 1); + unsigned int nr_pages = 0; size_t count = 0; int err; @@ -1193,7 +1226,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia, do { size_t tmp; - struct page *page; + struct folio *folio; pgoff_t index = pos >> PAGE_SHIFT; size_t bytes = min_t(size_t, PAGE_SIZE - offset, iov_iter_count(ii)); @@ -1205,27 +1238,30 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia, if (fault_in_iov_iter_readable(ii, bytes)) break; - err = -ENOMEM; - page = grab_cache_page_write_begin(mapping, index); - if (!page) + folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN, + mapping_gfp_mask(mapping)); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); break; + } if (mapping_writably_mapped(mapping)) - flush_dcache_page(page); + flush_dcache_folio(folio); - tmp = copy_page_from_iter_atomic(page, offset, bytes, ii); - flush_dcache_page(page); + tmp = copy_folio_from_iter_atomic(folio, offset, bytes, ii); + flush_dcache_folio(folio); if (!tmp) { - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); goto again; } err = 0; - ap->pages[ap->num_pages] = page; - ap->descs[ap->num_pages].length = tmp; - ap->num_pages++; + ap->folios[ap->num_folios] = folio; + ap->descs[ap->num_folios].length = tmp; + ap->num_folios++; + nr_pages++; count += tmp; pos += tmp; @@ -1235,18 +1271,18 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia, /* If we copied full page, mark it uptodate */ if (tmp == PAGE_SIZE) - SetPageUptodate(page); + folio_mark_uptodate(folio); - if (PageUptodate(page)) { - unlock_page(page); + if (folio_test_uptodate(folio)) { + folio_unlock(folio); } else { - ia->write.page_locked = true; + ia->write.folio_locked = true; break; } if (!fc->big_writes) break; } while (iov_iter_count(ii) && count < fc->max_write && - ap->num_pages < max_pages && offset == 0); + nr_pages < max_pages && offset == 0); return count > 0 ? count : err; } @@ -1280,8 +1316,8 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii) unsigned int nr_pages = fuse_wr_pages(pos, iov_iter_count(ii), fc->max_pages); - ap->pages = fuse_pages_alloc(nr_pages, GFP_KERNEL, &ap->descs); - if (!ap->pages) { + ap->folios = fuse_folios_alloc(nr_pages, GFP_KERNEL, &ap->descs); + if (!ap->folios) { err = -ENOMEM; break; } @@ -1303,7 +1339,7 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii) err = -EIO; } } - kfree(ap->pages); + kfree(ap->folios); } while (!err && iov_iter_count(ii)); fuse_write_update_attr(inode, pos, res); @@ -1430,11 +1466,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) task_io_account_write(count); - err = file_remove_privs(file); - if (err) - goto out; - - err = file_update_time(file); + err = kiocb_modified(iocb); if (err) goto out; @@ -1468,35 +1500,57 @@ static inline size_t fuse_get_frag_size(const struct iov_iter *ii, static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, size_t *nbytesp, int write, - unsigned int max_pages) + unsigned int max_pages, + bool use_pages_for_kvec_io) { + bool flush_or_invalidate = false; + unsigned int nr_pages = 0; size_t nbytes = 0; /* # bytes already packed in req */ ssize_t ret = 0; - /* Special case for kernel I/O: can copy directly into the buffer */ + /* Special case for kernel I/O: can copy directly into the buffer. + * However if the implementation of fuse_conn requires pages instead of + * pointer (e.g., virtio-fs), use iov_iter_extract_pages() instead. + */ if (iov_iter_is_kvec(ii)) { - unsigned long user_addr = fuse_get_user_addr(ii); - size_t frag_size = fuse_get_frag_size(ii, *nbytesp); + void *user_addr = (void *)fuse_get_user_addr(ii); - if (write) - ap->args.in_args[1].value = (void *) user_addr; - else - ap->args.out_args[0].value = (void *) user_addr; + if (!use_pages_for_kvec_io) { + size_t frag_size = fuse_get_frag_size(ii, *nbytesp); - iov_iter_advance(ii, frag_size); - *nbytesp = frag_size; - return 0; + if (write) + ap->args.in_args[1].value = user_addr; + else + ap->args.out_args[0].value = user_addr; + + iov_iter_advance(ii, frag_size); + *nbytesp = frag_size; + return 0; + } + + if (is_vmalloc_addr(user_addr)) { + ap->args.vmap_base = user_addr; + flush_or_invalidate = true; + } } - while (nbytes < *nbytesp && ap->num_pages < max_pages) { - unsigned npages; + /* + * Until there is support for iov_iter_extract_folios(), we have to + * manually extract pages using iov_iter_extract_pages() and then + * copy that to a folios array. + */ + struct page **pages = kzalloc(max_pages * sizeof(struct page *), + GFP_KERNEL); + if (!pages) + return -ENOMEM; + + while (nbytes < *nbytesp && nr_pages < max_pages) { + unsigned nfolios, i; size_t start; - struct page **pt_pages; - pt_pages = &ap->pages[ap->num_pages]; - ret = iov_iter_extract_pages(ii, &pt_pages, + ret = iov_iter_extract_pages(ii, &pages, *nbytesp - nbytes, - max_pages - ap->num_pages, + max_pages - nr_pages, 0, &start); if (ret < 0) break; @@ -1504,16 +1558,25 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, nbytes += ret; ret += start; - npages = DIV_ROUND_UP(ret, PAGE_SIZE); + /* Currently, all folios in FUSE are one page */ + nfolios = DIV_ROUND_UP(ret, PAGE_SIZE); - ap->descs[ap->num_pages].offset = start; - fuse_page_descs_length_init(ap->descs, ap->num_pages, npages); + ap->descs[ap->num_folios].offset = start; + fuse_folio_descs_length_init(ap->descs, ap->num_folios, nfolios); + for (i = 0; i < nfolios; i++) + ap->folios[i + ap->num_folios] = page_folio(pages[i]); - ap->num_pages += npages; - ap->descs[ap->num_pages - 1].length -= + ap->num_folios += nfolios; + ap->descs[ap->num_folios - 1].length -= (PAGE_SIZE - ret) & (PAGE_SIZE - 1); + nr_pages += nfolios; } + kfree(pages); + + if (write && flush_or_invalidate) + flush_kernel_vmap_range(ap->args.vmap_base, nbytes); + ap->args.invalidate_vmap = !write && flush_or_invalidate; ap->args.is_pinned = iov_iter_extract_will_pin(ii); ap->args.user_pages = true; if (write) @@ -1582,7 +1645,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, size_t nbytes = min(count, nmax); err = fuse_get_user_pages(&ia->ap, iter, &nbytes, write, - max_pages); + max_pages, fc->use_pages_for_kvec_io); if (err && !nbytes) break; @@ -1596,7 +1659,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, } if (!io->async || nres < 0) { - fuse_release_user_pages(&ia->ap, io->should_dirty); + fuse_release_user_pages(&ia->ap, nres, io->should_dirty); fuse_io_free(ia); } ia = NULL; @@ -1650,7 +1713,7 @@ static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) { ssize_t res; - if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) { + if (!is_sync_kiocb(iocb)) { res = fuse_direct_IO(iocb, to); } else { struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); @@ -1664,7 +1727,6 @@ static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct inode *inode = file_inode(iocb->ki_filp); - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); ssize_t res; bool exclusive; @@ -1672,9 +1734,11 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) res = generic_write_checks(iocb, from); if (res > 0) { task_io_account_write(res); - if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) { + if (!is_sync_kiocb(iocb)) { res = fuse_direct_IO(iocb, from); } else { + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); + res = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE); fuse_write_update_attr(inode, iocb->ki_pos, res); @@ -1760,21 +1824,21 @@ static void fuse_writepage_free(struct fuse_writepage_args *wpa) if (wpa->bucket) fuse_sync_bucket_dec(wpa->bucket); - for (i = 0; i < ap->num_pages; i++) - __free_page(ap->pages[i]); + for (i = 0; i < ap->num_folios; i++) + folio_put(ap->folios[i]); fuse_file_put(wpa->ia.ff, false); - kfree(ap->pages); + kfree(ap->folios); kfree(wpa); } -static void fuse_writepage_finish_stat(struct inode *inode, struct page *page) +static void fuse_writepage_finish_stat(struct inode *inode, struct folio *folio) { struct backing_dev_info *bdi = inode_to_bdi(inode); dec_wb_stat(&bdi->wb, WB_WRITEBACK); - dec_node_page_state(page, NR_WRITEBACK_TEMP); + node_stat_sub_folio(folio, NR_WRITEBACK_TEMP); wb_writeout_inc(&bdi->wb); } @@ -1785,8 +1849,8 @@ static void fuse_writepage_finish(struct fuse_writepage_args *wpa) struct fuse_inode *fi = get_fuse_inode(inode); int i; - for (i = 0; i < ap->num_pages; i++) - fuse_writepage_finish_stat(inode, ap->pages[i]); + for (i = 0; i < ap->num_folios; i++) + fuse_writepage_finish_stat(inode, ap->folios[i]); wake_up(&fi->page_waitq); } @@ -1801,7 +1865,8 @@ __acquires(fi->lock) struct fuse_inode *fi = get_fuse_inode(wpa->inode); struct fuse_write_in *inarg = &wpa->ia.write.in; struct fuse_args *args = &wpa->ia.ap.args; - __u64 data_size = wpa->ia.ap.num_pages * PAGE_SIZE; + /* Currently, all folios in FUSE are one page */ + __u64 data_size = wpa->ia.ap.num_folios * PAGE_SIZE; int err; fi->writectr++; @@ -1841,7 +1906,8 @@ __acquires(fi->lock) for (aux = wpa->next; aux; aux = next) { next = aux->next; aux->next = NULL; - fuse_writepage_finish_stat(aux->inode, aux->ia.ap.pages[0]); + fuse_writepage_finish_stat(aux->inode, + aux->ia.ap.folios[0]); fuse_writepage_free(aux); } @@ -1876,11 +1942,11 @@ static struct fuse_writepage_args *fuse_insert_writeback(struct rb_root *root, struct fuse_writepage_args *wpa) { pgoff_t idx_from = wpa->ia.write.in.offset >> PAGE_SHIFT; - pgoff_t idx_to = idx_from + wpa->ia.ap.num_pages - 1; + pgoff_t idx_to = idx_from + wpa->ia.ap.num_folios - 1; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; - WARN_ON(!wpa->ia.ap.num_pages); + WARN_ON(!wpa->ia.ap.num_folios); while (*p) { struct fuse_writepage_args *curr; pgoff_t curr_index; @@ -1891,7 +1957,7 @@ static struct fuse_writepage_args *fuse_insert_writeback(struct rb_root *root, WARN_ON(curr->inode != wpa->inode); curr_index = curr->ia.write.in.offset >> PAGE_SHIFT; - if (idx_from >= curr_index + curr->ia.ap.num_pages) + if (idx_from >= curr_index + curr->ia.ap.num_folios) p = &(*p)->rb_right; else if (idx_to < curr_index) p = &(*p)->rb_left; @@ -2023,9 +2089,9 @@ static struct fuse_writepage_args *fuse_writepage_args_alloc(void) wpa = kzalloc(sizeof(*wpa), GFP_NOFS); if (wpa) { ap = &wpa->ia.ap; - ap->num_pages = 0; - ap->pages = fuse_pages_alloc(1, GFP_NOFS, &ap->descs); - if (!ap->pages) { + ap->num_folios = 0; + ap->folios = fuse_folios_alloc(1, GFP_NOFS, &ap->descs); + if (!ap->folios) { kfree(wpa); wpa = NULL; } @@ -2049,19 +2115,19 @@ static void fuse_writepage_add_to_bucket(struct fuse_conn *fc, } static void fuse_writepage_args_page_fill(struct fuse_writepage_args *wpa, struct folio *folio, - struct folio *tmp_folio, uint32_t page_index) + struct folio *tmp_folio, uint32_t folio_index) { struct inode *inode = folio->mapping->host; struct fuse_args_pages *ap = &wpa->ia.ap; folio_copy(tmp_folio, folio); - ap->pages[page_index] = &tmp_folio->page; - ap->descs[page_index].offset = 0; - ap->descs[page_index].length = PAGE_SIZE; + ap->folios[folio_index] = tmp_folio; + ap->descs[folio_index].offset = 0; + ap->descs[folio_index].length = PAGE_SIZE; inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK); - inc_node_page_state(&tmp_folio->page, NR_WRITEBACK_TEMP); + node_stat_add_folio(tmp_folio, NR_WRITEBACK_TEMP); } static struct fuse_writepage_args *fuse_writepage_args_setup(struct folio *folio, @@ -2115,7 +2181,7 @@ static int fuse_writepage_locked(struct folio *folio) goto err_writepage_args; ap = &wpa->ia.ap; - ap->num_pages = 1; + ap->num_folios = 1; folio_start_writeback(folio); fuse_writepage_args_page_fill(wpa, folio, tmp_folio, 0); @@ -2143,32 +2209,32 @@ struct fuse_fill_wb_data { struct fuse_writepage_args *wpa; struct fuse_file *ff; struct inode *inode; - struct page **orig_pages; - unsigned int max_pages; + struct folio **orig_folios; + unsigned int max_folios; }; static bool fuse_pages_realloc(struct fuse_fill_wb_data *data) { struct fuse_args_pages *ap = &data->wpa->ia.ap; struct fuse_conn *fc = get_fuse_conn(data->inode); - struct page **pages; - struct fuse_page_desc *descs; - unsigned int npages = min_t(unsigned int, - max_t(unsigned int, data->max_pages * 2, - FUSE_DEFAULT_MAX_PAGES_PER_REQ), + struct folio **folios; + struct fuse_folio_desc *descs; + unsigned int nfolios = min_t(unsigned int, + max_t(unsigned int, data->max_folios * 2, + FUSE_DEFAULT_MAX_PAGES_PER_REQ), fc->max_pages); - WARN_ON(npages <= data->max_pages); + WARN_ON(nfolios <= data->max_folios); - pages = fuse_pages_alloc(npages, GFP_NOFS, &descs); - if (!pages) + folios = fuse_folios_alloc(nfolios, GFP_NOFS, &descs); + if (!folios) return false; - memcpy(pages, ap->pages, sizeof(struct page *) * ap->num_pages); - memcpy(descs, ap->descs, sizeof(struct fuse_page_desc) * ap->num_pages); - kfree(ap->pages); - ap->pages = pages; + memcpy(folios, ap->folios, sizeof(struct folio *) * ap->num_folios); + memcpy(descs, ap->descs, sizeof(struct fuse_folio_desc) * ap->num_folios); + kfree(ap->folios); + ap->folios = folios; ap->descs = descs; - data->max_pages = npages; + data->max_folios = nfolios; return true; } @@ -2178,7 +2244,7 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) struct fuse_writepage_args *wpa = data->wpa; struct inode *inode = data->inode; struct fuse_inode *fi = get_fuse_inode(inode); - int num_pages = wpa->ia.ap.num_pages; + int num_folios = wpa->ia.ap.num_folios; int i; spin_lock(&fi->lock); @@ -2186,8 +2252,8 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) fuse_flush_writepages(inode); spin_unlock(&fi->lock); - for (i = 0; i < num_pages; i++) - end_page_writeback(data->orig_pages[i]); + for (i = 0; i < num_folios; i++) + folio_end_writeback(data->orig_folios[i]); } /* @@ -2198,15 +2264,15 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) * swapping the new temp page with the old one. */ static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa, - struct page *page) + struct folio *folio) { struct fuse_inode *fi = get_fuse_inode(new_wpa->inode); struct fuse_writepage_args *tmp; struct fuse_writepage_args *old_wpa; struct fuse_args_pages *new_ap = &new_wpa->ia.ap; - WARN_ON(new_ap->num_pages != 0); - new_ap->num_pages = 1; + WARN_ON(new_ap->num_folios != 0); + new_ap->num_folios = 1; spin_lock(&fi->lock); old_wpa = fuse_insert_writeback(&fi->writepages, new_wpa); @@ -2220,9 +2286,9 @@ static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa, WARN_ON(tmp->inode != new_wpa->inode); curr_index = tmp->ia.write.in.offset >> PAGE_SHIFT; - if (curr_index == page->index) { - WARN_ON(tmp->ia.ap.num_pages != 1); - swap(tmp->ia.ap.pages[0], new_ap->pages[0]); + if (curr_index == folio->index) { + WARN_ON(tmp->ia.ap.num_folios != 1); + swap(tmp->ia.ap.folios[0], new_ap->folios[0]); break; } } @@ -2235,18 +2301,19 @@ static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa, spin_unlock(&fi->lock); if (tmp) { - fuse_writepage_finish_stat(new_wpa->inode, new_ap->pages[0]); + fuse_writepage_finish_stat(new_wpa->inode, + folio); fuse_writepage_free(new_wpa); } return false; } -static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page, +static bool fuse_writepage_need_send(struct fuse_conn *fc, struct folio *folio, struct fuse_args_pages *ap, struct fuse_fill_wb_data *data) { - WARN_ON(!ap->num_pages); + WARN_ON(!ap->num_folios); /* * Being under writeback is unlikely but possible. For example direct @@ -2254,23 +2321,23 @@ static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page, * the pages are faulted with get_user_pages(), and then after the read * completed. */ - if (fuse_page_is_writeback(data->inode, page->index)) + if (fuse_folio_is_writeback(data->inode, folio)) return true; /* Reached max pages */ - if (ap->num_pages == fc->max_pages) + if (ap->num_folios == fc->max_pages) return true; /* Reached max write bytes */ - if ((ap->num_pages + 1) * PAGE_SIZE > fc->max_write) + if ((ap->num_folios + 1) * PAGE_SIZE > fc->max_write) return true; /* Discontinuity */ - if (data->orig_pages[ap->num_pages - 1]->index + 1 != page->index) + if (data->orig_folios[ap->num_folios - 1]->index + 1 != folio_index(folio)) return true; /* Need to grow the pages array? If so, did the expansion fail? */ - if (ap->num_pages == data->max_pages && !fuse_pages_realloc(data)) + if (ap->num_folios == data->max_folios && !fuse_pages_realloc(data)) return true; return false; @@ -2295,7 +2362,7 @@ static int fuse_writepages_fill(struct folio *folio, goto out_unlock; } - if (wpa && fuse_writepage_need_send(fc, &folio->page, ap, data)) { + if (wpa && fuse_writepage_need_send(fc, folio, ap, data)) { fuse_writepages_send(data); data->wpa = NULL; } @@ -2314,7 +2381,7 @@ static int fuse_writepages_fill(struct folio *folio, * This is ensured by holding the page lock in page_mkwrite() while * checking fuse_page_is_writeback(). We already hold the page lock * since clear_page_dirty_for_io() and keep it held until we add the - * request to the fi->writepages list and increment ap->num_pages. + * request to the fi->writepages list and increment ap->num_folios. * After this fuse_page_is_writeback() will indicate that the page is * under writeback, so we can release the page lock. */ @@ -2326,13 +2393,13 @@ static int fuse_writepages_fill(struct folio *folio, goto out_unlock; } fuse_file_get(wpa->ia.ff); - data->max_pages = 1; + data->max_folios = 1; ap = &wpa->ia.ap; } folio_start_writeback(folio); - fuse_writepage_args_page_fill(wpa, folio, tmp_folio, ap->num_pages); - data->orig_pages[ap->num_pages] = &folio->page; + fuse_writepage_args_page_fill(wpa, folio, tmp_folio, ap->num_folios); + data->orig_folios[ap->num_folios] = folio; err = 0; if (data->wpa) { @@ -2341,9 +2408,9 @@ static int fuse_writepages_fill(struct folio *folio, * fuse_page_is_writeback(). */ spin_lock(&fi->lock); - ap->num_pages++; + ap->num_folios++; spin_unlock(&fi->lock); - } else if (fuse_writepage_add(wpa, &folio->page)) { + } else if (fuse_writepage_add(wpa, folio)) { data->wpa = wpa; } else { folio_end_writeback(folio); @@ -2375,21 +2442,21 @@ static int fuse_writepages(struct address_space *mapping, data.ff = NULL; err = -ENOMEM; - data.orig_pages = kcalloc(fc->max_pages, - sizeof(struct page *), - GFP_NOFS); - if (!data.orig_pages) + data.orig_folios = kcalloc(fc->max_pages, + sizeof(struct folio *), + GFP_NOFS); + if (!data.orig_folios) goto out; err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data); if (data.wpa) { - WARN_ON(!data.wpa->ia.ap.num_pages); + WARN_ON(!data.wpa->ia.ap.num_folios); fuse_writepages_send(&data); } if (data.ff) fuse_file_put(data.ff, false); - kfree(data.orig_pages); + kfree(data.orig_folios); out: return err; } @@ -2429,7 +2496,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, folio_zero_segment(folio, 0, off); goto success; } - err = fuse_do_readpage(file, &folio->page); + err = fuse_do_readfolio(file, folio); if (err) goto cleanup; success: @@ -2518,17 +2585,17 @@ static void fuse_vma_close(struct vm_area_struct *vma) */ static vm_fault_t fuse_page_mkwrite(struct vm_fault *vmf) { - struct page *page = vmf->page; + struct folio *folio = page_folio(vmf->page); struct inode *inode = file_inode(vmf->vma->vm_file); file_update_time(vmf->vma->vm_file); - lock_page(page); - if (page->mapping != inode->i_mapping) { - unlock_page(page); + folio_lock(folio); + if (folio->mapping != inode->i_mapping) { + folio_unlock(folio); return VM_FAULT_NOPAGE; } - fuse_wait_on_page_writeback(inode, page->index); + fuse_wait_on_folio_writeback(inode, folio); return VM_FAULT_LOCKED; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e6cc3d552b138..74744c6f28600 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -35,9 +35,6 @@ /** Default max number of pages that can be used in a single read request */ #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 -/** Maximum of max_pages received in init_out */ -#define FUSE_MAX_MAX_PAGES 256 - /** Bias for fi->writectr, meaning new writepages must not be sent */ #define FUSE_NOWRITE INT_MIN @@ -47,6 +44,9 @@ /** Number of dentries for each connection in the control filesystem */ #define FUSE_CTL_NUM_DENTRIES 5 +/** Maximum of max_pages received in init_out */ +extern unsigned int fuse_max_pages_limit; + /** List of active connections */ extern struct list_head fuse_conn_list; @@ -285,8 +285,8 @@ struct fuse_arg { void *value; }; -/** FUSE page descriptor */ -struct fuse_page_desc { +/** FUSE folio descriptor */ +struct fuse_folio_desc { unsigned int length; unsigned int offset; }; @@ -309,16 +309,19 @@ struct fuse_args { bool may_block:1; bool is_ext:1; bool is_pinned:1; + bool invalidate_vmap:1; struct fuse_in_arg in_args[3]; struct fuse_arg out_args[2]; void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error); + /* Used for kvec iter backed by vmalloc address */ + void *vmap_base; }; struct fuse_args_pages { struct fuse_args args; - struct page **pages; - struct fuse_page_desc *descs; - unsigned int num_pages; + struct folio **folios; + struct fuse_folio_desc *descs; + unsigned int num_folios; }; struct fuse_release_args { @@ -857,6 +860,9 @@ struct fuse_conn { /** Passthrough support for read/write IO */ unsigned int passthrough:1; + /* Use pages instead of pointer for kernel I/O */ + unsigned int use_pages_for_kvec_io:1; + /** Maximum stack depth for passthrough backing files */ int max_stack_depth; @@ -884,6 +890,9 @@ struct fuse_conn { /** Version counter for attribute changes */ atomic64_t attr_version; + /** Version counter for evict inode */ + atomic64_t evict_ctr; + /** Called on final put */ void (*release)(struct fuse_conn *); @@ -978,6 +987,11 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc) return atomic64_read(&fc->attr_version); } +static inline u64 fuse_get_evict_ctr(struct fuse_conn *fc) +{ + return atomic64_read(&fc->evict_ctr); +} + static inline bool fuse_stale_inode(const struct inode *inode, int generation, struct fuse_attr *attr) { @@ -995,25 +1009,25 @@ static inline bool fuse_is_bad(struct inode *inode) return unlikely(test_bit(FUSE_I_BAD, &get_fuse_inode(inode)->state)); } -static inline struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags, - struct fuse_page_desc **desc) +static inline struct folio **fuse_folios_alloc(unsigned int nfolios, gfp_t flags, + struct fuse_folio_desc **desc) { - struct page **pages; + struct folio **folios; - pages = kzalloc(npages * (sizeof(struct page *) + - sizeof(struct fuse_page_desc)), flags); - *desc = (void *) (pages + npages); + folios = kzalloc(nfolios * (sizeof(struct folio *) + + sizeof(struct fuse_folio_desc)), flags); + *desc = (void *) (folios + nfolios); - return pages; + return folios; } -static inline void fuse_page_descs_length_init(struct fuse_page_desc *descs, - unsigned int index, - unsigned int nr_pages) +static inline void fuse_folio_descs_length_init(struct fuse_folio_desc *descs, + unsigned int index, + unsigned int nr_folios) { int i; - for (i = index; i < index + nr_pages; i++) + for (i = index; i < index + nr_folios; i++) descs[i].length = PAGE_SIZE - descs[i].offset; } @@ -1037,7 +1051,8 @@ extern const struct dentry_operations fuse_root_dentry_operations; */ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, - u64 attr_valid, u64 attr_version); + u64 attr_valid, u64 attr_version, + u64 evict_ctr); int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name, struct fuse_entry_out *outarg, struct inode **inode); @@ -1062,7 +1077,7 @@ struct fuse_io_args { struct { struct fuse_write_in in; struct fuse_write_out out; - bool page_locked; + bool folio_locked; } write; }; struct fuse_args_pages ap; @@ -1127,7 +1142,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, struct fuse_statx *sx, - u64 attr_valid, u32 cache_mask); + u64 attr_valid, u32 cache_mask, + u64 evict_ctr); u32 fuse_get_cache_mask(struct inode *inode); @@ -1480,4 +1496,12 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe, size_t len, unsigned int flags); ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma); +#ifdef CONFIG_SYSCTL +extern int fuse_sysctl_register(void); +extern void fuse_sysctl_unregister(void); +#else +#define fuse_sysctl_register() (0) +#define fuse_sysctl_unregister() do { } while (0) +#endif /* CONFIG_SYSCTL */ + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index fd3321e29a3e5..3ce4f4e81d09e 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -35,6 +35,8 @@ DEFINE_MUTEX(fuse_mutex); static int set_global_limit(const char *val, const struct kernel_param *kp); +unsigned int fuse_max_pages_limit = 256; + unsigned max_user_bgreq; module_param_call(max_user_bgreq, set_global_limit, param_get_uint, &max_user_bgreq, 0644); @@ -173,6 +175,14 @@ static void fuse_evict_inode(struct inode *inode) fuse_cleanup_submount_lookup(fc, fi->submount_lookup); fi->submount_lookup = NULL; } + /* + * Evict of non-deleted inode may race with outstanding + * LOOKUP/READDIRPLUS requests and result in inconsistency when + * the request finishes. Deal with that here by bumping a + * counter that can be compared to the starting value. + */ + if (inode->i_nlink > 0) + atomic64_inc(&fc->evict_ctr); } if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) { WARN_ON(fi->iocachectr != 0); @@ -206,17 +216,30 @@ static ino_t fuse_squash_ino(u64 ino64) void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, struct fuse_statx *sx, - u64 attr_valid, u32 cache_mask) + u64 attr_valid, u32 cache_mask, + u64 evict_ctr) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); lockdep_assert_held(&fi->lock); + /* + * Clear basic stats from invalid mask. + * + * Don't do this if this is coming from a fuse_iget() call and there + * might have been a racing evict which would've invalidated the result + * if the attr_version would've been preserved. + * + * !evict_ctr -> this is create + * fi->attr_version != 0 -> this is not a new inode + * evict_ctr == fuse_get_evict_ctr() -> no evicts while during request + */ + if (!evict_ctr || fi->attr_version || evict_ctr == fuse_get_evict_ctr(fc)) + set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0); + fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->i_time = attr_valid; - /* Clear basic stats from invalid mask */ - set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0); inode->i_ino = fuse_squash_ino(attr->ino); inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); @@ -295,9 +318,9 @@ u32 fuse_get_cache_mask(struct inode *inode) return STATX_MTIME | STATX_CTIME | STATX_SIZE; } -void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, - struct fuse_statx *sx, - u64 attr_valid, u64 attr_version) +static void fuse_change_attributes_i(struct inode *inode, struct fuse_attr *attr, + struct fuse_statx *sx, u64 attr_valid, + u64 attr_version, u64 evict_ctr) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); @@ -331,7 +354,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, } old_mtime = inode_get_mtime(inode); - fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask); + fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask, + evict_ctr); oldsize = inode->i_size; /* @@ -372,6 +396,13 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, fuse_dax_dontcache(inode, attr->flags); } +void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, + struct fuse_statx *sx, u64 attr_valid, + u64 attr_version) +{ + fuse_change_attributes_i(inode, attr, sx, attr_valid, attr_version, 0); +} + static void fuse_init_submount_lookup(struct fuse_submount_lookup *sl, u64 nodeid) { @@ -426,7 +457,8 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp) struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, - u64 attr_valid, u64 attr_version) + u64 attr_valid, u64 attr_version, + u64 evict_ctr) { struct inode *inode; struct fuse_inode *fi; @@ -487,8 +519,8 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, fi->nlookup++; spin_unlock(&fi->lock); done: - fuse_change_attributes(inode, attr, NULL, attr_valid, attr_version); - + fuse_change_attributes_i(inode, attr, NULL, attr_valid, attr_version, + evict_ctr); return inode; } @@ -940,11 +972,12 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm, fc->initialized = 0; fc->connected = 1; atomic64_set(&fc->attr_version, 1); + atomic64_set(&fc->evict_ctr, 1); get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); fc->pid_ns = get_pid_ns(task_active_pid_ns(current)); fc->user_ns = get_user_ns(user_ns); fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ; - fc->max_pages_limit = FUSE_MAX_MAX_PAGES; + fc->max_pages_limit = fuse_max_pages_limit; if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) fuse_backing_files_init(fc); @@ -1001,7 +1034,7 @@ static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode) attr.mode = mode; attr.ino = FUSE_ROOT_ID; attr.nlink = 1; - return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0); + return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0, 0); } struct fuse_inode_handle { @@ -1610,7 +1643,8 @@ static int fuse_fill_super_submount(struct super_block *sb, return -ENOMEM; fuse_fill_attr_from_inode(&root_attr, parent_fi); - root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0); + root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0, + fuse_get_evict_ctr(fm->fc)); /* * This inode is just a duplicate, so it is not looked up and * its nlookup should not be incremented. fuse_iget() does @@ -2063,8 +2097,14 @@ static int __init fuse_fs_init(void) if (err) goto out3; + err = fuse_sysctl_register(); + if (err) + goto out4; + return 0; + out4: + unregister_filesystem(&fuse_fs_type); out3: unregister_fuseblk(); out2: @@ -2075,6 +2115,7 @@ static int __init fuse_fs_init(void) static void fuse_fs_cleanup(void) { + fuse_sysctl_unregister(); unregister_filesystem(&fuse_fs_type); unregister_fuseblk(); diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c index 572ce8a82ceb9..2d9abf48828f9 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -10,6 +10,8 @@ #include #include +#define FUSE_VERITY_ENABLE_ARG_MAX_PAGES 256 + static ssize_t fuse_send_ioctl(struct fuse_mount *fm, struct fuse_args *args, struct fuse_ioctl_out *outarg) { @@ -140,7 +142,7 @@ static int fuse_setup_enable_verity(unsigned long arg, struct iovec *iov, { struct fsverity_enable_arg enable; struct fsverity_enable_arg __user *uarg = (void __user *)arg; - const __u32 max_buffer_len = FUSE_MAX_MAX_PAGES * PAGE_SIZE; + const __u32 max_buffer_len = FUSE_VERITY_ENABLE_ARG_MAX_PAGES * PAGE_SIZE; if (copy_from_user(&enable, uarg, sizeof(enable))) return -EFAULT; @@ -249,12 +251,12 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); err = -ENOMEM; - ap.pages = fuse_pages_alloc(fm->fc->max_pages, GFP_KERNEL, &ap.descs); + ap.folios = fuse_folios_alloc(fm->fc->max_pages, GFP_KERNEL, &ap.descs); iov_page = (struct iovec *) __get_free_page(GFP_KERNEL); - if (!ap.pages || !iov_page) + if (!ap.folios || !iov_page) goto out; - fuse_page_descs_length_init(ap.descs, 0, fm->fc->max_pages); + fuse_folio_descs_length_init(ap.descs, 0, fm->fc->max_pages); /* * If restricted, initialize IO parameters as encoded in @cmd. @@ -304,14 +306,13 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, err = -ENOMEM; if (max_pages > fm->fc->max_pages) goto out; - while (ap.num_pages < max_pages) { - ap.pages[ap.num_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); - if (!ap.pages[ap.num_pages]) + while (ap.num_folios < max_pages) { + ap.folios[ap.num_folios] = folio_alloc(GFP_KERNEL | __GFP_HIGHMEM, 0); + if (!ap.folios[ap.num_folios]) goto out; - ap.num_pages++; + ap.num_folios++; } - /* okay, let's send it to the client */ ap.args.opcode = FUSE_IOCTL; ap.args.nodeid = ff->nodeid; @@ -325,8 +326,8 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; iov_iter_init(&ii, ITER_SOURCE, in_iov, in_iovs, in_size); - for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= ap.num_pages); i++) { - c = copy_page_from_iter(ap.pages[i], 0, PAGE_SIZE, &ii); + for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= ap.num_folios); i++) { + c = copy_folio_from_iter(ap.folios[i], 0, PAGE_SIZE, &ii); if (c != PAGE_SIZE && iov_iter_count(&ii)) goto out; } @@ -364,7 +365,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) goto out; - vaddr = kmap_local_page(ap.pages[0]); + vaddr = kmap_local_folio(ap.folios[0], 0); err = fuse_copy_ioctl_iovec(fm->fc, iov_page, vaddr, transferred, in_iovs + out_iovs, (flags & FUSE_IOCTL_COMPAT) != 0); @@ -392,17 +393,17 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; iov_iter_init(&ii, ITER_DEST, out_iov, out_iovs, transferred); - for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= ap.num_pages); i++) { - c = copy_page_to_iter(ap.pages[i], 0, PAGE_SIZE, &ii); + for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= ap.num_folios); i++) { + c = copy_folio_to_iter(ap.folios[i], 0, PAGE_SIZE, &ii); if (c != PAGE_SIZE && iov_iter_count(&ii)) goto out; } err = 0; out: free_page((unsigned long) iov_page); - while (ap.num_pages) - __free_page(ap.pages[--ap.num_pages]); - kfree(ap.pages); + while (ap.num_folios) + folio_put(ap.folios[--ap.num_folios]); + kfree(ap.folios); return err ? err : outarg.result; } diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index 0377b6dc24c80..17ce9636a2b1a 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -149,7 +149,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file, static int fuse_direntplus_link(struct file *file, struct fuse_direntplus *direntplus, - u64 attr_version) + u64 attr_version, u64 evict_ctr) { struct fuse_entry_out *o = &direntplus->entry_out; struct fuse_dirent *dirent = &direntplus->dirent; @@ -233,7 +233,7 @@ static int fuse_direntplus_link(struct file *file, } else { inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, ATTR_TIMEOUT(o), - attr_version); + attr_version, evict_ctr); if (!inode) inode = ERR_PTR(-ENOMEM); @@ -284,7 +284,8 @@ static void fuse_force_forget(struct file *file, u64 nodeid) } static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, - struct dir_context *ctx, u64 attr_version) + struct dir_context *ctx, u64 attr_version, + u64 evict_ctr) { struct fuse_direntplus *direntplus; struct fuse_dirent *dirent; @@ -319,7 +320,7 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, buf += reclen; nbytes -= reclen; - ret = fuse_direntplus_link(file, direntplus, attr_version); + ret = fuse_direntplus_link(file, direntplus, attr_version, evict_ctr); if (ret) fuse_force_forget(file, direntplus->entry_out.nodeid); } @@ -331,26 +332,27 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) { int plus; ssize_t res; - struct page *page; + struct folio *folio; struct inode *inode = file_inode(file); struct fuse_mount *fm = get_fuse_mount(inode); struct fuse_io_args ia = {}; struct fuse_args_pages *ap = &ia.ap; - struct fuse_page_desc desc = { .length = PAGE_SIZE }; - u64 attr_version = 0; + struct fuse_folio_desc desc = { .length = PAGE_SIZE }; + u64 attr_version = 0, evict_ctr = 0; bool locked; - page = alloc_page(GFP_KERNEL); - if (!page) + folio = folio_alloc(GFP_KERNEL, 0); + if (!folio) return -ENOMEM; plus = fuse_use_readdirplus(inode, ctx); ap->args.out_pages = true; - ap->num_pages = 1; - ap->pages = &page; + ap->num_folios = 1; + ap->folios = &folio; ap->descs = &desc; if (plus) { attr_version = fuse_get_attr_version(fm->fc); + evict_ctr = fuse_get_evict_ctr(fm->fc); fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE, FUSE_READDIRPLUS); } else { @@ -367,15 +369,16 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) if (ff->open_flags & FOPEN_CACHE_DIR) fuse_readdir_cache_end(file, ctx->pos); } else if (plus) { - res = parse_dirplusfile(page_address(page), res, - file, ctx, attr_version); + res = parse_dirplusfile(folio_address(folio), res, + file, ctx, attr_version, + evict_ctr); } else { - res = parse_dirfile(page_address(page), res, file, + res = parse_dirfile(folio_address(folio), res, file, ctx); } } - __free_page(page); + folio_put(folio); fuse_invalidate_atime(inode); return res; } diff --git a/fs/fuse/sysctl.c b/fs/fuse/sysctl.c new file mode 100644 index 0000000000000..b272bb333005a --- /dev/null +++ b/fs/fuse/sysctl.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/fuse/fuse_sysctl.c + * + * Sysctl interface to fuse parameters + */ +#include + +#include "fuse_i.h" + +static struct ctl_table_header *fuse_table_header; + +/* Bound by fuse_init_out max_pages, which is a u16 */ +static unsigned int sysctl_fuse_max_pages_limit = 65535; + +static struct ctl_table fuse_sysctl_table[] = { + { + .procname = "max_pages_limit", + .data = &fuse_max_pages_limit, + .maxlen = sizeof(fuse_max_pages_limit), + .mode = 0644, + .proc_handler = proc_douintvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = &sysctl_fuse_max_pages_limit, + }, +}; + +int fuse_sysctl_register(void) +{ + fuse_table_header = register_sysctl("fs/fuse", fuse_sysctl_table); + if (!fuse_table_header) + return -ENOMEM; + return 0; +} + +void fuse_sysctl_unregister(void) +{ + unregister_sysctl_table(fuse_table_header); + fuse_table_header = NULL; +} diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c index 6404a189e9890..82afe78ec5423 100644 --- a/fs/fuse/virtio_fs.c +++ b/fs/fuse/virtio_fs.c @@ -97,7 +97,8 @@ struct virtio_fs_req_work { }; static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq, - struct fuse_req *req, bool in_flight); + struct fuse_req *req, bool in_flight, + gfp_t gfp); static const struct constant_table dax_param_enums[] = { {"always", FUSE_DAX_ALWAYS }, @@ -242,7 +243,7 @@ static ssize_t cpu_list_show(struct kobject *kobj, qid = fsvq->vq->index; for (cpu = 0; cpu < nr_cpu_ids; cpu++) { - if (qid < VQ_REQUEST || (fs->mq_map[cpu] == qid - VQ_REQUEST)) { + if (qid < VQ_REQUEST || (fs->mq_map[cpu] == qid)) { if (first) ret = snprintf(buf + pos, size - pos, "%u", cpu); else @@ -521,6 +522,7 @@ static int virtio_fs_read_tag(struct virtio_device *vdev, struct virtio_fs *fs) return -EINVAL; } + dev_info(&vdev->dev, "discovered new tag: %s\n", fs->tag); return 0; } @@ -575,6 +577,8 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work) /* Dispatch pending requests */ while (1) { + unsigned int flags; + spin_lock(&fsvq->lock); req = list_first_entry_or_null(&fsvq->queued_reqs, struct fuse_req, list); @@ -585,7 +589,9 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work) list_del_init(&req->list); spin_unlock(&fsvq->lock); - ret = virtio_fs_enqueue_req(fsvq, req, true); + flags = memalloc_nofs_save(); + ret = virtio_fs_enqueue_req(fsvq, req, true, GFP_KERNEL); + memalloc_nofs_restore(flags); if (ret < 0) { if (ret == -ENOSPC) { spin_lock(&fsvq->lock); @@ -686,7 +692,7 @@ static void virtio_fs_hiprio_dispatch_work(struct work_struct *work) } /* Allocate and copy args into req->argbuf */ -static int copy_args_to_argbuf(struct fuse_req *req) +static int copy_args_to_argbuf(struct fuse_req *req, gfp_t gfp) { struct fuse_args *args = req->args; unsigned int offset = 0; @@ -700,7 +706,7 @@ static int copy_args_to_argbuf(struct fuse_req *req) len = fuse_len_args(num_in, (struct fuse_arg *) args->in_args) + fuse_len_args(num_out, args->out_args); - req->argbuf = kmalloc(len, GFP_ATOMIC); + req->argbuf = kmalloc(len, gfp); if (!req->argbuf) return -ENOMEM; @@ -760,7 +766,7 @@ static void virtio_fs_request_complete(struct fuse_req *req, struct fuse_args *args; struct fuse_args_pages *ap; unsigned int len, i, thislen; - struct page *page; + struct folio *folio; /* * TODO verify that server properly follows FUSE protocol @@ -772,12 +778,12 @@ static void virtio_fs_request_complete(struct fuse_req *req, if (args->out_pages && args->page_zeroing) { len = args->out_args[args->out_numargs - 1].size; ap = container_of(args, typeof(*ap), args); - for (i = 0; i < ap->num_pages; i++) { + for (i = 0; i < ap->num_folios; i++) { thislen = ap->descs[i].length; if (len < thislen) { WARN_ON(ap->descs[i].offset); - page = ap->pages[i]; - zero_user_segment(page, len, thislen); + folio = ap->folios[i]; + folio_zero_segment(folio, len, thislen); len = 0; } else { len -= thislen; @@ -870,23 +876,23 @@ static void virtio_fs_map_queues(struct virtio_device *vdev, struct virtio_fs *f goto fallback; for_each_cpu(cpu, mask) - fs->mq_map[cpu] = q; + fs->mq_map[cpu] = q + VQ_REQUEST; } return; fallback: /* Attempt to map evenly in groups over the CPUs */ masks = group_cpus_evenly(fs->num_request_queues); - /* If even this fails we default to all CPUs use queue zero */ + /* If even this fails we default to all CPUs use first request queue */ if (!masks) { for_each_possible_cpu(cpu) - fs->mq_map[cpu] = 0; + fs->mq_map[cpu] = VQ_REQUEST; return; } for (q = 0; q < fs->num_request_queues; q++) { for_each_cpu(cpu, &masks[q]) - fs->mq_map[cpu] = q; + fs->mq_map[cpu] = q + VQ_REQUEST; } kfree(masks); } @@ -1267,15 +1273,15 @@ static void virtio_fs_send_interrupt(struct fuse_iqueue *fiq, struct fuse_req *r } /* Count number of scatter-gather elements required */ -static unsigned int sg_count_fuse_pages(struct fuse_page_desc *page_descs, - unsigned int num_pages, - unsigned int total_len) +static unsigned int sg_count_fuse_folios(struct fuse_folio_desc *folio_descs, + unsigned int num_folios, + unsigned int total_len) { unsigned int i; unsigned int this_len; - for (i = 0; i < num_pages && total_len; i++) { - this_len = min(page_descs[i].length, total_len); + for (i = 0; i < num_folios && total_len; i++) { + this_len = min(folio_descs[i].length, total_len); total_len -= this_len; } @@ -1294,8 +1300,8 @@ static unsigned int sg_count_fuse_req(struct fuse_req *req) if (args->in_pages) { size = args->in_args[args->in_numargs - 1].size; - total_sgs += sg_count_fuse_pages(ap->descs, ap->num_pages, - size); + total_sgs += sg_count_fuse_folios(ap->descs, ap->num_folios, + size); } if (!test_bit(FR_ISREPLY, &req->flags)) @@ -1308,27 +1314,27 @@ static unsigned int sg_count_fuse_req(struct fuse_req *req) if (args->out_pages) { size = args->out_args[args->out_numargs - 1].size; - total_sgs += sg_count_fuse_pages(ap->descs, ap->num_pages, - size); + total_sgs += sg_count_fuse_folios(ap->descs, ap->num_folios, + size); } return total_sgs; } -/* Add pages to scatter-gather list and return number of elements used */ -static unsigned int sg_init_fuse_pages(struct scatterlist *sg, - struct page **pages, - struct fuse_page_desc *page_descs, - unsigned int num_pages, - unsigned int total_len) +/* Add folios to scatter-gather list and return number of elements used */ +static unsigned int sg_init_fuse_folios(struct scatterlist *sg, + struct folio **folios, + struct fuse_folio_desc *folio_descs, + unsigned int num_folios, + unsigned int total_len) { unsigned int i; unsigned int this_len; - for (i = 0; i < num_pages && total_len; i++) { + for (i = 0; i < num_folios && total_len; i++) { sg_init_table(&sg[i], 1); - this_len = min(page_descs[i].length, total_len); - sg_set_page(&sg[i], pages[i], this_len, page_descs[i].offset); + this_len = min(folio_descs[i].length, total_len); + sg_set_folio(&sg[i], folios[i], this_len, folio_descs[i].offset); total_len -= this_len; } @@ -1353,10 +1359,10 @@ static unsigned int sg_init_fuse_args(struct scatterlist *sg, sg_init_one(&sg[total_sgs++], argbuf, len); if (argpages) - total_sgs += sg_init_fuse_pages(&sg[total_sgs], - ap->pages, ap->descs, - ap->num_pages, - args[numargs - 1].size); + total_sgs += sg_init_fuse_folios(&sg[total_sgs], + ap->folios, ap->descs, + ap->num_folios, + args[numargs - 1].size); if (len_used) *len_used = len; @@ -1366,7 +1372,8 @@ static unsigned int sg_init_fuse_args(struct scatterlist *sg, /* Add a request to a virtqueue and kick the device */ static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq, - struct fuse_req *req, bool in_flight) + struct fuse_req *req, bool in_flight, + gfp_t gfp) { /* requests need at least 4 elements */ struct scatterlist *stack_sgs[6]; @@ -1387,8 +1394,8 @@ static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq, /* Does the sglist fit on the stack? */ total_sgs = sg_count_fuse_req(req); if (total_sgs > ARRAY_SIZE(stack_sgs)) { - sgs = kmalloc_array(total_sgs, sizeof(sgs[0]), GFP_ATOMIC); - sg = kmalloc_array(total_sgs, sizeof(sg[0]), GFP_ATOMIC); + sgs = kmalloc_array(total_sgs, sizeof(sgs[0]), gfp); + sg = kmalloc_array(total_sgs, sizeof(sg[0]), gfp); if (!sgs || !sg) { ret = -ENOMEM; goto out; @@ -1396,7 +1403,7 @@ static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq, } /* Use a bounce buffer since stack args cannot be mapped */ - ret = copy_args_to_argbuf(req); + ret = copy_args_to_argbuf(req, gfp); if (ret < 0) goto out; @@ -1481,7 +1488,7 @@ static void virtio_fs_send_req(struct fuse_iqueue *fiq, struct fuse_req *req) clear_bit(FR_PENDING, &req->flags); fs = fiq->priv; - queue_id = VQ_REQUEST + fs->mq_map[raw_smp_processor_id()]; + queue_id = fs->mq_map[raw_smp_processor_id()]; pr_debug("%s: opcode %u unique %#llx nodeid %#llx in.len %u out.len %u queue_id %u\n", __func__, req->in.h.opcode, req->in.h.unique, @@ -1490,7 +1497,7 @@ static void virtio_fs_send_req(struct fuse_iqueue *fiq, struct fuse_req *req) queue_id); fsvq = &fs->vqs[queue_id]; - ret = virtio_fs_enqueue_req(fsvq, req, false); + ret = virtio_fs_enqueue_req(fsvq, req, false, GFP_ATOMIC); if (ret < 0) { if (ret == -ENOSPC) { /* @@ -1691,6 +1698,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc) fc->delete_stale = true; fc->auto_submounts = true; fc->sync_fs = true; + fc->use_pages_for_kvec_io = true; /* Tell FUSE to split requests that exceed the virtqueue's size */ fc->max_pages_limit = min_t(unsigned int, fc->max_pages_limit, diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 4701c4aafbf4b..8c4c1f871a889 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "gfs2.h" #include "incore.h" @@ -562,11 +563,11 @@ static void state_change(struct gfs2_glock *gl, unsigned int new_state) gl->gl_tchange = jiffies; } -static void gfs2_set_demote(struct gfs2_glock *gl) +static void gfs2_set_demote(int nr, struct gfs2_glock *gl) { struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; - set_bit(GLF_DEMOTE, &gl->gl_flags); + set_bit(nr, &gl->gl_flags); smp_mb(); wake_up(&sdp->sd_async_glock_wait); } @@ -958,20 +959,22 @@ static void gfs2_glock_poke(struct gfs2_glock *gl) gfs2_holder_uninit(&gh); } -static bool gfs2_try_evict(struct gfs2_glock *gl) +static void gfs2_try_evict(struct gfs2_glock *gl) { struct gfs2_inode *ip; - bool evicted = false; /* * If there is contention on the iopen glock and we have an inode, try - * to grab and release the inode so that it can be evicted. This will - * allow the remote node to go ahead and delete the inode without us - * having to do it, which will avoid rgrp glock thrashing. + * to grab and release the inode so that it can be evicted. The + * GIF_DEFER_DELETE flag indicates to gfs2_evict_inode() that the inode + * should not be deleted locally. This will allow the remote node to + * go ahead and delete the inode without us having to do it, which will + * avoid rgrp glock thrashing. * * The remote node is likely still holding the corresponding inode * glock, so it will run before we get to verify that the delete has - * happened below. + * happened below. (Verification is triggered by the call to + * gfs2_queue_verify_delete() in gfs2_evict_inode().) */ spin_lock(&gl->gl_lockref.lock); ip = gl->gl_object; @@ -979,8 +982,14 @@ static bool gfs2_try_evict(struct gfs2_glock *gl) ip = NULL; spin_unlock(&gl->gl_lockref.lock); if (ip) { - gl->gl_no_formal_ino = ip->i_no_formal_ino; - set_bit(GIF_DEFERRED_DELETE, &ip->i_flags); + wait_on_inode(&ip->i_inode); + if (is_bad_inode(&ip->i_inode)) { + iput(&ip->i_inode); + ip = NULL; + } + } + if (ip) { + set_bit(GIF_DEFER_DELETE, &ip->i_flags); d_prune_aliases(&ip->i_inode); iput(&ip->i_inode); @@ -988,7 +997,7 @@ static bool gfs2_try_evict(struct gfs2_glock *gl) spin_lock(&gl->gl_lockref.lock); ip = gl->gl_object; if (ip) { - clear_bit(GIF_DEFERRED_DELETE, &ip->i_flags); + clear_bit(GIF_DEFER_DELETE, &ip->i_flags); if (!igrab(&ip->i_inode)) ip = NULL; } @@ -997,9 +1006,7 @@ static bool gfs2_try_evict(struct gfs2_glock *gl) gfs2_glock_poke(ip->i_gl); iput(&ip->i_inode); } - evicted = !ip; } - return evicted; } bool gfs2_queue_try_to_evict(struct gfs2_glock *gl) @@ -1008,18 +1015,18 @@ bool gfs2_queue_try_to_evict(struct gfs2_glock *gl) if (test_and_set_bit(GLF_TRY_TO_EVICT, &gl->gl_flags)) return false; - return queue_delayed_work(sdp->sd_delete_wq, - &gl->gl_delete, 0); + return !mod_delayed_work(sdp->sd_delete_wq, &gl->gl_delete, 0); } -static bool gfs2_queue_verify_evict(struct gfs2_glock *gl) +bool gfs2_queue_verify_delete(struct gfs2_glock *gl, bool later) { struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + unsigned long delay; - if (test_and_set_bit(GLF_VERIFY_EVICT, &gl->gl_flags)) + if (test_and_set_bit(GLF_VERIFY_DELETE, &gl->gl_flags)) return false; - return queue_delayed_work(sdp->sd_delete_wq, - &gl->gl_delete, 5 * HZ); + delay = later ? HZ + get_random_long() % (HZ * 9) : 0; + return queue_delayed_work(sdp->sd_delete_wq, &gl->gl_delete, delay); } static void delete_work_func(struct work_struct *work) @@ -1027,43 +1034,21 @@ static void delete_work_func(struct work_struct *work) struct delayed_work *dwork = to_delayed_work(work); struct gfs2_glock *gl = container_of(dwork, struct gfs2_glock, gl_delete); struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; - struct inode *inode; - u64 no_addr = gl->gl_name.ln_number; + bool verify_delete = test_and_clear_bit(GLF_VERIFY_DELETE, &gl->gl_flags); - if (test_and_clear_bit(GLF_TRY_TO_EVICT, &gl->gl_flags)) { - /* - * If we can evict the inode, give the remote node trying to - * delete the inode some time before verifying that the delete - * has happened. Otherwise, if we cause contention on the inode glock - * immediately, the remote node will think that we still have - * the inode in use, and so it will give up waiting. - * - * If we can't evict the inode, signal to the remote node that - * the inode is still in use. We'll later try to delete the - * inode locally in gfs2_evict_inode. - * - * FIXME: We only need to verify that the remote node has - * deleted the inode because nodes before this remote delete - * rework won't cooperate. At a later time, when we no longer - * care about compatibility with such nodes, we can skip this - * step entirely. - */ - if (gfs2_try_evict(gl)) { - if (test_bit(SDF_KILL, &sdp->sd_flags)) - goto out; - if (gfs2_queue_verify_evict(gl)) - return; - } - goto out; - } + if (test_and_clear_bit(GLF_TRY_TO_EVICT, &gl->gl_flags)) + gfs2_try_evict(gl); + + if (verify_delete) { + u64 no_addr = gl->gl_name.ln_number; + struct inode *inode; - if (test_and_clear_bit(GLF_VERIFY_EVICT, &gl->gl_flags)) { inode = gfs2_lookup_by_inum(sdp, no_addr, gl->gl_no_formal_ino, GFS2_BLKST_UNLINKED); if (IS_ERR(inode)) { if (PTR_ERR(inode) == -EAGAIN && !test_bit(SDF_KILL, &sdp->sd_flags) && - gfs2_queue_verify_evict(gl)) + gfs2_queue_verify_delete(gl, true)) return; } else { d_prune_aliases(inode); @@ -1071,7 +1056,6 @@ static void delete_work_func(struct work_struct *work) } } -out: gfs2_glock_put(gl); } @@ -1100,7 +1084,7 @@ static void glock_work_func(struct work_struct *work) if (!delay) { clear_bit(GLF_PENDING_DEMOTE, &gl->gl_flags); - gfs2_set_demote(gl); + gfs2_set_demote(GLF_DEMOTE, gl); } } run_queue(gl, 0); @@ -1442,10 +1426,7 @@ int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs) static void request_demote(struct gfs2_glock *gl, unsigned int state, unsigned long delay, bool remote) { - if (delay) - set_bit(GLF_PENDING_DEMOTE, &gl->gl_flags); - else - gfs2_set_demote(gl); + gfs2_set_demote(delay ? GLF_PENDING_DEMOTE : GLF_DEMOTE, gl); if (gl->gl_demote_state == LM_ST_EXCLUSIVE) { gl->gl_demote_state = state; gl->gl_demote_time = jiffies; @@ -1635,12 +1616,6 @@ int gfs2_glock_poll(struct gfs2_holder *gh) return test_bit(HIF_WAIT, &gh->gh_iflags) ? 0 : 1; } -static inline bool needs_demote(struct gfs2_glock *gl) -{ - return (test_bit(GLF_DEMOTE, &gl->gl_flags) || - test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags)); -} - static void __gfs2_glock_dq(struct gfs2_holder *gh) { struct gfs2_glock *gl = gh->gh_gl; @@ -1649,8 +1624,8 @@ static void __gfs2_glock_dq(struct gfs2_holder *gh) /* * This holder should not be cached, so mark it for demote. - * Note: this should be done before the check for needs_demote - * below. + * Note: this should be done before the glock_needs_demote + * check below. */ if (gh->gh_flags & GL_NOCACHE) request_demote(gl, LM_ST_UNLOCKED, 0, false); @@ -1663,7 +1638,7 @@ static void __gfs2_glock_dq(struct gfs2_holder *gh) * If there hasn't been a demote request we are done. * (Let the remaining holders, if any, keep holding it.) */ - if (!needs_demote(gl)) { + if (!glock_needs_demote(gl)) { if (list_empty(&gl->gl_holders)) fast_path = 1; } @@ -2117,7 +2092,7 @@ static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp) void gfs2_cancel_delete_work(struct gfs2_glock *gl) { clear_bit(GLF_TRY_TO_EVICT, &gl->gl_flags); - clear_bit(GLF_VERIFY_EVICT, &gl->gl_flags); + clear_bit(GLF_VERIFY_DELETE, &gl->gl_flags); if (cancel_delayed_work(&gl->gl_delete)) gfs2_glock_put(gl); } @@ -2370,7 +2345,7 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl) *p++ = 'N'; if (test_bit(GLF_TRY_TO_EVICT, gflags)) *p++ = 'e'; - if (test_bit(GLF_VERIFY_EVICT, gflags)) + if (test_bit(GLF_VERIFY_DELETE, gflags)) *p++ = 'E'; *p = 0; return buf; diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index adf0091cc98f9..c171f745650fd 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -245,6 +245,7 @@ static inline int gfs2_glock_nq_init(struct gfs2_glock *gl, void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state); void gfs2_glock_complete(struct gfs2_glock *gl, int ret); bool gfs2_queue_try_to_evict(struct gfs2_glock *gl); +bool gfs2_queue_verify_delete(struct gfs2_glock *gl, bool later); void gfs2_cancel_delete_work(struct gfs2_glock *gl); void gfs2_flush_delete_work(struct gfs2_sbd *sdp); void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); @@ -284,4 +285,10 @@ static inline bool gfs2_holder_queued(struct gfs2_holder *gh) void gfs2_inode_remember_delete(struct gfs2_glock *gl, u64 generation); bool gfs2_inode_already_deleted(struct gfs2_glock *gl, u64 generation); +static inline bool glock_needs_demote(struct gfs2_glock *gl) +{ + return (test_bit(GLF_DEMOTE, &gl->gl_flags) || + test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags)); +} + #endif /* __GLOCK_DOT_H__ */ diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 95d8081681dcc..eb4714f299efb 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -470,7 +470,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) * Returns: errno */ -int gfs2_inode_refresh(struct gfs2_inode *ip) +static int gfs2_inode_refresh(struct gfs2_inode *ip) { struct buffer_head *dibh; int error; @@ -494,11 +494,18 @@ int gfs2_inode_refresh(struct gfs2_inode *ip) static int inode_go_instantiate(struct gfs2_glock *gl) { struct gfs2_inode *ip = gl->gl_object; + struct gfs2_glock *io_gl; + int error; if (!ip) /* no inode to populate - read it in later */ return 0; - return gfs2_inode_refresh(ip); + error = gfs2_inode_refresh(ip); + if (error) + return error; + io_gl = ip->i_iopen_gh.gh_gl; + io_gl->gl_no_formal_ino = ip->i_no_formal_ino; + return 0; } static int inode_go_held(struct gfs2_holder *gh) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index aa4ef67a34e03..4e19cce3d906d 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -329,7 +329,7 @@ enum { GLF_BLOCKING = 15, GLF_UNLOCKED = 16, /* Wait for glock to be unlocked */ GLF_TRY_TO_EVICT = 17, /* iopen glocks only */ - GLF_VERIFY_EVICT = 18, /* iopen glocks only */ + GLF_VERIFY_DELETE = 18, /* iopen glocks only */ }; struct gfs2_glock { @@ -376,7 +376,7 @@ enum { GIF_SW_PAGED = 3, GIF_FREE_VFS_INODE = 5, GIF_GLOP_PENDING = 6, - GIF_DEFERRED_DELETE = 7, + GIF_DEFER_DELETE = 7, }; struct gfs2_inode { diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 1b95db2c3aac3..6fbbaaad1cd05 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -750,6 +750,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, if (error) goto fail_free_inode; gfs2_cancel_delete_work(io_gl); + io_gl->gl_no_formal_ino = ip->i_no_formal_ino; retry: error = insert_inode_locked4(inode, ip->i_no_addr, iget_test, &ip->i_no_addr); diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index fd15d1c6b6fb1..9e5e1622d50a6 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -93,8 +93,6 @@ struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, u64 no_formal_ino, unsigned int blktype); -int gfs2_inode_refresh(struct gfs2_inode *ip); - struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, int is_root); int gfs2_permission(struct mnt_idmap *idmap, diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index fa5134df985f7..58aeeae7ed8cd 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -224,8 +224,21 @@ static int make_mode(struct gfs2_sbd *sdp, const unsigned int lmstate) return -1; } +/* Taken from fs/dlm/lock.c. */ + +static bool middle_conversion(int cur, int req) +{ + return (cur == DLM_LOCK_PR && req == DLM_LOCK_CW) || + (cur == DLM_LOCK_CW && req == DLM_LOCK_PR); +} + +static bool down_conversion(int cur, int req) +{ + return !middle_conversion(cur, req) && req < cur; +} + static u32 make_flags(struct gfs2_glock *gl, const unsigned int gfs_flags, - const int req) + const int cur, const int req) { u32 lkf = 0; @@ -251,7 +264,14 @@ static u32 make_flags(struct gfs2_glock *gl, const unsigned int gfs_flags, if (!test_bit(GLF_INITIAL, &gl->gl_flags)) { lkf |= DLM_LKF_CONVERT; - if (test_bit(GLF_BLOCKING, &gl->gl_flags)) + + /* + * The DLM_LKF_QUECVT flag needs to be set for "first come, + * first served" semantics, but it must only be set for + * "upward" lock conversions or else DLM will reject the + * request as invalid. + */ + if (!down_conversion(cur, req)) lkf |= DLM_LKF_QUECVT; } @@ -271,13 +291,14 @@ static int gdlm_lock(struct gfs2_glock *gl, unsigned int req_state, unsigned int flags) { struct lm_lockstruct *ls = &gl->gl_name.ln_sbd->sd_lockstruct; - int req; + int cur, req; u32 lkf; char strname[GDLM_STRNAME_BYTES] = ""; int error; + cur = make_mode(gl->gl_name.ln_sbd, gl->gl_state); req = make_mode(gl->gl_name.ln_sbd, req_state); - lkf = make_flags(gl, flags, req); + lkf = make_flags(gl, flags, cur, req); gfs2_glstats_inc(gl, GFS2_LKS_DCOUNT); gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT); if (test_bit(GLF_INITIAL, &gl->gl_flags)) { diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 29c7728167652..b14e54b38ee80 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1879,7 +1879,7 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip */ ip = gl->gl_object; - if (ip || !gfs2_queue_try_to_evict(gl)) + if (ip || !gfs2_queue_verify_delete(gl, false)) gfs2_glock_put(gl); else found++; @@ -1987,10 +1987,8 @@ static bool gfs2_rgrp_used_recently(const struct gfs2_blkreserv *rs, static u32 gfs2_orlov_skip(const struct gfs2_inode *ip) { const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - u32 skip; - get_random_bytes(&skip, sizeof(skip)); - return skip % sdp->sd_rgrps; + return get_random_u32() % sdp->sd_rgrps; } static bool gfs2_select_rgrp(struct gfs2_rgrpd **pos, const struct gfs2_rgrpd *begin) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 6678060ed4d2b..92a3b6ddafdc1 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -44,10 +44,10 @@ #include "xattr.h" #include "lops.h" -enum dinode_demise { - SHOULD_DELETE_DINODE, - SHOULD_NOT_DELETE_DINODE, - SHOULD_DEFER_EVICTION, +enum evict_behavior { + EVICT_SHOULD_DELETE, + EVICT_SHOULD_SKIP_DELETE, + EVICT_SHOULD_DEFER_DELETE, }; /** @@ -1030,7 +1030,7 @@ static int gfs2_drop_inode(struct inode *inode) if (inode->i_nlink && gfs2_holder_initialized(&ip->i_iopen_gh)) { struct gfs2_glock *gl = ip->i_iopen_gh.gh_gl; - if (test_bit(GLF_DEMOTE, &gl->gl_flags)) + if (glock_needs_demote(gl)) clear_nlink(inode); } @@ -1045,7 +1045,7 @@ static int gfs2_drop_inode(struct inode *inode) struct gfs2_glock *gl = ip->i_iopen_gh.gh_gl; gfs2_glock_hold(gl); - if (!gfs2_queue_try_to_evict(gl)) + if (!gfs2_queue_verify_delete(gl, true)) gfs2_glock_put_async(gl); return 0; } @@ -1257,7 +1257,7 @@ static void gfs2_glock_put_eventually(struct gfs2_glock *gl) gfs2_glock_put(gl); } -static bool gfs2_upgrade_iopen_glock(struct inode *inode) +static enum evict_behavior gfs2_upgrade_iopen_glock(struct inode *inode) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); @@ -1272,9 +1272,9 @@ static bool gfs2_upgrade_iopen_glock(struct inode *inode) * exclusive access to the iopen glock here. * * Otherwise, the other nodes holding the lock will be notified about - * our locking request. If they do not have the inode open, they are - * expected to evict the cached inode and release the lock, allowing us - * to proceed. + * our locking request (see iopen_go_callback()). If they do not have + * the inode open, they are expected to evict the cached inode and + * release the lock, allowing us to proceed. * * Otherwise, if they cannot evict the inode, they are expected to poke * the inode glock (note: not the iopen glock). We will notice that @@ -1290,17 +1290,22 @@ static bool gfs2_upgrade_iopen_glock(struct inode *inode) gfs2_holder_reinit(LM_ST_EXCLUSIVE, GL_ASYNC | GL_NOCACHE, gh); error = gfs2_glock_nq(gh); if (error) - return false; + return EVICT_SHOULD_SKIP_DELETE; wait_event_interruptible_timeout(sdp->sd_async_glock_wait, !test_bit(HIF_WAIT, &gh->gh_iflags) || - test_bit(GLF_DEMOTE, &ip->i_gl->gl_flags), + glock_needs_demote(ip->i_gl), 5 * HZ); if (!test_bit(HIF_HOLDER, &gh->gh_iflags)) { gfs2_glock_dq(gh); - return false; + if (glock_needs_demote(ip->i_gl)) + return EVICT_SHOULD_SKIP_DELETE; + return EVICT_SHOULD_DEFER_DELETE; } - return gfs2_glock_holder_ready(gh) == 0; + error = gfs2_glock_holder_ready(gh); + if (error) + return EVICT_SHOULD_SKIP_DELETE; + return EVICT_SHOULD_DELETE; } /** @@ -1313,8 +1318,8 @@ static bool gfs2_upgrade_iopen_glock(struct inode *inode) * * Returns: the fate of the dinode */ -static enum dinode_demise evict_should_delete(struct inode *inode, - struct gfs2_holder *gh) +static enum evict_behavior evict_should_delete(struct inode *inode, + struct gfs2_holder *gh) { struct gfs2_inode *ip = GFS2_I(inode); struct super_block *sb = inode->i_sb; @@ -1324,12 +1329,12 @@ static enum dinode_demise evict_should_delete(struct inode *inode, if (unlikely(test_bit(GIF_ALLOC_FAILED, &ip->i_flags))) goto should_delete; - if (test_bit(GIF_DEFERRED_DELETE, &ip->i_flags)) - return SHOULD_DEFER_EVICTION; + if (test_bit(GIF_DEFER_DELETE, &ip->i_flags)) + return EVICT_SHOULD_DEFER_DELETE; /* Deletes should never happen under memory pressure anymore. */ if (WARN_ON_ONCE(current->flags & PF_MEMALLOC)) - return SHOULD_DEFER_EVICTION; + return EVICT_SHOULD_DEFER_DELETE; /* Must not read inode block until block type has been verified */ ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SKIP, gh); @@ -1337,34 +1342,37 @@ static enum dinode_demise evict_should_delete(struct inode *inode, glock_clear_object(ip->i_iopen_gh.gh_gl, ip); ip->i_iopen_gh.gh_flags |= GL_NOCACHE; gfs2_glock_dq_uninit(&ip->i_iopen_gh); - return SHOULD_DEFER_EVICTION; + return EVICT_SHOULD_DEFER_DELETE; } if (gfs2_inode_already_deleted(ip->i_gl, ip->i_no_formal_ino)) - return SHOULD_NOT_DELETE_DINODE; + return EVICT_SHOULD_SKIP_DELETE; ret = gfs2_check_blk_type(sdp, ip->i_no_addr, GFS2_BLKST_UNLINKED); if (ret) - return SHOULD_NOT_DELETE_DINODE; + return EVICT_SHOULD_SKIP_DELETE; ret = gfs2_instantiate(gh); if (ret) - return SHOULD_NOT_DELETE_DINODE; + return EVICT_SHOULD_SKIP_DELETE; /* * The inode may have been recreated in the meantime. */ if (inode->i_nlink) - return SHOULD_NOT_DELETE_DINODE; + return EVICT_SHOULD_SKIP_DELETE; should_delete: if (gfs2_holder_initialized(&ip->i_iopen_gh) && test_bit(HIF_HOLDER, &ip->i_iopen_gh.gh_iflags)) { - if (!gfs2_upgrade_iopen_glock(inode)) { + enum evict_behavior behavior = + gfs2_upgrade_iopen_glock(inode); + + if (behavior != EVICT_SHOULD_DELETE) { gfs2_holder_uninit(&ip->i_iopen_gh); - return SHOULD_NOT_DELETE_DINODE; + return behavior; } } - return SHOULD_DELETE_DINODE; + return EVICT_SHOULD_DELETE; } /** @@ -1475,8 +1483,10 @@ static void gfs2_evict_inode(struct inode *inode) struct gfs2_sbd *sdp = sb->s_fs_info; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder gh; + enum evict_behavior behavior; int ret; + gfs2_holder_mark_uninitialized(&gh); if (inode->i_nlink || sb_rdonly(sb) || !ip->i_no_addr) goto out; @@ -1488,11 +1498,20 @@ static void gfs2_evict_inode(struct inode *inode) if (!sdp->sd_jdesc) goto out; - gfs2_holder_mark_uninitialized(&gh); - ret = evict_should_delete(inode, &gh); - if (ret == SHOULD_DEFER_EVICTION) - goto out; - if (ret == SHOULD_DELETE_DINODE) + behavior = evict_should_delete(inode, &gh); + if (behavior == EVICT_SHOULD_DEFER_DELETE && + !test_bit(SDF_KILL, &sdp->sd_flags)) { + struct gfs2_glock *io_gl = ip->i_iopen_gh.gh_gl; + + if (io_gl) { + gfs2_glock_hold(io_gl); + if (!gfs2_queue_verify_delete(io_gl, true)) + gfs2_glock_put(io_gl); + goto out; + } + behavior = EVICT_SHOULD_DELETE; + } + if (behavior == EVICT_SHOULD_DELETE) ret = evict_unlinked_inode(inode); else ret = evict_linked_inode(inode); @@ -1500,11 +1519,11 @@ static void gfs2_evict_inode(struct inode *inode) if (gfs2_rs_active(&ip->i_res)) gfs2_rs_deltree(&ip->i_res); - if (gfs2_holder_initialized(&gh)) - gfs2_glock_dq_uninit(&gh); if (ret && ret != GLR_TRYFAILED && ret != -EROFS) fs_warn(sdp, "gfs2_evict_inode: %d\n", ret); out: + if (gfs2_holder_initialized(&gh)) + gfs2_glock_dq_uninit(&gh); truncate_inode_pages_final(&inode->i_data); if (ip->i_qadata) gfs2_assert_warn(sdp, ip->i_qadata->qa_ref == 0); @@ -1537,11 +1556,13 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb) if (!ip) return NULL; ip->i_no_addr = 0; + ip->i_no_formal_ino = 0; ip->i_flags = 0; ip->i_gl = NULL; gfs2_holder_mark_uninitialized(&ip->i_iopen_gh); memset(&ip->i_res, 0, sizeof(ip->i_res)); RB_CLEAR_NODE(&ip->i_res.rs_node); + ip->i_diskflags = 0; ip->i_rahead = 0; return &ip->i_inode; } diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 6d1cf2436ead6..7e51d2cec64b4 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -57,6 +57,7 @@ static int __init hostfs_args(char *options, int *add) { char *ptr; + *add = 0; ptr = strchr(options, ','); if (ptr != NULL) *ptr++ = '\0'; @@ -471,8 +472,8 @@ static int hostfs_write_begin(struct file *file, struct address_space *mapping, *foliop = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN, mapping_gfp_mask(mapping)); - if (!*foliop) - return -ENOMEM; + if (IS_ERR(*foliop)) + return PTR_ERR(*foliop); return 0; } diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index d42f01e0fc1c2..955f19e27e47c 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1350,40 +1350,12 @@ static inline int iomap_zero_iter_flush_and_stale(struct iomap_iter *i) return filemap_write_and_wait_range(mapping, i->pos, end); } -static loff_t iomap_zero_iter(struct iomap_iter *iter, bool *did_zero, - bool *range_dirty) +static loff_t iomap_zero_iter(struct iomap_iter *iter, bool *did_zero) { - const struct iomap *srcmap = iomap_iter_srcmap(iter); loff_t pos = iter->pos; loff_t length = iomap_length(iter); loff_t written = 0; - /* - * We must zero subranges of unwritten mappings that might be dirty in - * pagecache from previous writes. We only know whether the entire range - * was clean or not, however, and dirty folios may have been written - * back or reclaimed at any point after mapping lookup. - * - * The easiest way to deal with this is to flush pagecache to trigger - * any pending unwritten conversions and then grab the updated extents - * from the fs. The flush may change the current mapping, so mark it - * stale for the iterator to remap it for the next pass to handle - * properly. - * - * Note that holes are treated the same as unwritten because zero range - * is (ab)used for partial folio zeroing in some cases. Hole backed - * post-eof ranges can be dirtied via mapped write and the flush - * triggers writeback time post-eof zeroing. - */ - if (srcmap->type == IOMAP_HOLE || srcmap->type == IOMAP_UNWRITTEN) { - if (*range_dirty) { - *range_dirty = false; - return iomap_zero_iter_flush_and_stale(iter); - } - /* range is clean and already zeroed, nothing to do */ - return length; - } - do { struct folio *folio; int status; @@ -1397,6 +1369,8 @@ static loff_t iomap_zero_iter(struct iomap_iter *iter, bool *did_zero, if (iter->iomap.flags & IOMAP_F_STALE) break; + /* warn about zeroing folios beyond eof that won't write back */ + WARN_ON_ONCE(folio_pos(folio) > iter->inode->i_size); offset = offset_in_folio(folio, pos); if (bytes > folio_size(folio) - offset) bytes = folio_size(folio) - offset; @@ -1429,28 +1403,58 @@ iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero, .len = len, .flags = IOMAP_ZERO, }; + struct address_space *mapping = inode->i_mapping; + unsigned int blocksize = i_blocksize(inode); + unsigned int off = pos & (blocksize - 1); + loff_t plen = min_t(loff_t, len, blocksize - off); int ret; bool range_dirty; /* - * Zero range wants to skip pre-zeroed (i.e. unwritten) mappings, but - * pagecache must be flushed to ensure stale data from previous - * buffered writes is not exposed. A flush is only required for certain - * types of mappings, but checking pagecache after mapping lookup is - * racy with writeback and reclaim. + * Zero range can skip mappings that are zero on disk so long as + * pagecache is clean. If pagecache was dirty prior to zero range, the + * mapping converts on writeback completion and so must be zeroed. * - * Therefore, check the entire range first and pass along whether any - * part of it is dirty. If so and an underlying mapping warrants it, - * flush the cache at that point. This trades off the occasional false - * positive (and spurious flush, if the dirty data and mapping don't - * happen to overlap) for simplicity in handling a relatively uncommon - * situation. + * The simplest way to deal with this across a range is to flush + * pagecache and process the updated mappings. To avoid excessive + * flushing on partial eof zeroing, special case it to zero the + * unaligned start portion if already dirty in pagecache. + */ + if (off && + filemap_range_needs_writeback(mapping, pos, pos + plen - 1)) { + iter.len = plen; + while ((ret = iomap_iter(&iter, ops)) > 0) + iter.processed = iomap_zero_iter(&iter, did_zero); + + iter.len = len - (iter.pos - pos); + if (ret || !iter.len) + return ret; + } + + /* + * To avoid an unconditional flush, check pagecache state and only flush + * if dirty and the fs returns a mapping that might convert on + * writeback. */ range_dirty = filemap_range_needs_writeback(inode->i_mapping, - pos, pos + len - 1); + iter.pos, iter.pos + iter.len - 1); + while ((ret = iomap_iter(&iter, ops)) > 0) { + const struct iomap *srcmap = iomap_iter_srcmap(&iter); - while ((ret = iomap_iter(&iter, ops)) > 0) - iter.processed = iomap_zero_iter(&iter, did_zero, &range_dirty); + if (srcmap->type == IOMAP_HOLE || + srcmap->type == IOMAP_UNWRITTEN) { + loff_t proc = iomap_length(&iter); + + if (range_dirty) { + range_dirty = false; + proc = iomap_zero_iter_flush_and_stale(&iter); + } + iter.processed = proc; + continue; + } + + iter.processed = iomap_zero_iter(&iter, did_zero); + } return ret; } EXPORT_SYMBOL_GPL(iomap_zero_range); diff --git a/fs/iomap/iter.c b/fs/iomap/iter.c index 79a0614eaab77..3790918646af7 100644 --- a/fs/iomap/iter.c +++ b/fs/iomap/iter.c @@ -22,26 +22,25 @@ static inline int iomap_iter_advance(struct iomap_iter *iter) { bool stale = iter->iomap.flags & IOMAP_F_STALE; + int ret = 1; /* handle the previous iteration (if any) */ if (iter->iomap.length) { if (iter->processed < 0) return iter->processed; - if (!iter->processed && !stale) - return 0; if (WARN_ON_ONCE(iter->processed > iomap_length(iter))) return -EIO; iter->pos += iter->processed; iter->len -= iter->processed; - if (!iter->len) - return 0; + if (!iter->len || (!iter->processed && !stale)) + ret = 0; } - /* clear the state for the next iteration */ + /* clear the per iteration state */ iter->processed = 0; memset(&iter->iomap, 0, sizeof(iter->iomap)); memset(&iter->srcmap, 0, sizeof(iter->srcmap)); - return 1; + return ret; } static inline void iomap_iter_done(struct iomap_iter *iter) diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 79e771ab624f4..2b9ef713b844a 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -95,6 +95,9 @@ static int jffs2_rtime_decompress(unsigned char *data_in, positions[value]=outpos; if (repeat) { + if ((outpos + repeat) >= destlen) { + return 1; + } if (backoffs + repeat >= outpos) { while(repeat) { cpage_out[outpos++] = cpage_out[backoffs++]; diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index 556de100ebd5a..9854253d0108b 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -276,11 +276,6 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, end_rubin(&rs); - if (outpos > pos) { - /* We failed */ - return -1; - } - /* Tell the caller how much we managed to compress, * and how much space it took */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index acd32f05b5198..ef3a1e1b6cb06 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -338,10 +338,9 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl } while(--retlen); mtd_unpoint(c->mtd, jeb->offset, c->sector_size); if (retlen) { - pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08tx\n", - *wordebuf, - jeb->offset + - c->sector_size-retlen * sizeof(*wordebuf)); + *bad_offset = jeb->offset + c->sector_size - retlen * sizeof(*wordebuf); + pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08x\n", + *wordebuf, *bad_offset); return -EIO; } return 0; diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 822949d0eb005..1b833bbffcf5c 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -82,7 +82,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) nextlist = &c->erasable_list; } else if (!list_empty(&c->erasable_pending_wbuf_list)) { - /* There are blocks are wating for the wbuf sync */ + /* There are blocks are waiting for the wbuf sync */ jffs2_dbg(1, "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n"); spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index bbab2bdc71b6e..3fb9f9807b665 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "nodelist.h" #include "debug.h" @@ -317,9 +318,9 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c) And there's no space left. At all. */ pr_crit("Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", c->nr_erasing_blocks, c->nr_free_blocks, - list_empty(&c->erasable_list) ? "yes" : "no", - list_empty(&c->erasing_list) ? "yes" : "no", - list_empty(&c->erase_pending_list) ? "yes" : "no"); + str_yes_no(list_empty(&c->erasable_list)), + str_yes_no(list_empty(&c->erasing_list)), + str_yes_no(list_empty(&c->erase_pending_list))); return -ENOSPC; } @@ -630,8 +631,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref ref->flash_offset, jeb->used_size); BUG(); }) - jffs2_dbg(1, "Obsoleting previously unchecked node at 0x%08x of len %x\n", - ref_offset(ref), freed_len); + jffs2_dbg(1, "Obsoleting previously unchecked node at 0x%08x of len %x\n", + ref_offset(ref), freed_len); jeb->unchecked_size -= freed_len; c->unchecked_size -= freed_len; } else { @@ -641,8 +642,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref ref->flash_offset, jeb->used_size); BUG(); }) - jffs2_dbg(1, "Obsoleting node at 0x%08x of len %#x: ", - ref_offset(ref), freed_len); + jffs2_dbg(1, "Obsoleting node at 0x%08x of len %#x: ", + ref_offset(ref), freed_len); jeb->used_size -= freed_len; c->used_size -= freed_len; } @@ -883,7 +884,7 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c) jffs2_dbg(1, "%s(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n", __func__, c->nr_free_blocks, c->nr_erasing_blocks, - c->dirty_size, nr_very_dirty, ret ? "yes" : "no"); + c->dirty_size, nr_very_dirty, str_yes_no(ret)); return ret; } diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 03b4f99614bef..f987f78a894e7 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -72,7 +72,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info if (err != -EOPNOTSUPP) JFFS2_WARNING("MTD point failed: error code %d.\n", err); } else - pointed = 1; /* succefully pointed to device */ + pointed = 1; /* successfully pointed to device */ #endif if (!pointed) { diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index a3e97278b997c..6ea3448d2d31e 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -2,8 +2,9 @@ /* * linux/fs/lockd/clntxdr.c * - * XDR functions to encode/decode NLM version 3 RPC arguments and results. - * NLM version 3 is backwards compatible with NLM versions 1 and 2. + * XDR functions to encode/decode NLM version 1 and 3 RPC + * arguments and results. NLM version 2 is not specified + * by a standard, thus it is not implemented. * * NLM client-side only. * diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 8a72c418cdcc0..109e5caae8c70 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -46,14 +46,15 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, if (filp != NULL) { int mode = lock_to_openmode(&lock->fl); + lock->fl.c.flc_flags = FL_POSIX; + error = nlm_lookup_file(rqstp, &file, lock); if (error) goto no_locks; *filp = file; /* Set up the missing parts of the file_lock structure */ - lock->fl.c.flc_flags = FL_POSIX; - lock->fl.c.flc_file = file->f_file[mode]; + lock->fl.c.flc_file = file->f_file[mode]; lock->fl.c.flc_pid = current->tgid; lock->fl.fl_start = (loff_t)lock->lock_start; lock->fl.fl_end = lock->lock_len ? @@ -108,7 +109,8 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) test_owner = argp->lock.fl.c.flc_owner; /* Now check for conflicting locks */ - resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); + resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, + &resp->lock); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else @@ -142,18 +144,6 @@ __nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp) if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; -#if 0 - /* If supplied state doesn't match current state, we assume it's - * an old request that time-warped somehow. Any error return would - * do in this case because it's irrelevant anyway. - * - * NB: We don't retrieve the remote host's state yet. - */ - if (host->h_nsmstate && host->h_nsmstate != argp->state) { - resp->status = nlm_lck_denied_nolocks; - } else -#endif - /* Now try to lock the file */ resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie, diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 2359347c9fbd0..c1315df4b350b 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -608,7 +608,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, __be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_host *host, struct nlm_lock *lock, - struct nlm_lock *conflock, struct nlm_cookie *cookie) + struct nlm_lock *conflock) { int error; int mode; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index a03220e66ce02..f53d5177f2673 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -130,7 +130,8 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) test_owner = argp->lock.fl.c.flc_owner; /* Now check for conflicting locks */ - resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie)); + resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, + &argp->lock, &resp->lock)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else @@ -165,18 +166,6 @@ __nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp) if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; -#if 0 - /* If supplied state doesn't match current state, we assume it's - * an old request that time-warped somehow. Any error return would - * do in this case because it's irrelevant anyway. - * - * NB: We don't retrieve the remote host's state yet. - */ - if (host->h_nsmstate && host->h_nsmstate != argp->state) { - resp->status = nlm_lck_denied_nolocks; - } else -#endif - /* Now try to lock the file */ resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie, diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 3d28b9c3ed150..e343c820301fd 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -89,7 +89,6 @@ svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) return false; locks_init_lock(fl); - fl->c.flc_flags = FL_POSIX; fl->c.flc_type = F_RDLCK; nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len); return true; @@ -268,7 +267,6 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) struct nlm_args *argp = rqstp->rq_argp; struct nlm_lock *lock = &argp->lock; - memset(lock, 0, sizeof(*lock)); locks_init_lock(&lock->fl); lock->svid = ~(u32)0; diff --git a/fs/namespace.c b/fs/namespace.c index 6b0a17487d0f8..23e81c2a1e3fe 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -5057,21 +5057,32 @@ static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq) return 0; } -static inline int statmount_opt_unescape(struct seq_file *seq, char *buf_start) +static inline int statmount_opt_process(struct seq_file *seq, size_t start) { - char *buf_end, *opt_start, *opt_end; + char *buf_end, *opt_end, *src, *dst; int count = 0; + if (unlikely(seq_has_overflowed(seq))) + return -EAGAIN; + buf_end = seq->buf + seq->count; + dst = seq->buf + start; + src = dst + 1; /* skip initial comma */ + + if (src >= buf_end) { + seq->count = start; + return 0; + } + *buf_end = '\0'; - for (opt_start = buf_start + 1; opt_start < buf_end; opt_start = opt_end + 1) { - opt_end = strchrnul(opt_start, ','); + for (; src < buf_end; src = opt_end + 1) { + opt_end = strchrnul(src, ','); *opt_end = '\0'; - buf_start += string_unescape(opt_start, buf_start, 0, UNESCAPE_OCTAL) + 1; + dst += string_unescape(src, dst, 0, UNESCAPE_OCTAL) + 1; if (WARN_ON_ONCE(++count == INT_MAX)) return -EOVERFLOW; } - seq->count = buf_start - 1 - seq->buf; + seq->count = dst - 1 - seq->buf; return count; } @@ -5080,24 +5091,16 @@ static int statmount_opt_array(struct kstatmount *s, struct seq_file *seq) struct vfsmount *mnt = s->mnt; struct super_block *sb = mnt->mnt_sb; size_t start = seq->count; - char *buf_start; int err; if (!sb->s_op->show_options) return 0; - buf_start = seq->buf + start; err = sb->s_op->show_options(seq, mnt->mnt_root); if (err) return err; - if (unlikely(seq_has_overflowed(seq))) - return -EAGAIN; - - if (seq->count == start) - return 0; - - err = statmount_opt_unescape(seq, buf_start); + err = statmount_opt_process(seq, start); if (err < 0) return err; @@ -5110,22 +5113,13 @@ static int statmount_opt_sec_array(struct kstatmount *s, struct seq_file *seq) struct vfsmount *mnt = s->mnt; struct super_block *sb = mnt->mnt_sb; size_t start = seq->count; - char *buf_start; int err; - buf_start = seq->buf + start; - err = security_sb_show_options(seq, sb); - if (!err) + if (err) return err; - if (unlikely(seq_has_overflowed(seq))) - return -EAGAIN; - - if (seq->count == start) - return 0; - - err = statmount_opt_unescape(seq, buf_start); + err = statmount_opt_process(seq, start); if (err < 0) return err; diff --git a/fs/netfs/fscache_io.c b/fs/netfs/fscache_io.c index 38637e5c9b577..b1722a82c03d3 100644 --- a/fs/netfs/fscache_io.c +++ b/fs/netfs/fscache_io.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "internal.h" /** diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 0becdec129704..47189476b5538 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -571,19 +571,32 @@ bl_find_get_deviceid(struct nfs_server *server, if (!node) return ERR_PTR(-ENODEV); + /* + * Devices that are marked unavailable are left in the cache with a + * timeout to avoid sending GETDEVINFO after every LAYOUTGET, or + * constantly attempting to register the device. Once marked as + * unavailable they must be deleted and never reused. + */ if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) { unsigned long end = jiffies; unsigned long start = end - PNFS_DEVICE_RETRY_TIMEOUT; if (!time_in_range(node->timestamp_unavailable, start, end)) { + /* Uncork subsequent GETDEVINFO operations for this device */ nfs4_delete_deviceid(node->ld, node->nfs_client, id); goto retry; } goto out_put; } - if (!bl_register_dev(container_of(node, struct pnfs_block_dev, node))) + if (!bl_register_dev(container_of(node, struct pnfs_block_dev, node))) { + /* + * If we cannot register, treat this device as transient: + * Make a negative cache entry for the device + */ + nfs4_mark_deviceid_unavailable(node); goto out_put; + } return node; diff --git a/fs/nfs/blocklayout/dev.c b/fs/nfs/blocklayout/dev.c index 6252f44479457..cab8809f0e0f4 100644 --- a/fs/nfs/blocklayout/dev.c +++ b/fs/nfs/blocklayout/dev.c @@ -20,9 +20,6 @@ static void bl_unregister_scsi(struct pnfs_block_dev *dev) const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; int status; - if (!test_and_clear_bit(PNFS_BDEV_REGISTERED, &dev->flags)) - return; - status = ops->pr_register(bdev, dev->pr_key, 0, false); if (status) trace_bl_pr_key_unreg_err(bdev, dev->pr_key, status); @@ -58,7 +55,8 @@ static void bl_unregister_dev(struct pnfs_block_dev *dev) return; } - if (dev->type == PNFS_BLOCK_VOLUME_SCSI) + if (dev->type == PNFS_BLOCK_VOLUME_SCSI && + test_and_clear_bit(PNFS_BDEV_REGISTERED, &dev->flags)) bl_unregister_scsi(dev); } diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 03ecc77656151..550ca934c9cfc 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -55,9 +55,13 @@ #define NFSDBG_FACILITY NFSDBG_CLIENT static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); -static DEFINE_SPINLOCK(nfs_version_lock); -static DEFINE_MUTEX(nfs_version_mutex); -static LIST_HEAD(nfs_versions); +static DEFINE_RWLOCK(nfs_version_lock); + +static struct nfs_subversion *nfs_version_mods[5] = { + [2] = NULL, + [3] = NULL, + [4] = NULL, +}; /* * RPC cruft for NFS @@ -76,38 +80,38 @@ const struct rpc_program nfs_program = { .pipe_dir_name = NFS_PIPE_DIRNAME, }; -static struct nfs_subversion *find_nfs_version(unsigned int version) +static struct nfs_subversion *__find_nfs_version(unsigned int version) { struct nfs_subversion *nfs; - spin_lock(&nfs_version_lock); - - list_for_each_entry(nfs, &nfs_versions, list) { - if (nfs->rpc_ops->version == version) { - spin_unlock(&nfs_version_lock); - return nfs; - } - } - spin_unlock(&nfs_version_lock); - return ERR_PTR(-EPROTONOSUPPORT); + read_lock(&nfs_version_lock); + nfs = nfs_version_mods[version]; + read_unlock(&nfs_version_lock); + return nfs; } -struct nfs_subversion *get_nfs_version(unsigned int version) +struct nfs_subversion *find_nfs_version(unsigned int version) { - struct nfs_subversion *nfs = find_nfs_version(version); + struct nfs_subversion *nfs = __find_nfs_version(version); - if (IS_ERR(nfs)) { - mutex_lock(&nfs_version_mutex); - request_module("nfsv%d", version); - nfs = find_nfs_version(version); - mutex_unlock(&nfs_version_mutex); - } + if (!nfs && request_module("nfsv%d", version) == 0) + nfs = __find_nfs_version(version); - if (!IS_ERR(nfs) && !try_module_get(nfs->owner)) + if (!nfs) + return ERR_PTR(-EPROTONOSUPPORT); + + if (!get_nfs_version(nfs)) return ERR_PTR(-EAGAIN); + return nfs; } +int get_nfs_version(struct nfs_subversion *nfs) +{ + return try_module_get(nfs->owner); +} +EXPORT_SYMBOL_GPL(get_nfs_version); + void put_nfs_version(struct nfs_subversion *nfs) { module_put(nfs->owner); @@ -115,23 +119,23 @@ void put_nfs_version(struct nfs_subversion *nfs) void register_nfs_version(struct nfs_subversion *nfs) { - spin_lock(&nfs_version_lock); + write_lock(&nfs_version_lock); - list_add(&nfs->list, &nfs_versions); + nfs_version_mods[nfs->rpc_ops->version] = nfs; nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; - spin_unlock(&nfs_version_lock); + write_unlock(&nfs_version_lock); } EXPORT_SYMBOL_GPL(register_nfs_version); void unregister_nfs_version(struct nfs_subversion *nfs) { - spin_lock(&nfs_version_lock); + write_lock(&nfs_version_lock); nfs_version[nfs->rpc_ops->version] = NULL; - list_del(&nfs->list); + nfs_version_mods[nfs->rpc_ops->version] = NULL; - spin_unlock(&nfs_version_lock); + write_unlock(&nfs_version_lock); } EXPORT_SYMBOL_GPL(unregister_nfs_version); @@ -151,7 +155,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) clp->cl_minorversion = cl_init->minorversion; clp->cl_nfs_mod = cl_init->nfs_mod; - if (!try_module_get(clp->cl_nfs_mod->owner)) + if (!get_nfs_version(clp->cl_nfs_mod)) goto error_dealloc; clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 90079ca134dd3..b08dbe96bc579 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -454,8 +454,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, if (user_backed_iter(iter)) dreq->flags = NFS_ODIRECT_SHOULD_DIRTY; - if (!swap) - nfs_start_io_direct(inode); + if (!swap) { + result = nfs_start_io_direct(inode); + if (result) { + /* release the reference that would usually be + * consumed by nfs_direct_read_schedule_iovec() + */ + nfs_direct_req_release(dreq); + goto out_release; + } + } NFS_I(inode)->read_io += count; requested = nfs_direct_read_schedule_iovec(dreq, iter, iocb->ki_pos); @@ -1007,7 +1015,14 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, requested = nfs_direct_write_schedule_iovec(dreq, iter, pos, FLUSH_STABLE); } else { - nfs_start_io_direct(inode); + result = nfs_start_io_direct(inode); + if (result) { + /* release the reference that would usually be + * consumed by nfs_direct_write_schedule_iovec() + */ + nfs_direct_req_release(dreq); + goto out_release; + } requested = nfs_direct_write_schedule_iovec(dreq, iter, pos, FLUSH_COND_STABLE); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 6800ee92d742a..1bb646752e466 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -166,7 +166,10 @@ nfs_file_read(struct kiocb *iocb, struct iov_iter *to) iocb->ki_filp, iov_iter_count(to), (unsigned long) iocb->ki_pos); - nfs_start_io_read(inode); + result = nfs_start_io_read(inode); + if (result) + return result; + result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); if (!result) { result = generic_file_read_iter(iocb, to); @@ -187,7 +190,10 @@ nfs_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe dprintk("NFS: splice_read(%pD2, %zu@%llu)\n", in, len, *ppos); - nfs_start_io_read(inode); + result = nfs_start_io_read(inode); + if (result) + return result; + result = nfs_revalidate_mapping(inode, in->f_mapping); if (!result) { result = filemap_splice_read(in, ppos, pipe, len, flags); @@ -668,7 +674,9 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from) nfs_clear_invalid_mapping(file->f_mapping); since = filemap_sample_wb_err(file->f_mapping); - nfs_start_io_write(inode); + error = nfs_start_io_write(inode); + if (error) + return error; result = generic_write_checks(iocb, from); if (result > 0) result = generic_perform_write(iocb, from); diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index 7e000d782e283..b069385eea176 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -1467,7 +1467,7 @@ static int nfs_fs_context_validate(struct fs_context *fc) /* Load the NFS protocol module if we haven't done so yet */ if (!ctx->nfs_mod) { - nfs_mod = get_nfs_version(ctx->version); + nfs_mod = find_nfs_version(ctx->version); if (IS_ERR(nfs_mod)) { ret = PTR_ERR(nfs_mod); goto out_version_unavailable; @@ -1541,7 +1541,7 @@ static int nfs_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc) } nfs_copy_fh(ctx->mntfh, src->mntfh); - __module_get(ctx->nfs_mod->owner); + get_nfs_version(ctx->nfs_mod); ctx->client_address = NULL; ctx->mount_server.hostname = NULL; ctx->nfs_server.export_path = NULL; @@ -1633,7 +1633,7 @@ static int nfs_init_fs_context(struct fs_context *fc) } ctx->nfs_mod = nfss->nfs_client->cl_nfs_mod; - __module_get(ctx->nfs_mod->owner); + get_nfs_version(ctx->nfs_mod); } else { /* defaults */ ctx->timeo = NFS_UNSPEC_TIMEO; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 430733e3eff26..e564bd11ba607 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -6,13 +6,14 @@ #include "nfs4_fs.h" #include #include +#include #include #include #include #include #include -#define NFS_SB_MASK (SB_RDONLY|SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS) +#define NFS_SB_MASK (SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS) extern const struct export_operations nfs_export_ops; @@ -516,11 +517,11 @@ extern const struct netfs_request_ops nfs_netfs_ops; #endif /* io.c */ -extern void nfs_start_io_read(struct inode *inode); +extern __must_check int nfs_start_io_read(struct inode *inode); extern void nfs_end_io_read(struct inode *inode); -extern void nfs_start_io_write(struct inode *inode); +extern __must_check int nfs_start_io_write(struct inode *inode); extern void nfs_end_io_write(struct inode *inode); -extern void nfs_start_io_direct(struct inode *inode); +extern __must_check int nfs_start_io_direct(struct inode *inode); extern void nfs_end_io_direct(struct inode *inode); static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi) diff --git a/fs/nfs/io.c b/fs/nfs/io.c index b5551ed8f648b..3388faf2acb9f 100644 --- a/fs/nfs/io.c +++ b/fs/nfs/io.c @@ -39,19 +39,28 @@ static void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode) * Note that buffered writes and truncates both take a write lock on * inode->i_rwsem, meaning that those are serialised w.r.t. the reads. */ -void +int nfs_start_io_read(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); + int err; + /* Be an optimist! */ - down_read(&inode->i_rwsem); + err = down_read_killable(&inode->i_rwsem); + if (err) + return err; if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0) - return; + return 0; up_read(&inode->i_rwsem); + /* Slow path.... */ - down_write(&inode->i_rwsem); + err = down_write_killable(&inode->i_rwsem); + if (err) + return err; nfs_block_o_direct(nfsi, inode); downgrade_write(&inode->i_rwsem); + + return 0; } /** @@ -74,11 +83,15 @@ nfs_end_io_read(struct inode *inode) * Declare that a buffered read operation is about to start, and ensure * that we block all direct I/O. */ -void +int nfs_start_io_write(struct inode *inode) { - down_write(&inode->i_rwsem); - nfs_block_o_direct(NFS_I(inode), inode); + int err; + + err = down_write_killable(&inode->i_rwsem); + if (!err) + nfs_block_o_direct(NFS_I(inode), inode); + return err; } /** @@ -119,19 +132,28 @@ static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode) * Note that buffered writes and truncates both take a write lock on * inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT. */ -void +int nfs_start_io_direct(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); + int err; + /* Be an optimist! */ - down_read(&inode->i_rwsem); + err = down_read_killable(&inode->i_rwsem); + if (err) + return err; if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) != 0) - return; + return 0; up_read(&inode->i_rwsem); + /* Slow path.... */ - down_write(&inode->i_rwsem); + err = down_write_killable(&inode->i_rwsem); + if (err) + return err; nfs_block_buffered(nfsi, inode); downgrade_write(&inode->i_rwsem); + + return 0; } /** diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 8f0ce82a677e1..4b8618cf114ca 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -42,10 +42,8 @@ struct nfs_local_fsync_ctx { struct nfsd_file *localio; struct nfs_commit_data *data; struct work_struct work; - struct kref kref; struct completion *done; }; -static void nfs_local_fsync_work(struct work_struct *work); static bool localio_enabled __read_mostly = true; module_param(localio_enabled, bool, 0644); @@ -274,7 +272,7 @@ nfs_local_iocb_free(struct nfs_local_kiocb *iocb) static struct nfs_local_kiocb * nfs_local_iocb_alloc(struct nfs_pgio_header *hdr, - struct nfsd_file *localio, gfp_t flags) + struct file *file, gfp_t flags) { struct nfs_local_kiocb *iocb; @@ -287,9 +285,8 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr, kfree(iocb); return NULL; } - init_sync_kiocb(&iocb->kiocb, nfs_to->nfsd_file_file(localio)); + init_sync_kiocb(&iocb->kiocb, file); iocb->kiocb.ki_pos = hdr->args.offset; - iocb->localio = localio; iocb->hdr = hdr; iocb->kiocb.ki_flags &= ~IOCB_APPEND; return iocb; @@ -354,6 +351,12 @@ nfs_local_read_done(struct nfs_local_kiocb *iocb, long status) nfs_local_pgio_done(hdr, status); + /* + * Must clear replen otherwise NFSv3 data corruption will occur + * if/when switching from LOCALIO back to using normal RPC. + */ + hdr->res.replen = 0; + if (hdr->res.count != hdr->args.count || hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp))) hdr->res.eof = true; @@ -390,13 +393,19 @@ nfs_do_local_read(struct nfs_pgio_header *hdr, const struct rpc_call_ops *call_ops) { struct nfs_local_kiocb *iocb; + struct file *file = nfs_to->nfsd_file_file(localio); + + /* Don't support filesystems without read_iter */ + if (!file->f_op->read_iter) + return -EAGAIN; dprintk("%s: vfs_read count=%u pos=%llu\n", __func__, hdr->args.count, hdr->args.offset); - iocb = nfs_local_iocb_alloc(hdr, localio, GFP_KERNEL); + iocb = nfs_local_iocb_alloc(hdr, file, GFP_KERNEL); if (iocb == NULL) return -ENOMEM; + iocb->localio = localio; nfs_local_pgio_init(hdr, call_ops); hdr->res.eof = false; @@ -521,12 +530,7 @@ nfs_local_write_done(struct nfs_local_kiocb *iocb, long status) } if (status < 0) nfs_reset_boot_verifier(inode); - else if (nfs_should_remove_suid(inode)) { - /* Deal with the suid/sgid bit corner case */ - spin_lock(&inode->i_lock); - nfs_set_cache_invalid(inode, NFS_INO_INVALID_MODE); - spin_unlock(&inode->i_lock); - } + nfs_local_pgio_done(hdr, status); } @@ -564,14 +568,20 @@ nfs_do_local_write(struct nfs_pgio_header *hdr, const struct rpc_call_ops *call_ops) { struct nfs_local_kiocb *iocb; + struct file *file = nfs_to->nfsd_file_file(localio); + + /* Don't support filesystems without write_iter */ + if (!file->f_op->write_iter) + return -EAGAIN; dprintk("%s: vfs_write count=%u pos=%llu %s\n", __func__, hdr->args.count, hdr->args.offset, (hdr->args.stable == NFS_UNSTABLE) ? "unstable" : "stable"); - iocb = nfs_local_iocb_alloc(hdr, localio, GFP_NOIO); + iocb = nfs_local_iocb_alloc(hdr, file, GFP_NOIO); if (iocb == NULL) return -ENOMEM; + iocb->localio = localio; switch (hdr->args.stable) { default: @@ -597,16 +607,9 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, const struct rpc_call_ops *call_ops) { int status = 0; - struct file *filp = nfs_to->nfsd_file_file(localio); if (!hdr->args.count) return 0; - /* Don't support filesystems without read_iter/write_iter */ - if (!filp->f_op->read_iter || !filp->f_op->write_iter) { - nfs_local_disable(clp); - status = -EAGAIN; - goto out; - } switch (hdr->rw_mode) { case FMODE_READ: @@ -620,8 +623,10 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, hdr->rw_mode); status = -EINVAL; } -out: + if (status != 0) { + if (status == -EAGAIN) + nfs_local_disable(clp); nfs_to_nfsd_file_put_local(localio); hdr->task.tk_status = status; nfs_local_hdr_release(hdr, call_ops); @@ -678,40 +683,12 @@ nfs_local_release_commit_data(struct nfsd_file *localio, call_ops->rpc_release(data); } -static struct nfs_local_fsync_ctx * -nfs_local_fsync_ctx_alloc(struct nfs_commit_data *data, - struct nfsd_file *localio, gfp_t flags) -{ - struct nfs_local_fsync_ctx *ctx = kmalloc(sizeof(*ctx), flags); - - if (ctx != NULL) { - ctx->localio = localio; - ctx->data = data; - INIT_WORK(&ctx->work, nfs_local_fsync_work); - kref_init(&ctx->kref); - ctx->done = NULL; - } - return ctx; -} - -static void -nfs_local_fsync_ctx_kref_free(struct kref *kref) -{ - kfree(container_of(kref, struct nfs_local_fsync_ctx, kref)); -} - -static void -nfs_local_fsync_ctx_put(struct nfs_local_fsync_ctx *ctx) -{ - kref_put(&ctx->kref, nfs_local_fsync_ctx_kref_free); -} - static void nfs_local_fsync_ctx_free(struct nfs_local_fsync_ctx *ctx) { nfs_local_release_commit_data(ctx->localio, ctx->data, ctx->data->task.tk_ops); - nfs_local_fsync_ctx_put(ctx); + kfree(ctx); } static void @@ -730,6 +707,21 @@ nfs_local_fsync_work(struct work_struct *work) nfs_local_fsync_ctx_free(ctx); } +static struct nfs_local_fsync_ctx * +nfs_local_fsync_ctx_alloc(struct nfs_commit_data *data, + struct nfsd_file *localio, gfp_t flags) +{ + struct nfs_local_fsync_ctx *ctx = kmalloc(sizeof(*ctx), flags); + + if (ctx != NULL) { + ctx->localio = localio; + ctx->data = data; + INIT_WORK(&ctx->work, nfs_local_fsync_work); + ctx->done = NULL; + } + return ctx; +} + int nfs_local_commit(struct nfsd_file *localio, struct nfs_commit_data *data, const struct rpc_call_ops *call_ops, int how) @@ -744,7 +736,7 @@ int nfs_local_commit(struct nfsd_file *localio, } nfs_local_init_commit(data, call_ops); - kref_get(&ctx->kref); + if (how & FLUSH_SYNC) { DECLARE_COMPLETION_ONSTACK(done); ctx->done = &done; @@ -752,6 +744,6 @@ int nfs_local_commit(struct nfsd_file *localio, wait_for_completion(&done); } else queue_work(nfsiod_workqueue, &ctx->work); - nfs_local_fsync_ctx_put(ctx); + return 0; } diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index e7494cdd957e5..2d53574da6059 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -182,7 +182,7 @@ struct vfsmount *nfs_d_automount(struct path *path) ctx->version = client->rpc_ops->version; ctx->minorversion = client->cl_minorversion; ctx->nfs_mod = client->cl_nfs_mod; - __module_get(ctx->nfs_mod->owner); + get_nfs_version(ctx->nfs_mod); ret = client->rpc_ops->submount(fc, server); if (ret < 0) { diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h index 0d3ce0460e35b..8a5f51be013a6 100644 --- a/fs/nfs/nfs.h +++ b/fs/nfs/nfs.h @@ -19,10 +19,10 @@ struct nfs_subversion { const struct nfs_rpc_ops *rpc_ops; /* NFS operations */ const struct super_operations *sops; /* NFS Super operations */ const struct xattr_handler * const *xattr; /* NFS xattr handlers */ - struct list_head list; /* List of NFS versions */ }; -struct nfs_subversion *get_nfs_version(unsigned int); +struct nfs_subversion *find_nfs_version(unsigned int); +int get_nfs_version(struct nfs_subversion *); void put_nfs_version(struct nfs_subversion *); void register_nfs_version(struct nfs_subversion *); void unregister_nfs_version(struct nfs_subversion *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9d40319e063de..405f17e6e0b45 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2603,12 +2603,14 @@ static void nfs4_open_release(void *calldata) struct nfs4_opendata *data = calldata; struct nfs4_state *state = NULL; + /* In case of error, no cleanup! */ + if (data->rpc_status != 0 || !data->rpc_done) { + nfs_release_seqid(data->o_arg.seqid); + goto out_free; + } /* If this request hasn't been cancelled, do nothing */ if (!data->cancelled) goto out_free; - /* In case of error, no cleanup! */ - if (data->rpc_status != 0 || !data->rpc_done) - goto out_free; /* In case we need an open_confirm, no cleanup! */ if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) goto out_free; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index dafd61186557f..9a9f60a2291b4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1083,14 +1083,12 @@ void nfs_release_seqid(struct nfs_seqid *seqid) return; sequence = seqid->sequence; spin_lock(&sequence->lock); - list_del_init(&seqid->list); - if (!list_empty(&sequence->list)) { - struct nfs_seqid *next; - - next = list_first_entry(&sequence->list, - struct nfs_seqid, list); + if (list_is_first(&seqid->list, &sequence->list) && + !list_is_singular(&sequence->list)) { + struct nfs_seqid *next = list_next_entry(seqid, list); rpc_wake_up_queued_task(&sequence->wait, next->task); } + list_del_init(&seqid->list); spin_unlock(&sequence->lock); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index ead2dc55952db..50fa539611f5e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -144,6 +144,31 @@ static void nfs_io_completion_put(struct nfs_io_completion *ioc) kref_put(&ioc->refcount, nfs_io_completion_release); } +static void +nfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode) +{ + if (!test_and_set_bit(PG_INODE_REF, &req->wb_flags)) { + kref_get(&req->wb_kref); + atomic_long_inc(&NFS_I(inode)->nrequests); + } +} + +static int +nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode) +{ + int ret; + + if (!test_bit(PG_REMOVE, &req->wb_flags)) + return 0; + ret = nfs_page_group_lock(req); + if (ret) + return ret; + if (test_and_clear_bit(PG_REMOVE, &req->wb_flags)) + nfs_page_set_inode_ref(req, inode); + nfs_page_group_unlock(req); + return 0; +} + /** * nfs_folio_find_head_request - find head request associated with a folio * @folio: pointer to folio @@ -540,7 +565,6 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio) struct inode *inode = folio->mapping->host; struct nfs_page *head, *subreq; struct nfs_commit_info cinfo; - bool removed; int ret; /* @@ -565,18 +589,18 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio) goto retry; } - ret = nfs_page_group_lock(head); + ret = nfs_cancel_remove_inode(head, inode); if (ret < 0) goto out_unlock; - removed = test_bit(PG_REMOVE, &head->wb_flags); + ret = nfs_page_group_lock(head); + if (ret < 0) + goto out_unlock; /* lock each request in the page group */ for (subreq = head->wb_this_page; subreq != head; subreq = subreq->wb_this_page) { - if (test_bit(PG_REMOVE, &subreq->wb_flags)) - removed = true; ret = nfs_page_group_lock_subreq(head, subreq); if (ret < 0) goto out_unlock; @@ -584,21 +608,6 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio) nfs_page_group_unlock(head); - /* - * If PG_REMOVE is set on any request, I/O on that request has - * completed, but some requests were still under I/O at the time - * we locked the head request. - * - * In that case the above wait for all requests means that all I/O - * has now finished, and we can restart from a clean slate. Let the - * old requests go away and start from scratch instead. - */ - if (removed) { - nfs_unroll_locks(head, head); - nfs_unlock_and_release_request(head); - goto retry; - } - nfs_init_cinfo_from_inode(&cinfo, inode); nfs_join_page_group(head, &cinfo, inode); return head; @@ -772,7 +781,8 @@ static void nfs_inode_add_request(struct nfs_page *req) nfs_lock_request(req); spin_lock(&mapping->i_private_lock); set_bit(PG_MAPPED, &req->wb_flags); - folio_attach_private(folio, req); + folio_set_private(folio); + folio->private = req; spin_unlock(&mapping->i_private_lock); atomic_long_inc(&nfsi->nrequests); /* this a head request for a page group - mark it as having an @@ -796,7 +806,8 @@ static void nfs_inode_remove_request(struct nfs_page *req) spin_lock(&mapping->i_private_lock); if (likely(folio)) { - folio_detach_private(folio); + folio->private = NULL; + folio_clear_private(folio); clear_bit(PG_MAPPED, &req->wb_head->wb_flags); } spin_unlock(&mapping->i_private_lock); diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c index 09404d142d1ae..a74ec08f6c96d 100644 --- a/fs/nfs_common/nfslocalio.c +++ b/fs/nfs_common/nfslocalio.c @@ -155,11 +155,9 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid, /* We have an implied reference to net thanks to nfsd_serv_try_get */ localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, cred, nfs_fh, fmode); - if (IS_ERR(localio)) { - rcu_read_lock(); - nfs_to->nfsd_serv_put(net); - rcu_read_unlock(); - } + if (IS_ERR(localio)) + nfs_to_nfsd_net_put(net); + return localio; } EXPORT_SYMBOL_GPL(nfs_open_local_fh); diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index c82d8e3e0d4f2..eacafe46e3b67 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -40,15 +40,24 @@ #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) -static void expkey_put(struct kref *ref) +static void expkey_put_work(struct work_struct *work) { - struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); + struct svc_expkey *key = + container_of(to_rcu_work(work), struct svc_expkey, ek_rcu_work); if (test_bit(CACHE_VALID, &key->h.flags) && !test_bit(CACHE_NEGATIVE, &key->h.flags)) path_put(&key->ek_path); auth_domain_put(key->ek_client); - kfree_rcu(key, ek_rcu); + kfree(key); +} + +static void expkey_put(struct kref *ref) +{ + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); + + INIT_RCU_WORK(&key->ek_rcu_work, expkey_put_work); + queue_rcu_work(system_wq, &key->ek_rcu_work); } static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) @@ -355,16 +364,26 @@ static void export_stats_destroy(struct export_stats *stats) EXP_STATS_COUNTERS_NUM); } -static void svc_export_put(struct kref *ref) +static void svc_export_put_work(struct work_struct *work) { - struct svc_export *exp = container_of(ref, struct svc_export, h.ref); + struct svc_export *exp = + container_of(to_rcu_work(work), struct svc_export, ex_rcu_work); + path_put(&exp->ex_path); auth_domain_put(exp->ex_client); nfsd4_fslocs_free(&exp->ex_fslocs); export_stats_destroy(exp->ex_stats); kfree(exp->ex_stats); kfree(exp->ex_uuid); - kfree_rcu(exp, ex_rcu); + kfree(exp); +} + +static void svc_export_put(struct kref *ref) +{ + struct svc_export *exp = container_of(ref, struct svc_export, h.ref); + + INIT_RCU_WORK(&exp->ex_rcu_work, svc_export_put_work); + queue_rcu_work(system_wq, &exp->ex_rcu_work); } static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) @@ -1078,12 +1097,14 @@ static struct svc_export *exp_find(struct cache_detail *cd, * check_nfsd_access - check if access to export is allowed. * @exp: svc_export that is being accessed. * @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO). + * @may_bypass_gss: reduce strictness of authorization check * * Return values: * %nfs_ok if access is granted, or * %nfserr_wrongsec if access is denied */ -__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp) +__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, + bool may_bypass_gss) { struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors; struct svc_xprt *xprt; @@ -1140,6 +1161,23 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp) if (nfsd4_spo_must_allow(rqstp)) return nfs_ok; + /* Some calls may be processed without authentication + * on GSS exports. For example NFS2/3 calls on root + * directory, see section 2.3.2 of rfc 2623. + * For "may_bypass_gss" check that export has really + * enabled some flavor with authentication (GSS or any + * other) and also check that the used auth flavor is + * without authentication (none or sys). + */ + if (may_bypass_gss && ( + rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL || + rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)) { + for (f = exp->ex_flavors; f < end; f++) { + if (f->pseudoflavor >= RPC_AUTH_DES) + return 0; + } + } + denied: return nfserr_wrongsec; } @@ -1406,9 +1444,12 @@ static int e_show(struct seq_file *m, void *p) return 0; } - exp_get(exp); + if (!cache_get_rcu(&exp->h)) + return 0; + if (cache_check(cd, &exp->h, NULL)) return 0; + exp_put(exp); return svc_export_show(m, cd, cp); } diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h index 3794ae253a701..6f2fbaae01fa2 100644 --- a/fs/nfsd/export.h +++ b/fs/nfsd/export.h @@ -75,7 +75,7 @@ struct svc_export { u32 ex_layout_types; struct nfsd4_deviceid_map *ex_devid_map; struct cache_detail *cd; - struct rcu_head ex_rcu; + struct rcu_work ex_rcu_work; unsigned long ex_xprtsec_modes; struct export_stats *ex_stats; }; @@ -92,7 +92,7 @@ struct svc_expkey { u32 ek_fsid[6]; struct path ek_path; - struct rcu_head ek_rcu; + struct rcu_work ek_rcu_work; }; #define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) @@ -101,7 +101,8 @@ struct svc_expkey { struct svc_cred; int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp); -__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); +__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, + bool may_bypass_gss); /* * Function declarations diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index 09c444eb944fe..a1cdba42c4fad 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -391,19 +391,19 @@ nfsd_file_put(struct nfsd_file *nf) } /** - * nfsd_file_put_local - put the reference to nfsd_file and local nfsd_serv - * @nf: nfsd_file of which to put the references + * nfsd_file_put_local - put nfsd_file reference and arm nfsd_serv_put in caller + * @nf: nfsd_file of which to put the reference * - * First put the reference of the nfsd_file and then put the - * reference to the associated nn->nfsd_serv. + * First save the associated net to return to caller, then put + * the reference of the nfsd_file. */ -void -nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu) +struct net * +nfsd_file_put_local(struct nfsd_file *nf) { struct net *net = nf->nf_net; nfsd_file_put(nf); - nfsd_serv_put(net); + return net; } /** @@ -1047,7 +1047,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, * the last one however, since we should hold another. */ if (nfsd_file_lru_remove(nf)) - WARN_ON_ONCE(refcount_dec_and_test(&nf->nf_ref)); + refcount_dec(&nf->nf_ref); goto wait_for_construction; } @@ -1120,8 +1120,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, status = nfs_ok; trace_nfsd_file_opened(nf, status); } else { - ret = nfsd_open_verified(rqstp, fhp, may_flags, - &nf->nf_file); + ret = nfsd_open_verified(fhp, may_flags, &nf->nf_file); if (ret == -EOPENSTALE && stale_retry) { stale_retry = false; nfsd_file_unhash(nf); diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h index cadf3c2689c44..d5db6b34ba302 100644 --- a/fs/nfsd/filecache.h +++ b/fs/nfsd/filecache.h @@ -55,7 +55,7 @@ void nfsd_file_cache_shutdown(void); int nfsd_file_cache_start_net(struct net *net); void nfsd_file_cache_shutdown_net(struct net *net); void nfsd_file_put(struct nfsd_file *nf); -void nfsd_file_put_local(struct nfsd_file *nf); +struct net *nfsd_file_put_local(struct nfsd_file *nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); struct file *nfsd_file_file(struct nfsd_file *nf); void nfsd_file_close_inode_sync(struct inode *inode); diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 46a7f9b813e52..edc9f75dc75c6 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -38,11 +38,20 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, memcpy(&fh.fh_handle.fh_raw, f->data, f->size); fh.fh_export = NULL; + /* + * Allow BYPASS_GSS as some client implementations use AUTH_SYS + * for NLM even when GSS is used for NFS. + * Allow OWNER_OVERRIDE as permission might have been changed + * after the file was opened. + * Pass MAY_NLM so that authentication can be completely bypassed + * if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL + * for NLM requests. + */ access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ; - access |= NFSD_MAY_LOCK; + access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS; nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp); fh_put(&fh); - /* We return nlm error codes as nlm doesn't know + /* We return nlm error codes as nlm doesn't know * about nfsd, but nfsd does know about nlm.. */ switch (nfserr) { diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 96e786b5e5444..936ea1ad95867 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -198,8 +198,6 @@ summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas) memset(pas, 0, sizeof(*pas)); pas->mask = 07; - pe = acl->a_entries + acl->a_count; - FOREACH_ACL_ENTRY(pa, acl, pe) { switch (pa->e_tag) { case ACL_USER_OBJ: diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index b5b3ab9d719a7..3877b53e429fd 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -287,17 +287,17 @@ static int decode_cb_compound4res(struct xdr_stream *xdr, u32 length; __be32 *p; - p = xdr_inline_decode(xdr, 4 + 4); + p = xdr_inline_decode(xdr, XDR_UNIT); if (unlikely(p == NULL)) goto out_overflow; - hdr->status = be32_to_cpup(p++); + hdr->status = be32_to_cpup(p); /* Ignore the tag */ - length = be32_to_cpup(p++); - p = xdr_inline_decode(xdr, length + 4); - if (unlikely(p == NULL)) + if (xdr_stream_decode_u32(xdr, &length) < 0) + goto out_overflow; + if (xdr_inline_decode(xdr, length) == NULL) + goto out_overflow; + if (xdr_stream_decode_u32(xdr, &hdr->nops) < 0) goto out_overflow; - p += XDR_QUADLEN(length); - hdr->nops = be32_to_cpup(p); return 0; out_overflow: return -EIO; @@ -364,13 +364,29 @@ encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr, struct nfs4_delegation *dp = container_of(fattr, struct nfs4_delegation, dl_cb_fattr); struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle; + u32 bmap[1]; + + bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE; encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR); encode_nfs_fh4(xdr, fh); - encode_bitmap4(xdr, fattr->ncf_cb_bmap, ARRAY_SIZE(fattr->ncf_cb_bmap)); + encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap)); hdr->nops++; } +static u32 highest_slotid(struct nfsd4_session *ses) +{ + u32 idx; + + spin_lock(&ses->se_lock); + idx = fls(~ses->se_cb_slot_avail); + if (idx > 0) + --idx; + idx = max(idx, ses->se_cb_highest_slot); + spin_unlock(&ses->se_lock); + return idx; +} + /* * CB_SEQUENCE4args * @@ -397,15 +413,40 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr, encode_sessionid4(xdr, session); p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4); - *p++ = cpu_to_be32(session->se_cb_seq_nr); /* csa_sequenceid */ - *p++ = xdr_zero; /* csa_slotid */ - *p++ = xdr_zero; /* csa_highest_slotid */ + *p++ = cpu_to_be32(session->se_cb_seq_nr[cb->cb_held_slot]); /* csa_sequenceid */ + *p++ = cpu_to_be32(cb->cb_held_slot); /* csa_slotid */ + *p++ = cpu_to_be32(highest_slotid(session)); /* csa_highest_slotid */ *p++ = xdr_zero; /* csa_cachethis */ xdr_encode_empty_array(p); /* csa_referring_call_lists */ hdr->nops++; } +static void update_cb_slot_table(struct nfsd4_session *ses, u32 target) +{ + /* No need to do anything if nothing changed */ + if (likely(target == READ_ONCE(ses->se_cb_highest_slot))) + return; + + spin_lock(&ses->se_lock); + if (target > ses->se_cb_highest_slot) { + int i; + + target = min(target, NFSD_BC_SLOT_TABLE_SIZE - 1); + + /* + * Growing the slot table. Reset any new sequences to 1. + * + * NB: There is some debate about whether the RFC requires this, + * but the Linux client expects it. + */ + for (i = ses->se_cb_highest_slot + 1; i <= target; ++i) + ses->se_cb_seq_nr[i] = 1; + } + ses->se_cb_highest_slot = target; + spin_unlock(&ses->se_lock); +} + /* * CB_SEQUENCE4resok * @@ -433,7 +474,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, struct nfsd4_session *session = cb->cb_clp->cl_cb_session; int status = -ESERVERFAULT; __be32 *p; - u32 dummy; + u32 seqid, slotid, target; /* * If the server returns different values for sessionID, slotID or @@ -449,21 +490,22 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, } p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); - dummy = be32_to_cpup(p++); - if (dummy != session->se_cb_seq_nr) { + seqid = be32_to_cpup(p++); + if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) { dprintk("NFS: %s Invalid sequence number\n", __func__); goto out; } - dummy = be32_to_cpup(p++); - if (dummy != 0) { + slotid = be32_to_cpup(p++); + if (slotid != cb->cb_held_slot) { dprintk("NFS: %s Invalid slotid\n", __func__); goto out; } - /* - * FIXME: process highest slotid and target highest slotid - */ + p++; // ignore current highest slot value + + target = be32_to_cpup(p++); + update_cb_slot_table(session, target); status = 0; out: cb->cb_seq_status = status; @@ -1164,6 +1206,22 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) spin_unlock(&clp->cl_lock); } +static int grab_slot(struct nfsd4_session *ses) +{ + int idx; + + spin_lock(&ses->se_lock); + idx = ffs(ses->se_cb_slot_avail) - 1; + if (idx < 0 || idx > ses->se_cb_highest_slot) { + spin_unlock(&ses->se_lock); + return -1; + } + /* clear the bit for the slot */ + ses->se_cb_slot_avail &= ~BIT(idx); + spin_unlock(&ses->se_lock); + return idx; +} + /* * There's currently a single callback channel slot. * If the slot is available, then mark it busy. Otherwise, set the @@ -1172,28 +1230,32 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task) { struct nfs4_client *clp = cb->cb_clp; + struct nfsd4_session *ses = clp->cl_cb_session; - if (!cb->cb_holds_slot && - test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { + if (cb->cb_held_slot >= 0) + return true; + cb->cb_held_slot = grab_slot(ses); + if (cb->cb_held_slot < 0) { rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); /* Race breaker */ - if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { - dprintk("%s slot is busy\n", __func__); + cb->cb_held_slot = grab_slot(ses); + if (cb->cb_held_slot < 0) return false; - } rpc_wake_up_queued_task(&clp->cl_cb_waitq, task); } - cb->cb_holds_slot = true; return true; } static void nfsd41_cb_release_slot(struct nfsd4_callback *cb) { struct nfs4_client *clp = cb->cb_clp; + struct nfsd4_session *ses = clp->cl_cb_session; - if (cb->cb_holds_slot) { - cb->cb_holds_slot = false; - clear_bit(0, &clp->cl_cb_slot_busy); + if (cb->cb_held_slot >= 0) { + spin_lock(&ses->se_lock); + ses->se_cb_slot_avail |= BIT(cb->cb_held_slot); + spin_unlock(&ses->se_lock); + cb->cb_held_slot = -1; rpc_wake_up_next(&clp->cl_cb_waitq); } } @@ -1210,8 +1272,8 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb) } /* - * TODO: cb_sequence should support referring call lists, cachethis, multiple - * slots, and mark callback channel down on communication errors. + * TODO: cb_sequence should support referring call lists, cachethis, + * and mark callback channel down on communication errors. */ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) { @@ -1253,7 +1315,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback return true; } - if (!cb->cb_holds_slot) + if (cb->cb_held_slot < 0) goto need_restart; /* This is the operation status code for CB_SEQUENCE */ @@ -1267,10 +1329,10 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback * If CB_SEQUENCE returns an error, then the state of the slot * (sequence ID, cached reply) MUST NOT change. */ - ++session->se_cb_seq_nr; + ++session->se_cb_seq_nr[cb->cb_held_slot]; break; case -ESERVERFAULT: - ++session->se_cb_seq_nr; + ++session->se_cb_seq_nr[cb->cb_held_slot]; nfsd4_mark_cb_fault(cb->cb_clp); ret = false; break; @@ -1296,17 +1358,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback case -NFS4ERR_BADSLOT: goto retry_nowait; case -NFS4ERR_SEQ_MISORDERED: - if (session->se_cb_seq_nr != 1) { - session->se_cb_seq_nr = 1; + if (session->se_cb_seq_nr[cb->cb_held_slot] != 1) { + session->se_cb_seq_nr[cb->cb_held_slot] = 1; goto retry_nowait; } break; default: nfsd4_mark_cb_fault(cb->cb_clp); } - nfsd41_cb_release_slot(cb); - trace_nfsd_cb_free_slot(task, cb); + nfsd41_cb_release_slot(cb); if (RPC_SIGNALLED(task)) goto need_restart; @@ -1461,6 +1522,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) ses = c->cn_session; } spin_unlock(&clp->cl_lock); + if (!c) + return; err = setup_callback_client(clp, &conn, ses); if (err) { @@ -1524,7 +1587,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); cb->cb_status = 0; cb->cb_need_restart = false; - cb->cb_holds_slot = false; + cb->cb_held_slot = -1; } /** diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index d32f2dfd148fe..f8a10f90bc7a4 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -57,6 +57,8 @@ module_param(inter_copy_offload_enable, bool, 0644); MODULE_PARM_DESC(inter_copy_offload_enable, "Enable inter server to server copy offload. Default: false"); +static void cleanup_async_copy(struct nfsd4_copy *copy); + #ifdef CONFIG_NFSD_V4_2_INTER_SSC static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */ module_param(nfsd4_ssc_umount_timeout, int, 0644); @@ -1276,6 +1278,71 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return status; } +/** + * nfsd4_has_active_async_copies - Check for ongoing copy operations + * @clp: Client to be checked + * + * NFSD maintains state for async COPY operations after they complete, + * and this state remains in the nfs4_client's async_copies list. + * Ongoing copies should block the destruction of the nfs4_client, but + * completed copies should not. + * + * Return values: + * %true: At least one active async COPY is ongoing + * %false: No active async COPY operations were found + */ +bool nfsd4_has_active_async_copies(struct nfs4_client *clp) +{ + struct nfsd4_copy *copy; + bool result = false; + + spin_lock(&clp->async_lock); + list_for_each_entry(copy, &clp->async_copies, copies) { + if (!test_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags) && + !test_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) { + result = true; + break; + } + } + spin_unlock(&clp->async_lock); + return result; +} + +/** + * nfsd4_async_copy_reaper - Purge completed copies + * @nn: Network namespace with possible active copy information + */ +void nfsd4_async_copy_reaper(struct nfsd_net *nn) +{ + struct nfs4_client *clp; + struct nfsd4_copy *copy; + LIST_HEAD(reaplist); + + spin_lock(&nn->client_lock); + list_for_each_entry(clp, &nn->client_lru, cl_lru) { + struct list_head *pos, *next; + + spin_lock(&clp->async_lock); + list_for_each_safe(pos, next, &clp->async_copies) { + copy = list_entry(pos, struct nfsd4_copy, copies); + if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags)) { + if (--copy->cp_ttl) { + list_del_init(©->copies); + list_add(©->copies, &reaplist); + } + } + } + spin_unlock(&clp->async_lock); + } + spin_unlock(&nn->client_lock); + + while (!list_empty(&reaplist)) { + copy = list_first_entry(&reaplist, struct nfsd4_copy, copies); + list_del_init(©->copies); + cleanup_async_copy(copy); + } +} + static void nfs4_put_copy(struct nfsd4_copy *copy) { if (!refcount_dec_and_test(©->refcount)) @@ -1287,12 +1354,13 @@ static void nfs4_put_copy(struct nfsd4_copy *copy) static void nfsd4_stop_copy(struct nfsd4_copy *copy) { + trace_nfsd_copy_async_cancel(copy); if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) kthread_stop(copy->copy_task); nfs4_put_copy(copy); } -static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp) +static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp) { struct nfsd4_copy *copy = NULL; @@ -1301,6 +1369,9 @@ static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp) copy = list_first_entry(&clp->async_copies, struct nfsd4_copy, copies); refcount_inc(©->refcount); + copy->cp_clp = NULL; + if (!list_empty(©->copies)) + list_del_init(©->copies); } spin_unlock(&clp->async_lock); return copy; @@ -1310,7 +1381,7 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp) { struct nfsd4_copy *copy; - while ((copy = nfsd4_get_copy(clp)) != NULL) + while ((copy = nfsd4_unhash_copy(clp)) != NULL) nfsd4_stop_copy(copy); } #ifdef CONFIG_NFSD_V4_2_INTER_SSC @@ -1598,8 +1669,10 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb) { struct nfsd4_cb_offload *cbo = container_of(cb, struct nfsd4_cb_offload, co_cb); + struct nfsd4_copy *copy = + container_of(cbo, struct nfsd4_copy, cp_cb_offload); - kfree(cbo); + set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags); } static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, @@ -1609,6 +1682,13 @@ static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, container_of(cb, struct nfsd4_cb_offload, co_cb); trace_nfsd_cb_offload_done(&cbo->co_res.cb_stateid, task); + switch (task->tk_status) { + case -NFS4ERR_DELAY: + if (cbo->co_retries--) { + rpc_delay(task, 1 * HZ); + return 0; + } + } return 1; } @@ -1732,15 +1812,12 @@ static void cleanup_async_copy(struct nfsd4_copy *copy) static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) { - struct nfsd4_cb_offload *cbo; - - cbo = kzalloc(sizeof(*cbo), GFP_KERNEL); - if (!cbo) - return; + struct nfsd4_cb_offload *cbo = ©->cp_cb_offload; memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res)); memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh)); cbo->co_nfserr = copy->nfserr; + cbo->co_retries = 5; nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD); @@ -1786,10 +1863,13 @@ static int nfsd4_do_async_copy(void *data) } do_callback: + /* The kthread exits forthwith. Ensure that a subsequent + * OFFLOAD_CANCEL won't try to kill it again. */ + set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags); + set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); trace_nfsd_copy_async_done(copy); nfsd4_send_cb_offload(copy); - cleanup_async_copy(copy); return 0; } @@ -1843,6 +1923,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, async_copy->cp_nn = nn; INIT_LIST_HEAD(&async_copy->copies); refcount_set(&async_copy->refcount, 1); + async_copy->cp_ttl = NFSD_COPY_INITIAL_TTL; /* Arbitrary cap on number of pending async copy operations */ if (atomic_inc_return(&nn->pending_async_copies) > (int)rqstp->rq_pool->sp_nrthreads) @@ -2780,6 +2861,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) if (op->opdesc->op_get_currentstateid) op->opdesc->op_get_currentstateid(cstate, &op->u); op->status = op->opdesc->op_func(rqstp, cstate, &op->u); + trace_nfsd_compound_op_err(rqstp, op->opnum, op->status); /* Only from SEQUENCE */ if (cstate->status == nfserr_replay_cache) { @@ -2796,7 +2878,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) if (current_fh->fh_export && need_wrongsec_check(rqstp)) - op->status = check_nfsd_access(current_fh->fh_export, rqstp); + op->status = check_nfsd_access(current_fh->fh_export, rqstp, false); } encode_op: if (op->status == nfserr_replay_me) { @@ -3452,6 +3534,7 @@ static const struct nfsd4_operation nfsd4_ops[] = { /* NFSv4.1 operations */ [OP_EXCHANGE_ID] = { .op_func = nfsd4_exchange_id, + .op_release = nfsd4_exchange_id_release, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP | OP_MODIFIES_SOMETHING, .op_name = "OP_EXCHANGE_ID", diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index b7d61eb8afe9e..4a765555bf845 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -659,7 +659,8 @@ nfs4_reset_recoverydir(char *recdir) return status; status = -ENOTDIR; if (d_is_dir(path.dentry)) { - strcpy(user_recovery_dirname, recdir); + strscpy(user_recovery_dirname, recdir, + sizeof(user_recovery_dirname)); status = 0; } path_put(&path); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d80406f8b5682..741b9449f727d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -149,14 +149,14 @@ void nfsd4_destroy_laundry_wq(void) static bool is_session_dead(struct nfsd4_session *ses) { - return ses->se_flags & NFS4_SESSION_DEAD; + return ses->se_dead; } static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me) { if (atomic_read(&ses->se_ref) > ref_held_by_me) return nfserr_jukebox; - ses->se_flags |= NFS4_SESSION_DEAD; + ses->se_dead = true; return nfs_ok; } @@ -572,13 +572,6 @@ opaque_hashval(const void *ptr, int nbytes) return x; } -static void nfsd4_free_file_rcu(struct rcu_head *rcu) -{ - struct nfs4_file *fp = container_of(rcu, struct nfs4_file, fi_rcu); - - kmem_cache_free(file_slab, fp); -} - void put_nfs4_file(struct nfs4_file *fi) { @@ -586,7 +579,7 @@ put_nfs4_file(struct nfs4_file *fi) nfsd4_file_hash_remove(fi); WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate)); WARN_ON_ONCE(!list_empty(&fi->fi_delegations)); - call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu); + kfree_rcu(fi, fi_rcu); } } @@ -1184,7 +1177,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp, nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client, &nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR); dp->dl_cb_fattr.ncf_file_modified = false; - dp->dl_cb_fattr.ncf_cb_bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE; get_nfs4_file(fp); dp->dl_stid.sc_file = fp; return dp; @@ -1660,6 +1652,14 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp) free_ol_stateid_reaplist(&reaplist); } +static bool nfs4_openowner_unhashed(struct nfs4_openowner *oo) +{ + lockdep_assert_held(&oo->oo_owner.so_client->cl_lock); + + return list_empty(&oo->oo_owner.so_strhash) && + list_empty(&oo->oo_perclient); +} + static void unhash_openowner_locked(struct nfs4_openowner *oo) { struct nfs4_client *clp = oo->oo_owner.so_client; @@ -2010,8 +2010,10 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs, } memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs)); - memcpy(&new->se_bchannel, battrs, sizeof(struct nfsd4_channel_attrs)); - + new->se_cb_slot_avail = ~0U; + new->se_cb_highest_slot = min(battrs->maxreqs - 1, + NFSD_BC_SLOT_TABLE_SIZE - 1); + spin_lock_init(&new->se_lock); return new; out_free: while (i--) @@ -2142,11 +2144,14 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru INIT_LIST_HEAD(&new->se_conns); - new->se_cb_seq_nr = 1; - new->se_flags = cses->flags; + atomic_set(&new->se_ref, 0); + new->se_dead = false; new->se_cb_prog = cses->callback_prog; new->se_cb_sec = cses->cb_sec; - atomic_set(&new->se_ref, 0); + + for (idx = 0; idx < NFSD_BC_SLOT_TABLE_SIZE; ++idx) + new->se_cb_seq_nr[idx] = 1; + idx = hash_sessionid(&new->se_sessionid); list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); spin_lock(&clp->cl_lock); @@ -2239,21 +2244,16 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn) return 1; } -/* - * XXX Should we use a slab cache ? - * This type of memory management is somewhat inefficient, but we use it - * anyway since SETCLIENTID is not a common operation. - */ static struct nfs4_client *alloc_client(struct xdr_netobj name, struct nfsd_net *nn) { struct nfs4_client *clp; int i; - if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) { + if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients && + atomic_read(&nn->nfsd_courtesy_clients) > 0) mod_delayed_work(laundry_wq, &nn->laundromat_work, 0); - return NULL; - } + clp = kmem_cache_zalloc(client_slab, GFP_KERNEL); if (clp == NULL) return NULL; @@ -3160,7 +3160,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, kref_init(&clp->cl_nfsdfs.cl_ref); nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL); clp->cl_time = ktime_get_boottime_seconds(); - clear_bit(0, &clp->cl_cb_slot_busy); copy_verf(clp, verf); memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage)); clp->cl_cb_session = NULL; @@ -3487,7 +3486,7 @@ static bool client_has_state(struct nfs4_client *clp) #endif || !list_empty(&clp->cl_delegations) || !list_empty(&clp->cl_sessions) - || !list_empty(&clp->async_copies); + || nfsd4_has_active_async_copies(clp); } static __be32 copy_impl_id(struct nfs4_client *clp, @@ -3525,6 +3524,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __func__, rqstp, exid, exid->clname.len, exid->clname.data, addr_str, exid->flags, exid->spa_how); + exid->server_impl_name = kasprintf(GFP_KERNEL, "%s %s %s %s", + utsname()->sysname, utsname()->release, + utsname()->version, utsname()->machine); + if (!exid->server_impl_name) + return nfserr_jukebox; + if (exid->flags & ~EXCHGID4_FLAG_MASK_A) return nfserr_inval; @@ -3662,6 +3667,23 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, exid->seqid = conf->cl_cs_slot.sl_seqid + 1; nfsd4_set_ex_flags(conf, exid); + exid->nii_domain.len = sizeof("kernel.org") - 1; + exid->nii_domain.data = "kernel.org"; + + /* + * Note that RFC 8881 places no length limit on + * nii_name, but this implementation permits no + * more than NFS4_OPAQUE_LIMIT bytes. + */ + exid->nii_name.len = strlen(exid->server_impl_name); + if (exid->nii_name.len > NFS4_OPAQUE_LIMIT) + exid->nii_name.len = NFS4_OPAQUE_LIMIT; + exid->nii_name.data = exid->server_impl_name; + + /* just send zeros - the date is in nii_name */ + exid->nii_time.tv_sec = 0; + exid->nii_time.tv_nsec = 0; + dprintk("nfsd4_exchange_id seqid %d flags %x\n", conf->cl_cs_slot.sl_seqid, conf->cl_exchange_flags); status = nfs_ok; @@ -3678,6 +3700,14 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return status; } +void +nfsd4_exchange_id_release(union nfsd4_op_u *u) +{ + struct nfsd4_exchange_id *exid = &u->exchange_id; + + kfree(exid->server_impl_name); +} + static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse) { /* The slot is in use, and no response has been sent. */ @@ -3911,6 +3941,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, cr_ses->flags &= ~SESSION4_PERSIST; /* Upshifting from TCP to RDMA is not supported */ cr_ses->flags &= ~SESSION4_RDMA; + /* Report the correct number of backchannel slots */ + cr_ses->back_channel.maxreqs = new->se_cb_highest_slot + 1; init_session(rqstp, new, conf, cr_ses); nfsd4_get_session_locked(new); @@ -3931,7 +3963,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, return status; out_expired_error: - old = NULL; /* * Revert the slot seq_nr change so the server will process * the client's resend instead of returning a cached response. @@ -3946,8 +3977,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, out_free_conn: spin_unlock(&nn->client_lock); free_conn(conn); - if (old) - expire_client(old); out_free_session: __free_session(new); out_release_drc_mem: @@ -4975,6 +5004,12 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open) spin_lock(&oo->oo_owner.so_client->cl_lock); spin_lock(&fp->fi_lock); + if (nfs4_openowner_unhashed(oo)) { + mutex_unlock(&stp->st_mutex); + stp = NULL; + goto out_unlock; + } + retstp = nfsd4_find_existing_open(fp, open); if (retstp) goto out_unlock; @@ -5957,7 +5992,7 @@ nfs4_delegation_stat(struct nfs4_delegation *dp, struct svc_fh *currentfh, path.dentry = file_dentry(nf->nf_file); rc = vfs_getattr(&path, stat, - (STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE), + (STATX_MODE | STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE), AT_STATX_SYNC_AS_STAT); nfsd_file_put(nf); @@ -6041,8 +6076,7 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, } open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE; dp->dl_cb_fattr.ncf_cur_fsize = stat.size; - dp->dl_cb_fattr.ncf_initial_cinfo = - nfsd4_change_attribute(&stat, d_inode(currentfh->fh_dentry)); + dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat); trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); } else { open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; @@ -6127,6 +6161,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf if (!stp) { stp = init_open_stateid(fp, open); + if (!stp) { + status = nfserr_jukebox; + goto out; + } + if (!open->op_stp) new_stp = true; } @@ -6562,6 +6601,7 @@ nfs4_laundromat(struct nfsd_net *nn) _free_cpntf_state_locked(nn, cps); } spin_unlock(&nn->s2s_cp_lock); + nfsd4_async_copy_reaper(nn); nfs4_get_client_reaplist(nn, &reaplist, <); nfs4_process_client_reaplist(&reaplist); @@ -7943,11 +7983,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (check_lock_length(lock->lk_offset, lock->lk_length)) return nfserr_inval; - if ((status = fh_verify(rqstp, &cstate->current_fh, - S_IFREG, NFSD_MAY_LOCK))) { - dprintk("NFSD: nfsd4_lock: permission denied!\n"); + status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0); + if (status != nfs_ok) return status; - } sb = cstate->current_fh.fh_dentry->d_sb; if (lock->lk_is_new) { @@ -8854,8 +8892,7 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate, * nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict * @rqstp: RPC transaction context * @dentry: dentry of inode to be checked for a conflict - * @modified: return true if file was modified - * @size: new size of file if modified is true + * @pdp: returned WRITE delegation, if one was found * * This function is called when there is a conflict between a write * delegation and a change/size GETATTR from another client. The server @@ -8865,11 +8902,12 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate, * 18.7.4. * * Returns 0 if there is no conflict; otherwise an nfs_stat - * code is returned. + * code is returned. If @pdp is set to a non-NULL value, then the + * caller must put the reference. */ __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, - bool *modified, u64 *size) + struct nfs4_delegation **pdp) { __be32 status; struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); @@ -8880,10 +8918,9 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_cb_fattr *ncf; struct inode *inode = d_inode(dentry); - *modified = false; ctx = locks_inode_context(inode); if (!ctx) - return 0; + return nfs_ok; #define NON_NFSD_LEASE ((void *)1) @@ -8949,10 +8986,10 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, goto out_status; } ncf->ncf_cur_fsize = ncf->ncf_cb_fsize; - *size = ncf->ncf_cur_fsize; - *modified = true; + *pdp = dp; + return nfs_ok; } - status = 0; + status = nfs_ok; out_status: nfs4_put_stid(&dp->dl_stid); return status; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f118921250c31..53fac037611c0 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2652,13 +2652,10 @@ static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep, strlen = end - str; if (strlen) { - p = xdr_reserve_space(xdr, strlen + 4); - if (!p) + if (xdr_stream_encode_opaque(xdr, str, strlen) < 0) return nfserr_resource; - p = xdr_encode_opaque(p, str, strlen); count++; - } - else + } else end++; if (found_esc) end = next; @@ -2699,7 +2696,6 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr, const struct path *path) { struct path cur = *path; - __be32 *p; struct dentry **components = NULL; unsigned int ncomponents = 0; __be32 err = nfserr_jukebox; @@ -2730,24 +2726,19 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr, components[ncomponents++] = cur.dentry; cur.dentry = dget_parent(cur.dentry); } + err = nfserr_resource; - p = xdr_reserve_space(xdr, 4); - if (!p) + if (xdr_stream_encode_u32(xdr, ncomponents) != XDR_UNIT) goto out_free; - *p++ = cpu_to_be32(ncomponents); - while (ncomponents) { struct dentry *dentry = components[ncomponents - 1]; - unsigned int len; spin_lock(&dentry->d_lock); - len = dentry->d_name.len; - p = xdr_reserve_space(xdr, len + 4); - if (!p) { + if (xdr_stream_encode_opaque(xdr, dentry->d_name.name, + dentry->d_name.len) < 0) { spin_unlock(&dentry->d_lock); goto out_free; } - p = xdr_encode_opaque(p, dentry->d_name.name, len); dprintk("/%pd", dentry); spin_unlock(&dentry->d_lock); dput(dentry); @@ -2928,7 +2919,6 @@ struct nfsd4_fattr_args { struct kstat stat; struct kstatfs statfs; struct nfs4_acl *acl; - u64 size; #ifdef CONFIG_NFSD_V4_SECURITY_LABEL void *context; int contextlen; @@ -3040,14 +3030,14 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr, return nfs_ok; } - c = nfsd4_change_attribute(&args->stat, d_inode(args->dentry)); + c = nfsd4_change_attribute(&args->stat); return nfsd4_encode_changeid4(xdr, c); } static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr, const struct nfsd4_fattr_args *args) { - return nfsd4_encode_uint64_t(xdr, args->size); + return nfsd4_encode_uint64_t(xdr, args->stat.size); } static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr, @@ -3512,6 +3502,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, int ignore_crossmnt) { DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)); + struct nfs4_delegation *dp = NULL; struct nfsd4_fattr_args args; struct svc_fh *tempfh = NULL; int starting_len = xdr->buf->len; @@ -3526,8 +3517,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, .dentry = dentry, }; unsigned long bit; - bool file_modified = false; - u64 size = 0; WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1); WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval)); @@ -3555,10 +3544,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, if (status) goto out; } - args.size = 0; if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) { - status = nfsd4_deleg_getattr_conflict(rqstp, dentry, - &file_modified, &size); + status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp); if (status) goto out; } @@ -3566,12 +3553,16 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, err = vfs_getattr(&path, &args.stat, STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE, AT_STATX_SYNC_AS_STAT); + if (dp) { + struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr; + + if (ncf->ncf_file_modified) + args.stat.size = ncf->ncf_cur_fsize; + + nfs4_put_stid(&dp->dl_stid); + } if (err) goto out_nfserr; - if (file_modified) - args.size = size; - else - args.size = args.stat.size; if (!(args.stat.result_mask & STATX_BTIME)) /* underlying FS does not offer btime so we can't share it */ @@ -3767,7 +3758,7 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name, nfserr = nfserrno(err); goto out_put; } - nfserr = check_nfsd_access(exp, cd->rd_rqstp); + nfserr = check_nfsd_access(exp, cd->rd_rqstp, false); if (nfserr) goto out_put; @@ -4825,6 +4816,25 @@ nfsd4_encode_server_owner4(struct xdr_stream *xdr, struct svc_rqst *rqstp) return nfsd4_encode_opaque(xdr, nn->nfsd_name, strlen(nn->nfsd_name)); } +static __be32 +nfsd4_encode_nfs_impl_id4(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid) +{ + __be32 status; + + /* nii_domain */ + status = nfsd4_encode_opaque(xdr, exid->nii_domain.data, + exid->nii_domain.len); + if (status != nfs_ok) + return status; + /* nii_name */ + status = nfsd4_encode_opaque(xdr, exid->nii_name.data, + exid->nii_name.len); + if (status != nfs_ok) + return status; + /* nii_time */ + return nfsd4_encode_nfstime4(xdr, &exid->nii_time); +} + static __be32 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, union nfsd4_op_u *u) @@ -4859,8 +4869,11 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, if (nfserr != nfs_ok) return nfserr; /* eir_server_impl_id<1> */ - if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) + if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT) return nfserr_resource; + nfserr = nfsd4_encode_nfs_impl_id4(xdr, exid); + if (nfserr != nfs_ok) + return nfserr; return nfs_ok; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 40ad58a6a0361..6a831cb242dfb 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -320,6 +320,7 @@ __fh_verify(struct svc_rqst *rqstp, { struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct svc_export *exp = NULL; + bool may_bypass_gss = false; struct dentry *dentry; __be32 error; @@ -362,13 +363,12 @@ __fh_verify(struct svc_rqst *rqstp, if (error) goto out; - /* - * pseudoflavor restrictions are not enforced on NLM, - * which clients virtually always use auth_sys for, - * even while using RPCSEC_GSS for NFS. - */ - if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS) - goto skip_pseudoflavor_check; + if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM)) + /* NLM is allowed to fully bypass authentication */ + goto out; + + if (access & NFSD_MAY_BYPASS_GSS) + may_bypass_gss = true; /* * Clients may expect to be able to use auth_sys during mount, * even if they use gss for everything else; see section 2.3.2 @@ -376,13 +376,12 @@ __fh_verify(struct svc_rqst *rqstp, */ if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT && exp->ex_path.dentry == dentry) - goto skip_pseudoflavor_check; + may_bypass_gss = true; - error = check_nfsd_access(exp, rqstp); + error = check_nfsd_access(exp, rqstp, may_bypass_gss); if (error) goto out; -skip_pseudoflavor_check: /* Finally, check access permissions. */ error = nfsd_permission(cred, exp, dentry, access); out: @@ -667,20 +666,18 @@ fh_update(struct svc_fh *fhp) __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp) { bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); - struct inode *inode; struct kstat stat; __be32 err; if (fhp->fh_no_wcc || fhp->fh_pre_saved) return nfs_ok; - inode = d_inode(fhp->fh_dentry); err = fh_getattr(fhp, &stat); if (err) return err; if (v4) - fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode); + fhp->fh_pre_change = nfsd4_change_attribute(&stat); fhp->fh_pre_mtime = stat.mtime; fhp->fh_pre_ctime = stat.ctime; @@ -697,7 +694,6 @@ __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp) __be32 fh_fill_post_attrs(struct svc_fh *fhp) { bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); - struct inode *inode = d_inode(fhp->fh_dentry); __be32 err; if (fhp->fh_no_wcc) @@ -713,7 +709,7 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp) fhp->fh_post_saved = true; if (v4) fhp->fh_post_change = - nfsd4_change_attribute(&fhp->fh_post_attr, inode); + nfsd4_change_attribute(&fhp->fh_post_attr); return nfs_ok; } @@ -770,7 +766,7 @@ char * SVCFH_fmt(struct svc_fh *fhp) struct knfsd_fh *fh = &fhp->fh_handle; static char buf[2+1+1+64*3+1]; - if (fh->fh_size < 0 || fh->fh_size> 64) + if (fh->fh_size > 64) return "bad-fh"; sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw); return buf; @@ -804,7 +800,14 @@ enum fsid_source fsid_source(const struct svc_fh *fhp) return FSIDSOURCE_DEV; } -/* +/** + * nfsd4_change_attribute - Generate an NFSv4 change_attribute value + * @stat: inode attributes + * + * Caller must fill in @stat before calling, typically by invoking + * vfs_getattr() with STATX_MODE, STATX_CTIME, and STATX_CHANGE_COOKIE. + * Returns an unsigned 64-bit changeid4 value (RFC 8881 Section 3.2). + * * We could use i_version alone as the change attribute. However, i_version * can go backwards on a regular file after an unclean shutdown. On its own * that doesn't necessarily cause a problem, but if i_version goes backwards @@ -821,13 +824,13 @@ enum fsid_source fsid_source(const struct svc_fh *fhp) * assume that the new change attr is always logged to stable storage in some * fashion before the results can be seen. */ -u64 nfsd4_change_attribute(const struct kstat *stat, const struct inode *inode) +u64 nfsd4_change_attribute(const struct kstat *stat) { u64 chattr; if (stat->result_mask & STATX_CHANGE_COOKIE) { chattr = stat->change_cookie; - if (S_ISREG(inode->i_mode) && + if (S_ISREG(stat->mode) && !(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) { chattr += (u64)stat->ctime.tv_sec << 30; chattr += stat->ctime.tv_nsec; diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h index 5b7394801dc42..876152a91f122 100644 --- a/fs/nfsd/nfsfh.h +++ b/fs/nfsd/nfsfh.h @@ -297,8 +297,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp) fhp->fh_pre_saved = false; } -u64 nfsd4_change_attribute(const struct kstat *stat, - const struct inode *inode); +u64 nfsd4_change_attribute(const struct kstat *stat); __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp); __be32 fh_fill_post_attrs(struct svc_fh *fhp); __be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 35b3564c065fa..e16bb3717fb9b 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -71,8 +71,8 @@ struct nfsd4_callback { struct work_struct cb_work; int cb_seq_status; int cb_status; + int cb_held_slot; bool cb_need_restart; - bool cb_holds_slot; }; struct nfsd4_callback_ops { @@ -137,10 +137,24 @@ struct nfs4_cpntf_state { time64_t cpntf_time; /* last time stateid used */ }; +/* + * RFC 7862 Section 4.8 states: + * + * | A copy offload stateid will be valid until either (A) the client + * | or server restarts or (B) the client returns the resource by + * | issuing an OFFLOAD_CANCEL operation or the client replies to a + * | CB_OFFLOAD operation. + * + * Because a client might not reply to a CB_OFFLOAD, or a reply + * might get lost due to connection loss, NFSD purges async copy + * state after a short period to prevent it from accumulating + * over time. + */ +#define NFSD_COPY_INITIAL_TTL 10 + struct nfs4_cb_fattr { struct nfsd4_callback ncf_getattr; u32 ncf_cb_status; - u32 ncf_cb_bmap[1]; /* from CB_GETATTR reply */ u64 ncf_cb_change; @@ -290,6 +304,9 @@ struct nfsd4_conn { unsigned char cn_flags; }; +/* Maximum number of slots that nfsd will use in the backchannel */ +#define NFSD_BC_SLOT_TABLE_SIZE (sizeof(u32) * 8) + /* * Representation of a v4.1+ session. These are refcounted in a similar fashion * to the nfs4_client. References are only taken when the server is actively @@ -297,19 +314,19 @@ struct nfsd4_conn { */ struct nfsd4_session { atomic_t se_ref; + spinlock_t se_lock; + u32 se_cb_slot_avail; /* bitmap of available slots */ + u32 se_cb_highest_slot; /* highest slot client wants */ + u32 se_cb_prog; + bool se_dead; struct list_head se_hash; /* hash by sessionid */ struct list_head se_perclnt; -/* See SESSION4_PERSIST, etc. for standard flags; this is internal-only: */ -#define NFS4_SESSION_DEAD 0x010 - u32 se_flags; struct nfs4_client *se_client; struct nfs4_sessionid se_sessionid; struct nfsd4_channel_attrs se_fchannel; - struct nfsd4_channel_attrs se_bchannel; struct nfsd4_cb_sec se_cb_sec; struct list_head se_conns; - u32 se_cb_prog; - u32 se_cb_seq_nr; + u32 se_cb_seq_nr[NFSD_BC_SLOT_TABLE_SIZE]; struct nfsd4_slot *se_slots[]; /* forward channel slots */ }; @@ -443,9 +460,6 @@ struct nfs4_client { */ struct dentry *cl_nfsd_info_dentry; - /* for nfs41 callbacks */ - /* We currently support a single back channel with a single slot */ - unsigned long cl_cb_slot_busy; struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ /* wait here for slots */ struct net *net; @@ -742,6 +756,8 @@ extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, extern bool nfsd4_run_cb(struct nfsd4_callback *cb); extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfsd4_shutdown_copy(struct nfs4_client *clp); +void nfsd4_async_copy_reaper(struct nfsd_net *nn); +bool nfsd4_has_active_async_copies(struct nfs4_client *clp); extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, struct nfsd_net *nn); extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); @@ -784,5 +800,5 @@ static inline bool try_to_expire_client(struct nfs4_client *clp) } extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, - struct dentry *dentry, bool *file_modified, u64 *size); + struct dentry *dentry, struct nfs4_delegation **pdp); #endif /* NFSD4_STATE_H */ diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index b8470d4cbe99e..696c89f68a9e6 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -79,7 +79,7 @@ DEFINE_NFSD_XDR_ERR_EVENT(cant_encode); { NFSD_MAY_READ, "READ" }, \ { NFSD_MAY_SATTR, "SATTR" }, \ { NFSD_MAY_TRUNC, "TRUNC" }, \ - { NFSD_MAY_LOCK, "LOCK" }, \ + { NFSD_MAY_NLM, "NLM" }, \ { NFSD_MAY_OWNER_OVERRIDE, "OWNER_OVERRIDE" }, \ { NFSD_MAY_LOCAL_ACCESS, "LOCAL_ACCESS" }, \ { NFSD_MAY_BYPASS_GSS_ON_ROOT, "BYPASS_GSS_ON_ROOT" }, \ @@ -163,7 +163,7 @@ TRACE_EVENT(nfsd_compound_decode_err, __entry->opnum, __entry->status) ); -TRACE_EVENT(nfsd_compound_encode_err, +DECLARE_EVENT_CLASS(nfsd_compound_err_class, TP_PROTO( const struct svc_rqst *rqstp, u32 opnum, @@ -184,6 +184,18 @@ TRACE_EVENT(nfsd_compound_encode_err, __entry->opnum, __entry->status) ); +#define DEFINE_NFSD_COMPOUND_ERR_EVENT(name) \ +DEFINE_EVENT(nfsd_compound_err_class, nfsd_compound_##name##_err, \ + TP_PROTO( \ + const struct svc_rqst *rqstp, \ + u32 opnum, \ + __be32 status \ + ), \ + TP_ARGS(rqstp, opnum, status)) + +DEFINE_NFSD_COMPOUND_ERR_EVENT(op); +DEFINE_NFSD_COMPOUND_ERR_EVENT(encode); + #define show_fs_file_type(x) \ __print_symbolic(x, \ { S_IFLNK, "LNK" }, \ @@ -1685,7 +1697,7 @@ TRACE_EVENT(nfsd_cb_free_slot, __entry->cl_id = sid->clientid.cl_id; __entry->seqno = sid->sequence; __entry->reserved = sid->reserved; - __entry->slot_seqno = session->se_cb_seq_nr; + __entry->slot_seqno = session->se_cb_seq_nr[cb->cb_held_slot]; ), TP_printk(SUNRPC_TRACE_TASK_SPECIFIER " sessionid=%08x:%08x:%08x:%08x new slot seqno=%u", @@ -2232,7 +2244,7 @@ TRACE_EVENT(nfsd_copy_done, ) ); -TRACE_EVENT(nfsd_copy_async_done, +DECLARE_EVENT_CLASS(nfsd_copy_async_done_class, TP_PROTO( const struct nfsd4_copy *copy ), @@ -2301,6 +2313,15 @@ TRACE_EVENT(nfsd_copy_async_done, ) ); +#define DEFINE_COPY_ASYNC_DONE_EVENT(name) \ +DEFINE_EVENT(nfsd_copy_async_done_class, \ + nfsd_copy_async_##name, \ + TP_PROTO(const struct nfsd4_copy *copy), \ + TP_ARGS(copy)) + +DEFINE_COPY_ASYNC_DONE_EVENT(done); +DEFINE_COPY_ASYNC_DONE_EVENT(cancel); + #endif /* _NFSD_TRACE_H */ #undef TRACE_INCLUDE_PATH diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index d6d4f2a0e8982..29cb7b812d713 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -35,7 +35,6 @@ #include "xdr3.h" #ifdef CONFIG_NFSD_V4 -#include "../internal.h" #include "acl.h" #include "idmap.h" #include "xdr4.h" @@ -321,7 +320,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry); if (err) return err; - err = check_nfsd_access(exp, rqstp); + err = check_nfsd_access(exp, rqstp, false); if (err) goto out; /* @@ -861,8 +860,7 @@ int nfsd_open_break_lease(struct inode *inode, int access) * N.B. After this call fhp needs an fh_put */ static int -__nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, - int may_flags, struct file **filp) +__nfsd_open(struct svc_fh *fhp, umode_t type, int may_flags, struct file **filp) { struct path path; struct inode *inode; @@ -932,7 +930,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, retry: err = fh_verify(rqstp, fhp, type, may_flags); if (!err) { - host_err = __nfsd_open(rqstp, fhp, type, may_flags, filp); + host_err = __nfsd_open(fhp, type, may_flags, filp); if (host_err == -EOPENSTALE && !retried) { retried = true; fh_put(fhp); @@ -945,7 +943,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, /** * nfsd_open_verified - Open a regular file for the filecache - * @rqstp: RPC request * @fhp: NFS filehandle of the file to open * @may_flags: internal permission flags * @filp: OUT: open "struct file *" @@ -953,10 +950,9 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, * Returns zero on success, or a negative errno value. */ int -nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, int may_flags, - struct file **filp) +nfsd_open_verified(struct svc_fh *fhp, int may_flags, struct file **filp) { - return __nfsd_open(rqstp, fhp, S_IFREG, may_flags, filp); + return __nfsd_open(fhp, S_IFREG, may_flags, filp); } /* @@ -2509,7 +2505,7 @@ nfsd_permission(struct svc_cred *cred, struct svc_export *exp, (acc & NFSD_MAY_EXEC)? " exec" : "", (acc & NFSD_MAY_SATTR)? " sattr" : "", (acc & NFSD_MAY_TRUNC)? " trunc" : "", - (acc & NFSD_MAY_LOCK)? " lock" : "", + (acc & NFSD_MAY_NLM)? " nlm" : "", (acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "", inode->i_mode, IS_IMMUTABLE(inode)? " immut" : "", @@ -2534,16 +2530,6 @@ nfsd_permission(struct svc_cred *cred, struct svc_export *exp, if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode)) return nfserr_perm; - if (acc & NFSD_MAY_LOCK) { - /* If we cannot rely on authentication in NLM requests, - * just allow locks, otherwise require read permission, or - * ownership - */ - if (exp->ex_flags & NFSEXP_NOAUTHNLM) - return 0; - else - acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE; - } /* * The file owner always gets access permission for accesses that * would normally be checked at open time. This is to make diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 3ff1465225569..f9b09b8428566 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -20,7 +20,7 @@ #define NFSD_MAY_READ 0x004 /* == MAY_READ */ #define NFSD_MAY_SATTR 0x008 #define NFSD_MAY_TRUNC 0x010 -#define NFSD_MAY_LOCK 0x020 +#define NFSD_MAY_NLM 0x020 /* request is from lockd */ #define NFSD_MAY_MASK 0x03f /* extra hints to permission and open routines: */ @@ -114,8 +114,8 @@ __be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, int nfsd_open_break_lease(struct inode *, int); __be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t, int, struct file **); -int nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, - int may_flags, struct file **filp); +int nfsd_open_verified(struct svc_fh *fhp, int may_flags, + struct file **filp); __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, loff_t offset, unsigned long *count, diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 2a21a7662e030..382cc13893965 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -567,6 +567,7 @@ struct nfsd4_exchange_id { struct xdr_netobj nii_domain; struct xdr_netobj nii_name; struct timespec64 nii_time; + char *server_impl_name; }; struct nfsd4_sequence { @@ -675,6 +676,7 @@ struct nfsd4_cb_offload { struct nfsd4_callback co_cb; struct nfsd42_write_res co_res; __be32 co_nfserr; + unsigned int co_retries; struct knfsd_fh co_fh; }; @@ -693,12 +695,16 @@ struct nfsd4_copy { #define NFSD4_COPY_F_SYNCHRONOUS (2) #define NFSD4_COPY_F_COMMITTED (3) #define NFSD4_COPY_F_COMPLETED (4) +#define NFSD4_COPY_F_OFFLOAD_DONE (5) /* response */ __be32 nfserr; struct nfsd42_write_res cp_res; struct knfsd_fh fh; + /* offload callback */ + struct nfsd4_cb_offload cp_cb_offload; + struct nfs4_client *cp_clp; struct nfsd_file *nf_src; @@ -709,6 +715,7 @@ struct nfsd4_copy { struct list_head copies; struct task_struct *copy_task; refcount_t refcount; + unsigned int cp_ttl; struct nfsd4_ssc_umount_item *ss_nsui; struct nfs_fh c_fh; @@ -930,6 +937,7 @@ extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *, union nfsd4_op_u *u); extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *, union nfsd4_op_u *u); +void nfsd4_exchange_id_release(union nfsd4_op_u *u); extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *, union nfsd4_op_u *u); extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, diff --git a/fs/nilfs2/alloc.c b/fs/nilfs2/alloc.c index ba50388ee4bf1..ba3e1f591f369 100644 --- a/fs/nilfs2/alloc.c +++ b/fs/nilfs2/alloc.c @@ -177,12 +177,14 @@ nilfs_palloc_entry_blkoff(const struct inode *inode, __u64 nr) * nilfs_palloc_desc_block_init - initialize buffer of a group descriptor block * @inode: inode of metadata file * @bh: buffer head of the buffer to be initialized - * @kaddr: kernel address mapped for the page including the buffer + * @from: kernel address mapped for a chunk of the block + * + * This function does not yet support the case where block size > PAGE_SIZE. */ static void nilfs_palloc_desc_block_init(struct inode *inode, - struct buffer_head *bh, void *kaddr) + struct buffer_head *bh, void *from) { - struct nilfs_palloc_group_desc *desc = kaddr + bh_offset(bh); + struct nilfs_palloc_group_desc *desc = from; unsigned long n = nilfs_palloc_groups_per_desc_block(inode); __le32 nfrees; @@ -337,38 +339,55 @@ static int nilfs_palloc_delete_entry_block(struct inode *inode, __u64 nr) } /** - * nilfs_palloc_block_get_group_desc - get kernel address of a group descriptor + * nilfs_palloc_group_desc_offset - calculate the byte offset of a group + * descriptor in the folio containing it * @inode: inode of metadata file using this allocator * @group: group number - * @bh: buffer head of the buffer storing the group descriptor block - * @kaddr: kernel address mapped for the page including the buffer + * @bh: buffer head of the group descriptor block + * + * Return: Byte offset in the folio of the group descriptor for @group. */ -static struct nilfs_palloc_group_desc * -nilfs_palloc_block_get_group_desc(const struct inode *inode, - unsigned long group, - const struct buffer_head *bh, void *kaddr) +static size_t nilfs_palloc_group_desc_offset(const struct inode *inode, + unsigned long group, + const struct buffer_head *bh) { - return (struct nilfs_palloc_group_desc *)(kaddr + bh_offset(bh)) + - group % nilfs_palloc_groups_per_desc_block(inode); + return offset_in_folio(bh->b_folio, bh->b_data) + + sizeof(struct nilfs_palloc_group_desc) * + (group % nilfs_palloc_groups_per_desc_block(inode)); +} + +/** + * nilfs_palloc_bitmap_offset - calculate the byte offset of a bitmap block + * in the folio containing it + * @bh: buffer head of the bitmap block + * + * Return: Byte offset in the folio of the bitmap block for @bh. + */ +static size_t nilfs_palloc_bitmap_offset(const struct buffer_head *bh) +{ + return offset_in_folio(bh->b_folio, bh->b_data); } /** - * nilfs_palloc_block_get_entry - get kernel address of an entry + * nilfs_palloc_entry_offset - calculate the byte offset of an entry in the + * folio containing it * @inode: inode of metadata file using this allocator - * @nr: serial number of the entry (e.g. inode number) - * @bh: buffer head of the buffer storing the entry block - * @kaddr: kernel address mapped for the page including the buffer + * @nr: serial number of the entry (e.g. inode number) + * @bh: buffer head of the entry block + * + * Return: Byte offset in the folio of the entry @nr. */ -void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr, - const struct buffer_head *bh, void *kaddr) +size_t nilfs_palloc_entry_offset(const struct inode *inode, __u64 nr, + const struct buffer_head *bh) { - unsigned long entry_offset, group_offset; + unsigned long entry_index_in_group, entry_index_in_block; - nilfs_palloc_group(inode, nr, &group_offset); - entry_offset = group_offset % NILFS_MDT(inode)->mi_entries_per_block; + nilfs_palloc_group(inode, nr, &entry_index_in_group); + entry_index_in_block = entry_index_in_group % + NILFS_MDT(inode)->mi_entries_per_block; - return kaddr + bh_offset(bh) + - entry_offset * NILFS_MDT(inode)->mi_entry_size; + return offset_in_folio(bh->b_folio, bh->b_data) + + entry_index_in_block * NILFS_MDT(inode)->mi_entry_size; } /** @@ -506,7 +525,7 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, struct buffer_head *desc_bh, *bitmap_bh; struct nilfs_palloc_group_desc *desc; unsigned char *bitmap; - void *desc_kaddr, *bitmap_kaddr; + size_t doff, boff; unsigned long group, maxgroup, ngroups; unsigned long group_offset, maxgroup_offset; unsigned long n, entries_per_group; @@ -529,17 +548,17 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, ret = nilfs_palloc_get_desc_block(inode, group, 1, &desc_bh); if (ret < 0) return ret; - desc_kaddr = kmap_local_page(desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc( - inode, group, desc_bh, desc_kaddr); + + doff = nilfs_palloc_group_desc_offset(inode, group, desc_bh); + desc = kmap_local_folio(desc_bh->b_folio, doff); n = nilfs_palloc_rest_groups_in_desc_block(inode, group, maxgroup); - for (j = 0; j < n; j++, desc++, group++, group_offset = 0) { + for (j = 0; j < n; j++, group++, group_offset = 0) { lock = nilfs_mdt_bgl_lock(inode, group); - if (nilfs_palloc_group_desc_nfrees(desc, lock) == 0) + if (nilfs_palloc_group_desc_nfrees(&desc[j], lock) == 0) continue; - kunmap_local(desc_kaddr); + kunmap_local(desc); ret = nilfs_palloc_get_bitmap_block(inode, group, 1, &bitmap_bh); if (unlikely(ret < 0)) { @@ -547,12 +566,14 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, return ret; } - desc_kaddr = kmap_local_page(desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc( - inode, group, desc_bh, desc_kaddr); + /* + * Re-kmap the folio containing the first (and + * subsequent) group descriptors. + */ + desc = kmap_local_folio(desc_bh->b_folio, doff); - bitmap_kaddr = kmap_local_page(bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(bitmap_bh); + boff = nilfs_palloc_bitmap_offset(bitmap_bh); + bitmap = kmap_local_folio(bitmap_bh->b_folio, boff); pos = nilfs_palloc_find_available_slot( bitmap, group_offset, entries_per_group, lock, wrap); @@ -562,14 +583,14 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, * beginning, the wrap flag only has an effect on the * first search. */ - kunmap_local(bitmap_kaddr); + kunmap_local(bitmap); if (pos >= 0) goto found; brelse(bitmap_bh); } - kunmap_local(desc_kaddr); + kunmap_local(desc); brelse(desc_bh); } @@ -578,9 +599,9 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, found: /* found a free entry */ - nilfs_palloc_group_desc_add_entries(desc, lock, -1); + nilfs_palloc_group_desc_add_entries(&desc[j], lock, -1); req->pr_entry_nr = entries_per_group * group + pos; - kunmap_local(desc_kaddr); + kunmap_local(desc); req->pr_desc_bh = desc_bh; req->pr_bitmap_bh = bitmap_bh; @@ -611,18 +632,18 @@ void nilfs_palloc_commit_alloc_entry(struct inode *inode, void nilfs_palloc_commit_free_entry(struct inode *inode, struct nilfs_palloc_req *req) { - struct nilfs_palloc_group_desc *desc; unsigned long group, group_offset; + size_t doff, boff; + struct nilfs_palloc_group_desc *desc; unsigned char *bitmap; - void *desc_kaddr, *bitmap_kaddr; spinlock_t *lock; group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset); - desc_kaddr = kmap_local_page(req->pr_desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc(inode, group, - req->pr_desc_bh, desc_kaddr); - bitmap_kaddr = kmap_local_page(req->pr_bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(req->pr_bitmap_bh); + doff = nilfs_palloc_group_desc_offset(inode, group, req->pr_desc_bh); + desc = kmap_local_folio(req->pr_desc_bh->b_folio, doff); + + boff = nilfs_palloc_bitmap_offset(req->pr_bitmap_bh); + bitmap = kmap_local_folio(req->pr_bitmap_bh->b_folio, boff); lock = nilfs_mdt_bgl_lock(inode, group); if (!nilfs_clear_bit_atomic(lock, group_offset, bitmap)) @@ -633,8 +654,8 @@ void nilfs_palloc_commit_free_entry(struct inode *inode, else nilfs_palloc_group_desc_add_entries(desc, lock, 1); - kunmap_local(bitmap_kaddr); - kunmap_local(desc_kaddr); + kunmap_local(bitmap); + kunmap_local(desc); mark_buffer_dirty(req->pr_desc_bh); mark_buffer_dirty(req->pr_bitmap_bh); @@ -653,17 +674,17 @@ void nilfs_palloc_abort_alloc_entry(struct inode *inode, struct nilfs_palloc_req *req) { struct nilfs_palloc_group_desc *desc; - void *desc_kaddr, *bitmap_kaddr; + size_t doff, boff; unsigned char *bitmap; unsigned long group, group_offset; spinlock_t *lock; group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset); - desc_kaddr = kmap_local_page(req->pr_desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc(inode, group, - req->pr_desc_bh, desc_kaddr); - bitmap_kaddr = kmap_local_page(req->pr_bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(req->pr_bitmap_bh); + doff = nilfs_palloc_group_desc_offset(inode, group, req->pr_desc_bh); + desc = kmap_local_folio(req->pr_desc_bh->b_folio, doff); + + boff = nilfs_palloc_bitmap_offset(req->pr_bitmap_bh); + bitmap = kmap_local_folio(req->pr_bitmap_bh->b_folio, boff); lock = nilfs_mdt_bgl_lock(inode, group); if (!nilfs_clear_bit_atomic(lock, group_offset, bitmap)) @@ -674,8 +695,8 @@ void nilfs_palloc_abort_alloc_entry(struct inode *inode, else nilfs_palloc_group_desc_add_entries(desc, lock, 1); - kunmap_local(bitmap_kaddr); - kunmap_local(desc_kaddr); + kunmap_local(bitmap); + kunmap_local(desc); brelse(req->pr_bitmap_bh); brelse(req->pr_desc_bh); @@ -739,7 +760,7 @@ int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems) struct buffer_head *desc_bh, *bitmap_bh; struct nilfs_palloc_group_desc *desc; unsigned char *bitmap; - void *desc_kaddr, *bitmap_kaddr; + size_t doff, boff; unsigned long group, group_offset; __u64 group_min_nr, last_nrs[8]; const unsigned long epg = nilfs_palloc_entries_per_group(inode); @@ -767,8 +788,8 @@ int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems) /* Get the first entry number of the group */ group_min_nr = (__u64)group * epg; - bitmap_kaddr = kmap_local_page(bitmap_bh->b_page); - bitmap = bitmap_kaddr + bh_offset(bitmap_bh); + boff = nilfs_palloc_bitmap_offset(bitmap_bh); + bitmap = kmap_local_folio(bitmap_bh->b_folio, boff); lock = nilfs_mdt_bgl_lock(inode, group); j = i; @@ -813,7 +834,7 @@ int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems) entry_start = rounddown(group_offset, epb); } while (true); - kunmap_local(bitmap_kaddr); + kunmap_local(bitmap); mark_buffer_dirty(bitmap_bh); brelse(bitmap_bh); @@ -827,11 +848,10 @@ int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems) inode->i_ino); } - desc_kaddr = kmap_local_page(desc_bh->b_page); - desc = nilfs_palloc_block_get_group_desc( - inode, group, desc_bh, desc_kaddr); + doff = nilfs_palloc_group_desc_offset(inode, group, desc_bh); + desc = kmap_local_folio(desc_bh->b_folio, doff); nfree = nilfs_palloc_group_desc_add_entries(desc, lock, n); - kunmap_local(desc_kaddr); + kunmap_local(desc); mark_buffer_dirty(desc_bh); nilfs_mdt_mark_dirty(inode); brelse(desc_bh); diff --git a/fs/nilfs2/alloc.h b/fs/nilfs2/alloc.h index e19d7eb10084b..3f115ab7e9a76 100644 --- a/fs/nilfs2/alloc.h +++ b/fs/nilfs2/alloc.h @@ -31,8 +31,8 @@ nilfs_palloc_entries_per_group(const struct inode *inode) int nilfs_palloc_init_blockgroup(struct inode *, unsigned int); int nilfs_palloc_get_entry_block(struct inode *, __u64, int, struct buffer_head **); -void *nilfs_palloc_block_get_entry(const struct inode *, __u64, - const struct buffer_head *, void *); +size_t nilfs_palloc_entry_offset(const struct inode *inode, __u64 nr, + const struct buffer_head *bh); int nilfs_palloc_count_max_entries(struct inode *, u64, u64 *); diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c index f0ce37552446c..c20207d7a9890 100644 --- a/fs/nilfs2/cpfile.c +++ b/fs/nilfs2/cpfile.c @@ -68,54 +68,41 @@ static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile, static unsigned int nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile, struct buffer_head *bh, - void *kaddr, unsigned int n) { - struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); + struct nilfs_checkpoint *cp; unsigned int count; + cp = kmap_local_folio(bh->b_folio, + offset_in_folio(bh->b_folio, bh->b_data)); count = le32_to_cpu(cp->cp_checkpoints_count) + n; cp->cp_checkpoints_count = cpu_to_le32(count); + kunmap_local(cp); return count; } static unsigned int nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile, struct buffer_head *bh, - void *kaddr, unsigned int n) { - struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); + struct nilfs_checkpoint *cp; unsigned int count; + cp = kmap_local_folio(bh->b_folio, + offset_in_folio(bh->b_folio, bh->b_data)); WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n); count = le32_to_cpu(cp->cp_checkpoints_count) - n; cp->cp_checkpoints_count = cpu_to_le32(count); + kunmap_local(cp); return count; } -static inline struct nilfs_cpfile_header * -nilfs_cpfile_block_get_header(const struct inode *cpfile, - struct buffer_head *bh, - void *kaddr) -{ - return kaddr + bh_offset(bh); -} - -static struct nilfs_checkpoint * -nilfs_cpfile_block_get_checkpoint(const struct inode *cpfile, __u64 cno, - struct buffer_head *bh, - void *kaddr) -{ - return kaddr + bh_offset(bh) + nilfs_cpfile_get_offset(cpfile, cno) * - NILFS_MDT(cpfile)->mi_entry_size; -} - static void nilfs_cpfile_block_init(struct inode *cpfile, struct buffer_head *bh, - void *kaddr) + void *from) { - struct nilfs_checkpoint *cp = kaddr + bh_offset(bh); + struct nilfs_checkpoint *cp = from; size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; int n = nilfs_cpfile_checkpoints_per_block(cpfile); @@ -125,6 +112,54 @@ static void nilfs_cpfile_block_init(struct inode *cpfile, } } +/** + * nilfs_cpfile_checkpoint_offset - calculate the byte offset of a checkpoint + * entry in the folio containing it + * @cpfile: checkpoint file inode + * @cno: checkpoint number + * @bh: buffer head of block containing checkpoint indexed by @cno + * + * Return: Byte offset in the folio of the checkpoint specified by @cno. + */ +static size_t nilfs_cpfile_checkpoint_offset(const struct inode *cpfile, + __u64 cno, + struct buffer_head *bh) +{ + return offset_in_folio(bh->b_folio, bh->b_data) + + nilfs_cpfile_get_offset(cpfile, cno) * + NILFS_MDT(cpfile)->mi_entry_size; +} + +/** + * nilfs_cpfile_cp_snapshot_list_offset - calculate the byte offset of a + * checkpoint snapshot list in the folio + * containing it + * @cpfile: checkpoint file inode + * @cno: checkpoint number + * @bh: buffer head of block containing checkpoint indexed by @cno + * + * Return: Byte offset in the folio of the checkpoint snapshot list specified + * by @cno. + */ +static size_t nilfs_cpfile_cp_snapshot_list_offset(const struct inode *cpfile, + __u64 cno, + struct buffer_head *bh) +{ + return nilfs_cpfile_checkpoint_offset(cpfile, cno, bh) + + offsetof(struct nilfs_checkpoint, cp_snapshot_list); +} + +/** + * nilfs_cpfile_ch_snapshot_list_offset - calculate the byte offset of the + * snapshot list in the header + * + * Return: Byte offset in the folio of the checkpoint snapshot list + */ +static size_t nilfs_cpfile_ch_snapshot_list_offset(void) +{ + return offsetof(struct nilfs_cpfile_header, ch_snapshot_list); +} + static int nilfs_cpfile_get_header_block(struct inode *cpfile, struct buffer_head **bhp) { @@ -214,7 +249,7 @@ int nilfs_cpfile_read_checkpoint(struct inode *cpfile, __u64 cno, { struct buffer_head *cp_bh; struct nilfs_checkpoint *cp; - void *kaddr; + size_t offset; int ret; if (cno < 1 || cno > nilfs_mdt_cno(cpfile)) @@ -228,8 +263,8 @@ int nilfs_cpfile_read_checkpoint(struct inode *cpfile, __u64 cno, goto out_sem; } - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kmap_local_folio(cp_bh->b_folio, offset); if (nilfs_checkpoint_invalid(cp)) { ret = -EINVAL; goto put_cp; @@ -254,7 +289,7 @@ int nilfs_cpfile_read_checkpoint(struct inode *cpfile, __u64 cno, root->ifile = ifile; put_cp: - kunmap_local(kaddr); + kunmap_local(cp); brelse(cp_bh); out_sem: up_read(&NILFS_MDT(cpfile)->mi_sem); @@ -282,7 +317,7 @@ int nilfs_cpfile_create_checkpoint(struct inode *cpfile, __u64 cno) struct buffer_head *header_bh, *cp_bh; struct nilfs_cpfile_header *header; struct nilfs_checkpoint *cp; - void *kaddr; + size_t offset; int ret; if (WARN_ON_ONCE(cno < 1)) @@ -297,24 +332,22 @@ int nilfs_cpfile_create_checkpoint(struct inode *cpfile, __u64 cno) if (unlikely(ret < 0)) goto out_header; - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kmap_local_folio(cp_bh->b_folio, offset); if (nilfs_checkpoint_invalid(cp)) { /* a newly-created checkpoint */ nilfs_checkpoint_clear_invalid(cp); + kunmap_local(cp); if (!nilfs_cpfile_is_in_first(cpfile, cno)) nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh, - kaddr, 1); - kunmap_local(kaddr); + 1); - kaddr = kmap_local_page(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, - kaddr); + header = kmap_local_folio(header_bh->b_folio, 0); le64_add_cpu(&header->ch_ncheckpoints, 1); - kunmap_local(kaddr); + kunmap_local(header); mark_buffer_dirty(header_bh); } else { - kunmap_local(kaddr); + kunmap_local(cp); } /* Force the buffer and the inode to become dirty */ @@ -353,7 +386,7 @@ int nilfs_cpfile_finalize_checkpoint(struct inode *cpfile, __u64 cno, { struct buffer_head *cp_bh; struct nilfs_checkpoint *cp; - void *kaddr; + size_t offset; int ret; if (WARN_ON_ONCE(cno < 1)) @@ -367,10 +400,10 @@ int nilfs_cpfile_finalize_checkpoint(struct inode *cpfile, __u64 cno, goto out_sem; } - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kmap_local_folio(cp_bh->b_folio, offset); if (unlikely(nilfs_checkpoint_invalid(cp))) { - kunmap_local(kaddr); + kunmap_local(cp); brelse(cp_bh); goto error; } @@ -391,7 +424,7 @@ int nilfs_cpfile_finalize_checkpoint(struct inode *cpfile, __u64 cno, nilfs_write_inode_common(root->ifile, &cp->cp_ifile_inode); nilfs_bmap_write(NILFS_I(root->ifile)->i_bmap, &cp->cp_ifile_inode); - kunmap_local(kaddr); + kunmap_local(cp); brelse(cp_bh); out_sem: up_write(&NILFS_MDT(cpfile)->mi_sem); @@ -432,6 +465,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile, struct nilfs_checkpoint *cp; size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; __u64 cno; + size_t offset; void *kaddr; unsigned long tnicps; int ret, ncps, nicps, nss, count, i; @@ -462,9 +496,8 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile, continue; } - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint( - cpfile, cno, cp_bh, kaddr); + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kaddr = kmap_local_folio(cp_bh->b_folio, offset); nicps = 0; for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) { if (nilfs_checkpoint_snapshot(cp)) { @@ -474,43 +507,42 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile, nicps++; } } - if (nicps > 0) { - tnicps += nicps; - mark_buffer_dirty(cp_bh); - nilfs_mdt_mark_dirty(cpfile); - if (!nilfs_cpfile_is_in_first(cpfile, cno)) { - count = - nilfs_cpfile_block_sub_valid_checkpoints( - cpfile, cp_bh, kaddr, nicps); - if (count == 0) { - /* make hole */ - kunmap_local(kaddr); - brelse(cp_bh); - ret = - nilfs_cpfile_delete_checkpoint_block( - cpfile, cno); - if (ret == 0) - continue; - nilfs_err(cpfile->i_sb, - "error %d deleting checkpoint block", - ret); - break; - } - } + kunmap_local(kaddr); + + if (nicps <= 0) { + brelse(cp_bh); + continue; } - kunmap_local(kaddr); + tnicps += nicps; + mark_buffer_dirty(cp_bh); + nilfs_mdt_mark_dirty(cpfile); + if (nilfs_cpfile_is_in_first(cpfile, cno)) { + brelse(cp_bh); + continue; + } + + count = nilfs_cpfile_block_sub_valid_checkpoints(cpfile, cp_bh, + nicps); brelse(cp_bh); + if (count) + continue; + + /* Delete the block if there are no more valid checkpoints */ + ret = nilfs_cpfile_delete_checkpoint_block(cpfile, cno); + if (unlikely(ret)) { + nilfs_err(cpfile->i_sb, + "error %d deleting checkpoint block", ret); + break; + } } if (tnicps > 0) { - kaddr = kmap_local_page(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, - kaddr); + header = kmap_local_folio(header_bh->b_folio, 0); le64_add_cpu(&header->ch_ncheckpoints, -(u64)tnicps); mark_buffer_dirty(header_bh); nilfs_mdt_mark_dirty(cpfile); - kunmap_local(kaddr); + kunmap_local(header); } brelse(header_bh); @@ -544,6 +576,7 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop, struct buffer_head *bh; size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop; + size_t offset; void *kaddr; int n, ret; int ncps, i; @@ -562,8 +595,8 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop, } ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno); - kaddr = kmap_local_page(bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, bh); + cp = kaddr = kmap_local_folio(bh->b_folio, offset); for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) { if (!nilfs_checkpoint_invalid(cp)) { nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, @@ -597,7 +630,7 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, struct nilfs_cpinfo *ci = buf; __u64 curr = *cnop, next; unsigned long curr_blkoff, next_blkoff; - void *kaddr; + size_t offset; int n = 0, ret; down_read(&NILFS_MDT(cpfile)->mi_sem); @@ -606,10 +639,9 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, ret = nilfs_cpfile_get_header_block(cpfile, &bh); if (ret < 0) goto out; - kaddr = kmap_local_page(bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); + header = kmap_local_folio(bh->b_folio, 0); curr = le64_to_cpu(header->ch_snapshot_list.ssl_next); - kunmap_local(kaddr); + kunmap_local(header); brelse(bh); if (curr == 0) { ret = 0; @@ -627,9 +659,9 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, ret = 0; /* No snapshots (started from a hole block) */ goto out; } - kaddr = kmap_local_page(bh->b_page); + offset = nilfs_cpfile_checkpoint_offset(cpfile, curr, bh); + cp = kmap_local_folio(bh->b_folio, offset); while (n < nci) { - cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr); curr = ~(__u64)0; /* Terminator */ if (unlikely(nilfs_checkpoint_invalid(cp) || !nilfs_checkpoint_snapshot(cp))) @@ -641,9 +673,9 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, if (next == 0) break; /* reach end of the snapshot list */ + kunmap_local(cp); next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next); if (curr_blkoff != next_blkoff) { - kunmap_local(kaddr); brelse(bh); ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0, &bh); @@ -651,12 +683,13 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, WARN_ON(ret == -ENOENT); goto out; } - kaddr = kmap_local_page(bh->b_page); } + offset = nilfs_cpfile_checkpoint_offset(cpfile, next, bh); + cp = kmap_local_folio(bh->b_folio, offset); curr = next; curr_blkoff = next_blkoff; } - kunmap_local(kaddr); + kunmap_local(cp); brelse(bh); *cnop = curr; ret = n; @@ -733,26 +766,6 @@ int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno) return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1); } -static struct nilfs_snapshot_list * -nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile, - __u64 cno, - struct buffer_head *bh, - void *kaddr) -{ - struct nilfs_cpfile_header *header; - struct nilfs_checkpoint *cp; - struct nilfs_snapshot_list *list; - - if (cno != 0) { - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); - list = &cp->cp_snapshot_list; - } else { - header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); - list = &header->ch_snapshot_list; - } - return list; -} - static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno) { struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh; @@ -761,94 +774,103 @@ static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno) struct nilfs_snapshot_list *list; __u64 curr, prev; unsigned long curr_blkoff, prev_blkoff; - void *kaddr; + size_t offset, curr_list_offset, prev_list_offset; int ret; if (cno == 0) return -ENOENT; /* checkpoint number 0 is invalid */ down_write(&NILFS_MDT(cpfile)->mi_sem); + ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); + if (unlikely(ret < 0)) + goto out_sem; + ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); if (ret < 0) - goto out_sem; - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + goto out_header; + + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kmap_local_folio(cp_bh->b_folio, offset); if (nilfs_checkpoint_invalid(cp)) { ret = -ENOENT; - kunmap_local(kaddr); + kunmap_local(cp); goto out_cp; } if (nilfs_checkpoint_snapshot(cp)) { ret = 0; - kunmap_local(kaddr); + kunmap_local(cp); goto out_cp; } - kunmap_local(kaddr); + kunmap_local(cp); - ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); - if (ret < 0) - goto out_cp; - kaddr = kmap_local_page(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); + /* + * Find the last snapshot before the checkpoint being changed to + * snapshot mode by going backwards through the snapshot list. + * Set "prev" to its checkpoint number, or 0 if not found. + */ + header = kmap_local_folio(header_bh->b_folio, 0); list = &header->ch_snapshot_list; curr_bh = header_bh; get_bh(curr_bh); curr = 0; curr_blkoff = 0; + curr_list_offset = nilfs_cpfile_ch_snapshot_list_offset(); prev = le64_to_cpu(list->ssl_prev); while (prev > cno) { prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev); curr = prev; + kunmap_local(list); if (curr_blkoff != prev_blkoff) { - kunmap_local(kaddr); brelse(curr_bh); ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &curr_bh); - if (ret < 0) - goto out_header; - kaddr = kmap_local_page(curr_bh->b_page); + if (unlikely(ret < 0)) + goto out_cp; } + curr_list_offset = nilfs_cpfile_cp_snapshot_list_offset( + cpfile, curr, curr_bh); + list = kmap_local_folio(curr_bh->b_folio, curr_list_offset); curr_blkoff = prev_blkoff; - cp = nilfs_cpfile_block_get_checkpoint( - cpfile, curr, curr_bh, kaddr); - list = &cp->cp_snapshot_list; prev = le64_to_cpu(list->ssl_prev); } - kunmap_local(kaddr); + kunmap_local(list); if (prev != 0) { ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0, &prev_bh); if (ret < 0) goto out_curr; + + prev_list_offset = nilfs_cpfile_cp_snapshot_list_offset( + cpfile, prev, prev_bh); } else { prev_bh = header_bh; get_bh(prev_bh); + prev_list_offset = nilfs_cpfile_ch_snapshot_list_offset(); } - kaddr = kmap_local_page(curr_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, curr, curr_bh, kaddr); + /* Update the list entry for the next snapshot */ + list = kmap_local_folio(curr_bh->b_folio, curr_list_offset); list->ssl_prev = cpu_to_le64(cno); - kunmap_local(kaddr); + kunmap_local(list); - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + /* Update the checkpoint being changed to a snapshot */ + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kmap_local_folio(cp_bh->b_folio, offset); cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr); cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev); nilfs_checkpoint_set_snapshot(cp); - kunmap_local(kaddr); + kunmap_local(cp); - kaddr = kmap_local_page(prev_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, prev, prev_bh, kaddr); + /* Update the list entry for the previous snapshot */ + list = kmap_local_folio(prev_bh->b_folio, prev_list_offset); list->ssl_next = cpu_to_le64(cno); - kunmap_local(kaddr); + kunmap_local(list); - kaddr = kmap_local_page(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); + /* Update the statistics in the header */ + header = kmap_local_folio(header_bh->b_folio, 0); le64_add_cpu(&header->ch_nsnapshots, 1); - kunmap_local(kaddr); + kunmap_local(header); mark_buffer_dirty(prev_bh); mark_buffer_dirty(curr_bh); @@ -861,12 +883,12 @@ static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno) out_curr: brelse(curr_bh); - out_header: - brelse(header_bh); - out_cp: brelse(cp_bh); + out_header: + brelse(header_bh); + out_sem: up_write(&NILFS_MDT(cpfile)->mi_sem); return ret; @@ -879,79 +901,87 @@ static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno) struct nilfs_checkpoint *cp; struct nilfs_snapshot_list *list; __u64 next, prev; - void *kaddr; + size_t offset, next_list_offset, prev_list_offset; int ret; if (cno == 0) return -ENOENT; /* checkpoint number 0 is invalid */ down_write(&NILFS_MDT(cpfile)->mi_sem); + ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); + if (unlikely(ret < 0)) + goto out_sem; + ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh); if (ret < 0) - goto out_sem; - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + goto out_header; + + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, cp_bh); + cp = kmap_local_folio(cp_bh->b_folio, offset); if (nilfs_checkpoint_invalid(cp)) { ret = -ENOENT; - kunmap_local(kaddr); + kunmap_local(cp); goto out_cp; } if (!nilfs_checkpoint_snapshot(cp)) { ret = 0; - kunmap_local(kaddr); + kunmap_local(cp); goto out_cp; } list = &cp->cp_snapshot_list; next = le64_to_cpu(list->ssl_next); prev = le64_to_cpu(list->ssl_prev); - kunmap_local(kaddr); + kunmap_local(cp); - ret = nilfs_cpfile_get_header_block(cpfile, &header_bh); - if (ret < 0) - goto out_cp; if (next != 0) { ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0, &next_bh); if (ret < 0) - goto out_header; + goto out_cp; + + next_list_offset = nilfs_cpfile_cp_snapshot_list_offset( + cpfile, next, next_bh); } else { next_bh = header_bh; get_bh(next_bh); + next_list_offset = nilfs_cpfile_ch_snapshot_list_offset(); } if (prev != 0) { ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0, &prev_bh); if (ret < 0) goto out_next; + + prev_list_offset = nilfs_cpfile_cp_snapshot_list_offset( + cpfile, prev, prev_bh); } else { prev_bh = header_bh; get_bh(prev_bh); + prev_list_offset = nilfs_cpfile_ch_snapshot_list_offset(); } - kaddr = kmap_local_page(next_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, next, next_bh, kaddr); + /* Update the list entry for the next snapshot */ + list = kmap_local_folio(next_bh->b_folio, next_list_offset); list->ssl_prev = cpu_to_le64(prev); - kunmap_local(kaddr); + kunmap_local(list); - kaddr = kmap_local_page(prev_bh->b_page); - list = nilfs_cpfile_block_get_snapshot_list( - cpfile, prev, prev_bh, kaddr); + /* Update the list entry for the previous snapshot */ + list = kmap_local_folio(prev_bh->b_folio, prev_list_offset); list->ssl_next = cpu_to_le64(next); - kunmap_local(kaddr); + kunmap_local(list); - kaddr = kmap_local_page(cp_bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr); + /* Update the snapshot being changed back to a plain checkpoint */ + cp = kmap_local_folio(cp_bh->b_folio, offset); cp->cp_snapshot_list.ssl_next = cpu_to_le64(0); cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0); nilfs_checkpoint_clear_snapshot(cp); - kunmap_local(kaddr); + kunmap_local(cp); - kaddr = kmap_local_page(header_bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr); + /* Update the statistics in the header */ + header = kmap_local_folio(header_bh->b_folio, 0); le64_add_cpu(&header->ch_nsnapshots, -1); - kunmap_local(kaddr); + kunmap_local(header); mark_buffer_dirty(next_bh); mark_buffer_dirty(prev_bh); @@ -964,12 +994,12 @@ static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno) out_next: brelse(next_bh); - out_header: - brelse(header_bh); - out_cp: brelse(cp_bh); + out_header: + brelse(header_bh); + out_sem: up_write(&NILFS_MDT(cpfile)->mi_sem); return ret; @@ -990,7 +1020,7 @@ int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno) { struct buffer_head *bh; struct nilfs_checkpoint *cp; - void *kaddr; + size_t offset; int ret; /* @@ -1004,13 +1034,14 @@ int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno) ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh); if (ret < 0) goto out; - kaddr = kmap_local_page(bh->b_page); - cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); + + offset = nilfs_cpfile_checkpoint_offset(cpfile, cno, bh); + cp = kmap_local_folio(bh->b_folio, offset); if (nilfs_checkpoint_invalid(cp)) ret = -ENOENT; else ret = nilfs_checkpoint_snapshot(cp); - kunmap_local(kaddr); + kunmap_local(cp); brelse(bh); out: @@ -1079,7 +1110,6 @@ int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat) { struct buffer_head *bh; struct nilfs_cpfile_header *header; - void *kaddr; int ret; down_read(&NILFS_MDT(cpfile)->mi_sem); @@ -1087,12 +1117,11 @@ int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat) ret = nilfs_cpfile_get_header_block(cpfile, &bh); if (ret < 0) goto out_sem; - kaddr = kmap_local_page(bh->b_page); - header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr); + header = kmap_local_folio(bh->b_folio, 0); cpstat->cs_cno = nilfs_mdt_cno(cpfile); cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints); cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots); - kunmap_local(kaddr); + kunmap_local(header); brelse(bh); out_sem: diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c index 0bef662176a4c..e220dcb08aa65 100644 --- a/fs/nilfs2/dat.c +++ b/fs/nilfs2/dat.c @@ -89,15 +89,15 @@ int nilfs_dat_prepare_alloc(struct inode *dat, struct nilfs_palloc_req *req) void nilfs_dat_commit_alloc(struct inode *dat, struct nilfs_palloc_req *req) { struct nilfs_dat_entry *entry; - void *kaddr; + size_t offset; - kaddr = kmap_local_page(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, req->pr_entry_nr, + req->pr_entry_bh); + entry = kmap_local_folio(req->pr_entry_bh->b_folio, offset); entry->de_start = cpu_to_le64(NILFS_CNO_MIN); entry->de_end = cpu_to_le64(NILFS_CNO_MAX); entry->de_blocknr = cpu_to_le64(0); - kunmap_local(kaddr); + kunmap_local(entry); nilfs_palloc_commit_alloc_entry(dat, req); nilfs_dat_commit_entry(dat, req); @@ -113,15 +113,15 @@ static void nilfs_dat_commit_free(struct inode *dat, struct nilfs_palloc_req *req) { struct nilfs_dat_entry *entry; - void *kaddr; + size_t offset; - kaddr = kmap_local_page(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, req->pr_entry_nr, + req->pr_entry_bh); + entry = kmap_local_folio(req->pr_entry_bh->b_folio, offset); entry->de_start = cpu_to_le64(NILFS_CNO_MIN); entry->de_end = cpu_to_le64(NILFS_CNO_MIN); entry->de_blocknr = cpu_to_le64(0); - kunmap_local(kaddr); + kunmap_local(entry); nilfs_dat_commit_entry(dat, req); @@ -143,14 +143,14 @@ void nilfs_dat_commit_start(struct inode *dat, struct nilfs_palloc_req *req, sector_t blocknr) { struct nilfs_dat_entry *entry; - void *kaddr; + size_t offset; - kaddr = kmap_local_page(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, req->pr_entry_nr, + req->pr_entry_bh); + entry = kmap_local_folio(req->pr_entry_bh->b_folio, offset); entry->de_start = cpu_to_le64(nilfs_mdt_cno(dat)); entry->de_blocknr = cpu_to_le64(blocknr); - kunmap_local(kaddr); + kunmap_local(entry); nilfs_dat_commit_entry(dat, req); } @@ -160,19 +160,19 @@ int nilfs_dat_prepare_end(struct inode *dat, struct nilfs_palloc_req *req) struct nilfs_dat_entry *entry; __u64 start; sector_t blocknr; - void *kaddr; + size_t offset; int ret; ret = nilfs_dat_prepare_entry(dat, req, 0); if (ret < 0) return ret; - kaddr = kmap_local_page(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, req->pr_entry_nr, + req->pr_entry_bh); + entry = kmap_local_folio(req->pr_entry_bh->b_folio, offset); start = le64_to_cpu(entry->de_start); blocknr = le64_to_cpu(entry->de_blocknr); - kunmap_local(kaddr); + kunmap_local(entry); if (blocknr == 0) { ret = nilfs_palloc_prepare_free_entry(dat, req); @@ -200,11 +200,11 @@ void nilfs_dat_commit_end(struct inode *dat, struct nilfs_palloc_req *req, struct nilfs_dat_entry *entry; __u64 start, end; sector_t blocknr; - void *kaddr; + size_t offset; - kaddr = kmap_local_page(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, req->pr_entry_nr, + req->pr_entry_bh); + entry = kmap_local_folio(req->pr_entry_bh->b_folio, offset); end = start = le64_to_cpu(entry->de_start); if (!dead) { end = nilfs_mdt_cno(dat); @@ -212,7 +212,7 @@ void nilfs_dat_commit_end(struct inode *dat, struct nilfs_palloc_req *req, } entry->de_end = cpu_to_le64(end); blocknr = le64_to_cpu(entry->de_blocknr); - kunmap_local(kaddr); + kunmap_local(entry); if (blocknr == 0) nilfs_dat_commit_free(dat, req); @@ -225,14 +225,14 @@ void nilfs_dat_abort_end(struct inode *dat, struct nilfs_palloc_req *req) struct nilfs_dat_entry *entry; __u64 start; sector_t blocknr; - void *kaddr; + size_t offset; - kaddr = kmap_local_page(req->pr_entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, req->pr_entry_nr, - req->pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, req->pr_entry_nr, + req->pr_entry_bh); + entry = kmap_local_folio(req->pr_entry_bh->b_folio, offset); start = le64_to_cpu(entry->de_start); blocknr = le64_to_cpu(entry->de_blocknr); - kunmap_local(kaddr); + kunmap_local(entry); if (start == nilfs_mdt_cno(dat) && blocknr == 0) nilfs_palloc_abort_free_entry(dat, req); @@ -336,7 +336,7 @@ int nilfs_dat_move(struct inode *dat, __u64 vblocknr, sector_t blocknr) { struct buffer_head *entry_bh; struct nilfs_dat_entry *entry; - void *kaddr; + size_t offset; int ret; ret = nilfs_palloc_get_entry_block(dat, vblocknr, 0, &entry_bh); @@ -359,21 +359,21 @@ int nilfs_dat_move(struct inode *dat, __u64 vblocknr, sector_t blocknr) } } - kaddr = kmap_local_page(entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, vblocknr, entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, vblocknr, entry_bh); + entry = kmap_local_folio(entry_bh->b_folio, offset); if (unlikely(entry->de_blocknr == cpu_to_le64(0))) { nilfs_crit(dat->i_sb, "%s: invalid vblocknr = %llu, [%llu, %llu)", __func__, (unsigned long long)vblocknr, (unsigned long long)le64_to_cpu(entry->de_start), (unsigned long long)le64_to_cpu(entry->de_end)); - kunmap_local(kaddr); + kunmap_local(entry); brelse(entry_bh); return -EINVAL; } WARN_ON(blocknr == 0); entry->de_blocknr = cpu_to_le64(blocknr); - kunmap_local(kaddr); + kunmap_local(entry); mark_buffer_dirty(entry_bh); nilfs_mdt_mark_dirty(dat); @@ -407,7 +407,7 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) struct buffer_head *entry_bh, *bh; struct nilfs_dat_entry *entry; sector_t blocknr; - void *kaddr; + size_t offset; int ret; ret = nilfs_palloc_get_entry_block(dat, vblocknr, 0, &entry_bh); @@ -423,8 +423,8 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) } } - kaddr = kmap_local_page(entry_bh->b_page); - entry = nilfs_palloc_block_get_entry(dat, vblocknr, entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(dat, vblocknr, entry_bh); + entry = kmap_local_folio(entry_bh->b_folio, offset); blocknr = le64_to_cpu(entry->de_blocknr); if (blocknr == 0) { ret = -ENOENT; @@ -433,7 +433,7 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) *blocknrp = blocknr; out: - kunmap_local(kaddr); + kunmap_local(entry); brelse(entry_bh); return ret; } @@ -442,11 +442,12 @@ ssize_t nilfs_dat_get_vinfo(struct inode *dat, void *buf, unsigned int visz, size_t nvi) { struct buffer_head *entry_bh; - struct nilfs_dat_entry *entry; + struct nilfs_dat_entry *entry, *first_entry; struct nilfs_vinfo *vinfo = buf; __u64 first, last; - void *kaddr; + size_t offset; unsigned long entries_per_block = NILFS_MDT(dat)->mi_entries_per_block; + unsigned int entry_size = NILFS_MDT(dat)->mi_entry_size; int i, j, n, ret; for (i = 0; i < nvi; i += n) { @@ -454,23 +455,28 @@ ssize_t nilfs_dat_get_vinfo(struct inode *dat, void *buf, unsigned int visz, 0, &entry_bh); if (ret < 0) return ret; - kaddr = kmap_local_page(entry_bh->b_page); - /* last virtual block number in this block */ + first = vinfo->vi_vblocknr; first = div64_ul(first, entries_per_block); first *= entries_per_block; + /* first virtual block number in this block */ + last = first + entries_per_block - 1; + /* last virtual block number in this block */ + + offset = nilfs_palloc_entry_offset(dat, first, entry_bh); + first_entry = kmap_local_folio(entry_bh->b_folio, offset); for (j = i, n = 0; j < nvi && vinfo->vi_vblocknr >= first && vinfo->vi_vblocknr <= last; j++, n++, vinfo = (void *)vinfo + visz) { - entry = nilfs_palloc_block_get_entry( - dat, vinfo->vi_vblocknr, entry_bh, kaddr); + entry = (void *)first_entry + + (vinfo->vi_vblocknr - first) * entry_size; vinfo->vi_start = le64_to_cpu(entry->de_start); vinfo->vi_end = le64_to_cpu(entry->de_end); vinfo->vi_blocknr = le64_to_cpu(entry->de_blocknr); } - kunmap_local(kaddr); + kunmap_local(first_entry); brelse(entry_bh); } diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index a8602729586ab..14e8d82f86298 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -95,7 +95,7 @@ static void nilfs_commit_chunk(struct folio *folio, unsigned int nr_dirty; int err; - nr_dirty = nilfs_page_count_clean_buffers(&folio->page, from, to); + nr_dirty = nilfs_page_count_clean_buffers(folio, from, to); copied = block_write_end(NULL, mapping, pos, len, len, folio, NULL); if (pos + copied > dir->i_size) i_size_write(dir, pos + copied); diff --git a/fs/nilfs2/ifile.c b/fs/nilfs2/ifile.c index 1e86b9303b7ca..e7339eb3c08a5 100644 --- a/fs/nilfs2/ifile.c +++ b/fs/nilfs2/ifile.c @@ -98,7 +98,7 @@ int nilfs_ifile_delete_inode(struct inode *ifile, ino_t ino) .pr_entry_nr = ino, .pr_entry_bh = NULL }; struct nilfs_inode *raw_inode; - void *kaddr; + size_t offset; int ret; ret = nilfs_palloc_prepare_free_entry(ifile, &req); @@ -113,11 +113,11 @@ int nilfs_ifile_delete_inode(struct inode *ifile, ino_t ino) return ret; } - kaddr = kmap_local_page(req.pr_entry_bh->b_page); - raw_inode = nilfs_palloc_block_get_entry(ifile, req.pr_entry_nr, - req.pr_entry_bh, kaddr); + offset = nilfs_palloc_entry_offset(ifile, req.pr_entry_nr, + req.pr_entry_bh); + raw_inode = kmap_local_folio(req.pr_entry_bh->b_folio, offset); raw_inode->i_flags = 0; - kunmap_local(kaddr); + kunmap_local(raw_inode); mark_buffer_dirty(req.pr_entry_bh); brelse(req.pr_entry_bh); diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h index 625545cc2a989..5d116a566d9e4 100644 --- a/fs/nilfs2/ifile.h +++ b/fs/nilfs2/ifile.h @@ -21,9 +21,9 @@ static inline struct nilfs_inode * nilfs_ifile_map_inode(struct inode *ifile, ino_t ino, struct buffer_head *ibh) { - void *kaddr = kmap_local_page(ibh->b_page); + size_t __offset_in_folio = nilfs_palloc_entry_offset(ifile, ino, ibh); - return nilfs_palloc_block_get_entry(ifile, ino, ibh, kaddr); + return kmap_local_folio(ibh->b_folio, __offset_in_folio); } static inline void nilfs_ifile_unmap_inode(struct nilfs_inode *raw_inode) diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index be6acf6e2bfc5..cf9ba481ae376 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -170,37 +170,6 @@ static int nilfs_writepages(struct address_space *mapping, return err; } -static int nilfs_writepage(struct page *page, struct writeback_control *wbc) -{ - struct folio *folio = page_folio(page); - struct inode *inode = folio->mapping->host; - int err; - - if (sb_rdonly(inode->i_sb)) { - /* - * It means that filesystem was remounted in read-only - * mode because of error or metadata corruption. But we - * have dirty pages that try to be flushed in background. - * So, here we simply discard this dirty page. - */ - nilfs_clear_folio_dirty(folio); - folio_unlock(folio); - return -EROFS; - } - - folio_redirty_for_writepage(wbc, folio); - folio_unlock(folio); - - if (wbc->sync_mode == WB_SYNC_ALL) { - err = nilfs_construct_segment(inode->i_sb); - if (unlikely(err)) - return err; - } else if (wbc->for_reclaim) - nilfs_flush_segment(inode->i_sb, inode->i_ino); - - return 0; -} - static bool nilfs_dirty_folio(struct address_space *mapping, struct folio *folio) { @@ -273,7 +242,7 @@ static int nilfs_write_end(struct file *file, struct address_space *mapping, unsigned int nr_dirty; int err; - nr_dirty = nilfs_page_count_clean_buffers(&folio->page, start, + nr_dirty = nilfs_page_count_clean_buffers(folio, start, start + copied); copied = generic_write_end(file, mapping, pos, len, copied, folio, fsdata); @@ -295,7 +264,6 @@ nilfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) } const struct address_space_operations nilfs_aops = { - .writepage = nilfs_writepage, .read_folio = nilfs_read_folio, .writepages = nilfs_writepages, .dirty_folio = nilfs_dirty_folio, @@ -304,6 +272,7 @@ const struct address_space_operations nilfs_aops = { .write_end = nilfs_write_end, .invalidate_folio = block_invalidate_folio, .direct_IO = nilfs_direct_IO, + .migrate_folio = buffer_migrate_folio_norefs, .is_partially_uptodate = block_is_partially_uptodate, }; diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 2db6350b5ac2a..965b5ad1c0dff 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -33,7 +33,8 @@ nilfs_mdt_insert_new_block(struct inode *inode, unsigned long block, struct buffer_head *, void *)) { struct nilfs_inode_info *ii = NILFS_I(inode); - void *kaddr; + struct folio *folio = bh->b_folio; + void *from; int ret; /* Caller exclude read accesses using page lock */ @@ -47,12 +48,14 @@ nilfs_mdt_insert_new_block(struct inode *inode, unsigned long block, set_buffer_mapped(bh); - kaddr = kmap_local_page(bh->b_page); - memset(kaddr + bh_offset(bh), 0, i_blocksize(inode)); + /* Initialize block (block size > PAGE_SIZE not yet supported) */ + from = kmap_local_folio(folio, offset_in_folio(folio, bh->b_data)); + memset(from, 0, bh->b_size); if (init_block) - init_block(inode, bh, kaddr); - flush_dcache_page(bh->b_page); - kunmap_local(kaddr); + init_block(inode, bh, from); + kunmap_local(from); + + flush_dcache_folio(folio); set_buffer_uptodate(bh); mark_buffer_dirty(bh); @@ -395,10 +398,9 @@ int nilfs_mdt_fetch_dirty(struct inode *inode) return test_bit(NILFS_I_DIRTY, &ii->i_state); } -static int -nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) +static int nilfs_mdt_write_folio(struct folio *folio, + struct writeback_control *wbc) { - struct folio *folio = page_folio(page); struct inode *inode = folio->mapping->host; struct super_block *sb; int err = 0; @@ -431,11 +433,23 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) return err; } +static int nilfs_mdt_writeback(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct folio *folio = NULL; + int error; + + while ((folio = writeback_iter(mapping, wbc, folio, &error))) + error = nilfs_mdt_write_folio(folio, wbc); + + return error; +} static const struct address_space_operations def_mdt_aops = { .dirty_folio = block_dirty_folio, .invalidate_folio = block_invalidate_folio, - .writepage = nilfs_mdt_write_page, + .writepages = nilfs_mdt_writeback, + .migrate_folio = buffer_migrate_folio_norefs, }; static const struct inode_operations def_mdt_iops; @@ -570,7 +584,8 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh) if (!bh_frozen) bh_frozen = create_empty_buffers(folio, 1 << blkbits, 0); - bh_frozen = get_nth_bh(bh_frozen, bh_offset(bh) >> blkbits); + bh_frozen = get_nth_bh(bh_frozen, + offset_in_folio(folio, bh->b_data) >> blkbits); if (!buffer_uptodate(bh_frozen)) nilfs_copy_buffer(bh_frozen, bh); @@ -600,7 +615,8 @@ nilfs_mdt_get_frozen_buffer(struct inode *inode, struct buffer_head *bh) if (!IS_ERR(folio)) { bh_frozen = folio_buffers(folio); if (bh_frozen) { - n = bh_offset(bh) >> inode->i_blkbits; + n = offset_in_folio(folio, bh->b_data) >> + inode->i_blkbits; bh_frozen = get_nth_bh(bh_frozen, n); } folio_unlock(folio); diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 9a849397c768b..9de2a494a0694 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -422,14 +422,14 @@ void nilfs_clear_folio_dirty(struct folio *folio) __nilfs_clear_folio_dirty(folio); } -unsigned int nilfs_page_count_clean_buffers(struct page *page, +unsigned int nilfs_page_count_clean_buffers(struct folio *folio, unsigned int from, unsigned int to) { unsigned int block_start, block_end; struct buffer_head *bh, *head; unsigned int nc = 0; - for (bh = head = page_buffers(page), block_start = 0; + for (bh = head = folio_buffers(folio), block_start = 0; bh != head || !block_start; block_start = block_end, bh = bh->b_this_page) { block_end = block_start + bh->b_size; diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index 64521a03a19e0..136cd1c143c9c 100644 --- a/fs/nilfs2/page.h +++ b/fs/nilfs2/page.h @@ -43,8 +43,8 @@ int nilfs_copy_dirty_pages(struct address_space *, struct address_space *); void nilfs_copy_back_pages(struct address_space *, struct address_space *); void nilfs_clear_folio_dirty(struct folio *folio); void nilfs_clear_dirty_pages(struct address_space *mapping); -unsigned int nilfs_page_count_clean_buffers(struct page *, unsigned int, - unsigned int); +unsigned int nilfs_page_count_clean_buffers(struct folio *folio, + unsigned int from, unsigned int to); unsigned long nilfs_find_uncommitted_extent(struct inode *inode, sector_t start_blk, sector_t *blkoff); diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index 21d81097a89f8..e43405bf521e8 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -481,19 +481,16 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, static int nilfs_recovery_copy_block(struct the_nilfs *nilfs, struct nilfs_recovery_block *rb, - loff_t pos, struct page *page) + loff_t pos, struct folio *folio) { struct buffer_head *bh_org; - size_t from = pos & ~PAGE_MASK; - void *kaddr; + size_t from = offset_in_folio(folio, pos); bh_org = __bread(nilfs->ns_bdev, rb->blocknr, nilfs->ns_blocksize); if (unlikely(!bh_org)) return -EIO; - kaddr = kmap_local_page(page); - memcpy(kaddr + from, bh_org->b_data, bh_org->b_size); - kunmap_local(kaddr); + memcpy_to_folio(folio, from, bh_org->b_data, bh_org->b_size); brelse(bh_org); return 0; } @@ -531,13 +528,13 @@ static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, goto failed_inode; } - err = nilfs_recovery_copy_block(nilfs, rb, pos, &folio->page); + err = nilfs_recovery_copy_block(nilfs, rb, pos, folio); if (unlikely(err)) - goto failed_page; + goto failed_folio; err = nilfs_set_file_dirty(inode, 1); if (unlikely(err)) - goto failed_page; + goto failed_folio; block_write_end(NULL, inode->i_mapping, pos, blocksize, blocksize, folio, NULL); @@ -548,7 +545,7 @@ static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, (*nr_salvaged_blocks)++; goto next; - failed_page: + failed_folio: folio_unlock(folio); folio_put(folio); diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index dc431b4c34c96..e08cab03366b4 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -205,7 +205,6 @@ static void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf, { struct buffer_head *bh; struct nilfs_segment_summary *raw_sum; - void *kaddr; u32 crc; bh = list_entry(segbuf->sb_segsum_buffers.next, struct buffer_head, @@ -220,9 +219,13 @@ static void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf, crc = crc32_le(crc, bh->b_data, bh->b_size); } list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - kaddr = kmap_local_page(bh->b_page); - crc = crc32_le(crc, kaddr + bh_offset(bh), bh->b_size); - kunmap_local(kaddr); + size_t offset = offset_in_folio(bh->b_folio, bh->b_data); + unsigned char *from; + + /* Do not support block sizes larger than PAGE_SIZE */ + from = kmap_local_folio(bh->b_folio, offset); + crc = crc32_le(crc, from, bh->b_size); + kunmap_local(from); } raw_sum->ss_datasum = cpu_to_le32(crc); } @@ -374,7 +377,7 @@ static int nilfs_segbuf_submit_bh(struct nilfs_segment_buffer *segbuf, struct nilfs_write_info *wi, struct buffer_head *bh) { - int len, err; + int err; BUG_ON(wi->nr_vecs <= 0); repeat: @@ -385,8 +388,8 @@ static int nilfs_segbuf_submit_bh(struct nilfs_segment_buffer *segbuf, (wi->nilfs->ns_blocksize_bits - 9); } - len = bio_add_page(wi->bio, bh->b_page, bh->b_size, bh_offset(bh)); - if (len == bh->b_size) { + if (bio_add_folio(wi->bio, bh->b_folio, bh->b_size, + offset_in_folio(bh->b_folio, bh->b_data))) { wi->end++; return 0; } diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index eea5a6a12f7be..d3ecc813d6333 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c @@ -70,11 +70,20 @@ nilfs_sufile_segment_usages_in_block(const struct inode *sufile, __u64 curr, max - curr + 1); } -static struct nilfs_segment_usage * -nilfs_sufile_block_get_segment_usage(const struct inode *sufile, __u64 segnum, - struct buffer_head *bh, void *kaddr) +/** + * nilfs_sufile_segment_usage_offset - calculate the byte offset of a segment + * usage entry in the folio containing it + * @sufile: segment usage file inode + * @segnum: number of segment usage + * @bh: buffer head of block containing segment usage indexed by @segnum + * + * Return: Byte offset in the folio of the segment usage entry. + */ +static size_t nilfs_sufile_segment_usage_offset(const struct inode *sufile, + __u64 segnum, + struct buffer_head *bh) { - return kaddr + bh_offset(bh) + + return offset_in_folio(bh->b_folio, bh->b_data) + nilfs_sufile_get_offset(sufile, segnum) * NILFS_MDT(sufile)->mi_entry_size; } @@ -112,13 +121,11 @@ static void nilfs_sufile_mod_counter(struct buffer_head *header_bh, u64 ncleanadd, u64 ndirtyadd) { struct nilfs_sufile_header *header; - void *kaddr; - kaddr = kmap_local_page(header_bh->b_page); - header = kaddr + bh_offset(header_bh); + header = kmap_local_folio(header_bh->b_folio, 0); le64_add_cpu(&header->sh_ncleansegs, ncleanadd); le64_add_cpu(&header->sh_ndirtysegs, ndirtyadd); - kunmap_local(kaddr); + kunmap_local(header); mark_buffer_dirty(header_bh); } @@ -313,6 +320,7 @@ int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump) struct nilfs_sufile_info *sui = NILFS_SUI(sufile); size_t susz = NILFS_MDT(sufile)->mi_entry_size; __u64 segnum, maxsegnum, last_alloc; + size_t offset; void *kaddr; unsigned long nsegments, nsus, cnt; int ret, j; @@ -322,10 +330,9 @@ int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump) ret = nilfs_sufile_get_header_block(sufile, &header_bh); if (ret < 0) goto out_sem; - kaddr = kmap_local_page(header_bh->b_page); - header = kaddr + bh_offset(header_bh); + header = kmap_local_folio(header_bh->b_folio, 0); last_alloc = le64_to_cpu(header->sh_last_alloc); - kunmap_local(kaddr); + kunmap_local(header); nsegments = nilfs_sufile_get_nsegments(sufile); maxsegnum = sui->allocmax; @@ -359,9 +366,10 @@ int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump) &su_bh); if (ret < 0) goto out_header; - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); + + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, + su_bh); + su = kaddr = kmap_local_folio(su_bh->b_folio, offset); nsus = nilfs_sufile_segment_usages_in_block( sufile, segnum, maxsegnum); @@ -372,12 +380,11 @@ int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump) nilfs_segment_usage_set_dirty(su); kunmap_local(kaddr); - kaddr = kmap_local_page(header_bh->b_page); - header = kaddr + bh_offset(header_bh); + header = kmap_local_folio(header_bh->b_folio, 0); le64_add_cpu(&header->sh_ncleansegs, -1); le64_add_cpu(&header->sh_ndirtysegs, 1); header->sh_last_alloc = cpu_to_le64(segnum); - kunmap_local(kaddr); + kunmap_local(header); sui->ncleansegs--; mark_buffer_dirty(header_bh); @@ -411,18 +418,18 @@ void nilfs_sufile_do_cancel_free(struct inode *sufile, __u64 segnum, struct buffer_head *su_bh) { struct nilfs_segment_usage *su; - void *kaddr; + size_t offset; - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, su_bh); + su = kmap_local_folio(su_bh->b_folio, offset); if (unlikely(!nilfs_segment_usage_clean(su))) { nilfs_warn(sufile->i_sb, "%s: segment %llu must be clean", __func__, (unsigned long long)segnum); - kunmap_local(kaddr); + kunmap_local(su); return; } nilfs_segment_usage_set_dirty(su); - kunmap_local(kaddr); + kunmap_local(su); nilfs_sufile_mod_counter(header_bh, -1, 1); NILFS_SUI(sufile)->ncleansegs--; @@ -436,14 +443,14 @@ void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum, struct buffer_head *su_bh) { struct nilfs_segment_usage *su; - void *kaddr; + size_t offset; int clean, dirty; - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, su_bh); + su = kmap_local_folio(su_bh->b_folio, offset); if (su->su_flags == cpu_to_le32(BIT(NILFS_SEGMENT_USAGE_DIRTY)) && su->su_nblocks == cpu_to_le32(0)) { - kunmap_local(kaddr); + kunmap_local(su); return; } clean = nilfs_segment_usage_clean(su); @@ -453,7 +460,7 @@ void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum, su->su_lastmod = cpu_to_le64(0); su->su_nblocks = cpu_to_le32(0); su->su_flags = cpu_to_le32(BIT(NILFS_SEGMENT_USAGE_DIRTY)); - kunmap_local(kaddr); + kunmap_local(su); nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1); NILFS_SUI(sufile)->ncleansegs -= clean; @@ -467,15 +474,15 @@ void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum, struct buffer_head *su_bh) { struct nilfs_segment_usage *su; - void *kaddr; + size_t offset; int sudirty; - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, su_bh); + su = kmap_local_folio(su_bh->b_folio, offset); if (nilfs_segment_usage_clean(su)) { nilfs_warn(sufile->i_sb, "%s: segment %llu is already clean", __func__, (unsigned long long)segnum); - kunmap_local(kaddr); + kunmap_local(su); return; } if (unlikely(nilfs_segment_usage_error(su))) @@ -488,7 +495,7 @@ void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum, (unsigned long long)segnum); nilfs_segment_usage_set_clean(su); - kunmap_local(kaddr); + kunmap_local(su); mark_buffer_dirty(su_bh); nilfs_sufile_mod_counter(header_bh, 1, sudirty ? (u64)-1 : 0); @@ -507,7 +514,7 @@ void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum, int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum) { struct buffer_head *bh; - void *kaddr; + size_t offset; struct nilfs_segment_usage *su; int ret; @@ -523,12 +530,12 @@ int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum) goto out_sem; } - kaddr = kmap_local_page(bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, bh); + su = kmap_local_folio(bh->b_folio, offset); if (unlikely(nilfs_segment_usage_error(su))) { struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; - kunmap_local(kaddr); + kunmap_local(su); brelse(bh); if (nilfs_segment_is_active(nilfs, segnum)) { nilfs_error(sufile->i_sb, @@ -546,7 +553,7 @@ int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum) ret = -EIO; } else { nilfs_segment_usage_set_dirty(su); - kunmap_local(kaddr); + kunmap_local(su); mark_buffer_dirty(bh); nilfs_mdt_mark_dirty(sufile); brelse(bh); @@ -568,7 +575,7 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, { struct buffer_head *bh; struct nilfs_segment_usage *su; - void *kaddr; + size_t offset; int ret; down_write(&NILFS_MDT(sufile)->mi_sem); @@ -576,8 +583,8 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, if (ret < 0) goto out_sem; - kaddr = kmap_local_page(bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, bh); + su = kmap_local_folio(bh->b_folio, offset); if (modtime) { /* * Check segusage error and set su_lastmod only when updating @@ -587,7 +594,7 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, su->su_lastmod = cpu_to_le64(modtime); } su->su_nblocks = cpu_to_le32(nblocks); - kunmap_local(kaddr); + kunmap_local(su); mark_buffer_dirty(bh); nilfs_mdt_mark_dirty(sufile); @@ -619,7 +626,6 @@ int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat) struct buffer_head *header_bh; struct nilfs_sufile_header *header; struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; - void *kaddr; int ret; down_read(&NILFS_MDT(sufile)->mi_sem); @@ -628,8 +634,7 @@ int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat) if (ret < 0) goto out_sem; - kaddr = kmap_local_page(header_bh->b_page); - header = kaddr + bh_offset(header_bh); + header = kmap_local_folio(header_bh->b_folio, 0); sustat->ss_nsegs = nilfs_sufile_get_nsegments(sufile); sustat->ss_ncleansegs = le64_to_cpu(header->sh_ncleansegs); sustat->ss_ndirtysegs = le64_to_cpu(header->sh_ndirtysegs); @@ -638,7 +643,7 @@ int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat) spin_lock(&nilfs->ns_last_segment_lock); sustat->ss_prot_seq = nilfs->ns_prot_seq; spin_unlock(&nilfs->ns_last_segment_lock); - kunmap_local(kaddr); + kunmap_local(header); brelse(header_bh); out_sem: @@ -651,18 +656,18 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum, struct buffer_head *su_bh) { struct nilfs_segment_usage *su; - void *kaddr; + size_t offset; int suclean; - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, su_bh); + su = kmap_local_folio(su_bh->b_folio, offset); if (nilfs_segment_usage_error(su)) { - kunmap_local(kaddr); + kunmap_local(su); return; } suclean = nilfs_segment_usage_clean(su); nilfs_segment_usage_set_error(su); - kunmap_local(kaddr); + kunmap_local(su); if (suclean) { nilfs_sufile_mod_counter(header_bh, -1, 0); @@ -700,7 +705,7 @@ static int nilfs_sufile_truncate_range(struct inode *sufile, unsigned long segusages_per_block; unsigned long nsegs, ncleaned; __u64 segnum; - void *kaddr; + size_t offset; ssize_t n, nc; int ret; int j; @@ -731,16 +736,16 @@ static int nilfs_sufile_truncate_range(struct inode *sufile, /* hole */ continue; } - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, + su_bh); + su = kmap_local_folio(su_bh->b_folio, offset); su2 = su; for (j = 0; j < n; j++, su = (void *)su + susz) { if ((le32_to_cpu(su->su_flags) & ~BIT(NILFS_SEGMENT_USAGE_ERROR)) || nilfs_segment_is_active(nilfs, segnum + j)) { ret = -EBUSY; - kunmap_local(kaddr); + kunmap_local(su2); brelse(su_bh); goto out_header; } @@ -752,7 +757,7 @@ static int nilfs_sufile_truncate_range(struct inode *sufile, nc++; } } - kunmap_local(kaddr); + kunmap_local(su2); if (nc > 0) { mark_buffer_dirty(su_bh); ncleaned += nc; @@ -799,7 +804,6 @@ int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs) struct buffer_head *header_bh; struct nilfs_sufile_header *header; struct nilfs_sufile_info *sui = NILFS_SUI(sufile); - void *kaddr; unsigned long nsegs, nrsvsegs; int ret = 0; @@ -837,10 +841,9 @@ int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs) sui->allocmin = 0; } - kaddr = kmap_local_page(header_bh->b_page); - header = kaddr + bh_offset(header_bh); + header = kmap_local_folio(header_bh->b_folio, 0); header->sh_ncleansegs = cpu_to_le64(sui->ncleansegs); - kunmap_local(kaddr); + kunmap_local(header); mark_buffer_dirty(header_bh); nilfs_mdt_mark_dirty(sufile); @@ -874,6 +877,7 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, struct nilfs_suinfo *si = buf; size_t susz = NILFS_MDT(sufile)->mi_entry_size; struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; + size_t offset; void *kaddr; unsigned long nsegs, segusages_per_block; ssize_t n; @@ -901,9 +905,9 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, continue; } - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, + su_bh); + su = kaddr = kmap_local_folio(su_bh->b_folio, offset); for (j = 0; j < n; j++, su = (void *)su + susz, si = (void *)si + sisz) { si->sui_lastmod = le64_to_cpu(su->su_lastmod); @@ -951,7 +955,7 @@ ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf, struct buffer_head *header_bh, *bh; struct nilfs_suinfo_update *sup, *supend = buf + supsz * nsup; struct nilfs_segment_usage *su; - void *kaddr; + size_t offset; unsigned long blkoff, prev_blkoff; int cleansi, cleansu, dirtysi, dirtysu; long ncleaned = 0, ndirtied = 0; @@ -983,9 +987,9 @@ ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf, goto out_header; for (;;) { - kaddr = kmap_local_page(bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, sup->sup_segnum, bh, kaddr); + offset = nilfs_sufile_segment_usage_offset( + sufile, sup->sup_segnum, bh); + su = kmap_local_folio(bh->b_folio, offset); if (nilfs_suinfo_update_lastmod(sup)) su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod); @@ -1020,7 +1024,7 @@ ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf, su->su_flags = cpu_to_le32(sup->sup_sui.sui_flags); } - kunmap_local(kaddr); + kunmap_local(su); sup = (void *)sup + supsz; if (sup >= supend) @@ -1076,6 +1080,7 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; struct buffer_head *su_bh; struct nilfs_segment_usage *su; + size_t offset; void *kaddr; size_t n, i, susz = NILFS_MDT(sufile)->mi_entry_size; sector_t seg_start, seg_end, start_block, end_block; @@ -1125,9 +1130,9 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) continue; } - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage(sufile, segnum, - su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset(sufile, segnum, + su_bh); + su = kaddr = kmap_local_folio(su_bh->b_folio, offset); for (i = 0; i < n; ++i, ++segnum, su = (void *)su + susz) { if (!nilfs_segment_usage_clean(su)) continue; @@ -1167,9 +1172,10 @@ int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) } ndiscarded += nblocks; - kaddr = kmap_local_page(su_bh->b_page); - su = nilfs_sufile_block_get_segment_usage( - sufile, segnum, su_bh, kaddr); + offset = nilfs_sufile_segment_usage_offset( + sufile, segnum, su_bh); + su = kaddr = kmap_local_folio(su_bh->b_folio, + offset); } /* start new extent */ @@ -1221,7 +1227,6 @@ int nilfs_sufile_read(struct super_block *sb, size_t susize, struct nilfs_sufile_info *sui; struct buffer_head *header_bh; struct nilfs_sufile_header *header; - void *kaddr; int err; if (susize > sb->s_blocksize) { @@ -1262,10 +1267,9 @@ int nilfs_sufile_read(struct super_block *sb, size_t susize, } sui = NILFS_SUI(sufile); - kaddr = kmap_local_page(header_bh->b_page); - header = kaddr + bh_offset(header_bh); + header = kmap_local_folio(header_bh->b_folio, 0); sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs); - kunmap_local(kaddr); + kunmap_local(header); brelse(header_bh); sui->allocmax = nilfs_sufile_get_nsegments(sufile) - 1; diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 0763202d00c99..8d789b017fa9b 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -977,7 +977,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, /* Check for compressed frame. */ err = attr_is_frame_compressed(ni, attr_b, vcn >> NTFS_LZNT_CUNIT, - &hint); + &hint, run); if (err) goto out; @@ -1521,16 +1521,16 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, * attr_is_frame_compressed - Used to detect compressed frame. * * attr - base (primary) attribute segment. + * run - run to use, usually == &ni->file.run. * Only base segments contains valid 'attr->nres.c_unit' */ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, - CLST frame, CLST *clst_data) + CLST frame, CLST *clst_data, struct runs_tree *run) { int err; u32 clst_frame; CLST clen, lcn, vcn, alen, slen, vcn_next; size_t idx; - struct runs_tree *run; *clst_data = 0; @@ -1542,7 +1542,6 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, clst_frame = 1u << attr->nres.c_unit; vcn = frame * clst_frame; - run = &ni->file.run; if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { err = attr_load_runs_vcn(ni, attr->type, attr_name(attr), @@ -1678,7 +1677,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, if (err) goto out; - err = attr_is_frame_compressed(ni, attr_b, frame, &clst_data); + err = attr_is_frame_compressed(ni, attr_b, frame, &clst_data, run); if (err) goto out; diff --git a/fs/ntfs3/bitmap.c b/fs/ntfs3/bitmap.c index cf4fe21a50399..04107b9507170 100644 --- a/fs/ntfs3/bitmap.c +++ b/fs/ntfs3/bitmap.c @@ -710,20 +710,17 @@ int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) { int err = 0; struct super_block *sb = wnd->sb; - size_t bits0 = bits; u32 wbits = 8 * sb->s_blocksize; size_t iw = bit >> (sb->s_blocksize_bits + 3); u32 wbit = bit & (wbits - 1); struct buffer_head *bh; + u32 op; - while (iw < wnd->nwnd && bits) { - u32 tail, op; - + for (; iw < wnd->nwnd && bits; iw++, bit += op, bits -= op, wbit = 0) { if (iw + 1 == wnd->nwnd) wbits = wnd->bits_last; - tail = wbits - wbit; - op = min_t(u32, tail, bits); + op = min_t(u32, wbits - wbit, bits); bh = wnd_map(wnd, iw); if (IS_ERR(bh)) { @@ -736,20 +733,15 @@ int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) ntfs_bitmap_clear_le(bh->b_data, wbit, op); wnd->free_bits[iw] += op; + wnd->total_zeroes += op; set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); put_bh(bh); - wnd->total_zeroes += op; - bits -= op; - wbit = 0; - iw += 1; + wnd_add_free_ext(wnd, bit, op, false); } - - wnd_add_free_ext(wnd, bit, bits0, false); - return err; } @@ -760,20 +752,17 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) { int err = 0; struct super_block *sb = wnd->sb; - size_t bits0 = bits; size_t iw = bit >> (sb->s_blocksize_bits + 3); u32 wbits = 8 * sb->s_blocksize; u32 wbit = bit & (wbits - 1); struct buffer_head *bh; + u32 op; - while (iw < wnd->nwnd && bits) { - u32 tail, op; - + for (; iw < wnd->nwnd && bits; iw++, bit += op, bits -= op, wbit = 0) { if (unlikely(iw + 1 == wnd->nwnd)) wbits = wnd->bits_last; - tail = wbits - wbit; - op = min_t(u32, tail, bits); + op = min_t(u32, wbits - wbit, bits); bh = wnd_map(wnd, iw); if (IS_ERR(bh)) { @@ -785,21 +774,16 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) ntfs_bitmap_set_le(bh->b_data, wbit, op); wnd->free_bits[iw] -= op; + wnd->total_zeroes -= op; set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); put_bh(bh); - wnd->total_zeroes -= op; - bits -= op; - wbit = 0; - iw += 1; + if (!RB_EMPTY_ROOT(&wnd->start_tree)) + wnd_remove_free_ext(wnd, bit, op); } - - if (!RB_EMPTY_ROOT(&wnd->start_tree)) - wnd_remove_free_ext(wnd, bit, bits0); - return err; } @@ -852,15 +836,13 @@ static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits) size_t iw = bit >> (sb->s_blocksize_bits + 3); u32 wbits = 8 * sb->s_blocksize; u32 wbit = bit & (wbits - 1); + u32 op; - while (iw < wnd->nwnd && bits) { - u32 tail, op; - + for (; iw < wnd->nwnd && bits; iw++, bits -= op, wbit = 0) { if (unlikely(iw + 1 == wnd->nwnd)) wbits = wnd->bits_last; - tail = wbits - wbit; - op = min_t(u32, tail, bits); + op = min_t(u32, wbits - wbit, bits); if (wbits != wnd->free_bits[iw]) { bool ret; @@ -875,10 +857,6 @@ static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits) if (!ret) return false; } - - bits -= op; - wbit = 0; - iw += 1; } return true; @@ -928,6 +906,7 @@ bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) size_t iw = bit >> (sb->s_blocksize_bits + 3); u32 wbits = 8 * sb->s_blocksize; u32 wbit = bit & (wbits - 1); + u32 op; size_t end; struct rb_node *n; struct e_node *e; @@ -945,14 +924,11 @@ bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) return false; use_wnd: - while (iw < wnd->nwnd && bits) { - u32 tail, op; - + for (; iw < wnd->nwnd && bits; iw++, bits -= op, wbit = 0) { if (unlikely(iw + 1 == wnd->nwnd)) wbits = wnd->bits_last; - tail = wbits - wbit; - op = min_t(u32, tail, bits); + op = min_t(u32, wbits - wbit, bits); if (wnd->free_bits[iw]) { bool ret; @@ -966,10 +942,6 @@ bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) if (!ret) goto out; } - - bits -= op; - wbit = 0; - iw += 1; } ret = true; diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index e370eaf9bfe2e..3f96a11804c90 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -182,13 +182,15 @@ static int ntfs_extend_initialized_size(struct file *file, loff_t pos = valid; int err; + if (valid >= new_valid) + return 0; + if (is_resident(ni)) { ni->i_valid = new_valid; return 0; } WARN_ON(is_compressed(ni)); - WARN_ON(valid >= new_valid); for (;;) { u32 zerofrom, len; @@ -222,7 +224,7 @@ static int ntfs_extend_initialized_size(struct file *file, if (err) goto out; - folio_zero_range(folio, zerofrom, folio_size(folio)); + folio_zero_range(folio, zerofrom, folio_size(folio) - zerofrom); err = ntfs_write_end(file, mapping, pos, len, len, folio, NULL); if (err < 0) @@ -987,6 +989,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) u64 frame_vbo; pgoff_t index; bool frame_uptodate; + struct folio *folio; if (frame_size < PAGE_SIZE) { /* @@ -1041,8 +1044,9 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) if (err) { for (ip = 0; ip < pages_per_frame; ip++) { page = pages[ip]; - unlock_page(page); - put_page(page); + folio = page_folio(page); + folio_unlock(folio); + folio_put(folio); } goto out; } @@ -1052,9 +1056,10 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) off = offset_in_page(valid); for (; ip < pages_per_frame; ip++, off = 0) { page = pages[ip]; + folio = page_folio(page); zero_user_segment(page, off, PAGE_SIZE); flush_dcache_page(page); - SetPageUptodate(page); + folio_mark_uptodate(folio); } ni_lock(ni); @@ -1063,9 +1068,10 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) for (ip = 0; ip < pages_per_frame; ip++) { page = pages[ip]; - SetPageUptodate(page); - unlock_page(page); - put_page(page); + folio = page_folio(page); + folio_mark_uptodate(folio); + folio_unlock(folio); + folio_put(folio); } if (err) @@ -1107,8 +1113,9 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) for (ip = 0; ip < pages_per_frame; ip++) { page = pages[ip]; - unlock_page(page); - put_page(page); + folio = page_folio(page); + folio_unlock(folio); + folio_put(folio); } goto out; } @@ -1149,9 +1156,10 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) for (ip = 0; ip < pages_per_frame; ip++) { page = pages[ip]; ClearPageDirty(page); - SetPageUptodate(page); - unlock_page(page); - put_page(page); + folio = page_folio(page); + folio_mark_uptodate(folio); + folio_unlock(folio); + folio_put(folio); } if (err) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 41c7ffad27901..8b39d0ce5f289 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1900,46 +1900,6 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, return REPARSE_LINK; } -/* - * fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent - * but it uses 'fe_k' instead of fieinfo->fi_extents_start - */ -static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo, - struct fiemap_extent *fe_k, u64 logical, - u64 phys, u64 len, u32 flags) -{ - struct fiemap_extent extent; - - /* only count the extents */ - if (fieinfo->fi_extents_max == 0) { - fieinfo->fi_extents_mapped++; - return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; - } - - if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max) - return 1; - - if (flags & FIEMAP_EXTENT_DELALLOC) - flags |= FIEMAP_EXTENT_UNKNOWN; - if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED) - flags |= FIEMAP_EXTENT_ENCODED; - if (flags & (FIEMAP_EXTENT_DATA_TAIL | FIEMAP_EXTENT_DATA_INLINE)) - flags |= FIEMAP_EXTENT_NOT_ALIGNED; - - memset(&extent, 0, sizeof(extent)); - extent.fe_logical = logical; - extent.fe_physical = phys; - extent.fe_length = len; - extent.fe_flags = flags; - - memcpy(fe_k + fieinfo->fi_extents_mapped, &extent, sizeof(extent)); - - fieinfo->fi_extents_mapped++; - if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max) - return 1; - return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; -} - /* * ni_fiemap - Helper for file_fiemap(). * @@ -1950,11 +1910,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, __u64 vbo, __u64 len) { int err = 0; - struct fiemap_extent *fe_k = NULL; struct ntfs_sb_info *sbi = ni->mi.sbi; u8 cluster_bits = sbi->cluster_bits; - struct runs_tree *run; - struct rw_semaphore *run_lock; + struct runs_tree run; struct ATTRIB *attr; CLST vcn = vbo >> cluster_bits; CLST lcn, clen; @@ -1965,13 +1923,11 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, u32 flags; bool ok; + run_init(&run); if (S_ISDIR(ni->vfs_inode.i_mode)) { - run = &ni->dir.alloc_run; attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME, ARRAY_SIZE(I30_NAME), NULL, NULL); - run_lock = &ni->dir.run_lock; } else { - run = &ni->file.run; attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL); if (!attr) { @@ -1986,7 +1942,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, "fiemap is not supported for compressed file (cp -r)"); goto out; } - run_lock = &ni->file.run_lock; } if (!attr || !attr->non_res) { @@ -1998,51 +1953,32 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, goto out; } - /* - * To avoid lock problems replace pointer to user memory by pointer to kernel memory. - */ - fe_k = kmalloc_array(fieinfo->fi_extents_max, - sizeof(struct fiemap_extent), - GFP_NOFS | __GFP_ZERO); - if (!fe_k) { - err = -ENOMEM; - goto out; - } - end = vbo + len; alloc_size = le64_to_cpu(attr->nres.alloc_size); if (end > alloc_size) end = alloc_size; - down_read(run_lock); - while (vbo < end) { if (idx == -1) { - ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); + ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx); } else { CLST vcn_next = vcn; - ok = run_get_entry(run, ++idx, &vcn, &lcn, &clen) && + ok = run_get_entry(&run, ++idx, &vcn, &lcn, &clen) && vcn == vcn_next; if (!ok) vcn = vcn_next; } if (!ok) { - up_read(run_lock); - down_write(run_lock); - err = attr_load_runs_vcn(ni, attr->type, attr_name(attr), - attr->name_len, run, vcn); - - up_write(run_lock); - down_read(run_lock); + attr->name_len, &run, vcn); if (err) break; - ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); + ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx); if (!ok) { err = -EINVAL; @@ -2067,8 +2003,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, } else if (is_attr_compressed(attr)) { CLST clst_data; - err = attr_is_frame_compressed( - ni, attr, vcn >> attr->nres.c_unit, &clst_data); + err = attr_is_frame_compressed(ni, attr, + vcn >> attr->nres.c_unit, + &clst_data, &run); if (err) break; if (clst_data < NTFS_LZNT_CLUSTERS) @@ -2097,8 +2034,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (vbo + dlen >= end) flags |= FIEMAP_EXTENT_LAST; - err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, - dlen, flags); + err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen, + flags); if (err < 0) break; @@ -2119,8 +2056,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (vbo + bytes >= end) flags |= FIEMAP_EXTENT_LAST; - err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, bytes, - flags); + err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags); if (err < 0) break; if (err == 1) { @@ -2131,19 +2067,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, vbo += bytes; } - up_read(run_lock); - - /* - * Copy to user memory out of lock - */ - if (copy_to_user(fieinfo->fi_extents_start, fe_k, - fieinfo->fi_extents_max * - sizeof(struct fiemap_extent))) { - err = -EFAULT; - } - out: - kfree(fe_k); + run_close(&run); return err; } @@ -2672,7 +2597,8 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, down_write(&ni->file.run_lock); run_truncate_around(run, le64_to_cpu(attr->nres.svcn)); frame = frame_vbo >> (cluster_bits + NTFS_LZNT_CUNIT); - err = attr_is_frame_compressed(ni, attr, frame, &clst_data); + err = attr_is_frame_compressed(ni, attr, frame, &clst_data, + run); up_write(&ni->file.run_lock); if (err) goto out1; diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 0fa636038b4e4..03471bc9371cd 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2699,4 +2699,4 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) out: __putname(uni); return err; -} \ No newline at end of file +} diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 26e1e1379c04e..cd8e8374bb5a0 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -446,7 +446,8 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, struct runs_tree *run, u64 frame, u64 frames, u8 frame_bits, u32 *ondisk_size, u64 *vbo_data); int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, - CLST frame, CLST *clst_data); + CLST frame, CLST *clst_data, + struct runs_tree *run); int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, u64 new_valid); int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c index f810f0419d25e..61d53d39f3b9f 100644 --- a/fs/ntfs3/record.c +++ b/fs/ntfs3/record.c @@ -212,7 +212,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) return NULL; if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 || - !IS_ALIGNED(off, 4)) { + !IS_ALIGNED(off, 8)) { return NULL; } @@ -236,8 +236,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) off += asize; } - /* Can we use the first field (attr->type). */ - /* NOTE: this code also checks attr->size availability. */ + /* + * Can we use the first fields: + * attr->type, + * attr->size + */ if (off + 8 > used) { static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8); return NULL; @@ -259,10 +262,17 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) asize = le32_to_cpu(attr->size); + if (!IS_ALIGNED(asize, 8)) + return NULL; + /* Check overflow and boundary. */ if (off + asize < off || off + asize > used) return NULL; + /* Can we use the field attr->non_res. */ + if (off + 9 > used) + return NULL; + /* Check size of attribute. */ if (!attr->non_res) { /* Check resident fields. */ diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 58e988cd80490..6e86d66197ef2 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1055,8 +1055,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, { int ret, err; CLST next_vcn, lcn, len; - size_t index; - bool ok; + size_t index, done; + bool ok, zone; struct wnd_bitmap *wnd; ret = run_unpack(run, sbi, ino, svcn, evcn, vcn, run_buf, run_buf_size); @@ -1087,8 +1087,9 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, continue; down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); + zone = max(wnd->zone_bit, lcn) < min(wnd->zone_end, lcn + len); /* Check for free blocks. */ - ok = wnd_is_used(wnd, lcn, len); + ok = !zone && wnd_is_used(wnd, lcn, len); up_read(&wnd->rw_lock); if (ok) continue; @@ -1096,14 +1097,33 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, /* Looks like volume is corrupted. */ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); - if (down_write_trylock(&wnd->rw_lock)) { - /* Mark all zero bits as used in range [lcn, lcn+len). */ - size_t done; - err = wnd_set_used_safe(wnd, lcn, len, &done); - up_write(&wnd->rw_lock); - if (err) - return err; + if (!down_write_trylock(&wnd->rw_lock)) + continue; + + if (zone) { + /* + * Range [lcn, lcn + len) intersects with zone. + * To avoid complex with zone just turn it off. + */ + wnd_zone_set(wnd, 0, 0); + } + + /* Mark all zero bits as used in range [lcn, lcn+len). */ + err = wnd_set_used_safe(wnd, lcn, len, &done); + if (zone) { + /* Restore zone. Lock mft run. */ + struct rw_semaphore *lock = + is_mounted(sbi) ? &sbi->mft.ni->file.run_lock : + NULL; + if (lock) + down_read(lock); + ntfs_refresh_zone(sbi); + if (lock) + up_read(lock); } + up_write(&wnd->rw_lock); + if (err) + return err; } return ret; diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index ea9127ba32084..395e239206322 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -4767,7 +4767,7 @@ int ocfs2_insert_extent(handle_t *handle, } /* - * Allcate and add clusters into the extent b-tree. + * Allocate and add clusters into the extent b-tree. * The new clusters(clusters_to_add) will be inserted at logical_offset. * The extent b-tree's root is specified by et, and * it is not limited to the file storage. Any extent tree can use this diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index 45db1781ea735..1d1b4b7edba02 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -70,6 +70,8 @@ enum ocfs2_iocb_lock_bits { OCFS2_IOCB_NUM_LOCKS }; +#define ocfs2_iocb_init_rw_locked(iocb) \ + (iocb->private = NULL) #define ocfs2_iocb_clear_rw_locked(iocb) \ clear_bit(OCFS2_IOCB_RW_LOCK, (unsigned long *)&iocb->private) #define ocfs2_iocb_rw_locked_level(iocb) \ diff --git a/fs/ocfs2/cluster/quorum.c b/fs/ocfs2/cluster/quorum.c index 15d0ed9c13e5b..8bf17231d7b73 100644 --- a/fs/ocfs2/cluster/quorum.c +++ b/fs/ocfs2/cluster/quorum.c @@ -60,7 +60,7 @@ static void o2quo_fence_self(void) switch (o2nm_single_cluster->cl_fence_method) { case O2NM_FENCE_PANIC: panic("*** ocfs2 is very sorry to be fencing this system by " - "panicing ***\n"); + "panicking ***\n"); break; default: WARN_ON(o2nm_single_cluster->cl_fence_method >= diff --git a/fs/ocfs2/dlm/dlmapi.h b/fs/ocfs2/dlm/dlmapi.h index bae60ca2672a8..847a52dcbe7df 100644 --- a/fs/ocfs2/dlm/dlmapi.h +++ b/fs/ocfs2/dlm/dlmapi.h @@ -62,8 +62,6 @@ enum dlm_status { DLM_MAXSTATS, /* 41: upper limit for return code validation */ }; -/* for pretty-printing dlm_status error messages */ -const char *dlm_errmsg(enum dlm_status err); /* for pretty-printing dlm_status error names */ const char *dlm_errname(enum dlm_status err); diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index be5e9ed7da8d9..e9ef4e2b0e752 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -164,59 +164,6 @@ static const char *dlm_errnames[] = { [DLM_MAXSTATS] = "DLM_MAXSTATS", }; -static const char *dlm_errmsgs[] = { - [DLM_NORMAL] = "request in progress", - [DLM_GRANTED] = "request granted", - [DLM_DENIED] = "request denied", - [DLM_DENIED_NOLOCKS] = "request denied, out of system resources", - [DLM_WORKING] = "async request in progress", - [DLM_BLOCKED] = "lock request blocked", - [DLM_BLOCKED_ORPHAN] = "lock request blocked by a orphan lock", - [DLM_DENIED_GRACE_PERIOD] = "topological change in progress", - [DLM_SYSERR] = "system error", - [DLM_NOSUPPORT] = "unsupported", - [DLM_CANCELGRANT] = "can't cancel convert: already granted", - [DLM_IVLOCKID] = "bad lockid", - [DLM_SYNC] = "synchronous request granted", - [DLM_BADTYPE] = "bad resource type", - [DLM_BADRESOURCE] = "bad resource handle", - [DLM_MAXHANDLES] = "no more resource handles", - [DLM_NOCLINFO] = "can't contact cluster manager", - [DLM_NOLOCKMGR] = "can't contact lock manager", - [DLM_NOPURGED] = "can't contact purge daemon", - [DLM_BADARGS] = "bad api args", - [DLM_VOID] = "no status", - [DLM_NOTQUEUED] = "NOQUEUE was specified and request failed", - [DLM_IVBUFLEN] = "invalid resource name length", - [DLM_CVTUNGRANT] = "attempted to convert ungranted lock", - [DLM_BADPARAM] = "invalid lock mode specified", - [DLM_VALNOTVALID] = "value block has been invalidated", - [DLM_REJECTED] = "request rejected, unrecognized client", - [DLM_ABORT] = "blocked lock request cancelled", - [DLM_CANCEL] = "conversion request cancelled", - [DLM_IVRESHANDLE] = "invalid resource handle", - [DLM_DEADLOCK] = "deadlock recovery refused this request", - [DLM_DENIED_NOASTS] = "failed to allocate AST", - [DLM_FORWARD] = "request must wait for primary's response", - [DLM_TIMEOUT] = "timeout value for lock has expired", - [DLM_IVGROUPID] = "invalid group specification", - [DLM_VERS_CONFLICT] = "version conflicts prevent request handling", - [DLM_BAD_DEVICE_PATH] = "Locks device does not exist or path wrong", - [DLM_NO_DEVICE_PERMISSION] = "Client has insufficient perms for device", - [DLM_NO_CONTROL_DEVICE] = "Cannot set options on opened device ", - [DLM_RECOVERING] = "lock resource being recovered", - [DLM_MIGRATING] = "lock resource being migrated", - [DLM_MAXSTATS] = "invalid error number", -}; - -const char *dlm_errmsg(enum dlm_status err) -{ - if (err >= DLM_MAXSTATS || err < 0) - return dlm_errmsgs[DLM_MAXSTATS]; - return dlm_errmsgs[err]; -} -EXPORT_SYMBOL_GPL(dlm_errmsg); - const char *dlm_errname(enum dlm_status err) { if (err >= DLM_MAXSTATS || err < 0) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4fa6c840d20bf..957ced628eb13 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2398,6 +2398,8 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, } else inode_lock(inode); + ocfs2_iocb_init_rw_locked(iocb); + /* * Concurrent O_DIRECT writes are allowed with * mount_option "coherency=buffered". @@ -2544,6 +2546,8 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, if (!direct_io && nowait) return -EOPNOTSUPP; + ocfs2_iocb_init_rw_locked(iocb); + /* * buffered reads protect themselves in ->read_folio(). O_DIRECT reads * need locks to protect pending reads from racing with truncate. diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h index ebb5c99f490ef..788a8de922a43 100644 --- a/fs/ocfs2/quota.h +++ b/fs/ocfs2/quota.h @@ -97,7 +97,6 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off); int ocfs2_global_read_info(struct super_block *sb, int type); int ocfs2_global_write_info(struct super_block *sb, int type); -int ocfs2_global_read_dquot(struct dquot *dquot); int __ocfs2_sync_dquot(struct dquot *dquot, int freeing); static inline int ocfs2_sync_dquot(struct dquot *dquot) { diff --git a/fs/proc/array.c b/fs/proc/array.c index 34a47fb0c57f2..55ed3510d2bbb 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -109,7 +109,7 @@ void proc_task_name(struct seq_file *m, struct task_struct *p, bool escape) else if (p->flags & PF_KTHREAD) get_kthread_comm(tcomm, sizeof(tcomm), p); else - __get_task_comm(tcomm, sizeof(tcomm), p); + get_task_comm(tcomm, p); if (escape) seq_escape_str(m, tcomm, ESCAPE_SPACE | ESCAPE_SPECIAL, "\n\\"); diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 51446c59388f1..e376f48c4b8bf 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -493,13 +493,13 @@ static ssize_t read_kcore_iter(struct kiocb *iocb, struct iov_iter *iter) * the previous entry, search for a matching entry. */ if (!m || start < m->addr || start >= m->addr + m->size) { - struct kcore_list *iter; + struct kcore_list *pos; m = NULL; - list_for_each_entry(iter, &kclist_head, list) { - if (start >= iter->addr && - start < iter->addr + iter->size) { - m = iter; + list_for_each_entry(pos, &kclist_head, list) { + if (start >= pos->addr && + start < pos->addr + pos->size) { + m = pos; break; } } @@ -600,6 +600,7 @@ static ssize_t read_kcore_iter(struct kiocb *iocb, struct iov_iter *iter) ret = -EFAULT; goto out; } + ret = 0; /* * We know the bounce buffer is safe to copy from, so * use _copy_to_iter() directly. diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 06eb19dabb0ec..fe738623cf1ba 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -17,6 +17,11 @@ static void free_cached_dir(struct cached_fid *cfid); static void smb2_close_cached_fid(struct kref *ref); static void cfids_laundromat_worker(struct work_struct *work); +struct cached_dir_dentry { + struct list_head entry; + struct dentry *dentry; +}; + static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, const char *path, bool lookup_only, @@ -157,15 +162,17 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *npath; int retries = 0, cur_sleep = 1; - if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || - is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0)) + if (cifs_sb->root == NULL) + return -ENOENT; + + if (tcon == NULL) return -EOPNOTSUPP; ses = tcon->ses; cfids = tcon->cfids; - if (cifs_sb->root == NULL) - return -ENOENT; + if (cfids == NULL) + return -EOPNOTSUPP; replay_again: /* reinitialize for possible replay */ @@ -222,6 +229,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, } } cfid->dentry = dentry; + cfid->tcon = tcon; /* * We do not hold the lock for the open because in case @@ -293,7 +301,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, } goto oshr_free; } - cfid->tcon = tcon; cfid->is_open = true; spin_lock(&cfids->cfid_list_lock); @@ -389,7 +396,7 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon, struct cached_fids *cfids = tcon->cfids; if (cfids == NULL) - return -ENOENT; + return -EOPNOTSUPP; spin_lock(&cfids->cfid_list_lock); list_for_each_entry(cfid, &cfids->entries, entry) { @@ -470,7 +477,10 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb) struct cifs_tcon *tcon; struct tcon_link *tlink; struct cached_fids *cfids; + struct cached_dir_dentry *tmp_list, *q; + LIST_HEAD(entry); + spin_lock(&cifs_sb->tlink_tree_lock); for (node = rb_first(root); node; node = rb_next(node)) { tlink = rb_entry(node, struct tcon_link, tl_rbnode); tcon = tlink_tcon(tlink); @@ -479,11 +489,30 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb) cfids = tcon->cfids; if (cfids == NULL) continue; + spin_lock(&cfids->cfid_list_lock); list_for_each_entry(cfid, &cfids->entries, entry) { - dput(cfid->dentry); + tmp_list = kmalloc(sizeof(*tmp_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + spin_lock(&cfid->fid_lock); + tmp_list->dentry = cfid->dentry; cfid->dentry = NULL; + spin_unlock(&cfid->fid_lock); + + list_add_tail(&tmp_list->entry, &entry); } + spin_unlock(&cfids->cfid_list_lock); } + spin_unlock(&cifs_sb->tlink_tree_lock); + + list_for_each_entry_safe(tmp_list, q, &entry, entry) { + list_del(&tmp_list->entry); + dput(tmp_list->dentry); + kfree(tmp_list); + } + + /* Flush any pending work that will drop dentries */ + flush_workqueue(cfid_put_wq); } /* @@ -494,14 +523,18 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon) { struct cached_fids *cfids = tcon->cfids; struct cached_fid *cfid, *q; - LIST_HEAD(entry); if (cfids == NULL) return; + /* + * Mark all the cfids as closed, and move them to the cfids->dying list. + * They'll be cleaned up later by cfids_invalidation_worker. Take + * a reference to each cfid during this process. + */ spin_lock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { - list_move(&cfid->entry, &entry); + list_move(&cfid->entry, &cfids->dying); cfids->num_entries--; cfid->is_open = false; cfid->on_list = false; @@ -514,26 +547,47 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon) } else kref_get(&cfid->refcount); } + /* + * Queue dropping of the dentries once locks have been dropped + */ + if (!list_empty(&cfids->dying)) + queue_work(cfid_put_wq, &cfids->invalidation_work); spin_unlock(&cfids->cfid_list_lock); - - list_for_each_entry_safe(cfid, q, &entry, entry) { - list_del(&cfid->entry); - cancel_work_sync(&cfid->lease_break); - /* - * Drop the ref-count from above, either the lease-ref (if there - * was one) or the extra one acquired. - */ - kref_put(&cfid->refcount, smb2_close_cached_fid); - } } static void -smb2_cached_lease_break(struct work_struct *work) +cached_dir_offload_close(struct work_struct *work) { struct cached_fid *cfid = container_of(work, - struct cached_fid, lease_break); + struct cached_fid, close_work); + struct cifs_tcon *tcon = cfid->tcon; + + WARN_ON(cfid->on_list); kref_put(&cfid->refcount, smb2_close_cached_fid); + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close); +} + +/* + * Release the cached directory's dentry, and then queue work to drop cached + * directory itself (closing on server if needed). + * + * Must be called with a reference to the cached_fid and a reference to the + * tcon. + */ +static void cached_dir_put_work(struct work_struct *work) +{ + struct cached_fid *cfid = container_of(work, struct cached_fid, + put_work); + struct dentry *dentry; + + spin_lock(&cfid->fid_lock); + dentry = cfid->dentry; + cfid->dentry = NULL; + spin_unlock(&cfid->fid_lock); + + dput(dentry); + queue_work(serverclose_wq, &cfid->close_work); } int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) @@ -560,8 +614,10 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) cfid->on_list = false; cfids->num_entries--; - queue_work(cifsiod_wq, - &cfid->lease_break); + ++tcon->tc_count; + trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_cached_lease_break); + queue_work(cfid_put_wq, &cfid->put_work); spin_unlock(&cfids->cfid_list_lock); return true; } @@ -583,7 +639,8 @@ static struct cached_fid *init_cached_dir(const char *path) return NULL; } - INIT_WORK(&cfid->lease_break, smb2_cached_lease_break); + INIT_WORK(&cfid->close_work, cached_dir_offload_close); + INIT_WORK(&cfid->put_work, cached_dir_put_work); INIT_LIST_HEAD(&cfid->entry); INIT_LIST_HEAD(&cfid->dirents.entries); mutex_init(&cfid->dirents.de_mutex); @@ -596,6 +653,9 @@ static void free_cached_dir(struct cached_fid *cfid) { struct cached_dirent *dirent, *q; + WARN_ON(work_pending(&cfid->close_work)); + WARN_ON(work_pending(&cfid->put_work)); + dput(cfid->dentry); cfid->dentry = NULL; @@ -613,10 +673,30 @@ static void free_cached_dir(struct cached_fid *cfid) kfree(cfid); } +static void cfids_invalidation_worker(struct work_struct *work) +{ + struct cached_fids *cfids = container_of(work, struct cached_fids, + invalidation_work); + struct cached_fid *cfid, *q; + LIST_HEAD(entry); + + spin_lock(&cfids->cfid_list_lock); + /* move cfids->dying to the local list */ + list_cut_before(&entry, &cfids->dying, &cfids->dying); + spin_unlock(&cfids->cfid_list_lock); + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); + /* Drop the ref-count acquired in invalidate_all_cached_dirs */ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } +} + static void cfids_laundromat_worker(struct work_struct *work) { struct cached_fids *cfids; struct cached_fid *cfid, *q; + struct dentry *dentry; LIST_HEAD(entry); cfids = container_of(work, struct cached_fids, laundromat_work.work); @@ -642,18 +722,28 @@ static void cfids_laundromat_worker(struct work_struct *work) list_for_each_entry_safe(cfid, q, &entry, entry) { list_del(&cfid->entry); - /* - * Cancel and wait for the work to finish in case we are racing - * with it. - */ - cancel_work_sync(&cfid->lease_break); - /* - * Drop the ref-count from above, either the lease-ref (if there - * was one) or the extra one acquired. - */ - kref_put(&cfid->refcount, smb2_close_cached_fid); + + spin_lock(&cfid->fid_lock); + dentry = cfid->dentry; + cfid->dentry = NULL; + spin_unlock(&cfid->fid_lock); + + dput(dentry); + if (cfid->is_open) { + spin_lock(&cifs_tcp_ses_lock); + ++cfid->tcon->tc_count; + trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count, + netfs_trace_tcon_ref_get_cached_laundromat); + spin_unlock(&cifs_tcp_ses_lock); + queue_work(serverclose_wq, &cfid->close_work); + } else + /* + * Drop the ref-count from above, either the lease-ref (if there + * was one) or the extra one acquired. + */ + kref_put(&cfid->refcount, smb2_close_cached_fid); } - queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, + queue_delayed_work(cfid_put_wq, &cfids->laundromat_work, dir_cache_timeout * HZ); } @@ -666,9 +756,11 @@ struct cached_fids *init_cached_dirs(void) return NULL; spin_lock_init(&cfids->cfid_list_lock); INIT_LIST_HEAD(&cfids->entries); + INIT_LIST_HEAD(&cfids->dying); + INIT_WORK(&cfids->invalidation_work, cfids_invalidation_worker); INIT_DELAYED_WORK(&cfids->laundromat_work, cfids_laundromat_worker); - queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, + queue_delayed_work(cfid_put_wq, &cfids->laundromat_work, dir_cache_timeout * HZ); return cfids; @@ -687,6 +779,7 @@ void free_cached_dirs(struct cached_fids *cfids) return; cancel_delayed_work_sync(&cfids->laundromat_work); + cancel_work_sync(&cfids->invalidation_work); spin_lock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { @@ -694,6 +787,11 @@ void free_cached_dirs(struct cached_fids *cfids) cfid->is_open = false; list_move(&cfid->entry, &entry); } + list_for_each_entry_safe(cfid, q, &cfids->dying, entry) { + cfid->on_list = false; + cfid->is_open = false; + list_move(&cfid->entry, &entry); + } spin_unlock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &entry, entry) { diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h index 81ba0fd5cc16d..1dfe79d947a62 100644 --- a/fs/smb/client/cached_dir.h +++ b/fs/smb/client/cached_dir.h @@ -44,7 +44,8 @@ struct cached_fid { spinlock_t fid_lock; struct cifs_tcon *tcon; struct dentry *dentry; - struct work_struct lease_break; + struct work_struct put_work; + struct work_struct close_work; struct smb2_file_all_info file_all_info; struct cached_dirents dirents; }; @@ -53,10 +54,13 @@ struct cached_fid { struct cached_fids { /* Must be held when: * - accessing the cfids->entries list + * - accessing the cfids->dying list */ spinlock_t cfid_list_lock; int num_entries; struct list_head entries; + struct list_head dying; + struct work_struct invalidation_work; struct delayed_work laundromat_work; }; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 9798534710279..c9f9b6e979648 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -157,6 +157,7 @@ struct workqueue_struct *fileinfo_put_wq; struct workqueue_struct *cifsoplockd_wq; struct workqueue_struct *deferredclose_wq; struct workqueue_struct *serverclose_wq; +struct workqueue_struct *cfid_put_wq; __u32 cifs_lock_secret; /* @@ -1920,9 +1921,16 @@ init_cifs(void) goto out_destroy_deferredclose_wq; } + cfid_put_wq = alloc_workqueue("cfid_put_wq", + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!cfid_put_wq) { + rc = -ENOMEM; + goto out_destroy_serverclose_wq; + } + rc = cifs_init_inodecache(); if (rc) - goto out_destroy_serverclose_wq; + goto out_destroy_cfid_put_wq; rc = cifs_init_netfs(); if (rc) @@ -1990,6 +1998,8 @@ init_cifs(void) cifs_destroy_netfs(); out_destroy_inodecache: cifs_destroy_inodecache(); +out_destroy_cfid_put_wq: + destroy_workqueue(cfid_put_wq); out_destroy_serverclose_wq: destroy_workqueue(serverclose_wq); out_destroy_deferredclose_wq: diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 71b720dbb2ce3..a762dbbbd959b 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -146,6 +146,6 @@ extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ /* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 51 -#define CIFS_VERSION "2.51" +#define SMB3_PRODUCT_BUILD 52 +#define CIFS_VERSION "2.52" #endif /* _CIFSFS_H */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index fc33dfe7e9254..6e63abe461fd2 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -594,6 +594,7 @@ struct smb_version_operations { /* Check for STATUS_NETWORK_NAME_DELETED */ bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb, + const char *full_path, struct kvec *rsp_iov, struct cifs_open_info_data *data); int (*create_reparse_symlink)(const unsigned int xid, @@ -1990,7 +1991,7 @@ require use of the stronger protocol */ * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once * ->can_cache_brlcks * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc - * cached_fid->fid_mutex cifs_tcon->crfid tcon_info_alloc + * cached_fids->cfid_list_lock cifs_tcon->cfids->entries init_cached_dirs * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo * ->invalidHandle initiate_cifs_search @@ -2078,6 +2079,7 @@ extern struct workqueue_struct *fileinfo_put_wq; extern struct workqueue_struct *cifsoplockd_wq; extern struct workqueue_struct *deferredclose_wq; extern struct workqueue_struct *serverclose_wq; +extern struct workqueue_struct *cfid_put_wq; extern __u32 cifs_lock_secret; extern mempool_t *cifs_sm_req_poolp; diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index 075985bfb13a8..bbaaf16af20fd 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -314,8 +314,7 @@ extern void cifs_move_llist(struct list_head *source, struct list_head *dest); extern void cifs_free_llist(struct list_head *llist); extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); -extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, - const struct nls_table *nlsc); +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon); extern int cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, @@ -661,6 +660,7 @@ char *extract_hostname(const char *unc); char *extract_sharename(const char *unc); int parse_reparse_point(struct reparse_data_buffer *buf, u32 plen, struct cifs_sb_info *cifs_sb, + const char *full_path, bool unicode, struct cifs_open_info_data *data); int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, struct dentry *dentry, struct cifs_tcon *tcon, diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 4858331ee9182..bd42a419458e3 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -70,10 +70,9 @@ static struct { static int cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) { - int rc; - struct cifs_ses *ses; struct TCP_Server_Info *server; - struct nls_table *nls_codepage = NULL; + struct cifs_ses *ses; + int rc; /* * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for @@ -131,8 +130,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) } spin_unlock(&server->srv_lock); - nls_codepage = ses->local_nls; - /* * need to prevent multiple threads trying to simultaneously * reconnect the same SMB session @@ -156,7 +153,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) rc = cifs_negotiate_protocol(0, ses, server); if (!rc) - rc = cifs_setup_session(0, ses, server, nls_codepage); + rc = cifs_setup_session(0, ses, server, ses->local_nls); /* do we need to reconnect tcon? */ if (rc || !tcon->need_reconnect) { @@ -166,7 +163,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) skip_sess_setup: cifs_mark_open_files_invalid(tcon); - rc = cifs_tree_connect(0, tcon, nls_codepage); + rc = cifs_tree_connect(0, tcon); mutex_unlock(&ses->session_mutex); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); @@ -4320,8 +4317,8 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus * causing an infinite recursion. */ - rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, - (void **)&pSMB, (void **)&pSMBr); + rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, + (void **)&pSMB, (void **)&pSMBr); if (rc) return rc; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 0a97228c06b1e..56b3a9eb9b055 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -1897,11 +1897,35 @@ static int match_session(struct cifs_ses *ses, CIFS_MAX_USERNAME_LEN)) return 0; if ((ctx->username && strlen(ctx->username) != 0) && - ses->password != NULL && - strncmp(ses->password, - ctx->password ? ctx->password : "", - CIFS_MAX_PASSWORD_LEN)) - return 0; + ses->password != NULL) { + + /* New mount can only share sessions with an existing mount if: + * 1. Both password and password2 match, or + * 2. password2 of the old mount matches password of the new mount + * and password of the old mount matches password2 of the new + * mount + */ + if (ses->password2 != NULL && ctx->password2 != NULL) { + if (!((strncmp(ses->password, ctx->password ? + ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0 && + strncmp(ses->password2, ctx->password2, + CIFS_MAX_PASSWORD_LEN) == 0) || + (strncmp(ses->password, ctx->password2, + CIFS_MAX_PASSWORD_LEN) == 0 && + strncmp(ses->password2, ctx->password ? + ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0))) + return 0; + + } else if ((ses->password2 == NULL && ctx->password2 != NULL) || + (ses->password2 != NULL && ctx->password2 == NULL)) { + return 0; + + } else { + if (strncmp(ses->password, ctx->password ? + ctx->password : "", CIFS_MAX_PASSWORD_LEN)) + return 0; + } + } } if (strcmp(ctx->local_nls->charset, ses->local_nls->charset)) @@ -2244,6 +2268,7 @@ struct cifs_ses * cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) { int rc = 0; + int retries = 0; unsigned int xid; struct cifs_ses *ses; struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; @@ -2262,6 +2287,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) cifs_dbg(FYI, "Session needs reconnect\n"); mutex_lock(&ses->session_mutex); + +retry_old_session: rc = cifs_negotiate_protocol(xid, ses, server); if (rc) { mutex_unlock(&ses->session_mutex); @@ -2274,6 +2301,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) rc = cifs_setup_session(xid, ses, server, ctx->local_nls); if (rc) { + if (((rc == -EACCES) || (rc == -EKEYEXPIRED) || + (rc == -EKEYREVOKED)) && !retries && ses->password2) { + retries++; + cifs_dbg(FYI, "Session reconnect failed, retrying with alternate password\n"); + swap(ses->password, ses->password2); + goto retry_old_session; + } mutex_unlock(&ses->session_mutex); /* problem -- put our reference */ cifs_put_smb_ses(ses); @@ -2369,6 +2403,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses->chans_need_reconnect = 1; spin_unlock(&ses->chan_lock); +retry_new_session: mutex_lock(&ses->session_mutex); rc = cifs_negotiate_protocol(xid, ses, server); if (!rc) @@ -2381,8 +2416,16 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) sizeof(ses->smb3signingkey)); spin_unlock(&ses->chan_lock); - if (rc) - goto get_ses_fail; + if (rc) { + if (((rc == -EACCES) || (rc == -EKEYEXPIRED) || + (rc == -EKEYREVOKED)) && !retries && ses->password2) { + retries++; + cifs_dbg(FYI, "Session setup failed, retrying with alternate password\n"); + swap(ses->password, ses->password2); + goto retry_new_session; + } else + goto get_ses_fail; + } /* * success, put it on the list and add it as first channel @@ -2571,7 +2614,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) if (ses->server->dialect >= SMB20_PROT_ID && (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING)) - nohandlecache = ctx->nohandlecache; + nohandlecache = ctx->nohandlecache || !dir_cache_timeout; else nohandlecache = true; tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new); @@ -4344,10 +4387,10 @@ cifs_prune_tlinks(struct work_struct *work) } #ifndef CONFIG_CIFS_DFS_UPCALL -int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon) { - int rc; const struct smb_version_operations *ops = tcon->ses->server->ops; + int rc; /* only send once per connect */ spin_lock(&tcon->tc_lock); @@ -4370,7 +4413,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru tcon->status = TID_IN_TCON; spin_unlock(&tcon->tc_lock); - rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc); + rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, + tcon, tcon->ses->local_nls); if (rc) { spin_lock(&tcon->tc_lock); if (tcon->status == TID_IN_TCON) diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c index 3f6077c68d68a..4647df9e1e3bf 100644 --- a/fs/smb/client/dfs.c +++ b/fs/smb/client/dfs.c @@ -321,49 +321,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) return rc; } -/* Update dfs referral path of superblock */ -static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, - const char *target) -{ - int rc = 0; - size_t len = strlen(target); - char *refpath, *npath; - - if (unlikely(len < 2 || *target != '\\')) - return -EINVAL; - - if (target[1] == '\\') { - len += 1; - refpath = kmalloc(len, GFP_KERNEL); - if (!refpath) - return -ENOMEM; - - scnprintf(refpath, len, "%s", target); - } else { - len += sizeof("\\"); - refpath = kmalloc(len, GFP_KERNEL); - if (!refpath) - return -ENOMEM; - - scnprintf(refpath, len, "\\%s", target); - } - - npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); - kfree(refpath); - - if (IS_ERR(npath)) { - rc = PTR_ERR(npath); - } else { - mutex_lock(&server->refpath_lock); - spin_lock(&server->srv_lock); - kfree(server->leaf_fullpath); - server->leaf_fullpath = npath; - spin_unlock(&server->srv_lock); - mutex_unlock(&server->refpath_lock); - } - return rc; -} - static int target_share_matches_server(struct TCP_Server_Info *server, char *share, bool *target_match) { @@ -388,77 +345,22 @@ static int target_share_matches_server(struct TCP_Server_Info *server, char *sha return rc; } -static void __tree_connect_ipc(const unsigned int xid, char *tree, - struct cifs_sb_info *cifs_sb, - struct cifs_ses *ses) -{ - struct TCP_Server_Info *server = ses->server; - struct cifs_tcon *tcon = ses->tcon_ipc; - int rc; - - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - if (cifs_chan_needs_reconnect(ses, server) || - ses->ses_status != SES_GOOD) { - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n", - __func__); - return; - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - cifs_server_lock(server); - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); - cifs_server_unlock(server); - - rc = server->ops->tree_connect(xid, ses, tree, tcon, - cifs_sb->local_nls); - cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc); - spin_lock(&tcon->tc_lock); - if (rc) { - tcon->status = TID_NEED_TCON; - } else { - tcon->status = TID_GOOD; - tcon->need_reconnect = false; - } - spin_unlock(&tcon->tc_lock); -} - -static void tree_connect_ipc(const unsigned int xid, char *tree, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon) -{ - struct cifs_ses *ses = tcon->ses; - - __tree_connect_ipc(xid, tree, cifs_sb, ses); - __tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses)); -} - -static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, char *tree, bool islink, - struct dfs_cache_tgt_list *tl) +static int tree_connect_dfs_target(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + char *tree, bool islink, + struct dfs_cache_tgt_list *tl) { - int rc; + const struct smb_version_operations *ops = tcon->ses->server->ops; struct TCP_Server_Info *server = tcon->ses->server; - const struct smb_version_operations *ops = server->ops; - struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses); - char *share = NULL, *prefix = NULL; struct dfs_cache_tgt_iterator *tit; + char *share = NULL, *prefix = NULL; bool target_match; - - tit = dfs_cache_get_tgt_iterator(tl); - if (!tit) { - rc = -ENOENT; - goto out; - } + int rc = -ENOENT; /* Try to tree connect to all dfs targets */ - for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { - const char *target = dfs_cache_get_tgt_name(tit); - DFS_CACHE_TGT_LIST(ntl); - + for (tit = dfs_cache_get_tgt_iterator(tl); + tit; tit = dfs_cache_get_next_tgt(tl, tit)) { kfree(share); kfree(prefix); share = prefix = NULL; @@ -479,74 +381,21 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t } dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit); - tree_connect_ipc(xid, tree, cifs_sb, tcon); - scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); - if (!islink) { - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); - break; - } - - /* - * If no dfs referrals were returned from link target, then just do a TREE_CONNECT - * to it. Otherwise, cache the dfs referral and then mark current tcp ses for - * reconnect so either the demultiplex thread or the echo worker will reconnect to - * newly resolved target. - */ - if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, - NULL, &ntl)) { - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); - if (rc) - continue; - + rc = ops->tree_connect(xid, tcon->ses, tree, + tcon, tcon->ses->local_nls); + if (islink && !rc && cifs_sb) rc = cifs_update_super_prepath(cifs_sb, prefix); - } else { - /* Target is another dfs share */ - rc = update_server_fullpath(server, cifs_sb, target); - dfs_cache_free_tgts(tl); - - if (!rc) { - rc = -EREMOTE; - list_replace_init(&ntl.tl_list, &tl->tl_list); - } else - dfs_cache_free_tgts(&ntl); - } break; } -out: kfree(share); kfree(prefix); - - return rc; -} - -static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, char *tree, bool islink, - struct dfs_cache_tgt_list *tl) -{ - int rc; - int num_links = 0; - struct TCP_Server_Info *server = tcon->ses->server; - char *old_fullpath = server->leaf_fullpath; - - do { - rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); - if (!rc || rc != -EREMOTE) - break; - } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); - /* - * If we couldn't tree connect to any targets from last referral path, then - * retry it from newly resolved dfs referral. - */ - if (rc && server->leaf_fullpath != old_fullpath) - cifs_signal_cifsd_for_reconnect(server, true); - dfs_cache_free_tgts(tl); return rc; } -int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon) { int rc; struct TCP_Server_Info *server = tcon->ses->server; @@ -588,7 +437,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru cifs_server_lock(server); scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); cifs_server_unlock(server); - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); + rc = ops->tree_connect(xid, tcon->ses, tree, + tcon, tcon->ses->local_nls); goto out; } @@ -596,14 +446,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru if (!IS_ERR(sb)) cifs_sb = CIFS_SB(sb); - /* - * Tree connect to last share in @tcon->tree_name whether dfs super or - * cached dfs referral was not found. - */ - if (!cifs_sb || !server->leaf_fullpath || + /* Tree connect to last share in @tcon->tree_name if no DFS referral */ + if (!server->leaf_fullpath || dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) { - rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, - cifs_sb ? cifs_sb->local_nls : nlsc); + rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, + tcon, tcon->ses->local_nls); goto out; } diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 00820f57b434e..541608b0267ef 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -24,8 +24,8 @@ #include "dfs_cache.h" -#define CACHE_HTABLE_SIZE 32 -#define CACHE_MAX_ENTRIES 64 +#define CACHE_HTABLE_SIZE 512 +#define CACHE_MAX_ENTRIES 1024 #define CACHE_MIN_TTL 120 /* 2 minutes */ #define CACHE_DEFAULT_TTL 300 /* 5 minutes */ diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index c87879e4739b1..49123f458d0c7 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -920,12 +920,37 @@ do { \ cifs_sb->ctx->field = NULL; \ } while (0) +int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) +{ + if (ses->password && + cifs_sb->ctx->password && + strcmp(ses->password, cifs_sb->ctx->password)) { + kfree_sensitive(cifs_sb->ctx->password); + cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL); + if (!cifs_sb->ctx->password) + return -ENOMEM; + } + if (ses->password2 && + cifs_sb->ctx->password2 && + strcmp(ses->password2, cifs_sb->ctx->password2)) { + kfree_sensitive(cifs_sb->ctx->password2); + cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL); + if (!cifs_sb->ctx->password2) { + kfree_sensitive(cifs_sb->ctx->password); + cifs_sb->ctx->password = NULL; + return -ENOMEM; + } + } + return 0; +} + static int smb3_reconfigure(struct fs_context *fc) { struct smb3_fs_context *ctx = smb3_fc2context(fc); struct dentry *root = fc->root; struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses; + char *new_password = NULL, *new_password2 = NULL; bool need_recon = false; int rc; @@ -945,21 +970,63 @@ static int smb3_reconfigure(struct fs_context *fc) STEAL_STRING(cifs_sb, ctx, UNC); STEAL_STRING(cifs_sb, ctx, source); STEAL_STRING(cifs_sb, ctx, username); + if (need_recon == false) STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); else { - kfree_sensitive(ses->password); - ses->password = kstrdup(ctx->password, GFP_KERNEL); - if (!ses->password) - return -ENOMEM; - kfree_sensitive(ses->password2); - ses->password2 = kstrdup(ctx->password2, GFP_KERNEL); - if (!ses->password2) { - kfree_sensitive(ses->password); - ses->password = NULL; + if (ctx->password) { + new_password = kstrdup(ctx->password, GFP_KERNEL); + if (!new_password) + return -ENOMEM; + } else + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); + } + + /* + * if a new password2 has been specified, then reset it's value + * inside the ses struct + */ + if (ctx->password2) { + new_password2 = kstrdup(ctx->password2, GFP_KERNEL); + if (!new_password2) { + kfree_sensitive(new_password); return -ENOMEM; } + } else + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2); + + /* + * we may update the passwords in the ses struct below. Make sure we do + * not race with smb2_reconnect + */ + mutex_lock(&ses->session_mutex); + + /* + * smb2_reconnect may swap password and password2 in case session setup + * failed. First get ctx passwords in sync with ses passwords. It should + * be okay to do this even if this function were to return an error at a + * later stage + */ + rc = smb3_sync_session_ctx_passwords(cifs_sb, ses); + if (rc) { + mutex_unlock(&ses->session_mutex); + return rc; } + + /* + * now that allocations for passwords are done, commit them + */ + if (new_password) { + kfree_sensitive(ses->password); + ses->password = new_password; + } + if (new_password2) { + kfree_sensitive(ses->password2); + ses->password2 = new_password2; + } + + mutex_unlock(&ses->session_mutex); + STEAL_STRING(cifs_sb, ctx, domainname); STEAL_STRING(cifs_sb, ctx, nodename); STEAL_STRING(cifs_sb, ctx, iocharset); diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index 67b7fc48ac583..ac6baa774ad3a 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -309,6 +309,7 @@ static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *f } extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx); +extern int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses); extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); /* diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index de8063b44072f..42c030687918d 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -1137,6 +1137,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, rc = 0; } else if (iov && server->ops->parse_reparse_point) { rc = server->ops->parse_reparse_point(cifs_sb, + full_path, iov, data); } break; @@ -2495,13 +2496,10 @@ cifs_dentry_needs_reval(struct dentry *dentry) return true; if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { - spin_lock(&cfid->fid_lock); if (cfid->time && cifs_i->time > cfid->time) { - spin_unlock(&cfid->fid_lock); close_cached_dir(cfid); return false; } - spin_unlock(&cfid->fid_lock); close_cached_dir(cfid); } /* diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index 90da1e2b6217b..e81d2d78ddb7c 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -378,8 +378,8 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode, memset(iov, 0, sizeof(*iov)); - /* Exclude $LXDEV xattr for sockets and fifos */ - if (S_ISSOCK(_mode) || S_ISFIFO(_mode)) + /* Exclude $LXDEV xattr for non-device files */ + if (!S_ISBLK(_mode) && !S_ISCHR(_mode)) num_xattrs = ARRAY_SIZE(xattrs) - 1; else num_xattrs = ARRAY_SIZE(xattrs); @@ -535,9 +535,95 @@ static int parse_reparse_posix(struct reparse_posix_data *buf, return 0; } +int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len, + bool unicode, bool relative, + const char *full_path, + struct cifs_sb_info *cifs_sb) +{ + char sep = CIFS_DIR_SEP(cifs_sb); + char *linux_target = NULL; + char *smb_target = NULL; + int levels; + int rc; + int i; + + /* Check that length it valid for unicode/non-unicode mode */ + if (!len || (unicode && (len % 2))) { + cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); + rc = -EIO; + goto out; + } + + /* + * Check that buffer does not contain UTF-16 null codepoint in unicode + * mode or null byte in non-unicode mode because Linux cannot process + * symlink with null byte. + */ + if ((unicode && UniStrnlen((wchar_t *)buf, len/2) != len/2) || + (!unicode && strnlen(buf, len) != len)) { + cifs_dbg(VFS, "srv returned null byte in native symlink target location\n"); + rc = -EIO; + goto out; + } + + smb_target = cifs_strndup_from_utf16(buf, len, unicode, cifs_sb->local_nls); + if (!smb_target) { + rc = -ENOMEM; + goto out; + } + + if (smb_target[0] == sep && relative) { + /* + * This is a relative SMB symlink from the top of the share, + * which is the top level directory of the Linux mount point. + * Linux does not support such relative symlinks, so convert + * it to the relative symlink from the current directory. + * full_path is the SMB path to the symlink (from which is + * extracted current directory) and smb_target is the SMB path + * where symlink points, therefore full_path must always be on + * the SMB share. + */ + int smb_target_len = strlen(smb_target)+1; + levels = 0; + for (i = 1; full_path[i]; i++) { /* i=1 to skip leading sep */ + if (full_path[i] == sep) + levels++; + } + linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL); + if (!linux_target) { + rc = -ENOMEM; + goto out; + } + for (i = 0; i < levels; i++) { + linux_target[i*3 + 0] = '.'; + linux_target[i*3 + 1] = '.'; + linux_target[i*3 + 2] = sep; + } + memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */ + } else { + linux_target = smb_target; + smb_target = NULL; + } + + if (sep == '\\') + convert_delimiter(linux_target, '/'); + + rc = 0; + *target = linux_target; + + cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, *target); + +out: + if (rc != 0) + kfree(linux_target); + kfree(smb_target); + return rc; +} + static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, u32 plen, bool unicode, struct cifs_sb_info *cifs_sb, + const char *full_path, struct cifs_open_info_data *data) { unsigned int len; @@ -552,20 +638,64 @@ static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, return -EIO; } - data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, - len, unicode, + return smb2_parse_native_symlink(&data->symlink_target, + sym->PathBuffer + offs, + len, + unicode, + le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE, + full_path, + cifs_sb); +} + +static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf, + struct cifs_sb_info *cifs_sb, + struct cifs_open_info_data *data) +{ + int len = le16_to_cpu(buf->ReparseDataLength); + int symname_utf8_len; + __le16 *symname_utf16; + int symname_utf16_len; + + if (len <= sizeof(buf->Flags)) { + cifs_dbg(VFS, "srv returned malformed wsl symlink buffer\n"); + return -EIO; + } + + /* PathBuffer is in UTF-8 but without trailing null-term byte */ + symname_utf8_len = len - sizeof(buf->Flags); + /* + * Check that buffer does not contain null byte + * because Linux cannot process symlink with null byte. + */ + if (strnlen(buf->PathBuffer, symname_utf8_len) != symname_utf8_len) { + cifs_dbg(VFS, "srv returned null byte in wsl symlink target location\n"); + return -EIO; + } + symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL); + if (!symname_utf16) + return -ENOMEM; + symname_utf16_len = utf8s_to_utf16s(buf->PathBuffer, symname_utf8_len, + UTF16_LITTLE_ENDIAN, + symname_utf16, symname_utf8_len * 2); + if (symname_utf16_len < 0) { + kfree(symname_utf16); + return symname_utf16_len; + } + symname_utf16_len *= 2; /* utf8s_to_utf16s() returns number of u16 items, not byte length */ + + data->symlink_target = cifs_strndup_from_utf16((u8 *)symname_utf16, + symname_utf16_len, true, cifs_sb->local_nls); + kfree(symname_utf16); if (!data->symlink_target) return -ENOMEM; - convert_delimiter(data->symlink_target, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); - return 0; } int parse_reparse_point(struct reparse_data_buffer *buf, u32 plen, struct cifs_sb_info *cifs_sb, + const char *full_path, bool unicode, struct cifs_open_info_data *data) { struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); @@ -580,12 +710,20 @@ int parse_reparse_point(struct reparse_data_buffer *buf, case IO_REPARSE_TAG_SYMLINK: return parse_reparse_symlink( (struct reparse_symlink_data_buffer *)buf, - plen, unicode, cifs_sb, data); + plen, unicode, cifs_sb, full_path, data); case IO_REPARSE_TAG_LX_SYMLINK: + return parse_reparse_wsl_symlink( + (struct reparse_wsl_symlink_data_buffer *)buf, + cifs_sb, data); case IO_REPARSE_TAG_AF_UNIX: case IO_REPARSE_TAG_LX_FIFO: case IO_REPARSE_TAG_LX_CHR: case IO_REPARSE_TAG_LX_BLK: + if (le16_to_cpu(buf->ReparseDataLength) != 0) { + cifs_dbg(VFS, "srv returned malformed buffer for reparse point: 0x%08x\n", + le32_to_cpu(buf->ReparseTag)); + return -EIO; + } break; default: cifs_tcon_dbg(VFS | ONCE, "unhandled reparse tag: 0x%08x\n", @@ -596,6 +734,7 @@ int parse_reparse_point(struct reparse_data_buffer *buf, } int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, + const char *full_path, struct kvec *rsp_iov, struct cifs_open_info_data *data) { @@ -605,7 +744,7 @@ int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, buf = (struct reparse_data_buffer *)((u8 *)io + le32_to_cpu(io->OutputOffset)); - return parse_reparse_point(buf, plen, cifs_sb, true, data); + return parse_reparse_point(buf, plen, cifs_sb, full_path, true, data); } static void wsl_to_fattr(struct cifs_open_info_data *data, diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h index 2a9f4f9f79de0..ff05b0e75c928 100644 --- a/fs/smb/client/reparse.h +++ b/fs/smb/client/reparse.h @@ -117,7 +117,9 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, int smb2_mknod_reparse(unsigned int xid, struct inode *inode, struct dentry *dentry, struct cifs_tcon *tcon, const char *full_path, umode_t mode, dev_t dev); -int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov, +int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, + const char *full_path, + struct kvec *rsp_iov, struct cifs_open_info_data *data); #endif /* _CIFS_REPARSE_H */ diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index c88e9657f47a8..0bb77f9ec6861 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -347,10 +347,7 @@ cifs_disable_secondary_channels(struct cifs_ses *ses) spin_unlock(&ses->chan_lock); } -/* - * update the iface for the channel if necessary. - * Must be called with chan_lock held. - */ +/* update the iface for the channel if necessary. */ void cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) { diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index 9a6ece66c4d34..db3695eddcf9d 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -994,17 +994,17 @@ static int cifs_query_symlink(const unsigned int xid, } static int cifs_parse_reparse_point(struct cifs_sb_info *cifs_sb, + const char *full_path, struct kvec *rsp_iov, struct cifs_open_info_data *data) { struct reparse_data_buffer *buf; TRANSACT_IOCTL_RSP *io = rsp_iov->iov_base; - bool unicode = !!(io->hdr.Flags2 & SMBFLG2_UNICODE); u32 plen = le16_to_cpu(io->ByteCount); buf = (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol + le32_to_cpu(io->DataOffset)); - return parse_reparse_point(buf, plen, cifs_sb, unicode, data); + return parse_reparse_point(buf, plen, cifs_sb, full_path, true, data); } static bool diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index e301349b0078d..e836bc2193ddd 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -63,12 +63,12 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) return sym; } -int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path) +int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, + const char *full_path, char **path) { struct smb2_symlink_err_rsp *sym; unsigned int sub_offs, sub_len; unsigned int print_offs, print_len; - char *s; if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path) return -EINVAL; @@ -86,15 +86,13 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len) return -EINVAL; - s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true, - cifs_sb->local_nls); - if (!s) - return -ENOMEM; - convert_delimiter(s, '/'); - cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s); - - *path = s; - return 0; + return smb2_parse_native_symlink(path, + (char *)sym->PathBuffer + sub_offs, + sub_len, + true, + le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE, + full_path, + cifs_sb); } int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf) @@ -126,6 +124,7 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 goto out; if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) { rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, + oparms->path, &data->symlink_target); if (!rc) { memset(smb2_data, 0, sizeof(*smb2_data)); diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index e49d0c25eb038..a188908914fe8 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -828,6 +828,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, static int parse_create_response(struct cifs_open_info_data *data, struct cifs_sb_info *cifs_sb, + const char *full_path, const struct kvec *iov) { struct smb2_create_rsp *rsp = iov->iov_base; @@ -841,6 +842,7 @@ static int parse_create_response(struct cifs_open_info_data *data, break; case STATUS_STOPPED_ON_SYMLINK: rc = smb2_parse_symlink_response(cifs_sb, iov, + full_path, &data->symlink_target); if (rc) return rc; @@ -930,14 +932,14 @@ int smb2_query_path_info(const unsigned int xid, switch (rc) { case 0: - rc = parse_create_response(data, cifs_sb, &out_iov[0]); + rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]); break; case -EOPNOTSUPP: /* * BB TODO: When support for special files added to Samba * re-verify this path. */ - rc = parse_create_response(data, cifs_sb, &out_iov[0]); + rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]); if (rc || !data->reparse_point) goto out; diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index fa96ebed83108..87cb1872db28b 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2932,7 +2932,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, struct fsctl_get_dfs_referral_req *dfs_req = NULL; struct get_dfs_referral_rsp *dfs_rsp = NULL; u32 dfs_req_size = 0, dfs_rsp_size = 0; - int retry_count = 0; + int retry_once = 0; cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); @@ -2981,21 +2981,25 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, /* Path to resolve in an UTF-16 null-terminated string */ memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); - do { + for (;;) { rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_DFS_GET_REFERRALS, (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, (char **)&dfs_rsp, &dfs_rsp_size); - if (!is_retryable_error(rc)) + if (fatal_signal_pending(current)) { + rc = -EINTR; + break; + } + if (!is_retryable_error(rc) || retry_once++) break; usleep_range(512, 2048); - } while (++retry_count < 5); + } if (!rc && !dfs_rsp) rc = -EIO; if (rc) { if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) - cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); + cifs_tcon_dbg(FYI, "%s: ioctl error: rc=%d\n", __func__, rc); goto out; } diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 0552368355370..010eae9d6c47e 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -216,10 +216,9 @@ static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, bool from_reconnect) { - int rc = 0; - struct nls_table *nls_codepage = NULL; struct cifs_ses *ses; int xid; + int rc = 0; /* * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so @@ -229,11 +228,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, if (tcon == NULL) return 0; - /* - * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in - * cifs_tree_connect(). - */ - if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) + if (smb2_command == SMB2_TREE_CONNECT) return 0; spin_lock(&tcon->tc_lock); @@ -334,8 +329,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, } spin_unlock(&server->srv_lock); - nls_codepage = ses->local_nls; - /* * need to prevent multiple threads trying to simultaneously * reconnect the same SMB session @@ -372,7 +365,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, } } - rc = cifs_setup_session(0, ses, server, nls_codepage); + rc = cifs_setup_session(0, ses, server, ses->local_nls); if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { /* * Try alternate password for next reconnect (key rotation @@ -406,7 +399,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, if (tcon->use_persistent) tcon->need_reopen_files = true; - rc = cifs_tree_connect(0, tcon, nls_codepage); + rc = cifs_tree_connect(0, tcon); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); if (rc) { @@ -494,6 +487,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, case SMB2_CHANGE_NOTIFY: case SMB2_QUERY_INFO: case SMB2_SET_INFO: + case SMB2_IOCTL: rc = -EAGAIN; } failed: @@ -1231,7 +1225,9 @@ SMB2_negotiate(const unsigned int xid, * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context * Set the cipher type manually. */ - if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + if ((server->dialect == SMB30_PROT_ID || + server->dialect == SMB302_PROT_ID) && + (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 71504b30909e1..09349fa8da039 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -111,7 +111,14 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, unsigned int *pbytes_read); -int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path); +int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len, + bool unicode, bool relative, + const char *full_path, + struct cifs_sb_info *cifs_sb); +int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, + const struct kvec *iov, + const char *full_path, + char **path); int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf); extern int smb2_unlock_range(struct cifsFileInfo *cfile, diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index 0b52d22a91a0c..12cbd3428a6da 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -44,6 +44,8 @@ EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \ EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \ EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \ + EM(netfs_trace_tcon_ref_get_cached_laundromat, "GET Ch-Lau") \ + EM(netfs_trace_tcon_ref_get_cached_lease_break, "GET Ch-Lea") \ EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \ EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ @@ -52,6 +54,7 @@ EM(netfs_trace_tcon_ref_new, "NEW ") \ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ + EM(netfs_trace_tcon_ref_put_cached_close, "PUT Ch-Cls") \ EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \ EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \ EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \ diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 9f272cc8f5660..3c7c706c797d2 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -1552,6 +1552,15 @@ struct reparse_symlink_data_buffer { /* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ +/* For IO_REPARSE_TAG_LX_SYMLINK */ +struct reparse_wsl_symlink_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le32 Flags; + __u8 PathBuffer[]; /* Variable Length UTF-8 string without nul-term */ +} __packed; + struct validate_negotiate_info_req { __le32 Capabilities; __u8 Guid[SMB2_CLIENT_GUID_SIZE]; diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c index b931a99ab9c85..5c4c5121fece1 100644 --- a/fs/smb/server/asn1.c +++ b/fs/smb/server/asn1.c @@ -104,7 +104,7 @@ int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, oid_len + ntlmssp_len) * 2 + neg_result_len + oid_len + ntlmssp_len; - buf = kmalloc(total_len, GFP_KERNEL); + buf = kmalloc(total_len, KSMBD_DEFAULT_GFP); if (!buf) return -ENOMEM; @@ -140,7 +140,7 @@ int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + neg_result_len; - buf = kmalloc(total_len, GFP_KERNEL); + buf = kmalloc(total_len, KSMBD_DEFAULT_GFP); if (!buf) return -ENOMEM; @@ -217,7 +217,7 @@ static int ksmbd_neg_token_alloc(void *context, size_t hdrlen, if (!vlen) return -EINVAL; - conn->mechToken = kmemdup_nul(value, vlen, GFP_KERNEL); + conn->mechToken = kmemdup_nul(value, vlen, KSMBD_DEFAULT_GFP); if (!conn->mechToken) return -ENOMEM; diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 611716bc8f27c..1d1ffd0acaca2 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -151,7 +151,7 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* convert user_name to unicode */ len = strlen(user_name(sess->user)); - uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + uniname = kzalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP); if (!uniname) { ret = -ENOMEM; goto out; @@ -175,7 +175,7 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* Convert domain name or conn name to unicode and uppercase */ len = strlen(dname); - domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + domain = kzalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP); if (!domain) { ret = -ENOMEM; goto out; @@ -254,7 +254,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, } len = CIFS_CRYPTO_KEY_SIZE + blen; - construct = kzalloc(len, GFP_KERNEL); + construct = kzalloc(len, KSMBD_DEFAULT_GFP); if (!construct) { rc = -ENOMEM; goto out; @@ -361,7 +361,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, if (sess_key_len > CIFS_KEY_SIZE) return -EINVAL; - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), KSMBD_DEFAULT_GFP); if (!ctx_arc4) return -ENOMEM; @@ -451,7 +451,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, chgblob->NegotiateFlags = cpu_to_le32(flags); len = strlen(ksmbd_netbios_name()); - name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + name = kmalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP); if (!name) return -ENOMEM; @@ -1043,7 +1043,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, if (!nvec) return NULL; - nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL); + nr_entries = kcalloc(nvec, sizeof(int), KSMBD_DEFAULT_GFP); if (!nr_entries) return NULL; @@ -1063,7 +1063,8 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, /* Add two entries for transform header and signature */ total_entries += 2; - sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), + KSMBD_DEFAULT_GFP); if (!sg) { kfree(nr_entries); return NULL; @@ -1163,7 +1164,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, goto free_ctx; } - req = aead_request_alloc(tfm, GFP_KERNEL); + req = aead_request_alloc(tfm, KSMBD_DEFAULT_GFP); if (!req) { rc = -ENOMEM; goto free_ctx; @@ -1182,7 +1183,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, } iv_len = crypto_aead_ivsize(tfm); - iv = kzalloc(iv_len, GFP_KERNEL); + iv = kzalloc(iv_len, KSMBD_DEFAULT_GFP); if (!iv) { rc = -ENOMEM; goto free_sg; diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index e6a72f75ab94b..c14dd72e1b301 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -52,7 +52,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) { struct ksmbd_conn *conn; - conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + conn = kzalloc(sizeof(struct ksmbd_conn), KSMBD_DEFAULT_GFP); if (!conn) return NULL; @@ -359,7 +359,7 @@ int ksmbd_conn_handler_loop(void *p) /* 4 for rfc1002 length field */ /* 1 for implied bcc[0] */ size = pdu_size + 4 + 1; - conn->request_buf = kvmalloc(size, GFP_KERNEL); + conn->request_buf = kvmalloc(size, KSMBD_DEFAULT_GFP); if (!conn->request_buf) break; @@ -404,6 +404,7 @@ int ksmbd_conn_handler_loop(void *p) out: ksmbd_conn_set_releasing(conn); /* Wait till all reference dropped to the Server object*/ + ksmbd_debug(CONN, "Wait for all pending requests(%d)\n", atomic_read(&conn->r_count)); wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); if (IS_ENABLED(CONFIG_UNICODE)) @@ -462,7 +463,7 @@ static void stop_sessions(void) up_read(&conn_list_lock); if (!list_empty(&conn_list)) { - schedule_timeout_interruptible(HZ / 10); /* 100ms */ + msleep(100); goto again; } } diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c index 81488d04199da..ce733dc9a4a35 100644 --- a/fs/smb/server/crypto_ctx.c +++ b/fs/smb/server/crypto_ctx.c @@ -89,7 +89,7 @@ static struct shash_desc *alloc_shash_desc(int id) return NULL; shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!shash) crypto_free_shash(tfm); else @@ -133,7 +133,7 @@ static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) ctx_list.avail_ctx++; spin_unlock(&ctx_list.ctx_lock); - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP); if (!ctx) { spin_lock(&ctx_list.ctx_lock); ctx_list.avail_ctx--; @@ -258,7 +258,7 @@ int ksmbd_crypto_create(void) init_waitqueue_head(&ctx_list.ctx_wait); ctx_list.avail_ctx = 1; - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP); if (!ctx) return -ENOMEM; list_add(&ctx->list, &ctx_list.idle_ctx); diff --git a/fs/smb/server/glob.h b/fs/smb/server/glob.h index d528b20b37a85..4ea187af23480 100644 --- a/fs/smb/server/glob.h +++ b/fs/smb/server/glob.h @@ -44,4 +44,6 @@ extern int ksmbd_debug_types; #define UNICODE_LEN(x) ((x) * 2) +#define KSMBD_DEFAULT_GFP (GFP_KERNEL | __GFP_RETRY_MAYFAIL) + #endif /* __KSMBD_GLOB_H */ diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c index d7c676c151e20..4af2e6007c29d 100644 --- a/fs/smb/server/ksmbd_work.c +++ b/fs/smb/server/ksmbd_work.c @@ -18,7 +18,7 @@ static struct workqueue_struct *ksmbd_wq; struct ksmbd_work *ksmbd_alloc_work_struct(void) { - struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, KSMBD_DEFAULT_GFP); if (work) { work->compound_fid = KSMBD_NO_FID; @@ -30,7 +30,7 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) INIT_LIST_HEAD(&work->aux_read_list); work->iov_alloc_cnt = 4; work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!work->iov) { kmem_cache_free(work_cache, work); work = NULL; @@ -114,7 +114,7 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, if (aux_size) { need_iov_cnt++; - ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL); + ar = kmalloc(sizeof(struct aux_read), KSMBD_DEFAULT_GFP); if (!ar) return -ENOMEM; } @@ -125,7 +125,7 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, work->iov_alloc_cnt += 4; new = krealloc(work->iov, sizeof(struct kvec) * work->iov_alloc_cnt, - GFP_KERNEL | __GFP_ZERO); + KSMBD_DEFAULT_GFP | __GFP_ZERO); if (!new) { kfree(ar); work->iov_alloc_cnt -= 4; @@ -169,7 +169,7 @@ int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, int allocate_interim_rsp_buf(struct ksmbd_work *work) { - work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, KSMBD_DEFAULT_GFP); if (!work->response_buf) return -ENOMEM; work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; diff --git a/fs/smb/server/mgmt/ksmbd_ida.c b/fs/smb/server/mgmt/ksmbd_ida.c index a18e27e9e0cd9..0e2ae994ab525 100644 --- a/fs/smb/server/mgmt/ksmbd_ida.c +++ b/fs/smb/server/mgmt/ksmbd_ida.c @@ -4,31 +4,32 @@ */ #include "ksmbd_ida.h" +#include "../glob.h" int ksmbd_acquire_smb2_tid(struct ida *ida) { - return ida_alloc_range(ida, 1, 0xFFFFFFFE, GFP_KERNEL); + return ida_alloc_range(ida, 1, 0xFFFFFFFE, KSMBD_DEFAULT_GFP); } int ksmbd_acquire_smb2_uid(struct ida *ida) { int id; - id = ida_alloc_min(ida, 1, GFP_KERNEL); + id = ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP); if (id == 0xFFFE) - id = ida_alloc_min(ida, 1, GFP_KERNEL); + id = ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP); return id; } int ksmbd_acquire_async_msg_id(struct ida *ida) { - return ida_alloc_min(ida, 1, GFP_KERNEL); + return ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP); } int ksmbd_acquire_id(struct ida *ida) { - return ida_alloc(ida, GFP_KERNEL); + return ida_alloc(ida, KSMBD_DEFAULT_GFP); } void ksmbd_release_id(struct ida *ida, int id) diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c index d8d03070ae44b..d3d5f99bdd34e 100644 --- a/fs/smb/server/mgmt/share_config.c +++ b/fs/smb/server/mgmt/share_config.c @@ -102,11 +102,11 @@ static int parse_veto_list(struct ksmbd_share_config *share, if (!sz) break; - p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + p = kzalloc(sizeof(struct ksmbd_veto_pattern), KSMBD_DEFAULT_GFP); if (!p) return -ENOMEM; - p->pattern = kstrdup(veto_list, GFP_KERNEL); + p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP); if (!p->pattern) { kfree(p); return -ENOMEM; @@ -150,14 +150,14 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, goto out; } - share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); + share = kzalloc(sizeof(struct ksmbd_share_config), KSMBD_DEFAULT_GFP); if (!share) goto out; share->flags = resp->flags; atomic_set(&share->refcount, 1); INIT_LIST_HEAD(&share->veto_list); - share->name = kstrdup(name, GFP_KERNEL); + share->name = kstrdup(name, KSMBD_DEFAULT_GFP); if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { int path_len = PATH_MAX; @@ -166,7 +166,7 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, path_len = resp->payload_sz - resp->veto_list_sz; share->path = kstrndup(ksmbd_share_config_path(resp), path_len, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (share->path) { share->path_sz = strlen(share->path); while (share->path_sz > 1 && diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c index 94a52a75014a4..ecfc575086712 100644 --- a/fs/smb/server/mgmt/tree_connect.c +++ b/fs/smb/server/mgmt/tree_connect.c @@ -31,7 +31,8 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name) if (!sc) return status; - tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), + KSMBD_DEFAULT_GFP); if (!tree_conn) { status.ret = -ENOMEM; goto out_error; @@ -80,7 +81,7 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name) init_waitqueue_head(&tree_conn->refcount_q); ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, - GFP_KERNEL)); + KSMBD_DEFAULT_GFP)); if (ret) { status.ret = -ENOMEM; goto out_error; diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c index 421a4a95e216a..56c9a38ca8789 100644 --- a/fs/smb/server/mgmt/user_config.c +++ b/fs/smb/server/mgmt/user_config.c @@ -36,16 +36,16 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, { struct ksmbd_user *user; - user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); + user = kmalloc(sizeof(struct ksmbd_user), KSMBD_DEFAULT_GFP); if (!user) return NULL; - user->name = kstrdup(resp->account, GFP_KERNEL); + user->name = kstrdup(resp->account, KSMBD_DEFAULT_GFP); user->flags = resp->status; user->gid = resp->gid; user->uid = resp->uid; user->passkey_sz = resp->hash_sz; - user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); + user->passkey = kmalloc(resp->hash_sz, KSMBD_DEFAULT_GFP); if (user->passkey) memcpy(user->passkey, resp->hash, resp->hash_sz); @@ -64,7 +64,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, user->sgid = kmemdup(resp_ext->____payload, resp_ext->ngroups * sizeof(gid_t), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!user->sgid) goto err_free; diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index ad02fe555fda7..df92d746e89ca 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -98,7 +98,7 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) if (!method) return -EINVAL; - entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + entry = kzalloc(sizeof(struct ksmbd_session_rpc), KSMBD_DEFAULT_GFP); if (!entry) return -ENOMEM; @@ -106,7 +106,7 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) entry->id = ksmbd_ipc_id_alloc(); if (entry->id < 0) goto free_entry; - old = xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL); + old = xa_store(&sess->rpc_handle_list, entry->id, entry, KSMBD_DEFAULT_GFP); if (xa_is_err(old)) goto free_id; @@ -201,7 +201,7 @@ int ksmbd_session_register(struct ksmbd_conn *conn, sess->dialect = conn->dialect; memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); ksmbd_expire_session(conn); - return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); + return xa_err(xa_store(&conn->sessions, sess->id, sess, KSMBD_DEFAULT_GFP)); } static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) @@ -314,7 +314,7 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, { struct preauth_session *sess; - sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + sess = kmalloc(sizeof(struct preauth_session), KSMBD_DEFAULT_GFP); if (!sess) return NULL; @@ -398,7 +398,7 @@ static struct ksmbd_session *__session_create(int protocol) if (protocol != CIFDS_SESSION_FLAG_SMB2) return NULL; - sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); + sess = kzalloc(sizeof(struct ksmbd_session), KSMBD_DEFAULT_GFP); if (!sess) return NULL; diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c index 1a5faa6f6e7bc..cb2a11ffb23fe 100644 --- a/fs/smb/server/misc.c +++ b/fs/smb/server/misc.c @@ -165,7 +165,7 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share, char *pathname, *ab_pathname, *nt_pathname; int share_path_len = share->path_sz; - pathname = kmalloc(PATH_MAX, GFP_KERNEL); + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); if (!pathname) return ERR_PTR(-EACCES); @@ -180,7 +180,8 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share, goto free_pathname; } - nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); + nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, + KSMBD_DEFAULT_GFP); if (!nt_pathname) { nt_pathname = ERR_PTR(-ENOMEM); goto free_pathname; @@ -232,7 +233,7 @@ char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) char *cf_name; int cf_len; - cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); + cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, KSMBD_DEFAULT_GFP); if (!cf_name) return ERR_PTR(-ENOMEM); @@ -294,7 +295,7 @@ char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) path_len = share->path_sz; name_len = strlen(name); - new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP); if (!new_name) return new_name; @@ -320,7 +321,7 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, if (!sz) return NULL; - conv = kmalloc(sz, GFP_KERNEL); + conv = kmalloc(sz, KSMBD_DEFAULT_GFP); if (!conv) return NULL; diff --git a/fs/smb/server/ndr.c b/fs/smb/server/ndr.c index 3507d8f890749..58d71560f626b 100644 --- a/fs/smb/server/ndr.c +++ b/fs/smb/server/ndr.c @@ -18,7 +18,7 @@ static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) { char *data; - data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + data = krealloc(n->data, n->offset + sz + 1024, KSMBD_DEFAULT_GFP); if (!data) return -ENOMEM; @@ -174,7 +174,7 @@ int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) n->offset = 0; n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); + n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP); if (!n->data) return -ENOMEM; @@ -350,7 +350,7 @@ int ndr_encode_posix_acl(struct ndr *n, n->offset = 0; n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); + n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP); if (!n->data) return -ENOMEM; @@ -401,7 +401,7 @@ int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) n->offset = 0; n->length = 2048; - n->data = kzalloc(n->length, GFP_KERNEL); + n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP); if (!n->data) return -ENOMEM; @@ -505,7 +505,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) return ret; acl->sd_size = n->length - n->offset; - acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + acl->sd_buf = kzalloc(acl->sd_size, KSMBD_DEFAULT_GFP); if (!acl->sd_buf) return -ENOMEM; diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 4142c7ad5fa91..3a3fe4afbdf0d 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -34,7 +34,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; - opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + opinfo = kzalloc(sizeof(struct oplock_info), KSMBD_DEFAULT_GFP); if (!opinfo) return NULL; @@ -94,7 +94,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) { struct lease *lease; - lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + lease = kmalloc(sizeof(struct lease), KSMBD_DEFAULT_GFP); if (!lease) return -ENOMEM; @@ -709,7 +709,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) if (!work) return -ENOMEM; - br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + br_info = kmalloc(sizeof(struct oplock_break_info), KSMBD_DEFAULT_GFP); if (!br_info) { ksmbd_free_work_struct(work); return -ENOMEM; @@ -812,7 +812,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) if (!work) return -ENOMEM; - br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + br_info = kmalloc(sizeof(struct lease_break_info), KSMBD_DEFAULT_GFP); if (!br_info) { ksmbd_free_work_struct(work); return -ENOMEM; @@ -1057,7 +1057,7 @@ static int add_lease_global_list(struct oplock_info *opinfo) } read_unlock(&lease_list_lock); - lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + lb = kmalloc(sizeof(struct lease_table), KSMBD_DEFAULT_GFP); if (!lb) return -ENOMEM; @@ -1499,7 +1499,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (IS_ERR_OR_NULL(cc)) return NULL; - lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); + lreq = kzalloc(sizeof(struct lease_ctx_info), KSMBD_DEFAULT_GFP); if (!lreq) return NULL; diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index e6cfedba99923..930d7566b52ef 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -47,7 +47,7 @@ static int ___server_conf_set(int idx, char *val) return -EINVAL; kfree(server_conf.conf[idx]); - server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + server_conf.conf[idx] = kstrdup(val, KSMBD_DEFAULT_GFP); if (!server_conf.conf[idx]) return -ENOMEM; return 0; @@ -276,8 +276,12 @@ static void handle_ksmbd_work(struct work_struct *wk) * disconnection. waitqueue_active is safe because it * uses atomic operation for condition. */ + atomic_inc(&conn->refcnt); if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) wake_up(&conn->r_count_q); + + if (atomic_dec_and_test(&conn->refcnt)) + kfree(conn); } /** @@ -415,7 +419,7 @@ static int __queue_ctrl_work(int type) { struct server_ctrl_struct *ctrl; - ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + ctrl = kmalloc(sizeof(struct server_ctrl_struct), KSMBD_DEFAULT_GFP); if (!ctrl) return -ENOMEM; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 599118aed2053..416f7df4edef5 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -551,7 +551,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (le32_to_cpu(hdr->NextCommand) > 0) sz = large_sz; - work->response_buf = kvzalloc(sz, GFP_KERNEL); + work->response_buf = kvzalloc(sz, KSMBD_DEFAULT_GFP); if (!work->response_buf) return -ENOMEM; @@ -1147,7 +1147,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) case SMB311_PROT_ID: conn->preauth_info = kzalloc(sizeof(struct preauth_integrity_info), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!conn->preauth_info) { rc = -ENOMEM; rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -1266,7 +1266,7 @@ static int alloc_preauth_hash(struct ksmbd_session *sess, return 0; sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP); if (!sess->Preauth_HashValue) return -ENOMEM; @@ -1352,7 +1352,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, sz = sizeof(struct challenge_message); sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; - neg_blob = kzalloc(sz, GFP_KERNEL); + neg_blob = kzalloc(sz, KSMBD_DEFAULT_GFP); if (!neg_blob) return -ENOMEM; @@ -1543,12 +1543,12 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (conn->dialect >= SMB30_PROT_ID) { chann = lookup_chann_list(sess, conn); if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP); if (!chann) return -ENOMEM; chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, KSMBD_DEFAULT_GFP); } } @@ -1624,12 +1624,12 @@ static int krb5_authenticate(struct ksmbd_work *work, if (conn->dialect >= SMB30_PROT_ID) { chann = lookup_chann_list(sess, conn); if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP); if (!chann) return -ENOMEM; chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, KSMBD_DEFAULT_GFP); } } @@ -1666,7 +1666,7 @@ int smb2_sess_setup(struct ksmbd_work *work) unsigned int negblob_len, negblob_off; int rc = 0; - ksmbd_debug(SMB, "Received request for session setup\n"); + ksmbd_debug(SMB, "Received smb2 session setup request\n"); WORK_BUFFERS(work, req, rsp); @@ -1940,6 +1940,8 @@ int smb2_tree_connect(struct ksmbd_work *work) struct ksmbd_share_config *share = NULL; int rc = -EINVAL; + ksmbd_debug(SMB, "Received smb2 tree connect request\n"); + WORK_BUFFERS(work, req, rsp); treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset), @@ -2136,9 +2138,9 @@ int smb2_tree_disconnect(struct ksmbd_work *work) struct ksmbd_tree_connect *tcon = work->tcon; int err; - WORK_BUFFERS(work, req, rsp); + ksmbd_debug(SMB, "Received smb2 tree disconnect request\n"); - ksmbd_debug(SMB, "request\n"); + WORK_BUFFERS(work, req, rsp); if (!tcon) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); @@ -2203,7 +2205,7 @@ int smb2_session_logoff(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "request\n"); + ksmbd_debug(SMB, "Received smb2 session logoff request\n"); ksmbd_conn_lock(conn); if (!ksmbd_conn_good(conn)) { @@ -2346,7 +2348,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, le16_to_cpu(eabuf->EaValueLength)) return -EINVAL; - attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + attr_name = kmalloc(XATTR_NAME_MAX + 1, KSMBD_DEFAULT_GFP); if (!attr_name) return -ENOMEM; @@ -2849,6 +2851,8 @@ int smb2_open(struct ksmbd_work *work) __le32 daccess, maximal_access = 0; int iov_len = 0; + ksmbd_debug(SMB, "Received smb2 create request\n"); + WORK_BUFFERS(work, req, rsp); if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && @@ -2897,7 +2901,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out2; } } else { - name = kstrdup("", GFP_KERNEL); + name = kstrdup("", KSMBD_DEFAULT_GFP); if (!name) { rc = -ENOMEM; goto err_out2; @@ -3338,7 +3342,7 @@ int smb2_open(struct ksmbd_work *work) sizeof(struct smb_sid) * 3 + sizeof(struct smb_acl) + sizeof(struct smb_ace) * ace_num * 2, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!pntsd) { posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); @@ -4296,6 +4300,8 @@ int smb2_query_dir(struct ksmbd_work *work) int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; + ksmbd_debug(SMB, "Received smb2 query directory request\n"); + WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { @@ -4946,7 +4952,7 @@ static int get_file_stream_info(struct ksmbd_work *work, /* plus : size */ streamlen += 1; - stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + stream_buf = kmalloc(streamlen + 1, KSMBD_DEFAULT_GFP); if (!stream_buf) break; @@ -5602,9 +5608,9 @@ int smb2_query_info(struct ksmbd_work *work) struct smb2_query_info_rsp *rsp; int rc = 0; - WORK_BUFFERS(work, req, rsp); + ksmbd_debug(SMB, "Received request smb2 query info request\n"); - ksmbd_debug(SMB, "GOT query info request\n"); + WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { rc = -ENOMEM; @@ -5709,6 +5715,8 @@ int smb2_close(struct ksmbd_work *work) u64 time; int err = 0; + ksmbd_debug(SMB, "Received smb2 close request\n"); + WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -5825,6 +5833,8 @@ int smb2_echo(struct ksmbd_work *work) { struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + ksmbd_debug(SMB, "Received smb2 echo request\n"); + if (work->next_smb2_rcv_hdr_off) rsp = ksmbd_resp_buf_next(work); @@ -5921,7 +5931,7 @@ static int smb2_create_link(struct ksmbd_work *work, return -EINVAL; ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); if (!pathname) return -ENOMEM; @@ -6365,7 +6375,7 @@ int smb2_set_info(struct ksmbd_work *work) int rc = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - ksmbd_debug(SMB, "Received set info request\n"); + ksmbd_debug(SMB, "Received smb2 set info request\n"); if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); @@ -6485,7 +6495,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) } aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); + kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; @@ -6591,6 +6601,8 @@ int smb2_read(struct ksmbd_work *work) unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; void *aux_payload_buf; + ksmbd_debug(SMB, "Received smb2 read request\n"); + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe read request\n"); @@ -6664,7 +6676,7 @@ int smb2_read(struct ksmbd_work *work) ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", fp->filp, offset, length); - aux_payload_buf = kvzalloc(length, GFP_KERNEL); + aux_payload_buf = kvzalloc(length, KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; @@ -6816,7 +6828,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, int ret; ssize_t nbytes; - data_buf = kvzalloc(length, GFP_KERNEL); + data_buf = kvzalloc(length, KSMBD_DEFAULT_GFP); if (!data_buf) return -ENOMEM; @@ -6856,6 +6868,8 @@ int smb2_write(struct ksmbd_work *work) int err = 0; unsigned int max_write_size = work->conn->vals->max_write_size; + ksmbd_debug(SMB, "Received smb2 write request\n"); + WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { @@ -6994,7 +7008,7 @@ int smb2_flush(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); + ksmbd_debug(SMB, "Received smb2 flush request(fid : %llu)\n", req->VolatileFileId); err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); if (err) @@ -7145,7 +7159,7 @@ static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, { struct ksmbd_lock *lock; - lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + lock = kzalloc(sizeof(struct ksmbd_lock), KSMBD_DEFAULT_GFP); if (!lock) return NULL; @@ -7206,7 +7220,7 @@ int smb2_lock(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "Received lock request\n"); + ksmbd_debug(SMB, "Received smb2 lock request\n"); fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); @@ -7413,7 +7427,7 @@ int smb2_lock(struct ksmbd_work *work) "would have to wait for getting lock\n"); list_add(&smb_lock->llist, &rollback_list); - argv = kmalloc(sizeof(void *), GFP_KERNEL); + argv = kmalloc(sizeof(void *), KSMBD_DEFAULT_GFP); if (!argv) { err = -ENOMEM; goto out; @@ -7973,6 +7987,8 @@ int smb2_ioctl(struct ksmbd_work *work) int ret = 0; char *buffer; + ksmbd_debug(SMB, "Received smb2 ioctl request\n"); + if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); @@ -8599,6 +8615,8 @@ int smb2_oplock_break(struct ksmbd_work *work) struct smb2_oplock_break *req; struct smb2_oplock_break *rsp; + ksmbd_debug(SMB, "Received smb2 oplock break acknowledgment request\n"); + WORK_BUFFERS(work, req, rsp); switch (le16_to_cpu(req->StructureSize)) { @@ -8629,6 +8647,8 @@ int smb2_notify(struct ksmbd_work *work) struct smb2_change_notify_req *req; struct smb2_change_notify_rsp *rsp; + ksmbd_debug(SMB, "Received smb2 notify\n"); + WORK_BUFFERS(work, req, rsp); if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { @@ -8907,7 +8927,7 @@ int smb3_encrypt_resp(struct ksmbd_work *work) int rc = -ENOMEM; void *tr_buf; - tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, KSMBD_DEFAULT_GFP); if (!tr_buf) return rc; diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index 75b4eb856d32f..4e6f169fcf83b 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -358,7 +358,7 @@ static int smb1_check_user_session(struct ksmbd_work *work) static int smb1_allocate_rsp_buf(struct ksmbd_work *work) { work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; if (!work->response_buf) { @@ -576,7 +576,7 @@ static int smb_handle_negotiate(struct ksmbd_work *work) ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); - if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp, + if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp + 4, sizeof(struct smb_negotiate_rsp) - 4)) return -ENOMEM; diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 1c9775f1efa56..d39d3e553366d 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -345,10 +345,10 @@ int init_acl_state(struct posix_acl_state *state, int cnt) */ alloc = sizeof(struct posix_ace_state_array) + cnt * sizeof(struct posix_user_ace_state); - state->users = kzalloc(alloc, GFP_KERNEL); + state->users = kzalloc(alloc, KSMBD_DEFAULT_GFP); if (!state->users) return -ENOMEM; - state->groups = kzalloc(alloc, GFP_KERNEL); + state->groups = kzalloc(alloc, KSMBD_DEFAULT_GFP); if (!state->groups) { kfree(state->users); return -ENOMEM; @@ -410,7 +410,7 @@ static void parse_dacl(struct mnt_idmap *idmap, return; } - ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), KSMBD_DEFAULT_GFP); if (!ppace) { free_acl_state(&default_acl_state); free_acl_state(&acl_state); @@ -553,7 +553,7 @@ static void parse_dacl(struct mnt_idmap *idmap, if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { fattr->cf_acls = posix_acl_alloc(acl_state.users->n + - acl_state.groups->n + 4, GFP_KERNEL); + acl_state.groups->n + 4, KSMBD_DEFAULT_GFP); if (fattr->cf_acls) { cf_pace = fattr->cf_acls->a_entries; posix_state_to_acl(&acl_state, cf_pace); @@ -567,7 +567,7 @@ static void parse_dacl(struct mnt_idmap *idmap, if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { fattr->cf_dacls = posix_acl_alloc(default_acl_state.users->n + - default_acl_state.groups->n + 4, GFP_KERNEL); + default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP); if (fattr->cf_dacls) { cf_pdace = fattr->cf_dacls->a_entries; posix_state_to_acl(&default_acl_state, cf_pdace); @@ -595,7 +595,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { int flags = 0; - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + sid = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!sid) break; @@ -662,7 +662,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, pace = fattr->cf_dacls->a_entries; for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + sid = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!sid) break; @@ -906,7 +906,7 @@ int build_sec_desc(struct mnt_idmap *idmap, gid_t gid; unsigned int sid_type = SIDOWNER; - nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!nowner_sid_ptr) return -ENOMEM; @@ -915,7 +915,7 @@ int build_sec_desc(struct mnt_idmap *idmap, sid_type = SIDUNIX_USER; id_to_sid(uid, sid_type, nowner_sid_ptr); - ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!ngroup_sid_ptr) { kfree(nowner_sid_ptr); return -ENOMEM; @@ -1032,7 +1032,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, goto free_parent_pntsd; } - aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, + KSMBD_DEFAULT_GFP); if (!aces_base) { rc = -ENOMEM; goto free_parent_pntsd; @@ -1126,7 +1127,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, pntsd_alloc_size = sizeof(struct smb_ntsd) + powner_sid_size + pgroup_sid_size + sizeof(struct smb_acl) + nt_size; - pntsd = kzalloc(pntsd_alloc_size, GFP_KERNEL); + pntsd = kzalloc(pntsd_alloc_size, KSMBD_DEFAULT_GFP); if (!pntsd) { rc = -ENOMEM; goto free_aces_base; diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c index 2f27afb695f62..48cda3350e5a2 100644 --- a/fs/smb/server/transport_ipc.c +++ b/fs/smb/server/transport_ipc.c @@ -244,7 +244,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) struct ksmbd_ipc_msg *msg; size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); - msg = kvzalloc(msg_sz, GFP_KERNEL); + msg = kvzalloc(msg_sz, KSMBD_DEFAULT_GFP); if (msg) msg->sz = sz; return msg; @@ -283,7 +283,7 @@ static int handle_response(int type, void *payload, size_t sz) entry->type + 1, type); } - entry->response = kvzalloc(sz, GFP_KERNEL); + entry->response = kvzalloc(sz, KSMBD_DEFAULT_GFP); if (!entry->response) { ret = -ENOMEM; break; @@ -444,7 +444,7 @@ static int ipc_msg_send(struct ksmbd_ipc_msg *msg) if (!ksmbd_tools_pid) return ret; - skb = genlmsg_new(msg->sz, GFP_KERNEL); + skb = genlmsg_new(msg->sz, KSMBD_DEFAULT_GFP); if (!skb) return -ENOMEM; diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index 17c76713c6d08..0ef3c9f0bfebd 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -362,7 +362,7 @@ static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) struct smb_direct_transport *t; struct ksmbd_conn *conn; - t = kzalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc(sizeof(*t), KSMBD_DEFAULT_GFP); if (!t) return NULL; @@ -462,7 +462,7 @@ static struct smb_direct_sendmsg { struct smb_direct_sendmsg *msg; - msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + msg = mempool_alloc(t->sendmsg_mempool, KSMBD_DEFAULT_GFP); if (!msg) return ERR_PTR(-ENOMEM); msg->transport = t; @@ -1406,7 +1406,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, desc_buf = buf; for (i = 0; i < desc_num; i++) { msg = kzalloc(struct_size(msg, sg_list, SG_CHUNK_SIZE), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!msg) { ret = -ENOMEM; goto out; @@ -1852,7 +1852,7 @@ static int smb_direct_create_pools(struct smb_direct_transport *t) INIT_LIST_HEAD(&t->recvmsg_queue); for (i = 0; i < t->recv_credit_max; i++) { - recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + recvmsg = mempool_alloc(t->recvmsg_mempool, KSMBD_DEFAULT_GFP); if (!recvmsg) goto err; recvmsg->transport = t; @@ -2144,7 +2144,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) if (!rdma_frwr_is_supported(&ib_dev->attrs)) return 0; - smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); + smb_dev = kzalloc(sizeof(*smb_dev), KSMBD_DEFAULT_GFP); if (!smb_dev) return -ENOMEM; smb_dev->ib_dev = ib_dev; @@ -2289,6 +2289,9 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev) } } + ksmbd_debug(RDMA, "netdev(%s) rdma capable : %s\n", + netdev->name, rdma_capable ? "true" : "false"); + return rdma_capable; } diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index aaed9e293b2e0..0d9007285e30b 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -76,7 +76,7 @@ static struct tcp_transport *alloc_transport(struct socket *client_sk) struct tcp_transport *t; struct ksmbd_conn *conn; - t = kzalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc(sizeof(*t), KSMBD_DEFAULT_GFP); if (!t) return NULL; t->sock = client_sk; @@ -151,7 +151,7 @@ static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs return t->iov; /* not big enough -- allocate a new one and release the old */ - new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), KSMBD_DEFAULT_GFP); if (new_iov) { kfree(t->iov); t->iov = new_iov; @@ -521,6 +521,8 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, found = 1; if (iface->state != IFACE_STATE_DOWN) break; + ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n", + iface->name); ret = create_socket(iface); if (ret) return NOTIFY_OK; @@ -528,9 +530,11 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, } } if (!found && bind_additional_ifaces) { - iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP)); if (!iface) return NOTIFY_OK; + ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n", + iface->name); ret = create_socket(iface); if (ret) break; @@ -540,6 +544,8 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, list_for_each_entry(iface, &iface_list, entry) { if (!strcmp(iface->name, netdev->name) && iface->state == IFACE_STATE_CONFIGURED) { + ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n", + iface->name); tcp_stop_kthread(iface->ksmbd_kthread); iface->ksmbd_kthread = NULL; mutex_lock(&iface->sock_release_lock); @@ -600,7 +606,7 @@ static struct interface *alloc_iface(char *ifname) if (!ifname) return NULL; - iface = kzalloc(sizeof(struct interface), GFP_KERNEL); + iface = kzalloc(sizeof(struct interface), KSMBD_DEFAULT_GFP); if (!iface) { kfree(ifname); return NULL; @@ -624,7 +630,7 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) for_each_netdev(&init_net, netdev) { if (netif_is_bridge_port(netdev)) continue; - if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) { + if (!alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP))) { rtnl_unlock(); return -ENOMEM; } @@ -635,7 +641,7 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) } while (ifc_list_sz > 0) { - if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + if (!alloc_iface(kstrdup(ifc_list, KSMBD_DEFAULT_GFP))) return -ENOMEM; sz = strlen(ifc_list); diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c index 217106ff7b828..85e6791745ec8 100644 --- a/fs/smb/server/unicode.c +++ b/fs/smb/server/unicode.c @@ -297,7 +297,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, if (is_unicode) { len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); len += nls_nullsize(codepage); - dst = kmalloc(len, GFP_KERNEL); + dst = kmalloc(len, KSMBD_DEFAULT_GFP); if (!dst) return ERR_PTR(-ENOMEM); ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, @@ -309,7 +309,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, } else { len = strnlen(src, maxlen); len++; - dst = kmalloc(len, GFP_KERNEL); + dst = kmalloc(len, KSMBD_DEFAULT_GFP); if (!dst) return ERR_PTR(-ENOMEM); strscpy(dst, src, len); diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 7cbd580120d12..88d167a5f8b7b 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -444,7 +444,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, } if (v_len < size) { - wbuf = kvzalloc(size, GFP_KERNEL); + wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP); if (!wbuf) { err = -ENOMEM; goto out; @@ -865,7 +865,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) if (size <= 0) return size; - vlist = kvzalloc(size, GFP_KERNEL); + vlist = kvzalloc(size, KSMBD_DEFAULT_GFP); if (!vlist) return -ENOMEM; @@ -907,7 +907,7 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap, if (xattr_len < 0) return xattr_len; - buf = kmalloc(xattr_len + 1, GFP_KERNEL); + buf = kmalloc(xattr_len + 1, KSMBD_DEFAULT_GFP); if (!buf) return -ENOMEM; @@ -1411,7 +1411,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct mnt_idmap *id smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + sizeof(struct xattr_acl_entry) * posix_acls->a_count, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!smb_acl) goto out; @@ -1767,7 +1767,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, else type = ":$DATA"; - buf = kasprintf(GFP_KERNEL, "%s%s%s", + buf = kasprintf(KSMBD_DEFAULT_GFP, "%s%s%s", XATTR_NAME_STREAM, stream_name, type); if (!buf) return -ENOMEM; @@ -1896,7 +1896,7 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap, acl_state.group.allow; acl_state.mask.allow = 0x07; - acls = posix_acl_alloc(6, GFP_KERNEL); + acls = posix_acl_alloc(6, KSMBD_DEFAULT_GFP); if (!acls) { free_acl_state(&acl_state); return -ENOMEM; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index a19f4e563c7e5..8d1f30dcba7e8 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -188,7 +188,7 @@ static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) if (ci) return ci; - ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + ci = kmalloc(sizeof(struct ksmbd_inode), KSMBD_DEFAULT_GFP); if (!ci) return NULL; @@ -577,7 +577,7 @@ static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, return -EMFILE; } - idr_preload(GFP_KERNEL); + idr_preload(KSMBD_DEFAULT_GFP); write_lock(&ft->lock); ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); if (ret >= 0) { @@ -605,7 +605,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) struct ksmbd_file *fp; int ret; - fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); + fp = kmem_cache_zalloc(filp_cache, KSMBD_DEFAULT_GFP); if (!fp) { pr_err("Failed to allocate memory\n"); return ERR_PTR(-ENOMEM); @@ -923,7 +923,7 @@ int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, char *pathname, *ab_pathname; int ret = 0; - pathname = kmalloc(PATH_MAX, GFP_KERNEL); + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); if (!pathname) return -EACCES; @@ -983,7 +983,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) int ksmbd_init_file_table(struct ksmbd_file_table *ft) { - ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); + ft->idr = kzalloc(sizeof(struct idr), KSMBD_DEFAULT_GFP); if (!ft->idr) return -ENOMEM; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index d1995e2d6c943..785408861c01c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -91,9 +91,12 @@ static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, count = size - pos; } - if (!battr->read) + if (!battr->read && !battr->read_new) return -EIO; + if (battr->read_new) + return battr->read_new(of->file, kobj, battr, buf, pos, count); + return battr->read(of->file, kobj, battr, buf, pos, count); } @@ -152,9 +155,12 @@ static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf, if (!count) return 0; - if (!battr->write) + if (!battr->write && !battr->write_new) return -EIO; + if (battr->write_new) + return battr->write_new(of->file, kobj, battr, buf, pos, count); + return battr->write(of->file, kobj, battr, buf, pos, count); } @@ -315,7 +321,7 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, } int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, - const struct bin_attribute *battr, umode_t mode, + const struct bin_attribute *battr, umode_t mode, size_t size, kuid_t uid, kgid_t gid, const void *ns) { const struct attribute *attr = &battr->attr; @@ -323,13 +329,19 @@ int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, const struct kernfs_ops *ops; struct kernfs_node *kn; + if (battr->read && battr->read_new) + return -EINVAL; + + if (battr->write && battr->write_new) + return -EINVAL; + if (battr->mmap) ops = &sysfs_bin_kfops_mmap; - else if (battr->read && battr->write) + else if ((battr->read || battr->read_new) && (battr->write || battr->write_new)) ops = &sysfs_bin_kfops_rw; - else if (battr->read) + else if (battr->read || battr->read_new) ops = &sysfs_bin_kfops_ro; - else if (battr->write) + else if (battr->write || battr->write_new) ops = &sysfs_bin_kfops_wo; else ops = &sysfs_file_kfops_empty; @@ -340,7 +352,7 @@ int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, #endif kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid, - battr->size, ops, (void *)attr, ns, key); + size, ops, (void *)attr, ns, key); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(parent, attr->name); @@ -580,8 +592,8 @@ int sysfs_create_bin_file(struct kobject *kobj, return -EINVAL; kobject_get_ownership(kobj, &uid, &gid); - return sysfs_add_bin_file_mode_ns(kobj->sd, attr, attr->attr.mode, uid, - gid, NULL); + return sysfs_add_bin_file_mode_ns(kobj->sd, attr, attr->attr.mode, + attr->size, uid, gid, NULL); } EXPORT_SYMBOL_GPL(sysfs_create_bin_file); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index d22ad67a0f329..8b01a7eda5fb3 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -87,6 +87,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, if (grp->bin_attrs) { for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) { umode_t mode = (*bin_attr)->attr.mode; + size_t size = (*bin_attr)->size; if (update) kernfs_remove_by_name(parent, @@ -97,6 +98,8 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, if (!mode) continue; } + if (grp->bin_size) + size = grp->bin_size(kobj, *bin_attr, i); WARN(mode & ~(SYSFS_PREALLOC | 0664), "Attribute %s: Invalid permissions 0%o\n", @@ -104,7 +107,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, mode &= SYSFS_PREALLOC | 0664; error = sysfs_add_bin_file_mode_ns(parent, *bin_attr, - mode, uid, gid, + mode, size, uid, gid, NULL); if (error) break; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 3f28c9af57562..8e012f25e1c06 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -31,7 +31,7 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, umode_t amode, kuid_t uid, kgid_t gid, const void *ns); int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, - const struct bin_attribute *battr, umode_t mode, + const struct bin_attribute *battr, umode_t mode, size_t size, kuid_t uid, kgid_t gid, const void *ns); /* diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index d79cabe193c34..2c99349cf5375 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -213,12 +213,6 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { - case FS_IOC32_GETFLAGS: - cmd = FS_IOC_GETFLAGS; - break; - case FS_IOC32_SETFLAGS: - cmd = FS_IOC_SETFLAGS; - break; case FS_IOC_SET_ENCRYPTION_POLICY: case FS_IOC_GET_ENCRYPTION_POLICY: case FS_IOC_GET_ENCRYPTION_POLICY_EX: diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 4a35f9e8f6681..36ba79fbd2ff8 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -981,6 +981,13 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode) dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink); + if (kill_xattrs && ui->xattr_cnt > ubifs_xattr_max_cnt(c)) { + ubifs_err(c, "Cannot delete inode, it has too much xattrs!"); + err = -EPERM; + ubifs_ro_mode(c, err); + return err; + } + /* * If the inode is being deleted, do not write the attached data. No * need to synchronize the write-buffer either. @@ -1012,12 +1019,6 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode) struct inode *xino; struct ubifs_dent_node *xent, *pxent = NULL; - if (ui->xattr_cnt > ubifs_xattr_max_cnt(c)) { - err = -EPERM; - ubifs_err(c, "Cannot delete inode, it has too much xattrs!"); - goto out_release; - } - lowest_xent_key(c, &key, inode->i_ino); while (1) { xent = ubifs_tnc_next_ent(c, &key, &nm); diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index 07351fdce7223..aa8837e6247cf 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -577,7 +577,7 @@ static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c, /* Go right */ nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return (void *)nnode; + return ERR_CAST(nnode); /* Go down to level 1 */ while (nnode->level > 1) { @@ -594,7 +594,7 @@ static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c, } nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return (void *)nnode; + return ERR_CAST(nnode); } for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++) diff --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c index fb957d963ba6c..5555dd7408894 100644 --- a/fs/ubifs/orphan.c +++ b/fs/ubifs/orphan.c @@ -76,7 +76,7 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum) else if (inum > o->inum) p = &(*p)->rb_right; else { - ubifs_err(c, "orphaned twice"); + ubifs_err(c, "ino %lu orphaned twice", (unsigned long)inum); spin_unlock(&c->orphan_lock); kfree(orphan); return -EINVAL; diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 3fb308b6e1679..f3e3b20686085 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -773,10 +773,10 @@ static void init_constants_master(struct ubifs_info *c) * necessary to report something for the 'statfs()' call. * * Subtract the LEB reserved for GC, the LEB which is reserved for - * deletions, minimum LEBs for the index, and assume only one journal - * head is available. + * deletions, minimum LEBs for the index, the LEBs which are reserved + * for each journal head. */ - tmp64 = c->main_lebs - 1 - 1 - MIN_INDEX_LEBS - c->jhead_cnt + 1; + tmp64 = c->main_lebs - 1 - 1 - MIN_INDEX_LEBS - c->jhead_cnt; tmp64 *= (long long)c->leb_size - c->leb_overhead; tmp64 = ubifs_reported_space(c, tmp64); c->block_cnt = tmp64 >> UBIFS_BLOCK_SHIFT; @@ -2206,6 +2206,8 @@ static int ubifs_fill_super(struct super_block *sb, struct fs_context *fc) } super_set_uuid(sb, c->uuid, sizeof(c->uuid)); + super_set_sysfs_name_generic(sb, UBIFS_DFS_DIR_NAME, + c->vi.ubi_num, c->vi.vol_id); mutex_unlock(&c->umount_mutex); return 0; diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 45cacdcd4746b..33946b5181481 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -2930,8 +2930,6 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) dbg_tnc("xent '%s', ino %lu", xent->name, (unsigned long)xattr_inum); - ubifs_evict_xattr_inode(c, xattr_inum); - fname_name(&nm) = xent->name; fname_len(&nm) = le16_to_cpu(xent->nlen); err = ubifs_tnc_remove_nm(c, &key1, &nm); diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c index a55e04822d16e..7c43e0ccf6d47 100644 --- a/fs/ubifs/tnc_commit.c +++ b/fs/ubifs/tnc_commit.c @@ -657,6 +657,8 @@ static int get_znodes_to_commit(struct ubifs_info *c) znode->alt = 0; cnext = find_next_dirty(znode); if (!cnext) { + ubifs_assert(c, !znode->parent); + znode->cparent = NULL; znode->cnext = c->cnext; break; } diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index d69a5a42d6936..3375bbe0508c7 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -2040,13 +2040,10 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf, #ifdef CONFIG_UBIFS_FS_XATTR extern const struct xattr_handler * const ubifs_xattr_handlers[]; ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size); -void ubifs_evict_xattr_inode(struct ubifs_info *c, ino_t xattr_inum); int ubifs_purge_xattrs(struct inode *host); #else #define ubifs_listxattr NULL #define ubifs_xattr_handlers NULL -static inline void ubifs_evict_xattr_inode(struct ubifs_info *c, - ino_t xattr_inum) { } static inline int ubifs_purge_xattrs(struct inode *host) { return 0; diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index f734588b224a3..c21a0c2b3e907 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -48,19 +48,6 @@ #include #include -/* - * Extended attribute type constants. - * - * USER_XATTR: user extended attribute ("user.*") - * TRUSTED_XATTR: trusted extended attribute ("trusted.*) - * SECURITY_XATTR: security extended attribute ("security.*") - */ -enum { - USER_XATTR, - TRUSTED_XATTR, - SECURITY_XATTR, -}; - static const struct inode_operations empty_iops; static const struct file_operations empty_fops; @@ -532,8 +519,6 @@ int ubifs_purge_xattrs(struct inode *host) ubifs_err(c, "dead directory entry '%s', error %d", xent->name, err); ubifs_ro_mode(c, err); - kfree(pxent); - kfree(xent); goto out_err; } @@ -541,16 +526,12 @@ int ubifs_purge_xattrs(struct inode *host) clear_nlink(xino); err = remove_xattr(c, host, xino, &nm); + iput(xino); if (err) { - kfree(pxent); - kfree(xent); - iput(xino); ubifs_err(c, "cannot remove xattr, error %d", err); goto out_err; } - iput(xino); - kfree(pxent); pxent = xent; key_read(c, &xent->key, &key); @@ -566,32 +547,12 @@ int ubifs_purge_xattrs(struct inode *host) return 0; out_err: + kfree(pxent); + kfree(xent); up_write(&ubifs_inode(host)->xattr_sem); return err; } -/** - * ubifs_evict_xattr_inode - Evict an xattr inode. - * @c: UBIFS file-system description object - * @xattr_inum: xattr inode number - * - * When an inode that hosts xattrs is being removed we have to make sure - * that cached inodes of the xattrs also get removed from the inode cache - * otherwise we'd waste memory. This function looks up an inode from the - * inode cache and clears the link counter such that iput() will evict - * the inode. - */ -void ubifs_evict_xattr_inode(struct ubifs_info *c, ino_t xattr_inum) -{ - struct inode *inode; - - inode = ilookup(c->vfs_sb, xattr_inum); - if (inode) { - clear_nlink(inode); - iput(inode); - } -} - static int ubifs_xattr_remove(struct inode *host, const char *name) { struct inode *inode; diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h index 9b373d172a776..699c1a37b8e78 100644 --- a/include/acpi/pcc.h +++ b/include/acpi/pcc.h @@ -12,6 +12,7 @@ struct pcc_mbox_chan { struct mbox_chan *mchan; u64 shmem_base_addr; + void __iomem *shmem; u64 shmem_size; u32 latency; u32 max_access_rate; @@ -31,11 +32,13 @@ struct pcc_mbox_chan { #define PCC_CMD_COMPLETION_NOTIFY BIT(0) #define MAX_PCC_SUBSPACES 256 +#define PCC_ACK_FLAG_MASK 0x1 #ifdef CONFIG_PCC extern struct pcc_mbox_chan * pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id); extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan); +extern int pcc_mbox_ioremap(struct mbox_chan *chan); #else static inline struct pcc_mbox_chan * pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id) @@ -43,6 +46,10 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id) return ERR_PTR(-ENODEV); } static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { } +static inline int pcc_mbox_ioremap(struct mbox_chan *chan) +{ + return 0; +}; #endif #endif /* _PCC_H */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 565341c826e38..f3249b7df5cbc 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -15,7 +15,7 @@ /* ACPICA external files should not include ACPICA headers directly. */ #if !defined(BUILDING_ACPICA) && !defined(_LINUX_ACPI_H) -#error "Please don't include directly, include instead." +#error "Please do not include directly, include instead." #endif #endif diff --git a/include/asm-generic/delay.h b/include/asm-generic/delay.h index 76cf237b6e4cb..03b0ec7afca6f 100644 --- a/include/asm-generic/delay.h +++ b/include/asm-generic/delay.h @@ -75,11 +75,11 @@ static __always_inline void ndelay(unsigned long nsec) { if (__builtin_constant_p(nsec)) { if (nsec >= DELAY_CONST_MAX) - __bad_udelay(); + __bad_ndelay(); else __const_udelay(nsec * NDELAY_CONST_MULT); } else { - __udelay(nsec); + __ndelay(nsec); } } #define ndelay(x) ndelay(x) diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h index 0655aa5b57b29..bf47cca2c375d 100644 --- a/include/asm-generic/qspinlock.h +++ b/include/asm-generic/qspinlock.h @@ -136,6 +136,7 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock) } #endif +#ifndef __no_arch_spinlock_redefine /* * Remapping spinlock architecture specific functions to the corresponding * queued spinlock functions. @@ -146,5 +147,6 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock) #define arch_spin_lock(l) queued_spin_lock(l) #define arch_spin_trylock(l) queued_spin_trylock(l) #define arch_spin_unlock(l) queued_spin_unlock(l) +#endif #endif /* __ASM_GENERIC_QSPINLOCK_H */ diff --git a/include/asm-generic/spinlock.h b/include/asm-generic/spinlock.h index 90803a826ba03..970590baf61b6 100644 --- a/include/asm-generic/spinlock.h +++ b/include/asm-generic/spinlock.h @@ -1,94 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * 'Generic' ticket-lock implementation. - * - * It relies on atomic_fetch_add() having well defined forward progress - * guarantees under contention. If your architecture cannot provide this, stick - * to a test-and-set lock. - * - * It also relies on atomic_fetch_add() being safe vs smp_store_release() on a - * sub-word of the value. This is generally true for anything LL/SC although - * you'd be hard pressed to find anything useful in architecture specifications - * about this. If your architecture cannot do this you might be better off with - * a test-and-set. - * - * It further assumes atomic_*_release() + atomic_*_acquire() is RCpc and hence - * uses atomic_fetch_add() which is RCsc to create an RCsc hot path, along with - * a full fence after the spin to upgrade the otherwise-RCpc - * atomic_cond_read_acquire(). - * - * The implementation uses smp_cond_load_acquire() to spin, so if the - * architecture has WFE like instructions to sleep instead of poll for word - * modifications be sure to implement that (see ARM64 for example). - * - */ - #ifndef __ASM_GENERIC_SPINLOCK_H #define __ASM_GENERIC_SPINLOCK_H -#include -#include - -static __always_inline void arch_spin_lock(arch_spinlock_t *lock) -{ - u32 val = atomic_fetch_add(1<<16, lock); - u16 ticket = val >> 16; - - if (ticket == (u16)val) - return; - - /* - * atomic_cond_read_acquire() is RCpc, but rather than defining a - * custom cond_read_rcsc() here we just emit a full fence. We only - * need the prior reads before subsequent writes ordering from - * smb_mb(), but as atomic_cond_read_acquire() just emits reads and we - * have no outstanding writes due to the atomic_fetch_add() the extra - * orderings are free. - */ - atomic_cond_read_acquire(lock, ticket == (u16)VAL); - smp_mb(); -} - -static __always_inline bool arch_spin_trylock(arch_spinlock_t *lock) -{ - u32 old = atomic_read(lock); - - if ((old >> 16) != (old & 0xffff)) - return false; - - return atomic_try_cmpxchg(lock, &old, old + (1<<16)); /* SC, for RCsc */ -} - -static __always_inline void arch_spin_unlock(arch_spinlock_t *lock) -{ - u16 *ptr = (u16 *)lock + IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); - u32 val = atomic_read(lock); - - smp_store_release(ptr, (u16)val + 1); -} - -static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock) -{ - u32 val = lock.counter; - - return ((val >> 16) == (val & 0xffff)); -} - -static __always_inline int arch_spin_is_locked(arch_spinlock_t *lock) -{ - arch_spinlock_t val = READ_ONCE(*lock); - - return !arch_spin_value_unlocked(val); -} - -static __always_inline int arch_spin_is_contended(arch_spinlock_t *lock) -{ - u32 val = atomic_read(lock); - - return (s16)((val >> 16) - (val & 0xffff)) > 1; -} - +#include #include #endif /* __ASM_GENERIC_SPINLOCK_H */ diff --git a/include/asm-generic/spinlock_types.h b/include/asm-generic/spinlock_types.h index 8962bb730945d..f534aa5de3947 100644 --- a/include/asm-generic/spinlock_types.h +++ b/include/asm-generic/spinlock_types.h @@ -3,15 +3,7 @@ #ifndef __ASM_GENERIC_SPINLOCK_TYPES_H #define __ASM_GENERIC_SPINLOCK_TYPES_H -#include -typedef atomic_t arch_spinlock_t; - -/* - * qrwlock_types depends on arch_spinlock_t, so we must typedef that before the - * include. - */ -#include - -#define __ARCH_SPIN_LOCK_UNLOCKED ATOMIC_INIT(0) +#include +#include #endif /* __ASM_GENERIC_SPINLOCK_TYPES_H */ diff --git a/include/asm-generic/ticket_spinlock.h b/include/asm-generic/ticket_spinlock.h new file mode 100644 index 0000000000000..325779970d8a0 --- /dev/null +++ b/include/asm-generic/ticket_spinlock.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * 'Generic' ticket-lock implementation. + * + * It relies on atomic_fetch_add() having well defined forward progress + * guarantees under contention. If your architecture cannot provide this, stick + * to a test-and-set lock. + * + * It also relies on atomic_fetch_add() being safe vs smp_store_release() on a + * sub-word of the value. This is generally true for anything LL/SC although + * you'd be hard pressed to find anything useful in architecture specifications + * about this. If your architecture cannot do this you might be better off with + * a test-and-set. + * + * It further assumes atomic_*_release() + atomic_*_acquire() is RCpc and hence + * uses atomic_fetch_add() which is RCsc to create an RCsc hot path, along with + * a full fence after the spin to upgrade the otherwise-RCpc + * atomic_cond_read_acquire(). + * + * The implementation uses smp_cond_load_acquire() to spin, so if the + * architecture has WFE like instructions to sleep instead of poll for word + * modifications be sure to implement that (see ARM64 for example). + * + */ + +#ifndef __ASM_GENERIC_TICKET_SPINLOCK_H +#define __ASM_GENERIC_TICKET_SPINLOCK_H + +#include +#include + +static __always_inline void ticket_spin_lock(arch_spinlock_t *lock) +{ + u32 val = atomic_fetch_add(1<<16, &lock->val); + u16 ticket = val >> 16; + + if (ticket == (u16)val) + return; + + /* + * atomic_cond_read_acquire() is RCpc, but rather than defining a + * custom cond_read_rcsc() here we just emit a full fence. We only + * need the prior reads before subsequent writes ordering from + * smb_mb(), but as atomic_cond_read_acquire() just emits reads and we + * have no outstanding writes due to the atomic_fetch_add() the extra + * orderings are free. + */ + atomic_cond_read_acquire(&lock->val, ticket == (u16)VAL); + smp_mb(); +} + +static __always_inline bool ticket_spin_trylock(arch_spinlock_t *lock) +{ + u32 old = atomic_read(&lock->val); + + if ((old >> 16) != (old & 0xffff)) + return false; + + return atomic_try_cmpxchg(&lock->val, &old, old + (1<<16)); /* SC, for RCsc */ +} + +static __always_inline void ticket_spin_unlock(arch_spinlock_t *lock) +{ + u16 *ptr = (u16 *)lock + IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); + u32 val = atomic_read(&lock->val); + + smp_store_release(ptr, (u16)val + 1); +} + +static __always_inline int ticket_spin_value_unlocked(arch_spinlock_t lock) +{ + u32 val = lock.val.counter; + + return ((val >> 16) == (val & 0xffff)); +} + +static __always_inline int ticket_spin_is_locked(arch_spinlock_t *lock) +{ + arch_spinlock_t val = READ_ONCE(*lock); + + return !ticket_spin_value_unlocked(val); +} + +static __always_inline int ticket_spin_is_contended(arch_spinlock_t *lock) +{ + u32 val = atomic_read(&lock->val); + + return (s16)((val >> 16) - (val & 0xffff)) > 1; +} + +#ifndef __no_arch_spinlock_redefine +/* + * Remapping spinlock architecture specific functions to the corresponding + * ticket spinlock functions. + */ +#define arch_spin_is_locked(l) ticket_spin_is_locked(l) +#define arch_spin_is_contended(l) ticket_spin_is_contended(l) +#define arch_spin_value_unlocked(l) ticket_spin_value_unlocked(l) +#define arch_spin_lock(l) ticket_spin_lock(l) +#define arch_spin_trylock(l) ticket_spin_trylock(l) +#define arch_spin_unlock(l) ticket_spin_unlock(l) +#endif + +#endif /* __ASM_GENERIC_TICKET_SPINLOCK_H */ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index eeadbaeccf88b..54504013c7491 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -95,18 +95,25 @@ * With LTO_CLANG, the linker also splits sections by default, so we need * these macros to combine the sections during the final link. * + * With AUTOFDO_CLANG and PROPELLER_CLANG, by default, the linker splits + * text sections and regroups functions into subsections. + * * RODATA_MAIN is not used because existing code already defines .rodata.x * sections to be brought in with rodata. */ -#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) +#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) || \ +defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) #define TEXT_MAIN .text .text.[0-9a-zA-Z_]* +#else +#define TEXT_MAIN .text +#endif +#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) #define DATA_MAIN .data .data.[0-9a-zA-Z_]* .data..L* .data..compoundliteral* .data.$__unnamed_* .data.$L* #define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]* #define RODATA_MAIN .rodata .rodata.[0-9a-zA-Z_]* .rodata..L* #define BSS_MAIN .bss .bss.[0-9a-zA-Z_]* .bss..L* .bss..compoundliteral* #define SBSS_MAIN .sbss .sbss.[0-9a-zA-Z_]* #else -#define TEXT_MAIN .text #define DATA_MAIN .data #define SDATA_MAIN .sdata #define RODATA_MAIN .rodata @@ -350,9 +357,9 @@ *(.data..decrypted) \ *(.ref.data) \ *(.data..shared_aligned) /* percpu related */ \ - *(.data.unlikely) \ + *(.data..unlikely) \ __start_once = .; \ - *(.data.once) \ + *(.data..once) \ __end_once = .; \ STRUCT_ALIGN(); \ *(__tracepoints) \ @@ -549,24 +556,44 @@ __cpuidle_text_end = .; \ __noinstr_text_end = .; +#define TEXT_SPLIT \ + __split_text_start = .; \ + *(.text.split .text.split.[0-9a-zA-Z_]*) \ + __split_text_end = .; + +#define TEXT_UNLIKELY \ + __unlikely_text_start = .; \ + *(.text.unlikely .text.unlikely.*) \ + __unlikely_text_end = .; + +#define TEXT_HOT \ + __hot_text_start = .; \ + *(.text.hot .text.hot.*) \ + __hot_text_end = .; + /* * .text section. Map to function alignment to avoid address changes * during second ld run in second ld pass when generating System.map * - * TEXT_MAIN here will match .text.fixup and .text.unlikely if dead - * code elimination is enabled, so these sections should be converted - * to use ".." first. + * TEXT_MAIN here will match symbols with a fixed pattern (for example, + * .text.hot or .text.unlikely) if dead code elimination or + * function-section is enabled. Match these symbols first before + * TEXT_MAIN to ensure they are grouped together. + * + * Also placing .text.hot section at the beginning of a page, this + * would help the TLB performance. */ #define TEXT_TEXT \ ALIGN_FUNCTION(); \ - *(.text.hot .text.hot.*) \ - *(TEXT_MAIN .text.fixup) \ - *(.text.unlikely .text.unlikely.*) \ + *(.text.asan.* .text.tsan.*) \ *(.text.unknown .text.unknown.*) \ + TEXT_SPLIT \ + TEXT_UNLIKELY \ + . = ALIGN(PAGE_SIZE); \ + TEXT_HOT \ + *(TEXT_MAIN .text.fixup) \ NOINSTR_TEXT \ - *(.ref.text) \ - *(.text.asan.* .text.tsan.*) - + *(.ref.text) /* sched.text is aling to function alignment to secure we have same * address even at second ld pass when generating System.map */ diff --git a/include/dt-bindings/iio/adc/gehc,pmc-adc.h b/include/dt-bindings/iio/adc/gehc,pmc-adc.h new file mode 100644 index 0000000000000..2f291e3c76aec --- /dev/null +++ b/include/dt-bindings/iio/adc/gehc,pmc-adc.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ + +#ifndef _DT_BINDINGS_IIO_ADC_GEHC_PMC_ADC_H +#define _DT_BINDINGS_IIO_ADC_GEHC_PMC_ADC_H + +/* ADC channel type */ +#define GEHC_PMC_ADC_VOLTAGE 0 +#define GEHC_PMC_ADC_CURRENT 1 + +#endif diff --git a/include/dt-bindings/interconnect/qcom,qcs615-rpmh.h b/include/dt-bindings/interconnect/qcom,qcs615-rpmh.h new file mode 100644 index 0000000000000..84ae0d39e73cc --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,qcs615-rpmh.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_QCS615_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_QCS615_H + +#define MASTER_A1NOC_CFG 1 +#define MASTER_QDSS_BAM 2 +#define MASTER_QSPI 3 +#define MASTER_QUP_0 4 +#define MASTER_BLSP_1 5 +#define MASTER_CNOC_A2NOC 6 +#define MASTER_CRYPTO 7 +#define MASTER_IPA 8 +#define MASTER_EMAC_EVB 9 +#define MASTER_PCIE 10 +#define MASTER_QDSS_ETR 11 +#define MASTER_SDCC_1 12 +#define MASTER_SDCC_2 13 +#define MASTER_UFS_MEM 14 +#define MASTER_USB2 15 +#define MASTER_USB3_0 16 +#define SLAVE_A1NOC_SNOC 17 +#define SLAVE_LPASS_SNOC 18 +#define SLAVE_ANOC_PCIE_SNOC 19 +#define SLAVE_SERVICE_A2NOC 20 + +#define MASTER_CAMNOC_HF0_UNCOMP 1 +#define MASTER_CAMNOC_HF1_UNCOMP 2 +#define MASTER_CAMNOC_SF_UNCOMP 3 +#define SLAVE_CAMNOC_UNCOMP 4 + +#define MASTER_SPDM 1 +#define MASTER_SNOC_CNOC 2 +#define MASTER_QDSS_DAP 3 +#define SLAVE_A1NOC_CFG 4 +#define SLAVE_AHB2PHY_EAST 5 +#define SLAVE_AHB2PHY_WEST 6 +#define SLAVE_AOP 7 +#define SLAVE_AOSS 8 +#define SLAVE_CAMERA_CFG 9 +#define SLAVE_CLK_CTL 10 +#define SLAVE_RBCPR_CX_CFG 11 +#define SLAVE_RBCPR_MX_CFG 12 +#define SLAVE_CRYPTO_0_CFG 13 +#define SLAVE_CNOC_DDRSS 14 +#define SLAVE_DISPLAY_CFG 15 +#define SLAVE_EMAC_AVB_CFG 16 +#define SLAVE_GLM 17 +#define SLAVE_GFX3D_CFG 18 +#define SLAVE_IMEM_CFG 19 +#define SLAVE_IPA_CFG 20 +#define SLAVE_CNOC_MNOC_CFG 21 +#define SLAVE_PCIE_CFG 22 +#define SLAVE_PIMEM_CFG 23 +#define SLAVE_PRNG 24 +#define SLAVE_QDSS_CFG 25 +#define SLAVE_QSPI 26 +#define SLAVE_QUP_0 27 +#define SLAVE_QUP_1 28 +#define SLAVE_SDCC_1 29 +#define SLAVE_SDCC_2 30 +#define SLAVE_SNOC_CFG 31 +#define SLAVE_SPDM_WRAPPER 32 +#define SLAVE_TCSR 33 +#define SLAVE_TLMM_EAST 34 +#define SLAVE_TLMM_SOUTH 35 +#define SLAVE_TLMM_WEST 36 +#define SLAVE_UFS_MEM_CFG 37 +#define SLAVE_USB2 38 +#define SLAVE_USB3 39 +#define SLAVE_VENUS_CFG 40 +#define SLAVE_VSENSE_CTRL_CFG 41 +#define SLAVE_CNOC_A2NOC 42 +#define SLAVE_SERVICE_CNOC 43 + +#define MASTER_CNOC_DC_NOC 1 +#define SLAVE_DC_NOC_GEMNOC 2 +#define SLAVE_LLCC_CFG 3 + +#define MASTER_APPSS_PROC 1 +#define MASTER_GPU_TCU 2 +#define MASTER_SYS_TCU 3 +#define MASTER_GEM_NOC_CFG 4 +#define MASTER_GFX3D 5 +#define MASTER_MNOC_HF_MEM_NOC 6 +#define MASTER_MNOC_SF_MEM_NOC 7 +#define MASTER_SNOC_GC_MEM_NOC 8 +#define MASTER_SNOC_SF_MEM_NOC 9 +#define SLAVE_MSS_PROC_MS_MPU_CFG 10 +#define SLAVE_GEM_NOC_SNOC 11 +#define SLAVE_LLCC 12 +#define SLAVE_MEM_NOC_PCIE_SNOC 13 +#define SLAVE_SERVICE_GEM_NOC 14 + +#define MASTER_IPA_CORE 1 +#define SLAVE_IPA_CORE 2 + +#define MASTER_LLCC 1 +#define SLAVE_EBI1 2 + +#define MASTER_CNOC_MNOC_CFG 1 +#define MASTER_CAMNOC_HF0 2 +#define MASTER_CAMNOC_HF1 3 +#define MASTER_CAMNOC_SF 4 +#define MASTER_MDP0 5 +#define MASTER_ROTATOR 6 +#define MASTER_VIDEO_P0 7 +#define MASTER_VIDEO_PROC 8 +#define SLAVE_MNOC_SF_MEM_NOC 9 +#define SLAVE_MNOC_HF_MEM_NOC 10 +#define SLAVE_SERVICE_MNOC 11 + +#define MASTER_SNOC_CFG 1 +#define MASTER_A1NOC_SNOC 2 +#define MASTER_GEM_NOC_SNOC 3 +#define MASTER_GEM_NOC_PCIE_SNOC 4 +#define MASTER_LPASS_ANOC 5 +#define MASTER_ANOC_PCIE_SNOC 6 +#define MASTER_PIMEM 7 +#define MASTER_GIC 8 +#define SLAVE_APPSS 9 +#define SLAVE_SNOC_CNOC 10 +#define SLAVE_SNOC_GEM_NOC_SF 11 +#define SLAVE_SNOC_MEM_NOC_GC 12 +#define SLAVE_IMEM 13 +#define SLAVE_PIMEM 14 +#define SLAVE_SERVICE_SNOC 15 +#define SLAVE_PCIE_0 16 +#define SLAVE_QDSS_STM 17 +#define SLAVE_TCU 18 + +#endif + diff --git a/include/dt-bindings/interconnect/qcom,qcs8300-rpmh.h b/include/dt-bindings/interconnect/qcom,qcs8300-rpmh.h new file mode 100644 index 0000000000000..c5eeafa1b1dda --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,qcs8300-rpmh.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_QCS8300_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_QCS8300_H + +#define MASTER_QUP_3 0 +#define MASTER_EMAC 1 +#define MASTER_SDC 2 +#define MASTER_UFS_MEM 3 +#define MASTER_USB2 4 +#define MASTER_USB3_0 5 +#define SLAVE_A1NOC_SNOC 6 + +#define MASTER_QDSS_BAM 0 +#define MASTER_QUP_0 1 +#define MASTER_QUP_1 2 +#define MASTER_CNOC_A2NOC 3 +#define MASTER_CRYPTO_CORE0 4 +#define MASTER_CRYPTO_CORE1 5 +#define MASTER_IPA 6 +#define MASTER_QDSS_ETR_0 7 +#define MASTER_QDSS_ETR_1 8 +#define SLAVE_A2NOC_SNOC 9 + +#define MASTER_QUP_CORE_0 0 +#define MASTER_QUP_CORE_1 1 +#define MASTER_QUP_CORE_3 2 +#define SLAVE_QUP_CORE_0 3 +#define SLAVE_QUP_CORE_1 4 +#define SLAVE_QUP_CORE_3 5 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AHB2PHY_2 2 +#define SLAVE_AHB2PHY_3 3 +#define SLAVE_ANOC_THROTTLE_CFG 4 +#define SLAVE_AOSS 5 +#define SLAVE_APPSS 6 +#define SLAVE_BOOT_ROM 7 +#define SLAVE_CAMERA_CFG 8 +#define SLAVE_CAMERA_NRT_THROTTLE_CFG 9 +#define SLAVE_CAMERA_RT_THROTTLE_CFG 10 +#define SLAVE_CLK_CTL 11 +#define SLAVE_CDSP_CFG 12 +#define SLAVE_RBCPR_CX_CFG 13 +#define SLAVE_RBCPR_MMCX_CFG 14 +#define SLAVE_RBCPR_MX_CFG 15 +#define SLAVE_CPR_NSPCX 16 +#define SLAVE_CPR_NSPHMX 17 +#define SLAVE_CRYPTO_0_CFG 18 +#define SLAVE_CX_RDPM 19 +#define SLAVE_DISPLAY_CFG 20 +#define SLAVE_DISPLAY_RT_THROTTLE_CFG 21 +#define SLAVE_EMAC_CFG 22 +#define SLAVE_GP_DSP0_CFG 23 +#define SLAVE_GPDSP0_THROTTLE_CFG 24 +#define SLAVE_GPU_TCU_THROTTLE_CFG 25 +#define SLAVE_GFX3D_CFG 26 +#define SLAVE_HWKM 27 +#define SLAVE_IMEM_CFG 28 +#define SLAVE_IPA_CFG 29 +#define SLAVE_IPC_ROUTER_CFG 30 +#define SLAVE_LPASS 31 +#define SLAVE_LPASS_THROTTLE_CFG 32 +#define SLAVE_MX_RDPM 33 +#define SLAVE_MXC_RDPM 34 +#define SLAVE_PCIE_0_CFG 35 +#define SLAVE_PCIE_1_CFG 36 +#define SLAVE_PCIE_TCU_THROTTLE_CFG 37 +#define SLAVE_PCIE_THROTTLE_CFG 38 +#define SLAVE_PDM 39 +#define SLAVE_PIMEM_CFG 40 +#define SLAVE_PKA_WRAPPER_CFG 41 +#define SLAVE_QDSS_CFG 42 +#define SLAVE_QM_CFG 43 +#define SLAVE_QM_MPU_CFG 44 +#define SLAVE_QUP_0 45 +#define SLAVE_QUP_1 46 +#define SLAVE_QUP_3 47 +#define SLAVE_SAIL_THROTTLE_CFG 48 +#define SLAVE_SDC1 49 +#define SLAVE_SECURITY 50 +#define SLAVE_SNOC_THROTTLE_CFG 51 +#define SLAVE_TCSR 52 +#define SLAVE_TLMM 53 +#define SLAVE_TSC_CFG 54 +#define SLAVE_UFS_MEM_CFG 55 +#define SLAVE_USB2 56 +#define SLAVE_USB3_0 57 +#define SLAVE_VENUS_CFG 58 +#define SLAVE_VENUS_CVP_THROTTLE_CFG 59 +#define SLAVE_VENUS_V_CPU_THROTTLE_CFG 60 +#define SLAVE_VENUS_VCODEC_THROTTLE_CFG 61 +#define SLAVE_DDRSS_CFG 62 +#define SLAVE_GPDSP_NOC_CFG 63 +#define SLAVE_CNOC_MNOC_HF_CFG 64 +#define SLAVE_CNOC_MNOC_SF_CFG 65 +#define SLAVE_PCIE_ANOC_CFG 66 +#define SLAVE_SNOC_CFG 67 +#define SLAVE_BOOT_IMEM 68 +#define SLAVE_IMEM 69 +#define SLAVE_PIMEM 70 +#define SLAVE_PCIE_0 71 +#define SLAVE_PCIE_1 72 +#define SLAVE_QDSS_STM 73 +#define SLAVE_TCU 74 + +#define MASTER_CNOC_DC_NOC 0 +#define SLAVE_LLCC_CFG 1 +#define SLAVE_GEM_NOC_CFG 2 + +#define MASTER_GPU_TCU 0 +#define MASTER_PCIE_TCU 1 +#define MASTER_SYS_TCU 2 +#define MASTER_APPSS_PROC 3 +#define MASTER_COMPUTE_NOC 4 +#define MASTER_GEM_NOC_CFG 5 +#define MASTER_GPDSP_SAIL 6 +#define MASTER_GFX3D 7 +#define MASTER_MNOC_HF_MEM_NOC 8 +#define MASTER_MNOC_SF_MEM_NOC 9 +#define MASTER_ANOC_PCIE_GEM_NOC 10 +#define MASTER_SNOC_GC_MEM_NOC 11 +#define MASTER_SNOC_SF_MEM_NOC 12 +#define SLAVE_GEM_NOC_CNOC 13 +#define SLAVE_LLCC 14 +#define SLAVE_GEM_NOC_PCIE_CNOC 15 +#define SLAVE_SERVICE_GEM_NOC_1 16 +#define SLAVE_SERVICE_GEM_NOC_2 17 +#define SLAVE_SERVICE_GEM_NOC 18 +#define SLAVE_SERVICE_GEM_NOC2 19 + +#define MASTER_SAILSS_MD0 0 +#define MASTER_DSP0 1 +#define SLAVE_GP_DSP_SAIL_NOC 2 + +#define MASTER_CNOC_LPASS_AG_NOC 0 +#define MASTER_LPASS_PROC 1 +#define SLAVE_LPASS_CORE_CFG 2 +#define SLAVE_LPASS_LPI_CFG 3 +#define SLAVE_LPASS_MPU_CFG 4 +#define SLAVE_LPASS_TOP_CFG 5 +#define SLAVE_LPASS_SNOC 6 +#define SLAVE_SERVICES_LPASS_AML_NOC 7 +#define SLAVE_SERVICE_LPASS_AG_NOC 8 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_ICP 1 +#define MASTER_CAMNOC_SF 2 +#define MASTER_MDP0 3 +#define MASTER_MDP1 4 +#define MASTER_CNOC_MNOC_HF_CFG 5 +#define MASTER_CNOC_MNOC_SF_CFG 6 +#define MASTER_VIDEO_P0 7 +#define MASTER_VIDEO_PROC 8 +#define MASTER_VIDEO_V_PROC 9 +#define SLAVE_MNOC_HF_MEM_NOC 10 +#define SLAVE_MNOC_SF_MEM_NOC 11 +#define SLAVE_SERVICE_MNOC_HF 12 +#define SLAVE_SERVICE_MNOC_SF 13 + +#define MASTER_CDSP_NOC_CFG 0 +#define MASTER_CDSP_PROC 1 +#define SLAVE_HCP_A 2 +#define SLAVE_CDSP_MEM_NOC 3 +#define SLAVE_SERVICE_NSP_NOC 4 + +#define MASTER_PCIE_0 0 +#define MASTER_PCIE_1 1 +#define SLAVE_ANOC_PCIE_GEM_NOC 2 + +#define MASTER_GIC_AHB 0 +#define MASTER_A1NOC_SNOC 1 +#define MASTER_A2NOC_SNOC 2 +#define MASTER_LPASS_ANOC 3 +#define MASTER_SNOC_CFG 4 +#define MASTER_PIMEM 5 +#define MASTER_GIC 6 +#define SLAVE_SNOC_GEM_NOC_GC 7 +#define SLAVE_SNOC_GEM_NOC_SF 8 +#define SLAVE_SERVICE_SNOC 9 + +#endif diff --git a/include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h b/include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h new file mode 100644 index 0000000000000..aec7cbb7cd705 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024, Linaro Ltd. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_SAR2130P_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_SAR2130P_H + +#define MASTER_QUP_CORE_0 0 +#define MASTER_QUP_CORE_1 1 +#define SLAVE_QUP_CORE_0 2 +#define SLAVE_QUP_CORE_1 3 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define MASTER_QDSS_DAP 2 +#define SLAVE_AHB2PHY_SOUTH 3 +#define SLAVE_AOSS 4 +#define SLAVE_CAMERA_CFG 5 +#define SLAVE_CLK_CTL 6 +#define SLAVE_CDSP_CFG 7 +#define SLAVE_RBCPR_CX_CFG 8 +#define SLAVE_RBCPR_MMCX_CFG 9 +#define SLAVE_RBCPR_MXA_CFG 10 +#define SLAVE_RBCPR_MXC_CFG 11 +#define SLAVE_CPR_NSPCX 12 +#define SLAVE_CRYPTO_0_CFG 13 +#define SLAVE_CX_RDPM 14 +#define SLAVE_DISPLAY_CFG 15 +#define SLAVE_GFX3D_CFG 16 +#define SLAVE_IMEM_CFG 17 +#define SLAVE_IPC_ROUTER_CFG 18 +#define SLAVE_LPASS 19 +#define SLAVE_MX_RDPM 20 +#define SLAVE_PCIE_0_CFG 21 +#define SLAVE_PCIE_1_CFG 22 +#define SLAVE_PDM 23 +#define SLAVE_PIMEM_CFG 24 +#define SLAVE_PRNG 25 +#define SLAVE_QDSS_CFG 26 +#define SLAVE_QSPI_0 27 +#define SLAVE_QUP_0 28 +#define SLAVE_QUP_1 29 +#define SLAVE_SDCC_1 30 +#define SLAVE_TCSR 31 +#define SLAVE_TLMM 32 +#define SLAVE_TME_CFG 33 +#define SLAVE_USB3_0 34 +#define SLAVE_VENUS_CFG 35 +#define SLAVE_VSENSE_CTRL_CFG 36 +#define SLAVE_WLAN_Q6_CFG 37 +#define SLAVE_DDRSS_CFG 38 +#define SLAVE_CNOC_MNOC_CFG 39 +#define SLAVE_SNOC_CFG 40 +#define SLAVE_IMEM 41 +#define SLAVE_PIMEM 42 +#define SLAVE_SERVICE_CNOC 43 +#define SLAVE_PCIE_0 44 +#define SLAVE_PCIE_1 45 +#define SLAVE_QDSS_STM 46 +#define SLAVE_TCU 47 + +#define MASTER_GPU_TCU 0 +#define MASTER_SYS_TCU 1 +#define MASTER_APPSS_PROC 2 +#define MASTER_GFX3D 3 +#define MASTER_MNOC_HF_MEM_NOC 4 +#define MASTER_MNOC_SF_MEM_NOC 5 +#define MASTER_COMPUTE_NOC 6 +#define MASTER_ANOC_PCIE_GEM_NOC 7 +#define MASTER_SNOC_GC_MEM_NOC 8 +#define MASTER_SNOC_SF_MEM_NOC 9 +#define MASTER_WLAN_Q6 10 +#define SLAVE_GEM_NOC_CNOC 11 +#define SLAVE_LLCC 12 +#define SLAVE_MEM_NOC_PCIE_SNOC 13 + +#define MASTER_CNOC_LPASS_AG_NOC 0 +#define MASTER_LPASS_PROC 1 +#define SLAVE_LPASS_CORE_CFG 2 +#define SLAVE_LPASS_LPI_CFG 3 +#define SLAVE_LPASS_MPU_CFG 4 +#define SLAVE_LPASS_TOP_CFG 5 +#define SLAVE_LPASS_SNOC 6 +#define SLAVE_SERVICES_LPASS_AML_NOC 7 +#define SLAVE_SERVICE_LPASS_AG_NOC 8 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_ICP 1 +#define MASTER_CAMNOC_SF 2 +#define MASTER_LSR 3 +#define MASTER_MDP 4 +#define MASTER_CNOC_MNOC_CFG 5 +#define MASTER_VIDEO 6 +#define MASTER_VIDEO_CV_PROC 7 +#define MASTER_VIDEO_PROC 8 +#define MASTER_VIDEO_V_PROC 9 +#define SLAVE_MNOC_HF_MEM_NOC 10 +#define SLAVE_MNOC_SF_MEM_NOC 11 +#define SLAVE_SERVICE_MNOC 12 + +#define MASTER_CDSP_NOC_CFG 0 +#define MASTER_CDSP_PROC 1 +#define SLAVE_CDSP_MEM_NOC 2 +#define SLAVE_SERVICE_NSP_NOC 3 + +#define MASTER_PCIE_0 0 +#define MASTER_PCIE_1 1 +#define SLAVE_ANOC_PCIE_GEM_NOC 2 + +#define MASTER_GIC_AHB 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_QSPI_0 2 +#define MASTER_QUP_0 3 +#define MASTER_QUP_1 4 +#define MASTER_A2NOC_SNOC 5 +#define MASTER_CNOC_DATAPATH 6 +#define MASTER_LPASS_ANOC 7 +#define MASTER_SNOC_CFG 8 +#define MASTER_CRYPTO 9 +#define MASTER_PIMEM 10 +#define MASTER_GIC 11 +#define MASTER_QDSS_ETR 12 +#define MASTER_QDSS_ETR_1 13 +#define MASTER_SDCC_1 14 +#define MASTER_USB3_0 15 +#define SLAVE_A2NOC_SNOC 16 +#define SLAVE_SNOC_GEM_NOC_GC 17 +#define SLAVE_SNOC_GEM_NOC_SF 18 +#define SLAVE_SERVICE_SNOC 19 + +#endif diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index c819c5d16613b..fd650a8789b91 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -147,6 +147,9 @@ u64 timer_get_cval(struct arch_timer_context *ctxt); void kvm_timer_cpu_up(void); void kvm_timer_cpu_down(void); +/* CNTKCTL_EL1 valid bits as of DDI0487J.a */ +#define CNTKCTL_VALID_BITS (BIT(17) | GENMASK_ULL(9, 0)) + static inline bool has_cntpoff(void) { return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF)); diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index e08aeec5d936f..e61dd7dd22869 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -47,7 +47,8 @@ static __always_inline bool kvm_arm_support_pmu_v3(void) #define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS) u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx); void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val); -u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu); +u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu); +u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu); u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1); void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu); void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu); @@ -96,6 +97,8 @@ int kvm_arm_set_default_pmu(struct kvm *kvm); u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm); u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu); +bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int idx); +void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu); #else struct kvm_pmu { }; @@ -113,7 +116,11 @@ static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, } static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) {} -static inline u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu) +static inline u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu) +{ + return 0; +} +static inline u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu) { return 0; } @@ -187,6 +194,13 @@ static inline u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu) return 0; } +static inline bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int idx) +{ + return false; +} + +static inline void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu) {} + #endif #endif diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h index e8fb624013d15..cbaec804eb839 100644 --- a/include/kvm/arm_psci.h +++ b/include/kvm/arm_psci.h @@ -14,8 +14,10 @@ #define KVM_ARM_PSCI_0_2 PSCI_VERSION(0, 2) #define KVM_ARM_PSCI_1_0 PSCI_VERSION(1, 0) #define KVM_ARM_PSCI_1_1 PSCI_VERSION(1, 1) +#define KVM_ARM_PSCI_1_2 PSCI_VERSION(1, 2) +#define KVM_ARM_PSCI_1_3 PSCI_VERSION(1, 3) -#define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_1 +#define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_3 static inline int kvm_psci_version(struct kvm_vcpu *vcpu) { diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f5172549f9ba0..3a8ccfda34d29 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -26,7 +26,6 @@ #define VGIC_NR_SGIS 16 #define VGIC_NR_PPIS 16 #define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS) -#define VGIC_MAX_PRIVATE (VGIC_NR_PRIVATE_IRQS - 1) #define VGIC_MAX_SPI 1019 #define VGIC_MAX_RESERVED 1023 #define VGIC_MIN_LPI 8192 diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7dd24acd9ffee..05f39fbfa4856 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1530,17 +1530,7 @@ static inline int find_acpi_cpu_topology_hetero_id(unsigned int cpu) } #endif -#ifdef CONFIG_ARM64 -void acpi_arm_init(void); -#else -static inline void acpi_arm_init(void) { } -#endif - -#ifdef CONFIG_RISCV -void acpi_riscv_init(void); -#else -static inline void acpi_riscv_init(void) { } -#endif +void acpi_arch_init(void); #ifdef CONFIG_ACPI_PCC void acpi_init_pcc(void); diff --git a/include/linux/acpi_dma.h b/include/linux/acpi_dma.h index 72cedb916a9c2..e748b28776022 100644 --- a/include/linux/acpi_dma.h +++ b/include/linux/acpi_dma.h @@ -11,10 +11,11 @@ #ifndef __LINUX_ACPI_DMA_H #define __LINUX_ACPI_DMA_H -#include -#include #include #include +#include + +struct device; /** * struct acpi_dma_spec - slave device DMA resources @@ -65,7 +66,6 @@ int devm_acpi_dma_controller_register(struct device *dev, struct dma_chan *(*acpi_dma_xlate) (struct acpi_dma_spec *, struct acpi_dma *), void *data); -void devm_acpi_dma_controller_free(struct device *dev); struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, size_t index); @@ -94,9 +94,6 @@ static inline int devm_acpi_dma_controller_register(struct device *dev, { return -ENODEV; } -static inline void devm_acpi_dma_controller_free(struct device *dev) -{ -} static inline struct dma_chan *acpi_dma_request_slave_chan_by_index( struct device *dev, size_t index) diff --git a/include/linux/alcor_pci.h b/include/linux/alcor_pci.h index c4a0b23846d8d..dcb1d37dabc29 100644 --- a/include/linux/alcor_pci.h +++ b/include/linux/alcor_pci.h @@ -11,6 +11,7 @@ #define ALCOR_SD_CARD 0 #define ALCOR_MS_CARD 1 +#define DRV_NAME_ALCOR_PCI "alcor_pci" #define DRV_NAME_ALCOR_PCI_SDMMC "alcor_sdmmc" #define DRV_NAME_ALCOR_PCI_MS "alcor_ms" diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index dda2f3ea89cb5..9946276aff737 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -121,6 +121,7 @@ extern const struct bus_type amba_bustype; #ifdef CONFIG_ARM_AMBA int __amba_driver_register(struct amba_driver *, struct module *); void amba_driver_unregister(struct amba_driver *); +bool dev_is_amba(const struct device *dev); #else static inline int __amba_driver_register(struct amba_driver *drv, struct module *owner) @@ -130,6 +131,10 @@ static inline int __amba_driver_register(struct amba_driver *drv, static inline void amba_driver_unregister(struct amba_driver *drv) { } +static inline bool dev_is_amba(const struct device *dev) +{ + return false; +} #endif struct amba_device *amba_device_alloc(const char *, resource_size_t, size_t); diff --git a/include/linux/auxiliary_bus.h b/include/linux/auxiliary_bus.h index 31762324bcc9b..65dd7f1543747 100644 --- a/include/linux/auxiliary_bus.h +++ b/include/linux/auxiliary_bus.h @@ -269,8 +269,4 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv); #define module_auxiliary_driver(__auxiliary_driver) \ module_driver(__auxiliary_driver, auxiliary_driver_register, auxiliary_driver_unregister) -struct auxiliary_device *auxiliary_find_device(struct device *start, - const void *data, - device_match_t match); - #endif /* _AUXILIARY_BUS_H_ */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index a1fd0ddce5cf7..08a727b408164 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -333,6 +333,10 @@ typedef unsigned int __bitwise blk_features_t; #define BLK_FEAT_RAID_PARTIAL_STRIPES_EXPENSIVE \ ((__force blk_features_t)(1u << 15)) +/* stacked device can/does support atomic writes */ +#define BLK_FEAT_ATOMIC_WRITES_STACKED \ + ((__force blk_features_t)(1u << 16)) + /* * Flags automatically inherited when stacking limits. */ @@ -775,13 +779,13 @@ static inline void bdev_clear_flag(struct block_device *bdev, unsigned flag) atomic_andnot(flag, &bdev->__bd_flags); } -static inline int get_disk_ro(struct gendisk *disk) +static inline bool get_disk_ro(struct gendisk *disk) { return bdev_test_flag(disk->part0, BD_READ_ONLY) || test_bit(GD_READ_ONLY, &disk->state); } -static inline int bdev_read_only(struct block_device *bdev) +static inline bool bdev_read_only(struct block_device *bdev) { return bdev_test_flag(bdev, BD_READ_ONLY) || get_disk_ro(bdev->bd_disk); } @@ -1261,7 +1265,7 @@ static inline unsigned int queue_io_min(const struct request_queue *q) return q->limits.io_min; } -static inline int bdev_io_min(struct block_device *bdev) +static inline unsigned int bdev_io_min(struct block_device *bdev) { return queue_io_min(bdev_get_queue(bdev)); } @@ -1271,7 +1275,7 @@ static inline unsigned int queue_io_opt(const struct request_queue *q) return q->limits.io_opt; } -static inline int bdev_io_opt(struct block_device *bdev) +static inline unsigned int bdev_io_opt(struct block_device *bdev) { return queue_io_opt(bdev_get_queue(bdev)); } @@ -1417,7 +1421,7 @@ static inline bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector) return is_seq; } -static inline int queue_dma_alignment(const struct request_queue *q) +static inline unsigned int queue_dma_alignment(const struct request_queue *q) { return q->limits.dma_alignment; } @@ -1458,12 +1462,13 @@ static inline bool bdev_iter_is_aligned(struct block_device *bdev, bdev_logical_block_size(bdev) - 1); } -static inline int blk_lim_dma_alignment_and_pad(struct queue_limits *lim) +static inline unsigned int +blk_lim_dma_alignment_and_pad(struct queue_limits *lim) { return lim->dma_alignment | lim->dma_pad_mask; } -static inline int blk_rq_aligned(struct request_queue *q, unsigned long addr, +static inline bool blk_rq_aligned(struct request_queue *q, unsigned long addr, unsigned int len) { unsigned int alignment = blk_lim_dma_alignment_and_pad(&q->limits); @@ -1581,7 +1586,6 @@ static inline void bio_end_io_acct(struct bio *bio, unsigned long start_time) return bio_end_io_acct_remapped(bio, start_time, bio->bi_bdev); } -int bdev_read_only(struct block_device *bdev); int set_blocksize(struct file *file, int size); int lookup_bdev(const char *pathname, dev_t *dev); diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index ee1d0e5f9789f..2d7d86f0290d9 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -808,7 +808,7 @@ struct ceph_mds_caps { struct ceph_mds_cap_peer { __le64 cap_id; - __le32 seq; + __le32 issue_seq; __le32 mseq; __le32 mds; __u8 flags; @@ -822,7 +822,7 @@ struct ceph_mds_cap_release { struct ceph_mds_cap_item { __le64 ino; __le64 cap_id; - __le32 migrate_seq, seq; + __le32 migrate_seq, issue_seq; } __attribute__ ((packed)); #define CEPH_MDS_LEASE_REVOKE 1 /* mds -> client */ diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 15fb566d3f46a..733e7f93db66a 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -317,12 +317,6 @@ extern void ceph_release_page_vector(struct page **pages, int num_pages); extern void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty); extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags); -extern int ceph_copy_user_to_page_vector(struct page **pages, - const void __user *data, - loff_t off, size_t len); -extern void ceph_copy_to_page_vector(struct page **pages, - const void *data, - loff_t off, size_t len); extern void ceph_copy_from_page_vector(struct page **pages, void *data, loff_t off, size_t len); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index d7941478158cd..d55b30057a455 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -626,8 +626,6 @@ int ceph_osdc_notify(struct ceph_osd_client *osdc, u32 timeout, struct page ***preply_pages, size_t *preply_len); -int ceph_osdc_watch_check(struct ceph_osd_client *osdc, - struct ceph_osd_linger_request *lreq); int ceph_osdc_list_watchers(struct ceph_osd_client *osdc, struct ceph_object_id *oid, struct ceph_object_locator *oloc, diff --git a/include/linux/ceph/pagelist.h b/include/linux/ceph/pagelist.h index 5dead8486fd8f..879bec0863aa1 100644 --- a/include/linux/ceph/pagelist.h +++ b/include/linux/ceph/pagelist.h @@ -17,12 +17,6 @@ struct ceph_pagelist { refcount_t refcnt; }; -struct ceph_pagelist_cursor { - struct ceph_pagelist *pl; /* pagelist, for error checking */ - struct list_head *page_lru; /* page in list */ - size_t room; /* room remaining to reset to */ -}; - struct ceph_pagelist *ceph_pagelist_alloc(gfp_t gfp_flags); extern void ceph_pagelist_release(struct ceph_pagelist *pl); @@ -33,12 +27,6 @@ extern int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space); extern int ceph_pagelist_free_reserve(struct ceph_pagelist *pl); -extern void ceph_pagelist_set_cursor(struct ceph_pagelist *pl, - struct ceph_pagelist_cursor *c); - -extern int ceph_pagelist_truncate(struct ceph_pagelist *pl, - struct ceph_pagelist_cursor *c); - static inline int ceph_pagelist_encode_64(struct ceph_pagelist *pl, u64 v) { __le64 ev = cpu_to_le64(v); diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index 4c1a39dcb624e..2e7c2c282f3af 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef __LINUX_COMPILER_TYPES_H -#error "Please don't include directly, include instead." +#error "Please do not include directly, include instead." #endif /* Compiler specific definitions for Clang compiler */ diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index cd6f9aae311fc..d0ed9583743fc 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef __LINUX_COMPILER_TYPES_H -#error "Please don't include directly, include instead." +#error "Please do not include directly, include instead." #endif /* diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 4d4e23b6e3e76..469a64dd6495f 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -239,11 +239,18 @@ static inline void *offset_to_ptr(const int *off) #endif /* __ASSEMBLY__ */ +#ifdef __CHECKER__ +#define __BUILD_BUG_ON_ZERO_MSG(e, msg) (0) +#else /* __CHECKER__ */ +#define __BUILD_BUG_ON_ZERO_MSG(e, msg) ((int)sizeof(struct {_Static_assert(!(e), msg);})) +#endif /* __CHECKER__ */ + /* &a[0] degrades to a pointer: a different type from an array */ -#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) +#define __must_be_array(a) __BUILD_BUG_ON_ZERO_MSG(__same_type((a), &(a)[0]), "must be array") /* Require C Strings (i.e. NUL-terminated) lack the "nonstring" attribute. */ -#define __must_be_cstr(p) BUILD_BUG_ON_ZERO(__annotated(p, nonstring)) +#define __must_be_cstr(p) \ + __BUILD_BUG_ON_ZERO_MSG(__annotated(p, nonstring), "must be cstr (NUL-terminated)") /* * This returns a constant expression while determining if an argument is diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h index 32284cd26d52a..c16d4199bf923 100644 --- a/include/linux/compiler_attributes.h +++ b/include/linux/compiler_attributes.h @@ -94,19 +94,6 @@ # define __copy(symbol) #endif -/* - * Optional: only supported since gcc >= 15 - * Optional: only supported since clang >= 18 - * - * gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896 - * clang: https://github.com/llvm/llvm-project/pull/76348 - */ -#if __has_attribute(__counted_by__) -# define __counted_by(member) __attribute__((__counted_by__(member))) -#else -# define __counted_by(member) -#endif - /* * Optional: not supported by gcc * Optional: only supported since clang >= 14.0 diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 0c8b9601e6031..981cc3d7e3aa5 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -329,6 +329,25 @@ struct ftrace_likely_data { #define __no_sanitize_or_inline __always_inline #endif +/* + * Optional: only supported since gcc >= 15 + * Optional: only supported since clang >= 18 + * + * gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896 + * clang: https://github.com/llvm/llvm-project/pull/76348 + * + * __bdos on clang < 19.1.2 can erroneously return 0: + * https://github.com/llvm/llvm-project/pull/110497 + * + * __bdos on clang < 19.1.3 can be off by 4: + * https://github.com/llvm/llvm-project/pull/112636 + */ +#ifdef CONFIG_CC_HAS_COUNTED_BY +# define __counted_by(member) __attribute__((__counted_by__(member))) +#else +# define __counted_by(member) +#endif + /* * Apply __counted_by() when the Endianness matches to increase test coverage. */ diff --git a/include/linux/dax.h b/include/linux/dax.h index 9d3e3327af4c0..df41a0017b319 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -27,12 +27,6 @@ struct dax_operations { */ long (*direct_access)(struct dax_device *, pgoff_t, long, enum dax_access_mode, void **, pfn_t *); - /* - * Validate whether this device is usable as an fsdax backing - * device. - */ - bool (*dax_supported)(struct dax_device *, struct block_device *, int, - sector_t, sector_t); /* zero_page_range: required operation. Zero page range */ int (*zero_page_range)(struct dax_device *, pgoff_t, size_t); /* diff --git a/include/linux/eeprom_93cx6.h b/include/linux/eeprom_93cx6.h index c860c72a921d0..3a485cc0e0fa0 100644 --- a/include/linux/eeprom_93cx6.h +++ b/include/linux/eeprom_93cx6.h @@ -11,6 +11,8 @@ Supported chipsets: 93c46, 93c56 and 93c66. */ +#include + /* * EEPROM operation defines. */ @@ -34,6 +36,7 @@ * @register_write(struct eeprom_93cx6 *eeprom): handler to * write to the eeprom register by using all reg_* fields. * @width: eeprom width, should be one of the PCI_EEPROM_WIDTH_* defines + * @quirks: eeprom or controller quirks * @drive_data: Set if we're driving the data line. * @reg_data_in: register field to indicate data input * @reg_data_out: register field to indicate data output @@ -50,6 +53,9 @@ struct eeprom_93cx6 { void (*register_write)(struct eeprom_93cx6 *eeprom); int width; + unsigned int quirks; +/* Some EEPROMs require an extra clock cycle before reading */ +#define PCI_EEPROM_QUIRK_EXTRA_READ_CYCLE BIT(0) char drive_data; char reg_data_in; @@ -71,3 +77,8 @@ extern void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable); extern void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, u8 addr, u16 data); + +static inline bool has_quirk_extra_read_cycle(struct eeprom_93cx6 *eeprom) +{ + return eeprom->quirks & PCI_EEPROM_QUIRK_EXTRA_READ_CYCLE; +} diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index 1ab165c2939f8..4cc8801e50e39 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -160,6 +160,19 @@ struct fid { #define EXPORT_FH_FID 0x2 /* File handle may be non-decodeable */ #define EXPORT_FH_DIR_ONLY 0x4 /* Only decode file handle for a directory */ +/* + * Filesystems use only lower 8 bits of file_handle type for fid_type. + * name_to_handle_at() uses upper 16 bits of type as user flags to be + * interpreted by open_by_handle_at(). + */ +#define FILEID_USER_FLAGS_MASK 0xffff0000 +#define FILEID_USER_FLAGS(type) ((type) & FILEID_USER_FLAGS_MASK) + +/* Flags supported in encoded handle_type that is exported to user */ +#define FILEID_IS_CONNECTABLE 0x10000 +#define FILEID_IS_DIR 0x20000 +#define FILEID_VALID_USER_FLAGS (FILEID_IS_CONNECTABLE | FILEID_IS_DIR) + /** * struct export_operations - for nfsd to communicate with file systems * @encode_fh: encode a file handle fragment from a dentry diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index b0b821edfd97d..c24f8bc01045d 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -24,10 +24,11 @@ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ #define COMPRESS_ADDR ((block_t)-2) /* used as compressed data flag */ -#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) -#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) +#define F2FS_BLKSIZE_MASK (F2FS_BLKSIZE - 1) +#define F2FS_BYTES_TO_BLK(bytes) ((unsigned long long)(bytes) >> F2FS_BLKSIZE_BITS) +#define F2FS_BLK_TO_BYTES(blk) ((unsigned long long)(blk) << F2FS_BLKSIZE_BITS) #define F2FS_BLK_END_BYTES(blk) (F2FS_BLK_TO_BYTES(blk + 1) - 1) -#define F2FS_BLK_ALIGN(x) (F2FS_BYTES_TO_BLK((x) + F2FS_BLKSIZE - 1)) +#define F2FS_BLK_ALIGN(x) (F2FS_BYTES_TO_BLK((x) + F2FS_BLKSIZE - 1)) /* 0, 1(node nid), 2(meta nid) are reserved node id */ #define F2FS_RESERVED_NODE_NUM 3 diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 76d85ad82ec06..6d4dbc196b937 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -263,6 +263,7 @@ enum pm_query_id { PM_QID_PINCTRL_GET_PIN_GROUPS = 11, PM_QID_CLOCK_GET_NUM_CLOCKS = 12, PM_QID_CLOCK_GET_MAX_DIVISOR = 13, + PM_QID_PINCTRL_GET_ATTRIBUTES = 15, }; enum rpu_oper_mode { diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 6730ee900ee1c..8a03d9696b1c9 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -30,6 +30,8 @@ #define HID_USAGE_SENSOR_PROX 0x200011 #define HID_USAGE_SENSOR_DATA_PRESENCE 0x2004b0 #define HID_USAGE_SENSOR_HUMAN_PRESENCE 0x2004b1 +#define HID_USAGE_SENSOR_HUMAN_PROXIMITY 0x2004b2 +#define HID_USAGE_SENSOR_HUMAN_ATTENTION 0x2004bd /* Pressure (200031) */ #define HID_USAGE_SENSOR_PRESSURE 0x200031 diff --git a/include/linux/i2c-of-prober.h b/include/linux/i2c-of-prober.h new file mode 100644 index 0000000000000..bb6d47f50ee59 --- /dev/null +++ b/include/linux/i2c-of-prober.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Definitions for the Linux I2C OF component prober + * + * Copyright (C) 2024 Google LLC + */ + +#ifndef _LINUX_I2C_OF_PROBER_H +#define _LINUX_I2C_OF_PROBER_H + +#include +#include + +struct device; +struct device_node; + +/** + * struct i2c_of_probe_ops - I2C OF component prober callbacks + * + * A set of callbacks to be used by i2c_of_probe_component(). + * + * All callbacks are optional. Callbacks are called only once per run, and are + * used in the order they are defined in this structure. + * + * All callbacks that have return values shall return %0 on success, + * or a negative error number on failure. + * + * The @dev parameter passed to the callbacks is the same as @dev passed to + * i2c_of_probe_component(). It should only be used for dev_printk() calls + * and nothing else, especially not managed device resource (devres) APIs. + */ +struct i2c_of_probe_ops { + /** + * @enable: Retrieve and enable resources so that the components respond to probes. + * + * It is OK for this callback to return -EPROBE_DEFER since the intended use includes + * retrieving resources and enables them. Resources should be reverted to their initial + * state and released before returning if this fails. + */ + int (*enable)(struct device *dev, struct device_node *bus_node, void *data); + + /** + * @cleanup_early: Release exclusive resources prior to calling probe() on a + * detected component. + * + * Only called if a matching component is actually found. If none are found, + * resources that would have been released in this callback should be released in + * @free_resourcs_late instead. + */ + void (*cleanup_early)(struct device *dev, void *data); + + /** + * @cleanup: Opposite of @enable to balance refcounts and free resources after probing. + * + * Should check if resources were already freed by @cleanup_early. + */ + void (*cleanup)(struct device *dev, void *data); +}; + +/** + * struct i2c_of_probe_cfg - I2C OF component prober configuration + * @ops: Callbacks for the prober to use. + * @type: A string to match the device node name prefix to probe for. + */ +struct i2c_of_probe_cfg { + const struct i2c_of_probe_ops *ops; + const char *type; +}; + +#if IS_ENABLED(CONFIG_OF_DYNAMIC) + +int i2c_of_probe_component(struct device *dev, const struct i2c_of_probe_cfg *cfg, void *ctx); + +/** + * DOC: I2C OF component prober simple helpers + * + * Components such as trackpads are commonly connected to a devices baseboard + * with a 6-pin ribbon cable. That gives at most one voltage supply and one + * GPIO (commonly a "enable" or "reset" line) besides the I2C bus, interrupt + * pin, and common ground. Touchscreens, while integrated into the display + * panel's connection, typically have the same set of connections. + * + * A simple set of helpers are provided here for use with the I2C OF component + * prober. This implementation targets such components, allowing for at most + * one regulator supply. + * + * The following helpers are provided: + * * i2c_of_probe_simple_enable() + * * i2c_of_probe_simple_cleanup_early() + * * i2c_of_probe_simple_cleanup() + */ + +/** + * struct i2c_of_probe_simple_opts - Options for simple I2C component prober callbacks + * @res_node_compatible: Compatible string of device node to retrieve resources from. + * @supply_name: Name of regulator supply. + * @gpio_name: Name of GPIO. NULL if no GPIO line is used. Empty string ("") if GPIO + * line is unnamed. + * @post_power_on_delay_ms: Delay after regulators are powered on. Passed to msleep(). + * @post_gpio_config_delay_ms: Delay after GPIO is configured. Passed to msleep(). + * @gpio_assert_to_enable: %true if GPIO should be asserted, i.e. set to logical high, + * to enable the component. + * + * This describes power sequences common for the class of components supported by the + * simple component prober: + * * @gpio_name is configured to the non-active setting according to @gpio_assert_to_enable. + * * @supply_name regulator supply is enabled. + * * Wait for @post_power_on_delay_ms to pass. + * * @gpio_name is configured to the active setting according to @gpio_assert_to_enable. + * * Wait for @post_gpio_config_delay_ms to pass. + */ +struct i2c_of_probe_simple_opts { + const char *res_node_compatible; + const char *supply_name; + const char *gpio_name; + unsigned int post_power_on_delay_ms; + unsigned int post_gpio_config_delay_ms; + bool gpio_assert_to_enable; +}; + +struct gpio_desc; +struct regulator; + +struct i2c_of_probe_simple_ctx { + /* public: provided by user before helpers are used. */ + const struct i2c_of_probe_simple_opts *opts; + /* private: internal fields for helpers. */ + struct regulator *supply; + struct gpio_desc *gpiod; +}; + +int i2c_of_probe_simple_enable(struct device *dev, struct device_node *bus_node, void *data); +void i2c_of_probe_simple_cleanup_early(struct device *dev, void *data); +void i2c_of_probe_simple_cleanup(struct device *dev, void *data); + +extern struct i2c_of_probe_ops i2c_of_probe_simple_ops; + +#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ + +#endif /* _LINUX_I2C_OF_PROBER_H */ diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 2a1ed05d5782a..12d532b012c5a 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -298,7 +298,9 @@ enum i3c_open_drain_speed { * @I3C_ADDR_SLOT_I2C_DEV: address is assigned to an I2C device * @I3C_ADDR_SLOT_I3C_DEV: address is assigned to an I3C device * @I3C_ADDR_SLOT_STATUS_MASK: address slot mask - * + * @I3C_ADDR_SLOT_EXT_STATUS_MASK: address slot mask with extended information + * @I3C_ADDR_SLOT_EXT_DESIRED: the bitmask represents addresses that are preferred by some devices, + * such as the "assigned-address" property in a device tree source. * On an I3C bus, addresses are assigned dynamically, and we need to know which * addresses are free to use and which ones are already assigned. * @@ -311,8 +313,12 @@ enum i3c_addr_slot_status { I3C_ADDR_SLOT_I2C_DEV, I3C_ADDR_SLOT_I3C_DEV, I3C_ADDR_SLOT_STATUS_MASK = 3, + I3C_ADDR_SLOT_EXT_STATUS_MASK = 7, + I3C_ADDR_SLOT_EXT_DESIRED = BIT(2), }; +#define I3C_ADDR_SLOT_STATUS_BITS 4 + /** * struct i3c_bus - I3C bus object * @cur_master: I3C master currently driving the bus. Since I3C is multi-master @@ -354,7 +360,7 @@ enum i3c_addr_slot_status { struct i3c_bus { struct i3c_dev_desc *cur_master; int id; - unsigned long addrslots[((I2C_MAX_ADDR + 1) * 2) / BITS_PER_LONG]; + unsigned long addrslots[((I2C_MAX_ADDR + 1) * I3C_ADDR_SLOT_STATUS_BITS) / BITS_PER_LONG]; enum i3c_bus_mode mode; struct { unsigned long i3c; diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 37d56914d4857..10be00f3b1206 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -14,12 +14,14 @@ struct iio_dev; enum iio_backend_data_type { IIO_BACKEND_TWOS_COMPLEMENT, IIO_BACKEND_OFFSET_BINARY, + IIO_BACKEND_DATA_UNSIGNED, IIO_BACKEND_DATA_TYPE_MAX }; enum iio_backend_data_source { IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE, IIO_BACKEND_EXTERNAL, + IIO_BACKEND_INTERNAL_RAMP_16BIT, IIO_BACKEND_DATA_SOURCE_MAX }; @@ -89,6 +91,11 @@ enum iio_backend_sample_trigger { * @read_raw: Read a channel attribute from a backend device * @debugfs_print_chan_status: Print channel status into a buffer. * @debugfs_reg_access: Read or write register value of backend. + * @ddr_enable: Enable interface DDR (Double Data Rate) mode. + * @ddr_disable: Disable interface DDR (Double Data Rate) mode. + * @data_stream_enable: Enable data stream. + * @data_stream_disable: Disable data stream. + * @data_transfer_addr: Set data address. **/ struct iio_backend_ops { int (*enable)(struct iio_backend *back); @@ -129,6 +136,11 @@ struct iio_backend_ops { size_t len); int (*debugfs_reg_access)(struct iio_backend *back, unsigned int reg, unsigned int writeval, unsigned int *readval); + int (*ddr_enable)(struct iio_backend *back); + int (*ddr_disable)(struct iio_backend *back); + int (*data_stream_enable)(struct iio_backend *back); + int (*data_stream_disable)(struct iio_backend *back); + int (*data_transfer_addr)(struct iio_backend *back, u32 address); }; /** @@ -164,6 +176,11 @@ int iio_backend_data_sample_trigger(struct iio_backend *back, int devm_iio_backend_request_buffer(struct device *dev, struct iio_backend *back, struct iio_dev *indio_dev); +int iio_backend_ddr_enable(struct iio_backend *back); +int iio_backend_ddr_disable(struct iio_backend *back); +int iio_backend_data_stream_enable(struct iio_backend *back); +int iio_backend_data_stream_disable(struct iio_backend *back); +int iio_backend_data_transfer_addr(struct iio_backend *back, u32 address); ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len); diff --git a/include/linux/iio/driver.h b/include/linux/iio/driver.h index 7a157ed218f60..7f8b55551ed01 100644 --- a/include/linux/iio/driver.h +++ b/include/linux/iio/driver.h @@ -18,7 +18,7 @@ struct iio_map; * @map: array of mappings specifying association of channel with client */ int iio_map_array_register(struct iio_dev *indio_dev, - struct iio_map *map); + const struct iio_map *map); /** * iio_map_array_unregister() - tell the core to remove consumer mappings for @@ -38,6 +38,7 @@ int iio_map_array_unregister(struct iio_dev *indio_dev); * handle de-registration of the IIO map object when the device's refcount goes to * zero. */ -int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps); +int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, + const struct iio_map *maps); #endif diff --git a/include/linux/iio/events.h b/include/linux/iio/events.h index a4558c45a5488..72062a0c7c879 100644 --- a/include/linux/iio/events.h +++ b/include/linux/iio/events.h @@ -10,7 +10,7 @@ #include /** - * IIO_EVENT_CODE() - create event identifier + * _IIO_EVENT_CODE() - create event identifier * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @diff: Whether the event is for an differential channel or not. * @modifier: Modifier for the channel. Should be one of enum iio_modifier. @@ -19,10 +19,13 @@ * @chan: Channel number for non-differential channels. * @chan1: First channel number for differential channels. * @chan2: Second channel number for differential channels. + * + * Drivers should use the specialized macros below instead of using this one + * directly. */ -#define IIO_EVENT_CODE(chan_type, diff, modifier, direction, \ - type, chan, chan1, chan2) \ +#define _IIO_EVENT_CODE(chan_type, diff, modifier, direction, \ + type, chan, chan1, chan2) \ (((u64)type << 56) | ((u64)diff << 55) | \ ((u64)direction << 48) | ((u64)modifier << 40) | \ ((u64)chan_type << 32) | (((u16)chan2) << 16) | ((u16)chan1) | \ @@ -30,7 +33,8 @@ /** - * IIO_MOD_EVENT_CODE() - create event identifier for modified channels + * IIO_MOD_EVENT_CODE() - create event identifier for modified (non + * differential) channels * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @number: Channel number. * @modifier: Modifier for the channel. Should be one of enum iio_modifier. @@ -40,10 +44,11 @@ #define IIO_MOD_EVENT_CODE(chan_type, number, modifier, \ type, direction) \ - IIO_EVENT_CODE(chan_type, 0, modifier, direction, type, number, 0, 0) + _IIO_EVENT_CODE(chan_type, 0, modifier, direction, type, number, 0, 0) /** - * IIO_UNMOD_EVENT_CODE() - create event identifier for unmodified channels + * IIO_UNMOD_EVENT_CODE() - create event identifier for unmodified (non + * differential) channels * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @number: Channel number. * @type: Type of the event. Should be one of enum iio_event_type. @@ -51,6 +56,18 @@ */ #define IIO_UNMOD_EVENT_CODE(chan_type, number, type, direction) \ - IIO_EVENT_CODE(chan_type, 0, 0, direction, type, number, 0, 0) + _IIO_EVENT_CODE(chan_type, 0, 0, direction, type, number, 0, 0) + +/** + * IIO_DIFF_EVENT_CODE() - create event identifier for differential channels + * @chan_type: Type of the channel. Should be one of enum iio_chan_type. + * @chan1: First channel number for differential channels. + * @chan2: Second channel number for differential channels. + * @type: Type of the event. Should be one of enum iio_event_type. + * @direction: Direction of the event. One of enum iio_event_direction. + */ + +#define IIO_DIFF_EVENT_CODE(chan_type, chan1, chan2, type, direction) \ + _IIO_EVENT_CODE(chan_type, 1, 0, direction, type, 0, chan1, chan2) #endif diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index 5aec3945555bf..a89e7e43e4418 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -70,7 +70,7 @@ struct iio_dev_opaque { #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; - unsigned cached_reg_addr; + unsigned int cached_reg_addr; char read_buf[20]; unsigned int read_buf_len; #endif diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 18779b631e907..ae65890d45677 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -282,11 +282,11 @@ struct iio_chan_spec { const struct iio_chan_spec_ext_info *ext_info; const char *extend_name; const char *datasheet_name; - unsigned modified:1; - unsigned indexed:1; - unsigned output:1; - unsigned differential:1; - unsigned has_ext_scan_type:1; + unsigned int modified:1; + unsigned int indexed:1; + unsigned int output:1; + unsigned int differential:1; + unsigned int has_ext_scan_type:1; }; @@ -514,7 +514,7 @@ struct iio_info { const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state); + bool state); int (*read_event_value)(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, @@ -541,13 +541,13 @@ struct iio_info { int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask); int (*debugfs_reg_access)(struct iio_dev *indio_dev, - unsigned reg, unsigned writeval, - unsigned *readval); + unsigned int reg, unsigned int writeval, + unsigned int *readval); int (*fwnode_xlate)(struct iio_dev *indio_dev, const struct fwnode_reference_args *iiospec); - int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned val); + int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned int val); int (*hwfifo_flush_to_buffer)(struct iio_dev *indio_dev, - unsigned count); + unsigned int count); }; /** @@ -609,7 +609,7 @@ struct iio_dev { int scan_bytes; const unsigned long *available_scan_masks; - unsigned __private masklength; + unsigned int __private masklength; const unsigned long *active_scan_mask; bool scan_timestamp; struct iio_trigger *trig; @@ -624,7 +624,7 @@ struct iio_dev { const struct iio_info *info; const struct iio_buffer_setup_ops *setup_ops; - void *priv; + void *__private priv; }; int iio_device_id(struct iio_dev *indio_dev); @@ -785,7 +785,7 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv); /* The information at the returned address is guaranteed to be cacheline aligned */ static inline void *iio_priv(const struct iio_dev *indio_dev) { - return indio_dev->priv; + return ACCESS_PRIVATE(indio_dev, priv); } void iio_device_free(struct iio_dev *indio_dev); @@ -831,6 +831,7 @@ int iio_device_resume_triggering(struct iio_dev *indio_dev); bool iio_read_acpi_mount_matrix(struct device *dev, struct iio_mount_matrix *orientation, char *acpi_method); +const char *iio_get_acpi_device_name_and_data(struct device *dev, const void **data); #else static inline bool iio_read_acpi_mount_matrix(struct device *dev, struct iio_mount_matrix *orientation, @@ -838,7 +839,16 @@ static inline bool iio_read_acpi_mount_matrix(struct device *dev, { return false; } +static inline const char * +iio_get_acpi_device_name_and_data(struct device *dev, const void **data) +{ + return NULL; +} #endif +static inline const char *iio_get_acpi_device_name(struct device *dev) +{ + return iio_get_acpi_device_name_and_data(dev, NULL); +} /** * iio_get_current_scan_type - Get the current scan type for a channel diff --git a/include/linux/input.h b/include/linux/input.h index cd866b020a01d..7d7cb0593a63e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -286,6 +286,10 @@ struct input_handle; * @start: starts handler for given handle. This function is called by * input core right after connect() method and also when a process * that "grabbed" a device releases it + * @passive_observer: set to %true by drivers only interested in observing + * data stream from devices if there are other users present. Such + * drivers will not result in starting underlying hardware device + * when input_open_device() is called for their handles * @legacy_minors: set to %true by drivers using legacy minor ranges * @minor: beginning of range of 32 legacy minors for devices this driver * can provide @@ -321,6 +325,7 @@ struct input_handler { void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); + bool passive_observer; bool legacy_minors; int minor; const char *name; diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h index 593c10a02144e..011860ade268e 100644 --- a/include/linux/io_uring_types.h +++ b/include/linux/io_uring_types.h @@ -336,6 +336,7 @@ struct io_ring_ctx { */ struct { struct llist_head work_llist; + struct llist_head retry_llist; unsigned long check_cq; atomic_t cq_wait_nr; atomic_t cq_timeouts; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d6aaaec3caf46..318d27841130c 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -557,17 +557,19 @@ iommu_copy_struct_from_full_user_array(void *kdst, size_t kdst_entry_size, * @domain_alloc: allocate and return an iommu domain if success. Otherwise * NULL is returned. The domain is not fully initialized until * the caller iommu_domain_alloc() returns. - * @domain_alloc_user: Allocate an iommu domain corresponding to the input - * parameters as defined in include/uapi/linux/iommufd.h. - * Upon success, if the @user_data is valid and the @parent - * points to a kernel-managed domain, the new domain must be - * IOMMU_DOMAIN_NESTED type; otherwise, the @parent must be - * NULL while the @user_data can be optionally provided, the - * new domain must support __IOMMU_DOMAIN_PAGING. - * Upon failure, ERR_PTR must be returned. + * @domain_alloc_paging_flags: Allocate an iommu domain corresponding to the + * input parameters as defined in + * include/uapi/linux/iommufd.h. The @user_data can be + * optionally provided, the new domain must support + * __IOMMU_DOMAIN_PAGING. Upon failure, ERR_PTR must be + * returned. * @domain_alloc_paging: Allocate an iommu_domain that can be used for - * UNMANAGED, DMA, and DMA_FQ domain types. + * UNMANAGED, DMA, and DMA_FQ domain types. This is the + * same as invoking domain_alloc_paging_flags() with + * @flags=0, @user_data=NULL. A driver should implement + * only one of the two ops. * @domain_alloc_sva: Allocate an iommu_domain for Shared Virtual Addressing. + * @domain_alloc_nested: Allocate an iommu_domain for nested translation. * @probe_device: Add device to iommu driver handling * @release_device: Remove device from iommu driver handling * @probe_finalize: Do final setup work after the device is added to an IOMMU @@ -616,12 +618,15 @@ struct iommu_ops { /* Domain allocation and freeing by the iommu driver */ struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); - struct iommu_domain *(*domain_alloc_user)( - struct device *dev, u32 flags, struct iommu_domain *parent, + struct iommu_domain *(*domain_alloc_paging_flags)( + struct device *dev, u32 flags, const struct iommu_user_data *user_data); struct iommu_domain *(*domain_alloc_paging)(struct device *dev); struct iommu_domain *(*domain_alloc_sva)(struct device *dev, struct mm_struct *mm); + struct iommu_domain *(*domain_alloc_nested)( + struct device *dev, struct iommu_domain *parent, u32 flags, + const struct iommu_user_data *user_data); struct iommu_device *(*probe_device)(struct device *dev); void (*release_device)(struct device *dev); @@ -841,7 +846,6 @@ static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather) }; } -extern bool iommu_present(const struct bus_type *bus); extern bool device_iommu_capable(struct device *dev, enum iommu_cap cap); extern bool iommu_group_has_isolated_msi(struct iommu_group *group); struct iommu_domain *iommu_paging_domain_alloc_flags(struct device *dev, unsigned int flags); diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 6e9fb667a1c5a..5385349f0b8a6 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -249,6 +249,38 @@ struct resource *lookup_resource(struct resource *root, resource_size_t start); int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); resource_size_t resource_alignment(struct resource *res); + +/** + * resource_set_size - Calculate resource end address from size and start + * @res: Resource descriptor + * @size: Size of the resource + * + * Calculate the end address for @res based on @size. + * + * Note: The start address of @res must be set when calling this function. + * Prefer resource_set_range() if setting both the start address and @size. + */ +static inline void resource_set_size(struct resource *res, resource_size_t size) +{ + res->end = res->start + size - 1; +} + +/** + * resource_set_range - Set resource start and end addresses + * @res: Resource descriptor + * @start: Start address for the resource + * @size: Size of the resource + * + * Set @res start address and calculate the end address based on @size. + */ +static inline void resource_set_range(struct resource *res, + resource_size_t start, + resource_size_t size) +{ + res->start = start; + resource_set_size(res, size); +} + static inline resource_size_t resource_size(const struct resource *res) { return res->end - res->start + 1; diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 564868bdce898..fd743d4c4b4bd 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -37,7 +37,6 @@ */ #include -#include #include #include #include diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 45be36e5285ff..401439bb21e3e 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -97,6 +97,7 @@ #define KVM_PFN_ERR_HWPOISON (KVM_PFN_ERR_MASK + 1) #define KVM_PFN_ERR_RO_FAULT (KVM_PFN_ERR_MASK + 2) #define KVM_PFN_ERR_SIGPENDING (KVM_PFN_ERR_MASK + 3) +#define KVM_PFN_ERR_NEEDS_IO (KVM_PFN_ERR_MASK + 4) /* * error pfns indicate that the gfn is in slot but faild to @@ -153,13 +154,6 @@ static inline bool kvm_is_error_gpa(gpa_t gpa) return gpa == INVALID_GPA; } -#define KVM_ERR_PTR_BAD_PAGE (ERR_PTR(-ENOENT)) - -static inline bool is_error_page(struct page *page) -{ - return IS_ERR(page); -} - #define KVM_REQUEST_MASK GENMASK(7,0) #define KVM_REQUEST_NO_WAKEUP BIT(8) #define KVM_REQUEST_WAIT BIT(9) @@ -219,6 +213,7 @@ enum kvm_bus { KVM_PIO_BUS, KVM_VIRTIO_CCW_NOTIFY_BUS, KVM_FAST_MMIO_BUS, + KVM_IOCSR_BUS, KVM_NR_BUSES }; @@ -279,21 +274,19 @@ enum { READING_SHADOW_PAGE_TABLES, }; -#define KVM_UNMAPPED_PAGE ((void *) 0x500 + POISON_POINTER_DELTA) - struct kvm_host_map { /* * Only valid if the 'pfn' is managed by the host kernel (i.e. There is * a 'struct page' for it. When using mem= kernel parameter some memory * can be used as guest memory but they are not managed by host * kernel). - * If 'pfn' is not managed by the host kernel, this field is - * initialized to KVM_UNMAPPED_PAGE. */ + struct page *pinned_page; struct page *page; void *hva; kvm_pfn_t pfn; kvm_pfn_t gfn; + bool writable; }; /* @@ -342,7 +335,8 @@ struct kvm_vcpu { #ifndef __KVM_HAVE_ARCH_WQP struct rcuwait wait; #endif - struct pid __rcu *pid; + struct pid *pid; + rwlock_t pid_lock; int sigset_active; sigset_t sigset; unsigned int halt_poll_ns; @@ -1176,6 +1170,10 @@ static inline bool kvm_memslot_iter_is_valid(struct kvm_memslot_iter *iter, gfn_ kvm_memslot_iter_is_valid(iter, end); \ kvm_memslot_iter_next(iter)) +struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); +struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu); +struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn); + /* * KVM_SET_USER_MEMORY_REGION ioctl allows the following operations: * - create a new memory slot @@ -1214,33 +1212,70 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm); void kvm_arch_flush_shadow_memslot(struct kvm *kvm, struct kvm_memory_slot *slot); -int gfn_to_page_many_atomic(struct kvm_memory_slot *slot, gfn_t gfn, - struct page **pages, int nr_pages); +int kvm_prefetch_pages(struct kvm_memory_slot *slot, gfn_t gfn, + struct page **pages, int nr_pages); + +struct page *__gfn_to_page(struct kvm *kvm, gfn_t gfn, bool write); +static inline struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn) +{ + return __gfn_to_page(kvm, gfn, true); +} -struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable); unsigned long gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn); unsigned long gfn_to_hva_memslot_prot(struct kvm_memory_slot *slot, gfn_t gfn, bool *writable); + +static inline void kvm_release_page_unused(struct page *page) +{ + if (!page) + return; + + put_page(page); +} + void kvm_release_page_clean(struct page *page); void kvm_release_page_dirty(struct page *page); -kvm_pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn); -kvm_pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault, - bool *writable); -kvm_pfn_t gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn); -kvm_pfn_t gfn_to_pfn_memslot_atomic(const struct kvm_memory_slot *slot, gfn_t gfn); -kvm_pfn_t __gfn_to_pfn_memslot(const struct kvm_memory_slot *slot, gfn_t gfn, - bool atomic, bool interruptible, bool *async, - bool write_fault, bool *writable, hva_t *hva); - -void kvm_release_pfn_clean(kvm_pfn_t pfn); -void kvm_release_pfn_dirty(kvm_pfn_t pfn); -void kvm_set_pfn_dirty(kvm_pfn_t pfn); -void kvm_set_pfn_accessed(kvm_pfn_t pfn); - -void kvm_release_pfn(kvm_pfn_t pfn, bool dirty); +static inline void kvm_release_faultin_page(struct kvm *kvm, struct page *page, + bool unused, bool dirty) +{ + lockdep_assert_once(lockdep_is_held(&kvm->mmu_lock) || unused); + + if (!page) + return; + + /* + * If the page that KVM got from the *primary MMU* is writable, and KVM + * installed or reused a SPTE, mark the page/folio dirty. Note, this + * may mark a folio dirty even if KVM created a read-only SPTE, e.g. if + * the GFN is write-protected. Folios can't be safely marked dirty + * outside of mmu_lock as doing so could race with writeback on the + * folio. As a result, KVM can't mark folios dirty in the fast page + * fault handler, and so KVM must (somewhat) speculatively mark the + * folio dirty if KVM could locklessly make the SPTE writable. + */ + if (unused) + kvm_release_page_unused(page); + else if (dirty) + kvm_release_page_dirty(page); + else + kvm_release_page_clean(page); +} + +kvm_pfn_t __kvm_faultin_pfn(const struct kvm_memory_slot *slot, gfn_t gfn, + unsigned int foll, bool *writable, + struct page **refcounted_page); + +static inline kvm_pfn_t kvm_faultin_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, + bool write, bool *writable, + struct page **refcounted_page) +{ + return __kvm_faultin_pfn(kvm_vcpu_gfn_to_memslot(vcpu, gfn), gfn, + write ? FOLL_WRITE : 0, writable, refcounted_page); +} + int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, int len); int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len); @@ -1304,17 +1339,28 @@ int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, }) int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); -struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); bool kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn); bool kvm_vcpu_is_visible_gfn(struct kvm_vcpu *vcpu, gfn_t gfn); unsigned long kvm_host_page_size(struct kvm_vcpu *vcpu, gfn_t gfn); void mark_page_dirty_in_slot(struct kvm *kvm, const struct kvm_memory_slot *memslot, gfn_t gfn); void mark_page_dirty(struct kvm *kvm, gfn_t gfn); -struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu); -struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn); -int kvm_vcpu_map(struct kvm_vcpu *vcpu, gpa_t gpa, struct kvm_host_map *map); -void kvm_vcpu_unmap(struct kvm_vcpu *vcpu, struct kvm_host_map *map, bool dirty); +int __kvm_vcpu_map(struct kvm_vcpu *vcpu, gpa_t gpa, struct kvm_host_map *map, + bool writable); +void kvm_vcpu_unmap(struct kvm_vcpu *vcpu, struct kvm_host_map *map); + +static inline int kvm_vcpu_map(struct kvm_vcpu *vcpu, gpa_t gpa, + struct kvm_host_map *map) +{ + return __kvm_vcpu_map(vcpu, gpa, map, true); +} + +static inline int kvm_vcpu_map_readonly(struct kvm_vcpu *vcpu, gpa_t gpa, + struct kvm_host_map *map) +{ + return __kvm_vcpu_map(vcpu, gpa, map, false); +} + unsigned long kvm_vcpu_gfn_to_hva(struct kvm_vcpu *vcpu, gfn_t gfn); unsigned long kvm_vcpu_gfn_to_hva_prot(struct kvm_vcpu *vcpu, gfn_t gfn, bool *writable); int kvm_vcpu_read_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, void *data, int offset, @@ -1686,9 +1732,6 @@ void kvm_arch_sync_events(struct kvm *kvm); int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); -struct page *kvm_pfn_to_refcounted_page(kvm_pfn_t pfn); -bool kvm_is_zone_device_page(struct page *page); - struct kvm_irq_ack_notifier { struct hlist_node link; unsigned gsi; @@ -2382,12 +2425,6 @@ static inline int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) } #endif /* CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE */ -typedef int (*kvm_vm_thread_fn_t)(struct kvm *kvm, uintptr_t data); - -int kvm_vm_create_worker_thread(struct kvm *kvm, kvm_vm_thread_fn_t thread_fn, - uintptr_t data, const char *name, - struct task_struct **thread_ptr); - #ifdef CONFIG_KVM_XFER_TO_GUEST_WORK static inline void kvm_handle_signal_exit(struct kvm_vcpu *vcpu) { @@ -2461,11 +2498,13 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn) #ifdef CONFIG_KVM_PRIVATE_MEM int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot, - gfn_t gfn, kvm_pfn_t *pfn, int *max_order); + gfn_t gfn, kvm_pfn_t *pfn, struct page **page, + int *max_order); #else static inline int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn, - kvm_pfn_t *pfn, int *max_order) + kvm_pfn_t *pfn, struct page **page, + int *max_order) { KVM_BUG_ON(1, kvm); return -EIO; diff --git a/include/linux/list.h b/include/linux/list.h index 5f4b0a39cf46a..29a375889fb8f 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -686,14 +686,6 @@ static inline void list_splice_tail_init(struct list_head *list, #define list_for_each(pos, head) \ for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next) -/** - * list_for_each_reverse - iterate backwards over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each_reverse(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) - /** * list_for_each_rcu - Iterate over a list in an RCU-safe fashion * @pos: the &struct list_head to use as a loop cursor. diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 61c4b9c419042..c8f0f9458f2cc 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -278,9 +278,9 @@ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, int, struct nlm_cookie *, int); __be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct nlm_lock *); -__be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, - struct nlm_host *, struct nlm_lock *, - struct nlm_lock *, struct nlm_cookie *); +__be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, + struct nlm_host *host, struct nlm_lock *lock, + struct nlm_lock *conflock); __be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *); void nlmsvc_retry_blocked(struct svc_rqst *rqstp); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index 80cca94267615..17d53165d9f24 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -73,8 +73,6 @@ struct nlm_args { u32 fsm_mode; }; -typedef struct nlm_args nlm_args; - /* * Generic lockd result */ diff --git a/include/linux/mfd/88pm886.h b/include/linux/mfd/88pm886.h index 133aa302e4929..85eca44f39ab5 100644 --- a/include/linux/mfd/88pm886.h +++ b/include/linux/mfd/88pm886.h @@ -31,6 +31,15 @@ #define PM886_INT_WC BIT(1) #define PM886_INT_MASK_MODE BIT(2) +#define PM886_REG_RTC_CNT1 0xd1 +#define PM886_REG_RTC_CNT2 0xd2 +#define PM886_REG_RTC_CNT3 0xd3 +#define PM886_REG_RTC_CNT4 0xd4 +#define PM886_REG_RTC_SPARE1 0xea +#define PM886_REG_RTC_SPARE2 0xeb +#define PM886_REG_RTC_SPARE3 0xec +#define PM886_REG_RTC_SPARE4 0xed +#define PM886_REG_RTC_SPARE5 0xee #define PM886_REG_RTC_SPARE6 0xef #define PM886_REG_BUCK_EN 0x08 diff --git a/include/linux/min_heap.h b/include/linux/min_heap.h index 43a7b9dcf15e6..e781727c89161 100644 --- a/include/linux/min_heap.h +++ b/include/linux/min_heap.h @@ -38,9 +38,176 @@ struct min_heap_callbacks { void (*swp)(void *lhs, void *rhs, void *args); }; +/** + * is_aligned - is this pointer & size okay for word-wide copying? + * @base: pointer to data + * @size: size of each element + * @align: required alignment (typically 4 or 8) + * + * Returns true if elements can be copied using word loads and stores. + * The size must be a multiple of the alignment, and the base address must + * be if we do not have CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS. + * + * For some reason, gcc doesn't know to optimize "if (a & mask || b & mask)" + * to "if ((a | b) & mask)", so we do that by hand. + */ +__attribute_const__ __always_inline +static bool is_aligned(const void *base, size_t size, unsigned char align) +{ + unsigned char lsbits = (unsigned char)size; + + (void)base; +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + lsbits |= (unsigned char)(uintptr_t)base; +#endif + return (lsbits & (align - 1)) == 0; +} + +/** + * swap_words_32 - swap two elements in 32-bit chunks + * @a: pointer to the first element to swap + * @b: pointer to the second element to swap + * @n: element size (must be a multiple of 4) + * + * Exchange the two objects in memory. This exploits base+index addressing, + * which basically all CPUs have, to minimize loop overhead computations. + * + * For some reason, on x86 gcc 7.3.0 adds a redundant test of n at the + * bottom of the loop, even though the zero flag is still valid from the + * subtract (since the intervening mov instructions don't alter the flags). + * Gcc 8.1.0 doesn't have that problem. + */ +static __always_inline +void swap_words_32(void *a, void *b, size_t n) +{ + do { + u32 t = *(u32 *)(a + (n -= 4)); + *(u32 *)(a + n) = *(u32 *)(b + n); + *(u32 *)(b + n) = t; + } while (n); +} + +/** + * swap_words_64 - swap two elements in 64-bit chunks + * @a: pointer to the first element to swap + * @b: pointer to the second element to swap + * @n: element size (must be a multiple of 8) + * + * Exchange the two objects in memory. This exploits base+index + * addressing, which basically all CPUs have, to minimize loop overhead + * computations. + * + * We'd like to use 64-bit loads if possible. If they're not, emulating + * one requires base+index+4 addressing which x86 has but most other + * processors do not. If CONFIG_64BIT, we definitely have 64-bit loads, + * but it's possible to have 64-bit loads without 64-bit pointers (e.g. + * x32 ABI). Are there any cases the kernel needs to worry about? + */ +static __always_inline +void swap_words_64(void *a, void *b, size_t n) +{ + do { +#ifdef CONFIG_64BIT + u64 t = *(u64 *)(a + (n -= 8)); + *(u64 *)(a + n) = *(u64 *)(b + n); + *(u64 *)(b + n) = t; +#else + /* Use two 32-bit transfers to avoid base+index+4 addressing */ + u32 t = *(u32 *)(a + (n -= 4)); + *(u32 *)(a + n) = *(u32 *)(b + n); + *(u32 *)(b + n) = t; + + t = *(u32 *)(a + (n -= 4)); + *(u32 *)(a + n) = *(u32 *)(b + n); + *(u32 *)(b + n) = t; +#endif + } while (n); +} + +/** + * swap_bytes - swap two elements a byte at a time + * @a: pointer to the first element to swap + * @b: pointer to the second element to swap + * @n: element size + * + * This is the fallback if alignment doesn't allow using larger chunks. + */ +static __always_inline +void swap_bytes(void *a, void *b, size_t n) +{ + do { + char t = ((char *)a)[--n]; + ((char *)a)[n] = ((char *)b)[n]; + ((char *)b)[n] = t; + } while (n); +} + +/* + * The values are arbitrary as long as they can't be confused with + * a pointer, but small integers make for the smallest compare + * instructions. + */ +#define SWAP_WORDS_64 ((void (*)(void *, void *, void *))0) +#define SWAP_WORDS_32 ((void (*)(void *, void *, void *))1) +#define SWAP_BYTES ((void (*)(void *, void *, void *))2) + +/* + * Selects the appropriate swap function based on the element size. + */ +static __always_inline +void *select_swap_func(const void *base, size_t size) +{ + if (is_aligned(base, size, 8)) + return SWAP_WORDS_64; + else if (is_aligned(base, size, 4)) + return SWAP_WORDS_32; + else + return SWAP_BYTES; +} + +static __always_inline +void do_swap(void *a, void *b, size_t size, void (*swap_func)(void *lhs, void *rhs, void *args), + void *priv) +{ + if (swap_func == SWAP_WORDS_64) + swap_words_64(a, b, size); + else if (swap_func == SWAP_WORDS_32) + swap_words_32(a, b, size); + else if (swap_func == SWAP_BYTES) + swap_bytes(a, b, size); + else + swap_func(a, b, priv); +} + +/** + * parent - given the offset of the child, find the offset of the parent. + * @i: the offset of the heap element whose parent is sought. Non-zero. + * @lsbit: a precomputed 1-bit mask, equal to "size & -size" + * @size: size of each element + * + * In terms of array indexes, the parent of element j = @i/@size is simply + * (j-1)/2. But when working in byte offsets, we can't use implicit + * truncation of integer divides. + * + * Fortunately, we only need one bit of the quotient, not the full divide. + * @size has a least significant bit. That bit will be clear if @i is + * an even multiple of @size, and set if it's an odd multiple. + * + * Logically, we're doing "if (i & lsbit) i -= size;", but since the + * branch is unpredictable, it's done with a bit of clever branch-free + * code instead. + */ +__attribute_const__ __always_inline +static size_t parent(size_t i, unsigned int lsbit, size_t size) +{ + i -= size; + i -= size & -(i & lsbit); + return i / 2; +} + /* Initialize a min-heap. */ static __always_inline -void __min_heap_init(min_heap_char *heap, void *data, int size) +void __min_heap_init_inline(min_heap_char *heap, void *data, int size) { heap->nr = 0; heap->size = size; @@ -50,105 +217,114 @@ void __min_heap_init(min_heap_char *heap, void *data, int size) heap->data = heap->preallocated; } -#define min_heap_init(_heap, _data, _size) \ - __min_heap_init((min_heap_char *)_heap, _data, _size) +#define min_heap_init_inline(_heap, _data, _size) \ + __min_heap_init_inline((min_heap_char *)_heap, _data, _size) /* Get the minimum element from the heap. */ static __always_inline -void *__min_heap_peek(struct min_heap_char *heap) +void *__min_heap_peek_inline(struct min_heap_char *heap) { return heap->nr ? heap->data : NULL; } -#define min_heap_peek(_heap) \ - (__minheap_cast(_heap) __min_heap_peek((min_heap_char *)_heap)) +#define min_heap_peek_inline(_heap) \ + (__minheap_cast(_heap) __min_heap_peek_inline((min_heap_char *)_heap)) /* Check if the heap is full. */ static __always_inline -bool __min_heap_full(min_heap_char *heap) +bool __min_heap_full_inline(min_heap_char *heap) { return heap->nr == heap->size; } -#define min_heap_full(_heap) \ - __min_heap_full((min_heap_char *)_heap) +#define min_heap_full_inline(_heap) \ + __min_heap_full_inline((min_heap_char *)_heap) /* Sift the element at pos down the heap. */ static __always_inline -void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size, - const struct min_heap_callbacks *func, void *args) +void __min_heap_sift_down_inline(min_heap_char *heap, int pos, size_t elem_size, + const struct min_heap_callbacks *func, void *args) { - void *left, *right; + const unsigned long lsbit = elem_size & -elem_size; void *data = heap->data; - void *root = data + pos * elem_size; - int i = pos, j; + void (*swp)(void *lhs, void *rhs, void *args) = func->swp; + /* pre-scale counters for performance */ + size_t a = pos * elem_size; + size_t b, c, d; + size_t n = heap->nr * elem_size; + + if (!swp) + swp = select_swap_func(data, elem_size); /* Find the sift-down path all the way to the leaves. */ - for (;;) { - if (i * 2 + 2 >= heap->nr) - break; - left = data + (i * 2 + 1) * elem_size; - right = data + (i * 2 + 2) * elem_size; - i = func->less(left, right, args) ? i * 2 + 1 : i * 2 + 2; - } + for (b = a; c = 2 * b + elem_size, (d = c + elem_size) < n;) + b = func->less(data + c, data + d, args) ? c : d; /* Special case for the last leaf with no sibling. */ - if (i * 2 + 2 == heap->nr) - i = i * 2 + 1; + if (d == n) + b = c; /* Backtrack to the correct location. */ - while (i != pos && func->less(root, data + i * elem_size, args)) - i = (i - 1) / 2; + while (b != a && func->less(data + a, data + b, args)) + b = parent(b, lsbit, elem_size); /* Shift the element into its correct place. */ - j = i; - while (i != pos) { - i = (i - 1) / 2; - func->swp(data + i * elem_size, data + j * elem_size, args); + c = b; + while (b != a) { + b = parent(b, lsbit, elem_size); + do_swap(data + b, data + c, elem_size, swp, args); } } -#define min_heap_sift_down(_heap, _pos, _func, _args) \ - __min_heap_sift_down((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), _func, _args) +#define min_heap_sift_down_inline(_heap, _pos, _func, _args) \ + __min_heap_sift_down_inline((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), \ + _func, _args) /* Sift up ith element from the heap, O(log2(nr)). */ static __always_inline -void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx, - const struct min_heap_callbacks *func, void *args) +void __min_heap_sift_up_inline(min_heap_char *heap, size_t elem_size, size_t idx, + const struct min_heap_callbacks *func, void *args) { + const unsigned long lsbit = elem_size & -elem_size; void *data = heap->data; - size_t parent; + void (*swp)(void *lhs, void *rhs, void *args) = func->swp; + /* pre-scale counters for performance */ + size_t a = idx * elem_size, b; + + if (!swp) + swp = select_swap_func(data, elem_size); - while (idx) { - parent = (idx - 1) / 2; - if (func->less(data + parent * elem_size, data + idx * elem_size, args)) + while (a) { + b = parent(a, lsbit, elem_size); + if (func->less(data + b, data + a, args)) break; - func->swp(data + parent * elem_size, data + idx * elem_size, args); - idx = parent; + do_swap(data + a, data + b, elem_size, swp, args); + a = b; } } -#define min_heap_sift_up(_heap, _idx, _func, _args) \ - __min_heap_sift_up((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args) +#define min_heap_sift_up_inline(_heap, _idx, _func, _args) \ + __min_heap_sift_up_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, \ + _func, _args) /* Floyd's approach to heapification that is O(nr). */ static __always_inline -void __min_heapify_all(min_heap_char *heap, size_t elem_size, - const struct min_heap_callbacks *func, void *args) +void __min_heapify_all_inline(min_heap_char *heap, size_t elem_size, + const struct min_heap_callbacks *func, void *args) { int i; for (i = heap->nr / 2 - 1; i >= 0; i--) - __min_heap_sift_down(heap, i, elem_size, func, args); + __min_heap_sift_down_inline(heap, i, elem_size, func, args); } -#define min_heapify_all(_heap, _func, _args) \ - __min_heapify_all((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) +#define min_heapify_all_inline(_heap, _func, _args) \ + __min_heapify_all_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) /* Remove minimum element from the heap, O(log2(nr)). */ static __always_inline -bool __min_heap_pop(min_heap_char *heap, size_t elem_size, - const struct min_heap_callbacks *func, void *args) +bool __min_heap_pop_inline(min_heap_char *heap, size_t elem_size, + const struct min_heap_callbacks *func, void *args) { void *data = heap->data; @@ -158,13 +334,13 @@ bool __min_heap_pop(min_heap_char *heap, size_t elem_size, /* Place last element at the root (position 0) and then sift down. */ heap->nr--; memcpy(data, data + (heap->nr * elem_size), elem_size); - __min_heap_sift_down(heap, 0, elem_size, func, args); + __min_heap_sift_down_inline(heap, 0, elem_size, func, args); return true; } -#define min_heap_pop(_heap, _func, _args) \ - __min_heap_pop((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) +#define min_heap_pop_inline(_heap, _func, _args) \ + __min_heap_pop_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) /* * Remove the minimum element and then push the given element. The @@ -172,22 +348,21 @@ bool __min_heap_pop(min_heap_char *heap, size_t elem_size, * efficient than a pop followed by a push that does 2. */ static __always_inline -void __min_heap_pop_push(min_heap_char *heap, - const void *element, size_t elem_size, - const struct min_heap_callbacks *func, - void *args) +void __min_heap_pop_push_inline(min_heap_char *heap, const void *element, size_t elem_size, + const struct min_heap_callbacks *func, void *args) { memcpy(heap->data, element, elem_size); - __min_heap_sift_down(heap, 0, elem_size, func, args); + __min_heap_sift_down_inline(heap, 0, elem_size, func, args); } -#define min_heap_pop_push(_heap, _element, _func, _args) \ - __min_heap_pop_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args) +#define min_heap_pop_push_inline(_heap, _element, _func, _args) \ + __min_heap_pop_push_inline((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), \ + _func, _args) /* Push an element on to the heap, O(log2(nr)). */ static __always_inline -bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size, - const struct min_heap_callbacks *func, void *args) +bool __min_heap_push_inline(min_heap_char *heap, const void *element, size_t elem_size, + const struct min_heap_callbacks *func, void *args) { void *data = heap->data; int pos; @@ -201,35 +376,81 @@ bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size, heap->nr++; /* Sift child at pos up. */ - __min_heap_sift_up(heap, elem_size, pos, func, args); + __min_heap_sift_up_inline(heap, elem_size, pos, func, args); return true; } -#define min_heap_push(_heap, _element, _func, _args) \ - __min_heap_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args) +#define min_heap_push_inline(_heap, _element, _func, _args) \ + __min_heap_push_inline((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), \ + _func, _args) /* Remove ith element from the heap, O(log2(nr)). */ static __always_inline -bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx, - const struct min_heap_callbacks *func, void *args) +bool __min_heap_del_inline(min_heap_char *heap, size_t elem_size, size_t idx, + const struct min_heap_callbacks *func, void *args) { void *data = heap->data; + void (*swp)(void *lhs, void *rhs, void *args) = func->swp; if (WARN_ONCE(heap->nr <= 0, "Popping an empty heap")) return false; + if (!swp) + swp = select_swap_func(data, elem_size); + /* Place last element at the root (position 0) and then sift down. */ heap->nr--; if (idx == heap->nr) return true; - func->swp(data + (idx * elem_size), data + (heap->nr * elem_size), args); - __min_heap_sift_up(heap, elem_size, idx, func, args); - __min_heap_sift_down(heap, idx, elem_size, func, args); + do_swap(data + (idx * elem_size), data + (heap->nr * elem_size), elem_size, swp, args); + __min_heap_sift_up_inline(heap, elem_size, idx, func, args); + __min_heap_sift_down_inline(heap, idx, elem_size, func, args); return true; } +#define min_heap_del_inline(_heap, _idx, _func, _args) \ + __min_heap_del_inline((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, \ + _func, _args) + +void __min_heap_init(min_heap_char *heap, void *data, int size); +void *__min_heap_peek(struct min_heap_char *heap); +bool __min_heap_full(min_heap_char *heap); +void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size, + const struct min_heap_callbacks *func, void *args); +void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx, + const struct min_heap_callbacks *func, void *args); +void __min_heapify_all(min_heap_char *heap, size_t elem_size, + const struct min_heap_callbacks *func, void *args); +bool __min_heap_pop(min_heap_char *heap, size_t elem_size, + const struct min_heap_callbacks *func, void *args); +void __min_heap_pop_push(min_heap_char *heap, const void *element, size_t elem_size, + const struct min_heap_callbacks *func, void *args); +bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size, + const struct min_heap_callbacks *func, void *args); +bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx, + const struct min_heap_callbacks *func, void *args); + +#define min_heap_init(_heap, _data, _size) \ + __min_heap_init((min_heap_char *)_heap, _data, _size) +#define min_heap_peek(_heap) \ + (__minheap_cast(_heap) __min_heap_peek((min_heap_char *)_heap)) +#define min_heap_full(_heap) \ + __min_heap_full((min_heap_char *)_heap) +#define min_heap_sift_down(_heap, _pos, _func, _args) \ + __min_heap_sift_down((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), _func, _args) +#define min_heap_sift_up(_heap, _idx, _func, _args) \ + __min_heap_sift_up((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args) +#define min_heapify_all(_heap, _func, _args) \ + __min_heapify_all((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) +#define min_heap_pop(_heap, _func, _args) \ + __min_heap_pop((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) +#define min_heap_pop_push(_heap, _element, _func, _args) \ + __min_heap_pop_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), \ + _func, _args) +#define min_heap_push(_heap, _element, _func, _args) \ + __min_heap_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args) #define min_heap_del(_heap, _idx, _func, _args) \ __min_heap_del((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args) diff --git a/include/linux/misc/keba.h b/include/linux/misc/keba.h index 323b31a847c5b..a81d6fa708518 100644 --- a/include/linux/misc/keba.h +++ b/include/linux/misc/keba.h @@ -7,6 +7,7 @@ #include struct i2c_board_info; +struct spi_board_info; /** * struct keba_i2c_auxdev - KEBA I2C auxiliary device @@ -22,4 +23,50 @@ struct keba_i2c_auxdev { struct i2c_board_info *info; }; +/** + * struct keba_spi_auxdev - KEBA SPI auxiliary device + * @auxdev: auxiliary device object + * @io: address range of SPI controller IO memory + * @info_size: number of SPI devices to be probed + * @info: SPI devices to be probed + */ +struct keba_spi_auxdev { + struct auxiliary_device auxdev; + struct resource io; + int info_size; + struct spi_board_info *info; +}; + +/** + * struct keba_fan_auxdev - KEBA fan auxiliary device + * @auxdev: auxiliary device object + * @io: address range of fan controller IO memory + */ +struct keba_fan_auxdev { + struct auxiliary_device auxdev; + struct resource io; +}; + +/** + * struct keba_batt_auxdev - KEBA battery auxiliary device + * @auxdev: auxiliary device object + * @io: address range of battery controller IO memory + */ +struct keba_batt_auxdev { + struct auxiliary_device auxdev; + struct resource io; +}; + +/** + * struct keba_uart_auxdev - KEBA UART auxiliary device + * @auxdev: auxiliary device object + * @io: address range of UART controller IO memory + * @irq: number of UART controller interrupt + */ +struct keba_uart_auxdev { + struct auxiliary_device auxdev; + struct resource io; + unsigned int irq; +}; + #endif /* _LINUX_MISC_KEBA_H */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 2bbf73eb53e7b..c39c4945946c6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2550,6 +2550,7 @@ struct kvec; struct page *get_dump_page(unsigned long addr); bool folio_mark_dirty(struct folio *folio); +bool folio_mark_dirty_lock(struct folio *folio); bool set_page_dirty(struct page *page); int set_page_dirty_lock(struct page *page); diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h index 39a7714605a79..d7cb1e5ecbda9 100644 --- a/include/linux/mmdebug.h +++ b/include/linux/mmdebug.h @@ -46,7 +46,7 @@ void vma_iter_dump_tree(const struct vma_iterator *vmi); } \ } while (0) #define VM_WARN_ON_ONCE_PAGE(cond, page) ({ \ - static bool __section(".data.once") __warned; \ + static bool __section(".data..once") __warned; \ int __ret_warn_once = !!(cond); \ \ if (unlikely(__ret_warn_once && !__warned)) { \ @@ -66,7 +66,7 @@ void vma_iter_dump_tree(const struct vma_iterator *vmi); unlikely(__ret_warn); \ }) #define VM_WARN_ON_ONCE_FOLIO(cond, folio) ({ \ - static bool __section(".data.once") __warned; \ + static bool __section(".data..once") __warned; \ int __ret_warn_once = !!(cond); \ \ if (unlikely(__ret_warn_once && !__warned)) { \ @@ -77,7 +77,7 @@ void vma_iter_dump_tree(const struct vma_iterator *vmi); unlikely(__ret_warn_once); \ }) #define VM_WARN_ON_ONCE_MM(cond, mm) ({ \ - static bool __section(".data.once") __warned; \ + static bool __section(".data..once") __warned; \ int __ret_warn_once = !!(cond); \ \ if (unlikely(__ret_warn_once && !__warned)) { \ diff --git a/include/linux/module.h b/include/linux/module.h index 2a9386cbdf850..c60ee39cb9b12 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -247,7 +247,7 @@ extern void cleanup_module(void); #ifdef MODULE /* Creates an alias so file2alias.c can find device table. */ #define MODULE_DEVICE_TABLE(type, name) \ -extern typeof(name) __mod_##type##__##name##_device_table \ +extern typeof(name) __mod_device_table__##type##__##name \ __attribute__ ((unused, alias(__stringify(name)))) #else /* !MODULE */ #define MODULE_DEVICE_TABLE(type, name) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 12d8e47bc5a38..559273a0f16d8 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1336,7 +1336,7 @@ struct pnfs_commit_array { struct rcu_head rcu; refcount_t refcount; unsigned int nbuckets; - struct pnfs_commit_bucket buckets[]; + struct pnfs_commit_bucket buckets[] __counted_by(nbuckets); }; struct pnfs_ds_commit_info { diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h index 3982fea799195..9202f4b24343d 100644 --- a/include/linux/nfslocalio.h +++ b/include/linux/nfslocalio.h @@ -55,7 +55,7 @@ struct nfsd_localio_operations { const struct cred *, const struct nfs_fh *, const fmode_t); - void (*nfsd_file_put_local)(struct nfsd_file *); + struct net *(*nfsd_file_put_local)(struct nfsd_file *); struct file *(*nfsd_file_file)(struct nfsd_file *); } ____cacheline_aligned; @@ -66,7 +66,7 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *, struct rpc_clnt *, const struct cred *, const struct nfs_fh *, const fmode_t); -static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) +static inline void nfs_to_nfsd_net_put(struct net *net) { /* * Once reference to nfsd_serv is dropped, NFSD could be @@ -74,10 +74,22 @@ static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) * by always taking RCU. */ rcu_read_lock(); - nfs_to->nfsd_file_put_local(localio); + nfs_to->nfsd_serv_put(net); rcu_read_unlock(); } +static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) +{ + /* + * Must not hold RCU otherwise nfsd_file_put() can easily trigger: + * "Voluntary context switch within RCU read-side critical section!" + * by scheduling deep in underlying filesystem (e.g. XFS). + */ + struct net *net = nfs_to->nfsd_file_put_local(localio); + + nfs_to_nfsd_net_put(net); +} + #else /* CONFIG_NFS_LOCALIO */ static inline void nfsd_localio_ops_init(void) { diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 45702bdcbceb4..b42e647349688 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -237,7 +237,5 @@ static inline int notifier_to_errno(int ret) #define KBD_KEYSYM 0x0004 /* Keyboard keysym */ #define KBD_POST_KEYSYM 0x0005 /* Called after keyboard keysym interpretation */ -extern struct blocking_notifier_head reboot_notifier_list; - #endif /* __KERNEL__ */ #endif /* _LINUX_NOTIFIER_H */ diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 0a6e22038ce36..13377dde4527b 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -389,6 +389,11 @@ enum { NVME_CTRL_CTRATT_PREDICTABLE_LAT = 1 << 5, NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY = 1 << 7, NVME_CTRL_CTRATT_UUID_LIST = 1 << 9, + NVME_CTRL_SGLS_BYTE_ALIGNED = 1, + NVME_CTRL_SGLS_DWORD_ALIGNED = 2, + NVME_CTRL_SGLS_KSDBDS = 1 << 2, + NVME_CTRL_SGLS_MSDS = 1 << 19, + NVME_CTRL_SGLS_SAOS = 1 << 20, }; struct nvme_lbaf { @@ -2166,4 +2171,13 @@ enum nvme_pr_release_action { NVME_PR_RELEASE_ACT_CLEAR = 1, }; +enum nvme_pr_change_ptpl { + NVME_PR_CPTPL_NO_CHANGE = 0, + NVME_PR_CPTPL_RESV = 1 << 30, + NVME_PR_CPTPL_CLEARED = 2 << 30, + NVME_PR_CPTPL_PERSIST = 3 << 30, +}; + +#define NVME_PR_IGNORE_KEY (1 << 3) + #endif /* _LINUX_NVME_H */ diff --git a/include/linux/of.h b/include/linux/of.h index 086a60f3b8a6d..f921786cb8ac7 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -289,6 +289,9 @@ extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); +extern struct device_node *of_get_next_child_with_prefix(const struct device_node *node, + struct device_node *prev, + const char *prefix); extern struct device_node *of_get_next_available_child( const struct device_node *node, struct device_node *prev); extern struct device_node *of_get_next_reserved_child( @@ -1468,6 +1471,12 @@ static inline int of_property_read_s32(const struct device_node *np, child != NULL; \ child = of_get_next_child(parent, child)) +#define for_each_child_of_node_with_prefix(parent, child, prefix) \ + for (struct device_node *child __free(device_node) = \ + of_get_next_child_with_prefix(parent, NULL, prefix); \ + child != NULL; \ + child = of_get_next_child_with_prefix(parent, child, prefix)) + #define for_each_available_child_of_node(parent, child) \ for (child = of_get_next_available_child(parent, NULL); child != NULL; \ child = of_get_next_available_child(parent, child)) @@ -1651,6 +1660,10 @@ static inline int of_changeset_add_prop_u32(struct of_changeset *ocs, return of_changeset_add_prop_u32_array(ocs, np, prop_name, &val, 1); } +int of_changeset_update_prop_string(struct of_changeset *ocs, + struct device_node *np, + const char *prop_name, const char *str); + int of_changeset_add_prop_bool(struct of_changeset *ocs, struct device_node *np, const char *prop_name); diff --git a/include/linux/once.h b/include/linux/once.h index bc714d414448a..30346fcdc7995 100644 --- a/include/linux/once.h +++ b/include/linux/once.h @@ -46,7 +46,7 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key, #define DO_ONCE(func, ...) \ ({ \ bool ___ret = false; \ - static bool __section(".data.once") ___done = false; \ + static bool __section(".data..once") ___done = false; \ static DEFINE_STATIC_KEY_TRUE(___once_key); \ if (static_branch_unlikely(&___once_key)) { \ unsigned long ___flags; \ @@ -64,7 +64,7 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key, #define DO_ONCE_SLEEPABLE(func, ...) \ ({ \ bool ___ret = false; \ - static bool __section(".data.once") ___done = false; \ + static bool __section(".data..once") ___done = false; \ static DEFINE_STATIC_KEY_TRUE(___once_key); \ if (static_branch_unlikely(&___once_key)) { \ ___ret = __do_once_sleepable_start(&___done); \ diff --git a/include/linux/once_lite.h b/include/linux/once_lite.h index b7bce4983638f..27de7bc32a061 100644 --- a/include/linux/once_lite.h +++ b/include/linux/once_lite.h @@ -12,7 +12,7 @@ #define __ONCE_LITE_IF(condition) \ ({ \ - static bool __section(".data.once") __already_done; \ + static bool __section(".data..once") __already_done; \ bool __ret_cond = !!(condition); \ bool __ret_once = false; \ \ diff --git a/include/linux/pci-bwctrl.h b/include/linux/pci-bwctrl.h new file mode 100644 index 0000000000000..cee07127455b3 --- /dev/null +++ b/include/linux/pci-bwctrl.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * PCIe bandwidth controller + * + * Copyright (C) 2023-2024 Intel Corporation + */ + +#ifndef LINUX_PCI_BWCTRL_H +#define LINUX_PCI_BWCTRL_H + +#include + +struct thermal_cooling_device; + +#ifdef CONFIG_PCIE_THERMAL +struct thermal_cooling_device *pcie_cooling_device_register(struct pci_dev *port); +void pcie_cooling_device_unregister(struct thermal_cooling_device *cdev); +#else +static inline struct thermal_cooling_device *pcie_cooling_device_register(struct pci_dev *port) +{ + return NULL; +} +static inline void pcie_cooling_device_unregister(struct thermal_cooling_device *cdev) +{ +} +#endif + +#endif diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 42ef06136bd1a..e818e3fdcded9 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -32,11 +32,43 @@ pci_epc_interface_string(enum pci_epc_interface_type type) } } +/** + * struct pci_epc_map - information about EPC memory for mapping a RC PCI + * address range + * @pci_addr: start address of the RC PCI address range to map + * @pci_size: size of the RC PCI address range mapped from @pci_addr + * @map_pci_addr: RC PCI address used as the first address mapped (may be lower + * than @pci_addr) + * @map_size: size of the controller memory needed for mapping the RC PCI address + * range @map_pci_addr..@pci_addr+@pci_size + * @phys_base: base physical address of the allocated EPC memory for mapping the + * RC PCI address range + * @phys_addr: physical address at which @pci_addr is mapped + * @virt_base: base virtual address of the allocated EPC memory for mapping the + * RC PCI address range + * @virt_addr: virtual address at which @pci_addr is mapped + */ +struct pci_epc_map { + u64 pci_addr; + size_t pci_size; + + u64 map_pci_addr; + size_t map_size; + + phys_addr_t phys_base; + phys_addr_t phys_addr; + void __iomem *virt_base; + void __iomem *virt_addr; +}; + /** * struct pci_epc_ops - set of function pointers for performing EPC operations * @write_header: ops to populate configuration space header * @set_bar: ops to configure the BAR * @clear_bar: ops to reset the BAR + * @align_addr: operation to get the mapping address, mapping size and offset + * into a controller memory window needed to map an RC PCI address + * region * @map_addr: ops to map CPU address to PCI address * @unmap_addr: ops to unmap CPU address and PCI address * @set_msi: ops to set the requested number of MSI interrupts in the MSI @@ -61,6 +93,8 @@ struct pci_epc_ops { struct pci_epf_bar *epf_bar); void (*clear_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); + u64 (*align_addr)(struct pci_epc *epc, u64 pci_addr, size_t *size, + size_t *offset); int (*map_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t addr, u64 pci_addr, size_t size); void (*unmap_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, @@ -278,6 +312,10 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, phys_addr_t *phys_addr, size_t size); void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr, void __iomem *virt_addr, size_t size); +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u64 pci_addr, size_t pci_size, struct pci_epc_map *map); +void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_map *map); #else static inline void pci_epc_init_notify(struct pci_epc *epc) diff --git a/include/linux/pci-pwrctl.h b/include/linux/pci-pwrctrl.h similarity index 69% rename from include/linux/pci-pwrctl.h rename to include/linux/pci-pwrctrl.h index 0d23dddf59ec8..7d439b0675e91 100644 --- a/include/linux/pci-pwrctl.h +++ b/include/linux/pci-pwrctrl.h @@ -3,8 +3,8 @@ * Copyright (C) 2024 Linaro Ltd. */ -#ifndef __PCI_PWRCTL_H__ -#define __PCI_PWRCTL_H__ +#ifndef __PCI_PWRCTRL_H__ +#define __PCI_PWRCTRL_H__ #include #include @@ -29,14 +29,14 @@ struct device_link; */ /** - * struct pci_pwrctl - PCI device power control context. + * struct pci_pwrctrl - PCI device power control context. * @dev: Address of the power controlling device. * * An object of this type must be allocated by the PCI power control device and - * passed to the pwrctl subsystem to trigger a bus rescan and setup a device + * passed to the pwrctrl subsystem to trigger a bus rescan and setup a device * link with the device once it's up. */ -struct pci_pwrctl { +struct pci_pwrctrl { struct device *dev; /* Private: don't use. */ @@ -45,10 +45,10 @@ struct pci_pwrctl { struct work_struct work; }; -void pci_pwrctl_init(struct pci_pwrctl *pwrctl, struct device *dev); -int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl); -void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl); -int devm_pci_pwrctl_device_set_ready(struct device *dev, - struct pci_pwrctl *pwrctl); +void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev); +int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl); +void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl); +int devm_pci_pwrctrl_device_set_ready(struct device *dev, + struct pci_pwrctrl *pwrctrl); -#endif /* __PCI_PWRCTL_H__ */ +#endif /* __PCI_PWRCTRL_H__ */ diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h new file mode 100644 index 0000000000000..c3e806c13d646 --- /dev/null +++ b/include/linux/pci-tph.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TPH (TLP Processing Hints) + * + * Copyright (C) 2024 Advanced Micro Devices, Inc. + * Eric Van Tassell + * Wei Huang + */ +#ifndef LINUX_PCI_TPH_H +#define LINUX_PCI_TPH_H + +/* + * According to the ECN for PCI Firmware Spec, Steering Tag can be different + * depending on the memory type: Volatile Memory or Persistent Memory. When a + * caller query about a target's Steering Tag, it must provide the target's + * tph_mem_type. ECN link: https://members.pcisig.com/wg/PCI-SIG/document/15470. + */ +enum tph_mem_type { + TPH_MEM_TYPE_VM, /* volatile memory */ + TPH_MEM_TYPE_PM /* persistent memory */ +}; + +#ifdef CONFIG_PCIE_TPH +int pcie_tph_set_st_entry(struct pci_dev *pdev, + unsigned int index, u16 tag); +int pcie_tph_get_cpu_st(struct pci_dev *dev, + enum tph_mem_type mem_type, + unsigned int cpu_uid, u16 *tag); +void pcie_disable_tph(struct pci_dev *pdev); +int pcie_enable_tph(struct pci_dev *pdev, int mode); +#else +static inline int pcie_tph_set_st_entry(struct pci_dev *pdev, + unsigned int index, u16 tag) +{ return -EINVAL; } +static inline int pcie_tph_get_cpu_st(struct pci_dev *dev, + enum tph_mem_type mem_type, + unsigned int cpu_uid, u16 *tag) +{ return -EINVAL; } +static inline void pcie_disable_tph(struct pci_dev *pdev) { } +static inline int pcie_enable_tph(struct pci_dev *pdev, int mode) +{ return -EINVAL; } +#endif + +#endif /* LINUX_PCI_TPH_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 733ff6570e2d5..db9b47ce3eefd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -313,12 +313,20 @@ struct pci_vpd { }; struct irq_affinity; +struct pcie_bwctrl_data; struct pcie_link_state; struct pci_sriov; struct pci_p2pdma; struct rcec_ea; -/* The pci_dev structure describes PCI devices */ +/* struct pci_dev - describes a PCI device + * + * @supported_speeds: PCIe Supported Link Speeds Vector (+ reserved 0 at + * LSB). 0 when the supported speeds cannot be + * determined (e.g., for Root Complex Integrated + * Endpoints without the relevant Capability + * Registers). + */ struct pci_dev { struct list_head bus_list; /* Node in per-bus list */ struct pci_bus *bus; /* Bus this device is on */ @@ -434,6 +442,7 @@ struct pci_dev { unsigned int ats_enabled:1; /* Address Translation Svc */ unsigned int pasid_enabled:1; /* Process Address Space ID */ unsigned int pri_enabled:1; /* Page Request Interface */ + unsigned int tph_enabled:1; /* TLP Processing Hints */ unsigned int is_managed:1; /* Managed via devres */ unsigned int is_msi_managed:1; /* MSI release via devres installed */ unsigned int needs_freset:1; /* Requires fundamental reset */ @@ -495,6 +504,7 @@ struct pci_dev { unsigned int dpc_rp_extensions:1; u8 dpc_rp_log_size; #endif + struct pcie_bwctrl_data *link_bwctrl; #ifdef CONFIG_PCI_ATS union { struct pci_sriov *sriov; /* PF: SR-IOV info */ @@ -522,6 +532,7 @@ struct pci_dev { struct npem *npem; /* Native PCIe Enclosure Management */ #endif u16 acs_cap; /* ACS Capability offset */ + u8 supported_speeds; /* Supported Link Speeds Vector */ phys_addr_t rom; /* Physical address if not from BAR */ size_t romlen; /* Length if not from BAR */ /* @@ -534,6 +545,12 @@ struct pci_dev { /* These methods index pci_reset_fn_methods[] */ u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */ + +#ifdef CONFIG_PCIE_TPH + u16 tph_cap; /* TPH capability offset */ + u8 tph_mode; /* TPH mode */ + u8 tph_req_type; /* TPH requester type */ +#endif }; static inline struct pci_dev *pci_physfn(struct pci_dev *dev) @@ -626,27 +643,6 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge); -/* - * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond - * to P2P or CardBus bridge windows) go in a table. Additional ones (for - * buses below host bridges or subtractive decode bridges) go in the list. - * Use pci_bus_for_each_resource() to iterate through all the resources. - */ - -/* - * PCI_SUBTRACTIVE_DECODE means the bridge forwards the window implicitly - * and there's no way to program the bridge with the details of the window. - * This does not apply to ACPI _CRS windows, even with the _DEC subtractive- - * decode bit set, because they are explicit and can be programmed with _SRS. - */ -#define PCI_SUBTRACTIVE_DECODE 0x1 - -struct pci_bus_resource { - struct list_head list; - struct resource *res; - unsigned int flags; -}; - #define PCI_REGION_FLAG_MASK 0x0fU /* These bits of resource flags tell us the PCI region flags */ struct pci_bus { @@ -1274,6 +1270,7 @@ static inline int pcie_capability_clear_and_set_word(struct pci_dev *dev, { switch (pos) { case PCI_EXP_LNKCTL: + case PCI_EXP_LNKCTL2: case PCI_EXP_RTCTL: return pcie_capability_clear_and_set_word_locked(dev, pos, clear, set); @@ -1498,8 +1495,7 @@ void pci_add_resource(struct list_head *resources, struct resource *res); void pci_add_resource_offset(struct list_head *resources, struct resource *res, resource_size_t offset); void pci_free_resource_list(struct list_head *resources); -void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, - unsigned int flags); +void pci_bus_add_resource(struct pci_bus *bus, struct resource *res); struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n); void pci_bus_remove_resources(struct pci_bus *bus); void pci_bus_remove_resource(struct pci_bus *bus, struct resource *res); @@ -1623,8 +1619,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata); -void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), - void *userdata); int pci_cfg_space_size(struct pci_dev *dev); unsigned char pci_bus_max_busnr(struct pci_bus *bus); void pci_setup_bridge(struct pci_bus *bus); @@ -1786,9 +1780,19 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d, #ifdef CONFIG_PCIEPORTBUS extern bool pcie_ports_disabled; extern bool pcie_ports_native; + +int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req, + bool use_lt); #else #define pcie_ports_disabled true #define pcie_ports_native false + +static inline int pcie_set_target_speed(struct pci_dev *port, + enum pci_bus_speed speed_req, + bool use_lt) +{ + return -EOPNOTSUPP; +} #endif #define PCIE_LINK_STATE_L0S (BIT(0) | BIT(1)) /* Upstr/dwnstr L0s */ @@ -2293,15 +2297,15 @@ static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) { } #endif +int pcim_request_all_regions(struct pci_dev *pdev, const char *name); void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar, const char *name); +void pcim_iounmap_region(struct pci_dev *pdev, int bar); void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr); void __iomem * const *pcim_iomap_table(struct pci_dev *pdev); int pcim_request_region(struct pci_dev *pdev, int bar, const char *name); int pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name); -int pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask, - const char *name); void pcim_iounmap_regions(struct pci_dev *pdev, int mask); void __iomem *pcim_iomap_range(struct pci_dev *pdev, int bar, unsigned long offset, unsigned long len); @@ -2609,6 +2613,12 @@ pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; } static inline bool pci_pr3_present(struct pci_dev *pdev) { return false; } #endif +#if defined(CONFIG_X86) && defined(CONFIG_ACPI) +bool arch_pci_dev_is_removable(struct pci_dev *pdev); +#else +static inline bool arch_pci_dev_is_removable(struct pci_dev *pdev) { return false; } +#endif + #ifdef CONFIG_EEH static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 4cf6aaed5f35d..d2402bf4aea2d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -121,6 +121,7 @@ #define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310 #define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320 #define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330 +#define PCI_CLASS_SERIAL_USB_CDNS 0x0c0380 #define PCI_CLASS_SERIAL_USB_DEVICE 0x0c03fe #define PCI_CLASS_SERIAL_FIBER 0x0c04 #define PCI_CLASS_SERIAL_SMBUS 0x0c05 @@ -2421,6 +2422,9 @@ #define PCI_VENDOR_ID_QCOM 0x17cb #define PCI_VENDOR_ID_CDNS 0x17cd +#define PCI_DEVICE_ID_CDNS_USBSS 0x0100 +#define PCI_DEVICE_ID_CDNS_USB 0x0120 +#define PCI_DEVICE_ID_CDNS_USBSSP 0x0200 #define PCI_VENDOR_ID_ARECA 0x17d3 #define PCI_DEVICE_ID_ARECA_1110 0x1110 @@ -2709,9 +2713,6 @@ #define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 #define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 #define PCI_DEVICE_ID_INTEL_SST_TNG 0x119a -#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb -#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 -#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 #define PCI_DEVICE_ID_INTEL_82437 0x122d #define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 8efce7414fad6..35842d1e38796 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -220,15 +220,20 @@ do { \ (void)__vpp_verify; \ } while (0) +#define PERCPU_PTR(__p) \ +({ \ + unsigned long __pcpu_ptr = (__force unsigned long)(__p); \ + (typeof(*(__p)) __force __kernel *)(__pcpu_ptr); \ +}) + #ifdef CONFIG_SMP /* - * Add an offset to a pointer but keep the pointer as-is. Use RELOC_HIDE() - * to prevent the compiler from making incorrect assumptions about the - * pointer value. The weird cast keeps both GCC and sparse happy. + * Add an offset to a pointer. Use RELOC_HIDE() to prevent the compiler + * from making incorrect assumptions about the pointer value. */ #define SHIFT_PERCPU_PTR(__p, __offset) \ - RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)) + RELOC_HIDE(PERCPU_PTR(__p), (__offset)) #define per_cpu_ptr(ptr, cpu) \ ({ \ @@ -254,13 +259,13 @@ do { \ #else /* CONFIG_SMP */ -#define VERIFY_PERCPU_PTR(__p) \ +#define per_cpu_ptr(ptr, cpu) \ ({ \ - __verify_pcpu_ptr(__p); \ - (typeof(*(__p)) __kernel __force *)(__p); \ + (void)(cpu); \ + __verify_pcpu_ptr(ptr); \ + PERCPU_PTR(ptr); \ }) -#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); }) #define raw_cpu_ptr(ptr) per_cpu_ptr(ptr, 0) #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) diff --git a/include/linux/phy.h b/include/linux/phy.h index 77c6d6451638a..563c462056857 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -602,6 +602,7 @@ struct macsec_ops; * @supported_eee: supported PHY EEE linkmodes * @advertising_eee: Currently advertised EEE linkmodes * @enable_tx_lpi: When True, MAC should transmit LPI to PHY + * @eee_active: phylib private state, indicating that EEE has been negotiated * @eee_cfg: User configuration of EEE * @lp_advertising: Current link partner advertised linkmodes * @host_interfaces: PHY interface modes supported by host @@ -723,6 +724,7 @@ struct phy_device { /* Energy efficient ethernet modes which should be prohibited */ __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_broken_modes); bool enable_tx_lpi; + bool eee_active; struct eee_config eee_cfg; /* Host supported PHY interface types. Should be ignored if empty. */ diff --git a/include/linux/phy/phy-sun4i-usb.h b/include/linux/phy/phy-sun4i-usb.h index 91eb755ee73ba..f3e7b13608e4d 100644 --- a/include/linux/phy/phy-sun4i-usb.h +++ b/include/linux/phy/phy-sun4i-usb.h @@ -11,7 +11,7 @@ /** * sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect * @phy: reference to a sun4i usb phy - * @enabled: wether to enable or disable squelch detect + * @enabled: whether to enable or disable squelch detect */ void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled); diff --git a/include/linux/platform_data/sa11x0-serial.h b/include/linux/platform_data/sa11x0-serial.h index 8b79ab08af45e..a88096bc74e42 100644 --- a/include/linux/platform_data/sa11x0-serial.h +++ b/include/linux/platform_data/sa11x0-serial.h @@ -10,7 +10,6 @@ #define SA11X0_SERIAL_H struct uart_port; -struct uart_info; /* * This is a temporary structure for registering these diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 76cd1f9f13653..222f7530806c5 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -10,7 +10,7 @@ #define _LINUX_PM_WAKEUP_H #ifndef _DEVICE_H_ -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif #include diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 910d407ebe632..b98106e1a90f3 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -236,6 +236,8 @@ struct power_supply_config { char **supplied_to; size_t num_supplicants; + + bool no_wakeup_source; }; /* Description of power supply */ @@ -750,9 +752,9 @@ struct power_supply_battery_info { int temp_alert_max; int temp_min; int temp_max; - struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX]; + const struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX]; int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX]; - struct power_supply_resistance_temp_table *resist_table; + const struct power_supply_resistance_temp_table *resist_table; int resist_table_size; const struct power_supply_vbat_ri_table *vbat2ri_discharging; int vbat2ri_discharging_size; @@ -797,15 +799,15 @@ extern bool power_supply_battery_info_has_prop(struct power_supply_battery_info extern int power_supply_battery_info_get_prop(struct power_supply_battery_info *info, enum power_supply_property psp, union power_supply_propval *val); -extern int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table, +extern int power_supply_ocv2cap_simple(const struct power_supply_battery_ocv_table *table, int table_len, int ocv); -extern struct power_supply_battery_ocv_table * +extern const struct power_supply_battery_ocv_table * power_supply_find_ocv2cap_table(struct power_supply_battery_info *info, int temp, int *table_len); extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info, int ocv, int temp); extern int -power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table, +power_supply_temp2resist_simple(const struct power_supply_resistance_temp_table *table, int table_len, int temp); extern int power_supply_vbat2ri(struct power_supply_battery_info *info, int vbat_uv, bool charging); @@ -863,8 +865,6 @@ static inline int power_supply_set_property(struct power_supply *psy, const union power_supply_propval *val) { return 0; } #endif -extern int power_supply_property_is_writeable(struct power_supply *psy, - enum power_supply_property psp); extern void power_supply_external_power_changed(struct power_supply *psy); extern struct power_supply *__must_check @@ -872,17 +872,9 @@ power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg); extern struct power_supply *__must_check -power_supply_register_no_ws(struct device *parent, - const struct power_supply_desc *desc, - const struct power_supply_config *cfg); -extern struct power_supply *__must_check devm_power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg); -extern struct power_supply *__must_check -devm_power_supply_register_no_ws(struct device *parent, - const struct power_supply_desc *desc, - const struct power_supply_config *cfg); extern void power_supply_unregister(struct power_supply *psy); extern int power_supply_powers(struct power_supply *psy, struct device *dev); @@ -945,19 +937,6 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp) return false; } -#ifdef CONFIG_POWER_SUPPLY_HWMON -int power_supply_add_hwmon_sysfs(struct power_supply *psy); -void power_supply_remove_hwmon_sysfs(struct power_supply *psy); -#else -static inline int power_supply_add_hwmon_sysfs(struct power_supply *psy) -{ - return 0; -} - -static inline -void power_supply_remove_hwmon_sysfs(struct power_supply *psy) {} -#endif - #ifdef CONFIG_SYSFS ssize_t power_supply_charge_behaviour_show(struct device *dev, unsigned int available_behaviours, diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 58d84c59f3dda..48e5c03df1dd8 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -401,7 +401,7 @@ static inline int debug_lockdep_rcu_enabled(void) */ #define RCU_LOCKDEP_WARN(c, s) \ do { \ - static bool __section(".data.unlikely") __warned; \ + static bool __section(".data..unlikely") __warned; \ if (debug_lockdep_rcu_enabled() && (c) && \ debug_lockdep_rcu_enabled() && !__warned) { \ __warned = true; \ diff --git a/include/linux/rcupdate_trace.h b/include/linux/rcupdate_trace.h index eda493200663f..e6c44eb428ab6 100644 --- a/include/linux/rcupdate_trace.h +++ b/include/linux/rcupdate_trace.h @@ -10,6 +10,7 @@ #include #include +#include extern struct lockdep_map rcu_trace_lock_map; @@ -98,4 +99,8 @@ static inline void rcu_read_lock_trace(void) { BUG(); } static inline void rcu_read_unlock_trace(void) { BUG(); } #endif /* #ifdef CONFIG_TASKS_TRACE_RCU */ +DEFINE_LOCK_GUARD_0(rcu_tasks_trace, + rcu_read_lock_trace(), + rcu_read_unlock_trace()) + #endif /* __LINUX_RCUPDATE_TRACE_H */ diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h index 9465d5405fe28..373ba77071c6b 100644 --- a/include/linux/rtc/m48t59.h +++ b/include/linux/rtc/m48t59.h @@ -56,6 +56,9 @@ struct m48t59_plat_data { void __iomem *ioaddr; /* offset to RTC registers, automatically set according to the type */ unsigned int offset; + + /* YY digits (in RTC) are offset, i.e. year is 1900 + yy_offset + YY */ + int yy_offset; }; #endif /* _LINUX_RTC_M48T59_H_ */ diff --git a/include/linux/rtsx_common.h b/include/linux/rtsx_common.h index bf290ad14c57f..da9c8c6b5d50f 100644 --- a/include/linux/rtsx_common.h +++ b/include/linux/rtsx_common.h @@ -12,7 +12,6 @@ #define DRV_NAME_RTSX_PCI "rtsx_pci" #define DRV_NAME_RTSX_PCI_SDMMC "rtsx_pci_sdmmc" -#define DRV_NAME_RTSX_PCI_MS "rtsx_pci_ms" #define RTSX_REG_PAIR(addr, val) (((u32)(addr) << 16) | (u8)(val)) diff --git a/include/linux/rtsx_usb.h b/include/linux/rtsx_usb.h index 3247ed8e9ff0f..f267a06c6b1e7 100644 --- a/include/linux/rtsx_usb.h +++ b/include/linux/rtsx_usb.h @@ -12,6 +12,10 @@ #include +#define DRV_NAME_RTSX_USB "rtsx_usb" +#define DRV_NAME_RTSX_USB_SDMMC "rtsx_usb_sdmmc" +#define DRV_NAME_RTSX_USB_MS "rtsx_usb_ms" + /* related module names */ #define RTSX_USB_SD_CARD 0 #define RTSX_USB_MS_CARD 1 diff --git a/include/linux/rwlock.h b/include/linux/rwlock.h index c0ef596f340b5..5b87c6f4a243b 100644 --- a/include/linux/rwlock.h +++ b/include/linux/rwlock.h @@ -2,7 +2,7 @@ #define __LINUX_RWLOCK_H #ifndef __LINUX_INSIDE_SPINLOCK_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif /* diff --git a/include/linux/rwlock_api_smp.h b/include/linux/rwlock_api_smp.h index dceb0a59b6927..31d3d1116323e 100644 --- a/include/linux/rwlock_api_smp.h +++ b/include/linux/rwlock_api_smp.h @@ -2,7 +2,7 @@ #define __LINUX_RWLOCK_API_SMP_H #ifndef __LINUX_SPINLOCK_API_SMP_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif /* diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index e61d164622db4..c5e2239b550ef 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -273,7 +273,7 @@ static inline void sg_unmark_end(struct scatterlist *sg) } /* - * One 64-bit architectures there is a 4-byte padding in struct scatterlist + * On 64-bit architectures there is a 4-byte padding in struct scatterlist * (assuming also CONFIG_NEED_SG_DMA_LENGTH is set). Use this padding for DMA * flags bits to indicate when a specific dma address is a bus address or the * buffer may have been bounced via SWIOTLB. diff --git a/include/linux/sched.h b/include/linux/sched.h index f0e9e00d3cf52..d380bffee2eff 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1121,9 +1121,12 @@ struct task_struct { /* * executable name, excluding path. * - * - normally initialized setup_new_exec() - * - access it with [gs]et_task_comm() - * - lock it with task_lock() + * - normally initialized begin_new_exec() + * - set it with set_task_comm() + * - strscpy_pad() to ensure it is always NUL-terminated and + * zero-padded + * - task_lock() to ensure the operation is atomic and the name is + * fully updated. */ char comm[TASK_COMM_LEN]; @@ -1939,10 +1942,23 @@ static inline void set_task_comm(struct task_struct *tsk, const char *from) __set_task_comm(tsk, from, false); } -extern char *__get_task_comm(char *to, size_t len, struct task_struct *tsk); +/* + * - Why not use task_lock()? + * User space can randomly change their names anyway, so locking for readers + * doesn't make sense. For writers, locking is probably necessary, as a race + * condition could lead to long-term mixed results. + * The strscpy_pad() in __set_task_comm() can ensure that the task comm is + * always NUL-terminated and zero-padded. Therefore the race condition between + * reader and writer is not an issue. + * + * - BUILD_BUG_ON() can help prevent the buf from being truncated. + * Since the callers don't perform any return value checks, this safeguard is + * necessary. + */ #define get_task_comm(buf, tsk) ({ \ - BUILD_BUG_ON(sizeof(buf) != TASK_COMM_LEN); \ - __get_task_comm(buf, sizeof(buf), tsk); \ + BUILD_BUG_ON(sizeof(buf) < TASK_COMM_LEN); \ + strscpy_pad(buf, (tsk)->comm); \ + buf; \ }) #ifdef CONFIG_SMP diff --git a/include/linux/serio.h b/include/linux/serio.h index bf2191f253509..69a47674af653 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -6,6 +6,7 @@ #define _SERIO_H +#include #include #include #include @@ -161,4 +162,6 @@ static inline void serio_continue_rx(struct serio *serio) spin_unlock_irq(&serio->lock); } +DEFINE_GUARD(serio_pause_rx, struct serio *, serio_pause_rx(_T), serio_continue_rx(_T)) + #endif diff --git a/include/linux/slab.h b/include/linux/slab.h index 0268ea7abf8b9..10a971c2bde31 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -77,7 +77,17 @@ enum _slab_flag_bits { #define SLAB_POISON __SLAB_FLAG_BIT(_SLAB_POISON) /* Indicate a kmalloc slab */ #define SLAB_KMALLOC __SLAB_FLAG_BIT(_SLAB_KMALLOC) -/* Align objs on cache lines */ +/** + * define SLAB_HWCACHE_ALIGN - Align objects on cache line boundaries. + * + * Sufficiently large objects are aligned on cache line boundary. For object + * size smaller than a half of cache line size, the alignment is on the half of + * cache line size. In general, if object size is smaller than 1/2^n of cache + * line size, the alignment is adjusted to 1/2^n. + * + * If explicit alignment is also requested by the respective + * &struct kmem_cache_args field, the greater of both is alignments is applied. + */ #define SLAB_HWCACHE_ALIGN __SLAB_FLAG_BIT(_SLAB_HWCACHE_ALIGN) /* Use GFP_DMA memory */ #define SLAB_CACHE_DMA __SLAB_FLAG_BIT(_SLAB_CACHE_DMA) @@ -87,8 +97,8 @@ enum _slab_flag_bits { #define SLAB_STORE_USER __SLAB_FLAG_BIT(_SLAB_STORE_USER) /* Panic if kmem_cache_create() fails */ #define SLAB_PANIC __SLAB_FLAG_BIT(_SLAB_PANIC) -/* - * SLAB_TYPESAFE_BY_RCU - **WARNING** READ THIS! +/** + * define SLAB_TYPESAFE_BY_RCU - **WARNING** READ THIS! * * This delays freeing the SLAB page by a grace period, it does _NOT_ * delay object freeing. This means that if you do kmem_cache_free() @@ -99,20 +109,22 @@ enum _slab_flag_bits { * stays valid, the trick to using this is relying on an independent * object validation pass. Something like: * - * begin: - * rcu_read_lock(); - * obj = lockless_lookup(key); - * if (obj) { - * if (!try_get_ref(obj)) // might fail for free objects - * rcu_read_unlock(); - * goto begin; + * :: + * + * begin: + * rcu_read_lock(); + * obj = lockless_lookup(key); + * if (obj) { + * if (!try_get_ref(obj)) // might fail for free objects + * rcu_read_unlock(); + * goto begin; * - * if (obj->key != key) { // not the object we expected - * put_ref(obj); - * rcu_read_unlock(); - * goto begin; - * } - * } + * if (obj->key != key) { // not the object we expected + * put_ref(obj); + * rcu_read_unlock(); + * goto begin; + * } + * } * rcu_read_unlock(); * * This is useful if we need to approach a kernel structure obliquely, @@ -137,7 +149,6 @@ enum _slab_flag_bits { * * Note that SLAB_TYPESAFE_BY_RCU was originally named SLAB_DESTROY_BY_RCU. */ -/* Defer freeing slabs to RCU */ #define SLAB_TYPESAFE_BY_RCU __SLAB_FLAG_BIT(_SLAB_TYPESAFE_BY_RCU) /* Trace allocations and frees */ #define SLAB_TRACE __SLAB_FLAG_BIT(_SLAB_TRACE) @@ -170,7 +181,12 @@ enum _slab_flag_bits { #else # define SLAB_FAILSLAB __SLAB_FLAG_UNUSED #endif -/* Account to memcg */ +/** + * define SLAB_ACCOUNT - Account allocations to memcg. + * + * All object allocations from this cache will be memcg accounted, regardless of + * __GFP_ACCOUNT being or not being passed to individual allocations. + */ #ifdef CONFIG_MEMCG # define SLAB_ACCOUNT __SLAB_FLAG_BIT(_SLAB_ACCOUNT) #else @@ -197,7 +213,13 @@ enum _slab_flag_bits { #endif /* The following flags affect the page allocator grouping pages by mobility */ -/* Objects are reclaimable */ +/** + * define SLAB_RECLAIM_ACCOUNT - Objects are reclaimable. + * + * Use this flag for caches that have an associated shrinker. As a result, slab + * pages are allocated with __GFP_RECLAIMABLE, which affects grouping pages by + * mobility, and are accounted in SReclaimable counter in /proc/meminfo + */ #ifndef CONFIG_SLUB_TINY #define SLAB_RECLAIM_ACCOUNT __SLAB_FLAG_BIT(_SLAB_RECLAIM_ACCOUNT) #else diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h index 195debe2b1dbc..3e6c8e9d67aef 100644 --- a/include/linux/sockptr.h +++ b/include/linux/sockptr.h @@ -53,6 +53,8 @@ static inline int copy_from_sockptr_offset(void *dst, sockptr_t src, /* Deprecated. * This is unsafe, unless caller checked user provided optlen. * Prefer copy_safe_from_sockptr() instead. + * + * Returns 0 for success, or number of bytes not copied on error. */ static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 49d690f3d29a5..bd9836690da62 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -4,14 +4,21 @@ #ifndef __SOUNDWIRE_H #define __SOUNDWIRE_H +#include #include -#include +#include +#include #include #include +#include #include -#include +#include +#include #include +struct dentry; +struct fwnode_handle; + struct sdw_bus; struct sdw_slave; @@ -227,64 +234,36 @@ enum sdw_clk_stop_mode { /** * struct sdw_dp0_prop - DP0 properties + * @words: wordlengths supported * @max_word: Maximum number of bits in a Payload Channel Sample, 1 to 64 * (inclusive) * @min_word: Minimum number of bits in a Payload Channel Sample, 1 to 64 * (inclusive) * @num_words: number of wordlengths supported - * @words: wordlengths supported + * @ch_prep_timeout: Port-specific timeout value, in milliseconds * @BRA_flow_controlled: Slave implementation results in an OK_NotReady * response * @simple_ch_prep_sm: If channel prepare sequence is required - * @ch_prep_timeout: Port-specific timeout value, in milliseconds * @imp_def_interrupts: If set, each bit corresponds to support for * implementation-defined interrupts + * @num_lanes: array size of @lane_list + * @lane_list: indicates which Lanes can be used by DP0 * * The wordlengths are specified by Spec as max, min AND number of * discrete values, implementation can define based on the wordlengths they * support */ struct sdw_dp0_prop { + u32 *words; u32 max_word; u32 min_word; u32 num_words; - u32 *words; + u32 ch_prep_timeout; bool BRA_flow_controlled; bool simple_ch_prep_sm; - u32 ch_prep_timeout; bool imp_def_interrupts; -}; - -/** - * struct sdw_dpn_audio_mode - Audio mode properties for DPn - * @bus_min_freq: Minimum bus frequency, in Hz - * @bus_max_freq: Maximum bus frequency, in Hz - * @bus_num_freq: Number of discrete frequencies supported - * @bus_freq: Discrete bus frequencies, in Hz - * @min_freq: Minimum sampling frequency, in Hz - * @max_freq: Maximum sampling bus frequency, in Hz - * @num_freq: Number of discrete sampling frequency supported - * @freq: Discrete sampling frequencies, in Hz - * @prep_ch_behave: Specifies the dependencies between Channel Prepare - * sequence and bus clock configuration - * If 0, Channel Prepare can happen at any Bus clock rate - * If 1, Channel Prepare sequence shall happen only after Bus clock is - * changed to a frequency supported by this mode or compatible modes - * described by the next field - * @glitchless: Bitmap describing possible glitchless transitions from this - * Audio Mode to other Audio Modes - */ -struct sdw_dpn_audio_mode { - u32 bus_min_freq; - u32 bus_max_freq; - u32 bus_num_freq; - u32 *bus_freq; - u32 max_freq; - u32 min_freq; - u32 num_freq; - u32 *freq; - u32 prep_ch_behave; - u32 glitchless; + int num_lanes; + u32 *lane_list; }; /** @@ -299,24 +278,25 @@ struct sdw_dpn_audio_mode { * @type: Data port type. Full, Simplified or Reduced * @max_grouping: Maximum number of samples that can be grouped together for * a full data port - * @simple_ch_prep_sm: If the port supports simplified channel prepare state - * machine * @ch_prep_timeout: Port-specific timeout value, in milliseconds * @imp_def_interrupts: If set, each bit corresponds to support for * implementation-defined interrupts * @max_ch: Maximum channels supported * @min_ch: Minimum channels supported * @num_channels: Number of discrete channels supported - * @channels: Discrete channels supported * @num_ch_combinations: Number of channel combinations supported + * @channels: Discrete channels supported * @ch_combinations: Channel combinations supported + * @lane_list: indicates which Lanes can be used by DPn + * @num_lanes: array size of @lane_list * @modes: SDW mode supported * @max_async_buffer: Number of samples that this port can buffer in * asynchronous modes + * @port_encoding: Payload Channel Sample encoding schemes supported * @block_pack_mode: Type of block port mode supported * @read_only_wordlength: Read Only wordlength field in DPN_BlockCtrl1 register - * @port_encoding: Payload Channel Sample encoding schemes supported - * @audio_modes: Audio modes supported + * @simple_ch_prep_sm: If the port supports simplified channel prepare state + * machine */ struct sdw_dpn_prop { u32 num; @@ -326,25 +306,29 @@ struct sdw_dpn_prop { u32 *words; enum sdw_dpn_type type; u32 max_grouping; - bool simple_ch_prep_sm; u32 ch_prep_timeout; u32 imp_def_interrupts; u32 max_ch; u32 min_ch; u32 num_channels; - u32 *channels; u32 num_ch_combinations; + u32 *channels; u32 *ch_combinations; + u32 *lane_list; + int num_lanes; u32 modes; u32 max_async_buffer; + u32 port_encoding; bool block_pack_mode; bool read_only_wordlength; - u32 port_encoding; - struct sdw_dpn_audio_mode *audio_modes; + bool simple_ch_prep_sm; }; /** * struct sdw_slave_prop - SoundWire Slave properties + * @dp0_prop: Data Port 0 properties + * @src_dpn_prop: Source Data Port N properties + * @sink_dpn_prop: Sink Data Port N properties * @mipi_revision: Spec version of the implementation * @wake_capable: Wake-up events are supported * @test_mode_capable: If test mode is supported @@ -361,23 +345,26 @@ struct sdw_dpn_prop { * SCP_AddrPage2 * @bank_delay_support: Slave implements bank delay/bridge support registers * SCP_BankDelay and SCP_NextFrame + * @lane_control_support: Slave supports lane control * @p15_behave: Slave behavior when the Master attempts a read to the Port15 * alias - * @lane_control_support: Slave supports lane control * @master_count: Number of Masters present on this Slave * @source_ports: Bitmap identifying source ports * @sink_ports: Bitmap identifying sink ports - * @dp0_prop: Data Port 0 properties - * @src_dpn_prop: Source Data Port N properties - * @sink_dpn_prop: Sink Data Port N properties - * @scp_int1_mask: SCP_INT1_MASK desired settings * @quirks: bitmask identifying deltas from the MIPI specification + * @sdca_interrupt_register_list: indicates which sets of SDCA interrupt status + * and masks are supported + * @commit_register_supported: is PCP_Commit register supported + * @scp_int1_mask: SCP_INT1_MASK desired settings * @clock_reg_supported: the Peripheral implements the clock base and scale * registers introduced with the SoundWire 1.2 specification. SDCA devices * do not need to set this boolean property as the registers are required. * @use_domain_irq: call actual IRQ handler on slave, as well as callback */ struct sdw_slave_prop { + struct sdw_dp0_prop *dp0_prop; + struct sdw_dpn_prop *src_dpn_prop; + struct sdw_dpn_prop *sink_dpn_prop; u32 mipi_revision; bool wake_capable; bool test_mode_capable; @@ -389,16 +376,15 @@ struct sdw_slave_prop { bool high_PHY_capable; bool paging_support; bool bank_delay_support; - enum sdw_p15_behave p15_behave; bool lane_control_support; + enum sdw_p15_behave p15_behave; u32 master_count; u32 source_ports; u32 sink_ports; - struct sdw_dp0_prop *dp0_prop; - struct sdw_dpn_prop *src_dpn_prop; - struct sdw_dpn_prop *sink_dpn_prop; - u8 scp_int1_mask; u32 quirks; + u32 sdca_interrupt_register_list; + u8 commit_register_supported; + u8 scp_int1_mask; bool clock_reg_supported; bool use_domain_irq; }; @@ -407,13 +393,14 @@ struct sdw_slave_prop { /** * struct sdw_master_prop - Master properties + * @clk_gears: Clock gears supported + * @clk_freq: Clock frequencies supported, in Hz + * @quirks: bitmask identifying optional behavior beyond the scope of the MIPI specification * @revision: MIPI spec version of the implementation * @clk_stop_modes: Bitmap, bit N set when clock-stop-modeN supported * @max_clk_freq: Maximum Bus clock frequency, in Hz * @num_clk_gears: Number of clock gears supported - * @clk_gears: Clock gears supported * @num_clk_freq: Number of clock frequencies supported, in Hz - * @clk_freq: Clock frequencies supported, in Hz * @default_frame_rate: Controller default Frame rate, in Hz * @default_row: Number of rows * @default_col: Number of columns @@ -422,24 +409,23 @@ struct sdw_slave_prop { * command * @mclk_freq: clock reference passed to SoundWire Master, in Hz. * @hw_disabled: if true, the Master is not functional, typically due to pin-mux - * @quirks: bitmask identifying optional behavior beyond the scope of the MIPI specification */ struct sdw_master_prop { + u32 *clk_gears; + u32 *clk_freq; + u64 quirks; u32 revision; u32 clk_stop_modes; u32 max_clk_freq; u32 num_clk_gears; - u32 *clk_gears; u32 num_clk_freq; - u32 *clk_freq; u32 default_frame_rate; u32 default_row; u32 default_col; - bool dynamic_frame; u32 err_threshold; u32 mclk_freq; + bool dynamic_frame; bool hw_disabled; - u64 quirks; }; /* Definitions for Master quirks */ @@ -631,7 +617,6 @@ struct sdw_slave_ops { int (*clk_stop)(struct sdw_slave *slave, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type); - }; /** @@ -707,8 +692,7 @@ struct sdw_master_device { container_of(d, struct sdw_master_device, dev) struct sdw_driver { - int (*probe)(struct sdw_slave *sdw, - const struct sdw_device_id *id); + int (*probe)(struct sdw_slave *sdw, const struct sdw_device_id *id); int (*remove)(struct sdw_slave *sdw); void (*shutdown)(struct sdw_slave *sdw); @@ -727,7 +711,7 @@ struct sdw_driver { SDW_SLAVE_ENTRY_EXT((_mfg_id), (_part_id), 0, 0, (_drv_data)) int sdw_handle_slave_status(struct sdw_bus *bus, - enum sdw_slave_status status[]); + enum sdw_slave_status status[]); /* * SDW master structures and APIs @@ -809,29 +793,28 @@ struct sdw_enable_ch { */ struct sdw_master_port_ops { int (*dpn_set_port_params)(struct sdw_bus *bus, - struct sdw_port_params *port_params, - unsigned int bank); + struct sdw_port_params *port_params, + unsigned int bank); int (*dpn_set_port_transport_params)(struct sdw_bus *bus, - struct sdw_transport_params *transport_params, - enum sdw_reg_bank bank); - int (*dpn_port_prep)(struct sdw_bus *bus, - struct sdw_prepare_ch *prepare_ch); + struct sdw_transport_params *transport_params, + enum sdw_reg_bank bank); + int (*dpn_port_prep)(struct sdw_bus *bus, struct sdw_prepare_ch *prepare_ch); int (*dpn_port_enable_ch)(struct sdw_bus *bus, - struct sdw_enable_ch *enable_ch, unsigned int bank); + struct sdw_enable_ch *enable_ch, unsigned int bank); }; struct sdw_msg; /** - * struct sdw_defer - SDW deffered message - * @length: message length + * struct sdw_defer - SDW deferred message * @complete: message completion * @msg: SDW message + * @length: message length */ struct sdw_defer { + struct sdw_msg *msg; int length; struct completion complete; - struct sdw_msg *msg; }; /** @@ -852,14 +835,11 @@ struct sdw_defer { */ struct sdw_master_ops { int (*read_prop)(struct sdw_bus *bus); - u64 (*override_adr) - (struct sdw_bus *bus, u64 addr); - enum sdw_command_response (*xfer_msg) - (struct sdw_bus *bus, struct sdw_msg *msg); - enum sdw_command_response (*xfer_msg_defer) - (struct sdw_bus *bus); + u64 (*override_adr)(struct sdw_bus *bus, u64 addr); + enum sdw_command_response (*xfer_msg)(struct sdw_bus *bus, struct sdw_msg *msg); + enum sdw_command_response (*xfer_msg_defer)(struct sdw_bus *bus); int (*set_bus_conf)(struct sdw_bus *bus, - struct sdw_bus_params *params); + struct sdw_bus_params *params); int (*pre_bank_switch)(struct sdw_bus *bus); int (*post_bank_switch)(struct sdw_bus *bus); u32 (*read_ping_status)(struct sdw_bus *bus); @@ -874,68 +854,71 @@ struct sdw_master_ops { * struct sdw_bus - SoundWire bus * @dev: Shortcut to &bus->md->dev to avoid changing the entire code. * @md: Master device - * @controller_id: system-unique controller ID. If set to -1, the bus @id will be used. - * @link_id: Link id number, can be 0 to N, unique for each Controller - * @id: bus system-wide unique id - * @slaves: list of Slaves on this bus - * @assigned: Bitmap for Slave device numbers. - * Bit set implies used number, bit clear implies unused number. + * @bus_lock_key: bus lock key associated to @bus_lock * @bus_lock: bus lock + * @slaves: list of Slaves on this bus + * @msg_lock_key: message lock key associated to @msg_lock * @msg_lock: message lock - * @compute_params: points to Bus resource management implementation - * @ops: Master callback ops - * @port_ops: Master port callback ops - * @params: Current bus parameters - * @prop: Master properties - * @vendor_specific_prop: pointer to non-standard properties * @m_rt_list: List of Master instance of all stream(s) running on Bus. This * is used to compute and program bus bandwidth, clock, frame shape, * transport and port parameters - * @debugfs: Bus debugfs - * @domain: IRQ domain * @defer_msg: Defer message - * @clk_stop_timeout: Clock stop timeout computed - * @bank_switch_timeout: Bank switch timeout computed - * @multi_link: Store bus property that indicates if multi links - * are supported. This flag is populated by drivers after reading - * appropriate firmware (ACPI/DT). + * @params: Current bus parameters + * @stream_refcount: number of streams currently using this bus + * @ops: Master callback ops + * @port_ops: Master port callback ops + * @prop: Master properties + * @vendor_specific_prop: pointer to non-standard properties * @hw_sync_min_links: Number of links used by a stream above which * hardware-based synchronization is required. This value is only * meaningful if multi_link is set. If set to 1, hardware-based * synchronization will be used even if a stream only uses a single * SoundWire segment. - * @stream_refcount: number of streams currently using this bus + * @controller_id: system-unique controller ID. If set to -1, the bus @id will be used. + * @link_id: Link id number, can be 0 to N, unique for each Controller + * @id: bus system-wide unique id + * @compute_params: points to Bus resource management implementation + * @assigned: Bitmap for Slave device numbers. + * Bit set implies used number, bit clear implies unused number. + * @clk_stop_timeout: Clock stop timeout computed + * @bank_switch_timeout: Bank switch timeout computed + * @domain: IRQ domain + * @irq_chip: IRQ chip + * @debugfs: Bus debugfs (optional) + * @multi_link: Store bus property that indicates if multi links + * are supported. This flag is populated by drivers after reading + * appropriate firmware (ACPI/DT). */ struct sdw_bus { struct device *dev; struct sdw_master_device *md; - int controller_id; - unsigned int link_id; - int id; - struct list_head slaves; - DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); - struct mutex bus_lock; struct lock_class_key bus_lock_key; - struct mutex msg_lock; + struct mutex bus_lock; + struct list_head slaves; struct lock_class_key msg_lock_key; - int (*compute_params)(struct sdw_bus *bus); + struct mutex msg_lock; + struct list_head m_rt_list; + struct sdw_defer defer_msg; + struct sdw_bus_params params; + int stream_refcount; const struct sdw_master_ops *ops; const struct sdw_master_port_ops *port_ops; - struct sdw_bus_params params; struct sdw_master_prop prop; void *vendor_specific_prop; - struct list_head m_rt_list; + int hw_sync_min_links; + int controller_id; + unsigned int link_id; + int id; + int (*compute_params)(struct sdw_bus *bus); + DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); + unsigned int clk_stop_timeout; + u32 bank_switch_timeout; + struct irq_chip irq_chip; + struct irq_domain *domain; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif - struct irq_chip irq_chip; - struct irq_domain *domain; - struct sdw_defer defer_msg; - unsigned int clk_stop_timeout; - u32 bank_switch_timeout; bool multi_link; - int hw_sync_min_links; - int stream_refcount; }; int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, @@ -1013,18 +996,18 @@ struct sdw_stream_params { * @params: Stream parameters * @state: Current state of the stream * @type: Stream type PCM or PDM + * @m_rt_count: Count of Master runtime(s) in this stream * @master_list: List of Master runtime(s) in this stream. * master_list can contain only one m_rt per Master instance * for a stream - * @m_rt_count: Count of Master runtime(s) in this stream */ struct sdw_stream_runtime { const char *name; struct sdw_stream_params params; enum sdw_stream_state state; enum sdw_stream_type type; - struct list_head master_list; int m_rt_count; + struct list_head master_list; }; struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name); @@ -1033,12 +1016,12 @@ void sdw_release_stream(struct sdw_stream_runtime *stream); int sdw_compute_params(struct sdw_bus *bus); int sdw_stream_add_master(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, - const struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream); + struct sdw_stream_config *stream_config, + const struct sdw_port_config *port_config, + unsigned int num_ports, + struct sdw_stream_runtime *stream); int sdw_stream_remove_master(struct sdw_bus *bus, - struct sdw_stream_runtime *stream); + struct sdw_stream_runtime *stream); int sdw_startup_stream(void *sdw_substream); int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h index 585b4c58a8a6f..799f8578137b4 100644 --- a/include/linux/soundwire/sdw_amd.h +++ b/include/linux/soundwire/sdw_amd.h @@ -27,9 +27,11 @@ #define ACP_SDW0 0 #define ACP_SDW1 1 #define AMD_SDW_MAX_MANAGER_COUNT 2 +#define ACP63_PCI_REV_ID 0x63 struct acp_sdw_pdata { u16 instance; + u32 acp_rev; /* mutex to protect acp common register access */ struct mutex *acp_sdw_lock; }; @@ -66,6 +68,7 @@ struct sdw_amd_dai_runtime { * @instance: SoundWire manager instance * @quirks: SoundWire manager quirks * @wake_en_mask: wake enable mask per SoundWire manager + * @acp_rev: acp pci device revision id * @clk_stopped: flag set to true when clock is stopped * @power_mode_mask: flag interprets amd SoundWire manager power mode * @dai_runtime_array: dai runtime array @@ -94,6 +97,7 @@ struct amd_sdw_manager { u32 quirks; u32 wake_en_mask; u32 power_mode_mask; + u32 acp_rev; bool clk_stopped; struct sdw_amd_dai_runtime **dai_runtime_array; @@ -131,6 +135,7 @@ struct sdw_amd_ctx { * struct sdw_amd_res - Soundwire AMD global resource structure, * typically populated by the DSP driver/Legacy driver * + * @acp_rev: acp pci device revision id * @addr: acp pci device resource start address * @reg_range: ACP register range * @link_mask: bit-wise mask listing links selected by the DSP driver/ @@ -143,6 +148,7 @@ struct sdw_amd_ctx { * @acp_lock: mutex protecting acp common registers access */ struct sdw_amd_res { + u32 acp_rev; u32 addr; u32 reg_range; u32 link_mask; diff --git a/include/linux/spinlock_api_smp.h b/include/linux/spinlock_api_smp.h index 89eb6f4c659c7..9ecb0ab504e32 100644 --- a/include/linux/spinlock_api_smp.h +++ b/include/linux/spinlock_api_smp.h @@ -2,7 +2,7 @@ #define __LINUX_SPINLOCK_API_SMP_H #ifndef __LINUX_INSIDE_SPINLOCK_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif /* diff --git a/include/linux/spinlock_types_up.h b/include/linux/spinlock_types_up.h index 7f86a2016ac5c..fc4e2d017c201 100644 --- a/include/linux/spinlock_types_up.h +++ b/include/linux/spinlock_types_up.h @@ -2,7 +2,7 @@ #define __LINUX_SPINLOCK_TYPES_UP_H #ifndef __LINUX_SPINLOCK_TYPES_RAW_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif /* diff --git a/include/linux/spinlock_up.h b/include/linux/spinlock_up.h index c87204247592f..1e84e71ca495e 100644 --- a/include/linux/spinlock_up.h +++ b/include/linux/spinlock_up.h @@ -2,7 +2,7 @@ #define __LINUX_SPINLOCK_UP_H #ifndef __LINUX_INSIDE_SPINLOCK_H -# error "please don't include this file directly" +# error "Please do not include this file directly." #endif #include /* for cpu_relax() */ diff --git a/include/linux/string.h b/include/linux/string.h index 0dd27afcfaf76..493ac4862c777 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -335,7 +335,6 @@ int __sysfs_match_string(const char * const *array, size_t n, const char *s); #ifdef CONFIG_BINARY_PRINTF int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args); int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf); -int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4); #endif extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 5f775e104f9ab..a2ab813a9800b 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -680,6 +680,27 @@ xdr_stream_decode_u32(struct xdr_stream *xdr, __u32 *ptr) return 0; } +/** + * xdr_stream_decode_be32 - Decode a big-endian 32-bit integer + * @xdr: pointer to xdr_stream + * @ptr: location to store integer + * + * Return values: + * %0 on success + * %-EBADMSG on XDR buffer overflow + */ +static inline ssize_t +xdr_stream_decode_be32(struct xdr_stream *xdr, __be32 *ptr) +{ + const size_t count = sizeof(*ptr); + __be32 *p = xdr_inline_decode(xdr, count); + + if (unlikely(!p)) + return -EBADMSG; + *ptr = *p; + return 0; +} + /** * xdr_stream_decode_u64 - Decode a 64-bit integer * @xdr: pointer to xdr_stream diff --git a/include/linux/sunrpc/xdrgen/_defs.h b/include/linux/sunrpc/xdrgen/_defs.h index be9e623717588..20c7270aa64d2 100644 --- a/include/linux/sunrpc/xdrgen/_defs.h +++ b/include/linux/sunrpc/xdrgen/_defs.h @@ -23,4 +23,13 @@ typedef struct { u8 *data; } opaque; +#define XDR_void (0) +#define XDR_bool (1) +#define XDR_int (1) +#define XDR_unsigned_int (1) +#define XDR_long (1) +#define XDR_unsigned_long (1) +#define XDR_hyper (2) +#define XDR_unsigned_hyper (2) + #endif /* _SUNRPC_XDRGEN__DEFS_H_ */ diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index c4e64dc112063..0f2fcd244523f 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -87,6 +87,11 @@ do { \ * SYSFS_GROUP_VISIBLE() when assigning this callback to * specify separate _group_visible() and _attr_visible() * handlers. + * @bin_size: + * Optional: Function to return the size of a binary attribute + * of the group. Will be called repeatedly for each binary + * attribute in the group. Overwrites the size field embedded + * inside the attribute itself. * @attrs: Pointer to NULL terminated list of attributes. * @bin_attrs: Pointer to NULL terminated list of binary attributes. * Either attrs or bin_attrs or both must be provided. @@ -96,9 +101,15 @@ struct attribute_group { umode_t (*is_visible)(struct kobject *, struct attribute *, int); umode_t (*is_bin_visible)(struct kobject *, - struct bin_attribute *, int); + const struct bin_attribute *, int); + size_t (*bin_size)(struct kobject *, + const struct bin_attribute *, + int); struct attribute **attrs; - struct bin_attribute **bin_attrs; + union { + struct bin_attribute **bin_attrs; + const struct bin_attribute *const *bin_attrs_new; + }; }; #define SYSFS_PREALLOC 010000 @@ -191,22 +202,22 @@ struct attribute_group { * attributes, the group visibility is determined by the function * specified to is_visible() not is_bin_visible() */ -#define DEFINE_SYSFS_BIN_GROUP_VISIBLE(name) \ - static inline umode_t sysfs_group_visible_##name( \ - struct kobject *kobj, struct bin_attribute *attr, int n) \ - { \ - if (n == 0 && !name##_group_visible(kobj)) \ - return SYSFS_GROUP_INVISIBLE; \ - return name##_attr_visible(kobj, attr, n); \ +#define DEFINE_SYSFS_BIN_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, const struct bin_attribute *attr, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return name##_attr_visible(kobj, attr, n); \ } -#define DEFINE_SIMPLE_SYSFS_BIN_GROUP_VISIBLE(name) \ - static inline umode_t sysfs_group_visible_##name( \ - struct kobject *kobj, struct bin_attribute *a, int n) \ - { \ - if (n == 0 && !name##_group_visible(kobj)) \ - return SYSFS_GROUP_INVISIBLE; \ - return a->mode; \ +#define DEFINE_SIMPLE_SYSFS_BIN_GROUP_VISIBLE(name) \ + static inline umode_t sysfs_group_visible_##name( \ + struct kobject *kobj, const struct bin_attribute *a, int n) \ + { \ + if (n == 0 && !name##_group_visible(kobj)) \ + return SYSFS_GROUP_INVISIBLE; \ + return a->mode; \ } #define SYSFS_GROUP_VISIBLE(fn) sysfs_group_visible_##fn @@ -297,11 +308,15 @@ struct bin_attribute { struct address_space *(*f_mapping)(void); ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); + ssize_t (*read_new)(struct file *, struct kobject *, const struct bin_attribute *, + char *, loff_t, size_t); ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); - loff_t (*llseek)(struct file *, struct kobject *, struct bin_attribute *, + ssize_t (*write_new)(struct file *, struct kobject *, + const struct bin_attribute *, char *, loff_t, size_t); + loff_t (*llseek)(struct file *, struct kobject *, const struct bin_attribute *, loff_t, int); - int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, + int (*mmap)(struct file *, struct kobject *, const struct bin_attribute *attr, struct vm_area_struct *vma); }; @@ -317,25 +332,36 @@ struct bin_attribute { */ #define sysfs_bin_attr_init(bin_attr) sysfs_attr_init(&(bin_attr)->attr) +typedef ssize_t __sysfs_bin_rw_handler_new(struct file *, struct kobject *, + const struct bin_attribute *, char *, loff_t, size_t); + /* macros to create static binary attributes easier */ #define __BIN_ATTR(_name, _mode, _read, _write, _size) { \ .attr = { .name = __stringify(_name), .mode = _mode }, \ - .read = _read, \ - .write = _write, \ + .read = _Generic(_read, \ + __sysfs_bin_rw_handler_new * : NULL, \ + default : _read \ + ), \ + .read_new = _Generic(_read, \ + __sysfs_bin_rw_handler_new * : _read, \ + default : NULL \ + ), \ + .write = _Generic(_write, \ + __sysfs_bin_rw_handler_new * : NULL, \ + default : _write \ + ), \ + .write_new = _Generic(_write, \ + __sysfs_bin_rw_handler_new * : _write, \ + default : NULL \ + ), \ .size = _size, \ } -#define __BIN_ATTR_RO(_name, _size) { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .read = _name##_read, \ - .size = _size, \ -} +#define __BIN_ATTR_RO(_name, _size) \ + __BIN_ATTR(_name, 0444, _name##_read, NULL, _size) -#define __BIN_ATTR_WO(_name, _size) { \ - .attr = { .name = __stringify(_name), .mode = 0200 }, \ - .write = _name##_write, \ - .size = _size, \ -} +#define __BIN_ATTR_WO(_name, _size) \ + __BIN_ATTR(_name, 0200, NULL, _name##_write, _size) #define __BIN_ATTR_RW(_name, _size) \ __BIN_ATTR(_name, 0644, _name##_read, _name##_write, _size) @@ -356,11 +382,8 @@ struct bin_attribute bin_attr_##_name = __BIN_ATTR_WO(_name, _size) struct bin_attribute bin_attr_##_name = __BIN_ATTR_RW(_name, _size) -#define __BIN_ATTR_ADMIN_RO(_name, _size) { \ - .attr = { .name = __stringify(_name), .mode = 0400 }, \ - .read = _name##_read, \ - .size = _size, \ -} +#define __BIN_ATTR_ADMIN_RO(_name, _size) \ + __BIN_ATTR(_name, 0400, _name##_read, NULL, _size) #define __BIN_ATTR_ADMIN_RW(_name, _size) \ __BIN_ATTR(_name, 0600, _name##_read, _name##_write, _size) @@ -371,10 +394,8 @@ struct bin_attribute bin_attr_##_name = __BIN_ATTR_ADMIN_RO(_name, _size) #define BIN_ATTR_ADMIN_RW(_name, _size) \ struct bin_attribute bin_attr_##_name = __BIN_ATTR_ADMIN_RW(_name, _size) -#define __BIN_ATTR_SIMPLE_RO(_name, _mode) { \ - .attr = { .name = __stringify(_name), .mode = _mode }, \ - .read = sysfs_bin_attr_simple_read, \ -} +#define __BIN_ATTR_SIMPLE_RO(_name, _mode) \ + __BIN_ATTR(_name, _mode, sysfs_bin_attr_simple_read, NULL, 0) #define BIN_ATTR_SIMPLE_RO(_name) \ struct bin_attribute bin_attr_##_name = __BIN_ATTR_SIMPLE_RO(_name, 0444) diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 016b29a56c875..2a5df5b62cfc7 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -184,6 +184,7 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status); enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, + TRACE_FLAG_NEED_RESCHED_LAZY = 0x02, TRACE_FLAG_NEED_RESCHED = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 425123e921ac7..76d9055b2cff1 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -210,34 +210,16 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) #endif /* CONFIG_HAVE_STATIC_CALL */ /* - * it_func[0] is never NULL because there is at least one element in the array - * when the array itself is non NULL. - * - * With @syscall=0, the tracepoint callback array dereference is - * protected by disabling preemption. - * With @syscall=1, the tracepoint callback array dereference is - * protected by Tasks Trace RCU, which allows probes to handle page - * faults. + * Declare an exported function that Rust code can call to trigger this + * tracepoint. This function does not include the static branch; that is done + * in Rust to avoid a function call when the tracepoint is disabled. */ -#define __DO_TRACE(name, args, cond, syscall) \ - do { \ - int __maybe_unused __idx = 0; \ - \ - if (!(cond)) \ - return; \ - \ - if (syscall) \ - rcu_read_lock_trace(); \ - else \ - preempt_disable_notrace(); \ - \ - __DO_TRACE_CALL(name, TP_ARGS(args)); \ - \ - if (syscall) \ - rcu_read_unlock_trace(); \ - else \ - preempt_enable_notrace(); \ - } while (0) +#define DEFINE_RUST_DO_TRACE(name, proto, args) +#define __DEFINE_RUST_DO_TRACE(name, proto, args) \ + notrace void rust_do_trace_##name(proto) \ + { \ + __rust_do_trace_##name(args); \ + } /* * Make sure the alignment of the structure in the __tracepoints section will @@ -250,10 +232,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) * site if it is not watching, as it will need to be active when the * tracepoint is enabled. */ -#define __DECLARE_TRACE_COMMON(name, proto, args, cond, data_proto) \ +#define __DECLARE_TRACE_COMMON(name, proto, args, data_proto) \ extern int __traceiter_##name(data_proto); \ DECLARE_STATIC_CALL(tp_func_##name, __traceiter_##name); \ extern struct tracepoint __tracepoint_##name; \ + extern void rust_do_trace_##name(proto); \ static inline int \ register_trace_##name(void (*probe)(data_proto), void *data) \ { \ @@ -284,29 +267,43 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) } #define __DECLARE_TRACE(name, proto, args, cond, data_proto) \ - __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), cond, PARAMS(data_proto)) \ + __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \ + static inline void __rust_do_trace_##name(proto) \ + { \ + if (cond) { \ + guard(preempt_notrace)(); \ + __DO_TRACE_CALL(name, TP_ARGS(args)); \ + } \ + } \ static inline void trace_##name(proto) \ { \ - if (static_branch_unlikely(&__tracepoint_##name.key)) \ - __DO_TRACE(name, \ - TP_ARGS(args), \ - TP_CONDITION(cond), 0); \ + if (static_branch_unlikely(&__tracepoint_##name.key)) { \ + if (cond) { \ + guard(preempt_notrace)(); \ + __DO_TRACE_CALL(name, TP_ARGS(args)); \ + } \ + } \ if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ WARN_ONCE(!rcu_is_watching(), \ "RCU not watching for tracepoint"); \ } \ } -#define __DECLARE_TRACE_SYSCALL(name, proto, args, cond, data_proto) \ - __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), cond, PARAMS(data_proto)) \ +#define __DECLARE_TRACE_SYSCALL(name, proto, args, data_proto) \ + __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \ + static inline void __rust_do_trace_##name(proto) \ + { \ + guard(rcu_tasks_trace)(); \ + __DO_TRACE_CALL(name, TP_ARGS(args)); \ + } \ static inline void trace_##name(proto) \ { \ might_fault(); \ - if (static_branch_unlikely(&__tracepoint_##name.key)) \ - __DO_TRACE(name, \ - TP_ARGS(args), \ - TP_CONDITION(cond), 1); \ - if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ + if (static_branch_unlikely(&__tracepoint_##name.key)) { \ + guard(rcu_tasks_trace)(); \ + __DO_TRACE_CALL(name, TP_ARGS(args)); \ + } \ + if (IS_ENABLED(CONFIG_LOCKDEP)) { \ WARN_ONCE(!rcu_is_watching(), \ "RCU not watching for tracepoint"); \ } \ @@ -316,6 +313,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) * We have no guarantee that gcc and the linker won't up-align the tracepoint * structures, so we create an array of pointers that will be used for iteration * on the tracepoints. + * + * it_func[0] is never NULL because there is at least one element in the array + * when the array itself is non NULL. */ #define __DEFINE_TRACE_EXT(_name, _ext, proto, args) \ static const char __tpstrtab_##_name[] \ @@ -354,7 +354,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) void __probestub_##_name(void *__data, proto) \ { \ } \ - DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); + DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); \ + DEFINE_RUST_DO_TRACE(_name, TP_PROTO(proto), TP_ARGS(args)) #define DEFINE_TRACE_FN(_name, _reg, _unreg, _proto, _args) \ static struct tracepoint_ext __tracepoint_ext_##_name = { \ @@ -386,7 +387,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) #else /* !TRACEPOINTS_ENABLED */ -#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \ +#define __DECLARE_TRACE_COMMON(name, proto, args, data_proto) \ static inline void trace_##name(proto) \ { } \ static inline int \ @@ -410,7 +411,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) return false; \ } -#define __DECLARE_TRACE_SYSCALL __DECLARE_TRACE +#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \ + __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) + +#define __DECLARE_TRACE_SYSCALL(name, proto, args, data_proto) \ + __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) #define DEFINE_TRACE_FN(name, reg, unreg, proto, args) #define DEFINE_TRACE_SYSCALL(name, reg, unreg, proto, args) @@ -476,7 +481,6 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) #define DECLARE_TRACE_SYSCALL(name, proto, args) \ __DECLARE_TRACE_SYSCALL(name, PARAMS(proto), PARAMS(args), \ - cpu_online(raw_smp_processor_id()), \ PARAMS(void *__data, proto)) #define TRACE_EVENT_FLAGS(event, flag) diff --git a/include/linux/types.h b/include/linux/types.h index 2bc8766ba20ca..2d7b9ae8714ce 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -115,8 +115,9 @@ typedef u64 u_int64_t; typedef s64 int64_t; #endif -/* this is a special 64bit data type that is 8-byte aligned */ +/* These are the special 64-bit data types that are 8-byte aligned */ #define aligned_u64 __aligned_u64 +#define aligned_s64 __aligned_s64 #define aligned_be64 __aligned_be64 #define aligned_le64 __aligned_le64 diff --git a/include/linux/usb.h b/include/linux/usb.h index 672d8fc2abdb0..cfa8005e24f9f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1129,8 +1129,8 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size) /* ----------------------------------------------------------------------- */ /* Stuff for dynamic usb ids */ +extern struct mutex usb_dynids_lock; struct usb_dynids { - spinlock_t lock; struct list_head list; }; @@ -1243,7 +1243,7 @@ struct usb_driver { unsigned int disable_hub_initiated_lpm:1; unsigned int soft_unbind:1; }; -#define to_usb_driver(d) container_of(d, struct usb_driver, driver) +#define to_usb_driver(d) container_of_const(d, struct usb_driver, driver) /** * struct usb_device_driver - identifies USB device driver to usbcore @@ -1294,8 +1294,7 @@ struct usb_device_driver { unsigned int supports_autosuspend:1; unsigned int generic_subclass:1; }; -#define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ - driver) +#define to_usb_device_driver(d) container_of_const(d, struct usb_device_driver, driver) /** * struct usb_class_driver - identifies a USB driver that wants to use the USB major number diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 5a7f96684ea22..ebdfef124b2bc 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -65,6 +65,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_PHY_VBUS_CONTROL BIT(16) #define CI_HDRC_HAS_PORTSC_PEC_MISSED BIT(17) #define CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS BIT(18) +#define CI_HDRC_HAS_SHORT_PKT_LIMIT BIT(19) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index 2827ce72e5025..8539956bc2be1 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -53,7 +53,7 @@ struct bulk_cb_wrap { __le32 Signature; /* contains 'USBC' */ __u32 Tag; /* unique per command id */ __le32 DataTransferLength; /* size of data */ - __u8 Flags; /* direction in bit 0 */ + __u8 Flags; /* direction in bit 7 */ __u8 Lun; /* LUN normally 0 */ __u8 Length; /* length of the CDB */ __u8 CDB[16]; /* max command */ diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 549275f8ac1b3..d616b88070006 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -87,6 +87,17 @@ enum typec_orientation { TYPEC_ORIENTATION_REVERSE, }; +enum usb_mode { + USB_MODE_NONE, + USB_MODE_USB2, + USB_MODE_USB3, + USB_MODE_USB4 +}; + +#define USB_CAPABILITY_USB2 BIT(0) +#define USB_CAPABILITY_USB3 BIT(1) +#define USB_CAPABILITY_USB4 BIT(2) + /* * struct enter_usb_data - Enter_USB Message details * @eudo: Enter_USB Data Object @@ -209,6 +220,7 @@ struct typec_cable_desc { * @accessory: Audio, Debug or none. * @identity: Discover Identity command data * @pd_revision: USB Power Delivery Specification Revision if supported + * @usb_capability: Supported USB Modes * @attach: Notification about attached USB device * @deattach: Notification about removed USB device * @@ -226,6 +238,7 @@ struct typec_partner_desc { enum typec_accessory accessory; struct usb_pd_identity *identity; u16 pd_revision; /* 0300H = "3.0" */ + u8 usb_capability; void (*attach)(struct typec_partner *partner, struct device *dev); void (*deattach)(struct typec_partner *partner, struct device *dev); @@ -240,6 +253,8 @@ struct typec_partner_desc { * @port_type_set: Set port type * @pd_get: Get available USB Power Delivery Capabilities. * @pd_set: Set USB Power Delivery Capabilities. + * @default_usb_mode_set: USB Mode to be used by default with Enter_USB Message + * @enter_usb_mode: Change the active USB Mode */ struct typec_operations { int (*try_role)(struct typec_port *port, int role); @@ -250,6 +265,8 @@ struct typec_operations { enum typec_port_type type); struct usb_power_delivery **(*pd_get)(struct typec_port *port); int (*pd_set)(struct typec_port *port, struct usb_power_delivery *pd); + int (*default_usb_mode_set)(struct typec_port *port, enum usb_mode mode); + int (*enter_usb_mode)(struct typec_port *port, enum usb_mode mode); }; enum usb_pd_svdm_ver { @@ -267,6 +284,7 @@ enum usb_pd_svdm_ver { * @svdm_version: USB PD Structured VDM version if supported * @prefer_role: Initial role preference (DRP ports). * @accessory: Supported Accessory Modes + * @usb_capability: Supported USB Modes * @fwnode: Optional fwnode of the port * @driver_data: Private pointer for driver specific info * @pd: Optional USB Power Delivery Support @@ -283,6 +301,7 @@ struct typec_capability { int prefer_role; enum typec_accessory accessory[TYPEC_MAX_ACCESSORY]; unsigned int orientation_aware:1; + u8 usb_capability; struct fwnode_handle *fwnode; void *driver_data; @@ -350,6 +369,9 @@ int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_ int typec_partner_set_usb_power_delivery(struct typec_partner *partner, struct usb_power_delivery *pd); +void typec_partner_set_usb_mode(struct typec_partner *partner, enum usb_mode usb_mode); +void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode); + /** * struct typec_connector - Representation of Type-C port for external drivers * @attach: notification about device removal diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h index 6bb460c3e818b..825487fb66faf 100644 --- a/include/linux/util_macros.h +++ b/include/linux/util_macros.h @@ -4,19 +4,6 @@ #include -#define __find_closest(x, a, as, op) \ -({ \ - typeof(as) __fc_i, __fc_as = (as) - 1; \ - typeof(x) __fc_x = (x); \ - typeof(*a) const *__fc_a = (a); \ - for (__fc_i = 0; __fc_i < __fc_as; __fc_i++) { \ - if (__fc_x op DIV_ROUND_CLOSEST(__fc_a[__fc_i] + \ - __fc_a[__fc_i + 1], 2)) \ - break; \ - } \ - (__fc_i); \ -}) - /** * find_closest - locate the closest element in a sorted array * @x: The reference value. @@ -25,8 +12,27 @@ * @as: Size of 'a'. * * Returns the index of the element closest to 'x'. + * Note: If using an array of negative numbers (or mixed positive numbers), + * then be sure that 'x' is of a signed-type to get good results. */ -#define find_closest(x, a, as) __find_closest(x, a, as, <=) +#define find_closest(x, a, as) \ +({ \ + typeof(as) __fc_i, __fc_as = (as) - 1; \ + long __fc_mid_x, __fc_x = (x); \ + long __fc_left, __fc_right; \ + typeof(*a) const *__fc_a = (a); \ + for (__fc_i = 0; __fc_i < __fc_as; __fc_i++) { \ + __fc_mid_x = (__fc_a[__fc_i] + __fc_a[__fc_i + 1]) / 2; \ + if (__fc_x <= __fc_mid_x) { \ + __fc_left = __fc_x - __fc_a[__fc_i]; \ + __fc_right = __fc_a[__fc_i + 1] - __fc_x; \ + if (__fc_right < __fc_left) \ + __fc_i++; \ + break; \ + } \ + } \ + (__fc_i); \ +}) /** * find_closest_descending - locate the closest element in a sorted array @@ -36,9 +42,27 @@ * @as: Size of 'a'. * * Similar to find_closest() but 'a' is expected to be sorted in descending - * order. + * order. The iteration is done in reverse order, so that the comparison + * of '__fc_right' & '__fc_left' also works for unsigned numbers. */ -#define find_closest_descending(x, a, as) __find_closest(x, a, as, >=) +#define find_closest_descending(x, a, as) \ +({ \ + typeof(as) __fc_i, __fc_as = (as) - 1; \ + long __fc_mid_x, __fc_x = (x); \ + long __fc_left, __fc_right; \ + typeof(*a) const *__fc_a = (a); \ + for (__fc_i = __fc_as; __fc_i >= 1; __fc_i--) { \ + __fc_mid_x = (__fc_a[__fc_i] + __fc_a[__fc_i - 1]) / 2; \ + if (__fc_x <= __fc_mid_x) { \ + __fc_left = __fc_x - __fc_a[__fc_i]; \ + __fc_right = __fc_a[__fc_i - 1] - __fc_x; \ + if (__fc_right < __fc_left) \ + __fc_i--; \ + break; \ + } \ + } \ + (__fc_i); \ +}) /** * is_insidevar - check if the @ptr points inside the @var memory range. diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 338e0f5efb4ba..57cc4b07fd175 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -120,6 +120,7 @@ struct virtio_admin_cmd { struct scatterlist *data_sg; struct scatterlist *result_sg; struct completion completion; + u32 result_sg_size; int ret; }; diff --git a/include/linux/virtio_pci_admin.h b/include/linux/virtio_pci_admin.h index f4a100a0fe2e1..dffc92c17ad21 100644 --- a/include/linux/virtio_pci_admin.h +++ b/include/linux/virtio_pci_admin.h @@ -20,4 +20,15 @@ int virtio_pci_admin_legacy_io_notify_info(struct pci_dev *pdev, u64 *bar_offset); #endif +bool virtio_pci_admin_has_dev_parts(struct pci_dev *pdev); +int virtio_pci_admin_mode_set(struct pci_dev *pdev, u8 mode); +int virtio_pci_admin_obj_create(struct pci_dev *pdev, u16 obj_type, u8 operation_type, + u32 *obj_id); +int virtio_pci_admin_obj_destroy(struct pci_dev *pdev, u16 obj_type, u32 id); +int virtio_pci_admin_dev_parts_metadata_get(struct pci_dev *pdev, u16 obj_type, + u32 id, u8 metadata_type, u32 *out); +int virtio_pci_admin_dev_parts_get(struct pci_dev *pdev, u16 obj_type, u32 id, + u8 get_type, struct scatterlist *res_sg, u32 *res_size); +int virtio_pci_admin_dev_parts_set(struct pci_dev *pdev, struct scatterlist *data_sg); + #endif /* _LINUX_VIRTIO_PCI_ADMIN_H */ diff --git a/include/net/net_debug.h b/include/net/net_debug.h index 9fecb1496be3f..47f7a4a878b9f 100644 --- a/include/net/net_debug.h +++ b/include/net/net_debug.h @@ -27,7 +27,7 @@ void netdev_info(const struct net_device *dev, const char *format, ...); #define netdev_level_once(level, dev, fmt, ...) \ do { \ - static bool __section(".data.once") __print_once; \ + static bool __section(".data..once") __print_once; \ \ if (!__print_once) { \ __print_once = true; \ diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 8cd6da0480b79..72d2060904f6a 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -156,6 +156,7 @@ struct tasdevice_priv { struct tasdevice_rca rcabin; struct calidata cali_data; struct tasdevice_fw *fmw; + struct gpio_desc *speaker_id; struct gpio_desc *reset; struct mutex codec_lock; struct regmap *regmap; diff --git a/include/trace/define_trace.h b/include/trace/define_trace.h index 63fea2218afa4..e1c1079f8c8db 100644 --- a/include/trace/define_trace.h +++ b/include/trace/define_trace.h @@ -76,6 +76,13 @@ #define DECLARE_TRACE(name, proto, args) \ DEFINE_TRACE(name, PARAMS(proto), PARAMS(args)) +/* If requested, create helpers for calling these tracepoints from Rust. */ +#ifdef CREATE_RUST_TRACE_POINTS +#undef DEFINE_RUST_DO_TRACE +#define DEFINE_RUST_DO_TRACE(name, proto, args) \ + __DEFINE_RUST_DO_TRACE(name, PARAMS(proto), PARAMS(args)) +#endif + #undef TRACE_INCLUDE #undef __TRACE_INCLUDE @@ -134,6 +141,11 @@ # undef UNDEF_TRACE_INCLUDE_PATH #endif +#ifdef CREATE_RUST_TRACE_POINTS +# undef DEFINE_RUST_DO_TRACE +# define DEFINE_RUST_DO_TRACE(name, proto, args) +#endif + /* We may be processing more files */ #define CREATE_TRACE_POINTS diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 74e40d5d4af42..fc7d0f8ff0788 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -236,6 +236,41 @@ TRACE_EVENT(kvm_mmio, __entry->len, __entry->gpa, __entry->val) ); +#define KVM_TRACE_IOCSR_READ_UNSATISFIED 0 +#define KVM_TRACE_IOCSR_READ 1 +#define KVM_TRACE_IOCSR_WRITE 2 + +#define kvm_trace_symbol_iocsr \ + { KVM_TRACE_IOCSR_READ_UNSATISFIED, "unsatisfied-read" }, \ + { KVM_TRACE_IOCSR_READ, "read" }, \ + { KVM_TRACE_IOCSR_WRITE, "write" } + +TRACE_EVENT(kvm_iocsr, + TP_PROTO(int type, int len, u64 gpa, void *val), + TP_ARGS(type, len, gpa, val), + + TP_STRUCT__entry( + __field( u32, type ) + __field( u32, len ) + __field( u64, gpa ) + __field( u64, val ) + ), + + TP_fast_assign( + __entry->type = type; + __entry->len = len; + __entry->gpa = gpa; + __entry->val = 0; + if (val) + memcpy(&__entry->val, val, + min_t(u32, sizeof(__entry->val), len)); + ), + + TP_printk("iocsr %s len %u gpa 0x%llx val 0x%llx", + __print_symbolic(__entry->type, kvm_trace_symbol_iocsr), + __entry->len, __entry->gpa, __entry->val) +); + #define kvm_fpu_load_symbol \ {0, "unload"}, \ {1, "load"} diff --git a/include/trace/events/rust_sample.h b/include/trace/events/rust_sample.h new file mode 100644 index 0000000000000..dbc80ca2e4655 --- /dev/null +++ b/include/trace/events/rust_sample.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Tracepoints for `samples/rust/rust_print.rs`. + * + * Copyright (C) 2024 Google, Inc. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rust_sample + +#if !defined(_RUST_SAMPLE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _RUST_SAMPLE_TRACE_H + +#include + +TRACE_EVENT(rust_sample_loaded, + TP_PROTO(int magic_number), + TP_ARGS(magic_number), + TP_STRUCT__entry( + __field(int, magic_number) + ), + TP_fast_assign( + __entry->magic_number = magic_number; + ), + TP_printk("magic=%d", __entry->magic_number) +); + +#endif /* _RUST_SAMPLE_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 5e84952166895..b13dc275ef4a7 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -719,7 +719,7 @@ TRACE_EVENT(rpc_xdr_overflow, ), TP_printk(SUNRPC_TRACE_TASK_SPECIFIER - " %sv%d %s requested=%zu p=%p end=%p xdr=[%p,%zu]/%u/[%p,%zu]/%u\n", + " %sv%d %s requested=%zu p=%p end=%p xdr=[%p,%zu]/%u/[%p,%zu]/%u", __entry->task_id, __entry->client_id, __get_str(progname), __entry->version, __get_str(procedure), __entry->requested, __entry->p, __entry->end, @@ -777,7 +777,7 @@ TRACE_EVENT(rpc_xdr_alignment, ), TP_printk(SUNRPC_TRACE_TASK_SPECIFIER - " %sv%d %s offset=%zu copied=%u xdr=[%p,%zu]/%u/[%p,%zu]/%u\n", + " %sv%d %s offset=%zu copied=%u xdr=[%p,%zu]/%u/[%p,%zu]/%u", __entry->task_id, __entry->client_id, __get_str(progname), __entry->version, __get_str(procedure), __entry->offset, __entry->copied, diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index 9adc218fb6dfe..b44069d29cecc 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -451,6 +451,7 @@ typedef struct elf64_shdr { #define NT_MIPS_MSA 0x802 /* MIPS SIMD registers */ #define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ #define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */ +#define NT_RISCV_TAGGED_ADDR_CTRL 0x902 /* RISC-V tagged address control (prctl()) */ #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and status registers */ #define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD Extension registers */ diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h index 955d440be1046..f7aaf8d23e20c 100644 --- a/include/uapi/linux/f2fs.h +++ b/include/uapi/linux/f2fs.h @@ -43,6 +43,7 @@ #define F2FS_IOC_DECOMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 23) #define F2FS_IOC_COMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 24) #define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25) +#define F2FS_IOC_GET_DEV_ALIAS_FILE _IOR(F2FS_IOCTL_MAGIC, 26, __u32) /* * should be same as XFS_IOC_GOINGDOWN. diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index a40833bf2855e..6e6907e63bfc2 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -153,5 +153,6 @@ object identity and may not be usable with open_by_handle_at(2). */ #define AT_HANDLE_MNT_ID_UNIQUE 0x001 /* Return the u64 unique mount ID. */ +#define AT_HANDLE_CONNECTABLE 0x002 /* Request a connectable file handle */ #endif /* _UAPI_LINUX_FCNTL_H */ diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index f2e0b2d50e6b5..12886d4465e48 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -51,6 +51,7 @@ enum iio_chan_type { IIO_DELTA_VELOCITY, IIO_COLORTEMP, IIO_CHROMATICITY, + IIO_ATTENTION, }; enum iio_modifier { diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 4418d01929591..aac9a4f8fa9a6 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -873,20 +873,6 @@ enum { IORING_REG_WAIT_TS = (1U << 0), }; -/* - * Argument for IORING_REGISTER_CQWAIT_REG, registering a region of - * struct io_uring_reg_wait that can be indexed when io_uring_enter(2) is - * called rather than pass in a wait argument structure separately. - */ -struct io_uring_cqwait_reg_arg { - __u32 flags; - __u32 struct_size; - __u32 nr_entries; - __u32 pad; - __u64 user_addr; - __u64 pad2[3]; -}; - /* * Argument for io_uring_enter(2) with * IORING_GETEVENTS | IORING_ENTER_EXT_ARG_REG set, where the actual argument diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 637efc0551453..502ea63b5d2e7 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1158,7 +1158,15 @@ enum kvm_device_type { #define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_RISCV_AIA, #define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA + KVM_DEV_TYPE_LOONGARCH_IPI, +#define KVM_DEV_TYPE_LOONGARCH_IPI KVM_DEV_TYPE_LOONGARCH_IPI + KVM_DEV_TYPE_LOONGARCH_EIOINTC, +#define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC + KVM_DEV_TYPE_LOONGARCH_PCHPIC, +#define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_MAX, + }; struct kvm_vfio_spapr_tce { diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 12323b3334a9c..1601c7ed5fab7 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -340,7 +340,8 @@ #define PCI_MSIX_ENTRY_UPPER_ADDR 0x4 /* Message Upper Address */ #define PCI_MSIX_ENTRY_DATA 0x8 /* Message Data */ #define PCI_MSIX_ENTRY_VECTOR_CTRL 0xc /* Vector Control */ -#define PCI_MSIX_ENTRY_CTRL_MASKBIT 0x00000001 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 0x00000001 /* Mask Bit */ +#define PCI_MSIX_ENTRY_CTRL_ST 0xffff0000 /* Steering Tag */ /* CompactPCI Hotswap Register */ @@ -659,6 +660,7 @@ #define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */ #define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */ #define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ +#define PCI_EXP_DEVCAP2_TPH_COMP_MASK 0x00003000 /* TPH completer support */ #define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ @@ -678,6 +680,7 @@ #define PCI_EXP_DEVSTA2 0x2a /* Device Status 2 */ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 0x2c /* end of v2 EPs w/o link */ #define PCI_EXP_LNKCAP2 0x2c /* Link Capabilities 2 */ +#define PCI_EXP_LNKCAP2_SLS 0x000000fe /* Supported Link Speeds Vector */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ @@ -1023,15 +1026,34 @@ #define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */ #define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */ +/* TPH Completer Support */ +#define PCI_EXP_DEVCAP2_TPH_COMP_NONE 0x0 /* None */ +#define PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY 0x1 /* TPH only */ +#define PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH 0x3 /* TPH and Extended TPH */ + /* TPH Requester */ #define PCI_TPH_CAP 4 /* capability register */ -#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */ -#define PCI_TPH_LOC_NONE 0x000 /* no location */ -#define PCI_TPH_LOC_CAP 0x200 /* in capability */ -#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ -#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST table mask */ -#define PCI_TPH_CAP_ST_SHIFT 16 /* ST table shift */ -#define PCI_TPH_BASE_SIZEOF 0xc /* size with no ST table */ +#define PCI_TPH_CAP_ST_NS 0x00000001 /* No ST Mode Supported */ +#define PCI_TPH_CAP_ST_IV 0x00000002 /* Interrupt Vector Mode Supported */ +#define PCI_TPH_CAP_ST_DS 0x00000004 /* Device Specific Mode Supported */ +#define PCI_TPH_CAP_EXT_TPH 0x00000100 /* Ext TPH Requester Supported */ +#define PCI_TPH_CAP_LOC_MASK 0x00000600 /* ST Table Location */ +#define PCI_TPH_LOC_NONE 0x00000000 /* Not present */ +#define PCI_TPH_LOC_CAP 0x00000200 /* In capability */ +#define PCI_TPH_LOC_MSIX 0x00000400 /* In MSI-X */ +#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST Table Size */ +#define PCI_TPH_CAP_ST_SHIFT 16 /* ST Table Size shift */ +#define PCI_TPH_BASE_SIZEOF 0xc /* Size with no ST table */ + +#define PCI_TPH_CTRL 8 /* control register */ +#define PCI_TPH_CTRL_MODE_SEL_MASK 0x00000007 /* ST Mode Select */ +#define PCI_TPH_ST_NS_MODE 0x0 /* No ST Mode */ +#define PCI_TPH_ST_IV_MODE 0x1 /* Interrupt Vector Mode */ +#define PCI_TPH_ST_DS_MODE 0x2 /* Device Specific Mode */ +#define PCI_TPH_CTRL_REQ_EN_MASK 0x00000300 /* TPH Requester Enable */ +#define PCI_TPH_REQ_DISABLE 0x0 /* No TPH requests allowed */ +#define PCI_TPH_REQ_TPH_ONLY 0x1 /* TPH only requests allowed */ +#define PCI_TPH_REQ_EXT_TPH 0x3 /* Extended TPH requests allowed */ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 0x04 /* DPC Capability */ diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 557a3d2ac1d48..5c6080680cb27 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -230,7 +230,7 @@ struct prctl_mm_map { # define PR_PAC_APDBKEY (1UL << 3) # define PR_PAC_APGAKEY (1UL << 4) -/* Tagged user address controls for arm64 */ +/* Tagged user address controls for arm64 and RISC-V */ #define PR_SET_TAGGED_ADDR_CTRL 55 #define PR_GET_TAGGED_ADDR_CTRL 56 # define PR_TAGGED_ADDR_ENABLE (1UL << 0) @@ -244,6 +244,9 @@ struct prctl_mm_map { # define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) /* Unused; kept only for source compatibility */ # define PR_MTE_TCF_SHIFT 1 +/* RISC-V pointer masking tag length */ +# define PR_PMLEN_SHIFT 24 +# define PR_PMLEN_MASK (0x7fUL << PR_PMLEN_SHIFT) /* Control reclaim behavior when allocating memory */ #define PR_SET_IO_FLUSHER 57 diff --git a/include/uapi/linux/psci.h b/include/uapi/linux/psci.h index 42a40ad3fb622..81759ff385e60 100644 --- a/include/uapi/linux/psci.h +++ b/include/uapi/linux/psci.h @@ -59,6 +59,7 @@ #define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) #define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19) #define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20) +#define PSCI_1_3_FN_SYSTEM_OFF2 PSCI_0_2_FN(21) #define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12) #define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13) @@ -68,6 +69,7 @@ #define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) #define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20) +#define PSCI_1_3_FN64_SYSTEM_OFF2 PSCI_0_2_FN64(21) /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff @@ -100,6 +102,9 @@ #define PSCI_1_1_RESET_TYPE_SYSTEM_WARM_RESET 0 #define PSCI_1_1_RESET_TYPE_VENDOR_START 0x80000000U +/* PSCI v1.3 hibernate type for SYSTEM_OFF2 */ +#define PSCI_1_3_OFF_TYPE_HIBERNATE_OFF BIT(0) + /* PSCI version decoding (independent of PSCI version) */ #define PSCI_VERSION_MAJOR_SHIFT 16 #define PSCI_VERSION_MINOR_MASK \ diff --git a/include/uapi/linux/types.h b/include/uapi/linux/types.h index 6375a06840520..48b933938877d 100644 --- a/include/uapi/linux/types.h +++ b/include/uapi/linux/types.h @@ -53,6 +53,7 @@ typedef __u32 __bitwise __wsum; * No conversions are necessary between 32-bit user-space and a 64-bit kernel. */ #define __aligned_u64 __u64 __attribute__((aligned(8))) +#define __aligned_s64 __s64 __attribute__((aligned(8))) #define __aligned_be64 __be64 __attribute__((aligned(8))) #define __aligned_le64 __le64 __attribute__((aligned(8))) diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h index 2ff0e8a3a683d..526b5155e23c5 100644 --- a/include/uapi/linux/usb/video.h +++ b/include/uapi/linux/usb/video.h @@ -597,5 +597,63 @@ struct UVC_FRAME_MJPEG(n) { \ __le32 dwFrameInterval[n]; \ } __attribute__ ((packed)) +/* Frame Based Payload - 3.1.1. Frame Based Video Format Descriptor */ +struct uvc_format_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFormatIndex; + __u8 bNumFrameDescriptors; + __u8 guidFormat[16]; + __u8 bBitsPerPixel; + __u8 bDefaultFrameIndex; + __u8 bAspectRatioX; + __u8 bAspectRatioY; + __u8 bmInterfaceFlags; + __u8 bCopyProtect; + __u8 bVariableSize; +} __attribute__((__packed__)); + +#define UVC_DT_FORMAT_FRAMEBASED_SIZE 28 + +/* Frame Based Payload - 3.1.2. Frame Based Video Frame Descriptor */ +struct uvc_frame_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwDefaultFrameInterval; + __u8 bFrameIntervalType; + __u32 dwBytesPerLine; + __u32 dwFrameInterval[]; +} __attribute__((__packed__)); + +#define UVC_DT_FRAME_FRAMEBASED_SIZE(n) (26+4*(n)) + +#define UVC_FRAME_FRAMEBASED(n) \ + uvc_frame_framebased_##n + +#define DECLARE_UVC_FRAME_FRAMEBASED(n) \ +struct UVC_FRAME_FRAMEBASED(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bFrameIndex; \ + __u8 bmCapabilities; \ + __u16 wWidth; \ + __u16 wHeight; \ + __u32 dwMinBitRate; \ + __u32 dwMaxBitRate; \ + __u32 dwDefaultFrameInterval; \ + __u8 bFrameIntervalType; \ + __u32 dwBytesPerLine; \ + __u32 dwFrameInterval[n]; \ +} __attribute__ ((packed)) + #endif /* __LINUX_USB_VIDEO_H */ diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index a8208492e8227..1beb317df1b9b 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -40,6 +40,7 @@ #define _LINUX_VIRTIO_PCI_H #include +#include #ifndef VIRTIO_PCI_NO_LEGACY @@ -240,6 +241,17 @@ struct virtio_pci_cfg_cap { #define VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ 0x5 #define VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO 0x6 +/* Device parts access commands. */ +#define VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY 0x7 +#define VIRTIO_ADMIN_CMD_DEVICE_CAP_GET 0x8 +#define VIRTIO_ADMIN_CMD_DRIVER_CAP_SET 0x9 +#define VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE 0xa +#define VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY 0xd +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET 0xe +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET 0xf +#define VIRTIO_ADMIN_CMD_DEV_PARTS_SET 0x10 +#define VIRTIO_ADMIN_CMD_DEV_MODE_SET 0x11 + struct virtio_admin_cmd_hdr { __le16 opcode; /* @@ -286,4 +298,123 @@ struct virtio_admin_cmd_notify_info_result { struct virtio_admin_cmd_notify_info_data entries[VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO]; }; +#define VIRTIO_DEV_PARTS_CAP 0x0000 + +struct virtio_dev_parts_cap { + __u8 get_parts_resource_objects_limit; + __u8 set_parts_resource_objects_limit; +}; + +#define MAX_CAP_ID __KERNEL_DIV_ROUND_UP(VIRTIO_DEV_PARTS_CAP + 1, 64) + +struct virtio_admin_cmd_query_cap_id_result { + __le64 supported_caps[MAX_CAP_ID]; +}; + +struct virtio_admin_cmd_cap_get_data { + __le16 id; + __u8 reserved[6]; +}; + +struct virtio_admin_cmd_cap_set_data { + __le16 id; + __u8 reserved[6]; + __u8 cap_specific_data[]; +}; + +struct virtio_admin_cmd_resource_obj_cmd_hdr { + __le16 type; + __u8 reserved[2]; + __le32 id; /* Indicates unique resource object id per resource object type */ +}; + +struct virtio_admin_cmd_resource_obj_create_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + __le64 flags; + __u8 resource_obj_specific_data[]; +}; + +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS 0 + +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET 0 +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_SET 1 + +struct virtio_resource_obj_dev_parts { + __u8 type; + __u8 reserved[7]; +}; + +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE 0 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_COUNT 1 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_LIST 2 + +struct virtio_admin_cmd_dev_parts_metadata_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + __u8 type; + __u8 reserved[7]; +}; + +#define VIRTIO_DEV_PART_F_OPTIONAL 0 + +struct virtio_dev_part_hdr { + __le16 part_type; + __u8 flags; + __u8 reserved; + union { + struct { + __le32 offset; + __le32 reserved; + } pci_common_cfg; + struct { + __le16 index; + __u8 reserved[6]; + } vq_index; + } selector; + __le32 length; +}; + +struct virtio_dev_part { + struct virtio_dev_part_hdr hdr; + __u8 value[]; +}; + +struct virtio_admin_cmd_dev_parts_metadata_result { + union { + struct { + __le32 size; + __le32 reserved; + } parts_size; + struct { + __le32 count; + __le32 reserved; + } hdr_list_count; + struct { + __le32 count; + __le32 reserved; + struct virtio_dev_part_hdr hdrs[]; + } hdr_list; + }; +}; + +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_SELECTED 0 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_ALL 1 + +struct virtio_admin_cmd_dev_parts_get_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + __u8 type; + __u8 reserved[7]; + struct virtio_dev_part_hdr hdr_list[]; +}; + +struct virtio_admin_cmd_dev_parts_set_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + struct virtio_dev_part parts[]; +}; + +#define VIRTIO_ADMIN_CMD_DEV_MODE_F_STOPPED 0 + +struct virtio_admin_cmd_dev_mode_set_data { + __u8 flags; +}; + #endif diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index 3f68ae3e4330d..d7aca9e61684f 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -299,6 +299,8 @@ struct ufs_pwr_mode_info { * @max_num_rtt: maximum RTT supported by the host * @init: called when the driver is initialized * @exit: called to cleanup everything done in init + * @set_dma_mask: For setting another DMA mask than indicated by the 64AS + * capability bit. * @get_ufs_hci_version: called to get UFS HCI version * @clk_scale_notify: notifies that clks are scaled up/down * @setup_clocks: called before touching any of the controller registers @@ -341,6 +343,7 @@ struct ufs_hba_variant_ops { int (*init)(struct ufs_hba *); void (*exit)(struct ufs_hba *); u32 (*get_ufs_hci_version)(struct ufs_hba *); + int (*set_dma_mask)(struct ufs_hba *); int (*clk_scale_notify)(struct ufs_hba *, bool, enum ufs_notify_change_status); int (*setup_clocks)(struct ufs_hba *, bool, @@ -383,6 +386,7 @@ struct ufs_hba_variant_ops { int (*get_outstanding_cqs)(struct ufs_hba *hba, unsigned long *ocqs); int (*config_esi)(struct ufs_hba *hba); + void (*config_scsi_dev)(struct scsi_device *sdev); }; /* clock gating state */ @@ -623,12 +627,6 @@ enum ufshcd_quirks { */ UFSHCD_QUIRK_SKIP_PH_CONFIGURATION = 1 << 16, - /* - * This quirk needs to be enabled if the host controller has - * 64-bit addressing supported capability but it doesn't work. - */ - UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS = 1 << 17, - /* * This quirk needs to be enabled if the host controller has * auto-hibernate capability but it's FASTAUTO only. @@ -877,9 +875,10 @@ enum ufshcd_mcq_opr { * @tmf_tag_set: TMF tag set. * @tmf_queue: Used to allocate TMF tags. * @tmf_rqs: array with pointers to TMF requests while these are in progress. - * @active_uic_cmd: handle of active UIC command - * @uic_cmd_mutex: mutex for UIC command - * @uic_async_done: completion used during UIC processing + * @active_uic_cmd: pointer to active UIC command. + * @uic_cmd_mutex: mutex used for serializing UIC command processing. + * @uic_async_done: completion used to wait for power mode or hibernation state + * changes. * @ufshcd_state: UFSHCD state * @eh_flags: Error handling flags * @intr_mask: Interrupt Mask Bits @@ -927,7 +926,6 @@ enum ufshcd_mcq_opr { * @wb_mutex: used to serialize devfreq and sysfs write booster toggling * @clk_scaling_lock: used to serialize device commands and clock scaling * @desc_size: descriptor sizes reported by device - * @scsi_block_reqs_cnt: reference counting for scsi block requests * @bsg_dev: struct device associated with the BSG queue * @bsg_queue: BSG queue associated with the UFS controller * @rpm_dev_flush_recheck_work: used to suspend from RPM (runtime power @@ -1088,7 +1086,6 @@ struct ufs_hba { struct mutex wb_mutex; struct rw_semaphore clk_scaling_lock; - atomic_t scsi_block_reqs_cnt; struct device bsg_dev; struct request_queue *bsg_queue; @@ -1320,8 +1317,8 @@ void ufshcd_mcq_write_cqis(struct ufs_hba *hba, u32 val, int i); unsigned long ufshcd_mcq_poll_cqe_lock(struct ufs_hba *hba, struct ufs_hw_queue *hwq); void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba); -void ufshcd_mcq_enable_esi(struct ufs_hba *hba); void ufshcd_mcq_enable(struct ufs_hba *hba); +void ufshcd_mcq_enable_esi(struct ufs_hba *hba); void ufshcd_mcq_config_esi(struct ufs_hba *hba, struct msi_msg *msg); int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table, diff --git a/init/Kconfig b/init/Kconfig index b07f238f3badb..a20e6efd3f0fb 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -120,6 +120,15 @@ config CC_HAS_ASM_INLINE config CC_HAS_NO_PROFILE_FN_ATTR def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror) +config CC_HAS_COUNTED_BY + # TODO: when gcc 15 is released remove the build test and add + # a gcc version check + def_bool $(success,echo 'struct flex { int count; int array[] __attribute__((__counted_by__(count))); };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror) + # clang needs to be at least 19.1.3 to avoid __bdos miscalculations + # https://github.com/llvm/llvm-project/pull/110497 + # https://github.com/llvm/llvm-project/pull/112636 + depends on !(CC_IS_CLANG && CLANG_VERSION < 190103) + config PAHOLE_VERSION int default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE)) @@ -1157,6 +1166,7 @@ config CGROUP_HUGETLB config CPUSETS bool "Cpuset controller" depends on SMP + select UNION_FIND help This option will let you create and manage CPUSETs which allow dynamically partitioning a system into sets of CPUs and diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 8012933998837..06ff41484e29c 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -121,6 +121,7 @@ #define IO_COMPL_BATCH 32 #define IO_REQ_ALLOC_BATCH 8 +#define IO_LOCAL_TW_DEFAULT_MAX 20 struct io_defer_entry { struct list_head list; @@ -1255,12 +1256,14 @@ static void __cold io_move_task_work_from_local(struct io_ring_ctx *ctx) struct llist_node *node = llist_del_all(&ctx->work_llist); __io_fallback_tw(node, false); + node = llist_del_all(&ctx->retry_llist); + __io_fallback_tw(node, false); } static bool io_run_local_work_continue(struct io_ring_ctx *ctx, int events, int min_events) { - if (llist_empty(&ctx->work_llist)) + if (!io_local_work_pending(ctx)) return false; if (events < min_events) return true; @@ -1269,8 +1272,29 @@ static bool io_run_local_work_continue(struct io_ring_ctx *ctx, int events, return false; } +static int __io_run_local_work_loop(struct llist_node **node, + struct io_tw_state *ts, + int events) +{ + int ret = 0; + + while (*node) { + struct llist_node *next = (*node)->next; + struct io_kiocb *req = container_of(*node, struct io_kiocb, + io_task_work.node); + INDIRECT_CALL_2(req->io_task_work.func, + io_poll_task_func, io_req_rw_complete, + req, ts); + *node = next; + if (++ret >= events) + break; + } + + return ret; +} + static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, - int min_events) + int min_events, int max_events) { struct llist_node *node; unsigned int loops = 0; @@ -1281,25 +1305,23 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) atomic_andnot(IORING_SQ_TASKRUN, &ctx->rings->sq_flags); again: + min_events -= ret; + ret = __io_run_local_work_loop(&ctx->retry_llist.first, ts, max_events); + if (ctx->retry_llist.first) + goto retry_done; + /* * llists are in reverse order, flip it back the right way before * running the pending items. */ node = llist_reverse_order(llist_del_all(&ctx->work_llist)); - while (node) { - struct llist_node *next = node->next; - struct io_kiocb *req = container_of(node, struct io_kiocb, - io_task_work.node); - INDIRECT_CALL_2(req->io_task_work.func, - io_poll_task_func, io_req_rw_complete, - req, ts); - ret++; - node = next; - } + ret += __io_run_local_work_loop(&node, ts, max_events - ret); + ctx->retry_llist.first = node; loops++; if (io_run_local_work_continue(ctx, ret, min_events)) goto again; +retry_done: io_submit_flush_completions(ctx); if (io_run_local_work_continue(ctx, ret, min_events)) goto again; @@ -1313,18 +1335,20 @@ static inline int io_run_local_work_locked(struct io_ring_ctx *ctx, { struct io_tw_state ts = {}; - if (llist_empty(&ctx->work_llist)) + if (!io_local_work_pending(ctx)) return 0; - return __io_run_local_work(ctx, &ts, min_events); + return __io_run_local_work(ctx, &ts, min_events, + max(IO_LOCAL_TW_DEFAULT_MAX, min_events)); } -static int io_run_local_work(struct io_ring_ctx *ctx, int min_events) +static int io_run_local_work(struct io_ring_ctx *ctx, int min_events, + int max_events) { struct io_tw_state ts = {}; int ret; mutex_lock(&ctx->uring_lock); - ret = __io_run_local_work(ctx, &ts, min_events); + ret = __io_run_local_work(ctx, &ts, min_events, max_events); mutex_unlock(&ctx->uring_lock); return ret; } @@ -2328,9 +2352,9 @@ static int io_wake_function(struct wait_queue_entry *curr, unsigned int mode, int io_run_task_work_sig(struct io_ring_ctx *ctx) { - if (!llist_empty(&ctx->work_llist)) { + if (io_local_work_pending(ctx)) { __set_current_state(TASK_RUNNING); - if (io_run_local_work(ctx, INT_MAX) > 0) + if (io_run_local_work(ctx, INT_MAX, IO_LOCAL_TW_DEFAULT_MAX) > 0) return 0; } if (io_run_task_work() > 0) @@ -2459,7 +2483,7 @@ static inline int io_cqring_wait_schedule(struct io_ring_ctx *ctx, { if (unlikely(READ_ONCE(ctx->check_cq))) return 1; - if (unlikely(!llist_empty(&ctx->work_llist))) + if (unlikely(io_local_work_pending(ctx))) return 1; if (unlikely(task_work_pending(current))) return 1; @@ -2493,8 +2517,9 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, if (!io_allowed_run_tw(ctx)) return -EEXIST; - if (!llist_empty(&ctx->work_llist)) - io_run_local_work(ctx, min_events); + if (io_local_work_pending(ctx)) + io_run_local_work(ctx, min_events, + max(IO_LOCAL_TW_DEFAULT_MAX, min_events)); io_run_task_work(); if (unlikely(test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq))) @@ -2564,8 +2589,8 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, * If we got woken because of task_work being processed, run it * now rather than let the caller do another wait loop. */ - if (!llist_empty(&ctx->work_llist)) - io_run_local_work(ctx, nr_wait); + if (io_local_work_pending(ctx)) + io_run_local_work(ctx, nr_wait, nr_wait); io_run_task_work(); /* @@ -3077,7 +3102,7 @@ static __cold bool io_uring_try_cancel_requests(struct io_ring_ctx *ctx, if ((ctx->flags & IORING_SETUP_DEFER_TASKRUN) && io_allowed_defer_tw_run(ctx)) - ret |= io_run_local_work(ctx, INT_MAX) > 0; + ret |= io_run_local_work(ctx, INT_MAX, INT_MAX) > 0; ret |= io_cancel_defer_files(ctx, tctx, cancel_all); mutex_lock(&ctx->uring_lock); ret |= io_poll_remove_all(ctx, tctx, cancel_all); @@ -3158,7 +3183,7 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd) io_run_task_work(); io_uring_drop_tctx_refs(current); xa_for_each(&tctx->xa, index, node) { - if (!llist_empty(&node->ctx->work_llist)) { + if (io_local_work_pending(node->ctx)) { WARN_ON_ONCE(node->ctx->submitter_task && node->ctx->submitter_task != current); goto end_wait; diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index 4070d4c8ef971..12abee607e4aa 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -347,9 +347,14 @@ static inline int io_run_task_work(void) return ret; } +static inline bool io_local_work_pending(struct io_ring_ctx *ctx) +{ + return !llist_empty(&ctx->work_llist) || !llist_empty(&ctx->retry_llist); +} + static inline bool io_task_work_pending(struct io_ring_ctx *ctx) { - return task_work_pending(current) || !llist_empty(&ctx->work_llist); + return task_work_pending(current) || io_local_work_pending(ctx); } static inline void io_tw_lock(struct io_ring_ctx *ctx, struct io_tw_state *ts) @@ -484,6 +489,6 @@ enum { static inline bool io_has_work(struct io_ring_ctx *ctx) { return test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq) || - !llist_empty(&ctx->work_llist); + io_local_work_pending(ctx); } #endif diff --git a/io_uring/memmap.c b/io_uring/memmap.c index 6e6ee79ba94fc..57de9bccbf508 100644 --- a/io_uring/memmap.c +++ b/io_uring/memmap.c @@ -73,6 +73,8 @@ void *io_pages_map(struct page ***out_pages, unsigned short *npages, ret = io_mem_alloc_compound(pages, nr_pages, size, gfp); if (!IS_ERR(ret)) goto done; + if (nr_pages == 1) + goto fail; ret = io_mem_alloc_single(pages, nr_pages, size, gfp); if (!IS_ERR(ret)) { @@ -81,7 +83,7 @@ void *io_pages_map(struct page ***out_pages, unsigned short *npages, *npages = nr_pages; return ret; } - +fail: kvfree(pages); *out_pages = NULL; *npages = 0; @@ -136,7 +138,12 @@ struct page **io_pin_pages(unsigned long uaddr, unsigned long len, int *npages) struct page **pages; int ret; - end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (check_add_overflow(uaddr, len, &end)) + return ERR_PTR(-EOVERFLOW); + if (check_add_overflow(end, PAGE_SIZE - 1, &end)) + return ERR_PTR(-EOVERFLOW); + + end = end >> PAGE_SHIFT; start = uaddr >> PAGE_SHIFT; nr_pages = end - start; if (WARN_ON_ONCE(!nr_pages)) @@ -229,7 +236,7 @@ int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr, if (!reg->size || reg->mmap_offset || reg->id) return -EINVAL; if ((reg->size >> PAGE_SHIFT) > INT_MAX) - return E2BIG; + return -E2BIG; if ((reg->user_addr | reg->size) & ~PAGE_MASK) return -EINVAL; if (check_add_overflow(reg->user_addr, reg->size, &end)) diff --git a/io_uring/nop.c b/io_uring/nop.c index 6d470d4251eef..5e5196df650a1 100644 --- a/io_uring/nop.c +++ b/io_uring/nop.c @@ -35,10 +35,14 @@ int io_nop_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) nop->result = READ_ONCE(sqe->len); else nop->result = 0; - if (nop->flags & IORING_NOP_FIXED_FILE) + if (nop->flags & IORING_NOP_FILE) nop->fd = READ_ONCE(sqe->fd); + else + nop->fd = -1; if (nop->flags & IORING_NOP_FIXED_BUFFER) nop->buffer = READ_ONCE(sqe->buf_index); + else + nop->buffer = -1; return 0; } diff --git a/io_uring/register.c b/io_uring/register.c index 1a60f49166499..1e99c783abdf6 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -905,9 +905,10 @@ SYSCALL_DEFINE4(io_uring_register, unsigned int, fd, unsigned int, opcode, mutex_lock(&ctx->uring_lock); ret = __io_uring_register(ctx, opcode, arg, nr_args); - mutex_unlock(&ctx->uring_lock); + trace_io_uring_register(ctx, opcode, ctx->file_table.data.nr, ctx->buf_table.nr, ret); + mutex_unlock(&ctx->uring_lock); if (!use_registered_ring) fput(file); return ret; diff --git a/io_uring/tctx.c b/io_uring/tctx.c index 503f3ff8bc4f9..adc6e42c14df6 100644 --- a/io_uring/tctx.c +++ b/io_uring/tctx.c @@ -47,8 +47,19 @@ static struct io_wq *io_init_wq_offload(struct io_ring_ctx *ctx, void __io_uring_free(struct task_struct *tsk) { struct io_uring_task *tctx = tsk->io_uring; + struct io_tctx_node *node; + unsigned long index; - WARN_ON_ONCE(!xa_empty(&tctx->xa)); + /* + * Fault injection forcing allocation errors in the xa_store() path + * can lead to xa_empty() returning false, even though no actual + * node is stored in the xarray. Until that gets sorted out, attempt + * an iteration here and warn if any entries are found. + */ + xa_for_each(&tctx->xa, index, node) { + WARN_ON_ONCE(1); + break; + } WARN_ON_ONCE(tctx->io_wq); WARN_ON_ONCE(tctx->cached_refs); diff --git a/ipc/msg.c b/ipc/msg.c index fd08b3cb36d79..ee6af4fe52bff 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -978,7 +978,7 @@ SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, struct compat_msgbuf { compat_long_t mtype; - char mtext[1]; + char mtext[]; }; long compat_ksys_msgsnd(int msqid, compat_uptr_t msgp, diff --git a/ipc/namespace.c b/ipc/namespace.c index 6ecc30effd3ec..4df91ceeeafe9 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c @@ -83,13 +83,15 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns, err = msg_init_ns(ns); if (err) - goto fail_put; + goto fail_ipc; sem_init_ns(ns); shm_init_ns(ns); return ns; +fail_ipc: + retire_ipc_sysctls(ns); fail_mq: retire_mq_sysctls(ns); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 91afdd0d036ef..279ba5c420a49 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2729,7 +2729,7 @@ void __audit_ptrace(struct task_struct *t) context->target_uid = task_uid(t); context->target_sessionid = audit_get_sessionid(t); security_task_getlsmprop_obj(t, &context->target_ref); - memcpy(context->target_comm, t->comm, TASK_COMM_LEN); + strscpy(context->target_comm, t->comm); } /** @@ -2756,7 +2756,7 @@ int audit_signal_info_syscall(struct task_struct *t) ctx->target_uid = t_uid; ctx->target_sessionid = audit_get_sessionid(t); security_task_getlsmprop_obj(t, &ctx->target_ref); - memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN); + strscpy(ctx->target_comm, t->comm); return 0; } @@ -2777,7 +2777,7 @@ int audit_signal_info_syscall(struct task_struct *t) axp->target_uid[axp->pid_count] = t_uid; axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t); security_task_getlsmprop_obj(t, &axp->target_ref[axp->pid_count]); - memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN); + strscpy(axp->target_comm[axp->pid_count], t->comm); axp->pid_count++; return 0; diff --git a/kernel/cpu.c b/kernel/cpu.c index 6e34b52cb5ce7..b605334f8ee6a 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2866,7 +2866,6 @@ static struct attribute *cpuhp_cpu_attrs[] = { static const struct attribute_group cpuhp_cpu_attr_group = { .attrs = cpuhp_cpu_attrs, .name = "hotplug", - NULL }; static ssize_t states_show(struct device *dev, @@ -2898,7 +2897,6 @@ static struct attribute *cpuhp_cpu_root_attrs[] = { static const struct attribute_group cpuhp_cpu_root_attr_group = { .attrs = cpuhp_cpu_root_attrs, .name = "hotplug", - NULL }; #ifdef CONFIG_HOTPLUG_SMT @@ -3020,7 +3018,6 @@ static struct attribute *cpuhp_smt_attrs[] = { static const struct attribute_group cpuhp_smt_attr_group = { .attrs = cpuhp_smt_attrs, .name = "smt", - NULL }; static int __init cpu_smt_sysfs_init(void) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index c1048893f4b68..078fe5bc5a747 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -505,7 +505,8 @@ int crash_check_hotplug_support(void) crash_hotplug_lock(); /* Obtain lock while reading crash information */ if (!kexec_trylock()) { - pr_info("kexec_trylock() failed, kdump image may be inaccurate\n"); + if (!kexec_in_progress) + pr_info("kexec_trylock() failed, kdump image may be inaccurate\n"); crash_hotplug_unlock(); return 0; } @@ -547,7 +548,8 @@ static void crash_handle_hotplug_event(unsigned int hp_action, unsigned int cpu, crash_hotplug_lock(); /* Obtain lock while changing crash information */ if (!kexec_trylock()) { - pr_info("kexec_trylock() failed, kdump image may be inaccurate\n"); + if (!kexec_in_progress) + pr_info("kexec_trylock() failed, kdump image may be inaccurate\n"); crash_hotplug_unlock(); return; } diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index 295396226f318..e43c6de2bce4e 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -1219,7 +1219,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, entry->dev = dev; entry->type = dma_debug_single; - entry->paddr = page_to_phys(page); + entry->paddr = page_to_phys(page) + offset; entry->dev_addr = dma_addr; entry->size = size; entry->direction = direction; @@ -1377,6 +1377,18 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, } } +static phys_addr_t virt_to_paddr(void *virt) +{ + struct page *page; + + if (is_vmalloc_addr(virt)) + page = vmalloc_to_page(virt); + else + page = virt_to_page(virt); + + return page_to_phys(page) + offset_in_page(virt); +} + void debug_dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t dma_addr, void *virt, unsigned long attrs) @@ -1399,8 +1411,7 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size, entry->type = dma_debug_coherent; entry->dev = dev; - entry->paddr = page_to_phys((is_vmalloc_addr(virt) ? - vmalloc_to_page(virt) : virt_to_page(virt))); + entry->paddr = virt_to_paddr(virt); entry->size = size; entry->dev_addr = dma_addr; entry->direction = DMA_BIDIRECTIONAL; @@ -1423,8 +1434,7 @@ void debug_dma_free_coherent(struct device *dev, size_t size, if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt)) return; - ref.paddr = page_to_phys((is_vmalloc_addr(virt) ? - vmalloc_to_page(virt) : virt_to_page(virt))); + ref.paddr = virt_to_paddr(virt); if (unlikely(dma_debug_disabled())) return; diff --git a/kernel/events/core.c b/kernel/events/core.c index 5d4a54f50826a..065f9188b44a0 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3778,18 +3778,11 @@ static bool perf_less_group_idx(const void *l, const void *r, void __always_unus return le->group_index < re->group_index; } -static void swap_ptr(void *l, void *r, void __always_unused *args) -{ - void **lp = l, **rp = r; - - swap(*lp, *rp); -} - DEFINE_MIN_HEAP(struct perf_event *, perf_event_min_heap); static const struct min_heap_callbacks perf_min_heap = { .less = perf_less_group_idx, - .swp = swap_ptr, + .swp = NULL, }; static void __heap_add(struct perf_event_min_heap *heap, struct perf_event *event) @@ -3870,7 +3863,7 @@ static noinline int visit_groups_merge(struct perf_event_context *ctx, perf_assert_pmu_disabled((*evt)->pmu_ctx->pmu); } - min_heapify_all(&event_heap, &perf_min_heap, NULL); + min_heapify_all_inline(&event_heap, &perf_min_heap, NULL); while (event_heap.nr) { ret = func(*evt, data); @@ -3879,9 +3872,9 @@ static noinline int visit_groups_merge(struct perf_event_context *ctx, *evt = perf_event_groups_next(*evt, pmu); if (*evt) - min_heap_sift_down(&event_heap, 0, &perf_min_heap, NULL); + min_heap_sift_down_inline(&event_heap, 0, &perf_min_heap, NULL); else - min_heap_pop(&event_heap, &perf_min_heap, NULL); + min_heap_pop_inline(&event_heap, &perf_min_heap, NULL); } return 0; diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 6c2cb4e4f48da..bc4a61029b6dc 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -849,7 +849,7 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, cpu_events = alloc_percpu(typeof(*cpu_events)); if (!cpu_events) - return (void __percpu __force *)ERR_PTR(-ENOMEM); + return ERR_PTR_PCPU(-ENOMEM); cpus_read_lock(); for_each_online_cpu(cpu) { @@ -868,7 +868,7 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, return cpu_events; unregister_wide_hw_breakpoint(cpu_events); - return (void __percpu __force *)ERR_PTR(err); + return ERR_PTR_PCPU(err); } EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint); diff --git a/kernel/fork.c b/kernel/fork.c index f253e81d0c28e..1450b461d196a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -621,6 +621,12 @@ static void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm) exe_file = get_mm_exe_file(oldmm); RCU_INIT_POINTER(mm->exe_file, exe_file); + /* + * We depend on the oldmm having properly denied write access to the + * exe_file already. + */ + if (exe_file && deny_write_access(exe_file)) + pr_warn_once("deny_write_access() failed in %s\n", __func__); } #ifdef CONFIG_MMU @@ -1413,11 +1419,20 @@ int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) */ old_exe_file = rcu_dereference_raw(mm->exe_file); - if (new_exe_file) + if (new_exe_file) { + /* + * We expect the caller (i.e., sys_execve) to already denied + * write access, so this is unlikely to fail. + */ + if (unlikely(deny_write_access(new_exe_file))) + return -EACCES; get_file(new_exe_file); + } rcu_assign_pointer(mm->exe_file, new_exe_file); - if (old_exe_file) + if (old_exe_file) { + allow_write_access(old_exe_file); fput(old_exe_file); + } return 0; } @@ -1456,6 +1471,9 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) return ret; } + ret = deny_write_access(new_exe_file); + if (ret) + return -EACCES; get_file(new_exe_file); /* set the new file */ @@ -1464,8 +1482,10 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) rcu_assign_pointer(mm->exe_file, new_exe_file); mmap_write_unlock(mm); - if (old_exe_file) + if (old_exe_file) { + allow_write_access(old_exe_file); fput(old_exe_file); + } return 0; } diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 6de57246760e6..ebdd76b4ecbba 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -451,28 +451,6 @@ struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key * return NULL; } -int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval) -{ - int ret; - - pagefault_disable(); - ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); - pagefault_enable(); - - return ret; -} - -int futex_get_value_locked(u32 *dest, u32 __user *from) -{ - int ret; - - pagefault_disable(); - ret = __get_user(*dest, from); - pagefault_enable(); - - return ret ? -EFAULT : 0; -} - /** * wait_for_owner_exiting - Block until the owner has exited * @ret: owner's current futex lock status diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h index 8b195d06f4e8e..618ce1fe870e9 100644 --- a/kernel/futex/futex.h +++ b/kernel/futex/futex.h @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef CONFIG_PREEMPT_RT #include @@ -225,10 +226,64 @@ extern bool __futex_wake_mark(struct futex_q *q); extern void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q); extern int fault_in_user_writeable(u32 __user *uaddr); -extern int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval); -extern int futex_get_value_locked(u32 *dest, u32 __user *from); extern struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key *key); +static inline int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval) +{ + int ret; + + pagefault_disable(); + ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); + pagefault_enable(); + + return ret; +} + +/* + * This does a plain atomic user space read, and the user pointer has + * already been verified earlier by get_futex_key() to be both aligned + * and actually in user space, just like futex_atomic_cmpxchg_inatomic(). + * + * We still want to avoid any speculation, and while __get_user() is + * the traditional model for this, it's actually slower than doing + * this manually these days. + * + * We could just have a per-architecture special function for it, + * the same way we do futex_atomic_cmpxchg_inatomic(), but rather + * than force everybody to do that, write it out long-hand using + * the low-level user-access infrastructure. + * + * This looks a bit overkill, but generally just results in a couple + * of instructions. + */ +static __always_inline int futex_read_inatomic(u32 *dest, u32 __user *from) +{ + u32 val; + + if (can_do_masked_user_access()) + from = masked_user_access_begin(from); + else if (!user_read_access_begin(from, sizeof(*from))) + return -EFAULT; + unsafe_get_user(val, from, Efault); + user_access_end(); + *dest = val; + return 0; +Efault: + user_access_end(); + return -EFAULT; +} + +static inline int futex_get_value_locked(u32 *dest, u32 __user *from) +{ + int ret; + + pagefault_disable(); + ret = futex_read_inatomic(dest, from); + pagefault_enable(); + + return ret; +} + extern void __futex_unqueue(struct futex_q *q); extern void __futex_queue(struct futex_q *q, struct futex_hash_bucket *hb); extern int futex_unqueue(struct futex_q *q); diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 959d99583d1c8..c18717189f322 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -30,6 +30,11 @@ */ static int __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT; +/* + * Total number of tasks detected as hung since boot: + */ +static unsigned long __read_mostly sysctl_hung_task_detect_count; + /* * Limit number of tasks checked in a batch. * @@ -115,6 +120,12 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) if (time_is_after_jiffies(t->last_switch_time + timeout * HZ)) return; + /* + * This counter tracks the total number of tasks detected as hung + * since boot. + */ + sysctl_hung_task_detect_count++; + trace_sched_process_hang(t); if (sysctl_hung_task_panic) { @@ -314,6 +325,13 @@ static struct ctl_table hung_task_sysctls[] = { .proc_handler = proc_dointvec_minmax, .extra1 = SYSCTL_NEG_ONE, }, + { + .procname = "hung_task_detect_count", + .data = &sysctl_hung_task_detect_count, + .maxlen = sizeof(unsigned long), + .mode = 0444, + .proc_handler = proc_doulongvec_minmax, + }, }; static void __init hung_task_sysctl_init(void) diff --git a/kernel/kthread.c b/kernel/kthread.c index 9bb36897b6c62..a5ac612b16092 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -101,7 +101,7 @@ void get_kthread_comm(char *buf, size_t buf_size, struct task_struct *tsk) struct kthread *kthread = to_kthread(tsk); if (!kthread || !kthread->full_name) { - __get_task_comm(buf, buf_size, tsk); + strscpy(buf, tsk->comm, buf_size); return; } diff --git a/kernel/module/internal.h b/kernel/module/internal.h index 2ebece8a789f5..daef2be839022 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -80,7 +80,12 @@ struct load_info { unsigned int used_pages; #endif struct { - unsigned int sym, str, mod, vers, info, pcpu; + unsigned int sym; + unsigned int str; + unsigned int mod; + unsigned int vers; + unsigned int info; + unsigned int pcpu; } index; }; diff --git a/kernel/module/main.c b/kernel/module/main.c index d2e1b8976c7b7..5399c182b3cbe 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -195,6 +195,38 @@ static unsigned int find_sec(const struct load_info *info, const char *name) return 0; } +/** + * find_any_unique_sec() - Find a unique section index by name + * @info: Load info for the module to scan + * @name: Name of the section we're looking for + * + * Locates a unique section by name. Ignores SHF_ALLOC. + * + * Return: Section index if found uniquely, zero if absent, negative count + * of total instances if multiple were found. + */ +static int find_any_unique_sec(const struct load_info *info, const char *name) +{ + unsigned int idx; + unsigned int count = 0; + int i; + + for (i = 1; i < info->hdr->e_shnum; i++) { + if (strcmp(info->secstrings + info->sechdrs[i].sh_name, + name) == 0) { + count++; + idx = i; + } + } + if (count == 1) { + return idx; + } else if (count == 0) { + return 0; + } else { + return -count; + } +} + /* Find a module section, or NULL. */ static void *section_addr(const struct load_info *info, const char *name) { @@ -1679,7 +1711,7 @@ bool __weak module_exit_section(const char *name) return strstarts(name, ".exit"); } -static int validate_section_offset(struct load_info *info, Elf_Shdr *shdr) +static int validate_section_offset(const struct load_info *info, Elf_Shdr *shdr) { #if defined(CONFIG_64BIT) unsigned long long secend; @@ -1698,62 +1730,80 @@ static int validate_section_offset(struct load_info *info, Elf_Shdr *shdr) return 0; } -/* - * Check userspace passed ELF module against our expectations, and cache - * useful variables for further processing as we go. - * - * This does basic validity checks against section offsets and sizes, the - * section name string table, and the indices used for it (sh_name). +/** + * elf_validity_ehdr() - Checks an ELF header for module validity + * @info: Load info containing the ELF header to check * - * As a last step, since we're already checking the ELF sections we cache - * useful variables which will be used later for our convenience: + * Checks whether an ELF header could belong to a valid module. Checks: * - * o pointers to section headers - * o cache the modinfo symbol section - * o cache the string symbol section - * o cache the module section + * * ELF header is within the data the user provided + * * ELF magic is present + * * It is relocatable (not final linked, not core file, etc.) + * * The header's machine type matches what the architecture expects. + * * Optional arch-specific hook for other properties + * - module_elf_check_arch() is currently only used by PPC to check + * ELF ABI version, but may be used by others in the future. * - * As a last step we set info->mod to the temporary copy of the module in - * info->hdr. The final one will be allocated in move_module(). Any - * modifications we make to our copy of the module will be carried over - * to the final minted module. + * Return: %0 if valid, %-ENOEXEC on failure. */ -static int elf_validity_cache_copy(struct load_info *info, int flags) +static int elf_validity_ehdr(const struct load_info *info) { - unsigned int i; - Elf_Shdr *shdr, *strhdr; - int err; - unsigned int num_mod_secs = 0, mod_idx; - unsigned int num_info_secs = 0, info_idx; - unsigned int num_sym_secs = 0, sym_idx; - if (info->len < sizeof(*(info->hdr))) { pr_err("Invalid ELF header len %lu\n", info->len); - goto no_exec; + return -ENOEXEC; } - if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0) { pr_err("Invalid ELF header magic: != %s\n", ELFMAG); - goto no_exec; + return -ENOEXEC; } if (info->hdr->e_type != ET_REL) { pr_err("Invalid ELF header type: %u != %u\n", info->hdr->e_type, ET_REL); - goto no_exec; + return -ENOEXEC; } if (!elf_check_arch(info->hdr)) { pr_err("Invalid architecture in ELF header: %u\n", info->hdr->e_machine); - goto no_exec; + return -ENOEXEC; } if (!module_elf_check_arch(info->hdr)) { pr_err("Invalid module architecture in ELF header: %u\n", info->hdr->e_machine); - goto no_exec; + return -ENOEXEC; } + return 0; +} + +/** + * elf_validity_cache_sechdrs() - Cache section headers if valid + * @info: Load info to compute section headers from + * + * Checks: + * + * * ELF header is valid (see elf_validity_ehdr()) + * * Section headers are the size we expect + * * Section array fits in the user provided data + * * Section index 0 is NULL + * * Section contents are inbounds + * + * Then updates @info with a &load_info->sechdrs pointer if valid. + * + * Return: %0 if valid, negative error code if validation failed. + */ +static int elf_validity_cache_sechdrs(struct load_info *info) +{ + Elf_Shdr *sechdrs; + Elf_Shdr *shdr; + int i; + int err; + + err = elf_validity_ehdr(info); + if (err < 0) + return err; + if (info->hdr->e_shentsize != sizeof(Elf_Shdr)) { pr_err("Invalid ELF section header size\n"); - goto no_exec; + return -ENOEXEC; } /* @@ -1765,10 +1815,66 @@ static int elf_validity_cache_copy(struct load_info *info, int flags) || (info->hdr->e_shnum * sizeof(Elf_Shdr) > info->len - info->hdr->e_shoff)) { pr_err("Invalid ELF section header overflow\n"); - goto no_exec; + return -ENOEXEC; } - info->sechdrs = (void *)info->hdr + info->hdr->e_shoff; + sechdrs = (void *)info->hdr + info->hdr->e_shoff; + + /* + * The code assumes that section 0 has a length of zero and + * an addr of zero, so check for it. + */ + if (sechdrs[0].sh_type != SHT_NULL + || sechdrs[0].sh_size != 0 + || sechdrs[0].sh_addr != 0) { + pr_err("ELF Spec violation: section 0 type(%d)!=SH_NULL or non-zero len or addr\n", + sechdrs[0].sh_type); + return -ENOEXEC; + } + + /* Validate contents are inbounds */ + for (i = 1; i < info->hdr->e_shnum; i++) { + shdr = &sechdrs[i]; + switch (shdr->sh_type) { + case SHT_NULL: + case SHT_NOBITS: + /* No contents, offset/size don't mean anything */ + continue; + default: + err = validate_section_offset(info, shdr); + if (err < 0) { + pr_err("Invalid ELF section in module (section %u type %u)\n", + i, shdr->sh_type); + return err; + } + } + } + + info->sechdrs = sechdrs; + + return 0; +} + +/** + * elf_validity_cache_secstrings() - Caches section names if valid + * @info: Load info to cache section names from. Must have valid sechdrs. + * + * Specifically checks: + * + * * Section name table index is inbounds of section headers + * * Section name table is not empty + * * Section name table is NUL terminated + * * All section name offsets are inbounds of the section + * + * Then updates @info with a &load_info->secstrings pointer if valid. + * + * Return: %0 if valid, negative error code if validation failed. + */ +static int elf_validity_cache_secstrings(struct load_info *info) +{ + Elf_Shdr *strhdr, *shdr; + char *secstrings; + int i; /* * Verify if the section name table index is valid. @@ -1778,165 +1884,234 @@ static int elf_validity_cache_copy(struct load_info *info, int flags) pr_err("Invalid ELF section name index: %d || e_shstrndx (%d) >= e_shnum (%d)\n", info->hdr->e_shstrndx, info->hdr->e_shstrndx, info->hdr->e_shnum); - goto no_exec; + return -ENOEXEC; } strhdr = &info->sechdrs[info->hdr->e_shstrndx]; - err = validate_section_offset(info, strhdr); - if (err < 0) { - pr_err("Invalid ELF section hdr(type %u)\n", strhdr->sh_type); - return err; - } /* * The section name table must be NUL-terminated, as required * by the spec. This makes strcmp and pr_* calls that access * strings in the section safe. */ - info->secstrings = (void *)info->hdr + strhdr->sh_offset; + secstrings = (void *)info->hdr + strhdr->sh_offset; if (strhdr->sh_size == 0) { pr_err("empty section name table\n"); - goto no_exec; + return -ENOEXEC; } - if (info->secstrings[strhdr->sh_size - 1] != '\0') { + if (secstrings[strhdr->sh_size - 1] != '\0') { pr_err("ELF Spec violation: section name table isn't null terminated\n"); - goto no_exec; - } - - /* - * The code assumes that section 0 has a length of zero and - * an addr of zero, so check for it. - */ - if (info->sechdrs[0].sh_type != SHT_NULL - || info->sechdrs[0].sh_size != 0 - || info->sechdrs[0].sh_addr != 0) { - pr_err("ELF Spec violation: section 0 type(%d)!=SH_NULL or non-zero len or addr\n", - info->sechdrs[0].sh_type); - goto no_exec; + return -ENOEXEC; } - for (i = 1; i < info->hdr->e_shnum; i++) { + for (i = 0; i < info->hdr->e_shnum; i++) { shdr = &info->sechdrs[i]; - switch (shdr->sh_type) { - case SHT_NULL: - case SHT_NOBITS: + /* SHT_NULL means sh_name has an undefined value */ + if (shdr->sh_type == SHT_NULL) continue; - case SHT_SYMTAB: - if (shdr->sh_link == SHN_UNDEF - || shdr->sh_link >= info->hdr->e_shnum) { - pr_err("Invalid ELF sh_link!=SHN_UNDEF(%d) or (sh_link(%d) >= hdr->e_shnum(%d)\n", - shdr->sh_link, shdr->sh_link, - info->hdr->e_shnum); - goto no_exec; - } - num_sym_secs++; - sym_idx = i; - fallthrough; - default: - err = validate_section_offset(info, shdr); - if (err < 0) { - pr_err("Invalid ELF section in module (section %u type %u)\n", - i, shdr->sh_type); - return err; - } - if (strcmp(info->secstrings + shdr->sh_name, - ".gnu.linkonce.this_module") == 0) { - num_mod_secs++; - mod_idx = i; - } else if (strcmp(info->secstrings + shdr->sh_name, - ".modinfo") == 0) { - num_info_secs++; - info_idx = i; - } - - if (shdr->sh_flags & SHF_ALLOC) { - if (shdr->sh_name >= strhdr->sh_size) { - pr_err("Invalid ELF section name in module (section %u type %u)\n", - i, shdr->sh_type); - return -ENOEXEC; - } - } - break; + if (shdr->sh_name >= strhdr->sh_size) { + pr_err("Invalid ELF section name in module (section %u type %u)\n", + i, shdr->sh_type); + return -ENOEXEC; } } - if (num_info_secs > 1) { + info->secstrings = secstrings; + return 0; +} + +/** + * elf_validity_cache_index_info() - Validate and cache modinfo section + * @info: Load info to populate the modinfo index on. + * Must have &load_info->sechdrs and &load_info->secstrings populated + * + * Checks that if there is a .modinfo section, it is unique. + * Then, it caches its index in &load_info->index.info. + * Finally, it tries to populate the name to improve error messages. + * + * Return: %0 if valid, %-ENOEXEC if multiple modinfo sections were found. + */ +static int elf_validity_cache_index_info(struct load_info *info) +{ + int info_idx; + + info_idx = find_any_unique_sec(info, ".modinfo"); + + if (info_idx == 0) + /* Early return, no .modinfo */ + return 0; + + if (info_idx < 0) { pr_err("Only one .modinfo section must exist.\n"); - goto no_exec; - } else if (num_info_secs == 1) { - /* Try to find a name early so we can log errors with a module name */ - info->index.info = info_idx; - info->name = get_modinfo(info, "name"); + return -ENOEXEC; } - if (num_sym_secs != 1) { - pr_warn("%s: module has no symbols (stripped?)\n", - info->name ?: "(missing .modinfo section or name field)"); - goto no_exec; - } + info->index.info = info_idx; + /* Try to find a name early so we can log errors with a module name */ + info->name = get_modinfo(info, "name"); - /* Sets internal symbols and strings. */ - info->index.sym = sym_idx; - shdr = &info->sechdrs[sym_idx]; - info->index.str = shdr->sh_link; - info->strtab = (char *)info->hdr + info->sechdrs[info->index.str].sh_offset; + return 0; +} - /* - * The ".gnu.linkonce.this_module" ELF section is special. It is - * what modpost uses to refer to __this_module and let's use rely - * on THIS_MODULE to point to &__this_module properly. The kernel's - * modpost declares it on each modules's *.mod.c file. If the struct - * module of the kernel changes a full kernel rebuild is required. - * - * We have a few expectaions for this special section, the following - * code validates all this for us: - * - * o Only one section must exist - * o We expect the kernel to always have to allocate it: SHF_ALLOC - * o The section size must match the kernel's run time's struct module - * size - */ - if (num_mod_secs != 1) { - pr_err("module %s: Only one .gnu.linkonce.this_module section must exist.\n", +/** + * elf_validity_cache_index_mod() - Validates and caches this_module section + * @info: Load info to cache this_module on. + * Must have &load_info->sechdrs and &load_info->secstrings populated + * + * The ".gnu.linkonce.this_module" ELF section is special. It is what modpost + * uses to refer to __this_module and let's use rely on THIS_MODULE to point + * to &__this_module properly. The kernel's modpost declares it on each + * modules's *.mod.c file. If the struct module of the kernel changes a full + * kernel rebuild is required. + * + * We have a few expectations for this special section, this function + * validates all this for us: + * + * * The section has contents + * * The section is unique + * * We expect the kernel to always have to allocate it: SHF_ALLOC + * * The section size must match the kernel's run time's struct module + * size + * + * If all checks pass, the index will be cached in &load_info->index.mod + * + * Return: %0 on validation success, %-ENOEXEC on failure + */ +static int elf_validity_cache_index_mod(struct load_info *info) +{ + Elf_Shdr *shdr; + int mod_idx; + + mod_idx = find_any_unique_sec(info, ".gnu.linkonce.this_module"); + if (mod_idx <= 0) { + pr_err("module %s: Exactly one .gnu.linkonce.this_module section must exist.\n", info->name ?: "(missing .modinfo section or name field)"); - goto no_exec; + return -ENOEXEC; } shdr = &info->sechdrs[mod_idx]; - /* - * This is already implied on the switch above, however let's be - * pedantic about it. - */ if (shdr->sh_type == SHT_NOBITS) { pr_err("module %s: .gnu.linkonce.this_module section must have a size set\n", info->name ?: "(missing .modinfo section or name field)"); - goto no_exec; + return -ENOEXEC; } if (!(shdr->sh_flags & SHF_ALLOC)) { pr_err("module %s: .gnu.linkonce.this_module must occupy memory during process execution\n", info->name ?: "(missing .modinfo section or name field)"); - goto no_exec; + return -ENOEXEC; } if (shdr->sh_size != sizeof(struct module)) { pr_err("module %s: .gnu.linkonce.this_module section size must match the kernel's built struct module size at run time\n", info->name ?: "(missing .modinfo section or name field)"); - goto no_exec; + return -ENOEXEC; } info->index.mod = mod_idx; - /* This is temporary: point mod into copy of data. */ - info->mod = (void *)info->hdr + shdr->sh_offset; + return 0; +} - /* - * If we didn't load the .modinfo 'name' field earlier, fall back to - * on-disk struct mod 'name' field. - */ - if (!info->name) - info->name = info->mod->name; +/** + * elf_validity_cache_index_sym() - Validate and cache symtab index + * @info: Load info to cache symtab index in. + * Must have &load_info->sechdrs and &load_info->secstrings populated. + * + * Checks that there is exactly one symbol table, then caches its index in + * &load_info->index.sym. + * + * Return: %0 if valid, %-ENOEXEC on failure. + */ +static int elf_validity_cache_index_sym(struct load_info *info) +{ + unsigned int sym_idx; + unsigned int num_sym_secs = 0; + int i; + + for (i = 1; i < info->hdr->e_shnum; i++) { + if (info->sechdrs[i].sh_type == SHT_SYMTAB) { + num_sym_secs++; + sym_idx = i; + } + } + + if (num_sym_secs != 1) { + pr_warn("%s: module has no symbols (stripped?)\n", + info->name ?: "(missing .modinfo section or name field)"); + return -ENOEXEC; + } + + info->index.sym = sym_idx; + + return 0; +} + +/** + * elf_validity_cache_index_str() - Validate and cache strtab index + * @info: Load info to cache strtab index in. + * Must have &load_info->sechdrs and &load_info->secstrings populated. + * Must have &load_info->index.sym populated. + * + * Looks at the symbol table's associated string table, makes sure it is + * in-bounds, and caches it. + * + * Return: %0 if valid, %-ENOEXEC on failure. + */ +static int elf_validity_cache_index_str(struct load_info *info) +{ + unsigned int str_idx = info->sechdrs[info->index.sym].sh_link; + + if (str_idx == SHN_UNDEF || str_idx >= info->hdr->e_shnum) { + pr_err("Invalid ELF sh_link!=SHN_UNDEF(%d) or (sh_link(%d) >= hdr->e_shnum(%d)\n", + str_idx, str_idx, info->hdr->e_shnum); + return -ENOEXEC; + } + + info->index.str = str_idx; + return 0; +} + +/** + * elf_validity_cache_index() - Resolve, validate, cache section indices + * @info: Load info to read from and update. + * &load_info->sechdrs and &load_info->secstrings must be populated. + * @flags: Load flags, relevant to suppress version loading, see + * uapi/linux/module.h + * + * Populates &load_info->index, validating as it goes. + * See child functions for per-field validation: + * + * * elf_validity_cache_index_info() + * * elf_validity_cache_index_mod() + * * elf_validity_cache_index_sym() + * * elf_validity_cache_index_str() + * + * If versioning is not suppressed via flags, load the version index from + * a section called "__versions" with no validation. + * + * If CONFIG_SMP is enabled, load the percpu section by name with no + * validation. + * + * Return: 0 on success, negative error code if an index failed validation. + */ +static int elf_validity_cache_index(struct load_info *info, int flags) +{ + int err; + + err = elf_validity_cache_index_info(info); + if (err < 0) + return err; + err = elf_validity_cache_index_mod(info); + if (err < 0) + return err; + err = elf_validity_cache_index_sym(info); + if (err < 0) + return err; + err = elf_validity_cache_index_str(info); + if (err < 0) + return err; if (flags & MODULE_INIT_IGNORE_MODVERSIONS) info->index.vers = 0; /* Pretend no __versions section! */ @@ -1946,9 +2121,109 @@ static int elf_validity_cache_copy(struct load_info *info, int flags) info->index.pcpu = find_pcpusec(info); return 0; +} -no_exec: - return -ENOEXEC; +/** + * elf_validity_cache_strtab() - Validate and cache symbol string table + * @info: Load info to read from and update. + * Must have &load_info->sechdrs and &load_info->secstrings populated. + * Must have &load_info->index populated. + * + * Checks: + * + * * The string table is not empty. + * * The string table starts and ends with NUL (required by ELF spec). + * * Every &Elf_Sym->st_name offset in the symbol table is inbounds of the + * string table. + * + * And caches the pointer as &load_info->strtab in @info. + * + * Return: 0 on success, negative error code if a check failed. + */ +static int elf_validity_cache_strtab(struct load_info *info) +{ + Elf_Shdr *str_shdr = &info->sechdrs[info->index.str]; + Elf_Shdr *sym_shdr = &info->sechdrs[info->index.sym]; + char *strtab = (char *)info->hdr + str_shdr->sh_offset; + Elf_Sym *syms = (void *)info->hdr + sym_shdr->sh_offset; + int i; + + if (str_shdr->sh_size == 0) { + pr_err("empty symbol string table\n"); + return -ENOEXEC; + } + if (strtab[0] != '\0') { + pr_err("symbol string table missing leading NUL\n"); + return -ENOEXEC; + } + if (strtab[str_shdr->sh_size - 1] != '\0') { + pr_err("symbol string table isn't NUL terminated\n"); + return -ENOEXEC; + } + + /* + * Now that we know strtab is correctly structured, check symbol + * starts are inbounds before they're used later. + */ + for (i = 0; i < sym_shdr->sh_size / sizeof(*syms); i++) { + if (syms[i].st_name >= str_shdr->sh_size) { + pr_err("symbol name out of bounds in string table"); + return -ENOEXEC; + } + } + + info->strtab = strtab; + return 0; +} + +/* + * Check userspace passed ELF module against our expectations, and cache + * useful variables for further processing as we go. + * + * This does basic validity checks against section offsets and sizes, the + * section name string table, and the indices used for it (sh_name). + * + * As a last step, since we're already checking the ELF sections we cache + * useful variables which will be used later for our convenience: + * + * o pointers to section headers + * o cache the modinfo symbol section + * o cache the string symbol section + * o cache the module section + * + * As a last step we set info->mod to the temporary copy of the module in + * info->hdr. The final one will be allocated in move_module(). Any + * modifications we make to our copy of the module will be carried over + * to the final minted module. + */ +static int elf_validity_cache_copy(struct load_info *info, int flags) +{ + int err; + + err = elf_validity_cache_sechdrs(info); + if (err < 0) + return err; + err = elf_validity_cache_secstrings(info); + if (err < 0) + return err; + err = elf_validity_cache_index(info, flags); + if (err < 0) + return err; + err = elf_validity_cache_strtab(info); + if (err < 0) + return err; + + /* This is temporary: point mod into copy of data. */ + info->mod = (void *)info->hdr + info->sechdrs[info->index.mod].sh_offset; + + /* + * If we didn't load the .modinfo 'name' field earlier, fall back to + * on-disk struct mod 'name' field. + */ + if (!info->name) + info->name = info->mod->name; + + return 0; } #define COPY_CHUNK_SIZE (16*PAGE_SIZE) diff --git a/kernel/notifier.c b/kernel/notifier.c index b3ce28f39eb65..2f9fe7c30287f 100644 --- a/kernel/notifier.c +++ b/kernel/notifier.c @@ -5,18 +5,10 @@ #include #include #include -#include #define CREATE_TRACE_POINTS #include -/* - * Notifier list for kernel code which wants to be called - * at shutdown. This is used to stop any idling DMA operations - * and the like. - */ -BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); - /* * Notifier chain core routines. The exported routines below * are layered on top of these, with appropriate locking added. diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index e35829d360390..1f87aa01ba44f 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -685,8 +685,11 @@ static void power_down(void) } fallthrough; case HIBERNATION_SHUTDOWN: - if (kernel_can_power_off()) + if (kernel_can_power_off()) { + entering_platform_hibernation = true; kernel_power_off(); + entering_platform_hibernation = false; + } break; } kernel_halt(); diff --git a/kernel/reboot.c b/kernel/reboot.c index f05dbde2c93fe..a701000bab347 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -72,6 +72,13 @@ static bool poweroff_fallback_to_halt; */ void __weak (*pm_power_off)(void); +/* + * Notifier list for kernel code which wants to be called + * at shutdown. This is used to stop any idling DMA operations + * and the like. + */ +static BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); + /** * emergency_restart - reboot the system * @@ -1130,7 +1137,7 @@ static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char val = REBOOT_UNDEFINED_STR; } - return sprintf(buf, "%s\n", val); + return sysfs_emit(buf, "%s\n", val); } static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -1160,7 +1167,7 @@ static struct kobj_attribute reboot_mode_attr = __ATTR_RW(mode); #ifdef CONFIG_X86 static ssize_t force_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", reboot_force); + return sysfs_emit(buf, "%d\n", reboot_force); } static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -1207,7 +1214,7 @@ static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char val = REBOOT_UNDEFINED_STR; } - return sprintf(buf, "%s\n", val); + return sysfs_emit(buf, "%s\n", val); } static ssize_t type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -1240,7 +1247,7 @@ static struct kobj_attribute reboot_type_attr = __ATTR_RW(type); #ifdef CONFIG_SMP static ssize_t cpu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", reboot_cpu); + return sysfs_emit(buf, "%d\n", reboot_cpu); } static ssize_t cpu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) diff --git a/kernel/resource.c b/kernel/resource.c index d2c8143ae4ff9..c9fd26c063454 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -50,17 +50,35 @@ EXPORT_SYMBOL(iomem_resource); static DEFINE_RWLOCK(resource_lock); -static struct resource *next_resource(struct resource *p, bool skip_children) +/* + * Return the next node of @p in pre-order tree traversal. If + * @skip_children is true, skip the descendant nodes of @p in + * traversal. If @p is a descendant of @subtree_root, only traverse + * the subtree under @subtree_root. + */ +static struct resource *next_resource(struct resource *p, bool skip_children, + struct resource *subtree_root) { if (!skip_children && p->child) return p->child; - while (!p->sibling && p->parent) + while (!p->sibling && p->parent) { p = p->parent; + if (p == subtree_root) + return NULL; + } return p->sibling; } +/* + * Traverse the resource subtree under @_root in pre-order, excluding + * @_root itself. + * + * NOTE: '__p' is introduced to avoid shadowing '_p' outside of loop. + * And it is referenced to avoid unused variable warning. + */ #define for_each_resource(_root, _p, _skip_children) \ - for ((_p) = (_root)->child; (_p); (_p) = next_resource(_p, _skip_children)) + for (typeof(_root) __root = (_root), __p = _p = __root->child; \ + __p && _p; _p = next_resource(_p, _skip_children, __root)) #ifdef CONFIG_PROC_FS @@ -88,7 +106,7 @@ static void *r_next(struct seq_file *m, void *v, loff_t *pos) (*pos)++; - return (void *)next_resource(p, false); + return (void *)next_resource(p, false, NULL); } static void r_stop(struct seq_file *m, void *v) @@ -297,6 +315,11 @@ int release_resource(struct resource *old) EXPORT_SYMBOL(release_resource); +static bool is_type_match(struct resource *p, unsigned long flags, unsigned long desc) +{ + return (p->flags & flags) == flags && (desc == IORES_DESC_NONE || desc == p->desc); +} + /** * find_next_iomem_res - Finds the lowest iomem resource that covers part of * [@start..@end]. @@ -339,13 +362,9 @@ static int find_next_iomem_res(resource_size_t start, resource_size_t end, if (p->end < start) continue; - if ((p->flags & flags) != flags) - continue; - if ((desc != IORES_DESC_NONE) && (desc != p->desc)) - continue; - /* Found a match, break */ - break; + if (is_type_match(p, flags, desc)) + break; } if (p) { @@ -537,21 +556,18 @@ static int __region_intersects(struct resource *parent, resource_size_t start, size_t size, unsigned long flags, unsigned long desc) { - resource_size_t ostart, oend; int type = 0; int other = 0; struct resource *p, *dp; - bool is_type, covered; - struct resource res; + struct resource res, o; + bool covered; res.start = start; res.end = start + size - 1; for (p = parent->child; p ; p = p->sibling) { - if (!resource_overlaps(p, &res)) + if (!resource_intersection(p, &res, &o)) continue; - is_type = (p->flags & flags) == flags && - (desc == IORES_DESC_NONE || desc == p->desc); - if (is_type) { + if (is_type_match(p, flags, desc)) { type++; continue; } @@ -568,27 +584,23 @@ static int __region_intersects(struct resource *parent, resource_size_t start, * |-- "System RAM" --||-- "CXL Window 0a" --| */ covered = false; - ostart = max(res.start, p->start); - oend = min(res.end, p->end); for_each_resource(p, dp, false) { if (!resource_overlaps(dp, &res)) continue; - is_type = (dp->flags & flags) == flags && - (desc == IORES_DESC_NONE || desc == dp->desc); - if (is_type) { + if (is_type_match(dp, flags, desc)) { type++; /* - * Range from 'ostart' to 'dp->start' + * Range from 'o.start' to 'dp->start' * isn't covered by matched resource. */ - if (dp->start > ostart) + if (dp->start > o.start) break; - if (dp->end >= oend) { + if (dp->end >= o.end) { covered = true; break; } /* Remove covered range */ - ostart = max(ostart, dp->end + 1); + o.start = max(o.start, dp->end + 1); } } if (!covered) @@ -744,7 +756,7 @@ EXPORT_SYMBOL_GPL(find_resource_space); * @root: root resource descriptor * @old: resource descriptor desired by caller * @newsize: new size of the resource descriptor - * @constraint: the size and alignment constraints to be met. + * @constraint: the memory range and alignment constraints to be met. */ static int reallocate_resource(struct resource *root, struct resource *old, resource_size_t newsize, diff --git a/kernel/signal.c b/kernel/signal.c index 98b65cb358306..989b1cc9116a2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1959,14 +1959,15 @@ static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t, * * Where type is not PIDTYPE_PID, signals must be delivered to the * process. In this case, prefer to deliver to current if it is in - * the same thread group as the target process, which avoids - * unnecessarily waking up a potentially idle task. + * the same thread group as the target process and its sighand is + * stable, which avoids unnecessarily waking up a potentially idle task. */ static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr) { struct task_struct *t = pid_task(tmr->it_pid, tmr->it_pid_type); - if (t && tmr->it_pid_type != PIDTYPE_PID && same_thread_group(t, current)) + if (t && tmr->it_pid_type != PIDTYPE_PID && + same_thread_group(t, current) && !current->exit_state) t = current; return t; } diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index b550ebe0f03b2..163e7a2033b62 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -798,7 +798,7 @@ int __do_adjtimex(struct __kernel_timex *txc, const struct timespec64 *ts, txc->offset = shift_right(ntpdata->time_offset * NTP_INTERVAL_FREQ, NTP_SCALE_SHIFT); if (!(ntpdata->time_status & STA_NANO)) - txc->offset = (u32)txc->offset / NSEC_PER_USEC; + txc->offset = div_s64(txc->offset, NSEC_PER_USEC); } result = ntpdata->time_state; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3ef047ed97055..be62f0ea1814d 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2552,6 +2552,8 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status) trace_flags |= TRACE_FLAG_NEED_RESCHED; if (test_preempt_need_resched()) trace_flags |= TRACE_FLAG_PREEMPT_RESCHED; + if (IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY) && tif_test_bit(TIF_NEED_RESCHED_LAZY)) + trace_flags |= TRACE_FLAG_NEED_RESCHED_LAZY; return (trace_flags << 16) | (min_t(unsigned int, pc & 0xff, 0xf)) | (min_t(unsigned int, migration_disable_value(), 0xf)) << 4; } diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index e08aee34ef63d..da748b7cbc4d5 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -462,17 +462,29 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) bh_off ? 'b' : '.'; - switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | + switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED)) { + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'B'; + break; case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED: need_resched = 'N'; break; + case TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'L'; + break; + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY: + need_resched = 'b'; + break; case TRACE_FLAG_NEED_RESCHED: need_resched = 'n'; break; case TRACE_FLAG_PREEMPT_RESCHED: need_resched = 'p'; break; + case TRACE_FLAG_NEED_RESCHED_LAZY: + need_resched = 'l'; + break; default: need_resched = '.'; break; diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 5a93d4c446b81..41e0f7e9fa353 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -998,6 +998,7 @@ static int proc_watchdog_common(int which, const struct ctl_table *table, int wr mutex_lock(&watchdog_mutex); + old = *param; if (!write) { /* * On read synchronize the userspace interface. This is a @@ -1005,8 +1006,8 @@ static int proc_watchdog_common(int which, const struct ctl_table *table, int wr */ *param = (watchdog_enabled & which) != 0; err = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + *param = old; } else { - old = READ_ONCE(*param); err = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (!err && old != READ_ONCE(*param)) proc_watchdog_update(); diff --git a/lib/Kconfig b/lib/Kconfig index 50d85f38b5695..5a318f753b2f4 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -789,3 +789,9 @@ config POLYNOMIAL config FIRMWARE_TABLE bool + +config UNION_FIND + bool + +config MIN_HEAP + bool diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 5d9eca035d470..f3d7237058793 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2269,6 +2269,7 @@ config TEST_LIST_SORT config TEST_MIN_HEAP tristate "Min heap test" depends on DEBUG_KERNEL || m + select MIN_HEAP help Enable this to turn on min heap function tests. This test is executed only once during system boot (so affects only boot time), @@ -2619,6 +2620,23 @@ config CHECKSUM_KUNIT If unsure, say N. +config UTIL_MACROS_KUNIT + tristate "KUnit test util_macros.h functions at runtime" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this option to test the util_macros.h function at boot. + + KUnit tests run during boot and output the results to the debug log + in TAP format (http://testanything.org/). Only useful for kernel devs + running the KUnit test harness, and not intended for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config HASH_KUNIT_TEST tristate "KUnit Test for integer hash functions" if !KUNIT_ALL_TESTS depends on KUNIT @@ -2840,6 +2858,15 @@ config USERCOPY_KUNIT_TEST on the copy_to/from_user infrastructure, making sure basic user/kernel boundary testing is working. +config CRC16_KUNIT_TEST + tristate "KUnit tests for CRC16" + depends on KUNIT + default KUNIT_ALL_TESTS + select CRC16 + help + Enable this option to run unit tests for the kernel's CRC16 + implementation (). + config TEST_UDELAY tristate "udelay test driver" help @@ -2893,6 +2920,141 @@ config TEST_KMOD If unsure, say N. +config TEST_RUNTIME + bool + +config TEST_RUNTIME_MODULE + bool + +config TEST_KALLSYMS + tristate "module kallsyms find_symbol() test" + depends on m + select TEST_RUNTIME + select TEST_RUNTIME_MODULE + select TEST_KALLSYMS_A + select TEST_KALLSYMS_B + select TEST_KALLSYMS_C + select TEST_KALLSYMS_D + help + This allows us to stress test find_symbol() through the kallsyms + used to place symbols on the kernel ELF kallsyms and modules kallsyms + where we place kernel symbols such as exported symbols. + + We have four test modules: + + A: has KALLSYSMS_NUMSYMS exported symbols + B: uses one of A's symbols + C: adds KALLSYMS_SCALE_FACTOR * KALLSYSMS_NUMSYMS exported + D: adds 2 * the symbols than C + + We stress test find_symbol() through two means: + + 1) Upon load of B it will trigger simplify_symbols() to look for the + one symbol it uses from the module A with tons of symbols. This is an + indirect way for us to have B call resolve_symbol_wait() upon module + load. This will eventually call find_symbol() which will eventually + try to find the symbols used with find_exported_symbol_in_section(). + find_exported_symbol_in_section() uses bsearch() so a binary search + for each symbol. Binary search will at worst be O(log(n)) so the + larger TEST_MODULE_KALLSYSMS the worse the search. + + 2) The selftests should load C first, before B. Upon B's load towards + the end right before we call module B's init routine we get + complete_formation() called on the module. That will first check + for duplicate symbols with the call to verify_exported_symbols(). + That is when we'll force iteration on module C's insane symbol list. + Since it has 10 * KALLSYMS_NUMSYMS it means we can first test + just loading B without C. The amount of time it takes to load C Vs + B can give us an idea of the impact growth of the symbol space and + give us projection. Module A only uses one symbol from B so to allow + this scaling in module C to be proportional, if it used more symbols + then the first test would be doing more and increasing just the + search space would be slightly different. The last module, module D + will just increase the search space by twice the number of symbols in + C so to allow for full projects. + + tools/testing/selftests/module/find_symbol.sh + + The current defaults will incur a build delay of about 7 minutes + on an x86_64 with only 8 cores. Enable this only if you want to + stress test find_symbol() with thousands of symbols. At the same + time this is also useful to test building modules with thousands of + symbols, and if BTF is enabled this also stress tests adding BTF + information for each module. Currently enabling many more symbols + will segfault the build system. + + If unsure, say N. + +if TEST_KALLSYMS + +config TEST_KALLSYMS_A + tristate + depends on m + +config TEST_KALLSYMS_B + tristate + depends on m + +config TEST_KALLSYMS_C + tristate + depends on m + +config TEST_KALLSYMS_D + tristate + depends on m + +choice + prompt "Kallsym test range" + default TEST_KALLSYMS_LARGE + help + Selecting something other than "Fast" will enable tests which slow + down the build and may crash your build. + +config TEST_KALLSYMS_FAST + bool "Fast builds" + help + You won't really be testing kallsysms, so this just helps fast builds + when allmodconfig is used.. + +config TEST_KALLSYMS_LARGE + bool "Enable testing kallsyms with large exports" + help + This will enable larger number of symbols. This will slow down + your build considerably. + +config TEST_KALLSYMS_MAX + bool "Known kallsysms limits" + help + This will enable exports to the point we know we'll start crashing + builds. + +endchoice + +config TEST_KALLSYMS_NUMSYMS + int "test kallsyms number of symbols" + range 2 10000 + default 2 if TEST_KALLSYMS_FAST + default 100 if TEST_KALLSYMS_LARGE + default 10000 if TEST_KALLSYMS_MAX + help + The number of symbols to create on TEST_KALLSYMS_A, only one of which + module TEST_KALLSYMS_B will use. This also will be used + for how many symbols TEST_KALLSYMS_C will have, scaled up by + TEST_KALLSYMS_SCALE_FACTOR. Note that setting this to 10,000 will + trigger a segfault today, don't use anything close to it unless + you are aware that this should not be used for automated build tests. + +config TEST_KALLSYMS_SCALE_FACTOR + int "test kallsyms scale factor" + default 8 + help + How many more unusued symbols will TEST_KALLSYSMS_C have than + TEST_KALLSYMS_A. If 8, then module C will have 8 * syms + than module A. Then TEST_KALLSYMS_D will have double the amount + of symbols than C so to allow projections. + +endif # TEST_KALLSYMS + config TEST_DEBUG_VIRTUAL tristate "Test CONFIG_DEBUG_VIRTUAL feature" depends on DEBUG_VIRTUAL @@ -2983,6 +3145,22 @@ config TEST_OBJPOOL If unsure, say N. +config INT_POW_TEST + tristate "Integer exponentiation (int_pow) test" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This option enables the KUnit test suite for the int_pow function, + which performs integer exponentiation. The test suite is designed to + verify that the implementation of int_pow correctly computes the power + of a given base raised to a given exponent. + + Enabling this option will include tests that check various scenarios + and edge cases to ensure the accuracy and reliability of the exponentiation + function. + + If unsure, say N + endif # RUNTIME_TESTING_MENU config ARCH_USE_MEMTEST @@ -3078,19 +3256,3 @@ config RUST_KERNEL_DOCTESTS endmenu # "Rust" endmenu # Kernel hacking - -config INT_POW_TEST - tristate "Integer exponentiation (int_pow) test" if !KUNIT_ALL_TESTS - depends on KUNIT - default KUNIT_ALL_TESTS - help - This option enables the KUnit test suite for the int_pow function, - which performs integer exponentiation. The test suite is designed to - verify that the implementation of int_pow correctly computes the power - of a given base raised to a given exponent. - - Enabling this option will include tests that check various scenarios - and edge cases to ensure the accuracy and reliability of the exponentiation - function. - - If unsure, say N diff --git a/lib/Makefile b/lib/Makefile index b393dd8151e2d..a8155c972f028 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,10 +35,12 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ earlycpio.o seq_buf.o siphash.o dec_and_lock.o \ nmi_backtrace.o win_minmax.o memcat_p.o \ - buildid.o objpool.o union_find.o iomem_copy.o + buildid.o objpool.o iomem_copy.o +lib-$(CONFIG_UNION_FIND) += union_find.o lib-$(CONFIG_PRINTK) += dump_stack.o lib-$(CONFIG_SMP) += cpumask.o +lib-$(CONFIG_MIN_HEAP) += min_heap.o lib-y += kobject.o klist.o obj-y += lockref.o @@ -96,6 +98,7 @@ obj-$(CONFIG_TEST_XARRAY) += test_xarray.o obj-$(CONFIG_TEST_MAPLE_TREE) += test_maple_tree.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o obj-$(CONFIG_TEST_KMOD) += test_kmod.o +obj-$(CONFIG_TEST_RUNTIME) += tests/ obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o @@ -371,6 +374,7 @@ obj-$(CONFIG_PLDMFW) += pldmfw/ CFLAGS_bitfield_kunit.o := $(DISABLE_STRUCTLEAK_PLUGIN) obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o +obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o @@ -390,6 +394,7 @@ CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN) obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o +obj-$(CONFIG_CRC16_KUNIT_TEST) += crc16_kunit.o obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o diff --git a/lib/crc16_kunit.c b/lib/crc16_kunit.c new file mode 100644 index 0000000000000..0918c98a96d26 --- /dev/null +++ b/lib/crc16_kunit.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnits tests for CRC16. + * + * Copyright (C) 2024, LKCAMP + * Author: Vinicius Peixoto + * Author: Fabricio Gasperin + * Author: Enzo Bertoloti + */ +#include +#include +#include + +#define CRC16_KUNIT_DATA_SIZE 4096 +#define CRC16_KUNIT_TEST_SIZE 100 +#define CRC16_KUNIT_SEED 0x12345678 + +/** + * struct crc16_test - CRC16 test data + * @crc: initial input value to CRC16 + * @start: Start index within the data buffer + * @length: Length of the data + */ +static struct crc16_test { + u16 crc; + u16 start; + u16 length; +} tests[CRC16_KUNIT_TEST_SIZE]; + +u8 data[CRC16_KUNIT_DATA_SIZE]; + + +/* Naive implementation of CRC16 for validation purposes */ +static inline u16 _crc16_naive_byte(u16 crc, u8 data) +{ + u8 i = 0; + + crc ^= (u16) data; + for (i = 0; i < 8; i++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0xa001; + else + crc = crc >> 1; + } + + return crc; +} + + +static inline u16 _crc16_naive(u16 crc, u8 *buffer, size_t len) +{ + while (len--) + crc = _crc16_naive_byte(crc, *buffer++); + return crc; +} + + +/* Small helper for generating pseudorandom 16-bit data */ +static inline u16 _rand16(void) +{ + static u32 rand = CRC16_KUNIT_SEED; + + rand = next_pseudo_random32(rand); + return rand & 0xFFFF; +} + + +static int crc16_init_test_data(struct kunit_suite *suite) +{ + size_t i; + + /* Fill the data buffer with random bytes */ + for (i = 0; i < CRC16_KUNIT_DATA_SIZE; i++) + data[i] = _rand16() & 0xFF; + + /* Generate random test data while ensuring the random + * start + length values won't overflow the 4096-byte + * buffer (0x7FF * 2 = 0xFFE < 0x1000) + */ + for (size_t i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) { + tests[i].crc = _rand16(); + tests[i].start = _rand16() & 0x7FF; + tests[i].length = _rand16() & 0x7FF; + } + + return 0; +} + +static void crc16_test_empty(struct kunit *test) +{ + u16 crc; + + /* The result for empty data should be the same as the + * initial crc + */ + crc = crc16(0x00, data, 0); + KUNIT_EXPECT_EQ(test, crc, 0); + crc = crc16(0xFF, data, 0); + KUNIT_EXPECT_EQ(test, crc, 0xFF); +} + +static void crc16_test_correctness(struct kunit *test) +{ + size_t i; + u16 crc, crc_naive; + + for (i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) { + /* Compare results with the naive crc16 implementation */ + crc = crc16(tests[i].crc, data + tests[i].start, + tests[i].length); + crc_naive = _crc16_naive(tests[i].crc, data + tests[i].start, + tests[i].length); + KUNIT_EXPECT_EQ(test, crc, crc_naive); + } +} + + +static void crc16_test_combine(struct kunit *test) +{ + size_t i, j; + u16 crc, crc_naive; + + /* Make sure that combining two consecutive crc16 calculations + * yields the same result as calculating the crc16 for the whole thing + */ + for (i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) { + crc_naive = crc16(tests[i].crc, data + tests[i].start, tests[i].length); + for (j = 0; j < tests[i].length; j++) { + crc = crc16(tests[i].crc, data + tests[i].start, j); + crc = crc16(crc, data + tests[i].start + j, tests[i].length - j); + KUNIT_EXPECT_EQ(test, crc, crc_naive); + } + } +} + + +static struct kunit_case crc16_test_cases[] = { + KUNIT_CASE(crc16_test_empty), + KUNIT_CASE(crc16_test_combine), + KUNIT_CASE(crc16_test_correctness), + {}, +}; + +static struct kunit_suite crc16_test_suite = { + .name = "crc16", + .test_cases = crc16_test_cases, + .suite_init = crc16_init_test_data, +}; +kunit_test_suite(crc16_test_suite); + +MODULE_AUTHOR("Fabricio Gasperin "); +MODULE_AUTHOR("Vinicius Peixoto "); +MODULE_AUTHOR("Enzo Bertoloti "); +MODULE_DESCRIPTION("Unit tests for crc16"); +MODULE_LICENSE("GPL"); diff --git a/lib/devres.c b/lib/devres.c index 4fc152de6d8bb..73901160197eb 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -115,9 +115,8 @@ EXPORT_SYMBOL(devm_ioremap_wc); */ void devm_iounmap(struct device *dev, void __iomem *addr) { - WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match, + WARN_ON(devres_release(dev, devm_ioremap_release, devm_ioremap_match, (__force void *)addr)); - iounmap(addr); } EXPORT_SYMBOL(devm_iounmap); @@ -308,8 +307,7 @@ EXPORT_SYMBOL(devm_ioport_map); */ void devm_ioport_unmap(struct device *dev, void __iomem *addr) { - ioport_unmap(addr); - WARN_ON(devres_destroy(dev, devm_ioport_map_release, + WARN_ON(devres_release(dev, devm_ioport_map_release, devm_ioport_map_match, (__force void *)addr)); } EXPORT_SYMBOL(devm_ioport_unmap); diff --git a/lib/list-test.c b/lib/list-test.c index e207c4c98d702..9135cdc1bb39f 100644 --- a/lib/list-test.c +++ b/lib/list-test.c @@ -412,6 +412,8 @@ static void list_test_list_cut_position(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, cur, &entries[i]); i++; } + + KUNIT_EXPECT_EQ(test, i, 3); } static void list_test_list_cut_before(struct kunit *test) @@ -440,6 +442,8 @@ static void list_test_list_cut_before(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, cur, &entries[i]); i++; } + + KUNIT_EXPECT_EQ(test, i, 3); } static void list_test_list_splice(struct kunit *test) diff --git a/lib/list_sort.c b/lib/list_sort.c index 0fb59e92ca2d4..8d3f623536fed 100644 --- a/lib/list_sort.c +++ b/lib/list_sort.c @@ -1,9 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include #include #include -#include #include #include diff --git a/lib/min_heap.c b/lib/min_heap.c new file mode 100644 index 0000000000000..4485372ff3b1c --- /dev/null +++ b/lib/min_heap.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +void __min_heap_init(min_heap_char *heap, void *data, int size) +{ + __min_heap_init_inline(heap, data, size); +} +EXPORT_SYMBOL(__min_heap_init); + +void *__min_heap_peek(struct min_heap_char *heap) +{ + return __min_heap_peek_inline(heap); +} +EXPORT_SYMBOL(__min_heap_peek); + +bool __min_heap_full(min_heap_char *heap) +{ + return __min_heap_full_inline(heap); +} +EXPORT_SYMBOL(__min_heap_full); + +void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size, + const struct min_heap_callbacks *func, void *args) +{ + __min_heap_sift_down_inline(heap, pos, elem_size, func, args); +} +EXPORT_SYMBOL(__min_heap_sift_down); + +void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx, + const struct min_heap_callbacks *func, void *args) +{ + __min_heap_sift_up_inline(heap, elem_size, idx, func, args); +} +EXPORT_SYMBOL(__min_heap_sift_up); + +void __min_heapify_all(min_heap_char *heap, size_t elem_size, + const struct min_heap_callbacks *func, void *args) +{ + __min_heapify_all_inline(heap, elem_size, func, args); +} +EXPORT_SYMBOL(__min_heapify_all); + +bool __min_heap_pop(min_heap_char *heap, size_t elem_size, + const struct min_heap_callbacks *func, void *args) +{ + return __min_heap_pop_inline(heap, elem_size, func, args); +} +EXPORT_SYMBOL(__min_heap_pop); + +void __min_heap_pop_push(min_heap_char *heap, const void *element, size_t elem_size, + const struct min_heap_callbacks *func, void *args) +{ + __min_heap_pop_push_inline(heap, element, elem_size, func, args); +} +EXPORT_SYMBOL(__min_heap_pop_push); + +bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size, + const struct min_heap_callbacks *func, void *args) +{ + return __min_heap_push_inline(heap, element, elem_size, func, args); +} +EXPORT_SYMBOL(__min_heap_push); + +bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx, + const struct min_heap_callbacks *func, void *args) +{ + return __min_heap_del_inline(heap, elem_size, idx, func, args); +} +EXPORT_SYMBOL(__min_heap_del); diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c index 2abc78367dd11..5222c6393f116 100644 --- a/lib/overflow_kunit.c +++ b/lib/overflow_kunit.c @@ -1187,7 +1187,7 @@ static void DEFINE_FLEX_test(struct kunit *test) { /* Using _RAW_ on a __counted_by struct will initialize "counter" to zero */ DEFINE_RAW_FLEX(struct foo, two_but_zero, array, 2); -#if __has_attribute(__counted_by__) +#ifdef CONFIG_CC_HAS_COUNTED_BY int expected_raw_size = sizeof(struct foo); #else int expected_raw_size = sizeof(struct foo) + 2 * sizeof(s16); diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 473b2646f71c6..5bb6b8aff2325 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -474,14 +474,14 @@ int sg_alloc_append_table_from_pages(struct sg_append_table *sgt_append, return -EOPNOTSUPP; if (sgt_append->prv) { - unsigned long next_pfn = (page_to_phys(sg_page(sgt_append->prv)) + - sgt_append->prv->offset + sgt_append->prv->length) / PAGE_SIZE; + unsigned long next_pfn; if (WARN_ON(offset)) return -EINVAL; /* Merge contiguous pages into the last SG */ prv_len = sgt_append->prv->length; + next_pfn = (sg_phys(sgt_append->prv) + prv_len) / PAGE_SIZE; if (page_to_pfn(pages[0]) == next_pfn) { last_pg = pfn_to_page(next_pfn - 1); while (n_pages && pages_are_mergeable(pages[0], last_pg)) { diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c index 33564f965958c..f11691315c2fb 100644 --- a/lib/slub_kunit.c +++ b/lib/slub_kunit.c @@ -192,6 +192,47 @@ static void test_leak_destroy(struct kunit *test) KUNIT_EXPECT_EQ(test, 2, slab_errors); } +static void test_krealloc_redzone_zeroing(struct kunit *test) +{ + u8 *p; + int i; + struct kmem_cache *s = test_kmem_cache_create("TestSlub_krealloc", 64, + SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE); + + p = alloc_hooks(__kmalloc_cache_noprof(s, GFP_KERNEL, 48)); + memset(p, 0xff, 48); + + kasan_disable_current(); + OPTIMIZER_HIDE_VAR(p); + + /* Test shrink */ + p = krealloc(p, 40, GFP_KERNEL | __GFP_ZERO); + for (i = 40; i < 64; i++) + KUNIT_EXPECT_EQ(test, p[i], SLUB_RED_ACTIVE); + + /* Test grow within the same 64B kmalloc object */ + p = krealloc(p, 56, GFP_KERNEL | __GFP_ZERO); + for (i = 40; i < 56; i++) + KUNIT_EXPECT_EQ(test, p[i], 0); + for (i = 56; i < 64; i++) + KUNIT_EXPECT_EQ(test, p[i], SLUB_RED_ACTIVE); + + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 0, slab_errors); + + memset(p, 0xff, 56); + /* Test grow with allocating a bigger 128B object */ + p = krealloc(p, 112, GFP_KERNEL | __GFP_ZERO); + for (i = 0; i < 56; i++) + KUNIT_EXPECT_EQ(test, p[i], 0xff); + for (i = 56; i < 112; i++) + KUNIT_EXPECT_EQ(test, p[i], 0); + + kfree(p); + kasan_enable_current(); + kmem_cache_destroy(s); +} + static int test_init(struct kunit *test) { slab_errors = 0; @@ -214,6 +255,7 @@ static struct kunit_case test_cases[] = { KUNIT_CASE(test_kmalloc_redzone_access), KUNIT_CASE(test_kfree_rcu), KUNIT_CASE(test_leak_destroy), + KUNIT_CASE(test_krealloc_redzone_zeroing), {} }; diff --git a/lib/string.c b/lib/string.c index 76327b51e36f2..eb4486ed40d25 100644 --- a/lib/string.c +++ b/lib/string.c @@ -104,6 +104,12 @@ char *strncpy(char *dest, const char *src, size_t count) EXPORT_SYMBOL(strncpy); #endif +#ifdef __BIG_ENDIAN +# define ALLBUTLAST_BYTE_MASK (~255ul) +#else +# define ALLBUTLAST_BYTE_MASK (~0ul >> 8) +#endif + ssize_t sized_strscpy(char *dest, const char *src, size_t count) { const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; @@ -147,13 +153,18 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count) *(unsigned long *)(dest+res) = c & zero_bytemask(data); return res + find_zero(data); } + count -= sizeof(unsigned long); + if (unlikely(!count)) { + c &= ALLBUTLAST_BYTE_MASK; + *(unsigned long *)(dest+res) = c; + return -E2BIG; + } *(unsigned long *)(dest+res) = c; res += sizeof(unsigned long); - count -= sizeof(unsigned long); max -= sizeof(unsigned long); } - while (count) { + while (count > 1) { char c; c = src[res]; @@ -164,11 +175,11 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count) count--; } - /* Hit buffer length without finding a NUL; force NUL-termination. */ - if (res) - dest[res-1] = '\0'; + /* Force NUL-termination. */ + dest[res] = '\0'; - return -E2BIG; + /* Return E2BIG if the source didn't stop */ + return src[res] ? -E2BIG : res; } EXPORT_SYMBOL(sized_strscpy); diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 4f887aa62fa0c..91fa37b5c510a 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -57,7 +57,7 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units, static const unsigned int rounding[] = { 500, 50, 5 }; int i = 0, j; u32 remainder = 0, sf_cap; - char tmp[8]; + char tmp[12]; const char *unit; tmp[0] = '\0'; diff --git a/lib/test_min_heap.c b/lib/test_min_heap.c index 64c877e73b64d..e6fbb798558b5 100644 --- a/lib/test_min_heap.c +++ b/lib/test_min_heap.c @@ -23,14 +23,6 @@ static __init bool greater_than(const void *lhs, const void *rhs, void __always_ return *(int *)lhs > *(int *)rhs; } -static __init void swap_ints(void *lhs, void *rhs, void __always_unused *args) -{ - int temp = *(int *)lhs; - - *(int *)lhs = *(int *)rhs; - *(int *)rhs = temp; -} - static __init int pop_verify_heap(bool min_heap, struct min_heap_test *heap, const struct min_heap_callbacks *funcs) @@ -72,7 +64,7 @@ static __init int test_heapify_all(bool min_heap) }; struct min_heap_callbacks funcs = { .less = min_heap ? less_than : greater_than, - .swp = swap_ints, + .swp = NULL, }; int i, err; @@ -104,7 +96,7 @@ static __init int test_heap_push(bool min_heap) }; struct min_heap_callbacks funcs = { .less = min_heap ? less_than : greater_than, - .swp = swap_ints, + .swp = NULL, }; int i, temp, err; @@ -136,7 +128,7 @@ static __init int test_heap_pop_push(bool min_heap) }; struct min_heap_callbacks funcs = { .less = min_heap ? less_than : greater_than, - .swp = swap_ints, + .swp = NULL, }; int i, temp, err; @@ -175,7 +167,7 @@ static __init int test_heap_del(bool min_heap) heap.nr = ARRAY_SIZE(values); struct min_heap_callbacks funcs = { .less = min_heap ? less_than : greater_than, - .swp = swap_ints, + .swp = NULL, }; int i, err; diff --git a/lib/tests/Makefile b/lib/tests/Makefile new file mode 100644 index 0000000000000..8e4f42cb9c54f --- /dev/null +++ b/lib/tests/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TEST_RUNTIME_MODULE) += module/ diff --git a/lib/tests/module/.gitignore b/lib/tests/module/.gitignore new file mode 100644 index 0000000000000..8be7891b250fe --- /dev/null +++ b/lib/tests/module/.gitignore @@ -0,0 +1,4 @@ +test_kallsyms_a.c +test_kallsyms_b.c +test_kallsyms_c.c +test_kallsyms_d.c diff --git a/lib/tests/module/Makefile b/lib/tests/module/Makefile new file mode 100644 index 0000000000000..2f3e1a772c2c4 --- /dev/null +++ b/lib/tests/module/Makefile @@ -0,0 +1,14 @@ +obj-$(CONFIG_TEST_KALLSYMS_A) += test_kallsyms_a.o +obj-$(CONFIG_TEST_KALLSYMS_B) += test_kallsyms_b.o +obj-$(CONFIG_TEST_KALLSYMS_C) += test_kallsyms_c.o +obj-$(CONFIG_TEST_KALLSYMS_D) += test_kallsyms_d.o + +quiet_cmd_gen_test_kallsyms = GEN $@ + cmd_gen_test_kallsyms = $< $@ \ + $(CONFIG_TEST_KALLSYMS_NUMSYMS) \ + $(CONFIG_TEST_KALLSYMS_SCALE_FACTOR) + +$(obj)/%.c: $(src)/gen_test_kallsyms.sh FORCE + $(call if_changed,gen_test_kallsyms) + +targets += $(foreach x, a b c d, test_kallsyms_$(x).c) diff --git a/lib/tests/module/gen_test_kallsyms.sh b/lib/tests/module/gen_test_kallsyms.sh new file mode 100755 index 0000000000000..561dcac0f359c --- /dev/null +++ b/lib/tests/module/gen_test_kallsyms.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +TARGET=$(basename $1) +DIR=lib/tests/module +TARGET="$DIR/$TARGET" +NUM_SYMS=$2 +SCALE_FACTOR=$3 +TEST_TYPE=$(echo $TARGET | sed -e 's|lib/tests/module/test_kallsyms_||g') +TEST_TYPE=$(echo $TEST_TYPE | sed -e 's|.c||g') +FIRST_B_LOOKUP=1 + +if [[ $NUM_SYMS -gt 2 ]]; then + FIRST_B_LOOKUP=$((NUM_SYMS/2)) +fi + +gen_template_module_header() +{ + cat <<____END_MODULE +// SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 +/* + * Copyright (C) 2023 Luis Chamberlain + * + * Automatically generated code for testing, do not edit manually. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +____END_MODULE +} + +gen_num_syms() +{ + PREFIX=$1 + NUM=$2 + for i in $(seq 1 $NUM); do + printf "int auto_test_%s_%010d = 0;\n" $PREFIX $i + printf "EXPORT_SYMBOL_GPL(auto_test_%s_%010d);\n" $PREFIX $i + done + echo +} + +gen_template_module_data_a() +{ + gen_num_syms a $1 + cat <<____END_MODULE +static int auto_runtime_test(void) +{ + return 0; +} + +____END_MODULE +} + +gen_template_module_data_b() +{ + printf "\nextern int auto_test_a_%010d;\n\n" $FIRST_B_LOOKUP + echo "static int auto_runtime_test(void)" + echo "{" + printf "\nreturn auto_test_a_%010d;\n" $FIRST_B_LOOKUP + echo "}" +} + +gen_template_module_data_c() +{ + gen_num_syms c $1 + cat <<____END_MODULE +static int auto_runtime_test(void) +{ + return 0; +} + +____END_MODULE +} + +gen_template_module_data_d() +{ + gen_num_syms d $1 + cat <<____END_MODULE +static int auto_runtime_test(void) +{ + return 0; +} + +____END_MODULE +} + +gen_template_module_exit() +{ + cat <<____END_MODULE +static int __init auto_test_module_init(void) +{ + return auto_runtime_test(); +} +module_init(auto_test_module_init); + +static void __exit auto_test_module_exit(void) +{ +} +module_exit(auto_test_module_exit); + +MODULE_AUTHOR("Luis Chamberlain "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Test module for kallsyms"); +____END_MODULE +} + +case $TEST_TYPE in + a) + gen_template_module_header > $TARGET + gen_template_module_data_a $NUM_SYMS >> $TARGET + gen_template_module_exit >> $TARGET + ;; + b) + gen_template_module_header > $TARGET + gen_template_module_data_b >> $TARGET + gen_template_module_exit >> $TARGET + ;; + c) + gen_template_module_header > $TARGET + gen_template_module_data_c $((NUM_SYMS * SCALE_FACTOR)) >> $TARGET + gen_template_module_exit >> $TARGET + ;; + d) + gen_template_module_header > $TARGET + gen_template_module_data_d $((NUM_SYMS * SCALE_FACTOR * 2)) >> $TARGET + gen_template_module_exit >> $TARGET + ;; + *) + ;; +esac diff --git a/lib/util_macros_kunit.c b/lib/util_macros_kunit.c new file mode 100644 index 0000000000000..94cc9f0de50ab --- /dev/null +++ b/lib/util_macros_kunit.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test cases for bitfield helpers. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#define FIND_CLOSEST_RANGE_CHECK(from, to, array, exp_idx) \ +{ \ + int i; \ + for (i = from; i <= to; i++) { \ + int found = find_closest(i, array, ARRAY_SIZE(array)); \ + KUNIT_ASSERT_EQ(ctx, exp_idx, found); \ + } \ +} + +static void test_find_closest(struct kunit *ctx) +{ + /* This will test a few arrays that are found in drivers */ + static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + static const unsigned int ad7616_oversampling_avail[] = { + 1, 2, 4, 8, 16, 32, 64, 128, + }; + static u32 wd_timeout_table[] = { 2, 4, 6, 8, 16, 32, 48, 64 }; + static int array_prog1a[] = { 1, 2, 3, 4, 5 }; + static u32 array_prog1b[] = { 2, 3, 4, 5, 6 }; + static int array_prog1mix[] = { -2, -1, 0, 1, 2 }; + static int array_prog2a[] = { 1, 3, 5, 7 }; + static u32 array_prog2b[] = { 2, 4, 6, 8 }; + static int array_prog3a[] = { 1, 4, 7, 10 }; + static u32 array_prog3b[] = { 2, 5, 8, 11 }; + static int array_prog4a[] = { 1, 5, 9, 13 }; + static u32 array_prog4b[] = { 2, 6, 10, 14 }; + + FIND_CLOSEST_RANGE_CHECK(-3, 2, ina226_avg_tab, 0); + FIND_CLOSEST_RANGE_CHECK(3, 10, ina226_avg_tab, 1); + FIND_CLOSEST_RANGE_CHECK(11, 40, ina226_avg_tab, 2); + FIND_CLOSEST_RANGE_CHECK(41, 96, ina226_avg_tab, 3); + FIND_CLOSEST_RANGE_CHECK(97, 192, ina226_avg_tab, 4); + FIND_CLOSEST_RANGE_CHECK(193, 384, ina226_avg_tab, 5); + FIND_CLOSEST_RANGE_CHECK(385, 768, ina226_avg_tab, 6); + FIND_CLOSEST_RANGE_CHECK(769, 2048, ina226_avg_tab, 7); + + /* The array that found the bug that caused this kunit to exist */ + FIND_CLOSEST_RANGE_CHECK(-3, 1, ad7616_oversampling_avail, 0); + FIND_CLOSEST_RANGE_CHECK(2, 3, ad7616_oversampling_avail, 1); + FIND_CLOSEST_RANGE_CHECK(4, 6, ad7616_oversampling_avail, 2); + FIND_CLOSEST_RANGE_CHECK(7, 12, ad7616_oversampling_avail, 3); + FIND_CLOSEST_RANGE_CHECK(13, 24, ad7616_oversampling_avail, 4); + FIND_CLOSEST_RANGE_CHECK(25, 48, ad7616_oversampling_avail, 5); + FIND_CLOSEST_RANGE_CHECK(49, 96, ad7616_oversampling_avail, 6); + FIND_CLOSEST_RANGE_CHECK(97, 256, ad7616_oversampling_avail, 7); + + FIND_CLOSEST_RANGE_CHECK(-3, 3, wd_timeout_table, 0); + FIND_CLOSEST_RANGE_CHECK(4, 5, wd_timeout_table, 1); + FIND_CLOSEST_RANGE_CHECK(6, 7, wd_timeout_table, 2); + FIND_CLOSEST_RANGE_CHECK(8, 12, wd_timeout_table, 3); + FIND_CLOSEST_RANGE_CHECK(13, 24, wd_timeout_table, 4); + FIND_CLOSEST_RANGE_CHECK(25, 40, wd_timeout_table, 5); + FIND_CLOSEST_RANGE_CHECK(41, 56, wd_timeout_table, 6); + FIND_CLOSEST_RANGE_CHECK(57, 128, wd_timeout_table, 7); + + /* One could argue that find_closest() should not be used for monotonic + * arrays (like 1,2,3,4,5), but even so, it should work as long as the + * array is sorted ascending. */ + FIND_CLOSEST_RANGE_CHECK(-3, 1, array_prog1a, 0); + FIND_CLOSEST_RANGE_CHECK(2, 2, array_prog1a, 1); + FIND_CLOSEST_RANGE_CHECK(3, 3, array_prog1a, 2); + FIND_CLOSEST_RANGE_CHECK(4, 4, array_prog1a, 3); + FIND_CLOSEST_RANGE_CHECK(5, 8, array_prog1a, 4); + + FIND_CLOSEST_RANGE_CHECK(-3, 2, array_prog1b, 0); + FIND_CLOSEST_RANGE_CHECK(3, 3, array_prog1b, 1); + FIND_CLOSEST_RANGE_CHECK(4, 4, array_prog1b, 2); + FIND_CLOSEST_RANGE_CHECK(5, 5, array_prog1b, 3); + FIND_CLOSEST_RANGE_CHECK(6, 8, array_prog1b, 4); + + FIND_CLOSEST_RANGE_CHECK(-4, -2, array_prog1mix, 0); + FIND_CLOSEST_RANGE_CHECK(-1, -1, array_prog1mix, 1); + FIND_CLOSEST_RANGE_CHECK(0, 0, array_prog1mix, 2); + FIND_CLOSEST_RANGE_CHECK(1, 1, array_prog1mix, 3); + FIND_CLOSEST_RANGE_CHECK(2, 5, array_prog1mix, 4); + + FIND_CLOSEST_RANGE_CHECK(-3, 2, array_prog2a, 0); + FIND_CLOSEST_RANGE_CHECK(3, 4, array_prog2a, 1); + FIND_CLOSEST_RANGE_CHECK(5, 6, array_prog2a, 2); + FIND_CLOSEST_RANGE_CHECK(7, 10, array_prog2a, 3); + + FIND_CLOSEST_RANGE_CHECK(-3, 3, array_prog2b, 0); + FIND_CLOSEST_RANGE_CHECK(4, 5, array_prog2b, 1); + FIND_CLOSEST_RANGE_CHECK(6, 7, array_prog2b, 2); + FIND_CLOSEST_RANGE_CHECK(8, 10, array_prog2b, 3); + + FIND_CLOSEST_RANGE_CHECK(-3, 2, array_prog3a, 0); + FIND_CLOSEST_RANGE_CHECK(3, 5, array_prog3a, 1); + FIND_CLOSEST_RANGE_CHECK(6, 8, array_prog3a, 2); + FIND_CLOSEST_RANGE_CHECK(9, 20, array_prog3a, 3); + + FIND_CLOSEST_RANGE_CHECK(-3, 3, array_prog3b, 0); + FIND_CLOSEST_RANGE_CHECK(4, 6, array_prog3b, 1); + FIND_CLOSEST_RANGE_CHECK(7, 9, array_prog3b, 2); + FIND_CLOSEST_RANGE_CHECK(10, 20, array_prog3b, 3); + + FIND_CLOSEST_RANGE_CHECK(-3, 3, array_prog4a, 0); + FIND_CLOSEST_RANGE_CHECK(4, 7, array_prog4a, 1); + FIND_CLOSEST_RANGE_CHECK(8, 11, array_prog4a, 2); + FIND_CLOSEST_RANGE_CHECK(12, 20, array_prog4a, 3); + + FIND_CLOSEST_RANGE_CHECK(-3, 4, array_prog4b, 0); + FIND_CLOSEST_RANGE_CHECK(5, 8, array_prog4b, 1); + FIND_CLOSEST_RANGE_CHECK(9, 12, array_prog4b, 2); + FIND_CLOSEST_RANGE_CHECK(13, 20, array_prog4b, 3); +} + +#define FIND_CLOSEST_DESC_RANGE_CHECK(from, to, array, exp_idx) \ +{ \ + int i; \ + for (i = from; i <= to; i++) { \ + int found = find_closest_descending(i, array, \ + ARRAY_SIZE(array)); \ + KUNIT_ASSERT_EQ(ctx, exp_idx, found); \ + } \ +} + +static void test_find_closest_descending(struct kunit *ctx) +{ + /* Same arrays as 'test_find_closest' but reversed */ + static const int ina226_avg_tab[] = { 1024, 512, 256, 128, 64, 16, 4, 1 }; + static const unsigned int ad7616_oversampling_avail[] = { + 128, 64, 32, 16, 8, 4, 2, 1 + }; + static u32 wd_timeout_table[] = { 64, 48, 32, 16, 8, 6, 4, 2 }; + static int array_prog1a[] = { 5, 4, 3, 2, 1 }; + static u32 array_prog1b[] = { 6, 5, 4, 3, 2 }; + static int array_prog1mix[] = { 2, 1, 0, -1, -2 }; + static int array_prog2a[] = { 7, 5, 3, 1 }; + static u32 array_prog2b[] = { 8, 6, 4, 2 }; + static int array_prog3a[] = { 10, 7, 4, 1 }; + static u32 array_prog3b[] = { 11, 8, 5, 2 }; + static int array_prog4a[] = { 13, 9, 5, 1 }; + static u32 array_prog4b[] = { 14, 10, 6, 2 }; + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 2, ina226_avg_tab, 7); + FIND_CLOSEST_DESC_RANGE_CHECK(3, 10, ina226_avg_tab, 6); + FIND_CLOSEST_DESC_RANGE_CHECK(11, 40, ina226_avg_tab, 5); + FIND_CLOSEST_DESC_RANGE_CHECK(41, 96, ina226_avg_tab, 4); + FIND_CLOSEST_DESC_RANGE_CHECK(97, 192, ina226_avg_tab, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(193, 384, ina226_avg_tab, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(385, 768, ina226_avg_tab, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(769, 2048, ina226_avg_tab, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 1, ad7616_oversampling_avail, 7); + FIND_CLOSEST_DESC_RANGE_CHECK(2, 3, ad7616_oversampling_avail, 6); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 6, ad7616_oversampling_avail, 5); + FIND_CLOSEST_DESC_RANGE_CHECK(7, 12, ad7616_oversampling_avail, 4); + FIND_CLOSEST_DESC_RANGE_CHECK(13, 24, ad7616_oversampling_avail, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(25, 48, ad7616_oversampling_avail, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(49, 96, ad7616_oversampling_avail, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(97, 256, ad7616_oversampling_avail, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 3, wd_timeout_table, 7); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 5, wd_timeout_table, 6); + FIND_CLOSEST_DESC_RANGE_CHECK(6, 7, wd_timeout_table, 5); + FIND_CLOSEST_DESC_RANGE_CHECK(8, 12, wd_timeout_table, 4); + FIND_CLOSEST_DESC_RANGE_CHECK(13, 24, wd_timeout_table, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(25, 40, wd_timeout_table, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(41, 56, wd_timeout_table, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(57, 128, wd_timeout_table, 0); + + /* One could argue that find_closest_descending() should not be used + * for monotonic arrays (like 5,4,3,2,1), but even so, it should still + * it should work as long as the array is sorted descending. */ + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 1, array_prog1a, 4); + FIND_CLOSEST_DESC_RANGE_CHECK(2, 2, array_prog1a, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(3, 3, array_prog1a, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 4, array_prog1a, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(5, 8, array_prog1a, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 2, array_prog1b, 4); + FIND_CLOSEST_DESC_RANGE_CHECK(3, 3, array_prog1b, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 4, array_prog1b, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(5, 5, array_prog1b, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(6, 8, array_prog1b, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-4, -2, array_prog1mix, 4); + FIND_CLOSEST_DESC_RANGE_CHECK(-1, -1, array_prog1mix, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(0, 0, array_prog1mix, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(1, 1, array_prog1mix, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(2, 5, array_prog1mix, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 2, array_prog2a, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(3, 4, array_prog2a, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(5, 6, array_prog2a, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(7, 10, array_prog2a, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 3, array_prog2b, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 5, array_prog2b, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(6, 7, array_prog2b, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(8, 10, array_prog2b, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 2, array_prog3a, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(3, 5, array_prog3a, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(6, 8, array_prog3a, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(9, 20, array_prog3a, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 3, array_prog3b, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 6, array_prog3b, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(7, 9, array_prog3b, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(10, 20, array_prog3b, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 3, array_prog4a, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(4, 7, array_prog4a, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(8, 11, array_prog4a, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(12, 20, array_prog4a, 0); + + FIND_CLOSEST_DESC_RANGE_CHECK(-3, 4, array_prog4b, 3); + FIND_CLOSEST_DESC_RANGE_CHECK(5, 8, array_prog4b, 2); + FIND_CLOSEST_DESC_RANGE_CHECK(9, 12, array_prog4b, 1); + FIND_CLOSEST_DESC_RANGE_CHECK(13, 20, array_prog4b, 0); +} + +static struct kunit_case __refdata util_macros_test_cases[] = { + KUNIT_CASE(test_find_closest), + KUNIT_CASE(test_find_closest_descending), + {} +}; + +static struct kunit_suite util_macros_test_suite = { + .name = "util_macros.h", + .test_cases = util_macros_test_cases, +}; + +kunit_test_suites(&util_macros_test_suite); + +MODULE_AUTHOR("Alexandru Ardelean "); +MODULE_DESCRIPTION("Test cases for util_macros.h helpers"); +MODULE_LICENSE("GPL"); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 6ac02bbb7df14..9d3dac38a3f4a 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -3428,29 +3428,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) } EXPORT_SYMBOL_GPL(bstr_printf); -/** - * bprintf - Parse a format string and place args' binary value in a buffer - * @bin_buf: The buffer to place args' binary value - * @size: The size of the buffer(by words(32bits), not characters) - * @fmt: The format string to use - * @...: Arguments for the format string - * - * The function returns the number of words(u32) written - * into @bin_buf. - */ -int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) -{ - va_list args; - int ret; - - va_start(args, fmt); - ret = vbin_printf(bin_buf, size, fmt, args); - va_end(args); - - return ret; -} -EXPORT_SYMBOL_GPL(bprintf); - #endif /* CONFIG_BINARY_PRINTF */ /** diff --git a/mm/folio-compat.c b/mm/folio-compat.c index 80746182e9e8e..1d1832e2a599d 100644 --- a/mm/folio-compat.c +++ b/mm/folio-compat.c @@ -52,6 +52,12 @@ bool set_page_dirty(struct page *page) } EXPORT_SYMBOL(set_page_dirty); +int set_page_dirty_lock(struct page *page) +{ + return folio_mark_dirty_lock(page_folio(page)); +} +EXPORT_SYMBOL(set_page_dirty_lock); + bool clear_page_dirty_for_io(struct page *page) { return folio_clear_dirty_for_io(page_folio(page)); diff --git a/mm/internal.h b/mm/internal.h index 5a7302baeed7c..cb8d8e8e3ffa5 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -49,7 +49,7 @@ struct folio_batch; * when we specify __GFP_NOWARN. */ #define WARN_ON_ONCE_GFP(cond, gfp) ({ \ - static bool __section(".data.once") __warned; \ + static bool __section(".data..once") __warned; \ int __ret_warn_once = !!(cond); \ \ if (unlikely(!(gfp & __GFP_NOWARN) && __ret_warn_once && !__warned)) { \ diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c index 6310a180278b6..8b9e348113b13 100644 --- a/mm/kasan/generic.c +++ b/mm/kasan/generic.c @@ -392,9 +392,12 @@ void kasan_cache_create(struct kmem_cache *cache, unsigned int *size, * 1. Object is SLAB_TYPESAFE_BY_RCU, which means that it can * be touched after it was freed, or * 2. Object has a constructor, which means it's expected to - * retain its content until the next allocation. + * retain its content until the next allocation, or + * 3. It is from a kmalloc cache which enables the debug option + * to store original size. */ - if ((cache->flags & SLAB_TYPESAFE_BY_RCU) || cache->ctor) { + if ((cache->flags & SLAB_TYPESAFE_BY_RCU) || cache->ctor || + slub_debug_orig_size(cache)) { cache->kasan_info.free_meta_offset = *size; *size += sizeof(struct kasan_free_meta); goto free_meta_added; diff --git a/mm/kasan/kasan_test_rust.rs b/mm/kasan/kasan_test_rust.rs index caa7175964ef6..5b34edf30e724 100644 --- a/mm/kasan/kasan_test_rust.rs +++ b/mm/kasan/kasan_test_rust.rs @@ -11,11 +11,12 @@ use kernel::prelude::*; /// drop the vector, and touch it. #[no_mangle] pub extern "C" fn kasan_test_rust_uaf() -> u8 { - let mut v: Vec = Vec::new(); + let mut v: KVec = KVec::new(); for _ in 0..4096 { v.push(0x42, GFP_KERNEL).unwrap(); } let ptr: *mut u8 = addr_of_mut!(v[2048]); drop(v); + // SAFETY: Incorrect, on purpose. unsafe { *ptr } } diff --git a/mm/mm_init.c b/mm/mm_init.c index 1c205b0a86ed5..24b68b425afb1 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -722,6 +722,9 @@ static void __meminit init_reserved_page(unsigned long pfn, int nid) break; } __init_single_page(pfn_to_page(pfn), pfn, zid, nid); + + if (pageblock_aligned(pfn)) + set_pageblock_migratetype(pfn_to_page(pfn), MIGRATE_MOVABLE); } #else static inline void pgdat_set_deferred_range(pg_data_t *pgdat) {} @@ -2572,8 +2575,8 @@ static void __init report_meminit(void) stack = "off"; pr_info("mem auto-init: stack:%s, heap alloc:%s, heap free:%s\n", - stack, want_init_on_alloc(GFP_KERNEL) ? "on" : "off", - want_init_on_free() ? "on" : "off"); + stack, str_on_off(want_init_on_alloc(GFP_KERNEL)), + str_on_off(want_init_on_free())); if (want_init_on_free()) pr_info("mem auto-init: clearing system memory may take some time...\n"); } diff --git a/mm/page-writeback.c b/mm/page-writeback.c index fdb89ce85fff3..d213ead956750 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -2925,25 +2925,25 @@ bool folio_mark_dirty(struct folio *folio) EXPORT_SYMBOL(folio_mark_dirty); /* - * set_page_dirty() is racy if the caller has no reference against - * page->mapping->host, and if the page is unlocked. This is because another - * CPU could truncate the page off the mapping and then free the mapping. + * folio_mark_dirty() is racy if the caller has no reference against + * folio->mapping->host, and if the folio is unlocked. This is because another + * CPU could truncate the folio off the mapping and then free the mapping. * - * Usually, the page _is_ locked, or the caller is a user-space process which + * Usually, the folio _is_ locked, or the caller is a user-space process which * holds a reference on the inode by having an open file. * - * In other cases, the page should be locked before running set_page_dirty(). + * In other cases, the folio should be locked before running folio_mark_dirty(). */ -int set_page_dirty_lock(struct page *page) +bool folio_mark_dirty_lock(struct folio *folio) { - int ret; + bool ret; - lock_page(page); - ret = set_page_dirty(page); - unlock_page(page); + folio_lock(folio); + ret = folio_mark_dirty(folio); + folio_unlock(folio); return ret; } -EXPORT_SYMBOL(set_page_dirty_lock); +EXPORT_SYMBOL(folio_mark_dirty_lock); /* * This cancels just the dirty bit on the kernel page itself, it does NOT diff --git a/mm/slab.h b/mm/slab.h index 6c6fe6d630ce3..632fedd71fea7 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -73,6 +73,11 @@ struct slab { struct { unsigned inuse:16; unsigned objects:15; + /* + * If slab debugging is enabled then the + * frozen bit can be reused to indicate + * that the slab was corrupted + */ unsigned frozen:1; }; }; @@ -695,6 +700,12 @@ void __kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab) void __check_heap_object(const void *ptr, unsigned long n, const struct slab *slab, bool to_user); +static inline bool slub_debug_orig_size(struct kmem_cache *s) +{ + return (kmem_cache_debug_flags(s, SLAB_STORE_USER) && + (s->flags & SLAB_KMALLOC)); +} + #ifdef CONFIG_SLUB_DEBUG void skip_orig_size_check(struct kmem_cache *s, const void *object); #endif diff --git a/mm/slab_common.c b/mm/slab_common.c index a7174455db9f7..a29457bef626f 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -222,15 +222,12 @@ static struct kmem_cache *create_cache(const char *name, struct kmem_cache *s; int err; - if (WARN_ON(args->useroffset + args->usersize > object_size)) - args->useroffset = args->usersize = 0; - /* If a custom freelist pointer is requested make sure it's sane. */ err = -EINVAL; if (args->use_freeptr_offset && (args->freeptr_offset >= object_size || !(flags & SLAB_TYPESAFE_BY_RCU) || - !IS_ALIGNED(args->freeptr_offset, sizeof(freeptr_t)))) + !IS_ALIGNED(args->freeptr_offset, __alignof__(freeptr_t)))) goto out; err = -ENOMEM; @@ -257,11 +254,23 @@ static struct kmem_cache *create_cache(const char *name, * @object_size: The size of objects to be created in this cache. * @args: Additional arguments for the cache creation (see * &struct kmem_cache_args). - * @flags: See %SLAB_* flags for an explanation of individual @flags. + * @flags: See the desriptions of individual flags. The common ones are listed + * in the description below. * * Not to be called directly, use the kmem_cache_create() wrapper with the same * parameters. * + * Commonly used @flags: + * + * &SLAB_ACCOUNT - Account allocations to memcg. + * + * &SLAB_HWCACHE_ALIGN - Align objects on cache line boundaries. + * + * &SLAB_RECLAIM_ACCOUNT - Objects are reclaimable. + * + * &SLAB_TYPESAFE_BY_RCU - Slab page (not individual objects) freeing delayed + * by a grace period - see the full description before using. + * * Context: Cannot be called within a interrupt, but can be interrupted. * * Return: a pointer to the cache on success, NULL on failure. @@ -1199,90 +1208,6 @@ module_init(slab_proc_init); #endif /* CONFIG_SLUB_DEBUG */ -static __always_inline __realloc_size(2) void * -__do_krealloc(const void *p, size_t new_size, gfp_t flags) -{ - void *ret; - size_t ks; - - /* Check for double-free before calling ksize. */ - if (likely(!ZERO_OR_NULL_PTR(p))) { - if (!kasan_check_byte(p)) - return NULL; - ks = ksize(p); - } else - ks = 0; - - /* If the object still fits, repoison it precisely. */ - if (ks >= new_size) { - /* Zero out spare memory. */ - if (want_init_on_alloc(flags)) { - kasan_disable_current(); - memset(kasan_reset_tag(p) + new_size, 0, ks - new_size); - kasan_enable_current(); - } - - p = kasan_krealloc((void *)p, new_size, flags); - return (void *)p; - } - - ret = kmalloc_node_track_caller_noprof(new_size, flags, NUMA_NO_NODE, _RET_IP_); - if (ret && p) { - /* Disable KASAN checks as the object's redzone is accessed. */ - kasan_disable_current(); - memcpy(ret, kasan_reset_tag(p), ks); - kasan_enable_current(); - } - - return ret; -} - -/** - * krealloc - reallocate memory. The contents will remain unchanged. - * @p: object to reallocate memory for. - * @new_size: how many bytes of memory are required. - * @flags: the type of memory to allocate. - * - * If @p is %NULL, krealloc() behaves exactly like kmalloc(). If @new_size - * is 0 and @p is not a %NULL pointer, the object pointed to is freed. - * - * If __GFP_ZERO logic is requested, callers must ensure that, starting with the - * initial memory allocation, every subsequent call to this API for the same - * memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that - * __GFP_ZERO is not fully honored by this API. - * - * This is the case, since krealloc() only knows about the bucket size of an - * allocation (but not the exact size it was allocated with) and hence - * implements the following semantics for shrinking and growing buffers with - * __GFP_ZERO. - * - * new bucket - * 0 size size - * |--------|----------------| - * | keep | zero | - * - * In any case, the contents of the object pointed to are preserved up to the - * lesser of the new and old sizes. - * - * Return: pointer to the allocated memory or %NULL in case of error - */ -void *krealloc_noprof(const void *p, size_t new_size, gfp_t flags) -{ - void *ret; - - if (unlikely(!new_size)) { - kfree(p); - return ZERO_SIZE_PTR; - } - - ret = __do_krealloc(p, new_size, flags); - if (ret && kasan_reset_tag(p) != kasan_reset_tag(ret)) - kfree(p); - - return ret; -} -EXPORT_SYMBOL(krealloc_noprof); - /** * kfree_sensitive - Clear sensitive information in memory before freeing * @p: object to free memory of diff --git a/mm/slub.c b/mm/slub.c index 5b832512044e3..19980419b176c 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -218,6 +218,10 @@ DEFINE_STATIC_KEY_FALSE(slub_debug_enabled); #endif #endif /* CONFIG_SLUB_DEBUG */ +#ifdef CONFIG_NUMA +static DEFINE_STATIC_KEY_FALSE(strict_numa); +#endif + /* Structure holding parameters for get_partial() call chain */ struct partial_context { gfp_t flags; @@ -230,12 +234,6 @@ static inline bool kmem_cache_debug(struct kmem_cache *s) return kmem_cache_debug_flags(s, SLAB_DEBUG_FLAGS); } -static inline bool slub_debug_orig_size(struct kmem_cache *s) -{ - return (kmem_cache_debug_flags(s, SLAB_STORE_USER) && - (s->flags & SLAB_KMALLOC)); -} - void *fixup_red_left(struct kmem_cache *s, void *p) { if (kmem_cache_debug_flags(s, SLAB_RED_ZONE)) @@ -760,21 +758,10 @@ static inline void set_orig_size(struct kmem_cache *s, void *object, unsigned int orig_size) { void *p = kasan_reset_tag(object); - unsigned int kasan_meta_size; if (!slub_debug_orig_size(s)) return; - /* - * KASAN can save its free meta data inside of the object at offset 0. - * If this meta data size is larger than 'orig_size', it will overlap - * the data redzone in [orig_size+1, object_size]. Thus, we adjust - * 'orig_size' to be as at least as big as KASAN's meta data. - */ - kasan_meta_size = kasan_metadata_size(s, true); - if (kasan_meta_size > orig_size) - orig_size = kasan_meta_size; - p += get_info_end(s); p += sizeof(struct track) * 2; @@ -785,6 +772,9 @@ static inline unsigned int get_orig_size(struct kmem_cache *s, void *object) { void *p = kasan_reset_tag(object); + if (is_kfence_address(object)) + return kfence_ksize(object); + if (!slub_debug_orig_size(s)) return s->object_size; @@ -1423,6 +1413,11 @@ static int check_slab(struct kmem_cache *s, struct slab *slab) slab->inuse, slab->objects); return 0; } + if (slab->frozen) { + slab_err(s, slab, "Slab disabled since SLUB metadata consistency check failed"); + return 0; + } + /* Slab_pad_check fixes things up after itself */ slab_pad_check(s, slab); return 1; @@ -1603,6 +1598,7 @@ static noinline bool alloc_debug_processing(struct kmem_cache *s, slab_fix(s, "Marking all objects used"); slab->inuse = slab->objects; slab->freelist = NULL; + slab->frozen = 1; /* mark consistency-failed slab as frozen */ } return false; } @@ -2744,7 +2740,8 @@ static void *alloc_single_from_partial(struct kmem_cache *s, slab->inuse++; if (!alloc_debug_processing(s, slab, object, orig_size)) { - remove_partial(n, slab); + if (folio_test_slab(slab_folio(slab))) + remove_partial(n, slab); return NULL; } @@ -3956,6 +3953,28 @@ static __always_inline void *__slab_alloc_node(struct kmem_cache *s, object = c->freelist; slab = c->slab; +#ifdef CONFIG_NUMA + if (static_branch_unlikely(&strict_numa) && + node == NUMA_NO_NODE) { + + struct mempolicy *mpol = current->mempolicy; + + if (mpol) { + /* + * Special BIND rule support. If existing slab + * is in permitted set then do not redirect + * to a particular node. + * Otherwise we apply the memory policy to get + * the node we need to allocate on. + */ + if (mpol->mode != MPOL_BIND || !slab || + !node_isset(slab_nid(slab), mpol->nodes)) + + node = mempolicy_slab_node(); + } + } +#endif + if (!USE_LOCKLESS_FAST_PATH() || unlikely(!object || !slab || !node_match(slab, node))) { object = __slab_alloc(s, gfpflags, node, addr, c, orig_size); @@ -4728,6 +4747,126 @@ void kfree(const void *object) } EXPORT_SYMBOL(kfree); +static __always_inline __realloc_size(2) void * +__do_krealloc(const void *p, size_t new_size, gfp_t flags) +{ + void *ret; + size_t ks = 0; + int orig_size = 0; + struct kmem_cache *s = NULL; + + if (unlikely(ZERO_OR_NULL_PTR(p))) + goto alloc_new; + + /* Check for double-free. */ + if (!kasan_check_byte(p)) + return NULL; + + if (is_kfence_address(p)) { + ks = orig_size = kfence_ksize(p); + } else { + struct folio *folio; + + folio = virt_to_folio(p); + if (unlikely(!folio_test_slab(folio))) { + /* Big kmalloc object */ + WARN_ON(folio_size(folio) <= KMALLOC_MAX_CACHE_SIZE); + WARN_ON(p != folio_address(folio)); + ks = folio_size(folio); + } else { + s = folio_slab(folio)->slab_cache; + orig_size = get_orig_size(s, (void *)p); + ks = s->object_size; + } + } + + /* If the old object doesn't fit, allocate a bigger one */ + if (new_size > ks) + goto alloc_new; + + /* Zero out spare memory. */ + if (want_init_on_alloc(flags)) { + kasan_disable_current(); + if (orig_size && orig_size < new_size) + memset(kasan_reset_tag(p) + orig_size, 0, new_size - orig_size); + else + memset(kasan_reset_tag(p) + new_size, 0, ks - new_size); + kasan_enable_current(); + } + + /* Setup kmalloc redzone when needed */ + if (s && slub_debug_orig_size(s)) { + set_orig_size(s, (void *)p, new_size); + if (s->flags & SLAB_RED_ZONE && new_size < ks) + memset_no_sanitize_memory(kasan_reset_tag(p) + new_size, + SLUB_RED_ACTIVE, ks - new_size); + } + + p = kasan_krealloc(p, new_size, flags); + return (void *)p; + +alloc_new: + ret = kmalloc_node_track_caller_noprof(new_size, flags, NUMA_NO_NODE, _RET_IP_); + if (ret && p) { + /* Disable KASAN checks as the object's redzone is accessed. */ + kasan_disable_current(); + memcpy(ret, kasan_reset_tag(p), orig_size ?: ks); + kasan_enable_current(); + } + + return ret; +} + +/** + * krealloc - reallocate memory. The contents will remain unchanged. + * @p: object to reallocate memory for. + * @new_size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * If @p is %NULL, krealloc() behaves exactly like kmalloc(). If @new_size + * is 0 and @p is not a %NULL pointer, the object pointed to is freed. + * + * If __GFP_ZERO logic is requested, callers must ensure that, starting with the + * initial memory allocation, every subsequent call to this API for the same + * memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that + * __GFP_ZERO is not fully honored by this API. + * + * When slub_debug_orig_size() is off, krealloc() only knows about the bucket + * size of an allocation (but not the exact size it was allocated with) and + * hence implements the following semantics for shrinking and growing buffers + * with __GFP_ZERO. + * + * new bucket + * 0 size size + * |--------|----------------| + * | keep | zero | + * + * Otherwise, the original allocation size 'orig_size' could be used to + * precisely clear the requested size, and the new size will also be stored + * as the new 'orig_size'. + * + * In any case, the contents of the object pointed to are preserved up to the + * lesser of the new and old sizes. + * + * Return: pointer to the allocated memory or %NULL in case of error + */ +void *krealloc_noprof(const void *p, size_t new_size, gfp_t flags) +{ + void *ret; + + if (unlikely(!new_size)) { + kfree(p); + return ZERO_SIZE_PTR; + } + + ret = __do_krealloc(p, new_size, flags); + if (ret && kasan_reset_tag(p) != kasan_reset_tag(ret)) + kfree(p); + + return ret; +} +EXPORT_SYMBOL(krealloc_noprof); + struct detached_freelist { struct slab *slab; void *tail; @@ -5602,6 +5741,23 @@ static int __init setup_slub_min_objects(char *str) __setup("slab_min_objects=", setup_slub_min_objects); __setup_param("slub_min_objects=", slub_min_objects, setup_slub_min_objects, 0); +#ifdef CONFIG_NUMA +static int __init setup_slab_strict_numa(char *str) +{ + if (nr_node_ids > 1) { + static_branch_enable(&strict_numa); + pr_info("SLUB: Strict NUMA enabled.\n"); + } else { + pr_warn("slab_strict_numa parameter set on non NUMA system.\n"); + } + + return 1; +} + +__setup("slab_strict_numa", setup_slab_strict_numa); +#endif + + #ifdef CONFIG_HARDENED_USERCOPY /* * Rejects incorrectly sized objects and objects that are to be copied @@ -5960,7 +6116,8 @@ __kmem_cache_alias(const char *name, unsigned int size, unsigned int align, s = find_mergeable(size, align, flags, name, ctor); if (s) { if (sysfs_slab_alias(s, name)) - return NULL; + pr_err("SLUB: Unable to add cache alias %s to sysfs\n", + name); s->refcount++; @@ -6042,15 +6199,18 @@ int do_kmem_cache_create(struct kmem_cache *s, const char *name, if (!alloc_kmem_cache_cpus(s)) goto out; + err = 0; + /* Mutex is not taken during early boot */ - if (slab_state <= UP) { - err = 0; + if (slab_state <= UP) goto out; - } - err = sysfs_slab_add(s); - if (err) - goto out; + /* + * Failing to create sysfs files is not critical to SLUB functionality. + * If it fails, proceed with cache creation without these files. + */ + if (sysfs_slab_add(s)) + pr_err("SLUB: Unable to add cache %s to sysfs\n", s->name); if (s->flags & SLAB_STORE_USER) debugfs_slab_add(s); @@ -7120,7 +7280,8 @@ static int sysfs_slab_add(struct kmem_cache *s) void sysfs_slab_unlink(struct kmem_cache *s) { - kobject_del(&s->kobj); + if (s->kobj.state_in_sysfs) + kobject_del(&s->kobj); } void sysfs_slab_release(struct kmem_cache *s) @@ -7149,6 +7310,11 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) * If we have a leftover link then remove it. */ sysfs_remove_link(&slab_kset->kobj, name); + /* + * The original cache may have failed to generate sysfs file. + * In that case, sysfs_create_link() returns -ENOENT and + * symbolic link creation is skipped. + */ return sysfs_create_link(&slab_kset->kobj, &s->kobj, name); } diff --git a/mm/util.c b/mm/util.c index 60017d2a9e489..c1c3b06ab4f94 100644 --- a/mm/util.c +++ b/mm/util.c @@ -45,27 +45,41 @@ void kfree_const(const void *x) EXPORT_SYMBOL(kfree_const); /** - * kstrdup - allocate space for and copy an existing string - * @s: the string to duplicate + * __kmemdup_nul - Create a NUL-terminated string from @s, which might be unterminated. + * @s: The data to copy + * @len: The size of the data, not including the NUL terminator * @gfp: the GFP mask used in the kmalloc() call when allocating memory * - * Return: newly allocated copy of @s or %NULL in case of error + * Return: newly allocated copy of @s with NUL-termination or %NULL in + * case of error */ -noinline -char *kstrdup(const char *s, gfp_t gfp) +static __always_inline char *__kmemdup_nul(const char *s, size_t len, gfp_t gfp) { - size_t len; char *buf; - if (!s) + /* '+1' for the NUL terminator */ + buf = kmalloc_track_caller(len + 1, gfp); + if (!buf) return NULL; - len = strlen(s) + 1; - buf = kmalloc_track_caller(len, gfp); - if (buf) - memcpy(buf, s, len); + memcpy(buf, s, len); + /* Ensure the buf is always NUL-terminated, regardless of @s. */ + buf[len] = '\0'; return buf; } + +/** + * kstrdup - allocate space for and copy an existing string + * @s: the string to duplicate + * @gfp: the GFP mask used in the kmalloc() call when allocating memory + * + * Return: newly allocated copy of @s or %NULL in case of error + */ +noinline +char *kstrdup(const char *s, gfp_t gfp) +{ + return s ? __kmemdup_nul(s, strlen(s), gfp) : NULL; +} EXPORT_SYMBOL(kstrdup); /** @@ -100,19 +114,7 @@ EXPORT_SYMBOL(kstrdup_const); */ char *kstrndup(const char *s, size_t max, gfp_t gfp) { - size_t len; - char *buf; - - if (!s) - return NULL; - - len = strnlen(s, max); - buf = kmalloc_track_caller(len+1, gfp); - if (buf) { - memcpy(buf, s, len); - buf[len] = '\0'; - } - return buf; + return s ? __kmemdup_nul(s, strnlen(s, max), gfp) : NULL; } EXPORT_SYMBOL(kstrndup); @@ -186,17 +188,7 @@ EXPORT_SYMBOL(kvmemdup); */ char *kmemdup_nul(const char *s, size_t len, gfp_t gfp) { - char *buf; - - if (!s) - return NULL; - - buf = kmalloc_track_caller(len + 1, gfp); - if (buf) { - memcpy(buf, s, len); - buf[len] = '\0'; - } - return buf; + return s ? __kmemdup_nul(s, len, gfp) : NULL; } EXPORT_SYMBOL(kmemdup_nul); diff --git a/net/9p/Kconfig b/net/9p/Kconfig index ee967fd25312c..22f8c167845d1 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -41,8 +41,8 @@ config NET_9P_XEN two Xen domains. config NET_9P_USBG - bool "9P USB Gadget Transport" - depends on USB_GADGET=y || USB_GADGET=NET_9P + tristate "9P USB Gadget Transport" + depends on USB_GADGET select CONFIGFS_FS select USB_LIBCOMPOSITE help diff --git a/net/9p/trans_usbg.c b/net/9p/trans_usbg.c index 975b76839dca1..6b694f117aef2 100644 --- a/net/9p/trans_usbg.c +++ b/net/9p/trans_usbg.c @@ -909,9 +909,9 @@ static struct usb_function_instance *usb9pfs_alloc_instance(void) usb9pfs_opts->buflen = DEFAULT_BUFLEN; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (IS_ERR(dev)) { + if (!dev) { kfree(usb9pfs_opts); - return ERR_CAST(dev); + return ERR_PTR(-ENOMEM); } usb9pfs_opts->dev = dev; diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index dfdbe1ca53387..b9ff69c7522a1 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -286,7 +286,7 @@ static void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv) if (!priv->rings[i].intf) break; if (priv->rings[i].irq > 0) - unbind_from_irqhandler(priv->rings[i].irq, priv->dev); + unbind_from_irqhandler(priv->rings[i].irq, ring); if (priv->rings[i].data.in) { for (j = 0; j < (1 << priv->rings[i].intf->ring_order); @@ -465,6 +465,7 @@ static int xen_9pfs_front_init(struct xenbus_device *dev) goto error; } + xenbus_switch_state(dev, XenbusStateInitialised); return 0; error_xenbus: @@ -512,8 +513,10 @@ static void xen_9pfs_front_changed(struct xenbus_device *dev, break; case XenbusStateInitWait: - if (!xen_9pfs_front_init(dev)) - xenbus_switch_state(dev, XenbusStateInitialised); + if (dev->state != XenbusStateInitialising) + break; + + xen_9pfs_front_init(dev); break; case XenbusStateConnected: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1f6d083682b8e..b31192d473d09 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1318,7 +1318,8 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err) struct mgmt_mode *cp; /* Make sure cmd still outstanding. */ - if (cmd != pending_find(MGMT_OP_SET_POWERED, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_SET_POWERED, hdev)) return; cp = cmd->param; @@ -1351,7 +1352,13 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err) static int set_powered_sync(struct hci_dev *hdev, void *data) { struct mgmt_pending_cmd *cmd = data; - struct mgmt_mode *cp = cmd->param; + struct mgmt_mode *cp; + + /* Make sure cmd still outstanding. */ + if (cmd != pending_find(MGMT_OP_SET_POWERED, hdev)) + return -ECANCELED; + + cp = cmd->param; BT_DBG("%s", hdev->name); @@ -1511,7 +1518,8 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "err %d", err); /* Make sure cmd still outstanding. */ - if (cmd != pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) return; hci_dev_lock(hdev); @@ -1685,7 +1693,8 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "err %d", err); /* Make sure cmd still outstanding. */ - if (cmd != pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) return; hci_dev_lock(hdev); @@ -1917,7 +1926,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err) bool changed; /* Make sure cmd still outstanding. */ - if (cmd != pending_find(MGMT_OP_SET_SSP, hdev)) + if (err == -ECANCELED || cmd != pending_find(MGMT_OP_SET_SSP, hdev)) return; if (err) { @@ -3841,7 +3850,8 @@ static void set_name_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); - if (cmd != pending_find(MGMT_OP_SET_LOCAL_NAME, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_SET_LOCAL_NAME, hdev)) return; if (status) { @@ -4016,7 +4026,8 @@ static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err) struct sk_buff *skb = cmd->skb; u8 status = mgmt_status(err); - if (cmd != pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev)) return; if (!status) { @@ -5907,13 +5918,16 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err) { struct mgmt_pending_cmd *cmd = data; + bt_dev_dbg(hdev, "err %d", err); + + if (err == -ECANCELED) + return; + if (cmd != pending_find(MGMT_OP_START_DISCOVERY, hdev) && cmd != pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev) && cmd != pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev)) return; - bt_dev_dbg(hdev, "err %d", err); - mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(err), cmd->param, 1); mgmt_pending_remove(cmd); @@ -6146,7 +6160,8 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err) { struct mgmt_pending_cmd *cmd = data; - if (cmd != pending_find(MGMT_OP_STOP_DISCOVERY, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_STOP_DISCOVERY, hdev)) return; bt_dev_dbg(hdev, "err %d", err); @@ -8137,7 +8152,8 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data, u8 status = mgmt_status(err); u16 eir_len; - if (cmd != pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev)) + if (err == -ECANCELED || + cmd != pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev)) return; if (!status) { diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 1b8e468d24cf5..78f7bca244877 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -143,6 +143,7 @@ static void sco_sock_timeout(struct work_struct *work) sco_conn_lock(conn); if (!conn->hcon) { sco_conn_unlock(conn); + sco_conn_put(conn); return; } sk = sco_sock_hold(conn); @@ -192,7 +193,6 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) conn->hcon = hcon; sco_conn_unlock(conn); } - sco_conn_put(conn); return conn; } diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 051d22c0e4ad4..01b2ce1e8fc06 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -74,18 +74,6 @@ int ceph_crypto_key_clone(struct ceph_crypto_key *dst, return set_secret(dst, src->key); } -int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end) -{ - if (*p + sizeof(u16) + sizeof(key->created) + - sizeof(u16) + key->len > end) - return -ERANGE; - ceph_encode_16(p, key->type); - ceph_encode_copy(p, &key->created, sizeof(key->created)); - ceph_encode_16(p, key->len); - ceph_encode_copy(p, key->key, key->len); - return 0; -} - int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end) { int ret; diff --git a/net/ceph/crypto.h b/net/ceph/crypto.h index 13bd526349fa1..23de29fc613cf 100644 --- a/net/ceph/crypto.h +++ b/net/ceph/crypto.h @@ -21,7 +21,6 @@ struct ceph_crypto_key { int ceph_crypto_key_clone(struct ceph_crypto_key *dst, const struct ceph_crypto_key *src); -int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end); int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end); int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in); void ceph_crypto_key_destroy(struct ceph_crypto_key *key); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 9d078b37fe0b9..9b1168eb77abb 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -4999,40 +4999,6 @@ int ceph_osdc_notify(struct ceph_osd_client *osdc, } EXPORT_SYMBOL(ceph_osdc_notify); -/* - * Return the number of milliseconds since the watch was last - * confirmed, or an error. If there is an error, the watch is no - * longer valid, and should be destroyed with ceph_osdc_unwatch(). - */ -int ceph_osdc_watch_check(struct ceph_osd_client *osdc, - struct ceph_osd_linger_request *lreq) -{ - unsigned long stamp, age; - int ret; - - down_read(&osdc->lock); - mutex_lock(&lreq->lock); - stamp = lreq->watch_valid_thru; - if (!list_empty(&lreq->pending_lworks)) { - struct linger_work *lwork = - list_first_entry(&lreq->pending_lworks, - struct linger_work, - pending_item); - - if (time_before(lwork->queued_stamp, stamp)) - stamp = lwork->queued_stamp; - } - age = jiffies - stamp; - dout("%s lreq %p linger_id %llu age %lu last_error %d\n", __func__, - lreq, lreq->linger_id, age, lreq->last_error); - /* we are truncating to msecs, so return a safe upper bound */ - ret = lreq->last_error ?: 1 + jiffies_to_msecs(age); - - mutex_unlock(&lreq->lock); - up_read(&osdc->lock); - return ret; -} - static int decode_watcher(void **p, void *end, struct ceph_watch_item *item) { u8 struct_v; diff --git a/net/ceph/pagelist.c b/net/ceph/pagelist.c index 74622b278d576..5a9c4be5f2221 100644 --- a/net/ceph/pagelist.c +++ b/net/ceph/pagelist.c @@ -131,41 +131,3 @@ int ceph_pagelist_free_reserve(struct ceph_pagelist *pl) return 0; } EXPORT_SYMBOL(ceph_pagelist_free_reserve); - -/* Create a truncation point. */ -void ceph_pagelist_set_cursor(struct ceph_pagelist *pl, - struct ceph_pagelist_cursor *c) -{ - c->pl = pl; - c->page_lru = pl->head.prev; - c->room = pl->room; -} -EXPORT_SYMBOL(ceph_pagelist_set_cursor); - -/* Truncate a pagelist to the given point. Move extra pages to reserve. - * This won't sleep. - * Returns: 0 on success, - * -EINVAL if the pagelist doesn't match the trunc point pagelist - */ -int ceph_pagelist_truncate(struct ceph_pagelist *pl, - struct ceph_pagelist_cursor *c) -{ - struct page *page; - - if (pl != c->pl) - return -EINVAL; - ceph_pagelist_unmap_tail(pl); - while (pl->head.prev != c->page_lru) { - page = list_entry(pl->head.prev, struct page, lru); - /* move from pagelist to reserve */ - list_move_tail(&page->lru, &pl->free_list); - ++pl->num_pages_free; - } - pl->room = c->room; - if (!list_empty(&pl->head)) { - page = list_entry(pl->head.prev, struct page, lru); - pl->mapped_tail = kmap(page); - } - return 0; -} -EXPORT_SYMBOL(ceph_pagelist_truncate); diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c index 64305e7056a1c..4509757d8b3be 100644 --- a/net/ceph/pagevec.c +++ b/net/ceph/pagevec.c @@ -55,58 +55,6 @@ struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags) } EXPORT_SYMBOL(ceph_alloc_page_vector); -/* - * copy user data into a page vector - */ -int ceph_copy_user_to_page_vector(struct page **pages, - const void __user *data, - loff_t off, size_t len) -{ - int i = 0; - int po = off & ~PAGE_MASK; - int left = len; - int l, bad; - - while (left > 0) { - l = min_t(int, PAGE_SIZE-po, left); - bad = copy_from_user(page_address(pages[i]) + po, data, l); - if (bad == l) - return -EFAULT; - data += l - bad; - left -= l - bad; - po += l - bad; - if (po == PAGE_SIZE) { - po = 0; - i++; - } - } - return len; -} -EXPORT_SYMBOL(ceph_copy_user_to_page_vector); - -void ceph_copy_to_page_vector(struct page **pages, - const void *data, - loff_t off, size_t len) -{ - int i = 0; - size_t po = off & ~PAGE_MASK; - size_t left = len; - - while (left > 0) { - size_t l = min_t(size_t, PAGE_SIZE-po, left); - - memcpy(page_address(pages[i]) + po, data, l); - data += l; - left -= l; - po += l; - if (po == PAGE_SIZE) { - po = 0; - i++; - } - } -} -EXPORT_SYMBOL(ceph_copy_to_page_vector); - void ceph_copy_from_page_vector(struct page **pages, void *data, loff_t off, size_t len) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index dd142f444659d..58df76fe408a4 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2442,7 +2442,9 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) tgt_net = rtnl_get_net_ns_capable(skb->sk, netnsid); if (IS_ERR(tgt_net)) { NL_SET_ERR_MSG(extack, "Invalid target network namespace id"); - return PTR_ERR(tgt_net); + err = PTR_ERR(tgt_net); + netnsid = -1; + goto out; } break; case IFLA_EXT_MASK: @@ -2457,7 +2459,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) default: if (cb->strict_check) { NL_SET_ERR_MSG(extack, "Unsupported attribute in link dump request"); - return -EINVAL; + err = -EINVAL; + goto out; } } } @@ -2479,11 +2482,14 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) break; } - if (kind_ops) - rtnl_link_ops_put(kind_ops, ops_srcu_index); cb->seq = tgt_net->dev_base_seq; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); + +out: + + if (kind_ops) + rtnl_link_ops_put(kind_ops, ops_srcu_index); if (netnsid >= 0) put_net(tgt_net); diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 9e64496a5c1c8..31a416ee21ad9 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -268,6 +268,8 @@ static struct sk_buff *hsr_init_skb(struct hsr_port *master) skb->dev = master->dev; skb->priority = TC_PRIO_CONTROL; + skb_reset_network_header(skb); + skb_reset_transport_header(skb); if (dev_hard_header(skb, skb->dev, ETH_P_PRP, hsr->sup_multicast_addr, skb->dev->dev_addr, skb->len) <= 0) @@ -275,8 +277,6 @@ static struct sk_buff *hsr_init_skb(struct hsr_port *master) skb_reset_mac_header(skb); skb_reset_mac_len(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); return skb; out: diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 491c2c6b683e2..6872b5aff73e5 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1191,7 +1191,7 @@ static void reqsk_timer_handler(struct timer_list *t) drop: __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); - reqsk_put(req); + reqsk_put(oreq); } static bool reqsk_queue_hash_req(struct request_sock *req, diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index c58dd78509a27..c5b8ec5c0a8c0 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -120,6 +120,11 @@ static void ipmr_expire_process(struct timer_list *t); lockdep_rtnl_is_held() || \ list_empty(&net->ipv4.mr_tables)) +static bool ipmr_can_free_table(struct net *net) +{ + return !check_net(net) || !net->ipv4.mr_rules_ops; +} + static struct mr_table *ipmr_mr_table_iter(struct net *net, struct mr_table *mrt) { @@ -137,7 +142,7 @@ static struct mr_table *ipmr_mr_table_iter(struct net *net, return ret; } -static struct mr_table *ipmr_get_table(struct net *net, u32 id) +static struct mr_table *__ipmr_get_table(struct net *net, u32 id) { struct mr_table *mrt; @@ -148,6 +153,16 @@ static struct mr_table *ipmr_get_table(struct net *net, u32 id) return NULL; } +static struct mr_table *ipmr_get_table(struct net *net, u32 id) +{ + struct mr_table *mrt; + + rcu_read_lock(); + mrt = __ipmr_get_table(net, id); + rcu_read_unlock(); + return mrt; +} + static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, struct mr_table **mrt) { @@ -189,7 +204,7 @@ static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, arg->table = fib_rule_get_table(rule, arg); - mrt = ipmr_get_table(rule->fr_net, arg->table); + mrt = __ipmr_get_table(rule->fr_net, arg->table); if (!mrt) return -EAGAIN; res->mrt = mrt; @@ -302,6 +317,11 @@ EXPORT_SYMBOL(ipmr_rule_default); #define ipmr_for_each_table(mrt, net) \ for (mrt = net->ipv4.mrt; mrt; mrt = NULL) +static bool ipmr_can_free_table(struct net *net) +{ + return !check_net(net); +} + static struct mr_table *ipmr_mr_table_iter(struct net *net, struct mr_table *mrt) { @@ -315,6 +335,8 @@ static struct mr_table *ipmr_get_table(struct net *net, u32 id) return net->ipv4.mrt; } +#define __ipmr_get_table ipmr_get_table + static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, struct mr_table **mrt) { @@ -403,7 +425,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id) if (id != RT_TABLE_DEFAULT && id >= 1000000000) return ERR_PTR(-EINVAL); - mrt = ipmr_get_table(net, id); + mrt = __ipmr_get_table(net, id); if (mrt) return mrt; @@ -413,6 +435,10 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id) static void ipmr_free_table(struct mr_table *mrt) { + struct net *net = read_pnet(&mrt->net); + + WARN_ON_ONCE(!ipmr_can_free_table(net)); + timer_shutdown_sync(&mrt->ipmr_expire_timer); mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC | MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC); @@ -1374,7 +1400,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, goto out_unlock; } - mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); + mrt = __ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); if (!mrt) { ret = -ENOENT; goto out_unlock; @@ -2262,11 +2288,13 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, struct mr_table *mrt; int err; - mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); - if (!mrt) + rcu_read_lock(); + mrt = __ipmr_get_table(net, RT_TABLE_DEFAULT); + if (!mrt) { + rcu_read_unlock(); return -ENOENT; + } - rcu_read_lock(); cache = ipmr_cache_find(mrt, saddr, daddr); if (!cache && skb->dev) { int vif = ipmr_find_vif(mrt, skb->dev); @@ -2550,7 +2578,7 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, grp = nla_get_in_addr_default(tb[RTA_DST], 0); tableid = nla_get_u32_default(tb[RTA_TABLE], 0); - mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT); + mrt = __ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT); if (!mrt) { err = -ENOENT; goto errout_free; @@ -2604,7 +2632,7 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) if (filter.table_id) { struct mr_table *mrt; - mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); + mrt = __ipmr_get_table(sock_net(skb->sk), filter.table_id); if (!mrt) { if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IPMR) return skb->len; @@ -2712,7 +2740,7 @@ static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, break; } } - mrt = ipmr_get_table(net, tblid); + mrt = __ipmr_get_table(net, tblid); if (!mrt) { ret = -ENOENT; goto out; @@ -2920,13 +2948,15 @@ static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) struct net *net = seq_file_net(seq); struct mr_table *mrt; - mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); - if (!mrt) + rcu_read_lock(); + mrt = __ipmr_get_table(net, RT_TABLE_DEFAULT); + if (!mrt) { + rcu_read_unlock(); return ERR_PTR(-ENOENT); + } iter->mrt = mrt; - rcu_read_lock(); return mr_vif_seq_start(seq, pos); } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 96b5b2b0d5072..c489a1e6aec9a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2570,6 +2570,24 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev) return idev; } +static void delete_tempaddrs(struct inet6_dev *idev, + struct inet6_ifaddr *ifp) +{ + struct inet6_ifaddr *ift, *tmp; + + write_lock_bh(&idev->lock); + list_for_each_entry_safe(ift, tmp, &idev->tempaddr_list, tmp_list) { + if (ift->ifpub != ifp) + continue; + + in6_ifa_hold(ift); + write_unlock_bh(&idev->lock); + ipv6_del_addr(ift); + write_lock_bh(&idev->lock); + } + write_unlock_bh(&idev->lock); +} + static void manage_tempaddrs(struct inet6_dev *idev, struct inet6_ifaddr *ifp, __u32 valid_lft, __u32 prefered_lft, @@ -3124,11 +3142,12 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, in6_ifa_hold(ifp); read_unlock_bh(&idev->lock); - if (!(ifp->flags & IFA_F_TEMPORARY) && - (ifa_flags & IFA_F_MANAGETEMPADDR)) - manage_tempaddrs(idev, ifp, 0, 0, false, - jiffies); ipv6_del_addr(ifp); + + if (!(ifp->flags & IFA_F_TEMPORARY) && + (ifp->flags & IFA_F_MANAGETEMPADDR)) + delete_tempaddrs(idev, ifp); + addrconf_verify_rtnl(net); if (ipv6_addr_is_multicast(pfx)) { ipv6_mc_config(net->ipv6.mc_autojoin_sk, @@ -4952,14 +4971,12 @@ static int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp, } if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) { - if (was_managetempaddr && - !(ifp->flags & IFA_F_MANAGETEMPADDR)) { - cfg->valid_lft = 0; - cfg->preferred_lft = 0; - } - manage_tempaddrs(ifp->idev, ifp, cfg->valid_lft, - cfg->preferred_lft, !was_managetempaddr, - jiffies); + if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR)) + delete_tempaddrs(ifp->idev, ifp); + else + manage_tempaddrs(ifp->idev, ifp, cfg->valid_lft, + cfg->preferred_lft, !was_managetempaddr, + jiffies); } addrconf_verify_rtnl(net); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index d66f58932a793..7f1902ac35862 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -108,6 +108,11 @@ static void ipmr_expire_process(struct timer_list *t); lockdep_rtnl_is_held() || \ list_empty(&net->ipv6.mr6_tables)) +static bool ip6mr_can_free_table(struct net *net) +{ + return !check_net(net) || !net->ipv6.mr6_rules_ops; +} + static struct mr_table *ip6mr_mr_table_iter(struct net *net, struct mr_table *mrt) { @@ -125,7 +130,7 @@ static struct mr_table *ip6mr_mr_table_iter(struct net *net, return ret; } -static struct mr_table *ip6mr_get_table(struct net *net, u32 id) +static struct mr_table *__ip6mr_get_table(struct net *net, u32 id) { struct mr_table *mrt; @@ -136,6 +141,16 @@ static struct mr_table *ip6mr_get_table(struct net *net, u32 id) return NULL; } +static struct mr_table *ip6mr_get_table(struct net *net, u32 id) +{ + struct mr_table *mrt; + + rcu_read_lock(); + mrt = __ip6mr_get_table(net, id); + rcu_read_unlock(); + return mrt; +} + static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, struct mr_table **mrt) { @@ -177,7 +192,7 @@ static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp, arg->table = fib_rule_get_table(rule, arg); - mrt = ip6mr_get_table(rule->fr_net, arg->table); + mrt = __ip6mr_get_table(rule->fr_net, arg->table); if (!mrt) return -EAGAIN; res->mrt = mrt; @@ -291,6 +306,11 @@ EXPORT_SYMBOL(ip6mr_rule_default); #define ip6mr_for_each_table(mrt, net) \ for (mrt = net->ipv6.mrt6; mrt; mrt = NULL) +static bool ip6mr_can_free_table(struct net *net) +{ + return !check_net(net); +} + static struct mr_table *ip6mr_mr_table_iter(struct net *net, struct mr_table *mrt) { @@ -304,6 +324,8 @@ static struct mr_table *ip6mr_get_table(struct net *net, u32 id) return net->ipv6.mrt6; } +#define __ip6mr_get_table ip6mr_get_table + static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, struct mr_table **mrt) { @@ -382,7 +404,7 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id) { struct mr_table *mrt; - mrt = ip6mr_get_table(net, id); + mrt = __ip6mr_get_table(net, id); if (mrt) return mrt; @@ -392,6 +414,10 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id) static void ip6mr_free_table(struct mr_table *mrt) { + struct net *net = read_pnet(&mrt->net); + + WARN_ON_ONCE(!ip6mr_can_free_table(net)); + timer_shutdown_sync(&mrt->ipmr_expire_timer); mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC | MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC); @@ -411,13 +437,15 @@ static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos) struct net *net = seq_file_net(seq); struct mr_table *mrt; - mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); - if (!mrt) + rcu_read_lock(); + mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT); + if (!mrt) { + rcu_read_unlock(); return ERR_PTR(-ENOENT); + } iter->mrt = mrt; - rcu_read_lock(); return mr_vif_seq_start(seq, pos); } @@ -2278,11 +2306,13 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, struct mfc6_cache *cache; struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); - mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); - if (!mrt) + rcu_read_lock(); + mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT); + if (!mrt) { + rcu_read_unlock(); return -ENOENT; + } - rcu_read_lock(); cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); if (!cache && skb->dev) { int vif = ip6mr_find_vif(mrt, skb->dev); @@ -2562,7 +2592,7 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, grp = nla_get_in6_addr(tb[RTA_DST]); tableid = nla_get_u32_default(tb[RTA_TABLE], 0); - mrt = ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT); + mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT); if (!mrt) { NL_SET_ERR_MSG_MOD(extack, "MR table does not exist"); return -ENOENT; @@ -2609,7 +2639,7 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) if (filter.table_id) { struct mr_table *mrt; - mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id); + mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id); if (!mrt) { if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) return skb->len; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index c00323fa9eb66..7929df08d4e02 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1236,7 +1236,9 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, return -EOPNOTSUPP; /* receive/dequeue next skb: - * the function understands MSG_PEEK and, thus, does not dequeue skb */ + * the function understands MSG_PEEK and, thus, does not dequeue skb + * only refcount is increased. + */ skb = skb_recv_datagram(sk, flags, &err); if (!skb) { if (sk->sk_shutdown & RCV_SHUTDOWN) @@ -1252,9 +1254,8 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, cskb = skb; if (skb_copy_datagram_msg(cskb, offset, msg, copied)) { - if (!(flags & MSG_PEEK)) - skb_queue_head(&sk->sk_receive_queue, skb); - return -EFAULT; + err = -EFAULT; + goto err_out; } /* SOCK_SEQPACKET: set MSG_TRUNC if recv buf size is too small */ @@ -1271,11 +1272,8 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, err = put_cmsg(msg, SOL_IUCV, SCM_IUCV_TRGCLS, sizeof(IUCV_SKB_CB(skb)->class), (void *)&IUCV_SKB_CB(skb)->class); - if (err) { - if (!(flags & MSG_PEEK)) - skb_queue_head(&sk->sk_receive_queue, skb); - return err; - } + if (err) + goto err_out; /* Mark read part of skb as used */ if (!(flags & MSG_PEEK)) { @@ -1331,8 +1329,18 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, /* SOCK_SEQPACKET: return real length if MSG_TRUNC is set */ if (sk->sk_type == SOCK_SEQPACKET && (flags & MSG_TRUNC)) copied = rlen; + if (flags & MSG_PEEK) + skb_unref(skb); return copied; + +err_out: + if (!(flags & MSG_PEEK)) + skb_queue_head(&sk->sk_receive_queue, skb); + else + skb_unref(skb); + + return err; } static inline __poll_t iucv_accept_poll(struct sock *parent) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 3eec23ac5ab10..369a2f2e459cd 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1870,15 +1870,31 @@ static __net_exit void l2tp_pre_exit_net(struct net *net) } } +static int l2tp_idr_item_unexpected(int id, void *p, void *data) +{ + const char *idr_name = data; + + pr_err("l2tp: %s IDR not empty at net %d exit\n", idr_name, id); + WARN_ON_ONCE(1); + return 1; +} + static __net_exit void l2tp_exit_net(struct net *net) { struct l2tp_net *pn = l2tp_pernet(net); - WARN_ON_ONCE(!idr_is_empty(&pn->l2tp_v2_session_idr)); + /* Our per-net IDRs should be empty. Check that is so, to + * help catch cleanup races or refcnt leaks. + */ + idr_for_each(&pn->l2tp_v2_session_idr, l2tp_idr_item_unexpected, + "v2_session"); + idr_for_each(&pn->l2tp_v3_session_idr, l2tp_idr_item_unexpected, + "v3_session"); + idr_for_each(&pn->l2tp_tunnel_idr, l2tp_idr_item_unexpected, + "tunnel"); + idr_destroy(&pn->l2tp_v2_session_idr); - WARN_ON_ONCE(!idr_is_empty(&pn->l2tp_v3_session_idr)); idr_destroy(&pn->l2tp_v3_session_idr); - WARN_ON_ONCE(!idr_is_empty(&pn->l2tp_tunnel_idr)); idr_destroy(&pn->l2tp_tunnel_idr); } diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 4eb52add7103b..0259cde394ba0 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -1098,7 +1098,7 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, lock_sock(sk); if (unlikely(level != SOL_LLC || optlen != sizeof(int))) goto out; - rc = copy_from_sockptr(&opt, optval, sizeof(opt)); + rc = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen); if (rc) goto out; rc = -EINVAL; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index dd3517b0fdfd5..f4e7b5e4bb59f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2181,9 +2181,14 @@ netlink_ack_tlv_len(struct netlink_sock *nlk, int err, return tlvlen; } +static bool nlmsg_check_in_payload(const struct nlmsghdr *nlh, const void *addr) +{ + return !WARN_ON(addr < nlmsg_data(nlh) || + addr - (const void *) nlh >= nlh->nlmsg_len); +} + static void -netlink_ack_tlv_fill(struct sk_buff *in_skb, struct sk_buff *skb, - const struct nlmsghdr *nlh, int err, +netlink_ack_tlv_fill(struct sk_buff *skb, const struct nlmsghdr *nlh, int err, const struct netlink_ext_ack *extack) { if (extack->_msg) @@ -2195,9 +2200,7 @@ netlink_ack_tlv_fill(struct sk_buff *in_skb, struct sk_buff *skb, if (!err) return; - if (extack->bad_attr && - !WARN_ON((u8 *)extack->bad_attr < in_skb->data || - (u8 *)extack->bad_attr >= in_skb->data + in_skb->len)) + if (extack->bad_attr && nlmsg_check_in_payload(nlh, extack->bad_attr)) WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, (u8 *)extack->bad_attr - (const u8 *)nlh)); if (extack->policy) @@ -2206,9 +2209,7 @@ netlink_ack_tlv_fill(struct sk_buff *in_skb, struct sk_buff *skb, if (extack->miss_type) WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_MISS_TYPE, extack->miss_type)); - if (extack->miss_nest && - !WARN_ON((u8 *)extack->miss_nest < in_skb->data || - (u8 *)extack->miss_nest > in_skb->data + in_skb->len)) + if (extack->miss_nest && nlmsg_check_in_payload(nlh, extack->miss_nest)) WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_MISS_NEST, (u8 *)extack->miss_nest - (const u8 *)nlh)); } @@ -2237,7 +2238,7 @@ static int netlink_dump_done(struct netlink_sock *nlk, struct sk_buff *skb, if (extack_len) { nlh->nlmsg_flags |= NLM_F_ACK_TLVS; if (skb_tailroom(skb) >= extack_len) { - netlink_ack_tlv_fill(cb->skb, skb, cb->nlh, + netlink_ack_tlv_fill(skb, cb->nlh, nlk->dump_done_errno, extack); nlmsg_end(skb, nlh); } @@ -2496,7 +2497,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, } if (tlvlen) - netlink_ack_tlv_fill(in_skb, skb, nlh, err, extack); + netlink_ack_tlv_fill(skb, nlh, err, extack); nlmsg_end(skb, rep); diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index f4844683e1203..9d8bd0b37e41d 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -707,9 +707,10 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, ret = -EISCONN; if (rx->sk.sk_state != RXRPC_UNBOUND) goto error; - ret = copy_from_sockptr(&min_sec_level, optval, - sizeof(unsigned int)); - if (ret < 0) + ret = copy_safe_from_sockptr(&min_sec_level, + sizeof(min_sec_level), + optval, optlen); + if (ret) goto error; ret = -EINVAL; if (min_sec_level > RXRPC_SECURITY_MAX) diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index a97638bef6da5..a5e87f9ea9861 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -332,6 +332,12 @@ static bool fq_fastpath_check(const struct Qdisc *sch, struct sk_buff *skb, */ if (q->internal.qlen >= 8) return false; + + /* Ordering invariants fall apart if some delayed flows + * are ready but we haven't serviced them, yet. + */ + if (q->time_next_delayed_flow <= now + q->offload_horizon) + return false; } sk = skb->sk; diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 1bd3e531b0e09..059f6ef1ad189 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1427,7 +1427,9 @@ static int c_show(struct seq_file *m, void *p) seq_printf(m, "# expiry=%lld refcnt=%d flags=%lx\n", convert_to_wallclock(cp->expiry_time), kref_read(&cp->ref), cp->flags); - cache_get(cp); + if (!cache_get_rcu(cp)) + return 0; + if (cache_check(cd, cp, NULL)) /* cache_check does a cache_put on failure */ seq_puts(m, "# "); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index b785425c33151..95397677673bb 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1551,6 +1551,10 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv, newlen = error; if (protocol == IPPROTO_TCP) { + __netns_tracker_free(net, &sock->sk->ns_tracker, false); + sock->sk->sk_net_refcnt = 1; + get_net_track(net, &sock->sk->ns_tracker, GFP_KERNEL); + sock_inuse_add(net, 1); if ((error = kernel_listen(sock, 64)) < 0) goto bummer; } diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index 58ae6ec4f25b4..415c0310101f0 100644 --- a/net/sunrpc/xprtrdma/svc_rdma.c +++ b/net/sunrpc/xprtrdma/svc_rdma.c @@ -233,25 +233,34 @@ static int svc_rdma_proc_init(void) rc = percpu_counter_init(&svcrdma_stat_read, 0, GFP_KERNEL); if (rc) - goto out_err; + goto err; rc = percpu_counter_init(&svcrdma_stat_recv, 0, GFP_KERNEL); if (rc) - goto out_err; + goto err_read; rc = percpu_counter_init(&svcrdma_stat_sq_starve, 0, GFP_KERNEL); if (rc) - goto out_err; + goto err_recv; rc = percpu_counter_init(&svcrdma_stat_write, 0, GFP_KERNEL); if (rc) - goto out_err; + goto err_sq; svcrdma_table_header = register_sysctl("sunrpc/svc_rdma", svcrdma_parm_table); + if (!svcrdma_table_header) + goto err_write; + return 0; -out_err: +err_write: + rc = -ENOMEM; + percpu_counter_destroy(&svcrdma_stat_write); +err_sq: percpu_counter_destroy(&svcrdma_stat_sq_starve); +err_recv: percpu_counter_destroy(&svcrdma_stat_recv); +err_read: percpu_counter_destroy(&svcrdma_stat_read); +err: return rc; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index ae3fb9bc8a216..292022f0976e1 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -493,7 +493,13 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt) if (xdr_stream_decode_u32(&rctxt->rc_stream, &segcount)) return false; - /* A bogus segcount causes this buffer overflow check to fail. */ + /* Before trusting the segcount value enough to use it in + * a computation, perform a simple range check. This is an + * arbitrary but sensible limit (ie, not architectural). + */ + if (unlikely(segcount > RPCSVC_MAXPAGES)) + return false; + p = xdr_inline_decode(&rctxt->rc_stream, segcount * rpcrdma_segment_maxsz * sizeof(*p)); return p != NULL; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 1326fbf45a347..c60936d8cef71 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1198,6 +1198,7 @@ static void xs_sock_reset_state_flags(struct rpc_xprt *xprt) clear_bit(XPRT_SOCK_WAKE_WRITE, &transport->sock_state); clear_bit(XPRT_SOCK_WAKE_DISCONNECT, &transport->sock_state); clear_bit(XPRT_SOCK_NOSPACE, &transport->sock_state); + clear_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state); } static void xs_run_error_worker(struct sock_xprt *transport, unsigned int nr) @@ -1278,6 +1279,7 @@ static void xs_reset_transport(struct sock_xprt *transport) transport->file = NULL; sk->sk_user_data = NULL; + sk->sk_sndtimeo = 0; xs_restore_old_callbacks(transport, sk); xprt_clear_connected(xprt); @@ -1939,6 +1941,13 @@ static struct socket *xs_create_sock(struct rpc_xprt *xprt, goto out; } + if (protocol == IPPROTO_TCP) { + __netns_tracker_free(xprt->xprt_net, &sock->sk->ns_tracker, false); + sock->sk->sk_net_refcnt = 1; + get_net_track(xprt->xprt_net, &sock->sk->ns_tracker, GFP_KERNEL); + sock_inuse_add(xprt->xprt_net, 1); + } + filp = sock_alloc_file(sock, O_NONBLOCK, NULL); if (IS_ERR(filp)) return ERR_CAST(filp); @@ -2614,11 +2623,10 @@ static int xs_tls_handshake_sync(struct rpc_xprt *lower_xprt, struct xprtsec_par rc = wait_for_completion_interruptible_timeout(&lower_transport->handshake_done, XS_TLS_HANDSHAKE_TO); if (rc <= 0) { - if (!tls_handshake_cancel(sk)) { - if (rc == 0) - rc = -ETIMEDOUT; - goto out_put_xprt; - } + tls_handshake_cancel(sk); + if (rc == 0) + rc = -ETIMEDOUT; + goto out_put_xprt; } rc = lower_transport->xprt_err; diff --git a/rust/Makefile b/rust/Makefile index b5e0a73b78f3e..9da9042fd6279 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -3,7 +3,7 @@ # Where to place rustdoc generated documentation rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc -obj-$(CONFIG_RUST) += core.o compiler_builtins.o +obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o always-$(CONFIG_RUST) += exports_core_generated.h # Missing prototypes are expected in the helpers since these are exported @@ -15,8 +15,8 @@ always-$(CONFIG_RUST) += libmacros.so no-clean-files += libmacros.so always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs -obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o -always-$(CONFIG_RUST) += exports_alloc_generated.h exports_helpers_generated.h \ +obj-$(CONFIG_RUST) += bindings.o kernel.o +always-$(CONFIG_RUST) += exports_helpers_generated.h \ exports_bindings_generated.h exports_kernel_generated.h always-$(CONFIG_RUST) += uapi/uapi_generated.rs @@ -36,6 +36,8 @@ always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.o obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o +always-$(subst y,$(CONFIG_RUST),$(CONFIG_JUMP_LABEL)) += kernel/generated_arch_static_branch_asm.rs + # Avoids running `$(RUSTC)` for the sysroot when it may not be available. ifdef CONFIG_RUST @@ -53,15 +55,10 @@ endif core-cfgs = \ --cfg no_fp_fmt_parse -alloc-cfgs = \ - --cfg no_global_oom_handling \ - --cfg no_rc \ - --cfg no_sync - quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $< cmd_rustdoc = \ OBJTREE=$(abspath $(objtree)) \ - $(RUSTDOC) $(if $(rustdoc_host),$(rust_common_flags),$(rust_flags)) \ + $(RUSTDOC) $(filter-out $(skip_flags),$(if $(rustdoc_host),$(rust_common_flags),$(rust_flags))) \ $(rustc_target_flags) -L$(objtree)/$(obj) \ -Zunstable-options --generate-link-to-definition \ --output $(rustdoc_output) \ @@ -81,7 +78,7 @@ quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $< # command-like flags to solve the issue. Meanwhile, we use the non-custom case # and then retouch the generated files. rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \ - rustdoc-alloc rustdoc-kernel + rustdoc-kernel $(Q)cp $(srctree)/Documentation/images/logo.svg $(rustdoc_output)/static.files/ $(Q)cp $(srctree)/Documentation/images/COPYING-logo $(rustdoc_output)/static.files/ $(Q)find $(rustdoc_output) -name '*.html' -type f -print0 | xargs -0 sed -Ei \ @@ -98,6 +95,9 @@ rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \ rustdoc-macros: $(src)/macros/lib.rs FORCE +$(call if_changed,rustdoc) +# Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should +# not be needed -- see https://github.com/rust-lang/rust/pull/128307. +rustdoc-core: private skip_flags = -Wrustdoc::unescaped_backticks rustdoc-core: private rustc_target_flags = $(core-cfgs) rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs FORCE +$(call if_changed,rustdoc) @@ -105,20 +105,14 @@ rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs FORCE rustdoc-compiler_builtins: $(src)/compiler_builtins.rs rustdoc-core FORCE +$(call if_changed,rustdoc) -# We need to allow `rustdoc::broken_intra_doc_links` because some -# `no_global_oom_handling` functions refer to non-`no_global_oom_handling` -# functions. Ideally `rustdoc` would have a way to distinguish broken links -# due to things that are "configured out" vs. entirely non-existing ones. -rustdoc-alloc: private rustc_target_flags = $(alloc-cfgs) \ - -Arustdoc::broken_intra_doc_links -rustdoc-alloc: $(RUST_LIB_SRC)/alloc/src/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE +rustdoc-ffi: $(src)/ffi.rs rustdoc-core FORCE +$(call if_changed,rustdoc) -rustdoc-kernel: private rustc_target_flags = --extern alloc \ +rustdoc-kernel: private rustc_target_flags = --extern ffi \ --extern build_error --extern macros=$(objtree)/$(obj)/libmacros.so \ --extern bindings --extern uapi -rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \ - rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \ +rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-ffi rustdoc-macros \ + rustdoc-compiler_builtins $(obj)/libmacros.so \ $(obj)/bindings.o FORCE +$(call if_changed,rustdoc) @@ -135,19 +129,33 @@ quiet_cmd_rustc_test_library = RUSTC TL $< rusttestlib-build_error: $(src)/build_error.rs FORCE +$(call if_changed,rustc_test_library) +rusttestlib-ffi: $(src)/ffi.rs FORCE + +$(call if_changed,rustc_test_library) + rusttestlib-macros: private rustc_target_flags = --extern proc_macro rusttestlib-macros: private rustc_test_library_proc = yes rusttestlib-macros: $(src)/macros/lib.rs FORCE +$(call if_changed,rustc_test_library) -rusttestlib-bindings: $(src)/bindings/lib.rs FORCE +rusttestlib-kernel: private rustc_target_flags = --extern ffi \ + --extern build_error --extern macros \ + --extern bindings --extern uapi +rusttestlib-kernel: $(src)/kernel/lib.rs \ + rusttestlib-bindings rusttestlib-uapi rusttestlib-build_error \ + $(obj)/libmacros.so $(obj)/bindings.o FORCE + +$(call if_changed,rustc_test_library) + +rusttestlib-bindings: private rustc_target_flags = --extern ffi +rusttestlib-bindings: $(src)/bindings/lib.rs rusttestlib-ffi FORCE +$(call if_changed,rustc_test_library) -rusttestlib-uapi: $(src)/uapi/lib.rs FORCE +rusttestlib-uapi: private rustc_target_flags = --extern ffi +rusttestlib-uapi: $(src)/uapi/lib.rs rusttestlib-ffi FORCE +$(call if_changed,rustc_test_library) quiet_cmd_rustdoc_test = RUSTDOC T $< cmd_rustdoc_test = \ + RUST_MODFILE=test.rs \ OBJTREE=$(abspath $(objtree)) \ $(RUSTDOC) --test $(rust_common_flags) \ @$(objtree)/include/generated/rustc_cfg \ @@ -162,7 +170,7 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $< mkdir -p $(objtree)/$(obj)/test/doctests/kernel; \ OBJTREE=$(abspath $(objtree)) \ $(RUSTDOC) --test $(rust_flags) \ - -L$(objtree)/$(obj) --extern alloc --extern kernel \ + -L$(objtree)/$(obj) --extern ffi --extern kernel \ --extern build_error --extern macros \ --extern bindings --extern uapi \ --no-run --crate-name kernel -Zunstable-options \ @@ -192,19 +200,20 @@ quiet_cmd_rustc_test = RUSTC T $< rusttest: rusttest-macros rusttest-kernel -rusttest-macros: private rustc_target_flags = --extern proc_macro +rusttest-macros: private rustc_target_flags = --extern proc_macro \ + --extern macros --extern kernel rusttest-macros: private rustdoc_test_target_flags = --crate-type proc-macro -rusttest-macros: $(src)/macros/lib.rs FORCE +rusttest-macros: $(src)/macros/lib.rs \ + rusttestlib-macros rusttestlib-kernel FORCE +$(call if_changed,rustc_test) +$(call if_changed,rustdoc_test) -rusttest-kernel: private rustc_target_flags = --extern alloc \ +rusttest-kernel: private rustc_target_flags = --extern ffi \ --extern build_error --extern macros --extern bindings --extern uapi -rusttest-kernel: $(src)/kernel/lib.rs \ +rusttest-kernel: $(src)/kernel/lib.rs rusttestlib-ffi rusttestlib-kernel \ rusttestlib-build_error rusttestlib-macros rusttestlib-bindings \ rusttestlib-uapi FORCE +$(call if_changed,rustc_test) - +$(call if_changed,rustc_test_library) ifdef CONFIG_CC_IS_CLANG bindgen_c_flags = $(c_flags) @@ -265,12 +274,16 @@ else bindgen_c_flags_lto = $(bindgen_c_flags) endif -bindgen_c_flags_final = $(bindgen_c_flags_lto) -D__BINDGEN__ +# `-fno-builtin` is passed to avoid `bindgen` from using `clang` builtin +# prototypes for functions like `memcpy` -- if this flag is not passed, +# `bindgen`-generated prototypes use `c_ulong` or `c_uint` depending on +# architecture instead of generating `usize`. +bindgen_c_flags_final = $(bindgen_c_flags_lto) -fno-builtin -D__BINDGEN__ quiet_cmd_bindgen = BINDGEN $@ cmd_bindgen = \ $(BINDGEN) $< $(bindgen_target_flags) \ - --use-core --with-derive-default --ctypes-prefix core::ffi --no-layout-tests \ + --use-core --with-derive-default --ctypes-prefix ffi --no-layout-tests \ --no-debug '.*' --enable-function-attribute-detection \ -o $@ -- $(bindgen_c_flags_final) -DMODULE \ $(bindgen_target_cflags) $(bindgen_target_extra) @@ -311,9 +324,6 @@ quiet_cmd_exports = EXPORTS $@ $(obj)/exports_core_generated.h: $(obj)/core.o FORCE $(call if_changed,exports) -$(obj)/exports_alloc_generated.h: $(obj)/alloc.o FORCE - $(call if_changed,exports) - # Even though Rust kernel modules should never use the bindings directly, # symbols from the `bindings` crate and the C helpers need to be exported # because Rust generics and inlined functions may not get their code generated @@ -360,10 +370,10 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L rust-analyzer: $(Q)$(srctree)/scripts/generate_rust_analyzer.py \ - --cfgs='core=$(core-cfgs)' --cfgs='alloc=$(alloc-cfgs)' \ + --cfgs='core=$(core-cfgs)' \ $(realpath $(srctree)) $(realpath $(objtree)) \ - $(rustc_sysroot) $(RUST_LIB_SRC) $(KBUILD_EXTMOD) > \ - $(if $(KBUILD_EXTMOD),$(extmod_prefix),$(objtree))/rust-project.json + $(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \ + > rust-project.json redirect-intrinsics = \ __addsf3 __eqsf2 __extendsfdf2 __gesf2 __lesf2 __ltsf2 __mulsf3 __nesf2 __truncdfsf2 __unordsf2 \ @@ -398,30 +408,33 @@ $(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*' $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE +$(call if_changed_rule,rustc_library) -$(obj)/alloc.o: private skip_clippy = 1 -$(obj)/alloc.o: private skip_flags = -Wunreachable_pub -$(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs) -$(obj)/alloc.o: $(RUST_LIB_SRC)/alloc/src/lib.rs $(obj)/compiler_builtins.o FORCE +$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE +$(call if_changed_rule,rustc_library) -$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE +$(obj)/ffi.o: $(src)/ffi.rs $(obj)/compiler_builtins.o FORCE +$(call if_changed_rule,rustc_library) +$(obj)/bindings.o: private rustc_target_flags = --extern ffi $(obj)/bindings.o: $(src)/bindings/lib.rs \ - $(obj)/compiler_builtins.o \ + $(obj)/ffi.o \ $(obj)/bindings/bindings_generated.rs \ $(obj)/bindings/bindings_helpers_generated.rs FORCE +$(call if_changed_rule,rustc_library) +$(obj)/uapi.o: private rustc_target_flags = --extern ffi $(obj)/uapi.o: $(src)/uapi/lib.rs \ - $(obj)/compiler_builtins.o \ + $(obj)/ffi.o \ $(obj)/uapi/uapi_generated.rs FORCE +$(call if_changed_rule,rustc_library) -$(obj)/kernel.o: private rustc_target_flags = --extern alloc \ +$(obj)/kernel.o: private rustc_target_flags = --extern ffi \ --extern build_error --extern macros --extern bindings --extern uapi -$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \ +$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/build_error.o \ $(obj)/libmacros.so $(obj)/bindings.o $(obj)/uapi.o FORCE +$(call if_changed_rule,rustc_library) +ifdef CONFIG_JUMP_LABEL +$(obj)/kernel.o: $(obj)/kernel/generated_arch_static_branch_asm.rs +endif + endif # CONFIG_RUST diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters index b7c7483123b7a..0f96af8b9a7fe 100644 --- a/rust/bindgen_parameters +++ b/rust/bindgen_parameters @@ -1,5 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 +# We want to map these types to `isize`/`usize` manually, instead of +# define them as `int`/`long` depending on platform bitwidth. +--blocklist-type __kernel_s?size_t +--blocklist-type __kernel_ptrdiff_t + --opaque-type xregs_state --opaque-type desc_struct --opaque-type arch_lbr_state diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ca13659ded4c4..5c4dfe22f41a5 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -25,8 +27,10 @@ #include #include #include +#include #include #include +#include /* `bindgen` gets confused at certain things. */ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; @@ -37,4 +41,5 @@ const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT; const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT; const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO; const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM; +const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN; const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL; diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 93a1a3fc97bc9..014af0d1fc70c 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -25,7 +25,13 @@ )] #[allow(dead_code)] +#[allow(clippy::undocumented_unsafe_blocks)] mod bindings_raw { + // Manual definition for blocklisted types. + type __kernel_size_t = usize; + type __kernel_ssize_t = isize; + type __kernel_ptrdiff_t = isize; + // Use glob import here to expose all helpers. // Symbols defined within the module will take precedence to the glob import. pub use super::bindings_helper::*; diff --git a/rust/exports.c b/rust/exports.c index e5695f3b45b7a..587f0e776aba5 100644 --- a/rust/exports.c +++ b/rust/exports.c @@ -3,9 +3,9 @@ * A hack to export Rust symbols for loadable modules without having to redo * the entire `include/linux/export.h` logic in Rust. * - * This requires the Rust's new/future `v0` mangling scheme because the default - * one ("legacy") uses invalid characters for C identifiers (thus we cannot use - * the `EXPORT_SYMBOL_*` macros). + * This requires Rust's new/future `v0` mangling scheme because the default one + * ("legacy") uses invalid characters for C identifiers (thus we cannot use the + * `EXPORT_SYMBOL_*` macros). * * All symbols are exported as GPL-only to guarantee no GPL-only feature is * accidentally exposed. @@ -16,7 +16,6 @@ #define EXPORT_SYMBOL_RUST_GPL(sym) extern int sym; EXPORT_SYMBOL_GPL(sym) #include "exports_core_generated.h" -#include "exports_alloc_generated.h" #include "exports_helpers_generated.h" #include "exports_bindings_generated.h" #include "exports_kernel_generated.h" diff --git a/rust/ffi.rs b/rust/ffi.rs new file mode 100644 index 0000000000000..be153c4d551b2 --- /dev/null +++ b/rust/ffi.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Foreign function interface (FFI) types. +//! +//! This crate provides mapping from C primitive types to Rust ones. +//! +//! The Rust [`core`] crate provides [`core::ffi`], which maps integer types to the platform default +//! C ABI. The kernel does not use [`core::ffi`], so it can customise the mapping that deviates from +//! the platform default. + +#![no_std] + +pub use core::ffi::*; diff --git a/rust/helpers/build_bug.c b/rust/helpers/build_bug.c index e994f7b5928c0..44e5794880373 100644 --- a/rust/helpers/build_bug.c +++ b/rust/helpers/build_bug.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include const char *rust_helper_errname(int err) diff --git a/rust/helpers/err.c b/rust/helpers/err.c index be3d45ef78a25..544c7cb866328 100644 --- a/rust/helpers/err.c +++ b/rust/helpers/err.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include __force void *rust_helper_ERR_PTR(long err) { diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 62022b18caf5e..dcf827a61b52e 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -14,9 +14,11 @@ #include "cred.c" #include "err.c" #include "fs.c" +#include "jump_label.c" #include "kunit.c" #include "mutex.c" #include "page.c" +#include "pid_namespace.c" #include "rbtree.c" #include "refcount.c" #include "security.c" @@ -25,5 +27,6 @@ #include "spinlock.c" #include "task.c" #include "uaccess.c" +#include "vmalloc.c" #include "wait.c" #include "workqueue.c" diff --git a/rust/helpers/jump_label.c b/rust/helpers/jump_label.c new file mode 100644 index 0000000000000..fc1f1e0df08e7 --- /dev/null +++ b/rust/helpers/jump_label.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2024 Google LLC. + */ + +#include + +#ifndef CONFIG_JUMP_LABEL +int rust_helper_static_key_count(struct static_key *key) +{ + return static_key_count(key); +} +#endif diff --git a/rust/helpers/kunit.c b/rust/helpers/kunit.c index 9d725067eb3bc..b85a4d394c118 100644 --- a/rust/helpers/kunit.c +++ b/rust/helpers/kunit.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include struct kunit *rust_helper_kunit_get_current_test(void) { diff --git a/rust/helpers/mutex.c b/rust/helpers/mutex.c index a17ca8cdb50ca..7e00680958ef1 100644 --- a/rust/helpers/mutex.c +++ b/rust/helpers/mutex.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include void rust_helper_mutex_lock(struct mutex *lock) diff --git a/rust/helpers/pid_namespace.c b/rust/helpers/pid_namespace.c new file mode 100644 index 0000000000000..f41482bdec9a7 --- /dev/null +++ b/rust/helpers/pid_namespace.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +struct pid_namespace *rust_helper_get_pid_ns(struct pid_namespace *ns) +{ + return get_pid_ns(ns); +} + +void rust_helper_put_pid_ns(struct pid_namespace *ns) +{ + put_pid_ns(ns); +} + +/* Get a reference on a task's pid namespace. */ +struct pid_namespace *rust_helper_task_get_pid_ns(struct task_struct *task) +{ + struct pid_namespace *pid_ns; + + guard(rcu)(); + pid_ns = task_active_pid_ns(task); + if (pid_ns) + get_pid_ns(pid_ns); + return pid_ns; +} diff --git a/rust/helpers/refcount.c b/rust/helpers/refcount.c index f47afc148ec36..d6adbd2e45a18 100644 --- a/rust/helpers/refcount.c +++ b/rust/helpers/refcount.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include refcount_t rust_helper_REFCOUNT_INIT(int n) diff --git a/rust/helpers/signal.c b/rust/helpers/signal.c index 63c407f80c26b..1a6bbe9438e28 100644 --- a/rust/helpers/signal.c +++ b/rust/helpers/signal.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include int rust_helper_signal_pending(struct task_struct *t) diff --git a/rust/helpers/slab.c b/rust/helpers/slab.c index f043e087f9d66..a842bfbddcba9 100644 --- a/rust/helpers/slab.c +++ b/rust/helpers/slab.c @@ -7,3 +7,9 @@ rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) { return krealloc(objp, new_size, flags); } + +void * __must_check __realloc_size(2) +rust_helper_kvrealloc(const void *p, size_t size, gfp_t flags) +{ + return kvrealloc(p, size, flags); +} diff --git a/rust/helpers/spinlock.c b/rust/helpers/spinlock.c index 92f7fc4184253..5971fdf6f7550 100644 --- a/rust/helpers/spinlock.c +++ b/rust/helpers/spinlock.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include void rust_helper___spin_lock_init(spinlock_t *lock, const char *name, @@ -26,3 +25,8 @@ void rust_helper_spin_unlock(spinlock_t *lock) { spin_unlock(lock); } + +int rust_helper_spin_trylock(spinlock_t *lock) +{ + return spin_trylock(lock); +} diff --git a/rust/helpers/task.c b/rust/helpers/task.c index 7d66487db8312..31c33ea2dce6f 100644 --- a/rust/helpers/task.c +++ b/rust/helpers/task.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include struct task_struct *rust_helper_get_current(void) diff --git a/rust/helpers/vmalloc.c b/rust/helpers/vmalloc.c new file mode 100644 index 0000000000000..80d34501bbc01 --- /dev/null +++ b/rust/helpers/vmalloc.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void * __must_check __realloc_size(2) +rust_helper_vrealloc(const void *p, size_t size, gfp_t flags) +{ + return vrealloc(p, size, flags); +} diff --git a/rust/helpers/wait.c b/rust/helpers/wait.c index c7336bbf27507..ae48e33d9da3b 100644 --- a/rust/helpers/wait.c +++ b/rust/helpers/wait.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include void rust_helper_init_wait(struct wait_queue_entry *wq_entry) diff --git a/rust/helpers/workqueue.c b/rust/helpers/workqueue.c index f59427acc3237..b2b82753509bf 100644 --- a/rust/helpers/workqueue.c +++ b/rust/helpers/workqueue.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, diff --git a/rust/kernel/.gitignore b/rust/kernel/.gitignore new file mode 100644 index 0000000000000..6ba39a178f309 --- /dev/null +++ b/rust/kernel/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +/generated_arch_static_branch_asm.rs diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index 1966bd4070174..f2f7f3a53d298 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -1,23 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 -//! Extensions to the [`alloc`] crate. +//! Implementation of the kernel's memory allocation infrastructure. -#[cfg(not(test))] -#[cfg(not(testlib))] -mod allocator; -pub mod box_ext; -pub mod vec_ext; +#[cfg(not(any(test, testlib)))] +pub mod allocator; +pub mod kbox; +pub mod kvec; +pub mod layout; + +#[cfg(any(test, testlib))] +pub mod allocator_test; + +#[cfg(any(test, testlib))] +pub use self::allocator_test as allocator; + +pub use self::kbox::Box; +pub use self::kbox::KBox; +pub use self::kbox::KVBox; +pub use self::kbox::VBox; + +pub use self::kvec::IntoIter; +pub use self::kvec::KVVec; +pub use self::kvec::KVec; +pub use self::kvec::VVec; +pub use self::kvec::Vec; /// Indicates an allocation error. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct AllocError; +use core::{alloc::Layout, ptr::NonNull}; /// Flags to be used when allocating memory. /// /// They can be combined with the operators `|`, `&`, and `!`. /// /// Values can be used from the [`flags`] module. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub struct Flags(u32); impl Flags { @@ -25,6 +43,11 @@ impl Flags { pub(crate) fn as_raw(self) -> u32 { self.0 } + + /// Check whether `flags` is contained in `self`. + pub fn contains(self, flags: Flags) -> bool { + (self & flags) == flags + } } impl core::ops::BitOr for Flags { @@ -85,4 +108,117 @@ pub mod flags { /// use any filesystem callback. It is very likely to fail to allocate memory, even for very /// small allocations. pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT); + + /// Suppresses allocation failure reports. + /// + /// This is normally or'd with other flags. + pub const __GFP_NOWARN: Flags = Flags(bindings::__GFP_NOWARN); +} + +/// The kernel's [`Allocator`] trait. +/// +/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffers described +/// via [`Layout`]. +/// +/// [`Allocator`] is designed to be implemented as a ZST; [`Allocator`] functions do not operate on +/// an object instance. +/// +/// In order to be able to support `#[derive(SmartPointer)]` later on, we need to avoid a design +/// that requires an `Allocator` to be instantiated, hence its functions must not contain any kind +/// of `self` parameter. +/// +/// # Safety +/// +/// - A memory allocation returned from an allocator must remain valid until it is explicitly freed. +/// +/// - Any pointer to a valid memory allocation must be valid to be passed to any other [`Allocator`] +/// function of the same type. +/// +/// - Implementers must ensure that all trait functions abide by the guarantees documented in the +/// `# Guarantees` sections. +pub unsafe trait Allocator { + /// Allocate memory based on `layout` and `flags`. + /// + /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout + /// constraints (i.e. minimum size and alignment as specified by `layout`). + /// + /// This function is equivalent to `realloc` when called with `None`. + /// + /// # Guarantees + /// + /// When the return value is `Ok(ptr)`, then `ptr` is + /// - valid for reads and writes for `layout.size()` bytes, until it is passed to + /// [`Allocator::free`] or [`Allocator::realloc`], + /// - aligned to `layout.align()`, + /// + /// Additionally, `Flags` are honored as documented in + /// . + fn alloc(layout: Layout, flags: Flags) -> Result, AllocError> { + // SAFETY: Passing `None` to `realloc` is valid by its safety requirements and asks for a + // new memory allocation. + unsafe { Self::realloc(None, layout, Layout::new::<()>(), flags) } + } + + /// Re-allocate an existing memory allocation to satisfy the requested `layout`. + /// + /// If the requested size is zero, `realloc` behaves equivalent to `free`. + /// + /// If the requested size is larger than the size of the existing allocation, a successful call + /// to `realloc` guarantees that the new or grown buffer has at least `Layout::size` bytes, but + /// may also be larger. + /// + /// If the requested size is smaller than the size of the existing allocation, `realloc` may or + /// may not shrink the buffer; this is implementation specific to the allocator. + /// + /// On allocation failure, the existing buffer, if any, remains valid. + /// + /// The buffer is represented as `NonNull<[u8]>`. + /// + /// # Safety + /// + /// - If `ptr == Some(p)`, then `p` must point to an existing and valid memory allocation + /// created by this [`Allocator`]; if `old_layout` is zero-sized `p` does not need to be a + /// pointer returned by this [`Allocator`]. + /// - `ptr` is allowed to be `None`; in this case a new memory allocation is created and + /// `old_layout` is ignored. + /// - `old_layout` must match the `Layout` the allocation has been created with. + /// + /// # Guarantees + /// + /// This function has the same guarantees as [`Allocator::alloc`]. When `ptr == Some(p)`, then + /// it additionally guarantees that: + /// - the contents of the memory pointed to by `p` are preserved up to the lesser of the new + /// and old size, i.e. `ret_ptr[0..min(layout.size(), old_layout.size())] == + /// p[0..min(layout.size(), old_layout.size())]`. + /// - when the return value is `Err(AllocError)`, then `ptr` is still valid. + unsafe fn realloc( + ptr: Option>, + layout: Layout, + old_layout: Layout, + flags: Flags, + ) -> Result, AllocError>; + + /// Free an existing memory allocation. + /// + /// # Safety + /// + /// - `ptr` must point to an existing and valid memory allocation created by this [`Allocator`]; + /// if `old_layout` is zero-sized `p` does not need to be a pointer returned by this + /// [`Allocator`]. + /// - `layout` must match the `Layout` the allocation has been created with. + /// - The memory allocation at `ptr` must never again be read from or written to. + unsafe fn free(ptr: NonNull, layout: Layout) { + // SAFETY: The caller guarantees that `ptr` points at a valid allocation created by this + // allocator. We are passing a `Layout` with the smallest possible alignment, so it is + // smaller than or equal to the alignment previously used with this allocation. + let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), layout, Flags(0)) }; + } +} + +/// Returns a properly aligned dangling pointer from the given `layout`. +pub(crate) fn dangling_from_layout(layout: Layout) -> NonNull { + let ptr = layout.align() as *mut u8; + + // SAFETY: `layout.align()` (and hence `ptr`) is guaranteed to be non-zero. + unsafe { NonNull::new_unchecked(ptr) } } diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs index e6ea601f38c6d..439985e29fbc0 100644 --- a/rust/kernel/alloc/allocator.rs +++ b/rust/kernel/alloc/allocator.rs @@ -1,74 +1,188 @@ // SPDX-License-Identifier: GPL-2.0 //! Allocator support. +//! +//! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide" +//! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the +//! typical application of the different kernel allocators. +//! +//! Reference: -use super::{flags::*, Flags}; -use core::alloc::{GlobalAlloc, Layout}; +use super::Flags; +use core::alloc::Layout; use core::ptr; +use core::ptr::NonNull; -struct KernelAllocator; +use crate::alloc::{AllocError, Allocator}; +use crate::bindings; +use crate::pr_warn; -/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. +/// The contiguous kernel allocator. /// -/// # Safety +/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also +/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. /// -/// - `ptr` can be either null or a pointer which has been allocated by this allocator. -/// - `new_layout` must have a non-zero size. -pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 { +/// For more details see [self]. +pub struct Kmalloc; + +/// The virtually contiguous kernel allocator. +/// +/// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel +/// virtual space. It is typically used for large allocations. The memory allocated with this +/// allocator is not physically contiguous. +/// +/// For more details see [self]. +pub struct Vmalloc; + +/// The kvmalloc kernel allocator. +/// +/// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon +/// failure. This allocator is typically used when the size for the requested allocation is not +/// known and may exceed the capabilities of `Kmalloc`. +/// +/// For more details see [self]. +pub struct KVmalloc; + +/// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. +fn aligned_size(new_layout: Layout) -> usize { // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. let layout = new_layout.pad_to_align(); // Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()` // which together with the slab guarantees means the `krealloc` will return a properly aligned // object (see comments in `kmalloc()` for more information). - let size = layout.size(); - - // SAFETY: - // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the - // function safety requirement. - // - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according - // to the function safety requirement) - unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 } + layout.size() } -unsafe impl GlobalAlloc for KernelAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety - // requirement. - unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL) } - } +/// # Invariants +/// +/// One of the following: `krealloc`, `vrealloc`, `kvrealloc`. +struct ReallocFunc( + unsafe extern "C" fn(*const crate::ffi::c_void, usize, u32) -> *mut crate::ffi::c_void, +); - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - unsafe { - bindings::kfree(ptr as *const core::ffi::c_void); - } - } +impl ReallocFunc { + // INVARIANT: `krealloc` satisfies the type invariants. + const KREALLOC: Self = Self(bindings::krealloc); - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: - // - `new_size`, when rounded up to the nearest multiple of `layout.align()`, will not - // overflow `isize` by the function safety requirement. - // - `layout.align()` is a proper alignment (i.e. not zero and must be a power of two). - let layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; + // INVARIANT: `vrealloc` satisfies the type invariants. + const VREALLOC: Self = Self(bindings::vrealloc); + + // INVARIANT: `kvrealloc` satisfies the type invariants. + const KVREALLOC: Self = Self(bindings::kvrealloc); + + /// # Safety + /// + /// This method has the same safety requirements as [`Allocator::realloc`]. + /// + /// # Guarantees + /// + /// This method has the same guarantees as `Allocator::realloc`. Additionally + /// - it accepts any pointer to a valid memory allocation allocated by this function. + /// - memory allocated by this function remains valid until it is passed to this function. + unsafe fn call( + &self, + ptr: Option>, + layout: Layout, + old_layout: Layout, + flags: Flags, + ) -> Result, AllocError> { + let size = aligned_size(layout); + let ptr = match ptr { + Some(ptr) => { + if old_layout.size() == 0 { + ptr::null() + } else { + ptr.as_ptr() + } + } + None => ptr::null(), + }; // SAFETY: - // - `ptr` is either null or a pointer allocated by this allocator by the function safety - // requirement. - // - the size of `layout` is not zero because `new_size` is not zero by the function safety - // requirement. - unsafe { krealloc_aligned(ptr, layout, GFP_KERNEL) } + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that + // `ptr` is NULL or valid. + // - `ptr` is either NULL or valid by the safety requirements of this function. + // + // GUARANTEE: + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. + // - Those functions provide the guarantees of this function. + let raw_ptr = unsafe { + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. + self.0(ptr.cast(), size, flags.0).cast() + }; + + let ptr = if size == 0 { + crate::alloc::dangling_from_layout(layout) + } else { + NonNull::new(raw_ptr).ok_or(AllocError)? + }; + + Ok(NonNull::slice_from_raw_parts(ptr, size)) + } +} + +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that +// - memory remains valid until it is explicitly freed, +// - passing a pointer to a valid memory allocation is OK, +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. +unsafe impl Allocator for Kmalloc { + #[inline] + unsafe fn realloc( + ptr: Option>, + layout: Layout, + old_layout: Layout, + flags: Flags, + ) -> Result, AllocError> { + // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. + unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags) } } +} + +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that +// - memory remains valid until it is explicitly freed, +// - passing a pointer to a valid memory allocation is OK, +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. +unsafe impl Allocator for Vmalloc { + #[inline] + unsafe fn realloc( + ptr: Option>, + layout: Layout, + old_layout: Layout, + flags: Flags, + ) -> Result, AllocError> { + // TODO: Support alignments larger than PAGE_SIZE. + if layout.align() > bindings::PAGE_SIZE { + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n"); + return Err(AllocError); + } - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety - // requirement. - unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL | __GFP_ZERO) } + // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously + // allocated with this `Allocator`. + unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) } } } -#[global_allocator] -static ALLOCATOR: KernelAllocator = KernelAllocator; +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that +// - memory remains valid until it is explicitly freed, +// - passing a pointer to a valid memory allocation is OK, +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. +unsafe impl Allocator for KVmalloc { + #[inline] + unsafe fn realloc( + ptr: Option>, + layout: Layout, + old_layout: Layout, + flags: Flags, + ) -> Result, AllocError> { + // TODO: Support alignments larger than PAGE_SIZE. + if layout.align() > bindings::PAGE_SIZE { + pr_warn!("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n"); + return Err(AllocError); + } -// See . -#[no_mangle] -static __rust_no_alloc_shim_is_unstable: u8 = 0; + // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously + // allocated with this `Allocator`. + unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) } + } +} diff --git a/rust/kernel/alloc/allocator_test.rs b/rust/kernel/alloc/allocator_test.rs new file mode 100644 index 0000000000000..e3240d16040bd --- /dev/null +++ b/rust/kernel/alloc/allocator_test.rs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users +//! of those types (e.g. `CString`) use kernel allocators for instantiation. +//! +//! In order to allow userspace test cases to make use of such types as well, implement the +//! `Cmalloc` allocator within the allocator_test module and type alias all kernel allocators to +//! `Cmalloc`. The `Cmalloc` allocator uses libc's `realloc()` function as allocator backend. + +#![allow(missing_docs)] + +use super::{flags::*, AllocError, Allocator, Flags}; +use core::alloc::Layout; +use core::cmp; +use core::ptr; +use core::ptr::NonNull; + +/// The userspace allocator based on libc. +pub struct Cmalloc; + +pub type Kmalloc = Cmalloc; +pub type Vmalloc = Kmalloc; +pub type KVmalloc = Kmalloc; + +extern "C" { + #[link_name = "aligned_alloc"] + fn libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void; + + #[link_name = "free"] + fn libc_free(ptr: *mut crate::ffi::c_void); +} + +// SAFETY: +// - memory remains valid until it is explicitly freed, +// - passing a pointer to a valid memory allocation created by this `Allocator` is always OK, +// - `realloc` provides the guarantees as provided in the `# Guarantees` section. +unsafe impl Allocator for Cmalloc { + unsafe fn realloc( + ptr: Option>, + layout: Layout, + old_layout: Layout, + flags: Flags, + ) -> Result, AllocError> { + let src = match ptr { + Some(src) => { + if old_layout.size() == 0 { + ptr::null_mut() + } else { + src.as_ptr() + } + } + None => ptr::null_mut(), + }; + + if layout.size() == 0 { + // SAFETY: `src` is either NULL or was previously allocated with this `Allocator` + unsafe { libc_free(src.cast()) }; + + return Ok(NonNull::slice_from_raw_parts( + crate::alloc::dangling_from_layout(layout), + 0, + )); + } + + // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or + // exceeds the given size and alignment requirements. + let dst = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8; + let dst = NonNull::new(dst).ok_or(AllocError)?; + + if flags.contains(__GFP_ZERO) { + // SAFETY: The preceding calls to `libc_aligned_alloc` and `NonNull::new` + // guarantee that `dst` points to memory of at least `layout.size()` bytes. + unsafe { dst.as_ptr().write_bytes(0, layout.size()) }; + } + + if !src.is_null() { + // SAFETY: + // - `src` has previously been allocated with this `Allocator`; `dst` has just been + // newly allocated, hence the memory regions do not overlap. + // - both` src` and `dst` are properly aligned and valid for reads and writes + unsafe { + ptr::copy_nonoverlapping( + src, + dst.as_ptr(), + cmp::min(layout.size(), old_layout.size()), + ) + }; + } + + // SAFETY: `src` is either NULL or was previously allocated with this `Allocator` + unsafe { libc_free(src.cast()) }; + + Ok(NonNull::slice_from_raw_parts(dst, layout.size())) + } +} diff --git a/rust/kernel/alloc/box_ext.rs b/rust/kernel/alloc/box_ext.rs deleted file mode 100644 index 7009ad78d4e08..0000000000000 --- a/rust/kernel/alloc/box_ext.rs +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! Extensions to [`Box`] for fallible allocations. - -use super::{AllocError, Flags}; -use alloc::boxed::Box; -use core::{mem::MaybeUninit, ptr, result::Result}; - -/// Extensions to [`Box`]. -pub trait BoxExt: Sized { - /// Allocates a new box. - /// - /// The allocation may fail, in which case an error is returned. - fn new(x: T, flags: Flags) -> Result; - - /// Allocates a new uninitialised box. - /// - /// The allocation may fail, in which case an error is returned. - fn new_uninit(flags: Flags) -> Result>, AllocError>; - - /// Drops the contents, but keeps the allocation. - /// - /// # Examples - /// - /// ``` - /// use kernel::alloc::{flags, box_ext::BoxExt}; - /// let value = Box::new([0; 32], flags::GFP_KERNEL)?; - /// assert_eq!(*value, [0; 32]); - /// let mut value = Box::drop_contents(value); - /// // Now we can re-use `value`: - /// value.write([1; 32]); - /// // SAFETY: We just wrote to it. - /// let value = unsafe { value.assume_init() }; - /// assert_eq!(*value, [1; 32]); - /// # Ok::<(), Error>(()) - /// ``` - fn drop_contents(this: Self) -> Box>; -} - -impl BoxExt for Box { - fn new(x: T, flags: Flags) -> Result { - let mut b = >::new_uninit(flags)?; - b.write(x); - // SAFETY: We just wrote to it. - Ok(unsafe { b.assume_init() }) - } - - #[cfg(any(test, testlib))] - fn new_uninit(_flags: Flags) -> Result>, AllocError> { - Ok(Box::new_uninit()) - } - - #[cfg(not(any(test, testlib)))] - fn new_uninit(flags: Flags) -> Result>, AllocError> { - let ptr = if core::mem::size_of::>() == 0 { - core::ptr::NonNull::<_>::dangling().as_ptr() - } else { - let layout = core::alloc::Layout::new::>(); - - // SAFETY: Memory is being allocated (first arg is null). The only other source of - // safety issues is sleeping on atomic context, which is addressed by klint. Lastly, - // the type is not a SZT (checked above). - let ptr = - unsafe { super::allocator::krealloc_aligned(core::ptr::null_mut(), layout, flags) }; - if ptr.is_null() { - return Err(AllocError); - } - - ptr.cast::>() - }; - - // SAFETY: For non-zero-sized types, we allocate above using the global allocator. For - // zero-sized types, we use `NonNull::dangling`. - Ok(unsafe { Box::from_raw(ptr) }) - } - - fn drop_contents(this: Self) -> Box> { - let ptr = Box::into_raw(this); - // SAFETY: `ptr` is valid, because it came from `Box::into_raw`. - unsafe { ptr::drop_in_place(ptr) }; - - // CAST: `MaybeUninit` is a transparent wrapper of `T`. - let ptr = ptr.cast::>(); - - // SAFETY: `ptr` is valid for writes, because it came from `Box::into_raw` and it is valid for - // reads, since the pointer came from `Box::into_raw` and the type is `MaybeUninit`. - unsafe { Box::from_raw(ptr) } - } -} diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs new file mode 100644 index 0000000000000..9ce414361c2c6 --- /dev/null +++ b/rust/kernel/alloc/kbox.rs @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Box`]. + +#[allow(unused_imports)] // Used in doc comments. +use super::allocator::{KVmalloc, Kmalloc, Vmalloc}; +use super::{AllocError, Allocator, Flags}; +use core::alloc::Layout; +use core::fmt; +use core::marker::PhantomData; +use core::mem::ManuallyDrop; +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::ptr::NonNull; +use core::result::Result; + +use crate::init::{InPlaceInit, InPlaceWrite, Init, PinInit}; +use crate::types::ForeignOwnable; + +/// The kernel's [`Box`] type -- a heap allocation for a single value of type `T`. +/// +/// This is the kernel's version of the Rust stdlib's `Box`. There are several differences, +/// for example no `noalias` attribute is emitted and partially moving out of a `Box` is not +/// supported. There are also several API differences, e.g. `Box` always requires an [`Allocator`] +/// implementation to be passed as generic, page [`Flags`] when allocating memory and all functions +/// that may allocate memory are fallible. +/// +/// `Box` works with any of the kernel's allocators, e.g. [`Kmalloc`], [`Vmalloc`] or [`KVmalloc`]. +/// There are aliases for `Box` with these allocators ([`KBox`], [`VBox`], [`KVBox`]). +/// +/// When dropping a [`Box`], the value is also dropped and the heap memory is automatically freed. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KBox::::new_uninit(GFP_KERNEL | __GFP_NOWARN).is_err()); +/// ``` +/// +/// ``` +/// # use kernel::bindings; +/// const SIZE: usize = bindings::KMALLOC_MAX_SIZE as usize + 1; +/// struct Huge([u8; SIZE]); +/// +/// assert!(KVBox::::new_uninit(GFP_KERNEL).is_ok()); +/// ``` +/// +/// # Invariants +/// +/// `self.0` is always properly aligned and either points to memory allocated with `A` or, for +/// zero-sized types, is a dangling, well aligned pointer. +#[repr(transparent)] +pub struct Box(NonNull, PhantomData); + +/// Type alias for [`Box`] with a [`Kmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type KBox = Box; + +/// Type alias for [`Box`] with a [`Vmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let b = VBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type VBox = Box; + +/// Type alias for [`Box`] with a [`KVmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let b = KVBox::new(24_u64, GFP_KERNEL)?; +/// +/// assert_eq!(*b, 24_u64); +/// # Ok::<(), Error>(()) +/// ``` +pub type KVBox = Box; + +// SAFETY: `Box` is `Send` if `T` is `Send` because the `Box` owns a `T`. +unsafe impl Send for Box +where + T: Send + ?Sized, + A: Allocator, +{ +} + +// SAFETY: `Box` is `Sync` if `T` is `Sync` because the `Box` owns a `T`. +unsafe impl Sync for Box +where + T: Sync + ?Sized, + A: Allocator, +{ +} + +impl Box +where + T: ?Sized, + A: Allocator, +{ + /// Creates a new `Box` from a raw pointer. + /// + /// # Safety + /// + /// For non-ZSTs, `raw` must point at an allocation allocated with `A` that is sufficiently + /// aligned for and holds a valid `T`. The caller passes ownership of the allocation to the + /// `Box`. + /// + /// For ZSTs, `raw` must be a dangling, well aligned pointer. + #[inline] + pub const unsafe fn from_raw(raw: *mut T) -> Self { + // INVARIANT: Validity of `raw` is guaranteed by the safety preconditions of this function. + // SAFETY: By the safety preconditions of this function, `raw` is not a NULL pointer. + Self(unsafe { NonNull::new_unchecked(raw) }, PhantomData) + } + + /// Consumes the `Box` and returns a raw pointer. + /// + /// This will not run the destructor of `T` and for non-ZSTs the allocation will stay alive + /// indefinitely. Use [`Box::from_raw`] to recover the [`Box`], drop the value and free the + /// allocation, if any. + /// + /// # Examples + /// + /// ``` + /// let x = KBox::new(24, GFP_KERNEL)?; + /// let ptr = KBox::into_raw(x); + /// // SAFETY: `ptr` comes from a previous call to `KBox::into_raw`. + /// let x = unsafe { KBox::from_raw(ptr) }; + /// + /// assert_eq!(*x, 24); + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + pub fn into_raw(b: Self) -> *mut T { + ManuallyDrop::new(b).0.as_ptr() + } + + /// Consumes and leaks the `Box` and returns a mutable reference. + /// + /// See [`Box::into_raw`] for more details. + #[inline] + pub fn leak<'a>(b: Self) -> &'a mut T { + // SAFETY: `Box::into_raw` always returns a properly aligned and dereferenceable pointer + // which points to an initialized instance of `T`. + unsafe { &mut *Box::into_raw(b) } + } +} + +impl Box, A> +where + A: Allocator, +{ + /// Converts a `Box, A>` to a `Box`. + /// + /// It is undefined behavior to call this function while the value inside of `b` is not yet + /// fully initialized. + /// + /// # Safety + /// + /// Callers must ensure that the value inside of `b` is in an initialized state. + pub unsafe fn assume_init(self) -> Box { + let raw = Self::into_raw(self); + + // SAFETY: `raw` comes from a previous call to `Box::into_raw`. By the safety requirements + // of this function, the value inside the `Box` is in an initialized state. Hence, it is + // safe to reconstruct the `Box` as `Box`. + unsafe { Box::from_raw(raw.cast()) } + } + + /// Writes the value and converts to `Box`. + pub fn write(mut self, value: T) -> Box { + (*self).write(value); + + // SAFETY: We've just initialized `b`'s value. + unsafe { self.assume_init() } + } +} + +impl Box +where + A: Allocator, +{ + /// Creates a new `Box` and initializes its contents with `x`. + /// + /// New memory is allocated with `A`. The allocation may fail, in which case an error is + /// returned. For ZSTs no memory is allocated. + pub fn new(x: T, flags: Flags) -> Result { + let b = Self::new_uninit(flags)?; + Ok(Box::write(b, x)) + } + + /// Creates a new `Box` with uninitialized contents. + /// + /// New memory is allocated with `A`. The allocation may fail, in which case an error is + /// returned. For ZSTs no memory is allocated. + /// + /// # Examples + /// + /// ``` + /// let b = KBox::::new_uninit(GFP_KERNEL)?; + /// let b = KBox::write(b, 24); + /// + /// assert_eq!(*b, 24_u64); + /// # Ok::<(), Error>(()) + /// ``` + pub fn new_uninit(flags: Flags) -> Result, A>, AllocError> { + let layout = Layout::new::>(); + let ptr = A::alloc(layout, flags)?; + + // INVARIANT: `ptr` is either a dangling pointer or points to memory allocated with `A`, + // which is sufficient in size and alignment for storing a `T`. + Ok(Box(ptr.cast(), PhantomData)) + } + + /// Constructs a new `Pin>`. If `T` does not implement [`Unpin`], then `x` will be + /// pinned in memory and can't be moved. + #[inline] + pub fn pin(x: T, flags: Flags) -> Result>, AllocError> + where + A: 'static, + { + Ok(Self::new(x, flags)?.into()) + } + + /// Forgets the contents (does not run the destructor), but keeps the allocation. + fn forget_contents(this: Self) -> Box, A> { + let ptr = Self::into_raw(this); + + // SAFETY: `ptr` is valid, because it came from `Box::into_raw`. + unsafe { Box::from_raw(ptr.cast()) } + } + + /// Drops the contents, but keeps the allocation. + /// + /// # Examples + /// + /// ``` + /// let value = KBox::new([0; 32], GFP_KERNEL)?; + /// assert_eq!(*value, [0; 32]); + /// let value = KBox::drop_contents(value); + /// // Now we can re-use `value`: + /// let value = KBox::write(value, [1; 32]); + /// assert_eq!(*value, [1; 32]); + /// # Ok::<(), Error>(()) + /// ``` + pub fn drop_contents(this: Self) -> Box, A> { + let ptr = this.0.as_ptr(); + + // SAFETY: `ptr` is valid, because it came from `this`. After this call we never access the + // value stored in `this` again. + unsafe { core::ptr::drop_in_place(ptr) }; + + Self::forget_contents(this) + } + + /// Moves the `Box`'s value out of the `Box` and consumes the `Box`. + pub fn into_inner(b: Self) -> T { + // SAFETY: By the type invariant `&*b` is valid for `read`. + let value = unsafe { core::ptr::read(&*b) }; + let _ = Self::forget_contents(b); + value + } +} + +impl From> for Pin> +where + T: ?Sized, + A: Allocator, +{ + /// Converts a `Box` into a `Pin>`. If `T` does not implement [`Unpin`], then + /// `*b` will be pinned in memory and can't be moved. + /// + /// This moves `b` into `Pin` without moving `*b` or allocating and copying any memory. + fn from(b: Box) -> Self { + // SAFETY: The value wrapped inside a `Pin>` cannot be moved or replaced as long + // as `T` does not implement `Unpin`. + unsafe { Pin::new_unchecked(b) } + } +} + +impl InPlaceWrite for Box, A> +where + A: Allocator + 'static, +{ + type Initialized = Box; + + fn write_init(mut self, init: impl Init) -> Result { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid. + unsafe { init.__init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { Box::assume_init(self) }) + } + + fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid and will not be moved, because we pin it later. + unsafe { init.__pinned_init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { Box::assume_init(self) }.into()) + } +} + +impl InPlaceInit for Box +where + A: Allocator + 'static, +{ + type PinnedSelf = Pin; + + #[inline] + fn try_pin_init(init: impl PinInit, flags: Flags) -> Result, E> + where + E: From, + { + Box::<_, A>::new_uninit(flags)?.write_pin_init(init) + } + + #[inline] + fn try_init(init: impl Init, flags: Flags) -> Result + where + E: From, + { + Box::<_, A>::new_uninit(flags)?.write_init(init) + } +} + +impl ForeignOwnable for Box +where + A: Allocator, +{ + type Borrowed<'a> = &'a T; + + fn into_foreign(self) -> *const crate::ffi::c_void { + Box::into_raw(self) as _ + } + + unsafe fn from_foreign(ptr: *const crate::ffi::c_void) -> Self { + // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous + // call to `Self::into_foreign`. + unsafe { Box::from_raw(ptr as _) } + } + + unsafe fn borrow<'a>(ptr: *const crate::ffi::c_void) -> &'a T { + // SAFETY: The safety requirements of this method ensure that the object remains alive and + // immutable for the duration of 'a. + unsafe { &*ptr.cast() } + } +} + +impl ForeignOwnable for Pin> +where + A: Allocator, +{ + type Borrowed<'a> = Pin<&'a T>; + + fn into_foreign(self) -> *const crate::ffi::c_void { + // SAFETY: We are still treating the box as pinned. + Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) as _ + } + + unsafe fn from_foreign(ptr: *const crate::ffi::c_void) -> Self { + // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous + // call to `Self::into_foreign`. + unsafe { Pin::new_unchecked(Box::from_raw(ptr as _)) } + } + + unsafe fn borrow<'a>(ptr: *const crate::ffi::c_void) -> Pin<&'a T> { + // SAFETY: The safety requirements for this function ensure that the object is still alive, + // so it is safe to dereference the raw pointer. + // The safety requirements of `from_foreign` also ensure that the object remains alive for + // the lifetime of the returned value. + let r = unsafe { &*ptr.cast() }; + + // SAFETY: This pointer originates from a `Pin>`. + unsafe { Pin::new_unchecked(r) } + } +} + +impl Deref for Box +where + T: ?Sized, + A: Allocator, +{ + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized + // instance of `T`. + unsafe { self.0.as_ref() } + } +} + +impl DerefMut for Box +where + T: ?Sized, + A: Allocator, +{ + fn deref_mut(&mut self) -> &mut T { + // SAFETY: `self.0` is always properly aligned, dereferenceable and points to an initialized + // instance of `T`. + unsafe { self.0.as_mut() } + } +} + +impl fmt::Debug for Box +where + T: ?Sized + fmt::Debug, + A: Allocator, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Drop for Box +where + T: ?Sized, + A: Allocator, +{ + fn drop(&mut self) { + let layout = Layout::for_value::(self); + + // SAFETY: The pointer in `self.0` is guaranteed to be valid by the type invariant. + unsafe { core::ptr::drop_in_place::(self.deref_mut()) }; + + // SAFETY: + // - `self.0` was previously allocated with `A`. + // - `layout` is equal to the `Layout´ `self.0` was allocated with. + unsafe { A::free(self.0.cast(), layout) }; + } +} diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs new file mode 100644 index 0000000000000..ae9d072741ced --- /dev/null +++ b/rust/kernel/alloc/kvec.rs @@ -0,0 +1,913 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`Vec`]. + +use super::{ + allocator::{KVmalloc, Kmalloc, Vmalloc}, + layout::ArrayLayout, + AllocError, Allocator, Box, Flags, +}; +use core::{ + fmt, + marker::PhantomData, + mem::{ManuallyDrop, MaybeUninit}, + ops::Deref, + ops::DerefMut, + ops::Index, + ops::IndexMut, + ptr, + ptr::NonNull, + slice, + slice::SliceIndex, +}; + +/// Create a [`KVec`] containing the arguments. +/// +/// New memory is allocated with `GFP_KERNEL`. +/// +/// # Examples +/// +/// ``` +/// let mut v = kernel::kvec![]; +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(v, [1]); +/// +/// let mut v = kernel::kvec![1; 3]?; +/// v.push(4, GFP_KERNEL)?; +/// assert_eq!(v, [1, 1, 1, 4]); +/// +/// let mut v = kernel::kvec![1, 2, 3]?; +/// v.push(4, GFP_KERNEL)?; +/// assert_eq!(v, [1, 2, 3, 4]); +/// +/// # Ok::<(), Error>(()) +/// ``` +#[macro_export] +macro_rules! kvec { + () => ( + $crate::alloc::KVec::new() + ); + ($elem:expr; $n:expr) => ( + $crate::alloc::KVec::from_elem($elem, $n, GFP_KERNEL) + ); + ($($x:expr),+ $(,)?) => ( + match $crate::alloc::KBox::new_uninit(GFP_KERNEL) { + Ok(b) => Ok($crate::alloc::KVec::from($crate::alloc::KBox::write(b, [$($x),+]))), + Err(e) => Err(e), + } + ); +} + +/// The kernel's [`Vec`] type. +/// +/// A contiguous growable array type with contents allocated with the kernel's allocators (e.g. +/// [`Kmalloc`], [`Vmalloc`] or [`KVmalloc`]), written `Vec`. +/// +/// For non-zero-sized values, a [`Vec`] will use the given allocator `A` for its allocation. For +/// the most common allocators the type aliases [`KVec`], [`VVec`] and [`KVVec`] exist. +/// +/// For zero-sized types the [`Vec`]'s pointer must be `dangling_mut::`; no memory is allocated. +/// +/// Generally, [`Vec`] consists of a pointer that represents the vector's backing buffer, the +/// capacity of the vector (the number of elements that currently fit into the vector), its length +/// (the number of elements that are currently stored in the vector) and the `Allocator` type used +/// to allocate (and free) the backing buffer. +/// +/// A [`Vec`] can be deconstructed into and (re-)constructed from its previously named raw parts +/// and manually modified. +/// +/// [`Vec`]'s backing buffer gets, if required, automatically increased (re-allocated) when elements +/// are added to the vector. +/// +/// # Invariants +/// +/// - `self.ptr` is always properly aligned and either points to memory allocated with `A` or, for +/// zero-sized types, is a dangling, well aligned pointer. +/// +/// - `self.len` always represents the exact number of elements stored in the vector. +/// +/// - `self.layout` represents the absolute number of elements that can be stored within the vector +/// without re-allocation. For ZSTs `self.layout`'s capacity is zero. However, it is legal for the +/// backing buffer to be larger than `layout`. +/// +/// - The `Allocator` type `A` of the vector is the exact same `Allocator` type the backing buffer +/// was allocated with (and must be freed with). +pub struct Vec { + ptr: NonNull, + /// Represents the actual buffer size as `cap` times `size_of::` bytes. + /// + /// Note: This isn't quite the same as `Self::capacity`, which in contrast returns the number of + /// elements we can still store without reallocating. + layout: ArrayLayout, + len: usize, + _p: PhantomData, +} + +/// Type alias for [`Vec`] with a [`Kmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVec = Vec; + +/// Type alias for [`Vec`] with a [`Vmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = VVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type VVec = Vec; + +/// Type alias for [`Vec`] with a [`KVmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// let mut v = KVVec::new(); +/// v.push(1, GFP_KERNEL)?; +/// assert_eq!(&v, &[1]); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub type KVVec = Vec; + +// SAFETY: `Vec` is `Send` if `T` is `Send` because `Vec` owns its elements. +unsafe impl Send for Vec +where + T: Send, + A: Allocator, +{ +} + +// SAFETY: `Vec` is `Sync` if `T` is `Sync` because `Vec` owns its elements. +unsafe impl Sync for Vec +where + T: Sync, + A: Allocator, +{ +} + +impl Vec +where + A: Allocator, +{ + #[inline] + const fn is_zst() -> bool { + core::mem::size_of::() == 0 + } + + /// Returns the number of elements that can be stored within the vector without allocating + /// additional memory. + pub fn capacity(&self) -> usize { + if const { Self::is_zst() } { + usize::MAX + } else { + self.layout.len() + } + } + + /// Returns the number of elements stored within the vector. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Forcefully sets `self.len` to `new_len`. + /// + /// # Safety + /// + /// - `new_len` must be less than or equal to [`Self::capacity`]. + /// - If `new_len` is greater than `self.len`, all elements within the interval + /// [`self.len`,`new_len`) must be initialized. + #[inline] + pub unsafe fn set_len(&mut self, new_len: usize) { + debug_assert!(new_len <= self.capacity()); + self.len = new_len; + } + + /// Returns a slice of the entire vector. + #[inline] + pub fn as_slice(&self) -> &[T] { + self + } + + /// Returns a mutable slice of the entire vector. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [T] { + self + } + + /// Returns a mutable raw pointer to the vector's backing buffer, or, if `T` is a ZST, a + /// dangling raw pointer. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.ptr.as_ptr() + } + + /// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw + /// pointer. + #[inline] + pub fn as_ptr(&self) -> *const T { + self.ptr.as_ptr() + } + + /// Returns `true` if the vector contains no elements, `false` otherwise. + /// + /// # Examples + /// + /// ``` + /// let mut v = KVec::new(); + /// assert!(v.is_empty()); + /// + /// v.push(1, GFP_KERNEL); + /// assert!(!v.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Creates a new, empty `Vec`. + /// + /// This method does not allocate by itself. + #[inline] + pub const fn new() -> Self { + // INVARIANT: Since this is a new, empty `Vec` with no backing memory yet, + // - `ptr` is a properly aligned dangling pointer for type `T`, + // - `layout` is an empty `ArrayLayout` (zero capacity) + // - `len` is zero, since no elements can be or have been stored, + // - `A` is always valid. + Self { + ptr: NonNull::dangling(), + layout: ArrayLayout::empty(), + len: 0, + _p: PhantomData::, + } + } + + /// Returns a slice of `MaybeUninit` for the remaining spare capacity of the vector. + pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + // SAFETY: + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is + // guaranteed to be part of the same allocated object. + // - `self.len` can not overflow `isize`. + let ptr = unsafe { self.as_mut_ptr().add(self.len) } as *mut MaybeUninit; + + // SAFETY: The memory between `self.len` and `self.capacity` is guaranteed to be allocated + // and valid, but uninitialized. + unsafe { slice::from_raw_parts_mut(ptr, self.capacity() - self.len) } + } + + /// Appends an element to the back of the [`Vec`] instance. + /// + /// # Examples + /// + /// ``` + /// let mut v = KVec::new(); + /// v.push(1, GFP_KERNEL)?; + /// assert_eq!(&v, &[1]); + /// + /// v.push(2, GFP_KERNEL)?; + /// assert_eq!(&v, &[1, 2]); + /// # Ok::<(), Error>(()) + /// ``` + pub fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError> { + self.reserve(1, flags)?; + + // SAFETY: + // - `self.len` is smaller than `self.capacity` and hence, the resulting pointer is + // guaranteed to be part of the same allocated object. + // - `self.len` can not overflow `isize`. + let ptr = unsafe { self.as_mut_ptr().add(self.len) }; + + // SAFETY: + // - `ptr` is properly aligned and valid for writes. + unsafe { core::ptr::write(ptr, v) }; + + // SAFETY: We just initialised the first spare entry, so it is safe to increase the length + // by 1. We also know that the new length is <= capacity because of the previous call to + // `reserve` above. + unsafe { self.set_len(self.len() + 1) }; + Ok(()) + } + + /// Creates a new [`Vec`] instance with at least the given capacity. + /// + /// # Examples + /// + /// ``` + /// let v = KVec::::with_capacity(20, GFP_KERNEL)?; + /// + /// assert!(v.capacity() >= 20); + /// # Ok::<(), Error>(()) + /// ``` + pub fn with_capacity(capacity: usize, flags: Flags) -> Result { + let mut v = Vec::new(); + + v.reserve(capacity, flags)?; + + Ok(v) + } + + /// Creates a `Vec` from a pointer, a length and a capacity using the allocator `A`. + /// + /// # Examples + /// + /// ``` + /// let mut v = kernel::kvec![1, 2, 3]?; + /// v.reserve(1, GFP_KERNEL)?; + /// + /// let (mut ptr, mut len, cap) = v.into_raw_parts(); + /// + /// // SAFETY: We've just reserved memory for another element. + /// unsafe { ptr.add(len).write(4) }; + /// len += 1; + /// + /// // SAFETY: We only wrote an additional element at the end of the `KVec`'s buffer and + /// // correspondingly increased the length of the `KVec` by one. Otherwise, we construct it + /// // from the exact same raw parts. + /// let v = unsafe { KVec::from_raw_parts(ptr, len, cap) }; + /// + /// assert_eq!(v, [1, 2, 3, 4]); + /// + /// # Ok::<(), Error>(()) + /// ``` + /// + /// # Safety + /// + /// If `T` is a ZST: + /// + /// - `ptr` must be a dangling, well aligned pointer. + /// + /// Otherwise: + /// + /// - `ptr` must have been allocated with the allocator `A`. + /// - `ptr` must satisfy or exceed the alignment requirements of `T`. + /// - `ptr` must point to memory with a size of at least `size_of::() * capacity` bytes. + /// - The allocated size in bytes must not be larger than `isize::MAX`. + /// - `length` must be less than or equal to `capacity`. + /// - The first `length` elements must be initialized values of type `T`. + /// + /// It is also valid to create an empty `Vec` passing a dangling pointer for `ptr` and zero for + /// `cap` and `len`. + pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { + let layout = if Self::is_zst() { + ArrayLayout::empty() + } else { + // SAFETY: By the safety requirements of this function, `capacity * size_of::()` is + // smaller than `isize::MAX`. + unsafe { ArrayLayout::new_unchecked(capacity) } + }; + + // INVARIANT: For ZSTs, we store an empty `ArrayLayout`, all other type invariants are + // covered by the safety requirements of this function. + Self { + // SAFETY: By the safety requirements, `ptr` is either dangling or pointing to a valid + // memory allocation, allocated with `A`. + ptr: unsafe { NonNull::new_unchecked(ptr) }, + layout, + len: length, + _p: PhantomData::, + } + } + + /// Consumes the `Vec` and returns its raw components `pointer`, `length` and `capacity`. + /// + /// This will not run the destructor of the contained elements and for non-ZSTs the allocation + /// will stay alive indefinitely. Use [`Vec::from_raw_parts`] to recover the [`Vec`], drop the + /// elements and free the allocation, if any. + pub fn into_raw_parts(self) -> (*mut T, usize, usize) { + let mut me = ManuallyDrop::new(self); + let len = me.len(); + let capacity = me.capacity(); + let ptr = me.as_mut_ptr(); + (ptr, len, capacity) + } + + /// Ensures that the capacity exceeds the length by at least `additional` elements. + /// + /// # Examples + /// + /// ``` + /// let mut v = KVec::new(); + /// v.push(1, GFP_KERNEL)?; + /// + /// v.reserve(10, GFP_KERNEL)?; + /// let cap = v.capacity(); + /// assert!(cap >= 10); + /// + /// v.reserve(10, GFP_KERNEL)?; + /// let new_cap = v.capacity(); + /// assert_eq!(new_cap, cap); + /// + /// # Ok::<(), Error>(()) + /// ``` + pub fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError> { + let len = self.len(); + let cap = self.capacity(); + + if cap - len >= additional { + return Ok(()); + } + + if Self::is_zst() { + // The capacity is already `usize::MAX` for ZSTs, we can't go higher. + return Err(AllocError); + } + + // We know that `cap <= isize::MAX` because of the type invariants of `Self`. So the + // multiplication by two won't overflow. + let new_cap = core::cmp::max(cap * 2, len.checked_add(additional).ok_or(AllocError)?); + let layout = ArrayLayout::new(new_cap).map_err(|_| AllocError)?; + + // SAFETY: + // - `ptr` is valid because it's either `None` or comes from a previous call to + // `A::realloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + let ptr = unsafe { + A::realloc( + Some(self.ptr.cast()), + layout.into(), + self.layout.into(), + flags, + )? + }; + + // INVARIANT: + // - `layout` is some `ArrayLayout::`, + // - `ptr` has been created by `A::realloc` from `layout`. + self.ptr = ptr.cast(); + self.layout = layout; + + Ok(()) + } +} + +impl Vec { + /// Extend the vector by `n` clones of `value`. + pub fn extend_with(&mut self, n: usize, value: T, flags: Flags) -> Result<(), AllocError> { + if n == 0 { + return Ok(()); + } + + self.reserve(n, flags)?; + + let spare = self.spare_capacity_mut(); + + for item in spare.iter_mut().take(n - 1) { + item.write(value.clone()); + } + + // We can write the last element directly without cloning needlessly. + spare[n - 1].write(value); + + // SAFETY: + // - `self.len() + n < self.capacity()` due to the call to reserve above, + // - the loop and the line above initialized the next `n` elements. + unsafe { self.set_len(self.len() + n) }; + + Ok(()) + } + + /// Pushes clones of the elements of slice into the [`Vec`] instance. + /// + /// # Examples + /// + /// ``` + /// let mut v = KVec::new(); + /// v.push(1, GFP_KERNEL)?; + /// + /// v.extend_from_slice(&[20, 30, 40], GFP_KERNEL)?; + /// assert_eq!(&v, &[1, 20, 30, 40]); + /// + /// v.extend_from_slice(&[50, 60], GFP_KERNEL)?; + /// assert_eq!(&v, &[1, 20, 30, 40, 50, 60]); + /// # Ok::<(), Error>(()) + /// ``` + pub fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError> { + self.reserve(other.len(), flags)?; + for (slot, item) in core::iter::zip(self.spare_capacity_mut(), other) { + slot.write(item.clone()); + } + + // SAFETY: + // - `other.len()` spare entries have just been initialized, so it is safe to increase + // the length by the same number. + // - `self.len() + other.len() <= self.capacity()` is guaranteed by the preceding `reserve` + // call. + unsafe { self.set_len(self.len() + other.len()) }; + Ok(()) + } + + /// Create a new `Vec` and extend it by `n` clones of `value`. + pub fn from_elem(value: T, n: usize, flags: Flags) -> Result { + let mut v = Self::with_capacity(n, flags)?; + + v.extend_with(n, value, flags)?; + + Ok(v) + } +} + +impl Drop for Vec +where + A: Allocator, +{ + fn drop(&mut self) { + // SAFETY: `self.as_mut_ptr` is guaranteed to be valid by the type invariant. + unsafe { + ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut( + self.as_mut_ptr(), + self.len, + )) + }; + + // SAFETY: + // - `self.ptr` was previously allocated with `A`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + unsafe { A::free(self.ptr.cast(), self.layout.into()) }; + } +} + +impl From> for Vec +where + A: Allocator, +{ + fn from(b: Box<[T; N], A>) -> Vec { + let len = b.len(); + let ptr = Box::into_raw(b); + + // SAFETY: + // - `b` has been allocated with `A`, + // - `ptr` fulfills the alignment requirements for `T`, + // - `ptr` points to memory with at least a size of `size_of::() * len`, + // - all elements within `b` are initialized values of `T`, + // - `len` does not exceed `isize::MAX`. + unsafe { Vec::from_raw_parts(ptr as _, len, len) } + } +} + +impl Default for KVec { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl fmt::Debug for Vec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Deref for Vec +where + A: Allocator, +{ + type Target = [T]; + + #[inline] + fn deref(&self) -> &[T] { + // SAFETY: The memory behind `self.as_ptr()` is guaranteed to contain `self.len` + // initialized elements of type `T`. + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + } +} + +impl DerefMut for Vec +where + A: Allocator, +{ + #[inline] + fn deref_mut(&mut self) -> &mut [T] { + // SAFETY: The memory behind `self.as_ptr()` is guaranteed to contain `self.len` + // initialized elements of type `T`. + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + } +} + +impl Eq for Vec where A: Allocator {} + +impl, A> Index for Vec +where + A: Allocator, +{ + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(&**self, index) + } +} + +impl, A> IndexMut for Vec +where + A: Allocator, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(&mut **self, index) + } +} + +macro_rules! impl_slice_eq { + ($([$($vars:tt)*] $lhs:ty, $rhs:ty,)*) => { + $( + impl PartialEq<$rhs> for $lhs + where + T: PartialEq, + { + #[inline] + fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } + } + )* + } +} + +impl_slice_eq! { + [A1: Allocator, A2: Allocator] Vec, Vec, + [A: Allocator] Vec, &[U], + [A: Allocator] Vec, &mut [U], + [A: Allocator] &[T], Vec, + [A: Allocator] &mut [T], Vec, + [A: Allocator] Vec, [U], + [A: Allocator] [T], Vec, + [A: Allocator, const N: usize] Vec, [U; N], + [A: Allocator, const N: usize] Vec, &[U; N], +} + +impl<'a, T, A> IntoIterator for &'a Vec +where + A: Allocator, +{ + type Item = &'a T; + type IntoIter = slice::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec +where + A: Allocator, +{ + type Item = &'a mut T; + type IntoIter = slice::IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +/// An [`Iterator`] implementation for [`Vec`] that moves elements out of a vector. +/// +/// This structure is created by the [`Vec::into_iter`] method on [`Vec`] (provided by the +/// [`IntoIterator`] trait). +/// +/// # Examples +/// +/// ``` +/// let v = kernel::kvec![0, 1, 2]?; +/// let iter = v.into_iter(); +/// +/// # Ok::<(), Error>(()) +/// ``` +pub struct IntoIter { + ptr: *mut T, + buf: NonNull, + len: usize, + layout: ArrayLayout, + _p: PhantomData, +} + +impl IntoIter +where + A: Allocator, +{ + fn into_raw_parts(self) -> (*mut T, NonNull, usize, usize) { + let me = ManuallyDrop::new(self); + let ptr = me.ptr; + let buf = me.buf; + let len = me.len; + let cap = me.layout.len(); + (ptr, buf, len, cap) + } + + /// Same as `Iterator::collect` but specialized for `Vec`'s `IntoIter`. + /// + /// # Examples + /// + /// ``` + /// let v = kernel::kvec![1, 2, 3]?; + /// let mut it = v.into_iter(); + /// + /// assert_eq!(it.next(), Some(1)); + /// + /// let v = it.collect(GFP_KERNEL); + /// assert_eq!(v, [2, 3]); + /// + /// # Ok::<(), Error>(()) + /// ``` + /// + /// # Implementation details + /// + /// Currently, we can't implement `FromIterator`. There are a couple of issues with this trait + /// in the kernel, namely: + /// + /// - Rust's specialization feature is unstable. This prevents us to optimize for the special + /// case where `I::IntoIter` equals `Vec`'s `IntoIter` type. + /// - We also can't use `I::IntoIter`'s type ID either to work around this, since `FromIterator` + /// doesn't require this type to be `'static`. + /// - `FromIterator::from_iter` does return `Self` instead of `Result`, hence + /// we can't properly handle allocation failures. + /// - Neither `Iterator::collect` nor `FromIterator::from_iter` can handle additional allocation + /// flags. + /// + /// Instead, provide `IntoIter::collect`, such that we can at least convert a `IntoIter` into a + /// `Vec` again. + /// + /// Note that `IntoIter::collect` doesn't require `Flags`, since it re-uses the existing backing + /// buffer. However, this backing buffer may be shrunk to the actual count of elements. + pub fn collect(self, flags: Flags) -> Vec { + let old_layout = self.layout; + let (mut ptr, buf, len, mut cap) = self.into_raw_parts(); + let has_advanced = ptr != buf.as_ptr(); + + if has_advanced { + // Copy the contents we have advanced to at the beginning of the buffer. + // + // SAFETY: + // - `ptr` is valid for reads of `len * size_of::()` bytes, + // - `buf.as_ptr()` is valid for writes of `len * size_of::()` bytes, + // - `ptr` and `buf.as_ptr()` are not be subject to aliasing restrictions relative to + // each other, + // - both `ptr` and `buf.ptr()` are properly aligned. + unsafe { ptr::copy(ptr, buf.as_ptr(), len) }; + ptr = buf.as_ptr(); + + // SAFETY: `len` is guaranteed to be smaller than `self.layout.len()`. + let layout = unsafe { ArrayLayout::::new_unchecked(len) }; + + // SAFETY: `buf` points to the start of the backing buffer and `len` is guaranteed to be + // smaller than `cap`. Depending on `alloc` this operation may shrink the buffer or leaves + // it as it is. + ptr = match unsafe { + A::realloc(Some(buf.cast()), layout.into(), old_layout.into(), flags) + } { + // If we fail to shrink, which likely can't even happen, continue with the existing + // buffer. + Err(_) => ptr, + Ok(ptr) => { + cap = len; + ptr.as_ptr().cast() + } + }; + } + + // SAFETY: If the iterator has been advanced, the advanced elements have been copied to + // the beginning of the buffer and `len` has been adjusted accordingly. + // + // - `ptr` is guaranteed to point to the start of the backing buffer. + // - `cap` is either the original capacity or, after shrinking the buffer, equal to `len`. + // - `alloc` is guaranteed to be unchanged since `into_iter` has been called on the original + // `Vec`. + unsafe { Vec::from_raw_parts(ptr, len, cap) } + } +} + +impl Iterator for IntoIter +where + A: Allocator, +{ + type Item = T; + + /// # Examples + /// + /// ``` + /// let v = kernel::kvec![1, 2, 3]?; + /// let mut it = v.into_iter(); + /// + /// assert_eq!(it.next(), Some(1)); + /// assert_eq!(it.next(), Some(2)); + /// assert_eq!(it.next(), Some(3)); + /// assert_eq!(it.next(), None); + /// + /// # Ok::<(), Error>(()) + /// ``` + fn next(&mut self) -> Option { + if self.len == 0 { + return None; + } + + let current = self.ptr; + + // SAFETY: We can't overflow; decreasing `self.len` by one every time we advance `self.ptr` + // by one guarantees that. + unsafe { self.ptr = self.ptr.add(1) }; + + self.len -= 1; + + // SAFETY: `current` is guaranteed to point at a valid element within the buffer. + Some(unsafe { current.read() }) + } + + /// # Examples + /// + /// ``` + /// let v: KVec = kernel::kvec![1, 2, 3]?; + /// let mut iter = v.into_iter(); + /// let size = iter.size_hint().0; + /// + /// iter.next(); + /// assert_eq!(iter.size_hint().0, size - 1); + /// + /// iter.next(); + /// assert_eq!(iter.size_hint().0, size - 2); + /// + /// iter.next(); + /// assert_eq!(iter.size_hint().0, size - 3); + /// + /// # Ok::<(), Error>(()) + /// ``` + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl Drop for IntoIter +where + A: Allocator, +{ + fn drop(&mut self) { + // SAFETY: `self.ptr` is guaranteed to be valid by the type invariant. + unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr, self.len)) }; + + // SAFETY: + // - `self.buf` was previously allocated with `A`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + unsafe { A::free(self.buf.cast(), self.layout.into()) }; + } +} + +impl IntoIterator for Vec +where + A: Allocator, +{ + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the `Vec` and creates an `Iterator`, which moves each value out of the + /// vector (from start to end). + /// + /// # Examples + /// + /// ``` + /// let v = kernel::kvec![1, 2]?; + /// let mut v_iter = v.into_iter(); + /// + /// let first_element: Option = v_iter.next(); + /// + /// assert_eq!(first_element, Some(1)); + /// assert_eq!(v_iter.next(), Some(2)); + /// assert_eq!(v_iter.next(), None); + /// + /// # Ok::<(), Error>(()) + /// ``` + /// + /// ``` + /// let v = kernel::kvec![]; + /// let mut v_iter = v.into_iter(); + /// + /// let first_element: Option = v_iter.next(); + /// + /// assert_eq!(first_element, None); + /// + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + fn into_iter(self) -> Self::IntoIter { + let buf = self.ptr; + let layout = self.layout; + let (ptr, len, _) = self.into_raw_parts(); + + IntoIter { + ptr, + buf, + len, + layout, + _p: PhantomData::, + } + } +} diff --git a/rust/kernel/alloc/layout.rs b/rust/kernel/alloc/layout.rs new file mode 100644 index 0000000000000..4b3cd7fdc816c --- /dev/null +++ b/rust/kernel/alloc/layout.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Memory layout. +//! +//! Custom layout types extending or improving [`Layout`]. + +use core::{alloc::Layout, marker::PhantomData}; + +/// Error when constructing an [`ArrayLayout`]. +pub struct LayoutError; + +/// A layout for an array `[T; n]`. +/// +/// # Invariants +/// +/// - `len * size_of::() <= isize::MAX`. +pub struct ArrayLayout { + len: usize, + _phantom: PhantomData T>, +} + +impl Clone for ArrayLayout { + fn clone(&self) -> Self { + *self + } +} +impl Copy for ArrayLayout {} + +const ISIZE_MAX: usize = isize::MAX as usize; + +impl ArrayLayout { + /// Creates a new layout for `[T; 0]`. + pub const fn empty() -> Self { + // INVARIANT: `0 * size_of::() <= isize::MAX`. + Self { + len: 0, + _phantom: PhantomData, + } + } + + /// Creates a new layout for `[T; len]`. + /// + /// # Errors + /// + /// When `len * size_of::()` overflows or when `len * size_of::() > isize::MAX`. + pub const fn new(len: usize) -> Result { + match len.checked_mul(core::mem::size_of::()) { + Some(size) if size <= ISIZE_MAX => { + // INVARIANT: We checked above that `len * size_of::() <= isize::MAX`. + Ok(Self { + len, + _phantom: PhantomData, + }) + } + _ => Err(LayoutError), + } + } + + /// Creates a new layout for `[T; len]`. + /// + /// # Safety + /// + /// `len` must be a value, for which `len * size_of::() <= isize::MAX` is true. + pub unsafe fn new_unchecked(len: usize) -> Self { + // INVARIANT: By the safety requirements of this function + // `len * size_of::() <= isize::MAX`. + Self { + len, + _phantom: PhantomData, + } + } + + /// Returns the number of array elements represented by this layout. + pub const fn len(&self) -> usize { + self.len + } + + /// Returns `true` when no array elements are represented by this layout. + pub const fn is_empty(&self) -> bool { + self.len == 0 + } +} + +impl From> for Layout { + fn from(value: ArrayLayout) -> Self { + let res = Layout::array::(value.len); + // SAFETY: By the type invariant of `ArrayLayout` we have + // `len * size_of::() <= isize::MAX` and thus the result must be `Ok`. + unsafe { res.unwrap_unchecked() } + } +} diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs deleted file mode 100644 index 1297a4be32e8c..0000000000000 --- a/rust/kernel/alloc/vec_ext.rs +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! Extensions to [`Vec`] for fallible allocations. - -use super::{AllocError, Flags}; -use alloc::vec::Vec; - -/// Extensions to [`Vec`]. -pub trait VecExt: Sized { - /// Creates a new [`Vec`] instance with at least the given capacity. - /// - /// # Examples - /// - /// ``` - /// let v = Vec::::with_capacity(20, GFP_KERNEL)?; - /// - /// assert!(v.capacity() >= 20); - /// # Ok::<(), Error>(()) - /// ``` - fn with_capacity(capacity: usize, flags: Flags) -> Result; - - /// Appends an element to the back of the [`Vec`] instance. - /// - /// # Examples - /// - /// ``` - /// let mut v = Vec::new(); - /// v.push(1, GFP_KERNEL)?; - /// assert_eq!(&v, &[1]); - /// - /// v.push(2, GFP_KERNEL)?; - /// assert_eq!(&v, &[1, 2]); - /// # Ok::<(), Error>(()) - /// ``` - fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError>; - - /// Pushes clones of the elements of slice into the [`Vec`] instance. - /// - /// # Examples - /// - /// ``` - /// let mut v = Vec::new(); - /// v.push(1, GFP_KERNEL)?; - /// - /// v.extend_from_slice(&[20, 30, 40], GFP_KERNEL)?; - /// assert_eq!(&v, &[1, 20, 30, 40]); - /// - /// v.extend_from_slice(&[50, 60], GFP_KERNEL)?; - /// assert_eq!(&v, &[1, 20, 30, 40, 50, 60]); - /// # Ok::<(), Error>(()) - /// ``` - fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError> - where - T: Clone; - - /// Ensures that the capacity exceeds the length by at least `additional` elements. - /// - /// # Examples - /// - /// ``` - /// let mut v = Vec::new(); - /// v.push(1, GFP_KERNEL)?; - /// - /// v.reserve(10, GFP_KERNEL)?; - /// let cap = v.capacity(); - /// assert!(cap >= 10); - /// - /// v.reserve(10, GFP_KERNEL)?; - /// let new_cap = v.capacity(); - /// assert_eq!(new_cap, cap); - /// - /// # Ok::<(), Error>(()) - /// ``` - fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError>; -} - -impl VecExt for Vec { - fn with_capacity(capacity: usize, flags: Flags) -> Result { - let mut v = Vec::new(); - >::reserve(&mut v, capacity, flags)?; - Ok(v) - } - - fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError> { - >::reserve(self, 1, flags)?; - let s = self.spare_capacity_mut(); - s[0].write(v); - - // SAFETY: We just initialised the first spare entry, so it is safe to increase the length - // by 1. We also know that the new length is <= capacity because of the previous call to - // `reserve` above. - unsafe { self.set_len(self.len() + 1) }; - Ok(()) - } - - fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError> - where - T: Clone, - { - >::reserve(self, other.len(), flags)?; - for (slot, item) in core::iter::zip(self.spare_capacity_mut(), other) { - slot.write(item.clone()); - } - - // SAFETY: We just initialised the `other.len()` spare entries, so it is safe to increase - // the length by the same amount. We also know that the new length is <= capacity because - // of the previous call to `reserve` above. - unsafe { self.set_len(self.len() + other.len()) }; - Ok(()) - } - - #[cfg(any(test, testlib))] - fn reserve(&mut self, additional: usize, _flags: Flags) -> Result<(), AllocError> { - Vec::reserve(self, additional); - Ok(()) - } - - #[cfg(not(any(test, testlib)))] - fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError> { - let len = self.len(); - let cap = self.capacity(); - - if cap - len >= additional { - return Ok(()); - } - - if core::mem::size_of::() == 0 { - // The capacity is already `usize::MAX` for SZTs, we can't go higher. - return Err(AllocError); - } - - // We know cap is <= `isize::MAX` because `Layout::array` fails if the resulting byte size - // is greater than `isize::MAX`. So the multiplication by two won't overflow. - let new_cap = core::cmp::max(cap * 2, len.checked_add(additional).ok_or(AllocError)?); - let layout = core::alloc::Layout::array::(new_cap).map_err(|_| AllocError)?; - - let (old_ptr, len, cap) = destructure(self); - - // We need to make sure that `ptr` is either NULL or comes from a previous call to - // `krealloc_aligned`. A `Vec`'s `ptr` value is not guaranteed to be NULL and might be - // dangling after being created with `Vec::new`. Instead, we can rely on `Vec`'s capacity - // to be zero if no memory has been allocated yet. - let ptr = if cap == 0 { - core::ptr::null_mut() - } else { - old_ptr - }; - - // SAFETY: `ptr` is valid because it's either NULL or comes from a previous call to - // `krealloc_aligned`. We also verified that the type is not a ZST. - let new_ptr = unsafe { super::allocator::krealloc_aligned(ptr.cast(), layout, flags) }; - if new_ptr.is_null() { - // SAFETY: We are just rebuilding the existing `Vec` with no changes. - unsafe { rebuild(self, old_ptr, len, cap) }; - Err(AllocError) - } else { - // SAFETY: `ptr` has been reallocated with the layout for `new_cap` elements. New cap - // is greater than `cap`, so it continues to be >= `len`. - unsafe { rebuild(self, new_ptr.cast::(), len, new_cap) }; - Ok(()) - } - } -} - -#[cfg(not(any(test, testlib)))] -fn destructure(v: &mut Vec) -> (*mut T, usize, usize) { - let mut tmp = Vec::new(); - core::mem::swap(&mut tmp, v); - let mut tmp = core::mem::ManuallyDrop::new(tmp); - let len = tmp.len(); - let cap = tmp.capacity(); - (tmp.as_mut_ptr(), len, cap) -} - -/// Rebuilds a `Vec` from a pointer, length, and capacity. -/// -/// # Safety -/// -/// The same as [`Vec::from_raw_parts`]. -#[cfg(not(any(test, testlib)))] -unsafe fn rebuild(v: &mut Vec, ptr: *mut T, len: usize, cap: usize) { - // SAFETY: The safety requirements from this function satisfy those of `from_raw_parts`. - let mut tmp = unsafe { Vec::from_raw_parts(ptr, len, cap) }; - core::mem::swap(&mut tmp, v); -} diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs index 708125dce96a9..798c4ae0bdedd 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -45,7 +45,7 @@ impl GenDiskBuilder { /// Validate block size by verifying that it is between 512 and `PAGE_SIZE`, /// and that it is a power of two. - fn validate_block_size(size: u32) -> Result<()> { + fn validate_block_size(size: u32) -> Result { if !(512..=bindings::PAGE_SIZE as u32).contains(&size) || !size.is_power_of_two() { Err(error::code::EINVAL) } else { diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs index 9ba7fdfeb4b22..c8646d0d98669 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -131,7 +131,7 @@ impl OperationsVTable { unsafe extern "C" fn poll_callback( _hctx: *mut bindings::blk_mq_hw_ctx, _iob: *mut bindings::io_comp_batch, - ) -> core::ffi::c_int { + ) -> crate::ffi::c_int { T::poll().into() } @@ -145,9 +145,9 @@ impl OperationsVTable { /// for the same context. unsafe extern "C" fn init_hctx_callback( _hctx: *mut bindings::blk_mq_hw_ctx, - _tagset_data: *mut core::ffi::c_void, - _hctx_idx: core::ffi::c_uint, - ) -> core::ffi::c_int { + _tagset_data: *mut crate::ffi::c_void, + _hctx_idx: crate::ffi::c_uint, + ) -> crate::ffi::c_int { from_result(|| Ok(0)) } @@ -159,7 +159,7 @@ impl OperationsVTable { /// This function may only be called by blk-mq C infrastructure. unsafe extern "C" fn exit_hctx_callback( _hctx: *mut bindings::blk_mq_hw_ctx, - _hctx_idx: core::ffi::c_uint, + _hctx_idx: crate::ffi::c_uint, ) { } @@ -176,9 +176,9 @@ impl OperationsVTable { unsafe extern "C" fn init_request_callback( _set: *mut bindings::blk_mq_tag_set, rq: *mut bindings::request, - _hctx_idx: core::ffi::c_uint, - _numa_node: core::ffi::c_uint, - ) -> core::ffi::c_int { + _hctx_idx: crate::ffi::c_uint, + _numa_node: crate::ffi::c_uint, + ) -> crate::ffi::c_int { from_result(|| { // SAFETY: By the safety requirements of this function, `rq` points // to a valid allocation. @@ -203,7 +203,7 @@ impl OperationsVTable { unsafe extern "C" fn exit_request_callback( _set: *mut bindings::blk_mq_tag_set, rq: *mut bindings::request, - _hctx_idx: core::ffi::c_uint, + _hctx_idx: crate::ffi::c_uint, ) { // SAFETY: The tagset invariants guarantee that all requests are allocated with extra memory // for the request data. diff --git a/rust/kernel/block/mq/raw_writer.rs b/rust/kernel/block/mq/raw_writer.rs index 9222465d670bf..7e2159e4f6a6f 100644 --- a/rust/kernel/block/mq/raw_writer.rs +++ b/rust/kernel/block/mq/raw_writer.rs @@ -25,7 +25,7 @@ impl<'a> RawWriter<'a> { } pub(crate) fn from_array( - a: &'a mut [core::ffi::c_char; N], + a: &'a mut [crate::ffi::c_char; N], ) -> Result> { Self::new( // SAFETY: the buffer of `a` is valid for read and write as `u8` for diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs index a0e22827f3f4e..7943f43b95753 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -16,50 +16,55 @@ use core::{ sync::atomic::{AtomicU64, Ordering}, }; -/// A wrapper around a blk-mq `struct request`. This represents an IO request. +/// A wrapper around a blk-mq [`struct request`]. This represents an IO request. /// /// # Implementation details /// /// There are four states for a request that the Rust bindings care about: /// -/// A) Request is owned by block layer (refcount 0) -/// B) Request is owned by driver but with zero `ARef`s in existence -/// (refcount 1) -/// C) Request is owned by driver with exactly one `ARef` in existence -/// (refcount 2) -/// D) Request is owned by driver with more than one `ARef` in existence -/// (refcount > 2) +/// 1. Request is owned by block layer (refcount 0). +/// 2. Request is owned by driver but with zero [`ARef`]s in existence +/// (refcount 1). +/// 3. Request is owned by driver with exactly one [`ARef`] in existence +/// (refcount 2). +/// 4. Request is owned by driver with more than one [`ARef`] in existence +/// (refcount > 2). /// /// -/// We need to track A and B to ensure we fail tag to request conversions for +/// We need to track 1 and 2 to ensure we fail tag to request conversions for /// requests that are not owned by the driver. /// -/// We need to track C and D to ensure that it is safe to end the request and hand +/// We need to track 3 and 4 to ensure that it is safe to end the request and hand /// back ownership to the block layer. /// /// The states are tracked through the private `refcount` field of /// `RequestDataWrapper`. This structure lives in the private data area of the C -/// `struct request`. +/// [`struct request`]. /// /// # Invariants /// -/// * `self.0` is a valid `struct request` created by the C portion of the kernel. +/// * `self.0` is a valid [`struct request`] created by the C portion of the +/// kernel. /// * The private data area associated with this request must be an initialized /// and valid `RequestDataWrapper`. /// * `self` is reference counted by atomic modification of -/// self.wrapper_ref().refcount(). +/// `self.wrapper_ref().refcount()`. +/// +/// [`struct request`]: srctree/include/linux/blk-mq.h /// #[repr(transparent)] pub struct Request(Opaque, PhantomData); impl Request { - /// Create an `ARef` from a `struct request` pointer. + /// Create an [`ARef`] from a [`struct request`] pointer. /// /// # Safety /// /// * The caller must own a refcount on `ptr` that is transferred to the - /// returned `ARef`. - /// * The type invariants for `Request` must hold for the pointee of `ptr`. + /// returned [`ARef`]. + /// * The type invariants for [`Request`] must hold for the pointee of `ptr`. + /// + /// [`struct request`]: srctree/include/linux/blk-mq.h pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::request) -> ARef { // INVARIANT: By the safety requirements of this function, invariants are upheld. // SAFETY: By the safety requirement of this function, we own a @@ -84,12 +89,14 @@ impl Request { } /// Try to take exclusive ownership of `this` by dropping the refcount to 0. - /// This fails if `this` is not the only `ARef` pointing to the underlying - /// `Request`. + /// This fails if `this` is not the only [`ARef`] pointing to the underlying + /// [`Request`]. /// - /// If the operation is successful, `Ok` is returned with a pointer to the - /// C `struct request`. If the operation fails, `this` is returned in the - /// `Err` variant. + /// If the operation is successful, [`Ok`] is returned with a pointer to the + /// C [`struct request`]. If the operation fails, `this` is returned in the + /// [`Err`] variant. + /// + /// [`struct request`]: srctree/include/linux/blk-mq.h fn try_set_end(this: ARef) -> Result<*mut bindings::request, ARef> { // We can race with `TagSet::tag_to_rq` if let Err(_old) = this.wrapper_ref().refcount().compare_exchange( @@ -109,7 +116,7 @@ impl Request { /// Notify the block layer that the request has been completed without errors. /// - /// This function will return `Err` if `this` is not the only `ARef` + /// This function will return [`Err`] if `this` is not the only [`ARef`] /// referencing the request. pub fn end_ok(this: ARef) -> Result<(), ARef> { let request_ptr = Self::try_set_end(this)?; @@ -123,13 +130,13 @@ impl Request { Ok(()) } - /// Return a pointer to the `RequestDataWrapper` stored in the private area + /// Return a pointer to the [`RequestDataWrapper`] stored in the private area /// of the request structure. /// /// # Safety /// /// - `this` must point to a valid allocation of size at least size of - /// `Self` plus size of `RequestDataWrapper`. + /// [`Self`] plus size of [`RequestDataWrapper`]. pub(crate) unsafe fn wrapper_ptr(this: *mut Self) -> NonNull { let request_ptr = this.cast::(); // SAFETY: By safety requirements for this function, `this` is a @@ -141,7 +148,7 @@ impl Request { unsafe { NonNull::new_unchecked(wrapper_ptr) } } - /// Return a reference to the `RequestDataWrapper` stored in the private + /// Return a reference to the [`RequestDataWrapper`] stored in the private /// area of the request structure. pub(crate) fn wrapper_ref(&self) -> &RequestDataWrapper { // SAFETY: By type invariant, `self.0` is a valid allocation. Further, @@ -152,13 +159,15 @@ impl Request { } } -/// A wrapper around data stored in the private area of the C `struct request`. +/// A wrapper around data stored in the private area of the C [`struct request`]. +/// +/// [`struct request`]: srctree/include/linux/blk-mq.h pub(crate) struct RequestDataWrapper { /// The Rust request refcount has the following states: /// /// - 0: The request is owned by C block layer. - /// - 1: The request is owned by Rust abstractions but there are no ARef references to it. - /// - 2+: There are `ARef` references to the request. + /// - 1: The request is owned by Rust abstractions but there are no [`ARef`] references to it. + /// - 2+: There are [`ARef`] references to the request. refcount: AtomicU64, } @@ -204,7 +213,7 @@ fn atomic_relaxed_op_return(target: &AtomicU64, op: impl Fn(u64) -> u64) -> u64 } /// Store the result of `op(target.load)` in `target` if `target.load() != -/// pred`, returning true if the target was updated. +/// pred`, returning [`true`] if the target was updated. fn atomic_relaxed_op_unless(target: &AtomicU64, op: impl Fn(u64) -> u64, pred: u64) -> bool { target .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| { diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs index f9a1ca655a35b..d7f175a05d992 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -53,7 +53,7 @@ impl TagSet { queue_depth: num_tags, cmd_size, flags: bindings::BLK_MQ_F_SHOULD_MERGE, - driver_data: core::ptr::null_mut::(), + driver_data: core::ptr::null_mut::(), nr_maps: num_maps, ..tag_set } diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index c8199ee079eff..c926e0c2b8528 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -8,7 +8,10 @@ use crate::{ bindings, types::{ARef, Opaque}, }; -use core::ptr; +use core::{fmt, ptr}; + +#[cfg(CONFIG_PRINTK)] +use crate::c_str; /// A reference-counted device. /// @@ -73,6 +76,110 @@ impl Device { // SAFETY: Guaranteed by the safety requirements of the function. unsafe { &*ptr.cast() } } + + /// Prints an emergency-level message (level 0) prefixed with device information. + /// + /// More details are available from [`dev_emerg`]. + /// + /// [`dev_emerg`]: crate::dev_emerg + pub fn pr_emerg(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_EMERG, args) }; + } + + /// Prints an alert-level message (level 1) prefixed with device information. + /// + /// More details are available from [`dev_alert`]. + /// + /// [`dev_alert`]: crate::dev_alert + pub fn pr_alert(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_ALERT, args) }; + } + + /// Prints a critical-level message (level 2) prefixed with device information. + /// + /// More details are available from [`dev_crit`]. + /// + /// [`dev_crit`]: crate::dev_crit + pub fn pr_crit(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_CRIT, args) }; + } + + /// Prints an error-level message (level 3) prefixed with device information. + /// + /// More details are available from [`dev_err`]. + /// + /// [`dev_err`]: crate::dev_err + pub fn pr_err(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_ERR, args) }; + } + + /// Prints a warning-level message (level 4) prefixed with device information. + /// + /// More details are available from [`dev_warn`]. + /// + /// [`dev_warn`]: crate::dev_warn + pub fn pr_warn(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_WARNING, args) }; + } + + /// Prints a notice-level message (level 5) prefixed with device information. + /// + /// More details are available from [`dev_notice`]. + /// + /// [`dev_notice`]: crate::dev_notice + pub fn pr_notice(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_NOTICE, args) }; + } + + /// Prints an info-level message (level 6) prefixed with device information. + /// + /// More details are available from [`dev_info`]. + /// + /// [`dev_info`]: crate::dev_info + pub fn pr_info(&self, args: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_INFO, args) }; + } + + /// Prints a debug-level message (level 7) prefixed with device information. + /// + /// More details are available from [`dev_dbg`]. + /// + /// [`dev_dbg`]: crate::dev_dbg + pub fn pr_dbg(&self, args: fmt::Arguments<'_>) { + if cfg!(debug_assertions) { + // SAFETY: `klevel` is null-terminated, uses one of the kernel constants. + unsafe { self.printk(bindings::KERN_DEBUG, args) }; + } + } + + /// Prints the provided message to the console. + /// + /// # Safety + /// + /// Callers must ensure that `klevel` is null-terminated; in particular, one of the + /// `KERN_*`constants, for example, `KERN_CRIT`, `KERN_ALERT`, etc. + #[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))] + unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) { + // SAFETY: `klevel` is null-terminated and one of the kernel constants. `self.as_raw` + // is valid because `self` is valid. The "%pA" format string expects a pointer to + // `fmt::Arguments`, which is what we're passing as the last argument. + #[cfg(CONFIG_PRINTK)] + unsafe { + bindings::_dev_printk( + klevel as *const _ as *const core::ffi::c_char, + self.as_raw(), + c_str!("%pA").as_char_ptr(), + &msg as *const _ as *const core::ffi::c_void, + ) + }; + } } // SAFETY: Instances of `Device` are always reference-counted. @@ -94,3 +201,213 @@ unsafe impl Send for Device {} // SAFETY: `Device` can be shared among threads because all immutable methods are protected by the // synchronization in `struct device`. unsafe impl Sync for Device {} + +#[doc(hidden)] +#[macro_export] +macro_rules! dev_printk { + ($method:ident, $dev:expr, $($f:tt)*) => { + { + ($dev).$method(core::format_args!($($f)*)); + } + } +} + +/// Prints an emergency-level message (level 0) prefixed with device information. +/// +/// This level should be used if the system is unusable. +/// +/// Equivalent to the kernel's `dev_emerg` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_emerg!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_emerg { + ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*); } +} + +/// Prints an alert-level message (level 1) prefixed with device information. +/// +/// This level should be used if action must be taken immediately. +/// +/// Equivalent to the kernel's `dev_alert` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_alert!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_alert { + ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*); } +} + +/// Prints a critical-level message (level 2) prefixed with device information. +/// +/// This level should be used in critical conditions. +/// +/// Equivalent to the kernel's `dev_crit` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_crit!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_crit { + ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*); } +} + +/// Prints an error-level message (level 3) prefixed with device information. +/// +/// This level should be used in error conditions. +/// +/// Equivalent to the kernel's `dev_err` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_err!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_err { + ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*); } +} + +/// Prints a warning-level message (level 4) prefixed with device information. +/// +/// This level should be used in warning conditions. +/// +/// Equivalent to the kernel's `dev_warn` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_warn!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_warn { + ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*); } +} + +/// Prints a notice-level message (level 5) prefixed with device information. +/// +/// This level should be used in normal but significant conditions. +/// +/// Equivalent to the kernel's `dev_notice` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_notice!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_notice { + ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*); } +} + +/// Prints an info-level message (level 6) prefixed with device information. +/// +/// This level should be used for informational messages. +/// +/// Equivalent to the kernel's `dev_info` macro. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_info!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_info { + ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*); } +} + +/// Prints a debug-level message (level 7) prefixed with device information. +/// +/// This level should be used for debug messages. +/// +/// Equivalent to the kernel's `dev_dbg` macro, except that it doesn't support dynamic debug yet. +/// +/// Mimics the interface of [`std::print!`]. More information about the syntax is available from +/// [`core::fmt`] and `alloc::format!`. +/// +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// # use kernel::device::Device; +/// +/// fn example(dev: &Device) { +/// dev_dbg!(dev, "hello {}\n", "there"); +/// } +/// ``` +#[macro_export] +macro_rules! dev_dbg { + ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*); } +} diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 6f1587a2524e8..52c5024324474 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -6,9 +6,10 @@ use crate::{alloc::AllocError, str::CStr}; -use alloc::alloc::LayoutError; +use core::alloc::LayoutError; use core::fmt; +use core::num::NonZeroI32; use core::num::TryFromIntError; use core::str::Utf8Error; @@ -20,7 +21,11 @@ pub mod code { $( #[doc = $doc] )* - pub const $err: super::Error = super::Error(-(crate::bindings::$err as i32)); + pub const $err: super::Error = + match super::Error::try_from_errno(-(crate::bindings::$err as i32)) { + Some(err) => err, + None => panic!("Invalid errno in `declare_err!`"), + }; }; } @@ -88,14 +93,14 @@ pub mod code { /// /// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`). #[derive(Clone, Copy, PartialEq, Eq)] -pub struct Error(core::ffi::c_int); +pub struct Error(NonZeroI32); impl Error { /// Creates an [`Error`] from a kernel error code. /// /// It is a bug to pass an out-of-range `errno`. `EINVAL` would /// be returned in such a case. - pub(crate) fn from_errno(errno: core::ffi::c_int) -> Error { + pub fn from_errno(errno: crate::ffi::c_int) -> Error { if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 { // TODO: Make it a `WARN_ONCE` once available. crate::pr_warn!( @@ -107,7 +112,20 @@ impl Error { // INVARIANT: The check above ensures the type invariant // will hold. - Error(errno) + // SAFETY: `errno` is checked above to be in a valid range. + unsafe { Error::from_errno_unchecked(errno) } + } + + /// Creates an [`Error`] from a kernel error code. + /// + /// Returns [`None`] if `errno` is out-of-range. + const fn try_from_errno(errno: crate::ffi::c_int) -> Option { + if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 { + return None; + } + + // SAFETY: `errno` is checked above to be in a valid range. + Some(unsafe { Error::from_errno_unchecked(errno) }) } /// Creates an [`Error`] from a kernel error code. @@ -115,38 +133,38 @@ impl Error { /// # Safety /// /// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`). - unsafe fn from_errno_unchecked(errno: core::ffi::c_int) -> Error { + const unsafe fn from_errno_unchecked(errno: crate::ffi::c_int) -> Error { // INVARIANT: The contract ensures the type invariant // will hold. - Error(errno) + // SAFETY: The caller guarantees `errno` is non-zero. + Error(unsafe { NonZeroI32::new_unchecked(errno) }) } /// Returns the kernel error code. - pub fn to_errno(self) -> core::ffi::c_int { - self.0 + pub fn to_errno(self) -> crate::ffi::c_int { + self.0.get() } #[cfg(CONFIG_BLOCK)] pub(crate) fn to_blk_status(self) -> bindings::blk_status_t { // SAFETY: `self.0` is a valid error due to its invariant. - unsafe { bindings::errno_to_blk_status(self.0) } + unsafe { bindings::errno_to_blk_status(self.0.get()) } } /// Returns the error encoded as a pointer. - #[allow(dead_code)] - pub(crate) fn to_ptr(self) -> *mut T { + pub fn to_ptr(self) -> *mut T { #[cfg_attr(target_pointer_width = "32", allow(clippy::useless_conversion))] // SAFETY: `self.0` is a valid error due to its invariant. unsafe { - bindings::ERR_PTR(self.0.into()) as *mut _ + bindings::ERR_PTR(self.0.get().into()) as *mut _ } } /// Returns a string representing the error, if one exists. - #[cfg(not(testlib))] + #[cfg(not(any(test, testlib)))] pub fn name(&self) -> Option<&'static CStr> { // SAFETY: Just an FFI call, there are no extra safety requirements. - let ptr = unsafe { bindings::errname(-self.0) }; + let ptr = unsafe { bindings::errname(-self.0.get()) }; if ptr.is_null() { None } else { @@ -160,7 +178,7 @@ impl Error { /// When `testlib` is configured, this always returns `None` to avoid the dependency on a /// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still /// run in userspace. - #[cfg(testlib)] + #[cfg(any(test, testlib))] pub fn name(&self) -> Option<&'static CStr> { None } @@ -171,9 +189,11 @@ impl fmt::Debug for Error { match self.name() { // Print out number if no name can be found. None => f.debug_tuple("Error").field(&-self.0).finish(), - // SAFETY: These strings are ASCII-only. Some(name) => f - .debug_tuple(unsafe { core::str::from_utf8_unchecked(name) }) + .debug_tuple( + // SAFETY: These strings are ASCII-only. + unsafe { core::str::from_utf8_unchecked(name) }, + ) .finish(), } } @@ -239,7 +259,7 @@ pub type Result = core::result::Result; /// Converts an integer as returned by a C kernel function to an error if it's negative, and /// `Ok(())` otherwise. -pub fn to_result(err: core::ffi::c_int) -> Result { +pub fn to_result(err: crate::ffi::c_int) -> Result { if err < 0 { Err(Error::from_errno(err)) } else { @@ -262,21 +282,21 @@ pub fn to_result(err: core::ffi::c_int) -> Result { /// fn devm_platform_ioremap_resource( /// pdev: &mut PlatformDevice, /// index: u32, -/// ) -> Result<*mut core::ffi::c_void> { +/// ) -> Result<*mut kernel::ffi::c_void> { /// // SAFETY: `pdev` points to a valid platform device. There are no safety requirements /// // on `index`. /// from_err_ptr(unsafe { bindings::devm_platform_ioremap_resource(pdev.to_ptr(), index) }) /// } /// ``` -// TODO: Remove `dead_code` marker once an in-kernel client is available. -#[allow(dead_code)] -pub(crate) fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { - // CAST: Casting a pointer to `*const core::ffi::c_void` is always valid. - let const_ptr: *const core::ffi::c_void = ptr.cast(); +pub fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { + // CAST: Casting a pointer to `*const crate::ffi::c_void` is always valid. + let const_ptr: *const crate::ffi::c_void = ptr.cast(); // SAFETY: The FFI function does not deref the pointer. if unsafe { bindings::IS_ERR(const_ptr) } { // SAFETY: The FFI function does not deref the pointer. let err = unsafe { bindings::PTR_ERR(const_ptr) }; + + #[allow(clippy::unnecessary_cast)] // CAST: If `IS_ERR()` returns `true`, // then `PTR_ERR()` is guaranteed to return a // negative value greater-or-equal to `-bindings::MAX_ERRNO`, @@ -286,8 +306,7 @@ pub(crate) fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { // // SAFETY: `IS_ERR()` ensures `err` is a // negative value greater-or-equal to `-bindings::MAX_ERRNO`. - #[allow(clippy::unnecessary_cast)] - return Err(unsafe { Error::from_errno_unchecked(err as core::ffi::c_int) }); + return Err(unsafe { Error::from_errno_unchecked(err as crate::ffi::c_int) }); } Ok(ptr) } @@ -307,7 +326,7 @@ pub(crate) fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { /// # use kernel::bindings; /// unsafe extern "C" fn probe_callback( /// pdev: *mut bindings::platform_device, -/// ) -> core::ffi::c_int { +/// ) -> kernel::ffi::c_int { /// from_result(|| { /// let ptr = devm_alloc(pdev)?; /// bindings::platform_set_drvdata(pdev, ptr); @@ -315,9 +334,7 @@ pub(crate) fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { /// }) /// } /// ``` -// TODO: Remove `dead_code` marker once an in-kernel client is available. -#[allow(dead_code)] -pub(crate) fn from_result(f: F) -> T +pub fn from_result(f: F) -> T where T: From, F: FnOnce() -> Result, diff --git a/rust/kernel/generated_arch_static_branch_asm.rs.S b/rust/kernel/generated_arch_static_branch_asm.rs.S new file mode 100644 index 0000000000000..2afb638708db3 --- /dev/null +++ b/rust/kernel/generated_arch_static_branch_asm.rs.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include + +// Cut here. + +::kernel::concat_literals!(ARCH_STATIC_BRANCH_ASM("{symb} + {off} + {branch}", "{l_yes}")) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index a17ac8762d8f9..347049df556b1 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -13,7 +13,7 @@ //! To initialize a `struct` with an in-place constructor you will need two things: //! - an in-place constructor, //! - a memory location that can hold your `struct` (this can be the [stack], an [`Arc`], -//! [`UniqueArc`], [`Box`] or any other smart pointer that implements [`InPlaceInit`]). +//! [`UniqueArc`], [`KBox`] or any other smart pointer that implements [`InPlaceInit`]). //! //! To get an in-place constructor there are generally three options: //! - directly creating an in-place constructor using the [`pin_init!`] macro, @@ -35,7 +35,7 @@ //! that you need to write `<-` instead of `:` for fields that you want to initialize in-place. //! //! ```rust -//! # #![allow(clippy::disallowed_names)] +//! # #![expect(clippy::disallowed_names)] //! use kernel::sync::{new_mutex, Mutex}; //! # use core::pin::Pin; //! #[pin_data] @@ -55,7 +55,7 @@ //! (or just the stack) to actually initialize a `Foo`: //! //! ```rust -//! # #![allow(clippy::disallowed_names)] +//! # #![expect(clippy::disallowed_names)] //! # use kernel::sync::{new_mutex, Mutex}; //! # use core::pin::Pin; //! # #[pin_data] @@ -68,7 +68,7 @@ //! # a <- new_mutex!(42, "Foo::a"), //! # b: 24, //! # }); -//! let foo: Result>> = Box::pin_init(foo, GFP_KERNEL); +//! let foo: Result>> = KBox::pin_init(foo, GFP_KERNEL); //! ``` //! //! For more information see the [`pin_init!`] macro. @@ -87,20 +87,19 @@ //! To declare an init macro/function you just return an [`impl PinInit`]: //! //! ```rust -//! # #![allow(clippy::disallowed_names)] //! # use kernel::{sync::Mutex, new_mutex, init::PinInit, try_pin_init}; //! #[pin_data] //! struct DriverData { //! #[pin] //! status: Mutex, -//! buffer: Box<[u8; 1_000_000]>, +//! buffer: KBox<[u8; 1_000_000]>, //! } //! //! impl DriverData { //! fn new() -> impl PinInit { //! try_pin_init!(Self { //! status <- new_mutex!(0, "DriverData::status"), -//! buffer: Box::init(kernel::init::zeroed(), GFP_KERNEL)?, +//! buffer: KBox::init(kernel::init::zeroed(), GFP_KERNEL)?, //! }) //! } //! } @@ -121,11 +120,12 @@ //! `slot` gets called. //! //! ```rust -//! # #![allow(unreachable_pub, clippy::disallowed_names)] +//! # #![expect(unreachable_pub, clippy::disallowed_names)] //! use kernel::{init, types::Opaque}; //! use core::{ptr::addr_of_mut, marker::PhantomPinned, pin::Pin}; //! # mod bindings { -//! # #![allow(non_camel_case_types)] +//! # #![expect(non_camel_case_types)] +//! # #![expect(clippy::missing_safety_doc)] //! # pub struct foo; //! # pub unsafe fn init_foo(_ptr: *mut foo) {} //! # pub unsafe fn destroy_foo(_ptr: *mut foo) {} @@ -133,7 +133,7 @@ //! # } //! # // `Error::from_errno` is `pub(crate)` in the `kernel` crate, thus provide a workaround. //! # trait FromErrno { -//! # fn from_errno(errno: core::ffi::c_int) -> Error { +//! # fn from_errno(errno: kernel::ffi::c_int) -> Error { //! # // Dummy error that can be constructed outside the `kernel` crate. //! # Error::from(core::fmt::Error) //! # } @@ -211,13 +211,12 @@ //! [`pin_init!`]: crate::pin_init! use crate::{ - alloc::{box_ext::BoxExt, AllocError, Flags}, + alloc::{AllocError, Flags, KBox}, error::{self, Error}, sync::Arc, sync::UniqueArc, types::{Opaque, ScopeGuard}, }; -use alloc::boxed::Box; use core::{ cell::UnsafeCell, convert::Infallible, @@ -238,7 +237,7 @@ pub mod macros; /// # Examples /// /// ```rust -/// # #![allow(clippy::disallowed_names)] +/// # #![expect(clippy::disallowed_names)] /// # use kernel::{init, macros::pin_data, pin_init, stack_pin_init, init::*, sync::Mutex, new_mutex}; /// # use core::pin::Pin; /// #[pin_data] @@ -290,7 +289,7 @@ macro_rules! stack_pin_init { /// # Examples /// /// ```rust,ignore -/// # #![allow(clippy::disallowed_names)] +/// # #![expect(clippy::disallowed_names)] /// # use kernel::{init, pin_init, stack_try_pin_init, init::*, sync::Mutex, new_mutex}; /// # use macros::pin_data; /// # use core::{alloc::AllocError, pin::Pin}; @@ -298,7 +297,7 @@ macro_rules! stack_pin_init { /// struct Foo { /// #[pin] /// a: Mutex, -/// b: Box, +/// b: KBox, /// } /// /// struct Bar { @@ -307,7 +306,7 @@ macro_rules! stack_pin_init { /// /// stack_try_pin_init!(let foo: Result, AllocError> = pin_init!(Foo { /// a <- new_mutex!(42), -/// b: Box::new(Bar { +/// b: KBox::new(Bar { /// x: 64, /// }, GFP_KERNEL)?, /// })); @@ -316,7 +315,7 @@ macro_rules! stack_pin_init { /// ``` /// /// ```rust,ignore -/// # #![allow(clippy::disallowed_names)] +/// # #![expect(clippy::disallowed_names)] /// # use kernel::{init, pin_init, stack_try_pin_init, init::*, sync::Mutex, new_mutex}; /// # use macros::pin_data; /// # use core::{alloc::AllocError, pin::Pin}; @@ -324,7 +323,7 @@ macro_rules! stack_pin_init { /// struct Foo { /// #[pin] /// a: Mutex, -/// b: Box, +/// b: KBox, /// } /// /// struct Bar { @@ -333,7 +332,7 @@ macro_rules! stack_pin_init { /// /// stack_try_pin_init!(let foo: Pin<&mut Foo> =? pin_init!(Foo { /// a <- new_mutex!(42), -/// b: Box::new(Bar { +/// b: KBox::new(Bar { /// x: 64, /// }, GFP_KERNEL)?, /// })); @@ -368,7 +367,6 @@ macro_rules! stack_try_pin_init { /// The syntax is almost identical to that of a normal `struct` initializer: /// /// ```rust -/// # #![allow(clippy::disallowed_names)] /// # use kernel::{init, pin_init, macros::pin_data, init::*}; /// # use core::pin::Pin; /// #[pin_data] @@ -392,7 +390,7 @@ macro_rules! stack_try_pin_init { /// }, /// }); /// # initializer } -/// # Box::pin_init(demo(), GFP_KERNEL).unwrap(); +/// # KBox::pin_init(demo(), GFP_KERNEL).unwrap(); /// ``` /// /// Arbitrary Rust expressions can be used to set the value of a variable. @@ -413,7 +411,6 @@ macro_rules! stack_try_pin_init { /// To create an initializer function, simply declare it like this: /// /// ```rust -/// # #![allow(clippy::disallowed_names)] /// # use kernel::{init, pin_init, init::*}; /// # use core::pin::Pin; /// # #[pin_data] @@ -440,7 +437,7 @@ macro_rules! stack_try_pin_init { /// Users of `Foo` can now create it like this: /// /// ```rust -/// # #![allow(clippy::disallowed_names)] +/// # #![expect(clippy::disallowed_names)] /// # use kernel::{init, pin_init, macros::pin_data, init::*}; /// # use core::pin::Pin; /// # #[pin_data] @@ -462,13 +459,12 @@ macro_rules! stack_try_pin_init { /// # }) /// # } /// # } -/// let foo = Box::pin_init(Foo::new(), GFP_KERNEL); +/// let foo = KBox::pin_init(Foo::new(), GFP_KERNEL); /// ``` /// /// They can also easily embed it into their own `struct`s: /// /// ```rust -/// # #![allow(clippy::disallowed_names)] /// # use kernel::{init, pin_init, macros::pin_data, init::*}; /// # use core::pin::Pin; /// # #[pin_data] @@ -541,6 +537,7 @@ macro_rules! stack_try_pin_init { /// } /// pin_init!(&this in Buf { /// buf: [0; 64], +/// // SAFETY: TODO. /// ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() }, /// pin: PhantomPinned, /// }); @@ -590,11 +587,10 @@ macro_rules! pin_init { /// # Examples /// /// ```rust -/// # #![feature(new_uninit)] /// use kernel::{init::{self, PinInit}, error::Error}; /// #[pin_data] /// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, +/// big: KBox<[u8; 1024 * 1024 * 1024]>, /// small: [u8; 1024 * 1024], /// ptr: *mut u8, /// } @@ -602,7 +598,7 @@ macro_rules! pin_init { /// impl BigBuf { /// fn new() -> impl PinInit { /// try_pin_init!(Self { -/// big: Box::init(init::zeroed(), GFP_KERNEL)?, +/// big: KBox::init(init::zeroed(), GFP_KERNEL)?, /// small: [0; 1024 * 1024], /// ptr: core::ptr::null_mut(), /// }? Error) @@ -694,16 +690,16 @@ macro_rules! init { /// # Examples /// /// ```rust -/// use kernel::{init::{PinInit, zeroed}, error::Error}; +/// use kernel::{alloc::KBox, init::{PinInit, zeroed}, error::Error}; /// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, +/// big: KBox<[u8; 1024 * 1024 * 1024]>, /// small: [u8; 1024 * 1024], /// } /// /// impl BigBuf { /// fn new() -> impl Init { /// try_init!(Self { -/// big: Box::init(zeroed(), GFP_KERNEL)?, +/// big: KBox::init(zeroed(), GFP_KERNEL)?, /// small: [0; 1024 * 1024], /// }? Error) /// } @@ -814,8 +810,8 @@ macro_rules! assert_pinned { /// A pin-initializer for the type `T`. /// /// To use this initializer, you will need a suitable memory location that can hold a `T`. This can -/// be [`Box`], [`Arc`], [`UniqueArc`] or even the stack (see [`stack_pin_init!`]). Use the -/// [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc`] on this. +/// be [`KBox`], [`Arc`], [`UniqueArc`] or even the stack (see [`stack_pin_init!`]). Use +/// the [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc`] on this. /// /// Also see the [module description](self). /// @@ -854,7 +850,7 @@ pub unsafe trait PinInit: Sized { /// # Examples /// /// ```rust - /// # #![allow(clippy::disallowed_names)] + /// # #![expect(clippy::disallowed_names)] /// use kernel::{types::Opaque, init::pin_init_from_closure}; /// #[repr(C)] /// struct RawFoo([u8; 16]); @@ -875,6 +871,7 @@ pub unsafe trait PinInit: Sized { /// } /// /// let foo = pin_init!(Foo { + /// // SAFETY: TODO. /// raw <- unsafe { /// Opaque::ffi_init(|s| { /// init_foo(s); @@ -894,7 +891,7 @@ pub unsafe trait PinInit: Sized { } /// An initializer returned by [`PinInit::pin_chain`]. -pub struct ChainPinInit(I, F, __internal::Invariant<(E, Box)>); +pub struct ChainPinInit(I, F, __internal::Invariant<(E, KBox)>); // SAFETY: The `__pinned_init` function is implemented such that it // - returns `Ok(())` on successful initialization, @@ -920,8 +917,8 @@ where /// An initializer for `T`. /// /// To use this initializer, you will need a suitable memory location that can hold a `T`. This can -/// be [`Box`], [`Arc`], [`UniqueArc`] or even the stack (see [`stack_pin_init!`]). Use the -/// [`InPlaceInit::init`] function of a smart pointer like [`Arc`] on this. Because +/// be [`KBox`], [`Arc`], [`UniqueArc`] or even the stack (see [`stack_pin_init!`]). Use +/// the [`InPlaceInit::init`] function of a smart pointer like [`Arc`] on this. Because /// [`PinInit`] is a super trait, you can use every function that takes it as well. /// /// Also see the [module description](self). @@ -965,7 +962,7 @@ pub unsafe trait Init: PinInit { /// # Examples /// /// ```rust - /// # #![allow(clippy::disallowed_names)] + /// # #![expect(clippy::disallowed_names)] /// use kernel::{types::Opaque, init::{self, init_from_closure}}; /// struct Foo { /// buf: [u8; 1_000_000], @@ -993,7 +990,7 @@ pub unsafe trait Init: PinInit { } /// An initializer returned by [`Init::chain`]. -pub struct ChainInit(I, F, __internal::Invariant<(E, Box)>); +pub struct ChainInit(I, F, __internal::Invariant<(E, KBox)>); // SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization, @@ -1077,8 +1074,9 @@ pub fn uninit() -> impl Init, E> { /// # Examples /// /// ```rust -/// use kernel::{error::Error, init::init_array_from_fn}; -/// let array: Box<[usize; 1_000]> = Box::init::(init_array_from_fn(|i| i), GFP_KERNEL).unwrap(); +/// use kernel::{alloc::KBox, error::Error, init::init_array_from_fn}; +/// let array: KBox<[usize; 1_000]> = +/// KBox::init::(init_array_from_fn(|i| i), GFP_KERNEL).unwrap(); /// assert_eq!(array.len(), 1_000); /// ``` pub fn init_array_from_fn( @@ -1162,6 +1160,7 @@ where // SAFETY: Every type can be initialized by-value. unsafe impl Init for T { unsafe fn __init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: TODO. unsafe { slot.write(self) }; Ok(()) } @@ -1170,6 +1169,7 @@ unsafe impl Init for T { // SAFETY: Every type can be initialized by-value. `__pinned_init` calls `__init`. unsafe impl PinInit for T { unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: TODO. unsafe { self.__init(slot) } } } @@ -1243,26 +1243,6 @@ impl InPlaceInit for Arc { } } -impl InPlaceInit for Box { - type PinnedSelf = Pin; - - #[inline] - fn try_pin_init(init: impl PinInit, flags: Flags) -> Result - where - E: From, - { - as BoxExt<_>>::new_uninit(flags)?.write_pin_init(init) - } - - #[inline] - fn try_init(init: impl Init, flags: Flags) -> Result - where - E: From, - { - as BoxExt<_>>::new_uninit(flags)?.write_init(init) - } -} - impl InPlaceInit for UniqueArc { type PinnedSelf = Pin; @@ -1299,28 +1279,6 @@ pub trait InPlaceWrite { fn write_pin_init(self, init: impl PinInit) -> Result, E>; } -impl InPlaceWrite for Box> { - type Initialized = Box; - - fn write_init(mut self, init: impl Init) -> Result { - let slot = self.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid. - unsafe { init.__init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { self.assume_init() }) - } - - fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { - let slot = self.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { self.assume_init() }.into()) - } -} - impl InPlaceWrite for UniqueArc> { type Initialized = UniqueArc; @@ -1411,6 +1369,7 @@ pub fn zeroed() -> impl Init { macro_rules! impl_zeroable { ($($({$($generics:tt)*})? $t:ty, )*) => { + // SAFETY: Safety comments written in the macro invocation. $(unsafe impl$($($generics)*)? Zeroable for $t {})* }; } @@ -1451,7 +1410,7 @@ impl_zeroable! { // // In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant. {} Option>, - {} Option>, + {} Option>, // SAFETY: `null` pointer is valid. // diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs index 13cefd37512f8..74329cc3262c0 100644 --- a/rust/kernel/init/__internal.rs +++ b/rust/kernel/init/__internal.rs @@ -15,9 +15,10 @@ use super::*; /// [this table]: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns pub(super) type Invariant = PhantomData *mut T>; -/// This is the module-internal type implementing `PinInit` and `Init`. It is unsafe to create this -/// type, since the closure needs to fulfill the same safety requirement as the -/// `__pinned_init`/`__init` functions. +/// Module-internal type implementing `PinInit` and `Init`. +/// +/// It is unsafe to create this type, since the closure needs to fulfill the same safety +/// requirement as the `__pinned_init`/`__init` functions. pub(crate) struct InitClosure(pub(crate) F, pub(crate) Invariant<(E, T)>); // SAFETY: While constructing the `InitClosure`, the user promised that it upholds the @@ -53,6 +54,7 @@ where pub unsafe trait HasPinData { type PinData: PinData; + #[expect(clippy::missing_safety_doc)] unsafe fn __pin_data() -> Self::PinData; } @@ -82,6 +84,7 @@ pub unsafe trait PinData: Copy { pub unsafe trait HasInitData { type InitData: InitData; + #[expect(clippy::missing_safety_doc)] unsafe fn __init_data() -> Self::InitData; } @@ -102,7 +105,7 @@ pub unsafe trait InitData: Copy { } } -pub struct AllData(PhantomData) -> Box>); +pub struct AllData(PhantomData) -> KBox>); impl Clone for AllData { fn clone(&self) -> Self { @@ -112,10 +115,12 @@ impl Clone for AllData { impl Copy for AllData {} +// SAFETY: TODO. unsafe impl InitData for AllData { type Datee = T; } +// SAFETY: TODO. unsafe impl HasInitData for T { type InitData = AllData; diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index 9a0c4650ef676..1fd146a832416 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -182,13 +182,13 @@ //! // Normally `Drop` bounds do not have the correct semantics, but for this purpose they do //! // (normally people want to know if a type has any kind of drop glue at all, here we want //! // to know if it has any kind of custom drop glue, which is exactly what this bound does). -//! #[allow(drop_bounds)] +//! #[expect(drop_bounds)] //! impl MustNotImplDrop for T {} //! impl MustNotImplDrop for Bar {} //! // Here comes a convenience check, if one implemented `PinnedDrop`, but forgot to add it to //! // `#[pin_data]`, then this will error with the same mechanic as above, this is not needed //! // for safety, but a good sanity check, since no normal code calls `PinnedDrop::drop`. -//! #[allow(non_camel_case_types)] +//! #[expect(non_camel_case_types)] //! trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} //! impl< //! T: ::kernel::init::PinnedDrop, @@ -513,6 +513,7 @@ macro_rules! __pinned_drop { } ), ) => { + // SAFETY: TODO. unsafe $($impl_sig)* { // Inherit all attributes and the type/ident tokens for the signature. $(#[$($attr)*])* @@ -872,6 +873,7 @@ macro_rules! __pin_data { } } + // SAFETY: TODO. unsafe impl<$($impl_generics)*> $crate::init::__internal::PinData for __ThePinData<$($ty_generics)*> where $($whr)* @@ -923,14 +925,14 @@ macro_rules! __pin_data { // `Drop`. Additionally we will implement this trait for the struct leading to a conflict, // if it also implements `Drop` trait MustNotImplDrop {} - #[allow(drop_bounds)] + #[expect(drop_bounds)] impl MustNotImplDrop for T {} impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)*> where $($whr)* {} // We also take care to prevent users from writing a useless `PinnedDrop` implementation. // They might implement `PinnedDrop` correctly for the struct, but forget to give // `PinnedDrop` as the parameter to `#[pin_data]`. - #[allow(non_camel_case_types)] + #[expect(non_camel_case_types)] trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} @@ -987,6 +989,7 @@ macro_rules! __pin_data { // // The functions are `unsafe` to prevent accidentally calling them. #[allow(dead_code)] + #[expect(clippy::missing_safety_doc)] impl<$($impl_generics)*> $pin_data<$($ty_generics)*> where $($whr)* { @@ -997,6 +1000,7 @@ macro_rules! __pin_data { slot: *mut $p_type, init: impl $crate::init::PinInit<$p_type, E>, ) -> ::core::result::Result<(), E> { + // SAFETY: TODO. unsafe { $crate::init::PinInit::__pinned_init(init, slot) } } )* @@ -1007,6 +1011,7 @@ macro_rules! __pin_data { slot: *mut $type, init: impl $crate::init::Init<$type, E>, ) -> ::core::result::Result<(), E> { + // SAFETY: TODO. unsafe { $crate::init::Init::__init(init, slot) } } )* @@ -1121,6 +1126,8 @@ macro_rules! __init_internal { // no possibility of returning without `unsafe`. struct __InitOk; // Get the data about fields from the supplied type. + // + // SAFETY: TODO. let data = unsafe { use $crate::init::__internal::$has_data; // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal @@ -1176,6 +1183,7 @@ macro_rules! __init_internal { let init = move |slot| -> ::core::result::Result<(), $err> { init(slot).map(|__InitOk| ()) }; + // SAFETY: TODO. let init = unsafe { $crate::init::$construct_closure::<_, $err>(init) }; init }}; @@ -1324,6 +1332,8 @@ macro_rules! __init_internal { // Endpoint, nothing more to munch, create the initializer. // Since we are in the closure that is never called, this will never get executed. // We abuse `slot` to get the correct type inference here: + // + // SAFETY: TODO. unsafe { // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal // information that is associated to already parsed fragments, so a path fragment diff --git a/rust/kernel/ioctl.rs b/rust/kernel/ioctl.rs index cfa7d080b5319..2fc7662339e54 100644 --- a/rust/kernel/ioctl.rs +++ b/rust/kernel/ioctl.rs @@ -4,7 +4,7 @@ //! //! C header: [`include/asm-generic/ioctl.h`](srctree/include/asm-generic/ioctl.h) -#![allow(non_snake_case)] +#![expect(non_snake_case)] use crate::build_assert; diff --git a/rust/kernel/jump_label.rs b/rust/kernel/jump_label.rs new file mode 100644 index 0000000000000..4e974c768dbd5 --- /dev/null +++ b/rust/kernel/jump_label.rs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Logic for static keys. +//! +//! C header: [`include/linux/jump_label.h`](srctree/include/linux/jump_label.h). + +/// Branch based on a static key. +/// +/// Takes three arguments: +/// +/// * `key` - the path to the static variable containing the `static_key`. +/// * `keytyp` - the type of `key`. +/// * `field` - the name of the field of `key` that contains the `static_key`. +/// +/// # Safety +/// +/// The macro must be used with a real static key defined by C. +#[macro_export] +macro_rules! static_branch_unlikely { + ($key:path, $keytyp:ty, $field:ident) => {{ + let _key: *const $keytyp = ::core::ptr::addr_of!($key); + let _key: *const $crate::bindings::static_key_false = ::core::ptr::addr_of!((*_key).$field); + let _key: *const $crate::bindings::static_key = _key.cast(); + + #[cfg(not(CONFIG_JUMP_LABEL))] + { + $crate::bindings::static_key_count(_key.cast_mut()) > 0 + } + + #[cfg(CONFIG_JUMP_LABEL)] + $crate::jump_label::arch_static_branch! { $key, $keytyp, $field, false } + }}; +} +pub use static_branch_unlikely; + +/// Assert that the assembly block evaluates to a string literal. +#[cfg(CONFIG_JUMP_LABEL)] +const _: &str = include!(concat!( + env!("OBJTREE"), + "/rust/kernel/generated_arch_static_branch_asm.rs" +)); + +#[macro_export] +#[doc(hidden)] +#[cfg(CONFIG_JUMP_LABEL)] +macro_rules! arch_static_branch { + ($key:path, $keytyp:ty, $field:ident, $branch:expr) => {'my_label: { + $crate::asm!( + include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_static_branch_asm.rs")); + l_yes = label { + break 'my_label true; + }, + symb = sym $key, + off = const ::core::mem::offset_of!($keytyp, $field), + branch = const $crate::jump_label::bool_to_int($branch), + ); + + break 'my_label false; + }}; +} + +#[cfg(CONFIG_JUMP_LABEL)] +pub use arch_static_branch; + +/// A helper used by inline assembly to pass a boolean to as a `const` parameter. +/// +/// Using this function instead of a cast lets you assert that the input is a boolean, and not some +/// other type that can also be cast to an integer. +#[doc(hidden)] +pub const fn bool_to_int(b: bool) -> i32 { + b as i32 +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9843eedd42939..e1065a7551a39 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -12,10 +12,11 @@ //! do so first instead of bypassing this crate. #![no_std] +#![feature(arbitrary_self_types)] #![feature(coerce_unsized)] #![feature(dispatch_from_dyn)] -#![feature(new_uninit)] -#![feature(receiver_trait)] +#![feature(inline_const)] +#![feature(lint_reasons)] #![feature(unsize)] // Ensure conditional compilation based on the kernel configuration works; @@ -26,6 +27,8 @@ compile_error!("Missing kernel configuration for conditional compilation"); // Allow proc-macros to refer to `::kernel` inside the `kernel` crate (this crate). extern crate self as kernel; +pub use ffi; + pub mod alloc; #[cfg(CONFIG_BLOCK)] pub mod block; @@ -38,12 +41,15 @@ pub mod firmware; pub mod fs; pub mod init; pub mod ioctl; +pub mod jump_label; #[cfg(CONFIG_KUNIT)] pub mod kunit; pub mod list; +pub mod miscdevice; #[cfg(CONFIG_NET)] pub mod net; pub mod page; +pub mod pid_namespace; pub mod prelude; pub mod print; pub mod rbtree; @@ -57,6 +63,8 @@ pub mod str; pub mod sync; pub mod task; pub mod time; +pub mod tracepoint; +pub mod transmute; pub mod types; pub mod uaccess; pub mod workqueue; @@ -85,9 +93,32 @@ pub trait Module: Sized + Sync + Send { fn init(module: &'static ThisModule) -> error::Result; } +/// A module that is pinned and initialised in-place. +pub trait InPlaceModule: Sync + Send { + /// Creates an initialiser for the module. + /// + /// It is called when the module is loaded. + fn init(module: &'static ThisModule) -> impl init::PinInit; +} + +impl InPlaceModule for T { + fn init(module: &'static ThisModule) -> impl init::PinInit { + let initer = move |slot: *mut Self| { + let m = ::init(module)?; + + // SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`. + unsafe { slot.write(m) }; + Ok(()) + }; + + // SAFETY: On success, `initer` always fully initialises an instance of `Self`. + unsafe { init::pin_init_from_closure(initer) } + } +} + /// Equivalent to `THIS_MODULE` in the C API. /// -/// C header: [`include/linux/export.h`](srctree/include/linux/export.h) +/// C header: [`include/linux/init.h`](srctree/include/linux/init.h) pub struct ThisModule(*mut bindings::module); // SAFETY: `THIS_MODULE` may be used from all threads within a module. @@ -150,3 +181,38 @@ macro_rules! container_of { ptr.sub(offset) as *const $type }} } + +/// Helper for `.rs.S` files. +#[doc(hidden)] +#[macro_export] +macro_rules! concat_literals { + ($( $asm:literal )* ) => { + ::core::concat!($($asm),*) + }; +} + +/// Wrapper around `asm!` configured for use in the kernel. +/// +/// Uses a semicolon to avoid parsing ambiguities, even though this does not match native `asm!` +/// syntax. +// For x86, `asm!` uses intel syntax by default, but we want to use at&t syntax in the kernel. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[macro_export] +macro_rules! asm { + ($($asm:expr),* ; $($rest:tt)*) => { + ::core::arch::asm!( $($asm)*, options(att_syntax), $($rest)* ) + }; +} + +/// Wrapper around `asm!` configured for use in the kernel. +/// +/// Uses a semicolon to avoid parsing ambiguities, even though this does not match native `asm!` +/// syntax. +// For non-x86 arches we just pass through to `asm!`. +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +#[macro_export] +macro_rules! asm { + ($($asm:expr),* ; $($rest:tt)*) => { + ::core::arch::asm!( $($asm)*, $($rest)* ) + }; +} diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs index 5b4aec29eb675..fb93330f4af48 100644 --- a/rust/kernel/list.rs +++ b/rust/kernel/list.rs @@ -354,6 +354,7 @@ impl, const ID: u64> List { /// /// `item` must not be in a different linked list (with the same id). pub unsafe fn remove(&mut self, item: &T) -> Option> { + // SAFETY: TODO. let mut item = unsafe { ListLinks::fields(T::view_links(item)) }; // SAFETY: The user provided a reference, and reference are never dangling. // diff --git a/rust/kernel/list/arc.rs b/rust/kernel/list/arc.rs index d801b9dc6291d..3483d8c232c4f 100644 --- a/rust/kernel/list/arc.rs +++ b/rust/kernel/list/arc.rs @@ -441,9 +441,6 @@ where } } -// This is to allow [`ListArc`] (and variants) to be used as the type of `self`. -impl core::ops::Receiver for ListArc where T: ListArcSafe + ?Sized {} - // This is to allow coercion from `ListArc` to `ListArc` if `T` can be converted to the // dynamically-sized type (DST) `U`. impl core::ops::CoerceUnsized> for ListArc diff --git a/rust/kernel/list/arc_field.rs b/rust/kernel/list/arc_field.rs index 2330f673427ab..c4b9dd5039826 100644 --- a/rust/kernel/list/arc_field.rs +++ b/rust/kernel/list/arc_field.rs @@ -56,7 +56,7 @@ impl ListArcField { /// /// The caller must have mutable access to the `ListArc` containing the struct with this /// field for the duration of the returned reference. - #[allow(clippy::mut_from_ref)] + #[expect(clippy::mut_from_ref)] pub unsafe fn assert_mut(&self) -> &mut T { // SAFETY: The caller has exclusive access to the `ListArc`, so they also have exclusive // access to this field. diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs new file mode 100644 index 0000000000000..7e2a79b3ae263 --- /dev/null +++ b/rust/kernel/miscdevice.rs @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Miscdevice support. +//! +//! C headers: [`include/linux/miscdevice.h`](srctree/include/linux/miscdevice.h). +//! +//! Reference: + +use crate::{ + bindings, + error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, + prelude::*, + str::CStr, + types::{ForeignOwnable, Opaque}, +}; +use core::{ + ffi::{c_int, c_long, c_uint, c_ulong}, + marker::PhantomData, + mem::MaybeUninit, + pin::Pin, +}; + +/// Options for creating a misc device. +#[derive(Copy, Clone)] +pub struct MiscDeviceOptions { + /// The name of the miscdevice. + pub name: &'static CStr, +} + +impl MiscDeviceOptions { + /// Create a raw `struct miscdev` ready for registration. + pub const fn into_raw(self) -> bindings::miscdevice { + // SAFETY: All zeros is valid for this C type. + let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; + result.minor = bindings::MISC_DYNAMIC_MINOR as _; + result.name = self.name.as_char_ptr(); + result.fops = create_vtable::(); + result + } +} + +/// A registration of a miscdevice. +/// +/// # Invariants +/// +/// `inner` is a registered misc device. +#[repr(transparent)] +#[pin_data(PinnedDrop)] +pub struct MiscDeviceRegistration { + #[pin] + inner: Opaque, + _t: PhantomData, +} + +// SAFETY: It is allowed to call `misc_deregister` on a different thread from where you called +// `misc_register`. +unsafe impl Send for MiscDeviceRegistration {} +// SAFETY: All `&self` methods on this type are written to ensure that it is safe to call them in +// parallel. +unsafe impl Sync for MiscDeviceRegistration {} + +impl MiscDeviceRegistration { + /// Register a misc device. + pub fn register(opts: MiscDeviceOptions) -> impl PinInit { + try_pin_init!(Self { + inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| { + // SAFETY: The initializer can write to the provided `slot`. + unsafe { slot.write(opts.into_raw::()) }; + + // SAFETY: We just wrote the misc device options to the slot. The miscdevice will + // get unregistered before `slot` is deallocated because the memory is pinned and + // the destructor of this type deallocates the memory. + // INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered + // misc device. + to_result(unsafe { bindings::misc_register(slot) }) + }), + _t: PhantomData, + }) + } + + /// Returns a raw pointer to the misc device. + pub fn as_raw(&self) -> *mut bindings::miscdevice { + self.inner.get() + } +} + +#[pinned_drop] +impl PinnedDrop for MiscDeviceRegistration { + fn drop(self: Pin<&mut Self>) { + // SAFETY: We know that the device is registered by the type invariants. + unsafe { bindings::misc_deregister(self.inner.get()) }; + } +} + +/// Trait implemented by the private data of an open misc device. +#[vtable] +pub trait MiscDevice { + /// What kind of pointer should `Self` be wrapped in. + type Ptr: ForeignOwnable + Send + Sync; + + /// Called when the misc device is opened. + /// + /// The returned pointer will be stored as the private data for the file. + fn open() -> Result; + + /// Called when the misc device is released. + fn release(device: Self::Ptr) { + drop(device); + } + + /// Handler for ioctls. + /// + /// The `cmd` argument is usually manipulated using the utilties in [`kernel::ioctl`]. + /// + /// [`kernel::ioctl`]: mod@crate::ioctl + fn ioctl( + _device: ::Borrowed<'_>, + _cmd: u32, + _arg: usize, + ) -> Result { + kernel::build_error(VTABLE_DEFAULT_ERROR) + } + + /// Handler for ioctls. + /// + /// Used for 32-bit userspace on 64-bit platforms. + /// + /// This method is optional and only needs to be provided if the ioctl relies on structures + /// that have different layout on 32-bit and 64-bit userspace. If no implementation is + /// provided, then `compat_ptr_ioctl` will be used instead. + #[cfg(CONFIG_COMPAT)] + fn compat_ioctl( + _device: ::Borrowed<'_>, + _cmd: u32, + _arg: usize, + ) -> Result { + kernel::build_error(VTABLE_DEFAULT_ERROR) + } +} + +const fn create_vtable() -> &'static bindings::file_operations { + const fn maybe_fn(check: bool, func: T) -> Option { + if check { + Some(func) + } else { + None + } + } + + struct VtableHelper { + _t: PhantomData, + } + impl VtableHelper { + const VTABLE: bindings::file_operations = bindings::file_operations { + open: Some(fops_open::), + release: Some(fops_release::), + unlocked_ioctl: maybe_fn(T::HAS_IOCTL, fops_ioctl::), + #[cfg(CONFIG_COMPAT)] + compat_ioctl: if T::HAS_COMPAT_IOCTL { + Some(fops_compat_ioctl::) + } else if T::HAS_IOCTL { + Some(bindings::compat_ptr_ioctl) + } else { + None + }, + // SAFETY: All zeros is a valid value for `bindings::file_operations`. + ..unsafe { MaybeUninit::zeroed().assume_init() } + }; + } + + &VtableHelper::::VTABLE +} + +/// # Safety +/// +/// `file` and `inode` must be the file and inode for a file that is undergoing initialization. +/// The file must be associated with a `MiscDeviceRegistration`. +unsafe extern "C" fn fops_open( + inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_int { + // SAFETY: The pointers are valid and for a file being opened. + let ret = unsafe { bindings::generic_file_open(inode, file) }; + if ret != 0 { + return ret; + } + + let ptr = match T::open() { + Ok(ptr) => ptr, + Err(err) => return err.to_errno(), + }; + + // SAFETY: The open call of a file owns the private data. + unsafe { (*file).private_data = ptr.into_foreign().cast_mut() }; + + 0 +} + +/// # Safety +/// +/// `file` and `inode` must be the file and inode for a file that is being released. The file must +/// be associated with a `MiscDeviceRegistration`. +unsafe extern "C" fn fops_release( + _inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_int { + // SAFETY: The release call of a file owns the private data. + let private = unsafe { (*file).private_data }; + // SAFETY: The release call of a file owns the private data. + let ptr = unsafe { ::from_foreign(private) }; + + T::release(ptr); + + 0 +} + +/// # Safety +/// +/// `file` must be a valid file that is associated with a `MiscDeviceRegistration`. +unsafe extern "C" fn fops_ioctl( + file: *mut bindings::file, + cmd: c_uint, + arg: c_ulong, +) -> c_long { + // SAFETY: The ioctl call of a file can access the private data. + let private = unsafe { (*file).private_data }; + // SAFETY: Ioctl calls can borrow the private data of the file. + let device = unsafe { ::borrow(private) }; + + match T::ioctl(device, cmd, arg as usize) { + Ok(ret) => ret as c_long, + Err(err) => err.to_errno() as c_long, + } +} + +/// # Safety +/// +/// `file` must be a valid file that is associated with a `MiscDeviceRegistration`. +#[cfg(CONFIG_COMPAT)] +unsafe extern "C" fn fops_compat_ioctl( + file: *mut bindings::file, + cmd: c_uint, + arg: c_ulong, +) -> c_long { + // SAFETY: The compat ioctl call of a file can access the private data. + let private = unsafe { (*file).private_data }; + // SAFETY: Ioctl calls can borrow the private data of the file. + let device = unsafe { ::borrow(private) }; + + match T::compat_ioctl(device, cmd, arg as usize) { + Ok(ret) => ret as c_long, + Err(err) => err.to_errno() as c_long, + } +} diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index 801907fba1997..b89c681d97c01 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -314,7 +314,7 @@ impl Adapter { /// `phydev` must be passed by the corresponding callback in `phy_driver`. unsafe extern "C" fn soft_reset_callback( phydev: *mut bindings::phy_device, - ) -> core::ffi::c_int { + ) -> crate::ffi::c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -328,7 +328,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn probe_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int { + unsafe extern "C" fn probe_callback(phydev: *mut bindings::phy_device) -> crate::ffi::c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we can exclusively access `phy_device` because @@ -345,7 +345,7 @@ impl Adapter { /// `phydev` must be passed by the corresponding callback in `phy_driver`. unsafe extern "C" fn get_features_callback( phydev: *mut bindings::phy_device, - ) -> core::ffi::c_int { + ) -> crate::ffi::c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -359,7 +359,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn suspend_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int { + unsafe extern "C" fn suspend_callback(phydev: *mut bindings::phy_device) -> crate::ffi::c_int { from_result(|| { // SAFETY: The C core code ensures that the accessors on // `Device` are okay to call even though `phy_device->lock` @@ -373,7 +373,7 @@ impl Adapter { /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. - unsafe extern "C" fn resume_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int { + unsafe extern "C" fn resume_callback(phydev: *mut bindings::phy_device) -> crate::ffi::c_int { from_result(|| { // SAFETY: The C core code ensures that the accessors on // `Device` are okay to call even though `phy_device->lock` @@ -389,7 +389,7 @@ impl Adapter { /// `phydev` must be passed by the corresponding callback in `phy_driver`. unsafe extern "C" fn config_aneg_callback( phydev: *mut bindings::phy_device, - ) -> core::ffi::c_int { + ) -> crate::ffi::c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -405,7 +405,7 @@ impl Adapter { /// `phydev` must be passed by the corresponding callback in `phy_driver`. unsafe extern "C" fn read_status_callback( phydev: *mut bindings::phy_device, - ) -> core::ffi::c_int { + ) -> crate::ffi::c_int { from_result(|| { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on @@ -421,7 +421,7 @@ impl Adapter { /// `phydev` must be passed by the corresponding callback in `phy_driver`. unsafe extern "C" fn match_phy_device_callback( phydev: *mut bindings::phy_device, - ) -> core::ffi::c_int { + ) -> crate::ffi::c_int { // SAFETY: This callback is called only in contexts // where we hold `phy_device->lock`, so the accessors on // `Device` are okay to call. diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs index 208a006d587c1..fdac6c375fe46 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -20,6 +20,16 @@ pub const PAGE_SIZE: usize = bindings::PAGE_SIZE; /// A bitmask that gives the page containing a given address. pub const PAGE_MASK: usize = !(PAGE_SIZE - 1); +/// Round up the given number to the next multiple of [`PAGE_SIZE`]. +/// +/// It is incorrect to pass an address where the next multiple of [`PAGE_SIZE`] doesn't fit in a +/// [`usize`]. +pub const fn page_align(addr: usize) -> usize { + // Parentheses around `PAGE_SIZE - 1` to avoid triggering overflow sanitizers in the wrong + // cases. + (addr + (PAGE_SIZE - 1)) & PAGE_MASK +} + /// A pointer to a page that owns the page allocation. /// /// # Invariants diff --git a/rust/kernel/pid_namespace.rs b/rust/kernel/pid_namespace.rs new file mode 100644 index 0000000000000..0e93808e4639b --- /dev/null +++ b/rust/kernel/pid_namespace.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (c) 2024 Christian Brauner + +//! Pid namespaces. +//! +//! C header: [`include/linux/pid_namespace.h`](srctree/include/linux/pid_namespace.h) and +//! [`include/linux/pid.h`](srctree/include/linux/pid.h) + +use crate::{ + bindings, + types::{AlwaysRefCounted, Opaque}, +}; +use core::ptr; + +/// Wraps the kernel's `struct pid_namespace`. Thread safe. +/// +/// This structure represents the Rust abstraction for a C `struct pid_namespace`. This +/// implementation abstracts the usage of an already existing C `struct pid_namespace` within Rust +/// code that we get passed from the C side. +#[repr(transparent)] +pub struct PidNamespace { + inner: Opaque, +} + +impl PidNamespace { + /// Returns a raw pointer to the inner C struct. + #[inline] + pub fn as_ptr(&self) -> *mut bindings::pid_namespace { + self.inner.get() + } + + /// Creates a reference to a [`PidNamespace`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the + /// returned [`PidNamespace`] reference. + pub unsafe fn from_ptr<'a>(ptr: *const bindings::pid_namespace) -> &'a Self { + // SAFETY: The safety requirements guarantee the validity of the dereference, while the + // `PidNamespace` type being transparent makes the cast ok. + unsafe { &*ptr.cast() } + } +} + +// SAFETY: Instances of `PidNamespace` are always reference-counted. +unsafe impl AlwaysRefCounted for PidNamespace { + #[inline] + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_pid_ns(self.as_ptr()) }; + } + + #[inline] + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::put_pid_ns(obj.cast().as_ptr()) } + } +} + +// SAFETY: +// - `PidNamespace::dec_ref` can be called from any thread. +// - It is okay to send ownership of `PidNamespace` across thread boundaries. +unsafe impl Send for PidNamespace {} + +// SAFETY: It's OK to access `PidNamespace` through shared references from other threads because +// we're either accessing properties that don't change or that are properly synchronised by C code. +unsafe impl Sync for PidNamespace {} diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 4571daec0961b..9ab4e0b6cbc91 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -14,10 +14,7 @@ #[doc(no_inline)] pub use core::pin::Pin; -pub use crate::alloc::{box_ext::BoxExt, flags::*, vec_ext::VecExt}; - -#[doc(no_inline)] -pub use alloc::{boxed::Box, vec::Vec}; +pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec, Vec}; #[doc(no_inline)] pub use macros::{module, pin_data, pinned_drop, vtable, Zeroable}; @@ -27,6 +24,8 @@ pub use super::build_assert; // `super::std_vendor` is hidden, which makes the macro inline for some reason. #[doc(no_inline)] pub use super::dbg; +pub use super::fmt; +pub use super::{dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn}; pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn}; pub use super::{init, pin_init, try_init, try_pin_init}; diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index 508b0221256c9..a28077a7cb301 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -14,6 +14,7 @@ use core::{ use crate::str::RawFormatter; // Called from `vsprintf` with format specifier `%pA`. +#[expect(clippy::missing_safety_doc)] #[no_mangle] unsafe extern "C" fn rust_fmt_argument( buf: *mut c_char, @@ -23,6 +24,7 @@ unsafe extern "C" fn rust_fmt_argument( use fmt::Write; // SAFETY: The C contract guarantees that `buf` is valid if it's less than `end`. let mut w = unsafe { RawFormatter::from_ptrs(buf.cast(), end.cast()) }; + // SAFETY: TODO. let _ = w.write_fmt(unsafe { *(ptr as *const fmt::Arguments<'_>) }); w.pos().cast() } @@ -102,6 +104,7 @@ pub unsafe fn call_printk( ) { // `_printk` does not seem to fail in any path. #[cfg(CONFIG_PRINTK)] + // SAFETY: TODO. unsafe { bindings::_printk( format_string.as_ptr() as _, @@ -137,7 +140,7 @@ pub fn call_printk_cont(args: fmt::Arguments<'_>) { #[doc(hidden)] #[cfg(not(testlib))] #[macro_export] -#[allow(clippy::crate_in_macro_def)] +#[expect(clippy::crate_in_macro_def)] macro_rules! print_macro ( // The non-continuation cases (most of them, e.g. `INFO`). ($format_string:path, false, $($arg:tt)+) => ( diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs index 25eb36fd1cdce..cb4415a122582 100644 --- a/rust/kernel/rbtree.rs +++ b/rust/kernel/rbtree.rs @@ -7,7 +7,6 @@ //! Reference: use crate::{alloc::Flags, bindings, container_of, error::Result, prelude::*}; -use alloc::boxed::Box; use core::{ cmp::{Ord, Ordering}, marker::PhantomData, @@ -497,7 +496,7 @@ impl Drop for RBTree { // but it is not observable. The loop invariant is still maintained. // SAFETY: `this` is valid per the loop invariant. - unsafe { drop(Box::from_raw(this.cast_mut())) }; + unsafe { drop(KBox::from_raw(this.cast_mut())) }; } } } @@ -764,7 +763,7 @@ impl<'a, K, V> Cursor<'a, K, V> { // point to the links field of `Node` objects. let this = unsafe { container_of!(self.current.as_ptr(), Node, links) }.cast_mut(); // SAFETY: `this` is valid by the type invariants as described above. - let node = unsafe { Box::from_raw(this) }; + let node = unsafe { KBox::from_raw(this) }; let node = RBTreeNode { node }; // SAFETY: The reference to the tree used to create the cursor outlives the cursor, so // the tree cannot change. By the tree invariant, all nodes are valid. @@ -809,7 +808,7 @@ impl<'a, K, V> Cursor<'a, K, V> { // point to the links field of `Node` objects. let this = unsafe { container_of!(neighbor, Node, links) }.cast_mut(); // SAFETY: `this` is valid by the type invariants as described above. - let node = unsafe { Box::from_raw(this) }; + let node = unsafe { KBox::from_raw(this) }; return Some(RBTreeNode { node }); } None @@ -884,7 +883,8 @@ impl<'a, K, V> Cursor<'a, K, V> { NonNull::new(neighbor) } - /// SAFETY: + /// # Safety + /// /// - `node` must be a valid pointer to a node in an [`RBTree`]. /// - The caller has immutable access to `node` for the duration of 'b. unsafe fn to_key_value<'b>(node: NonNull) -> (&'b K, &'b V) { @@ -894,7 +894,8 @@ impl<'a, K, V> Cursor<'a, K, V> { (k, unsafe { &*v }) } - /// SAFETY: + /// # Safety + /// /// - `node` must be a valid pointer to a node in an [`RBTree`]. /// - The caller has mutable access to `node` for the duration of 'b. unsafe fn to_key_value_mut<'b>(node: NonNull) -> (&'b K, &'b mut V) { @@ -904,7 +905,8 @@ impl<'a, K, V> Cursor<'a, K, V> { (k, unsafe { &mut *v }) } - /// SAFETY: + /// # Safety + /// /// - `node` must be a valid pointer to a node in an [`RBTree`]. /// - The caller has immutable access to the key for the duration of 'b. unsafe fn to_key_value_raw<'b>(node: NonNull) -> (&'b K, *mut V) { @@ -1035,7 +1037,7 @@ impl Iterator for IterRaw { /// It contains the memory needed to hold a node that can be inserted into a red-black tree. One /// can be obtained by directly allocating it ([`RBTreeNodeReservation::new`]). pub struct RBTreeNodeReservation { - node: Box>>, + node: KBox>>, } impl RBTreeNodeReservation { @@ -1043,7 +1045,7 @@ impl RBTreeNodeReservation { /// call to [`RBTree::insert`]. pub fn new(flags: Flags) -> Result> { Ok(RBTreeNodeReservation { - node: as BoxExt<_>>::new_uninit(flags)?, + node: KBox::new_uninit(flags)?, }) } } @@ -1059,14 +1061,15 @@ impl RBTreeNodeReservation { /// Initialises a node reservation. /// /// It then becomes an [`RBTreeNode`] that can be inserted into a tree. - pub fn into_node(mut self, key: K, value: V) -> RBTreeNode { - self.node.write(Node { - key, - value, - links: bindings::rb_node::default(), - }); - // SAFETY: We just wrote to it. - let node = unsafe { self.node.assume_init() }; + pub fn into_node(self, key: K, value: V) -> RBTreeNode { + let node = KBox::write( + self.node, + Node { + key, + value, + links: bindings::rb_node::default(), + }, + ); RBTreeNode { node } } } @@ -1076,7 +1079,7 @@ impl RBTreeNodeReservation { /// The node is fully initialised (with key and value) and can be inserted into a tree without any /// extra allocations or failure paths. pub struct RBTreeNode { - node: Box>, + node: KBox>, } impl RBTreeNode { @@ -1088,7 +1091,9 @@ impl RBTreeNode { /// Get the key and value from inside the node. pub fn to_key_value(self) -> (K, V) { - (self.node.key, self.node.value) + let node = KBox::into_inner(self.node); + + (node.key, node.value) } } @@ -1110,7 +1115,7 @@ impl RBTreeNode { /// may be freed (but only for the key/value; memory for the node itself is kept for reuse). pub fn into_reservation(self) -> RBTreeNodeReservation { RBTreeNodeReservation { - node: Box::drop_contents(self.node), + node: KBox::drop_contents(self.node), } } } @@ -1161,7 +1166,7 @@ impl<'a, K, V> RawVacantEntry<'a, K, V> { /// The `node` must have a key such that inserting it here does not break the ordering of this /// [`RBTree`]. fn insert(self, node: RBTreeNode) -> &'a mut V { - let node = Box::into_raw(node.node); + let node = KBox::into_raw(node.node); // SAFETY: `node` is valid at least until we call `Box::from_raw`, which only happens when // the node is removed or replaced. @@ -1235,21 +1240,24 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { // SAFETY: The node was a node in the tree, but we removed it, so we can convert it // back into a box. node: unsafe { - Box::from_raw(container_of!(self.node_links, Node, links).cast_mut()) + KBox::from_raw(container_of!(self.node_links, Node, links).cast_mut()) }, } } /// Takes the value of the entry out of the map, and returns it. pub fn remove(self) -> V { - self.remove_node().node.value + let rb_node = self.remove_node(); + let node = KBox::into_inner(rb_node.node); + + node.value } /// Swap the current node for the provided node. /// /// The key of both nodes must be equal. fn replace(self, node: RBTreeNode) -> RBTreeNode { - let node = Box::into_raw(node.node); + let node = KBox::into_raw(node.node); // SAFETY: `node` is valid at least until we call `Box::from_raw`, which only happens when // the node is removed or replaced. @@ -1265,7 +1273,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { // - `self.node_ptr` produces a valid pointer to a node in the tree. // - Now that we removed this entry from the tree, we can convert the node to a box. let old_node = - unsafe { Box::from_raw(container_of!(self.node_links, Node, links).cast_mut()) }; + unsafe { KBox::from_raw(container_of!(self.node_links, Node, links).cast_mut()) }; RBTreeNode { node: old_node } } diff --git a/rust/kernel/std_vendor.rs b/rust/kernel/std_vendor.rs index 67bf9d37ddb55..279bd353687a7 100644 --- a/rust/kernel/std_vendor.rs +++ b/rust/kernel/std_vendor.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT +//! Rust standard library vendored code. +//! //! The contents of this file come from the Rust standard library, hosted in //! the repository, licensed under //! "Apache-2.0 OR MIT" and adapted for kernel use. For copyright details, @@ -14,9 +16,9 @@ /// /// ```rust /// let a = 2; -/// # #[allow(clippy::dbg_macro)] +/// # #[expect(clippy::disallowed_macros)] /// let b = dbg!(a * 2) + 1; -/// // ^-- prints: [src/main.rs:2] a * 2 = 4 +/// // ^-- prints: [src/main.rs:3:9] a * 2 = 4 /// assert_eq!(b, 5); /// ``` /// @@ -52,7 +54,7 @@ /// With a method call: /// /// ```rust -/// # #[allow(clippy::dbg_macro)] +/// # #[expect(clippy::disallowed_macros)] /// fn foo(n: usize) { /// if dbg!(n.checked_sub(4)).is_some() { /// // ... @@ -65,14 +67,13 @@ /// This prints to the kernel log: /// /// ```text,ignore -/// [src/main.rs:4] n.checked_sub(4) = None +/// [src/main.rs:3:8] n.checked_sub(4) = None /// ``` /// /// Naive factorial implementation: /// /// ```rust -/// # #[allow(clippy::dbg_macro)] -/// # { +/// # #![expect(clippy::disallowed_macros)] /// fn factorial(n: u32) -> u32 { /// if dbg!(n <= 1) { /// dbg!(1) @@ -82,21 +83,20 @@ /// } /// /// dbg!(factorial(4)); -/// # } /// ``` /// /// This prints to the kernel log: /// /// ```text,ignore -/// [src/main.rs:3] n <= 1 = false -/// [src/main.rs:3] n <= 1 = false -/// [src/main.rs:3] n <= 1 = false -/// [src/main.rs:3] n <= 1 = true -/// [src/main.rs:4] 1 = 1 -/// [src/main.rs:5] n * factorial(n - 1) = 2 -/// [src/main.rs:5] n * factorial(n - 1) = 6 -/// [src/main.rs:5] n * factorial(n - 1) = 24 -/// [src/main.rs:11] factorial(4) = 24 +/// [src/main.rs:3:8] n <= 1 = false +/// [src/main.rs:3:8] n <= 1 = false +/// [src/main.rs:3:8] n <= 1 = false +/// [src/main.rs:3:8] n <= 1 = true +/// [src/main.rs:4:9] 1 = 1 +/// [src/main.rs:5:9] n * factorial(n - 1) = 2 +/// [src/main.rs:5:9] n * factorial(n - 1) = 6 +/// [src/main.rs:5:9] n * factorial(n - 1) = 24 +/// [src/main.rs:11:1] factorial(4) = 24 /// ``` /// /// The `dbg!(..)` macro moves the input: @@ -118,7 +118,7 @@ /// a tuple (and return it, too): /// /// ``` -/// # #[allow(clippy::dbg_macro)] +/// # #![expect(clippy::disallowed_macros)] /// assert_eq!(dbg!(1usize, 2u32), (1, 2)); /// ``` /// @@ -127,11 +127,9 @@ /// invocations. You can use a 1-tuple directly if you need one: /// /// ``` -/// # #[allow(clippy::dbg_macro)] -/// # { +/// # #![expect(clippy::disallowed_macros)] /// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored /// assert_eq!((1,), dbg!((1u32,))); // 1-tuple -/// # } /// ``` /// /// [`std::dbg`]: https://doc.rust-lang.org/std/macro.dbg.html diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index bb8d4f41475b5..d04c12a1426d1 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -2,8 +2,7 @@ //! String representations. -use crate::alloc::{flags::*, vec_ext::VecExt, AllocError}; -use alloc::vec::Vec; +use crate::alloc::{flags::*, AllocError, KVec}; use core::fmt::{self, Write}; use core::ops::{self, Deref, DerefMut, Index}; @@ -162,10 +161,10 @@ impl CStr { /// Returns the length of this string with `NUL`. #[inline] pub const fn len_with_nul(&self) -> usize { - // SAFETY: This is one of the invariant of `CStr`. - // We add a `unreachable_unchecked` here to hint the optimizer that - // the value returned from this function is non-zero. if self.0.is_empty() { + // SAFETY: This is one of the invariant of `CStr`. + // We add a `unreachable_unchecked` here to hint the optimizer that + // the value returned from this function is non-zero. unsafe { core::hint::unreachable_unchecked() }; } self.0.len() @@ -185,7 +184,7 @@ impl CStr { /// last at least `'a`. When `CStr` is alive, the memory pointed by `ptr` /// must not be mutated. #[inline] - pub unsafe fn from_char_ptr<'a>(ptr: *const core::ffi::c_char) -> &'a Self { + pub unsafe fn from_char_ptr<'a>(ptr: *const crate::ffi::c_char) -> &'a Self { // SAFETY: The safety precondition guarantees `ptr` is a valid pointer // to a `NUL`-terminated C string. let len = unsafe { bindings::strlen(ptr) } + 1; @@ -248,7 +247,7 @@ impl CStr { /// Returns a C pointer to the string. #[inline] - pub const fn as_char_ptr(&self) -> *const core::ffi::c_char { + pub const fn as_char_ptr(&self) -> *const crate::ffi::c_char { self.0.as_ptr() as _ } @@ -301,6 +300,7 @@ impl CStr { /// ``` #[inline] pub unsafe fn as_str_unchecked(&self) -> &str { + // SAFETY: TODO. unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } } @@ -524,7 +524,28 @@ macro_rules! c_str { #[cfg(test)] mod tests { use super::*; - use alloc::format; + + struct String(CString); + + impl String { + fn from_fmt(args: fmt::Arguments<'_>) -> Self { + String(CString::try_from_fmt(args).unwrap()) + } + } + + impl Deref for String { + type Target = str; + + fn deref(&self) -> &str { + self.0.to_str().unwrap() + } + } + + macro_rules! format { + ($($f:tt)*) => ({ + &*String::from_fmt(kernel::fmt!($($f)*)) + }) + } const ALL_ASCII_CHARS: &'static str = "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\ @@ -790,7 +811,7 @@ impl fmt::Write for Formatter { /// assert_eq!(s.is_ok(), false); /// ``` pub struct CString { - buf: Vec, + buf: KVec, } impl CString { @@ -803,7 +824,7 @@ impl CString { let size = f.bytes_written(); // Allocate a vector with the required number of bytes, and write to it. - let mut buf = as VecExt<_>>::with_capacity(size, GFP_KERNEL)?; + let mut buf = KVec::with_capacity(size, GFP_KERNEL)?; // SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes. let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) }; f.write_fmt(args)?; @@ -850,10 +871,9 @@ impl<'a> TryFrom<&'a CStr> for CString { type Error = AllocError; fn try_from(cstr: &'a CStr) -> Result { - let mut buf = Vec::new(); + let mut buf = KVec::new(); - as VecExt<_>>::extend_from_slice(&mut buf, cstr.as_bytes_with_nul(), GFP_KERNEL) - .map_err(|_| AllocError)?; + buf.extend_from_slice(cstr.as_bytes_with_nul(), GFP_KERNEL)?; // INVARIANT: The `CStr` and `CString` types have the same invariants for // the string data, and we copied it over without changes. diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index bae4a5179c721..1eab7ebf25fd3 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -15,6 +15,7 @@ pub mod poll; pub use arc::{Arc, ArcBorrow, UniqueArc}; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; +pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy}; pub use lock::mutex::{new_mutex, Mutex}; pub use lock::spinlock::{new_spinlock, SpinLock}; pub use locked_by::LockedBy; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 3021f30fd822f..fa4509406ee90 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -17,13 +17,12 @@ //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html use crate::{ - alloc::{box_ext::BoxExt, AllocError, Flags}, + alloc::{AllocError, Flags, KBox}, bindings, init::{self, InPlaceInit, Init, PinInit}, try_init, types::{ForeignOwnable, Opaque}, }; -use alloc::boxed::Box; use core::{ alloc::Layout, fmt, @@ -171,9 +170,6 @@ impl ArcInner { } } -// This is to allow [`Arc`] (and variants) to be used as the type of `self`. -impl core::ops::Receiver for Arc {} - // This is to allow coercion from `Arc` to `Arc` if `T` can be converted to the // dynamically-sized type (DST) `U`. impl, U: ?Sized> core::ops::CoerceUnsized> for Arc {} @@ -204,11 +200,11 @@ impl Arc { data: contents, }; - let inner = as BoxExt<_>>::new(value, flags)?; + let inner = KBox::new(value, flags)?; // SAFETY: We just created `inner` with a reference count of 1, which is owned by the new // `Arc` object. - Ok(unsafe { Self::from_inner(Box::leak(inner).into()) }) + Ok(unsafe { Self::from_inner(KBox::leak(inner).into()) }) } } @@ -336,12 +332,12 @@ impl Arc { impl ForeignOwnable for Arc { type Borrowed<'a> = ArcBorrow<'a, T>; - fn into_foreign(self) -> *const core::ffi::c_void { + fn into_foreign(self) -> *const crate::ffi::c_void { ManuallyDrop::new(self).ptr.as_ptr() as _ } - unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> ArcBorrow<'a, T> { - // SAFETY: By the safety requirement of this function, we know that `ptr` came from + unsafe fn borrow<'a>(ptr: *const crate::ffi::c_void) -> ArcBorrow<'a, T> { + // By the safety requirement of this function, we know that `ptr` came from // a previous call to `Arc::into_foreign`. let inner = NonNull::new(ptr as *mut ArcInner).unwrap(); @@ -350,7 +346,7 @@ impl ForeignOwnable for Arc { unsafe { ArcBorrow::new(inner) } } - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { + unsafe fn from_foreign(ptr: *const crate::ffi::c_void) -> Self { // SAFETY: By the safety requirement of this function, we know that `ptr` came from // a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and // holds a reference count increment that is transferrable to us. @@ -401,8 +397,8 @@ impl Drop for Arc { if is_zero { // The count reached zero, we must free the memory. // - // SAFETY: The pointer was initialised from the result of `Box::leak`. - unsafe { drop(Box::from_raw(self.ptr.as_ptr())) }; + // SAFETY: The pointer was initialised from the result of `KBox::leak`. + unsafe { drop(KBox::from_raw(self.ptr.as_ptr())) }; } } } @@ -480,9 +476,6 @@ pub struct ArcBorrow<'a, T: ?Sized + 'a> { _p: PhantomData<&'a ()>, } -// This is to allow [`ArcBorrow`] (and variants) to be used as the type of `self`. -impl core::ops::Receiver for ArcBorrow<'_, T> {} - // This is to allow `ArcBorrow` to be dispatched on when `ArcBorrow` can be coerced into // `ArcBorrow`. impl, U: ?Sized> core::ops::DispatchFromDyn> @@ -647,7 +640,7 @@ impl UniqueArc { /// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet. pub fn new_uninit(flags: Flags) -> Result>, AllocError> { // INVARIANT: The refcount is initialised to a non-zero value. - let inner = Box::try_init::( + let inner = KBox::try_init::( try_init!(ArcInner { // SAFETY: There are no safety requirements for this FFI call. refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }), @@ -657,8 +650,8 @@ impl UniqueArc { )?; Ok(UniqueArc { // INVARIANT: The newly-created object has a refcount of 1. - // SAFETY: The pointer from the `Box` is valid. - inner: unsafe { Arc::from_inner(Box::leak(inner).into()) }, + // SAFETY: The pointer from the `KBox` is valid. + inner: unsafe { Arc::from_inner(KBox::leak(inner).into()) }, }) } } diff --git a/rust/kernel/sync/arc/std_vendor.rs b/rust/kernel/sync/arc/std_vendor.rs index a66a0c2831b3e..11b3f4ecca5f7 100644 --- a/rust/kernel/sync/arc/std_vendor.rs +++ b/rust/kernel/sync/arc/std_vendor.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT +//! Rust standard library vendored code. +//! //! The contents of this file come from the Rust standard library, hosted in //! the repository, licensed under //! "Apache-2.0 OR MIT" and adapted for kernel use. For copyright details, diff --git a/rust/kernel/sync/condvar.rs b/rust/kernel/sync/condvar.rs index 2b306afbe56d9..7df565038d7d0 100644 --- a/rust/kernel/sync/condvar.rs +++ b/rust/kernel/sync/condvar.rs @@ -7,6 +7,7 @@ use super::{lock::Backend, lock::Guard, LockClassKey}; use crate::{ + ffi::{c_int, c_long}, init::PinInit, pin_init, str::CStr, @@ -14,7 +15,6 @@ use crate::{ time::Jiffies, types::Opaque, }; -use core::ffi::{c_int, c_long}; use core::marker::PhantomPinned; use core::ptr; use macros::pin_data; @@ -70,8 +70,8 @@ pub use new_condvar; /// } /// /// /// Allocates a new boxed `Example`. -/// fn new_example() -> Result>> { -/// Box::pin_init(pin_init!(Example { +/// fn new_example() -> Result>> { +/// KBox::pin_init(pin_init!(Example { /// value <- new_mutex!(0), /// value_changed <- new_condvar!(), /// }), GFP_KERNEL) @@ -93,7 +93,6 @@ pub struct CondVar { } // SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread. -#[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for CondVar {} // SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on multiple threads diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index d6e9bab114b87..41dcddac69e20 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -18,6 +18,9 @@ use macros::pin_data; pub mod mutex; pub mod spinlock; +pub(super) mod global; +pub use global::{GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy}; + /// The "backend" of a lock. /// /// It is the actual implementation of the lock, without the need to repeat patterns used in all @@ -51,7 +54,7 @@ pub unsafe trait Backend { /// remain valid for read indefinitely. unsafe fn init( ptr: *mut Self::State, - name: *const core::ffi::c_char, + name: *const crate::ffi::c_char, key: *mut bindings::lock_class_key, ); @@ -63,6 +66,13 @@ pub unsafe trait Backend { #[must_use] unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState; + /// Tries to acquire the lock. + /// + /// # Safety + /// + /// Callers must ensure that [`Backend::init`] has been previously called. + unsafe fn try_lock(ptr: *mut Self::State) -> Option; + /// Releases the lock, giving up its ownership. /// /// # Safety @@ -133,6 +143,15 @@ impl Lock { // SAFETY: The lock was just acquired. unsafe { Guard::new(self, state) } } + + /// Tries to acquire the lock. + /// + /// Returns a guard that can be used to access the data protected by the lock if successful. + pub fn try_lock(&self) -> Option> { + // SAFETY: The constructor of the type calls `init`, so the existence of the object proves + // that `init` was called. + unsafe { B::try_lock(self.state.get()).map(|state| Guard::new(self, state)) } + } } /// A lock guard. @@ -155,9 +174,9 @@ impl Guard<'_, T, B> { // SAFETY: The caller owns the lock, so it is safe to unlock it. unsafe { B::unlock(self.lock.state.get(), &self.state) }; - // SAFETY: The lock was just unlocked above and is being relocked now. - let _relock = - ScopeGuard::new(|| unsafe { B::relock(self.lock.state.get(), &mut self.state) }); + let _relock = ScopeGuard::new(|| + // SAFETY: The lock was just unlocked above and is being relocked now. + unsafe { B::relock(self.lock.state.get(), &mut self.state) }); cb() } diff --git a/rust/kernel/sync/lock/global.rs b/rust/kernel/sync/lock/global.rs new file mode 100644 index 0000000000000..480ee724e3cc4 --- /dev/null +++ b/rust/kernel/sync/lock/global.rs @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Support for defining statics containing locks. + +use crate::{ + str::CStr, + sync::lock::{Backend, Guard, Lock}, + sync::{LockClassKey, LockedBy}, + types::Opaque, +}; +use core::{ + cell::UnsafeCell, + marker::{PhantomData, PhantomPinned}, +}; + +/// Trait implemented for marker types for global locks. +/// +/// See [`global_lock!`] for examples. +pub trait GlobalLockBackend { + /// The name for this global lock. + const NAME: &'static CStr; + /// Item type stored in this global lock. + type Item: 'static; + /// The backend used for this global lock. + type Backend: Backend + 'static; + /// The class for this global lock. + fn get_lock_class() -> &'static LockClassKey; +} + +/// Type used for global locks. +/// +/// See [`global_lock!`] for examples. +pub struct GlobalLock { + inner: Lock, +} + +impl GlobalLock { + /// Creates a global lock. + /// + /// # Safety + /// + /// * Before any other method on this lock is called, [`Self::init`] must be called. + /// * The type `B` must not be used with any other lock. + pub const unsafe fn new(data: B::Item) -> Self { + Self { + inner: Lock { + state: Opaque::uninit(), + data: UnsafeCell::new(data), + _pin: PhantomPinned, + }, + } + } + + /// Initializes a global lock. + /// + /// # Safety + /// + /// Must not be called more than once on a given lock. + pub unsafe fn init(&'static self) { + // SAFETY: The pointer to `state` is valid for the duration of this call, and both `name` + // and `key` are valid indefinitely. The `state` is pinned since we have a `'static` + // reference to `self`. + // + // We have exclusive access to the `state` since the caller of `new` promised to call + // `init` before using any other methods. As `init` can only be called once, all other + // uses of this lock must happen after this call. + unsafe { + B::Backend::init( + self.inner.state.get(), + B::NAME.as_char_ptr(), + B::get_lock_class().as_ptr(), + ) + } + } + + /// Lock this global lock. + pub fn lock(&'static self) -> GlobalGuard { + GlobalGuard { + inner: self.inner.lock(), + } + } + + /// Try to lock this global lock. + pub fn try_lock(&'static self) -> Option> { + Some(GlobalGuard { + inner: self.inner.try_lock()?, + }) + } +} + +/// A guard for a [`GlobalLock`]. +/// +/// See [`global_lock!`] for examples. +pub struct GlobalGuard { + inner: Guard<'static, B::Item, B::Backend>, +} + +impl core::ops::Deref for GlobalGuard { + type Target = B::Item; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl core::ops::DerefMut for GlobalGuard { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +/// A version of [`LockedBy`] for a [`GlobalLock`]. +/// +/// See [`global_lock!`] for examples. +pub struct GlobalLockedBy { + _backend: PhantomData, + value: UnsafeCell, +} + +// SAFETY: The same thread-safety rules as `LockedBy` apply to `GlobalLockedBy`. +unsafe impl Send for GlobalLockedBy +where + T: ?Sized, + B: GlobalLockBackend, + LockedBy: Send, +{ +} + +// SAFETY: The same thread-safety rules as `LockedBy` apply to `GlobalLockedBy`. +unsafe impl Sync for GlobalLockedBy +where + T: ?Sized, + B: GlobalLockBackend, + LockedBy: Sync, +{ +} + +impl GlobalLockedBy { + /// Create a new [`GlobalLockedBy`]. + /// + /// The provided value will be protected by the global lock indicated by `B`. + pub fn new(val: T) -> Self { + Self { + value: UnsafeCell::new(val), + _backend: PhantomData, + } + } +} + +impl GlobalLockedBy { + /// Access the value immutably. + /// + /// The caller must prove shared access to the lock. + pub fn as_ref<'a>(&'a self, _guard: &'a GlobalGuard) -> &'a T { + // SAFETY: The lock is globally unique, so there can only be one guard. + unsafe { &*self.value.get() } + } + + /// Access the value mutably. + /// + /// The caller must prove shared exclusive to the lock. + pub fn as_mut<'a>(&'a self, _guard: &'a mut GlobalGuard) -> &'a mut T { + // SAFETY: The lock is globally unique, so there can only be one guard. + unsafe { &mut *self.value.get() } + } + + /// Access the value mutably directly. + /// + /// The caller has exclusive access to this `GlobalLockedBy`, so they do not need to hold the + /// lock. + pub fn get_mut(&mut self) -> &mut T { + self.value.get_mut() + } +} + +/// Defines a global lock. +/// +/// The global mutex must be initialized before first use. Usually this is done by calling +/// [`GlobalLock::init`] in the module initializer. +/// +/// # Examples +/// +/// A global counter: +/// +/// ``` +/// # mod ex { +/// # use kernel::prelude::*; +/// kernel::sync::global_lock! { +/// // SAFETY: Initialized in module initializer before first use. +/// unsafe(uninit) static MY_COUNTER: Mutex = 0; +/// } +/// +/// fn increment_counter() -> u32 { +/// let mut guard = MY_COUNTER.lock(); +/// *guard += 1; +/// *guard +/// } +/// +/// impl kernel::Module for MyModule { +/// fn init(_module: &'static ThisModule) -> Result { +/// // SAFETY: Called exactly once. +/// unsafe { MY_COUNTER.init() }; +/// +/// Ok(MyModule {}) +/// } +/// } +/// # struct MyModule {} +/// # } +/// ``` +/// +/// A global mutex used to protect all instances of a given struct: +/// +/// ``` +/// # mod ex { +/// # use kernel::prelude::*; +/// use kernel::sync::{GlobalGuard, GlobalLockedBy}; +/// +/// kernel::sync::global_lock! { +/// // SAFETY: Initialized in module initializer before first use. +/// unsafe(uninit) static MY_MUTEX: Mutex<()> = (); +/// } +/// +/// /// All instances of this struct are protected by `MY_MUTEX`. +/// struct MyStruct { +/// my_counter: GlobalLockedBy, +/// } +/// +/// impl MyStruct { +/// /// Increment the counter in this instance. +/// /// +/// /// The caller must hold the `MY_MUTEX` mutex. +/// fn increment(&self, guard: &mut GlobalGuard) -> u32 { +/// let my_counter = self.my_counter.as_mut(guard); +/// *my_counter += 1; +/// *my_counter +/// } +/// } +/// +/// impl kernel::Module for MyModule { +/// fn init(_module: &'static ThisModule) -> Result { +/// // SAFETY: Called exactly once. +/// unsafe { MY_MUTEX.init() }; +/// +/// Ok(MyModule {}) +/// } +/// } +/// # struct MyModule {} +/// # } +/// ``` +#[macro_export] +macro_rules! global_lock { + { + $(#[$meta:meta])* $pub:vis + unsafe(uninit) static $name:ident: $kind:ident<$valuety:ty> = $value:expr; + } => { + #[doc = ::core::concat!( + "Backend type used by [`", + ::core::stringify!($name), + "`](static@", + ::core::stringify!($name), + ")." + )] + #[allow(non_camel_case_types, unreachable_pub)] + $pub enum $name {} + + impl $crate::sync::lock::GlobalLockBackend for $name { + const NAME: &'static $crate::str::CStr = $crate::c_str!(::core::stringify!($name)); + type Item = $valuety; + type Backend = $crate::global_lock_inner!(backend $kind); + + fn get_lock_class() -> &'static $crate::sync::LockClassKey { + $crate::static_lock_class!() + } + } + + $(#[$meta])* + $pub static $name: $crate::sync::lock::GlobalLock<$name> = { + // Defined here to be outside the unsafe scope. + let init: $valuety = $value; + + // SAFETY: + // * The user of this macro promises to initialize the macro before use. + // * We are only generating one static with this backend type. + unsafe { $crate::sync::lock::GlobalLock::new(init) } + }; + }; +} +pub use global_lock; + +#[doc(hidden)] +#[macro_export] +macro_rules! global_lock_inner { + (backend Mutex) => { + $crate::sync::lock::mutex::MutexBackend + }; + (backend SpinLock) => { + $crate::sync::lock::spinlock::SpinLockBackend + }; +} diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs index 30632070ee670..0e946ebefce12 100644 --- a/rust/kernel/sync/lock/mutex.rs +++ b/rust/kernel/sync/lock/mutex.rs @@ -58,7 +58,7 @@ pub use new_mutex; /// } /// /// // Allocate a boxed `Example`. -/// let e = Box::pin_init(Example::new(), GFP_KERNEL)?; +/// let e = KBox::pin_init(Example::new(), GFP_KERNEL)?; /// assert_eq!(e.c, 10); /// assert_eq!(e.d.lock().a, 20); /// assert_eq!(e.d.lock().b, 30); @@ -96,7 +96,7 @@ unsafe impl super::Backend for MutexBackend { unsafe fn init( ptr: *mut Self::State, - name: *const core::ffi::c_char, + name: *const crate::ffi::c_char, key: *mut bindings::lock_class_key, ) { // SAFETY: The safety requirements ensure that `ptr` is valid for writes, and `name` and @@ -115,4 +115,15 @@ unsafe impl super::Backend for MutexBackend { // caller is the owner of the mutex. unsafe { bindings::mutex_unlock(ptr) }; } + + unsafe fn try_lock(ptr: *mut Self::State) -> Option { + // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use. + let result = unsafe { bindings::mutex_trylock(ptr) }; + + if result != 0 { + Some(()) + } else { + None + } + } } diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs index ea5c5bc1ce12e..9f4d128bed983 100644 --- a/rust/kernel/sync/lock/spinlock.rs +++ b/rust/kernel/sync/lock/spinlock.rs @@ -56,7 +56,7 @@ pub use new_spinlock; /// } /// /// // Allocate a boxed `Example`. -/// let e = Box::pin_init(Example::new(), GFP_KERNEL)?; +/// let e = KBox::pin_init(Example::new(), GFP_KERNEL)?; /// assert_eq!(e.c, 10); /// assert_eq!(e.d.lock().a, 20); /// assert_eq!(e.d.lock().b, 30); @@ -95,7 +95,7 @@ unsafe impl super::Backend for SpinLockBackend { unsafe fn init( ptr: *mut Self::State, - name: *const core::ffi::c_char, + name: *const crate::ffi::c_char, key: *mut bindings::lock_class_key, ) { // SAFETY: The safety requirements ensure that `ptr` is valid for writes, and `name` and @@ -114,4 +114,15 @@ unsafe impl super::Backend for SpinLockBackend { // caller is the owner of the spinlock. unsafe { bindings::spin_unlock(ptr) } } + + unsafe fn try_lock(ptr: *mut Self::State) -> Option { + // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use. + let result = unsafe { bindings::spin_trylock(ptr) }; + + if result != 0 { + Some(()) + } else { + None + } + } } diff --git a/rust/kernel/sync/locked_by.rs b/rust/kernel/sync/locked_by.rs index ce2ee8d878658..a7b244675c2b9 100644 --- a/rust/kernel/sync/locked_by.rs +++ b/rust/kernel/sync/locked_by.rs @@ -43,7 +43,7 @@ use core::{cell::UnsafeCell, mem::size_of, ptr}; /// struct InnerDirectory { /// /// The sum of the bytes used by all files. /// bytes_used: u64, -/// _files: Vec, +/// _files: KVec, /// } /// /// struct Directory { diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 0805990758755..07bc22a7645c0 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -6,11 +6,12 @@ use crate::{ bindings, - types::{NotThreadSafe, Opaque}, + ffi::{c_int, c_long, c_uint}, + pid_namespace::PidNamespace, + types::{ARef, NotThreadSafe, Opaque}, }; use core::{ cmp::{Eq, PartialEq}, - ffi::{c_int, c_long, c_uint}, ops::Deref, ptr, }; @@ -36,6 +37,16 @@ macro_rules! current { }; } +/// Returns the currently running task's pid namespace. +#[macro_export] +macro_rules! current_pid_ns { + () => { + // SAFETY: Deref + addr-of below create a temporary `PidNamespaceRef` that cannot outlive + // the caller. + unsafe { &*$crate::task::Task::current_pid_ns() } + }; +} + /// Wraps the kernel's `struct task_struct`. /// /// # Invariants @@ -145,6 +156,97 @@ impl Task { } } + /// Returns a PidNamespace reference for the currently executing task's/thread's pid namespace. + /// + /// This function can be used to create an unbounded lifetime by e.g., storing the returned + /// PidNamespace in a global variable which would be a bug. So the recommended way to get the + /// current task's/thread's pid namespace is to use the [`current_pid_ns`] macro because it is + /// safe. + /// + /// # Safety + /// + /// Callers must ensure that the returned object doesn't outlive the current task/thread. + pub unsafe fn current_pid_ns() -> impl Deref { + struct PidNamespaceRef<'a> { + task: &'a PidNamespace, + _not_send: NotThreadSafe, + } + + impl Deref for PidNamespaceRef<'_> { + type Target = PidNamespace; + + fn deref(&self) -> &Self::Target { + self.task + } + } + + // The lifetime of `PidNamespace` is bound to `Task` and `struct pid`. + // + // The `PidNamespace` of a `Task` doesn't ever change once the `Task` is alive. A + // `unshare(CLONE_NEWPID)` or `setns(fd_pidns/pidfd, CLONE_NEWPID)` will not have an effect + // on the calling `Task`'s pid namespace. It will only effect the pid namespace of children + // created by the calling `Task`. This invariant guarantees that after having acquired a + // reference to a `Task`'s pid namespace it will remain unchanged. + // + // When a task has exited and been reaped `release_task()` will be called. This will set + // the `PidNamespace` of the task to `NULL`. So retrieving the `PidNamespace` of a task + // that is dead will return `NULL`. Note, that neither holding the RCU lock nor holding a + // referencing count to + // the `Task` will prevent `release_task()` being called. + // + // In order to retrieve the `PidNamespace` of a `Task` the `task_active_pid_ns()` function + // can be used. There are two cases to consider: + // + // (1) retrieving the `PidNamespace` of the `current` task + // (2) retrieving the `PidNamespace` of a non-`current` task + // + // From system call context retrieving the `PidNamespace` for case (1) is always safe and + // requires neither RCU locking nor a reference count to be held. Retrieving the + // `PidNamespace` after `release_task()` for current will return `NULL` but no codepath + // like that is exposed to Rust. + // + // Retrieving the `PidNamespace` from system call context for (2) requires RCU protection. + // Accessing `PidNamespace` outside of RCU protection requires a reference count that + // must've been acquired while holding the RCU lock. Note that accessing a non-`current` + // task means `NULL` can be returned as the non-`current` task could have already passed + // through `release_task()`. + // + // To retrieve (1) the `current_pid_ns!()` macro should be used which ensure that the + // returned `PidNamespace` cannot outlive the calling scope. The associated + // `current_pid_ns()` function should not be called directly as it could be abused to + // created an unbounded lifetime for `PidNamespace`. The `current_pid_ns!()` macro allows + // Rust to handle the common case of accessing `current`'s `PidNamespace` without RCU + // protection and without having to acquire a reference count. + // + // For (2) the `task_get_pid_ns()` method must be used. This will always acquire a + // reference on `PidNamespace` and will return an `Option` to force the caller to + // explicitly handle the case where `PidNamespace` is `None`, something that tends to be + // forgotten when doing the equivalent operation in `C`. Missing RCU primitives make it + // difficult to perform operations that are otherwise safe without holding a reference + // count as long as RCU protection is guaranteed. But it is not important currently. But we + // do want it in the future. + // + // Note for (2) the required RCU protection around calling `task_active_pid_ns()` + // synchronizes against putting the last reference of the associated `struct pid` of + // `task->thread_pid`. The `struct pid` stored in that field is used to retrieve the + // `PidNamespace` of the caller. When `release_task()` is called `task->thread_pid` will be + // `NULL`ed and `put_pid()` on said `struct pid` will be delayed in `free_pid()` via + // `call_rcu()` allowing everyone with an RCU protected access to the `struct pid` acquired + // from `task->thread_pid` to finish. + // + // SAFETY: The current task's pid namespace is valid as long as the current task is running. + let pidns = unsafe { bindings::task_active_pid_ns(Task::current_raw()) }; + PidNamespaceRef { + // SAFETY: If the current thread is still running, the current task and its associated + // pid namespace are valid. `PidNamespaceRef` is not `Send`, so we know it cannot be + // transferred to another thread (where it could potentially outlive the current + // `Task`). The caller needs to ensure that the PidNamespaceRef doesn't outlive the + // current task/thread. + task: unsafe { PidNamespace::from_ptr(pidns) }, + _not_send: NotThreadSafe, + } + } + /// Returns a raw pointer to the task. #[inline] pub fn as_ptr(&self) -> *mut bindings::task_struct { @@ -188,11 +290,32 @@ impl Task { unsafe { bindings::signal_pending(self.as_ptr()) != 0 } } - /// Returns the given task's pid in the current pid namespace. - pub fn pid_in_current_ns(&self) -> Pid { - // SAFETY: It's valid to pass a null pointer as the namespace (defaults to current - // namespace). The task pointer is also valid. - unsafe { bindings::task_tgid_nr_ns(self.as_ptr(), ptr::null_mut()) } + /// Returns task's pid namespace with elevated reference count + pub fn get_pid_ns(&self) -> Option> { + // SAFETY: By the type invariant, we know that `self.0` is valid. + let ptr = unsafe { bindings::task_get_pid_ns(self.as_ptr()) }; + if ptr.is_null() { + None + } else { + // SAFETY: `ptr` is valid by the safety requirements of this function. And we own a + // reference count via `task_get_pid_ns()`. + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::pid_namespace`. + Some(unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(ptr.cast::())) }) + } + } + + /// Returns the given task's pid in the provided pid namespace. + #[doc(alias = "task_tgid_nr_ns")] + pub fn tgid_nr_ns(&self, pidns: Option<&PidNamespace>) -> Pid { + let pidns = match pidns { + Some(pidns) => pidns.as_ptr(), + None => core::ptr::null_mut(), + }; + // SAFETY: By the type invariant, we know that `self.0` is valid. We received a valid + // PidNamespace that we can use as a pointer or we received an empty PidNamespace and + // thus pass a null pointer. The underlying C function is safe to be used with NULL + // pointers. + unsafe { bindings::task_tgid_nr_ns(self.as_ptr(), pidns) } } /// Wakes up the task. diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index e3bb5e89f88da..379c0f5772e57 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -12,10 +12,10 @@ pub const NSEC_PER_MSEC: i64 = bindings::NSEC_PER_MSEC as i64; /// The time unit of Linux kernel. One jiffy equals (1/HZ) second. -pub type Jiffies = core::ffi::c_ulong; +pub type Jiffies = crate::ffi::c_ulong; /// The millisecond time unit. -pub type Msecs = core::ffi::c_uint; +pub type Msecs = crate::ffi::c_uint; /// Converts milliseconds to jiffies. #[inline] diff --git a/rust/kernel/tracepoint.rs b/rust/kernel/tracepoint.rs new file mode 100644 index 0000000000000..c6e80aa99e8e1 --- /dev/null +++ b/rust/kernel/tracepoint.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Logic for tracepoints. + +/// Declare the Rust entry point for a tracepoint. +/// +/// This macro generates an unsafe function that calls into C, and its safety requirements will be +/// whatever the relevant C code requires. To document these safety requirements, you may add +/// doc-comments when invoking the macro. +#[macro_export] +macro_rules! declare_trace { + ($($(#[$attr:meta])* $pub:vis unsafe fn $name:ident($($argname:ident : $argtyp:ty),* $(,)?);)*) => {$( + $( #[$attr] )* + #[inline(always)] + $pub unsafe fn $name($($argname : $argtyp),*) { + #[cfg(CONFIG_TRACEPOINTS)] + { + // SAFETY: It's always okay to query the static key for a tracepoint. + let should_trace = unsafe { + $crate::macros::paste! { + $crate::jump_label::static_branch_unlikely!( + $crate::bindings::[< __tracepoint_ $name >], + $crate::bindings::tracepoint, + key + ) + } + }; + + if should_trace { + $crate::macros::paste! { + // SAFETY: The caller guarantees that it is okay to call this tracepoint. + unsafe { $crate::bindings::[< rust_do_trace_ $name >]($($argname),*) }; + } + } + } + + #[cfg(not(CONFIG_TRACEPOINTS))] + { + // If tracepoints are disabled, insert a trivial use of each argument + // to avoid unused argument warnings. + $( let _unused = $argname; )* + } + } + )*} +} + +pub use declare_trace; diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs new file mode 100644 index 0000000000000..1c7d43771a37b --- /dev/null +++ b/rust/kernel/transmute.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Traits for transmuting types. + +/// Types for which any bit pattern is valid. +/// +/// Not all types are valid for all values. For example, a `bool` must be either zero or one, so +/// reading arbitrary bytes into something that contains a `bool` is not okay. +/// +/// It's okay for the type to have padding, as initializing those bytes has no effect. +/// +/// # Safety +/// +/// All bit-patterns must be valid for this type. This type must not have interior mutability. +pub unsafe trait FromBytes {} + +macro_rules! impl_frombytes { + ($($({$($generics:tt)*})? $t:ty, )*) => { + // SAFETY: Safety comments written in the macro invocation. + $(unsafe impl$($($generics)*)? FromBytes for $t {})* + }; +} + +impl_frombytes! { + // SAFETY: All bit patterns are acceptable values of the types below. + u8, u16, u32, u64, usize, + i8, i16, i32, i64, isize, + + // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit + // patterns are also acceptable for arrays of that type. + {} [T], + {} [T; N], +} + +/// Types that can be viewed as an immutable slice of initialized bytes. +/// +/// If a struct implements this trait, then it is okay to copy it byte-for-byte to userspace. This +/// means that it should not have any padding, as padding bytes are uninitialized. Reading +/// uninitialized memory is not just undefined behavior, it may even lead to leaking sensitive +/// information on the stack to userspace. +/// +/// The struct should also not hold kernel pointers, as kernel pointer addresses are also considered +/// sensitive. However, leaking kernel pointers is not considered undefined behavior by Rust, so +/// this is a correctness requirement, but not a safety requirement. +/// +/// # Safety +/// +/// Values of this type may not contain any uninitialized bytes. This type must not have interior +/// mutability. +pub unsafe trait AsBytes {} + +macro_rules! impl_asbytes { + ($($({$($generics:tt)*})? $t:ty, )*) => { + // SAFETY: Safety comments written in the macro invocation. + $(unsafe impl$($($generics)*)? AsBytes for $t {})* + }; +} + +impl_asbytes! { + // SAFETY: Instances of the following types have no uninitialized portions. + u8, u16, u32, u64, usize, + i8, i16, i32, i64, isize, + bool, + char, + str, + + // SAFETY: If individual values in an array have no uninitialized portions, then the array + // itself does not have any uninitialized portions either. + {} [T], + {} [T; N], +} diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 3238ffaab031b..ec6457bb3084a 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -3,13 +3,11 @@ //! Kernel types. use crate::init::{self, PinInit}; -use alloc::boxed::Box; use core::{ cell::UnsafeCell, marker::{PhantomData, PhantomPinned}, mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, - pin::Pin, ptr::NonNull, }; @@ -31,7 +29,7 @@ pub trait ForeignOwnable: Sized { /// For example, it might be invalid, dangling or pointing to uninitialized memory. Using it in /// any way except for [`ForeignOwnable::from_foreign`], [`ForeignOwnable::borrow`], /// [`ForeignOwnable::try_from_foreign`] can result in undefined behavior. - fn into_foreign(self) -> *const core::ffi::c_void; + fn into_foreign(self) -> *const crate::ffi::c_void; /// Borrows a foreign-owned object. /// @@ -39,7 +37,7 @@ pub trait ForeignOwnable: Sized { /// /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. - unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Self::Borrowed<'a>; + unsafe fn borrow<'a>(ptr: *const crate::ffi::c_void) -> Self::Borrowed<'a>; /// Converts a foreign-owned object back to a Rust-owned one. /// @@ -49,7 +47,7 @@ pub trait ForeignOwnable: Sized { /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] for /// this object must have been dropped. - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self; + unsafe fn from_foreign(ptr: *const crate::ffi::c_void) -> Self; /// Tries to convert a foreign-owned object back to a Rust-owned one. /// @@ -60,7 +58,7 @@ pub trait ForeignOwnable: Sized { /// /// `ptr` must either be null or satisfy the safety requirements for /// [`ForeignOwnable::from_foreign`]. - unsafe fn try_from_foreign(ptr: *const core::ffi::c_void) -> Option { + unsafe fn try_from_foreign(ptr: *const crate::ffi::c_void) -> Option { if ptr.is_null() { None } else { @@ -71,64 +69,16 @@ pub trait ForeignOwnable: Sized { } } -impl ForeignOwnable for Box { - type Borrowed<'a> = &'a T; - - fn into_foreign(self) -> *const core::ffi::c_void { - Box::into_raw(self) as _ - } - - unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T { - // SAFETY: The safety requirements for this function ensure that the object is still alive, - // so it is safe to dereference the raw pointer. - // The safety requirements of `from_foreign` also ensure that the object remains alive for - // the lifetime of the returned value. - unsafe { &*ptr.cast() } - } - - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { - // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous - // call to `Self::into_foreign`. - unsafe { Box::from_raw(ptr as _) } - } -} - -impl ForeignOwnable for Pin> { - type Borrowed<'a> = Pin<&'a T>; - - fn into_foreign(self) -> *const core::ffi::c_void { - // SAFETY: We are still treating the box as pinned. - Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) as _ - } - - unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Pin<&'a T> { - // SAFETY: The safety requirements for this function ensure that the object is still alive, - // so it is safe to dereference the raw pointer. - // The safety requirements of `from_foreign` also ensure that the object remains alive for - // the lifetime of the returned value. - let r = unsafe { &*ptr.cast() }; - - // SAFETY: This pointer originates from a `Pin>`. - unsafe { Pin::new_unchecked(r) } - } - - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { - // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous - // call to `Self::into_foreign`. - unsafe { Pin::new_unchecked(Box::from_raw(ptr as _)) } - } -} - impl ForeignOwnable for () { type Borrowed<'a> = (); - fn into_foreign(self) -> *const core::ffi::c_void { + fn into_foreign(self) -> *const crate::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } - unsafe fn borrow<'a>(_: *const core::ffi::c_void) -> Self::Borrowed<'a> {} + unsafe fn borrow<'a>(_: *const crate::ffi::c_void) -> Self::Borrowed<'a> {} - unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {} + unsafe fn from_foreign(_: *const crate::ffi::c_void) -> Self {} } /// Runs a cleanup function/closure when dropped. @@ -185,7 +135,7 @@ impl ForeignOwnable for () { /// # use kernel::types::ScopeGuard; /// fn example3(arg: bool) -> Result { /// let mut vec = -/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len())); +/// ScopeGuard::new_with_data(KVec::new(), |v| pr_info!("vec had {} elements\n", v.len())); /// /// vec.push(10u8, GFP_KERNEL)?; /// if arg { @@ -225,7 +175,7 @@ impl ScopeGuard { impl ScopeGuard<(), fn(())> { /// Creates a new guarded object with the given cleanup function. pub fn new(cleanup: impl FnOnce()) -> ScopeGuard<(), impl FnOnce(())> { - ScopeGuard::new_with_data((), move |_| cleanup()) + ScopeGuard::new_with_data((), move |()| cleanup()) } } @@ -256,7 +206,58 @@ impl Drop for ScopeGuard { /// Stores an opaque value. /// -/// This is meant to be used with FFI objects that are never interpreted by Rust code. +/// `Opaque` is meant to be used with FFI objects that are never interpreted by Rust code. +/// +/// It is used to wrap structs from the C side, like for example `Opaque`. +/// It gets rid of all the usual assumptions that Rust has for a value: +/// +/// * The value is allowed to be uninitialized (for example have invalid bit patterns: `3` for a +/// [`bool`]). +/// * The value is allowed to be mutated, when a `&Opaque` exists on the Rust side. +/// * No uniqueness for mutable references: it is fine to have multiple `&mut Opaque` point to +/// the same value. +/// * The value is not allowed to be shared with other threads (i.e. it is `!Sync`). +/// +/// This has to be used for all values that the C side has access to, because it can't be ensured +/// that the C side is adhering to the usual constraints that Rust needs. +/// +/// Using `Opaque` allows to continue to use references on the Rust side even for values shared +/// with C. +/// +/// # Examples +/// +/// ``` +/// # #![expect(unreachable_pub, clippy::disallowed_names)] +/// use kernel::types::Opaque; +/// # // Emulate a C struct binding which is from C, maybe uninitialized or not, only the C side +/// # // knows. +/// # mod bindings { +/// # pub struct Foo { +/// # pub val: u8, +/// # } +/// # } +/// +/// // `foo.val` is assumed to be handled on the C side, so we use `Opaque` to wrap it. +/// pub struct Foo { +/// foo: Opaque, +/// } +/// +/// impl Foo { +/// pub fn get_val(&self) -> u8 { +/// let ptr = Opaque::get(&self.foo); +/// +/// // SAFETY: `Self` is valid from C side. +/// unsafe { (*ptr).val } +/// } +/// } +/// +/// // Create an instance of `Foo` with the `Opaque` wrapper. +/// let foo = Foo { +/// foo: Opaque::new(bindings::Foo { val: 0xdb }), +/// }; +/// +/// assert_eq!(foo.get_val(), 0xdb); +/// ``` #[repr(transparent)] pub struct Opaque { value: UnsafeCell>, @@ -299,6 +300,22 @@ impl Opaque { } } + /// Creates a fallible pin-initializer from the given initializer closure. + /// + /// The returned initializer calls the given closure with the pointer to the inner `T` of this + /// `Opaque`. Since this memory is uninitialized, the closure is not allowed to read from it. + /// + /// This function is safe, because the `T` inside of an `Opaque` is allowed to be + /// uninitialized. Additionally, access to the inner `T` requires `unsafe`, so the caller needs + /// to verify at that point that the inner value is valid. + pub fn try_ffi_init( + init_func: impl FnOnce(*mut T) -> Result<(), E>, + ) -> impl PinInit { + // SAFETY: We contain a `MaybeUninit`, so it is OK for the `init_func` to not fully + // initialize the `T`. + unsafe { init::pin_init_from_closure::<_, E>(move |slot| init_func(Self::raw_get(slot))) } + } + /// Returns a raw pointer to the opaque data. pub const fn get(&self) -> *mut T { UnsafeCell::get(&self.value).cast::() @@ -410,6 +427,7 @@ impl ARef { /// /// struct Empty {} /// + /// # // SAFETY: TODO. /// unsafe impl AlwaysRefCounted for Empty { /// fn inc_ref(&self) {} /// unsafe fn dec_ref(_obj: NonNull) {} @@ -417,6 +435,7 @@ impl ARef { /// /// let mut data = Empty {}; /// let ptr = NonNull::::new(&mut data as *mut _).unwrap(); + /// # // SAFETY: TODO. /// let data_ref: ARef = unsafe { ARef::from_raw(ptr) }; /// let raw_ptr: NonNull = ARef::into_raw(data_ref); /// @@ -461,6 +480,15 @@ impl Drop for ARef { } /// A sum type that always holds either a value of type `L` or `R`. +/// +/// # Examples +/// +/// ``` +/// use kernel::types::Either; +/// +/// let left_value: Either = Either::Left(7); +/// let right_value: Either = Either::Right("right value"); +/// ``` pub enum Either { /// Constructs an instance of [`Either`] containing a value of type `L`. Left(L), @@ -469,70 +497,6 @@ pub enum Either { Right(R), } -/// Types for which any bit pattern is valid. -/// -/// Not all types are valid for all values. For example, a `bool` must be either zero or one, so -/// reading arbitrary bytes into something that contains a `bool` is not okay. -/// -/// It's okay for the type to have padding, as initializing those bytes has no effect. -/// -/// # Safety -/// -/// All bit-patterns must be valid for this type. This type must not have interior mutability. -pub unsafe trait FromBytes {} - -// SAFETY: All bit patterns are acceptable values of the types below. -unsafe impl FromBytes for u8 {} -unsafe impl FromBytes for u16 {} -unsafe impl FromBytes for u32 {} -unsafe impl FromBytes for u64 {} -unsafe impl FromBytes for usize {} -unsafe impl FromBytes for i8 {} -unsafe impl FromBytes for i16 {} -unsafe impl FromBytes for i32 {} -unsafe impl FromBytes for i64 {} -unsafe impl FromBytes for isize {} -// SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit -// patterns are also acceptable for arrays of that type. -unsafe impl FromBytes for [T] {} -unsafe impl FromBytes for [T; N] {} - -/// Types that can be viewed as an immutable slice of initialized bytes. -/// -/// If a struct implements this trait, then it is okay to copy it byte-for-byte to userspace. This -/// means that it should not have any padding, as padding bytes are uninitialized. Reading -/// uninitialized memory is not just undefined behavior, it may even lead to leaking sensitive -/// information on the stack to userspace. -/// -/// The struct should also not hold kernel pointers, as kernel pointer addresses are also considered -/// sensitive. However, leaking kernel pointers is not considered undefined behavior by Rust, so -/// this is a correctness requirement, but not a safety requirement. -/// -/// # Safety -/// -/// Values of this type may not contain any uninitialized bytes. This type must not have interior -/// mutability. -pub unsafe trait AsBytes {} - -// SAFETY: Instances of the following types have no uninitialized portions. -unsafe impl AsBytes for u8 {} -unsafe impl AsBytes for u16 {} -unsafe impl AsBytes for u32 {} -unsafe impl AsBytes for u64 {} -unsafe impl AsBytes for usize {} -unsafe impl AsBytes for i8 {} -unsafe impl AsBytes for i16 {} -unsafe impl AsBytes for i32 {} -unsafe impl AsBytes for i64 {} -unsafe impl AsBytes for isize {} -unsafe impl AsBytes for bool {} -unsafe impl AsBytes for char {} -unsafe impl AsBytes for str {} -// SAFETY: If individual values in an array have no uninitialized portions, then the array itself -// does not have any uninitialized portions either. -unsafe impl AsBytes for [T] {} -unsafe impl AsBytes for [T; N] {} - /// Zero-sized type to mark types not [`Send`]. /// /// Add this type as a field to your struct if your type should not be sent to a different task. diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index e9347cff99ab2..05b0b8d13b10d 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -8,11 +8,10 @@ use crate::{ alloc::Flags, bindings, error::Result, + ffi::{c_ulong, c_void}, prelude::*, - types::{AsBytes, FromBytes}, + transmute::{AsBytes, FromBytes}, }; -use alloc::vec::Vec; -use core::ffi::{c_ulong, c_void}; use core::mem::{size_of, MaybeUninit}; /// The type used for userspace addresses. @@ -46,15 +45,14 @@ pub type UserPtr = usize; /// every byte in the region. /// /// ```no_run -/// use alloc::vec::Vec; -/// use core::ffi::c_void; +/// use kernel::ffi::c_void; /// use kernel::error::Result; /// use kernel::uaccess::{UserPtr, UserSlice}; /// /// fn bytes_add_one(uptr: UserPtr, len: usize) -> Result<()> { /// let (read, mut write) = UserSlice::new(uptr, len).reader_writer(); /// -/// let mut buf = Vec::new(); +/// let mut buf = KVec::new(); /// read.read_all(&mut buf, GFP_KERNEL)?; /// /// for b in &mut buf { @@ -69,8 +67,7 @@ pub type UserPtr = usize; /// Example illustrating a TOCTOU (time-of-check to time-of-use) bug. /// /// ```no_run -/// use alloc::vec::Vec; -/// use core::ffi::c_void; +/// use kernel::ffi::c_void; /// use kernel::error::{code::EINVAL, Result}; /// use kernel::uaccess::{UserPtr, UserSlice}; /// @@ -78,21 +75,21 @@ pub type UserPtr = usize; /// fn is_valid(uptr: UserPtr, len: usize) -> Result { /// let read = UserSlice::new(uptr, len).reader(); /// -/// let mut buf = Vec::new(); +/// let mut buf = KVec::new(); /// read.read_all(&mut buf, GFP_KERNEL)?; /// /// todo!() /// } /// /// /// Returns the bytes behind this user pointer if they are valid. -/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result> { +/// fn get_bytes_if_valid(uptr: UserPtr, len: usize) -> Result> { /// if !is_valid(uptr, len)? { /// return Err(EINVAL); /// } /// /// let read = UserSlice::new(uptr, len).reader(); /// -/// let mut buf = Vec::new(); +/// let mut buf = KVec::new(); /// read.read_all(&mut buf, GFP_KERNEL)?; /// /// // THIS IS A BUG! The bytes could have changed since we checked them. @@ -130,7 +127,7 @@ impl UserSlice { /// Reads the entirety of the user slice, appending it to the end of the provided buffer. /// /// Fails with [`EFAULT`] if the read happens on a bad address. - pub fn read_all(self, buf: &mut Vec, flags: Flags) -> Result { + pub fn read_all(self, buf: &mut KVec, flags: Flags) -> Result { self.reader().read_all(buf, flags) } @@ -291,9 +288,9 @@ impl UserSliceReader { /// Reads the entirety of the user slice, appending it to the end of the provided buffer. /// /// Fails with [`EFAULT`] if the read happens on a bad address. - pub fn read_all(mut self, buf: &mut Vec, flags: Flags) -> Result { + pub fn read_all(mut self, buf: &mut KVec, flags: Flags) -> Result { let len = self.length; - VecExt::::reserve(buf, len, flags)?; + buf.reserve(len, flags)?; // The call to `try_reserve` was successful, so the spare capacity is at least `len` bytes // long. diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 553a5cba2adcb..4d1d2062f6eba 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -216,7 +216,7 @@ impl Queue { func: Some(func), }); - self.enqueue(Box::pin_init(init, flags).map_err(|_| AllocError)?); + self.enqueue(KBox::pin_init(init, flags).map_err(|_| AllocError)?); Ok(()) } } @@ -239,9 +239,9 @@ impl ClosureWork { } impl WorkItem for ClosureWork { - type Pointer = Pin>; + type Pointer = Pin>; - fn run(mut this: Pin>) { + fn run(mut this: Pin>) { if let Some(func) = this.as_mut().project().take() { (func)() } @@ -297,7 +297,7 @@ pub unsafe trait RawWorkItem { /// Defines the method that should be called directly when a work item is executed. /// -/// This trait is implemented by `Pin>` and [`Arc`], and is mainly intended to be +/// This trait is implemented by `Pin>` and [`Arc`], and is mainly intended to be /// implemented for smart pointer types. For your own structs, you would implement [`WorkItem`] /// instead. The [`run`] method on this trait will usually just perform the appropriate /// `container_of` translation and then call into the [`run`][WorkItem::run] method from the @@ -329,7 +329,7 @@ pub unsafe trait WorkItemPointer: RawWorkItem { /// This trait is used when the `work_struct` field is defined using the [`Work`] helper. pub trait WorkItem { /// The pointer type that this struct is wrapped in. This will typically be `Arc` or - /// `Pin>`. + /// `Pin>`. type Pointer: WorkItemPointer; /// The method that should be called when this work item is executed. @@ -366,7 +366,6 @@ unsafe impl Sync for Work {} impl Work { /// Creates a new instance of [`Work`]. #[inline] - #[allow(clippy::new_ret_no_self)] pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit where T: WorkItem, @@ -520,13 +519,14 @@ impl_has_work! { impl{T} HasWork for ClosureWork { self.work } } +// SAFETY: TODO. unsafe impl WorkItemPointer for Arc where T: WorkItem, T: HasWork, { unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { - // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work`. + // The `__enqueue` method always uses a `work_struct` stored in a `Work`. let ptr = ptr as *mut Work; // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`. let ptr = unsafe { T::work_container_of(ptr) }; @@ -537,6 +537,7 @@ where } } +// SAFETY: TODO. unsafe impl RawWorkItem for Arc where T: WorkItem, @@ -565,18 +566,19 @@ where } } -unsafe impl WorkItemPointer for Pin> +// SAFETY: TODO. +unsafe impl WorkItemPointer for Pin> where T: WorkItem, T: HasWork, { unsafe extern "C" fn run(ptr: *mut bindings::work_struct) { - // SAFETY: The `__enqueue` method always uses a `work_struct` stored in a `Work`. + // The `__enqueue` method always uses a `work_struct` stored in a `Work`. let ptr = ptr as *mut Work; // SAFETY: This computes the pointer that `__enqueue` got from `Arc::into_raw`. let ptr = unsafe { T::work_container_of(ptr) }; // SAFETY: This pointer comes from `Arc::into_raw` and we've been given back ownership. - let boxed = unsafe { Box::from_raw(ptr) }; + let boxed = unsafe { KBox::from_raw(ptr) }; // SAFETY: The box was already pinned when it was enqueued. let pinned = unsafe { Pin::new_unchecked(boxed) }; @@ -584,7 +586,8 @@ where } } -unsafe impl RawWorkItem for Pin> +// SAFETY: TODO. +unsafe impl RawWorkItem for Pin> where T: WorkItem, T: HasWork, @@ -598,9 +601,9 @@ where // SAFETY: We're not going to move `self` or any of its fields, so its okay to temporarily // remove the `Pin` wrapper. let boxed = unsafe { Pin::into_inner_unchecked(self) }; - let ptr = Box::into_raw(boxed); + let ptr = KBox::into_raw(boxed); - // SAFETY: Pointers into a `Box` point at a valid value. + // SAFETY: Pointers into a `KBox` point at a valid value. let work_ptr = unsafe { T::raw_get_work(ptr) }; // SAFETY: `raw_get_work` returns a pointer to a valid value. let work_ptr = unsafe { Work::raw_get(work_ptr) }; diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index a626b1145e5c4..4ab94e44adfe3 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -30,7 +30,7 @@ use proc_macro::TokenStream; /// /// # Examples /// -/// ```ignore +/// ``` /// use kernel::prelude::*; /// /// module!{ @@ -42,22 +42,16 @@ use proc_macro::TokenStream; /// alias: ["alternate_module_name"], /// } /// -/// struct MyModule; +/// struct MyModule(i32); /// /// impl kernel::Module for MyModule { -/// fn init() -> Result { -/// // If the parameter is writeable, then the kparam lock must be -/// // taken to read the parameter: -/// { -/// let lock = THIS_MODULE.kernel_param_lock(); -/// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock)); -/// } -/// // If the parameter is read only, it can be read without locking -/// // the kernel parameters: -/// pr_info!("i32 param is: {}\n", my_i32.read()); -/// Ok(Self) +/// fn init(_module: &'static ThisModule) -> Result { +/// let foo: i32 = 42; +/// pr_info!("I contain: {}\n", foo); +/// Ok(Self(foo)) /// } /// } +/// # fn main() {} /// ``` /// /// ## Firmware @@ -69,7 +63,7 @@ use proc_macro::TokenStream; /// build an initramfs uses this information to put the firmware files into /// the initramfs image. /// -/// ```ignore +/// ``` /// use kernel::prelude::*; /// /// module!{ @@ -84,10 +78,11 @@ use proc_macro::TokenStream; /// struct MyDeviceDriverModule; /// /// impl kernel::Module for MyDeviceDriverModule { -/// fn init() -> Result { +/// fn init(_module: &'static ThisModule) -> Result { /// Ok(Self) /// } /// } +/// # fn main() {} /// ``` /// /// # Supported argument types @@ -132,7 +127,7 @@ pub fn module(ts: TokenStream) -> TokenStream { /// calls to this function at compile time: /// /// ```compile_fail -/// # use kernel::error::VTABLE_DEFAULT_ERROR; +/// # // Intentionally missing `use`s to simplify `rusttest`. /// kernel::build_error(VTABLE_DEFAULT_ERROR) /// ``` /// @@ -142,7 +137,7 @@ pub fn module(ts: TokenStream) -> TokenStream { /// /// # Examples /// -/// ```ignore +/// ``` /// use kernel::error::VTABLE_DEFAULT_ERROR; /// use kernel::prelude::*; /// @@ -187,12 +182,27 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream { /// /// # Examples /// -/// ```ignore -/// use kernel::macro::concat_idents; +/// ``` +/// # const binder_driver_return_protocol_BR_OK: u32 = 0; +/// # const binder_driver_return_protocol_BR_ERROR: u32 = 1; +/// # const binder_driver_return_protocol_BR_TRANSACTION: u32 = 2; +/// # const binder_driver_return_protocol_BR_REPLY: u32 = 3; +/// # const binder_driver_return_protocol_BR_DEAD_REPLY: u32 = 4; +/// # const binder_driver_return_protocol_BR_TRANSACTION_COMPLETE: u32 = 5; +/// # const binder_driver_return_protocol_BR_INCREFS: u32 = 6; +/// # const binder_driver_return_protocol_BR_ACQUIRE: u32 = 7; +/// # const binder_driver_return_protocol_BR_RELEASE: u32 = 8; +/// # const binder_driver_return_protocol_BR_DECREFS: u32 = 9; +/// # const binder_driver_return_protocol_BR_NOOP: u32 = 10; +/// # const binder_driver_return_protocol_BR_SPAWN_LOOPER: u32 = 11; +/// # const binder_driver_return_protocol_BR_DEAD_BINDER: u32 = 12; +/// # const binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE: u32 = 13; +/// # const binder_driver_return_protocol_BR_FAILED_REPLY: u32 = 14; +/// use kernel::macros::concat_idents; /// /// macro_rules! pub_no_prefix { /// ($prefix:ident, $($newname:ident),+) => { -/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+ +/// $(pub(crate) const $newname: u32 = concat_idents!($prefix, $newname);)+ /// }; /// } /// @@ -238,21 +248,35 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream { /// /// # Examples /// -/// ```rust,ignore +/// ``` +/// # #![feature(lint_reasons)] +/// # use kernel::prelude::*; +/// # use std::{sync::Mutex, process::Command}; +/// # use kernel::macros::pin_data; /// #[pin_data] /// struct DriverData { /// #[pin] -/// queue: Mutex>, -/// buf: Box<[u8; 1024 * 1024]>, +/// queue: Mutex>, +/// buf: KBox<[u8; 1024 * 1024]>, /// } /// ``` /// -/// ```rust,ignore +/// ``` +/// # #![feature(lint_reasons)] +/// # use kernel::prelude::*; +/// # use std::{sync::Mutex, process::Command}; +/// # use core::pin::Pin; +/// # pub struct Info; +/// # mod bindings { +/// # pub unsafe fn destroy_info(_ptr: *mut super::Info) {} +/// # } +/// use kernel::macros::{pin_data, pinned_drop}; +/// /// #[pin_data(PinnedDrop)] /// struct DriverData { /// #[pin] -/// queue: Mutex>, -/// buf: Box<[u8; 1024 * 1024]>, +/// queue: Mutex>, +/// buf: KBox<[u8; 1024 * 1024]>, /// raw_info: *mut Info, /// } /// @@ -262,6 +286,7 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream { /// unsafe { bindings::destroy_info(self.raw_info) }; /// } /// } +/// # fn main() {} /// ``` /// /// [`pin_init!`]: ../kernel/macro.pin_init.html @@ -277,13 +302,22 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { /// /// # Examples /// -/// ```rust,ignore +/// ``` +/// # #![feature(lint_reasons)] +/// # use kernel::prelude::*; +/// # use macros::{pin_data, pinned_drop}; +/// # use std::{sync::Mutex, process::Command}; +/// # use core::pin::Pin; +/// # mod bindings { +/// # pub struct Info; +/// # pub unsafe fn destroy_info(_ptr: *mut Info) {} +/// # } /// #[pin_data(PinnedDrop)] /// struct DriverData { /// #[pin] -/// queue: Mutex>, -/// buf: Box<[u8; 1024 * 1024]>, -/// raw_info: *mut Info, +/// queue: Mutex>, +/// buf: KBox<[u8; 1024 * 1024]>, +/// raw_info: *mut bindings::Info, /// } /// /// #[pinned_drop] @@ -309,12 +343,25 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// /// # Example /// -/// ```ignore -/// use kernel::macro::paste; -/// +/// ``` +/// # const binder_driver_return_protocol_BR_OK: u32 = 0; +/// # const binder_driver_return_protocol_BR_ERROR: u32 = 1; +/// # const binder_driver_return_protocol_BR_TRANSACTION: u32 = 2; +/// # const binder_driver_return_protocol_BR_REPLY: u32 = 3; +/// # const binder_driver_return_protocol_BR_DEAD_REPLY: u32 = 4; +/// # const binder_driver_return_protocol_BR_TRANSACTION_COMPLETE: u32 = 5; +/// # const binder_driver_return_protocol_BR_INCREFS: u32 = 6; +/// # const binder_driver_return_protocol_BR_ACQUIRE: u32 = 7; +/// # const binder_driver_return_protocol_BR_RELEASE: u32 = 8; +/// # const binder_driver_return_protocol_BR_DECREFS: u32 = 9; +/// # const binder_driver_return_protocol_BR_NOOP: u32 = 10; +/// # const binder_driver_return_protocol_BR_SPAWN_LOOPER: u32 = 11; +/// # const binder_driver_return_protocol_BR_DEAD_BINDER: u32 = 12; +/// # const binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE: u32 = 13; +/// # const binder_driver_return_protocol_BR_FAILED_REPLY: u32 = 14; /// macro_rules! pub_no_prefix { /// ($prefix:ident, $($newname:ident),+) => { -/// paste! { +/// kernel::macros::paste! { /// $(pub(crate) const $newname: u32 = [<$prefix $newname>];)+ /// } /// }; @@ -353,13 +400,26 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// * `lower`: change the identifier to lower case. /// * `upper`: change the identifier to upper case. /// -/// ```ignore -/// use kernel::macro::paste; -/// +/// ``` +/// # const binder_driver_return_protocol_BR_OK: u32 = 0; +/// # const binder_driver_return_protocol_BR_ERROR: u32 = 1; +/// # const binder_driver_return_protocol_BR_TRANSACTION: u32 = 2; +/// # const binder_driver_return_protocol_BR_REPLY: u32 = 3; +/// # const binder_driver_return_protocol_BR_DEAD_REPLY: u32 = 4; +/// # const binder_driver_return_protocol_BR_TRANSACTION_COMPLETE: u32 = 5; +/// # const binder_driver_return_protocol_BR_INCREFS: u32 = 6; +/// # const binder_driver_return_protocol_BR_ACQUIRE: u32 = 7; +/// # const binder_driver_return_protocol_BR_RELEASE: u32 = 8; +/// # const binder_driver_return_protocol_BR_DECREFS: u32 = 9; +/// # const binder_driver_return_protocol_BR_NOOP: u32 = 10; +/// # const binder_driver_return_protocol_BR_SPAWN_LOOPER: u32 = 11; +/// # const binder_driver_return_protocol_BR_DEAD_BINDER: u32 = 12; +/// # const binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE: u32 = 13; +/// # const binder_driver_return_protocol_BR_FAILED_REPLY: u32 = 14; /// macro_rules! pub_no_prefix { /// ($prefix:ident, $($newname:ident),+) => { /// kernel::macros::paste! { -/// $(pub(crate) const fn [<$newname:lower:span>]: u32 = [<$prefix $newname:span>];)+ +/// $(pub(crate) const fn [<$newname:lower:span>]() -> u32 { [<$prefix $newname:span>] })+ /// } /// }; /// } @@ -390,7 +450,7 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// /// Literals can also be concatenated with other identifiers: /// -/// ```ignore +/// ``` /// macro_rules! create_numbered_fn { /// ($name:literal, $val:literal) => { /// kernel::macros::paste! { @@ -418,7 +478,9 @@ pub fn paste(input: TokenStream) -> TokenStream { /// /// # Examples /// -/// ```rust,ignore +/// ``` +/// use kernel::macros::Zeroable; +/// /// #[derive(Zeroable)] /// pub struct DriverData { /// id: i64, diff --git a/rust/macros/module.rs b/rust/macros/module.rs index aef3b132f32b3..2587f41b0d392 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -232,6 +232,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { mod __module_init {{ mod __module_init {{ use super::super::{type_}; + use kernel::init::PinInit; /// The \"Rust loadable module\" mark. // @@ -242,7 +243,8 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[used] static __IS_RUST_MODULE: () = (); - static mut __MOD: Option<{type_}> = None; + static mut __MOD: core::mem::MaybeUninit<{type_}> = + core::mem::MaybeUninit::uninit(); // Loadable modules need to export the `{{init,cleanup}}_module` identifiers. /// # Safety @@ -253,7 +255,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[doc(hidden)] #[no_mangle] #[link_section = \".init.text\"] - pub unsafe extern \"C\" fn init_module() -> core::ffi::c_int {{ + pub unsafe extern \"C\" fn init_module() -> kernel::ffi::c_int {{ // SAFETY: This function is inaccessible to the outside due to the double // module wrapping it. It is called exactly once by the C side via its // unique name. @@ -292,7 +294,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[doc(hidden)] #[link_section = \"{initcall_section}\"] #[used] - pub static __{name}_initcall: extern \"C\" fn() -> core::ffi::c_int = __{name}_init; + pub static __{name}_initcall: extern \"C\" fn() -> kernel::ffi::c_int = __{name}_init; #[cfg(not(MODULE))] #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] @@ -307,7 +309,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[cfg(not(MODULE))] #[doc(hidden)] #[no_mangle] - pub extern \"C\" fn __{name}_init() -> core::ffi::c_int {{ + pub extern \"C\" fn __{name}_init() -> kernel::ffi::c_int {{ // SAFETY: This function is inaccessible to the outside due to the double // module wrapping it. It is called exactly once by the C side via its // placement above in the initcall section. @@ -330,21 +332,15 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { /// # Safety /// /// This function must only be called once. - unsafe fn __init() -> core::ffi::c_int {{ - match <{type_} as kernel::Module>::init(&super::super::THIS_MODULE) {{ - Ok(m) => {{ - // SAFETY: No data race, since `__MOD` can only be accessed by this - // module and there only `__init` and `__exit` access it. These - // functions are only called once and `__exit` cannot be called - // before or during `__init`. - unsafe {{ - __MOD = Some(m); - }} - return 0; - }} - Err(e) => {{ - return e.to_errno(); - }} + unsafe fn __init() -> kernel::ffi::c_int {{ + let initer = + <{type_} as kernel::InPlaceModule>::init(&super::super::THIS_MODULE); + // SAFETY: No data race, since `__MOD` can only be accessed by this module + // and there only `__init` and `__exit` access it. These functions are only + // called once and `__exit` cannot be called before or during `__init`. + match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{ + Ok(m) => 0, + Err(e) => e.to_errno(), }} }} @@ -359,7 +355,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { // called once and `__init` was already called. unsafe {{ // Invokes `drop()` on `__MOD`, which should be used for cleanup. - __MOD = None; + __MOD.assume_init_drop(); }} }} diff --git a/rust/macros/paste.rs b/rust/macros/paste.rs index f40d42b35b586..6529a387673fb 100644 --- a/rust/macros/paste.rs +++ b/rust/macros/paste.rs @@ -2,7 +2,7 @@ use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree}; -fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree { +fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> { let mut tokens = tokens.iter(); let mut segments = Vec::new(); let mut span = None; @@ -46,12 +46,21 @@ fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree { }; segments.push((value, sp)); } - _ => panic!("unexpected token in paste segments"), + Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => { + let tokens = group.stream().into_iter().collect::>(); + segments.append(&mut concat_helper(tokens.as_slice())); + } + token => panic!("unexpected token in paste segments: {:?}", token), }; } + segments +} + +fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree { + let segments = concat_helper(tokens); let pasted: String = segments.into_iter().map(|x| x.0).collect(); - TokenTree::Ident(Ident::new(&pasted, span.unwrap_or(group_span))) + TokenTree::Ident(Ident::new(&pasted, group_span)) } pub(crate) fn expand(tokens: &mut Vec) { diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs index 80a00260e3e7a..13495910271fa 100644 --- a/rust/uapi/lib.rs +++ b/rust/uapi/lib.rs @@ -14,6 +14,7 @@ #![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))] #![allow( clippy::all, + clippy::undocumented_unsafe_blocks, dead_code, missing_docs, non_camel_case_types, @@ -24,4 +25,9 @@ unsafe_op_in_unsafe_fn )] +// Manual definition of blocklisted types. +type __kernel_size_t = usize; +type __kernel_ssize_t = isize; +type __kernel_ptrdiff_t = isize; + include!(concat!(env!("OBJTREE"), "/rust/uapi/uapi_generated.rs")); diff --git a/samples/hw_breakpoint/data_breakpoint.c b/samples/hw_breakpoint/data_breakpoint.c index a2c831e89ce0e..fbb03b66dcbd1 100644 --- a/samples/hw_breakpoint/data_breakpoint.c +++ b/samples/hw_breakpoint/data_breakpoint.c @@ -52,8 +52,8 @@ static int __init hw_break_module_init(void) attr.bp_type = HW_BREAKPOINT_W; sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL); - if (IS_ERR((void __force *)sample_hbp)) { - ret = PTR_ERR((void __force *)sample_hbp); + if (IS_ERR_PCPU(sample_hbp)) { + ret = PTR_ERR_PCPU(sample_hbp); goto fail; } diff --git a/samples/kfifo/dma-example.c b/samples/kfifo/dma-example.c index 48df719dac8c6..8076ac410161a 100644 --- a/samples/kfifo/dma-example.c +++ b/samples/kfifo/dma-example.c @@ -9,6 +9,7 @@ #include #include #include +#include /* * This module shows how to handle fifo dma operations. diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 03086dabbea44..c1a5c16553955 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 +ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o +rust_print-y := rust_print_main.o rust_print_events.o + subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 2a9eaab62d1ca..4aaf117bf8e3c 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -13,7 +13,7 @@ module! { } struct RustMinimal { - numbers: Vec, + numbers: KVec, } impl kernel::Module for RustMinimal { @@ -21,7 +21,7 @@ impl kernel::Module for RustMinimal { pr_info!("Rust minimal sample (init)\n"); pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); - let mut numbers = Vec::new(); + let mut numbers = KVec::new(); numbers.push(72, GFP_KERNEL)?; numbers.push(108, GFP_KERNEL)?; numbers.push(200, GFP_KERNEL)?; diff --git a/samples/rust/rust_print_events.c b/samples/rust/rust_print_events.c new file mode 100644 index 0000000000000..a9169ff0edf18 --- /dev/null +++ b/samples/rust/rust_print_events.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2024 Google LLC + */ + +#define CREATE_TRACE_POINTS +#define CREATE_RUST_TRACE_POINTS +#include diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print_main.rs similarity index 84% rename from samples/rust/rust_print.rs rename to samples/rust/rust_print_main.rs index 6eabb0d79ea3a..aed90a6feecfa 100644 --- a/samples/rust/rust_print.rs +++ b/samples/rust/rust_print_main.rs @@ -15,6 +15,7 @@ module! { struct RustPrint; +#[expect(clippy::disallowed_macros)] fn arc_print() -> Result { use kernel::sync::*; @@ -69,6 +70,8 @@ impl kernel::Module for RustPrint { arc_print()?; + trace::trace_rust_sample_loaded(42); + Ok(RustPrint) } } @@ -78,3 +81,19 @@ impl Drop for RustPrint { pr_info!("Rust printing macros sample (exit)\n"); } } + +mod trace { + use core::ffi::c_int; + + kernel::declare_trace! { + /// # Safety + /// + /// Always safe to call. + unsafe fn rust_sample_loaded(magic: c_int); + } + + pub(crate) fn trace_rust_sample_loaded(magic: i32) { + // SAFETY: Always safe to call. + unsafe { rust_sample_loaded(magic as c_int) } + } +} diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index ed8a7493524b2..8c311b997e246 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -205,7 +205,7 @@ if_changed_dep = $(if $(if-changed-cond),$(cmd_and_fixdep),@:) cmd_and_fixdep = \ $(cmd); \ - scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\ + $(objtree)/scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\ rm -f $(depfile) # Usage: $(call if_changed_rule,foo) diff --git a/scripts/Makefile.autofdo b/scripts/Makefile.autofdo new file mode 100644 index 0000000000000..1caf2457e585c --- /dev/null +++ b/scripts/Makefile.autofdo @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Enable available and selected Clang AutoFDO features. + +CFLAGS_AUTOFDO_CLANG := -fdebug-info-for-profiling -mllvm -enable-fs-discriminator=true -mllvm -improved-fs-discriminator=true + +ifndef CONFIG_DEBUG_INFO + CFLAGS_AUTOFDO_CLANG += -gmlt +endif + +ifdef CLANG_AUTOFDO_PROFILE + CFLAGS_AUTOFDO_CLANG += -fprofile-sample-use=$(CLANG_AUTOFDO_PROFILE) -ffunction-sections + CFLAGS_AUTOFDO_CLANG += -fsplit-machine-functions +endif + +ifdef CONFIG_LTO_CLANG_THIN + ifdef CLANG_AUTOFDO_PROFILE + KBUILD_LDFLAGS += --lto-sample-profile=$(CLANG_AUTOFDO_PROFILE) + endif + KBUILD_LDFLAGS += --mllvm=-enable-fs-discriminator=true --mllvm=-improved-fs-discriminator=true -plugin-opt=thinlto + KBUILD_LDFLAGS += -plugin-opt=-split-machine-functions +endif + +export CFLAGS_AUTOFDO_CLANG diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 8f423a1faf507..c16e4cf54d770 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -3,7 +3,7 @@ # Building # ========================================================================== -src := $(if $(VPATH),$(VPATH)/)$(obj) +src := $(srcroot)/$(obj) PHONY := $(obj)/ $(obj)/: @@ -34,7 +34,7 @@ subdir-asflags-y := subdir-ccflags-y := # Read auto.conf if it exists, otherwise ignore --include include/config/auto.conf +-include $(objtree)/include/config/auto.conf include $(srctree)/scripts/Kbuild.include include $(srctree)/scripts/Makefile.compiler @@ -107,20 +107,14 @@ cmd_cpp_i_c = $(CPP) $(c_flags) -o $@ $< $(obj)/%.i: $(obj)/%.c FORCE $(call if_changed_dep,cpp_i_c) -genksyms = scripts/genksyms/genksyms \ - $(if $(1), -T $(2)) \ - $(if $(KBUILD_PRESERVE), -p) \ - -r $(or $(wildcard $(2:.symtypes=.symref)), /dev/null) +genksyms = $(objtree)/scripts/genksyms/genksyms \ + $(if $(KBUILD_SYMTYPES), -T $(@:.o=.symtypes)) \ + $(if $(KBUILD_PRESERVE), -p) \ + $(addprefix -r , $(wildcard $(@:.o=.symref))) # These mirror gensymtypes_S and co below, keep them in synch. cmd_gensymtypes_c = $(CPP) -D__GENKSYMS__ $(c_flags) $< | $(genksyms) -quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@ - cmd_cc_symtypes_c = $(call cmd_gensymtypes_c,true,$@) >/dev/null - -$(obj)/%.symtypes : $(obj)/%.c FORCE - $(call cmd,cc_symtypes_c) - # LLVM assembly # Generate .ll files from .c quiet_cmd_cc_ll_c = CC $(quiet_modtag) $@ @@ -135,17 +129,6 @@ $(obj)/%.ll: $(obj)/%.c FORCE is-single-obj-m = $(and $(part-of-module),$(filter $@, $(obj-m)),y) -# When a module consists of a single object, there is no reason to keep LLVM IR. -# Make $(LD) covert LLVM IR to ELF here. -ifdef CONFIG_LTO_CLANG -cmd_ld_single_m = $(if $(is-single-obj-m), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@) -endif - -quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ - cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \ - $(cmd_ld_single_m) \ - $(cmd_objtool) - ifdef CONFIG_MODVERSIONS # When module versioning is enabled the following steps are executed: # o compile a .o from .c @@ -158,8 +141,7 @@ ifdef CONFIG_MODVERSIONS gen_symversions = \ if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \ - $(call cmd_gensymtypes_$(1),$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \ - >> $(dot-target).cmd; \ + $(cmd_gensymtypes_$1) >> $(dot-target).cmd; \ fi cmd_gen_symversions_c = $(call gen_symversions,c) @@ -207,23 +189,6 @@ ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),) cmd_warn_shared_object = $(if $(word 2, $(modname-multi)),$(warning $(kbuild-file): $*.o is added to multiple modules: $(modname-multi))) endif -define rule_cc_o_c - $(call cmd_and_fixdep,cc_o_c) - $(call cmd,checksrc) - $(call cmd,checkdoc) - $(call cmd,gen_objtooldep) - $(call cmd,gen_symversions_c) - $(call cmd,record_mcount) - $(call cmd,warn_shared_object) -endef - -define rule_as_o_S - $(call cmd_and_fixdep,as_o_S) - $(call cmd,gen_objtooldep) - $(call cmd,gen_symversions_S) - $(call cmd,warn_shared_object) -endef - # Built-in and composite module parts $(obj)/%.o: $(obj)/%.c $(recordmcount_source) FORCE $(call if_changed_rule,cc_o_c) @@ -248,17 +213,18 @@ $(obj)/%.lst: $(obj)/%.c FORCE # Compile Rust sources (.rs) # --------------------------------------------------------------------------- -rust_allowed_features := new_uninit +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree # modules case. rust_common_cmd = \ + OBJTREE=$(abspath $(objtree)) \ RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \ -Zallow-features=$(rust_allowed_features) \ -Zcrate-attr=no_std \ -Zcrate-attr='feature($(rust_allowed_features))' \ - -Zunstable-options --extern force:alloc --extern kernel \ + -Zunstable-options --extern kernel \ --crate-type rlib -L $(objtree)/rust/ \ --crate-name $(basename $(notdir $@)) \ --sysroot=/dev/null \ @@ -303,6 +269,12 @@ quiet_cmd_rustc_ll_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ $(obj)/%.ll: $(obj)/%.rs FORCE +$(call if_changed_dep,rustc_ll_rs) +quiet_cmd_rustc_rs_rs_S = RSCPP $(quiet_modtag) $@ + cmd_rustc_rs_rs_S = $(CPP) $(c_flags) -xc -C -P $< | sed '1,/^\/\/ Cut here.$$/d' >$@ + +$(obj)/%.rs: $(obj)/%.rs.S FORCE + +$(call if_changed_dep,rustc_rs_rs_S) + # Compile assembler sources (.S) # --------------------------------------------------------------------------- @@ -323,22 +295,12 @@ cmd_gensymtypes_S = \ $(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/EXPORT_SYMBOL(\1);/p' ; } | \ $(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms) -quiet_cmd_cc_symtypes_S = SYM $(quiet_modtag) $@ - cmd_cc_symtypes_S = $(call cmd_gensymtypes_S,true,$@) >/dev/null - -$(obj)/%.symtypes : $(obj)/%.S FORCE - $(call cmd,cc_symtypes_S) - - quiet_cmd_cpp_s_S = CPP $(quiet_modtag) $@ cmd_cpp_s_S = $(CPP) $(a_flags) -o $@ $< $(obj)/%.s: $(obj)/%.S FORCE $(call if_changed_dep,cpp_s_S) -quiet_cmd_as_o_S = AS $(quiet_modtag) $@ - cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool) - ifdef CONFIG_ASM_MODVERSIONS # versioning matches the C process described above, with difference that diff --git a/scripts/Makefile.clang b/scripts/Makefile.clang index 6c23c6af797fb..2435efae67f53 100644 --- a/scripts/Makefile.clang +++ b/scripts/Makefile.clang @@ -10,6 +10,7 @@ CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu CLANG_TARGET_FLAGS_riscv := riscv64-linux-gnu CLANG_TARGET_FLAGS_s390 := s390x-linux-gnu +CLANG_TARGET_FLAGS_sparc := sparc64-linux-gnu CLANG_TARGET_FLAGS_x86 := x86_64-linux-gnu CLANG_TARGET_FLAGS_um := $(CLANG_TARGET_FLAGS_$(SUBARCH)) CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(SRCARCH)) diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean index 4fcfab40ed61a..6ead00ec7313b 100644 --- a/scripts/Makefile.clean +++ b/scripts/Makefile.clean @@ -3,7 +3,7 @@ # Cleaning up # ========================================================================== -src := $(if $(VPATH),$(VPATH)/)$(obj) +src := $(srcroot)/$(obj) PHONY := __clean __clean: diff --git a/scripts/Makefile.compiler b/scripts/Makefile.compiler index e0842496d26ed..8c1029687e2e4 100644 --- a/scripts/Makefile.compiler +++ b/scripts/Makefile.compiler @@ -13,7 +13,7 @@ cc-cross-prefix = $(firstword $(foreach c, $(1), \ $(if $(shell command -v -- $(c)gcc 2>/dev/null), $(c)))) # output directory for tests below -TMPOUT = $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_$$$$ +TMPOUT = .tmp_$$$$ # try-run # Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise) diff --git a/scripts/Makefile.host b/scripts/Makefile.host index e01c13a588ddd..c1dedf646a39f 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -96,12 +96,10 @@ hostrust_flags = --out-dir $(dir $@) --emit=dep-info=$(depfile) \ $(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \ $(HOSTRUSTFLAGS_$(target-stem)) -# $(objtree)/$(obj) for including generated headers from checkin source files -ifeq ($(KBUILD_EXTMOD),) +# $(obj) for including generated headers from checkin source files ifdef building_out_of_srctree -hostc_flags += -I $(objtree)/$(obj) -hostcxx_flags += -I $(objtree)/$(obj) -endif +hostc_flags += -I $(obj) +hostcxx_flags += -I $(obj) endif ##### diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 01a9f567d5af4..7395200538da8 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -191,15 +191,33 @@ _c_flags += $(if $(patsubst n%,, \ -D__KCSAN_INSTRUMENT_BARRIERS__) endif +# +# Enable AutoFDO build flags except some files or directories we don't want to +# enable (depends on variables AUTOFDO_PROFILE_obj.o and AUTOFDO_PROFILE). +# +ifeq ($(CONFIG_AUTOFDO_CLANG),y) +_c_flags += $(if $(patsubst n%,, \ + $(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(is-kernel-object)), \ + $(CFLAGS_AUTOFDO_CLANG)) +endif + +# +# Enable Propeller build flags except some files or directories we don't want to +# enable (depends on variables AUTOFDO_PROPELLER_obj.o and PROPELLER_PROFILE). +# +ifdef CONFIG_PROPELLER_CLANG +_c_flags += $(if $(patsubst n%,, \ + $(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(PROPELLER_PROFILE))$(is-kernel-object), \ + $(CFLAGS_PROPELLER_CLANG)) +endif + # $(src) for including checkin headers from generated source files # $(obj) for including generated headers from checkin source files -ifeq ($(KBUILD_EXTMOD),) ifdef building_out_of_srctree _c_flags += $(addprefix -I, $(src) $(obj)) _a_flags += $(addprefix -I, $(src) $(obj)) _cpp_flags += $(addprefix -I, $(src) $(obj)) endif -endif # If $(is-kernel-object) is 'y', this object will be linked to vmlinux or modules is-kernel-object = $(or $(part-of-builtin),$(part-of-module)) @@ -280,6 +298,42 @@ $(foreach m, $1, \ $(addprefix $(obj)/, $(call suffix-search, $(patsubst $(obj)/%,%,$m), $2, $3)))) endef +# Build commands +# =========================================================================== +# These are shared by some Makefile.* files. + +objtool-enabled := y + +ifdef CONFIG_LTO_CLANG +# objtool cannot process LLVM IR. Make $(LD) covert LLVM IR to ELF here. +cmd_ld_single = $(if $(objtool-enabled), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@) +endif + +quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ + cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \ + $(cmd_ld_single) \ + $(cmd_objtool) + +define rule_cc_o_c + $(call cmd_and_fixdep,cc_o_c) + $(call cmd,checksrc) + $(call cmd,checkdoc) + $(call cmd,gen_objtooldep) + $(call cmd,gen_symversions_c) + $(call cmd,record_mcount) + $(call cmd,warn_shared_object) +endef + +quiet_cmd_as_o_S = AS $(quiet_modtag) $@ + cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool) + +define rule_as_o_S + $(call cmd_and_fixdep,as_o_S) + $(call cmd,gen_objtooldep) + $(call cmd,gen_symversions_S) + $(call cmd,warn_shared_object) +endef + # Copy a file # =========================================================================== # 'cp' preserves permissions. If you use it to copy a file in read-only srctree, @@ -371,10 +425,10 @@ quiet_cmd_lzo_with_size = LZO $@ cmd_lzo_with_size = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@ quiet_cmd_lz4 = LZ4 $@ - cmd_lz4 = cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout > $@ + cmd_lz4 = cat $(real-prereqs) | $(LZ4) -l -9 - - > $@ quiet_cmd_lz4_with_size = LZ4 $@ - cmd_lz4_with_size = { cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout; \ + cmd_lz4_with_size = { cat $(real-prereqs) | $(LZ4) -l -9 - -; \ $(size_append); } > $@ # U-Boot mkimage diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal index 1482884ec3ca8..542ba462ed3ec 100644 --- a/scripts/Makefile.modfinal +++ b/scripts/Makefile.modfinal @@ -6,14 +6,12 @@ PHONY := __modfinal __modfinal: -include include/config/auto.conf +include $(objtree)/include/config/auto.conf include $(srctree)/scripts/Kbuild.include - -# for c_flags include $(srctree)/scripts/Makefile.lib # find all modules listed in modules.order -modules := $(call read-file, $(MODORDER)) +modules := $(call read-file, modules.order) __modfinal: $(modules:%.o=%.ko) @: @@ -22,30 +20,27 @@ __modfinal: $(modules:%.o=%.ko) modname = $(notdir $(@:.mod.o=)) part-of-module = y GCOV_PROFILE := n -KCSAN_SANITIZE := n - -quiet_cmd_cc_o_c = CC [M] $@ - cmd_cc_o_c = $(CC) $(filter-out $(CC_FLAGS_CFI), $(c_flags)) -c -o $@ $< +ccflags-remove-y := $(CC_FLAGS_CFI) %.mod.o: %.mod.c FORCE - $(call if_changed_dep,cc_o_c) + $(call if_changed_rule,cc_o_c) -$(extmod_prefix).module-common.o: $(srctree)/scripts/module-common.c FORCE - $(call if_changed_dep,cc_o_c) +.module-common.o: $(srctree)/scripts/module-common.c FORCE + $(call if_changed_rule,cc_o_c) quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = \ $(LD) -r $(KBUILD_LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ - -T scripts/module.lds -o $@ $(filter %.o, $^) + -T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^) quiet_cmd_btf_ko = BTF [M] $@ cmd_btf_ko = \ - if [ ! -f vmlinux ]; then \ + if [ ! -f $(objtree)/vmlinux ]; then \ printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \ else \ - LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base vmlinux $@; \ - $(RESOLVE_BTFIDS) -b vmlinux $@; \ + LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \ + $(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@; \ fi; # Same as newer-prereqs, but allows to exclude specified extra dependencies @@ -57,13 +52,13 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \ printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:) # Re-generate module BTFs if either module's .ko or vmlinux changed -%.ko: %.o %.mod.o $(extmod_prefix).module-common.o scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE - +$(call if_changed_except,ld_ko_o,vmlinux) +%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE + +$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux) ifdef CONFIG_DEBUG_INFO_BTF_MODULES +$(if $(newer-prereqs),$(call cmd,btf_ko)) endif -targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) $(extmod_prefix).module-common.o +targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o # Add FORCE to the prerequisites of a target to force it to be always rebuilt. # --------------------------------------------------------------------------- diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index d977209431898..f97c9926ed31b 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -6,7 +6,7 @@ PHONY := __modinst __modinst: -include include/config/auto.conf +include $(objtree)/include/config/auto.conf include $(srctree)/scripts/Kbuild.include install-y := @@ -40,7 +40,7 @@ $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin. endif -modules := $(call read-file, $(MODORDER)) +modules := $(call read-file, modules.order) ifeq ($(KBUILD_EXTMOD),) dst := $(MODLIB)/kernel @@ -59,7 +59,7 @@ suffix-$(CONFIG_MODULE_COMPRESS_XZ) := .xz suffix-$(CONFIG_MODULE_COMPRESS_ZSTD) := .zst endif -modules := $(patsubst $(extmod_prefix)%.o, $(dst)/%.ko$(suffix-y), $(modules)) +modules := $(patsubst %.o, $(dst)/%.ko$(suffix-y), $(modules)) install-$(CONFIG_MODULES) += $(modules) __modinst: $(install-y) @@ -119,7 +119,7 @@ endif # Create necessary directories $(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir))) -$(dst)/%.ko: $(extmod_prefix)%.ko FORCE +$(dst)/%.ko: %.ko FORCE $(call cmd,install) $(call cmd,strip) $(call cmd,sign) diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 44936ebad161e..ab0e94ea62496 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -35,10 +35,10 @@ PHONY := __modpost __modpost: -include include/config/auto.conf +include $(objtree)/include/config/auto.conf include $(srctree)/scripts/Kbuild.include -MODPOST = scripts/mod/modpost +MODPOST = $(objtree)/scripts/mod/modpost modpost-args = \ $(if $(CONFIG_MODULES),-M) \ @@ -46,7 +46,7 @@ modpost-args = \ $(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \ $(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \ $(if $(KBUILD_MODPOST_WARN),-w) \ - $(if $(KBUILD_NSDEPS),-d $(MODULES_NSDEPS)) \ + $(if $(KBUILD_NSDEPS),-d modules.nsdeps) \ $(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-N) \ $(if $(findstring 1, $(KBUILD_EXTRA_WARN)),-W) \ -o $@ @@ -61,8 +61,8 @@ endif # Read out modules.order to pass in modpost. # Otherwise, allmodconfig would fail with "Argument list too long". ifdef KBUILD_MODULES -modpost-args += -T $(MODORDER) -modpost-deps += $(MODORDER) +modpost-args += -T modules.order +modpost-deps += modules.order endif ifeq ($(KBUILD_EXTMOD),) @@ -111,19 +111,19 @@ endif else # set src + obj - they may be used in the modules's Makefile -obj := $(KBUILD_EXTMOD) -src := $(if $(VPATH),$(VPATH)/)$(obj) +obj := . +src := $(srcroot) # Include the module's Makefile to find KBUILD_EXTRA_SYMBOLS include $(kbuild-file) -output-symdump := $(KBUILD_EXTMOD)/Module.symvers +output-symdump := Module.symvers -ifeq ($(wildcard Module.symvers),) -missing-input := Module.symvers +ifeq ($(wildcard $(objtree)/Module.symvers),) +missing-input := $(objtree)/Module.symvers else -modpost-args += -i Module.symvers -modpost-deps += Module.symvers +modpost-args += -i $(objtree)/Module.symvers +modpost-deps += $(objtree)/Module.symvers endif modpost-args += -e $(addprefix -i , $(KBUILD_EXTRA_SYMBOLS)) diff --git a/scripts/Makefile.propeller b/scripts/Makefile.propeller new file mode 100644 index 0000000000000..48a660128e256 --- /dev/null +++ b/scripts/Makefile.propeller @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Enable available and selected Clang Propeller features. +ifdef CLANG_PROPELLER_PROFILE_PREFIX + CFLAGS_PROPELLER_CLANG := -fbasic-block-sections=list=$(CLANG_PROPELLER_PROFILE_PREFIX)_cc_profile.txt -ffunction-sections + KBUILD_LDFLAGS += --symbol-ordering-file=$(CLANG_PROPELLER_PROFILE_PREFIX)_ld_profile.txt --no-warn-symbol-ordering +else + # Starting with Clang v20, the '-fbasic-block-sections=labels' option is + # deprecated. Use the recommended '-fbasic-block-address-map' option. + # Link: https://github.com/llvm/llvm-project/pull/110039 + ifeq ($(call clang-min-version, 200000),y) + CFLAGS_PROPELLER_CLANG := -fbasic-block-address-map + else + CFLAGS_PROPELLER_CLANG := -fbasic-block-sections=labels + endif +endif + +# Propeller requires debug information to embed module names in the profiles. +# If CONFIG_DEBUG_INFO is not enabled, set -gmlt option. Skip this for AutoFDO, +# as the option should already be set. +ifndef CONFIG_DEBUG_INFO + ifndef CONFIG_AUTOFDO_CLANG + CFLAGS_PROPELLER_CLANG += -gmlt + endif +endif + +ifdef CONFIG_LTO_CLANG_THIN + ifdef CLANG_PROPELLER_PROFILE_PREFIX + KBUILD_LDFLAGS += --lto-basic-block-sections=$(CLANG_PROPELLER_PROFILE_PREFIX)_cc_profile.txt + else + ifeq ($(call test-ge, $(CONFIG_LLD_VERSION), 200000),y) + KBUILD_LDFLAGS += --lto-basic-block-address-map + else + KBUILD_LDFLAGS += --lto-basic-block-sections=labels + endif + endif +endif + +export CFLAGS_PROPELLER_CLANG diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index dddad554e9127..873caaa553134 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -5,17 +5,53 @@ __default: vmlinux include include/config/auto.conf include $(srctree)/scripts/Kbuild.include - -# for c_flags include $(srctree)/scripts/Makefile.lib targets := -quiet_cmd_cc_o_c = CC $@ - cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< - %.o: %.c FORCE - $(call if_changed_dep,cc_o_c) + $(call if_changed_rule,cc_o_c) + +%.o: %.S FORCE + $(call if_changed_rule,as_o_S) + +# Built-in dtb +# --------------------------------------------------------------------------- + +quiet_cmd_wrap_dtbs = WRAP $@ + cmd_wrap_dtbs = { \ + echo '\#include '; \ + echo '.section .dtb.init.rodata,"a"'; \ + while read dtb; do \ + symbase=__dtb_$$(basename -s .dtb "$${dtb}" | tr - _); \ + echo '.balign STRUCT_ALIGNMENT'; \ + echo ".global $${symbase}_begin"; \ + echo "$${symbase}_begin:"; \ + echo '.incbin "'$$dtb'" '; \ + echo ".global $${symbase}_end"; \ + echo "$${symbase}_end:"; \ + done < $<; \ + } > $@ + +.builtin-dtbs.S: .builtin-dtbs-list FORCE + $(call if_changed,wrap_dtbs) + +quiet_cmd_gen_dtbs_list = GEN $@ + cmd_gen_dtbs_list = \ + $(if $(CONFIG_BUILTIN_DTB_NAME), echo "arch/$(SRCARCH)/boot/dts/$(CONFIG_BUILTIN_DTB_NAME).dtb",:) > $@ + +.builtin-dtbs-list: arch/$(SRCARCH)/boot/dts/dtbs-list FORCE + $(call if_changed,$(if $(CONFIG_BUILTIN_DTB_ALL),copy,gen_dtbs_list)) + +targets += .builtin-dtbs-list + +ifdef CONFIG_GENERIC_BUILTIN_DTB +targets += .builtin-dtbs.S .builtin-dtbs.o +vmlinux: .builtin-dtbs.o +endif + +# vmlinux +# --------------------------------------------------------------------------- ifdef CONFIG_MODULES targets += .vmlinux.export.o @@ -39,6 +75,9 @@ cmd_link_vmlinux = \ targets += vmlinux vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE +$(call if_changed_dep,link_vmlinux) +ifdef CONFIG_DEBUG_INFO_BTF +vmlinux: $(RESOLVE_BTFIDS) +endif # module.builtin.ranges # --------------------------------------------------------------------------- diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 98790fe5115d8..9eed3683ad76c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3209,36 +3209,31 @@ sub process { # Check Fixes: styles is correct if (!$in_header_lines && - $line =~ /^\s*fixes:?\s*(?:commit\s*)?[0-9a-f]{5,}\b/i) { - my $orig_commit = ""; - my $id = "0123456789ab"; - my $title = "commit title"; - my $tag_case = 1; - my $tag_space = 1; - my $id_length = 1; - my $id_case = 1; + $line =~ /^\s*(fixes:?)\s*(?:commit\s*)?([0-9a-f]{5,40})(?:\s*($balanced_parens))?/i) { + my $tag = $1; + my $orig_commit = $2; + my $title; my $title_has_quotes = 0; $fixes_tag = 1; - - if ($line =~ /(\s*fixes:?)\s+([0-9a-f]{5,})\s+($balanced_parens)/i) { - my $tag = $1; - $orig_commit = $2; - $title = $3; - - $tag_case = 0 if $tag eq "Fixes:"; - $tag_space = 0 if ($line =~ /^fixes:? [0-9a-f]{5,} ($balanced_parens)/i); - - $id_length = 0 if ($orig_commit =~ /^[0-9a-f]{12}$/i); - $id_case = 0 if ($orig_commit !~ /[A-F]/); - + if (defined $3) { # Always strip leading/trailing parens then double quotes if existing - $title = substr($title, 1, -1); + $title = substr($3, 1, -1); if ($title =~ /^".*"$/) { $title = substr($title, 1, -1); $title_has_quotes = 1; } + } else { + $title = "commit title" } + + my $tag_case = not ($tag eq "Fixes:"); + my $tag_space = not ($line =~ /^fixes:? [0-9a-f]{5,40} ($balanced_parens)/i); + + my $id_length = not ($orig_commit =~ /^[0-9a-f]{12}$/i); + my $id_case = not ($orig_commit !~ /[A-F]/); + + my $id = "0123456789ab"; my ($cid, $ctitle) = git_commit_info($orig_commit, $id, $title); diff --git a/scripts/coccicheck b/scripts/coccicheck index e52cb43fede60..0e6bc5a10320c 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -80,11 +80,7 @@ command results in a shift count error.' NPROC=1 else ONLINE=0 - if [ "$KBUILD_EXTMOD" = "" ] ; then - OPTIONS="--dir $srctree $COCCIINCLUDE" - else - OPTIONS="--dir $KBUILD_EXTMOD $COCCIINCLUDE" - fi + OPTIONS="--dir $srcroot $COCCIINCLUDE" # Use only one thread per core by default if hyperthreading is enabled THREADS_PER_CORE=$(LANG=C lscpu | grep "Thread(s) per core: " | tr -cd "[:digit:]") diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index 826836d264c6a..46fa18b80fc1b 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -311,7 +311,12 @@ handle_line() { parse_symbol # modifies $symbol # Add up the line number to the symbol - echo "${words[@]}" "$symbol $module" + if [[ -z ${module} ]] + then + echo "${words[@]}" "$symbol" + else + echo "${words[@]}" "$symbol $module" + fi } while read line; do diff --git a/scripts/depmod.sh b/scripts/depmod.sh index e22da27fe13eb..3c34fecacbc82 100755 --- a/scripts/depmod.sh +++ b/scripts/depmod.sh @@ -12,7 +12,7 @@ KERNELRELEASE=$1 : ${DEPMOD:=depmod} -if ! test -r System.map ; then +if ! test -r "${objtree}/System.map" ; then echo "Warning: modules_install: missing 'System.map' file. Skipping depmod." >&2 exit 0 fi @@ -25,7 +25,7 @@ if [ -z $(command -v $DEPMOD) ]; then exit 0 fi -set -- -ae -F System.map +set -- -ae -F "${objtree}/System.map" if test -n "$INSTALL_MOD_PATH"; then set -- "$@" -b "$INSTALL_MOD_PATH" fi diff --git a/scripts/export_report.pl b/scripts/export_report.pl deleted file mode 100755 index feb3d5542a62d..0000000000000 --- a/scripts/export_report.pl +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-only -# -# (C) Copyright IBM Corporation 2006. -# Author : Ram Pai (linuxram@us.ibm.com) -# -# Usage: export_report.pl -k Module.symvers [-o report_file ] -f *.mod.c -# - -use warnings; -use Getopt::Std; -use strict; - -sub numerically { - my $no1 = (split /\s+/, $a)[1]; - my $no2 = (split /\s+/, $b)[1]; - return $no1 <=> $no2; -} - -sub alphabetically { - my ($module1, $value1) = @{$a}; - my ($module2, $value2) = @{$b}; - return $value1 <=> $value2 || $module2 cmp $module1; -} - -sub print_depends_on { - my ($href) = @_; - print "\n"; - for my $mod (sort keys %$href) { - my $list = $href->{$mod}; - print "\t$mod:\n"; - foreach my $sym (sort numerically @{$list}) { - my ($symbol, $no) = split /\s+/, $sym; - printf("\t\t%-25s\n", $symbol); - } - print "\n"; - } - print "\n"; - print "~"x80 , "\n"; -} - -sub usage { - print "Usage: @_ -h -k Module.symvers [ -o outputfile ] \n", - "\t-f: treat all the non-option argument as .mod.c files. ", - "Recommend using this as the last option\n", - "\t-h: print detailed help\n", - "\t-k: the path to Module.symvers file. By default uses ", - "the file from the current directory\n", - "\t-o outputfile: output the report to outputfile\n"; - exit 0; -} - -sub collectcfiles { - my @file; - open my $fh, '< modules.order' or die "cannot open modules.order: $!\n"; - while (<$fh>) { - s/\.ko$/.mod.c/; - push (@file, $_) - } - close($fh); - chomp @file; - return @file; -} - -my (%SYMBOL, %MODULE, %opt, @allcfiles); - -if (not getopts('hk:o:f',\%opt) or defined $opt{'h'}) { - usage($0); -} - -if (defined $opt{'f'}) { - @allcfiles = @ARGV; -} else { - @allcfiles = collectcfiles(); -} - -if (not defined $opt{'k'}) { - $opt{'k'} = "Module.symvers"; -} - -open (my $module_symvers, '<', $opt{'k'}) - or die "Sorry, cannot open $opt{'k'}: $!\n"; - -if (defined $opt{'o'}) { - open (my $out, '>', $opt{'o'}) - or die "Sorry, cannot open $opt{'o'} $!\n"; - - select $out; -} - -# -# collect all the symbols and their attributes from the -# Module.symvers file -# -while ( <$module_symvers> ) { - chomp; - my (undef, $symbol, $module, $gpl, $namespace) = split('\t'); - $SYMBOL { $symbol } = [ $module , "0" , $symbol, $gpl]; -} -close($module_symvers); - -# -# collect the usage count of each symbol. -# -my $modversion_warnings = 0; - -foreach my $thismod (@allcfiles) { - my $module; - - unless (open ($module, '<', $thismod)) { - warn "Sorry, cannot open $thismod: $!\n"; - next; - } - - my $state=0; - while ( <$module> ) { - chomp; - if ($state == 0) { - $state = 1 if ($_ =~ /static const struct modversion_info/); - next; - } - if ($state == 1) { - $state = 2 if ($_ =~ /__attribute__\(\(section\("__versions"\)\)\)/); - next; - } - if ($state == 2) { - if ( $_ !~ /0x[0-9a-f]+,/ ) { - next; - } - my $sym = (split /([,"])/,)[4]; - my ($module, $value, $symbol, $gpl) = @{$SYMBOL{$sym}}; - $SYMBOL{ $sym } = [ $module, $value+1, $symbol, $gpl]; - push(@{$MODULE{$thismod}} , $sym); - } - } - if ($state != 2) { - warn "WARNING:$thismod is not built with CONFIG_MODVERSIONS enabled\n"; - $modversion_warnings++; - } - close($module); -} - -print "\tThis file reports the exported symbols usage patterns by in-tree\n", - "\t\t\t\tmodules\n"; -printf("%s\n\n\n","x"x80); -printf("\t\t\t\tINDEX\n\n\n"); -printf("SECTION 1: Usage counts of all exported symbols\n"); -printf("SECTION 2: List of modules and the exported symbols they use\n"); -printf("%s\n\n\n","x"x80); -printf("SECTION 1:\tThe exported symbols and their usage count\n\n"); -printf("%-25s\t%-25s\t%-5s\t%-25s\n", "Symbol", "Module", "Usage count", - "export type"); - -# -# print the list of unused exported symbols -# -foreach my $list (sort alphabetically values(%SYMBOL)) { - my ($module, $value, $symbol, $gpl) = @{$list}; - printf("%-25s\t%-25s\t%-10s\t", $symbol, $module, $value); - if (defined $gpl) { - printf("%-25s\n",$gpl); - } else { - printf("\n"); - } -} -printf("%s\n\n\n","x"x80); - -printf("SECTION 2:\n\tThis section reports export-symbol-usage of in-kernel -modules. Each module lists the modules, and the symbols from that module that -it uses. Each listed symbol reports the number of modules using it\n"); - -print "\nNOTE: Got $modversion_warnings CONFIG_MODVERSIONS warnings\n\n" - if $modversion_warnings; - -print "~"x80 , "\n"; -for my $thismod (sort keys %MODULE) { - my $list = $MODULE{$thismod}; - my %depends; - $thismod =~ s/\.mod\.c/.ko/; - print "\t\t\t$thismod\n"; - foreach my $symbol (@{$list}) { - my ($module, $value, undef, $gpl) = @{$SYMBOL{$symbol}}; - push (@{$depends{"$module"}}, "$symbol $value"); - } - print_depends_on(\%depends); -} diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py index 298dfcc25eaea..fa15f872ddbe8 100644 --- a/scripts/gdb/linux/modules.py +++ b/scripts/gdb/linux/modules.py @@ -19,6 +19,9 @@ module_type = utils.CachedType("struct module") +def has_modules(): + return utils.gdb_eval_or_none("modules") is not None + def module_list(): global module_type modules = utils.gdb_eval_or_none("modules") diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index e8316beb17a71..f6c1b063775a7 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -178,6 +178,9 @@ def invoke(self, arg, from_tty): self.load_all_symbols() + if not modules.has_modules(): + return + if hasattr(gdb, 'Breakpoint'): if self.breakpoint is not None: self.breakpoint.delete() diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index d2bc63cde8c6a..09e1d166d8d23 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -64,13 +64,6 @@ def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=Tr [], ) - append_crate( - "alloc", - sysroot_src / "alloc" / "src" / "lib.rs", - ["core", "compiler_builtins"], - cfg=crates_cfgs.get("alloc", []), - ) - append_crate( "macros", srctree / "rust" / "macros" / "lib.rs", @@ -96,7 +89,7 @@ def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=Tr append_crate( "kernel", srctree / "rust" / "kernel" / "lib.rs", - ["core", "alloc", "macros", "build_error", "bindings"], + ["core", "macros", "build_error", "bindings"], cfg=cfg, ) crates[-1]["source"] = { @@ -133,7 +126,7 @@ def is_root_crate(build_file, target): append_crate( name, path, - ["core", "alloc", "kernel"], + ["core", "kernel"], cfg=cfg, ) diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index f3901c55df239..07f9b8cfb2337 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c @@ -632,54 +632,55 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) void export_symbol(const char *name) { struct symbol *sym; + unsigned long crc; + int has_changed = 0; sym = find_symbol(name, SYM_NORMAL, 0); - if (!sym) + if (!sym) { error_with_pos("export undefined symbol %s", name); - else { - unsigned long crc; - int has_changed = 0; + return; + } - if (flag_dump_defs) - fprintf(debugfile, "Export %s == <", name); + if (flag_dump_defs) + fprintf(debugfile, "Export %s == <", name); - expansion_trail = (struct symbol *)-1L; + expansion_trail = (struct symbol *)-1L; - sym->expansion_trail = expansion_trail; - expansion_trail = sym; - crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; + sym->expansion_trail = expansion_trail; + expansion_trail = sym; + crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; - sym = expansion_trail; - while (sym != (struct symbol *)-1L) { - struct symbol *n = sym->expansion_trail; + sym = expansion_trail; + while (sym != (struct symbol *)-1L) { + struct symbol *n = sym->expansion_trail; - if (sym->status != STATUS_UNCHANGED) { - if (!has_changed) { - print_location(); - fprintf(stderr, "%s: %s: modversion " - "changed because of changes " - "in ", flag_preserve ? "error" : - "warning", name); - } else - fprintf(stderr, ", "); - print_type_name(sym->type, sym->name); - if (sym->status == STATUS_DEFINED) - fprintf(stderr, " (became defined)"); - has_changed = 1; - if (flag_preserve) - errors++; + if (sym->status != STATUS_UNCHANGED) { + if (!has_changed) { + print_location(); + fprintf(stderr, + "%s: %s: modversion changed because of changes in ", + flag_preserve ? "error" : "warning", + name); + } else { + fprintf(stderr, ", "); } - sym->expansion_trail = 0; - sym = n; + print_type_name(sym->type, sym->name); + if (sym->status == STATUS_DEFINED) + fprintf(stderr, " (became defined)"); + has_changed = 1; + if (flag_preserve) + errors++; } - if (has_changed) - fprintf(stderr, "\n"); + sym->expansion_trail = 0; + sym = n; + } + if (has_changed) + fprintf(stderr, "\n"); - if (flag_dump_defs) - fputs(">\n", debugfile); + if (flag_dump_defs) + fputs(">\n", debugfile); - printf("#SYMVER %s 0x%08lx\n", name, crc); - } + printf("#SYMVER %s 0x%08lx\n", name, crc); } /*----------------------------------------------------------------------*/ diff --git a/scripts/head-object-list.txt b/scripts/head-object-list.txt index fd5d00bac447d..f12b4a7b8406b 100644 --- a/scripts/head-object-list.txt +++ b/scripts/head-object-list.txt @@ -23,7 +23,6 @@ arch/m68k/coldfire/head.o arch/m68k/kernel/head.o arch/m68k/kernel/sun3-head.o arch/microblaze/kernel/head.o -arch/mips/kernel/head.o arch/nios2/kernel/head.o arch/openrisc/kernel/head.o arch/parisc/kernel/head.o diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 3d7d454c54da3..8abe570419552 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -628,7 +628,7 @@ static const struct option long_opts[] = { static void conf_usage(const char *progname) { - printf("Usage: %s [options] \n", progname); + printf("Usage: %s [options] kconfig_file\n", progname); printf("\n"); printf("Generic options:\n"); printf(" -h, --help Print this message and exit.\n"); @@ -653,6 +653,9 @@ static void conf_usage(const char *progname) printf(" --mod2yesconfig Change answers from mod to yes if possible\n"); printf(" --mod2noconfig Change answers from mod to no if possible\n"); printf(" (If none of the above is given, --oldaskconfig is the default)\n"); + printf("\n"); + printf("Arguments:\n"); + printf(" kconfig_file Top-level Kconfig file.\n"); } int main(int ac, char **av) diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index 63519cd24bc7f..8914b4e8f2a81 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -34,6 +34,7 @@ bool sym_string_valid(struct symbol *sym, const char *newval); bool sym_string_within_range(struct symbol *sym, const char *str); bool sym_set_string_value(struct symbol *sym, const char *newval); bool sym_is_changeable(const struct symbol *sym); +struct menu *sym_get_prompt_menu(const struct symbol *sym); struct menu *sym_get_choice_menu(const struct symbol *sym); const char * sym_get_string_value(struct symbol *sym); diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 063b4f7ccbdb3..c0b2dabf6c894 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -467,7 +467,7 @@ static void handle_f9(int *key, struct menu *current_item) return; } -/* return != 0 to indicate the key was handles */ +/* return != 0 to indicate the key was handled */ static int process_special_keys(int *key, struct menu *menu) { int i; diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 72b605efe549d..4bfdf8ac2a9a3 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -277,6 +277,15 @@ int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...) case KEY_RIGHT: menu_driver(menu, REQ_RIGHT_ITEM); break; + case 9: /* TAB */ + if (btn_num > 1) { + /* cycle through buttons */ + if (item_index(current_item(menu)) == btn_num - 1) + menu_driver(menu, REQ_FIRST_ITEM); + else + menu_driver(menu, REQ_NEXT_ITEM); + } + break; case 10: /* ENTER */ case 27: /* ESCAPE */ case ' ': diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index bc43fb67c7c41..68372d3ff3253 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -24,7 +24,6 @@ int cdebug = PRINTD; static void yyerror(const char *err); -static void zconfprint(const char *err, ...); static void zconf_error(const char *err, ...); static bool zconf_endtoken(const char *tokenname, const char *expected_tokenname); @@ -183,7 +182,7 @@ menuconfig_stmt: menuconfig_entry_start config_option_list if (current_entry->prompt) current_entry->prompt->type = P_MENU; else - zconfprint("warning: menuconfig statement without prompt"); + zconf_error("menuconfig statement without prompt"); printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); }; @@ -293,12 +292,6 @@ choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); }; -choice_option: T_BOOL T_WORD_QUOTE if_expr T_EOL -{ - menu_add_prompt(P_PROMPT, $2, $3); - printd(DEBUG_PARSE, "%s:%d:bool\n", cur_filename, cur_lineno); -}; - choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL { menu_add_symbol(P_DEFAULT, $2, $3); @@ -408,14 +401,14 @@ help: help_start T_HELPTEXT { if (current_entry->help) { free(current_entry->help); - zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used", - current_entry->sym->name ?: ""); + zconf_error("'%s' defined with more than one help text", + current_entry->sym->name ?: ""); } /* Is the help text empty or all whitespace? */ if ($2[strspn($2, " \f\n\r\t\v")] == '\0') - zconfprint("warning: '%s' defined with blank help text", - current_entry->sym->name ?: ""); + zconf_error("'%s' defined with blank help text", + current_entry->sym->name ?: ""); current_entry->help = $2; }; @@ -598,17 +591,6 @@ static bool zconf_endtoken(const char *tokenname, return true; } -static void zconfprint(const char *err, ...) -{ - va_list ap; - - fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); - va_start(ap, err); - vfprintf(stderr, err, ap); - va_end(ap); - fprintf(stderr, "\n"); -} - static void zconf_error(const char *err, ...) { va_list ap; diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index e260cab1c2aff..6c92ef1e16efb 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -110,7 +110,7 @@ void ConfigItem::updateMenu(void) if (prop) switch (prop->type) { case P_MENU: - if (list->mode == singleMode || list->mode == symbolMode) { + if (list->mode == singleMode) { /* a menuconfig entry is displayed differently * depending whether it's at the view root or a child. */ @@ -159,7 +159,7 @@ void ConfigItem::updateMenu(void) ch = 'M'; break; default: - if (sym_is_choice_value(sym) && type == S_BOOLEAN) + if (sym_is_choice_value(sym)) setIcon(promptColIdx, choiceNoIcon); else setIcon(promptColIdx, symbolNoIcon); @@ -175,17 +175,16 @@ void ConfigItem::updateMenu(void) setText(dataColIdx, sym_get_string_value(sym)); break; } - if (!sym_has_value(sym) && visible) + if (!sym_has_value(sym)) prompt += " (NEW)"; set_prompt: setText(promptColIdx, prompt); } -void ConfigItem::testUpdateMenu(bool v) +void ConfigItem::testUpdateMenu(void) { ConfigItem* i; - visible = v; if (!menu) return; @@ -307,7 +306,6 @@ ConfigList::ConfigList(QWidget *parent, const char *name) { setObjectName(name); setSortingEnabled(false); - setRootIsDecorated(true); setVerticalScrollMode(ScrollPerPixel); setHorizontalScrollMode(ScrollPerPixel); @@ -430,27 +428,26 @@ void ConfigList::updateList() item = (ConfigItem*)(*it); if (!item->menu) continue; - item->testUpdateMenu(menu_is_visible(item->menu)); + item->testUpdateMenu(); ++it; } return; } - if (rootEntry != &rootmenu && (mode == singleMode || - (mode == symbolMode && rootEntry->parent != &rootmenu))) { + if (rootEntry != &rootmenu && mode == singleMode) { item = (ConfigItem *)topLevelItem(0); if (!item) - item = new ConfigItem(this, 0, true); + item = new ConfigItem(this, 0); last = item; } if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) && rootEntry->sym && rootEntry->prompt) { item = last ? last->nextSibling() : nullptr; if (!item) - item = new ConfigItem(this, last, rootEntry, true); + item = new ConfigItem(this, last, rootEntry); else - item->testUpdateMenu(true); + item->testUpdateMenu(); updateMenuList(item, rootEntry); update(); @@ -599,7 +596,6 @@ void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu) struct menu* child; ConfigItem* item; ConfigItem* last; - bool visible; enum prop_type type; if (!menu) { @@ -631,14 +627,13 @@ void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu) break; } - visible = menu_is_visible(child); if (!menuSkip(child)) { if (!child->sym && !child->list && !child->prompt) continue; if (!item || item->menu != child) - item = new ConfigItem(parent, last, child, visible); + item = new ConfigItem(parent, last, child); else - item->testUpdateMenu(visible); + item->testUpdateMenu(); if (mode == fullMode || mode == menuMode || type != P_MENU) updateMenuList(item, child); @@ -664,7 +659,6 @@ void ConfigList::updateMenuList(struct menu *menu) struct menu* child; ConfigItem* item; ConfigItem* last; - bool visible; enum prop_type type; if (!menu) { @@ -696,14 +690,13 @@ void ConfigList::updateMenuList(struct menu *menu) break; } - visible = menu_is_visible(child); if (!menuSkip(child)) { if (!child->sym && !child->list && !child->prompt) continue; if (!item || item->menu != child) - item = new ConfigItem(this, last, child, visible); + item = new ConfigItem(this, last, child); else - item->testUpdateMenu(visible); + item->testUpdateMenu(); if (mode == fullMode || mode == menuMode || type != P_MENU) updateMenuList(item, child); @@ -731,7 +724,7 @@ void ConfigList::keyPressEvent(QKeyEvent* ev) struct menu *menu; enum prop_type type; - if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) { + if (ev->key() == Qt::Key_Escape && mode == singleMode) { emit parentSelected(); ev->accept(); return; @@ -781,13 +774,6 @@ void ConfigList::keyPressEvent(QKeyEvent* ev) ev->accept(); } -void ConfigList::mousePressEvent(QMouseEvent* e) -{ - //QPoint p(contentsToViewport(e->pos())); - //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y()); - Parent::mousePressEvent(e); -} - void ConfigList::mouseReleaseEvent(QMouseEvent* e) { QPoint p = e->pos(); @@ -834,13 +820,6 @@ void ConfigList::mouseReleaseEvent(QMouseEvent* e) Parent::mouseReleaseEvent(e); } -void ConfigList::mouseMoveEvent(QMouseEvent* e) -{ - //QPoint p(contentsToViewport(e->pos())); - //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y()); - Parent::mouseMoveEvent(e); -} - void ConfigList::mouseDoubleClickEvent(QMouseEvent* e) { QPoint p = e->pos(); @@ -1022,7 +1001,7 @@ void ConfigInfoView::menuInfo(void) if (sym->name) { stream << " ("; if (showDebug()) - stream << "name << "\">"; + stream << "name << "\">"; stream << print_filter(sym->name); if (showDebug()) stream << ""; @@ -1031,7 +1010,7 @@ void ConfigInfoView::menuInfo(void) } else if (sym->name) { stream << ""; if (showDebug()) - stream << "name << "\">"; + stream << "name << "\">"; stream << print_filter(sym->name); if (showDebug()) stream << ""; @@ -1086,9 +1065,9 @@ QString ConfigInfoView::debug_info(struct symbol *sym) switch (prop->type) { case P_PROMPT: case P_MENU: - stream << "prompt: name << "\">"; + stream << "prompt: "; stream << print_filter(prop->text); - stream << "
"; + stream << "
"; break; case P_DEFAULT: case P_SELECT: @@ -1122,28 +1101,19 @@ QString ConfigInfoView::print_filter(const QString &str) { QRegularExpression re("[<>&\"\\n]"); QString res = str; + + QHash patterns; + patterns['<'] = "<"; + patterns['>'] = ">"; + patterns['&'] = "&"; + patterns['"'] = """; + patterns['\n'] = "
"; + for (int i = 0; (i = res.indexOf(re, i)) >= 0;) { - switch (res[i].toLatin1()) { - case '<': - res.replace(i, 1, "<"); - i += 4; - break; - case '>': - res.replace(i, 1, ">"); - i += 4; - break; - case '&': - res.replace(i, 1, "&"); - i += 5; - break; - case '"': - res.replace(i, 1, """); - i += 6; - break; - case '\n': - res.replace(i, 1, "
"); - i += 4; - break; + const QString n = patterns.value(res[i], QString()); + if (!n.isEmpty()) { + res.replace(i, 1, n); + i += n.length(); } } return res; @@ -1154,7 +1124,7 @@ void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char QTextStream *stream = reinterpret_cast(data); if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) { - *stream << "name << "\">"; + *stream << "name << "\">"; *stream << print_filter(str); *stream << ""; } else { @@ -1164,39 +1134,11 @@ void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char void ConfigInfoView::clicked(const QUrl &url) { - QByteArray str = url.toEncoded(); - const std::size_t count = str.size(); - char *data = new char[count + 2]; // '$' + '\0' - struct symbol **result; - struct menu *m = NULL; - - if (count < 1) { - delete[] data; - return; - } - - memcpy(data, str.constData(), count); - data[count] = '\0'; - - /* Seek for exact match */ - data[0] = '^'; - strcat(data, "$"); - result = sym_re_search(data); - if (!result) { - delete[] data; - return; - } + struct menu *m; - sym = *result; - - /* Seek for the menu which holds the symbol */ - for (struct property *prop = sym->prop; prop; prop = prop->next) { - if (prop->type != P_PROMPT && prop->type != P_MENU) - continue; - m = prop->menu; - break; - } + sym = sym_find(url.toEncoded().constData()); + m = sym_get_prompt_menu(sym); if (!m) { /* Symbol is not visible as a menu */ symbolInfo(); @@ -1204,9 +1146,6 @@ void ConfigInfoView::clicked(const QUrl &url) } else { emit menuSelected(m); } - - free(result); - delete[] data; } void ConfigInfoView::contextMenuEvent(QContextMenuEvent *event) @@ -1240,8 +1179,7 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent) layout2->addWidget(searchButton); layout1->addLayout(layout2); - split = new QSplitter(this); - split->setOrientation(Qt::Vertical); + split = new QSplitter(Qt::Vertical, this); list = new ConfigList(split, "search"); list->mode = listMode; info = new ConfigInfoView(split, "search"); @@ -1300,8 +1238,7 @@ void ConfigSearchWindow::search(void) return; for (p = result; *p; p++) { for_all_prompts((*p), prop) - lastItem = new ConfigItem(list, lastItem, prop->menu, - menu_is_visible(prop->menu)); + lastItem = new ConfigItem(list, lastItem, prop->menu); } } @@ -1341,61 +1278,56 @@ ConfigMainWindow::ConfigMainWindow(void) ConfigItem::menubackIcon = QIcon(QPixmap(xpm_menuback)); QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); setCentralWidget(widget); - split1 = new QSplitter(widget); - split1->setOrientation(Qt::Horizontal); - split1->setChildrenCollapsible(false); - - menuList = new ConfigList(widget, "menu"); + QVBoxLayout *layout = new QVBoxLayout(widget); - split2 = new QSplitter(widget); + split2 = new QSplitter(Qt::Vertical, widget); + layout->addWidget(split2); split2->setChildrenCollapsible(false); - split2->setOrientation(Qt::Vertical); - // create config tree - configList = new ConfigList(widget, "config"); + split1 = new QSplitter(Qt::Horizontal, split2); + split1->setChildrenCollapsible(false); - helpText = new ConfigInfoView(widget, "help"); + configList = new ConfigList(split1, "config"); - layout->addWidget(split2); - split2->addWidget(split1); - split1->addWidget(configList); - split1->addWidget(menuList); - split2->addWidget(helpText); + menuList = new ConfigList(split1, "menu"); + helpText = new ConfigInfoView(split2, "help"); setTabOrder(configList, helpText); + configList->setFocus(); backAction = new QAction(QPixmap(xpm_back), "Back", this); + backAction->setShortcut(QKeySequence::Back); connect(backAction, &QAction::triggered, this, &ConfigMainWindow::goBack); QAction *quitAction = new QAction("&Quit", this); - quitAction->setShortcut(Qt::CTRL | Qt::Key_Q); + quitAction->setShortcut(QKeySequence::Quit); connect(quitAction, &QAction::triggered, this, &ConfigMainWindow::close); - QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this); - loadAction->setShortcut(Qt::CTRL | Qt::Key_L); + QAction *loadAction = new QAction(QPixmap(xpm_load), "&Open", this); + loadAction->setShortcut(QKeySequence::Open); connect(loadAction, &QAction::triggered, this, &ConfigMainWindow::loadConfig); saveAction = new QAction(QPixmap(xpm_save), "&Save", this); - saveAction->setShortcut(Qt::CTRL | Qt::Key_S); + saveAction->setShortcut(QKeySequence::Save); connect(saveAction, &QAction::triggered, this, &ConfigMainWindow::saveConfig); conf_set_changed_callback(conf_changed); - configname = xstrdup(conf_get_configname()); + configname = conf_get_configname(); QAction *saveAsAction = new QAction("Save &As...", this); + saveAsAction->setShortcut(QKeySequence::SaveAs); connect(saveAsAction, &QAction::triggered, this, &ConfigMainWindow::saveConfigAs); QAction *searchAction = new QAction("&Find", this); - searchAction->setShortcut(Qt::CTRL | Qt::Key_F); + searchAction->setShortcut(QKeySequence::Find); connect(searchAction, &QAction::triggered, this, &ConfigMainWindow::searchConfig); singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this); @@ -1505,6 +1437,9 @@ ConfigMainWindow::ConfigMainWindow(void) connect(helpText, &ConfigInfoView::menuSelected, this, &ConfigMainWindow::setMenuLink); + connect(configApp, &QApplication::aboutToQuit, + this, &ConfigMainWindow::saveSettings); + conf_read(NULL); QString listMode = configSettings->value("/listMode", "symbol").toString(); @@ -1528,28 +1463,22 @@ ConfigMainWindow::ConfigMainWindow(void) void ConfigMainWindow::loadConfig(void) { QString str; - QByteArray ba; - const char *name; str = QFileDialog::getOpenFileName(this, "", configname); if (str.isNull()) return; - ba = str.toLocal8Bit(); - name = ba.data(); - - if (conf_read(name)) + if (conf_read(str.toLocal8Bit().constData())) QMessageBox::information(this, "qconf", "Unable to load configuration!"); - free(configname); - configname = xstrdup(name); + configname = str; ConfigList::updateListAllForAll(); } bool ConfigMainWindow::saveConfig(void) { - if (conf_write(configname)) { + if (conf_write(configname.toLocal8Bit().constData())) { QMessageBox::information(this, "qconf", "Unable to save configuration!"); return false; } @@ -1561,23 +1490,17 @@ bool ConfigMainWindow::saveConfig(void) void ConfigMainWindow::saveConfigAs(void) { QString str; - QByteArray ba; - const char *name; str = QFileDialog::getSaveFileName(this, "", configname); if (str.isNull()) return; - ba = str.toLocal8Bit(); - name = ba.data(); - - if (conf_write(name)) { + if (conf_write(str.toLocal8Bit().constData())) { QMessageBox::information(this, "qconf", "Unable to save configuration!"); } conf_write_autoconf(0); - free(configname); - configname = xstrdup(name); + configname = str; } void ConfigMainWindow::searchConfig(void) @@ -1662,9 +1585,6 @@ void ConfigMainWindow::listFocusChanged(void) void ConfigMainWindow::goBack(void) { - if (configList->rootEntry == &rootmenu) - return; - configList->setParentMenu(); } @@ -1905,8 +1825,6 @@ int main(int ac, char** av) v = new ConfigMainWindow(); //zconfdump(stdout); - configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); - configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); v->show(); configApp->exec(); diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 53373064d90ac..62ab3286d04f7 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -55,9 +55,7 @@ class ConfigList : public QTreeWidget { protected: void keyPressEvent(QKeyEvent *e); - void mousePressEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent *e); void focusInEvent(QFocusEvent *e); void contextMenuEvent(QContextMenuEvent *e); @@ -116,25 +114,25 @@ public slots: class ConfigItem : public QTreeWidgetItem { typedef class QTreeWidgetItem Parent; public: - ConfigItem(ConfigList *parent, ConfigItem *after, struct menu *m, bool v) - : Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false) + ConfigItem(ConfigList *parent, ConfigItem *after, struct menu *m) + : Parent(parent, after), nextItem(0), menu(m), goParent(false) { init(); } - ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v) - : Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false) + ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m) + : Parent(parent, after), nextItem(0), menu(m), goParent(false) { init(); } - ConfigItem(ConfigList *parent, ConfigItem *after, bool v) - : Parent(parent, after), nextItem(0), menu(0), visible(v), goParent(true) + ConfigItem(ConfigList *parent, ConfigItem *after) + : Parent(parent, after), nextItem(0), menu(0), goParent(true) { init(); } ~ConfigItem(void); void init(void); void updateMenu(void); - void testUpdateMenu(bool v); + void testUpdateMenu(void); ConfigList* listView() const { return (ConfigList*)Parent::treeWidget(); @@ -161,7 +159,6 @@ class ConfigItem : public QTreeWidgetItem { ConfigItem* nextItem; struct menu *menu; - bool visible; bool goParent; static QIcon symbolYesIcon, symbolModIcon, symbolNoIcon; @@ -237,7 +234,7 @@ public slots: class ConfigMainWindow : public QMainWindow { Q_OBJECT - char *configname; + QString configname; static QAction *saveAction; static void conf_changed(bool); public: diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index d51cd7ac15d2c..8e23faab5d227 100755 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -144,6 +144,7 @@ sub read_config { my %prompts; my %objects; my %config2kfile; +my %defaults; my $var; my $iflevel = 0; my @ifdeps; @@ -220,8 +221,9 @@ sub read_kconfig { $depends{$config} = $1; } elsif ($state eq "DEP" && /^\s*depends\s+on\s+(.*)$/) { $depends{$config} .= " " . $1; - } elsif ($state eq "DEP" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) { + } elsif ($state ne "NONE" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) { my $dep = $3; + $defaults{$config} = 1; if ($dep !~ /^\s*(y|m|n)\s*$/) { $dep =~ s/.*\sif\s+//; $depends{$config} .= " " . $dep; @@ -503,7 +505,7 @@ sub parse_config_selects # Check if something other than a module selects this config if (defined($orig_configs{$conf}) && $orig_configs{$conf} ne "m") { - dprint "$conf (non module) selects config, we are good\n"; + dprint "$conf (non module) selects $config, we are good\n"; # we are good with this return; } @@ -523,8 +525,16 @@ sub parse_config_selects # If no possible config selected this, then something happened. if (!defined($next_config)) { - print STDERR "WARNING: $config is required, but nothing in the\n"; - print STDERR " current config selects it.\n"; + + # Some config options have no prompt, and nothing selects them, but + # they stay turned on once the final checks for the configs + # are done. These configs have a default option, so turn off the + # warnings for configs with default options. + if (!defined($defaults{$config})) { + print STDERR "WARNING: $config is required, but nothing in the\n"; + print STDERR " current config selects it.\n"; + } + return; } diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index a3af93aaaf32a..89b84bf8e21fa 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -70,6 +70,24 @@ const char *sym_type_name(enum symbol_type type) return "???"; } +/** + * sym_get_prompt_menu - get the menu entry with a prompt + * + * @sym: a symbol pointer + * + * Return: the menu entry with a prompt. + */ +struct menu *sym_get_prompt_menu(const struct symbol *sym) +{ + struct menu *m; + + list_for_each_entry(m, &sym->menus, link) + if (m->prompt) + return m; + + return NULL; +} + /** * sym_get_choice_menu - get the parent choice menu if present * @@ -80,18 +98,12 @@ const char *sym_type_name(enum symbol_type type) struct menu *sym_get_choice_menu(const struct symbol *sym) { struct menu *menu = NULL; - struct menu *m; /* * Choice members must have a prompt. Find a menu entry with a prompt, * and assume it resides inside a choice block. */ - list_for_each_entry(m, &sym->menus, link) - if (m->prompt) { - menu = m; - break; - } - + menu = sym_get_prompt_menu(sym); if (!menu) return NULL; diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index a3c634b2f348f..d853ddb3b28c1 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -68,6 +68,10 @@ vmlinux_link() libs="${KBUILD_VMLINUX_LIBS}" fi + if is_enabled CONFIG_GENERIC_BUILTIN_DTB; then + objs="${objs} .builtin-dtbs.o" + fi + if is_enabled CONFIG_MODULES; then objs="${objs} .vmlinux.export.o" fi @@ -208,8 +212,8 @@ kallsymso= strip_debug= if is_enabled CONFIG_KALLSYMS; then - true > .tmp_vmlinux.kallsyms0.syms - kallsyms .tmp_vmlinux.kallsyms0.syms .tmp_vmlinux0.kallsyms + true > .tmp_vmlinux0.syms + kallsyms .tmp_vmlinux0.syms .tmp_vmlinux0.kallsyms fi if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then @@ -236,14 +240,14 @@ if is_enabled CONFIG_KALLSYMS; then # Generate section listing all symbols and add it into vmlinux # It's a four step process: # 0) Generate a dummy __kallsyms with empty symbol list. - # 1) Link .tmp_vmlinux.kallsyms1 so it has all symbols and sections, + # 1) Link .tmp_vmlinux1.kallsyms so it has all symbols and sections, # with a dummy __kallsyms. - # Running kallsyms on that gives us .tmp_kallsyms1.o with + # Running kallsyms on that gives us .tmp_vmlinux1.kallsyms.o with # the right size - # 2) Link .tmp_vmlinux.kallsyms2 so it now has a __kallsyms section of + # 2) Link .tmp_vmlinux2.kallsyms so it now has a __kallsyms section of # the right size, but due to the added section, some # addresses have shifted. - # From here, we generate a correct .tmp_vmlinux.kallsyms2.o + # From here, we generate a correct .tmp_vmlinux2.kallsyms.o # 3) That link may have expanded the kernel image enough that # more linker branch stubs / trampolines had to be added, which # introduces new names, which further expands kallsyms. Do another diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index c4cc11aa558f5..5b5745f00eb30 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -10,6 +10,12 @@ * of the GNU General Public License, incorporated herein by reference. */ +#include +#include + +#include "list.h" +#include "xalloc.h" + #include "modpost.h" #include "devicetable-offsets.h" @@ -31,6 +37,66 @@ typedef Elf64_Addr kernel_ulong_t; #include #include +/** + * module_alias_printf - add auto-generated MODULE_ALIAS() + * + * @mod: module + * @append_wildcard: append '*' for future extension if not exist yet + * @fmt: printf(3)-like format + */ +static void __attribute__((format (printf, 3, 4))) +module_alias_printf(struct module *mod, bool append_wildcard, + const char *fmt, ...) +{ + struct module_alias *new, *als; + size_t len; + int n; + va_list ap; + + /* Determine required size. */ + va_start(ap, fmt); + n = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (n < 0) { + error("vsnprintf failed\n"); + return; + } + + len = n + 1; /* extra byte for '\0' */ + + if (append_wildcard) + len++; /* extra byte for '*' */ + + new = xmalloc(sizeof(*new) + len); + + /* Now, really print it to the allocated buffer */ + va_start(ap, fmt); + n = vsnprintf(new->str, len, fmt, ap); + va_end(ap); + + if (n < 0) { + error("vsnprintf failed\n"); + free(new); + return; + } + + if (append_wildcard && (n == 0 || new->str[n - 1] != '*')) { + new->str[n] = '*'; + new->str[n + 1] = '\0'; + } + + /* avoid duplication */ + list_for_each_entry(als, &mod->aliases, node) { + if (!strcmp(als->str, new->str)) { + free(new); + return; + } + } + + list_add_tail(&new->node, &mod->aliases); +} + typedef uint32_t __u32; typedef uint16_t __u16; typedef unsigned char __u8; @@ -56,35 +122,24 @@ typedef struct { * we handle those differences explicitly below */ #include "../../include/linux/mod_devicetable.h" -/* This array collects all instances that use the generic do_table */ struct devtable { - const char *device_id; /* name of table, __mod___*_device_table. */ + const char *device_id; unsigned long id_size; - int (*do_entry)(const char *filename, void *symval, char *alias); + void (*do_entry)(struct module *mod, void *symval); }; -/* Size of alias provided to do_entry functions */ -#define ALIAS_SIZE 500 - /* Define a variable f that holds the value of field f of struct devid * based at address m. */ #define DEF_FIELD(m, devid, f) \ typeof(((struct devid *)0)->f) f = TO_NATIVE(*(typeof(f) *)((m) + OFF_##devid##_##f)) -/* Define a variable v that holds the address of field f of struct devid - * based at address m. Due to the way typeof works, for a field of type - * T[N] the variable has type T(*)[N], _not_ T*. - */ -#define DEF_FIELD_ADDR_VAR(m, devid, f, v) \ - typeof(((struct devid *)0)->f) *v = ((m) + OFF_##devid##_##f) - /* Define a variable f that holds the address of field f of struct devid * based at address m. Due to the way typeof works, for a field of type * T[N] the variable has type T(*)[N], _not_ T*. */ #define DEF_FIELD_ADDR(m, devid, f) \ - DEF_FIELD_ADDR_VAR(m, devid, f, f) + typeof(((struct devid *)0)->f) *f = ((m) + OFF_##devid##_##f) #define ADD(str, sep, cond, field) \ do { \ @@ -99,15 +154,6 @@ do { \ sprintf(str + strlen(str), "*"); \ } while(0) -/* End in a wildcard, for future extension */ -static inline void add_wildcard(char *str) -{ - int len = strlen(str); - - if (str[len - 1] != '*') - strcat(str + len, "*"); -} - static inline void add_uuid(char *str, uuid_le uuid) { int len = strlen(str); @@ -130,40 +176,6 @@ static inline void add_guid(char *str, guid_t guid) guid.b[12], guid.b[13], guid.b[14], guid.b[15]); } -/** - * Check that sizeof(device_id type) are consistent with size of section - * in .o file. If in-consistent then userspace and kernel does not agree - * on actual size which is a bug. - * Also verify that the final entry in the table is all zeros. - * Ignore both checks if build host differ from target host and size differs. - **/ -static void device_id_check(const char *modname, const char *device_id, - unsigned long size, unsigned long id_size, - void *symval) -{ - int i; - - if (size % id_size || size < id_size) { - fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo of the size of section __mod_%s___device_table=%lu.\n" - "Fix definition of struct %s_device_id in mod_devicetable.h\n", - modname, device_id, id_size, device_id, size, device_id); - } - /* Verify last one is a terminator */ - for (i = 0; i < id_size; i++ ) { - if (*(uint8_t*)(symval+size-id_size+i)) { - fprintf(stderr, - "%s: struct %s_device_id is %lu bytes. The last of %lu is:\n", - modname, device_id, id_size, size / id_size); - for (i = 0; i < id_size; i++ ) - fprintf(stderr,"0x%02x ", - *(uint8_t*)(symval+size-id_size+i) ); - fprintf(stderr,"\n"); - fatal("%s: struct %s_device_id is not terminated with a NULL entry!\n", - modname, device_id); - } - } -} - /* USB is special because the bcdDevice can be matched against a numeric range */ /* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */ static void do_usb_entry(void *symval, @@ -229,9 +241,7 @@ static void do_usb_entry(void *symval, ADD(alias, "in", match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER, bInterfaceNumber); - add_wildcard(alias); - buf_printf(&mod->dev_table_buf, - "MODULE_ALIAS(\"%s\");\n", alias); + module_alias_printf(mod, true, "%s", alias); } /* Handles increment/decrement of BCD formatted integers */ @@ -273,7 +283,7 @@ static unsigned int incbcd(unsigned int *bcd, return init; } -static void do_usb_entry_multi(void *symval, struct module *mod) +static void do_usb_entry_multi(struct module *mod, void *symval) { unsigned int devlo, devhi; unsigned char chi, clo, max; @@ -338,22 +348,7 @@ static void do_usb_entry_multi(void *symval, struct module *mod) } } -static void do_usb_table(void *symval, unsigned long size, - struct module *mod) -{ - unsigned int i; - const unsigned long id_size = SIZE_usb_device_id; - - device_id_check(mod->name, "usb", size, id_size, symval); - - /* Leave last one: it's the terminator. */ - size -= id_size; - - for (i = 0; i < size; i += id_size) - do_usb_entry_multi(symval + i, mod); -} - -static void do_of_entry_multi(void *symval, struct module *mod) +static void do_of_entry(struct module *mod, void *symval) { char alias[500]; int len; @@ -375,56 +370,39 @@ static void do_of_entry_multi(void *symval, struct module *mod) if (isspace(*tmp)) *tmp = '_'; - buf_printf(&mod->dev_table_buf, "MODULE_ALIAS(\"%s\");\n", alias); - strcat(alias, "C"); - add_wildcard(alias); - buf_printf(&mod->dev_table_buf, "MODULE_ALIAS(\"%s\");\n", alias); -} - -static void do_of_table(void *symval, unsigned long size, - struct module *mod) -{ - unsigned int i; - const unsigned long id_size = SIZE_of_device_id; - - device_id_check(mod->name, "of", size, id_size, symval); - - /* Leave last one: it's the terminator. */ - size -= id_size; - - for (i = 0; i < size; i += id_size) - do_of_entry_multi(symval + i, mod); + module_alias_printf(mod, false, "%s", alias); + module_alias_printf(mod, false, "%sC*", alias); } /* Looks like: hid:bNvNpN */ -static int do_hid_entry(const char *filename, - void *symval, char *alias) +static void do_hid_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, hid_device_id, bus); DEF_FIELD(symval, hid_device_id, group); DEF_FIELD(symval, hid_device_id, vendor); DEF_FIELD(symval, hid_device_id, product); - sprintf(alias, "hid:"); ADD(alias, "b", bus != HID_BUS_ANY, bus); ADD(alias, "g", group != HID_GROUP_ANY, group); ADD(alias, "v", vendor != HID_ANY_ID, vendor); ADD(alias, "p", product != HID_ANY_ID, product); - return 1; + module_alias_printf(mod, false, "hid:%s", alias); } /* Looks like: ieee1394:venNmoNspNverN */ -static int do_ieee1394_entry(const char *filename, - void *symval, char *alias) +static void do_ieee1394_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, ieee1394_device_id, match_flags); DEF_FIELD(symval, ieee1394_device_id, vendor_id); DEF_FIELD(symval, ieee1394_device_id, model_id); DEF_FIELD(symval, ieee1394_device_id, specifier_id); DEF_FIELD(symval, ieee1394_device_id, version); - strcpy(alias, "ieee1394:"); ADD(alias, "ven", match_flags & IEEE1394_MATCH_VENDOR_ID, vendor_id); ADD(alias, "mo", match_flags & IEEE1394_MATCH_MODEL_ID, @@ -434,14 +412,13 @@ static int do_ieee1394_entry(const char *filename, ADD(alias, "ver", match_flags & IEEE1394_MATCH_VERSION, version); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "ieee1394:%s", alias); } /* Looks like: pci:vNdNsvNsdNbcNscNiN or _pci:vNdNsvNsdNbcNscNiN. */ -static int do_pci_entry(const char *filename, - void *symval, char *alias) +static void do_pci_entry(struct module *mod, void *symval) { + char alias[256]; /* Class field can be divided into these three. */ unsigned char baseclass, subclass, interface, baseclass_mask, subclass_mask, interface_mask; @@ -464,7 +441,6 @@ static int do_pci_entry(const char *filename, default: warn("Unknown PCI driver_override alias %08X\n", override_only); - return 0; } ADD(alias, "v", vendor != PCI_ANY_ID, vendor); @@ -483,28 +459,28 @@ static int do_pci_entry(const char *filename, || (subclass_mask != 0 && subclass_mask != 0xFF) || (interface_mask != 0 && interface_mask != 0xFF)) { warn("Can't handle masks in %s:%04X\n", - filename, class_mask); - return 0; + mod->name, class_mask); + return; } ADD(alias, "bc", baseclass_mask == 0xFF, baseclass); ADD(alias, "sc", subclass_mask == 0xFF, subclass); ADD(alias, "i", interface_mask == 0xFF, interface); - add_wildcard(alias); - return 1; + + module_alias_printf(mod, true, "%s", alias); } /* looks like: "ccw:tNmNdtNdmN" */ -static int do_ccw_entry(const char *filename, - void *symval, char *alias) +static void do_ccw_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, ccw_device_id, match_flags); DEF_FIELD(symval, ccw_device_id, cu_type); DEF_FIELD(symval, ccw_device_id, cu_model); DEF_FIELD(symval, ccw_device_id, dev_type); DEF_FIELD(symval, ccw_device_id, dev_model); - strcpy(alias, "ccw:"); ADD(alias, "t", match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE, cu_type); ADD(alias, "m", match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL, @@ -513,47 +489,42 @@ static int do_ccw_entry(const char *filename, dev_type); ADD(alias, "dm", match_flags&CCW_DEVICE_ID_MATCH_DEVICE_MODEL, dev_model); - add_wildcard(alias); - return 1; + + module_alias_printf(mod, true, "ccw:%s", alias); } /* looks like: "ap:tN" */ -static int do_ap_entry(const char *filename, - void *symval, char *alias) +static void do_ap_entry(struct module *mod, void *symval) { DEF_FIELD(symval, ap_device_id, dev_type); - sprintf(alias, "ap:t%02X*", dev_type); - return 1; + module_alias_printf(mod, false, "ap:t%02X*", dev_type); } /* looks like: "css:tN" */ -static int do_css_entry(const char *filename, - void *symval, char *alias) +static void do_css_entry(struct module *mod, void *symval) { DEF_FIELD(symval, css_device_id, type); - sprintf(alias, "css:t%01X", type); - return 1; + module_alias_printf(mod, false, "css:t%01X", type); } /* Looks like: "serio:tyNprNidNexN" */ -static int do_serio_entry(const char *filename, - void *symval, char *alias) +static void do_serio_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, serio_device_id, type); DEF_FIELD(symval, serio_device_id, proto); DEF_FIELD(symval, serio_device_id, id); DEF_FIELD(symval, serio_device_id, extra); - strcpy(alias, "serio:"); ADD(alias, "ty", type != SERIO_ANY, type); ADD(alias, "pr", proto != SERIO_ANY, proto); ADD(alias, "id", id != SERIO_ANY, id); ADD(alias, "ex", extra != SERIO_ANY, extra); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "serio:%s", alias); } /* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or @@ -563,21 +534,19 @@ static int do_serio_entry(const char *filename, * or _CLS. Also, bb, ss, and pp can be substituted with ?? * as don't care byte. */ -static int do_acpi_entry(const char *filename, - void *symval, char *alias) +static void do_acpi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, acpi_device_id, id); DEF_FIELD(symval, acpi_device_id, cls); DEF_FIELD(symval, acpi_device_id, cls_msk); - if (id && strlen((const char *)*id)) - sprintf(alias, "acpi*:%s:*", *id); + if ((*id)[0]) + module_alias_printf(mod, false, "acpi*:%s:*", *id); else { + char alias[256]; int i, byte_shift, cnt = 0; unsigned int msk; - sprintf(&alias[cnt], "acpi*:"); - cnt = 6; for (i = 1; i <= 3; i++) { byte_shift = 8 * (3-i); msk = (cls_msk >> byte_shift) & 0xFF; @@ -588,101 +557,49 @@ static int do_acpi_entry(const char *filename, sprintf(&alias[cnt], "??"); cnt += 2; } - sprintf(&alias[cnt], ":*"); + module_alias_printf(mod, false, "acpi*:%s:*", alias); } - return 1; } /* looks like: "pnp:dD" */ -static void do_pnp_device_entry(void *symval, unsigned long size, - struct module *mod) +static void do_pnp_device_entry(struct module *mod, void *symval) { - const unsigned long id_size = SIZE_pnp_device_id; - const unsigned int count = (size / id_size)-1; - unsigned int i; - - device_id_check(mod->name, "pnp", size, id_size, symval); + DEF_FIELD_ADDR(symval, pnp_device_id, id); + char acpi_id[sizeof(*id)]; - for (i = 0; i < count; i++) { - DEF_FIELD_ADDR(symval + i*id_size, pnp_device_id, id); - char acpi_id[sizeof(*id)]; - int j; - - buf_printf(&mod->dev_table_buf, - "MODULE_ALIAS(\"pnp:d%s*\");\n", *id); - - /* fix broken pnp bus lowercasing */ - for (j = 0; j < sizeof(acpi_id); j++) - acpi_id[j] = toupper((*id)[j]); - buf_printf(&mod->dev_table_buf, - "MODULE_ALIAS(\"acpi*:%s:*\");\n", acpi_id); - } + /* fix broken pnp bus lowercasing */ + for (unsigned int i = 0; i < sizeof(acpi_id); i++) + acpi_id[i] = toupper((*id)[i]); + module_alias_printf(mod, false, "pnp:d%s*", *id); + module_alias_printf(mod, false, "acpi*:%s:*", acpi_id); } /* looks like: "pnp:dD" for every device of the card */ -static void do_pnp_card_entries(void *symval, unsigned long size, - struct module *mod) +static void do_pnp_card_entry(struct module *mod, void *symval) { - const unsigned long id_size = SIZE_pnp_card_device_id; - const unsigned int count = (size / id_size)-1; - unsigned int i; - - device_id_check(mod->name, "pnp", size, id_size, symval); - - for (i = 0; i < count; i++) { - unsigned int j; - DEF_FIELD_ADDR(symval + i * id_size, pnp_card_device_id, devs); - - for (j = 0; j < PNP_MAX_DEVICES; j++) { - const char *id = (char *)(*devs)[j].id; - int i2, j2; - int dup = 0; - - if (!id[0]) - break; + DEF_FIELD_ADDR(symval, pnp_card_device_id, devs); - /* find duplicate, already added value */ - for (i2 = 0; i2 < i && !dup; i2++) { - DEF_FIELD_ADDR_VAR(symval + i2 * id_size, - pnp_card_device_id, - devs, devs_dup); + for (unsigned int i = 0; i < PNP_MAX_DEVICES; i++) { + const char *id = (char *)(*devs)[i].id; + char acpi_id[PNP_ID_LEN]; - for (j2 = 0; j2 < PNP_MAX_DEVICES; j2++) { - const char *id2 = - (char *)(*devs_dup)[j2].id; - - if (!id2[0]) - break; - - if (!strcmp(id, id2)) { - dup = 1; - break; - } - } - } - - /* add an individual alias for every device entry */ - if (!dup) { - char acpi_id[PNP_ID_LEN]; - int k; + if (!id[0]) + break; - buf_printf(&mod->dev_table_buf, - "MODULE_ALIAS(\"pnp:d%s*\");\n", id); + /* fix broken pnp bus lowercasing */ + for (unsigned int j = 0; j < sizeof(acpi_id); j++) + acpi_id[j] = toupper(id[j]); - /* fix broken pnp bus lowercasing */ - for (k = 0; k < sizeof(acpi_id); k++) - acpi_id[k] = toupper(id[k]); - buf_printf(&mod->dev_table_buf, - "MODULE_ALIAS(\"acpi*:%s:*\");\n", acpi_id); - } - } + /* add an individual alias for every device entry */ + module_alias_printf(mod, false, "pnp:d%s*", id); + module_alias_printf(mod, false, "acpi*:%s:*", acpi_id); } } /* Looks like: pcmcia:mNcNfNfnNpfnNvaNvbNvcNvdN. */ -static int do_pcmcia_entry(const char *filename, - void *symval, char *alias) +static void do_pcmcia_entry(struct module *mod, void *symval) { + char alias[256] = {}; unsigned int i; DEF_FIELD(symval, pcmcia_device_id, match_flags); DEF_FIELD(symval, pcmcia_device_id, manf_id); @@ -696,7 +613,6 @@ static int do_pcmcia_entry(const char *filename, (*prod_id_hash)[i] = TO_NATIVE((*prod_id_hash)[i]); } - strcpy(alias, "pcmcia:"); ADD(alias, "m", match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID, manf_id); ADD(alias, "c", match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID, @@ -712,13 +628,12 @@ static int do_pcmcia_entry(const char *filename, ADD(alias, "pc", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3, (*prod_id_hash)[2]); ADD(alias, "pd", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4, (*prod_id_hash)[3]); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "pcmcia:%s", alias); } -static int do_vio_entry(const char *filename, void *symval, - char *alias) +static void do_vio_entry(struct module *mod, void *symval) { + char alias[256]; char *tmp; DEF_FIELD_ADDR(symval, vio_device_id, type); DEF_FIELD_ADDR(symval, vio_device_id, compat); @@ -731,8 +646,7 @@ static int do_vio_entry(const char *filename, void *symval, if (isspace (*tmp)) *tmp = '_'; - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "%s", alias); } static void do_input(char *alias, @@ -748,9 +662,10 @@ static void do_input(char *alias, } /* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */ -static int do_input_entry(const char *filename, void *symval, - char *alias) +static void do_input_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, input_device_id, flags); DEF_FIELD(symval, input_device_id, bustype); DEF_FIELD(symval, input_device_id, vendor); @@ -766,8 +681,6 @@ static int do_input_entry(const char *filename, void *symval, DEF_FIELD_ADDR(symval, input_device_id, ffbit); DEF_FIELD_ADDR(symval, input_device_id, swbit); - sprintf(alias, "input:"); - ADD(alias, "b", flags & INPUT_DEVICE_ID_MATCH_BUS, bustype); ADD(alias, "v", flags & INPUT_DEVICE_ID_MATCH_VENDOR, vendor); ADD(alias, "p", flags & INPUT_DEVICE_ID_MATCH_PRODUCT, product); @@ -802,102 +715,96 @@ static int do_input_entry(const char *filename, void *symval, sprintf(alias + strlen(alias), "w*"); if (flags & INPUT_DEVICE_ID_MATCH_SWBIT) do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX); - return 1; + + module_alias_printf(mod, false, "input:%s", alias); } -static int do_eisa_entry(const char *filename, void *symval, - char *alias) +static void do_eisa_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, eisa_device_id, sig); - if (sig[0]) - sprintf(alias, EISA_DEVICE_MODALIAS_FMT "*", *sig); - else - strcat(alias, "*"); - return 1; + module_alias_printf(mod, false, EISA_DEVICE_MODALIAS_FMT "*", *sig); } /* Looks like: parisc:tNhvNrevNsvN */ -static int do_parisc_entry(const char *filename, void *symval, - char *alias) +static void do_parisc_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, parisc_device_id, hw_type); DEF_FIELD(symval, parisc_device_id, hversion); DEF_FIELD(symval, parisc_device_id, hversion_rev); DEF_FIELD(symval, parisc_device_id, sversion); - strcpy(alias, "parisc:"); ADD(alias, "t", hw_type != PA_HWTYPE_ANY_ID, hw_type); ADD(alias, "hv", hversion != PA_HVERSION_ANY_ID, hversion); ADD(alias, "rev", hversion_rev != PA_HVERSION_REV_ANY_ID, hversion_rev); ADD(alias, "sv", sversion != PA_SVERSION_ANY_ID, sversion); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "parisc:%s", alias); } /* Looks like: sdio:cNvNdN. */ -static int do_sdio_entry(const char *filename, - void *symval, char *alias) +static void do_sdio_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, sdio_device_id, class); DEF_FIELD(symval, sdio_device_id, vendor); DEF_FIELD(symval, sdio_device_id, device); - strcpy(alias, "sdio:"); ADD(alias, "c", class != (__u8)SDIO_ANY_ID, class); ADD(alias, "v", vendor != (__u16)SDIO_ANY_ID, vendor); ADD(alias, "d", device != (__u16)SDIO_ANY_ID, device); - add_wildcard(alias); - return 1; + + module_alias_printf(mod, true, "sdio:%s", alias); } /* Looks like: ssb:vNidNrevN. */ -static int do_ssb_entry(const char *filename, - void *symval, char *alias) +static void do_ssb_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, ssb_device_id, vendor); DEF_FIELD(symval, ssb_device_id, coreid); DEF_FIELD(symval, ssb_device_id, revision); - strcpy(alias, "ssb:"); ADD(alias, "v", vendor != SSB_ANY_VENDOR, vendor); ADD(alias, "id", coreid != SSB_ANY_ID, coreid); ADD(alias, "rev", revision != SSB_ANY_REV, revision); - add_wildcard(alias); - return 1; + + module_alias_printf(mod, true, "ssb:%s", alias); } /* Looks like: bcma:mNidNrevNclN. */ -static int do_bcma_entry(const char *filename, - void *symval, char *alias) +static void do_bcma_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, bcma_device_id, manuf); DEF_FIELD(symval, bcma_device_id, id); DEF_FIELD(symval, bcma_device_id, rev); DEF_FIELD(symval, bcma_device_id, class); - strcpy(alias, "bcma:"); ADD(alias, "m", manuf != BCMA_ANY_MANUF, manuf); ADD(alias, "id", id != BCMA_ANY_ID, id); ADD(alias, "rev", rev != BCMA_ANY_REV, rev); ADD(alias, "cl", class != BCMA_ANY_CLASS, class); - add_wildcard(alias); - return 1; + + module_alias_printf(mod, true, "bcma:%s", alias); } /* Looks like: virtio:dNvN */ -static int do_virtio_entry(const char *filename, void *symval, - char *alias) +static void do_virtio_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, virtio_device_id, device); DEF_FIELD(symval, virtio_device_id, vendor); - strcpy(alias, "virtio:"); ADD(alias, "d", device != VIRTIO_DEV_ANY_ID, device); ADD(alias, "v", vendor != VIRTIO_DEV_ANY_ID, vendor); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "virtio:%s", alias); } /* @@ -906,8 +813,7 @@ static int do_virtio_entry(const char *filename, void *symval, * in the name. */ -static int do_vmbus_entry(const char *filename, void *symval, - char *alias) +static void do_vmbus_entry(struct module *mod, void *symval) { int i; DEF_FIELD_ADDR(symval, hv_vmbus_device_id, guid); @@ -916,68 +822,57 @@ static int do_vmbus_entry(const char *filename, void *symval, for (i = 0; i < (sizeof(*guid) * 2); i += 2) sprintf(&guid_name[i], "%02x", TO_NATIVE((guid->b)[i/2])); - strcpy(alias, "vmbus:"); - strcat(alias, guid_name); - - return 1; + module_alias_printf(mod, false, "vmbus:%s", guid_name); } /* Looks like: rpmsg:S */ -static int do_rpmsg_entry(const char *filename, void *symval, - char *alias) +static void do_rpmsg_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, rpmsg_device_id, name); - sprintf(alias, RPMSG_DEVICE_MODALIAS_FMT, *name); - return 1; + module_alias_printf(mod, false, RPMSG_DEVICE_MODALIAS_FMT, *name); } /* Looks like: i2c:S */ -static int do_i2c_entry(const char *filename, void *symval, - char *alias) +static void do_i2c_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, i2c_device_id, name); - sprintf(alias, I2C_MODULE_PREFIX "%s", *name); - return 1; + module_alias_printf(mod, false, I2C_MODULE_PREFIX "%s", *name); } -static int do_i3c_entry(const char *filename, void *symval, - char *alias) +static void do_i3c_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, i3c_device_id, match_flags); DEF_FIELD(symval, i3c_device_id, dcr); DEF_FIELD(symval, i3c_device_id, manuf_id); DEF_FIELD(symval, i3c_device_id, part_id); DEF_FIELD(symval, i3c_device_id, extra_info); - strcpy(alias, "i3c:"); ADD(alias, "dcr", match_flags & I3C_MATCH_DCR, dcr); ADD(alias, "manuf", match_flags & I3C_MATCH_MANUF, manuf_id); ADD(alias, "part", match_flags & I3C_MATCH_PART, part_id); ADD(alias, "ext", match_flags & I3C_MATCH_EXTRA_INFO, extra_info); - return 1; + module_alias_printf(mod, false, "i3c:%s", alias); } -static int do_slim_entry(const char *filename, void *symval, char *alias) +static void do_slim_entry(struct module *mod, void *symval) { DEF_FIELD(symval, slim_device_id, manf_id); DEF_FIELD(symval, slim_device_id, prod_code); - sprintf(alias, "slim:%x:%x:*", manf_id, prod_code); - - return 1; + module_alias_printf(mod, false, "slim:%x:%x:*", manf_id, prod_code); } /* Looks like: spi:S */ -static int do_spi_entry(const char *filename, void *symval, - char *alias) +static void do_spi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, spi_device_id, name); - sprintf(alias, SPI_MODULE_PREFIX "%s", *name); - return 1; + module_alias_printf(mod, false, SPI_MODULE_PREFIX "%s", *name); } static const struct dmifield { @@ -1012,12 +907,11 @@ static void dmi_ascii_filter(char *d, const char *s) } -static int do_dmi_entry(const char *filename, void *symval, - char *alias) +static void do_dmi_entry(struct module *mod, void *symval) { + char alias[256] = {}; int i, j; DEF_FIELD_ADDR(symval, dmi_system_id, matches); - sprintf(alias, "dmi*"); for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) { for (j = 0; j < 4; j++) { @@ -1032,80 +926,75 @@ static int do_dmi_entry(const char *filename, void *symval, } } - strcat(alias, ":"); - return 1; + module_alias_printf(mod, false, "dmi*%s:", alias); } -static int do_platform_entry(const char *filename, - void *symval, char *alias) +static void do_platform_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, platform_device_id, name); - sprintf(alias, PLATFORM_MODULE_PREFIX "%s", *name); - return 1; + + module_alias_printf(mod, false, PLATFORM_MODULE_PREFIX "%s", *name); } -static int do_mdio_entry(const char *filename, - void *symval, char *alias) +static void do_mdio_entry(struct module *mod, void *symval) { + char id[33]; int i; DEF_FIELD(symval, mdio_device_id, phy_id); DEF_FIELD(symval, mdio_device_id, phy_id_mask); - alias += sprintf(alias, MDIO_MODULE_PREFIX); - for (i = 0; i < 32; i++) { if (!((phy_id_mask >> (31-i)) & 1)) - *(alias++) = '?'; + id[i] = '?'; else if ((phy_id >> (31-i)) & 1) - *(alias++) = '1'; + id[i] = '1'; else - *(alias++) = '0'; + id[i] = '0'; } /* Terminate the string */ - *alias = 0; + id[32] = '\0'; - return 1; + module_alias_printf(mod, false, MDIO_MODULE_PREFIX "%s", id); } /* Looks like: zorro:iN. */ -static int do_zorro_entry(const char *filename, void *symval, - char *alias) +static void do_zorro_entry(struct module *mod, void *symval) { + char alias[256] = {}; DEF_FIELD(symval, zorro_device_id, id); - strcpy(alias, "zorro:"); + ADD(alias, "i", id != ZORRO_WILDCARD, id); - return 1; + + module_alias_printf(mod, false, "zorro:%s", alias); } /* looks like: "pnp:dD" */ -static int do_isapnp_entry(const char *filename, - void *symval, char *alias) +static void do_isapnp_entry(struct module *mod, void *symval) { DEF_FIELD(symval, isapnp_device_id, vendor); DEF_FIELD(symval, isapnp_device_id, function); - sprintf(alias, "pnp:d%c%c%c%x%x%x%x*", + module_alias_printf(mod, false, "pnp:d%c%c%c%x%x%x%x*", 'A' + ((vendor >> 2) & 0x3f) - 1, 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, 'A' + ((vendor >> 8) & 0x1f) - 1, (function >> 4) & 0x0f, function & 0x0f, (function >> 12) & 0x0f, (function >> 8) & 0x0f); - return 1; } /* Looks like: "ipack:fNvNdN". */ -static int do_ipack_entry(const char *filename, - void *symval, char *alias) +static void do_ipack_entry(struct module *mod, void *symval) { + char alias[256] = {}; DEF_FIELD(symval, ipack_device_id, format); DEF_FIELD(symval, ipack_device_id, vendor); DEF_FIELD(symval, ipack_device_id, device); - strcpy(alias, "ipack:"); + ADD(alias, "f", format != IPACK_ANY_FORMAT, format); ADD(alias, "v", vendor != IPACK_ANY_ID, vendor); ADD(alias, "d", device != IPACK_ANY_ID, device); - add_wildcard(alias); - return 1; + + module_alias_printf(mod, true, "ipack:%s", alias); } /* @@ -1156,9 +1045,9 @@ static void append_nibble_mask(char **outp, * N is exactly 8 digits, where each is an upper-case hex digit, or * a ? or [] pattern matching exactly one digit. */ -static int do_amba_entry(const char *filename, - void *symval, char *alias) +static void do_amba_entry(struct module *mod, void *symval) { + char alias[256]; unsigned int digit; char *p = alias; DEF_FIELD(symval, amba_id, id); @@ -1166,15 +1055,14 @@ static int do_amba_entry(const char *filename, if ((id & mask) != id) fatal("%s: Masked-off bit(s) of AMBA device ID are non-zero: id=0x%08X, mask=0x%08X. Please fix this driver.\n", - filename, id, mask); + mod->name, id, mask); - p += sprintf(alias, "amba:d"); for (digit = 0; digit < 8; digit++) append_nibble_mask(&p, (id >> (4 * (7 - digit))) & 0xf, (mask >> (4 * (7 - digit))) & 0xf); - return 1; + module_alias_printf(mod, false, "amba:d%s", alias); } /* @@ -1183,13 +1071,11 @@ static int do_amba_entry(const char *filename, * N is exactly 2 digits, where each is an upper-case hex digit, or * a ? or [] pattern matching exactly one digit. */ -static int do_mips_cdmm_entry(const char *filename, - void *symval, char *alias) +static void do_mips_cdmm_entry(struct module *mod, void *symval) { DEF_FIELD(symval, mips_cdmm_device_id, type); - sprintf(alias, "mipscdmm:t%02X*", type); - return 1; + module_alias_printf(mod, false, "mipscdmm:t%02X*", type); } /* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,* @@ -1198,137 +1084,130 @@ static int do_mips_cdmm_entry(const char *filename, * complicated. */ -static int do_x86cpu_entry(const char *filename, void *symval, - char *alias) +static void do_x86cpu_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, x86_cpu_id, feature); DEF_FIELD(symval, x86_cpu_id, family); DEF_FIELD(symval, x86_cpu_id, model); DEF_FIELD(symval, x86_cpu_id, vendor); - strcpy(alias, "cpu:type:x86,"); ADD(alias, "ven", vendor != X86_VENDOR_ANY, vendor); ADD(alias, "fam", family != X86_FAMILY_ANY, family); ADD(alias, "mod", model != X86_MODEL_ANY, model); strcat(alias, ":feature:*"); if (feature != X86_FEATURE_ANY) sprintf(alias + strlen(alias), "%04X*", feature); - return 1; + + module_alias_printf(mod, false, "cpu:type:x86,%s", alias); } /* LOOKS like cpu:type:*:feature:*FEAT* */ -static int do_cpu_entry(const char *filename, void *symval, char *alias) +static void do_cpu_entry(struct module *mod, void *symval) { DEF_FIELD(symval, cpu_feature, feature); - sprintf(alias, "cpu:type:*:feature:*%04X*", feature); - return 1; + module_alias_printf(mod, false, "cpu:type:*:feature:*%04X*", feature); } /* Looks like: mei:S:uuid:N:* */ -static int do_mei_entry(const char *filename, void *symval, - char *alias) +static void do_mei_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD_ADDR(symval, mei_cl_device_id, name); DEF_FIELD_ADDR(symval, mei_cl_device_id, uuid); DEF_FIELD(symval, mei_cl_device_id, version); - sprintf(alias, MEI_CL_MODULE_PREFIX); - sprintf(alias + strlen(alias), "%s:", (*name)[0] ? *name : "*"); add_uuid(alias, *uuid); ADD(alias, ":", version != MEI_CL_VERSION_ANY, version); - strcat(alias, ":*"); - - return 1; + module_alias_printf(mod, false, MEI_CL_MODULE_PREFIX "%s:%s:*", + (*name)[0] ? *name : "*", alias); } /* Looks like: rapidio:vNdNavNadN */ -static int do_rio_entry(const char *filename, - void *symval, char *alias) +static void do_rio_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, rio_device_id, did); DEF_FIELD(symval, rio_device_id, vid); DEF_FIELD(symval, rio_device_id, asm_did); DEF_FIELD(symval, rio_device_id, asm_vid); - strcpy(alias, "rapidio:"); ADD(alias, "v", vid != RIO_ANY_ID, vid); ADD(alias, "d", did != RIO_ANY_ID, did); ADD(alias, "av", asm_vid != RIO_ANY_ID, asm_vid); ADD(alias, "ad", asm_did != RIO_ANY_ID, asm_did); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "rapidio:%s", alias); } /* Looks like: ulpi:vNpN */ -static int do_ulpi_entry(const char *filename, void *symval, - char *alias) +static void do_ulpi_entry(struct module *mod, void *symval) { DEF_FIELD(symval, ulpi_device_id, vendor); DEF_FIELD(symval, ulpi_device_id, product); - sprintf(alias, "ulpi:v%04xp%04x", vendor, product); - - return 1; + module_alias_printf(mod, false, "ulpi:v%04xp%04x", vendor, product); } /* Looks like: hdaudio:vNrNaN */ -static int do_hda_entry(const char *filename, void *symval, char *alias) +static void do_hda_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, hda_device_id, vendor_id); DEF_FIELD(symval, hda_device_id, rev_id); DEF_FIELD(symval, hda_device_id, api_version); - strcpy(alias, "hdaudio:"); ADD(alias, "v", vendor_id != 0, vendor_id); ADD(alias, "r", rev_id != 0, rev_id); ADD(alias, "a", api_version != 0, api_version); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "hdaudio:%s", alias); } /* Looks like: sdw:mNpNvNcN */ -static int do_sdw_entry(const char *filename, void *symval, char *alias) +static void do_sdw_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, sdw_device_id, mfg_id); DEF_FIELD(symval, sdw_device_id, part_id); DEF_FIELD(symval, sdw_device_id, sdw_version); DEF_FIELD(symval, sdw_device_id, class_id); - strcpy(alias, "sdw:"); ADD(alias, "m", mfg_id != 0, mfg_id); ADD(alias, "p", part_id != 0, part_id); ADD(alias, "v", sdw_version != 0, sdw_version); ADD(alias, "c", class_id != 0, class_id); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "sdw:%s", alias); } /* Looks like: fsl-mc:vNdN */ -static int do_fsl_mc_entry(const char *filename, void *symval, - char *alias) +static void do_fsl_mc_entry(struct module *mod, void *symval) { DEF_FIELD(symval, fsl_mc_device_id, vendor); DEF_FIELD_ADDR(symval, fsl_mc_device_id, obj_type); - sprintf(alias, "fsl-mc:v%08Xd%s", vendor, *obj_type); - return 1; + module_alias_printf(mod, false, "fsl-mc:v%08Xd%s", vendor, *obj_type); } /* Looks like: tbsvc:kSpNvNrN */ -static int do_tbsvc_entry(const char *filename, void *symval, char *alias) +static void do_tbsvc_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, tb_service_id, match_flags); DEF_FIELD_ADDR(symval, tb_service_id, protocol_key); DEF_FIELD(symval, tb_service_id, protocol_id); DEF_FIELD(symval, tb_service_id, protocol_version); DEF_FIELD(symval, tb_service_id, protocol_revision); - strcpy(alias, "tbsvc:"); if (match_flags & TBSVC_MATCH_PROTOCOL_KEY) sprintf(alias + strlen(alias), "k%s", *protocol_key); else @@ -1339,93 +1218,80 @@ static int do_tbsvc_entry(const char *filename, void *symval, char *alias) ADD(alias, "r", match_flags & TBSVC_MATCH_PROTOCOL_REVISION, protocol_revision); - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "tbsvc:%s", alias); } /* Looks like: typec:idNmN */ -static int do_typec_entry(const char *filename, void *symval, char *alias) +static void do_typec_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, typec_device_id, svid); DEF_FIELD(symval, typec_device_id, mode); - sprintf(alias, "typec:id%04X", svid); ADD(alias, "m", mode != TYPEC_ANY_MODE, mode); - return 1; + module_alias_printf(mod, false, "typec:id%04X%s", svid, alias); } /* Looks like: tee:uuid */ -static int do_tee_entry(const char *filename, void *symval, char *alias) +static void do_tee_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, tee_client_device_id, uuid); - sprintf(alias, "tee:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + module_alias_printf(mod, true, + "tee:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid->b[0], uuid->b[1], uuid->b[2], uuid->b[3], uuid->b[4], uuid->b[5], uuid->b[6], uuid->b[7], uuid->b[8], uuid->b[9], uuid->b[10], uuid->b[11], uuid->b[12], uuid->b[13], uuid->b[14], uuid->b[15]); - - add_wildcard(alias); - return 1; } /* Looks like: wmi:guid */ -static int do_wmi_entry(const char *filename, void *symval, char *alias) +static void do_wmi_entry(struct module *mod, void *symval) { - int len; DEF_FIELD_ADDR(symval, wmi_device_id, guid_string); if (strlen(*guid_string) != UUID_STRING_LEN) { warn("Invalid WMI device id 'wmi:%s' in '%s'\n", - *guid_string, filename); - return 0; + *guid_string, mod->name); + return; } - len = snprintf(alias, ALIAS_SIZE, WMI_MODULE_PREFIX "%s", *guid_string); - if (len < 0 || len >= ALIAS_SIZE) { - warn("Could not generate all MODULE_ALIAS's in '%s'\n", - filename); - return 0; - } - return 1; + module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", *guid_string); } /* Looks like: mhi:S */ -static int do_mhi_entry(const char *filename, void *symval, char *alias) +static void do_mhi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, mhi_device_id, chan); - sprintf(alias, MHI_DEVICE_MODALIAS_FMT, *chan); - return 1; + module_alias_printf(mod, false, MHI_DEVICE_MODALIAS_FMT, *chan); } /* Looks like: mhi_ep:S */ -static int do_mhi_ep_entry(const char *filename, void *symval, char *alias) +static void do_mhi_ep_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, mhi_device_id, chan); - sprintf(alias, MHI_EP_DEVICE_MODALIAS_FMT, *chan); - return 1; + module_alias_printf(mod, false, MHI_EP_DEVICE_MODALIAS_FMT, *chan); } /* Looks like: ishtp:{guid} */ -static int do_ishtp_entry(const char *filename, void *symval, char *alias) +static void do_ishtp_entry(struct module *mod, void *symval) { + char alias[256] = {}; DEF_FIELD_ADDR(symval, ishtp_device_id, guid); - strcpy(alias, ISHTP_MODULE_PREFIX "{"); add_guid(alias, *guid); - strcat(alias, "}"); - return 1; + module_alias_printf(mod, false, ISHTP_MODULE_PREFIX "{%s}", alias); } -static int do_auxiliary_entry(const char *filename, void *symval, char *alias) +static void do_auxiliary_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, auxiliary_device_id, name); - sprintf(alias, AUXILIARY_MODULE_PREFIX "%s", *name); - return 1; + module_alias_printf(mod, false, AUXILIARY_MODULE_PREFIX "%s", *name); } /* @@ -1433,8 +1299,10 @@ static int do_auxiliary_entry(const char *filename, void *symval, char *alias) * * N is exactly 2 digits, where each is an upper-case hex digit. */ -static int do_ssam_entry(const char *filename, void *symval, char *alias) +static void do_ssam_entry(struct module *mod, void *symval) { + char alias[256] = {}; + DEF_FIELD(symval, ssam_device_id, match_flags); DEF_FIELD(symval, ssam_device_id, domain); DEF_FIELD(symval, ssam_device_id, category); @@ -1442,30 +1310,28 @@ static int do_ssam_entry(const char *filename, void *symval, char *alias) DEF_FIELD(symval, ssam_device_id, instance); DEF_FIELD(symval, ssam_device_id, function); - sprintf(alias, "ssam:d%02Xc%02X", domain, category); ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target); ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); - return 1; + module_alias_printf(mod, false, "ssam:d%02Xc%02X%s", + domain, category, alias); } /* Looks like: dfl:tNfN */ -static int do_dfl_entry(const char *filename, void *symval, char *alias) +static void do_dfl_entry(struct module *mod, void *symval) { DEF_FIELD(symval, dfl_device_id, type); DEF_FIELD(symval, dfl_device_id, feature_id); - sprintf(alias, "dfl:t%04Xf%04X", type, feature_id); - - add_wildcard(alias); - return 1; + module_alias_printf(mod, true, "dfl:t%04Xf%04X", type, feature_id); } /* Looks like: cdx:vNdN */ -static int do_cdx_entry(const char *filename, void *symval, - char *alias) +static void do_cdx_entry(struct module *mod, void *symval) { + char alias[256]; + DEF_FIELD(symval, cdx_device_id, vendor); DEF_FIELD(symval, cdx_device_id, device); DEF_FIELD(symval, cdx_device_id, subvendor); @@ -1484,7 +1350,7 @@ static int do_cdx_entry(const char *filename, void *symval, default: warn("Unknown CDX driver_override alias %08X\n", override_only); - return 0; + return; } ADD(alias, "v", vendor != CDX_ANY_ID, vendor); @@ -1493,24 +1359,22 @@ static int do_cdx_entry(const char *filename, void *symval, ADD(alias, "sd", subdevice != CDX_ANY_ID, subdevice); ADD(alias, "c", class_mask == 0xFFFFFF, class); - return 1; + module_alias_printf(mod, false, "%s", alias); } -static int do_vchiq_entry(const char *filename, void *symval, char *alias) +static void do_vchiq_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, vchiq_device_id, name); - sprintf(alias, "vchiq:%s", *name); - return 1; + module_alias_printf(mod, false, "vchiq:%s", *name); } /* Looks like: coreboot:tN */ -static int do_coreboot_entry(const char *filename, void *symval, char *alias) +static void do_coreboot_entry(struct module *mod, void *symval) { DEF_FIELD(symval, coreboot_device_id, tag); - sprintf(alias, "coreboot:t%08X", tag); - return 1; + module_alias_printf(mod, false, "coreboot:t%08X", tag); } /* Does namelen bytes of name exactly match the symbol? */ @@ -1522,25 +1386,34 @@ static bool sym_is(const char *name, unsigned namelen, const char *symbol) return memcmp(name, symbol, namelen) == 0; } -static void do_table(void *symval, unsigned long size, +static void do_table(const char *name, void *symval, unsigned long size, unsigned long id_size, const char *device_id, - int (*do_entry)(const char *filename, void *symval, char *alias), + void (*do_entry)(struct module *mod, void *symval), struct module *mod) { unsigned int i; - char alias[ALIAS_SIZE]; - device_id_check(mod->name, device_id, size, id_size, symval); - /* Leave last one: it's the terminator. */ - size -= id_size; + if (size % id_size || size < id_size) { + error("%s: type mismatch between %s[] and MODULE_DEVICE_TABLE(%s, ...)\n", + mod->name, name, device_id); + return; + } - for (i = 0; i < size; i += id_size) { - if (do_entry(mod->name, symval+i, alias)) { - buf_printf(&mod->dev_table_buf, - "MODULE_ALIAS(\"%s\");\n", alias); + /* Verify the last entry is a terminator */ + for (i = size - id_size; i < size; i++) { + if (*(uint8_t *)(symval + i)) { + error("%s: %s[] is not terminated with a NULL entry\n", + mod->name, name); + return; } } + + /* Leave last one: it's the terminator. */ + size -= id_size; + + for (i = 0; i < size; i += id_size) + do_entry(mod, symval + i); } static const struct devtable devtable[] = { @@ -1596,6 +1469,10 @@ static const struct devtable devtable[] = { {"cdx", SIZE_cdx_device_id, do_cdx_entry}, {"vchiq", SIZE_vchiq_device_id, do_vchiq_entry}, {"coreboot", SIZE_coreboot_device_id, do_coreboot_entry}, + {"of", SIZE_of_device_id, do_of_entry}, + {"usb", SIZE_usb_device_id, do_usb_entry_multi}, + {"pnp", SIZE_pnp_device_id, do_pnp_device_entry}, + {"pnp_card", SIZE_pnp_card_device_id, do_pnp_card_entry}, }; /* Create MODULE_ALIAS() statements. @@ -1606,8 +1483,9 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, { void *symval; char *zeros = NULL; - const char *name, *identifier; - unsigned int namelen; + const char *type, *name; + size_t typelen; + static const char *prefix = "__mod_device_table__"; /* We're looking for a section relative symbol */ if (!sym->st_shndx || get_secindex(info, sym) >= info->num_sections) @@ -1617,19 +1495,16 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) return; - /* All our symbols are of form __mod____device_table. */ - if (strncmp(symname, "__mod_", strlen("__mod_"))) - return; - name = symname + strlen("__mod_"); - namelen = strlen(name); - if (namelen < strlen("_device_table")) - return; - if (strcmp(name + namelen - strlen("_device_table"), "_device_table")) + /* All our symbols are of form __mod_device_table____. */ + if (!strstarts(symname, prefix)) return; - identifier = strstr(name, "__"); - if (!identifier) + type = symname + strlen(prefix); + + name = strstr(type, "__"); + if (!name) return; - namelen = identifier - name; + typelen = name - type; + name += strlen("__"); /* Handle all-NULL symbols allocated into .bss */ if (info->sechdrs[get_secindex(info, sym)].sh_type & SHT_NOBITS) { @@ -1639,35 +1514,15 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, symval = sym_get_data(info, sym); } - /* First handle the "special" cases */ - if (sym_is(name, namelen, "usb")) - do_usb_table(symval, sym->st_size, mod); - else if (sym_is(name, namelen, "of")) - do_of_table(symval, sym->st_size, mod); - else if (sym_is(name, namelen, "pnp")) - do_pnp_device_entry(symval, sym->st_size, mod); - else if (sym_is(name, namelen, "pnp_card")) - do_pnp_card_entries(symval, sym->st_size, mod); - else { - int i; - - for (i = 0; i < ARRAY_SIZE(devtable); i++) { - const struct devtable *p = &devtable[i]; + for (int i = 0; i < ARRAY_SIZE(devtable); i++) { + const struct devtable *p = &devtable[i]; - if (sym_is(name, namelen, p->device_id)) { - do_table(symval, sym->st_size, p->id_size, - p->device_id, p->do_entry, mod); - break; - } + if (sym_is(type, typelen, p->device_id)) { + do_table(name, symval, sym->st_size, p->id_size, + p->device_id, p->do_entry, mod); + break; } } - free(zeros); -} -/* Now add out buffered information to the generated C source */ -void add_moddevtable(struct buffer *buf, struct module *mod) -{ - buf_printf(buf, "\n"); - buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos); - free(mod->dev_table_buf.p); + free(zeros); } diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 107393a8c48a5..0584cbcdbd2d6 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -176,6 +177,7 @@ static struct module *new_module(const char *name, size_t namelen) INIT_LIST_HEAD(&mod->unresolved_symbols); INIT_LIST_HEAD(&mod->missing_namespaces); INIT_LIST_HEAD(&mod->imported_namespaces); + INIT_LIST_HEAD(&mod->aliases); memcpy(mod->name, name, namelen); mod->name[namelen] = '\0'; @@ -209,19 +211,6 @@ struct symbol { static HASHTABLE_DEFINE(symbol_hashtable, 1U << 10); -/* This is based on the hash algorithm from gdbm, via tdb */ -static inline unsigned int tdb_hash(const char *name) -{ - unsigned value; /* Used to compute the hash value. */ - unsigned i; /* Used to cycle through random values. */ - - /* Set the initial value from the key size. */ - for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) - value = (value + (((unsigned char *)name)[i] << (i*5 % 24))); - - return (1103515243 * value + 12345); -} - /** * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module @@ -239,7 +228,7 @@ static struct symbol *alloc_symbol(const char *name) /* For the hash of exported symbols */ static void hash_add_symbol(struct symbol *sym) { - hash_add(symbol_hashtable, &sym->hnode, tdb_hash(sym->name)); + hash_add(symbol_hashtable, &sym->hnode, hash_str(sym->name)); } static void sym_add_unresolved(const char *name, struct module *mod, bool weak) @@ -260,7 +249,7 @@ static struct symbol *sym_find_with_module(const char *name, struct module *mod) if (name[0] == '.') name++; - hash_for_each_possible(symbol_hashtable, s, hnode, tdb_hash(name)) { + hash_for_each_possible(symbol_hashtable, s, hnode, hash_str(name)) { if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } @@ -340,8 +329,6 @@ static const char *sec_name(const struct elf_info *info, unsigned int secindex) return sech_name(info, &info->sechdrs[secindex]); } -#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) - static struct symbol *sym_add_exported(const char *name, struct module *mod, bool gpl_only, const char *namespace) { @@ -1966,6 +1953,7 @@ static void write_vmlinux_export_c_file(struct module *mod) static void write_mod_c_file(struct module *mod) { struct buffer buf = { }; + struct module_alias *alias, *next; char fname[PATH_MAX]; int ret; @@ -1973,7 +1961,14 @@ static void write_mod_c_file(struct module *mod) add_exported_symbols(&buf, mod); add_versions(&buf, mod); add_depends(&buf, mod); - add_moddevtable(&buf, mod); + + buf_printf(&buf, "\n"); + list_for_each_entry_safe(alias, next, &mod->aliases, node) { + buf_printf(&buf, "MODULE_ALIAS(\"%s\");\n", alias->str); + list_del(&alias->node); + free(alias); + } + add_srcversion(&buf, mod); ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index ada3a36cc4bc9..49848fcbe2a12 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -67,6 +67,8 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) + struct buffer { char *p; int pos; @@ -79,6 +81,22 @@ buf_printf(struct buffer *buf, const char *fmt, ...); void buf_write(struct buffer *buf, const char *s, int len); +/** + * struct module_alias - auto-generated MODULE_ALIAS() + * + * @node: linked to module::aliases + * @str: a string for MODULE_ALIAS() + */ +struct module_alias { + struct list_head node; + char str[]; +}; + +/** + * struct module - represent a module (vmlinux or *.ko) + * + * @aliases: list head for module_aliases + */ struct module { struct list_head list; struct list_head exported_symbols; @@ -89,12 +107,12 @@ struct module { bool seen; bool has_init; bool has_cleanup; - struct buffer dev_table_buf; char srcversion[25]; // Missing namespace dependencies struct list_head missing_namespaces; // Actual imported namespaces struct list_head imported_namespaces; + struct list_head aliases; char name[]; }; @@ -170,7 +188,6 @@ Elf_Sym *symsearch_find_nearest(struct elf_info *elf, Elf_Addr addr, /* file2alias.c */ void handle_moddevtable(struct module *mod, struct elf_info *info, Elf_Sym *sym, const char *symname); -void add_moddevtable(struct buffer *buf, struct module *mod); /* sumversion.c */ void get_src_version(const char *modname, char sum[], unsigned sumlen); diff --git a/scripts/module.lds.S b/scripts/module.lds.S index 711c6e0299365..c2f80f9141d40 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -18,10 +18,10 @@ SECTIONS { *(.export_symbol) } - __ksymtab 0 : { *(SORT(___ksymtab+*)) } - __ksymtab_gpl 0 : { *(SORT(___ksymtab_gpl+*)) } - __kcrctab 0 : { *(SORT(___kcrctab+*)) } - __kcrctab_gpl 0 : { *(SORT(___kcrctab_gpl+*)) } + __ksymtab 0 : ALIGN(8) { *(SORT(___ksymtab+*)) } + __ksymtab_gpl 0 : ALIGN(8) { *(SORT(___ksymtab_gpl+*)) } + __kcrctab 0 : ALIGN(4) { *(SORT(___kcrctab+*)) } + __kcrctab_gpl 0 : ALIGN(4) { *(SORT(___kcrctab_gpl+*)) } .ctors 0 : ALIGN(8) { *(SORT(.ctors.*)) *(.ctors) } .init_array 0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) } @@ -29,6 +29,7 @@ SECTIONS { .altinstructions 0 : ALIGN(8) { KEEP(*(.altinstructions)) } __bug_table 0 : ALIGN(8) { KEEP(*(__bug_table)) } __jump_table 0 : ALIGN(8) { KEEP(*(__jump_table)) } + __ex_table 0 : ALIGN(4) { KEEP(*(__ex_table)) } __patchable_function_entries : { *(__patchable_function_entries) } diff --git a/scripts/nsdeps b/scripts/nsdeps index f1718cc0d700b..bab4ec870e505 100644 --- a/scripts/nsdeps +++ b/scripts/nsdeps @@ -19,12 +19,6 @@ if ! { echo "$SPATCH_REQ_VERSION"; echo "$SPATCH_VERSION"; } | sort -CV ; then exit 1 fi -if [ "$KBUILD_EXTMOD" ]; then - src_prefix= -else - src_prefix=$srctree/ -fi - generate_deps_for_ns() { $SPATCH --very-quiet --in-place --sp-file \ $srctree/scripts/coccinelle/misc/add_namespace.cocci -D nsdeps -D ns=$1 $2 @@ -34,7 +28,7 @@ generate_deps() { local mod=${1%.ko:} shift local namespaces="$*" - local mod_source_files=$(sed "s|^\(.*\)\.o$|${src_prefix}\1.c|" $mod.mod) + local mod_source_files=$(sed "s|^\(.*\)\.o$|${srcroot}/\1.c|" $mod.mod) for ns in $namespaces; do echo "Adding namespace $ns to module $mod.ko." @@ -57,4 +51,4 @@ generate_deps() { while read line do generate_deps $line -done < $MODULES_NSDEPS +done < modules.nsdeps diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 441b0bb66e0d0..fb686fd3266f0 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -96,16 +96,18 @@ install_linux_image_dbg () { # Parse modules.order directly because 'make modules_install' may sign, # compress modules, and then run unneeded depmod. - while read -r mod; do - mod="${mod%.o}.ko" - dbg="${pdir}/usr/lib/debug/lib/modules/${KERNELRELEASE}/kernel/${mod}" - buildid=$("${READELF}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p') - link="${pdir}/usr/lib/debug/.build-id/${buildid}.debug" - - mkdir -p "${dbg%/*}" "${link%/*}" - "${OBJCOPY}" --only-keep-debug "${mod}" "${dbg}" - ln -sf --relative "${dbg}" "${link}" - done < modules.order + if is_enabled CONFIG_MODULES; then + while read -r mod; do + mod="${mod%.o}.ko" + dbg="${pdir}/usr/lib/debug/lib/modules/${KERNELRELEASE}/kernel/${mod}" + buildid=$("${READELF}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p') + link="${pdir}/usr/lib/debug/.build-id/${buildid}.debug" + + mkdir -p "${dbg%/*}" "${link%/*}" + "${OBJCOPY}" --only-keep-debug "${mod}" "${dbg}" + ln -sf --relative "${dbg}" "${link}" + done < modules.order + fi # Build debug package # Different tools want the image in different locations diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index 7ec1f061a519c..64d958ee45f38 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -51,6 +51,13 @@ mkdir -p "${destdir}" if [ "${CC}" != "${HOSTCC}" ]; then echo "Rebuilding host programs with ${CC}..." + # This leverages external module building. + # - Clear sub_make_done to allow the top-level Makefile to redo sub-make. + # - Filter out --no-print-directory to print "Entering directory" logs + # when Make changes the working directory. + unset sub_make_done + MAKEFLAGS=$(echo "${MAKEFLAGS}" | sed s/--no-print-directory//) + cat <<-'EOF' > "${destdir}/Kbuild" subdir-y := scripts EOF diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index fc3b7fa709fcf..4ffcc70f8e319 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -202,7 +202,7 @@ Build-Depends-Arch: bc, bison, cpio, flex, gcc-${host_gnu} , kmod, libelf-dev:native, libssl-dev:native, libssl-dev , - rsync + python3:native, rsync Homepage: https://www.kernel.org/ Package: $packagename-$version diff --git a/scripts/rust_is_available.sh b/scripts/rust_is_available.sh index 5262c56dd674e..93c0ef7fb3fb2 100755 --- a/scripts/rust_is_available.sh +++ b/scripts/rust_is_available.sh @@ -225,6 +225,21 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then exit 1 fi +if [ "$bindgen_libclang_cversion" -ge 1900100 ] && + [ "$rust_bindings_generator_cversion" -lt 6905 ]; then + # Distributions may have patched the issue (e.g. Debian did). + if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_concat.h | grep -q foofoo; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.69.5 together with libclang >= 19.1" + echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2824)," + echo >&2 "*** unless patched (like Debian's)." + echo >&2 "*** Your bindgen version: $rust_bindings_generator_version" + echo >&2 "*** Your libclang version: $bindgen_libclang_version" + echo >&2 "***" + warning=1 + fi +fi + # If the C compiler is Clang, then we can also check whether its version # matches the `libclang` version used by the Rust bindings generator. # diff --git a/scripts/rust_is_available_bindgen_libclang_concat.h b/scripts/rust_is_available_bindgen_libclang_concat.h new file mode 100644 index 0000000000000..efc6e98d0f1d0 --- /dev/null +++ b/scripts/rust_is_available_bindgen_libclang_concat.h @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define F(x) int x##x +F(foo); diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py index 413741037fb30..4fcc319dea84e 100755 --- a/scripts/rust_is_available_test.py +++ b/scripts/rust_is_available_test.py @@ -54,7 +54,7 @@ def generate_rustc(cls, stdout): """) @classmethod - def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False): + def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False): if libclang_stderr is None: libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})" else: @@ -65,12 +65,19 @@ def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched= else: version_0_66_case = "raise SystemExit(1)" + if libclang_concat_patched: + libclang_concat_case = "print('pub static mut foofoo: ::std::os::raw::c_int;')" + else: + libclang_concat_case = "pass" + return cls.generate_executable(f"""#!/usr/bin/env python3 import sys if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv): {libclang_case} elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv): {version_0_66_case} +elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv): + {libclang_concat_case} else: print({repr(version_stdout)}) """) @@ -268,6 +275,31 @@ def test_bindgen_libclang_old_version(self): result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen }) self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr) + def test_bindgen_bad_libclang_concat(self): + for (bindgen_version, libclang_version, expected_not_patched) in ( + ("0.69.4", "18.0.0", self.Expected.SUCCESS), + ("0.69.4", "19.1.0", self.Expected.SUCCESS_WITH_WARNINGS), + ("0.69.4", "19.2.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.69.5", "18.0.0", self.Expected.SUCCESS), + ("0.69.5", "19.1.0", self.Expected.SUCCESS), + ("0.69.5", "19.2.0", self.Expected.SUCCESS), + + ("0.70.0", "18.0.0", self.Expected.SUCCESS), + ("0.70.0", "19.1.0", self.Expected.SUCCESS), + ("0.70.0", "19.2.0", self.Expected.SUCCESS), + ): + with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version): + cc = self.generate_clang(f"clang version {libclang_version}") + libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false" + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr) + result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc }) + if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS: + self.assertIn(f"Rust bindings generator '{bindgen}' < 0.69.5 together with libclang >= 19.1", result.stderr) + + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True) + result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc }) + def test_clang_matches_bindgen_libclang_different_bindgen(self): bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false") result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen }) diff --git a/scripts/setlocalversion b/scripts/setlocalversion index 38b96c6797f40..28169d7e143b6 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -10,6 +10,8 @@ # # +set -e + usage() { echo "Usage: $0 [--no-local] [srctree]" >&2 exit 1 @@ -30,6 +32,29 @@ if test $# -gt 0 -o ! -d "$srctree"; then usage fi +try_tag() { + tag="$1" + + # Is $tag an annotated tag? + if [ "$(git cat-file -t "$tag" 2> /dev/null)" != tag ]; then + return + fi + + # Is it an ancestor of HEAD, and if so, how many commits are in $tag..HEAD? + # shellcheck disable=SC2046 # word splitting is the point here + set -- $(git rev-list --count --left-right "$tag"...HEAD 2> /dev/null) + + # $1 is 0 if and only if $tag is an ancestor of HEAD. Use + # string comparison, because $1 is empty if the 'git rev-list' + # command somehow failed. + if [ "$1" != 0 ]; then + return + fi + + # $2 is the number of commits in the range $tag..HEAD, possibly 0. + count="$2" +} + scm_version() { local short=false @@ -61,33 +86,33 @@ scm_version() # stable kernel: 6.1.7 -> v6.1.7 version_tag=v$(echo "${KERNELVERSION}" | sed -E 's/^([0-9]+\.[0-9]+)\.0(.*)$/\1\2/') + # try_tag initializes count if the tag is usable. + count= + # If a localversion* file exists, and the corresponding # annotated tag exists and is an ancestor of HEAD, use # it. This is the case in linux-next. - tag=${file_localversion#-} - desc= - if [ -n "${tag}" ]; then - desc=$(git describe --match=$tag 2>/dev/null) + if [ -n "${file_localversion#-}" ] ; then + try_tag "${file_localversion#-}" fi # Otherwise, if a localversion* file exists, and the tag # obtained by appending it to the tag derived from # KERNELVERSION exists and is an ancestor of HEAD, use # it. This is e.g. the case in linux-rt. - if [ -z "${desc}" ] && [ -n "${file_localversion}" ]; then - tag="${version_tag}${file_localversion}" - desc=$(git describe --match=$tag 2>/dev/null) + if [ -z "${count}" ] && [ -n "${file_localversion}" ]; then + try_tag "${version_tag}${file_localversion}" fi # Otherwise, default to the annotated tag derived from KERNELVERSION. - if [ -z "${desc}" ]; then - tag="${version_tag}" - desc=$(git describe --match=$tag 2>/dev/null) + if [ -z "${count}" ]; then + try_tag "${version_tag}" fi - # If we are at the tagged commit, we ignore it because the version is - # well-defined. - if [ "${tag}" != "${desc}" ]; then + # If we are at the tagged commit, we ignore it because the + # version is well-defined. If none of the attempted tags exist + # or were usable, $count is still empty. + if [ -z "${count}" ] || [ "${count}" -gt 0 ]; then # If only the short version is requested, don't bother # running further git commands @@ -95,14 +120,15 @@ scm_version() echo "+" return fi + # If we are past the tagged commit, we pretty print it. # (like 6.1.0-14595-g292a089d78d3) - if [ -n "${desc}" ]; then - echo "${desc}" | awk -F- '{printf("-%05d", $(NF-1))}' + if [ -n "${count}" ]; then + printf "%s%05d" "-" "${count}" fi # Add -g and exactly 12 hex chars. - printf '%s%s' -g "$(echo $head | cut -c1-12)" + printf '%s%.12s' -g "$head" fi if ${no_dirty}; then diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 554329a074ce9..05bd9ca1fbfa1 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -141,6 +141,7 @@ anomoly||anomaly anonynous||anonymous anway||anyway aplication||application +apeared||appeared appearence||appearance applicaion||application appliction||application @@ -155,6 +156,7 @@ apropriate||appropriate aquainted||acquainted aquired||acquired aquisition||acquisition +aquires||acquires arbitary||arbitrary architechture||architecture archtecture||architecture @@ -185,10 +187,12 @@ assotiated||associated asssert||assert assum||assume assumtpion||assumption +asume||assume asuming||assuming asycronous||asynchronous asychronous||asynchronous asynchnous||asynchronous +asynchrnous||asynchronous asynchronus||asynchronous asynchromous||asynchronous asymetric||asymmetric @@ -269,6 +273,7 @@ caculate||calculate caculation||calculation cadidate||candidate cahces||caches +calcluate||calculate calender||calendar calescing||coalescing calibraiton||calibration @@ -331,6 +336,7 @@ chouse||chose circumvernt||circumvent claread||cleared clared||cleared +clearify||clarify closeing||closing clustred||clustered cnfiguration||configuration @@ -379,12 +385,14 @@ comsumed||consumed comunicate||communicate comunication||communication conbination||combination +concurent||concurrent conditionaly||conditionally conditon||condition condtion||condition condtional||conditional conected||connected conector||connector +configed||configured configration||configuration configred||configured configuartion||configuration @@ -394,6 +402,7 @@ configuratoin||configuration configuraton||configuration configuretion||configuration configutation||configuration +congiuration||configuration conider||consider conjuction||conjunction connecetd||connected @@ -403,6 +412,7 @@ connnection||connection connnections||connections consistancy||consistency consistant||consistent +consits||consists containes||contains containts||contains contaisn||contains @@ -452,6 +462,7 @@ decendants||descendants decompres||decompress decsribed||described decription||description +detault||default dectected||detected defailt||default deferal||deferral @@ -487,6 +498,7 @@ depreacte||deprecate desactivate||deactivate desciptor||descriptor desciptors||descriptors +descritpor||descriptor descripto||descriptor descripton||description descrition||description @@ -601,6 +613,7 @@ enchanced||enhanced encorporating||incorporating encrupted||encrypted encrypiton||encryption +encryped||encrypted encryptio||encryption endianess||endianness enpoint||endpoint @@ -630,6 +643,7 @@ etsbalishment||establishment evalute||evaluate evalutes||evaluates evalution||evaluation +evaulated||evaluated excecutable||executable excceed||exceed exceded||exceeded @@ -650,6 +664,7 @@ exlcude||exclude exlcuding||excluding exlcusive||exclusive exlusive||exclusive +exlicitly||explicitly exmaple||example expecially||especially experies||expires @@ -659,6 +674,7 @@ explict||explicit explictely||explicitly explictly||explicitly expresion||expression +exprienced||experienced exprimental||experimental extened||extended exteneded||extended @@ -834,6 +850,7 @@ informations||information informtion||information infromation||information ingore||ignore +inheritence||inheritance inital||initial initalized||initialized initalised||initialized @@ -878,6 +895,7 @@ interoprability||interoperability interuupt||interrupt interupt||interrupt interupts||interrupts +interurpt||interrupt interrface||interface interrrupt||interrupt interrup||interrupt @@ -925,6 +943,7 @@ jumpimng||jumping juse||just jus||just kown||known +lable||label langage||language langauage||language langauge||language @@ -995,6 +1014,7 @@ metdata||metadata micropone||microphone microprocesspr||microprocessor migrateable||migratable +miliseconds||milliseconds millenium||millennium milliseonds||milliseconds minimim||minimum @@ -1132,6 +1152,7 @@ palne||plane paramameters||parameters paramaters||parameters paramater||parameter +paramenters||parameters parametes||parameters parametised||parametrised paramter||parameter @@ -1177,9 +1198,11 @@ poiter||pointer posible||possible positon||position possibilites||possibilities +postion||position potocol||protocol powerfull||powerful pramater||parameter +preambule||preamble preamle||preamble preample||preamble preapre||prepare @@ -1269,6 +1292,7 @@ raoming||roaming reasearcher||researcher reasearchers||researchers reasearch||research +recalcualte||recalculate receieve||receive recepient||recipient recevied||received @@ -1291,6 +1315,7 @@ refcounf||refcount refence||reference refered||referred referenace||reference +refererence||reference refering||referring refernces||references refernnce||reference @@ -1315,12 +1340,14 @@ reloade||reload remoote||remote remore||remote removeable||removable +repective||respective repectively||respectively replacable||replaceable replacments||replacements replys||replies reponse||response representaion||representation +repsonse||response reqeust||request reqister||register requed||requeued @@ -1362,6 +1389,7 @@ reuest||request reuqest||request reutnred||returned revsion||revision +rewritting||rewriting rmeoved||removed rmeove||remove rmeoves||removes @@ -1444,6 +1472,7 @@ soluation||solution souce||source speach||speech specfic||specific +specfication||specification specfield||specified speciefied||specified specifc||specific @@ -1544,6 +1573,7 @@ syncronus||synchronous syste||system sytem||system sythesis||synthesis +tagert||target taht||that tained||tainted tarffic||traffic @@ -1574,6 +1604,7 @@ tiggers||triggers tiggered||triggered tipically||typically timeing||timing +timming||timing timout||timeout tmis||this toogle||toggle @@ -1597,8 +1628,10 @@ transision||transition transistioned||transitioned transmittd||transmitted transormed||transformed +trasaction||transaction trasfer||transfer trasmission||transmission +trasmitter||transmitter treshold||threshold triggerd||triggered trigerred||triggered diff --git a/scripts/tags.sh b/scripts/tags.sh index 0d01c1cafb709..b212363779980 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -189,7 +189,7 @@ regex_c=( '/\ #include #include +#include #include "include/apparmor.h" #include "include/capability.h" @@ -30,8 +31,9 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = { }; struct audit_cache { - struct aa_profile *profile; - kernel_cap_t caps; + const struct cred *ad_subj_cred; + /* Capabilities go from 0 to CAP_LAST_CAP */ + u64 ktime_ns_expiration[CAP_LAST_CAP+1]; }; static DEFINE_PER_CPU(struct audit_cache, audit_cache); @@ -64,6 +66,8 @@ static void audit_cb(struct audit_buffer *ab, void *va) static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile, int cap, int error) { + const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */ + struct aa_ruleset *rules = list_first_entry(&profile->rules, typeof(*rules), list); struct audit_cache *ent; @@ -89,15 +93,16 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile /* Do simple duplicate message elimination */ ent = &get_cpu_var(audit_cache); - if (profile == ent->profile && cap_raised(ent->caps, cap)) { + /* If the capability was never raised the timestamp check would also catch that */ + if (ad->subj_cred == ent->ad_subj_cred && ktime_get_ns() <= ent->ktime_ns_expiration[cap]) { put_cpu_var(audit_cache); if (COMPLAIN_MODE(profile)) return complain_error(error); return error; } else { - aa_put_profile(ent->profile); - ent->profile = aa_get_profile(profile); - cap_raise(ent->caps, cap); + put_cred(ent->ad_subj_cred); + ent->ad_subj_cred = get_cred(ad->subj_cred); + ent->ktime_ns_expiration[cap] = ktime_get_ns() + AUDIT_CACHE_TIMEOUT_NS; } put_cpu_var(audit_cache); @@ -109,7 +114,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile * @profile: profile being enforced (NOT NULL, NOT unconfined) * @cap: capability to test if allowed * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated - * @ad: audit data (MAY BE NULL indicating no auditing) + * @ad: audit data (NOT NULL) * * Returns: 0 if allowed else -EPERM */ diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 2bc34dce9a468..5939bd9a9b9bb 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -636,6 +636,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, struct aa_ruleset *rules = list_first_entry(&profile->rules, typeof(*rules), list); struct aa_label *new = NULL; + struct aa_profile *new_profile = NULL; const char *info = NULL, *name = NULL, *target = NULL; aa_state_t state = rules->file->start[AA_CLASS_FILE]; struct aa_perms perms = {}; @@ -680,15 +681,18 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, /* hack ix fallback - improve how this is detected */ goto audit; } else if (!new) { - error = -EACCES; info = "profile transition not found"; - /* remove MAY_EXEC to audit as failure */ + /* remove MAY_EXEC to audit as failure or complaint */ perms.allow &= ~MAY_EXEC; + if (COMPLAIN_MODE(profile)) { + /* create null profile instead of failing */ + goto create_learning_profile; + } + error = -EACCES; } } else if (COMPLAIN_MODE(profile)) { +create_learning_profile: /* no exec permission - learning mode */ - struct aa_profile *new_profile = NULL; - new_profile = aa_new_learning_profile(profile, false, name, GFP_KERNEL); if (!new_profile) { @@ -709,8 +713,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, if (!(perms.xindex & AA_X_UNSAFE)) { if (DEBUG_ON) { - dbg_printk("apparmor: scrubbing environment variables" - " for %s profile=", name); + dbg_printk("apparmor: setting AT_SECURE for %s profile=", + name); aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } @@ -789,8 +793,8 @@ static int profile_onexec(const struct cred *subj_cred, if (!(perms.xindex & AA_X_UNSAFE)) { if (DEBUG_ON) { - dbg_printk("apparmor: scrubbing environment " - "variables for %s label=", xname); + dbg_printk("apparmor: setting AT_SECURE for %s label=", + xname); aa_label_printk(onexec, GFP_KERNEL); dbg_printk("\n"); } @@ -821,33 +825,19 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, AA_BUG(!bprm); AA_BUG(!buffer); - if (!stack) { - error = fn_for_each_in_ns(label, profile, - profile_onexec(subj_cred, profile, onexec, stack, - bprm, buffer, cond, unsafe)); - if (error) - return ERR_PTR(error); - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, - aa_get_newest_label(onexec), - profile_transition(subj_cred, profile, bprm, - buffer, - cond, unsafe)); - - } else { - /* TODO: determine how much we want to loosen this */ - error = fn_for_each_in_ns(label, profile, - profile_onexec(subj_cred, profile, onexec, stack, bprm, - buffer, cond, unsafe)); - if (error) - return ERR_PTR(error); - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, - aa_label_merge(&profile->label, onexec, - GFP_KERNEL), - profile_transition(subj_cred, profile, bprm, - buffer, - cond, unsafe)); - } + /* TODO: determine how much we want to loosen this */ + error = fn_for_each_in_ns(label, profile, + profile_onexec(subj_cred, profile, onexec, stack, + bprm, buffer, cond, unsafe)); + if (error) + return ERR_PTR(error); + new = fn_label_build_in_ns(label, profile, GFP_KERNEL, + stack ? aa_label_merge(&profile->label, onexec, + GFP_KERNEL) + : aa_get_newest_label(onexec), + profile_transition(subj_cred, profile, bprm, + buffer, cond, unsafe)); if (new) return new; @@ -960,8 +950,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) if (unsafe) { if (DEBUG_ON) { - dbg_printk("scrubbing environment variables for %s " - "label=", bprm->filename); + dbg_printk("setting AT_SECURE for %s label=", + bprm->filename); aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } @@ -971,8 +961,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) if (label->proxy != new->proxy) { /* when transitioning clear unsafe personality bits */ if (DEBUG_ON) { - dbg_printk("apparmor: clearing unsafe personality " - "bits. %s label=", bprm->filename); + dbg_printk("apparmor: clearing unsafe personality bits. %s label=", + bprm->filename); aa_label_printk(new, GFP_KERNEL); dbg_printk("\n"); } diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 2a72e6b17d68b..93290ae300bb2 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -160,31 +160,7 @@ int aa_label_next_confined(struct aa_label *l, int i); #define label_for_each_cont(I, L, P) \ for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i)) -#define next_comb(I, L1, L2) \ -do { \ - (I).j++; \ - if ((I).j >= (L2)->size) { \ - (I).i++; \ - (I).j = 0; \ - } \ -} while (0) - -/* for each combination of P1 in L1, and P2 in L2 */ -#define label_for_each_comb(I, L1, L2, P1, P2) \ -for ((I).i = (I).j = 0; \ - ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \ - (I) = next_comb(I, L1, L2)) - -#define fn_for_each_comb(L1, L2, P1, P2, FN) \ -({ \ - struct label_it i; \ - int __E = 0; \ - label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \ - last_error(__E, (FN)); \ - } \ - __E; \ -}) /* for each profile that is enforcing confinement in a label */ #define label_for_each_confined(I, L, P) \ @@ -291,8 +267,6 @@ bool aa_label_replace(struct aa_label *old, struct aa_label *new); bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old, struct aa_label *new); -struct aa_label *aa_label_find(struct aa_label *l); - struct aa_profile *aa_label_next_in_merge(struct label_it *I, struct aa_label *a, struct aa_label *b); @@ -320,8 +294,6 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, struct aa_label *label, int flags, gfp_t gfp); void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, gfp_t gfp); -void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp); -void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp); void aa_label_printk(struct aa_label *label, gfp_t gfp); struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index d7a894b1031ff..f11a0db7f51da 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -59,7 +59,6 @@ extern int apparmor_initialized; /* fn's in lib */ const char *skipn_spaces(const char *str, size_t n); -char *aa_split_fqname(char *args, char **ns_name); const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, size_t *ns_len); void aa_info_message(const char *str); diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 4bb0405c91908..536ce3abd5986 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -87,10 +87,12 @@ struct table_header { char td_data[]; }; -#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) +#define TABLE_DATAU16(TABLE) ((u16 *)((TABLE)->td_data)) +#define TABLE_DATAU32(TABLE) ((u32 *)((TABLE)->td_data)) +#define DEFAULT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) #define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) -#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) -#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) +#define NEXT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) +#define CHECK_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) #define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) #define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) #define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 0f7e913c3fc21..bbaa7d39a39ac 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -213,9 +213,6 @@ void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend); void aa_profile_match_label(struct aa_profile *profile, struct aa_ruleset *rules, struct aa_label *label, int type, u32 request, struct aa_perms *perms); -int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, - u32 request, int type, u32 *deny, - struct apparmor_audit_data *ad); int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms, u32 request, struct apparmor_audit_data *ad, void (*cb)(struct audit_buffer *, void *)); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 75088cc310b67..757e3c232c571 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -264,7 +264,6 @@ void aa_free_profile(struct aa_profile *profile); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, size_t n); -struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, const char *fqname, size_t n); diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h index cc6d1c9f4a472..f6a515640950f 100644 --- a/security/apparmor/include/secid.h +++ b/security/apparmor/include/secid.h @@ -34,6 +34,5 @@ void apparmor_release_secctx(char *secdata, u32 seclen); int aa_alloc_secid(struct aa_label *label, gfp_t gfp); void aa_free_secid(u32 secid); -void aa_secid_update(u32 secid, struct aa_label *label); #endif /* __AA_SECID_H */ diff --git a/security/apparmor/label.c b/security/apparmor/label.c index c71e4615dd460..91483ecacc16a 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -899,23 +899,6 @@ struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len, return vec_create_and_insert_label(vec, len, gfp); } -/** - * aa_label_find - find label @label in label set - * @label: label to find (NOT NULL) - * - * Requires: caller to hold a valid ref on l - * - * Returns: refcounted @label if @label is in tree - * refcounted label that is equiv to @label in tree - * else NULL if @label or equiv is not in tree - */ -struct aa_label *aa_label_find(struct aa_label *label) -{ - AA_BUG(!label); - - return vec_find(label->vec, label->size); -} - /** * aa_label_insert - insert label @label into @ls or return existing label @@ -1811,22 +1794,6 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, pr_info("%s", label->hname); } -void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp) -{ - struct aa_ns *ns = aa_get_current_ns(); - - aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp); - aa_put_ns(ns); -} - -void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp) -{ - struct aa_ns *ns = aa_get_current_ns(); - - aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp); - aa_put_ns(ns); -} - void aa_label_printk(struct aa_label *label, gfp_t gfp) { struct aa_ns *ns = aa_get_current_ns(); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index cd569fbbfe36d..7db62213e352e 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -45,44 +45,6 @@ void aa_free_str_table(struct aa_str_table *t) } } -/** - * aa_split_fqname - split a fqname into a profile and namespace name - * @fqname: a full qualified name in namespace profile format (NOT NULL) - * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) - * - * Returns: profile name or NULL if one is not specified - * - * Split a namespace name from a profile name (see policy.c for naming - * description). If a portion of the name is missing it returns NULL for - * that portion. - * - * NOTE: may modify the @fqname string. The pointers returned point - * into the @fqname string. - */ -char *aa_split_fqname(char *fqname, char **ns_name) -{ - char *name = strim(fqname); - - *ns_name = NULL; - if (name[0] == ':') { - char *split = strchr(&name[1], ':'); - *ns_name = skip_spaces(&name[1]); - if (split) { - /* overwrite ':' with \0 */ - *split++ = 0; - if (strncmp(split, "//", 2) == 0) - split += 2; - name = skip_spaces(split); - } else - /* a ns name without a following profile is allowed */ - name = NULL; - } - if (name && *name == 0) - name = NULL; - - return name; -} - /** * skipn_spaces - Removes leading whitespace from @str. * @str: The string to be stripped. @@ -275,33 +237,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, audit_log_format(ab, "\""); } -/** - * aa_audit_perms_cb - generic callback fn for auditing perms - * @ab: audit buffer (NOT NULL) - * @va: audit struct to audit values of (NOT NULL) - */ -static void aa_audit_perms_cb(struct audit_buffer *ab, void *va) -{ - struct common_audit_data *sa = va; - struct apparmor_audit_data *ad = aad(sa); - - if (ad->request) { - audit_log_format(ab, " requested_mask="); - aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs, - PERMS_CHRS_MASK, aa_file_perm_names, - PERMS_NAMES_MASK); - } - if (ad->denied) { - audit_log_format(ab, "denied_mask="); - aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs, - PERMS_CHRS_MASK, aa_file_perm_names, - PERMS_NAMES_MASK); - } - audit_log_format(ab, " peer="); - aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, - FLAGS_NONE, GFP_ATOMIC); -} - /** * aa_apply_modes_to_perms - apply namespace and profile flags to perms * @profile: that perms where computed from @@ -349,25 +284,6 @@ void aa_profile_match_label(struct aa_profile *profile, } -/* currently unused */ -int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, - u32 request, int type, u32 *deny, - struct apparmor_audit_data *ad) -{ - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); - struct aa_perms perms; - - ad->peer = &target->label; - ad->request = request; - - aa_profile_match_label(profile, rules, &target->label, type, request, - &perms); - aa_apply_modes_to_perms(profile, &perms); - *deny |= request & perms.deny; - return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb); -} - /** * aa_check_perms - do audit mode selection based on perms set * @profile: profile being checked diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 517d77d3c34cc..f2d9c57f87943 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -247,6 +247,42 @@ void aa_dfa_free_kref(struct kref *kref) dfa_free(dfa); } + + +/** + * remap_data16_to_data32 - remap u16 @old table to a u32 based table + * @old: table to remap + * + * Returns: new table with u32 entries instead of u16. + * + * Note: will free @old so caller does not have to + */ +static struct table_header *remap_data16_to_data32(struct table_header *old) +{ + struct table_header *new; + size_t tsize; + u32 i; + + tsize = table_size(old->td_lolen, YYTD_DATA32); + new = kvzalloc(tsize, GFP_KERNEL); + if (!new) { + kvfree(old); + return NULL; + } + new->td_id = old->td_id; + new->td_flags = YYTD_DATA32; + new->td_lolen = old->td_lolen; + + for (i = 0; i < old->td_lolen; i++) + TABLE_DATAU32(new)[i] = (u32) TABLE_DATAU16(old)[i]; + + kvfree(old); + if (is_vmalloc_addr(new)) + vm_unmap_aliases(); + + return new; +} + /** * aa_dfa_unpack - unpack the binary tables of a serialized dfa * @blob: aligned serialized stream of data to unpack (NOT NULL) @@ -326,8 +362,10 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) case YYTD_ID_DEF: case YYTD_ID_NXT: case YYTD_ID_CHK: - if (table->td_flags != YYTD_DATA16) + if (!(table->td_flags == YYTD_DATA16 || + table->td_flags == YYTD_DATA32)) { goto fail; + } break; case YYTD_ID_EC: if (table->td_flags != YYTD_DATA8) @@ -342,6 +380,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) dfa->tables[table->td_id] = table; data += table_size(table->td_lolen, table->td_flags); size -= table_size(table->td_lolen, table->td_flags); + + /* + * this remapping has to be done after incrementing data above + * for now straight remap, later have dfa support both + */ + switch (table->td_id) { + case YYTD_ID_DEF: + case YYTD_ID_NXT: + case YYTD_ID_CHK: + if (table->td_flags == YYTD_DATA16) { + table = remap_data16_to_data32(table); + if (!table) + goto fail; + } + dfa->tables[table->td_id] = table; + break; + } table = NULL; } error = verify_table_headers(dfa->tables, flags); @@ -395,10 +450,10 @@ do { \ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start, const char *str, int len) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); aa_state_t state = start; if (state == DFA_NOMATCH) @@ -434,10 +489,10 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start, */ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); aa_state_t state = start; if (state == DFA_NOMATCH) @@ -472,10 +527,10 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str) */ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); /* current state is , matching character *str */ if (dfa->tables[YYTD_ID_EC]) { @@ -490,10 +545,10 @@ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c) aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); u32 b = (base)[(state)]; if (!(b & MATCH_FLAG_OOB_TRANSITION)) @@ -521,10 +576,10 @@ aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state) aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start, const char *str, const char **retpos) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa); aa_state_t state = start, pos; @@ -582,10 +637,10 @@ aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start, aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start, const char *str, int n, const char **retpos) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa); aa_state_t state = start, pos; @@ -658,10 +713,10 @@ static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start, const char *str, struct match_workbuf *wb, unsigned int *count) { - u16 *def = DEFAULT_TABLE(dfa); + u32 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); - u16 *next = NEXT_TABLE(dfa); - u16 *check = CHECK_TABLE(dfa); + u32 *next = NEXT_TABLE(dfa); + u32 *check = CHECK_TABLE(dfa); aa_state_t state = start, pos; AA_BUG(!dfa); diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 45ec994b558d7..d6c74c357ffd5 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -130,7 +130,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, /* handle error conditions - and still allow a partial path to * be returned. */ - if (!res || IS_ERR(res)) { + if (IS_ERR_OR_NULL(res)) { if (PTR_ERR(res) == -ENAMETOOLONG) { error = -ENAMETOOLONG; *name = buf; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 14df15e356952..d0244fab06530 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -103,8 +103,7 @@ static void aa_free_pdb(struct aa_policydb *pdb) { if (pdb) { aa_put_dfa(pdb->dfa); - if (pdb->perms) - kvfree(pdb->perms); + kvfree(pdb->perms); aa_free_str_table(&pdb->trans); kfree(pdb); } @@ -580,11 +579,6 @@ struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, return profile; } -struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) -{ - return aa_lookupn_profile(ns, hname, strlen(hname)); -} - struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, const char *fqname, size_t n) { @@ -626,6 +620,7 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, /* TODO: ideally we should inherit abi from parent */ profile->label.flags |= FLAG_NULL; + profile->attach.xmatch = aa_get_pdb(nullpdb); rules = list_first_entry(&profile->rules, typeof(*rules), list); rules->file = aa_get_pdb(nullpdb); rules->policy = aa_get_pdb(nullpdb); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 3483c595f999f..992b74c50d641 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -645,10 +645,13 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_ruleset *rules) static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) { + u32 reserved; + if (version != 1) return false; - return aa_unpack_u32(e, &perm->allow, NULL) && + /* reserved entry is for later expansion, discard for now */ + return aa_unpack_u32(e, &reserved, NULL) && aa_unpack_u32(e, &perm->allow, NULL) && aa_unpack_u32(e, &perm->deny, NULL) && aa_unpack_u32(e, &perm->subtree, NULL) && diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index c64733d6c98fb..f070902da8fcc 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -281,6 +281,8 @@ static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test) ((uintptr_t)puf->e->start <= (uintptr_t)string) && ((uintptr_t)string <= (uintptr_t)puf->e->end)); KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); + + kfree(string); } static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test) @@ -296,6 +298,8 @@ static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test) ((uintptr_t)puf->e->start <= (uintptr_t)string) && ((uintptr_t)string <= (uintptr_t)puf->e->end)); KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); + + kfree(string); } static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test) @@ -313,6 +317,8 @@ static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test) KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_NULL(test, string); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); + + kfree(string); } static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test) diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c index 6350d107013a4..47dc08fc583e9 100644 --- a/security/apparmor/secid.c +++ b/security/apparmor/secid.c @@ -39,20 +39,6 @@ int apparmor_display_secid_mode; * TODO: use secid_update in label replace */ -/** - * aa_secid_update - update a secid mapping to a new label - * @secid: secid to update - * @label: label the secid will now map to - */ -void aa_secid_update(u32 secid, struct aa_label *label) -{ - unsigned long flags; - - xa_lock_irqsave(&aa_secids, flags); - __xa_store(&aa_secids, secid, label, 0); - xa_unlock_irqrestore(&aa_secids, flags); -} - /* * see label for inverse aa_label_to_secid */ diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index dbfd554b4624e..21a8e54c383f0 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -635,7 +635,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; - struct lsm_prop prop = { }; + struct lsm_prop inode_prop = { }; if (!lsm_rule->lsm[i].rule) { if (!lsm_rule->lsm[i].args_p) @@ -649,15 +649,16 @@ static bool ima_match_rules(struct ima_rule_entry *rule, case LSM_OBJ_USER: case LSM_OBJ_ROLE: case LSM_OBJ_TYPE: - security_inode_getlsmprop(inode, &prop); - rc = ima_filter_rule_match(&prop, lsm_rule->lsm[i].type, + security_inode_getlsmprop(inode, &inode_prop); + rc = ima_filter_rule_match(&inode_prop, + lsm_rule->lsm[i].type, Audit_equal, lsm_rule->lsm[i].rule); break; case LSM_SUBJ_USER: case LSM_SUBJ_ROLE: case LSM_SUBJ_TYPE: - rc = ima_filter_rule_match(&prop, lsm_rule->lsm[i].type, + rc = ima_filter_rule_match(prop, lsm_rule->lsm[i].type, Audit_equal, lsm_rule->lsm[i].rule); break; diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 849e832719e21..9a8352972086c 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -207,7 +207,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current)); - audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); + audit_log_untrustedstring(ab, get_task_comm(comm, current)); switch (a->type) { case LSM_AUDIT_DATA_NONE: @@ -302,7 +302,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, char comm[sizeof(tsk->comm)]; audit_log_format(ab, " opid=%d ocomm=", pid); audit_log_untrustedstring(ab, - memcpy(comm, tsk->comm, sizeof(comm))); + get_task_comm(comm, tsk)); } } break; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 234f4789b7874..6cd5bb0ba380c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -708,7 +708,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, if (new_value) { char comm[sizeof(current->comm)]; - memcpy(comm, current->comm, sizeof(comm)); + strscpy(comm, current->comm); pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n", comm, current->pid); } diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 96d4d7eb879f3..8dfffdc101a25 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -180,7 +180,7 @@ static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) /** * snd_ac97_codec_driver_register - register an AC97 codec driver - * @dev: AC97 driver codec to register + * @drv: AC97 driver codec to register * * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital * controller. @@ -196,7 +196,7 @@ EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register); /** * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver - * @dev: AC97 codec driver to unregister + * @drv: AC97 codec driver to unregister * * Unregister a previously registered ac97 codec driver. */ @@ -338,6 +338,7 @@ static int ac97_add_adapter(struct ac97_controller *ac97_ctrl) * @dev: the device providing the ac97 DC function * @slots_available: mask of the ac97 codecs that can be scanned and probed * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3 + * @codecs_pdata: codec platform data * * Register a digital controller which can control up to 4 ac97 codecs. This is * the controller side of the AC97 AC-link, while the slave side are the codecs. diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b44d205cc838e..8a3384342e8db 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3813,9 +3813,11 @@ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) return VM_FAULT_SIGBUS; if (substream->ops->page) page = substream->ops->page(substream, offset); - else if (!snd_pcm_get_dma_buf(substream)) + else if (!snd_pcm_get_dma_buf(substream)) { + if (WARN_ON_ONCE(!runtime->dma_area)) + return VM_FAULT_SIGBUS; page = virt_to_page(runtime->dma_area + offset); - else + } else page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset); if (!page) return VM_FAULT_SIGBUS; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 03306be5fa024..348ce1b7725ea 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -724,8 +724,9 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, newbuf = kvzalloc(params->buffer_size, GFP_KERNEL); if (!newbuf) return -ENOMEM; - guard(spinlock_irq)(&substream->lock); + spin_lock_irq(&substream->lock); if (runtime->buffer_ref) { + spin_unlock_irq(&substream->lock); kvfree(newbuf); return -EBUSY; } @@ -733,6 +734,7 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; __reset_runtime_ptrs(runtime, is_input); + spin_unlock_irq(&substream->lock); kvfree(oldbuf); } runtime->avail_min = params->avail_min; diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c index bfed1a25fc8f7..84e337ecbddd0 100644 --- a/sound/core/sound_kunit.c +++ b/sound/core/sound_kunit.c @@ -172,6 +172,7 @@ static void test_format_fill_silence(struct kunit *test) u32 i, j; buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer); for (i = 0; i < ARRAY_SIZE(buf_samples); i++) { for (j = 0; j < ARRAY_SIZE(valid_fmt); j++) @@ -208,8 +209,12 @@ static void test_playback_avail(struct kunit *test) struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); u32 i; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r); + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control); for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) { r->buffer_size = p_avail_data[i].buffer_size; @@ -232,8 +237,12 @@ static void test_capture_avail(struct kunit *test) struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); u32 i; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r); + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control); for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) { r->buffer_size = c_avail_data[i].buffer_size; @@ -247,6 +256,7 @@ static void test_capture_avail(struct kunit *test) static void test_card_set_id(struct kunit *test) { struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card); snd_card_set_id(card, VALID_NAME); KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME); @@ -280,6 +290,7 @@ static void test_pcm_format_name(struct kunit *test) static void test_card_add_component(struct kunit *test) { struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card); snd_component_add(card, TEST_FIRST_COMPONENT); KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT); diff --git a/sound/core/ump.c b/sound/core/ump.c index 5d4dd207e5ab2..6d0aac6c763fe 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -788,7 +788,10 @@ static void fill_fb_info(struct snd_ump_endpoint *ump, info->ui_hint = buf->fb_info.ui_hint; info->first_group = buf->fb_info.first_group; info->num_groups = buf->fb_info.num_groups; - info->flags = buf->fb_info.midi_10; + if (buf->fb_info.midi_10 < 2) + info->flags = buf->fb_info.midi_10; + else + info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED; info->active = buf->fb_info.active; info->midi_ci_version = buf->fb_info.midi_ci_version; info->sysex8_streams = buf->fb_info.sysex8_streams; diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index fdd4fe16225fc..5a84591b13fc2 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -464,7 +464,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, return -ENOMEM; } - err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, + hpi_stream_get_info_ex(dpcm->h_stream, NULL, &dpcm->hpi_buffer_attached, NULL, NULL, NULL); } bytes_per_sec = params_rate(params) * params_channels(params); diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 8e74be038b0fa..84393f4f429df 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -933,6 +933,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, bool match_all_pins) { const struct snd_hda_pin_quirk *pq; + const char *name = NULL; if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) return; @@ -946,9 +947,10 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; - codec_dbg(codec, "%s: picked fixup %s (pin match)\n", - codec->core.chip_name, codec->fixup_name); + name = pq->name; #endif + codec_info(codec, "%s: picked fixup %s (pin match)\n", + codec->core.chip_name, name ? name : ""); codec->fixup_list = fixlist; return; } @@ -1015,8 +1017,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { id = HDA_FIXUP_ID_NO_FIXUP; fixlist = NULL; - codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", - codec->core.chip_name); + codec_info(codec, "%s: picked no fixup (nofixup specified)\n", + codec->core.chip_name); goto found; } @@ -1026,8 +1028,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (!strcmp(codec->modelname, models->name)) { id = models->id; name = models->name; - codec_dbg(codec, "%s: picked fixup %s (model specified)\n", - codec->core.chip_name, codec->fixup_name); + codec_info(codec, "%s: picked fixup %s (model specified)\n", + codec->core.chip_name, name); goto found; } models++; @@ -1085,9 +1087,9 @@ void snd_hda_pick_fixup(struct hda_codec *codec, #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; #endif - codec_dbg(codec, "%s: picked fixup %s for %s %04x:%04x\n", - codec->core.chip_name, name ? name : "", - type, q->subvendor, q->subdevice); + codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n", + codec->core.chip_name, name ? name : "", + type, q->subvendor, q->subdevice); found: codec->fixup_id = id; codec->fixup_list = fixlist; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 56a3622ca2c1e..2bf5c512ebaf3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -473,6 +473,8 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) break; case 0x10ec0234: case 0x10ec0274: + alc_write_coef_idx(codec, 0x6e, 0x0c25); + fallthrough; case 0x10ec0294: case 0x10ec0700: case 0x10ec0701: @@ -3613,25 +3615,22 @@ static void alc256_init(struct hda_codec *codec) hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); + msleep(75); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(75); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); /* @@ -3655,29 +3654,28 @@ static void alc256_shutup(struct hda_codec *codec) alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); - snd_hda_codec_write(codec, hp_pin, 0, + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); + msleep(75); /* 3k pull low control for Headset jack. */ /* NOTE: call this before clearing the pin, otherwise codec stalls */ /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly * when booting with headset plugged. So skip setting it for the codec alc257 */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); + if (spec->en_3kpull_low) + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); + msleep(75); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); @@ -3772,33 +3770,28 @@ static void alc225_init(struct hda_codec *codec) hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - if (hp1_pin_sense || hp2_pin_sense) + if (hp1_pin_sense || hp2_pin_sense) { msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(85); - - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(100); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + msleep(75); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } } static void alc225_shutup(struct hda_codec *codec) @@ -3810,36 +3803,35 @@ static void alc225_shutup(struct hda_codec *codec) if (!hp_pin) hp_pin = 0x21; - alc_disable_headset_jack_key(codec); - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - if (hp1_pin_sense || hp2_pin_sense) + if (hp1_pin_sense || hp2_pin_sense) { + alc_disable_headset_jack_key(codec); + /* 3k pull low control for Headset jack. */ + alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); msleep(2); - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(85); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(75); - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(100); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_enable_headset_jack_key(codec); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); if (spec->ultra_low_power) { @@ -3850,9 +3842,6 @@ static void alc225_shutup(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); msleep(30); } - - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_enable_headset_jack_key(codec); } static void alc_default_init(struct hda_codec *codec) @@ -6502,6 +6491,16 @@ static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec, } } +/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */ +static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn[] = { 0x02, 0x03 }; + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + } +} + /* Hook to update amp GPIO4 for automute */ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, struct hda_jack_callback *jack) @@ -7559,6 +7558,7 @@ enum { ALC269_FIXUP_THINKPAD_ACPI, ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, + ALC269VC_FIXUP_INFINIX_Y4_MAX, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -7782,6 +7782,8 @@ enum { ALC256_FIXUP_CHROME_BOOK, ALC245_FIXUP_CLEVO_NOISY_MIC, ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, + ALC233_FIXUP_MEDION_MTL_SPK, + ALC294_FIXUP_BASS_SPEAKER_15, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -7945,6 +7947,15 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VC_FIXUP_INFINIX_Y4_MAX] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* use as internal speaker */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -10074,6 +10085,17 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC233_FIXUP_MEDION_MTL_SPK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, + { } + }, + }, + [ALC294_FIXUP_BASS_SPEAKER_15] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc294_fixup_bass_speaker_15, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -10499,6 +10521,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -10582,6 +10605,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), @@ -10943,7 +10967,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), + SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 370d847517f9a..45cfb5a6f309c 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -110,10 +111,20 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) return 1; } +static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false }; + +static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = { + { "speakerid-gpios", &speakerid_gpios, 1 }, + { } +}; + static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct acpi_device *adev; + struct device *physdev; LIST_HEAD(resources); + const char *sub; + uint32_t subid; int ret; adev = acpi_dev_get_first_match_dev(hid, NULL, -1); @@ -123,18 +134,45 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) return -ENODEV; } + physdev = get_device(acpi_get_first_physical_node(adev)); ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); - if (ret < 0) + if (ret < 0) { + dev_err(p->dev, "Failed to get ACPI resource.\n"); + goto err; + } + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) { + dev_err(p->dev, "Failed to get SUBSYS ID.\n"); goto err; + } + /* Speaker id was needed for ASUS projects. */ + ret = kstrtou32(sub, 16, &subid); + if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) { + ret = devm_acpi_dev_add_driver_gpios(p->dev, + tas2781_speaker_id_gpios); + if (ret < 0) + dev_err(p->dev, "Failed to add driver gpio %d.\n", + ret); + p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN); + if (IS_ERR(p->speaker_id)) { + dev_err(p->dev, "Failed to get Speaker id.\n"); + ret = PTR_ERR(p->speaker_id); + goto err; + } + } else { + p->speaker_id = NULL; + } acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); + put_device(physdev); acpi_dev_put(adev); return 0; err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); + put_device(physdev); acpi_dev_put(adev); return ret; @@ -615,7 +653,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) struct tasdevice_priv *tas_priv = context; struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); struct hda_codec *codec = tas_priv->codec; - int i, ret; + int i, ret, spk_id; pm_runtime_get_sync(tas_priv->dev); mutex_lock(&tas_priv->codec_lock); @@ -648,8 +686,25 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tasdevice_dsp_remove(tas_priv); tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X.bin", - codec->core.subsystem_id & 0xffff); + if (tas_priv->speaker_id != NULL) { + // Speaker id need to be checked for ASUS only. + spk_id = gpiod_get_value(tas_priv->speaker_id); + if (spk_id < 0) { + // Speaker id is not valid, use default. + dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id); + spk_id = 0; + } + snprintf(tas_priv->coef_binaryname, + sizeof(tas_priv->coef_binaryname), + "TAS2XXX%04X%d.bin", + lower_16_bits(codec->core.subsystem_id), + spk_id); + } else { + snprintf(tas_priv->coef_binaryname, + sizeof(tas_priv->coef_binaryname), + "TAS2XXX%04X.bin", + lower_16_bits(codec->core.subsystem_id)); + } ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index e62fb1ad6d77c..49b71082c4859 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2108,7 +2108,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) for (i=0; ivolumePhase[i] = 0; - err = pcim_iomap_regions_request_all(pci, 1 << 0, "korg1212"); + err = pcim_request_all_regions(pci, "korg1212"); if (err < 0) return err; @@ -2130,7 +2130,9 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->iomem2, iomem2_size, stateName[korg1212->cardState]); - korg1212->iobase = pcim_iomap_table(pci)[0]; + korg1212->iobase = pcim_iomap(pci, 0, 0); + if (!korg1212->iobase) + return -ENOMEM; err = devm_request_irq(&pci->dev, pci->irq, snd_korg1212_interrupt, IRQF_SHARED, diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 6dec44f516c13..c7590d4989bba 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -163,6 +163,7 @@ config SND_SOC_AMD_SOUNDWIRE config SND_SOC_AMD_PS tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support" select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + select SND_SOC_ACPI_AMD_MATCH depends on X86 && PCI && ACPI help This option enables Audio Coprocessor i.e ACP v6.3 support on diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 39208305dd6c3..e54eabaa4d3e1 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -231,6 +231,7 @@ struct sdw_dma_ring_buf_reg { * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled * @addr: pci ioremap address * @reg_range: ACP reigister range + * @acp_rev: ACP PCI revision id * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance */ @@ -254,6 +255,7 @@ struct acp63_dev_data { bool sdw_en_stat; u32 addr; u32 reg_range; + u32 acp_rev; u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; }; diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index a7583844f5b4e..aef73ec6f7ef4 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -267,6 +267,7 @@ static int amd_sdw_probe(struct device *dev) sdw_res.acp_lock = &acp_data->acp_lock; sdw_res.count = acp_data->info.count; sdw_res.mmio_base = acp_data->acp63_base; + sdw_res.acp_rev = acp_data->acp_rev; sdw_res.link_mask = acp_data->info.link_mask; ret = sdw_amd_probe(&sdw_res, &acp_data->sdw); if (ret) @@ -575,6 +576,7 @@ static int snd_acp63_probe(struct pci_dev *pci, } adata->addr = addr; adata->reg_range = ACP63_REG_END - ACP63_REG_START; + adata->acp_rev = pci->revision; pci_set_master(pci); pci_set_drvdata(pci, adata); mutex_init(&adata->acp_lock); diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 2436e8deb2be4..e38c5885dadfb 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -220,6 +220,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21J6"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21M1"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -241,6 +248,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21M5"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21ME"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -409,6 +423,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14 2022"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"), + DMI_MATCH(DMI_PRODUCT_NAME, "Redmi G 2022"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -537,8 +558,14 @@ static int acp6x_probe(struct platform_device *pdev) struct acp6x_pdm *machine = NULL; struct snd_soc_card *card; struct acpi_device *adev; + acpi_handle handle; + acpi_integer dmic_status; int ret; + bool is_dmic_enable, wov_en; + /* IF WOV entry not found, enable dmic based on AcpDmicConnected entry*/ + is_dmic_enable = false; + wov_en = true; /* check the parent device's firmware node has _DSD or not */ adev = ACPI_COMPANION(pdev->dev.parent); if (adev) { @@ -546,9 +573,19 @@ static int acp6x_probe(struct platform_device *pdev) if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) && obj->integer.value == 1) - platform_set_drvdata(pdev, &acp6x_card); + is_dmic_enable = true; } + handle = ACPI_HANDLE(pdev->dev.parent); + ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status); + if (!ACPI_FAILURE(ret)) + wov_en = dmic_status; + + if (is_dmic_enable && wov_en) + platform_set_drvdata(pdev, &acp6x_card); + else + return 0; + /* check for any DMI overrides */ dmi_id = dmi_first_match(yc_acp_quirk_table); if (dmi_id) diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c index c9e7d40c47cc1..b4f4696809dd2 100644 --- a/sound/soc/apple/mca.c +++ b/sound/soc/apple/mca.c @@ -616,7 +616,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, tdm_slot_width = 32; if (tdm_slot_width < params_width(params)) { - dev_err(dev, "TDM slots too narrow (tdm=%d params=%d)\n", + dev_err(dev, "TDM slots too narrow (tdm=%u params=%d)\n", tdm_slot_width, params_width(params)); return -EINVAL; } diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index dcf770b55c4bd..231400661c906 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -274,6 +274,9 @@ static int imx_audmix_probe(struct platform_device *pdev) /* Add AUDMIX Backend */ be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "audmix-%d", i); + if (!be_name) + return -ENOMEM; + priv->dai[num_dai + i].cpus = &dlc[1]; priv->dai[num_dai + i].codecs = &snd_soc_dummy_dlc; diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 84abdba9ddb6d..e04b88a57535c 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -1277,10 +1277,12 @@ static int mt8188_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, for_each_card_prelinks(card, i, dai_link) { if (strcmp(dai_link->name, "DPTX_BE") == 0) { - if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) + if (dai_link->num_codecs && + strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) dai_link->init = mt8188_dptx_codec_init; } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) + if (dai_link->num_codecs && + strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) dai_link->init = mt8188_hdmi_codec_init; } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || strcmp(dai_link->name, "UL_SRC_BE") == 0) { @@ -1292,6 +1294,9 @@ static int mt8188_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 || strcmp(dai_link->name, "ETDM1_IN_BE") == 0 || strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + if (!dai_link->num_codecs) + continue; + if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) { /* * The TDM protocol settings with fixed 4 slots are defined in diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 1aba9c75594eb..b1598cc5587e3 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -1091,7 +1091,7 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) dai_link->ignore = 0; } - if (dai_link->num_codecs && dai_link->codecs[0].dai_name && + if (dai_link->num_codecs && strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0) dai_link->ops = &mt8192_rt1015_i2s_ops; } @@ -1119,7 +1119,7 @@ static int mt8192_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, int i; for_each_card_prelinks(card, i, dai_link) - if (dai_link->num_codecs && dai_link->codecs[0].dai_name && + if (dai_link->num_codecs && strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0) dai_link->ops = &mt8192_rt1015_i2s_ops; } diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index 56b9d2433a1eb..2b9cb3248795b 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -1378,10 +1378,12 @@ static int mt8195_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, for_each_card_prelinks(card, i, dai_link) { if (strcmp(dai_link->name, "DPTX_BE") == 0) { - if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) + if (dai_link->num_codecs && + strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) dai_link->init = mt8195_dptx_codec_init; } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) + if (dai_link->num_codecs && + strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) dai_link->init = mt8195_hdmi_codec_init; } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || strcmp(dai_link->name, "UL_SRC1_BE") == 0 || @@ -1394,6 +1396,9 @@ static int mt8195_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data, strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 || strcmp(dai_link->name, "ETDM1_IN_BE") == 0 || strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + if (!dai_link->num_codecs) + continue; + if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) { if (!(codec_init & MAX98390_CODEC_INIT)) { dai_link->init = mt8195_max98390_init; diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 95d4762c9d939..f7814dadf3bac 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -693,6 +693,7 @@ static int amd_sof_sdw_probe(struct snd_sof_dev *sdev) sdw_res.count = acp_data->info.count; sdw_res.link_mask = acp_data->info.link_mask; sdw_res.mmio_base = sdev->bar[ACP_DSP_BAR]; + sdw_res.acp_rev = acp_data->pci_rev; ret = sdw_amd_probe(&sdw_res, &acp_data->sdw); if (ret) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index be61e377e59e0..c2fce554a674b 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -20,6 +20,9 @@ /* size of tplg ABI in bytes */ #define SOF_IPC3_TPLG_ABI_SIZE 3 +/* Base of SOF_DAI_INTEL_ALH, this should be aligned with SOC_SDW_INTEL_BIDIR_PDI_BASE */ +#define INTEL_ALH_DAI_INDEX_BASE 2 + struct sof_widget_data { int ctrl_type; int ipc_cmd; @@ -1594,6 +1597,17 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) if (ret < 0) goto free; + /* Subtract the base to match the FW dai index. */ + if (comp_dai->type == SOF_DAI_INTEL_ALH) { + if (comp_dai->dai_index < INTEL_ALH_DAI_INDEX_BASE) { + dev_err(sdev->dev, + "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n", + comp_dai->dai_index, INTEL_ALH_DAI_INDEX_BASE); + return -EINVAL; + } + comp_dai->dai_index -= INTEL_ALH_DAI_INDEX_BASE; + } + dev_dbg(scomp->dev, "dai %s: type %d index %d\n", swidget->widget->name, comp_dai->type, comp_dai->dai_index); sof_dbg_comp_config(scomp, &comp_dai->config); @@ -2167,8 +2181,16 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * case SOF_DAI_INTEL_ALH: if (data) { /* save the dai_index during hw_params and reuse it for hw_free */ - if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) - config->dai_index = data->dai_index; + if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { + /* Subtract the base to match the FW dai index. */ + if (data->dai_index < INTEL_ALH_DAI_INDEX_BASE) { + dev_err(sdev->dev, + "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n", + config->dai_index, INTEL_ALH_DAI_INDEX_BASE); + return -EINVAL; + } + config->dai_index = data->dai_index - INTEL_ALH_DAI_INDEX_BASE; + } config->alh.stream_id = data->dai_data; } break; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 8f85200292f3f..842ba5b801eae 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -36,6 +36,12 @@ union uac23_clock_multiplier_desc { struct uac_clock_multiplier_descriptor v3; }; +/* check whether the descriptor bLength has the minimal length */ +#define DESC_LENGTH_CHECK(p, proto) \ + ((proto) == UAC_VERSION_3 ? \ + ((p)->v3.bLength >= sizeof((p)->v3)) : \ + ((p)->v2.bLength >= sizeof((p)->v2))) + #define GET_VAL(p, proto, field) \ ((proto) == UAC_VERSION_3 ? (p)->v3.field : (p)->v2.field) @@ -58,6 +64,8 @@ static bool validate_clock_source(void *p, int id, int proto) { union uac23_clock_source_desc *cs = p; + if (!DESC_LENGTH_CHECK(cs, proto)) + return false; return GET_VAL(cs, proto, bClockID) == id; } @@ -65,13 +73,27 @@ static bool validate_clock_selector(void *p, int id, int proto) { union uac23_clock_selector_desc *cs = p; - return GET_VAL(cs, proto, bClockID) == id; + if (!DESC_LENGTH_CHECK(cs, proto)) + return false; + if (GET_VAL(cs, proto, bClockID) != id) + return false; + /* additional length check for baCSourceID array (in bNrInPins size) + * and two more fields (which sizes depend on the protocol) + */ + if (proto == UAC_VERSION_3) + return cs->v3.bLength >= sizeof(cs->v3) + cs->v3.bNrInPins + + 4 /* bmControls */ + 2 /* wCSelectorDescrStr */; + else + return cs->v2.bLength >= sizeof(cs->v2) + cs->v2.bNrInPins + + 1 /* bmControls */ + 1 /* iClockSelector */; } static bool validate_clock_multiplier(void *p, int id, int proto) { union uac23_clock_multiplier_desc *cs = p; + if (!DESC_LENGTH_CHECK(cs, proto)) + return false; return GET_VAL(cs, proto, bClockID) == id; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index cbfbb064a9c23..8bc959b60be3b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -555,6 +555,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf) { struct usb_host_config *config = dev->actconfig; + struct usb_device_descriptor new_device_descriptor; int err; if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD || @@ -566,10 +567,14 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac if (err < 0) dev_dbg(&dev->dev, "error sending boot message: %d\n", err); err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; + &new_device_descriptor, sizeof(new_device_descriptor)); if (err < 0) dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err); + if (new_device_descriptor.bNumConfigurations > dev->descriptor.bNumConfigurations) + dev_dbg(&dev->dev, "error too large bNumConfigurations: %d\n", + new_device_descriptor.bNumConfigurations); + else + memcpy(&dev->descriptor, &new_device_descriptor, sizeof(dev->descriptor)); err = usb_reset_configuration(dev); if (err < 0) dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err); @@ -901,6 +906,7 @@ static void mbox2_setup_48_24_magic(struct usb_device *dev) static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; + struct usb_device_descriptor new_device_descriptor; int err; u8 bootresponse[0x12]; int fwsize; @@ -936,10 +942,14 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "device initialised!\n"); err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; + &new_device_descriptor, sizeof(new_device_descriptor)); if (err < 0) dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err); + if (new_device_descriptor.bNumConfigurations > dev->descriptor.bNumConfigurations) + dev_dbg(&dev->dev, "error too large bNumConfigurations: %d\n", + new_device_descriptor.bNumConfigurations); + else + memcpy(&dev->descriptor, &new_device_descriptor, sizeof(dev->descriptor)); err = usb_reset_configuration(dev); if (err < 0) @@ -1249,6 +1259,7 @@ static void mbox3_setup_defaults(struct usb_device *dev) static int snd_usb_mbox3_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; + struct usb_device_descriptor new_device_descriptor; int err; int descriptor_size; @@ -1262,10 +1273,14 @@ static int snd_usb_mbox3_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "MBOX3: device initialised!\n"); err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - &dev->descriptor, sizeof(dev->descriptor)); - config = dev->actconfig; + &new_device_descriptor, sizeof(new_device_descriptor)); if (err < 0) dev_dbg(&dev->dev, "MBOX3: error usb_get_descriptor: %d\n", err); + if (new_device_descriptor.bNumConfigurations > dev->descriptor.bNumConfigurations) + dev_dbg(&dev->dev, "MBOX3: error too large bNumConfigurations: %d\n", + new_device_descriptor.bNumConfigurations); + else + memcpy(&dev->descriptor, &new_device_descriptor, sizeof(dev->descriptor)); err = usb_reset_configuration(dev); if (err < 0) diff --git a/tools/arch/arm64/include/asm/brk-imm.h b/tools/arch/arm64/include/asm/brk-imm.h new file mode 100644 index 0000000000000..beb42c62b6acc --- /dev/null +++ b/tools/arch/arm64/include/asm/brk-imm.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2012 ARM Ltd. + */ + +#ifndef __ASM_BRK_IMM_H +#define __ASM_BRK_IMM_H + +/* + * #imm16 values used for BRK instruction generation + * 0x004: for installing kprobes + * 0x005: for installing uprobes + * 0x006: for kprobe software single-step + * 0x007: for kretprobe return + * Allowed values for kgdb are 0x400 - 0x7ff + * 0x100: for triggering a fault on purpose (reserved) + * 0x400: for dynamic BRK instruction + * 0x401: for compile time BRK instruction + * 0x800: kernel-mode BUG() and WARN() traps + * 0x9xx: tag-based KASAN trap (allowed values 0x900 - 0x9ff) + * 0x55xx: Undefined Behavior Sanitizer traps ('U' << 8) + * 0x8xxx: Control-Flow Integrity traps + */ +#define KPROBES_BRK_IMM 0x004 +#define UPROBES_BRK_IMM 0x005 +#define KPROBES_BRK_SS_IMM 0x006 +#define KRETPROBES_BRK_IMM 0x007 +#define FAULT_BRK_IMM 0x100 +#define KGDB_DYN_DBG_BRK_IMM 0x400 +#define KGDB_COMPILED_DBG_BRK_IMM 0x401 +#define BUG_BRK_IMM 0x800 +#define KASAN_BRK_IMM 0x900 +#define KASAN_BRK_MASK 0x0ff +#define UBSAN_BRK_IMM 0x5500 +#define UBSAN_BRK_MASK 0x00ff + +#define CFI_BRK_IMM_TARGET GENMASK(4, 0) +#define CFI_BRK_IMM_TYPE GENMASK(9, 5) +#define CFI_BRK_IMM_BASE 0x8000 +#define CFI_BRK_IMM_MASK (CFI_BRK_IMM_TARGET | CFI_BRK_IMM_TYPE) + +#endif diff --git a/tools/arch/arm64/include/asm/esr.h b/tools/arch/arm64/include/asm/esr.h new file mode 100644 index 0000000000000..bd592ca815711 --- /dev/null +++ b/tools/arch/arm64/include/asm/esr.h @@ -0,0 +1,455 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - ARM Ltd + * Author: Marc Zyngier + */ + +#ifndef __ASM_ESR_H +#define __ASM_ESR_H + +#include + +#define ESR_ELx_EC_UNKNOWN UL(0x00) +#define ESR_ELx_EC_WFx UL(0x01) +/* Unallocated EC: 0x02 */ +#define ESR_ELx_EC_CP15_32 UL(0x03) +#define ESR_ELx_EC_CP15_64 UL(0x04) +#define ESR_ELx_EC_CP14_MR UL(0x05) +#define ESR_ELx_EC_CP14_LS UL(0x06) +#define ESR_ELx_EC_FP_ASIMD UL(0x07) +#define ESR_ELx_EC_CP10_ID UL(0x08) /* EL2 only */ +#define ESR_ELx_EC_PAC UL(0x09) /* EL2 and above */ +/* Unallocated EC: 0x0A - 0x0B */ +#define ESR_ELx_EC_CP14_64 UL(0x0C) +#define ESR_ELx_EC_BTI UL(0x0D) +#define ESR_ELx_EC_ILL UL(0x0E) +/* Unallocated EC: 0x0F - 0x10 */ +#define ESR_ELx_EC_SVC32 UL(0x11) +#define ESR_ELx_EC_HVC32 UL(0x12) /* EL2 only */ +#define ESR_ELx_EC_SMC32 UL(0x13) /* EL2 and above */ +/* Unallocated EC: 0x14 */ +#define ESR_ELx_EC_SVC64 UL(0x15) +#define ESR_ELx_EC_HVC64 UL(0x16) /* EL2 and above */ +#define ESR_ELx_EC_SMC64 UL(0x17) /* EL2 and above */ +#define ESR_ELx_EC_SYS64 UL(0x18) +#define ESR_ELx_EC_SVE UL(0x19) +#define ESR_ELx_EC_ERET UL(0x1a) /* EL2 only */ +/* Unallocated EC: 0x1B */ +#define ESR_ELx_EC_FPAC UL(0x1C) /* EL1 and above */ +#define ESR_ELx_EC_SME UL(0x1D) +/* Unallocated EC: 0x1E */ +#define ESR_ELx_EC_IMP_DEF UL(0x1f) /* EL3 only */ +#define ESR_ELx_EC_IABT_LOW UL(0x20) +#define ESR_ELx_EC_IABT_CUR UL(0x21) +#define ESR_ELx_EC_PC_ALIGN UL(0x22) +/* Unallocated EC: 0x23 */ +#define ESR_ELx_EC_DABT_LOW UL(0x24) +#define ESR_ELx_EC_DABT_CUR UL(0x25) +#define ESR_ELx_EC_SP_ALIGN UL(0x26) +#define ESR_ELx_EC_MOPS UL(0x27) +#define ESR_ELx_EC_FP_EXC32 UL(0x28) +/* Unallocated EC: 0x29 - 0x2B */ +#define ESR_ELx_EC_FP_EXC64 UL(0x2C) +/* Unallocated EC: 0x2D - 0x2E */ +#define ESR_ELx_EC_SERROR UL(0x2F) +#define ESR_ELx_EC_BREAKPT_LOW UL(0x30) +#define ESR_ELx_EC_BREAKPT_CUR UL(0x31) +#define ESR_ELx_EC_SOFTSTP_LOW UL(0x32) +#define ESR_ELx_EC_SOFTSTP_CUR UL(0x33) +#define ESR_ELx_EC_WATCHPT_LOW UL(0x34) +#define ESR_ELx_EC_WATCHPT_CUR UL(0x35) +/* Unallocated EC: 0x36 - 0x37 */ +#define ESR_ELx_EC_BKPT32 UL(0x38) +/* Unallocated EC: 0x39 */ +#define ESR_ELx_EC_VECTOR32 UL(0x3A) /* EL2 only */ +/* Unallocated EC: 0x3B */ +#define ESR_ELx_EC_BRK64 UL(0x3C) +/* Unallocated EC: 0x3D - 0x3F */ +#define ESR_ELx_EC_MAX UL(0x3F) + +#define ESR_ELx_EC_SHIFT (26) +#define ESR_ELx_EC_WIDTH (6) +#define ESR_ELx_EC_MASK (UL(0x3F) << ESR_ELx_EC_SHIFT) +#define ESR_ELx_EC(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT) + +#define ESR_ELx_IL_SHIFT (25) +#define ESR_ELx_IL (UL(1) << ESR_ELx_IL_SHIFT) +#define ESR_ELx_ISS_MASK (GENMASK(24, 0)) +#define ESR_ELx_ISS(esr) ((esr) & ESR_ELx_ISS_MASK) +#define ESR_ELx_ISS2_SHIFT (32) +#define ESR_ELx_ISS2_MASK (GENMASK_ULL(55, 32)) +#define ESR_ELx_ISS2(esr) (((esr) & ESR_ELx_ISS2_MASK) >> ESR_ELx_ISS2_SHIFT) + +/* ISS field definitions shared by different classes */ +#define ESR_ELx_WNR_SHIFT (6) +#define ESR_ELx_WNR (UL(1) << ESR_ELx_WNR_SHIFT) + +/* Asynchronous Error Type */ +#define ESR_ELx_IDS_SHIFT (24) +#define ESR_ELx_IDS (UL(1) << ESR_ELx_IDS_SHIFT) +#define ESR_ELx_AET_SHIFT (10) +#define ESR_ELx_AET (UL(0x7) << ESR_ELx_AET_SHIFT) + +#define ESR_ELx_AET_UC (UL(0) << ESR_ELx_AET_SHIFT) +#define ESR_ELx_AET_UEU (UL(1) << ESR_ELx_AET_SHIFT) +#define ESR_ELx_AET_UEO (UL(2) << ESR_ELx_AET_SHIFT) +#define ESR_ELx_AET_UER (UL(3) << ESR_ELx_AET_SHIFT) +#define ESR_ELx_AET_CE (UL(6) << ESR_ELx_AET_SHIFT) + +/* Shared ISS field definitions for Data/Instruction aborts */ +#define ESR_ELx_SET_SHIFT (11) +#define ESR_ELx_SET_MASK (UL(3) << ESR_ELx_SET_SHIFT) +#define ESR_ELx_FnV_SHIFT (10) +#define ESR_ELx_FnV (UL(1) << ESR_ELx_FnV_SHIFT) +#define ESR_ELx_EA_SHIFT (9) +#define ESR_ELx_EA (UL(1) << ESR_ELx_EA_SHIFT) +#define ESR_ELx_S1PTW_SHIFT (7) +#define ESR_ELx_S1PTW (UL(1) << ESR_ELx_S1PTW_SHIFT) + +/* Shared ISS fault status code(IFSC/DFSC) for Data/Instruction aborts */ +#define ESR_ELx_FSC (0x3F) +#define ESR_ELx_FSC_TYPE (0x3C) +#define ESR_ELx_FSC_LEVEL (0x03) +#define ESR_ELx_FSC_EXTABT (0x10) +#define ESR_ELx_FSC_MTE (0x11) +#define ESR_ELx_FSC_SERROR (0x11) +#define ESR_ELx_FSC_ACCESS (0x08) +#define ESR_ELx_FSC_FAULT (0x04) +#define ESR_ELx_FSC_PERM (0x0C) +#define ESR_ELx_FSC_SEA_TTW(n) (0x14 + (n)) +#define ESR_ELx_FSC_SECC (0x18) +#define ESR_ELx_FSC_SECC_TTW(n) (0x1c + (n)) + +/* Status codes for individual page table levels */ +#define ESR_ELx_FSC_ACCESS_L(n) (ESR_ELx_FSC_ACCESS + (n)) +#define ESR_ELx_FSC_PERM_L(n) (ESR_ELx_FSC_PERM + (n)) + +#define ESR_ELx_FSC_FAULT_nL (0x2C) +#define ESR_ELx_FSC_FAULT_L(n) (((n) < 0 ? ESR_ELx_FSC_FAULT_nL : \ + ESR_ELx_FSC_FAULT) + (n)) + +/* ISS field definitions for Data Aborts */ +#define ESR_ELx_ISV_SHIFT (24) +#define ESR_ELx_ISV (UL(1) << ESR_ELx_ISV_SHIFT) +#define ESR_ELx_SAS_SHIFT (22) +#define ESR_ELx_SAS (UL(3) << ESR_ELx_SAS_SHIFT) +#define ESR_ELx_SSE_SHIFT (21) +#define ESR_ELx_SSE (UL(1) << ESR_ELx_SSE_SHIFT) +#define ESR_ELx_SRT_SHIFT (16) +#define ESR_ELx_SRT_MASK (UL(0x1F) << ESR_ELx_SRT_SHIFT) +#define ESR_ELx_SF_SHIFT (15) +#define ESR_ELx_SF (UL(1) << ESR_ELx_SF_SHIFT) +#define ESR_ELx_AR_SHIFT (14) +#define ESR_ELx_AR (UL(1) << ESR_ELx_AR_SHIFT) +#define ESR_ELx_CM_SHIFT (8) +#define ESR_ELx_CM (UL(1) << ESR_ELx_CM_SHIFT) + +/* ISS2 field definitions for Data Aborts */ +#define ESR_ELx_TnD_SHIFT (10) +#define ESR_ELx_TnD (UL(1) << ESR_ELx_TnD_SHIFT) +#define ESR_ELx_TagAccess_SHIFT (9) +#define ESR_ELx_TagAccess (UL(1) << ESR_ELx_TagAccess_SHIFT) +#define ESR_ELx_GCS_SHIFT (8) +#define ESR_ELx_GCS (UL(1) << ESR_ELx_GCS_SHIFT) +#define ESR_ELx_Overlay_SHIFT (6) +#define ESR_ELx_Overlay (UL(1) << ESR_ELx_Overlay_SHIFT) +#define ESR_ELx_DirtyBit_SHIFT (5) +#define ESR_ELx_DirtyBit (UL(1) << ESR_ELx_DirtyBit_SHIFT) +#define ESR_ELx_Xs_SHIFT (0) +#define ESR_ELx_Xs_MASK (GENMASK_ULL(4, 0)) + +/* ISS field definitions for exceptions taken in to Hyp */ +#define ESR_ELx_FSC_ADDRSZ (0x00) +#define ESR_ELx_FSC_ADDRSZ_L(n) (ESR_ELx_FSC_ADDRSZ + (n)) +#define ESR_ELx_CV (UL(1) << 24) +#define ESR_ELx_COND_SHIFT (20) +#define ESR_ELx_COND_MASK (UL(0xF) << ESR_ELx_COND_SHIFT) +#define ESR_ELx_WFx_ISS_RN (UL(0x1F) << 5) +#define ESR_ELx_WFx_ISS_RV (UL(1) << 2) +#define ESR_ELx_WFx_ISS_TI (UL(3) << 0) +#define ESR_ELx_WFx_ISS_WFxT (UL(2) << 0) +#define ESR_ELx_WFx_ISS_WFI (UL(0) << 0) +#define ESR_ELx_WFx_ISS_WFE (UL(1) << 0) +#define ESR_ELx_xVC_IMM_MASK ((UL(1) << 16) - 1) + +#define DISR_EL1_IDS (UL(1) << 24) +/* + * DISR_EL1 and ESR_ELx share the bottom 13 bits, but the RES0 bits may mean + * different things in the future... + */ +#define DISR_EL1_ESR_MASK (ESR_ELx_AET | ESR_ELx_EA | ESR_ELx_FSC) + +/* ESR value templates for specific events */ +#define ESR_ELx_WFx_MASK (ESR_ELx_EC_MASK | \ + (ESR_ELx_WFx_ISS_TI & ~ESR_ELx_WFx_ISS_WFxT)) +#define ESR_ELx_WFx_WFI_VAL ((ESR_ELx_EC_WFx << ESR_ELx_EC_SHIFT) | \ + ESR_ELx_WFx_ISS_WFI) + +/* BRK instruction trap from AArch64 state */ +#define ESR_ELx_BRK64_ISS_COMMENT_MASK 0xffff + +/* ISS field definitions for System instruction traps */ +#define ESR_ELx_SYS64_ISS_RES0_SHIFT 22 +#define ESR_ELx_SYS64_ISS_RES0_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_RES0_SHIFT) +#define ESR_ELx_SYS64_ISS_DIR_MASK 0x1 +#define ESR_ELx_SYS64_ISS_DIR_READ 0x1 +#define ESR_ELx_SYS64_ISS_DIR_WRITE 0x0 + +#define ESR_ELx_SYS64_ISS_RT_SHIFT 5 +#define ESR_ELx_SYS64_ISS_RT_MASK (UL(0x1f) << ESR_ELx_SYS64_ISS_RT_SHIFT) +#define ESR_ELx_SYS64_ISS_CRM_SHIFT 1 +#define ESR_ELx_SYS64_ISS_CRM_MASK (UL(0xf) << ESR_ELx_SYS64_ISS_CRM_SHIFT) +#define ESR_ELx_SYS64_ISS_CRN_SHIFT 10 +#define ESR_ELx_SYS64_ISS_CRN_MASK (UL(0xf) << ESR_ELx_SYS64_ISS_CRN_SHIFT) +#define ESR_ELx_SYS64_ISS_OP1_SHIFT 14 +#define ESR_ELx_SYS64_ISS_OP1_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_OP1_SHIFT) +#define ESR_ELx_SYS64_ISS_OP2_SHIFT 17 +#define ESR_ELx_SYS64_ISS_OP2_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_OP2_SHIFT) +#define ESR_ELx_SYS64_ISS_OP0_SHIFT 20 +#define ESR_ELx_SYS64_ISS_OP0_MASK (UL(0x3) << ESR_ELx_SYS64_ISS_OP0_SHIFT) +#define ESR_ELx_SYS64_ISS_SYS_MASK (ESR_ELx_SYS64_ISS_OP0_MASK | \ + ESR_ELx_SYS64_ISS_OP1_MASK | \ + ESR_ELx_SYS64_ISS_OP2_MASK | \ + ESR_ELx_SYS64_ISS_CRN_MASK | \ + ESR_ELx_SYS64_ISS_CRM_MASK) +#define ESR_ELx_SYS64_ISS_SYS_VAL(op0, op1, op2, crn, crm) \ + (((op0) << ESR_ELx_SYS64_ISS_OP0_SHIFT) | \ + ((op1) << ESR_ELx_SYS64_ISS_OP1_SHIFT) | \ + ((op2) << ESR_ELx_SYS64_ISS_OP2_SHIFT) | \ + ((crn) << ESR_ELx_SYS64_ISS_CRN_SHIFT) | \ + ((crm) << ESR_ELx_SYS64_ISS_CRM_SHIFT)) + +#define ESR_ELx_SYS64_ISS_SYS_OP_MASK (ESR_ELx_SYS64_ISS_SYS_MASK | \ + ESR_ELx_SYS64_ISS_DIR_MASK) +#define ESR_ELx_SYS64_ISS_RT(esr) \ + (((esr) & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT) +/* + * User space cache operations have the following sysreg encoding + * in System instructions. + * op0=1, op1=3, op2=1, crn=7, crm={ 5, 10, 11, 12, 13, 14 }, WRITE (L=0) + */ +#define ESR_ELx_SYS64_ISS_CRM_DC_CIVAC 14 +#define ESR_ELx_SYS64_ISS_CRM_DC_CVADP 13 +#define ESR_ELx_SYS64_ISS_CRM_DC_CVAP 12 +#define ESR_ELx_SYS64_ISS_CRM_DC_CVAU 11 +#define ESR_ELx_SYS64_ISS_CRM_DC_CVAC 10 +#define ESR_ELx_SYS64_ISS_CRM_IC_IVAU 5 + +#define ESR_ELx_SYS64_ISS_EL0_CACHE_OP_MASK (ESR_ELx_SYS64_ISS_OP0_MASK | \ + ESR_ELx_SYS64_ISS_OP1_MASK | \ + ESR_ELx_SYS64_ISS_OP2_MASK | \ + ESR_ELx_SYS64_ISS_CRN_MASK | \ + ESR_ELx_SYS64_ISS_DIR_MASK) +#define ESR_ELx_SYS64_ISS_EL0_CACHE_OP_VAL \ + (ESR_ELx_SYS64_ISS_SYS_VAL(1, 3, 1, 7, 0) | \ + ESR_ELx_SYS64_ISS_DIR_WRITE) +/* + * User space MRS operations which are supported for emulation + * have the following sysreg encoding in System instructions. + * op0 = 3, op1= 0, crn = 0, {crm = 0, 4-7}, READ (L = 1) + */ +#define ESR_ELx_SYS64_ISS_SYS_MRS_OP_MASK (ESR_ELx_SYS64_ISS_OP0_MASK | \ + ESR_ELx_SYS64_ISS_OP1_MASK | \ + ESR_ELx_SYS64_ISS_CRN_MASK | \ + ESR_ELx_SYS64_ISS_DIR_MASK) +#define ESR_ELx_SYS64_ISS_SYS_MRS_OP_VAL \ + (ESR_ELx_SYS64_ISS_SYS_VAL(3, 0, 0, 0, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + +#define ESR_ELx_SYS64_ISS_SYS_CTR ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 1, 0, 0) +#define ESR_ELx_SYS64_ISS_SYS_CTR_READ (ESR_ELx_SYS64_ISS_SYS_CTR | \ + ESR_ELx_SYS64_ISS_DIR_READ) + +#define ESR_ELx_SYS64_ISS_SYS_CNTVCT (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 2, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + +#define ESR_ELx_SYS64_ISS_SYS_CNTVCTSS (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 6, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + +#define ESR_ELx_SYS64_ISS_SYS_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + +#define esr_sys64_to_sysreg(e) \ + sys_reg((((e) & ESR_ELx_SYS64_ISS_OP0_MASK) >> \ + ESR_ELx_SYS64_ISS_OP0_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_OP1_MASK) >> \ + ESR_ELx_SYS64_ISS_OP1_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_CRN_MASK) >> \ + ESR_ELx_SYS64_ISS_CRN_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_CRM_MASK) >> \ + ESR_ELx_SYS64_ISS_CRM_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> \ + ESR_ELx_SYS64_ISS_OP2_SHIFT)) + +#define esr_cp15_to_sysreg(e) \ + sys_reg(3, \ + (((e) & ESR_ELx_SYS64_ISS_OP1_MASK) >> \ + ESR_ELx_SYS64_ISS_OP1_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_CRN_MASK) >> \ + ESR_ELx_SYS64_ISS_CRN_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_CRM_MASK) >> \ + ESR_ELx_SYS64_ISS_CRM_SHIFT), \ + (((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> \ + ESR_ELx_SYS64_ISS_OP2_SHIFT)) + +/* ISS field definitions for ERET/ERETAA/ERETAB trapping */ +#define ESR_ELx_ERET_ISS_ERET 0x2 +#define ESR_ELx_ERET_ISS_ERETA 0x1 + +/* + * ISS field definitions for floating-point exception traps + * (FP_EXC_32/FP_EXC_64). + * + * (The FPEXC_* constants are used instead for common bits.) + */ + +#define ESR_ELx_FP_EXC_TFV (UL(1) << 23) + +/* + * ISS field definitions for CP15 accesses + */ +#define ESR_ELx_CP15_32_ISS_DIR_MASK 0x1 +#define ESR_ELx_CP15_32_ISS_DIR_READ 0x1 +#define ESR_ELx_CP15_32_ISS_DIR_WRITE 0x0 + +#define ESR_ELx_CP15_32_ISS_RT_SHIFT 5 +#define ESR_ELx_CP15_32_ISS_RT_MASK (UL(0x1f) << ESR_ELx_CP15_32_ISS_RT_SHIFT) +#define ESR_ELx_CP15_32_ISS_CRM_SHIFT 1 +#define ESR_ELx_CP15_32_ISS_CRM_MASK (UL(0xf) << ESR_ELx_CP15_32_ISS_CRM_SHIFT) +#define ESR_ELx_CP15_32_ISS_CRN_SHIFT 10 +#define ESR_ELx_CP15_32_ISS_CRN_MASK (UL(0xf) << ESR_ELx_CP15_32_ISS_CRN_SHIFT) +#define ESR_ELx_CP15_32_ISS_OP1_SHIFT 14 +#define ESR_ELx_CP15_32_ISS_OP1_MASK (UL(0x7) << ESR_ELx_CP15_32_ISS_OP1_SHIFT) +#define ESR_ELx_CP15_32_ISS_OP2_SHIFT 17 +#define ESR_ELx_CP15_32_ISS_OP2_MASK (UL(0x7) << ESR_ELx_CP15_32_ISS_OP2_SHIFT) + +#define ESR_ELx_CP15_32_ISS_SYS_MASK (ESR_ELx_CP15_32_ISS_OP1_MASK | \ + ESR_ELx_CP15_32_ISS_OP2_MASK | \ + ESR_ELx_CP15_32_ISS_CRN_MASK | \ + ESR_ELx_CP15_32_ISS_CRM_MASK | \ + ESR_ELx_CP15_32_ISS_DIR_MASK) +#define ESR_ELx_CP15_32_ISS_SYS_VAL(op1, op2, crn, crm) \ + (((op1) << ESR_ELx_CP15_32_ISS_OP1_SHIFT) | \ + ((op2) << ESR_ELx_CP15_32_ISS_OP2_SHIFT) | \ + ((crn) << ESR_ELx_CP15_32_ISS_CRN_SHIFT) | \ + ((crm) << ESR_ELx_CP15_32_ISS_CRM_SHIFT)) + +#define ESR_ELx_CP15_64_ISS_DIR_MASK 0x1 +#define ESR_ELx_CP15_64_ISS_DIR_READ 0x1 +#define ESR_ELx_CP15_64_ISS_DIR_WRITE 0x0 + +#define ESR_ELx_CP15_64_ISS_RT_SHIFT 5 +#define ESR_ELx_CP15_64_ISS_RT_MASK (UL(0x1f) << ESR_ELx_CP15_64_ISS_RT_SHIFT) + +#define ESR_ELx_CP15_64_ISS_RT2_SHIFT 10 +#define ESR_ELx_CP15_64_ISS_RT2_MASK (UL(0x1f) << ESR_ELx_CP15_64_ISS_RT2_SHIFT) + +#define ESR_ELx_CP15_64_ISS_OP1_SHIFT 16 +#define ESR_ELx_CP15_64_ISS_OP1_MASK (UL(0xf) << ESR_ELx_CP15_64_ISS_OP1_SHIFT) +#define ESR_ELx_CP15_64_ISS_CRM_SHIFT 1 +#define ESR_ELx_CP15_64_ISS_CRM_MASK (UL(0xf) << ESR_ELx_CP15_64_ISS_CRM_SHIFT) + +#define ESR_ELx_CP15_64_ISS_SYS_VAL(op1, crm) \ + (((op1) << ESR_ELx_CP15_64_ISS_OP1_SHIFT) | \ + ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT)) + +#define ESR_ELx_CP15_64_ISS_SYS_MASK (ESR_ELx_CP15_64_ISS_OP1_MASK | \ + ESR_ELx_CP15_64_ISS_CRM_MASK | \ + ESR_ELx_CP15_64_ISS_DIR_MASK) + +#define ESR_ELx_CP15_64_ISS_SYS_CNTVCT (ESR_ELx_CP15_64_ISS_SYS_VAL(1, 14) | \ + ESR_ELx_CP15_64_ISS_DIR_READ) + +#define ESR_ELx_CP15_64_ISS_SYS_CNTVCTSS (ESR_ELx_CP15_64_ISS_SYS_VAL(9, 14) | \ + ESR_ELx_CP15_64_ISS_DIR_READ) + +#define ESR_ELx_CP15_32_ISS_SYS_CNTFRQ (ESR_ELx_CP15_32_ISS_SYS_VAL(0, 0, 14, 0) |\ + ESR_ELx_CP15_32_ISS_DIR_READ) + +/* + * ISS values for SME traps + */ + +#define ESR_ELx_SME_ISS_SME_DISABLED 0 +#define ESR_ELx_SME_ISS_ILL 1 +#define ESR_ELx_SME_ISS_SM_DISABLED 2 +#define ESR_ELx_SME_ISS_ZA_DISABLED 3 +#define ESR_ELx_SME_ISS_ZT_DISABLED 4 + +/* ISS field definitions for MOPS exceptions */ +#define ESR_ELx_MOPS_ISS_MEM_INST (UL(1) << 24) +#define ESR_ELx_MOPS_ISS_FROM_EPILOGUE (UL(1) << 18) +#define ESR_ELx_MOPS_ISS_WRONG_OPTION (UL(1) << 17) +#define ESR_ELx_MOPS_ISS_OPTION_A (UL(1) << 16) +#define ESR_ELx_MOPS_ISS_DESTREG(esr) (((esr) & (UL(0x1f) << 10)) >> 10) +#define ESR_ELx_MOPS_ISS_SRCREG(esr) (((esr) & (UL(0x1f) << 5)) >> 5) +#define ESR_ELx_MOPS_ISS_SIZEREG(esr) (((esr) & (UL(0x1f) << 0)) >> 0) + +#ifndef __ASSEMBLY__ +#include + +static inline unsigned long esr_brk_comment(unsigned long esr) +{ + return esr & ESR_ELx_BRK64_ISS_COMMENT_MASK; +} + +static inline bool esr_is_data_abort(unsigned long esr) +{ + const unsigned long ec = ESR_ELx_EC(esr); + + return ec == ESR_ELx_EC_DABT_LOW || ec == ESR_ELx_EC_DABT_CUR; +} + +static inline bool esr_is_cfi_brk(unsigned long esr) +{ + return ESR_ELx_EC(esr) == ESR_ELx_EC_BRK64 && + (esr_brk_comment(esr) & ~CFI_BRK_IMM_MASK) == CFI_BRK_IMM_BASE; +} + +static inline bool esr_fsc_is_translation_fault(unsigned long esr) +{ + esr = esr & ESR_ELx_FSC; + + return (esr == ESR_ELx_FSC_FAULT_L(3)) || + (esr == ESR_ELx_FSC_FAULT_L(2)) || + (esr == ESR_ELx_FSC_FAULT_L(1)) || + (esr == ESR_ELx_FSC_FAULT_L(0)) || + (esr == ESR_ELx_FSC_FAULT_L(-1)); +} + +static inline bool esr_fsc_is_permission_fault(unsigned long esr) +{ + esr = esr & ESR_ELx_FSC; + + return (esr == ESR_ELx_FSC_PERM_L(3)) || + (esr == ESR_ELx_FSC_PERM_L(2)) || + (esr == ESR_ELx_FSC_PERM_L(1)) || + (esr == ESR_ELx_FSC_PERM_L(0)); +} + +static inline bool esr_fsc_is_access_flag_fault(unsigned long esr) +{ + esr = esr & ESR_ELx_FSC; + + return (esr == ESR_ELx_FSC_ACCESS_L(3)) || + (esr == ESR_ELx_FSC_ACCESS_L(2)) || + (esr == ESR_ELx_FSC_ACCESS_L(1)) || + (esr == ESR_ELx_FSC_ACCESS_L(0)); +} + +/* Indicate whether ESR.EC==0x1A is for an ERETAx instruction */ +static inline bool esr_iss_is_eretax(unsigned long esr) +{ + return esr & ESR_ELx_ERET_ISS_ERET; +} + +/* Indicate which key is used for ERETAx (false: A-Key, true: B-Key) */ +static inline bool esr_iss_is_eretab(unsigned long esr) +{ + return esr & ESR_ELx_ERET_ISS_ERETA; +} + +const char *esr_get_class_string(unsigned long esr); +#endif /* __ASSEMBLY */ + +#endif /* __ASM_ESR_H */ diff --git a/tools/arch/s390/include/uapi/asm/kvm.h b/tools/arch/s390/include/uapi/asm/kvm.h index 05eaf6db3ad4c..60345dd2cba2d 100644 --- a/tools/arch/s390/include/uapi/asm/kvm.h +++ b/tools/arch/s390/include/uapi/asm/kvm.h @@ -469,7 +469,8 @@ struct kvm_s390_vm_cpu_subfunc { __u8 kdsa[16]; /* with MSA9 */ __u8 sortl[32]; /* with STFLE.150 */ __u8 dfltcc[32]; /* with STFLE.151 */ - __u8 reserved[1728]; + __u8 pfcr[16]; /* with STFLE.201 */ + __u8 reserved[1712]; }; #define KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST 6 diff --git a/tools/bpf/bpftool/pids.c b/tools/bpf/bpftool/pids.c index 9b898571b49e9..23f488cf17403 100644 --- a/tools/bpf/bpftool/pids.c +++ b/tools/bpf/bpftool/pids.c @@ -54,6 +54,7 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e) ref = &refs->refs[refs->ref_cnt]; ref->pid = e->pid; memcpy(ref->comm, e->comm, sizeof(ref->comm)); + ref->comm[sizeof(ref->comm) - 1] = '\0'; refs->ref_cnt++; return; @@ -77,6 +78,7 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e) ref = &refs->refs[0]; ref->pid = e->pid; memcpy(ref->comm, e->comm, sizeof(ref->comm)); + ref->comm[sizeof(ref->comm) - 1] = '\0'; refs->ref_cnt = 1; refs->has_bpf_cookie = e->has_bpf_cookie; refs->bpf_cookie = e->bpf_cookie; diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 2ebfb826dcea5..bca47d136f058 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -30,9 +30,7 @@ endef # FEATURE_TESTS_BASIC := \ backtrace \ - dwarf \ - dwarf_getlocations \ - dwarf_getcfi \ + libdw \ eventfd \ fortify-source \ get_current_dir_name \ @@ -61,7 +59,6 @@ FEATURE_TESTS_BASIC := \ reallocarray \ stackprotector-all \ timerfd \ - libdw-dwarf-unwind \ zlib \ lzma \ get_cpuid \ @@ -121,8 +118,7 @@ ifeq ($(FEATURE_TESTS),all) endif FEATURE_DISPLAY ?= \ - dwarf \ - dwarf_getlocations \ + libdw \ glibc \ libbfd \ libbfd-buildid \ @@ -134,7 +130,6 @@ FEATURE_DISPLAY ?= \ libpython \ libcrypto \ libunwind \ - libdw-dwarf-unwind \ libcapstone \ llvm-perf \ zlib \ @@ -234,7 +229,7 @@ endef # # generates feature value assignment for name, like: -# $(call feature_assign,dwarf) == feature-dwarf=1 +# $(call feature_assign,libdw) == feature-libdw=1 # feature_assign = feature-$(1)=$(feature-$(1)) diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 28cdfb11a7553..043dfd00fce72 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -5,9 +5,7 @@ FILES= \ test-all.bin \ test-backtrace.bin \ test-bionic.bin \ - test-dwarf.bin \ - test-dwarf_getlocations.bin \ - test-dwarf_getcfi.bin \ + test-libdw.bin \ test-eventfd.bin \ test-fortify-source.bin \ test-get_current_dir_name.bin \ @@ -53,7 +51,6 @@ FILES= \ test-pthread-barrier.bin \ test-stackprotector-all.bin \ test-timerfd.bin \ - test-libdw-dwarf-unwind.bin \ test-libbabeltrace.bin \ test-libcapstone.bin \ test-compile-32.bin \ @@ -169,9 +166,9 @@ $(OUTPUT)test-libopencsd.bin: $(BUILD) # -lopencsd_c_api -lopencsd provided by # $(FEATURE_CHECK_LDFLAGS-libopencsd) -DWARFLIBS := -ldw +DWLIBS := -ldw ifeq ($(findstring -static,${LDFLAGS}),-static) - DWARFLIBS += -lelf -lz -llzma -lbz2 -lzstd + DWLIBS += -lelf -lz -llzma -lbz2 -lzstd LIBDW_VERSION := $(shell $(PKG_CONFIG) --modversion libdw).0.0 LIBDW_VERSION_1 := $(word 1, $(subst ., ,$(LIBDW_VERSION))) @@ -180,21 +177,15 @@ ifeq ($(findstring -static,${LDFLAGS}),-static) # Elfutils merged libebl.a into libdw.a starting from version 0.177, # Link libebl.a only if libdw is older than this version. ifeq ($(shell test $(LIBDW_VERSION_2) -lt 177; echo $$?),0) - DWARFLIBS += -lebl + DWLIBS += -lebl endif # Must put -ldl after -lebl for dependency DWARFLIBS += -ldl endif -$(OUTPUT)test-dwarf.bin: - $(BUILD) $(DWARFLIBS) - -$(OUTPUT)test-dwarf_getlocations.bin: - $(BUILD) $(DWARFLIBS) - -$(OUTPUT)test-dwarf_getcfi.bin: - $(BUILD) $(DWARFLIBS) +$(OUTPUT)test-libdw.bin: + $(BUILD) $(DWLIBS) $(OUTPUT)test-libelf-getphdrnum.bin: $(BUILD) -lelf @@ -321,9 +312,6 @@ $(OUTPUT)test-backtrace.bin: $(OUTPUT)test-timerfd.bin: $(BUILD) -$(OUTPUT)test-libdw-dwarf-unwind.bin: - $(BUILD) # -ldw provided by $(FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind) - $(OUTPUT)test-libbabeltrace.bin: $(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace) diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index 6f4bf386a3b5c..59ef3d7fe6a4e 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c @@ -38,12 +38,8 @@ # include "test-glibc.c" #undef main -#define main main_test_dwarf -# include "test-dwarf.c" -#undef main - -#define main main_test_dwarf_getlocations -# include "test-dwarf_getlocations.c" +#define main main_test_libdw +# include "test-libdw.c" #undef main #define main main_test_eventfd @@ -98,10 +94,6 @@ # include "test-stackprotector-all.c" #undef main -#define main main_test_libdw_dwarf_unwind -# include "test-libdw-dwarf-unwind.c" -#undef main - #define main main_test_zlib # include "test-zlib.c" #undef main @@ -187,8 +179,7 @@ int main(int argc, char *argv[]) main_test_get_current_dir_name(); main_test_gettid(); main_test_glibc(); - main_test_dwarf(); - main_test_dwarf_getlocations(); + main_test_libdw(); main_test_eventfd(); main_test_libelf_getphdrnum(); main_test_libelf_gelf_getnote(); @@ -202,7 +193,6 @@ int main(int argc, char *argv[]) main_test_numa_num_possible_cpus(); main_test_timerfd(); main_test_stackprotector_all(); - main_test_libdw_dwarf_unwind(); main_test_zlib(); main_test_pthread_attr_setaffinity_np(); main_test_pthread_barrier(); diff --git a/tools/build/feature/test-dwarf.c b/tools/build/feature/test-dwarf.c deleted file mode 100644 index 8d474bd7371b2..0000000000000 --- a/tools/build/feature/test-dwarf.c +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include - -int main(void) -{ - Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); - - return (long)dbg; -} diff --git a/tools/build/feature/test-dwarf_getcfi.c b/tools/build/feature/test-dwarf_getcfi.c deleted file mode 100644 index 50e7d7cb7bdf9..0000000000000 --- a/tools/build/feature/test-dwarf_getcfi.c +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -int main(void) -{ - Dwarf *dwarf = NULL; - return dwarf_getcfi(dwarf) == NULL; -} diff --git a/tools/build/feature/test-dwarf_getlocations.c b/tools/build/feature/test-dwarf_getlocations.c deleted file mode 100644 index 78fb4a1fa68c3..0000000000000 --- a/tools/build/feature/test-dwarf_getlocations.c +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -int main(void) -{ - Dwarf_Addr base, start, end; - Dwarf_Attribute attr; - Dwarf_Op *op; - size_t nops; - ptrdiff_t offset = 0; - return (int)dwarf_getlocations(&attr, offset, &base, &start, &end, &op, &nops); -} diff --git a/tools/build/feature/test-libdw-dwarf-unwind.c b/tools/build/feature/test-libdw-dwarf-unwind.c deleted file mode 100644 index ed03d95056090..0000000000000 --- a/tools/build/feature/test-libdw-dwarf-unwind.c +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include - -int main(void) -{ - /* - * This function is guarded via: __nonnull_attribute__ (1, 2). - * Passing '1' as arguments value. This code is never executed, - * only compiled. - */ - dwfl_thread_getframes((void *) 1, (void *) 1, NULL); - return 0; -} diff --git a/tools/build/feature/test-libdw.c b/tools/build/feature/test-libdw.c new file mode 100644 index 0000000000000..2fb59479ab771 --- /dev/null +++ b/tools/build/feature/test-libdw.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +int test_libdw(void) +{ + Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + + return (long)dbg; +} + +int test_libdw_unwind(void) +{ + /* + * This function is guarded via: __nonnull_attribute__ (1, 2). + * Passing '1' as arguments value. This code is never executed, + * only compiled. + */ + dwfl_thread_getframes((void *) 1, (void *) 1, NULL); + return 0; +} + +int test_libdw_getlocations(void) +{ + Dwarf_Addr base, start, end; + Dwarf_Attribute attr; + Dwarf_Op *op; + size_t nops; + ptrdiff_t offset = 0; + + return (int)dwarf_getlocations(&attr, offset, &base, &start, &end, &op, &nops); +} + +int test_libdw_getcfi(void) +{ + Dwarf *dwarf = NULL; + + return dwarf_getcfi(dwarf) == NULL; +} + +int test_elfutils(void) +{ + Dwarf_CFI *cfi = NULL; + + dwarf_cfi_end(cfi); + return 0; +} + +int main(void) +{ + return test_libdw() + test_libdw_unwind() + test_libdw_getlocations() + + test_libdw_getcfi() + test_elfutils(); +} diff --git a/tools/build/feature/test-libtraceevent.c b/tools/build/feature/test-libtraceevent.c index 416b11ffd4b4f..804ad80dbbd93 100644 --- a/tools/build/feature/test-libtraceevent.c +++ b/tools/build/feature/test-libtraceevent.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include +#include int main(void) { diff --git a/tools/firewire/decode-fcp.c b/tools/firewire/decode-fcp.c index b67ebc88434d9..f115a3be8d1ef 100644 --- a/tools/firewire/decode-fcp.c +++ b/tools/firewire/decode-fcp.c @@ -160,7 +160,7 @@ decode_avc(struct link_transaction *t) name = info->name; } - printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s", + printf("av/c %s, subunit_type=%s, subunit_id=%u, opcode=%s", ctype_names[frame->ctype], subunit_type_names[frame->subunit_type], frame->subunit_id, name); diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index 156e0356e8147..9a906de3a9efa 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c @@ -771,7 +771,7 @@ print_packet(uint32_t *data, size_t length) if (pp->phy_config.set_root) printf(" set_root_id=%02x", pp->phy_config.root_id); if (pp->phy_config.set_gap_count) - printf(" set_gap_count=%d", pp->phy_config.gap_count); + printf(" set_gap_count=%u", pp->phy_config.gap_count); } break; @@ -781,13 +781,13 @@ print_packet(uint32_t *data, size_t length) case PHY_PACKET_SELF_ID: if (pp->self_id.extended) { - printf("extended self id: phy_id=%02x, seq=%d", + printf("extended self id: phy_id=%02x, seq=%u", pp->ext_self_id.phy_id, pp->ext_self_id.sequence); } else { static const char * const speed_names[] = { "S100", "S200", "S400", "BETA" }; - printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s", + printf("self id: phy_id=%02x, link %s, gap_count=%u speed=%s%s%s", pp->self_id.phy_id, (pp->self_id.link_active ? "active" : "not active"), pp->self_id.gap_count, diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 8073c9e4fe46a..cccf62ea2b8f9 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -63,6 +63,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_DELTA_VELOCITY] = "deltavelocity", [IIO_COLORTEMP] = "colortemp", [IIO_CHROMATICITY] = "chromaticity", + [IIO_ATTENTION] = "attention", }; static const char * const iio_ev_type_text[] = { @@ -183,6 +184,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_DELTA_VELOCITY: case IIO_COLORTEMP: case IIO_CHROMATICITY: + case IIO_ATTENTION: break; default: return false; @@ -449,6 +451,7 @@ int main(int argc, char **argv) enable_events(dev_dir_name, 0); free(chrdev_name); + free(dev_dir_name); return ret; } diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h index 62e7c901ac28c..e20f98e14e814 100644 --- a/tools/include/linux/compiler-gcc.h +++ b/tools/include/linux/compiler-gcc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _TOOLS_LINUX_COMPILER_H_ -#error "Please don't include directly, include instead." +#error "Please do not include directly, include instead." #endif /* diff --git a/tools/lib/api/io.h b/tools/lib/api/io.h index d3eb04d1bc897..1731996b2c32a 100644 --- a/tools/lib/api/io.h +++ b/tools/lib/api/io.h @@ -189,6 +189,7 @@ static inline ssize_t io__getdelim(struct io *io, char **line_out, size_t *line_ err_out: free(line); *line_out = NULL; + *line_len_out = 0; return -ENOMEM; } diff --git a/tools/lib/list_sort.c b/tools/lib/list_sort.c index 69affa251fa78..bb99e493dcd10 100644 --- a/tools/lib/list_sort.c +++ b/tools/lib/list_sort.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include #include -#include #include #include diff --git a/tools/lib/perf/Documentation/Makefile b/tools/lib/perf/Documentation/Makefile index 972754082a854..573ca5b275567 100644 --- a/tools/lib/perf/Documentation/Makefile +++ b/tools/lib/perf/Documentation/Makefile @@ -121,7 +121,7 @@ install-man: all $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir); \ $(INSTALL) -m 644 $(MAN_7) $(DESTDIR)$(man7dir); -install-html: +install-html: $(MAN_HTML) $(call QUIET_INSTALL, html) \ $(INSTALL) -d -m 755 $(DESTDIR)$(htmldir); \ $(INSTALL) -m 644 $(MAN_HTML) $(DESTDIR)$(htmldir); \ diff --git a/tools/lib/perf/evsel.c b/tools/lib/perf/evsel.c index c07160953224a..c475319e2e410 100644 --- a/tools/lib/perf/evsel.c +++ b/tools/lib/perf/evsel.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr, int idx) { INIT_LIST_HEAD(&evsel->node); + INIT_LIST_HEAD(&evsel->per_stream_periods); evsel->attr = *attr; evsel->idx = idx; evsel->leader = evsel; @@ -531,10 +533,56 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) void perf_evsel__free_id(struct perf_evsel *evsel) { + struct perf_sample_id_period *pos, *n; + xyarray__delete(evsel->sample_id); evsel->sample_id = NULL; zfree(&evsel->id); evsel->ids = 0; + + perf_evsel_for_each_per_thread_period_safe(evsel, n, pos) { + list_del_init(&pos->node); + free(pos); + } +} + +bool perf_evsel__attr_has_per_thread_sample_period(struct perf_evsel *evsel) +{ + return (evsel->attr.sample_type & PERF_SAMPLE_READ) && + (evsel->attr.sample_type & PERF_SAMPLE_TID) && + evsel->attr.inherit; +} + +u64 *perf_sample_id__get_period_storage(struct perf_sample_id *sid, u32 tid, bool per_thread) +{ + struct hlist_head *head; + struct perf_sample_id_period *res; + int hash; + + if (!per_thread) + return &sid->period; + + hash = hash_32(tid, PERF_SAMPLE_ID__HLIST_BITS); + head = &sid->periods[hash]; + + hlist_for_each_entry(res, head, hnode) + if (res->tid == tid) + return &res->period; + + if (sid->evsel == NULL) + return NULL; + + res = zalloc(sizeof(struct perf_sample_id_period)); + if (res == NULL) + return NULL; + + INIT_LIST_HEAD(&res->node); + res->tid = tid; + + list_add_tail(&res->node, &sid->evsel->per_stream_periods); + hlist_add_head(&res->hnode, &sid->periods[hash]); + + return &res->period; } void perf_counts_values__scale(struct perf_counts_values *count, diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h index 5cd220a61962e..ea78defa77d0b 100644 --- a/tools/lib/perf/include/internal/evsel.h +++ b/tools/lib/perf/include/internal/evsel.h @@ -11,6 +11,32 @@ struct perf_thread_map; struct xyarray; +/** + * The per-thread accumulated period storage node. + */ +struct perf_sample_id_period { + struct list_head node; + struct hlist_node hnode; + /* Holds total ID period value for PERF_SAMPLE_READ processing. */ + u64 period; + /* The TID that the values belongs to */ + u32 tid; +}; + +/** + * perf_evsel_for_each_per_thread_period_safe - safely iterate thru all the + * per_stream_periods + * @evlist:perf_evsel instance to iterate + * @item: struct perf_sample_id_period iterator + * @tmp: struct perf_sample_id_period temp iterator + */ +#define perf_evsel_for_each_per_thread_period_safe(evsel, tmp, item) \ + list_for_each_entry_safe(item, tmp, &(evsel)->per_stream_periods, node) + + +#define PERF_SAMPLE_ID__HLIST_BITS 4 +#define PERF_SAMPLE_ID__HLIST_SIZE (1 << PERF_SAMPLE_ID__HLIST_BITS) + /* * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are * more than one entry in the evlist. @@ -34,8 +60,32 @@ struct perf_sample_id { pid_t machine_pid; struct perf_cpu vcpu; - /* Holds total ID period value for PERF_SAMPLE_READ processing. */ - u64 period; + /* + * Per-thread, and global event counts are mutually exclusive: + * Whilst it is possible to combine events into a group with differing + * values of PERF_SAMPLE_READ, it is not valid to have inconsistent + * values for `inherit`. Therefore it is not possible to have a + * situation where a per-thread event is sampled as a global event; + * all !inherit groups are global, and all groups where the sampling + * event is inherit + PERF_SAMPLE_READ will be per-thread. Any event + * that is part of such a group that is inherit but not PERF_SAMPLE_READ + * will be read as per-thread. If such an event can also trigger a + * sample (such as with sample_period > 0) then it will not cause + * `read_format` to be included in its PERF_RECORD_SAMPLE, and + * therefore will not expose the per-thread group members as global. + */ + union { + /* + * Holds total ID period value for PERF_SAMPLE_READ processing + * (when period is not per-thread). + */ + u64 period; + /* + * Holds total ID period value for PERF_SAMPLE_READ processing + * (when period is per-thread). + */ + struct hlist_head periods[PERF_SAMPLE_ID__HLIST_SIZE]; + }; }; struct perf_evsel { @@ -58,6 +108,10 @@ struct perf_evsel { u32 ids; struct perf_evsel *leader; + /* For events where the read_format value is per-thread rather than + * global, stores the per-thread cumulative period */ + struct list_head per_stream_periods; + /* parse modifier helper */ int nr_members; /* @@ -88,4 +142,9 @@ int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter); int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); void perf_evsel__free_id(struct perf_evsel *evsel); +bool perf_evsel__attr_has_per_thread_sample_period(struct perf_evsel *evsel); + +u64 *perf_sample_id__get_period_storage(struct perf_sample_id *sid, u32 tid, + bool per_thread); + #endif /* __LIBPERF_INTERNAL_EVSEL_H */ diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c index 4e3a557a2f374..0a764c25c384f 100644 --- a/tools/lib/subcmd/run-command.c +++ b/tools/lib/subcmd/run-command.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -217,8 +218,40 @@ static int wait_or_whine(struct child_process *cmd, bool block) int check_if_command_finished(struct child_process *cmd) { +#ifdef __linux__ + char filename[FILENAME_MAX + 12]; + char status_line[256]; + FILE *status_file; + + /* + * Check by reading /proc//status as calling waitpid causes + * stdout/stderr to be closed and data lost. + */ + sprintf(filename, "/proc/%d/status", cmd->pid); + status_file = fopen(filename, "r"); + if (status_file == NULL) { + /* Open failed assume finish_command was called. */ + return true; + } + while (fgets(status_line, sizeof(status_line), status_file) != NULL) { + char *p; + + if (strncmp(status_line, "State:", 6)) + continue; + + fclose(status_file); + p = status_line + 6; + while (isspace(*p)) + p++; + return *p == 'Z' ? 1 : 0; + } + /* Read failed assume finish_command was called. */ + fclose(status_file); + return 1; +#else wait_or_whine(cmd, /*block=*/false); return cmd->finished; +#endif } int finish_command(struct child_process *cmd) diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h index dfac76e35ac73..c742b08815dcb 100644 --- a/tools/lib/subcmd/subcmd-util.h +++ b/tools/lib/subcmd/subcmd-util.h @@ -20,8 +20,8 @@ static __noreturn inline void die(const char *err, ...) va_start(params, err); report(" Fatal: ", err, params); - exit(128); va_end(params); + exit(128); } #define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) diff --git a/tools/net/sunrpc/extract.sh b/tools/net/sunrpc/extract.sh new file mode 100755 index 0000000000000..f944066f25bcc --- /dev/null +++ b/tools/net/sunrpc/extract.sh @@ -0,0 +1,11 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Extract an RPC protocol specification from an RFC document. +# The version of this script comes from RFC 8166. +# +# Usage: +# $ extract.sh < rfcNNNN.txt > protocol.x +# + +grep '^ *///' | sed 's?^ */// ??' | sed 's?^ *///$??' diff --git a/tools/net/sunrpc/xdrgen/README b/tools/net/sunrpc/xdrgen/README index 92f7738ad50c3..27218a78ab402 100644 --- a/tools/net/sunrpc/xdrgen/README +++ b/tools/net/sunrpc/xdrgen/README @@ -150,6 +150,23 @@ Pragma directives specify exceptions to the normal generation of encoding and decoding functions. Currently one directive is implemented: "public". +Pragma big_endian +------ ---------- + + pragma big_endian ; + +For variables that might contain only a small number values, it +is more efficient to avoid the byte-swap when encoding or decoding +on little-endian machines. Such is often the case with error status +codes. For example: + + pragma big_endian nfsstat3; + +In this case, when generating an XDR struct or union containing a +field of type "nfsstat3", xdrgen will make the type of that field +"__be32" instead of "enum nfsstat3". XDR unions then switch on the +non-byte-swapped value of that field. + Pragma exclude ------ ------- diff --git a/tools/net/sunrpc/xdrgen/generators/__init__.py b/tools/net/sunrpc/xdrgen/generators/__init__.py index fd24574612742..b98574a36a4ac 100644 --- a/tools/net/sunrpc/xdrgen/generators/__init__.py +++ b/tools/net/sunrpc/xdrgen/generators/__init__.py @@ -111,3 +111,7 @@ def emit_definition(self, node: _XdrAst) -> None: def emit_encoder(self, node: _XdrAst) -> None: """Emit one encoder function for this XDR type""" raise NotImplementedError("Encoder generation not supported") + + def emit_maxsize(self, node: _XdrAst) -> None: + """Emit one maxsize macro for this XDR type""" + raise NotImplementedError("Maxsize macro generation not supported") diff --git a/tools/net/sunrpc/xdrgen/generators/enum.py b/tools/net/sunrpc/xdrgen/generators/enum.py index 855e43f4ae38e..e62f715d3996f 100644 --- a/tools/net/sunrpc/xdrgen/generators/enum.py +++ b/tools/net/sunrpc/xdrgen/generators/enum.py @@ -4,7 +4,7 @@ """Generate code to handle XDR enum types""" from generators import SourceGenerator, create_jinja2_environment -from xdr_ast import _XdrEnum, public_apis +from xdr_ast import _XdrEnum, public_apis, big_endian, get_header_name class XdrEnumGenerator(SourceGenerator): @@ -18,7 +18,7 @@ def __init__(self, language: str, peer: str): def emit_declaration(self, node: _XdrEnum) -> None: """Emit one declaration pair for an XDR enum type""" if node.name in public_apis: - template = self.environment.get_template("declaration/close.j2") + template = self.environment.get_template("declaration/enum.j2") print(template.render(name=node.name)) def emit_definition(self, node: _XdrEnum) -> None: @@ -30,15 +30,35 @@ def emit_definition(self, node: _XdrEnum) -> None: for enumerator in node.enumerators: print(template.render(name=enumerator.name, value=enumerator.value)) - template = self.environment.get_template("definition/close.j2") + if node.name in big_endian: + template = self.environment.get_template("definition/close_be.j2") + else: + template = self.environment.get_template("definition/close.j2") print(template.render(name=node.name)) def emit_decoder(self, node: _XdrEnum) -> None: """Emit one decoder function for an XDR enum type""" - template = self.environment.get_template("decoder/enum.j2") + if node.name in big_endian: + template = self.environment.get_template("decoder/enum_be.j2") + else: + template = self.environment.get_template("decoder/enum.j2") print(template.render(name=node.name)) def emit_encoder(self, node: _XdrEnum) -> None: """Emit one encoder function for an XDR enum type""" - template = self.environment.get_template("encoder/enum.j2") + if node.name in big_endian: + template = self.environment.get_template("encoder/enum_be.j2") + else: + template = self.environment.get_template("encoder/enum.j2") print(template.render(name=node.name)) + + def emit_maxsize(self, node: _XdrEnum) -> None: + """Emit one maxsize macro for an XDR enum type""" + macro_name = get_header_name().upper() + "_" + node.name + "_sz" + template = self.environment.get_template("maxsize/enum.j2") + print( + template.render( + macro=macro_name, + width=" + ".join(node.symbolic_width()), + ) + ) diff --git a/tools/net/sunrpc/xdrgen/generators/pointer.py b/tools/net/sunrpc/xdrgen/generators/pointer.py index b0b27f1819c8c..6dbda60ad2db4 100644 --- a/tools/net/sunrpc/xdrgen/generators/pointer.py +++ b/tools/net/sunrpc/xdrgen/generators/pointer.py @@ -8,11 +8,11 @@ from generators import SourceGenerator, kernel_c_type from generators import create_jinja2_environment, get_jinja2_template -from xdr_ast import _XdrBasic, _XdrVariableLengthString +from xdr_ast import _XdrBasic, _XdrString from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration -from xdr_ast import public_apis +from xdr_ast import public_apis, get_header_name def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None: @@ -46,7 +46,7 @@ def emit_pointer_member_definition( elif isinstance(field, _XdrVariableLengthOpaque): template = get_jinja2_template(environment, "definition", field.template) print(template.render(name=field.name)) - elif isinstance(field, _XdrVariableLengthString): + elif isinstance(field, _XdrString): template = get_jinja2_template(environment, "definition", field.template) print(template.render(name=field.name)) elif isinstance(field, _XdrFixedLengthArray): @@ -119,7 +119,7 @@ def emit_pointer_member_decoder( maxsize=field.maxsize, ) ) - elif isinstance(field, _XdrVariableLengthString): + elif isinstance(field, _XdrString): template = get_jinja2_template(environment, "decoder", field.template) print( template.render( @@ -198,7 +198,7 @@ def emit_pointer_member_encoder( maxsize=field.maxsize, ) ) - elif isinstance(field, _XdrVariableLengthString): + elif isinstance(field, _XdrString): template = get_jinja2_template(environment, "encoder", field.template) print( template.render( @@ -247,6 +247,18 @@ def emit_pointer_encoder(environment: Environment, node: _XdrPointer) -> None: print(template.render()) +def emit_pointer_maxsize(environment: Environment, node: _XdrPointer) -> None: + """Emit one maxsize macro for an XDR pointer type""" + macro_name = get_header_name().upper() + "_" + node.name + "_sz" + template = get_jinja2_template(environment, "maxsize", "pointer") + print( + template.render( + macro=macro_name, + width=" + ".join(node.symbolic_width()), + ) + ) + + class XdrPointerGenerator(SourceGenerator): """Generate source code for XDR pointer""" @@ -270,3 +282,7 @@ def emit_decoder(self, node: _XdrPointer) -> None: def emit_encoder(self, node: _XdrPointer) -> None: """Emit one encoder function for an XDR pointer type""" emit_pointer_encoder(self.environment, node) + + def emit_maxsize(self, node: _XdrPointer) -> None: + """Emit one maxsize macro for an XDR pointer type""" + emit_pointer_maxsize(self.environment, node) diff --git a/tools/net/sunrpc/xdrgen/generators/struct.py b/tools/net/sunrpc/xdrgen/generators/struct.py index b694cd4708290..64911de46f623 100644 --- a/tools/net/sunrpc/xdrgen/generators/struct.py +++ b/tools/net/sunrpc/xdrgen/generators/struct.py @@ -8,11 +8,11 @@ from generators import SourceGenerator, kernel_c_type from generators import create_jinja2_environment, get_jinja2_template -from xdr_ast import _XdrBasic, _XdrVariableLengthString +from xdr_ast import _XdrBasic, _XdrString from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration -from xdr_ast import public_apis +from xdr_ast import public_apis, get_header_name def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None: @@ -46,7 +46,7 @@ def emit_struct_member_definition( elif isinstance(field, _XdrVariableLengthOpaque): template = get_jinja2_template(environment, "definition", field.template) print(template.render(name=field.name)) - elif isinstance(field, _XdrVariableLengthString): + elif isinstance(field, _XdrString): template = get_jinja2_template(environment, "definition", field.template) print(template.render(name=field.name)) elif isinstance(field, _XdrFixedLengthArray): @@ -119,7 +119,7 @@ def emit_struct_member_decoder( maxsize=field.maxsize, ) ) - elif isinstance(field, _XdrVariableLengthString): + elif isinstance(field, _XdrString): template = get_jinja2_template(environment, "decoder", field.template) print( template.render( @@ -198,7 +198,7 @@ def emit_struct_member_encoder( maxsize=field.maxsize, ) ) - elif isinstance(field, _XdrVariableLengthString): + elif isinstance(field, _XdrString): template = get_jinja2_template(environment, "encoder", field.template) print( template.render( @@ -247,6 +247,18 @@ def emit_struct_encoder(environment: Environment, node: _XdrStruct) -> None: print(template.render()) +def emit_struct_maxsize(environment: Environment, node: _XdrStruct) -> None: + """Emit one maxsize macro for an XDR struct type""" + macro_name = get_header_name().upper() + "_" + node.name + "_sz" + template = get_jinja2_template(environment, "maxsize", "struct") + print( + template.render( + macro=macro_name, + width=" + ".join(node.symbolic_width()), + ) + ) + + class XdrStructGenerator(SourceGenerator): """Generate source code for XDR structs""" @@ -270,3 +282,7 @@ def emit_decoder(self, node: _XdrStruct) -> None: def emit_encoder(self, node: _XdrStruct) -> None: """Emit one encoder function for an XDR struct type""" emit_struct_encoder(self.environment, node) + + def emit_maxsize(self, node: _XdrStruct) -> None: + """Emit one maxsize macro for an XDR struct type""" + emit_struct_maxsize(self.environment, node) diff --git a/tools/net/sunrpc/xdrgen/generators/typedef.py b/tools/net/sunrpc/xdrgen/generators/typedef.py index 85a1b23033339..fab72e9d69158 100644 --- a/tools/net/sunrpc/xdrgen/generators/typedef.py +++ b/tools/net/sunrpc/xdrgen/generators/typedef.py @@ -8,11 +8,11 @@ from generators import SourceGenerator, kernel_c_type from generators import create_jinja2_environment, get_jinja2_template -from xdr_ast import _XdrBasic, _XdrTypedef, _XdrVariableLengthString +from xdr_ast import _XdrBasic, _XdrTypedef, _XdrString from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray from xdr_ast import _XdrOptionalData, _XdrVoid, _XdrDeclaration -from xdr_ast import public_apis +from xdr_ast import public_apis, get_header_name def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> None: @@ -28,7 +28,7 @@ def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> classifier=node.spec.c_classifier, ) ) - elif isinstance(node, _XdrVariableLengthString): + elif isinstance(node, _XdrString): template = get_jinja2_template(environment, "declaration", node.template) print(template.render(name=node.name)) elif isinstance(node, _XdrFixedLengthOpaque): @@ -74,7 +74,7 @@ def emit_type_definition(environment: Environment, node: _XdrDeclaration) -> Non classifier=node.spec.c_classifier, ) ) - elif isinstance(node, _XdrVariableLengthString): + elif isinstance(node, _XdrString): template = get_jinja2_template(environment, "definition", node.template) print(template.render(name=node.name)) elif isinstance(node, _XdrFixedLengthOpaque): @@ -119,7 +119,7 @@ def emit_typedef_decoder(environment: Environment, node: _XdrDeclaration) -> Non type=node.spec.type_name, ) ) - elif isinstance(node, _XdrVariableLengthString): + elif isinstance(node, _XdrString): template = get_jinja2_template(environment, "decoder", node.template) print( template.render( @@ -180,7 +180,7 @@ def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> Non type=node.spec.type_name, ) ) - elif isinstance(node, _XdrVariableLengthString): + elif isinstance(node, _XdrString): template = get_jinja2_template(environment, "encoder", node.template) print( template.render( @@ -230,6 +230,18 @@ def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> Non raise NotImplementedError("typedef: type not recognized") +def emit_typedef_maxsize(environment: Environment, node: _XdrDeclaration) -> None: + """Emit a maxsize macro for an XDR typedef""" + macro_name = get_header_name().upper() + "_" + node.name + "_sz" + template = get_jinja2_template(environment, "maxsize", node.template) + print( + template.render( + macro=macro_name, + width=" + ".join(node.symbolic_width()), + ) + ) + + class XdrTypedefGenerator(SourceGenerator): """Generate source code for XDR typedefs""" @@ -253,3 +265,7 @@ def emit_decoder(self, node: _XdrTypedef) -> None: def emit_encoder(self, node: _XdrTypedef) -> None: """Emit one encoder function for an XDR typedef""" emit_typedef_encoder(self.environment, node.declaration) + + def emit_maxsize(self, node: _XdrTypedef) -> None: + """Emit one maxsize macro for an XDR typedef""" + emit_typedef_maxsize(self.environment, node.declaration) diff --git a/tools/net/sunrpc/xdrgen/generators/union.py b/tools/net/sunrpc/xdrgen/generators/union.py index 7974967bbb9fd..2cca00e279cd9 100644 --- a/tools/net/sunrpc/xdrgen/generators/union.py +++ b/tools/net/sunrpc/xdrgen/generators/union.py @@ -8,8 +8,8 @@ from generators import SourceGenerator from generators import create_jinja2_environment, get_jinja2_template -from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid -from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis +from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid, get_header_name +from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis, big_endian def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None: @@ -77,13 +77,18 @@ def emit_union_switch_spec_decoder( print(template.render(name=node.name, type=node.spec.type_name)) -def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None: +def emit_union_case_spec_decoder( + environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool +) -> None: """Emit decoder functions for an XDR union's case arm""" if isinstance(node.arm, _XdrVoid): return - template = get_jinja2_template(environment, "decoder", "case_spec") + if big_endian_discriminant: + template = get_jinja2_template(environment, "decoder", "case_spec_be") + else: + template = get_jinja2_template(environment, "decoder", "case_spec") for case in node.values: print(template.render(case=case)) @@ -136,7 +141,11 @@ def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None: emit_union_switch_spec_decoder(environment, node.discriminant) for case in node.cases: - emit_union_case_spec_decoder(environment, case) + emit_union_case_spec_decoder( + environment, + case, + node.discriminant.spec.type_name in big_endian, + ) emit_union_default_spec_decoder(environment, node) @@ -153,17 +162,21 @@ def emit_union_switch_spec_encoder( print(template.render(name=node.name, type=node.spec.type_name)) -def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None: +def emit_union_case_spec_encoder( + environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool +) -> None: """Emit encoder functions for an XDR union's case arm""" if isinstance(node.arm, _XdrVoid): return - template = get_jinja2_template(environment, "encoder", "case_spec") + if big_endian_discriminant: + template = get_jinja2_template(environment, "encoder", "case_spec_be") + else: + template = get_jinja2_template(environment, "encoder", "case_spec") for case in node.values: print(template.render(case=case)) - assert isinstance(node.arm, _XdrBasic) template = get_jinja2_template(environment, "encoder", node.arm.template) print( template.render( @@ -192,7 +205,6 @@ def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) - print(template.render()) return - assert isinstance(default_case.arm, _XdrBasic) template = get_jinja2_template(environment, "encoder", default_case.arm.template) print( template.render( @@ -210,7 +222,11 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None: emit_union_switch_spec_encoder(environment, node.discriminant) for case in node.cases: - emit_union_case_spec_encoder(environment, case) + emit_union_case_spec_encoder( + environment, + case, + node.discriminant.spec.type_name in big_endian, + ) emit_union_default_spec_encoder(environment, node) @@ -218,6 +234,18 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None: print(template.render()) +def emit_union_maxsize(environment: Environment, node: _XdrUnion) -> None: + """Emit one maxsize macro for an XDR union type""" + macro_name = get_header_name().upper() + "_" + node.name + "_sz" + template = get_jinja2_template(environment, "maxsize", "union") + print( + template.render( + macro=macro_name, + width=" + ".join(node.symbolic_width()), + ) + ) + + class XdrUnionGenerator(SourceGenerator): """Generate source code for XDR unions""" @@ -241,3 +269,7 @@ def emit_decoder(self, node: _XdrUnion) -> None: def emit_encoder(self, node: _XdrUnion) -> None: """Emit one encoder function for an XDR union""" emit_union_encoder(self.environment, node) + + def emit_maxsize(self, node: _XdrUnion) -> None: + """Emit one maxsize macro for an XDR union""" + emit_union_maxsize(self.environment, node) diff --git a/tools/net/sunrpc/xdrgen/grammars/xdr.lark b/tools/net/sunrpc/xdrgen/grammars/xdr.lark index f3c4552e548d6..7c2c1b8c86d12 100644 --- a/tools/net/sunrpc/xdrgen/grammars/xdr.lark +++ b/tools/net/sunrpc/xdrgen/grammars/xdr.lark @@ -3,7 +3,7 @@ declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque | "opaque" identifier "<" [ value ] ">" -> variable_length_opaque - | "string" identifier "<" [ value ] ">" -> variable_length_string + | "string" identifier "<" [ value ] ">" -> string | type_specifier identifier "[" value "]" -> fixed_length_array | type_specifier identifier "<" [ value ] ">" -> variable_length_array | type_specifier "*" identifier -> optional_data @@ -87,12 +87,14 @@ procedure_def : type_specifier identifier "(" type_specifier ")" "=" c pragma_def : "pragma" directive identifier [ identifier ] ";" -directive : exclude_directive +directive : big_endian_directive + | exclude_directive | header_directive | pages_directive | public_directive | skip_directive +big_endian_directive : "big_endian" exclude_directive : "exclude" header_directive : "header" pages_directive : "pages" diff --git a/tools/net/sunrpc/xdrgen/subcmds/definitions.py b/tools/net/sunrpc/xdrgen/subcmds/definitions.py index 5cd13d53221f6..c956e27f37c09 100644 --- a/tools/net/sunrpc/xdrgen/subcmds/definitions.py +++ b/tools/net/sunrpc/xdrgen/subcmds/definitions.py @@ -28,9 +28,7 @@ logger.setLevel(logging.INFO) -def emit_header_definitions( - root: Specification, language: str, peer: str -) -> None: +def emit_header_definitions(root: Specification, language: str, peer: str) -> None: """Emit header definitions""" for definition in root.definitions: if isinstance(definition.value, _XdrConstant): @@ -52,6 +50,25 @@ def emit_header_definitions( gen.emit_definition(definition.value) +def emit_header_maxsize(root: Specification, language: str, peer: str) -> None: + """Emit header maxsize macros""" + print("") + for definition in root.definitions: + if isinstance(definition.value, _XdrEnum): + gen = XdrEnumGenerator(language, peer) + elif isinstance(definition.value, _XdrPointer): + gen = XdrPointerGenerator(language, peer) + elif isinstance(definition.value, _XdrTypedef): + gen = XdrTypedefGenerator(language, peer) + elif isinstance(definition.value, _XdrStruct): + gen = XdrStructGenerator(language, peer) + elif isinstance(definition.value, _XdrUnion): + gen = XdrUnionGenerator(language, peer) + else: + continue + gen.emit_maxsize(definition.value) + + def handle_parse_error(e: UnexpectedInput) -> bool: """Simple parse error reporting, no recovery attempted""" print(e) @@ -71,6 +88,7 @@ def subcmd(args: Namespace) -> int: gen.emit_definition(args.filename, ast) emit_header_definitions(ast, args.language, args.peer) + emit_header_maxsize(ast, args.language, args.peer) gen = XdrHeaderBottomGenerator(args.language, args.peer) gen.emit_definition(args.filename, ast) diff --git a/tools/net/sunrpc/xdrgen/subcmds/source.py b/tools/net/sunrpc/xdrgen/subcmds/source.py index 00c04ad15b895..2024954748f09 100644 --- a/tools/net/sunrpc/xdrgen/subcmds/source.py +++ b/tools/net/sunrpc/xdrgen/subcmds/source.py @@ -83,8 +83,7 @@ def generate_client_source(filename: str, root: Specification, language: str) -> gen = XdrSourceTopGenerator(language, "client") gen.emit_source(filename, root) - # cel: todo: client needs XDR size macros - + print("") for definition in root.definitions: emit_source_encoder(definition.value, language, "client") for definition in root.definitions: diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 deleted file mode 100644 index ab1e576c95318..0000000000000 --- a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 +++ /dev/null @@ -1,4 +0,0 @@ -{# SPDX-License-Identifier: GPL-2.0 #} - -bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr); -bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enum.j2 new file mode 100644 index 0000000000000..d1405c7c5354a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enum.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 index 341d829afeda9..6482984f1cb71 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 @@ -8,7 +8,7 @@ bool {% else %} static bool __maybe_unused {% endif %} -xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr) +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) { u32 val; diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2 new file mode 100644 index 0000000000000..44c391c10b42e --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2 @@ -0,0 +1,14 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* enum {{ name }} (big-endian) */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) +{ + return xdr_stream_decode_be32(xdr, ptr) == 0; +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 index 9e62344a976aa..a07586cbee179 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 @@ -1,2 +1,3 @@ {# SPDX-License-Identifier: GPL-2.0 #} }; +typedef enum {{ name }} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close_be.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close_be.j2 new file mode 100644 index 0000000000000..2c18948bddf75 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close_be.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +}; +typedef __be32 {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 index bd0a770e50f24..67245b9a914d2 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 @@ -8,7 +8,7 @@ bool {% else %} static bool __maybe_unused {% endif %} -xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value) +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value) { return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; } diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum_be.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum_be.j2 new file mode 100644 index 0000000000000..fbbcc45948d6a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum_be.j2 @@ -0,0 +1,14 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* enum {{ name }} (big-endian) */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value) +{ + return xdr_stream_encode_be32(xdr, value) == XDR_UNIT; +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/maxsize/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/maxsize/enum.j2 new file mode 100644 index 0000000000000..45c1d4c21b220 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/maxsize/enum.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/pointer/definition/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/maxsize/pointer.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/maxsize/pointer.j2 new file mode 100644 index 0000000000000..9f3bfb47d2f47 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/maxsize/pointer.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} \ + ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 index d304eccb5c402..aa9940e322db9 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 @@ -13,10 +13,6 @@ static int {{ program }}_xdr_dec_{{ result }}(struct rpc_rqst *req, if (!xdrgen_decode_{{ result }}(xdr, result)) return -EIO; - if (result->stat != nfs_ok) { - trace_nfs_xdr_status(xdr, (int)result->stat); - return {{ program }}_stat_to_errno(result->stat); - } {% endif %} return 0; } diff --git a/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 b/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 index e3a802cbc4d75..c5518c519854a 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 @@ -3,6 +3,11 @@ // XDR specification file: {{ filename }} // XDR specification modification time: {{ mtime }} -#include +#include -#include "{{ program }}xdr_gen.h" +#include +#include +#include +#include + +#include diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/struct/decoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/struct/definition/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/struct/encoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/maxsize/struct.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/maxsize/struct.j2 new file mode 100644 index 0000000000000..9f3bfb47d2f47 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/maxsize/struct.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} \ + ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/typedef/definition/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/basic.j2 new file mode 100644 index 0000000000000..9f3bfb47d2f47 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/basic.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} \ + ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/fixed_length_opaque.j2 new file mode 100644 index 0000000000000..45c1d4c21b220 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/fixed_length_opaque.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/string.j2 new file mode 100644 index 0000000000000..45c1d4c21b220 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/string.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_array.j2 new file mode 100644 index 0000000000000..45c1d4c21b220 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_array.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_opaque.j2 new file mode 100644 index 0000000000000..45c1d4c21b220 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_opaque.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec_be.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec_be.j2 new file mode 100644 index 0000000000000..917f3a1c45888 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec_be.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + case __constant_cpu_to_be32({{ case }}): diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/string.j2 similarity index 100% rename from tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 rename to tools/net/sunrpc/xdrgen/templates/C/union/decoder/string.j2 diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec_be.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec_be.j2 new file mode 100644 index 0000000000000..917f3a1c45888 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec_be.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + case __constant_cpu_to_be32({{ case }}): diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/maxsize/union.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/maxsize/union.j2 new file mode 100644 index 0000000000000..9f3bfb47d2f47 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/maxsize/union.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +#define {{ '{:<31}'.format(macro) }} \ + ({{ width }}) diff --git a/tools/net/sunrpc/xdrgen/xdr_ast.py b/tools/net/sunrpc/xdrgen/xdr_ast.py index dbd3fcf9c9576..5233e73c7046a 100644 --- a/tools/net/sunrpc/xdrgen/xdr_ast.py +++ b/tools/net/sunrpc/xdrgen/xdr_ast.py @@ -12,13 +12,50 @@ this_module = sys.modules[__name__] +big_endian = [] excluded_apis = [] header_name = "none" public_apis = [] -enums = set() structs = set() pass_by_reference = set() +constants = {} + + +def xdr_quadlen(val: str) -> int: + """Return integer XDR width of an XDR type""" + if val in constants: + octets = constants[val] + else: + octets = int(val) + return int((octets + 3) / 4) + + +symbolic_widths = { + "void": ["XDR_void"], + "bool": ["XDR_bool"], + "int": ["XDR_int"], + "unsigned_int": ["XDR_unsigned_int"], + "long": ["XDR_long"], + "unsigned_long": ["XDR_unsigned_long"], + "hyper": ["XDR_hyper"], + "unsigned_hyper": ["XDR_unsigned_hyper"], +} + +# Numeric XDR widths are tracked in a dictionary that is keyed +# by type_name because sometimes a caller has nothing more than +# the type_name to use to figure out the numeric width. +max_widths = { + "void": 0, + "bool": 1, + "int": 1, + "unsigned_int": 1, + "long": 1, + "unsigned_long": 1, + "hyper": 2, + "unsigned_hyper": 2, +} + @dataclass class _XdrAst(ast_utils.Ast): @@ -51,18 +88,31 @@ class _XdrTypeSpecifier(_XdrAst): """Corresponds to 'type_specifier' in the XDR language grammar""" type_name: str - c_classifier: str + c_classifier: str = "" @dataclass class _XdrDefinedType(_XdrTypeSpecifier): """Corresponds to a type defined by the input specification""" + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return [get_header_name().upper() + "_" + self.type_name + "_sz"] + + def __post_init__(self): + if self.type_name in structs: + self.c_classifier = "struct " + symbolic_widths[self.type_name] = self.symbolic_width() + @dataclass class _XdrBuiltInType(_XdrTypeSpecifier): """Corresponds to a built-in XDR type""" + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return symbolic_widths[self.type_name] + @dataclass class _XdrDeclaration(_XdrAst): @@ -77,6 +127,18 @@ class _XdrFixedLengthOpaque(_XdrDeclaration): size: str template: str = "fixed_length_opaque" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return xdr_quadlen(self.size) + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return ["XDR_QUADLEN(" + self.size + ")"] + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrVariableLengthOpaque(_XdrDeclaration): @@ -86,14 +148,44 @@ class _XdrVariableLengthOpaque(_XdrDeclaration): maxsize: str template: str = "variable_length_opaque" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return 1 + xdr_quadlen(self.maxsize) + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + widths = ["XDR_unsigned_int"] + if self.maxsize != "0": + widths.append("XDR_QUADLEN(" + self.maxsize + ")") + return widths + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass -class _XdrVariableLengthString(_XdrDeclaration): +class _XdrString(_XdrDeclaration): """A (NUL-terminated) variable-length string declaration""" name: str maxsize: str - template: str = "variable_length_string" + template: str = "string" + + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return 1 + xdr_quadlen(self.maxsize) + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + widths = ["XDR_unsigned_int"] + if self.maxsize != "0": + widths.append("XDR_QUADLEN(" + self.maxsize + ")") + return widths + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() @dataclass @@ -105,6 +197,19 @@ class _XdrFixedLengthArray(_XdrDeclaration): size: str template: str = "fixed_length_array" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return xdr_quadlen(self.size) * max_widths[self.spec.type_name] + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + item_width = " + ".join(symbolic_widths[self.spec.type_name]) + return ["(" + self.size + " * (" + item_width + "))"] + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrVariableLengthArray(_XdrDeclaration): @@ -115,6 +220,22 @@ class _XdrVariableLengthArray(_XdrDeclaration): maxsize: str template: str = "variable_length_array" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return 1 + (xdr_quadlen(self.maxsize) * max_widths[self.spec.type_name]) + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + widths = ["XDR_unsigned_int"] + if self.maxsize != "0": + item_width = " + ".join(symbolic_widths[self.spec.type_name]) + widths.append("(" + self.maxsize + " * (" + item_width + "))") + return widths + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrOptionalData(_XdrDeclaration): @@ -124,6 +245,20 @@ class _XdrOptionalData(_XdrDeclaration): spec: _XdrTypeSpecifier template: str = "optional_data" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return 1 + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return ["XDR_bool"] + + def __post_init__(self): + structs.add(self.name) + pass_by_reference.add(self.name) + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrBasic(_XdrDeclaration): @@ -133,13 +268,34 @@ class _XdrBasic(_XdrDeclaration): spec: _XdrTypeSpecifier template: str = "basic" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return max_widths[self.spec.type_name] + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return symbolic_widths[self.spec.type_name] + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrVoid(_XdrDeclaration): """A void declaration""" + name: str = "void" template: str = "void" + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return 0 + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return [] + @dataclass class _XdrConstant(_XdrAst): @@ -148,6 +304,10 @@ class _XdrConstant(_XdrAst): name: str value: str + def __post_init__(self): + if self.value not in constants: + constants[self.name] = int(self.value, 0) + @dataclass class _XdrEnumerator(_XdrAst): @@ -156,6 +316,10 @@ class _XdrEnumerator(_XdrAst): name: str value: str + def __post_init__(self): + if self.value not in constants: + constants[self.name] = int(self.value, 0) + @dataclass class _XdrEnum(_XdrAst): @@ -166,6 +330,18 @@ class _XdrEnum(_XdrAst): maximum: int enumerators: List[_XdrEnumerator] + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return 1 + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return ["XDR_int"] + + def __post_init__(self): + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrStruct(_XdrAst): @@ -174,6 +350,26 @@ class _XdrStruct(_XdrAst): name: str fields: List[_XdrDeclaration] + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + width = 0 + for field in self.fields: + width += field.max_width() + return width + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + widths = [] + for field in self.fields: + widths += field.symbolic_width() + return widths + + def __post_init__(self): + structs.add(self.name) + pass_by_reference.add(self.name) + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrPointer(_XdrAst): @@ -182,6 +378,27 @@ class _XdrPointer(_XdrAst): name: str fields: List[_XdrDeclaration] + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + width = 1 + for field in self.fields[0:-1]: + width += field.max_width() + return width + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + widths = [] + widths += ["XDR_bool"] + for field in self.fields[0:-1]: + widths += field.symbolic_width() + return widths + + def __post_init__(self): + structs.add(self.name) + pass_by_reference.add(self.name) + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _XdrTypedef(_XdrAst): @@ -189,6 +406,23 @@ class _XdrTypedef(_XdrAst): declaration: _XdrDeclaration + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + return self.declaration.max_width() + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + return self.declaration.symbolic_width() + + def __post_init__(self): + if isinstance(self.declaration, _XdrBasic): + new_type = self.declaration + if isinstance(new_type.spec, _XdrDefinedType): + if new_type.spec.type_name in pass_by_reference: + pass_by_reference.add(new_type.name) + max_widths[new_type.name] = self.max_width() + symbolic_widths[new_type.name] = self.symbolic_width() + @dataclass class _XdrCaseSpec(_XdrAst): @@ -216,6 +450,36 @@ class _XdrUnion(_XdrAst): cases: List[_XdrCaseSpec] default: _XdrDeclaration + def max_width(self) -> int: + """Return width of type in XDR_UNITS""" + max_width = 0 + for case in self.cases: + if case.arm.max_width() > max_width: + max_width = case.arm.max_width() + if self.default: + if self.default.arm.max_width() > max_width: + max_width = self.default.arm.max_width() + return 1 + max_width + + def symbolic_width(self) -> List: + """Return list containing XDR width of type's components""" + max_width = 0 + for case in self.cases: + if case.arm.max_width() > max_width: + max_width = case.arm.max_width() + width = case.arm.symbolic_width() + if self.default: + if self.default.arm.max_width() > max_width: + max_width = self.default.arm.max_width() + width = self.default.arm.symbolic_width() + return symbolic_widths[self.discriminant.name] + width + + def __post_init__(self): + structs.add(self.name) + pass_by_reference.add(self.name) + max_widths[self.name] = self.max_width() + symbolic_widths[self.name] = self.symbolic_width() + @dataclass class _RpcProcedure(_XdrAst): @@ -290,24 +554,13 @@ def constant(self, children): return _XdrConstantValue(value) def type_specifier(self, children): - """Instantiate one type_specifier object""" - c_classifier = "" + """Instantiate one _XdrTypeSpecifier object""" if isinstance(children[0], _XdrIdentifier): name = children[0].symbol - if name in enums: - c_classifier = "enum " - if name in structs: - c_classifier = "struct " - return _XdrDefinedType( - type_name=name, - c_classifier=c_classifier, - ) - - token = children[0].data - return _XdrBuiltInType( - type_name=token.value, - c_classifier=c_classifier, - ) + return _XdrDefinedType(type_name=name) + + name = children[0].data.value + return _XdrBuiltInType(type_name=name) def constant_def(self, children): """Instantiate one _XdrConstant object""" @@ -320,7 +573,6 @@ def constant_def(self, children): def enum(self, children): """Instantiate one _XdrEnum object""" enum_name = children[0].symbol - enums.add(enum_name) i = 0 enumerators = [] @@ -350,15 +602,15 @@ def variable_length_opaque(self, children): return _XdrVariableLengthOpaque(name, maxsize) - def variable_length_string(self, children): - """Instantiate one _XdrVariableLengthString declaration object""" + def string(self, children): + """Instantiate one _XdrString declaration object""" name = children[0].symbol if children[1] is not None: maxsize = children[1].value else: maxsize = "0" - return _XdrVariableLengthString(name, maxsize) + return _XdrString(name, maxsize) def fixed_length_array(self, children): """Instantiate one _XdrFixedLengthArray declaration object""" @@ -383,8 +635,6 @@ def optional_data(self, children): """Instantiate one _XdrOptionalData declaration object""" spec = children[0] name = children[1].symbol - structs.add(name) - pass_by_reference.add(name) return _XdrOptionalData(name, spec) @@ -403,8 +653,6 @@ def void(self, children): def struct(self, children): """Instantiate one _XdrStruct object""" name = children[0].symbol - structs.add(name) - pass_by_reference.add(name) fields = children[1].children last_field = fields[-1] @@ -419,11 +667,6 @@ def struct(self, children): def typedef(self, children): """Instantiate one _XdrTypedef object""" new_type = children[0] - if isinstance(new_type, _XdrBasic) and isinstance( - new_type.spec, _XdrDefinedType - ): - if new_type.spec.type_name in pass_by_reference: - pass_by_reference.add(new_type.name) return _XdrTypedef(new_type) @@ -445,8 +688,6 @@ def default_spec(self, children): def union(self, children): """Instantiate one _XdrUnion object""" name = children[0].symbol - structs.add(name) - pass_by_reference.add(name) body = children[1] discriminant = body.children[0].children[0] @@ -484,6 +725,8 @@ def pragma_def(self, children): """Instantiate one _Pragma object""" directive = children[0].children[0].data match directive: + case "big_endian_directive": + big_endian.append(children[1].symbol) case "exclude_directive": excluded_apis.append(children[1].symbol) case "header_directive": diff --git a/tools/net/sunrpc/xdrgen/xdrgen b/tools/net/sunrpc/xdrgen/xdrgen index 95f303b2861b1..43762be39252d 100755 --- a/tools/net/sunrpc/xdrgen/xdrgen +++ b/tools/net/sunrpc/xdrgen/xdrgen @@ -128,5 +128,7 @@ There is NO WARRANTY, to the extent permitted by law.""", try: if __name__ == "__main__": sys.exit(main()) -except (SystemExit, KeyboardInterrupt, BrokenPipeError): +except SystemExit: + sys.exit(0) +except (KeyboardInterrupt, BrokenPipeError): sys.exit(1) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f7586f82b967a..4ce176ad411fb 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4573,6 +4573,8 @@ static int validate_ibt(struct objtool_file *file) !strcmp(sec->name, "__jump_table") || !strcmp(sec->name, "__mcount_loc") || !strcmp(sec->name, ".kcfi_traps") || + !strcmp(sec->name, ".llvm.call-graph-profile") || + !strcmp(sec->name, ".llvm_bb_addr_map") || !strcmp(sec->name, "__tracepoints") || strstr(sec->name, "__patchable_function_entries")) continue; diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 3d27983dc908d..6f64d611faea9 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -224,12 +224,17 @@ int find_symbol_hole_containing(const struct section *sec, unsigned long offset) if (n) return 0; /* not a hole */ - /* didn't find a symbol for which @offset is after it */ - if (!hole.sym) - return 0; /* not a hole */ + /* + * @offset >= sym->offset + sym->len, find symbol after it. + * When hole.sym is empty, use the first node to compute the hole. + * If there is no symbol in the section, the first node will be NULL, + * in which case, -1 is returned to skip the whole section. + */ + if (hole.sym) + n = rb_next(&hole.sym->node); + else + n = rb_first_cached(&sec->symbol_tree); - /* @offset >= sym->offset + sym->len, find symbol after it */ - n = rb_next(&hole.sym->node); if (!n) return -1; /* until end of address space */ diff --git a/tools/pci/pcitest.c b/tools/pci/pcitest.c index 470258009ddc2..7b530d838d408 100644 --- a/tools/pci/pcitest.c +++ b/tools/pci/pcitest.c @@ -95,7 +95,7 @@ static int run_test(struct pci_test *test) if (test->msinum > 0 && test->msinum <= 32) { ret = ioctl(fd, PCITEST_MSI, test->msinum); - fprintf(stdout, "MSI%d:\t\t", test->msinum); + fprintf(stdout, "MSI%u:\t\t", test->msinum); if (ret < 0) fprintf(stdout, "TEST FAILED\n"); else @@ -104,7 +104,7 @@ static int run_test(struct pci_test *test) if (test->msixnum > 0 && test->msixnum <= 2048) { ret = ioctl(fd, PCITEST_MSIX, test->msixnum); - fprintf(stdout, "MSI-X%d:\t\t", test->msixnum); + fprintf(stdout, "MSI-X%u:\t\t", test->msixnum); if (ret < 0) fprintf(stdout, "TEST FAILED\n"); else @@ -116,7 +116,7 @@ static int run_test(struct pci_test *test) if (test->use_dma) param.flags = PCITEST_FLAGS_USE_DMA; ret = ioctl(fd, PCITEST_WRITE, ¶m); - fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size); + fprintf(stdout, "WRITE (%7lu bytes):\t\t", test->size); if (ret < 0) fprintf(stdout, "TEST FAILED\n"); else @@ -128,7 +128,7 @@ static int run_test(struct pci_test *test) if (test->use_dma) param.flags = PCITEST_FLAGS_USE_DMA; ret = ioctl(fd, PCITEST_READ, ¶m); - fprintf(stdout, "READ (%7ld bytes):\t\t", test->size); + fprintf(stdout, "READ (%7lu bytes):\t\t", test->size); if (ret < 0) fprintf(stdout, "TEST FAILED\n"); else @@ -140,7 +140,7 @@ static int run_test(struct pci_test *test) if (test->use_dma) param.flags = PCITEST_FLAGS_USE_DMA; ret = ioctl(fd, PCITEST_COPY, ¶m); - fprintf(stdout, "COPY (%7ld bytes):\t\t", test->size); + fprintf(stdout, "COPY (%7lu bytes):\t\t", test->size); if (ret < 0) fprintf(stdout, "TEST FAILED\n"); else diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index f5b81d439387a..5aaf73df6700d 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -39,9 +39,9 @@ trace/beauty/generated/ pmu-events/pmu-events.c pmu-events/jevents pmu-events/metric_test.log -tests/shell/*.shellcheck_log -tests/shell/coresight/*.shellcheck_log -tests/shell/lib/*.shellcheck_log +pmu-events/empty-pmu-events.log +pmu-events/test-empty-pmu-events.c +*.shellcheck_log feature/ libapi/ libbpf/ diff --git a/tools/perf/Documentation/itrace.txt b/tools/perf/Documentation/itrace.txt index 19cc179be9a78..40476b227f8d6 100644 --- a/tools/perf/Documentation/itrace.txt +++ b/tools/perf/Documentation/itrace.txt @@ -1,6 +1,6 @@ i synthesize instructions events y synthesize cycles events - b synthesize branches events (branch misses for Arm SPE) + b synthesize branches events c synthesize branches events (calls only) r synthesize branches events (returns only) x synthesize transactions events diff --git a/tools/perf/Documentation/perf-arm-spe.txt b/tools/perf/Documentation/perf-arm-spe.txt index 0a3eda482307a..de2b0b479249e 100644 --- a/tools/perf/Documentation/perf-arm-spe.txt +++ b/tools/perf/Documentation/perf-arm-spe.txt @@ -187,7 +187,7 @@ groups: 7 llc-access 2 tlb-miss 1K tlb-access - 36 branch-miss + 36 branch 0 remote-access 900 memory diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt index 10f69fb6850bc..31741499e7867 100644 --- a/tools/perf/Documentation/perf-check.txt +++ b/tools/perf/Documentation/perf-check.txt @@ -47,15 +47,15 @@ feature:: bpf / HAVE_LIBBPF_SUPPORT bpf_skeletons / HAVE_BPF_SKEL debuginfod / HAVE_DEBUGINFOD_SUPPORT - dwarf / HAVE_DWARF_SUPPORT - dwarf_getlocations / HAVE_DWARF_GETLOCATIONS_SUPPORT + dwarf / HAVE_LIBDW_SUPPORT + dwarf_getlocations / HAVE_LIBDW_SUPPORT dwarf-unwind / HAVE_DWARF_UNWIND_SUPPORT auxtrace / HAVE_AUXTRACE_SUPPORT libaudit / HAVE_LIBAUDIT_SUPPORT libbfd / HAVE_LIBBFD_SUPPORT libcapstone / HAVE_LIBCAPSTONE_SUPPORT libcrypto / HAVE_LIBCRYPTO_SUPPORT - libdw-dwarf-unwind / HAVE_DWARF_SUPPORT + libdw-dwarf-unwind / HAVE_LIBDW_SUPPORT libelf / HAVE_LIBELF_SUPPORT libnuma / HAVE_LIBNUMA_SUPPORT libopencsd / HAVE_CSTRACE_SUPPORT diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 379f9d7a8ab11..1f668d4724e37 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -247,6 +247,19 @@ annotate.*:: These are in control of addresses, jump function, source code in lines of assembly code from a specific program. + annotate.disassemblers:: + Choose the disassembler to use: "objdump", "llvm", "capstone", + if not specified it will first try, if available, the "llvm" one, + then, if it fails, "capstone", and finally the original "objdump" + based one. + + Choosing a different one is useful when handling some feature that + is known to be best support at some point by one of the options, + to compare the output when in doubt about some bug, etc. + + This can be a list, in order of preference, the first one that works + finishes the process. + annotate.addr2line:: addr2line binary to use for file names and line numbers. diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index dea005410ec02..d0c65fad419a0 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -8,7 +8,7 @@ perf-list - List all symbolic event types SYNOPSIS -------- [verse] -'perf list' [--no-desc] [--long-desc] +'perf list' [] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob] DESCRIPTION @@ -243,6 +243,21 @@ For accessing trace point events perf needs to have read access to /sys/kernel/tracing, even when perf_event_paranoid is in a relaxed setting. +TOOL/HWMON EVENTS +----------------- + +Some events don't have an associated PMU instead reading values +available to software without perf_event_open. As these events don't +support sampling they can only really be read by tools like perf stat. + +Tool events provide times and certain system parameters. Examples +include duration_time, user_time, system_time and num_cpus_online. + +Hwmon events provide easy access to hwmon sysfs data typically in +/sys/class/hwmon. This information includes temperatures, fan speeds +and energy usage. + + TRACING ------- diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 7c66d81ab978c..87f8645194062 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -391,6 +391,14 @@ OPTIONS This allows to examine the path the program took to each sample. The data collection must have used -b (or -j) and -g. + Also show with some branch flags that can be: + - Predicted: display the average percentage of predicated branches. + (predicated number / total number) + - Abort: display the number of tsx aborted branches. + - Cycles: cycles in basic block. + + - iterations: display the average number of iterations in callchain list. + --addr2line=:: Path to addr2line binary. diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 3db64954a267a..6dbbddb6464d0 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -221,6 +221,14 @@ OPTIONS for 'perf sched timehist' priorities are specified with -: 120-129. A combination of both can also be provided: 0,120-129. +-P:: +--pre-migrations:: + Show pre-migration wait time. pre-migration wait time is the time spent + by a task waiting on a runqueue but not getting the chance to run there + and is migrated to a different runqueue where it is finally run. This + time between sched_wakeup and migrate_task is the pre-migration wait + time. + OPTIONS for 'perf sched replay' ------------------------------ diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt index 13e37e9385ee4..27a1cac6fe763 100644 --- a/tools/perf/Documentation/perf-script-python.txt +++ b/tools/perf/Documentation/perf-script-python.txt @@ -624,7 +624,7 @@ as perf_trace_context.perf_script_context . perf_set_itrace_options(context, itrace_options) - set --itrace options if they have not been set already perf_sample_srcline(context) - returns source_file_name, line_number perf_sample_srccode(context) - returns source_file_name, line_number, source_line - + perf_config_get(config_name) - returns the value of the named config item, or None if unset Util.py Module ~~~~~~~~~~~~~~ diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 9acb8d1f65889..efcdec528a8f7 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt @@ -48,3 +48,20 @@ OPTIONS --dso:: Specify a DSO for the "Symbols" test. + +-w:: +--workload=:: + Run a built-in workload, to list them use '--list-workloads', current ones include: + noploop, thloop, leafloop, sqrtloop, brstack, datasym and landlock. + + Used with the shell script regression tests. + + Some accept an extra parameter: + + seconds: leafloop, noploop, sqrtloop, thloop + nrloops: brstack + + The datasym and landlock workloads don't accept any. + +--list-workloads:: + List the available workloads to use with -w/--workload. diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index d4332675babb7..2916d59c88cd0 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -31,7 +31,7 @@ $(call detected_var,SRCARCH) ifneq ($(NO_SYSCALL_TABLE),1) NO_SYSCALL_TABLE := 1 - ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 powerpc arm64 s390 mips loongarch)) + ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 powerpc arm64 s390 mips loongarch riscv)) NO_SYSCALL_TABLE := 0 endif @@ -83,6 +83,10 @@ ifeq ($(ARCH),mips) LIBUNWIND_LIBS = -lunwind -lunwind-mips endif +ifeq ($(ARCH),riscv) + CFLAGS += -I$(OUTPUT)arch/riscv/include/generated +endif + # So far there's only x86 and arm libdw unwind support merged in perf. # Disable it on all other architectures in case libdw unwind # support is detected in system. Add supported architectures @@ -91,6 +95,10 @@ ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv loon NO_LIBDW_DWARF_UNWIND := 1 endif +ifneq ($(LIBUNWIND),1) + NO_LIBUNWIND := 1 +endif + ifeq ($(LIBUNWIND_LIBS),) NO_LIBUNWIND := 1 endif @@ -162,8 +170,8 @@ ifeq ($(findstring -static,${LDFLAGS}),-static) # Must put -ldl after -lebl for dependency DWARFLIBS += -ldl endif -FEATURE_CHECK_CFLAGS-libdw-dwarf-unwind := $(LIBDW_CFLAGS) -FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) $(DWARFLIBS) +FEATURE_CHECK_CFLAGS-libdw := $(LIBDW_CFLAGS) +FEATURE_CHECK_LDFLAGS-libdw := $(LIBDW_LDFLAGS) $(DWARFLIBS) # for linking with debug library, run like: # make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/ @@ -203,10 +211,6 @@ FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arc # include ARCH specific config -include $(src-perf)/arch/$(SRCARCH)/Makefile -ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET - CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET -endif - include $(srctree)/tools/scripts/utilities.mak ifeq ($(call get-executable,$(FLEX)),) @@ -426,7 +430,7 @@ ifeq ($(feature-file-handle), 1) endif ifdef NO_LIBELF - NO_DWARF := 1 + NO_LIBDW := 1 NO_LIBUNWIND := 1 NO_LIBDW_DWARF_UNWIND := 1 NO_LIBBPF := 1 @@ -461,28 +465,11 @@ else endif endif else - ifndef NO_LIBDW_DWARF_UNWIND - ifneq ($(feature-libdw-dwarf-unwind),1) - NO_LIBDW_DWARF_UNWIND := 1 - $(warning No libdw DWARF unwind found, Please install elfutils-devel/libdw-dev >= 0.158 and/or set LIBDW_DIR) + ifneq ($(feature-libdw), 1) + ifndef NO_LIBDW + $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.157, disables dwarf support. Please install new elfutils-devel/libdw-dev) + NO_LIBDW := 1 endif - endif - ifneq ($(feature-dwarf), 1) - ifndef NO_DWARF - $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev) - NO_DWARF := 1 - endif - else - ifneq ($(feature-dwarf_getlocations), 1) - $(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.157) - else - CFLAGS += -DHAVE_DWARF_GETLOCATIONS_SUPPORT - endif # dwarf_getlocations - ifneq ($(feature-dwarf_getcfi), 1) - $(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.142) - else - CFLAGS += -DHAVE_DWARF_CFI_SUPPORT - endif # dwarf_getcfi endif # Dwarf support endif # libelf support endif # NO_LIBELF @@ -493,7 +480,7 @@ ifeq ($(feature-libaio), 1) endif endif -ifdef NO_DWARF +ifdef NO_LIBDW NO_LIBDW_DWARF_UNWIND := 1 endif @@ -571,17 +558,12 @@ ifndef NO_LIBELF endif endif - ifndef NO_DWARF - ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) - $(warning DWARF register mappings have not been defined for architecture $(SRCARCH), DWARF support disabled) - NO_DWARF := 1 - else - CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) - LDFLAGS += $(LIBDW_LDFLAGS) - EXTLIBS += ${DWARFLIBS} - $(call detected,CONFIG_DWARF) - endif # PERF_HAVE_DWARF_REGS - endif # NO_DWARF + ifndef NO_LIBDW + CFLAGS += -DHAVE_LIBDW_SUPPORT $(LIBDW_CFLAGS) + LDFLAGS += $(LIBDW_LDFLAGS) + EXTLIBS += ${DWARFLIBS} + $(call detected,CONFIG_LIBDW) + endif # NO_LIBDW ifndef NO_LIBBPF ifeq ($(feature-bpf), 1) @@ -630,7 +612,7 @@ ifdef PERF_HAVE_JITDUMP endif ifeq ($(SRCARCH),powerpc) - ifndef NO_DWARF + ifndef NO_LIBDW CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX endif endif @@ -750,8 +732,6 @@ endif ifeq ($(dwarf-post-unwind),1) CFLAGS += -DHAVE_DWARF_UNWIND_SUPPORT $(call detected,CONFIG_DWARF_UNWIND) -else - NO_DWARF_UNWIND := 1 endif ifndef NO_LOCAL_LIBUNWIND @@ -1194,7 +1174,7 @@ endif ifneq ($(NO_LIBTRACEEVENT),1) $(call feature_check,libtraceevent) ifeq ($(feature-libtraceevent), 1) - CFLAGS += -DHAVE_LIBTRACEEVENT + CFLAGS += -DHAVE_LIBTRACEEVENT $(shell $(PKG_CONFIG) --cflags libtraceevent) LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L libtraceevent) EXTLIBS += $(shell $(PKG_CONFIG) --libs-only-l libtraceevent) LIBTRACEEVENT_VERSION := $(shell $(PKG_CONFIG) --modversion libtraceevent).0.0 diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 9dd2e8d3f3c9b..d74241a151313 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -40,7 +40,7 @@ include ../scripts/utilities.mak # # Define EXTRA_PERFLIBS to pass extra libraries to PERFLIBS. # -# Define NO_DWARF if you do not want debug-info analysis feature at all. +# Define NO_LIBDW if you do not want debug-info analysis feature at all. # # Define WERROR=0 to disable treating any warnings as errors. # @@ -52,7 +52,7 @@ include ../scripts/utilities.mak # # Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds) # -# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf +# Define LIBUNWIND if you do not want libunwind dependency for dwarf # backtrace post unwind. # # Define NO_BACKTRACE if you do not want stack backtrace debug feature @@ -1128,12 +1128,11 @@ endif install-tests: all install-gtk $(call QUIET_INSTALL, tests) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ - $(INSTALL) tests/attr.py -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ $(INSTALL) tests/pe-file.exe* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \ - $(INSTALL) tests/attr/* -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \ $(INSTALL) tests/shell/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/attr'; \ + $(INSTALL) tests/shell/attr/* -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/attr'; \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \ $(INSTALL) tests/shell/lib/*.sh -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \ $(INSTALL) tests/shell/lib/*.py -m 644 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \ diff --git a/tools/perf/arch/arc/annotate/instructions.c b/tools/perf/arch/arc/annotate/instructions.c index 2f00e995c7e38..e5619770a1af7 100644 --- a/tools/perf/arch/arc/annotate/instructions.c +++ b/tools/perf/arch/arc/annotate/instructions.c @@ -5,5 +5,7 @@ static int arc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) { arch->initialized = true; arch->objdump.comment_char = ';'; + arch->e_machine = EM_ARC; + arch->e_flags = 0; return 0; } diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile index 1d88fdab13bf8..8b59ce8efb892 100644 --- a/tools/perf/arch/arm/Makefile +++ b/tools/perf/arch/arm/Makefile @@ -1,5 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif PERF_HAVE_JITDUMP := 1 diff --git a/tools/perf/arch/arm/annotate/instructions.c b/tools/perf/arch/arm/annotate/instructions.c index 2ff6cedeb9c53..cf91a43362b0e 100644 --- a/tools/perf/arch/arm/annotate/instructions.c +++ b/tools/perf/arch/arm/annotate/instructions.c @@ -53,6 +53,8 @@ static int arm__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = arm__associate_instruction_ops; arch->objdump.comment_char = ';'; arch->objdump.skip_functions_char = '+'; + arch->e_machine = EM_ARM; + arch->e_flags = 0; return 0; out_free_call: diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build index e6dd7cd79ebd0..f7a8b37d1c688 100644 --- a/tools/perf/arch/arm/util/Build +++ b/tools/perf/arch/arm/util/Build @@ -1,7 +1,5 @@ perf-util-y += perf_regs.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o - perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c deleted file mode 100644 index fc5f71c918020..0000000000000 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Will Deacon, ARM Ltd. - */ - -#include -#include -#include - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} -#define GPR_DWARFNUM_NAME(num) \ - {.name = __stringify(%r##num), .dwarfnum = num} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} - -/* - * Reference: - * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf - */ -static const struct pt_regs_dwarfnum regdwarfnum_table[] = { - GPR_DWARFNUM_NAME(0), - GPR_DWARFNUM_NAME(1), - GPR_DWARFNUM_NAME(2), - GPR_DWARFNUM_NAME(3), - GPR_DWARFNUM_NAME(4), - GPR_DWARFNUM_NAME(5), - GPR_DWARFNUM_NAME(6), - GPR_DWARFNUM_NAME(7), - GPR_DWARFNUM_NAME(8), - GPR_DWARFNUM_NAME(9), - GPR_DWARFNUM_NAME(10), - REG_DWARFNUM_NAME("%fp", 11), - REG_DWARFNUM_NAME("%ip", 12), - REG_DWARFNUM_NAME("%sp", 13), - REG_DWARFNUM_NAME("%lr", 14), - REG_DWARFNUM_NAME("%pc", 15), - REG_DWARFNUM_END, -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (roff->dwarfnum == n) - return roff->name; - return NULL; -} diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile index 5735ed4479bb9..91570d5d428e4 100644 --- a/tools/perf/arch/arm64/Makefile +++ b/tools/perf/arch/arm64/Makefile @@ -1,9 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif PERF_HAVE_JITDUMP := 1 -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 HAVE_KVM_STAT_SUPPORT := 1 # diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c index f86d9f4798bd0..d465d093e7eb5 100644 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ b/tools/perf/arch/arm64/annotate/instructions.c @@ -113,6 +113,8 @@ static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = arm64__associate_instruction_ops; arch->objdump.comment_char = '/'; arch->objdump.skip_functions_char = '+'; + arch->e_machine = EM_AARCH64; + arch->e_flags = 0; return 0; out_free_call: diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build index 343ef7589a77a..a74521b79eaaa 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build @@ -4,7 +4,6 @@ perf-util-y += perf_regs.o perf-util-y += tsc.o perf-util-y += pmu.o perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index 2be99fdf997db..22b19dcc6beb8 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -23,9 +23,12 @@ #include "../../../util/debug.h" #include "../../../util/auxtrace.h" #include "../../../util/record.h" +#include "../../../util/header.h" #include "../../../util/arm-spe.h" #include // reallocarray +#define ARM_SPE_CPU_MAGIC 0x1010101010101010ULL + #define KiB(x) ((x) * 1024) #define MiB(x) ((x) * 1024 * 1024) @@ -37,11 +40,84 @@ struct arm_spe_recording { bool *wrapped; }; +/* + * arm_spe_find_cpus() returns a new cpu map, and the caller should invoke + * perf_cpu_map__put() to release the map after use. + */ +static struct perf_cpu_map *arm_spe_find_cpus(struct evlist *evlist) +{ + struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); + struct perf_cpu_map *intersect_cpus; + + /* cpu map is not "any" CPU , we have specific CPUs to work with */ + if (!perf_cpu_map__has_any_cpu(event_cpus)) { + intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus); + perf_cpu_map__put(online_cpus); + /* Event can be "any" CPU so count all CPUs. */ + } else { + intersect_cpus = online_cpus; + } + + return intersect_cpus; +} + static size_t arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused, - struct evlist *evlist __maybe_unused) + struct evlist *evlist) +{ + struct perf_cpu_map *cpu_map = arm_spe_find_cpus(evlist); + size_t size; + + if (!cpu_map) + return 0; + + size = ARM_SPE_AUXTRACE_PRIV_MAX + + ARM_SPE_CPU_PRIV_MAX * perf_cpu_map__nr(cpu_map); + size *= sizeof(u64); + + perf_cpu_map__put(cpu_map); + return size; +} + +static int arm_spe_save_cpu_header(struct auxtrace_record *itr, + struct perf_cpu cpu, __u64 data[]) { - return ARM_SPE_AUXTRACE_PRIV_SIZE; + struct arm_spe_recording *sper = + container_of(itr, struct arm_spe_recording, itr); + struct perf_pmu *pmu = NULL; + char *cpuid = NULL; + u64 val; + + /* Read CPU MIDR */ + cpuid = get_cpuid_allow_env_override(cpu); + if (!cpuid) + return -ENOMEM; + val = strtol(cpuid, NULL, 16); + + data[ARM_SPE_MAGIC] = ARM_SPE_CPU_MAGIC; + data[ARM_SPE_CPU] = cpu.cpu; + data[ARM_SPE_CPU_NR_PARAMS] = ARM_SPE_CPU_PRIV_MAX - ARM_SPE_CPU_MIDR; + data[ARM_SPE_CPU_MIDR] = val; + + /* Find the associate Arm SPE PMU for the CPU */ + if (perf_cpu_map__has(sper->arm_spe_pmu->cpus, cpu)) + pmu = sper->arm_spe_pmu; + + if (!pmu) { + /* No Arm SPE PMU is found */ + data[ARM_SPE_CPU_PMU_TYPE] = ULLONG_MAX; + data[ARM_SPE_CAP_MIN_IVAL] = 0; + } else { + data[ARM_SPE_CPU_PMU_TYPE] = pmu->type; + + if (perf_pmu__scan_file(pmu, "caps/min_interval", "%lu", &val) != 1) + val = 0; + data[ARM_SPE_CAP_MIN_IVAL] = val; + } + + free(cpuid); + return ARM_SPE_CPU_PRIV_MAX; } static int arm_spe_info_fill(struct auxtrace_record *itr, @@ -49,20 +125,46 @@ static int arm_spe_info_fill(struct auxtrace_record *itr, struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size) { + int i, ret; + size_t offset; struct arm_spe_recording *sper = container_of(itr, struct arm_spe_recording, itr); struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; + struct perf_cpu_map *cpu_map; + struct perf_cpu cpu; + __u64 *data; - if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE) + if (priv_size != arm_spe_info_priv_size(itr, session->evlist)) return -EINVAL; if (!session->evlist->core.nr_mmaps) return -EINVAL; + cpu_map = arm_spe_find_cpus(session->evlist); + if (!cpu_map) + return -EINVAL; + auxtrace_info->type = PERF_AUXTRACE_ARM_SPE; - auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type; + auxtrace_info->priv[ARM_SPE_HEADER_VERSION] = ARM_SPE_HEADER_CURRENT_VERSION; + auxtrace_info->priv[ARM_SPE_HEADER_SIZE] = + ARM_SPE_AUXTRACE_PRIV_MAX - ARM_SPE_HEADER_VERSION; + auxtrace_info->priv[ARM_SPE_PMU_TYPE_V2] = arm_spe_pmu->type; + auxtrace_info->priv[ARM_SPE_CPUS_NUM] = perf_cpu_map__nr(cpu_map); + + offset = ARM_SPE_AUXTRACE_PRIV_MAX; + perf_cpu_map__for_each_cpu(cpu, i, cpu_map) { + assert(offset < priv_size); + data = &auxtrace_info->priv[offset]; + ret = arm_spe_save_cpu_header(itr, cpu, data); + if (ret < 0) + goto out; + offset += ret; + } - return 0; + ret = 0; +out: + perf_cpu_map__put(cpu_map); + return ret; } static void @@ -188,9 +290,9 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, evlist__for_each_entry(evlist, evsel) { if (evsel__is_aux_event(evsel)) { - if (!strstarts(evsel->pmu_name, ARM_SPE_PMU_NAME)) { + if (!strstarts(evsel->pmu->name, ARM_SPE_PMU_NAME)) { pr_err("Found unexpected auxtrace event: %s\n", - evsel->pmu_name); + evsel->pmu->name); return -EINVAL; } opts->full_auxtrace = true; diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c deleted file mode 100644 index 917b97d7c5d36..0000000000000 --- a/tools/perf/arch/arm64/util/dwarf-regs.c +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Will Deacon, ARM Ltd. - */ - -#include -#include -#include -#include -#include /* for struct user_pt_regs */ -#include - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} -#define GPR_DWARFNUM_NAME(num) \ - {.name = __stringify(%x##num), .dwarfnum = num} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} -#define DWARFNUM2OFFSET(index) \ - (index * sizeof((struct user_pt_regs *)0)->regs[0]) - -/* - * Reference: - * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf - */ -static const struct pt_regs_dwarfnum regdwarfnum_table[] = { - GPR_DWARFNUM_NAME(0), - GPR_DWARFNUM_NAME(1), - GPR_DWARFNUM_NAME(2), - GPR_DWARFNUM_NAME(3), - GPR_DWARFNUM_NAME(4), - GPR_DWARFNUM_NAME(5), - GPR_DWARFNUM_NAME(6), - GPR_DWARFNUM_NAME(7), - GPR_DWARFNUM_NAME(8), - GPR_DWARFNUM_NAME(9), - GPR_DWARFNUM_NAME(10), - GPR_DWARFNUM_NAME(11), - GPR_DWARFNUM_NAME(12), - GPR_DWARFNUM_NAME(13), - GPR_DWARFNUM_NAME(14), - GPR_DWARFNUM_NAME(15), - GPR_DWARFNUM_NAME(16), - GPR_DWARFNUM_NAME(17), - GPR_DWARFNUM_NAME(18), - GPR_DWARFNUM_NAME(19), - GPR_DWARFNUM_NAME(20), - GPR_DWARFNUM_NAME(21), - GPR_DWARFNUM_NAME(22), - GPR_DWARFNUM_NAME(23), - GPR_DWARFNUM_NAME(24), - GPR_DWARFNUM_NAME(25), - GPR_DWARFNUM_NAME(26), - GPR_DWARFNUM_NAME(27), - GPR_DWARFNUM_NAME(28), - GPR_DWARFNUM_NAME(29), - REG_DWARFNUM_NAME("%lr", 30), - REG_DWARFNUM_NAME("%sp", 31), - REG_DWARFNUM_END, -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (roff->dwarfnum == n) - return roff->name; - return NULL; -} - -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_dwarfnum *roff; - - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (!strcmp(roff->name, name)) - return DWARFNUM2OFFSET(roff->dwarfnum); - return -EINVAL; -} diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c index 741df3614a09a..f445a2dd62934 100644 --- a/tools/perf/arch/arm64/util/header.c +++ b/tools/perf/arch/arm64/util/header.c @@ -14,73 +14,66 @@ #define MIDR_REVISION_MASK GENMASK(3, 0) #define MIDR_VARIANT_MASK GENMASK(23, 20) -static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus) +static int _get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) { + char path[PATH_MAX]; + FILE *file; const char *sysfs = sysfs__mountpoint(); - struct perf_cpu cpu; - int idx, ret = EINVAL; + assert(cpu.cpu != -1); if (!sysfs || sz < MIDR_SIZE) return EINVAL; - perf_cpu_map__for_each_cpu(cpu, idx, cpus) { - char path[PATH_MAX]; - FILE *file; - - scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, - sysfs, cpu.cpu); - - file = fopen(path, "r"); - if (!file) { - pr_debug("fopen failed for file %s\n", path); - continue; - } - - if (!fgets(buf, MIDR_SIZE, file)) { - fclose(file); - continue; - } - fclose(file); + scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, sysfs, cpu.cpu); - /* got midr break loop */ - ret = 0; - break; + file = fopen(path, "r"); + if (!file) { + pr_debug("fopen failed for file %s\n", path); + return EINVAL; } - return ret; + if (!fgets(buf, MIDR_SIZE, file)) { + pr_debug("Failed to read file %s\n", path); + fclose(file); + return EINVAL; + } + fclose(file); + return 0; } -int get_cpuid(char *buf, size_t sz) +int get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) { - struct perf_cpu_map *cpus = perf_cpu_map__new_online_cpus(); - int ret; + struct perf_cpu_map *cpus; + int idx; + if (cpu.cpu != -1) + return _get_cpuid(buf, sz, cpu); + + cpus = perf_cpu_map__new_online_cpus(); if (!cpus) return EINVAL; - ret = _get_cpuid(buf, sz, cpus); - - perf_cpu_map__put(cpus); + perf_cpu_map__for_each_cpu(cpu, idx, cpus) { + int ret = _get_cpuid(buf, sz, cpu); - return ret; + if (ret == 0) + return 0; + } + return EINVAL; } -char *get_cpuid_str(struct perf_pmu *pmu) +char *get_cpuid_str(struct perf_cpu cpu) { - char *buf = NULL; + char *buf = malloc(MIDR_SIZE); int res; - if (!pmu || !pmu->cpus) - return NULL; - - buf = malloc(MIDR_SIZE); if (!buf) return NULL; /* read midr from list of cpus mapped to this pmu */ - res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus); + res = get_cpuid(buf, MIDR_SIZE, cpu); if (res) { - pr_err("failed to get cpuid string for PMU %s\n", pmu->name); + pr_err("failed to get cpuid string for CPU %d\n", cpu.cpu); free(buf); buf = NULL; } diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c index 2a4eab2d160ee..895fb0d0610cf 100644 --- a/tools/perf/arch/arm64/util/pmu.c +++ b/tools/perf/arch/arm64/util/pmu.c @@ -1,30 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include "../../../util/cpumap.h" -#include "../../../util/header.h" #include "../../../util/pmu.h" #include "../../../util/pmus.h" +#include "../../../util/tool_pmu.h" #include -#include -const struct pmu_metrics_table *pmu_metrics_table__find(void) -{ - struct perf_pmu *pmu; - - /* Metrics aren't currently supported on heterogeneous Arm systems */ - if (perf_pmus__num_core_pmus() > 1) - return NULL; - - /* Doesn't matter which one here because they'll all be the same */ - pmu = perf_pmus__find_core_pmu(); - if (pmu) - return perf_pmu__find_metrics_table(pmu); - - return NULL; -} - -double perf_pmu__cpu_slots_per_cycle(void) +u64 tool_pmu__cpu_slots_per_cycle(void) { char path[PATH_MAX]; unsigned long long slots = 0; @@ -41,5 +22,5 @@ double perf_pmu__cpu_slots_per_cycle(void) filename__read_ull(path, &slots); } - return slots ? (double)slots : NAN; + return slots; } diff --git a/tools/perf/arch/csky/Makefile b/tools/perf/arch/csky/Makefile deleted file mode 100644 index 88c08eed9c7be..0000000000000 --- a/tools/perf/arch/csky/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif diff --git a/tools/perf/arch/csky/annotate/instructions.c b/tools/perf/arch/csky/annotate/instructions.c index 5337bfb7d5fcf..14270311d215e 100644 --- a/tools/perf/arch/csky/annotate/instructions.c +++ b/tools/perf/arch/csky/annotate/instructions.c @@ -43,6 +43,11 @@ static int csky__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->initialized = true; arch->objdump.comment_char = '/'; arch->associate_instruction_ops = csky__associate_ins_ops; - + arch->e_machine = EM_CSKY; +#if defined(__CSKYABIV2__) + arch->e_flags = EF_CSKY_ABIV2; +#else + arch->e_flags = EF_CSKY_ABIV1; +#endif return 0; } diff --git a/tools/perf/arch/csky/util/Build b/tools/perf/arch/csky/util/Build index 99d83f41bf433..5e6ea82c42021 100644 --- a/tools/perf/arch/csky/util/Build +++ b/tools/perf/arch/csky/util/Build @@ -1,4 +1,3 @@ perf-util-y += perf_regs.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile index c89d6bb6b184b..52544d59245ba 100644 --- a/tools/perf/arch/loongarch/Makefile +++ b/tools/perf/arch/loongarch/Makefile @@ -1,8 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 PERF_HAVE_JITDUMP := 1 HAVE_KVM_STAT_SUPPORT := 1 diff --git a/tools/perf/arch/loongarch/annotate/instructions.c b/tools/perf/arch/loongarch/annotate/instructions.c index ab43b1ab51e3b..70262d5f14442 100644 --- a/tools/perf/arch/loongarch/annotate/instructions.c +++ b/tools/perf/arch/loongarch/annotate/instructions.c @@ -131,6 +131,8 @@ int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = loongarch__associate_ins_ops; arch->initialized = true; arch->objdump.comment_char = '#'; + arch->e_machine = EM_LOONGARCH; + arch->e_flags = 0; } return 0; diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build index b6b97de482333..0aa31986ecb5b 100644 --- a/tools/perf/arch/loongarch/util/Build +++ b/tools/perf/arch/loongarch/util/Build @@ -1,7 +1,6 @@ perf-util-y += header.o perf-util-y += perf_regs.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o diff --git a/tools/perf/arch/loongarch/util/dwarf-regs.c b/tools/perf/arch/loongarch/util/dwarf-regs.c deleted file mode 100644 index 0f6ebc3874631..0000000000000 --- a/tools/perf/arch/loongarch/util/dwarf-regs.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2020-2023 Loongson Technology Corporation Limited - */ - -#include -#include /* for EINVAL */ -#include /* for strcmp */ -#include - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -static struct pt_regs_dwarfnum loongarch_gpr_table[] = { - {"%r0", 0}, {"%r1", 1}, {"%r2", 2}, {"%r3", 3}, - {"%r4", 4}, {"%r5", 5}, {"%r6", 6}, {"%r7", 7}, - {"%r8", 8}, {"%r9", 9}, {"%r10", 10}, {"%r11", 11}, - {"%r12", 12}, {"%r13", 13}, {"%r14", 14}, {"%r15", 15}, - {"%r16", 16}, {"%r17", 17}, {"%r18", 18}, {"%r19", 19}, - {"%r20", 20}, {"%r21", 21}, {"%r22", 22}, {"%r23", 23}, - {"%r24", 24}, {"%r25", 25}, {"%r26", 26}, {"%r27", 27}, - {"%r28", 28}, {"%r29", 29}, {"%r30", 30}, {"%r31", 31}, - {NULL, 0} -}; - -const char *get_arch_regstr(unsigned int n) -{ - n %= 32; - return loongarch_gpr_table[n].name; -} - -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_dwarfnum *roff; - - for (roff = loongarch_gpr_table; roff->name != NULL; roff++) - if (!strcmp(roff->name, name)) - return roff->dwarfnum; - return -EINVAL; -} diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c index d962dff55512b..0c6d823334a21 100644 --- a/tools/perf/arch/loongarch/util/header.c +++ b/tools/perf/arch/loongarch/util/header.c @@ -70,7 +70,7 @@ static char *_get_cpuid(void) return cpuid; } -int get_cpuid(char *buffer, size_t sz) +int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused) { int ret = 0; char *cpuid = _get_cpuid(); @@ -90,7 +90,7 @@ int get_cpuid(char *buffer, size_t sz) return ret; } -char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +char *get_cpuid_str(struct perf_cpu cpu __maybe_unused) { return _get_cpuid(); } diff --git a/tools/perf/arch/mips/Makefile b/tools/perf/arch/mips/Makefile index cd0b011b3be5f..827168f1077a6 100644 --- a/tools/perf/arch/mips/Makefile +++ b/tools/perf/arch/mips/Makefile @@ -1,8 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif - # Syscall table generation for perf out := $(OUTPUT)arch/mips/include/generated/asm header := $(out)/syscalls_n64.c diff --git a/tools/perf/arch/mips/annotate/instructions.c b/tools/perf/arch/mips/annotate/instructions.c index 340993f2a897a..b50b46c613d66 100644 --- a/tools/perf/arch/mips/annotate/instructions.c +++ b/tools/perf/arch/mips/annotate/instructions.c @@ -40,6 +40,8 @@ int mips__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = mips__associate_ins_ops; arch->initialized = true; arch->objdump.comment_char = '#'; + arch->e_machine = EM_MIPS; + arch->e_flags = 0; } return 0; diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Build index e4644f1e68a03..691fa2051958a 100644 --- a/tools/perf/arch/mips/util/Build +++ b/tools/perf/arch/mips/util/Build @@ -1,3 +1,2 @@ perf-util-y += perf_regs.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o diff --git a/tools/perf/arch/mips/util/dwarf-regs.c b/tools/perf/arch/mips/util/dwarf-regs.c deleted file mode 100644 index 25c13a91c2a75..0000000000000 --- a/tools/perf/arch/mips/util/dwarf-regs.c +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2013 Cavium, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include - -static const char *mips_gpr_names[32] = { - "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", - "$10", "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19", - "$20", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", - "$30", "$31" -}; - -const char *get_arch_regstr(unsigned int n) -{ - if (n < 32) - return mips_gpr_names[n]; - if (n == 64) - return "hi"; - if (n == 65) - return "lo"; - return NULL; -} diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile index bf6d323574f66..dc8f4fb8e324a 100644 --- a/tools/perf/arch/powerpc/Makefile +++ b/tools/perf/arch/powerpc/Makefile @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif - HAVE_KVM_STAT_SUPPORT := 1 -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 PERF_HAVE_JITDUMP := 1 # diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index ede9eeade0ab3..ca567cfdcbdb4 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -255,7 +255,7 @@ static struct ins_ops *check_ppc_insn(struct disasm_line *dl) * is moved to r31. update_insn_state_powerpc tracks these state * changes */ -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT static void update_insn_state_powerpc(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused, struct disasm_line *dl) @@ -300,7 +300,7 @@ static void update_insn_state_powerpc(struct type_state *state, insn_offset, src->reg1, dst->reg1); pr_debug_type_name(&tsr->type, tsr->kind); } -#endif /* HAVE_DWARF_SUPPORT */ +#endif /* HAVE_LIBDW_SUPPORT */ static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) { @@ -309,6 +309,8 @@ static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = powerpc__associate_instruction_ops; arch->objdump.comment_char = '#'; annotate_opts.show_asm_raw = true; + arch->e_machine = EM_PPC; + arch->e_flags = 0; } return 0; diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index 6c588ecdf3bd4..ed82715080f96 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build @@ -7,8 +7,7 @@ perf-util-y += sym-handling.o perf-util-y += evsel.o perf-util-y += event.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o -perf-util-$(CONFIG_DWARF) += skip-callchain-idx.o +perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c deleted file mode 100644 index 104c7ae5c433d..0000000000000 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Ian Munsie, IBM Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; - unsigned int ptregs_offset; -}; - -#define REG_DWARFNUM_NAME(r, num) \ - {.name = __stringify(%)__stringify(r), .dwarfnum = num, \ - .ptregs_offset = offsetof(struct pt_regs, r)} -#define GPR_DWARFNUM_NAME(num) \ - {.name = __stringify(%gpr##num), .dwarfnum = num, \ - .ptregs_offset = offsetof(struct pt_regs, gpr[num])} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0, .ptregs_offset = 0} - -/* - * Reference: - * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html - */ -static const struct pt_regs_dwarfnum regdwarfnum_table[] = { - GPR_DWARFNUM_NAME(0), - GPR_DWARFNUM_NAME(1), - GPR_DWARFNUM_NAME(2), - GPR_DWARFNUM_NAME(3), - GPR_DWARFNUM_NAME(4), - GPR_DWARFNUM_NAME(5), - GPR_DWARFNUM_NAME(6), - GPR_DWARFNUM_NAME(7), - GPR_DWARFNUM_NAME(8), - GPR_DWARFNUM_NAME(9), - GPR_DWARFNUM_NAME(10), - GPR_DWARFNUM_NAME(11), - GPR_DWARFNUM_NAME(12), - GPR_DWARFNUM_NAME(13), - GPR_DWARFNUM_NAME(14), - GPR_DWARFNUM_NAME(15), - GPR_DWARFNUM_NAME(16), - GPR_DWARFNUM_NAME(17), - GPR_DWARFNUM_NAME(18), - GPR_DWARFNUM_NAME(19), - GPR_DWARFNUM_NAME(20), - GPR_DWARFNUM_NAME(21), - GPR_DWARFNUM_NAME(22), - GPR_DWARFNUM_NAME(23), - GPR_DWARFNUM_NAME(24), - GPR_DWARFNUM_NAME(25), - GPR_DWARFNUM_NAME(26), - GPR_DWARFNUM_NAME(27), - GPR_DWARFNUM_NAME(28), - GPR_DWARFNUM_NAME(29), - GPR_DWARFNUM_NAME(30), - GPR_DWARFNUM_NAME(31), - REG_DWARFNUM_NAME(msr, 66), - REG_DWARFNUM_NAME(ctr, 109), - REG_DWARFNUM_NAME(link, 108), - REG_DWARFNUM_NAME(xer, 101), - REG_DWARFNUM_NAME(dar, 119), - REG_DWARFNUM_NAME(dsisr, 118), - REG_DWARFNUM_END, -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (roff->dwarfnum == n) - return roff->name; - return NULL; -} - -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (!strcmp(roff->name, name)) - return roff->ptregs_offset; - return -EINVAL; -} - -#define PPC_OP(op) (((op) >> 26) & 0x3F) -#define PPC_RA(a) (((a) >> 16) & 0x1f) -#define PPC_RT(t) (((t) >> 21) & 0x1f) -#define PPC_RB(b) (((b) >> 11) & 0x1f) -#define PPC_D(D) ((D) & 0xfffe) -#define PPC_DS(DS) ((DS) & 0xfffc) -#define OP_LD 58 -#define OP_STD 62 - -static int get_source_reg(u32 raw_insn) -{ - return PPC_RA(raw_insn); -} - -static int get_target_reg(u32 raw_insn) -{ - return PPC_RT(raw_insn); -} - -static int get_offset_opcode(u32 raw_insn) -{ - int opcode = PPC_OP(raw_insn); - - /* DS- form */ - if ((opcode == OP_LD) || (opcode == OP_STD)) - return PPC_DS(raw_insn); - else - return PPC_D(raw_insn); -} - -/* - * Fills the required fields for op_loc depending on if it - * is a source or target. - * D form: ins RT,D(RA) -> src_reg1 = RA, offset = D, dst_reg1 = RT - * DS form: ins RT,DS(RA) -> src_reg1 = RA, offset = DS, dst_reg1 = RT - * X form: ins RT,RA,RB -> src_reg1 = RA, src_reg2 = RB, dst_reg1 = RT - */ -void get_powerpc_regs(u32 raw_insn, int is_source, - struct annotated_op_loc *op_loc) -{ - if (is_source) - op_loc->reg1 = get_source_reg(raw_insn); - else - op_loc->reg1 = get_target_reg(raw_insn); - - if (op_loc->multi_regs) - op_loc->reg2 = PPC_RB(raw_insn); - - /* TODO: Implement offset handling for X Form */ - if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31)) - op_loc->offset = get_offset_opcode(raw_insn); -} diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index 6b00efd53638d..c7df534dbf8f8 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c @@ -10,9 +10,21 @@ #include "utils_header.h" #include "metricgroup.h" #include +#include + +static bool is_compat_mode(void) +{ + u64 base_platform = getauxval(AT_BASE_PLATFORM); + u64 platform = getauxval(AT_PLATFORM); + + if (!strcmp((char *)platform, (char *)base_platform)) + return false; + + return true; +} int -get_cpuid(char *buffer, size_t sz) +get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused) { unsigned long pvr; int nb; @@ -30,11 +42,29 @@ get_cpuid(char *buffer, size_t sz) } char * -get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +get_cpuid_str(struct perf_cpu cpu __maybe_unused) { char *bufp; + unsigned long pvr; + + /* + * IBM Power System supports compatible mode. That is + * Nth generation platform can support previous generation + * OS in a mode called compatibile mode. For ex. LPAR can be + * booted in a Power9 mode when the system is a Power10. + * + * In the compatible mode, care must be taken when generating + * PVR value. When read, PVR will be of the AT_BASE_PLATFORM + * To support generic events, return 0x00ffffff as pvr when + * booted in compat mode. Based on this pvr value, json will + * pick events from pmu-events/arch/powerpc/compat + */ + if (!is_compat_mode()) + pvr = mfspr(SPRN_PVR); + else + pvr = 0x00ffffff; - if (asprintf(&bufp, "0x%.8lx", mfspr(SPRN_PVR)) < 0) + if (asprintf(&bufp, "0x%.8lx", pvr) < 0) bufp = NULL; return bufp; diff --git a/tools/perf/arch/riscv/Makefile b/tools/perf/arch/riscv/Makefile index 90c3c476a242c..18ad078000e2b 100644 --- a/tools/perf/arch/riscv/Makefile +++ b/tools/perf/arch/riscv/Makefile @@ -1,6 +1,25 @@ -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 +# SPDX-License-Identifier: GPL-2.0 PERF_HAVE_JITDUMP := 1 HAVE_KVM_STAT_SUPPORT := 1 + +# +# Syscall table generation for perf +# + +out := $(OUTPUT)arch/riscv/include/generated/asm +header := $(out)/syscalls.c +incpath := $(srctree)/tools +sysdef := $(srctree)/tools/arch/riscv/include/uapi/asm/unistd.h +sysprf := $(srctree)/tools/perf/arch/riscv/entry/syscalls/ +systbl := $(sysprf)/mksyscalltbl + +# Create output directory if not already present +$(shell [ -d '$(out)' ] || mkdir -p '$(out)') + +$(header): $(sysdef) $(systbl) + $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@ + +clean:: + $(call QUIET_CLEAN, riscv) $(RM) $(header) + +archheaders: $(header) diff --git a/tools/perf/arch/riscv/entry/syscalls/mksyscalltbl b/tools/perf/arch/riscv/entry/syscalls/mksyscalltbl new file mode 100755 index 0000000000000..c59f5e852b977 --- /dev/null +++ b/tools/perf/arch/riscv/entry/syscalls/mksyscalltbl @@ -0,0 +1,47 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Generate system call table for perf. Derived from +# powerpc script. +# +# Copyright IBM Corp. 2017 +# Author(s): Hendrik Brueckner +# Changed by: Ravi Bangoria +# Changed by: Kim Phillips +# Changed by: Björn Töpel + +gcc=$1 +hostcc=$2 +incpath=$3 +input=$4 + +if ! test -r $input; then + echo "Could not read input file" >&2 + exit 1 +fi + +create_sc_table() +{ + local sc nr max_nr + + while read sc nr; do + printf "%s\n" " [$nr] = \"$sc\"," + max_nr=$nr + done + + echo "#define SYSCALLTBL_RISCV_MAX_ID $max_nr" +} + +create_table() +{ + echo "#include \"$input\"" + echo "static const char *const syscalltbl_riscv[] = {" + create_sc_table + echo "};" +} + +$gcc -E -dM -x c -I $incpath/include/uapi $input \ + |awk '$2 ~ "__NR" && $3 !~ "__NR3264_" { + sub("^#define __NR(3264)?_", ""); + print | "sort -k2 -n"}' \ + |create_table diff --git a/tools/perf/arch/riscv/include/dwarf-regs-table.h b/tools/perf/arch/riscv/include/dwarf-regs-table.h new file mode 100644 index 0000000000000..a45b63a6d5a8e --- /dev/null +++ b/tools/perf/arch/riscv/include/dwarf-regs-table.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifdef DEFINE_DWARF_REGSTR_TABLE +/* This is included in perf/util/dwarf-regs.c */ + +#define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg + +static const char * const riscv_regstr_tbl[] = { + REG_DWARFNUM_NAME("%zero", 0), + REG_DWARFNUM_NAME("%ra", 1), + REG_DWARFNUM_NAME("%sp", 2), + REG_DWARFNUM_NAME("%gp", 3), + REG_DWARFNUM_NAME("%tp", 4), + REG_DWARFNUM_NAME("%t0", 5), + REG_DWARFNUM_NAME("%t1", 6), + REG_DWARFNUM_NAME("%t2", 7), + REG_DWARFNUM_NAME("%s0", 8), + REG_DWARFNUM_NAME("%s1", 9), + REG_DWARFNUM_NAME("%a0", 10), + REG_DWARFNUM_NAME("%a1", 11), + REG_DWARFNUM_NAME("%a2", 12), + REG_DWARFNUM_NAME("%a3", 13), + REG_DWARFNUM_NAME("%a4", 14), + REG_DWARFNUM_NAME("%a5", 15), + REG_DWARFNUM_NAME("%a6", 16), + REG_DWARFNUM_NAME("%a7", 17), + REG_DWARFNUM_NAME("%s2", 18), + REG_DWARFNUM_NAME("%s3", 19), + REG_DWARFNUM_NAME("%s4", 20), + REG_DWARFNUM_NAME("%s5", 21), + REG_DWARFNUM_NAME("%s6", 22), + REG_DWARFNUM_NAME("%s7", 23), + REG_DWARFNUM_NAME("%s8", 24), + REG_DWARFNUM_NAME("%s9", 25), + REG_DWARFNUM_NAME("%s10", 26), + REG_DWARFNUM_NAME("%s11", 27), + REG_DWARFNUM_NAME("%t3", 28), + REG_DWARFNUM_NAME("%t4", 29), + REG_DWARFNUM_NAME("%t5", 30), + REG_DWARFNUM_NAME("%t6", 31), +}; + +#endif diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build index f865cb0489ec6..58a672246024d 100644 --- a/tools/perf/arch/riscv/util/Build +++ b/tools/perf/arch/riscv/util/Build @@ -2,5 +2,4 @@ perf-util-y += perf_regs.o perf-util-y += header.o perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/riscv/util/dwarf-regs.c b/tools/perf/arch/riscv/util/dwarf-regs.c deleted file mode 100644 index cd0504c02e2e0..0000000000000 --- a/tools/perf/arch/riscv/util/dwarf-regs.c +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. - * Mapping of DWARF debug register numbers into register names. - */ - -#include -#include /* for EINVAL */ -#include /* for strcmp */ -#include - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} - -struct pt_regs_dwarfnum riscv_dwarf_regs_table[] = { - REG_DWARFNUM_NAME("%zero", 0), - REG_DWARFNUM_NAME("%ra", 1), - REG_DWARFNUM_NAME("%sp", 2), - REG_DWARFNUM_NAME("%gp", 3), - REG_DWARFNUM_NAME("%tp", 4), - REG_DWARFNUM_NAME("%t0", 5), - REG_DWARFNUM_NAME("%t1", 6), - REG_DWARFNUM_NAME("%t2", 7), - REG_DWARFNUM_NAME("%s0", 8), - REG_DWARFNUM_NAME("%s1", 9), - REG_DWARFNUM_NAME("%a0", 10), - REG_DWARFNUM_NAME("%a1", 11), - REG_DWARFNUM_NAME("%a2", 12), - REG_DWARFNUM_NAME("%a3", 13), - REG_DWARFNUM_NAME("%a4", 14), - REG_DWARFNUM_NAME("%a5", 15), - REG_DWARFNUM_NAME("%a6", 16), - REG_DWARFNUM_NAME("%a7", 17), - REG_DWARFNUM_NAME("%s2", 18), - REG_DWARFNUM_NAME("%s3", 19), - REG_DWARFNUM_NAME("%s4", 20), - REG_DWARFNUM_NAME("%s5", 21), - REG_DWARFNUM_NAME("%s6", 22), - REG_DWARFNUM_NAME("%s7", 23), - REG_DWARFNUM_NAME("%s8", 24), - REG_DWARFNUM_NAME("%s9", 25), - REG_DWARFNUM_NAME("%s10", 26), - REG_DWARFNUM_NAME("%s11", 27), - REG_DWARFNUM_NAME("%t3", 28), - REG_DWARFNUM_NAME("%t4", 29), - REG_DWARFNUM_NAME("%t5", 30), - REG_DWARFNUM_NAME("%t6", 31), - REG_DWARFNUM_END, -}; - -#define RISCV_MAX_REGS ((sizeof(riscv_dwarf_regs_table) / \ - sizeof(riscv_dwarf_regs_table[0])) - 1) - -const char *get_arch_regstr(unsigned int n) -{ - return (n < RISCV_MAX_REGS) ? riscv_dwarf_regs_table[n].name : NULL; -} - -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_dwarfnum *roff; - - for (roff = riscv_dwarf_regs_table; roff->name; roff++) - if (!strcmp(roff->name, name)) - return roff->dwarfnum; - return -EINVAL; -} diff --git a/tools/perf/arch/riscv/util/header.c b/tools/perf/arch/riscv/util/header.c index 1b29030021eeb..4b839203d4a54 100644 --- a/tools/perf/arch/riscv/util/header.c +++ b/tools/perf/arch/riscv/util/header.c @@ -81,7 +81,7 @@ static char *_get_cpuid(void) return cpuid; } -int get_cpuid(char *buffer, size_t sz) +int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused) { char *cpuid = _get_cpuid(); int ret = 0; @@ -98,7 +98,7 @@ int get_cpuid(char *buffer, size_t sz) } char * -get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +get_cpuid_str(struct perf_cpu cpu __maybe_unused) { return _get_cpuid(); } diff --git a/tools/perf/arch/riscv64/annotate/instructions.c b/tools/perf/arch/riscv64/annotate/instructions.c index 869a0eb289531..55cf911633f8b 100644 --- a/tools/perf/arch/riscv64/annotate/instructions.c +++ b/tools/perf/arch/riscv64/annotate/instructions.c @@ -28,6 +28,8 @@ int riscv64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = riscv64__associate_ins_ops; arch->initialized = true; arch->objdump.comment_char = '#'; + arch->e_machine = EM_RISCV; + arch->e_flags = 0; } return 0; diff --git a/tools/perf/arch/s390/Makefile b/tools/perf/arch/s390/Makefile index 56994e63b43ae..c431c21b11ef8 100644 --- a/tools/perf/arch/s390/Makefile +++ b/tools/perf/arch/s390/Makefile @@ -1,9 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif HAVE_KVM_STAT_SUPPORT := 1 -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 PERF_HAVE_JITDUMP := 1 # diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c index eeac25cca699a..c61193f1e0964 100644 --- a/tools/perf/arch/s390/annotate/instructions.c +++ b/tools/perf/arch/s390/annotate/instructions.c @@ -166,6 +166,8 @@ static int s390__annotate_init(struct arch *arch, char *cpuid __maybe_unused) if (s390__cpuid_parse(arch, cpuid)) err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING; } + arch->e_machine = EM_S390; + arch->e_flags = 0; } return err; diff --git a/tools/perf/arch/s390/util/Build b/tools/perf/arch/s390/util/Build index 1ac830030ff3f..736c0ad091943 100644 --- a/tools/perf/arch/s390/util/Build +++ b/tools/perf/arch/s390/util/Build @@ -2,7 +2,6 @@ perf-util-y += header.o perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o perf-util-y += perf_regs.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o perf-util-y += machine.o diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c deleted file mode 100644 index dfddb3099bfa5..0000000000000 --- a/tools/perf/arch/s390/util/dwarf-regs.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright IBM Corp. 2010, 2017 - * Author(s): Hendrik Brueckner - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "dwarf-regs-table.h" - -const char *get_arch_regstr(unsigned int n) -{ - return (n >= ARRAY_SIZE(s390_dwarf_regs)) ? NULL : s390_dwarf_regs[n]; -} - -/* - * Convert the register name into an offset to struct pt_regs (kernel). - * This is required by the BPF prologue generator. The BPF - * program is called in the BPF overflow handler in the perf - * core. - */ -int regs_query_register_offset(const char *name) -{ - unsigned long gpr; - - if (!name || strncmp(name, "%r", 2)) - return -EINVAL; - - errno = 0; - gpr = strtoul(name + 2, NULL, 10); - if (errno || gpr >= 16) - return -EINVAL; - - return offsetof(user_pt_regs, gprs) + 8 * gpr; -} diff --git a/tools/perf/arch/s390/util/header.c b/tools/perf/arch/s390/util/header.c index 7933f6871c818..db54677a17d24 100644 --- a/tools/perf/arch/s390/util/header.c +++ b/tools/perf/arch/s390/util/header.c @@ -27,7 +27,7 @@ #define SYSINFO "/proc/sysinfo" #define SRVLVL "/proc/service_levels" -int get_cpuid(char *buffer, size_t sz) +int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused) { char *cp, *line = NULL, *line2; char type[8], model[33], version[8], manufacturer[32], authorization[8]; @@ -137,11 +137,11 @@ int get_cpuid(char *buffer, size_t sz) return (nbytes >= sz) ? ENOBUFS : 0; } -char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +char *get_cpuid_str(struct perf_cpu cpu) { char *buf = malloc(128); - if (buf && get_cpuid(buf, 128)) + if (buf && get_cpuid(buf, 128, cpu)) zfree(&buf); return buf; } diff --git a/tools/perf/arch/sh/Build b/tools/perf/arch/sh/Build deleted file mode 100644 index e63eabc2c8f41..0000000000000 --- a/tools/perf/arch/sh/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-y += util/ diff --git a/tools/perf/arch/sh/Makefile b/tools/perf/arch/sh/Makefile deleted file mode 100644 index 88c08eed9c7be..0000000000000 --- a/tools/perf/arch/sh/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif diff --git a/tools/perf/arch/sh/util/Build b/tools/perf/arch/sh/util/Build deleted file mode 100644 index 32f44fc4ab985..0000000000000 --- a/tools/perf/arch/sh/util/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-$(CONFIG_DWARF) += dwarf-regs.o diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c deleted file mode 100644 index 4b17fc86c73bd..0000000000000 --- a/tools/perf/arch/sh/util/dwarf-regs.c +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Matt Fleming - */ - -#include -#include - -/* - * Generic dwarf analysis helpers - */ - -#define SH_MAX_REGS 18 -const char *sh_regs_table[SH_MAX_REGS] = { - "r0", - "r1", - "r2", - "r3", - "r4", - "r5", - "r6", - "r7", - "r8", - "r9", - "r10", - "r11", - "r12", - "r13", - "r14", - "r15", - "pc", - "pr", -}; - -/* Return architecture dependent register string (for kprobe-tracer) */ -const char *get_arch_regstr(unsigned int n) -{ - return (n < SH_MAX_REGS) ? sh_regs_table[n] : NULL; -} diff --git a/tools/perf/arch/sparc/Build b/tools/perf/arch/sparc/Build deleted file mode 100644 index e63eabc2c8f41..0000000000000 --- a/tools/perf/arch/sparc/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-y += util/ diff --git a/tools/perf/arch/sparc/Makefile b/tools/perf/arch/sparc/Makefile index 4031db72ba71a..8b59ce8efb892 100644 --- a/tools/perf/arch/sparc/Makefile +++ b/tools/perf/arch/sparc/Makefile @@ -1,6 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif - PERF_HAVE_JITDUMP := 1 diff --git a/tools/perf/arch/sparc/annotate/instructions.c b/tools/perf/arch/sparc/annotate/instructions.c index 2614c010c2352..68c31580ccfc4 100644 --- a/tools/perf/arch/sparc/annotate/instructions.c +++ b/tools/perf/arch/sparc/annotate/instructions.c @@ -163,6 +163,8 @@ static int sparc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->initialized = true; arch->associate_instruction_ops = sparc__associate_instruction_ops; arch->objdump.comment_char = '#'; + arch->e_machine = EM_SPARC; + arch->e_flags = 0; } return 0; diff --git a/tools/perf/arch/sparc/util/Build b/tools/perf/arch/sparc/util/Build deleted file mode 100644 index 32f44fc4ab985..0000000000000 --- a/tools/perf/arch/sparc/util/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-$(CONFIG_DWARF) += dwarf-regs.o diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c deleted file mode 100644 index 1282cb2dc7bdc..0000000000000 --- a/tools/perf/arch/sparc/util/dwarf-regs.c +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 David S. Miller - */ - -#include -#include - -#define SPARC_MAX_REGS 96 - -const char *sparc_regs_table[SPARC_MAX_REGS] = { - "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", - "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", - "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", - "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", - "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", - "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", - "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", - "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", - "%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39", - "%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47", - "%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55", - "%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63", -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - return (n < SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL; -} diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 67b4969a67383..a6b6e0a9308a8 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile @@ -1,9 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif HAVE_KVM_STAT_SUPPORT := 1 -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 PERF_HAVE_JITDUMP := 1 ### diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 5caf5a17f03d8..ae94b1f0b9cce 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -202,12 +202,13 @@ static int x86__annotate_init(struct arch *arch, char *cpuid) if (x86__cpuid_parse(arch, cpuid)) err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING; } - + arch->e_machine = EM_X86_64; + arch->e_flags = 0; arch->initialized = true; return err; } -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT static void update_insn_state_x86(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, struct disasm_line *dl) diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c deleted file mode 100644 index 360a082fc9280..0000000000000 --- a/tools/perf/arch/x86/tests/intel-cqm.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "tests/tests.h" -#include "cloexec.h" -#include "debug.h" -#include "evlist.h" -#include "evsel.h" -#include "arch-tests.h" -#include // page_size - -#include -#include -#include -#include -#include - -static pid_t spawn(void) -{ - pid_t pid; - - pid = fork(); - if (pid) - return pid; - - while(1) - sleep(5); - return 0; -} - -/* - * Create an event group that contains both a sampled hardware - * (cpu-cycles) and software (intel_cqm/llc_occupancy/) event. We then - * wait for the hardware perf counter to overflow and generate a PMI, - * which triggers an event read for both of the events in the group. - * - * Since reading Intel CQM event counters requires sending SMP IPIs, the - * CQM pmu needs to handle the above situation gracefully, and return - * the last read counter value to avoid triggering a WARN_ON_ONCE() in - * smp_call_function_many() caused by sending IPIs from NMI context. - */ -int test__intel_cqm_count_nmi_context(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - struct evlist *evlist = NULL; - struct evsel *evsel = NULL; - struct perf_event_attr pe; - int i, fd[2], flag, ret; - size_t mmap_len; - void *event; - pid_t pid; - int err = TEST_FAIL; - - flag = perf_event_open_cloexec_flag(); - - evlist = evlist__new(); - if (!evlist) { - pr_debug("evlist__new failed\n"); - return TEST_FAIL; - } - - ret = parse_event(evlist, "intel_cqm/llc_occupancy/"); - if (ret) { - pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n"); - err = TEST_SKIP; - goto out; - } - - evsel = evlist__first(evlist); - if (!evsel) { - pr_debug("evlist__first failed\n"); - goto out; - } - - memset(&pe, 0, sizeof(pe)); - pe.size = sizeof(pe); - - pe.type = PERF_TYPE_HARDWARE; - pe.config = PERF_COUNT_HW_CPU_CYCLES; - pe.read_format = PERF_FORMAT_GROUP; - - pe.sample_period = 128; - pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ; - - pid = spawn(); - - fd[0] = sys_perf_event_open(&pe, pid, -1, -1, flag); - if (fd[0] < 0) { - pr_debug("failed to open event\n"); - goto out; - } - - memset(&pe, 0, sizeof(pe)); - pe.size = sizeof(pe); - - pe.type = evsel->attr.type; - pe.config = evsel->attr.config; - - fd[1] = sys_perf_event_open(&pe, pid, -1, fd[0], flag); - if (fd[1] < 0) { - pr_debug("failed to open event\n"); - goto out; - } - - /* - * Pick a power-of-two number of pages + 1 for the meta-data - * page (struct perf_event_mmap_page). See tools/perf/design.txt. - */ - mmap_len = page_size * 65; - - event = mmap(NULL, mmap_len, PROT_READ, MAP_SHARED, fd[0], 0); - if (event == (void *)(-1)) { - pr_debug("failed to mmap %d\n", errno); - goto out; - } - - sleep(1); - - err = TEST_OK; - - munmap(event, mmap_len); - - for (i = 0; i < 2; i++) - close(fd[i]); - - kill(pid, SIGKILL); - wait(NULL); -out: - evlist__delete(evlist); - return err; -} diff --git a/tools/perf/arch/x86/tests/intel-pt-test.c b/tools/perf/arch/x86/tests/intel-pt-test.c index 09d61fa736e36..b217ed67cd4e5 100644 --- a/tools/perf/arch/x86/tests/intel-pt-test.c +++ b/tools/perf/arch/x86/tests/intel-pt-test.c @@ -375,7 +375,7 @@ static int get_pt_caps(int cpu, struct pt_caps *caps) return 0; } -static bool is_hydrid(void) +static bool is_hybrid(void) { unsigned int eax, ebx, ecx, edx = 0; bool result; @@ -441,7 +441,7 @@ int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest) int ret = TEST_OK; int cpu; - if (!is_hydrid()) { + if (!is_hybrid()) { test->test_cases[subtest].skip_reason = "not hybrid"; return TEST_SKIP; } diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build index 2607ed5c42966..848327378694d 100644 --- a/tools/perf/arch/x86/util/Build +++ b/tools/perf/arch/x86/util/Build @@ -10,10 +10,6 @@ perf-util-y += evlist.o perf-util-y += mem-events.o perf-util-y += evsel.o perf-util-y += iostat.o -perf-util-y += env.o - -perf-util-$(CONFIG_DWARF) += dwarf-regs.o -perf-util-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/x86/util/auxtrace.c b/tools/perf/arch/x86/util/auxtrace.c index 354780ff1605d..ecbf61a7eb3a3 100644 --- a/tools/perf/arch/x86/util/auxtrace.c +++ b/tools/perf/arch/x86/util/auxtrace.c @@ -55,11 +55,12 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, int *err) { char buffer[64]; + struct perf_cpu cpu = perf_cpu_map__min(evlist->core.all_cpus); int ret; *err = 0; - ret = get_cpuid(buffer, sizeof(buffer)); + ret = get_cpuid(buffer, sizeof(buffer), cpu); if (ret) { *err = ret; return NULL; diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c deleted file mode 100644 index 399c4a0a29d8c..0000000000000 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. - * Extracted from probe-finder.c - * - * Written by Masami Hiramatsu - */ - -#include -#include /* for EINVAL */ -#include /* for strcmp */ -#include /* for struct pt_regs */ -#include /* for offsetof */ -#include - -/* - * See arch/x86/kernel/ptrace.c. - * Different from it: - * - * - Since struct pt_regs is defined differently for user and kernel, - * but we want to use 'ax, bx' instead of 'rax, rbx' (which is struct - * field name of user's pt_regs), we make REG_OFFSET_NAME to accept - * both string name and reg field name. - * - * - Since accessing x86_32's pt_regs from x86_64 building is difficult - * and vise versa, we simply fill offset with -1, so - * get_arch_regstr() still works but regs_query_register_offset() - * returns error. - * The only inconvenience caused by it now is that we are not allowed - * to generate BPF prologue for a x86_64 kernel if perf is built for - * x86_32. This is really a rare usecase. - * - * - Order is different from kernel's ptrace.c for get_arch_regstr(). Use - * the order defined by dwarf. - */ - -struct pt_regs_offset { - const char *name; - int offset; -}; - -#define REG_OFFSET_END {.name = NULL, .offset = 0} - -#ifdef __x86_64__ -# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)} -# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = -1} -#else -# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = -1} -# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)} -#endif - -/* TODO: switching by dwarf address size */ -#ifndef __x86_64__ -static const struct pt_regs_offset x86_32_regoffset_table[] = { - REG_OFFSET_NAME_32("%ax", eax), - REG_OFFSET_NAME_32("%cx", ecx), - REG_OFFSET_NAME_32("%dx", edx), - REG_OFFSET_NAME_32("%bx", ebx), - REG_OFFSET_NAME_32("$stack", esp), /* Stack address instead of %sp */ - REG_OFFSET_NAME_32("%bp", ebp), - REG_OFFSET_NAME_32("%si", esi), - REG_OFFSET_NAME_32("%di", edi), - REG_OFFSET_END, -}; - -#define regoffset_table x86_32_regoffset_table -#else -static const struct pt_regs_offset x86_64_regoffset_table[] = { - REG_OFFSET_NAME_64("%ax", rax), - REG_OFFSET_NAME_64("%dx", rdx), - REG_OFFSET_NAME_64("%cx", rcx), - REG_OFFSET_NAME_64("%bx", rbx), - REG_OFFSET_NAME_64("%si", rsi), - REG_OFFSET_NAME_64("%di", rdi), - REG_OFFSET_NAME_64("%bp", rbp), - REG_OFFSET_NAME_64("%sp", rsp), - REG_OFFSET_NAME_64("%r8", r8), - REG_OFFSET_NAME_64("%r9", r9), - REG_OFFSET_NAME_64("%r10", r10), - REG_OFFSET_NAME_64("%r11", r11), - REG_OFFSET_NAME_64("%r12", r12), - REG_OFFSET_NAME_64("%r13", r13), - REG_OFFSET_NAME_64("%r14", r14), - REG_OFFSET_NAME_64("%r15", r15), - REG_OFFSET_END, -}; - -#define regoffset_table x86_64_regoffset_table -#endif - -/* Minus 1 for the ending REG_OFFSET_END */ -#define ARCH_MAX_REGS ((sizeof(regoffset_table) / sizeof(regoffset_table[0])) - 1) - -/* Return architecture dependent register string (for kprobe-tracer) */ -const char *get_arch_regstr(unsigned int n) -{ - return (n < ARCH_MAX_REGS) ? regoffset_table[n].name : NULL; -} - -/* Reuse code from arch/x86/kernel/ptrace.c */ -/** - * regs_query_register_offset() - query register offset from its name - * @name: the name of a register - * - * regs_query_register_offset() returns the offset of a register in struct - * pt_regs from its name. If the name is invalid, this returns -EINVAL; - */ -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_offset *roff; - for (roff = regoffset_table; roff->name != NULL; roff++) - if (!strcmp(roff->name, name)) - return roff->offset; - return -EINVAL; -} - -struct dwarf_regs_idx { - const char *name; - int idx; -}; - -static const struct dwarf_regs_idx x86_regidx_table[] = { - { "rax", 0 }, { "eax", 0 }, { "ax", 0 }, { "al", 0 }, - { "rdx", 1 }, { "edx", 1 }, { "dx", 1 }, { "dl", 1 }, - { "rcx", 2 }, { "ecx", 2 }, { "cx", 2 }, { "cl", 2 }, - { "rbx", 3 }, { "edx", 3 }, { "bx", 3 }, { "bl", 3 }, - { "rsi", 4 }, { "esi", 4 }, { "si", 4 }, { "sil", 4 }, - { "rdi", 5 }, { "edi", 5 }, { "di", 5 }, { "dil", 5 }, - { "rbp", 6 }, { "ebp", 6 }, { "bp", 6 }, { "bpl", 6 }, - { "rsp", 7 }, { "esp", 7 }, { "sp", 7 }, { "spl", 7 }, - { "r8", 8 }, { "r8d", 8 }, { "r8w", 8 }, { "r8b", 8 }, - { "r9", 9 }, { "r9d", 9 }, { "r9w", 9 }, { "r9b", 9 }, - { "r10", 10 }, { "r10d", 10 }, { "r10w", 10 }, { "r10b", 10 }, - { "r11", 11 }, { "r11d", 11 }, { "r11w", 11 }, { "r11b", 11 }, - { "r12", 12 }, { "r12d", 12 }, { "r12w", 12 }, { "r12b", 12 }, - { "r13", 13 }, { "r13d", 13 }, { "r13w", 13 }, { "r13b", 13 }, - { "r14", 14 }, { "r14d", 14 }, { "r14w", 14 }, { "r14b", 14 }, - { "r15", 15 }, { "r15d", 15 }, { "r15w", 15 }, { "r15b", 15 }, - { "rip", DWARF_REG_PC }, -}; - -int get_arch_regnum(const char *name) -{ - unsigned int i; - - if (*name != '%') - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(x86_regidx_table); i++) - if (!strcmp(x86_regidx_table[i].name, name + 1)) - return x86_regidx_table[i].idx; - return -ENOENT; -} diff --git a/tools/perf/arch/x86/util/env.c b/tools/perf/arch/x86/util/env.c deleted file mode 100644 index 3e537ffb1353a..0000000000000 --- a/tools/perf/arch/x86/util/env.c +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "linux/string.h" -#include "util/env.h" -#include "env.h" - -bool x86__is_amd_cpu(void) -{ - struct perf_env env = { .total_mem = 0, }; - static int is_amd; /* 0: Uninitialized, 1: Yes, -1: No */ - - if (is_amd) - goto ret; - - perf_env__cpuid(&env); - is_amd = env.cpuid && strstarts(env.cpuid, "AuthenticAMD") ? 1 : -1; - perf_env__exit(&env); -ret: - return is_amd >= 1 ? true : false; -} diff --git a/tools/perf/arch/x86/util/env.h b/tools/perf/arch/x86/util/env.h deleted file mode 100644 index d78f080b6b3f8..0000000000000 --- a/tools/perf/arch/x86/util/env.h +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _X86_ENV_H -#define _X86_ENV_H - -bool x86__is_amd_cpu(void); - -#endif /* _X86_ENV_H */ diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index cebdd483149e8..447a734e591c8 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -1,91 +1,86 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include "util/pmu.h" -#include "util/pmus.h" -#include "util/evlist.h" -#include "util/parse-events.h" -#include "util/event.h" +#include +#include "../../../util/evlist.h" +#include "../../../util/evsel.h" #include "topdown.h" #include "evsel.h" -static int ___evlist__add_default_attrs(struct evlist *evlist, - struct perf_event_attr *attrs, - size_t nr_attrs) -{ - LIST_HEAD(head); - size_t i = 0; - - for (i = 0; i < nr_attrs; i++) - event_attr_init(attrs + i); - - if (perf_pmus__num_core_pmus() == 1) - return evlist__add_attrs(evlist, attrs, nr_attrs); - - for (i = 0; i < nr_attrs; i++) { - struct perf_pmu *pmu = NULL; - - if (attrs[i].type == PERF_TYPE_SOFTWARE) { - struct evsel *evsel = evsel__new(attrs + i); - - if (evsel == NULL) - goto out_delete_partial_list; - list_add_tail(&evsel->core.node, &head); - continue; - } - - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { - struct perf_cpu_map *cpus; - struct evsel *evsel; - - evsel = evsel__new(attrs + i); - if (evsel == NULL) - goto out_delete_partial_list; - evsel->core.attr.config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT; - cpus = perf_cpu_map__get(pmu->cpus); - evsel->core.cpus = cpus; - evsel->core.own_cpus = perf_cpu_map__get(cpus); - evsel->pmu_name = strdup(pmu->name); - list_add_tail(&evsel->core.node, &head); - } - } - - evlist__splice_list_tail(evlist, &head); - - return 0; - -out_delete_partial_list: - { - struct evsel *evsel, *n; - - __evlist__for_each_entry_safe(&head, n, evsel) - evsel__delete(evsel); - } - return -1; -} - -int arch_evlist__add_default_attrs(struct evlist *evlist, - struct perf_event_attr *attrs, - size_t nr_attrs) -{ - if (!nr_attrs) - return 0; - - return ___evlist__add_default_attrs(evlist, attrs, nr_attrs); -} - int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs) { + /* + * Currently the following topdown events sequence are supported to + * move and regroup correctly. + * + * a. all events in a group + * perf stat -e "{instructions,topdown-retiring,slots}" -C0 sleep 1 + * WARNING: events were regrouped to match PMUs + * Performance counter stats for 'CPU(s) 0': + * 15,066,240 slots + * 1,899,760 instructions + * 2,126,998 topdown-retiring + * b. all events not in a group + * perf stat -e "instructions,topdown-retiring,slots" -C0 sleep 1 + * WARNING: events were regrouped to match PMUs + * Performance counter stats for 'CPU(s) 0': + * 2,045,561 instructions + * 17,108,370 slots + * 2,281,116 topdown-retiring + * c. slots event in a group but topdown metrics events outside the group + * perf stat -e "{instructions,slots},topdown-retiring" -C0 sleep 1 + * WARNING: events were regrouped to match PMUs + * Performance counter stats for 'CPU(s) 0': + * 20,323,878 slots + * 2,634,884 instructions + * 3,028,656 topdown-retiring + * d. slots event and topdown metrics events in two groups + * perf stat -e "{instructions,slots},{topdown-retiring}" -C0 sleep 1 + * WARNING: events were regrouped to match PMUs + * Performance counter stats for 'CPU(s) 0': + * 26,319,024 slots + * 2,427,791 instructions + * 2,683,508 topdown-retiring + * + * If slots event and topdown metrics events are not in same group, the + * topdown metrics events must be first event after the slots event group, + * otherwise topdown metrics events can't be regrouped correctly, e.g. + * + * a. perf stat -e "{instructions,slots},cycles,topdown-retiring" -C0 sleep 1 + * WARNING: events were regrouped to match PMUs + * Performance counter stats for 'CPU(s) 0': + * 17,923,134 slots + * 2,154,855 instructions + * 3,015,058 cycles + * topdown-retiring + * + * If slots event and topdown metrics events are in two groups, the group which + * has topdown metrics events must contain only the topdown metrics event, + * otherwise topdown metrics event can't be regrouped correctly as well, e.g. + * + * a. perf stat -e "{instructions,slots},{topdown-retiring,cycles}" -C0 sleep 1 + * WARNING: events were regrouped to match PMUs + * Error: + * The sys_perf_event_open() syscall returned with 22 (Invalid argument) for + * event (topdown-retiring) + */ if (topdown_sys_has_perf_metrics() && (arch_evsel__must_be_in_group(lhs) || arch_evsel__must_be_in_group(rhs))) { /* Ensure the topdown slots comes first. */ - if (strcasestr(lhs->name, "slots") && !strcasestr(lhs->name, "uops_retired.slots")) + if (arch_is_topdown_slots(lhs)) return -1; - if (strcasestr(rhs->name, "slots") && !strcasestr(rhs->name, "uops_retired.slots")) + if (arch_is_topdown_slots(rhs)) return 1; - /* Followed by topdown events. */ - if (strcasestr(lhs->name, "topdown") && !strcasestr(rhs->name, "topdown")) + + /* + * Move topdown metrics events forward only when topdown metrics + * events are not in same group with previous slots event. If + * topdown metrics events are already in same group with slots + * event, do nothing. + */ + if (arch_is_topdown_metrics(lhs) && !arch_is_topdown_metrics(rhs) && + lhs->core.leader != rhs->core.leader) return -1; - if (!strcasestr(lhs->name, "topdown") && strcasestr(rhs->name, "topdown")) + if (!arch_is_topdown_metrics(lhs) && arch_is_topdown_metrics(rhs) && + lhs->core.leader != rhs->core.leader) return 1; } diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 090d0f371891f..3dd29ba2c23b0 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -6,6 +6,7 @@ #include "util/pmu.h" #include "util/pmus.h" #include "linux/string.h" +#include "topdown.h" #include "evsel.h" #include "util/debug.h" #include "env.h" @@ -21,7 +22,8 @@ void arch_evsel__set_sample_weight(struct evsel *evsel) /* Check whether the evsel's PMU supports the perf metrics */ bool evsel__sys_has_perf_metrics(const struct evsel *evsel) { - const char *pmu_name = evsel->pmu_name ? evsel->pmu_name : "cpu"; + struct perf_pmu *pmu; + u32 type = evsel->core.attr.type; /* * The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU @@ -31,11 +33,31 @@ bool evsel__sys_has_perf_metrics(const struct evsel *evsel) * Checking both the PERF_TYPE_RAW type and the slots event * should be good enough to detect the perf metrics feature. */ - if ((evsel->core.attr.type == PERF_TYPE_RAW) && - perf_pmus__have_event(pmu_name, "slots")) - return true; +again: + switch (type) { + case PERF_TYPE_HARDWARE: + case PERF_TYPE_HW_CACHE: + type = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT; + if (type) + goto again; + break; + case PERF_TYPE_RAW: + break; + default: + return false; + } + + pmu = evsel->pmu; + if (pmu && perf_pmu__is_fake(pmu)) + pmu = NULL; - return false; + if (!pmu) { + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + if (pmu->type == PERF_TYPE_RAW) + break; + } + } + return pmu && perf_pmu__have_event(pmu, "slots"); } bool arch_evsel__must_be_in_group(const struct evsel *evsel) @@ -44,7 +66,7 @@ bool arch_evsel__must_be_in_group(const struct evsel *evsel) strcasestr(evsel->name, "uops_retired.slots")) return false; - return strcasestr(evsel->name, "topdown") || strcasestr(evsel->name, "slots"); + return arch_is_topdown_metrics(evsel) || arch_is_topdown_slots(evsel); } int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size) @@ -63,7 +85,7 @@ int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size) return scnprintf(bf, size, "%s", event_name); return scnprintf(bf, size, "%s/%s/", - evsel->pmu_name ? evsel->pmu_name : "cpu", + evsel->pmu ? evsel->pmu->name : "cpu", event_name); } @@ -108,7 +130,7 @@ int arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size) return 0; if (!evsel->core.attr.precise_ip && - !(evsel->pmu_name && !strncmp(evsel->pmu_name, "ibs", 3))) + !(evsel->pmu && !strncmp(evsel->pmu->name, "ibs", 3))) return 0; /* More verbose IBS errors. */ diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index a51444a77a5f2..412977f8aa837 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c @@ -58,13 +58,12 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt) } int -get_cpuid(char *buffer, size_t sz) +get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused) { return __get_cpuid(buffer, sz, "%s,%u,%u,%u$"); } -char * -get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +char *get_cpuid_str(struct perf_cpu cpu __maybe_unused) { char *buf = malloc(128); diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index ea510a7486b14..8f235d8b67b62 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -75,7 +75,8 @@ static int intel_pt_parse_terms_with_default(const struct perf_pmu *pmu, goto out_free; attr.config = *config; - err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*err=*/NULL); + err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*apply_hardcoded=*/false, + /*err=*/NULL); if (err) goto out_free; diff --git a/tools/perf/arch/x86/util/iostat.c b/tools/perf/arch/x86/util/iostat.c index df7b5dfcc26a5..366b44d0bb7e5 100644 --- a/tools/perf/arch/x86/util/iostat.c +++ b/tools/perf/arch/x86/util/iostat.c @@ -444,7 +444,7 @@ void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel, iostat_value = (count->val - prev_count_val) / ((double) count->run / count->ena); } - out->print_metric(config, out->ctx, NULL, "%8.0f", iostat_metric, + out->print_metric(config, out->ctx, METRIC_THRESHOLD_UNKNOWN, "%8.0f", iostat_metric, iostat_value / (256 * 1024)); } diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c index c3d89d6ba1bf0..e0060dac2a9f9 100644 --- a/tools/perf/arch/x86/util/pmu.c +++ b/tools/perf/arch/x86/util/pmu.c @@ -16,7 +16,7 @@ #include "../../../util/fncache.h" #include "../../../util/pmus.h" #include "mem-events.h" -#include "env.h" +#include "util/env.h" void perf_pmu__arch_init(struct perf_pmu *pmu __maybe_unused) { diff --git a/tools/perf/arch/x86/util/topdown.c b/tools/perf/arch/x86/util/topdown.c index 3f9a267d4501b..f63747d0abdf9 100644 --- a/tools/perf/arch/x86/util/topdown.c +++ b/tools/perf/arch/x86/util/topdown.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "api/fs/fs.h" #include "util/evsel.h" +#include "util/evlist.h" #include "util/pmu.h" #include "util/pmus.h" #include "util/topdown.h" @@ -32,6 +33,31 @@ bool topdown_sys_has_perf_metrics(void) } #define TOPDOWN_SLOTS 0x0400 +bool arch_is_topdown_slots(const struct evsel *evsel) +{ + if (evsel->core.attr.config == TOPDOWN_SLOTS) + return true; + + return false; +} + +bool arch_is_topdown_metrics(const struct evsel *evsel) +{ + int config = evsel->core.attr.config; + const char *name_from_config; + struct perf_pmu *pmu; + + /* All topdown events have an event code of 0. */ + if ((config & 0xFF) != 0) + return false; + + pmu = evsel__find_pmu(evsel); + if (!pmu || !pmu->is_core) + return false; + + name_from_config = perf_pmu__name_from_config(pmu, config); + return name_from_config && strcasestr(name_from_config, "topdown"); +} /* * Check whether a topdown group supports sample-read. @@ -41,11 +67,24 @@ bool topdown_sys_has_perf_metrics(void) */ bool arch_topdown_sample_read(struct evsel *leader) { + struct evsel *evsel; + if (!evsel__sys_has_perf_metrics(leader)) return false; - if (leader->core.attr.config == TOPDOWN_SLOTS) - return true; + if (!arch_is_topdown_slots(leader)) + return false; + + /* + * If slots event as leader event but no topdown metric events + * in group, slots event should still sample as leader. + */ + evlist__for_each_entry(leader->evlist, evsel) { + if (evsel->core.leader != leader->core.leader) + return false; + if (evsel != leader && arch_is_topdown_metrics(evsel)) + return true; + } return false; } diff --git a/tools/perf/arch/x86/util/topdown.h b/tools/perf/arch/x86/util/topdown.h index 46bf9273e572f..1bae9b1822d71 100644 --- a/tools/perf/arch/x86/util/topdown.h +++ b/tools/perf/arch/x86/util/topdown.h @@ -3,5 +3,7 @@ #define _TOPDOWN_H 1 bool topdown_sys_has_perf_metrics(void); +bool arch_is_topdown_slots(const struct evsel *evsel); +bool arch_is_topdown_metrics(const struct evsel *evsel); #endif diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index e2d6cfe21057b..3a439e4b12d28 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c @@ -24,9 +24,9 @@ u64 rdtsc(void) * ... * will return 3000000000. */ -static double cpuinfo_tsc_freq(void) +static u64 cpuinfo_tsc_freq(void) { - double result = 0; + u64 result = 0; FILE *cpuinfo; char *line = NULL; size_t len = 0; @@ -34,20 +34,22 @@ static double cpuinfo_tsc_freq(void) cpuinfo = fopen("/proc/cpuinfo", "r"); if (!cpuinfo) { pr_err("Failed to read /proc/cpuinfo for TSC frequency\n"); - return NAN; + return 0; } while (getline(&line, &len, cpuinfo) > 0) { if (!strncmp(line, "model name", 10)) { char *pos = strstr(line + 11, " @ "); + double float_result; - if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) { - result *= 1000000000; + if (pos && sscanf(pos, " @ %lfGHz", &float_result) == 1) { + float_result *= 1000000000; + result = (u64)float_result; goto out; } } } out: - if (fpclassify(result) == FP_ZERO) + if (result == 0) pr_err("Failed to find TSC frequency in /proc/cpuinfo\n"); free(line); @@ -55,7 +57,7 @@ static double cpuinfo_tsc_freq(void) return result; } -double arch_get_tsc_freq(void) +u64 arch_get_tsc_freq(void) { unsigned int a, b, c, d, lvl; static bool cached; @@ -86,6 +88,6 @@ double arch_get_tsc_freq(void) return tsc; } - tsc = (double)c * (double)b / (double)a; + tsc = (u64)c * (u64)b / (u64)a; return tsc; } diff --git a/tools/perf/arch/xtensa/Build b/tools/perf/arch/xtensa/Build deleted file mode 100644 index e63eabc2c8f41..0000000000000 --- a/tools/perf/arch/xtensa/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-y += util/ diff --git a/tools/perf/arch/xtensa/Makefile b/tools/perf/arch/xtensa/Makefile deleted file mode 100644 index 88c08eed9c7be..0000000000000 --- a/tools/perf/arch/xtensa/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif diff --git a/tools/perf/arch/xtensa/util/Build b/tools/perf/arch/xtensa/util/Build deleted file mode 100644 index e813e618954b0..0000000000000 --- a/tools/perf/arch/xtensa/util/Build +++ /dev/null @@ -1 +0,0 @@ -perf-$(CONFIG_DWARF) += dwarf-regs.o diff --git a/tools/perf/arch/xtensa/util/dwarf-regs.c b/tools/perf/arch/xtensa/util/dwarf-regs.c deleted file mode 100644 index 12f5457300f5c..0000000000000 --- a/tools/perf/arch/xtensa/util/dwarf-regs.c +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (c) 2015 Cadence Design Systems Inc. - */ - -#include -#include - -#define XTENSA_MAX_REGS 16 - -const char *xtensa_regs_table[XTENSA_MAX_REGS] = { - "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", - "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15", -}; - -const char *get_arch_regstr(unsigned int n) -{ - return n < XTENSA_MAX_REGS ? xtensa_regs_table[n] : NULL; -} diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 1fbd7c947abc2..19be2aaf4dc0c 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include "../util/header.h" #include "../util/mutex.h" +#include #include #include @@ -533,6 +535,57 @@ static int parse_cpu_list(const char *arg) return 0; } +/* + * Check whether a CPU is online + * + * Returns: + * 1 -> if CPU is online + * 0 -> if CPU is offline + * -1 -> error case + */ +static int is_cpu_online(unsigned int cpu) +{ + char *str; + size_t strlen; + char buf[256]; + int status = -1; + struct stat statbuf; + + snprintf(buf, sizeof(buf), + "/sys/devices/system/cpu/cpu%d", cpu); + if (stat(buf, &statbuf) != 0) + return 0; + + /* + * Check if /sys/devices/system/cpu/cpux/online file + * exists. Some cases cpu0 won't have online file since + * it is not expected to be turned off generally. + * In kernels without CONFIG_HOTPLUG_CPU, this + * file won't exist + */ + snprintf(buf, sizeof(buf), + "/sys/devices/system/cpu/cpu%d/online", cpu); + if (stat(buf, &statbuf) != 0) + return 1; + + /* + * Read online file using sysfs__read_str. + * If read or open fails, return -1. + * If read succeeds, return value from file + * which gets stored in "str" + */ + snprintf(buf, sizeof(buf), + "devices/system/cpu/cpu%d/online", cpu); + + if (sysfs__read_str(buf, &str, &strlen) < 0) + return status; + + status = atoi(str); + + free(str); + return status; +} + static int parse_setup_cpu_list(void) { struct thread_data *td; diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 3af6d3c55aba1..e2562677df964 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,8 @@ struct thread_data { int nr; int pipe_read; int pipe_write; + struct epoll_event epoll_ev; + int epoll_fd; bool cgroup_failed; pthread_t pthread; }; @@ -44,6 +47,7 @@ static int loops = LOOPS_DEFAULT; /* Use processes by default: */ static bool threaded; +static bool nonblocking; static char *cgrp_names[2]; static struct cgroup *cgrps[2]; @@ -81,6 +85,7 @@ static int parse_two_cgroups(const struct option *opt __maybe_unused, } static const struct option options[] = { + OPT_BOOLEAN('n', "nonblocking", &nonblocking, "Use non-blocking operations"), OPT_INTEGER('l', "loop", &loops, "Specify number of loops"), OPT_BOOLEAN('T', "threaded", &threaded, "Specify threads/process based task setup"), OPT_CALLBACK('G', "cgroups", NULL, "SEND,RECV", @@ -165,11 +170,25 @@ static void exit_cgroup(int nr) free(cgrp_names[nr]); } +static inline int read_pipe(struct thread_data *td) +{ + int ret, m; +retry: + if (nonblocking) { + ret = epoll_wait(td->epoll_fd, &td->epoll_ev, 1, -1); + if (ret < 0) + return ret; + } + ret = read(td->pipe_read, &m, sizeof(int)); + if (nonblocking && ret < 0 && errno == EWOULDBLOCK) + goto retry; + return ret; +} + static void *worker_thread(void *__tdata) { struct thread_data *td = __tdata; - int m = 0, i; - int ret; + int i, ret, m = 0; ret = enter_cgroup(td->nr); if (ret < 0) { @@ -177,16 +196,23 @@ static void *worker_thread(void *__tdata) return NULL; } + if (nonblocking) { + td->epoll_ev.events = EPOLLIN; + td->epoll_fd = epoll_create(1); + BUG_ON(td->epoll_fd < 0); + BUG_ON(epoll_ctl(td->epoll_fd, EPOLL_CTL_ADD, td->pipe_read, &td->epoll_ev) < 0); + } + for (i = 0; i < loops; i++) { if (!td->nr) { - ret = read(td->pipe_read, &m, sizeof(int)); + ret = read_pipe(td); BUG_ON(ret != sizeof(int)); ret = write(td->pipe_write, &m, sizeof(int)); BUG_ON(ret != sizeof(int)); } else { ret = write(td->pipe_write, &m, sizeof(int)); BUG_ON(ret != sizeof(int)); - ret = read(td->pipe_read, &m, sizeof(int)); + ret = read_pipe(td); BUG_ON(ret != sizeof(int)); } } @@ -209,13 +235,16 @@ int bench_sched_pipe(int argc, const char **argv) * discarding returned value of read(), write() * causes error in building environment for perf */ - int __maybe_unused ret, wait_stat; + int __maybe_unused ret, wait_stat, flags = 0; pid_t pid, retpid __maybe_unused; argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0); - BUG_ON(pipe(pipe_1)); - BUG_ON(pipe(pipe_2)); + if (nonblocking) + flags |= O_NONBLOCK; + + BUG_ON(pipe2(pipe_1, flags)); + BUG_ON(pipe2(pipe_2, flags)); gettimeofday(&start, NULL); diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3dc6197ef3fa6..bb87e6e7687dc 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -840,7 +840,7 @@ int cmd_annotate(int argc, const char **argv) } #endif -#ifndef HAVE_DWARF_GETLOCATIONS_SUPPORT +#ifndef HAVE_LIBDW_SUPPORT if (annotate.data_type) { pr_err("Error: Data type profiling is disabled due to missing DWARF support\n"); return -ENOTSUP; diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c index 0b76b6e42b781..2346536a5ee14 100644 --- a/tools/perf/builtin-check.c +++ b/tools/perf/builtin-check.c @@ -27,15 +27,15 @@ struct feature_status supported_features[] = { FEATURE_STATUS("bpf", HAVE_LIBBPF_SUPPORT), FEATURE_STATUS("bpf_skeletons", HAVE_BPF_SKEL), FEATURE_STATUS("debuginfod", HAVE_DEBUGINFOD_SUPPORT), - FEATURE_STATUS("dwarf", HAVE_DWARF_SUPPORT), - FEATURE_STATUS("dwarf_getlocations", HAVE_DWARF_GETLOCATIONS_SUPPORT), + FEATURE_STATUS("dwarf", HAVE_LIBDW_SUPPORT), + FEATURE_STATUS("dwarf_getlocations", HAVE_LIBDW_SUPPORT), FEATURE_STATUS("dwarf-unwind", HAVE_DWARF_UNWIND_SUPPORT), FEATURE_STATUS("auxtrace", HAVE_AUXTRACE_SUPPORT), FEATURE_STATUS("libaudit", HAVE_LIBAUDIT_SUPPORT), FEATURE_STATUS("libbfd", HAVE_LIBBFD_SUPPORT), FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT), FEATURE_STATUS("libcrypto", HAVE_LIBCRYPTO_SUPPORT), - FEATURE_STATUS("libdw-dwarf-unwind", HAVE_DWARF_SUPPORT), + FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT), FEATURE_STATUS("libelf", HAVE_LIBELF_SUPPORT), FEATURE_STATUS("libnuma", HAVE_LIBNUMA_SUPPORT), FEATURE_STATUS("libopencsd", HAVE_CSTRACE_SUPPORT), diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 23326dd203339..82fb7773e03e6 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -469,13 +469,13 @@ static int diff__process_sample_event(const struct perf_tool *tool, static struct perf_diff pdiff; -static struct evsel *evsel_match(struct evsel *evsel, - struct evlist *evlist) +static struct evsel *evsel_match(struct evsel *evsel, struct evlist *evlist) { struct evsel *e; evlist__for_each_entry(evlist, e) { - if (evsel__match2(evsel, e)) + if ((evsel->core.attr.type == e->core.attr.type) && + (evsel->core.attr.config == e->core.attr.config)) return e; } diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index abcdc49b7a987..272d3c70810e7 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -815,7 +815,7 @@ static void display_histogram(int buckets[], bool use_nsec) bar_len = buckets[0] * bar_total / total; printf(" %4d - %-4d %s | %10d | %.*s%*s |\n", - 0, 1, "us", buckets[0], bar_len, bar, bar_total - bar_len, ""); + 0, 1, use_nsec ? "ns" : "us", buckets[0], bar_len, bar, bar_total - bar_len, ""); for (i = 1; i < NUM_BUCKET - 1; i++) { int start = (1 << (i - 1)); diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index a756147e2eec7..4d8d94146f8dd 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -36,7 +36,7 @@ #include #include -#include +#include static int kmem_slab; static int kmem_page; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 55ea17c5ff02a..274568d712d12 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1226,7 +1226,9 @@ static int cpu_isa_config(struct perf_kvm_stat *kvm) int err; if (kvm->live) { - err = get_cpuid(buf, sizeof(buf)); + struct perf_cpu cpu = {-1}; + + err = get_cpuid(buf, sizeof(buf), cpu); if (err != 0) { pr_err("Failed to look up CPU type: %s\n", str_error_r(err, buf, sizeof(buf))); @@ -2147,6 +2149,7 @@ int cmd_kvm(int argc, const char **argv) "buildid-list", "stat", NULL }; const char *kvm_usage[] = { NULL, NULL }; + exclude_GH_default = true; perf_host = 0; perf_guest = 1; diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index c1daf82c9b923..8234410cba4c8 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 65b8cba324be4..9e7fdfcdd7ffb 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -19,6 +19,7 @@ #include "util/string2.h" #include "util/strlist.h" #include "util/strbuf.h" +#include "util/tool_pmu.h" #include #include #include @@ -112,7 +113,7 @@ static void wordwrap(FILE *fp, const char *s, int start, int max, int corr) } } -static void default_print_event(void *ps, const char *pmu_name, const char *topic, +static void default_print_event(void *ps, const char *topic, const char *pmu_name, const char *event_name, const char *event_alias, const char *scale_unit __maybe_unused, bool deprecated, const char *event_type_desc, @@ -353,7 +354,7 @@ static void fix_escape_fprintf(FILE *fp, struct strbuf *buf, const char *fmt, .. fputs(buf->buf, fp); } -static void json_print_event(void *ps, const char *pmu_name, const char *topic, +static void json_print_event(void *ps, const char *topic, const char *pmu_name, const char *event_name, const char *event_alias, const char *scale_unit, bool deprecated, const char *event_type_desc, @@ -614,9 +615,18 @@ int cmd_list(int argc, const char **argv) event_symbols_hw, PERF_COUNT_HW_MAX); else if (strcmp(argv[i], "sw") == 0 || strcmp(argv[i], "software") == 0) { + char *old_pmu_glob = default_ps.pmu_glob; + print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE, event_symbols_sw, PERF_COUNT_SW_MAX); - print_tool_events(&print_cb, ps); + default_ps.pmu_glob = strdup("tool"); + if (!default_ps.pmu_glob) { + ret = -1; + goto out; + } + perf_pmus__print_pmu_events(&print_cb, ps); + zfree(&default_ps.pmu_glob); + default_ps.pmu_glob = old_pmu_glob; } else if (strcmp(argv[i], "cache") == 0 || strcmp(argv[i], "hwcache") == 0) print_hwcache_events(&print_cb, ps); @@ -664,7 +674,6 @@ int cmd_list(int argc, const char **argv) event_symbols_hw, PERF_COUNT_HW_MAX); print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE, event_symbols_sw, PERF_COUNT_SW_MAX); - print_tool_events(&print_cb, ps); print_hwcache_events(&print_cb, ps); perf_pmus__print_pmu_events(&print_cb, ps); print_tracepoint_events(&print_cb, ps); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 003a3bcebfdfc..69800e4d9530b 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -229,7 +229,7 @@ static int opt_set_target_ns(const struct option *opt __maybe_unused, /* Command option callbacks */ -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT static int opt_show_lines(const struct option *opt, const char *str, int unset __maybe_unused) { @@ -505,7 +505,7 @@ static int perf_del_probe_events(struct strfilter *filter) return ret; } -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT #define PROBEDEF_STR \ "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT [[NAME=]ARG ...]" #else @@ -521,7 +521,7 @@ __cmd_probe(int argc, const char **argv) "perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", "perf probe [] --del '[GROUP:]EVENT' ...", "perf probe --list [GROUP:]EVENT ...", -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT "perf probe [] --line 'LINEDESC'", "perf probe [] --vars 'PROBEPOINT'", #endif @@ -545,7 +545,7 @@ __cmd_probe(int argc, const char **argv) "\t\tFUNC:\tFunction name\n" "\t\tOFF:\tOffset from function entry (in byte)\n" "\t\t%return:\tPut the probe at function return\n" -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT "\t\tSRC:\tSource code path\n" "\t\tRL:\tRelative line number from function entry.\n" "\t\tAL:\tAbsolute line number in file.\n" @@ -612,11 +612,11 @@ __cmd_probe(int argc, const char **argv) set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE); set_option_flag(options, 'D', "definition", PARSE_OPT_EXCLUSIVE); set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE); -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE); #else -# define set_nobuild(s, l, c) set_option_nobuild(options, s, l, "NO_DWARF=1", c) +# define set_nobuild(s, l, c) set_option_nobuild(options, s, l, "NO_LIBDW=1", c) set_nobuild('L', "line", false); set_nobuild('V', "vars", false); set_nobuild('\0', "externs", false); @@ -694,7 +694,7 @@ __cmd_probe(int argc, const char **argv) if (ret < 0) pr_err_with_code(" Error: Failed to show functions.", ret); return ret; -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT case 'L': ret = show_line_range(¶ms->line_range, params->target, params->nsi, params->uprobes); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index adbaf80b398c1..f832524729211 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -4157,9 +4157,7 @@ int cmd_record(int argc, const char **argv) record.opts.tail_synthesize = true; if (rec->evlist->core.nr_entries == 0) { - bool can_profile_kernel = perf_event_paranoid_check(1); - - err = parse_event(rec->evlist, can_profile_kernel ? "cycles:P" : "cycles:Pu"); + err = parse_event(rec->evlist, "cycles:P"); if (err) goto out; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5dc17ffee27a2..048c91960ba91 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -70,7 +70,7 @@ #include #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif struct report { @@ -455,7 +455,7 @@ static int report__setup_sample_type(struct report *rep) if (!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY)) rep->nonany_branch_mode = true; -#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT) +#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_LIBDW_SUPPORT) if (dwarf_callchain_users) { ui__warning("Please install libunwind or libdw " "development packages during the perf build.\n"); @@ -1271,6 +1271,10 @@ static int process_attr(const struct perf_tool *tool __maybe_unused, return 0; } +#define CALLCHAIN_BRANCH_SORT_ORDER \ + "srcline,symbol,dso,callchain_branch_predicted," \ + "callchain_branch_abort,callchain_branch_cycles" + int cmd_report(int argc, const char **argv) { struct perf_session *session; @@ -1639,7 +1643,7 @@ int cmd_report(int argc, const char **argv) symbol_conf.use_callchain = true; callchain_register_param(&callchain_param); if (sort_order == NULL) - sort_order = "srcline,symbol,dso"; + sort_order = CALLCHAIN_BRANCH_SORT_ORDER; } if (report.mem_mode) { @@ -1701,7 +1705,7 @@ int cmd_report(int argc, const char **argv) report.data_type = true; annotate_opts.annotate_src = false; -#ifndef HAVE_DWARF_GETLOCATIONS_SUPPORT +#ifndef HAVE_LIBDW_SUPPORT pr_err("Error: Data type profiling is disabled due to missing DWARF support\n"); goto error; #endif diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 5981cc51abc88..7049c60ebf77c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -68,7 +68,6 @@ struct task_desc { struct sched_atom **atoms; pthread_t thread; - sem_t sleep_sem; sem_t ready_for_work; sem_t work_done_sem; @@ -80,12 +79,10 @@ enum sched_event_type { SCHED_EVENT_RUN, SCHED_EVENT_SLEEP, SCHED_EVENT_WAKEUP, - SCHED_EVENT_MIGRATION, }; struct sched_atom { enum sched_event_type type; - int specific_wait; u64 timestamp; u64 duration; unsigned long nr; @@ -228,6 +225,7 @@ struct perf_sched { bool show_wakeups; bool show_next; bool show_migrations; + bool pre_migrations; bool show_state; bool show_prio; u64 skipped_samples; @@ -247,7 +245,9 @@ struct thread_runtime { u64 dt_iowait; /* time between CPU access by iowait (off cpu) */ u64 dt_preempt; /* time between CPU access by preempt (off cpu) */ u64 dt_delay; /* time between wakeup and sched-in */ + u64 dt_pre_mig; /* time between migration and wakeup */ u64 ready_to_run; /* time of wakeup */ + u64 migrated; /* time when a thread is migrated */ struct stats run_stats; u64 total_run_time; @@ -255,6 +255,7 @@ struct thread_runtime { u64 total_iowait_time; u64 total_preempt_time; u64 total_delay_time; + u64 total_pre_mig_time; char last_state; @@ -421,14 +422,13 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); sem_init(wakee_event->wait_sem, 0, 0); - wakee_event->specific_wait = 1; event->wait_sem = wakee_event->wait_sem; sched->nr_wakeup_events++; } static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *task, - u64 timestamp, const char task_state __maybe_unused) + u64 timestamp) { struct sched_atom *event = get_new_event(task, timestamp); @@ -468,7 +468,7 @@ static struct task_desc *register_pid(struct perf_sched *sched, * every task starts in sleeping state - this gets ignored * if there's no wakeup pointing to this sleep state: */ - add_sched_event_sleep(sched, task, 0, 0); + add_sched_event_sleep(sched, task, 0); sched->pid_to_task[pid] = task; sched->nr_tasks++; @@ -529,8 +529,6 @@ static void perf_sched__process_event(struct perf_sched *sched, ret = sem_post(atom->wait_sem); BUG_ON(ret); break; - case SCHED_EVENT_MIGRATION: - break; default: BUG_ON(1); } @@ -673,7 +671,6 @@ static void create_tasks(struct perf_sched *sched) parms->task = task = sched->tasks[i]; parms->sched = sched; parms->fd = self_open_counters(sched, i); - sem_init(&task->sleep_sem, 0, 0); sem_init(&task->ready_for_work, 0, 0); sem_init(&task->work_done_sem, 0, 0); task->curr_event = 0; @@ -697,7 +694,6 @@ static void destroy_tasks(struct perf_sched *sched) task = sched->tasks[i]; err = pthread_join(task->thread, NULL); BUG_ON(err); - sem_destroy(&task->sleep_sem); sem_destroy(&task->ready_for_work); sem_destroy(&task->work_done_sem); } @@ -751,7 +747,6 @@ static void wait_for_tasks(struct perf_sched *sched) for (i = 0; i < sched->nr_tasks; i++) { task = sched->tasks[i]; - sem_init(&task->sleep_sem, 0, 0); task->curr_event = 0; } } @@ -852,7 +847,6 @@ static int replay_switch_event(struct perf_sched *sched, *next_comm = evsel__strval(evsel, sample, "next_comm"); const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"), next_pid = evsel__intval(evsel, sample, "next_pid"); - const char prev_state = evsel__taskstate(evsel, sample, "prev_state"); struct task_desc *prev, __maybe_unused *next; u64 timestamp0, timestamp = sample->time; int cpu = sample->cpu; @@ -884,7 +878,7 @@ static int replay_switch_event(struct perf_sched *sched, sched->cpu_last_switched[cpu] = timestamp; add_sched_event_run(sched, prev, timestamp, delta); - add_sched_event_sleep(sched, prev, timestamp, prev_state); + add_sched_event_sleep(sched, prev, timestamp); return 0; } @@ -1749,7 +1743,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, } if (sched->map.comp && new_cpu) - color_fprintf(stdout, color, " (CPU %d)", this_cpu); + color_fprintf(stdout, color, " (CPU %d)", this_cpu.cpu); if (proceed != 1) { color_fprintf(stdout, color, "\n"); @@ -2083,14 +2077,15 @@ static void timehist_header(struct perf_sched *sched) printf(" "); } - if (sched->show_prio) { - printf(" %-*s %-*s %9s %9s %9s", - comm_width, "task name", MAX_PRIO_STR_LEN, "prio", - "wait time", "sch delay", "run time"); - } else { - printf(" %-*s %9s %9s %9s", comm_width, - "task name", "wait time", "sch delay", "run time"); - } + printf(" %-*s", comm_width, "task name"); + + if (sched->show_prio) + printf(" %-*s", MAX_PRIO_STR_LEN, "prio"); + + printf(" %9s %9s %9s", "wait time", "sch delay", "run time"); + + if (sched->pre_migrations) + printf(" %9s", "pre-mig time"); if (sched->show_state) printf(" %s", "state"); @@ -2105,17 +2100,15 @@ static void timehist_header(struct perf_sched *sched) if (sched->show_cpu_visual) printf(" %*s ", ncpus, ""); - if (sched->show_prio) { - printf(" %-*s %-*s %9s %9s %9s", - comm_width, "[tid/pid]", MAX_PRIO_STR_LEN, "", - "(msec)", "(msec)", "(msec)"); - } else { - printf(" %-*s %9s %9s %9s", comm_width, - "[tid/pid]", "(msec)", "(msec)", "(msec)"); - } + printf(" %-*s", comm_width, "[tid/pid]"); - if (sched->show_state) - printf(" %5s", ""); + if (sched->show_prio) + printf(" %-*s", MAX_PRIO_STR_LEN, ""); + + printf(" %9s %9s %9s", "(msec)", "(msec)", "(msec)"); + + if (sched->pre_migrations) + printf(" %9s", "(msec)"); printf("\n"); @@ -2127,15 +2120,15 @@ static void timehist_header(struct perf_sched *sched) if (sched->show_cpu_visual) printf(" %.*s ", ncpus, graph_dotted_line); - if (sched->show_prio) { - printf(" %.*s %.*s %.9s %.9s %.9s", - comm_width, graph_dotted_line, MAX_PRIO_STR_LEN, graph_dotted_line, - graph_dotted_line, graph_dotted_line, graph_dotted_line); - } else { - printf(" %.*s %.9s %.9s %.9s", comm_width, - graph_dotted_line, graph_dotted_line, graph_dotted_line, - graph_dotted_line); - } + printf(" %.*s", comm_width, graph_dotted_line); + + if (sched->show_prio) + printf(" %.*s", MAX_PRIO_STR_LEN, graph_dotted_line); + + printf(" %.9s %.9s %.9s", graph_dotted_line, graph_dotted_line, graph_dotted_line); + + if (sched->pre_migrations) + printf(" %.9s", graph_dotted_line); if (sched->show_state) printf(" %.5s", graph_dotted_line); @@ -2190,6 +2183,8 @@ static void timehist_print_sample(struct perf_sched *sched, print_sched_time(tr->dt_delay, 6); print_sched_time(tr->dt_run, 6); + if (sched->pre_migrations) + print_sched_time(tr->dt_pre_mig, 6); if (sched->show_state) printf(" %5c ", thread__tid(thread) == 0 ? 'I' : state); @@ -2227,18 +2222,21 @@ static void timehist_print_sample(struct perf_sched *sched, * last_time = time of last sched change event for current task * (i.e, time process was last scheduled out) * ready_to_run = time of wakeup for current task + * migrated = time of task migration to another CPU * - * -----|------------|------------|------------|------ - * last ready tprev t + * -----|-------------|-------------|-------------|-------------|----- + * last ready migrated tprev t * time to run * - * |-------- dt_wait --------| - * |- dt_delay -|-- dt_run --| + * |---------------- dt_wait ----------------| + * |--------- dt_delay ---------|-- dt_run --| + * |- dt_pre_mig -| * - * dt_run = run time of current task - * dt_wait = time between last schedule out event for task and tprev - * represents time spent off the cpu - * dt_delay = time between wakeup and schedule-in of task + * dt_run = run time of current task + * dt_wait = time between last schedule out event for task and tprev + * represents time spent off the cpu + * dt_delay = time between wakeup and schedule-in of task + * dt_pre_mig = time between wakeup and migration to another CPU */ static void timehist_update_runtime_stats(struct thread_runtime *r, @@ -2249,6 +2247,7 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, r->dt_iowait = 0; r->dt_preempt = 0; r->dt_run = 0; + r->dt_pre_mig = 0; if (tprev) { r->dt_run = t - tprev; @@ -2257,6 +2256,9 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, pr_debug("time travel: wakeup time for task > previous sched_switch event\n"); else r->dt_delay = tprev - r->ready_to_run; + + if ((r->migrated > r->ready_to_run) && (r->migrated < tprev)) + r->dt_pre_mig = r->migrated - r->ready_to_run; } if (r->last_time > tprev) @@ -2280,6 +2282,7 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, r->total_sleep_time += r->dt_sleep; r->total_iowait_time += r->dt_iowait; r->total_preempt_time += r->dt_preempt; + r->total_pre_mig_time += r->dt_pre_mig; } static bool is_idle_sample(struct perf_sample *sample, @@ -2693,9 +2696,13 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, return -1; tr->migrations++; + tr->migrated = sample->time; /* show migrations if requested */ - timehist_print_migration_event(sched, evsel, sample, machine, thread); + if (sched->show_migrations) { + timehist_print_migration_event(sched, evsel, sample, + machine, thread); + } return 0; } @@ -2846,11 +2853,13 @@ static int timehist_sched_change_event(const struct perf_tool *tool, /* last state is used to determine where to account wait time */ tr->last_state = state; - /* sched out event for task so reset ready to run time */ + /* sched out event for task so reset ready to run time and migrated time */ if (state == 'R') tr->ready_to_run = t; else tr->ready_to_run = 0; + + tr->migrated = 0; } evsel__save_time(evsel, sample->time, sample->cpu); @@ -3290,8 +3299,8 @@ static int perf_sched__timehist(struct perf_sched *sched) goto out; } - if (sched->show_migrations && - perf_session__set_tracepoints_handlers(session, migrate_handlers)) + if ((sched->show_migrations || sched->pre_migrations) && + perf_session__set_tracepoints_handlers(session, migrate_handlers)) goto out; /* pre-allocate struct for per-CPU idle stats */ @@ -3833,6 +3842,7 @@ int cmd_sched(int argc, const char **argv) OPT_BOOLEAN(0, "show-prio", &sched.show_prio, "Show task priority"), OPT_STRING(0, "prio", &sched.prio_str, "prio", "analyze events only for given task priority(ies)"), + OPT_BOOLEAN('P', "pre-migrations", &sched.pre_migrations, "Show pre-migration wait time"), OPT_PARENT(sched_options) }; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index a644787fa9e1d..9e47905f75a6b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -67,7 +67,7 @@ #include #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif static char const *script_name; @@ -1728,6 +1728,7 @@ static struct { {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"}, {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentry"}, {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_BRANCH_MISS, "br miss"}, {0, NULL} }; @@ -2136,11 +2137,11 @@ struct metric_ctx { }; static void script_print_metric(struct perf_stat_config *config __maybe_unused, - void *ctx, const char *color, - const char *fmt, - const char *unit, double val) + void *ctx, enum metric_threshold_classify thresh, + const char *fmt, const char *unit, double val) { struct metric_ctx *mctx = ctx; + const char *color = metric_threshold_classify__color(thresh); if (!fmt) return; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 689a3d43c2584..fdf5172646a59 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -46,6 +46,7 @@ #include "util/parse-events.h" #include "util/pmus.h" #include "util/pmu.h" +#include "util/tool_pmu.h" #include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" @@ -294,14 +295,14 @@ static int read_single_counter(struct evsel *counter, int cpu_map_idx, int threa * terminates. Use the wait4 values in that case. */ if (err && cpu_map_idx == 0 && - (evsel__tool_event(counter) == PERF_TOOL_USER_TIME || - evsel__tool_event(counter) == PERF_TOOL_SYSTEM_TIME)) { + (evsel__tool_event(counter) == TOOL_PMU__EVENT_USER_TIME || + evsel__tool_event(counter) == TOOL_PMU__EVENT_SYSTEM_TIME)) { u64 val, *start_time; struct perf_counts_values *count = perf_counts(counter->counts, cpu_map_idx, thread); start_time = xyarray__entry(counter->start_times, cpu_map_idx, thread); - if (evsel__tool_event(counter) == PERF_TOOL_USER_TIME) + if (evsel__tool_event(counter) == TOOL_PMU__EVENT_USER_TIME) val = ru_stats.ru_utime_usec_stat.mean; else val = ru_stats.ru_stime_usec_stat.mean; @@ -639,8 +640,7 @@ static enum counter_recovery stat_handle_error(struct evsel *counter) * (behavior changed with commit b0a873e). */ if (errno == EINVAL || errno == ENOSYS || - errno == ENOENT || errno == EOPNOTSUPP || - errno == ENXIO) { + errno == ENOENT || errno == ENXIO) { if (verbose > 0) ui__warning("%s event is not supported by the kernel.\n", evsel__name(counter)); @@ -658,7 +658,7 @@ static enum counter_recovery stat_handle_error(struct evsel *counter) if (verbose > 0) ui__warning("%s\n", msg); return COUNTER_RETRY; - } else if (target__has_per_thread(&target) && + } else if (target__has_per_thread(&target) && errno != EOPNOTSUPP && evsel_list->core.threads && evsel_list->core.threads->err_thread != -1) { /* @@ -679,6 +679,19 @@ static enum counter_recovery stat_handle_error(struct evsel *counter) return COUNTER_SKIP; } + if (errno == EOPNOTSUPP) { + if (verbose > 0) { + ui__warning("%s event is not supported by the kernel.\n", + evsel__name(counter)); + } + counter->supported = false; + counter->errored = true; + + if ((evsel__leader(counter) != counter) || + !(counter->core.leader->nr_members > 1)) + return COUNTER_SKIP; + } + evsel__open_strerror(counter, &target, errno, msg, sizeof(msg)); ui__error("%s\n", msg); @@ -716,15 +729,19 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } if (!cpu_map__is_dummy(evsel_list->core.user_requested_cpus)) { - if (affinity__setup(&saved_affinity) < 0) - return -1; + if (affinity__setup(&saved_affinity) < 0) { + err = -1; + goto err_out; + } affinity = &saved_affinity; } evlist__for_each_entry(evsel_list, counter) { counter->reset_group = false; - if (bpf_counter__load(counter, &target)) - return -1; + if (bpf_counter__load(counter, &target)) { + err = -1; + goto err_out; + } if (!(evsel__is_bperf(counter))) all_counters_use_bpf = false; } @@ -767,7 +784,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) switch (stat_handle_error(counter)) { case COUNTER_FATAL: - return -1; + err = -1; + goto err_out; case COUNTER_RETRY: goto try_again; case COUNTER_SKIP: @@ -808,7 +826,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) switch (stat_handle_error(counter)) { case COUNTER_FATAL: - return -1; + err = -1; + goto err_out; case COUNTER_RETRY: goto try_again_reset; case COUNTER_SKIP: @@ -821,6 +840,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } } affinity__cleanup(affinity); + affinity = NULL; evlist__for_each_entry(evsel_list, counter) { if (!counter->supported) { @@ -833,8 +853,10 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) stat_config.unit_width = l; if (evsel__should_store_id(counter) && - evsel__store_ids(counter, evsel_list)) - return -1; + evsel__store_ids(counter, evsel_list)) { + err = -1; + goto err_out; + } } if (evlist__apply_filters(evsel_list, &counter, &target)) { @@ -855,20 +877,23 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } if (err < 0) - return err; + goto err_out; err = perf_event__synthesize_stat_events(&stat_config, NULL, evsel_list, process_synthesized_event, is_pipe); if (err < 0) - return err; + goto err_out; + } if (target.initial_delay) { pr_info(EVLIST_DISABLED_MSG); } else { err = enable_counters(); - if (err) - return -1; + if (err) { + err = -1; + goto err_out; + } } /* Exec the command, if any */ @@ -878,8 +903,10 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) if (target.initial_delay > 0) { usleep(target.initial_delay * USEC_PER_MSEC); err = enable_counters(); - if (err) - return -1; + if (err) { + err = -1; + goto err_out; + } pr_info(EVLIST_ENABLED_MSG); } @@ -899,7 +926,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) if (workload_exec_errno) { const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); pr_err("Workload failed: %s\n", emsg); - return -1; + err = -1; + goto err_out; } if (WIFSIGNALED(status)) @@ -946,8 +974,23 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) evlist__close(evsel_list); return WEXITSTATUS(status); + +err_out: + if (forks) + evlist__cancel_workload(evsel_list); + + affinity__cleanup(affinity); + return err; } +/* + * Returns -1 for fatal errors which signifies to not continue + * when in repeat mode. + * + * Returns < -1 error codes when stat record is used. These + * result in the stat information being displayed, but writing + * to the file fails and is non fatal. + */ static int run_perf_stat(int argc, const char **argv, int run_idx) { int ret; @@ -1814,130 +1857,25 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st) } /* - * Add default attributes, if there were no attributes specified or + * Add default events, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: */ -static int add_default_attributes(void) +static int add_default_events(void) { - struct perf_event_attr default_attrs0[] = { - - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS }, - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, - - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, -}; - struct perf_event_attr frontend_attrs[] = { - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, -}; - struct perf_event_attr backend_attrs[] = { - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, -}; - struct perf_event_attr default_attrs1[] = { - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, - -}; - -/* - * Detailed stats (-d), covering the L1 and last level data caches: - */ - struct perf_event_attr detailed_attrs[] = { - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_LL << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_LL << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, -}; - -/* - * Very detailed stats (-d -d), covering the instruction cache and the TLB caches: - */ - struct perf_event_attr very_detailed_attrs[] = { - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1I << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1I << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_DTLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_DTLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_ITLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_ITLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - -}; + const char *pmu = parse_events_option_args.pmu_filter ?: "all"; + struct parse_events_error err; + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + int ret = 0; -/* - * Very, very detailed stats (-d -d -d), adding prefetch events: - */ - struct perf_event_attr very_very_detailed_attrs[] = { - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, -}; + if (!evlist) + return -ENOMEM; - struct perf_event_attr default_null_attrs[] = {}; - const char *pmu = parse_events_option_args.pmu_filter ?: "all"; + parse_events_error__init(&err); /* Set attrs if no event is selected and !null_run: */ if (stat_config.null_run) - return 0; + goto out; if (transaction_run) { /* Handle -T as -M transaction. Once platform specific metrics @@ -1947,9 +1885,10 @@ static int add_default_attributes(void) */ if (!metricgroup__has_metric(pmu, "transaction")) { pr_err("Missing transaction metrics\n"); - return -1; + ret = -1; + goto out; } - return metricgroup__parse_groups(evsel_list, pmu, "transaction", + ret = metricgroup__parse_groups(evlist, pmu, "transaction", stat_config.metric_no_group, stat_config.metric_no_merge, stat_config.metric_no_threshold, @@ -1957,6 +1896,7 @@ static int add_default_attributes(void) stat_config.system_wide, stat_config.hardware_aware_grouping, &stat_config.metric_events); + goto out; } if (smi_cost) { @@ -1964,26 +1904,29 @@ static int add_default_attributes(void) if (sysfs__read_int(FREEZE_ON_SMI_PATH, &smi) < 0) { pr_err("freeze_on_smi is not supported.\n"); - return -1; + ret = -1; + goto out; } if (!smi) { if (sysfs__write_int(FREEZE_ON_SMI_PATH, 1) < 0) { - fprintf(stderr, "Failed to set freeze_on_smi.\n"); - return -1; + pr_err("Failed to set freeze_on_smi.\n"); + ret = -1; + goto out; } smi_reset = true; } if (!metricgroup__has_metric(pmu, "smi")) { pr_err("Missing smi metrics\n"); - return -1; + ret = -1; + goto out; } if (!force_metric_only) stat_config.metric_only = true; - return metricgroup__parse_groups(evsel_list, pmu, "smi", + ret = metricgroup__parse_groups(evlist, pmu, "smi", stat_config.metric_no_group, stat_config.metric_no_merge, stat_config.metric_no_threshold, @@ -1991,6 +1934,7 @@ static int add_default_attributes(void) stat_config.system_wide, stat_config.hardware_aware_grouping, &stat_config.metric_events); + goto out; } if (topdown_run) { @@ -2003,21 +1947,23 @@ static int add_default_attributes(void) if (!max_level) { pr_err("Topdown requested but the topdown metric groups aren't present.\n" "(See perf list the metric groups have names like TopdownL1)\n"); - return -1; + ret = -1; + goto out; } if (stat_config.topdown_level > max_level) { pr_err("Invalid top-down metrics level. The max level is %u.\n", max_level); - return -1; - } else if (!stat_config.topdown_level) + ret = -1; + goto out; + } else if (!stat_config.topdown_level) { stat_config.topdown_level = 1; - + } if (!stat_config.interval && !stat_config.metric_only) { fprintf(stat_config.output, "Topdown accuracy may decrease when measuring long periods.\n" "Please print the result regularly, e.g. -I1000\n"); } str[8] = stat_config.topdown_level + '0'; - if (metricgroup__parse_groups(evsel_list, + if (metricgroup__parse_groups(evlist, pmu, str, /*metric_no_group=*/false, /*metric_no_merge=*/false, @@ -2025,41 +1971,49 @@ static int add_default_attributes(void) stat_config.user_requested_cpu_list, stat_config.system_wide, stat_config.hardware_aware_grouping, - &stat_config.metric_events) < 0) - return -1; + &stat_config.metric_events) < 0) { + ret = -1; + goto out; + } } if (!stat_config.topdown_level) stat_config.topdown_level = 1; - if (!evsel_list->core.nr_entries) { + if (!evlist->core.nr_entries && !evsel_list->core.nr_entries) { /* No events so add defaults. */ if (target__has_cpu(&target)) - default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK; + ret = parse_events(evlist, "cpu-clock", &err); + else + ret = parse_events(evlist, "task-clock", &err); + if (ret) + goto out; + + ret = parse_events(evlist, + "context-switches," + "cpu-migrations," + "page-faults," + "instructions," + "cycles," + "stalled-cycles-frontend," + "stalled-cycles-backend," + "branches," + "branch-misses", + &err); + if (ret) + goto out; - if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0) - return -1; - if (perf_pmus__have_event("cpu", "stalled-cycles-frontend")) { - if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0) - return -1; - } - if (perf_pmus__have_event("cpu", "stalled-cycles-backend")) { - if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0) - return -1; - } - if (evlist__add_default_attrs(evsel_list, default_attrs1) < 0) - return -1; /* * Add TopdownL1 metrics if they exist. To minimize * multiplexing, don't request threshold computation. */ if (metricgroup__has_metric(pmu, "Default")) { struct evlist *metric_evlist = evlist__new(); - struct evsel *metric_evsel; - - if (!metric_evlist) - return -1; + if (!metric_evlist) { + ret = -ENOMEM; + goto out; + } if (metricgroup__parse_groups(metric_evlist, pmu, "Default", /*metric_no_group=*/false, /*metric_no_merge=*/false, @@ -2067,43 +2021,71 @@ static int add_default_attributes(void) stat_config.user_requested_cpu_list, stat_config.system_wide, stat_config.hardware_aware_grouping, - &stat_config.metric_events) < 0) - return -1; - - evlist__for_each_entry(metric_evlist, metric_evsel) { - metric_evsel->skippable = true; - metric_evsel->default_metricgroup = true; + &stat_config.metric_events) < 0) { + ret = -1; + goto out; } - evlist__splice_list_tail(evsel_list, &metric_evlist->core.entries); + + evlist__for_each_entry(metric_evlist, evsel) + evsel->default_metricgroup = true; + + evlist__splice_list_tail(evlist, &metric_evlist->core.entries); evlist__delete(metric_evlist); } - - /* Platform specific attrs */ - if (evlist__add_default_attrs(evsel_list, default_null_attrs) < 0) - return -1; } /* Detailed events get appended to the event list: */ - if (detailed_run < 1) - return 0; - - /* Append detailed run extra attributes: */ - if (evlist__add_default_attrs(evsel_list, detailed_attrs) < 0) - return -1; - - if (detailed_run < 2) - return 0; - - /* Append very detailed run extra attributes: */ - if (evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0) - return -1; - - if (detailed_run < 3) - return 0; - - /* Append very, very detailed run extra attributes: */ - return evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); + if (!ret && detailed_run >= 1) { + /* + * Detailed stats (-d), covering the L1 and last level data + * caches: + */ + ret = parse_events(evlist, + "L1-dcache-loads," + "L1-dcache-load-misses," + "LLC-loads," + "LLC-load-misses", + &err); + } + if (!ret && detailed_run >= 2) { + /* + * Very detailed stats (-d -d), covering the instruction cache + * and the TLB caches: + */ + ret = parse_events(evlist, + "L1-icache-loads," + "L1-icache-load-misses," + "dTLB-loads," + "dTLB-load-misses," + "iTLB-loads," + "iTLB-load-misses", + &err); + } + if (!ret && detailed_run >= 3) { + /* + * Very, very detailed stats (-d -d -d), adding prefetch events: + */ + ret = parse_events(evlist, + "L1-dcache-prefetches," + "L1-dcache-prefetch-misses", + &err); + } +out: + if (!ret) { + evlist__for_each_entry(evlist, evsel) { + /* + * Make at least one event non-skippable so fatal errors are visible. + * 'cycles' always used to be default and non-skippable, so use that. + */ + if (strcmp("cycles", evsel__name(evsel))) + evsel->skippable = true; + } + } + parse_events_error__exit(&err); + evlist__splice_list_tail(evsel_list, &evlist->core.entries); + evlist__delete(evlist); + return ret; } static const char * const stat_record_usage[] = { @@ -2591,6 +2573,14 @@ int cmd_stat(int argc, const char **argv) goto out; } + if (stat_config.csv_output || (stat_config.metric_only && stat_config.json_output)) { + /* + * Current CSV and metric-only JSON output doesn't display the + * metric threshold so don't compute it. + */ + stat_config.metric_no_threshold = true; + } + if (stat_config.walltime_run_table && stat_config.run_count <= 1) { fprintf(stderr, "--table is only supported with -r\n"); parse_options_usage(stat_usage, stat_options, "r", 1); @@ -2651,6 +2641,7 @@ int cmd_stat(int argc, const char **argv) } else if (big_num_opt == 0) /* User passed --no-big-num */ stat_config.big_num = false; + target.inherit = !stat_config.no_inherit; err = target__validate(&target); if (err) { target__strerror(&target, err, errbuf, BUFSIZ); @@ -2760,7 +2751,7 @@ int cmd_stat(int argc, const char **argv) } } - if (add_default_attributes()) + if (add_default_events()) goto out; if (stat_config.cgroup_list) { @@ -2879,7 +2870,10 @@ int cmd_stat(int argc, const char **argv) evlist__reset_prev_raw_counts(evsel_list); status = run_perf_stat(argc, argv, run_idx); - if (forever && status != -1 && !interval) { + if (status == -1) + break; + + if (forever && !interval) { print_counters(NULL, argc, argv); perf_stat__reset_stats(); } diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 218c8b44d7bea..068d297aaf44b 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -38,7 +38,7 @@ #include "util/tracepoint.h" #include "util/util.h" #include -#include +#include #ifdef LACKS_OPEN_MEMSTREAM_PROTOTYPE FILE *open_memstream(char **ptr, size_t *sizeloc); @@ -1158,7 +1158,6 @@ static void draw_io_bars(struct timechart *tchart) } svg_box(Y, c->start_time, c->end_time, "process3"); - sample = c->io_samples; for (sample = c->io_samples; sample; sample = sample->next) { double h = (double)sample->bytes / c->max_bytes; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d3f11b90d0255..6a1a128fe6450 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -88,7 +88,7 @@ #include #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif #ifndef O_CLOEXEC @@ -1873,7 +1873,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine, switch (event->header.type) { case PERF_RECORD_LOST: color_fprintf(trace->output, PERF_COLOR_RED, - "LOST %" PRIu64 " events!\n", event->lost.lost); + "LOST %" PRIu64 " events!\n", (u64)event->lost.lost); ret = machine__process_lost_event(machine, event, sample); break; default: @@ -2702,6 +2702,7 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, char msg[1024]; void *args, *augmented_args = NULL; int augmented_args_size; + size_t printed = 0; if (sc == NULL) return -1; @@ -2717,8 +2718,8 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, args = perf_evsel__sc_tp_ptr(evsel, args, sample); augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); - syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); - fprintf(trace->output, "%s", msg); + printed += syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); + fprintf(trace->output, "%.*s", (int)printed, msg); err = 0; out_put: thread__put(thread); @@ -3087,7 +3088,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, printed += syscall_arg_fmt__scnprintf_val(arg, bf + printed, size - printed, &syscall_arg, val); } - return printed + fprintf(trace->output, "%s", bf); + return printed + fprintf(trace->output, "%.*s", (int)printed, bf); } static int trace__event_handler(struct trace *trace, struct evsel *evsel, @@ -3096,13 +3097,8 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, { struct thread *thread; int callchain_ret = 0; - /* - * Check if we called perf_evsel__disable(evsel) due to, for instance, - * this event's max_events having been hit and this is an entry coming - * from the ring buffer that we should discard, since the max events - * have already been considered/printed. - */ - if (evsel->disabled) + + if (evsel->nr_events_printed >= evsel->max_events) return 0; thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); @@ -4326,6 +4322,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv) sizeof(__u32), BPF_ANY); } } + + if (trace->skel) + trace->filter_pids.map = trace->skel->maps.pids_filtered; #endif err = trace__set_filter_pids(trace); if (err < 0) @@ -5449,6 +5448,10 @@ int cmd_trace(int argc, const char **argv) if (trace.summary_only) trace.summary = trace.summary_only; + /* Keep exited threads, otherwise information might be lost for summary */ + if (trace.summary) + symbol_conf.keep_exited_threads = true; + if (output_name != NULL) { err = trace__open_output(&trace, output_name); if (err < 0) { diff --git a/tools/perf/check-header_ignore_hunks/lib/list_sort.c b/tools/perf/check-header_ignore_hunks/lib/list_sort.c index 32d98cb34f80a..b7316d29857d4 100644 --- a/tools/perf/check-header_ignore_hunks/lib/list_sort.c +++ b/tools/perf/check-header_ignore_hunks/lib/list_sort.c @@ -1,11 +1,4 @@ -@@ -1,5 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0 - #include -+#include - #include - #include - #include -@@ -52,6 +53,7 @@ +@@ -50,6 +50,7 @@ struct list_head *a, struct list_head *b) { struct list_head *tail = head; @@ -13,7 +6,7 @@ for (;;) { /* if equal, take 'a' -- important for sort stability */ -@@ -77,6 +79,15 @@ +@@ -75,6 +76,15 @@ /* Finish linking remainder of list b on to tail */ tail->next = b; do { diff --git a/tools/perf/dlfilters/dlfilter-test-api-v0.c b/tools/perf/dlfilters/dlfilter-test-api-v0.c index 4083b1abeaabe..4ca2d7b2ea6c8 100644 --- a/tools/perf/dlfilters/dlfilter-test-api-v0.c +++ b/tools/perf/dlfilters/dlfilter-test-api-v0.c @@ -220,7 +220,7 @@ static int check_sample(struct filter_data *d, const struct perf_dlfilter_sample CHECK_SAMPLE(raw_callchain_nr); CHECK(!sample->raw_callchain); -#define EVENT_NAME "branches:" +#define EVENT_NAME "branches" CHECK(!strncmp(sample->event, EVENT_NAME, strlen(EVENT_NAME))); return 0; diff --git a/tools/perf/dlfilters/dlfilter-test-api-v2.c b/tools/perf/dlfilters/dlfilter-test-api-v2.c index 32ff619e881ca..00d73a16c4fda 100644 --- a/tools/perf/dlfilters/dlfilter-test-api-v2.c +++ b/tools/perf/dlfilters/dlfilter-test-api-v2.c @@ -235,7 +235,7 @@ static int check_sample(struct filter_data *d, const struct perf_dlfilter_sample CHECK_SAMPLE(raw_callchain_nr); CHECK(!sample->raw_callchain); -#define EVENT_NAME "branches:" +#define EVENT_NAME "branches" CHECK(!strncmp(sample->event, EVENT_NAME, strlen(EVENT_NAME))); return 0; diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 4def800f4089d..a2987f2cfe1a3 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -542,8 +542,6 @@ int main(int argc, const char **argv) } cmd = argv[0]; - test_attr__init(); - /* * We use PATH to find perf commands, but we prepend some higher * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH diff --git a/tools/perf/pmu-events/arch/arm64/freescale/imx91/sys/ddrc.json b/tools/perf/pmu-events/arch/arm64/freescale/imx91/sys/ddrc.json new file mode 100644 index 0000000000000..74ac12660a294 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/freescale/imx91/sys/ddrc.json @@ -0,0 +1,9 @@ +[ + { + "BriefDescription": "ddr cycles event", + "EventCode": "0x00", + "EventName": "imx91_ddr.cycles", + "Unit": "imx9_ddr", + "Compat": "imx91" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/freescale/imx91/sys/metrics.json b/tools/perf/pmu-events/arch/arm64/freescale/imx91/sys/metrics.json new file mode 100644 index 0000000000000..f0c5911eb2d0c --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/freescale/imx91/sys/metrics.json @@ -0,0 +1,26 @@ +[ + { + "BriefDescription": "bandwidth usage for lpddr4 evk board", + "MetricName": "imx91_bandwidth_usage.lpddr4", + "MetricExpr": "(((( imx9_ddr0@ddrc_pm_0@ ) * 2 * 8 ) + (( imx9_ddr0@ddrc_pm_3@ + imx9_ddr0@ddrc_pm_5@ + imx9_ddr0@ddrc_pm_7@ + imx9_ddr0@ddrc_pm_9@ - imx9_ddr0@ddrc_pm_2@ - imx9_ddr0@ddrc_pm_4@ - imx9_ddr0@ddrc_pm_6@ - imx9_ddr0@ddrc_pm_8@ ) * 32 )) / duration_time) / (2400 * 1000000 * 2)", + "ScaleUnit": "1e2%", + "Unit": "imx9_ddr", + "Compat": "imx91" + }, + { + "BriefDescription": "bytes all masters read from ddr", + "MetricName": "imx91_ddr_read.all", + "MetricExpr": "( imx9_ddr0@ddrc_pm_0@ ) * 2 * 8", + "ScaleUnit": "9.765625e-4KB", + "Unit": "imx9_ddr", + "Compat": "imx91" + }, + { + "BriefDescription": "bytes all masters write to ddr", + "MetricName": "imx91_ddr_write.all", + "MetricExpr": "( imx9_ddr0@ddrc_pm_3@ + imx9_ddr0@ddrc_pm_5@ + imx9_ddr0@ddrc_pm_7@ + imx9_ddr0@ddrc_pm_9@ - imx9_ddr0@ddrc_pm_2@ - imx9_ddr0@ddrc_pm_4@ - imx9_ddr0@ddrc_pm_6@ - imx9_ddr0@ddrc_pm_8@ ) * 32", + "ScaleUnit": "9.765625e-4KB", + "Unit": "imx9_ddr", + "Compat": "imx91" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/freescale/imx95/sys/metrics.json b/tools/perf/pmu-events/arch/arm64/freescale/imx95/sys/metrics.json index 126ce980f6f25..45a0d51dfb639 100644 --- a/tools/perf/pmu-events/arch/arm64/freescale/imx95/sys/metrics.json +++ b/tools/perf/pmu-events/arch/arm64/freescale/imx95/sys/metrics.json @@ -7,6 +7,14 @@ "Unit": "imx9_ddr", "Compat": "imx95" }, + { + "BriefDescription": "bandwidth usage for lpddr4x evk board", + "MetricName": "imx95_bandwidth_usage.lpddr4x", + "MetricExpr": "(( imx9_ddr0@eddrtq_pm_rd_beat_filt0\\,axi_mask\\=0x000\\,axi_id\\=0x000@ + imx9_ddr0@eddrtq_pm_wr_beat_filt\\,axi_mask\\=0x000\\,axi_id\\=0x000@ ) * 32 / duration_time) / (4000 * 1000000 * 4)", + "ScaleUnit": "1e2%", + "Unit": "imx9_ddr", + "Compat": "imx95" + }, { "BriefDescription": "bytes of all masters read from ddr", "MetricName": "imx95_ddr_read.all", diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/metrics.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/metrics.json index 6463531b99410..b6a0d2de8534a 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/metrics.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/metrics.json @@ -3,235 +3,235 @@ "MetricExpr": "FETCH_BUBBLE / (4 * CPU_CYCLES)", "PublicDescription": "Frontend bound L1 topdown metric", "BriefDescription": "Frontend bound L1 topdown metric", - "DefaultMetricgroupName": "TopDownL1", - "MetricGroup": "Default;TopDownL1", + "DefaultMetricgroupName": "TopdownL1", + "MetricGroup": "Default;TopdownL1", "MetricName": "frontend_bound" }, { "MetricExpr": "(INST_SPEC - INST_RETIRED) / (4 * CPU_CYCLES)", "PublicDescription": "Bad Speculation L1 topdown metric", "BriefDescription": "Bad Speculation L1 topdown metric", - "DefaultMetricgroupName": "TopDownL1", - "MetricGroup": "Default;TopDownL1", + "DefaultMetricgroupName": "TopdownL1", + "MetricGroup": "Default;TopdownL1", "MetricName": "bad_speculation" }, { "MetricExpr": "INST_RETIRED / (CPU_CYCLES * 4)", "PublicDescription": "Retiring L1 topdown metric", "BriefDescription": "Retiring L1 topdown metric", - "DefaultMetricgroupName": "TopDownL1", - "MetricGroup": "Default;TopDownL1", + "DefaultMetricgroupName": "TopdownL1", + "MetricGroup": "Default;TopdownL1", "MetricName": "retiring" }, { "MetricExpr": "1 - (frontend_bound + bad_speculation + retiring)", "PublicDescription": "Backend Bound L1 topdown metric", "BriefDescription": "Backend Bound L1 topdown metric", - "DefaultMetricgroupName": "TopDownL1", - "MetricGroup": "Default;TopDownL1", + "DefaultMetricgroupName": "TopdownL1", + "MetricGroup": "Default;TopdownL1", "MetricName": "backend_bound" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x201d@ / CPU_CYCLES", "PublicDescription": "Fetch latency bound L2 topdown metric", "BriefDescription": "Fetch latency bound L2 topdown metric", - "MetricGroup": "TopDownL2", + "MetricGroup": "TopdownL2", "MetricName": "fetch_latency_bound" }, { "MetricExpr": "frontend_bound - fetch_latency_bound", "PublicDescription": "Fetch bandwidth bound L2 topdown metric", "BriefDescription": "Fetch bandwidth bound L2 topdown metric", - "MetricGroup": "TopDownL2", + "MetricGroup": "TopdownL2", "MetricName": "fetch_bandwidth_bound" }, { "MetricExpr": "(bad_speculation * BR_MIS_PRED) / (BR_MIS_PRED + armv8_pmuv3_0@event\\=0x2013@)", "PublicDescription": "Branch mispredicts L2 topdown metric", "BriefDescription": "Branch mispredicts L2 topdown metric", - "MetricGroup": "TopDownL2", + "MetricGroup": "TopdownL2", "MetricName": "branch_mispredicts" }, { "MetricExpr": "bad_speculation - branch_mispredicts", "PublicDescription": "Machine clears L2 topdown metric", "BriefDescription": "Machine clears L2 topdown metric", - "MetricGroup": "TopDownL2", + "MetricGroup": "TopdownL2", "MetricName": "machine_clears" }, { "MetricExpr": "(EXE_STALL_CYCLE - (MEM_STALL_ANYLOAD + armv8_pmuv3_0@event\\=0x7005@)) / CPU_CYCLES", "PublicDescription": "Core bound L2 topdown metric", "BriefDescription": "Core bound L2 topdown metric", - "MetricGroup": "TopDownL2", + "MetricGroup": "TopdownL2", "MetricName": "core_bound" }, { "MetricExpr": "(MEM_STALL_ANYLOAD + armv8_pmuv3_0@event\\=0x7005@) / CPU_CYCLES", "PublicDescription": "Memory bound L2 topdown metric", "BriefDescription": "Memory bound L2 topdown metric", - "MetricGroup": "TopDownL2", + "MetricGroup": "TopdownL2", "MetricName": "memory_bound" }, { "MetricExpr": "(((L2I_TLB - L2I_TLB_REFILL) * 15) + (L2I_TLB_REFILL * 100)) / CPU_CYCLES", "PublicDescription": "Idle by itlb miss L3 topdown metric", "BriefDescription": "Idle by itlb miss L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "idle_by_itlb_miss" }, { "MetricExpr": "(((L2I_CACHE - L2I_CACHE_REFILL) * 15) + (L2I_CACHE_REFILL * 100)) / CPU_CYCLES", "PublicDescription": "Idle by icache miss L3 topdown metric", "BriefDescription": "Idle by icache miss L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "idle_by_icache_miss" }, { "MetricExpr": "(BR_MIS_PRED * 5) / CPU_CYCLES", "PublicDescription": "BP misp flush L3 topdown metric", "BriefDescription": "BP misp flush L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "bp_misp_flush" }, { "MetricExpr": "(armv8_pmuv3_0@event\\=0x2013@ * 5) / CPU_CYCLES", "PublicDescription": "OOO flush L3 topdown metric", "BriefDescription": "OOO flush L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "ooo_flush" }, { "MetricExpr": "(armv8_pmuv3_0@event\\=0x1001@ * 5) / CPU_CYCLES", "PublicDescription": "Static predictor flush L3 topdown metric", "BriefDescription": "Static predictor flush L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "sp_flush" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x1010@ / BR_MIS_PRED", "PublicDescription": "Indirect branch L3 topdown metric", "BriefDescription": "Indirect branch L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "indirect_branch" }, { "MetricExpr": "(armv8_pmuv3_0@event\\=0x1013@ + armv8_pmuv3_0@event\\=0x1016@) / BR_MIS_PRED", "PublicDescription": "Push branch L3 topdown metric", "BriefDescription": "Push branch L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "push_branch" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x100d@ / BR_MIS_PRED", "PublicDescription": "Pop branch L3 topdown metric", "BriefDescription": "Pop branch L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "pop_branch" }, { "MetricExpr": "(BR_MIS_PRED - armv8_pmuv3_0@event\\=0x1010@ - armv8_pmuv3_0@event\\=0x1013@ - armv8_pmuv3_0@event\\=0x1016@ - armv8_pmuv3_0@event\\=0x100d@) / BR_MIS_PRED", "PublicDescription": "Other branch L3 topdown metric", "BriefDescription": "Other branch L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "other_branch" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x2012@ / armv8_pmuv3_0@event\\=0x2013@", "PublicDescription": "Nuke flush L3 topdown metric", "BriefDescription": "Nuke flush L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "nuke_flush" }, { "MetricExpr": "1 - nuke_flush", "PublicDescription": "Other flush L3 topdown metric", "BriefDescription": "Other flush L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "other_flush" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x2010@ / CPU_CYCLES", "PublicDescription": "Sync stall L3 topdown metric", "BriefDescription": "Sync stall L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "sync_stall" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x2004@ / CPU_CYCLES", "PublicDescription": "Rob stall L3 topdown metric", "BriefDescription": "Rob stall L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "rob_stall" }, { "MetricExpr": "(armv8_pmuv3_0@event\\=0x2006@ + armv8_pmuv3_0@event\\=0x2007@ + armv8_pmuv3_0@event\\=0x2008@) / CPU_CYCLES", "PublicDescription": "Ptag stall L3 topdown metric", "BriefDescription": "Ptag stall L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "ptag_stall" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x201e@ / CPU_CYCLES", "PublicDescription": "SaveOpQ stall L3 topdown metric", "BriefDescription": "SaveOpQ stall L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "saveopq_stall" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x2005@ / CPU_CYCLES", "PublicDescription": "PC buffer stall L3 topdown metric", "BriefDescription": "PC buffer stall L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "pc_buffer_stall" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x7002@ / CPU_CYCLES", "PublicDescription": "Divider L3 topdown metric", "BriefDescription": "Divider L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "divider" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x7003@ / CPU_CYCLES", "PublicDescription": "FSU stall L3 topdown metric", "BriefDescription": "FSU stall L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "fsu_stall" }, { "MetricExpr": "core_bound - divider - fsu_stall", "PublicDescription": "EXE ports util L3 topdown metric", "BriefDescription": "EXE ports util L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "exe_ports_util" }, { "MetricExpr": "(MEM_STALL_ANYLOAD - MEM_STALL_L1MISS) / CPU_CYCLES", "PublicDescription": "L1 bound L3 topdown metric", "BriefDescription": "L1 bound L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "l1_bound" }, { "MetricExpr": "(MEM_STALL_L1MISS - MEM_STALL_L2MISS) / CPU_CYCLES", "PublicDescription": "L2 bound L3 topdown metric", "BriefDescription": "L2 bound L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "l2_bound" }, { "MetricExpr": "MEM_STALL_L2MISS / CPU_CYCLES", "PublicDescription": "Mem bound L3 topdown metric", "BriefDescription": "Mem bound L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "mem_bound" }, { "MetricExpr": "armv8_pmuv3_0@event\\=0x7005@ / CPU_CYCLES", "PublicDescription": "Store bound L3 topdown metric", "BriefDescription": "Store bound L3 topdown metric", - "MetricGroup": "TopDownL3", + "MetricGroup": "TopdownL3", "MetricName": "store_bound" } ] diff --git a/tools/perf/pmu-events/arch/common/common/tool.json b/tools/perf/pmu-events/arch/common/common/tool.json new file mode 100644 index 0000000000000..12f2ef1813a63 --- /dev/null +++ b/tools/perf/pmu-events/arch/common/common/tool.json @@ -0,0 +1,74 @@ +[ + { + "Unit": "tool", + "EventName": "duration_time", + "BriefDescription": "Wall clock interval time in nanoseconds", + "ConfigCode": "1" + }, + { + "Unit": "tool", + "EventName": "user_time", + "BriefDescription": "User (non-kernel) time in nanoseconds", + "ConfigCode": "2" + }, + { + "Unit": "tool", + "EventName": "system_time", + "BriefDescription": "System/kernel time in nanoseconds", + "ConfigCode": "3" + }, + { + "Unit": "tool", + "EventName": "has_pmem", + "BriefDescription": "1 if persistent memory installed otherwise 0", + "ConfigCode": "4" + }, + { + "Unit": "tool", + "EventName": "num_cores", + "BriefDescription": "Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU", + "ConfigCode": "5" + }, + { + "Unit": "tool", + "EventName": "num_cpus", + "BriefDescription": "Number of logical Linux CPUs. There may be multiple such CPUs on a core", + "ConfigCode": "6" + }, + { + "Unit": "tool", + "EventName": "num_cpus_online", + "BriefDescription": "Number of online logical Linux CPUs. There may be multiple such CPUs on a core", + "ConfigCode": "7" + }, + { + "Unit": "tool", + "EventName": "num_dies", + "BriefDescription": "Number of dies. Each die has 1 or more cores", + "ConfigCode": "8" + }, + { + "Unit": "tool", + "EventName": "num_packages", + "BriefDescription": "Number of packages. Each package has 1 or more die", + "ConfigCode": "9" + }, + { + "Unit": "tool", + "EventName": "slots", + "BriefDescription": "Number of functional units that in parallel can execute parts of an instruction", + "ConfigCode": "10" + }, + { + "Unit": "tool", + "EventName": "smt_on", + "BriefDescription": "1 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0", + "ConfigCode": "11" + }, + { + "Unit": "tool", + "EventName": "system_tsc_freq", + "BriefDescription": "The amount a Time Stamp Counter (TSC) increases per second", + "ConfigCode": "12" + } +] diff --git a/tools/perf/pmu-events/arch/powerpc/compat/generic-events.json b/tools/perf/pmu-events/arch/powerpc/compat/generic-events.json new file mode 100644 index 0000000000000..6f5e8efcb098c --- /dev/null +++ b/tools/perf/pmu-events/arch/powerpc/compat/generic-events.json @@ -0,0 +1,117 @@ +[ + { + "EventCode": "0x600F4", + "EventName": "PM_CYC", + "BriefDescription": "Processor cycles." + }, + { + "EventCode": "0x100F2", + "EventName": "PM_CYC_INST_CMPL", + "BriefDescription": "1 or more ppc insts finished" + }, + { + "EventCode": "0x100f4", + "EventName": "PM_FLOP_CMPL", + "BriefDescription": "Floating Point Operations Finished." + }, + { + "EventCode": "0x100F6", + "EventName": "PM_L1_ITLB_MISS", + "BriefDescription": "Number of I-ERAT reloads." + }, + { + "EventCode": "0x100F8", + "EventName": "PM_NO_INST_AVAIL", + "BriefDescription": "Number of cycles the ICT has no itags assigned to this thread." + }, + { + "EventCode": "0x100fc", + "EventName": "PM_LD_CMPL", + "BriefDescription": "Load instruction completed." + }, + { + "EventCode": "0x200F0", + "EventName": "PM_ST_CMPL", + "BriefDescription": "Stores completed from S2Q (2nd-level store queue)." + }, + { + "EventCode": "0x200F2", + "EventName": "PM_INST_DISP", + "BriefDescription": "PowerPC instruction dispatched." + }, + { + "EventCode": "0x200F4", + "EventName": "PM_RUN_CYC", + "BriefDescription": "Processor cycles gated by the run latch." + }, + { + "EventCode": "0x200F6", + "EventName": "PM_L1_DTLB_RELOAD", + "BriefDescription": "DERAT Reloaded due to a DERAT miss." + }, + { + "EventCode": "0x200FA", + "EventName": "PM_BR_TAKEN_CMPL", + "BriefDescription": "Branch Taken instruction completed." + }, + { + "EventCode": "0x200FC", + "EventName": "PM_L1_ICACHE_MISS", + "BriefDescription": "Demand instruction cache miss." + }, + { + "EventCode": "0x200FE", + "EventName": "PM_L1_RELOAD_FROM_MEM", + "BriefDescription": "L1 Dcache reload from memory" + }, + { + "EventCode": "0x300F0", + "EventName": "PM_ST_MISS_L1", + "BriefDescription": "Store Missed L1" + }, + { + "EventCode": "0x300FC", + "EventName": "PM_DTLB_MISS", + "BriefDescription": "Data PTEG reload" + }, + { + "EventCode": "0x300FE", + "EventName": "PM_DATA_FROM_L3MISS", + "BriefDescription": "Demand LD - L3 Miss (not L2 hit and not L3 hit)" + }, + { + "EventCode": "0x400F0", + "EventName": "PM_LD_MISS_L1", + "BriefDescription": "L1 Dcache load miss" + }, + { + "EventCode": "0x400F2", + "EventName": "PM_CYC_INST_DISP", + "BriefDescription": "Cycle when instruction(s) dispatched." + }, + { + "EventCode": "0x400F6", + "EventName": "PM_BR_MPRED_CMPL", + "BriefDescription": "A mispredicted branch completed. Includes direction and target." + }, + { + "EventCode": "0x400FA", + "EventName": "PM_RUN_INST_CMPL", + "BriefDescription": "PowerPC instruction completed while the run latch is set." + }, + { + "EventCode": "0x400FC", + "EventName": "PM_ITLB_MISS", + "BriefDescription": "Instruction TLB reload (after a miss), all page sizes. Includes only demand misses." + }, + { + "EventCode": "0x400fe", + "EventName": "PM_LD_NOT_CACHED", + "BriefDescription": "Load data not cached." + }, + { + "EventCode": "0x500fa", + "EventName": "PM_INST_CMPL", + "BriefDescription": "Instructions." + } +] diff --git a/tools/perf/pmu-events/arch/powerpc/mapfile.csv b/tools/perf/pmu-events/arch/powerpc/mapfile.csv index 4d5e9138d4cc2..cbd3cb443784f 100644 --- a/tools/perf/pmu-events/arch/powerpc/mapfile.csv +++ b/tools/perf/pmu-events/arch/powerpc/mapfile.csv @@ -16,3 +16,4 @@ 0x004e[[:xdigit:]]{4},1,power9,core 0x0080[[:xdigit:]]{4},1,power10,core 0x0082[[:xdigit:]]{4},1,power10,core +0x00ffffff,1,compat,core diff --git a/tools/perf/pmu-events/arch/x86/amdzen5/data-fabric.json b/tools/perf/pmu-events/arch/x86/amdzen5/data-fabric.json new file mode 100644 index 0000000000000..fa06569d881d1 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/amdzen5/data-fabric.json @@ -0,0 +1,1634 @@ +[ + { + "EventName": "local_or_remote_socket_read_data_beats_dram_0", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 0.", + "EventCode": "0x1f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_1", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 1.", + "EventCode": "0x5f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_2", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 2.", + "EventCode": "0x9f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_3", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 3.", + "EventCode": "0xdf", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_4", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 4.", + "EventCode": "0x11f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_5", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 5.", + "EventCode": "0x15f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_6", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 6.", + "EventCode": "0x19f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_7", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 7.", + "EventCode": "0x1df", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_8", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 8.", + "EventCode": "0x21f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_9", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 9.", + "EventCode": "0x25f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_10", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 10.", + "EventCode": "0x29f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_read_data_beats_dram_11", + "PublicDescription": "Read data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 11.", + "EventCode": "0x2df", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_0", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 0.", + "EventCode": "0x1f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_1", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 1.", + "EventCode": "0x5f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_2", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 2.", + "EventCode": "0x9f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_3", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 3.", + "EventCode": "0xdf", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_4", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 4.", + "EventCode": "0x11f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_5", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 5.", + "EventCode": "0x15f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_6", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 6.", + "EventCode": "0x19f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_7", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 7.", + "EventCode": "0x1df", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_8", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 8.", + "EventCode": "0x21f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_9", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 9.", + "EventCode": "0x25f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_10", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 10.", + "EventCode": "0x29f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_write_data_beats_dram_11", + "PublicDescription": "Write data beats (64 bytes) for transactions between local socket and DRAM Channel 11.", + "EventCode": "0x2df", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_0", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 0.", + "EventCode": "0x1f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_1", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 1.", + "EventCode": "0x5f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_2", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 2.", + "EventCode": "0x9f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_3", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 3.", + "EventCode": "0xdf", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_4", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 4.", + "EventCode": "0x11f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_5", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 5.", + "EventCode": "0x15f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_6", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 6.", + "EventCode": "0x19f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_7", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 7.", + "EventCode": "0x1df", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_8", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 8.", + "EventCode": "0x21f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_9", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 9.", + "EventCode": "0x25f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_10", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 10.", + "EventCode": "0x29f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_write_data_beats_dram_11", + "PublicDescription": "Write data beats (64 bytes) for transactions between remote socket and DRAM Channel 11.", + "EventCode": "0x2df", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_0", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 0.", + "EventCode": "0x1f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_1", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 1.", + "EventCode": "0x5f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_2", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 2.", + "EventCode": "0x9f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_3", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 3.", + "EventCode": "0xdf", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_4", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 4.", + "EventCode": "0x11f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_5", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 5.", + "EventCode": "0x15f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_6", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 6.", + "EventCode": "0x19f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_7", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 7.", + "EventCode": "0x1df", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_8", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 8.", + "EventCode": "0x21f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_9", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 9.", + "EventCode": "0x25f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_10", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 10.", + "EventCode": "0x29f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_write_data_beats_dram_11", + "PublicDescription": "Write data beats (64 bytes) for transactions between local or remote socket and DRAM Channel 11.", + "EventCode": "0x2df", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_0", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 0.", + "EventCode": "0x81f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_1", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 1.", + "EventCode": "0x85f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_2", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 2.", + "EventCode": "0x89f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_3", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 3.", + "EventCode": "0x8df", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_4", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 4.", + "EventCode": "0x91f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_5", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 5.", + "EventCode": "0x95f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_6", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 6.", + "EventCode": "0x99f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_read_data_beats_io_7", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local socket and IO Root Complex 7.", + "EventCode": "0x9df", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_0", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 0.", + "EventCode": "0x81f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_1", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 1.", + "EventCode": "0x85f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_2", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 2.", + "EventCode": "0x89f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_3", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 3.", + "EventCode": "0x8df", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_4", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 4.", + "EventCode": "0x91f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_5", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 5.", + "EventCode": "0x95f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_6", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 6.", + "EventCode": "0x99f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_upstream_write_data_beats_io_7", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local socket and IO Root Complex 7.", + "EventCode": "0x9df", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_0", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 0.", + "EventCode": "0x81f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_1", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 1.", + "EventCode": "0x85f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_2", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 2.", + "EventCode": "0x89f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_3", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 3.", + "EventCode": "0x8df", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_4", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 4.", + "EventCode": "0x91f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_5", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 5.", + "EventCode": "0x95f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_6", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 6.", + "EventCode": "0x99f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_read_data_beats_io_7", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between remote socket and IO Root Complex 7.", + "EventCode": "0x9df", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_0", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 0.", + "EventCode": "0x81f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_1", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 1.", + "EventCode": "0x85f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_2", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 2.", + "EventCode": "0x89f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_3", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 3.", + "EventCode": "0x8df", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_4", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 4.", + "EventCode": "0x91f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_5", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 5.", + "EventCode": "0x95f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_6", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 6.", + "EventCode": "0x99f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_upstream_write_data_beats_io_7", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between remote socket and IO Root Complex 7.", + "EventCode": "0x9df", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_0", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 0.", + "EventCode": "0x81f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_1", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 1.", + "EventCode": "0x85f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_2", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 2.", + "EventCode": "0x89f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_3", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 3.", + "EventCode": "0x8df", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_4", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 4.", + "EventCode": "0x91f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_5", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 5.", + "EventCode": "0x95f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_6", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 6.", + "EventCode": "0x99f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_read_data_beats_io_7", + "PublicDescription": "Upstream DMA read data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 7.", + "EventCode": "0x9df", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_0", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 0.", + "EventCode": "0x81f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_1", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 1.", + "EventCode": "0x85f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_2", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 2.", + "EventCode": "0x89f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_3", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 3.", + "EventCode": "0x8df", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_4", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 4.", + "EventCode": "0x91f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_5", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 5.", + "EventCode": "0x95f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_6", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 6.", + "EventCode": "0x99f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_upstream_write_data_beats_io_7", + "PublicDescription": "Upstream DMA write data beats (64 bytes) for transactions between local or remote socket and IO Root Complex 7.", + "EventCode": "0x9df", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_0", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 0.", + "EventCode": "0x41e", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_1", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 1.", + "EventCode": "0x45e", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_2", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 2.", + "EventCode": "0x49e", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_3", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 3.", + "EventCode": "0x4de", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_4", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 4.", + "EventCode": "0x51e", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_5", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 5.", + "EventCode": "0x55e", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_6", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 6.", + "EventCode": "0x59e", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_7", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 7.", + "EventCode": "0x5de", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_8", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 8.", + "EventCode": "0x41f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_9", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 9.", + "EventCode": "0x45f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_10", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 10.", + "EventCode": "0x49f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_11", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 11.", + "EventCode": "0x4df", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_12", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 12.", + "EventCode": "0x51f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_13", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 13.", + "EventCode": "0x55f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_14", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 14.", + "EventCode": "0x59f", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_cfi_15", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local socket and Core-to-Fabric Interface 15.", + "EventCode": "0x5df", + "UMask": "0x7fe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_0", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 0.", + "EventCode": "0x41e", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_1", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 1.", + "EventCode": "0x45e", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_2", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 2.", + "EventCode": "0x49e", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_3", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 3.", + "EventCode": "0x4de", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_4", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 4.", + "EventCode": "0x51e", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_5", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 5.", + "EventCode": "0x55e", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_6", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 6.", + "EventCode": "0x59e", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_7", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 7.", + "EventCode": "0x5de", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_8", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 8.", + "EventCode": "0x41f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_9", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 9.", + "EventCode": "0x45f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_10", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 10.", + "EventCode": "0x49f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_11", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 11.", + "EventCode": "0x4df", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_12", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 12.", + "EventCode": "0x51f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_13", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 13.", + "EventCode": "0x55f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_14", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 14.", + "EventCode": "0x59f", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_cfi_15", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and Core-to-Fabric Interface 15.", + "EventCode": "0x5df", + "UMask": "0x7ff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_0", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 0.", + "EventCode": "0x41e", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_1", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 1.", + "EventCode": "0x45e", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_2", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 2.", + "EventCode": "0x49e", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_3", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 3.", + "EventCode": "0x4de", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_4", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 4.", + "EventCode": "0x51e", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_5", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 5.", + "EventCode": "0x55e", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_6", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 6.", + "EventCode": "0x59e", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_7", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 7.", + "EventCode": "0x5de", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_8", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 8.", + "EventCode": "0x41f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_9", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 9.", + "EventCode": "0x45f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_10", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 10.", + "EventCode": "0x49f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_11", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 11.", + "EventCode": "0x4df", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_12", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 12.", + "EventCode": "0x51f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_13", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 13.", + "EventCode": "0x55f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_14", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 14.", + "EventCode": "0x59f", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_inbound_data_beats_cfi_15", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between remote socket and Core-to-Fabric Interface 15.", + "EventCode": "0x5df", + "UMask": "0xbfe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_0", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 0.", + "EventCode": "0x41e", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_1", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 1.", + "EventCode": "0x45e", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_2", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 2.", + "EventCode": "0x49e", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_3", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 3.", + "EventCode": "0x4de", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_4", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 4.", + "EventCode": "0x51e", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_5", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 5.", + "EventCode": "0x55e", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_6", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 6.", + "EventCode": "0x59e", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_7", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 7.", + "EventCode": "0x5de", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_8", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 8.", + "EventCode": "0x41f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_9", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 9.", + "EventCode": "0x45f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_10", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 10.", + "EventCode": "0x49f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_11", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 11.", + "EventCode": "0x4df", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_12", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 12.", + "EventCode": "0x51f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_13", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 13.", + "EventCode": "0x55f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_14", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 14.", + "EventCode": "0x59f", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "remote_socket_outbound_data_beats_cfi_15", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between remote socket and Core-to-Fabric Interface 15.", + "EventCode": "0x5df", + "UMask": "0xbff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_0", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 0.", + "EventCode": "0x41e", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_1", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 1.", + "EventCode": "0x45e", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_2", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 2.", + "EventCode": "0x49e", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_3", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 3.", + "EventCode": "0x4de", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_4", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 4.", + "EventCode": "0x51e", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_5", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 5.", + "EventCode": "0x55e", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_6", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 6.", + "EventCode": "0x59e", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_7", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 7.", + "EventCode": "0x5de", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_8", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 8.", + "EventCode": "0x41f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_9", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 9.", + "EventCode": "0x45f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_10", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 10.", + "EventCode": "0x49f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_11", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 11.", + "EventCode": "0x4df", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_12", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 12.", + "EventCode": "0x51f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_13", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 13.", + "EventCode": "0x55f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_14", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 14.", + "EventCode": "0x59f", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_inbound_data_beats_cfi_15", + "PublicDescription": "Inbound data beats (32 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 15.", + "EventCode": "0x5df", + "UMask": "0xffe", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_0", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 0.", + "EventCode": "0x41e", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_1", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 1.", + "EventCode": "0x45e", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_2", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 2.", + "EventCode": "0x49e", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_3", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 3.", + "EventCode": "0x4de", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_4", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 4.", + "EventCode": "0x51e", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_5", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 5.", + "EventCode": "0x55e", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_6", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 6.", + "EventCode": "0x59e", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_7", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 7.", + "EventCode": "0x5de", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_8", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 8.", + "EventCode": "0x41f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_9", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 9.", + "EventCode": "0x45f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_10", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 10.", + "EventCode": "0x49f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_11", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 11.", + "EventCode": "0x4df", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_12", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 12.", + "EventCode": "0x51f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_13", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 13.", + "EventCode": "0x55f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_14", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 14.", + "EventCode": "0x59f", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_or_remote_socket_outbound_data_beats_cfi_15", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local or remote socket and Core-to-Fabric Interface 15.", + "EventCode": "0x5df", + "UMask": "0xfff", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_link_0", + "PublicDescription": "Inbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 0.", + "EventCode": "0xd5f", + "UMask": "0xf3f", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_link_1", + "PublicDescription": "Inbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 1.", + "EventCode": "0xd9f", + "UMask": "0xf3f", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_link_2", + "PublicDescription": "Inbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 2.", + "EventCode": "0xddf", + "UMask": "0xf3f", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_link_3", + "PublicDescription": "Inbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 3.", + "EventCode": "0xe1f", + "UMask": "0xf3f", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_link_4", + "PublicDescription": "Inbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 4.", + "EventCode": "0xe5f", + "UMask": "0xf3f", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_inbound_data_beats_link_5", + "PublicDescription": "Inbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 5.", + "EventCode": "0xe9f", + "UMask": "0xf3f", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_link_0", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 0.", + "EventCode": "0xd5f", + "UMask": "0xf3e", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_link_1", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 1.", + "EventCode": "0xd9f", + "UMask": "0xf3e", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_link_2", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 2.", + "EventCode": "0xddf", + "UMask": "0xf3e", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_link_3", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 3.", + "EventCode": "0xe1f", + "UMask": "0xf3e", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_link_4", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 4.", + "EventCode": "0xe5f", + "UMask": "0xf3e", + "PerPkg": "1", + "Unit": "DFPMC" + }, + { + "EventName": "local_socket_outbound_data_beats_link_5", + "PublicDescription": "Outbound data beats (64 bytes) for transactions between local socket and remote socket over Cross-socket Link 5.", + "EventCode": "0xe9f", + "UMask": "0xf3e", + "PerPkg": "1", + "Unit": "DFPMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/amdzen5/load-store.json b/tools/perf/pmu-events/arch/x86/amdzen5/load-store.json index af2fdf1f55d64..ff6627a778057 100644 --- a/tools/perf/pmu-events/arch/x86/amdzen5/load-store.json +++ b/tools/perf/pmu-events/arch/x86/amdzen5/load-store.json @@ -96,6 +96,12 @@ "BriefDescription": "Demand data cache fills from L3 cache or different L2 cache in the same CCX.", "UMask": "0x02" }, + { + "EventName": "ls_dmnd_fills_from_sys.local_all", + "EventCode": "0x43", + "BriefDescription": "Demand data cache fills from local L2 cache, L3 cache or different L2 cache in the same CCX.", + "UMask": "0x03" + }, { "EventName": "ls_dmnd_fills_from_sys.near_cache", "EventCode": "0x43", @@ -114,12 +120,30 @@ "BriefDescription": "Demand data cache fills from cache of another CCX when the address was in a different NUMA node.", "UMask": "0x10" }, + { + "EventName": "ls_dmnd_fills_from_sys.remote_cache", + "EventCode": "0x43", + "BriefDescription": "Demand data cache fills from cache of another CCX when the address was in the same or a different NUMA node.", + "UMask": "0x14" + }, { "EventName": "ls_dmnd_fills_from_sys.dram_io_far", "EventCode": "0x43", "BriefDescription": "Demand data cache fills from either DRAM or MMIO in a different NUMA node (same or different socket).", "UMask": "0x40" }, + { + "EventName": "ls_dmnd_fills_from_sys.dram_io_all", + "EventCode": "0x43", + "BriefDescription": "Demand data cache fills from either DRAM or MMIO in the same or a different NUMA node (same or different socket).", + "UMask": "0x48" + }, + { + "EventName": "ls_dmnd_fills_from_sys.far_all", + "EventCode": "0x43", + "BriefDescription": "Demand data cache fills from either cache of another CCX, DRAM or MMIO when the address was in a different NUMA node (same or different socket).", + "UMask": "0x50" + }, { "EventName": "ls_dmnd_fills_from_sys.alternate_memories", "EventCode": "0x43", @@ -192,12 +216,6 @@ "BriefDescription": "Any data cache fills from either cache of another CCX, DRAM or MMIO when the address was in a different NUMA node (same or different socket).", "UMask": "0x50" }, - { - "EventName": "ls_any_fills_from_sys.all_dram_io", - "EventCode": "0x44", - "BriefDescription": "Any data cache fills from either DRAM or MMIO in any NUMA node (same or different socket).", - "UMask": "0x48" - }, { "EventName": "ls_any_fills_from_sys.alternate_memories", "EventCode": "0x44", @@ -342,6 +360,12 @@ "BriefDescription": "Software prefetch data cache fills from L3 cache or different L2 cache in the same CCX.", "UMask": "0x02" }, + { + "EventName": "ls_sw_pf_dc_fills.local_all", + "EventCode": "0x59", + "BriefDescription": "Software prefetch data cache fills from local L2 cache, L3 cache or different L2 cache in the same CCX.", + "UMask": "0x03" + }, { "EventName": "ls_sw_pf_dc_fills.near_cache", "EventCode": "0x59", @@ -360,12 +384,30 @@ "BriefDescription": "Software prefetch data cache fills from cache of another CCX in a different NUMA node.", "UMask": "0x10" }, + { + "EventName": "ls_sw_pf_dc_fills.remote_cache", + "EventCode": "0x59", + "BriefDescription": "Software prefetch data cache fills from cache of another CCX when the address was in the same or a different NUMA node.", + "UMask": "0x14" + }, { "EventName": "ls_sw_pf_dc_fills.dram_io_far", "EventCode": "0x59", "BriefDescription": "Software prefetch data cache fills from either DRAM or MMIO in a different NUMA node (same or different socket).", "UMask": "0x40" }, + { + "EventName": "ls_sw_pf_dc_fills.dram_io_all", + "EventCode": "0x59", + "BriefDescription": "Software prefetch data cache fills from either DRAM or MMIO in the same or a different NUMA node (same or different socket).", + "UMask": "0x48" + }, + { + "EventName": "ls_sw_pf_dc_fills.far_all", + "EventCode": "0x59", + "BriefDescription": "Software prefetch data cache fills from either cache of another CCX, DRAM or MMIO when the address was in a different NUMA node (same or different socket).", + "UMask": "0x50" + }, { "EventName": "ls_sw_pf_dc_fills.alternate_memories", "EventCode": "0x59", @@ -390,6 +432,12 @@ "BriefDescription": "Hardware prefetch data cache fills from L3 cache or different L2 cache in the same CCX.", "UMask": "0x02" }, + { + "EventName": "ls_hw_pf_dc_fills.local_all", + "EventCode": "0x5a", + "BriefDescription": "Hardware prefetch data cache fills from local L2 cache, L3 cache or different L2 cache in the same CCX.", + "UMask": "0x03" + }, { "EventName": "ls_hw_pf_dc_fills.near_cache", "EventCode": "0x5a", @@ -408,12 +456,30 @@ "BriefDescription": "Hardware prefetch data cache fills from cache of another CCX when the address was in a different NUMA node.", "UMask": "0x10" }, + { + "EventName": "ls_hw_pf_dc_fills.remote_cache", + "EventCode": "0x5a", + "BriefDescription": "Hardware prefetch data cache fills from cache of another CCX when the address was in the same or a different NUMA node.", + "UMask": "0x14" + }, { "EventName": "ls_hw_pf_dc_fills.dram_io_far", "EventCode": "0x5a", "BriefDescription": "Hardware prefetch data cache fills from either DRAM or MMIO in a different NUMA node (same or different socket).", "UMask": "0x40" }, + { + "EventName": "ls_hw_pf_dc_fills.dram_io_all", + "EventCode": "0x5a", + "BriefDescription": "Hardware prefetch data cache fills from either DRAM or MMIO in the same or a different NUMA node (same or different socket).", + "UMask": "0x48" + }, + { + "EventName": "ls_hw_pf_dc_fills.far_all", + "EventCode": "0x5a", + "BriefDescription": "Hardware prefetch data cache fills from either cache of another CCX, DRAM or MMIO when the address was in a different NUMA node (same or different socket).", + "UMask": "0x50" + }, { "EventName": "ls_hw_pf_dc_fills.alternate_memories", "EventCode": "0x5a", diff --git a/tools/perf/pmu-events/arch/x86/amdzen5/recommended.json b/tools/perf/pmu-events/arch/x86/amdzen5/recommended.json index c97874039c1e7..635d57e3bc151 100644 --- a/tools/perf/pmu-events/arch/x86/amdzen5/recommended.json +++ b/tools/perf/pmu-events/arch/x86/amdzen5/recommended.json @@ -341,5 +341,117 @@ "MetricGroup": "memory_controller", "PerPkg": "1", "ScaleUnit": "1per_memclk" + }, + { + "MetricName": "dram_read_bandwidth_for_local_or_remote_socket", + "BriefDescription": "DRAM read data bandwidth for accesses in local or remote socket.", + "MetricExpr": "(local_or_remote_socket_read_data_beats_dram_0 + local_or_remote_socket_read_data_beats_dram_1 + local_or_remote_socket_read_data_beats_dram_2 + local_or_remote_socket_read_data_beats_dram_3 + local_or_remote_socket_read_data_beats_dram_4 + local_or_remote_socket_read_data_beats_dram_5 + local_or_remote_socket_read_data_beats_dram_6 + local_or_remote_socket_read_data_beats_dram_7 + local_or_remote_socket_read_data_beats_dram_8 + local_or_remote_socket_read_data_beats_dram_9 + local_or_remote_socket_read_data_beats_dram_10 + local_or_remote_socket_read_data_beats_dram_11) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "dram_write_bandwidth_for_local_socket", + "BriefDescription": "DRAM write data bandwidth for accesses in local socket.", + "MetricExpr": "(local_socket_write_data_beats_dram_0 + local_socket_write_data_beats_dram_1 + local_socket_write_data_beats_dram_2 + local_socket_write_data_beats_dram_3 + local_socket_write_data_beats_dram_4 + local_socket_write_data_beats_dram_5 + local_socket_write_data_beats_dram_6 + local_socket_write_data_beats_dram_7 + local_socket_write_data_beats_dram_8 + local_socket_write_data_beats_dram_9 + local_socket_write_data_beats_dram_10 + local_socket_write_data_beats_dram_11) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "dram_write_bandwidth_for_remote_socket", + "BriefDescription": "DRAM write data bandwidth for accesses in remote socket.", + "MetricExpr": "(remote_socket_write_data_beats_dram_0 + remote_socket_write_data_beats_dram_1 + remote_socket_write_data_beats_dram_2 + remote_socket_write_data_beats_dram_3 + remote_socket_write_data_beats_dram_4 + remote_socket_write_data_beats_dram_5 + remote_socket_write_data_beats_dram_6 + remote_socket_write_data_beats_dram_7 + remote_socket_write_data_beats_dram_8 + remote_socket_write_data_beats_dram_9 + remote_socket_write_data_beats_dram_10 + remote_socket_write_data_beats_dram_11) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "dram_write_bandwidth_for_local_or_remote_socket", + "BriefDescription": "DRAM write data bandwidth for accesses in local or remote socket.", + "MetricExpr": "(local_or_remote_socket_write_data_beats_dram_0 + local_or_remote_socket_write_data_beats_dram_1 + local_or_remote_socket_write_data_beats_dram_2 + local_or_remote_socket_write_data_beats_dram_3 + local_or_remote_socket_write_data_beats_dram_4 + local_or_remote_socket_write_data_beats_dram_5 + local_or_remote_socket_write_data_beats_dram_6 + local_or_remote_socket_write_data_beats_dram_7 + local_or_remote_socket_write_data_beats_dram_8 + local_or_remote_socket_write_data_beats_dram_9 + local_or_remote_socket_write_data_beats_dram_10 + local_or_remote_socket_write_data_beats_dram_11) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "upstream_dma_read_bandwidth_for_local_socket", + "BriefDescription": "Upstream DMA read data bandwidth for accesses in local socket.", + "MetricExpr": "(local_socket_upstream_read_data_beats_io_0 + local_socket_upstream_read_data_beats_io_1 + local_socket_upstream_read_data_beats_io_2 + local_socket_upstream_read_data_beats_io_3 + local_socket_upstream_read_data_beats_io_4 + local_socket_upstream_read_data_beats_io_5 + local_socket_upstream_read_data_beats_io_6 + local_socket_upstream_read_data_beats_io_7) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "upstream_dma_write_bandwidth_for_local_socket", + "BriefDescription": "Upstream DMA write data bandwidth for accesses in local socket.", + "MetricExpr": "(local_socket_upstream_write_data_beats_io_0 + local_socket_upstream_write_data_beats_io_1 + local_socket_upstream_write_data_beats_io_2 + local_socket_upstream_write_data_beats_io_3 + local_socket_upstream_write_data_beats_io_4 + local_socket_upstream_write_data_beats_io_5 + local_socket_upstream_write_data_beats_io_6 + local_socket_upstream_write_data_beats_io_7) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "upstream_dma_read_bandwidth_for_remote_socket", + "BriefDescription": "Upstream DMA read data bandwidth for accesses in remote socket.", + "MetricExpr": "(remote_socket_upstream_read_data_beats_io_0 + remote_socket_upstream_read_data_beats_io_1 + remote_socket_upstream_read_data_beats_io_2 + remote_socket_upstream_read_data_beats_io_3 + remote_socket_upstream_read_data_beats_io_4 + remote_socket_upstream_read_data_beats_io_5 + remote_socket_upstream_read_data_beats_io_6 + remote_socket_upstream_read_data_beats_io_7) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "upstream_dma_write_bandwidth_for_remote_socket", + "BriefDescription": "Upstream DMA write data bandwidth for accesses in remote socket.", + "MetricExpr": "(remote_socket_upstream_write_data_beats_io_0 + remote_socket_upstream_write_data_beats_io_1 + remote_socket_upstream_write_data_beats_io_2 + remote_socket_upstream_write_data_beats_io_3 + remote_socket_upstream_write_data_beats_io_4 + remote_socket_upstream_write_data_beats_io_5 + remote_socket_upstream_write_data_beats_io_6 + remote_socket_upstream_write_data_beats_io_7) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "core_inbound_data_bandwidth_for_local_socket", + "BriefDescription": "Core inbound data bandwidth for accesses in local socket.", + "MetricExpr": "(local_socket_inbound_data_beats_cfi_0 + local_socket_inbound_data_beats_cfi_1 + local_socket_inbound_data_beats_cfi_2 + local_socket_inbound_data_beats_cfi_3 + local_socket_inbound_data_beats_cfi_4 + local_socket_inbound_data_beats_cfi_5 + local_socket_inbound_data_beats_cfi_6 + local_socket_inbound_data_beats_cfi_7 + local_socket_inbound_data_beats_cfi_8 + local_socket_inbound_data_beats_cfi_9 + local_socket_inbound_data_beats_cfi_10 + local_socket_inbound_data_beats_cfi_11 + local_socket_inbound_data_beats_cfi_12 + local_socket_inbound_data_beats_cfi_13 + local_socket_inbound_data_beats_cfi_14 + local_socket_inbound_data_beats_cfi_15) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "3.2e-5MB/s" + }, + { + "MetricName": "core_outbound_data_bandwidth_for_local_socket", + "BriefDescription": "Core outbound data bandwidth for accesses in local socket.", + "MetricExpr": "(local_socket_outbound_data_beats_cfi_0 + local_socket_outbound_data_beats_cfi_1 + local_socket_outbound_data_beats_cfi_2 + local_socket_outbound_data_beats_cfi_3 + local_socket_outbound_data_beats_cfi_4 + local_socket_outbound_data_beats_cfi_5 + local_socket_outbound_data_beats_cfi_6 + local_socket_outbound_data_beats_cfi_7 + local_socket_outbound_data_beats_cfi_8 + local_socket_outbound_data_beats_cfi_9 + local_socket_outbound_data_beats_cfi_10 + local_socket_outbound_data_beats_cfi_11 + local_socket_outbound_data_beats_cfi_12 + local_socket_outbound_data_beats_cfi_13 + local_socket_outbound_data_beats_cfi_14 + local_socket_outbound_data_beats_cfi_15) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "core_inbound_data_bandwidth_for_remote_socket", + "BriefDescription": "Core inbound data bandwidth for accesses in remote socket.", + "MetricExpr": "(remote_socket_inbound_data_beats_cfi_0 + remote_socket_inbound_data_beats_cfi_1 + remote_socket_inbound_data_beats_cfi_2 + remote_socket_inbound_data_beats_cfi_3 + remote_socket_inbound_data_beats_cfi_4 + remote_socket_inbound_data_beats_cfi_5 + remote_socket_inbound_data_beats_cfi_6 + remote_socket_inbound_data_beats_cfi_7 + remote_socket_inbound_data_beats_cfi_8 + remote_socket_inbound_data_beats_cfi_9 + remote_socket_inbound_data_beats_cfi_10 + remote_socket_inbound_data_beats_cfi_11 + remote_socket_inbound_data_beats_cfi_12 + remote_socket_inbound_data_beats_cfi_13 + remote_socket_inbound_data_beats_cfi_14 + remote_socket_inbound_data_beats_cfi_15) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "3.2e-5MB/s" + }, + { + "MetricName": "core_outbound_data_bandwidth_for_remote_socket", + "BriefDescription": "Core outbound data bandwidth for accesses in remote socket.", + "MetricExpr": "(remote_socket_outbound_data_beats_cfi_0 + remote_socket_outbound_data_beats_cfi_1 + remote_socket_outbound_data_beats_cfi_2 + remote_socket_outbound_data_beats_cfi_3 + remote_socket_outbound_data_beats_cfi_4 + remote_socket_outbound_data_beats_cfi_5 + remote_socket_outbound_data_beats_cfi_6 + remote_socket_outbound_data_beats_cfi_7 + remote_socket_outbound_data_beats_cfi_8 + remote_socket_outbound_data_beats_cfi_9 + remote_socket_outbound_data_beats_cfi_10 + remote_socket_outbound_data_beats_cfi_11 + remote_socket_outbound_data_beats_cfi_12 + remote_socket_outbound_data_beats_cfi_13 + remote_socket_outbound_data_beats_cfi_14 + remote_socket_outbound_data_beats_cfi_15) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "cross_socket_inbound_data_bandwidth_for_local_socket", + "BriefDescription": "Inbound data bandwidth for accesses between local socket and remote socket.", + "MetricExpr": "(local_socket_inbound_data_beats_link_0 + local_socket_inbound_data_beats_link_1 + local_socket_inbound_data_beats_link_2 + local_socket_inbound_data_beats_link_3 + local_socket_inbound_data_beats_link_4 + local_socket_inbound_data_beats_link_5) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" + }, + { + "MetricName": "cross_socket_outbound_data_bandwidth_for_local_socket", + "BriefDescription": "Outbound data bandwidth for accesses between local socket and remote socket.", + "MetricExpr": "(local_socket_outbound_data_beats_link_0 + local_socket_outbound_data_beats_link_1 + local_socket_outbound_data_beats_link_2 + local_socket_outbound_data_beats_link_3 + local_socket_outbound_data_beats_link_4 + local_socket_outbound_data_beats_link_5) / duration_time", + "MetricGroup": "data_fabric", + "PerPkg": "1", + "ScaleUnit": "6.4e-5MB/s" } ] diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index c592079982fbd..1c7a2cfa321fc 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -19,72 +19,109 @@ struct pmu_table_entry { }; static const char *const big_c_string = -/* offset=0 */ "default_core\000" -/* offset=13 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000" -/* offset=72 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000" -/* offset=131 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000" -/* offset=226 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000" -/* offset=325 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000" -/* offset=455 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000" -/* offset=570 */ "hisi_sccl,ddrc\000" -/* offset=585 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000" -/* offset=671 */ "uncore_cbox\000" -/* offset=683 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000" -/* offset=914 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000" -/* offset=979 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000" -/* offset=1050 */ "hisi_sccl,l3c\000" -/* offset=1064 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000" -/* offset=1144 */ "uncore_imc_free_running\000" -/* offset=1168 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000" -/* offset=1263 */ "uncore_imc\000" -/* offset=1274 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000" -/* offset=1352 */ "uncore_sys_ddr_pmu\000" -/* offset=1371 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000" -/* offset=1444 */ "uncore_sys_ccn_pmu\000" -/* offset=1463 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000" -/* offset=1537 */ "uncore_sys_cmn_pmu\000" -/* offset=1556 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000" -/* offset=1696 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000" -/* offset=1718 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000" -/* offset=1781 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000" -/* offset=1947 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" -/* offset=2011 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" -/* offset=2078 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000" -/* offset=2149 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000" -/* offset=2243 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000" -/* offset=2377 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000" -/* offset=2441 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000" -/* offset=2509 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000" -/* offset=2579 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000" -/* offset=2601 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000" -/* offset=2623 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000" -/* offset=2643 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000" +/* offset=0 */ "tool\000" +/* offset=5 */ "duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000" +/* offset=78 */ "user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000" +/* offset=145 */ "system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000" +/* offset=210 */ "has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000" +/* offset=283 */ "num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000" +/* offset=425 */ "num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000" +/* offset=525 */ "num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000" +/* offset=639 */ "num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000" +/* offset=712 */ "num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000" +/* offset=795 */ "slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000" +/* offset=902 */ "smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000" +/* offset=1006 */ "system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000" +/* offset=1102 */ "default_core\000" +/* offset=1115 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000" +/* offset=1174 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000" +/* offset=1233 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000" +/* offset=1328 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000" +/* offset=1427 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000" +/* offset=1557 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000" +/* offset=1672 */ "hisi_sccl,ddrc\000" +/* offset=1687 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000" +/* offset=1773 */ "uncore_cbox\000" +/* offset=1785 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000" +/* offset=2016 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000" +/* offset=2081 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000" +/* offset=2152 */ "hisi_sccl,l3c\000" +/* offset=2166 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000" +/* offset=2246 */ "uncore_imc_free_running\000" +/* offset=2270 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000" +/* offset=2365 */ "uncore_imc\000" +/* offset=2376 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000" +/* offset=2454 */ "uncore_sys_ddr_pmu\000" +/* offset=2473 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000" +/* offset=2546 */ "uncore_sys_ccn_pmu\000" +/* offset=2565 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000" +/* offset=2639 */ "uncore_sys_cmn_pmu\000" +/* offset=2658 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000" +/* offset=2798 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000" +/* offset=2820 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000" +/* offset=2883 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000" +/* offset=3049 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" +/* offset=3113 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" +/* offset=3180 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000" +/* offset=3251 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000" +/* offset=3345 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000" +/* offset=3479 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000" +/* offset=3543 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000" +/* offset=3611 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000" +/* offset=3681 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000" +/* offset=3703 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000" +/* offset=3725 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000" +/* offset=3745 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000" ; +static const struct compact_pmu_event pmu_events__common_tool[] = { +{ 5 }, /* duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000 */ +{ 210 }, /* has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000 */ +{ 283 }, /* num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000 */ +{ 425 }, /* num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000 */ +{ 525 }, /* num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000 */ +{ 639 }, /* num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000 */ +{ 712 }, /* num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000 */ +{ 795 }, /* slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000 */ +{ 902 }, /* smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000 */ +{ 145 }, /* system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000 */ +{ 1006 }, /* system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000 */ +{ 78 }, /* user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000 */ + +}; + +const struct pmu_table_entry pmu_events__common[] = { +{ + .entries = pmu_events__common_tool, + .num_entries = ARRAY_SIZE(pmu_events__common_tool), + .pmu_name = { 0 /* tool\000 */ }, +}, +}; + static const struct compact_pmu_event pmu_events__test_soc_cpu_default_core[] = { -{ 13 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000 */ -{ 72 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000 */ -{ 325 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000 */ -{ 455 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000 */ -{ 131 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000 */ -{ 226 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000 */ +{ 1115 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000 */ +{ 1174 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000 */ +{ 1427 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000 */ +{ 1557 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000 */ +{ 1233 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000 */ +{ 1328 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_ddrc[] = { -{ 585 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000 */ +{ 1687 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_l3c[] = { -{ 1064 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000 */ +{ 2166 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_cbox[] = { -{ 914 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000 */ -{ 979 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000 */ -{ 683 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000 */ +{ 2016 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000 */ +{ 2081 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000 */ +{ 1785 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc[] = { -{ 1274 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000 */ +{ 2376 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc_free_running[] = { -{ 1168 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000 */ +{ 2270 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000 */ }; @@ -92,51 +129,51 @@ const struct pmu_table_entry pmu_events__test_soc_cpu[] = { { .entries = pmu_events__test_soc_cpu_default_core, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_default_core), - .pmu_name = { 0 /* default_core\000 */ }, + .pmu_name = { 1102 /* default_core\000 */ }, }, { .entries = pmu_events__test_soc_cpu_hisi_sccl_ddrc, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_ddrc), - .pmu_name = { 570 /* hisi_sccl,ddrc\000 */ }, + .pmu_name = { 1672 /* hisi_sccl,ddrc\000 */ }, }, { .entries = pmu_events__test_soc_cpu_hisi_sccl_l3c, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_l3c), - .pmu_name = { 1050 /* hisi_sccl,l3c\000 */ }, + .pmu_name = { 2152 /* hisi_sccl,l3c\000 */ }, }, { .entries = pmu_events__test_soc_cpu_uncore_cbox, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_cbox), - .pmu_name = { 671 /* uncore_cbox\000 */ }, + .pmu_name = { 1773 /* uncore_cbox\000 */ }, }, { .entries = pmu_events__test_soc_cpu_uncore_imc, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc), - .pmu_name = { 1263 /* uncore_imc\000 */ }, + .pmu_name = { 2365 /* uncore_imc\000 */ }, }, { .entries = pmu_events__test_soc_cpu_uncore_imc_free_running, .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc_free_running), - .pmu_name = { 1144 /* uncore_imc_free_running\000 */ }, + .pmu_name = { 2246 /* uncore_imc_free_running\000 */ }, }, }; static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] = { -{ 1696 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */ -{ 2377 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */ -{ 2149 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000 */ -{ 2243 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000 */ -{ 2441 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ -{ 2509 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ -{ 1781 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000 */ -{ 1718 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */ -{ 2643 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */ -{ 2579 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */ -{ 2601 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */ -{ 2623 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */ -{ 2078 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */ -{ 1947 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ -{ 2011 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ +{ 2798 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */ +{ 3479 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */ +{ 3251 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000 */ +{ 3345 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000 */ +{ 3543 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ +{ 3611 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ +{ 2883 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000 */ +{ 2820 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */ +{ 3745 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */ +{ 3681 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */ +{ 3703 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */ +{ 3725 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */ +{ 3180 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */ +{ 3049 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ +{ 3113 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ }; @@ -144,18 +181,18 @@ const struct pmu_table_entry pmu_metrics__test_soc_cpu[] = { { .entries = pmu_metrics__test_soc_cpu_default_core, .num_entries = ARRAY_SIZE(pmu_metrics__test_soc_cpu_default_core), - .pmu_name = { 0 /* default_core\000 */ }, + .pmu_name = { 1102 /* default_core\000 */ }, }, }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ccn_pmu[] = { -{ 1463 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000 */ +{ 2565 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_cmn_pmu[] = { -{ 1556 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000 */ +{ 2658 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000 */ }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ddr_pmu[] = { -{ 1371 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000 */ +{ 2473 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000 */ }; @@ -163,17 +200,17 @@ const struct pmu_table_entry pmu_events__test_soc_sys[] = { { .entries = pmu_events__test_soc_sys_uncore_sys_ccn_pmu, .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ccn_pmu), - .pmu_name = { 1444 /* uncore_sys_ccn_pmu\000 */ }, + .pmu_name = { 2546 /* uncore_sys_ccn_pmu\000 */ }, }, { .entries = pmu_events__test_soc_sys_uncore_sys_cmn_pmu, .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_cmn_pmu), - .pmu_name = { 1537 /* uncore_sys_cmn_pmu\000 */ }, + .pmu_name = { 2639 /* uncore_sys_cmn_pmu\000 */ }, }, { .entries = pmu_events__test_soc_sys_uncore_sys_ddr_pmu, .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ddr_pmu), - .pmu_name = { 1352 /* uncore_sys_ddr_pmu\000 */ }, + .pmu_name = { 2454 /* uncore_sys_ddr_pmu\000 */ }, }, }; @@ -210,6 +247,15 @@ struct pmu_events_map { * table of PMU events. */ const struct pmu_events_map pmu_events_map[] = { +{ + .arch = "common", + .cpuid = "common", + .event_table = { + .pmus = pmu_events__common, + .num_pmus = ARRAY_SIZE(pmu_events__common), + }, + .metric_table = {}, +}, { .arch = "testarch", .cpuid = "testcpu", @@ -380,7 +426,7 @@ int pmu_events_table__for_each_event(const struct pmu_events_table *table, continue; ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); - if (pmu || ret) + if (ret) return ret; } return 0; @@ -457,11 +503,11 @@ int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, return 0; } -static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) +static const struct pmu_events_map *map_for_cpu(struct perf_cpu cpu) { static struct { const struct pmu_events_map *map; - struct perf_pmu *pmu; + struct perf_cpu cpu; } last_result; static struct { const struct pmu_events_map *map; @@ -472,10 +518,10 @@ static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) char *cpuid = NULL; size_t i; - if (has_last_result && last_result.pmu == pmu) + if (has_last_result && last_result.cpu.cpu == cpu.cpu) return last_result.map; - cpuid = perf_pmu__getcpuid(pmu); + cpuid = get_cpuid_allow_env_override(cpu); /* * On some platforms which uses cpus map, cpuid can be NULL for @@ -506,12 +552,21 @@ static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) has_last_map_search = true; } out_update_last_result: - last_result.pmu = pmu; + last_result.cpu = cpu; last_result.map = map; has_last_result = true; return map; } +static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) +{ + struct perf_cpu cpu = {-1}; + + if (pmu) + cpu = perf_cpu_map__min(pmu->cpus); + return map_for_cpu(cpu); +} + const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) { const struct pmu_events_map *map = map_for_pmu(pmu); @@ -532,24 +587,12 @@ const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) return NULL; } -const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) +const struct pmu_metrics_table *pmu_metrics_table__find(void) { - const struct pmu_events_map *map = map_for_pmu(pmu); - - if (!map) - return NULL; + struct perf_cpu cpu = {-1}; + const struct pmu_events_map *map = map_for_cpu(cpu); - if (!pmu) - return &map->metric_table; - - for (size_t i = 0; i < map->metric_table.num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &map->metric_table.pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - - if (pmu__name_match(pmu, pmu_name)) - return &map->metric_table; - } - return NULL; + return map ? &map->metric_table : NULL; } const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index bb0a5d92df4a1..d781a377757a1 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -292,6 +292,7 @@ def unit_to_pmu(unit: str) -> Optional[str]: 'cpu_atom': 'cpu_atom', 'ali_drw': 'ali_drw', 'arm_cmn': 'arm_cmn', + 'tool': 'tool', } return table[unit] if unit in table else f'uncore_{unit.lower()}' @@ -721,6 +722,17 @@ def print_mapping_table(archs: Sequence[str]) -> None: \t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), \t} }, +""") + elif arch == 'common': + _args.output_file.write("""{ +\t.arch = "common", +\t.cpuid = "common", +\t.event_table = { +\t\t.pmus = pmu_events__common, +\t\t.num_pmus = ARRAY_SIZE(pmu_events__common), +\t}, +\t.metric_table = {}, +}, """) else: with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile: @@ -930,7 +942,7 @@ def print_system_mapping_table() -> None: continue; ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); - if (pmu || ret) + if (ret) return ret; } return 0; @@ -1007,11 +1019,11 @@ def print_system_mapping_table() -> None: return 0; } -static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) +static const struct pmu_events_map *map_for_cpu(struct perf_cpu cpu) { static struct { const struct pmu_events_map *map; - struct perf_pmu *pmu; + struct perf_cpu cpu; } last_result; static struct { const struct pmu_events_map *map; @@ -1022,10 +1034,10 @@ def print_system_mapping_table() -> None: char *cpuid = NULL; size_t i; - if (has_last_result && last_result.pmu == pmu) + if (has_last_result && last_result.cpu.cpu == cpu.cpu) return last_result.map; - cpuid = perf_pmu__getcpuid(pmu); + cpuid = get_cpuid_allow_env_override(cpu); /* * On some platforms which uses cpus map, cpuid can be NULL for @@ -1056,12 +1068,21 @@ def print_system_mapping_table() -> None: has_last_map_search = true; } out_update_last_result: - last_result.pmu = pmu; + last_result.cpu = cpu; last_result.map = map; has_last_result = true; return map; } +static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) +{ + struct perf_cpu cpu = {-1}; + + if (pmu) + cpu = perf_cpu_map__min(pmu->cpus); + return map_for_cpu(cpu); +} + const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) { const struct pmu_events_map *map = map_for_pmu(pmu); @@ -1082,24 +1103,12 @@ def print_system_mapping_table() -> None: return NULL; } -const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) +const struct pmu_metrics_table *pmu_metrics_table__find(void) { - const struct pmu_events_map *map = map_for_pmu(pmu); - - if (!map) - return NULL; - - if (!pmu) - return &map->metric_table; + struct perf_cpu cpu = {-1}; + const struct pmu_events_map *map = map_for_cpu(cpu); - for (size_t i = 0; i < map->metric_table.num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &map->metric_table.pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - - if (pmu__name_match(pmu, pmu_name)) - return &map->metric_table; - } - return NULL; + return map ? &map->metric_table : NULL; } const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) @@ -1241,7 +1250,7 @@ def ftw(path: str, parents: Sequence[str], if len(parents) == _args.model.split(',')[0].count('/'): # We're testing the correct directory. item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name - if 'test' not in item_path and item_path not in _args.model.split(','): + if 'test' not in item_path and 'common' not in item_path and item_path not in _args.model.split(','): continue action(parents, item) if item.is_dir(): @@ -1289,7 +1298,7 @@ def ftw(path: str, parents: Sequence[str], for item in os.scandir(_args.starting_dir): if not item.is_dir(): continue - if item.name == _args.arch or _args.arch == 'all' or item.name == 'test': + if item.name == _args.arch or _args.arch == 'all' or item.name == 'test' or item.name == 'common': archs.append(item.name) if len(archs) < 2 and _args.arch != 'none': diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index 5435ad92180cf..675562e6f7704 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -103,7 +103,7 @@ int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pm void *data); const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu); -const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu); +const struct pmu_metrics_table *pmu_metrics_table__find(void); const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid); const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid); int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data); diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 3954bd1587ce9..01f54d6724a5f 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c @@ -12,6 +12,7 @@ #define PY_SSIZE_T_CLEAN #include +#include "../../../util/config.h" #include "../../../util/trace-event.h" #include "../../../util/event.h" #include "../../../util/symbol.h" @@ -182,6 +183,15 @@ static PyObject *perf_sample_srccode(PyObject *obj, PyObject *args) return perf_sample_src(obj, args, true); } +static PyObject *__perf_config_get(PyObject *obj, PyObject *args) +{ + const char *config_name; + + if (!PyArg_ParseTuple(args, "s", &config_name)) + return NULL; + return Py_BuildValue("s", perf_config_get(config_name)); +} + static PyMethodDef ContextMethods[] = { #ifdef HAVE_LIBTRACEEVENT { "common_pc", perf_trace_context_common_pc, METH_VARARGS, @@ -199,6 +209,7 @@ static PyMethodDef ContextMethods[] = { METH_VARARGS, "Get source file name and line number."}, { "perf_sample_srccode", perf_sample_srccode, METH_VARARGS, "Get source file name, line number and line."}, + { "perf_config_get", __perf_config_get, METH_VARARGS, "Get perf config entry"}, { NULL, NULL, 0, NULL} }; diff --git a/tools/perf/scripts/python/arm-cs-trace-disasm.py b/tools/perf/scripts/python/arm-cs-trace-disasm.py index 7aff02d84ffb3..ba208c90d6311 100755 --- a/tools/perf/scripts/python/arm-cs-trace-disasm.py +++ b/tools/perf/scripts/python/arm-cs-trace-disasm.py @@ -11,36 +11,74 @@ from os import path import re from subprocess import * -from optparse import OptionParser, make_option +import argparse +import platform -from perf_trace_context import perf_set_itrace_options, \ - perf_sample_insn, perf_sample_srccode +from perf_trace_context import perf_sample_srccode, perf_config_get # Below are some example commands for using this script. +# Note a --kcore recording is required for accurate decode +# due to the alternatives patching mechanism. However this +# script only supports reading vmlinux for disassembly dump, +# meaning that any patched instructions will appear +# as unpatched, but the instruction ranges themselves will +# be correct. In addition to this, source line info comes +# from Perf, and when using kcore there is no debug info. The +# following lists the supported features in each mode: +# +# +-----------+-----------------+------------------+------------------+ +# | Recording | Accurate decode | Source line dump | Disassembly dump | +# +-----------+-----------------+------------------+------------------+ +# | --kcore | yes | no | yes | +# | normal | no | yes | yes | +# +-----------+-----------------+------------------+------------------+ +# +# Output disassembly with objdump and auto detect vmlinux +# (when running on same machine.) +# perf script -s scripts/python/arm-cs-trace-disasm.py -d # -# Output disassembly with objdump: -# perf script -s scripts/python/arm-cs-trace-disasm.py \ -# -- -d objdump -k path/to/vmlinux # Output disassembly with llvm-objdump: # perf script -s scripts/python/arm-cs-trace-disasm.py \ # -- -d llvm-objdump-11 -k path/to/vmlinux +# # Output only source line and symbols: # perf script -s scripts/python/arm-cs-trace-disasm.py -# Command line parsing. -option_list = [ - # formatting options for the bottom entry of the stack - make_option("-k", "--vmlinux", dest="vmlinux_name", - help="Set path to vmlinux file"), - make_option("-d", "--objdump", dest="objdump_name", - help="Set path to objdump executable file"), - make_option("-v", "--verbose", dest="verbose", - action="store_true", default=False, - help="Enable debugging log") -] +def default_objdump(): + config = perf_config_get("annotate.objdump") + return config if config else "objdump" -parser = OptionParser(option_list=option_list) -(options, args) = parser.parse_args() +# Command line parsing. +def int_arg(v): + v = int(v) + if v < 0: + raise argparse.ArgumentTypeError("Argument must be a positive integer") + return v + +args = argparse.ArgumentParser() +args.add_argument("-k", "--vmlinux", + help="Set path to vmlinux file. Omit to autodetect if running on same machine") +args.add_argument("-d", "--objdump", nargs="?", const=default_objdump(), + help="Show disassembly. Can also be used to change the objdump path"), +args.add_argument("-v", "--verbose", action="store_true", help="Enable debugging log") +args.add_argument("--start-time", type=int_arg, help="Monotonic clock time of sample to start from. " + "See 'time' field on samples in -v mode.") +args.add_argument("--stop-time", type=int_arg, help="Monotonic clock time of sample to stop at. " + "See 'time' field on samples in -v mode.") +args.add_argument("--start-sample", type=int_arg, help="Index of sample to start from. " + "See 'index' field on samples in -v mode.") +args.add_argument("--stop-sample", type=int_arg, help="Index of sample to stop at. " + "See 'index' field on samples in -v mode.") + +options = args.parse_args() +if (options.start_time and options.stop_time and + options.start_time >= options.stop_time): + print("--start-time must less than --stop-time") + exit(2) +if (options.start_sample and options.stop_sample and + options.start_sample >= options.stop_sample): + print("--start-sample must less than --stop-sample") + exit(2) # Initialize global dicts and regular expression disasm_cache = dict() @@ -48,11 +86,23 @@ disasm_re = re.compile(r"^\s*([0-9a-fA-F]+):") disasm_func_re = re.compile(r"^\s*([0-9a-fA-F]+)\s.*:") cache_size = 64*1024 +sample_idx = -1 glb_source_file_name = None glb_line_number = None glb_dso = None +kver = platform.release() +vmlinux_paths = [ + f"/usr/lib/debug/boot/vmlinux-{kver}.debug", + f"/usr/lib/debug/lib/modules/{kver}/vmlinux", + f"/lib/modules/{kver}/build/vmlinux", + f"/usr/lib/debug/boot/vmlinux-{kver}", + f"/boot/vmlinux-{kver}", + f"/boot/vmlinux", + f"vmlinux" +] + def get_optional(perf_dict, field): if field in perf_dict: return perf_dict[field] @@ -63,12 +113,25 @@ def get_offset(perf_dict, field): return "+%#x" % perf_dict[field] return "" +def find_vmlinux(): + if hasattr(find_vmlinux, "path"): + return find_vmlinux.path + + for v in vmlinux_paths: + if os.access(v, os.R_OK): + find_vmlinux.path = v + break + else: + find_vmlinux.path = None + + return find_vmlinux.path + def get_dso_file_path(dso_name, dso_build_id): if (dso_name == "[kernel.kallsyms]" or dso_name == "vmlinux"): - if (options.vmlinux_name): - return options.vmlinux_name; + if (options.vmlinux): + return options.vmlinux; else: - return dso_name + return find_vmlinux() if find_vmlinux() else dso_name if (dso_name == "[vdso]") : append = "/vdso" @@ -92,7 +155,7 @@ def read_disam(dso_fname, dso_start, start_addr, stop_addr): else: start_addr = start_addr - dso_start; stop_addr = stop_addr - dso_start; - disasm = [ options.objdump_name, "-d", "-z", + disasm = [ options.objdump, "-d", "-z", "--start-address="+format(start_addr,"#x"), "--stop-address="+format(stop_addr,"#x") ] disasm += [ dso_fname ] @@ -112,10 +175,10 @@ def print_disam(dso_fname, dso_start, start_addr, stop_addr): def print_sample(sample): print("Sample = { cpu: %04d addr: 0x%016x phys_addr: 0x%016x ip: 0x%016x " \ - "pid: %d tid: %d period: %d time: %d }" % \ + "pid: %d tid: %d period: %d time: %d index: %d}" % \ (sample['cpu'], sample['addr'], sample['phys_addr'], \ sample['ip'], sample['pid'], sample['tid'], \ - sample['period'], sample['time'])) + sample['period'], sample['time'], sample_idx)) def trace_begin(): print('ARM CoreSight Trace Data Assembler Dump') @@ -177,6 +240,7 @@ def print_srccode(comm, param_dict, sample, symbol, dso): def process_event(param_dict): global cache_size global options + global sample_idx sample = param_dict["sample"] comm = param_dict["comm"] @@ -187,11 +251,26 @@ def process_event(param_dict): dso_start = get_optional(param_dict, "dso_map_start") dso_end = get_optional(param_dict, "dso_map_end") symbol = get_optional(param_dict, "symbol") + map_pgoff = get_optional(param_dict, "map_pgoff") + # check for valid map offset + if (str(map_pgoff) == '[unknown]'): + map_pgoff = 0 cpu = sample["cpu"] ip = sample["ip"] addr = sample["addr"] + sample_idx += 1 + + if (options.start_time and sample["time"] < options.start_time): + return + if (options.stop_time and sample["time"] > options.stop_time): + exit(0) + if (options.start_sample and sample_idx < options.start_sample): + return + if (options.stop_sample and sample_idx > options.stop_sample): + exit(0) + if (options.verbose == True): print("Event type: %s" % name) print_sample(sample) @@ -243,9 +322,10 @@ def process_event(param_dict): # Record for previous sample packet cpu_data[str(cpu) + 'addr'] = addr - # Handle CS_ETM_TRACE_ON packet if start_addr=0 and stop_addr=4 - if (start_addr == 0 and stop_addr == 4): - print("CPU%d: CS_ETM_TRACE_ON packet is inserted" % cpu) + # Filter out zero start_address. Optionally identify CS_ETM_TRACE_ON packet + if (start_addr == 0): + if ((stop_addr == 4) and (options.verbose == True)): + print("CPU%d: CS_ETM_TRACE_ON packet is inserted" % cpu) return if (start_addr < int(dso_start) or start_addr > int(dso_end)): @@ -256,19 +336,20 @@ def process_event(param_dict): print("Stop address 0x%x is out of range [ 0x%x .. 0x%x ] for dso %s" % (stop_addr, int(dso_start), int(dso_end), dso)) return - if (options.objdump_name != None): + if (options.objdump != None): # It doesn't need to decrease virtual memory offset for disassembly # for kernel dso and executable file dso, so in this case we set # vm_start to zero. if (dso == "[kernel.kallsyms]" or dso_start == 0x400000): dso_vm_start = 0 + map_pgoff = 0 else: dso_vm_start = int(dso_start) dso_fname = get_dso_file_path(dso, dso_bid) if path.exists(dso_fname): - print_disam(dso_fname, dso_vm_start, start_addr, stop_addr) + print_disam(dso_fname, dso_vm_start, start_addr + map_pgoff, stop_addr + map_pgoff) else: - print("Failed to find dso %s for address range [ 0x%x .. 0x%x ]" % (dso, start_addr, stop_addr)) + print("Failed to find dso %s for address range [ 0x%x .. 0x%x ]" % (dso, start_addr + map_pgoff, stop_addr + map_pgoff)) print_srccode(comm, param_dict, sample, symbol, dso) diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 5671ee5300195..ec4e1f0347424 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -4,7 +4,6 @@ perf-test-y += builtin-test.o perf-test-y += tests-scripts.o perf-test-y += parse-events.o perf-test-y += dso-data.o -perf-test-y += attr.o perf-test-y += vmlinux-kallsyms.o perf-test-$(CONFIG_LIBTRACEEVENT) += openat-syscall.o perf-test-$(CONFIG_LIBTRACEEVENT) += openat-syscall-all-cpus.o @@ -67,12 +66,13 @@ perf-test-y += sigtrap.o perf-test-y += event_groups.o perf-test-y += symbols.o perf-test-y += util.o +perf-test-y += hwmon_pmu.o +perf-test-y += tool_pmu.o ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc)) perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif -CFLAGS_attr.o += -DBINDIR="BUILD_STR($(bindir_SQ))" -DPYTHON="BUILD_STR($(PYTHON_WORD))" CFLAGS_python-use.o += -DPYTHONPATH="BUILD_STR($(OUTPUT)python)" -DPYTHON="BUILD_STR($(PYTHON_WORD))" CFLAGS_dwarf-unwind.o += -fno-optimize-sibling-calls diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c deleted file mode 100644 index 97e1bdd6ec0e9..0000000000000 --- a/tools/perf/tests/attr.c +++ /dev/null @@ -1,218 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * The struct perf_event_attr test support. - * - * This test is embedded inside into perf directly and is governed - * by the PERF_TEST_ATTR environment variable and hook inside - * sys_perf_event_open function. - * - * The general idea is to store 'struct perf_event_attr' details for - * each event created within single perf command. Each event details - * are stored into separate text file. Once perf command is finished - * these files can be checked for values we expect for command. - * - * Besides 'struct perf_event_attr' values we also store 'fd' and - * 'group_fd' values to allow checking for groups created. - * - * This all is triggered by setting PERF_TEST_ATTR environment variable. - * It must contain name of existing directory with access and write - * permissions. All the event text files are stored there. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "event.h" -#include "util.h" -#include "tests.h" -#include "pmus.h" - -#define ENV "PERF_TEST_ATTR" - -static char *dir; -static bool ready; - -void test_attr__init(void) -{ - dir = getenv(ENV); - test_attr__enabled = (dir != NULL); -} - -#define BUFSIZE 1024 - -#define __WRITE_ASS(str, fmt, data) \ -do { \ - char buf[BUFSIZE]; \ - size_t size; \ - \ - size = snprintf(buf, BUFSIZE, #str "=%"fmt "\n", data); \ - if (1 != fwrite(buf, size, 1, file)) { \ - perror("test attr - failed to write event file"); \ - fclose(file); \ - return -1; \ - } \ - \ -} while (0) - -#define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field) - -static int store_event(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, - int fd, int group_fd, unsigned long flags) -{ - FILE *file; - char path[PATH_MAX]; - - if (!ready) - return 0; - - snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir, - attr->type, attr->config, fd); - - file = fopen(path, "w+"); - if (!file) { - perror("test attr - failed to open event file"); - return -1; - } - - if (fprintf(file, "[event-%d-%llu-%d]\n", - attr->type, attr->config, fd) < 0) { - perror("test attr - failed to write event file"); - fclose(file); - return -1; - } - - /* syscall arguments */ - __WRITE_ASS(fd, "d", fd); - __WRITE_ASS(group_fd, "d", group_fd); - __WRITE_ASS(cpu, "d", cpu.cpu); - __WRITE_ASS(pid, "d", pid); - __WRITE_ASS(flags, "lu", flags); - - /* struct perf_event_attr */ - WRITE_ASS(type, PRIu32); - WRITE_ASS(size, PRIu32); - WRITE_ASS(config, "llu"); - WRITE_ASS(sample_period, "llu"); - WRITE_ASS(sample_type, "llu"); - WRITE_ASS(read_format, "llu"); - WRITE_ASS(disabled, "d"); - WRITE_ASS(inherit, "d"); - WRITE_ASS(pinned, "d"); - WRITE_ASS(exclusive, "d"); - WRITE_ASS(exclude_user, "d"); - WRITE_ASS(exclude_kernel, "d"); - WRITE_ASS(exclude_hv, "d"); - WRITE_ASS(exclude_idle, "d"); - WRITE_ASS(mmap, "d"); - WRITE_ASS(comm, "d"); - WRITE_ASS(freq, "d"); - WRITE_ASS(inherit_stat, "d"); - WRITE_ASS(enable_on_exec, "d"); - WRITE_ASS(task, "d"); - WRITE_ASS(watermark, "d"); - WRITE_ASS(precise_ip, "d"); - WRITE_ASS(mmap_data, "d"); - WRITE_ASS(sample_id_all, "d"); - WRITE_ASS(exclude_host, "d"); - WRITE_ASS(exclude_guest, "d"); - WRITE_ASS(exclude_callchain_kernel, "d"); - WRITE_ASS(exclude_callchain_user, "d"); - WRITE_ASS(mmap2, "d"); - WRITE_ASS(comm_exec, "d"); - WRITE_ASS(context_switch, "d"); - WRITE_ASS(write_backward, "d"); - WRITE_ASS(namespaces, "d"); - WRITE_ASS(use_clockid, "d"); - WRITE_ASS(wakeup_events, PRIu32); - WRITE_ASS(bp_type, PRIu32); - WRITE_ASS(config1, "llu"); - WRITE_ASS(config2, "llu"); - WRITE_ASS(branch_sample_type, "llu"); - WRITE_ASS(sample_regs_user, "llu"); - WRITE_ASS(sample_stack_user, PRIu32); - - fclose(file); - return 0; -} - -void test_attr__open(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, - int fd, int group_fd, unsigned long flags) -{ - int errno_saved = errno; - - if ((fd != -1) && store_event(attr, pid, cpu, fd, group_fd, flags)) { - pr_err("test attr FAILED"); - exit(128); - } - - errno = errno_saved; -} - -void test_attr__ready(void) -{ - if (unlikely(test_attr__enabled) && !ready) - ready = true; -} - -static int run_dir(const char *d, const char *perf) -{ - char v[] = "-vvvvv"; - int vcnt = min(verbose, (int) sizeof(v) - 1); - char cmd[3*PATH_MAX]; - - if (verbose > 0) - vcnt++; - - scnprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", - d, d, perf, vcnt, v); - - return system(cmd) ? TEST_FAIL : TEST_OK; -} - -static int test__attr(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - struct stat st; - char path_perf[PATH_MAX]; - char path_dir[PATH_MAX]; - char *exec_path; - - if (perf_pmus__num_core_pmus() > 1) { - /* - * TODO: Attribute tests hard code the PMU type. If there are >1 - * core PMU then each PMU will have a different type which - * requires additional support. - */ - pr_debug("Skip test on hybrid systems"); - return TEST_SKIP; - } - - /* First try development tree tests. */ - if (!lstat("./tests", &st)) - return run_dir("./tests", "./perf"); - - exec_path = get_argv_exec_path(); - if (exec_path == NULL) - return -1; - - /* Then installed path. */ - snprintf(path_dir, PATH_MAX, "%s/tests", exec_path); - snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR); - free(exec_path); - - if (!lstat(path_dir, &st) && - !lstat(path_perf, &st)) - return run_dir(path_dir, path_perf); - - return TEST_SKIP; -} - -DEFINE_SUITE("Setup struct perf_event_attr", attr); diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 470a9709427dd..8dcf74d3c0a39 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -39,11 +40,8 @@ * making them easier to debug. */ static bool dont_fork; -/* Don't fork the tests in parallel and wait for their completion. */ -static bool sequential = true; -/* Do it in parallel, lacks infrastructure to avoid running tests that clash for resources, - * So leave it as the developers choice to enable while working on the needed infra */ -static bool parallel; +/* Fork the tests in parallel and wait for their completion. */ +static bool sequential; const char *dso_to_test; const char *test_objdump_path = "objdump"; @@ -73,13 +71,14 @@ static struct test_suite *generic_tests[] = { &suite__PERF_RECORD, &suite__pmu, &suite__pmu_events, + &suite__hwmon_pmu, + &suite__tool_pmu, &suite__dso_data, &suite__perf_evsel__roundtrip_name_test, #ifdef HAVE_LIBTRACEEVENT &suite__perf_evsel__tp_sched_test, &suite__syscall_openat_tp_fields, #endif - &suite__attr, &suite__hists_link, &suite__python_use, &suite__bp_signal, @@ -139,12 +138,6 @@ static struct test_suite *generic_tests[] = { NULL, }; -static struct test_suite **tests[] = { - generic_tests, - arch_tests, - NULL, /* shell tests created at runtime. */ -}; - static struct test_workload *workloads[] = { &workload__noploop, &workload__thloop, @@ -155,6 +148,9 @@ static struct test_workload *workloads[] = { &workload__landlock, }; +#define workloads__for_each(workload) \ + for (unsigned i = 0; i < ARRAY_SIZE(workloads) && ({ workload = workloads[i]; 1; }); i++) + static int num_subtests(const struct test_suite *t) { int num; @@ -198,6 +194,14 @@ static test_fnptr test_function(const struct test_suite *t, int subtest) return t->test_cases[subtest].run_case; } +static bool test_exclusive(const struct test_suite *t, int subtest) +{ + if (subtest <= 0) + return t->test_cases[0].exclusive; + + return t->test_cases[subtest].exclusive; +} + static bool perf_test__matches(const char *desc, int curr, int argc, const char *argv[]) { int i; @@ -229,20 +233,47 @@ struct child_test { int subtest; }; +static jmp_buf run_test_jmp_buf; + +static void child_test_sig_handler(int sig) +{ + siglongjmp(run_test_jmp_buf, sig); +} + static int run_test_child(struct child_process *process) { + const int signals[] = { + SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM, + }; struct child_test *child = container_of(process, struct child_test, process); int err; + err = sigsetjmp(run_test_jmp_buf, 1); + if (err) { + fprintf(stderr, "\n---- unexpected signal (%d) ----\n", err); + err = err > 0 ? -err : -1; + goto err_out; + } + + for (size_t i = 0; i < ARRAY_SIZE(signals); i++) + signal(signals[i], child_test_sig_handler); + pr_debug("--- start ---\n"); pr_debug("test child forked, pid %d\n", getpid()); err = test_function(child->test, child->subtest)(child->test, child->subtest); pr_debug("---- end(%d) ----\n", err); + +err_out: fflush(NULL); + for (size_t i = 0; i < ARRAY_SIZE(signals); i++) + signal(signals[i], SIG_DFL); return -err; } -static int print_test_result(struct test_suite *t, int i, int subtest, int result, int width) +#define TEST_RUNNING -3 + +static int print_test_result(struct test_suite *t, int i, int subtest, int result, int width, + int running) { if (has_subtests(t)) { int subw = width > 2 ? width - 2 : width; @@ -252,6 +283,9 @@ static int print_test_result(struct test_suite *t, int i, int subtest, int resul pr_info("%3d: %-*s:", i + 1, width, test_description(t, subtest)); switch (result) { + case TEST_RUNNING: + color_fprintf(stderr, PERF_COLOR_YELLOW, " Running (%d active)\n", running); + break; case TEST_OK: pr_info(" Ok\n"); break; @@ -273,16 +307,25 @@ static int print_test_result(struct test_suite *t, int i, int subtest, int resul return 0; } -static int finish_test(struct child_test *child_test, int width) +static void finish_test(struct child_test **child_tests, int running_test, int child_test_num, + int width) { - struct test_suite *t = child_test->test; - int i = child_test->test_num; - int subi = child_test->subtest; - int err = child_test->process.err; - bool err_done = err <= 0; + struct child_test *child_test = child_tests[running_test]; + struct test_suite *t; + int i, subi, err; + bool err_done = false; struct strbuf err_output = STRBUF_INIT; + int last_running = -1; int ret; + if (child_test == NULL) { + /* Test wasn't started. */ + return; + } + t = child_test->test; + i = child_test->test_num; + subi = child_test->subtest; + err = child_test->process.err; /* * For test suites with subtests, display the suite name ahead of the * sub test names. @@ -294,7 +337,7 @@ static int finish_test(struct child_test *child_test, int width) * Busy loop reading from the child's stdout/stderr that are set to be * non-blocking until EOF. */ - if (!err_done) + if (err > 0) fcntl(err, F_SETFL, O_NONBLOCK); if (verbose > 1) { if (has_subtests(t)) @@ -308,57 +351,90 @@ static int finish_test(struct child_test *child_test, int width) .events = POLLIN | POLLERR | POLLHUP | POLLNVAL, }, }; - char buf[512]; - ssize_t len; + if (perf_use_color_default) { + int running = 0; - /* Poll to avoid excessive spinning, timeout set for 100ms. */ - poll(pfds, ARRAY_SIZE(pfds), /*timeout=*/100); - if (!err_done && pfds[0].revents) { - errno = 0; - len = read(err, buf, sizeof(buf) - 1); + for (int y = running_test; y < child_test_num; y++) { + if (child_tests[y] == NULL) + continue; + if (check_if_command_finished(&child_tests[y]->process) == 0) + running++; + } + if (running != last_running) { + if (last_running != -1) { + /* + * Erase "Running (.. active)" line + * printed before poll/sleep. + */ + fprintf(debug_file(), PERF_COLOR_DELETE_LINE); + } + print_test_result(t, i, subi, TEST_RUNNING, width, running); + last_running = running; + } + } - if (len <= 0) { - err_done = errno != EAGAIN; - } else { - buf[len] = '\0'; - if (verbose > 1) - fprintf(stdout, "%s", buf); - else + err_done = true; + if (err <= 0) { + /* No child stderr to poll, sleep for 10ms for child to complete. */ + usleep(10 * 1000); + } else { + /* Poll to avoid excessive spinning, timeout set for 100ms. */ + poll(pfds, ARRAY_SIZE(pfds), /*timeout=*/100); + if (pfds[0].revents) { + char buf[512]; + ssize_t len; + + len = read(err, buf, sizeof(buf) - 1); + + if (len > 0) { + err_done = false; + buf[len] = '\0'; strbuf_addstr(&err_output, buf); + } } } + if (err_done) + err_done = check_if_command_finished(&child_test->process); + } + if (perf_use_color_default && last_running != -1) { + /* Erase "Running (.. active)" line printed before poll/sleep. */ + fprintf(debug_file(), PERF_COLOR_DELETE_LINE); } /* Clean up child process. */ ret = finish_command(&child_test->process); - if (verbose == 1 && ret == TEST_FAIL) { - /* Add header for test that was skipped above. */ - if (has_subtests(t)) - pr_info("%3d.%1d: %s:\n", i + 1, subi + 1, test_description(t, subi)); - else - pr_info("%3d: %s:\n", i + 1, test_description(t, -1)); + if (verbose > 1 || (verbose == 1 && ret == TEST_FAIL)) fprintf(stderr, "%s", err_output.buf); - } + strbuf_release(&err_output); - print_test_result(t, i, subi, ret, width); + print_test_result(t, i, subi, ret, width, /*running=*/0); if (err > 0) close(err); - return 0; + zfree(&child_tests[running_test]); } static int start_test(struct test_suite *test, int i, int subi, struct child_test **child, - int width) + int width, int pass) { int err; *child = NULL; if (dont_fork) { - pr_debug("--- start ---\n"); - err = test_function(test, subi)(test, subi); - pr_debug("---- end ----\n"); - print_test_result(test, i, subi, err, width); + if (pass == 1) { + pr_debug("--- start ---\n"); + err = test_function(test, subi)(test, subi); + pr_debug("---- end ----\n"); + print_test_result(test, i, subi, err, width, /*running=*/0); + } + return 0; + } + if (pass == 1 && !sequential && test_exclusive(test, subi)) { + /* When parallel, skip exclusive tests on the first pass. */ + return 0; + } + if (pass != 1 && (sequential || !test_exclusive(test, subi))) { + /* Sequential and non-exclusive tests were run on the first pass. */ return 0; } - *child = zalloc(sizeof(**child)); if (!*child) return -ENOMEM; @@ -377,35 +453,42 @@ static int start_test(struct test_suite *test, int i, int subi, struct child_tes (*child)->process.err = -1; } (*child)->process.no_exec_cmd = run_test_child; - err = start_command(&(*child)->process); - if (err || !sequential) - return err; - return finish_test(*child, width); + if (sequential || pass == 2) { + err = start_command(&(*child)->process); + if (err) + return err; + finish_test(child, /*running_test=*/0, /*child_test_num=*/1, width); + return 0; + } + return start_command(&(*child)->process); } -#define for_each_test(j, k, t) \ - for (j = 0, k = 0; j < ARRAY_SIZE(tests); j++, k = 0) \ - while ((t = tests[j][k++]) != NULL) +/* State outside of __cmd_test for the sake of the signal handler. */ + +static size_t num_tests; +static struct child_test **child_tests; +static jmp_buf cmd_test_jmp_buf; -static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) +static void cmd_test_sig_handler(int sig) { - struct test_suite *t; - unsigned int j, k; - int i = 0; - int width = 0; - size_t num_tests = 0; - struct child_test **child_tests; - int child_test_num = 0; + siglongjmp(cmd_test_jmp_buf, sig); +} + +static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], + struct intlist *skiplist) +{ + static int width = 0; + int err = 0; - for_each_test(j, k, t) { - int len = strlen(test_description(t, -1)); + for (struct test_suite **t = suites; *t; t++) { + int len = strlen(test_description(*t, -1)); if (width < len) width = len; - if (has_subtests(t)) { - for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { - len = strlen(test_description(t, subi)); + if (has_subtests(*t)) { + for (int subi = 0, subn = num_subtests(*t); subi < subn; subi++) { + len = strlen(test_description(*t, subi)); if (width < len) width = len; num_tests++; @@ -418,97 +501,137 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) if (!child_tests) return -ENOMEM; - for_each_test(j, k, t) { - int curr = i++; - - if (!perf_test__matches(test_description(t, -1), curr, argc, argv)) { - bool skip = true; + err = sigsetjmp(cmd_test_jmp_buf, 1); + if (err) { + pr_err("\nSignal (%d) while running tests.\nTerminating tests with the same signal\n", + err); + for (size_t x = 0; x < num_tests; x++) { + struct child_test *child_test = child_tests[x]; - for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { - if (perf_test__matches(test_description(t, subi), - curr, argc, argv)) - skip = false; - } - - if (skip) + if (!child_test) continue; - } - if (intlist__find(skiplist, i)) { - pr_info("%3d: %-*s:", curr + 1, width, test_description(t, -1)); - color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); - continue; + pr_debug3("Killing %d pid %d\n", + child_test->test_num + 1, + child_test->process.pid); + kill(child_test->process.pid, err); } + goto err_out; + } + signal(SIGINT, cmd_test_sig_handler); + signal(SIGTERM, cmd_test_sig_handler); - if (!has_subtests(t)) { - int err = start_test(t, curr, -1, &child_tests[child_test_num++], width); + /* + * In parallel mode pass 1 runs non-exclusive tests in parallel, pass 2 + * runs the exclusive tests sequentially. In other modes all tests are + * run in pass 1. + */ + for (int pass = 1; pass <= 2; pass++) { + int child_test_num = 0; + int i = 0; + + for (struct test_suite **t = suites; *t; t++) { + int curr = i++; + + if (!perf_test__matches(test_description(*t, -1), curr, argc, argv)) { + /* + * Test suite shouldn't be run based on + * description. See if subtest should. + */ + bool skip = true; + + for (int subi = 0, subn = num_subtests(*t); subi < subn; subi++) { + if (perf_test__matches(test_description(*t, subi), + curr, argc, argv)) + skip = false; + } + + if (skip) + continue; + } - if (err) { - /* TODO: if !sequential waitpid the already forked children. */ - free(child_tests); - return err; + if (intlist__find(skiplist, i)) { + pr_info("%3d: %-*s:", curr + 1, width, test_description(*t, -1)); + color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); + continue; } - } else { - for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { - int err; - if (!perf_test__matches(test_description(t, subi), + if (!has_subtests(*t)) { + err = start_test(*t, curr, -1, &child_tests[child_test_num++], + width, pass); + if (err) + goto err_out; + continue; + } + for (int subi = 0, subn = num_subtests(*t); subi < subn; subi++) { + if (!perf_test__matches(test_description(*t, subi), curr, argc, argv)) continue; - err = start_test(t, curr, subi, &child_tests[child_test_num++], - width); + err = start_test(*t, curr, subi, &child_tests[child_test_num++], + width, pass); if (err) - return err; + goto err_out; } } - } - for (i = 0; i < child_test_num; i++) { if (!sequential) { - int ret = finish_test(child_tests[i], width); - - if (ret) - return ret; + /* Parallel mode starts tests but doesn't finish them. Do that now. */ + for (size_t x = 0; x < num_tests; x++) + finish_test(child_tests, x, num_tests, width); } - free(child_tests[i]); + } +err_out: + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + if (err) { + pr_err("Internal test harness failure. Completing any started tests:\n:"); + for (size_t x = 0; x < num_tests; x++) + finish_test(child_tests, x, num_tests, width); } free(child_tests); - return 0; + return err; } -static int perf_test__list(int argc, const char **argv) +static int perf_test__list(struct test_suite **suites, int argc, const char **argv) { - unsigned int j, k; - struct test_suite *t; int i = 0; - for_each_test(j, k, t) { + for (struct test_suite **t = suites; *t; t++) { int curr = i++; - if (!perf_test__matches(test_description(t, -1), curr, argc, argv)) + if (!perf_test__matches(test_description(*t, -1), curr, argc, argv)) continue; - pr_info("%3d: %s\n", i, test_description(t, -1)); + pr_info("%3d: %s\n", i, test_description(*t, -1)); - if (has_subtests(t)) { - int subn = num_subtests(t); + if (has_subtests(*t)) { + int subn = num_subtests(*t); int subi; for (subi = 0; subi < subn; subi++) pr_info("%3d:%1d: %s\n", i, subi + 1, - test_description(t, subi)); + test_description(*t, subi)); } } return 0; } +static int workloads__fprintf_list(FILE *fp) +{ + struct test_workload *twl; + int printed = 0; + + workloads__for_each(twl) + printed += fprintf(fp, "%s\n", twl->name); + + return printed; +} + static int run_workload(const char *work, int argc, const char **argv) { - unsigned int i = 0; struct test_workload *twl; - for (i = 0; i < ARRAY_SIZE(workloads); i++) { - twl = workloads[i]; + workloads__for_each(twl) { if (!strcmp(twl->name, work)) return twl->func(argc, argv); } @@ -526,6 +649,55 @@ static int perf_test__config(const char *var, const char *value, return 0; } +static struct test_suite **build_suites(void) +{ + /* + * TODO: suites is static to avoid needing to clean up the scripts tests + * for leak sanitizer. + */ + static struct test_suite **suites[] = { + generic_tests, + arch_tests, + NULL, + }; + struct test_suite **result; + struct test_suite *t; + size_t n = 0, num_suites = 0; + + if (suites[2] == NULL) + suites[2] = create_script_test_suites(); + +#define for_each_test(t) \ + for (size_t i = 0, j = 0; i < ARRAY_SIZE(suites); i++, j = 0) \ + while ((t = suites[i][j++]) != NULL) + + for_each_test(t) + num_suites++; + + result = calloc(num_suites + 1, sizeof(struct test_suite *)); + + for (int pass = 1; pass <= 2; pass++) { + for_each_test(t) { + bool exclusive = false; + + if (!has_subtests(t)) { + exclusive = test_exclusive(t, -1); + } else { + for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { + if (test_exclusive(t, subi)) { + exclusive = true; + break; + } + } + } + if ((!exclusive && pass == 1) || (exclusive && pass == 2)) + result[n++] = t; + } + } + return result; +#undef for_each_test +} + int cmd_test(int argc, const char **argv) { const char *test_usage[] = { @@ -534,16 +706,17 @@ int cmd_test(int argc, const char **argv) }; const char *skip = NULL; const char *workload = NULL; + bool list_workloads = false; const struct option test_options[] = { OPT_STRING('s', "skip", &skip, "tests", "tests to skip"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('F', "dont-fork", &dont_fork, "Do not fork for testcase"), - OPT_BOOLEAN('p', "parallel", ¶llel, "Run the tests in parallel"), OPT_BOOLEAN('S', "sequential", &sequential, "Run the tests one after another rather than in parallel"), - OPT_STRING('w', "workload", &workload, "work", "workload to run for testing"), + OPT_STRING('w', "workload", &workload, "work", "workload to run for testing, use '--list-workloads' to list the available ones."), + OPT_BOOLEAN(0, "list-workloads", &list_workloads, "List the available builtin workloads to use with -w/--workload"), OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"), OPT_STRING(0, "objdump", &test_objdump_path, "path", "objdump binary to use for disassembly and annotations"), @@ -552,6 +725,7 @@ int cmd_test(int argc, const char **argv) const char * const test_subcommands[] = { "list", NULL }; struct intlist *skiplist = NULL; int ret = hists__init(); + struct test_suite **suites; if (ret < 0) return ret; @@ -561,22 +735,29 @@ int cmd_test(int argc, const char **argv) /* Unbuffered output */ setvbuf(stdout, NULL, _IONBF, 0); - tests[2] = create_script_test_suites(); argc = parse_options_subcommand(argc, argv, test_options, test_subcommands, test_usage, 0); - if (argc >= 1 && !strcmp(argv[0], "list")) - return perf_test__list(argc - 1, argv + 1); + if (argc >= 1 && !strcmp(argv[0], "list")) { + suites = build_suites(); + ret = perf_test__list(suites, argc - 1, argv + 1); + free(suites); + return ret; + } if (workload) return run_workload(workload, argc, argv); + if (list_workloads) { + workloads__fprintf_list(stdout); + return 0; + } + if (dont_fork) sequential = true; - else if (parallel) - sequential = false; symbol_conf.priv_size = sizeof(int); symbol_conf.try_vmlinux_path = true; + if (symbol__init(NULL) < 0) return -1; @@ -588,5 +769,8 @@ int cmd_test(int argc, const char **argv) */ rlimit__bump_memlock(); - return __cmd_test(argc, argv, skiplist); + suites = build_suites(); + ret = __cmd_test(suites, argc, argv, skiplist); + free(suites); + return ret; } diff --git a/tools/perf/tests/demangle-java-test.c b/tools/perf/tests/demangle-java-test.c index 44d1be303b67a..93c94408bdc8f 100644 --- a/tools/perf/tests/demangle-java-test.c +++ b/tools/perf/tests/demangle-java-test.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "tests.h" #include "session.h" #include "debug.h" @@ -28,7 +29,7 @@ static int test__demangle_java(struct test_suite *test __maybe_unused, int subte "void java.lang.Object()" }, }; - for (i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) { + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { buf = java_demangle_sym(test_cases[i].mangled, 0); if (strcmp(buf, test_cases[i].demangled)) { pr_debug("FAILED: %s: %s != %s\n", test_cases[i].mangled, diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c index e155f0e0e04d5..deefe5003bfc2 100644 --- a/tools/perf/tests/event-times.c +++ b/tools/perf/tests/event-times.c @@ -126,6 +126,7 @@ static int attach__cpu_disabled(struct evlist *evlist) evsel->core.attr.disabled = 1; err = evsel__open_per_cpu(evsel, cpus, -1); + perf_cpu_map__put(cpus); if (err) { if (err == -EACCES) return TEST_SKIP; @@ -134,7 +135,6 @@ static int attach__cpu_disabled(struct evlist *evlist) return err; } - perf_cpu_map__put(cpus); return evsel__enable(evsel); } @@ -153,10 +153,10 @@ static int attach__cpu_enabled(struct evlist *evlist) } err = evsel__open_per_cpu(evsel, cpus, -1); + perf_cpu_map__put(cpus); if (err == -EACCES) return TEST_SKIP; - perf_cpu_map__put(cpus); return err ? TEST_FAIL : TEST_OK; } @@ -188,6 +188,7 @@ static int test_times(int (attach)(struct evlist *), err = attach(evlist); if (err == TEST_SKIP) { pr_debug(" SKIP : not enough rights\n"); + evlist__delete(evlist); return err; } diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index cf4da3d748c29..226196fb96779 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include +#include #include "evsel.h" #include "tests.h" #include "debug.h" @@ -36,33 +36,33 @@ static int test__perf_evsel__tp_sched_test(struct test_suite *test __maybe_unuse int subtest __maybe_unused) { struct evsel *evsel = evsel__newtp("sched", "sched_switch"); - int ret = 0; + int ret = TEST_OK; if (IS_ERR(evsel)) { pr_debug("evsel__newtp failed with %ld\n", PTR_ERR(evsel)); - return -1; + return PTR_ERR(evsel) == -EACCES ? TEST_SKIP : TEST_FAIL; } if (evsel__test_field(evsel, "prev_comm", 16, false)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prev_pid", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prev_prio", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prev_state", sizeof(long), true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "next_comm", 16, false)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "next_pid", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "next_prio", 4, true)) - ret = -1; + ret = TEST_FAIL; evsel__delete(evsel); @@ -70,23 +70,33 @@ static int test__perf_evsel__tp_sched_test(struct test_suite *test __maybe_unuse if (IS_ERR(evsel)) { pr_debug("evsel__newtp failed with %ld\n", PTR_ERR(evsel)); - return -1; + return TEST_FAIL; } if (evsel__test_field(evsel, "comm", 16, false)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "pid", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "prio", 4, true)) - ret = -1; + ret = TEST_FAIL; if (evsel__test_field(evsel, "target_cpu", 4, true)) - ret = -1; + ret = TEST_FAIL; evsel__delete(evsel); return ret; } -DEFINE_SUITE("Parse sched tracepoints fields", perf_evsel__tp_sched_test); +static struct test_case tests__perf_evsel__tp_sched_test[] = { + TEST_CASE_REASON("Parse sched tracepoints fields", + perf_evsel__tp_sched_test, + "permissions"), + { .name = NULL, } +}; + +struct test_suite suite__perf_evsel__tp_sched_test = { + .desc = "Parse sched tracepoints fields", + .test_cases = tests__perf_evsel__tp_sched_test, +}; diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c index e3aa9d4fcf3a5..41ff1affdfcdf 100644 --- a/tools/perf/tests/expr.c +++ b/tools/perf/tests/expr.c @@ -6,6 +6,7 @@ #include "util/header.h" #include "util/smt.h" #include "tests.h" +#include #include #include #include @@ -76,8 +77,8 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u struct expr_parse_ctx *ctx; bool is_intel = false; char strcmp_cpuid_buf[256]; - struct perf_pmu *pmu = perf_pmus__find_core_pmu(); - char *cpuid = perf_pmu__getcpuid(pmu); + struct perf_cpu cpu = {-1}; + char *cpuid = get_cpuid_allow_env_override(cpu); char *escaped_cpuid1, *escaped_cpuid2; TEST_ASSERT_VAL("get_cpuid", cpuid); diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c new file mode 100644 index 0000000000000..f8bcee9660d53 --- /dev/null +++ b/tools/perf/tests/hwmon_pmu.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "debug.h" +#include "evlist.h" +#include "hwmon_pmu.h" +#include "parse-events.h" +#include "tests.h" +#include +#include +#include +#include +#include + +static const struct test_event { + const char *name; + const char *alias; + long config; +} test_events[] = { + { + "temp_test_hwmon_event1", + "temp1", + 0xA0001, + }, + { + "temp_test_hwmon_event2", + "temp2", + 0xA0002, + }, +}; + +/* Cleanup test PMU directory. */ +static int test_pmu_put(const char *dir, struct perf_pmu *hwm) +{ + char buf[PATH_MAX + 20]; + int ret; + + if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) { + pr_err("Failure to set up buffer for \"%s\"\n", dir); + return -EINVAL; + } + ret = system(buf); + if (ret) + pr_err("Failure to \"%s\"\n", buf); + + list_del(&hwm->list); + perf_pmu__delete(hwm); + return ret; +} + +/* + * Prepare test PMU directory data, normally exported by kernel at + * /sys/class/hwmon/hwmon/. Give as input a buffer to hold the file + * path, the result is PMU loaded using that directory. + */ +static struct perf_pmu *test_pmu_get(char *dir, size_t sz) +{ + const char *test_hwmon_name_nl = "A test hwmon PMU\n"; + const char *test_hwmon_name = "A test hwmon PMU"; + /* Simulated hwmon items. */ + const struct test_item { + const char *name; + const char *value; + } test_items[] = { + { "temp1_label", "test hwmon event1\n", }, + { "temp1_input", "40000\n", }, + { "temp2_label", "test hwmon event2\n", }, + { "temp2_input", "50000\n", }, + }; + int dirfd, file; + struct perf_pmu *hwm = NULL; + ssize_t len; + + /* Create equivalent of sysfs mount point. */ + scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX"); + if (!mkdtemp(dir)) { + pr_err("mkdtemp failed\n"); + dir[0] = '\0'; + return NULL; + } + dirfd = open(dir, O_DIRECTORY); + if (dirfd < 0) { + pr_err("Failed to open test directory \"%s\"\n", dir); + goto err_out; + } + + /* Create the test hwmon directory and give it a name. */ + if (mkdirat(dirfd, "hwmon1234", 0755) < 0) { + pr_err("Failed to mkdir hwmon directory\n"); + goto err_out; + } + file = openat(dirfd, "hwmon1234/name", O_WRONLY | O_CREAT, 0600); + if (!file) { + pr_err("Failed to open for writing file \"name\"\n"); + goto err_out; + } + len = strlen(test_hwmon_name_nl); + if (write(file, test_hwmon_name_nl, len) < len) { + close(file); + pr_err("Failed to write to 'name' file\n"); + goto err_out; + } + close(file); + + /* Create test hwmon files. */ + for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) { + const struct test_item *item = &test_items[i]; + + file = openat(dirfd, item->name, O_WRONLY | O_CREAT, 0600); + if (!file) { + pr_err("Failed to open for writing file \"%s\"\n", item->name); + goto err_out; + } + + if (write(file, item->value, strlen(item->value)) < 0) { + pr_err("Failed to write to file \"%s\"\n", item->name); + close(file); + goto err_out; + } + close(file); + } + + /* Make the PMU reading the files created above. */ + hwm = perf_pmus__add_test_hwmon_pmu(dirfd, "hwmon1234", test_hwmon_name); + if (!hwm) + pr_err("Test hwmon creation failed\n"); + +err_out: + if (!hwm) { + test_pmu_put(dir, hwm); + if (dirfd >= 0) + close(dirfd); + } + return hwm; +} + +static int do_test(size_t i, bool with_pmu, bool with_alias) +{ + const char *test_event = with_alias ? test_events[i].alias : test_events[i].name; + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + struct parse_events_error err; + int ret; + char str[128]; + bool found = false; + + if (!evlist) { + pr_err("evlist allocation failed\n"); + return TEST_FAIL; + } + + if (with_pmu) + snprintf(str, sizeof(str), "hwmon_a_test_hwmon_pmu/%s/", test_event); + else + strlcpy(str, test_event, sizeof(str)); + + pr_debug("Testing '%s'\n", str); + parse_events_error__init(&err); + ret = parse_events(evlist, str, &err); + if (ret) { + pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n", + __FILE__, __LINE__, str, ret); + parse_events_error__print(&err, str); + ret = TEST_FAIL; + goto out; + } + + ret = TEST_OK; + if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) { + pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n", + __FILE__, __LINE__, str, evlist->core.nr_entries); + ret = TEST_FAIL; + goto out; + } + + evlist__for_each_entry(evlist, evsel) { + if (!evsel->pmu || !evsel->pmu->name || + strcmp(evsel->pmu->name, "hwmon_a_test_hwmon_pmu")) + continue; + + if (evsel->core.attr.config != (u64)test_events[i].config) { + pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n", + __FILE__, __LINE__, str, + evsel->core.attr.config, + test_events[i].config); + ret = TEST_FAIL; + goto out; + } + found = true; + } + + if (!found) { + pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n", + __FILE__, __LINE__, str); + ret = TEST_FAIL; + } + +out: + parse_events_error__exit(&err); + evlist__delete(evlist); + return ret; +} + +static int test__hwmon_pmu(bool with_pmu) +{ + char dir[PATH_MAX]; + struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir)); + int ret = TEST_OK; + + if (!pmu) + return TEST_FAIL; + + for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) { + ret = do_test(i, with_pmu, /*with_alias=*/false); + + if (ret != TEST_OK) + break; + + ret = do_test(i, with_pmu, /*with_alias=*/true); + + if (ret != TEST_OK) + break; + } + test_pmu_put(dir, pmu); + return ret; +} + +static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + return test__hwmon_pmu(/*with_pmu=*/false); +} + +static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + return test__hwmon_pmu(/*with_pmu=*/true); +} + +static int test__parse_hwmon_filename(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + const struct hwmon_parse_test { + const char *filename; + enum hwmon_type type; + int number; + enum hwmon_item item; + bool alarm; + bool parse_ok; + } tests[] = { + { + .filename = "cpu0_accuracy", + .type = HWMON_TYPE_CPU, + .number = 0, + .item = HWMON_ITEM_ACCURACY, + .alarm = false, + .parse_ok = true, + }, + { + .filename = "temp1_input", + .type = HWMON_TYPE_TEMP, + .number = 1, + .item = HWMON_ITEM_INPUT, + .alarm = false, + .parse_ok = true, + }, + { + .filename = "fan2_vid", + .type = HWMON_TYPE_FAN, + .number = 2, + .item = HWMON_ITEM_VID, + .alarm = false, + .parse_ok = true, + }, + { + .filename = "power3_crit_alarm", + .type = HWMON_TYPE_POWER, + .number = 3, + .item = HWMON_ITEM_CRIT, + .alarm = true, + .parse_ok = true, + }, + { + .filename = "intrusion4_average_interval_min_alarm", + .type = HWMON_TYPE_INTRUSION, + .number = 4, + .item = HWMON_ITEM_AVERAGE_INTERVAL_MIN, + .alarm = true, + .parse_ok = true, + }, + { + .filename = "badtype5_baditem", + .type = HWMON_TYPE_NONE, + .number = 5, + .item = HWMON_ITEM_NONE, + .alarm = false, + .parse_ok = false, + }, + { + .filename = "humidity6_baditem", + .type = HWMON_TYPE_NONE, + .number = 6, + .item = HWMON_ITEM_NONE, + .alarm = false, + .parse_ok = false, + }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(tests); i++) { + enum hwmon_type type; + int number; + enum hwmon_item item; + bool alarm; + + TEST_ASSERT_EQUAL("parse_hwmon_filename", + parse_hwmon_filename( + tests[i].filename, + &type, + &number, + &item, + &alarm), + tests[i].parse_ok + ); + if (tests[i].parse_ok) { + TEST_ASSERT_EQUAL("parse_hwmon_filename type", type, tests[i].type); + TEST_ASSERT_EQUAL("parse_hwmon_filename number", number, tests[i].number); + TEST_ASSERT_EQUAL("parse_hwmon_filename item", item, tests[i].item); + TEST_ASSERT_EQUAL("parse_hwmon_filename alarm", alarm, tests[i].alarm); + } + } + return TEST_OK; +} + +static struct test_case tests__hwmon_pmu[] = { + TEST_CASE("Basic parsing test", parse_hwmon_filename), + TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu), + TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu), + { .name = NULL, } +}; + +struct test_suite suite__hwmon_pmu = { + .desc = "Hwmon PMU", + .test_cases = tests__hwmon_pmu, +}; diff --git a/tools/perf/tests/make b/tools/perf/tests/make index a5040772043f6..a7fcbd589752a 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -81,7 +81,7 @@ make_no_gtk2 := NO_GTK2=1 make_no_ui := NO_SLANG=1 NO_GTK2=1 make_no_demangle := NO_DEMANGLE=1 make_no_libelf := NO_LIBELF=1 -make_no_libunwind := NO_LIBUNWIND=1 +make_libunwind := LIBUNWIND=1 make_no_libdw_dwarf_unwind := NO_LIBDW_DWARF_UNWIND=1 make_no_backtrace := NO_BACKTRACE=1 make_no_libcapstone := NO_CAPSTONE=1 @@ -121,7 +121,7 @@ make_static := LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX3 # all the NO_* variable combined make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_GTK2=1 -make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 +make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_BACKTRACE=1 make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1 @@ -153,7 +153,7 @@ run += make_no_gtk2 run += make_no_ui run += make_no_demangle run += make_no_libelf -run += make_no_libunwind +run += make_libunwind run += make_no_libdw_dwarf_unwind run += make_no_backtrace run += make_no_libcapstone diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 888df8eca9812..3943da441979c 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -40,7 +40,7 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused int flags = O_RDONLY | O_DIRECTORY; struct evlist *evlist = evlist__new(); struct evsel *evsel; - int err = -1, i, nr_events = 0, nr_polls = 0; + int ret = TEST_FAIL, err, i, nr_events = 0, nr_polls = 0; char sbuf[STRERR_BUFSIZE]; if (evlist == NULL) { @@ -51,6 +51,7 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused evsel = evsel__newtp("syscalls", "sys_enter_openat"); if (IS_ERR(evsel)) { pr_debug("%s: evsel__newtp\n", __func__); + ret = PTR_ERR(evsel) == -EACCES ? TEST_SKIP : TEST_FAIL; goto out_delete_evlist; } @@ -138,11 +139,21 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused } } out_ok: - err = 0; + ret = TEST_OK; out_delete_evlist: evlist__delete(evlist); out: - return err; + return ret; } -DEFINE_SUITE("syscalls:sys_enter_openat event fields", syscall_openat_tp_fields); +static struct test_case tests__syscall_openat_tp_fields[] = { + TEST_CASE_REASON("syscalls:sys_enter_openat event fields", + syscall_openat_tp_fields, + "permissions"), + { .name = NULL, } +}; + +struct test_suite suite__syscall_openat_tp_fields = { + .desc = "syscalls:sys_enter_openat event fields", + .test_cases = tests__syscall_openat_tp_fields, +}; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 9e3086d021508..82a19674a38f7 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -730,7 +730,7 @@ static int test__checkevent_pmu_events(struct evlist *evlist) TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type || - strcmp(evsel->pmu_name, "cpu")); + strcmp(evsel->pmu->name, "cpu")); TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", @@ -898,8 +898,7 @@ static int test__group1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); @@ -932,7 +931,7 @@ static int test__group2(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); @@ -947,7 +946,7 @@ static int test__group2(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); if (evsel__has_leader(evsel, leader)) @@ -1016,9 +1015,8 @@ static int test__group3(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ TEST_ASSERT_VAL("wrong exclude guest", - evsel->core.attr.exclude_guest); + !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", @@ -1072,7 +1070,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); @@ -1103,8 +1101,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 1); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1122,8 +1119,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - /* use of precise requires exclude_guest */ - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); @@ -1222,7 +1218,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); @@ -1437,7 +1433,7 @@ static int test__leader_sample1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1453,7 +1449,7 @@ static int test__leader_sample1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); @@ -1468,7 +1464,7 @@ static int test__leader_sample1(struct evlist *evlist) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1497,7 +1493,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -1513,7 +1509,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused) TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv); - TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index be18506f6a242..6a681e3fb552d 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -176,7 +176,8 @@ static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest } memset(&attr, 0, sizeof(attr)); - ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL); + ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, + /*apply_hardcoded=*/false, /*err=*/NULL); if (ret) { pr_err("perf_pmu__config_terms failed"); goto err_out; diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh index 2ccf4f1d46b60..1590a37363deb 100755 --- a/tools/perf/tests/shell/annotate.sh +++ b/tools/perf/tests/shell/annotate.sh @@ -44,7 +44,7 @@ test_basic() { fi # Generate the annotated output file - perf annotate -i "${perfdata}" --stdio 2> /dev/null > "${perfout}" + perf annotate --no-demangle -i "${perfdata}" --stdio 2> /dev/null | head -250 > "${perfout}" # check if it has the target symbol if ! grep "${testsym}" "${perfout}" @@ -63,8 +63,8 @@ test_basic() { fi # check again with a target symbol name - if ! perf annotate -i "${perfdata}" "${testsym}" 2> /dev/null | \ - grep -m 3 "${disasm_regex}" + if ! perf annotate --no-demangle -i "${perfdata}" "${testsym}" 2> /dev/null | \ + head -250 | grep -m 3 "${disasm_regex}" then echo "Basic annotate [Failed: missing disasm output when specifying the target symbol]" err=1 @@ -72,8 +72,8 @@ test_basic() { fi # check one more with external objdump tool (forced by --objdump option) - if ! perf annotate -i "${perfdata}" --objdump=objdump 2> /dev/null | \ - grep -m 3 "${disasm_regex}" + if ! perf annotate --no-demangle -i "${perfdata}" --objdump=objdump 2> /dev/null | \ + head -250 | grep -m 3 "${disasm_regex}" then echo "Basic annotate [Failed: missing disasm output from non default disassembler (using --objdump)]" err=1 diff --git a/tools/perf/tests/shell/attr.sh b/tools/perf/tests/shell/attr.sh new file mode 100755 index 0000000000000..5a4e43b2471de --- /dev/null +++ b/tools/perf/tests/shell/attr.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Perf attribute expectations test +# SPDX-License-Identifier: GPL-2.0 + +err=0 + +cleanup() { + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +shelldir=$(dirname "$0") +perf_path=$(which perf) +python "${shelldir}"/lib/attr.py -d "${shelldir}"/attr -v -p "$perf_path" +cleanup +exit $err diff --git a/tools/perf/tests/attr/README b/tools/perf/tests/shell/attr/README similarity index 95% rename from tools/perf/tests/attr/README rename to tools/perf/tests/shell/attr/README index 4066fec7180a8..67c4ca76b85d5 100644 --- a/tools/perf/tests/attr/README +++ b/tools/perf/tests/shell/attr/README @@ -51,6 +51,8 @@ Following tests are defined (with perf commands): perf record --call-graph fp kill (test-record-graph-fp-aarch64) perf record -e '{cycles,instructions}' kill (test-record-group1) perf record -e '{cycles/period=1/,instructions/period=2/}:S' kill (test-record-group2) + perf record -e '{cycles,cache-misses}:S' kill (test-record-group-sampling1) + perf record -c 10000 -e '{cycles,cache-misses}:S' kill (test-record-group-sampling2) perf record -D kill (test-record-no-delay) perf record -i kill (test-record-no-inherit) perf record -n kill (test-record-no-samples) diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/shell/attr/base-record similarity index 100% rename from tools/perf/tests/attr/base-record rename to tools/perf/tests/shell/attr/base-record diff --git a/tools/perf/tests/attr/base-record-spe b/tools/perf/tests/shell/attr/base-record-spe similarity index 100% rename from tools/perf/tests/attr/base-record-spe rename to tools/perf/tests/shell/attr/base-record-spe diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/shell/attr/base-stat similarity index 100% rename from tools/perf/tests/attr/base-stat rename to tools/perf/tests/shell/attr/base-stat diff --git a/tools/perf/tests/attr/system-wide-dummy b/tools/perf/tests/shell/attr/system-wide-dummy similarity index 100% rename from tools/perf/tests/attr/system-wide-dummy rename to tools/perf/tests/shell/attr/system-wide-dummy diff --git a/tools/perf/tests/attr/test-record-C0 b/tools/perf/tests/shell/attr/test-record-C0 similarity index 95% rename from tools/perf/tests/attr/test-record-C0 rename to tools/perf/tests/shell/attr/test-record-C0 index 198e8429a1bf8..1049ac8b52f27 100644 --- a/tools/perf/tests/attr/test-record-C0 +++ b/tools/perf/tests/shell/attr/test-record-C0 @@ -18,5 +18,7 @@ sample_type=65927 mmap=0 comm=0 task=0 +inherit=0 [event:system-wide-dummy] +inherit=0 diff --git a/tools/perf/tests/attr/test-record-basic b/tools/perf/tests/shell/attr/test-record-basic similarity index 100% rename from tools/perf/tests/attr/test-record-basic rename to tools/perf/tests/shell/attr/test-record-basic diff --git a/tools/perf/tests/attr/test-record-branch-any b/tools/perf/tests/shell/attr/test-record-branch-any similarity index 100% rename from tools/perf/tests/attr/test-record-branch-any rename to tools/perf/tests/shell/attr/test-record-branch-any diff --git a/tools/perf/tests/attr/test-record-branch-filter-any b/tools/perf/tests/shell/attr/test-record-branch-filter-any similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-any rename to tools/perf/tests/shell/attr/test-record-branch-filter-any diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_call b/tools/perf/tests/shell/attr/test-record-branch-filter-any_call similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-any_call rename to tools/perf/tests/shell/attr/test-record-branch-filter-any_call diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_ret b/tools/perf/tests/shell/attr/test-record-branch-filter-any_ret similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-any_ret rename to tools/perf/tests/shell/attr/test-record-branch-filter-any_ret diff --git a/tools/perf/tests/attr/test-record-branch-filter-hv b/tools/perf/tests/shell/attr/test-record-branch-filter-hv similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-hv rename to tools/perf/tests/shell/attr/test-record-branch-filter-hv diff --git a/tools/perf/tests/attr/test-record-branch-filter-ind_call b/tools/perf/tests/shell/attr/test-record-branch-filter-ind_call similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-ind_call rename to tools/perf/tests/shell/attr/test-record-branch-filter-ind_call diff --git a/tools/perf/tests/attr/test-record-branch-filter-k b/tools/perf/tests/shell/attr/test-record-branch-filter-k similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-k rename to tools/perf/tests/shell/attr/test-record-branch-filter-k diff --git a/tools/perf/tests/attr/test-record-branch-filter-u b/tools/perf/tests/shell/attr/test-record-branch-filter-u similarity index 100% rename from tools/perf/tests/attr/test-record-branch-filter-u rename to tools/perf/tests/shell/attr/test-record-branch-filter-u diff --git a/tools/perf/tests/attr/test-record-count b/tools/perf/tests/shell/attr/test-record-count similarity index 100% rename from tools/perf/tests/attr/test-record-count rename to tools/perf/tests/shell/attr/test-record-count diff --git a/tools/perf/tests/attr/test-record-data b/tools/perf/tests/shell/attr/test-record-data similarity index 100% rename from tools/perf/tests/attr/test-record-data rename to tools/perf/tests/shell/attr/test-record-data diff --git a/tools/perf/tests/attr/test-record-dummy-C0 b/tools/perf/tests/shell/attr/test-record-dummy-C0 similarity index 96% rename from tools/perf/tests/attr/test-record-dummy-C0 rename to tools/perf/tests/shell/attr/test-record-dummy-C0 index 576ec48b3aafa..91499405fff4d 100644 --- a/tools/perf/tests/attr/test-record-dummy-C0 +++ b/tools/perf/tests/shell/attr/test-record-dummy-C0 @@ -19,7 +19,7 @@ sample_period=4000 sample_type=391 read_format=4|20 disabled=0 -inherit=1 +inherit=0 pinned=0 exclusive=0 exclude_user=0 @@ -37,7 +37,7 @@ precise_ip=0 mmap_data=0 sample_id_all=1 exclude_host=0 -exclude_guest=1 +exclude_guest=0 exclude_callchain_kernel=0 exclude_callchain_user=0 mmap2=1 diff --git a/tools/perf/tests/attr/test-record-freq b/tools/perf/tests/shell/attr/test-record-freq similarity index 100% rename from tools/perf/tests/attr/test-record-freq rename to tools/perf/tests/shell/attr/test-record-freq diff --git a/tools/perf/tests/attr/test-record-graph-default b/tools/perf/tests/shell/attr/test-record-graph-default similarity index 100% rename from tools/perf/tests/attr/test-record-graph-default rename to tools/perf/tests/shell/attr/test-record-graph-default diff --git a/tools/perf/tests/attr/test-record-graph-default-aarch64 b/tools/perf/tests/shell/attr/test-record-graph-default-aarch64 similarity index 100% rename from tools/perf/tests/attr/test-record-graph-default-aarch64 rename to tools/perf/tests/shell/attr/test-record-graph-default-aarch64 diff --git a/tools/perf/tests/attr/test-record-graph-dwarf b/tools/perf/tests/shell/attr/test-record-graph-dwarf similarity index 100% rename from tools/perf/tests/attr/test-record-graph-dwarf rename to tools/perf/tests/shell/attr/test-record-graph-dwarf diff --git a/tools/perf/tests/attr/test-record-graph-fp b/tools/perf/tests/shell/attr/test-record-graph-fp similarity index 100% rename from tools/perf/tests/attr/test-record-graph-fp rename to tools/perf/tests/shell/attr/test-record-graph-fp diff --git a/tools/perf/tests/attr/test-record-graph-fp-aarch64 b/tools/perf/tests/shell/attr/test-record-graph-fp-aarch64 similarity index 100% rename from tools/perf/tests/attr/test-record-graph-fp-aarch64 rename to tools/perf/tests/shell/attr/test-record-graph-fp-aarch64 diff --git a/tools/perf/tests/attr/test-record-group-sampling b/tools/perf/tests/shell/attr/test-record-group-sampling similarity index 88% rename from tools/perf/tests/attr/test-record-group-sampling rename to tools/perf/tests/shell/attr/test-record-group-sampling index 97e7e64a38f07..86a940d7895d7 100644 --- a/tools/perf/tests/attr/test-record-group-sampling +++ b/tools/perf/tests/shell/attr/test-record-group-sampling @@ -2,6 +2,7 @@ command = record args = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 ret = 1 +kernel_until = 6.12 [event-1:base-record] fd=1 @@ -18,7 +19,7 @@ group_fd=1 type=0 config=3 -# default | PERF_SAMPLE_READ +# default | PERF_SAMPLE_READ | PERF_SAMPLE_PERIOD sample_type=343 # PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling1 b/tools/perf/tests/shell/attr/test-record-group-sampling1 new file mode 100644 index 0000000000000..4748ab7bf6843 --- /dev/null +++ b/tools/perf/tests/shell/attr/test-record-group-sampling1 @@ -0,0 +1,50 @@ +[config] +command = record +args = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 +ret = 1 +kernel_since = 6.12 + +[event-1:base-record] +fd=1 +group_fd=-1 + +# cycles +type=0 +config=0 + +# default | PERF_SAMPLE_READ | PERF_SAMPLE_PERIOD +sample_type=343 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=1 +mmap=1 +comm=1 +enable_on_exec=1 +disabled=1 + +# inherit is enabled for group sampling +inherit=1 + +[event-2:base-record] +fd=2 +group_fd=1 + +# cache-misses +type=0 +config=3 + +# default | PERF_SAMPLE_READ | PERF_SAMPLE_PERIOD +sample_type=343 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=0 +mmap=0 +comm=0 +enable_on_exec=0 +disabled=0 +freq=0 + +# inherit is enabled for group sampling +inherit=1 diff --git a/tools/perf/tests/shell/attr/test-record-group-sampling2 b/tools/perf/tests/shell/attr/test-record-group-sampling2 new file mode 100644 index 0000000000000..e0432244a0eb7 --- /dev/null +++ b/tools/perf/tests/shell/attr/test-record-group-sampling2 @@ -0,0 +1,61 @@ +[config] +command = record +args = --no-bpf-event -c 10000 -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 +ret = 1 +kernel_since = 6.12 + +[event-1:base-record] +fd=1 +group_fd=-1 + +# cycles +type=0 +config=0 + +# default | PERF_SAMPLE_READ +sample_type=87 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=1 +mmap=1 +comm=1 +enable_on_exec=1 +disabled=1 + +# inherit is enabled for group sampling +inherit=1 + +# sampling disabled +sample_freq=0 +sample_period=10000 +freq=0 +write_backward=0 + +[event-2:base-record] +fd=2 +group_fd=1 + +# cache-misses +type=0 +config=3 + +# default | PERF_SAMPLE_READ +sample_type=87 + +# PERF_FORMAT_ID | PERF_FORMAT_GROUP | PERF_FORMAT_LOST | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING +read_format=28|31 +task=0 +mmap=0 +comm=0 +enable_on_exec=0 +disabled=0 + +# inherit is enabled for group sampling +inherit=1 + +# sampling disabled +sample_freq=0 +sample_period=0 +freq=0 +write_backward=0 diff --git a/tools/perf/tests/attr/test-record-group1 b/tools/perf/tests/shell/attr/test-record-group1 similarity index 100% rename from tools/perf/tests/attr/test-record-group1 rename to tools/perf/tests/shell/attr/test-record-group1 diff --git a/tools/perf/tests/attr/test-record-group2 b/tools/perf/tests/shell/attr/test-record-group2 similarity index 95% rename from tools/perf/tests/attr/test-record-group2 rename to tools/perf/tests/shell/attr/test-record-group2 index cebdaa8e64e47..891d41a7bddf7 100644 --- a/tools/perf/tests/attr/test-record-group2 +++ b/tools/perf/tests/shell/attr/test-record-group2 @@ -2,6 +2,7 @@ command = record args = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1 ret = 1 +kernel_until = 6.12 [event-1:base-record] fd=1 diff --git a/tools/perf/tests/shell/attr/test-record-group3 b/tools/perf/tests/shell/attr/test-record-group3 new file mode 100644 index 0000000000000..249be884959ea --- /dev/null +++ b/tools/perf/tests/shell/attr/test-record-group3 @@ -0,0 +1,31 @@ +[config] +command = record +args = --no-bpf-event -e '{cycles/period=1234000/,instructions/period=6789000/}:S' kill >/dev/null 2>&1 +ret = 1 +kernel_since = 6.12 + +[event-1:base-record] +fd=1 +group_fd=-1 +config=0|1 +sample_period=1234000 +sample_type=87 +read_format=28|31 +disabled=1 +inherit=1 +freq=0 + +[event-2:base-record] +fd=2 +group_fd=1 +config=0|1 +sample_period=6789000 +sample_type=87 +read_format=28|31 +disabled=0 +inherit=1 +mmap=0 +comm=0 +freq=0 +enable_on_exec=0 +task=0 diff --git a/tools/perf/tests/attr/test-record-no-buffering b/tools/perf/tests/shell/attr/test-record-no-buffering similarity index 100% rename from tools/perf/tests/attr/test-record-no-buffering rename to tools/perf/tests/shell/attr/test-record-no-buffering diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/shell/attr/test-record-no-inherit similarity index 100% rename from tools/perf/tests/attr/test-record-no-inherit rename to tools/perf/tests/shell/attr/test-record-no-inherit diff --git a/tools/perf/tests/attr/test-record-no-samples b/tools/perf/tests/shell/attr/test-record-no-samples similarity index 100% rename from tools/perf/tests/attr/test-record-no-samples rename to tools/perf/tests/shell/attr/test-record-no-samples diff --git a/tools/perf/tests/attr/test-record-period b/tools/perf/tests/shell/attr/test-record-period similarity index 100% rename from tools/perf/tests/attr/test-record-period rename to tools/perf/tests/shell/attr/test-record-period diff --git a/tools/perf/tests/attr/test-record-pfm-period b/tools/perf/tests/shell/attr/test-record-pfm-period similarity index 100% rename from tools/perf/tests/attr/test-record-pfm-period rename to tools/perf/tests/shell/attr/test-record-pfm-period diff --git a/tools/perf/tests/attr/test-record-raw b/tools/perf/tests/shell/attr/test-record-raw similarity index 100% rename from tools/perf/tests/attr/test-record-raw rename to tools/perf/tests/shell/attr/test-record-raw diff --git a/tools/perf/tests/attr/test-record-spe-period b/tools/perf/tests/shell/attr/test-record-spe-period similarity index 100% rename from tools/perf/tests/attr/test-record-spe-period rename to tools/perf/tests/shell/attr/test-record-spe-period diff --git a/tools/perf/tests/attr/test-record-spe-period-term b/tools/perf/tests/shell/attr/test-record-spe-period-term similarity index 100% rename from tools/perf/tests/attr/test-record-spe-period-term rename to tools/perf/tests/shell/attr/test-record-spe-period-term diff --git a/tools/perf/tests/attr/test-record-spe-physical-address b/tools/perf/tests/shell/attr/test-record-spe-physical-address similarity index 100% rename from tools/perf/tests/attr/test-record-spe-physical-address rename to tools/perf/tests/shell/attr/test-record-spe-physical-address diff --git a/tools/perf/tests/attr/test-record-user-regs-no-sve-aarch64 b/tools/perf/tests/shell/attr/test-record-user-regs-no-sve-aarch64 similarity index 100% rename from tools/perf/tests/attr/test-record-user-regs-no-sve-aarch64 rename to tools/perf/tests/shell/attr/test-record-user-regs-no-sve-aarch64 diff --git a/tools/perf/tests/attr/test-record-user-regs-old-sve-aarch64 b/tools/perf/tests/shell/attr/test-record-user-regs-old-sve-aarch64 similarity index 100% rename from tools/perf/tests/attr/test-record-user-regs-old-sve-aarch64 rename to tools/perf/tests/shell/attr/test-record-user-regs-old-sve-aarch64 diff --git a/tools/perf/tests/attr/test-record-user-regs-sve-aarch64 b/tools/perf/tests/shell/attr/test-record-user-regs-sve-aarch64 similarity index 100% rename from tools/perf/tests/attr/test-record-user-regs-sve-aarch64 rename to tools/perf/tests/shell/attr/test-record-user-regs-sve-aarch64 diff --git a/tools/perf/tests/attr/test-stat-C0 b/tools/perf/tests/shell/attr/test-stat-C0 similarity index 100% rename from tools/perf/tests/attr/test-stat-C0 rename to tools/perf/tests/shell/attr/test-stat-C0 diff --git a/tools/perf/tests/attr/test-stat-basic b/tools/perf/tests/shell/attr/test-stat-basic similarity index 100% rename from tools/perf/tests/attr/test-stat-basic rename to tools/perf/tests/shell/attr/test-stat-basic diff --git a/tools/perf/tests/attr/test-stat-default b/tools/perf/tests/shell/attr/test-stat-default similarity index 81% rename from tools/perf/tests/attr/test-stat-default rename to tools/perf/tests/shell/attr/test-stat-default index a1e2da0a9a6dd..e47fb49446799 100644 --- a/tools/perf/tests/attr/test-stat-default +++ b/tools/perf/tests/shell/attr/test-stat-default @@ -88,98 +88,142 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 diff --git a/tools/perf/tests/attr/test-stat-detailed-1 b/tools/perf/tests/shell/attr/test-stat-detailed-1 similarity index 85% rename from tools/perf/tests/attr/test-stat-detailed-1 rename to tools/perf/tests/shell/attr/test-stat-detailed-1 index 1c52cb05c900d..3d500d3e0c5c8 100644 --- a/tools/perf/tests/attr/test-stat-detailed-1 +++ b/tools/perf/tests/shell/attr/test-stat-detailed-1 @@ -90,99 +90,143 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 @@ -190,8 +234,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event25:base-stat] -fd=25 +[event29:base-stat] +fd=29 type=3 config=0 optional=1 @@ -200,8 +244,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event26:base-stat] -fd=26 +[event30:base-stat] +fd=30 type=3 config=65536 optional=1 @@ -210,8 +254,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event27:base-stat] -fd=27 +[event31:base-stat] +fd=31 type=3 config=2 optional=1 @@ -220,8 +264,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event28:base-stat] -fd=28 +[event32:base-stat] +fd=32 type=3 config=65538 optional=1 diff --git a/tools/perf/tests/attr/test-stat-detailed-2 b/tools/perf/tests/shell/attr/test-stat-detailed-2 similarity index 88% rename from tools/perf/tests/attr/test-stat-detailed-2 rename to tools/perf/tests/shell/attr/test-stat-detailed-2 index 7e961d24a885a..01777a63752fe 100644 --- a/tools/perf/tests/attr/test-stat-detailed-2 +++ b/tools/perf/tests/shell/attr/test-stat-detailed-2 @@ -90,99 +90,143 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 @@ -190,8 +234,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event25:base-stat] -fd=25 +[event29:base-stat] +fd=29 type=3 config=0 optional=1 @@ -200,8 +244,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event26:base-stat] -fd=26 +[event30:base-stat] +fd=30 type=3 config=65536 optional=1 @@ -210,8 +254,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event27:base-stat] -fd=27 +[event31:base-stat] +fd=31 type=3 config=2 optional=1 @@ -220,8 +264,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event28:base-stat] -fd=28 +[event32:base-stat] +fd=32 type=3 config=65538 optional=1 @@ -230,8 +274,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event29:base-stat] -fd=29 +[event33:base-stat] +fd=33 type=3 config=1 optional=1 @@ -240,8 +284,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event30:base-stat] -fd=30 +[event34:base-stat] +fd=34 type=3 config=65537 optional=1 @@ -250,8 +294,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event31:base-stat] -fd=31 +[event35:base-stat] +fd=35 type=3 config=3 optional=1 @@ -260,8 +304,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event32:base-stat] -fd=32 +[event36:base-stat] +fd=36 type=3 config=65539 optional=1 @@ -270,8 +314,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event33:base-stat] -fd=33 +[event37:base-stat] +fd=37 type=3 config=4 optional=1 @@ -280,8 +324,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event34:base-stat] -fd=34 +[event38:base-stat] +fd=38 type=3 config=65540 optional=1 diff --git a/tools/perf/tests/attr/test-stat-detailed-3 b/tools/perf/tests/shell/attr/test-stat-detailed-3 similarity index 89% rename from tools/perf/tests/attr/test-stat-detailed-3 rename to tools/perf/tests/shell/attr/test-stat-detailed-3 index e50535f45977c..8400abd7e1e48 100644 --- a/tools/perf/tests/attr/test-stat-detailed-3 +++ b/tools/perf/tests/shell/attr/test-stat-detailed-3 @@ -90,99 +90,143 @@ enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-fe-bound (0x8200) +# PERF_TYPE_RAW / topdown-bad-spec (0x8100) [event13:base-stat] fd=13 group_fd=11 type=4 -config=33280 +config=33024 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-be-bound (0x8300) +# PERF_TYPE_RAW / topdown-fe-bound (0x8200) [event14:base-stat] fd=14 group_fd=11 type=4 -config=33536 +config=33280 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / topdown-bad-spec (0x8100) +# PERF_TYPE_RAW / topdown-be-bound (0x8300) [event15:base-stat] fd=15 group_fd=11 type=4 -config=33024 +config=33536 disabled=0 enable_on_exec=0 read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING +# PERF_TYPE_RAW / topdown-heavy-ops (0x8400) [event16:base-stat] fd=16 +group_fd=11 type=4 -config=4109 +config=33792 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ +# PERF_TYPE_RAW / topdown-br-mispredict (0x8500) [event17:base-stat] fd=17 +group_fd=11 type=4 -config=17039629 +config=34048 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD +# PERF_TYPE_RAW / topdown-fetch-lat (0x8600) [event18:base-stat] fd=18 +group_fd=11 type=4 -config=60 +config=34304 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY +# PERF_TYPE_RAW / topdown-mem-bound (0x8700) [event19:base-stat] fd=19 +group_fd=11 type=4 -config=2097421 +config=34560 +disabled=0 +enable_on_exec=0 +read_format=15 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK +# PERF_TYPE_RAW / INT_MISC.UOP_DROPPING [event20:base-stat] fd=20 type=4 -config=316 +config=4109 optional=1 -# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +# PERF_TYPE_RAW / cpu/INT_MISC.RECOVERY_CYCLES,cmask=1,edge/ [event21:base-stat] fd=21 type=4 -config=412 +config=17039629 optional=1 -# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.THREAD [event22:base-stat] fd=22 type=4 -config=572 +config=60 optional=1 -# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +# PERF_TYPE_RAW / INT_MISC.RECOVERY_CYCLES_ANY [event23:base-stat] fd=23 type=4 -config=706 +config=2097421 optional=1 -# PERF_TYPE_RAW / UOPS_ISSUED.ANY +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.REF_XCLK [event24:base-stat] fd=24 type=4 +config=316 +optional=1 + +# PERF_TYPE_RAW / IDQ_UOPS_NOT_DELIVERED.CORE +[event25:base-stat] +fd=25 +type=4 +config=412 +optional=1 + +# PERF_TYPE_RAW / CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE +[event26:base-stat] +fd=26 +type=4 +config=572 +optional=1 + +# PERF_TYPE_RAW / UOPS_RETIRED.RETIRE_SLOTS +[event27:base-stat] +fd=27 +type=4 +config=706 +optional=1 + +# PERF_TYPE_RAW / UOPS_ISSUED.ANY +[event28:base-stat] +fd=28 +type=4 config=270 optional=1 @@ -190,8 +234,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event25:base-stat] -fd=25 +[event29:base-stat] +fd=29 type=3 config=0 optional=1 @@ -200,8 +244,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event26:base-stat] -fd=26 +[event30:base-stat] +fd=30 type=3 config=65536 optional=1 @@ -210,8 +254,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event27:base-stat] -fd=27 +[event31:base-stat] +fd=31 type=3 config=2 optional=1 @@ -220,8 +264,8 @@ optional=1 # PERF_COUNT_HW_CACHE_LL << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event28:base-stat] -fd=28 +[event32:base-stat] +fd=32 type=3 config=65538 optional=1 @@ -230,8 +274,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event29:base-stat] -fd=29 +[event33:base-stat] +fd=33 type=3 config=1 optional=1 @@ -240,8 +284,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1I << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event30:base-stat] -fd=30 +[event34:base-stat] +fd=34 type=3 config=65537 optional=1 @@ -250,8 +294,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event31:base-stat] -fd=31 +[event35:base-stat] +fd=35 type=3 config=3 optional=1 @@ -260,8 +304,8 @@ optional=1 # PERF_COUNT_HW_CACHE_DTLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event32:base-stat] -fd=32 +[event36:base-stat] +fd=36 type=3 config=65539 optional=1 @@ -270,8 +314,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event33:base-stat] -fd=33 +[event37:base-stat] +fd=37 type=3 config=4 optional=1 @@ -280,8 +324,8 @@ optional=1 # PERF_COUNT_HW_CACHE_ITLB << 0 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event34:base-stat] -fd=34 +[event38:base-stat] +fd=38 type=3 config=65540 optional=1 @@ -290,8 +334,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) -[event35:base-stat] -fd=35 +[event39:base-stat] +fd=39 type=3 config=512 optional=1 @@ -300,8 +344,8 @@ optional=1 # PERF_COUNT_HW_CACHE_L1D << 0 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) -[event36:base-stat] -fd=36 +[event40:base-stat] +fd=40 type=3 config=66048 optional=1 diff --git a/tools/perf/tests/attr/test-stat-group1 b/tools/perf/tests/shell/attr/test-stat-group1 similarity index 100% rename from tools/perf/tests/attr/test-stat-group1 rename to tools/perf/tests/shell/attr/test-stat-group1 diff --git a/tools/perf/tests/attr/test-stat-no-inherit b/tools/perf/tests/shell/attr/test-stat-no-inherit similarity index 100% rename from tools/perf/tests/attr/test-stat-no-inherit rename to tools/perf/tests/shell/attr/test-stat-no-inherit diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop.sh b/tools/perf/tests/shell/coresight/asm_pure_loop.sh index 2d65defb7e0f1..c63bc8c73e262 100755 --- a/tools/perf/tests/shell/coresight/asm_pure_loop.sh +++ b/tools/perf/tests/shell/coresight/asm_pure_loop.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / ASM Pure Loop +# CoreSight / ASM Pure Loop (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler , 2021 diff --git a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh index ddcc9bb850f5f..8e29630957c8d 100755 --- a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh +++ b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Memcpy 16k 10 Threads +# CoreSight / Memcpy 16k 10 Threads (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler , 2021 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh index 2ce5e139b2fdd..0c4c82a1c8e15 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Thread Loop 10 Threads - Check TID +# CoreSight / Thread Loop 10 Threads - Check TID (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler , 2021 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh index 3ad9498753d7b..d3aea9fc6ced9 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Thread Loop 2 Threads - Check TID +# CoreSight / Thread Loop 2 Threads - Check TID (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler , 2021 diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh index 4fbb4a29aad3f..7429d3a2ae436 100755 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh +++ b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -# CoreSight / Unroll Loop Thread 10 +# CoreSight / Unroll Loop Thread 10 (exclusive) # SPDX-License-Identifier: GPL-2.0 # Carsten Haitzler , 2021 diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh index a6ee740f0d7ec..2df05052c324a 100755 --- a/tools/perf/tests/shell/ftrace.sh +++ b/tools/perf/tests/shell/ftrace.sh @@ -67,12 +67,12 @@ test_ftrace_latency() { test_ftrace_profile() { echo "perf ftrace profile test" - perf ftrace profile sleep 0.1 > "${output}" + perf ftrace profile -m 16M sleep 0.1 > "${output}" grep ^# "${output}" grep sleep "${output}" grep schedule "${output}" grep execve "${output}" - time_re="[[:space:]]+10[[:digit:]]{4}\.[[:digit:]]{3}" + time_re="[[:space:]]+1[[:digit:]]{5}\.[[:digit:]]{3}" # 100283.000 100283.000 100283.000 1 __x64_sys_clock_nanosleep # Check for one *clock_nanosleep line with a Count of just 1 that takes a bit more than 0.1 seconds # Strip the _x64_sys part to work with other architectures diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/shell/lib/attr.py similarity index 94% rename from tools/perf/tests/attr.py rename to tools/perf/tests/shell/lib/attr.py index e890c261ad269..3db9a7d78715f 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/shell/lib/attr.py @@ -246,6 +246,23 @@ def skip_test_arch(self, myarch): return False return True + def restore_sample_rate(self, value=10000): + try: + # Check value of sample_rate + with open("/proc/sys/kernel/perf_event_max_sample_rate", "r") as fIn: + curr_value = fIn.readline() + # If too low restore to reasonable value + if not curr_value or int(curr_value) < int(value): + with open("/proc/sys/kernel/perf_event_max_sample_rate", "w") as fOut: + fOut.write(str(value)) + + except IOError as e: + log.warning("couldn't restore sample_rate value: I/O error %s" % e) + except ValueError as e: + log.warning("couldn't restore sample_rate value: Value error %s" % e) + except TypeError as e: + log.warning("couldn't restore sample_rate value: Type error %s" % e) + def load_events(self, path, events): parser_event = configparser.ConfigParser() parser_event.read(path) @@ -283,6 +300,7 @@ def run_cmd(self, tempdir): if self.skip_test_kernel_until(): raise Notest(self, "new kernel skip") + self.restore_sample_rate() cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir, self.perf, self.command, tempdir, self.args) ret = os.WEXITSTATUS(os.system(cmd)) diff --git a/tools/perf/tests/shell/lib/coresight.sh b/tools/perf/tests/shell/lib/coresight.sh index 11ed2c25ed912..184d62e7e5bdf 100644 --- a/tools/perf/tests/shell/lib/coresight.sh +++ b/tools/perf/tests/shell/lib/coresight.sh @@ -18,7 +18,7 @@ BIN="$DIR/$TEST" # If the test tool/binary does not exist and is executable then skip the test if ! test -x "$BIN"; then exit 2; fi # If CoreSight is not available, skip the test -perf list cs_etm | grep -q cs_etm || exit 2 +perf list pmu | grep -q cs_etm || exit 2 DATD="." # If the data dir env is set then make the data dir use that instead of ./ if test -n "$PERF_TEST_CORESIGHT_DATADIR"; then diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py index abc1fd7377822..8ddb855861319 100644 --- a/tools/perf/tests/shell/lib/perf_json_output_lint.py +++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py @@ -57,6 +57,7 @@ def check_json_output(expected_items): 'interval': lambda x: isfloat(x), 'metric-unit': lambda x: True, 'metric-value': lambda x: isfloat(x), + 'metric-threshold': lambda x: x in ['unknown', 'good', 'less good', 'nearly bad', 'bad'], 'metricgroup': lambda x: True, 'node': lambda x: True, 'pcnt-running': lambda x: isfloat(x), @@ -68,13 +69,15 @@ def check_json_output(expected_items): for item in json.loads(input): if expected_items != -1: count = len(item) - if count != expected_items and count >= 1 and count <= 6 and 'metric-value' in item: + if count != expected_items and count >= 1 and count <= 7 and 'metric-value' in item: # Events that generate >1 metric may have isolated metric # values and possibly other prefixes like interval, core, # aggregate-number, or event-runtime/pcnt-running from multiplexing. pass elif count != expected_items and count >= 1 and count <= 5 and 'metricgroup' in item: pass + elif count == expected_items + 1 and 'metric-threshold' in item: + pass elif count != expected_items: raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}' f' in \'{item}\'') diff --git a/tools/perf/tests/shell/list.sh b/tools/perf/tests/shell/list.sh index 8a868ae64560e..76a9846cff223 100755 --- a/tools/perf/tests/shell/list.sh +++ b/tools/perf/tests/shell/list.sh @@ -24,8 +24,11 @@ trap trap_cleanup EXIT TERM INT test_list_json() { echo "Json output test" + # Generate perf list json output into list_output file. perf list -j -o "${list_output}" - $PYTHON -m json.tool "${list_output}" + # Validate the json using python, redirect the json copy to /dev/null as + # otherwise the test may block writing to stdout. + $PYTHON -m json.tool "${list_output}" /dev/null echo "Json output test [Success]" } diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index c1ec5762215ba..30d195d4c62f7 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -27,7 +27,7 @@ check() { exit fi - if ! perf list | grep -q lock:contention_begin; then + if ! perf list tracepoint | grep -q lock:contention_begin; then echo "[Skip] No lock contention tracepoints" err=2 exit diff --git a/tools/perf/tests/shell/perftool-testsuite_report.sh b/tools/perf/tests/shell/perftool-testsuite_report.sh index 973012ce92a76..a8cf75b4e77ec 100755 --- a/tools/perf/tests/shell/perftool-testsuite_report.sh +++ b/tools/perf/tests/shell/perftool-testsuite_report.sh @@ -1,5 +1,5 @@ #!/bin/bash -# perftool-testsuite_report +# perftool-testsuite_report (exclusive) # SPDX-License-Identifier: GPL-2.0 test -d "$(dirname "$0")/base_report" || exit 2 diff --git a/tools/perf/tests/shell/pipe_test.sh b/tools/perf/tests/shell/pipe_test.sh index d4c8005ce9b96..e459aa99a9515 100755 --- a/tools/perf/tests/shell/pipe_test.sh +++ b/tools/perf/tests/shell/pipe_test.sh @@ -13,6 +13,7 @@ skip_test_missing_symbol ${sym} data=$(mktemp /tmp/perf.data.XXXXXX) data2=$(mktemp /tmp/perf.data2.XXXXXX) prog="perf test -w noploop" +[ "$(uname -m)" = "s390x" ] && prog="$prog 3" err=0 set -e diff --git a/tools/perf/tests/shell/probe_vfs_getname.sh b/tools/perf/tests/shell/probe_vfs_getname.sh index 554e12e83c55f..0c5aacc446b3e 100755 --- a/tools/perf/tests/shell/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/probe_vfs_getname.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Add vfs_getname probe to get syscall args filenames +# Add vfs_getname probe to get syscall args filenames (exclusive) # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo , 2017 diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index f38c8ead0b03e..47a26f25db9fe 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -40,8 +40,8 @@ trace_libc_inet_pton_backtrace() { case "$(uname -m)" in s390x) eventattr='call-graph=dwarf,max-stack=4' - echo "(__GI_)?getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected - echo "main\+0x[[:xdigit:]]+[[:space:]]\(.*/bin/ping.*\)$" >> $expected + echo "((__GI_)?getaddrinfo|text_to_binary_address)\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected + echo "(gaih_inet|main)\+0x[[:xdigit:]]+[[:space:]]\(inlined|.*/bin/ping.*\)$" >> $expected ;; ppc64|ppc64le) eventattr='max-stack=4' diff --git a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh index 9a61928e3c9aa..5940fdc1df37c 100755 --- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh +++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Use vfs_getname probe to get syscall args filenames +# Use vfs_getname probe to get syscall args filenames (exclusive) # Uses the 'perf test shell' library to add probe:vfs_getname to the system # then use it with 'perf record' using 'touch' to write to a temp file, then diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh index 048078ee2eca5..0fc7a909ae9b5 100755 --- a/tools/perf/tests/shell/record.sh +++ b/tools/perf/tests/shell/record.sh @@ -1,5 +1,5 @@ #!/bin/bash -# perf record tests +# perf record tests (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e @@ -17,6 +17,7 @@ skip_test_missing_symbol ${testsym} err=0 perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) +script_output=$(mktemp /tmp/__perf_test.perf.data.XXXXX.script) testprog="perf test -w thloop" cpu_pmu_dir="/sys/bus/event_source/devices/cpu*" br_cntr_file="/caps/branch_counter_nr" @@ -93,7 +94,7 @@ test_per_thread() { test_register_capture() { echo "Register capture test" - if ! perf list | grep -q 'br_inst_retired.near_call' + if ! perf list pmu | grep -q 'br_inst_retired.near_call' then echo "Register capture test [Skipped missing event]" return @@ -228,6 +229,73 @@ test_cgroup() { echo "Cgroup sampling test [Success]" } +test_leader_sampling() { + echo "Basic leader sampling test" + if ! perf record -o "${perfdata}" -e "{instructions,instructions}:Su" -- \ + perf test -w brstack 2> /dev/null + then + echo "Leader sampling [Failed record]" + err=1 + return + fi + index=0 + perf script -i "${perfdata}" > $script_output + while IFS= read -r line + do + # Check if the two instruction counts are equal in each record + instructions=$(echo $line | awk '{for(i=1;i<=NF;i++) if($i=="instructions:") print $(i-1)}') + if [ $(($index%2)) -ne 0 ] && [ ${instructions}x != ${prev_instructions}x ] + then + echo "Leader sampling [Failed inconsistent instructions count]" + err=1 + return + fi + index=$(($index+1)) + prev_instructions=$instructions + done < $script_output + echo "Basic leader sampling test [Success]" +} + +test_topdown_leader_sampling() { + echo "Topdown leader sampling test" + if ! perf stat -e "{slots,topdown-retiring}" true 2> /dev/null + then + echo "Topdown leader sampling [Skipped event parsing failed]" + return + fi + if ! perf record -o "${perfdata}" -e "{instructions,slots,topdown-retiring}:S" true 2> /dev/null + then + echo "Topdown leader sampling [Failed topdown events not reordered correctly]" + err=1 + return + fi + echo "Topdown leader sampling test [Success]" +} + +test_precise_max() { + echo "precise_max attribute test" + if ! perf stat -e "cycles,instructions" true 2> /dev/null + then + echo "precise_max attribute [Skipped no hardware events]" + return + fi + # Just to make sure it doesn't fail + if ! perf record -o "${perfdata}" -e "cycles:P" true 2> /dev/null + then + echo "precise_max attribute [Failed cycles:P event]" + err=1 + return + fi + # On AMD, cycles and instructions events are treated differently + if ! perf record -o "${perfdata}" -e "instructions:P" true 2> /dev/null + then + echo "precise_max attribute [Failed instructions:P event]" + err=1 + return + fi + echo "precise_max attribute test [Success]" +} + # raise the limit of file descriptors to minimum if [[ $default_fd_limit -lt $min_fd_limit ]]; then ulimit -Sn $min_fd_limit @@ -239,6 +307,9 @@ test_system_wide test_workload test_branch_counter test_cgroup +test_leader_sampling +test_topdown_leader_sampling +test_precise_max # restore the default value ulimit -Sn $default_fd_limit diff --git a/tools/perf/tests/shell/record_lbr.sh b/tools/perf/tests/shell/record_lbr.sh index 32314641217e6..8d750ee631f87 100755 --- a/tools/perf/tests/shell/record_lbr.sh +++ b/tools/perf/tests/shell/record_lbr.sh @@ -1,5 +1,5 @@ #!/bin/bash -# perf record LBR tests +# perf record LBR tests (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e diff --git a/tools/perf/tests/shell/record_offcpu.sh b/tools/perf/tests/shell/record_offcpu.sh index 67c925f3a15aa..678947fe69eed 100755 --- a/tools/perf/tests/shell/record_offcpu.sh +++ b/tools/perf/tests/shell/record_offcpu.sh @@ -1,5 +1,5 @@ #!/bin/sh -# perf record offcpu profiling tests +# perf record offcpu profiling tests (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e diff --git a/tools/perf/tests/shell/stat.sh b/tools/perf/tests/shell/stat.sh index 3f1e67795490a..7a8adf81e4b39 100755 --- a/tools/perf/tests/shell/stat.sh +++ b/tools/perf/tests/shell/stat.sh @@ -73,9 +73,33 @@ test_topdown_groups() { err=1 return fi - if perf stat -e '{topdown-retiring,slots}' true 2>&1 | grep -E -q "" + if perf stat -e 'instructions,topdown-retiring,slots' true 2>&1 | grep -E -q "" then - echo "Topdown event group test [Failed slots not reordered first]" + echo "Topdown event group test [Failed slots not reordered first in no-group case]" + err=1 + return + fi + if perf stat -e '{instructions,topdown-retiring,slots}' true 2>&1 | grep -E -q "" + then + echo "Topdown event group test [Failed slots not reordered first in single group case]" + err=1 + return + fi + if perf stat -e '{instructions,slots},topdown-retiring' true 2>&1 | grep -E -q "" + then + echo "Topdown event group test [Failed topdown metrics event not move into slots group]" + err=1 + return + fi + if perf stat -e '{instructions,slots},{topdown-retiring}' true 2>&1 | grep -E -q "" + then + echo "Topdown event group test [Failed topdown metrics group not merge into slots group]" + err=1 + return + fi + if perf stat -e '{instructions,r400,r8000}' true 2>&1 | grep -E -q "" + then + echo "Topdown event group test [Failed raw format slots not reordered first]" err=1 return fi @@ -117,16 +141,18 @@ test_cputype() { # Find a known PMU for cputype. pmu="" - for i in cpu cpu_atom armv8_pmuv3_0 + devs="/sys/bus/event_source/devices" + for i in $devs/cpu $devs/cpu_atom $devs/armv8_pmuv3_0 $devs/armv8_cortex_* do - if test -d "/sys/devices/$i" + i_base=$(basename "$i") + if test -d "$i" then - pmu="$i" + pmu="$i_base" break fi - if perf stat -e "$i/instructions/" true > /dev/null 2>&1 + if perf stat -e "$i_base/instructions/" true > /dev/null 2>&1 then - pmu="$i" + pmu="$i_base" break fi done @@ -146,6 +172,30 @@ test_cputype() { echo "cputype test [Success]" } +test_hybrid() { + # Test the default stat command on hybrid devices opens one cycles event for + # each CPU type. + echo "hybrid test" + + # Count the number of core PMUs, assume minimum of 1 + pmus=$(ls /sys/bus/event_source/devices/*/cpus 2>/dev/null | wc -l) + if [ "$pmus" -lt 1 ] + then + pmus=1 + fi + + # Run default Perf stat + cycles_events=$(perf stat -- true 2>&1 | grep -E "/cycles/[uH]*| cycles[:uH]* " -c) + + if [ "$pmus" -ne "$cycles_events" ] + then + echo "hybrid test [Found $pmus PMUs but $cycles_events cycles events. Failed]" + err=1 + return + fi + echo "hybrid test [Success]" +} + test_default_stat test_stat_record_report test_stat_record_script @@ -153,4 +203,5 @@ test_stat_repeat_weak_groups test_topdown_groups test_topdown_weak_groups test_cputype +test_hybrid exit $err diff --git a/tools/perf/tests/shell/stat_all_metricgroups.sh b/tools/perf/tests/shell/stat_all_metricgroups.sh index 55ef9c9ded2d7..c6d61a4ac3e7d 100755 --- a/tools/perf/tests/shell/stat_all_metricgroups.sh +++ b/tools/perf/tests/shell/stat_all_metricgroups.sh @@ -1,9 +1,7 @@ -#!/bin/sh +#!/bin/bash # perf all metricgroups test # SPDX-License-Identifier: GPL-2.0 -set -e - ParanoidAndNotRoot() { [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] @@ -14,11 +12,37 @@ if ParanoidAndNotRoot 0 then system_wide_flag="" fi - +err=0 for m in $(perf list --raw-dump metricgroups) do echo "Testing $m" - perf stat -M "$m" $system_wide_flag sleep 0.01 + result=$(perf stat -M "$m" $system_wide_flag sleep 0.01 2>&1) + result_err=$? + if [[ $result_err -gt 0 ]] + then + if [[ "$result" =~ \ + "Access to performance monitoring and observability operations is limited" ]] + then + echo "Permission failure" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + elif [[ "$result" =~ "in per-thread mode, enable system wide" ]] + then + echo "Permissions - need system wide mode" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + else + echo "Metric group $m failed" + echo $result + err=1 # Fail + fi + fi done -exit 0 +exit $err diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh index 54774525e18a7..73e9347e88a96 100755 --- a/tools/perf/tests/shell/stat_all_metrics.sh +++ b/tools/perf/tests/shell/stat_all_metrics.sh @@ -2,42 +2,87 @@ # perf all metrics test # SPDX-License-Identifier: GPL-2.0 +ParanoidAndNotRoot() +{ + [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] +} + +system_wide_flag="-a" +if ParanoidAndNotRoot 0 +then + system_wide_flag="" +fi + err=0 for m in $(perf list --raw-dump metrics); do echo "Testing $m" - result=$(perf stat -M "$m" true 2>&1) - if [[ "$result" =~ ${m:0:50} ]] || [[ "$result" =~ "" ]] + result=$(perf stat -M "$m" $system_wide_flag -- sleep 0.01 2>&1) + result_err=$? + if [[ $result_err -gt 0 ]] then - continue + if [[ "$result" =~ \ + "Access to performance monitoring and observability operations is limited" ]] + then + echo "Permission failure" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "in per-thread mode, enable system wide" ]] + then + echo "Permissions - need system wide mode" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "" ]] + then + echo "Not supported events" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]] + then + echo "FP issues" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + elif [[ "$result" =~ "PMM" ]] + then + echo "Optane memory issues" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + continue + fi fi - # Failed so try system wide. - result=$(perf stat -M "$m" -a sleep 0.01 2>&1) + if [[ "$result" =~ ${m:0:50} ]] then continue fi - # Failed again, possibly the workload was too small so retry with something - # longer. - result=$(perf stat -M "$m" perf bench internals synthesize 2>&1) + + # Failed, possibly the workload was too small so retry with something longer. + result=$(perf stat -M "$m" $system_wide_flag -- perf bench internals synthesize 2>&1) if [[ "$result" =~ ${m:0:50} ]] then continue fi echo "Metric '$m' not printed in:" echo "$result" - if [[ "$err" != "1" ]] - then - err=2 - if [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]] - then - echo "Skip, not fail, for FP issues" - elif [[ "$result" =~ "PMM" ]] - then - echo "Skip, not fail, for Optane memory issues" - else - err=1 - fi - fi + err=1 done exit "$err" diff --git a/tools/perf/tests/shell/stat_all_pmu.sh b/tools/perf/tests/shell/stat_all_pmu.sh index d2a3506e0d196..8b148b300be11 100755 --- a/tools/perf/tests/shell/stat_all_pmu.sh +++ b/tools/perf/tests/shell/stat_all_pmu.sh @@ -1,23 +1,51 @@ -#!/bin/sh -# perf all PMU test +#!/bin/bash +# perf all PMU test (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e +err=0 +result="" + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + echo "$result" + exit 1 +} +trap trap_cleanup EXIT TERM INT # Test all PMU events; however exclude parameterized ones (name contains '?') -for p in $(perf list --raw-dump pmu | sed 's/[[:graph:]]\+?[[:graph:]]\+[[:space:]]//g'); do +for p in $(perf list --raw-dump pmu | sed 's/[[:graph:]]\+?[[:graph:]]\+[[:space:]]//g') +do echo "Testing $p" result=$(perf stat -e "$p" true 2>&1) - if ! echo "$result" | grep -q "$p" && ! echo "$result" | grep -q "" ; then - # We failed to see the event and it is supported. Possibly the workload was - # too small so retry with something longer. - result=$(perf stat -e "$p" perf bench internals synthesize 2>&1) - if ! echo "$result" | grep -q "$p" ; then - echo "Event '$p' not printed in:" - echo "$result" - exit 1 - fi + if echo "$result" | grep -q "$p" + then + # Event seen in output. + continue + fi + if echo "$result" | grep -q "" + then + # Event not supported, so ignore. + continue + fi + if echo "$result" | grep -q "Access to performance monitoring and observability operations is limited." + then + # Access is limited, so ignore. + continue + fi + + # We failed to see the event and it is supported. Possibly the workload was + # too small so retry with something longer. + result=$(perf stat -e "$p" perf bench internals synthesize 2>&1) + if echo "$result" | grep -q "$p" + then + # Event seen in output. + continue fi + echo "Error: event '$p' not printed in:" + echo "$result" + err=1 done -exit 0 +trap - EXIT TERM INT +exit $err diff --git a/tools/perf/tests/shell/stat_bpf_counters.sh b/tools/perf/tests/shell/stat_bpf_counters.sh index f250b7d6f773f..95d2ad5d17c67 100755 --- a/tools/perf/tests/shell/stat_bpf_counters.sh +++ b/tools/perf/tests/shell/stat_bpf_counters.sh @@ -1,10 +1,10 @@ #!/bin/sh -# perf stat --bpf-counters test +# perf stat --bpf-counters test (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e -workload="perf test -w brstack" +workload="perf test -w sqrtloop" # check whether $2 is within +/- 20% of $1 compare_number() diff --git a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh index e75d0780dc788..2ec69060c42ff 100755 --- a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh +++ b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh @@ -58,22 +58,9 @@ check_system_wide_counted() fi } -check_cpu_list_counted() -{ - check_cpu_list_counted_output=$(perf stat -C 0,1 --bpf-counters --for-each-cgroup ${test_cgroups} -e cpu-clock -x, taskset -c 1 sleep 1 2>&1) - if echo ${check_cpu_list_counted_output} | grep -q -F " /dev/null 2>&1 + perf script -i ${perfdata} -s python:${script_path} -- \ + -d --stop-sample=30 2> /dev/null > ${file} + grep -q -e ${branch_search} ${file} + echo "Found kernel branches" +else + # kcore is required for correct kernel decode due to runtime code patching + echo "No kcore, skipping kernel test" +fi + +## Test user ## +echo "Testing userspace disassembly" +perf record -o ${perfdata} -e cs_etm//u -- touch $file > /dev/null 2>&1 +perf script -i ${perfdata} -s python:${script_path} -- \ + -d --stop-sample=30 2> /dev/null > ${file} +grep -q -e ${branch_search} ${file} +echo "Found userspace branches" + +glb_err=0 diff --git a/tools/perf/tests/shell/test_arm_spe.sh b/tools/perf/tests/shell/test_arm_spe.sh index 03d5c7d12ee53..3258368634f79 100755 --- a/tools/perf/tests/shell/test_arm_spe.sh +++ b/tools/perf/tests/shell/test_arm_spe.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Check Arm SPE trace data recording and synthesized samples +# Check Arm SPE trace data recording and synthesized samples (exclusive) # Uses the 'perf record' to record trace data of Arm SPE events; # then verify if any SPE event samples are generated by SPE with @@ -9,7 +9,7 @@ # German Gomez , 2021 skip_if_no_arm_spe_event() { - perf list | grep -E -q 'arm_spe_[0-9]+//' && return 0 + perf list pmu | grep -E -q 'arm_spe_[0-9]+//' && return 0 # arm_spe event doesn't exist return 2 diff --git a/tools/perf/tests/shell/test_arm_spe_fork.sh b/tools/perf/tests/shell/test_arm_spe_fork.sh index 1a7e6a82d0e34..8efeef9fb9564 100755 --- a/tools/perf/tests/shell/test_arm_spe_fork.sh +++ b/tools/perf/tests/shell/test_arm_spe_fork.sh @@ -5,7 +5,7 @@ # German Gomez , 2022 skip_if_no_arm_spe_event() { - perf list | grep -E -q 'arm_spe_[0-9]+//' && return 0 + perf list pmu | grep -E -q 'arm_spe_[0-9]+//' && return 0 return 2 } diff --git a/tools/perf/tests/shell/test_data_symbol.sh b/tools/perf/tests/shell/test_data_symbol.sh index 3dfa91832aa87..c86da02350596 100755 --- a/tools/perf/tests/shell/test_data_symbol.sh +++ b/tools/perf/tests/shell/test_data_symbol.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Test data symbol +# Test data symbol (exclusive) # SPDX-License-Identifier: GPL-2.0 # Leo Yan , 2022 diff --git a/tools/perf/tests/shell/test_intel_pt.sh b/tools/perf/tests/shell/test_intel_pt.sh index 723ec501f99ab..e6f0070975f66 100755 --- a/tools/perf/tests/shell/test_intel_pt.sh +++ b/tools/perf/tests/shell/test_intel_pt.sh @@ -1,11 +1,11 @@ #!/bin/sh -# Miscellaneous Intel PT testing +# Miscellaneous Intel PT testing (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e # Skip if no Intel PT -perf list | grep -q 'intel_pt//' || exit 2 +perf list pmu | grep -q 'intel_pt//' || exit 2 shelldir=$(dirname "$0") # shellcheck source=lib/waiting.sh diff --git a/tools/perf/tests/shell/test_stat_intel_tpebs.sh b/tools/perf/tests/shell/test_stat_intel_tpebs.sh index c60b29add9801..f95fc64bf0a7b 100755 --- a/tools/perf/tests/shell/test_stat_intel_tpebs.sh +++ b/tools/perf/tests/shell/test_stat_intel_tpebs.sh @@ -1,5 +1,5 @@ #!/bin/bash -# test Intel TPEBS counting mode +# test Intel TPEBS counting mode (exclusive) # SPDX-License-Identifier: GPL-2.0 set -e @@ -8,12 +8,15 @@ grep -q GenuineIntel /proc/cpuinfo || { echo Skipping non-Intel; exit 2; } # Use this event for testing because it should exist in all platforms event=cache-misses:R +# Hybrid platforms output like "cpu_atom/cache-misses/R", rather than as above +alt_name=/cache-misses/R + # Without this cmd option, default value or zero is returned -echo "Testing without --record-tpebs" -result=$(perf stat -e "$event" true 2>&1) -[[ "$result" =~ $event ]] || exit 1 +#echo "Testing without --record-tpebs" +#result=$(perf stat -e "$event" true 2>&1) +#[[ "$result" =~ $event || "$result" =~ $alt_name ]] || exit 1 # In platforms that do not support TPEBS, it should execute without error. echo "Testing with --record-tpebs" result=$(perf stat -e "$event" --record-tpebs -a sleep 0.01 2>&1) -[[ "$result" =~ "perf record" && "$result" =~ $event ]] || exit 1 +[[ "$result" =~ "perf record" && "$result" =~ $event || "$result" =~ $alt_name ]] || exit 1 diff --git a/tools/perf/tests/shell/trace+probe_vfs_getname.sh b/tools/perf/tests/shell/trace+probe_vfs_getname.sh index 3146a1eece078..708a13f006353 100755 --- a/tools/perf/tests/shell/trace+probe_vfs_getname.sh +++ b/tools/perf/tests/shell/trace+probe_vfs_getname.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Check open filename arg using perf trace + vfs_getname +# Check open filename arg using perf trace + vfs_getname (exclusive) # Uses the 'perf test shell' library to add probe:vfs_getname to the system # then use it with 'perf trace' using 'touch' to write to a temp file, then @@ -19,7 +19,7 @@ skip_if_no_perf_trace || exit 2 . "$(dirname $0)"/lib/probe_vfs_getname.sh trace_open_vfs_getname() { - evts="$(echo "$(perf list syscalls:sys_enter_open* 2>/dev/null | grep -E 'open(at)? ' | sed -r 's/.*sys_enter_([a-z]+) +\[.*$/\1/')" | sed ':a;N;s:\n:,:g')" + evts="$(echo "$(perf list tracepoint 2>/dev/null | grep -E 'syscalls:sys_enter_open(at)? ' | sed -r 's/.*sys_enter_([a-z]+) +\[.*$/\1/')" | sed ':a;N;s:\n:,:g')" perf trace -e $evts touch $file 2>&1 | \ grep -E " +[0-9]+\.[0-9]+ +\( +[0-9]+\.[0-9]+ ms\): +touch/[0-9]+ open(at)?\((dfd: +CWD, +)?filename: +\"?${file}\"?, +flags: CREAT\|NOCTTY\|NONBLOCK\|WRONLY, +mode: +IRUGO\|IWUGO\) += +[0-9]+$" } diff --git a/tools/perf/tests/shell/trace_exit_race.sh b/tools/perf/tests/shell/trace_exit_race.sh new file mode 100755 index 0000000000000..fbb0adc33a889 --- /dev/null +++ b/tools/perf/tests/shell/trace_exit_race.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# perf trace exit race +# SPDX-License-Identifier: GPL-2.0 + +# Check that the last events of a perf trace'd subprocess are not +# lost. Specifically, trace the exiting syscall of "true" 10 times and ensure +# the output contains 10 correct lines. + +# shellcheck source=lib/probe.sh +. "$(dirname $0)"/lib/probe.sh + +skip_if_no_perf_trace || exit 2 + +if [ "$1" = "-v" ]; then + verbose="1" +fi + +iter=10 +regexp=" +[0-9]+\.[0-9]+ [0-9]+ syscalls:sys_enter_exit_group\(\)$" + +trace_shutdown_race() { + for _ in $(seq $iter); do + perf trace --no-comm -e syscalls:sys_enter_exit_group true 2>>$file + done + result="$(grep -c -E "$regexp" $file)" + [ $result = $iter ] +} + + +file=$(mktemp /tmp/temporary_file.XXXXX) + +# Do not use whatever ~/.perfconfig file, it may change the output +# via trace.{show_timestamp,show_prefix,etc} +export PERF_CONFIG=/dev/null + +trace_shutdown_race +err=$? + +if [ $err != 0 ] && [ "${verbose}" = "1" ]; then + lines_not_matching=$(mktemp /tmp/temporary_file.XXXXX) + if grep -v -E "$regexp" $file > $lines_not_matching ; then + echo "Lines not matching the expected regexp: '$regexp':" + cat $lines_not_matching + else + echo "Missing output, expected $iter but only got $result" + fi + rm -f $lines_not_matching +fi + +rm -f ${file} +exit $err diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index d33d0952025cf..8e328bbd509dc 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -152,4 +152,11 @@ static int test__task_exit(struct test_suite *test __maybe_unused, int subtest _ return err; } -DEFINE_SUITE("Number of exit events of a simple workload", task_exit); +struct test_case tests__task_exit[] = { + TEST_CASE_EXCLUSIVE("Number of exit events of a simple workload", task_exit), + { .name = NULL, } +}; +struct test_suite suite__task_exit = { + .desc = "Number of exit events of a simple workload", + .test_cases = tests__task_exit, +}; diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c index ed114b0442936..cf3ae0c1d8717 100644 --- a/tools/perf/tests/tests-scripts.c +++ b/tools/perf/tests/tests-scripts.c @@ -175,6 +175,7 @@ static void append_script(int dir_fd, const char *name, char *desc, struct test_suite *test_suite, **result_tmp; struct test_case *tests; size_t len; + char *exclusive; snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd); len = readlink(link, filename, sizeof(filename)); @@ -191,9 +192,13 @@ static void append_script(int dir_fd, const char *name, char *desc, return; } tests[0].name = strdup_check(name); + exclusive = strstr(desc, " (exclusive)"); + if (exclusive != NULL) { + tests[0].exclusive = true; + exclusive[0] = '\0'; + } tests[0].desc = strdup_check(desc); tests[0].run_case = shell_test__run; - test_suite = zalloc(sizeof(*test_suite)); if (!test_suite) { pr_err("Out of memory while building script test suite list\n"); diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 6ea2be86b7bf8..cb58b43aa063c 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -36,6 +36,7 @@ struct test_case { const char *desc; const char *skip_reason; test_fnptr run_case; + bool exclusive; }; struct test_suite { @@ -62,6 +63,14 @@ struct test_suite { .skip_reason = _reason, \ } +#define TEST_CASE_EXCLUSIVE(description, _name) \ + { \ + .name = #_name, \ + .desc = description, \ + .run_case = test__##_name, \ + .exclusive = true, \ + } + #define DEFINE_SUITE(description, _name) \ struct test_case tests__##_name[] = { \ TEST_CASE(description, _name), \ @@ -83,6 +92,8 @@ DECLARE_SUITE(perf_evsel__tp_sched_test); DECLARE_SUITE(syscall_openat_tp_fields); DECLARE_SUITE(pmu); DECLARE_SUITE(pmu_events); +DECLARE_SUITE(hwmon_pmu); +DECLARE_SUITE(tool_pmu); DECLARE_SUITE(attr); DECLARE_SUITE(dso_data); DECLARE_SUITE(dso_data_cache); diff --git a/tools/perf/tests/tool_pmu.c b/tools/perf/tests/tool_pmu.c new file mode 100644 index 0000000000000..187942b749b7c --- /dev/null +++ b/tools/perf/tests/tool_pmu.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "tests.h" +#include "tool_pmu.h" + +static int do_test(enum tool_pmu_event ev, bool with_pmu) +{ + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + struct parse_events_error err; + int ret; + char str[128]; + bool found = false; + + if (!evlist) { + pr_err("evlist allocation failed\n"); + return TEST_FAIL; + } + + if (with_pmu) + snprintf(str, sizeof(str), "tool/%s/", tool_pmu__event_to_str(ev)); + else + snprintf(str, sizeof(str), "%s", tool_pmu__event_to_str(ev)); + + parse_events_error__init(&err); + ret = parse_events(evlist, str, &err); + if (ret) { + if (tool_pmu__skip_event(tool_pmu__event_to_str(ev))) { + ret = TEST_OK; + goto out; + } + + pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n", + __FILE__, __LINE__, str, ret); + parse_events_error__print(&err, str); + ret = TEST_FAIL; + goto out; + } + + ret = TEST_OK; + if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) { + pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n", + __FILE__, __LINE__, str, evlist->core.nr_entries); + ret = TEST_FAIL; + goto out; + } + + evlist__for_each_entry(evlist, evsel) { + if (perf_pmu__is_tool(evsel->pmu)) { + if (evsel->core.attr.config != ev) { + pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %d\n", + __FILE__, __LINE__, str, evsel->core.attr.config, ev); + ret = TEST_FAIL; + goto out; + } + found = true; + } + } + + if (!found && !tool_pmu__skip_event(tool_pmu__event_to_str(ev))) { + pr_debug("FAILED %s:%d Didn't find tool event '%s' in parsed evsels\n", + __FILE__, __LINE__, str); + ret = TEST_FAIL; + } + +out: + parse_events_error__exit(&err); + evlist__delete(evlist); + return ret; +} + +static int test__tool_pmu_without_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int i; + + tool_pmu__for_each_event(i) { + int ret = do_test(i, /*with_pmu=*/false); + + if (ret != TEST_OK) + return ret; + } + return TEST_OK; +} + +static int test__tool_pmu_with_pmu(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int i; + + tool_pmu__for_each_event(i) { + int ret = do_test(i, /*with_pmu=*/true); + + if (ret != TEST_OK) + return ret; + } + return TEST_OK; +} + +static struct test_case tests__tool_pmu[] = { + TEST_CASE("Parsing without PMU name", tool_pmu_without_pmu), + TEST_CASE("Parsing with PMU name", tool_pmu_with_pmu), + { .name = NULL, } +}; + +struct test_suite suite__tool_pmu = { + .desc = "Tool PMU", + .test_cases = tests__tool_pmu, +}; diff --git a/tools/perf/util/Build b/tools/perf/util/Build index dc616292b2ddf..c06d2ee9024c6 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -83,6 +83,8 @@ perf-util-y += pmu.o perf-util-y += pmus.o perf-util-y += pmu-flex.o perf-util-y += pmu-bison.o +perf-util-y += hwmon_pmu.o +perf-util-y += tool_pmu.o perf-util-y += svghelper.o perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-info.o perf-util-y += trace-event-scripting.o @@ -199,11 +201,14 @@ ifndef CONFIG_SETNS perf-util-y += setns.o endif -perf-util-$(CONFIG_DWARF) += probe-finder.o -perf-util-$(CONFIG_DWARF) += dwarf-aux.o -perf-util-$(CONFIG_DWARF) += dwarf-regs.o -perf-util-$(CONFIG_DWARF) += debuginfo.o -perf-util-$(CONFIG_DWARF) += annotate-data.o +perf-util-$(CONFIG_LIBDW) += probe-finder.o +perf-util-$(CONFIG_LIBDW) += dwarf-aux.o +perf-util-$(CONFIG_LIBDW) += dwarf-regs.o +perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o +perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o +perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o +perf-util-$(CONFIG_LIBDW) += debuginfo.o +perf-util-$(CONFIG_LIBDW) += annotate-data.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o @@ -234,7 +239,7 @@ perf-util-$(CONFIG_LIBLLVM) += llvm-c-helpers.o ifdef CONFIG_JITDUMP perf-util-$(CONFIG_LIBELF) += jitdump.o perf-util-$(CONFIG_LIBELF) += genelf.o -perf-util-$(CONFIG_DWARF) += genelf_debug.o +perf-util-$(CONFIG_LIBDW) += genelf_debug.o endif perf-util-y += perf-hooks.o diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index 8ac0fd94a0ba4..98c80b2268dde 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -9,7 +9,7 @@ #include "dwarf-regs.h" #include "annotate.h" -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT #include "debuginfo.h" #endif @@ -165,7 +165,7 @@ struct annotated_data_stat { }; extern struct annotated_data_stat ann_data_stat; -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT /* * Type information in a register, valid when @ok is true. * The @caller_saved registers are invalidated after a function call. @@ -244,7 +244,7 @@ bool get_global_var_info(struct data_loc_info *dloc, u64 addr, const char **var_name, int *var_offset); void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind); -#else /* HAVE_DWARF_SUPPORT */ +#else /* HAVE_LIBDW_SUPPORT */ static inline struct annotated_data_type * find_data_type(struct data_loc_info *dloc __maybe_unused) @@ -276,7 +276,7 @@ static inline int hist_entry__annotate_data_tty(struct hist_entry *he __maybe_un return -1; } -#endif /* HAVE_DWARF_SUPPORT */ +#endif /* HAVE_LIBDW_SUPPORT */ #ifdef HAVE_SLANG_SUPPORT int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 37ce43c4eb8f6..32e15c9f53f3c 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -2116,6 +2116,12 @@ static int annotation__config(const char *var, const char *value, void *data) opt->offset_level = ANNOTATION__MAX_OFFSET_LEVEL; else if (opt->offset_level < ANNOTATION__MIN_OFFSET_LEVEL) opt->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; + } else if (!strcmp(var, "annotate.disassemblers")) { + opt->disassemblers_str = strdup(value); + if (!opt->disassemblers_str) { + pr_err("Not enough memory for annotate.disassemblers\n"); + return -1; + } } else if (!strcmp(var, "annotate.hide_src_code")) { opt->hide_src_code = perf_config_bool("hide_src_code", value); } else if (!strcmp(var, "annotate.jump_arrows")) { @@ -2292,7 +2298,7 @@ static int extract_reg_offset(struct arch *arch, const char *str, if (regname == NULL) return -1; - op_loc->reg1 = get_dwarf_regnum(regname, 0); + op_loc->reg1 = get_dwarf_regnum(regname, arch->e_machine, arch->e_flags); free(regname); /* Get the second register */ @@ -2305,7 +2311,7 @@ static int extract_reg_offset(struct arch *arch, const char *str, if (regname == NULL) return -1; - op_loc->reg2 = get_dwarf_regnum(regname, 0); + op_loc->reg2 = get_dwarf_regnum(regname, arch->e_machine, arch->e_flags); free(regname); } return 0; @@ -2405,7 +2411,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, return -1; if (*s == arch->objdump.register_char) - op_loc->reg1 = get_dwarf_regnum(s, 0); + op_loc->reg1 = get_dwarf_regnum(s, arch->e_machine, arch->e_flags); else if (*s == arch->objdump.imm_char) { op_loc->offset = strtol(s + 1, &p, 0); if (p && p != s + 1) diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 8b9e05a1932f2..194a05cbc506e 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -34,6 +34,9 @@ struct annotated_data_type; #define ANNOTATION__BR_CNTR_WIDTH 30 #define ANNOTATION_DUMMY_LEN 256 +// llvm, capstone, objdump +#define MAX_DISASSEMBLERS 3 + struct annotation_options { bool hide_src_code, use_offset, @@ -49,11 +52,14 @@ struct annotation_options { annotate_src, full_addr; u8 offset_level; + u8 nr_disassemblers; int min_pcnt; int max_lines; int context; char *objdump_path; char *disassembler_style; + const char *disassemblers_str; + const char *disassemblers[MAX_DISASSEMBLERS]; const char *prefix; const char *prefix_strip; unsigned int percent_type; diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h index 1443c28545a94..358c611eeddbb 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h +++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h @@ -56,15 +56,15 @@ enum arm_spe_op_type { ARM_SPE_OP_BR_INDIRECT = 1 << 17, }; -enum arm_spe_neoverse_data_source { - ARM_SPE_NV_L1D = 0x0, - ARM_SPE_NV_L2 = 0x8, - ARM_SPE_NV_PEER_CORE = 0x9, - ARM_SPE_NV_LOCAL_CLUSTER = 0xa, - ARM_SPE_NV_SYS_CACHE = 0xb, - ARM_SPE_NV_PEER_CLUSTER = 0xc, - ARM_SPE_NV_REMOTE = 0xd, - ARM_SPE_NV_DRAM = 0xe, +enum arm_spe_common_data_source { + ARM_SPE_COMMON_DS_L1D = 0x0, + ARM_SPE_COMMON_DS_L2 = 0x8, + ARM_SPE_COMMON_DS_PEER_CORE = 0x9, + ARM_SPE_COMMON_DS_LOCAL_CLUSTER = 0xa, + ARM_SPE_COMMON_DS_SYS_CACHE = 0xb, + ARM_SPE_COMMON_DS_PEER_CLUSTER = 0xc, + ARM_SPE_COMMON_DS_REMOTE = 0xd, + ARM_SPE_COMMON_DS_DRAM = 0xe, }; struct arm_spe_record { diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 138ffc71b32dd..dbf13f47879c0 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -46,7 +46,6 @@ struct arm_spe { struct perf_session *session; struct machine *machine; u32 pmu_type; - u64 midr; struct perf_tsc_conversion tc; @@ -69,7 +68,7 @@ struct arm_spe { u64 llc_access_id; u64 tlb_miss_id; u64 tlb_access_id; - u64 branch_miss_id; + u64 branch_id; u64 remote_access_id; u64 memory_id; u64 instructions_id; @@ -78,6 +77,11 @@ struct arm_spe { unsigned long num_events; u8 use_ctx_pkt_for_pid; + + u64 **metadata; + u64 metadata_ver; + u64 metadata_nr_cpu; + bool is_homogeneous; }; struct arm_spe_queue { @@ -96,6 +100,7 @@ struct arm_spe_queue { u64 timestamp; struct thread *thread; u64 period_instructions; + u32 flags; }; static void arm_spe_dump(struct arm_spe *spe __maybe_unused, @@ -118,7 +123,7 @@ static void arm_spe_dump(struct arm_spe *spe __maybe_unused, else pkt_len = 1; printf("."); - color_fprintf(stdout, color, " %08x: ", pos); + color_fprintf(stdout, color, " %08zx: ", pos); for (i = 0; i < pkt_len; i++) color_fprintf(stdout, color, " %02x", buf[i]); for (; i < 16; i++) @@ -273,6 +278,20 @@ static int arm_spe_set_tid(struct arm_spe_queue *speq, pid_t tid) return 0; } +static u64 *arm_spe__get_metadata_by_cpu(struct arm_spe *spe, u64 cpu) +{ + u64 i; + + if (!spe->metadata) + return NULL; + + for (i = 0; i < spe->metadata_nr_cpu; i++) + if (spe->metadata[i][ARM_SPE_CPU] == cpu) + return spe->metadata[i]; + + return NULL; +} + static struct simd_flags arm_spe__synth_simd_flags(const struct arm_spe_record *record) { struct simd_flags simd_flags = {}; @@ -376,6 +395,7 @@ static int arm_spe__synth_branch_sample(struct arm_spe_queue *speq, sample.stream_id = spe_events_id; sample.addr = record->to_ip; sample.weight = record->latency; + sample.flags = speq->flags; return arm_spe_deliver_synth_event(spe, speq, event, &sample); } @@ -400,24 +420,44 @@ static int arm_spe__synth_instruction_sample(struct arm_spe_queue *speq, sample.id = spe_events_id; sample.stream_id = spe_events_id; - sample.addr = record->virt_addr; + sample.addr = record->to_ip; sample.phys_addr = record->phys_addr; sample.data_src = data_src; sample.period = spe->instructions_sample_period; sample.weight = record->latency; + sample.flags = speq->flags; return arm_spe_deliver_synth_event(spe, speq, event, &sample); } -static const struct midr_range neoverse_spe[] = { +static const struct midr_range common_ds_encoding_cpus[] = { + MIDR_ALL_VERSIONS(MIDR_CORTEX_A720), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A725), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X1C), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X3), + MIDR_ALL_VERSIONS(MIDR_CORTEX_X925), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2), {}, }; -static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *record, - union perf_mem_data_src *data_src) +static void arm_spe__sample_flags(struct arm_spe_queue *speq) +{ + const struct arm_spe_record *record = &speq->decoder->record; + + speq->flags = 0; + if (record->op & ARM_SPE_OP_BRANCH_ERET) { + speq->flags = PERF_IP_FLAG_BRANCH; + + if (record->type & ARM_SPE_BRANCH_MISS) + speq->flags |= PERF_IP_FLAG_BRANCH_MISS; + } +} + +static void arm_spe__synth_data_source_common(const struct arm_spe_record *record, + union perf_mem_data_src *data_src) { /* * Even though four levels of cache hierarchy are possible, no known @@ -439,17 +479,17 @@ static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *rec } switch (record->source) { - case ARM_SPE_NV_L1D: + case ARM_SPE_COMMON_DS_L1D: data_src->mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_HIT; data_src->mem_lvl_num = PERF_MEM_LVLNUM_L1; data_src->mem_snoop = PERF_MEM_SNOOP_NONE; break; - case ARM_SPE_NV_L2: + case ARM_SPE_COMMON_DS_L2: data_src->mem_lvl = PERF_MEM_LVL_L2 | PERF_MEM_LVL_HIT; data_src->mem_lvl_num = PERF_MEM_LVLNUM_L2; data_src->mem_snoop = PERF_MEM_SNOOP_NONE; break; - case ARM_SPE_NV_PEER_CORE: + case ARM_SPE_COMMON_DS_PEER_CORE: data_src->mem_lvl = PERF_MEM_LVL_L2 | PERF_MEM_LVL_HIT; data_src->mem_lvl_num = PERF_MEM_LVLNUM_L2; data_src->mem_snoopx = PERF_MEM_SNOOPX_PEER; @@ -458,8 +498,8 @@ static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *rec * We don't know if this is L1, L2 but we do know it was a cache-2-cache * transfer, so set SNOOPX_PEER */ - case ARM_SPE_NV_LOCAL_CLUSTER: - case ARM_SPE_NV_PEER_CLUSTER: + case ARM_SPE_COMMON_DS_LOCAL_CLUSTER: + case ARM_SPE_COMMON_DS_PEER_CLUSTER: data_src->mem_lvl = PERF_MEM_LVL_L3 | PERF_MEM_LVL_HIT; data_src->mem_lvl_num = PERF_MEM_LVLNUM_L3; data_src->mem_snoopx = PERF_MEM_SNOOPX_PEER; @@ -467,7 +507,7 @@ static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *rec /* * System cache is assumed to be L3 */ - case ARM_SPE_NV_SYS_CACHE: + case ARM_SPE_COMMON_DS_SYS_CACHE: data_src->mem_lvl = PERF_MEM_LVL_L3 | PERF_MEM_LVL_HIT; data_src->mem_lvl_num = PERF_MEM_LVLNUM_L3; data_src->mem_snoop = PERF_MEM_SNOOP_HIT; @@ -476,13 +516,13 @@ static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *rec * We don't know what level it hit in, except it came from the other * socket */ - case ARM_SPE_NV_REMOTE: + case ARM_SPE_COMMON_DS_REMOTE: data_src->mem_lvl = PERF_MEM_LVL_REM_CCE1; data_src->mem_lvl_num = PERF_MEM_LVLNUM_ANY_CACHE; data_src->mem_remote = PERF_MEM_REMOTE_REMOTE; data_src->mem_snoopx = PERF_MEM_SNOOPX_PEER; break; - case ARM_SPE_NV_DRAM: + case ARM_SPE_COMMON_DS_DRAM: data_src->mem_lvl = PERF_MEM_LVL_LOC_RAM | PERF_MEM_LVL_HIT; data_src->mem_lvl_num = PERF_MEM_LVLNUM_RAM; data_src->mem_snoop = PERF_MEM_SNOOP_NONE; @@ -492,8 +532,8 @@ static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *rec } } -static void arm_spe__synth_data_source_generic(const struct arm_spe_record *record, - union perf_mem_data_src *data_src) +static void arm_spe__synth_memory_level(const struct arm_spe_record *record, + union perf_mem_data_src *data_src) { if (record->type & (ARM_SPE_LLC_ACCESS | ARM_SPE_LLC_MISS)) { data_src->mem_lvl = PERF_MEM_LVL_L3; @@ -515,10 +555,55 @@ static void arm_spe__synth_data_source_generic(const struct arm_spe_record *reco data_src->mem_lvl |= PERF_MEM_LVL_REM_CCE1; } -static u64 arm_spe__synth_data_source(const struct arm_spe_record *record, u64 midr) +static bool arm_spe__is_common_ds_encoding(struct arm_spe_queue *speq) +{ + struct arm_spe *spe = speq->spe; + bool is_in_cpu_list; + u64 *metadata = NULL; + u64 midr = 0; + + /* Metadata version 1 assumes all CPUs are the same (old behavior) */ + if (spe->metadata_ver == 1) { + const char *cpuid; + + pr_warning_once("Old SPE metadata, re-record to improve decode accuracy\n"); + cpuid = perf_env__cpuid(spe->session->evlist->env); + midr = strtol(cpuid, NULL, 16); + } else { + /* CPU ID is -1 for per-thread mode */ + if (speq->cpu < 0) { + /* + * On the heterogeneous system, due to CPU ID is -1, + * cannot confirm the data source packet is supported. + */ + if (!spe->is_homogeneous) + return false; + + /* In homogeneous system, simply use CPU0's metadata */ + if (spe->metadata) + metadata = spe->metadata[0]; + } else { + metadata = arm_spe__get_metadata_by_cpu(spe, speq->cpu); + } + + if (!metadata) + return false; + + midr = metadata[ARM_SPE_CPU_MIDR]; + } + + is_in_cpu_list = is_midr_in_range_list(midr, common_ds_encoding_cpus); + if (is_in_cpu_list) + return true; + else + return false; +} + +static u64 arm_spe__synth_data_source(struct arm_spe_queue *speq, + const struct arm_spe_record *record) { union perf_mem_data_src data_src = { .mem_op = PERF_MEM_OP_NA }; - bool is_neoverse = is_midr_in_range_list(midr, neoverse_spe); + bool is_common = arm_spe__is_common_ds_encoding(speq); if (record->op & ARM_SPE_OP_LD) data_src.mem_op = PERF_MEM_OP_LOAD; @@ -527,10 +612,10 @@ static u64 arm_spe__synth_data_source(const struct arm_spe_record *record, u64 m else return 0; - if (is_neoverse) - arm_spe__synth_data_source_neoverse(record, &data_src); + if (is_common) + arm_spe__synth_data_source_common(record, &data_src); else - arm_spe__synth_data_source_generic(record, &data_src); + arm_spe__synth_memory_level(record, &data_src); if (record->type & (ARM_SPE_TLB_ACCESS | ARM_SPE_TLB_MISS)) { data_src.mem_dtlb = PERF_MEM_TLB_WK; @@ -551,7 +636,8 @@ static int arm_spe_sample(struct arm_spe_queue *speq) u64 data_src; int err; - data_src = arm_spe__synth_data_source(record, spe->midr); + arm_spe__sample_flags(speq); + data_src = arm_spe__synth_data_source(speq, record); if (spe->sample_flc) { if (record->type & ARM_SPE_L1D_MISS) { @@ -601,8 +687,8 @@ static int arm_spe_sample(struct arm_spe_queue *speq) } } - if (spe->sample_branch && (record->type & ARM_SPE_BRANCH_MISS)) { - err = arm_spe__synth_branch_sample(speq, spe->branch_miss_id); + if (spe->sample_branch && (record->op & ARM_SPE_OP_BRANCH_ERET)) { + err = arm_spe__synth_branch_sample(speq, spe->branch_id); if (err) return err; } @@ -1016,6 +1102,73 @@ static int arm_spe_flush(struct perf_session *session __maybe_unused, return 0; } +static u64 *arm_spe__alloc_per_cpu_metadata(u64 *buf, int per_cpu_size) +{ + u64 *metadata; + + metadata = zalloc(per_cpu_size); + if (!metadata) + return NULL; + + memcpy(metadata, buf, per_cpu_size); + return metadata; +} + +static void arm_spe__free_metadata(u64 **metadata, int nr_cpu) +{ + int i; + + for (i = 0; i < nr_cpu; i++) + zfree(&metadata[i]); + free(metadata); +} + +static u64 **arm_spe__alloc_metadata(struct perf_record_auxtrace_info *info, + u64 *ver, int *nr_cpu) +{ + u64 *ptr = (u64 *)info->priv; + u64 metadata_size; + u64 **metadata = NULL; + int hdr_sz, per_cpu_sz, i; + + metadata_size = info->header.size - + sizeof(struct perf_record_auxtrace_info); + + /* Metadata version 1 */ + if (metadata_size == ARM_SPE_AUXTRACE_V1_PRIV_SIZE) { + *ver = 1; + *nr_cpu = 0; + /* No per CPU metadata */ + return NULL; + } + + *ver = ptr[ARM_SPE_HEADER_VERSION]; + hdr_sz = ptr[ARM_SPE_HEADER_SIZE]; + *nr_cpu = ptr[ARM_SPE_CPUS_NUM]; + + metadata = calloc(*nr_cpu, sizeof(*metadata)); + if (!metadata) + return NULL; + + /* Locate the start address of per CPU metadata */ + ptr += hdr_sz; + per_cpu_sz = (metadata_size - (hdr_sz * sizeof(u64))) / (*nr_cpu); + + for (i = 0; i < *nr_cpu; i++) { + metadata[i] = arm_spe__alloc_per_cpu_metadata(ptr, per_cpu_sz); + if (!metadata[i]) + goto err_per_cpu_metadata; + + ptr += per_cpu_sz / sizeof(u64); + } + + return metadata; + +err_per_cpu_metadata: + arm_spe__free_metadata(metadata, *nr_cpu); + return NULL; +} + static void arm_spe_free_queue(void *priv) { struct arm_spe_queue *speq = priv; @@ -1050,6 +1203,7 @@ static void arm_spe_free(struct perf_session *session) auxtrace_heap__free(&spe->heap); arm_spe_free_events(session); session->auxtrace = NULL; + arm_spe__free_metadata(spe->metadata, spe->metadata_nr_cpu); free(spe); } @@ -1061,16 +1215,60 @@ static bool arm_spe_evsel_is_auxtrace(struct perf_session *session, return evsel->core.attr.type == spe->pmu_type; } -static const char * const arm_spe_info_fmts[] = { - [ARM_SPE_PMU_TYPE] = " PMU Type %"PRId64"\n", +static const char * const metadata_hdr_v1_fmts[] = { + [ARM_SPE_PMU_TYPE] = " PMU Type :%"PRId64"\n", + [ARM_SPE_PER_CPU_MMAPS] = " Per CPU mmaps :%"PRId64"\n", +}; + +static const char * const metadata_hdr_fmts[] = { + [ARM_SPE_HEADER_VERSION] = " Header version :%"PRId64"\n", + [ARM_SPE_HEADER_SIZE] = " Header size :%"PRId64"\n", + [ARM_SPE_PMU_TYPE_V2] = " PMU type v2 :%"PRId64"\n", + [ARM_SPE_CPUS_NUM] = " CPU number :%"PRId64"\n", +}; + +static const char * const metadata_per_cpu_fmts[] = { + [ARM_SPE_MAGIC] = " Magic :0x%"PRIx64"\n", + [ARM_SPE_CPU] = " CPU # :%"PRId64"\n", + [ARM_SPE_CPU_NR_PARAMS] = " Num of params :%"PRId64"\n", + [ARM_SPE_CPU_MIDR] = " MIDR :0x%"PRIx64"\n", + [ARM_SPE_CPU_PMU_TYPE] = " PMU Type :%"PRId64"\n", + [ARM_SPE_CAP_MIN_IVAL] = " Min Interval :%"PRId64"\n", }; -static void arm_spe_print_info(__u64 *arr) +static void arm_spe_print_info(struct arm_spe *spe, __u64 *arr) { + unsigned int i, cpu, hdr_size, cpu_num, cpu_size; + const char * const *hdr_fmts; + if (!dump_trace) return; - fprintf(stdout, arm_spe_info_fmts[ARM_SPE_PMU_TYPE], arr[ARM_SPE_PMU_TYPE]); + if (spe->metadata_ver == 1) { + cpu_num = 0; + hdr_size = ARM_SPE_AUXTRACE_V1_PRIV_MAX; + hdr_fmts = metadata_hdr_v1_fmts; + } else { + cpu_num = arr[ARM_SPE_CPUS_NUM]; + hdr_size = arr[ARM_SPE_HEADER_SIZE]; + hdr_fmts = metadata_hdr_fmts; + } + + for (i = 0; i < hdr_size; i++) + fprintf(stdout, hdr_fmts[i], arr[i]); + + arr += hdr_size; + for (cpu = 0; cpu < cpu_num; cpu++) { + /* + * The parameters from ARM_SPE_MAGIC to ARM_SPE_CPU_NR_PARAMS + * are fixed. The sequential parameter size is decided by the + * field 'ARM_SPE_CPU_NR_PARAMS'. + */ + cpu_size = (ARM_SPE_CPU_NR_PARAMS + 1) + arr[ARM_SPE_CPU_NR_PARAMS]; + for (i = 0; i < cpu_size; i++) + fprintf(stdout, metadata_per_cpu_fmts[i], arr[i]); + arr += cpu_size; + } } static void arm_spe_set_event_name(struct evlist *evlist, u64 id, @@ -1202,12 +1400,12 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) if (spe->synth_opts.branches) { spe->sample_branch = true; - /* Branch miss */ + /* Branch */ err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; - spe->branch_miss_id = id; - arm_spe_set_event_name(evlist, id, "branch-miss"); + spe->branch_id = id; + arm_spe_set_event_name(evlist, id, "branch"); id += 1; } @@ -1258,24 +1456,57 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) return 0; } +static bool arm_spe__is_homogeneous(u64 **metadata, int nr_cpu) +{ + u64 midr; + int i; + + if (!nr_cpu) + return false; + + for (i = 0; i < nr_cpu; i++) { + if (!metadata[i]) + return false; + + if (i == 0) { + midr = metadata[i][ARM_SPE_CPU_MIDR]; + continue; + } + + if (midr != metadata[i][ARM_SPE_CPU_MIDR]) + return false; + } + + return true; +} + int arm_spe_process_auxtrace_info(union perf_event *event, struct perf_session *session) { struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; - size_t min_sz = sizeof(u64) * ARM_SPE_AUXTRACE_PRIV_MAX; + size_t min_sz = ARM_SPE_AUXTRACE_V1_PRIV_SIZE; struct perf_record_time_conv *tc = &session->time_conv; - const char *cpuid = perf_env__cpuid(session->evlist->env); - u64 midr = strtol(cpuid, NULL, 16); struct arm_spe *spe; - int err; + u64 **metadata = NULL; + u64 metadata_ver; + int nr_cpu, err; if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) + min_sz) return -EINVAL; + metadata = arm_spe__alloc_metadata(auxtrace_info, &metadata_ver, + &nr_cpu); + if (!metadata && metadata_ver != 1) { + pr_err("Failed to parse Arm SPE metadata.\n"); + return -EINVAL; + } + spe = zalloc(sizeof(struct arm_spe)); - if (!spe) - return -ENOMEM; + if (!spe) { + err = -ENOMEM; + goto err_free_metadata; + } err = auxtrace_queues__init(&spe->queues); if (err) @@ -1284,8 +1515,14 @@ int arm_spe_process_auxtrace_info(union perf_event *event, spe->session = session; spe->machine = &session->machines.host; /* No kvm support */ spe->auxtrace_type = auxtrace_info->type; - spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE]; - spe->midr = midr; + if (metadata_ver == 1) + spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE]; + else + spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE_V2]; + spe->metadata = metadata; + spe->metadata_ver = metadata_ver; + spe->metadata_nr_cpu = nr_cpu; + spe->is_homogeneous = arm_spe__is_homogeneous(metadata, nr_cpu); spe->timeless_decoding = arm_spe__is_timeless_decoding(spe); @@ -1318,7 +1555,7 @@ int arm_spe_process_auxtrace_info(union perf_event *event, spe->auxtrace.evsel_is_auxtrace = arm_spe_evsel_is_auxtrace; session->auxtrace = &spe->auxtrace; - arm_spe_print_info(&auxtrace_info->priv[0]); + arm_spe_print_info(spe, &auxtrace_info->priv[0]); if (dump_trace) return 0; @@ -1346,5 +1583,7 @@ int arm_spe_process_auxtrace_info(union perf_event *event, session->auxtrace = NULL; err_free: free(spe); +err_free_metadata: + arm_spe__free_metadata(metadata, nr_cpu); return err; } diff --git a/tools/perf/util/arm-spe.h b/tools/perf/util/arm-spe.h index 4f4900c18f3e2..390679a4af2fb 100644 --- a/tools/perf/util/arm-spe.h +++ b/tools/perf/util/arm-spe.h @@ -12,10 +12,46 @@ enum { ARM_SPE_PMU_TYPE, ARM_SPE_PER_CPU_MMAPS, + ARM_SPE_AUXTRACE_V1_PRIV_MAX, +}; + +#define ARM_SPE_AUXTRACE_V1_PRIV_SIZE \ + (ARM_SPE_AUXTRACE_V1_PRIV_MAX * sizeof(u64)) + +enum { + /* + * The old metadata format (defined above) does not include a + * field for version number. Version 1 is reserved and starts + * from version 2. + */ + ARM_SPE_HEADER_VERSION, + /* Number of sizeof(u64) */ + ARM_SPE_HEADER_SIZE, + /* PMU type shared by CPUs */ + ARM_SPE_PMU_TYPE_V2, + /* Number of CPUs */ + ARM_SPE_CPUS_NUM, ARM_SPE_AUXTRACE_PRIV_MAX, }; -#define ARM_SPE_AUXTRACE_PRIV_SIZE (ARM_SPE_AUXTRACE_PRIV_MAX * sizeof(u64)) +enum { + /* Magic number */ + ARM_SPE_MAGIC, + /* CPU logical number in system */ + ARM_SPE_CPU, + /* Number of parameters */ + ARM_SPE_CPU_NR_PARAMS, + /* CPU MIDR */ + ARM_SPE_CPU_MIDR, + /* Associated PMU type */ + ARM_SPE_CPU_PMU_TYPE, + /* Minimal interval */ + ARM_SPE_CAP_MIN_IVAL, + ARM_SPE_CPU_PRIV_MAX, +}; + +#define ARM_SPE_HEADER_CURRENT_VERSION 2 + union perf_event; struct perf_session; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index a1895a4f530be..dddaf4f3ffeda 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -75,7 +75,6 @@ enum itrace_period_type { * (not fully accurate, since CYC packets are only emitted * together with other events, such as branches) * @branches: whether to synthesize 'branches' events - * (branch misses only for Arm SPE) * @transactions: whether to synthesize events for transactions * @ptwrites: whether to synthesize events for ptwrites * @pwr_events: whether to synthesize power events @@ -650,7 +649,7 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session, #define ITRACE_HELP \ " i[period]: synthesize instructions events\n" \ " y[period]: synthesize cycles events (same period as i)\n" \ -" b: synthesize branches events (branch misses for Arm SPE)\n" \ +" b: synthesize branches events\n" \ " c: synthesize branches events (calls only)\n" \ " r: synthesize branches events (returns only)\n" \ " x: synthesize transactions events\n" \ diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index e87b6789eb9ef..a4fdf6911ec1c 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -375,7 +375,7 @@ static int create_idx_hash(struct evsel *evsel, struct perf_bpf_filter_entry *en pfi = zalloc(sizeof(*pfi)); if (pfi == NULL) { pr_err("Cannot save pinned filter index\n"); - goto err; + return -ENOMEM; } pfi->evsel = evsel; diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h deleted file mode 100644 index 66dcf751ef654..0000000000000 --- a/tools/perf/util/bpf-prologue.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2015, He Kuang - * Copyright (C) 2015, Huawei Inc. - */ -#ifndef __BPF_PROLOGUE_H -#define __BPF_PROLOGUE_H - -struct probe_trace_arg; -struct bpf_insn; - -#define BPF_PROLOGUE_MAX_ARGS 3 -#define BPF_PROLOGUE_START_ARG_REG BPF_REG_3 -#define BPF_PROLOGUE_FETCH_RESULT_REG BPF_REG_2 - -#ifdef HAVE_BPF_PROLOGUE -int bpf__gen_prologue(struct probe_trace_arg *args, int nargs, - struct bpf_insn *new_prog, size_t *new_cnt, - size_t cnt_space); -#else -#include -#include - -static inline int -bpf__gen_prologue(struct probe_trace_arg *args __maybe_unused, - int nargs __maybe_unused, - struct bpf_insn *new_prog __maybe_unused, - size_t *new_cnt, - size_t cnt_space __maybe_unused) -{ - if (!new_cnt) - return -EINVAL; - *new_cnt = 0; - return -ENOTSUP; -} -#endif -#endif /* __BPF_PROLOGUE_H */ diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c index 7a8af60e0f515..73fcafbffc6a6 100644 --- a/tools/perf/util/bpf_counter.c +++ b/tools/perf/util/bpf_counter.c @@ -394,6 +394,7 @@ static int bperf_check_target(struct evsel *evsel, } static struct perf_cpu_map *all_cpu_map; +static __u32 filter_entry_cnt; static int bperf_reload_leader_program(struct evsel *evsel, int attr_map_fd, struct perf_event_attr_map_entry *entry) @@ -444,12 +445,32 @@ static int bperf_reload_leader_program(struct evsel *evsel, int attr_map_fd, return err; } +static int bperf_attach_follower_program(struct bperf_follower_bpf *skel, + enum bperf_filter_type filter_type, + bool inherit) +{ + struct bpf_link *link; + int err = 0; + + if ((filter_type == BPERF_FILTER_PID || + filter_type == BPERF_FILTER_TGID) && inherit) + /* attach all follower bpf progs to enable event inheritance */ + err = bperf_follower_bpf__attach(skel); + else { + link = bpf_program__attach(skel->progs.fexit_XXX); + if (IS_ERR(link)) + err = PTR_ERR(link); + } + + return err; +} + static int bperf__load(struct evsel *evsel, struct target *target) { struct perf_event_attr_map_entry entry = {0xffffffff, 0xffffffff}; int attr_map_fd, diff_map_fd = -1, err; enum bperf_filter_type filter_type; - __u32 filter_entry_cnt, i; + __u32 i; if (bperf_check_target(evsel, target, &filter_type, &filter_entry_cnt)) return -1; @@ -529,9 +550,6 @@ static int bperf__load(struct evsel *evsel, struct target *target) /* set up reading map */ bpf_map__set_max_entries(evsel->follower_skel->maps.accum_readings, filter_entry_cnt); - /* set up follower filter based on target */ - bpf_map__set_max_entries(evsel->follower_skel->maps.filter, - filter_entry_cnt); err = bperf_follower_bpf__load(evsel->follower_skel); if (err) { pr_err("Failed to load follower skeleton\n"); @@ -543,6 +561,7 @@ static int bperf__load(struct evsel *evsel, struct target *target) for (i = 0; i < filter_entry_cnt; i++) { int filter_map_fd; __u32 key; + struct bperf_filter_value fval = { i, 0 }; if (filter_type == BPERF_FILTER_PID || filter_type == BPERF_FILTER_TGID) @@ -553,12 +572,14 @@ static int bperf__load(struct evsel *evsel, struct target *target) break; filter_map_fd = bpf_map__fd(evsel->follower_skel->maps.filter); - bpf_map_update_elem(filter_map_fd, &key, &i, BPF_ANY); + bpf_map_update_elem(filter_map_fd, &key, &fval, BPF_ANY); } evsel->follower_skel->bss->type = filter_type; + evsel->follower_skel->bss->inherit = target->inherit; - err = bperf_follower_bpf__attach(evsel->follower_skel); + err = bperf_attach_follower_program(evsel->follower_skel, filter_type, + target->inherit); out: if (err && evsel->bperf_leader_link_fd >= 0) @@ -623,7 +644,7 @@ static int bperf__read(struct evsel *evsel) bperf_sync_counters(evsel); reading_map_fd = bpf_map__fd(skel->maps.accum_readings); - for (i = 0; i < bpf_map__max_entries(skel->maps.accum_readings); i++) { + for (i = 0; i < filter_entry_cnt; i++) { struct perf_cpu entry; __u32 cpu; diff --git a/tools/perf/util/bpf_skel/bperf_follower.bpf.c b/tools/perf/util/bpf_skel/bperf_follower.bpf.c index f193998530d43..0595063139a3d 100644 --- a/tools/perf/util/bpf_skel/bperf_follower.bpf.c +++ b/tools/perf/util/bpf_skel/bperf_follower.bpf.c @@ -5,6 +5,8 @@ #include #include "bperf_u.h" +#define MAX_ENTRIES 102400 + struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(key_size, sizeof(__u32)); @@ -22,25 +24,29 @@ struct { struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, sizeof(__u32)); - __uint(value_size, sizeof(__u32)); + __uint(value_size, sizeof(struct bperf_filter_value)); + __uint(max_entries, MAX_ENTRIES); + __uint(map_flags, BPF_F_NO_PREALLOC); } filter SEC(".maps"); enum bperf_filter_type type = 0; int enabled = 0; +int inherit; SEC("fexit/XXX") int BPF_PROG(fexit_XXX) { struct bpf_perf_event_value *diff_val, *accum_val; __u32 filter_key, zero = 0; - __u32 *accum_key; + __u32 accum_key; + struct bperf_filter_value *fval; if (!enabled) return 0; switch (type) { case BPERF_FILTER_GLOBAL: - accum_key = &zero; + accum_key = zero; goto do_add; case BPERF_FILTER_CPU: filter_key = bpf_get_smp_processor_id(); @@ -49,22 +55,34 @@ int BPF_PROG(fexit_XXX) filter_key = bpf_get_current_pid_tgid() & 0xffffffff; break; case BPERF_FILTER_TGID: - filter_key = bpf_get_current_pid_tgid() >> 32; + /* Use pid as the filter_key to exclude new task counts + * when inherit is disabled. Don't worry about the existing + * children in TGID losing their counts, bpf_counter has + * already added them to the filter map via perf_thread_map + * before this bpf prog runs. + */ + filter_key = inherit ? + bpf_get_current_pid_tgid() >> 32 : + bpf_get_current_pid_tgid() & 0xffffffff; break; default: return 0; } - accum_key = bpf_map_lookup_elem(&filter, &filter_key); - if (!accum_key) + fval = bpf_map_lookup_elem(&filter, &filter_key); + if (!fval) return 0; + accum_key = fval->accum_key; + if (fval->exited) + bpf_map_delete_elem(&filter, &filter_key); + do_add: diff_val = bpf_map_lookup_elem(&diff_readings, &zero); if (!diff_val) return 0; - accum_val = bpf_map_lookup_elem(&accum_readings, accum_key); + accum_val = bpf_map_lookup_elem(&accum_readings, &accum_key); if (!accum_val) return 0; @@ -75,4 +93,70 @@ int BPF_PROG(fexit_XXX) return 0; } +/* The program is only used for PID or TGID filter types. */ +SEC("tp_btf/task_newtask") +int BPF_PROG(on_newtask, struct task_struct *task, __u64 clone_flags) +{ + __u32 parent_key, child_key; + struct bperf_filter_value *parent_fval; + struct bperf_filter_value child_fval = { 0 }; + + if (!enabled) + return 0; + + switch (type) { + case BPERF_FILTER_PID: + parent_key = bpf_get_current_pid_tgid() & 0xffffffff; + child_key = task->pid; + break; + case BPERF_FILTER_TGID: + parent_key = bpf_get_current_pid_tgid() >> 32; + child_key = task->tgid; + if (child_key == parent_key) + return 0; + break; + default: + return 0; + } + + /* Check if the current task is one of the target tasks to be counted */ + parent_fval = bpf_map_lookup_elem(&filter, &parent_key); + if (!parent_fval) + return 0; + + /* Start counting for the new task by adding it into filter map, + * inherit the accum key of its parent task so that they can be + * counted together. + */ + child_fval.accum_key = parent_fval->accum_key; + child_fval.exited = 0; + bpf_map_update_elem(&filter, &child_key, &child_fval, BPF_NOEXIST); + + return 0; +} + +/* The program is only used for PID or TGID filter types. */ +SEC("tp_btf/sched_process_exit") +int BPF_PROG(on_exittask, struct task_struct *task) +{ + __u32 pid; + struct bperf_filter_value *fval; + + if (!enabled) + return 0; + + /* Stop counting for this task by removing it from filter map. + * For TGID type, if the pid can be found in the map, it means that + * this pid belongs to the leader task. After the task exits, the + * tgid of its child tasks (if any) will be 1, so the pid can be + * safely removed. + */ + pid = task->pid; + fval = bpf_map_lookup_elem(&filter, &pid); + if (fval) + fval->exited = 1; + + return 0; +} + char LICENSE[] SEC("license") = "Dual BSD/GPL"; diff --git a/tools/perf/util/bpf_skel/bperf_u.h b/tools/perf/util/bpf_skel/bperf_u.h index 1ce0c2c905c11..4a4a753980bef 100644 --- a/tools/perf/util/bpf_skel/bperf_u.h +++ b/tools/perf/util/bpf_skel/bperf_u.h @@ -11,4 +11,9 @@ enum bperf_filter_type { BPERF_FILTER_TGID, }; +struct bperf_filter_value { + __u32 accum_key; + __u8 exited; +}; + #endif /* __BPERF_STAT_U_H */ diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index bffbdd216a6a5..e51f0a676a228 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -93,34 +93,6 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) return r; } -/* - * This function splits the buffer by newlines and colors the lines individually. - * - * Returns 0 on success. - */ -int color_fwrite_lines(FILE *fp, const char *color, - size_t count, const char *buf) -{ - if (!*color) - return fwrite(buf, count, 1, fp) != 1; - - while (count) { - char *p = memchr(buf, '\n', count); - - if (p != buf && (fputs(color, fp) < 0 || - fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 || - fputs(PERF_COLOR_RESET, fp) < 0)) - return -1; - if (!p) - return 0; - if (fputc('\n', fp) < 0) - return -1; - count -= p + 1 - buf; - buf = p + 1; - } - return 0; -} - const char *get_percent_color(double percent) { const char *color = PERF_COLOR_NORMAL; diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 01f7bed21c9b2..9a7248dbe2d74 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -2,6 +2,7 @@ #ifndef __PERF_COLOR_H #define __PERF_COLOR_H +#include #include #include @@ -22,6 +23,7 @@ #define MIN_GREEN 0.5 #define MIN_RED 5.0 +#define PERF_COLOR_DELETE_LINE "\033[A\33[2K\r" /* * This variable stores the value of color.ui */ @@ -37,12 +39,11 @@ int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) int color_vsnprintf(char *bf, size_t size, const char *color, const char *fmt, va_list args); int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); -int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); -int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); -int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); +int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) __printf(3, 4); +int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...) __printf(4, 5); int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); -int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); -int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) __printf(3, 4); +int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...) __printf(3, 4); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 7a650de0db83f..68f9407ca74b9 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -912,6 +912,7 @@ void set_buildid_dir(const char *dir) struct perf_config_scan_data { const char *name; const char *fmt; + const char *value; va_list args; int ret; }; @@ -939,3 +940,24 @@ int perf_config_scan(const char *name, const char *fmt, ...) return d.ret; } + +static int perf_config_get_cb(const char *var, const char *value, void *data) +{ + struct perf_config_scan_data *d = data; + + if (!strcmp(var, d->name)) + d->value = value; + + return 0; +} + +const char *perf_config_get(const char *name) +{ + struct perf_config_scan_data d = { + .name = name, + .value = NULL, + }; + + perf_config(perf_config_get_cb, &d); + return d.value; +} diff --git a/tools/perf/util/config.h b/tools/perf/util/config.h index 2e5e808928a55..9971313d61c1e 100644 --- a/tools/perf/util/config.h +++ b/tools/perf/util/config.h @@ -30,6 +30,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *); int perf_default_config(const char *, const char *, void *); int perf_config(config_fn_t fn, void *); int perf_config_scan(const char *name, const char *fmt, ...) __scanf(2, 3); +const char *perf_config_get(const char *name); int perf_config_set(struct perf_config_set *set, config_fn_t fn, void *data); int perf_config_int(int *dest, const char *, const char *); diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index b78ef0262135c..b85a8837bddcd 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -685,9 +685,14 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params, } if (d_params->operation == CS_ETM_OPERATION_DECODE) { + int decode_flags = OCSD_CREATE_FLG_FULL_DECODER; +#ifdef OCSD_OPFLG_N_UNCOND_DIR_BR_CHK + decode_flags |= OCSD_OPFLG_N_UNCOND_DIR_BR_CHK | OCSD_OPFLG_CHK_RANGE_CONTINUE | + ETM4_OPFLG_PKTDEC_AA64_OPCODE_CHK; +#endif if (ocsd_dt_create_decoder(decoder->dcd_tree, decoder->decoder_name, - OCSD_CREATE_FLG_FULL_DECODER, + decode_flags, trace_config, &csid)) return -1; diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 40f047baef810..0bf9e5c27b599 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -2490,12 +2490,6 @@ static void cs_etm__clear_all_traceid_queues(struct cs_etm_queue *etmq) /* Ignore return value */ cs_etm__process_traceid_queue(etmq, tidq); - - /* - * Generate an instruction sample with the remaining - * branchstack entries. - */ - cs_etm__flush(etmq, tidq); } } @@ -2638,7 +2632,7 @@ static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm) while (1) { if (!etm->heap.heap_cnt) - goto out; + break; /* Take the entry at the top of the min heap */ cs_queue_nr = etm->heap.heap_array[0].queue_nr; @@ -2721,6 +2715,23 @@ static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm) ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, cs_timestamp); } + for (i = 0; i < etm->queues.nr_queues; i++) { + struct int_node *inode; + + etmq = etm->queues.queue_array[i].priv; + if (!etmq) + continue; + + intlist__for_each_entry(inode, etmq->traceid_queues_list) { + int idx = (int)(intptr_t)inode->priv; + + /* Flush any remaining branch stack entries */ + tidq = etmq->traceid_queues[idx]; + ret = cs_etm__end_block(etmq, tidq); + if (ret) + return ret; + } + } out: return ret; } diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 021e9b1d5cc52..f0599c61fab47 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -36,7 +36,7 @@ #include "util/sample.h" #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif #define pr_N(n, fmt, ...) \ diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index 20bfb0884e9ea..8304cd2d4a9ce 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -28,7 +28,7 @@ #include "util/tool.h" #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif struct convert_json { diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index d633d15329fa0..995f6bb05b5f8 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -27,7 +27,7 @@ #include #ifdef HAVE_LIBTRACEEVENT -#include +#include #else #define LIBTRACEEVENT_VERSION 0 #endif diff --git a/tools/perf/util/debuginfo.h b/tools/perf/util/debuginfo.h index ad6422c3f8ca3..a52d69932815c 100644 --- a/tools/perf/util/debuginfo.h +++ b/tools/perf/util/debuginfo.h @@ -5,7 +5,7 @@ #include #include -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT #include "dwarf-aux.h" @@ -25,7 +25,7 @@ void debuginfo__delete(struct debuginfo *dbg); int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, bool adjust_offset); -#else /* HAVE_DWARF_SUPPORT */ +#else /* HAVE_LIBDW_SUPPORT */ /* dummy debug information structure */ struct debuginfo { @@ -49,7 +49,7 @@ static inline int debuginfo__get_text_offset(struct debuginfo *dbg __maybe_unuse return -EINVAL; } -#endif /* HAVE_DWARF_SUPPORT */ +#endif /* HAVE_LIBDW_SUPPORT */ #ifdef HAVE_DEBUGINFOD_SUPPORT int get_source_from_debuginfod(const char *raw_path, const char *sbuild_id, diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index f05ba7739c1e9..41a2b08670dc5 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -18,6 +18,7 @@ #include "disasm.h" #include "disasm_bpf.h" #include "dso.h" +#include "dwarf-regs.h" #include "env.h" #include "evsel.h" #include "map.h" @@ -151,14 +152,14 @@ static struct arch architectures[] = { .memory_ref_char = '(', .imm_char = '$', }, -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT .update_insn_state = update_insn_state_x86, #endif }, { .name = "powerpc", .init = powerpc__annotate_init, -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT .update_insn_state = update_insn_state_powerpc, #endif }, @@ -1423,6 +1424,15 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym, } #endif +#if !defined(HAVE_LIBCAPSTONE_SUPPORT) || !defined(HAVE_LIBLLVM_SUPPORT) +static void symbol__disassembler_missing(const char *disassembler, const char *filename, + struct symbol *sym) +{ + pr_debug("The %s disassembler isn't linked in for %s in %s\n", + disassembler, sym->name, filename); +} +#endif + #ifdef HAVE_LIBCAPSTONE_SUPPORT static void print_capstone_detail(cs_insn *insn, char *buf, size_t len, struct annotate_args *args, u64 addr) @@ -1573,7 +1583,7 @@ static int symbol__disassemble_capstone_powerpc(char *filename, struct symbol *s dl = disasm_line__new(args); if (dl == NULL) - goto err; + break; annotation_line__add(&dl->al, ¬es->src->source); @@ -1603,18 +1613,6 @@ static int symbol__disassemble_capstone_powerpc(char *filename, struct symbol *s err: if (fd >= 0) close(fd); - if (needs_cs_close) { - struct disasm_line *tmp; - - /* - * It probably failed in the middle of the above loop. - * Release any resources it might add. - */ - list_for_each_entry_safe(dl, tmp, ¬es->src->source, al.node) { - list_del(&dl->al.node); - free(dl); - } - } count = -1; goto out; } @@ -1627,12 +1625,12 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, u64 start = map__rip_2objdump(map, sym->start); u64 len; u64 offset; - int i, count; + int i, count, free_count; bool is_64bit = false; bool needs_cs_close = false; u8 *buf = NULL; csh handle; - cs_insn *insn; + cs_insn *insn = NULL; char disasm_buf[512]; struct disasm_line *dl; @@ -1664,7 +1662,7 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, needs_cs_close = true; - count = cs_disasm(handle, buf, len, start, len, &insn); + free_count = count = cs_disasm(handle, buf, len, start, len, &insn); for (i = 0, offset = 0; i < count; i++) { int printed; @@ -1702,8 +1700,11 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, } out: - if (needs_cs_close) + if (needs_cs_close) { cs_close(&handle); + if (free_count > 0) + cs_free(insn, free_count); + } free(buf); return count < 0 ? count : 0; @@ -1717,13 +1718,27 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, */ list_for_each_entry_safe(dl, tmp, ¬es->src->source, al.node) { list_del(&dl->al.node); - free(dl); + disasm_line__free(dl); } } count = -1; goto out; } -#endif +#else // HAVE_LIBCAPSTONE_SUPPORT +static int symbol__disassemble_capstone(char *filename, struct symbol *sym, + struct annotate_args *args __maybe_unused) +{ + symbol__disassembler_missing("capstone", filename, sym); + return -1; +} + +static int symbol__disassemble_capstone_powerpc(char *filename, struct symbol *sym, + struct annotate_args *args __maybe_unused) +{ + symbol__disassembler_missing("capstone powerpc", filename, sym); + return -1; +} +#endif // HAVE_LIBCAPSTONE_SUPPORT static int symbol__disassemble_raw(char *filename, struct symbol *sym, struct annotate_args *args) @@ -1782,7 +1797,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym, sprintf(args->line, "%x", line[i]); dl = disasm_line__new(args); if (dl == NULL) - goto err; + break; annotation_line__add(&dl->al, ¬es->src->source); offset += 4; @@ -1991,7 +2006,14 @@ static int symbol__disassemble_llvm(char *filename, struct symbol *sym, free(line_storage); return ret; } -#endif +#else // HAVE_LIBLLVM_SUPPORT +static int symbol__disassemble_llvm(char *filename, struct symbol *sym, + struct annotate_args *args __maybe_unused) +{ + symbol__disassembler_missing("LLVM", filename, sym); + return -1; +} +#endif // HAVE_LIBLLVM_SUPPORT /* * Possibly create a new version of line with tabs expanded. Returns the @@ -2053,17 +2075,14 @@ static char *expand_tabs(char *line, char **storage, size_t *storage_len) return new_line; } -int symbol__disassemble(struct symbol *sym, struct annotate_args *args) +static int symbol__disassemble_objdump(const char *filename, struct symbol *sym, + struct annotate_args *args) { struct annotation_options *opts = &annotate_opts; struct map *map = args->ms.map; struct dso *dso = map__dso(map); char *command; FILE *file; - char symfs_filename[PATH_MAX]; - struct kcore_extract kce; - bool delete_extract = false; - bool decomp = false; int lineno = 0; char *fileloc = NULL; int nline; @@ -2078,77 +2097,7 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) NULL, }; struct child_process objdump_process; - int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); - - if (err) - return err; - - pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, - symfs_filename, sym->name, map__unmap_ip(map, sym->start), - map__unmap_ip(map, sym->end)); - - pr_debug("annotating [%p] %30s : [%p] %30s\n", - dso, dso__long_name(dso), sym, sym->name); - - if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) { - return symbol__disassemble_bpf(sym, args); - } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE) { - return symbol__disassemble_bpf_image(sym, args); - } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) { - return -1; - } else if (dso__is_kcore(dso)) { - kce.kcore_filename = symfs_filename; - kce.addr = map__rip_2objdump(map, sym->start); - kce.offs = sym->start; - kce.len = sym->end - sym->start; - if (!kcore_extract__create(&kce)) { - delete_extract = true; - strlcpy(symfs_filename, kce.extract_filename, - sizeof(symfs_filename)); - } - } else if (dso__needs_decompress(dso)) { - char tmp[KMOD_DECOMP_LEN]; - - if (dso__decompress_kmodule_path(dso, symfs_filename, - tmp, sizeof(tmp)) < 0) - return -1; - - decomp = true; - strcpy(symfs_filename, tmp); - } - - /* - * For powerpc data type profiling, use the dso__data_read_offset - * to read raw instruction directly and interpret the binary code - * to understand instructions and register fields. For sort keys as - * type and typeoff, disassemble to mnemonic notation is - * not required in case of powerpc. - */ - if (arch__is(args->arch, "powerpc")) { - extern const char *sort_order; - - if (sort_order && !strstr(sort_order, "sym")) { - err = symbol__disassemble_raw(symfs_filename, sym, args); - if (err == 0) - goto out_remove_tmp; -#ifdef HAVE_LIBCAPSTONE_SUPPORT - err = symbol__disassemble_capstone_powerpc(symfs_filename, sym, args); - if (err == 0) - goto out_remove_tmp; -#endif - } - } - -#ifdef HAVE_LIBLLVM_SUPPORT - err = symbol__disassemble_llvm(symfs_filename, sym, args); - if (err == 0) - goto out_remove_tmp; -#endif -#ifdef HAVE_LIBCAPSTONE_SUPPORT - err = symbol__disassemble_capstone(symfs_filename, sym, args); - if (err == 0) - goto out_remove_tmp; -#endif + int err; err = asprintf(&command, "%s %s%s --start-address=0x%016" PRIx64 @@ -2171,13 +2120,13 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (err < 0) { pr_err("Failure allocating memory for the command to run\n"); - goto out_remove_tmp; + return err; } pr_debug("Executing: %s\n", command); objdump_argv[2] = command; - objdump_argv[4] = symfs_filename; + objdump_argv[4] = filename; /* Create a pipe to read from for stdout */ memset(&objdump_process, 0, sizeof(objdump_process)); @@ -2215,8 +2164,8 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) break; /* Skip lines containing "filename:" */ - match = strstr(line, symfs_filename); - if (match && match[strlen(symfs_filename)] == ':') + match = strstr(line, filename); + if (match && match[strlen(filename)] == ':') continue; expanded_line = strim(line); @@ -2261,7 +2210,150 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) out_free_command: free(command); + return err; +} + +static int annotation_options__init_disassemblers(struct annotation_options *options) +{ + char *disassembler; + + if (options->disassemblers_str == NULL) { + const char *default_disassemblers_str = +#ifdef HAVE_LIBLLVM_SUPPORT + "llvm," +#endif +#ifdef HAVE_LIBCAPSTONE_SUPPORT + "capstone," +#endif + "objdump"; + + options->disassemblers_str = strdup(default_disassemblers_str); + if (!options->disassemblers_str) + goto out_enomem; + } + + disassembler = strdup(options->disassemblers_str); + if (disassembler == NULL) + goto out_enomem; + + while (1) { + char *comma = strchr(disassembler, ','); + + if (comma != NULL) + *comma = '\0'; + + options->disassemblers[options->nr_disassemblers++] = strim(disassembler); + + if (comma == NULL) + break; + + disassembler = comma + 1; + + if (options->nr_disassemblers >= MAX_DISASSEMBLERS) { + pr_debug("annotate.disassemblers can have at most %d entries, ignoring \"%s\"\n", + MAX_DISASSEMBLERS, disassembler); + break; + } + } + + return 0; + +out_enomem: + pr_err("Not enough memory for annotate.disassemblers\n"); + return -1; +} + +int symbol__disassemble(struct symbol *sym, struct annotate_args *args) +{ + struct annotation_options *options = args->options; + struct map *map = args->ms.map; + struct dso *dso = map__dso(map); + char symfs_filename[PATH_MAX]; + bool delete_extract = false; + struct kcore_extract kce; + const char *disassembler; + bool decomp = false; + int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); + + if (err) + return err; + + pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, + symfs_filename, sym->name, map__unmap_ip(map, sym->start), + map__unmap_ip(map, sym->end)); + + pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name); + if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) { + return symbol__disassemble_bpf(sym, args); + } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE) { + return symbol__disassemble_bpf_image(sym, args); + } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) { + return -1; + } else if (dso__is_kcore(dso)) { + kce.addr = map__rip_2objdump(map, sym->start); + kce.kcore_filename = symfs_filename; + kce.len = sym->end - sym->start; + kce.offs = sym->start; + + if (!kcore_extract__create(&kce)) { + delete_extract = true; + strlcpy(symfs_filename, kce.extract_filename, sizeof(symfs_filename)); + } + } else if (dso__needs_decompress(dso)) { + char tmp[KMOD_DECOMP_LEN]; + + if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) + return -1; + + decomp = true; + strcpy(symfs_filename, tmp); + } + + /* + * For powerpc data type profiling, use the dso__data_read_offset to + * read raw instruction directly and interpret the binary code to + * understand instructions and register fields. For sort keys as type + * and typeoff, disassemble to mnemonic notation is not required in + * case of powerpc. + */ + if (arch__is(args->arch, "powerpc")) { + extern const char *sort_order; + + if (sort_order && !strstr(sort_order, "sym")) { + err = symbol__disassemble_raw(symfs_filename, sym, args); + if (err == 0) + goto out_remove_tmp; + + err = symbol__disassemble_capstone_powerpc(symfs_filename, sym, args); + if (err == 0) + goto out_remove_tmp; + } + } + + err = annotation_options__init_disassemblers(options); + if (err) + goto out_remove_tmp; + + err = -1; + + for (int i = 0; i < options->nr_disassemblers && err != 0; ++i) { + disassembler = options->disassemblers[i]; + + if (!strcmp(disassembler, "llvm")) + err = symbol__disassemble_llvm(symfs_filename, sym, args); + else if (!strcmp(disassembler, "capstone")) + err = symbol__disassemble_capstone(symfs_filename, sym, args); + else if (!strcmp(disassembler, "objdump")) + err = symbol__disassemble_objdump(symfs_filename, sym, args); + else + pr_debug("Unknown disassembler %s, skipping...\n", disassembler); + } + + if (err == 0) { + pr_debug("Disassembled with %s\nannotate.disassemblers=%s\n", + disassembler, options->disassemblers_str); + } out_remove_tmp: if (decomp) unlink(symfs_filename); diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h index f56beedeb9dad..c135db2416b5c 100644 --- a/tools/perf/util/disasm.h +++ b/tools/perf/util/disasm.h @@ -4,7 +4,7 @@ #include "map_symbol.h" -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT #include "dwarf-aux.h" #endif @@ -39,11 +39,15 @@ struct arch { char memory_ref_char; char imm_char; } objdump; -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT void (*update_insn_state)(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, struct disasm_line *dl); #endif + /** @e_machine: ELF machine associated with arch. */ + unsigned int e_machine; + /** @e_flags: Optional ELF flags associated with arch. */ + unsigned int e_flags; }; struct ins { diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 92eb9c8dc3e52..559c953ca1727 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1182,7 +1182,6 @@ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf) return ret < 0 ? ret : strbuf_addf(buf, "\t%s", dwarf_diename(vr_die)); } -#if defined(HAVE_DWARF_GETLOCATIONS_SUPPORT) || defined(HAVE_DWARF_CFI_SUPPORT) static int reg_from_dwarf_op(Dwarf_Op *op) { switch (op->atom) { @@ -1245,9 +1244,7 @@ static bool check_allowed_ops(Dwarf_Op *ops, size_t nops) } return true; } -#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT || HAVE_DWARF_CFI_SUPPORT */ -#ifdef HAVE_DWARF_GETLOCATIONS_SUPPORT /** * die_get_var_innermost_scope - Get innermost scope range of given variable DIE * @sp_die: a subprogram DIE @@ -1697,9 +1694,7 @@ void die_collect_global_vars(Dwarf_Die *cu_die, struct die_var_type **var_types) die_find_child(cu_die, __die_collect_global_vars_cb, (void *)var_types, &die_mem); } -#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */ -#ifdef HAVE_DWARF_CFI_SUPPORT /** * die_get_cfa - Get frame base information * @dwarf: a Dwarf info @@ -1732,7 +1727,6 @@ int die_get_cfa(Dwarf *dwarf, u64 pc, int *preg, int *poffset) } return -1; } -#endif /* HAVE_DWARF_CFI_SUPPORT */ /* * die_has_loclist - Check if DW_AT_location of @vr_die is a location list diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index bd75058125693..892c8c5c23fc0 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -9,7 +9,6 @@ #include #include #include -#include struct strbuf; @@ -157,8 +156,6 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset, Dwarf_Die *die_m /* Return type info where the pointer and offset point to */ Dwarf_Die *die_deref_ptr_type(Dwarf_Die *ptr_die, int offset, Dwarf_Die *die_mem); -#ifdef HAVE_DWARF_GETLOCATIONS_SUPPORT - /* Get byte offset range of given variable DIE */ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf); @@ -177,58 +174,7 @@ void die_collect_vars(Dwarf_Die *sc_die, struct die_var_type **var_types); /* Save all global variables in this CU */ void die_collect_global_vars(Dwarf_Die *cu_die, struct die_var_type **var_types); -#else /* HAVE_DWARF_GETLOCATIONS_SUPPORT */ - -static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused, - Dwarf_Die *vr_die __maybe_unused, - struct strbuf *buf __maybe_unused) -{ - return -ENOTSUP; -} - -static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unused, - Dwarf_Addr pc __maybe_unused, - int reg __maybe_unused, - int *poffset __maybe_unused, - bool is_fbreg __maybe_unused, - Dwarf_Die *die_mem __maybe_unused) -{ - return NULL; -} - -static inline Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die __maybe_unused, - Dwarf_Addr addr __maybe_unused, - Dwarf_Die *die_mem __maybe_unused, - int *offset __maybe_unused) -{ - return NULL; -} - -static inline void die_collect_vars(Dwarf_Die *sc_die __maybe_unused, - struct die_var_type **var_types __maybe_unused) -{ -} - -static inline void die_collect_global_vars(Dwarf_Die *cu_die __maybe_unused, - struct die_var_type **var_types __maybe_unused) -{ -} - -#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */ - -#ifdef HAVE_DWARF_CFI_SUPPORT - /* Get the frame base information from CFA */ int die_get_cfa(Dwarf *dwarf, u64 pc, int *preg, int *poffset); -#else /* HAVE_DWARF_CFI_SUPPORT */ - -static inline int die_get_cfa(Dwarf *dwarf __maybe_unused, u64 pc __maybe_unused, - int *preg __maybe_unused, int *poffset __maybe_unused) -{ - return -1; -} - -#endif /* HAVE_DWARF_CFI_SUPPORT */ - #endif /* _DWARF_AUX_H */ diff --git a/tools/perf/arch/csky/util/dwarf-regs.c b/tools/perf/util/dwarf-regs-csky.c similarity index 74% rename from tools/perf/arch/csky/util/dwarf-regs.c rename to tools/perf/util/dwarf-regs-csky.c index ca86ecaeacbb5..d38ef1f07f3e3 100644 --- a/tools/perf/arch/csky/util/dwarf-regs.c +++ b/tools/perf/util/dwarf-regs-csky.c @@ -5,9 +5,8 @@ #include #include -#if defined(__CSKYABIV2__) -#define CSKY_MAX_REGS 73 -const char *csky_dwarf_regs_table[CSKY_MAX_REGS] = { +#define CSKY_ABIV2_MAX_REGS 73 +const char *csky_dwarf_regs_table_abiv2[CSKY_ABIV2_MAX_REGS] = { /* r0 ~ r8 */ "%a0", "%a1", "%a2", "%a3", "%regs0", "%regs1", "%regs2", "%regs3", /* r9 ~ r15 */ @@ -26,9 +25,9 @@ const char *csky_dwarf_regs_table[CSKY_MAX_REGS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "%epc", }; -#else -#define CSKY_MAX_REGS 57 -const char *csky_dwarf_regs_table[CSKY_MAX_REGS] = { + +#define CSKY_ABIV1_MAX_REGS 57 +const char *csky_dwarf_regs_table_abiv1[CSKY_ABIV1_MAX_REGS] = { /* r0 ~ r8 */ "%sp", "%regs9", "%a0", "%a1", "%a2", "%a3", "%regs0", "%regs1", /* r9 ~ r15 */ @@ -41,9 +40,11 @@ const char *csky_dwarf_regs_table[CSKY_MAX_REGS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "%epc", }; -#endif -const char *get_arch_regstr(unsigned int n) +const char *get_csky_regstr(unsigned int n, unsigned int flags) { - return (n < CSKY_MAX_REGS) ? csky_dwarf_regs_table[n] : NULL; + if (flags & EF_CSKY_ABIV2) + return (n < CSKY_ABIV2_MAX_REGS) ? csky_dwarf_regs_table_abiv2[n] : NULL; + + return (n < CSKY_ABIV1_MAX_REGS) ? csky_dwarf_regs_table_abiv1[n] : NULL; } diff --git a/tools/perf/util/dwarf-regs-powerpc.c b/tools/perf/util/dwarf-regs-powerpc.c new file mode 100644 index 0000000000000..caf77a234c785 --- /dev/null +++ b/tools/perf/util/dwarf-regs-powerpc.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Mapping of DWARF debug register numbers into register names. + * + * Copyright (C) 2010 Ian Munsie, IBM Corporation. + */ + +#include + +#define PPC_OP(op) (((op) >> 26) & 0x3F) +#define PPC_RA(a) (((a) >> 16) & 0x1f) +#define PPC_RT(t) (((t) >> 21) & 0x1f) +#define PPC_RB(b) (((b) >> 11) & 0x1f) +#define PPC_D(D) ((D) & 0xfffe) +#define PPC_DS(DS) ((DS) & 0xfffc) +#define OP_LD 58 +#define OP_STD 62 + +static int get_source_reg(u32 raw_insn) +{ + return PPC_RA(raw_insn); +} + +static int get_target_reg(u32 raw_insn) +{ + return PPC_RT(raw_insn); +} + +static int get_offset_opcode(u32 raw_insn) +{ + int opcode = PPC_OP(raw_insn); + + /* DS- form */ + if ((opcode == OP_LD) || (opcode == OP_STD)) + return PPC_DS(raw_insn); + else + return PPC_D(raw_insn); +} + +/* + * Fills the required fields for op_loc depending on if it + * is a source or target. + * D form: ins RT,D(RA) -> src_reg1 = RA, offset = D, dst_reg1 = RT + * DS form: ins RT,DS(RA) -> src_reg1 = RA, offset = DS, dst_reg1 = RT + * X form: ins RT,RA,RB -> src_reg1 = RA, src_reg2 = RB, dst_reg1 = RT + */ +void get_powerpc_regs(u32 raw_insn, int is_source, + struct annotated_op_loc *op_loc) +{ + if (is_source) + op_loc->reg1 = get_source_reg(raw_insn); + else + op_loc->reg1 = get_target_reg(raw_insn); + + if (op_loc->multi_regs) + op_loc->reg2 = PPC_RB(raw_insn); + + /* TODO: Implement offset handling for X Form */ + if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31)) + op_loc->offset = get_offset_opcode(raw_insn); +} diff --git a/tools/perf/util/dwarf-regs-x86.c b/tools/perf/util/dwarf-regs-x86.c new file mode 100644 index 0000000000000..7a55c65e8da6e --- /dev/null +++ b/tools/perf/util/dwarf-regs-x86.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. + * Extracted from probe-finder.c + * + * Written by Masami Hiramatsu + */ + +#include /* for EINVAL */ +#include /* for strcmp */ +#include /* for ARRAY_SIZE */ +#include + +struct dwarf_regs_idx { + const char *name; + int idx; +}; + +static const struct dwarf_regs_idx x86_regidx_table[] = { + { "rax", 0 }, { "eax", 0 }, { "ax", 0 }, { "al", 0 }, + { "rdx", 1 }, { "edx", 1 }, { "dx", 1 }, { "dl", 1 }, + { "rcx", 2 }, { "ecx", 2 }, { "cx", 2 }, { "cl", 2 }, + { "rbx", 3 }, { "edx", 3 }, { "bx", 3 }, { "bl", 3 }, + { "rsi", 4 }, { "esi", 4 }, { "si", 4 }, { "sil", 4 }, + { "rdi", 5 }, { "edi", 5 }, { "di", 5 }, { "dil", 5 }, + { "rbp", 6 }, { "ebp", 6 }, { "bp", 6 }, { "bpl", 6 }, + { "rsp", 7 }, { "esp", 7 }, { "sp", 7 }, { "spl", 7 }, + { "r8", 8 }, { "r8d", 8 }, { "r8w", 8 }, { "r8b", 8 }, + { "r9", 9 }, { "r9d", 9 }, { "r9w", 9 }, { "r9b", 9 }, + { "r10", 10 }, { "r10d", 10 }, { "r10w", 10 }, { "r10b", 10 }, + { "r11", 11 }, { "r11d", 11 }, { "r11w", 11 }, { "r11b", 11 }, + { "r12", 12 }, { "r12d", 12 }, { "r12w", 12 }, { "r12b", 12 }, + { "r13", 13 }, { "r13d", 13 }, { "r13w", 13 }, { "r13b", 13 }, + { "r14", 14 }, { "r14d", 14 }, { "r14w", 14 }, { "r14b", 14 }, + { "r15", 15 }, { "r15d", 15 }, { "r15w", 15 }, { "r15b", 15 }, + { "rip", DWARF_REG_PC }, +}; + +int get_x86_regnum(const char *name) +{ + unsigned int i; + + if (*name != '%') + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(x86_regidx_table); i++) + if (!strcmp(x86_regidx_table[i].name, name + 1)) + return x86_regidx_table[i].idx; + return -ENOENT; +} diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c index 5b7f86c0063f2..28a1cfdf26d40 100644 --- a/tools/perf/util/dwarf-regs.c +++ b/tools/perf/util/dwarf-regs.c @@ -13,14 +13,6 @@ #include #include -#ifndef EM_AARCH64 -#define EM_AARCH64 183 /* ARM 64 bit */ -#endif - -#ifndef EM_LOONGARCH -#define EM_LOONGARCH 258 /* LoongArch */ -#endif - /* Define const char * {arch}_register_tbl[] */ #define DEFINE_DWARF_REGSTR_TABLE #include "../arch/x86/include/dwarf-regs-table.h" @@ -28,6 +20,7 @@ #include "../arch/arm64/include/dwarf-regs-table.h" #include "../arch/sh/include/dwarf-regs-table.h" #include "../arch/powerpc/include/dwarf-regs-table.h" +#include "../arch/riscv/include/dwarf-regs-table.h" #include "../arch/s390/include/dwarf-regs-table.h" #include "../arch/sparc/include/dwarf-regs-table.h" #include "../arch/xtensa/include/dwarf-regs-table.h" @@ -37,11 +30,13 @@ #define __get_dwarf_regstr(tbl, n) (((n) < ARRAY_SIZE(tbl)) ? (tbl)[(n)] : NULL) /* Return architecture dependent register string (for kprobe-tracer) */ -const char *get_dwarf_regstr(unsigned int n, unsigned int machine) +const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int flags) { + if (machine == EM_NONE) { + /* Generic arch - use host arch */ + machine = EM_HOST; + } switch (machine) { - case EM_NONE: /* Generic arch - use host arch */ - return get_arch_regstr(n); case EM_386: return __get_dwarf_regstr(x86_32_regstr_tbl, n); case EM_X86_64: @@ -50,6 +45,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine) return __get_dwarf_regstr(arm_regstr_tbl, n); case EM_AARCH64: return __get_dwarf_regstr(aarch64_regstr_tbl, n); + case EM_CSKY: + return get_csky_regstr(n, flags); case EM_SH: return __get_dwarf_regstr(sh_regstr_tbl, n); case EM_S390: @@ -57,6 +54,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine) case EM_PPC: case EM_PPC64: return __get_dwarf_regstr(powerpc_regstr_tbl, n); + case EM_RISCV: + return __get_dwarf_regstr(riscv_regstr_tbl, n); case EM_SPARC: case EM_SPARCV9: return __get_dwarf_regstr(sparc_regstr_tbl, n); @@ -72,13 +71,15 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine) return NULL; } +#if EM_HOST != EM_X86_64 && EM_HOST != EM_386 __weak int get_arch_regnum(const char *name __maybe_unused) { return -ENOTSUP; } +#endif /* Return DWARF register number from architecture register name */ -int get_dwarf_regnum(const char *name, unsigned int machine) +int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags __maybe_unused) { char *regname = strdup(name); int reg = -1; @@ -92,10 +93,21 @@ int get_dwarf_regnum(const char *name, unsigned int machine) if (p) *p = '\0'; + if (machine == EM_NONE) { + /* Generic arch - use host arch */ + machine = EM_HOST; + } switch (machine) { - case EM_NONE: /* Generic arch - use host arch */ +#if EM_HOST != EM_X86_64 && EM_HOST != EM_386 + case EM_HOST: reg = get_arch_regnum(regname); break; +#endif + case EM_X86_64: + fallthrough; + case EM_386: + reg = get_x86_regnum(regname); + break; default: pr_err("ELF MACHINE %x is not supported.\n", machine); } diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 1edbccfc3281d..e2843ca2edd92 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -5,12 +5,14 @@ #include "util/header.h" #include "linux/compiler.h" #include +#include #include #include "cgroup.h" #include #include #include #include +#include "pmu.h" #include "pmus.h" #include "strbuf.h" #include "trace/beauty/beauty.h" @@ -372,7 +374,8 @@ int perf_env__read_pmu_mappings(struct perf_env *env) int perf_env__read_cpuid(struct perf_env *env) { char cpuid[128]; - int err = get_cpuid(cpuid, sizeof(cpuid)); + struct perf_cpu cpu = {-1}; + int err = get_cpuid(cpuid, sizeof(cpuid), cpu); if (err) return err; @@ -639,3 +642,25 @@ void perf_env__find_br_cntr_info(struct perf_env *env, env->pmu_caps->br_cntr_width; } } + +bool perf_env__is_x86_amd_cpu(struct perf_env *env) +{ + static int is_amd; /* 0: Uninitialized, 1: Yes, -1: No */ + + if (is_amd == 0) + is_amd = env->cpuid && strstarts(env->cpuid, "AuthenticAMD") ? 1 : -1; + + return is_amd >= 1 ? true : false; +} + +bool x86__is_amd_cpu(void) +{ + struct perf_env env = { .total_mem = 0, }; + bool is_amd; + + perf_env__cpuid(&env); + is_amd = perf_env__is_x86_amd_cpu(&env); + perf_env__exit(&env); + + return is_amd; +} diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 51b36c36019be..ae604c4edbb7e 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -195,4 +195,8 @@ bool perf_env__has_pmu_mapping(struct perf_env *env, const char *pmu_name); void perf_env__find_br_cntr_info(struct perf_env *env, unsigned int *nr, unsigned int *width); + +bool x86__is_amd_cpu(void); +bool perf_env__is_x86_amd_cpu(struct perf_env *env); + #endif /* __PERF_ENV_H */ diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index f8742e6230a5a..2744c54f404e7 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -66,6 +66,7 @@ enum { PERF_IP_FLAG_VMEXIT = 1ULL << 12, PERF_IP_FLAG_INTR_DISABLE = 1ULL << 13, PERF_IP_FLAG_INTR_TOGGLE = 1ULL << 14, + PERF_IP_FLAG_BRANCH_MISS = 1ULL << 15, }; #define PERF_IP_FLAG_CHARS "bcrosyiABExghDt" diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f14b7e6ff1dcc..f0dd174e2debd 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -319,62 +320,6 @@ struct evsel *evlist__add_sched_switch(struct evlist *evlist, bool system_wide) } #endif -int evlist__add_attrs(struct evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs) -{ - struct evsel *evsel, *n; - LIST_HEAD(head); - size_t i; - - for (i = 0; i < nr_attrs; i++) { - evsel = evsel__new_idx(attrs + i, evlist->core.nr_entries + i); - if (evsel == NULL) - goto out_delete_partial_list; - list_add_tail(&evsel->core.node, &head); - } - - evlist__splice_list_tail(evlist, &head); - - return 0; - -out_delete_partial_list: - __evlist__for_each_entry_safe(&head, n, evsel) - evsel__delete(evsel); - return -1; -} - -int __evlist__add_default_attrs(struct evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs) -{ - size_t i; - - for (i = 0; i < nr_attrs; i++) - event_attr_init(attrs + i); - - return evlist__add_attrs(evlist, attrs, nr_attrs); -} - -__weak int arch_evlist__add_default_attrs(struct evlist *evlist, - struct perf_event_attr *attrs, - size_t nr_attrs) -{ - if (!nr_attrs) - return 0; - - return __evlist__add_default_attrs(evlist, attrs, nr_attrs); -} - -struct evsel *evlist__find_tracepoint_by_id(struct evlist *evlist, int id) -{ - struct evsel *evsel; - - evlist__for_each_entry(evlist, evsel) { - if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT && - (int)evsel->core.attr.config == id) - return evsel; - } - - return NULL; -} - struct evsel *evlist__find_tracepoint_by_name(struct evlist *evlist, const char *name) { struct evsel *evsel; @@ -1199,11 +1144,6 @@ int evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids) return ret; } -int evlist__set_tp_filter_pid(struct evlist *evlist, pid_t pid) -{ - return evlist__set_tp_filter_pids(evlist, 1, &pid); -} - int evlist__append_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids) { char *filter = asprintf__tp_filter_pids(npids, pids); @@ -1484,6 +1424,8 @@ int evlist__prepare_workload(struct evlist *evlist, struct target *target, const int child_ready_pipe[2], go_pipe[2]; char bf; + evlist->workload.cork_fd = -1; + if (pipe(child_ready_pipe) < 0) { perror("failed to create 'ready' pipe"); return -1; @@ -1536,7 +1478,7 @@ int evlist__prepare_workload(struct evlist *evlist, struct target *target, const * For cancelling the workload without actually running it, * the parent will just close workload.cork_fd, without writing * anything, i.e. read will return zero and we just exit() - * here. + * here (See evlist__cancel_workload()). */ if (ret != 1) { if (ret == -1) @@ -1600,7 +1542,7 @@ int evlist__prepare_workload(struct evlist *evlist, struct target *target, const int evlist__start_workload(struct evlist *evlist) { - if (evlist->workload.cork_fd > 0) { + if (evlist->workload.cork_fd >= 0) { char bf = 0; int ret; /* @@ -1611,12 +1553,24 @@ int evlist__start_workload(struct evlist *evlist) perror("unable to write to pipe"); close(evlist->workload.cork_fd); + evlist->workload.cork_fd = -1; return ret; } return 0; } +void evlist__cancel_workload(struct evlist *evlist) +{ + int status; + + if (evlist->workload.cork_fd >= 0) { + close(evlist->workload.cork_fd); + evlist->workload.cork_fd = -1; + waitpid(evlist->workload.pid, &status, WNOHANG); + } +} + int evlist__parse_sample(struct evlist *evlist, union perf_event *event, struct perf_sample *sample) { struct evsel *evsel = evlist__event2evsel(evlist, event); @@ -2619,7 +2573,8 @@ void evlist__uniquify_name(struct evlist *evlist) else attributes = empty_attributes; - if (asprintf(&new_name, "%s/%s/%s", pos->pmu_name, pos->name, attributes + 1)) { + if (asprintf(&new_name, "%s/%s/%s", pos->pmu ? pos->pmu->name : "", + pos->name, attributes + 1)) { free(pos->name); pos->name = new_name; } else { diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index bcc1c6984bb58..adddb1db1ad2b 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -102,18 +102,6 @@ void evlist__delete(struct evlist *evlist); void evlist__add(struct evlist *evlist, struct evsel *entry); void evlist__remove(struct evlist *evlist, struct evsel *evsel); -int evlist__add_attrs(struct evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs); - -int __evlist__add_default_attrs(struct evlist *evlist, - struct perf_event_attr *attrs, size_t nr_attrs); - -int arch_evlist__add_default_attrs(struct evlist *evlist, - struct perf_event_attr *attrs, - size_t nr_attrs); - -#define evlist__add_default_attrs(evlist, array) \ - arch_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array)) - int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs); int evlist__add_dummy(struct evlist *evlist); @@ -144,7 +132,6 @@ int __evlist__set_tracepoints_handlers(struct evlist *evlist, __evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) int evlist__set_tp_filter(struct evlist *evlist, const char *filter); -int evlist__set_tp_filter_pid(struct evlist *evlist, pid_t pid); int evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids); int evlist__append_tp_filter(struct evlist *evlist, const char *filter); @@ -152,7 +139,6 @@ int evlist__append_tp_filter(struct evlist *evlist, const char *filter); int evlist__append_tp_filter_pid(struct evlist *evlist, pid_t pid); int evlist__append_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids); -struct evsel *evlist__find_tracepoint_by_id(struct evlist *evlist, int id); struct evsel *evlist__find_tracepoint_by_name(struct evlist *evlist, const char *name); int evlist__add_pollfd(struct evlist *evlist, int fd); @@ -186,6 +172,7 @@ int evlist__prepare_workload(struct evlist *evlist, struct target *target, const char *argv[], bool pipe_output, void (*exec_error)(int signo, siginfo_t *info, void *ucontext)); int evlist__start_workload(struct evlist *evlist); +void evlist__cancel_workload(struct evlist *evlist); struct option; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index dbf9c8cee3c56..f745723d486ba 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -5,12 +5,16 @@ * Parts came from builtin-{top,stat,record}.c, see those files for further * copyright notes. */ +/* + * Powerpc needs __SANE_USERSPACE_TYPES__ before to select + * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. + */ +#define __SANE_USERSPACE_TYPES__ #include #include #include #include -#include #include #include #include @@ -20,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +56,8 @@ #include "off_cpu.h" #include "pmu.h" #include "pmus.h" +#include "hwmon_pmu.h" +#include "tool_pmu.h" #include "rlimit.h" #include "../perf-sys.h" #include "util/parse-branch-options.h" @@ -64,46 +71,135 @@ #include #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif struct perf_missing_features perf_missing_features; static clockid_t clockid; -static const char *const perf_tool_event__tool_names[PERF_TOOL_MAX] = { - NULL, - "duration_time", - "user_time", - "system_time", -}; - -const char *perf_tool_event__to_str(enum perf_tool_event ev) +static int evsel__no_extra_init(struct evsel *evsel __maybe_unused) { - if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX) - return perf_tool_event__tool_names[ev]; - - return NULL; + return 0; } -enum perf_tool_event perf_tool_event__from_str(const char *str) +static bool test_attr__enabled(void) { - int i; + static bool test_attr__enabled; + static bool test_attr__enabled_tested; + + if (!test_attr__enabled_tested) { + char *dir = getenv("PERF_TEST_ATTR"); - perf_tool_event__for_each_event(i) { - if (!strcmp(str, perf_tool_event__tool_names[i])) - return i; + test_attr__enabled = (dir != NULL); + test_attr__enabled_tested = true; } - return PERF_TOOL_NONE; + return test_attr__enabled; } +#define __WRITE_ASS(str, fmt, data) \ +do { \ + if (fprintf(file, #str "=%"fmt "\n", data) < 0) { \ + perror("test attr - failed to write event file"); \ + fclose(file); \ + return -1; \ + } \ +} while (0) -static int evsel__no_extra_init(struct evsel *evsel __maybe_unused) +#define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field) + +static int store_event(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, + int fd, int group_fd, unsigned long flags) { + FILE *file; + char path[PATH_MAX]; + char *dir = getenv("PERF_TEST_ATTR"); + + snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir, + attr->type, attr->config, fd); + + file = fopen(path, "w+"); + if (!file) { + perror("test attr - failed to open event file"); + return -1; + } + + if (fprintf(file, "[event-%d-%llu-%d]\n", + attr->type, attr->config, fd) < 0) { + perror("test attr - failed to write event file"); + fclose(file); + return -1; + } + + /* syscall arguments */ + __WRITE_ASS(fd, "d", fd); + __WRITE_ASS(group_fd, "d", group_fd); + __WRITE_ASS(cpu, "d", cpu.cpu); + __WRITE_ASS(pid, "d", pid); + __WRITE_ASS(flags, "lu", flags); + + /* struct perf_event_attr */ + WRITE_ASS(type, PRIu32); + WRITE_ASS(size, PRIu32); + WRITE_ASS(config, "llu"); + WRITE_ASS(sample_period, "llu"); + WRITE_ASS(sample_type, "llu"); + WRITE_ASS(read_format, "llu"); + WRITE_ASS(disabled, "d"); + WRITE_ASS(inherit, "d"); + WRITE_ASS(pinned, "d"); + WRITE_ASS(exclusive, "d"); + WRITE_ASS(exclude_user, "d"); + WRITE_ASS(exclude_kernel, "d"); + WRITE_ASS(exclude_hv, "d"); + WRITE_ASS(exclude_idle, "d"); + WRITE_ASS(mmap, "d"); + WRITE_ASS(comm, "d"); + WRITE_ASS(freq, "d"); + WRITE_ASS(inherit_stat, "d"); + WRITE_ASS(enable_on_exec, "d"); + WRITE_ASS(task, "d"); + WRITE_ASS(watermark, "d"); + WRITE_ASS(precise_ip, "d"); + WRITE_ASS(mmap_data, "d"); + WRITE_ASS(sample_id_all, "d"); + WRITE_ASS(exclude_host, "d"); + WRITE_ASS(exclude_guest, "d"); + WRITE_ASS(exclude_callchain_kernel, "d"); + WRITE_ASS(exclude_callchain_user, "d"); + WRITE_ASS(mmap2, "d"); + WRITE_ASS(comm_exec, "d"); + WRITE_ASS(context_switch, "d"); + WRITE_ASS(write_backward, "d"); + WRITE_ASS(namespaces, "d"); + WRITE_ASS(use_clockid, "d"); + WRITE_ASS(wakeup_events, PRIu32); + WRITE_ASS(bp_type, PRIu32); + WRITE_ASS(config1, "llu"); + WRITE_ASS(config2, "llu"); + WRITE_ASS(branch_sample_type, "llu"); + WRITE_ASS(sample_regs_user, "llu"); + WRITE_ASS(sample_stack_user, PRIu32); + + fclose(file); return 0; } -void __weak test_attr__ready(void) { } +#undef __WRITE_ASS +#undef WRITE_ASS + +static void test_attr__open(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, + int fd, int group_fd, unsigned long flags) +{ + int errno_saved = errno; + + if ((fd != -1) && store_event(attr, pid, cpu, fd, group_fd, flags)) { + pr_err("test attr FAILED"); + exit(128); + } + + errno = errno_saved; +} static void evsel__no_extra_fini(struct evsel *evsel __maybe_unused) { @@ -296,9 +392,9 @@ void evsel__init(struct evsel *evsel, evsel->metric_events = NULL; evsel->per_pkg_mask = NULL; evsel->collect_stat = false; - evsel->pmu_name = NULL; evsel->group_pmu_name = NULL; evsel->skippable = false; + evsel->alternate_hw_config = PERF_COUNT_HW_MAX; } struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx) @@ -393,11 +489,6 @@ struct evsel *evsel__clone(struct evsel *orig) if (evsel->group_name == NULL) goto out_err; } - if (orig->pmu_name) { - evsel->pmu_name = strdup(orig->pmu_name); - if (evsel->pmu_name == NULL) - goto out_err; - } if (orig->group_pmu_name) { evsel->group_pmu_name = strdup(orig->group_pmu_name); if (evsel->group_pmu_name == NULL) @@ -421,7 +512,6 @@ struct evsel *evsel__clone(struct evsel *orig) evsel->core.leader = orig->core.leader; evsel->max_events = orig->max_events; - evsel->tool_event = orig->tool_event; free((char *)evsel->unit); evsel->unit = strdup(orig->unit); if (evsel->unit == NULL) @@ -445,6 +535,8 @@ struct evsel *evsel__clone(struct evsel *orig) if (evsel__copy_config_terms(evsel, orig) < 0) goto out_err; + evsel->alternate_hw_config = orig->alternate_hw_config; + return evsel; out_err: @@ -548,7 +640,6 @@ static int evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size) { int colon = 0, r = 0; struct perf_event_attr *attr = &evsel->core.attr; - bool exclude_guest_default = false; #define MOD_PRINT(context, mod) do { \ if (!attr->exclude_##context) { \ @@ -560,17 +651,15 @@ static int evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size) MOD_PRINT(kernel, 'k'); MOD_PRINT(user, 'u'); MOD_PRINT(hv, 'h'); - exclude_guest_default = true; } if (attr->precise_ip) { if (!colon) colon = ++r; r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp"); - exclude_guest_default = true; } - if (attr->exclude_host || attr->exclude_guest == exclude_guest_default) { + if (attr->exclude_host || attr->exclude_guest) { MOD_PRINT(host, 'H'); MOD_PRINT(guest, 'G'); } @@ -617,11 +706,6 @@ static int evsel__sw_name(struct evsel *evsel, char *bf, size_t size) return r + evsel__add_modifiers(evsel, bf + r, size - r); } -static int evsel__tool_name(enum perf_tool_event ev, char *bf, size_t size) -{ - return scnprintf(bf, size, "%s", perf_tool_event__to_str(ev)); -} - static int __evsel__bp_name(char *bf, size_t size, u64 addr, u64 type) { int r; @@ -772,10 +856,7 @@ const char *evsel__name(struct evsel *evsel) break; case PERF_TYPE_SOFTWARE: - if (evsel__is_tool(evsel)) - evsel__tool_name(evsel__tool_event(evsel), bf, sizeof(bf)); - else - evsel__sw_name(evsel, bf, sizeof(bf)); + evsel__sw_name(evsel, bf, sizeof(bf)); break; case PERF_TYPE_TRACEPOINT: @@ -786,6 +867,10 @@ const char *evsel__name(struct evsel *evsel) evsel__bp_name(evsel, bf, sizeof(bf)); break; + case PERF_PMU_TYPE_TOOL: + scnprintf(bf, sizeof(bf), "%s", evsel__tool_pmu_event_name(evsel)); + break; + default: scnprintf(bf, sizeof(bf), "unknown attr type: %d", evsel->core.attr.type); @@ -811,7 +896,7 @@ const char *evsel__metric_id(const struct evsel *evsel) return evsel->metric_id; if (evsel__is_tool(evsel)) - return perf_tool_event__to_str(evsel__tool_event(evsel)); + return evsel__tool_pmu_event_name(evsel); return "unknown"; } @@ -862,7 +947,6 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o { bool function = evsel__is_function_event(evsel); struct perf_event_attr *attr = &evsel->core.attr; - const char *arch = perf_env__arch(evsel__env(evsel)); evsel__set_sample_bit(evsel, CALLCHAIN); @@ -893,6 +977,8 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o if (param->record_mode == CALLCHAIN_DWARF) { if (!function) { + const char *arch = perf_env__arch(evsel__env(evsel)); + evsel__set_sample_bit(evsel, REGS_USER); evsel__set_sample_bit(evsel, STACK_USER); if (opts->sample_user_regs && @@ -1150,7 +1236,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; - attr->inherit = !opts->no_inherit; + attr->inherit = target__has_cpu(&opts->target) ? 0 : !opts->no_inherit; attr->write_backward = opts->overwrite ? 1 : 0; attr->read_format = PERF_FORMAT_LOST; @@ -1172,7 +1258,15 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, */ if (leader->core.nr_members > 1) { attr->read_format |= PERF_FORMAT_GROUP; - attr->inherit = 0; + } + + /* + * Inherit + SAMPLE_READ requires SAMPLE_TID in the read_format + */ + if (attr->inherit) { + evsel__set_sample_bit(evsel, TID); + evsel->core.attr.read_format |= + PERF_FORMAT_ID; } } @@ -1494,7 +1588,6 @@ void evsel__exit(struct evsel *evsel) zfree(&evsel->group_name); zfree(&evsel->name); zfree(&evsel->filter); - zfree(&evsel->pmu_name); zfree(&evsel->group_pmu_name); zfree(&evsel->unit); zfree(&evsel->metric_id); @@ -1503,8 +1596,8 @@ void evsel__exit(struct evsel *evsel) evsel->per_pkg_mask = NULL; zfree(&evsel->metric_events); perf_evsel__object.fini(evsel); - if (evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME || - evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) + if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME || + evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) xyarray__delete(evsel->start_times); } @@ -1684,171 +1777,31 @@ static int evsel__read_group(struct evsel *leader, int cpu_map_idx, int thread) return evsel__process_group_data(leader, cpu_map_idx, thread, data); } -static bool read_until_char(struct io *io, char e) -{ - int c; - - do { - c = io__get_char(io); - if (c == -1) - return false; - } while (c != e); - return true; -} - -static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val) -{ - char buf[256]; - struct io io; - int i; - - io__init(&io, fd, buf, sizeof(buf)); - - /* Skip lines to relevant CPU. */ - for (i = -1; i < cpu.cpu; i++) { - if (!read_until_char(&io, '\n')) - return -EINVAL; - } - /* Skip to "cpu". */ - if (io__get_char(&io) != 'c') return -EINVAL; - if (io__get_char(&io) != 'p') return -EINVAL; - if (io__get_char(&io) != 'u') return -EINVAL; - - /* Skip N of cpuN. */ - if (!read_until_char(&io, ' ')) - return -EINVAL; - - i = 1; - while (true) { - if (io__get_dec(&io, val) != ' ') - break; - if (field == i) - return 0; - i++; - } - return -EINVAL; -} - -static int read_pid_stat_field(int fd, int field, __u64 *val) -{ - char buf[256]; - struct io io; - int c, i; - - io__init(&io, fd, buf, sizeof(buf)); - if (io__get_dec(&io, val) != ' ') - return -EINVAL; - if (field == 1) - return 0; - - /* Skip comm. */ - if (io__get_char(&io) != '(' || !read_until_char(&io, ')')) - return -EINVAL; - if (field == 2) - return -EINVAL; /* String can't be returned. */ - - /* Skip state */ - if (io__get_char(&io) != ' ' || io__get_char(&io) == -1) - return -EINVAL; - if (field == 3) - return -EINVAL; /* String can't be returned. */ - - /* Loop over numeric fields*/ - if (io__get_char(&io) != ' ') - return -EINVAL; - - i = 4; - while (true) { - c = io__get_dec(&io, val); - if (c == -1) - return -EINVAL; - if (c == -2) { - /* Assume a -ve was read */ - c = io__get_dec(&io, val); - *val *= -1; - } - if (c != ' ') - return -EINVAL; - if (field == i) - return 0; - i++; - } - return -EINVAL; -} - -static int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread) +bool __evsel__match(const struct evsel *evsel, u32 type, u64 config) { - __u64 *start_time, cur_time, delta_start; - int fd, err = 0; - struct perf_counts_values *count; - bool adjust = false; - count = perf_counts(evsel->counts, cpu_map_idx, thread); + u32 e_type = evsel->core.attr.type; + u64 e_config = evsel->core.attr.config; - switch (evsel__tool_event(evsel)) { - case PERF_TOOL_DURATION_TIME: - /* - * Pretend duration_time is only on the first CPU and thread, or - * else aggregation will scale duration_time by the number of - * CPUs/threads. - */ - start_time = &evsel->start_time; - if (cpu_map_idx == 0 && thread == 0) - cur_time = rdclock(); - else - cur_time = *start_time; - break; - case PERF_TOOL_USER_TIME: - case PERF_TOOL_SYSTEM_TIME: { - bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME; - - start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread); - fd = FD(evsel, cpu_map_idx, thread); - lseek(fd, SEEK_SET, 0); - if (evsel->pid_stat) { - /* The event exists solely on 1 CPU. */ - if (cpu_map_idx == 0) - err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time); - else - cur_time = 0; - } else { - /* The event is for all threads. */ - if (thread == 0) { - struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus, - cpu_map_idx); - - err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time); - } else { - cur_time = 0; - } - } - adjust = true; - break; - } - case PERF_TOOL_NONE: - case PERF_TOOL_MAX: - default: - err = -EINVAL; + if (e_type != type) { + return type == PERF_TYPE_HARDWARE && evsel->pmu && evsel->pmu->is_core && + evsel->alternate_hw_config == config; } - if (err) - return err; - delta_start = cur_time - *start_time; - if (adjust) { - __u64 ticks_per_sec = sysconf(_SC_CLK_TCK); + if ((type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) && + perf_pmus__supports_extended_type()) + e_config &= PERF_HW_EVENT_MASK; - delta_start *= 1000000000 / ticks_per_sec; - } - count->val = delta_start; - count->ena = count->run = delta_start; - count->lost = 0; - return 0; + return e_config == config; } int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread) { if (evsel__is_tool(evsel)) - return evsel__read_tool(evsel, cpu_map_idx, thread); + return evsel__tool_pmu_read(evsel, cpu_map_idx, thread); + + if (evsel__is_hwmon(evsel)) + return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread); if (evsel__is_retire_lat(evsel)) return evsel__read_retire_lat(evsel, cpu_map_idx, thread); @@ -2042,6 +1995,7 @@ static struct perf_thread_map *empty_thread_map; static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, struct perf_thread_map *threads) { + int ret = 0; int nthreads = perf_thread_map__nr(threads); if ((perf_missing_features.write_backward && evsel->core.attr.write_backward) || @@ -2072,23 +2026,21 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, perf_evsel__alloc_fd(&evsel->core, perf_cpu_map__nr(cpus), nthreads) < 0) return -ENOMEM; - if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME || - evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) && - !evsel->start_times) { - evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64)); - if (!evsel->start_times) - return -ENOMEM; - } + if (evsel__is_tool(evsel)) + ret = evsel__tool_pmu_prepare_open(evsel, cpus, nthreads); evsel->open_flags = PERF_FLAG_FD_CLOEXEC; if (evsel->cgrp) evsel->open_flags |= PERF_FLAG_PID_CGROUP; - return 0; + return ret; } static void evsel__disable_missing_features(struct evsel *evsel) { + if (perf_missing_features.inherit_sample_read && evsel->core.attr.inherit && + (evsel->core.attr.sample_type & PERF_SAMPLE_READ)) + evsel->core.attr.inherit = 0; if (perf_missing_features.branch_counters) evsel->core.attr.branch_sample_type &= ~PERF_SAMPLE_BRANCH_COUNTERS; if (perf_missing_features.read_lost) @@ -2138,120 +2090,346 @@ int evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, return err; } -bool evsel__detect_missing_features(struct evsel *evsel) +static bool has_attr_feature(struct perf_event_attr *attr, unsigned long flags) +{ + int fd = syscall(SYS_perf_event_open, attr, /*pid=*/0, /*cpu=*/-1, + /*group_fd=*/-1, flags); + close(fd); + + if (fd < 0) { + attr->exclude_kernel = 1; + + fd = syscall(SYS_perf_event_open, attr, /*pid=*/0, /*cpu=*/-1, + /*group_fd=*/-1, flags); + close(fd); + } + + if (fd < 0) { + attr->exclude_hv = 1; + + fd = syscall(SYS_perf_event_open, attr, /*pid=*/0, /*cpu=*/-1, + /*group_fd=*/-1, flags); + close(fd); + } + + if (fd < 0) { + attr->exclude_guest = 1; + + fd = syscall(SYS_perf_event_open, attr, /*pid=*/0, /*cpu=*/-1, + /*group_fd=*/-1, flags); + close(fd); + } + + attr->exclude_kernel = 0; + attr->exclude_guest = 0; + attr->exclude_hv = 0; + + return fd >= 0; +} + +static void evsel__detect_missing_pmu_features(struct evsel *evsel) { + struct perf_event_attr attr = { + .type = evsel->core.attr.type, + .config = evsel->core.attr.config, + .disabled = 1, + }; + struct perf_pmu *pmu = evsel->pmu; + int old_errno; + + old_errno = errno; + + if (pmu == NULL) + pmu = evsel->pmu = evsel__find_pmu(evsel); + + if (pmu == NULL || pmu->missing_features.checked) + goto out; + + /* + * Must probe features in the order they were added to the + * perf_event_attr interface. These are kernel core limitation but + * specific to PMUs with branch stack. So we can detect with the given + * hardware event and stop on the first one succeeded. + */ + + /* Please add new feature detection here. */ + + attr.exclude_guest = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + pmu->missing_features.exclude_guest = true; + pr_debug2("switching off exclude_guest for PMU %s\n", pmu->name); + +found: + pmu->missing_features.checked = true; +out: + errno = old_errno; +} + +static void evsel__detect_missing_brstack_features(struct evsel *evsel) +{ + static bool detection_done = false; + struct perf_event_attr attr = { + .type = evsel->core.attr.type, + .config = evsel->core.attr.config, + .disabled = 1, + .sample_type = PERF_SAMPLE_BRANCH_STACK, + .sample_period = 1000, + }; + int old_errno; + + if (detection_done) + return; + + old_errno = errno; + + /* + * Must probe features in the order they were added to the + * perf_event_attr interface. These are PMU specific limitation + * so we can detect with the given hardware event and stop on the + * first one succeeded. + */ + + /* Please add new feature detection here. */ + + attr.branch_sample_type = PERF_SAMPLE_BRANCH_COUNTERS; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.branch_counters = true; + pr_debug2("switching off branch counters support\n"); + + attr.branch_sample_type = PERF_SAMPLE_BRANCH_HW_INDEX; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.branch_hw_idx = true; + pr_debug2("switching off branch HW index support\n"); + + attr.branch_sample_type = PERF_SAMPLE_BRANCH_NO_CYCLES | PERF_SAMPLE_BRANCH_NO_FLAGS; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.lbr_flags = true; + pr_debug2_peo("switching off branch sample type no (cycles/flags)\n"); + +found: + detection_done = true; + errno = old_errno; +} + +static bool evsel__detect_missing_features(struct evsel *evsel) +{ + static bool detection_done = false; + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_TASK_CLOCK, + .disabled = 1, + }; + int old_errno; + + evsel__detect_missing_pmu_features(evsel); + + if (evsel__has_br_stack(evsel)) + evsel__detect_missing_brstack_features(evsel); + + if (detection_done) + goto check; + + old_errno = errno; + /* * Must probe features in the order they were added to the - * perf_event_attr interface. + * perf_event_attr interface. These are kernel core limitation + * not PMU-specific so we can detect with a software event and + * stop on the first one succeeded. */ - if (!perf_missing_features.branch_counters && - (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) { - perf_missing_features.branch_counters = true; - pr_debug2("switching off branch counters support\n"); + + /* Please add new feature detection here. */ + + attr.inherit = true; + attr.sample_type = PERF_SAMPLE_READ; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.inherit_sample_read = true; + pr_debug2("Using PERF_SAMPLE_READ / :S modifier is not compatible with inherit, falling back to no-inherit.\n"); + attr.inherit = false; + attr.sample_type = 0; + + attr.read_format = PERF_FORMAT_LOST; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.read_lost = true; + pr_debug2("switching off PERF_FORMAT_LOST support\n"); + attr.read_format = 0; + + attr.sample_type = PERF_SAMPLE_WEIGHT_STRUCT; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.weight_struct = true; + pr_debug2("switching off weight struct support\n"); + attr.sample_type = 0; + + attr.sample_type = PERF_SAMPLE_CODE_PAGE_SIZE; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.code_page_size = true; + pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support\n"); + attr.sample_type = 0; + + attr.sample_type = PERF_SAMPLE_DATA_PAGE_SIZE; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.data_page_size = true; + pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support\n"); + attr.sample_type = 0; + + attr.cgroup = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.cgroup = true; + pr_debug2_peo("Kernel has no cgroup sampling support\n"); + attr.cgroup = 0; + + attr.aux_output = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.aux_output = true; + pr_debug2_peo("Kernel has no attr.aux_output support\n"); + attr.aux_output = 0; + + attr.bpf_event = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.bpf = true; + pr_debug2_peo("switching off bpf_event\n"); + attr.bpf_event = 0; + + attr.ksymbol = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.ksymbol = true; + pr_debug2_peo("switching off ksymbol\n"); + attr.ksymbol = 0; + + attr.write_backward = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.write_backward = true; + pr_debug2_peo("switching off write_backward\n"); + attr.write_backward = 0; + + attr.use_clockid = 1; + attr.clockid = CLOCK_MONOTONIC; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.clockid = true; + pr_debug2_peo("switching off clockid\n"); + attr.use_clockid = 0; + attr.clockid = 0; + + if (has_attr_feature(&attr, /*flags=*/PERF_FLAG_FD_CLOEXEC)) + goto found; + perf_missing_features.cloexec = true; + pr_debug2_peo("switching off cloexec flag\n"); + + attr.mmap2 = 1; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.mmap2 = true; + pr_debug2_peo("switching off mmap2\n"); + attr.mmap2 = 0; + + /* set this unconditionally? */ + perf_missing_features.sample_id_all = true; + pr_debug2_peo("switching off sample_id_all\n"); + + attr.inherit = 1; + attr.read_format = PERF_FORMAT_GROUP; + if (has_attr_feature(&attr, /*flags=*/0)) + goto found; + perf_missing_features.group_read = true; + pr_debug2_peo("switching off group read\n"); + attr.inherit = 0; + attr.read_format = 0; + +found: + detection_done = true; + errno = old_errno; + +check: + if (evsel->core.attr.inherit && + (evsel->core.attr.sample_type & PERF_SAMPLE_READ) && + perf_missing_features.inherit_sample_read) return true; - } else if (!perf_missing_features.read_lost && - (evsel->core.attr.read_format & PERF_FORMAT_LOST)) { - perf_missing_features.read_lost = true; - pr_debug2("switching off PERF_FORMAT_LOST support\n"); + + if ((evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) && + perf_missing_features.branch_counters) return true; - } else if (!perf_missing_features.weight_struct && - (evsel->core.attr.sample_type & PERF_SAMPLE_WEIGHT_STRUCT)) { - perf_missing_features.weight_struct = true; - pr_debug2("switching off weight struct support\n"); + + if ((evsel->core.attr.read_format & PERF_FORMAT_LOST) && + perf_missing_features.read_lost) return true; - } else if (!perf_missing_features.code_page_size && - (evsel->core.attr.sample_type & PERF_SAMPLE_CODE_PAGE_SIZE)) { - perf_missing_features.code_page_size = true; - pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support, bailing out\n"); - return false; - } else if (!perf_missing_features.data_page_size && - (evsel->core.attr.sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)) { - perf_missing_features.data_page_size = true; - pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support, bailing out\n"); - return false; - } else if (!perf_missing_features.cgroup && evsel->core.attr.cgroup) { - perf_missing_features.cgroup = true; - pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n"); - return false; - } else if (!perf_missing_features.branch_hw_idx && - (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX)) { - perf_missing_features.branch_hw_idx = true; - pr_debug2("switching off branch HW index support\n"); + + if ((evsel->core.attr.sample_type & PERF_SAMPLE_WEIGHT_STRUCT) && + perf_missing_features.weight_struct) return true; - } else if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) { - perf_missing_features.aux_output = true; - pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n"); - return false; - } else if (!perf_missing_features.bpf && evsel->core.attr.bpf_event) { - perf_missing_features.bpf = true; - pr_debug2_peo("switching off bpf_event\n"); + + if (evsel->core.attr.use_clockid && evsel->core.attr.clockid != CLOCK_MONOTONIC && + !perf_missing_features.clockid) { + perf_missing_features.clockid_wrong = true; return true; - } else if (!perf_missing_features.ksymbol && evsel->core.attr.ksymbol) { - perf_missing_features.ksymbol = true; - pr_debug2_peo("switching off ksymbol\n"); + } + + if (evsel->core.attr.use_clockid && perf_missing_features.clockid) return true; - } else if (!perf_missing_features.write_backward && evsel->core.attr.write_backward) { - perf_missing_features.write_backward = true; - pr_debug2_peo("switching off write_backward\n"); - return false; - } else if (!perf_missing_features.clockid_wrong && evsel->core.attr.use_clockid) { - perf_missing_features.clockid_wrong = true; - pr_debug2_peo("switching off clockid\n"); + + if ((evsel->open_flags & PERF_FLAG_FD_CLOEXEC) && + perf_missing_features.cloexec) return true; - } else if (!perf_missing_features.clockid && evsel->core.attr.use_clockid) { - perf_missing_features.clockid = true; - pr_debug2_peo("switching off use_clockid\n"); + + if (evsel->core.attr.mmap2 && perf_missing_features.mmap2) return true; - } else if (!perf_missing_features.cloexec && (evsel->open_flags & PERF_FLAG_FD_CLOEXEC)) { - perf_missing_features.cloexec = true; - pr_debug2_peo("switching off cloexec flag\n"); + + if ((evsel->core.attr.branch_sample_type & (PERF_SAMPLE_BRANCH_NO_FLAGS | + PERF_SAMPLE_BRANCH_NO_CYCLES)) && + perf_missing_features.lbr_flags) return true; - } else if (!perf_missing_features.mmap2 && evsel->core.attr.mmap2) { - perf_missing_features.mmap2 = true; - pr_debug2_peo("switching off mmap2\n"); + + if (evsel->core.attr.inherit && (evsel->core.attr.read_format & PERF_FORMAT_GROUP) && + perf_missing_features.group_read) return true; - } else if (evsel->core.attr.exclude_guest || evsel->core.attr.exclude_host) { - if (evsel->pmu == NULL) - evsel->pmu = evsel__find_pmu(evsel); - - if (evsel->pmu) - evsel->pmu->missing_features.exclude_guest = true; - else { - /* we cannot find PMU, disable attrs now */ - evsel->core.attr.exclude_host = false; - evsel->core.attr.exclude_guest = false; - } - if (evsel->exclude_GH) { - pr_debug2_peo("PMU has no exclude_host/guest support, bailing out\n"); - return false; - } - if (!perf_missing_features.exclude_guest) { - perf_missing_features.exclude_guest = true; - pr_debug2_peo("switching off exclude_guest, exclude_host\n"); - } + if (evsel->core.attr.ksymbol && perf_missing_features.ksymbol) return true; - } else if (!perf_missing_features.sample_id_all) { - perf_missing_features.sample_id_all = true; - pr_debug2_peo("switching off sample_id_all\n"); + + if (evsel->core.attr.bpf_event && perf_missing_features.bpf) return true; - } else if (!perf_missing_features.lbr_flags && - (evsel->core.attr.branch_sample_type & - (PERF_SAMPLE_BRANCH_NO_CYCLES | - PERF_SAMPLE_BRANCH_NO_FLAGS))) { - perf_missing_features.lbr_flags = true; - pr_debug2_peo("switching off branch sample type no (cycles/flags)\n"); + + if ((evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) && + perf_missing_features.branch_hw_idx) return true; - } else if (!perf_missing_features.group_read && - evsel->core.attr.inherit && - (evsel->core.attr.read_format & PERF_FORMAT_GROUP) && - evsel__is_group_leader(evsel)) { - perf_missing_features.group_read = true; - pr_debug2_peo("switching off group read\n"); + + if (evsel->core.attr.sample_id_all && perf_missing_features.sample_id_all) + return true; + + return false; +} + +static bool evsel__handle_error_quirks(struct evsel *evsel, int error) +{ + /* + * AMD core PMU tries to forward events with precise_ip to IBS PMU + * implicitly. But IBS PMU has more restrictions so it can fail with + * supported event attributes. Let's forward it back to the core PMU + * by clearing precise_ip only if it's from precise_max (:P). + */ + if ((error == -EINVAL || error == -ENOENT) && x86__is_amd_cpu() && + evsel->core.attr.precise_ip && evsel->precise_max) { + evsel->core.attr.precise_ip = 0; + pr_debug2_peo("removing precise_ip on AMD\n"); + display_attr(&evsel->core.attr); return true; - } else { - return false; } + + return false; } static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, @@ -2262,13 +2440,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int pid = -1, err, old_errno; enum rlimit_action set_rlimit = NO_CHANGE; - if (evsel__tool_event(evsel) == PERF_TOOL_DURATION_TIME) { - if (evsel->core.attr.sample_period) /* no sampling */ - return -EINVAL; - evsel->start_time = rdclock(); - return 0; - } - if (evsel__is_retire_lat(evsel)) return tpebs_start(evsel->evlist); @@ -2293,6 +2464,17 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, pr_debug3("Opening: %s\n", evsel__name(evsel)); display_attr(&evsel->core.attr); + if (evsel__is_tool(evsel)) { + return evsel__tool_pmu_open(evsel, threads, + start_cpu_map_idx, + end_cpu_map_idx); + } + if (evsel__is_hwmon(evsel)) { + return evsel__hwmon_pmu_open(evsel, threads, + start_cpu_map_idx, + end_cpu_map_idx); + } + for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { for (thread = 0; thread < nthreads; thread++) { @@ -2304,46 +2486,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, if (!evsel->cgrp && !evsel->core.system_wide) pid = perf_thread_map__pid(threads, thread); - if (evsel__tool_event(evsel) == PERF_TOOL_USER_TIME || - evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME) { - bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME; - __u64 *start_time = NULL; - - if (evsel->core.attr.sample_period) { - /* no sampling */ - err = -EINVAL; - goto out_close; - } - if (pid > -1) { - char buf[64]; - - snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); - fd = open(buf, O_RDONLY); - evsel->pid_stat = true; - } else { - fd = open("/proc/stat", O_RDONLY); - } - FD(evsel, idx, thread) = fd; - if (fd < 0) { - err = -errno; - goto out_close; - } - start_time = xyarray__entry(evsel->start_times, idx, thread); - if (pid > -1) { - err = read_pid_stat_field(fd, system ? 15 : 14, - start_time); - } else { - struct perf_cpu cpu; - - cpu = perf_cpu_map__cpu(evsel->core.cpus, idx); - err = read_stat_field(fd, cpu, system ? 3 : 1, - start_time); - } - if (err) - goto out_close; - continue; - } - group_fd = get_group_fd(evsel, idx, thread); if (group_fd == -2) { @@ -2352,8 +2494,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, goto out_close; } - test_attr__ready(); - /* Debug message used by test scripts */ pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx", pid, perf_cpu_map__cpu(cpus, idx).cpu, group_fd, evsel->open_flags); @@ -2374,7 +2514,7 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, bpf_counter__install_pe(evsel, idx, fd); - if (unlikely(test_attr__enabled)) { + if (unlikely(test_attr__enabled())) { test_attr__open(&evsel->core.attr, pid, perf_cpu_map__cpu(cpus, idx), fd, group_fd, evsel->open_flags); @@ -2415,9 +2555,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, return 0; try_fallback: - if (evsel__precise_ip_fallback(evsel)) - goto retry_open; - if (evsel__ignore_missing_thread(evsel, perf_cpu_map__nr(cpus), idx, threads, thread, err)) { /* We just removed 1 thread, so lower the upper nthreads limit. */ @@ -2434,11 +2571,15 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, if (err == -EMFILE && rlimit__increase_nofile(&set_rlimit)) goto retry_open; - if (err != -EINVAL || idx > 0 || thread > 0) - goto out_close; + if (err == -EOPNOTSUPP && evsel__precise_ip_fallback(evsel)) + goto retry_open; - if (evsel__detect_missing_features(evsel)) + if (err == -EINVAL && evsel__detect_missing_features(evsel)) goto fallback_missing_features; + + if (evsel__handle_error_quirks(evsel, err)) + goto retry_open; + out_close: if (err) threads->err_thread = thread; @@ -3244,6 +3385,27 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err, evsel->core.attr.exclude_kernel = 1; evsel->core.attr.exclude_hv = 1; + return true; + } else if (err == EOPNOTSUPP && !evsel->core.attr.exclude_guest && + !evsel->exclude_GH) { + const char *name = evsel__name(evsel); + char *new_name; + const char *sep = ":"; + + /* Is there already the separator in the name. */ + if (strchr(name, '/') || + (strchr(name, ':') && !evsel->is_libpfm_event)) + sep = ""; + + if (asprintf(&new_name, "%s%sH", name, sep) < 0) + return false; + + free(evsel->name); + evsel->name = new_name; + /* Apple M1 requires exclude_guest */ + scnprintf(msg, msgsize, "trying to fall back to excluding guest samples"); + evsel->core.attr.exclude_guest = 1; + return true; } @@ -3415,7 +3577,7 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, return scnprintf(msg, size, "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" - "/bin/dmesg | grep -i perf may provide additional information.\n", + "\"dmesg | grep -i perf\" may provide additional information.\n", err, str_error_r(err, sbuf, sizeof(sbuf)), evsel__name(evsel)); } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 15e745a9a798f..04934a7af1745 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -11,6 +11,7 @@ #include #include "symbol_conf.h" #include "pmus.h" +#include "pmu.h" struct bpf_object; struct cgroup; @@ -22,25 +23,9 @@ struct target; struct hashmap; struct bperf_leader_bpf; struct bperf_follower_bpf; -struct perf_pmu; typedef int (evsel__sb_cb_t)(union perf_event *event, void *data); -enum perf_tool_event { - PERF_TOOL_NONE = 0, - PERF_TOOL_DURATION_TIME = 1, - PERF_TOOL_USER_TIME = 2, - PERF_TOOL_SYSTEM_TIME = 3, - - PERF_TOOL_MAX, -}; - -const char *perf_tool_event__to_str(enum perf_tool_event ev); -enum perf_tool_event perf_tool_event__from_str(const char *str); - -#define perf_tool_event__for_each_event(ev) \ - for ((ev) = PERF_TOOL_DURATION_TIME; (ev) < PERF_TOOL_MAX; ev++) - /** struct evsel - event selector * * @evlist - evlist this evsel is in, if it is in one. @@ -72,7 +57,6 @@ struct evsel { struct { char *name; char *group_name; - const char *pmu_name; const char *group_pmu_name; #ifdef HAVE_LIBTRACEEVENT struct tep_event *tp_format; @@ -83,7 +67,6 @@ struct evsel { const char *unit; struct cgroup *cgrp; const char *metric_id; - enum perf_tool_event tool_event; /* parse modifier helper */ int exclude_GH; int sample_read; @@ -102,6 +85,7 @@ struct evsel { int bpf_fd; struct bpf_object *bpf_obj; struct list_head config_terms; + u64 alternate_hw_config; }; /* @@ -183,7 +167,7 @@ struct evsel { unsigned long open_flags; int precise_ip_original; - /* for missing_features */ + /* The PMU the event is from. Used for missing_features, PMU name, etc. */ struct perf_pmu *pmu; /* For tool events */ @@ -221,6 +205,7 @@ struct perf_missing_features { bool weight_struct; bool read_lost; bool branch_counters; + bool inherit_sample_read; }; extern struct perf_missing_features perf_missing_features; @@ -320,21 +305,11 @@ const char *evsel__name(struct evsel *evsel); bool evsel__name_is(struct evsel *evsel, const char *name); const char *evsel__metric_id(const struct evsel *evsel); -static inline bool evsel__is_tool(const struct evsel *evsel) -{ - return evsel->tool_event != PERF_TOOL_NONE; -} - static inline bool evsel__is_retire_lat(const struct evsel *evsel) { return evsel->retire_lat; } -static inline enum perf_tool_event evsel__tool_event(const struct evsel *evsel) -{ - return evsel->tool_event; -} - const char *evsel__group_name(struct evsel *evsel); int evsel__group_desc(struct evsel *evsel, char *buf, size_t size); @@ -368,7 +343,6 @@ int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, void evsel__close(struct evsel *evsel); int evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, struct perf_thread_map *threads); -bool evsel__detect_missing_features(struct evsel *evsel); bool evsel__precise_ip_fallback(struct evsel *evsel); @@ -393,26 +367,10 @@ u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sam struct tep_format_field *evsel__field(struct evsel *evsel, const char *name); struct tep_format_field *evsel__common_field(struct evsel *evsel, const char *name); -static inline bool __evsel__match(const struct evsel *evsel, u32 type, u64 config) -{ - if (evsel->core.attr.type != type) - return false; - - if ((type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) && - perf_pmus__supports_extended_type()) - return (evsel->core.attr.config & PERF_HW_EVENT_MASK) == config; - - return evsel->core.attr.config == config; -} +bool __evsel__match(const struct evsel *evsel, u32 type, u64 config); #define evsel__match(evsel, t, c) __evsel__match(evsel, PERF_TYPE_##t, PERF_COUNT_##c) -static inline bool evsel__match2(struct evsel *e1, struct evsel *e2) -{ - return (e1->core.attr.type == e2->core.attr.type) && - (e1->core.attr.config == e2->core.attr.config); -} - int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread); int __evsel__read_on_cpu(struct evsel *evsel, int cpu_map_idx, int thread, bool scale); diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index c2c0500d5da99..86b7f46f9e2a9 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -14,7 +14,7 @@ #include "dso.h" #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index b2536a59c44e6..f289044a1f7c6 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -5,25 +5,22 @@ #include #include #include "metricgroup.h" -#include "cpumap.h" -#include "cputopo.h" #include "debug.h" #include "evlist.h" #include "expr.h" +#include "smt.h" +#include "tool_pmu.h" #include #include #include "util/hashmap.h" #include "util/header.h" #include "util/pmu.h" -#include "smt.h" -#include "tsc.h" -#include +#include #include #include #include #include #include -#include "pmu.h" struct expr_id_data { union { @@ -393,90 +390,26 @@ double expr_id_data__source_count(const struct expr_id_data *data) return data->val.source_count; } -#if !defined(__i386__) && !defined(__x86_64__) -double arch_get_tsc_freq(void) -{ - return 0.0; -} -#endif - -static double has_pmem(void) -{ - static bool has_pmem, cached; - const char *sysfs = sysfs__mountpoint(); - char path[PATH_MAX]; - - if (!cached) { - snprintf(path, sizeof(path), "%s/firmware/acpi/tables/NFIT", sysfs); - has_pmem = access(path, F_OK) == 0; - cached = true; - } - return has_pmem ? 1.0 : 0.0; -} - double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx) { - const struct cpu_topology *topology; double result = NAN; + enum tool_pmu_event ev = tool_pmu__str_to_event(literal + 1); - if (!strcmp("#num_cpus", literal)) { - result = cpu__max_present_cpu().cpu; - goto out; - } - if (!strcmp("#num_cpus_online", literal)) { - struct perf_cpu_map *online = cpu_map__online(); - - if (online) - result = perf_cpu_map__nr(online); - goto out; - } + if (ev != TOOL_PMU__EVENT_NONE) { + u64 count; - if (!strcasecmp("#system_tsc_freq", literal)) { - result = arch_get_tsc_freq(); - goto out; - } + if (tool_pmu__read_event(ev, &count)) + result = count; + else + pr_err("Failure to read '%s'", literal); - /* - * Assume that topology strings are consistent, such as CPUs "0-1" - * wouldn't be listed as "0,1", and so after deduplication the number of - * these strings gives an indication of the number of packages, dies, - * etc. - */ - if (!strcasecmp("#smt_on", literal)) { - result = smt_on() ? 1.0 : 0.0; - goto out; - } - if (!strcmp("#core_wide", literal)) { + } else if (!strcmp("#core_wide", literal)) { result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list) ? 1.0 : 0.0; - goto out; - } - if (!strcmp("#num_packages", literal)) { - topology = online_topology(); - result = topology->package_cpus_lists; - goto out; - } - if (!strcmp("#num_dies", literal)) { - topology = online_topology(); - result = topology->die_cpus_lists; - goto out; - } - if (!strcmp("#num_cores", literal)) { - topology = online_topology(); - result = topology->core_cpus_lists; - goto out; - } - if (!strcmp("#slots", literal)) { - result = perf_pmu__cpu_slots_per_cycle(); - goto out; - } - if (!strcmp("#has_pmem", literal)) { - result = has_pmem(); - goto out; + } else { + pr_err("Unrecognized literal '%s'", literal); } - pr_err("Unrecognized literal '%s'", literal); -out: pr_debug2("literal: %s = %f\n", literal, result); return result; } @@ -523,8 +456,8 @@ double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx __maybe_unused, bool compute_ids __maybe_unused, const char *test_id) { double ret; - struct perf_pmu *pmu = perf_pmus__find_core_pmu(); - char *cpuid = perf_pmu__getcpuid(pmu); + struct perf_cpu cpu = {-1}; + char *cpuid = get_cpuid_allow_env_override(cpu); if (!cpuid) return NAN; diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index c8f6bee1fa619..cdce7f173d00a 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c @@ -16,7 +16,7 @@ #include #include #include -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT #include #endif @@ -499,7 +499,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_size = sizeof(bnote); shdr->sh_entsize = 0; -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT if (debug && nr_debug_entries) { retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); if (retval) diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index 4e2e4f40e134f..9f0b875d6548c 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h @@ -8,7 +8,7 @@ int jit_write_elf(int fd, uint64_t code_addr, const char *sym, const void *code, int csize, void *debug, int nr_debug_entries, void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size); -#ifdef HAVE_DWARF_SUPPORT +#ifdef HAVE_LIBDW_SUPPORT /* genelf_debug.c */ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries); #endif diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h index c12f8320e6682..0c4f155e8eb74 100644 --- a/tools/perf/util/hashmap.h +++ b/tools/perf/util/hashmap.h @@ -166,8 +166,8 @@ bool hashmap_find(const struct hashmap *map, long key, long *value); * @bkt: integer used as a bucket loop cursor */ #define hashmap__for_each_entry(map, cur, bkt) \ - for (bkt = 0; bkt < map->cap; bkt++) \ - for (cur = map->buckets[bkt]; cur; cur = cur->next) + for (bkt = 0; bkt < (map)->cap; bkt++) \ + for (cur = (map)->buckets[bkt]; cur; cur = cur->next) /* * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe @@ -178,8 +178,8 @@ bool hashmap_find(const struct hashmap *map, long key, long *value); * @bkt: integer used as a bucket loop cursor */ #define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \ - for (bkt = 0; bkt < map->cap; bkt++) \ - for (cur = map->buckets[bkt]; \ + for (bkt = 0; bkt < (map)->cap; bkt++) \ + for (cur = (map)->buckets[bkt]; \ cur && ({tmp = cur->next; true; }); \ cur = tmp) @@ -190,19 +190,19 @@ bool hashmap_find(const struct hashmap *map, long key, long *value); * @key: key to iterate entries for */ #define hashmap__for_each_key_entry(map, cur, _key) \ - for (cur = map->buckets \ - ? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), map->cap_bits)] \ + for (cur = (map)->buckets \ + ? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \ : NULL; \ cur; \ cur = cur->next) \ - if (map->equal_fn(cur->key, (_key), map->ctx)) + if ((map)->equal_fn(cur->key, (_key), (map)->ctx)) #define hashmap__for_each_key_entry_safe(map, cur, tmp, _key) \ - for (cur = map->buckets \ - ? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), map->cap_bits)] \ + for (cur = (map)->buckets \ + ? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \ : NULL; \ cur && ({ tmp = cur->next; true; }); \ cur = tmp) \ - if (map->equal_fn(cur->key, (_key), map->ctx)) + if ((map)->equal_fn(cur->key, (_key), (map)->ctx)) #endif /* __LIBBPF_HASHMAP_H */ diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index a6386d12afd72..3451e542b69a8 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -58,7 +58,7 @@ #include #ifdef HAVE_LIBTRACEEVENT -#include +#include #endif /* @@ -819,11 +819,31 @@ static int write_group_desc(struct feat_fd *ff, * Each architecture should provide a more precise id string that * can be use to match the architecture's "mapfile". */ -char * __weak get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +char * __weak get_cpuid_str(struct perf_cpu cpu __maybe_unused) { return NULL; } +char *get_cpuid_allow_env_override(struct perf_cpu cpu) +{ + char *cpuid; + static bool printed; + + cpuid = getenv("PERF_CPUID"); + if (cpuid) + cpuid = strdup(cpuid); + if (!cpuid) + cpuid = get_cpuid_str(cpu); + if (!cpuid) + return NULL; + + if (!printed) { + pr_debug("Using CPUID %s\n", cpuid); + printed = true; + } + return cpuid; +} + /* Return zero when the cpuid from the mapfile.csv matches the * cpuid string generated on this platform. * Otherwise return non-zero. @@ -856,18 +876,19 @@ int __weak strcmp_cpuid_str(const char *mapcpuid, const char *cpuid) * default get_cpuid(): nothing gets recorded * actual implementation must be in arch/$(SRCARCH)/util/header.c */ -int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused) +int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused, + struct perf_cpu cpu __maybe_unused) { return ENOSYS; /* Not implemented */ } -static int write_cpuid(struct feat_fd *ff, - struct evlist *evlist __maybe_unused) +static int write_cpuid(struct feat_fd *ff, struct evlist *evlist) { + struct perf_cpu cpu = perf_cpu_map__min(evlist->core.all_cpus); char buffer[64]; int ret; - ret = get_cpuid(buffer, sizeof(buffer)); + ret = get_cpuid(buffer, sizeof(buffer), cpu); if (ret) return -1; @@ -987,57 +1008,6 @@ static int write_dir_format(struct feat_fd *ff, return do_write(ff, &data->dir.version, sizeof(data->dir.version)); } -/* - * Check whether a CPU is online - * - * Returns: - * 1 -> if CPU is online - * 0 -> if CPU is offline - * -1 -> error case - */ -int is_cpu_online(unsigned int cpu) -{ - char *str; - size_t strlen; - char buf[256]; - int status = -1; - struct stat statbuf; - - snprintf(buf, sizeof(buf), - "/sys/devices/system/cpu/cpu%d", cpu); - if (stat(buf, &statbuf) != 0) - return 0; - - /* - * Check if /sys/devices/system/cpu/cpux/online file - * exists. Some cases cpu0 won't have online file since - * it is not expected to be turned off generally. - * In kernels without CONFIG_HOTPLUG_CPU, this - * file won't exist - */ - snprintf(buf, sizeof(buf), - "/sys/devices/system/cpu/cpu%d/online", cpu); - if (stat(buf, &statbuf) != 0) - return 1; - - /* - * Read online file using sysfs__read_str. - * If read or open fails, return -1. - * If read succeeds, return value from file - * which gets stored in "str" - */ - snprintf(buf, sizeof(buf), - "devices/system/cpu/cpu%d/online", cpu); - - if (sysfs__read_str(buf, &str, &strlen) < 0) - return status; - - status = atoi(str); - - free(str); - return status; -} - #ifdef HAVE_LIBBPF_SUPPORT static int write_bpf_prog_info(struct feat_fd *ff, struct evlist *evlist __maybe_unused) diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a63a361f20f49..5201af6305f4b 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -10,7 +10,13 @@ #include #include #include "env.h" -#include "pmu.h" +#include + +struct evlist; +union perf_event; +struct perf_header; +struct perf_session; +struct perf_tool; enum { HEADER_RESERVED = 0, /* always cleared */ @@ -91,8 +97,6 @@ struct perf_pipe_file_header { u64 size; }; -struct perf_header; - int perf_file_header__read(struct perf_file_header *header, struct perf_header *ph, int fd); @@ -124,11 +128,6 @@ struct perf_header_feature_ops { bool synthesize; }; -struct evlist; -struct perf_session; -struct perf_tool; -union perf_event; - extern const char perf_version_string[]; int perf_session__read_header(struct perf_session *session); @@ -196,14 +195,16 @@ int write_padded(struct feat_fd *fd, const void *bf, #define MAX_CACHE_LVL 4 -int is_cpu_online(unsigned int cpu); int build_caches_for_cpu(u32 cpu, struct cpu_cache_level caches[], u32 *cntp); /* * arch specific callback */ -int get_cpuid(char *buffer, size_t sz); +int get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu); + +char *get_cpuid_str(struct perf_cpu cpu); + +char *get_cpuid_allow_env_override(struct perf_cpu cpu); -char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused); int strcmp_cpuid_str(const char *s1, const char *s2); #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f387e85a00873..fff1345658016 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -218,6 +218,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) hists__new_col_len(hists, HISTC_LOCAL_P_STAGE_CYC, 13); hists__new_col_len(hists, HISTC_GLOBAL_P_STAGE_CYC, 13); hists__new_col_len(hists, HISTC_ADDR, BITS_PER_LONG / 4 + 2); + hists__new_col_len(hists, HISTC_CALLCHAIN_BRANCH_PREDICTED, 9); + hists__new_col_len(hists, HISTC_CALLCHAIN_BRANCH_ABORT, 5); + hists__new_col_len(hists, HISTC_CALLCHAIN_BRANCH_CYCLES, 6); if (symbol_conf.nanosecs) hists__new_col_len(hists, HISTC_TIME, 16); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 7d7ae94b4b31f..1131056924d9c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -87,6 +87,9 @@ enum hist_column { HISTC_TYPE_OFFSET, HISTC_SYMBOL_OFFSET, HISTC_TYPE_CACHELINE, + HISTC_CALLCHAIN_BRANCH_PREDICTED, + HISTC_CALLCHAIN_BRANCH_ABORT, + HISTC_CALLCHAIN_BRANCH_CYCLES, HISTC_NR_COLS, /* Last entry */ }; diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c new file mode 100644 index 0000000000000..e61429b38ba7b --- /dev/null +++ b/tools/perf/util/hwmon_pmu.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "counts.h" +#include "debug.h" +#include "evsel.h" +#include "hashmap.h" +#include "hwmon_pmu.h" +#include "pmu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Strings that correspond to enum hwmon_type. */ +static const char * const hwmon_type_strs[HWMON_TYPE_MAX] = { + NULL, + "cpu", + "curr", + "energy", + "fan", + "humidity", + "in", + "intrusion", + "power", + "pwm", + "temp", +}; +#define LONGEST_HWMON_TYPE_STR "intrusion" + +/** Strings that correspond to enum hwmon_item. */ +static const char * const hwmon_item_strs[HWMON_ITEM__MAX] = { + NULL, + "accuracy", + "alarm", + "auto_channels_temp", + "average", + "average_highest", + "average_interval", + "average_interval_max", + "average_interval_min", + "average_lowest", + "average_max", + "average_min", + "beep", + "cap", + "cap_hyst", + "cap_max", + "cap_min", + "crit", + "crit_hyst", + "div", + "emergency", + "emergency_hist", + "enable", + "fault", + "freq", + "highest", + "input", + "label", + "lcrit", + "lcrit_hyst", + "lowest", + "max", + "max_hyst", + "min", + "min_hyst", + "mod", + "offset", + "pulses", + "rated_max", + "rated_min", + "reset_history", + "target", + "type", + "vid", +}; +#define LONGEST_HWMON_ITEM_STR "average_interval_max" + +static const char *const hwmon_units[HWMON_TYPE_MAX] = { + NULL, + "V", /* cpu */ + "A", /* curr */ + "J", /* energy */ + "rpm", /* fan */ + "%", /* humidity */ + "V", /* in */ + "", /* intrusion */ + "W", /* power */ + "Hz", /* pwm */ + "'C", /* temp */ +}; + +struct hwmon_pmu { + struct perf_pmu pmu; + struct hashmap events; + int hwmon_dir_fd; +}; + +/** + * union hwmon_pmu_event_key: Key for hwmon_pmu->events as such each key + * represents an event. + * + * Related hwmon files start that this key represents. + */ +union hwmon_pmu_event_key { + long type_and_num; + struct { + int num :16; + enum hwmon_type type :8; + }; +}; + +/** + * struct hwmon_pmu_event_value: Value in hwmon_pmu->events. + * + * Hwmon files are of the form _ and may have a suffix + * _alarm. + */ +struct hwmon_pmu_event_value { + /** @items: which item files are present. */ + DECLARE_BITMAP(items, HWMON_ITEM__MAX); + /** @alarm_items: which item files are present. */ + DECLARE_BITMAP(alarm_items, HWMON_ITEM__MAX); + /** @label: contents of _label if present. */ + char *label; + /** @name: name computed from label of the form _